Пример #1
0
	protected function loadTags($reload = false) {
		if ($this->loaded['tags'] && !$reload) return;
		
		if (!$this->id) {
			return;
		}
		
		Z_Core::debug("Loading tags for item $this->id");
		
		$sql = "SELECT tagID FROM itemTags JOIN tags USING (tagID) WHERE itemID=?";
		$tagIDs = Zotero_DB::columnQuery(
			$sql, $this->id, Zotero_Shards::getByLibraryID($this->libraryID)
		);
		$this->tags = [];
		if ($tagIDs) {
			foreach ($tagIDs as $tagID) {
				$this->tags[] = Zotero_Tags::get($this->libraryID, $tagID, true);
			}
		}
		$this->loaded['tags'] = true;
		$this->clearChanged('tags');
	}
Пример #2
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;
 }
Пример #3
0
 public function save($full = false)
 {
     if (!$this->libraryID) {
         trigger_error("Library ID must be set before saving", E_USER_ERROR);
     }
     Zotero_Tags::editCheck($this);
     if (!$this->changed) {
         Z_Core::debug("Tag {$this->id} has not changed");
         return false;
     }
     $shardID = Zotero_Shards::getByLibraryID($this->libraryID);
     Zotero_DB::beginTransaction();
     try {
         $tagID = $this->id ? $this->id : Zotero_ID::get('tags');
         $isNew = !$this->id;
         Z_Core::debug("Saving tag {$tagID}");
         $key = $this->key ? $this->key : $this->generateKey();
         $timestamp = Zotero_DB::getTransactionTimestamp();
         $dateAdded = $this->dateAdded ? $this->dateAdded : $timestamp;
         $dateModified = $this->dateModified ? $this->dateModified : $timestamp;
         $fields = "name=?, `type`=?, dateAdded=?, dateModified=?,\n\t\t\t\tlibraryID=?, `key`=?, serverDateModified=?";
         $params = array($this->name, $this->type ? $this->type : 0, $dateAdded, $dateModified, $this->libraryID, $key, $timestamp);
         try {
             if ($isNew) {
                 $sql = "INSERT INTO tags SET tagID=?, {$fields}";
                 $stmt = Zotero_DB::getStatement($sql, true, $shardID);
                 Zotero_DB::queryFromStatement($stmt, array_merge(array($tagID), $params));
                 // Remove from delete log if it's there
                 $sql = "DELETE FROM syncDeleteLogKeys WHERE libraryID=? AND objectType='tag' AND `key`=?";
                 Zotero_DB::query($sql, array($this->libraryID, $key), $shardID);
             } else {
                 $sql = "UPDATE tags SET {$fields} WHERE tagID=?";
                 $stmt = Zotero_DB::getStatement($sql, true, Zotero_Shards::getByLibraryID($this->libraryID));
                 Zotero_DB::queryFromStatement($stmt, array_merge($params, array($tagID)));
             }
         } catch (Exception $e) {
             // If an incoming tag is the same as an existing tag, but with a different key,
             // then delete the old tag and add its linked items to the new tag
             if (preg_match("/Duplicate entry .+ for key 'uniqueTags'/", $e->getMessage())) {
                 // GET existing tag
                 $existing = Zotero_Tags::getIDs($this->libraryID, $this->name);
                 if (!$existing) {
                     throw new Exception("Existing tag not found");
                 }
                 foreach ($existing as $id) {
                     $tag = Zotero_Tags::get($this->libraryID, $id, true);
                     if ($tag->__get('type') == $this->type) {
                         $linked = $tag->getLinkedItems(true);
                         Zotero_Tags::delete($this->libraryID, $tag->key);
                         break;
                     }
                 }
                 // Save again
                 if ($isNew) {
                     $sql = "INSERT INTO tags SET tagID=?, {$fields}";
                     $stmt = Zotero_DB::getStatement($sql, true, $shardID);
                     Zotero_DB::queryFromStatement($stmt, array_merge(array($tagID), $params));
                     // Remove from delete log if it's there
                     $sql = "DELETE FROM syncDeleteLogKeys WHERE libraryID=? AND objectType='tag' AND `key`=?";
                     Zotero_DB::query($sql, array($this->libraryID, $key), $shardID);
                 } else {
                     $sql = "UPDATE tags SET {$fields} WHERE tagID=?";
                     $stmt = Zotero_DB::getStatement($sql, true, Zotero_Shards::getByLibraryID($this->libraryID));
                     Zotero_DB::queryFromStatement($stmt, array_merge($params, array($tagID)));
                 }
                 $new = array_unique(array_merge($linked, $this->getLinkedItems(true)));
                 $this->setLinkedItems($new);
             } else {
                 throw $e;
             }
         }
         // Linked items
         if ($full || !empty($this->changed['linkedItems'])) {
             $removed = array();
             $newids = array();
             $currentIDs = $this->getLinkedItems(true);
             if (!$currentIDs) {
                 $currentIDs = array();
             }
             if ($full) {
                 $sql = "SELECT itemID FROM itemTags WHERE tagID=?";
                 $stmt = Zotero_DB::getStatement($sql, true, $shardID);
                 $dbItemIDs = Zotero_DB::columnQueryFromStatement($stmt, $tagID);
                 if ($dbItemIDs) {
                     $removed = array_diff($dbItemIDs, $currentIDs);
                     $newids = array_diff($currentIDs, $dbItemIDs);
                 } else {
                     $newids = $currentIDs;
                 }
             } else {
                 if ($this->previousData['linkedItems']) {
                     $removed = array_diff($this->previousData['linkedItems'], $currentIDs);
                     $newids = array_diff($currentIDs, $this->previousData['linkedItems']);
                 } else {
                     $newids = $currentIDs;
                 }
             }
             if ($removed) {
                 $sql = "DELETE FROM itemTags WHERE tagID=? AND itemID IN (";
                 $q = array_fill(0, sizeOf($removed), '?');
                 $sql .= implode(', ', $q) . ")";
                 Zotero_DB::query($sql, array_merge(array($this->id), $removed), $shardID);
             }
             if ($newids) {
                 $newids = array_values($newids);
                 $sql = "INSERT INTO itemTags (tagID, itemID) VALUES ";
                 $maxInsertGroups = 50;
                 Zotero_DB::bulkInsert($sql, $newids, $maxInsertGroups, $tagID, $shardID);
             }
             //Zotero.Notifier.trigger('add', 'collection-item', $this->id . '-' . $itemID);
         }
         Zotero_DB::commit();
         Zotero_Tags::cachePrimaryData(array('id' => $tagID, 'libraryID' => $this->libraryID, 'key' => $key, 'name' => $this->name, 'type' => $this->type ? $this->type : 0, 'dateAdded' => $dateAdded, 'dateModified' => $dateModified));
     } catch (Exception $e) {
         Zotero_DB::rollback();
         throw $e;
     }
     // If successful, set values in object
     if (!$this->id) {
         $this->id = $tagID;
     }
     if (!$this->key) {
         $this->key = $key;
     }
     $this->init();
     if ($isNew) {
         Zotero_Tags::cache($this);
         Zotero_Tags::cacheLibraryKeyID($this->libraryID, $key, $tagID);
     }
     return $this->id;
 }
