uri.js (12661B)
1 /* 2 ***** BEGIN LICENSE BLOCK ***** 3 4 Copyright © 2009 Center for History and New Media 5 George Mason University, Fairfax, Virginia, USA 6 http://zotero.org 7 8 This file is part of Zotero. 9 10 Zotero is free software: you can redistribute it and/or modify 11 it under the terms of the GNU Affero General Public License as published by 12 the Free Software Foundation, either version 3 of the License, or 13 (at your option) any later version. 14 15 Zotero is distributed in the hope that it will be useful, 16 but WITHOUT ANY WARRANTY; without even the implied warranty of 17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 GNU Affero General Public License for more details. 19 20 You should have received a copy of the GNU Affero General Public License 21 along with Zotero. If not, see <http://www.gnu.org/licenses/>. 22 23 ***** END LICENSE BLOCK ***** 24 */ 25 26 27 Zotero.URI = new function () { 28 Zotero.defineProperty(this, 'defaultPrefix', { 29 value: 'http://zotero.org/' 30 }); 31 32 // This should match all possible URIs. Match groups are as follows: 33 // 1: users|groups 34 // 2: local/|NULL 35 // 3: userID|groupID|localUserKey 36 // 4: publications|feeds/libraryID|NULL 37 // 5: items|collections|NULL 38 // 6: itemKey|collectionKey|NULL 39 var uriPartsRe = new RegExp( 40 '^' + Zotero.Utilities.quotemeta(this.defaultPrefix) 41 + '(users|groups)/(local/)?(\\w+)(?:/(publications|feeds/\\w+))?' 42 + '(?:/(items|collections)/(\\w+))?' 43 ); 44 45 /** 46 * Get a URI with the user's local key, if there is one 47 * 48 * @return {String|False} e.g., 'http://zotero.org/users/local/v3aG8nQf' 49 */ 50 this.getLocalUserURI = function () { 51 return this.defaultPrefix + "users/local/" + Zotero.Users.getLocalUserKey(); 52 } 53 54 55 /** 56 * Get a URI for the user, creating a local user key if necessary 57 * 58 * @return {String} 59 */ 60 this.getCurrentUserURI = function () { 61 var userID = Zotero.Users.getCurrentUserID(); 62 if (userID) { 63 return this.defaultPrefix + "users/" + userID; 64 } 65 66 return this.getLocalUserURI(); 67 } 68 69 70 this.getCurrentUserLibraryURI = function () { 71 var userID = Zotero.Users.getCurrentUserID(); 72 if (!userID) { 73 return false; 74 } 75 return this.getCurrentUserURI() + "/items"; 76 } 77 78 79 this.getLibraryURI = function (libraryID) { 80 return this.defaultPrefix + this.getLibraryPath(libraryID); 81 } 82 83 84 /** 85 * Get path portion of library URI (e.g., users/6 or groups/1) 86 */ 87 this.getLibraryPath = function (libraryID) { 88 var libraryType = Zotero.Libraries.get(libraryID).libraryType; 89 90 switch (libraryType) { 91 case 'user': 92 var id = Zotero.Users.getCurrentUserID(); 93 if (!id) { 94 id = 'local/' + Zotero.Users.getLocalUserKey(); 95 } 96 97 if (libraryType == 'publications') { 98 return "users/" + id + "/" + libraryType; 99 } 100 101 break; 102 103 case 'feed': 104 // Since feeds are not currently synced, generate a local URI 105 return "users/local/" + Zotero.Users.getLocalUserKey() + "/feeds/" + libraryID; 106 107 case 'group': 108 var id = Zotero.Groups.getGroupIDFromLibraryID(libraryID); 109 break; 110 111 default: 112 throw new Error(`Unsupported library type '${libraryType}' for library ${libraryID}`); 113 } 114 115 return libraryType + "s/" + id; 116 } 117 118 119 /** 120 * Get library from path (e.g., users/6 or groups/1) 121 * 122 * @return {Zotero.Library|false} 123 */ 124 this.getPathLibrary = function (path) { 125 let matches = path.match(/^\/\/?users\/(\d+)/); 126 if (matches) { 127 let userID = matches[1]; 128 let currentUserID = Zotero.Users.getCurrentUserID(); 129 if (userID != currentUserID) { 130 Zotero.debug("User ID from streaming server doesn't match current id! " 131 + `(${userID} != ${currentUserID})`); 132 return false; 133 } 134 return Zotero.Libraries.userLibrary; 135 } 136 matches = path.match(/^\/groups\/(\d+)/); 137 if (matches) { 138 let groupID = matches[1]; 139 return Zotero.Groups.get(groupID); 140 } 141 } 142 143 144 /** 145 * Return URI of item, which might be a local URI if user hasn't synced 146 */ 147 this.getItemURI = function (item) { 148 return this._getObjectURI(item); 149 } 150 151 152 /** 153 * Get path portion of item URI (e.g., users/6/items/ABCD1234 or groups/1/items/ABCD1234) 154 */ 155 this.getItemPath = function (item) { 156 return this._getObjectPath(item); 157 } 158 159 160 this.getFeedItemURI = function(feedItem) { 161 return this.getItemURI(feedItem); 162 } 163 164 this.getFeedItemPath = function(feedItem) { 165 return this.getItemPath(feedItem); 166 } 167 168 /** 169 * Return URI of collection, which might be a local URI if user hasn't synced 170 */ 171 this.getCollectionURI = function (collection) { 172 return this._getObjectURI(collection); 173 } 174 175 176 /** 177 * Get path portion of collection URI (e.g., users/6/collections/ABCD1234 or groups/1/collections/ABCD1234) 178 */ 179 this.getCollectionPath = function (collection) { 180 return this._getObjectPath(collection); 181 } 182 183 this.getFeedURI = function(feed) { 184 return this.getLibraryURI(feed); 185 } 186 187 this.getFeedPath = function(feed) { 188 return this.getLibraryPath(feed); 189 } 190 191 192 this.getGroupsURL = function () { 193 return ZOTERO_CONFIG.WWW_BASE_URL + "groups"; 194 } 195 196 197 /** 198 * @param {Zotero.Group} group 199 * @return {String} 200 */ 201 this.getGroupURI = function (group, webRoot) { 202 var uri = this._getObjectURI(group); 203 if (webRoot) { 204 uri = uri.replace(ZOTERO_CONFIG.BASE_URI, ZOTERO_CONFIG.WWW_BASE_URL); 205 } 206 return uri; 207 } 208 209 this._getObjectPath = function(obj) { 210 let path = this.getLibraryPath(obj.libraryID); 211 if (obj instanceof Zotero.Library) { 212 return path; 213 } 214 215 if (obj instanceof Zotero.Item) { 216 return path + '/items/' + obj.key; 217 } 218 219 if (obj instanceof Zotero.Collection) { 220 return path + '/collections/' + obj.key; 221 } 222 223 throw new Error("Unsupported object type '" + obj._objectType + "'"); 224 } 225 226 this._getObjectURI = function(obj) { 227 return this.defaultPrefix + this._getObjectPath(obj); 228 } 229 230 /** 231 * Convert an item URI into an item 232 * 233 * @param {String} itemURI 234 * @return {Promise<Zotero.Item|false>} 235 */ 236 this.getURIItem = Zotero.Promise.method(function (itemURI) { 237 var obj = this._getURIObject(itemURI, 'item'); 238 if (!obj) return false; 239 return Zotero.Items.getByLibraryAndKeyAsync(obj.libraryID, obj.key); 240 }); 241 242 243 /** 244 * @param {String} itemURI 245 * @return {Object|FALSE} - Object with 'libraryID' and 'key', or FALSE if item not found 246 */ 247 this.getURIItemLibraryKey = function (itemURI) { 248 return this._getURIObject(itemURI, 'item'); 249 } 250 251 252 /** 253 * Convert an item URI into a libraryID and key from the database, without relying on global state 254 * 255 * Note that while the URI must point to a valid library, the item doesn't need to exist 256 */ 257 this.getURIItemLibraryKeyFromDB = function (itemURI) { 258 return this._getURIObjectLibraryKeyFromDB(itemURI, 'item'); 259 } 260 261 262 /** 263 * @param {String} itemURI 264 * @return {Integer|FALSE} - itemID of matching item, or FALSE if none 265 */ 266 this.getURIItemID = function (itemURI) { 267 var obj = this._getURIObject(itemURI, 'item'); 268 if (!obj) return false; 269 return Zotero.Items.getIDFromLibraryAndKey(obj.libraryID, obj.key); 270 } 271 272 273 /** 274 * Convert a collection URI into a collection 275 * 276 * @param {String} collectionURI 277 * @param {Zotero.Collection|FALSE} 278 * @return {Promise<Zotero.Collection|false>} 279 */ 280 this.getURICollection = Zotero.Promise.method(function (collectionURI) { 281 var obj = this._getURIObject(collectionURI, 'collection'); 282 if (!obj) return false; 283 return Zotero.Collections.getByLibraryAndKeyAsync(obj.libraryID, obj.key); 284 }); 285 286 287 /** 288 * @param {String} collectionURI 289 * @return {Object|FALSE} - Object with 'libraryID' and 'key', or FALSE if item not found 290 */ 291 this.getURICollectionLibraryKey = function (collectionURI) { 292 return this._getURIObject(collectionURI, 'collection');; 293 } 294 295 296 /** 297 * @param {String} collectionURI 298 * @return {Integer|FALSE} - collectionID of matching collection, or FALSE if none 299 */ 300 this.getURICollectionID = function (collectionURI) { 301 var obj = this._getURIObject(collectionURI, 'collection'); 302 if (!obj) return false; 303 return Zotero.Collections.getIDFromLibraryAndKey(obj.libraryID, obj.key); 304 } 305 306 307 /** 308 * Convert a library URI into a libraryID 309 * 310 * @param {String} libraryURI 311 * @return {Integer|FALSE} - libraryID, or FALSE if no matching library 312 */ 313 this.getURILibrary = function (libraryURI) { 314 let library = this._getURIObjectLibrary(libraryURI); 315 return library ? library.id : false; 316 } 317 318 319 this.getURIFeed = function (feedURI) { 320 return this._getURIObjectLibrary(feedURI, 'feed'); 321 } 322 323 324 /** 325 * Convert an object URI into an object containing libraryID and key 326 * 327 * @param {String} objectURI 328 * @param {String} [type] Object type to expect 329 * @return {Object|FALSE} - An object containing libraryID, objectType and 330 * key. Key and objectType may not be present if the URI references a 331 * library itself 332 */ 333 this._getURIObject = function (objectURI, type) { 334 let uri = objectURI.replace(/\/+$/, ''); // Drop trailing / 335 let uriParts = uri.match(uriPartsRe); 336 337 if (!uriParts) { 338 throw new Error("Could not parse object URI " + objectURI); 339 } 340 341 let library = this._getURIObjectLibrary(objectURI); 342 if (!library) return false; 343 344 let retObj = {libraryID: library.libraryID}; 345 if (!uriParts[5]) { 346 // References the library itself 347 return retObj; 348 } 349 350 retObj.objectType = uriParts[5] == 'items' ? 'item' : 'collection'; 351 retObj.key = uriParts[6]; 352 353 if (type && type != retObj.objectType) return false; 354 355 return retObj; 356 }; 357 358 359 /** 360 * Convert an object URI into a Zotero.Library that the object is in 361 * 362 * @param {String} objectURI 363 * @return {Zotero.Library|FALSE} - An object referenced by the URI 364 */ 365 this._getURIObjectLibrary = function (objectURI) { 366 let uri = objectURI.replace(/\/+$/, ''); // Drop trailing "/" 367 let uriParts = uri.match(uriPartsRe); 368 369 if (!uriParts) { 370 throw new Error("Could not parse object URI " + objectURI); 371 } 372 373 let library; 374 if (uriParts[1] == 'users') { 375 let type = uriParts[4]; 376 if (!type) { 377 // Handles local and synced libraries 378 library = Zotero.Libraries.get(Zotero.Libraries.userLibraryID); 379 } else { 380 let feedID = type.split('/')[1]; 381 library = Zotero.Libraries.get(feedID); 382 } 383 } else { 384 // Group libraries 385 library = Zotero.Groups.get(uriParts[3]); 386 } 387 388 if (!library) { 389 Zotero.debug("Could not find a library for URI " + objectURI, 2, true); 390 return false; 391 } 392 393 return library; 394 } 395 396 397 /** 398 * Convert an object URI into a libraryID from the database, without relying on global state 399 * 400 * @param {String} objectURI 401 * @return {Promise<Integer|FALSE>} - A promise for either a libraryID or FALSE if a matching 402 * library couldn't be found 403 */ 404 this._getURIObjectLibraryID = Zotero.Promise.coroutine(function* (objectURI) { 405 let uri = objectURI.replace(/\/+$/, ''); // Drop trailing "/" 406 let uriParts = uri.match(uriPartsRe); 407 408 let libraryID; 409 if (uriParts[1] == 'users') { 410 let type = uriParts[4]; 411 // Personal library 412 if (!type || type == 'publications') { 413 libraryID = yield Zotero.DB.valueQueryAsync( 414 "SELECT libraryID FROM libraries WHERE type='user'" 415 ); 416 } 417 // Feed libraries 418 else { 419 libraryID = type.split('/')[1]; 420 } 421 } 422 // Group libraries 423 else { 424 libraryID = yield Zotero.DB.valueQueryAsync( 425 "SELECT libraryID FROM groups WHERE groupID=?", uriParts[3] 426 ); 427 } 428 429 if (!libraryID) { 430 Zotero.debug("Could not find a library for URI " + objectURI, 2, true); 431 return false; 432 } 433 434 return libraryID; 435 }); 436 437 438 439 /** 440 * Convert an object URI into a libraryID and key from the database, without relying on global state 441 * 442 * Note that while the URI must point to a valid library, the object doesn't need to exist 443 * 444 * @param {String} objectURI - Object URI 445 * @param {String} type - Object type 446 * @return {Promise<Object|FALSE>} - A promise for an object with 'objectType', 'libraryID', 'key' 447 * or FALSE if library didn't exist 448 */ 449 this._getURIObjectLibraryKeyFromDB = Zotero.Promise.coroutine(function* (objectURI, type) { 450 let uri = objectURI.replace(/\/+$/, ''); // Drop trailing / 451 let uriParts = uri.match(uriPartsRe); 452 453 if (!uriParts) { 454 throw new Error("Could not parse object URI " + uri); 455 } 456 457 let libraryID = yield this._getURIObjectLibraryID(uri); 458 if (!libraryID) { 459 return false; 460 } 461 462 let retObj = { libraryID }; 463 if (!uriParts[5]) { 464 // References the library itself 465 return false; 466 } 467 468 retObj.objectType = uriParts[5] == 'items' ? 'item' : 'collection'; 469 retObj.key = uriParts[6]; 470 471 if (type && type != retObj.objectType) return false; 472 473 return retObj; 474 }); 475 }