public static function getAllAdvanced($libraryID, $onlyTopLevel = false, $params)
 {
     $results = array('collections' => array(), 'total' => 0);
     $shardID = Zotero_Shards::getByLibraryID($libraryID);
     $sql = "SELECT SQL_CALC_FOUND_ROWS collectionID FROM collections\n\t\t\t\tWHERE libraryID=? ";
     if ($onlyTopLevel) {
         $sql .= "AND parentCollectionID IS NULL ";
     }
     if (!empty($params['order'])) {
         $order = $params['order'];
         if ($order == 'title') {
             $order = 'collectionName';
         }
         $sql .= "ORDER BY {$order} ";
         if (!empty($params['sort'])) {
             $sql .= $params['sort'] . " ";
         }
     }
     $sqlParams = array($libraryID);
     if (!empty($params['limit'])) {
         $sql .= "LIMIT ?, ?";
         $sqlParams[] = $params['start'] ? $params['start'] : 0;
         $sqlParams[] = $params['limit'];
     }
     $ids = Zotero_DB::columnQuery($sql, $sqlParams, $shardID);
     if ($ids) {
         $results['total'] = Zotero_DB::valueQuery("SELECT FOUND_ROWS()", false, $shardID);
         $collections = array();
         foreach ($ids as $id) {
             $collections[] = self::get($libraryID, $id);
         }
         $results['collections'] = $collections;
     }
     return $results;
 }
Esempio n. 2
0
 /**
  * Check if search exists in the database
  *
  * @return	bool			TRUE if the item exists, FALSE if not
  */
 public function exists()
 {
     if (!$this->id) {
         trigger_error('$this->id not set');
     }
     $sql = "SELECT COUNT(*) FROM savedSearches WHERE searchID=?";
     return !!Zotero_DB::valueQuery($sql, $this->id, Zotero_Shards::getByLibraryID($this->libraryID));
 }
Esempio n. 3
0
 public static function getName($charsetOrCharsetID)
 {
     if (isset(self::$charsets[$charsetOrCharsetID])) {
         return self::$charsets[$charsetOrCharsetID];
     }
     $sql = "(SELECT charset FROM charsets WHERE charsetID=?) UNION\n\t\t\t\t(SELECT charset FROM charsets WHERE charset=?) LIMIT 1";
     $charset = Zotero_DB::valueQuery($sql, array($charsetOrCharsetID, $charsetOrCharsetID));
     self::$charsets[$charsetOrCharsetID] = $charset;
     return $charset;
 }
Esempio n. 4
0
 public static function generate()
 {
     $tries = 5;
     while ($tries > 0) {
         $str = Zotero_Utilities::randomString(24, 'mixed');
         $sql = "SELECT COUNT(*) FROM `keys` WHERE `key`=?";
         if (Zotero_DB::valueQuery($sql, $str)) {
             $tries--;
             continue;
         }
         return $str;
     }
     throw new Exception("Unique key could not be generated");
 }
Esempio n. 5
0
 public static function get($libraryID, $creatorID, $skipCheck = false)
 {
     if (!$libraryID) {
         throw new Exception("Library ID not set");
     }
     if (!$creatorID) {
         throw new Exception("Creator ID not set");
     }
     if (!empty(self::$creatorsByID[$creatorID])) {
         return self::$creatorsByID[$creatorID];
     }
     if (!$skipCheck) {
         $sql = 'SELECT COUNT(*) FROM creators WHERE creatorID=?';
         $result = Zotero_DB::valueQuery($sql, $creatorID, Zotero_Shards::getByLibraryID($libraryID));
         if (!$result) {
             return false;
         }
     }
     $creator = new Zotero_Creator();
     $creator->libraryID = $libraryID;
     $creator->id = $creatorID;
     self::$creatorsByID[$creatorID] = $creator;
     return self::$creatorsByID[$creatorID];
 }
Esempio n. 6
0
 private static function countDeletedObjectKeys($userID, $timestamp, $updatedLibraryIDs)
 {
     /*
     $sql = "SELECT version FROM version WHERE schema='syncdeletelog'";
     $syncLogStart = Zotero_DB::valueQuery($sql);
     if (!$syncLogStart) {
     	throw ('Sync log start time not found');
     }
     */
     /*
     // Last sync time is before start of log
     if ($lastSyncDate && new Date($syncLogStart * 1000) > $lastSyncDate) {
     	return -1;
     }
     */
     $shardLibraryIDs = array();
     // Personal library
     $shardID = Zotero_Shards::getByUserID($userID);
     $libraryID = Zotero_Users::getLibraryIDFromUserID($userID);
     if (in_array($libraryID, $updatedLibraryIDs)) {
         $shardLibraryIDs[$shardID] = array($libraryID);
     }
     // Group libraries
     $groupIDs = Zotero_Groups::getUserGroups($userID);
     if ($groupIDs) {
         // Separate groups into shards for querying
         foreach ($groupIDs as $groupID) {
             $libraryID = Zotero_Groups::getLibraryIDFromGroupID($groupID);
             // If library hasn't changed, skip
             if (!in_array($libraryID, $updatedLibraryIDs)) {
                 continue;
             }
             $shardID = Zotero_Shards::getByLibraryID($libraryID);
             if (!isset($shardLibraryIDs[$shardID])) {
                 $shardLibraryIDs[$shardID] = array();
             }
             $shardLibraryIDs[$shardID][] = $libraryID;
         }
     }
     // Send query at each shard
     $rows = array();
     $count = 0;
     foreach ($shardLibraryIDs as $shardID => $libraryIDs) {
         $sql = "SELECT COUNT(*) FROM syncDeleteLogKeys WHERE libraryID IN (" . implode(', ', array_fill(0, sizeOf($libraryIDs), '?')) . ") " . "AND objectType != 'tagName'";
         $params = $libraryIDs;
         if ($timestamp) {
             $sql .= " AND timestamp >= FROM_UNIXTIME(?)";
             $params[] = $timestamp;
         }
         $count += Zotero_DB::valueQuery($sql, $params, $shardID);
     }
     return $count;
 }
Esempio n. 7
0
 public static function getLastStorageSync($libraryID)
 {
     $sql = "SELECT UNIX_TIMESTAMP(serverDateModified) AS time FROM items\n\t\t\t\tJOIN storageFileItems USING (itemID) WHERE libraryID=?\n\t\t\t\tORDER BY time DESC LIMIT 1";
     return Zotero_DB::valueQuery($sql, $libraryID, Zotero_Shards::getByLibraryID($libraryID));
 }
Esempio n. 8
0
 public function numItems($includeDeleted = false)
 {
     $sql = "SELECT COUNT(*) FROM collectionItems ";
     if (!$includeDeleted) {
         $sql .= "LEFT JOIN deletedItems DI USING (itemID)";
     }
     $sql .= "WHERE collectionID=?";
     if (!$includeDeleted) {
         $sql .= " AND DI.itemID IS NULL";
     }
     return Zotero_DB::valueQuery($sql, $this->id, Zotero_Shards::getByLibraryID($this->libraryID));
 }
Esempio n. 9
0
 public static function isLocked($libraryID)
 {
     $sql = "SELECT COUNT(*) FROM syncUploadQueueLocks WHERE libraryID=?";
     if (Zotero_DB::valueQuery($sql, $libraryID)) {
         return true;
     }
     $sql = "SELECT COUNT(*) FROM syncProcessLocks WHERE libraryID=?";
     return !!Zotero_DB::valueQuery($sql, $libraryID);
 }
Esempio n. 10
0
	public function numTags() {
		if (!$this->id) {
			return 0;
		}
		
		$sql = "SELECT COUNT(*) FROM itemTags WHERE itemID=?";
		return (int) Zotero_DB::valueQuery($sql, $this->id, Zotero_Shards::getByLibraryID($this->libraryID));
	}
Esempio n. 11
0
 private static function checkForLibrary($libraryID, $shardID)
 {
     $tables = array('collections', 'creators', 'items', 'relations', 'savedSearches', 'shardLibraries', 'tags', 'syncDeleteLogIDs', 'syncDeleteLogKeys');
     foreach ($tables as $table) {
         $sql = "SELECT COUNT(*) FROM {$table} WHERE libraryID=?";
         if (Zotero_DB::valueQuery($sql, $libraryID, $shardID)) {
             return true;
         }
     }
     return false;
 }
