Skip to content

Commit

Permalink
Merge pull request #159 from collective/issue_158
Browse files Browse the repository at this point in the history
closes #158
  • Loading branch information
hvelarde committed Apr 18, 2013
2 parents d3d66e7 + 2929a34 commit 77522bb
Show file tree
Hide file tree
Showing 11 changed files with 159 additions and 55 deletions.
5 changes: 4 additions & 1 deletion docs/HISTORY.rst
Expand Up @@ -8,11 +8,13 @@ Because you have to know where your towel is.

- Implements an original size scale to show the original image. [jpgimenez]

- Improve the way than images are accesed from the original object,
using the standard images traversal. (issue `#158`_) [jpgimenez]

- Fixed a bug with Plone 4.3 that avoided TinyMCE being displayed for
RichText. (closes `#157`_).
[ericof]


1.0a2 (2013-04-09)
^^^^^^^^^^^^^^^^^^
- Move Galleria's stylesheet and JS init to <head>. [davilima6]
Expand Down Expand Up @@ -65,4 +67,5 @@ Because you have to know where your towel is.
.. _`#146`: https://github.com/collective/collective.cover/issues/146
.. _`#152`: https://github.com/collective/collective.cover/issues/152
.. _`#157`: https://github.com/collective/collective.cover/issues/157
.. _`#158`: https://github.com/collective/collective.cover/issues/158
.. _`PloneFormGen`: https://pypi.python.org/pypi/Products.PloneFormGen
1 change: 1 addition & 0 deletions setup.py
Expand Up @@ -50,6 +50,7 @@
'collective.js.galleria',
'collective.js.jqueryui',
'Pillow',
'plone.cachepurging',
'plone.app.blocks',
'plone.app.dexterity[grok,relations]',
'plone.app.jquery>=1.7.2',
Expand Down
2 changes: 1 addition & 1 deletion src/collective/cover/testing.py
Expand Up @@ -88,7 +88,7 @@ def setUpPloneSite(self, portal):
# Install into Plone site using portal_setup
self.applyProfile(portal, 'collective.cover:default')
self.applyProfile(portal, 'collective.cover:testfixture')
portal['my-image'].setImage(loadFile('canoneye.jpg'))
portal['my-image'].setImage(generate_jpeg(50, 50))
portal['my-image1'].setImage(generate_jpeg(50, 50))
portal['my-image2'].setImage(generate_jpeg(50, 50))
portal['my-file'].setFile(loadFile('lorem_ipsum.txt'))
Expand Down
83 changes: 60 additions & 23 deletions src/collective/cover/tests/test_basic_tile.py
Expand Up @@ -4,7 +4,6 @@

from DateTime import DateTime

from ZODB.blob import Blob
from zope.interface.verify import verifyClass
from zope.interface.verify import verifyObject

Expand All @@ -16,8 +15,16 @@
from collective.cover.tiles.permissions import ITilesPermissions
from zope.annotation.interfaces import IAnnotations
from collective.cover.tiles.configuration import ITilesConfigurationScreen
from plone.scale.storage import AnnotationStorage as BaseAnnotationStorage
from collective.cover.tiles.base import AnnotationStorage
from zope.component import queryMultiAdapter
from plone.namedfile.file import NamedBlobImage as NamedImageFile
from plone.tiles.interfaces import ITileDataManager
from zope.annotation.interfaces import IAttributeAnnotatable
from zope.interface import alsoProvides
from zope.component import provideUtility
from zope.globalrequest import setRequest
from plone.registry.interfaces import IRegistry
from plone.cachepurging.interfaces import ICachePurgingSettings
from zope.component import queryUtility


class BasicTileTestCase(unittest.TestCase):
Expand Down Expand Up @@ -100,8 +107,8 @@ def test_basic_tile_render(self):
self.assertIn(
"This news item was created for testing purposes", rendered)

# the image must be there
self.assertIn('test-basic-tile/@@images', rendered)
# the image must be there, pointing to original image
self.assertIn('my-news-item/@@images', rendered)

