Пример #1
0
 private static function getNext($table)
 {
     $sql = "REPLACE INTO {$table} (stub) VALUES ('a')";
     if (Z_Core::probability(2)) {
         try {
             Zotero_ID_DB_1::query($sql);
             $id = Zotero_ID_DB_1::valueQuery("SELECT LAST_INSERT_ID()");
         } catch (Exception $e) {
             Z_Core::logError("Error accessing ID server 1");
             Zotero_ID_DB_2::query($sql);
             $id = Zotero_ID_DB_2::valueQuery("SELECT LAST_INSERT_ID()");
         }
     } else {
         try {
             Zotero_ID_DB_2::query($sql);
             $id = Zotero_ID_DB_2::valueQuery("SELECT LAST_INSERT_ID()");
         } catch (Exception $e) {
             Z_Core::logError("Error accessing ID server 2");
             Zotero_ID_DB_1::query($sql);
             $id = Zotero_ID_DB_1::valueQuery("SELECT LAST_INSERT_ID()");
         }
     }
     if (!$id || !is_int($id)) {
         throw new Exception("Invalid id {$id}");
     }
     return $id;
 }
Пример #2
0
 public static function htmlPurify($text)
 {
     if (!isset(self::$HTMLPurifier)) {
         require 'HTMLPurifier/HTMLPurifier.standalone.php';
         $c = HTMLPurifier_Config::createDefault();
         $c->set('HTML.Doctype', 'XHTML 1.0 Strict');
         $c->set('Cache.SerializerPath', Z_ENV_TMP_PATH);
         self::$HTMLPurifier = new HTMLPurifier($c);
     }
     return self::$HTMLPurifier->purify($text);
 }
Пример #3
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;
 }
Пример #4
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();
 }
Пример #5
0
 /**
  * Log an array of category/message pairs
  */
 public static function logm($categoryMessagePairs)
 {
     $scribe = Z_CONFIG::$LOG_TO_SCRIBE;
     $cli = Z_Core::isCommandLine();
     // Scribe and CLI need additional info
     if ($scribe || $cli) {
         // Parse timestamp into date and milliseconds
         $ts = microtime(true);
         if (strpos($ts, '.') === false) {
             $ts .= '.';
         }
         list($ts, $msec) = explode('.', $ts);
         $date = new DateTime(date(DATE_RFC822, $ts));
         $date->setTimezone(new DateTimeZone(Z_CONFIG::$LOG_TIMEZONE));
         $date = $date->format('Y-m-d H:i:s') . '.' . str_pad($msec, 4, '0');
         // Get remote IP address
         if (!$cli) {
             $ipAddress = IPAddress::getIP();
         }
         // Get server hostname
         if ($scribe) {
             $host = gethostname();
             if (strpos($host, '.') !== false) {
                 $host = substr($host, 0, strpos($host, '.'));
             }
         }
     }
     $messages = array();
     foreach ($categoryMessagePairs as $pair) {
         // Scribe
         if ($scribe) {
             $messages[] = array('category' => $pair[0], 'message' => "{$date} [{$ipAddress}] [{$host}] " . $pair[1]);
         } else {
             if ($cli) {
                 $messages[] = array('category' => $pair[0], 'message' => $date . " " . $pair[1]);
             } else {
                 $messages[] = array('category' => $pair[0], 'message' => $pair[1]);
             }
         }
     }
     if (Z_CONFIG::$LOG_TO_SCRIBE) {
         self::logToScribe($messages);
     } else {
         self::logToErrorLog($messages);
     }
 }
Пример #6
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));
     }
 }
