示例#1
0
 public static function add($userID)
 {
     Z_Core::debug("Creating publications library for user {$userID}");
     Zotero_DB::beginTransaction();
     // Use same shard as user library
     $shardID = Zotero_Shards::getByUserID($userID);
     $libraryID = Zotero_Libraries::add('publications', $shardID);
     $sql = "INSERT INTO userPublications (userID, libraryID) VALUES (?, ?)";
     Zotero_DB::query($sql, [$userID, $libraryID]);
     Zotero_DB::commit();
     return $libraryID;
 }
示例#2
0
 public static function deleteBatch($queueURL, $batchEntries)
 {
     Z_Core::debug("Deleting " . sizeOf($batchEntries) . " messages from {$queueURL}", 4);
     $response = self::$sqs->deleteMessageBatch(['QueueUrl' => $queueURL, 'Entries' => $batchEntries]);
     $response = self::processResponse($response);
     if (!$response) {
         return false;
     }
     foreach ($response->body->DeleteMessageBatchResult[0]->BatchResultErrorEntry as $error) {
         error_log("Error deleting SQS message: " . $error->Code . ": " . $error->Message);
     }
     return $response->body->DeleteMessageBatchResult[0]->DeleteMessageBatchResultEntry->count();
 }
示例#3
0
 public static function notifyProcessor($mode, $signal, $addr, $port)
 {
     switch ($mode) {
         case 'download':
         case 'upload':
         case 'error':
             break;
         default:
             throw new Exception("Invalid processor mode '{$mode}'");
     }
     if (!$addr) {
         throw new Exception("Host address not provided");
     }
     Z_Core::debug("Notifying {$mode} processor {$addr} with signal {$signal}");
     $socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
     $success = socket_sendto($socket, $signal, strlen($signal), MSG_EOF, $addr, $port);
     if (!$success) {
         $code = socket_last_error($socket);
         throw new Exception(socket_strerror($code));
     }
 }
示例#4
0
 public static function strToDate($string)
 {
     // Parse 'yesterday'/'today'/'tomorrow'
     $lc = strtolower($string);
     if ($lc == 'yesterday' || $lc == self::getString('date.yesterday')) {
         $string = date("Y-m-d", strtotime('yesterday'));
     } else {
         if ($lc == 'today' || $lc == self::getString('date.today')) {
             $string = date("Y-m-d");
         } else {
             if ($lc == 'tomorrow' || $lc == self::getString('date.tomorrow')) {
                 $string = date("Y-m-d", strtotime('tomorrow'));
             }
         }
     }
     $date = array();
     // skip empty things
     if (!$string) {
         return $date;
     }
     $string = preg_replace(array("/^\\s+/", "/\\s+\$/", "/\\s+/"), array("", "", " "), $string);
     // first, directly inspect the string
     preg_match(self::$slashRE, $string, $m);
     if ($m && (empty($m[5]) || $m[3] == $m[5] || $m[3] == "年" && $m[5] == "月") && (!empty($m[2]) && !empty($m[4]) && !empty($m[6]) || empty($m[1]) && empty($m[7]))) {
         // require that either all parts are found,
         // or else this is the entire date field
         // figure out date based on parts
         if (mb_strlen($m[2]) == 3 || mb_strlen($m[2]) == 4 || $m[3] == "年") {
             // ISO 8601 style date (big endian)
             $date['year'] = $m[2];
             $date['month'] = $m[4];
             $date['day'] = $m[6];
         } else {
             // local style date (middle or little endian)
             $date['year'] = $m[6];
             $country = substr(self::$locale, 3);
             if ($country == "US" || $country == "FM" || $country == "PW" || $country == "PH") {
                 // The Philippines
                 $date['month'] = $m[2];
                 $date['day'] = $m[4];
             } else {
                 $date['month'] = $m[4];
                 $date['day'] = $m[2];
             }
         }
         if ($date['year']) {
             $date['year'] = (int) $date['year'];
         }
         if ($date['day']) {
             $date['day'] = (int) $date['day'];
         }
         if ($date['month']) {
             $date['month'] = (int) $date['month'];
             if ($date['month'] > 12) {
                 // swap day and month
                 $tmp = $date['day'];
                 $date['day'] = $date['month'];
                 $date['month'] = $tmp;
             }
         }
         if ((empty($date['month']) || $date['month'] <= 12) && (empty($date['day']) || $date['day'] <= 31)) {
             if (!empty($date['year']) && $date['year'] < 100) {
                 // for two digit years, determine proper
                 $year = date('Y');
                 $twoDigitYear = date('y');
                 $century = $year - $twoDigitYear;
                 if ($date['year'] <= $twoDigitYear) {
                     // assume this date is from our century
                     $date['year'] = $century + $date['year'];
                 } else {
                     // assume this date is from the previous century
                     $date['year'] = $century - 100 + $date['year'];
                 }
             }
             Z_Core::debug("DATE: retrieved with algorithms: " . json_encode($date));
             $date['part'] = $m[1] . $m[7];
         } else {
             // give up; we failed the sanity check
             Z_Core::debug("DATE: algorithms failed sanity check");
             $date = array("part" => $string);
         }
     } else {
         //Zotero.debug("DATE: could not apply algorithms");
         $date['part'] = $string;
     }
     // couldn't find something with the algorithms; use regexp
     // YEAR
     if (empty($date['year'])) {
         if (preg_match(self::$yearRE, $date['part'], $m)) {
             $date['year'] = $m[2];
             $date['part'] = $m[1] . $m[3];
             Z_Core::debug("DATE: got year (" . $date['year'] . ", " . $date['part'] . ")");
         }
     }
     // MONTH
     if (empty($date['month'])) {
         // compile month regular expression
         $months = array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec');
         // If using a non-English bibliography locale, try those too
         if (self::$locale != 'en-US') {
             throw new Exception("Unimplemented");
             //$months = array_merge($months, .concat(Zotero.$date['month']s.short);
         }
         if (!self::$monthRE) {
             self::$monthRE = "/^(.*)\\b(" . implode("|", $months) . ")[^ ]*(?: (.*)\$|\$)/iu";
         }
         if (preg_match(self::$monthRE, $date['part'], $m)) {
             // Modulo 12 in case we have multiple languages
             $date['month'] = array_search(ucwords(strtolower($m[2])), $months) % 12 + 1;
             $date['part'] = $m[1] . (isset($m[3]) ? $m[3] : '');
             Z_Core::debug("DATE: got month (" . $date['month'] . ", " . $date['part'] . ")");
         }
     }
     // DAY
     if (empty($date['day'])) {
         // compile day regular expression
         if (!self::$dayRE) {
             $daySuffixes = preg_replace("/, ?/", "|", self::getString("date.daySuffixes"));
             self::$dayRE = "/\\b([0-9]{1,2})(?:" . $daySuffixes . ")?\\b(.*)/iu";
         }
         if (preg_match(self::$dayRE, $date['part'], $m, PREG_OFFSET_CAPTURE)) {
             $day = (int) $m[1][0];
             // Sanity check
             if ($day <= 31) {
                 $date['day'] = $day;
                 if ($m[0][1] > 0) {
                     $date['part'] = substr($date['part'], 0, $m[0][1]);
                     if ($m[2][0]) {
                         $date['part'] .= " " . $m[2][0];
                     }
                 } else {
                     $date['part'] = $m[2][0];
                 }
                 Z_Core::debug("DATE: got day (" . $date['day'] . ", " . $date['part'] . ")");
             }
         }
     }
     // clean up date part
     if ($date['part']) {
         $date['part'] = preg_replace(array("/^[^A-Za-z0-9]+/", "/[^A-Za-z0-9]+\$/"), "", $date['part']);
     }
     if ($date['part'] === "" || !isset($date['part'])) {
         unset($date['part']);
     }
     return $date;
 }
