zope.dublincore-3.8.2/0000755000175000017500000000000011527746337014551 5ustar tseavertseaverzope.dublincore-3.8.2/buildout.cfg0000644000175000017500000000042611527746247017063 0ustar tseavertseaver[buildout] extends = http://download.zope.org/zopetoolkit/index/1.0.1/zopeapp-versions.cfg http://download.zope.org/zopetoolkit/index/1.0.1/ztk-versions.cfg versions = versions develop = . parts = test [test] recipe = zc.recipe.testrunner eggs = zope.dublincore [test] zope.dublincore-3.8.2/COPYRIGHT.txt0000644000175000017500000000004011527746247016654 0ustar tseavertseaverZope Foundation and Contributorszope.dublincore-3.8.2/README.txt0000644000175000017500000000122511527746247016247 0ustar tseavertseaver``zope.dublincore`` provides a Dublin Core support for Zope-based web applications. This includes: * an ``IZopeDublinCore`` interface definition that can be implemented by objects directly or via an adapter to support DublinCore metadata. * an ``IZopeDublinCore`` adapter for annotatable objects (objects providing ``IAnnotatable`` from ``zope.annotation``). * a partial adapter for objects that already implement some of the ``IZopeDublinCore`` API, * a "Metadata" browser page (which by default appears in the ZMI), * subscribers to various object lifecycle events that automatically set the created and modified date and some other metadata. zope.dublincore-3.8.2/LICENSE.txt0000644000175000017500000000402611527746247016376 0ustar tseavertseaverZope Public License (ZPL) Version 2.1 A copyright notice accompanies this license document that identifies the copyright holders. This license has been certified as open source. It has also been designated as GPL compatible by the Free Software Foundation (FSF). Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions in source code must retain the accompanying copyright notice, this list of conditions, and the following disclaimer. 2. Redistributions in binary form must reproduce the accompanying copyright notice, this list of conditions, and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Names of the copyright holders must not be used to endorse or promote products derived from this software without prior written permission from the copyright holders. 4. The right to distribute this software or to use it for any purpose does not give you the right to use Servicemarks (sm) or Trademarks (tm) of the copyright holders. Use of them is covered by separate agreement with the copyright holders. 5. If any files are modified, you must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. Disclaimer THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. zope.dublincore-3.8.2/bootstrap.py0000644000175000017500000000330211527746247017136 0ustar tseavertseaver############################################################################## # # Copyright (c) 2006 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Bootstrap a buildout-based project Simply run this script in a directory containing a buildout.cfg. The script accepts buildout command-line options, so you can use the -c option to specify an alternate configuration file. """ import os, shutil, sys, tempfile, urllib2 tmpeggs = tempfile.mkdtemp() ez = {} exec urllib2.urlopen('http://peak.telecommunity.com/dist/ez_setup.py' ).read() in ez ez['use_setuptools'](to_dir=tmpeggs, download_delay=0) import pkg_resources cmd = 'from setuptools.command.easy_install import main; main()' if sys.platform == 'win32': cmd = '"%s"' % cmd # work around spawn lamosity on windows ws = pkg_resources.working_set assert os.spawnle( os.P_WAIT, sys.executable, sys.executable, '-c', cmd, '-mqNxd', tmpeggs, 'zc.buildout', dict(os.environ, PYTHONPATH= ws.find(pkg_resources.Requirement.parse('setuptools')).location ), ) == 0 ws.add_entry(tmpeggs) ws.require('zc.buildout') import zc.buildout.buildout zc.buildout.buildout.main(sys.argv[1:] + ['bootstrap']) shutil.rmtree(tmpeggs) zope.dublincore-3.8.2/setup.py0000644000175000017500000000474211527746247016272 0ustar tseavertseaver############################################################################## # # Copyright (c) 2007 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## # This package is developed by the Zope Toolkit project, documented here: # http://docs.zope.org/zopetoolkit # When developing and releasing this package, please follow the documented # Zope Toolkit policies as described by this documentation. ############################################################################## """Setup for zope.dublincore package """ from setuptools import setup, find_packages import os.path def read(*path): return open(os.path.join(*path)).read() + '\n\n' version = '3.8.2' long_description = ( '.. contents::\n\n' + '========\n' + 'Overview\n' + '========\n' + read('README.txt') + read('src', 'zope', 'dublincore', 'property.txt') + read('src', 'zope', 'dublincore', 'tests', 'partial.txt') + read('src', 'zope', 'dublincore', 'tests', 'timeannotators.txt') + read('CHANGES.txt') ) setup( name="zope.dublincore", version=version, url='http://pypi.python.org/pypi/zope.dublincore', license='ZPL 2.1', description='Zope Dublin Core implementation', long_description=long_description, author='Zope Foundation and Contributors', author_email='zope-dev@zope.org', packages=find_packages('src'), package_dir={'':'src'}, namespace_packages=['zope'], include_package_data=True, extras_require=dict( test=['zope.testing >= 3.8', 'zope.annotation', 'zope.configuration', ] ), install_requires = ['setuptools', 'pytz', 'zope.component[zcml]', 'zope.datetime', 'zope.interface', 'zope.lifecycleevent', 'zope.location', 'zope.schema', 'zope.security[zcml]>=3.8', ], zip_safe = False ) zope.dublincore-3.8.2/src/0000755000175000017500000000000011527746337015340 5ustar tseavertseaverzope.dublincore-3.8.2/src/zope/0000755000175000017500000000000011527746337016315 5ustar tseavertseaverzope.dublincore-3.8.2/src/zope/dublincore/0000755000175000017500000000000011527746337020443 5ustar tseavertseaverzope.dublincore-3.8.2/src/zope/dublincore/property.py0000644000175000017500000000643611527746247022712 0ustar tseavertseaver############################################################################## # # Copyright (c) 2005 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Base DublinCore property adapter. """ __docformat__ = 'restructuredtext' from zope import schema from zope.dublincore.interfaces import IZopeDublinCore from zope.dublincore.zopedublincore import SequenceProperty _marker = object() class DCProperty(object): """Adapt to a dublin core property Handles DC list properties as scalar property. """ def __init__(self, name): self.__name = name def __get__(self, inst, klass): if inst is None: return self name = self.__name inst = IZopeDublinCore(inst) value = getattr(inst, name, _marker) if value is _marker: field = IZopeDublinCore[name].bind(inst) value = getattr(field, 'default', _marker) if value is _marker: raise AttributeError(name) if isinstance(value, (list, tuple)): value = value[0] return value def __set__(self, inst, value): name = self.__name inst = IZopeDublinCore(inst) field = IZopeDublinCore[name].bind(inst) if isinstance(field, schema.List): if isinstance(value, tuple): value = list(value) else: value = [value] elif isinstance(field, schema.Tuple): if isinstance(value, list): value = tuple(value) else: value = (value,) field.validate(value) if field.readonly and inst.__dict__.has_key(name): raise ValueError(name, 'field is readonly') setattr(inst, name, value) def __getattr__(self, name): return getattr(IZopeDublinCore[self.__name], name) class DCListProperty(DCProperty): """Adapt to a dublin core list property Returns the DC property unchanged. """ def __init__(self, name): self.__name = name def __get__(self, inst, klass): if inst is None: return self name = self.__name inst = IZopeDublinCore(inst) value = getattr(inst, name, _marker) if value is _marker: field = IZopeDublinCore[name].bind(inst) value = getattr(field, 'default', _marker) if value is _marker: raise AttributeError(name) return value def __set__(self, inst, value): name = self.__name inst = IZopeDublinCore(inst) field = IZopeDublinCore[name].bind(inst) if isinstance(field, schema.Tuple): value = tuple(value) field.validate(value) if field.readonly and inst.__dict__.has_key(name): raise ValueError(name, 'field is readonly') setattr(inst, name, value) zope.dublincore-3.8.2/src/zope/dublincore/dcterms.py0000644000175000017500000001547411527746247022471 0ustar tseavertseaver############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Support information for qualified Dublin Core Metadata. """ __docformat__ = 'restructuredtext' from zope.dublincore import dcsv # useful namespace URIs DC_NS = "http://purl.org/dc/elements/1.1/" DCTERMS_NS = "http://purl.org/dc/terms/" XSI_NS = "http://www.w3.org/2001/XMLSchema-instance" W3CDTF = "W3CDTF" def splitEncoding(name): if "." not in name: return name, None parts = name.split(".") if parts[-1] in encodings: if len(parts) == 2: return parts else: return ".".join(parts[:-1]), parts[-1] else: return name, None # The type validator function must raise an exception if the value # passed isn't valid for the type being check, other just return. _dcmitypes = {} for x in ("Collection Dataset Event Image InteractiveResource" " Service Software Sound Text PhysicalObject").split(): _dcmitypes[x.lower()] = x del x def check_dcmitype(value): if value.lower() not in _dcmitypes: raise ValueError("%r not a valid DCMIType") def check_imt(value): pass def check_iso639_2(value): pass def check_rfc1766(value): pass def check_uri(value): pass def check_point(value): pass def check_iso3166(value): pass def check_box(value): pass def check_tgn(value): pass _period_fields = "name start end scheme".split() def check_period(value): # checks a Period in DCSV format; see: # http://dublincore.org/documents/dcmi-period/ items = dcsv.decode(value) d = dcsv.createMapping(items) for k in d: if k not in _period_fields: raise ValueError("unknown field label %r" % k) if d.get("scheme", W3CDTF).upper() == W3CDTF: if "start" in d: check_w3cdtf(d["start"]) if "end" in d: check_w3cdtf(d["end"]) def check_w3cdtf(value): pass def check_rfc3066(value): pass encodings = { # name --> (allowed for, validator|None), "LCSH": (("Subject",), None), "MESH": (("Subject",), None), "DDC": (("Subject",), None), "LCC": (("Subject",), None), "UDC": (("Subject",), None), "DCMIType": (("Type",), check_dcmitype), "IMT": (("Format",), check_imt), "ISO639-2": (("Language",), check_iso639_2), "RFC1766": (("Language",), check_rfc1766), "URI": (("Identifier", "Relation", "Source",), check_uri), "Point": (("Coverage.Spatial",), check_point), "ISO3166": (("Coverage.Spatial",), check_iso3166), "Box": (("Coverage.Spatial",), check_box), "TGN": (("Coverage.Spatial",), check_tgn), "Period": (("Coverage.Temporal",), check_period), W3CDTF: (("Coverage.Temporal", "Date",), check_w3cdtf), "RFC3066": (("Language",), check_rfc3066), } name_to_element = { # unqualified DCMES 1.1 "Title": ("dc:title", ""), "Creator": ("dc:creator", ""), "Subject": ("dc:subject", ""), "Description": ("dc:description", ""), "Publisher": ("dc:publisher", ""), "Contributor": ("dc:contributor", ""), "Date": ("dc:date", "dcterms:"+W3CDTF), "Type": ("dc:type", ""), "Format": ("dc:format", ""), "Identifier": ("dc:identifier", ""), "Source": ("dc:source", ""), "Language": ("dc:language", ""), "Relation": ("dc:relation", ""), "Coverage": ("dc:coverage", ""), "Rights": ("dc:rights", ""), # qualified DCMES 1.1 (directly handled by Zope) "Date.Created": ("dcterms:created", "dcterms:"+W3CDTF), "Date.Modified": ("dcterms:modified", "dcterms:"+W3CDTF), # qualified DCMES 1.1 (not used by Zope) "Audience": ("dcterms:audience", ""), "Audience.Education Level": ("dcterms:educationLevel", ""), "Audience.Mediator": ("dcterms:mediator", ""), "Coverage.Spatial": ("dcterms:spatial", ""), "Coverage.Temporal": ("dcterms:temporal", ""), "Date.Accepted": ("dcterms:accepted", "dcterms:"+W3CDTF), "Date.Available": ("dcterms:available", "dcterms:"+W3CDTF), "Date.Copyrighted": ("dcterms:copyrighted","dcterms:"+W3CDTF), "Date.Issued": ("dcterms:issued", "dcterms:"+W3CDTF), "Date.Submitted": ("dcterms:submitted", "dcterms:"+W3CDTF), "Date.Valid": ("dcterms:valid", "dcterms:"+W3CDTF), "Description.Abstract": ("dcterms:abstract", ""), "Description.Table Of Contents": ("dcterms:tableOfContents", ""), "Format": ("dc:format", ""), "Format.Extent": ("dcterms:extent", ""), "Format.Medium": ("dcterms:medium", ""), "Identifier.Bibliographic Citation": ("dcterms:bibliographicCitation", ""), "Relation.Is Version Of": ("dcterms:isVersionOf", ""), "Relation.Has Version": ("dcterms:hasVersion", ""), "Relation.Is Replaced By": ("dcterms:isReplacedBy", ""), "Relation.Replaces": ("dcterms:replaces", ""), "Relation.Is Required By": ("dcterms:isRequiredBy", ""), "Relation.Requires": ("dcterms:requires", ""), "Relation.Is Part Of": ("dcterms:isPartOf", ""), "Relation.Has Part": ("dcterms:hasPart", ""), "Relation.Is Referenced By": ("dcterms:isReferencedBy", ""), "Relation.References": ("dcterms:references", ""), "Relation.Is Format Of": ("dcterms:isFormatOf", ""), "Relation.Has Format": ("dcterms:hasFormat", ""), "Relation.Conforms To": ("dcterms:conformsTo", ""), "Rights.Access Rights": ("dcterms:accessRights", ""), "Title.Alternative": ("dcterms:alternative", ""), } _prefix_to_ns = { "dc": DC_NS, "dcterms": DCTERMS_NS, # "xsi": XSI_NS, dont' use this for element names, only attrs } element_to_name = {} for name, (qname, attrs) in name_to_element.iteritems(): prefix, localname = qname.split(":") elem_name = _prefix_to_ns[prefix], localname element_to_name[elem_name] = name name_to_element[name] = (elem_name, attrs) zope.dublincore-3.8.2/src/zope/dublincore/browser/0000755000175000017500000000000011527746337022126 5ustar tseavertseaverzope.dublincore-3.8.2/src/zope/dublincore/browser/metadataedit.py0000644000175000017500000000401411527746247025125 0ustar tseavertseaver############################################################################## # # Copyright (c) 2002 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Dublin Core Meta Data View """ __docformat__ = 'restructuredtext' from datetime import datetime from zope.event import notify from zope.dublincore.interfaces import IZopeDublinCore from zope.lifecycleevent import ObjectModifiedEvent, Attributes from zope.i18nmessageid import MessageFactory _ = MessageFactory('zope') class MetaDataEdit(object): """Provide view for editing basic dublin-core meta-data.""" def edit(self): request = self.request formatter = self.request.locale.dates.getFormatter('dateTime', 'medium') dc = IZopeDublinCore(self.context) message='' if 'dctitle' in request: dc.title = unicode(request['dctitle']) dc.description = unicode(request['dcdescription']) description = Attributes(IZopeDublinCore, 'title', 'description') notify(ObjectModifiedEvent(self.context, description)) message = _("Changed data ${datetime}", mapping={'datetime': formatter.format(datetime.utcnow())}) return { 'message': message, 'dctitle': dc.title, 'dcdescription': dc.description, 'modified': (dc.modified or dc.created) and \ formatter.format(dc.modified or dc.created) or '', 'created': dc.created and formatter.format(dc.created) or '', 'creators': dc.creators } zope.dublincore-3.8.2/src/zope/dublincore/browser/box.pt0000644000175000017500000000141011527746247023257 0ustar tseavertseaver
Title:
Description:
Created: 2000-01-01 01:01:01
Modified: 2000-01-01 01:01:01
zope.dublincore-3.8.2/src/zope/dublincore/browser/configure.zcml0000644000175000017500000000121711527746247024777 0ustar tseavertseaver zope.dublincore-3.8.2/src/zope/dublincore/browser/__init__.py0000644000175000017500000000007511527746247024241 0ustar tseavertseaver# # This file is necessary to make this directory a package. zope.dublincore-3.8.2/src/zope/dublincore/browser/edit.pt0000644000175000017500000000336211527746247023424 0ustar tseavertseaver

