Remove now obsolete loops.search package.
This commit is contained in:
		
							parent
							
								
									ad8c236aa5
								
							
						
					
					
						commit
						0d1bb5a722
					
				
					 6 changed files with 0 additions and 955 deletions
				
			
		|  | @ -1,239 +0,0 @@ | |||
| =================================================================== | ||||
| loops.search - Provide search functionality for the loops framework | ||||
| =================================================================== | ||||
| 
 | ||||
|   ($Id$) | ||||
| 
 | ||||
| 
 | ||||
| Let's do some basic set up | ||||
| 
 | ||||
|   >>> from zope.app.testing.setup import placefulSetUp, placefulTearDown | ||||
|   >>> 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 | ||||
| ZCML setup): | ||||
| 
 | ||||
|   >>> from loops.concept import Concept | ||||
|   >>> from loops.type import ConceptType, TypeConcept | ||||
|   >>> from loops.interfaces import ITypeConcept | ||||
|   >>> from loops.base import Loops | ||||
|   >>> from loops.expert.testsetup import TestSite | ||||
|   >>> t = TestSite(site) | ||||
|   >>> concepts, resources, views = t.setup() | ||||
| 
 | ||||
|   >>> loopsRoot = site['loops'] | ||||
|   >>> query = concepts['query'] | ||||
|   >>> topic = concepts['topic'] | ||||
| 
 | ||||
| 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 | ||||
| 
 | ||||
| Search views | ||||
| ------------ | ||||
| 
 | ||||
| Now we are ready to create a search view object: | ||||
| 
 | ||||
|   >>> from zope.publisher.browser import TestRequest | ||||
|   >>> from loops.expert.browser.search 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.rowNum | ||||
|   1 | ||||
|   >>> searchView.rowNum | ||||
|   2 | ||||
| 
 | ||||
| The search view provides vocabularies for types that allow the selection | ||||
| of types to search for; this needs an ITypeManager adapter registered via | ||||
| zcml in real life: | ||||
| 
 | ||||
|   >>> from loops.type import LoopsTypeManager | ||||
|   >>> component.provideAdapter(LoopsTypeManager) | ||||
| 
 | ||||
|   >>> t = searchView.typesForSearch() | ||||
|   >>> len(t) | ||||
|   14 | ||||
|   >>> t.getTermByToken('loops:resource:*').title | ||||
|   'Any Resource' | ||||
| 
 | ||||
|   >>> t = searchView.conceptTypesForSearch() | ||||
|   >>> len(t) | ||||
|   11 | ||||
|   >>> t.getTermByToken('loops:concept:*').title | ||||
|   'Any Concept' | ||||
| 
 | ||||
| To execute the search in the context of a node we have to set up a node | ||||
| view for our page. The submitReplacing method returns a JavaScript call | ||||
| that will replace the results part on the search page; as this registers | ||||
| the dojo library with the view's controller we also have to supply | ||||
| a controller attribute for the search view. | ||||
| 
 | ||||
|   >>> from loops.browser.node import NodeView | ||||
|   >>> request = TestRequest() | ||||
|   >>> pageView = NodeView(page, request) | ||||
| 
 | ||||
|   >>> from cybertools.browser.liquid.controller import Controller | ||||
|   >>> searchView.controller = Controller(searchView, request) | ||||
| 
 | ||||
|   >>> searchView.submitReplacing('1.results', '1.search.form', pageView) | ||||
|   'submitReplacing("1.results", "1.search.form", | ||||
|        "http://127.0.0.1/loops/views/page/.target80/@@searchresults.html");...' | ||||
| 
 | ||||
| Basic (text/title) search | ||||
| ------------------------- | ||||
| 
 | ||||
| The searchresults.html view, i.e. the SearchResults view class provides the | ||||
| result set of the search via its `results` property. | ||||
| 
 | ||||
| Before accessing the `results` property we have to prepare a | ||||
| resource we can search for and index it in the catalog. | ||||
| 
 | ||||
|   >>> from loops.resource import Resource | ||||
|   >>> rplone = resources['plone'] = Resource() | ||||
| 
 | ||||
|   >>> from zope.app.catalog.interfaces import ICatalog | ||||
|   >>> from loops import util | ||||
|   >>> catalog = component.getUtility(ICatalog) | ||||
|   >>> catalog.index_doc(int(util.getUidForObject(rplone)), rplone) | ||||
| 
 | ||||
|   >>> from loops.expert.browser.search import SearchResults | ||||
|   >>> form = {'search.2.title': True, 'search.2.text': u'plone'} | ||||
|   >>> request = TestRequest(form=form) | ||||
|   >>> resultsView = SearchResults(page, request) | ||||
| 
 | ||||
|   >>> results = list(resultsView.results) | ||||
|   >>> len(results) | ||||
|   1 | ||||
|   >>> results[0].context == rplone | ||||
|   True | ||||
| 
 | ||||
|   >>> form = {'search.2.title': True, 'search.2.text': u'foo'} | ||||
|   >>> request = TestRequest(form=form) | ||||
|   >>> resultsView = SearchResults(page, request) | ||||
|   >>> len(list(resultsView.results)) | ||||
|   0 | ||||
| 
 | ||||
| Search via related concepts | ||||
| --------------------------- | ||||
| 
 | ||||
| We first have to prepare some test concepts (topics); we also assign our test | ||||
| resource (rplone) from above to one of the topics: | ||||
| 
 | ||||
