function openSearchGenerateBaseTags($openSearchOperation) { if ($openSearchOperation == "Error") { // OpenSearch Atom XML is used for diagnostics $atomCollection = atomGenerateBaseTags($openSearchOperation); } elseif ($openSearchOperation == "Description") { $atomCollection = new XML("OpenSearchDescription"); $atomCollection->setTagAttribute("xmlns", "http://a9.com/-/spec/opensearch/1.1/"); $atomCollection->setTagAttribute("xmlns:opensearch", "http://a9.com/-/spec/opensearch/1.1/"); $atomCollection->setTagAttribute("xmlns:mozilla", "http://www.mozilla.org/2006/browser/search/"); } return $atomCollection; }
function atomCollection($result, $rowOffset, $showRows, $exportStylesheet, $displayType) { global $databaseBaseURL; // these variables are defined in 'ini.inc.php' global $contentTypeCharset; global $convertExportDataToUTF8; global $citeStyle; global $citeOrder; global $query; // Individual records are objects and collections of records are strings $atomCollectionDoc = new XMLDocument(); if ($convertExportDataToUTF8 == "yes" and $contentTypeCharset != "UTF-8") { $atomCollectionDoc->setEncoding("UTF-8"); } else { $atomCollectionDoc->setEncoding($contentTypeCharset); } // Generate the basic OpenSearch Atom XML tree required for a query response: $atomCollection = atomGenerateBaseTags("Results"); $showRowsOriginal = $showRows; // save original value of '$showRows' (which may get modified by the 'seekInMySQLResultsToOffset()' function below) // Find out how many rows are available and (if there were rows found) seek to the current offset: // function 'seekInMySQLResultsToOffset()' is defined in 'include.inc.php' list($result, $rowOffset, $showRows, $rowsFound, $previousOffset, $nextOffset, $showMaxRow) = seekInMySQLResultsToOffset($result, $rowOffset, $showRows, $displayType, ""); // Setup some required variables: if ($rowsFound != 0 and $showRowsOriginal != 0) { $startIndex = $rowOffset + 1; // for OpenSearch, the index of the first search result is 1 while the first row number in a MySQL result set is 0, so we have to add 1 if ($showMaxRow < $rowsFound) { // if we are not on the last results page $itemsPerPage = $showRows; } else { // last results page $itemsPerPage = $rowsFound - $rowOffset; } // adopt value for '$itemsPerPage' so that it equals the number of records displayed on the last page (which may be less than '$showRows') if ($rowsFound > $showRows) { // Calculate the maximum number of pages needed: $lastPage = $rowsFound / $showRows; // workaround for always rounding upward (since I don't know better! :-/): if (preg_match("/[0-9]+\\.[0-9+]/", $lastPage)) { // if the result number is not an integer $lastPage = (int) $lastPage + 1; } // we convert the number into an integer and add 1 // Calculate the offset of the first record that's displayed on the last results page: // NOTE: Should the last offset take the current '$rowOffset' into account? I.e., take '$rowOffset' and see // how many full chunks of '$showRows' can be stacked on top of it until '$rowsFound' is reached. // The offset of the first of the remaining records then constitutes the '$lastOffset'. $lastOffset = ($lastPage - 1) * $showRows; } else { // there's only one page to be displayed $lastOffset = 0; } } else { $startIndex = 0; // note that "0" will currently cause an empty element to be returned (instead of the number "0"), should this be changed? $itemsPerPage = 0; $lastOffset = 0; } // Extract the 'WHERE' clause from the current SQL query: $queryWhereClause = extractWHEREclause($query); // function 'extractWHEREclause()' is defined in 'include.inc.php' // Setup base URL and its corresponding query parameter for formats // that are supported by both, 'show.php'/'rss.php' AND 'opensearch.php': if (!isset($_SESSION['cqlQuery'])) { // (while 'opensearch.php' writes the user's OpenSearch/CQL query into a session variable, this // does not happen (and is not possible) if Atom XML is exported via the regular refbase GUI) // Generate Atom links using 'show.php' URLs (or 'rss.php' in case of RSS XML): $baseURL = "show.php"; $cqlQuery = ""; $queryParametersArray["where"] = $queryWhereClause; } else { // Generate Atom links using 'opensearch.php' URLs: $baseURL = "opensearch.php"; // Extract the original OpenSearch/CQL query that was saved by 'opensearch.php' as a session variable: $cqlQuery = $_SESSION['cqlQuery']; $queryParametersArray["query"] = $cqlQuery; // Clear the 'cqlQuery' session variable so that subsequent calls of this function won't accidentally use an outdated OpenSearch/CQL query: // Note: Though we clear the session variable, the current message is still available to this script via '$cqlQuery': deleteSessionVariable("cqlQuery"); // function 'deleteSessionVariable()' is defined in 'include.inc.php' } // ---------------------------------------------------------- // Add feed-level tags: // - 'link' elements: // NOTE: According to the Atom spec, a feed is limited to ONE 'rel=alternate' link per type and hreflang! // A) Formats supported by both, 'show.php'/'rss.php' AND 'opensearch.php': // - RSS feed for full query results: atomLink($atomCollection, $databaseBaseURL . generateURL($baseURL, "RSS XML", $queryParametersArray, true, $showRows), "alternate", "RSS XML", "Current query results as RSS feed"); // function 'generateURL()' is defined in 'include.inc.php' // - HTML output for current results page: atomLink($atomCollection, $databaseBaseURL . generateURL($baseURL, "html", $queryParametersArray, true, $showRows, $rowOffset, $citeStyle, $citeOrder), "alternate", "html", "Current results page as HTML"); // - SRW_DC XML data for current results page: // NOTE: A link to SRW_MODS XML is already used with this type! // atomLink($atomCollection, $databaseBaseURL . generateURL($baseURL, "SRW_DC XML", $queryParametersArray, true, $showRows, $rowOffset, $citeStyle, $citeOrder), "alternate", "SRW_DC XML", "Current results page as SRW_DC XML data"); // - SRW_MODS XML data for current results page: atomLink($atomCollection, $databaseBaseURL . generateURL($baseURL, "SRW_MODS XML", $queryParametersArray, true, $showRows, $rowOffset, $citeStyle, $citeOrder), "alternate", "SRW_MODS XML", "Current results page as SRW_MODS XML data"); // - Atom XML data for current results page: atomLink($atomCollection, $databaseBaseURL . generateURL($baseURL, "Atom XML", $queryParametersArray, true, $showRows, $rowOffset, $citeStyle, $citeOrder), "self", "Atom XML", "Current results page"); // - Atom XML data for first results page: atomLink($atomCollection, $databaseBaseURL . generateURL($baseURL, "Atom XML", $queryParametersArray, true, $showRows, 0, $citeStyle, $citeOrder), "first", "Atom XML", "First results page"); // - Atom XML data for previous results page: if ($startIndex > $showRows) { // if there are any previous results pages atomLink($atomCollection, $databaseBaseURL . generateURL($baseURL, "Atom XML", $queryParametersArray, true, $showRows, $previousOffset, $citeStyle, $citeOrder), "previous", "Atom XML", "Previous results page"); } // - Atom XML data for next results page: if ($showMaxRow < $rowsFound) { // if we are not on the last results page atomLink($atomCollection, $databaseBaseURL . generateURL($baseURL, "Atom XML", $queryParametersArray, true, $showRows, $nextOffset, $citeStyle, $citeOrder), "next", "Atom XML", "Next results page"); } // - Atom XML data for last results page: atomLink($atomCollection, $databaseBaseURL . generateURL($baseURL, "Atom XML", $queryParametersArray, true, $showRows, $lastOffset, $citeStyle, $citeOrder), "last", "Atom XML", "Last results page"); // B) Other export formats supported by 'show.php': // NOTE: These export formats currently do not support paging of results via '$showRows' and '$rowOffset' and thus always export the entire result set! // TODO: add links for ADS, ISI and Word XML // - BibTeX data for all results: // atomLink($atomCollection, $databaseBaseURL . generateURL("show.php", "BibTeX", array("where" => $queryWhereClause), true, $showRows, $rowOffset, "", $citeOrder), "alternate", "BibTeX", "All results as BibTeX data"); // - Endnote data for all results: // atomLink($atomCollection, $databaseBaseURL . generateURL("show.php", "Endnote", array("where" => $queryWhereClause), true, $showRows, $rowOffset, "", $citeOrder), "alternate", "Endnote", "All results as Endnote data"); // - RIS data for all results: // atomLink($atomCollection, $databaseBaseURL . generateURL("show.php", "RIS", array("where" => $queryWhereClause), true, $showRows, $rowOffset, "", $citeOrder), "alternate", "RIS", "All results as RIS data"); // - MODS XML data for all results: // NOTE: A link to SRW_MODS XML is already used with this type! // atomLink($atomCollection, $databaseBaseURL . generateURL("show.php", "MODS XML", array("where" => $queryWhereClause), true, $showRows, $rowOffset, "", $citeOrder), "alternate", "MODS XML", "All results as MODS XML data"); // - OAI_DC XML data for all results: // NOTE: A link to SRW_MODS XML is already used with this type! // atomLink($atomCollection, $databaseBaseURL . generateURL("show.php", "OAI_DC XML", array("where" => $queryWhereClause), true, $showRows, $rowOffset, $citeStyle, $citeOrder), "alternate", "OAI_DC XML", "All results as OAI_DC XML data"); // - ODF XML data for all results: // atomLink($atomCollection, $databaseBaseURL . generateURL("show.php", "ODF XML", array("where" => $queryWhereClause, "exportType" => "file"), true, $showRows, $rowOffset, "", $citeOrder), "alternate", "ODF XML", "All results as ODF XML data"); // C) Citation formats supported by 'show.php': // NOTE: Citation formats support paging of results via '$showRows' and '$rowOffset' if the 'client' parameter contains a value that starts with "cli" // - RTF citations for current results page: atomLink($atomCollection, $databaseBaseURL . generateURL("show.php", "RTF", array("where" => $queryWhereClause, "client" => "cli-refbase_atom-1.0"), true, $showRows, $rowOffset, $citeStyle, $citeOrder), "alternate", "RTF", "Current results page as citations in RTF format"); // - PDF citations for current results page: atomLink($atomCollection, $databaseBaseURL . generateURL("show.php", "PDF", array("where" => $queryWhereClause, "client" => "cli-refbase_atom-1.0"), true, $showRows, $rowOffset, $citeStyle, $citeOrder), "alternate", "PDF", "Current results page as citations in PDF format"); // - LaTeX citations for current results page: atomLink($atomCollection, $databaseBaseURL . generateURL("show.php", "LaTeX", array("where" => $queryWhereClause, "client" => "cli-refbase_atom-1.0"), true, $showRows, $rowOffset, $citeStyle, $citeOrder), "alternate", "LaTeX", "Current results page as citations in LaTeX format"); // - LaTeX .bbl citations for current results page: // NOTE: A link to LaTeX citations is already used with this type! // atomLink($atomCollection, $databaseBaseURL . generateURL("show.php", "LaTeX .bbl", array("where" => $queryWhereClause, "client" => "cli-refbase_atom-1.0"), true, $showRows, $rowOffset, $citeStyle, $citeOrder), "alternate", "LaTeX .bbl", "Current results page as citations in LaTeX .bbl format"); // - Markdown citations for current results page: atomLink($atomCollection, $databaseBaseURL . generateURL("show.php", "Markdown", array("where" => $queryWhereClause, "client" => "cli-refbase_atom-1.0"), true, $showRows, $rowOffset, $citeStyle, $citeOrder), "alternate", "Markdown", "Current results page as citations in Markdown format"); // - ASCII citations for current results page: // (NOTE: A link to Markdown citations is already used with this type! // Additionally, ASCII output with 'client=cli...' causes text output to be meant as shell response) // atomLink($atomCollection, $databaseBaseURL . generateURL("show.php", "ASCII", array("where" => $queryWhereClause, "client" => "cli-refbase_atom-1.0"), true, $showRows, $rowOffset, $citeStyle, $citeOrder), "alternate", "ASCII", "Current results page as citations in ASCII format"); // - 'id': // NOTE: is this a valid feed ID? // TODO: should we rather use a feed ID that conforms to the Tag URI (RFC 4151)? Spec: <http://tools.ietf.org/html/rfc4151>; // or should we use an ID such as '<id>urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6</id>' ? addNewBranch($atomCollection, "id", array(), $databaseBaseURL . generateURL($baseURL, "Atom XML", $queryParametersArray, true, $showRows, $rowOffset, $citeStyle, $citeOrder)); // function 'addNewBranch()' is defined in 'webservice.inc.php' // - OpenSearch elements: // - 'opensearch:totalResults' contains the number of search results available for the current search: // NOTE: The OpenSearch spec says: "If the totalResults element does not appear on the page then the // search client should consider the current page to be the last page of search results." // So does that, in turn, mean that we should better skip this element on the last results page? addNewBranch($atomCollection, "opensearch:totalResults", array(), $rowsFound); // - 'opensearch:startIndex' contains the index of the first search result in the current set of search results: addNewBranch($atomCollection, "opensearch:startIndex", array(), $startIndex); // - 'opensearch:itemsPerPage' contains the number of search results returned per page: addNewBranch($atomCollection, "opensearch:itemsPerPage", array(), $itemsPerPage); // - 'opensearch:Query' defines a search query that can be performed by search clients: if (!empty($cqlQuery)) { // convert query string to UTF-8: // (if '$convertExportDataToUTF8' is set to "yes" in 'ini.inc.php' and character encoding is not UTF-8 already) if ($convertExportDataToUTF8 == "yes" and $contentTypeCharset != "UTF-8") { $cqlQuery = convertToCharacterEncoding("UTF-8", "IGNORE", $cqlQuery); } // function 'convertToCharacterEncoding()' is defined in 'include.inc.php' addNewBranch($atomCollection, "opensearch:Query", array("role" => "request", "title" => "Current query", "searchTerms" => $cqlQuery, "startIndex" => $startIndex, "count" => $itemsPerPage), ""); } // ---------------------------------------------------------- // Add Atom XML entries: if ($showRowsOriginal != 0) { // Define inline text markup to be used by the 'citeRecord()' function: $markupPatternsArray = array("bold-prefix" => "<b>", "bold-suffix" => "</b>", "italic-prefix" => "<i>", "italic-suffix" => "</i>", "underline-prefix" => "<u>", "underline-suffix" => "</u>", "endash" => "–", "emdash" => "—", "ampersand" => "&", "double-quote" => """, "double-quote-left" => "“", "double-quote-right" => "”", "single-quote" => "'", "single-quote-left" => "‘", "single-quote-right" => "’", "less-than" => "<", "greater-than" => ">", "newline" => "\n<br>\n"); $exportArray = array(); // array for individually exported records // Generate the export for each record and push them onto an array: for ($rowCounter = 0; $rowCounter < $showRows && ($row = @mysql_fetch_array($result)); $rowCounter++) { // Export the current record as Atom XML entry: $entry = atomEntry($row, $markupPatternsArray); if (!empty($entry)) { // unless the record buffer is empty... array_push($exportArray, $entry); } // ...add it to an array of exports } // for each of the Atom XML entries in the result set... foreach ($exportArray as $atom) { $atomCollection->addXMLasBranch($atom); } } $atomCollectionDoc->setXML($atomCollection); $atomCollectionString = $atomCollectionDoc->getXMLString(); // Add the XML Stylesheet definition: // Note that this is just a hack (that should get fixed) since I don't know how to do it properly using the ActiveLink PHP XML Package ?:-/ if (!empty($exportStylesheet)) { $atomCollectionString = preg_replace("/(?=\\<feed)/i", "<?xml-stylesheet type=\"text/xsl\" href=\"" . $exportStylesheet . "\"?>\n", $atomCollectionString); } // NOTE: Firefox >=2.x, Safari >=2.x and IE >=7.x break client-side XSL for RSS and Atom feeds! // See e.g.: <http://decafbad.com/blog/2006/11/02/firefox-20-breaks-client-side-xsl-for-rss-and-atom-feeds> // TODO: Re-evaluate: This is a VERY dirty hack that prevents the feed sniffing and subsequent // browser applied default XSLT stylesheet that has been implemented by FireFox 2, Safari 2 // and Internet Explorer 7. To prevent the feed sniffing we insert a comment before the feed // element that is larger than 512 bytes. See: <http://feedme.mind-it.info/pivot/entry.php?id=9> if (!empty($exportStylesheet)) { $atomCollectionString = preg_replace("/(?=\\<feed)/i", "<!-- This is a comment that has been inserted since Internet Explorer 7, FireFox 2 and Safari 3 break client-side XSL for RSS and Atom feeds, i.e. these browsers don't honour a xml stylesheet instruction but instead apply their own default XSLT stylesheet. While this makes sense for reasons of consistency, it's very unfortunate that there's no proper option to circumvent this behaviour since it effectively prevents custom feed-based GUI solutions that were made for other purposes than the ones intended by the browser developers. Luckily the designers of these browsers use very brittle sniffing techniques that can be overridden by consuming the first 512 bytes of an XML file. This comment provides these essential 512 bytes of crud, thus preventing the feed sniffing and subsequent applied default XSLT stylesheet that has been implemented by Internet Explorer 7, FireFox 2 and Safari 2. But, unfortunately, it destroys the nice simplicity and cleanliness of this Atom feed. For more info see e.g. <http://decafbad.com/blog/2006/11/02/firefox-20-breaks-client-side-xsl-for-rss-and-atom-feeds> and <http://feedme.mind-it.info/pivot/entry.php?id=9>. -->\n", $atomCollectionString); } return $atomCollectionString; }