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'): def listTypes(self, include=None, exclude=None, sortOn='title'):
types = [dict(token=t.token, title=t.title) types = [dict(token=t.token, title=t.title)
for t in ITypeManager(self.context).listTypes(include, exclude)] for t in ITypeManager(self.context).listTypes(include, exclude)]
if sortOn: #if sortOn:
types.sort(key=lambda x: x[sortOn]) # types.sort(key=lambda x: x[sortOn])
return types return types
def getTypesVocabulary(self, include=None):
return util.KeywordVocabulary(self.listTypes(include, ('hidden',)))
def resourceTypes(self): def resourceTypes(self):
return util.KeywordVocabulary(self.listTypes(('resource',), ('hidden',))) return util.KeywordVocabulary(self.listTypes(('resource',), ('hidden',)))
#if t.factory == Resource]) # ? if necessary -> type.qualifiers #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') 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 # vocabulary stuff
class LoopsTerms(object): 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 # 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 # it under the terms of the GNU General Public License as published by
@ -60,10 +60,13 @@ class ConceptEditForm(EditForm):
@property @property
def form_fields(self): def form_fields(self):
fields = FormFields(IConcept)
typeInterface = self.typeInterface typeInterface = self.typeInterface
if typeInterface is not None: if typeInterface is None:
fields = FormFields(fields, typeInterface) fields = FormFields(IConcept)
elif 'title' in typeInterface: # new "complete" type interface
fields = FormFields(typeInterface)
else:
fields = FormFields(IConcept, typeInterface)
return fields return fields
def setUpWidgets(self, ignore_request=False): def setUpWidgets(self, ignore_request=False):
@ -96,9 +99,12 @@ class ConceptView(BaseView):
def fieldData(self): def fieldData(self):
ti = IType(self.context).typeInterface ti = IType(self.context).typeInterface
if not ti: return if not ti:
return
adapter = ti(self.context) adapter = ti(self.context)
for n, f in schema.getFieldsInOrder(ti): for n, f in schema.getFieldsInOrder(ti):
if n in ('title', 'description',): # already shown in header
continue
value = getattr(adapter, n, '') value = getattr(adapter, n, '')
if not value: if not value:
continue continue

View file

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

View file

