## -*- coding: utf-8 -*- ## ## CDDL HEADER START ## ## The contents of this file are subject to the terms of the ## Common Development and Distribution License (the "License"). ## You may not use this file except in compliance with the License. ## ## You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE ## or http://www.opensolaris.org/os/licensing. ## See the License for the specific language governing permissions ## and limitations under the License. ## ## When distributing Covered Code, include this CDDL HEADER in each ## file and include the License file at usr/src/OPENSOLARIS.LICENSE. ## If applicable, add the following below this CDDL HEADER, with the ## fields enclosed by brackets "[]" replaced with your own identifying ## information: Portions Copyright [yyyy] [name of copyright owner] ## ## CDDL HEADER END ## ## Copyright 2009 Sun Microsystems, Inc. All rights reserved. ## Use is subject to license terms. ## <%! import itertools import pkg.actions as actions import pkg.query_parser as qp import pkg.server.api_errors as api_errors import pkg.version as version import urllib import urlparse %>\ <%inherit file="layout.shtml"/>\ <%page args="g_vars"/>\ <% catalog = g_vars["catalog"] request = g_vars["request"] %>\ <%def name="page_title(g_vars)"><% return "Package Search" %>\ <%def name="get_search_criteria(request)"><% # Based on the request parameters, return a dict representing the # search criteria. criteria = { "token": request.params.get("token", ""), "query_error": request.params.get("qe", ""), } criteria["searched"] = len(criteria["token"]) show = request.params.get("show", "p") if show == "p" or show not in ("a", "p"): criteria["return_type"] = qp.Query.RETURN_PACKAGES elif show == "a": criteria["return_type"] = qp.Query.RETURN_ACTIONS for name, default in (("rpp", 50), ("start", 0), ("failed", 0), ("sav", 0), ("cs", 0)): val = request.params.get(name, default) try: val = int(val) except ValueError: val = default criteria[name] = val return criteria %>\ <%def name="search(catalog, request, criteria)"><% # Gets the search results for the specified catalog based on the # provided search criteria. query_error = None token = criteria["token"] cs = criteria["cs"] rpp = criteria["rpp"] return_type = criteria["return_type"] start_val = criteria["start"] # This criteria is optional, so use get to retrieve it. mver = criteria.get("selected_ver", None) if mver: # Replace leading version components with wildcard so that # matching is only performed using build_release and branch. mver = "*,%s-%s" % (mver.build_release, mver.branch) # Determines whether all versions or only the latest vresion of a # package is shown. This is ignored when the return_type is not # qp.Query.RETURN_PACKAGES. sav = criteria["sav"] try: # Search results are limited to just one more than the # results per page so that a query that exceeds it can be # detected. results = catalog.search(token, case_sensitive=cs, return_type=return_type, start_point=start_val, num_to_return=(rpp + 1), matching_version=mver, return_latest=not sav) except qp.QueryException, e: results = None query_error = e.html() # Before showing the results, the type of results being shown has to be # determined since the user might have overriden the return_type # selection above using query syntax. To do that, the first result will # have to be checked for the real return type. if results: try: result = results.next() except StopIteration: result = None results = None pass if result and result[1] == qp.Query.RETURN_PACKAGES: return_type = result[1] results = itertools.chain([result], results) elif result and result[1] == qp.Query.RETURN_ACTIONS: return_type = result[1] results = itertools.chain([result], results) elif result: return_type = qp.Query.RETURN_PACKAGES query_error = "Only the display of packages or " \ "actions for search results is supported." request.log("Unsupported return_type '%s' " "requested for search query: '%s'." % (return_type, token)) return return_type, results, query_error %>\ <%def name="display_search_form(criteria, request)"><% # Returns an HTML form with all of the elements needed to perform a # search using the specified search criteria and request. token = criteria["token"] search_uri = "advanced_search.shtml" if criteria["searched"]: search_uri = request.url(qs=request.query_string, relative=True) search_uri = search_uri.replace("search.shtml", "advanced_search.shtml") %>\

Advanced Search

