introduced simple search using concept hierarchy
git-svn-id: svn://svn.cy55.de/Zope3/src/loops/trunk@1329 fd906abe-77d9-0310-91a1-e0d9ade77398
This commit is contained in:
		
							parent
							
								
									01680a666a
								
							
						
					
					
						commit
						029632fde7
					
				
					 5 changed files with 190 additions and 48 deletions
				
			
		| 
						 | 
					@ -553,7 +553,7 @@
 | 
				
			||||||
      for="loops.interfaces.INode"
 | 
					      for="loops.interfaces.INode"
 | 
				
			||||||
      class="loops.browser.node.InlineEdit"
 | 
					      class="loops.browser.node.InlineEdit"
 | 
				
			||||||
      attribute="save"
 | 
					      attribute="save"
 | 
				
			||||||
      permission="zope.View"
 | 
					      permission="zope.ManageContent"
 | 
				
			||||||
      />
 | 
					      />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  <!-- render file or image assigned to a node as target -->
 | 
					  <!-- render file or image assigned to a node as target -->
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										2
									
								
								query.py
									
										
									
									
									
								
							
							
						
						
									
										2
									
								
								query.py
									
										
									
									
									
								
							| 
						 | 
					@ -53,7 +53,7 @@ class IQueryConcept(Interface):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    viewName = schema.TextLine(
 | 
					    viewName = schema.TextLine(
 | 
				
			||||||
        title=_(u'Adapter/View name'),
 | 
					        title=_(u'Adapter/View name'),
 | 
				
			||||||
        description=_(u'The name of the (mulit-) adapter (typically a view) '
 | 
					        description=_(u'The name of the (multi-) adapter (typically a view) '
 | 
				
			||||||
                       'to be used for the query and for presenting '
 | 
					                       'to be used for the query and for presenting '
 | 
				
			||||||
                       'the results'),
 | 
					                       'the results'),
 | 
				
			||||||
        default=u'',
 | 
					        default=u'',
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -50,6 +50,9 @@ In addition we create a concept that holds the search page and a node
 | 
				
			||||||
  >>> page = views['page'] = Node('Search Page')
 | 
					  >>> page = views['page'] = Node('Search Page')
 | 
				
			||||||
  >>> page.target = search
 | 
					  >>> page.target = search
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Search views
 | 
				
			||||||
 | 
					------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Now we are ready to create a search view object:
 | 
					Now we are ready to create a search view object:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  >>> from zope.publisher.browser import TestRequest
 | 
					  >>> from zope.publisher.browser import TestRequest
 | 
				
			||||||
| 
						 | 
					@ -65,6 +68,25 @@ accessed exactly once per row:
 | 
				
			||||||
  >>> searchView.rowNum
 | 
					  >>> searchView.rowNum
 | 
				
			||||||
  2
 | 
					  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)
 | 
				
			||||||
 | 
					  11
 | 
				
			||||||
 | 
					  >>> t.getTermByToken('loops:resource:*').title
 | 
				
			||||||
 | 
					  'Any Resource'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  >>> t = searchView.conceptTypesForSearch()
 | 
				
			||||||
 | 
					  >>> len(t)
 | 
				
			||||||
 | 
					  5
 | 
				
			||||||
 | 
					  >>> t.getTermByToken('loops:concept:*').title
 | 
				
			||||||
 | 
					  'Any Concept'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
To execute the search in the context of a node we have to set up a node
 | 
					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
 | 
					view for our page. The submitReplacing method returns a JavaScript call
 | 
				
			||||||
that will replace the results part on the search page; as this registers
 | 
					that will replace the results part on the search page; as this registers
 | 
				
			||||||
| 
						 | 
					@ -82,22 +104,86 @@ a controller attribute for the search view.
 | 
				
			||||||
  'return submitReplacing("1.results", "1.search.form",
 | 
					  'return submitReplacing("1.results", "1.search.form",
 | 
				
			||||||
       "http://127.0.0.1/loops/views/page/.target19/@@searchresults.html")'
 | 
					       "http://127.0.0.1/loops/views/page/.target19/@@searchresults.html")'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Basic (text/title) search
 | 
				
			||||||
 | 
					-------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
The searchresults.html view, i.e. the SearchResults view class provides the
 | 
					The searchresults.html view, i.e. the SearchResults view class provides the
 | 
				
			||||||