Esempio n. 12
0
 public static function search($libraryID, $onlyTopLevel = false, $params)
 {
     $results = array('results' => array(), 'total' => 0);
     $shardID = Zotero_Shards::getByLibraryID($libraryID);
     $sql = "SELECT SQL_CALC_FOUND_ROWS DISTINCT ";
     if ($params['format'] == 'keys') {
         $sql .= "`key`";
     } else {
         $sql .= "`key`, version";
     }
     $sql .= " FROM collections WHERE libraryID=? ";
     $sqlParams = array($libraryID);
     if ($onlyTopLevel) {
         $sql .= "AND parentCollectionID IS NULL ";
     }
     // Pass a list of collectionIDs, for when the initial search is done via SQL
     $collectionIDs = !empty($params['collectionIDs']) ? $params['collectionIDs'] : array();
     $collectionKeys = $params['collectionKey'];
     if ($collectionIDs) {
         $sql .= "AND collectionID IN (" . implode(', ', array_fill(0, sizeOf($collectionIDs), '?')) . ") ";
         $sqlParams = array_merge($sqlParams, $collectionIDs);
     }
     if ($collectionKeys) {
         $sql .= "AND `key` IN (" . implode(', ', array_fill(0, sizeOf($collectionKeys), '?')) . ") ";
         $sqlParams = array_merge($sqlParams, $collectionKeys);
     }
     if (!empty($params['q'])) {
         $sql .= "AND collectionName LIKE ? ";
         $sqlParams[] = '%' . $params['q'] . '%';
     }
     if (!empty($params['since'])) {
         $sql .= "AND version > ? ";
         $sqlParams[] = $params['since'];
     }
     // TEMP: for sync transition
     if (!empty($params['sincetime'])) {
         $sql .= "AND serverDateModified >= FROM_UNIXTIME(?) ";
         $sqlParams[] = $params['sincetime'];
     }
     if (!empty($params['sort'])) {
         switch ($params['sort']) {
             case 'title':
                 $orderSQL = 'collectionName';
                 break;
             case 'collectionKeyList':
                 $orderSQL = "FIELD(`key`," . implode(',', array_fill(0, sizeOf($collectionKeys), '?')) . ")";
                 $sqlParams = array_merge($sqlParams, $collectionKeys);
                 break;
             default:
                 $orderSQL = $params['sort'];
         }
         $sql .= "ORDER BY {$orderSQL}";
         if (!empty($params['direction'])) {
             $sql .= " {$params['direction']}";
         }
         $sql .= ", ";
     }
     $sql .= "version " . (!empty($params['direction']) ? $params['direction'] : "ASC") . ", collectionID " . (!empty($params['direction']) ? $params['direction'] : "ASC") . " ";
     if (!empty($params['limit'])) {
         $sql .= "LIMIT ?, ?";
         $sqlParams[] = $params['start'] ? $params['start'] : 0;
         $sqlParams[] = $params['limit'];
     }
     if ($params['format'] == 'keys') {
         $rows = Zotero_DB::columnQuery($sql, $sqlParams, $shardID);
     } else {
         $rows = Zotero_DB::query($sql, $sqlParams, $shardID);
     }
     $results['total'] = Zotero_DB::valueQuery("SELECT FOUND_ROWS()", false, $shardID);
     if ($rows) {
         if ($params['format'] == 'keys') {
             $results['results'] = $rows;
         } else {
             if ($params['format'] == 'versions') {
                 foreach ($rows as $row) {
                     $results['results'][$row['key']] = $row['version'];
                 }
             } else {
                 $collections = [];
                 foreach ($rows as $row) {
                     $obj = self::getByLibraryAndKey($libraryID, $row['key']);
                     $obj->setAvailableVersion($row['version']);
                     $collections[] = $obj;
                 }
                 $results['results'] = $collections;
             }
         }
     }
     return $results;
 }