# the localized time must be there
utils = getMultiAdapter((self.portal, self.request), name=u'plone')
Expand Down Expand Up @@ -152,29 +159,59 @@ def test_populate_with_file(self):

def test_image_traverser(self):
obj = self.portal['my-image']
self.tile.populate_with_object(obj)
data = self.tile.data
scales = queryMultiAdapter((obj, self.request), name="images")
self.tile.data['image'] = NamedImageFile(str(scales.scale('image').data))
data_mgr = ITileDataManager(self.tile)
data_mgr.set(data)
scales = self.layer['portal'].restrictedTraverse('@@%s/%s/@@images' %
('collective.cover.basic',
'test-basic-tile',))
img = scales.scale('image')
self.assertTrue(images_are_equal(str(self.tile.data['image'].data),
str(img.index_html().read())))

def test_modified_scale(self):
obj = self.portal['my-image']
obj_scales = obj.restrictedTraverse('@@images')
self.assertFalse(BaseAnnotationStorage(obj).items())
obj_scales.scale('image', width=64, height=64)
obj_storage = BaseAnnotationStorage(obj)
obj_storage[(('fieldname', 'image'),
('height', 64),
('width', 64))]['data'] = Blob(generate_jpeg(64, 64))
def test_basic_tile_image(self):
obj = self.portal['my-news-item']
obj.setImage(generate_jpeg(128, 128))
obj.reindexObject()

self.tile.populate_with_object(obj)
tile_storage = AnnotationStorage(self.tile)
self.assertTrue(images_are_equal(
obj_storage[(('fieldname', 'image'),
('height', 64),
('width', 64))]['data'].open().read(),
tile_storage[(('fieldname', 'image'),
('height', 64),
('width', 64))]['data'].open().read()))
rendered = self.tile()

# the image must be there, pointing to original image
self.assertIn('my-news-item/@@images', rendered)

# old code copy the image
self.assertNotIn('test-basic-tile/@@images', rendered)

def test_basic_tile_purge_cache(self):
request = self.request
alsoProvides(request, IAttributeAnnotatable)
setRequest(request)

registry = queryUtility(IRegistry)
registry.registerInterface(ICachePurgingSettings)
provideUtility(registry, IRegistry)

settings = registry.forInterface(ICachePurgingSettings)
settings.enabled = True
settings.cachingProxies = ('http://localhost:1234',)

obj = self.portal['my-image']
data = self.tile.data
scales = queryMultiAdapter((obj, self.request), name="images")
self.tile.data['image'] = NamedImageFile(str(scales.scale('image').data))
data_mgr = ITileDataManager(self.tile)
data_mgr.set(data)

self.assertEquals(set(['/@@collective.cover.basic/test-basic-tile',
'/@@collective.cover.basic/test-basic-tile/@@images/image',
'/@@collective.cover.basic/test-basic-tile/@@images/icon',
'/@@collective.cover.basic/test-basic-tile/@@images/mini',
'/@@collective.cover.basic/test-basic-tile/@@images/large',
'/@@collective.cover.basic/test-basic-tile/@@images/listing',
'/@@collective.cover.basic/test-basic-tile/@@images/thumb',
'/@@collective.cover.basic/test-basic-tile/@@images/preview',
'/@@collective.cover.basic/test-basic-tile/@@images/tile']),
IAnnotations(request)['plone.cachepurging.urls'])
32 changes: 31 additions & 1 deletion src/collective/cover/tiles/base.py
Expand Up @@ -7,7 +7,7 @@

from logging import exception
from AccessControl import Unauthorized
from Acquisition import aq_base
from Acquisition import aq_base, aq_parent
from ZODB.POSException import ConflictError

from zope.component import getMultiAdapter
Expand Down Expand Up @@ -51,6 +51,8 @@
from collective.cover.tiles.permissions import ITilesPermissions

from collective.cover import _
from z3c.caching.interfaces import IPurgePaths
from zope.component import adapts

logger = logging.getLogger(PROJECTNAME)