Пример #7
0
 public static function getAllAdvanced($userID = false, $params = array(), $permissions = null)
 {
     $buffer = 20;
     $maxTimes = 3;
     $groups = array();
     $start = !empty($params['start']) ? $params['start'] : 0;
     $limit = !empty($params['limit']) ? $params['limit'] + $buffer : false;
     $totalResults = null;
     $times = 0;
     while (true) {
         if ($times > 0) {
             Z_Core::logError('Getting more groups in Zotero_Groups::getAllAdvanced()');
         }
         $sql = "SELECT SQL_CALC_FOUND_ROWS G.groupID, GUO.userID AS ownerUserID FROM groups G\n\t\t\t\t\tJOIN groupUsers GUO ON (G.groupID=GUO.groupID AND GUO.role='owner') ";
         $sqlParams = array();
         if ($userID) {
             $sql .= "JOIN groupUsers GUA ON (G.groupID=GUA.groupID) WHERE GUA.userID=? ";
             $sqlParams[] = $userID;
         }
         $paramSQL = "";
         $includeEmpty = false;
         if (!empty($params['q'])) {
             if (!is_array($params['q'])) {
                 $params['q'] = array($params['q']);
             }
             foreach ($params['q'] as $q) {
                 $field = explode(":", $q);
                 if (sizeOf($field) == 2) {
                     switch ($field[0]) {
                         case 'slug':
                             $includeEmpty = true;
                             break;
                         default:
                             throw new Exception("Cannot search by group field '{$field[0]}'", Z_ERROR_INVALID_GROUP_TYPE);
                     }
                     $paramSQL .= "AND " . $field[0];
                     // If first character is '-', negate
                     $paramSQL .= $field[0][0] == '-' ? '!' : '';
                     $paramSQL .= "=? ";
                     $sqlParams[] = $field[1];
                 } else {
                     $paramSQL .= "AND name LIKE ? ";
                     $sqlParams[] = "%{$q}%";
                 }
             }
         }
         if (!$userID) {
             if ($includeEmpty) {
                 $sql .= "WHERE 1 ";
             } else {
                 // Don't include groups that have never had items
                 $sql .= "JOIN libraries L ON (G.libraryID=L.libraryID)\n\t\t\t\t\t\t\tWHERE L.lastUpdated != '0000-00-00 00:00:00' ";
             }
         }
         $sql .= $paramSQL;
         if (!empty($params['fq'])) {
             if (!is_array($params['fq'])) {
                 $params['fq'] = array($params['fq']);
             }
             foreach ($params['fq'] as $fq) {
                 $facet = explode(":", $fq);
                 if (sizeOf($facet) == 2 && preg_match('/-?GroupType/', $facet[0])) {
                     switch ($facet[1]) {
                         case 'PublicOpen':
                         case 'PublicClosed':
                         case 'Private':
                             break;
                         default:
                             throw new Exception("Invalid group type '{$facet[1]}'", Z_ERROR_INVALID_GROUP_TYPE);
                     }
                     $sql .= "AND type";
                     // If first character is '-', negate
                     $sql .= $facet[0][0] == '-' ? '!' : '';
                     $sql .= "=? ";
                     $sqlParams[] = $facet[1];
                 }
             }
         }
         if (!empty($params['order'])) {
             $order = $params['order'];
             if ($order == 'title') {
                 $order = 'name';
             }
             $sql .= "ORDER BY {$order}";
             if (!empty($params['sort'])) {
                 $sql .= " " . $params['sort'] . " ";
             }
         }
         // Set limit higher than the actual limit, in case some groups are
         // removed during access checks
         //
         // Actual limiting is done below
         if ($limit) {
             $sql .= "LIMIT ?, ?";
             $sqlParams[] = $start;
             $sqlParams[] = $limit;
         }
         $rows = Zotero_DB::query($sql, $sqlParams);
         if (!$rows) {
             break;
         }
         if (!$totalResults) {
             $foundRows = Zotero_DB::valueQuery("SELECT FOUND_ROWS()");
             $totalResults = $foundRows;
         }
         // Include only groups with non-banned owners
         $owners = array();
         foreach ($rows as $row) {
             $owners[] = $row['ownerUserID'];
         }
         $owners = Zotero_Users::getValidUsers($owners);
         $ids = array();
         foreach ($rows as $row) {
             if (!in_array($row['ownerUserID'], $owners)) {
                 $totalResults--;
                 continue;
             }
             $ids[] = $row['groupID'];
         }
         $batchStartPos = sizeOf($groups);
         foreach ($ids as $id) {
             $group = Zotero_Groups::get($id, true);
             $groups[] = $group;
         }
         // Remove groups that can't be accessed
         if ($permissions) {
             for ($i = $batchStartPos; $i < sizeOf($groups); $i++) {
                 $libraryID = (int) $groups[$i]->libraryID;
                 // TEMP: casting shouldn't be necessary
                 if (!$permissions->canAccess($libraryID)) {
                     array_splice($groups, $i, 1);
                     $i--;
                     $totalResults--;
                 }
             }
         }
         $times++;
         if ($times == $maxTimes) {
             Z_Core::logError('Too many queries in Zotero_Groups::getAllAdvanced()');
             break;
         }
         if (empty($params['limit'])) {
             break;
         }
         // If we have enough groups to fill the limit, stop
         if (sizeOf($groups) > $params['limit']) {
             break;
         }
         // If there no more rows, stop
         if ($start + sizeOf($rows) == $foundRows) {
             break;
         }
         // This shouldn't happen
         if ($start + sizeOf($rows) > $foundRows) {
             Z_Core::logError('More rows than $foundRows in Zotero_Groups::getAllAdvanced()');
         }
         $start = $start + sizeOf($rows);
         // Get number we still need plus the buffer or all remaining, whichever is lower
         $limit = min($params['limit'] - sizeOf($groups) + $buffer, $foundRows - $start);
     }
     // TODO: generate previous start value
     if (!$groups) {
         return array('groups' => array(), 'totalResults' => 0);
     }
     // Fake limiting -- we can't just use SQL limit because
     // some groups might be inaccessible
     if (!empty($params['limit'])) {
         $groups = array_slice($groups, 0, $params['limit']);
     }
     $results = array('groups' => $groups, 'totalResults' => $totalResults);
     return $results;
 }
