Skip to content

Commit

Permalink
Merge pull request #8 from plone/jensens-cleanup
Browse files Browse the repository at this point in the history
Cleanup: PEP8, plone-coding conventions, ReST fixes, documentation ov…
  • Loading branch information
mauritsvanrees committed Oct 12, 2015
2 parents 2ab8a02 + 8a44d1d commit 77a62df
Show file tree
Hide file tree
Showing 15 changed files with 254 additions and 204 deletions.
4 changes: 3 additions & 1 deletion CHANGES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ Changelog
1.0.5 (unreleased)
------------------

- Nothing changed yet.
- Cleanup: PEP8, plone-coding conventions, ReST fixes, documentation
overhaul, et al.
[jensens]


1.0.4 (2015-03-21)
Expand Down
148 changes: 72 additions & 76 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,10 @@ Introduction
============

``plone.resource`` publishes directories of static files via the ZPublisher.
These directories may be located either in the ZODB (as OFS folders and
files), or on the filesystem.
These directories may be located either in the ZODB (as OFS folders and files), or on the filesystem.

Each resource directory has a type and a name. When combined, these are used
to traverse to the resource directory. For example::
Each resource directory has a type and a name. When combined, these are used to traverse to the resource directory.
For example::

/++theme++mytheme/<subpath>
/++sitelayout++mylayout/<subpath>
Expand All @@ -16,133 +15,130 @@ to traverse to the resource directory. For example::
Where resources can be stored
-----------------------------

Resource directory contents can be found by the traverser in several different
places. The following locations are tried in order.
Resource directory contents can be found by the traverser in several different places.
The following locations are tried in order.

Files in the ZODB
^^^^^^^^^^^^^^^^^

Installing ``plone.resource`` creates a folder called portal_resources which
can be used to store resource directories persistently. By convention, the
top-level folders under this folder correspond to resource types, and the
second-level folders correspond to the resource directory name.
Installing ``plone.resource`` creates a Zope-folder called ``portal_resources``.
It can be used to store resource directories persistently.
By convention:

So, the file traversable at /++theme++mytheme/myfile could be physically
located at some_site/++etc++site/resources/theme/mytheme
- the top-level folders under this folder correspond to resource types,
- the second-level folders correspond to the resource directory name.

(XXX: provide a helper to upload a tarball/zip)
So, the file traversable at ``/++theme++mytheme/myfile`` could be physically located at ``some_site/++etc++site/resources/theme/mytheme``

.. TODO (XXX: provide a helper to upload a tarball/zip)
Files in Python distributions
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

A folder in a Python distribution (e.g. egg) can be registered as a resource
directory of a particular type and name using the plone:static ZCML
directive. For example, this registers a directory named "theme" as a
resource directory of type "theme". It would be accessible at
++theme++mytheme::
A folder in a Python distribution (e.g. egg) can be registered as a resource directory of a particular type and name using the ``plone:static`` ZCML directive.
For example, this registers a directory named "theme" as a resource directory of type "theme" under the name "mytheme".
It would be accessible at ``++theme++mytheme``::

<plone:static
directory="theme"
type="theme"
name="mytheme"
/>
directory="theme"
type="theme"
name="mytheme"
/>

.. note::
You must do ``<include package="plone.resource" file="meta.zcml"/>``
before you can use the plone:static directive.

The name of the resource directory defaults to the name of the package, so
can be omitted. e.g. the following directive in a package named "mytheme"
would result in the same registration as above::
The name of the resource directory defaults to the name of the package, so can be omitted.
E.g. the following directive in a package named "mytheme" would result in the same registration as above::

<plone:static
directory="resources"
type="theme"
/>
directory="resources"
type="theme"
/>

Traversing upward in directory paths using .. is not supported, as it could
allow unwanted file access.
Traversing upward in directory paths using ``..`` is not supported for security reasons, as it could allow unwanted file access.

Minimum zcml config example
^^^^^^^^^^^^^^^^^^^^^^^^^^^
Note::

<configure xmlns:plone="http://namespaces.plone.org/plone">
::

<configure xmlns:plone="http://namespaces.plone.org/plone">
<include package="plone.resource" file="meta.zcml"/>
<plone:static
directory="resources"
type="theme"
name="myproject"
directory="resources"
type="theme"
name="myproject"
/>
</configure>
</configure>

..

Files in a central resource directory
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

