work in progress: search

git-svn-id: svn://svn.cy55.de/Zope3/src/loops/trunk@1294 fd906abe-77d9-0310-91a1-e0d9ade77398
This commit is contained in:
helmutm 2006-08-08 19:00:23 +00:00
parent b4131bfcaf
commit 3bd8e181be
10 changed files with 231 additions and 44 deletions

View file

@ -40,6 +40,7 @@ from zope.schema.vocabulary import SimpleTerm
from zope.security import canAccess, canWrite from zope.security import canAccess, canWrite
from zope.security.proxy import removeSecurityProxy from zope.security.proxy import removeSecurityProxy
from cybertools.relation.interfaces import IRelationRegistry
from cybertools.typology.interfaces import IType from cybertools.typology.interfaces import IType
from loops.interfaces import IView from loops.interfaces import IView
from loops import util from loops import util
@ -167,7 +168,8 @@ class BaseView(object):
@Lazy @Lazy
def uniqueId(self): def uniqueId(self):
return zapi.getUtility(IIntIds).getId(self.context) return zapi.getUtility(IRelationRegistry).getUniqueIdForObject(self.context)
#return zapi.getUtility(IIntIds).getId(self.context)
@Lazy @Lazy
def editable(self): def editable(self):

View file

@ -74,3 +74,13 @@ div.image {
margin-right: 5px; margin-right: 5px;
} }
/* search stuff */
.searchForm input.button, input.submit {
padding: 2px;
}
.searchForm input.submit {
font-weight: bold;
}

View file

@ -5,9 +5,19 @@ function openEditWindow(url) {
zmi.focus(); zmi.focus();
return false; return false;
} }
function focusOpener() { function focusOpener() {
if (typeof(opener) != 'undefined' && opener != null) { if (typeof(opener) != 'undefined' && opener != null) {
opener.location.reload(); opener.location.reload();
opener.focus(); opener.focus();
} }
} }
function submitReplacing(targetId, formId, actionUrl) {
dojo.io.updateNode(targetId, {
url: actionUrl,
formNode: dojo.byId(formId),
method: 'post'
});
return false;
}

View file

@ -8,7 +8,9 @@
tal:define="dummy python: tal:define="dummy python:
controller.macros.register('css', resourceName='loops.css', media='all'); controller.macros.register('css', resourceName='loops.css', media='all');
dummy python: dummy python:
controller.macros.register('js', resourceName='loops.js');" /> controller.macros.register('js', resourceName='loops.js');
dummy python:
controller.macros.register('js', resourceName='ajax.dojo/dojo.js');" />
<metal:block fill-slot="actions" /> <metal:block fill-slot="actions" />

View file

@ -122,6 +122,13 @@ class NodeView(BaseView):
basicView._viewName = self.context.viewName basicView._viewName = self.context.viewName
return basicView.view return basicView.view
@Lazy
def targetUrl(self):
t = self.target
if t is not None:
return '%s/.target%s' % (self.url, t.uniqueId)
return ''
def renderTarget(self): def renderTarget(self):
target = self.target target = self.target
return target is not None and target.render() or u'' return target is not None and target.render() or u''

View file

@ -291,8 +291,6 @@
<adapter factory="loops.type.ConceptType" /> <adapter factory="loops.type.ConceptType" />
<adapter factory="loops.type.ResourceType" <adapter factory="loops.type.ResourceType"
for="loops.interfaces.IDocument" /> for="loops.interfaces.IDocument" />
<!--<adapter factory="loops.type.ResourceType"
for="loops.interfaces.IMediaAsset" />-->
<adapter factory="loops.type.LoopsTypeManager" /> <adapter factory="loops.type.LoopsTypeManager" />
<adapter factory="loops.type.TypeConcept" trusted="True" /> <adapter factory="loops.type.TypeConcept" trusted="True" />

View file