Esempio n. 13
0
 public static function search($libraryID, $onlyTopLevel = false, $params = array(), $includeTrashed = false, $asKeys = false)
 {
     $rnd = "_" . uniqid($libraryID . "_");
     if ($asKeys) {
         $results = array('keys' => array(), 'total' => 0);
     } else {
         $results = array('items' => array(), 'total' => 0);
     }
     $shardID = Zotero_Shards::getByLibraryID($libraryID);
     $itemIDs = array();
     $keys = array();
     $deleteTempTable = array();
     // Pass a list of itemIDs, for when the initial search is done via SQL
     if (!empty($params['itemIDs'])) {
         $itemIDs = $params['itemIDs'];
     }
     if (!empty($params['itemKey'])) {
         $keys = explode(',', $params['itemKey']);
     }
     $titleSort = !empty($params['order']) && $params['order'] == 'title';
     $sql = "SELECT SQL_CALC_FOUND_ROWS DISTINCT " . ($asKeys ? "I.key" : "I.itemID") . " FROM items I ";
     $sqlParams = array($libraryID);
     if (!empty($params['q']) || $titleSort) {
         $titleFieldIDs = array_merge(array(Zotero_ItemFields::getID('title')), Zotero_ItemFields::getTypeFieldsFromBase('title'));
         $sql .= "LEFT JOIN itemData IDT ON (IDT.itemID=I.itemID AND IDT.fieldID IN (" . implode(',', $titleFieldIDs) . ")) ";
     }
     if (!empty($params['q'])) {
         $sql .= "LEFT JOIN itemCreators IC ON (IC.itemID=I.itemID)\n\t\t\t\t\tLEFT JOIN creators C ON (C.creatorID=IC.creatorID) ";
     }
     if ($onlyTopLevel || !empty($params['q']) || $titleSort) {
         $sql .= "LEFT JOIN itemNotes INo ON (INo.itemID=I.itemID) ";
     }
     if ($onlyTopLevel) {
         $sql .= "LEFT JOIN itemAttachments IA ON (IA.itemID=I.itemID) ";
     }
     if (!$includeTrashed) {
         $sql .= "LEFT JOIN deletedItems DI ON (DI.itemID=I.itemID) ";
     }
     if (!empty($params['order'])) {
         switch ($params['order']) {
             case 'title':
             case 'creator':
                 $sql .= "LEFT JOIN itemSortFields ISF ON (ISF.itemID=I.itemID) ";
                 break;
             case 'date':
                 $dateFieldIDs = array_merge(array(Zotero_ItemFields::getID('date')), Zotero_ItemFields::getTypeFieldsFromBase('date'));
                 $sql .= "LEFT JOIN itemData IDD ON (IDD.itemID=I.itemID AND IDD.fieldID IN (" . implode(',', $dateFieldIDs) . ")) ";
                 break;
             case 'itemType':
                 // Create temporary table to store item type names
                 //
                 // We use IF NOT EXISTS just to make sure there are
                 // no problems with restoration from the binary log
                 $sql2 = "CREATE TEMPORARY TABLE IF NOT EXISTS tmpItemTypeNames{$rnd}\n\t\t\t\t\t\t\t(itemTypeID SMALLINT UNSIGNED NOT NULL,\n\t\t\t\t\t\t\titemTypeName VARCHAR(255) NOT NULL,\n\t\t\t\t\t\t\tPRIMARY KEY (itemTypeID),\n\t\t\t\t\t\t\tINDEX (itemTypeName))";
                 Zotero_DB::query($sql2, false, $shardID);
                 $deleteTempTable['tmpItemTypeNames'] = true;
                 $types = Zotero_ItemTypes::getAll('en-US');
                 foreach ($types as $type) {
                     $sql2 = "INSERT INTO tmpItemTypeNames{$rnd} VALUES (?, ?)";
                     Zotero_DB::query($sql2, array($type['id'], $type['localized']), $shardID);
                 }
                 // Join temp table to query
                 $sql .= "JOIN tmpItemTypeNames{$rnd} TITN ON (TITN.itemTypeID=I.itemTypeID) ";
                 break;
             case 'addedBy':
                 $isGroup = Zotero_Libraries::getType($libraryID) == 'group';
                 if ($isGroup) {
                     // Create temporary table to store usernames
                     //
                     // We use IF NOT EXISTS just to make sure there are
                     // no problems with restoration from the binary log
                     $sql2 = "CREATE TEMPORARY TABLE IF NOT EXISTS tmpCreatedByUsers{$rnd}\n\t\t\t\t\t\t\t\t(userID INT UNSIGNED NOT NULL,\n\t\t\t\t\t\t\t\tusername VARCHAR(255) NOT NULL,\n\t\t\t\t\t\t\t\tPRIMARY KEY (userID),\n\t\t\t\t\t\t\t\tINDEX (username))";
                     Zotero_DB::query($sql2, false, $shardID);
                     $deleteTempTable['tmpCreatedByUsers'] = true;
                     $sql2 = "SELECT DISTINCT createdByUserID FROM items\n\t\t\t\t\t\t\t\tJOIN groupItems USING (itemID) WHERE\n\t\t\t\t\t\t\t\tcreatedByUserID IS NOT NULL AND ";
                     if ($itemIDs) {
                         $sql2 .= "itemID IN (" . implode(', ', array_fill(0, sizeOf($itemIDs), '?')) . ") ";
                         $createdByUserIDs = Zotero_DB::columnQuery($sql2, $itemIDs, $shardID);
                     } else {
                         $sql2 .= "libraryID=?";
                         $createdByUserIDs = Zotero_DB::columnQuery($sql2, $libraryID, $shardID);
                     }
                     // Populate temp table with usernames
                     if ($createdByUserIDs) {
                         $toAdd = array();
                         foreach ($createdByUserIDs as $createdByUserID) {
                             $toAdd[] = array($createdByUserID, Zotero_Users::getUsername($createdByUserID));
                         }
                         $sql2 = "INSERT IGNORE INTO tmpCreatedByUsers{$rnd} VALUES ";
                         Zotero_DB::bulkInsert($sql2, $toAdd, 50, false, $shardID);
                         // Join temp table to query
                         $sql .= "JOIN groupItems GI ON (GI.itemID=I.itemID)\n\t\t\t\t\t\t\t\t\tJOIN tmpCreatedByUsers{$rnd} TCBU ON (TCBU.userID=GI.createdByUserID) ";
                     }
                 }
                 break;
         }
     }
     $sql .= "WHERE I.libraryID=? ";
     if ($onlyTopLevel) {
         $sql .= "AND INo.sourceItemID IS NULL AND IA.sourceItemID IS NULL ";
     }
     if (!$includeTrashed) {
         $sql .= "AND DI.itemID IS NULL ";
     }
     // Search on title and creators
     if (!empty($params['q'])) {
         $sql .= "AND (";
         $sql .= "IDT.value LIKE ? ";
         $sqlParams[] = '%' . $params['q'] . '%';
         $sql .= "OR title LIKE ? ";
         $sqlParams[] = '%' . $params['q'] . '%';
         $sql .= "OR TRIM(CONCAT(firstName, ' ', lastName)) LIKE ?";
         $sqlParams[] = '%' . $params['q'] . '%';
         $sql .= ") ";
     }
     // Search on itemType
     if (!empty($params['itemType'])) {
         $itemTypes = Zotero_API::getSearchParamValues($params, 'itemType');
         if ($itemTypes) {
             if (sizeOf($itemTypes) > 1) {
                 throw new Exception("Cannot specify 'itemType' more than once", Z_ERROR_INVALID_INPUT);
             }
             $itemTypes = $itemTypes[0];
             $itemTypeIDs = array();
             foreach ($itemTypes['values'] as $itemType) {
                 $itemTypeID = Zotero_ItemTypes::getID($itemType);
                 if (!$itemTypeID) {
                     throw new Exception("Invalid itemType '{$itemType}'", Z_ERROR_INVALID_INPUT);
                 }
                 $itemTypeIDs[] = $itemTypeID;
             }
             $sql .= "AND I.itemTypeID " . ($itemTypes['negation'] ? "NOT " : "") . "IN (" . implode(',', array_fill(0, sizeOf($itemTypeIDs), '?')) . ") ";
             $sqlParams = array_merge($sqlParams, $itemTypeIDs);
         }
     }
     // Tags
     //
     // ?tag=foo
     // ?tag=foo bar // phrase
     // ?tag=-foo // negation
     // ?tag=\-foo // literal hyphen (only for first character)
     // ?tag=foo&tag=bar // AND
     // ?tag=foo&tagType=0
     // ?tag=foo bar || bar&tagType=0
     $tagSets = Zotero_API::getSearchParamValues($params, 'tag');
     if ($tagSets) {
         $sql2 = "SELECT itemID FROM items WHERE 1 ";
         $sqlParams2 = array();
         if ($tagSets) {
             foreach ($tagSets as $set) {
                 $positives = array();
                 $negatives = array();
                 $tagIDs = array();
                 foreach ($set['values'] as $tag) {
                     $ids = Zotero_Tags::getIDs($libraryID, $tag);
                     if (!$ids) {
                         $ids = array(0);
                     }
                     $tagIDs = array_merge($tagIDs, $ids);
                 }
                 $tagIDs = array_unique($tagIDs);
                 if ($set['negation']) {
                     $negatives = array_merge($negatives, $tagIDs);
                 } else {
                     $positives = array_merge($positives, $tagIDs);
                 }
                 if ($positives) {
                     $sql2 .= "AND itemID IN (SELECT itemID FROM items JOIN itemTags USING (itemID)\n\t\t\t\t\t\t\t\tWHERE tagID IN (" . implode(',', array_fill(0, sizeOf($positives), '?')) . ")) ";
                     $sqlParams2 = array_merge($sqlParams2, $positives);
                 }
                 if ($negatives) {
                     $sql2 .= "AND itemID NOT IN (SELECT itemID FROM items JOIN itemTags USING (itemID)\n\t\t\t\t\t\t\t\tWHERE tagID IN (" . implode(',', array_fill(0, sizeOf($negatives), '?')) . ")) ";
                     $sqlParams2 = array_merge($sqlParams2, $negatives);
                 }
             }
         }
         $tagItems = Zotero_DB::columnQuery($sql2, $sqlParams2, $shardID);
         // No matches
         if (!$tagItems) {
             return $results;
         }
         // Combine with passed keys
         if ($itemIDs) {
             $itemIDs = array_intersect($itemIDs, $tagItems);
             // None of the tag matches match the passed keys
             if (!$itemIDs) {
                 return $results;
             }
         } else {
             $itemIDs = $tagItems;
         }
     }
     if ($itemIDs) {
         $sql .= "AND I.itemID IN (" . implode(', ', array_fill(0, sizeOf($itemIDs), '?')) . ") ";
         $sqlParams = array_merge($sqlParams, $itemIDs);
     }
     if ($keys) {
         $sql .= "AND `key` IN (" . implode(', ', array_fill(0, sizeOf($keys), '?')) . ") ";
         $sqlParams = array_merge($sqlParams, $keys);
     }
     $sql .= "ORDER BY ";
     if (!empty($params['order'])) {
         switch ($params['order']) {
             case 'dateAdded':
             case 'dateModified':
             case 'serverDateModified':
                 $orderSQL = "I." . $params['order'];
                 break;
             case 'itemType':
                 $orderSQL = "TITN.itemTypeName";
                 break;
             case 'title':
                 $orderSQL = "IFNULL(COALESCE(sortTitle, IDT.value, INo.title), '')";
                 break;
             case 'creator':
                 $orderSQL = "ISF.creatorSummary";
                 break;
                 // TODO: generic base field mapping-aware sorting
             // TODO: generic base field mapping-aware sorting
             case 'date':
                 $orderSQL = "IDD.value";
                 break;
             case 'addedBy':
                 if ($isGroup && $createdByUserIDs) {
                     $orderSQL = "TCBU.username";
                 } else {
                     $orderSQL = "1";
                 }
                 break;
             default:
                 $fieldID = Zotero_ItemFields::getID($params['order']);
                 if (!$fieldID) {
                     throw new Exception("Invalid order field '" . $params['order'] . "'");
                 }
                 $orderSQL = "(SELECT value FROM itemData WHERE itemID=I.itemID AND fieldID=?)";
                 if (!$params['emptyFirst']) {
                     $sqlParams[] = $fieldID;
                 }
                 $sqlParams[] = $fieldID;
         }
         if (!empty($params['sort'])) {
             $dir = $params['sort'];
         } else {
             $dir = "ASC";
         }
         if (!$params['emptyFirst']) {
             $sql .= "IFNULL({$orderSQL}, '') = '' {$dir}, ";
         }
         $sql .= $orderSQL;
         $sql .= " {$dir}, ";
     }
     $sql .= "I.itemID " . (!empty($params['sort']) ? $params['sort'] : "ASC") . " ";
     if (!empty($params['limit'])) {
         $sql .= "LIMIT ?, ?";
         $sqlParams[] = $params['start'] ? $params['start'] : 0;
         $sqlParams[] = $params['limit'];
     }
     $itemIDs = Zotero_DB::columnQuery($sql, $sqlParams, $shardID);
     $results['total'] = Zotero_DB::valueQuery("SELECT FOUND_ROWS()", false, $shardID);
     if ($itemIDs) {
         if ($asKeys) {
             $results['keys'] = $itemIDs;
         } else {
             $results['items'] = Zotero_Items::get($libraryID, $itemIDs);
         }
     }
     if (!empty($deleteTempTable['tmpCreatedByUsers'])) {
         $sql = "DROP TEMPORARY TABLE IF EXISTS tmpCreatedByUsers{$rnd}";
         Zotero_DB::query($sql, false, $shardID);
     }
     if (!empty($deleteTempTable['tmpItemTypeNames'])) {
         $sql = "DROP TEMPORARY TABLE IF EXISTS tmpItemTypeNames{$rnd}";
         Zotero_DB::query($sql, false, $shardID);
     }
     return $results;
 }
