getattr -> restrictedTraverse : si on utilise geattr au lieu de restrictedTraverse...
[Portfolio.git] / photo.py
1 # -*- coding: utf-8 -*-
2 ############################################################
3 # Copyright © 2005-2008 Benoît PIN <benoit.pin@ensmp.fr> #
4 # Plinn - http://plinn.org #
5 # #
6 # This program is free software; you can redistribute it #
7 # and/or modify it under the terms of the Creative Commons #
8 # "Attribution-Noncommercial 2.0 Generic" #
9 # http://creativecommons.org/licenses/by-nc/2.0/ #
10 ############################################################
11 """ CMFAware Image
12 """
13
14 from Globals import InitializeClass
15 from AccessControl import ClassSecurityInfo
16 from AccessControl.requestmethod import postonly
17 from DateTime import DateTime
18 from Products.CMFCore.permissions import View, AccessContentsInformation, \
19 ModifyPortalContent, ManageProperties, \
20 ReviewPortalContent
21 from permissions import ViewRawImage
22 from zope.component.factory import Factory
23 from zope.interface import implements
24 from webdav.interfaces import IWriteLock
25 from Products.CMFCore.interfaces import IContentish
26 from Products.CMFCore.interfaces import IDynamicType
27
28 from Products.CMFCore.DynamicType import DynamicType
29 from Products.CMFCore.CMFCatalogAware import CMFCatalogAware
30 from Products.Photo.Photo import Photo as BasePhoto
31 from Products.CMFDefault.DublinCore import DefaultDublinCoreImpl
32 from Products.CMFCore.utils import getToolByName, getUtilityByInterfaceName
33 from Products.Photo.cache import memoizedmethod
34 from Products.DCWorkflow.utils import modifyRolesForPermission
35 from interfaces import IPhoto
36
37 class Photo(DynamicType, CMFCatalogAware, BasePhoto, DefaultDublinCoreImpl) :
38 """ Photo CMF aware """
39
40 implements(IPhoto, IContentish, IWriteLock, IDynamicType)
41
42 meta_type = BasePhoto.meta_type
43 manage_options = BasePhoto.manage_options
44 security = ClassSecurityInfo()
45
46 security.declareProtected(ViewRawImage, 'index_html')
47 security.declareProtected(ViewRawImage, 'getJpegImage')
48
49 def __init__(self, id, title='', file='', content_type='', precondition='', **kw) :
50 DefaultDublinCoreImpl.__init__(self, title=title)
51 BasePhoto.__init__(self, id, title, file, content_type=content_type, precondition=precondition, **kw)
52 self.id = id
53 self.title = title
54
55 now = DateTime()
56 self.creation_date = now
57 self.modification_date = now
58
59 def update_data(self, data, content_type=None) :
60 BasePhoto.update_data(self, data, content_type=content_type)
61 # update_data can be invoked during construction
62 # in this case, reindexObject put a parasite catalag entry.
63 if self.getParentNode() :
64 self.reindexObject()
65
66
67 def _getAfterResizingHooks(self) :
68 pim = getToolByName(self, 'portal_image_manipulation')
69 return pim.image.objectValues(['Script (Python)'])
70
71 def _getAfterTilingHooks(self) :
72 pim = getToolByName(self, 'portal_image_manipulation')
73 return pim.tile.objectValues(['Script (Python)'])
74
75 #
76 # Dublin Core interface
77 #
78
79 security.declareProtected(View, 'Title')
80 @memoizedmethod()
81 def Title(self):
82 """ returns dc:title from xmp
83 """
84 photoshopHeadline = self.getXmpValue('photoshop:Headline')
85 dcTitle = self.getXmpValue('dc:title')
86
87 return dcTitle or photoshopHeadline
88
89
90 security.declareProtected(View, 'listCreators')
91 @memoizedmethod()
92 def listCreators(self):
93 """ returns creator from dc:creator from xmp
94 """
95 return self.getXmpValue('dc:creator')
96
97
98 security.declareProtected(View, 'Description')
99 @memoizedmethod()
100 def Description(self) :
101 """ returns dc:description from xmp """
102 return self.getXmpValue('dc:description')
103
104
105 security.declareProtected(View, 'Subject')
106 @memoizedmethod()
107 def Subject(self):
108 """ returns subject from dc:subject from xmp
109 """
110 return self.getXmpValue('dc:subject')
111
112 security.declareProtected(View, 'Rights')
113 @memoizedmethod()
114 def Rights(self):
115 """ returns rights from dc:rights from xmp
116 """
117 return self.getXmpValue('dc:rights')
118
119 security.declareProtected(ModifyPortalContent, 'editMetadata')
120 def editMetadata(self, **kw):
121 """
122 Need to add check for webDAV locked resource for TTW methods.
123 """
124 # as per bug #69, we cant assume they use the webdav
125 # locking interface, and fail gracefully if they dont
126 if hasattr(self, 'failIfLocked'):
127 self.failIfLocked()
128
129 self.setXmpFields(**kw)
130 for name in ('Title', 'listCreators', 'Description', 'Subject', 'Rights') :
131 self._clearCacheFor(name)
132 self.reindexObject()
133
134
135 def _clearCacheFor(self, name) :
136 try :
137 del self._methodResultsCache[name]
138 except KeyError : pass
139
140
141 security.declareProtected(View, 'SearchableText')
142 def SearchableText(self):
143 """ Return textuals metadata"""
144
145 searchable = (self.Title()
146 , self.Description()
147 , ' '.join(self.Subject())
148 , self.getId()
149 , self.Creator())
150 return ' '.join(searchable)
151
152 security.declareProtected(View, 'DateTimeOriginal')
153 @memoizedmethod()
154 def DateTimeOriginal(self) :
155 """ return DateTimeOriginal exif tag value or created """
156 dto = self.getXmpValue('exif:DateTimeOriginal')
157 if dto :
158 return DateTime(dto)
159 else :
160 return self.created()
161
162
163 CreationDate = DefaultDublinCoreImpl.CreationDate
164
165 Format = BasePhoto.getContentType
166
167 security.declareProtected(ReviewPortalContent, 'hideForAnonymous')
168 @postonly
169 def hideForAnonymous(self, REQUEST=None):
170 ' '
171 modifyRolesForPermission(self, View, ( 'Contributor'
172 , 'Downloader'
173 , 'Manager'
174 , 'Owner'
175 , 'Reader')
176 )
177 self._hiddenForAnon = True
178 self.reindexObjectSecurity()
179 self.reindexObject(idxs=['hiddenForAnonymous'])
180
181
182 security.declareProtected(ReviewPortalContent, 'resetHide')
183 @postonly
184 def resetHide(self, REQUEST=None):
185 ' '
186 modifyRolesForPermission(self, View, [])
187 self._hiddenForAnon = False
188 self.reindexObjectSecurity()
189 self.reindexObject(idxs=['hiddenForAnonymous'])
190
191 security.declareProtected(View, 'hiddenForAnonymous')
192 def hiddenForAnonymous(self):
193 return getattr(self, '_hiddenForAnon', False)
194
195
196 #
197 # SimpleItem interface
198 #
199
200 def title_or_id(self):
201 """Return the title if it is not blank and the id otherwise.
202 """
203 return self.Title().strip() or self.getId()
204
205 def title_and_id(self):
206 """Return the title if it is not blank and the id otherwise.
207
208 If the title is not blank, then the id is included in parens.
209 """
210 title = self.Title()
211 id = self.getId()
212 return title and ("%s (%s)" % (title,id)) or id
213
214
215 InitializeClass(Photo)
216
217 class _PhotoFactory(Factory) :
218 def __call__(self, *args, **kw):
219 if not kw.has_key('thumb_height') or not kw.has_key('thumb_width') :
220 utool = getUtilityByInterfaceName('Products.CMFCore.interfaces.IURLTool')
221 portal = utool.getPortalObject()
222 size = portal.getProperty('thumb_size')
223 kw.update({'thumb_height' : size, 'thumb_width' : size })
224 return self._callable(*args, **kw)
225
226
227 PhotoFactory = _PhotoFactory(Photo)