diff --git a/resources/README b/resources/README index 838938d27..c66093669 100644 --- a/resources/README +++ b/resources/README @@ -6,6 +6,7 @@ and follow instructions For the manual install, refer to the Technical Documentation available on http://erm.library.nd.edu +For setting up the API, please see the README in resources/api/ ************************** License / Copyright notices diff --git a/resources/admin/classes/domain/Fund.php b/resources/admin/classes/domain/Fund.php index ac97676f8..0a0d86e11 100644 --- a/resources/admin/classes/domain/Fund.php +++ b/resources/admin/classes/domain/Fund.php @@ -57,6 +57,12 @@ public function getNumberOfChildren(){ return $result['childCount']; } + public function getFundIDFromFundCode($fundCode) { + $query = "SELECT fundID FROM Fund WHERE fundCode = '" . $fundCode . "';"; + $result = $this->db->processQuery($query, 'assoc'); + return isset($result['fundID']) ? $result['fundID'] : null; + } + //returns array of archived objects public function getUnArchivedFunds(){ diff --git a/resources/admin/classes/domain/NoteType.php b/resources/admin/classes/domain/NoteType.php index 6095dd99f..6c1ffd7cb 100644 --- a/resources/admin/classes/domain/NoteType.php +++ b/resources/admin/classes/domain/NoteType.php @@ -63,6 +63,16 @@ public function getInitialNoteTypeID() { } } + public function getNoteTypeIDByName($name) { + $query = "SELECT * FROM NoteType WHERE shortName='$name' LIMIT 0,1"; + $result = $this->db->processQuery($query, 'assoc'); + + if (isset($result['noteTypeID'])){ + return $result['noteTypeID']; + }else{ + return null; + } + } //returns number of children for this particular contact role public function getNumberOfChildren(){ @@ -79,4 +89,4 @@ public function getNumberOfChildren(){ } -?> \ No newline at end of file +?> diff --git a/resources/admin/classes/domain/Resource.php b/resources/admin/classes/domain/Resource.php index 09bd4aae8..0675b5625 100644 --- a/resources/admin/classes/domain/Resource.php +++ b/resources/admin/classes/domain/Resource.php @@ -2101,6 +2101,10 @@ public function enterNewWorkflow($workflowID = null){ $resourceStep->stepID = $step->stepID; $resourceStep->priorStepID = $step->priorStepID; $resourceStep->stepName = $step->stepName; + $resourceStep->stepStartDate = ''; + $resourceStep->stepEndDate = ''; + $resourceStep->archivingDate = ''; + $resourceStep->endLoginID = ''; $resourceStep->userGroupID = $step->userGroupID; $resourceStep->displayOrderSequence = $step->displayOrderSequence; @@ -2108,7 +2112,6 @@ public function enterNewWorkflow($workflowID = null){ } - //Start the first step //this handles updating the db and sending notifications for approval groups foreach ($this->getFirstSteps() as $resourceStep) { diff --git a/resources/api/.htaccess b/resources/api/.htaccess new file mode 100644 index 000000000..a93607007 --- /dev/null +++ b/resources/api/.htaccess @@ -0,0 +1,4 @@ +RewriteEngine On +RewriteCond %{REQUEST_FILENAME} !-f +RewriteCond %{REQUEST_FILENAME} !-d +RewriteRule ^(.*)$ index.php [QSA,L] diff --git a/resources/api/INSTALL b/resources/api/INSTALL new file mode 100644 index 000000000..3ec83166d --- /dev/null +++ b/resources/api/INSTALL @@ -0,0 +1,4 @@ +The API uses Flight, a fast, simple, extensible framework for PHP. +Just install Flight in this directory, so Flight.php is available like this: resources/api/Flight/flight/Flight.php + +http://flightphp.com/ diff --git a/resources/api/README b/resources/api/README new file mode 100644 index 000000000..4b45d15d5 --- /dev/null +++ b/resources/api/README @@ -0,0 +1,15 @@ +Edit resources/admin/configuration.ini and fill apiAuthorizedIP in the [settings] section with the IP address of the client accessing the API: +apiAuthorizedIP="192.168.1.1" + +You can set multiple IPs (comma-separated): +apiAuthorizedIP="192.168.1.1,192.168.2" + +You can also set incomplete IPs so the whole range will be allowed: +apiAuthorizedIP="192.168.1" +(will authorize all IPs from 192.168.1.1 to 192.168.1.255) + +If you are trying to access the API from the machine it is installed on, you can use the loopback interface (127.0.0.1). + +Hint: if you don't know which IP address will be requesting the API, just leave apiAuthorizedIP blank (apiAuthorizedIP="") and access the example form with your browser ( http://yourcoral.tld/resources/api_client/ ), you will get an error like this: +You are not authorized to use this service. +Unauthorized IP: 127.0.0.1 diff --git a/resources/api/index.php b/resources/api/index.php new file mode 100644 index 000000000..52adbb359 --- /dev/null +++ b/resources/api/index.php @@ -0,0 +1,328 @@ +data['user']) ? Flight::request()->data['user'] : 'API'; + $status = new Status(); + $resource = new Resource(); + $resource->createDate = date( 'Y-m-d' ); + $resource->createLoginID = $user; + $resource->statusID = $status->getIDFromName('progress'); + $resource->updateDate = ''; + $resource->updateLoginID = ''; + $resource->orderNumber = ''; + $resource->systemNumber = ''; + $resource->userLimitID = ''; + $resource->authenticationUserName = ''; + $resource->authenticationPassword = ''; + $resource->storageLocationID = ''; + $resource->registeredIPAddresses = ''; + $resource->coverageText = ''; + $resource->archiveDate = ''; + $resource->archiveLoginID = ''; + $resource->workflowRestartDate = ''; + $resource->workflowRestartLoginID = ''; + $resource->currentStartDate = ''; + $resource->currentEndDate = ''; + $resource->subscriptionAlertEnabledInd = ''; + $resource->authenticationTypeID = ''; + $resource->accessMethodID = ''; + $resource->recordSetIdentifier = ''; + $resource->hasOclcHoldings = ''; + $resource->numberRecordsAvailable = ''; + $resource->numberRecordsLoaded = ''; + $resource->bibSourceURL = ''; + $resource->catalogingTypeID = ''; + $resource->catalogingStatusID = ''; + $resource->mandatoryResource = ''; + $resource->resourceID = null; + + $fieldNames = array("titleText", "descriptionText", "providerText", "resourceURL", "resourceAltURL", "noteText", "resourceTypeID", "resourceFormatID", "acquisitionTypeID"); + foreach ($fieldNames as $fieldName) { + $resource->$fieldName = Flight::request()->data->$fieldName; + } + try { + $resource->save(); + $resourceID = $resource->primaryKey; + $resource = new Resource(new NamedArguments(array('primaryKey' => $resourceID))); + + //add administering site + if (Flight::request()->data['administeringSiteID']) { + foreach (Flight::request()->data['administeringSiteID'] as $administeringSiteID) { + $resourceAdministeringSiteLink = new ResourceAdministeringSiteLink(); + $resourceAdministeringSiteLink->resourceAdministeringSiteLinkID = ''; + $resourceAdministeringSiteLink->resourceID = $resourceID; + $resourceAdministeringSiteLink->administeringSiteID = $administeringSiteID; + try { + $resourceAdministeringSiteLink->save(); + } catch (Exception $e) { + echo $e->getMessage(); + } + } + } + + // add home location + foreach (array("homeLocationNote" => "Home Location") as $key => $value) { + if (Flight::request()->data[$key]) { + $noteType = new NoteType(); + $noteTypeID = $value ? createNoteType($value) : $noteType->getInitialNoteTypeID(); + $resourceNote = new ResourceNote(); + $resourceNote->resourceNoteID = ''; + $resourceNote->updateLoginID = $user; + $resourceNote->updateDate = date( 'Y-m-d' ); + $resourceNote->noteTypeID = $noteTypeID; + $resourceNote->tabName = 'Product'; + $resourceNote->resourceID = $resourceID; + $resourceNote->noteText = Flight::request()->data[$key]; + $resourceNote->save(); + } + } + + // General notes + $noteText = ''; + foreach (array("noteText" => "Note", "providerText" => "Provider", "publicationYear" => "Publication Year or order start date", "edition" => "Edition", "holdLocation" => "Hold location", "patronHold" => "Patron hold") as $key => $value) { + if (Flight::request()->data[$key]) { + $noteText .= $value . ": " . Flight::request()->data[$key] . "\n"; + } + + } + if ($noteText) { + $noteType = new NoteType(); + $noteTypeID = $noteType->getInitialNoteTypeID(); + $resourceNote = new ResourceNote(); + $resourceNote->resourceNoteID = ''; + $resourceNote->updateLoginID = $user; + $resourceNote->updateDate = date( 'Y-m-d' ); + $resourceNote->noteTypeID = $noteTypeID; + $resourceNote->tabName = 'Product'; + $resourceNote->resourceID = $resourceID; + $resourceNote->noteText = $noteText; + $resourceNote->save(); + } + + // add existing license and/or license required + $noteText = ''; + foreach (array("licenseRequired" => "License required?", "existingLicense" => "Existing License?") as $key => $value) { + $noteText .= $value . " " . Flight::request()->data[$key] . "\n"; + } + if ($noteText) { + $noteTypeID = createNoteType("License Type"); + $resourceNote = new ResourceNote(); + $resourceNote->resourceNoteID = ''; + $resourceNote->updateLoginID = $user; + $resourceNote->updateDate = date( 'Y-m-d' ); + $resourceNote->noteTypeID = $noteTypeID; + $resourceNote->tabName = 'Acquisitions'; + $resourceNote->resourceID = $resourceID; + $resourceNote->noteText = $noteText; + $resourceNote->save(); + } + + // add CM Ranking + foreach (array("CMRanking" => "CM Ranking") as $key => $value) { + if (Flight::request()->data[$key]) { + $noteTypeID = createNoteType("CM Ranking"); + $resourceNote = new ResourceNote(); + $resourceNote->resourceNoteID = ''; + $resourceNote->updateLoginID = $user; + $resourceNote->updateDate = date( 'Y-m-d' ); + $resourceNote->noteTypeID = $noteTypeID; + $resourceNote->tabName = 'Product'; + $resourceNote->resourceID = $resourceID; + $resourceNote->noteText = $value . ": " . Flight::request()->data[$key]; + $resourceNote->save(); + } + } + + // add CM Important Factor + $noteText = ''; + foreach (array("ripCode" => "RIP code", "subjectCoverage" => "Subject coverage", "audience" => "Audience", "frequency" => "Frequency and language", "access" => "Access via indexes", "contributingFactors" => "Contributing factors") as $key => $value) { + if (Flight::request()->data[$key]) { + $noteText .= $value . ": " . Flight::request()->data[$key] . "\n"; + } + } + if ($noteText) { + $noteTypeID = createNoteType("CM Important Factor"); + $resourceNote = new ResourceNote(); + $resourceNote->resourceNoteID = ''; + $resourceNote->updateLoginID = $user; + $resourceNote->updateDate = date( 'Y-m-d' ); + $resourceNote->noteTypeID = $noteTypeID; + $resourceNote->tabName = 'Product'; + $resourceNote->resourceID = $resourceID; + $resourceNote->noteText = $noteText; + $resourceNote->save(); + } + + // add fund and cost + if (Flight::request()->data['cost'] || Flight::request()->data['fund']) { + $rp = new ResourcePayment(); + $rp->resourcePaymentID = ''; + $rp->selectorLoginID = $user; + $rp->year = ''; + $rp->subscriptionStartDate = ''; + $rp->subscriptionEndDate = ''; + $rp->costDetailsID = ''; + $rp->costNote = ''; + $rp->invoiceNum = ''; + $rp->resourceID = $resourceID; + $rp->paymentAmount = cost_to_integer(Flight::request()->data['cost']); + $rp->currencyCode = 'USD'; + $rp->orderTypeID = 2; + $rp->priceTaxExcluded = ''; + $rp->priceTaxIncluded = ''; + $rp->taxRate = ''; + $fundCode = Flight::request()->data['fund']; + // Check if the fund already exists + + $fundObj = new Fund(); + $fundID = $fundObj->getFundIDFromFundCode($fundCode); + + // Add it if not + if (!$fundID) { + $fundObj->fundID = ''; + $fundObj->fundCode = $fundCode; + $fundObj->shortName = $fundCode; + $fundObj->archived = null; + $fundObj->save(); + } + // Create the resourcePayment + $rp->fundID = $fundID; + $rp->save(); + } + + + $resource->enterNewWorkflow(); + + + } catch (Exception $e) { + Flight::json(array('error' => $e->getMessage())); + } + Flight::json(array('resourceID' => $resourceID)); + +}); + +Flight::route('/version/', function() { + Flight::json(array('API' => 'v1')); +}); + +Flight::route('/getResourceTypes/', function() { + $rt = new ResourceType(); + $resourceTypeArray = $rt->allAsArray(); + Flight::json($resourceTypeArray); +}); + +Flight::route('/getResourceType/@id', function($id) { + $resourceTypeObj = new ResourceType(new NamedArguments(array('primaryKey' => $id))); + Flight::json($resourceTypeObj->shortName); +}); + +Flight::route('/getAcquisitionTypes/', function() { + $acquisitionTypeObj = new AcquisitionType(); + $acquisitionTypeArray = $acquisitionTypeObj->sortedArray(); + Flight::json($acquisitionTypeArray); +}); + +Flight::route('/getAcquisitionType/@id', function($id) { + $acquisitionTypeObj = new AcquisitionType(new NamedArguments(array('primaryKey' => $id))); + Flight::json($acquisitionTypeObj->shortName); +}); + +Flight::route('/getResourceFormats/', function() { + $resourceFormatObj = new ResourceFormat(); + $resourceFormatArray = $resourceFormatObj->sortedArray(); + Flight::json($resourceFormatArray); +}); + +Flight::route('/getResourceFormat/@id', function($id) { + $resourceFormatObj = new ResourceFormat(new NamedArguments(array('primaryKey' => $id))); + Flight::json($resourceFormatObj->shortName); +}); + +Flight::route('/getAdministeringSites/', function() { + $as = new AdministeringSite(); + $asArray = $as->allAsArray(); + Flight::json($asArray); +}); + +Flight::route('/getAdministeringSite/@id', function($id) { + $as = new AdministeringSite(new NamedArguments(array('primaryKey' => $id))); + Flight::json($as->shortName); +}); + + +Flight::start(); + +function isAllowed() { + $config = new Configuration(); + + // If apiAuthorizedIP is not set, don't allow + if (!$config->settings->apiAuthorizedIP) { return 0; } + + // If apiAuthorizedIP could not be parsed, don't allow + $authorizedIP = explode(',', $config->settings->apiAuthorizedIP); + if (!$authorizedIP) { return 0; } + + // If a matching IP has been found, allow + if (array_filter($authorizedIP, "IpFilter")) { return 1; } + + return 0; +} + +// A matching IP is either a complete IP or the start of one (allowing IP range) +function IpFilter($var) { + $pos = strpos($_SERVER['REMOTE_ADDR'], $var); + return $pos === false ? false : true; +} + +// Create a note type if it doesn't exist +// Return noteTypeID +function createNoteType($name) { + $noteType = new NoteType(); + $noteTypeID = $noteType->getNoteTypeIDByName($name); + if ($noteTypeID) return $noteTypeID; + + $noteType->shortName = $name; + $noteType->noteTypeID = ''; + $noteType->save(); + return $noteType->noteTypeID; +} + +function userExists($user) { + $createUser = new User(new NamedArguments(array('primaryKey' => $user))); + return $createUser->loginID ? true : false; +} +?> diff --git a/resources/api_client/INSTALL b/resources/api_client/INSTALL new file mode 100644 index 000000000..abf21b953 --- /dev/null +++ b/resources/api_client/INSTALL @@ -0,0 +1,4 @@ +This example form uses Unirest, a set of lightweight HTTP libraries. +Install Unirest in this directory with composer, so autoload.php is available like this: resources/api_client/vendor/autoload.php + +http://unirest.io/php.html diff --git a/resources/api_client/README b/resources/api_client/README new file mode 100644 index 000000000..9880cb461 --- /dev/null +++ b/resources/api_client/README @@ -0,0 +1,3 @@ +This is an example form that uses the Coral API. +Edit api_client/index.php and set the $server variable: +$server = "http://yourcoral.tld/resources/api/"; diff --git a/resources/api_client/composer.json b/resources/api_client/composer.json new file mode 100644 index 000000000..77f127477 --- /dev/null +++ b/resources/api_client/composer.json @@ -0,0 +1,5 @@ +{ + "require-dev": { + "mashape/unirest-php": "2.*" + } +} diff --git a/resources/api_client/index.php b/resources/api_client/index.php new file mode 100644 index 000000000..5e5edcf73 --- /dev/null +++ b/resources/api_client/index.php @@ -0,0 +1,266 @@ + + + + +
+ + + +The resource was correctly submitted (resource " . $response->body->resourceID . ")
"; + ?> +You are not authorized to use this service.
"; + echo $response->body; + } + if ($response->code == 500) { + echo "This service encountered an error.
"; + } + } else { +?> + +'; + foreach ($response->body as $resourceType) { + echo ' "; + } + echo ''; +} + +function getAcquisitionTypesAsRadio($server) { + $response = Unirest\Request::post($server . "getAcquisitionTypes/", $headers, $body); + foreach ($response->body as $resourceType) { + if (strtolower($resourceType->shortName) == "approved" || strtolower($resourceType->shortName) == "need approval") { + echo ' ' . $resourceType->shortName; + } + } +} + +function getResourceFormatsAsDropdown($server) { + $response = Unirest\Request::post($server . "getResourceFormats/", $headers, $body); + echo ''; +} + +function getAdministeringSitesAsDropdown($server) { + $response = Unirest\Request::post($server . "getAdministeringSites/", $headers, $body); + echo ''; +} + + + +?> + diff --git a/resources/api_client/pure-min.css b/resources/api_client/pure-min.css new file mode 100644 index 000000000..f0aa374f6 --- /dev/null +++ b/resources/api_client/pure-min.css @@ -0,0 +1,11 @@ +/*! +Pure v0.6.0 +Copyright 2014 Yahoo! Inc. All rights reserved. +Licensed under the BSD License. +https://github.com/yahoo/pure/blob/master/LICENSE.md +*/ +/*! +normalize.css v^3.0 | MIT License | git.io/normalize +Copyright (c) Nicolas Gallagher and Jonathan Neal +*/ +/*! normalize.css v3.0.2 | MIT License | git.io/normalize */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{font-size:2em;margin:.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}.hidden,[hidden]{display:none!important}.pure-img{max-width:100%;height:auto;display:block}.pure-g{letter-spacing:-.31em;*letter-spacing:normal;*word-spacing:-.43em;text-rendering:optimizespeed;font-family:FreeSans,Arimo,"Droid Sans",Helvetica,Arial,sans-serif;display:-webkit-flex;-webkit-flex-flow:row wrap;display:-ms-flexbox;-ms-flex-flow:row wrap;-ms-align-content:flex-start;-webkit-align-content:flex-start;align-content:flex-start}.opera-only :-o-prefocus,.pure-g{word-spacing:-.43em}.pure-u{display:inline-block;*display:inline;zoom:1;letter-spacing:normal;word-spacing:normal;vertical-align:top;text-rendering:auto}.pure-g [class *="pure-u"]{font-family:sans-serif}.pure-u-1,.pure-u-1-1,.pure-u-1-2,.pure-u-1-3,.pure-u-2-3,.pure-u-1-4,.pure-u-3-4,.pure-u-1-5,.pure-u-2-5,.pure-u-3-5,.pure-u-4-5,.pure-u-5-5,.pure-u-1-6,.pure-u-5-6,.pure-u-1-8,.pure-u-3-8,.pure-u-5-8,.pure-u-7-8,.pure-u-1-12,.pure-u-5-12,.pure-u-7-12,.pure-u-11-12,.pure-u-1-24,.pure-u-2-24,.pure-u-3-24,.pure-u-4-24,.pure-u-5-24,.pure-u-6-24,.pure-u-7-24,.pure-u-8-24,.pure-u-9-24,.pure-u-10-24,.pure-u-11-24,.pure-u-12-24,.pure-u-13-24,.pure-u-14-24,.pure-u-15-24,.pure-u-16-24,.pure-u-17-24,.pure-u-18-24,.pure-u-19-24,.pure-u-20-24,.pure-u-21-24,.pure-u-22-24,.pure-u-23-24,.pure-u-24-24{display:inline-block;*display:inline;zoom:1;letter-spacing:normal;word-spacing:normal;vertical-align:top;text-rendering:auto}.pure-u-1-24{width:4.1667%;*width:4.1357%}.pure-u-1-12,.pure-u-2-24{width:8.3333%;*width:8.3023%}.pure-u-1-8,.pure-u-3-24{width:12.5%;*width:12.469%}.pure-u-1-6,.pure-u-4-24{width:16.6667%;*width:16.6357%}.pure-u-1-5{width:20%;*width:19.969%}.pure-u-5-24{width:20.8333%;*width:20.8023%}.pure-u-1-4,.pure-u-6-24{width:25%;*width:24.969%}.pure-u-7-24{width:29.1667%;*width:29.1357%}.pure-u-1-3,.pure-u-8-24{width:33.3333%;*width:33.3023%}.pure-u-3-8,.pure-u-9-24{width:37.5%;*width:37.469%}.pure-u-2-5{width:40%;*width:39.969%}.pure-u-5-12,.pure-u-10-24{width:41.6667%;*width:41.6357%}.pure-u-11-24{width:45.8333%;*width:45.8023%}.pure-u-1-2,.pure-u-12-24{width:50%;*width:49.969%}.pure-u-13-24{width:54.1667%;*width:54.1357%}.pure-u-7-12,.pure-u-14-24{width:58.3333%;*width:58.3023%}.pure-u-3-5{width:60%;*width:59.969%}.pure-u-5-8,.pure-u-15-24{width:62.5%;*width:62.469%}.pure-u-2-3,.pure-u-16-24{width:66.6667%;*width:66.6357%}.pure-u-17-24{width:70.8333%;*width:70.8023%}.pure-u-3-4,.pure-u-18-24{width:75%;*width:74.969%}.pure-u-19-24{width:79.1667%;*width:79.1357%}.pure-u-4-5{width:80%;*width:79.969%}.pure-u-5-6,.pure-u-20-24{width:83.3333%;*width:83.3023%}.pure-u-7-8,.pure-u-21-24{width:87.5%;*width:87.469%}.pure-u-11-12,.pure-u-22-24{width:91.6667%;*width:91.6357%}.pure-u-23-24{width:95.8333%;*width:95.8023%}.pure-u-1,.pure-u-1-1,.pure-u-5-5,.pure-u-24-24{width:100%}.pure-button{display:inline-block;zoom:1;line-height:normal;white-space:nowrap;vertical-align:middle;text-align:center;cursor:pointer;-webkit-user-drag:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.pure-button::-moz-focus-inner{padding:0;border:0}.pure-button{font-family:inherit;font-size:100%;padding:.5em 1em;color:#444;color:rgba(0,0,0,.8);border:1px solid #999;border:0 rgba(0,0,0,0);background-color:#E6E6E6;text-decoration:none;border-radius:2px}.pure-button-hover,.pure-button:hover,.pure-button:focus{filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#1a000000', GradientType=0);background-image:-webkit-gradient(linear,0 0,0 100%,from(transparent),color-stop(40%,rgba(0,0,0,.05)),to(rgba(0,0,0,.1)));background-image:-webkit-linear-gradient(transparent,rgba(0,0,0,.05) 40%,rgba(0,0,0,.1));background-image:-moz-linear-gradient(top,rgba(0,0,0,.05) 0,rgba(0,0,0,.1));background-image:-o-linear-gradient(transparent,rgba(0,0,0,.05) 40%,rgba(0,0,0,.1));background-image:linear-gradient(transparent,rgba(0,0,0,.05) 40%,rgba(0,0,0,.1))}.pure-button:focus{outline:0}.pure-button-active,.pure-button:active{box-shadow:0 0 0 1px rgba(0,0,0,.15) inset,0 0 6px rgba(0,0,0,.2) inset;border-color:#000\9}.pure-button[disabled],.pure-button-disabled,.pure-button-disabled:hover,.pure-button-disabled:focus,.pure-button-disabled:active{border:0;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);filter:alpha(opacity=40);-khtml-opacity:.4;-moz-opacity:.4;opacity:.4;cursor:not-allowed;box-shadow:none}.pure-button-hidden{display:none}.pure-button::-moz-focus-inner{padding:0;border:0}.pure-button-primary,.pure-button-selected,a.pure-button-primary,a.pure-button-selected{background-color:#0078e7;color:#fff}.pure-form input[type=text],.pure-form input[type=password],.pure-form input[type=email],.pure-form input[type=url],.pure-form input[type=date],.pure-form input[type=month],.pure-form input[type=time],.pure-form input[type=datetime],.pure-form input[type=datetime-local],.pure-form input[type=week],.pure-form input[type=number],.pure-form input[type=search],.pure-form input[type=tel],.pure-form input[type=color],.pure-form select,.pure-form textarea{padding:.5em .6em;display:inline-block;border:1px solid #ccc;box-shadow:inset 0 1px 3px #ddd;border-radius:4px;vertical-align:middle;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.pure-form input:not([type]){padding:.5em .6em;display:inline-block;border:1px solid #ccc;box-shadow:inset 0 1px 3px #ddd;border-radius:4px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.pure-form input[type=color]{padding:.2em .5em}.pure-form input[type=text]:focus,.pure-form input[type=password]:focus,.pure-form input[type=email]:focus,.pure-form input[type=url]:focus,.pure-form input[type=date]:focus,.pure-form input[type=month]:focus,.pure-form input[type=time]:focus,.pure-form input[type=datetime]:focus,.pure-form input[type=datetime-local]:focus,.pure-form input[type=week]:focus,.pure-form input[type=number]:focus,.pure-form input[type=search]:focus,.pure-form input[type=tel]:focus,.pure-form input[type=color]:focus,.pure-form select:focus,.pure-form textarea:focus{outline:0;border-color:#129FEA}.pure-form input:not([type]):focus{outline:0;border-color:#129FEA}.pure-form input[type=file]:focus,.pure-form input[type=radio]:focus,.pure-form input[type=checkbox]:focus{outline:thin solid #129FEA;outline:1px auto #129FEA}.pure-form .pure-checkbox,.pure-form .pure-radio{margin:.5em 0;display:block}.pure-form input[type=text][disabled],.pure-form input[type=password][disabled],.pure-form input[type=email][disabled],.pure-form input[type=url][disabled],.pure-form input[type=date][disabled],.pure-form input[type=month][disabled],.pure-form input[type=time][disabled],.pure-form input[type=datetime][disabled],.pure-form input[type=datetime-local][disabled],.pure-form input[type=week][disabled],.pure-form input[type=number][disabled],.pure-form input[type=search][disabled],.pure-form input[type=tel][disabled],.pure-form input[type=color][disabled],.pure-form select[disabled],.pure-form textarea[disabled]{cursor:not-allowed;background-color:#eaeded;color:#cad2d3}.pure-form input:not([type])[disabled]{cursor:not-allowed;background-color:#eaeded;color:#cad2d3}.pure-form input[readonly],.pure-form select[readonly],.pure-form textarea[readonly]{background-color:#eee;color:#777;border-color:#ccc}.pure-form input:focus:invalid,.pure-form textarea:focus:invalid,.pure-form select:focus:invalid{color:#b94a48;border-color:#e9322d}.pure-form input[type=file]:focus:invalid:focus,.pure-form input[type=radio]:focus:invalid:focus,.pure-form input[type=checkbox]:focus:invalid:focus{outline-color:#e9322d}.pure-form select{height:2.25em;border:1px solid #ccc;background-color:#fff}.pure-form select[multiple]{height:auto}.pure-form label{margin:.5em 0 .2em}.pure-form fieldset{margin:0;padding:.35em 0 .75em;border:0}.pure-form legend{display:block;width:100%;padding:.3em 0;margin-bottom:.3em;color:#333;border-bottom:1px solid #e5e5e5}.pure-form-stacked input[type=text],.pure-form-stacked input[type=password],.pure-form-stacked input[type=email],.pure-form-stacked input[type=url],.pure-form-stacked input[type=date],.pure-form-stacked input[type=month],.pure-form-stacked input[type=time],.pure-form-stacked input[type=datetime],.pure-form-stacked input[type=datetime-local],.pure-form-stacked input[type=week],.pure-form-stacked input[type=number],.pure-form-stacked input[type=search],.pure-form-stacked input[type=tel],.pure-form-stacked input[type=color],.pure-form-stacked input[type=file],.pure-form-stacked select,.pure-form-stacked label,.pure-form-stacked textarea{display:block;margin:.25em 0}.pure-form-stacked input:not([type]){display:block;margin:.25em 0}.pure-form-aligned input,.pure-form-aligned textarea,.pure-form-aligned select,.pure-form-aligned .pure-help-inline,.pure-form-message-inline{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.pure-form-aligned textarea{vertical-align:top}.pure-form-aligned .pure-control-group{margin-bottom:.5em}.pure-form-aligned .pure-control-group label{text-align:right;display:inline-block;vertical-align:middle;width:10em;margin:0 1em 0 0}.pure-form-aligned .pure-controls{margin:1.5em 0 0 11em}.pure-form input.pure-input-rounded,.pure-form .pure-input-rounded{border-radius:2em;padding:.5em 1em}.pure-form .pure-group fieldset{margin-bottom:10px}.pure-form .pure-group input,.pure-form .pure-group textarea{display:block;padding:10px;margin:0 0 -1px;border-radius:0;position:relative;top:-1px}.pure-form .pure-group input:focus,.pure-form .pure-group textarea:focus{z-index:3}.pure-form .pure-group input:first-child,.pure-form .pure-group textarea:first-child{top:1px;border-radius:4px 4px 0 0;margin:0}.pure-form .pure-group input:first-child:last-child,.pure-form .pure-group textarea:first-child:last-child{top:1px;border-radius:4px;margin:0}.pure-form .pure-group input:last-child,.pure-form .pure-group textarea:last-child{top:-2px;border-radius:0 0 4px 4px;margin:0}.pure-form .pure-group button{margin:.35em 0}.pure-form .pure-input-1{width:100%}.pure-form .pure-input-2-3{width:66%}.pure-form .pure-input-1-2{width:50%}.pure-form .pure-input-1-3{width:33%}.pure-form .pure-input-1-4{width:25%}.pure-form .pure-help-inline,.pure-form-message-inline{display:inline-block;padding-left:.3em;color:#666;vertical-align:middle;font-size:.875em}.pure-form-message{display:block;color:#666;font-size:.875em}@media only screen and (max-width :480px){.pure-form button[type=submit]{margin:.7em 0 0}.pure-form input:not([type]),.pure-form input[type=text],.pure-form input[type=password],.pure-form input[type=email],.pure-form input[type=url],.pure-form input[type=date],.pure-form input[type=month],.pure-form input[type=time],.pure-form input[type=datetime],.pure-form input[type=datetime-local],.pure-form input[type=week],.pure-form input[type=number],.pure-form input[type=search],.pure-form input[type=tel],.pure-form input[type=color],.pure-form label{margin-bottom:.3em;display:block}.pure-group input:not([type]),.pure-group input[type=text],.pure-group input[type=password],.pure-group input[type=email],.pure-group input[type=url],.pure-group input[type=date],.pure-group input[type=month],.pure-group input[type=time],.pure-group input[type=datetime],.pure-group input[type=datetime-local],.pure-group input[type=week],.pure-group input[type=number],.pure-group input[type=search],.pure-group input[type=tel],.pure-group input[type=color]{margin-bottom:0}.pure-form-aligned .pure-control-group label{margin-bottom:.3em;text-align:left;display:block;width:100%}.pure-form-aligned .pure-controls{margin:1.5em 0 0}.pure-form .pure-help-inline,.pure-form-message-inline,.pure-form-message{display:block;font-size:.75em;padding:.2em 0 .8em}}.pure-menu{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.pure-menu-fixed{position:fixed;left:0;top:0;z-index:3}.pure-menu-list,.pure-menu-item{position:relative}.pure-menu-list{list-style:none;margin:0;padding:0}.pure-menu-item{padding:0;margin:0;height:100%}.pure-menu-link,.pure-menu-heading{display:block;text-decoration:none;white-space:nowrap}.pure-menu-horizontal{width:100%;white-space:nowrap}.pure-menu-horizontal .pure-menu-list{display:inline-block}.pure-menu-horizontal .pure-menu-item,.pure-menu-horizontal .pure-menu-heading,.pure-menu-horizontal .pure-menu-separator{display:inline-block;*display:inline;zoom:1;vertical-align:middle}.pure-menu-item .pure-menu-item{display:block}.pure-menu-children{display:none;position:absolute;left:100%;top:0;margin:0;padding:0;z-index:3}.pure-menu-horizontal .pure-menu-children{left:0;top:auto;width:inherit}.pure-menu-allow-hover:hover>.pure-menu-children,.pure-menu-active>.pure-menu-children{display:block;position:absolute}.pure-menu-has-children>.pure-menu-link:after{padding-left:.5em;content:"\25B8";font-size:small}.pure-menu-horizontal .pure-menu-has-children>.pure-menu-link:after{content:"\25BE"}.pure-menu-scrollable{overflow-y:scroll;overflow-x:hidden}.pure-menu-scrollable .pure-menu-list{display:block}.pure-menu-horizontal.pure-menu-scrollable .pure-menu-list{display:inline-block}.pure-menu-horizontal.pure-menu-scrollable{white-space:nowrap;overflow-y:hidden;overflow-x:auto;-ms-overflow-style:none;-webkit-overflow-scrolling:touch;padding:.5em 0}.pure-menu-horizontal.pure-menu-scrollable::-webkit-scrollbar{display:none}.pure-menu-separator{background-color:#ccc;height:1px;margin:.3em 0}.pure-menu-horizontal .pure-menu-separator{width:1px;height:1.3em;margin:0 .3em}.pure-menu-heading{text-transform:uppercase;color:#565d64}.pure-menu-link{color:#777}.pure-menu-children{background-color:#fff}.pure-menu-link,.pure-menu-disabled,.pure-menu-heading{padding:.5em 1em}.pure-menu-disabled{opacity:.5}.pure-menu-disabled .pure-menu-link:hover{background-color:transparent}.pure-menu-active>.pure-menu-link,.pure-menu-link:hover,.pure-menu-link:focus{background-color:#eee}.pure-menu-selected .pure-menu-link,.pure-menu-selected .pure-menu-link:visited{color:#000}.pure-table{border-collapse:collapse;border-spacing:0;empty-cells:show;border:1px solid #cbcbcb}.pure-table caption{color:#000;font:italic 85%/1 arial,sans-serif;padding:1em 0;text-align:center}.pure-table td,.pure-table th{border-left:1px solid #cbcbcb;border-width:0 0 0 1px;font-size:inherit;margin:0;overflow:visible;padding:.5em 1em}.pure-table td:first-child,.pure-table th:first-child{border-left-width:0}.pure-table thead{background-color:#e0e0e0;color:#000;text-align:left;vertical-align:bottom}.pure-table td{background-color:transparent}.pure-table-odd td{background-color:#f2f2f2}.pure-table-striped tr:nth-child(2n-1) td{background-color:#f2f2f2}.pure-table-bordered td{border-bottom:1px solid #cbcbcb}.pure-table-bordered tbody>tr:last-child>td{border-bottom-width:0}.pure-table-horizontal td,.pure-table-horizontal th{border-width:0 0 1px;border-bottom:1px solid #cbcbcb}.pure-table-horizontal tbody>tr:last-child>td{border-bottom-width:0} \ No newline at end of file