Esempio n. 14
0
 public static function getLastStorageSync($userID)
 {
     $lastModified = false;
     $libraryIDs = Zotero_Libraries::getUserLibraries($userID);
     $shardIDs = Zotero_Shards::getUserShards($userID);
     foreach ($shardIDs as $shardID) {
         $sql = "SELECT UNIX_TIMESTAMP(serverDateModified) AS time FROM items\n\t\t\t\t\tJOIN storageFileItems USING (itemID)\n\t\t\t\t\tWHERE libraryID IN (" . implode(', ', array_fill(0, sizeOf($libraryIDs), '?')) . ")\n\t\t\t\t\tORDER BY time DESC LIMIT 1";
         $time = Zotero_DB::valueQuery($sql, $libraryIDs, $shardID);
         if ($time > $lastModified) {
             $lastModified = $time;
         }
     }
     return $lastModified;
 }
Esempio n. 15
0
 public static function getAllAdvanced($libraryID, $params)
 {
     $results = array('objects' => array(), 'total' => 0);
     $sql = "SELECT SQL_CALC_FOUND_ROWS tagID FROM tags ";
     if (!empty($params['order']) && $params['order'] == 'numItems') {
         $sql .= " LEFT JOIN itemTags USING (tagID)";
     }
     $sql .= "WHERE libraryID=? ";
     $sqlParams = array($libraryID);
     if (!empty($params['q'])) {
         if (!is_array($params['q'])) {
             $params['q'] = array($params['q']);
         }
         foreach ($params['q'] as $q) {
             $sql .= "AND name LIKE ? ";
             $sqlParams[] = "%{$q}%";
         }
     }
     $tagTypeSets = Zotero_API::getSearchParamValues($params, 'tagType');
     if ($tagTypeSets) {
         $positives = array();
         $negatives = array();
         foreach ($tagTypeSets as $set) {
             if ($set['negation']) {
                 $negatives = array_merge($negatives, $set['values']);
             } else {
                 $positives = array_merge($positives, $set['values']);
             }
         }
         if ($positives) {
             $sql .= "AND type IN (" . implode(',', array_fill(0, sizeOf($positives), '?')) . ") ";
             $sqlParams = array_merge($sqlParams, $positives);
         }
         if ($negatives) {
             $sql .= "AND type NOT IN (" . implode(',', array_fill(0, sizeOf($negatives), '?')) . ") ";
             $sqlParams = array_merge($sqlParams, $negatives);
         }
     }
     if (!empty($params['order'])) {
         $order = $params['order'];
         if ($order == 'title') {
             // Force a case-insensitive sort
             $sql .= "ORDER BY name COLLATE utf8_unicode_ci ";
         } else {
             if ($order == 'numItems') {
                 $sql .= "GROUP BY tags.tagID ORDER BY COUNT(tags.tagID)";
             } else {
                 $sql .= "ORDER BY {$order} ";
             }
         }
         if (!empty($params['sort'])) {
             $sql .= " " . $params['sort'] . " ";
         }
     }
     if (!empty($params['limit'])) {
         $sql .= "LIMIT ?, ?";
         $sqlParams[] = $params['start'] ? $params['start'] : 0;
         $sqlParams[] = $params['limit'];
     }
     $shardID = Zotero_Shards::getByLibraryID($libraryID);
     $ids = Zotero_DB::columnQuery($sql, $sqlParams, $shardID);
     if ($ids) {
         $results['total'] = Zotero_DB::valueQuery("SELECT FOUND_ROWS()", false, $shardID);
         $tags = array();
         foreach ($ids as $id) {
             $tags[] = Zotero_Tags::get($libraryID, $id);
         }
         $results['objects'] = $tags;
     }
     return $results;
 }
Esempio n. 16
0
 /**
  * Check if setting exists in the database
  *
  * @return bool TRUE if the setting exists, FALSE if not
  */
 public function exists()
 {
     $sql = "SELECT COUNT(*) FROM settings WHERE libraryID=? AND name=?";
     return !!Zotero_DB::valueQuery($sql, array($this->libraryID, $this->name), Zotero_Shards::getByLibraryID($this->libraryID));
 }
Esempio n. 17
0
 /**
  * Check if search exists in the database
  *
  * @return	bool			TRUE if the relation exists, FALSE if not
  */
 public function exists()
 {
     $shardID = Zotero_Shards::getByLibraryID($this->libraryID);
     if ($this->id) {
         $sql = "SELECT COUNT(*) FROM relations WHERE relationID=?";
         return !!Zotero_DB::valueQuery($sql, $this->id, $shardID);
     }
     if ($this->subject && $this->predicate && $this->object) {
         $sql = "SELECT COUNT(*) FROM relations WHERE libraryID=? AND\n\t\t\t\t\t\tsubject=? AND predicate=? AND object=?";
         $params = array($this->libraryID, $this->subject, $this->predicate, $this->object);
         return !!Zotero_DB::valueQuery($sql, $params, $shardID);
     }
     throw new Exception("ID or subject/predicate/object not set");
 }
Esempio n. 18
0
 private function getParentKey()
 {
     if ($this->_parent !== false) {
         if (!$this->_parent) {
             return null;
         }
         if (is_string($this->_parent)) {
             return $this->_parent;
         }
         $parentCollection = Zotero_Collections::get($this->libraryID, $this->_parent);
         return $parentCollection->key;
     }
     if (!$this->id) {
         return false;
     }
     $sql = "SELECT B.`key` FROM collections A JOIN collections B\n\t\t\t\tON (A.parentCollectionID=B.collectionID) WHERE A.collectionID=?";
     $key = Zotero_DB::valueQuery($sql, $this->id, Zotero_Shards::getByLibraryID($this->libraryID));
     if (!$key) {
         $key = null;
     }
     $this->_parent = $key;
     return $key;
 }
Esempio n. 19
0
 public static function addCustomType($name)
 {
     if (self::getID($name)) {
         throw new Exception("Item type '{$name}' already exists");
     }
     if (!preg_match('/^[a-z][^\\s0-9]+$/', $name)) {
         throw new Exception("Invalid item type name '{$name}'");
     }
     // TODO: make sure user hasn't added too many already
     throw new Exception("Unimplemented");
     // TODO: add to cache
     Zotero_DB::beginTransaction();
     $sql = "SELECT NEXT_ID(itemTypeID) FROM itemTypes";
     $itemTypeID = Zotero_DB::valueQuery($sql);
     $sql = "INSERT INTO itemTypes (?, ?, ?)";
     Zotero_DB::query($sql, array($itemTypeID, $name, 1));
     Zotero_DB::commit();
     return $itemTypeID;
 }
Esempio n. 20
0
 public static function getTransactionTimestamp()
 {
     $instance = self::getInstance();
     if ($instance->transactionLevel == 0) {
         throw new Exception("Transaction not open");
     }
     if (empty($instance->transactionTimestamp)) {
         $instance->transactionTimestamp = Zotero_DB::valueQuery("SELECT NOW()");
     }
     return $instance->transactionTimestamp;
 }
Esempio n. 21
0
 public static function getUserUsage($userID)
 {
     $usage = array();
     $libraryID = Zotero_Users::getLibraryIDFromUserID($userID);
     $sql = "SELECT SUM(size) AS bytes FROM storageFileItems\n\t\t\t\tJOIN items USING (itemID) WHERE libraryID=?";
     $libraryBytes = Zotero_DB::valueQuery($sql, $libraryID, Zotero_Shards::getByLibraryID($libraryID));
     $usage['library'] = round($libraryBytes / 1024 / 1024, 1);
     $groupBytes = 0;
     $usage['groups'] = array();
     $ownedLibraries = Zotero_Groups::getUserOwnedGroupLibraries($userID);
     if ($ownedLibraries) {
         $shardIDs = Zotero_Groups::getUserGroupShards($userID);
         foreach ($shardIDs as $shardID) {
             $sql = "SELECT libraryID, SUM(size) AS `bytes` FROM storageFileItems\n\t\t\t\t\t\tJOIN items I USING (itemID)\n\t\t\t\t\t\tWHERE libraryID IN\n\t\t\t\t\t\t(" . implode(', ', array_fill(0, sizeOf($ownedLibraries), '?')) . ")\n\t\t\t\t\t\tGROUP BY libraryID WITH ROLLUP";
             $libraries = Zotero_DB::query($sql, $ownedLibraries, $shardID);
             if ($libraries) {
                 foreach ($libraries as $library) {
                     if ($library['libraryID']) {
                         $usage['groups'][] = array('id' => Zotero_Groups::getGroupIDFromLibraryID($library['libraryID']), 'usage' => round($library['bytes'] / 1024 / 1024, 1));
                     } else {
                         $groupBytes += $library['bytes'];
                     }
                 }
             }
         }
     }
     $usage['total'] = round(($libraryBytes + $groupBytes) / 1024 / 1024, 1);
     return $usage;
 }