示例#5
0
 /**
  * Download file from S3, extract it if necessary, and return a temporary URL
  * pointing to the main file
  */
 public static function getTemporaryURL(Zotero_Item $item, $localOnly = false)
 {
     $extURLPrefix = Z_CONFIG::$ATTACHMENT_SERVER_URL;
     if ($extURLPrefix[strlen($extURLPrefix) - 1] != "/") {
         $extURLPrefix .= "/";
     }
     $info = Zotero_Storage::getLocalFileItemInfo($item);
     $storageFileID = $info['storageFileID'];
     $filename = $info['filename'];
     $mtime = $info['mtime'];
     $zip = $info['zip'];
     $realFilename = preg_replace("/^storage:/", "", $item->attachmentPath);
     $realFilename = self::decodeRelativeDescriptorString($realFilename);
     $realEncodedFilename = rawurlencode($realFilename);
     $docroot = Z_CONFIG::$ATTACHMENT_SERVER_DOCROOT;
     // Check memcached to see if file is already extracted
     $key = "attachmentServerString_" . $storageFileID . "_" . $mtime;
     if ($randomStr = Z_Core::$MC->get($key)) {
         Z_Core::debug("Got attachment path '{$randomStr}/{$realEncodedFilename}' from memcached");
         return $extURLPrefix . "{$randomStr}/{$realEncodedFilename}";
     }
     $localAddr = gethostbyname(gethostname());
     // See if this is an attachment host
     $index = false;
     $skipHost = false;
     for ($i = 0, $len = sizeOf(Z_CONFIG::$ATTACHMENT_SERVER_HOSTS); $i < $len; $i++) {
         $hostAddr = gethostbyname(Z_CONFIG::$ATTACHMENT_SERVER_HOSTS[$i]);
         if ($hostAddr != $localAddr) {
             continue;
         }
         // Make a HEAD request on the local static port to make sure
         // this host is actually functional
         $url = "http://" . Z_CONFIG::$ATTACHMENT_SERVER_HOSTS[$i] . ":" . Z_CONFIG::$ATTACHMENT_SERVER_STATIC_PORT . "/";
         Z_Core::debug("Making HEAD request to {$url}");
         $ch = curl_init($url);
         curl_setopt($ch, CURLOPT_NOBODY, 1);
         curl_setopt($ch, CURLOPT_HTTPHEADER, array("Expect:"));
         curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 1);
         curl_setopt($ch, CURLOPT_TIMEOUT, 2);
         curl_setopt($ch, CURLOPT_HEADER, 0);
         // do not return HTTP headers
         curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
         $response = curl_exec($ch);
         $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
         if ($code != 200) {
             $skipHost = Z_CONFIG::$ATTACHMENT_SERVER_HOSTS[$i];
             if ($code == 0) {
                 Z_Core::logError("Error connecting to local attachments server");
             } else {
                 Z_Core::logError("Local attachments server returned {$code}");
             }
             break;
         }
         $index = $i + 1;
         break;
     }
     // If not, make an internal root request to trigger the extraction on
     // one of them and retrieve the temporary URL
     if ($index === false) {
         // Prevent redirect madness if target server doesn't think it's an
         // attachment server
         if ($localOnly) {
             throw new Exception("Internal attachments request hit a non-attachment server");
         }
         $prefix = 'http://' . Z_CONFIG::$API_SUPER_USERNAME . ":" . Z_CONFIG::$API_SUPER_PASSWORD . "@";
         $path = Zotero_API::getItemURI($item) . "/file/view?int=1";
         $path = preg_replace('/^[^:]+:\\/\\/[^\\/]+/', '', $path);
         $context = stream_context_create(array('http' => array('follow_location' => 0)));
         $url = false;
         $hosts = Z_CONFIG::$ATTACHMENT_SERVER_HOSTS;
         // Try in random order
         shuffle($hosts);
         foreach ($hosts as $host) {
             // Don't try the local host again if we know it's not working
             if ($host == $skipHost) {
                 continue;
             }
             $intURL = $prefix . $host . ":" . Z_CONFIG::$ATTACHMENT_SERVER_DYNAMIC_PORT . $path;
             Z_Core::debug("Making GET request to {$host}");
             if (file_get_contents($intURL, false, $context) !== false) {
                 foreach ($http_response_header as $header) {
                     if (preg_match('/^Location:\\s*(.+)$/', $header, $matches)) {
                         if (strpos($matches[1], $extURLPrefix) !== 0) {
                             throw new Exception("Redirect location '" . $matches[1] . "'" . " does not begin with {$extURLPrefix}");
                         }
                         return $matches[1];
                     }
                 }
             }
         }
         return false;
     }
     // If this is an attachment host, do the download/extraction inline
     // and generate a random number with an embedded host id.
     //
     // The reverse proxy routes incoming file requests to the proper hosts
     // using the embedded id.
     //
     // A cron job deletes old attachment directories
     $randomStr = rand(1000000, 2147483647);
     // Seventh number is the host id
     $randomStr = substr($randomStr, 0, 6) . $index . substr($randomStr, 6);
     // Download file
     $dir = $docroot . $randomStr . "/";
     $downloadDir = $zip ? $dir . "ztmp/" : $dir;
     Z_Core::debug("Downloading attachment to {$dir}");
     if (!mkdir($downloadDir, 0777, true)) {
         throw new Exception("Unable to create directory '{$downloadDir}'");
     }
     if ($zip) {
         $response = Zotero_Storage::downloadFile($info, $downloadDir);
     } else {
         $response = Zotero_Storage::downloadFile($info, $downloadDir, $realFilename);
     }
     if ($response) {
         if ($zip) {
             $success = self::extractZip($downloadDir . $info['filename'], $dir);
             unlink($downloadDir . $info['filename']);
             rmdir($downloadDir);
             // Make sure charset is just a string with no spaces or newlines
             if (preg_match('/^[^\\s]+/', trim($item->attachmentCharset), $matches)) {
                 $charset = $matches[0];
             } else {
                 $charset = 'Off';
             }
             file_put_contents($dir . ".htaccess", "AddDefaultCharset " . $charset);
         } else {
             $success = true;
             if (preg_match('/^[^\\s]+/', trim($item->attachmentContentType), $matches)) {
                 $contentType = $matches[0];
                 $charset = trim($item->attachmentCharset);
                 if (substr($charset, 0, 5) == 'text/' && preg_match('/^[^\\s]+/', $charset, $matches)) {
                     $contentType .= '; ' . $matches[0];
                 }
                 file_put_contents($dir . ".htaccess", "ForceType " . $contentType);
             }
         }
     }
     if (!$response || !$success) {
         return false;
     }
     Z_Core::$MC->set($key, $randomStr, self::$cacheTime);
     return $extURLPrefix . "{$randomStr}/" . $realEncodedFilename;
 }
