loops - work in progress: assign concepts via dojo.Dialog

git-svn-id: svn://svn.cy55.de/Zope3/src/loops/trunk@1379 fd906abe-77d9-0310-91a1-e0d9ade77398
This commit is contained in:
helmutm 2006-10-02 08:22:05 +00:00
parent 94a14aac45
commit 1437934070
13 changed files with 149 additions and 65 deletions

View file

@ -618,8 +618,11 @@ End-user Forms
The browser.form and related modules provide additional support for forms The browser.form and related modules provide additional support for forms
that are shown in the end-user interface. that are shown in the end-user interface.
Creating an object
------------------
>>> from loops.browser.form import CreateObjectForm, CreateObject, ResourceNameChooser >>> from loops.browser.form import CreateObjectForm, CreateObject, ResourceNameChooser
>>> form = CreateObjectForm(m112, TestRequest) >>> form = CreateObjectForm(m112, TestRequest())
>>> from loops.interfaces import INote, ITypeConcept >>> from loops.interfaces import INote, ITypeConcept
>>> from loops.type import TypeConcept >>> from loops.type import TypeConcept
@ -631,8 +634,8 @@ that are shown in the end-user interface.
>>> ITypeConcept(note_tc).typeInterface = INote >>> ITypeConcept(note_tc).typeInterface = INote
>>> component.provideAdapter(ResourceNameChooser) >>> component.provideAdapter(ResourceNameChooser)
>>> request = TestRequest(form={'form.title': 'Test Note', >>> request = TestRequest(form={'form.title': u'Test Note',
... 'form.type': '.loops/concepts/note'}) ... 'form.type': u'.loops/concepts/note'})
>>> view = NodeView(m112, request) >>> view = NodeView(m112, request)
>>> cont = CreateObject(view, request) >>> cont = CreateObject(view, request)
>>> cont.update() >>> cont.update()
@ -640,15 +643,15 @@ that are shown in the end-user interface.
>>> sorted(resources.keys()) >>> sorted(resources.keys())
[...u'test_note'...] [...u'test_note'...]
>>> resources['test_note'].title >>> resources['test_note'].title
'Test Note' u'Test Note'
If there is a concept selected in the combo box we assign this to the newly If there is a concept selected in the combo box we assign this to the newly
created object: created object:
>>> from loops import util >>> from loops import util
>>> topicUid = util.getUidForObject(topic) >>> topicUid = util.getUidForObject(topic)
>>> request = TestRequest(form={'form.title': 'Test Note', >>> request = TestRequest(form={'form.title': u'Test Note',
... 'form.type': '.loops/concepts/note', ... 'form.type': u'.loops/concepts/note',
... 'form.concept.search.text_selected': str(topicUid)}) ... 'form.concept.search.text_selected': str(topicUid)})
>>> view = NodeView(m112, request) >>> view = NodeView(m112, request)
>>> cont = CreateObject(view, request) >>> cont = CreateObject(view, request)
@ -660,6 +663,21 @@ created object:
>>> sorted(t.__name__ for t in note.getConcepts()) >>> sorted(t.__name__ for t in note.getConcepts())
[u'note', u'topic'] [u'note', u'topic']
Editing an object
-----------------
>>> from loops.browser.form import EditObjectForm, EditObject
>>> m112.target = resources['test_note']
>>> form = EditObjectForm(m112, TestRequest())
>>> request = TestRequest(form={'form.title': u'Test Note - changed'})
>>> view = NodeView(m112, request)
>>> cont = EditObject(view, request)
>>> cont.update()
True
>>> resources['test_note'].title
u'Test Note - changed'
Import/Export Import/Export
============= =============

View file

