forked from ZeitOnline/zeit.content.image
/
interfaces.py
313 lines (219 loc) · 9.55 KB
/
interfaces.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
# coding: utf8
from zeit.cms.i18n import MessageFactory as _
import PIL.Image
import zc.form.field
import zc.sourcefactory.contextual
import zeit.cms.content.contentsource
import zeit.cms.content.interfaces
import zeit.cms.interfaces
import zeit.cms.repository.interfaces
import zeit.cms.workingcopy.interfaces
import zope.file.interfaces
import zope.schema
IMAGE_NAMESPACE = 'http://namespaces.zeit.de/CMS/image'
class ImageProcessingError(TypeError):
"""An error raised it's not possible to process the Image."""
class IImageType(zeit.cms.interfaces.ICMSContentType):
"""The interface of image interfaces."""
class AlignmentSource(zeit.cms.content.sources.SimpleFixedValueSource):
values = (u'left', u'center', u'right')
class ILinkField(zope.interface.Interface):
"""Marker interface so we can register a custom widget for this field."""
class IImageMetadata(zope.interface.Interface):
title = zope.schema.TextLine(
title=_("Image title"),
default=u'',
required=False)
year = zope.schema.Int(
title=_("Year"),
min=1900,
max=2100,
required=False)
volume = zope.schema.Int(
title=_("Volume"),
min=1,
max=53,
required=False)
copyrights = zope.schema.Tuple(
title=_("Copyrights"),
default=((u'©', None, False),),
missing_value=(),
required=False,
value_type=zc.form.field.Combination(
(zope.schema.TextLine(
title=_("Copyright"),
min_length=3,
required=True),
zope.schema.URI(
title=_('Link'),
description=_('Link to copyright holder'),
required=False),
zope.schema.Bool(
title=_('set nofollow'),
required=False))))
alt = zope.schema.TextLine(
title=_("Alternative text"),
description=_("Enter a textual description of the image"),
default=u'',
required=False)
caption = zope.schema.Text(
title=_("Image sub text"),
default=u'',
required=False)
links_to = zope.schema.URI(
title=_('Links to'),
description=_('Enter a URL this image should link to.'),
required=False)
# XXX Disabled because the frontend does not interpret rewritten links
# correctly yet.
# zope.interface.alsoProvides(links_to, ILinkField)
alignment = zope.schema.Choice(
title=_('Alignment'),
source=AlignmentSource(),
default='left')
acquire_metadata = zope.schema.Bool(
title=u'True if metadata should be acquired from the parent.')
class IImage(zeit.cms.interfaces.IAsset,
zeit.cms.repository.interfaces.IDAVContent,
zope.file.interfaces.IFile):
"""Image."""
def getImageSize():
"""return tuple (width, heigth) of image."""
format = zope.interface.Attribute(
'Our mimeType formatted as a PIL-compatible format (e.g. JPEG, PNG)')
ratio = zope.interface.Attribute('width/height')
class ITransform(zope.interface.Interface):
def thumbnail(width, height, filter=PIL.Image.ANTIALIAS):
"""Create a thumbnail version of the image.
returns IImage object.
"""
def resize(width=None, height=None, filter=PIL.Image.ANTIALIAS):
"""Create a resized version of the image.
returns IImage object.
raises TypeError of both width and height are ommited.
"""
def create_variant_image(variant, size=None):
"""Create a cropped version of the image, using the configuration
information (focus point, zoom, etc.) of the given IVariant.
A tuple `size` can be given to resize the resulting image to a certain
size.
returns IImage object.
"""
class IPersistentThumbnail(IImage):
"""Persistent thumbnail version of an image."""
class IThumbnailFolder(zope.interface.Interface):
"""The folder where to find thumbnails for an image."""
class MasterImageSource(
zc.sourcefactory.contextual.BasicContextualSourceFactory):
def getValues(self, context):
repository = zope.component.getUtility(
zeit.cms.repository.interfaces.IRepository)
for name in repository.getContent(context.uniqueId):
yield name
class IImageGroup(zeit.cms.repository.interfaces.ICollection,
zeit.cms.interfaces.IAsset,
zeit.cms.repository.interfaces.IDAVContent):
"""An image group groups images with the same motif together."""
master_image = zope.schema.Choice(
title=_('Master image'),
source=MasterImageSource())
variants = zope.schema.Dict(
title=_('Setting for variants'))
def variant_url(name, width=None, height=None, thumbnail=False):
"""Return an URL path to the variant with the given name that matches
the width/height requirements most closely.
This is only the path so clients can prepend the proper hostname etc.,
so e.g. vivi can generate URLs that point to its own repository as well
as to www.zeit.de, for example.
If thumbnail is True, return a path for an image that was generated
by a downsampled version instead of the full master image.
If a `variant-secret` is configured in the zeit.content.image product
config, adds a signed hash of width and height to the URL, to prevent
URL spoofing.
"""
def create_variant_image(key, source=None):
"""For internal use: dynamically create a cropped version of the source
image, according to the settings of the variant determined by key.
"""
class IRepositoryImageGroup(IImageGroup):
"""An image group in the repository. It contains images.
"""
class ILocalImageGroup(IImageGroup,
zeit.cms.workingcopy.interfaces.ILocalContent):
"""Local version of an image group.
The local version only holds the metadata, therefore it is not a container.
"""
class IVariants(zope.interface.common.mapping.IEnumerableMapping):
"""Object-oriented access to IImageGroup.variants."""
class IVariant(zope.interface.Interface):
id = zope.schema.TextLine(description=u'Unique Variant name')
name = zope.schema.TextLine(description=u'Grouping of Variant sizes')
display_name = zope.schema.TextLine(description=u'Displayed name')
focus_x = zope.schema.Float(
description=u'Position of the focus point relative to image width')
focus_y = zope.schema.Float(
description=u'Position of the focus point relative to image height')
zoom = zope.schema.Float(
description=u'Zoom factor used, i.e. 1 for no zoom')
aspect_ratio = zope.schema.TextLine(
description=u"String representation of ratio as X:Y, e.g. 16:9")
max_size = zope.schema.TextLine(
description=u"Maximum width / height of this Variant, e.g. 160x90")
ratio = zope.interface.Attribute(
'Float representation of ratio')
max_width = zope.interface.Attribute(
'Shorthand to access width of max_size')
max_height = zope.interface.Attribute(
'Shorthand to access height of max_size')
relative_image_path = zope.interface.Attribute(
'Image path relative to the ImageGroup the Variant lives in')
is_default = zope.interface.Attribute(
'Bool whether this Variant represents the default configuration')
legacy_name = zope.interface.Attribute(
'If applicable, the legacy name this Variant was mapped from')
class IImageSource(zope.interface.common.mapping.IEnumerableMapping):
"""A source for images."""
class ImageSource(zeit.cms.content.contentsource.CMSContentSource):
zope.interface.implements(IImageSource)
check_interfaces = IImageType
name = 'images'
imageSource = ImageSource()
class BareImageSource(zeit.cms.content.contentsource.CMSContentSource):
zope.interface.implements(IImageSource)
check_interfaces = (IImage,)
name = 'bare-images'
bareImageSource = BareImageSource()
class ImageGroupSource(zeit.cms.content.contentsource.CMSContentSource):
zope.interface.implements(IImageSource)
check_interfaces = (IImageGroup,)
name = 'image-groups'
def __contains__(self, value):
# backwards compatibility: IImages used to contain both IImage and
# IImageGroup, so there might still be content with IImage around
# which needs to be accessible.
if IImage.providedBy(value):
return True
return super(ImageGroupSource, self).__contains__(value)
imageGroupSource = ImageGroupSource()
class IThumbnails(zope.container.interfaces.IReadContainer):
"""Traverser to access smaller images via /thumbnails"""
source_image = zope.schema.Choice(source=bareImageSource)
class IImages(zope.interface.Interface):
"""An object which references images."""
image = zope.schema.Choice(
title=_('Image'),
description=_("Drag an image group here"),
required=False,
source=imageGroupSource)
class IReferences(zope.interface.Interface):
references = zope.schema.Tuple(
title=_('Objects using this image'),
readonly=True,
value_type=zope.schema.Choice(
source=zeit.cms.content.contentsource.cmsContentSource))
class IMasterImage(zope.interface.Interface):
"""Marks an image in an image group as master for the other images."""
class IImageReference(zeit.cms.content.interfaces.IReference,
IImageMetadata):
"""Reference to an image, allows overriding metadata locally for the
referring content object."""