public function itemToAtom($itemID) { if (!is_int($itemID)) { throw new Exception("itemID must be an integer (was " . gettype($itemID) . ")"); } if (!$this->loaded) { $this->load(); } //$groupUserData = $this->getUserData($itemID); $item = Zotero_Items::get($this->libraryID, $itemID); if (!$item) { throw new Exception("Item {$itemID} doesn't exist"); } $xml = new SimpleXMLElement('<?xml version="1.0" encoding="UTF-8"?>' . '<entry xmlns="' . Zotero_Atom::$nsAtom . '" ' . 'xmlns:zapi="' . Zotero_Atom::$nsZoteroAPI . '" ' . 'xmlns:xfer="' . Zotero_Atom::$nsZoteroTransfer . '"/>'); $title = $item->getDisplayTitle(true); $title = $title ? $title : '[Untitled]'; // Strip HTML from note titles if ($item->isNote()) { // Clean and strip HTML, giving us an HTML-encoded plaintext string $title = strip_tags(Zotero_Notes::sanitize($title)); // Unencode plaintext string $title = html_entity_decode($title); } $xml->title = $title; $author = $xml->addChild('author'); $author->name = Zotero_Libraries::getName($item->libraryID); $author->uri = Zotero_URI::getLibraryURI($item->libraryID); $xml->id = Zotero_URI::getItemURI($item); $xml->published = Zotero_Date::sqlToISO8601($item->dateAdded); $xml->updated = Zotero_Date::sqlToISO8601($item->dateModified); $link = $xml->addChild("link"); $link['rel'] = "self"; $link['type'] = "application/atom+xml"; $link['href'] = Zotero_API::getItemURI($item); $link = $xml->addChild('link'); $link['rel'] = 'alternate'; $link['type'] = 'text/html'; $link['href'] = Zotero_URI::getItemURI($item, true); $xml->content['type'] = 'application/xml'; $itemXML = new SimpleXMLElement('<item xmlns="' . Zotero_Atom::$nsZoteroTransfer . '"/>'); // This method of adding the element seems to be necessary to get the // namespace prefix to show up $fNode = dom_import_simplexml($xml->content); $subNode = dom_import_simplexml($itemXML); $importedNode = $fNode->ownerDocument->importNode($subNode, true); $fNode->appendChild($importedNode); $xml->content->item['id'] = $itemID; return $xml; }
private function getNoteHash() { if (!$this->isNote() && !$this->isAttachment()) { trigger_error("getNoteHash() can only be called on notes and attachments", E_USER_ERROR); } if (!$this->id) { return ''; } // Store access time for later garbage collection //$this->noteAccessTime = new Date(); return Zotero_Notes::getHash($this->libraryID, $this->id); }
/** * Handle uploaded data, overwriting existing data */ public function upload() { $this->sessionCheck(); // Another session is either queued or writing — upload data won't be valid, // so client should wait and return to /updated with 'upload' flag Zotero_DB::beginTransaction(); if (Zotero_Sync::userIsReadLocked($this->userID) || Zotero_Sync::userIsWriteLocked($this->userID)) { Zotero_DB::commit(); $locked = $this->responseXML->addChild('locked'); $locked['wait'] = $this->getWaitTime($this->sessionID); $this->end(); } Zotero_DB::commit(); $this->clearWaitTime($this->sessionID); if (empty($_REQUEST['updateKey'])) { $this->error(400, 'INVALID_UPLOAD_DATA', 'Update key not provided'); } if ($_REQUEST['updateKey'] != Zotero_Users::getUpdateKey($this->userID)) { $this->e409("Server data has changed since last retrieval"); } // TODO: change to POST if (empty($_REQUEST['data'])) { $this->error(400, 'MISSING_UPLOAD_DATA', 'Uploaded data not provided'); } $xmldata =& $_REQUEST['data']; try { $doc = new DOMDocument(); $doc->loadXML($xmldata, LIBXML_PARSEHUGE); // For huge uploads, make sure notes aren't bigger than SimpleXML can parse if (strlen($xmldata) > 7000000) { $xpath = new DOMXPath($doc); $results = $xpath->query('/data/items/item/note[string-length(text()) > ' . Zotero_Notes::$MAX_NOTE_LENGTH . ']'); if ($results->length) { $noteElem = $results->item(0); $text = $noteElem->textContent; $libraryID = $noteElem->parentNode->getAttribute('libraryID'); $key = $noteElem->parentNode->getAttribute('key'); // UTF-8 (0xC2 0xA0) isn't trimmed by default $whitespace = chr(0x20) . chr(0x9) . chr(0xa) . chr(0xd) . chr(0x0) . chr(0xb) . chr(0xc2) . chr(0xa0); $excerpt = iconv("UTF-8", "UTF-8//IGNORE", Zotero_Notes::noteToTitle(trim($text), true)); $excerpt = trim($excerpt, $whitespace); // If tag-stripped version is empty, just return raw HTML if ($excerpt == '') { $excerpt = iconv("UTF-8", "UTF-8//IGNORE", preg_replace('/\\s+/', ' ', mb_substr(trim($text), 0, Zotero_Notes::$MAX_TITLE_LENGTH))); $excerpt = html_entity_decode($excerpt); $excerpt = trim($excerpt, $whitespace); } $msg = "=Note '" . $excerpt . "...' too long"; if ($key) { $msg .= " for item '" . $libraryID . "/" . $key . "'"; } throw new Exception($msg, Z_ERROR_NOTE_TOO_LONG); } } } catch (Exception $e) { $this->handleUploadError($e, $xmldata); } function relaxNGErrorHandler($errno, $errstr) { //Z_Core::logError($errstr); } set_error_handler('relaxNGErrorHandler'); set_time_limit(60); if (!$doc->relaxNGValidate(Z_ENV_MODEL_PATH . 'relax-ng/upload.rng')) { $id = substr(md5(uniqid(rand(), true)), 0, 10); $str = date("D M j G:i:s T Y") . "\n"; $str .= "IP address: " . $_SERVER['REMOTE_ADDR'] . "\n"; if (isset($_SERVER['HTTP_X_ZOTERO_VERSION'])) { $str .= "Version: " . $_SERVER['HTTP_X_ZOTERO_VERSION'] . "\n"; } $str .= "Error: RELAX NG validation failed\n\n"; $str .= $xmldata; if (!file_put_contents(Z_CONFIG::$SYNC_ERROR_PATH . $id, $str)) { error_log("Unable to save error report to " . Z_CONFIG::$SYNC_ERROR_PATH . $id); } $this->error(500, 'INVALID_UPLOAD_DATA', "Uploaded data not well-formed (Report ID: {$id})"); } restore_error_handler(); try { $xml = simplexml_import_dom($doc); $queue = true; if (Z_ENV_TESTING_SITE && !empty($_GET['noqueue'])) { $queue = false; } if ($queue) { $affectedLibraries = Zotero_Sync::parseAffectedLibraries($xmldata); // Relations-only uploads don't have affected libraries if (!$affectedLibraries) { $affectedLibraries = array(Zotero_Users::getLibraryIDFromUserID($this->userID)); } Zotero_Sync::queueUpload($this->userID, $this->sessionID, $xmldata, $affectedLibraries); try { Zotero_Processors::notifyProcessors('upload'); Zotero_Processors::notifyProcessors('error'); usleep(750000); } catch (Exception $e) { Z_Core::logError($e); } // Give processor a chance to finish while we're still here $this->uploadstatus(); } else { set_time_limit(210); $timestamp = Zotero_Sync::processUpload($this->userID, $xml); $this->responseXML['timestamp'] = $timestamp; $this->responseXML->addChild('uploaded'); $this->end(); } } catch (Exception $e) { $this->handleUploadError($e, $xmldata); } }