示例#6
0
	protected function loadRelations($reload = false) {
		if ($this->loaded['relations'] && !$reload) return;
		
		if (!$this->id) {
			return;
		}
		
		Z_Core::debug("Loading relations for item $this->id");
		
		$this->loadPrimaryData(false, true);
		
		$itemURI = Zotero_URI::getItemURI($this);
		
		$relations = Zotero_Relations::getByURIs($this->libraryID, $itemURI);
		$relations = array_map(function ($rel) {
			return [$rel->predicate, $rel->object];
		}, $relations);
		
		// Related items are bidirectional, so include any with this item as the object
		$reverseRelations = Zotero_Relations::getByURIs(
			$this->libraryID, false, Zotero_Relations::$relatedItemPredicate, $itemURI
		);
		foreach ($reverseRelations as $rel) {
			$r = [$rel->predicate, $rel->subject];
			// Only add if not already added in other direction
			if (!in_array($r, $relations)) {
				$relations[] = $r;
			}
		}
		
		// Also include any owl:sameAs relations with this item as the object
		// (as sent by client via classic sync)
		$reverseRelations = Zotero_Relations::getByURIs(
			$this->libraryID, false, Zotero_Relations::$linkedObjectPredicate, $itemURI
		);
		foreach ($reverseRelations as $rel) {
			$relations[] = [$rel->predicate, $rel->subject];
		}
		
		// TEMP: Get old-style related items
		//
		// Add related items
		$sql = "SELECT `key` FROM itemRelated IR "
			. "JOIN items I ON (IR.linkedItemID=I.itemID) "
			. "WHERE IR.itemID=?";
		$relatedItemKeys = Zotero_DB::columnQuery($sql, $this->id, Zotero_Shards::getByLibraryID($this->libraryID));
		if ($relatedItemKeys) {
			$prefix = Zotero_URI::getLibraryURI($this->libraryID) . "/items/";
			$predicate = Zotero_Relations::$relatedItemPredicate;
			foreach ($relatedItemKeys as $key) {
				$relations[] = [$predicate, $prefix . $key];
			}
		}
		// Reverse as well
		$sql = "SELECT `key` FROM itemRelated IR JOIN items I USING (itemID) WHERE IR.linkedItemID=?";
		$reverseRelatedItemKeys = Zotero_DB::columnQuery(
			$sql, $this->id, Zotero_Shards::getByLibraryID($this->libraryID)
		);
		if ($reverseRelatedItemKeys) {
			$prefix = Zotero_URI::getLibraryURI($this->libraryID) . "/items/";
			$predicate = Zotero_Relations::$relatedItemPredicate;
			foreach ($reverseRelatedItemKeys as $key) {
				$relations[] = [$predicate, $prefix . $key];
			}
		}
		
		$this->relations = $relations;
		$this->loaded['relations'] = true;
		$this->clearChanged('relations');
	}
示例#7
0
 public function rollback()
 {
     if (!$this->queuing) {
         Z_Core::debug('Transaction not open in Z_MemcachedClientLocal::rollback()');
         return;
     }
     if (!$this->queue) {
         return;
     }
     $this->queuing = false;
     $this->queue = array();
     $this->queueKeyPos = array();
     $this->queueValues = array();
 }
示例#8
0
 private function loadLinkedItems()
 {
     Z_Core::debug("Loading linked items for tag {$this->id}");
     if (!$this->id && !$this->key) {
         $this->linkedItemsLoaded = true;
         return;
     }
     if (!$this->loaded) {
         $this->load();
     }
     if (!$this->id) {
         $this->linkedItemsLoaded = true;
         return;
     }
     $sql = "SELECT itemID FROM itemTags WHERE tagID=?";
     $stmt = Zotero_DB::getStatement($sql, true, Zotero_Shards::getByLibraryID($this->libraryID));
     $ids = Zotero_DB::columnQueryFromStatement($stmt, $this->id);
     $this->linkedItems = array();
     if ($ids) {
         $this->linkedItems = Zotero_Items::get($this->libraryID, $ids);
     }
     $this->linkedItemsLoaded = true;
 }
示例#9
0
 private function load()
 {
     $libraryID = $this->libraryID;
     $id = $this->id;
     $key = $this->key;
     Z_Core::debug("Loading data for search " . ($id ? $id : $key));
     if (!$libraryID) {
         throw new Exception("Library ID not set");
     }
     if (!$id && !$key) {
         throw new Exception("ID or key not set");
     }
     $shardID = Zotero_Shards::getByLibraryID($libraryID);
     $sql = "SELECT searchID AS id, searchName AS name, dateAdded,\n\t\t\t\tdateModified, libraryID, `key`, version\n\t\t\t\tFROM savedSearches WHERE ";
     if ($id) {
         $sql .= "searchID=?";
         $params = $id;
     } else {
         $sql .= "libraryID=? AND `key`=?";
         $params = array($libraryID, $key);
     }
     $sql .= " GROUP BY searchID";
     $data = Zotero_DB::rowQuery($sql, $params, $shardID);
     $this->loaded = true;
     if (!$data) {
         return;
     }
     foreach ($data as $key => $val) {
         $this->{$key} = $val;
     }
     $sql = "SELECT * FROM savedSearchConditions\n\t\t\t\tWHERE searchID=? ORDER BY searchConditionID";
     $conditions = Zotero_DB::query($sql, $this->id, $shardID);
     foreach ($conditions as $condition) {
         $searchConditionID = $condition['searchConditionID'];
         $this->conditions[$searchConditionID] = array('id' => $searchConditionID, 'condition' => $condition['condition'], 'mode' => $condition['mode'], 'operator' => $condition['operator'], 'value' => $condition['value'], 'required' => $condition['required']);
     }
 }