Esempio n. 22
0
 public static function search($libraryID, $onlyTopLevel = false, $params = array(), $includeTrashed = false, Zotero_Permissions $permissions = null)
 {
     $rnd = "_" . uniqid($libraryID . "_");
     $results = array('results' => array(), 'total' => 0);
     // Default empty library
     if ($libraryID === 0) {
         return $results;
     }
     $shardID = Zotero_Shards::getByLibraryID($libraryID);
     $includeNotes = true;
     if ($permissions && !$permissions->canAccess($libraryID, 'notes')) {
         $includeNotes = false;
     }
     // Pass a list of itemIDs, for when the initial search is done via SQL
     $itemIDs = !empty($params['itemIDs']) ? $params['itemIDs'] : array();
     $itemKeys = $params['itemKey'];
     $titleSort = !empty($params['sort']) && $params['sort'] == 'title';
     $parentItemSort = !empty($params['sort']) && in_array($params['sort'], ['itemType', 'dateAdded', 'dateModified', 'serverDateModified', 'addedBy']);
     $sql = "SELECT SQL_CALC_FOUND_ROWS DISTINCT ";
     // In /top mode, use the parent item's values for most joins
     if ($onlyTopLevel) {
         $itemIDSelector = "COALESCE(IA.sourceItemID, INo.sourceItemID, I.itemID)";
         $itemKeySelector = "COALESCE(IP.key, I.key)";
         $itemVersionSelector = "COALESCE(IP.version, I.version)";
         $itemTypeIDSelector = "COALESCE(IP.itemTypeID, I.itemTypeID)";
     } else {
         $itemIDSelector = "I.itemID";
         $itemKeySelector = "I.key";
         $itemVersionSelector = "I.version";
         $itemTypeIDSelector = "I.itemTypeID";
     }
     if ($params['format'] == 'keys' || $params['format'] == 'versions') {
         // In /top mode, display the parent item of matching items
         $sql .= "{$itemKeySelector} AS `key`";
         if ($params['format'] == 'versions') {
             $sql .= ", {$itemVersionSelector} AS version";
         }
     } else {
         $sql .= "{$itemIDSelector} AS itemID";
     }
     $sql .= " FROM items I ";
     $sqlParams = array($libraryID);
     // For /top, we need the parent itemID
     if ($onlyTopLevel) {
         $sql .= "LEFT JOIN itemAttachments IA ON (IA.itemID=I.itemID) ";
     }
     // For /top, we need the parent itemID; for 'q' we need the note; for sorting by title,
     // we need the note title
     if ($onlyTopLevel || !empty($params['q']) || $titleSort) {
         $sql .= "LEFT JOIN itemNotes INo ON (INo.itemID=I.itemID) ";
     }
     // For some /top requests, pull in the parent item's items row
     if ($onlyTopLevel && ($params['format'] == 'keys' || $params['format'] == 'versions' || $parentItemSort)) {
         $sql .= "LEFT JOIN items IP ON ({$itemIDSelector}=IP.itemID) ";
     }
     // Pull in titles
     if (!empty($params['q']) || $titleSort) {
         $titleFieldIDs = array_merge(array(Zotero_ItemFields::getID('title')), Zotero_ItemFields::getTypeFieldsFromBase('title'));
         $sql .= "LEFT JOIN itemData IDT ON (IDT.itemID=I.itemID AND IDT.fieldID IN " . "(" . implode(',', $titleFieldIDs) . ")) ";
     }
     // When sorting by title in /top mode, we need the title of the parent item
     if ($onlyTopLevel && $titleSort) {
         $titleSortDataTable = "IDTSort";
         $titleSortNoteTable = "INoSort";
         $sql .= "LEFT JOIN itemData IDTSort ON (IDTSort.itemID={$itemIDSelector} AND " . "IDTSort.fieldID IN (" . implode(',', $titleFieldIDs) . ")) " . "LEFT JOIN itemNotes INoSort ON (INoSort.itemID={$itemIDSelector}) ";
     } else {
         $titleSortDataTable = "IDT";
         $titleSortNoteTable = "INo";
     }
     if (!empty($params['q'])) {
         // Pull in creators
         $sql .= "LEFT JOIN itemCreators IC ON (IC.itemID=I.itemID) " . "LEFT JOIN creators C ON (C.creatorID=IC.creatorID) ";
         // Pull in dates
         $dateFieldIDs = array_merge(array(Zotero_ItemFields::getID('date')), Zotero_ItemFields::getTypeFieldsFromBase('date'));
         $sql .= "LEFT JOIN itemData IDD ON (IDD.itemID=I.itemID AND IDD.fieldID IN " . "(" . implode(',', $dateFieldIDs) . ")) ";
     }
     if ($includeTrashed) {
         if (!empty($params['trashedItemsOnly'])) {
             $sql .= "JOIN deletedItems DI ON (DI.itemID=I.itemID) ";
         }
     } else {
         $sql .= "LEFT JOIN deletedItems DI ON (DI.itemID=I.itemID) ";
         // In /top mode, we don't want to show results for deleted parents or children
         if ($onlyTopLevel) {
             $sql .= "LEFT JOIN deletedItems DIP ON (DIP.itemID={$itemIDSelector}) ";
         }
     }
     if (!empty($params['sort'])) {
         switch ($params['sort']) {
             case 'title':
             case 'creator':
                 $sql .= "LEFT JOIN itemSortFields ISF ON (ISF.itemID={$itemIDSelector}) ";
                 break;
             case 'date':
                 // When sorting by date in /top mode, we need the date of the parent item
                 if ($onlyTopLevel) {
                     $sortTable = "IDDSort";
                     // Pull in dates
                     $dateFieldIDs = array_merge(array(Zotero_ItemFields::getID('date')), Zotero_ItemFields::getTypeFieldsFromBase('date'));
                     $sql .= "LEFT JOIN itemData IDDSort ON (IDDSort.itemID={$itemIDSelector} AND " . "IDDSort.fieldID IN (" . implode(',', $dateFieldIDs) . ")) ";
                 } else {
                     $sortTable = "IDD";
                     if (empty($params['q'])) {
                         $dateFieldIDs = array_merge(array(Zotero_ItemFields::getID('date')), Zotero_ItemFields::getTypeFieldsFromBase('date'));
                         $sql .= "LEFT JOIN itemData IDD ON (IDD.itemID=I.itemID AND IDD.fieldID IN (" . implode(',', $dateFieldIDs) . ")) ";
                     }
                 }
                 break;
             case 'itemType':
                 $locale = 'en-US';
                 $types = Zotero_ItemTypes::getAll($locale);
                 // TEMP: get localized string
                 // DEBUG: Why is attachment skipped in getAll()?
                 $types[] = array('id' => 14, 'localized' => 'Attachment');
                 foreach ($types as $type) {
                     $sql2 = "INSERT IGNORE INTO tmpItemTypeNames VALUES (?, ?, ?)";
                     Zotero_DB::query($sql2, array($type['id'], $locale, $type['localized']), $shardID);
                 }
                 // Join temp table to query
                 $sql .= "JOIN tmpItemTypeNames TITN ON (TITN.itemTypeID={$itemTypeIDSelector}) ";
                 break;
             case 'addedBy':
                 $isGroup = Zotero_Libraries::getType($libraryID) == 'group';
                 if ($isGroup) {
                     $sql2 = "SELECT DISTINCT createdByUserID FROM items\n\t\t\t\t\t\t\t\tJOIN groupItems USING (itemID) WHERE\n\t\t\t\t\t\t\t\tcreatedByUserID IS NOT NULL AND ";
                     if ($itemIDs) {
                         $sql2 .= "itemID IN (" . implode(', ', array_fill(0, sizeOf($itemIDs), '?')) . ") ";
                         $createdByUserIDs = Zotero_DB::columnQuery($sql2, $itemIDs, $shardID);
                     } else {
                         $sql2 .= "libraryID=?";
                         $createdByUserIDs = Zotero_DB::columnQuery($sql2, $libraryID, $shardID);
                     }
                     // Populate temp table with usernames
                     if ($createdByUserIDs) {
                         $toAdd = array();
                         foreach ($createdByUserIDs as $createdByUserID) {
                             $toAdd[] = array($createdByUserID, Zotero_Users::getUsername($createdByUserID));
                         }
                         $sql2 = "INSERT IGNORE INTO tmpCreatedByUsers VALUES ";
                         Zotero_DB::bulkInsert($sql2, $toAdd, 50, false, $shardID);
                         // Join temp table to query
                         $sql .= "LEFT JOIN groupItems GI ON (GI.itemID=I.itemID)\n\t\t\t\t\t\t\t\t\tLEFT JOIN tmpCreatedByUsers TCBU ON (TCBU.userID=GI.createdByUserID) ";
                     }
                 }
                 break;
         }
     }
     $sql .= "WHERE I.libraryID=? ";
     if (!$includeTrashed) {
         $sql .= "AND DI.itemID IS NULL ";
         // Hide deleted parents in /top mode
         if ($onlyTopLevel) {
             $sql .= "AND DIP.itemID IS NULL ";
         }
     }
     // Search on title, creators, and dates
     if (!empty($params['q'])) {
         $sql .= "AND (";
         $sql .= "IDT.value LIKE ? ";
         $sqlParams[] = '%' . $params['q'] . '%';
         $sql .= "OR INo.title LIKE ? ";
         $sqlParams[] = '%' . $params['q'] . '%';
         $sql .= "OR TRIM(CONCAT(firstName, ' ', lastName)) LIKE ? ";
         $sqlParams[] = '%' . $params['q'] . '%';
         $sql .= "OR SUBSTR(IDD.value, 1, 4) = ?";
         $sqlParams[] = $params['q'];
         // Full-text search
         if ($params['qmode'] == 'everything') {
             $ftKeys = Zotero_FullText::searchInLibrary($libraryID, $params['q']);
             if ($ftKeys) {
                 $sql .= " OR I.key IN (" . implode(', ', array_fill(0, sizeOf($ftKeys), '?')) . ") ";
                 $sqlParams = array_merge($sqlParams, $ftKeys);
             }
         }
         $sql .= ") ";
     }
     // Search on itemType
     if (!empty($params['itemType'])) {
         $itemTypes = Zotero_API::getSearchParamValues($params, 'itemType');
         if ($itemTypes) {
             if (sizeOf($itemTypes) > 1) {
                 throw new Exception("Cannot specify 'itemType' more than once", Z_ERROR_INVALID_INPUT);
             }
             $itemTypes = $itemTypes[0];
             $itemTypeIDs = array();
             foreach ($itemTypes['values'] as $itemType) {
                 $itemTypeID = Zotero_ItemTypes::getID($itemType);
                 if (!$itemTypeID) {
                     throw new Exception("Invalid itemType '{$itemType}'", Z_ERROR_INVALID_INPUT);
                 }
                 $itemTypeIDs[] = $itemTypeID;
             }
             $sql .= "AND I.itemTypeID " . ($itemTypes['negation'] ? "NOT " : "") . "IN (" . implode(',', array_fill(0, sizeOf($itemTypeIDs), '?')) . ") ";
             $sqlParams = array_merge($sqlParams, $itemTypeIDs);
         }
     }
     if (!$includeNotes) {
         $sql .= "AND I.itemTypeID != 1 ";
     }
     if (!empty($params['since'])) {
         $sql .= "AND {$itemVersionSelector} > ? ";
         $sqlParams[] = $params['since'];
     }
     // TEMP: for sync transition
     if (!empty($params['sincetime']) && $params['sincetime'] != 1) {
         $sql .= "AND I.serverDateModified >= FROM_UNIXTIME(?) ";
         $sqlParams[] = $params['sincetime'];
     }
     // Tags
     //
     // ?tag=foo
     // ?tag=foo bar // phrase
     // ?tag=-foo // negation
     // ?tag=\-foo // literal hyphen (only for first character)
     // ?tag=foo&tag=bar // AND
     $tagSets = Zotero_API::getSearchParamValues($params, 'tag');
     if ($tagSets) {
         $sql2 = "SELECT itemID FROM items WHERE libraryID=?\n";
         $sqlParams2 = array($libraryID);
         $positives = array();
         $negatives = array();
         foreach ($tagSets as $set) {
             $tagIDs = array();
             foreach ($set['values'] as $tag) {
                 $ids = Zotero_Tags::getIDs($libraryID, $tag, true);
                 if (!$ids) {
                     $ids = array(0);
                 }
                 $tagIDs = array_merge($tagIDs, $ids);
             }
             $tagIDs = array_unique($tagIDs);
             $tmpSQL = "SELECT itemID FROM items JOIN itemTags USING (itemID) " . "WHERE tagID IN (" . implode(',', array_fill(0, sizeOf($tagIDs), '?')) . ")";
             $ids = Zotero_DB::columnQuery($tmpSQL, $tagIDs, $shardID);
             if (!$ids) {
                 // If no negative tags, skip this tag set
                 if ($set['negation']) {
                     continue;
                 }
                 // If no positive tags, return no matches
                 return $results;
             }
             $ids = $ids ? $ids : array();
             $sql2 .= " AND itemID " . ($set['negation'] ? "NOT " : "") . " IN (" . implode(',', array_fill(0, sizeOf($ids), '?')) . ")";
             $sqlParams2 = array_merge($sqlParams2, $ids);
         }
         $tagItems = Zotero_DB::columnQuery($sql2, $sqlParams2, $shardID);
         // No matches
         if (!$tagItems) {
             return $results;
         }
         // Combine with passed ids
         if ($itemIDs) {
             $itemIDs = array_intersect($itemIDs, $tagItems);
             // None of the tag matches match the passed ids
             if (!$itemIDs) {
                 return $results;
             }
         } else {
             $itemIDs = $tagItems;
         }
     }
     if ($itemIDs) {
         $sql .= "AND I.itemID IN (" . implode(', ', array_fill(0, sizeOf($itemIDs), '?')) . ") ";
         $sqlParams = array_merge($sqlParams, $itemIDs);
     }
     if ($itemKeys) {
         $sql .= "AND I.key IN (" . implode(', ', array_fill(0, sizeOf($itemKeys), '?')) . ") ";
         $sqlParams = array_merge($sqlParams, $itemKeys);
     }
     $sql .= "ORDER BY ";
     if (!empty($params['sort'])) {
         switch ($params['sort']) {
             case 'dateAdded':
             case 'dateModified':
             case 'serverDateModified':
                 if ($onlyTopLevel) {
                     $orderSQL = "IP." . $params['sort'];
                 } else {
                     $orderSQL = "I." . $params['sort'];
                 }
                 break;
             case 'itemType':
                 $orderSQL = "TITN.itemTypeName";
                 /*
                 // Optional method for sorting by localized item type name, which would avoid
                 // the INSERT and JOIN above and allow these requests to use DB read replicas
                 $locale = 'en-US';
                 $types = Zotero_ItemTypes::getAll($locale);
                 // TEMP: get localized string
                 // DEBUG: Why is attachment skipped in getAll()?
                 $types[] = [
                 	'id' => 14,
                 	'localized' => 'Attachment'
                 ];
                 usort($types, function ($a, $b) {
                 	return strcasecmp($a['localized'], $b['localized']);
                 });
                 // Pass order of localized item type names for sorting
                 // e.g., FIELD(14, 12, 14, 26...) for sorting "Attachment" after "Artwork"
                 $orderSQL = "FIELD($itemTypeIDSelector, "
                 	. implode(", ", array_map(function ($x) {
                 		return $x['id'];
                 	}, $types)) . ")";
                 // If itemTypeID isn't found in passed list (currently only for NSF Reviewer),
                 // sort last
                 $orderSQL = "IFNULL(NULLIF($orderSQL, 0), 99999)";
                 // All items have types, so no need to check for empty sort values
                 $params['emptyFirst'] = true;
                 */
                 break;
             case 'title':
                 $orderSQL = "IFNULL(COALESCE(sortTitle, {$titleSortDataTable}.value, {$titleSortNoteTable}.title), '')";
                 break;
             case 'creator':
                 $orderSQL = "ISF.creatorSummary";
                 break;
                 // TODO: generic base field mapping-aware sorting
             // TODO: generic base field mapping-aware sorting
             case 'date':
                 $orderSQL = "{$sortTable}.value";
                 break;
             case 'addedBy':
                 if ($isGroup && $createdByUserIDs) {
                     $orderSQL = "TCBU.username";
                 } else {
                     $orderSQL = ($onlyTopLevel ? "IP" : "I") . ".dateAdded";
                 }
                 break;
             case 'itemKeyList':
                 $orderSQL = "FIELD(I.key," . implode(',', array_fill(0, sizeOf($itemKeys), '?')) . ")";
                 $sqlParams = array_merge($sqlParams, $itemKeys);
                 break;
             default:
                 $fieldID = Zotero_ItemFields::getID($params['sort']);
                 if (!$fieldID) {
                     throw new Exception("Invalid order field '" . $params['sort'] . "'");
                 }
                 $orderSQL = "(SELECT value FROM itemData WHERE itemID=I.itemID AND fieldID=?)";
                 if (!$params['emptyFirst']) {
                     $sqlParams[] = $fieldID;
                 }
                 $sqlParams[] = $fieldID;
         }
         if (!empty($params['direction'])) {
             $dir = $params['direction'];
         } else {
             $dir = "ASC";
         }
         if (!$params['emptyFirst']) {
             $sql .= "IFNULL({$orderSQL}, '') = '' {$dir}, ";
         }
         $sql .= $orderSQL . " {$dir}, ";
     }
     $sql .= "I.version " . (!empty($params['direction']) ? $params['direction'] : "ASC") . ", I.itemID " . (!empty($params['direction']) ? $params['direction'] : "ASC") . " ";
     if (!empty($params['limit'])) {
         $sql .= "LIMIT ?, ?";
         $sqlParams[] = $params['start'] ? $params['start'] : 0;
         $sqlParams[] = $params['limit'];
     }
     // Log SQL statement with embedded parameters
     /*if (true || !empty($_GET['sqldebug'])) {
     			error_log($onlyTopLevel);
     			
     			$debugSQL = "";
     			$parts = explode("?", $sql);
     			$debugSQLParams = $sqlParams;
     			foreach ($parts as $part) {
     				$val = array_shift($debugSQLParams);
     				$debugSQL .= $part;
     				if (!is_null($val)) {
     					$debugSQL .= is_int($val) ? $val : '"' . $val . '"';
     				}
     			}
     			error_log($debugSQL . ";");
     		}*/
     if ($params['format'] == 'versions') {
         $rows = Zotero_DB::query($sql, $sqlParams, $shardID);
     } else {
         $rows = Zotero_DB::columnQuery($sql, $sqlParams, $shardID);
     }
     $results['total'] = Zotero_DB::valueQuery("SELECT FOUND_ROWS()", false, $shardID);
     if ($rows) {
         if ($params['format'] == 'keys') {
             $results['results'] = $rows;
         } else {
             if ($params['format'] == 'versions') {
                 foreach ($rows as $row) {
                     $results['results'][$row['key']] = $row['version'];
                 }
             } else {
                 $results['results'] = Zotero_Items::get($libraryID, $rows);
             }
         }
     }
     return $results;
 }