Пример #8
0
 public function items()
 {
     if (($this->method == 'POST' || $this->method == 'PUT') && !$this->body) {
         $this->e400("{$this->method} data not provided");
     }
     $itemIDs = array();
     $responseItems = array();
     $responseKeys = array();
     $totalResults = null;
     //
     // Single item
     //
     if (($this->objectID || $this->objectKey) && !$this->subset) {
         if ($this->fileMode) {
             if ($this->fileView) {
                 $this->allowMethods(array('GET', 'HEAD', 'POST'));
             } else {
                 $this->allowMethods(array('GET', 'PUT', 'POST', 'HEAD', 'PATCH'));
             }
         } else {
             $this->allowMethods(array('GET', 'PUT', 'DELETE'));
         }
         // Check for general library access
         if (!$this->permissions->canAccess($this->objectLibraryID)) {
             //var_dump($this->objectLibraryID);
             //var_dump($this->permissions);
             $this->e403();
         }
         if ($this->objectKey) {
             $item = Zotero_Items::getByLibraryAndKey($this->objectLibraryID, $this->objectKey);
         } else {
             try {
                 $item = Zotero_Items::get($this->objectLibraryID, $this->objectID);
             } catch (Exception $e) {
                 if ($e->getCode() == Z_ERROR_OBJECT_LIBRARY_MISMATCH) {
                     $item = false;
                 } else {
                     throw $e;
                 }
             }
         }
         if (!$item) {
             // Possibly temporary workaround to block unnecessary full syncs
             if ($this->fileMode && $this->method == 'POST') {
                 // If > 2 requests for missing file, trigger a full sync via 404
                 $cacheKey = "apiMissingFile_" . $this->objectLibraryID . "_" . ($this->objectKey ? $this->objectKey : $this->objectID);
                 $set = Z_Core::$MC->get($cacheKey);
                 if (!$set) {
                     Z_Core::$MC->set($cacheKey, 1, 86400);
                 } else {
                     if ($set < 2) {
                         Z_Core::$MC->increment($cacheKey);
                     } else {
                         Z_Core::$MC->delete($cacheKey);
                         $this->e404("A file sync error occurred. Please sync again.");
                     }
                 }
                 $this->e500("A file sync error occurred. Please sync again.");
             }
             // If we have an id, make sure this isn't really an all-numeric key
             if ($this->objectID && strlen($this->objectID) == 8 && preg_match('/[0-9]{8}/', $this->objectID)) {
                 $item = Zotero_Items::getByLibraryAndKey($this->objectLibraryID, $this->objectID);
                 if ($item) {
                     $this->objectKey = $this->objectID;
                     unset($this->objectID);
                 }
             }
             if (!$item) {
                 $this->e404("Item does not exist");
             }
         }
         if ($item->isNote() && !$this->permissions->canAccess($this->objectLibraryID, 'notes')) {
             $this->e403();
         }
         // Make sure URL libraryID matches item libraryID
         if ($this->objectLibraryID != $item->libraryID) {
             $this->e404("Item does not exist");
         }
         // File access mode
         if ($this->fileMode) {
             $this->_handleFileRequest($item);
         }
         // If id, redirect to key URL
         if ($this->objectID) {
             $this->allowMethods(array('GET'));
             $qs = !empty($_SERVER['QUERY_STRING']) ? '?' . $_SERVER['QUERY_STRING'] : '';
             header("Location: " . Zotero_API::getItemURI($item) . $qs);
             exit;
         }
         if ($this->scopeObject) {
             switch ($this->scopeObject) {
                 // Remove item from collection
                 case 'collections':
                     $this->allowMethods(array('DELETE'));
                     if (!$this->permissions->canWrite($this->objectLibraryID)) {
                         $this->e403("Write access denied");
                     }
                     $collection = Zotero_Collections::getByLibraryAndKey($this->objectLibraryID, $this->scopeObjectKey);
                     if (!$collection) {
                         $this->e404("Collection not found");
                     }
                     if (!$collection->hasItem($item->id)) {
                         $this->e404("Item not found in collection");
                     }
                     Zotero_DB::beginTransaction();
                     $timestamp = Zotero_Libraries::updateTimestamps($this->objectLibraryID);
                     Zotero_DB::registerTransactionTimestamp($timestamp);
                     $collection->removeItem($item->id);
                     Zotero_DB::commit();
                     $this->e204();
                 default:
                     $this->e400();
             }
         }
         if ($this->method == 'PUT' || $this->method == 'DELETE') {
             if (!$this->permissions->canWrite($this->objectLibraryID)) {
                 $this->e403("Write access denied");
             }
             if (!Z_CONFIG::$TESTING_SITE || empty($_GET['skipetag'])) {
                 if (empty($_SERVER['HTTP_IF_MATCH'])) {
                     $this->e400("If-Match header not provided");
                 }
                 if (!preg_match('/^"?([a-f0-9]{32})"?$/', $_SERVER['HTTP_IF_MATCH'], $matches)) {
                     $this->e400("Invalid ETag in If-Match header");
                 }
                 if ($item->etag != $matches[1]) {
                     $this->e412("ETag does not match current version of item");
                 }
             }
             // Update existing item
             if ($this->method == 'PUT') {
                 $obj = $this->jsonDecode($this->body);
                 Zotero_Items::updateFromJSON($item, $obj, false, null, $this->userID);
                 $this->queryParams['format'] = 'atom';
                 $this->queryParams['content'] = array('json');
                 if ($cacheKey = $this->getWriteTokenCacheKey()) {
                     Z_Core::$MC->set($cacheKey, true, $this->writeTokenCacheTime);
                 }
             } else {
                 Zotero_Items::delete($this->objectLibraryID, $this->objectKey, true);
                 try {
                     Zotero_Processors::notifyProcessors('index');
                 } catch (Exception $e) {
                     Z_Core::logError($e);
                 }
                 $this->e204();
             }
         }
         // Display item
         switch ($this->queryParams['format']) {
             case 'atom':
                 $this->responseXML = Zotero_Items::convertItemToAtom($item, $this->queryParams, $this->apiVersion, $this->permissions);
                 break;
             case 'bib':
                 echo Zotero_Cite::getBibliographyFromCitationServer(array($item), $this->queryParams['style'], $this->queryParams['css']);
                 exit;
             case 'csljson':
                 $json = Zotero_Cite::getJSONFromItems(array($item), true);
                 if ($this->queryParams['pprint']) {
                     header("Content-Type: text/plain");
                     $json = Zotero_Utilities::json_encode_pretty($json);
                 } else {
                     header("Content-Type: application/vnd.citationstyles.csl+json");
                     $json = json_encode($json);
                 }
                 echo $json;
                 exit;
             default:
                 $export = Zotero_Translate::doExport(array($item), $this->queryParams['format']);
                 if ($this->queryParams['pprint']) {
                     header("Content-Type: text/plain");
                 } else {
                     header("Content-Type: " . $export['mimeType']);
                 }
                 echo $export['body'];
                 exit;
         }
     } else {
         $this->allowMethods(array('GET', 'POST'));
         if (!$this->permissions->canAccess($this->objectLibraryID)) {
             $this->e403();
         }
         $includeTrashed = false;
         $formatAsKeys = $this->queryParams['format'] == 'keys';
         if ($this->scopeObject) {
             $this->allowMethods(array('GET', 'POST'));
             // If id, redirect to key URL
             if ($this->scopeObjectID) {
                 $this->allowMethods(array('GET'));
                 if (!in_array($this->scopeObject, array("collections", "tags"))) {
                     $this->e400();
                 }
                 $className = 'Zotero_' . ucwords($this->scopeObject);
                 $obj = call_user_func(array($className, 'get'), $this->objectLibraryID, $this->scopeObjectID);
                 if (!$obj) {
                     $this->e404("Scope " . substr($this->scopeObject, 0, -1) . " not found");
                 }
                 $base = call_user_func(array('Zotero_API', 'get' . substr(ucwords($this->scopeObject), 0, -1) . 'URI'), $obj);
                 $qs = !empty($_SERVER['QUERY_STRING']) ? '?' . $_SERVER['QUERY_STRING'] : '';
                 header("Location: " . $base . "/items" . $qs);
                 exit;
             }
             switch ($this->scopeObject) {
                 case 'collections':
                     $collection = Zotero_Collections::getByLibraryAndKey($this->objectLibraryID, $this->scopeObjectKey);
                     if (!$collection) {
                         $this->e404("Collection not found");
                     }
                     // Add items to collection
                     if ($this->method == 'POST') {
                         if (!$this->permissions->canWrite($this->objectLibraryID)) {
                             $this->e403("Write access denied");
                         }
                         Zotero_DB::beginTransaction();
                         $timestamp = Zotero_Libraries::updateTimestamps($this->objectLibraryID);
                         Zotero_DB::registerTransactionTimestamp($timestamp);
                         $itemKeys = explode(' ', $this->body);
                         $itemIDs = array();
                         foreach ($itemKeys as $key) {
                             try {
                                 $item = Zotero_Items::getByLibraryAndKey($this->objectLibraryID, $key);
                             } catch (Exception $e) {
                                 if ($e->getCode() == Z_ERROR_OBJECT_LIBRARY_MISMATCH) {
                                     $item = false;
                                 } else {
                                     throw $e;
                                 }
                             }
                             if (!$item) {
                                 throw new Exception("Item '{$key}' not found in library", Z_ERROR_INVALID_INPUT);
                             }
                             if ($item->getSource()) {
                                 throw new Exception("Child items cannot be added to collections directly", Z_ERROR_INVALID_INPUT);
                             }
                             $itemIDs[] = $item->id;
                         }
                         $collection->addItems($itemIDs);
                         Zotero_DB::commit();
                         $this->e204();
                     }
                     $title = "Items in Collection ‘" . $collection->name . "’";
                     $itemIDs = $collection->getChildItems();
                     break;
                 case 'tags':
                     $this->allowMethods(array('GET'));
                     $tagIDs = Zotero_Tags::getIDs($this->objectLibraryID, $this->scopeObjectName);
                     if (!$tagIDs) {
                         $this->e404("Tag not found");
                     }
                     $itemIDs = array();
                     $title = '';
                     foreach ($tagIDs as $tagID) {
                         $tag = new Zotero_Tag();
                         $tag->libraryID = $this->objectLibraryID;
                         $tag->id = $tagID;
                         // Use a real tag name, in case case differs
                         if (!$title) {
                             $title = "Items of Tag ‘" . $tag->name . "’";
                         }
                         $itemIDs = array_merge($itemIDs, $tag->getLinkedItems(true));
                     }
                     $itemIDs = array_unique($itemIDs);
                     break;
                 default:
                     throw new Exception("Invalid items scope object '{$this->scopeObject}'");
             }
         } else {
             // Top-level items
             if ($this->subset == 'top') {
                 $this->allowMethods(array('GET'));
                 $title = "Top-Level Items";
                 $results = Zotero_Items::search($this->objectLibraryID, true, $this->queryParams, false, $formatAsKeys);
             } else {
                 if ($this->subset == 'trash') {
                     $this->allowMethods(array('GET'));
                     $title = "Deleted Items";
                     $itemIDs = Zotero_Items::getDeleted($this->objectLibraryID, true);
                     $includeTrashed = true;
                 } else {
                     if ($this->subset == 'children') {
                         // If we have an id, make sure this isn't really an all-numeric key
                         if ($this->objectID && strlen($this->objectID) == 8 && preg_match('/[0-9]{8}/', $this->objectID)) {
                             $item = Zotero_Items::getByLibraryAndKey($this->objectLibraryID, $this->objectID);
                             if ($item) {
                                 $this->objectKey = $this->objectID;
                                 unset($this->objectID);
                             }
                         }
                         // If id, redirect to key URL
                         if ($this->objectID) {
                             $this->allowMethods(array('GET'));
                             $item = Zotero_Items::get($this->objectLibraryID, $this->objectID);
                             if (!$item) {
                                 $this->e404("Item not found");
                             }
                             $qs = !empty($_SERVER['QUERY_STRING']) ? '?' . $_SERVER['QUERY_STRING'] : '';
                             header("Location: " . Zotero_API::getItemURI($item) . '/children' . $qs);
                             exit;
                         }
                         $item = Zotero_Items::getByLibraryAndKey($this->objectLibraryID, $this->objectKey);
                         if (!$item) {
                             $this->e404("Item not found");
                         }
                         // Create new child items
                         if ($this->method == 'POST') {
                             if (!$this->permissions->canWrite($this->objectLibraryID)) {
                                 $this->e403("Write access denied");
                             }
                             $obj = $this->jsonDecode($this->body);
                             $keys = Zotero_Items::addFromJSON($obj, $this->objectLibraryID, $item, $this->userID);
                             if ($cacheKey = $this->getWriteTokenCacheKey()) {
                                 Z_Core::$MC->set($cacheKey, true, $this->writeTokenCacheTime);
                             }
                             $uri = Zotero_API::getItemURI($item) . "/children";
                             $queryString = "itemKey=" . urlencode(implode(",", $keys)) . "&content=json";
                             if ($this->apiKey) {
                                 $queryString .= "&key=" . $this->apiKey;
                             }
                             $uri .= "?" . $queryString;
                             $this->responseCode = 201;
                             $this->queryParams = Zotero_API::parseQueryParams($queryString, $this->action, false);
                         }
                         // Display items
                         $title = "Child Items of ‘" . $item->getDisplayTitle() . "’";
                         $notes = $item->getNotes();
                         $attachments = $item->getAttachments();
                         $itemIDs = array_merge($notes, $attachments);
                     } else {
                         // Create new items
                         if ($this->method == 'POST') {
                             if (!$this->permissions->canWrite($this->objectLibraryID)) {
                                 $this->e403("Write access denied");
                             }
                             $obj = $this->jsonDecode($this->body);
                             if (isset($obj->url)) {
                                 $response = Zotero_Items::addFromURL($obj, $this->objectLibraryID, $this->userID, $this->getTranslationToken());
                                 if ($response instanceof stdClass) {
                                     header("Content-Type: application/json");
                                     echo json_encode($response->select);
                                     $this->e300();
                                 } else {
                                     if (is_int($response)) {
                                         switch ($response) {
                                             case 501:
                                                 $this->e501("No translators found for URL");
                                                 break;
                                             default:
                                                 $this->e500("Error translating URL");
                                         }
                                     } else {
                                         $keys = $response;
                                     }
                                 }
                             } else {
                                 $keys = Zotero_Items::addFromJSON($obj, $this->objectLibraryID, null, $this->userID);
                             }
                             if (!$keys) {
                                 throw new Exception("No items added");
                             }
                             if ($cacheKey = $this->getWriteTokenCacheKey()) {
                                 Z_Core::$MC->set($cacheKey, true, $this->writeTokenCacheTime);
                             }
                             $uri = Zotero_API::getItemsURI($this->objectLibraryID);
                             $queryString = "itemKey=" . urlencode(implode(",", $keys)) . "&content=json";
                             if ($this->apiKey) {
                                 $queryString .= "&key=" . $this->apiKey;
                             }
                             $uri .= "?" . $queryString;
                             $this->responseCode = 201;
                             $this->queryParams = Zotero_API::parseQueryParams($queryString, $this->action, false);
                         }
                         $title = "Items";
                         $results = Zotero_Items::search($this->objectLibraryID, false, $this->queryParams, false, $formatAsKeys);
                     }
                 }
             }
             if (!empty($results)) {
                 if ($formatAsKeys) {
                     $responseKeys = $results['keys'];
                 } else {
                     $responseItems = $results['items'];
                 }
                 $totalResults = $results['total'];
             }
         }
         if ($this->queryParams['format'] == 'bib') {
             if (($itemIDs ? sizeOf($itemIDs) : $results['total']) > Zotero_API::$maxBibliographyItems) {
                 $this->e413("Cannot generate bibliography with more than " . Zotero_API::$maxBibliographyItems . " items");
             }
         }
         if ($itemIDs) {
             $this->queryParams['itemIDs'] = $itemIDs;
             $results = Zotero_Items::search($this->objectLibraryID, false, $this->queryParams, $includeTrashed, $formatAsKeys);
             if ($formatAsKeys) {
                 $responseKeys = $results['keys'];
             } else {
                 $responseItems = $results['items'];
             }
             $totalResults = $results['total'];
         } else {
             if (!isset($results)) {
                 if ($formatAsKeys) {
                     $responseKeys = array();
                 } else {
                     $responseItems = array();
                 }
                 $totalResults = 0;
             }
         }
         // Remove notes if not user and not public
         for ($i = 0; $i < sizeOf($responseItems); $i++) {
             if ($responseItems[$i]->isNote() && !$this->permissions->canAccess($responseItems[$i]->libraryID, 'notes')) {
                 array_splice($responseItems, $i, 1);
                 $totalResults--;
                 $i--;
             }
         }
         switch ($this->queryParams['format']) {
             case 'atom':
                 $this->responseXML = Zotero_Atom::createAtomFeed($this->getFeedNamePrefix($this->objectLibraryID) . $title, $this->uri, $responseItems, $totalResults, $this->queryParams, $this->apiVersion, $this->permissions);
                 break;
             case 'bib':
                 echo Zotero_Cite::getBibliographyFromCitationServer($responseItems, $this->queryParams['style'], $this->queryParams['css']);
                 exit;
             case 'csljson':
                 $json = Zotero_Cite::getJSONFromItems($responseItems, true);
                 if ($this->queryParams['pprint']) {
                     header("Content-Type: text/plain");
                     $json = Zotero_Utilities::json_encode_pretty($json);
                 } else {
                     header("Content-Type: application/vnd.citationstyles.csl+json");
                     $json = json_encode($json);
                 }
                 echo $json;
                 exit;
             case 'keys':
                 if (!$formatAsKeys) {
                     $responseKeys = array();
                     foreach ($responseItems as $item) {
                         $responseKeys[] = $item->key;
                     }
                 }
                 header("Content-Type: text/plain");
                 echo implode("\n", $responseKeys) . "\n";
                 exit;
             default:
                 $export = Zotero_Translate::doExport($responseItems, $this->queryParams['format']);
                 if ($this->queryParams['pprint']) {
                     header("Content-Type: text/plain");
                 } else {
                     header("Content-Type: " . $export['mimeType']);
                 }
                 echo $export['body'];
                 exit;
         }
     }
     $this->end();
 }
