// Copyright: Matthias Steffens and the file's // original author(s). // // This code is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY. Please see the GNU General Public // License for more details. // // File: ./includes/atomxml.inc.php // Repository: $HeadURL$ // Author(s): Matthias Steffens // // Created: 09-Jan-08, 00:30 // Modified: $Date: 2017-04-13 02:00:18 +0000 (Thu, 13 Apr 2017) $ // $Author$ // $Revision: 1416 $ // This include file contains functions that'll export records to OpenSearch Atom XML. // Requires ActiveLink PHP XML Package, which is available under the GPL from: // . See 'opensearch.php' for more info. // TODO: I18n // Incorporate some include files: include_once 'includes/webservice.inc.php'; // include functions that are commonly used with the refbase webservices // Import the ActiveLink Packages require_once("classes/include.php"); import("org.active-link.xml.XML"); import("org.active-link.xml.XMLDocument"); // -------------------------------------------------------------------- // Return records as OpenSearch Atom XML: // // Specs: // // // // TODO: - is the feed-level ID valid? (see notes for the 'id' element) // - see inline comments labeled with "TODO" 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)) // if some records were found AND the OpenSearch query did not contain 'maximumRecords=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) // if found results don't fit on a single results page { // 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'])) // if there's no stored OpenSearch/CQL query available { // (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 // if an OpenSearch/CQL query is available, we prefer 'opensearch.php' URLs { // 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: ; // or should we use an ID such as 'urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6' ? 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) // we omit the records list in the response if the OpenSearch query did contain 'maximumRecords=0' { // Define inline text markup to be used by the 'citeRecord()' function: $markupPatternsArray = array("bold-prefix" => "", "bold-suffix" => "", "italic-prefix" => "", "italic-suffix" => "", "underline-prefix" => "", "underline-suffix" => "", "endash" => "–", "emdash" => "—", "ampersand" => "&", // this ensures correct encoding of ampersands which are inserted into the author string by citation styles such as APA "double-quote" => """, "double-quote-left" => "“", "double-quote-right" => "”", "single-quote" => "'", "single-quote-left" => "‘", "single-quote-right" => "’", "less-than" => "<", "greater-than" => ">", "newline" => "\n
\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 = @ mysqli_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("/(?=\\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.: // 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: if (!empty($exportStylesheet)) $atomCollectionString = preg_replace("/(?=\ and . -->\n",$atomCollectionString); return $atomCollectionString; } // -------------------------------------------------------------------- // Generate an OpenSearch Atom XML entry: // (returns an XML object (atom) of a single record) function atomEntry($row, $markupPatternsArray) { global $databaseBaseURL; // these variables are defined in 'ini.inc.php' global $contentTypeCharset; global $fileVisibility; global $fileVisibilityException; global $filesBaseURL; global $convertExportDataToUTF8; global $defaultCiteStyle; global $citeStyle; global $citeOrder; global $alnum, $alpha, $cntrl, $dash, $digit, $graph, $lower, $print, $punct, $space, $upper, $word, $patternModifiers; // defined in 'transtab_unicode_charset.inc.php' and 'transtab_latin1_charset.inc.php' // The array '$transtab_refbase_unicode' contains search & replace patterns for conversion from refbase markup to Unicode entities. global $transtab_refbase_unicode; // defined in 'transtab_refbase_unicode.inc.php' // The array '$transtab_refbase_ascii' contains search & replace patterns for conversion from refbase markup to plain text. global $transtab_refbase_ascii; // defined in 'transtab_refbase_ascii.inc.php' // The array '$transtab_refbase_html' contains search & replace patterns for conversion from refbase markup to HTML markup & entities. // Note that this will only convert markup which wasn't converted to Unicode entities by '$transtab_refbase_unicode'; this will provide // for correct rendering of italic and bold letters in 'content' elements (which is of 'type="xhtml"'). global $transtab_refbase_html; // defined in 'transtab_refbase_html.inc.php' // NOTE: We remove again some search & replace patterns that are present by default in '$transtab_refbase_html' since they cause // problems here; this is surely hacky but I don't know any better. :-/ unset($transtab_refbase_html['/ +- +/']); // this would incorrectly convert author initials such as "J. - L." to "J. – L." // Define inline text markup to generate a plain text citation string: // (to be included within a 'dcterms:bibliographicCitation' element) $markupPatternsArrayPlain = array("bold-prefix" => "", // NOTE: should we rather keep refbase font-shape markup (like _italic_ and **bold**) for plain text output? "bold-suffix" => "", "italic-prefix" => "", "italic-suffix" => "", "underline-prefix" => "", "underline-suffix" => "", "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" ); // This is a stupid hack that maps the names of the '$row' array keys to those used // by the '$formVars' array (which is required by function 'generateCiteKey()') // (eventually, the '$formVars' array should use the MySQL field names as names for its array keys) $formVars = buildFormVarsArray($row); // function 'buildFormVarsArray()' is defined in 'include.inc.php' // Generate or extract the cite key for this record: // (to be included within a 'dc:identifier' element) $citeKey = generateCiteKey($formVars); // function 'generateCiteKey()' is defined in 'include.inc.php' // Generate OpenURL data: // (to be included within a 'dc:identifier' element) $openURL = openURL($row, "openurl:"); // function 'openURL()' is defined in 'openurl.inc.php' // Encode special chars and perform charset conversions: foreach ($row as $rowFieldName => $rowFieldValue) { // We only convert those special chars to entities which are supported by XML: // function 'encodeHTMLspecialchars()' is defined in 'include.inc.php' $row[$rowFieldName] = encodeHTMLspecialchars($row[$rowFieldName]); // Convert field data to UTF-8: // (if '$convertExportDataToUTF8' is set to "yes" in 'ini.inc.php' and character encoding is not UTF-8 already) // (Note that charset conversion can only be done *after* the cite key has been generated, otherwise cite key // generation will produce garbled text!) // function 'convertToCharacterEncoding()' is defined in 'include.inc.php' if (($convertExportDataToUTF8 == "yes") AND ($contentTypeCharset != "UTF-8")) $row[$rowFieldName] = convertToCharacterEncoding("UTF-8", "IGNORE", $row[$rowFieldName]); } // Defines field-specific search & replace 'actions' that will be applied to all those refbase fields that are listed in the corresponding 'fields' element: // (If you don't want to perform any search and replace actions, specify an empty array, like: '$fieldSpecificSearchReplaceActionsArray = array();'. // Note that the search patterns MUST include the leading & trailing slashes -- which is done to allow for mode modifiers such as 'imsxU'.) // "/Search Pattern/" => "Replace Pattern" $fieldSpecificSearchReplaceActionsArray = array(); if ($convertExportDataToUTF8 == "yes") $fieldSpecificSearchReplaceActionsArray[] = array('fields' => array("title", "publication", "abbrev_journal", "address", "keywords", "abstract", "orig_title", "series_title", "abbrev_series_title", "notes"), 'actions' => $transtab_refbase_unicode ); // Apply field-specific search & replace 'actions' to all fields that are listed in the 'fields' element of the arrays contained in '$fieldSpecificSearchReplaceActionsArray': foreach ($fieldSpecificSearchReplaceActionsArray as $fieldActionsArray) foreach ($row as $rowFieldName => $rowFieldValue) if (in_array($rowFieldName, $fieldActionsArray['fields'])) $row[$rowFieldName] = searchReplaceText($fieldActionsArray['actions'], $rowFieldValue, true); // function 'searchReplaceText()' is defined in 'include.inc.php' // Fetch the name of the citation style file that's associated with the style given in '$citeStyle': $citeStyleFile = getStyleFile($citeStyle); // function 'getStyleFile()' is defined in 'include.inc.php' if (empty($citeStyleFile)) { $citeStyle = $defaultCiteStyle; // if the given cite style could not be found, we'll use the default cite style which is defined by the '$defaultCiteStyle' variable in 'ini.inc.php' $citeStyleFile = getStyleFile($citeStyle); } // Include the found citation style file *once*: include_once "cite/" . $citeStyleFile; // Generate a proper citation for this record, ordering attributes according to the chosen output style & record type: // - Plain text version of citation string: // (the plain text version of the citation string will be included in the 'dcterms:bibliographicCitation' element which should contain plain text only) $recordCitationPlain = citeRecord($row, $citeStyle, "", $markupPatternsArrayPlain, false); // function 'citeRecord()' is defined in the citation style file given in '$citeStyleFile' (which, in turn, must reside in the 'styles' directory of the refbase root directory) // Convert any refbase markup that remains in the citation string (such as _italic_ or **bold**) to plain text: $recordCitationPlain = searchReplaceText($transtab_refbase_ascii, $recordCitationPlain, true); // - HTML version of citation string: // (note that, for output of Atom XML, we do NOT HTML encode higher ASCII characters; thus, the last param in the below function call is 'false') $recordCitation = citeRecord($row, $citeStyle, "", $markupPatternsArray, false); // Convert any refbase markup that remains in the citation string (such as _italic_ or **bold**) into HTML markup: // (the HTML version of the citation string will be included in the Atom 'content' element which uses 'type="xhtml"') $recordCitation = searchReplaceText($transtab_refbase_html, $recordCitation, true); // Save a plain text version of the title to a new variable: // (this will be included in the 'dc:title' element which should contain plain text only) $titlePlain = searchReplaceText($transtab_refbase_ascii, $row['title'], true); // Convert any remaining refbase markup in the abstract & title to HTML markup: // (we use 'type="xhtml"' in the Atom 'title' and 'summary' elements) $row['title'] = searchReplaceText($transtab_refbase_html, $row['title'], true); $row['abstract'] = searchReplaceText($transtab_refbase_html, $row['abstract'], true); // Convert keywords to plain text: // (keywords will be written to 'dc:subject' elements which should contain plain text only) $row['keywords'] = searchReplaceText($transtab_refbase_ascii, $row['keywords'], true); // To avoid advertising email adresses in public Atom XML output, we remove the email address from contents of the // 'modified_by' and 'created_by' fields which get displayed in the 'author' and 'content' elements. // The following pattern does not attempt to do fancy parsing of email addresses but simply assumes the string format of the // 'modified_by' and 'created_by' fields (table 'refs'). If you change the string format, you must modify this pattern as well! $creatorName = preg_replace("/(.+?) \([^)]+\)/", "\\1", $row['created_by']); $editorName = preg_replace("/(.+?) \([^)]+\)/", "\\1", $row['modified_by']); // Strip any " (ed)" or " (eds)" suffix from author/editor string: if (preg_match("/ *\(eds?\)$/", $row['author'])) $row['author'] = preg_replace("/[ \r\n]*\(eds?\)/i", "", $row['author']); if (preg_match("/ *\(eds?\)$/", $row['editor'])) $row['editor'] = preg_replace("/[ \r\n]*\(eds?\)/i", "", $row['editor']); // Include a link to any corresponding file if one of the following conditions is met: // - the variable '$fileVisibility' (defined in 'ini.inc.php') is set to 'everyone' // - the variable '$fileVisibility' is set to 'login' AND the user is logged in // - the variable '$fileVisibility' is set to 'user-specific' AND the 'user_permissions' session variable contains 'allow_download' // - the array variable '$fileVisibilityException' (defined in 'ini.inc.php') contains a pattern (in array element 1) that matches the contents of the field given (in array element 0) // // TODO: - the URL-generating code should be made into a dedicated function (since it's shared with 'modsxml.inc.php' and 'oaidcxml.inc.php') $printURL = false; if ($fileVisibility == "everyone" OR ($fileVisibility == "login" AND isset($_SESSION['loginEmail'])) OR ($fileVisibility == "user-specific" AND (isset($_SESSION['user_permissions']) AND preg_match("/allow_download/", $_SESSION['user_permissions']))) OR (!empty($fileVisibilityException) AND preg_match($fileVisibilityException[1], $row[$fileVisibilityException[0]]))) { if (!empty($row['file'])) { if (preg_match('#^(https?|ftp|file)://#i', $row['file'])) // if the 'file' field contains a full URL (starting with "http://", "https://", "ftp://", or "file://") { $URLprefix = ""; // we don't alter the URL given in the 'file' field } else // if the 'file' field contains only a partial path (like 'polarbiol/10240001.pdf') or just a file name (like '10240001.pdf') { // use the base URL of the standard files directory as prefix: if (preg_match('#^/#', $filesBaseURL)) // absolute path -> file dir is located outside of refbase root dir $URLprefix = 'http://' . $_SERVER['HTTP_HOST'] . $filesBaseURL; else // relative path -> file dir is located within refbase root dir $URLprefix = $databaseBaseURL . $filesBaseURL; } $printURL = true; } } // ---------------------------------------------------------- // Start Atom XML entry: $entry = new XML("entry"); // create an XML object for a single record // Add entry-level tags: // (not yet used: category, contributor, source, rights) // - 'id': addNewBranch($entry, "id", array(), $databaseBaseURL . generateURL("show.php", "html", array("record" => $row['serial']), true)); // - 'title': // TODO: - the 'title' element is required for 'entry', so we may need to insert something else if the record's title is missing // addNewBranch($entry, "title", array("type" => "text"), $titlePlain); // plain text version addNewBranch($entry, "title", array("type" => "xhtml"), '
' . $row['title'] . '
' ); // - 'updated': addNewBranch($entry, "updated", array(), generateISO8601TimeStamp($row['modified_date'], $row['modified_time'])); // function 'generateISO8601TimeStamp()' is defined in 'include.inc.php' // - 'published': // NOTE: we use the 'published' element to indicate the date/time when the record was created in the refbase database, // and not when the record's resource was originally published addNewBranch($entry, "published", array(), generateISO8601TimeStamp($row['created_date'], $row['created_time'])); // - 'link': // NOTE: According to the Atom spec, a feed is limited to ONE 'rel=alternate' link per type and hreflang! // A) Main display formats: // - HTML output for this record: // NOTE: How can we output an 'alternate' link to the HTML citation with the same 'type'? // But, maybe, this isn't necessary since a client GUI (layered over the Atom XML data) would expose // the record citation (from the 'content' or 'bibliographicCitation' element) anyhow... ? atomLink($entry, $databaseBaseURL . generateURL("show.php", "html", array("record" => $row['serial']), true, "", "", $citeStyle), "alternate", "html", "View record in HTML format"); // function 'generateURL()' is defined in 'include.inc.php' // B) Export formats // NOTE: should we rather generate 'unapi.php' and 'opensearch.php' URLs where possible? // TODO: add links for ADS, ISI, RSS XML and Word XML // - BibTeX data for this record: atomLink($entry, $databaseBaseURL . generateURL("show.php", "BibTeX", array("record" => $row['serial']), true), "alternate", "BibTeX", "Export record in BibTeX format"); // - Endnote data for this record: atomLink($entry, $databaseBaseURL . generateURL("show.php", "Endnote", array("record" => $row['serial']), true), "alternate", "Endnote", "Export record in Endnote format"); // - RIS data for this record: atomLink($entry, $databaseBaseURL . generateURL("show.php", "RIS", array("record" => $row['serial']), true), "alternate", "RIS", "Export record in RIS format"); // - Atom XML data for this record: atomLink($entry, $databaseBaseURL . generateURL("show.php", "Atom XML", array("record" => $row['serial']), true, "", "", $citeStyle), "alternate", "Atom XML", "Export record as Atom XML"); // - MODS XML data for this record: // NOTE: while we include a link to SRW_MODS XML on feed level, we instead include a link to MODS XML on entry level since the SRW overhead isn't really needed here atomLink($entry, $databaseBaseURL . generateURL("show.php", "MODS XML", array("record" => $row['serial']), true), "alternate", "MODS XML", "Export record as MODS XML"); // - OAI_DC XML data for this record: // NOTE: A link to MODS XML is already used with this type! // atomLink($entry, $databaseBaseURL . generateURL("show.php", "OAI_DC XML", array("record" => $row['serial']), true, "", "", $citeStyle), "alternate", "OAI_DC XML", "Export record as OAI_DC XML"); // - ODF XML data for this record: atomLink($entry, $databaseBaseURL . generateURL("show.php", "ODF XML", array("record" => $row['serial'], "exportType" => "file"), true), "alternate", "ODF XML", "Export record as ODF XML"); // - SRW_DC XML data for this record: // NOTE: A link to MODS XML is already used with this type! // atomLink($entry, $databaseBaseURL . generateURL("show.php", "SRW_DC XML", array("record" => $row['serial']), true, "", "", $citeStyle), "alternate", "SRW_DC XML", "Export record as SRW_DC XML"); // - SRW_MODS XML data for this record: // NOTE: A link to MODS XML is already used with this type! // atomLink($entry, $databaseBaseURL . generateURL("show.php", "SRW_MODS XML", array("record" => $row['serial']), true), "alternate", "SRW_MODS XML", "Export record as SRW_MODS XML"); // C) Citation formats: // - RTF citations for this record: atomLink($entry, $databaseBaseURL . generateURL("show.php", "RTF", array("record" => $row['serial']), true, "", "", $citeStyle, $citeOrder), "alternate", "RTF", "Output record as citation in RTF format"); // - PDF citations for this record: atomLink($entry, $databaseBaseURL . generateURL("show.php", "PDF", array("record" => $row['serial']), true, "", "", $citeStyle, $citeOrder), "alternate", "PDF", "Output record as citation in PDF format"); // - LaTeX citations for this record: atomLink($entry, $databaseBaseURL . generateURL("show.php", "LaTeX", array("record" => $row['serial']), true, "", "", $citeStyle, $citeOrder), "alternate", "LaTeX", "Output record as citation in LaTeX format"); // - LaTeX .bbl citations for this record: // NOTE: A link to a LaTeX citation is already used with this type! // atomLink($entry, $databaseBaseURL . generateURL("show.php", "LaTeX .bbl", array("record" => $row['serial']), true, "", "", $citeStyle, $citeOrder), "alternate", "LaTeX .bbl", "Output record as citation in LaTeX .bbl format"); // - Markdown citations for this record: atomLink($entry, $databaseBaseURL . generateURL("show.php", "Markdown", array("record" => $row['serial']), true, "", "", $citeStyle, $citeOrder), "alternate", "Markdown", "Output record as citation in Markdown format"); // - ASCII citations for this record: // NOTE: A link to a Markdown citation is already used with this type! // atomLink($entry, $databaseBaseURL . generateURL("show.php", "ASCII", array("record" => $row['serial']), true, "", "", $citeStyle, $citeOrder), "alternate", "ASCII", "Output record as citation in ASCII format"); // D) Related links: // - Related URL: // TODO: - the 'type' (and 'title'?) attributes should get adopted if something other than an URL pointing to a HTML page is given if (!empty($row['url'])) atomLink($entry, $row['url'], "related", "html", "Web page"); // - Related FILE: // NOTE: - should we better use the 'enclosure' element instead? // TODO: - the 'type' attribute should get adopted if something other than PDF is given if ($printURL) atomLink($entry, $URLprefix . $row['file'], "related", "PDF", "Electronic full text"); // - 'author': // NOTE: The Atom 'author' element contains the database user who created this record, // while the publication authors are contained within 'dc:creator' elements $recAuthorBranch = new XMLBranch("author"); $recAuthorBranch->setTagContent($creatorName, "author/name"); $entry->addXMLBranch($recAuthorBranch); // - 'contributor': // NOTE: The Atom 'contributor' element contains the database user who edited this record last, // while the publication editors are contained within 'dc:contributor' elements if ($creatorName != $editorName) { $recEditorBranch = new XMLBranch("contributor"); $recEditorBranch->setTagContent($editorName, "contributor/name"); $entry->addXMLBranch($recEditorBranch); } // - 'content': // NOTE: According to the Atom spec, all HTML markup must be escaped if 'type="html"' is used. In case of // 'type="xhtml"', HTML markup is not entity escaped but must be wrapped in a single XHTML 'div' element. // See: // // addNewBranch($entry, "content", array("type" => "html"), encodeHTMLspecialchars($recordCitation)); addNewBranch($entry, "content", array("type" => "xhtml"), '
' . '
$row['serial']), true) . '">
' // should we omit unAPI IDs? and is it valid to nest divs within the main enclosing divs? . $recordCitation // . "