示例#10
0
 public function save()
 {
     if (!$this->libraryID) {
         trigger_error("Library ID must be set before saving", E_USER_ERROR);
     }
     Zotero_DB::beginTransaction();
     try {
         $shardID = Zotero_Shards::getByLibraryID($this->libraryID);
         $relationID = $this->id ? $this->id : Zotero_ID::get('relations');
         Z_Core::debug("Saving relation {$relationID}");
         $sql = "INSERT INTO relations\n\t\t\t\t\t(relationID, libraryID, subject, predicate, object, serverDateModified)\n\t\t\t\t\tVALUES (?, ?, ?, ?, ?, ?)";
         $timestamp = Zotero_DB::getTransactionTimestamp();
         $params = array($relationID, $this->libraryID, $this->subject, $this->predicate, $this->object, $timestamp);
         $insertID = Zotero_DB::query($sql, $params, $shardID);
         if (!$this->id) {
             if (!$insertID) {
                 throw new Exception("Relation id not available after INSERT");
             }
             $this->id = $insertID;
         }
         // Remove from delete log if it's there
         $sql = "DELETE FROM syncDeleteLogKeys WHERE libraryID=? AND objectType='relation' AND `key`=?";
         Zotero_DB::query($sql, array($this->libraryID, $this->getKey()), $shardID);
         Zotero_DB::commit();
     } catch (Exception $e) {
         Zotero_DB::rollback();
         throw $e;
     }
     return $this->id;
 }
示例#11
0
 private function loadChildItems()
 {
     Z_Core::debug("Loading child items for collection {$this->id}");
     if ($this->childItemsLoaded) {
         trigger_error("Child items for collection {$this->id} already loaded", E_USER_ERROR);
     }
     if (!$this->id) {
         trigger_error('$this->id not set', E_USER_ERROR);
     }
     $sql = "SELECT itemID FROM collectionItems WHERE collectionID=?";
     $ids = Zotero_DB::columnQuery($sql, $this->id, Zotero_Shards::getByLibraryID($this->libraryID));
     $this->childItems = $ids ? $ids : array();
     $this->childItemsLoaded = true;
 }
示例#12
0
 public function updateConditions($conditions)
 {
     $this->loadPrimaryData();
     $this->loadConditions();
     for ($i = 1, $len = sizeOf($conditions); $i <= $len; $i++) {
         // Compare existing values to new values
         if (isset($this->conditions[$i])) {
             if ($this->conditions[$i]['condition'] == $conditions[$i - 1]['condition'] && $this->conditions[$i]['mode'] == $conditions[$i - 1]['mode'] && $this->conditions[$i]['operator'] == $conditions[$i - 1]['operator'] && $this->conditions[$i]['value'] == $conditions[$i - 1]['value']) {
                 continue;
             }
         }
         $this->changed['conditions'] = true;
     }
     if (!empty($this->changed['conditions']) || sizeOf($this->conditions) > $conditions) {
         $this->conditions = $conditions;
     } else {
         Z_Core::debug("Conditions have not changed for search {$this->id}");
     }
 }
示例#13
0
 public function hasChanged()
 {
     $changed = array_filter(array_keys($this->changed), function ($dataType) {
         return $this->changed[$dataType];
     });
     foreach ($changed as $dataType) {
         if ($dataType == 'primaryData' && is_array($this->changed['primaryData'])) {
             foreach ($this->changed['primaryData'] as $field => $val) {
                 Z_Core::debug("{$field} has changed for item {$this->libraryKey}");
             }
         } else {
             Z_Core::debug("{$dataType} has changed for item {$this->libraryKey}");
         }
     }
     return !!$changed;
 }
示例#14
0
 public function save()
 {
     if (!$this->loaded) {
         Z_Core::debug("Not saving unloaded key {$this->id}");
         return;
     }
     if (!$this->userID) {
         throw new Exception("Cannot save key without userID");
     }
     if (!$this->name) {
         throw new Exception("Cannot save key without name");
     }
     if (strlen($this->name) > 255) {
         throw new Exception("Key name too long", Z_ERROR_KEY_NAME_TOO_LONG);
     }
     Zotero_DB::beginTransaction();
     if (!$this->key) {
         $isNew = true;
         $this->key = Zotero_Keys::generate();
     } else {
         $isNew = false;
     }
     $fields = array('key', 'userID', 'name');
     $sql = "INSERT INTO `keys` (keyID, `key`, userID, name) VALUES (?, ?, ?, ?)";
     $params = array($this->id);
     foreach ($fields as $field) {
         $params[] = $this->{$field};
     }
     $sql .= " ON DUPLICATE KEY UPDATE ";
     $q = array();
     foreach ($fields as $field) {
         $q[] = "`{$field}`=?";
         $params[] = $this->{$field};
     }
     $sql .= implode(", ", $q);
     $insertID = Zotero_DB::query($sql, $params);
     if (!$this->id) {
         if (!$insertID) {
             throw new Exception("Key id not available after INSERT");
         }
         $this->id = $insertID;
     }
     if (!$insertID) {
         $sql = "SELECT * FROM keyPermissions WHERE keyID=?";
         $oldRows = Zotero_DB::query($sql, $this->id);
     }
     $oldPermissions = [];
     $newPermissions = [];
     $librariesToAdd = [];
     $librariesToRemove = [];
     // Massage rows into permissions format
     if (!$isNew && isset($oldRows)) {
         foreach ($oldRows as $row) {
             $oldPermissions[$row['libraryID']][$row['permission']] = !!$row['granted'];
         }
     }
     // Delete existing permissions
     $sql = "DELETE FROM keyPermissions WHERE keyID=?";
     Zotero_DB::query($sql, $this->id);
     if (isset($this->changed['permissions'])) {
         foreach ($this->changed['permissions'] as $libraryID => $p) {
             foreach ($p as $permission => $changed) {
                 $enabled = $this->permissions[$libraryID][$permission];
                 if (!$enabled) {
                     continue;
                 }
                 $sql = "INSERT INTO keyPermissions VALUES (?, ?, ?, ?)";
                 // TODO: support negative permissions
                 Zotero_DB::query($sql, array($this->id, $libraryID, $permission, 1));
                 $newPermissions[$libraryID][$permission] = true;
             }
         }
     }
     $this->permissions = $newPermissions;
     // Send notifications for added and removed API key – library pairs
     if (!$isNew) {
         $librariesToAdd = $this->permissionsDiff($oldPermissions, $newPermissions, $this->userID);
         $librariesToRemove = $this->permissionsDiff($newPermissions, $oldPermissions, $this->userID);
         if ($librariesToAdd) {
             Zotero_Notifier::trigger('add', 'apikey-library', array_map(function ($libraryID) {
                 return $this->key . "-" . $libraryID;
             }, array_unique($librariesToAdd)));
         }
         if ($librariesToRemove) {
             Zotero_Notifier::trigger('remove', 'apikey-library', array_map(function ($libraryID) {
                 return $this->key . "-" . $libraryID;
             }, array_unique($librariesToRemove)));
         }
     }
     Zotero_DB::commit();
     $this->load();
     return $this->id;
 }