Esempio n. 23
0
 public static function getGroupIDFromLibraryID($libraryID)
 {
     $cacheKey = 'libraryGroupID_' . $libraryID;
     $groupID = Z_Core::$MC->get($cacheKey);
     if ($groupID) {
         return $groupID;
     }
     $sql = "SELECT groupID FROM groups WHERE libraryID=?";
     $groupID = Zotero_DB::valueQuery($sql, $libraryID);
     if (!$groupID) {
         trigger_error("Group with libraryID {$libraryID} does not exist", E_USER_ERROR);
     }
     Z_Core::$MC->set($cacheKey, $groupID);
     return $groupID;
 }
Esempio n. 24
0
 /**
  * Check if search exists in the database
  *
  * @return	bool			TRUE if the relation exists, FALSE if not
  */
 public function exists()
 {
     $shardID = Zotero_Shards::getByLibraryID($this->libraryID);
     if ($this->id) {
         $sql = "SELECT COUNT(*) FROM relations WHERE relationID=?";
         return !!Zotero_DB::valueQuery($sql, $this->id, $shardID);
     }
     if ($this->subject && $this->predicate && $this->object) {
         $sql = "SELECT COUNT(*) FROM relations WHERE libraryID=? AND `key`=?";
         $params = array($this->libraryID, $this->getKey());
         $exists = !!Zotero_DB::valueQuery($sql, $params, $shardID);
         // TEMP
         // For linked items, check reverse order too, since client can save in reverse
         // order when an item is dragged from a group to a personal library
         if (!$exists && $this->predicate == Zotero_Relations::$linkedObjectPredicate && Zotero_Libraries::getType($this->libraryID) == 'user') {
             $sql = "SELECT COUNT(*) FROM relations WHERE libraryID=? AND `key`=?";
             $params = [$this->libraryID, Zotero_Relations::makeKey($this->object, $this->predicate, $this->subject)];
             return !!Zotero_DB::valueQuery($sql, $params, $shardID);
         }
         return $exists;
     }
     throw new Exception("ID or subject/predicate/object not set");
 }
