// 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/webservice.inc.php // Repository: $HeadURL: file:///svn/p/refbase/code/branches/bleeding-edge/includes/webservice.inc.php $ // Author(s): Matthias Steffens // // Created: 04-Feb-06, 22:02 // Modified: $Date: 2012-02-27 20:25:30 +0000 (Mon, 27 Feb 2012) $ // $Author: msteffens $ // $Revision: 1337 $ // This include file contains functions that are used in conjunction with the refbase webservices. // Requires ActiveLink PHP XML Package, which is available under the GPL from: // . See 'sru.php' and 'opensearch.php' for more info. // Import the ActiveLink Packages require_once("classes/include.php"); import("org.active-link.xml.XML"); import("org.active-link.xml.XMLDocument"); // -------------------------------------------------------------------- // Add a new XML branch, optionally with an attribute and tag content: // // TODO: this function should also accept arrays to add multiple content tags function addNewBranch(&$thisBranch, $elementName, $elementAttributeArray, $elementContent) { $newBranch = new XMLBranch($elementName); if (!empty($elementAttributeArray)) foreach ($elementAttributeArray as $elementAttributeKey => $elementAttributeValue) $newBranch->setTagAttribute($elementAttributeKey, $elementAttributeValue); if (!empty($elementContent)) $newBranch->setTagContent($elementContent); $thisBranch->addXMLBranch($newBranch); } // ------------------------------------------------------------------------------------------------------------------- // Parse CQL query: // This function parses a CQL query into its elements (context set, index, relation and search term(s)), // builds appropriate SQL search terms and returns a hierarchical array containing the converted search terms // (this array, in turn, gets merged into a full SQL WHERE clause by function 'appendToWhereClause()' in // 'include.inc.php') // // NOTE: we don't provide a full CQL parser here but will (for now) concentrate on a rather limited feature // set that makes sense in conjunction with refbase. However, future versions should employ far better // CQL parsing logic. // // TODO: the special index 'main_fields' should be mapped to the user's preferred list of "main fields" function parseCQL($sruVersion, $sruQuery, $operation = "") { 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' // map CQL indexes to refbase field names: $indexNamesArray = mapCQLIndexes(); $searchArray = array(); // intialize array that will hold information about context set, index name, relation and search value $searchSubArray1 = array(); // -------------------------------- if (!empty($sruQuery)) { // check for presence of context set/index name and any of the main relations: if (!preg_match('/^[^\" <>=]+( +(all|any|exact|within) +| *(<>|<=|>=|<|>|=) *)/', $sruQuery)) { // if no context set/index name and relation was given we'll add meaningful defaults: if (preg_match("/^suggest$/i", $operation)) $sruQuery = "main_fields all " . $sruQuery; // for OpenSearch search suggestions, we use the special 'main_fields' index by default else $sruQuery = "cql.serverChoice all " . $sruQuery; // otherwise we currently use 'cql.serverChoice' (since 'main_fields' isn't yet supported for regular OpenSearch queries) } // extract the context set: if (preg_match('/^([^\" <>=.]+)\./', $sruQuery)) $contextSet = preg_replace('/^([^\" <>=.]+)\..*/', '\\1', $sruQuery); else $contextSet = ""; // use the default context set // extract the index: $indexName = preg_replace('/^(?:[^\" <>=.]+\.)?([^\" <>=.]+).*/', '\\1', $sruQuery); // ---------------- // return a fatal diagnostic if the CQL query does contain an unrecognized 'set.index' identifier: // (a) verify that the given context set (if any) is recognized: if (!empty($contextSet)) { $contextSetIndexConnector = "."; $contextSetLabel = "context set '" . $contextSet . "'"; if (!preg_match("/^(dc|bath|rec|bib|cql)$/", $contextSet)) { returnDiagnostic(15, $contextSet); // unsupported context set (function 'returnDiagnostic()' is defined in 'opensearch.php' and 'sru.php') exit; } } else { $contextSetIndexConnector = ""; $contextSetLabel = "empty context set"; } // (b) verify that the given 'set.index' term is recognized: if (!isset($indexNamesArray[$contextSet . $contextSetIndexConnector . $indexName])) { if (isset($indexNamesArray[$indexName]) OR isset($indexNamesArray["dc." . $indexName]) OR isset($indexNamesArray["bath." . $indexName]) OR isset($indexNamesArray["rec." . $indexName]) OR isset($indexNamesArray["bib." . $indexName]) OR isset($indexNamesArray["cql." . $indexName])) // this may be clumsy but I don't know any better, right now { returnDiagnostic(10, "Unsupported combination of " . $contextSetLabel . " with index '" . $indexName . "'"); // unsupported combination of context set & index } else { returnDiagnostic(16, $indexName); // unsupported index } exit; } // ---------------- // extract the main relation (relation modifiers aren't supported yet!): $mainRelation = preg_replace('/^[^\" <>=]+( +(all|any|exact|within) +| *(<>|<=|>=|<|>|=) *).*/', '\\1', $sruQuery); // remove any runs of leading or trailing whitespace: $mainRelation = trim($mainRelation); // ---------------- // extract the search term: $searchTerm = preg_replace('/^[^\" <>=]+(?: +(?:all|any|exact|within) +| *(?:<>|<=|>=|<|>|=) *)(.*)/', '\\1', $sruQuery); // remove slashes from search term if 'magic_quotes_gpc = On': $searchTerm = stripSlashesIfMagicQuotes($searchTerm); // function 'stripSlashesIfMagicQuotes()' is defined in 'include.inc.php' // remove any leading or trailing quotes from the search term: // (note that multiple query parts connected with boolean operators aren't supported yet!) $searchTerm = preg_replace('/^\"/', '', $searchTerm); $searchTerm = preg_replace('/\"$/', '', $searchTerm); // OpenSearch search suggestions ('$operation=suggest'): since CQL matches full words (not sub-strings), // we need to make sure that every search term ends with the '*' masking character: if (preg_match("/^suggest$/i", $operation) AND ($mainRelation != "exact")) $searchTerm = preg_replace("/([$word]+)(?![?*^])/$patternModifiers", "\\1*", $searchTerm); // escape meta characters (including '/' that is used as delimiter for the PCRE replace functions below and which gets passed as second argument): $searchTerm = preg_quote($searchTerm, "/"); // escape special regular expression characters: . \ + * ? [ ^ ] $ ( ) { } = ! < > | : // account for CQL anchoring ('^') and masking ('*' and '?') characters: // NOTE: in the code block above we quote everything to escape possible meta characters, // so all special chars in the block below have to be matched in their escaped form! // (The expression '\\\\' in the patterns below describes only *one* backslash! -> '\'. // The reason for this is that before the regex engine can interpret the \\ into \, PHP interprets it. // Thus, you have to escape your backslashes twice: once for PHP, and once for the regex engine.) // // more info about masking characters in CQL: // more info about word anchoring in CQL: // recognize any anchor at the beginning of a search term (like '^foo'): // (in CQL, a word beginning with ^ must be the first in its field) $searchTerm = preg_replace('/(^| )\\\\\^/', '\\1^', $searchTerm); // convert any anchor at the end of a search term (like 'foo^') to the correct MySQL variant ('foo$'): // (in CQL, a word ending with ^ must be the last in its field) $searchTerm = preg_replace('/\\\\\^( |$)/', '$\\1', $searchTerm); // recognize any masking ('*' and '?') characters: // Note: by "character" we do refer to *word* characters here, i.e., any character that is not a space or punctuation character (see below); // however, I'm not sure if the masking characters '*' and '?' should also include non-word characters! $searchTerm = preg_replace('/(?:]] // // They match the beginning and end of words, respectively. A word is a sequence of word characters that is not preceded by or // followed by word characters. A word character is an alphanumeric character in the alnum class or an underscore (_). $whereClausePart .= implode(" AND " . $indexNamesArray[$contextSet . $contextSetIndexConnector . $indexName], $whereClauseSubPartsArray); } else $whereClausePart .= " RLIKE " . quote_smart("(^|[[:space:][:punct:]])" . $searchTerm . "([[:space:][:punct:]]|$)"); } elseif ($mainRelation == "any") // matches full words (not sub-strings); 'any' means "any of these words" { $searchTerm = splitAndMerge("/ +/", "|", $searchTerm); // function 'splitAndMerge()' is defined in 'include.inc.php' $whereClausePart .= " RLIKE " . quote_smart("(^|[[:space:][:punct:]])(" . $searchTerm . ")([[:space:][:punct:]]|$)"); } elseif ($mainRelation == "exact") // 'exact' is used for exact string matching, i.e., it matches field contents exactly $whereClausePart .= " = " . quote_smart($searchTerm); elseif ($mainRelation == "within") // matches a range (i.e. requires two space-separated dimensions) { if (preg_match("/[^ ]+ [^ ]+/", $searchTerm)) { $searchTermArray = preg_split("/ +/", $searchTerm); $whereClausePart .= " >= " . quote_smart($searchTermArray[0]) . " AND " . $indexNamesArray[$contextSet . $contextSetIndexConnector . $indexName] . " <= " . quote_smart($searchTermArray[1]); } else { returnDiagnostic(36, "Search term requires two space-separated dimensions. Example: dc.date within \"2004 2005\""); exit; } } elseif ($mainRelation == "=") // matches full words (not sub-strings); '=' is used for word adjacency, the words appear in that order with no others intervening $whereClausePart .= " RLIKE " . quote_smart("(^|[[:space:][:punct:]])" . $searchTerm . "([[:space:][:punct:]]|$)"); elseif ($mainRelation == "<>") // does this also match full words (and not sub-strings) ?:-/ $whereClausePart .= " NOT RLIKE " . quote_smart("(^|[[:space:][:punct:]])" . $searchTerm . "([[:space:][:punct:]]|$)"); elseif ($mainRelation == "<") $whereClausePart .= " < " . quote_smart($searchTerm); elseif ($mainRelation == "<=") $whereClausePart .= " <= " . quote_smart($searchTerm); elseif ($mainRelation == ">") $whereClausePart .= " > " . quote_smart($searchTerm); elseif ($mainRelation == ">=") $whereClausePart .= " >= " . quote_smart($searchTerm); $searchSubArray1[] = array("_boolean" => "", "_query" => $whereClausePart); } // -------------------------------- else // '$sruQuery' was empty -> return all records: { $searchSubArray1[] = array("_boolean" => "", "_query" => "serial RLIKE " . quote_smart(".+")); } // -------------------------------- if (!empty($searchSubArray1)) $searchArray[] = array("_boolean" => "", "_query" => $searchSubArray1); return $searchArray; } // ------------------------------------------------------------------------------------------------------------------- // Add a metadata element to the given object: // As an example, the function call 'addMetaElement($object, "dc", "title", array("lang" => "en"), "this is a title")' // would add 'this is a title' as a new branch to the given '$object'. // // TODO: expand function so that it can be also used for formats other than XML (e.g. HTML) function addMetaElement(&$object, $namespace, $elementName, $elementAttributeArray, $elementContent, $elementType = "", $format = "xml") { $addStatus = false; if (!empty($elementName) AND !empty($elementContent)) { // Preprocess element contents (if necessary): // - 'creator', 'contributor': if (preg_match("/^(creator|contributor)$/", $elementName)) $elementContent = getPersons($elementContent); // get an array of all creators (i.e. authors) or contributors (e.g. editors) // - 'identifier': // NOTE: should we support any other identifiers from the "info" URI scheme? // see // - DOI: elseif ($elementName == "identifier" AND $elementType == "doi") $elementContent = "info:doi/" . $elementContent; // - PMID: elseif ($elementName == "identifier" AND $elementType == "pmid") { // extract any PubMed ID from the given '$elementContent': // NOTE: should this better be done in the calling function? $pubmedID = preg_replace("/.*?PMID *: *(\d+).*/i", "\\1", $elementContent); $elementContent = "info:pmid/" . $pubmedID; } // - arXiv: elseif ($elementName == "identifier" AND $elementType == "arxiv") { // extract any arXiv ID from the given '$elementContent': // NOTE: see note for PMID $arxivID = preg_replace("/.*?arXiv *: *([^ ;]+).*/i", "\\1", $elementContent); $elementContent = "info:arxiv/" . $arxivID; } // - ISBN: // NOTE: we could also output the ISBN or ISSN as a value URI within a // 'dcterms:isPartOf' relation property, e.g.: // 'urn:ISSN:0740-8188' elseif ($elementName == "identifier" AND $elementType == "isbn") $elementContent = "urn:ISBN:" . $elementContent; // - ISSN: // NOTE: see note for ISBN above elseif ($elementName == "identifier" AND $elementType == "issn") $elementContent = "urn:ISSN:" . $elementContent; // - OpenURL: elseif ($elementName == "identifier" AND $elementType == "openurl") { if (!preg_match("/^openurl:/", $elementContent)) $elementContent = "openurl:" . $elementContent; // use "openurl:" prefix if doesn't already exist in the given OpenURL } // - URL: // NOTE: the 'url:' prefix is non-standard, is there a better way to // include a permanent URL for a record in Simple Dublin Core XML output? elseif ($elementName == "identifier" AND $elementType == "url") $elementContent = "url:" . $elementContent; // - Cite key: // NOTE: the 'citekey:' prefix is non-standard, is there a better way to // include the cite key in Simple Dublin Core XML output? elseif ($elementName == "identifier" AND $elementType == "citekey") $elementContent = "citekey:" . $elementContent; // - Bibliographic citation: // NOTE: the 'citation:' prefix is non-standard, is there a better way to // include the bibliographic citation in Simple Dublin Core XML output? elseif ($elementName == "identifier" AND $elementType == "citation") $elementContent = "citation:" . $elementContent; // - 'source': // - Series: // NOTE: the 'series:' prefix is non-standard, is there a better way to // include series information in Simple Dublin Core XML output? elseif ($elementName == "source" AND $elementType == "series") $elementContent = "series:" . $elementContent; // - ISSN: // NOTE: see note for ISBN above elseif ($elementName == "source" AND $elementType == "issn") $elementContent = "urn:ISSN:" . $elementContent; // - 'relation': // - URL: // NOTE: the 'url:' prefix is non-standard, is there a better way to // include a permanent URL for a record in Simple Dublin Core XML output? elseif ($elementName == "relation" AND $elementType == "url") $elementContent = "url:" . $elementContent; // - FILE: // NOTE: the 'file:' prefix is non-standard, is there a better way to // include an URL to a file representing this record in Simple Dublin Core XML output? elseif ($elementName == "relation" AND $elementType == "file") $elementContent = "file:" . $elementContent; // - 'type': elseif ($elementName == "type") { if (preg_match("/^((Simple|oai)?[- _]?(dc|Dublin[- _]?Core)[- _]?(terms)?)$/i", $namespace)) { // Map refbase types to the corresponding eprint/resource types suggested for Simple // Dublin Core (): $dcTypesArray = mapDCTypes(); // NOTE: for '$elementName="type"', variable '$elementType' is supposed to contain the // thesis type from the refbase 'thesis' field (e.g. "Ph.D. thesis") if (isset($dcTypesArray[$elementContent]) AND empty($elementType)) $elementContent = $dcTypesArray[$elementContent]; elseif (!empty($elementType)) $elementContent = $dcTypesArray["Thesis"]; } } // - 'subject': if ($elementName == "subject") $elementContent = preg_split("/\s*;\s*/", $elementContent, -1, PREG_SPLIT_NO_EMPTY); // get an array of all keywords // - 'language': // TODO: convert to ISO notation (i.e. "en" instead of "English", etc) // see if ($elementName == "language") $elementContent = preg_split("/\s*[;,]\s*/", $elementContent, -1, PREG_SPLIT_NO_EMPTY); // get an array of all languages // Prefix element name with given namespace: if (!empty($namespace)) $elementName = $namespace . ":" . $elementName; // Add metadata element(s) to the given object: if (is_array($elementContent)) // add each array item as a new element: { foreach ($elementContent as $singleElement) addNewBranch($object, $elementName, $elementAttributeArray, $singleElement); } else // add string in '$elementContent' as a new element: addNewBranch($object, $elementName, $elementAttributeArray, $elementContent); $addStatus = true; } return $addStatus; } // -------------------------------------------------------------------- // Split a string of person names (authors/editors) into an array: function getPersons($personString, $standardizePersonNames = true, $betweenNamesDelim = "/ *; */", $nameGivenDelim = "/ *, */", $newBetweenGivensDelim = ".") { if ($standardizePersonNames) { // NOTE: We standardize person names (e.g. add dots between initials if missing) in an attempt to adhere to // the recommendations given at // // Call the 'reArrangeAuthorContents()' function (defined in 'include.inc.php') in order to re-order contents of the author field. Required Parameters: // 1. input: contents of the author field // 2. input: boolean value that specifies whether the author's family name comes first (within one author) in the source string // ('true' means that the family name is followed by the given name (or initials), 'false' if it's the other way around) // // 3. input: pattern describing old delimiter that separates different authors // 4. output: for all authors except the last author: new delimiter that separates different authors // 5. output: for the last author: new delimiter that separates the last author from all other authors // // 6. input: pattern describing old delimiter that separates author name & initials (within one author) // 7. output: for the first author: new delimiter that separates author name & initials (within one author) // 8. output: for all authors except the first author: new delimiter that separates author name & initials (within one author) // 9. output: new delimiter that separates multiple initials (within one author) // 10. output: for the first author: boolean value that specifies if initials go *before* the author's name ['true'], or *after* the author's name ['false'] (which is the default in the db) // 11. output: for all authors except the first author: boolean value that specifies if initials go *before* the author's name ['true'], or *after* the author's name ['false'] (which is the default in the db) // 12. output: boolean value that specifies whether an author's full given name(s) shall be shortened to initial(s) // // 13. output: if the total number of authors is greater than the given number (integer >= 1), only the number of authors given in (14) will be included in the citation along with the string given in (15); keep empty if all authors shall be returned // 14. output: number of authors (integer >= 1) that is included in the citation if the total number of authors is greater than the number given in (13); keep empty if not applicable // 15. output: string that's appended to the number of authors given in (14) if the total number of authors is greater than the number given in (13); the actual number of authors can be printed by including '__NUMBER_OF_AUTHORS__' (without quotes) within the string // // 16. output: boolean value that specifies whether the re-ordered string shall be returned with higher ASCII chars HTML encoded $personString = reArrangeAuthorContents($personString, // 1. true, // 2. $betweenNamesDelim, // 3. "; ", // 4. "; ", // 5. $nameGivenDelim, // 6. ", ", // 7. ", ", // 8. $newBetweenGivensDelim, // 9. false, // 10. false, // 11. true, // 12. "", // 13. "", // 14. "", // 15. false); // 16. $betweenNamesDelim = "/\s*;\s*/"; } $nameArray = array(); if (!preg_match("#^/.*/$#", $betweenNamesDelim)) $betweenNamesDelim = "/" . $betweenNamesDelim . "/"; // add search pattern delimiters $nameArray = preg_split($betweenNamesDelim, $personString, -1, PREG_SPLIT_NO_EMPTY); // get a list of all authors/editors return $nameArray; } // ------------------------------------------------------------------------------------------------------------------- // Map CQL indexes to refbase field names: function mapCQLIndexes() { // TODO: - add support for the OAI indexes 'oai.identifier' and 'oai.datestamp' // - the CQL indexes 'creationDate' and 'lastModificationDate' // contain both date & time info so this needs to be parsed into two // refbase fields (which isn't done yet!) // - if no context set & index name are given in the query, we should search // the user's preferred list of "main fields" by default! (cql.serverChoice) $indexNamesArray = array("dc.creator" => "author", // "CQL context_set.index_name" => "refbase field name" "dc.title" => "title", "dc.date" => "year", "dc.language" => "language", "dc.description" => "abstract", "dc.contributor" => "editor", "dc.subject" => "keywords", "dc.format" => "medium", "dc.type" => "type", "dc.publisher" => "publisher", "dc.coverage" => "area", // "bath.name" => "author", // "bath.topicalSubject" => "keywords", "bath.isbn" => "isbn", "bath.issn" => "issn", "bath.corporateName" => "corporate_author", "bath.conferenceName" => "conference", "bath.notes" => "notes", "rec.identifier" => "serial", "rec.creationDate" => "created_date-created_time", // see TODO note above "rec.creationAgentName" => "created_by", "rec.lastModificationDate" => "modified_date-modified_time", // see TODO note above "rec.lastModificationAgentName" => "modified_by", "bib.citekey" => "cite_key", "oai.identifier" => "serial", // "oai.datestamp" => "modified_date-modified_time", // see TODO note above (same as 'rec.lastModificationDate') "cql.serverChoice" => "keywords", // TODO: the special index 'main_fields' should resolve to 'cql.serverChoice', and that, in turn, should resolve to the user's preferred list of "main fields"; // alternatively, function 'parseCQL()' could map 'main_fields' to the user's preferred list of "main fields" -- and 'cql.serverChoice' would just resolve to a single field (as specified here) "main_fields" => "main_fields", // NOTE: the special index 'main_fields' currently only works for OpenSearch search suggestions, otherwise we'll fall back to 'cql.serverChoice' "author" => "author", // for indexes that have no public context set we simply accept refbase field names "title" => "title", "year" => "year", "publication" => "publication", "abbrev_journal" => "abbrev_journal", "volume" => "volume", "issue" => "issue", "pages" => "pages", "address" => "address", "corporate_author" => "corporate_author", "keywords" => "keywords", "abstract" => "abstract", "publisher" => "publisher", "place" => "place", "editor" => "editor", "language" => "language", "summary_language" => "summary_language", "orig_title" => "orig_title", "series_editor" => "series_editor", "series_title" => "series_title", "abbrev_series_title" => "abbrev_series_title", "series_volume" => "series_volume", "series_issue" => "series_issue", "edition" => "edition", "issn" => "issn", "isbn" => "isbn", "medium" => "medium", "area" => "area", "expedition" => "expedition", "conference" => "conference", "notes" => "notes", "approved" => "approved", "location" => "location", "call_number" => "call_number", "serial" => "serial", "type" => "type", "thesis" => "thesis", "file" => "file", "url" => "url", "doi" => "doi", "contribution_id" => "contribution_id", "online_publication" => "online_publication", "online_citation" => "online_citation", "created_date-created_time" => "created_date-created_time", // see TODO note above "created_by" => "created_by", "modified_date-modified_time" => "modified_date-modified_time", // see TODO note above "modified_by" => "modified_by", "orig_record" => "orig_record", "marked" => "marked", // in case of 'sru.php', querying for user-specific fields requires that the 'x-...authenticationToken' is given in the SRU query "copy" => "copy",// for 'opensearch.php', querying of user-specific fields will only work with a user being logged in "selected" => "selected", "user_keys" => "user_keys", "user_notes" => "user_notes", "user_file" => "user_file", "user_groups" => "user_groups", "related" => "related", "cite_key" => "cite_key" // currently, only the user-specific 'cite_key' field can be queried by every user using 'sru.php' ); return $indexNamesArray; } // ------------------------------------------------------------------------------------------------------------------- // Map SRU/W diagnostic numbers to their corresponding messages: // Spec: , // function mapSRWDiagnostics() { $diagMessagesArray = array(1 => "General system error", // Details: Debugging information (traceback) 2 => "System temporarily unavailable", 3 => "Authentication error", 4 => "Unsupported operation", 5 => "Unsupported version", // Details: Highest version supported 6 => "Unsupported parameter value", // Details: Name of parameter 7 => "Mandatory parameter not supplied", // Details: Name of missing parameter 8 => "Unsupported Parameter", // Details: Name of the unsupported parameter 10 => "Query syntax error", 15 => "Unsupported context set", // Details: URI or short name of context set 16 => "Unsupported index", // Details: Name of index 24 => "Unsupported combination of relation and term", 36 => "Term in invalid format for index or relation", 39 => "Proximity not supported", 50 => "Result sets not supported", 61 => "First record position out of range", 64 => "Record temporarily unavailable", 65 => "Record does not exist", 66 => "Unknown schema for retrieval", // Details: Schema URI or short name (of the unsupported one) 67 => "Record not available in this schema", // Details: Schema URI or short name 68 => "Not authorised to send record", 69 => "Not authorised to send record in this schema", 70 => "Record too large to send", // Details: Maximum record size 71 => "Unsupported record packing", 72 => "XPath retrieval unsupported", 80 => "Sort not supported", 110 => "Stylesheets not supported" ); return $diagMessagesArray; } // ------------------------------------------------------------------------------------------------------------------- // Map refbase types to the corresponding eprint/resource types suggested for Simple Dublin Core[1]: // [1]: // for mappings marked with (*), the above article doesn't offer a type that sufficiently matches the refbase type function mapDCTypes() { $dcTypesArray = array("Journal Article" => "JournalArticle", "Abstract" => "Abstract", // (*) "Book Chapter" => "BookChapter", "Book Whole" => "Book", "Conference Article" => "ConferencePaper", "Conference Volume" => "ConferenceProceedings", "Journal" => "Journal", // (*) "Magazine Article" => "MagazineArticle", // (*) "Manual" => "Manual", // (*) "Manuscript" => "Preprint", "Map" => "Map", // (*) "Miscellaneous" => "Other", "Newspaper Article" => "NewsArticle", "Patent" => "Patent", // (*) "Report" => "TechnicalReport", "Software" => "Software", // (*) // "" => "ConferencePoster", // "" => "InCollection", // "" => "OnlineJournalArticle", "Thesis" => "Thesis" // since refbase currently doesn't use a 'Thesis' type, this has to be dealt with in the calling function ); return $dcTypesArray; } // -------------------------------------------------------------------- ?>