provide create and edit dialogs for concepts - starting with glossary items

git-svn-id: svn://svn.cy55.de/Zope3/src/loops/trunk@2209 fd906abe-77d9-0310-91a1-e0d9ade77398
This commit is contained in:
helmutm 2007-12-03 10:23:38 +00:00
parent 3a3755f466
commit 309f5f1e21
13 changed files with 228 additions and 73 deletions

View file

@ -239,10 +239,13 @@ class BaseView(GenericView):
def listTypes(self, include=None, exclude=None, sortOn='title'):
types = [dict(token=t.token, title=t.title)
for t in ITypeManager(self.context).listTypes(include, exclude)]
if sortOn:
types.sort(key=lambda x: x[sortOn])
#if sortOn:
# types.sort(key=lambda x: x[sortOn])
return types
def getTypesVocabulary(self, include=None):
return util.KeywordVocabulary(self.listTypes(include, ('hidden',)))
def resourceTypes(self):
return util.KeywordVocabulary(self.listTypes(('resource',), ('hidden',)))
#if t.factory == Resource]) # ? if necessary -> type.qualifiers
@ -377,16 +380,6 @@ class BaseView(GenericView):
cm.register('js', 'dojo.js', resourceName='ajax.dojo/dojo.js')
# actions - obsolete, see loops.browser.action
#class Action(object):
# def __init__(self, renderer, url, **kw):
# self.renderer = renderer
# self.url = url
# self.__dict__.update(kw)
# vocabulary stuff
class LoopsTerms(object):

View file

@ -1,5 +1,5 @@
#
# Copyright (c) 2004 Helmut Merz helmutm@cy55.de
# Copyright (c) 2007 Helmut Merz helmutm@cy55.de
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@ -60,10 +60,13 @@ class ConceptEditForm(EditForm):
@property
def form_fields(self):
fields = FormFields(IConcept)
typeInterface = self.typeInterface
if typeInterface is not None:
fields = FormFields(fields, typeInterface)
if typeInterface is None:
fields = FormFields(IConcept)
elif 'title' in typeInterface: # new "complete" type interface
fields = FormFields(typeInterface)
else:
fields = FormFields(IConcept, typeInterface)
return fields
def setUpWidgets(self, ignore_request=False):
@ -96,9 +99,12 @@ class ConceptView(BaseView):
def fieldData(self):
ti = IType(self.context).typeInterface
if not ti: return
if not ti:
return
adapter = ti(self.context)
for n, f in schema.getFieldsInOrder(ti):
if n in ('title', 'description',): # already shown in header
continue
value = getattr(adapter, n, '')
if not value:
continue

View file

@ -557,6 +557,13 @@
permission="zope.ManageContent"
/>
<page
name="create_concept.html"
for="loops.interfaces.INode"
class="loops.browser.form.CreateConceptForm"
permission="zope.ManageContent"
/>
<page
name="edit_concept.html"
for="loops.interfaces.INode"
@ -571,6 +578,13 @@
permission="zope.ManageContent"
/>
<page
name="inner_concept_form.html"
for="loops.interfaces.INode"
class="loops.browser.form.InnerConceptForm"
permission="zope.ManageContent"
/>
<zope:adapter
name="create_resource"
for="loops.browser.node.NodeView
@ -587,6 +601,22 @@
permission="zope.ManageContent"
/>
<zope:adapter
name="create_concept"
for="loops.browser.node.NodeView
zope.publisher.interfaces.browser.IBrowserRequest"
factory="loops.browser.form.CreateConcept"
permission="zope.ManageContent"
/>
<zope:adapter
name="edit_concept"
for="loops.browser.node.NodeView
zope.publisher.interfaces.browser.IBrowserRequest"
factory="loops.browser.form.EditConcept"
permission="zope.ManageContent"
/>
<!-- inner HTML views -->
<page

View file

