public static function getLibraryURI($libraryID, $skipNames = false) { $libraryType = Zotero_Libraries::getType($libraryID); switch ($libraryType) { case 'user': $id = Zotero_Users::getUserIDFromLibraryID($libraryID); return self::getUserURI($id, $skipNames); case 'group': $id = Zotero_Groups::getGroupIDFromLibraryID($libraryID); $group = Zotero_Groups::get($id); return self::getGroupURI($group, $skipNames); } }
public static function getLibraryURI($libraryID, $www = false, $useSlug = false) { $libraryType = Zotero_Libraries::getType($libraryID); switch ($libraryType) { case 'user': $id = Zotero_Users::getUserIDFromLibraryID($libraryID); return self::getUserURI($id, $www, $useSlug); case 'publications': $id = Zotero_Users::getUserIDFromLibraryID($libraryID); return self::getUserURI($id, $www, $useSlug) . "/publications"; case 'group': $id = Zotero_Groups::getGroupIDFromLibraryID($libraryID); $group = Zotero_Groups::get($id); return self::getGroupURI($group, $www, $useSlug); default: throw new Exception("Invalid library type '{$libraryType}'"); } }
public static function getLibraryURI($libraryID) { $libraryType = Zotero_Libraries::getType($libraryID); switch ($libraryType) { case 'user': $id = Zotero_Users::getUserIDFromLibraryID($libraryID); return self::getBaseURI() . "users/{$id}"; case 'publications': $id = Zotero_Users::getUserIDFromLibraryID($libraryID); return self::getBaseURI() . "users/{$id}/publications"; case 'group': $id = Zotero_Groups::getGroupIDFromLibraryID($libraryID); return self::getBaseURI() . "groups/{$id}"; default: throw new Exception("Invalid library type '{$libraryType}'"); } }
public static function getUserUsage($userID) { $usage = array(); $libraryID = Zotero_Users::getLibraryIDFromUserID($userID); $sql = "SELECT SUM(size) AS bytes FROM storageFileItems\n\t\t\t\tJOIN items USING (itemID) WHERE libraryID=?"; $libraryBytes = Zotero_DB::valueQuery($sql, $libraryID, Zotero_Shards::getByLibraryID($libraryID)); $usage['library'] = round($libraryBytes / 1024 / 1024, 1); $groupBytes = 0; $usage['groups'] = array(); $ownedLibraries = Zotero_Groups::getUserOwnedGroupLibraries($userID); if ($ownedLibraries) { $shardIDs = Zotero_Groups::getUserGroupShards($userID); foreach ($shardIDs as $shardID) { $sql = "SELECT libraryID, SUM(size) AS `bytes` FROM storageFileItems\n\t\t\t\t\t\tJOIN items I USING (itemID)\n\t\t\t\t\t\tWHERE libraryID IN\n\t\t\t\t\t\t(" . implode(', ', array_fill(0, sizeOf($ownedLibraries), '?')) . ")\n\t\t\t\t\t\tGROUP BY libraryID WITH ROLLUP"; $libraries = Zotero_DB::query($sql, $ownedLibraries, $shardID); if ($libraries) { foreach ($libraries as $library) { if ($library['libraryID']) { $usage['groups'][] = array('id' => Zotero_Groups::getGroupIDFromLibraryID($library['libraryID']), 'usage' => round($library['bytes'] / 1024 / 1024, 1)); } else { $groupBytes += $library['bytes']; } } } } } $usage['total'] = round(($libraryBytes + $groupBytes) / 1024 / 1024, 1); return $usage; }
/** * Handle S3 request * * Permission-checking provided by items() */ private function _handleFileRequest($item) { if (!$this->permissions->canAccess($this->objectLibraryID, 'files')) { $this->e403(); } $this->allowMethods(array('HEAD', 'GET', 'POST', 'PATCH')); if (!$item->isAttachment()) { $this->e400("Item is not an attachment"); } // File info for client sync // // Use of HEAD method is deprecated after 2.0.8/2.1b1 due to // compatibility problems with proxies and security software if ($this->method == 'HEAD' || $this->method == 'GET' && $this->fileMode == 'info') { $info = Zotero_S3::getLocalFileItemInfo($item); if (!$info) { $this->e404(); } /* header("Last-Modified: " . gmdate('r', $info['uploaded'])); header("Content-Type: " . $info['type']); */ header("Content-Length: " . $info['size']); header("ETag: " . $info['hash']); header("X-Zotero-Filename: " . $info['filename']); header("X-Zotero-Modification-Time: " . $info['mtime']); header("X-Zotero-Compressed: " . ($info['zip'] ? 'Yes' : 'No')); header_remove("X-Powered-By"); } else { if ($this->method == 'GET' || $this->method == 'POST' && $this->fileView) { if ($this->fileView) { $info = Zotero_S3::getLocalFileItemInfo($item); if (!$info) { $this->e404(); } // For zip files, redirect to files domain if ($info['zip']) { $url = Zotero_Attachments::getTemporaryURL($item, !empty($_GET['int'])); if (!$url) { $this->e500(); } header("Location: {$url}"); exit; } } // For single files, redirect to S3 $url = Zotero_S3::getDownloadURL($item, 60); if (!$url) { $this->e404(); } Zotero_S3::logDownload($item, $this->userID, IPAddress::getIP()); header("Location: {$url}"); exit; } else { if ($this->method == 'POST' || $this->method == 'PATCH') { if (!$item->isImportedAttachment()) { $this->e400("Cannot upload file for linked file/URL attachment item"); } $libraryID = $item->libraryID; $type = Zotero_Libraries::getType($libraryID); if ($type == 'group') { $groupID = Zotero_Groups::getGroupIDFromLibraryID($libraryID); $group = Zotero_Groups::get($groupID); if (!$group->userCanEditFiles($this->userID)) { $this->e403("You do not have file editing access"); } } else { $group = null; } // If not the client, require If-Match or If-None-Match if (!$this->httpAuth) { if (empty($_SERVER['HTTP_IF_MATCH']) && empty($_SERVER['HTTP_IF_NONE_MATCH'])) { $this->e428("If-Match/If-None-Match header not provided"); } if (!empty($_SERVER['HTTP_IF_MATCH'])) { if (!preg_match('/^"?([a-f0-9]{32})"?$/', $_SERVER['HTTP_IF_MATCH'], $matches)) { $this->e400("Invalid ETag in If-Match header"); } if (!$item->attachmentStorageHash) { $info = Zotero_S3::getLocalFileItemInfo($item); $this->e412("ETag set but file does not exist"); } if ($item->attachmentStorageHash != $matches[1]) { $this->e412("ETag does not match current version of file"); } } else { if ($_SERVER['HTTP_IF_NONE_MATCH'] != "*") { $this->e400("Invalid value for If-None-Match header"); } if ($this->attachmentStorageHash) { $this->e412("If-None-Match: * set but file exists"); } } } // // Upload authorization // if (!isset($_POST['update']) && !isset($_REQUEST['upload'])) { $info = new Zotero_StorageFileInfo(); // Validate upload metadata if (empty($_REQUEST['md5'])) { $this->e400('MD5 hash not provided'); } $info->hash = $_REQUEST['md5']; if (!preg_match('/[abcdefg0-9]{32}/', $info->hash)) { $this->e400('Invalid MD5 hash'); } if (empty($_REQUEST['mtime'])) { $this->e400('File modification time not provided'); } $info->mtime = $_REQUEST['mtime']; if (!isset($_REQUEST['filename']) || $_REQUEST['filename'] === "") { $this->e400('File name not provided'); } $info->filename = $_REQUEST['filename']; if (!isset($_REQUEST['filesize'])) { $this->e400('File size not provided'); } $info->size = $_REQUEST['filesize']; if (!is_numeric($info->size)) { $this->e400("Invalid file size"); } $info->contentType = isset($_REQUEST['contentType']) ? $_REQUEST['contentType'] : ""; if (!preg_match("/^[a-zA-Z0-9\\-\\/]+\$/", $info->contentType)) { $info->contentType = ""; } $info->charset = isset($_REQUEST['charset']) ? $_REQUEST['charset'] : ""; if (!preg_match("/^[a-zA-Z0-9\\-]+\$/", $info->charset)) { $info->charset = ""; } $contentTypeHeader = $info->contentType . ($info->contentType && $info->charset ? "; charset=" . $info->charset : ""); $info->zip = !empty($_REQUEST['zip']); // Reject file if it would put account over quota if ($group) { $quota = Zotero_S3::getEffectiveUserQuota($group->ownerUserID); $usage = Zotero_S3::getUserUsage($group->ownerUserID); } else { $quota = Zotero_S3::getEffectiveUserQuota($this->objectUserID); $usage = Zotero_S3::getUserUsage($this->objectUserID); } $total = $usage['total']; $fileSizeMB = round($info->size / 1024 / 1024, 1); if ($total + $fileSizeMB > $quota) { $this->e413("File would exceed quota ({$total} + {$fileSizeMB} > {$quota})"); } Zotero_DB::query("SET TRANSACTION ISOLATION LEVEL SERIALIZABLE"); Zotero_DB::beginTransaction(); // See if file exists with this filename $localInfo = Zotero_S3::getLocalFileInfo($info); if ($localInfo) { $storageFileID = $localInfo['storageFileID']; // Verify file size if ($localInfo['size'] != $info->size) { throw new Exception("Specified file size incorrect for existing file " . $info->hash . "/" . $info->filename . " ({$localInfo['size']} != {$info->size})"); } } else { $oldStorageFileID = Zotero_S3::getFileByHash($info->hash, $info->zip); if ($oldStorageFileID) { // Verify file size $localInfo = Zotero_S3::getFileInfoByID($oldStorageFileID); if ($localInfo['size'] != $info->size) { throw new Exception("Specified file size incorrect for duplicated file " . $info->hash . "/" . $info->filename . " ({$localInfo['size']} != {$info->size})"); } // Create new file on S3 with new name $storageFileID = Zotero_S3::duplicateFile($oldStorageFileID, $info->filename, $info->zip, $contentTypeHeader); if (!$storageFileID) { $this->e500("File duplication failed"); } } } // If we already have a file, add/update storageFileItems row and stop if (!empty($storageFileID)) { Zotero_S3::updateFileItemInfo($item, $storageFileID, $info); Zotero_DB::commit(); if ($this->httpAuth) { header('Content-Type: application/xml'); echo "<exists/>"; } else { header('Content-Type: application/json'); echo json_encode(array('exists' => 1)); } exit; } Zotero_DB::commit(); // Add request to upload queue $uploadKey = Zotero_S3::queueUpload($this->userID, $info); // User over queue limit if (!$uploadKey) { header('Retry-After: ' . Zotero_S3::$uploadQueueTimeout); if ($this->httpAuth) { $this->e413("Too many queued uploads"); } else { $this->e429("Too many queued uploads"); } } // Output XML for client requests (which use HTTP Auth) if ($this->httpAuth) { $params = Zotero_S3::generateUploadPOSTParams($item, $info, true); header('Content-Type: application/xml'); $xml = new SimpleXMLElement('<upload/>'); $xml->url = Zotero_S3::getUploadBaseURL(); $xml->key = $uploadKey; foreach ($params as $key => $val) { $xml->params->{$key} = $val; } echo $xml->asXML(); } else { if (!empty($_REQUEST['params']) && $_REQUEST['params'] == "1") { $params = array("url" => Zotero_S3::getUploadBaseURL(), "params" => array()); foreach (Zotero_S3::generateUploadPOSTParams($item, $info) as $key => $val) { $params['params'][$key] = $val; } } else { $params = Zotero_S3::getUploadPOSTData($item, $info); } $params['uploadKey'] = $uploadKey; header('Content-Type: application/json'); echo json_encode($params); } exit; } // // API partial upload and post-upload file registration // if (isset($_REQUEST['upload'])) { $uploadKey = $_REQUEST['upload']; if (!$uploadKey) { $this->e400("Upload key not provided"); } $info = Zotero_S3::getUploadInfo($uploadKey); if (!$info) { $this->e400("Upload key not found"); } // Partial upload if ($this->method == 'PATCH') { if (empty($_REQUEST['algorithm'])) { throw new Exception("Algorithm not specified", Z_ERROR_INVALID_INPUT); } $storageFileID = Zotero_S3::patchFile($item, $info, $_REQUEST['algorithm'], $this->body); } else { $remoteInfo = Zotero_S3::getRemoteFileInfo($info); if (!$remoteInfo) { error_log("Remote file {$info->hash}/{$info->filename} not found"); $this->e400("Remote file not found"); } if ($remoteInfo['size'] != $info->size) { error_log("Uploaded file size does not match ({$remoteInfo['size']} != {$info->size}) for file {$info->hash}/{$info->filename}"); } } // Set an automatic shared lock in getLocalFileInfo() to prevent // two simultaneous transactions from adding a file Zotero_DB::query("SET TRANSACTION ISOLATION LEVEL SERIALIZABLE"); Zotero_DB::beginTransaction(); if (!isset($storageFileID)) { // Check if file already exists, which can happen if two identical // files are uploaded simultaneously $fileInfo = Zotero_S3::getLocalFileInfo($info); if ($fileInfo) { $storageFileID = $fileInfo['storageFileID']; } else { $storageFileID = Zotero_S3::addFile($info); } } Zotero_S3::updateFileItemInfo($item, $storageFileID, $info); Zotero_S3::logUpload($this->userID, $item, $uploadKey, IPAddress::getIP()); Zotero_DB::commit(); header("HTTP/1.1 204 No Content"); exit; } // // Client post-upload file registration // if (isset($_POST['update'])) { $this->allowMethods(array('POST')); if (empty($_POST['mtime'])) { throw new Exception('File modification time not provided'); } $uploadKey = $_POST['update']; $info = Zotero_S3::getUploadInfo($uploadKey); if (!$info) { $this->e400("Upload key not found"); } $remoteInfo = Zotero_S3::getRemoteFileInfo($info); if (!$remoteInfo) { $this->e400("Remote file not found"); } if (!isset($info->size)) { throw new Exception("Size information not available"); } $info->mtime = $_POST['mtime']; // Set an automatic shared lock in getLocalFileInfo() to prevent // two simultaneous transactions from adding a file Zotero_DB::query("SET TRANSACTION ISOLATION LEVEL SERIALIZABLE"); Zotero_DB::beginTransaction(); // Check if file already exists, which can happen if two identical // files are uploaded simultaneously $fileInfo = Zotero_S3::getLocalFileInfo($info); if ($fileInfo) { $storageFileID = $fileInfo['storageFileID']; } else { $storageFileID = Zotero_S3::addFile($info); } Zotero_S3::updateFileItemInfo($item, $storageFileID, $info); Zotero_S3::logUpload($this->userID, $item, $uploadKey, IPAddress::getIP()); Zotero_DB::commit(); header("HTTP/1.1 204 No Content"); exit; } } } } exit; }
public static function toJSON($libraryID) { // TODO: cache $libraryType = Zotero_Libraries::getType($libraryID); if ($libraryType == 'user') { $objectUserID = Zotero_Users::getUserIDFromLibraryID($libraryID); $json = ['type' => $libraryType, 'id' => $objectUserID, 'name' => self::getName($libraryID), 'links' => ['alternate' => ['href' => Zotero_URI::getUserURI($objectUserID, true), 'type' => 'text/html']]]; } else { if ($libraryType == 'publications') { $objectUserID = Zotero_Users::getUserIDFromLibraryID($libraryID); $json = ['type' => $libraryType, 'id' => $objectUserID, 'name' => self::getName($libraryID), 'links' => ['alternate' => ['href' => Zotero_URI::getUserURI($objectUserID, true) . "/publications", 'type' => 'text/html']]]; } else { if ($libraryType == 'group') { $objectGroupID = Zotero_Groups::getGroupIDFromLibraryID($libraryID); $group = Zotero_Groups::get($objectGroupID); $json = ['type' => $libraryType, 'id' => $objectGroupID, 'name' => self::getName($libraryID), 'links' => ['alternate' => ['href' => Zotero_URI::getGroupURI($group, true), 'type' => 'text/html']]]; } else { throw new Exception("Invalid library type '{$libraryType}'"); } } } return $json; }
public static function isEditable($obj) { $type = static::field('object'); // Only enforce for sync controller for now if (empty($GLOBALS['controller']) || !$GLOBALS['controller'] instanceof SyncController) { return true; } // Make sure user has access privileges to delete $userID = $GLOBALS['controller']->userID; if (!$userID) { return true; } $objectLibraryID = $obj->libraryID; $libraryType = Zotero_Libraries::getType($objectLibraryID); switch ($libraryType) { case 'user': if (!empty($GLOBALS['controller']->userLibraryID)) { $userLibraryID = $GLOBALS['controller']->userLibraryID; } else { $userLibraryID = Zotero_Users::getLibraryIDFromUserID($userID); } if ($objectLibraryID != $userLibraryID) { return false; } return true; case 'group': $groupID = Zotero_Groups::getGroupIDFromLibraryID($objectLibraryID); $group = Zotero_Groups::get($groupID); if (!$group->hasUser($userID) || !$group->userCanEdit($userID)) { return false; } if ($type == 'item' && $obj->isImportedAttachment() && !$group->userCanEditFiles($userID)) { return false; } return true; default: throw new Exception("Unsupported library type '{$libraryType}'"); } }
private function handleUploadError(Exception $e, $xmldata) { $msg = $e->getMessage(); if ($msg[0] == '=') { $msg = substr($msg, 1); $explicit = true; // TODO: more specific error messages } else { $explicit = false; } switch ($e->getCode()) { case Z_ERROR_TAG_TOO_LONG: case Z_ERROR_COLLECTION_TOO_LONG: break; default: Z_Core::logError($msg); } if (!$explicit && Z_ENV_TESTING_SITE) { switch ($e->getCode()) { case Z_ERROR_COLLECTION_NOT_FOUND: case Z_ERROR_CREATOR_NOT_FOUND: case Z_ERROR_ITEM_NOT_FOUND: case Z_ERROR_TAG_TOO_LONG: case Z_ERROR_LIBRARY_ACCESS_DENIED: case Z_ERROR_TAG_LINKED_ITEM_NOT_FOUND: break; default: throw $e; } $id = 'N/A'; } else { $id = substr(md5(uniqid(rand(), true)), 0, 8); $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 .= $msg; switch ($e->getCode()) { // Don't log uploaded data for some errors case Z_ERROR_TAG_TOO_LONG: case Z_ERROR_FIELD_TOO_LONG: case Z_ERROR_NOTE_TOO_LONG: case Z_ERROR_COLLECTION_TOO_LONG: break; default: $str .= "\n\n" . $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); } } Zotero_DB::rollback(true); switch ($e->getCode()) { case Z_ERROR_LIBRARY_ACCESS_DENIED: preg_match('/[Ll]ibrary ([0-9]+)/', $e->getMessage(), $matches); $libraryID = $matches ? $matches[1] : null; $this->error(400, 'LIBRARY_ACCESS_DENIED', "Cannot make changes to library (Report ID: {$id})", array('libraryID' => $libraryID)); break; case Z_ERROR_ITEM_NOT_FOUND: case Z_ERROR_COLLECTION_NOT_FOUND: case Z_ERROR_CREATOR_NOT_FOUND: error_log($e); $this->error(500, "FULL_SYNC_REQUIRED", "Please perform a full sync in the Sync->Reset pane of the Zotero preferences. (Report ID: {$id})"); break; case Z_ERROR_TAG_TOO_LONG: $message = $e->getMessage(); preg_match("/Tag '(.+)' too long/s", $message, $matches); if ($matches) { $name = $matches[1]; $this->error(400, "TAG_TOO_LONG", "Tag '" . mb_substr($name, 0, 50) . "…' too long", array(), array("tag" => $name)); } break; case Z_ERROR_COLLECTION_TOO_LONG: $message = $e->getMessage(); preg_match("/Collection '(.+)' too long/s", $message, $matches); if ($matches) { $name = $matches[1]; $this->error(400, "COLLECTION_TOO_LONG", "Collection '" . mb_substr($name, 0, 50) . "…' too long", array(), array("collection" => $name)); } break; case Z_ERROR_NOTE_TOO_LONG: preg_match("/Note '(.+)' too long(?: for item '(.+)\\/(.+)')?/s", $msg, $matches); if ($matches) { $name = $matches[1]; $libraryID = false; if (isset($matches[2])) { $libraryID = (int) $matches[2]; $itemKey = $matches[3]; if (Zotero_Libraries::getType($libraryID) == 'group') { $groupID = Zotero_Groups::getGroupIDFromLibraryID($libraryID); $group = Zotero_Groups::get($groupID); $libraryName = $group->name; } else { $libraryName = false; } } else { $itemKey = ''; } $showNoteKey = false; if (isset($_SERVER['HTTP_X_ZOTERO_VERSION'])) { require_once '../model/ToolkitVersionComparator.inc.php'; $showNoteKey = ToolkitVersionComparator::compare($_SERVER['HTTP_X_ZOTERO_VERSION'], "4.0.27") < 0; } if ($showNoteKey) { $this->error(400, "ERROR_PROCESSING_UPLOAD_DATA", "The note '" . mb_substr($name, 0, 50) . "…' in " . ($libraryName === false ? "your library " : "the group '{$libraryName}' ") . "is too long to sync to zotero.org.\n\n" . "Search for the excerpt above or copy and paste " . "'{$itemKey}' into the Zotero search bar. " . "Shorten the note, or delete it and empty the Zotero " . "trash, and then try syncing again."); } else { $this->error(400, "NOTE_TOO_LONG", "The note '" . mb_substr($name, 0, 50) . "…' in " . ($libraryName === false ? "your library " : "the group '{$libraryName}' ") . "is too long to sync to zotero.org.\n\n" . "Shorten the note, or delete it and empty the Zotero " . "trash, and then try syncing again.", [], $libraryID ? ["item" => $libraryID . "/" . $itemKey] : []); } } break; case Z_ERROR_FIELD_TOO_LONG: preg_match("/(.+) field value '(.+)\\.\\.\\.' too long(?: for item '(.+)')?/s", $msg, $matches); if ($matches) { $fieldName = $matches[1]; $value = $matches[2]; if (isset($matches[3])) { $parts = explode("/", $matches[3]); $libraryID = (int) $parts[0]; $itemKey = $parts[1]; if (Zotero_Libraries::getType($libraryID) == 'group') { $groupID = Zotero_Groups::getGroupIDFromLibraryID($libraryID); $group = Zotero_Groups::get($groupID); $libraryName = "the group '" . $group->name . "'"; } else { $libraryName = "your personal library"; } } else { $libraryName = "one of your libraries"; $itemKey = false; } $this->error(400, "ERROR_PROCESSING_UPLOAD_DATA", "The {$fieldName} field value '{$value}…' in {$libraryName} is " . "too long to sync to zotero.org.\n\n" . "Search for the excerpt above " . ($itemKey === false ? "using " : "or copy and paste " . "'{$itemKey}' into ") . "the Zotero search bar. " . "Shorten the field, or delete the item and empty the " . "Zotero trash, and then try syncing again."); } break; case Z_ERROR_ARRAY_SIZE_MISMATCH: $this->error(400, 'DATABASE_TOO_LARGE', "Databases of this size cannot yet be synced. Please check back soon. (Report ID: {$id})"); break; case Z_ERROR_TAG_LINKED_ITEM_NOT_FOUND: $this->error(400, 'WRONG_LIBRARY_TAG_ITEM', "Error processing uploaded data (Report ID: {$id})"); break; case Z_ERROR_SHARD_READ_ONLY: case Z_ERROR_SHARD_UNAVAILABLE: $this->error(503, 'SERVER_ERROR', Z_CONFIG::$MAINTENANCE_MESSAGE); break; } if (strpos($msg, "Lock wait timeout exceeded; try restarting transaction") !== false || strpos($msg, "MySQL error: Deadlock found when trying to get lock; try restarting transaction") !== false) { $this->error(500, 'TIMEOUT', "Sync upload timed out. Please try again in a few minutes. (Report ID: {$id})"); } if (strpos($msg, "Data too long for column 'xmldata'") !== false) { $this->error(400, 'DATABASE_TOO_LARGE', "Databases of this size cannot yet be synced. Please check back soon. (Report ID: {$id})"); } // On certain messages, send 400 to prevent auto-retry if (strpos($msg, " too long") !== false || strpos($msg, "First and last name are empty") !== false) { $this->error(400, 'ERROR_PROCESSING_UPLOAD_DATA', $explicit ? $msg : "Error processing uploaded data (Report ID: {$id})"); } if (preg_match("/Incorrect datetime value: '([0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2})' " . "for column 'date(Added|Modified)'/", $msg, $matches)) { if (isset($_SERVER['HTTP_X_ZOTERO_VERSION'])) { require_once '../model/ToolkitVersionComparator.inc.php'; if (ToolkitVersionComparator::compare($_SERVER['HTTP_X_ZOTERO_VERSION'], "2.1rc1") < 0) { $msg = "Invalid timestamp '{$matches[1]}' in uploaded data. Upgrade to Zotero 2.1rc1 when available to fix automatically."; } else { $msg = "Invalid timestamp '{$matches[1]}' in uploaded data. Sync again to correct automatically."; } } else { $msg = "Invalid timestamp '{$matches[1]}' in uploaded data. Upgrade to Zotero 2.1rc1 when available to fix automatically."; } $this->error(400, 'INVALID_TIMESTAMP', $msg); } $this->error(500, 'ERROR_PROCESSING_UPLOAD_DATA', $explicit ? $msg : "Error processing uploaded data (Report ID: {$id})"); }
public static function getLibraryURI($libraryID) { $libraryType = Zotero_Libraries::getType($libraryID); switch ($libraryType) { case 'user': $id = Zotero_Users::getUserIDFromLibraryID($libraryID); return self::getBaseURI() . "users/{$id}"; case 'group': $id = Zotero_Groups::getGroupIDFromLibraryID($libraryID); return self::getBaseURI() . "groups/{$id}"; } }
public static function getOwner($libraryID) { $type = self::getType($libraryID); switch ($type) { case 'user': return Zotero_Users::getUserIDFromLibraryID($libraryID); case 'group': $groupID = Zotero_Groups::getGroupIDFromLibraryID($libraryID); $group = Zotero_Groups::get($groupID); return $group->ownerUserID; } }
public function toJSON() { if (($this->id || $this->key) && !$this->loaded) { $this->load(); } $json = []; $json['key'] = $this->key; $json['userID'] = $this->userID; $json['username'] = Zotero_Users::getUsername($this->userID); $json['name'] = $this->name; if ($this->permissions) { $json['access'] = ['user' => [], 'groups' => []]; foreach ($this->permissions as $libraryID => $p) { // group="all" is stored as libraryID 0 if ($libraryID === 0) { $json['access']['groups']['all']['library'] = true; $json['access']['groups']['all']['write'] = !empty($p['write']); } else { $type = Zotero_Libraries::getType($libraryID); switch ($type) { case 'user': $json['access']['user']['library'] = true; foreach ($p as $permission => $granted) { if ($permission == 'library') { continue; } $json['access']['user'][$permission] = (bool) $granted; } break; case 'group': $groupID = Zotero_Groups::getGroupIDFromLibraryID($libraryID); $json['access']['groups'][$groupID]['library'] = true; $json['access']['groups'][$groupID]['write'] = !empty($p['write']); break; } } } if (sizeOf($json['access']['user']) === 0) { unset($json['access']['user']); } if (sizeOf($json['access']['groups']) === 0) { unset($json['access']['groups']); } } $json['dateAdded'] = Zotero_Date::sqlToISO8601($this->dateAdded); if ($this->lastUsed != '0000-00-00 00:00:00') { $json['lastUsed'] = Zotero_Date::sqlToISO8601($this->lastUsed); } $ips = $this->getRecentIPs(); if ($ips) { $json['recentIPs'] = $ips; } return $json; }
/** * This should be called after canAccess() */ public function canWrite($libraryID) { if ($this->super) { return true; } if ($libraryID === 0) { return false; } if (!$libraryID) { throw new Exception('libraryID not provided'); } if (!empty($this->permissions[$libraryID]['write'])) { return true; } $libraryType = Zotero_Libraries::getType($libraryID); switch ($libraryType) { case 'user': return false; // Write permissions match key's write access to user library // Write permissions match key's write access to user library case 'publications': $userLibraryID = Zotero_Users::getLibraryIDFromUserID($this->userID); return $this->canWrite($userLibraryID); case 'group': $groupID = Zotero_Groups::getGroupIDFromLibraryID($libraryID); // If key has write access to all groups, grant access if user // has write access to group if (!empty($this->permissions[0]['write'])) { $group = Zotero_Groups::get($groupID); return $group->userCanEdit($this->userID); } return false; default: throw new Exception("Unsupported library type '{$libraryType}'"); } }
/** * Converts key to a SimpleXMLElement item * * @return SimpleXMLElement Key data as SimpleXML element */ public function toXML() { if (($this->id || $this->key) && !$this->loaded) { $this->load(); } $xml = '<key/>'; $xml = new SimpleXMLElement($xml); $xml['key'] = $this->key; $xml['dateAdded'] = $this->dateAdded; if ($this->lastUsed != '0000-00-00 00:00:00') { $xml['lastUsed'] = $this->lastUsed; } $xml->name = $this->name; if ($this->permissions) { foreach ($this->permissions as $libraryID => $p) { $access = $xml->addChild('access'); // group="all" is stored as libraryID 0 if ($libraryID === 0) { $access['group'] = 'all'; if (!empty($p['write'])) { $access['write'] = 1; } continue; } $type = Zotero_Libraries::getType($libraryID); switch ($type) { case 'user': foreach ($p as $permission => $granted) { $access[$permission] = (int) $granted; } break; case 'group': $access['group'] = Zotero_Groups::getGroupIDFromLibraryID($libraryID); if (!empty($p['write'])) { $access['write'] = 1; } break; } } } $ips = $this->getRecentIPs(); if ($ips) { $xml->recentIPs = implode(' ', $ips); } return $xml; }