first working version of fulltext and title search

git-svn-id: svn://svn.cy55.de/Zope3/src/loops/trunk@1317 fd906abe-77d9-0310-91a1-e0d9ade77398
This commit is contained in:
helmutm 2006-08-19 15:42:29 +00:00
parent f8f9ee1fd8
commit 8ee75bbf77
9 changed files with 144 additions and 51 deletions

View file

@ -25,12 +25,10 @@ $Id$
from zope.app import zapi
from zope.app.dublincore.interfaces import IZopeDublinCore
from zope.app.form.browser.interfaces import ITerms
from zope.app.intid.interfaces import IIntIds
from zope.cachedescriptors.property import Lazy
from zope.dottedname.resolve import resolve
from zope.formlib import form
from zope.formlib.form import FormFields
from zope.formlib.form import EditForm as BaseEditForm
from zope.formlib.form import AddForm as BaseAddForm
from zope.formlib.namedtemplate import NamedTemplate
from zope.interface import Interface, implements
from zope.app.publisher.browser import applySkin
@ -44,7 +42,7 @@ from cybertools.browser.view import GenericView
from cybertools.relation.interfaces import IRelationRegistry
from cybertools.typology.interfaces import IType
from loops.interfaces import IView
from loops import util
#from loops import util
from loops.util import _
@ -64,13 +62,13 @@ class IAddForm(Interface):
)
class AddForm(BaseAddForm):
class AddForm(form.AddForm):
form_fields = FormFields(IAddForm)
template = NamedTemplate('loops.pageform')
class EditForm(BaseEditForm):
class EditForm(form.EditForm):
template = NamedTemplate('loops.pageform')
@ -181,10 +179,6 @@ class BaseView(GenericView):
# this may depend on system and user settings...
return True
#@Lazy
#def inlineEditable(self):
# return self.inlineEditingActive and canWrite(self.context, 'title')
inlineEditable = False
def inlineEdit(self, id):

View file

@ -39,6 +39,9 @@ from zope.proxy import removeAllProxies
from zope.security import canAccess, canWrite
from zope.security.proxy import removeSecurityProxy
from zope.app.event.objectevent import ObjectModifiedEvent, Attributes
from zope.event import notify
from cybertools.ajax import innerHtml
from cybertools.browser import configurator
from cybertools.browser.view import GenericView
@ -299,6 +302,7 @@ class InlineEdit(NodeView):
def save(self):
target = self.virtualTargetObject
target.data = self.request.form['editorContent']
notify(ObjectModifiedEvent(target, Attributes(IResource, 'data')))
# special (named) views for nodes

View file

@ -76,13 +76,13 @@ class ResourceRelation(BaseRelation):
# concept
class Concept(Contained, Persistent):
implements(IConcept, IConceptManagerContained, IRelatable)
proxyInterface = IConceptView
_title = u''
def getTitle(self): return self._title
def setTitle(self, title): self._title = title
@ -132,7 +132,7 @@ class Concept(Contained, Persistent):
relationships = [ConceptRelation(None, self, p) for p in predicates]
# TODO: sort...
return getRelations(first=parent, second=self, relationships=relationships)
def getParents(self, predicates=None):
return [r.first for r in self.getParentRelations(predicates)]
@ -158,7 +158,7 @@ class Concept(Contained, Persistent):
def deassignParent(self, parent, predicates=None):
parent.deassignChild(self, predicates)
# resource relations
def getResourceRelations(self, predicates=None, resource=None):
@ -184,7 +184,7 @@ class Concept(Contained, Persistent):
# concept manager
class ConceptManager(BTreeContainer):
implements(IConceptManager, ILoopsContained)
@ -211,7 +211,7 @@ class ConceptManager(BTreeContainer):
def getViewManager(self):
return self.getLoopsRoot().getViewManager()
# adapters and similar components
@ -291,10 +291,12 @@ class IndexAttributes(object):
def __init__(self, context):
self.context = context
# obsolete, use TextIndexNG (with indexableContent() method) instead
def text(self):
context = self.context
return ' '.join((zapi.getName(context), context.title,))
def title(self):
return self.text()
context = self.context
return ' '.join((zapi.getName(context), context.title,))

View file

