Back to index

moin  1.9.0~rc2
fullsearch.py
Go to the documentation of this file.
00001 # -*- coding: iso-8859-1 -*-
00002 """
00003     MoinMoin - fullsearch action
00004 
00005     This is the backend of the search form. Search pages and print results.
00006 
00007     @copyright: 2001 by Juergen Hermann <jh@web.de>
00008     @license: GNU GPL, see COPYING for details.
00009 """
00010 
00011 import re, time
00012 from MoinMoin.Page import Page
00013 from MoinMoin import wikiutil
00014 from MoinMoin.support.parsedatetime.parsedatetime import Calendar
00015 from MoinMoin.web.utils import check_surge_protect
00016 
00017 def checkTitleSearch(request):
00018     """ Return 1 for title search, 0 for full text search, -1 for idiot spammer
00019         who tries to press all buttons at once.
00020 
00021     When used in FullSearch macro, we have 'titlesearch' parameter with
00022     '0' or '1'. In standard search, we have either 'titlesearch' or
00023     'fullsearch' with localized string. If both missing, default to
00024     True (might happen with Safari) if this isn't an advanced search.
00025 """
00026     form = request.values
00027     if 'titlesearch' in form and 'fullsearch' in form:
00028         ret = -1 # spammer / bot
00029     else:
00030         try:
00031             ret = int(form['titlesearch'])
00032         except ValueError:
00033             ret = 1
00034         except KeyError:
00035             ret = ('fullsearch' not in form and not isAdvancedSearch(request)) and 1 or 0
00036     return ret
00037 
00038 def isAdvancedSearch(request):
00039     """ Return True if advanced search is requested """
00040     try:
00041         return int(request.values['advancedsearch'])
00042     except KeyError:
00043         return False
00044 
00045 
00046 def searchHints(f, hints):
00047     """ Return a paragraph showing hints for a search
00048 
00049     @param f: the formatter to use
00050     @param hints: list of hints (as strings) to show
00051     """
00052     return ''.join([
00053         f.paragraph(1, attr={'class': 'searchhint'}),
00054         # this is illegal formatter usage anyway, so we can directly use a literal
00055         "<br>".join(hints),
00056         f.paragraph(0),
00057     ])
00058 
00059 
00060 def execute(pagename, request, fieldname='value', titlesearch=0, statistic=0):
00061     _ = request.getText
00062     titlesearch = checkTitleSearch(request)
00063     if titlesearch < 0:
00064         check_surge_protect(request, kick=True) # get rid of spammer
00065         return
00066 
00067     advancedsearch = isAdvancedSearch(request)
00068 
00069     form = request.values
00070 
00071     # context is relevant only for full search
00072     if titlesearch:
00073         context = 0
00074     elif advancedsearch:
00075         context = 180 # XXX: hardcoded context count for advancedsearch
00076     else:
00077         context = int(form.get('context', 0))
00078 
00079     # Get other form parameters
00080     needle = form.get(fieldname, '')
00081     case = int(form.get('case', 0))
00082     regex = int(form.get('regex', 0)) # no interface currently
00083     hitsFrom = int(form.get('from', 0))
00084     mtime = None
00085     msg = ''
00086     historysearch = 0
00087 
00088     # if advanced search is enabled we construct our own search query
00089     if advancedsearch:
00090         and_terms = form.get('and_terms', '').strip()
00091         or_terms = form.get('or_terms', '').strip()
00092         not_terms = form.get('not_terms', '').strip()
00093         #xor_terms = form.get('xor_terms', '').strip()
00094         categories = form.getlist('categories') or ['']
00095         timeframe = form.get('time', '').strip()
00096         language = form.getlist('language') or ['']
00097         mimetype = form.getlist('mimetype') or [0]
00098         excludeunderlay = form.get('excludeunderlay', 0)
00099         nosystemitems = form.get('nosystemitems', 0)
00100         historysearch = form.get('historysearch', 0)
00101 
00102         mtime = form.get('mtime', '')
00103         if mtime:
00104             mtime_parsed = None
00105 
00106             # get mtime from known date/time formats
00107             for fmt in (request.user.datetime_fmt,
00108                     request.cfg.datetime_fmt, request.user.date_fmt,
00109                     request.cfg.date_fmt):
00110                 try:
00111                     mtime_parsed = time.strptime(mtime, fmt)
00112                 except ValueError:
00113                     continue
00114                 else:
00115                     break
00116 
00117             if mtime_parsed:
00118                 mtime = time.mktime(mtime_parsed)
00119             else:
00120                 # didn't work, let's try parsedatetime
00121                 cal = Calendar()
00122                 mtime_parsed, parsed_what = cal.parse(mtime)
00123                 # XXX it is unclear if usage of localtime here and in parsedatetime module is correct.
00124                 # time.localtime is the SERVER's local time and of no relevance to the user (being
00125                 # somewhere in the world)
00126                 # mktime is reverse function for localtime, so this maybe fixes it again!?
00127                 if parsed_what > 0 and mtime_parsed <= time.localtime():
00128                     mtime = time.mktime(mtime_parsed)
00129                 else:
00130                     mtime_parsed = None # we don't use invalid stuff
00131 
00132             # show info
00133             if mtime_parsed:
00134                 # XXX mtime_msg is not shown in some cases
00135                 mtime_msg = _("(!) Only pages changed since '''%s''' are being displayed!",
00136                               wiki=True) % request.user.getFormattedDateTime(mtime)
00137             else:
00138                 mtime_msg = _('/!\\ The modification date you entered was not '
00139                         'recognized and is therefore not considered for the '
00140                         'search results!', wiki=True)
00141         else:
00142             mtime_msg = None
00143 
00144         word_re = re.compile(r'(\"[\w\s]+"|\w+)')
00145         needle = ''
00146         if categories[0]:
00147             needle += 'category:%s ' % ','.join(categories)
00148         if language[0]:
00149             needle += 'language:%s ' % ','.join(language)
00150         if mimetype[0]:
00151             needle += 'mimetype:%s ' % ','.join(mimetype)
00152         if excludeunderlay:
00153             needle += '-domain:underlay '
00154         if nosystemitems:
00155             needle += '-domain:system '
00156         if and_terms:
00157             needle += '(%s) ' % and_terms
00158         if not_terms:
00159             needle += '(%s) ' % ' '.join(['-%s' % t for t in word_re.findall(not_terms)])
00160         if or_terms:
00161             needle += '(%s) ' % ' or '.join(word_re.findall(or_terms))
00162 
00163     # check for sensible search term
00164     stripped = needle.strip()
00165     if len(stripped) == 0:
00166         request.theme.add_msg(_('Please use a more selective search term instead '
00167                 'of {{{"%s"}}}', wiki=True) % wikiutil.escape(needle), "error")
00168         Page(request, pagename).send_page()
00169         return
00170     needle = stripped
00171 
00172     # Setup for type of search
00173     if titlesearch:
00174         title = _('Title Search: "%s"')
00175         sort = 'page_name'
00176     else:
00177         if advancedsearch:
00178             title = _('Advanced Search: "%s"')
00179         else:
00180             title = _('Full Text Search: "%s"')
00181         sort = 'weight'
00182 
00183     # search the pages
00184     from MoinMoin.search import searchPages, QueryParser, QueryError
00185     try:
00186         query = QueryParser(case=case, regex=regex,
00187                 titlesearch=titlesearch).parse_query(needle)
00188     except QueryError: # catch errors in the search query
00189         request.theme.add_msg(_('Your search query {{{"%s"}}} is invalid. Please refer to '
00190                 'HelpOnSearching for more information.', wiki=True, percent=True) % wikiutil.escape(needle), "error")
00191         Page(request, pagename).send_page()
00192         return
00193 
00194     results = searchPages(request, query, sort, mtime, historysearch)
00195 
00196     # directly show a single hit for title searches
00197     # this is the "quick jump" functionality if you don't remember
00198     # the pagename exactly, but just some parts of it
00199     if titlesearch and len(results.hits) == 1:
00200         page = results.hits[0]
00201         if not page.attachment: # we did not find an attachment
00202             page = Page(request, page.page_name)
00203             highlight = query.highlight_re()
00204             if highlight:
00205                 querydict = {'highlight': highlight}
00206             else:
00207                 querydict = {}
00208             url = page.url(request, querystr=querydict)
00209             request.http_redirect(url)
00210             return
00211     if not results.hits: # no hits?
00212         f = request.formatter
00213         querydict = dict(wikiutil.parseQueryString(request.query_string))
00214         querydict.update({'titlesearch': 0})
00215 
00216         request.theme.add_msg(_('Your search query {{{"%s"}}} didn\'t return any results. '
00217                 'Please change some terms and refer to HelpOnSearching for '
00218                 'more information.%s', wiki=True, percent=True) % (wikiutil.escape(needle),
00219                     titlesearch and ''.join([
00220                         '<br>',
00221                         _('(!) Consider performing a', wiki=True), ' ',
00222                         f.url(1, href=request.page.url(request, querydict, escape=0)),
00223                         _('full-text search with your search terms'),
00224                         f.url(0), '.',
00225                     ]) or ''), "error")
00226         Page(request, pagename).send_page()
00227         return
00228 
00229     # This action generates data using the user language
00230     request.setContentLanguage(request.lang)
00231 
00232     request.theme.send_title(title % needle, pagename=pagename)
00233 
00234     # Start content (important for RTL support)
00235     request.write(request.formatter.startContent("content"))
00236 
00237     # Hints
00238     f = request.formatter
00239     hints = []
00240 
00241     if titlesearch:
00242         querydict = dict(wikiutil.parseQueryString(request.query_string))
00243         querydict.update({'titlesearch': 0})
00244 
00245         hints.append(''.join([
00246             _("(!) You're performing a title search that might not include"
00247                 ' all related results of your search query in this wiki. <<BR>>', wiki=True),
00248             ' ',
00249             f.url(1, href=request.page.url(request, querydict, escape=0)),
00250             f.text(_('Click here to perform a full-text search with your '
00251                 'search terms!')),
00252             f.url(0),
00253         ]))
00254 
00255     if advancedsearch and mtime_msg:
00256         hints.append(mtime_msg)
00257 
00258     if hints:
00259         request.write(searchHints(f, hints))
00260 
00261     # Search stats
00262     request.write(results.stats(request, request.formatter, hitsFrom))
00263 
00264     # Then search results
00265     info = not titlesearch
00266     if context:
00267         output = results.pageListWithContext(request, request.formatter,
00268                 info=info, context=context, hitsFrom=hitsFrom, hitsInfo=1)
00269     else:
00270         output = results.pageList(request, request.formatter, info=info,
00271                 hitsFrom=hitsFrom, hitsInfo=1)
00272 
00273     request.write(output)
00274 
00275     request.write(request.formatter.endContent())
00276     request.theme.send_footer(pagename)
00277     request.theme.send_closing_html()
00278