first running version of creating and roundtrip editing resources via dojo.Dialog
git-svn-id: svn://svn.cy55.de/Zope3/src/loops/trunk@1359 fd906abe-77d9-0310-91a1-e0d9ade77398
This commit is contained in:
parent
63a13d6854
commit
94a14aac45
10 changed files with 272 additions and 107 deletions
|
@ -631,7 +631,8 @@ that are shown in the end-user interface.
|
|||
>>> ITypeConcept(note_tc).typeInterface = INote
|
||||
|
||||
>>> component.provideAdapter(ResourceNameChooser)
|
||||
>>> request = TestRequest(form={'form.title': 'Test Note'})
|
||||
>>> request = TestRequest(form={'form.title': 'Test Note',
|
||||
... 'form.type': '.loops/concepts/note'})
|
||||
>>> view = NodeView(m112, request)
|
||||
>>> cont = CreateObject(view, request)
|
||||
>>> cont.update()
|
||||
|
@ -647,7 +648,8 @@ created object:
|
|||
>>> from loops import util
|
||||
>>> topicUid = util.getUidForObject(topic)
|
||||
>>> request = TestRequest(form={'form.title': 'Test Note',
|
||||
... 'form.concept.search.text_selected': topicUid})
|
||||
... 'form.type': '.loops/concepts/note',
|
||||
... 'form.concept.search.text_selected': str(topicUid)})
|
||||
>>> view = NodeView(m112, request)
|
||||
>>> cont = CreateObject(view, request)
|
||||
>>> cont.update()
|
||||
|
|
|
@ -42,6 +42,7 @@ from cybertools.browser.view import GenericView
|
|||
from cybertools.relation.interfaces import IRelationRegistry
|
||||
from cybertools.typology.interfaces import IType, ITypeManager
|
||||
from loops.interfaces import IView
|
||||
from loops.resource import Resource
|
||||
from loops.type import ITypeConcept
|
||||
from loops import util
|
||||
from loops.util import _
|
||||
|
@ -170,6 +171,15 @@ class BaseView(GenericView):
|
|||
for o in objs:
|
||||
yield BaseView(o, request)
|
||||
|
||||
def resourceTypes(self):
|
||||
return util.KeywordVocabulary([(t.token, t.title)
|
||||
for t in ITypeManager(self.context).listTypes(('resource',))
|
||||
if t.factory == Resource])
|
||||
|
||||
def conceptTypes(self):
|
||||
return util.KeywordVocabulary([(t.token, t.title)
|
||||
for t in ITypeManager(self.context).listTypes(('concept',))])
|
||||
|
||||
def typesForSearch(self):
|
||||
general = [('loops:resource:*', 'Any Resource'),
|
||||
('loops:concept:*', 'Any Concept'),]
|
||||
|
@ -179,9 +189,13 @@ class BaseView(GenericView):
|
|||
|
||||
def conceptTypesForSearch(self):
|
||||
general = [('loops:concept:*', 'Any Concept'),]
|
||||
return util.KeywordVocabulary(general + sorted([(t.tokenForSearch, t.title)
|
||||
for t in ITypeManager(self.context).types
|
||||
if 'concept' in t.qualifiers]))
|
||||
return util.KeywordVocabulary(general + [(t.tokenForSearch, t.title)
|
||||
for t in ITypeManager(self.context).listTypes(('concept',))])
|
||||
|
||||
def resourceTypesForSearch(self):
|
||||
general = [('loops:resource:*', 'Any Resource'),]
|
||||
return util.KeywordVocabulary(general + [(t.tokenForSearch, t.title)
|
||||
for t in ITypeManager(self.context).listTypes(('resource',))])
|
||||
|
||||
@Lazy
|
||||
def uniqueId(self):
|
||||
|
|
|
@ -244,24 +244,6 @@ class ConceptConfigureView(ConceptView):
|
|||
result = [r for r in result if r.conceptType is None]
|
||||
return self.viewIterator(result)
|
||||
|
||||
def conceptTypes(self):
|
||||
return util.KeywordVocabulary([(t.token, t.title)
|
||||
for t in ITypeManager(self.context).listTypes(('concept',))])
|
||||
|
||||
def conceptTypesForSearch(self):
|
||||
general = [('loops:concept:*', 'Any'),]
|
||||
return util.KeywordVocabulary(general + [(t.tokenForSearch, t.title)
|
||||
for t in ITypeManager(self.context).listTypes(('concept',))])
|
||||
|
||||
def resourceTypes(self):
|
||||
return util.KeywordVocabulary([(t.token, t.title)
|
||||
for t in ITypeManager(self.context).listTypes(('resource',))])
|
||||
|
||||
def resourceTypesForSearch(self):
|
||||
general = [('loops:resource:*', 'Any'),]
|
||||
return util.KeywordVocabulary(general + [(t.tokenForSearch, t.title)
|
||||
for t in ITypeManager(self.context).listTypes(('resource',))])
|
||||
|
||||
def predicates(self):
|
||||
preds = PredicateSourceList(self.context)
|
||||
terms = zapi.getMultiAdapter((preds, self.request), ITerms)
|
||||
|
|
|
@ -303,7 +303,7 @@
|
|||
/>
|
||||
|
||||
<page
|
||||
for="loops.interfaces.IDocument"
|
||||
for="loops.interfaces.IResource"
|
||||
name="document.html"
|
||||
permission="zope.View"
|
||||
template="document.pt"
|
||||
|
@ -566,6 +566,20 @@
|
|||
permission="zope.ManageContent"
|
||||
/>
|
||||
|
||||
<page
|
||||
name="edit_object.html"
|
||||
for="loops.interfaces.INode"
|
||||
class="loops.browser.form.EditObjectForm"
|
||||
permission="zope.ManageContent"
|
||||
/>
|
||||
|
||||
<page
|
||||
name="inner_form.html"
|
||||
for="loops.interfaces.INode"
|
||||
class="loops.browser.form.InnerForm"
|
||||
permission="zope.ManageContent"
|
||||
/>
|
||||
|
||||
<zope:adapter
|
||||
name="create_resource"
|
||||
for="loops.browser.node.NodeView
|
||||
|
@ -574,6 +588,14 @@
|
|||
permission="zope.ManageContent"
|
||||
/>
|
||||
|
||||
<zope:adapter
|
||||
name="edit_resource"
|
||||
for="loops.browser.node.NodeView
|
||||
zope.publisher.interfaces.browser.IBrowserRequest"
|
||||
factory="loops.browser.form.EditObject"
|
||||
permission="zope.ManageContent"
|
||||
/>
|
||||
|
||||
<zope:adapter factory="loops.browser.form.ResourceNameChooser" />
|
||||
|
||||
<!-- inner HTML views -->
|
||||
|
|
108
browser/form.py
108
browser/form.py
|
@ -31,36 +31,26 @@ from zope.app.container.interfaces import INameChooser
|
|||
from zope.app.container.contained import NameChooser
|
||||
from zope.app.pagetemplate import ViewPageTemplateFile
|
||||
from zope.cachedescriptors.property import Lazy
|
||||
from zope.formlib.form import Form, FormFields
|
||||
from zope.formlib.form import Form, EditForm, FormFields
|
||||
from zope.publisher.interfaces import BadRequest
|
||||
|
||||
from cybertools.ajax import innerHtml
|
||||
from cybertools.browser.form import FormController
|
||||
from cybertools.typology.interfaces import IType
|
||||
from loops.interfaces import IResourceManager
|
||||
from loops.interfaces import IResourceManager, INote
|
||||
from loops.browser.node import NodeView
|
||||
from loops.resource import Resource
|
||||
from loops.type import ITypeConcept
|
||||
from loops import util
|
||||
from loops.util import _
|
||||
|
||||
class CreateObjectForm(NodeView, Form):
|
||||
|
||||
class ObjectForm(NodeView):
|
||||
|
||||
template = ViewPageTemplateFile('form_macros.pt')
|
||||
|
||||
@property
|
||||
def macro(self): return self.template.macros['create']
|
||||
|
||||
form_fields = FormFields(
|
||||
schema.TextLine(__name__='title', title=_(u'Title')),
|
||||
schema.Text(__name__='data', title=_(u'Body Text')),
|
||||
schema.TextLine(__name__='linkUrl', title=_(u'Link'), required=False),
|
||||
)
|
||||
|
||||
title = _(u'Enter Note')
|
||||
form_action = 'create_resource'
|
||||
|
||||
def __init__(self, context, request):
|
||||
super(CreateObjectForm, self).__init__(context, request)
|
||||
super(ObjectForm, self).__init__(context, request)
|
||||
|
||||
def setUp(self):
|
||||
self.setUpWidgets()
|
||||
|
@ -70,6 +60,52 @@ class CreateObjectForm(NodeView, Form):
|
|||
return innerHtml(self)
|
||||
|
||||
|
||||
class CreateObjectForm(ObjectForm, Form):
|
||||
|
||||
@property
|
||||
def macro(self): return self.template.macros['create']
|
||||
|
||||
title = _(u'Create Resource, Type = ')
|
||||
form_action = 'create_resource'
|
||||
|
||||
@property
|
||||
def form_fields(self):
|
||||
typeToken = self.request.get('form.type')
|
||||
if typeToken:
|
||||
t = self.loopsRoot.loopsTraverse(typeToken)
|
||||
ifc = ITypeConcept(t).typeInterface
|
||||
else:
|
||||
ifc = INote
|
||||
return FormFields(ifc)
|
||||
|
||||
|
||||
class EditObjectForm(ObjectForm, EditForm):
|
||||
|
||||
@property
|
||||
def macro(self): return self.template.macros['edit']
|
||||
|
||||
title = _(u'Edit Resource')
|
||||
form_action = 'edit_resource'
|
||||
|
||||
@Lazy
|
||||
def typeInterface(self):
|
||||
return IType(self.context).typeInterface
|
||||
|
||||
@property
|
||||
def form_fields(self):
|
||||
return FormFields(self.typeInterface)
|
||||
|
||||
def __init__(self, context, request):
|
||||
super(EditObjectForm, self).__init__(context, request)
|
||||
self.context = self.virtualTargetObject
|
||||
|
||||
|
||||
class InnerForm(ObjectForm):
|
||||
|
||||
@property
|
||||
def macro(self): return self.template.macros['fields']
|
||||
|
||||
|
||||
class CreateObject(FormController):
|
||||
|
||||
@Lazy
|
||||
|
@ -87,12 +123,13 @@ class CreateObject(FormController):
|
|||
raise BadRequest('Title field is empty')
|
||||
name = INameChooser(container).chooseName(title, obj)
|
||||
container[name] = obj
|
||||
obj.resourceType = self.loopsRoot.getConceptManager()['note']
|
||||
tc = form.get('form.type') or '.loops/concepts/note'
|
||||
obj.resourceType = self.loopsRoot.loopsTraverse(tc)
|
||||
adapter = IType(obj).typeInterface(obj)
|
||||
for k in form.keys():
|
||||
if k.startswith(prefix):
|
||||
fn = k[len(prefix):]
|
||||
if fn in ('action',):
|
||||
if fn in ('action', 'type',) or fn.endswith('-empty-marker'):
|
||||
continue
|
||||
value = form[k]
|
||||
if fn.startswith(conceptPrefix):
|
||||
|
@ -104,7 +141,7 @@ class CreateObject(FormController):
|
|||
return True
|
||||
|
||||
def assignConcepts(self, obj, fieldName, value):
|
||||
if fieldName == 'search.text_selected':
|
||||
if value and fieldName == 'search.text_selected':
|
||||
concept = util.getObjectForUid(value)
|
||||
obj.assignConcept(concept)
|
||||
|
||||
|
@ -117,3 +154,36 @@ class ResourceNameChooser(NameChooser):
|
|||
name = title.replace(' ', '_').lower()
|
||||
name = super(ResourceNameChooser, self).chooseName(name, obj)
|
||||
return name
|
||||
|
||||
|
||||
class EditObject(FormController):
|
||||
|
||||
@Lazy
|
||||
def loopsRoot(self):
|
||||
return self.view.loopsRoot
|
||||
|
||||
def update(self):
|
||||
prefix = 'form.'
|
||||
conceptPrefix = 'concept.'
|
||||
form = self.request.form
|
||||
obj = self.view.virtualTargetObject
|
||||
adapter = IType(obj).typeInterface(obj)
|
||||
for k in form.keys():
|
||||
if k.startswith(prefix):
|
||||
fn = k[len(prefix):]
|
||||
if fn in ('action', 'type',) or fn.endswith('-empty-marker'):
|
||||
continue
|
||||
value = form[k]
|
||||
if fn.startswith(conceptPrefix):
|
||||
self.assignConcepts(obj, fn[len(conceptPrefix):], value)
|
||||
else:
|
||||
setattr(adapter, fn, value)
|
||||
notify(ObjectModifiedEvent(obj))
|
||||
return True
|
||||
|
||||
def assignConcepts(self, obj, fieldName, value):
|
||||
if value and fieldName == 'search.text_selected':
|
||||
concept = util.getObjectForUid(value)
|
||||
obj.assignConcept(concept)
|
||||
|
||||
|
||||
|
|
|
@ -1,16 +1,82 @@
|
|||
<metal:block define-macro="create">
|
||||
<div tal:define="dummy view/setUp">
|
||||
<form method="post">
|
||||
<input type="hidden" name="form.action" value="create"
|
||||
<metal:block define-macro="edit">
|
||||
<form method="post">
|
||||
<input type="hidden" name="form.action" value="edit"
|
||||
tal:attributes="value view/form_action" />
|
||||
<table cellpadding="3" class="form">
|
||||
<table cellpadding="3" class="form">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th colspan="4">
|
||||
<br />
|
||||
<span tal:replace="view/title"
|
||||
i18n:translate="">Create Information Object</span>
|
||||
i18n:translate="">Edit Information Object
|
||||
</span>
|
||||
</th>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
<tbody id="form.fields">
|
||||
<metal:fields use-macro="view/template/macros/fields" />
|
||||
</tbody>
|
||||
|
||||
<tbody>
|
||||
<tr>
|
||||
<td colspan="4" class="headline">Assign Concept(s)</td>
|
||||
</tr>
|
||||
<tr metal:use-macro="view/template/macros/search_concepts" />
|
||||
<tr metal:use-macro="view/template/macros/buttons" />
|
||||
</tbody>
|
||||
</table>
|
||||
</form>
|
||||
</metal:block>
|
||||
|
||||
|
||||
<metal:block define-macro="create">
|
||||
<form method="post">
|
||||
<input type="hidden" name="form.action" value="create"
|
||||
tal:attributes="value view/form_action" />
|
||||
<table cellpadding="3" class="form">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th colspan="4">
|
||||
<br />
|
||||
<span tal:replace="view/title"
|
||||
i18n:translate="">Create Information Object</span>
|
||||
<select name="form.type" id="form.type"
|
||||
tal:attributes="onChange
|
||||
string:return replaceFieldsNode(
|
||||
'form.fields', 'form.type', 'inner_form.html')">
|
||||
<option value=".loops/concepts/note"
|
||||
tal:repeat="type view/resourceTypes"
|
||||
tal:content="type/title"
|
||||
tal:attributes="value type/token;
|
||||
selected python:
|
||||
type.token == (request.get('form.type')
|
||||
or '.loops/concepts/note')">
|
||||
Note
|
||||
</option>
|
||||
</select>
|
||||
</th>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
<tbody id="form.fields">
|
||||
<metal:fields use-macro="view/template/macros/fields" />
|
||||
</tbody>
|
||||
|
||||
<tbody>
|
||||
<tr>
|
||||
<td colspan="4" class="headline">Assign Concept(s)</td>
|
||||
</tr>
|
||||
<tr metal:use-macro="view/template/macros/search_concepts" />
|
||||
<tr metal:use-macro="view/template/macros/buttons" />
|
||||
</tbody>
|
||||
</table>
|
||||
</form>
|
||||
</metal:block>
|
||||
|
||||
|
||||
<metal:fields define-macro="fields">
|
||||
<tal:block tal:define="dummy view/setUp">
|
||||
<tr tal:repeat="widget view/widgets">
|
||||
<td class="label"
|
||||
tal:define="hint widget/hint">
|
||||
|
@ -31,42 +97,40 @@
|
|||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tal:block>
|
||||
</metal:fields>
|
||||
|
||||
<tr>
|
||||
<td colspan="4" class="headline">Assign Concept(s)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="form.concept.search.type">Type:</label></td>
|
||||
<td>
|
||||
<select name="form.concept.search.type" id="form.concept.search.type"
|
||||
tal:attributes="onChange
|
||||
string:setConceptTypeForComboBox(
|
||||
'form.concept.search.type', 'form.concept.search.text')">
|
||||
<tal:types repeat="type view/conceptTypesForSearch">
|
||||
<option value="loops:*"
|
||||
i18n:translate=""
|
||||
tal:attributes="value type/token"
|
||||
tal:content="type/title">Topic</option>
|
||||
</tal:types>
|
||||
</select>
|
||||
</td>
|
||||
<td><label for="form.concept.search.text">Search text:</label></td>
|
||||
<td>
|
||||
<input dojoType="comboBox" mode="remote" autoComplete="False"
|
||||
name="form.concept.search.text" id="form.concept.search.text"
|
||||
tal:attributes="dataUrl
|
||||
string:${context/@@absolute_url}/listConceptsForComboBox.js?searchString=%{searchString}&searchType=" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
||||
<tr metal:define-macro="search_concepts">
|
||||
<td><label for="form.concept.search.type">Type:</label></td>
|
||||
<td>
|
||||
<select name="form.concept.search.type" id="form.concept.search.type"
|
||||
tal:attributes="onChange
|
||||
string:setConceptTypeForComboBox(
|
||||
'form.concept.search.type', 'form.concept.search.text')">
|
||||
<tal:types repeat="type view/conceptTypesForSearch">
|
||||
<option value="loops:*"
|
||||
i18n:translate=""
|
||||
tal:attributes="value type/token"
|
||||
tal:content="type/title">Topic</option>
|
||||
</tal:types>
|
||||
</select>
|
||||
</td>
|
||||
<td><label for="form.concept.search.text">Search text:</label></td>
|
||||
<td>
|
||||
<input dojoType="comboBox" mode="remote" autoComplete="False"
|
||||
name="form.concept.search.text" id="form.concept.search.text"
|
||||
tal:attributes="dataUrl
|
||||
string:${context/@@absolute_url}/listConceptsForComboBox.js?searchString=%{searchString}&searchType=" />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
|
||||
<tr metal:define-macro="buttons">
|
||||
<td colspan="4">
|
||||
<input type="button" value="Cancel"
|
||||
onclick="createObjectDlg.hide()">
|
||||
onclick="objectDlg.hide()">
|
||||
<input type="submit" value="Save"
|
||||
onclick="createObjectDlg.hide()">
|
||||
onclick="objectDlg.hide()">
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</form>
|
||||
</div>
|
||||
</metal:block>
|
||||
</tr>
|
||||
|
|
|
@ -13,9 +13,12 @@ function focusOpener() {
|
|||
}
|
||||
}
|
||||
|
||||
/*function listConceptsForComboBox() {
|
||||
return [['Zope', 'zope'], ]
|
||||
}*/
|
||||
function replaceFieldsNode(targetId, typeId, url) {
|
||||
token = dojo.byId(typeId).value;
|
||||
dojo.io.updateNode(targetId, {
|
||||
url: url + '?form.type=' + token,
|
||||
});
|
||||
}
|
||||
|
||||
function submitReplacing(targetId, formId, actionUrl) {
|
||||
dojo.io.updateNode(targetId, {
|
||||
|
@ -54,19 +57,17 @@ function setConceptTypeForComboBox(typeId, cbId) {
|
|||
dp.searchUrl = newUrl;
|
||||
}
|
||||
|
||||
var createObjectDlg = false;
|
||||
var objectDlg = false;
|
||||
|
||||
function createObjectDialog() {
|
||||
//createObjectDlg = dojo.widget.byId('createObject');
|
||||
//createObjectDlg = false;
|
||||
function objectDialog(url) {
|
||||
dojo.require('dojo.widget.Dialog');
|
||||
dojo.require('dojo.widget.ComboBox');
|
||||
if (!createObjectDlg) {
|
||||
createObjectDlg = dojo.widget.fromScript('Dialog',
|
||||
if (!objectDlg) {
|
||||
objectDlg = dojo.widget.fromScript('Dialog',
|
||||
{bgColor: 'white', bgOpacity: 0.5, toggle: 'fade', toggleDuration: 250,
|
||||
executeScripts: true,
|
||||
href: 'create_object.html'
|
||||
}, dojo.byId('createObject'));
|
||||
href: url
|
||||
}, dojo.byId('objectDialog'));
|
||||
}
|
||||
createObjectDlg.show();
|
||||
objectDlg.show();
|
||||
}
|
||||
|
|
|
@ -206,13 +206,16 @@
|
|||
<metal:actions define-macro="actions">
|
||||
<div class="menu-2"
|
||||
tal:define="dummy view/registerDojo">
|
||||
<a href="javascript:createObjectDialog()"
|
||||
onclick="createObjectDialog(); return false;">
|
||||
<a href="#"
|
||||
onclick="objectDialog('create_object.html'); return false;">
|
||||
Create Resource...
|
||||
</a><br />
|
||||
<a href="#"
|
||||
onclick="objectDialog('edit_object.html'); return false;">
|
||||
Edit Resource...
|
||||
</a>
|
||||
<span id="createObject">
|
||||
</span>
|
||||
</div>
|
||||
<span id="objectDialog"></span>
|
||||
</metal:actions>
|
||||
|
||||
|
||||
|
|
|
@ -72,7 +72,7 @@ class IConcept(ILoopsObject, IPotentialTarget):
|
|||
title=_(u'Title'),
|
||||
description=_(u'Title of the concept'),
|
||||
default=u'',
|
||||
required=False)
|
||||
required=True)
|
||||
|
||||
conceptType = schema.Choice(
|
||||
title=_(u'Concept Type'),
|
||||
|
@ -190,7 +190,7 @@ class IBaseResource(ILoopsObject):
|
|||
description=_(u'Title of the resource'),
|
||||
default=u'',
|
||||
missing_value=u'',
|
||||
required=False)
|
||||
required=True)
|
||||
|
||||
resourceType = schema.Choice(
|
||||
title=_(u'Resource Type'),
|
||||
|
@ -228,7 +228,7 @@ class IResourceSchema(Interface):
|
|||
description=_(u'Title of the resource'),
|
||||
default=u'',
|
||||
missing_value=u'',
|
||||
required=False)
|
||||
required=True)
|
||||
|
||||
data = schema.Bytes(
|
||||
title=_(u'Data'),
|
||||
|
@ -549,7 +549,7 @@ class IResourceAdapter(IBaseResourceSchema):
|
|||
"""
|
||||
|
||||
|
||||
class IFile(IResourceAdapter):
|
||||
class IFile(IResourceAdapter, IResourceSchema):
|
||||
""" A media asset that is not shown on a (web) page like an image but
|
||||
may be downloaded instead.
|
||||
"""
|
||||
|
|
15
resource.py
15
resource.py
|
@ -194,6 +194,15 @@ class FileAdapter(ResourceAdapterBase):
|
|||
"""
|
||||
|
||||
implements(IFile)
|
||||
_schemas = list(IFile) + list(IBaseResource)
|
||||
|
||||
|
||||
class TextDocumentAdapter(ResourceAdapterBase):
|
||||
""" A type adapter for providing text document functionality for resources.
|
||||
"""
|
||||
|
||||
implements(IDocument)
|
||||
_schemas = list(IDocument) + list(IBaseResource)
|
||||
|
||||
|
||||
class NoteAdapter(ResourceAdapterBase):
|
||||
|
@ -203,13 +212,11 @@ class NoteAdapter(ResourceAdapterBase):
|
|||
implements(INote)
|
||||
_schemas = list(INote) + list(IBaseResource)
|
||||
|
||||
#contentType = u'text/restructured'
|
||||
|
||||
|
||||
class DocumentWriteFileAdapter(object):
|
||||
|
||||
implements(IWriteFile)
|
||||
adapts(IDocument)
|
||||
adapts(IResource)
|
||||
|
||||
def __init__(self, context):
|
||||
self.context = context
|
||||
|
@ -222,7 +229,7 @@ class DocumentWriteFileAdapter(object):
|
|||
class DocumentReadFileAdapter(object):
|
||||
|
||||
implements(IReadFile)
|
||||
adapts(IDocument)
|
||||
adapts(IResource)
|
||||
|
||||
def __init__(self, context):
|
||||
self.context = context
|
||||
|
|
Loading…
Add table
Reference in a new issue