@ -276,12 +276,22 @@
<require like_class="zope.app.dublincore.annotatableadapter.ZDCAnnotatableAdapter" />
</class>
<adapter factory="loops.concept.IndexAttributes" />
<adapter factory="loops.resource.IndexAttributes" />
<adapter factory="loops.concept.IndexAttributes" trusted="True" />
<class class="loops.concept.IndexAttributes">
<allow interface="loops.interfaces.IIndexAttributes" />
</class>
<adapter factory="loops.resource.IndexAttributes" trusted="True" />
<class class="loops.resource.IndexAttributes">
<allow interface="loops.interfaces.IIndexAttributes" />
</class>
<adapter factory="loops.resource.IndexableResource" trusted="True" />
<class class="loops.resource.IndexableResource">
<require permission="zope.View"
interface="loops.interfaces.IBaseResourceSchema" />
<allow interface="textindexng.interfaces.IIndexableContent" />
</class>
<class class="textindexng.content.IndexContentCollector">
<allow interface="textindexng.interfaces.IIndexContentCollector" />
</class>
<adapter factory="loops.resource.DocumentReadFileAdapter" />
@ -350,6 +360,21 @@
name="loops.PredicateSource"
/>
<!-- textindexng converters -->
<utility
provides="textindexng.interfaces.IConverter"
component="textindexng.converters.null.NullConverter"
name="text/structured"
/>
<utility
provides="textindexng.interfaces.IConverter"
component="textindexng.converters.null.NullConverter"
name="text/restructured"
/>
<include package=".knowledge" />
<include package=".organize" />
<include package=".process" />

View file

@ -37,6 +37,9 @@ from zope import schema
from persistent import Persistent
from cStringIO import StringIO
from zope.app.event.objectevent import ObjectModifiedEvent, Attributes
from zope.event import notify
from textindexng.interfaces import IIndexableContent
from textindexng.content import IndexContentCollector
from cybertools.relation.registry import getRelations
@ -205,6 +208,7 @@ class DocumentWriteFileAdapter(object):
def write(self, data):
self.context.data = unicode(data.replace('\r', ''), 'UTF-8')
notify(ObjectModifiedEvent(self.context, Attributes(IDocument, 'data')))
class DocumentReadFileAdapter(object):
@ -230,6 +234,7 @@ class IndexAttributes(object):
def __init__(self, context):
self.context = context
# obsolete, use TextIndexNG (with indexableContent() method) instead
def text(self):
context = self.context
return ' '.join((zapi.getName(context), context.title, context.data)).strip()

View file

@ -11,6 +11,7 @@ Let's do some basic set up
>>> site = placefulSetUp(True)
>>> from zope import component, interface
>>> from zope.interface import implements
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
@ -81,3 +82,22 @@ a controller attribute for the search view.
'return submitReplacing("1.results", "1.search.form",
"http://127.0.0.1/loops/views/page/.target19/@@searchresults.html")'
The searchresults.html view, i.e. the SearchResults view class provides the
result set of the search via its `results` property.
>>> from loops.search.browser import SearchResults
>>> form = {'search.2.title': 'yes', 'search.2.text': u'foo' }
>>> request = TestRequest(form=form)
>>> resultsView = SearchResults(page, request)
Before accessing the `results` property we have to prepare a catalog.
>>> from zope.app.catalog.interfaces import ICatalog
>>> class DummyCat(object):
... implements(ICatalog)
... def searchResults(self, **criteria):
... return []
>>> component.provideUtility(DummyCat())
>>> resultsView.results
[]

View file

