diff --git a/README.txt b/README.txt
index 7703b04..142096e 100755
--- a/README.txt
+++ b/README.txt
@@ -651,7 +651,7 @@ on data provided in this form:
>>> from loops.type import TypeConcept
>>> from loops.resource import NoteAdapter
>>> component.provideAdapter(TypeConcept)
- >>> component.provideAdapter(NoteAdapter)
+ >>> component.provideAdapter(NoteAdapter, provides=INote)
>>> note_tc = concepts['note'] = Concept('Note')
>>> note_tc.conceptType = typeObject
>>> ITypeConcept(note_tc).typeInterface = INote
diff --git a/browser/common.py b/browser/common.py
index f90bff4..74730d0 100644
--- a/browser/common.py
+++ b/browser/common.py
@@ -25,6 +25,7 @@ $Id$
from zope.app import zapi
from zope import component
from zope.app.form.browser.interfaces import ITerms
+from zope.app.security.interfaces import IAuthentication
from zope.cachedescriptors.property import Lazy
from zope.dottedname.resolve import resolve
from zope.dublincore.interfaces import IZopeDublinCore
@@ -118,6 +119,17 @@ class BaseView(GenericView):
d = dc.modified or dc.created
return d and d.strftime('%Y-%m-%d %H:%M') or ''
+ @Lazy
+ def creators(self):
+ cr = IZopeDublinCore(self.context).creators or []
+ pau = component.getUtility(IAuthentication)
+ creators = []
+ for c in cr:
+ principal = pau.getPrincipal(c)
+ if principal is not None:
+ creators.append(principal.title)
+ return ', '.join(creators)
+
@Lazy
def loopsRoot(self):
return self.context.getLoopsRoot()
diff --git a/browser/concept_macros.pt b/browser/concept_macros.pt
index 38c11be..3e2fed5 100644
--- a/browser/concept_macros.pt
+++ b/browser/concept_macros.pt
@@ -86,6 +86,7 @@
tal:condition="view/useVersioning">V
Size |
Modification Date |
+ Author(s) |
Type
- Type |
-
-
-
- describing...
- |
+ 2007-03-30 |
+ John |
diff --git a/browser/configure.zcml b/browser/configure.zcml
index 08b6042..c65efa5 100644
--- a/browser/configure.zcml
+++ b/browser/configure.zcml
@@ -77,10 +77,10 @@
for="zope.interface.Interface"
name="loops.pageform" />
-
+ name="loops.dataform" />-->
-
+
@@ -169,13 +166,9 @@
tal:condition="nocall:target">
diff --git a/browser/resource.py b/browser/resource.py
index 1ed3ad4..fc20f2b 100644
--- a/browser/resource.py
+++ b/browser/resource.py
@@ -27,11 +27,13 @@ from zope import component
from zope.app import zapi
from zope.app.catalog.interfaces import ICatalog
from zope.dublincore.interfaces import ICMFDublinCore
+from zope.app.form.browser.textwidgets import FileWidget
from zope.app.pagetemplate import ViewPageTemplateFile
from zope.app.security.interfaces import IUnauthenticatedPrincipal
from zope.formlib.form import FormFields
from zope.formlib.interfaces import DISPLAY_UNWRITEABLE
from zope.proxy import removeAllProxies
+from zope.schema.interfaces import IBytes
from zope.security import canAccess, canWrite
from zope.security.proxy import removeSecurityProxy
@@ -54,6 +56,14 @@ renderingFactories = {
}
+class CustomFileWidget(FileWidget):
+
+ def hasInput(self):
+ print 'hasInput', self.request.form.get(self.name)
+ if not self.request.form.get(self.name):
+ return False
+
+
class ResourceEditForm(EditForm):
@Lazy
@@ -67,6 +77,9 @@ class ResourceEditForm(EditForm):
if typeInterface is not None:
omit = [f for f in typeInterface if f in IBaseResource]
fields = FormFields(fields.omit(*omit), typeInterface)
+ dataField = fields['data']
+ if IBytes.providedBy(dataField):
+ dataField.customWidget = CustomFileWidget
return fields
def setUpWidgets(self, ignore_request=False):
diff --git a/browser/util.py b/browser/util.py
index 129ca60..5e1d818 100644
--- a/browser/util.py
+++ b/browser/util.py
@@ -30,7 +30,8 @@ from zope.formlib.namedtemplate import NamedTemplateImplementation
pageform = NamedTemplateImplementation(ViewPageTemplateFile('pageform.pt'))
-dataform = NamedTemplateImplementation(ViewPageTemplateFile('dataform.pt'))
+#dataform = NamedTemplateImplementation(ViewPageTemplateFile('dataform.pt'))
+dataform = ViewPageTemplateFile('dataform.pt')
concept_macros = NamedTemplateImplementation(ViewPageTemplateFile('concept_macros.pt'))
node_macros = NamedTemplateImplementation(ViewPageTemplateFile('node_macros.pt'))
diff --git a/common.py b/common.py
index f1ab5e4..b955c0a 100644
--- a/common.py
+++ b/common.py
@@ -29,6 +29,8 @@ from zope.dublincore.zopedublincore import ScalarProperty
from zope.component import adapts
from zope.interface import implements
from zope.cachedescriptors.property import Lazy
+
+from cybertools.storage.interfaces import IStorageInfo
from loops.interfaces import ILoopsObject, ILoopsContained, IConcept, IResource
from loops.interfaces import IResourceAdapter
@@ -72,10 +74,15 @@ class AdapterBase(object):
class ResourceAdapterBase(AdapterBase):
+ implements(IStorageInfo)
adapts(IResource)
+ _adapterAttributes = ('storageName', 'storageParams', ) + AdapterBase._adapterAttributes
_contextAttributes = list(IResourceAdapter)
+ storageName = None
+ storageParams = None
+
# other adapters
diff --git a/configure.zcml b/configure.zcml
index 05687ee..d482317 100644
--- a/configure.zcml
+++ b/configure.zcml
@@ -344,7 +344,9 @@
set_schema="loops.query.IQueryConcept" />
-
+
@@ -361,7 +363,9 @@
set_schema="loops.interfaces.IFile" />
-
+
diff --git a/helpers.txt b/helpers.txt
index 9b0e35c..144b8f4 100755
--- a/helpers.txt
+++ b/helpers.txt
@@ -207,6 +207,10 @@ Now let's have a look at resources.
Using the type machinery we can also specify options that may be used
for controlling e.g. storage for external files.
+ >>> from loops.interfaces import IFile
+ >>> from loops.resource import FileAdapter
+ >>> component.provideAdapter(FileAdapter, provides=IFile)
+
>>> extfile = concepts['extfile'] = Concept(u'External File')
>>> ef1 = resources['ef1'] = Resource(u'Extfile #1')
>>> ef1.resourceType = extfile
diff --git a/organize/browser.py b/organize/browser.py
index 7df85a8..5174825 100644
--- a/organize/browser.py
+++ b/organize/browser.py
@@ -41,6 +41,7 @@ from loops.browser.concept import ConceptRelationView
from loops.organize.interfaces import ANNOTATION_KEY, IMemberRegistrationManager
from loops.organize.interfaces import IMemberRegistration
from loops.organize.party import getPersonForUser
+import loops.browser.util
_ = MessageFactory('zope')
@@ -75,11 +76,23 @@ class PasswordWidget(BasePasswordWidget):
class MemberRegistration(Form, NodeView):
form_fields = FormFields(IMemberRegistration).omit('age')
- template = NamedTemplate('loops.dataform')
+ template = loops.browser.util.dataform
label = _(u'Member Registration')
def __init__(self, context, request):
- NodeView.__init__(self, context, request)
+ #NodeView.__init__(self, context, request)
+ super(MemberRegistration, self).__init__(context, request)
+
+ @Lazy
+ def macro(self):
+ return self.template.macros['content']
+
+ @Lazy
+ def item(self):
+ return self
+
+ def __call__(self, *args, **kw):
+ return NodeView.__call__(self, *args, **kw)
@action(_(u'Register'))
def handle_register_action(self, action, data):
diff --git a/organize/configure.zcml b/organize/configure.zcml
index 1b2611e..91331a2 100644
--- a/organize/configure.zcml
+++ b/organize/configure.zcml
@@ -77,10 +77,12 @@
interface="loops.organize.interfaces.IMemberRegistrationManager" />
-
+ permission="zope.Public"
+ />
Error)
return concepts and concepts[0] or cm.get('file', None)
def setResourceType(self, concept):
- if concept is None:
+ if concept is None: # this should not happen
return
current = self.getResourceType()
if current != concept:
+ # change storage if necessary, and migrate data
+ oldType = IType(self)
+ from loops.type import ConceptTypeInfo
+ newType = ConceptTypeInfo(concept)
+ self.migrateStorage(oldType, newType)
+ # assign new type parent
typePred = self.getLoopsRoot().getConceptManager().getTypePredicate()
if typePred is None:
raise ValueError('No type predicate found for ' + getName(self))
@@ -122,6 +133,8 @@ class Resource(Image, Contained):
return self.resourceType
def _setData(self, data):
+ #if not data:
+ # return
dataFile = StringIO(data) # let File tear it into pieces
super(Resource, self)._setData(dataFile)
if not self.contentType:
@@ -130,6 +143,7 @@ class Resource(Image, Contained):
data = property(Image._getData, _setData)
def guessContentType(self, data):
+ # probably obsolete, use zope.contenttype.guess_content_type()
if not isinstance(data, str): # seems to be a file object
data = data.read(20)
if data.startswith('%PDF'):
@@ -204,6 +218,32 @@ class Resource(Image, Contained):
return '%.1f %s' % (size, unit)
#return '%s %s' % (util.getNiceNumber(size), unit)
+ # storage migration
+
+ def migrateStorage(self, oldType, newType):
+ oldType = removeSecurityProxy(oldType)
+ newType = removeSecurityProxy(newType)
+ context = removeSecurityProxy(self)
+ oldAdapted = newAdapted = context
+ oldTi = removeSecurityProxy(oldType.typeInterface)
+ if oldTi is not None:
+ oldAdapted = oldTi(context)
+ newTi = removeSecurityProxy(newType.typeInterface)
+ newOptions = {}
+ if newTi is not None:
+ newAdapted = newTi(context)
+ # make sure we use options of new type:
+ newOptions = newType.optionsDict
+ object.__setattr__(newAdapted, 'options', newOptions)
+ #print 'migrateStorage:', newAdapted, newOptions, oldAdapted, oldAdapted.storageName
+ if newOptions.get('storage') != oldAdapted.storageName:
+ data = oldAdapted.data
+ #print 'data', data
+ oldAdapted.data = '' # clear old storage
+ context._storageName = None # let's take storage from new type options
+ context._storageParams = None # "
+ newAdapted.data = data
+
# Document and MediaAsset are legacy classes, will become obsolete
@@ -240,15 +280,41 @@ class FileAdapter(ResourceAdapterBase):
_contextAttributes = list(IFile) + list(IBaseResource)
_adapterAttributes = ResourceAdapterBase._adapterAttributes + ('data',)
- def setData(self, data): self.context.data = data
+ def setData(self, data):
+ #if self.storageName is None:
+ # self.storageName = 'zopefile'
+ self.storageName = None
+ self.context.data = data
def getData(self): return self.context.data
data = property(getData, setData)
+ @Lazy
+ def options(self):
+ return IType(self.context).optionsDict
+
+ def getStorageName(self):
+ return (getattr(self.context, '_storageName', None)
+ or self.options.get('storage', None))
+ def setStorageName(self, value):
+ self.context._storageName = value
+ storageName = property(getStorageName, setStorageName)
+
class ExternalFileAdapter(FileAdapter):
implements(IExternalFile)
+ def getStorageParams(self):
+ params = getattr(self.context, '_storageParams', None)
+ if params is not None:
+ return params
+ else:
+ value = self.options.get('storage_parameters') or 'extfiles'
+ return dict(subdirectory=value)
+ def setStorageParams(self, value):
+ self.context._storageParams = value
+ storageParams = property(getStorageParams, setStorageParams)
+
@Lazy
def externalAddress(self):
# or is this an editable attribute?
@@ -256,23 +322,17 @@ class ExternalFileAdapter(FileAdapter):
# anyway: an attribute of the context object.
return self.context.__name__
- @Lazy
- def options(self):
- return IType(self.context).optionsDict
-
- @Lazy
- def storageName(self):
- return self.options.get('storage')
-
- @Lazy
- def storageParams(self):
- params = self.options.get('storage_parameters') or 'extfiles'
- return dict(subdirectory=params)
-
def setData(self, data):
- storage = component.getUtility(IExternalStorage, name=self.storageName)
- storage.setData(self.externalAddress, data, params=self.storageParams)
+ if not data:
+ return
+ storageParams = self.storageParams
+ storageName = self.storageName
+ storage = component.getUtility(IExternalStorage, name=storageName)
+ storage.setData(self.externalAddress, data, params=storageParams)
self.context._size = len(data)
+ # remember storage settings:
+ self.storageParams = storageParams
+ self.storageName = storageName
def getData(self):
storage = component.getUtility(IExternalStorage, name=self.storageName)
diff --git a/search/search.pt b/search/search.pt
index 0faf31f..4b483bf 100644
--- a/search/search.pt
+++ b/search/search.pt
@@ -54,6 +54,7 @@
tal:condition="view/useVersioning">V
Size |
Modification Date |
+ Author(s) |
@@ -80,12 +81,7 @@
Size
modified |
-
-
-
- describing...
- |
+ John |
diff --git a/setup.py b/setup.py
index 172a098..9946f46 100644
--- a/setup.py
+++ b/setup.py
@@ -90,6 +90,7 @@ class SetupManager(object):
#ITypeConcept(image).typeInterface = IImage
ITypeConcept(textdocument).typeInterface = ITextDocument
ITypeConcept(note).typeInterface = INote
+ #ITypeConcept(note).viewName = 'note.html'
hasType.conceptType = predicate
standard.conceptType = predicate
diff --git a/xmlrpc/README.txt b/xmlrpc/README.txt
index b5db9b2..4a99d2f 100755
--- a/xmlrpc/README.txt
+++ b/xmlrpc/README.txt
@@ -141,8 +141,10 @@ Resources
---------
>>> from loops.resource import TextDocumentAdapter
- >>> from loops.interfaces import IResource, ITextDocument
+ >>> from loops.interfaces import IResource, ITextDocument, IFile
>>> component.provideAdapter(TextDocumentAdapter, (IResource,), ITextDocument)
+ >>> from loops.resource import FileAdapter
+ >>> component.provideAdapter(FileAdapter, provides=IFile)
>>> zope3Id = xrf.getObjectByName('zope3')['id']
>>> td01 = resources['td01'] = Resource(u'Doc1')