@ -37,8 +37,9 @@ from zope.publisher.interfaces import BadRequest
from cybertools.ajax import innerHtml from cybertools.ajax import innerHtml
from cybertools.browser.form import FormController from cybertools.browser.form import FormController
from cybertools.typology.interfaces import IType from cybertools.typology.interfaces import IType
from loops.interfaces import IResourceManager, INote from loops.interfaces import IResourceManager, INote, IDocument
from loops.browser.node import NodeView from loops.browser.node import NodeView
from loops.browser.concept import ConceptRelationView
from loops.resource import Resource from loops.resource import Resource
from loops.type import ITypeConcept from loops.type import ITypeConcept
from loops import util from loops import util
@ -54,6 +55,7 @@ class ObjectForm(NodeView):
def setUp(self): def setUp(self):
self.setUpWidgets() self.setUpWidgets()
# TODO: such stuff should depend on self.typeInterface
self.widgets['data'].height = 3 self.widgets['data'].height = 3
def __call__(self): def __call__(self):
@ -67,6 +69,7 @@ class CreateObjectForm(ObjectForm, Form):
title = _(u'Create Resource, Type = ') title = _(u'Create Resource, Type = ')
form_action = 'create_resource' form_action = 'create_resource'
dialog_name = 'create'
@property @property
def form_fields(self): def form_fields(self):
@ -76,9 +79,16 @@ class CreateObjectForm(ObjectForm, Form):
ifc = ITypeConcept(t).typeInterface ifc = ITypeConcept(t).typeInterface
else: else:
ifc = INote ifc = INote
self.typeInterface = ifc
return FormFields(ifc) return FormFields(ifc)
class InnerForm(CreateObjectForm):
@property
def macro(self): return self.template.macros['fields']
class EditObjectForm(ObjectForm, EditForm): class EditObjectForm(ObjectForm, EditForm):
@property @property
@ -86,26 +96,26 @@ class EditObjectForm(ObjectForm, EditForm):
title = _(u'Edit Resource') title = _(u'Edit Resource')
form_action = 'edit_resource' form_action = 'edit_resource'
dialog_name = 'edit'
@Lazy @Lazy
def typeInterface(self): def typeInterface(self):
return IType(self.context).typeInterface return IType(self.context).typeInterface or IDocument
@property @property
def form_fields(self): def form_fields(self):
return FormFields(self.typeInterface) return FormFields(self.typeInterface)
@property
def assignments(self):
for c in self.context.getConceptRelations():
yield ConceptRelationView(c, self.request)
def __init__(self, context, request): def __init__(self, context, request):
super(EditObjectForm, self).__init__(context, request) super(EditObjectForm, self).__init__(context, request)
self.context = self.virtualTargetObject self.context = self.virtualTargetObject
class InnerForm(ObjectForm):
@property
def macro(self): return self.template.macros['fields']
class CreateObject(FormController): class CreateObject(FormController):
@Lazy @Lazy

View file

