search.shtml revision 1431
1N/A## -*- coding: utf-8 -*-
1N/A##
1N/A## CDDL HEADER START
1N/A##
1N/A## The contents of this file are subject to the terms of the
1N/A## Common Development and Distribution License (the "License").
1N/A## You may not use this file except in compliance with the License.
1N/A##
1N/A## You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
1N/A## or http://www.opensolaris.org/os/licensing.
1N/A## See the License for the specific language governing permissions
1N/A## and limitations under the License.
1N/A##
1N/A## When distributing Covered Code, include this CDDL HEADER in each
1N/A## file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1N/A## If applicable, add the following below this CDDL HEADER, with the
1N/A## fields enclosed by brackets "[]" replaced with your own identifying
1N/A## information: Portions Copyright [yyyy] [name of copyright owner]
1N/A##
1N/A## CDDL HEADER END
1N/A##
1N/A## Copyright 2009 Sun Microsystems, Inc. All rights reserved.
1N/A## Use is subject to license terms.
1N/A##
1N/A<%!
1N/A import itertools
1N/A import pkg.actions as actions
1N/A import pkg.query_parser as qp
1N/A import pkg.server.api_errors as api_errors
1N/A import pkg.version as version
1N/A import urllib
1N/A import urlparse
1N/A%>\
1N/A<%inherit file="layout.shtml"/>\
1N/A<%page args="g_vars"/>\
1N/A<%
1N/A catalog = g_vars["catalog"]
1N/A request = g_vars["request"]
1N/A%>\
1N/A<%def name="page_title(g_vars)"><%
1N/A return "Package Search"
1N/A%></%def>\
1N/A<%def name="get_search_criteria(request)"><%
1N/A # Based on the request parameters, return a dict representing the
1N/A # search criteria.
1N/A criteria = {
1N/A "token": request.params.get("token", ""),
1N/A "query_error": request.params.get("qe", ""),
1N/A }
1N/A
1N/A criteria["searched"] = len(criteria["token"])
1N/A
1N/A show = request.params.get("show", "p")
1N/A if show == "p" or show not in ("a", "p"):
1N/A criteria["return_type"] = qp.Query.RETURN_PACKAGES
1N/A elif show == "a":
1N/A criteria["return_type"] = qp.Query.RETURN_ACTIONS
1N/A
1N/A for name, default in (("rpp", 50), ("start", 0), ("failed", 0),
1N/A ("sav", 0), ("cs", 0)):
1N/A val = request.params.get(name, default)
1N/A try:
1N/A val = int(val)
1N/A except ValueError:
1N/A val = default
1N/A
1N/A # Force boolean type for these parameters.
1N/A if name in ("cs", "sav"):
1N/A if val:
1N/A val = True
1N/A else:
1N/A val = False
1N/A
1N/A criteria[name] = val
1N/A
1N/A return criteria
1N/A%></%def>\
1N/A<%def name="search(catalog, request, criteria)"><%
1N/A # Gets the search results for the specified catalog based on the
1N/A # provided search criteria.
1N/A query_error = None
1N/A token = criteria["token"]
1N/A cs = criteria["cs"]
1N/A rpp = criteria["rpp"]
1N/A return_type = criteria["return_type"]
1N/A start_val = criteria["start"]
1N/A
1N/A # This criteria is optional, so use get to retrieve it.
1N/A mver = criteria.get("selected_ver", None)
1N/A
1N/A if mver:
1N/A # Replace leading version components with wildcard so that
1N/A # matching is only performed using build_release and branch.
1N/A mver = "*,%s-%s" % (mver.build_release, mver.branch)
1N/A
1N/A # Determines whether all versions or only the latest vresion of a
1N/A # package is shown. This is ignored when the return_type is not
1N/A # qp.Query.RETURN_PACKAGES.
1N/A sav = criteria["sav"]
1N/A
1N/A try:
1N/A # Search results are limited to just one more than the
1N/A # results per page so that a query that exceeds it can be
1N/A # detected.
1N/A results = catalog.search(token,
1N/A case_sensitive=cs,
1N/A return_type=return_type,
1N/A start_point=start_val,
1N/A num_to_return=(rpp + 1),
1N/A matching_version=mver,
1N/A return_latest=not sav)
1N/A except qp.QueryException, e:
1N/A results = None
1N/A query_error = e.html()
1N/A
1N/A # Before showing the results, the type of results being shown has to be
1N/A # determined since the user might have overriden the return_type
1N/A # selection above using query syntax. To do that, the first result will
1N/A # have to be checked for the real return type.
1N/A if results:
1N/A try:
1N/A result = results.next()
1N/A except StopIteration:
1N/A result = None
1N/A results = None
1N/A pass
1N/A
1N/A if result and result[1] == qp.Query.RETURN_PACKAGES:
1N/A return_type = result[1]
1N/A results = itertools.chain([result], results)
1N/A elif result and result[1] == qp.Query.RETURN_ACTIONS:
1N/A return_type = result[1]
1N/A results = itertools.chain([result], results)
1N/A elif result:
1N/A return_type = qp.Query.RETURN_PACKAGES
1N/A query_error = "Only the display of packages or " \
1N/A "actions for search results is supported."
1N/A
1N/A request.log("Unsupported return_type '%s' "
1N/A "requested for search query: '%s'." % (return_type,
1N/A token))
1N/A
1N/A return return_type, results, query_error
1N/A%></%def>\
1N/A<%def name="display_search_form(criteria, request)"><%
1N/A # Returns an HTML form with all of the elements needed to perform a
1N/A # search using the specified search criteria and request.
1N/A token = criteria["token"]
1N/A
1N/A search_uri = "advanced_search.shtml"
1N/A if criteria["searched"]:
1N/A search_uri = request.url(qs=request.query_string, relative=True)
1N/A search_uri = search_uri.replace("search.shtml",
1N/A "advanced_search.shtml")
1N/A%>\
1N/A<form class="search" action="search.shtml">
1N/A <p>
1N/A <input id="search-field" type="text" size="40"
1N/A maxlength="512" name="token"
1N/A value="${token | h}"/>
1N/A <input id="submit-search" type="submit"
1N/A name="action" value="Search"/>
1N/A <a href="${search_uri | h}">Advanced Search</a>
1N/A </p>
1N/A</form>
1N/A</%def>\
1N/A<%def name="get_prev_page_uri(criteria, request)"><%
1N/A # Returns a URL relative to the current request path with the
1N/A # starting range of the previous page of search results set.
1N/A
1N/A uri = request.url(qs=request.query_string, relative=True)
1N/A scheme, netloc, path, params, query, fragment = urlparse.urlparse(uri)
1N/A
1N/A nparams = []
1N/A for name, val in request.params.iteritems():
1N/A if name == "start":
1N/A continue
1N/A nparams.append((name, val))
1N/A
1N/A start = criteria["start"]
1N/A start = start - criteria["rpp"]
1N/A if start < 0:
1N/A start = 0
1N/A nparams.append(("start", start))
1N/A
1N/A qs = urllib.urlencode(nparams)
1N/A uri = urlparse.urlunparse((scheme, netloc, path, params, qs, fragment))
1N/A
1N/A return uri
1N/A%></%def>\
1N/A<%def name="get_next_page_uri(criteria, request, result_count)"><%
1N/A # Returns a URL relative to the current request path with the
1N/A # starting range of the next page of search results set.
1N/A
1N/A uri = request.url(qs=request.query_string, relative=True)
1N/A scheme, netloc, path, params, query, fragment = urlparse.urlparse(uri)
1N/A
1N/A nparams = []
1N/A for name, val in request.params.iteritems():
1N/A if name == "start":
1N/A continue
1N/A nparams.append((name, val))
1N/A
1N/A start = criteria["start"]
1N/A nparams.append(("start", (start + result_count - 1)))
1N/A
1N/A qs = urllib.urlencode(nparams)
1N/A uri = urlparse.urlunparse((scheme, netloc, path, params, qs, fragment))
1N/A
1N/A return uri
1N/A%></%def>\
1N/A<%def name="display_pagination(criteria, result_count, colspan=1)"><%
1N/A # Returns a table row with the appropriate pagination controls
1N/A # based on the provided search criteria.
1N/A
1N/A show_prev = criteria["start"] > 0
1N/A show_next = result_count > criteria["rpp"]
1N/A if not (show_prev or show_next):
1N/A # Nothing to display.
1N/A return ""
1N/A%>\
1N/A<tr class="last">
1N/A% if show_prev:
1N/A <td colspan="${colspan}">
1N/A <a href="${self.get_prev_page_uri(criteria, request) | h}">
1N/APrevious</a>
1N/A </td>
1N/A% else:
1N/A <td colspan="${colspan}">&nbsp;</td>
1N/A% endif
1N/A% if show_next:
1N/A <td class="last" colspan="${colspan}">
1N/A <a href="${self.get_next_page_uri(criteria, request,
1N/A result_count) | h}">
1N/ANext</a>
1N/A </td>
1N/A% else:
1N/A <td class="last" colspan="${colspan}">&nbsp;</td>
1N/A% endif
1N/A</tr>
1N/A</%def>\
1N/A<div id="yui-main">
1N/A% if not catalog.search_available:
1N/A <div class="yui-b">
1N/A <p>Search functionality is not available at this time.</p>
1N/A </div>
1N/A% else:
1N/A<%
1N/A results = None
1N/A return_type = None
1N/A
1N/A criteria = self.get_search_criteria(request)
1N/A searched = criteria["searched"]
1N/A failed = criteria["failed"]
1N/A query_error = criteria["query_error"]
1N/A
1N/A if not failed and searched:
1N/A return_type, results, query_error = self.search(
1N/A catalog, request, criteria)
1N/A
1N/A if query_error or not results:
1N/A # Reload the page with a few extra query parameters set
1N/A # so that failed searches can be detected in the server
1N/A # logs (including that of any proxies in front of the
1N/A # depot server).
1N/A uri = request.url(qs=request.query_string,
1N/A relative=True)
1N/A scheme, netloc, path, params, query, \
1N/A fragment = urlparse.urlparse(uri)
1N/A
1N/A nparams = []
1N/A for name, val in request.params.iteritems():
1N/A if name in ("failed", "query_error"):
1N/A continue
1N/A nparams.append((name, val))
1N/A
1N/A nparams.append(("failed", 1))
1N/A if query_error:
1N/A nparams.append(("qe", query_error))
1N/A
1N/A qs = urllib.urlencode(nparams)
1N/A uri = urlparse.urlunparse((scheme, netloc, path, params,
qs, fragment))
raise api_errors.RedirectException(uri)
rpp = criteria["rpp"]
result_count = 0
%>\
<div class="yui-b">
${self.display_search_form(criteria, request)}
% if not searched:
<p>Search Tips:</p>
<ul class="tips">
<li>All searches are case-insensitive.</li>
<li>To find packages that contain a specific
file, start your search criteria with a '/':<br/>
<kbd>/usr/bin/vim</kbd></li>
<li>To find packages based on a partial match,
use the wildcard characters '*' or '?':<br/>
<kbd>*.xhtm?</kbd></li>
<li>To find packages based on specific
matching characters use '[' and ']':<br/>
<kbd>/usr/bin/[ca]t</kbd></li>
</ul>
% endif
</div>
<div class="yui-b results">
% if searched and return_type == qp.Query.RETURN_PACKAGES:
## Showing packages.
<table summary="A list of packages from the repository catalog
that matched the specified search criteria.">
<tr class="first">
<th>Package</th>
<th>Install</th>
<th colspan="2">Manifest</th>
</tr>
% for v, return_type, vals in results:
<%
pfmri = vals
if result_count % 2:
rclass = ' class="odd"'
else:
rclass = ""
result_count += 1
if result_count > rpp:
break
stem = pfmri.split("@", 1)[0]
phref = self.shared.rpath(g_vars, "info/0/%s" % (
urllib.quote(pfmri, "")))
# XXX the .p5i extension is a bogus hack because
# packagemanager requires it and shouldn't.
p5ihref = self.shared.rpath(g_vars, "p5i/0/%s.p5i" % (
urllib.quote(stem, "")))
mhref = self.shared.rpath(g_vars, "manifest/0/%s" % (
urllib.quote(pfmri, "")))
%>\
<tr${rclass}>
<td>
<a title="Package Information Summary"
href="${phref}">${pfmri}</a>
</td>
<td>
<a class="p5i"
title="Launch the Package Manager and install this package"
href="${p5ihref}">Install</a>
</td>
<td colspan="2">
<a title="Package Manifest"
href="${mhref}">Manifest</a>
</td>
</tr>
% endfor
${display_pagination(criteria, result_count, colspan=2)}
</table>
% elif searched and return_type == qp.Query.RETURN_ACTIONS:
<table summary="A list of actions from the repository that
matched the specified search criteria.">
<tr class="first">
<th>Index</th>
<th>Action</th>
<th>Value</th>
<th>Package</th>
</tr>
% for v, return_type, vals in results:
<%
pfmri, match, action = vals
a = actions.fromstr(action.rstrip())
action = a.name
if isinstance(a, actions.attribute.AttributeAction):
index = a.attrs.get(a.key_attr)
value = match
else:
index = match
value = a.attrs.get(a.key_attr)
%>
<%
if result_count % 2:
rclass = ' class="odd"'
else:
rclass = ""
result_count += 1
if result_count > rpp:
break
phref = self.shared.rpath(g_vars, "info/0/%s" % (
urllib.quote(pfmri, "")))
%>\
<tr${rclass}>
<td>${index | h}</td>
<td>${action | h}</td>
<td>${value | h}</td>
<td><a href="${phref}">${pfmri}</a></td>
</tr>
% endfor
${display_pagination(criteria, result_count, colspan=2)}
</table>
% elif query_error:
<%
token = criteria["token"]
%>
<p>Your search - <b>${token | h}</b> - is not a valid query.</p>
<p>Suggestions:</p>
<ul>
<li>Remove special characters ('(', ')', '&lt;', '&gt;')
from your tokens. Replace them with '*' or '?'.</li>
<li>Ensure that each branch of your boolean query is
returning the same kind of information. 'foo AND &lt; bar &gt;' will not work.</li>
</ul>
<p>Details:</p>
${query_error}
% elif failed:
<%
token = criteria["token"]
%>
<p>Your search - <b>${token | h}</b> - did not match any
packages.</p>
<p>Suggestions:</p>
<ul>
<li>Ensure that all words are spelled correctly.</li>
<li>Try different keywords.</li>
<li>Try more general keywords.</li>
</ul>
% endif
</div>
% endif
</div>