示例#15
0
 protected function loadChildItems($reload = false)
 {
     if ($this->loaded['childItems'] && !$reload) {
         return;
     }
     Z_Core::debug("Loading child items for collection {$this->id}");
     if (!$this->id) {
         trigger_error('$this->id not set', E_USER_ERROR);
     }
     $sql = "SELECT itemID FROM collectionItems WHERE collectionID=?";
     $ids = Zotero_DB::columnQuery($sql, $this->id, Zotero_Shards::getByLibraryID($this->libraryID));
     $this->childItems = $ids ? $ids : [];
     $this->loaded['childItems'] = true;
     $this->clearChanged('childItems');
 }
示例#16
0
 private function load()
 {
     $libraryID = $this->libraryID;
     $name = $this->name;
     Z_Core::debug("Loading data for setting {$libraryID}/{$name}");
     if (!$libraryID) {
         throw new Exception("Library ID not set");
     }
     if (!$name) {
         throw new Exception("Name not set");
     }
     $row = Zotero_Settings::getPrimaryDataByKey($libraryID, $name);
     $this->loaded = true;
     if (!$row) {
         return;
     }
     foreach ($row as $key => $val) {
         if ($key == 'value') {
             $val = json_decode($val);
         }
         $this->{$key} = $val;
     }
 }
示例#17
0
 public static function delete($libraryID, $key, $updateLibrary = false)
 {
     $table = static::field('table');
     $id = static::field('id');
     $type = static::field('object');
     $types = static::field('objects');
     if (!$key) {
         throw new Exception("Invalid key {$key}");
     }
     // Get object (and trigger caching)
     $obj = static::getByLibraryAndKey($libraryID, $key);
     if (!$obj) {
         return;
     }
     static::editCheck($obj);
     Z_Core::debug("Deleting {$type} {$libraryID}/{$key}", 4);
     $shardID = Zotero_Shards::getByLibraryID($libraryID);
     Zotero_DB::beginTransaction();
     // Needed for API deletes to get propagated via sync
     if ($updateLibrary) {
         $timestamp = Zotero_Libraries::updateTimestamps($obj->libraryID);
         Zotero_DB::registerTransactionTimestamp($timestamp);
     }
     // Delete child items
     if ($type == 'item') {
         if ($obj->isRegularItem()) {
             $children = array_merge($obj->getNotes(), $obj->getAttachments());
             if ($children) {
                 $children = Zotero_Items::get($libraryID, $children);
                 foreach ($children as $child) {
                     static::delete($child->libraryID, $child->key);
                 }
             }
         }
     }
     if ($type == 'relation') {
         // TODO: add key column to relations to speed this up
         $sql = "DELETE FROM {$table} WHERE libraryID=? AND MD5(CONCAT(subject, '_', predicate, '_', object))=?";
         $deleted = Zotero_DB::query($sql, array($libraryID, $key), $shardID);
     } else {
         $sql = "DELETE FROM {$table} WHERE libraryID=? AND `key`=?";
         $deleted = Zotero_DB::query($sql, array($libraryID, $key), $shardID);
     }
     unset(self::$idCache[$type][$libraryID][$key]);
     static::uncachePrimaryData($libraryID, $key);
     if ($deleted) {
         $sql = "INSERT INTO syncDeleteLogKeys (libraryID, objectType, `key`, timestamp)\n\t\t\t\t\t\tVALUES (?, '{$type}', ?, ?) ON DUPLICATE KEY UPDATE timestamp=?";
         $timestamp = Zotero_DB::getTransactionTimestamp();
         $params = array($libraryID, $key, $timestamp, $timestamp);
         Zotero_DB::query($sql, $params, $shardID);
     }
     Zotero_DB::commit();
 }
示例#18
0
 public static function reset()
 {
     Z_Core::debug("Resetting Notifier event queue");
     self::$locked = false;
     self::$queue = array();
     self::$inTransaction = false;
 }
示例#19
0
 public function updateCondition($searchConditionID, $condition, $mode, $operator, $value, $required)
 {
     if ($this->id && !$this->loaded) {
         $this->load(false);
     }
     if (!isset($this->conditions[$searchConditionID])) {
         trigger_error("Invalid searchConditionID {$searchConditionID}", E_USER_ERROR);
     }
     /*
     if (!Zotero_SearchConditions::hasOperator($condition, $operator)) {
     	trigger_error("Invalid operator $operator", E_USER_ERROR);
     }
     */
     $existingCondition = $this->conditions[$searchConditionID];
     if ($existingCondition['condition'] == $condition && $existingCondition['mode'] == $mode && $existingCondition['operator'] == $operator && $existingCondition['value'] == $value && $existingCondition['required'] == $required) {
         Z_Core::debug("Condition {$searchConditionID} for search\n\t\t\t\t{$this->id} has not changed");
         return;
     }
     $this->conditions[$searchConditionID] = array('id' => $searchConditionID, 'condition' => $condition, 'mode' => $mode, 'operator' => $operator, 'value' => $value, 'required' => $required);
     $this->changed = true;
     //$this->sql = null;
     //$this->sqlParams = null;
 }
示例#20
0
*/
$params = (require 'config/routes.inc.php');
if (!$params || !isset($params['controller']) || $params['controller'] == 404) {
    header("HTTP/1.0 404 Not Found");
    include 'errors/404.php';
    return;
}
// Parse variables from router
$controllerName = ucwords($params['controller']);
$action = !empty($params['action']) ? $params['action'] : lcfirst($controllerName);
$directory = !empty($params['directory']) ? $params['directory'] . '/' : "";
$extra = !empty($params['extra']) ? $params['extra'] : array();
// Attempt to load controller
$controllerFile = Z_ENV_CONTROLLER_PATH . $directory . $controllerName . 'Controller.php';
Z_Core::debug($_SERVER['REQUEST_METHOD'] . " to " . Z_ENV_SELF);
Z_Core::debug("Controller is {$controllerFile}");
if (file_exists($controllerFile)) {
    require 'mvc/Controller.inc.php';
    require $controllerFile;
    $controllerClass = $controllerName . 'Controller';
    $controller = new $controllerClass($controllerName, $action, $params);
    $controller->init($extra);
    if (method_exists($controllerClass, $action)) {
        call_user_func(array($controller, $action));
        Z_Core::exitClean();
    } else {
        throw new Exception("Action '{$action}' not found in {$controllerFile}");
    }
}
// If controller not found, load error document
header("HTTP/1.0 404 Not Found");
示例#21
0
 private static function send($message)
 {
     $message = json_encode($message, JSON_UNESCAPED_SLASHES);
     Z_Core::debug("Sending notification: " . $message);
     foreach (self::$messageReceivers as $receiver) {
         $receiver(Z_CONFIG::$SNS_TOPIC_STREAM_EVENTS, $message);
     }
 }
示例#22
0
 /**
  * Store item in internal id-based cache
  */
 public static function cache(Zotero_Item $item)
 {
     if (isset(self::$itemsByID[$item->id])) {
         Z_Core::debug("Item {$item->id} is already cached");
     }
     self::$itemsByID[$item->id] = $item;
 }