@ -4,3 +4,76 @@ loops.search - Provide search functionality for the loops framework
($Id$) ($Id$)
Let's do some basic set up
>>> from zope.app.testing.setup import placefulSetUp, placefulTearDown
>>> site = placefulSetUp(True)
>>> from zope import component, interface
and setup a simple loops site with a concept manager and some concepts
(with all the type machinery, what in real life is done via standard
ZCML setup):
>>> from cybertools.relation.interfaces import IRelationRegistry
>>> from cybertools.relation.registry import DummyRelationRegistry
>>> relations = DummyRelationRegistry()
>>> component.provideUtility(relations, IRelationRegistry)
>>> from loops.type import ConceptType, TypeConcept
>>> from loops.interfaces import ITypeConcept
>>> component.provideAdapter(ConceptType)
>>> component.provideAdapter(TypeConcept)
>>> from loops import Loops
>>> loopsRoot = site['loops'] = Loops()
>>> from loops.setup import SetupManager
>>> setup = SetupManager(loopsRoot)
>>> concepts, resources, views = setup.setup()
>>> typeConcept = concepts['type']
>>> from loops.concept import Concept
>>> query = concepts['query'] = Concept(u'Query')
>>> topic = concepts['topic'] = Concept(u'Topic')
>>> for c in (query, topic): c.conceptType = typeConcept
In addition we create a concept that holds the search page and a node
(page) that links to this concept:
>>> search = concepts['search'] = Concept(u'Search')
>>> search.conceptType = query
>>> from loops.view import Node
>>> page = views['page'] = Node('Search Page')
>>> page.target = search
Now we are ready to create a search view object:
>>> from zope.publisher.browser import TestRequest
>>> from loops.search.browser import Search
>>> searchView = Search(search, TestRequest())
The search view provides values for identifying the search form itself
and the parameter rows; the rowNum is auto-incremented, so it should be
accessed exactly once per row:
>>> searchView.itemNum
1
>>> searchView.rowNum
1
>>> searchView.rowNum
2
To execute the search in the context of a node we have to set up a node
view on our page. The submitReplacing method returns a JavaScript call
that will replace the results part on the search page:
>>> from loops.browser.node import NodeView
>>> pageView = NodeView(page, TestRequest())
>>> searchView.submitReplacing('1.results', '1.search.form', pageView)
'return submitReplacing("1.results", "1.search.form",
"http://127.0.0.1/loops/views/page/.target19/@@searchresults.html")'

View file

@ -42,7 +42,42 @@ class Search(BaseView):
template = NamedTemplate('loops.search_macros') template = NamedTemplate('loops.search_macros')
maxRowNum = 0
@Lazy @Lazy
def macro(self): def macro(self):
return self.template.macros['search'] return self.template.macros['search']
@Lazy
def itemNum(self):
""" Return a number identifying the item (the current search form)
on the page.
"""
return self.request.get('loops.itemNum', 1)
@property
def rowNum(self):
""" Return the rowNum to be used for identifying the current row.
"""
n = self.request.get('loops.rowNum', 0)
if n: # if given directly we don't use the calculation
return n
n = (self.maxRowNum or self.request.get('loops.maxRowNum', 0)) + 1
self.maxRowNum = n
return n
def submitReplacing(self, targetId, formId, view):
return 'return submitReplacing("%s", "%s", "%s")' % (
targetId, formId,
'%s/.target%s/@@searchresults.html' % (view.url, self.uniqueId))
class SearchResults(BaseView):
innerHtml_template = NamedTemplate('loops.search_macros')
innerHtml_macro = 'search_results'
template = NamedTemplate('ajax.inner.html')
def __call__(self):
return self.template(self)

View file

@ -24,5 +24,11 @@
permission="zope.View" permission="zope.View"
/> />
<browser:page
name="searchresults.html"
for="loops.interfaces.ILoopsObject"
class="loops.search.browser.SearchResults"
permission="zope.View"
/>
</configure> </configure>

View file