Message here

Title
Description
Created
2000-01-01 01:01:01
Content Last Modified
2000-01-01 01:01:01
Creator
Bart Simpson
zope.dublincore-3.8.2/src/zope/dublincore/property.txt0000644000175000017500000000214211527746247023067 0ustar tseavertseaver====================== Dublin Core Properties ====================== A dublin core property allows us to use properties from dublin core by simply defining a property as DCProperty. >>> from zope.dublincore import property >>> from zope import interface >>> from zope.annotation.interfaces import IAttributeAnnotatable >>> class DC(object): ... interface.implements(IAttributeAnnotatable) ... title = property.DCProperty('title') ... author = property.DCProperty('creators') ... authors = property.DCListProperty('creators') >>> obj = DC() >>> obj.title = u'My title' >>> obj.title u'My title' Let's see if the title is really stored in dublin core: >>> from zope.dublincore.interfaces import IZopeDublinCore >>> IZopeDublinCore(obj).title u'My title' Even if a dublin core property is a list property we can set and get the property as scalar type: >>> obj.author = u'me' >>> obj.author u'me' DCListProperty acts on the list: >>> obj.authors (u'me',) >>> obj.authors = [u'I', u'others'] >>> obj.authors (u'I', u'others') >>> obj.author u'I' zope.dublincore-3.8.2/src/zope/dublincore/testing.py0000644000175000017500000000173211527746247022475 0ustar tseavertseaver############################################################################## # # Copyright (c) 2005 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Testing support """ __docformat__ = 'restructuredtext' from zope import component from annotatableadapter import ZDCAnnotatableAdapter from interfaces import IWriteZopeDublinCore def setUpDublinCore(): component.provideAdapter(ZDCAnnotatableAdapter, provides=IWriteZopeDublinCore, ) zope.dublincore-3.8.2/src/zope/dublincore/interfaces.py0000644000175000017500000003413611527746247023147 0ustar tseavertseaver############################################################################## # # Copyright (c) 2002 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Dublin Core interfaces """ __docformat__ = 'restructuredtext' from zope.interface import Interface from zope.schema import Text, TextLine, Datetime, Tuple class IDublinCoreElementItem(Interface): """A qualified dublin core element""" qualification = TextLine( title = u"Qualification", description = u"The element qualification" ) value = Text( title = u"Value", description = u"The element value", ) class IGeneralDublinCore(Interface): """Dublin-core data access interface The Dublin Core, http://dublincore.org/, is a meta data standard that specifies a set of standard data elements. It provides flexibility of interpretation of these elements by providing for element qualifiers that specialize the meaning of specific elements. For example, a date element might have a qualifier, like "creation" to indicate that the date is a creation date. In addition, any element may be repeated. For some elements, like subject, and contributor, this is obviously necessary, but for other elements, like title and description, allowing repetitions is not very useful and adds complexity. This interface provides methods for retrieving data in full generality, to be compliant with the Dublin Core standard. Other interfaces will provide more convenient access methods tailored to specific element usage patterns. """ def getQualifiedTitles(): """Return a sequence of Title IDublinCoreElementItem. """ def getQualifiedCreators(): """Return a sequence of Creator IDublinCoreElementItem. """ def getQualifiedSubjects(): """Return a sequence of Subject IDublinCoreElementItem. """ def getQualifiedDescriptions(): """Return a sequence of Description IDublinCoreElementItem. """ def getQualifiedPublishers(): """Return a sequence of Publisher IDublinCoreElementItem. """ def getQualifiedContributors(): """Return a sequence of Contributor IDublinCoreElementItem. """ def getQualifiedDates(): """Return a sequence of Date IDublinCoreElementItem. """ def getQualifiedTypes(): """Return a sequence of Type IDublinCoreElementItem. """ def getQualifiedFormats(): """Return a sequence of Format IDublinCoreElementItem. """ def getQualifiedIdentifiers(): """Return a sequence of Identifier IDublinCoreElementItem. """ def getQualifiedSources(): """Return a sequence of Source IDublinCoreElementItem. """ def getQualifiedLanguages(): """Return a sequence of Language IDublinCoreElementItem. """ def getQualifiedRelations(): """Return a sequence of Relation IDublinCoreElementItem. """ def getQualifiedCoverages(): """Return a sequence of Coverage IDublinCoreElementItem. """ def getQualifiedRights(): """Return a sequence of Rights IDublinCoreElementItem. """ class IWritableGeneralDublinCore(Interface): """Provide write access to dublin core data This interface augments `IStandardDublinCore` with methods for writing elements. """ def setQualifiedTitles(qualified_titles): """Set the qualified Title elements. The argument must be a sequence of `IDublinCoreElementItem`. """ def setQualifiedCreators(qualified_creators): """Set the qualified Creator elements. The argument must be a sequence of Creator `IDublinCoreElementItem`. """ def setQualifiedSubjects(qualified_subjects): """Set the qualified Subjects elements. The argument must be a sequence of Subject `IDublinCoreElementItem`. """ def setQualifiedDescriptions(qualified_descriptions): """Set the qualified Descriptions elements. The argument must be a sequence of Description `IDublinCoreElementItem`. """ def setQualifiedPublishers(qualified_publishers): """Set the qualified Publishers elements. The argument must be a sequence of Publisher `IDublinCoreElementItem`. """ def setQualifiedContributors(qualified_contributors): """Set the qualified Contributors elements. The argument must be a sequence of Contributor `IDublinCoreElementItem`. """ def setQualifiedDates(qualified_dates): """Set the qualified Dates elements. The argument must be a sequence of Date `IDublinCoreElementItem`. """ def setQualifiedTypes(qualified_types): """Set the qualified Types elements. The argument must be a sequence of Type `IDublinCoreElementItem`. """ def setQualifiedFormats(qualified_formats): """Set the qualified Formats elements. The argument must be a sequence of Format `IDublinCoreElementItem`. """ def setQualifiedIdentifiers(qualified_identifiers): """Set the qualified Identifiers elements. The argument must be a sequence of Identifier `IDublinCoreElementItem`. """ def setQualifiedSources(qualified_sources): """Set the qualified Sources elements. The argument must be a sequence of Source `IDublinCoreElementItem`. """ def setQualifiedLanguages(qualified_languages): """Set the qualified Languages elements. The argument must be a sequence of Language `IDublinCoreElementItem`. """ def setQualifiedRelations(qualified_relations): """Set the qualified Relations elements. The argument must be a sequence of Relation `IDublinCoreElementItem`. """ def setQualifiedCoverages(qualified_coverages): """Set the qualified Coverages elements. The argument must be a sequence of Coverage `IDublinCoreElementItem`. """ def setQualifiedRights(qualified_rights): """Set the qualified Rights elements. The argument must be a sequence of Rights `IDublinCoreElementItem`. """ class IDCDescriptiveProperties(Interface): """Basic descriptive meta-data properties """ title = TextLine( title = u'Title', description = u"The first unqualified Dublin Core 'Title' element value." ) description = Text( title = u'Description', description = u"The first unqualified Dublin Core 'Description' element value.", ) class IDCTimes(Interface): """Time properties """ created = Datetime( title = u'Creation Date', description = u"The date and time that an object is created. " u"\nThis is normally set automatically." ) modified = Datetime( title = u'Modification Date', description = u"The date and time that the object was last modified in a\n" u"meaningful way." ) class IDCPublishing(Interface): """Publishing properties """ effective = Datetime( title = u'Effective Date', description = u"The date and time that an object should be published. " ) expires = Datetime( title = u'Expiration Date', description = u"The date and time that the object should become unpublished." ) class IDCExtended(Interface): """Extended properties This is a mixed bag of properties we want but that we probably haven't quite figured out yet. """ creators = Tuple( title = u'Creators', description = u"The unqualified Dublin Core 'Creator' element values", value_type = TextLine(), ) subjects = Tuple( title = u'Subjects', description = u"The unqualified Dublin Core 'Subject' element values", value_type = TextLine(), ) publisher = Text( title = u'Publisher', description = u"The first unqualified Dublin Core 'Publisher' element value.", ) contributors = Tuple( title = u'Contributors', description = u"The unqualified Dublin Core 'Contributor' element values", value_type = TextLine(), ) class ICMFDublinCore(Interface): """This interface duplicates the CMF dublin core interface. """ def Title(): """Return the resource title. The first unqualified Dublin Core `Title` element value is returned as a unicode string if an unqualified element is defined, otherwise, an empty unicode string is returned. """ def Creator(): """Return the resource creators. Return the full name(s) of the author(s) of the content object. The unqualified Dublin Core `Creator` element values are returned as a sequence of unicode strings. """ def Subject(): """Return the resource subjects. The unqualified Dublin Core `Subject` element values are returned as a sequence of unicode strings. """ def Description(): """Return the resource description Return a natural language description of this object. The first unqualified Dublin Core `Description` element value is returned as a unicode string if an unqualified element is defined, otherwise, an empty unicode string is returned. """ def Publisher(): """Dublin Core element - resource publisher Return full formal name of the entity or person responsible for publishing the resource. The first unqualified Dublin Core `Publisher` element value is returned as a unicode string if an unqualified element is defined, otherwise, an empty unicode string is returned. """ def Contributors(): """Return the resource contributors Return any additional collaborators. The unqualified Dublin Core `Contributor` element values are returned as a sequence of unicode strings. """ def Date(): """Return the default date The first unqualified Dublin Core `Date` element value is returned as a unicode string if an unqualified element is defined, otherwise, an empty unicode string is returned. The string is formatted 'YYYY-MM-DD H24:MN:SS TZ'. """ def CreationDate(): """Return the creation date. The value of the first Dublin Core `Date` element qualified by 'creation' is returned as a unicode string if a qualified element is defined, otherwise, an empty unicode string is returned. The string is formatted 'YYYY-MM-DD H24:MN:SS TZ'. """ def EffectiveDate(): """Return the effective date The value of the first Dublin Core `Date` element qualified by 'effective' is returned as a unicode string if a qualified element is defined, otherwise, an empty unicode string is returned. The string is formatted 'YYYY-MM-DD H24:MN:SS TZ'. """ def ExpirationDate(): """Date resource expires. The value of the first Dublin Core `Date` element qualified by 'expiration' is returned as a unicode string if a qualified element is defined, otherwise, an empty unicode string is returned. The string is formatted 'YYYY-MM-DD H24:MN:SS TZ'. """ def ModificationDate(): """Date resource last modified. The value of the first Dublin Core `Date` element qualified by 'modification' is returned as a unicode string if a qualified element is defined, otherwise, an empty unicode string is returned. The string is formatted 'YYYY-MM-DD H24:MN:SS TZ'. """ def Type(): """Return the resource type Return a human-readable type name for the resource. The first unqualified Dublin Core `Type` element value is returned as a unicode string if an unqualified element is defined, otherwise, an empty unicode string is returned. """ def Format(): """Return the resource format. Return the resource's MIME type (e.g., 'text/html', 'image/png', etc.). The first unqualified Dublin Core `Format` element value is returned as a unicode string if an unqualified element is defined, otherwise, an empty unicode string is returned. """ def Identifier(): """Return the URL of the resource. This value is computed. It is included in the output of qualifiedIdentifiers with the qualification 'url'. """ def Language(): """Return the resource language. Return the RFC language code (e.g., 'en-US', 'pt-BR') for the resource. The first unqualified Dublin Core `Language` element value is returned as a unicode string if an unqualified element is defined, otherwise, an empty unicode string is returned. """ def Rights(): """Return the resource rights. Return a string describing the intellectual property status, if any, of the resource. for the resource. The first unqualified Dublin Core `Rights` element value is returned as a unicode string if an unqualified element is defined, otherwise, an empty unicode string is returned. """ class IZopeDublinCore( IGeneralDublinCore, ICMFDublinCore, IDCDescriptiveProperties, IDCTimes, IDCPublishing, IDCExtended, ): """Zope Dublin Core properties""" class IWriteZopeDublinCore( IZopeDublinCore, IWritableGeneralDublinCore, ): """Zope Dublin Core properties with generate update support""" zope.dublincore-3.8.2/src/zope/dublincore/zopedublincore.py0000644000175000017500000002407111527746247024045 0ustar tseavertseaver############################################################################## # # Copyright (c) 2002 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Zope's Dublin Core Implementation """ __docformat__ = 'restructuredtext' from datetime import datetime from zope.interface import implements from zope.datetime import parseDatetimetz from zope.dublincore.interfaces import IZopeDublinCore class SimpleProperty(object): def __init__(self, name): self.__name__ = name class ScalarProperty(SimpleProperty): def __get__(self, inst, klass): if inst is None: return self data = inst._mapping.get(self.__name__, ()) if data: return data[0] else: return u'' def __set__(self, inst, value): if not isinstance(value, unicode): raise TypeError("Element must be unicode") dict = inst._mapping __name__ = self.__name__ inst._changed() dict[__name__] = (value, ) + dict.get(__name__, ())[1:] def _scalar_get(inst, name): data = inst._mapping.get(name, ()) if data: return data[0] else: return u'' class DateProperty(ScalarProperty): def __get__(self, inst, klass): if inst is None: return self data = inst._mapping.get(self.__name__, ()) if data: return parseDatetimetz(data[0]) else: return None def __set__(self, inst, value): if not isinstance(value, datetime): raise TypeError("Element must be %s", datetime) value = unicode(value.isoformat('T'), 'ascii') super(DateProperty, self).__set__(inst, value) class SequenceProperty(SimpleProperty): def __get__(self, inst, klass): if inst is None: return self return inst._mapping.get(self.__name__, ()) def __set__(self, inst, value): value = tuple(value) for v in value: if not isinstance(v, unicode): raise TypeError("Elements must be unicode") inst._changed() inst._mapping[self.__name__] = value class ZopeDublinCore(object): """Zope Dublin Core Mixin Subclasses should define either `_changed()` or `_p_changed`. Just mix with `Persistence` to get a persistent version. """ implements(IZopeDublinCore) def __init__(self, mapping=None): if mapping is None: mapping = {} self._mapping = mapping def _changed(self): self._p_changed = True title = ScalarProperty(u'Title') def Title(self): "See `IZopeDublinCore`" return self.title creators = SequenceProperty(u'Creator') def Creator(self): "See `IZopeDublinCore`" return self.creators subjects = SequenceProperty(u'Subject') def Subject(self): "See `IZopeDublinCore`" return self.subjects description = ScalarProperty(u'Description') def Description(self): "See `IZopeDublinCore`" return self.description publisher = ScalarProperty(u'Publisher') def Publisher(self): "See IZopeDublinCore" return self.publisher contributors = SequenceProperty(u'Contributor') def Contributors(self): "See `IZopeDublinCore`" return self.contributors def Date(self): "See IZopeDublinCore" return _scalar_get(self, u'Date') created = DateProperty(u'Date.Created') def CreationDate(self): "See `IZopeDublinCore`" return _scalar_get(self, u'Date.Created') effective = DateProperty(u'Date.Effective') def EffectiveDate(self): "See `IZopeDublinCore`" return _scalar_get(self, u'Date.Effective') expires = DateProperty(u'Date.Expires') def ExpirationDate(self): "See `IZopeDublinCore`" return _scalar_get(self, u'Date.Expires') modified = DateProperty(u'Date.Modified') def ModificationDate(self): "See `IZopeDublinCore`" return _scalar_get(self, u'Date.Modified') type = ScalarProperty(u'Type') def Type(self): "See `IZopeDublinCore`" return self.type format = ScalarProperty(u'Format') def Format(self): "See `IZopeDublinCore`" return self.format identifier = ScalarProperty(u'Identifier') def Identifier(self): "See `IZopeDublinCore`" return self.identifier language = ScalarProperty(u'Language') def Language(self): "See `IZopeDublinCore`" return self.language rights = ScalarProperty(u'Rights') def Rights(self): "See `IZopeDublinCore`" return self.rights def setQualifiedTitles(self, qualified_titles): "See `IWritableDublinCore`" return _set_qualified(self, u'Title', qualified_titles) def setQualifiedCreators(self, qualified_creators): "See `IWritableDublinCore`" return _set_qualified(self, u'Creator', qualified_creators) def setQualifiedSubjects(self, qualified_subjects): "See `IWritableDublinCore`" return _set_qualified(self, u'Subject', qualified_subjects) def setQualifiedDescriptions(self, qualified_descriptions): "See `IWritableDublinCore`" return _set_qualified(self, u'Description', qualified_descriptions) def setQualifiedPublishers(self, qualified_publishers): "See `IWritableDublinCore`" return _set_qualified(self, u'Publisher', qualified_publishers) def setQualifiedContributors(self, qualified_contributors): "See `IWritableDublinCore`" return _set_qualified(self, u'Contributor', qualified_contributors) def setQualifiedDates(self, qualified_dates): "See `IWritableDublinCore`" return _set_qualified(self, u'Date', qualified_dates) def setQualifiedTypes(self, qualified_types): "See `IWritableDublinCore`" return _set_qualified(self, u'Type', qualified_types) def setQualifiedFormats(self, qualified_formats): "See `IWritableDublinCore`" return _set_qualified(self, u'Format', qualified_formats) def setQualifiedIdentifiers(self, qualified_identifiers): "See `IWritableDublinCore`" return _set_qualified(self, u'Identifier', qualified_identifiers) def setQualifiedSources(self, qualified_sources): "See `IWritableDublinCore`" return _set_qualified(self, u'Source', qualified_sources) def setQualifiedLanguages(self, qualified_languages): "See `IWritableDublinCore`" return _set_qualified(self, u'Language', qualified_languages) def setQualifiedRelations(self, qualified_relations): "See `IWritableDublinCore`" return _set_qualified(self, u'Relation', qualified_relations) def setQualifiedCoverages(self, qualified_coverages): "See `IWritableDublinCore`" return _set_qualified(self, u'Coverage', qualified_coverages) def setQualifiedRights(self, qualified_rights): "See `IWritableDublinCore`" return _set_qualified(self, u'Rights', qualified_rights) def getQualifiedTitles(self): "See `IStandardDublinCore`" return _get_qualified(self, u'Title') def getQualifiedCreators(self): "See `IStandardDublinCore`" return _get_qualified(self, u'Creator') def getQualifiedSubjects(self): "See `IStandardDublinCore`" return _get_qualified(self, u'Subject') def getQualifiedDescriptions(self): "See `IStandardDublinCore`" return _get_qualified(self, u'Description') def getQualifiedPublishers(self): "See `IStandardDublinCore`" return _get_qualified(self, u'Publisher') def getQualifiedContributors(self): "See `IStandardDublinCore`" return _get_qualified(self, u'Contributor') def getQualifiedDates(self): "See `IStandardDublinCore`" return _get_qualified(self, u'Date') def getQualifiedTypes(self): "See `IStandardDublinCore`" return _get_qualified(self, u'Type') def getQualifiedFormats(self): "See `IStandardDublinCore`" return _get_qualified(self, u'Format') def getQualifiedIdentifiers(self): "See `IStandardDublinCore`" return _get_qualified(self, u'Identifier') def getQualifiedSources(self): "See `IStandardDublinCore`" return _get_qualified(self, u'Source') def getQualifiedLanguages(self): "See `IStandardDublinCore`" return _get_qualified(self, u'Language') def getQualifiedRelations(self): "See `IStandardDublinCore`" return _get_qualified(self, u'Relation') def getQualifiedCoverages(self): "See `IStandardDublinCore`" return _get_qualified(self, u'Coverage') def getQualifiedRights(self): "See `IStandardDublinCore`" return _get_qualified(self, u'Rights') def _set_qualified(self, name, qvalue): data = {} dict = self._mapping for qualification, value in qvalue: data[qualification] = data.get(qualification, ()) + (value, ) self._changed() for qualification, values in data.iteritems(): qname = qualification and (name + '.' + qualification) or name dict[qname] = values def _get_qualified(self, name): result = [] for aname, avalue in self._mapping.iteritems(): if aname == name: qualification = u'' for value in avalue: result.append((qualification, value)) elif aname.startswith(name): qualification = aname[len(name)+1:] for value in avalue: result.append((qualification, value)) return tuple(result) __doc__ = ZopeDublinCore.__doc__ + __doc__ zope.dublincore-3.8.2/src/zope/dublincore/annotatableadapter.py0000644000175000017500000001030011527746247024640 0ustar tseavertseaver############################################################################## # # Copyright (c) 2002 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Dublin Core Annotatable Adapter """ __docformat__ = 'restructuredtext' from persistent.dict import PersistentDict from zope.annotation.interfaces import IAnnotatable from zope.annotation.interfaces import IAnnotations from zope.component import adapts from zope.interface import implements from zope.location import Location from zope.dublincore.interfaces import IWriteZopeDublinCore from zope.dublincore.zopedublincore import DateProperty from zope.dublincore.zopedublincore import ScalarProperty from zope.dublincore.zopedublincore import ZopeDublinCore DCkey = "zope.app.dublincore.ZopeDublinCore" class ZDCAnnotatableAdapter(ZopeDublinCore, Location): """Adapt annotatable objects to Zope Dublin Core.""" implements(IWriteZopeDublinCore) adapts(IAnnotatable) annotations = None def __init__(self, context): annotations = IAnnotations(context) dcdata = annotations.get(DCkey) if dcdata is None: self.annotations = annotations dcdata = ZDCAnnotationData() super(ZDCAnnotatableAdapter, self).__init__(dcdata) def _changed(self): if self.annotations is not None: self.annotations[DCkey] = self._mapping self.annotations = None class ZDCAnnotationData(PersistentDict): """Data for a Dublin Core annotation. A specialized class is used to allow an alternate fssync serialization to be registered. See the zope.dublincore.fssync package. """ # Hybrid adapters. # # Adapter factories created using this support the Dublin Core using a # mixture of annotations and data on the context object. class DirectProperty(object): def __init__(self, name, attrname): self.__name__ = name self.__attrname = attrname def __get__(self, inst, klass): if inst is None: return self context = inst._ZDCPartialAnnotatableAdapter__context return getattr(context, self.__attrname, u"") def __set__(self, inst, value): if not isinstance(value, unicode): raise TypeError("Element must be unicode") context = inst._ZDCPartialAnnotatableAdapter__context oldvalue = getattr(context, self.__attrname, None) if oldvalue != value: setattr(context, self.__attrname, value) def partialAnnotatableAdapterFactory(direct_fields): if not direct_fields: raise ValueError("only use partialAnnotatableAdapterFactory()" " if at least one DC field is implemented directly") fieldmap = {} try: # is direct_fields a sequence or a mapping? direct_fields[0] except KeyError: # direct_fields: { dc_name: attribute_name } fieldmap.update(direct_fields) else: for attrname in direct_fields: fieldmap[attrname] = attrname class ZDCPartialAnnotatableAdapter(ZDCAnnotatableAdapter): def __init__(self, context): self.__context = context # can't use super() since this isn't a globally available class ZDCAnnotatableAdapter.__init__(self, context) for dcname, attrname in fieldmap.iteritems(): oldprop = ZopeDublinCore.__dict__.get(dcname) if oldprop is None: raise ValueError("%r is not a valid DC field" % dcname) if (isinstance(oldprop, DateProperty) or not isinstance(oldprop, ScalarProperty)): raise ValueError("%r is not a supported DC field" % dcname) prop = DirectProperty(dcname, attrname) setattr(ZDCPartialAnnotatableAdapter, dcname, prop) return ZDCPartialAnnotatableAdapter zope.dublincore-3.8.2/src/zope/dublincore/timeannotators.py0000644000175000017500000000342511527746247024070 0ustar tseavertseaver############################################################################## # # Copyright (c) 2002 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Objects that take care of annotating dublin core meta data times """ __docformat__ = 'restructuredtext' from datetime import datetime import pytz from zope.dublincore.interfaces import IZopeDublinCore from zope.security.proxy import removeSecurityProxy def ModifiedAnnotator(object, event=None): if event is None: # annotator was only called the event as only argument object = object.object dc = IZopeDublinCore(object, None) if dc is not None: # Principals that can modify objects do not necessary have permissions # to arbitrarily modify DC data, see issue 373 dc = removeSecurityProxy(dc) dc.modified = datetime.now(pytz.utc) def CreatedAnnotator(object, event=None): if event is None: # annotator was only called the event as only argument object = object.object dc = IZopeDublinCore(object, None) if dc is not None: # Principals that can create objects do not necessary have permissions # to arbitrarily modify DC data, see issue 373 dc = removeSecurityProxy(dc) now = datetime.now(pytz.utc) dc.created = now dc.modified = now zope.dublincore-3.8.2/src/zope/dublincore/configure.zcml0000644000175000017500000000372411527746247023321 0ustar tseavertseaver zope.dublincore-3.8.2/src/zope/dublincore/xmlmetadata.py0000644000175000017500000001712311527746247023322 0ustar tseavertseaver############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Dublin Core XML data parser and writer """ __docformat__ = 'restructuredtext' import xml.sax import xml.sax.handler from cStringIO import StringIO from xml.sax.saxutils import escape, quoteattr from zope.dublincore import dcterms XSI_TYPE = (dcterms.XSI_NS, "type") dublin_core_namespaces = dcterms.DC_NS, dcterms.DCTERMS_NS DEFAULT_NAMESPACE_PREFIXES = { # uri: prefix, dcterms.DC_NS: "dc", dcterms.DCTERMS_NS: "dcterms", dcterms.XSI_NS: "xsi", } class NamespaceTracker(object): def __init__(self, mapping=None): self._mapping = {} self._used = {} if mapping: self._mapping.update(mapping) self._counter = 0 def encode(self, (uri, localname)): if not uri: return localname if uri not in self._mapping: self._counter += 1 prefix = "ns%d" % self._counter self._mapping[uri] = prefix self._used[prefix] = uri else: prefix = self._mapping[uri] if prefix not in self._used: self._used[prefix] = uri if prefix: return "%s:%s" % (prefix, localname) else: return localname def getPrefixMappings(self): return self._used.items() def dumpString(mapping): sio = StringIO() nsmap = NamespaceTracker(DEFAULT_NAMESPACE_PREFIXES) items = mapping.items() items.sort() prev = None for name, values in items: name, type = dcterms.splitEncoding(name) group = name.split(".", 1)[0] if prev != group: sio.write("\n") prev = group if name in dcterms.name_to_element: element, t = dcterms.name_to_element[name] qname = nsmap.encode(element) if not type: type = t if type: type = " %s=%s" % (nsmap.encode((dcterms.XSI_NS, "type")), quoteattr(type)) for value in values: sio.write(" <%s%s>\n %s\n \n" % (qname, type, _encode_string(value), qname)) else: raise RuntimeError("could not serialize %r metadata element" % name) content = sio.getvalue() sio = StringIO() sio.write("\n" "\n") sio.write(content) sio.write("\n") return sio.getvalue() try: unicode except NameError: _encode_string = escape else: def _encode_string(s): if isinstance(s, unicode): s = s.encode('utf-8') return escape(s) def parse(source, error_handler=None): parser, ch = _setup_parser(error_handler) parser.parse(source) return ch.mapping def parseString(text, error_handler=None): parser, ch = _setup_parser(error_handler) parser.feed(text) parser.close() return ch.mapping def _setup_parser(error_handler): parser = xml.sax.make_parser() ch = DublinCoreHandler() parser.setFeature(xml.sax.handler.feature_namespaces, True) parser.setContentHandler(ch) if error_handler is not None: parser.setErrorHandler(error_handler) return parser, ch class PrefixManager(object): # We don't use this other than in the DublinCoreHandler, but it's # entirely general so we'll separate it out for now. """General handler for namespace prefixes. This should be used as a mix-in when creating a ContentHandler. """ __prefix_map = None def startPrefixMapping(self, prefix, uri): if self.__prefix_map is None: self.__prefix_map = {} pm = self.__prefix_map pm.setdefault(prefix, []).append(uri) def endPrefixMapping(self, prefix): pm = self.__prefix_map uris = pm[prefix] del uris[-1] if not uris: del pm[prefix] def get_uri(self, prefix): pm = self.__prefix_map if pm is None: return None if prefix in pm: return pm[prefix][-1] else: return None class DublinCoreHandler(PrefixManager, xml.sax.handler.ContentHandler): def startDocument(self): self.mapping = {} self.stack = [] def get_dc_container(self): name = None for (uri, localname), dcelem, validator in self.stack: if uri in dublin_core_namespaces: name = uri, localname if name in dcterms.element_to_name: # dcelem contains type info, so go back to the mapping return dcterms.element_to_name[name] else: return None def startElementNS(self, name, qname, attrs): self.buffer = u"" # TODO: need convert element to metadata element name dcelem = validator = None if name in dcterms.element_to_name: dcelem = dcterms.element_to_name[name] type = attrs.get(XSI_TYPE) if type: if not dcelem: raise ValueError( "data type specified for unknown metadata element: %s" % qname) if ":" in type: prefix, t = type.split(":", 1) ns = self.get_uri(prefix) if ns != dcterms.DCTERMS_NS: raise ValueError("unknown data type namespace: %s" % t) type = t if type not in dcterms.encodings: raise ValueError("unknown data type: %r" % type) allowed_in, validator = dcterms.encodings[type] dcelem_split = dcelem.split(".") for elem in allowed_in: elem_split = elem.split(".") if dcelem_split[:len(elem_split)] == elem_split: break else: raise ValueError("%s values are not allowed for %r" % (type, dcelem)) dcelem = "%s.%s" % (dcelem, type) if dcelem: cont = self.get_dc_container() if cont and cont != dcelem: prefix = cont + "." if not dcelem.startswith(prefix): raise ValueError("%s is not a valid refinement for %s" % (dcelem, cont)) self.stack.append((name, dcelem, validator)) def endElementNS(self, name, qname): startname, dcelem, validator = self.stack.pop() assert startname == name if self.buffer is None: return data = self.buffer.strip() self.buffer = None if not dcelem: return if validator is not None: validator(data) if dcelem in self.mapping: self.mapping[dcelem] += (data,) else: self.mapping[dcelem] = (data,) def characters(self, data): if self.buffer is not None: self.buffer += data zope.dublincore-3.8.2/src/zope/dublincore/security.zcml0000644000175000017500000000074411527746247023206 0ustar tseavertseaver zope.dublincore-3.8.2/src/zope/dublincore/tests/0000755000175000017500000000000011527746337021605 5ustar tseavertseaverzope.dublincore-3.8.2/src/zope/dublincore/tests/test_zcml.py0000644000175000017500000000244111527746247024164 0ustar tseavertseaver############################################################################## # # Copyright (c) 2010 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """ Tests that ZCML can be loaded. """ import unittest class ZCMLTests(unittest.TestCase): from zope.component.testing import setUp from zope.component.testing import tearDown def test_loadable(self): # N.B.: this test deliberately avoids any "ftesting" / layers # support: its purpose is to ensure that the package's # ZCML file is loadable *without* loading any other ZCML. from zope.configuration.xmlconfig import file import zope.dublincore return file('configure.zcml', package=zope.dublincore) def test_suite(): return unittest.TestSuite(( unittest.makeSuite(ZCMLTests), )) zope.dublincore-3.8.2/src/zope/dublincore/tests/test_property.py0000644000175000017500000000250611527746247025105 0ustar tseavertseaver############################################################################## # # Copyright (c) 2006 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Test the Dublin Core Property implementation """ __docformat__ = "reStructuredText" import doctest import unittest from zope import component from zope.testing import cleanup from zope.annotation.attribute import AttributeAnnotations from zope.dublincore import testing def setUp(test): cleanup.setUp() component.provideAdapter(AttributeAnnotations) testing.setUpDublinCore() def tearDown(test): cleanup.tearDown() def test_suite(): return unittest.TestSuite( ( doctest.DocFileSuite( '../property.txt', setUp=setUp, tearDown=tearDown, optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS, ), )) zope.dublincore-3.8.2/src/zope/dublincore/tests/timeannotators.txt0000644000175000017500000000616111527746247025421 0ustar tseavertseaver=============== Time annotators =============== Time annotators store the creation resp. last modification time of an object. Set up ====== >>> class Content(object): ... created = None ... modified = None The annotations are stored on the ``IZopeDublinCore`` adapter. This dummy adapter reads and writes from/to the context object. >>> import zope.component >>> import zope.dublincore.interfaces >>> class DummyDublinCore(object): ... def __init__(self, context): ... self.__dict__['context'] = context ... ... def __getattr__(self, name): ... return getattr(self.context, name) ... ... def __setattr__(self, name, value): ... setattr(self.context, name, value) >>> zope.component.provideAdapter( ... DummyDublinCore, (Content,), zope.dublincore.interfaces.IZopeDublinCore) Created annotator ================= The created annotator sets creation and modification time to current time. >>> content = Content() It is registered for the ``ObjectCreatedEvent``: >>> import zope.dublincore.timeannotators >>> import zope.lifecycleevent.interfaces >>> zope.component.provideHandler( ... zope.dublincore.timeannotators.CreatedAnnotator, ... (zope.lifecycleevent.interfaces.IObjectCreatedEvent,)) >>> import zope.event >>> import zope.lifecycleevent >>> zope.event.notify(zope.lifecycleevent.ObjectCreatedEvent(content)) Both ``created`` and ``modified`` get set: >>> content.created datetime.datetime(, tzinfo=) >>> content.modified datetime.datetime(, tzinfo=) The created annotator can also be registered for (object, event): >>> zope.component.provideHandler( ... zope.dublincore.timeannotators.CreatedAnnotator, ... (None, ... zope.lifecycleevent.interfaces.IObjectCreatedEvent,)) >>> content = Content() >>> ignored = zope.component.subscribers( ... (content, zope.lifecycleevent.ObjectCreatedEvent(content)), None) Both ``created`` and ``modified`` get set this way, too: >>> content.created datetime.datetime(, tzinfo=) >>> content.modified datetime.datetime(, tzinfo=) Modified annotator ================== The modified annotator only sets the modification time to current time. >>> content = Content() It is registered for the ``ObjectModifiedEvent``: >>> zope.component.provideHandler( ... zope.dublincore.timeannotators.ModifiedAnnotator, ... (zope.lifecycleevent.interfaces.IObjectModifiedEvent,)) >>> zope.event.notify(zope.lifecycleevent.ObjectModifiedEvent(content)) Only ``modified`` gets set: >>> print content.created None >>> content.modified datetime.datetime(, tzinfo=) The modified annotator can also be registered for (object, event): >>> zope.component.provideHandler( ... zope.dublincore.timeannotators.ModifiedAnnotator, ... (None, ... zope.lifecycleevent.interfaces.IObjectModifiedEvent,)) >>> content = Content() >>> ignored = zope.component.subscribers( ... (content, zope.lifecycleevent.ObjectModifiedEvent(content)), None) ``modified`` gets set, this way, too: >>> print content.created None >>> content.modified datetime.datetime(, tzinfo=) zope.dublincore-3.8.2/src/zope/dublincore/tests/test_timeannotators.py0000644000175000017500000000222711527746247026270 0ustar tseavertseaver############################################################################## # # Copyright (c) 2010 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Test time annotator. """ import doctest import zope.testing.renormalizing import re datetime_re = ( '[0-9]{4}, ' # YYYY '[0-9]{1,2}, ' # MM '[0-9]{1,2}, ' # DD '[0-9]{1,2}, ' # HH '[0-9]{1,2}, ' # MM '[0-9]{1,2}' # SS '(, [0-9]{1,6})?' # uSec (skipped if 0) ) def test_suite(): return doctest.DocFileSuite( 'timeannotators.txt', checker=zope.testing.renormalizing.RENormalizing( [(re.compile(datetime_re), '')]) ) zope.dublincore-3.8.2/src/zope/dublincore/tests/test_annotatableadapter.py0000644000175000017500000002056111527746247027053 0ustar tseavertseaver############################################################################## # # Copyright (c) 2009 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Tests annotatableadapter. """ import unittest _marker = object() class ZDCAnnotatableAdapterTests(unittest.TestCase): _registered = False def setUp(self): from zope.testing.cleanup import cleanUp cleanUp() def tearDown(self): from zope.testing.cleanup import cleanUp cleanUp() def _getTargetClass(self): from zope.dublincore.annotatableadapter import ZDCAnnotatableAdapter return ZDCAnnotatableAdapter def _registerAnnotations(self, dcdata=None): from zope.component import provideAdapter from zope.interface import Interface from zope.annotation.interfaces import IAnnotations from zope.dublincore.annotatableadapter import DCkey class _Annotations(dict): pass instance = _Annotations({DCkey: dcdata}) def _factory(context): return instance if not self._registered: provideAdapter(_factory, (Interface, ), IAnnotations) self._registered = True return instance def _makeOne(self, context=_marker): if context is _marker: context = self._makeContext() return self._getTargetClass()(context) def _makeContext(self): class DummyContext(object): pass return DummyContext() def test_class_conforms_to_IWriteZopeDublinCore(self): from zope.interface.verify import verifyClass from zope.dublincore.interfaces import IWriteZopeDublinCore verifyClass(IWriteZopeDublinCore, self._getTargetClass()) def test_instance_conforms_to_IWriteZopeDublinCore(self): from zope.interface.verify import verifyObject from zope.dublincore.interfaces import IWriteZopeDublinCore self._registerAnnotations() verifyObject(IWriteZopeDublinCore, self._makeOne()) def test_ctor_wo_existing_DC_annotations(self): from zope.dublincore.annotatableadapter import DCkey self._registerAnnotations() context = self._makeContext() adapter = self._makeOne(context) self.assertEqual(adapter.annotations[DCkey], None) self.assertEqual(adapter._mapping, {}) def test_ctor_w_existing_DC_annotations(self): DCDATA = {'title': 'TITLE'} self._registerAnnotations(DCDATA) context = self._makeContext() adapter = self._makeOne(context) self.assertEqual(adapter.annotations, None) self.assertEqual(adapter._mapping, DCDATA) def test__changed_wo_existing_DC_annotations(self): from zope.dublincore.annotatableadapter import DCkey annotations = self._registerAnnotations() context = self._makeContext() adapter = self._makeOne(context) adapter._mapping['title'] = 'NEW TITLE' adapter._changed() self.assertEqual(annotations[DCkey]['title'], 'NEW TITLE') def test__changed_w_existing_DC_annotations(self): from zope.dublincore.annotatableadapter import DCkey DCDATA = {'title': 'TITLE'} annotations = self._registerAnnotations(DCDATA) context = self._makeContext() adapter = self._makeOne(context) adapter._changed() self.assertEqual(annotations[DCkey]['title'], 'TITLE') #unchanged class DirectPropertyTests(unittest.TestCase): def _getTargetClass(self): from zope.dublincore.annotatableadapter import DirectProperty return DirectProperty def _makeOne(self, name, attrname): return self._getTargetClass()(name, attrname) def test___get___via_klass(self): prop = self._makeOne('title', 'headline') class Testing(object): title = prop self.failUnless(Testing.title is prop) def test___get___via_instance(self): prop = self._makeOne('title', 'headline') class Context(object): headline = u'HEADLINE' class ZDCPartialAnnotatableAdapter(object): title = prop def __init__(self, context): self.__context = context context = Context() testing = ZDCPartialAnnotatableAdapter(context) self.assertEqual(testing.title, u'HEADLINE') def test___set___non_unicode_raises(self): prop = self._makeOne('title', 'headline') class Context(object): headline = u'HEADLINE' class ZDCPartialAnnotatableAdapter(object): title = prop def __init__(self, context): self.__context = context context = Context() testing = ZDCPartialAnnotatableAdapter(context) try: testing.title = 123 except TypeError: pass else: self.fail("Didn't raise TypeError") def test___set___unchanged_doesnt_mutate(self): prop = self._makeOne('title', 'headline') class Context(object): headline = u'HEADLINE' def __setattr__(self, name, value): assert 0 class ZDCPartialAnnotatableAdapter(object): title = prop def __init__(self, context): self.__context = context context = Context() testing = ZDCPartialAnnotatableAdapter(context) testing.title = u'HEADLINE' # doesn't raise def test___set___changed_mutates(self): prop = self._makeOne('title', 'headline') class Context(object): headline = u'HEADLINE1' class ZDCPartialAnnotatableAdapter(object): title = prop def __init__(self, context): self.__context = context context = Context() testing = ZDCPartialAnnotatableAdapter(context) testing.title = u'HEADLINE2' self.assertEqual(context.headline, u'HEADLINE2') class Test_partialAnnotatableAdapterFactory(unittest.TestCase): def _callFUT(self, direct_fields): from zope.dublincore.annotatableadapter \ import partialAnnotatableAdapterFactory return partialAnnotatableAdapterFactory(direct_fields) def test_w_empty_list_raises(self): self.assertRaises(ValueError, self._callFUT, []) def test_w_empty_dict_raises(self): self.assertRaises(ValueError, self._callFUT, {}) def test_w_unknown_field_raises(self): self.assertRaises(ValueError, self._callFUT, ['nonesuch']) def test_w_date_fields_raises(self): self.assertRaises(ValueError, self._callFUT, ['created']) self.assertRaises(ValueError, self._callFUT, ['modified']) self.assertRaises(ValueError, self._callFUT, ['effective']) self.assertRaises(ValueError, self._callFUT, ['expires']) def test_w_sequence_fields_raises(self): self.assertRaises(ValueError, self._callFUT, ['creators']) self.assertRaises(ValueError, self._callFUT, ['subjects']) self.assertRaises(ValueError, self._callFUT, ['contributors']) def test_w_scalar_prop_samename(self): from zope.dublincore.annotatableadapter import DirectProperty klass = self._callFUT(['title']) prop = klass.title self.failUnless(isinstance(prop, DirectProperty)) self.assertEqual(prop.__name__, 'title') self.assertEqual(prop._DirectProperty__attrname, 'title') # XXX def test_w_scalar_prop_mapped(self): from zope.dublincore.annotatableadapter import DirectProperty klass = self._callFUT({'title': 'headline'}) prop = klass.title self.failUnless(isinstance(prop, DirectProperty)) self.assertEqual(prop.__name__, 'title') self.assertEqual(prop._DirectProperty__attrname, 'headline') # XXX def test_suite(): return unittest.TestSuite(( unittest.makeSuite(ZDCAnnotatableAdapterTests), unittest.makeSuite(DirectPropertyTests), unittest.makeSuite(Test_partialAnnotatableAdapterFactory), )) zope.dublincore-3.8.2/src/zope/dublincore/tests/test_xmlmetadata.py0000644000175000017500000003075511527746247025531 0ustar tseavertseaver############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Test loading of Dublin Core metadata from the XML representation. """ import unittest from zope.dublincore import dcterms from zope.dublincore.xmlmetadata import dumpString, parseString class XMLDublinCoreLoadingTests(unittest.TestCase): # Note: We're not using the 'traditional' namespace prefixes in # the tests since we want to make sure we're doing the right thing # in the content handler. Also, we should use something we're not # using in zope.dublincore.dcterms. _prefix = ("\n" "\n" % (dcterms.DC_NS, dcterms.DCTERMS_NS, dcterms.XSI_NS)) _suffix = "\n" def parse(self, text): return parseString("%s%s%s" % (self._prefix, text, self._suffix)) def check1(self, text, name, value, generic=None): expected = {name: (value,)} m = self.parse(text) self.assertEqual(m, expected) m = self.parse("%s" % text) self.assertEqual(m, expected) if generic: m = self.parse("<%s>%s" % (generic, text, generic)) self.assertEqual(m, expected) m = self.parse("<%s>%s" % (generic, text, generic)) self.assertEqual(m, expected) # tests with acceptable input def test_empty(self): m = parseString("") self.assertEqual(m, {}) # core elements and related refinements def test_simple_title(self): self.check1("Foo", "Title", u"Foo") def test_two_titles(self): m = self.parse("Foo" "Bar") self.assertEqual(m, {"Title": (u"Foo", u"Bar")}) def test_alternative_title(self): m = self.parse("Foo" "Bar") self.assertEqual(m, {"Title": (u"Foo",), "Title.Alternative": (u"Bar",)}) def test_creator(self): self.check1("somebody", "Creator", "somebody") def test_subject(self): self.check1("something", "Subject", "something") self.check1("something", "Subject.LCSH", "something") self.check1("something", "Subject.MESH", "something") self.check1("something", "Subject.DDC", "something") self.check1("something", "Subject.LCC", "something") self.check1("something", "Subject.UDC", "something") def test_description(self): self.check1("foo", "Description", "foo") self.check1("foo", "Description.Abstract", "foo", generic="d:description") self.check1("foo", "Description.Table Of Contents", "foo", generic="d:description") def test_publisher(self): self.check1("pub", "Publisher", "pub") def test_contributor(self): self.check1("somebody", "Contributor", "somebody") def test_date(self): self.check1("2003-08-20", "Date", "2003-08-20") # refinements used by Zope self.check1("2003-08-20", "Date.Created", "2003-08-20", generic="d:date") self.check1("2003-08-20", "Date.Modified", "2003-08-20", generic="d:date") # other refinements self.check1("2003-08-20", "Date.Accepted", "2003-08-20", generic="d:date") self.check1("2003-08-20", "Date.Available", "2003-08-20", generic="d:date") self.check1("2003-08-20", "Date.Copyrighted", "2003-08-20", generic="d:date") self.check1("2003-08-20", "Date.Issued", "2003-08-20", generic="d:date") self.check1("2003-08-20", "Date.Submitted", "2003-08-20", generic="d:date") self.check1("2003-08-20", "Date.Valid", "2003-08-20", generic="d:date") def test_type(self): self.check1("some type", "Type", "some type") self.check1("Collection", "Type.DCMIType", "Collection") def test_format(self): self.check1("some format", "Format", "some format") self.check1("text/xml", "Format.IMT", "text/xml") self.check1("1 hour", "Format.Extent", "1 hour", generic="d:format") self.check1("70mm IMAX celluloid", "Format.Medium", "70mm IMAX celluloid", generic="d:format") def test_identifier(self): self.check1("ident", "Identifier", "ident") self.check1("" " citation " "", "Identifier.Bibliographic Citation", "citation", generic="d:identifier") def test_source(self): self.check1("src", "Source", "src") self.check1("http://example.com/", "Source.URI", "http://example.com/") def test_language(self): self.check1("Klingon", "Language", "Klingon") self.check1("abc", "Language.ISO639-2", "abc") self.check1("en", "Language.RFC1766", "en") self.check1("en-GB-oed", "Language.RFC3066", "en-GB-oed") def test_relation(self): self.check1("rel", "Relation", "rel") self.check1("that", "Relation.Is Version Of", "that", generic="d:relation") self.check1("that", "Relation.Has Version", "that", generic="d:relation") self.check1("that", "Relation.Is Replaced By", "that", generic="d:relation") self.check1("that", "Relation.Replaces", "that", generic="d:relation") self.check1("that", "Relation.Is Required By", "that", generic="d:relation") self.check1("that", "Relation.Requires", "that", generic="d:relation") self.check1("that", "Relation.Is Part Of", "that", generic="d:relation") self.check1("that", "Relation.Has Part", "that", generic="d:relation") self.check1("that", "Relation.Is Referenced By", "that", generic="d:relation") self.check1("that", "Relation.References", "that", generic="d:relation") self.check1("that", "Relation.Is Format Of", "that", generic="d:relation") self.check1("that", "Relation.Has Format", "that", generic="d:relation") self.check1("that", "Relation.Conforms To", "that", generic="d:relation") def test_coverage(self): self.check1("how much", "Coverage", "how much") self.check1("where", "Coverage.Spatial", "where", generic="d:coverage") self.check1("when", "Coverage.Temporal", "when", generic="d:coverage") self.check1("" " name=Period Name; start=1812; end=2112; " "", "Coverage.Temporal.Period", "name=Period Name; start=1812; end=2112;", generic="d:coverage") self.check1("2003-08-20", "Coverage.Temporal.W3CDTF", "2003-08-20", generic="d:coverage") def test_rights(self): self.check1("rights", "Rights", "rights") self.check1("rights", "Rights.Access Rights", "rights", generic="d:rights") # non-core elements def test_audience(self): # Audience is the only DCMI element not in the core self.check1("people", "Audience", "people") self.check1("people", "Audience.Education Level", "people", generic="t:audience") self.check1("people", "Audience.Mediator", "people", generic="t:audience") def test_nested_refinement(self): # direct nesting self.check1(("" "Foo" ""), "Title.Alternative", u"Foo") # nesting with an intermediate element self.check1(("" "Foo" ""), "Title.Alternative", u"Foo") # tests with errors in the input def test_invalid_nested_refinement(self): self.assertRaises(ValueError, self.parse, ("" "Title" "")) self.assertRaises(ValueError, self.parse, ("" "Title" "")) def test_invalid_type(self): self.assertRaises(ValueError, self.parse, "x") def test_invalid_dcmitype(self): self.assertRaises(ValueError, self.parse, "flub") class XMLDublinCoreSerializationTests(unittest.TestCase): def roundtrip(self, mapping): text = dumpString(mapping) parsed = parseString(text) self.assertEqual(parsed, mapping) def test_serialize_empty(self): self.roundtrip({}) def test_single_entry(self): self.roundtrip({"Title.Alternative": (u"Foo",)}) def test_two_titles(self): self.roundtrip({"Title": (u"Foo", u"Bar")}) def test_suite(): suite = unittest.makeSuite(XMLDublinCoreLoadingTests) suite.addTest(unittest.makeSuite(XMLDublinCoreSerializationTests)) return suite if __name__ == "__main__": unittest.main(defaultTest="test_suite") zope.dublincore-3.8.2/src/zope/dublincore/tests/test_zopedublincore.py0000644000175000017500000001667611527746247026262 0ustar tseavertseaver############################################################################## # # Copyright (c) 2001, 2002 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Test Zope's Dublin Core implementation """ from unittest import TestCase, TestSuite, main, makeSuite class Test(TestCase): def testImplementa(self): from zope.interface.verify import verifyObject from zope.dublincore.interfaces import IZopeDublinCore verifyObject(IZopeDublinCore, self.dc) def _Test__new(self): from zope.dublincore.zopedublincore import ZopeDublinCore return ZopeDublinCore() def setUp(self): self.dc = self._Test__new() def __testGetQualified(self, name, values): ovalues = getattr(self.dc, 'getQualified'+name)() ivalues = list(values) ivalues.sort() ovalues = list(ovalues) ovalues.sort() self.assertEqual(ovalues, ivalues) def __testQualified(self, name, values = [ (u'', u'blah blah'), (u'old', u'bleep bleep'), (u'old', u'bleep bleep \u1111'), (u'foo\u1111', u'bleep bleep'), ] ): getattr(self.dc, 'setQualified'+name)(values) self.__testGetQualified(name, values) def testOtherQualified(self): for name in ('Sources', 'Relations', 'Coverages'): self.__testQualified(name) def testScalars(self): for qname, mname, pname in ( ('Titles', 'Title', 'title'), ('Descriptions', 'Description', 'description'), ('Publishers', 'Publisher', 'publisher'), ('Types', 'Type', 'type'), ('Formats', 'Format', 'format'), ('Identifiers', 'Identifier', 'identifier'), ('Languages', 'Language', 'language'), ('Rights', 'Rights', 'rights'), ): self.__testQualified(qname) dc = self.dc self.assertEqual(getattr(dc, pname), u'blah blah') self.assertEqual(getattr(dc, mname)(), u'blah blah') self.assertRaises(Exception, setattr, dc, pname, 'foo') setattr(dc, pname, u'foo') self.assertEqual(getattr(dc, pname), u'foo') self.assertEqual(getattr(dc, mname)(), u'foo') self.__testGetQualified(qname, [ (u'', u'foo'), (u'old', u'bleep bleep'), (u'old', u'bleep bleep \u1111'), (u'foo\u1111', u'bleep bleep'), ] ) def testSequences(self): for qname, mname, pname in ( ('Creators', 'Creator', 'creators'), ('Subjects', 'Subject', 'subjects'), ('Contributors', 'Contributors', 'contributors'), ): self.__testQualified(qname, [ (u'', u'foo'), (u'', u'bar'), (u'', u'baz'), (u'', u'baz\u1111'), (u'old', u'bleep bleep'), (u'old', u'bleep bleep \u1111'), (u'foo\u1111', u'bleep bleep'), ] ) dc = self.dc v = getattr(dc, pname) v = list(v) v.sort() self.assertEqual(v, [u'bar', u'baz', u'baz\u1111', u'foo']) v = getattr(dc, mname)() v = list(v) v.sort() self.assertEqual(v, [u'bar', u'baz', u'baz\u1111', u'foo']) self.assertRaises(Exception, setattr, dc, pname, 'foo') self.assertRaises(Exception, setattr, dc, pname, ['foo']) setattr(dc, pname, [u'high', u'low', u'spam', u'eggs', u'ham', ]) v = getattr(dc, pname) v = list(v) v.sort() self.assertEqual(v, [u'eggs', u'ham', u'high', u'low', u'spam']) v = getattr(dc, mname)() v = list(v) v.sort() self.assertEqual(v, [u'eggs', u'ham', u'high', u'low', u'spam']) self.__testGetQualified(qname, [ (u'', u'high'), (u'', u'low'), (u'', u'spam'), (u'', u'eggs'), (u'', u'ham'), (u'old', u'bleep bleep'), (u'old', u'bleep bleep \u1111'), (u'foo\u1111', u'bleep bleep'), ] ) def testDates(self): self.__testQualified('Dates', [ (u'', u'1990-01-01'), (u'Created', u'1980-10-01T23:11:10-04:00'), (u'Modified', u'2002-10-01T12:09:22-04:00'), (u'Effective', u'2002-10-09T00:00:00-04:00'), (u'Expires', u'2002-10-16T00:00:00-04:00'), (u'xxx', u'2000-07-04'), (u'xxx', u'2001-12-31'), (u'foo \u1111', u'2001-12-31'), ]) from zope.datetime import parseDatetimetz dc = self.dc self.assertEqual(dc.created, parseDatetimetz('1980-10-01T23:11:10-04:00')) self.assertEqual(dc.modified, parseDatetimetz('2002-10-01T12:09:22-04:00')) self.assertEqual(dc.effective, parseDatetimetz('2002-10-09T00:00:00-04:00')) self.assertEqual(dc.expires, parseDatetimetz('2002-10-16T00:00:00-04:00')) self.assertEqual(dc.Date(), u'1990-01-01') self.assertEqual(dc.CreationDate(), u'1980-10-01T23:11:10-04:00') self.assertEqual(dc.ModificationDate(), u'2002-10-01T12:09:22-04:00') self.assertEqual(dc.EffectiveDate(), u'2002-10-09T00:00:00-04:00') self.assertEqual(dc.ExpirationDate(), u'2002-10-16T00:00:00-04:00') dt = parseDatetimetz('2002-10-03T14:51:55-04:00') dc.modified = dt self.assertRaises(Exception, setattr, dc, 'modified', 'foo') modified = [qv[1] for qv in dc.getQualifiedDates() if qv[0] == u'Modified'] self.failIf(len(modified) != 1, "should be only one: %r" % modified) self.assertEqual(parseDatetimetz(modified[0]), dt) modified = dc.ModificationDate() self.assertEqual(parseDatetimetz(modified), dt) def test_suite(): return TestSuite(( makeSuite(Test), )) if __name__=='__main__': main(defaultTest='test_suite') zope.dublincore-3.8.2/src/zope/dublincore/tests/partial.txt0000644000175000017500000001065111527746247024005 0ustar tseavertseaver==================================== Dublin Core metadata as content data ==================================== Sometimes we want to include data in content objects which mirrors one or more Dublin Core fields. In these cases, we want the Dublin Core structures to use the data in the content object rather than keeping a separate value in the annotations typically used. What fields we want to do this with can vary, however, and we may not want the Dublin Core APIs to constrain our choices of field names for our content objects. To deal with this, we can use speciallized adapter implementations tailored to specific content objects. To make this a bit easier, there is a factory for such adapters. Let's take a look at the simplest case of this to start with. We have some content object with a `title` attribute that should mirror the Dublin Core `title` field:: >>> import zope.interface >>> import zope.annotation.interfaces >>> class Content(object): ... ... zope.interface.implements( ... zope.annotation.interfaces.IAttributeAnnotatable) ... ... title = u"" ... description = u"" To avoid having a discrepency between the `title` attribute of our content object and the equivalent Dublin Core field, we can provide a specific adapter for our object:: >>> from zope.dublincore import annotatableadapter >>> factory = annotatableadapter.partialAnnotatableAdapterFactory( ... ["title"]) This creates an adapter factory that maps the Dublin Core `title` field to the `title` attribute on instances of our `Content` class. Multiple mappings may be specified by naming the additional fields in the sequence passed to `partialAnnotatableAdapterFactory()`. (We'll see later how to use different attribute names for Dublin Core fields.) Let's see what happens when we use the adapter. When using the adapter to retrieve a field set to use the content object, the value stored on the content object is used:: >>> content = Content() >>> adapter = factory(content) >>> adapter.title u'' >>> content.title = u"New Title" >>> adapter.title u'New Title' If we set the relevant Dublin Core field using the adapter, the content object is updated:: >>> adapter.title = u"Adapted Title" >>> content.title u'Adapted Title' Dublin Core fields which are not specifically mapped to the content object do not affect the content object:: >>> adapter.description = u"Some long description." >>> content.description u'' >>> adapter.description u'Some long description.' Using arbitrary field names =========================== We've seen the simple approach, allowing a Dublin Core field to be stored on the content object using an attribute of the same name as the DC field. However, we may want to use a different name for some reason. The `partialAnnotatableAdapterFactory()` supports this as well. If we call `partialAnnotatableAdapterFactory()` with a mapping instead of a sequence, the mapping is used to map Dublin Core field names to attribute names on the content object. Let's look at an example where we want the `abstract` attribute on the content object to be used for the `description` Dublin Core field:: >>> class Content(object): ... ... zope.interface.implements( ... zope.annotation.interfaces.IAttributeAnnotatable) ... ... abstract = u"" We can create the adapter factory by passing a mapping to `partialAnnotatableAdapterFactory()`:: >>> factory = annotatableadapter.partialAnnotatableAdapterFactory( ... {"description": "abstract"}) We can check the effects of the adapter as before:: >>> content = Content() >>> adapter = factory(content) >>> adapter.description u'' >>> content.abstract = u"What it's about." >>> adapter.description u"What it's about." >>> adapter.description = u"Change of plans." >>> content.abstract u'Change of plans.' Limitations =========== The current implementation has a number of limitations to be aware of; hopefully these can be removed in the future. - Only simple string properties, like `title`, are supported. This is largely because other field types have not been given sufficient thought. Attempting to use this for other fields will cause a `ValueError` to be raised by `partialAnnotatableAdapterFactory()`. - The CMF-like APIs are not supported in the generated adapters. It is not clear that these APIs are used, but content object implementations should be aware of this limitation. zope.dublincore-3.8.2/src/zope/dublincore/tests/test_zdcannotatableadapter.py0000644000175000017500000000324011527746247027547 0ustar tseavertseaver############################################################################## # # Copyright (c) 2001, 2002 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Test the Dublin Core annotations adapter. """ import unittest from zope.annotation.interfaces import IAnnotations from zope.component.testing import PlacelessSetup from zope.interface import implements class TestAnnotations(dict): implements(IAnnotations) class DublinCoreAdapterTest(PlacelessSetup, unittest.TestCase): def testZDCAnnotatableAdapter(self): from zope.dublincore.annotatableadapter import ZDCAnnotatableAdapter annotations = TestAnnotations() dc = ZDCAnnotatableAdapter(annotations) self.failIf(annotations, "There shouldn't be any data yet") self.assertEqual(dc.title, u'') self.failIf(annotations, "There shouldn't be any data yet") dc.title = u"Test title" self.failUnless(annotations, "There should be data now!") dc = ZDCAnnotatableAdapter(annotations) self.assertEqual(dc.title, u'Test title') def test_suite(): return unittest.makeSuite(DublinCoreAdapterTest) if __name__=='__main__': unittest.main(defaultTest='test_suite') zope.dublincore-3.8.2/src/zope/dublincore/tests/__init__.py0000644000175000017500000000007511527746247023720 0ustar tseavertseaver# # This file is necessary to make this directory a package. zope.dublincore-3.8.2/src/zope/dublincore/tests/test_creatorannotator.py0000644000175000017500000001032011527746247026577 0ustar tseavertseaver############################################################################## # # Copyright (c) 2001, 2002 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Tests creator annotation. """ import unittest class CreatorAnnotatorTests(unittest.TestCase): def setUp(self): from zope.testing.cleanup import cleanUp cleanUp() self._makeInterface() self._registerAdapter() def tearDown(self): from zope.testing.cleanup import cleanUp from zope.security.management import endInteraction endInteraction() cleanUp() def _callFUT(self, event): from zope.dublincore.creatorannotator import CreatorAnnotator return CreatorAnnotator(event) def _makeInterface(self): from zope.interface import Interface class IDummyContent(Interface): pass self._iface = IDummyContent def _registerAdapter(self): from zope.component import provideAdapter from zope.dublincore.interfaces import IZopeDublinCore provideAdapter(DummyDCAdapter, (self._iface, ), IZopeDublinCore) def _makeContextAndEvent(self): from zope.interface import implements class DummyDublinCore(object): implements(self._iface) creators = () class DummyEvent(object): def __init__(self, object): self.object = object context = DummyDublinCore() event = DummyEvent(context) return context, event def _setPrincipal(self, id): from zope.security.management import newInteraction class DummyPrincipal(object): title = 'TITLE' description = 'DESCRIPTION' def __init__(self, id): self.id = id if id is None: newInteraction(DummyRequest(None)) else: newInteraction(DummyRequest(DummyPrincipal(id))) def test_w_no_request(self): context, event = self._makeContextAndEvent() self._callFUT(event) self.assertEqual(context.creators, ()) def test_w_request_no_existing_creators(self): context, event = self._makeContextAndEvent() self._setPrincipal('phred') self._callFUT(event) self.assertEqual(context.creators, ('phred',)) def test_w_request_w_existing_creator_nomatch(self): context, event = self._makeContextAndEvent() context.creators = ('bharney',) self._setPrincipal('phred') self._callFUT(event) self.assertEqual(context.creators, ('bharney', 'phred',)) def test_w_request_w_existing_creator_match(self): context, event = self._makeContextAndEvent() context.creators = ('bharney', 'phred') self._setPrincipal('phred') self._callFUT(event) self.assertEqual(context.creators, ('bharney', 'phred',)) def test_w_request_no_principal(self): context, event = self._makeContextAndEvent() context.creators = ('bharney', 'phred') self._setPrincipal(None) self._callFUT(event) self.assertEqual(context.creators, ('bharney', 'phred',)) class CreatorAnnotatorObjectEventTests(CreatorAnnotatorTests): def _callFUT(self, event): from zope.dublincore.creatorannotator import CreatorAnnotator return CreatorAnnotator(event.object, event) class DummyDCAdapter(object): def _getcreator(self): return self.context.creators def _setcreator(self, value): self.context.creators = value creators = property(_getcreator, _setcreator, None, "Adapted Creators") def __init__(self, context): self.context = context class DummyRequest(object): def __init__(self, principal): self.principal = principal self.interaction = None zope.dublincore-3.8.2/src/zope/dublincore/tests/test_partialannotatable.py0000644000175000017500000000071011527746247027061 0ustar tseavertseaver"""Tests of the 'partial' annotatable adapter. """ __docformat__ = "reStructuredText" import doctest import zope.component.testing from zope.component.testing import tearDown from zope.annotation.attribute import AttributeAnnotations def setUp(test): zope.component.testing.setUp(test) zope.component.provideAdapter(AttributeAnnotations) def test_suite(): return doctest.DocFileSuite( "partial.txt", setUp=setUp, tearDown=tearDown) zope.dublincore-3.8.2/src/zope/dublincore/tests/test_dcsv.py0000644000175000017500000001636711527746247024172 0ustar tseavertseaver############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Test the Dublib Core Structured Value support functions. """ import unittest from doctest import DocTestSuite from zope.dublincore.dcsv import encode, decode # TODO still need tests for errors, and createMapping() def test_decode_empty(): """ >>> decode('') [] >>> decode(' ') [] """ def test_decode_simple_value(): """ >>> decode('v') [('', 'v')] >>> decode(' v ') [('', 'v')] >>> decode('v;') [('', 'v')] >>> decode(' v ; ') [('', 'v')] """ def test_decode_simple_escaped_value(): # Make the docstring a raw string to avoid having escapes # interpreted twice; each test within the docstring will be parsed # again! r""" >>> decode(r'\v') [('', 'v')] >>> decode(r'\;') [('', ';')] >>> decode(r'\;;') [('', ';')] >>> decode(r'\= ') [('', '=')] >>> decode(r'\= ; ') [('', '=')] >>> decode(r'\\\=\; ; ') [('', '\\=;')] >>> decode(r'\\\=\;') [('', '\\=;')] >>> decode(r'\\\=\; = ; ') [('\\=;', '')] >>> decode(r'\;\;\;;') [('', ';;;')] """ def test_decode_trailing_backslash(): r""" >>> decode('\\') [('', '\\')] >>> decode('v\\') [('', 'v\\')] These are tricky, but for different reasons: >>> decode(r'v\ ') [('', 'v\\')] >>> decode(r'v\ ; ') [('', 'v')] """ def test_decode_simple_list(): """ >>> decode('a;b;c') [('', 'a'), ('', 'b'), ('', 'c')] >>> decode('a;b;c;') [('', 'a'), ('', 'b'), ('', 'c')] """ def test_decode_simple_escaped_list(): r""" >>> decode(r'\a;\b;\c') [('', 'a'), ('', 'b'), ('', 'c')] >>> decode(r' \a ; \b ; \c ; ') [('', 'a'), ('', 'b'), ('', 'c')] >>> decode(r'\;;b;c') [('', ';'), ('', 'b'), ('', 'c')] >>> decode(r' \=;b;c;') [('', '='), ('', 'b'), ('', 'c')] """ def test_decode_empty_values(): # weird case; hard to know the intent of the specification """ >>> decode('=') [('', '')] >>> decode(';') [('', '')] >>> decode(' ; ') [('', '')] >>> decode(';;') [('', ''), ('', '')] >>> decode(' ; ; ') [('', ''), ('', '')] >>> decode('=;') [('', '')] >>> decode(' = ; ') [('', '')] >>> decode('=;=;') [('', ''), ('', '')] >>> decode(' = ; = ; ') [('', ''), ('', '')] >>> decode(' = ; = ; = ') [('', ''), ('', ''), ('', '')] """ def test_decode_labeled_values(): """ >>> decode('a=b') [('a', 'b')] >>> decode('a=b;') [('a', 'b')] >>> decode('a=b;c=d') [('a', 'b'), ('c', 'd')] Not really sure about this one yet; assuming that the space in 'd ;' is supposed to be removed until we have information that says otherwise: >>> decode('a =b; c= d ;') [('a', 'b'), ('c', 'd')] """ def test_decode_mixed_values(): """ >>> decode('a;b=c') [('', 'a'), ('b', 'c')] >>> decode('a=b;c') [('a', 'b'), ('', 'c')] >>> decode('a;b=c; ') [('', 'a'), ('b', 'c')] >>> decode('a=b;c ; ') [('a', 'b'), ('', 'c')] >>> decode('a;b;c;d=e;f;g;') [('', 'a'), ('', 'b'), ('', 'c'), ('d', 'e'), ('', 'f'), ('', 'g')] >>> decode('a=b;c=d;e;f=g') [('a', 'b'), ('c', 'd'), ('', 'e'), ('f', 'g')] """ def test_decode_duplicate_labels(): """ >>> decode('a=b;a=c; a=d') [('a', 'b'), ('a', 'c'), ('a', 'd')] """ def test_encode_empty_list(): """ >>> encode([]) '' """ def test_encode_single_item(): """ >>> encode(['']) ';' >>> encode([('', '')]) ';' >>> encode(['a']) 'a;' >>> encode([('', 'a')]) 'a;' >>> encode([('a','')]) 'a=;' >>> encode([('a', 'b')]) 'a=b;' The label from a pair can be any non-true value: >>> encode([(None, '')]) ';' >>> encode([(None, 'a')]) 'a;' >>> encode([(0, 'a')]) 'a;' >>> encode([((), 'a')]) 'a;' This may be a mis-feature, but seems harmless since no one in their right mind would use it intentionally (except maybe with None). """ def test_encode_single_value_needing_escapes(): r""" >>> encode(['=']) '\\=;' >>> encode([';']) '\\;;' >>> encode(['\\']) '\\\\;' >>> encode([r'\\']) '\\\\\\\\;' """ def test_encode_labeled_value_needing_escapes(): r""" Escaping needed in the labels: >>> encode([('\\', '')]) '\\\\=;' >>> encode([('\\', 'a')]) '\\\\=a;' >>> encode([('=', '')]) '\\==;' >>> encode([(';', 'a')]) '\\;=a;' Escaping needed in the values: >>> encode([('a', '\\')]) 'a=\\\\;' >>> encode([('a', '=')]) 'a=\\=;' >>> encode([('a', ';')]) 'a=\\;;' Escaping needed in both: >>> encode([('\\', '\\')]) '\\\\=\\\\;' >>> encode([('=', '=')]) '\\==\\=;' >>> encode([(';', ';')]) '\\;=\\;;' """ def test_encode_simple_list(): """ >>> encode(['a', 'b', 'c']) 'a; b; c;' >>> encode(['', '', '']) '; ; ;' >>> encode(['a b', 'c d']) 'a b; c d;' """ def test_encode_labeled_values(): # Single items were tested above; these all demonstrate with more # than one item. """ >>> encode([('a', ''), ('b', '')]) 'a=; b=;' >>> encode([('a', 'b'), ('c', 'd')]) 'a=b; c=d;' """ def test_encode_mixed_items(): """ >>> encode(['a', ('b', 'c')]) 'a; b=c;' >>> encode([('', 'a'), ('b', 'c')]) 'a; b=c;' >>> encode([('b', 'c'), 'a']) 'b=c; a;' >>> encode([('b', 'c'), ('', 'a')]) 'b=c; a;' """ def test_encode_error_non_strings(): """ >>> encode([(42, '')]) Traceback (most recent call last): ... TypeError: labels must be strings; found 42 >>> encode([('', 42)]) Traceback (most recent call last): ... TypeError: values must be strings; found 42 >>> encode([('label', 42)]) Traceback (most recent call last): ... TypeError: values must be strings; found 42 """ def test_encode_error_outer_whitespace(): """ >>> encode([' a']) Traceback (most recent call last): ... ValueError: values may not include leading or trailing spaces: ' a' >>> encode(['a ']) Traceback (most recent call last): ... ValueError: values may not include leading or trailing spaces: 'a ' >>> encode([('', 'a ')]) Traceback (most recent call last): ... ValueError: values may not include leading or trailing spaces: 'a ' >>> encode([('label', 'a ')]) Traceback (most recent call last): ... ValueError: values may not include leading or trailing spaces: 'a ' """ def test_suite(): return DocTestSuite() if __name__ == '__main__': unittest.main(defaultTest="test_suite") zope.dublincore-3.8.2/src/zope/dublincore/__init__.py0000644000175000017500000000007511527746247022556 0ustar tseavertseaver# # This file is necessary to make this directory a package. zope.dublincore-3.8.2/src/zope/dublincore/creatorannotator.py0000644000175000017500000000352111527746247024403 0ustar tseavertseaver############################################################################## # # Copyright (c) 2002 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Object that takes care of annotating the dublin core creator field. """ __docformat__ = 'restructuredtext' from zope.dublincore.interfaces import IZopeDublinCore from zope.security.management import queryInteraction from zope.security.proxy import removeSecurityProxy def CreatorAnnotator(object, event=None): """Update Dublin-Core creator property""" if event is None: # annotator was only called the event as only argument object = object.object dc = IZopeDublinCore(object, None) # Principals that can create objects do not necessarily have # 'zope.app.dublincore.change' permission. # https://bugs.launchpad.net/zope3/+bug/98124 dc = removeSecurityProxy(dc) if dc is None: return # Try to find a principal for that one. If there # is no principal then we don't touch the list # of creators. interaction = queryInteraction() if interaction is not None: for participation in interaction.participations: if participation.principal is None: continue principalid = participation.principal.id if not principalid in dc.creators: dc.creators = dc.creators + (unicode(principalid), ) zope.dublincore-3.8.2/src/zope/dublincore/dcsv.py0000644000175000017500000001007011527746247021752 0ustar tseavertseaver############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Functions for working with Dublin Core Structured Values (DCSV) scheme. DCSV is specified in 'DCMI DCSV: A syntax for writing a list of labelled values in a text string', at: http://dublincore.org/documents/dcmi-dcsv/ """ __docformat__ = 'restructuredtext' import re __all__ = "encode", "decode" try: basestring except NameError: # define basestring in Python 2.2.x: try: unicode except NameError: basestring = str else: basestring = str, unicode def encode(items): L = [] for item in items: if isinstance(item, basestring): L.append(_encode_string(item, "values") + ";") else: k, v = item if not isinstance(v, basestring): raise TypeError("values must be strings; found %r" % v) v = _encode_string(v, "values") if k: if not isinstance(k, basestring): raise TypeError("labels must be strings; found %r" % k) k = _encode_string(k, "labels") s = "%s=%s;" % (k, v) else: s = v + ";" L.append(s) return " ".join(L) def _encode_string(s, what): if s.strip() != s: raise ValueError("%s may not include leading or trailing spaces: %r" % (what, s)) return s.replace("\\", r"\\").replace(";", r"\;").replace("=", r"\=") def decode(text): items = [] text = text.strip() while text: m = _find_interesting(text) if m: prefix, char = m.group(1, 2) prefix = _decode_string(prefix).rstrip() if char == ";": items.append(('', prefix)) text = text[m.end():].lstrip() continue else: # char == "=" text = text[m.end():].lstrip() # else we have a label m = _find_value(text) if m: value = m.group(1) text = text[m.end():].lstrip() else: value = text text = '' items.append((prefix, _decode_string(value))) else: items.append(('', _decode_string(text))) break return items _prefix = r"((?:[^;\\=]|\\.)*)" _find_interesting = re.compile(_prefix + "([;=])").match _find_value = re.compile(_prefix + ";").match def _decode_string(s): if "\\" not in s: return s.rstrip() r = "" while s: c1 = s[0] if c1 == "\\": c2 = s[1:2] if not c2: return r + c1 r += c2 s = s[2:] else: r += c1 s = s[1:] return r.rstrip() def createMapping(items, allow_duplicates=False): mapping = {} for item in items: if isinstance(item, basestring): raise ValueError("can't create mapping with unlabelled data") k, v = item if not isinstance(k, basestring): raise TypeError("labels must be strings; found %r" % k) if not isinstance(v, basestring): raise TypeError("values must be strings; found %r" % v) if k in mapping: if allow_duplicates: mapping[k].append(v) else: raise ValueError("labels may not have more than one value") else: if allow_duplicates: mapping[k] = [v] else: mapping[k] = v return mapping zope.dublincore-3.8.2/src/zope/__init__.py0000644000175000017500000000007011527746247020423 0ustar tseavertseaver__import__('pkg_resources').declare_namespace(__name__) zope.dublincore-3.8.2/src/zope.dublincore.egg-info/0000755000175000017500000000000011527746337022134 5ustar tseavertseaverzope.dublincore-3.8.2/src/zope.dublincore.egg-info/not-zip-safe0000644000175000017500000000000111527746270024356 0ustar tseavertseaver zope.dublincore-3.8.2/src/zope.dublincore.egg-info/namespace_packages.txt0000644000175000017500000000000511527746337026462 0ustar tseavertseaverzope zope.dublincore-3.8.2/src/zope.dublincore.egg-info/requires.txt0000644000175000017500000000030711527746337024534 0ustar tseavertseaversetuptools pytz zope.component[zcml] zope.datetime zope.interface zope.lifecycleevent zope.location zope.schema zope.security[zcml]>=3.8 [test] zope.testing >= 3.8 zope.annotation zope.configurationzope.dublincore-3.8.2/src/zope.dublincore.egg-info/SOURCES.txt0000644000175000017500000000323411527746337024022 0ustar tseavertseaverCHANGES.txt COPYRIGHT.txt LICENSE.txt README.txt bootstrap.py buildout.cfg setup.py src/zope/__init__.py src/zope.dublincore.egg-info/PKG-INFO src/zope.dublincore.egg-info/SOURCES.txt src/zope.dublincore.egg-info/dependency_links.txt src/zope.dublincore.egg-info/namespace_packages.txt src/zope.dublincore.egg-info/not-zip-safe src/zope.dublincore.egg-info/requires.txt src/zope.dublincore.egg-info/top_level.txt src/zope/dublincore/__init__.py src/zope/dublincore/annotatableadapter.py src/zope/dublincore/configure.zcml src/zope/dublincore/creatorannotator.py src/zope/dublincore/dcsv.py src/zope/dublincore/dcterms.py src/zope/dublincore/interfaces.py src/zope/dublincore/property.py src/zope/dublincore/property.txt src/zope/dublincore/security.zcml src/zope/dublincore/testing.py src/zope/dublincore/timeannotators.py src/zope/dublincore/xmlmetadata.py src/zope/dublincore/zopedublincore.py src/zope/dublincore/browser/__init__.py src/zope/dublincore/browser/box.pt src/zope/dublincore/browser/configure.zcml src/zope/dublincore/browser/edit.pt src/zope/dublincore/browser/metadataedit.py src/zope/dublincore/tests/__init__.py src/zope/dublincore/tests/partial.txt src/zope/dublincore/tests/test_annotatableadapter.py src/zope/dublincore/tests/test_creatorannotator.py src/zope/dublincore/tests/test_dcsv.py src/zope/dublincore/tests/test_partialannotatable.py src/zope/dublincore/tests/test_property.py src/zope/dublincore/tests/test_timeannotators.py src/zope/dublincore/tests/test_xmlmetadata.py src/zope/dublincore/tests/test_zcml.py src/zope/dublincore/tests/test_zdcannotatableadapter.py src/zope/dublincore/tests/test_zopedublincore.py src/zope/dublincore/tests/timeannotators.txtzope.dublincore-3.8.2/src/zope.dublincore.egg-info/top_level.txt0000644000175000017500000000000511527746337024661 0ustar tseavertseaverzope zope.dublincore-3.8.2/src/zope.dublincore.egg-info/dependency_links.txt0000644000175000017500000000000111527746337026202 0ustar tseavertseaver zope.dublincore-3.8.2/src/zope.dublincore.egg-info/PKG-INFO0000644000175000017500000004004311527746337023232 0ustar tseavertseaverMetadata-Version: 1.0 Name: zope.dublincore Version: 3.8.2 Summary: Zope Dublin Core implementation Home-page: http://pypi.python.org/pypi/zope.dublincore Author: Zope Foundation and Contributors Author-email: zope-dev@zope.org License: ZPL 2.1 Description: .. contents:: ======== Overview ======== ``zope.dublincore`` provides a Dublin Core support for Zope-based web applications. This includes: * an ``IZopeDublinCore`` interface definition that can be implemented by objects directly or via an adapter to support DublinCore metadata. * an ``IZopeDublinCore`` adapter for annotatable objects (objects providing ``IAnnotatable`` from ``zope.annotation``). * a partial adapter for objects that already implement some of the ``IZopeDublinCore`` API, * a "Metadata" browser page (which by default appears in the ZMI), * subscribers to various object lifecycle events that automatically set the created and modified date and some other metadata. ====================== Dublin Core Properties ====================== A dublin core property allows us to use properties from dublin core by simply defining a property as DCProperty. >>> from zope.dublincore import property >>> from zope import interface >>> from zope.annotation.interfaces import IAttributeAnnotatable >>> class DC(object): ... interface.implements(IAttributeAnnotatable) ... title = property.DCProperty('title') ... author = property.DCProperty('creators') ... authors = property.DCListProperty('creators') >>> obj = DC() >>> obj.title = u'My title' >>> obj.title u'My title' Let's see if the title is really stored in dublin core: >>> from zope.dublincore.interfaces import IZopeDublinCore >>> IZopeDublinCore(obj).title u'My title' Even if a dublin core property is a list property we can set and get the property as scalar type: >>> obj.author = u'me' >>> obj.author u'me' DCListProperty acts on the list: >>> obj.authors (u'me',) >>> obj.authors = [u'I', u'others'] >>> obj.authors (u'I', u'others') >>> obj.author u'I' ==================================== Dublin Core metadata as content data ==================================== Sometimes we want to include data in content objects which mirrors one or more Dublin Core fields. In these cases, we want the Dublin Core structures to use the data in the content object rather than keeping a separate value in the annotations typically used. What fields we want to do this with can vary, however, and we may not want the Dublin Core APIs to constrain our choices of field names for our content objects. To deal with this, we can use speciallized adapter implementations tailored to specific content objects. To make this a bit easier, there is a factory for such adapters. Let's take a look at the simplest case of this to start with. We have some content object with a `title` attribute that should mirror the Dublin Core `title` field:: >>> import zope.interface >>> import zope.annotation.interfaces >>> class Content(object): ... ... zope.interface.implements( ... zope.annotation.interfaces.IAttributeAnnotatable) ... ... title = u"" ... description = u"" To avoid having a discrepency between the `title` attribute of our content object and the equivalent Dublin Core field, we can provide a specific adapter for our object:: >>> from zope.dublincore import annotatableadapter >>> factory = annotatableadapter.partialAnnotatableAdapterFactory( ... ["title"]) This creates an adapter factory that maps the Dublin Core `title` field to the `title` attribute on instances of our `Content` class. Multiple mappings may be specified by naming the additional fields in the sequence passed to `partialAnnotatableAdapterFactory()`. (We'll see later how to use different attribute names for Dublin Core fields.) Let's see what happens when we use the adapter. When using the adapter to retrieve a field set to use the content object, the value stored on the content object is used:: >>> content = Content() >>> adapter = factory(content) >>> adapter.title u'' >>> content.title = u"New Title" >>> adapter.title u'New Title' If we set the relevant Dublin Core field using the adapter, the content object is updated:: >>> adapter.title = u"Adapted Title" >>> content.title u'Adapted Title' Dublin Core fields which are not specifically mapped to the content object do not affect the content object:: >>> adapter.description = u"Some long description." >>> content.description u'' >>> adapter.description u'Some long description.' Using arbitrary field names =========================== We've seen the simple approach, allowing a Dublin Core field to be stored on the content object using an attribute of the same name as the DC field. However, we may want to use a different name for some reason. The `partialAnnotatableAdapterFactory()` supports this as well. If we call `partialAnnotatableAdapterFactory()` with a mapping instead of a sequence, the mapping is used to map Dublin Core field names to attribute names on the content object. Let's look at an example where we want the `abstract` attribute on the content object to be used for the `description` Dublin Core field:: >>> class Content(object): ... ... zope.interface.implements( ... zope.annotation.interfaces.IAttributeAnnotatable) ... ... abstract = u"" We can create the adapter factory by passing a mapping to `partialAnnotatableAdapterFactory()`:: >>> factory = annotatableadapter.partialAnnotatableAdapterFactory( ... {"description": "abstract"}) We can check the effects of the adapter as before:: >>> content = Content() >>> adapter = factory(content) >>> adapter.description u'' >>> content.abstract = u"What it's about." >>> adapter.description u"What it's about." >>> adapter.description = u"Change of plans." >>> content.abstract u'Change of plans.' Limitations =========== The current implementation has a number of limitations to be aware of; hopefully these can be removed in the future. - Only simple string properties, like `title`, are supported. This is largely because other field types have not been given sufficient thought. Attempting to use this for other fields will cause a `ValueError` to be raised by `partialAnnotatableAdapterFactory()`. - The CMF-like APIs are not supported in the generated adapters. It is not clear that these APIs are used, but content object implementations should be aware of this limitation. =============== Time annotators =============== Time annotators store the creation resp. last modification time of an object. Set up ====== >>> class Content(object): ... created = None ... modified = None The annotations are stored on the ``IZopeDublinCore`` adapter. This dummy adapter reads and writes from/to the context object. >>> import zope.component >>> import zope.dublincore.interfaces >>> class DummyDublinCore(object): ... def __init__(self, context): ... self.__dict__['context'] = context ... ... def __getattr__(self, name): ... return getattr(self.context, name) ... ... def __setattr__(self, name, value): ... setattr(self.context, name, value) >>> zope.component.provideAdapter( ... DummyDublinCore, (Content,), zope.dublincore.interfaces.IZopeDublinCore) Created annotator ================= The created annotator sets creation and modification time to current time. >>> content = Content() It is registered for the ``ObjectCreatedEvent``: >>> import zope.dublincore.timeannotators >>> import zope.lifecycleevent.interfaces >>> zope.component.provideHandler( ... zope.dublincore.timeannotators.CreatedAnnotator, ... (zope.lifecycleevent.interfaces.IObjectCreatedEvent,)) >>> import zope.event >>> import zope.lifecycleevent >>> zope.event.notify(zope.lifecycleevent.ObjectCreatedEvent(content)) Both ``created`` and ``modified`` get set: >>> content.created datetime.datetime(, tzinfo=) >>> content.modified datetime.datetime(, tzinfo=) The created annotator can also be registered for (object, event): >>> zope.component.provideHandler( ... zope.dublincore.timeannotators.CreatedAnnotator, ... (None, ... zope.lifecycleevent.interfaces.IObjectCreatedEvent,)) >>> content = Content() >>> ignored = zope.component.subscribers( ... (content, zope.lifecycleevent.ObjectCreatedEvent(content)), None) Both ``created`` and ``modified`` get set this way, too: >>> content.created datetime.datetime(, tzinfo=) >>> content.modified datetime.datetime(, tzinfo=) Modified annotator ================== The modified annotator only sets the modification time to current time. >>> content = Content() It is registered for the ``ObjectModifiedEvent``: >>> zope.component.provideHandler( ... zope.dublincore.timeannotators.ModifiedAnnotator, ... (zope.lifecycleevent.interfaces.IObjectModifiedEvent,)) >>> zope.event.notify(zope.lifecycleevent.ObjectModifiedEvent(content)) Only ``modified`` gets set: >>> print content.created None >>> content.modified datetime.datetime(, tzinfo=) The modified annotator can also be registered for (object, event): >>> zope.component.provideHandler( ... zope.dublincore.timeannotators.ModifiedAnnotator, ... (None, ... zope.lifecycleevent.interfaces.IObjectModifiedEvent,)) >>> content = Content() >>> ignored = zope.component.subscribers( ... (content, zope.lifecycleevent.ObjectModifiedEvent(content)), None) ``modified`` gets set, this way, too: >>> print content.created None >>> content.modified datetime.datetime(, tzinfo=) ======= Changes ======= 3.8.2 (2010-02-19) ================== - Updated regex normalizer to guard against test failure when a datetime's microseconds value is zero. 3.8.1 (2010-12-14) ================== - Added missing test dependency on zope.configuration and missing dependency of security.zcml on zope.security's meta.zcml. 3.8.0 (2010-09-14) ================== - Registered the annotators also for (object, event), so copy-pasting a folder, changes the dublin core data of the contained objects, too. The changed annotators are the following: - ``zope.dublincore.timeannotators.ModifiedAnnotator`` - ``zope.dublincore.timeannotators.CreatedAnnotator`` - ``zope.dublincore.creatorannotator.CreatorAnnotator`` 3.7.0 (2010-08-19) ================== - Removed backward-compatibility shims for deprecated ``zope.app.dublincore.*`` permissions. - Removed include the zcml configuration of ``zope.dublincore.browser``. - Using python`s doctest instead of deprecated ``zope.testing.doctest``. 3.6.3 (2010-04-23) ================== - Restored backward-compatible ``zope.app.dublincore.*`` permissions, mapping them onto the new permissions using the ```` directive. These shims will be removed in 3.7.0. - Added unit (not functional) test for loadability of ``configure.zcml``. 3.6.2 (2010-04-20) ================== - Repaired regression introduced in 3.6.1: the renamed permissions were not updated in other ZCML files. 3.6.1 (2010-04-19) ================== - Renamed the ``zope.app.dublincore.*`` permissions to ``zope.dublincore.*``. Applications may need to fix up grants based on the old permissions. - Added tests for ``zope.dublincore.timeannotators``. - Added not declared dependency on ``zope.lifecycleevent``. 3.6.0 (2009-12-02) ================== - Removed the marker interface IZopeDublinCoreAnnotatable which doesn't seem to be used. - Made the registration of ZDCAnnotatableAdapter conditional, lifting the dependency on zope.annotation and thereby the ZODB, leaving it as a test dependency. 3.5.0 (2009-09-15) ================== - Add missing dependencies. - Get rid of any testing dependencies beyond zope.testing. - Include browser ZCML configuration only if zope.browserpage is installed. - Specify i18n domain in package's ``configure.zcml``, because we use message IDs for permission titles. - Remove unused imports, fix one test that was inactive because of being overriden by another one by a mistake. 3.4.2 (2009-01-31) ================== - Declare dependency on zope.datetime. 3.4.1 (2009-01-26) ================== - Test dependencies are declared in a `test` extra now. - Fix: Make CreatorAnnotator not to fail if participation principal is None 3.4.0 (2007-09-28) ================== No further changes since 3.4.0a1. 3.4.0a1 (2007-04-22) ==================== Initial release as a separate project, corresponds to zope.dublincore from Zope 3.4.0a1 Platform: UNKNOWN zope.dublincore-3.8.2/CHANGES.txt0000644000175000017500000000566111527746247016372 0ustar tseavertseaver======= Changes ======= 3.8.2 (2010-02-19) ================== - Updated regex normalizer to guard against test failure when a datetime's microseconds value is zero. 3.8.1 (2010-12-14) ================== - Added missing test dependency on zope.configuration and missing dependency of security.zcml on zope.security's meta.zcml. 3.8.0 (2010-09-14) ================== - Registered the annotators also for (object, event), so copy-pasting a folder, changes the dublin core data of the contained objects, too. The changed annotators are the following: - ``zope.dublincore.timeannotators.ModifiedAnnotator`` - ``zope.dublincore.timeannotators.CreatedAnnotator`` - ``zope.dublincore.creatorannotator.CreatorAnnotator`` 3.7.0 (2010-08-19) ================== - Removed backward-compatibility shims for deprecated ``zope.app.dublincore.*`` permissions. - Removed include the zcml configuration of ``zope.dublincore.browser``. - Using python`s doctest instead of deprecated ``zope.testing.doctest``. 3.6.3 (2010-04-23) ================== - Restored backward-compatible ``zope.app.dublincore.*`` permissions, mapping them onto the new permissions using the ```` directive. These shims will be removed in 3.7.0. - Added unit (not functional) test for loadability of ``configure.zcml``. 3.6.2 (2010-04-20) ================== - Repaired regression introduced in 3.6.1: the renamed permissions were not updated in other ZCML files. 3.6.1 (2010-04-19) ================== - Renamed the ``zope.app.dublincore.*`` permissions to ``zope.dublincore.*``. Applications may need to fix up grants based on the old permissions. - Added tests for ``zope.dublincore.timeannotators``. - Added not declared dependency on ``zope.lifecycleevent``. 3.6.0 (2009-12-02) ================== - Removed the marker interface IZopeDublinCoreAnnotatable which doesn't seem to be used. - Made the registration of ZDCAnnotatableAdapter conditional, lifting the dependency on zope.annotation and thereby the ZODB, leaving it as a test dependency. 3.5.0 (2009-09-15) ================== - Add missing dependencies. - Get rid of any testing dependencies beyond zope.testing. - Include browser ZCML configuration only if zope.browserpage is installed. - Specify i18n domain in package's ``configure.zcml``, because we use message IDs for permission titles. - Remove unused imports, fix one test that was inactive because of being overriden by another one by a mistake. 3.4.2 (2009-01-31) ================== - Declare dependency on zope.datetime. 3.4.1 (2009-01-26) ================== - Test dependencies are declared in a `test` extra now. - Fix: Make CreatorAnnotator not to fail if participation principal is None 3.4.0 (2007-09-28) ================== No further changes since 3.4.0a1. 3.4.0a1 (2007-04-22) ==================== Initial release as a separate project, corresponds to zope.dublincore from Zope 3.4.0a1 zope.dublincore-3.8.2/setup.cfg0000644000175000017500000000007311527746337016372 0ustar tseavertseaver[egg_info] tag_build = tag_date = 0 tag_svn_revision = 0 zope.dublincore-3.8.2/PKG-INFO0000644000175000017500000004004311527746337015647 0ustar tseavertseaverMetadata-Version: 1.0 Name: zope.dublincore Version: 3.8.2 Summary: Zope Dublin Core implementation Home-page: http://pypi.python.org/pypi/zope.dublincore Author: Zope Foundation and Contributors Author-email: zope-dev@zope.org License: ZPL 2.1 Description: .. contents:: ======== Overview ======== ``zope.dublincore`` provides a Dublin Core support for Zope-based web applications. This includes: * an ``IZopeDublinCore`` interface definition that can be implemented by objects directly or via an adapter to support DublinCore metadata. * an ``IZopeDublinCore`` adapter for annotatable objects (objects providing ``IAnnotatable`` from ``zope.annotation``). * a partial adapter for objects that already implement some of the ``IZopeDublinCore`` API, * a "Metadata" browser page (which by default appears in the ZMI), * subscribers to various object lifecycle events that automatically set the created and modified date and some other metadata. ====================== Dublin Core Properties ====================== A dublin core property allows us to use properties from dublin core by simply defining a property as DCProperty. >>> from zope.dublincore import property >>> from zope import interface >>> from zope.annotation.interfaces import IAttributeAnnotatable >>> class DC(object): ... interface.implements(IAttributeAnnotatable) ... title = property.DCProperty('title') ... author = property.DCProperty('creators') ... authors = property.DCListProperty('creators') >>> obj = DC() >>> obj.title = u'My title' >>> obj.title u'My title' Let's see if the title is really stored in dublin core: >>> from zope.dublincore.interfaces import IZopeDublinCore >>> IZopeDublinCore(obj).title u'My title' Even if a dublin core property is a list property we can set and get the property as scalar type: >>> obj.author = u'me' >>> obj.author u'me' DCListProperty acts on the list: >>> obj.authors (u'me',) >>> obj.authors = [u'I', u'others'] >>> obj.authors (u'I', u'others') >>> obj.author u'I' ==================================== Dublin Core metadata as content data ==================================== Sometimes we want to include data in content objects which mirrors one or more Dublin Core fields. In these cases, we want the Dublin Core structures to use the data in the content object rather than keeping a separate value in the annotations typically used. What fields we want to do this with can vary, however, and we may not want the Dublin Core APIs to constrain our choices of field names for our content objects. To deal with this, we can use speciallized adapter implementations tailored to specific content objects. To make this a bit easier, there is a factory for such adapters. Let's take a look at the simplest case of this to start with. We have some content object with a `title` attribute that should mirror the Dublin Core `title` field:: >>> import zope.interface >>> import zope.annotation.interfaces >>> class Content(object): ... ... zope.interface.implements( ... zope.annotation.interfaces.IAttributeAnnotatable) ... ... title = u"" ... description = u"" To avoid having a discrepency between the `title` attribute of our content object and the equivalent Dublin Core field, we can provide a specific adapter for our object:: >>> from zope.dublincore import annotatableadapter >>> factory = annotatableadapter.partialAnnotatableAdapterFactory( ... ["title"]) This creates an adapter factory that maps the Dublin Core `title` field to the `title` attribute on instances of our `Content` class. Multiple mappings may be specified by naming the additional fields in the sequence passed to `partialAnnotatableAdapterFactory()`. (We'll see later how to use different attribute names for Dublin Core fields.) Let's see what happens when we use the adapter. When using the adapter to retrieve a field set to use the content object, the value stored on the content object is used:: >>> content = Content() >>> adapter = factory(content) >>> adapter.title u'' >>> content.title = u"New Title" >>> adapter.title u'New Title' If we set the relevant Dublin Core field using the adapter, the content object is updated:: >>> adapter.title = u"Adapted Title" >>> content.title u'Adapted Title' Dublin Core fields which are not specifically mapped to the content object do not affect the content object:: >>> adapter.description = u"Some long description." >>> content.description u'' >>> adapter.description u'Some long description.' Using arbitrary field names =========================== We've seen the simple approach, allowing a Dublin Core field to be stored on the content object using an attribute of the same name as the DC field. However, we may want to use a different name for some reason. The `partialAnnotatableAdapterFactory()` supports this as well. If we call `partialAnnotatableAdapterFactory()` with a mapping instead of a sequence, the mapping is used to map Dublin Core field names to attribute names on the content object. Let's look at an example where we want the `abstract` attribute on the content object to be used for the `description` Dublin Core field:: >>> class Content(object): ... ... zope.interface.implements( ... zope.annotation.interfaces.IAttributeAnnotatable) ... ... abstract = u"" We can create the adapter factory by passing a mapping to `partialAnnotatableAdapterFactory()`:: >>> factory = annotatableadapter.partialAnnotatableAdapterFactory( ... {"description": "abstract"}) We can check the effects of the adapter as before:: >>> content = Content() >>> adapter = factory(content) >>> adapter.description u'' >>> content.abstract = u"What it's about." >>> adapter.description u"What it's about." >>> adapter.description = u"Change of plans." >>> content.abstract u'Change of plans.' Limitations =========== The current implementation has a number of limitations to be aware of; hopefully these can be removed in the future. - Only simple string properties, like `title`, are supported. This is largely because other field types have not been given sufficient thought. Attempting to use this for other fields will cause a `ValueError` to be raised by `partialAnnotatableAdapterFactory()`. - The CMF-like APIs are not supported in the generated adapters. It is not clear that these APIs are used, but content object implementations should be aware of this limitation. =============== Time annotators =============== Time annotators store the creation resp. last modification time of an object. Set up ====== >>> class Content(object): ... created = None ... modified = None The annotations are stored on the ``IZopeDublinCore`` adapter. This dummy adapter reads and writes from/to the context object. >>> import zope.component >>> import zope.dublincore.interfaces >>> class DummyDublinCore(object): ... def __init__(self, context): ... self.__dict__['context'] = context ... ... def __getattr__(self, name): ... return getattr(self.context, name) ... ... def __setattr__(self, name, value): ... setattr(self.context, name, value) >>> zope.component.provideAdapter( ... DummyDublinCore, (Content,), zope.dublincore.interfaces.IZopeDublinCore) Created annotator ================= The created annotator sets creation and modification time to current time. >>> content = Content() It is registered for the ``ObjectCreatedEvent``: >>> import zope.dublincore.timeannotators >>> import zope.lifecycleevent.interfaces >>> zope.component.provideHandler( ... zope.dublincore.timeannotators.CreatedAnnotator, ... (zope.lifecycleevent.interfaces.IObjectCreatedEvent,)) >>> import zope.event >>> import zope.lifecycleevent >>> zope.event.notify(zope.lifecycleevent.ObjectCreatedEvent(content)) Both ``created`` and ``modified`` get set: >>> content.created datetime.datetime(, tzinfo=) >>> content.modified datetime.datetime(, tzinfo=) The created annotator can also be registered for (object, event): >>> zope.component.provideHandler( ... zope.dublincore.timeannotators.CreatedAnnotator, ... (None, ... zope.lifecycleevent.interfaces.IObjectCreatedEvent,)) >>> content = Content() >>> ignored = zope.component.subscribers( ... (content, zope.lifecycleevent.ObjectCreatedEvent(content)), None) Both ``created`` and ``modified`` get set this way, too: >>> content.created datetime.datetime(, tzinfo=) >>> content.modified datetime.datetime(, tzinfo=) Modified annotator ================== The modified annotator only sets the modification time to current time. >>> content = Content() It is registered for the ``ObjectModifiedEvent``: >>> zope.component.provideHandler( ... zope.dublincore.timeannotators.ModifiedAnnotator, ... (zope.lifecycleevent.interfaces.IObjectModifiedEvent,)) >>> zope.event.notify(zope.lifecycleevent.ObjectModifiedEvent(content)) Only ``modified`` gets set: >>> print content.created None >>> content.modified datetime.datetime(, tzinfo=) The modified annotator can also be registered for (object, event): >>> zope.component.provideHandler( ... zope.dublincore.timeannotators.ModifiedAnnotator, ... (None, ... zope.lifecycleevent.interfaces.IObjectModifiedEvent,)) >>> content = Content() >>> ignored = zope.component.subscribers( ... (content, zope.lifecycleevent.ObjectModifiedEvent(content)), None) ``modified`` gets set, this way, too: >>> print content.created None >>> content.modified datetime.datetime(, tzinfo=) ======= Changes ======= 3.8.2 (2010-02-19) ================== - Updated regex normalizer to guard against test failure when a datetime's microseconds value is zero. 3.8.1 (2010-12-14) ================== - Added missing test dependency on zope.configuration and missing dependency of security.zcml on zope.security's meta.zcml. 3.8.0 (2010-09-14) ================== - Registered the annotators also for (object, event), so copy-pasting a folder, changes the dublin core data of the contained objects, too. The changed annotators are the following: - ``zope.dublincore.timeannotators.ModifiedAnnotator`` - ``zope.dublincore.timeannotators.CreatedAnnotator`` - ``zope.dublincore.creatorannotator.CreatorAnnotator`` 3.7.0 (2010-08-19) ================== - Removed backward-compatibility shims for deprecated ``zope.app.dublincore.*`` permissions. - Removed include the zcml configuration of ``zope.dublincore.browser``. - Using python`s doctest instead of deprecated ``zope.testing.doctest``. 3.6.3 (2010-04-23) ================== - Restored backward-compatible ``zope.app.dublincore.*`` permissions, mapping them onto the new permissions using the ```` directive. These shims will be removed in 3.7.0. - Added unit (not functional) test for loadability of ``configure.zcml``. 3.6.2 (2010-04-20) ================== - Repaired regression introduced in 3.6.1: the renamed permissions were not updated in other ZCML files. 3.6.1 (2010-04-19) ================== - Renamed the ``zope.app.dublincore.*`` permissions to ``zope.dublincore.*``. Applications may need to fix up grants based on the old permissions. - Added tests for ``zope.dublincore.timeannotators``. - Added not declared dependency on ``zope.lifecycleevent``. 3.6.0 (2009-12-02) ================== - Removed the marker interface IZopeDublinCoreAnnotatable which doesn't seem to be used. - Made the registration of ZDCAnnotatableAdapter conditional, lifting the dependency on zope.annotation and thereby the ZODB, leaving it as a test dependency. 3.5.0 (2009-09-15) ================== - Add missing dependencies. - Get rid of any testing dependencies beyond zope.testing. - Include browser ZCML configuration only if zope.browserpage is installed. - Specify i18n domain in package's ``configure.zcml``, because we use message IDs for permission titles. - Remove unused imports, fix one test that was inactive because of being overriden by another one by a mistake. 3.4.2 (2009-01-31) ================== - Declare dependency on zope.datetime. 3.4.1 (2009-01-26) ================== - Test dependencies are declared in a `test` extra now. - Fix: Make CreatorAnnotator not to fail if participation principal is None 3.4.0 (2007-09-28) ================== No further changes since 3.4.0a1. 3.4.0a1 (2007-04-22) ==================== Initial release as a separate project, corresponds to zope.dublincore from Zope 3.4.0a1 Platform: UNKNOWN