示例#23
0
 private function setRelatedItems($itemIDs)
 {
     if (!$this->loaded['relatedItems']) {
         $this->loadRelatedItems();
     }
     if (!is_array($itemIDs)) {
         trigger_error('$itemIDs must be an array', E_USER_ERROR);
     }
     $currentIDs = $this->relatedItems;
     if (!$currentIDs) {
         $currentIDs = array();
     }
     $oldIDs = array();
     // children being kept
     $newIDs = array();
     // new children
     if (!$itemIDs) {
         if (!$currentIDs) {
             Z_Core::debug("No related items added", 4);
             return false;
         }
     } else {
         /*
         // Don't bother with this because the DB trigger takes care of it
         $found = Zotero_Items::get($this->libraryID, $itemIDs);
         if (sizeOf($found) != sizeOf($itemIDs)) {
         	throw new Exception("Related item(s) not found (" . sizeOf($found) . " != " . sizeOf($itemIDs) . ")");
         }
         */
         foreach ($itemIDs as $itemID) {
             if ($itemID == $this->id) {
                 Z_Core::debug("Can't relate item to itself in Zotero.Item.setRelatedItems()", 2);
                 continue;
             }
             if (in_array($itemID, $currentIDs)) {
                 Z_Core::debug("Item {$this->id} is already related to item {$itemID}");
                 $oldIDs[] = $itemID;
                 continue;
             }
             // TODO: check if related on other side (like client)?
             $newIDs[] = $itemID;
         }
     }
     // Mark as changed if new or removed ids
     if ($newIDs || sizeOf($oldIDs) != sizeOf($currentIDs)) {
         if (!$this->changed['relatedItems']) {
             $this->storePreviousData('relatedItems');
             $this->changed['relatedItems'] = true;
         }
     } else {
         Z_Core::debug('Related items not changed', 4);
         return false;
     }
     $this->relatedItems = array_merge($oldIDs, $newIDs);
     return true;
 }
示例#24
0
 protected static function logQuery($sql, $params, $shardID)
 {
     Z_Core::debug($sql . ($params ? " (" . (is_scalar($params) ? $params : implode(",", $params)) . ") ({$shardID})" : ""));
 }
示例#25
0
 private static function load($libraryID, $ids = [], array $options = [])
 {
     $loaded = [];
     if (!$libraryID) {
         throw new Exception("libraryID must be provided");
     }
     if ($libraryID !== false && !empty(self::$loadedLibraries[$libraryID])) {
         return $loaded;
     }
     $sql = self::getPrimaryDataSQL() . ' AND O.libraryID=?';
     $params = [$libraryID];
     if ($ids) {
         $sql .= ' AND O.' . self::$idColumn . ' IN (' . implode(',', $ids) . ')';
     }
     $t = microtime();
     $rows = Zotero_DB::query($sql, $params, Zotero_Shards::getByLibraryID($libraryID));
     foreach ($rows as $row) {
         $id = $row['id'];
         // Existing object -- reload in place
         if (isset(self::$objectCache[$id])) {
             self::$objectCache[$id]->loadFromRow($row, true);
             $obj = self::$objectCache[$id];
         } else {
             $class = "Zotero_" . self::$ObjectType;
             $obj = new $class();
             $obj->loadFromRow($row, true);
             if (!$options || !$options->noCache) {
                 self::registerObject($obj);
             }
         }
         $loaded[$id] = $obj;
     }
     Z_Core::debug("Loaded " . self::$objectTypePlural . " in " . (microtime() - $t) . "ms");
     if (!$ids) {
         self::$loadedLibraries[$libraryID] = true;
         // If loading all objects, remove cached objects that no longer exist
         foreach (self::$objectCache as $obj) {
             if ($libraryID !== false && obj . libraryID !== libraryID) {
                 continue;
             }
             if (empty($loaded[$obj->id])) {
                 self::unload($obj->id);
             }
         }
     }
     return $loaded;
 }
示例#26
0
 public function erase()
 {
     if (!$this->loaded) {
         Z_Core::debug("Not deleting unloaded group {$this->id}");
         return;
     }
     Zotero_DB::beginTransaction();
     $userIDs = self::getUsers();
     $this->logGroupLibraryRemoval();
     Zotero_Libraries::deleteCachedData($this->libraryID);
     Zotero_Libraries::clearAllData($this->libraryID);
     $sql = "DELETE FROM shardLibraries WHERE libraryID=?";
     $deleted = Zotero_DB::query($sql, $this->libraryID, Zotero_Shards::getByLibraryID($this->libraryID));
     if (!$deleted) {
         throw new Exception("Group not deleted");
     }
     $sql = "DELETE FROM libraries WHERE libraryID=?";
     $deleted = Zotero_DB::query($sql, $this->libraryID);
     if (!$deleted) {
         throw new Exception("Group not deleted");
     }
     // Delete key permissions for this library, and then delete any keys
     // that had no other permissions
     $sql = "SELECT keyID FROM keyPermissions WHERE libraryID=?";
     $keyIDs = Zotero_DB::columnQuery($sql, $this->libraryID);
     if ($keyIDs) {
         $sql = "DELETE FROM keyPermissions WHERE libraryID=?";
         Zotero_DB::query($sql, $this->libraryID);
         $sql = "DELETE K FROM `keys` K LEFT JOIN keyPermissions KP USING (keyID)\n\t\t\t\t\tWHERE keyID IN (" . implode(', ', array_fill(0, sizeOf($keyIDs), '?')) . ") AND KP.keyID IS NULL";
         Zotero_DB::query($sql, $keyIDs);
     }
     // If group is locked by a sync, flag group for a timestamp update
     // once the sync is done so that the uploading user gets the change
     try {
         foreach ($userIDs as $userID) {
             if ($syncUploadQueueID = Zotero_Sync::getUploadQueueIDByUserID($userID)) {
                 Zotero_Sync::postWriteLog($syncUploadQueueID, 'group', $this->id, 'delete');
             }
         }
     } catch (Exception $e) {
         Z_Core::logError($e);
     }
     Zotero_Notifier::trigger('delete', 'library', $this->libraryID);
     Zotero_DB::commit();
     $this->erased = true;
 }