Пример #9
0
 public static function getInstitutionalUserQuota($userID)
 {
     // TODO: config
     $dev = Z_ENV_TESTING_SITE ? "_test" : "";
     $databaseName = "zotero_www{$dev}";
     // Get maximum institutional quota by e-mail domain
     $sql = "SELECT IFNULL(MAX(storageQuota), 0) FROM {$databaseName}.users_email\n\t\t\t\tJOIN {$databaseName}.storage_institutions ON (SUBSTRING_INDEX(email, '@', -1)=domain)\n\t\t\t\tWHERE userID=?";
     try {
         $institutionalDomainQuota = Zotero_WWW_DB_2::valueQuery($sql, $userID);
     } catch (Exception $e) {
         Z_Core::logError("WARNING: {$e} -- retrying on primary");
         $institutionalDomainQuota = Zotero_WWW_DB_1::valueQuery($sql, $userID);
     }
     // Get maximum institutional quota by e-mail address
     $sql = "SELECT IFNULL(MAX(storageQuota), 0) FROM {$databaseName}.users_email\n\t\t\t\tJOIN {$databaseName}.storage_institution_email USING (email)\n\t\t\t\tJOIN {$databaseName}.storage_institutions USING (institutionID)\n\t\t\t\tWHERE userID=?";
     try {
         $institutionalEmailQuota = Zotero_WWW_DB_2::valueQuery($sql, $userID);
     } catch (Exception $e) {
         Z_Core::logError("WARNING: {$e} -- retrying on primary");
         $institutionalEmailQuota = Zotero_WWW_DB_1::valueQuery($sql, $userID);
     }
     $quota = max($institutionalDomainQuota, $institutionalEmailQuota);
     return $quota ? $quota : false;
 }