|   >>> czope = concepts['zope'] = Concept(u'Zope') | ||||
|   >>> czope2 = concepts['zope2'] = Concept(u'Zope 2') | ||||
|   >>> czope3 = concepts['zope3'] = Concept(u'Zope 3') | ||||
|   >>> cplone = concepts['plone'] = Concept(u'Plone') | ||||
|   >>> for c in (czope, czope2, czope3, cplone): | ||||
|   ...     c.conceptType = topic | ||||
|   ...     catalog.index_doc(int(util.getUidForObject(c)), c) | ||||
|   >>> czope.assignChild(czope2) | ||||
|   >>> czope.assignChild(czope3) | ||||
|   >>> czope2.assignChild(cplone) | ||||
|   >>> rplone.assignConcept(cplone) | ||||
| 
 | ||||
| Now we can fill our search form and execute the query; note that all concepts | ||||
| found are listed, plus all their children and all resources associated | ||||
| with them: | ||||
| 
 | ||||
|   >>> uid = util.getUidForObject(concepts['zope']) | ||||
|   >>> form = {'search.3.type': 'loops:concept:topic', 'search.3.text': uid} | ||||
|   >>> request = TestRequest(form=form) | ||||
|   >>> resultsView = SearchResults(page, request) | ||||
|   >>> results = list(resultsView.results) | ||||
|   >>> len(results) | ||||
|   5 | ||||
|   >>> results[0].context.__name__ | ||||
|   u'plone' | ||||
| 
 | ||||
|   >>> uid = util.getUidForObject(concepts['zope3']) | ||||
|   >>> form = {'search.3.type': 'loops:concept:topic', 'search.3.text': uid} | ||||
|   >>> request = TestRequest(form=form) | ||||
|   >>> resultsView = SearchResults(page, request) | ||||
|   >>> results = list(resultsView.results) | ||||
|   >>> len(results) | ||||
|   1 | ||||
|   >>> results[0].context.__name__ | ||||
|   u'zope3' | ||||
| 
 | ||||
| To support easy entry of concepts to search for we can preselect the available | ||||
| concepts (optionally restricted to a certain type) by entering text parts | ||||
| of the concepts' titles: | ||||
| 
 | ||||
|   >>> form = {'searchType': 'loops:concept:topic', 'name': u'zope'} | ||||
|   >>> request = TestRequest(form=form) | ||||
|   >>> view = Search(page, request) | ||||
|   >>> view.listConcepts() | ||||
|   u"{identifier: 'id', items: [{label: 'Zope (Topic)', name: 'Zope', id: '85'}, {label: 'Zope 2 (Topic)', name: 'Zope 2', id: '87'}, {label: 'Zope 3 (Topic)', name: 'Zope 3', id: '89'}]}" | ||||
| 
 | ||||
| Preset Concept Types on Search Forms | ||||
| ------------------------------------ | ||||
| 
 | ||||
| Often we want to include certain types in our search. We can instruct | ||||
| the search form to include lines for these types by giving these types | ||||
| a certain qualifier, via the option attribute of the type interface. | ||||
| 
 | ||||
| Let's start with a new type, the customer type. | ||||
| 
 | ||||
|   >>> customer = concepts['customer'] | ||||
|   >>> custType = ITypeConcept(customer) | ||||
|   >>> custType.options | ||||
|   [] | ||||
| 
 | ||||
|   >>> cust1 = concepts['cust1'] | ||||
|   >>> cust2 = concepts['cust2'] | ||||
|   >>> for c in (cust1, cust2): | ||||
|   ...     c.conceptType = customer | ||||
|   ...     catalog.index_doc(int(util.getUidForObject(c)), c) | ||||
| 
 | ||||
|   >>> from cybertools.typology.interfaces import IType | ||||
|   >>> IType(cust1).qualifiers | ||||
|   ('concept',) | ||||
| 
 | ||||
|   >>> searchView = Search(search, TestRequest()) | ||||
|   >>> list(searchView.presetSearchTypes) | ||||
|   [] | ||||
| 
 | ||||
| We can now add a 'search' qualifier to the customer type's options | ||||
| and thus include the customer type in the preset search types. | ||||
| 
 | ||||
|   >>> custType.options = ('qualifier:search',) | ||||
|   >>> IType(cust1).qualifiers | ||||
|   ('concept', 'search') | ||||
|   >>> searchView = Search(search, TestRequest()) | ||||
|   >>> list(searchView.presetSearchTypes) | ||||
|   [{'token': 'loops:concept:customer', 'title': u'Customer'}] | ||||
| 
 | ||||
|   >>> searchView.conceptsForType('loops:concept:customer') | ||||
|   [{'token': 'none', 'title': u'not selected'}, | ||||
|    {'token': '58', 'title': u'Customer 1'}, | ||||
|    {'token': '60', 'title': u'Customer 2'}, | ||||
|    {'token': '62', 'title': u'Customer 3'}] | ||||
| 
 | ||||
| Let's use this new search option for querying: | ||||
| 
 | ||||
|   >>> form = {'search.4.text_selected': u'58'} | ||||
|   >>> resultsView = SearchResults(page, TestRequest(form=form)) | ||||
|   >>> results = list(resultsView.results) | ||||
|   >>> results[0].title | ||||
|   u'Customer 1' | ||||
| 
 | ||||
| 
 | ||||
| Automatic Filtering | ||||
| ------------------- | ||||
| 
 | ||||
