Implementing the lower-case search into the website

A few days ago, I did the database work to ignore case when browsing to a cat/port. Tonight, I did the website side.

database access

First, I altered the database function (in classes/element_record.php) to use the new stored procedure:

-       function FetchByName($Name) {
+       function FetchByName($Name, $caseSensitive = true) {
+               $Debug = 0;
+
+               if ($Debug) echo "looking for '$Name' and caseSensitive is '$caseSensitive'<br>";
                if (IsSet($Name)) {
                        $this->element_pathname = $Name;
                        $this->id = '';
                }
-               $sql = "select * from elementGet('" . pg_escape_string($Name) . "')";
 
-               $result = pg_exec($this->dbh, $sql);
+               if ($caseSensitive) {
+                       $sql = "select * from elementGet('" . pg_escape_string($Name) . "')";
+                       if ($Debug) echo "invoking $sql<br>";
+                       $result = pg_exec($this->dbh, $sql);
+               } else {
+                       $result = pg_query_params($this->dbh, 'select * from elementGetCaseInsensitive($1)', array($Name));
+               }
+
                if ($result) {
+                       if ($Debug) echo "we got a result<br>";
+
                        $numrows = pg_numrows($result);
+                       if ($Debug) echo "we have '$numrows' rows<br>";
                        if ($numrows == 1) {
                                $myrow = pg_fetch_array ($result, 0);
                                $this->PopulateValues($myrow);

On line one, I modify the function to pass in an optional parameter which preserves the original behavior.

Line 10 is the original code.

Lines 13-20 invokes the elementGetCaseInsensitive() stored procedure to work on a case insensitive solution.

Redirecting when finding a different match

When getting a result, we check to see if the path differs. If we get a result, we know it matches, but we need to know if it’s a different case. Here is that code (from rewrite/functions.php):

-       if ($ElementRecord->FetchByName('/ports/head/' . $pathname)) {
+       if ($ElementRecord->FetchByName(FRESHPORTS_PORTS_TREE_PREFIX . PATH_NAME, 0)) {
                $IsElement = true;
                if ($Debug) echo 'we found an element for that<br>';
+               if ($Debug) echo "we have: '$ElementRecord->element_pathname'<br>";
+               if ($Debug) echo " we had: '" . FRESHPORTS_PORTS_TREE_PREFIX . PATH_NAME . "'<br>";
+               if (PathnameDiffers($ElementRecord->element_pathname . '/', FRESHPORTS_PORTS_TREE_PREFIX . PATH_NAME)) {
+                       # in a case insensitive search, we want to redirect if the case was wrong
+                       if ($Debug) echo "we are redirecting to '" . $ElementRecord->element_pathname . "/'<br>";
+                       if ($Debug) echo 'which normalizes to ' . str_replace(FRESHPORTS_PORTS_TREE_PREFIX, '/', $ElementRecord->element_pathname . '/<br>');
+                       $https = ((!empty($_SERVER['HTTPS'])) && ($_SERVER['HTTPS'] != 'off'));
+                       if ($https) {
+                               $protocol = "https";
+                       } else {
+                               $protocol = "http";
+                       }
+
+                       header("HTTP/1.1 301 Moved Permanently");
+                       header('Location: ' . $protocol . '://' . $_SERVER['HTTP_HOST'] . str_replace(FRESHPORTS_PORTS_TREE_PREFIX, '/', $ElementRecord->element_pathname . '/'));
+                       exit;
+               }
+       }
+

Line 7 invokes PathDiffers to do a comparison between the two strings. This is not a simple A != B operation. sysutils/bacula9-server is not the same as sysutils/bacula9-server/ despite them showing the same content on the website. The paths, or URIs, must first be ‘normalized’ (that’s my chosen terminology, not anything official).

If the user has included a trailing slash (/) on their URL, the code will strip that before looking for a matching entry in the element table.

The debugging information shows some of this:

the URI is
'/sysutils/bacula9-SERVER/'

the url parts are
array(1) {
  ["path"]=>
  string(25) "/sysutils/bacula9-SERVER/"
}

The pathname is '/sysutils/bacula9-SERVER/'
we found an element for that
we have: '/ports/head/sysutils/bacula9-server'
we had: '/ports/head/sysutils/bacula9-SERVER'
we are redirecting to '/ports/head/sysutils/bacula9-server/'
which normalizes to /sysutils/bacula9-server/

The function in question looks like this:

function PathnameDiffers($Path1, $Path2) {
    # if the two paths are different, we might want to redirect
    # if one path ends in a / and the other does not, adjust them
    if (substr($Path1, -1) == '/') {
        if (substr($Path2, -1) != '/') {
            $Path2 .= '/';
        }
    } else {
        if (substr($Path2, -1) == '/') {
            $Path1 .= '/';
        }
    }

    return $Path1 != $Path2;
}

That redirect happens early enough in the code to handle all elements, not just ports (e.g. cat/ports/Makefile or distinfo or pkg-descr).

Website Pin Facebook Twitter Myspace Friendfeed Technorati del.icio.us Digg Google StumbleUpon Premium Responsive

Leave a Comment

Scroll to Top