From e96a237c4b11a1d11fab1ce028c383dd8ffe30df Mon Sep 17 00:00:00 2001 From: Bogdan Abaev Date: Tue, 20 Jun 2023 15:09:43 -0400 Subject: [PATCH 1/3] unfiled items api calls /users/XXX/items/unfiled lists all items without collection /users/XXX/items/unfiled/tags lists all tags or items without collection Fixes: #11 --- controllers/ItemsController.php | 9 +++++++++ controllers/TagsController.php | 7 +++++++ include/config/routes.inc.php | 4 ++++ model/Items.inc.php | 10 ++++++++++ 4 files changed, 30 insertions(+) diff --git a/controllers/ItemsController.php b/controllers/ItemsController.php index d3a6e489..56977d4d 100644 --- a/controllers/ItemsController.php +++ b/controllers/ItemsController.php @@ -424,6 +424,15 @@ public function items() { $this->permissions ); } + // Unfiled items + else if ($this->subset == 'unfiled') { + $this->allowMethods(array('GET')); + + $title = "Unfiled items"; + $itemIDs = Zotero_Items::getItemsWithoutCollection( + $this->objectLibraryID + ); + } else if ($this->subset == 'children') { $item = Zotero_Items::getByLibraryAndKey($this->objectLibraryID, $this->objectKey); if (!$item) { diff --git a/controllers/TagsController.php b/controllers/TagsController.php index dad6a482..cc520cf9 100644 --- a/controllers/TagsController.php +++ b/controllers/TagsController.php @@ -144,6 +144,13 @@ public function tags() { $results = Zotero_Tags::search($this->objectLibraryID, $tagParams); } } + // Unfiled + else if($this->scopeObjectKey == 'unfiled') { + $title = "Unfiled tags"; + $items = Zotero_Items::getItemsWithoutCollection($this->objectLibraryID); + $this->queryParams['itemIDs'] = $items; + $results = Zotero_Tags::search($this->objectLibraryID, $this->queryParams); + } // Tags within a collection or item else if ($this->scopeObjectKey) { switch ($this->scopeObject) { diff --git a/include/config/routes.inc.php b/include/config/routes.inc.php index b8b1a75d..c7f30535 100644 --- a/include/config/routes.inc.php +++ b/include/config/routes.inc.php @@ -107,6 +107,10 @@ $router->map('/users/i:objectUserID/publications/items/:objectKey/children', ['controller' => 'Items', 'extra' => ['publications' => true, 'subset' => 'children']]); $router->map('/users/i:objectUserID/publications/items/:objectKey', ['controller' => 'Items', 'extra' => ['publications' => true]]); +// Unfiled items +$router->map('/users/i:objectUserID/items/unfiled', array('controller' => 'Items', 'extra' => array('subset' => 'unfiled'))); +$router->map('/users/i:objectUserID/items/unfiled/tags', array('controller' => 'Tags', 'extra' => array('subset' => 'unfiled'))); + // Other top-level URLs, with an optional key and subset $router->map('/users/i:objectUserID/:controller/:objectKey/:subset'); $router->map('/groups/i:objectGroupID/:controller/:objectKey/:subset'); diff --git a/model/Items.inc.php b/model/Items.inc.php index df08d0c6..aa1e5bce 100644 --- a/model/Items.inc.php +++ b/model/Items.inc.php @@ -2574,6 +2574,16 @@ private static function loadItems($libraryID, $itemIDs=array()) { } } } + + public static function getItemsWithoutCollection($libraryID) { + $sql = "SELECT items.itemID + FROM items + LEFT JOIN collectionItems ON items.itemID = collectionItems.itemID + WHERE collectionItems.collectionID IS NULL + AND items.libraryID = ?;"; + $items = Zotero_DB::columnQuery($sql, $libraryID, Zotero_Shards::getByLibraryID($libraryID)); + return $items; + } public static function getSortTitle($title) { From 058638c657a432c4627a4cad4af47e6fc298d7d1 Mon Sep 17 00:00:00 2001 From: Bogdan Abaev Date: Fri, 23 Jun 2023 17:23:18 +0000 Subject: [PATCH 2/3] fetch unfiled items through main search function --- controllers/ItemsController.php | 10 +++++++--- include/config/routes.inc.php | 4 ++-- model/Items.inc.php | 20 +++++++++----------- 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/controllers/ItemsController.php b/controllers/ItemsController.php index 56977d4d..43c25974 100644 --- a/controllers/ItemsController.php +++ b/controllers/ItemsController.php @@ -426,11 +426,15 @@ public function items() { } // Unfiled items else if ($this->subset == 'unfiled') { - $this->allowMethods(array('GET')); + $this->allowMethods(['GET']); $title = "Unfiled items"; - $itemIDs = Zotero_Items::getItemsWithoutCollection( - $this->objectLibraryID + $results = Zotero_Items::search( + $this->objectLibraryID, + true, + $this->queryParams, + $this->permissions, + true ); } else if ($this->subset == 'children') { diff --git a/include/config/routes.inc.php b/include/config/routes.inc.php index c7f30535..8d44fd4c 100644 --- a/include/config/routes.inc.php +++ b/include/config/routes.inc.php @@ -108,8 +108,8 @@ $router->map('/users/i:objectUserID/publications/items/:objectKey', ['controller' => 'Items', 'extra' => ['publications' => true]]); // Unfiled items -$router->map('/users/i:objectUserID/items/unfiled', array('controller' => 'Items', 'extra' => array('subset' => 'unfiled'))); -$router->map('/users/i:objectUserID/items/unfiled/tags', array('controller' => 'Tags', 'extra' => array('subset' => 'unfiled'))); +$router->map('/users/i:objectUserID/items/unfiled',['controller' => 'Items', 'extra' => ['subset' => 'unfiled']]); +$router->map('/users/i:objectUserID/items/unfiled/tags', ['controller' => 'Tags', 'extra' => ['subset' => 'unfiled']]); // Other top-level URLs, with an optional key and subset $router->map('/users/i:objectUserID/:controller/:objectKey/:subset'); diff --git a/model/Items.inc.php b/model/Items.inc.php index aa1e5bce..6cbe290a 100644 --- a/model/Items.inc.php +++ b/model/Items.inc.php @@ -65,7 +65,7 @@ public static function getDeleted($libraryID, $asIDs) { } - public static function search($libraryID, $onlyTopLevel = false, array $params = [], Zotero_Permissions $permissions = null) { + public static function search($libraryID, $onlyTopLevel = false, array $params = [], Zotero_Permissions $permissions = null, $unfiled = false ) { $rnd = "_" . uniqid($libraryID . "_"); $results = array('results' => array(), 'total' => 0); @@ -154,6 +154,10 @@ public static function search($libraryID, $onlyTopLevel = false, array $params = && ($params['format'] == 'keys' || $params['format'] == 'versions' || $topLevelItemSort)) { $sql .= "LEFT JOIN items ITLI ON (ITLI.itemID=$itemIDSelector) "; } + + if ($unfiled) { + $sql .= "LEFT JOIN collectionItems CI ON (CI.itemID=I.itemID) "; + } } // For 'q' we need the note; for sorting by title, we need the note title @@ -490,6 +494,10 @@ public static function search($libraryID, $onlyTopLevel = false, array $params = if ($skipITLI) { $sql .= "AND ITL.itemID IS NULL "; } + + if ($unfiled) { + $sql .= "AND CI.collectionID IS NULL "; + } $sql .= "ORDER BY "; @@ -2575,16 +2583,6 @@ private static function loadItems($libraryID, $itemIDs=array()) { } } - public static function getItemsWithoutCollection($libraryID) { - $sql = "SELECT items.itemID - FROM items - LEFT JOIN collectionItems ON items.itemID = collectionItems.itemID - WHERE collectionItems.collectionID IS NULL - AND items.libraryID = ?;"; - $items = Zotero_DB::columnQuery($sql, $libraryID, Zotero_Shards::getByLibraryID($libraryID)); - return $items; - } - public static function getSortTitle($title) { if (!$title) { From 297de2d7b4c6cb9070903553a7f7249575d7715b Mon Sep 17 00:00:00 2001 From: Bogdan Abaev Date: Fri, 23 Jun 2023 18:08:53 +0000 Subject: [PATCH 3/3] /items/unfiled/tags going through search function --- controllers/TagsController.php | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/controllers/TagsController.php b/controllers/TagsController.php index cc520cf9..6f5ed48d 100644 --- a/controllers/TagsController.php +++ b/controllers/TagsController.php @@ -72,7 +72,7 @@ public function tags() { $this->allowMethods(array('GET')); // Tags within items - if (($this->scopeObject == 'items' && !$this->scopeObjectKey) || $this->scopeObject == 'collection-items') { + if (($this->scopeObject == 'items' && (!$this->scopeObjectKey || $this->scopeObjectKey == 'unfiled')) || $this->scopeObject == 'collection-items') { // Proxy certain query parameters to items search $validItemParams = [ 'itemQ' => 'q', @@ -127,6 +127,17 @@ public function tags() { $this->permissions ); } + // Unfiled + else if($this->scopeObjectKey == 'unfiled') { + $title = "Unfiled tags"; + $itemResults = Zotero_Items::search( + $this->objectLibraryID, + true, + $itemParams, + $this->permissions, + true + ); + } else { $itemResults = Zotero_Items::search( $this->objectLibraryID, @@ -144,13 +155,6 @@ public function tags() { $results = Zotero_Tags::search($this->objectLibraryID, $tagParams); } } - // Unfiled - else if($this->scopeObjectKey == 'unfiled') { - $title = "Unfiled tags"; - $items = Zotero_Items::getItemsWithoutCollection($this->objectLibraryID); - $this->queryParams['itemIDs'] = $items; - $results = Zotero_Tags::search($this->objectLibraryID, $this->queryParams); - } // Tags within a collection or item else if ($this->scopeObjectKey) { switch ($this->scopeObject) {