示例#27
0
Zotero_DB::addCallback("commit", array(Z_Core::$MC, "commit"));
Zotero_DB::addCallback("reset", array(Z_Core::$MC, "reset"));
//
// Set up AWS service factory
//
$awsConfig = ['region' => !empty(Z_CONFIG::$AWS_REGION) ? Z_CONFIG::$AWS_REGION : 'us-east-1', 'version' => 'latest', 'signature' => 'v4'];
// IAM role authentication
if (empty(Z_CONFIG::$AWS_ACCESS_KEY)) {
    // If APC cache is available, use that to cache temporary credentials
    if (function_exists('apc_store')) {
        $cache = new \Doctrine\Common\Cache\ApcCache();
    } else {
        $cache = new \Doctrine\Common\Cache\FilesystemCache(Z_ENV_BASE_PATH . 'tmp/cache');
    }
    $awsConfig['credentials'] = new \Aws\DoctrineCacheAdapter($cache);
} else {
    $awsConfig['credentials'] = ['key' => Z_CONFIG::$AWS_ACCESS_KEY, 'secret' => Z_CONFIG::$AWS_SECRET_KEY];
}
Z_Core::$AWS = new Aws\Sdk($awsConfig);
unset($awsConfig);
// Elastica
Z_Core::$Elastica = new \Elastica\Client(array('connections' => array_map(function ($hostAndPort) {
    preg_match('/^([^:]+)(:[0-9]+)?$/', $hostAndPort, $matches);
    return ['host' => $matches[1], 'port' => isset($matches[2]) ? $matches[2] : 9200];
}, Z_CONFIG::$SEARCH_HOSTS)));
require 'interfaces/IAuthenticationPlugin.inc.php';
require 'log.inc.php';
Z_Core::$debug = !empty(Z_CONFIG::$DEBUG_LOG);
// Load in functions
require 'functions/string.inc.php';
require 'functions/array.inc.php';
示例#28
0
 public function save()
 {
     if (!$this->libraryID) {
         trigger_error("Library ID must be set before saving", E_USER_ERROR);
     }
     Zotero_Creators::editCheck($this);
     // If empty, move on
     if ($this->firstName === '' && $this->lastName === '') {
         throw new Exception('First and last name are empty');
     }
     if ($this->fieldMode == 1 && $this->firstName !== '') {
         throw new Exception('First name must be empty in single-field mode');
     }
     if (!$this->hasChanged()) {
         Z_Core::debug("Creator {$this->id} has not changed");
         return false;
     }
     Zotero_DB::beginTransaction();
     try {
         $creatorID = $this->id ? $this->id : Zotero_ID::get('creators');
         $isNew = !$this->id;
         Z_Core::debug("Saving creator {$this->id}");
         $key = $this->key ? $this->key : $this->generateKey();
         $timestamp = Zotero_DB::getTransactionTimestamp();
         $dateAdded = $this->dateAdded ? $this->dateAdded : $timestamp;
         $dateModified = $this->changed['dateModified'] ? $this->dateModified : $timestamp;
         $fields = "firstName=?, lastName=?, fieldMode=?,\n\t\t\t\t\t\tlibraryID=?, `key`=?, dateAdded=?, dateModified=?, serverDateModified=?";
         $params = array($this->firstName, $this->lastName, $this->fieldMode, $this->libraryID, $key, $dateAdded, $dateModified, $timestamp);
         $shardID = Zotero_Shards::getByLibraryID($this->libraryID);
         try {
             if ($isNew) {
                 $sql = "INSERT INTO creators SET creatorID=?, {$fields}";
                 $stmt = Zotero_DB::getStatement($sql, true, $shardID);
                 Zotero_DB::queryFromStatement($stmt, array_merge(array($creatorID), $params));
                 // Remove from delete log if it's there
                 $sql = "DELETE FROM syncDeleteLogKeys WHERE libraryID=? AND objectType='creator' AND `key`=?";
                 Zotero_DB::query($sql, array($this->libraryID, $key), $shardID);
             } else {
                 $sql = "UPDATE creators SET {$fields} WHERE creatorID=?";
                 $stmt = Zotero_DB::getStatement($sql, true, $shardID);
                 Zotero_DB::queryFromStatement($stmt, array_merge($params, array($creatorID)));
             }
         } catch (Exception $e) {
             if (strpos($e->getMessage(), " too long") !== false) {
                 if (strlen($this->firstName) > 255) {
                     throw new Exception("=First name '" . mb_substr($this->firstName, 0, 50) . "…' too long");
                 }
                 if (strlen($this->lastName) > 255) {
                     if ($this->fieldMode == 1) {
                         throw new Exception("=Last name '" . mb_substr($this->lastName, 0, 50) . "…' too long");
                     } else {
                         throw new Exception("=Name '" . mb_substr($this->lastName, 0, 50) . "…' too long");
                     }
                 }
             }
             throw $e;
         }
         // The client updates the mod time of associated items here, but
         // we don't, because either A) this is from syncing, where appropriate
         // mod times come from the client or B) the change is made through
         // $item->setCreator(), which updates the mod time.
         //
         // If the server started to make other independent creator changes,
         // linked items would need to be updated.
         Zotero_DB::commit();
         Zotero_Creators::cachePrimaryData(array('id' => $creatorID, 'libraryID' => $this->libraryID, 'key' => $key, 'dateAdded' => $dateAdded, 'dateModified' => $dateModified, 'firstName' => $this->firstName, 'lastName' => $this->lastName, 'fieldMode' => $this->fieldMode));
     } catch (Exception $e) {
         Zotero_DB::rollback();
         throw $e;
     }
     // If successful, set values in object
     if (!$this->id) {
         $this->id = $creatorID;
     }
     if (!$this->key) {
         $this->key = $key;
     }
     $this->init();
     if ($isNew) {
         Zotero_Creators::cache($this);
         Zotero_Creators::cacheLibraryKeyID($this->libraryID, $key, $creatorID);
     }
     // TODO: invalidate memcache?
     return $this->id;
 }