Expand Down Expand Up @@ -389,3 +391,31 @@ def scale(self, fieldname=None, scale=None,
info['fieldname'] = fieldname
scale_view = ImageScale(self.context, self.request, **info)
return scale_view.__of__(self.context)


class PersistentCoverTilePurgePaths(object):
"""Paths to purge for cover tiles
"""

implements(IPurgePaths)
adapts(IPersistentCoverTile)

def __init__(self, context):
self.context = context

def getRelativePaths(self):
context = self.context.aq_inner
portal_state = getMultiAdapter((context, context.request),
name=u'plone_portal_state')
prefix = context.url.replace(portal_state.portal_url(), '', 1)

yield prefix
for _, v in context.data.items():
if INamedImage.providedBy(v):
yield "%s/@@images/image" % prefix
scales = aq_parent(context).restrictedTraverse(prefix + '/@@images')
for size in scales.getAvailableSizes().keys():
yield "%s/@@images/%s" % (prefix, size,)

def getAbsolutePaths(self):
return []
42 changes: 22 additions & 20 deletions src/collective/cover/tiles/basic.py
@@ -1,15 +1,11 @@
# -*- coding: utf-8 -*-
import time
from Acquisition import aq_inner
from zope import schema
from zope.interface import implements
from zope.component import getUtility
from zope.component import queryMultiAdapter

from plone.memoize import view
from plone.memoize.instance import memoizedproperty
from plone.namedfile.field import NamedBlobImage as NamedImage
from plone.namedfile.file import NamedBlobImage as NamedImageFile
from plone.registry.interfaces import IRegistry
from plone.tiles.interfaces import ITileDataManager
from plone.uuid.interfaces import IUUID
Expand All @@ -23,8 +19,7 @@
from collective.cover.tiles.base import PersistentCoverTile
from collective.cover.controlpanel import ICoverSettings
from collective.cover.tiles.configuration_view import IDefaultConfigureForm
from collective.cover.tiles.base import AnnotationStorage
from plone.scale.storage import AnnotationStorage as BaseAnnotationStorage
from plone.app.uuid.utils import uuidToObject


class IBasicTile(IPersistentCoverTile):
Expand Down Expand Up @@ -109,6 +104,21 @@ def Subject(self):
if self.brain is not None:
return self.brain.Subject

def img_obj(self):
""" Return the image object, internal or external.
"""
if self.data.get('image') not in (None, True):
return self
elif self.data.get('uuid') is not None:
obj = uuidToObject(self.data.get('uuid'))
try:
# Target obj have an image?
obj.restrictedTraverse('@@images').scale('image')
return obj
except AttributeError:
return None
return None

def populate_with_object(self, obj):
super(BasicTile, self).populate_with_object(obj)

Expand All @@ -128,22 +138,8 @@ def populate_with_object(self, obj):
# behaviour enable then it will not work here
# we need to figure out how to enforce the use of
# plone.app.referenceablebehavior

obj = aq_inner(obj)
try:
scales = queryMultiAdapter((obj, self.request), name="images")
data['image'] = NamedImageFile(str(scales.scale('image').data))
except AttributeError:
pass
data_mgr = ITileDataManager(self)
data_mgr.set(data)
tile_storage = AnnotationStorage(self)
obj_storage = BaseAnnotationStorage(obj)
for k, v in obj_storage.items():
tile_storage.storage[k] = v
tile_storage.storage[k]['modified'] = '%f' % time.time()
scale_data = obj_storage.storage[k]['data'].open().read()
tile_storage.storage[k]['data'] = NamedImageFile(str(scale_data))

@view.memoize
def accepted_ct(self):
Expand All @@ -158,3 +154,9 @@ def accepted_ct(self):
registry = getUtility(IRegistry)
settings = registry.forInterface(ICoverSettings)
return settings.searchable_content_types

def get_configured_fields(self):
if self.data['image'] is None and self.data['uuid']:
self.data['image'] = True
fields = super(BasicTile, self).get_configured_fields()
return fields
1 change: 0 additions & 1 deletion src/collective/cover/tiles/carousel.py
@@ -1,5 +1,4 @@
# -*- coding: utf-8 -*-

from zope import schema
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile

Expand Down
2 changes: 2 additions & 0 deletions src/collective/cover/tiles/configure.zcml
Expand Up @@ -225,4 +225,6 @@
factory=".list.GenericUIDsProvider"
/>

<adapter factory=".base.PersistentCoverTilePurgePaths" name="collective.cover.tiles" />

</configure>
3 changes: 3 additions & 0 deletions src/collective/cover/tiles/data.py
Expand Up @@ -9,6 +9,8 @@
from plone.namedfile.interfaces import INamedImage

from collective.cover.tiles.base import IPersistentCoverTile
from z3c.caching.purge import Purge
from zope.event import notify


class PersistentCoverTileDataManager(PersistentTileDataManager):
Expand Down Expand Up @@ -41,5 +43,6 @@ def set(self, data):
(self.key in self.annotations and
data[k] != self.annotations[self.key][k])):
# set modification time of the image
notify(Purge(self.tile))
data['%s_mtime' % k] = '%f' % time.time()
self.annotations[self.key] = PersistentDict(data)
25 changes: 21 additions & 4 deletions src/collective/cover/tiles/image.py
@@ -1,17 +1,21 @@
# -*- coding: utf-8 -*-