| TODO - more to come... | ||||
| 
 | ||||
|  | @ -1,4 +0,0 @@ | |||
| """ | ||||
| $Id$ | ||||
| """ | ||||
| 
 | ||||
|  | @ -1,295 +0,0 @@ | |||
| # | ||||
| #  Copyright (c) 2011 Helmut Merz helmutm@cy55.de | ||||
| # | ||||
| #  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 | ||||
| #  the Free Software Foundation; either version 2 of the License, or | ||||
| #  (at your option) any later version. | ||||
| # | ||||
| #  This program is distributed in the hope that it will be useful, | ||||
| #  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| #  GNU General Public License for more details. | ||||
| # | ||||
| #  You should have received a copy of the GNU General Public License | ||||
| #  along with this program; if not, write to the Free Software | ||||
| #  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
| # | ||||
| 
 | ||||
| """ | ||||
| Definition of basic view classes and other browser related stuff for the | ||||
| loops.search package. | ||||
| 
 | ||||
| $Id: browser.py 4160 2011-02-07 16:53:08Z helmutm $ | ||||
| """ | ||||
| 
 | ||||
| from zope import interface, component | ||||
| from zope import traversing | ||||
| from zope.app.pagetemplate import ViewPageTemplateFile | ||||
| from zope.cachedescriptors.property import Lazy | ||||
| from zope.formlib.namedtemplate import NamedTemplate, NamedTemplateImplementation | ||||
| from zope.i18nmessageid import MessageFactory | ||||
| 
 | ||||
| from cybertools.ajax import innerHtml | ||||
| from cybertools.relation.interfaces import IRelationRegistry | ||||
| from cybertools.stateful.interfaces import IStateful, IStatesDefinition | ||||
| from cybertools.typology.interfaces import ITypeManager | ||||
| from loops.browser.common import BaseView | ||||
| from loops.browser.node import NodeView | ||||
| from loops.common import adapted, AdapterBase | ||||
| #from loops.expert.browser.search import searchMacrosTemplate as search_template | ||||
| from loops.expert.concept import ConceptQuery, FullQuery | ||||
| from loops.interfaces import IResource | ||||
| from loops.organize.personal.browser.filter import FilterView | ||||
| from loops import util | ||||
| from loops.util import _ | ||||
| 
 | ||||
| 
 | ||||
| #search_template = ViewPageTemplateFile('search.pt') | ||||
| 
 | ||||
| 
 | ||||
| class Search(BaseView): | ||||
| 
 | ||||
|     maxRowNum = 0 | ||||
| 
 | ||||
|     @Lazy | ||||
|     def search_macros(self): | ||||
|         return self.controller.getTemplateMacros('search', search_template) | ||||
| 
 | ||||
|     @Lazy | ||||
|     def macro(self): | ||||
|         return self.search_macros['search'] | ||||
| 
 | ||||
