You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@bloodhound.apache.org by ju...@apache.org on 2013/02/05 15:22:55 UTC
svn commit: r1442601 [1/2] - in
/incubator/bloodhound/branches/bep_0003_multiproduct: ./
bloodhound_dashboard/ bloodhound_dashboard/bhdashboard/htdocs/js/
bloodhound_multiproduct/ bloodhound_search/ bloodhound_search/bhsearch/
bloodhound_search/bhsearc...
Author: jure
Date: Tue Feb 5 14:22:55 2013
New Revision: 1442601
URL: http://svn.apache.org/viewvc?rev=1442601&view=rev
Log:
Sync merge from trunk
Added:
incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_dashboard/bhdashboard/htdocs/js/bloodhound-stickyscroll.js
- copied unchanged from r1442600, incubator/bloodhound/trunk/bloodhound_dashboard/bhdashboard/htdocs/js/bloodhound-stickyscroll.js
incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/tests/base.py
- copied unchanged from r1442600, incubator/bloodhound/trunk/bloodhound_search/bhsearch/tests/base.py
Removed:
incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/tests/utils.py
Modified:
incubator/bloodhound/branches/bep_0003_multiproduct/ (props changed)
incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_dashboard/ (props changed)
incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_multiproduct/ (props changed)
incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/ (props changed)
incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/__init__.py
incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/api.py
incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/search_resources/base.py
incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/search_resources/milestone_search.py
incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/search_resources/ticket_search.py
incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/search_resources/wiki_search.py
incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/templates/bhsearch.html
incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/tests/api.py
incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/tests/index_with_whoosh.py
incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/tests/milestone_search.py
incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/tests/real_index_view.py
incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/tests/ticket_search.py
incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/tests/web_ui.py
incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/tests/whoosh_backend.py
incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/tests/wiki_search.py
incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/web_ui.py
incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/whoosh_backend.py
incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_theme/ (props changed)
incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_theme/bhtheme/htdocs/bloodhound.css
incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_theme/bhtheme/templates/bh_search.html
incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_theme/bhtheme/templates/bh_ticket.html
incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_theme/bhtheme/templates/bh_ticket_box.html
incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_theme/bhtheme/templates/bloodhound_theme.html
incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_theme/bhtheme/theme.py
incubator/bloodhound/branches/bep_0003_multiproduct/installer/ (props changed)
incubator/bloodhound/branches/bep_0003_multiproduct/installer/bloodhound_setup.py
incubator/bloodhound/branches/bep_0003_multiproduct/trac/ (props changed)
incubator/bloodhound/branches/bep_0003_multiproduct/trac/trac/web/chrome.py
Propchange: incubator/bloodhound/branches/bep_0003_multiproduct/
------------------------------------------------------------------------------
--- svn:ignore (original)
+++ svn:ignore Tue Feb 5 14:22:55 2013
@@ -1 +1,7 @@
*.DS_Store
+.idea
+.project
+.pydev
+.git
+.gitignore
+.hg
Propchange: incubator/bloodhound/branches/bep_0003_multiproduct/
------------------------------------------------------------------------------
Merged /incubator/bloodhound/trunk:r1440985-1442600
Propchange: incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_dashboard/
------------------------------------------------------------------------------
--- svn:ignore (added)
+++ svn:ignore Tue Feb 5 14:22:55 2013
@@ -0,0 +1 @@
+*.egg-info
Propchange: incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_multiproduct/
------------------------------------------------------------------------------
--- svn:ignore (added)
+++ svn:ignore Tue Feb 5 14:22:55 2013
@@ -0,0 +1 @@
+*.egg-info
Propchange: incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/
------------------------------------------------------------------------------
--- svn:ignore (added)
+++ svn:ignore Tue Feb 5 14:22:55 2013
@@ -0,0 +1 @@
+*.egg-info
Modified: incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/__init__.py
URL: http://svn.apache.org/viewvc/incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/__init__.py?rev=1442601&r1=1442600&r2=1442601&view=diff
==============================================================================
--- incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/__init__.py (original)
+++ incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/__init__.py Tue Feb 5 14:22:55 2013
@@ -35,3 +35,4 @@ except Exception, exc:
# raise
msg = "Exception %s raised: '%s'" % (exc.__class__.__name__, str(exc))
+BHSEARCH_CONFIG_SECTION = "bhsearch"
Modified: incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/api.py
URL: http://svn.apache.org/viewvc/incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/api.py?rev=1442601&r1=1442600&r2=1442601&view=diff
==============================================================================
--- incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/api.py (original)
+++ incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/api.py Tue Feb 5 14:22:55 2013
@@ -33,10 +33,6 @@ class IndexFields(object):
AUTHOR = 'author'
CONTENT = 'content'
STATUS = 'status'
- DUE = 'due'
- COMPLETED = 'completed'
- MILESTONE = 'milestone'
- COMPONENT = 'component'
class QueryResult(object):
def __init__(self):
@@ -89,8 +85,14 @@ class ISearchBackend(Interface):
Open existing index, if index does not exist, create new one
"""
- def query(query, sort = None, fields = None, boost = None, filter = None,
- facets = None, pagenum = 1, pagelen = 20):
+ def query(
+ query,
+ sort = None,
+ fields = None,
+ filter = None,
+ facets = None,
+ pagenum = 1,
+ pagelen = 20):
"""
Perform query implementation
@@ -127,10 +129,17 @@ class ISearchParticipant(Interface):
Passes the request object to do permission checking."""
def get_title():
- """Return resource title"""
+ """Return resource title."""
def get_default_facets():
- """Return default facets for the specific resource type"""
+ """Return default facets for the specific resource type."""
+
+ def get_default_view():
+ """Return True if grid is enabled by default for specific resource."""
+
+ def get_default_view_fields(view):
+ """Return list of fields should be returned in grid by default."""
+
class IQueryParser(Interface):
"""Extension point for Bloodhound Search query parser.
@@ -185,9 +194,15 @@ class BloodhoundSearchApi(Component):
index_participants = ExtensionPoint(IIndexParticipant)
- def query(self, query, sort = None, fields = None,
- boost = None, filter = None,
- facets = None, pagenum = 1, pagelen = 20):
+ def query(
+ self,
+ query,
+ sort = None,
+ fields = None,
+ filter = None,
+ facets = None,
+ pagenum = 1,
+ pagelen = 20):
"""Return query result from an underlying search backend.
Arguments:
@@ -221,7 +236,6 @@ class BloodhoundSearchApi(Component):
facets = facets,
pagenum = pagenum,
pagelen = pagelen,
- boost = boost,
)
for query_processor in self.query_processors:
query_processor.query_pre_process(query_parameters)
Modified: incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/search_resources/base.py
URL: http://svn.apache.org/viewvc/incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/search_resources/base.py?rev=1442601&r1=1442600&r2=1442601&view=diff
==============================================================================
--- incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/search_resources/base.py (original)
+++ incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/search_resources/base.py Tue Feb 5 14:22:55 2013
@@ -29,3 +29,19 @@ class BaseIndexer(Component):
silence_on_error = BoolOption('bhsearch', 'silence_on_error', "True",
"""If true, do not throw an exception during indexing a resource""")
+
+class BaseSearchParticipant(Component):
+ default_view = None
+ default_grid_fields = None
+ default_facets = None
+
+ def get_default_facets(self):
+ return self.default_facets
+
+ def get_default_view(self):
+ return self.default_view
+
+ def get_default_view_fields(self, view):
+ if view == "grid":
+ return self.default_grid_fields
+ return None
Modified: incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/search_resources/milestone_search.py
URL: http://svn.apache.org/viewvc/incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/search_resources/milestone_search.py?rev=1442601&r1=1442600&r2=1442601&view=diff
==============================================================================
--- incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/search_resources/milestone_search.py (original)
+++ incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/search_resources/milestone_search.py Tue Feb 5 14:22:55 2013
@@ -19,25 +19,29 @@
# under the License.
r"""Milestone specifics for Bloodhound Search plugin."""
-from bhsearch.api import IIndexParticipant, BloodhoundSearchApi, IndexFields, \
- ISearchParticipant
-from bhsearch.search_resources.base import BaseIndexer
+from bhsearch import BHSEARCH_CONFIG_SECTION
+from bhsearch.api import (IIndexParticipant, BloodhoundSearchApi, IndexFields,
+ ISearchParticipant)
+from bhsearch.search_resources.base import BaseIndexer, BaseSearchParticipant
from trac.ticket import IMilestoneChangeListener, Milestone
-from trac.config import ListOption
-from trac.core import implements, Component
+from trac.config import ListOption, Option
+from trac.core import implements
MILESTONE_TYPE = u"milestone"
+class MilestoneFields(IndexFields):
+ DUE = "due"
+ COMPLETED = "completed"
+
class MilestoneIndexer(BaseIndexer):
implements(IMilestoneChangeListener, IIndexParticipant)
optional_fields = {
- 'description': IndexFields.CONTENT,
- 'due': IndexFields.DUE,
- 'completed': IndexFields.COMPLETED,
+ 'description': MilestoneFields.CONTENT,
+ 'due': MilestoneFields.DUE,
+ 'completed': MilestoneFields.COMPLETED,
}
-
# IMilestoneChangeListener methods
def milestone_created(self, milestone):
self._index_milestone(milestone)
@@ -110,11 +114,31 @@ class MilestoneIndexer(BaseIndexer):
for milestone in Milestone.select(self.env, include_completed=True):
yield self.build_doc(milestone)
-class MilestoneSearchParticipant(Component):
+class MilestoneSearchParticipant(BaseSearchParticipant):
implements(ISearchParticipant)
- default_facets = ListOption('bhsearch', 'default_facets_milestone',
- doc="""Default facets applied to search through milestones""")
+ default_facets = []
+ default_grid_fields = [
+ MilestoneFields.ID, MilestoneFields.DUE, MilestoneFields.COMPLETED]
+ prefix = MILESTONE_TYPE
+
+ default_facets = ListOption(
+ BHSEARCH_CONFIG_SECTION,
+ prefix + '_default_facets',
+ default=",".join(default_facets),
+ doc="""Default facets applied to search view of specific resource""")
+
+ default_view = Option(
+ BHSEARCH_CONFIG_SECTION,
+ prefix + '_default_view',
+ doc = """If true, show grid as default view for specific resource in
+ Bloodhound Search results""")
+
+ default_grid_fields = ListOption(
+ BHSEARCH_CONFIG_SECTION,
+ prefix + '_default_grid_fields',
+ default=",".join(default_grid_fields),
+ doc="""Default fields for grid view for specific resource""")
#ISearchParticipant members
def get_search_filters(self, req=None):
@@ -124,9 +148,6 @@ class MilestoneSearchParticipant(Compone
def get_title(self):
return "Milestone"
- def get_default_facets(self):
- return self.default_facets
-
def format_search_results(self, res):
#TODO: add better milestone rendering
return u'Milestone: %s' % res['id']
Modified: incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/search_resources/ticket_search.py
URL: http://svn.apache.org/viewvc/incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/search_resources/ticket_search.py?rev=1442601&r1=1442600&r2=1442601&view=diff
==============================================================================
--- incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/search_resources/ticket_search.py (original)
+++ incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/search_resources/ticket_search.py Tue Feb 5 14:22:55 2013
@@ -19,22 +19,42 @@
# under the License.
r"""Ticket specifics for Bloodhound Search plugin."""
+from bhsearch import BHSEARCH_CONFIG_SECTION
from bhsearch.api import ISearchParticipant, BloodhoundSearchApi, \
IIndexParticipant, IndexFields
-from bhsearch.search_resources.base import BaseIndexer
+from bhsearch.search_resources.base import BaseIndexer, BaseSearchParticipant
from genshi.builder import tag
from trac.ticket.api import ITicketChangeListener
from trac.ticket import Ticket
from trac.ticket.query import Query
-from trac.config import ListOption
-from trac.core import implements, Component
+from trac.config import ListOption, Option
+from trac.core import implements
TICKET_TYPE = u"ticket"
-TICKET_STATUS = u"status"
+
+class TicketFields(IndexFields):
+ SUMMARY = "summary"
+ MILESTONE = 'milestone'
+ COMPONENT = 'component'
+ KEYWORDS = "keywords"
+ RESOLUTION = "resolution"
+ CHANGES = 'changes'
class TicketIndexer(BaseIndexer):
implements(ITicketChangeListener, IIndexParticipant)
+ optional_fields = {
+ 'component': TicketFields.COMPONENT,
+ 'description': TicketFields.CONTENT,
+ 'keywords': TicketFields.KEYWORDS,
+ 'milestone': TicketFields.MILESTONE,
+ 'summary': TicketFields.SUMMARY,
+ 'status': TicketFields.STATUS,
+ 'resolution': TicketFields.RESOLUTION,
+ 'reporter': TicketFields.AUTHOR,
+ }
+
+
#ITicketChangeListener methods
def ticket_created(self, ticket):
"""Index a recently created ticket."""
@@ -75,28 +95,17 @@ class TicketIndexer(BaseIndexer):
def build_doc(self, trac_doc):
ticket = trac_doc
doc = {
- IndexFields.ID: unicode(ticket.id),
+ IndexFields.ID: str(ticket.id),
IndexFields.TYPE: TICKET_TYPE,
IndexFields.TIME: ticket.time_changed,
}
- fields = [
- ('component',),
- ('description',IndexFields.CONTENT),
- ('keywords',),
- ('milestone',),
- ('summary',),
- ('status', TICKET_STATUS),
- ('resolution',),
- ('reporter',IndexFields.AUTHOR),
- ]
- for f in fields:
- if f[0] in ticket.values:
- if len(f) == 1:
- doc[f[0]] = ticket.values[f[0]]
- elif len(f) == 2:
- doc[f[1]] = ticket.values[f[0]]
- doc['changes'] = u'\n\n'.join([x[4] for x in ticket.get_changelog()
- if x[2] == u'comment'])
+
+ for field, index_field in self.optional_fields.iteritems():
+ if field in ticket.values:
+ doc[index_field] = ticket.values[field]
+
+ doc[TicketFields.CHANGES] = u'\n\n'.join(
+ [x[4] for x in ticket.get_changelog() if x[2] == u'comment'])
return doc
def get_entries_for_index(self):
@@ -111,12 +120,40 @@ class TicketIndexer(BaseIndexer):
return query.execute()
-class TicketSearchParticipant(Component):
+class TicketSearchParticipant(BaseSearchParticipant):
implements(ISearchParticipant)
- default_facets = ListOption('bhsearch', 'default_facets_ticket',
- 'status,milestone,component',
- doc="""Default facets applied to search through tickets""")
+ default_facets = [
+ TicketFields.STATUS,
+ TicketFields.MILESTONE,
+ TicketFields.COMPONENT,
+ ]
+ default_grid_fields = [
+ TicketFields.ID,
+ TicketFields.SUMMARY,
+ TicketFields.STATUS,
+ TicketFields.MILESTONE,
+ TicketFields.COMPONENT,
+ ]
+ prefix = TICKET_TYPE
+
+ default_facets = ListOption(
+ BHSEARCH_CONFIG_SECTION,
+ prefix + '_default_facets',
+ default=",".join(default_facets),
+ doc="""Default facets applied to search view of specific resource""")
+
+ default_view = Option(
+ BHSEARCH_CONFIG_SECTION,
+ prefix + '_default_view',
+ doc = """If true, show grid as default view for specific resource in
+ Bloodhound Search results""")
+
+ default_grid_fields = ListOption(
+ BHSEARCH_CONFIG_SECTION,
+ prefix + '_default_grid_fields',
+ default = ",".join(default_grid_fields),
+ doc="""Default fields for grid view for specific resource""")
#ISearchParticipant members
def get_search_filters(self, req=None):
@@ -126,22 +163,19 @@ class TicketSearchParticipant(Component)
def get_title(self):
return "Ticket"
- def get_default_facets(self):
- return self.default_facets
-
def format_search_results(self, res):
- if not TICKET_STATUS in res:
+ if not TicketFields.STATUS in res:
stat = 'undefined_status'
css_class = 'undefined_status'
else:
- css_class = res[TICKET_STATUS]
- if res[TICKET_STATUS] == 'closed':
+ css_class = res[TicketFields.STATUS]
+ if res[TicketFields.STATUS] == 'closed':
resolution = ""
if 'resolution' in res:
resolution = res['resolution']
stat = '%s: %s' % (res['status'], resolution)
else:
- stat = res[TICKET_STATUS]
+ stat = res[TicketFields.STATUS]
id = tag(tag.span('#'+res['id'], class_=css_class))
return id + ': %s (%s)' % (res['summary'], stat)
Modified: incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/search_resources/wiki_search.py
URL: http://svn.apache.org/viewvc/incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/search_resources/wiki_search.py?rev=1442601&r1=1442600&r2=1442601&view=diff
==============================================================================
--- incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/search_resources/wiki_search.py (original)
+++ incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/search_resources/wiki_search.py Tue Feb 5 14:22:55 2013
@@ -19,11 +19,12 @@
# under the License.
r"""Wiki specifics for Bloodhound Search plugin."""
-from bhsearch.api import ISearchParticipant, BloodhoundSearchApi, \
- IIndexParticipant, IndexFields
-from bhsearch.search_resources.base import BaseIndexer
-from trac.core import implements, Component
-from trac.config import ListOption
+from bhsearch import BHSEARCH_CONFIG_SECTION
+from bhsearch.api import (ISearchParticipant, BloodhoundSearchApi,
+ IIndexParticipant, IndexFields)
+from bhsearch.search_resources.base import BaseIndexer, BaseSearchParticipant
+from trac.core import implements
+from trac.config import ListOption, Option
from trac.wiki import IWikiChangeListener, WikiSystem, WikiPage
WIKI_TYPE = u"wiki"
@@ -106,11 +107,34 @@ class WikiIndexer(BaseIndexer):
page = WikiPage(self.env, page_name)
yield self.build_doc(page)
-class WikiSearchParticipant(Component):
+class WikiSearchParticipant(BaseSearchParticipant):
implements(ISearchParticipant)
- default_facets = ListOption('bhsearch', 'default_facets_wiki',
- doc="""Default facets applied to search through wiki pages""")
+ default_facets = []
+ default_grid_fields = [
+ IndexFields.ID,
+ IndexFields.TIME,
+ IndexFields.AUTHOR
+ ]
+ prefix = WIKI_TYPE
+
+ default_facets = ListOption(
+ BHSEARCH_CONFIG_SECTION,
+ prefix + '_default_facets',
+ default=",".join(default_facets),
+ doc="""Default facets applied to search view of specific resource""")
+
+ default_view = Option(
+ BHSEARCH_CONFIG_SECTION,
+ prefix + '_default_view',
+ doc = """If true, show grid as default view for specific resource in
+ Bloodhound Search results""")
+
+ default_grid_fields = ListOption(
+ BHSEARCH_CONFIG_SECTION,
+ prefix + '_default_grid_fields',
+ default = ",".join(default_grid_fields),
+ doc="""Default fields for grid view for specific resource""")
#ISearchParticipant members
def get_search_filters(self, req=None):
@@ -120,9 +144,6 @@ class WikiSearchParticipant(Component):
def get_title(self):
return "Wiki"
- def get_default_facets(self):
- return self.default_facets
-
def format_search_results(self, res):
return u'%s: %s...' % (res['id'], res['content'][:50])
Modified: incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/templates/bhsearch.html
URL: http://svn.apache.org/viewvc/incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/templates/bhsearch.html?rev=1442601&r1=1442600&r2=1442601&view=diff
==============================================================================
--- incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/templates/bhsearch.html (original)
+++ incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/templates/bhsearch.html Tue Feb 5 14:22:55 2013
@@ -56,16 +56,17 @@
<body>
<div id="content" class="row">
- <h1>This page provides prototype functionality.</h1>
+ <h1>Advanced search</h1>
<form id="fullsearch" action="${href.bhsearch()}" method="get">
<p>
- <input type="text" id="q" name="q" size="40" value="${query}" />
+ <input type="text" id="q" name="q" size="60" value="${query}" />
<!--So far, we will not support noquickjump mode for form submission-->
<!--<input type="hidden" name="noquickjump" value="1" />-->
<input py:if="active_type" type="hidden" name="type" value="${active_type}" />
<py:for each="active_filter in active_filter_queries">
<input type="hidden" name="fq" value="${active_filter.query}" />
</py:for>
+ <input py:if="active_view" type="hidden" name="view" value="${active_view}" />
<input type="submit" value="${_('Search')}" />
</p>
</form>
@@ -99,21 +100,82 @@
<py:if test="results">
<div class="span8">
- <h2 py:if="results">
+ <h2>
Results <small>(${results.displayed_items()})</small>
</h2>
- <div>
- <dl id="results">
- <py:for each="result in results">
- <dt><a href="${result.href}" class="searchable">${result.title}</a></dt>
- <dd class="searchable">${result.excerpt}</dd>
- <dd>
- <py:if test="result.author"><span class="author" i18n:msg="author">By ${format_author(result.author)}</span> —</py:if>
- <span class="date">${result.date}</span>
- </dd>
- </py:for>
- </dl>
+ <div class="pull-right" py:if="all_views">
+ <!--TODO: change presentation. Current implementation is very basic. -->
+ View as:
+ <py:for each="idx, view in enumerate(all_views)">
+ <b py:if="view.is_active">${view.label}</b>
+ <a href="${view.href}" py:if="not view.is_active">${view.label}</a>
+ <py:if test="idx+1!=len(all_views)"> | </py:if>
+ </py:for>
+ </div>
+
+ <div>
+ <py:if test="view=='grid'">
+ <!--todo: implement rendering in pluggable manner-->
+ <!--Rendering results in grid view-->
+ <table class="listing tickets table table-bordered table-condensed query" style="border-radius: 0px 0px 4px 4px">
+ <!--render table header-->
+ <thead>
+ <tr class="trac-columns">
+ <th py:for="header in headers"
+ class="$header.name${(' desc' if header.sort=='DESC' else ' asc') if header.sort else ''}">
+ <?python asc = _('(ascending)'); desc = _('(descending)') ?>
+ <a title="${_('Sort by %(col)s %(direction)s', col=header.label,
+ direction=(desc if header.sort=='ASC' else asc))}"
+ href="$header.href">${header.label}</a>
+ </th>
+ </tr>
+ </thead>
+
+ <tbody>
+ <!--render table rows-->
+ <py:for each="idx, result in enumerate(results)">
+ <tr class="${'odd' if idx % 2 else 'even'} prio${result.priority_value}${
+ ' added' if 'added' in result else ''}${
+ ' changed' if 'changed' in result else ''}${
+ ' removed' if 'removed' in result else ''}">
+ <py:for each="idx, header in enumerate(headers)" py:choose="">
+ <py:with vars="name = header.name; value = result[name]; title = _('View ')+ result['type']">
+ <td py:when="name == 'id'" class="id"><a href="$result.href" title="${title}"
+ class="${classes(closed=result.status == 'closed')}">#$result.id</a></td>
+ <td py:otherwise="" class="$name" py:choose="">
+ <a py:when="name == 'summary'" href="$result.href" title="title">$value</a>
+ <py:when test="isinstance(value, datetime)">${pretty_dateinfo(value, dateonly=True)}</py:when>
+ <py:when test="name == 'reporter'">${authorinfo(value)}</py:when>
+ <py:when test="name == 'cc'">${format_emails(ticket_context, value)}</py:when>
+ <py:when test="name == 'owner' and value">${authorinfo(value)}</py:when>
+ <py:when test="name == 'milestone'"><a py:if="value" title="View milestone" href="${href.milestone(value)}">${value}</a></py:when>
+ <!--<py:when test="name == 'content'">${wiki_to_oneliner(ticket_context, value)}</py:when>-->
+ <py:when test="header.wikify">${wiki_to_oneliner(ticket_context, value)}</py:when>
+ <py:otherwise>$value</py:otherwise>
+ </td>
+ </py:with>
+ </py:for>
+ </tr>
+ </py:for>
+ </tbody>
+ </table>
+ </py:if>
+
+ <!--<py:if test="not headers">-->
+ <py:if test="not view">
+ <!--Rendering results in free form-->
+ <dl id="results">
+ <py:for each="result in results">
+ <dt><a href="${result.href}" class="searchable">${result.title}</a></dt>
+ <dd class="searchable">${result.excerpt}</dd>
+ <dd>
+ <py:if test="result.author"><span class="author" i18n:msg="author">By ${format_author(result.author)}</span> —</py:if>
+ <span class="date">${result.date}</span>
+ </dd>
+ </py:for>
+ </dl>
+ </py:if>
</div>
<xi:include py:with="paginator = results" href="bh_page_index.html" />
Modified: incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/tests/api.py
URL: http://svn.apache.org/viewvc/incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/tests/api.py?rev=1442601&r1=1442600&r2=1442601&view=diff
==============================================================================
--- incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/tests/api.py (original)
+++ incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/tests/api.py Tue Feb 5 14:22:55 2013
@@ -22,7 +22,7 @@ import tempfile
import shutil
from bhsearch.api import BloodhoundSearchApi, ASC
from bhsearch.query_parser import DefaultQueryParser
-from bhsearch.tests.utils import BaseBloodhoundSearchTest
+from bhsearch.tests.base import BaseBloodhoundSearchTest
from bhsearch.search_resources.ticket_search import TicketSearchParticipant
from bhsearch.whoosh_backend import WhooshBackend
Modified: incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/tests/index_with_whoosh.py
URL: http://svn.apache.org/viewvc/incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/tests/index_with_whoosh.py?rev=1442601&r1=1442600&r2=1442601&view=diff
==============================================================================
--- incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/tests/index_with_whoosh.py (original)
+++ incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/tests/index_with_whoosh.py Tue Feb 5 14:22:55 2013
@@ -22,14 +22,12 @@ import unittest
import tempfile
import shutil
from bhsearch.api import BloodhoundSearchApi
-from bhsearch.milestone_search import MilestoneIndexer
-from bhsearch.tests.utils import BaseBloodhoundSearchTest
+from bhsearch.search_resources.milestone_search import MilestoneIndexer
+from bhsearch.tests.base import BaseBloodhoundSearchTest
from bhsearch.search_resources.ticket_search import TicketIndexer
from bhsearch.whoosh_backend import WhooshBackend
-from bhsearch.search_resources.wiki_search import WikiIndexer
from trac.test import EnvironmentStub
-from trac.ticket.api import TicketSystem
class IndexWhooshTestCase(BaseBloodhoundSearchTest):
@@ -39,10 +37,6 @@ class IndexWhooshTestCase(BaseBloodhound
self.whoosh_backend = WhooshBackend(self.env)
self.whoosh_backend.recreate_index()
self.search_api = BloodhoundSearchApi(self.env)
- self.ticket_indexer = TicketIndexer(self.env)
- self.wiki_indexer = WikiIndexer(self.env)
- self.milestone_indexer = MilestoneIndexer(self.env)
- self.ticket_system = TicketSystem(self.env)
def tearDown(self):
shutil.rmtree(self.env.path)
@@ -51,7 +45,7 @@ class IndexWhooshTestCase(BaseBloodhound
def test_can_index_ticket(self):
ticket = self.create_dummy_ticket()
ticket.id = "1"
- self.ticket_indexer.ticket_created(ticket)
+ TicketIndexer(self.env).ticket_created(ticket)
results = self.search_api.query("*:*")
self.print_result(results)
@@ -112,6 +106,7 @@ class IndexWhooshTestCase(BaseBloodhound
self.assertEqual(2, results.hits)
def test_can_reindex_milestones(self):
+ MilestoneIndexer(self.env)
self.insert_milestone("M1")
self.insert_milestone("M2")
self.whoosh_backend.recreate_index()
Modified: incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/tests/milestone_search.py
URL: http://svn.apache.org/viewvc/incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/tests/milestone_search.py?rev=1442601&r1=1442600&r2=1442601&view=diff
==============================================================================
--- incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/tests/milestone_search.py (original)
+++ incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/tests/milestone_search.py Tue Feb 5 14:22:55 2013
@@ -17,17 +17,14 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-import shutil
import unittest
-import tempfile
from bhsearch.api import BloodhoundSearchApi
-from bhsearch.search_resources.milestone_search import MilestoneSearchParticipant
-from bhsearch.query_parser import DefaultQueryParser
-from bhsearch.tests.utils import BaseBloodhoundSearchTest
+from bhsearch.search_resources.milestone_search import (
+ MilestoneSearchParticipant)
+from bhsearch.tests.base import BaseBloodhoundSearchTest
from bhsearch.whoosh_backend import WhooshBackend
-from trac.test import EnvironmentStub
from trac.ticket import Milestone
@@ -35,18 +32,10 @@ class MilestoneIndexerEventsTestCase(Bas
DUMMY_MILESTONE_NAME = "dummyName"
def setUp(self):
- self.env = EnvironmentStub(enable=['bhsearch.*'])
- self.env.path = tempfile.mkdtemp('bhsearch-tempenv')
- self.env.config.set('bhsearch', 'silence_on_error', "False")
+ super(MilestoneIndexerEventsTestCase, self).setUp()
self.whoosh_backend = WhooshBackend(self.env)
self.whoosh_backend.recreate_index()
self.search_api = BloodhoundSearchApi(self.env)
- self.milestone_participant = MilestoneSearchParticipant(self.env)
- self.query_parser = DefaultQueryParser(self.env)
-
- def tearDown(self):
- shutil.rmtree(self.env.path)
- self.env.reset_db()
def test_can_index_created_milestone(self):
#arrange
@@ -132,10 +121,32 @@ class MilestoneIndexerEventsTestCase(Bas
self.assertEqual(self.DUMMY_MILESTONE_NAME, doc["id"])
self.assertEqual("milestone", doc["type"])
+class MilestoneSearchParticipantTestCase(BaseBloodhoundSearchTest):
+ def setUp(self):
+ super(MilestoneSearchParticipantTestCase, self).setUp()
+ self.milestone_search = MilestoneSearchParticipant(self.env)
+
+ def test_can_get_default_grid_fields(self):
+ grid_fields = self.milestone_search.get_default_view_fields("grid")
+ print grid_fields
+ self.assertGreater(len(grid_fields), 0)
+
+ def test_can_get_default_facets(self):
+ default_facets = self.milestone_search.get_default_facets()
+ print default_facets
+ self.assertIsNotNone(default_facets)
+
+ def test_can_get_is_grid_view_defaults(self):
+ default_grid_fields = self.milestone_search.get_default_view_fields(
+ "grid")
+ print default_grid_fields
+ self.assertIsNotNone(default_grid_fields)
def suite():
suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(MilestoneIndexerEventsTestCase, 'test'))
+ suite.addTest(
+ unittest.makeSuite(MilestoneSearchParticipantTestCase, 'test'))
return suite
if __name__ == '__main__':
Modified: incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/tests/real_index_view.py
URL: http://svn.apache.org/viewvc/incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/tests/real_index_view.py?rev=1442601&r1=1442600&r2=1442601&view=diff
==============================================================================
--- incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/tests/real_index_view.py (original)
+++ incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/tests/real_index_view.py Tue Feb 5 14:22:55 2013
@@ -20,7 +20,7 @@
import unittest
from bhsearch.web_ui import RequestParameters
import os
-from bhsearch.tests.utils import BaseBloodhoundSearchTest
+from bhsearch.tests.base import BaseBloodhoundSearchTest
from bhsearch.whoosh_backend import WhooshBackend
from trac.test import EnvironmentStub, Mock, MockPerm
Modified: incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/tests/ticket_search.py
URL: http://svn.apache.org/viewvc/incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/tests/ticket_search.py?rev=1442601&r1=1442600&r2=1442601&view=diff
==============================================================================
--- incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/tests/ticket_search.py (original)
+++ incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/tests/ticket_search.py Tue Feb 5 14:22:55 2013
@@ -20,7 +20,7 @@
import unittest
import tempfile
-from bhsearch.tests.utils import BaseBloodhoundSearchTest
+from bhsearch.tests.base import BaseBloodhoundSearchTest
from bhsearch.search_resources.ticket_search import TicketIndexer
from trac.test import EnvironmentStub
Modified: incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/tests/web_ui.py
URL: http://svn.apache.org/viewvc/incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/tests/web_ui.py?rev=1442601&r1=1442600&r2=1442601&view=diff
==============================================================================
--- incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/tests/web_ui.py (original)
+++ incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/tests/web_ui.py Tue Feb 5 14:22:55 2013
@@ -23,7 +23,7 @@ import shutil
from urllib import urlencode, unquote
-from bhsearch.tests.utils import BaseBloodhoundSearchTest
+from bhsearch.tests.base import BaseBloodhoundSearchTest
from bhsearch.web_ui import RequestParameters
from bhsearch.whoosh_backend import WhooshBackend
@@ -39,9 +39,7 @@ DEFAULT_DOCS_PER_PAGE = 10
class WebUiTestCaseWithWhoosh(BaseBloodhoundSearchTest):
def setUp(self):
- self.env = EnvironmentStub(enable=['trac.*', 'bhsearch.*'])
- self.env.path = tempfile.mkdtemp('bhsearch-tempenv')
-
+ super(WebUiTestCaseWithWhoosh, self).setUp(['trac.*', 'bhsearch.*'])
whoosh_backend = WhooshBackend(self.env)
whoosh_backend.recreate_index()
@@ -62,10 +60,6 @@ class WebUiTestCaseWithWhoosh(BaseBloodh
self.redirect_permanent = permanent
raise RequestDone
- def tearDown(self):
- shutil.rmtree(self.env.path)
- self.env.reset_db()
-
def test_can_process_empty_request(self):
data = self.process_request()
self.assertEqual("", data["query"])
@@ -441,6 +435,86 @@ class WebUiTestCaseWithWhoosh(BaseBloodh
self.assertEqual('T1 (new)', quick_jump_data["description"])
self.assertEqual('/main/ticket/1', quick_jump_data["href"])
+
+ def test_that_ticket_search_can_return_in_grid(self):
+ #arrange
+ self.env.config.set(
+ 'bhsearch',
+ 'ticket_is_grid_view_default',
+ 'True')
+ self.env.config.set(
+ 'bhsearch',
+ 'ticket_default_grid_fields',
+ 'id,status,milestone,component')
+ self.insert_ticket("T1", component="c1", status="new", milestone="A")
+ #act
+ self.req.args[RequestParameters.QUERY] = "*"
+ self.req.args[RequestParameters.TYPE] = "ticket"
+ self.req.args[RequestParameters.VIEW] = "grid"
+ data = self.process_request()
+ #assert
+ grid_data = data["headers"]
+ self.assertIsNotNone(grid_data)
+ fields = [column["name"] for column in grid_data]
+ self.assertEquals(["id", "status", "milestone", "component"], fields)
+
+ def test_that_grid_is_switched_off_by_default(self):
+ #arrange
+ self.insert_ticket("T1", component="c1", status="new", milestone="A")
+ #act
+ self.req.args[RequestParameters.QUERY] = "*"
+ data = self.process_request()
+ #assert
+ self.assertNotIn("headers", data)
+ self.assertNotIn("view", data)
+
+ def test_that_grid_is_switched_off_by_default_for_ticket(self):
+ #arrange
+ self.insert_ticket("T1", component="c1", status="new", milestone="A")
+ #act
+ self.req.args[RequestParameters.QUERY] = "*"
+ self.req.args[RequestParameters.TYPE] = "ticket"
+ data = self.process_request()
+ #assert
+ self.assertNotIn("headers", data)
+ self.assertNotIn("view", data)
+
+
+ def test_can_returns_all_views(self):
+ #arrange
+ self.insert_ticket("T1", component="c1", status="new", milestone="A")
+ #act
+ self.req.args[RequestParameters.QUERY] = "*"
+ data = self.process_request()
+ #assert
+ all_views = data["all_views"]
+ free_view = all_views[0]
+ self.assertTrue(free_view["is_active"])
+ self.assertNotIn("view=", free_view["href"])
+ grid = all_views[1]
+ self.assertFalse(grid["is_active"])
+ self.assertIn("view=grid", grid["href"])
+
+ def test_that_active_view_is_not_set_if_not_requested(self):
+ #arrange
+ self.insert_ticket("T1", component="c1", status="new", milestone="A")
+ #act
+ self.req.args[RequestParameters.QUERY] = "*"
+ data = self.process_request()
+ #assert
+ self.assertNotIn("active_view", data)
+
+ def test_that_active_view_is_set_if_requested(self):
+ #arrange
+ self.insert_ticket("T1", component="c1", status="new", milestone="A")
+ #act
+ self.req.args[RequestParameters.QUERY] = "*"
+ self.req.args[RequestParameters.VIEW] = "grid"
+ data = self.process_request()
+ #assert
+ self.assertEqual("grid", data["active_view"])
+
+
def _count_parameter_in_url(self, url, parameter_name, value):
parameter_to_find = (parameter_name, value)
parsed_parameters = parse_arg_list(url)
Modified: incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/tests/whoosh_backend.py
URL: http://svn.apache.org/viewvc/incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/tests/whoosh_backend.py?rev=1442601&r1=1442600&r2=1442601&view=diff
==============================================================================
--- incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/tests/whoosh_backend.py (original)
+++ incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/tests/whoosh_backend.py Tue Feb 5 14:22:55 2013
@@ -24,9 +24,9 @@ import tempfile
import shutil
from bhsearch.api import ASC, DESC, SCORE
from bhsearch.query_parser import DefaultQueryParser
-from bhsearch.tests.utils import BaseBloodhoundSearchTest
-from bhsearch.whoosh_backend import WhooshBackend, \
- WhooshEmptyFacetErrorWorkaround
+from bhsearch.tests.base import BaseBloodhoundSearchTest
+from bhsearch.whoosh_backend import (WhooshBackend,
+ WhooshEmptyFacetErrorWorkaround)
from trac.test import EnvironmentStub
from trac.util.datefmt import FixedOffset, utc
from whoosh import index, sorting, query
Modified: incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/tests/wiki_search.py
URL: http://svn.apache.org/viewvc/incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/tests/wiki_search.py?rev=1442601&r1=1442600&r2=1442601&view=diff
==============================================================================
--- incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/tests/wiki_search.py (original)
+++ incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/tests/wiki_search.py Tue Feb 5 14:22:55 2013
@@ -25,7 +25,8 @@ from bhsearch.api import BloodhoundSearc
from bhsearch.query_parser import DefaultQueryParser
from bhsearch.tests.utils import BaseBloodhoundSearchTest
from bhsearch.whoosh_backend import WhooshBackend
-from bhsearch.search_resources.wiki_search import WikiIndexer, WikiSearchParticipant
+from bhsearch.search_resources.wiki_search import (
+ WikiIndexer, WikiSearchParticipant)
from trac.test import EnvironmentStub
from trac.wiki import WikiSystem, WikiPage
Modified: incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/web_ui.py
URL: http://svn.apache.org/viewvc/incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/web_ui.py?rev=1442601&r1=1442600&r2=1442601&view=diff
==============================================================================
--- incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/web_ui.py (original)
+++ incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/web_ui.py Tue Feb 5 14:22:55 2013
@@ -22,28 +22,29 @@ r"""Bloodhound Search user interface."""
import copy
import pkg_resources
+from bhsearch import BHSEARCH_CONFIG_SECTION
import re
from trac.core import Component, implements, TracError
from genshi.builder import tag
from trac.perm import IPermissionRequestor
from trac.search import shorten_result
-from trac.config import OrderedExtensionsOption, ListOption
+from trac.config import OrderedExtensionsOption, ListOption, Option
from trac.util.presentation import Paginator
from trac.util.datefmt import format_datetime, user_time
from trac.web import IRequestHandler
from trac.util.translation import _
from trac.util.html import find_element
from trac.web.chrome import (INavigationContributor, ITemplateProvider,
- add_link, add_stylesheet, web_context)
+ add_link, add_stylesheet, web_context)
from bhsearch.api import (BloodhoundSearchApi, ISearchParticipant, SCORE, ASC,
- DESC, IndexFields)
+ DESC, IndexFields)
from trac.wiki.formatter import extract_link
SEARCH_PERMISSION = 'SEARCH_VIEW'
DEFAULT_RESULTS_PER_PAGE = 10
DEFAULT_SORT = [(SCORE, ASC), ("time", DESC)]
-
+#VIEW_GRID = "grid"
class RequestParameters(object):
"""
@@ -57,21 +58,23 @@ class RequestParameters(object):
NO_QUICK_JUMP = "noquickjump"
PAGELEN = "pagelen"
FILTER_QUERY = "fq"
+ VIEW = "view"
def __init__(self, req):
self.req = req
- self.query = req.args.getfirst(self.QUERY)
- if self.query is None:
+ self.original_query = req.args.getfirst(self.QUERY)
+ if self.original_query is None:
self.query = ""
else:
- self.query = self.query.strip()
+ self.query = self.original_query.strip()
self.no_quick_jump = int(req.args.getfirst(self.NO_QUICK_JUMP, '0'))
+ self.view = req.args.getfirst(self.VIEW)
self.filter_queries = req.args.getlist(self.FILTER_QUERY)
self.filter_queries = self._remove_possible_duplications(
- self.filter_queries)
+ self.filter_queries)
#TODO: retrieve sort from query string
self.sort = DEFAULT_SORT
@@ -86,11 +89,12 @@ class RequestParameters(object):
RequestParameters.FILTER_QUERY: []
}
+ if self.original_query is not None:
+ self.params[self.QUERY] = self.original_query
if self.no_quick_jump > 0:
self.params[self.NO_QUICK_JUMP] = self.no_quick_jump
-
- if self.query:
- self.params[self.QUERY] = self.query
+ if self.view is not None:
+ self.params[self.VIEW] = self.view
if self.pagelen != DEFAULT_RESULTS_PER_PAGE:
self.params[self.PAGELEN] = self.pagelen
if self.page > 1:
@@ -107,31 +111,36 @@ class RequestParameters(object):
def create_href(
self,
- page = None,
+ page=None,
type=None,
- skip_type = False,
- skip_page = False,
- additional_filter = None,
- force_filters = None,
- ):
+ skip_type=False,
+ skip_page=False,
+ additional_filter=None,
+ force_filters=None,
+ view=None,
+ skip_view=False,
+ ):
params = copy.deepcopy(self.params)
#noquickjump parameter should be always set to 1 for urls
params[self.NO_QUICK_JUMP] = 1
- if page:
+ if skip_view:
+ self._delete_if_exists(params, self.VIEW)
+ elif view:
+ params[self.VIEW] = view
+
+ if skip_page:
+ self._delete_if_exists(params, self.PAGE)
+ elif page:
params[self.PAGE] = page
- if skip_page and self.PAGE in params:
- del(params[self.PAGE])
-
- if type:
+ if skip_type:
+ self._delete_if_exists(params, self.TYPE)
+ elif type:
params[self.TYPE] = type
- if skip_type and self.TYPE in params:
- del(params[self.TYPE])
-
- if additional_filter and \
+ if additional_filter and\
additional_filter not in params[self.FILTER_QUERY]:
params[self.FILTER_QUERY].append(additional_filter)
elif force_filters is not None:
@@ -139,15 +148,33 @@ class RequestParameters(object):
return self.req.href.bhsearch(**params)
+ def _delete_if_exists(self, params, name):
+ if name in params:
+ del params[name]
+
def is_show_all_mode(self):
return self.type is None
+
class BloodhoundSearchModule(Component):
"""Main search page"""
+ DATA_HEADERS = "headers"
+ DATA_ALL_VIEWS = "all_views"
+ DATA_ACTIVE_VIEW = "active_view"
+ DATA_VIEW = "view"
+ DATA_VIEW_GRID = "grid"
+ #bhsearch may support more pluggable views later
+ VIEWS_SUPPORTED = {
+ None: "Free text",
+ DATA_VIEW_GRID: "Grid"
+ }
+ VIEWS_WITH_KNOWN_FIELDS = [DATA_VIEW_GRID]
+
+ OBLIGATORY_FIELDS_TO_SELECT = [IndexFields.ID, IndexFields.TYPE]
implements(INavigationContributor, IPermissionRequestor, IRequestHandler,
- ITemplateProvider,
- # IWikiSyntaxProvider #todo: implement later
+ ITemplateProvider,
+ # IWikiSyntaxProvider #todo: implement later
)
search_participants = OrderedExtensionsOption(
@@ -157,9 +184,31 @@ class BloodhoundSearchModule(Component):
"TicketSearchParticipant, WikiSearchParticipant"
)
-
- default_facets_all = ListOption('bhsearch', 'default_facets_all',
- doc="""Default facets applied to search through all resources""")
+ prefix = "all"
+ default_grid_fields = [
+ IndexFields.ID,
+ IndexFields.TYPE,
+ IndexFields.TIME,
+ IndexFields.AUTHOR,
+ IndexFields.CONTENT,
+ ]
+
+ default_facets = ListOption(
+ BHSEARCH_CONFIG_SECTION,
+ prefix + '_default_facets',
+ doc="""Default facets applied to search view of all resources""")
+
+ default_view = Option(
+ BHSEARCH_CONFIG_SECTION,
+ prefix + '_default_view',
+ doc="""If true, show grid as default view for specific resource in
+ Bloodhound Search results""")
+
+ default_grid_fields = ListOption(
+ BHSEARCH_CONFIG_SECTION,
+ prefix + '_default_grid_fields',
+ default=",".join(default_grid_fields),
+ doc="""Default fields for grid view for specific resource""")
# INavigationContributor methods
@@ -186,44 +235,46 @@ class BloodhoundSearchModule(Component):
allowed_participants = self._get_allowed_participants(req)
data = {
'query': parameters.query,
- }
+ }
self._prepare_allowed_types(allowed_participants, parameters, data)
self._prepare_active_filter_queries(
parameters,
data,
)
- working_query_string = parameters.query.strip()
-
#TBD: should search return results on empty query?
-# if not any((
-# working_query_string,
-# parameters.type,
-# parameters.filter_queries,
-# )):
-# return self._return_data(req, data)
+ # if not any((
+ # query,
+ # parameters.type,
+ # parameters.filter_queries,
+ # )):
+ # return self._return_data(req, data)
self._prepare_quick_jump(
parameters,
- working_query_string,
data)
+ fields = self._prepare_fields_and_view(
+ allowed_participants, parameters, data)
query_filter = self._prepare_query_filter(
- parameters,
- allowed_participants)
+ parameters, allowed_participants)
facets = self._prepare_facets(parameters, allowed_participants)
query_system = BloodhoundSearchApi(self.env)
query_result = query_system.query(
- working_query_string,
+ parameters.query,
pagenum=parameters.page,
pagelen=parameters.pagelen,
sort=parameters.sort,
+ fields=fields,
facets=facets,
- filter=query_filter)
+ filter=query_filter,
+ )
- ui_docs = [self._process_doc(doc, req, allowed_participants)
+ is_free_text_view = (self.DATA_VIEW not in data)
+ ui_docs = [self._process_doc(
+ doc, req, allowed_participants, is_free_text_view)
for doc in query_result.docs]
results = Paginator(
@@ -234,22 +285,21 @@ class BloodhoundSearchModule(Component):
results.shown_pages = self._prepare_shown_pages(
parameters,
- shown_pages = results.get_shown_pages(parameters.pagelen))
+ shown_pages=results.get_shown_pages(parameters.pagelen))
results.current_page = {'href': None,
'class': 'current',
'string': str(results.page + 1),
- 'title':None}
+ 'title': None}
if results.has_next_page:
- next_href = parameters.create_href(page = parameters.page + 1)
+ next_href = parameters.create_href(page=parameters.page + 1)
add_link(req, 'next', next_href, _('Next Page'))
if results.has_previous_page:
- prev_href = parameters.create_href(page = parameters.page - 1)
+ prev_href = parameters.create_href(page=parameters.page - 1)
add_link(req, 'prev', prev_href, _('Previous Page'))
-
data['results'] = results
self._prepare_result_facet_counts(
@@ -261,15 +311,89 @@ class BloodhoundSearchModule(Component):
data['page_href'] = parameters.create_href()
return self._return_data(req, data)
+
+ def _prepare_fields_and_view(self, allowed_participants, parameters, data):
+ self._add_views_selector(parameters, data)
+ active_participant = self._get_active_participant(
+ parameters, allowed_participants)
+ view = self._get_view(parameters, active_participant)
+ if view:
+ data[self.DATA_VIEW] = view
+ fields_to_select = None
+ if view in self.VIEWS_WITH_KNOWN_FIELDS:
+ if active_participant:
+ fields_in_view = active_participant.get_default_view_fields(
+ view)
+ elif view == self.DATA_VIEW_GRID:
+ fields_in_view = self.default_grid_fields
+ else:
+ raise TracError("Unsupported view: %s" % view)
+ data[self.DATA_HEADERS] = [self._create_headers_item(field)
+ for field in fields_in_view]
+ fields_to_select = self._optionally_add_obligatory_fields(
+ fields_in_view)
+ return fields_to_select
+
+ def _add_views_selector(self, parameters, data):
+ active_view = parameters.view
+ all_views = []
+ for view, label in self.VIEWS_SUPPORTED.iteritems():
+ all_views.append(dict(
+ label=_(label),
+ href=parameters.create_href(
+ view=view, skip_view=(view is None)),
+ is_active = (view == active_view)
+ ))
+ data[self.DATA_ALL_VIEWS] = all_views
+ if parameters.view:
+ data[self.DATA_ACTIVE_VIEW] = parameters.view
+
+ def _optionally_add_obligatory_fields(self, fields_in_view):
+ fields_to_select = list(fields_in_view)
+ for obligatory_field in self.OBLIGATORY_FIELDS_TO_SELECT:
+ if obligatory_field is not fields_to_select:
+ fields_to_select.append(obligatory_field)
+ return fields_to_select
+
+ def _create_headers_item(self, field):
+ return dict(
+ name=field,
+ href="",
+ #TODO:add translated column label. Now it is really temporary
+ #workaround
+ label=field,
+ sort=None,
+ )
+
+ def _get_view(self, parameters, active_participant):
+ view = parameters.view
+ if view is None:
+ if active_participant is not None:
+ view = active_participant.get_default_view()
+ else:
+ view = self.default_view
+ if view is not None:
+ view = view.strip().lower()
+ return view
+
+
+ def _get_active_participant(self, parameters, allowed_participants):
+ active_type = parameters.type
+ if active_type is not None and \
+ active_type in allowed_participants:
+ return allowed_participants[active_type]
+ else:
+ return None
+
+
def _prepare_quick_jump(self,
parameters,
- working_query_string,
data):
- if not working_query_string:
+ if not parameters.query:
return
check_result = self._check_quickjump(
parameters.req,
- working_query_string)
+ parameters.query)
if check_result:
data["quickjump"] = check_result
@@ -301,7 +425,6 @@ class BloodhoundSearchModule(Component):
req.redirect(quickjump_href)
-
def _prepare_allowed_types(self, allowed_participants, parameters, data):
active_type = parameters.type
if active_type and active_type not in allowed_participants:
@@ -327,16 +450,15 @@ class BloodhoundSearchModule(Component):
type = participant_with_type[participant]
allowed_types.append(dict(
label=_(participant.get_title()),
- active=(type ==active_type),
+ active=(type == active_type),
href=parameters.create_href(
type=type,
skip_page=True,
force_filters=[],
),
))
- data["types"] = allowed_types
- data["active_type"] = active_type
-
+ data["types"] = allowed_types
+ data["active_type"] = active_type
def _prepare_active_filter_queries(
@@ -393,8 +515,8 @@ class BloodhoundSearchModule(Component):
href = parameters.create_href(
skip_page=True,
additional_filter=self._create_term_expression(
- field,
- field_value)
+ field,
+ field_value)
)
per_field_dict[field_value] = dict(
count=count,
@@ -427,7 +549,7 @@ class BloodhoundSearchModule(Component):
#TODO: add possibility of specifying facets in query parameters
if parameters.is_show_all_mode():
facets = [IndexFields.TYPE]
- facets.extend(self.default_facets_all)
+ facets.extend(self.default_facets)
else:
type_participant = allowed_participants[parameters.type]
facets = type_participant.get_default_facets()
@@ -445,10 +567,8 @@ class BloodhoundSearchModule(Component):
add_stylesheet(req, 'common/css/search.css')
return 'bhsearch.html', data, None
- def _process_doc(self, doc, req, allowed_participants):
- #todo: introduce copy by predefined value
+ def _process_doc(self, doc, req, allowed_participants, is_free_text_view):
ui_doc = dict(doc)
-
ui_doc["href"] = req.href(doc['type'], doc['id'])
#todo: perform content adaptation here
if doc.has_key('content'):
@@ -456,8 +576,9 @@ class BloodhoundSearchModule(Component):
if doc.has_key('time'):
ui_doc['date'] = user_time(req, format_datetime, doc['time'])
- ui_doc['title'] = allowed_participants[doc['type']]\
- .format_search_results(doc)
+ if is_free_text_view:
+ ui_doc['title'] = allowed_participants[
+ doc['type']].format_search_results(doc)
return ui_doc
def _prepare_shown_pages(self, parameters, shown_pages):
@@ -465,7 +586,7 @@ class BloodhoundSearchModule(Component):
for shown_page in shown_pages:
page_href = parameters.create_href(page=shown_page)
page_data.append([page_href, None, str(shown_page),
- 'page ' + str(shown_page)])
+ 'page ' + str(shown_page)])
fields = ['href', 'class', 'string', 'title']
result_shown_pages = [dict(zip(fields, p)) for p in page_data]
return result_shown_pages
@@ -473,8 +594,8 @@ class BloodhoundSearchModule(Component):
# ITemplateProvider methods
def get_htdocs_dirs(self):
-# return [('bhsearch',
-# pkg_resources.resource_filename(__name__, 'htdocs'))]
+ # return [('bhsearch',
+ # pkg_resources.resource_filename(__name__, 'htdocs'))]
return []
def get_templates_dirs(self):
Modified: incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/whoosh_backend.py
URL: http://svn.apache.org/viewvc/incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/whoosh_backend.py?rev=1442601&r1=1442600&r2=1442601&view=diff
==============================================================================
--- incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/whoosh_backend.py (original)
+++ incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/whoosh_backend.py Tue Feb 5 14:22:55 2013
@@ -23,6 +23,7 @@ from bhsearch.api import ISearchBackend,
IDocIndexPreprocessor, IResultPostprocessor, IndexFields, \
IQueryPreprocessor
import os
+from bhsearch.search_resources.ticket_search import TicketFields
from trac.core import Component, implements, TracError
from trac.config import Option
from trac.util.text import empty
@@ -162,7 +163,6 @@ class WhooshBackend(Component):
query,
sort = None,
fields = None,
- boost = None,
filter = None,
facets = None,
pagenum = 1,
@@ -359,8 +359,8 @@ class WhooshEmptyFacetErrorWorkaround(Co
should_not_be_empty_fields = [
IndexFields.STATUS,
- IndexFields.MILESTONE,
- IndexFields.COMPONENT,
+ TicketFields.MILESTONE,
+ TicketFields.COMPONENT,
]
#IDocIndexPreprocessor methods
Propchange: incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_theme/
------------------------------------------------------------------------------
--- svn:ignore (added)
+++ svn:ignore Tue Feb 5 14:22:55 2013
@@ -0,0 +1 @@
+*.egg-info
Modified: incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_theme/bhtheme/htdocs/bloodhound.css
URL: http://svn.apache.org/viewvc/incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_theme/bhtheme/htdocs/bloodhound.css?rev=1442601&r1=1442600&r2=1442601&view=diff
==============================================================================
--- incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_theme/bhtheme/htdocs/bloodhound.css (original)
+++ incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_theme/bhtheme/htdocs/bloodhound.css Tue Feb 5 14:22:55 2013
@@ -17,8 +17,29 @@
under the License.
*/
+body {
+ background-color: rgb(92%, 91%, 88%);
+}
+
+.container:first-child {
+ background-color: rgb(100%, 100%, 100%);
+ padding: 5px 20px;
+ border-left: 1px solid rgb(70%, 70%, 70%);
+ border-right: 1px solid rgb(70%, 70%, 70%);
+ border-bottom: 1px solid rgb(70%, 70%, 70%);
+ box-shadow: 0px 0px 10px 2px rgba(50%, 50%, 50%, 0.4);
+ border-bottom-left-radius: 7px;
+ border-bottom-right-radius: 7px;
+}
+
+#searchbox {
+ margin-top: 0;
+ margin-bottom: 0;
+}
+
#mainsearch {
- margin-bottom: 0px;
+ margin-bottom: 0;
+ display: inline-block;
}
#mainnav {
@@ -134,9 +155,9 @@ div.reports form {
/* @end */
-#ticket table.properties td,
-#ticket table.properties th {
- font-size: 100%;
+#ticket .properties table td,
+#ticket .properties table th {
+ border: none;
}
#ticket footer {
@@ -144,6 +165,14 @@ div.reports form {
}
+#trac-ticket-title {
+ margin-bottom: 5px;
+}
+
+#trac-ticket-title span.ticket-info {
+ font-weight: normal;
+}
+
#activityfeed img {
display: none;
}
@@ -156,6 +185,11 @@ div.reports form {
color: grey;
}
+#mobile-activity {
+ margin-top: 50px;
+}
+
+
div.reports form,
div.reports .inlinebutton {
float: right;
@@ -172,12 +206,6 @@ div.reports h2 {
/* @group Wiki */
-/*
-textarea.wikitext {
- min-width: 66%;
-}
-*/
-
.wiki-toc {
float: right;
margin-left: 25px;
@@ -202,33 +230,43 @@ pre.wiki {
/* @group Ticket page modifications */
-#comment {
- width: 605px;
+.bh-ticket-buttons {
+ padding-left: 5px;
}
-#field-description {
- width: 505px;
+.clearboth {
+ clear: both;
}
-#qct-fieldset #field-description {
- width: auto;
+.ticket .keywords {
+ margin: 20px 0;
}
-.bh-ticket-buttons {
- padding-left: 5px;
+.ticket .properties h5 {
+ margin-top: 0;
+ margin-bottom: 0;
}
-.ticket .properties .enum h5, #basic-properties h5 {
- margin-top: 0px;
+.ticket-box-field, .ticket-box-info {
+ margin-top: 0;
+ margin-bottom: 0;
}
-.ownership {
- margin-left: 25px;
+.ticket-box-info dt {
+ text-align: left;
}
-.ticket textarea {
- height: auto;
- width: auto;
+#modifyproperties th, #modifyproperties td {
+ vertical-align: baseline;
+ border: none;
+}
+
+.ticket-summary #field-summary {
+ margin-bottom: 0;
+}
+
+.ticket .properties .enum h5 {
+ margin-top: 0px;
}
.trac-loading {
@@ -259,6 +297,10 @@ pre.wiki {
width: 260px;
}
+#qct-fieldset #field-description {
+ width: auto;
+}
+
#qct-box #qct-cancel {
display: inline;
color: #08C;
@@ -478,14 +520,107 @@ input[type="submit"].btn.btn-micro {
display: inherit !important;
}
+.breadcrumb {
+ background-color: #FBFBFB;
+ background-image: -moz-linear-gradient(center top , #FFFFFF, #F5F5F5);
+ background-repeat: repeat-x;
+ border: 1px solid #DDDDDD;
+ border-radius: 3px 3px 3px 3px;
+ box-shadow: 0 1px 0 #FFFFFF inset;
+ list-style: none outside none;
+ margin: 0 0 18px;
+ padding: 7px 14px;
+}
+
@media (max-width: 979px) {
.hidden-desktop {
display: inherit !important;
}
}
-.help-msg[title] {
- cursor: help;
+@media (min-width: 979px) and (max-width: 1199px) {
+ .main-nav ul {
+ background-color: red;
+ }
+}
+
+@media (max-width: 767px) {
+ body {
+ padding-left: 8px;
+ padding-right: 8px;
+ }
+
+ #logo, #usermenu, #breadcrumbbar {
+ float: left;
+ }
+
+ #searchbox {
+ float: left;
+ margin-right: 10px;
+ }
+
+ .breadcrumb {
+ margin-top: 7px;
+ }
+
+ .comment-meta {
+ margin-bottom: 5px;
+ }
+
+ .comment-meta .id,
+ .comment-meta .date {
+ text-align: right;
+ }
+
+ .sticky {
+ width: 100%;
+ position: relative;
+ left: 0px;
+ padding-left: 29px;
+ }
+
+ #belowStatus {
+ margin-top: 5px;
+ }
+
+ .ticket .properties div[class*="span"] {
+ float: left;
+ margin-left: 10px;
+ margin-right: 10px;
+ }
+
+ #changelog .change .span2 {
+ float: left;
+ }
+}
+
+@media (max-width: 480px) {
+ body {
+ padding-left: 0px;
+ padding-right: 0px;
+ }
+
+ h1 {
+ font-size: 30px;
+ margin-bottom: 0px;
+ line-height: 1;
+ }
+
+ .container:first-child {
+ border-left: 0px solid white;
+ border-right: 0px solid white;
+ padding: 0px;
+ }
+
+ header, #belowStatus, #mobile-activity,
+ #stickyStatus, #footer-container {
+ padding-left: 7px;
+ padding-right: 7px;
+ }
+
+ .ownership {
+ padding-left: 15px;
+ }
}
/* Revert some changes introduced in 2.1.0 */
@@ -496,18 +631,6 @@ h6 {
text-transform: uppercase;
}
-.breadcrumb {
- background-color: #FBFBFB;
- background-image: -moz-linear-gradient(center top , #FFFFFF, #F5F5F5);
- background-repeat: repeat-x;
- border: 1px solid #DDDDDD;
- border-radius: 3px 3px 3px 3px;
- box-shadow: 0 1px 0 #FFFFFF inset;
- list-style: none outside none;
- margin: 0 0 18px;
- padding: 7px 14px;
-}
-
/* @end */
/* @group Bootstrap extensions */
@@ -679,34 +802,116 @@ endColorstr='#ffe6e6e6', GradientType=0)
/* @group Sticky Status */
-[class*="span"].stickyStatus {
- margin-left: 0px;
+#stickyStatus {
+ position: relative;
+ background-color: white;
+ z-index: 20;
}
-.stickyStatus.affix {
- background-color: #FFFFFF;
- border-bottom: 2px solid #A4A4A4;
- position: fixed;
+/* The box-shadow overhanging to the left and right are hacks only.
+ We should look for a better solution for production code. */
+#stickyStatus.sticky {
+ position: fixed !important;
top: 0px;
- z-index: 1024;
+ opacity: 1;
+ background-color: rgb(93%, 93%, 93%);
+
+ padding-bottom: 10px;
+
+ border-bottom: 1px solid rgba(75%, 75%, 75%, 0.5);
+
+ -webkit-box-shadow: 0px 6px 5px -4px rgba(50%, 50%, 50%, 0.5),
+ -20px 0px 0px rgb(93%, 93%, 93%),
+ 20px 0px 0px rgb(93%, 93%, 93%);
+ -moz-box-shadow: 0px 6px 5px -4px rgba(50%, 50%, 50%, 0.5),
+ -20px 0px 0px rgb(93%, 93%, 93%),
+ 20px 0px 0px rgb(93%, 93%, 93%);
+ box-shadow: 0px 6px 5px -4px rgba(50%, 50%, 50%, 0.5),
+ -20px 0px 0px rgb(93%, 93%, 93%),
+ 20px 0px 0px rgb(93%, 93%, 93%);
+
+ -webkit-transition: all 0.3s ease-out;
+ -moz-transition: all 0.3s ease-out;
+ -o-transition: all 0.3s ease-out;
+ -ms-transition: all 0.3s ease-out;
+ transition: all 0.3s ease-out;
+}
+
+#belowStatus {
+ position: relative;
+ margin-top: 20px;
+}
+
+.offsetSticky {
+ height: 130px;
+}
+
+#stickyStatus h2,
+#stickyStatus ul {
+ margin-bottom: 3px;
+}
+
+#stickyStatus h2 {
+ line-height: 30px;
+}
+
+.local-nav {
+ display: inline-block;
+ margin-left: 0;
+}
+
+.ownership {
+ padding-left: 25px;
+}
+
+#description {
+ margin-top: 10px;
+}
+
+.comment {
+ border-top: 1px solid rgb(80%, 80%, 80%);
+ margin-bottom: 10px;
+ padding-top: 5px;
+}
+
+.comment-meta {
+ overflow: hidden;
+}
+
+.comment-meta .id,
+.comment-meta .date {
+ float: right;
+ color: #888;
+ font-size: 90%;
+}
+
+.comment-meta span:first-child {
+ font-weight: 700;
+}
+
+.add-comment textarea {
+ height: 100px;
+ border: 1px solid #eee;
+ color: lightgray;
+ display: block;
}
/* @end */
/* @group Wiki Tables */
-TABLE.wiki {
+table.wiki {
border: 1px solid #CCCCCC;
border-collapse: collapse;
border-spacing: 0;
}
-TABLE.wiki td {
+table.wiki td {
border: 1px solid #CCCCCC;
padding: 0.1em 0.25em;
}
-TABLE.wiki th {
+table.wiki th {
border: 1px solid #BBBBBB;
padding: 0.1em 0.25em;
background-color: #F7F7F7;
Modified: incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_theme/bhtheme/templates/bh_search.html
URL: http://svn.apache.org/viewvc/incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_theme/bhtheme/templates/bh_search.html?rev=1442601&r1=1442600&r2=1442601&view=diff
==============================================================================
--- incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_theme/bhtheme/templates/bh_search.html (original)
+++ incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_theme/bhtheme/templates/bh_search.html Tue Feb 5 14:22:55 2013
@@ -40,66 +40,60 @@
</script>
</head>
<body>
- <py:choose>
- <div py:when="query" id="content" class="row">
-
- <h2 py:if="results">
- Results <small>(${results.displayed_items()})</small>
- </h2>
-
- <form id="fullsearch" action="${href.search()}" method="get">
- <input type="hidden" name="q" value="${query}" />
- <input type="hidden" name="noquickjump" value="1" />
- <div class="span2">
- <input class="btn" type="submit" value="${_('Apply filters')}" />
- </div>
- <div class="span10 filters">
- <py:for each="filter in filters">
- <input type="checkbox" id="${filter.name}" name="${filter.name}"
- checked="${filter.active or None}" />
- <label for="${filter.name}">
- <span class="label">${filter.label}</span>
- </label>
- </py:for>
- </div>
- </form>
-
- <div class="span12" py:if="results or quickjump">
- <div>
- <dl id="results">
- <py:if test="quickjump">
- <dt id="quickjump">
- <a href="${quickjump.href}" i18n:msg="name">Quickjump to ${quickjump.name}</a>
- </dt>
- <dd>${quickjump.description}</dd>
- </py:if>
- <py:for each="result in results">
- <dt><a href="${result.href}" class="searchable">${result.title}</a></dt>
- <dd class="searchable">${result.excerpt}</dd>
- <dd>
- <py:if test="result.author"><span class="author" i18n:msg="author">By ${format_author(result.author)}</span> —</py:if>
- <span class="date">${result.date}</span>
- </dd>
- </py:for>
- </dl>
- </div>
- <xi:include py:with="paginator = results" href="bh_page_index.html" />
+ <div id="content" class="row">
+
+ <h2 py:if="results">
+ Results <small>(${results.displayed_items()})</small>
+ </h2>
+
+ <form id="fullsearch" action="${href.search()}" method="get">
+ <input type="hidden" name="q" value="${query}" />
+ <input type="hidden" name="noquickjump" value="1" />
+ <div class="span2">
+ <input class="btn" type="submit" value="${_('Apply filters')}" />
</div>
-
- <div class="span12"
- py:if="query and not (results or quickjump)">
- <p id="notfound" class="alert">
- No matches found.
- </p>
+ <div class="span10 filters">
+ <py:for each="filter in filters">
+ <input type="checkbox" id="${filter.name}" name="${filter.name}"
+ checked="${filter.active or None}" />
+ <label for="${filter.name}">
+ <span class="label">${filter.label}</span>
+ </label>
+ </py:for>
+ </div>
+ </form>
+
+ <div class="span12" py:if="results or quickjump">
+ <div>
+ <dl id="results">
+ <py:if test="quickjump">
+ <dt id="quickjump">
+ <a href="${quickjump.href}" i18n:msg="name">Quickjump to ${quickjump.name}</a>
+ </dt>
+ <dd>${quickjump.description}</dd>
+ </py:if>
+ <py:for each="result in results">
+ <dt><a href="${result.href}" class="searchable">${result.title}</a></dt>
+ <dd class="searchable">${result.excerpt}</dd>
+ <dd>
+ <py:if test="result.author"><span class="author" i18n:msg="author">By ${format_author(result.author)}</span> —</py:if>
+ <span class="date">${result.date}</span>
+ </dd>
+ </py:for>
+ </dl>
</div>
-
+ <xi:include py:with="paginator = results" href="bh_page_index.html" />
</div>
- <p py:otherwise="" class="alert">
- <span class="label label-warning">Warning</span>
- Search query too short. Query must be at least 3 characters long.
- Please type your query string in search box and try again.
- </p>
- </py:choose>
+
+ <div class="span12"
+ py:if="query and not (results or quickjump)">
+ <p id="notfound" class="alert">
+ No matches found.
+ </p>
+ </div>
+
+ </div>
+
<div id="help" class="help-block pull-right" i18n:msg="">
<span class="label label-info">Note:</span>
See <a href="${href.wiki('TracSearch')}">TracSearch</a>