@ -25,6 +25,7 @@ $Id$
from zope import component, interface, schema
from zope.component import adapts
from zope.event import notify
from zope.interface import Interface
from zope.lifecycleevent import ObjectCreatedEvent, ObjectModifiedEvent
from zope.app.container.interfaces import INameChooser
@ -46,8 +47,8 @@ from cybertools.composer.schema.browser.common import schema_macros, schema_edit
from cybertools.composer.schema.schema import FormState
from cybertools.typology.interfaces import IType, ITypeManager
from loops.common import adapted
from loops.concept import Concept, ResourceRelation
from loops.interfaces import IConcept, IResourceManager, IDocument
from loops.concept import Concept, ConceptRelation, ResourceRelation
from loops.interfaces import IConcept, IConceptSchema, IResourceManager, IDocument
from loops.interfaces import IFile, IExternalFile, INote, ITextDocument
from loops.browser.node import NodeView
from loops.browser.concept import ConceptRelationView
@ -98,9 +99,10 @@ class ObjectForm(NodeView):
@Lazy
def schema(self):
#ti = self.typeInterface or Interface #IConcept
ti = self.typeInterface or IConceptSchema
schemaFactory = component.getAdapter(self.adapted, ISchemaFactory)
return schemaFactory(self.typeInterface, manager=self,
request=self.request)
return schemaFactory(ti, manager=self, request=self.request)
@Lazy
def fields(self):
@ -186,11 +188,10 @@ class EditConceptForm(EditObjectForm):
title = _(u'Edit Concept')
form_action = 'edit_concept'
isInnerHtml = False
@Lazy
def typeInterface(self):
return IType(self.target).typeInterface or IConcept
return IType(self.target).typeInterface or IConceptSchema
@property
def assignments(self):
@ -224,13 +225,11 @@ class CreateObjectForm(ObjectForm):
t = self.loopsRoot.loopsTraverse(typeToken)
return removeSecurityProxy(ITypeConcept(t).typeInterface)
else:
#return INote
return ITextDocument
@property
def assignments(self):
target = self.virtualTargetObject
#target = self.target
if (IConcept.providedBy(target) and
target.conceptType !=
self.loopsRoot.getConceptManager().getTypeConcept()):
@ -239,10 +238,58 @@ class CreateObjectForm(ObjectForm):
return ()
class CreateConceptForm(CreateObjectForm):
title = _(u'Create Concept, Type = ')
form_action = 'create_concept'
@Lazy
def dialog_name(self):
return self.request.get('dialog', 'createConcept')
@Lazy
def adapted(self):
ti = self.typeInterface
if ti is None:
return Concept()
return ti(Concept())
@Lazy
def instance(self):
return IInstance(Concept())
@Lazy
def typeInterface(self):
typeToken = self.request.get('form.type')
if typeToken:
t = self.loopsRoot.loopsTraverse(typeToken)
return removeSecurityProxy(ITypeConcept(t).typeInterface)
else:
return None
@property
def assignments(self):
target = self.virtualTargetObject
if (IConcept.providedBy(target) and
target.conceptType !=
self.loopsRoot.getConceptManager().getTypeConcept()):
rv = ConceptRelationView(ConceptRelation(target, None), self.request)
return (rv,)
return ()
class InnerForm(CreateObjectForm):
@property
def macro(self): return self.fieldRenderers['fields']
def macro(self):
return self.fieldRenderers['fields']
class InnerConceptForm(CreateConceptForm):
@property
def macro(self):
return self.fieldRenderers['fields']
# processing form input
@ -296,12 +343,6 @@ class EditObject(FormController):
self.object = obj
formState = self.updateFields()
# TODO: error handling
#errors = self.updateFields()
#if errors:
# self.view.setUp()
# for fieldName, message in errors.items():
# self.view.widgets[fieldName].error = message
# return True
self.request.response.redirect(self.view.virtualTargetUrl + '?version=this')
return False
@ -342,8 +383,10 @@ class EditObject(FormController):
return dict(fileupload=self.handleFileUpload)
def collectConcepts(self, fieldName, value):
if self.old is None: self.old = []
if self.selected is None: self.selected = []
if self.old is None:
self.old = []
if self.selected is None:
self.selected = []
for v in value:
if fieldName == 'old':
self.old.append(v)
@ -356,15 +399,24 @@ class EditObject(FormController):
c, p = v.split(':')
concept = util.getObjectForUid(c)
predicate = util.getObjectForUid(p)
obj.deassignConcept(concept, [predicate])
self.deassignConcept(obj, concept, [predicate])
for v in self.selected:
if v != 'none' and v not in self.old:
c, p = v.split(':')
concept = util.getObjectForUid(c)
predicate = util.getObjectForUid(p)
exists = obj.getConceptRelations(predicates=[p], concept=concept)
exists = self.getConceptRelations(obj, [p], concept)
if not exists:
obj.assignConcept(concept, predicate)
self.assignConcept(obj, concept, predicate)
def getConceptRelations(self, obj, predicates, concept):
return obj.getConceptRelations(predicates=predicates, concept=concept)
def assignConcept(self, obj, concept, predicate):
obj.assignConcept(concept, predicate)
def deassignConcept(self, obj, concept, predicates):
obj.deassignConcept(concept, predicates)
def checkCreateVersion(self, obj):
form = self.request.form
@ -379,14 +431,15 @@ class EditObject(FormController):
class CreateObject(EditObject):
def update(self):
form = self.request.form
container = self.loopsRoot.getResourceManager()
title = form.get('title')
if not title:
raise BadRequest('Title field is empty')
obj = Resource(title)
data = form.get('data')
factory = Resource
defaultTypeToken = '.loops/concepts/textdocument'
@Lazy
def container(self):
return self.loopsRoot.getResourceManager()
def getNameFromData(self):
data = self.request.form.get('data')
if data and isinstance(data, FileUpload):
name = getattr(data, 'filename', None)
# strip path from IE uploads:
@ -394,11 +447,21 @@ class CreateObject(EditObject):
name = name.rsplit('\\', 1)[-1]
else:
name = None
return name
def update(self):
form = self.request.form
container = self.container
title = form.get('title')
if not title:
raise BadRequest('Title field is empty')
obj = self.factory(title)
name = self.getNameFromData()
# TODO: validate fields
name = INameChooser(container).chooseName(name, obj)
container[name] = obj
tc = form.get('form.type') or '.loops/concepts/note'
obj.resourceType = self.loopsRoot.loopsTraverse(tc)
tc = form.get('form.type') or self.defaultTypeToken
obj.setType(self.loopsRoot.loopsTraverse(tc))
notify(ObjectCreatedEvent(obj))
self.object = obj
self.updateFields() # TODO: suppress validation
@ -406,3 +469,38 @@ class CreateObject(EditObject):
self.request.response.redirect(self.view.request.URL)
return False
class EditConcept(EditObject):
def getConceptRelations(self, obj, predicates, concept):
return obj.getParentRelations(predicates=predicates, parent=concept)
def assignConcept(self, obj, concept, predicate):
obj.assignParent(concept, predicate)
def deassignConcept(self, obj, concept, predicates):
obj.deassignParent(concept, predicates)
def update(self):
self.object = self.view.virtualTargetObject
formState = self.updateFields()
# TODO: error handling
self.request.response.redirect(self.view.virtualTargetUrl)
return False
class CreateConcept(EditConcept, CreateObject):
factory = Concept
defaultTypeToken = '.loops/concepts/topic'
@Lazy
def container(self):
return self.loopsRoot.getConceptManager()
def getNameFromData(self):
return None
def update(self):
return CreateObject.update(self)

