public function toHTML($asSimpleXML=false) { $html = new SimpleXMLElement('<table/>'); /* // Title $tr = $html->addChild('tr'); $tr->addAttribute('class', 'title'); $tr->addChild('th', Zotero_ItemFields::getLocalizedString(false, 'title')); $tr->addChild('td', htmlspecialchars($item->getDisplayTitle(true))); */ // Item type Zotero_Atom::addHTMLRow( $html, "itemType", Zotero_ItemFields::getLocalizedString(false, 'itemType'), Zotero_ItemTypes::getLocalizedString($this->itemTypeID) ); // Creators $creators = $this->getCreators(); if ($creators) { $displayText = ''; foreach ($creators as $creator) { // Two fields if ($creator['ref']->fieldMode == 0) { $displayText = $creator['ref']->firstName . ' ' . $creator['ref']->lastName; } // Single field else if ($creator['ref']->fieldMode == 1) { $displayText = $creator['ref']->lastName; } else { // TODO } Zotero_Atom::addHTMLRow( $html, "creator", Zotero_CreatorTypes::getLocalizedString($creator['creatorTypeID']), trim($displayText) ); } } $primaryFields = array(); $fields = array_merge($primaryFields, $this->getUsedFields()); foreach ($fields as $field) { if (Zotero_Items::isPrimaryField($field)) { $fieldName = $field; } else { $fieldName = Zotero_ItemFields::getName($field); } // Skip certain fields switch ($fieldName) { case '': case 'userID': case 'libraryID': case 'key': case 'itemTypeID': case 'itemID': case 'title': case 'serverDateModified': case 'version': continue 2; } if (Zotero_ItemFields::isFieldOfBase($fieldName, 'title')) { continue; } $localizedFieldName = Zotero_ItemFields::getLocalizedString(false, $field); $value = $this->getField($field); $value = trim($value); // Skip empty fields if (!$value) { continue; } $fieldText = ''; // Shorten long URLs manually until Firefox wraps at ? // (like Safari) or supports the CSS3 word-wrap property if (false && preg_match("'https?://'", $value)) { $fieldText = $value; $firstSpace = strpos($value, ' '); // Break up long uninterrupted string if (($firstSpace === false && strlen($value) > 29) || $firstSpace > 29) { $stripped = false; /* // Strip query string for sites we know don't need it for each(var re in _noQueryStringSites) { if (re.test($field)){ var pos = $field.indexOf('?'); if (pos != -1) { fieldText = $field.substr(0, pos); stripped = true; } break; } } */ if (!$stripped) { // Add a line-break after the ? of long URLs //$fieldText = str_replace($field.replace('?', "?<ZOTEROBREAK/>"); // Strip query string variables from the end while the // query string is longer than the main part $pos = strpos($fieldText, '?'); if ($pos !== false) { while ($pos < (strlen($fieldText) / 2)) { $lastAmp = strrpos($fieldText, '&'); if ($lastAmp === false) { break; } $fieldText = substr($fieldText, 0, $lastAmp); $shortened = true; } // Append '&...' to the end if ($shortened) { $fieldText .= "&…"; } } } } if ($field == 'url') { $linkContainer = new SimpleXMLElement("<container/>"); $linkContainer->a = $value; $linkContainer->a['href'] = $fieldText; } } // Remove SQL date from multipart dates // (e.g. '2006-00-00 Summer 2006' becomes 'Summer 2006') else if ($fieldName == 'date') { $fieldText = $value; } // Convert dates to local format else if ($fieldName == 'accessDate' || $fieldName == 'dateAdded' || $fieldName == 'dateModified') { //$date = Zotero.Date.sqlToDate($field, true) $date = $value; //fieldText = escapeXML(date.toLocaleString()); $fieldText = $date; } else { $fieldText = $value; } if (isset($linkContainer)) { $tr = Zotero_Atom::addHTMLRow($html, $fieldName, $localizedFieldName, "", true); $tdNode = dom_import_simplexml($tr->td); $linkNode = dom_import_simplexml($linkContainer->a); $importedNode = $tdNode->ownerDocument->importNode($linkNode, true); $tdNode->appendChild($importedNode); unset($linkContainer); } else { Zotero_Atom::addHTMLRow($html, $fieldName, $localizedFieldName, $fieldText); } } if ($this->isNote() || $this->isAttachment()) { $note = $this->getNote(true); if ($note) { $tr = Zotero_Atom::addHTMLRow($html, "note", "Note", "", true); try { $noteXML = @new SimpleXMLElement("<td>" . $note . "</td>"); $trNode = dom_import_simplexml($tr); $tdNode = $trNode->getElementsByTagName("td")->item(0); $noteNode = dom_import_simplexml($noteXML); $importedNode = $trNode->ownerDocument->importNode($noteNode, true); $trNode->replaceChild($importedNode, $tdNode); unset($noteXML); } catch (Exception $e) { // Store non-HTML notes as <pre> $tr->td->pre = $note; } } } if ($this->isAttachment()) { Zotero_Atom::addHTMLRow($html, "linkMode", "Link Mode", $this->attachmentLinkMode); Zotero_Atom::addHTMLRow($html, "mimeType", "MIME Type", $this->attachmentMIMEType); Zotero_Atom::addHTMLRow($html, "charset", "Character Set", $this->attachmentCharset); // TODO: get from a constant /*if ($this->attachmentLinkMode != 3) { $doc->addField('path', $this->attachmentPath); }*/ } if ($this->getDeleted()) { Zotero_Atom::addHTMLRow($html, "deleted", "Deleted", "Yes"); } if ($asSimpleXML) { return $html; } return str_replace('<?xml version="1.0"?>', '', $html->asXML()); }
/** * JSON type/field data */ public function mappings() { if (!empty($_GET['locale']) && $_GET['locale'] != 'en-US') { $this->e400("Non-English locales are not yet supported"); } $locale = empty($_GET['locale']) ? 'en-US' : $_GET['locale']; if ($this->subset == 'itemTypeFields') { if (empty($_GET['itemType'])) { $this->e400("'itemType' not provided"); } $itemType = $_GET['itemType']; $itemTypeID = Zotero_ItemTypes::getID($itemType); if (!$itemTypeID) { $this->e400("Invalid item type '{$itemType}'"); } } else { if ($this->subset == 'itemTypeCreatorTypes') { if (empty($_GET['itemType'])) { $this->e400("'itemType' not provided"); } $itemType = $_GET['itemType']; $itemTypeID = Zotero_ItemTypes::getID($itemType); if (!$itemTypeID) { $this->e400("Invalid item type '{$itemType}'"); } // Notes and attachments don't have creators if ($itemType == 'note' || $itemType == 'attachment') { echo "[]"; exit; } } } // TODO: check If-Modified-Since and return 304 if not changed $cacheKey = $this->subset . "JSON"; if (isset($itemTypeID)) { $cacheKey .= "_" . $itemTypeID; } $ttl = 60; if ($this->queryParams['pprint']) { $cacheKey .= "_pprint"; } $json = Z_Core::$MC->get($cacheKey); if ($json) { if ($this->queryParams['pprint']) { header("Content-Type: text/plain"); } else { header("Content-Type: application/json"); } echo $json; exit; } switch ($this->subset) { case 'itemTypes': $rows = Zotero_ItemTypes::getAll($locale); $propName = 'itemType'; break; case 'itemTypeFields': $fieldIDs = Zotero_ItemFields::getItemTypeFields($itemTypeID); $rows = array(); foreach ($fieldIDs as $fieldID) { $fieldName = Zotero_ItemFields::getName($fieldID); $rows[] = array('name' => $fieldName, 'localized' => Zotero_ItemFields::getLocalizedString($itemTypeID, $fieldName, $locale)); } $propName = 'field'; break; case 'itemFields': $rows = Zotero_ItemFields::getAll($locale); $propName = 'field'; break; case 'itemTypeCreatorTypes': $rows = Zotero_CreatorTypes::getTypesForItemType($itemTypeID, $locale); $propName = 'creatorType'; break; case 'creatorFields': $rows = Zotero_Creators::getLocalizedFieldNames(); $propName = 'field'; break; } $json = array(); foreach ($rows as $row) { $json[] = array($propName => $row['name'], 'localized' => $row['localized']); } if ($this->queryParams['pprint']) { header("Content-Type: text/plain"); $json = Zotero_Utilities::json_encode_pretty($json); Z_Core::$MC->set($cacheKey, $json, $ttl); } else { header("Content-Type: application/json"); $json = json_encode($json); Z_Core::$MC->set($cacheKey, $json, $ttl); } echo $json; exit; }
public static function checkUploadForErrors($syncProcessID) { Zotero_DB::beginTransaction(); if (Z_Core::probability(30)) { $sql = "UPDATE syncUploadQueue SET started=NULL, errorCheck=0 WHERE started IS NOT NULL AND errorCheck=1 AND\n\t\t\t\t\t\tstarted < (NOW() - INTERVAL 15 MINUTE) AND finished IS NULL"; Zotero_DB::query($sql); } // Get a queued process that hasn't been error-checked and is large enough to warrant it $sql = "SELECT * FROM syncUploadQueue WHERE started IS NULL AND errorCheck=0\n\t\t\t\tAND dataLength>=" . self::$minErrorCheckSize . " ORDER BY added LIMIT 1 FOR UPDATE"; $row = Zotero_DB::rowQuery($sql); // No pending processes if (!$row) { Zotero_DB::commit(); return 0; } $sql = "UPDATE syncUploadQueue SET started=NOW(), errorCheck=1 WHERE syncUploadQueueID=?"; Zotero_DB::query($sql, array($row['syncUploadQueueID'])); // We track error processes as upload processes that just get reset back to // started=NULL on completion (but with errorCheck=2) self::addUploadProcess($row['userID'], null, $row['syncUploadQueueID'], $syncProcessID); Zotero_DB::commit(); try { $doc = new DOMDocument(); $doc->loadXML($row['xmldata']); // Get long tags $value = Zotero_Tags::getLongDataValueFromXML($doc); if ($value) { throw new Exception("Tag '" . $value . "' too long", Z_ERROR_TAG_TOO_LONG); } // Get long collection names $value = Zotero_Collections::getLongDataValueFromXML($doc); if ($value) { throw new Exception("Collection '" . $value . "' too long", Z_ERROR_COLLECTION_TOO_LONG); } // Get long creator names $node = Zotero_Creators::getLongDataValueFromXML($doc); // returns DOMNode rather than value if ($node) { if ($node->nodeName == 'firstName') { throw new Exception("=First name '" . mb_substr($node->nodeValue, 0, 50) . "…' too long"); } if ($node->nodeName == 'lastName') { throw new Exception("=Last name '" . mb_substr($node->nodeValue, 0, 50) . "…' too long"); } if ($node->nodeName == 'name') { throw new Exception("=Name '" . mb_substr($node->nodeValue, 0, 50) . "…' too long"); } } $node = Zotero_Items::getLongDataValueFromXML($doc); // returns DOMNode rather than value if ($node) { $fieldName = $node->getAttribute('name'); $fieldName = Zotero_ItemFields::getLocalizedString(null, $fieldName); if ($fieldName) { $start = "'{$fieldName}' field"; } else { $start = "Field"; } throw new Exception("={$start} value '" . mb_substr($node->nodeValue, 0, 50) . "...' too long"); } } catch (Exception $e) { //Z_Core::logError($e); Zotero_DB::beginTransaction(); $sql = "UPDATE syncUploadQueue SET syncProcessID=NULL, finished=?,\n\t\t\t\t\t\terrorCode=?, errorMessage=? WHERE syncUploadQueueID=?"; Zotero_DB::query($sql, array(Zotero_DB::getTransactionTimestamp(), $e->getCode(), serialize($e), $row['syncUploadQueueID'])); $sql = "DELETE FROM syncUploadQueueLocks WHERE syncUploadQueueID=?"; Zotero_DB::query($sql, $row['syncUploadQueueID']); self::removeUploadProcess($syncProcessID); Zotero_DB::commit(); return -2; } Zotero_DB::beginTransaction(); $sql = "UPDATE syncUploadQueue SET syncProcessID=NULL, started=NULL, errorCheck=2 WHERE syncUploadQueueID=?"; Zotero_DB::query($sql, $row['syncUploadQueueID']); self::removeUploadProcess($syncProcessID); Zotero_DB::commit(); return 1; }
public static function checkUploadForErrors($syncProcessID) { Zotero_DB::beginTransaction(); if (Z_Core::probability(30)) { $sql = "UPDATE syncUploadQueue SET started=NULL, errorCheck=0 WHERE started IS NOT NULL AND errorCheck=1 AND\n\t\t\t\t\t\tstarted < (NOW() - INTERVAL 15 MINUTE) AND finished IS NULL"; Zotero_DB::query($sql); } // Get a queued process that hasn't been error-checked and is large enough to warrant it $sql = "SELECT * FROM syncUploadQueue WHERE started IS NULL AND errorCheck=0\n\t\t\t\tAND dataLength>=" . self::$minErrorCheckSize . " ORDER BY added LIMIT 1 FOR UPDATE"; $row = Zotero_DB::rowQuery($sql); // No pending processes if (!$row) { Zotero_DB::commit(); return 0; } $sql = "UPDATE syncUploadQueue SET started=NOW(), errorCheck=1 WHERE syncUploadQueueID=?"; Zotero_DB::query($sql, array($row['syncUploadQueueID'])); // We track error processes as upload processes that just get reset back to // started=NULL on completion (but with errorCheck=2) self::addUploadProcess($row['userID'], null, $row['syncUploadQueueID'], $syncProcessID); Zotero_DB::commit(); try { $doc = new DOMDocument(); $doc->loadXML($row['xmldata'], LIBXML_COMPACT | LIBXML_PARSEHUGE); // Get long tags $value = Zotero_Tags::getLongDataValueFromXML($doc); if ($value) { throw new Exception("Tag '" . $value . "' too long", Z_ERROR_TAG_TOO_LONG); } // Get long collection names $value = Zotero_Collections::getLongDataValueFromXML($doc); if ($value) { throw new Exception("Collection '" . $value . "' too long", Z_ERROR_COLLECTION_TOO_LONG); } // Get long creator names $node = Zotero_Creators::getLongDataValueFromXML($doc); // returns DOMNode rather than value if ($node) { $name = mb_substr($node->nodeValue, 0, 50); throw new Exception("=The name ‘{$name}…’ is too long to sync.\n\n" . "Search for the item with this name and shorten it. " . "Note that the item may be in the trash or in a group library.\n\n" . "If you receive this message repeatedly for items saved from a " . "particular site, you can report this issue in the Zotero Forums.", Z_ERROR_CREATOR_TOO_LONG); } // Get long item data fields $node = Zotero_Items::getLongDataValueFromXML($doc); // returns DOMNode rather than value if ($node) { $libraryID = $node->parentNode->getAttribute('libraryID'); $key = $node->parentNode->getAttribute('key'); if ($libraryID) { $key = $libraryID . "/" . $key; } $fieldName = $node->getAttribute('name'); $fieldName = Zotero_ItemFields::getLocalizedString(null, $fieldName); if ($fieldName) { $start = "{$fieldName} field"; } else { $start = "Field"; } throw new Exception("={$start} value '" . mb_substr($node->nodeValue, 0, 75) . "...' too long for item '{$key}'", Z_ERROR_FIELD_TOO_LONG); } } catch (Exception $e) { //Z_Core::logError($e); Zotero_DB::beginTransaction(); $sql = "UPDATE syncUploadQueue SET syncProcessID=NULL, finished=?,\n\t\t\t\t\t\terrorCode=?, errorMessage=? WHERE syncUploadQueueID=?"; Zotero_DB::query($sql, array(Zotero_DB::getTransactionTimestamp(), $e->getCode(), serialize($e), $row['syncUploadQueueID'])); $sql = "DELETE FROM syncUploadQueueLocks WHERE syncUploadQueueID=?"; Zotero_DB::query($sql, $row['syncUploadQueueID']); self::removeUploadProcess($syncProcessID); Zotero_DB::commit(); return -2; } Zotero_DB::beginTransaction(); $sql = "UPDATE syncUploadQueue SET syncProcessID=NULL, started=NULL, errorCheck=2 WHERE syncUploadQueueID=?"; Zotero_DB::query($sql, $row['syncUploadQueueID']); self::removeUploadProcess($syncProcessID); Zotero_DB::commit(); return 1; }
/** * JSON type/field data */ public function mappings() { if (!empty($_GET['locale']) && $_GET['locale'] != 'en-US') { $this->e400("Non-English locales are not yet supported"); } $locale = empty($_GET['locale']) ? 'en-US' : $_GET['locale']; if ($this->subset == 'itemTypeFields') { if (empty($_GET['itemType'])) { $this->e400("'itemType' not provided"); } $itemType = $_GET['itemType']; $itemTypeID = Zotero_ItemTypes::getID($itemType); if (!$itemTypeID) { $this->e400("Invalid item type '{$itemType}'"); } } else { if ($this->subset == 'itemTypeCreatorTypes') { if (empty($_GET['itemType'])) { $this->e400("'itemType' not provided"); } $itemType = $_GET['itemType']; $itemTypeID = Zotero_ItemTypes::getID($itemType); if (!$itemTypeID) { $this->e400("Invalid item type '{$itemType}'"); } // Notes and attachments don't have creators if ($itemType == 'note' || $itemType == 'attachment') { echo "[]"; exit; } } } // TODO: check If-Modified-Since and return 304 if not changed $cacheKey = $this->subset . "JSON"; if (isset($itemTypeID)) { $cacheKey .= "_" . $itemTypeID; } $cacheKey .= '_' . $this->apiVersion; $ttl = 60; $json = Z_Core::$MC->get($cacheKey); if ($json) { header("Content-Type: application/json"); echo $json; exit; } switch ($this->subset) { case 'itemTypes': $rows = Zotero_ItemTypes::getAll($locale); $propName = 'itemType'; break; case 'itemTypeFields': $fieldIDs = Zotero_ItemFields::getItemTypeFields($itemTypeID); $rows = array(); foreach ($fieldIDs as $fieldID) { $fieldName = Zotero_ItemFields::getName($fieldID); $rows[] = array('id' => $fieldID, 'name' => $fieldName, 'localized' => Zotero_ItemFields::getLocalizedString($itemTypeID, $fieldName, $locale)); } $propName = 'field'; break; case 'itemFields': $rows = Zotero_ItemFields::getAll($locale); $propName = 'field'; break; case 'itemTypeCreatorTypes': $rows = Zotero_CreatorTypes::getTypesForItemType($itemTypeID, $locale); $propName = 'creatorType'; break; case 'creatorFields': $rows = Zotero_Creators::getLocalizedFieldNames(); $propName = 'field'; break; } $json = array(); foreach ($rows as $row) { // Before v3, computerProgram's 'versionNumber' was just 'version' if ($this->apiVersion < 3 && ($this->subset == 'itemTypeFields' || $this->subset == 'itemFields') && $row['id'] == 81) { $row['name'] = 'version'; } $json[] = array($propName => $row['name'], 'localized' => $row['localized']); } header("Content-Type: application/json"); $json = Zotero_Utilities::formatJSON($json); Z_Core::$MC->set($cacheKey, $json, $ttl); echo $json; exit; }