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:
		
							parent
							
								
									b4131bfcaf
								
							
						
					
					
						commit
						3bd8e181be
					
				
					 10 changed files with 231 additions and 44 deletions
				
			
		|  | @ -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): | ||||||
|  |  | ||||||
|  | @ -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; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  |  | ||||||
|  | @ -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; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -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" /> | ||||||
|  |  | ||||||
|  | @ -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'' | ||||||
|  |  | ||||||
|  | @ -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" /> | ||||||
|  |  | ||||||
|  | @ -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")' | ||||||
|  | 
 | ||||||
|  |  | ||||||
|  | @ -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) | ||||||
|  | 
 | ||||||
|  |  | ||||||
|  | @ -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> | ||||||
|  |  | ||||||
							
								
								
									
										124
									
								
								search/search.pt
									
										
									
									
									
								
							
							
						
						
									
										124
									
								
								search/search.pt
									
										
									
									
									
								
							|  | @ -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="−" | ||||||
|  |            title="Remove search parameter" | ||||||
|  |            tal:condition="python: selection not in ['type', 'text']" />  | ||||||
|  |   </td> | ||||||
|   <td> |   <td> | ||||||
|     <input type="button" value="−" />  |     <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" />  | ||||||
|  |   </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>   | ||||||
|  |     <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" />  | ||||||
|  |   </div> | ||||||
|  | </metal:text> | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | <!-- obsolete --> | ||||||
|  | <tr metal:define-macro="search_row_selectable" id="search.row.1.1"> | ||||||
|  |   <td> | ||||||
|  |     <input type="button" value="−" | ||||||
|  |            title="Remove search parameter" />  | ||||||
|   </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>   |  | ||||||
|     <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="+" />  |  | ||||||
|   </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="+" />  |  | ||||||
|   </div> |  | ||||||
| </metal:text> |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		
		Reference in a new issue
	
	 helmutm
						helmutm