View file

@ -36,7 +36,9 @@
<metal:block define-macro="create" i18n:domain="loops">
<form method="post" enctype="multipart/form-data">
<form method="post" enctype="multipart/form-data"
tal:define="qualifier request/qualifier | string:resource;
innerForm request/inner_form | string:inner_form.html">
<input type="hidden" name="form.action" value="create"
tal:attributes="value view/form_action" />
<table cellpadding="3" class="form">
@ -47,10 +49,12 @@
tal:attributes="onChange
string:return replaceFieldsNode(
'form.fields', 'form.type',
'${view/url}/inner_form.html')">
'${view/url}/$innerForm')">
<option value=".loops/concepts/note"
i18n:translate=""
tal:repeat="type view/resourceTypes"
xtal:repeat="type view/resourceTypes"
tal:repeat="type python:
view.getTypesVocabulary((qualifier,))"
tal:content="type/title"
tal:attributes="value type/token;
selected python:

View file

@ -23,7 +23,6 @@ $Id$
"""
from zope import component, schema
#from zope.app import zapi
from zope.app.container.btree import BTreeContainer
from zope.app.container.contained import Contained
from zope.cachedescriptors.property import Lazy
@ -130,6 +129,9 @@ class Concept(Contained, Persistent):
def getType(self):
return self.conceptType
def setType(self, value):
self.conceptType = value
def getLoopsRoot(self):
return getParent(self).getLoopsRoot()
@ -252,7 +254,6 @@ class ConceptManager(BTreeContainer):
def getTypeConcept(self):
if self.typeConcept is None:
#self.typeConcept = self['type']
self.typeConcept = self.get('type')
return self.typeConcept
@ -311,7 +312,6 @@ class PredicateSourceList(object):
typePred = cm.getTypePredicate()
if defPred is not None and typePred is not None:
result.append(defPred)
#result.append(typePred)
predType = defPred.conceptType
if predType is not None and predType != cm.getTypeConcept():
result.extend(p for p in predType.getChildren([typePred])

View file

@ -69,11 +69,8 @@ class IPotentialTarget(Interface):
# concept interfaces
class IConcept(ILoopsObject, IPotentialTarget):
""" The concept is the central element of the loops framework.
A concept is related to other concepts, may have resources
associated with it and may be referenced by views.
class IConceptSchema(Interface):
""" The primary fields of a concept object.
"""
title = schema.TextLine(
@ -90,6 +87,14 @@ class IConcept(ILoopsObject, IPotentialTarget):
missing_value=u'',
required=False)
class IConcept(IConceptSchema, ILoopsObject, IPotentialTarget):
""" The concept is the central element of the loops framework.
A concept is related to other concepts, may have resources
associated with it and may be referenced by views.
"""
conceptType = schema.Choice(
title=_(u'Concept Type'),
description=_(u"The type of the concept, specified by a relation to "
@ -575,7 +580,8 @@ class IIndexAttributes(Interface):
# types stuff
class ITypeConcept(Interface):
#class ITypeConcept(Interface):
class ITypeConcept(IConceptSchema):
""" Concepts of type 'type' should be adaptable to this interface.
"""

View file

@ -27,12 +27,12 @@
</zope:class>
<zope:adapter factory="loops.knowledge.knowledge.Topic"
provides="cybertools.knowledge.interfaces.IKnowledgeElement"
provides="loops.knowledge.interfaces.ITopic"
trusted="True" />
<zope:class class="loops.knowledge.knowledge.Topic">
<require permission="zope.View"
interface="cybertools.knowledge.interfaces.IKnowledgeElement" />
interface="loops.knowledge.interfaces.ITopic" />
</zope:class>
<zope:adapter factory="loops.knowledge.knowledge.Task"

View file

@ -46,10 +46,14 @@ class GlossaryView(ConceptView):
if category == 'portlet':
actions.append(Action(self, title='Create Glossary Item...',
description='Create a new glossary item.',
url='create_glossaryitem.html',
onClick="objectDialog('create', '%s/create_object.html'); "
"return false;" % self.virtualTargetUrl,
innerHtmlId='dialog.create'))
url='create_concept.html',
onClick="objectDialog('createGlossaryItem', "
" '%s/create_concept.html?qualifier=concept"
"&form.type=.loops/concepts/topic"
"&inner_form=inner_concept_form.html"
"&dialog=createGlossaryItem'); "
"return false;" % page.virtualTargetUrl,
innerHtmlId='dialog.createGlossaryItem'))
return actions
@ -62,11 +66,13 @@ class GlossaryItemView(ConceptView):
def getActions(self, category='object', page=None):
actions = []
if category == 'portlet':
actions.append(Action(self, title='Create Glossary Item...',
description='Create a new glossary item.',
url='create_glossaryitem.html'))
actions.append(Action(self, title='Edit Glossary Item...',
description='Modify glossary item.',
url='edit_glossaryitem.html'))
url='edit_concept.html',
onClick="objectDialog('editGlossaryItem', "
" '%s/edit_concept.html"
"?dialog=editGlossaryItem'); "
"return false;" % page.virtualTargetUrl,
innerHtmlId='dialog.editGlossaryItem'))
return actions