Пример #4
0
 /**
  * $tags is an array of objects with properties 'tag' and 'type'
  */
 public function setTags($newTags)
 {
     if (!$this->id) {
         throw new Exception('itemID not set');
     }
     $numTags = $this->numTags();
     if (!$newTags && !$numTags) {
         return false;
     }
     Zotero_DB::beginTransaction();
     $existingTags = $this->getTags();
     $toAdd = array();
     $toRemove = array();
     // Get new tags not in existing
     for ($i = 0, $len = sizeOf($newTags); $i < $len; $i++) {
         if (!isset($newTags[$i]->type)) {
             $newTags[$i]->type = 0;
         }
         $name = trim($newTags[$i]->tag);
         // 'tag', not 'name', since that's what JSON uses
         $type = $newTags[$i]->type;
         foreach ($existingTags as $tag) {
             // Do a case-insensitive comparison, to match the client
             if (strtolower($tag->name) == strtolower($name) && $tag->type == $type) {
                 continue 2;
             }
         }
         $toAdd[] = $newTags[$i];
     }
     // Get existing tags not in new
     for ($i = 0, $len = sizeOf($existingTags); $i < $len; $i++) {
         $name = $existingTags[$i]->name;
         $type = $existingTags[$i]->type;
         foreach ($newTags as $tag) {
             if (strtolower($tag->tag) == strtolower($name) && $tag->type == $type) {
                 continue 2;
             }
         }
         $toRemove[] = $existingTags[$i];
     }
     foreach ($toAdd as $tag) {
         $name = $tag->tag;
         $type = $tag->type;
         $tagID = Zotero_Tags::getID($this->libraryID, $name, $type, true);
         if (!$tagID) {
             $tag = new Zotero_Tag();
             $tag->libraryID = $this->libraryID;
             $tag->name = $name;
             $tag->type = $type;
             $tagID = $tag->save();
         }
         $tag = Zotero_Tags::get($this->libraryID, $tagID);
         $tag->addItem($this->id);
         $tag->save();
     }
     foreach ($toRemove as $tag) {
         $tag->removeItem($this->id);
         $tag->save();
     }
     Zotero_DB::commit();
     return $toAdd || $toRemove;
 }