@ -1,24 +1,33 @@
<metal:search define-macro="search"> <metal:search define-macro="search">
<div id="search" <div id="search"
tal:define="template nocall:item/template"> tal:define="macros item/template/macros">
<h3 tal:attributes="class string:content-$level; <h3 tal:attributes="class string:content-$level;
ondblclick item/openEditWindow"> ondblclick item/openEditWindow">
Search Search
</h3> </h3>
<div metal:define-macro="search_form"> <div metal:define-macro="search_form" class="searchForm"
tal:define="idPrefix string:${item/itemNum}.search;
formId string:$idPrefix.form">
<fieldset class="box"> <fieldset class="box">
<form action="."> <form action="." method="post" id="1.search.form"
tal:attributes="id formId">
<table cellpadding="3"> <table cellpadding="3">
<tal:block define="search_selection string:type"> <tal:block repeat="search_selection python: ['type', 'text']">
<metal:row use-macro="template/macros/search_row" /> <metal:row use-macro="macros/search_row" />
</tal:block>
<tal:block define="search_selection string:text">
<metal:row use-macro="template/macros/search_row" />
</tal:block> </tal:block>
<tr> <tr>
<td colspan="2"> <td colspan="2">
<input type="button" name="search.add.1" value=" Add filter " /> <input type="button" value="Add concept filter" class="button" />
<input type="button" value="Add attribute filter" class="button" />
</td>
</tr>
<tr>
<td colspan="2">
<input type="submit" name="1.search" value="Search" class="submit"
tal:attributes="name string:$idPrefix.submit;
onclick python:
item.submitReplacing('1.results', formId, view)" />
</td> </td>
</tr> </tr>
</table> </table>
@ -26,9 +35,11 @@
</fieldset> </fieldset>
</div> </div>
<div metal:define-macro="search_results"> <div metal:define-macro="search_results"
id="1.results">
<fieldset class="box"> <fieldset class="box">
Search results Search results
<p tal:content="python:request.get('1.search.1.text', 'nothing')" />
</fieldset> </fieldset>
</div> </div>
@ -36,9 +47,70 @@
</metal:search> </metal:search>
<tr metal:define-macro="search_row" id="search.row.1.1"> <tr metal:define-macro="search_row" id="search.row.1.1"
tal:define="rowNum item/rowNum;
idPrefix string:$idPrefix.$rowNum;
selection search_selection | item/searchSelection"
tal:attributes="id string:$idPrefix.row">
<td style="width: 30px">
<input type="button" value="&minus;"
title="Remove search parameter"
tal:condition="python: selection not in ['type', 'text']" />&nbsp;
</td>
<td> <td>
<input type="button" value="&minus;" />&nbsp; <div metal:use-macro="macros/?selection" />
</td>
</tr>
<metal:text define-macro="type">
<div>
<h3>Type(s) to search for</h3>
<label for="text"
tal:attributes="for string:$idPrefix.text">Type:</label>
<select name="text"
tal:attributes="name string:$idPrefix.text;
id string:$idPrefix.text;">
<option value=".loops/concepts/topic">Topic</option>
<option value="loops.resource.Document">Document</option>
</select>
<input type="button" value="+"
title="Add type" />&nbsp;
</div>
</metal:text>
<metal:text define-macro="text">
<div>
<h3>Search text</h3>
<input type="checkbox" checked
name="title" id="title"
tal:attributes="name string:$idPrefix.title;
id string:$idPrefix.title;" />
<label for="title"
tal:attributes="for string:$idPrefix.title">Title</label>
<input type="checkbox"
name="full" id="full"
tal:attributes="name string:$idPrefix.full;
id string:$idPrefix.full;" />
<label for="full"
tal:attributes="for string:$idPrefix.full">Full text</label>&nbsp;&nbsp;
<label for="text"
tal:attributes="for string:$idPrefix.text">Search words:</label>
<input type="text" name="text"
tal:attributes="name string:$idPrefix.text;
id string:$idPrefix.text;" />
<input type="button" value="+"
title="Add search word" />&nbsp;
</div>
</metal:text>
<!-- obsolete -->
<tr metal:define-macro="search_row_selectable" id="search.row.1.1">
<td>
<input type="button" value="&minus;"
title="Remove search parameter" />&nbsp;
</td> </td>
<td tal:define="selection search_selection | string:type"> <td tal:define="selection search_selection | string:type">
<select> <select>
@ -54,31 +126,3 @@
</tr> </tr>
<metal:text define-macro="text">
<div id="search.text.1.1">
<input type="checkbox"
name="search.text.title.1.1" id="search.text.title.1.1" />
<label for="search.text.title.1.1">Title</label>
<input type="checkbox"
name="search.text.full.1.1" id="search.text.full.1.1" />
<label for="search.text.full.1.1">Full text</label>&nbsp;&nbsp;
<label for="search.text.text.1.1">Search words:</label>
<input type="text" name="search.text.text.1.1" />
<input type="text" name="search.text.text.1.1" />
<input type="button" name="search.text.add.1.1" value="+" />&nbsp;
</div>
</metal:text>
<metal:text define-macro="type">
<div id="search.type.1.2">
<label for="search.text.1.1">Type:</label>
<select type="text" name="search.type.text.1.2">
<option value=".loops/concepts/topic">Topic</option>
<option value="loops.resource.Document">Document</option>
</select>
<input type="button" name="search.type.add.1.2" value="+" />&nbsp;
</div>
</metal:text>