|     @property | ||||
|     def rowNum(self): | ||||
|         """ Return the rowNum to be used for identifying the current search | ||||
|             parameter 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 | ||||
| 
 | ||||
|     @Lazy | ||||
|     def presetSearchTypes(self): | ||||
|         """ Return a list of concept type info dictionaries (see BaseView) | ||||
|             that should be displayed on a separate search parameter row. | ||||
|         """ | ||||
|         #return ITypeManager(self.context).listTypes(include=('search',)) | ||||
|         return self.listTypesForSearch(include=('search',)) | ||||
| 
 | ||||
|     def conceptsForType(self, token): | ||||
|         result = ConceptQuery(self).query(type=token) | ||||
|         fv = FilterView(self.context, self.request) | ||||
|         result = fv.apply(result) | ||||
|         result.sort(key=lambda x: x.title) | ||||
|         noSelection = dict(token='none', title=u'not selected') | ||||
|         return [noSelection] + [dict(title=adapted(o, self.languageInfo).title, | ||||
|                                      token=util.getUidForObject(o)) | ||||
|                                     for o in result] | ||||
| 
 | ||||
|     def initDojo(self): | ||||
|         self.registerDojo() | ||||
|         cm = self.controller.macros | ||||
|         jsCall = ('dojo.require("dojo.parser");' | ||||
|                   'dojo.require("dijit.form.FilteringSelect");' | ||||
|                   'dojo.require("dojox.data.QueryReadStore");') | ||||
|         cm.register('js-execute', jsCall, jsCall=jsCall) | ||||
| 
 | ||||
|     def listConcepts(self, filterMethod=None): | ||||
|         """ Used for dijit.FilteringSelect. | ||||
|         """ | ||||
|         request = self.request | ||||
|         request.response.setHeader('Content-Type', 'text/plain; charset=UTF-8') | ||||
|         title = request.get('name') | ||||
|         if title == '*': | ||||
|             title = None | ||||
|         types = request.get('searchType') | ||||
|         data = [] | ||||
|         if title or types: | ||||
|             if title is not None: | ||||
|                 title = title.replace('(', ' ').replace(')', ' ').replace(' -', ' ') | ||||
|                 #title = title.split(' ', 1)[0] | ||||
|             if not types: | ||||
|                 types = ['loops:concept:*'] | ||||
|             if not isinstance(types, (list, tuple)): | ||||
|                 types = [types] | ||||
|             for type in types: | ||||
|                 result = self.executeQuery(title=title or None, type=type, | ||||
|                                                  exclude=('hidden',)) | ||||
|                 fv = FilterView(self.context, self.request) | ||||
|                 result = fv.apply(result) | ||||
|                 for o in result: | ||||
|                     if o.getLoopsRoot() == self.loopsRoot: | ||||
|                         adObj = adapted(o, self.languageInfo) | ||||
|                         if filterMethod is not None and not filterMethod(adObj): | ||||
|                             continue | ||||
|                         name = self.getRowName(adObj) | ||||
|                         if title and title.endswith('*'): | ||||
|                             title = title[:-1] | ||||
|                         sort = ((title and name.startswith(title) and '0' or '1') | ||||
|                                 + name.lower()) | ||||
|                         if o.conceptType is None: | ||||
|                             raise ValueError('Concept Type missing for %r.' % name) | ||||
|                         data.append({'label': self.getRowLabel(adObj, name), | ||||
|                                      'name': name, | ||||
|                                      'id': util.getUidForObject(o), | ||||
|                                      'sort': sort}) | ||||
|         data.sort(key=lambda x: x['sort']) | ||||
|         if not title: | ||||
|             data.insert(0, {'label': '', 'name': '', 'id': ''}) | ||||
|         json = [] | ||||
|         for item in data[:100]: | ||||
|             json.append("{label: '%s', name: '%s', id: '%s'}" % | ||||
|                           (item['label'], item['name'], item['id'])) | ||||
|         json = "{identifier: 'id', items: [%s]}" % ', '.join(json) | ||||
|         #print '***', json | ||||
|         return json | ||||
| 
 | ||||
|     def executeQuery(self, **kw): | ||||
|         return ConceptQuery(self).query(**kw) | ||||
| 
 | ||||
|     def getRowName(self, obj): | ||||
|         return obj.getLongTitle() | ||||
| 
 | ||||
|     def getRowLabel(self, obj, name=None): | ||||
|         if isinstance(obj, AdapterBase): | ||||
|             obj = obj.context | ||||
|         if name is None: | ||||
|             name = obj.title | ||||
|         return '%s (%s)' % (name, obj.conceptType.title) | ||||
| 
 | ||||
|     @Lazy | ||||
|     def statesDefinitions(self): | ||||
|         return [component.getUtility(IStatesDefinition, name=n) | ||||
|                     for n in self.globalOptions('organize.stateful.resource', ())] | ||||
| 
 | ||||
|     @Lazy | ||||
|     def selectedStates(self): | ||||
|         result = {} | ||||
|         for k, v in self.request.form.items(): | ||||
|             if k.startswith('state.') and v: | ||||
|                 result[k] = v | ||||
|         return result | ||||
| 
 | ||||
|     def submitReplacing(self, targetId, formId, view): | ||||
|         self.registerDojo() | ||||
|         return 'submitReplacing("%s", "%s", "%s"); return false;' % ( | ||||
|                     targetId, formId, | ||||
|                     '%s/.target%s/@@searchresults.html' % (view.url, self.uniqueId)) | ||||
| 
 | ||||
|     @Lazy | ||||
|     def results(self): | ||||
|         form = self.request.form | ||||
|         type = form.get('search.1.text', 'loops:*') | ||||
|         text = form.get('search.2.text') | ||||
|         if text is not None: | ||||
|             text = util.toUnicode(text, encoding='ISO8859-15') # IE hack!!! | ||||
|         useTitle = form.get('search.2.title') | ||||
|         useFull = form.get('search.2.full') | ||||
|         conceptType = form.get('search.3.type', 'loops:concept:*') | ||||
|         #conceptTitle = form.get('search.3.text') | ||||
|         #if conceptTitle is not None: | ||||
|         #    conceptTitle = util.toUnicode(conceptTitle, encoding='ISO8859-15') | ||||
|         conceptUid = form.get('search.3.text') | ||||
|         result = FullQuery(self).query(text=text, type=type, | ||||
|                            useTitle=useTitle, useFull=useFull, | ||||
|                            #conceptTitle=conceptTitle, | ||||
|                            conceptUid=conceptUid, | ||||
|                            conceptType=conceptType) | ||||
|         rowNum = 4 | ||||
|         while rowNum < 10: | ||||
|             addCriteria = form.get('search.%i.text_selected' % rowNum) | ||||
|             rowNum += 1 | ||||
|             if not addCriteria: | ||||
|                 break | ||||
|             if addCriteria == 'none': | ||||
|                 continue | ||||
|             addSelection = FullQuery(self).query(text=text, type=type, | ||||
|                                 useTitle=useTitle, useFull=useFull, | ||||
|                                 conceptUid=addCriteria) | ||||
|             if result: | ||||
|                 result = [r for r in result if r in addSelection] | ||||
|             else: | ||||
|                 result = addSelection | ||||
|         result = [r for r in result if self.checkStates(r)] | ||||
|         fv = FilterView(self.context, self.request) | ||||
|         result = fv.apply(result) | ||||
|         result = sorted(result, key=lambda x: x.title.lower()) | ||||
|         return self.viewIterator(result) | ||||
| 
 | ||||
|     def checkStates(self, obj): | ||||
|         if not IResource.providedBy(obj): | ||||
|             return True | ||||
|         for std, states in self.selectedStates.items(): | ||||
|             if std.startswith('state.resource.'): | ||||
|                 std = std[len('state.resource.'):] | ||||
|             else: | ||||
|                 continue | ||||
|             stf = component.queryAdapter(obj, IStateful, name=std) | ||||
|             if stf is None: | ||||
|                 continue | ||||
|             for state in states: | ||||
|                 if stf.state == state: | ||||
|                     break | ||||
|                 else: | ||||
|                     return False | ||||
|         return True | ||||
| 
 | ||||
| 
 | ||||
| #class SearchResults(BaseView): | ||||
| class SearchResults(NodeView): | ||||
|     """ Provides results as inner HTML """ | ||||
| 
 | ||||
|     @Lazy | ||||
|     def search_macros(self): | ||||
|         return self.controller.getTemplateMacros('search', search_template) | ||||
| 
 | ||||
|     @Lazy | ||||
|     def macro(self): | ||||
|         return self.search_macros['search_results'] | ||||
| 
 | ||||
|     def __call__(self): | ||||
|         return innerHtml(self) | ||||
| 
 | ||||
|     @Lazy | ||||
|     def results(self): | ||||
|         form = self.request.form | ||||
|         type = form.get('search.1.text', 'loops:*') | ||||
|         text = form.get('search.2.text') | ||||
|         if text is not None: | ||||
|             text = util.toUnicode(text, encoding='ISO8859-15') # IE hack!!! | ||||
|         useTitle = form.get('search.2.title') | ||||
|         useFull = form.get('search.2.full') | ||||
|         conceptType = form.get('search.3.type', 'loops:concept:*') | ||||
|         #conceptTitle = form.get('search.3.text') | ||||
|         #if conceptTitle is not None: | ||||
|         #    conceptTitle = util.toUnicode(conceptTitle, encoding='ISO8859-15') | ||||
|         conceptUid = form.get('search.3.text') | ||||
|         result = FullQuery(self).query(text=text, type=type, | ||||
|                            useTitle=useTitle, useFull=useFull, | ||||
|                            #conceptTitle=conceptTitle, | ||||
|                            conceptUid=conceptUid, | ||||
|                            conceptType=conceptType) | ||||
|         rowNum = 4 | ||||
|         while rowNum < 10: | ||||
|             addCriteria = form.get('search.%i.text_selected' % rowNum) | ||||
|             rowNum += 1 | ||||
|             if not addCriteria: | ||||
|                 break | ||||
|             if addCriteria == 'none': | ||||
|                 continue | ||||
|             addSelection = FullQuery(self).query(text=text, type=type, | ||||
|                                 useTitle=useTitle, useFull=useFull, | ||||
|                                 conceptUid=addCriteria) | ||||
|             if result: | ||||
|                 result = [r for r in result if r in addSelection] | ||||
|             else: | ||||
|                 result = addSelection | ||||
|         fv = FilterView(self.context, self.request) | ||||
|         result = fv.apply(result) | ||||
|         result = sorted(result, key=lambda x: x.title.lower()) | ||||
|         return self.viewIterator(result) | ||||
| 
 | ||||
|  | @ -1,33 +0,0 @@ | |||
| <!-- $Id$ --> | ||||
| 
 | ||||
| <configure | ||||
|     xmlns:zope="http://namespaces.zope.org/zope" | ||||
|     xmlns:browser="http://namespaces.zope.org/browser" | ||||
|     i18n_domain="zope" | ||||
|     > | ||||
| 
 | ||||
|   <!--<zope:adapter | ||||
|       name="search" | ||||
|       for="loops.interfaces.IConcept | ||||
|            zope.publisher.interfaces.browser.IBrowserRequest" | ||||
|       provides="zope.interface.Interface" | ||||
|       factory="loops.search.browser.Search" | ||||
|       permission="zope.View" | ||||
|       /> | ||||
| 
 | ||||
|   <browser:page | ||||
|       name="listConceptsForComboBox.js" | ||||
|       for="loops.interfaces.ILoopsObject" | ||||
|       class="loops.search.browser.Search" | ||||
|       attribute="listConcepts" | ||||
|       permission="zope.View" | ||||
|       /> | ||||
| 
 | ||||
|   <browser:page | ||||
|       name="searchresults.html" | ||||
|       for="loops.interfaces.ILoopsObject" | ||||
|       class="loops.search.browser.SearchResults" | ||||
|       permission="zope.View" | ||||
|       />--> | ||||
| 
 | ||||
| </configure> | ||||
							
								
								
									
										358
									
								
								search/search.pt
									
										
									
									
									
								
							
							
						
						
									
										358
									
								
								search/search.pt
									
										
									
									
									
								
							|  | @ -1,358 +0,0 @@ | |||
| <metal:search define-macro="search" | ||||
|               i18n:domain="loops"> | ||||
|   <div id="search" | ||||
|        tal:define="macros item/search_macros; | ||||
|                    idPrefix string:${view/itemNum}.search; | ||||
|                    formId string:$idPrefix.form; | ||||
|                    resultsId string:$idPrefix.results; | ||||
|                    submitted request/search.submitted|nothing"> | ||||
|     <h1 tal:attributes="class string:content-$level; | ||||
|                         ondblclick item/openEditWindow" | ||||
|       tal:content="item/title"> | ||||
|       Search | ||||
|     </h1> | ||||
| 
 | ||||
|     <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_param python: | ||||
|                             ['type', 'text', 'concept', 'state']"> | ||||
|               <metal:row use-macro="macros/search_row" /> | ||||
|             </tal:block> | ||||
|             <tr tal:condition="nothing"> | ||||
|               <td colspan="2"> | ||||
|                 <input type="button" value="Add concept filter" class="button" /> | ||||
|                 <input type="button" value="Add attribute filter" class="button" /> | ||||
|               </td> | ||||
|             </tr> | ||||
|             <tr> | ||||
|               <td></td> | ||||
|               <td colspan="3"><br /> | ||||
|                 <input type="submit" name="button.search" value="Search" class="submit" | ||||
|                        i18n:attributes="value" | ||||
|                        tal:attributes="xx_onclick python: | ||||
|                                 item.submitReplacing(resultsId, formId, view)" /> | ||||
|               </td> | ||||
|             </tr> | ||||
|           </table> | ||||
|         </form> | ||||
|       </fieldset> | ||||
|     </div> | ||||
|     <tal:results condition="request/search.submitted|nothing"> | ||||
|       <metal:results use-macro="item/search_macros/results" /> | ||||
|     </tal:results> | ||||
| 
 | ||||
|     <div metal:define-macro="search_results" id="1.search.results" | ||||
|          tal:attributes="id resultsId | request/search.resultsId" | ||||
|          i18n:domain="loops" | ||||
|          xtal:define="item nocall:view; | ||||
|                      controller nocall:view/@@controller; | ||||
|                      resourceBase controller/resourceBase;"> | ||||
|       <fieldset class="box" | ||||
|                 tal:condition="request/search.submitted | nothing"> | ||||
|       <metal:results define-macro="results"> | ||||
|         <h2 i18n:translate="">Search results</h2> | ||||
|         <table class="listing" summary="Search results" | ||||
|                i18n:attributes="summary"> | ||||
|           <thead> | ||||
|             <tr> | ||||
|               <th i18n:translate="">Title</th> | ||||
|               <th i18n:translate="">Type</th> | ||||
|               <th i18n:translate="" | ||||
|                   tal:condition="view/useVersioning">V</th> | ||||
|               <th i18n:translate="">Size</th> | ||||
|               <th i18n:translate="">Modification Date</th> | ||||
|               <th i18n:translate="">Author(s)</th> | ||||
|               <th i18n:translate="" | ||||
|                   tal:condition="view/showObjectActions">Info</th> | ||||
|             </tr> | ||||
|           </thead> | ||||
|           <tbody> | ||||
|             <tal:items tal:repeat="row item/results"> | ||||
|               <tal:item define="class python: repeat['row'].odd() and 'even' or 'odd'; | ||||
|                                 description row/description"> | ||||
|                 <tr tal:attributes="class class"> | ||||
|                   <td> | ||||
|                     <a tal:attributes="href | ||||
|                         string:${view/url}/.target${row/uniqueId}?version=this; | ||||
|                                        title description"> | ||||
|                       <img tal:define="icon row/icon" | ||||
|                            tal:condition="icon" | ||||
|                            tal:attributes="src icon/src" /> | ||||
|                       <div tal:content="row/title" /></a> | ||||
|                   </td> | ||||
|                   <td i18n:translate="" class="center" | ||||
|                       tal:content="row/longTypeTitle|row/typeTitle">Type</td> | ||||
|                   <tal:version condition="view/useVersioning"> | ||||
|                     <td class="center" | ||||
|                         tal:define="versionId row/versionId|string:"> | ||||
|                       <a href="#" | ||||
|                          tal:content="versionId" | ||||
|                          tal:omit-tag="python: versionId and versionId=='1.1'" | ||||
|                          tal:attributes="href string:${view/url}/.target${row/uniqueId}?loops.viewName=listversions">1.1</a> | ||||
|                     </td> | ||||
|                   </tal:version> | ||||
|                   <td class="nowrap number"> | ||||
|                     <span tal:replace="row/context/sizeForDisplay|string:">Size</span> | ||||
|                   </td> | ||||
|                   <td><span tal:replace="row/modified">modified</span></td> | ||||
|                   <td><span tal:replace="row/creators">John</span></td> | ||||
|                   <td class="nowrap center" | ||||
|                       tal:define="target nocall:row; | ||||
|                                   style nothing" | ||||
|                       tal:condition="view/showObjectActions"> | ||||
|                     <div metal:use-macro="views/node_macros/object_actions" /> | ||||
|                   </td> | ||||
|                 </tr> | ||||
|               </tal:item> | ||||
|             </tal:items> | ||||
|           </tbody> | ||||
|         </table> | ||||
|       </metal:results> | ||||
|       </fieldset> | ||||
|     </div> | ||||
| 
 | ||||
|   </div> | ||||
| </metal:search> | ||||
| 
 | ||||
| 
 | ||||
| <div metal:define-macro="search_row" id="1.1.row" | ||||
|            tal:define="rowNum item/rowNum; | ||||
|                        basicIdPrefix idPrefix; | ||||
|                        idPrefix string:$idPrefix.$rowNum; | ||||
|                        namePrefix string:search.$rowNum; | ||||
|                        param search_param | item/searchParam" | ||||
|            tal:attributes="id string:$idPrefix.row"> | ||||
|       <div metal:use-macro="macros/?param" /> | ||||
| </div> | ||||
| 
 | ||||
| 
 | ||||
| <metal:text define-macro="type"> | ||||
|   <tr> | ||||
|     <td metal:use-macro="macros/minus"/> | ||||
|     <td colspan="3"> | ||||
|       <h2 i18n:translate="">Type(s) to search for</h2> | ||||
|     </td> | ||||
|   <tr> | ||||
|     <td></td> | ||||
|     <td> | ||||
|       <label for="text" | ||||
|              tal:attributes="for string:$idPrefix.text"> | ||||
|         <span i18n:translate="">Type</span>:</label> | ||||
|       <select name="text" | ||||
|               tal:define="name string:$namePrefix.text; | ||||
|                           value request/?name|nothing" | ||||
|               tal:attributes="name name; | ||||
|                               id string:$idPrefix.text;"> | ||||
|         <tal:types repeat="type item/typesForSearch"> | ||||
|           <option value="loops:*" | ||||
|                   i18n:translate="" | ||||
|                   tal:attributes="value type/token; | ||||
|                                   selected python: value == type.token" | ||||
|                   tal:content="type/title">Topic</option> | ||||
|         </tal:types> | ||||
|       </select> | ||||
|       <input type="button" value="+" | ||||
|              title="Add type" | ||||
|              tal:condition="nothing" />  | ||||
|     </td> | ||||
|     <td colspan="2"></td> | ||||
|   </tr> | ||||
| </metal:text> | ||||
| 
 | ||||
| 
 | ||||
| <metal:text define-macro="text"> | ||||
|   <tr> | ||||
|     <td metal:use-macro="macros/minus"/> | ||||
|     <td colspan="3"> | ||||
|       <h2 i18n:translate="">Text-based search</h2> | ||||
|     </td> | ||||
|   <tr> | ||||
|     <td></td> | ||||
|     <td> | ||||
|       <input type="checkbox" value="yes" | ||||
|              tal:define="name string:$namePrefix.title" | ||||
|              tal:attributes="name name; | ||||
|                              id string:$idPrefix.title; | ||||
|                              checked request/?name|not:submitted|string:yes" /> | ||||
|       <label for="title" | ||||
|              i18n:translate="" | ||||
|              tal:attributes="for string:$idPrefix.title">Title</label> | ||||
|       <input type="checkbox" value="yes" | ||||
|              tal:define="name string:$namePrefix.full" | ||||
|              tal:attributes="name name; | ||||
|                              id string:$idPrefix.full; | ||||
|                              checked request/?name|nothing" /> | ||||
|       <label for="full" | ||||
|              i18n:translate="" | ||||
|              tal:attributes="for string:$idPrefix.full">Full text</label>   | ||||
|     </td> | ||||
|     <td> | ||||
|       <label for="text" | ||||
|              tal:attributes="for string:$idPrefix.text"> | ||||
|         <span i18n:translate="">Search text</span>:</label> | ||||
|     </td> | ||||
|     <td> | ||||
|       <input type="text" | ||||
|              tal:define="name string:$namePrefix.text" | ||||
|              tal:attributes="name name; | ||||
|                              id string:$idPrefix.text; | ||||
|                              value request/?name|nothing" /> | ||||
|       <input type="button" value="+" | ||||
|              title="Add search word" | ||||
|              tal:condition="nothing" />  | ||||
|     </td> | ||||
|   </tr> | ||||
| </metal:text> | ||||
| 
 | ||||
| 
 | ||||
| <metal:text define-macro="concept"> | ||||
|   <tr> | ||||
|     <td metal:use-macro="macros/minus"/> | ||||
|     <td colspan="3"> | ||||
|       <h2 i18n:translate="">Search via related concepts</h2> | ||||
|     </td> | ||||
|   <tr tal:repeat="type item/presetSearchTypes"> | ||||
|     <tal:preset define="rowNum item/rowNum; | ||||
|                         idPrefix string:$basicIdPrefix.$rowNum; | ||||
|                         namePrefix string:search.$rowNum;"> | ||||
|       <td></td> | ||||
|       <td> | ||||
|         <span i18n:translate="">Type</span>: | ||||
|         <b tal:content="type/title" i18n:translate="" /> | ||||
|         <input type="hidden" name="type" value="" | ||||
|                tal:attributes="name string:$namePrefix.type; | ||||
|                                value type/token" /> | ||||
|       </td> | ||||
|       <td> | ||||
|         <label for="text" | ||||
|                tal:attributes="for string:$idPrefix.text"> | ||||
|           <span i18n:translate="">Concept for Search</span>:</label> | ||||
|       </td> | ||||
|       <td> | ||||
|         <select name="text_selected" | ||||
|                 tal:define="name string:$namePrefix.text_selected; | ||||
|                             value request/?name|nothing" | ||||
|                 tal:attributes="name name; | ||||
|                                 id string:$idPrefix.text;"> | ||||
|           <tal:concepts repeat="concept python: item.conceptsForType(type['token'])"> | ||||
|             <option tal:attributes="value concept/token; | ||||
|                                     selected python: value == concept['token']" | ||||
|                     i18n:translate="" | ||||
|                     tal:content="concept/title">Zope Corp</option> | ||||
|           </tal:concepts> | ||||
|         </select> | ||||
|       </td> | ||||
|     </tal:preset> | ||||
|   </tr> | ||||
|   <tr> | ||||
|     <td></td> | ||||
|     <td> | ||||
|       <label for="type" | ||||
|              tal:attributes="for string:$idPrefix.type"> | ||||
|         <span i18n:translate="">Type</span>:</label> | ||||
|       <select name="type" | ||||
|               tal:define="name string:$namePrefix.type; | ||||
|                           global conceptTypeValue request/?name|string:" | ||||
|               tal:attributes="name name; | ||||
|                               id string:$idPrefix.type; | ||||
|                               value conceptTypeValue; | ||||
|                               onChange string:setConceptTypeForComboBox('$idPrefix.type', '$idPrefix.text')"> | ||||
|         <tal:types repeat="type item/conceptTypesForSearch"> | ||||
|           <option value="loops:*" | ||||
|                   i18n:translate="" | ||||
|                   tal:attributes="value type/token; | ||||
|                                   selected python: conceptTypeValue == type.token" | ||||
|                   tal:content="type/title">Topic</option> | ||||
|         </tal:types> | ||||
|       </select>   | ||||
|     </td> | ||||
|     <td> | ||||
|       <label for="text" | ||||
|              tal:attributes="for string:$idPrefix.text"> | ||||
|         <span i18n:translate="">Concept for Search</span>:</label> | ||||
|       <input type="text" name="text" | ||||
|              tal:condition="nothing" | ||||
|              tal:attributes="name string:$namePrefix.text; | ||||
|                              id string:$idPrefix.text;" /> | ||||
|       <input type="button" value="+" | ||||
|              title="Add type" | ||||
|              tal:condition="nothing" />  | ||||
|     </td> | ||||
|     <td> | ||||
|       <tal:combo tal:define="dummy item/initDojo; | ||||
|                              name string:$namePrefix.text; | ||||
|                              value request/?name|nothing; | ||||
|                              concept python: | ||||
|                                 value and view.getObjectForUid(value); | ||||
|                              displayValue python: | ||||
|                                 concept and concept.title or u''"> | ||||
|         <div dojoType="dojox.data.QueryReadStore" jsId="conceptSearch" | ||||
|              url="listConceptsForComboBox.js?searchType=" | ||||
|              tal:attributes="url | ||||
|                     string:listConceptsForComboBox.js?searchType=$conceptTypeValue"> | ||||
|         </div> | ||||
|         <input dojoType="dijit.form.FilteringSelect" store="conceptSearch" | ||||
|                autoComplete="False" labelAttr="name" style="height: 16px" | ||||
|                name="concept.search.text" id="concept.search.text" | ||||
|                tal:attributes="name name; | ||||
|                                id string:$idPrefix.text; | ||||
|                                displayedValue displayValue" /> | ||||
|       </tal:combo> | ||||
|     </td> | ||||
|   </tr> | ||||
| </metal:text> | ||||
| 
 | ||||
| 
 | ||||
| <metal:text define-macro="state" | ||||
|             tal:define="stateDefs item/statesDefinitions; | ||||
|                         deftype string:resource" | ||||
|             tal:condition="stateDefs"> | ||||
|   <tr> | ||||
|     <td metal:use-macro="macros/minus"/> | ||||
|     <td colspan="3"> | ||||
|       <h2 i18n:translate="">Restrict to objects with certain states</h2> | ||||
|     </td> | ||||
|   <tr> | ||||
|     <td></td> | ||||
|     <th i18n:translate="">Workflow</th> | ||||
|     <th colspan="2" | ||||
|         i18n:translate="">States</th> | ||||
|   </tr> | ||||
|   <tr tal:repeat="def stateDefs"> | ||||
|     <td></td> | ||||
|     <td valign="top" | ||||
|         tal:content="def/name" | ||||
|         i18n:translate=""></td> | ||||
|     <td colspan="2"> | ||||
|       <tal:states repeat="state def/states"> | ||||
|         <tal:state define="name string:state.$deftype.${def/name}; | ||||
|                            value state/name"> | ||||
|             <input type="checkbox" | ||||
|                    tal:attributes="name string:$name:list; | ||||
|                                    value value; | ||||
|                                    checked python: | ||||
|                                        value in item.selectedStates.get(name, ()); | ||||
|                                    id string:$name.$value" | ||||
|                 /> <label tal:content="state/title" | ||||
|                                i18n:translate="" | ||||
|                                tal:attributes="for string:$name.$value" /> | ||||
|               | ||||
|         </tal:state> | ||||
|       </tal:states> | ||||
|     </td> | ||||
|   </tr> | ||||
| </metal:text> | ||||
| 
 | ||||
| 
 | ||||
| <td metal:define-macro="minus"> | ||||
|       <input type="button" value="−" | ||||
|              title="Remove search parameter" | ||||
|              tal:condition="python: | ||||
|                     param not in ['type', 'text', 'concept', 'state']" />  | ||||
| </td> | ||||
|  | @ -1,26 +0,0 @@ | |||
| # $Id$ | ||||
| 
 | ||||
| import unittest, doctest | ||||
| from zope.testing.doctestunit import DocFileSuite | ||||
| from zope.interface.verify import verifyClass | ||||
| 
 | ||||
| #from loops.search.interfaces import ... | ||||
| 
 | ||||
| 
 | ||||
| class Test(unittest.TestCase): | ||||
|     "Basic tests for the loops.search package." | ||||
| 
 | ||||
|     def testBase(self): | ||||
|         pass | ||||
| 
 | ||||
| 
 | ||||
| def test_suite(): | ||||
|     flags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS | ||||
|     return unittest.TestSuite(( | ||||
|                 unittest.makeSuite(Test), | ||||
|                 DocFileSuite('README.txt', | ||||
|                              optionflags=flags,), | ||||
|            )) | ||||
| 
 | ||||
| if __name__ == '__main__': | ||||
|     unittest.main(defaultTest='test_suite') | ||||
		Loading…
	
	Add table
		
		Reference in a new issue