" // last-modified info is already contained in the Atom elements 'contributor' and 'updated' // . "Edited by " . $editorName // . " on " . generateRFC2822TimeStamp($row['modified_date'], $row['modified_time']) . "." // function 'generateRFC2822TimeStamp()' is defined in 'include.inc.php' . '
' ); // - 'summary': if (!empty($row['abstract'])) { addNewBranch($entry, "summary", array("type" => "xhtml"), '
' . $row['abstract'] . '
' ); } // ---------------------------------------------------------- // Add Dublin Core elements: // NOTE: With a few exceptions, we try to adhere to the guidelines given at // "Using simple Dublin Core to describe eprints" by Andy Powell et al. // See: // - 'dc:title': if (!empty($row['title'])) addMetaElement($entry, "dc", "title", array(), $titlePlain); // function 'addMetaElement()' is defined in 'webservice.inc.php' // - 'dc:creator': // NOTE: should we use 'foaf:maker' instead of (or in addition to) 'dc:creator'? // ( xmlns:foaf="http://xmlns.com/foaf/0.1/" ) // // // // [ Name of author 1 ] // // if (!empty($row['author']) AND ($row['author'] != $row['editor'])) addMetaElement($entry, "dc", "creator", array(), $row['author']); // - 'dc:creator': // TODO: add refbase corporate author(s) as 'dc:creator' // - 'dc:contributor': if (!empty($row['editor'])) addMetaElement($entry, "dc", "contributor", array(), $row['editor']); // - 'dc:description': // NOTE: since we already use the Atom-native 'summary' element for the record // abstract/summary, we don't add the abstract again as 'dc:description' // - 'dc:identifier': // - DOI: if (!empty($row['doi'])) addMetaElement($entry, "dc", "identifier", array(), $row['doi'], "doi"); // - PMID: if (!empty($row['notes']) AND preg_match("/PMID *: *\d+/i", $row['notes'])) addMetaElement($entry, "dc", "identifier", array(), $row['notes'], "pmid"); // - arXiv: if (!empty($row['notes']) AND preg_match("/arXiv *: *[^ ;]+/i", $row['notes'])) addMetaElement($entry, "dc", "identifier", array(), $row['notes'], "arxiv"); // - ISBN: if (!empty($row['isbn'])) addMetaElement($entry, "dc", "identifier", array(), $row['isbn'], "isbn"); // - OpenURL: addMetaElement($entry, "dc", "identifier", array(), $openURL, "openurl"); // - Cite key: addMetaElement($entry, "dc", "identifier", array(), $citeKey, "citekey"); // - 'dcterms:bibliographicCitation': // NOTE: While Andy Powell (see link above) recommends to put this into a // 'dc:identifier' element, we'll put it into a 'dcterms:bibliographicCitation' // element instead, since the citation couldn't be uniquely identified within a // 'dc:identifier' element without a 'citation:' prefix (or the like) but that // would be non-standard. Within 'dcterms:bibliographicCitation', the citation // can be uniquely identified and extracted easily. // Compare with 'oaidcxml.inc.php' where, for 'oai_dc:dc' output, we put the // bibliographic citation into a 'dc:identifier' element and use a "citation:" // prefix: // addMetaElement($entry, "dc", "identifier", array(), encodeHTMLspecialchars($recordCitationPlain), "citation"); addMetaElement($entry, "dcterms", "bibliographicCitation", array(), encodeHTMLspecialchars($recordCitationPlain)); // - 'dc:source': // NOTE: - In , // Andy Powell et al. recommend that this element should NOT be used! // However, for Atom XML output, we do use the 'dc:source' element for series // info (series title plus volume & issue). // Compare with 'oaidcxml.inc.php' where, for 'oai_dc:dc' output, we also // include publication info in a 'dc:source' element. // Example: Polar Biology, Vol. 25, No. 10 // - Series info: if (!empty($row['series_title']) OR !empty($row['abbrev_series_title'])) { if (!empty($row['series_title'])) $series = $row['series_title']; elseif (!empty($row['abbrev_series_title'])) $series = $row['abbrev_series_title']; if (!empty($row['series_volume'])) $series .= ", Vol. " . $row['series_volume']; if (!empty($row['series_issue'])) $series .= ", No. " . $row['series_issue']; if (!empty($series)) addMetaElement($entry, "dc", "source", array(), $series); // NOTE: To distinguish between regular publication & series info, // should we better use a "series:" prefix here? If so, use: // addMetaElement($entry, "dc", "source", array(), $series, "series"); } // - 'dc:date': if (!empty($row['year'])) addMetaElement($entry, "dc", "date", array(), $row['year']); // - 'dc:type': if (!empty($row['type'])) addMetaElement($entry, "dc", "type", array(), $row['type'], $row['thesis']); // In case of a thesis, we add another 'dc:type' element with the actual thesis type: if (!empty($row['thesis'])) addMetaElement($entry, "dc", "type", array(), $row['thesis']); // - 'dc:format': // TODO: ideally, we should parse the content of the refbase 'medium' field and map it // to a media-type term from if (!empty($row['medium'])) $mediaType = $row['medium']; else $mediaType = "text"; addMetaElement($entry, "dc", "format", array(), $mediaType); // - 'dc:subject': if (!empty($row['keywords'])) addMetaElement($entry, "dc", "subject", array(), $row['keywords']); // - 'dc:coverage': // TODO: should we add contents from the refbase 'area' field as 'dc:coverage' element(s)? // - 'dc:relation': // NOTE: currently, we only add 'related' links (and not 'alternate' links) as 'dc:relation' // - Related URL: if (!empty($row['url'])) addMetaElement($entry, "dc", "relation", array(), $row['url'], "url"); // - Related FILE: if ($printURL) addMetaElement($entry, "dc", "relation", array(), $URLprefix . $row['file'], "file"); // - 'dc:publisher': if (!empty($row['publisher'])) addMetaElement($entry, "dc", "publisher", array(), $row['publisher']); // - 'dc:language': // TODO: convert to ISO notation (i.e. "en" instead of "English", etc) if (!empty($row['language'])) addMetaElement($entry, "dc", "language", array(), $row['language']); // ---------------------------------------------------------- // Add PRISM elements: // (not yet used: section) // - 'prism:issn': // NOTE: see note for ISBN above if (!empty($row['issn'])) addMetaElement($entry, "prism", "issn", array(), $row['issn']); // - 'prism:publicationName': if (!empty($row['publication'])) addMetaElement($entry, "prism", "publicationName", array(), $row['publication']); elseif (!empty($row['abbrev_journal'])) addMetaElement($entry, "prism", "publicationName", array(), $row['abbrev_journal']); // - 'prism:publicationDate': if (!empty($row['year'])) addMetaElement($entry, "prism", "publicationDate", array(), $row['year']); // - 'prism:volume': if (!empty($row['volume'])) addMetaElement($entry, "prism", "volume", array(), $row['volume']); // - 'prism:number': if (!empty($row['issue'])) addMetaElement($entry, "prism", "number", array(), $row['issue']); // - 'prism:startingPage', 'prism:endingPage': // TODO: Similar code is used in 'include.in.php', 'modsxml.inc.php' and 'openurl.inc.php', // so this should be made into a dedicated function! if (!empty($row['pages']) AND preg_match("/\d+/i", $row['pages'])) // if the 'pages' field contains a number { $pages = preg_replace("/^\D*(\d+)( *[$dash]+ *\d+)?.*/i$patternModifiers", "\\1\\2", $row['pages']); // extract page range (if there's any), otherwise just the first number $startPage = preg_replace("/^\D*(\d+).*/i", "\\1", $row['pages']); // extract starting page $endPage = extractDetailsFromField("pages", $pages, "/\D+/", "[-1]"); // extract ending page (function 'extractDetailsFromField()' is defined in 'include.inc.php') // NOTE: To extract the ending page, we'll use function 'extractDetailsFromField()' // instead of just grabbing a matched regex pattern since it'll also work // when just a number but no range is given (e.g. when startPage = endPage) // - 'prism:startingPage': if (preg_match("/\d+ *[$dash]+ *\d+/i$patternModifiers", $row['pages'])) // if there's a page range addMetaElement($entry, "prism", "startingPage", array(), $startPage); // - 'prism:endingPage': addMetaElement($entry, "prism", "endingPage", array(), $endPage); } // See also other potentially useful elements from arXiv Atom feeds: // (arXiv example: ) // // // // Margarita Voitikova // // // Institute of Molecular and Atomic Physics, National Academy of Sciences of Belarus // // // // // 6 pages, 3 figures, submitted for publication // // // // Theory in Biosciences, 123, 431 (2005) // // // return $entry; } // -------------------------------------------------------------------- // Add a link to an Atom XML object: // // Specs: // function atomLink(&$atom, $url, $linkRelation = "", $linkFormat = "", $linkTitle = "") { $linkType = ""; $elementAttributeArray = array(); // Define media types for the different formats: // TODO: add types for ADS, ISI and Word XML if (preg_match("/^HTML$/i", $linkFormat)) $linkType = "text/html"; elseif (preg_match("/^RTF$/i", $linkFormat)) $linkType = "application/rtf"; elseif (preg_match("/^BibTeX$/i", $linkFormat)) $linkType = "application/x-bibtex"; elseif (preg_match("/^Endnote$/i", $linkFormat)) $linkType = "application/x-endnote-refer"; elseif (preg_match("/^RIS$/i", $linkFormat)) $linkType = "application/x-Research-Info-Systems"; elseif (preg_match("/^Atom([ _]?XML)?$/i", $linkFormat)) $linkType = "application/atom+xml"; elseif (preg_match("/^RSS([ _]?XML)?$/i", $linkFormat)) $linkType = "application/rss+xml"; elseif (preg_match("/^((MODS|(OAI_)?DC)([ _]?XML)?|SRW([ _]?(MODS|DC))?([ _]?XML)?|unAPI)$/i", $linkFormat)) $linkType = "application/xml"; elseif (preg_match("/^ODF([ _]?XML)?$/i", $linkFormat)) $linkType = "application/vnd.oasis.opendocument.spreadsheet"; elseif (preg_match("/^OpenSearch$/i", $linkFormat)) $linkType = "application/opensearchdescription+xml"; elseif (preg_match("/^RTF$/i", $linkFormat)) $linkType = "application/rtf"; elseif (preg_match("/^PDF$/i", $linkFormat)) $linkType = "application/pdf"; elseif (preg_match("/^LaTeX([ _]?\.?bbl)?$/i", $linkFormat)) $linkType = "application/x-latex"; elseif (preg_match("/^(Markdown|ASCII)$/i", $linkFormat)) $linkType = "text/plain"; if (!empty($url)) { // Add 'rel' attribute which contains a single link relationship type: // (predefined values: alternate, enclosure, related, self, via) if (!empty($linkRelation)) $elementAttributeArray["rel"] = $linkRelation; // Add 'type' attribute which indicates the media type of the resource: if (!empty($linkType)) $elementAttributeArray["type"] = $linkType; // Add 'title' attribute which contains human readable information about the link: // (typically for display purposes) if (!empty($linkTitle)) $elementAttributeArray["title"] = $linkTitle; // Add 'href' attribute (required) which contains the URI of the referenced resource: // (typically a Web page) $elementAttributeArray["href"] = $url; // Add 'link' element as a new XML branch to the '$atom' object: addNewBranch($atom, "link", $elementAttributeArray, ""); } return $atom; } // -------------------------------------------------------------------- // Generate the basic OpenSearch Atom XML tree required for a query response: // ('$atomOperation' can be either "Error" or "Results") // // Specs: // // function atomGenerateBaseTags($atomOperation) { global $officialDatabaseName; // these variables are specified in 'ini.inc.php' global $databaseBaseURL; global $feedbackEmail; global $contentTypeCharset; global $convertExportDataToUTF8; global $logoImageURL; global $faviconImageURL; global $query; $atomCollection = new XML("feed"); $atomCollection->setTagAttribute("xmlns", "http://www.w3.org/2005/Atom"); $atomCollection->setTagAttribute("xmlns:opensearch", "http://a9.com/-/spec/opensearch/1.1/"); $atomCollection->setTagAttribute("xmlns:unapi", "http://unapi.info/"); // NOTE: is the unAPI namespace ok? Or should we use "http://unapi.info/specs/", or maybe something like "http://purl.org/unapi/ns/" ? $atomCollection->setTagAttribute("xmlns:dc", "http://purl.org/dc/elements/1.1/"); $atomCollection->setTagAttribute("xmlns:dcterms", "http://purl.org/dc/terms/"); $atomCollection->setTagAttribute("xmlns:prism", "http://prismstandard.org/namespaces/1.2/basic/"); $officialDatabaseNameConv = encodeHTMLspecialchars($officialDatabaseName); // function 'encodeHTMLspecialchars()' is defined in 'include.inc.php' if ($atomOperation != "Error") // for OpenSearch diagnostics (i.e. errors), Atom XML is served with the original database encoding, otherwise: { // convert database name 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")) $officialDatabaseNameConv = convertToCharacterEncoding("UTF-8", "IGNORE", $officialDatabaseNameConv); // function 'convertToCharacterEncoding()' is defined in 'include.inc.php' } // ---------------------------------------------------------- // Add feed-level tags: // (not yet used: category, contributor, rights) // - 'title': addNewBranch($atomCollection, "title", array("type" => "text"), $officialDatabaseNameConv); // - 'subtitle': if ($atomOperation == "Error") addNewBranch($atomCollection, "subtitle", array(), "Search error!"); else { // ...extract the 'WHERE' clause from the SQL query to include a natural-language version (well, sort of) within the 'subtitle' element: $queryWhereClause = extractWHEREclause($query); // function 'extractWHEREclause()' is defined in 'include.inc.php' // construct a meaningful feed description based on the actual 'WHERE' clause: // TODO: For Atom XML, the query string should not get HTML encoded! $subTitle = "Displays records where " . encodeHTML(explainSQLQuery($queryWhereClause)); // functions 'encodeHTML()' and 'explainSQLQuery()' are defined in 'include.inc.php' addNewBranch($atomCollection, "subtitle", array(), $subTitle); } // - 'updated': // (TODO: the timestamp in the 'updated' element should really only get updated if any of the matching records was updated, right?) addNewBranch($atomCollection, "updated", array(), generateISO8601TimeStamp()); // function 'generateISO8601TimeStamp()' is defined in 'include.inc.php' // - 'author': $authorBranch = new XMLBranch("author"); $authorBranch->setTagContent($officialDatabaseNameConv, "author/name"); $authorBranch->setTagContent($feedbackEmail, "author/email"); $authorBranch->setTagContent($databaseBaseURL, "author/uri"); $atomCollection->addXMLBranch($authorBranch); // - 'generator', 'icon', 'logo': addNewBranch($atomCollection, "generator", array("uri" => "http://www.refbase.net/", "version" => "0.9.5"), "Web Reference Database (http://refbase.sourceforge.net)"); addNewBranch($atomCollection, "icon", array(), $databaseBaseURL . $faviconImageURL); addNewBranch($atomCollection, "logo", array(), $databaseBaseURL . $logoImageURL); // - 'link' (more links will be added in function 'atomCollection()'): // - link to OpenSearch Description file: atomLink($atomCollection, $databaseBaseURL . "opensearch.php?operation=explain", "search", "OpenSearch", $officialDatabaseNameConv); // - link to unAPI server: atomLink($atomCollection, $databaseBaseURL . "unapi.php", "unapi:unapi-server", "unAPI", "unAPI"); return $atomCollection; } // -------------------------------------------------------------------- ?>