Пример #10
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;
 }
Пример #11
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;
 }
Пример #12
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();
 }
Пример #13
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;
 }
Пример #14
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;
 }
Пример #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
 public static function sanitize($text)
 {
     return Z_Core::htmlPurify($text);
 }
Пример #17
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;
 }
Пример #18
0
 protected static function logQuery($sql, $params, $shardID)
 {
     Z_Core::debug($sql . ($params ? " (" . (is_scalar($params) ? $params : implode(",", $params)) . ") ({$shardID})" : ""));
 }
Пример #19
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);
     }
 }
Пример #20
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');
	}
Пример #21
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;
 }
Пример #22
0
 private static function extractZip($file, $destDir)
 {
     $za = new ZipArchive();
     $za->open($file);
     $entries = array();
     for ($i = 0, $max = $za->numFiles; $i < $max; $i++) {
         $stat = $za->statIndex($i);
         // Skip files not at the top level
         if ($stat['name'] != basename($stat['name'])) {
             continue;
         }
         // Skip dot files or ztmp (which we use as temp dir)
         if ($stat['name'][0] == '.' || $stat['name'] == 'ztmp') {
             continue;
         }
         if (preg_match("/%ZB64\$/", $stat['name'])) {
             $filename = Z_Base64::decode(substr($stat['name'], 0, -5));
             $filename = self::decodeRelativeDescriptorString($filename);
             $za->renameIndex($i, $filename);
         } else {
             $filename = $stat['name'];
         }
         $entries[] = $filename;
     }
     $success = $za->extractTo($destDir, $entries);
     $za->close();
     if (!$success) {
         Z_Core::logError($za->getStatusString());
     }
     return $success;
 }
Пример #23
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();
 }
