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 / shortcode / shortcode.request.php

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

1,169 lines 39.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 * Processes the Zotero request. Accepts WP AJAX or PHP calls.
6 *
7 * Used by: shortcode.php
8 *
9 * @param bol $is_ajax Whether it's an WP AJAX call.
10 *
11 * @return str JSON with: (a) meta about request, and (b) all data for this request.
12 */
13 function Zotpress_shortcode_request( $zpr=false, $checkcache=false )
14 {
15 $is_ajax = isset($_GET['zpShortcode_nonce']);
16
17 if ( $is_ajax )
18 check_ajax_referer( 'zpShortcode_nonce_val', 'zpShortcode_nonce' );
19
20 // Set up database
21 global $wpdb, $post;
22
23 // Prep request vars
24 if ( $zpr === false || $zpr == '' )
25 $zpr = Zotpress_prep_ajax_request_vars($wpdb);
26
27 // Include relevant classes and functions
28 include( dirname(__FILE__) . '/../request/request.class.php' );
29 include( dirname(__FILE__) . '/../request/request.functions.php' );
30
31 // Set up request queue (for items)
32 // Structure: [api_user_id] => [items], [requests]
33 $zp_request_queue = array();
34
35 // Set up Zotpress request
36 $zp_import_contents = new ZotpressRequest();
37
38 // Set up request meta
39 $zp_request_meta = array( "request_last" => (int) $zpr["request_last"], "request_next" => 0 );
40
41 // Set up data variable
42 $zp_all_the_data = array();
43
44
45
46 // +---------------------------+
47 // | Format Zotero request URL |
48 // +---------------------------+
49
50 // Account for items + collection_id
51 if ( $zpr["item_type"] == "items"
52 && $zpr["collection_id"] !== false )
53 {
54 $zpr["item_type"] = "collections";
55 $zpr["sub"] = "items";
56 $zpr["get_top"] = false;
57 }
58
59 // Account for items + zp_tag_id
60 if ( $zpr["item_type"] == "items"
61 && $zpr["tag_id"] !== false )
62 $zpr["get_top"] = false;
63
64 // Account for collection_id + get_top
65 if ( $zpr["get_top"]
66 && $zpr["collection_id"] !== false )
67 {
68 $zpr["get_top"] = false;
69 $zpr["sub"] = "collections";
70 }
71
72 // Account for tag display - let's limit it
73 if ( $zpr["is_dropdown"] === true
74 && $zpr["item_type"] == "tags" )
75 {
76 $zpr["sortby"] = "numItems"; // title
77 $zpr["order"] = "desc"; // asc
78 $zpr["limit"] = 100; if ( $zpr["maxtags"] ) $zpr["limit"] = $zpr["maxtags"];
79 $zpr["overwrite_last_request"] = $zpr["limit"]; // QUESTION: Unsure
80 $zpr["overwrite_request"] = true;
81
82 // REVIEW: we don't want tags within a collection,
83 // accounting for Library Dropdown browse bar (7.3.1.1)
84 // $zpr["get_top"] = false;
85 // $zpr["collection_id"] = false;
86 }
87
88 // Account for $zpr["maxresults"]
89 // TODO: The Help says this is only for the searchbar ..........
90 // TEST: Isn't this only for the library?
91 if ( $zpr["is_dropdown"] === true
92 && $zpr["maxresults"] )
93 {
94 // If 50 or less, set as limit
95 if ( (int) $zpr["maxresults"] <= 50 )
96 {
97 $zpr["limit"] = $zpr["maxresults"];
98 $zpr["overwrite_request"] = true;
99 }
100
101 // If over 50, then overwrite last_request
102 else
103 {
104 $zpr["overwrite_last_request"] = $zpr["maxresults"];
105 }
106 }
107
108
109 // Handle the possible formats of item/s for bib and in-text
110 // REVIEW: Actually, removed page numbers (if any) in shortcode.intext.php
111 //
112 // IN-TEXT FORMATS:
113 // [zotpressInText item="NCXAA92F"]
114 // [zotpressInText item="{NCXAA92F}"]
115 // [zotpressInText item="{NCXAA92F,10-15}"]
116 // [zotpressInText items="{NCXAA92F,10-15},{55MKF89B,1578},{3ITTIXHP}"]
117 // [zotpressInText items="{000001:NCXAA92F,10-15},{000003:3ITTIXHP}"]
118 // [zotpressInText items="{000001:NCXAA92F,10-15},{000003:3ITTIXHP,(33,36)}"]
119 // So no multiples without curlies or non-curlies in multiples
120 //
121 // BIB FORMATS:
122 // [zotpress item="GMGCJU34"]
123 // [zotpress items="GMGCJU34,U9Z5JTKC"]
124 // [zotpress item="{000001:XH4BS8MA},{000001:CN73PTWE},{000003:CZR96TX9}"]
125 // BASICALLY: Create a list of unique item keys per API User ID
126 // REVIEW: Deal with everything!!!!!!! In the new format
127 if ( $zpr["item_key"]
128 && strpos( $zpr["item_key"], ":" ) !== false )
129 {
130 $zp_items = preg_split( "/((;)|(,))+/", $zpr["item_key"] );
131
132 foreach ( $zp_items as $id => $zp_item_data )
133 {
134 $zp_item = explode( ":", substr( $zp_item_data, 1, -1 ) );
135
136 // Create items queue for API User ID, if it doesn't exist
137 if ( ! array_key_exists( $zp_item[0], $zp_request_queue ) )
138 $zp_request_queue[$zp_item[0]]["items"] = "";
139
140 // Add item if not in queue
141 if ( strpos( $zp_request_queue[$zp_item[0]]["items"], $zp_item[1] ) === false )
142 {
143 $temp_item_key = $zp_item[1];
144
145 if ( strlen( $zp_request_queue[$zp_item[0]]["items"] ) != 0 )
146 $temp_item_key = ",".$temp_item_key;
147
148 $zp_request_queue[$zp_item[0]]["items"] .= $temp_item_key;
149 }
150 }
151 }
152
153
154
155 // +--------------------+
156 // | Build request URLs |
157 // +--------------------+
158
159 if ( $zp_request_queue !== [] )
160 {
161 // REVIEW: Does setting $zp_request_queue here overwrite it for each account?
162 foreach ( $zp_request_queue as $api_user_id => $zp_request_data )
163 $zp_request_queue = Zotpress_prep_request_URL( $wpdb, $zpr, $zp_request_queue, $api_user_id, $zp_request_data );
164 }
165 else
166 {
167 $zp_request_queue = Zotpress_prep_request_URL( $wpdb, $zpr, $zp_request_queue );
168 }
169
170
171
172
173 // +---------+
174 // | TESTING |
175 // +---------+
176
177 // var_dump($zp_request_queue);exit;
178
179 // if ( $zpr["request_start"] == 50 ) {
180 // var_dump("shortcode.request.php TESTING: ");
181 // print_r($_GET); var_dump("<br /><br />url: ".$zp_import_url);
182 // var_dump(" AFTER \n\n");
183 // }
184
185
186
187
188
189
190 // +------------------+
191 // | Request the data |
192 // +------------------+
193
194 $zp_request = array();
195 $zp_error = false;
196 $zp_usecache = false;
197 $zp_updateneeded = false;
198
199 // Account for missing/unsynced accounts or no requests
200 if ( $zp_request_queue === false )
201 $zp_error = "Zotpress account not found.";
202
203 // New in 12.3.13: Check for missing data
204 // if ( $zp_import_contents->request_error )
205
206
207 if ( ! $zp_error )
208 {
209 foreach ( $zp_request_queue as $zp_request_account )
210 {
211 // Multiple/more than one request
212 if ( count($zp_request_account["requests"]) > 1 )
213 {
214 foreach ( $zp_request_account["requests"] as $zp_request_url )
215 {
216 // Check the cache with PHP
217 if ( $checkcache && ! $zpr["request_update"] )
218 {
219 $zp_checkcache = $zp_import_contents->get_request_cache( $zp_request_url, $zpr["update"] );
220 $zp_checkcache_json = json_decode( $zp_checkcache['json'] );
221
222 if ( gettype($zp_checkcache_json) != 'array'
223 && property_exists($zp_checkcache_json, 'status')
224 && $zp_checkcache_json->status == 'No Cache' )
225 {
226 // $zp_usecache = false;
227 }
228 else // Continue as normal with cache
229 {
230 $zp_imported = $zp_checkcache;
231 $zp_usecache = true;
232 }
233 }
234 else // Otherwise, assume JS Ajax
235 {
236 $zp_imported = $zp_import_contents->get_request_contents( $zp_request_url, $zpr["update"] );
237
238 if ( $zp_imported["updateneeded"] )
239 $zp_updateneeded = true;
240 }
241
242 // Stop and let JS Ajax take over
243 if ( $checkcache && ! $zp_usecache )
244 continue;
245
246 // Deal with possible errors
247 if ( gettype($zp_imported) == "string"
248 && substr($zp_imported, 0, 5) == "Error" )
249 {
250 $zp_error = substr($zp_imported, 7, -1);
251 continue;
252 }
253
254 // Create all-requests json if doesn't exists
255 if ( empty($zp_request) )
256 $zp_request = $zp_imported;
257
258 // Add to existing all-requests json
259 $zp_request["json"] = rtrim($zp_request["json"], "]") . "," . $zp_imported["json"] . "]";
260 }
261 }
262
263 // Just one request
264 else
265 {
266 // First, check the cache with PHP
267 if ( $checkcache
268 && ! $zpr["request_update"] )
269 {
270 $zp_checkcache = $zp_import_contents->get_request_cache( $zp_request_account["requests"][0], $zpr["update"] );
271 $zp_checkcache_json = json_decode( $zp_checkcache['json'], false );
272
273 if ( gettype($zp_checkcache_json) != 'array'
274 && property_exists($zp_checkcache_json, 'status')
275 && $zp_checkcache_json->status == 'No Cache' )
276 {
277 // $zp_usecache = false;
278 }
279 else // Continue as normal with cache
280 {
281 $zp_imported = $zp_checkcache;
282 $zp_usecache = true;
283 }
284
285 // if ( $zp_checkcache["updateneeded"] )
286 // $zp_updateneeded = true;
287
288 }
289 else if ( $zpr["request_update"] )
290 {
291 $zp_imported = $zp_import_contents->get_request_update( $zp_request_account["requests"][0], $zpr["update"] );
292 }
293 else // Otherwise, assume JS AJAX
294 {
295 $zp_imported = $zp_import_contents->get_request_contents( $zp_request_account["requests"][0], $zpr["update"] );
296
297 if ( isset($zp_imported["updateneeded"]) )
298 $zp_updateneeded = true;
299 }
300
301 // Stop and let JS Ajax take over
302 if ( ( $checkcache
303 && ! $zp_usecache ) )
304 continue;
305
306 // Deal with possible error
307 if ( gettype($zp_imported) == "string"
308 && substr($zp_imported, 0, 5) == "Error" )
309 {
310 $zp_error = substr($zp_imported, 7, -1);
311 }
312
313 // Create all-requests json if doesn't exists
314 else
315 {
316 if ( empty($zp_request) )
317 {
318 $zp_request = $zp_imported;
319 }
320
321 else // Add to existing all-requests json
322 {
323 $zp_request["json"] = rtrim($zp_request["json"], "]") . "," . ltrim($zp_imported["json"], "[") . "]";
324 }
325 }
326
327 // 7.4 Update: Might not exist?
328 if ( ! isset($zp_request["json"]) )
329 $zp_error = "JSON not found";
330 elseif ( isset($zp_request["json"]) && $zp_request["json"] == "Not found" )
331 $zp_error = $zp_request["json"];
332
333 // } elseif ( empty($zp_request) ) {
334 // $zp_request = $zp_imported;
335 // } else // Add to existing all-requests json
336 // {
337 // $zp_request["json"] = rtrim($zp_request["json"], "]") . "," . ltrim($zp_imported["json"], "[") . "]";
338 // }
339 } // Just one request
340 } // Request the data (foreach)
341 } // If no error
342
343 // Fix formatting quirk
344 if ( ( ! $checkcache && ! $zp_error )
345 || ( $checkcache && $zp_usecache && ! $zp_error ) )
346 $zp_request["json"] = str_replace("}}]]", "}}]", $zp_request["json"]);
347
348 if ( ( ! $checkcache && ( ! $zp_error && $zp_request["json"] != "0" ) )
349 || ( $checkcache && $zp_usecache && ( ! $zp_error && $zp_request["json"] != "0" ) ) )
350 {
351 // Decode the JSONs
352 // Thanks to Adnreea Onica @ StackOverflow
353 $temp_headers = json_decode( $zp_request["headers"] );
354 // $temp_headers = json_encode( (array)$zp_request["headers"] );
355 // $temp_headers = json_decode( str_replace('\u0000*\u0000','', $temp_headers) );
356
357 // $temp_data = json_encode( (array)$zp_request["json"] );
358 // $temp_data = json_decode( str_replace('\u0000*\u0000','', $temp_data) );
359 $temp_data = json_decode( $zp_request["json"] );
360
361 // Figure out if there's multiple requests and how many
362 // if ( $zpr["request_start"] == 0
363 // && ( property_exists($temp_headers, 'link')
364 // && $temp_headers->link !== null )
365 // && strpos( $temp_headers->link, 'rel="last"' ) !== false )
366
367 if ( $zpr["request_start"] == 0
368 && isset( $temp_headers->link )
369 && strpos( $temp_headers->link, 'rel="last"' ) !== false )
370 {
371 $temp_link = explode( ";", $temp_headers->link );
372 $temp_link = explode( "start=", $temp_link[1] );
373 $temp_link = explode( "&", $temp_link[1] );
374
375 // // FIX: Accounted for limit ...?
376 // if ( $zpr["limit"] ) {
377 // $zp_request_meta["request_last"] = (int) $zpr["limit"];
378 // }
379 // else {
380 $zp_request_meta["request_last"] = (int) $temp_link[0];
381 // }
382 }
383
384 // Figure out the next starting position for the next request, if any
385 // 7.3.3: Changed from >= to >
386 if ( $zp_request_meta["request_last"] >= ($zpr["request_start"] + $zpr["limit"]) ) {
387
388 // 7.3.9: Only if next is greater than limit
389 $zp_request_meta["request_next"] = $zpr["request_start"] + $zpr["limit"];
390 }
391
392 // Overwrite request if limit
393 // 7.3.3: Fix for collections?
394 if ( ( $zpr["item_type"] == "items" || $zpr["item_type"] == "collections" )
395 && $zpr["overwrite_request"] === true )
396 {
397 $zp_request_meta["request_next"] = 0;
398 $zp_request_meta["request_last"] = 0;
399 }
400
401 // Overwrite last_request
402 if ( $zpr["overwrite_last_request"] )
403 {
404 // Make sure it's less than the total available items
405 if ( isset( $temp_headers->{"total-results"} )
406 && $temp_headers->{"total-results"} < $zpr["overwrite_last_request"] )
407 $zpr["overwrite_last_request"] = (int) (ceil( (int) $temp_headers->{"total-results"} / $zpr["limit"] ) - 1) * $zpr["limit"];
408 else
409 $zpr["overwrite_last_request"] = (int) ceil( $zpr["overwrite_last_request"] / $zpr["limit"] ) * $zpr["limit"];
410
411 $zp_request_meta["request_last"] = $zpr["overwrite_last_request"];
412 }
413
414
415 // +-----------------+
416 // | Format the data |
417 // +-----------------+
418
419 if ( count($temp_data) > 0 )
420 {
421 // If single, place the object into an array
422 if ( gettype($temp_data) == "object" )
423 {
424 $temp = $temp_data;
425 $temp_data = array();
426 $temp_data[0] = $temp;
427 }
428
429 // Set up conditional vars
430 if ( $zpr["shownotes"] ) $zp_notes_num = 1;
431 if ( $zpr["showimage"] ) $zp_showimage_keys = "";
432
433 // Get individual items
434 foreach ( $temp_data as $item )
435 {
436 // Set target for links
437 $zp_target_output = "";
438 if ( $zpr["target"] )
439 $zp_target_output = "target='_blank' ";
440
441 // Author filtering: skip non-matching authors
442 // TODO: Breaking with multi name
443 // EVENTUAL TODO: Zotero API 3 searches title and author, so wrong authors can appear
444 if ( $zpr["author"]
445 && count($item->data->creators) > 0 )
446 {
447 $zp_authors_check = false;
448
449 // 7.3.10: CHECK: Have to replace the apostrophe entity ...
450 $zpr["author"] = str_replace('#039;', "'", $zpr["author"]);
451
452 // Deal with multiple authors
453 if ( gettype($zpr["author"]) != "array"
454 && strpos($zpr["author"], ",") !== false )
455 {
456 $zp_authors = explode( ",", $zpr["author"] );
457
458 foreach ( $zp_authors as $author )
459 if ( zotpress_check_author_continue( $item, $author ) === true )
460 $zp_authors_check = true;
461 }
462
463 // Single author or inclusive
464 else
465 {
466 if ( $zpr["inclusive"] === false )
467 {
468 // 7.3.10: CHECK: Why is this assuming 1? Setting to 0:
469 $author_exists_count = 0;
470
471 // 7.3.10: CHECK: Not always an array ...
472 if ( is_array($zpr["author"]) ) {
473
474 foreach ( $zpr["author"] as $author )
475 if ( zotpress_check_author_continue( $item, $author ) === true )
476 $author_exists_count++;
477
478 // if ( $author_exists_count == count($zpr["author"]) +1 )
479 if ( $author_exists_count == count($zpr["author"]) )
480 $zp_authors_check = true;
481 }
482 else { // Just a string/single author
483
484 if ( zotpress_check_author_continue( $item, $zpr["author"] ) === true )
485 $zp_authors_check = true;
486 }
487
488 }
489 else // inclusive and single
490 {
491 if ( zotpress_check_author_continue( $item, $zpr["author"] ) === true )
492 $zp_authors_check = true;
493 }
494
495 // } elseif (zotpress_check_author_continue( $item, $zpr["author"] ) === true) {
496 // $zp_authors_check = true;
497 // }
498 }
499
500 if ( $zp_authors_check === false )
501 continue;
502 } // author
503
504 // Year filtering: skip non-matching years
505 if ( $zpr["year"]
506 && property_exists($item->meta, "parsedDate") )
507 {
508 // multiple
509 if ( strpos($zpr["year"], ",") !== false )
510 {
511 $zp_years_check = false;
512 $zp_years = explode( ",", $zpr["year"] );
513
514 foreach ( $zp_years as $year )
515 if ( zotpress_get_year( $item->meta->parsedDate ) == $year )
516 $zp_years_check = true;
517
518 if ( ! $zp_years_check )
519 continue;
520 }
521 else // single
522 {
523 if ( zotpress_get_year( $item->meta->parsedDate ) != $zpr["year"] )
524 continue;
525 }
526 // } elseif (zotpress_get_year( $item->meta->parsedDate ) != $zpr["year"]) {
527 // continue;
528 // }
529 }
530
531 // Skip non-matching years for author-year pairs
532 // if ( $zpr["year"] && $zpr["author"] && (property_exists($item->meta, 'parsedDate') && $item->meta->parsedDate !== null) && zotpress_get_year( $item->meta->parsedDate ) != $zpr["year"] )
533 if ( $zpr["year"]
534 && $zpr["author"]
535 && isset($item->meta->parsedDate) )
536 continue;
537
538 // Add item key for show image
539 if ( $zpr["showimage"] ) $zp_showimage_keys .= " ".$item->key;
540
541 // Modify style based on language
542 // Languages: jp
543 if ( isset($item->data->language)
544 // && $item->data->language !== null
545 // && $item->data->language != ""
546 && $item->data->language == "ja" )
547 {
548 // Change ", and " to comma
549 $item->bib = str_ireplace(", and ", ", ", $item->bib);
550
551 // Remove "In "
552 $item->bib = str_ireplace("In ", "", $item->bib);
553 }
554
555 // Hyperlink or URL Wrap
556 if ( isset($item->data->url)
557 // && $item->data->url !== null
558 && strlen($item->data->url) > 0 )
559 {
560 if ( $zpr["urlwrap"]
561 && $zpr["urlwrap"] == "title"
562 && $item->data->title )
563 {
564 // First: Get rid of text URL if it appears as text in the citation:
565 // REVIEW: Does this account for all citation styles?
566 /* chicago-author-date */ $item->bib = str_ireplace( htmlentities($item->data->url."."), "", $item->bib ); // Note the period
567 /* APA */ $item->bib = str_ireplace( htmlentities($item->data->url), "", $item->bib );
568
569 // 7.3.14 (@timtom): Fix APA citation by removing the last part of the "Retrieved from" text
570 $item->bib = preg_replace('/(Retrieved \w+ \d+, \d+)(, from)/','${1}.',$item->bib);
571
572 $item->bib = str_ireplace( " Retrieved from ", "", $item->bib );
573 $item->bib = str_ireplace( " Available from: ", "", $item->bib );
574
575
576 // Next, get rid of double space characters (two space characters next to each other):
577 $item->bib = preg_replace( '/&#xA0;/', ' ', preg_replace( '/[[:blank:]]+/', ' ', $item->bib ) );
578 $item->data->title = preg_replace( '/&#xA0;/', ' ', preg_replace( '/[[:blank:]]+/', ' ', $item->data->title ) );
579
580
581 // Next, replace space entities with real spaces:
582 $item->bib = str_ireplace("&nbsp;", " ", $item->bib );
583 $item->data->title = str_ireplace("&nbsp;", " ", $item->data->title );
584
585
586 // Next, replace entity quotes:
587 $item->bib = str_ireplace( "&ldquo;", "&quot;",
588 str_ireplace( "&rdquo;", "&quot;",
589 htmlentities(
590 html_entity_decode( $item->bib, ENT_QUOTES, "UTF-8" ),
591 ENT_QUOTES,
592 "UTF-8"
593 )
594 )
595 );
596 $item->data->title = str_ireplace( "&ldquo;", "&quot;",
597 str_ireplace( "&rdquo;", "&quot;",
598 htmlentities(
599 html_entity_decode( $item->data->title, ENT_QUOTES, "UTF-8" ),
600 ENT_QUOTES,
601 "UTF-8"
602 )
603 )
604 );
605
606
607 // Next, replace special Word characters:
608 // Thanks to Walter Tross @ Stack Overflow; CC BY-SA 3.0: https://creativecommons.org/licenses/by-sa/3.0/
609 $chr_map = array(
610 "\xC2\x82" => "'", // U+0082U+201A single low-9 quotation mark
611 "\xC2\x84" => '"', // U+0084U+201E double low-9 quotation mark
612 "\xC2\x8B" => "'", // U+008BU+2039 single left-pointing angle quotation mark
613 "\xC2\x91" => "'", // U+0091U+2018 left single quotation mark
614 "\xC2\x92" => "'", // U+0092U+2019 right single quotation mark
615 "\xC2\x93" => '"', // U+0093U+201C left double quotation mark
616 "\xC2\x94" => '"', // U+0094U+201D right double quotation mark
617 "\xC2\x9B" => "'", // U+009BU+203A single right-pointing angle quotation mark
618 "\xC2\xAB" => '"', // U+00AB left-pointing double angle quotation mark
619 "\xC2\xBB" => '"', // U+00BB right-pointing double angle quotation mark
620 "\xE2\x80\x98" => "'", // U+2018 left single quotation mark
621 "\xE2\x80\x99" => "'", // U+2019 right single quotation mark
622 "\xE2\x80\x9A" => "'", // U+201A single low-9 quotation mark
623 "\xE2\x80\x9B" => "'", // U+201B single high-reversed-9 quotation mark
624 "\xE2\x80\x9C" => '"', // U+201C left double quotation mark
625 "\xE2\x80\x9D" => '"', // U+201D right double quotation mark
626 "\xE2\x80\x9E" => '"', // U+201E double low-9 quotation mark
627 "\xE2\x80\x9F" => '"', // U+201F double high-reversed-9 quotation mark
628 "\xE2\x80\xB9" => "'", // U+2039 single left-pointing angle quotation mark
629 "\xE2\x80\xBA" => "'", // U+203A single right-pointing angle quotation mark
630 "\xE2\x80\xAF" => " " // 7.3.14 (@timtom): U+206F nominal digit shapes
631 );
632 $chr = array_keys( $chr_map );
633 $rpl = array_values( $chr_map );
634 $item->bib = str_ireplace( $chr, $rpl, html_entity_decode( $item->bib, ENT_QUOTES, "UTF-8" ) );
635 $item->data->title = str_ireplace( $chr, $rpl, html_entity_decode( $item->data->title, ENT_QUOTES, "UTF-8" ) );
636
637 // Re-encode for foreign characters, but don't encode quotes:
638 $item->bib = htmlentities( $item->bib, ENT_NOQUOTES, "UTF-8" );
639 $item->data->title = htmlentities( $item->data->title, ENT_NOQUOTES, "UTF-8" );
640
641
642 // Next, prep title:
643 // $item->data->title = htmlentities( $item->data->title, ENT_COMPAT, "UTF-8" );
644
645
646 // If wrapping title, wrap it:
647 $item->bib = str_ireplace(
648 $item->data->title,
649 "<a class='zp-ItemURL' ".$zp_target_output."href='".$item->data->url."'>".$item->data->title."</a>",
650 $item->bib
651 );
652
653 // Finally, revert bib entities:
654 $item->bib = html_entity_decode( $item->bib, ENT_QUOTES, "UTF-8" );
655 $item->data->title = html_entity_decode( $item->data->title, ENT_QUOTES, "UTF-8" );
656
657 }
658 else // Just hyperlink the URL text
659 {
660 $item->bib = str_ireplace(
661 htmlentities($item->data->url),
662 "<a class='zp-ItemURL' ".$zp_target_output."href='".$item->data->url."'>".$item->data->url."</a>",
663 $item->bib
664 );
665 }
666 } // Hyperlink or URL wrap
667
668 // Hyperlink DOIs
669 if ( isset($item->data->DOI)
670 // && $item->data->DOI !== null
671 && strlen($item->data->DOI) > 0 )
672 {
673 // Styles without http
674 if ( strpos( $item->bib, "doi:" ) !== false
675 && strpos( $item->bib, "doi.org" ) == false )
676 {
677 $item->bib = str_ireplace(
678 "doi:" . $item->data->DOI,
679 "<a class='zp-DOIURL' ".$zp_target_output."href='http://doi.org/".$item->data->DOI."'>http://doi.org/".$item->data->DOI."</a>",
680 $item->bib
681 );
682 }
683 // Styles with http
684 elseif ( strpos( $item->bib, "http://doi.org/" ) !== false
685 && strpos( $item->bib, "</a>" ) == false )
686 {
687 $item->bib = str_ireplace(
688 "http://doi.org/" . $item->data->DOI,
689 "<a class='zp-DOIURL' ".$zp_target_output."href='http://doi.org/".$item->data->DOI."'>http://doi.org/".$item->data->DOI."</a>",
690 $item->bib
691 );
692 }
693 // HTTPS format
694 elseif ( strpos( $item->bib, "https://doi.org/" ) !== false
695 && strpos( $item->bib, "</a>" ) == false )
696 {
697 $item->bib = str_ireplace(
698 "https://doi.org/" . $item->data->DOI,
699 "<a class='zp-DOIURL' ".$zp_target_output."href='https://doi.org/".$item->data->DOI."'>https://doi.org/".$item->data->DOI."</a>",
700 $item->bib
701 );
702 }
703 }
704
705 // Cite link (RIS)
706 if ( $zpr["citeable"] ) {
707
708 // REVIEW: Why is this needed? Why is api_user_id empty sometimes?
709 $tempUserId = '';
710 if ( isset($api_user_id) )
711 $tempUserId = $api_user_id;
712 else
713 $tempUserId = $zpr["api_user_id"];
714
715 $item->bib = preg_replace( '~(.*)' . preg_quote('</div>', '~') . '(.*?)~', '$1' . " <a title='Cite in RIS Format' class='zp-CiteRIS' data-zp-cite='api_user_id=".$tempUserId."&amp;item_key=".$item->key."' href='javascript:void(0);'>Cite</a> </div>" . '$2', $item->bib, 1 );
716 // $item->bib = preg_replace( '~(.*)' . preg_quote('</div>', '~') . '(.*?)~', '$1' . " <a title='Cite in RIS Format' class='zp-CiteRIS' href='".ZOTPRESS_PLUGIN_URL."lib/request/request.cite.php?api_user_id=".$tempUserId."&amp;item_key=".$item->key."'>Cite</a> </div>" . '$2', $item->bib, 1 );
717 // $item->bib = preg_replace( '~(.*)' . preg_quote('</div>', '~') . '(.*?)~', '$1' . " <a title='Cite in RIS Format' class='zp-CiteRIS' href='".ZOTPRESS_PLUGIN_URL."lib/request/request.cite.php?api_user_id=".$zpr["api_user_id"]."&amp;item_key=".$item->key."'>Cite</a> </div>" . '$2', $item->bib, 1 );
718 }
719
720 // Highlight text
721 if ( $zpr["highlight"] )
722 $item->bib = str_ireplace( $zpr["highlight"], "<strong>".$zpr["highlight"]."</strong>", $item->bib );
723
724 // Downloads, notes
725 if ( $zpr["downloadable"]
726 || $zpr["shownotes"] )
727 {
728 // Check if item has children that could be downloads
729 if ( $item->meta->numChildren > 0 )
730 {
731 // REVIEW: Why is this needed? Why is api_user_id empty sometimes?
732 $tempUserId = '';
733 if ( isset($api_user_id) )
734 $tempUserId = $api_user_id;
735 else
736 $tempUserId = $zpr["api_user_id"];
737
738 // Get the user's account
739 $zp_account = zotpress_get_account ($wpdb, $tempUserId);
740
741 $zp_child_url = "https://api.zotero.org/".$zp_account[0]->account_type."/".$tempUserId."/items";
742 $zp_child_url .= "/".$item->key."/children?";
743 if (!is_null($zp_account[0]->public_key) && trim($zp_account[0]->public_key) != "")
744 $zp_child_url .= "key=".$zp_account[0]->public_key."&";
745 $zp_child_url .= "&format=json&include=data";
746
747 // Get data
748 $zp_import_child = new ZotpressRequest();
749 $zp_child_request = $zp_import_child->get_request_contents( $zp_child_url, $zpr["update"] );
750 $zp_children = json_decode( $zp_child_request["json"] );
751
752 // If the item changes and no longer exists on
753 // the Zotero side, it will return an error message
754 if ( ! is_null($zp_children)
755 && $zp_children != "Item not found" )
756 {
757 $zp_download_meta = false;
758 $zp_notes_meta = array();
759
760 foreach ( $zp_children as $zp_child )
761 {
762 // Check for downloads
763 if ( $zpr["downloadable"] )
764 {
765 // Check for downloadable file (attached)
766 if ( isset($zp_child->data->linkMode)
767 && ( ( $zp_child->data->linkMode == "imported_file"
768 || $zp_child->data->linkMode == "imported_url" )
769 && preg_match('(pdf|doc|docx|ppt|pptx|latex|rtf|odt|odp)', $zp_child->data->filename) === 1 ) )
770 {
771 $zp_download_meta = array (
772 "dlkey" => $zp_child->key,
773 "contentType" => $zp_child->data->contentType
774 );
775
776 // Display download link if file exists
777 if ( $zp_download_meta !== [] ) {
778 $item->bib = preg_replace('~(.*)' . preg_quote( '</div>', '~') . '(.*?)~', '$1' . " <a title='Download' class='zp-DownloadURL zp-getDownloadURL' data-zp-dl='api_user_id=".$tempUserId."&amp;dlkey=".$zp_download_meta["dlkey"]."&amp;content_type=".$zp_download_meta["contentType"]."' href='javascript:void(0);'>Download</a></div>" . '$2', $item->bib, 1 );
779 // $item->bib = preg_replace('~(.*)' . preg_quote( '</div>', '~') . '(.*?)~', '$1' . " <a title='Download' class='zp-DownloadURL zp-getDownloadURL' href='".ZOTPRESS_PLUGIN_URL."lib/request/request.dl.php?api_user_id=".$tempUserId."&amp;dlkey=".$zp_download_meta["dlkey"]."&amp;content_type=".$zp_download_meta["contentType"]."'>Download</a></div>" . '$2', $item->bib, 1 );
780 }
781 }
782
783 // Check for link to downloadable file (third-party)
784 else if ( isset($zp_child->data->linkMode)
785 && ( $zp_child->data->linkMode == "linked_url"
786 && preg_match('(pdf|doc|docx|ppt|pptx|latex|rtf|odt|odp)', $zp_child->data->url) === 1 ) )
787 {
788 $item->bib = preg_replace('~(.*)' . preg_quote( '</div>', '~') . '(.*?)~', '$1' . " <a title='Download' class='zp-DownloadURL' href='".$zp_child->data->url."'>Download</a></div>" . '$2', $item->bib, 1 );
789 }
790 }
791
792 // Check for notes
793 if ( $zpr["shownotes"]
794 && ( isset($zp_child->data->itemType)
795 && $zp_child->data->itemType == "note" ) )
796 {
797 $zp_notes_meta[count($zp_notes_meta)] = $zp_child->data->note;
798 }
799 }
800
801 // // Display download link if file exists
802 // if ( $zp_download_meta )
803 // $item->bib = preg_replace('~(.*)' . preg_quote( '</div>', '~') . '(.*?)~', '$1' . " <a title='Download' class='zp-DownloadURL' href='".ZOTPRESS_PLUGIN_URL."lib/request/request.dl.php?api_user_id=".$zpr["api_user_id"]."&amp;dlkey=".$zp_download_meta["dlkey"]."&amp;content_type=".$zp_download_meta["contentType"]."'>Download</a></div>" . '$2', $item->bib, 1 );
804
805 // Display notes, if any
806 if ( $zp_notes_meta !== [] )
807 {
808 $temp_notes = "<li id=\"zp-Note-".$item->key."\">\n";
809
810 if ( count($zp_notes_meta) == 1 )
811 {
812 $temp_notes .= $zp_notes_meta[0]."\n";
813 }
814 else // multiple
815 {
816 $temp_notes .= "<ul class='zp-Citation-Item-Notes'>\n";
817
818 foreach ($zp_notes_meta as $zp_note_meta)
819 $temp_notes .= "<li class='zp-Citation-note'>" . $zp_note_meta . "\n</li>\n";
820
821 $temp_notes .= "\n</ul><!-- .zp-Citation-Item-Notes -->\n\n";
822 }
823
824 // Add to item
825 $item->notes = $temp_notes . "</li>\n";
826
827 // Add note reference to citation
828 $note_class = "zp-Notes-Reference"; if ( is_admin_bar_showing() ) $note_class .= " zp-Admin-Bar-Showing";
829 $item->bib = preg_replace('~(.*)' . preg_quote('</div>', '~') . '(.*?)~', '$1' . " <sup class=\"".$note_class."\"><a href=\"#zp-Note-".$item->key."\">".$zp_notes_num."</a></sup> </div>" . '$2', $item->bib, 1);
830 $zp_notes_num++;
831 }
832 } // Children exist; not "Item not found"
833 } // Check if item has children
834 } // $zpr["downloadable"]
835
836 $zp_all_the_data[] = $item;
837
838 } // foreach item
839
840 // Show tags
841 if ( $zpr["showtags"] )
842 {
843 // Decode JSON format
844 $zp_tags = json_decode( $zp_request["tags"] );
845
846 // Just add to the data; let the front-end handle the display
847 // i.e., add to item.data.tags
848 foreach ( $zp_all_the_data as $id => $data )
849 {
850 // Tags are connected to item key
851 if ( property_exists( $zp_tags, $data->key ) )
852 $zp_all_the_data[$id]->data->tags = $zp_tags->{$data->key};
853 }
854 }
855
856
857 // Show images
858 if ( $zpr["showimage"] )
859 {
860 // Get images for all item keys from zpdb, if they exist
861 $zp_temp_img_keys = explode(" ", trim($zp_showimage_keys) );
862 $zp_temp_img_keys_count = count($zp_temp_img_keys);
863 $zp_temp_img_ph = array_fill(0, $zp_temp_img_keys_count, "s");
864 foreach ( $zp_temp_img_ph as $i => $ii )
865 $zp_temp_img_ph[$i] = '\'%'.($i+1).'$s\'';
866 $zp_temp_img_ph = implode(",", $zp_temp_img_ph);
867
868 // 7.4: PCP doesn't properly support IN(), so ...
869 $wpdb->zp_img_map_keys = $zp_temp_img_ph;
870
871 $zp_images = $wpdb->get_results(
872 $wpdb->prepare(
873 "
874 SELECT * FROM `".$wpdb->prefix."zotpress_zoteroItemImages`
875 WHERE `item_key` IN ({$wpdb->zp_img_map_keys})
876 ",
877 $zp_temp_img_keys
878 )
879 );
880
881 if ( count($zp_images) > 0 )
882 {
883 foreach ( $zp_images as $image )
884 {
885 $zp_thumbnail = wp_get_attachment_image_src($image->image);
886
887 foreach ( $zp_all_the_data as $id => $data )
888 {
889 if ( $data->key == $image->item_key)
890 {
891 $zp_all_the_data[$id]->image = $zp_thumbnail;
892
893 // URL Wrap for images
894 if ( $zpr["urlwrap"] && $zpr["urlwrap"] == "image" && $zp_all_the_data[$id]->data->url != "" )
895 {
896 // Get rid of default URL listing
897 // TODO: Does this account for all citation styles?
898 $zp_all_the_data[$id]->bib = str_replace( htmlentities($zp_all_the_data[$id]->data->url), "", $zp_all_the_data[$id]->bib );
899 $zp_all_the_data[$id]->bib = str_replace( " Retrieved from ", "", $zp_all_the_data[$id]->bib );
900 $zp_all_the_data[$id]->bib = str_replace( " Available from: ", "", $zp_all_the_data[$id]->bib );
901 }
902 }
903 }
904 }
905 } // If images found in zpdb
906
907 // Check open lib next
908 // REVIEW: Will break if Open Library is down!
909 // 7.4.3: Added fixes via André Lambelet, e.g., https, headers
910 if ( $zpr["showimage"] === "openlib" )
911 {
912 $zp_showimage_keys = explode( ",", $zp_showimage_keys );
913
914 foreach ( $zp_all_the_data as $id => $data )
915 {
916 if ( ! in_array( $data->key, $zp_showimage_keys )
917 && ( isset($data->data->ISBN) && $data->data->ISBN != "" ) )
918 {
919 $openlib_url = "https://covers.openlibrary.org/b/isbn/".$data->data->ISBN."-M.jpg";
920
921 // First, get the headers
922 $openlib_headers = @get_headers( $openlib_url );
923
924 // And make sure Open Library / the source is online
925 // if ( $openlib_headers[0] == "HTTP/1.1 302 Found" )
926 $openlib_status = isset($openlib_headers[0]) ? $openlib_headers[0] : '';
927 $openlib_ctype = '';
928 foreach ( $openlib_headers as $h ) {
929 if ( stripos( $h, 'content-type:' ) === 0 ) {
930 $openlib_ctype = strtolower( $h );
931 break;
932 }
933 }
934 if ( ( strpos($openlib_status, '200') !== false && strpos($openlib_ctype, 'image/jpeg') !== false )
935 || strpos($openlib_status, '302') !== false )
936 {
937 $zp_all_the_data[$id]->image = array( $openlib_url );
938
939 // URL Wrap for images
940 if ( $zpr["urlwrap"]
941 && $zpr["urlwrap"] == 'image'
942 && $zp_all_the_data[$id]->data->url != '' )
943 {
944 // Get rid of default URL listing
945 // TODO: Does this account for all citation styles?
946 $zp_all_the_data[$id]->bib = str_replace( htmlentities($zp_all_the_data[$id]->data->url), "", $zp_all_the_data[$id]->bib );
947 $zp_all_the_data[$id]->bib = str_replace( " Retrieved from ", "", $zp_all_the_data[$id]->bib );
948 $zp_all_the_data[$id]->bib = str_replace( " Available from: ", "", $zp_all_the_data[$id]->bib );
949 }
950 }
951 }
952 }
953 }
954 } // showimage
955
956 // 7.4: Major change to passing and parsing bib HTML
957 // 7.4 Update: There's a 30 second timeout without the cache with downloads ...
958 // TODO: Perhaps cache the download requests and deal with them separately
959 foreach ( $zp_all_the_data as $id => $data )
960 {
961 // 7.4 Update: Trying to avoid errors ...
962 if ( isset($zp_all_the_data[$id]->bib) )
963 $zp_all_the_data[$id]->bib = esc_html($zp_all_the_data[$id]->bib);
964
965 // 7.4: Abstract breaking when unicode exists
966 // if ( isset($zp_all_the_data[$id]->data->abstractNote) )
967 // $zp_all_the_data[$id]->data->abstractNote = mb_convert_encoding($zp_all_the_data[$id]->data->abstractNote, 'UTF-8', 'UCS-2BE');
968 // 7.4.1: Now appears in another language sometimes ... fix by Jeremy Varnham (@jvarn13)
969 if ( isset($zp_all_the_data[$id]->data->abstractNote) )
970 $zp_all_the_data[$id]->data->abstractNote = esc_html($zp_all_the_data[$id]->data->abstractNote);
971 }
972
973 // Re-sort with order of entry if bib and default sort
974 if ( $zpr["item_type"] == "items"
975 && $zpr["sortby"] == "default"
976 && count($zpr["item_keys_order"]) > 0 )
977 {
978 $temp_arr = array();
979
980 foreach ( $zpr["item_keys_order"] as $temp_key )
981 {
982 foreach ( $zp_all_the_data as $temp_data )
983 {
984 if ( $temp_data->key == $temp_key )
985 $temp_arr[] = $temp_data;
986 }
987 }
988
989 $zp_all_the_data = $temp_arr;
990 }
991
992
993 // Remove extra meta data:
994 foreach ( $zp_all_the_data as $id => $data )
995 {
996 unset( $zp_all_the_data[$id]->meta->createdByUser );
997 }
998
999 } // if there's data (more than 0)
1000 }
1001
1002 else // No results
1003 {
1004 // $zp_all_the_data = ""; // Necessary?
1005 }
1006
1007
1008
1009 // +----------------------------+
1010 // | Finish and output the data |
1011 // +----------------------------+
1012
1013 unset($zp_import_contents);
1014 unset($zp_import_url);
1015 unset($zp_xml);
1016 unset($api_user_id);
1017 unset($zp_account);
1018
1019 $wpdb->flush();
1020
1021 // Deal with cache scenarios:
1022 // 1. Used the cache
1023 // 2. Didn't check (why???)
1024 // TEST: Why are these checks needed? There's no "else" ... removing for now
1025 if ( ( $checkcache && $zp_usecache )
1026 || ( ! $checkcache ) )
1027 {
1028 $zp_output = '';
1029
1030 if ( $zp_usecache )
1031 {
1032 // Indicate new (just cached) or cached used:
1033 $zp_request_meta['used_cache'] = true;
1034 }
1035
1036 if ( count($zp_all_the_data) > 0
1037 && $zp_all_the_data != "" )
1038 {
1039 $zp_json_encoded = wp_json_encode(
1040 array (
1041 "status" => "success",
1042 "updateneeded" => $zp_updateneeded,
1043 "instance" => $zpr["instance_id"],
1044 "meta" => $zp_request_meta,
1045 "data" => $zp_all_the_data
1046 )
1047 );
1048
1049 if ( $is_ajax ) // JS:
1050 {
1051 // echo $zp_json_encoded;
1052 echo wp_kses($zp_json_encoded,
1053 array(
1054 'p' => array(),
1055 'i' => array(),
1056 'b' => array(),
1057 'em' => array(),
1058 'strong' => array(),
1059 'div' => array(
1060 'class' => array(),
1061 'style' => array()
1062 ),
1063 )
1064 );
1065
1066 exit(); // REVIEW: Causing to break if error
1067 }
1068 else // PHP:
1069 {
1070 // TEST: Is this right?
1071 $zp_output = "\t\t\t\t";
1072
1073 if ( $zp_updateneeded )
1074 $zp_output .= '<span class="ZP_UPDATENEEDED ZP_ATTR">true</span>';
1075 $zp_output .= '<span class="ZP_CACHETIMER ZP_ATTR">'.get_option("Zotpress_DefaultCacheTimer").'</span>';
1076
1077 // $zp_output .= '<span class="ZP_USED_CACHE ZP_ATTR">true</span>';
1078 $zp_output .= '<span class="ZP_JSON ZP_ATTR">'.rawurlencode($zp_json_encoded).'</span>';
1079 $zp_output .= "\n\n";
1080
1081 foreach ( $zp_all_the_data as $zp_citation )
1082 {
1083 // QUESTION: Why is it a string?
1084 // $zp_citation = json_decode($zp_citation);
1085
1086 $zp_output .= "\t\t\t\t";
1087 $zp_output .= '<div id="zp-ID-'.$post->ID.'-'.$zp_citation->library->id.'-'.$zp_citation->key.'"';
1088
1089 $tempItemDate = "0000-00-00";
1090 $tempItemYear = "0000";
1091 $tempItemType = $zp_citation->data->itemType;
1092 if ( property_exists($zp_citation->meta, "parsedDate")) {
1093 $tempItemDate = $zp_citation->meta->parsedDate;
1094 $tempItemYear = substr($zp_citation->meta->parsedDate, 0, 4);
1095 }
1096
1097 $tempAuthor = "";
1098 if ( property_exists($zp_citation->meta, "creatorSummary"))
1099 $tempAuthor = str_replace(' ', '-', $zp_citation->meta->creatorSummary);
1100
1101 $zp_output .= " data-zp-author-date='".$tempAuthor."-".$tempItemDate."'";
1102 $zp_output .= " data-zp-date-author='".$tempItemDate."-".$tempAuthor."'";
1103 $zp_output .= " data-zp-date='".$tempItemDate."'";
1104 $zp_output .= " data-zp-year='".$tempItemYear."'";
1105 $zp_output .= " data-zp-itemtype='".$tempItemType."'";
1106 $zp_output .= ' class="zp-Entry zpSearchResultsItem">';
1107 $zp_output .= "\n";
1108 // 7.4: Major change to passing and parsing bib HTML
1109 // $zp_output .= $zp_citation->bib;
1110 $zp_output .= html_entity_decode($zp_citation->bib);
1111 $zp_output .= "\n\t\t\t\t";
1112 $zp_output .= '</div><!-- .zp-Entry .zpSearchResultsItem -->';
1113 }
1114 return $zp_output;
1115 }
1116 }
1117 else // No data or error:
1118 {
1119 if ( ! isset($zpr["instance_id"]) )
1120 $zpr["instance_id"] = false;
1121
1122 // Indicate problem or no cache:
1123 $zp_request_meta['used_cache'] = false;
1124
1125 if ( $is_ajax )
1126 if ( $zp_error )
1127 $zp_output = wp_json_encode(
1128 array (
1129 "status" => "error",
1130 "instance" => $zpr["instance_id"],
1131 "meta" => $zp_request_meta,
1132 "data" => $zp_error
1133 )
1134 );
1135 else // catchall, likely just no items for request
1136 $zp_output = wp_json_encode(
1137 array (
1138 "status" => "empty",
1139 "instance" => $zpr["instance_id"],
1140 "meta" => $zp_request_meta,
1141 "data" => "0"
1142 )
1143 );
1144
1145 // echo $zp_output;
1146 echo wp_kses($zp_output,
1147 array(
1148 'p' => array(),
1149 'i' => array(),
1150 'b' => array(),
1151 'em' => array(),
1152 'strong' => array(),
1153 'div' => array(
1154 'id' => array(),
1155 'class' => array(),
1156 'style' => array()
1157 ),
1158 )
1159 );
1160
1161 if ( $is_ajax )
1162 exit(); // REVIEW: Causing to break if error
1163 }
1164 }
1165 }
1166 add_action( 'wp_ajax_zpRetrieveViaShortcode', 'Zotpress_shortcode_request' );
1167 add_action( 'wp_ajax_nopriv_zpRetrieveViaShortcode', 'Zotpress_shortcode_request' );
1168
1169 ?>