If the ``plone:static`` directive is used from ``site.zcml`` (i.e., with no
active package in the ZCML import context), then it may specify the
absolute path to a top-level resources directory. This directory should
have the same sub-directory structure as the in-ZODB resources directory in
that top-level directories are resource types, and 2nd-level directories
are resource directory names. In addition, in order for resources to be
available, the top-level directories require a traverser to be registered.
If the ``plone:static`` directive is used from ``site.zcml`` (i.e., with no active package in the ZCML import context),
then it may specify the absolute path to a top-level resources directory.

For example, the following in ``site.zcml`` would register the given path
within the buildout root::
This directory should have the same sub-directory structure as explained above (in-ZODB resources directory):

<plone:static
directory="/path/to/buildout/var/resources"
/>
- the top-level folders under this folder correspond to resource types,
- the second-level folders correspond to the resource directory name.

In addition, in order for resources to be available, the top-level directories *require a traverser* to be registered!

For example, the following in ``site.zcml`` registers the given path within the buildout root::

<plone:static
directory="/path/to/buildout/resources"
/>

Typically, this can be injected into ``site.zcml`` by specifying the
``resources option`` in the `plone.recipe.zope2instance`_
buildout recipe, like this::
In order to automate this at buildout time, `plone.recipe.zope2instance`_ recipe has the option ``resources``.
It injects the above zcml snippet with into ``site.zcml`` by specifying the option like this::

[instance]
...
resources = ${buildout:directory}/resources
...

As a worked example, if one wanted to serve resources for use with
``plone.app.theming``, which provides the ``++theme++`` traverser, then
a resource located at::
Example:
Using ``plone.app.theming`` - which provides the ``++theme++`` traverser - given an image file located in filesystem at::

${directory}/resources/theme/my.project/logo.png
${buildout:directory}/resources/theme/my.project/logo.png``

would be traversable at a URL like so::
This would be traversable at a URL like so::

http://localhost:8080/Plone/++theme++my.project/logo.png
http://localhost:8080/Plone/++theme++my.project/logo.png

.. _`plone.recipe.zope2instance`: http://pypi.python.org/pypi/plone.recipe.zope2instance

Additional traversers
---------------------

Traversers can be registered via ZCML using an adapter like so::
Custom traversers can be registered via ZCML using an adapter like so::

<adapter
name="demo"
for="* zope.publisher.interfaces.IRequest"
provides="zope.traversing.interfaces.ITraversable"
factory="my.project.traversal.MyTraverser"
/>
<adapter
name="demo"
for="* zope.publisher.interfaces.IRequest"
provides="zope.traversing.interfaces.ITraversable"
factory="my.project.traversal.MyTraverser"
/>

with a corresponding factory definition of::
with a corresponding simple factory definition of::

from plone.resource.traversal import ResourceTraverser
class MyTraverser(ResourceTraverser):
name = 'demo'
from plone.resource.traversal import ResourceTraverser
class MyTraverser(ResourceTraverser):
name = 'demo'

This, when coupled with configuration like that in the
`Files in a central resource directory`_ section above, would mean that
resources located at::
This, when coupled with configuration like that in the `Files in a central resource directory`_ section above, would mean that resources located at::

${directory}/resources/demo/my.project/logo.png
${buildout:directory}/resources/demo/my.project/logo.png

would be traversable at a URL like so::
would be traversable at a URL like so::

http://localhost:8080/Plone/++demo++my.project/logo.png
http://localhost:8080/Plone/++demo++my.project/logo.png

What types of resources can be stored
-------------------------------------
.. TODO: What types of resources can be stored
8 changes: 2 additions & 6 deletions plone/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,2 @@
# See http://peak.telecommunity.com/DevCenter/setuptools#namespace-packages
try:
__import__('pkg_resources').declare_namespace(__name__)
except ImportError: # pragma: no cover
from pkgutil import extend_path
__path__ = extend_path(__path__, __name__)
# -*- coding: utf-8 -*-
__import__('pkg_resources').declare_namespace(__name__)
1 change: 1 addition & 0 deletions plone/resource/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# -*- coding: utf-8 -*-
13 changes: 7 additions & 6 deletions plone/resource/caching.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
from zope.interface import Interface
from zope.interface import implements
from zope.component import adapts
# -*- coding: utf-8 -*-
from plone.caching.interfaces import IRulesetLookup
from plone.resource.interfaces import IUniqueResourceRequest
from zope.component import adapter
from zope.interface import implementer
from zope.interface import Interface