Пример #24
0
 private function error($httpCode = 500, $code, $message = false, $attributes = array(), $elements = array())
 {
     header("Content-Type: text/xml");
     if ($httpCode) {
         header("HTTP/1.1 " . $httpCode);
     }
     $this->responseXML->error['code'] = $code;
     if ($message) {
         $this->responseXML->error = $message;
     }
     foreach ($attributes as $attr => $val) {
         $this->responseXML->error[$attr] = $val;
     }
     foreach ($elements as $name => $val) {
         $this->responseXML->{$name} = $val;
     }
     $xmlstr = $this->responseXML->asXML();
     // Strip XML declaration, since it will be added automatically when in XML mode
     $xmlstr = preg_replace("/<\\?xml.+?>\n/", '', $xmlstr);
     echo $xmlstr;
     $this->logRequestTime();
     Z_Core::exitClean();
 }
Пример #25
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']);
     }
 }
Пример #26
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");
include 'errors/404.php';
Пример #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
 /**
  * Add sync process and associated locks to database
  */
 private static function addUploadProcess($userID, $libraryIDs, $syncQueueID = null, $syncProcessID = null)
 {
     Zotero_DB::beginTransaction();
     $syncProcessID = $syncProcessID ? $syncProcessID : Zotero_ID::getBigInt();
     $sql = "INSERT INTO syncProcesses (syncProcessID, userID) VALUES (?, ?)";
     try {
         Zotero_DB::query($sql, array($syncProcessID, $userID));
     } catch (Exception $e) {
         $sql = "SELECT CONCAT(syncProcessID,' ',userID,' ',started) FROM syncProcesses WHERE userID=?";
         $val = Zotero_DB::valueQuery($sql, $userID);
         Z_Core::logError($val);
     }
     if ($libraryIDs) {
         $sql = "INSERT INTO syncProcessLocks VALUES ";
         $sql .= implode(', ', array_fill(0, sizeOf($libraryIDs), '(?,?)'));
         $params = array();
         foreach ($libraryIDs as $libraryID) {
             $params[] = $syncProcessID;
             $params[] = $libraryID;
         }
         Zotero_DB::query($sql, $params);
     }
     // Record the process id in the queue entry, if given
     if ($syncQueueID) {
         $sql = "UPDATE syncUploadQueue SET syncProcessID=? WHERE syncUploadQueueID=?";
         Zotero_DB::query($sql, array($syncProcessID, $syncQueueID));
     }
     Zotero_DB::commit();
     return $syncProcessID;
 }
