// 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/include.inc.php // Repository: $HeadURL: file:///svn/p/refbase/code/branches/bleeding-edge/includes/include.inc.php $ // Author(s): Matthias Steffens // // Created: 16-Apr-02, 10:54 // Modified: $Date: 2018-02-16 21:28:11 +0000 (Fri, 16 Feb 2018) $ // $Author: karnesky $ // $Revision: 1420 $ // This file contains important // functions that are shared // between all scripts. // Incorporate some include files: include 'initialize/db.inc.php'; // 'db.inc.php' is included to hide username and password include 'initialize/ini.inc.php'; // include common variables // include transliteration tables: include 'includes/transtab_unicode_ascii.inc.php'; // include unicode -> ascii transliteration table include 'includes/transtab_latin1_ascii.inc.php'; // include latin1 -> ascii transliteration table include 'includes/transtab_unicode_latin1.inc.php'; // include unicode -> latin1 transliteration table include 'includes/transtab_unicode_refbase.inc.php'; // include unicode -> refbase transliteration table if ($contentTypeCharset == "UTF-8") // variable '$contentTypeCharset' is defined in 'ini.inc.php' include_once 'includes/transtab_unicode_charset.inc.php'; // include unicode character case conversion tables else // we assume "ISO-8859-1" by default include_once 'includes/transtab_latin1_charset.inc.php'; // include latin1 character case conversion tables // -------------------------------------------------------------------- // Untaint user data: function clean($input, $maxlength) { $input = substr($input, 0, $maxlength); $input = EscapeShellCmd($input); return ($input); } // -------------------------------------------------------------------- // Start a session: function start_session($updateUserFormatsStylesTypesPermissions) { global $databaseBaseURL; // these variables are defined in 'ini.inc.php' global $defaultMainFields; global $filesBaseDir; global $filesBaseURL; global $loginEmail; global $loginUserID; global $loginFirstName; global $loginLastName; global $abbrevInstitution; global $lastLogin; global $referer; // '$referer' is made globally available from within this function global $connection; // Initialize the session: if (!isset($_SESSION["sessionID"])) { // Ensure that cookies are enabled: if (ini_get('session.use_cookies') == 0) // if 'session.use_cookies' is OFF for the current directory ini_set('session.use_cookies', 1); // enable storage of sessions within cookies session_start(); $sessionID = session_id(); // get the current session ID if (!empty($sessionID)) saveSessionVariable("sessionID", $sessionID); } // Set the system's locale information: list($systemLocaleCollate, $systemLocaleCType) = setSystemLocale(); // Set the default timezone used by all date/time functions // Note: The 'date_default_timezone_set/date_default_timezone_get' functions are available since PHP 5.1.0 if (function_exists("date_default_timezone_set") && function_exists("date_default_timezone_get")) @date_default_timezone_set(@date_default_timezone_get()); // NOTE: Upon first connection to the MySQL server, function 'connectToMySQLDatabase()' will query the // MySQL server for the MySQL version and save it to a session variable // Extract session variables (only necessary if register globals is OFF!): if (isset($_SESSION['loginEmail'])) { $loginEmail = $_SESSION['loginEmail']; $loginUserID = $_SESSION['loginUserID']; $loginFirstName = $_SESSION['loginFirstName']; $loginLastName = $_SESSION['loginLastName']; $abbrevInstitution = $_SESSION['abbrevInstitution']; $lastLogin = $_SESSION['lastLogin']; } elseif ($updateUserFormatsStylesTypesPermissions) { // If the user isn't logged in we set the available export formats, citation styles, document types and permissions to // the defaults which are specified in the 'formats', 'styles', 'types' and 'user_permissions' tables for 'user_id = 0'. // (a 'user_id' of zero is used within these tables to indicate the default settings if the user isn't logged in) // NOTE: As an exception, for anyone who isn't logged in, we don't load the default number of records from option // 'records_per_page' in table 'user_options', but instead use the value given in variable '$defaultNumberOfRecords' // in 'ini.inc.php'. Similarly, if the user isn't logged in, the list of "main fields" is taken from variable // '$defaultMainFields' in 'ini.inc.php' and not from option 'main_fields' in table 'user_options. Same holds true // for variable '$autoCompleteUserInput' vs. option 'show_auto_completions'. // Get all export formats that were selected by the admin to be visible if a user isn't logged in // and (if some formats were found) save them as semicolon-delimited string to the session variable 'user_export_formats': getVisibleUserFormatsStylesTypes(0, "format", "export"); // Get all citation formats that were selected by the admin to be visible if a user isn't logged in // and (if some formats were found) save them as semicolon-delimited string to the session variable 'user_cite_formats': getVisibleUserFormatsStylesTypes(0, "format", "cite"); // Get all citation styles that were selected by the admin to be visible if a user isn't logged in // and (if some styles were found) save them as semicolon-delimited string to the session variable 'user_styles': getVisibleUserFormatsStylesTypes(0, "style", ""); // Get all document types that were selected by the admin to be visible if a user isn't logged in // and (if some types were found) save them as semicolon-delimited string to the session variable 'user_types': getVisibleUserFormatsStylesTypes(0, "type", ""); // Get the user permissions for the current user // and save all allowed user actions as semicolon-delimited string to the session variable 'user_permissions': getPermissions(0, "user", true); // Get the default view for the current user // and save it to the session variable 'userDefaultView': getDefaultView(0); // Get the default number of records per page preferred by the current user // and save it to the session variable 'userRecordsPerPage': getDefaultNumberOfRecords(0); // Get the user's preference for displaying auto-completions // and save it to the session variable 'userAutoCompletions': getPrefAutoCompletions(0); // Get the list of "main fields" for the current user // and save the list of fields as comma-delimited string to the session variable 'userMainFields': getMainFields(0); } else // if ($updateUserFormatsStylesTypesPermissions == false) { // The scripts 'error.php', 'install.php' & 'update.php' use 'start_session(false);' so that they execute without errors // when there isn't any database yet. However, function 'buildQuickSearchElements()' (which builds the "Quick Search" form // in the page header) requires the session variable 'userMainFields' to be present. So we take the list of "main fields" // directly from the global variable '$defaultMainFields' and save it as session variable (we cannot use function // 'getMainFields()' here since this would require database access): if (!isset($_SESSION['userMainFields'])) saveSessionVariable("userMainFields", $defaultMainFields); } // Set the referrer: if (isset($_REQUEST['referer']) AND !empty($_REQUEST['referer']) AND strpos($_REQUEST['referer'], '://')===false) $referer = $_REQUEST['referer']; // get the referring URL from the superglobal '$_REQUEST' variable (if any) elseif (isset($_SESSION['referer']) AND !empty($_SESSION['referer'])) { $referer = $_SESSION['referer']; // get the referring URL from the superglobal '$_SESSION' variable (if any) deleteSessionVariable("referer"); } elseif (isset($_SERVER['HTTP_REFERER']) AND !empty($_SERVER['HTTP_REFERER'])) $referer = $_SERVER['HTTP_REFERER']; // get the referring URL from the superglobal '$_SERVER' variable (if any) else // as an example, the referrer won't be set if a user clicked on a URL of type 'show.php?record=12345' within an email announcement $referer = "index.php"; // if all other attempts fail, we'll re-direct to the main page // Verify important variables from 'ini.inc.php': // - Ensure that the given paths/URLs end with a slash: $databaseBaseURL = checkPath($databaseBaseURL, "URL"); $filesBaseDir = checkPath($filesBaseDir); $filesBaseURL = checkPath($filesBaseURL, "URL"); } // -------------------------------------------------------------------- // Create a new session variable: function saveSessionVariable($sessionVariableName, $sessionVariableContents) { // since PHP 4.1.0 or greater, adding variables directly to the '$_SESSION' variable // will register a session variable regardless whether register globals is ON or OFF! $_SESSION[$sessionVariableName] = $sessionVariableContents; } // -------------------------------------------------------------------- // Remove a session variable: function deleteSessionVariable($sessionVariableName) { if (ini_get('register_globals') == 1) // register globals is ON for the current directory session_unregister($sessionVariableName); // clear the specified session variable else // register globals is OFF for the current directory unset($_SESSION[$sessionVariableName]); // clear the specified session variable } // -------------------------------------------------------------------- // Connect to the MySQL database: // TODO: I18n function connectToMySQLDatabase() { global $hostName; // these variables are specified in 'db.inc.php' global $username; global $password; global $databaseName; global $contentTypeCharset; // defined in 'ini.inc.php' global $connection; // If a connection parameter is not available, then use our own connection to avoid any locking problems if (!isset($connection)) { // (1) OPEN the database connection: // (variables are set by include file 'db.inc.php'!) if (!($connection = @ mysqli_connect($hostName, $username, $password, $databaseName))) if (mysqli_errno($connection) != 0) // this works around a stupid(?) behaviour of the Roxen webserver that returns 'errno: 0' on success! ?:-( showErrorMsg("The following error occurred while trying to connect to the host:"); // Get the MySQL version and save it to a session variable: if (!isset($_SESSION['mysqlVersion'])) saveSessionVariable("mysqlVersion", getMySQLversion()); // (2) Set the connection character set (if connected to MySQL 4.1.x or greater): // more info at if (isset($_SESSION['mysqlVersion']) AND ($_SESSION['mysqlVersion'] >= 4.1)) { // NOTE: "SET NAMES ..." requires MySQL 4.1 or greater if ($contentTypeCharset == "UTF-8") queryMySQLDatabase("SET NAMES utf8"); // set the character set for this connection to 'utf8' else queryMySQLDatabase("SET NAMES latin1"); // by default, we establish a 'latin1' connection } } } // -------------------------------------------------------------------- // Query the MySQL database: // NOTE: This function requires an established MySQL $connection // TODO: I18n function queryMySQLDatabase($query) { global $connection; global $client; // (3) RUN the query on the database through the connection: if (!($result = @ mysqli_query($connection, $query))) if (mysqli_errno($connection) != 0) // this works around a stupid(?) behaviour of the Roxen webserver that returns 'errno: 0' on success! ?:-( { if (isset($client) AND preg_match("/^cli/i", $client)) // if the query originated from a command line client such as the "refbase" CLI client ("cli-refbase-1.0") showErrorMsg("Your query: " . encodeHTML($query) . "\n\ncaused the following error:"); else showErrorMsg("Your query: '" . $query . "' caused the following error:"); } return $result; } // -------------------------------------------------------------------- // Disconnect from the MySQL database: // TODO: I18n function disconnectFromMySQLDatabase() { global $connection; if (isset($connection)) // (5) CLOSE the database connection: if (!(mysqli_close($connection))) if (mysqli_errno($connection) != 0) // this works around a stupid(?) behaviour of the Roxen webserver that returns 'errno: 0' on success! ?:-( showErrorMsg("The following error occurred while trying to disconnect from the database:"); } // -------------------------------------------------------------------- // Get MySQL version: // NOTE: This function requires an established MySQL $connection function getMySQLversion() { // CONSTRUCT SQL QUERY: $query = "SELECT VERSION()"; $result = queryMySQLDatabase($query); // RUN the query on the database through the connection $row = mysqli_fetch_row($result); // fetch the current row into the array $row (it'll be always *one* row, but anyhow) $mysqlVersionString = $row[0]; // extract the contents of the first (and only) row (returned version string will be something like "4.0.20-standard" etc.) $mysqlVersion = preg_replace("/^(\d+\.\d+).+/", "\\1", $mysqlVersionString); // extract main version number (e.g. "4.0") from version string return $mysqlVersion; } // -------------------------------------------------------------------- // Get MySQL field info: // (i.e. fetch field (column) information from a given result resource; returns the // field property given in '$propertyName', else an array of all field properties; // see ) function getMySQLFieldInfo($result, $fieldOffset, $propertyName = "") { $fieldInfoArray = array(); // Get field (column) metadata: $fieldInfo = mysqli_fetch_field_direct($result, (int)$fieldOffset); // returns an object containing the field information // Copy object properties to an array: $fieldInfoArray["name"] = $fieldInfo->name; // column name $fieldInfoArray["table"] = $fieldInfo->table; // name of the table the column belongs to $fieldInfoArray["type"] = $fieldInfo->type; // the type of the column $fieldInfoArray["def"] = $fieldInfo->def; // default value of the column $fieldInfoArray["max_length"] = $fieldInfo->max_length; // maximum length of the column $fieldInfoArray["max_length"] = $fieldInfo->max_length; // maximum length of the column $fieldInfoArray["primary_key"] = $fieldInfo->primary_key; // 1 if the column is a primary key $fieldInfoArray["unique_key"] = $fieldInfo->unique_key; // 1 if the column is a unique key $fieldInfoArray["multiple_key"] = $fieldInfo->multiple_key; // 1 if the column is a non-unique key $fieldInfoArray["numeric"] = $fieldInfo->numeric; // 1 if the column is numeric $fieldInfoArray["blob"] = $fieldInfo->blob; // 1 if the column is a BLOB $fieldInfoArray["unsigned"] = $fieldInfo->unsigned; // 1 if the column is unsigned $fieldInfoArray["zerofill"] = $fieldInfo->zerofill; // 1 if the column is zero-filled if (!empty($propertyName) AND isset($fieldInfoArray[$propertyName])) return $fieldInfoArray[$propertyName]; else return $fieldInfoArray; } // -------------------------------------------------------------------- // Find out how many rows are available and (if there were rows found) seek to the current offset: // Note that this function will also (re-)assign values to the variables '$rowOffset', '$showRows', // '$rowsFound', '$previousOffset', '$nextOffset' and '$showMaxRow'. function seekInMySQLResultsToOffset($result, $rowOffset, $showRows, $displayType, $citeType) { // Find out how many rows are available: $rowsFound = @ mysqli_num_rows($result); if ($rowsFound > 0) // If there were rows found ... { // ... setup variables in order to facilitate "previous" & "next" browsing: // a) Set '$rowOffset' to zero if not previously defined, or if a wrong number (<=0) was given if (empty($rowOffset) || ($rowOffset <= 0) || ((($displayType != "Export") AND !($displayType == "Cite" AND (!preg_match("/^html$/i", $citeType)))) && ($showRows >= $rowsFound))) // the third condition is only necessary if '$rowOffset' gets embedded within the 'displayOptions' form (see function 'buildDisplayOptionsElements()' in 'include.inc.php') $rowOffset = 0; // Adjust the '$showRows' value if not previously defined, or if a wrong number (<=0 or float) was given if (empty($showRows) || ($showRows <= 0) || !preg_match("/^[0-9]+$/", $showRows)) $showRows = $_SESSION['userRecordsPerPage']; // get the default number of records per page preferred by the current user // Adjust '$rowOffset' if it's value exceeds the number of rows found: if ($rowOffset > ($rowsFound - 1)) { if ($rowsFound > $showRows) $rowOffset = ($rowsFound - $showRows); // start display at first record of last page to be displayed else $rowOffset = 0; // start display at the very first record } if (($displayType != "Export") AND !($displayType == "Cite" AND (!preg_match("/^html$/i", $citeType)))) // we have to exclude '$displayType=Export' here since, for export, '$rowOffset' must always point to the first row number in the result set that should be returned { // NOTE: The current value of '$rowOffset' is embedded as hidden tag within the 'displayOptions' form. By this, the current row offset can be re-applied // after the user pressed the 'Show'/'Hide' button within the 'displayOptions' form. But then, to avoid that browse links don't behave as expected, // we need to adjust the actual value of '$rowOffset' to an exact multiple of '$showRows': $offsetRatio = ($rowOffset / $showRows); if (!is_integer($offsetRatio)) // check whether the value of the '$offsetRatio' variable is not an integer { // if '$offsetRatio' is a float: $offsetCorrectionFactor = floor($offsetRatio); // get it's next lower integer if ($offsetCorrectionFactor != 0) $rowOffset = ($offsetCorrectionFactor * $showRows); // correct the current row offset to the closest multiple of '$showRows' *below* the current row offset else $rowOffset = 0; } } // b) The "Previous" page begins at the current offset LESS the number of rows per page $previousOffset = $rowOffset - $showRows; // c) The "Next" page begins at the current offset PLUS the number of rows per page $nextOffset = $rowOffset + $showRows; // d) Seek to the current offset mysqli_data_seek($result, $rowOffset); // move internal result pointer to the row number given in '$rowOffset' } else // set variables to zero in order to prevent 'Undefined variable...' messages when nothing was found ('$rowsFound = 0'): { $rowOffset = 0; $previousOffset = 0; $nextOffset = 0; } // Calculate the maximum result number on each page ('$showMaxRow' is required as parameter to the 'displayDetails()' function) if (($rowOffset + $showRows) < $rowsFound) $showMaxRow = ($rowOffset + $showRows); // maximum result number on each page else $showMaxRow = $rowsFound; // for the last results page, correct the maximum result number if necessary return array($result, $rowOffset, $showRows, $rowsFound, $previousOffset, $nextOffset, $showMaxRow); } // -------------------------------------------------------------------- // Show error (prepares error output and redirects it to 'error.php' which displays the error message): // TODO: I18n function showErrorMsg($headerMsg) { global $client; global $connection; $errorNo = mysqli_errno($connection); $errorMsg = mysqli_error($connection); if (preg_match("/^cli/i", $client)) // if the query originated from a command line client such as the "refbase" CLI client ("cli-refbase-1.0") // note that we also HTML encode the '$errorMsg' for CLI clients since a malicious user could use the client parameter to perform a cross-site scripting (XSS) attack echo $headerMsg . "\n\nError " . $errorNo . ": " . encodeHTML($errorMsg) . "\n\n"; else // in case of regular HTML output, '$errorMsg' gets HTML encoded in 'error.php' header("Location: error.php?errorNo=" . $errorNo . "&errorMsg=" . rawurlencode($errorMsg) . "&headerMsg=" . rawurlencode($headerMsg)); exit; } // -------------------------------------------------------------------- // Generate and return a message, and optionally save the message to a session variable: // Following optional variables can be passed with the '$message' and will be used for non-CLI clients: // - '$class' defines the name of the CSS class of the span element that encloses the message // - '$flavour' is the (valid!) name of an HTML phrase element (such as "strong" or "em") that's wrapped around the message // - '$sessionVariable' is the name of the session variable (such as "HeaderString") to which the message shall be saved // - '$prefix' is a string that's added at the beginning of the generated message string // - '$suffix' is a string that's appended at the end of the generated message string function returnMsg($message, $class = "", $flavour = "", $sessionVariable = "", $prefix = "", $suffix = "") { global $client; // Because we now sanitize the header message, we no longer output it as // HTML /* if (preg_match("/^cli/i", $client)) // if the query originated from a command line client such as the "refbase" CLI client ("cli-refbase-1.0") { */ $fullMsg = $message . "\n\n"; // for CLI clients, we just echo the message text echo $fullMsg; /* } else // return an HTML-formatted message: { $fullMsg = $prefix; if (!empty($flavour)) $fullMsg .= '<' . $flavour . '>'; if (!empty($class)) $fullMsg .= '' . $message . ''; else $fullMsg .= $message; if (!empty($flavour)) $fullMsg .= ''; $fullMsg .= $suffix; if (!empty($sessionVariable)) saveSessionVariable($sessionVariable, $fullMsg); // write message to session variable } */ return $fullMsg; } // -------------------------------------------------------------------- // Show whether the user is logged in or not: // TODO: I18n function showLogin() { global $loginEmail; global $loginWelcomeMsg; global $loginFirstName; global $loginLastName; global $abbrevInstitution; global $loginUserID; global $loginStatus; global $loginLinks; global $adminLoginEmail; // ('$adminLoginEmail' is specified in 'ini.inc.php') global $loc; // '$loc' is made globally available in 'core.php' // $referer = $_SERVER["REQUEST_URI"]; // 'REQUEST_URI' does only seem to work for GET requests (but not for POST requests!) ?:-/ // so, as a workaround, we build an appropriate query string from scratch (which will also work for POST requests): // --- BEGIN WORKAROUND --- global $formType; global $displayType; global $queryURL; global $showQuery; global $showLinks; global $showRows; global $rowOffset; global $citeStyle; global $citeOrder; global $orderBy; global $recordAction; global $serialNo; global $headerMsg; global $errorNo; global $errorMsg; // Get the path to the currently executing script, relative to the document root: $scriptURL = scriptURL(); // Extract checkbox variable values from the request: if (isset($_REQUEST['marked'])) $recordSerialsArray = $_REQUEST['marked']; // extract the values of all checked checkboxes (i.e., the serials of all selected records) else $recordSerialsArray = ""; $recordSerialsString = ""; // initialize variable // join array elements: if (!empty($recordSerialsArray)) // the user did check some checkboxes $recordSerialsString = implode("&marked[]=", $recordSerialsArray); // prefix each record serial (except the first one) with "&marked[]=" $recordSerialsString = "&marked[]=" . $recordSerialsString; // prefix also the very first record serial with "&marked[]=" // based on the refering script we adjust the parameters that get included in the link: if (preg_match("#/(index|install|update|simple_search|advanced_search|sql_search|library_search|duplicate_manager|duplicate_search|opensearch|query_history|extract|users|user_details|user_receipt)\.php#i", $scriptURL)) $referer = $scriptURL; // we don't need to provide any parameters if the user clicked login/logout on the main page, the install/update page or any of the search pages (we just need // to re-locate back to these pages after successful login/logout). Logout on 'install.php', 'users.php', 'user_details.php' or 'user_receipt.php' will redirect to 'index.php'. elseif (preg_match("#/user_options\.php#i", $scriptURL)) $referer = $scriptURL . "?" . "userID=" . $loginUserID; elseif (preg_match("#/(record|receipt)\.php#i", $scriptURL)) $referer = $scriptURL . "?" . "recordAction=" . $recordAction . "&serialNo=" . $serialNo . "&headerMsg=" . rawurlencode($headerMsg); elseif (preg_match("#/error\.php#i", $scriptURL)) $referer = $scriptURL . "?" . "errorNo=" . $errorNo . "&errorMsg=" . rawurlencode($errorMsg) . "&headerMsg=" . rawurlencode($headerMsg); else $referer = $scriptURL . "?" . "formType=" . "sqlSearch" . "&submit=" . $displayType . "&headerMsg=" . rawurlencode($headerMsg) . "&sqlQuery=" . $queryURL . "&showQuery=" . $showQuery . "&showLinks=" . $showLinks . "&showRows=" . $showRows . "&rowOffset=" . $rowOffset . $recordSerialsString . "&citeStyle=" . rawurlencode($citeStyle) . "&citeOrder=" . $citeOrder . "&orderBy=" . rawurlencode($orderBy); // --- END WORKAROUND ----- // Is the user logged in? if (isset($_SESSION['loginEmail'])) { $loginStatus = $loc["Welcome"]; $loginWelcomeMsg = "" . encodeHTML($loginFirstName) . " " . encodeHTML($loginLastName) . "!"; if ($loginEmail == $adminLoginEmail) $loginStatus .= " " . $loc["Admin"] . ""; $loginLinks = ""; if ($loginEmail == $adminLoginEmail) // if the admin is logged in, add the 'Add User' & 'Manage Users' links: { $loginLinks .= "Add User  |  "; $loginLinks .= "Manage Users  |  "; } else // if a normal user is logged in, we add the 'My Refs' and 'Options' links instead: { $loginLinks .= "" . $loc["MyRefs"] . "  |  "; if (isset($_SESSION['user_permissions']) AND preg_match("/allow_modify_options/", $_SESSION['user_permissions'])) // if the 'user_permissions' session variable contains 'allow_modify_options'... // ... include a link to 'user_receipt.php': $loginLinks .= "" . $loc["Options"] . "  |  "; } $loginLinks .= "" . $loc["Logout"] . ""; } else { if (preg_match("#.*(record|import[^.]*)\.php#i", $scriptURL)) $loginStatus = "" . $loc["Warning_LoginToSubmitForm"] . "!"; else $loginStatus = ""; $loginWelcomeMsg = ""; $loginLinks = "" . $loc["Login"] . ""; } // Although the '$referer' variable gets included as GET parameter above, we'll also save the variable as session variable: // (this should help re-directing to the correct page if a user called 'user_login/logout.php' manually, i.e., without parameters) saveSessionVariable("referer", $referer); } // -------------------------------------------------------------------- // Enable 'accesskey' attribute for the specified link/form element: // '$type' must be either "attribute" or "title", and '$key' must be the // name of an array key from variable '$accessKeys' (in 'ini.inc.php') function addAccessKey($type, $key) { global $accessKeys; // defined in 'ini.inc.php' $accessKeyString = ""; if (isset($accessKeys) AND (!empty($accessKeys[$key]) OR ($accessKeys[$key] == "0"))) { if ($type == "attribute") // add 'accesskey' attribute (like ' accesskey="h"') to the specified link or form element $accessKeyString = " accesskey=\"" . $accessKeys[$key] . "\""; elseif ($type == "title") // add access key hint (like ' [ctrl-h]') to the title attribute value of the specified link or form element $accessKeyString = " [ctrl-" . $accessKeys[$key] . "]"; } return $accessKeyString; } // -------------------------------------------------------------------- // Get the 'user_id' for the record entry in table 'auth' whose email matches that in '$emailAddress': function getUserID($emailAddress) { global $tableAuth; // defined in 'db.inc.php' connectToMySQLDatabase(); // CONSTRUCT SQL QUERY: $query = "SELECT user_id FROM $tableAuth WHERE email = " . quote_smart($emailAddress); $result = queryMySQLDatabase($query); // RUN the query on the database through the connection $row = mysqli_fetch_array($result); return($row["user_id"]); } // -------------------------------------------------------------------- // ADD RECORDS // Adds record(s) to the database (i.e., add one or more row entries to MySQL table 'refs'): // Notes: - the function will return the serial number(s) of all newly created record(s) in an array structure // - structure of '$importDataArray' (sample values given in "..."): // array( // ['type'] => "refbase" // mandatory; indicates the array format of the 'records' element (currently only "refbase" is recognized, a standardized "bibliophile" format may be provided later on) // ['version'] => "1.0" // mandatory; the version of the given array structure // ['records'] => array( // mandatory; array of arrays containing the records & field data that should be imported; the sub-array element key must correspond to a refbase database field name from table 'refs' // [0] => array( // first record // [author] => "..." // - contents of 'author' field // [title] => "..." // - contents of 'title' field // ... // ) // [1] => array( // second record // [author] => "..." // - contents of 'author' field // [title] => "..." // - contents of 'title' field // ... // ) // ... // ) // ['creator'] => "http://refbase.net" // optional; the (preferably unique) name of the calling script/importer, use an URI if possible // ['author'] => "Matthias Steffens" // optional; the name of the person who developed the script/importer and/or who can be contacted in case of problems // ['contact'] => "refbase@extracts.de" // optional; the contact address of the person specified under 'author', use an email address if possible // ['options'] => array( // optional; array with settings that control the behaviour of the 'addRecords()' function, currently there's only one option: // [prefix_call_number] => "true" // if "true", any 'call_number' string will be prefixed with the correct call number prefix of the currently logged-in user (e.g. 'IPÖ @ msteffens @ ') // ) // ) function addRecords($importDataArray) { global $loginUserID; global $tableRefs, $tableUserData; // defined in 'db.inc.php' global $connection; connectToMySQLDatabase(); $recognizedArrayFormatsAndVersions = array('refbase' => array("1.0")); // for each recognized format, this array lists its format identifier as element key and an array of known versions as element value $serialNumbersArray = array(); // initialize array variable which will hold the serial numbers of all imported records // Verify the structure of the '$importDataArray': if (!empty($importDataArray['type']) AND !empty($importDataArray['version']) AND !empty($importDataArray['records'])) // the array elements 'type', 'version' and 'records' are mandatory and must not be empty { // Currently, we only support the default "refbase" array structure in its initial version ("1.0") (support for other more generalized array formats may come later) if (($importDataArray['type'] == "refbase") AND (in_array($importDataArray['version'], $recognizedArrayFormatsAndVersions['refbase']))) { $recordsArray = $importDataArray['records']; // get the array of records that shall be imported // First, setup some required variables: // Get the current date (e.g. '2003-12-31'), time (e.g. '23:59:49') and user name & email address (e.g. 'Matthias Steffens (refbase@extracts.de)'): // note that we use the same time stamp for ALL imported records (so that users can easily identify all records belonging to one import action) list ($currentDate, $currentTime, $currentUser) = getCurrentDateTimeUser(); // LOOP OVER EACH RECORD: foreach ($recordsArray as $recordData) // for each record... { // Initialize some variables (in order to avoid "undefined index" messages when the particular array elements are not available): if (isset($recordData['author'])) $author = $recordData['author']; else $author = ""; if (isset($recordData['pages'])) $pages = $recordData['pages']; else $pages = ""; if (isset($recordData['volume'])) $volume = $recordData['volume']; else $volume = ""; if (isset($recordData['series_volume'])) $seriesVolume = $recordData['series_volume']; else $seriesVolume = ""; // Assign correct values to the calculation fields 'first_author', 'author_count', 'first_page', 'volume_numeric' and 'series_volume_numeric': list ($firstAuthor, $authorCount, $firstPage, $volumeNumeric, $seriesVolumeNumeric) = generateCalculationFieldContent($author, $pages, $volume, $seriesVolume); // CONSTRUCT SQL QUERY: // INSERT - construct a query to add data as new record $queryRefs = ""; // note: we'll prefix "INSERT INTO $tableRefs SET " *after* we've parsed all array elements to trap the case that none of the array elements did contain any data if (!empty($recordData['author'])) $queryRefs .= "author = " . quote_smart($recordData['author']) . ", "; if (!empty($firstAuthor)) $queryRefs .= "first_author = " . quote_smart($firstAuthor) . ", "; if (!empty($authorCount)) $queryRefs .= "author_count = " . quote_smart($authorCount) . ", "; if (!empty($recordData['title'])) $queryRefs .= "title = " . quote_smart($recordData['title']) . ", "; if (!empty($recordData['year'])) $queryRefs .= "year = " . quote_smart($recordData['year']) . ", "; if (!empty($recordData['publication'])) $queryRefs .= "publication = " . quote_smart($recordData['publication']) . ", "; if (!empty($recordData['abbrev_journal'])) $queryRefs .= "abbrev_journal = " . quote_smart($recordData['abbrev_journal']) . ", "; if (!empty($recordData['volume'])) $queryRefs .= "volume = " . quote_smart($recordData['volume']) . ", "; if (!empty($volumeNumeric)) $queryRefs .= "volume_numeric = " . quote_smart($volumeNumeric) . ", "; if (!empty($recordData['issue'])) $queryRefs .= "issue = " . quote_smart($recordData['issue']) . ", "; if (!empty($recordData['pages'])) $queryRefs .= "pages = " . quote_smart($recordData['pages']) . ", "; if (!empty($firstPage)) $queryRefs .= "first_page = " . quote_smart($firstPage) . ", "; if (!empty($recordData['address'])) $queryRefs .= "address = " . quote_smart($recordData['address']) . ", "; if (!empty($recordData['corporate_author'])) $queryRefs .= "corporate_author = " . quote_smart($recordData['corporate_author']) . ", "; if (!empty($recordData['keywords'])) $queryRefs .= "keywords = " . quote_smart($recordData['keywords']) . ", "; if (!empty($recordData['abstract'])) $queryRefs .= "abstract = " . quote_smart($recordData['abstract']) . ", "; if (!empty($recordData['publisher'])) $queryRefs .= "publisher = " . quote_smart($recordData['publisher']) . ", "; if (!empty($recordData['place'])) $queryRefs .= "place = " . quote_smart($recordData['place']) . ", "; if (!empty($recordData['editor'])) $queryRefs .= "editor = " . quote_smart($recordData['editor']) . ", "; if (!empty($recordData['language'])) $queryRefs .= "language = " . quote_smart($recordData['language']) . ", "; if (!empty($recordData['summary_language'])) $queryRefs .= "summary_language = " . quote_smart($recordData['summary_language']) . ", "; if (!empty($recordData['orig_title'])) $queryRefs .= "orig_title = " . quote_smart($recordData['orig_title']) . ", "; if (!empty($recordData['series_editor'])) $queryRefs .= "series_editor = " . quote_smart($recordData['series_editor']) . ", "; if (!empty($recordData['series_title'])) $queryRefs .= "series_title = " . quote_smart($recordData['series_title']) . ", "; if (!empty($recordData['abbrev_series_title'])) $queryRefs .= "abbrev_series_title = " . quote_smart($recordData['abbrev_series_title']) . ", "; if (!empty($recordData['series_volume'])) $queryRefs .= "series_volume = " . quote_smart($recordData['series_volume']) . ", "; if (!empty($seriesVolumeNumeric)) $queryRefs .= "series_volume_numeric = " . quote_smart($seriesVolumeNumeric) . ", "; if (!empty($recordData['series_issue'])) $queryRefs .= "series_issue = " . quote_smart($recordData['series_issue']) . ", "; if (!empty($recordData['edition'])) $queryRefs .= "edition = " . quote_smart($recordData['edition']) . ", "; if (!empty($recordData['issn'])) $queryRefs .= "issn = " . quote_smart($recordData['issn']) . ", "; if (!empty($recordData['isbn'])) $queryRefs .= "isbn = " . quote_smart($recordData['isbn']) . ", "; if (!empty($recordData['medium'])) $queryRefs .= "medium = " . quote_smart($recordData['medium']) . ", "; if (!empty($recordData['area'])) $queryRefs .= "area = " . quote_smart($recordData['area']) . ", "; if (!empty($recordData['expedition'])) $queryRefs .= "expedition = " . quote_smart($recordData['expedition']) . ", "; if (!empty($recordData['conference'])) $queryRefs .= "conference = " . quote_smart($recordData['conference']) . ", "; // the 'location' and 'call_number' fields are handled below if (!empty($recordData['approved'])) $queryRefs .= "approved = " . quote_smart($recordData['approved']) . ", "; if (!empty($recordData['file'])) $queryRefs .= "file = " . quote_smart($recordData['file']) . ", "; // the 'serial' field is handled below if (!empty($recordData['orig_record'])) $queryRefs .= "orig_record = " . quote_smart($recordData['orig_record']) . ", "; if (!empty($recordData['type'])) $queryRefs .= "type = " . quote_smart($recordData['type']) . ", "; if (!empty($recordData['thesis'])) $queryRefs .= "thesis = " . quote_smart($recordData['thesis']) . ", "; if (!empty($recordData['notes'])) $queryRefs .= "notes = " . quote_smart($recordData['notes']) . ", "; if (!empty($recordData['url'])) $queryRefs .= "url = " . quote_smart($recordData['url']) . ", "; if (!empty($recordData['doi'])) $queryRefs .= "doi = " . quote_smart($recordData['doi']) . ", "; if (!empty($recordData['contribution_id'])) $queryRefs .= "contribution_id = " . quote_smart($recordData['contribution_id']) . ", "; if (!empty($recordData['online_publication'])) $queryRefs .= "online_publication = " . quote_smart($recordData['online_publication']) . ", "; if (!empty($recordData['online_citation'])) $queryRefs .= "online_citation = " . quote_smart($recordData['online_citation']) . ", "; if (!empty($queryRefs)) // go ahead, if some array elements did contain data { // we only honour the 'call_number' string if some other record data were passed as well: // // if the 'prefix_call_number' option is set to "true", any 'call_number' string will be prefixed with // the correct call number prefix of the currently logged-in user (e.g. 'IPÖ @ msteffens @ '): if ((isset($_SESSION['loginEmail'])) AND (isset($importDataArray['options']['prefix_call_number'])) AND ($importDataArray['options']['prefix_call_number'] == "true")) { $callNumberPrefix = getCallNumberPrefix(); // build a correct call number prefix for the currently logged-in user (e.g. 'IPÖ @ msteffens') if (!empty($recordData['call_number'])) $queryRefs .= "call_number = " . quote_smart($callNumberPrefix . " @ " . $recordData['call_number']) . ", "; // add call number prefix to 'call_number' string else $queryRefs .= "call_number = " . quote_smart($callNumberPrefix . " @ ") . ", "; // similar to the GUI behaviour, we'll also add a call number prefix if the 'call_number' string is empty } else { if (!empty($recordData['call_number'])) $queryRefs .= "call_number = " . quote_smart($recordData['call_number']) . ", "; } // if no specific cite key exists in '$recordData', any existing 'call_number' string gets also copied to the // user-specific 'cite_key' field (which will ensure that this original call number/cite key is retained as // cite key upon export); however, note that (depending on the user's settings) the cite key may get modified // or regenerated by function 'generateCiteKey()' below if (isset($_SESSION['loginEmail']) AND !empty($recordData['call_number']) AND empty($recordData['cite_key'])) $recordData['cite_key'] = $recordData['call_number']; // for the 'location' field, we accept input from the '$recordData', // but if no data were given, we'll add the currently logged-in user to the 'location' field: if (!empty($recordData['location'])) $queryRefs .= "location = " . quote_smart($recordData['location']) . ", "; elseif (isset($_SESSION['loginEmail'])) $queryRefs .= "location = " . quote_smart($currentUser) . ", "; $queryRefs .= "serial = NULL, "; // inserting 'NULL' into an auto_increment PRIMARY KEY attribute allocates the next available key value // we accept custom values for the *date/*time/*by fields if they are in correct format (*date: 'YYYY-MM-DD'; *time: 'HH:MM:SS'; *by: 'string'), // otherwise we'll use the current date & time as well as the currently logged-in user name & email address: if (!empty($recordData['created_by'])) $queryRefs .= "created_by = " . quote_smart($recordData['created_by']) . ", "; elseif (isset($_SESSION['loginEmail'])) $queryRefs .= "created_by = " . quote_smart($currentUser) . ", "; if (!empty($recordData['created_date']) AND preg_match("/^\d{4}-\d{2}-\d{2}$/", $recordData['created_date'])) $queryRefs .= "created_date = " . quote_smart($recordData['created_date']) . ", "; else $queryRefs .= "created_date = " . quote_smart($currentDate) . ", "; if (!empty($recordData['created_time']) AND preg_match("/^\d{2}:\d{2}:\d{2}$/", $recordData['created_time'])) $queryRefs .= "created_time = " . quote_smart($recordData['created_time']) . ", "; else $queryRefs .= "created_time = " . quote_smart($currentTime) . ", "; if (!empty($recordData['modified_by'])) $queryRefs .= "modified_by = " . quote_smart($recordData['modified_by']) . ", "; elseif (isset($_SESSION['loginEmail'])) $queryRefs .= "modified_by = " . quote_smart($currentUser) . ", "; if (!empty($recordData['modified_date']) AND preg_match("/^\d{4}-\d{2}-\d{2}$/", $recordData['modified_date'])) $queryRefs .= "modified_date = " . quote_smart($recordData['modified_date']) . ", "; else $queryRefs .= "modified_date = " . quote_smart($currentDate) . ", "; if (!empty($recordData['modified_time']) AND preg_match("/^\d{2}:\d{2}:\d{2}$/", $recordData['modified_time'])) $queryRefs .= "modified_time = " . quote_smart($recordData['modified_time']) . ""; else $queryRefs .= "modified_time = " . quote_smart($currentTime); $queryRefs = "INSERT INTO $tableRefs SET " . $queryRefs; // finalize the query by prefixing it with the actual MySQL command // ADD RECORD: // RUN the query on the database through the connection: $result = queryMySQLDatabase($queryRefs); // Get the record id that was created: $serialNo = @ mysqli_insert_id($connection); // find out the unique ID number of the newly created record (Note: this function should be called immediately after the // SQL INSERT statement! After any subsequent query it won't be possible to retrieve the auto_increment identifier value for THIS record!) // ADD USER DATA: if (isset($_SESSION['loginEmail'])) { // Note: At the moment, the record in table 'user_data' will be always created for the currently logged-in user, // i.e. we don't try to match any custom data given in the 'location' field with users from table 'users' // in order to set the 'user_id' in table 'user_data' accordingly // This is a stupid hack that maps the names of the '$recordData' 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($recordData); // Generate or extract the cite key for this record: $citeKey = generateCiteKey($formVars); // Construct SQL query: $queryUserData = "INSERT INTO $tableUserData SET "; if (!empty($recordData['marked']) AND preg_match("/^(no|yes)$/", $recordData['marked'])) $queryUserData .= "marked = " . quote_smart($recordData['marked']) . ", "; if (!empty($recordData['copy']) AND preg_match("/^(false|true|ordered|fetch)$/", $recordData['copy'])) $queryUserData .= "copy = " . quote_smart($recordData['copy']) . ", "; else $queryUserData .= "copy = 'true', "; // by default, 'false' would get inserted if omitted; we insert 'true' here in order to be consistent with manual record additions if (!empty($recordData['selected']) AND preg_match("/^(no|yes)$/", $recordData['selected'])) $queryUserData .= "selected = " . quote_smart($recordData['selected']) . ", "; if (!empty($recordData['user_keys'])) $queryUserData .= "user_keys = " . quote_smart($recordData['user_keys']) . ", "; if (!empty($recordData['user_notes'])) $queryUserData .= "user_notes = " . quote_smart($recordData['user_notes']) . ", "; if (!empty($recordData['user_file'])) $queryUserData .= "user_file = " . quote_smart($recordData['user_file']) . ", "; if (!empty($recordData['user_groups'])) $queryUserData .= "user_groups = " . quote_smart($recordData['user_groups']) . ", "; $queryUserData .= "cite_key = " . quote_smart($citeKey) . ", "; if (!empty($recordData['related'])) $queryUserData .= "related = " . quote_smart($recordData['related']) . ", "; $queryUserData .= "record_id = " . quote_smart($serialNo) . ", " . "user_id = " . quote_smart($loginUserID) . ", " // '$loginUserID' is provided as session variable . "data_id = NULL"; // inserting 'NULL' into an auto_increment PRIMARY KEY attribute allocates the next available key value // RUN the query on the database through the connection: $result = queryMySQLDatabase($queryUserData); } // Append this record's serial number to the array of imported record serials: $serialNumbersArray[] = $serialNo; } // else: '$recordData' did not contain any data, so we skip this record } // (END LOOP OVER EACH RECORD) } // else: unknown array structure, return an empty '$serialNumbersArray' } // else: couldn't verify structure of '$importDataArray', return an empty '$serialNumbersArray' return $serialNumbersArray; // return list of serial numbers of all imported records } // -------------------------------------------------------------------- // Assign correct values to the calculation fields 'first_author', 'author_count', 'first_page', 'volume_numeric' and 'series_volume_numeric': function generateCalculationFieldContent($author, $pages, $volume, $seriesVolume) { if (!empty($author)) { // Standardize contents of the author field (which will ensure correct sorting upon Citation output): // - shorten author's full given name(s) to initial(s) // - remove any delimiters (such as dots and/or whitespace) from author's initials // 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 $author = reArrangeAuthorContents($author, // 1. true, // 2. "/ *; */", // 3. "; ", // 4. "; ", // 5. "/ *, */", // 6. ", ", // 7. ", ", // 8. "", // 9. false, // 10. false, // 11. true, // 12. "", // 13. "", // 14. "", // 15. false); // 16. // 'first_author' field: $firstAuthor = preg_replace("/^([^;]+).*/i", "\\1", $author); // extract first author from 'author' field $firstAuthor = trim($firstAuthor); // remove leading & trailing whitespace (if any) $firstAuthor = preg_replace("/ *\(eds?\)$/i", "", $firstAuthor); // remove any existing editor info from the 'first_author' string, i.e., kill any trailing " (ed)" or " (eds)" // 'author_count' field: if (!preg_match("/;/", $author)) // if the 'author' field does NOT contain a ';' (which would delimit multiple authors) => single author $authorCount = "1"; // indicates a single author elseif (preg_match("/^[^;]+;[^;]+$/", $author)) // the 'author' field does contain exactly one ';' => two authors $authorCount = "2"; // indicates two authors elseif (preg_match("/^[^;]+;[^;]+;[^;]+/", $author)) // the 'author' field does contain at least two ';' => more than two authors $authorCount = "3"; // indicates three (or more) authors } else { $firstAuthor = ""; $authorCount = ""; } // 'first_page' field: if (!empty($pages)) { if (preg_match("/([0-9]+)/", $pages)) // if the 'pages' field contains any numeric value(s) $firstPage = preg_replace("/^[^0-9]*([0-9]+).*/i", "\\1", $pages); // extract first page from 'pages' field else $firstPage = ""; } else $firstPage = ""; // 'volume_numeric' field: if (!empty($volume)) { if (preg_match("/([0-9]+)/", $volume)) // if the 'volume' field contains any numeric value(s) $volumeNumeric = preg_replace("/^[^0-9]*([0-9]+).*/i", "\\1", $volume); // extract first number from 'volume' field else $volumeNumeric = ""; } else $volumeNumeric = ""; // 'series_volume_numeric' field: if (!empty($seriesVolume)) { if (preg_match("/([0-9]+)/", $seriesVolume)) // if the 'series_volume' field contains any numeric value(s) $seriesVolumeNumeric = preg_replace("/^[^0-9]*([0-9]+).*/i", "\\1", $seriesVolume); // extract first number from 'series_volume' field else $seriesVolumeNumeric = ""; } else $seriesVolumeNumeric = ""; return array($firstAuthor, $authorCount, $firstPage, $volumeNumeric, $seriesVolumeNumeric); } // -------------------------------------------------------------------- // Generic function that provides email sending capability: function sendEmail($emailRecipient, $emailSubject, $emailBody) { global $adminLoginEmail; // these variables are specified in 'ini.inc.php' global $contentTypeCharset; // Setup some additional headers: $emailHeaders = "From: " . $adminLoginEmail . "\n" . "Return-Path: " . $adminLoginEmail . "\n" . "X-Sender: " . $adminLoginEmail . "\n" . "X-Mailer: PHP\n" . "X-Priority: 3\n" . "Content-Type: text/plain; charset=" . $contentTypeCharset; // Send the email: mail($emailRecipient, $emailSubject, $emailBody, $emailHeaders); } // -------------------------------------------------------------------- // Map MySQL field names to their localized names: // // TODO: - ensure that the names for field 'user_groups' in tables 'refs' and 'users' are // set correctly (user-specific groups of references vs. admin groups of users) // - add "DropDownFieldName_*" entries for unique field names of table 'users' function mapFieldNames($isDropDown = false) { global $loc; // '$loc' is made globally available in 'core.php' if ($isDropDown) // field names intended for inclusion into a dropdown form element: { $fieldNamesArray = array("author" => $loc["DropDownFieldName_Author"], // "author_count" => $loc[""], // "first_author" => $loc[""], "address" => $loc["DropDownFieldName_Address"], "corporate_author" => $loc["DropDownFieldName_CorporateAuthor"], "thesis" => $loc["DropDownFieldName_Thesis"], "title" => $loc["DropDownFieldName_Title"], "orig_title" => $loc["DropDownFieldName_OrigTitle"], "year" => $loc["DropDownFieldName_Year"], "publication" => $loc["DropDownFieldName_Publication"], "abbrev_journal" => $loc["DropDownFieldName_AbbrevJournal"], "editor" => $loc["DropDownFieldName_Editor"], "volume" => $loc["DropDownFieldName_Volume"], // "volume_numeric" => $loc[""], "issue" => $loc["DropDownFieldName_Issue"], "pages" => $loc["DropDownFieldName_Pages"], // "first_page" => $loc[""], "series_title" => $loc["DropDownFieldName_SeriesTitle"], "abbrev_series_title" => $loc["DropDownFieldName_AbbrevSeriesTitle"], "series_editor" => $loc["DropDownFieldName_SeriesEditor"], "series_volume" => $loc["DropDownFieldName_SeriesVolume"], // "series_volume_numeric" => $loc[""], "series_issue" => $loc["DropDownFieldName_SeriesIssue"], "publisher" => $loc["DropDownFieldName_Publisher"], "place" => $loc["DropDownFieldName_Place"], "edition" => $loc["DropDownFieldName_Edition"], "medium" => $loc["DropDownFieldName_Medium"], "issn" => $loc["DropDownFieldName_Issn"], "isbn" => $loc["DropDownFieldName_Isbn"], "language" => $loc["DropDownFieldName_Language"], "summary_language" => $loc["DropDownFieldName_SummaryLanguage"], "keywords" => $loc["DropDownFieldName_Keywords"], "abstract" => $loc["DropDownFieldName_Abstract"], "area" => $loc["DropDownFieldName_Area"], "expedition" => $loc["DropDownFieldName_Expedition"], "conference" => $loc["DropDownFieldName_Conference"], "doi" => $loc["DropDownFieldName_Doi"], "url" => $loc["DropDownFieldName_Url"], "file" => $loc["DropDownFieldName_File"], "notes" => $loc["DropDownFieldName_Notes"], "location" => $loc["DropDownFieldName_Location"], "call_number" => $loc["DropDownFieldName_CallNumber"], "serial" => $loc["DropDownFieldName_Serial"], "type" => $loc["DropDownFieldName_Type"], "approved" => $loc["DropDownFieldName_Approved"], "created_date" => $loc["DropDownFieldName_CreatedDate"], "created_time" => $loc["DropDownFieldName_CreatedTime"], "created_by" => $loc["DropDownFieldName_CreatedBy"], "modified_date" => $loc["DropDownFieldName_ModifiedDate"], "modified_time" => $loc["DropDownFieldName_ModifiedTime"], "modified_by" => $loc["DropDownFieldName_ModifiedBy"], "marked" => $loc["DropDownFieldName_Marked"], "copy" => $loc["DropDownFieldName_Copy"], "selected" => $loc["DropDownFieldName_Selected"], "user_keys" => $loc["DropDownFieldName_UserKeys"], "user_notes" => $loc["DropDownFieldName_UserNotes"], "user_file" => $loc["DropDownFieldName_UserFile"], "user_groups" => $loc["DropDownFieldName_UserGroups"], "cite_key" => $loc["DropDownFieldName_CiteKey"], // "related" => $loc[""] ); } else // field names intended as title word or column heading: { $fieldNamesArray = array("author" => $loc["Author"], "author_count" => $loc["AuthorCount"], "first_author" => $loc["AuthorFirst"], "address" => $loc["Address"], "corporate_author" => $loc["CorporateAuthor"], "thesis" => $loc["Thesis"], "title" => $loc["Title"], "orig_title" => $loc["TitleOriginal"], "year" => $loc["Year"], "publication" => $loc["Publication"], "abbrev_journal" => $loc["JournalAbbr"], "editor" => $loc["Editor"], "volume" => $loc["Volume"], "volume_numeric" => $loc["VolumeNumeric"], "issue" => $loc["Issue"], "pages" => $loc["Pages"], "first_page" => $loc["PagesFirst"], "series_title" => $loc["TitleSeries"], "abbrev_series_title" => $loc["TitleSeriesAbbr"], "series_editor" => $loc["SeriesEditor"], "series_volume" => $loc["SeriesVolume"], "series_volume_numeric" => $loc["SeriesVolumeNumeric"], "series_issue" => $loc["SeriesIssue"], "publisher" => $loc["Publisher"], "place" => $loc["PublisherPlace"], "edition" => $loc["Edition"], "medium" => $loc["Medium"], "issn" => $loc["ISSN"], "isbn" => $loc["ISBN"], "language" => $loc["Language"], "summary_language" => $loc["LanguageSummary"], "keywords" => $loc["Keywords"], "abstract" => $loc["Abstract"], "area" => $loc["Area"], "expedition" => $loc["Expedition"], "conference" => $loc["Conference"], "doi" => $loc["DOI"], "url" => $loc["URL"], "file" => $loc["File"], "notes" => $loc["Notes"], "location" => $loc["Location"], "call_number" => $loc["CallNumber"], "serial" => $loc["Serial"], "type" => $loc["Type"], "approved" => $loc["Approved"], "created_date" => $loc["CreationDate"], "created_time" => $loc["CreationTime"], "created_by" => $loc["Creator"], "modified_date" => $loc["ModifiedDate"], "modified_time" => $loc["ModifiedTime"], "modified_by" => $loc["Modifier"], "marked" => $loc["Marked"], "copy" => $loc["Copy"], "selected" => $loc["Selected"], "user_keys" => $loc["UserKeys"], "user_notes" => $loc["UserNotes"], "user_file" => $loc["UserFile"], "user_groups" => $loc["UserGroups"], // see TODO note above "cite_key" => $loc["CiteKey"], "related" => $loc["Related"], // field names from table 'users' (that aren't covered by any of the above): "first_name" => $loc["FirstName"], "last_name" => $loc["LastName"], "institution" => $loc["Institution"], "abbrev_institution" => $loc["InstitutionAbbr"], "corporate_institution" => $loc["CorporateInstitution"], "address_line_1" => $loc["AddressLine1"], "address_line_2" => $loc["AddressLine2"], "address_line_3" => $loc["AddressLine3"], "zip_code" => $loc["ZipCode"], "city" => $loc["City"], "state" => $loc["State"], "country" => $loc["Country"], "phone" => $loc["Phone"], "email" => $loc["Email"], "last_login" => $loc["LastLogin"], "logins" => $loc["Logins"], "user_id" => $loc["UserID"], // "user_groups" => $loc["UserGroups"], // see TODO note above ); } return $fieldNamesArray; } // -------------------------------------------------------------------- // BUILD FIELD NAME LINKS // (i.e., build clickable column headers for each available column) // TODO: I18n function buildFieldNameLinks($href, $query, $newORDER, $result, $i, $showQuery, $showLinks, $rowOffset, $showRows, $wrapResults, $citeStyle, $HTMLbeforeLink, $HTMLafterLink, $formType, $submitType, $linkName, $orig_fieldname, $headerMsg, $viewType) { global $databaseBaseURL; // defined in 'ini.inc.php' global $loc; // '$loc' is made globally available in 'core.php' global $client; // Setup the base URL: if (preg_match("/^(cli|inc)/i", $client) OR ($wrapResults == "0")) // we use absolute links for CLI clients, for include mechanisms, or when returning only a partial document structure $baseURL = $databaseBaseURL; else $baseURL = ""; // Map MySQL field names to localized column names: $fieldNamesArray = mapFieldNames(); // Get all field properties of the current MySQL field: $fieldInfoArray = getMySQLFieldInfo($result, $i); if (empty($orig_fieldname)) // if there's no fixed original fieldname specified (as is the case for all fields but the 'Links' column) { // Get the attribute name: $orig_fieldname = $fieldInfoArray["name"]; } if (empty($linkName)) // if there's no fixed link name specified (as is the case for all fields but the 'Links' column)... { if (isset($fieldNamesArray[$orig_fieldname])) { $linkName = $fieldNamesArray[$orig_fieldname]; // ...use the attribute's localized name as link name } else // ...use MySQL field name as fall back: { // Replace substrings with spaces: $linkName = str_replace("_"," ",$orig_fieldname); // Form words (i.e., make the first char of a word uppercase): $linkName = ucwords($linkName); } } // Setup some variables (in order to enable sorting by clicking on column titles) // NOTE: Column sorting with any queries that include the 'LIMIT'... parameter // will (technically) work. However, every new query will limit the selection to a *different* list of records!! ?:-/ if (empty($newORDER)) // if there's no fixed ORDER BY string specified (as is the case for all fields but the 'Links' column) { if ($fieldInfoArray["numeric"] == "1") // Check if the field's data type is numeric (if so we'll append " DESC" to the ORDER clause) $newORDER = ("ORDER BY " . $orig_fieldname . " DESC"); // Build the appropriate ORDER BY clause (sort numeric fields in DESCENDING order) else $newORDER = ("ORDER BY " . $orig_fieldname); // Build the appropriate ORDER BY clause } if ($orig_fieldname == "pages") // when original field name = 'pages' then... { $newORDER = preg_replace("/ORDER BY pages/i", "ORDER BY first_page DESC", $newORDER); // ...sort by 'first_page' instead $orig_fieldname = "first_page"; // adjust '$orig_fieldname' variable accordingly } if ($orig_fieldname == "volume") // when original field name = 'volume' then... { $newORDER = preg_replace("/ORDER BY volume/i", "ORDER BY volume_numeric DESC", $newORDER); // ...sort by 'volume_numeric' instead $orig_fieldname = "volume_numeric"; // adjust '$orig_fieldname' variable accordingly } if ($orig_fieldname == "series_volume") // when original field name = 'series_volume' then... { $newORDER = preg_replace("/ORDER BY series_volume/i", "ORDER BY series_volume_numeric DESC", $newORDER); // ...sort by 'series_volume_numeric' instead $orig_fieldname = "series_volume_numeric"; // adjust '$orig_fieldname' variable accordingly } if ($orig_fieldname == "marked") // when original field name = 'marked' then... $newORDER = preg_replace("/ORDER BY marked/i", "ORDER BY marked DESC", $newORDER); // ...sort 'marked' column in DESCENDING order (so that 'yes' sorts before 'no') if ($orig_fieldname == "last_login") // when original field name = 'last_login' (defined in 'users' table) then... $newORDER = preg_replace("/ORDER BY last_login/i", "ORDER BY last_login DESC", $newORDER); // ...sort 'last_login' column in DESCENDING order (so that latest date+time sorts first) $orderBy = preg_replace("/ORDER BY /i", "", $newORDER); // remove 'ORDER BY ' phrase in order to store just the 'ORDER BY' field spec within the 'orderBy' variable // call the 'newORDERclause()' function to replace the ORDER clause: $queryURLNewOrder = newORDERclause($newORDER, $query); // in the link title, we'll report the field that is actually used for sorting: if (isset($fieldNamesArray[$orig_fieldname])) $linkTitleFieldName = $fieldNamesArray[$orig_fieldname]; else $linkTitleFieldName = $linkName; // figure out if clicking on the current field name will sort in ascending or descending order: // (note that for 1st-level sort attributes, this value will be modified again below) if (preg_match("/ORDER BY [^ ]+ DESC/i", $newORDER)) // if 1st-level sort is in descending order... $linkTitleSortOrder = $loc["descendingOrder"]; // ...sorting will be conducted in DESCending order else $linkTitleSortOrder = $loc["ascendingOrder"]; // ...sorting will be conducted in ASCending order // toggle sort order for the 1st-level sort attribute: if (preg_match("/ORDER BY $orig_fieldname(?! DESC)/i", $query)) // if 1st-level sort is by this attribute (in ASCending order)... { $queryURLNewOrder = preg_replace("/(ORDER%20BY%20$orig_fieldname)(?!%20DESC)/i", "\\1%20DESC", $queryURLNewOrder); // ...change sort order to DESCending $linkTitleSortOrder = $loc["descendingOrder"]; // adjust the link title attribute's sort info accordingly } elseif (preg_match("/ORDER BY $orig_fieldname DESC/i", $query)) // if 1st-level sort is by this attribute (in DESCending order)... { $queryURLNewOrder = preg_replace("/(ORDER%20BY%20$orig_fieldname)%20DESC/i", "\\1", $queryURLNewOrder); // ...change sort order to ASCending $linkTitleSortOrder = $loc["ascendingOrder"]; // adjust the link title attribute's sort info accordingly } // build an informative string that get's displayed when a user mouses over a link: $linkTitle = "\"" . $loc["LinkTitle_SortByField_Prefix"] . $linkTitleFieldName . $loc["LinkTitle_SortByField_Suffix"] . ", " . $linkTitleSortOrder . "\""; // start the table header tag & print the attribute name as link: $tableHeaderLink = $HTMLbeforeLink . "" . $linkName . ""; // append sort indicator after the 1st-level sort attribute: if (preg_match("/ORDER BY $orig_fieldname(?! DESC)(?=,| LIMIT|$)/i", $query)) // if 1st-level sort is by this attribute (in ASCending order)... $tableHeaderLink .= " \"(up)\""; // ...append an upward arrow image elseif (preg_match("/ORDER BY $orig_fieldname DESC/i", $query)) // if 1st-level sort is by this attribute (in DESCending order)... $tableHeaderLink .= " \"(down)\""; // ...append a downward arrow image $tableHeaderLink .= $HTMLafterLink; // append any necessary HTML return $tableHeaderLink; } // -------------------------------------------------------------------- // Build SELECT clause: // (if given, '$additionalFields' & '$customSELECTclause' must contain // a string of comma-separated field names) // TODO: add support for 'users.php' SELECT clauses function buildSELECTclause($displayType, $showLinks, $additionalFields = "", $addUserSpecificFields = true, $addRequiredFields = true, $customSELECTclause = "", $browseByField = "") { global $defaultFieldsListViewMajor; // these variables are specified in 'ini.inc.php' global $defaultFieldsListViewMinor; global $additionalFieldsCitationView; global $showAdditionalFieldsDetailsViewDefault; global $showUserSpecificFieldsDetailsViewDefault; if (empty($displayType)) $displayType = $_SESSION['userDefaultView']; // get the default view for the current user $querySELECTclause = "SELECT "; if (!empty($customSELECTclause)) // if given, honour any custom SQL SELECT clause: { $querySELECTclause .= $customSELECTclause; } else // build a new SELECT clause that's suitable for the given '$displayType': { // Details view: if (preg_match("/^(Display)$/i", $displayType)) // select all fields required to display record details: { if ($showAdditionalFieldsDetailsViewDefault == "no") // omit additional fields: { $querySELECTclause .= "author, title, type, year, publication, abbrev_journal, volume, issue, pages, keywords, abstract"; } else // display all fields: { $querySELECTclause .= "author, title, type, year, publication, abbrev_journal, volume, issue, pages, keywords, abstract, address, corporate_author, thesis, publisher, place, editor, language, summary_language, orig_title, series_editor, series_title, abbrev_series_title, series_volume, series_issue, edition, issn, isbn, medium, area, expedition, conference, notes, approved"; if (isset($_SESSION['loginEmail'])) $querySELECTclause .= ", location"; // we only add the 'location' field if the user is logged in } $querySELECTclause .= ", call_number, serial"; if ($showUserSpecificFieldsDetailsViewDefault == "no") $addUserSpecificFields = false; } // Edit mode & Export: elseif (preg_match("/^(Edit|Export)$/i", $displayType)) // select all fields required to display record details (in edit mode) or to export a record: { $querySELECTclause .= "author, title, type, year, publication, abbrev_journal, volume, issue, pages, keywords, abstract, address, corporate_author, thesis, publisher, place, editor, language, summary_language, orig_title, series_editor, series_title, abbrev_series_title, series_volume, series_issue, edition, issn, isbn, medium, area, expedition, conference, notes, approved"; if (isset($_SESSION['loginEmail'])) $querySELECTclause .= ", location"; // we only add the 'location' field if the user is logged in $querySELECTclause .= ", contribution_id, online_publication, online_citation, created_date, created_time, created_by, modified_date, modified_time, modified_by, call_number, serial"; } // Citation view & RSS output: elseif (preg_match("/^(Cite|RSS)$/i", $displayType)) // select all fields required to build proper record citations: { $querySELECTclause .= "author, title, type, year, publication, abbrev_journal, volume, issue, pages, keywords, abstract, thesis, editor, publisher, place, abbrev_series_title, series_title, series_editor, series_volume, series_issue, edition, language, author_count, online_publication, online_citation, doi, serial"; if ($displayType == "RSS") // for RSS output, we add some additional fields: $querySELECTclause .= ", created_date, created_time, created_by, modified_date, modified_time, modified_by"; if (!empty($additionalFieldsCitationView)) // append all fields from '$additionalFieldsCitationView' that aren't yet included in the SELECT clause foreach ($additionalFieldsCitationView as $field) if (!preg_match("/\b" . $field . "\b/", $querySELECTclause)) { if (preg_match("/^(marked|copy|selected|user_keys|user_notes|user_file|user_groups|cite_key|related)$/", $field)) // if '$field' is one of the user-specific fields, we'll add all of them below $addUserSpecificFields = true; else // append field: $querySELECTclause .= ", " . $field; } } // Browse view: elseif (preg_match("/^Browse$/i", $displayType)) { $querySELECTclause .= escapeSQL($browseByField) . ", COUNT(*) AS records"; } // List view: else // produce the default columnar output style: { $querySELECTclause .= $defaultFieldsListViewMajor; if (!empty($defaultFieldsListViewMinor)) { $querySELECTclause .= ", " . $defaultFieldsListViewMinor; } } } // All views (except Browse view): if (!preg_match("/^Browse$/i", $displayType)) { if (!empty($additionalFields)) { if ($querySELECTclause != "SELECT ") $querySELECTclause .= ", "; // add a comma as field separator, if other fields have already been added to the SELECT clause $querySELECTclause .= $additionalFields; } // NOTE: Functions 'displayColumns()' and 'displayDetails()' (in 'search.php') apply some logic that prevents some or all of the // below fields from getting displayed. This means that you must adopt these functions if you add or remove fields below. if ($addUserSpecificFields) { if (isset($_SESSION['loginEmail'])) // if a user is logged in... $querySELECTclause .= ", marked, copy, selected, user_keys, user_notes, user_file, user_groups, cite_key, related"; // add user-specific fields } if ($addRequiredFields) { // NOTE: Although it won't be visible the 'orig_record' & 'serial' columns get included in every search query // (that's executed directly and not included into HTML as a web link or routed again thru other scripts). // The 'orig_record' column is required in order to present visual feedback on duplicate records, and // the 'serial' column is required in order to obtain unique checkbox names. For SQL queries passed to // 'search.php' directly, function 'verifySQLQuery()' in 'include.inc.php' will add these columns. $querySELECTclause .= ", orig_record, serial"; // add 'orig_record' and 'serial' columns if ($showLinks == "1" OR (preg_match("/^(Edit|Export)$/i", $displayType))) $querySELECTclause .= ", file, url, doi"; // add 'file', 'url' & 'doi' columns if ($showLinks == "1" AND (!preg_match("/^(Edit|Export)$/i", $displayType))) $querySELECTclause .= ", isbn, type"; // add 'isbn' & 'type columns (for export and edit mode, these columns have already been added above) } } return $querySELECTclause; } // -------------------------------------------------------------------- // REPLACE ORDER CLAUSE IN SQL QUERY function newORDERclause($newOrderBy, $query, $encodeQuery = true) { // replace any existing ORDER BY clause with the new one given in '$newOrderBy': $newQuery = preg_replace("/ORDER BY .+?(?=LIMIT.*|GROUP BY.*|HAVING.*|PROCEDURE.*|FOR UPDATE.*|LOCK IN.*|$)/i", $newOrderBy, $query); if ($encodeQuery) $newQuery = rawurlencode($newQuery); // URL encode query return $newQuery; } // -------------------------------------------------------------------- // REPLACE SELECT CLAUSE IN SQL QUERY function newSELECTclause($newSelectClause, $query, $encodeQuery = true) { // replace any existing SELECT clause with the new one given in '$newSelectClause': $newQuery = preg_replace("/SELECT .+?(?= FROM)/i", $newSelectClause, $query); if ($encodeQuery) $newQuery = rawurlencode($newQuery); // URL encode query return $newQuery; } // -------------------------------------------------------------------- // BUILD BROWSE LINKS // (i.e., build a TABLE row with links for "previous" & "next" browsing, as well as links to intermediate pages) // TODO: - use divs + CSS styling (instead of a table-based layout) for _all_ output (not only for 'viewType=Mobile') // - use function 'generateURL()' to build the link URLs function buildBrowseLinks($href, $query, $NoColumns, $rowsFound, $showQuery, $showLinks, $showRows, $rowOffset, $previousOffset, $nextOffset, $wrapResults, $maxPageNo, $formType, $displayType, $citeStyle, $citeOrder, $orderBy, $headerMsg, $viewType) { global $databaseBaseURL; // these variables are defined in 'ini.inc.php' global $displayResultsHeaderDefault; global $displayResultsFooterDefault; global $loc; // '$loc' is made globally available in 'core.php' global $client; // First, calculate the offset page number: $pageOffset = ($rowOffset / $showRows); // workaround for always rounding upward (since I don't know better! :-/): if (preg_match("/[0-9]+\.[0-9+]/",$pageOffset)) // if the result number is not an integer.. $pageOffset = (int) $pageOffset + 1; // we convert the number into an integer and add 1 // set the offset page number to a multiple of $maxPageNo: $pageOffset = $maxPageNo * (int) ($pageOffset / $maxPageNo); // Plus, 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 // Setup the base URL: if (preg_match("/^(cli|inc)/i", $client) OR ($wrapResults == "0")) // we use absolute links for CLI clients, for include mechanisms, or when returning only a partial document structure $baseURL = $databaseBaseURL; else $baseURL = ""; if (preg_match("/^Mobile$/i", $viewType)) { $BrowseLinks = "\n
"; } else { // Start a : $BrowseLinks = "\n
"; // Start a
row: $BrowseLinks .= "\n"; } if (preg_match("/^Mobile$/i", $viewType)) $BrowseLinks .= "\n\t"; elseif (preg_match("/^Print$/i", $viewType) OR preg_match("/^cli/i", $client)) $BrowseLinks .= "\n\t"; elseif (($href == "users.php") OR !isset($displayResultsFooterDefault[$displayType]) OR (isset($displayResultsFooterDefault[$displayType]) AND ($displayResultsFooterDefault[$displayType] != "hidden"))) { $BrowseLinks .= "\n\t"; } else // don't show the select/deselect links when the results footer is hidden { $BrowseLinks .= "\n\t"; } if (preg_match("/^Mobile$/i", $viewType)) $BrowseLinks .= "\n\t
"; else $BrowseLinks .= "\n\t
"; if (preg_match("/^Mobile$/i", $viewType)) $BrowseLinks .= "\n\t
"; else $BrowseLinks .= "\n\t
" . "\n" . "\n
" . $loc["Home"] . "" . "\n\t\t" . $loc["SelectAll"] . "   " . "\n\t\t" . $loc["DeselectAll"] . "" . "\n\t "; // a) If there's a page range below the one currently shown, // create a "[xx-xx]" link (linking directly to the previous range of pages): if ($pageOffset > "0") { $previousRangeFirstPage = ($pageOffset - $maxPageNo + 1); // calculate the first page of the previous page range $previousRangeLastPage = ($previousRangeFirstPage + $maxPageNo - 1); // calculate the last page of the previous page range $BrowseLinks .= "\n\t\t[" . $previousRangeFirstPage . "–" . $previousRangeLastPage . "] "; } // b) Are there any previous pages? if ($rowOffset > 0) // Yes, so create a previous link $BrowseLinks .= "\n\t\t<<"; else // No, there is no previous page so don't print a link $BrowseLinks .= "\n\t\t<<"; // c) Output the page numbers as links: // Count through the number of pages in the results: for($x=($pageOffset * $showRows), $page=($pageOffset + 1); $x<$rowsFound && $page <= ($pageOffset + $maxPageNo); $x+=$showRows, $page++) // Is this the current page? if ($x < $rowOffset || $x > ($rowOffset + $showRows - 1)) // No, so print out a link $BrowseLinks .= " \n\t\t" . $page . ""; else // Yes, so don't print a link $BrowseLinks .= " \n\t\t$page"; // current page is set in BOLD $BrowseLinks .= " "; // d) Are there any Next pages? if ($rowsFound > $nextOffset) // Yes, so create a next link $BrowseLinks .= "\n\t\t>>"; else // No, there is no next page so don't print a link $BrowseLinks .= "\n\t\t>>"; // e) If there's a page range above the one currently shown, // create a "[xx-xx]" link (linking directly to the next range of pages): if ($pageOffset < ($lastPage - $maxPageNo)) { $nextRangeFirstPage = ($pageOffset + $maxPageNo + 1); // calculate the first page of the next page range $nextRangeLastPage = ($nextRangeFirstPage + $maxPageNo - 1); // calculate the last page of the next page range if ($nextRangeLastPage > $lastPage) $nextRangeLastPage = $lastPage; // adjust if this is the last range of pages and if it doesn't go up to the max allowed no of pages $BrowseLinks .= "\n\t\t [" . $nextRangeFirstPage . "–" . $nextRangeLastPage . "]"; } if (preg_match("/^Mobile$/i", $viewType)) $BrowseLinks .= "\n\t"; else $BrowseLinks .= "\n\t"; // Add view links: $viewLinksArray = array(); if (($href == "search.php") AND !preg_match("/^Browse$/i", $displayType) AND !preg_match("/^Mobile$/i", $viewType)) { if (isset($_SESSION['user_permissions']) AND preg_match("/allow_list_view/", $_SESSION['user_permissions'])) { if (preg_match("/^(Cite|Display)$/i", $displayType)) // display a link to List view: { // Replace current SELECT clause with one that's appropriate for List view: if (isset($_SESSION['lastListViewQuery'])) $listViewSelectClause = "SELECT " . extractSELECTclause($_SESSION['lastListViewQuery']); // get SELECT clause from any previous List view query else $listViewSelectClause = buildSELECTclause("List", $showLinks, "", false, false); // produce the default columnar output style $listViewQuery = newSELECTclause($listViewSelectClause, $query); // replace SELECT clause in current query and URL encode query // f) create a 'List View' link that will show the currently displayed result set in List view: $viewLinksArray[] = ""; } else $viewLinksArray[] = "
" . $loc["ListView"] . "
"; } if (isset($_SESSION['user_permissions']) AND preg_match("/allow_cite/", $_SESSION['user_permissions'])) { if (!preg_match("/^Cite$/i", $displayType)) // display a link to Citation view: { // Replace current SELECT clause with one that's appropriate for Citation view: $citeViewSelectClause = buildSELECTclause("Cite", $showLinks, "", false, false); // select all fields required to build proper record citations $citeViewQuery = newSELECTclause($citeViewSelectClause, $query); // replace SELECT clause in current query and URL encode query // g) create a 'Citations' link that will show the currently displayed result set in Citation view: $viewLinksArray[] = ""; } else $viewLinksArray[] = "
" . $loc["Citations"] . "
"; } if (isset($_SESSION['user_permissions']) AND preg_match("/allow_details_view/", $_SESSION['user_permissions'])) { if (!preg_match("/^Display$/i", $displayType)) // display a link to Details view: { // Replace current SELECT clause with one that's appropriate for Details view: if (isset($_SESSION['lastDetailsViewQuery'])) $detailsViewSelectClause = "SELECT " . extractSELECTclause($_SESSION['lastDetailsViewQuery']); // get SELECT clause from previous Details view query (if any) else $detailsViewSelectClause = buildSELECTclause("Display", $showLinks, "", false, false); // select all fields required to display record details $detailsViewQuery = newSELECTclause($detailsViewSelectClause, $query); // replace SELECT clause in current query and URL encode query // h) create a 'Details' link that will show the currently displayed result set in Details view: $viewLinksArray[] = ""; } else $viewLinksArray[] = "
" . $loc["Details"] . "
"; } if (count($viewLinksArray) > 1) $BrowseLinks .= "\n\t\t
" . "\n\t\t\t" . implode("\n\t\t\t | ", $viewLinksArray) . "\n\t\t
"; } // Note: we omit 'Web/Print View' links for include mechanisms! if (!preg_match("/^inc/i", $client)) { $BrowseLinks .= "\n\t\t"; if (count($viewLinksArray) > 1) $BrowseLinks .= "   "; if (preg_match("/^(Print|Mobile)$/i", $viewType)) { // i) create a 'Web View' link that will show the currently displayed result set in web view: $BrowseLinks .= "\"web\""; } else { if (isset($_SESSION['user_permissions']) AND preg_match("/allow_print_view/", $_SESSION['user_permissions'])) // if the 'user_permissions' session variable contains 'allow_print_view'... // j) create a 'Print View' link that will show the currently displayed result set in print view: $BrowseLinks .= "\"print\""; } } if (preg_match("/^Mobile$/i", $viewType)) { $BrowseLinks .= "\n\t" . "\n"; } else { $BrowseLinks .= "\n\t
"; } return $BrowseLinks; } // -------------------------------------------------------------------- // BUILD QUICK SEARCH ELEMENTS // (i.e., generate the "Quick Search" form) function buildQuickSearchElements($query, $queryURL, $showQuery, $showLinks, $showRows, $citeStyle, $citeOrder, $displayType) { global $tableRefs; // defined in 'db.inc.php' global $loc; // '$loc' is made globally available in 'core.php' global $client; if (!preg_match("/^SELECT/i", $queryURL) OR !preg_match("/%20FROM%20" . $tableRefs . "%20/i", $queryURL)) // only include SELECT queries that query table 'refs' $queryURL = ""; // this excludes e.g. queries that query table 'users' $encodedCiteStyle = rawurlencode($citeStyle); $encodedClient = rawurlencode($client); $accessKeyAttribute = addAccessKey("attribute", "qck_search"); $accessKeyTitle = addAccessKey("title", "qck_search"); // extract the first field from the 'WHERE' clause: if (preg_match("/ WHERE [ ()]*(\w+)/i", $query)) $firstField = preg_replace("/.+ WHERE [ ()]*(\w+).*/i", "\\1", $query); else $firstField = ""; // build HTML elements that allow for search suggestions for text entered by the user: if (isset($_SESSION['userAutoCompletions']) AND ($_SESSION['userAutoCompletions'] == "yes")) $suggestElements = buildSuggestElements("quickSearchName", "quickSearchSuggestions", "quickSearchSuggestProgress", "id-quickSearchSelector-", "\t\t\t\t\t\t"); else $suggestElements = ""; // add the "Quick Search" form: $quickSearchForm = <<
$loc[QuickSearch]:
$suggestElements
EOF; return $quickSearchForm; } // -------------------------------------------------------------------- // BUILD REFINE SEARCH ELEMENTS // (i.e., provide options to refine the search results) function buildRefineSearchElements($href, $queryURL, $showQuery, $showLinks, $showRows, $citeStyle, $citeOrder, $dropDownFieldsArray, $dropDownFieldSelected, $displayType) { global $loc; // '$loc' is made globally available in 'core.php' global $client; $encodedCiteStyle = rawurlencode($citeStyle); $encodedClient = rawurlencode($client); $accessKeyAttribute = addAccessKey("attribute", "refine"); $accessKeyTitle = addAccessKey("title", "refine"); // build HTML elements that allow for search suggestions for text entered by the user: if (($href == "search.php") AND (isset($_SESSION['userAutoCompletions']) AND ($_SESSION['userAutoCompletions'] == "yes"))) $suggestElements = buildSuggestElements("refineSearchName", "refineSearchSuggestions", "refineSearchSuggestProgress", "id-refineSearchSelector-", "\t\t\t\t\t"); else $suggestElements = ""; $refineSearchForm = <<
$loc[SearchWithinResults]:
$suggestElements
EOF; return $refineSearchForm; } // -------------------------------------------------------------------- // BUILD USER GROUP FORM ELEMENTS // (i.e., provide options to show the user's personal reference groups -OR- the admin's user groups) // Note: this function serves two purposes (which must not be confused!): // - if "$href = search.php", it will modify the values of the 'user_groups' field of the 'user_data' table (where a user can assign one or more groups to particular *references*) // - if "$href = users.php", this function will modify the values of the 'user_groups' field of the 'users' table (where the admin can assign one or more groups to particular *users*) function buildGroupSearchElements($href, $queryURL, $query, $showQuery, $showLinks, $showRows, $citeStyle, $citeOrder, $displayType) { global $loc; // '$loc' is made globally available in 'core.php' global $client; if (preg_match("/.+user_groups RLIKE \"[()|^.;* ]+[^;]+?[()|$.;* ]+\"/i", $query)) // if the query does contain a 'WHERE' clause that searches for a particular user group // TODO: improve the legibility & robustness of the below regex pattern (yes, it's ugly) $currentGroup = preg_replace("/.+user_groups RLIKE \"(?:\[:(?:space|punct):\]|[()|^.;* \]\[])+([^;]+?(?:\[\^\[:space:\]\[:punct:\]\]\*)?)(?:\[:(?:space|punct):\]|[()|$.;* \]\[])+\".*/i", "\\1", $query); // extract the particular group name else $currentGroup = ""; // show the 'Show My Groups' form: // - if the admin is logged in and calls 'users.php' (since only the admin will be allowed to call 'users.php', checking '$href' is sufficient here) -OR- // - if a user is logged in AND the 'user_permissions' session variable contains 'allow_user_groups' if (($href == "users.php") OR (isset($_SESSION['loginEmail']) AND (isset($_SESSION['user_permissions']) AND preg_match("/allow_user_groups/", $_SESSION['user_permissions'])))) { if (($href == "search.php" AND isset($_SESSION['userGroups'])) OR ($href == "users.php" AND isset($_SESSION['adminUserGroups']))) // if the appropriate session variable is set { $groupSearchDisabled = ""; $groupSearchSelectorTitle = $loc["DescriptionSelectFieldGroupsForm"]; $groupSearchButtonTitle = $loc["DescriptionShowButtonGroupsForm"]; } else { $groupSearchDisabled = " disabled"; // disable the 'Show My Groups' form if the session variable holding the user's groups isnt't available $groupSearchSelectorTitle = "(" . $loc["DescriptionSelectFieldGroupsFormDisabled"] . ")"; $groupSearchButtonTitle = "(" . $loc["DescriptionShowButtonGroupsFormDisabled"] . ")"; } // adjust the form & dropdown labels according to the calling script (which is either 'search.php' or 'users.php') if ($href == "search.php") { $formLegend = $loc["ShowMyGroup"] . ":"; $dropdownLabel = $loc["My"] . ":"; } elseif ($href == "users.php") { $formLegend = $loc["ShowUserGroup"] . ":"; $dropdownLabel = $loc["Users"] . ":"; } else // currently, '$href' will be either 'search.php' or 'users.php', but anyhow { $formLegend = $loc["ShowGroup"] . ":"; $dropdownLabel = ""; } $encodedCiteStyle = rawurlencode($citeStyle); $encodedClient = rawurlencode($client); $groupSearchForm = <<
$formLegend
EOF; } else $groupSearchForm = ""; return $groupSearchForm; } // -------------------------------------------------------------------- // BUILD DISPLAY OPTIONS FORM ELEMENTS // (i.e., provide options to show/hide columns or change the number of records displayed per page) function buildDisplayOptionsElements($href, $queryURL, $showQuery, $showLinks, $rowOffset, $showRows, $citeStyle, $citeOrder, $dropDownFieldsArray, $dropDownFieldSelected, $fieldsToDisplay, $displayType, $headerMsg) { global $loc; // '$loc' is made globally available in 'core.php' global $client; if ($displayType == "Browse") { $submitValue = $loc["ButtonTitle_Browse"]; $submitTitle = $loc["DescriptionShowButtonDisplayOptionsFormBrowseView"]; $selectorDivID = "optShowHideField"; $selectorID = "displayOptionsSelector"; $selectorLabel = $loc["Field"] . ":"; $selectorTitle = $loc["DescriptionSelectFieldDisplayOptionsFormBrowseView"]; $showRowsLabel = $loc["ShowRecordsPerPage_SuffixBrowseView"]; $showRowsTitle = $loc["DescriptionShowRecordsPerPageBrowseView"]; } elseif ($displayType == "Cite") { $submitValue = $loc["ButtonTitle_Show"]; $submitTitle = $loc["DescriptionShowButtonDisplayOptionsFormCiteView"]; $selectorDivID = "optCiteStyle"; $selectorID = "citeStyle"; $selectorLabel = $loc["Style"] . ":"; $selectorTitle = $loc["DescriptionSelectStyleDisplayOptionsFormCiteView"]; $showRowsLabel = $loc["ShowRecordsPerPage_SuffixCiteView"]; $showRowsTitle = $loc["DescriptionShowRecordsPerPage"]; } elseif ($displayType == "Display") { $submitValue = $loc["ButtonTitle_Show"]; $submitTitle = $loc["DescriptionShowButtonDisplayOptionsFormDetailsView"]; $selectorDivID = "optShowHideField"; $selectorID = "displayOptionsSelector"; $selectorLabel = $loc["Field"] . ":"; $selectorTitle = $loc["DescriptionSelectFieldDisplayOptionsFormDetailsView"]; $showRowsLabel = $loc["ShowRecordsPerPage_Suffix"]; $showRowsTitle = $loc["DescriptionShowRecordsPerPage"]; } else { $submitValue = $loc["ButtonTitle_Show"]; $submitTitle = $loc["DescriptionShowButtonDisplayOptionsForm"]; $selectorDivID = "optShowHideField"; $selectorID = "displayOptionsSelector"; $selectorLabel = $loc["Field"] . ":"; $selectorTitle = $loc["DescriptionSelectFieldDisplayOptionsForm"]; $showRowsLabel = $loc["ShowRecordsPerPage_Suffix"]; $showRowsTitle = $loc["DescriptionShowRecordsPerPage"]; } if (($displayType != "Cite") AND ($fieldsToDisplay < 2)) { $hideButtonDisabled = " disabled"; // disable the 'Hide' button if there's currently only one field being displayed (except the links column) $hideButtonTitle = "(" . $loc["DescriptionHideButtonDisplayOptionsFormOnlyOneField"] . ")"; } else { $hideButtonDisabled = ""; if ($displayType == "Display") $hideButtonTitle = $loc["DescriptionHideButtonDisplayOptionsFormDetailsView"]; else $hideButtonTitle = $loc["DescriptionHideButtonDisplayOptionsForm"]; } if (($displayType == "Cite") AND (!isset($_SESSION['user_styles']))) $citeStyleDisabled = " disabled"; // for Citation view, disable the style popup if the session variable holding the user's styles isn't available else $citeStyleDisabled = ""; $encodedCiteStyle = rawurlencode($citeStyle); $encodedClient = rawurlencode($client); $encodedHeaderMsg = rawurlencode($headerMsg); $accessKeyAttribute = addAccessKey("attribute", "max_rows"); $accessKeyTitle = addAccessKey("title", "max_rows"); // NOTE: we embed the current value of '$rowOffset' as hidden tag within the 'displayOptions' form. By this, the current row offset can be re-applied after the user pressed the 'Show'/'Hide' button within the 'displayOptions' form. // To avoid that browse links don't behave as expected, the actual value of '$rowOffset' will be adjusted in function 'seekInMySQLResultsToOffset()' to an exact multiple of '$showRows'! $displayOptionsForm = <<
$loc[DisplayOptions]:
EOF; if (!preg_match("/^(Browse|Cite)$/i", $displayType)) $displayOptionsForm .= "\n\t\t\t\t\t\t"; $displayOptionsForm .= <<
EOF; if ($displayType == "Cite") { $displayOptionsForm .= <<
EOF; return $displayOptionsForm; } // -------------------------------------------------------------------- // BUILD SUGGEST ELEMENTS // (i.e., provide HTML elements that will generate auto-completions or search suggestions for text entered by the user in text entry fields) // requires the Prototype & script.aculo.us JavaScript frameworks: and // more info about 'Ajax.Autocompleter': // // NOTE: I don't know how to pass custom values (such as the CQL index) to the callback function. Therefore, I'm using a dirty hack here where I add // '$CQLIndex' (i.e. an "id-" prefix plus the ID of the HTML form element that contains the selected field) at the beginning of the query parameter // ('paramName'). This ID is required by the callback function to fetch the name of the currently selected refbase field. function buildSuggestElements($searchFieldID, $searchSuggestionsID, $suggestProgressID, $CQLIndex, $prefix = "\t\t", $tokens = "''", $frequency = 0.8, $minChars = 2, $callBack = "addCQLIndex", $suggestURL = "opensearch.php", $paramName = "query", $parameters = "operation=suggest&recordSchema=html") { global $contentTypeCharset; // defined in 'ini.inc.php' $suggestElements = <<