phpZotero.php 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561
  1. <?php
  2. /**
  3. * @version 0.2
  4. * @copyright Jeremy Boggs, Sean Takats, 2009-2011
  5. * @license http://www.gnu.org/licenses/gpl-3.0.txt
  6. * @package phpZotero
  7. */
  8. /**
  9. * Primary class for using the Zotero API.
  10. *
  11. * @package phpZotero
  12. */
  13. class phpZotero {
  14. const ZOTERO_URI = 'https://api.zotero.org/';
  15. protected $_apiKey;
  16. protected $_ch;
  17. protected $_tmpFile;
  18. protected $_status = null;
  19. protected $_defaultParams;
  20. /**
  21. * Constructor for the phpZotero object.
  22. *
  23. * @param string The private Zotero API key.
  24. */
  25. public function __construct($apiKey = null, $defaultParams = array()) {
  26. $this->_apiKey = $apiKey;
  27. $this->_defaultParams = $defaultParams;
  28. if (function_exists('curl_init')) {
  29. $this->_ch = curl_init();
  30. } else {
  31. throw new Exception("You need cURL");
  32. }
  33. }
  34. /**
  35. * Destructor, closes cURL.
  36. */
  37. public function __destruct() {
  38. curl_close($this->_ch);
  39. if(isset($this->_tmpFile)) {
  40. fclose($this->_tmpFile);
  41. }
  42. }
  43. /**
  44. * Returns a URL with cURL.
  45. *
  46. * @param string The URL.
  47. * @param string The POST or PUT body.
  48. * @param string The HTTP method to use.
  49. * @param array Headers for the HTTP request.
  50. */
  51. protected function _httpRequest($url, $body=NULL, $method='GET', $headers=array()) {
  52. $ch = $this->_ch;
  53. curl_setopt($ch, CURLOPT_URL, $url);
  54. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  55. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); //added for running locally on MAMP
  56. curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
  57. //earlier versions (prior to update support) assumed non-null $body meant POST
  58. if(!is_null($body) && ($method != 'PUT') ) {
  59. $method = 'POST';
  60. }
  61. switch($method) {
  62. case 'GET':
  63. curl_setopt($ch, CURLOPT_HTTPGET, TRUE);
  64. break;
  65. case 'POST':
  66. curl_setopt($ch, CURLOPT_POST, 1);
  67. curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
  68. curl_setopt($ch, CURLOPT_FOLLOWLOCATION, TRUE);
  69. break;
  70. case 'PUT':
  71. //PHP curl needs a tmp file from which to PUT
  72. $this->_tmpFile = tmpfile();
  73. fwrite($this->_tmpFile, $body);
  74. fseek($this->_tmpFile, 0);
  75. curl_setopt($ch, CURLOPT_PUT, 1);
  76. curl_setopt($ch, CURLOPT_INFILE, $this->_tmpFile);
  77. curl_setopt($ch, CURLOPT_INFILESIZE, strlen($body));
  78. curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
  79. break;
  80. case 'DELETE':
  81. curl_setopt($ch, CURLOPT_DELETE, 1);
  82. break;
  83. }
  84. $xml = curl_exec($ch);
  85. $this->_status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
  86. if(isset($this->_tmpFile)) {
  87. fclose($this->_tmpFile);
  88. }
  89. return $xml;
  90. }
  91. /**
  92. * Returns a Zotero API feed response.
  93. *
  94. * @param string The request.
  95. * @param array An array of parameters.
  96. * @param string The HTTP method to use.
  97. * @param array Headers for the HTTP request.
  98. */
  99. protected function _zoteroRequest($request, $parameters = array(), $body=NULL, $method='GET', $headers=array()) {
  100. $requestUri = $this->_zoteroUri($request, $parameters);
  101. if ($response = $this->_httpRequest($requestUri, $body, $method, $headers)) {
  102. return $response;
  103. }
  104. return false;
  105. }
  106. /**
  107. * Constructs a valid Zotero URI with query string.
  108. *
  109. * @param string The request path.
  110. * @param array An array of parameters
  111. * @return string A Zotero URI.
  112. */
  113. protected function _zoteroUri($request, $parameters = array())
  114. {
  115. $uri = self::ZOTERO_URI . $request;
  116. $parameters = $this->_filterParams($parameters);
  117. // If there are parameters, build a query.
  118. if (count($parameters) > 0) {
  119. $uri = $uri . '?' . http_build_query($parameters);
  120. }
  121. return $uri;
  122. }
  123. /**
  124. * Adds the API key and default parameters to the parameters not already set.
  125. *
  126. * @param array An array of parameters.
  127. * @return array
  128. */
  129. protected function _filterParams($parameters = array())
  130. {
  131. if (!isset($parameters['key']) && $this->_apiKey) {
  132. $parameters['key'] = $this->_apiKey;
  133. }
  134. foreach($this->_defaultParams as $key=>$value) {
  135. if (!isset($parameters[$key]) && isset($this->_defaultParams[$key])) {
  136. $parameters[$key] = $this->_defaultParams[$key];
  137. }
  138. }
  139. return $parameters;
  140. }
  141. /**
  142. * Gets all Zotero items for a user or group library.
  143. *
  144. * @param int The Zotero user or group ID.
  145. * @param array An optional array of parameters.
  146. * @param string The library type, users or groups
  147. */
  148. public function getItems($zoteroId, $parameters = array(), $libraryType="users") {
  149. return $this->_zoteroRequest("$libraryType/$zoteroId/items", $parameters);
  150. }
  151. /**
  152. * Gets all top-level Zotero items for a user or group.
  153. *
  154. * @param int The user or group ID.
  155. * @param array An optional array of parameters.
  156. * @param string The library type, users or groups
  157. */
  158. public function getItemsTop($zoteroId, $parameters = array(), $libraryType="users") {
  159. return $this->_zoteroRequest("$libraryType/$zoteroId/items/top", $parameters);
  160. }
  161. /**
  162. * Gets a particular Zotero item by ID.
  163. *
  164. * @param int The user or group ID.
  165. * @param string The item key.
  166. * @param array An optional array of parameters.
  167. * @param string The library type, users or groups
  168. */
  169. public function getItem($zoteroId, $itemKey, $parameters = array(), $libraryType="users") {
  170. return $this->_zoteroRequest("$libraryType/$zoteroId/items/$itemKey", $parameters);
  171. }
  172. /**
  173. * Gets a particular Zotero item by ID and etag, to check for updates
  174. *
  175. * @param int The user or group ID.
  176. * @param string The item key.
  177. * @param string The item etag.
  178. * @param array An optional array of parameters.
  179. * @param string The library type, users or groups
  180. */
  181. public function getUpdatedItemEtag($zoteroId, $itemKey, $itemEtag, $parameters = array(), $libraryType="users" ) {
  182. $headers = array("If-Match: $itemEtag");
  183. return $this->_zoteroRequest("$libraryType/$zoteroId/items/$itemKey", $parameters, null, 'GET', $headers );
  184. }
  185. /**
  186. * Gets a particular Zotero item by ID and modified date, to check for updates
  187. *
  188. * @param int The user or group ID.
  189. * @param string The item key.
  190. * @param string The header to set for If-Modified-Since, e.g. "2011-04-15T15:45:53+00:00".
  191. * @param array An optional array of parameters.
  192. * @param string The library type, users or groups
  193. */
  194. public function getUpdatedItemModified($zoteroId, $itemKey, $updated, $parameters = array(), $libraryType="users" ) {
  195. $headers = array("If-Modified-Since: $updated");
  196. return $this->_zoteroRequest("$libraryType/$zoteroId/items/$itemKey", $parameters, null, 'GET', $headers );
  197. }
  198. /**
  199. * Gets the tags associated with a given Zotero item.
  200. *
  201. * @param int The user or group ID.
  202. * @param string The item key.
  203. * @param array An optional array of parameters.
  204. * @param string The library type, users or groups
  205. */
  206. public function getItemTags($zoteroId, $itemKey, $parameters = array(), $libraryType="users") {
  207. return $this->_zoteroRequest("$libraryType/$zoteroId/items/$itemKey/tags", $parameters);
  208. }
  209. /**
  210. * Gets the children associated with a given Zotero item.
  211. *
  212. * @param int The user or group ID.
  213. * @param string The item key.
  214. * @param array An optional array of parameters.
  215. * @param string The library type, users or groups
  216. */
  217. public function getItemChildren($zoteroId, $itemKey, $parameters = array(), $libraryType="users") {
  218. return $this->_zoteroRequest("$libraryType/$zoteroId/items/$itemKey/children", $parameters);
  219. }
  220. /**
  221. * Gets the URI of a user item file.
  222. *
  223. * @param int The user or group ID.
  224. * @param string The item key.
  225. * @param array Additional parameters for the request.
  226. * @return string the file URI.
  227. * @param string The library type, users or groups
  228. */
  229. public function getItemFile($zoteroId, $itemKey, $parameters = array(), $libraryType="users") {
  230. $path = "users/$zoteroId/items/$itemKey/file";
  231. return $this->_zoteroUri($path, $parameters);
  232. }
  233. /**
  234. * Gets all the collections for a user or group.
  235. *
  236. * @param int The user or group ID.
  237. * @param array An optional array of parameters
  238. * @param string The library type, users or groups
  239. */
  240. public function getCollections($zoteroId, $parameters = array(), $libraryType="users") {
  241. return $this->_zoteroRequest("$libraryType/$zoteroId/collections", $parameters);
  242. }
  243. /**
  244. * Gets all top-level collections for a user or group.
  245. *
  246. * @param int The user or group ID.
  247. * @param array An optional array of parameters
  248. * @param string The library type, users or groups
  249. */
  250. public function getCollectionsTop($zoteroId, $parameters = array(), $libraryType="users") {
  251. return $this->_zoteroRequest("$libraryType/$zoteroId/collections/top", $parameters);
  252. }
  253. /**
  254. * Gets a specific collection for a given user or group.
  255. *
  256. * @param int The user or group ID.
  257. * @param string The collection key.
  258. * @param array An optional array of parameters.
  259. * @param string The library type, users or groups
  260. */
  261. public function getCollection($zoteroId, $collectionKey, $parameters = array(), $libraryType="users") {
  262. return $this->_zoteroRequest("$libraryType/$zoteroId/collections/$collectionKey", $parameters);
  263. }
  264. /**
  265. * Get the items in a specific collection for a given user or group.
  266. *
  267. * @param int The user or group ID.
  268. * @param string The collection key.
  269. * @param array An optional array of parameters.
  270. * @param string The library type, users or groups
  271. */
  272. public function getCollectionItems($zoteroId, $collectionKey, $parameters = array(), $libraryType="users") {
  273. return $this->_zoteroRequest("$libraryType/$zoteroId/collections/$collectionKey/items", $parameters);
  274. }
  275. /**
  276. * Gets the tags for a user or group.
  277. *
  278. * @param int The user or group ID.
  279. * @param array An optional array of parameters.
  280. * @param string The library type, users or groups
  281. */
  282. public function getTags($zoteroId, $parameters = array(), $libraryType="users") {
  283. return $this->_zoteroRequest("$libraryType/$zoteroId/tags", $parameters);
  284. }
  285. /**
  286. * Gets a specific tag for a user or group.
  287. *
  288. * @param int The user or group ID.
  289. * @param string The tag.
  290. * @param array An optional array of parameters.
  291. * @param string The library type, users or groups
  292. */
  293. public function getUserTag($zoteroId, $tag, $parameters = array(), $libraryType="users") {
  294. if($tag = urlencode($tag)) {
  295. return $this->_zoteroRequest("$libraryType/$zoteroId/tags/$tag", $parameters);
  296. }
  297. }
  298. /**
  299. * Gets the items tagged with a given tag.
  300. *
  301. * @param int The user or group ID.
  302. * @param string The tag.
  303. * @param array An optional array of parameters.
  304. * @param string The library type, users or groups
  305. */
  306. public function getTagItems($zoteroId, $tag, $parameters = array(), $libraryType="users") {
  307. if($tag = urlencode($tag)) {
  308. return $this->_zoteroRequest("$libraryType/$zoteroId/tags/$tag/items", $parameters);
  309. }
  310. }
  311. /**
  312. * Gets the groups the API key has access to
  313. *
  314. * @param int $zoteroId the user id
  315. * @param array $parameters An optional array of parameters
  316. */
  317. public function getUserGroups($zoteroId, $parameters = array()) {
  318. return $this->_zoteroRequest("users/$zoteroId/groups", $parameters);
  319. }
  320. /**
  321. * Gets a group.
  322. *
  323. * @param int The group ID.
  324. * @param array An optional array of parameters.
  325. */
  326. public function getGroup($groupId, $parameters = array())
  327. {
  328. return $this->_zoteroRequest("groups/$groupId", $parameters);
  329. }
  330. /**
  331. * Loads XML response into DOM document.
  332. *
  333. * @param string The XML response.
  334. *
  335. */
  336. public function getDom($xml) {
  337. $dom = new DOMDocument();
  338. $dom->loadXML($xml);
  339. return $dom;
  340. }
  341. /**
  342. * Gets the start page from the Zotero feed.
  343. *
  344. * @param string The DOM output.
  345. * @param string The rel attribute to find.
  346. */
  347. public function getPageStart($dom, $rel) {
  348. $xpath = new DOMXPath($dom);
  349. $xpath->registerNamespace('atom', 'http://www.w3.org/2005/Atom');
  350. $nextLink = $xpath->evaluate("//atom:link[@rel = '$rel']/@href");
  351. $nextLinkUrl = $nextLink->item(0)->nodeValue;
  352. if ($nextLinkUrl) {
  353. $start = substr(strrchr($nextLinkUrl, '='), 1);
  354. return $start;
  355. }
  356. return false;
  357. }
  358. /**
  359. * Gets the URL for the next page.
  360. *
  361. * @param string The DOM output.
  362. */
  363. public function getNextPageStart($dom) {
  364. return $this->getPageStart($dom, 'next');
  365. }
  366. /**
  367. * Gets the URL for the last page.
  368. *
  369. * @param string The DOM output.
  370. */
  371. public function getLastPageStart($dom) {
  372. return $this->getPageStart($dom, 'last');
  373. }
  374. /**
  375. * Gets the URL for the first page.
  376. *
  377. * @param string The DOM output.
  378. */
  379. public function getFirstPageStart($dom) {
  380. return $this->getPageStart($dom, 'first');
  381. }
  382. /**
  383. * Gets the total results for a specific query.
  384. *
  385. * @param string The DOM output.
  386. */
  387. public function getTotalResults($dom) {
  388. $totalResults = $dom->getElementsByTagNameNS('http://zotero.org/ns/api', 'totalResults');
  389. return $totalResults->item(0)->nodeValue;
  390. }
  391. /**
  392. * Gets the key for a specific query.
  393. *
  394. * @param string The DOM output.
  395. */
  396. public function getKey($dom) {
  397. $key = $dom->getElementsByTagNameNS('http://zotero.org/ns/api', 'key');
  398. return $key->item(0)->nodeValue;
  399. }
  400. /**
  401. * Gets all available item types.
  402. */
  403. public function getAllItemTypes() {
  404. return $this->_zoteroRequest('itemTypes', null, null);
  405. }
  406. /**
  407. * Gets all available item fields.
  408. */
  409. public function getAllItemFields() {
  410. return $this->_zoteroRequest('itemFields', null, null);
  411. }
  412. /**
  413. * Gets valid creator types for a given item type.
  414. *
  415. * @param string The item type.
  416. */
  417. public function getValidCreatorTypes($itemType) {
  418. $parameters['itemType'] = $itemType;
  419. return $this->_zoteroRequest('itemTypeCreatorTypes', $parameters, null);
  420. }
  421. /**
  422. * Gets localized creator fields.
  423. */
  424. public function getLocalizedCreatorFields() {
  425. return $this->_zoteroRequest('creatorFields', null, null);
  426. }
  427. /**
  428. * Gets a template for a given item type
  429. *
  430. * @param string The item type.
  431. */
  432. public function getItemTemplate($itemType) {
  433. $parameters['itemType'] = $itemType;
  434. return $this->_zoteroRequest('items/new', $parameters, null);
  435. }
  436. /**
  437. * Adds an item to a user or group library.
  438. *
  439. * @param int The Zotero user or group ID.
  440. * @param string The item fields, in JSON.
  441. * @param string The library type, users or groups
  442. */
  443. public function createItem($zoteroId, $itemFields) {
  444. return $this->_zoteroRequest("users/$zoteroId/items", null, $itemFields, 'POST');
  445. }
  446. /**
  447. * Updates an item
  448. *
  449. * @param int $zoteroId The Zotero user or group ID
  450. * @param string $itemFields The item fields, in JSON.
  451. * @param string $itemKey The Zotero item key for the item.
  452. * @param string $itemEtag The Zotero item etag for the item.
  453. * @param string $libraryType The library type, users or groups
  454. */
  455. public function updateItem($zoteroId, $itemFields, $itemKey, $itemEtag, $libraryType="users") {
  456. $headers = array("If-Match: $itemEtag");
  457. return $this->_zoteroRequest("$libraryType/$zoteroId/items/$itemKey", null, $itemFields, 'PUT', $headers );
  458. }
  459. /**
  460. * Deletes an item
  461. *
  462. * @param int $zoteroId The Zotero user or group ID
  463. * @param string $itemKey The Zotero item key for the item.
  464. * @param string $itemEtag The Zotero item etag for the item.
  465. * @param string $libraryType The library type, users or groups
  466. */
  467. public function deleteItem($zoteroId, $itemKey, $itemEtag, $libraryType="users") {
  468. $headers = array("If-Match: $itemEtag");
  469. return $this->_zoteroRequest("$libraryType/$zoteroId/items/$itemKey", null, $itemFields, 'DELETE', $headers );
  470. }
  471. /**
  472. * Adds items to a library collection.
  473. *
  474. * @param int The Zotero user or group ID.
  475. * @param string The collection key.
  476. * @param string A space-delimited list of item keys.
  477. * @param string The library type, users or groups
  478. */
  479. public function addItemsToCollection($zoteroId, $collectionKey, $itemKeys, $libraryType="users") {
  480. return $this->_zoteroRequest("$libraryType/$zoteroId/collections/$collectionKey/items", null, $itemKeys);
  481. }
  482. /**
  483. * Adds a collection to a user or group library.
  484. *
  485. * @param int The Zotero user or group ID.
  486. * @param string The collection fields, in JSON.
  487. * @param string The library type, users or groups
  488. */
  489. public function createCollection($zoteroId, $collectionFields, $libraryType="users") {
  490. return $this->_zoteroRequest("$libraryType/$zoteroId/collections", null, $collectionFields);
  491. }
  492. public function getResponseStatus() {
  493. return $this->_status;
  494. }
  495. public function getDefaultParam($param) {
  496. if(isset($this->_defaultParams[$param])) {
  497. return $this->_defaultParams[$param];
  498. }
  499. return FALSE;
  500. }
  501. }