diff --git a/README.txt b/README.txt index 30f518a..c4d8838 100755 --- a/README.txt +++ b/README.txt @@ -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() diff --git a/browser/common.py b/browser/common.py index 5ade9b5..2a1198d 100644 --- a/browser/common.py +++ b/browser/common.py @@ -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): diff --git a/browser/concept.py b/browser/concept.py index 69e53b4..cb6aa5b 100644 --- a/browser/concept.py +++ b/browser/concept.py @@ -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) diff --git a/browser/configure.zcml b/browser/configure.zcml index 3feae2f..c61e6bb 100644 --- a/browser/configure.zcml +++ b/browser/configure.zcml @@ -303,7 +303,7 @@ /> + + + + + + diff --git a/browser/form.py b/browser/form.py index 49941a6..4e03714 100644 --- a/browser/form.py +++ b/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) + + diff --git a/browser/form_macros.pt b/browser/form_macros.pt index 3fbc673..7a83407 100644 --- a/browser/form_macros.pt +++ b/browser/form_macros.pt @@ -1,16 +1,82 @@ - - - - + + - + + + Create Information Object + i18n:translate="">Edit Information Object + + + + + + + + + Assign Concept(s) + + + + + + + + + + + + + + + + + + Create Information Object + + + Note + + + + + + + + + + + + + Assign Concept(s) + + + + + + + + + + + @@ -31,42 +97,40 @@ + + - - Assign Concept(s) - - - Type: - - - - Topic - - - - Search text: - - - - - + + + Type: + + + + Topic + + + + Search text: + + + + + + + + onclick="objectDlg.hide()"> + onclick="objectDlg.hide()"> - - - - - + diff --git a/browser/loops.js b/browser/loops.js index 3166cb2..29a8ae5 100644 --- a/browser/loops.js +++ b/browser/loops.js @@ -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(); } diff --git a/browser/node_macros.pt b/browser/node_macros.pt index cb20d27..8bd08df 100644 --- a/browser/node_macros.pt +++ b/browser/node_macros.pt @@ -206,13 +206,16 @@ - + Create Resource... + + + Edit Resource... - - + diff --git a/interfaces.py b/interfaces.py index 781b73d..4adc68f 100644 --- a/interfaces.py +++ b/interfaces.py @@ -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. """ diff --git a/resource.py b/resource.py index 82093d7..94cfd17 100644 --- a/resource.py +++ b/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