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