Esempio n. 25
0
 /**
  * Returns user's object ids updated since |timestamp|, keyed by libraryID,
  * or count of all updated items if $countOnly is true
  *
  * @param	int			$libraryID			User ID
  * @param	string		$timestamp			Unix timestamp of last sync time
  * @param	array		$updatedLibraryIDs	Libraries with updated data
  * @return	array|int
  */
 public static function getUpdated($userID, $timestamp, $updatedLibraryIDs, $countOnly = false)
 {
     $table = self::$table;
     $id = self::$idColumn;
     $type = self::$objectType;
     $types = self::$objectTypePlural;
     $timestampCol = "serverDateModified";
     // All joined groups have to be checked
     $joinedGroupIDs = Zotero_Groups::getJoined($userID, $timestamp);
     $joinedLibraryIDs = array();
     foreach ($joinedGroupIDs as $groupID) {
         $joinedLibraryIDs[] = Zotero_Groups::getLibraryIDFromGroupID($groupID);
     }
     // Separate libraries into shards for querying
     $libraryIDs = array_unique(array_merge($joinedLibraryIDs, $updatedLibraryIDs));
     $shardLibraryIDs = array();
     foreach ($libraryIDs as $libraryID) {
         $shardID = Zotero_Shards::getByLibraryID($libraryID);
         if (!isset($shardLibraryIDs[$shardID])) {
             $shardLibraryIDs[$shardID] = array('updated' => array(), 'joined' => array());
         }
         if (in_array($libraryID, $joinedLibraryIDs)) {
             $shardLibraryIDs[$shardID]['joined'][] = $libraryID;
         } else {
             $shardLibraryIDs[$shardID]['updated'][] = $libraryID;
         }
     }
     if ($countOnly) {
         $count = 0;
         $fieldList = "COUNT(*)";
     } else {
         $updatedByLibraryID = array();
         $fieldList = "libraryID, {$id} AS id";
     }
     // Send query at each shard
     foreach ($shardLibraryIDs as $shardID => $libraryIDs) {
         $sql = "SELECT {$fieldList} FROM {$table} WHERE ";
         if ($libraryIDs['updated']) {
             $sql .= "(libraryID IN (" . implode(', ', array_fill(0, sizeOf($libraryIDs['updated']), '?')) . ")";
             $params = $libraryIDs['updated'];
             $sql .= " AND {$timestampCol} >= FROM_UNIXTIME(?))";
             $params[] = $timestamp;
         }
         if ($libraryIDs['joined']) {
             if ($libraryIDs['updated']) {
                 $sql .= " OR ";
             } else {
                 $params = array();
             }
             $sql .= "libraryID IN (" . implode(', ', array_fill(0, sizeOf($libraryIDs['joined']), '?')) . ")";
             $params = array_merge($params, $libraryIDs['joined']);
         }
         if ($countOnly) {
             $count += Zotero_DB::valueQuery($sql, $params, $shardID);
         } else {
             $rows = Zotero_DB::query($sql, $params, $shardID);
             if ($rows) {
                 // Separate ids by libraryID
                 foreach ($rows as $row) {
                     $updatedByLibraryID[$row['libraryID']][] = $row['id'];
                 }
             }
         }
     }
     return $countOnly ? $count : $updatedByLibraryID;
 }
Esempio n. 26
0
 private function load()
 {
     $sql = "SELECT * FROM groups WHERE groupID=?";
     $row = Zotero_DB::rowQuery($sql, $this->id);
     if (!$row) {
         return false;
     }
     foreach ($row as $field => $value) {
         switch ($field) {
             case 'groupID':
             case 'slug':
                 continue 2;
         }
         $this->{$field} = $value;
     }
     $sql = "SELECT userID FROM groupUsers WHERE groupID=? AND role='owner'";
     $userID = Zotero_DB::valueQuery($sql, $this->id);
     if (!$userID) {
         throw new Exception("Group {$this->id} doesn't have an owner");
     }
     $this->ownerUserID = $userID;
     $this->loaded = true;
     $this->changed = array();
 }