Пример #5
0
    public function tags()
    {
        $this->allowMethods(array('GET'));
        if (!$this->permissions->canAccess($this->objectLibraryID)) {
            $this->e403();
        }
        $tags = array();
        $totalResults = 0;
        $name = $this->objectName;
        $fixedValues = array();
        // Set of tags matching name
        if ($name && $this->subset != 'tags') {
            $tagIDs = Zotero_Tags::getIDs($this->objectLibraryID, $name);
            if (!$tagIDs) {
                $this->e404();
            }
            $title = "Tags matching ‘" . $name . "’";
        } else {
            if ($this->scopeObject) {
                // If id, redirect to key URL
                if ($this->scopeObjectID) {
                    if (!in_array($this->scopeObject, array("collections", "items"))) {
                        $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 . "/tags" . $qs);
                    exit;
                }
                switch ($this->scopeObject) {
                    case 'collections':
                        $collection = Zotero_Collections::getByLibraryAndKey($this->objectLibraryID, $this->scopeObjectKey);
                        if (!$collection) {
                            $this->e404();
                        }
                        $title = "Tags in Collection ‘" . $collection->name . "’";
                        $counts = $collection->getTagItemCounts();
                        $tagIDs = array();
                        if ($counts) {
                            foreach ($counts as $tagID => $count) {
                                $tagIDs[] = $tagID;
                                $fixedValues[$tagID] = array('numItems' => $count);
                            }
                        }
                        break;
                    case 'items':
                        $item = Zotero_Items::getByLibraryAndKey($this->objectLibraryID, $this->scopeObjectKey);
                        if (!$item) {
                            $this->e404();
                        }
                        $title = "Tags of '" . $item->getDisplayTitle() . "'";
                        $tagIDs = $item->getTags(true);
                        break;
                    default:
                        throw new Exception("Invalid tags scope object '{$this->scopeObject}'");
                }
            } else {
                $title = "Tags";
                $results = Zotero_Tags::getAllAdvanced($this->objectLibraryID, $this->queryParams);
                $tags = $results['objects'];
                $totalResults = $results['total'];
            }
        }
        if (!empty($tagIDs)) {
            foreach ($tagIDs as $tagID) {
                $tags[] = Zotero_Tags::get($this->objectLibraryID, $tagID);
            }
            // Fake sorting and limiting
            $totalResults = sizeOf($tags);
            $key = $this->queryParams['order'];
            // 'title' order means 'name' for tags
            if ($key == 'title') {
                $key = 'name';
            }
            $dir = $this->queryParams['sort'];
            $cmp = create_function('$a, $b', '$dir = "' . $dir . '" == "asc" ? 1 : -1;
				if ($a->' . $key . ' == $b->' . $key . ') {
					return 0;
				}
				else {
					return ($a->' . $key . ' > $b->' . $key . ') ? $dir : ($dir * -1);}');
            usort($tags, $cmp);
            $tags = array_slice($tags, $this->queryParams['start'], $this->queryParams['limit']);
        }
        $this->responseXML = Zotero_Atom::createAtomFeed($this->getFeedNamePrefix($this->objectLibraryID) . $title, $this->uri, $tags, $totalResults, $this->queryParams, $this->apiVersion, $this->permissions, $fixedValues);
        $this->end();
    }
Пример #6
0
 /**
  * Returns all tags assigned to items in this collection
  */
 public function getTags($asIDs = false)
 {
     $sql = "SELECT tagID FROM tags JOIN itemTags USING (tagID)\n\t\t\t\tJOIN collectionItems USING (itemID) WHERE collectionID=? ORDER BY name";
     $tagIDs = Zotero_DB::columnQuery($sql, $this->id, Zotero_Shards::getByLibraryID($this->libraryID));
     if (!$tagIDs) {
         return false;
     }
     if ($asIDs) {
         return $tagIDs;
     }
     $tagObjs = array();
     foreach ($tagIDs as $tagID) {
         $tag = Zotero_Tags::get($tagID, true);
         $tagObjs[] = $tag;
     }
     return $tagObjs;
 }
Пример #7
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;
 }