\ <%def name="get_prev_page_uri(criteria, request)"><% # Returns a URL relative to the current request path with the # starting range of the previous page of search results set. uri = request.url(qs=request.query_string, relative=True) scheme, netloc, path, params, query, fragment = urlparse.urlparse(uri) nparams = [] for name, val in request.params.iteritems(): if name == "start": continue nparams.append((name, val)) start = criteria["start"] start = start - criteria["rpp"] if start < 0: start = 0 nparams.append(("start", start)) qs = urllib.urlencode(nparams) uri = urlparse.urlunparse((scheme, netloc, path, params, qs, fragment)) return uri %>\ <%def name="get_next_page_uri(criteria, request, result_count)"><% # Returns a URL relative to the current request path with the # starting range of the next page of search results set. uri = request.url(qs=request.query_string, relative=True) scheme, netloc, path, params, query, fragment = urlparse.urlparse(uri) nparams = [] for name, val in request.params.iteritems(): if name == "start": continue nparams.append((name, val)) start = criteria["start"] nparams.append(("start", (start + result_count - 1))) qs = urllib.urlencode(nparams) uri = urlparse.urlunparse((scheme, netloc, path, params, qs, fragment)) return uri %>\ <%def name="display_pagination(criteria, result_count, colspan=1)"><% # Returns a table row with the appropriate pagination controls # based on the provided search criteria. show_prev = criteria["start"] > 0 show_next = result_count > criteria["rpp"] if not (show_prev or show_next): # Nothing to display. return "" %>\
% if show_prev: Previous % else: % endif % if show_next: Next % else: % endif
\
% if not catalog.search_available:

Search functionality is not available at this time.

% else: <% results = None return_type = None criteria = self.get_search_criteria(request) searched = criteria["searched"] failed = criteria["failed"] query_error = criteria["query_error"] if not failed and searched: return_type, results, query_error = self.search( catalog, request, criteria) if query_error or not results: # Reload the page with a few extra query parameters set # so that failed searches can be detected in the server # logs (including that of any proxies in front of the # depot server). uri = request.url(qs=request.query_string, relative=True) scheme, netloc, path, params, query, \ fragment = urlparse.urlparse(uri) nparams = [] for name, val in request.params.iteritems(): if name in ("failed", "query_error"): continue nparams.append((name, val)) nparams.append(("failed", 1)) if query_error: nparams.append(("qe", query_error)) qs = urllib.urlencode(nparams) uri = urlparse.urlunparse((scheme, netloc, path, params, qs, fragment)) raise api_errors.RedirectException(uri) rpp = criteria["rpp"] result_count = 0 %>\
${self.display_search_form(criteria, request)} % if not searched:

Search Tips:

- All searches are case-insensitive.
- To find packages that contain a specific file, start your search criteria with a '/':
  `/usr/bin/vim`
- To find packages based on a partial match, use the wildcard characters '*' or '?':
  `*.xhtm?`
- To find packages based on specific matching characters use '[' and ']':
  `/usr/bin/[ca]t`

% endif
% if searched and return_type == qp.Query.RETURN_PACKAGES: ## Showing packages.

Package | Install | Manifest
--- | --- | ---
% 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, ""))) %>\ | ${pfmri} | Install | Manifest % endfor ${display_pagination(criteria, result_count, colspan=2)}

% elif searched and return_type == qp.Query.RETURN_ACTIONS:

Index | Action | Value | Package
--- | --- | --- | ---
% 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, ""))) %>\ | ${index | h} | ${action | h} | ${value | h} | ${pfmri} % endfor ${display_pagination(criteria, result_count, colspan=2)}

% elif query_error: <% token = criteria["token"] %>

Your search - ${token | h} - is not a valid query.

Suggestions:

- Remove special characters ('(', ')', '<', '>') from your tokens. Replace them with '*' or '?'.
- Ensure that each branch of your boolean query is returning the same kind of information. 'foo AND < bar >' will not work.

Details:

${query_error} % elif failed: <% token = criteria["token"] %>

Your search - ${token | h} - did not match any packages.

Suggestions:

- Ensure that all words are spelled correctly.
- Try different keywords.
- Try more general keywords.

% endif
% endif