示例#29
0
 /**
  * Converts a Zotero_Item object to a SimpleXMLElement item
  *
  * @param	object				$item		Zotero_Item object
  * @param	array				$data
  * @return	SimpleXMLElement					Item data as SimpleXML element
  */
 public static function convertItemToXML(Zotero_Item $item, $data = array())
 {
     $t = microtime(true);
     // Check cache for all items except imported attachments,
     // which don't have their versions updated when the client
     // updates their file metadata
     if (!$item->isImportedAttachment()) {
         $cacheVersion = 1;
         $cacheKey = "syncXMLItem_" . $item->libraryID . "/" . $item->id . "_" . $item->version . "_" . md5(json_encode($data)) . "_" . $cacheVersion . (isset(Z_CONFIG::$CACHE_VERSION_SYNC_XML_ITEM) ? "_" . Z_CONFIG::$CACHE_VERSION_SYNC_XML_ITEM : "");
         $xmlstr = Z_Core::$MC->get($cacheKey);
     } else {
         $cacheKey = false;
         $xmlstr = false;
     }
     if ($xmlstr) {
         $xml = new SimpleXMLElement($xmlstr);
         StatsD::timing("api.items.itemToSyncXML.cached", (microtime(true) - $t) * 1000);
         StatsD::increment("memcached.items.itemToSyncXML.hit");
         // Skip the cache every 10 times for now, to ensure cache sanity
         if (Z_Core::probability(10)) {
             //$xmlstr = $xml->saveXML();
         } else {
             Z_Core::debug("Using cached sync XML item");
             return $xml;
         }
     }
     $xml = new SimpleXMLElement('<item/>');
     // Primary fields
     foreach (self::$primaryFields as $field) {
         switch ($field) {
             case 'id':
             case 'serverDateModified':
             case 'version':
                 continue 2;
             case 'itemTypeID':
                 $xmlField = 'itemType';
                 $xmlValue = Zotero_ItemTypes::getName($item->{$field});
                 break;
             default:
                 $xmlField = $field;
                 $xmlValue = $item->{$field};
         }
         $xml[$xmlField] = $xmlValue;
     }
     // Item data
     $itemTypeID = $item->itemTypeID;
     $fieldIDs = $item->getUsedFields();
     foreach ($fieldIDs as $fieldID) {
         $val = $item->getField($fieldID);
         if ($val == '') {
             continue;
         }
         $f = $xml->addChild('field', htmlspecialchars($val));
         $fieldName = Zotero_ItemFields::getName($fieldID);
         // Special handling for renamed computerProgram 'version' field
         if ($itemTypeID == 32 && $fieldName == 'versionNumber') {
             $fieldName = 'version';
         }
         $f['name'] = htmlspecialchars($fieldName);
     }
     // Deleted item flag
     if ($item->deleted) {
         $xml['deleted'] = '1';
     }
     if ($item->isNote() || $item->isAttachment()) {
         $sourceItemID = $item->getSource();
         if ($sourceItemID) {
             $sourceItem = Zotero_Items::get($item->libraryID, $sourceItemID);
             if (!$sourceItem) {
                 throw new Exception("Source item {$sourceItemID} not found");
             }
             $xml['sourceItem'] = $sourceItem->key;
         }
     }
     // Group modification info
     $createdByUserID = null;
     $lastModifiedByUserID = null;
     switch (Zotero_Libraries::getType($item->libraryID)) {
         case 'group':
             $createdByUserID = $item->createdByUserID;
             $lastModifiedByUserID = $item->lastModifiedByUserID;
             break;
     }
     if ($createdByUserID) {
         $xml['createdByUserID'] = $createdByUserID;
     }
     if ($lastModifiedByUserID) {
         $xml['lastModifiedByUserID'] = $lastModifiedByUserID;
     }
     if ($item->isAttachment()) {
         $xml['linkMode'] = $item->attachmentLinkMode;
         $xml['mimeType'] = $item->attachmentMIMEType;
         if ($item->attachmentCharset) {
             $xml['charset'] = $item->attachmentCharset;
         }
         $storageModTime = $item->attachmentStorageModTime;
         if ($storageModTime) {
             $xml['storageModTime'] = $storageModTime;
         }
         $storageHash = $item->attachmentStorageHash;
         if ($storageHash) {
             $xml['storageHash'] = $storageHash;
         }
         // TODO: get from a constant
         if ($item->attachmentLinkMode != 3) {
             $xml->addChild('path', htmlspecialchars($item->attachmentPath));
         }
     }
     // Note
     if ($item->isNote() || $item->isAttachment()) {
         // Get htmlspecialchars'ed note
         $note = $item->getNote(false, true);
         if ($note !== '') {
             $xml->addChild('note', $note);
         } else {
             if ($item->isNote()) {
                 $xml->addChild('note', '');
             }
         }
     }
     // Creators
     $creators = $item->getCreators();
     if ($creators) {
         foreach ($creators as $index => $creator) {
             $c = $xml->addChild('creator');
             $c['key'] = $creator['ref']->key;
             $c['creatorType'] = htmlspecialchars(Zotero_CreatorTypes::getName($creator['creatorTypeID']));
             $c['index'] = $index;
             if (empty($data['updatedCreators']) || !in_array($creator['ref']->id, $data['updatedCreators'])) {
                 $cNode = dom_import_simplexml($c);
                 $creatorXML = Zotero_Creators::convertCreatorToXML($creator['ref'], $cNode->ownerDocument);
                 $cNode->appendChild($creatorXML);
             }
         }
     }
     // Related items
     $relatedKeys = $item->relatedItems;
     $keys = array();
     foreach ($relatedKeys as $relatedKey) {
         if (Zotero_Items::getByLibraryAndKey($item->libraryID, $relatedKey)) {
             $keys[] = $relatedKey;
         }
     }
     if ($keys) {
         $xml->related = implode(' ', $keys);
     }
     if ($xmlstr) {
         $uncached = $xml->saveXML();
         if ($xmlstr != $uncached) {
             error_log("Cached sync XML item does not match");
             error_log("  Cached: " . $xmlstr);
             error_log("Uncached: " . $uncached);
         }
     } else {
         $xmlstr = $xml->saveXML();
         if ($cacheKey) {
             Z_Core::$MC->set($cacheKey, $xmlstr, 3600);
             // 1 hour for now
         }
         StatsD::timing("api.items.itemToSyncXML.uncached", (microtime(true) - $t) * 1000);
         StatsD::increment("memcached.items.itemToSyncXML.miss");
     }
     return $xml;
 }
示例#30
0
 public function save()
 {
     if (!$this->loaded) {
         Z_Core::debug("Not saving unloaded key {$this->id}");
         return;
     }
     if (!$this->userID) {
         throw new Exception("Cannot save key without userID");
     }
     if (!$this->name) {
         throw new Exception("Cannot save key without name");
     }
     if (strlen($this->name) > 255) {
         throw new Exception("Key name too long", Z_ERROR_KEY_NAME_TOO_LONG);
     }
     Zotero_DB::beginTransaction();
     if (!$this->key) {
         $this->key = Zotero_Keys::generate();
     }
     $fields = array('key', 'userID', 'name');
     $sql = "INSERT INTO `keys` (keyID, `key`, userID, name) VALUES (?, ?, ?, ?)";
     $params = array($this->id);
     foreach ($fields as $field) {
         $params[] = $this->{$field};
     }
     $sql .= " ON DUPLICATE KEY UPDATE ";
     $q = array();
     foreach ($fields as $field) {
         $q[] = "`{$field}`=?";
         $params[] = $this->{$field};
     }
     $sql .= implode(", ", $q);
     $insertID = Zotero_DB::query($sql, $params);
     if (!$this->id) {
         if (!$insertID) {
             throw new Exception("Key id not available after INSERT");
         }
         $this->id = $insertID;
     }
     // Delete existing permissions
     $sql = "DELETE FROM keyPermissions WHERE keyID=?";
     Zotero_DB::query($sql, $this->id);
     if (isset($this->changed['permissions'])) {
         foreach ($this->changed['permissions'] as $libraryID => $p) {
             foreach ($p as $permission => $changed) {
                 $enabled = $this->permissions[$libraryID][$permission];
                 if (!$enabled) {
                     continue;
                 }
                 $sql = "INSERT INTO keyPermissions VALUES (?, ?, ?, ?)";
                 // TODO: support negative permissions
                 Zotero_DB::query($sql, array($this->id, $libraryID, $permission, 1));
             }
         }
     }
     Zotero_DB::commit();
     $this->load();
     return $this->id;
 }