Пример #8
0
 private function loadTags()
 {
     if (!$this->id) {
         return;
     }
     Z_Core::debug("Loading tags for item {$this->id}");
     if ($this->loaded['tags']) {
         throw new Exception("Tags already loaded for item {$this->id}");
     }
     $sql = "SELECT tagID FROM itemTags JOIN tags USING (tagID) WHERE itemID=?";
     $tagIDs = Zotero_DB::columnQuery($sql, $this->id, Zotero_Shards::getByLibraryID($this->libraryID));
     $this->tags = [];
     if ($tagIDs) {
         foreach ($tagIDs as $tagID) {
             $this->tags[] = Zotero_Tags::get($this->libraryID, $tagID, true);
         }
     }
     $this->loaded['tags'] = true;
 }
Пример #9
0
 public function tags()
 {
     $this->allowMethods(['HEAD', 'GET', 'DELETE']);
     if (!$this->permissions->canAccess($this->objectLibraryID)) {
         $this->e403();
     }
     if ($this->isWriteMethod()) {
         // Check for library write access
         if (!$this->permissions->canWrite($this->objectLibraryID)) {
             $this->e403("Write access denied");
         }
         // Make sure library hasn't been modified
         $this->checkLibraryIfUnmodifiedSinceVersion(true);
         Zotero_Libraries::updateVersionAndTimestamp($this->objectLibraryID);
     }
     $tagIDs = array();
     $results = array();
     $name = $this->objectName;
     $fixedValues = array();
     $this->libraryVersion = Zotero_Libraries::getUpdatedVersion($this->objectLibraryID);
     // Set of tags matching name
     if ($name && $this->subset != 'tags') {
         $this->allowMethods(array('GET'));
         $tagIDs = Zotero_Tags::getIDs($this->objectLibraryID, $name);
         if (!$tagIDs) {
             $this->e404();
         }
         $title = "Tags matching ‘" . $name . "’";
     } else {
         $this->allowMethods(array('GET', 'DELETE'));
         if ($this->scopeObject) {
             $this->allowMethods(array('GET'));
             switch ($this->scopeObject) {
                 case 'collections':
                     $collection = Zotero_Collections::getByLibraryAndKey($this->objectLibraryID, $this->scopeObjectKey);
                     if (!$collection) {
                         $this->e404();
                     }
                     $title = "Tags in Collection ‘" . $collection->name . "’";
                     $counts = $collection->getTagItemCounts();
                     $tagIDs = array();
                     if ($counts) {
                         foreach ($counts as $tagID => $count) {
                             $tagIDs[] = $tagID;
                             $fixedValues[$tagID] = array('numItems' => $count);
                         }
                     }
                     break;
                 case 'items':
                     $item = Zotero_Items::getByLibraryAndKey($this->objectLibraryID, $this->scopeObjectKey);
                     if (!$item) {
                         $this->e404();
                     }
                     $title = "Tags of '" . $item->getDisplayTitle() . "'";
                     $tagIDs = $item->getTags(true);
                     break;
                 default:
                     throw new Exception("Invalid tags scope object '{$this->scopeObject}'");
             }
         } else {
             if ($this->method == 'DELETE') {
                 // Filter for specific tags with "?tag=foo || bar"
                 $tagNames = !empty($this->queryParams['tag']) ? explode(' || ', $this->queryParams['tag']) : array();
                 Zotero_DB::beginTransaction();
                 foreach ($tagNames as $tagName) {
                     $tagIDs = Zotero_Tags::getIDs($this->objectLibraryID, $tagName);
                     foreach ($tagIDs as $tagID) {
                         $tag = Zotero_Tags::get($this->objectLibraryID, $tagID, true);
                         Zotero_Tags::delete($this->objectLibraryID, $tag->key);
                     }
                 }
                 Zotero_DB::commit();
                 $this->e204();
             } else {
                 $title = "Tags";
                 $results = Zotero_Tags::search($this->objectLibraryID, $this->queryParams);
             }
         }
     }
     if ($tagIDs) {
         $this->queryParams['tagIDs'] = $tagIDs;
         $results = Zotero_Tags::search($this->objectLibraryID, $this->queryParams);
     }
     $this->generateMultiResponse($results, $title, $fixedValues);
     $this->end();
 }