Пример #29
0
 /**
  * Converts a Zotero_Item object to a SimpleXMLElement Atom object
  *
  * Note: Increment Z_CONFIG::$CACHE_VERSION_ATOM_ENTRY when changing
  * the response.
  *
  * @param	object				$item		Zotero_Item object
  * @param	string				$content
  * @return	SimpleXMLElement					Item data as SimpleXML element
  */
 public static function convertItemToAtom(Zotero_Item $item, $queryParams, $permissions, $sharedData = null)
 {
     $t = microtime(true);
     // Uncached stuff or parts of the cache key
     $version = $item->version;
     $parent = $item->getSource();
     $isRegularItem = !$parent && $item->isRegularItem();
     $downloadDetails = $permissions->canAccess($item->libraryID, 'files') ? Zotero_Storage::getDownloadDetails($item) : false;
     if ($isRegularItem) {
         $numChildren = $permissions->canAccess($item->libraryID, 'notes') ? $item->numChildren() : $item->numAttachments();
     }
     // <id> changes based on group visibility in v1
     if ($queryParams['v'] < 2) {
         $id = Zotero_URI::getItemURI($item, false, true);
     } else {
         $id = Zotero_URI::getItemURI($item);
     }
     $libraryType = Zotero_Libraries::getType($item->libraryID);
     // Any query parameters that have an effect on the output
     // need to be added here
     $allowedParams = array('content', 'style', 'css', 'linkwrap');
     $cachedParams = Z_Array::filterKeys($queryParams, $allowedParams);
     $cacheVersion = 2;
     $cacheKey = "atomEntry_" . $item->libraryID . "/" . $item->id . "_" . md5($version . json_encode($cachedParams) . ($downloadDetails ? 'hasFile' : '') . ($libraryType == 'group' ? 'id' . $id : '')) . "_" . $queryParams['v'] . "_" . $cacheVersion . (isset(Z_CONFIG::$CACHE_VERSION_ATOM_ENTRY) ? "_" . Z_CONFIG::$CACHE_VERSION_ATOM_ENTRY : "") . (in_array('bib', $queryParams['content']) && isset(Z_CONFIG::$CACHE_VERSION_BIB) ? "_" . Z_CONFIG::$CACHE_VERSION_BIB : "");
     $xmlstr = Z_Core::$MC->get($cacheKey);
     if ($xmlstr) {
         try {
             // TEMP: Strip control characters
             $xmlstr = Zotero_Utilities::cleanString($xmlstr, true);
             $doc = new DOMDocument();
             $doc->loadXML($xmlstr);
             $xpath = new DOMXpath($doc);
             $xpath->registerNamespace('atom', Zotero_Atom::$nsAtom);
             $xpath->registerNamespace('zapi', Zotero_Atom::$nsZoteroAPI);
             $xpath->registerNamespace('xhtml', Zotero_Atom::$nsXHTML);
             // Make sure numChildren reflects the current permissions
             if ($isRegularItem) {
                 $xpath->query('/atom:entry/zapi:numChildren')->item(0)->nodeValue = $numChildren;
             }
             // To prevent PHP from messing with namespace declarations,
             // we have to extract, remove, and then add back <content>
             // subelements. Otherwise the subelements become, say,
             // <default:span xmlns="http://www.w3.org/1999/xhtml"> instead
             // of just <span xmlns="http://www.w3.org/1999/xhtml">, and
             // xmlns:default="http://www.w3.org/1999/xhtml" gets added to
             // the parent <entry>. While you might reasonably think that
             //
             // echo $xml->saveXML();
             //
             // and
             //
             // $xml = new SimpleXMLElement($xml->saveXML());
             // echo $xml->saveXML();
             //
             // would be identical, you would be wrong.
             $multiFormat = !!$xpath->query('/atom:entry/atom:content/zapi:subcontent')->length;
             $contentNodes = array();
             if ($multiFormat) {
                 $contentNodes = $xpath->query('/atom:entry/atom:content/zapi:subcontent');
             } else {
                 $contentNodes = $xpath->query('/atom:entry/atom:content');
             }
             foreach ($contentNodes as $contentNode) {
                 $contentParts = array();
                 while ($contentNode->hasChildNodes()) {
                     $contentParts[] = $doc->saveXML($contentNode->firstChild);
                     $contentNode->removeChild($contentNode->firstChild);
                 }
                 foreach ($contentParts as $part) {
                     if (!trim($part)) {
                         continue;
                     }
                     // Strip the namespace and add it back via SimpleXMLElement,
                     // which keeps it from being changed later
                     if (preg_match('%^<[^>]+xmlns="http://www.w3.org/1999/xhtml"%', $part)) {
                         $part = preg_replace('%^(<[^>]+)xmlns="http://www.w3.org/1999/xhtml"%', '$1', $part);
                         $html = new SimpleXMLElement($part);
                         $html['xmlns'] = "http://www.w3.org/1999/xhtml";
                         $subNode = dom_import_simplexml($html);
                         $importedNode = $doc->importNode($subNode, true);
                         $contentNode->appendChild($importedNode);
                     } else {
                         if (preg_match('%^<[^>]+xmlns="http://zotero.org/ns/transfer"%', $part)) {
                             $part = preg_replace('%^(<[^>]+)xmlns="http://zotero.org/ns/transfer"%', '$1', $part);
                             $html = new SimpleXMLElement($part);
                             $html['xmlns'] = "http://zotero.org/ns/transfer";
                             $subNode = dom_import_simplexml($html);
                             $importedNode = $doc->importNode($subNode, true);
                             $contentNode->appendChild($importedNode);
                         } else {
                             $docFrag = $doc->createDocumentFragment();
                             $docFrag->appendXML($part);
                             $contentNode->appendChild($docFrag);
                         }
                     }
                 }
             }
             $xml = simplexml_import_dom($doc);
             StatsD::timing("api.items.itemToAtom.cached", (microtime(true) - $t) * 1000);
             StatsD::increment("memcached.items.itemToAtom.hit");
             // Skip the cache every 10 times for now, to ensure cache sanity
             if (Z_Core::probability(10)) {
                 $xmlstr = $xml->saveXML();
             } else {
                 return $xml;
             }
         } catch (Exception $e) {
             error_log($xmlstr);
             error_log("WARNING: " . $e);
         }
     }
     $content = $queryParams['content'];
     $contentIsHTML = sizeOf($content) == 1 && $content[0] == 'html';
     $contentParamString = urlencode(implode(',', $content));
     $style = $queryParams['style'];
     $entry = '<?xml version="1.0" encoding="UTF-8"?>' . '<entry xmlns="' . Zotero_Atom::$nsAtom . '" xmlns:zapi="' . Zotero_Atom::$nsZoteroAPI . '"/>';
     $xml = new SimpleXMLElement($entry);
     $title = $item->getDisplayTitle(true);
     $title = $title ? $title : '[Untitled]';
     $xml->title = $title;
     $author = $xml->addChild('author');
     $createdByUserID = null;
     $lastModifiedByUserID = null;
     switch (Zotero_Libraries::getType($item->libraryID)) {
         case 'group':
             $createdByUserID = $item->createdByUserID;
             // Used for zapi:lastModifiedByUser below
             $lastModifiedByUserID = $item->lastModifiedByUserID;
             break;
     }
     if ($createdByUserID) {
         $author->name = Zotero_Users::getUsername($createdByUserID);
         $author->uri = Zotero_URI::getUserURI($createdByUserID);
     } else {
         $author->name = Zotero_Libraries::getName($item->libraryID);
         $author->uri = Zotero_URI::getLibraryURI($item->libraryID);
     }
     $xml->id = $id;
     $xml->published = Zotero_Date::sqlToISO8601($item->dateAdded);
     $xml->updated = Zotero_Date::sqlToISO8601($item->dateModified);
     $link = $xml->addChild("link");
     $link['rel'] = "self";
     $link['type'] = "application/atom+xml";
     $href = Zotero_API::getItemURI($item);
     if (!$contentIsHTML) {
         $href .= "?content={$contentParamString}";
     }
     $link['href'] = $href;
     if ($parent) {
         // TODO: handle group items?
         $parentItem = Zotero_Items::get($item->libraryID, $parent);
         $link = $xml->addChild("link");
         $link['rel'] = "up";
         $link['type'] = "application/atom+xml";
         $href = Zotero_API::getItemURI($parentItem);
         if (!$contentIsHTML) {
             $href .= "?content={$contentParamString}";
         }
         $link['href'] = $href;
     }
     $link = $xml->addChild('link');
     $link['rel'] = 'alternate';
     $link['type'] = 'text/html';
     $link['href'] = Zotero_URI::getItemURI($item, true);
     // If appropriate permissions and the file is stored in ZFS, get file request link
     if ($downloadDetails) {
         $details = $downloadDetails;
         $link = $xml->addChild('link');
         $link['rel'] = 'enclosure';
         $type = $item->attachmentMIMEType;
         if ($type) {
             $link['type'] = $type;
         }
         $link['href'] = $details['url'];
         if (!empty($details['filename'])) {
             $link['title'] = $details['filename'];
         }
         if (isset($details['size'])) {
             $link['length'] = $details['size'];
         }
     }
     $xml->addChild('zapi:key', $item->key, Zotero_Atom::$nsZoteroAPI);
     $xml->addChild('zapi:version', $item->version, Zotero_Atom::$nsZoteroAPI);
     if ($lastModifiedByUserID) {
         $xml->addChild('zapi:lastModifiedByUser', Zotero_Users::getUsername($lastModifiedByUserID), Zotero_Atom::$nsZoteroAPI);
     }
     $xml->addChild('zapi:itemType', Zotero_ItemTypes::getName($item->itemTypeID), Zotero_Atom::$nsZoteroAPI);
     if ($isRegularItem) {
         $val = $item->creatorSummary;
         if ($val !== '') {
             $xml->addChild('zapi:creatorSummary', htmlspecialchars($val), Zotero_Atom::$nsZoteroAPI);
         }
         $val = $item->getField('date', true, true, true);
         if ($val !== '') {
             if ($queryParams['v'] < 3) {
                 $val = substr($val, 0, 4);
                 if ($val !== '0000') {
                     $xml->addChild('zapi:year', $val, Zotero_Atom::$nsZoteroAPI);
                 }
             } else {
                 $sqlDate = Zotero_Date::multipartToSQL($val);
                 if (substr($sqlDate, 0, 4) !== '0000') {
                     $xml->addChild('zapi:parsedDate', Zotero_Date::sqlToISO8601($sqlDate), Zotero_Atom::$nsZoteroAPI);
                 }
             }
         }
         $xml->addChild('zapi:numChildren', $numChildren, Zotero_Atom::$nsZoteroAPI);
     }
     if ($queryParams['v'] < 3) {
         $xml->addChild('zapi:numTags', $item->numTags(), Zotero_Atom::$nsZoteroAPI);
     }
     $xml->content = '';
     //
     // DOM XML from here on out
     //
     $contentNode = dom_import_simplexml($xml->content);
     $domDoc = $contentNode->ownerDocument;
     $multiFormat = sizeOf($content) > 1;
     // Create a root XML document for multi-format responses
     if ($multiFormat) {
         $contentNode->setAttribute('type', 'application/xml');
         /*$multicontent = $domDoc->createElementNS(
         			Zotero_Atom::$nsZoteroAPI, 'multicontent'
         		);
         		$contentNode->appendChild($multicontent);*/
     }
     foreach ($content as $type) {
         // Set the target to either the main <content>
         // or a <multicontent> <content>
         if (!$multiFormat) {
             $target = $contentNode;
         } else {
             $target = $domDoc->createElementNS(Zotero_Atom::$nsZoteroAPI, 'subcontent');
             $contentNode->appendChild($target);
         }
         $target->setAttributeNS(Zotero_Atom::$nsZoteroAPI, "zapi:type", $type);
         if ($type == 'html') {
             if (!$multiFormat) {
                 $target->setAttribute('type', 'xhtml');
             }
             $div = $domDoc->createElementNS(Zotero_Atom::$nsXHTML, 'div');
             $target->appendChild($div);
             $html = $item->toHTML(true);
             $subNode = dom_import_simplexml($html);
             $importedNode = $domDoc->importNode($subNode, true);
             $div->appendChild($importedNode);
         } else {
             if ($type == 'citation') {
                 if (!$multiFormat) {
                     $target->setAttribute('type', 'xhtml');
                 }
                 if (isset($sharedData[$type][$item->libraryID . "/" . $item->key])) {
                     $html = $sharedData[$type][$item->libraryID . "/" . $item->key];
                 } else {
                     if ($sharedData !== null) {
                         //error_log("Citation not found in sharedData -- retrieving individually");
                     }
                     $html = Zotero_Cite::getCitationFromCiteServer($item, $queryParams);
                 }
                 $html = new SimpleXMLElement($html);
                 $html['xmlns'] = Zotero_Atom::$nsXHTML;
                 $subNode = dom_import_simplexml($html);
                 $importedNode = $domDoc->importNode($subNode, true);
                 $target->appendChild($importedNode);
             } else {
                 if ($type == 'bib') {
                     if (!$multiFormat) {
                         $target->setAttribute('type', 'xhtml');
                     }
                     if (isset($sharedData[$type][$item->libraryID . "/" . $item->key])) {
                         $html = $sharedData[$type][$item->libraryID . "/" . $item->key];
                     } else {
                         if ($sharedData !== null) {
                             //error_log("Bibliography not found in sharedData -- retrieving individually");
                         }
                         $html = Zotero_Cite::getBibliographyFromCitationServer(array($item), $queryParams);
                     }
                     $html = new SimpleXMLElement($html);
                     $html['xmlns'] = Zotero_Atom::$nsXHTML;
                     $subNode = dom_import_simplexml($html);
                     $importedNode = $domDoc->importNode($subNode, true);
                     $target->appendChild($importedNode);
                 } else {
                     if ($type == 'json') {
                         if ($queryParams['v'] < 2) {
                             $target->setAttributeNS(Zotero_Atom::$nsZoteroAPI, "zapi:etag", $item->etag);
                         }
                         $textNode = $domDoc->createTextNode($item->toJSON(false, $queryParams, true));
                         $target->appendChild($textNode);
                     } else {
                         if ($type == 'csljson') {
                             $arr = $item->toCSLItem();
                             $json = Zotero_Utilities::formatJSON($arr);
                             $textNode = $domDoc->createTextNode($json);
                             $target->appendChild($textNode);
                         } else {
                             if (in_array($type, Zotero_Translate::$exportFormats)) {
                                 $export = Zotero_Translate::doExport(array($item), $type);
                                 $target->setAttribute('type', $export['mimeType']);
                                 // Insert XML into document
                                 if (preg_match('/\\+xml$/', $export['mimeType'])) {
                                     // Strip prolog
                                     $body = preg_replace('/^<\\?xml.+\\n/', "", $export['body']);
                                     $subNode = $domDoc->createDocumentFragment();
                                     $subNode->appendXML($body);
                                     $target->appendChild($subNode);
                                 } else {
                                     $textNode = $domDoc->createTextNode($export['body']);
                                     $target->appendChild($textNode);
                                 }
                             }
                         }
                     }
                 }
             }
         }
     }
     // TEMP
     if ($xmlstr) {
         $uncached = $xml->saveXML();
         if ($xmlstr != $uncached) {
             $uncached = str_replace('<zapi:year></zapi:year>', '<zapi:year/>', $uncached);
             $uncached = str_replace('<content zapi:type="none"></content>', '<content zapi:type="none"/>', $uncached);
             $uncached = str_replace('<zapi:subcontent zapi:type="coins" type="text/html"></zapi:subcontent>', '<zapi:subcontent zapi:type="coins" type="text/html"/>', $uncached);
             $uncached = str_replace('<title></title>', '<title/>', $uncached);
             $uncached = str_replace('<note></note>', '<note/>', $uncached);
             $uncached = str_replace('<path></path>', '<path/>', $uncached);
             $uncached = str_replace('<td></td>', '<td/>', $uncached);
             if ($xmlstr != $uncached) {
                 error_log("Cached Atom item entry does not match");
                 error_log("  Cached: " . $xmlstr);
                 error_log("Uncached: " . $uncached);
                 Z_Core::$MC->set($cacheKey, $uncached, 3600);
                 // 1 hour for now
             }
         }
     } else {
         $xmlstr = $xml->saveXML();
         Z_Core::$MC->set($cacheKey, $xmlstr, 3600);
         // 1 hour for now
         StatsD::timing("api.items.itemToAtom.uncached", (microtime(true) - $t) * 1000);
         StatsD::increment("memcached.items.itemToAtom.miss");
     }
     return $xml;
 }
Пример #30
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;
 }