@implementer(IRulesetLookup)
@adapter(Interface, IUniqueResourceRequest)
class UniqueResourceLookup(object):
"""Unique resource ruleset lookup.
Returns 'plone.stableResource' for requests marked with
IUniqueResourceRequest.
"""

implements(IRulesetLookup)
adapts(Interface, IUniqueResourceRequest)

def __init__(self, published, request):
pass

Expand Down
33 changes: 17 additions & 16 deletions plone/resource/directory.py
Original file line number Diff line number Diff line change
@@ -1,33 +1,32 @@
import os.path
import re
import zipfile

# -*- coding: utf-8 -*-
from Acquisition import aq_base, aq_parent
from OFS.Image import File
from OFS.interfaces import IObjectManager
from plone.resource.file import FilesystemFile
from plone.resource.interfaces import IResourceDirectory
from plone.resource.interfaces import IWritableResourceDirectory
from Products.BTreeFolder2.BTreeFolder2 import BTreeFolder2
from Products.CMFCore.utils import getToolByName
from StringIO import StringIO
from zExceptions import NotFound
from zope.interface import implements
from zope.interface import implementer
from zope.site.hooks import getSite

from plone.resource.file import FilesystemFile
from plone.resource.interfaces import IResourceDirectory
from plone.resource.interfaces import IWritableResourceDirectory
import os.path
import re
import zipfile

# filter dot files, Mac resource forks
FILTERS = (r'\..*', '__MACOSX')
FILTERS = [re.compile(pattern) for pattern in FILTERS]


@implementer(IWritableResourceDirectory)
class PersistentResourceDirectory(object):
"""A resource directory stored in the ZODB.
It is assumed that directories provide IObjectManager
and that files are instances of OFS.Image.File.
"""
implements(IWritableResourceDirectory)

def __init__(self, context=None):
if context is None:
Expand Down Expand Up @@ -94,7 +93,7 @@ def readFile(self, path):

def listDirectory(self):
return [n for n in self.context.objectIds()
if not any(filter.match(n) for filter in FILTERS)]
if not any(filter.match(n) for filter in FILTERS)]

def isDirectory(self, path):
try:
Expand Down Expand Up @@ -181,10 +180,11 @@ def importZip(self, f):
data = f.open(member).read()
self.writeFile(path, data)


@implementer(IResourceDirectory)
class FilesystemResourceDirectory(object):
"""A resource directory based on files in the filesystem.
"""
implements(IResourceDirectory)

__allow_access_to_unprotected_subobjects__ = True

Expand Down Expand Up @@ -241,7 +241,7 @@ def readFile(self, path):
def listDirectory(self):
names = os.listdir(self.directory)
return [n for n in names
if not any(filter.match(n) for filter in FILTERS)]
if not any(filter.match(n) for filter in FILTERS)]

def isDirectory(self, path):
return os.path.isdir(self._resolveSubpath(path))
Expand All @@ -250,7 +250,6 @@ def isFile(self, path):
return os.path.isfile(self._resolveSubpath(path))

def exportZip(self, out):
prefix = self.__name__
zf = zipfile.ZipFile(out, 'w')

toStrip = len(self.directory.replace(os.path.sep, '/')) + 1
Expand All @@ -266,6 +265,8 @@ def exportZip(self, out):
):
continue

zf.writestr('/'.join([prefix, path,]), self.readFile(path))

zf.writestr(
'/'.join([self.__name__, path, ]),
self.readFile(path)
)
zf.close()
7 changes: 6 additions & 1 deletion plone/resource/download.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# -*- coding: utf-8 -*-
from StringIO import StringIO
from zope.publisher.browser import BrowserView


class DownloadView(BrowserView):

def __call__(self):
Expand All @@ -16,7 +18,10 @@ def __call__(self):
self.context.exportZip(out)

response.setHeader('Content-Type', 'application/zip')
response.setHeader('Content-Disposition', 'attachment; filename="%s.zip"' % name)
response.setHeader(
'Content-Disposition',
'attachment; filename="%s.zip"' % name
)
response.setHeader('Content-Length', len(out.getvalue()))

response.write(out.getvalue())

0 comments on commit 77a62df

Please sign in to comment.