@ -5,7 +5,7 @@
<table cellpadding="3" class="form"> <table cellpadding="3" class="form">
<tbody> <tbody>
<tr> <tr>
<th colspan="4"> <th colspan="5">
<br /> <br />
<span tal:replace="view/title" <span tal:replace="view/title"
i18n:translate="">Edit Information Object i18n:translate="">Edit Information Object
@ -20,8 +20,9 @@
<tbody> <tbody>
<tr> <tr>
<td colspan="4" class="headline">Assign Concept(s)</td> <td colspan="5" class="headline">Concept Assignments</td>
</tr> </tr>
<tr metal:use-macro="view/template/macros/assignments" />
<tr metal:use-macro="view/template/macros/search_concepts" /> <tr metal:use-macro="view/template/macros/search_concepts" />
<tr metal:use-macro="view/template/macros/buttons" /> <tr metal:use-macro="view/template/macros/buttons" />
</tbody> </tbody>
@ -37,7 +38,7 @@
<table cellpadding="3" class="form"> <table cellpadding="3" class="form">
<tbody> <tbody>
<tr> <tr>
<th colspan="4"> <th colspan="5">
<br /> <br />
<span tal:replace="view/title" <span tal:replace="view/title"
i18n:translate="">Create Information Object</span> i18n:translate="">Create Information Object</span>
@ -65,7 +66,7 @@
<tbody> <tbody>
<tr> <tr>
<td colspan="4" class="headline">Assign Concept(s)</td> <td colspan="5" class="headline">Assign Concept(s)</td>
</tr> </tr>
<tr metal:use-macro="view/template/macros/search_concepts" /> <tr metal:use-macro="view/template/macros/search_concepts" />
<tr metal:use-macro="view/template/macros/buttons" /> <tr metal:use-macro="view/template/macros/buttons" />
@ -86,7 +87,7 @@
tal:content="widget/label">label</span> tal:content="widget/label">label</span>
</label> </label>
</td> </td>
<td class="field" colspan="3" <td class="field" colspan="4"
tal:define="hint widget/hint"> tal:define="hint widget/hint">
<div class="widget" tal:content="structure widget"> <div class="widget" tal:content="structure widget">
<input type="text" /> <input type="text" />
@ -101,6 +102,21 @@
</metal:fields> </metal:fields>
<metal:assignments define-macro="assignments">
<tbody id="form.assignments">
<tr tal:repeat="relation view/assignments">
<td colspan="5">
<input type="hidden" name="form.assignments.tokens:list"
tal:attributes="value relation/token" />
<input type="checkbox" checked />
<span tal:content="relation/title">Something</span>
(<span tal:content="relation/typeTitle">Topic</span>)
</td>
</tr>
</tbody>
</metal:assignments>
<tr metal:define-macro="search_concepts"> <tr metal:define-macro="search_concepts">
<td><label for="form.concept.search.type">Type:</label></td> <td><label for="form.concept.search.type">Type:</label></td>
<td> <td>
@ -123,14 +139,19 @@
tal:attributes="dataUrl tal:attributes="dataUrl
string:${context/@@absolute_url}/listConceptsForComboBox.js?searchString=%{searchString}&searchType=" /> string:${context/@@absolute_url}/listConceptsForComboBox.js?searchString=%{searchString}&searchType=" />
</td> </td>
<td>
<input type="button" value="Select"
onClick="addConceptAssignment()" />
</td>
</tr> </tr>
<tr metal:define-macro="buttons"> <tr metal:define-macro="buttons">
<td colspan="4"> <td colspan="5"
<input type="button" value="Cancel" tal:define="dlgName view/dialog_name">
onclick="objectDlg.hide()"> <input type="button" value="Cancel" onClick="dlg.hide();"
<input type="submit" value="Save" tal:attributes="onClick string:dialogs['$dlgName'].hide()">
onclick="objectDlg.hide()"> <input type="submit" value="Save" onClick="dlg.hide()"
tal:attributes="onClick string:dialogs['$dlgName'].hide()">
</td> </td>
</tr> </tr>

View file