Esempio n. 27
0
 public static function search($libraryID, $params)
 {
     $results = array('results' => array(), 'total' => 0);
     // Default empty library
     if ($libraryID === 0) {
         return $results;
     }
     $shardID = Zotero_Shards::getByLibraryID($libraryID);
     $sql = "SELECT SQL_CALC_FOUND_ROWS DISTINCT tagID FROM tags " . "JOIN itemTags USING (tagID) WHERE libraryID=? ";
     $sqlParams = array($libraryID);
     // Pass a list of tagIDs, for when the initial search is done via SQL
     $tagIDs = !empty($params['tagIDs']) ? $params['tagIDs'] : array();
     // Filter for specific tags with "?tag=foo || bar"
     $tagNames = !empty($params['tag']) ? explode(' || ', $params['tag']) : array();
     if ($tagIDs) {
         $sql .= "AND tagID IN (" . implode(', ', array_fill(0, sizeOf($tagIDs), '?')) . ") ";
         $sqlParams = array_merge($sqlParams, $tagIDs);
     }
     if ($tagNames) {
         $sql .= "AND `name` IN (" . implode(', ', array_fill(0, sizeOf($tagNames), '?')) . ") ";
         $sqlParams = array_merge($sqlParams, $tagNames);
     }
     if (!empty($params['q'])) {
         if (!is_array($params['q'])) {
             $params['q'] = array($params['q']);
         }
         foreach ($params['q'] as $q) {
             $sql .= "AND name LIKE ? ";
             $sqlParams[] = "%{$q}%";
         }
     }
     $tagTypeSets = Zotero_API::getSearchParamValues($params, 'tagType');
     if ($tagTypeSets) {
         $positives = array();
         $negatives = array();
         foreach ($tagTypeSets as $set) {
             if ($set['negation']) {
                 $negatives = array_merge($negatives, $set['values']);
             } else {
                 $positives = array_merge($positives, $set['values']);
             }
         }
         if ($positives) {
             $sql .= "AND type IN (" . implode(',', array_fill(0, sizeOf($positives), '?')) . ") ";
             $sqlParams = array_merge($sqlParams, $positives);
         }
         if ($negatives) {
             $sql .= "AND type NOT IN (" . implode(',', array_fill(0, sizeOf($negatives), '?')) . ") ";
             $sqlParams = array_merge($sqlParams, $negatives);
         }
     }
     if (!empty($params['since'])) {
         $sql .= "AND version > ? ";
         $sqlParams[] = $params['since'];
     }
     if (!empty($params['sort'])) {
         $order = $params['sort'];
         if ($order == 'title') {
             // Force a case-insensitive sort
             $sql .= "ORDER BY name COLLATE utf8_unicode_ci ";
         } else {
             if ($order == 'numItems') {
                 $sql .= "GROUP BY tags.tagID ORDER BY COUNT(tags.tagID)";
             } else {
                 $sql .= "ORDER BY {$order} ";
             }
         }
         if (!empty($params['direction'])) {
             $sql .= " " . $params['direction'] . " ";
         }
     }
     if (!empty($params['limit'])) {
         $sql .= "LIMIT ?, ?";
         $sqlParams[] = $params['start'] ? $params['start'] : 0;
         $sqlParams[] = $params['limit'];
     }
     $ids = Zotero_DB::columnQuery($sql, $sqlParams, $shardID);
     $results['total'] = Zotero_DB::valueQuery("SELECT FOUND_ROWS()", false, $shardID);
     if ($ids) {
         $tags = array();
         foreach ($ids as $id) {
             $tags[] = Zotero_Tags::get($libraryID, $id);
         }
         $results['results'] = $tags;
     }
     return $results;
 }
Esempio n. 28
0
 public static function addCustomField($name)
 {
     if (self::getID($name)) {
         trigger_error("Field '{$name}' already exists", E_USER_ERROR);
     }
     if (!preg_match('/^[a-z][^\\s0-9]+$/', $name)) {
         trigger_error("Invalid field name '{$name}'", E_USER_ERROR);
     }
     // TODO: make sure user hasn't added too many already
     trigger_error("Unimplemented", E_USER_ERROR);
     // TODO: add to cache
     Zotero_DB::beginTransaction();
     $sql = "SELECT NEXT_ID(fieldID) FROM fields";
     $fieldID = Zotero_DB::valueQuery($sql);
     $sql = "INSERT INTO fields (?, ?, ?)";
     Zotero_DB::query($sql, array($fieldID, $name, 1));
     Zotero_DB::commit();
     return $fieldID;
 }
Esempio n. 29
0
 public static function addCustomType($name)
 {
     if (self::getID($name)) {
         trigger_error("Item type '{$name}' already exists", E_USER_ERROR);
     }
     if (!preg_match('/^[a-z][^\\s0-9]+$/', $name)) {
         trigger_error("Invalid item type name '{$name}'", E_USER_ERROR);
     }
     // TODO: make sure user hasn't added too many already
     Zotero_DB::beginTransaction();
     $sql = "SELECT NEXT_ID(creatorTypeID) FROM creatorTypes";
     $creatorTypeID = Zotero_DB::valueQuery($sql);
     $sql = "INSERT INTO creatorTypes (?, ?, ?)";
     Zotero_DB::query($sql, array($creatorTypeID, $name, 1));
     Zotero_DB::commit();
     return $creatorTypeID;
 }
Esempio n. 30
0
 public static function search($libraryID, $params)
 {
     $results = array('results' => array(), 'total' => 0);
     $shardID = Zotero_Shards::getByLibraryID($libraryID);
     $sql = "SELECT SQL_CALC_FOUND_ROWS DISTINCT ";
     if ($params['format'] == 'keys') {
         $sql .= "`key`";
     } else {
         if ($params['format'] == 'versions') {
             $sql .= "`key`, version";
         } else {
             $sql .= "searchID";
         }
     }
     $sql .= " FROM savedSearches WHERE libraryID=? ";
     $sqlParams = array($libraryID);
     // Pass a list of searchIDs, for when the initial search is done via SQL
     $searchIDs = !empty($params['searchIDs']) ? $params['searchIDs'] : array();
     // Or keys, for the searchKey parameter
     $searchKeys = $params['searchKey'];
     if (!empty($params['since'])) {
         $sql .= "AND version > ? ";
         $sqlParams[] = $params['since'];
     }
     // TEMP: for sync transition
     if (!empty($params['sincetime'])) {
         $sql .= "AND serverDateModified >= FROM_UNIXTIME(?) ";
         $sqlParams[] = $params['sincetime'];
     }
     if ($searchIDs) {
         $sql .= "AND searchID IN (" . implode(', ', array_fill(0, sizeOf($searchIDs), '?')) . ") ";
         $sqlParams = array_merge($sqlParams, $searchIDs);
     }
     if ($searchKeys) {
         $sql .= "AND `key` IN (" . implode(', ', array_fill(0, sizeOf($searchKeys), '?')) . ") ";
         $sqlParams = array_merge($sqlParams, $searchKeys);
     }
     if (!empty($params['sort'])) {
         switch ($params['sort']) {
             case 'title':
                 $orderSQL = 'searchName';
                 break;
             case 'searchKeyList':
                 $orderSQL = "FIELD(`key`," . implode(',', array_fill(0, sizeOf($searchKeys), '?')) . ")";
                 $sqlParams = array_merge($sqlParams, $searchKeys);
                 break;
             default:
                 $orderSQL = $params['sort'];
         }
         $sql .= "ORDER BY {$orderSQL}";
         if (!empty($params['direction'])) {
             $sql .= " {$params['direction']}";
         }
         $sql .= ", ";
     }
     $sql .= "version " . (!empty($params['direction']) ? $params['direction'] : "ASC") . ", searchID " . (!empty($params['direction']) ? $params['direction'] : "ASC") . " ";
     if (!empty($params['limit'])) {
         $sql .= "LIMIT ?, ?";
         $sqlParams[] = $params['start'] ? $params['start'] : 0;
         $sqlParams[] = $params['limit'];
     }
     if ($params['format'] == 'versions') {
         $rows = Zotero_DB::query($sql, $sqlParams, $shardID);
     } else {
         $rows = Zotero_DB::columnQuery($sql, $sqlParams, $shardID);
     }
     $results['total'] = Zotero_DB::valueQuery("SELECT FOUND_ROWS()", false, $shardID);
     if ($rows) {
         if ($params['format'] == 'keys') {
             $results['results'] = $rows;
         } else {
             if ($params['format'] == 'versions') {
                 foreach ($rows as $row) {
                     $results['results'][$row['key']] = $row['version'];
                 }
             } else {
                 $searches = array();
                 foreach ($rows as $id) {
                     $searches[] = self::get($libraryID, $id);
                 }
                 $results['results'] = $searches;
             }
         }
     }
     return $results;
 }