change according to the PEP 8 and trac style add the duplicate ticket feature to the popover ticket create
On Fri, Jul 18, 2014 at 12:55 AM, <[email protected]> wrote: > Author: thimal > Date: Thu Jul 17 19:25:49 2014 > New Revision: 1611444 > > URL: http://svn.apache.org/r1611444 > Log: > add the duplicate feature to popover ticket create and did some style > change according to the bloodhound style > > Added: > > bloodhound/branches/bep_0013_dynamic_clientside_features/bloodhound_theme/bhtheme/htdocs/js/popoverDupSearch.js > (with props) > Modified: > > bloodhound/branches/bep_0013_dynamic_clientside_features/bloodhound_theme/bhtheme/htdocs/js/DupeSearch.js > > bloodhound/branches/bep_0013_dynamic_clientside_features/bloodhound_theme/bhtheme/theme.py > > Modified: > bloodhound/branches/bep_0013_dynamic_clientside_features/bloodhound_theme/bhtheme/htdocs/js/DupeSearch.js > URL: > http://svn.apache.org/viewvc/bloodhound/branches/bep_0013_dynamic_clientside_features/bloodhound_theme/bhtheme/htdocs/js/DupeSearch.js?rev=1611444&r1=1611443&r2=1611444&view=diff > > ============================================================================== > --- > bloodhound/branches/bep_0013_dynamic_clientside_features/bloodhound_theme/bhtheme/htdocs/js/DupeSearch.js > (original) > +++ > bloodhound/branches/bep_0013_dynamic_clientside_features/bloodhound_theme/bhtheme/htdocs/js/DupeSearch.js > Thu Jul 17 19:25:49 2014 > @@ -1,17 +1,17 @@ > -$(document).ready(function() { > +jQuery(document).ready(function() { > > $('div#content.ticket h2#vc-summary').blur(function() { > var text = $('div#content.ticket h2#vc-summary').text(); > if (text.length > 0) { > > var html = '<h5 class="loading">Loading related > tickets..</h5>'; > - var dupeticketlistDiv = $('div#content.ticket > h2#vc-summary + div#dupeticketlist'); > - if (dupeticketlistDiv.length == 0) { > + var duplicate_eticket_list_div = > $('div#content.ticket h2#vc-summary + div#dupeticketlist'); > + if (duplicate_eticket_list_div.length == 0) { > $('div#content.ticket > h2#vc-summary').after('<div id="dupeticketlist" > style="display:none;"></div>'); > - dupeticketlistDiv = $('div#content.ticket > h2#vc-summary + div#dupeticketlist'); > + duplicate_eticket_list_div = > $('div#content.ticket h2#vc-summary + div#dupeticketlist'); > } > - $('ul',dupeticketlistDiv).slideUp('fast'); > - dupeticketlistDiv.html(html).slideDown(); > + $('ul',duplicate_eticket_list_div).slideUp('fast'); > + duplicate_eticket_list_div.html(html).slideDown(); > > $.ajax({ > url:'duplicate_ticket_search', > @@ -20,48 +20,51 @@ $(document).ready(function() { > > success: function(data, status) { > var tickets =data; > - var ticketBaseHref = 'ticket/'; > - var searchBaseHref = > 'bhsearch?type=ticket&q='; > - var maxTickets = 15; > + var ticket_base_Href = 'ticket/'; > + var search_base_Href = > 'bhsearch?type=ticket&q='; > + var max_tickets = 15; > > var html = ''; > if (tickets === null) { > // error > - > dupeticketlistDiv.html('<h5 class="error">Error loading tickets.</h5>'); > + > duplicate_eticket_list_div.html('<h5 class="error">Error loading > tickets.</h5>'); > } else if (tickets.length <= 0) { > // no dupe tickets > - > dupeticketlistDiv.slideUp(); > + > duplicate_eticket_list_div.slideUp(); > } else { > - html = '<h5>Possible > related tickets:</h5><ul style="display:none;">' > + html = '<h5>Possible > related tickets:</h5><ul id="results">' > tickets = > tickets.reverse(); > > - for (var i = 0; i < > tickets.length && i < maxTickets; i++) { > + for (var i = 0; i < > tickets.length && i < max_tickets; i++) { > var ticket = > tickets[i]; > html += '<li > class="highlight_matches" title="' + ticket.description + > - '"><a > href="' + ticketBaseHref + ticket.url + > + '"><a > href="' + ticket_base_Href + ticket.url + > > '"><span class="' + htmlencode(ticket.status) + '">#' + > - > ticket.url + '</span></a>: ' + htmlencode(ticket.type) + ': ' + > - > ticket.summary + '(' + htmlencode(ticket.status) + > - > (ticket.url ? ': ' + htmlencode(ticket.url) : '') + > - ')' + > '</li>' > + > ticket.url + '</span></a>: ' + > + > ticket.summary + ' (' + htmlencode(ticket.status) > + +': '+ > htmlencode(ticket.type) + > + ') ' > +'<span class="author">created by '+ticket.owner +'</span> <span > class="date">at ' +ticket.date+ '</span></li>' > } > html += '</ul>'; > - if (tickets.length > > maxTickets) { > + if (tickets.length > > max_tickets) { > var text = > $('div#content.ticket input#field-summary').val(); > - html += '<a > href="' + searchBaseHref + escape(text) + '">More..</a>'; > + html += '<a > href="' + search_base_Href + escape(text) + '">More..</a>'; > } > > - > dupeticketlistDiv.html(html); > - $('> ul', > dupeticketlistDiv).slideDown(); > + > duplicate_eticket_list_div.html(html); > + $('> ul', > duplicate_eticket_list_div).slideDown(); > > } > > }, > error: function(xhr, textStatus, > exception) { > - dupeticketlistDiv.html('<h5 > class="error">Error loading tickets: ' + textStatus + '</h5>'); > + > duplicate_eticket_list_div.html('<h5 class="error">Error loading tickets: ' > + textStatus + '</h5>'); > } > }); > - } > + }else{ > + var duplicate_eticket_list_div = $('div#content.ticket > div#dupeticketlist'); > + duplicate_eticket_list_div.slideUp('fast'); > + } > }); > > function htmlencode(text) { > > Added: > bloodhound/branches/bep_0013_dynamic_clientside_features/bloodhound_theme/bhtheme/htdocs/js/popoverDupSearch.js > URL: > http://svn.apache.org/viewvc/bloodhound/branches/bep_0013_dynamic_clientside_features/bloodhound_theme/bhtheme/htdocs/js/popoverDupSearch.js?rev=1611444&view=auto > > ============================================================================== > --- > bloodhound/branches/bep_0013_dynamic_clientside_features/bloodhound_theme/bhtheme/htdocs/js/popoverDupSearch.js > (added) > +++ > bloodhound/branches/bep_0013_dynamic_clientside_features/bloodhound_theme/bhtheme/htdocs/js/popoverDupSearch.js > Thu Jul 17 19:25:49 2014 > @@ -0,0 +1,73 @@ > +jQuery(document).ready(function() { > + > + $('input#field-summary.input-block-level').blur(function() { > + var text = > $('input#field-summary.input-block-level').val(); > + if (text.length > 0) { > + > + var html = '<h5 class="loading">Loading related > tickets..</h5>'; > + var dupelicate_ticket_list_div = > $('div.popover-content input#field-summary + div#dupeticketlist'); > + if (dupelicate_ticket_list_div.length == 0) { > + $('div.popover-content > input#field-summary').after('<div id="dupeticketlist" > style="display:none;"></div>'); > + dupelicate_ticket_list_div = > $('div.popover-content input#field-summary + div#dupeticketlist'); > + } > + $(dupelicate_ticket_list_div).slideUp('fast'); > + dupelicate_ticket_list_div.html(html).slideDown(); > + > + $.ajax({ > + url:'duplicate_ticket_search', > + data:{q:text}, > + type:'GET', > + success: function(data, status) { > + var tickets =data; > + var ticket_base_href = 'ticket/'; > + var search_base_Href = > 'bhsearch?type=ticket&q='; > + var max_tickets = 5; > + > + var html = ''; > + if (tickets === null) { > + // error > + > dupelicate_ticket_list_div.html('<h5 class="error">Error loading > tickets.</h5>'); > + } else if (tickets.length <= 0) { > + // no dupe tickets > + > dupelicate_ticket_list_div.slideUp(); > + } else { > + html = '<h5>Possible > related tickets:</h5><ul style="display:none;">'; > + //tickets = > tickets.reverse(); > + > + for (var i = 0; i < > tickets.length && i < max_tickets; i++) { > + var ticket = > tickets[i]; > + html += '<li > class="highlight_matches" title="' + ticket.description + > + '"><a > href="' + ticket_base_href + ticket.url + > + > '"><span class="' + htmlencode(ticket.status) + '">#' + > + > ticket.url + '</span></a>: ' + htmlencode(ticket.type) + ': ' + > + > ticket.summary + '(' + htmlencode(ticket.status) + > + > (ticket.url ? ': ' + htmlencode(ticket.url) : '') + > + ')' + > '</li>' > + } > + html += '</ul>'; > + if (tickets.length > > max_tickets) { > + var text = > $('div.popover-content input#field-summary').val(); > + html += '<a > href="' + search_base_Href + escape(text) + '">More..</a>'; > + } > + > + > dupelicate_ticket_list_div.html(html); > + $('> ul', > dupelicate_ticket_list_div).slideDown(); > + > + } > + > + }, > + error: function(xhr, textStatus, > exception) { > + > dupelicate_ticket_list_div.html('<h5 class="error">Error loading tickets: ' > + textStatus + '</h5>'); > + } > + }); > + }else{ > + var dupelicate_ticket_list_div = $('div.popover-content > input#field-summary + div#dupeticketlist'); > + dupelicate_ticket_list_div.slideUp(); > + } > + }); > + > + function htmlencode(text) { > + return $('<div/>').text(text).html().replace(/"/g, > '"').replace(/'/g, '''); > + } > +}); > + > > Propchange: > bloodhound/branches/bep_0013_dynamic_clientside_features/bloodhound_theme/bhtheme/htdocs/js/popoverDupSearch.js > > ------------------------------------------------------------------------------ > svn:eol-style = native > > Modified: > bloodhound/branches/bep_0013_dynamic_clientside_features/bloodhound_theme/bhtheme/theme.py > URL: > http://svn.apache.org/viewvc/bloodhound/branches/bep_0013_dynamic_clientside_features/bloodhound_theme/bhtheme/theme.py?rev=1611444&r1=1611443&r2=1611444&view=diff > > ============================================================================== > --- > bloodhound/branches/bep_0013_dynamic_clientside_features/bloodhound_theme/bhtheme/theme.py > (original) > +++ > bloodhound/branches/bep_0013_dynamic_clientside_features/bloodhound_theme/bhtheme/theme.py > Thu Jul 17 19:25:49 2014 > @@ -19,6 +19,7 @@ > > import sys > > +from trac.util.datefmt import format_datetime, user_time > from collections import Counter > > import fnmatch > @@ -42,7 +43,7 @@ from trac.util.presentation import to_js > from trac.versioncontrol.web_ui.browser import BrowserModule > from trac.web.api import IRequestFilter, IRequestHandler, > ITemplateStreamFilter > from trac.web.chrome import (add_stylesheet, add_warning, > INavigationContributor, > - ITemplateProvider, prevnext_nav, Chrome, > add_script) > + ITemplateProvider, prevnext_nav, Chrome, > add_script, add_script_data) > from trac.wiki.admin import WikiAdmin > from trac.wiki.formatter import format_to_html > > @@ -694,8 +695,8 @@ class AutocompleteUsers(Component): > implements(IRequestFilter, IRequestHandler, > ITemplateProvider, ITemplateStreamFilter) > > - selectfields = ListOption('autocomplete', 'fields', default='', > - doc='select fields to transform to > autocomplete text boxes') > + select_fields = ListOption('autocomplete', 'fields', default='', > + doc='select fields to transform to > autocomplete text boxes') > > # IRequestHandler methods > > @@ -711,16 +712,15 @@ class AutocompleteUsers(Component): > if req.args.get('users', '1') == '1': > users = self._get_users(req) > if req.perm.has_permission('EMAIL_VIEW'): > - subjects = ['{"label":"%s %s %s","value":"%s"}' % > (user[USER] and '%s' % user[USER] or '', > - user[EMAIL] and '<%s>' % > user[EMAIL] or '', > - user[NAME] and '%s' % > user[NAME] or '', > - user[USER]) > - for value, user in users] # value unused > (placeholder needed for sorting) > + subjects = ['{"label":"%s %s %s","value":"%s"}' % > (user[USER] and '%s' % user[USER] or > + '', > user[EMAIL] and '<%s>' % user[EMAIL] or > + '', > user[NAME] and '%s' % user[NAME] or > + '', > user[USER]) > + for value, user in users] > else: > - subjects = ['{"label":"%s %s","value":"%s"}' % > (user[USER] and '%s' % user[USER] or '', > - user[NAME] and '%s' % > user[NAME] or '', > - user[USER]) > - for value, user in users] # value unused > (placeholder needed for sorting) > + subjects = ['{"label":"%s %s","value":"%s"}' % > (user[USER] and '%s' % user[USER] or '', user[NAME] and > + '%s' % > user[NAME] or'', user[USER]) > + for value, user in users] > > respond_str = ','.join(subjects).encode('utf-8') > respond_str = '[' + respond_str + ']' > @@ -730,7 +730,6 @@ class AutocompleteUsers(Component): > > def get_htdocs_dirs(self): > from pkg_resources import resource_filename > - > return [('autocompleteusers', resource_filename(__name__, > 'htdocs'))] > > def get_templates_dirs(self): > @@ -750,7 +749,6 @@ class AutocompleteUsers(Component): > add_script(req, 'autocompleteusers/js/format_item.js') > if template == 'query.html': > add_script(req, > 'autocompleteusers/js/autocomplete_query.js') > - > return template, data, content_type > > # ITemplateStreamFilter methods > @@ -762,7 +760,7 @@ class AutocompleteUsers(Component): > fields = [field['name'] for field in data['ticket'].fields > if field['type'] == 'select'] > fields = set(sum([fnmatch.filter(fields, pattern) > - for pattern in self.selectfields], [])) > + for pattern in self.select_fields], [])) > > js = "" > > @@ -770,7 +768,7 @@ class AutocompleteUsers(Component): > > restrict_owner = self.env.config.getbool('ticket', > 'restrict_owner') > if req.path_info.startswith('/ticket/'): > - js = """$(document).bind('DOMSubtreeModified', function > (){ > + js = """jQuery(document).bind('DOMSubtreeModified', > function (){ > $( "#field-cc" ).autocomplete({ > source: "user_list" > multiple: true, > @@ -779,7 +777,7 @@ class AutocompleteUsers(Component): > }); > });""" > if not restrict_owner: > - js = """$(document).bind('DOMSubtreeModified', > function (){ > + js = """jQuery(document).bind('DOMSubtreeModified', > function (){ > > $( "#field-cc" ).autocomplete({ > source: "user_list", > @@ -817,16 +815,14 @@ class AutocompleteUsers(Component): > formatItem: formatItem > }); > });""" > - stream = stream | > Transformer('.//head').append(tag.script(Markup(js), > - > type='text/javascript')) > + stream = stream | > Transformer('.//head').append(tag.script(Markup(js), > type='text/javascript')) > > elif filename == 'bh_admin_perms.html': > users = self._get_users(req) > - subjects = ['{"label":"%s %s %s","value":"%s"}' % (user[USER] > and '%s' % user[USER] or '', > - user[EMAIL] and '<%s>' % > user[EMAIL] or '', > - user[NAME] and '%s' % user[NAME] or > '', > - user[USER]) > - for value, user in users] # value unused > (placeholder needed for sorting) > + subjects = ['{"label":"%s %s %s","value":"%s"}' % (user[USER] > and '%s' % user[USER] or '', user[EMAIL] and > + '<%s>' % > user[EMAIL] or '', user[NAME] and > + '%s' % > user[NAME] or '', user[USER]) > + for value, user in users] > > groups = self._get_groups(req) > if groups: > @@ -839,7 +835,7 @@ class AutocompleteUsers(Component): > respond_str_groups = > ','.join(subjects_groups).encode('utf-8') > respond_str_groups = '[' + respond_str_groups + ']' > > - js = """$(document).ready(function () { > + js = """jQuery(document).ready(function () { > var subjects = %(subject)s > var groups = %(group)s > $("#gp_subject").autocomplete( { > @@ -855,15 +851,12 @@ class AutocompleteUsers(Component): > formatItem: formatItem > }); > });""" > - js_ticket = js % {'subject': respond_str_subjects, > - 'group': respond_str_groups > + js_ticket = js % {'subject': respond_str_subjects, 'group': > respond_str_groups > } > - stream = stream | > Transformer('.//head').append(tag.script(Markup(js_ticket), > - > type='text/javascript')) > > + stream = stream | > Transformer('.//head').append(tag.script(Markup(js_ticket), > type='text/javascript')) > return stream > > - > # Private methods > > def _get_groups(self, req): > @@ -877,9 +870,10 @@ class AutocompleteUsers(Component): > db = self.env.get_db_cnx() > cursor = db.cursor() > cursor.execute("""SELECT DISTINCT username FROM permission""") > - usernames = [user[0] for user in self.env.get_known_users()] > - return sorted([row[0] for row in cursor if not row[0] in usernames > - and row[0].lower().startswith(query)]) > + user_names = [user[0] for user in self.env.get_known_users()] > + return sorted([row[0] > + for row in cursor > + if not row[0] in user_names and > row[0].lower().startswith(query)]) > > def _get_users(self, req): > # instead of known_users, could be > @@ -929,8 +923,7 @@ class KeywordSuggestModule(Component): > def post_process_request(self, req, template, data, content_type): > """add the necessary javascript and css files > """ > - if req.path_info.startswith('/ticket/') or \ > - req.path_info.startswith('/newticket') or \ > + if req.path_info.startswith('/ticket/') or > req.path_info.startswith('/newticket') or \ > (req.path_info.startswith('/query')): > add_script(req, > 'keywordssuggest/js/bootstrap-tagsinput.js') > add_stylesheet(req, > 'keywordssuggest/css/bootstrap-tagsinput.css') > @@ -942,8 +935,7 @@ class KeywordSuggestModule(Component): > """add the jQuery tagsinput function to ticket and query pages > """ > > - if not (filename == 'bh_ticket.html' or > - (filename == 'bh_query.html')): > + if not (filename == 'bh_ticket.html' or (filename == > 'bh_query.html')): > return stream > > keywords = self._get_keywords_string(req) > @@ -970,7 +962,6 @@ class KeywordSuggestModule(Component): > js = """jQuery(document).ready(function($) { > var keywords = %(keywords)s > > - > $('%(field)s').tagsinput({ > typeahead: { > source: keywords > @@ -979,7 +970,7 @@ class KeywordSuggestModule(Component): > });""" > > if filename == 'bh_query.html': > - js = """$(document).ready(function ($) { > + js = """jQuery(document).ready(function ($) { > function addAutocompleteBehavior() { > var filters = $('#filters'); > var contains = $.contains // jQuery 1.4+ > @@ -1040,14 +1031,10 @@ class KeywordSuggestModule(Component): > js_ticket = js % {'field': '#field-' + self.field_opt, > 'keywords': keywords > } > - stream = stream | Transformer('.//head').append \ > - (tag.script(Markup(js_ticket), > - type='text/javascript')) > + stream = stream | > Transformer('.//head').append(tag.script(Markup(js_ticket), > type='text/javascript')) > if req.path_info.startswith('/query'): > js_ticket = js % {'keywords': keywords} > - stream = stream | Transformer('.//head').append \ > - (tag.script(Markup(js_ticket), > - type='text/javascript')) > + stream = stream | > Transformer('.//head').append(tag.script(Markup(js_ticket), > type='text/javascript')) > > return stream > > @@ -1070,16 +1057,18 @@ class KeywordSuggestModule(Component): > # get keywords from db > db = self.env.get_db_cnx() > cursor = db.cursor() > - product = self.env.product._data['prefix'] > - sql = """SELECT t.keywords FROM ticket AS t WHERE t.keywords IS > NOT null AND t.product ='%s'""" % product > - > - cursor.execute(sql) > keywords = [] > - for row in cursor: > - if not row[0] == '': > - row_val = str(row[0]).split(',') > - for val in row_val: > - keywords.append(val.strip()) > + if self.env.product is not None: > + product = self.env.product._data['prefix'] > + sql = """SELECT t.keywords FROM ticket AS t WHERE t.keywords > IS NOT null AND t.product ='%s'""" % product > + > + cursor.execute(sql) > + > + for row in cursor: > + if not row[0] == '': > + row_val = str(row[0]).split(',') > + for val in row_val: > + keywords.append(val.strip()) > # sort keywords according to frequency of occurrence > if keywords: > keyword_dic = Counter(keywords) > @@ -1092,8 +1081,10 @@ class KeywordSuggestModule(Component): > # component to find duplicate tickets > #DuplicateTicketSearch component basic structure is taken from trac > DuplicateTicketSearch plugin > #https://trac-hacks.org/wiki/DuplicateTicketSearchPlugin > + > + > class DuplicateTicketSearch(Component): > - implements(ITemplateProvider, ITemplateStreamFilter,IRequestHandler) > + implements(ITemplateProvider, ITemplateStreamFilter, IRequestHandler) > > # ITemplateProvider methods > > @@ -1104,10 +1095,10 @@ class DuplicateTicketSearch(Component): > def get_templates_dirs(self): > return [] > > - > # ITemplateStreamFilter methods > > def filter_stream(self, req, method, filename, stream, data): > + add_script(req, 'duplicateticketsearch/js/popoverDupSearch.js') > > if filename == 'bh_ticket.html': > ticket = data.get('ticket') > @@ -1116,7 +1107,7 @@ class DuplicateTicketSearch(Component): > > return stream > > - # IRequestHandler methods > + # IRequestHandler methods > > def match_request(self, req): > """Handle requests sent to /user_list and /ticket/user_list > @@ -1124,23 +1115,78 @@ class DuplicateTicketSearch(Component): > return req.path_info.rstrip('/') == '/duplicate_ticket_search' > > def process_request(self, req): > - product = self.env.product._data['prefix'] > - query_result = BloodhoundSearchApi(self.env).query( > - req.args.get('q'), > - pagenum=1, > - pagelen=10, > - filter=['type:"ticket"', 'product:"'+product+'"'], > - highlight=True, > - ) > - ticket_list = [] > - cnt = 0 > - for ticket in query_result.docs: > - ticket_list.append(to_json({'summary': > query_result.highlighting[cnt]['summary'], 'description': > query_result.highlighting[cnt]['content'],'type':ticket['type'] > ,'status':ticket['status'] , 'owner':ticket['author'] > ,'date':ticket['time'].strftime('%m/%d/%Y') ,'url': ticket['id']})) > - cnt+1 > + terms = req.args.get('q').split(' ') > + > + with self.env.db_direct_query as db: > + sql, args = self._search_to_sql(db, ['summary', 'keywords', > 'description'], terms) > + sql2, args2 = self._search_to_sql(db, ['newvalue'], terms) > + sql3, args3 = self._search_to_sql(db, ['value'], terms) > + if self.env.product is not None: > + product_sql = "product='%s' AND" % > self.env.product._data['prefix'] > + else: > + product_sql = "" > + ticket_list = [] > + ticket_list_value = [] > + for summary, desc, author, type, tid, ts, status, resolution > in \ > + db("""SELECT summary, description, reporter, type, id, > + time, status, resolution > + FROM ticket > + WHERE (%s id IN ( > + SELECT id FROM ticket WHERE %s > + UNION > + SELECT ticket FROM ticket_change > + WHERE field='comment' AND %s > + UNION > + SELECT ticket FROM ticket_custom WHERE %s > + )) > + """ % (product_sql, sql, sql2, sql3), > + args + args2 + args3): > + > + summary_term_count = 0 > + summary_list = summary.split(' ') > + for s in summary_list: > + for t in terms: > + if s.lower() == t.lower(): > + summary = summary.replace(s,'<em>'+t+'</em>') > + summary_term_count += 1 > + break > + > + ticket_list.append(to_json({'summary': summary, > 'description': desc, 'type': type, 'status': status, > + 'owner': author, 'date': > user_time(req, format_datetime, ts), 'url': tid})) > + ticket_list_value.append(summary_term_count) > + ticket_list = [x for (y, x) in sorted(zip(ticket_list_value, > ticket_list))] > str_list = '['+','.join(ticket_list)+']' > > req.send(str_list, 'application/json') > > + # Private methods > + > + def _search_to_sql(self, db, columns, terms): > + """Convert a search query into an SQL WHERE clause and > corresponding > + parameters. > + > + The result is returned as an `(sql, params)` tuple. > + """ > + assert columns and terms > + > + likes = ['%s %s' % (i, db.like()) for i in columns] > + c = ' OR '.join(likes) > + sql = '(' + ') OR ('.join([c] * len(terms)) + ')' > + args = [] > + for t in terms: > + args.extend(['%' + db.like_escape(t) + '%'] * len(columns)) > + return sql, tuple(args) > + > + > + > + > + > + > + > + > + > + > + > > > > > > -- *Thimal Kempitiya <http://www.facebook.com/thimalk> UndergraduateDepartment of Computer Science and Engineering University of Moratuwa.*