View file

@ -26,4 +26,5 @@
</a><tal:comma condition="not:repeat/related/end">,</tal:comma>
</span>
</p>
<metal:resources use-macro="item/conceptMacros/conceptresources" />
</metal:block>

View file

@ -29,8 +29,10 @@ from zope.i18nmessageid import MessageFactory
from zope.security.proxy import removeSecurityProxy
from cybertools.knowledge.interfaces import IKnowing, IRequirementProfile
from loops.organize.interfaces import IPerson as IBasePerson
from cybertools.knowledge.interfaces import IKnowledgeElement
from cybertools.organize.interfaces import ITask as IBaseTask
from loops.interfaces import IConceptSchema
from loops.organize.interfaces import IPerson as IBasePerson
_ = MessageFactory('zope')
@ -45,3 +47,9 @@ class IPerson(IBasePerson, IKnowing):
class ITask(IBaseTask, IRequirementProfile):
""" A task, also acting as a knowledge requirement profile.
"""
class ITopic(IConceptSchema, IKnowledgeElement):
""" Just a topic, some general classification concept.
"""

View file

@ -33,7 +33,7 @@ from cybertools.typology.interfaces import IType
from cybertools.knowledge.interfaces import IKnowledgeElement, IKnowledgeProvider
from cybertools.knowledge.knowing import Knowing
from loops.interfaces import IConcept, IResource
from loops.knowledge.interfaces import IPerson, ITask
from loops.knowledge.interfaces import IPerson, ITask, ITopic
from loops.organize.party import Person as BasePerson
from loops.organize.task import Task as BaseTask
from loops.common import AdapterBase
@ -42,7 +42,7 @@ from loops.type import TypeInterfaceSourceList
# register type interfaces - (TODO: use a function for this)
TypeInterfaceSourceList.typeInterfaces += (IPerson, IKnowledgeElement,
TypeInterfaceSourceList.typeInterfaces += (IPerson, ITopic, IKnowledgeElement,
ITask, IKnowledgeProvider)
@ -99,7 +99,7 @@ class Topic(AdapterBase, KnowledgeAdapterMixin):
may act as a knowledge element.
"""
implements(IKnowledgeElement)
implements(ITopic)
_adapterAttributes = ('context', '__parent__', 'parent')
def getParent(self):

View file

@ -140,6 +140,9 @@ class Resource(Image, Contained):
def getType(self):
return self.resourceType
def setType(self, value):
self.resourceType = value
def _setData(self, data):
#if not data:
# return