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