@ -24,6 +24,7 @@ $Id$
"""
from zope import interface, component
from zope.app.catalog.interfaces import ICatalog
from zope.app.pagetemplate import ViewPageTemplateFile
from zope.cachedescriptors.property import Lazy
from zope.formlib.namedtemplate import NamedTemplate, NamedTemplateImplementation
@ -75,3 +76,22 @@ class SearchResults(BaseView):
def __call__(self):
return innerHtml(self)
@Lazy
def results(self):
request = self.request
text = request.get('search.2.text')
if not text:
return set()
useTitle = request.get('search.2.title')
useFull = request.get('search.2.full')
r1 = r2 = set()
cat = component.getUtility(ICatalog)
if useFull:
criteria = {'loops_resource_textng': {'query': text},}
r1 = set(cat.searchResults(**criteria))
if useTitle:
criteria = {'loops_title': text,}
r2 = set(cat.searchResults(**criteria))
result = [BaseView(r, request) for r in r1.union(r2)]
return result

View file

@ -1,19 +1,23 @@
<metal:search define-macro="search">
<div id="search"
tal:define="macros item/template/macros">
tal:define="macros item/template/macros;
idPrefix string:${view/itemNum}.search;
formId string:$idPrefix.form;
resultsId string:$idPrefix.results">
<h3 tal:attributes="class string:content-$level;
ondblclick item/openEditWindow">
Search
</h3>
<div metal:define-macro="search_form" class="searchForm"
tal:define="idPrefix string:${view/itemNum}.search;
formId string:$idPrefix.form">
<div metal:define-macro="search_form" class="searchForm">
<fieldset class="box">
<form action="." method="post" id="1.search.form"
tal:attributes="id formId">
<input type="hidden" name="search.submitted" value="yes" />
<input type="hidden" name="search.resultsId" value="1.results"
tal:attributes="value resultsId" />
<table cellpadding="3">
<tal:block repeat="search_selection python: ['type', 'text']">
<tal:block repeat="search_param python: ['type', 'text']">
<metal:row use-macro="macros/search_row" />
</tal:block>
<tr>
@ -24,10 +28,9 @@
</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)" />
<input type="submit" name="button.search" value="Search" class="submit"
tal:attributes="onclick python:
item.submitReplacing(resultsId, formId, view)" />
</td>
</tr>
</table>
@ -35,11 +38,17 @@
</fieldset>
</div>
<div metal:define-macro="search_results"
id="1.results">
<fieldset class="box">
Search results
<p tal:content="python:request.get('2.search.1.text', 'nothing')" />
<div metal:define-macro="search_results" id="1.search.results"
tal:attributes="id resultsId | request/search.resultsId">
<fieldset class="box"
tal:condition="request/search.submitted | nothing">
<legend>Search results</legend>
<div tal:repeat="row view/results">
<div>
<a tal:attributes="href string:${view/url}/.target${row/uniqueId}"
tal:content="row/title" />
</div>
</div>
</fieldset>
</div>
@ -47,18 +56,22 @@
</metal:search>
<tr metal:define-macro="search_row" id="search.row.1.1"
<tr metal:define-macro="search_row" id="1.1.row"
tal:define="rowNum item/rowNum;
idPrefix string:$idPrefix.$rowNum;
selection search_selection | item/searchSelection"
namePrefix string:search.$rowNum;
param search_param | item/searchParam"
tal:attributes="id string:$idPrefix.row">
<td style="width: 30px">
<input type="hidden" name="paramtype" value="type"
tal:attributes="name string:$namePrefix.paramtype;
value param" />
<input type="button" value="&minus;"
title="Remove search parameter"
tal:condition="python: selection not in ['type', 'text']" />&nbsp;
tal:condition="python: param not in ['type', 'text']" />&nbsp;
</td>
<td>
<div metal:use-macro="macros/?selection" />
<div metal:use-macro="macros/?param" />
</td>
</tr>
@ -69,7 +82,7 @@
<label for="text"
tal:attributes="for string:$idPrefix.text">Type:</label>
<select name="text"
tal:attributes="name string:$idPrefix.text;
tal:attributes="name string:$namePrefix.text;
id string:$idPrefix.text;">
<option value=".loops/concepts/topic">Topic</option>
<option value="loops.resource.Document">Document</option>
@ -84,21 +97,21 @@
<div>
<h3>Search text</h3>
<input type="checkbox" checked
name="title" id="title"
tal:attributes="name string:$idPrefix.title;
name="title" id="title" value="yes"
tal:attributes="name string:$namePrefix.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;
name="full" id="full" value="yes"
tal:attributes="name string:$namePrefix.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;
tal:attributes="name string:$namePrefix.text;
id string:$idPrefix.text;" />
<input type="button" value="+"
title="Add search word" />&nbsp;

View file

@ -31,8 +31,10 @@ from zope.interface import implements
from zope import schema
from zope.security.proxy import removeSecurityProxy
from loops.interfaces import IResource
from loops.interfaces import IDocument, IMediaAsset
from zope.app.event.objectevent import ObjectModifiedEvent, Attributes
from zope.event import notify
from loops.interfaces import ILoopsObject, IResource, IDocument, IMediaAsset
from loops.interfaces import IDocumentView, IMediaAssetView
from loops.interfaces import IView
from loops.interfaces import IConcept, IConceptView
@ -52,10 +54,13 @@ class TargetProxy(object):
@Lazy
def target(self):
return self.context.target
def getTitle(self):
return self.target.title
def setTitle(self, title): self.target.title = title
def setTitle(self, title):
self.target.title = title
notify(ObjectModifiedEvent(self.target,
Attributes(ILoopsObject, 'title')))
title = property(getTitle, setTitle)
@ -65,7 +70,9 @@ class ConceptProxy(TargetProxy):
adapts(IConceptView)
def getConceptType(self): return self.target.conceptType
def setConceptType(self, conceptType): self.target.conceptType = conceptType
def setConceptType(self, conceptType):
self.target.conceptType = conceptType
notify(ObjectModifiedEvent(self.target, Attributes(IConcept, 'conceptType')))
conceptType = property(getConceptType, setConceptType)
def getChildren(self, predicates=None):
@ -81,6 +88,7 @@ class ConceptProxy(TargetProxy):
class ResourceProxy(TargetProxy):
def setContentType(self, contentType):
notify(ObjectModifiedEvent(self.target, Attributes(IResource, 'contentType')))
self.target.contentType = contentType
def getContentType(self): return self.target.contentType
contentType = property(getContentType, setContentType)
@ -91,7 +99,9 @@ class DocumentProxy(ResourceProxy):
implements(IDocument)
adapts(IDocumentView)
def setData(self, data): self.target.data = data
def setData(self, data):
self.target.data = data
notify(ObjectModifiedEvent(self.target, Attributes(IDocument, 'data')))
def getData(self): return self.target.data
data = property(getData, setData)