PluginProbe
Zotpress / trunk
Zotpress vtrunk
7.4.4 trunk 1.0 1.1 1.2 1.3 1.4 1.5 1.6 2.0 2.1 2.2 2.3 2.4 2.5 2.5.1 2.5.2 2.6 2.6.1 3.0 3.0.1 3.0.2 3.0.3 3.0.4 3.1 All 138 releases
zotpress / lib / request / request.class.php

request.class.php in Zotpress trunk, at lib/request/request.class.php

512 lines 23.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
2
3
4 /**
5 *
6 * ZOTPRESS REQUEST CLASS
7 *
8 * Based on Sean Huber's CURL library with additions by Mike Purvis.
9 * Checks for updates every 10 minutes or based on cache timer setting.
10 *
11 * Requires: request url (e.g. https://api.zotero.org/...), api user id (can be accessed from request url)
12 * Returns: array with json and headers (json-formatted)
13 *
14 */
15
16 if ( ! class_exists('ZotpressRequest') )
17 {
18 class ZotpressRequest
19 {
20 public $update = false,
21 $request_error = false,
22 $check_every_n_mins = 10, // 10 minutes by default
23 $api_user_id,
24 $request_type = 'item';
25
26 // REVIEW: This was causing problems for some people ...
27 // Could it be how the database is set up?
28 // e.g., https://stackoverflow.com/questions/36028844/warning-gzdecode-data-error-in-php
29 function zotpress_gzdecode( $data )
30 {
31 if ( ! is_null( $data ) )
32 // Thanks to Waynn Lue (StackOverflow)
33 if ( function_exists("gzdecode") )
34 return gzdecode($data);
35 else
36 return gzinflate(substr($data,10,-8));
37 }
38
39
40 function set_request_meta( $url, $update, $request_type = 'item' )
41 {
42 $this->update = $update;
43
44 if ( $request_type != 'item' )
45 $this->request_type = $request_type;
46
47 // Get and set api user id
48 $divider = "users/";
49 if ( strpos( $url, "groups" ) !== false )
50 $divider = "groups/";
51 $temp1 = explode( $divider, $url );
52 $temp2 = explode( "/", $temp1[1] );
53 $this->api_user_id = $temp2[0];
54 }
55
56
57 // NOTE: used by shortcode.request.php
58 function get_request_cache( $url, $update, $request_type = 'item' )
59 {
60 $this->set_request_meta( $url, $update, $request_type );
61
62 $data = $this->check_and_get_cache( $url );
63
64 // Check for request errors
65 if ( $this->request_error !== false )
66 return 'Error: ' . $this->request_error; // exit();
67 else // Otherwise, return the data
68 return $data;
69 }
70
71
72 // NEW in 7.3.6: Request an update if cached version out of date
73 function get_request_update( $url, $update, $request_type = 'item' )
74 {
75 global $wpdb;
76
77 $this->set_request_meta( $url, $update, $request_type );
78
79 $data = $this->getRegular( $wpdb, $url, true );
80 $data["json"] = $data["data"];
81
82 // Check for request errors
83 if ( $this->request_error !== false )
84 return 'Error: ' . $this->request_error; // exit();
85 else // Otherwise, return the data
86 return $data;
87 }
88
89
90 function get_request_contents( $url, $update, $request_type = 'item' )
91 {
92 $this->set_request_meta( $url, $update, $request_type );
93
94 // NEW in 7.3.6: First, check the cache:
95 $data = $this->check_and_get_cache( $url );
96 $data_json = json_decode($data["json"]);
97
98 // Only try to update if time has passed:
99 $updateneeded = $data["updateneeded"];
100
101 // Then, proceed without cache if none exists;
102 // if no cache, then array returned:
103 if ( ! is_array($data_json) )
104 // if ( property_exists($data_json, "status")
105 // && $data_json->status == "No Cache" )
106 {
107 $data = $this->get_xml_data( $url, $data["updateneeded"] );
108 }
109
110 // Check for request errors
111 if ( $this->request_error !== false )
112 return 'Error: ' . $this->request_error; // exit();
113 else // Otherwise, return the data
114 return $data;
115 }
116
117
118 // Limit Zotero request calls based on elapsed time
119 function check_time( $last_time )
120 {
121 // Set time zone based on WP installation
122 // 7.4: Removing to rely on WP
123 // date_default_timezone_set( wp_timezone_string() );
124
125 // Set up the dates to compare
126 $last_time = date_create($last_time);
127 $now = date_create();
128
129 $timeElapsed = date_diff($last_time, $now);
130
131 // Convert to total minutes difference
132 $timeElapsedMin = ( $timeElapsed->y * 525600 )
133 + ( $timeElapsed->m * 43800 )
134 + ( $timeElapsed->d * 1440 )
135 + ( $timeElapsed->i )
136 + ( $timeElapsed->s * 0.0166667 );
137
138 // 7.4.4: Added cache timer
139 if ( get_option("Zotpress_DefaultCacheTimer")
140 && is_int( get_option("Zotpress_DefaultCacheTimer") )
141 && get_option("Zotpress_DefaultCacheTimer") >= 10 )
142 $this->check_every_n_mins = get_option("Zotpress_DefaultCacheTimer");
143
144 if ( $timeElapsedMin > $this->check_every_n_mins )
145 return true;
146 else // Not time yet
147 return false;
148 }
149
150
151 function check_and_get_cache( $url )
152 {
153 global $wpdb;
154
155 // First, check db to see if cached version exists
156 $zp_results = $wpdb->get_results(
157 $wpdb->prepare(
158 "
159 SELECT DISTINCT ".$wpdb->prefix."zotpress_cache.*
160 FROM ".$wpdb->prefix."zotpress_cache
161 WHERE ".$wpdb->prefix."zotpress_cache.request_id = %s
162 AND ".$wpdb->prefix."zotpress_cache.api_user_id = %s
163 ",
164 array( md5($url), $this->api_user_id )
165 ), OBJECT
166 );
167 // unset($zp_query);
168
169 $updateneeded = false;
170
171 if ( count($zp_results) != 0 )
172 {
173 // Cache exists, but is it out of date? Check:
174 if ( isset($zp_results[0]->retrieved)
175 && $this->check_time($zp_results[0]->retrieved) )
176 $updateneeded = true;
177
178 // Use the cache:
179 $json = $this->zotpress_gzdecode( $zp_results[0]->json );
180 $tags = $this->zotpress_gzdecode( $zp_results[0]->tags );
181 $headers = $zp_results[0]->headers;
182 }
183
184 else // No cache
185 {
186 // $json = json_encode( array('status' => 'No Cache') );
187 $json = wp_json_encode( array('status' => 'No Cache') );
188 $tags = false;
189 $headers = false;
190 }
191
192 $wpdb->flush();
193
194 return array(
195 "json" => $json,
196 "tags" => $tags,
197 "headers" => $headers,
198 "updateneeded" => $updateneeded );
199 }
200
201
202 function get_xml_data( $url, $updateneeded=false )
203 {
204 global $wpdb;
205
206 // Just want to check for cached version
207 if ( $this->update === false )
208 {
209 // First, check db to see if cached version exists
210 $zp_results = $wpdb->get_results(
211 $wpdb->prepare(
212 "
213 SELECT DISTINCT ".$wpdb->prefix."zotpress_cache.*
214 FROM ".$wpdb->prefix."zotpress_cache
215 WHERE ".$wpdb->prefix."zotpress_cache.request_id = %s
216 AND ".$wpdb->prefix."zotpress_cache.api_user_id = %s
217 ",
218 array( md5($url), $this->api_user_id )
219 ), OBJECT
220 );
221
222 // Cache exists
223 if ( count($zp_results) > 0 )
224 {
225 $json = $this->zotpress_gzdecode($zp_results[0]->json);
226 $tags = $this->zotpress_gzdecode($zp_results[0]->tags);
227 $headers = $zp_results[0]->headers;
228 }
229
230 else // No cached
231 {
232 $regular = $this->getRegular( $wpdb, $url );
233
234 $json = $regular['data'];
235 $tags = $regular['tags'];
236 $headers = $regular['headers'];
237 }
238
239 $wpdb->flush();
240 }
241
242 else // Normal or RIS
243 {
244 $regular = $this->getRegular( $wpdb, $url );
245
246 $json = $regular['data'];
247 $tags = $regular['tags'];
248 $headers = $regular['headers'];
249 }
250
251 return array( "json" => $json, "tags" => $tags, "headers" => $headers, "updateneeded" => $updateneeded );
252 }
253
254
255 function getRegular( $wpdb, $url )
256 {
257 global $wpdb;
258
259 // First, check db to see if cached version exists
260 $zp_results = $wpdb->get_results(
261 $wpdb->prepare(
262 "
263 SELECT DISTINCT ".$wpdb->prefix."zotpress_cache.*
264 FROM ".$wpdb->prefix."zotpress_cache
265 WHERE ".$wpdb->prefix."zotpress_cache.request_id = %s
266 AND ".$wpdb->prefix."zotpress_cache.api_user_id = %s
267 ",
268 array( md5($url), $this->api_user_id )
269 ), OBJECT
270 );
271
272 // Then, if no cached version, proceed and save one.
273 // Or, if cached version exists, check to see if it's out of date,
274 // and return whichever is newer (and cache the newest).
275 // if ( count($zp_results) == 0
276 // || ( property_exists($zp_results[0], 'retrieved') && $zp_results[0]->retrieved !== null
277 // && $this->check_time($zp_results[0]->retrieved) ) )
278 // {
279 if ( count($zp_results) == 0
280 || ( isset($zp_results[0]->retrieved)
281 && $this->check_time($zp_results[0]->retrieved) ) )
282 {
283 $headers_arr = array ( "Zotero-API-Version" => "3" );
284
285 if ( count($zp_results) > 0 )
286 $headers_arr["If-Modified-Since-Version"] = $zp_results[0]->libver;
287
288 // Get response
289 $response = wp_remote_get( $url, array ( 'headers' => $headers_arr ) );
290
291 if ( is_wp_error($response) )
292 $this->request_error = $response->get_error_message();
293 // 7.3.13: No collection and tag error reporting:
294 else if ( $response["body"] == "Collection not found"
295 || $response["body"] == "Tag not found" )
296 $this->request_error = $response["body"];
297 else
298 // $headers = json_encode( wp_remote_retrieve_headers( $response )->getAll() );
299 $headers = wp_json_encode( wp_remote_retrieve_headers( $response )->getAll() );
300 }
301
302 if ( ! $this->request_error )
303 {
304 // Proceed if no cached version or to check server for newer
305 if ( count($zp_results) == 0
306 || ( isset($response["response"]["code"])
307 && $response["response"]["code"] != "304" ) )
308 {
309 // Deal with errors
310 if ( is_wp_error($response)
311 || ! isset($response['body']) )
312 {
313 $this->request_error = $response->get_error_message();
314
315 if ( $response->get_error_code() == "http_request_failed" )
316 {
317 // Try again with less restrictions
318 add_filter('https_ssl_verify', '__return_false');
319 $response = wp_remote_get( $url, array( 'headers' => array("Zotero-API-Version" => "2") ) );
320
321 if (is_wp_error($response) || ! isset($response['body'])) {
322 $this->request_error = $response->get_error_message();
323 } elseif ($response == "An error occurred" || ( isset($response['body']) && $response['body'] == "An error occurred")) {
324 $this->request_error = "WordPress was unable to import from Zotero. This is likely caused by an incorrect citation style name. For example, 'mla' is now 'modern-language-association'. Use the name found in the style's URL at the Zotero Style Repository.";
325 } else // no errors this time
326 {
327 $this->request_error = false;
328 }
329 }
330 }
331
332 elseif ( $response == "An error occurred"
333 || ( isset($response['body'])
334 && $response['body'] == "An error occurred") )
335 {
336 $this->request_error = "WordPress was unable to import from Zotero. This is likely caused by an incorrect citation style name. For example, 'mla' is now 'modern-language-association'. Use the name found in the style's URL at the Zotero Style Repository.";
337 }
338
339 // Then, get actual data
340 $data = wp_remote_retrieve_body( $response ); // Thanks to Trainsmart.com developer!
341
342 // Make sure tags didn't return an error -- redo if so
343 if ( $data == "Tag not found" )
344 {
345 $url_break = explode("/", $url);
346 $url = $url_break[0]."//".$url_break[2]."/".$url_break[3]."/".$url_break[4]."/".$url_break[7];
347 $url = str_replace("=50", "=5", $url);
348
349 $data = $this->get_xml_data( $url );
350 }
351
352 // Add or update cache, if not attachment, etc.
353 if ( isset($response["headers"]["last-modified-version"]) )
354 {
355 if ( $this->request_type != 'ris' )
356 {
357 $data = json_decode($data); // will become 'json'
358 $tags = array(); // empty for now; by item key later
359
360 // If not array, turn into one for simplicity
361 if ( ! is_array($data) ) $data = array($data);
362
363 // Remove unncessary details
364 // REVIEW: Does this account for all unused metadata? Depends on item type ...
365 foreach( $data as $id => $item )
366 {
367 if ( property_exists($data[$id], 'version') ) unset($data[$id]->version);
368 if ( property_exists($data[$id], 'links') ) unset($data[$id]->links);
369
370 if ( property_exists($data[$id], 'library') )
371 {
372 if ( property_exists($data[$id]->library, 'type') ) unset($data[$id]->library->type);
373 if ( property_exists($data[$id]->library, 'name') ) unset($data[$id]->library->name);
374 if ( property_exists($data[$id]->library, 'links') ) unset($data[$id]->library->links);
375 }
376 if ( property_exists($data[$id], 'data') )
377 {
378 if ( property_exists($data[$id]->data, 'key') ) unset($data[$id]->data->key);
379 if ( property_exists($data[$id]->data, 'version') ) unset($data[$id]->data->version);
380 if ( property_exists($data[$id]->data, 'series') ) unset($data[$id]->data->series);
381 if ( property_exists($data[$id]->data, 'seriesNumber') ) unset($data[$id]->data->seriesNumber);
382 if ( property_exists($data[$id]->data, 'seriesTitle') ) unset($data[$id]->data->seriesTitle);
383 if ( property_exists($data[$id]->data, 'seriesText') ) unset($data[$id]->data->seriesText);
384 if ( property_exists($data[$id]->data, 'publicationTitle') ) unset($data[$id]->data->publicationTitle);
385 if ( property_exists($data[$id]->data, 'journalAbbreviation') ) unset($data[$id]->data->journalAbbreviation);
386 if ( property_exists($data[$id]->data, 'issue') ) unset($data[$id]->data->issue);
387 if ( property_exists($data[$id]->data, 'volume') ) unset($data[$id]->data->volume);
388 if ( property_exists($data[$id]->data, 'numberOfVolumes') ) unset($data[$id]->data->numberOfVolumes);
389 if ( property_exists($data[$id]->data, 'edition') ) unset($data[$id]->data->edition);
390 if ( property_exists($data[$id]->data, 'place') ) unset($data[$id]->data->place);
391 if ( property_exists($data[$id]->data, 'publisher') ) unset($data[$id]->data->publisher);
392 if ( property_exists($data[$id]->data, 'pages') ) unset($data[$id]->data->pages);
393 if ( property_exists($data[$id]->data, 'numPages') ) unset($data[$id]->data->numPages);
394 if ( property_exists($data[$id]->data, 'shortTitle') ) unset($data[$id]->data->shortTitle);
395 if ( property_exists($data[$id]->data, 'accessDate') ) unset($data[$id]->data->accessDate);
396 if ( property_exists($data[$id]->data, 'archive') ) unset($data[$id]->data->archive);
397 if ( property_exists($data[$id]->data, 'archiveLocation') ) unset($data[$id]->data->archiveLocation);
398 if ( property_exists($data[$id]->data, 'libraryCatalog') ) unset($data[$id]->data->libraryCatalog);
399 if ( property_exists($data[$id]->data, 'callNumber') ) unset($data[$id]->data->callNumber);
400 if ( property_exists($data[$id]->data, 'rights') ) unset($data[$id]->data->rights);
401 if ( property_exists($data[$id]->data, 'extra') ) unset($data[$id]->data->extra);
402 if ( property_exists($data[$id]->data, 'relations') ) unset($data[$id]->data->relations);
403 if ( property_exists($data[$id]->data, 'dateAdded') ) unset($data[$id]->data->dateAdded);
404 if ( property_exists($data[$id]->data, 'websiteTitle') ) unset($data[$id]->data->websiteTitle);
405 if ( property_exists($data[$id]->data, 'websiteType') ) unset($data[$id]->data->websiteType);
406 if ( property_exists($data[$id]->data, 'inPublications') ) unset($data[$id]->data->inPublications);
407 if ( property_exists($data[$id]->data, 'presentationType') ) unset($data[$id]->data->presentationType);
408 if ( property_exists($data[$id]->data, 'meetingName') ) unset($data[$id]->data->meetingName);
409 }
410
411 // As of 7.1.4, tags are saved separately
412 // due to possibily large quantities and the
413 // limits of blob; so we always save now
414 // REVIEW: Do we need the account, too?
415
416 // 7.4 Update: Might not exist
417 if ( isset($item->key) )
418 $tags[$item->key] = "";
419
420 if ( property_exists($data[$id], 'data')
421 && property_exists($data[$id]->data, 'tags') )
422 {
423 $tags[$item->key] = $data[$id]->data->tags;
424 unset($data[$id]->data->tags);
425 }
426 }
427
428 $json = wp_json_encode($data);
429 $tags = wp_json_encode($tags);
430 // $json = json_encode($data);
431 // $tags = json_encode($tags);
432
433 $wpdb->query(
434 $wpdb->prepare(
435 "
436 INSERT INTO ".$wpdb->prefix."zotpress_cache
437 ( request_id, api_user_id, json, tags, headers, libver, retrieved )
438 VALUES ( %s, %s, %s, %s, %s, %d, %s )
439 ON DUPLICATE KEY UPDATE
440 json = VALUES(json),
441 tags = VALUES(tags),
442 headers = VALUES(headers),
443 libver = VALUES(libver),
444 retrieved = VALUES(retrieved)
445 ",
446 array
447 (
448 md5( $url ),
449 $this->api_user_id,
450 gzencode($json),
451 gzencode($tags), // 7.1.4: separated from $data
452 $headers,
453 $response["headers"]["last-modified-version"],
454 gmdate('m/d/Y h:i:s a')
455 ))
456 );
457 }
458
459 else // assume 'ris'
460 {
461 // REVIEW: Eventually cache?
462 // NOTE: $data is everything / the RIS
463 $json = $data;
464 $tags = false;
465 // $headers = $response["headers"];
466 }
467 }
468
469 else
470 {
471 // If not an item, e.g., if attachment, PDF, etc.
472 $json = $data;
473 $tags = false;
474 $headers = $response["headers"];
475 }
476 }
477
478 // Retrieve cached version
479 else
480 {
481 // Reset retrieved datetime:
482 $wpdb->query(
483 $wpdb->prepare(
484 "
485 INSERT INTO ".$wpdb->prefix."zotpress_cache
486 ( request_id, api_user_id, retrieved )
487 VALUES ( %s, %s, %s )
488 ON DUPLICATE KEY UPDATE
489 retrieved = VALUES(retrieved)
490 ",
491 array
492 (
493 md5( $url ),
494 $this->api_user_id,
495 gmdate('m/d/Y h:i:s a')
496 ))
497 );
498
499 $json = $this->zotpress_gzdecode($zp_results[0]->json);
500 $tags = $this->zotpress_gzdecode($zp_results[0]->tags);
501 $headers = $zp_results[0]->headers;
502 }
503 }
504
505 $wpdb->flush();
506
507 return array( "data" => $json, "tags" => $tags, "headers" => $headers );
508 }
509 }
510 }
511
512 ?>