import time
from Acquisition import aq_inner
from zope.interface import implements
from zope.component import queryMultiAdapter

from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile

from plone.namedfile.field import NamedBlobImage as NamedImage
from plone.namedfile.file import NamedBlobImage
from plone.namedfile.file import NamedBlobImage as NamedImageFile

from plone.tiles.interfaces import ITileDataManager

from collective.cover import _
from collective.cover.tiles.base import IPersistentCoverTile
from collective.cover.tiles.base import PersistentCoverTile
from collective.cover.tiles.base import AnnotationStorage
from plone.scale.storage import AnnotationStorage as BaseAnnotationStorage


class IImageTile(IPersistentCoverTile):
Expand All @@ -37,9 +41,22 @@ def populate_with_object(self, obj):
# check permissions
super(ImageTile, self).populate_with_object(obj)

data = {}
obj = aq_inner(obj)
try:
scales = queryMultiAdapter((obj, self.request), name="images")
data['image'] = NamedImageFile(str(scales.scale('image').data))
except AttributeError:
pass
data_mgr = ITileDataManager(self)

data_mgr.set({'image': NamedBlobImage(obj.getImage().data)})
data_mgr.set(data)
tile_storage = AnnotationStorage(self)
obj_storage = BaseAnnotationStorage(obj)
for k, v in obj_storage.items():
tile_storage.storage[k] = v
tile_storage.storage[k]['modified'] = '%f' % time.time()
scale_data = obj_storage.storage[k]['data'].open().read()
tile_storage.storage[k]['data'] = NamedImageFile(str(scale_data))

def accepted_ct(self):
""" Return a list of content types accepted by the tile.
Expand Down
18 changes: 14 additions & 4 deletions src/collective/cover/tiles/templates/basic.pt
Expand Up @@ -40,10 +40,20 @@
</h4>
</tal:title>

<tal:image condition="python:field['id'] == 'image'"
define="scale python:field.get('scale', 'large').split()[0];
position python:field.get('position', '');">
<span metal:use-macro="context/@@tile_macros/image" />
<tal:image condition="python:field['id'] == 'image' and img_obj"
define="scale python:field.get('scale', 'large');
position python:field.get('position', '');
img_obj view/img_obj;">
<a class="imag" tal:attributes="href view/getURL;
title description;">
<img tal:define="scales img_obj/@@images;
thumbnail python: scales.scale('image', scale=scale);"
tal:condition="thumbnail"
tal:attributes="src thumbnail/url;
width thumbnail/width;
height thumbnail/height;
class position;" />
</a>
</tal:image>

<tal:description condition="python:field['id'] == 'description'">
Expand Down

0 comments on commit 77522bb

Please sign in to comment.