result set of the search via its `results` property.
 | 
					result set of the search via its `results` property.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  >>> from loops.search.browser import SearchResults
 | 
					Before accessing the `results` property we have to prepare a (for testing
 | 
				
			||||||
  >>> form = {'search.2.title': 'yes', 'search.2.text': u'foo' }
 | 
					purposes fairly primitive) catalog and a resource we can search for:
 | 
				
			||||||
  >>> request = TestRequest(form=form)
 | 
					 | 
				
			||||||
  >>> resultsView = SearchResults(page, request)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Before accessing the `results` property we have to prepare a catalog.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  >>> from zope.app.catalog.interfaces import ICatalog
 | 
					  >>> from zope.app.catalog.interfaces import ICatalog
 | 
				
			||||||
  >>> class DummyCat(object):
 | 
					  >>> class DummyCat(object):
 | 
				
			||||||
  ...     implements(ICatalog)
 | 
					  ...     implements(ICatalog)
 | 
				
			||||||
  ...     def searchResults(self, **criteria):
 | 
					  ...     def searchResults(self, **criteria):
 | 
				
			||||||
 | 
					  ...         name = criteria.get('loops_title')
 | 
				
			||||||
 | 
					  ...         type = criteria.get('loops_type', ('resource',))
 | 
				
			||||||
 | 
					  ...         if name:
 | 
				
			||||||
 | 
					  ...              if 'concept' in type[0]:
 | 
				
			||||||
 | 
					  ...                  result = concepts.get(name)
 | 
				
			||||||
 | 
					  ...              else:
 | 
				
			||||||
 | 
					  ...                  result = resources.get(name)
 | 
				
			||||||
 | 
					  ...              if result:
 | 
				
			||||||
 | 
					  ...                  return [result]
 | 
				
			||||||
  ...         return []
 | 
					  ...         return []
 | 
				
			||||||
  >>> component.provideUtility(DummyCat())
 | 
					  >>> component.provideUtility(DummyCat())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  >>> list(resultsView.results)
 | 
					  >>> from loops.resource import Resource
 | 
				
			||||||
  []
 | 
					  >>> rplone = resources['plone'] = Resource()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  >>> from loops.search.browser 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('Zope')
 | 
				
			||||||
 | 
					  >>> czope2 = concepts['zope2'] = Concept('Zope 2')
 | 
				
			||||||
 | 
					  >>> czope3 = concepts['zope3'] = Concept('Zope 3')
 | 
				
			||||||
 | 
					  >>> cplone = concepts['plone'] = Concept('Plone')
 | 
				
			||||||
 | 
					  >>> for c in (czope, czope2, czope3, cplone):
 | 
				
			||||||
 | 
					  ...     c.conceptType = topic
 | 
				
			||||||
 | 
					  >>> 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:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  >>> form = {'search.3.type': 'loops:concept:topic', 'search.3.text': u'zope'}
 | 
				
			||||||
 | 
					  >>> request = TestRequest(form=form)
 | 
				
			||||||
 | 
					  >>> resultsView = SearchResults(page, request)
 | 
				
			||||||
 | 
					  >>> results = list(resultsView.results)
 | 
				
			||||||
 | 
					  >>> len(results)
 | 
				
			||||||
 | 
					  5
 | 
				
			||||||
 | 
					  >>> results[0].context.__name__
 | 
				
			||||||
 | 
					  u'plone'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  >>> form = {'search.3.type': 'loops:concept:topic', 'search.3.text': u'zope3'}
 | 
				
			||||||
 | 
					  >>> request = TestRequest(form=form)
 | 
				
			||||||
 | 
					  >>> resultsView = SearchResults(page, request)
 | 
				
			||||||
 | 
					  >>> results = list(resultsView.results)
 | 
				
			||||||
 | 
					  >>> len(results)
 | 
				
			||||||
 | 
					  1
 | 
				
			||||||
 | 
					  >>> results[0].context.__name__
 | 
				
			||||||
 | 
					  u'zope3'
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -31,7 +31,9 @@ from zope.formlib.namedtemplate import NamedTemplate, NamedTemplateImplementatio
 | 
				
			||||||
from zope.i18nmessageid import MessageFactory
 | 
					from zope.i18nmessageid import MessageFactory
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from cybertools.ajax import innerHtml
 | 
					from cybertools.ajax import innerHtml
 | 
				
			||||||
 | 
					from cybertools.typology.interfaces import ITypeManager
 | 
				
			||||||