@ -25,6 +25,7 @@ $Id$
from zope import component, interface, schema from zope import component, interface, schema
from zope.component import adapts from zope.component import adapts
from zope.event import notify from zope.event import notify
from zope.interface import Interface
from zope.lifecycleevent import ObjectCreatedEvent, ObjectModifiedEvent from zope.lifecycleevent import ObjectCreatedEvent, ObjectModifiedEvent
from zope.app.container.interfaces import INameChooser 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.composer.schema.schema import FormState
from cybertools.typology.interfaces import IType, ITypeManager from cybertools.typology.interfaces import IType, ITypeManager
from loops.common import adapted from loops.common import adapted
from loops.concept import Concept, ResourceRelation from loops.concept import Concept, ConceptRelation, ResourceRelation
from loops.interfaces import IConcept, IResourceManager, IDocument from loops.interfaces import IConcept, IConceptSchema, IResourceManager, IDocument
from loops.interfaces import IFile, IExternalFile, INote, ITextDocument from loops.interfaces import IFile, IExternalFile, INote, ITextDocument
from loops.browser.node import NodeView from loops.browser.node import NodeView
from loops.browser.concept import ConceptRelationView from loops.browser.concept import ConceptRelationView
@ -98,9 +99,10 @@ class ObjectForm(NodeView):
@Lazy @Lazy
def schema(self): def schema(self):
#ti = self.typeInterface or Interface #IConcept
ti = self.typeInterface or IConceptSchema
schemaFactory = component.getAdapter(self.adapted, ISchemaFactory) schemaFactory = component.getAdapter(self.adapted, ISchemaFactory)
return schemaFactory(self.typeInterface, manager=self, return schemaFactory(ti, manager=self, request=self.request)
request=self.request)
@Lazy @Lazy
def fields(self): def fields(self):
@ -186,11 +188,10 @@ class EditConceptForm(EditObjectForm):
title = _(u'Edit Concept') title = _(u'Edit Concept')
form_action = 'edit_concept' form_action = 'edit_concept'
isInnerHtml = False
@Lazy @Lazy
def typeInterface(self): def typeInterface(self):
return IType(self.target).typeInterface or IConcept return IType(self.target).typeInterface or IConceptSchema
@property @property
def assignments(self): def assignments(self):
@ -224,13 +225,11 @@ class CreateObjectForm(ObjectForm):
t = self.loopsRoot.loopsTraverse(typeToken) t = self.loopsRoot.loopsTraverse(typeToken)
return removeSecurityProxy(ITypeConcept(t).typeInterface) return removeSecurityProxy(ITypeConcept(t).typeInterface)
else: else:
#return INote
return ITextDocument return ITextDocument
@property @property
def assignments(self): def assignments(self):
target = self.virtualTargetObject target = self.virtualTargetObject
#target = self.target
if (IConcept.providedBy(target) and if (IConcept.providedBy(target) and
target.conceptType != target.conceptType !=
self.loopsRoot.getConceptManager().getTypeConcept()): self.loopsRoot.getConceptManager().getTypeConcept()):
@ -239,10 +238,58 @@ class CreateObjectForm(ObjectForm):
return () 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): class InnerForm(CreateObjectForm):
@property @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 # processing form input
@ -296,12 +343,6 @@ class EditObject(FormController):
self.object = obj self.object = obj
formState = self.updateFields() formState = self.updateFields()
# TODO: error handling # 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') self.request.response.redirect(self.view.virtualTargetUrl + '?version=this')
return False return False
@ -342,8 +383,10 @@ class EditObject(FormController):
return dict(fileupload=self.handleFileUpload) return dict(fileupload=self.handleFileUpload)
def collectConcepts(self, fieldName, value): def collectConcepts(self, fieldName, value):
if self.old is None: self.old = [] if self.old is None:
if self.selected is None: self.selected = [] self.old = []
if self.selected is None:
self.selected = []
for v in value: for v in value:
if fieldName == 'old': if fieldName == 'old':
self.old.append(v) self.old.append(v)
@ -356,15 +399,24 @@ class EditObject(FormController):
c, p = v.split(':') c, p = v.split(':')
concept = util.getObjectForUid(c) concept = util.getObjectForUid(c)
predicate = util.getObjectForUid(p) predicate = util.getObjectForUid(p)
obj.deassignConcept(concept, [predicate]) self.deassignConcept(obj, concept, [predicate])
for v in self.selected: for v in self.selected:
if v != 'none' and v not in self.old: if v != 'none' and v not in self.old:
c, p = v.split(':') c, p = v.split(':')
concept = util.getObjectForUid(c) concept = util.getObjectForUid(c)
predicate = util.getObjectForUid(p) predicate = util.getObjectForUid(p)
exists = obj.getConceptRelations(predicates=[p], concept=concept) exists = self.getConceptRelations(obj, [p], concept)
if not exists: 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): def checkCreateVersion(self, obj):
form = self.request.form form = self.request.form
@ -379,14 +431,15 @@ class EditObject(FormController):
class CreateObject(EditObject): class CreateObject(EditObject):
def update(self): factory = Resource
form = self.request.form defaultTypeToken = '.loops/concepts/textdocument'
container = self.loopsRoot.getResourceManager()
title = form.get('title') @Lazy
if not title: def container(self):
raise BadRequest('Title field is empty') return self.loopsRoot.getResourceManager()
obj = Resource(title)
data = form.get('data') def getNameFromData(self):
data = self.request.form.get('data')
if data and isinstance(data, FileUpload): if data and isinstance(data, FileUpload):
name = getattr(data, 'filename', None) name = getattr(data, 'filename', None)
# strip path from IE uploads: # strip path from IE uploads:
@ -394,11 +447,21 @@ class CreateObject(EditObject):
name = name.rsplit('\\', 1)[-1] name = name.rsplit('\\', 1)[-1]
else: else:
name = None 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 # TODO: validate fields
name = INameChooser(container).chooseName(name, obj) name = INameChooser(container).chooseName(name, obj)
container[name] = obj container[name] = obj
tc = form.get('form.type') or '.loops/concepts/note' tc = form.get('form.type') or self.defaultTypeToken
obj.resourceType = self.loopsRoot.loopsTraverse(tc) obj.setType(self.loopsRoot.loopsTraverse(tc))
notify(ObjectCreatedEvent(obj)) notify(ObjectCreatedEvent(obj))
self.object = obj self.object = obj
self.updateFields() # TODO: suppress validation self.updateFields() # TODO: suppress validation
@ -406,3 +469,38 @@ class CreateObject(EditObject):
self.request.response.redirect(self.view.request.URL) self.request.response.redirect(self.view.request.URL)
return False 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"> <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" <input type="hidden" name="form.action" value="create"
tal:attributes="value view/form_action" /> tal:attributes="value view/form_action" />
<table cellpadding="3" class="form"> <table cellpadding="3" class="form">
@ -47,10 +49,12 @@
tal:attributes="onChange tal:attributes="onChange
string:return replaceFieldsNode( string:return replaceFieldsNode(
'form.fields', 'form.type', 'form.fields', 'form.type',
'${view/url}/inner_form.html')"> '${view/url}/$innerForm')">
<option value=".loops/concepts/note" <option value=".loops/concepts/note"
i18n:translate="" i18n:translate=""
tal:repeat="type view/resourceTypes" xtal:repeat="type view/resourceTypes"
tal:repeat="type python:
view.getTypesVocabulary((qualifier,))"
tal:content="type/title" tal:content="type/title"
tal:attributes="value type/token; tal:attributes="value type/token;
selected python: selected python:

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -29,8 +29,10 @@ from zope.i18nmessageid import MessageFactory
from zope.security.proxy import removeSecurityProxy from zope.security.proxy import removeSecurityProxy
from cybertools.knowledge.interfaces import IKnowing, IRequirementProfile 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 cybertools.organize.interfaces import ITask as IBaseTask
from loops.interfaces import IConceptSchema
from loops.organize.interfaces import IPerson as IBasePerson
_ = MessageFactory('zope') _ = MessageFactory('zope')
@ -45,3 +47,9 @@ class IPerson(IBasePerson, IKnowing):
class ITask(IBaseTask, IRequirementProfile): class ITask(IBaseTask, IRequirementProfile):
""" A task, also acting as a knowledge requirement profile. """ 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.interfaces import IKnowledgeElement, IKnowledgeProvider
from cybertools.knowledge.knowing import Knowing from cybertools.knowledge.knowing import Knowing
from loops.interfaces import IConcept, IResource 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.party import Person as BasePerson
from loops.organize.task import Task as BaseTask from loops.organize.task import Task as BaseTask
from loops.common import AdapterBase from loops.common import AdapterBase
@ -42,7 +42,7 @@ from loops.type import TypeInterfaceSourceList
# register type interfaces - (TODO: use a function for this) # register type interfaces - (TODO: use a function for this)
TypeInterfaceSourceList.typeInterfaces += (IPerson, IKnowledgeElement, TypeInterfaceSourceList.typeInterfaces += (IPerson, ITopic, IKnowledgeElement,
ITask, IKnowledgeProvider) ITask, IKnowledgeProvider)
@ -99,7 +99,7 @@ class Topic(AdapterBase, KnowledgeAdapterMixin):
may act as a knowledge element. may act as a knowledge element.
""" """
implements(IKnowledgeElement) implements(ITopic)
_adapterAttributes = ('context', '__parent__', 'parent') _adapterAttributes = ('context', '__parent__', 'parent')
def getParent(self): def getParent(self):

View file

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