Imperial College Library’s Operational Plan for 2016-2017 included a project to replace a paper-based process for registering external users with a more modern approach. The User Services team approached Library Information Systems to work on a web-based form which would submit data directly to the library management system. Following a couple of months of development, we’re pleased to launch this registration form web application and pleased to make the code available as open-source licensed. The code is available from GitHub at https://github.com/SimonXIX/user_form_alma (licensed under a GNU General Public License version 3 (GPLv3) License) and could be useful to other libraries looking to design a user registration form linking to Ex Libris Alma’s API.
At its core, this was a project to reduce manual processes which relied on humans and paper forms. Imperial College Library is available to certain groups of external users: these include Imperial College alumni, researchers from other HEIs and workplaces, researchers from HEIs registered with the Sconul Access Scheme, and users from certain NHS trusts. (1) Previously, User Services handled these external users by having individuals fill out a paper form when they entered the library and then checking and manually transferring the data from the form into the library management system, Ex Libris Alma. The user would then receive a card (or a temporary card) and enter the library. There were in fact several paper forms, colour-coded based on the type of user (or ‘User Group’ in Alma-parlance) and with each form asking for different data based on the requirements of that type of user. Given that all the data ended up in Alma, the interim steps of putting the data on paper and then manually transferring it were redundant. It seemed like a simple process to automate.
The User Services team outlined the following objectives for the project:
- Launch an easy-to-use online registration form.
- Include a validation step to ensure quality of metadata.
- Achieve automated data entry from the form into Ex Libris Alma.
- Reduce the time spent by User Services staff in administration processes.
Based on earlier investigation of the Ex Libris Alma API, we had determined that an automated process was possible. The ‘Create user’ function (documented on Ex Libris’ Developer Network here https://developers.exlibrisgroup.com/alma/apis/users/POST/gwPcGly021r0XQMGAttqcNyT3YiaSYVA/0aa8d36f-53d6-48ff-8996-485b90b103e4) creates a user record in the Alma database based on a submitted XML object. We chose to create a HTML form into which a user could submit their data: the form would submit to a PHP script (2) which would clean up the data, create the required XML object, and then POST the XML to the Alma API.
Creating the HTML form submitting to PHP was relatively easy and was based upon the instructions and examples at http://www.w3schools.com/php/php_forms.asp. These instructions contained some basic details on data validation using PHP and ensuring security of data. The Ex Libris Developer Network provide code samples for submitting to the specified Alma API function. They also provide samples of the XML for a user object at https://developers.exlibrisgroup.com/alma/apis/xsd/rest_user.xsd: this schema is also used as the basis for the data feed between our Student Information System and Alma. So the spine of the application was built by assembling these separate chunks of code into a single file. The first prototype was a very basic HTML form which only contained those fields required to make a minimal user record in Alma.
During a few meetings and email exchanges, we unearthed the hidden complexity behind the seemingly simple manual process. As part of the process, Library Assistants assigned each user a User Group (a field which determines the borrowing rights of a user) and various Statistical Categories (for internal statistics and the annual Sconul statistical return). In other words, a human would interpret the data on the paper form and assign the appropriate User Group by making several micro-decisions. For a human well-versed in the rules for access to the library, this assignment was almost instinctual. To translate this into a process that a computer could do automatically, we had to unpick every decision and map out how the parameters contributed to determine a single User Group per user and multiple Statistical Categories. NHS users presented the most complexity. For example, if a user is NHS staff from a core NHS trust and their contract ends in more than 8 weeks, then they are assigned the User Group ‘NHS’ and given a Statistical Category 3 note with their job title in it. However if they are NHS staff from a core trust and their contract ends in fewer than 8 weeks, their User Group is determined based on the hospital they are working at: ‘CXREF’ for those at Charing Cross Hospital, ‘HHREF’ for those at Hammersmith Hospital, etc.
Since the series of human decisions is based upon reason and logic, we knew that it was translatable into machine-readable terms. No matter how complex, all decision processes based on a rational and logical process can be automated. (3) Although seemingly complex when expressed in natural language, mapping it onto a diagram (see above) made it easier to conceive of the decision tree as translatable into a series of binary decisions.
We turned the human decision-making process into machine-readable PHP using a series of if-else statements. The decision tree for NHS users, expressed in the paragraph above, was translated into the following:
if ($_POST[ "nhs_status" ] == "trust" ) { if ($_POST[ "ucat1_trust_NHS" ] == "OTHER" ) { $ucat1 = test_input($_POST[ "ucat1_trust_NHS" ]); $user_group = "MEDREF" ; $note1 = "Library staff: please check eligibility to join." ; } elseif($_POST[ "ucat1_trust_NHS" ] == "HILLINGNHS" || $_POST[ "ucat1_trust_NHS" ] == "RBHRB" || $_POST[ "ucat1_trust_NHS" ] == "WESTMIDNHS" ) { $ucat1 = test_input($_POST[ "ucat1_trust_NHS" ]); if ($campus_code == "CHELSEA" ) { $user_group = "CWREF" ; } elseif($campus_code == "CHARINGX" ) { $user_group = "CXREF" ; } elseif($campus_code == "HAMM" ) { $user_group = "HHREF" ; } elseif($campus_code == "BROMPTON" ) { $user_group = "RBREF" ; } elseif($campus_code == "ST_MARYS" ) { $user_group = "SMREF" ; } } else { $ucat1 = test_input($_POST[ "ucat1_trust_NHS" ]); if ($expiry_date > $eight_weeks_later) { $user_group = "NHS" ; } else { if ($campus_code == "CHELSEA" ) { $user_group = "CWREF" ; } elseif($campus_code == "CHARINGX" ) { $user_group = "CXREF" ; } elseif($campus_code == "HAMM" ) { $user_group = "HHREF" ; } elseif($campus_code == "BROMPTON" ) { $user_group = "RBREF" ; } elseif($campus_code == "ST_MARYS" ) { $user_group = "SMREF" ; } } } } elseif($_POST[ "nhs_status" ] == "placement" ) { if ($_POST[ "ucat1_placement_NHS" ] == "OTHER" ) { $user_group = "MEDREF" ; $note1 = "Library staff: please check elibility to join." ; } else { $ucat1 = test_input($_POST[ "ucat1_placement_NHS" ]); if ($expiry_date > $eight_weeks_later) { if ($ucat3 == "STDMID" || $ucat3 == "STDNURS" ) { $user_group = "NHSPS" ; } else { if ($campus_code == "CHELSEA" ) { $user_group = "CWLOCAL" ; } elseif($campus_code == "CHARINGX" ) { $user_group = "CXLOCAL" ; } elseif($campus_code == "HAMM" ) { $user_group = "HHLOCAL" ; } elseif($campus_code == "BROMPTON" ) { $user_group = "RBLOCAL" ; } elseif($campus_code == "ST_MARYS" ) { $user_group = "SMLOCAL" ; } } } else { if ($campus_code == "CHELSEA" ) { $user_group = "CWREF" ; } elseif($campus_code == "CHARINGX" ) { $user_group = "CXREF" ; } elseif($campus_code == "HAMM" ) { $user_group = "HHREF" ; } elseif($campus_code == "BROMPTON" ) { $user_group = "RBREF" ; } elseif($campus_code == "ST_MARYS" ) { $user_group = "SMREF" ; } } } } |
The script incorporates several other decision trees for the other types of user to logically determine which User Group, Notes, and Statistical Category fields should be assigned based on the user’s answers.
One of the major design requirements was that the form should show only those fields relevant to the type of user filling it in. An NHS user sees a different form to a Sconul Access Scheme user. We wanted the form to dynamically hide and show fields based on the answer to the first question, ‘What category of user are you?’. As with most coding questions, this was answered by searching Stack Overflow: https://stackoverflow.com/questions/13426472/dynamic-html-form. The form hides and displays HTML divs using JavaScript (specifically some functions from the jQuery library). This was intended to straightforwardly show different forms based on the answer to the first question but expanded as the underlying complexity of the process was revealed. The finished form uses a branched approach which presents several layers of divs to the user with different questions based on their previous answers. (4) There are distinct user experience advantages to this approach. The user only sees those fields specifically relevant to them and doesn’t see redundant fields. Presenting fields in which the user has to type ‘N/A’ is the kind of user experience that we sought to avoid.
Similar logic is used to handle the assembling of the XML that gets submitted to the Ex Libris Alma API. Certain fields in the XML will only be populated if the user takes a particular route through the form. For example, the ‘work address’ field is only filled in for certain NHS users. We therefore weren’t able to use one XML template which would cover all users. If any elements of an XML stanza were wrapping a blank field, then the Alma API would respond with a 401664 error, ‘Mandatory field is missing’. So the XML had to be constructed by the script based on which fields had been entered by the user. Again, we used a series of if statements to determine if there was no data present in a particular field and assign the variable a blank chunk of XML if that were the case; otherwise to fill the variable with valid XML. The XML is then assembled by the line:
# assemble XML from component parts $xml=$corexml1 . $birthdatexml . $corexml2 . "" . "" . $addressxml . $addressnhsxml . "" . $emailxml . $phonexml . "" . $notesxml . "" . $ucat1xml . $ucat2xml . $ucat3xml . "" . "" ; |
Data cleansing and validation is largely handled by a single function:
function test_input($data) { $data = trim($data); $data = stripslashes($data); $data = htmlspecialchars($data); return $data; } |
Every field inputted by the user is run through test_input to trim whitespace from the end, strip slashes, and remove special characters that would disrupt the script. Data validation is also handled in the HTML form itself through HTML5 validation of input fields. ‘input type=”email”‘, for example, will only accept text strings structured as valid email addresses. For the few fields required dates, we used a date-picker input type from the jQuery library: this not only improves the user experience by saving the user the time of entering a date manually but it ensures that dates are entered in the precise format that we need. (5)
Some extra functional requirements were added to the project at the last minute. First, the form must send a confirmation email to the email address provided and the message must be personalised based on their user group. This was achieved using the instructions at https://stackoverflow.com/questions/18379238/send-email-with-php-from-html-form-on-submit-with-the-same-script and setting up if conditions to select a $message string based on the user group field. Second, we found that the Windows server where we intended to host the live form threw an error when PHP’s cURL library was used. Although POSTing the XML via cURL worked perfectly on the Linux server used for development and testing, when moved to this Windows server, it failed. After liaising with ICT to debug this, we decided the path of least resistance was to rewrite the POST request to submit the XML via some means other than cURL. (6) Our live version of the form submits using:
$url = $almaurl. '/almaws/v1/users' ; $queryParams = '?' . urlencode( 'apikey' ) . '=' . urlencode($apikey); $fullurl = $url.$queryParams; $stream_options = array( 'http' => array( 'method' => 'POST' , 'header' => 'Content-Type: application/xml' . "\r\n" , 'content' => $xml)); $context = stream_context_create($stream_options); $response = file_get_contents($fullurl, null , $context); |
However the cURL request was the preferred option and is included in the GitHub version of the script commented out.
Karine Larose, Systems Librarian, led work on the styling of the form. With her experience of user experience research, Karine was well-placed to consider the user experience ramifications of the form’s design and style it appropriately. She considered a number of different styles that the form could use and liaised with the team’s Library Systems Developer. The finished form uses a Bootstrap style with some added CSS for jQueryUI stuff like the date picker. The colours chosen complement Imperial College’s house-style without being too intrusive.
This web application demonstrates how well-developed APIs make it possible to create new systems functions outside the confines of the library management system. This allows the library more freedom and autonomy in choosing our own functions to implement on our own timescale. It also allowed for more granular control over all elements of the design: as library systems workers, it was a relief to be asked “Can you change the wording of field x?” and to be able to reply “Yes, we can change it to anything you want.” Creating our own bespoke software allows us to create software that meets our unique needs better than any software we could purchase from a third-party company. This project and this piece of code demonstrates the value of moving away from the monolithic ‘one-stop-shop’ library system to a network of modular code maintained around central databases. Writing our own library software makes us free.
endnotes
(1) Imperial College London Library Services provide library services to a number of teaching hospitals around south-west London. We have arrangements with these core NHS trusts (and peripheral trusts) to provide library access for their staff and students.
(2) PHP was chosen as the scripting language based purely on the team’s existing knowledge of it and not due to any sublime considerations about the elegance of PHP (c.f. https://www.xkcd.com/224/)
(4) Like a choose-your-own-adventure story. Which is a good way to think about the branching logic of coding in general. If the user is an Imperial College alumnus, then ask about their date of graduation ( / turn to page 379). If not, don’t ( / turn to page 19).
(5) British dd-mm-yy format with hyphens instead of slashes. It turns out that PHP assumes the US date format if the string contains slashes and assumes the British date format if the string contains hyphens. We had to locally modify the jQuery files to change from the US date format so whereas our live form points to local .js files on the local server, the version on GitHub contains links to hosted .js files.
(6) As mentioned earlier, Ex Libris helpfully provide code samples in various languages for how to submit POST requests directly to their API. Unhelpfully, the code samples for both ‘cURL’ and ‘PHP’ use cURL: they do not provide a sample of a pure PHP POST request. The answer, using file_get_contents to POST, was found at https://stackoverflow.com/questions/1660983/sending-xml-data-using-http-post-with-php