from loops.browser.common import BaseView
 | 
					from loops.browser.common import BaseView
 | 
				
			||||||
 | 
					from loops import util
 | 
				
			||||||
 | 
					
 | 
				
			||||||
_ = MessageFactory('zope')
 | 
					_ = MessageFactory('zope')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -59,6 +61,12 @@ class Search(BaseView):
 | 
				
			||||||
        self.maxRowNum = n
 | 
					        self.maxRowNum = n
 | 
				
			||||||
        return n
 | 
					        return n
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def conceptTypesForSearch(self):
 | 
				
			||||||
 | 
					        general = [('loops:concept:*', 'Any Concept'),]
 | 
				
			||||||
 | 
					        return util.KeywordVocabulary(general + sorted([(t.tokenForSearch, t.title)
 | 
				
			||||||
 | 
					                        for t in ITypeManager(self.context).types
 | 
				
			||||||
 | 
					                            if 'concept' in t.qualifiers]))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def submitReplacing(self, targetId, formId, view):
 | 
					    def submitReplacing(self, targetId, formId, view):
 | 
				
			||||||
        self.registerDojo()
 | 
					        self.registerDojo()
 | 
				
			||||||
        return 'return submitReplacing("%s", "%s", "%s")' % (
 | 
					        return 'return submitReplacing("%s", "%s", "%s")' % (
 | 
				
			||||||
| 
						 | 
					@ -76,31 +84,73 @@ class SearchResults(BaseView):
 | 
				
			||||||
    def __call__(self):
 | 
					    def __call__(self):
 | 
				
			||||||
        return innerHtml(self)
 | 
					        return innerHtml(self)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Lazy
 | 
				
			||||||
 | 
					    def catalog(self):
 | 
				
			||||||
 | 
					        return component.getUtility(ICatalog)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Lazy
 | 
					    @Lazy
 | 
				
			||||||
    def results(self):
 | 
					    def results(self):
 | 
				
			||||||
 | 
					        result = set()
 | 
				
			||||||
        request = self.request
 | 
					        request = self.request
 | 
				
			||||||
 | 
					        r3 = self.queryConcepts()
 | 
				
			||||||
        type = request.get('search.1.text', 'loops:resource:*')
 | 
					        type = request.get('search.1.text', 'loops:resource:*')
 | 
				
			||||||
        text = request.get('search.2.text')
 | 
					        text = request.get('search.2.text')
 | 
				
			||||||
        if not text and '*' in type: # there should be some sort of selection...
 | 
					        if not r3 and not text and '*' in type: # there should be some sort of selection...
 | 
				
			||||||
            return set()
 | 
					            return result
 | 
				
			||||||
        useTitle = request.get('search.2.title')
 | 
					        if text or not '*' in type:
 | 
				
			||||||
        useFull = request.get('search.2.full')
 | 
					            useTitle = request.get('search.2.title')
 | 
				
			||||||
        r1 = set()
 | 
					            useFull = request.get('search.2.full')
 | 
				
			||||||
        cat = component.getUtility(ICatalog)
 | 
					            r1 = set()
 | 
				
			||||||
        if useFull and text and not type.startswith('loops:concept:'):
 | 
					            cat = self.catalog
 | 
				
			||||||
            criteria = {'loops_resource_textng': {'query': text},}
 | 
					            if useFull and text and not type.startswith('loops:concept:'):
 | 
				
			||||||
            r1 = set(cat.searchResults(**criteria))
 | 
					                criteria = {'loops_resource_textng': {'query': text},}
 | 
				
			||||||
 | 
					                r1 = set(cat.searchResults(**criteria))
 | 
				
			||||||
 | 
					            if type.endswith('*'):
 | 
				
			||||||
 | 
					                start = type[:-1]
 | 
				
			||||||
 | 
					                end = start + '\x7f'
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                start = end = type
 | 
				
			||||||
 | 
					            criteria = {'loops_type': (start, end),}
 | 
				
			||||||
 | 
					            if useTitle and text:
 | 
				
			||||||
 | 
					                criteria['loops_title'] = text
 | 
				
			||||||
 | 
					            r2 = set(cat.searchResults(**criteria))
 | 
				
			||||||
 | 
					            result = r1.union(r2)
 | 
				
			||||||
 | 
					            result = set(r for r in result if r.getLoopsRoot() == self.loopsRoot)
 | 
				
			||||||
 | 
					        if r3 is not None:
 | 
				
			||||||
 | 
					            if result:
 | 
				
			||||||
 | 
					                result = result.intersection(r3)
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                result = r3
 | 
				
			||||||
 | 
					        result = sorted(result, key=lambda x: x.title.lower())
 | 
				
			||||||
 | 
					        return self.viewIterator(result)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def queryConcepts(self):
 | 
				
			||||||
 | 
					        result = set()
 | 
				
			||||||
 | 
					        cat = self.catalog
 | 
				
			||||||
 | 
					        request = self.request
 | 
				
			||||||
 | 
					        type = request.get('search.3.type', 'loops:concept:*')
 | 
				
			||||||
 | 
					        text = request.get('search.3.text')
 | 
				
			||||||
 | 
					        if not text and '*' in type:
 | 
				
			||||||
 | 
					            return None
 | 
				
			||||||
        if type.endswith('*'):
 | 
					        if type.endswith('*'):
 | 
				
			||||||
            start = type[:-1]
 | 
					            start = type[:-1]
 | 
				
			||||||
            end = start + '\x7f'
 | 
					            end = start + '\x7f'
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            start = end = type
 | 
					            start = end = type
 | 
				
			||||||
        criteria = {'loops_type': (start, end),}
 | 
					        criteria = {'loops_type': (start, end),}
 | 
				
			||||||
        if useTitle and text:
 | 
					        if text:
 | 
				
			||||||
            criteria['loops_title'] = text
 | 
					            criteria['loops_title'] = text
 | 
				
			||||||
        r2 = set(cat.searchResults(**criteria))
 | 
					        queue = list(cat.searchResults(**criteria))
 | 
				
			||||||
        result = r1.union(r2)
 | 
					        concepts = []
 | 
				
			||||||
        result = [r for r in result if r.getLoopsRoot() == self.loopsRoot]
 | 
					        while queue:
 | 
				
			||||||
        result.sort(key=lambda x: x.title.lower())
 | 
					            c = queue.pop(0)
 | 
				
			||||||
        return self.viewIterator(result)
 | 
					            concepts.append(c)
 | 
				
			||||||
 | 
					            for child in c.getChildren():
 | 
				
			||||||
 | 
					                # TODO: check for tree level, use relevance factors, ...
 | 
				
			||||||
 | 
					                if child not in queue and child not in concepts:
 | 
				
			||||||
 | 
					                    queue.append(child)
 | 
				
			||||||
 | 
					        for c in concepts:
 | 
				
			||||||
 | 
					            result.add(c)
 | 
				
			||||||
 | 
					            result.update(c.getResources())
 | 
				
			||||||
 | 
					        return result
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -17,7 +17,7 @@
 | 
				
			||||||
          <input type="hidden" name="search.resultsId" value="1.results"
 | 
					          <input type="hidden" name="search.resultsId" value="1.results"
 | 
				
			||||||
                 tal:attributes="value resultsId" />
 | 
					                 tal:attributes="value resultsId" />
 | 
				
			||||||
          <table cellpadding="3">
 | 
					          <table cellpadding="3">
 | 
				
			||||||
            <tal:block repeat="search_param python: ['type', 'text']">
 | 
					            <tal:block repeat="search_param python: ['type', 'text', 'concept']">
 | 
				
			||||||
              <metal:row use-macro="macros/search_row" />
 | 
					              <metal:row use-macro="macros/search_row" />
 | 
				
			||||||
            </tal:block>
 | 
					            </tal:block>
 | 
				
			||||||
            <tr tal:condition="nothing">
 | 
					            <tr tal:condition="nothing">
 | 
				
			||||||
| 
						 | 
					@ -68,7 +68,7 @@
 | 
				
			||||||
                           value param" />
 | 
					                           value param" />
 | 
				
			||||||
    <input type="button" value="−"
 | 
					    <input type="button" value="−"
 | 
				
			||||||
           title="Remove search parameter"
 | 
					           title="Remove search parameter"
 | 
				
			||||||
           tal:condition="python: param not in ['type', 'text']" /> 
 | 
					           tal:condition="python: param not in ['type', 'text', 'concept']" /> 
 | 
				
			||||||
  </td>
 | 
					  </td>
 | 
				
			||||||
  <td>
 | 
					  <td>
 | 
				
			||||||
    <div metal:use-macro="macros/?param" />
 | 
					    <div metal:use-macro="macros/?param" />
 | 
				
			||||||
| 
						 | 
					@ -84,7 +84,7 @@
 | 
				
			||||||
    <select name="text"
 | 
					    <select name="text"
 | 
				
			||||||
            tal:attributes="name string:$namePrefix.text;
 | 
					            tal:attributes="name string:$namePrefix.text;
 | 
				
			||||||
                            id string:$idPrefix.text;">
 | 
					                            id string:$idPrefix.text;">
 | 
				
			||||||
      <tal:types repeat="type view/typesForSearch">
 | 
					      <tal:types repeat="type item/typesForSearch">
 | 
				
			||||||
        <option value="loops:*"
 | 
					        <option value="loops:*"
 | 
				
			||||||
                i18n:translate=""
 | 
					                i18n:translate=""
 | 
				
			||||||
                tal:attributes="value type/token"
 | 
					                tal:attributes="value type/token"
 | 
				
			||||||
| 
						 | 
					@ -114,7 +114,7 @@
 | 
				
			||||||
    <label for="full"
 | 
					    <label for="full"
 | 
				
			||||||
           tal:attributes="for string:$idPrefix.full">Full text</label>  
 | 
					           tal:attributes="for string:$idPrefix.full">Full text</label>  
 | 
				
			||||||
    <label for="text"
 | 
					    <label for="text"
 | 
				
			||||||
           tal:attributes="for string:$idPrefix.text">Search words:</label>
 | 
					           tal:attributes="for string:$idPrefix.text">Search text:</label>
 | 
				
			||||||
    <input type="text" name="text"
 | 
					    <input type="text" name="text"
 | 
				
			||||||
           tal:attributes="name string:$namePrefix.text;
 | 
					           tal:attributes="name string:$namePrefix.text;
 | 
				
			||||||
                           id string:$idPrefix.text;" />
 | 
					                           id string:$idPrefix.text;" />
 | 
				
			||||||
| 
						 | 
					@ -125,23 +125,29 @@
 | 
				
			||||||
</metal:text>
 | 
					</metal:text>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<!-- obsolete -->
 | 
					<metal:text define-macro="concept">
 | 
				
			||||||
<tr metal:define-macro="search_row_selectable" id="search.row.1.1">
 | 
					  <div>
 | 
				
			||||||
  <td>
 | 
					    <h3>Search via related conceps</h3>
 | 
				
			||||||
    <input type="button" value="−"
 | 
					    <label for="type"
 | 
				
			||||||
           title="Remove search parameter" /> 
 | 
					           tal:attributes="for string:$idPrefix.type">Type:</label>
 | 
				
			||||||
  </td>
 | 
					    <select name="type"
 | 
				
			||||||
  <td tal:define="selection search_selection | string:type">
 | 
					            tal:attributes="name string:$namePrefix.type;
 | 
				
			||||||
    <select>
 | 
					                            id string:$idPrefix.type;">
 | 
				
			||||||
      <option value="type"
 | 
					      <tal:types repeat="type item/conceptTypesForSearch">
 | 
				
			||||||
              tal:attributes="selected python: selection=='type'">Type</option>
 | 
					        <option value="loops:*"
 | 
				
			||||||
      <option value="text"
 | 
					                i18n:translate=""
 | 
				
			||||||
              tal:attributes="selected python: selection=='text'">Text</option>
 | 
					                tal:attributes="value type/token"
 | 
				
			||||||
      <option value="attribute">Attribute value</option>
 | 
					                tal:content="type/title">Topic</option>
 | 
				
			||||||
      <option value="concept">Concept</option>
 | 
					      </tal:types>
 | 
				
			||||||
    </select>
 | 
					    </select>  
 | 
				
			||||||
    <input metal:use-macro="template/macros/?selection" />
 | 
					    <label for="text"
 | 
				
			||||||
  </td>
 | 
					           tal:attributes="for string:$idPrefix.text">Search text:</label>
 | 
				
			||||||
</tr>
 | 
					    <input type="text" name="text"
 | 
				
			||||||
 | 
					           tal:attributes="name string:$namePrefix.text;
 | 
				
			||||||
 | 
					                           id string:$idPrefix.text;" />
 | 
				
			||||||
 | 
					    <input type="button" value="+"
 | 
				
			||||||
 | 
					           title="Add type"
 | 
				
			||||||
 | 
					           tal:condition="nothing" /> 
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					</metal:text>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		
		Reference in a new issue