Author: jure
Date: Wed Mar 13 12:31:28 2013
New Revision: 1455906
URL: http://svn.apache.org/r1455906
Log:
Sync merge from trunk
Added:
incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/templates/bhsearch_breadcrumbs.html
- copied unchanged from r1455900,
incubator/bloodhound/trunk/bloodhound_search/bhsearch/templates/bhsearch_breadcrumbs.html
incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_theme/bhtheme/templates/bh_timeline.html
- copied unchanged from r1455900,
incubator/bloodhound/trunk/bloodhound_theme/bhtheme/templates/bh_timeline.html
Modified:
incubator/bloodhound/branches/bep_0003_multiproduct/ (props changed)
incubator/bloodhound/branches/bep_0003_multiproduct/LICENSE
incubator/bloodhound/branches/bep_0003_multiproduct/NOTICE
incubator/bloodhound/branches/bep_0003_multiproduct/RELEASE_NOTES
incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/api.py
incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/query_parser.py
incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/search_resources/ticket_search.py
incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/templates/bhsearch.html
incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/tests/__init__.py
incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/tests/api.py
incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/tests/query_parser.py
incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/tests/web_ui.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/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
Propchange: incubator/bloodhound/branches/bep_0003_multiproduct/
------------------------------------------------------------------------------
Merged /incubator/bloodhound/trunk:r1455438-1455900
Modified: incubator/bloodhound/branches/bep_0003_multiproduct/LICENSE
URL:
http://svn.apache.org/viewvc/incubator/bloodhound/branches/bep_0003_multiproduct/LICENSE?rev=1455906&r1=1455905&r2=1455906&view=diff
==============================================================================
--- incubator/bloodhound/branches/bep_0003_multiproduct/LICENSE (original)
+++ incubator/bloodhound/branches/bep_0003_multiproduct/LICENSE Wed Mar 13
12:31:28 2013
@@ -209,7 +209,7 @@ separate copyright notices and license t
for the these subcomponents is subject to the terms and conditions of the
following licenses.
-For Trac, found in the trac/ directory:
+For Trac, in the trac/ directory:
Copyright (C) 2003-2013 Edgewall Software
All rights reserved.
@@ -243,7 +243,7 @@ For Trac, found in the trac/ directory:
For Bootstrap, as provided by the files
bloodhound_dashboard/bhdashboard/htdocs/css/bootstrap.css
bloodhound_dashboard/bhdashboard/htdocs/css/bootstrap-responsive.css
-and the bloodhound_dashboard/bhdashboard/htdocs/js/ directory:
+and bloodhound_dashboard/bhdashboard/htdocs/js/bootstrap.js:
Copyright 2012 Twitter, Inc.
@@ -262,9 +262,14 @@ and the bloodhound_dashboard/bhdashboard
For jQuery, in the file
trac/trac/htdocs/js/jquery.js
- Copyright 2011 jQuery Foundation and other contributors
- http://jquery.com/
-
+ Copyright 2013 jQuery Foundation and other contributors,
+ http://jqueryui.com/
+
+ This software consists of voluntary contributions made by many
+ individuals (AUTHORS.txt, http://jqueryui.com/about) For exact
+ contribution history, see the revision history and logs, available
+ at http://jquery-ui.googlecode.com/svn/
+
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
@@ -272,10 +277,10 @@ trac/trac/htdocs/js/jquery.js
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
-
+
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
-
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
@@ -284,6 +289,59 @@ trac/trac/htdocs/js/jquery.js
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+For jQuery UI, in the file
+trac/trac/htdocs/js/jquery-ui.js
+
+ Copyright 2013 jQuery Foundation and other contributors,
+ http://jqueryui.com/
+
+ This software consists of voluntary contributions made by many
+ individuals (AUTHORS.txt, http://jqueryui.com/about) For exact
+ contribution history, see the revision history and logs, available
+ at http://jquery-ui.googlecode.com/svn/
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+For jQuery Timepicker Addon, in the file
+trac/trac/htdocs/js/jquery-ui-addons.js
+
+ Copyright (c) 2009 Trent Richardson, http://trentrichardson.com/Impromptu/
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
For LRU and LFU cache decorators, developed by Raymond Hettinger, in the file
bloodhound_multiproduct/multiproduct/cache.py:
Modified: incubator/bloodhound/branches/bep_0003_multiproduct/NOTICE
URL:
http://svn.apache.org/viewvc/incubator/bloodhound/branches/bep_0003_multiproduct/NOTICE?rev=1455906&r1=1455905&r2=1455906&view=diff
==============================================================================
--- incubator/bloodhound/branches/bep_0003_multiproduct/NOTICE (original)
+++ incubator/bloodhound/branches/bep_0003_multiproduct/NOTICE Wed Mar 13
12:31:28 2013
@@ -16,7 +16,15 @@ jQuery - licensed under the MIT License
This product includes software (jQuery) developed by
jQuery (http://jquery.org/)
+jQuery UI - licensed under MIT License
+This product includes software (jQuery UI) developed by
+jQuery UI (http://jqueryui.com/)
+
+jQuery Timepicker Addon - licensed under MIT License
+This product includes software (jQuery Timepicker Addon)
+developed by Trent Richardson
+(http://trentrichardson.com/examples/timepicker/)
+
LRU and LFU cache decorators - licensed under the PSF License
This product includes code (LRU and LFU cache decorators) developed by
Raymond Hettinger
(http://code.activestate.com/recipes/498245-lru-and-lfu-cache-decorators/)
-
Modified: incubator/bloodhound/branches/bep_0003_multiproduct/RELEASE_NOTES
URL:
http://svn.apache.org/viewvc/incubator/bloodhound/branches/bep_0003_multiproduct/RELEASE_NOTES?rev=1455906&r1=1455905&r2=1455906&view=diff
==============================================================================
--- incubator/bloodhound/branches/bep_0003_multiproduct/RELEASE_NOTES (original)
+++ incubator/bloodhound/branches/bep_0003_multiproduct/RELEASE_NOTES Wed Mar
13 12:31:28 2013
@@ -3,6 +3,7 @@
* Implemented Bootstrap templates for the repository browser, log viewer and
attachment pages.
* Upgraded Bootstrap to version 2.3.1.
* Upgraded Trac to version 1.0.1.
+ * Added beta version of bhsearch plugin. Find more information on
https://issues.apache.org/bloodhound/wiki/BloodhoundSearch
* Various bug fixes and enhancements.
* Not fixed for this release:
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=1455906&r1=1455905&r2=1455906&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
Wed Mar 13 12:31:28 2013
@@ -186,7 +186,7 @@ class IQueryParser(Interface):
"""Extension point for Bloodhound Search query parser.
"""
- def parse(query_string):
+ def parse(query_string, context):
"""Parse query from string"""
def parse_filters(filters):
@@ -219,7 +219,7 @@ class IQueryPreprocessor(Interface):
class IMetaKeywordParser(Interface):
"""Extension point for custom meta keywords."""
- def match(text):
+ def match(text, context):
"""If text matches the keyword, return its transformed value."""
@@ -255,7 +255,8 @@ class BloodhoundSearchApi(Component):
pagenum = 1,
pagelen = 20,
highlight = False,
- highlight_fields = None):
+ highlight_fields = None,
+ context = None):
"""Return query result from an underlying search backend.
Arguments:
@@ -271,12 +272,13 @@ class BloodhoundSearchApi(Component):
:param pagelen: paging support
:param highlight: highlight matched terms in fields
:param highlight_fields: list of fields to highlight
+ :param context: request context
:return: result QueryResult
"""
self.env.log.debug("Receive query request: %s", locals())
- parsed_query = self.parser.parse(query)
+ parsed_query = self.parser.parse(query, context)
parsed_filters = self.parser.parse_filters(filter)
# TODO: add query parsers and meta keywords post-parsing
Modified:
incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/query_parser.py
URL:
http://svn.apache.org/viewvc/incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/query_parser.py?rev=1455906&r1=1455905&r2=1455906&view=diff
==============================================================================
---
incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/query_parser.py
(original)
+++
incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/query_parser.py
Wed Mar 13 12:31:28 2013
@@ -39,9 +39,10 @@ class MetaKeywordPlugin(qparser.TaggingP
expr = r"[$](?P<text>[^ \t\r\n]+)(?= |$|\))"
nodetype = qparser.syntax.WordNode
- def __init__(self, meta_keyword_parsers=()):
+ def __init__(self, meta_keyword_parsers=(), context=None):
super(MetaKeywordPlugin, self).__init__()
self.meta_keyword_parsers = meta_keyword_parsers
+ self.context = context
def match(self, parser, text, pos):
match = qparser.TaggingPlugin.match(self, parser, text, pos)
@@ -50,7 +51,8 @@ class MetaKeywordPlugin(qparser.TaggingP
candidate = match.text
for meta_keyword_parser in self.meta_keyword_parsers:
- expanded_meta_keyword = meta_keyword_parser.match(candidate)
+ expanded_meta_keyword = meta_keyword_parser.match(candidate,
+ self.context)
if expanded_meta_keyword is not None:
node = MetaKeywordNode(parser.tag(expanded_meta_keyword))
return node.set_range(match.startchar, match.endchar)
@@ -91,28 +93,13 @@ class DefaultQueryParser(Component):
meta_keyword_parsers = ExtensionPoint(IMetaKeywordParser)
- def __init__(self):
- super(DefaultQueryParser, self).__init__()
- self.parser = MultifieldParser(
- self.field_boosts.keys(),
- WhooshBackend.SCHEMA,
- fieldboosts=self.field_boosts
- )
- self.parser.add_plugin(
- MetaKeywordPlugin(meta_keyword_parsers=self.meta_keyword_parsers)
- )
-
- def parse(self, query_string):
+ def parse(self, query_string, context=None):
+ parser = self._create_parser(context)
query_string = query_string.strip()
-
if query_string == "" or query_string == "*" or query_string == "*:*":
return query.Every()
-
query_string = unicode(query_string)
- parsed_query = self.parser.parse(query_string)
-
- #todo: impalement pluggable mechanism for query post processing
- #e.g. meta keyword replacement etc.
+ parsed_query = parser.parse(query_string)
return parsed_query
def parse_filters(self, filters):
@@ -125,13 +112,25 @@ class DefaultQueryParser(Component):
def _parse_filter(self, filter):
return self.parse(unicode(filter))
+ def _create_parser(self, context):
+ parser = MultifieldParser(
+ self.field_boosts.keys(),
+ WhooshBackend.SCHEMA,
+ fieldboosts=self.field_boosts
+ )
+ parser.add_plugin(
+ MetaKeywordPlugin(meta_keyword_parsers=self.meta_keyword_parsers,
+ context=context)
+ )
+ return parser
+
class DocTypeMetaKeywordParser(Component):
implements(IMetaKeywordParser)
search_participants = ExtensionPoint(ISearchParticipant)
- def match(self, text):
+ def match(self, text, context):
documents = [p.get_participant_type()
for p in self.search_participants]
if text in documents:
@@ -141,7 +140,7 @@ class DocTypeMetaKeywordParser(Component
class ResolvedMetaKeywordParser(Component):
implements(IMetaKeywordParser)
- def match(self, text):
+ def match(self, text, context):
if text == u'resolved':
return u'status:(resolved OR closed)'
@@ -149,7 +148,23 @@ class ResolvedMetaKeywordParser(Componen
class UnResolvedMetaKeywordParser(Component):
implements(IMetaKeywordParser)
- def match(self, text):
+ def match(self, text, context):
if text == u'unresolved':
return u'NOT $resolved'
+
+class MeMetaKeywordParser(Component):
+ implements(IMetaKeywordParser)
+
+ def match(self, text, context):
+ if text == u'me':
+ username = unicode(context.req.authname)
+ return username
+
+
+class MyMetaKeywordParser(Component):
+ implements(IMetaKeywordParser)
+
+ def match(self, text, context):
+ if text == u'my':
+ return u'owner:$me'
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=1455906&r1=1455905&r2=1455906&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
Wed Mar 13 12:31:28 2013
@@ -39,6 +39,7 @@ class TicketFields(IndexFields):
KEYWORDS = "keywords"
RESOLUTION = "resolution"
CHANGES = 'changes'
+ OWNER = 'owner'
class TicketIndexer(BaseIndexer):
implements(ITicketChangeListener, IIndexParticipant)
@@ -52,6 +53,7 @@ class TicketIndexer(BaseIndexer):
'status': TicketFields.STATUS,
'resolution': TicketFields.RESOLUTION,
'reporter': TicketFields.AUTHOR,
+ 'owner': TicketFields.OWNER,
}
def __init__(self):
@@ -205,5 +207,6 @@ class TicketSearchParticipant(BaseSearch
id = res['hilited_id'] or res['id']
id = tag.span('#', id, class_=css_class)
- return tag(id, ': ', res['hilited_summary'], ' (%s)' % stat)
+ summary = res['hilited_summary'] or res['summary']
+ return tag(id, ': ', summary, ' (%s)' % stat)
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=1455906&r1=1455905&r2=1455906&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
Wed Mar 13 12:31:28 2013
@@ -56,33 +56,6 @@
<body>
<div id="content" class="row">
<div class="span12">
- <h1>Advanced search</h1>
- <form id="fullsearch" action="${href.bhsearch()}" method="get">
- <!--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}" />
- <input py:if="active_view" type="hidden" name="view"
value="${active_view}" />
- <input py:if="active_sort" type="hidden" name="sort"
value="${active_sort.expression}" />
- <py:for each="active_filter in active_filter_queries">
- <input type="hidden" name="fq" value="${active_filter.query}" />
- </py:for>
-
- <div class="input-append">
- <input type="text" id="q" name="q" class="span4"
value="${query}" />
- <button type="submit" class="btn btn-warning">
- <span class="hidden-phone">${_('Search')}</span>
- <i class="icon-search icon-white"></i>
- </button>
- </div>
-
- <div id="active_sort" py:if="active_sort">
- Sort by: <strong>${active_sort.expression}</strong> (<a
href="${active_sort.href}">remove</a>)
- </div>
-
- </form>
- </div>
-
- <div class="span12">
<py:if test="quickjump">
<dt id="quickjump">
<a href="${quickjump.href}" i18n:msg="name">Quickjump to
${quickjump.name}</a>
@@ -102,13 +75,6 @@
</ul>
</div>
- <!--Render filters breadcrumbs-->
- <div id="active_filter_queries" py:if="active_filter_queries">
- <py:for each="active_filter in active_filter_queries">
- > <a href="${active_filter.href}">${active_filter.label}</a>
- </py:for>
- </div>
-
<div py:if="results" class="row">
<div class="span3 facets">
<py:if test="facet_counts">
@@ -128,7 +94,8 @@
</py:if>
</div>
- <div class="${'span12' if not facet_counts else 'span9
search_results'}">
+ <div class="${'span12' if not facet_counts else 'span9'}${
+ ' search_results'}">
<h2>
Results <small>(${results.displayed_items()})</small>
</h2>
@@ -200,7 +167,7 @@
<dl id="results">
<py:for each="result in results">
<dt><a href="${result.href}"
class="searchable">${result.title}</a></dt>
- <dd class="searchable">${result.content}</dd>
+ <dd class="searchable">${result.hilited_content or
result.content}</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>
@@ -215,7 +182,6 @@
</div>
</div>
-
<div class="span12"
py:if="query and not (results or quickjump)">
<p id="notfound" class="alert">
@@ -223,6 +189,12 @@
</p>
</div>
+ <div py:if="debug.enabled" class="span12">
+ <p>DEBUG INFO:
+ <pre>${pprint(debug)}</pre>
+ </p>
+ </div>
+
<div id="help" class="help-block pull-right" i18n:msg="">
<strong>Note:</strong> See <a
href="${href.wiki('BloodhoundSearch')}">BloodhoundSearch</a>
for help on searching.
Modified:
incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/tests/__init__.py
URL:
http://svn.apache.org/viewvc/incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/tests/__init__.py?rev=1455906&r1=1455905&r2=1455906&view=diff
==============================================================================
---
incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/tests/__init__.py
(original)
+++
incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/tests/__init__.py
Wed Mar 13 12:31:28 2013
@@ -29,6 +29,7 @@ def suite():
test_suite.addTest(whoosh_backend.suite())
test_suite.addTest(web_ui.suite())
test_suite.addTest(api.suite())
+ test_suite.addTest(query_parser.suite())
test_suite.addTest(ticket_search.suite())
test_suite.addTest(wiki_search.suite())
test_suite.addTest(milestone_search.suite())
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=1455906&r1=1455905&r2=1455906&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
Wed Mar 13 12:31:28 2013
@@ -18,26 +18,18 @@
# specific language governing permissions and limitations
# under the License.
import unittest
-import tempfile
import shutil
from bhsearch.api import BloodhoundSearchApi, ASC, SortInstruction
from bhsearch.query_parser import DefaultQueryParser
from bhsearch.tests.base import BaseBloodhoundSearchTest
from bhsearch.search_resources.ticket_search import TicketSearchParticipant
-
from bhsearch.whoosh_backend import WhooshBackend
-from trac.test import EnvironmentStub
-from trac.ticket.api import TicketSystem
class ApiQueryWithWhooshTestCase(BaseBloodhoundSearchTest):
def setUp(self):
- self.env = EnvironmentStub(enable=['bhsearch.*'])
- self.env.path = tempfile.mkdtemp('bhsearch-tempenv')
- self.env.config.set('bhsearch', 'silence_on_error', "False")
- self.ticket_system = TicketSystem(self.env)
- self.whoosh_backend = WhooshBackend(self.env)
- self.whoosh_backend.recreate_index()
+ super(ApiQueryWithWhooshTestCase, self).setUp()
+ WhooshBackend(self.env).recreate_index()
self.search_api = BloodhoundSearchApi(self.env)
self.ticket_participant = TicketSearchParticipant(self.env)
self.query_parser = DefaultQueryParser(self.env)
Modified:
incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/tests/query_parser.py
URL:
http://svn.apache.org/viewvc/incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/tests/query_parser.py?rev=1455906&r1=1455905&r2=1455906&view=diff
==============================================================================
---
incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/tests/query_parser.py
(original)
+++
incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_search/bhsearch/tests/query_parser.py
Wed Mar 13 12:31:28 2013
@@ -69,6 +69,26 @@ class MetaKeywordsParsingTestCase(BaseBl
)
]))
+ def test_can_parse_keyword_me(self):
+ context = self._mock_context_with_username('username')
+
+ parsed_query = self.parser.parse("author:$me", context)
+
+ self.assertEqual(parsed_query, terms.Term('author', 'username'))
+
+ def test_can_parse_keyword_my(self):
+ context = self._mock_context_with_username('username')
+
+ parsed_query = self.parser.parse("$my", context)
+
+ self.assertEqual(parsed_query, terms.Term('owner', 'username'))
+
+ def _mock_context_with_username(self, username):
+ class context:
+ class req:
+ authname = username
+ return context
+
def suite():
test_suite = unittest.TestSuite()
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=1455906&r1=1455905&r2=1455906&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
Wed Mar 13 12:31:28 2013
@@ -140,8 +140,8 @@ class WebUiTestCaseWithWhoosh(BaseBloodh
#act
data = self.process_request()
#assert
- active_type = data["active_type"]
- self.assertEquals("ticket", active_type)
+ extra_search_options = dict(data["extra_search_fields"])
+ self.assertEquals("ticket", extra_search_options['type'])
resource_types = data["types"]
@@ -513,7 +513,8 @@ class WebUiTestCaseWithWhoosh(BaseBloodh
self.req.args[RequestParameters.VIEW] = "grid"
data = self.process_request()
#assert
- self.assertEqual("grid", data["active_view"])
+ extra_search_options = dict(data["extra_search_fields"])
+ self.assertEqual("grid", extra_search_options["view"])
def test_can_apply_sorting(self):
#arrange
@@ -585,9 +586,9 @@ class WebUiTestCaseWithWhoosh(BaseBloodh
data = self.process_request()
#assert
- active_sort = data["active_sort"]
- self.assertEquals("id, time desc", active_sort["expression"])
- self.assertNotIn("sort=", active_sort["href"])
+ extra_search_options = dict(data["extra_search_fields"])
+ self.assertEquals("id, time desc", extra_search_options["sort"])
+ #self.assertNotIn("sort=", active_sort["href"])
def test_that_document_summary_contains_highlighted_search_terms(self):
term = "searchterm"
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=1455906&r1=1455905&r2=1455906&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
Wed Mar 13 12:31:28 2013
@@ -60,6 +60,7 @@ class RequestParameters(object):
FILTER_QUERY = "fq"
VIEW = "view"
SORT = "sort"
+ DEBUG = "debug"
def __init__(self, req):
self.req = req
@@ -85,6 +86,7 @@ class RequestParameters(object):
DEFAULT_RESULTS_PER_PAGE))
self.page = int(req.args.getfirst(self.PAGE, '1'))
self.type = req.args.getfirst(self.TYPE)
+ self.debug = int(req.args.getfirst(self.DEBUG, '0'))
self.params = {
RequestParameters.FILTER_QUERY: []
@@ -106,6 +108,8 @@ class RequestParameters(object):
self.params[RequestParameters.FILTER_QUERY] = self.filter_queries
if self.sort_string:
self.params[RequestParameters.SORT] = self.sort_string
+ if self.debug:
+ self.params[RequestParameters.DEBUG] = self.debug
def _parse_sort(self, sort_string):
if not sort_string:
@@ -272,6 +276,9 @@ class BloodhoundSearchModule(Component):
self.default_facets
)
+ # compatibility with legacy search
+ req.search_query = request_context.parameters.query
+
query_result = BloodhoundSearchApi(self.env).query(
request_context.parameters.query,
pagenum=request_context.page,
@@ -281,6 +288,7 @@ class BloodhoundSearchModule(Component):
facets=request_context.facets,
filter=request_context.query_filter,
highlight=True,
+ context=request_context,
)
request_context.process_results(query_result)
@@ -301,20 +309,19 @@ class BloodhoundSearchModule(Component):
class RequestContext(object):
DATA_ACTIVE_FILTER_QUERIES = 'active_filter_queries'
- DATA_ACTIVE_TYPE = "active_type"
- DATA_ACTIVE_SORT = "active_sort"
+ DATA_BREADCRUMBS_TEMPLATE = 'resourcepath_template'
DATA_TYPES = "types"
DATA_HEADERS = "headers"
DATA_ALL_VIEWS = "all_views"
- DATA_ACTIVE_VIEW = "active_view"
DATA_VIEW = "view"
DATA_VIEW_GRID = "grid"
DATA_FACET_COUNTS = 'facet_counts'
DATA_DEBUG = 'debug'
DATA_PAGE_HREF = 'page_href'
DATA_RESULTS = 'results'
+ DATA_QUERY = 'query'
DATA_QUICK_JUMP = "quickjump"
-
+ DATA_SEARCH_EXTRAS = 'extra_search_fields'
#bhsearch may support more pluggable views later
VIEWS_SUPPORTED = {
@@ -338,7 +345,10 @@ class RequestContext(object):
self.env = env
self.req = req
self.parameters = RequestParameters(req)
- self.data = {'query': self.parameters.query}
+ self.data = {
+ self.DATA_QUERY: self.parameters.query,
+ self.DATA_SEARCH_EXTRAS: [],
+ }
self.search_participants = search_participants
self.default_view = default_view
self.all_grid_fields = all_grid_fields
@@ -349,9 +359,8 @@ class RequestContext(object):
if self.parameters.sort:
self.sort = self.parameters.sort
- self.data[self.DATA_ACTIVE_SORT] = dict(
- expression=self.parameters.sort_string,
- href=self.parameters.create_href(skip_sort=True)
+ self.data[self.DATA_SEARCH_EXTRAS].append(
+ (RequestParameters.SORT, self.parameters.sort_string)
)
else:
self.sort = self.DEFAULT_SORT
@@ -417,7 +426,9 @@ class RequestContext(object):
),
))
self.data[self.DATA_TYPES] = allowed_types
- self.data[self.DATA_ACTIVE_TYPE] = active_type
+ self.data[self.DATA_SEARCH_EXTRAS].append(
+ (RequestParameters.TYPE, active_type)
+ )
def _prepare_active_filter_queries(self):
current_filters = self.parameters.filter_queries
@@ -435,6 +446,10 @@ class RequestContext(object):
) for filter_query in self.parameters.filter_queries
]
self.data[self.DATA_ACTIVE_FILTER_QUERIES] = active_filter_queries
+ for filter_query in active_filter_queries:
+ self.data[self.DATA_SEARCH_EXTRAS].append(
+ (RequestParameters.FILTER_QUERY, filter_query['query'])
+ )
def _prepare_quick_jump(self):
if not self.parameters.query:
@@ -497,7 +512,9 @@ class RequestContext(object):
def _add_views_selector(self):
active_view = self.parameters.view
if active_view:
- self.data[self.DATA_ACTIVE_VIEW] = active_view
+ self.data[self.DATA_SEARCH_EXTRAS].append(
+ (RequestParameters.VIEW, active_view)
+ )
all_views = []
for view, label in self.VIEWS_SUPPORTED.iteritems():
@@ -635,7 +652,11 @@ class RequestContext(object):
query_result.highlighting)
self._prepare_results(docs, query_result.hits)
self._prepare_result_facet_counts(query_result.facets)
+ self._prepare_breadcrumbs_template()
self.data[self.DATA_DEBUG] = query_result.debug
+ if self.parameters.debug:
+ self.data[self.DATA_DEBUG]['enabled'] = True
+ self.data[self.DATA_SEARCH_EXTRAS].append(('debug', '1'))
self.data[self.DATA_PAGE_HREF] = self.parameters.create_href()
def _prepare_result_facet_counts(self, result_facets):
@@ -697,3 +718,6 @@ class RequestContext(object):
def _create_genshi_fragment(self, html_fragment):
return tag(HTML(html_fragment))
+
+ def _prepare_breadcrumbs_template(self):
+ self.data[self.DATA_BREADCRUMBS_TEMPLATE] = 'bhsearch_breadcrumbs.html'
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=1455906&r1=1455905&r2=1455906&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
Wed Mar 13 12:31:28 2013
@@ -69,7 +69,9 @@ class WhooshBackend(Component):
content=TEXT(stored=True,
analyzer=analysis.StandardAnalyzer(stoplist=None)),
changes=TEXT(analyzer=analysis.StandardAnalyzer(stoplist=None)),
- )
+ owner=TEXT(stored=True,
+ analyzer=analysis.SimpleAnalyzer()),
+ )
max_fragment_size = IntOption('bhsearch', 'max_fragment_size', 240,
'The maximum number of characters allowed in a '
@@ -224,6 +226,11 @@ class WhooshBackend(Component):
fields,
highlight_fields,
query_parameters)
+ try:
+ results.debug['actual_query'] =
unicode(query.simplify(searcher))
+ except TypeError:
+ # Simplify has a bug that causes it to fail sometimes.
+ pass
return results
def _workaround_join_query_and_filter(
Modified:
incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_theme/bhtheme/templates/bh_ticket.html
URL:
http://svn.apache.org/viewvc/incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_theme/bhtheme/templates/bh_ticket.html?rev=1455906&r1=1455905&r2=1455906&view=diff
==============================================================================
---
incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_theme/bhtheme/templates/bh_ticket.html
(original)
+++
incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_theme/bhtheme/templates/bh_ticket.html
Wed Mar 13 12:31:28 2013
@@ -64,6 +64,8 @@
<py:if test="has_property_editor">
// Install in place editing
+ install_workflow();
+ $('#edit-cancel').click(revert_ticket);
var modify_elem = $('#modify');
<py:if test="ticket.exists">
@@ -109,45 +111,7 @@
addWikiFormattingToolbar(editor.get(0));
}
if (fieldnm === 'summary') {
- // Install inline edit form
- var submit_ticket = $('#tmpl-inplace-submit').html();
- submit_ticket = $(submit_ticket).prepend(editor)
- .appendTo(fc);
- submit_ticket.find('#edit-cancel').click(revert_ticket);
editor.wrap('<div class="btn-group"></div>')
-
- // Workflow actions
- var actions_box = submit_ticket.find('#workflow-actions')
- .click(function(e) { e.stopPropagation(); });
- $('#action').children('div').each(function() {
- var action_ui = $(this).clone(false).prependTo(actions_box)
- .wrap('<li style="padding: 5px 10px"></li>');
- var action_trigger = action_ui.find('input[name=action]');
-
- function action_click() {
- var newlabel = action_ui.find('label[for^=action_]')
- .text();
- $('#submit-action-label').text(newlabel);
-
- // Enable | disable action controls
- actions_box.find('input[name=action]').each(function() {
- $(this).siblings().find("*[id]")
- .enable($(this).checked());
- $(this).siblings().filter("*[id]")
- .enable($(this).checked());
- });
- }
- action_trigger.click(action_click);
- if (action_trigger.attr('checked'))
- action_click();
-
- var action_help = action_ui.find('.help-block').detach()
- .text().replace(/\s+/g, ' ').replace(/^ Tip /g, 'Tip:
')
- .replace(/^\s$/, '');
- if (action_help)
- $('<i class="icon-info-sign"></i>').appendTo(action_ui)
- .attr('title', action_help);
- })
}
});
@@ -157,18 +121,72 @@
$('#inplace-propertyform').submit(function() {
$('#hidden-comment').val($('#comment').val());
})
- $('#inplace-edit-button').hide();
+ $('#inplace-edit').hide();
+ $('#edit-state-buttons').show();
+ $('#vc-status a').hide();
+ $('#edit-workflow-buttons').show();
}
- function revert_ticket() {
+ function revert_ticket(e) {
$('[data-edit="inplace"]').each(function() {
var fc = $(this).removeClass('edit-active');
fc.html(fc.attr('data-edit-orig')).attr('data-edit-orig', '');
});
- $('#inplace-edit-button').show();
+ $('#inplace-edit').show();
$('h2#vc-summary span').attr('contenteditable', 'false');
+ $('#edit-state-buttons').hide();
+ $('#vc-status a').show();
+ $('#edit-workflow-buttons').hide();
+ e.preventDefault();
}
+ function install_workflow(){
+ var actions_box = $('#workflow-actions')
+ .click(function(e) { e.stopPropagation(); });
+ $('#action').children('div').each(function() {
+ var action_ui = $(this).clone(false).prependTo(actions_box)
+ .wrap('<li style="padding: 5px 10px"></li>');
+ var action_trigger = action_ui.find('input[name=action]');
+ var action_select_trigger = action_ui.find('select');
+ var action_input_trigger =
action_ui.find('input:not([name=action])');
+
+ function action_click() {
+ var action = action_ui.find('input[name=action]').val();
+ var newowner =
action_ui.find('input[id$=_reassign_owner]').val();
+ var newresolution =
action_ui.find('select[id$=_resolution]').val();
+ var newlabel = action_ui.find('label[for^=action_]')
+ .text();
+ if (action === 'leave')
+ newlabel = newlabel + ' as ' + $('#vc-status a').text();
+ else if (newowner)
+ newlabel = newlabel + ' to ' + newowner;
+ else if (newresolution)
+ newlabel = newlabel + ' as ' + newresolution;
+ $('#submit-action-label').text(newlabel);
+
+ // Enable | disable action controls
+ actions_box.find('input[name=action]').each(function() {
+ $(this).siblings().find("*[id]")
+ .enable($(this).checked());
+ $(this).siblings().filter("*[id]")
+ .enable($(this).checked());
+ });
+ }
+ action_trigger.click(action_click);
+ action_select_trigger.change(action_click);
+ action_input_trigger.blur(action_click);
+ if (action_trigger.attr('checked'))
+ action_click();
+
+ var action_help = action_ui.find('.help-block').detach()
+ .text().replace(/\s+/g, ' ').replace(/^ Tip /g, 'Tip: ')
+ .replace(/^\s$/, '');
+ if (action_help)
+ $('<i class="icon-info-sign"></i>').appendTo(action_ui)
+ .attr('title', action_help);
+ });
+ }
+
$('h2#vc-summary span').blur(function () {
if ($('#vc-summary.edit-active').length == 1) {
$('#vc-summary.ticket-summary
input#field-summary').val($('#vc-summary span').text());
@@ -270,6 +288,15 @@
<i class="icon-edit"></i>
<span class="hidden-phone hidden-tablet">${_('Modify
Ticket')}</span>
</button>
+ <div id="edit-state-buttons" class="btn-toolbar" style="display:
None">
+ <button id="edit-submit" class="btn" type="submit"
+ value="Submit changes" name="submit">
+ Submit changes
+ </button>
+ <button id="edit-cancel" class="btn-link" title="Discard changes">
+ Cancel
+ </button>
+ </div>
</div>
</py:if>
</py:def>
@@ -373,7 +400,7 @@
</div>
<!--! do not show attachments for old versions of this ticket or for
new tickets -->
- <div py:if="not version and version != 0 and ticket.exists"
class="span8">
+ <div class="$colspan" py:if="not version and version != 0 and
ticket.exists">
<xi:include href="bh_list_of_attachments.html"
py:with="alist = attachments; foldable = True"/>
</div>
@@ -665,24 +692,6 @@
<script type="text/x-tmpl" id="tmpl-inplace-submit"
py:if="has_property_editor">
<div class="btn-toolbar">
<div class="btn-group">
- <button id="edit-submit" class="btn btn-primary" type="submit"
- value="Submit changes" name="submit">
- Update (<span id="submit-action-label"></span>)
- </button>
- <button class="btn btn-primary dropdown-toggle"
data-toggle="dropdown">
- <span class="caret"></span>
- </button>
- <ul class="dropdown-menu">
- <fieldset id="workflow-actions">
- </fieldset>
- </ul>
- </div>
- <div class="btn-group">
- <button id="edit-cancel" class="btn-link" title="Discard changes">
- Cancel
- </button>
- </div>
- <div class="btn-group">
<span class="trac-loading"> </span>
</div>
</div>
Modified:
incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_theme/bhtheme/templates/bh_ticket_box.html
URL:
http://svn.apache.org/viewvc/incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_theme/bhtheme/templates/bh_ticket_box.html?rev=1455906&r1=1455905&r2=1455906&view=diff
==============================================================================
---
incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_theme/bhtheme/templates/bh_ticket_box.html
(original)
+++
incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_theme/bhtheme/templates/bh_ticket_box.html
Wed Mar 13 12:31:28 2013
@@ -60,6 +60,18 @@ Arguments:
<py:otherwise>${ticket[field.name]}</py:otherwise>
</py:choose>
</py:if>
+ <py:if test="field.name == 'status'">
+ <div id="edit-workflow-buttons" class="btn-group"
style="display: none">
+ <button class="btn dropdown-toggle" data-toggle="dropdown"
style="white-space: normal">
+ <span id="submit-action-label"></span>
+ <span class="caret"></span>
+ </button>
+ <ul class="dropdown-menu">
+ <fieldset id="workflow-actions">
+ </fieldset>
+ </ul>
+ </div>
+ </py:if>
</div>
</div>
</div>
Modified:
incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_theme/bhtheme/templates/bloodhound_theme.html
URL:
http://svn.apache.org/viewvc/incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_theme/bhtheme/templates/bloodhound_theme.html?rev=1455906&r1=1455905&r2=1455906&view=diff
==============================================================================
---
incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_theme/bhtheme/templates/bloodhound_theme.html
(original)
+++
incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_theme/bhtheme/templates/bloodhound_theme.html
Wed Mar 13 12:31:28 2013
@@ -90,11 +90,20 @@
<div class="span6">
<div id="searchbox" class="btn-toolbar">
<div class="btn-group">
- <form id="mainsearch" class="form-inline"
action="${href.search()}" method="get">
+ <form id="mainsearch" class="form-inline"
+ action="${href.search() if req.path_info == u'/search'
else
+ href.bhsearch() if (req.path_info ==
u'/bhsearch' or
+ is_bhsearch_default) else
+ href.search()}"
+ method="get">
<div class="input-append">
<input type="text" class="span3" id="q" name="q"
placeholder="Search anything. Try #EF-492."
value="${req.search_query}" />
+ <input py:for="name, value in extra_search_fields"
+ type="hidden"
+ name="$name"
+ value="$value" />
<button type="submit" class="btn btn-warning">
<span class="hidden-phone hidden-tablet">Search</span>
<i class="icon-search icon-white"></i>
Modified:
incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_theme/bhtheme/theme.py
URL:
http://svn.apache.org/viewvc/incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_theme/bhtheme/theme.py?rev=1455906&r1=1455905&r2=1455906&view=diff
==============================================================================
---
incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_theme/bhtheme/theme.py
(original)
+++
incubator/bloodhound/branches/bep_0003_multiproduct/bloodhound_theme/bhtheme/theme.py
Wed Mar 13 12:31:28 2013
@@ -23,7 +23,7 @@ from genshi.core import TEXT
from genshi.filters.transform import Transformer
from genshi.output import DocType
-from trac.config import Option
+from trac.config import Option, BoolOption
from trac.core import *
from trac.config import ListOption
from trac.mimeview.api import get_mimetype
@@ -135,6 +135,7 @@ class BloodhoundTheme(ThemeBase):
# General purpose
'about.html' : ('bh_about.html', None),
'history_view.html' : ('bh_history_view.html', None),
+ 'timeline.html' : ('bh_timeline.html', None),
# Account manager plugin
'login.html' : ('bh_login.html', None),
@@ -157,6 +158,8 @@ class BloodhoundTheme(ThemeBase):
labels_footer_right = Option('labels', 'footer_right', '')
+ is_bhsearch_default = BoolOption('bhsearch', 'is_default', False)
+
_wiki_pages = None
Chrome.default_html_doctype = DocType.HTML5
@@ -283,6 +286,7 @@ class BloodhoundTheme(ThemeBase):
if is_active_theme and data is not None:
data['responsive_layout'] = self.env.config.getbool(
'bloodhound', 'responsive_layout', 'true')
+ data['is_bhsearch_default'] = self.is_bhsearch_default
return template, data, content_type