@ -86,9 +86,9 @@ div.image {
/* dojo stuff */ /* dojo stuff */
.dojoComboBox { /*.dojoComboBox {
width: 200px; width: 200px;
} }*/
.dojoDialog { .dojoDialog {
background: #eee; background: #eee;

View file

@ -33,13 +33,15 @@ function inlineEdit(id, saveUrl) {
//dojo.require('dojo.widget.Editor'); //dojo.require('dojo.widget.Editor');
var iconNode = dojo.byId('inlineedit_icon'); var iconNode = dojo.byId('inlineedit_icon');
iconNode.style.visibility = 'hidden'; iconNode.style.visibility = 'hidden';
var editor = dojo.widget.fromScript('Editor', //var editor = dojo.widget.fromScript('Editor',
editor = dojo.widget.createWidget('Editor',
{items: ['save', '|', 'formatblock', '|', {items: ['save', '|', 'formatblock', '|',
'insertunorderedlist', 'insertorderedlist', '|', 'insertunorderedlist', 'insertorderedlist', '|',
'bold', 'italic', '|', 'createLink', 'insertimage'], 'bold', 'italic', '|', 'createLink', 'insertimage'],
saveUrl: saveUrl, saveUrl: saveUrl,
closeOnSave: true, closeOnSave: true,
htmlEditing: true, htmlEditing: true,
//onClose: function() {
onSave: function() { onSave: function() {
this.disableToolbar(true); this.disableToolbar(true);
iconNode.style.visibility = 'visible'; iconNode.style.visibility = 'visible';
@ -57,17 +59,34 @@ function setConceptTypeForComboBox(typeId, cbId) {
dp.searchUrl = newUrl; dp.searchUrl = newUrl;
} }
var objectDlg = false; var dialogs = {}
function objectDialog(url) { function objectDialog(dlgName, url) {
dojo.require('dojo.widget.Dialog'); dojo.require('dojo.widget.Dialog');
dojo.require('dojo.widget.ComboBox'); dojo.require('dojo.widget.ComboBox');
if (!objectDlg) { dlg = dialogs[dlgName];
objectDlg = dojo.widget.fromScript('Dialog', if (!dlg) {
//dlg = dojo.widget.fromScript('Dialog',
dlg = dojo.widget.createWidget('Dialog',
{bgColor: 'white', bgOpacity: 0.5, toggle: 'fade', toggleDuration: 250, {bgColor: 'white', bgOpacity: 0.5, toggle: 'fade', toggleDuration: 250,
executeScripts: true, executeScripts: true,
href: url href: url
}, dojo.byId('objectDialog')); }, dojo.byId('dialog.' + dlgName));
dialogs[dlgName] = dlg;
} }
objectDlg.show(); dlg.show();
} }
function addConceptAssignment() {
node = dojo.byId('form.assignments');
token = document.getElementsByName('form.concept.search.text_selected')[0].value;
if (token.length == 0) {return false;}
title = document.getElementsByName('form.concept.search.text')[0].value;
var td = document.createElement('td');
td.setAttribute('colspan', '5');
td.innerHTML = '<input type="hidden" name="form.assignments.tokens:list" value="' + token + '" /><input type="checkbox" checked /><span>' + title + '</span>';
var tr = document.createElement('tr');
tr.appendChild(td);
node.appendChild(tr);
}

View file

@ -291,6 +291,10 @@ class NodeView(BaseView):
if target is not None: if target is not None:
return BaseView(target, self.request).url return BaseView(target, self.request).url
@Lazy
def hasEditableTarget(self):
return IResource.providedBy(self.virtualTargetObject)
@Lazy @Lazy
def inlineEditable(self): def inlineEditable(self):
target = self.virtualTarget target = self.virtualTarget

View file

@ -26,24 +26,26 @@
<div tal:define="target nocall:item/target" <div tal:define="target nocall:item/target"
tal:condition="nocall:target"> tal:condition="nocall:target">
<tal:ignore condition="nothing"> <tal:ignore condition="nothing">
<metal:editicons define-macro="editicons"> <div metal:define-macro="editicons"
<div class="subcolumn" id="xedit_icon" style="float: right; border: 1px solid #ccc;
margin-top: 1em">
<span id="xedit_icon"
tal:condition="item/xeditable | nothing"> tal:condition="item/xeditable | nothing">
<a href="#" title="Edit" style="padding: 5px" <a href="#" title="Edit" style="padding: 5px"
tal:attributes="href string:${item/realTargetUrl}/external_edit; tal:attributes="href string:${item/realTargetUrl}/external_edit;
title string:Edit '${target/title}' with External Editor"><img title string:Edit '${target/title}' with External Editor"><img
src="edit.gif" alt="Edit" src="edit.gif" alt="Edit"
tal:attributes="src context/++resource++edit.gif" /></a> tal:attributes="src context/++resource++edit.gif" /></a>
</div> </span>
<div class="subcolumn" id="inlineedit_icon" <span id="inlineedit_icon"
tal:condition="item/inlineEditable"> tal:condition="item/inlineEditable">
<a href="#" title="Edit" style="padding: 5px" <a href="#" title="Edit" style="padding: 5px"
tal:attributes="title string:Edit '${target/title}' with Inline Editor; tal:attributes="title string:Edit '${target/title}' with Inline Editor;
onclick python: item.inlineEdit(id)"><img onclick python: item.inlineEdit(id)"><img
src="edit.gif" alt="Edit" src="edit.gif" alt="Edit"
tal:attributes="src context/++resource++edit.gif" /></a> tal:attributes="src context/++resource++edit.gif" /></a>
</div> </span>
</metal:editicons> </div>
</tal:ignore> </tal:ignore>
<div class="content-1 subcolumn" id="1.body" <div class="content-1 subcolumn" id="1.body"
tal:define="node nocall:item; tal:define="node nocall:item;
@ -204,18 +206,23 @@
<metal:actions define-macro="actions"> <metal:actions define-macro="actions">
<div class="menu-2" <tal:actions define="dummy view/registerDojo">
tal:define="dummy view/registerDojo"> <div class="menu-2">
<a href="#" <a href="#"
onclick="objectDialog('create_object.html'); return false;"> onclick="objectDialog('create', 'create_object.html'); return false;">
Create Resource... Create Resource...
</a><br /> </a>
<a href="#" </div>
onclick="objectDialog('edit_object.html'); return false;"> <div class="menu-2"
Edit Resource... tal:condition="view/hasEditableTarget">
</a> <a href="#"
</div> onclick="objectDialog('edit', 'edit_object.html'); return false;">
<span id="objectDialog"></span> Edit Resource...
</a>
</div>
</tal:actions>
<span id="dialog.create"></span>
<span id="dialog.edit"></span>
</metal:actions> </metal:actions>

View file

@ -122,6 +122,11 @@ class ResourceView(BaseView):
for r in self.context.getConceptRelations(): for r in self.context.getConceptRelations():
yield ConceptRelationView(r, self.request) yield ConceptRelationView(r, self.request)
def relatedConcepts(self):
for c in self.concepts():
if c.isProtected: continue
yield c
def clients(self): def clients(self):
for node in self.context.getClients(): for node in self.context.getClients():
yield NodeView(node, self.request) yield NodeView(node, self.request)

View file

@ -1,12 +1,12 @@
<metal:block define-macro="render"> <metal:block define-macro="render">
<div tal:attributes="ondblclick python: item.openEditWindow('configure.html')"> <div tal:attributes="ondblclick python: item.openEditWindow('configure.html')">
<h1 tal:content="item/title">Title</h1>
<tal:body define="itemNum view/itemNum; <tal:body define="itemNum view/itemNum;
id string:$itemNum.body;"> id string:$itemNum.body;">
<tal:edit define="target nocall:item; <tal:edit define="target nocall:item;
item nocall:node|nocall:view;"> item nocall:node|nocall:view;">
<div metal:use-macro="views/node_macros/editicons" /> <div metal:use-macro="views/node_macros/editicons" />
</tal:edit> </tal:edit>
<h1 tal:content="item/title">Title</h1>
<div class="content-1" id="1.body" <div class="content-1" id="1.body"
tal:attributes="id id;" tal:attributes="id id;"
tal:content="structure item/render"> tal:content="structure item/render">
@ -66,11 +66,11 @@
<metal:actions define-macro="related"> <metal:actions define-macro="related">
<div class="menu-2" <div class="menu-2"
tal:repeat="concept macro/info/concepts"> tal:repeat="concept macro/info/relatedConcepts">
<a href="#" <a href="#"
tal:content="concept/title"
tal:attributes="href string:${view/url}/.target${concept/uniqueId}"> tal:attributes="href string:${view/url}/.target${concept/uniqueId}">
Concept <span tal:replace="concept/title">Concept</span>
(<i tal:content="concept/typeTitle">Type</i>)
</a> </a>
</div> </div>
</metal:actions> </metal:actions>

View file

@ -46,7 +46,7 @@ from cybertools.typology.interfaces import ITypeManager
from interfaces import IBaseResource, IResource from interfaces import IBaseResource, IResource
from interfaces import IFile, INote from interfaces import IFile, INote
from interfaces import IDocument, IDocumentSchema, IDocumentView from interfaces import IDocument, ITextDocument, IDocumentSchema, IDocumentView
from interfaces import IMediaAsset, IMediaAssetView from interfaces import IMediaAsset, IMediaAssetView
from interfaces import IResourceManager, IResourceManagerContained from interfaces import IResourceManager, IResourceManagerContained
from interfaces import ILoopsContained from interfaces import ILoopsContained
@ -222,7 +222,7 @@ class DocumentWriteFileAdapter(object):
self.context = context self.context = context
def write(self, data): def write(self, data):
self.context.data = unicode(data.replace('\r', ''), 'UTF-8') ITextDocument(self.context).data = unicode(data.replace('\r', ''), 'UTF-8')
notify(ObjectModifiedEvent(self.context, Attributes(IDocument, 'data'))) notify(ObjectModifiedEvent(self.context, Attributes(IDocument, 'data')))

View file

@ -1,4 +1,4 @@
=================================================================== 4===================================================================
loops.search - Provide search functionality for the loops framework loops.search - Provide search functionality for the loops framework
=================================================================== ===================================================================
@ -198,7 +198,7 @@ of the concepts' titles:
>>> request = TestRequest(form=form) >>> request = TestRequest(form=form)
>>> view = Search(page, request) >>> view = Search(page, request)
>>> view.listConcepts() >>> view.listConcepts()
"[['Zope', '23']]" "[['Zope (Topic)', '23']]"
TODO - more to come... TODO - more to come...

View file

@ -80,7 +80,7 @@ class Search(BaseView):
result = ConceptQuery(self).query(title=title, type=type) result = ConceptQuery(self).query(title=title, type=type)
registry = component.getUtility(IRelationRegistry) registry = component.getUtility(IRelationRegistry)
# simple way to provide JSON format: # simple way to provide JSON format:
return str(sorted([[`o.title`[2:-1], return str(sorted([[`o.title`[2:-1] + ' (%s)' % `o.conceptType.title`[2:-1],
`registry.getUniqueIdForObject(o)`] `registry.getUniqueIdForObject(o)`]
for o in result for o in result
if o.getLoopsRoot() == self.loopsRoot])).replace('\\\\x', '\\x') if o.getLoopsRoot() == self.loopsRoot])).replace('\\\\x', '\\x')

View file

@ -28,7 +28,7 @@
</tr> </tr>
<tr> <tr>
<td></td> <td></td>
<td colspan="2"> <td colspan="3">
<input type="submit" name="button.search" value="Search" class="submit" <input type="submit" name="button.search" value="Search" class="submit"
tal:attributes="onclick python: tal:attributes="onclick python:
item.submitReplacing(resultsId, formId, view)" /> item.submitReplacing(resultsId, formId, view)" />
@ -83,7 +83,7 @@
<metal:text define-macro="type"> <metal:text define-macro="type">
<tr> <tr>
<td metal:use-macro="macros/minus"/> <td metal:use-macro="macros/minus"/>
<td colspan="2"> <td colspan="3">
<h3>Type(s) to search for</h3> <h3>Type(s) to search for</h3>
</td> </td>
<tr> <tr>
@ -105,7 +105,7 @@
title="Add type" title="Add type"
tal:condition="nothing" />&nbsp; tal:condition="nothing" />&nbsp;
</td> </td>
<td></td> <td colspan="2"></td>
</tr> </tr>
</metal:text> </metal:text>
@ -113,7 +113,7 @@
<metal:text define-macro="text"> <metal:text define-macro="text">
<tr> <tr>
<td metal:use-macro="macros/minus"/> <td metal:use-macro="macros/minus"/>
<td colspan="2"> <td colspan="3">
<h3>Search text</h3> <h3>Search text</h3>
</td> </td>
<tr> <tr>
@ -151,7 +151,7 @@
<metal:text define-macro="concept"> <metal:text define-macro="concept">
<tr> <tr>
<td metal:use-macro="macros/minus"/> <td metal:use-macro="macros/minus"/>
<td colspan="2"> <td colspan="3">
<h3>Search via related concepts</h3> <h3>Search via related concepts</h3>
</td> </td>
<tr> <tr>