| 
									
										
										
										
											2009-06-29 14:06:01 +02:00
										 |  |  | '''This package contains mixin classes that are mixed in with generated classes:
 | 
					
						
							| 
									
										
										
										
											2010-10-14 14:43:56 +02:00
										 |  |  |    - mixins/BaseMixin is mixed in with Standard Archetypes classes; | 
					
						
							|  |  |  |    - mixins/ToolMixin is mixed in with the generated application Tool class.'''
 | 
					
						
							| 
									
										
										
										
											2009-06-29 14:06:01 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | # ------------------------------------------------------------------------------ | 
					
						
							| 
									
										
										
										
											2011-01-28 14:36:30 +01:00
										 |  |  | import os, os.path, sys, types, mimetypes, urllib | 
					
						
							| 
									
										
										
										
											2009-06-29 14:06:01 +02:00
										 |  |  | import appy.gen | 
					
						
							| 
									
										
										
										
											2011-02-01 11:09:54 +01:00
										 |  |  | from appy.gen import Type, String, Selection, Role, No | 
					
						
							| 
									
										
										
										
											2010-08-05 18:23:17 +02:00
										 |  |  | from appy.gen.utils import * | 
					
						
							|  |  |  | from appy.gen.layout import Table, defaultPageLayouts | 
					
						
							|  |  |  | from appy.gen.plone25.descriptors import ClassDescriptor | 
					
						
							| 
									
										
										
										
											2011-02-01 11:09:54 +01:00
										 |  |  | from appy.gen.plone25.utils import updateRolesForPermission,checkTransitionGuard | 
					
						
							| 
									
										
										
										
											2009-06-29 14:06:01 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | # ------------------------------------------------------------------------------ | 
					
						
							| 
									
										
										
										
											2010-10-14 14:43:56 +02:00
										 |  |  | class BaseMixin: | 
					
						
							|  |  |  |     '''Every Archetype class generated by appy.gen inherits from this class or
 | 
					
						
							|  |  |  |        a subclass of it.'''
 | 
					
						
							|  |  |  |     _appy_meta_type = 'Class' | 
					
						
							| 
									
										
										
										
											2009-06-29 14:06:01 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-10-29 14:36:36 +02:00
										 |  |  |     def get_o(self): | 
					
						
							| 
									
										
										
										
											2010-11-22 09:36:14 +01:00
										 |  |  |         '''In some cases, we want the Zope object, we don't know if the current
 | 
					
						
							| 
									
										
										
										
											2010-10-29 14:36:36 +02:00
										 |  |  |            object is a Zope or Appy object. By defining this property, | 
					
						
							|  |  |  |            "someObject.o" produces always the Zope object, be someObject an Appy | 
					
						
							|  |  |  |            or Zope object.'''
 | 
					
						
							|  |  |  |         return self | 
					
						
							|  |  |  |     o = property(get_o) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-08-05 18:23:17 +02:00
										 |  |  |     def createOrUpdate(self, created, values): | 
					
						
							| 
									
										
										
										
											2009-10-18 14:52:27 +02:00
										 |  |  |         '''This method creates (if p_created is True) or updates an object.
 | 
					
						
							| 
									
										
										
										
											2010-08-05 18:23:17 +02:00
										 |  |  |            p_values are manipulated versions of those from the HTTP request. | 
					
						
							|  |  |  |            In the case of an object creation (p_created is True), p_self is a | 
					
						
							|  |  |  |            temporary object created in the request by portal_factory, and this | 
					
						
							|  |  |  |            method creates the corresponding final object. In the case of an | 
					
						
							|  |  |  |            update, this method simply updates fields of p_self.'''
 | 
					
						
							| 
									
										
										
										
											2009-07-28 10:14:40 +02:00
										 |  |  |         rq = self.REQUEST | 
					
						
							| 
									
										
										
										
											2009-10-18 14:52:27 +02:00
										 |  |  |         obj = self | 
					
						
							|  |  |  |         if created: | 
					
						
							|  |  |  |             obj = self.portal_factory.doCreate(self, self.id) # portal_factory | 
					
						
							|  |  |  |             # creates the final object from the temp object. | 
					
						
							| 
									
										
										
										
											2009-12-14 20:22:55 +01:00
										 |  |  |         previousData = None | 
					
						
							|  |  |  |         if not created: previousData = self.rememberPreviousData() | 
					
						
							| 
									
										
										
										
											2009-12-17 21:14:52 +01:00
										 |  |  |         # Perform the change on the object, unless self is a tool being created. | 
					
						
							| 
									
										
										
										
											2010-08-05 18:23:17 +02:00
										 |  |  |         if (obj._appy_meta_type == 'Tool') and created: | 
					
						
							| 
									
										
										
										
											2009-12-17 21:14:52 +01:00
										 |  |  |             # We do not process form data (=real update on the object) if the | 
					
						
							|  |  |  |             # tool itself is being created. | 
					
						
							|  |  |  |             pass | 
					
						
							|  |  |  |         else: | 
					
						
							| 
									
										
										
										
											2010-08-05 18:23:17 +02:00
										 |  |  |             # Store in the database the new value coming from the form | 
					
						
							|  |  |  |             for appyType in self.getAppyTypes('edit', rq.get('page')): | 
					
						
							|  |  |  |                 value = getattr(values, appyType.name, None) | 
					
						
							|  |  |  |                 appyType.store(obj, value) | 
					
						
							|  |  |  |             if created: | 
					
						
							|  |  |  |                 # Now we have a title for the object, so we derive a nice id | 
					
						
							| 
									
										
										
										
											2010-09-13 21:04:10 +02:00
										 |  |  |                 obj.unmarkCreationFlag() | 
					
						
							| 
									
										
										
										
											2010-08-05 18:23:17 +02:00
										 |  |  |                 obj._renameAfterCreation(check_auto_id=True) | 
					
						
							| 
									
										
										
										
											2009-12-14 20:22:55 +01:00
										 |  |  |         if previousData: | 
					
						
							|  |  |  |             # Keep in history potential changes on historized fields | 
					
						
							|  |  |  |             self.historizeData(previousData) | 
					
						
							| 
									
										
										
										
											2009-07-28 10:14:40 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-10-14 14:43:56 +02:00
										 |  |  |         # Manage potential link with an initiator object | 
					
						
							|  |  |  |         if created and rq.get('nav', None): | 
					
						
							|  |  |  |             # Get the initiator | 
					
						
							|  |  |  |             splitted = rq['nav'].split('.') | 
					
						
							|  |  |  |             if splitted[0] == 'search': return # Not an initiator but a search. | 
					
						
							|  |  |  |             initiator = self.uid_catalog(UID=splitted[1])[0].getObject() | 
					
						
							|  |  |  |             fieldName = splitted[2].split(':')[0] | 
					
						
							|  |  |  |             initiator.appy().link(fieldName, obj) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Call the custom "onEdit" if available | 
					
						
							| 
									
										
										
										
											2011-01-28 14:36:30 +01:00
										 |  |  |         msg = None # The message to display to the user. It can be set by onEdit | 
					
						
							| 
									
										
										
										
											2009-10-18 14:52:27 +02:00
										 |  |  |         if obj.wrapperClass: | 
					
						
							| 
									
										
										
										
											2010-08-05 18:23:17 +02:00
										 |  |  |             appyObject = obj.appy() | 
					
						
							| 
									
										
										
										
											2011-01-28 14:36:30 +01:00
										 |  |  |             if hasattr(appyObject, 'onEdit'): | 
					
						
							|  |  |  |                 msg = appyObject.onEdit(created) | 
					
						
							| 
									
										
										
										
											2010-10-14 14:43:56 +02:00
										 |  |  |         # Manage "add" permissions and reindex the object | 
					
						
							| 
									
										
										
										
											2009-10-18 14:52:27 +02:00
										 |  |  |         obj._appy_managePermissions() | 
					
						
							| 
									
										
										
										
											2009-11-24 22:41:42 +01:00
										 |  |  |         obj.reindexObject() | 
					
						
							| 
									
										
										
										
											2011-01-28 14:36:30 +01:00
										 |  |  |         return obj, msg | 
					
						
							| 
									
										
										
										
											2009-10-20 16:57:00 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def delete(self): | 
					
						
							|  |  |  |         '''This methods is self's suicide.''' | 
					
						
							| 
									
										
										
										
											2010-11-26 17:30:46 +01:00
										 |  |  |         # Call a custom "onDelete" if it exists | 
					
						
							|  |  |  |         appyObj = self.appy() | 
					
						
							|  |  |  |         if hasattr(appyObj, 'onDelete'): appyObj.onDelete() | 
					
						
							|  |  |  |         # Delete the object | 
					
						
							| 
									
										
										
										
											2009-10-20 16:57:00 +02:00
										 |  |  |         self.getParentNode().manage_delObjects([self.id]) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-11-22 09:36:14 +01:00
										 |  |  |     def onDelete(self): | 
					
						
							|  |  |  |         rq = self.REQUEST | 
					
						
							|  |  |  |         self.delete() | 
					
						
							|  |  |  |         if self.getUrl(rq['HTTP_REFERER'],mode='raw') ==self.getUrl(mode='raw'): | 
					
						
							|  |  |  |             # We were consulting the object that has been deleted. Go back to | 
					
						
							|  |  |  |             # the main page. | 
					
						
							|  |  |  |             urlBack = self.getTool().getSiteUrl() | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             urlBack = self.getUrl(rq['HTTP_REFERER']) | 
					
						
							| 
									
										
										
										
											2011-02-12 17:09:11 +01:00
										 |  |  |         self.say(self.translate('delete_done')) | 
					
						
							| 
									
										
										
										
											2010-11-22 09:36:14 +01:00
										 |  |  |         self.goto(urlBack) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-10-20 16:57:00 +02:00
										 |  |  |     def onCreate(self): | 
					
						
							|  |  |  |         '''This method is called when a user wants to create a root object in
 | 
					
						
							|  |  |  |            the application folder or an object through a reference field.'''
 | 
					
						
							|  |  |  |         rq = self.REQUEST | 
					
						
							| 
									
										
										
										
											2010-09-19 15:04:44 +02:00
										 |  |  |         typeName = rq.get('type_name') | 
					
						
							|  |  |  |         # Create the params to add to the URL we will redirect the user to | 
					
						
							|  |  |  |         # create the object. | 
					
						
							|  |  |  |         urlParams = {'mode':'edit', 'page':'main', 'nav':''} | 
					
						
							|  |  |  |         if rq.get('nav', None): | 
					
						
							| 
									
										
										
										
											2009-10-20 16:57:00 +02:00
										 |  |  |             # The object to create will be linked to an initiator object through | 
					
						
							| 
									
										
										
										
											2010-09-19 15:04:44 +02:00
										 |  |  |             # a ref field. We create here a new navigation string with one more | 
					
						
							|  |  |  |             # item, that will be the currently created item. | 
					
						
							|  |  |  |             splitted = rq.get('nav').split('.') | 
					
						
							|  |  |  |             splitted[-1] = splitted[-2] = str(int(splitted[-1])+1) | 
					
						
							|  |  |  |             urlParams['nav'] = '.'.join(splitted) | 
					
						
							|  |  |  |         # Determine base URL | 
					
						
							|  |  |  |         baseUrl = self.absolute_url() | 
					
						
							|  |  |  |         if (self._appy_meta_type == 'Tool') and not urlParams['nav']: | 
					
						
							|  |  |  |             # This is the creation of a root object in the app folder | 
					
						
							|  |  |  |             baseUrl = self.getAppFolder().absolute_url() | 
					
						
							|  |  |  |         objId = self.generateUniqueId(typeName) | 
					
						
							|  |  |  |         editUrl = '%s/portal_factory/%s/%s/skyn/edit' % \ | 
					
						
							|  |  |  |                   (baseUrl, typeName, objId) | 
					
						
							|  |  |  |         return self.goto(self.getUrl(editUrl, **urlParams)) | 
					
						
							| 
									
										
										
										
											2009-10-20 16:57:00 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-10-14 14:43:56 +02:00
										 |  |  |     def onCreateWithoutForm(self): | 
					
						
							|  |  |  |         '''This method is called when a user wants to create a object from a
 | 
					
						
							|  |  |  |            reference field, automatically (without displaying a form).'''
 | 
					
						
							|  |  |  |         rq = self.REQUEST | 
					
						
							|  |  |  |         self.appy().create(rq['fieldName']) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-08-05 18:23:17 +02:00
										 |  |  |     def intraFieldValidation(self, errors, values): | 
					
						
							|  |  |  |         '''This method performs field-specific validation for every field from
 | 
					
						
							|  |  |  |            the page that is being created or edited. For every field whose | 
					
						
							|  |  |  |            validation generates an error, we add an entry in p_errors. For every | 
					
						
							|  |  |  |            field, we add in p_values an entry with the "ready-to-store" field | 
					
						
							|  |  |  |            value.'''
 | 
					
						
							|  |  |  |         rq = self.REQUEST | 
					
						
							|  |  |  |         for appyType in self.getAppyTypes('edit', rq.form.get('page')): | 
					
						
							|  |  |  |             if not appyType.validable: continue | 
					
						
							|  |  |  |             value = appyType.getRequestValue(rq) | 
					
						
							|  |  |  |             message = appyType.validate(self, value) | 
					
						
							|  |  |  |             if message: | 
					
						
							|  |  |  |                 setattr(errors, appyType.name, message) | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 setattr(values, appyType.name, appyType.getStorableValue(value)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def interFieldValidation(self, errors, values): | 
					
						
							|  |  |  |         '''This method is called when individual validation of all fields
 | 
					
						
							|  |  |  |            succeed (when editing or creating an object). Then, this method | 
					
						
							|  |  |  |            performs inter-field validation. This way, the user must first | 
					
						
							|  |  |  |            correct individual fields before being confronted to potential | 
					
						
							|  |  |  |            inter-field validation errors.'''
 | 
					
						
							|  |  |  |         obj = self.appy() | 
					
						
							|  |  |  |         if not hasattr(obj, 'validate'): return | 
					
						
							| 
									
										
										
										
											2011-02-06 17:39:36 +01:00
										 |  |  |         msg = obj.validate(values, errors) | 
					
						
							| 
									
										
										
										
											2010-10-14 14:43:56 +02:00
										 |  |  |         # Those custom validation methods may have added fields in the given | 
					
						
							| 
									
										
										
										
											2010-08-05 18:23:17 +02:00
										 |  |  |         # p_errors object. Within this object, for every error message that is | 
					
						
							|  |  |  |         # not a string, we replace it with the standard validation error for the | 
					
						
							|  |  |  |         # corresponding field. | 
					
						
							|  |  |  |         for key, value in errors.__dict__.iteritems(): | 
					
						
							|  |  |  |             resValue = value | 
					
						
							|  |  |  |             if not isinstance(resValue, basestring): | 
					
						
							| 
									
										
										
										
											2010-11-16 15:32:47 +01:00
										 |  |  |                 resValue = self.translate('field_invalid') | 
					
						
							| 
									
										
										
										
											2010-08-05 18:23:17 +02:00
										 |  |  |             setattr(errors, key, resValue) | 
					
						
							| 
									
										
										
										
											2011-02-06 17:39:36 +01:00
										 |  |  |         return msg | 
					
						
							| 
									
										
										
										
											2010-08-05 18:23:17 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-10-18 14:52:27 +02:00
										 |  |  |     def onUpdate(self): | 
					
						
							|  |  |  |         '''This method is executed when a user wants to update an object.
 | 
					
						
							|  |  |  |            The object may be a temporary object created by portal_factory in | 
					
						
							|  |  |  |            the request. In this case, the update consists in the creation of | 
					
						
							|  |  |  |            the "final" object in the database. If the object is not a temporary | 
					
						
							|  |  |  |            one, this method updates its fields in the database.'''
 | 
					
						
							|  |  |  |         rq = self.REQUEST | 
					
						
							| 
									
										
										
										
											2010-11-22 09:36:14 +01:00
										 |  |  |         tool = self.getTool() | 
					
						
							| 
									
										
										
										
											2009-10-18 14:52:27 +02:00
										 |  |  |         errorMessage = self.translate( | 
					
						
							|  |  |  |             'Please correct the indicated errors.', domain='plone') | 
					
						
							| 
									
										
										
										
											2010-08-05 18:23:17 +02:00
										 |  |  |         isNew = rq.get('is_new') == 'True' | 
					
						
							| 
									
										
										
										
											2009-10-18 14:52:27 +02:00
										 |  |  |         # Go back to the consult view if the user clicked on 'Cancel' | 
					
						
							| 
									
										
										
										
											2010-08-05 18:23:17 +02:00
										 |  |  |         if rq.get('buttonCancel.x', None): | 
					
						
							|  |  |  |             if isNew: | 
					
						
							| 
									
										
										
										
											2010-09-19 15:04:44 +02:00
										 |  |  |                 if rq.get('nav', ''): | 
					
						
							|  |  |  |                     # We can go back to the initiator page. | 
					
						
							|  |  |  |                     splitted = rq['nav'].split('.') | 
					
						
							| 
									
										
										
										
											2010-11-22 09:36:14 +01:00
										 |  |  |                     initiator = tool.getObject(splitted[1]) | 
					
						
							| 
									
										
										
										
											2010-09-19 15:04:44 +02:00
										 |  |  |                     initiatorPage = splitted[2].split(':')[1] | 
					
						
							|  |  |  |                     urlBack = initiator.getUrl(page=initiatorPage, nav='') | 
					
						
							|  |  |  |                 else: | 
					
						
							| 
									
										
										
										
											2010-11-05 13:05:10 +01:00
										 |  |  |                     # Go back to the root of the site. | 
					
						
							| 
									
										
										
										
											2010-11-22 09:36:14 +01:00
										 |  |  |                     urlBack = tool.getSiteUrl() | 
					
						
							| 
									
										
										
										
											2009-10-25 21:42:08 +01:00
										 |  |  |             else: | 
					
						
							| 
									
										
										
										
											2010-09-17 15:32:48 +02:00
										 |  |  |                 urlBack = self.getUrl() | 
					
						
							| 
									
										
										
										
											2011-02-12 17:09:11 +01:00
										 |  |  |             self.say(self.translate('Changes canceled.', domain='plone')) | 
					
						
							| 
									
										
										
										
											2010-09-17 15:32:48 +02:00
										 |  |  |             return self.goto(urlBack) | 
					
						
							| 
									
										
										
										
											2009-10-18 14:52:27 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-08-05 18:23:17 +02:00
										 |  |  |         # Object for storing validation errors | 
					
						
							|  |  |  |         errors = AppyObject() | 
					
						
							|  |  |  |         # Object for storing the (converted) values from the request | 
					
						
							|  |  |  |         values = AppyObject() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-10-18 14:52:27 +02:00
										 |  |  |         # Trigger field-specific validation | 
					
						
							| 
									
										
										
										
											2010-08-05 18:23:17 +02:00
										 |  |  |         self.intraFieldValidation(errors, values) | 
					
						
							|  |  |  |         if errors.__dict__: | 
					
						
							|  |  |  |             rq.set('errors', errors.__dict__) | 
					
						
							| 
									
										
										
										
											2011-02-12 17:09:11 +01:00
										 |  |  |             self.say(errorMessage) | 
					
						
							| 
									
										
										
										
											2011-02-15 20:15:58 +01:00
										 |  |  |             return self.gotoEdit() | 
					
						
							| 
									
										
										
										
											2010-08-05 18:23:17 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         # Trigger inter-field validation | 
					
						
							| 
									
										
										
										
											2011-02-06 17:39:36 +01:00
										 |  |  |         msg = self.interFieldValidation(errors, values) | 
					
						
							|  |  |  |         if not msg: msg = errorMessage | 
					
						
							| 
									
										
										
										
											2010-08-05 18:23:17 +02:00
										 |  |  |         if errors.__dict__: | 
					
						
							|  |  |  |             rq.set('errors', errors.__dict__) | 
					
						
							| 
									
										
										
										
											2011-02-12 17:09:11 +01:00
										 |  |  |             self.say(msg) | 
					
						
							| 
									
										
										
										
											2011-02-15 20:15:58 +01:00
										 |  |  |             return self.gotoEdit() | 
					
						
							| 
									
										
										
										
											2010-08-05 18:23:17 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-11-22 09:36:14 +01:00
										 |  |  |         # Before saving data, must we ask a confirmation by the user ? | 
					
						
							|  |  |  |         appyObj = self.appy() | 
					
						
							|  |  |  |         saveConfirmed = rq.get('confirmed') == 'True' | 
					
						
							|  |  |  |         if hasattr(appyObj, 'confirm') and not saveConfirmed: | 
					
						
							|  |  |  |             msg = appyObj.confirm(values) | 
					
						
							|  |  |  |             if msg: | 
					
						
							|  |  |  |                 rq.set('confirmMsg', msg.replace("'", "\\'")) | 
					
						
							| 
									
										
										
										
											2011-02-15 20:15:58 +01:00
										 |  |  |                 return self.gotoEdit() | 
					
						
							| 
									
										
										
										
											2010-11-22 09:36:14 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-08-05 18:23:17 +02:00
										 |  |  |         # Create or update the object in the database | 
					
						
							| 
									
										
										
										
											2011-01-28 14:36:30 +01:00
										 |  |  |         obj, msg = self.createOrUpdate(isNew, values) | 
					
						
							| 
									
										
										
										
											2010-08-05 18:23:17 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         # Redirect the user to the appropriate page | 
					
						
							| 
									
										
										
										
											2011-01-28 14:36:30 +01:00
										 |  |  |         if not msg: msg = obj.translate('Changes saved.', domain='plone') | 
					
						
							|  |  |  |         # If the object has already been deleted (ie, it is a kind of transient | 
					
						
							|  |  |  |         # object like a one-shot form and has already been deleted in method | 
					
						
							|  |  |  |         # onEdit), redirect to the main site page. | 
					
						
							|  |  |  |         if not getattr(obj.getParentNode(), obj.id, None): | 
					
						
							|  |  |  |             obj.unindexObject() | 
					
						
							|  |  |  |             return self.goto(tool.getSiteUrl(), msg) | 
					
						
							|  |  |  |         # If the user can't access the object anymore, redirect him to the | 
					
						
							|  |  |  |         # main site page. | 
					
						
							|  |  |  |         user = self.portal_membership.getAuthenticatedMember() | 
					
						
							|  |  |  |         if not user.has_permission('View', obj): | 
					
						
							|  |  |  |             return self.goto(tool.getSiteUrl(), msg) | 
					
						
							| 
									
										
										
										
											2010-11-22 09:36:14 +01:00
										 |  |  |         if rq.get('buttonOk.x', None) or saveConfirmed: | 
					
						
							| 
									
										
										
										
											2010-08-05 18:23:17 +02:00
										 |  |  |             # Go to the consult view for this object | 
					
						
							| 
									
										
										
										
											2011-02-12 17:09:11 +01:00
										 |  |  |             obj.say(msg) | 
					
						
							| 
									
										
										
										
											2010-09-17 15:32:48 +02:00
										 |  |  |             return self.goto(obj.getUrl()) | 
					
						
							| 
									
										
										
										
											2010-08-05 18:23:17 +02:00
										 |  |  |         if rq.get('buttonPrevious.x', None): | 
					
						
							| 
									
										
										
										
											2010-09-13 21:04:10 +02:00
										 |  |  |             # Go to the previous page for this object. | 
					
						
							| 
									
										
										
										
											2010-08-05 18:23:17 +02:00
										 |  |  |             # We recompute the list of phases and pages because things | 
					
						
							|  |  |  |             # may have changed since the object has been updated (ie, | 
					
						
							|  |  |  |             # additional pages may be shown or hidden now, so the next and | 
					
						
							| 
									
										
										
										
											2010-09-13 21:04:10 +02:00
										 |  |  |             # previous pages may have changed). Moreover, previous and next | 
					
						
							|  |  |  |             # pages may not be available in "edit" mode, so we return the edit | 
					
						
							|  |  |  |             # or view pages depending on page.show. | 
					
						
							| 
									
										
										
										
											2010-10-19 10:47:42 +02:00
										 |  |  |             phaseInfo = self.getAppyPhases(currentOnly=True, layoutType='edit') | 
					
						
							|  |  |  |             pageName, pageInfo = self.getPreviousPage(phaseInfo, rq['page']) | 
					
						
							|  |  |  |             if pageName: | 
					
						
							| 
									
										
										
										
											2010-09-17 15:32:48 +02:00
										 |  |  |                 # Return to the edit or view page? | 
					
						
							| 
									
										
										
										
											2010-10-19 10:47:42 +02:00
										 |  |  |                 if pageInfo['showOnEdit']: | 
					
						
							|  |  |  |                     rq.set('page', pageName) | 
					
						
							| 
									
										
										
										
											2011-02-15 20:15:58 +01:00
										 |  |  |                     return obj.gotoEdit() | 
					
						
							| 
									
										
										
										
											2010-09-13 21:04:10 +02:00
										 |  |  |                 else: | 
					
						
							| 
									
										
										
										
											2010-10-19 10:47:42 +02:00
										 |  |  |                     return self.goto(obj.getUrl(page=pageName)) | 
					
						
							| 
									
										
										
										
											2009-10-18 14:52:27 +02:00
										 |  |  |             else: | 
					
						
							| 
									
										
										
										
											2011-02-12 17:09:11 +01:00
										 |  |  |                 obj.say(msg) | 
					
						
							| 
									
										
										
										
											2010-09-17 15:32:48 +02:00
										 |  |  |                 return self.goto(obj.getUrl()) | 
					
						
							| 
									
										
										
										
											2010-08-05 18:23:17 +02:00
										 |  |  |         if rq.get('buttonNext.x', None): | 
					
						
							| 
									
										
										
										
											2010-09-13 21:04:10 +02:00
										 |  |  |             # Go to the next page for this object | 
					
						
							| 
									
										
										
										
											2010-11-26 17:30:46 +01:00
										 |  |  |             # We remember page name, because the next method may set a new | 
					
						
							|  |  |  |             # current page if the current one is not visible anymore. | 
					
						
							|  |  |  |             pageName = rq['page'] | 
					
						
							| 
									
										
										
										
											2010-10-19 10:47:42 +02:00
										 |  |  |             phaseInfo = self.getAppyPhases(currentOnly=True, layoutType='edit') | 
					
						
							| 
									
										
										
										
											2010-11-26 17:30:46 +01:00
										 |  |  |             pageName, pageInfo = self.getNextPage(phaseInfo, pageName) | 
					
						
							| 
									
										
										
										
											2010-10-19 10:47:42 +02:00
										 |  |  |             if pageName: | 
					
						
							| 
									
										
										
										
											2010-09-17 15:32:48 +02:00
										 |  |  |                 # Return to the edit or view page? | 
					
						
							| 
									
										
										
										
											2010-10-19 10:47:42 +02:00
										 |  |  |                 if pageInfo['showOnEdit']: | 
					
						
							|  |  |  |                     rq.set('page', pageName) | 
					
						
							| 
									
										
										
										
											2011-02-15 20:15:58 +01:00
										 |  |  |                     return obj.gotoEdit() | 
					
						
							| 
									
										
										
										
											2010-09-13 21:04:10 +02:00
										 |  |  |                 else: | 
					
						
							| 
									
										
										
										
											2010-10-19 10:47:42 +02:00
										 |  |  |                     return self.goto(obj.getUrl(page=pageName)) | 
					
						
							| 
									
										
										
										
											2010-08-05 18:23:17 +02:00
										 |  |  |             else: | 
					
						
							| 
									
										
										
										
											2011-02-12 17:09:11 +01:00
										 |  |  |                 obj.say(msg) | 
					
						
							| 
									
										
										
										
											2010-09-17 15:32:48 +02:00
										 |  |  |                 return self.goto(obj.getUrl()) | 
					
						
							| 
									
										
										
										
											2011-02-15 20:15:58 +01:00
										 |  |  |         return obj.gotoEdit() | 
					
						
							| 
									
										
										
										
											2009-07-28 10:14:40 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-02-12 17:09:11 +01:00
										 |  |  |     def say(self, msg, type='info'): | 
					
						
							|  |  |  |         '''Prints a p_msg in the user interface. p_logLevel may be "info",
 | 
					
						
							|  |  |  |            "warning" or "error".'''
 | 
					
						
							|  |  |  |         mType = type | 
					
						
							|  |  |  |         if mType == 'warning': mType = 'warn' | 
					
						
							|  |  |  |         elif mType == 'error': mType = 'stop' | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             self.plone_utils.addPortalMessage(msg, type=mType) | 
					
						
							|  |  |  |         except UnicodeDecodeError: | 
					
						
							|  |  |  |             self.plone_utils.addPortalMessage(msg.decode('utf-8'), type=mType) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def log(self, msg, type='info'): | 
					
						
							|  |  |  |         '''Logs a p_msg in the log file. p_logLevel may be "info", "warning"
 | 
					
						
							|  |  |  |            or "error".'''
 | 
					
						
							|  |  |  |         logger = self.getProductConfig().logger | 
					
						
							|  |  |  |         if type == 'warning': logMethod = logger.warn | 
					
						
							|  |  |  |         elif type == 'error': logMethod = logger.error | 
					
						
							|  |  |  |         else: logMethod = logger.info | 
					
						
							|  |  |  |         logMethod(msg) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-12-14 20:22:55 +01:00
										 |  |  |     def rememberPreviousData(self): | 
					
						
							|  |  |  |         '''This method is called before updating an object and remembers, for
 | 
					
						
							|  |  |  |            every historized field, the previous value. Result is a dict | 
					
						
							|  |  |  |            ~{s_fieldName: previousFieldValue}~'''
 | 
					
						
							|  |  |  |         res = {} | 
					
						
							| 
									
										
										
										
											2010-08-05 18:23:17 +02:00
										 |  |  |         for appyType in self.getAllAppyTypes(): | 
					
						
							|  |  |  |             if appyType.historized: | 
					
						
							| 
									
										
										
										
											2010-10-14 14:43:56 +02:00
										 |  |  |                 res[appyType.name] = appyType.getValue(self) | 
					
						
							| 
									
										
										
										
											2009-12-14 20:22:55 +01:00
										 |  |  |         return res | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-12-17 14:46:55 +01:00
										 |  |  |     def addDataChange(self, changes, notForPreviouslyEmptyValues=False): | 
					
						
							| 
									
										
										
										
											2010-01-14 08:56:04 +01:00
										 |  |  |         '''This method allows to add "manually" a data change into the objet's
 | 
					
						
							|  |  |  |            history. Indeed, data changes are "automatically" recorded only when | 
					
						
							|  |  |  |            a HTTP form is uploaded, not if, in the code, a setter is called on | 
					
						
							| 
									
										
										
										
											2010-12-17 14:46:55 +01:00
										 |  |  |            a field. The method is also called by m_historizeData below, that | 
					
						
							|  |  |  |            performs "automatic" recording when a HTTP form is uploaded. Field | 
					
						
							|  |  |  |            changes for which the previous value was empty are not recorded into | 
					
						
							|  |  |  |            the history if p_notForPreviouslyEmptyValues is True.'''
 | 
					
						
							| 
									
										
										
										
											2010-10-14 14:43:56 +02:00
										 |  |  |         # Add to the p_changes dict the field labels | 
					
						
							| 
									
										
										
										
											2010-12-17 14:46:55 +01:00
										 |  |  |         for fieldName in changes.keys(): | 
					
						
							| 
									
										
										
										
											2010-10-14 14:43:56 +02:00
										 |  |  |             appyType = self.getAppyType(fieldName) | 
					
						
							| 
									
										
										
										
											2010-12-17 14:46:55 +01:00
										 |  |  |             if notForPreviouslyEmptyValues and \ | 
					
						
							|  |  |  |                appyType.isEmptyValue(changes[fieldName], self): | 
					
						
							|  |  |  |                 del changes[fieldName] | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 changes[fieldName] = (changes[fieldName], appyType.labelId) | 
					
						
							| 
									
										
										
										
											2010-01-14 08:56:04 +01:00
										 |  |  |         # Create the event to record in the history | 
					
						
							|  |  |  |         DateTime = self.getProductConfig().DateTime | 
					
						
							|  |  |  |         state = self.portal_workflow.getInfoFor(self, 'review_state') | 
					
						
							|  |  |  |         user = self.portal_membership.getAuthenticatedMember() | 
					
						
							|  |  |  |         event = {'action': '_datachange_', 'changes': changes, | 
					
						
							|  |  |  |                  'review_state': state, 'actor': user.id, | 
					
						
							|  |  |  |                  'time': DateTime(), 'comments': ''} | 
					
						
							|  |  |  |         # Add the event to the history | 
					
						
							|  |  |  |         histKey = self.workflow_history.keys()[0] | 
					
						
							|  |  |  |         self.workflow_history[histKey] += (event,) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-12-14 20:22:55 +01:00
										 |  |  |     def historizeData(self, previousData): | 
					
						
							|  |  |  |         '''Records in the object history potential changes on historized fields.
 | 
					
						
							|  |  |  |            p_previousData contains the values, before an update, of the | 
					
						
							|  |  |  |            historized fields, while p_self already contains the (potentially) | 
					
						
							|  |  |  |            modified values.'''
 | 
					
						
							|  |  |  |         # Remove from previousData all values that were not changed | 
					
						
							| 
									
										
										
										
											2010-10-14 14:43:56 +02:00
										 |  |  |         for field in previousData.keys(): | 
					
						
							|  |  |  |             prev = previousData[field] | 
					
						
							|  |  |  |             appyType = self.getAppyType(field) | 
					
						
							|  |  |  |             curr = appyType.getValue(self) | 
					
						
							| 
									
										
										
										
											2009-12-15 21:30:43 +01:00
										 |  |  |             if (prev == curr) or ((prev == None) and (curr == '')) or \ | 
					
						
							|  |  |  |                ((prev == '') and (curr == None)): | 
					
						
							| 
									
										
										
										
											2010-10-14 14:43:56 +02:00
										 |  |  |                 del previousData[field] | 
					
						
							|  |  |  |             if (appyType.type == 'Ref') and (field in previousData): | 
					
						
							| 
									
										
										
										
											2010-10-29 14:36:36 +02:00
										 |  |  |                 previousData[field] = [r.title for r in previousData[field]] | 
					
						
							| 
									
										
										
										
											2009-12-14 20:22:55 +01:00
										 |  |  |         if previousData: | 
					
						
							| 
									
										
										
										
											2010-10-14 14:43:56 +02:00
										 |  |  |             self.addDataChange(previousData) | 
					
						
							| 
									
										
										
										
											2009-12-14 20:22:55 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-01-28 14:36:30 +01:00
										 |  |  |     def goto(self, url, msg=None): | 
					
						
							| 
									
										
										
										
											2009-10-25 21:42:08 +01:00
										 |  |  |         '''Brings the user to some p_url after an action has been executed.''' | 
					
						
							| 
									
										
										
										
											2011-01-28 14:36:30 +01:00
										 |  |  |         if msg: | 
					
						
							| 
									
										
										
										
											2011-02-15 15:59:55 +01:00
										 |  |  |             # Remove previous message if any | 
					
						
							|  |  |  |             if 'portal_status_message=' in url: | 
					
						
							|  |  |  |                 url = url[:url.find('portal_status_message=')-1] | 
					
						
							|  |  |  |             if '?' in url: op = '&' | 
					
						
							|  |  |  |             else: op = '?' | 
					
						
							|  |  |  |             url += op + urllib.urlencode([('portal_status_message',msg)]) | 
					
						
							| 
									
										
										
										
											2010-09-17 15:32:48 +02:00
										 |  |  |         return self.REQUEST.RESPONSE.redirect(url) | 
					
						
							| 
									
										
										
										
											2009-10-25 21:42:08 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-02-15 20:15:58 +01:00
										 |  |  |     def gotoEdit(self): | 
					
						
							|  |  |  |         '''Brings the user to the edit page for this object. This method takes
 | 
					
						
							|  |  |  |            care of not carrying any password value. Unlike m_goto above, there | 
					
						
							|  |  |  |            is no HTTP redirect here: we execute directly macro "edit" and we | 
					
						
							|  |  |  |            return the result.'''
 | 
					
						
							|  |  |  |         page = self.REQUEST.get('page', 'main') | 
					
						
							|  |  |  |         for field in self.getAppyTypes('edit', page): | 
					
						
							|  |  |  |             if (field.type == 'String') and (field.format == 3): | 
					
						
							|  |  |  |                 self.REQUEST.set(field.name, '') | 
					
						
							|  |  |  |         return self.skyn.edit(self) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-08-05 18:23:17 +02:00
										 |  |  |     def showField(self, name, layoutType='view'): | 
					
						
							|  |  |  |         '''Must I show field named p_name on this p_layoutType ?''' | 
					
						
							| 
									
										
										
										
											2011-02-01 11:09:54 +01:00
										 |  |  |         if name == 'state': return False | 
					
						
							| 
									
										
										
										
											2010-08-05 18:23:17 +02:00
										 |  |  |         return self.getAppyType(name).isShowable(self, layoutType) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def getMethod(self, methodName): | 
					
						
							|  |  |  |         '''Returns the method named p_methodName.''' | 
					
						
							|  |  |  |         return getattr(self, methodName, None) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-09-17 15:32:48 +02:00
										 |  |  |     def getFieldValue(self, name, onlyIfSync=False, layoutType=None): | 
					
						
							|  |  |  |         '''Returns the database value of field named p_name for p_self.
 | 
					
						
							|  |  |  |            If p_onlyIfSync is True, it returns the value only if appyType can be | 
					
						
							|  |  |  |            retrieved in synchronous mode.'''
 | 
					
						
							|  |  |  |         appyType = self.getAppyType(name) | 
					
						
							|  |  |  |         if not onlyIfSync or (onlyIfSync and appyType.sync[layoutType]): | 
					
						
							|  |  |  |             return appyType.getValue(self) | 
					
						
							|  |  |  |         return None | 
					
						
							| 
									
										
										
										
											2010-08-05 18:23:17 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-09-17 09:27:14 +02:00
										 |  |  |     def getFormattedFieldValue(self, name, value): | 
					
						
							|  |  |  |         '''Gets a nice, string representation of p_value which is a value from
 | 
					
						
							|  |  |  |            field named p_name.'''
 | 
					
						
							|  |  |  |         return self.getAppyType(name).getFormattedValue(self, value) | 
					
						
							| 
									
										
										
										
											2009-06-29 14:06:01 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-08-27 08:59:53 +02:00
										 |  |  |     def getAppyRefs(self, name, startNumber=None): | 
					
						
							|  |  |  |         '''Gets the objects linked to me through Ref field named p_name.
 | 
					
						
							| 
									
										
										
										
											2009-10-25 21:42:08 +01:00
										 |  |  |            If p_startNumber is None, this method returns all referred objects. | 
					
						
							| 
									
										
										
										
											2010-10-14 14:43:56 +02:00
										 |  |  |            If p_startNumber is a number, this method will return | 
					
						
							|  |  |  |            appyType.maxPerPage objects, starting at p_startNumber.'''
 | 
					
						
							| 
									
										
										
										
											2010-08-27 08:59:53 +02:00
										 |  |  |         appyType = self.getAppyType(name) | 
					
						
							| 
									
										
										
										
											2010-10-14 14:43:56 +02:00
										 |  |  |         return appyType.getValue(self, type='zobjects', someObjects=True, | 
					
						
							|  |  |  |                                  startNumber=startNumber).__dict__ | 
					
						
							| 
									
										
										
										
											2010-08-27 08:59:53 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def getSelectableAppyRefs(self, name): | 
					
						
							|  |  |  |         '''p_name is the name of a Ref field. This method returns the list of
 | 
					
						
							|  |  |  |            all objects that can be selected to be linked as references to p_self | 
					
						
							|  |  |  |            through field p_name.'''
 | 
					
						
							|  |  |  |         appyType = self.getAppyType(name) | 
					
						
							|  |  |  |         if not appyType.select: | 
					
						
							|  |  |  |             # No select method has been defined: we must retrieve all objects | 
					
						
							|  |  |  |             # of the referred type that the user is allowed to access. | 
					
						
							|  |  |  |             return self.appy().search(appyType.klass) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             return appyType.select(self.appy()) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     xhtmlToText = re.compile('<.*?>', re.S) | 
					
						
							|  |  |  |     def getReferenceLabel(self, name, refObject): | 
					
						
							|  |  |  |         '''p_name is the name of a Ref field with link=True. I need to display,
 | 
					
						
							|  |  |  |            on an edit view, the p_refObject in the listbox that will allow | 
					
						
							|  |  |  |            the user to choose which object(s) to link through the Ref. | 
					
						
							|  |  |  |            The information to display may only be the object title or more if | 
					
						
							|  |  |  |            field.shownInfo is used.'''
 | 
					
						
							|  |  |  |         appyType = self.getAppyType(name) | 
					
						
							|  |  |  |         res = refObject.title | 
					
						
							|  |  |  |         if 'title' in appyType.shownInfo: | 
					
						
							|  |  |  |             # We may place it at another place | 
					
						
							|  |  |  |             res = '' | 
					
						
							|  |  |  |         for fieldName in appyType.shownInfo: | 
					
						
							|  |  |  |             refType = refObject.o.getAppyType(fieldName) | 
					
						
							|  |  |  |             value = getattr(refObject, fieldName) | 
					
						
							|  |  |  |             value = refType.getFormattedValue(refObject.o, value) | 
					
						
							| 
									
										
										
										
											2011-01-14 09:06:25 +01:00
										 |  |  |             if refType.type == 'String': | 
					
						
							|  |  |  |                 if refType.format == 2: | 
					
						
							|  |  |  |                     value = self.xhtmlToText.sub(' ', value) | 
					
						
							|  |  |  |                 elif type(value) in sequenceTypes: | 
					
						
							|  |  |  |                     value = ', '.join(value) | 
					
						
							| 
									
										
										
										
											2010-08-27 08:59:53 +02:00
										 |  |  |             prefix = '' | 
					
						
							|  |  |  |             if res: | 
					
						
							|  |  |  |                 prefix = ' | ' | 
					
						
							|  |  |  |             res += prefix + value | 
					
						
							|  |  |  |         maxWidth = appyType.width or 30 | 
					
						
							|  |  |  |         if len(res) > maxWidth: | 
					
						
							|  |  |  |             res = res[:maxWidth-2] + '...' | 
					
						
							|  |  |  |         return res | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def getReferenceUid(self, refObject): | 
					
						
							|  |  |  |         '''Returns the UID of referred object p_refObject.''' | 
					
						
							|  |  |  |         return refObject.o.UID() | 
					
						
							| 
									
										
										
										
											2009-06-29 14:06:01 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def getAppyRefIndex(self, fieldName, obj): | 
					
						
							|  |  |  |         '''Gets the position of p_obj within Ref field named p_fieldName.''' | 
					
						
							| 
									
										
										
										
											2010-02-12 10:59:42 +01:00
										 |  |  |         sortedObjectsUids = self._appy_getSortedField(fieldName) | 
					
						
							| 
									
										
										
										
											2009-06-29 14:06:01 +02:00
										 |  |  |         res = sortedObjectsUids.index(obj.UID()) | 
					
						
							|  |  |  |         return res | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-11-13 17:54:08 +01:00
										 |  |  |     def isDebug(self): | 
					
						
							|  |  |  |         '''Are we in debug mode ?''' | 
					
						
							|  |  |  |         for arg in sys.argv: | 
					
						
							|  |  |  |             if arg == 'debug-mode=on': return True | 
					
						
							|  |  |  |         return False | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def getClass(self, reloaded=False): | 
					
						
							|  |  |  |         '''Returns the Appy class that dictates self's behaviour.''' | 
					
						
							|  |  |  |         if not reloaded: | 
					
						
							|  |  |  |             return self.getTool().getAppyClass(self.__class__.__name__) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             klass = self.appy().klass | 
					
						
							|  |  |  |             moduleName = klass.__module__ | 
					
						
							|  |  |  |             exec 'import %s' % moduleName | 
					
						
							|  |  |  |             exec 'reload(%s)' % moduleName | 
					
						
							|  |  |  |             exec 'res = %s.%s' % (moduleName, klass.__name__) | 
					
						
							| 
									
										
										
										
											2010-11-16 15:32:47 +01:00
										 |  |  |             # More manipulations may have occurred in m_update | 
					
						
							|  |  |  |             if hasattr(res, 'update'): | 
					
						
							|  |  |  |                 parentName = res.__bases__[-1].__name__ | 
					
						
							|  |  |  |                 moduleName = 'Products.%s.Extensions.appyWrappers' % \ | 
					
						
							|  |  |  |                              self.getTool().getAppName() | 
					
						
							|  |  |  |                 exec 'import %s' % moduleName | 
					
						
							|  |  |  |                 exec 'parent = %s.%s' % (moduleName, parentName) | 
					
						
							|  |  |  |                 res.update(parent) | 
					
						
							| 
									
										
										
										
											2010-11-13 17:54:08 +01:00
										 |  |  |             return res | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-08-05 18:23:17 +02:00
										 |  |  |     def getAppyType(self, name, asDict=False, className=None): | 
					
						
							|  |  |  |         '''Returns the Appy type named p_name. If no p_className is defined, the
 | 
					
						
							|  |  |  |            field is supposed to belong to self's class.''' | 
					
						
							|  |  |  |         className = className or self.__class__.__name__ | 
					
						
							|  |  |  |         attrs = self.getProductConfig().attributesDict[className] | 
					
						
							|  |  |  |         appyType = attrs.get(name, None) | 
					
						
							|  |  |  |         if appyType and asDict: return appyType.__dict__ | 
					
						
							|  |  |  |         return appyType | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def getAllAppyTypes(self, className=None): | 
					
						
							|  |  |  |         '''Returns the ordered list of all Appy types for self's class if
 | 
					
						
							|  |  |  |            p_className is not specified, or for p_className else.'''
 | 
					
						
							|  |  |  |         className = className or self.__class__.__name__ | 
					
						
							|  |  |  |         return self.getProductConfig().attributes[className] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-10-19 10:47:42 +02:00
										 |  |  |     def getGroupedAppyTypes(self, layoutType, pageName): | 
					
						
							| 
									
										
										
										
											2010-08-05 18:23:17 +02:00
										 |  |  |         '''Returns the fields sorted by group. For every field, the appyType
 | 
					
						
							|  |  |  |            (dict version) is given.'''
 | 
					
						
							| 
									
										
										
										
											2009-06-29 14:06:01 +02:00
										 |  |  |         res = [] | 
					
						
							| 
									
										
										
										
											2010-08-05 18:23:17 +02:00
										 |  |  |         groups = {} # The already encountered groups | 
					
						
							| 
									
										
										
										
											2010-11-13 17:54:08 +01:00
										 |  |  |         # In debug mode, reload the module containing self's class. | 
					
						
							|  |  |  |         debug = self.isDebug() | 
					
						
							|  |  |  |         if debug: | 
					
						
							|  |  |  |             klass = self.getClass(reloaded=True) | 
					
						
							| 
									
										
										
										
											2010-08-05 18:23:17 +02:00
										 |  |  |         for appyType in self.getAllAppyTypes(): | 
					
						
							| 
									
										
										
										
											2010-11-13 17:54:08 +01:00
										 |  |  |             if debug: | 
					
						
							|  |  |  |                 appyType = appyType.reload(klass, self) | 
					
						
							| 
									
										
										
										
											2010-10-19 10:47:42 +02:00
										 |  |  |             if appyType.page.name != pageName: continue | 
					
						
							| 
									
										
										
										
											2010-08-05 18:23:17 +02:00
										 |  |  |             if not appyType.isShowable(self, layoutType): continue | 
					
						
							|  |  |  |             if not appyType.group: | 
					
						
							|  |  |  |                 res.append(appyType.__dict__) | 
					
						
							| 
									
										
										
										
											2009-06-29 14:06:01 +02:00
										 |  |  |             else: | 
					
						
							| 
									
										
										
										
											2010-08-05 18:23:17 +02:00
										 |  |  |                 # Insert the GroupDescr instance corresponding to | 
					
						
							|  |  |  |                 # appyType.group at the right place | 
					
						
							|  |  |  |                 groupDescr = appyType.group.insertInto(res, groups, | 
					
						
							|  |  |  |                     appyType.page, self.meta_type) | 
					
						
							|  |  |  |                 GroupDescr.addWidget(groupDescr, appyType.__dict__) | 
					
						
							| 
									
										
										
										
											2009-06-29 14:06:01 +02:00
										 |  |  |         return res | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-10-19 10:47:42 +02:00
										 |  |  |     def getAppyTypes(self, layoutType, pageName): | 
					
						
							| 
									
										
										
										
											2010-08-05 18:23:17 +02:00
										 |  |  |         '''Returns the list of appyTypes that belong to a given p_page, for a
 | 
					
						
							|  |  |  |            given p_layoutType.'''
 | 
					
						
							| 
									
										
										
										
											2009-06-29 14:06:01 +02:00
										 |  |  |         res = [] | 
					
						
							| 
									
										
										
										
											2010-08-05 18:23:17 +02:00
										 |  |  |         for appyType in self.getAllAppyTypes(): | 
					
						
							| 
									
										
										
										
											2010-10-19 10:47:42 +02:00
										 |  |  |             if appyType.page.name != pageName: continue | 
					
						
							| 
									
										
										
										
											2010-08-05 18:23:17 +02:00
										 |  |  |             if not appyType.isShowable(self, layoutType): continue | 
					
						
							|  |  |  |             res.append(appyType) | 
					
						
							| 
									
										
										
										
											2009-06-29 14:06:01 +02:00
										 |  |  |         return res | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-12-17 14:46:55 +01:00
										 |  |  |     def getCssAndJs(self, fields, layoutType): | 
					
						
							|  |  |  |         '''Gets the list of Javascript and CSS files required by Appy types
 | 
					
						
							|  |  |  |            p_fields when shown on p_layoutType.'''
 | 
					
						
							|  |  |  |         # lists css and js below are not sets, because order of Javascript | 
					
						
							|  |  |  |         # inclusion can be important, and this could be losed by using sets. | 
					
						
							| 
									
										
										
										
											2010-08-05 18:23:17 +02:00
										 |  |  |         css = [] | 
					
						
							|  |  |  |         js = [] | 
					
						
							| 
									
										
										
										
											2010-12-17 14:46:55 +01:00
										 |  |  |         for field in fields: | 
					
						
							|  |  |  |             fieldCss = field.getCss(layoutType) | 
					
						
							|  |  |  |             if fieldCss: | 
					
						
							|  |  |  |                 for fcss in fieldCss: | 
					
						
							|  |  |  |                     if fcss not in css: css.append(fcss) | 
					
						
							|  |  |  |             fieldJs = field.getJs(layoutType) | 
					
						
							|  |  |  |             if fieldJs: | 
					
						
							|  |  |  |                 for fjs in fieldJs: | 
					
						
							|  |  |  |                     if fjs not in js: js.append(fjs) | 
					
						
							|  |  |  |         return {'css':css, 'js':js} | 
					
						
							| 
									
										
										
										
											2010-08-05 18:23:17 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-12-06 11:11:40 +01:00
										 |  |  |     def getAppyTypesFromNames(self, fieldNames, asDict=True, addTitle=True): | 
					
						
							|  |  |  |         '''Gets the Appy types named p_fieldNames. If 'title' is not among
 | 
					
						
							|  |  |  |            p_fieldNames and p_addTitle is True, field 'title' is prepended to | 
					
						
							|  |  |  |            the result.'''
 | 
					
						
							|  |  |  |         res = [] | 
					
						
							|  |  |  |         for name in fieldNames: | 
					
						
							|  |  |  |             appyType = self.getAppyType(name, asDict) | 
					
						
							|  |  |  |             if appyType: res.append(appyType) | 
					
						
							| 
									
										
										
										
											2011-01-19 20:51:43 +01:00
										 |  |  |             elif name == 'state': | 
					
						
							| 
									
										
										
										
											2010-12-06 11:11:40 +01:00
										 |  |  |                 # We do not return a appyType if the attribute is not a *real* | 
					
						
							|  |  |  |                 # attribute, but the workfow state. | 
					
						
							|  |  |  |                 res.append({'name': name, 'labelId': 'workflow_state', | 
					
						
							|  |  |  |                             'filterable': False}) | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 self.appy().log('Field "%s", used as shownInfo in a Ref, ' \ | 
					
						
							|  |  |  |                                 'was not found.' % name, type='warning') | 
					
						
							|  |  |  |         if addTitle and ('title' not in fieldNames): | 
					
						
							|  |  |  |             res.insert(0, self.getAppyType('title', asDict)) | 
					
						
							|  |  |  |         return res | 
					
						
							| 
									
										
										
										
											2010-08-05 18:23:17 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-06-29 14:06:01 +02:00
										 |  |  |     def getAppyStates(self, phase, currentOnly=False): | 
					
						
							|  |  |  |         '''Returns information about the states that are related to p_phase.
 | 
					
						
							|  |  |  |            If p_currentOnly is True, we return the current state, even if not | 
					
						
							|  |  |  |            related to p_phase.'''
 | 
					
						
							|  |  |  |         res = [] | 
					
						
							|  |  |  |         dcWorkflow = self.getWorkflow(appy=False) | 
					
						
							|  |  |  |         if not dcWorkflow: return res | 
					
						
							|  |  |  |         currentState = self.portal_workflow.getInfoFor(self, 'review_state') | 
					
						
							|  |  |  |         if currentOnly: | 
					
						
							|  |  |  |             return [StateDescr(currentState,'current').get()] | 
					
						
							|  |  |  |         workflow = self.getWorkflow(appy=True) | 
					
						
							|  |  |  |         if workflow: | 
					
						
							|  |  |  |             stateStatus = 'done' | 
					
						
							|  |  |  |             for stateName in workflow._states: | 
					
						
							|  |  |  |                 if stateName == currentState: | 
					
						
							|  |  |  |                     stateStatus = 'current' | 
					
						
							|  |  |  |                 elif stateStatus != 'done': | 
					
						
							|  |  |  |                     stateStatus = 'future' | 
					
						
							|  |  |  |                 state = getattr(workflow, stateName) | 
					
						
							|  |  |  |                 if (state.phase == phase) and \ | 
					
						
							|  |  |  |                    (self._appy_showState(workflow, state.show)): | 
					
						
							|  |  |  |                     res.append(StateDescr(stateName, stateStatus).get()) | 
					
						
							|  |  |  |         return res | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-02-01 11:09:54 +01:00
										 |  |  |     def getAppyTransitions(self, includeFake=True, includeNotShowable=False): | 
					
						
							|  |  |  |         '''This method is similar to portal_workflow.getTransitionsFor, but:
 | 
					
						
							|  |  |  |            * is able (or not, depending on boolean p_includeFake) to retrieve | 
					
						
							|  |  |  |              transitions that the user can't trigger, but for which he needs to | 
					
						
							|  |  |  |              know for what reason he can't trigger it; | 
					
						
							|  |  |  |            * is able (or not, depending on p_includeNotShowable) to include | 
					
						
							|  |  |  |              transitions for which show=False at the Appy level. Indeed, because | 
					
						
							|  |  |  |              "showability" is only a GUI concern, and not a security concern, | 
					
						
							|  |  |  |              in some cases it has sense to set includeNotShowable=True, because | 
					
						
							|  |  |  |              those transitions are triggerable from a security point of view; | 
					
						
							|  |  |  |            * the transition-info is richer: it contains fake-related info (as | 
					
						
							|  |  |  |              described above) and confirm-related info (ie, when clicking on | 
					
						
							|  |  |  |              the button, do we ask the user to confirm via a popup?)'''
 | 
					
						
							| 
									
										
										
										
											2009-11-06 11:33:56 +01:00
										 |  |  |         res = [] | 
					
						
							| 
									
										
										
										
											2011-02-01 11:09:54 +01:00
										 |  |  |         # Get some Plone stuff from the Plone-level config.py | 
					
						
							|  |  |  |         TRIGGER_USER_ACTION = self.getProductConfig().TRIGGER_USER_ACTION | 
					
						
							|  |  |  |         sm = self.getProductConfig().getSecurityManager | 
					
						
							|  |  |  |         # Get the workflow definition for p_obj. | 
					
						
							|  |  |  |         workflow = self.getWorkflow(appy=False) | 
					
						
							|  |  |  |         if not workflow: return res | 
					
						
							|  |  |  |         appyWorkflow = self.getWorkflow(appy=True) | 
					
						
							|  |  |  |         # What is the current state for this object? | 
					
						
							|  |  |  |         currentState = workflow._getWorkflowStateOf(self) | 
					
						
							|  |  |  |         if not currentState: return res | 
					
						
							|  |  |  |         # Analyse all the transitions that start from this state. | 
					
						
							|  |  |  |         for transitionId in currentState.transitions: | 
					
						
							|  |  |  |             transition = workflow.transitions.get(transitionId, None) | 
					
						
							|  |  |  |             appyTr = appyWorkflow._transitionsMapping[transitionId] | 
					
						
							|  |  |  |             if not transition or (transition.trigger_type!=TRIGGER_USER_ACTION)\ | 
					
						
							|  |  |  |                or not transition.actbox_name: continue | 
					
						
							|  |  |  |             # We have a possible candidate for a user-triggerable transition | 
					
						
							|  |  |  |             if transition.guard is None: | 
					
						
							|  |  |  |                 mayTrigger = True | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 mayTrigger = checkTransitionGuard(transition.guard, sm(), | 
					
						
							|  |  |  |                                                   workflow, self) | 
					
						
							|  |  |  |             # Compute the condition that will lead to including or not this | 
					
						
							|  |  |  |             # transition | 
					
						
							|  |  |  |             if not includeFake: | 
					
						
							|  |  |  |                 includeIt = mayTrigger | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 includeIt = mayTrigger or isinstance(mayTrigger, No) | 
					
						
							|  |  |  |             if not includeNotShowable: | 
					
						
							|  |  |  |                 includeIt = includeIt and appyTr.isShowable(appyWorkflow, self) | 
					
						
							|  |  |  |             if not includeIt: continue | 
					
						
							|  |  |  |             # Add transition-info to the result. | 
					
						
							|  |  |  |             tInfo = {'id': transition.id, 'title': transition.title, | 
					
						
							|  |  |  |                      'title_or_id': transition.title_or_id(), | 
					
						
							|  |  |  |                      'description': transition.description, 'confirm': '', | 
					
						
							|  |  |  |                      'name': transition.actbox_name, 'may_trigger': True, | 
					
						
							|  |  |  |                      'url': transition.actbox_url % | 
					
						
							|  |  |  |                         {'content_url': self.absolute_url(), | 
					
						
							|  |  |  |                          'portal_url' : '', 'folder_url' : ''}} | 
					
						
							|  |  |  |             if appyTr.confirm: | 
					
						
							|  |  |  |                 label = '%s_confirm' % tInfo['name'] | 
					
						
							|  |  |  |                 tInfo['confirm'] = self.translate(label, format='js') | 
					
						
							|  |  |  |             if not mayTrigger: | 
					
						
							|  |  |  |                 tInfo['may_trigger'] = False | 
					
						
							|  |  |  |                 tInfo['reason'] = mayTrigger.msg | 
					
						
							|  |  |  |             res.append(tInfo) | 
					
						
							| 
									
										
										
										
											2009-11-06 11:33:56 +01:00
										 |  |  |         return res | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-10-19 10:47:42 +02:00
										 |  |  |     def getAppyPhases(self, currentOnly=False, layoutType='view'): | 
					
						
							| 
									
										
										
										
											2009-06-29 14:06:01 +02:00
										 |  |  |         '''Gets the list of phases that are defined for this content type. If
 | 
					
						
							| 
									
										
										
										
											2010-10-19 10:47:42 +02:00
										 |  |  |            p_currentOnly is True, the search is limited to the phase where the | 
					
						
							|  |  |  |            current page (as defined in the request) lies.'''
 | 
					
						
							| 
									
										
										
										
											2009-06-29 14:06:01 +02:00
										 |  |  |         # Get the list of phases | 
					
						
							|  |  |  |         res = [] # Ordered list of phases | 
					
						
							|  |  |  |         phases = {} # Dict of phases | 
					
						
							| 
									
										
										
										
											2010-08-05 18:23:17 +02:00
										 |  |  |         for appyType in self.getAllAppyTypes(): | 
					
						
							| 
									
										
										
										
											2010-10-19 10:47:42 +02:00
										 |  |  |             typePhase = appyType.page.phase | 
					
						
							|  |  |  |             if typePhase not in phases: | 
					
						
							|  |  |  |                 states = self.getAppyStates(typePhase) | 
					
						
							|  |  |  |                 phase = PhaseDescr(typePhase, states, self) | 
					
						
							| 
									
										
										
										
											2009-06-29 14:06:01 +02:00
										 |  |  |                 res.append(phase.__dict__) | 
					
						
							| 
									
										
										
										
											2010-10-19 10:47:42 +02:00
										 |  |  |                 phases[typePhase] = phase | 
					
						
							| 
									
										
										
										
											2009-06-29 14:06:01 +02:00
										 |  |  |             else: | 
					
						
							| 
									
										
										
										
											2010-10-19 10:47:42 +02:00
										 |  |  |                 phase = phases[typePhase] | 
					
						
							|  |  |  |             phase.addPage(appyType, self, layoutType) | 
					
						
							| 
									
										
										
										
											2009-06-29 14:06:01 +02:00
										 |  |  |         # Remove phases that have no visible page | 
					
						
							|  |  |  |         for i in range(len(res)-1, -1, -1): | 
					
						
							|  |  |  |             if not res[i]['pages']: | 
					
						
							|  |  |  |                 del phases[res[i]['name']] | 
					
						
							|  |  |  |                 del res[i] | 
					
						
							|  |  |  |         # Then, compute status of phases | 
					
						
							|  |  |  |         for ph in phases.itervalues(): | 
					
						
							| 
									
										
										
										
											2010-08-05 18:23:17 +02:00
										 |  |  |             ph.computeStatus(res) | 
					
						
							| 
									
										
										
										
											2009-06-29 14:06:01 +02:00
										 |  |  |             ph.totalNbOfPhases = len(res) | 
					
						
							| 
									
										
										
										
											2010-10-19 10:47:42 +02:00
										 |  |  |         # Restrict the result to the current phase if required | 
					
						
							| 
									
										
										
										
											2009-06-29 14:06:01 +02:00
										 |  |  |         if currentOnly: | 
					
						
							| 
									
										
										
										
											2010-10-19 10:47:42 +02:00
										 |  |  |             rq = self.REQUEST | 
					
						
							|  |  |  |             page = rq.get('page', 'main') | 
					
						
							| 
									
										
										
										
											2009-06-29 14:06:01 +02:00
										 |  |  |             for phaseInfo in res: | 
					
						
							| 
									
										
										
										
											2010-08-05 18:23:17 +02:00
										 |  |  |                 if page in phaseInfo['pages']: | 
					
						
							| 
									
										
										
										
											2009-06-29 14:06:01 +02:00
										 |  |  |                     return phaseInfo | 
					
						
							| 
									
										
										
										
											2010-10-19 10:47:42 +02:00
										 |  |  |             # If I am here, it means that the page as defined in the request, | 
					
						
							|  |  |  |             # or 'main' by default, is not existing nor visible in any phase. | 
					
						
							| 
									
										
										
										
											2010-10-29 14:36:36 +02:00
										 |  |  |             # In this case I find the first visible page among all phases. | 
					
						
							|  |  |  |             viewAttr = 'showOn%s' % layoutType.capitalize() | 
					
						
							|  |  |  |             for phase in res: | 
					
						
							|  |  |  |                 for page in phase['pages']: | 
					
						
							|  |  |  |                     if phase['pagesInfo'][page][viewAttr]: | 
					
						
							|  |  |  |                         rq.set('page', page) | 
					
						
							|  |  |  |                         pageFound = True | 
					
						
							|  |  |  |                         break | 
					
						
							|  |  |  |             return phase | 
					
						
							| 
									
										
										
										
											2009-06-29 14:06:01 +02:00
										 |  |  |         else: | 
					
						
							|  |  |  |             return res | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-08-05 18:23:17 +02:00
										 |  |  |     def getPreviousPage(self, phase, page): | 
					
						
							|  |  |  |         '''Returns the page that precedes p_page which is in p_phase.''' | 
					
						
							| 
									
										
										
										
											2010-11-26 17:30:46 +01:00
										 |  |  |         try: | 
					
						
							|  |  |  |             pageIndex = phase['pages'].index(page) | 
					
						
							|  |  |  |         except ValueError: | 
					
						
							|  |  |  |             # The current page is probably not visible anymore. Return the | 
					
						
							|  |  |  |             # first available page in current phase. | 
					
						
							|  |  |  |             res = phase['pages'][0] | 
					
						
							|  |  |  |             return res, phase['pagesInfo'][res] | 
					
						
							| 
									
										
										
										
											2010-08-05 18:23:17 +02:00
										 |  |  |         if pageIndex > 0: | 
					
						
							|  |  |  |             # We stay on the same phase, previous page | 
					
						
							| 
									
										
										
										
											2010-09-13 21:04:10 +02:00
										 |  |  |             res = phase['pages'][pageIndex-1] | 
					
						
							| 
									
										
										
										
											2010-10-19 10:47:42 +02:00
										 |  |  |             resInfo = phase['pagesInfo'][res] | 
					
						
							|  |  |  |             return res, resInfo | 
					
						
							| 
									
										
										
										
											2010-08-05 18:23:17 +02:00
										 |  |  |         else: | 
					
						
							|  |  |  |             if phase['previousPhase']: | 
					
						
							|  |  |  |                 # We go to the last page of previous phase | 
					
						
							|  |  |  |                 previousPhase = phase['previousPhase'] | 
					
						
							| 
									
										
										
										
											2010-09-13 21:04:10 +02:00
										 |  |  |                 res = previousPhase['pages'][-1] | 
					
						
							| 
									
										
										
										
											2010-10-19 10:47:42 +02:00
										 |  |  |                 resInfo = previousPhase['pagesInfo'][res] | 
					
						
							|  |  |  |                 return res, resInfo | 
					
						
							| 
									
										
										
										
											2010-08-05 18:23:17 +02:00
										 |  |  |             else: | 
					
						
							| 
									
										
										
										
											2010-09-13 21:04:10 +02:00
										 |  |  |                 return None, None | 
					
						
							| 
									
										
										
										
											2010-08-05 18:23:17 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def getNextPage(self, phase, page): | 
					
						
							|  |  |  |         '''Returns the page that follows p_page which is in p_phase.''' | 
					
						
							| 
									
										
										
										
											2010-11-26 17:30:46 +01:00
										 |  |  |         try: | 
					
						
							|  |  |  |             pageIndex = phase['pages'].index(page) | 
					
						
							|  |  |  |         except ValueError: | 
					
						
							|  |  |  |             # The current page is probably not visible anymore. Return the | 
					
						
							|  |  |  |             # first available page in current phase. | 
					
						
							|  |  |  |             res = phase['pages'][0] | 
					
						
							|  |  |  |             return res, phase['pagesInfo'][res] | 
					
						
							| 
									
										
										
										
											2010-08-05 18:23:17 +02:00
										 |  |  |         if pageIndex < len(phase['pages'])-1: | 
					
						
							|  |  |  |             # We stay on the same phase, next page | 
					
						
							| 
									
										
										
										
											2010-09-13 21:04:10 +02:00
										 |  |  |             res = phase['pages'][pageIndex+1] | 
					
						
							| 
									
										
										
										
											2010-10-19 10:47:42 +02:00
										 |  |  |             resInfo = phase['pagesInfo'][res] | 
					
						
							|  |  |  |             return res, resInfo | 
					
						
							| 
									
										
										
										
											2010-08-05 18:23:17 +02:00
										 |  |  |         else: | 
					
						
							|  |  |  |             if phase['nextPhase']: | 
					
						
							|  |  |  |                 # We go to the first page of next phase | 
					
						
							|  |  |  |                 nextPhase = phase['nextPhase'] | 
					
						
							| 
									
										
										
										
											2010-09-13 21:04:10 +02:00
										 |  |  |                 res = nextPhase['pages'][0] | 
					
						
							| 
									
										
										
										
											2010-10-19 10:47:42 +02:00
										 |  |  |                 resInfo = nextPhase['pagesInfo'][res] | 
					
						
							|  |  |  |                 return res, resInfo | 
					
						
							| 
									
										
										
										
											2010-08-05 18:23:17 +02:00
										 |  |  |             else: | 
					
						
							| 
									
										
										
										
											2010-09-13 21:04:10 +02:00
										 |  |  |                 return None, None | 
					
						
							| 
									
										
										
										
											2010-08-05 18:23:17 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-10-20 16:57:00 +02:00
										 |  |  |     def changeRefOrder(self, fieldName, objectUid, newIndex, isDelta): | 
					
						
							| 
									
										
										
										
											2009-06-29 14:06:01 +02:00
										 |  |  |         '''This method changes the position of object with uid p_objectUid in
 | 
					
						
							|  |  |  |            reference field p_fieldName to p_newIndex i p_isDelta is False, or | 
					
						
							|  |  |  |            to actualIndex+p_newIndex if p_isDelta is True.'''
 | 
					
						
							| 
									
										
										
										
											2010-02-12 10:59:42 +01:00
										 |  |  |         sortedObjectsUids = self._appy_getSortedField(fieldName) | 
					
						
							| 
									
										
										
										
											2009-06-29 14:06:01 +02:00
										 |  |  |         oldIndex = sortedObjectsUids.index(objectUid) | 
					
						
							|  |  |  |         sortedObjectsUids.remove(objectUid) | 
					
						
							|  |  |  |         if isDelta: | 
					
						
							|  |  |  |             newIndex = oldIndex + newIndex | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             pass # To implement later on | 
					
						
							|  |  |  |         sortedObjectsUids.insert(newIndex, objectUid) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-10-20 16:57:00 +02:00
										 |  |  |     def onChangeRefOrder(self): | 
					
						
							|  |  |  |         '''This method is called when the user wants to change order of an
 | 
					
						
							|  |  |  |            item in a reference field.'''
 | 
					
						
							|  |  |  |         rq = self.REQUEST | 
					
						
							| 
									
										
										
										
											2009-10-25 21:42:08 +01:00
										 |  |  |         # Move the item up (-1), down (+1) ? | 
					
						
							| 
									
										
										
										
											2009-10-20 16:57:00 +02:00
										 |  |  |         move = -1 # Move up | 
					
						
							| 
									
										
										
										
											2009-10-25 21:42:08 +01:00
										 |  |  |         if rq['move'] == 'down': | 
					
						
							|  |  |  |             move = 1 # Down | 
					
						
							| 
									
										
										
										
											2009-10-20 16:57:00 +02:00
										 |  |  |         isDelta = True | 
					
						
							|  |  |  |         self.changeRefOrder(rq['fieldName'], rq['refObjectUid'], move, isDelta) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-04-30 12:05:29 +02:00
										 |  |  |     def onSortReference(self): | 
					
						
							|  |  |  |         '''This method is called when the user wants to sort the content of a
 | 
					
						
							|  |  |  |            reference field.'''
 | 
					
						
							|  |  |  |         rq = self.REQUEST | 
					
						
							|  |  |  |         fieldName = rq.get('fieldName') | 
					
						
							|  |  |  |         sortKey = rq.get('sortKey') | 
					
						
							|  |  |  |         reverse = rq.get('reverse') == 'True' | 
					
						
							|  |  |  |         self.appy().sort(fieldName, sortKey=sortKey, reverse=reverse) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-06-29 14:06:01 +02:00
										 |  |  |     def getWorkflow(self, appy=True): | 
					
						
							|  |  |  |         '''Returns the Appy workflow instance that is relevant for this
 | 
					
						
							|  |  |  |            object. If p_appy is False, it returns the DC workflow.'''
 | 
					
						
							|  |  |  |         res = None | 
					
						
							|  |  |  |         if appy: | 
					
						
							|  |  |  |             # Get the workflow class first | 
					
						
							|  |  |  |             workflowClass = None | 
					
						
							|  |  |  |             if self.wrapperClass: | 
					
						
							| 
									
										
										
										
											2010-08-05 18:23:17 +02:00
										 |  |  |                 appyClass = self.wrapperClass.__bases__[-1] | 
					
						
							| 
									
										
										
										
											2009-06-29 14:06:01 +02:00
										 |  |  |                 if hasattr(appyClass, 'workflow'): | 
					
						
							|  |  |  |                     workflowClass = appyClass.workflow | 
					
						
							|  |  |  |             if workflowClass: | 
					
						
							|  |  |  |                 # Get the corresponding prototypical workflow instance | 
					
						
							|  |  |  |                 res = self.getProductConfig().workflowInstances[workflowClass] | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             dcWorkflows = self.portal_workflow.getWorkflowsFor(self) | 
					
						
							|  |  |  |             if dcWorkflows: | 
					
						
							|  |  |  |                 res = dcWorkflows[0] | 
					
						
							|  |  |  |         return res | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def getWorkflowLabel(self, stateName=None): | 
					
						
							|  |  |  |         '''Gets the i18n label for the workflow current state. If no p_stateName
 | 
					
						
							|  |  |  |            is given, workflow label is given for the current state.'''
 | 
					
						
							|  |  |  |         res = '' | 
					
						
							|  |  |  |         wf = self.getWorkflow(appy=False) | 
					
						
							|  |  |  |         if wf: | 
					
						
							|  |  |  |             res = stateName | 
					
						
							|  |  |  |             if not res: | 
					
						
							|  |  |  |                 res = self.portal_workflow.getInfoFor(self, 'review_state') | 
					
						
							|  |  |  |             appyWf = self.getWorkflow(appy=True) | 
					
						
							|  |  |  |             if appyWf: | 
					
						
							|  |  |  |                 res = '%s_%s' % (wf.id, res) | 
					
						
							|  |  |  |         return res | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-12-14 20:22:55 +01:00
										 |  |  |     def hasHistory(self): | 
					
						
							|  |  |  |         '''Has this object an history?''' | 
					
						
							|  |  |  |         if hasattr(self.aq_base, 'workflow_history') and self.workflow_history: | 
					
						
							|  |  |  |             key = self.workflow_history.keys()[0] | 
					
						
							|  |  |  |             for event in self.workflow_history[key]: | 
					
						
							|  |  |  |                 if event['action'] and (event['comments'] != '_invisible_'): | 
					
						
							|  |  |  |                     return True | 
					
						
							|  |  |  |         return False | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-01-29 02:18:14 +01:00
										 |  |  |     def getHistory(self, startNumber=0, reverse=True, includeInvisible=False, | 
					
						
							|  |  |  |                    batchSize=5): | 
					
						
							| 
									
										
										
										
											2009-12-14 20:22:55 +01:00
										 |  |  |         '''Returns the history for this object, sorted in reverse order (most
 | 
					
						
							|  |  |  |            recent change first) if p_reverse is True.'''
 | 
					
						
							|  |  |  |         key = self.workflow_history.keys()[0] | 
					
						
							|  |  |  |         history = list(self.workflow_history[key][1:]) | 
					
						
							|  |  |  |         if not includeInvisible: | 
					
						
							|  |  |  |             history = [e for e in history if e['comments'] != '_invisible_'] | 
					
						
							|  |  |  |         if reverse: history.reverse() | 
					
						
							|  |  |  |         return {'events': history[startNumber:startNumber+batchSize], | 
					
						
							| 
									
										
										
										
											2011-01-29 02:18:14 +01:00
										 |  |  |                 'totalNumber': len(history)} | 
					
						
							| 
									
										
										
										
											2009-12-14 20:22:55 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-06-29 14:06:01 +02:00
										 |  |  |     def may(self, transitionName): | 
					
						
							|  |  |  |         '''May the user execute transition named p_transitionName?''' | 
					
						
							|  |  |  |         # Get the Appy workflow instance | 
					
						
							|  |  |  |         workflow = self.getWorkflow() | 
					
						
							|  |  |  |         res = False | 
					
						
							|  |  |  |         if workflow: | 
					
						
							|  |  |  |             # Get the corresponding Appy transition | 
					
						
							|  |  |  |             transition = workflow._transitionsMapping[transitionName] | 
					
						
							|  |  |  |             user = self.portal_membership.getAuthenticatedMember() | 
					
						
							| 
									
										
										
										
											2010-09-02 16:16:08 +02:00
										 |  |  |             if isinstance(transition.condition, Role): | 
					
						
							| 
									
										
										
										
											2009-06-29 14:06:01 +02:00
										 |  |  |                 # It is a role. Transition may be triggered if the user has this | 
					
						
							|  |  |  |                 # role. | 
					
						
							| 
									
										
										
										
											2010-09-02 16:16:08 +02:00
										 |  |  |                 res = user.has_role(transition.condition.name, self) | 
					
						
							| 
									
										
										
										
											2009-06-29 14:06:01 +02:00
										 |  |  |             elif type(transition.condition) == types.FunctionType: | 
					
						
							| 
									
										
										
										
											2010-02-08 08:53:30 +01:00
										 |  |  |                 res = transition.condition(workflow, self.appy()) | 
					
						
							| 
									
										
										
										
											2009-06-29 14:06:01 +02:00
										 |  |  |             elif type(transition.condition) in (tuple, list): | 
					
						
							|  |  |  |                 # It is a list of roles and or functions. Transition may be | 
					
						
							|  |  |  |                 # triggered if user has at least one of those roles and if all | 
					
						
							|  |  |  |                 # functions return True. | 
					
						
							|  |  |  |                 hasRole = None | 
					
						
							|  |  |  |                 for roleOrFunction in transition.condition: | 
					
						
							|  |  |  |                     if isinstance(roleOrFunction, basestring): | 
					
						
							|  |  |  |                         if hasRole == None: | 
					
						
							|  |  |  |                             hasRole = False | 
					
						
							|  |  |  |                         if user.has_role(roleOrFunction, self): | 
					
						
							|  |  |  |                             hasRole = True | 
					
						
							|  |  |  |                     elif type(roleOrFunction) == types.FunctionType: | 
					
						
							| 
									
										
										
										
											2010-02-08 08:53:30 +01:00
										 |  |  |                         if not roleOrFunction(workflow, self.appy()): | 
					
						
							| 
									
										
										
										
											2009-06-29 14:06:01 +02:00
										 |  |  |                             return False | 
					
						
							|  |  |  |                 if hasRole != False: | 
					
						
							|  |  |  |                     res = True | 
					
						
							|  |  |  |         return res | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-12-17 14:46:55 +01:00
										 |  |  |     def mayNavigate(self): | 
					
						
							|  |  |  |         '''May the currently logged user see the navigation panel linked to
 | 
					
						
							|  |  |  |            this object?'''
 | 
					
						
							|  |  |  |         appyObj = self.appy() | 
					
						
							|  |  |  |         if hasattr(appyObj, 'mayNavigate'): return appyObj.mayNavigate() | 
					
						
							|  |  |  |         return True | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def mayDelete(self): | 
					
						
							|  |  |  |         '''May the currently logged user delete this object? This condition
 | 
					
						
							|  |  |  |            comes as an addition/refinement to the corresponding workflow | 
					
						
							|  |  |  |            permission.'''
 | 
					
						
							|  |  |  |         appyObj = self.appy() | 
					
						
							|  |  |  |         if hasattr(appyObj, 'mayDelete'): return appyObj.mayDelete() | 
					
						
							|  |  |  |         return True | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-06-29 14:06:01 +02:00
										 |  |  |     def executeAppyAction(self, actionName, reindex=True): | 
					
						
							|  |  |  |         '''Executes action with p_fieldName on this object.''' | 
					
						
							| 
									
										
										
										
											2010-08-05 18:23:17 +02:00
										 |  |  |         appyType = self.getAppyType(actionName) | 
					
						
							| 
									
										
										
										
											2010-02-08 08:53:30 +01:00
										 |  |  |         actionRes = appyType(self.appy()) | 
					
						
							| 
									
										
										
										
											2010-11-26 17:30:46 +01:00
										 |  |  |         if self.getParentNode().get(self.id): | 
					
						
							| 
									
										
										
										
											2010-12-17 14:46:55 +01:00
										 |  |  |             # Else, it means that the action has led to self's deletion. | 
					
						
							| 
									
										
										
										
											2010-11-26 17:30:46 +01:00
										 |  |  |             self.reindexObject() | 
					
						
							| 
									
										
										
										
											2009-10-25 21:42:08 +01:00
										 |  |  |         return appyType.result, actionRes | 
					
						
							| 
									
										
										
										
											2009-06-29 14:06:01 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-10-20 16:57:00 +02:00
										 |  |  |     def onExecuteAppyAction(self): | 
					
						
							|  |  |  |         '''This method is called every time a user wants to execute an Appy
 | 
					
						
							|  |  |  |            action on an object.'''
 | 
					
						
							|  |  |  |         rq = self.REQUEST | 
					
						
							| 
									
										
										
										
											2009-10-25 21:42:08 +01:00
										 |  |  |         resultType, actionResult = self.executeAppyAction(rq['fieldName']) | 
					
						
							|  |  |  |         successfull, msg = actionResult | 
					
						
							| 
									
										
										
										
											2009-10-20 16:57:00 +02:00
										 |  |  |         if not msg: | 
					
						
							|  |  |  |             # Use the default i18n messages | 
					
						
							|  |  |  |             suffix = 'ko' | 
					
						
							| 
									
										
										
										
											2009-10-25 21:42:08 +01:00
										 |  |  |             if successfull: | 
					
						
							| 
									
										
										
										
											2009-10-20 16:57:00 +02:00
										 |  |  |                 suffix = 'ok' | 
					
						
							| 
									
										
										
										
											2010-08-05 18:23:17 +02:00
										 |  |  |             appyType = self.getAppyType(rq['fieldName']) | 
					
						
							|  |  |  |             label = '%s_action_%s' % (appyType.labelId, suffix) | 
					
						
							| 
									
										
										
										
											2009-10-20 16:57:00 +02:00
										 |  |  |             msg = self.translate(label) | 
					
						
							| 
									
										
										
										
											2009-10-25 21:42:08 +01:00
										 |  |  |         if (resultType == 'computation') or not successfull: | 
					
						
							| 
									
										
										
										
											2011-02-12 17:09:11 +01:00
										 |  |  |             self.say(msg) | 
					
						
							| 
									
										
										
										
											2010-09-17 15:32:48 +02:00
										 |  |  |             return self.goto(self.getUrl(rq['HTTP_REFERER'])) | 
					
						
							| 
									
										
										
										
											2011-02-12 17:09:11 +01:00
										 |  |  |         elif resultType.startswith('file'): | 
					
						
							| 
									
										
										
										
											2011-01-14 09:06:25 +01:00
										 |  |  |             # msg does not contain a message, but a file instance. | 
					
						
							|  |  |  |             response = self.REQUEST.RESPONSE | 
					
						
							|  |  |  |             response.setHeader('Content-Type',mimetypes.guess_type(msg.name)[0]) | 
					
						
							|  |  |  |             response.setHeader('Content-Disposition', 'inline;filename="%s"' %\ | 
					
						
							| 
									
										
										
										
											2011-02-12 17:09:11 +01:00
										 |  |  |                                os.path.basename(msg.name)) | 
					
						
							| 
									
										
										
										
											2011-01-14 09:06:25 +01:00
										 |  |  |             response.write(msg.read()) | 
					
						
							|  |  |  |             msg.close() | 
					
						
							| 
									
										
										
										
											2011-02-12 17:09:11 +01:00
										 |  |  |             if resultType == 'filetmp': | 
					
						
							|  |  |  |                 # p_msg is a temp file. We need to delete it. | 
					
						
							|  |  |  |                 try: | 
					
						
							|  |  |  |                     os.remove(msg.name) | 
					
						
							|  |  |  |                     self.log('Temp file "%s" was deleted.' % msg.name) | 
					
						
							|  |  |  |                 except IOError, err: | 
					
						
							|  |  |  |                     self.log('Could not remove temp "%s" (%s).' % \ | 
					
						
							|  |  |  |                              (msg.name, str(err)), type='warning') | 
					
						
							|  |  |  |                 except OSError, err: | 
					
						
							|  |  |  |                     self.log('Could not remove temp "%s" (%s).' % \ | 
					
						
							|  |  |  |                              (msg.name, str(err)), type='warning') | 
					
						
							| 
									
										
										
										
											2010-11-26 17:30:46 +01:00
										 |  |  |         elif resultType == 'redirect': | 
					
						
							|  |  |  |             # msg does not contain a message, but the URL where to redirect | 
					
						
							|  |  |  |             # the user. | 
					
						
							|  |  |  |             return self.goto(msg) | 
					
						
							| 
									
										
										
										
											2009-10-20 16:57:00 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def onTriggerTransition(self): | 
					
						
							|  |  |  |         '''This method is called whenever a user wants to trigger a workflow
 | 
					
						
							|  |  |  |            transition on an object.'''
 | 
					
						
							|  |  |  |         rq = self.REQUEST | 
					
						
							|  |  |  |         self.portal_workflow.doActionFor(self, rq['workflow_action'], | 
					
						
							| 
									
										
										
										
											2010-11-30 17:41:18 +01:00
										 |  |  |                                          comment = rq.get('comment', '')) | 
					
						
							| 
									
										
										
										
											2010-01-12 08:58:40 +01:00
										 |  |  |         self.reindexObject() | 
					
						
							| 
									
										
										
										
											2010-11-22 09:36:14 +01:00
										 |  |  |         # Where to redirect the user back ? | 
					
						
							|  |  |  |         # TODO (?): remove the "phase" param for redirecting the user to the | 
					
						
							|  |  |  |         # next phase when relevant. | 
					
						
							|  |  |  |         return self.goto(self.getUrl(rq['HTTP_REFERER'])) | 
					
						
							| 
									
										
										
										
											2009-10-20 16:57:00 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-08-12 11:56:42 +02:00
										 |  |  |     def fieldValueSelected(self, fieldName, vocabValue, dbValue): | 
					
						
							| 
									
										
										
										
											2009-06-29 14:06:01 +02:00
										 |  |  |         '''When displaying a selection box (ie a String with a validator being a
 | 
					
						
							|  |  |  |            list), must the _vocabValue appear as selected?'''
 | 
					
						
							| 
									
										
										
										
											2010-08-05 18:23:17 +02:00
										 |  |  |         rq = self.REQUEST | 
					
						
							|  |  |  |         # Get the value we must compare (from request or from database) | 
					
						
							|  |  |  |         if rq.has_key(fieldName): | 
					
						
							|  |  |  |             compValue = rq.get(fieldName) | 
					
						
							| 
									
										
										
										
											2009-06-29 14:06:01 +02:00
										 |  |  |         else: | 
					
						
							| 
									
										
										
										
											2010-08-12 11:56:42 +02:00
										 |  |  |             compValue = dbValue | 
					
						
							| 
									
										
										
										
											2010-08-05 18:23:17 +02:00
										 |  |  |         # Compare the value | 
					
						
							|  |  |  |         if type(compValue) in sequenceTypes: | 
					
						
							|  |  |  |             if vocabValue in compValue: return True | 
					
						
							| 
									
										
										
										
											2009-06-29 14:06:01 +02:00
										 |  |  |         else: | 
					
						
							| 
									
										
										
										
											2010-08-05 18:23:17 +02:00
										 |  |  |             if vocabValue == compValue: return True | 
					
						
							| 
									
										
										
										
											2009-06-29 14:06:01 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-08-12 11:56:42 +02:00
										 |  |  |     def checkboxChecked(self, fieldName, dbValue): | 
					
						
							| 
									
										
										
										
											2009-06-29 14:06:01 +02:00
										 |  |  |         '''When displaying a checkbox, must it be checked or not?''' | 
					
						
							| 
									
										
										
										
											2010-08-05 18:23:17 +02:00
										 |  |  |         rq = self.REQUEST | 
					
						
							|  |  |  |         # Get the value we must compare (from request or from database) | 
					
						
							|  |  |  |         if rq.has_key(fieldName): | 
					
						
							|  |  |  |             compValue = rq.get(fieldName) | 
					
						
							|  |  |  |             compValue = compValue in ('True', 1, '1') | 
					
						
							| 
									
										
										
										
											2009-06-29 14:06:01 +02:00
										 |  |  |         else: | 
					
						
							| 
									
										
										
										
											2010-08-12 11:56:42 +02:00
										 |  |  |             compValue = dbValue | 
					
						
							| 
									
										
										
										
											2010-08-05 18:23:17 +02:00
										 |  |  |         # Compare the value | 
					
						
							|  |  |  |         return compValue | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-08-12 11:56:42 +02:00
										 |  |  |     def dateValueSelected(self, fieldName, fieldPart, dateValue, dbValue): | 
					
						
							| 
									
										
										
										
											2010-08-05 18:23:17 +02:00
										 |  |  |         '''When displaying a date field, must the particular p_dateValue be
 | 
					
						
							|  |  |  |            selected in the field corresponding to the date part?'''
 | 
					
						
							|  |  |  |         # Get the value we must compare (from request or from database) | 
					
						
							|  |  |  |         rq = self.REQUEST | 
					
						
							|  |  |  |         partName = '%s_%s' % (fieldName, fieldPart) | 
					
						
							|  |  |  |         if rq.has_key(partName): | 
					
						
							|  |  |  |             compValue = rq.get(partName) | 
					
						
							|  |  |  |             if compValue.isdigit(): | 
					
						
							|  |  |  |                 compValue = int(compValue) | 
					
						
							|  |  |  |         else: | 
					
						
							| 
									
										
										
										
											2010-08-12 11:56:42 +02:00
										 |  |  |             compValue = dbValue | 
					
						
							| 
									
										
										
										
											2010-08-05 18:23:17 +02:00
										 |  |  |             if compValue: | 
					
						
							|  |  |  |                 compValue = getattr(compValue, fieldPart)() | 
					
						
							|  |  |  |         # Compare the value | 
					
						
							|  |  |  |         return compValue == dateValue | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def getPossibleValues(self, name, withTranslations, withBlankValue, | 
					
						
							|  |  |  |                           className=None): | 
					
						
							|  |  |  |         '''Gets the possible values for field named p_name. This field must be a
 | 
					
						
							|  |  |  |            String with isSelection()=True. If p_withTranslations is True, | 
					
						
							|  |  |  |            instead of returning a list of string values, the result is a list | 
					
						
							|  |  |  |            of tuples (s_value, s_translation). If p_withBlankValue is True, a | 
					
						
							|  |  |  |            blank value is prepended to the list. If no p_className is defined, | 
					
						
							|  |  |  |            the field is supposed to belong to self's class''' | 
					
						
							|  |  |  |         appyType = self.getAppyType(name, className=className) | 
					
						
							| 
									
										
										
										
											2010-12-17 14:46:55 +01:00
										 |  |  |         if className: | 
					
						
							|  |  |  |             # We need an instance of className, but self can be an instance of | 
					
						
							|  |  |  |             # another class. So here we will search such an instance. | 
					
						
							|  |  |  |             brains = self.executeQuery(className, maxResults=1, brainsOnly=True) | 
					
						
							|  |  |  |             if brains: | 
					
						
							|  |  |  |                 obj = brains[0].getObject() | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 obj = self | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             obj = self | 
					
						
							|  |  |  |         return appyType.getPossibleValues(obj, withTranslations, withBlankValue) | 
					
						
							| 
									
										
										
										
											2009-06-29 14:06:01 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-08-04 14:39:43 +02:00
										 |  |  |     def appy(self): | 
					
						
							| 
									
										
										
										
											2010-02-08 08:53:30 +01:00
										 |  |  |         '''Returns a wrapper object allowing to manipulate p_self the Appy
 | 
					
						
							|  |  |  |            way.'''
 | 
					
						
							| 
									
										
										
										
											2010-08-05 18:23:17 +02:00
										 |  |  |         # Create the dict for storing Appy wrapper on the REQUEST if needed. | 
					
						
							|  |  |  |         rq = self.REQUEST | 
					
						
							|  |  |  |         if not hasattr(rq, 'appyWrappers'): rq.appyWrappers = {} | 
					
						
							|  |  |  |         # Return the Appy wrapper from rq.appyWrappers if already there | 
					
						
							|  |  |  |         uid = self.UID() | 
					
						
							|  |  |  |         if uid in rq.appyWrappers: return rq.appyWrappers[uid] | 
					
						
							|  |  |  |         # Create the Appy wrapper, cache it in rq.appyWrappers and return it | 
					
						
							|  |  |  |         wrapper = self.wrapperClass(self) | 
					
						
							|  |  |  |         rq.appyWrappers[uid] = wrapper | 
					
						
							|  |  |  |         return wrapper | 
					
						
							| 
									
										
										
										
											2009-06-29 14:06:01 +02:00
										 |  |  |   | 
					
						
							|  |  |  |     def _appy_showState(self, workflow, stateShow): | 
					
						
							|  |  |  |         '''Must I show a state whose "show value" is p_stateShow?''' | 
					
						
							|  |  |  |         if callable(stateShow): | 
					
						
							| 
									
										
										
										
											2010-02-08 08:53:30 +01:00
										 |  |  |             return stateShow(workflow, self.appy()) | 
					
						
							| 
									
										
										
										
											2009-06-29 14:06:01 +02:00
										 |  |  |         else: return stateShow | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _appy_managePermissions(self): | 
					
						
							|  |  |  |         '''When an object is created or updated, we must update "add"
 | 
					
						
							|  |  |  |            permissions accordingly: if the object is a folder, we must set on | 
					
						
							|  |  |  |            it permissions that will allow to create, inside it, objects through | 
					
						
							|  |  |  |            Ref fields; if it is not a folder, we must update permissions on its | 
					
						
							|  |  |  |            parent folder instead.'''
 | 
					
						
							|  |  |  |         # Determine on which folder we need to set "add" permissions | 
					
						
							|  |  |  |         folder = self | 
					
						
							|  |  |  |         if not self.isPrincipiaFolderish: | 
					
						
							|  |  |  |             folder = self.getParentNode() | 
					
						
							|  |  |  |         # On this folder, set "add" permissions for every content type that will | 
					
						
							|  |  |  |         # be created through reference fields | 
					
						
							| 
									
										
										
										
											2010-08-27 08:59:53 +02:00
										 |  |  |         allCreators = {} # One key for every add permission | 
					
						
							|  |  |  |         addPermissions = self.getProductConfig().ADD_CONTENT_PERMISSIONS | 
					
						
							| 
									
										
										
										
											2010-08-05 18:23:17 +02:00
										 |  |  |         for appyType in self.getAllAppyTypes(): | 
					
						
							| 
									
										
										
										
											2010-08-27 08:59:53 +02:00
										 |  |  |             if appyType.type != 'Ref': continue | 
					
						
							|  |  |  |             if appyType.isBack or appyType.link: continue | 
					
						
							|  |  |  |             # Indeed, no possibility to create objects with such Ref | 
					
						
							| 
									
										
										
										
											2010-09-02 16:16:08 +02:00
										 |  |  |             refType = self.getTool().getPortalType(appyType.klass) | 
					
						
							|  |  |  |             if refType not in addPermissions: continue | 
					
						
							| 
									
										
										
										
											2010-08-27 08:59:53 +02:00
										 |  |  |             # Get roles that may add this content type | 
					
						
							| 
									
										
										
										
											2010-09-02 16:16:08 +02:00
										 |  |  |             creators = getattr(appyType.klass, 'creators', None) | 
					
						
							| 
									
										
										
										
											2010-08-27 08:59:53 +02:00
										 |  |  |             if not creators: | 
					
						
							|  |  |  |                 creators = self.getProductConfig().defaultAddRoles | 
					
						
							|  |  |  |             # Add those creators to the list of creators for this meta_type | 
					
						
							| 
									
										
										
										
											2010-09-02 16:16:08 +02:00
										 |  |  |             addPermission = addPermissions[refType] | 
					
						
							| 
									
										
										
										
											2010-08-27 08:59:53 +02:00
										 |  |  |             if addPermission in allCreators: | 
					
						
							|  |  |  |                 allCreators[addPermission] = allCreators[\ | 
					
						
							|  |  |  |                                              addPermission].union(creators) | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 allCreators[addPermission] = set(creators) | 
					
						
							|  |  |  |         # Update the permissions | 
					
						
							|  |  |  |         for permission, creators in allCreators.iteritems(): | 
					
						
							|  |  |  |             updateRolesForPermission(permission, tuple(creators), folder) | 
					
						
							| 
									
										
										
										
											2009-06-29 14:06:01 +02:00
										 |  |  |         # Beyond content-type-specific "add" permissions, creators must also | 
					
						
							|  |  |  |         # have the main permission "Add portal content". | 
					
						
							| 
									
										
										
										
											2010-08-27 08:59:53 +02:00
										 |  |  |         permission = 'Add portal content' | 
					
						
							|  |  |  |         for creators in allCreators.itervalues(): | 
					
						
							|  |  |  |             updateRolesForPermission(permission, tuple(creators), folder) | 
					
						
							| 
									
										
										
										
											2009-06-29 14:06:01 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def _appy_getPortalType(self, request): | 
					
						
							|  |  |  |         '''Guess the portal_type of p_self from info about p_self and
 | 
					
						
							|  |  |  |            p_request.'''
 | 
					
						
							|  |  |  |         res = None | 
					
						
							|  |  |  |         # If the object is being created, self.portal_type is not correctly | 
					
						
							|  |  |  |         # initialized yet. | 
					
						
							|  |  |  |         if request.has_key('__factory__info__'): | 
					
						
							|  |  |  |             factoryInfo = request['__factory__info__'] | 
					
						
							|  |  |  |             if factoryInfo.has_key('stack'): | 
					
						
							|  |  |  |                 res = factoryInfo['stack'][0] | 
					
						
							|  |  |  |         if not res: | 
					
						
							|  |  |  |             res = self.portal_type | 
					
						
							|  |  |  |         return res | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-02-12 10:59:42 +01:00
										 |  |  |     def _appy_getSortedField(self, fieldName): | 
					
						
							|  |  |  |         '''Gets, for reference field p_fieldName, the Appy persistent list
 | 
					
						
							|  |  |  |            that contains the sorted list of referred object UIDs. If this list | 
					
						
							|  |  |  |            does not exist, it is created.'''
 | 
					
						
							|  |  |  |         sortedFieldName = '_appy_%s' % fieldName | 
					
						
							|  |  |  |         if not hasattr(self.aq_base, sortedFieldName): | 
					
						
							|  |  |  |             pList = self.getProductConfig().PersistentList | 
					
						
							|  |  |  |             exec 'self.%s = pList()' % sortedFieldName | 
					
						
							|  |  |  |         return getattr(self, sortedFieldName) | 
					
						
							| 
									
										
										
										
											2009-06-29 14:06:01 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-09-17 15:32:48 +02:00
										 |  |  |     getUrlDefaults = {'page':True, 'nav':True} | 
					
						
							|  |  |  |     def getUrl(self, base=None, mode='view', **kwargs): | 
					
						
							|  |  |  |         '''Returns a Appy URL.
 | 
					
						
							|  |  |  |            * If p_base is None, it will be the base URL for this object | 
					
						
							|  |  |  |              (ie, self.absolute_url()). | 
					
						
							| 
									
										
										
										
											2010-11-22 09:36:14 +01:00
										 |  |  |            * p_mode can be "edit", "view" or "raw" (a non-param, base URL) | 
					
						
							| 
									
										
										
										
											2010-09-17 15:32:48 +02:00
										 |  |  |            * p_kwargs can store additional parameters to add to the URL. | 
					
						
							|  |  |  |              In this dict, every value that is a string will be added to the | 
					
						
							|  |  |  |              URL as-is. Every value that is True will be replaced by the value | 
					
						
							|  |  |  |              in the request for the corresponding key (if existing; else, the | 
					
						
							|  |  |  |              param will not be included in the URL at all).'''
 | 
					
						
							| 
									
										
										
										
											2010-11-22 09:36:14 +01:00
										 |  |  |         # Define the URL suffix | 
					
						
							|  |  |  |         suffix = '' | 
					
						
							|  |  |  |         if mode != 'raw': suffix = '/skyn/%s' % mode | 
					
						
							|  |  |  |         # Define base URL if omitted | 
					
						
							| 
									
										
										
										
											2010-09-19 15:04:44 +02:00
										 |  |  |         if not base: | 
					
						
							| 
									
										
										
										
											2010-11-22 09:36:14 +01:00
										 |  |  |             base = self.absolute_url() + suffix | 
					
						
							|  |  |  |         # If a raw URL is asked, remove any param and suffix. | 
					
						
							|  |  |  |         if mode == 'raw': | 
					
						
							|  |  |  |             if '?' in base: base = base[:base.index('?')] | 
					
						
							|  |  |  |             base = base.strip('/') | 
					
						
							|  |  |  |             for mode in ('view', 'edit'): | 
					
						
							|  |  |  |                 suffix = 'skyn/%s' % mode | 
					
						
							|  |  |  |                 if base.endswith(suffix): | 
					
						
							|  |  |  |                     base = base[:-len(suffix)].strip('/') | 
					
						
							|  |  |  |                     break | 
					
						
							|  |  |  |             return base | 
					
						
							| 
									
										
										
										
											2010-09-17 15:32:48 +02:00
										 |  |  |         # Manage default args | 
					
						
							|  |  |  |         if not kwargs: kwargs = self.getUrlDefaults | 
					
						
							|  |  |  |         if 'page' not in kwargs: kwargs['page'] = True | 
					
						
							|  |  |  |         if 'nav'  not in kwargs: kwargs['nav'] = True | 
					
						
							|  |  |  |         # Create URL parameters from kwargs | 
					
						
							|  |  |  |         params = [] | 
					
						
							|  |  |  |         for name, value in kwargs.iteritems(): | 
					
						
							|  |  |  |             if isinstance(value, basestring): | 
					
						
							|  |  |  |                 params.append('%s=%s' % (name, value)) | 
					
						
							|  |  |  |             elif self.REQUEST.get(name, ''): | 
					
						
							|  |  |  |                 params.append('%s=%s' % (name, self.REQUEST[name])) | 
					
						
							|  |  |  |         if params: | 
					
						
							|  |  |  |             params = '&'.join(params) | 
					
						
							|  |  |  |             if base.find('?') != -1: params = '&' + params | 
					
						
							|  |  |  |             else:                    params = '?' + params | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             params = '' | 
					
						
							| 
									
										
										
										
											2010-09-19 15:04:44 +02:00
										 |  |  |         return '%s%s' % (base, params) | 
					
						
							| 
									
										
										
										
											2009-10-18 14:52:27 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-01-19 20:51:43 +01:00
										 |  |  |     def getUserLanguage(self): | 
					
						
							| 
									
										
										
										
											2011-01-17 14:49:56 +01:00
										 |  |  |         '''Gets the language (code) of the current user.''' | 
					
						
							|  |  |  |         # Try first the "LANGUAGE" key from the request | 
					
						
							|  |  |  |         res = self.REQUEST.get('LANGUAGE', None) | 
					
						
							|  |  |  |         if res: return res | 
					
						
							|  |  |  |         # Try then the HTTP_ACCEPT_LANGUAGE key from the request, which stores | 
					
						
							|  |  |  |         # language preferences as defined in the user's browser. Several | 
					
						
							|  |  |  |         # languages can be listed, from most to less wanted. | 
					
						
							|  |  |  |         res = self.REQUEST.get('HTTP_ACCEPT_LANGUAGE', None) | 
					
						
							|  |  |  |         if not res: return 'en' | 
					
						
							|  |  |  |         if ',' in res: res = res[:res.find(',')] | 
					
						
							|  |  |  |         if '-' in res: res = res[:res.find('-')] | 
					
						
							|  |  |  |         return res | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-02-23 11:30:44 +01:00
										 |  |  |     def formatText(self, text, format='html'): | 
					
						
							|  |  |  |         '''Produces a representation of p_text into the desired p_format, which
 | 
					
						
							|  |  |  |            is 'html' by default.'''
 | 
					
						
							|  |  |  |         if format in ('html', 'xhtml'): | 
					
						
							|  |  |  |             res = text.replace('\r\n', '<br/>').replace('\n', '<br/>') | 
					
						
							|  |  |  |         elif format == 'js': | 
					
						
							|  |  |  |             res = text.replace('\r\n', '').replace('\n', '') | 
					
						
							|  |  |  |             res = res.replace("'", "\\'") | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             res = text | 
					
						
							|  |  |  |         return res | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-10-14 14:43:56 +02:00
										 |  |  |     def translate(self, label, mapping={}, domain=None, default=None, | 
					
						
							| 
									
										
										
										
											2011-03-04 10:30:45 +01:00
										 |  |  |                   language=None, format='html', field=None, className=None): | 
					
						
							|  |  |  |         '''Translates a given p_label into p_domain with p_mapping.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |            If p_field is given, p_label does not correspond to a full label | 
					
						
							|  |  |  |            name, but to a label type linked to p_field: "label", "descr" | 
					
						
							|  |  |  |            or "help". Indeed, in this case, a specific i18n mapping may be | 
					
						
							|  |  |  |            available on the field, so we must merge this mapping into | 
					
						
							|  |  |  |            p_mapping. If p_className is not given, we consider p_self being an | 
					
						
							|  |  |  |            instance of the class where p_field is defined.'''
 | 
					
						
							| 
									
										
										
										
											2009-10-18 14:52:27 +02:00
										 |  |  |         cfg = self.getProductConfig() | 
					
						
							|  |  |  |         if not domain: domain = cfg.PROJECTNAME | 
					
						
							| 
									
										
										
										
											2011-01-14 09:06:25 +01:00
										 |  |  |         if domain != cfg.PROJECTNAME: | 
					
						
							|  |  |  |             # We need to translate something that is in a standard Zope catalog | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 res = self.Control_Panel.TranslationService.utranslate( | 
					
						
							|  |  |  |                     domain, label, mapping, self, default=default, | 
					
						
							|  |  |  |                     target_language=language) | 
					
						
							|  |  |  |             except AttributeError: | 
					
						
							|  |  |  |                 # When run in test mode, Zope does not create the | 
					
						
							|  |  |  |                 # TranslationService | 
					
						
							|  |  |  |                 res = label | 
					
						
							|  |  |  |         else: | 
					
						
							| 
									
										
										
										
											2011-03-04 10:30:45 +01:00
										 |  |  |             # Get the label name, and the field-specific mapping if any. | 
					
						
							|  |  |  |             if field: | 
					
						
							|  |  |  |                 # Maybe we do not have the field itself, but only its name | 
					
						
							|  |  |  |                 if isinstance(field, basestring): | 
					
						
							|  |  |  |                     field = self.getAppyType(field, className=className) | 
					
						
							|  |  |  |                 # Include field-specific mapping if any. | 
					
						
							|  |  |  |                 fieldMapping = field.mapping[label] | 
					
						
							|  |  |  |                 if fieldMapping: | 
					
						
							|  |  |  |                     if callable(fieldMapping): | 
					
						
							|  |  |  |                         fieldMapping = field.callMethod(self, fieldMapping) | 
					
						
							|  |  |  |                     mapping.update(fieldMapping) | 
					
						
							|  |  |  |                 # Get the label | 
					
						
							|  |  |  |                 label = getattr(field, label+'Id') | 
					
						
							| 
									
										
										
										
											2011-01-14 09:06:25 +01:00
										 |  |  |             # We will get the translation from a Translation object. | 
					
						
							|  |  |  |             # In what language must we get the translation? | 
					
						
							| 
									
										
										
										
											2011-01-19 20:51:43 +01:00
										 |  |  |             if not language: language = self.getUserLanguage() | 
					
						
							| 
									
										
										
										
											2011-01-14 09:06:25 +01:00
										 |  |  |             tool = self.getTool() | 
					
						
							| 
									
										
										
										
											2011-01-17 14:49:56 +01:00
										 |  |  |             try: | 
					
						
							|  |  |  |                 translation = getattr(tool, language).appy() | 
					
						
							|  |  |  |             except AttributeError: | 
					
						
							|  |  |  |                 # We have no translation for this language. Fallback to 'en'. | 
					
						
							|  |  |  |                 translation = getattr(tool, 'en').appy() | 
					
						
							| 
									
										
										
										
											2011-01-14 09:06:25 +01:00
										 |  |  |             res = getattr(translation, label, '') | 
					
						
							| 
									
										
										
										
											2011-01-17 14:49:56 +01:00
										 |  |  |             if not res: | 
					
						
							|  |  |  |                 # Fallback to 'en'. | 
					
						
							|  |  |  |                 translation = getattr(tool, 'en').appy() | 
					
						
							|  |  |  |                 res = getattr(translation, label, '') | 
					
						
							| 
									
										
										
										
											2011-01-18 15:48:55 +01:00
										 |  |  |             # If still no result, put the label instead of a translated message | 
					
						
							|  |  |  |             if not res: res = label | 
					
						
							|  |  |  |             else: | 
					
						
							| 
									
										
										
										
											2011-01-29 02:18:14 +01:00
										 |  |  |                 # Perform replacements, according to p_format. | 
					
						
							| 
									
										
										
										
											2011-02-23 11:30:44 +01:00
										 |  |  |                 res = self.formatText(res, format) | 
					
						
							| 
									
										
										
										
											2011-01-29 02:18:14 +01:00
										 |  |  |                 # Perform variable replacements | 
					
						
							| 
									
										
										
										
											2011-01-18 15:48:55 +01:00
										 |  |  |                 for name, repl in mapping.iteritems(): | 
					
						
							|  |  |  |                     res = res.replace('${%s}' % name, repl) | 
					
						
							| 
									
										
										
										
											2010-11-10 15:15:00 +01:00
										 |  |  |         return res | 
					
						
							| 
									
										
										
										
											2010-08-05 18:23:17 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def getPageLayout(self, layoutType): | 
					
						
							| 
									
										
										
										
											2010-09-17 15:32:48 +02:00
										 |  |  |         '''Returns the layout corresponding to p_layoutType for p_self.''' | 
					
						
							| 
									
										
										
										
											2010-08-05 18:23:17 +02:00
										 |  |  |         appyClass = self.wrapperClass.__bases__[-1] | 
					
						
							|  |  |  |         if hasattr(appyClass, 'layouts'): | 
					
						
							|  |  |  |             layout = appyClass.layouts[layoutType] | 
					
						
							|  |  |  |             if isinstance(layout, basestring): | 
					
						
							|  |  |  |                 layout = Table(layout) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             layout = defaultPageLayouts[layoutType] | 
					
						
							|  |  |  |         return layout.get() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def getPageTemplate(self, skyn, templateName): | 
					
						
							|  |  |  |         '''Returns, in the skyn folder, the page template corresponding to
 | 
					
						
							|  |  |  |            p_templateName.'''
 | 
					
						
							|  |  |  |         res = skyn | 
					
						
							|  |  |  |         for name in templateName.split('/'): | 
					
						
							|  |  |  |             res = res.get(name) | 
					
						
							|  |  |  |         return res | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def download(self): | 
					
						
							|  |  |  |         '''Downloads the content of the file that is in the File field named
 | 
					
						
							|  |  |  |            p_name.'''
 | 
					
						
							|  |  |  |         name = self.REQUEST.get('name') | 
					
						
							|  |  |  |         if not name: return | 
					
						
							|  |  |  |         appyType = self.getAppyType(name) | 
					
						
							|  |  |  |         if (not appyType.type =='File') or not appyType.isShowable(self,'view'): | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  |         theFile = getattr(self, name, None) | 
					
						
							|  |  |  |         if theFile: | 
					
						
							|  |  |  |             response = self.REQUEST.RESPONSE | 
					
						
							|  |  |  |             response.setHeader('Content-Disposition', 'inline;filename="%s"' % \ | 
					
						
							|  |  |  |                                theFile.filename) | 
					
						
							|  |  |  |             response.setHeader('Cachecontrol', 'no-cache') | 
					
						
							|  |  |  |             response.setHeader('Expires', 'Thu, 11 Dec 1975 12:05:05 GMT') | 
					
						
							|  |  |  |             return theFile.index_html(self.REQUEST, self.REQUEST.RESPONSE) | 
					
						
							| 
									
										
										
										
											2010-11-30 17:41:18 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def SearchableText(self): | 
					
						
							|  |  |  |         '''This method concatenates the content of every field with
 | 
					
						
							|  |  |  |            searchable=True for indexing purposes.'''
 | 
					
						
							|  |  |  |         res = [] | 
					
						
							|  |  |  |         for field in self.getAllAppyTypes(): | 
					
						
							|  |  |  |             if not field.searchable: continue | 
					
						
							|  |  |  |             res.append(field.getIndexValue(self, forSearch=True)) | 
					
						
							|  |  |  |         return res | 
					
						
							| 
									
										
										
										
											2009-06-29 14:06:01 +02:00
										 |  |  | # ------------------------------------------------------------------------------ |