PluginProbe
GiveWP – Donation Plugin and Fundraising Platform / 4.16.4
GiveWP – Donation Plugin and Fundraising Platform v4.16.4
4.18.0 4.17.0 4.16.9 4.16.8.1 4.16.8 4.16.7.2 4.16.7.1 4.16.7 4.16.6.1 4.16.6 4.16.5.1 4.16.5 4.16.4 4.16.3 4.16.2 4.16.1 4.16.0 4.15.5 4.15.4 4.15.3 4.15.2 4.15.1 4.15.0 2.3.0 2.3.1 All 257 releases
give / includes / misc-functions.php

misc-functions.php in GiveWP – Donation Plugin and Fundraising Platform 4.16.4, at includes/misc-functions.php

2,692 lines 114.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Misc Functions
4 *
5 * @package Give
6 * @subpackage Functions
7 * @copyright Copyright (c) 2016, GiveWP
8 * @license https://opensource.org/licenses/gpl-license GNU Public License
9 * @since 1.0
10 */
11
12 use Give\License\PremiumAddonsListManager;
13 use Give\License\Repositories\LicenseRepository;
14 use Give\License\ValueObjects\LicenseOptionKeys;
15
16 // Exit if accessed directly.
17 if ( ! defined( 'ABSPATH' ) ) {
18 exit;
19 }
20
21 /**
22 * Is Test Mode Enabled.
23 *
24 * @return bool $ret True if return mode is enabled, false otherwise
25 * @since 1.0
26 */
27 function give_is_test_mode() {
28
29 $ret = give_is_setting_enabled( give_get_option( 'test_mode' ) );
30
31 return (bool) apply_filters( 'give_is_test_mode', $ret );
32
33 }
34
35 /**
36 * Get the current page URL.
37 *
38 * @return string $current_url Current page URL.
39 * @since 1.0
40 */
41 function give_get_current_page_url() {
42
43 global $wp;
44
45 if ( get_option( 'permalink_structure' ) ) {
46 $base = trailingslashit( home_url( $wp->request ) );
47 } else {
48 $base = add_query_arg( $wp->query_string, '', trailingslashit( home_url( $wp->request ) ) );
49 $base = remove_query_arg( [ 'post_type', 'name' ], $base );
50 }
51
52 $scheme = is_ssl() ? 'https' : 'http';
53 $current_uri = set_url_scheme( $base, $scheme );
54
55 if ( is_front_page() ) {
56 $current_uri = home_url( '/' );
57 }
58
59 /**
60 * Filter the current page url
61 *
62 * @param string $current_uri
63 *
64 * @since 1.0
65 */
66 return esc_url_raw( apply_filters( 'give_get_current_page_url', $current_uri ) );
67
68 }
69
70
71 /**
72 * Verify credit card numbers live?
73 *
74 * @return bool $ret True is verify credit cards is live
75 * @since 1.0
76 */
77 function give_is_cc_verify_enabled() {
78
79 $ret = true;
80
81 /**
82 * Enable if use a single gateway other than PayPal or Manual. We have to assume it accepts credit cards.
83 * Enable if using more than one gateway if they are not both PayPal and manual, again assuming credit card usage.
84 */
85 $gateways = give_get_enabled_payment_gateways();
86
87 if ( count( $gateways ) == 1 && ! isset( $gateways['paypal'] ) && ! isset( $gateways['manual'] ) ) {
88 $ret = true;
89 } elseif ( count( $gateways ) == 1 ) {
90 $ret = false;
91 } elseif ( count( $gateways ) == 2 && isset( $gateways['paypal'] ) && isset( $gateways['manual'] ) ) {
92 $ret = false;
93 }
94
95 /**
96 * Fire the filter
97 *
98 * @param bool $ret
99 *
100 * @since 1.0
101 */
102 return (bool) apply_filters( 'give_is_cc_verify_enabled', $ret );
103 }
104
105 /**
106 * Retrieve timezone.
107 *
108 * @return string $timezone The timezone ID.
109 * @since 1.0
110 */
111 function give_get_timezone_id() {
112
113 // if site timezone string exists, return it.
114 if ( $timezone = get_option( 'timezone_string' ) ) {
115 return $timezone;
116 }
117
118 // get UTC offset, if it isn't set return UTC.
119 if ( ! ( $utc_offset = 3600 * get_option( 'gmt_offset', 0 ) ) ) {
120 return 'UTC';
121 }
122
123 // attempt to guess the timezone string from the UTC offset.
124 $timezone = timezone_name_from_abbr( '', $utc_offset );
125
126 // last try, guess timezone string manually.
127 if ( $timezone === false ) {
128
129 $is_dst = date( 'I' );
130
131 foreach ( timezone_abbreviations_list() as $abbr ) {
132 foreach ( $abbr as $city ) {
133 if ( $city['dst'] == $is_dst && $city['offset'] == $utc_offset ) {
134 return $city['timezone_id'];
135 }
136 }
137 }
138 }
139
140 // Fallback.
141 return 'UTC';
142 }
143
144
145 /**
146 * Get User IP
147 *
148 * Returns the IP address of the current visitor
149 *
150 * @since 2.33.5 Add $single param.
151 * @since 1.0
152 *
153 * @return string $ip User's IP address
154 */
155 function give_get_ip($single = true)
156 {
157 $ip_addresses = '127.0.0.1';
158 $header_type = '';
159
160 if ( ! empty( $_SERVER['HTTP_CLIENT_IP'] ) ) {
161 // check ip from share internet
162 $ip_addresses = $_SERVER['HTTP_CLIENT_IP'];
163 $header_type = 'HTTP_CLIENT_IP';
164 } elseif ( ! empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) {
165 // to check ip is pass from proxy
166 $ip_addresses = $_SERVER['HTTP_X_FORWARDED_FOR'];
167 $header_type = 'HTTP_X_FORWARDED_FOR';
168 } elseif ( ! empty( $_SERVER['REMOTE_ADDR'] ) ) {
169 $ip_addresses = $_SERVER['REMOTE_ADDR'];
170 $header_type = 'REMOTE_ADDR';
171 }
172
173 /**
174 * Filter the IP
175 *
176 * @since 2.33.5 Add $single and $header_type params.
177 * @since 1.0
178 */
179 $ip_addresses = apply_filters('give_get_ip', $ip_addresses, $single, $header_type);
180
181 // Filter empty values.
182 if (false !== strpos($ip_addresses, ',')) {
183 $ip_addresses = give_clean(explode(',', $ip_addresses));
184 $ip_addresses = array_filter($ip_addresses);
185 $ip_addresses = implode(',', $ip_addresses);
186 } else {
187 $ip_addresses = give_clean($ip_addresses);
188 }
189
190 if ($single && false !== strpos($ip_addresses, ',')) {
191 return explode(',', $ip_addresses)[0];
192 }
193
194 return $ip_addresses;
195 }
196
197
198 /**
199 * Store Donation Data in Sessions
200 *
201 * Used for storing info about donation
202 *
203 * @param $purchase_data
204 *
205 * @since 1.0
206 *
207 * @uses Give()->session->set()
208 */
209 function give_set_purchase_session( $purchase_data = [] ) {
210 Give()->session->set( 'give_purchase', $purchase_data );
211 Give()->session->set( 'give_email', $purchase_data['user_email'] );
212 }
213
214 /**
215 * Retrieve Donation Data from Session
216 *
217 * Used for retrieving info about donation
218 * after completing a donation
219 *
220 * @return mixed array | false
221 * @uses Give()->session->get()
222 * @since 1.0
223 */
224 function give_get_purchase_session() {
225 return Give()->session->get( 'give_purchase' );
226 }
227
228 /**
229 * Retrieve Payment Key of the Receipt Access Session.
230 *
231 * @return array|string
232 * @since 1.8.17
233 */
234 function give_get_receipt_session() {
235 return Give()->session->get( 'receipt_access' );
236 }
237
238 /**
239 * Retrieve Payment Key of the History Access Session.
240 *
241 * @return array|string
242 * @since 1.8.17
243 */
244 function give_get_history_session() {
245 return (bool) Give()->session->get( 'history_access' );
246 }
247
248 /**
249 * Generate Item Title for Payment Gateway.
250 *
251 * @since 1.8.14
252 * @since 2.9.6 Function will return form title with selected form level if price id set to zero. Added second param to return result with requested character length.
253 *
254 * @param array $payment_data Payment Data.
255 *
256 * @param string|null $length
257 *
258 * @return string By default, the name of the form. Then the price level text if any is found.
259 */
260 function give_payment_gateway_item_title( $payment_data, $length = null ) {
261
262 $form_id = intval( $payment_data['post_data']['give-form-id'] );
263 $item_name = isset( $payment_data['post_data']['give-form-title'] ) ? $payment_data['post_data']['give-form-title'] : '';
264 $price_id = isset( $payment_data['post_data']['give-price-id'] ) ? $payment_data['post_data']['give-price-id'] : '';
265
266 // Verify has variable prices.
267 if ( give_has_variable_prices( $form_id ) ) {
268
269 $item_price_level_text = give_get_price_option_name( $form_id, $price_id, 0, false );
270
271 /**
272 * Output donation level text if:
273 *
274 * 1. It's not a custom amount
275 * 2. The level field has actual text and isn't the amount (which is already displayed on the receipt).
276 */
277 if ( 'custom' !== $price_id && ! empty( $item_price_level_text ) ) {
278 // Matches a donation level - append level text.
279 $item_name .= ' - ' . $item_price_level_text;
280 }
281 }
282
283 /**
284 * Filter the Item Title of Payment Gateway.
285 *
286 * @param string $item_name Item Title of Payment Gateway.
287 * @param int $form_id Donation Form ID.
288 * @param array $payment_data Payment Data.
289 *
290 * @return string
291 * @since 1.8.14
292 */
293 $item_name = apply_filters( 'give_payment_gateway_item_title', $item_name, $form_id, $payment_data );
294
295 // Cut the length
296 if ( $length ) {
297 $item_name = substr( $item_name, 0, $length );
298 }
299
300 return $item_name;
301 }
302
303 /**
304 * Get Donation Summary
305 *
306 * Creates a donation summary for payment gateways from the donation data before the payment is created in the database.
307 *
308 * @param array $donation_data
309 * @param bool $name_and_email
310 * @param int $length
311 *
312 * @return string
313 * @since 1.8.12
314 */
315 function give_payment_gateway_donation_summary( $donation_data, $name_and_email = true, $length = 255 ) {
316
317 $form_id = isset( $donation_data['post_data']['give-form-id'] ) ? $donation_data['post_data']['give-form-id'] : '';
318 $price_id = isset( $donation_data['post_data']['give-price-id'] ) ? $donation_data['post_data']['give-price-id'] : '';
319
320 // Form title.
321 $summary = ( ! empty( $donation_data['post_data']['give-form-title'] ) ? $donation_data['post_data']['give-form-title'] : ( ! empty( $form_id ) ? wp_sprintf( __( 'Donation Form ID: %d', 'give' ), $form_id ) : __( 'Untitled donation form', 'give' ) ) );
322
323 // Form multilevel if applicable.
324 if ( ! empty( $price_id ) && 'custom' !== $price_id ) {
325 $summary .= ': ' . give_get_price_option_name( $form_id, $donation_data['post_data']['give-price-id'] );
326 }
327
328 // Add Donor's name + email if requested.
329 if ( $name_and_email ) {
330
331 // First name
332 if ( isset( $donation_data['user_info']['first_name'] ) && ! empty( $donation_data['user_info']['first_name'] ) ) {
333 $summary .= ' - ' . $donation_data['user_info']['first_name'];
334 }
335
336 if ( isset( $donation_data['user_info']['last_name'] ) && ! empty( $donation_data['user_info']['last_name'] ) ) {
337 $summary .= ' ' . $donation_data['user_info']['last_name'];
338 }
339
340 $summary .= ' (' . $donation_data['user_email'] . ')';
341 }
342
343 // Cut the length
344 $summary = substr( $summary, 0, $length );
345
346 return apply_filters( 'give_payment_gateway_donation_summary', $summary );
347 }
348
349
350 /**
351 * Get user host
352 *
353 * Returns the webhost this site is using if possible
354 *
355 * @return string $host if detected, false otherwise
356 * @since 1.0
357 */
358 function give_get_host() {
359 $find_host = gethostname();
360
361 if ( strpos( $find_host, 'sgvps.net' ) ) {
362 $host = 'Siteground';
363 } elseif ( defined( 'WPE_APIKEY' ) ) {
364 $host = 'WP Engine';
365 } elseif ( defined( 'PAGELYBIN' ) || strpos( $find_host, 'pagelyhosting.com' ) ) {
366 $host = 'Pagely';
367 } elseif ( strpos( $find_host, 'secureserver.net' ) ) {
368 $host = 'GoDaddy/Media Temple';
369 } elseif ( DB_HOST == 'localhost:/tmp/mysql5.sock' ) {
370 $host = 'ICDSoft';
371 } elseif ( DB_HOST == 'mysqlv5' ) {
372 $host = 'NetworkSolutions';
373 } elseif ( strpos( DB_HOST, 'ipagemysql.com' ) !== false ) {
374 $host = 'iPage';
375 } elseif ( strpos( DB_HOST, 'ipowermysql.com' ) !== false ) {
376 $host = 'IPower';
377 } elseif ( strpos( DB_HOST, '.gridserver.com' ) !== false ) {
378 $host = 'MediaTemple Grid';
379 } elseif ( strpos( DB_HOST, '.pair.com' ) !== false ) {
380 $host = 'pair Networks';
381 } elseif ( strpos( DB_HOST, '.stabletransit.com' ) !== false ) {
382 $host = 'Rackspace Cloud';
383 } elseif ( strpos( DB_HOST, '.sysfix.eu' ) !== false ) {
384 $host = 'SysFix.eu Power Hosting';
385 } elseif ( strpos( $_SERVER['SERVER_NAME'], 'Flywheel' ) !== false || strpos( $find_host, 'fw' ) ) {
386 $host = 'Flywheel';
387 } else {
388 // Adding a general fallback for data gathering
389 $host = 'DBH: ' . DB_HOST . ', SRV: ' . $_SERVER['SERVER_NAME'];
390 }
391
392 return $host;
393 }
394
395 /**
396 * Marks a function as deprecated and informs when it has been used.
397 *
398 * There is a hook give_deprecated_function_run that will be called that can be used
399 * to get the backtrace up to what file and function called the deprecated
400 * function.
401 *
402 * The current behavior is to trigger a user error if WP_DEBUG is true.
403 *
404 * This function is to be used in every function that is deprecated.
405 *
406 * @param string $function The function that was called.
407 * @param string $version The plugin version that deprecated the function.
408 * @param string $replacement Optional. The function that should have been called.
409 * @param array $backtrace Optional. Contains stack backtrace of deprecated function.
410 *
411 * @uses do_action() Calls 'give_deprecated_function_run' and passes the function name, what to use instead,
412 * and the version the function was deprecated in.
413 * @uses apply_filters() Calls 'give_deprecated_function_trigger_error' and expects boolean value of true to do
414 * trigger or false to not trigger error.
415 */
416 function _give_deprecated_function( $function, $version, $replacement = null, $backtrace = null ) {
417
418 /**
419 * Fires while give deprecated function call occurs.
420 *
421 * Allow you to hook to deprecated function call.
422 *
423 * @param string $function The function that was called.
424 * @param string $replacement Optional. The function that should have been called.
425 * @param string $version The plugin version that deprecated the function.
426 *
427 * @since 1.0
428 */
429 do_action( 'give_deprecated_function_run', $function, $replacement, $version );
430
431 $show_errors = current_user_can( 'manage_options' );
432
433 // Allow plugin to filter the output error trigger.
434 if ( WP_DEBUG && apply_filters( 'give_deprecated_function_trigger_error', $show_errors ) ) {
435 if ( ! is_null( $replacement ) ) {
436 trigger_error( sprintf( __( '%1$s is <strong>deprecated</strong> since GiveWP version %2$s! Use %3$s instead.', 'give' ), $function, $version, $replacement ) );
437 trigger_error( print_r( $backtrace, 1 ) ); // Limited to previous 1028 characters, but since we only need to move back 1 in stack that should be fine.
438 // Alternatively we could dump this to a file.
439 } else {
440 trigger_error( sprintf( __( '%1$s is <strong>deprecated</strong> since GiveWP version %2$s with no alternative available.', 'give' ), $function, $version ) );
441 trigger_error( print_r( $backtrace, 1 ) );// Limited to previous 1028 characters, but since we only need to move back 1 in stack that should be fine.
442 // Alternatively we could dump this to a file.
443 }
444 }
445 }
446
447 /**
448 * Give Get Admin ID
449 *
450 * Helper function to return the ID of the post for admin usage
451 *
452 * @return string $post_id
453 */
454 function give_get_admin_post_id() {
455 $post_id = isset( $_REQUEST['post'] ) ? absint( $_REQUEST['post'] ) : null;
456
457 $post_id = ! empty( $post_id ) ? $post_id : ( isset( $_REQUEST['post_id'] ) ? absint( $_REQUEST['post_id'] ) : null );
458
459 $post_id = ! empty( $post_id ) ? $post_id : ( isset( $_REQUEST['post_ID'] ) ? absint( $_REQUEST['post_ID'] ) : null );
460
461 return $post_id;
462 }
463
464 /**
465 * Get PHP Arg Separator Output
466 *
467 * @return string Arg separator output
468 * @since 1.0
469 */
470 function give_get_php_arg_separator_output() {
471 return ini_get( 'arg_separator.output' );
472 }
473
474
475 /**
476 * Month Num To Name
477 *
478 * Takes a month number and returns the name three letter name of it.
479 *
480 * @param int $n
481 *
482 * @return string Short month name
483 * @since 1.0
484 */
485 function give_month_num_to_name( $n ) {
486 $timestamp = mktime( 0, 0, 0, $n, 1, 2005 );
487
488 return date_i18n( 'M', $timestamp );
489 }
490
491 /**
492 * Checks whether function is disabled.
493 *
494 * @param string $function Name of the function.
495 *
496 * @return bool Whether or not function is disabled.
497 * @since 1.0
498 */
499 function give_is_func_disabled( $function ) {
500 $disabled = explode( ',', ini_get( 'disable_functions' ) );
501
502 return in_array( $function, $disabled );
503 }
504
505
506 /**
507 * Create SVG library function
508 *
509 * @param string $icon
510 *
511 * @return string
512 */
513 function give_svg_icons( $icon ) {
514
515 // Store your SVGs in an associative array
516 $svgs = [
517 'microphone' => 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iaXNvLTg4NTktMSI/Pg0KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDE2LjAuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPg0KPCFET0NUWVBFIHN2ZyBQVUJMSUMgIi0vL1czQy8vRFREIFNWRyAxLjEvL0VOIiAiaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkIj4NCjxzdmcgdmVyc2lvbj0iMS4xIiBpZD0iQ2FwYV8xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB4PSIwcHgiIHk9IjBweCINCgkgd2lkdGg9IjY0cHgiIGhlaWdodD0iMTAwcHgiIHZpZXdCb3g9IjAgLTIwIDY0IDEyMCIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgNjQgMTAwOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+DQo8Zz4NCgk8Zz4NCgkJPHBhdGggZD0iTTYyLDM2LjIxNWgtM2MtMS4xLDAtMiwwLjktMiwyVjUyYzAsNi42ODYtNS4yNjYsMTgtMjUsMThTNyw1OC42ODYsNyw1MlYzOC4yMTVjMC0xLjEtMC45LTItMi0ySDJjLTEuMSwwLTIsMC45LTIsMlY1Mg0KCQkJYzAsMTEuMTg0LDguMjE1LDIzLjE1MiwyNywyNC44MDFWOTBIMTRjLTEuMSwwLTIsMC44OTgtMiwydjZjMCwxLjEsMC45LDIsMiwyaDM2YzEuMSwwLDItMC45LDItMnYtNmMwLTEuMTAyLTAuOS0yLTItMkgzN1Y3Ni44MDENCgkJCUM1NS43ODUsNzUuMTUyLDY0LDYzLjE4NCw2NCw1MlYzOC4yMTVDNjQsMzcuMTE1LDYzLjEsMzYuMjE1LDYyLDM2LjIxNXoiLz4NCgkJPHBhdGggZD0iTTMyLDYwYzExLjczMiwwLDE1LTQuODE4LDE1LThWMzYuMjE1SDE3VjUyQzE3LDU1LjE4MiwyMC4yNjYsNjAsMzIsNjB6Ii8+DQoJCTxwYXRoIGQ9Ik00Nyw4YzAtMy4xODQtMy4yNjgtOC0xNS04QzIwLjI2NiwwLDE3LDQuODE2LDE3LDh2MjEuMjE1aDMwVjh6Ii8+DQoJPC9nPg0KPC9nPg0KPGc+DQo8L2c+DQo8Zz4NCjwvZz4NCjxnPg0KPC9nPg0KPGc+DQo8L2c+DQo8Zz4NCjwvZz4NCjxnPg0KPC9nPg0KPGc+DQo8L2c+DQo8Zz4NCjwvZz4NCjxnPg0KPC9nPg0KPGc+DQo8L2c+DQo8Zz4NCjwvZz4NCjxnPg0KPC9nPg0KPGc+DQo8L2c+DQo8Zz4NCjwvZz4NCjxnPg0KPC9nPg0KPC9zdmc+DQo=',
518 'alert' => 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iaXNvLTg4NTktMSI/Pg0KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDE2LjAuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPg0KPCFET0NUWVBFIHN2ZyBQVUJMSUMgIi0vL1czQy8vRFREIFNWRyAxLjEvL0VOIiAiaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkIj4NCjxzdmcgdmVyc2lvbj0iMS4xIiBpZD0iQ2FwYV8xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB4PSIwcHgiIHk9IjBweCINCgkgd2lkdGg9IjI4LjkzOHB4IiBoZWlnaHQ9IjI1LjAwNXB4IiB2aWV3Qm94PSIwIDAgMjguOTM4IDI1LjAwNSIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgMjguOTM4IDI1LjAwNTsiDQoJIHhtbDpzcGFjZT0icHJlc2VydmUiPg0KPHBhdGggc3R5bGU9ImZpbGw6IzAwMDAwMDsiIGQ9Ik0yOC44NTksMjQuMTU4TDE0Ljk1NywwLjI3OUMxNC44NTYsMC4xMDYsMTQuNjcsMCwxNC40NjgsMGMtMC4xOTgsMC0wLjM4MywwLjEwNi0wLjQ4MSwwLjI3OQ0KCUwwLjA3OSwyNC4xNThjLTAuMTAyLDAuMTc1LTAuMTA2LDAuMzg5LTAuMDA2LDAuNTY1YzAuMTAzLDAuMTc0LDAuMjg3LDAuMjgyLDAuNDg4LDAuMjgyaDI3LjgxNGMwLjIwMSwwLDAuMzg5LTAuMTA4LDAuNDg4LTAuMjgyDQoJYzAuMDQ3LTAuMDg4LDAuMDc0LTAuMTg2LDAuMDc0LTAuMjgxQzI4LjkzOCwyNC4zNDMsMjguOTExLDI0LjI0NSwyOC44NTksMjQuMTU4eiBNMTYuMzY5LDguNDc1bC0wLjQ2Miw5LjQ5M2gtMi4zODlsLTAuNDYxLTkuNDkzDQoJSDE2LjM2OXogTTE0LjcxMSwyMi44MjhoLTAuMDQyYy0xLjA4OSwwLTEuODQzLTAuODE3LTEuODQzLTEuOTA3YzAtMS4xMzEsMC43NzQtMS45MDcsMS44ODUtMS45MDdzMS44NDYsMC43NzUsMS44NjcsMS45MDcNCglDMTYuNTc5LDIyLjAxMSwxNS44NDQsMjIuODI4LDE0LjcxMSwyMi44Mjh6Ii8+DQo8Zz4NCjwvZz4NCjxnPg0KPC9nPg0KPGc+DQo8L2c+DQo8Zz4NCjwvZz4NCjxnPg0KPC9nPg0KPGc+DQo8L2c+DQo8Zz4NCjwvZz4NCjxnPg0KPC9nPg0KPGc+DQo8L2c+DQo8Zz4NCjwvZz4NCjxnPg0KPC9nPg0KPGc+DQo8L2c+DQo8Zz4NCjwvZz4NCjxnPg0KPC9nPg0KPGc+DQo8L2c+DQo8L3N2Zz4NCg==',
519 'placemark' => 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAxNi4wLjAsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+DQo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4Ig0KCSB3aWR0aD0iMTAwcHgiIGhlaWdodD0iMTAwcHgiIHZpZXdCb3g9IjAgMCAxMDAgMTAwIiBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCAxMDAgMTAwIiB4bWw6c3BhY2U9InByZXNlcnZlIj4NCjxnPg0KCTxwYXRoIGQ9Ik01MC40MzQsMjAuMjcxYy0xMi40OTksMC0yMi42NjgsMTAuMTY5LTIyLjY2OCwyMi42NjhjMCwxMS44MTQsMTguODE1LDMyLjE1NSwyMC45NiwzNC40MzdsMS43MDgsMS44MTZsMS43MDgtMS44MTYNCgkJYzIuMTQ1LTIuMjgxLDIwLjk2LTIyLjYyMywyMC45Ni0zNC40MzdDNzMuMTAzLDMwLjQ0LDYyLjkzNCwyMC4yNzEsNTAuNDM0LDIwLjI3MXogTTUwLjQzNCw1Mi4zMmMtNS4xNzIsMC05LjM4LTQuMjA4LTkuMzgtOS4zOA0KCQlzNC4yMDgtOS4zOCw5LjM4LTkuMzhjNS4xNzMsMCw5LjM4LDQuMjA4LDkuMzgsOS4zOFM1NS42MDcsNTIuMzIsNTAuNDM0LDUyLjMyeiIvPg0KPC9nPg0KPC9zdmc+DQo=',
520 'give_grey' => 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPjxzdmcgdmVyc2lvbj0iMS4xIiBpZD0iTGF5ZXJfMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeD0iMHB4IiB5PSIwcHgiIHZpZXdCb3g9IjEwMC4xIDAgNDAwIDQwMCIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAxMDAuMSAwIDQwMCA0MDAiIHhtbDpzcGFjZT0icHJlc2VydmUiPjxnIGlkPSJMYXllcl8xXzFfIj48Y2lyY2xlIGZpbGw9IiM2NkJCNkEiIGN4PSItNDA3LjMiIGN5PSIzNDYuMyIgcj0iNDIuMiIvPjxnPjxnPjxwYXRoIGZpbGw9IiM1NDZFN0EiIGQ9Ik0tNzg2LjQsMTMzLjh2LTEyLjVoNC44YzMuOCwwLDYuNiwyLjUsNi42LDYuNHMtMi44LDYuNC02LjYsNi40aC00LjhWMTMzLjh6IE0tNzc3LjUsMTI3LjVjMC0yLjMtMS4zLTMuOC0zLjgtMy44aC0yLjN2Ny45aDIuM0MtNzc5LDEzMS42LTc3Ny41LDEyOS44LTc3Ny41LDEyNy41eiIvPjxwYXRoIGZpbGw9IiM1NDZFN0EiIGQ9Ik0tNzcxLjYsMTMzLjh2LTEyLjVoOC45djIuM2gtNi4xdjIuNWg2LjF2Mi4zaC02LjF2Mi44aDYuMXYyLjNoLTguOVYxMzMuOHoiLz48cGF0aCBmaWxsPSIjNTQ2RTdBIiBkPSJNLTc0OC41LDEzMy44di04LjdsLTMuNiw4LjdoLTEuM2wtMy42LTguN3Y4LjdoLTIuNXYtMTIuNWgzLjhsMy4xLDcuNmwzLjEtNy42aDMuOHYxMi41SC03NDguNXoiLz48cGF0aCBmaWxsPSIjNTQ2RTdBIiBkPSJNLTc0Mi40LDEyNy41YzAtMy44LDIuOC02LjQsNi42LTYuNHM2LjYsMi44LDYuNiw2LjRjMCwzLjgtMi44LDYuNC02LjYsNi40Qy03MzkuOCwxMzQuMS03NDIuNCwxMzEuMy03NDIuNCwxMjcuNXogTS03MzIuMiwxMjcuNWMwLTIuMy0xLjUtNC4xLTMuOC00LjFjLTIuMywwLTMuOCwxLjgtMy44LDQuMWMwLDIuMywxLjUsNC4xLDMuOCw0LjFDLTczMy43LDEzMS42LTczMi4yLDEyOS44LTczMi4yLDEyNy41eiIvPjxwYXRoIGZpbGw9IiM1NDZFN0EiIGQ9Ik0tNzI2LjgsMTI3LjVjMC0zLjgsMi44LTYuNCw2LjYtNi40YzIuOCwwLDQuMywxLjUsNS4zLDMuMWwtMi4zLDFjLTAuNS0xLTEuNS0xLjgtMy4xLTEuOGMtMi4zLDAtMy44LDEuOC0zLjgsNC4xYzAsMi4zLDEuNSw0LjEsMy44LDQuMWMxLjMsMCwyLjMtMC44LDMuMS0xLjhsMi4zLDFjLTEsMS41LTIuNSwzLjEtNS4zLDMuMUMtNzIzLjgsMTM0LjEtNzI2LjgsMTMxLjMtNzI2LjgsMTI3LjV6Ii8+PHBhdGggZmlsbD0iIzU0NkU3QSIgZD0iTS03MDQuNywxMzMuOGwtMi41LTQuM2gtMnY0LjNoLTIuNXYtMTIuNWg1LjljMi41LDAsNC4xLDEuOCw0LjEsNC4xYzAsMi4zLTEuMywzLjMtMi44LDMuOGwyLjgsNC44aC0yLjhWMTMzLjh6IE0tNzA0LjUsMTI1LjJjMC0xLTAuOC0xLjgtMS44LTEuOGgtMi44djMuM2gyLjhDLTcwNS41LDEyNy03MDQuNSwxMjYuNS03MDQuNSwxMjUuMnoiLz48cGF0aCBmaWxsPSIjNTQ2RTdBIiBkPSJNLTY4OS43LDEzMy44bC0wLjgtMmgtNS4zbC0wLjgsMmgtMy4xbDQuOC0xMi41aDMuM2w0LjgsMTIuNUgtNjg5Ljd6IE0tNjkzLjMsMTIzLjlsLTIsNS4zaDMuOEwtNjkzLjMsMTIzLjl6Ii8+PHBhdGggZmlsbD0iIzU0NkU3QSIgZD0iTS02ODIuNiwxMzMuOHYtMTAuMmgtMy42di0yLjNoOS45djIuM2gtMy42djEwLjJILTY4Mi42eiIvPjxwYXRoIGZpbGw9IiM1NDZFN0EiIGQ9Ik0tNjczLjIsMTMzLjh2LTEyLjVoMi41djEyLjVILTY3My4yeiIvPjxwYXRoIGZpbGw9IiM1NDZFN0EiIGQ9Ik0tNjY3LDEzMy44di0ybDUuOS03LjloLTUuOXYtMi4zaDkuNHYybC01LjksOC4xaDYuMXYyLjNoLTkuN1YxMzMuOHoiLz48cGF0aCBmaWxsPSIjNTQ2RTdBIiBkPSJNLTY1NC4xLDEzMy44di0xMi41aDIuNXYxMi41SC02NTQuMXoiLz48cGF0aCBmaWxsPSIjNTQ2RTdBIiBkPSJNLTYzOS4xLDEzMy44bC01LjktOC4xdjguMWgtMi41di0xMi41aDIuOGw1LjksNy45di03LjloMi41djEyLjVILTYzOS4xeiIvPjxwYXRoIGZpbGw9IiM1NDZFN0EiIGQ9Ik0tNjMzLjIsMTI3LjVjMC00LjEsMy4xLTYuNCw2LjYtNi40YzIuNSwwLDQuMywxLjMsNS4xLDIuOGwtMi4zLDEuM2MtMC41LTAuOC0xLjUtMS41LTMuMS0xLjVjLTIuMywwLTMuOCwxLjgtMy44LDQuMWMwLDIuMywxLjUsNC4xLDMuOCw0LjFjMSwwLDItMC41LDIuNS0xdi0xLjVoLTMuM1YxMjdoNS45djQuOGMtMS4zLDEuNS0zLjEsMi4zLTUuMywyLjNDLTYzMC4yLDEzNC4xLTYzMy4yLDEzMS42LTYzMy4yLDEyNy41eiIvPjxwYXRoIGZpbGw9IiM1NDZFN0EiIGQ9Ik0tNjEyLjEsMTI3LjVjMC00LjEsMy4xLTYuNCw2LjYtNi40YzIuNSwwLDQuMywxLjMsNS4xLDIuOGwtMi4zLDEuM2MtMC41LTAuOC0xLjUtMS41LTMuMS0xLjVjLTIuMywwLTMuOCwxLjgtMy44LDQuMWMwLDIuMywxLjUsNC4xLDMuOCw0LjFjMSwwLDItMC41LDIuNS0xdi0xLjVoLTMuM1YxMjdoNS45djQuOGMtMS4zLDEuNS0zLjEsMi4zLTUuMywyLjNDLTYwOSwxMzQuMS02MTIuMSwxMzEuNi02MTIuMSwxMjcuNXoiLz48cGF0aCBmaWxsPSIjNTQ2RTdBIiBkPSJNLTU5Ni42LDEzMy44di0xMi41aDguOXYyLjNoLTYuMXYyLjVoNi4xdjIuM2gtNi4xdjIuOGg2LjF2Mi4zaC04LjlWMTMzLjh6Ii8+PHBhdGggZmlsbD0iIzU0NkU3QSIgZD0iTS01NzUuNywxMzMuOGwtNS45LTguMXY4LjFoLTIuNXYtMTIuNWgyLjhsNS45LDcuOXYtNy45aDIuNXYxMi41SC01NzUuN3oiLz48cGF0aCBmaWxsPSIjNTQ2RTdBIiBkPSJNLTU2OS4xLDEzMy44di0xMi41aDguOXYyLjNoLTYuMXYyLjVoNi4xdjIuM2gtNi4xdjIuOGg2LjF2Mi4zaC04LjlWMTMzLjh6Ii8+PHBhdGggZmlsbD0iIzU0NkU3QSIgZD0iTS01NDkuNywxMzMuOGwtMi41LTQuM2gtMnY0LjNoLTIuNXYtMTIuNWg1LjljMi41LDAsNC4xLDEuOCw0LjEsNC4xYzAsMi4zLTEuMywzLjMtMi44LDMuOGwyLjgsNC44aC0yLjhWMTMzLjh6IE0tNTQ5LjUsMTI1LjJjMC0xLTAuOC0xLjgtMS44LTEuOGgtMi44djMuM2gyLjhDLTU1MC4zLDEyNy01NDkuNSwxMjYuNS01NDkuNSwxMjUuMnoiLz48cGF0aCBmaWxsPSIjNTQ2RTdBIiBkPSJNLTU0My45LDEyNy41YzAtMy44LDIuOC02LjQsNi42LTYuNHM2LjYsMi44LDYuNiw2LjRjMCwzLjgtMi44LDYuNC02LjYsNi40Qy01NDEuMywxMzQuMS01NDMuOSwxMzEuMy01NDMuOSwxMjcuNXogTS01MzMuNywxMjcuNWMwLTIuMy0xLjUtNC4xLTMuOC00LjFzLTMuOCwxLjgtMy44LDQuMWMwLDIuMywxLjUsNC4xLDMuOCw0LjFDLTUzNS4yLDEzMS42LTUzMy43LDEyOS44LTUzMy43LDEyNy41eiIvPjxwYXRoIGZpbGw9IiM1NDZFN0EiIGQ9Ik0tNTI4LjYsMTMyLjFsMS41LTJjMC44LDEsMi4zLDEuOCw0LjEsMS44YzEuNSwwLDIuMy0wLjgsMi4zLTEuM2MwLTIuMy03LjEtMC44LTcuMS01LjNjMC0yLDEuOC0zLjgsNC44LTMuOGMyLDAsMy42LDAuNSw0LjgsMS44bC0xLjUsMmMtMS0xLTIuMy0xLjMtMy42LTEuM2MtMSwwLTEuOCwwLjUtMS44LDEuM2MwLDIsNy4xLDAuOCw3LjEsNS4zYzAsMi4zLTEuNSw0LjEtNS4xLDQuMUMtNTI1LjYsMTM0LjEtNTI3LjQsMTMzLjEtNTI4LjYsMTMyLjF6Ii8+PHBhdGggZmlsbD0iIzU0NkU3QSIgZD0iTS01MTUuMSwxMzMuOHYtMTIuNWgyLjV2MTIuNUgtNTE1LjF6Ii8+PHBhdGggZmlsbD0iIzU0NkU3QSIgZD0iTS01MDUuNywxMzMuOHYtMTAuMmgtMy42di0yLjNoOS45djIuM2gtMy42djEwLjJILTUwNS43eiIvPjxwYXRoIGZpbGw9IiM1NDZFN0EiIGQ9Ik0tNDkyLjcsMTMzLjh2LTUuMWwtNC44LTcuNGgzLjFsMy4xLDUuMWwzLjEtNS4xaDMuMWwtNC44LDcuNHY1LjFILTQ5Mi43eiIvPjwvZz48Zz48Zz48cGF0aCBmaWxsPSIjNjZCQjZBIiBkPSJNLTQ4NS45LDQ0LjNoLTEuM2wwLjMsMS4zYzIsOS45LDAuMywyNC43LTcuNCwzMy44Yy00LjMsNS4zLTkuOSw4LjEtMTYuOCw4LjFjLTEwLjksMC0xNS0xMy0xNS41LTI3LjdjMTcuOC00LjMsMjkuOC0xNS41LDI5LjgtMjguNWMwLTkuNC0yLjgtMjQuOS0yMS40LTI0LjljLTE3LjYsMC0yNi41LDI2LjItMjguMiw0NC41Yy04LjktMC4zLTE1LjUtNC4zLTE5LjYtOC4xYzEuNS02LjQsMi4zLTEyLjIsMi4zLTE3LjZjMC03LjQtNS4xLTEwLjctOS45LTEwLjdjLTYuOSwwLTE0LDYuNi0xNCwxOS4zYzAsNy42LDIuOCwxNCw4LjcsMTguNmMtNS4xLDEyLTEzLjcsMjIuMS0xNi41LDI1LjRjLTIuMy00LjgtOS43LTIyLjQtMTItNDEuNWMyLjgtNy42LDQuMy0xNCw0LjMtMTdjMC00LjgtMy4xLTcuNi04LjEtNy42Yy02LjksMC0xNy44LDQuMy0xOC4xLDQuNmwtMC41LDAuM3YwLjhjMCwwLjMsMy4zLDE1LjUsNi42LDMyLjNjLTYuNCwxMC40LTE3LjYsMjcuNy0yMy4yLDI3LjdjLTEwLjIsMCw2LjYtNTIuMi0wLjgtNTMuOWMtMC4zLDAtMC41LDAtMC44LDAuM2MtMy42LDIuMy00My41LDI0LjQtOTYuNywyNC40YzAsMCwwLDEsMC41LDJjMC4zLDAuOCwxLDEuNSwxLDEuNWMxNSwxLjgsMzYuNC0wLjMsNTIuNy0yLjVjLTkuNCwyMC4xLTI2LDMzLjMtNDEuMiwzMy4zYy0yOC44LDAtNTAuOS0zNC45LTUwLjktMzQuOWM4LjktNy45LDIzLjQtMzMuMyw0NC44LTMzLjNjMjEuMSwwLDMwLjMsMTEuNywzMC4zLDExLjdsMi4zLTMuOGMwLDAtOS45LTM0LjYtMzcuOS0zNC42cy01Ny44LDQ1LjgtNzUuMSw1Ni41YzAsMCwyMy45LDU2LjUsNzYuMSw1Ni41YzQzLjgsMCw1NS00Miw1Ny01Mi4yYzEwLjctMS41LDE4LjEtMy4xLDE4LjEtMy4xcy0yLjgsMjEuNC0yLjgsMzAuM3M5LjksMTguMywxOC4xLDE4LjNjNi45LDAsMjAuOS0xNC4yLDMxLTMxLjZsMC41LDJjNS4zLDE5LjYsMTIsMjkuOCwxOS44LDI5LjhjNy45LDAsMjAuOS0xNi4zLDI5LjMtMzYuOWM4LjQsMy42LDE4LjMsNC42LDI0LjIsNC44YzIuMywzNS40LDMxLjgsMzYuNCwzNS40LDM2LjRjMjEuOSwwLDQwLjUtMTUuOCw0MC41LTM0LjRDLTQ3MC42LDQ0LjUtNDg1LjYsNDQuMy00ODUuOSw0NC4zeiBNLTUxMi42LDI5LjVjMCwwLTAuMywxMS43LTEzLjUsMTcuNmMxLjMtMTUuNSw1LjEtMjkuNSw3LjYtMjkuNUMtNTE1LjYsMTcuOC01MTIuNiwyMi4xLTUxMi42LDI5LjV6Ii8+PHBhdGggZmlsbD0iIzY2QkI2QSIgZD0iTS02NjUsMTUuNWMwLDAuNSwwLjMsMC44LDAuOCwxYzEwLjQsMS41LDE3LjMtMS44LDE3LjMtMTguNmMwLTE1LjgtMTYuMy0zLjMtMTkuMy0xYy0wLjMsMC4zLTAuMywwLjUtMC4zLDFDLTY2My43LDQuMS02NjQuOCwxMy02NjUsMTUuNXoiLz48L2c+PGxpbmVhckdyYWRpZW50IGlkPSJTVkdJRF8xXyIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIHgxPSItMjg5LjU4NjQiIHkxPSIzNzMuMjM3OSIgeDI9Ii0yODIuODg0MiIgeTI9IjM3NS40NzE5IiBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDIuNTQ0NSAwIDAgLTIuNTQ0NSAxMDAuMTI3MiAxMDE3LjgxMTcpIj48c3RvcCAgb2Zmc2V0PSIwIiBzdHlsZT0ic3RvcC1jb2xvcjojNjZCQjZBIi8+PHN0b3AgIG9mZnNldD0iMSIgc3R5bGU9InN0b3AtY29sb3I6IzM3OEY0MyIvPjwvbGluZWFyR3JhZGllbnQ+PHBhdGggZmlsbD0idXJsKCNTVkdJRF8xXykiIGQ9Ik0tNjIzLDQ5LjRjLTQuMSw2LjktMTAuMiwxNi4zLTE1LjUsMjIuMWMxLjMsMy4xLDIuOCw2LjksNC4zLDkuOWM0LjgtNS4zLDkuNy0xMi4yLDE0LTE5LjNMLTYyMyw0OS40eiIvPjxsaW5lYXJHcmFkaWVudCBpZD0iU1ZHSURfMl8iIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiB4MT0iLTI2OS4wNTc3IiB5MT0iMzcxLjU0NDEiIHgyPSItMjY1LjE3MDUiIHkyPSIzNzguMzgwMiIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgyLjU0NDUgMCAwIC0yLjU0NDUgMTAwLjEyNzIgMTAxNy44MTE3KSI+PHN0b3AgIG9mZnNldD0iMCIgc3R5bGU9InN0b3AtY29sb3I6IzY2QkI2QSIvPjxzdG9wICBvZmZzZXQ9IjEiIHN0eWxlPSJzdG9wLWNvbG9yOiMzNzhGNDMiLz48L2xpbmVhckdyYWRpZW50PjxwYXRoIGZpbGw9InVybCgjU1ZHSURfMl8pIiBkPSJNLTU3NC43LDU0LjdjLTItMS0zLjgtMi41LTMuOC0yLjVjLTMuNiw3LjktOC40LDE1LjMtMTIuMiwyMC4xYzEuOCwyLjUsNC44LDUuOSw3LjEsOC40YzQuNi02LjQsOS40LTE0LjgsMTMtMjMuN0MtNTcwLjQsNTYuNy01NzIuNiw1Ni01NzQuNyw1NC43eiIvPjxsaW5lYXJHcmFkaWVudCBpZD0iU1ZHSURfM18iIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiB4MT0iLTI0OC42NDE2IiB5MT0iMzY4LjM4MzUiIHgyPSItMjQ5LjQ0NTkiIHkyPSIzNzUuNTMyMyIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgyLjU0NDUgMCAwIC0yLjU0NDUgMTAwLjEyNzIgMTAxNy44MTE3KSI+PHN0b3AgIG9mZnNldD0iMCIgc3R5bGU9InN0b3AtY29sb3I6IzY2QkI2QSIvPjxzdG9wICBvZmZzZXQ9IjEiIHN0eWxlPSJzdG9wLWNvbG9yOiMzNzhGNDMiLz48L2xpbmVhckdyYWRpZW50PjxwYXRoIGZpbGw9InVybCgjU1ZHSURfM18pIiBkPSJNLTUyNi4zLDU5LjhjMCwwLTUuMSwxLTEwLjIsMS41cy05LjksMC4zLTkuOSwwLjNjMC44LDEwLjIsMy42LDE3LjMsNy40LDIyLjZsMTguNi0xLjVDLTUyNC4zLDc3LjYtNTI2LjEsNjktNTI2LjMsNTkuOHoiLz48bGluZWFyR3JhZGllbnQgaWQ9IlNWR0lEXzRfIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9Ii0yNDkuOCIgeTE9IjM4My41ODEiIHgyPSItMjQ5LjgiIHkyPSIzNzYuMzc2MyIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgyLjU0NDUgMCAwIC0yLjU0NDUgMTAwLjEyNzIgMTAxNy44MTE3KSI+PHN0b3AgIG9mZnNldD0iMCIgc3R5bGU9InN0b3AtY29sb3I6IzY2QkI2QSIvPjxzdG9wICBvZmZzZXQ9IjEiIHN0eWxlPSJzdG9wLWNvbG9yOiMzNzhGNDMiLz48L2xpbmVhckdyYWRpZW50PjxwYXRoIGZpbGw9InVybCgjU1ZHSURfNF8pIiBkPSJNLTU0MS4xLDI4LjhMLTU0MS4xLDI4LjhjLTAuNSwxLjUtMS4zLDMuMy0xLjgsNS4xYzAsMC41LTAuMywwLjgtMC4zLDEuM2MtMSwzLjMtMS44LDYuNi0yLjMsOS45YzAsMC41LTAuMywwLjgtMC4zLDEuM2MtMC4zLDEuNS0wLjUsMy4xLTAuNSw0LjZjMTIsMCwyMC4xLTMuNiwyMC4xLTMuNmMwLTEuMywwLjMtMi4zLDAuMy0zLjZjMC0wLjMsMC0wLjUsMC0wLjhjMC0xLDAuMy0xLjgsMC4zLTIuOGMwLTAuMywwLTAuNSwwLTAuOGMwLjMtMi4zLDAuOC00LjYsMS02LjZMLTU0MS4xLDI4Ljh6IE0tNTQ2LjQsNTAuNkwtNTQ2LjQsNTAuNkwtNTQ2LjQsNTAuNkwtNTQ2LjQsNTAuNnoiLz48bGluZWFyR3JhZGllbnQgaWQ9IlNWR0lEXzVfIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9Ii0zMTMiIHkxPSIzNzEuNzcyMiIgeDI9Ii0zMTMiIHkyPSIzODAuNzA4MyIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgyLjU0NDUgMCAwIC0yLjU0NDUgMTAwLjEyNzIgMTAxNy44MTE3KSI+PHN0b3AgIG9mZnNldD0iMCIgc3R5bGU9InN0b3AtY29sb3I6IzY2QkI2QSIvPjxzdG9wICBvZmZzZXQ9IjEiIHN0eWxlPSJzdG9wLWNvbG9yOiMzNzhGNDMiLz48L2xpbmVhckdyYWRpZW50PjxwYXRoIGZpbGw9InVybCgjU1ZHSURfNV8pIiBkPSJNLTcwOC4zLDcyLjhsMTEuMiw0LjhjNS4zLTcuNiw4LjctMTUuNSwxMC43LTIxLjZsMi04LjFsLTUuMywwLjhDLTY5NC41LDU4LjgtNzAxLjEsNjYuOS03MDguMyw3Mi44eiIvPjxsaW5lYXJHcmFkaWVudCBpZD0iU1ZHSURfNl8iIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiB4MT0iLTI3Ny41MjU1IiB5MT0iMzkwLjIxMyIgeDI9Ii0yNzguNjQ3OSIgeTI9IjM4OC40MjU0IiBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDIuNTQ0NSAwIDAgLTIuNTQ0NSAxMDAuMTI3MiAxMDE3LjgxMTcpIj48c3RvcCAgb2Zmc2V0PSIwIiBzdHlsZT0ic3RvcC1jb2xvcjojNjZCQjZBIi8+PHN0b3AgIG9mZnNldD0iMSIgc3R5bGU9InN0b3AtY29sb3I6IzM3OEY0MyIvPjwvbGluZWFyR3JhZGllbnQ+PHBhdGggZmlsbD0idXJsKCNTVkdJRF82XykiIGQ9Ik0tNjA3LDM2LjFjMi44LTcuNiw0LjMtMTQsNC4zLTE3YzAtMC4zLDAtMC41LDAtMC44bC02LjYsMkMtNjA5LjMsMjAuNC02MDksMjQuNy02MDcsMzYuMXoiLz48bGluZWFyR3JhZGllbnQgaWQ9IlNWR0lEXzdfIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9Ii0yNjIuNTgxMyIgeTE9IjM4Ni40ODI3IiB4Mj0iLTI2My4yMTUiIHkyPSIzODQuMDc0OSIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgyLjU0NDUgMCAwIC0yLjU0NDUgMTAwLjEyNzIgMTAxNy44MTE3KSI+PHN0b3AgIG9mZnNldD0iMCIgc3R5bGU9InN0b3AtY29sb3I6IzY2QkI2QSIvPjxzdG9wICBvZmZzZXQ9IjEiIHN0eWxlPSJzdG9wLWNvbG9yOiMzNzhGNDMiLz48L2xpbmVhckdyYWRpZW50PjxwYXRoIGZpbGw9InVybCgjU1ZHSURfN18pIiBkPSJNLTU3MS40LDMwLjVjMCwwLTEuOCw1LjMsNS42LDEyYzEuMy01LjMsMi0xMC43LDIuMy0xNS4zTC01NzEuNCwzMC41eiIvPjwvZz48L2c+PGc+PGc+PHBhdGggZmlsbD0iIzY2QkI2QSIgZD0iTS04MDcuOCwzNDYuNmgtMC41djAuNWMwLjgsNC4zLDAsMTAuNC0zLjEsMTQuNWMtMS44LDIuMy00LjMsMy42LTcuMSwzLjZjLTQuNiwwLTYuNC01LjYtNi42LTExLjdjNy42LTEuOCwxMi43LTYuNiwxMi43LTEyLjJjMC00LjEtMS4zLTEwLjctOS4yLTEwLjdjLTcuNCwwLTExLjIsMTEuMi0xMiwxOC44Yy0zLjgsMC02LjYtMS44LTguNC0zLjZjMC44LTIuOCwxLTUuMSwxLTcuNGMwLTMuMS0yLjMtNC42LTQuMS00LjZjLTMuMSwwLTUuOSwyLjgtNS45LDguMWMwLDMuMywxLjMsNS45LDMuOCw3LjljLTIuMyw1LjEtNS45LDkuNC03LjEsMTAuN2MtMS0yLTQuMS05LjctNS4xLTE3LjZjMS4zLTMuMywxLjgtNS45LDEuOC03LjFjMC0yLTEuMy0zLjMtMy42LTMuM2MtMy4xLDAtNy42LDEuOC03LjYsMmwtMC4zLDAuM3YwLjNjMCwwLDEuMyw2LjYsMi44LDEzLjdjLTIuOCw0LjMtNy40LDExLjctOS45LDExLjdjLTQuMywwLDIuOC0yMi4xLTAuMy0yMi45aC0wLjNjLTEuNSwxLTE4LjYsMTAuNC00MSwxMC40YzAsMCwwLDAuNSwwLjMsMC44YzAuMywwLjMsMC41LDAuNSwwLjUsMC41YzYuNCwwLjgsMTUuNSwwLDIyLjQtMWMtNC4xLDguNC0xMC45LDE0LjItMTcuNiwxNC4yYy0xMi4yLDAtMjEuNi0xNC44LTIxLjYtMTQuOGMzLjgtMy4zLDkuOS0xNC4yLDE5LjEtMTQuMmM4LjksMCwxMyw0LjgsMTMsNC44bDEtMS41YzAsMC00LjMtMTQuOC0xNi0xNC44cy0yNC40LDE5LjYtMzEuOCwyMy45YzAsMCwxMC4yLDI0LjIsMzIuMywyNC4yYzE4LjYsMCwyMy40LTE3LjgsMjQuMi0yMi4xYzQuNi0wLjgsNy42LTEuMyw3LjYtMS4zcy0xLDkuMi0xLDEzYzAsMy44LDQuMSw3LjksNy42LDcuOWMzLjEsMCw4LjktNi4xLDEzLjItMTMuNWwwLjMsMC44YzIuMyw4LjQsNS4xLDEyLjcsOC40LDEyLjdzOC45LTYuOSwxMi41LTE1LjVjMy42LDEuNSw3LjksMiwxMC4yLDJjMSwxNSwxMy41LDE1LjUsMTUsMTUuNWM5LjQsMCwxNy4zLTYuNiwxNy4zLTE0LjVDLTgwMS40LDM0Ni44LTgwNy44LDM0Ni42LTgwNy44LDM0Ni42eiBNLTgxOSwzNDAuMmMwLDAsMCw1LjEtNS45LDcuNmMwLjUtNi42LDItMTIuNSwzLjMtMTIuNUMtODIwLjUsMzM1LjQtODE5LDMzNy4yLTgxOSwzNDAuMnoiLz48cGF0aCBmaWxsPSIjNjZCQjZBIiBkPSJNLTg4My44LDMzNC40YzAsMC4zLDAsMC4zLDAuMywwLjVjNC4zLDAuNSw3LjQtMC44LDcuNC03LjljMC02LjYtNi45LTEuNS04LjEtMC41YzAsMC0wLjMsMC4zLDAsMC41Qy04ODMuMywzMjkuNS04ODMuOCwzMzMuMS04ODMuOCwzMzQuNHoiLz48L2c+PGxpbmVhckdyYWRpZW50IGlkPSJTVkdJRF84XyIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIHgxPSItMzgyLjAwNzQiIHkxPSIyNTkuODQ3NSIgeDI9Ii0zNzkuMTU4NSIgeTI9IjI2MC43OTcyIiBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDIuNTQ0NSAwIDAgLTIuNTQ0NSAxMDAuMTI3MiAxMDE3LjgxMTcpIj48c3RvcCAgb2Zmc2V0PSIwIiBzdHlsZT0ic3RvcC1jb2xvcjojNjZCQjZBIi8+PHN0b3AgIG9mZnNldD0iMSIgc3R5bGU9InN0b3AtY29sb3I6IzM3OEY0MyIvPjwvbGluZWFyR3JhZGllbnQ+PHBhdGggZmlsbD0idXJsKCNTVkdJRF84XykiIGQ9Ik0tODY2LDM0OC42Yy0xLjgsMi44LTQuMyw2LjktNi42LDkuNGMwLjUsMS4zLDEuMywyLjgsMS44LDQuM2MyLTIuMyw0LjEtNS4xLDYuMS04LjFMLTg2NiwzNDguNnoiLz48bGluZWFyR3JhZGllbnQgaWQ9IlNWR0lEXzlfIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9Ii0zNzMuMTYyNiIgeTE9IjI1OS4wNDIzIiB4Mj0iLTM3MS41MTAyIiB5Mj0iMjYxLjk0ODIiIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMi41NDQ1IDAgMCAtMi41NDQ1IDEwMC4xMjcyIDEwMTcuODExNykiPjxzdG9wICBvZmZzZXQ9IjAiIHN0eWxlPSJzdG9wLWNvbG9yOiM2NkJCNkEiLz48c3RvcCAgb2Zmc2V0PSIxIiBzdHlsZT0ic3RvcC1jb2xvcjojMzc4RjQzIi8+PC9saW5lYXJHcmFkaWVudD48cGF0aCBmaWxsPSJ1cmwoI1NWR0lEXzlfKSIgZD0iTS04NDUuNCwzNTAuOWMtMC44LTAuNS0xLjUtMS0xLjUtMWMtMS41LDMuMy0zLjYsNi40LTUuMSw4LjdjMC44LDEsMiwyLjUsMy4xLDMuNmMyLTIuOCw0LjEtNi4xLDUuNi0xMC4yQy04NDMuNiwzNTEuOS04NDQuNywzNTEuNC04NDUuNCwzNTAuOXoiLz48bGluZWFyR3JhZGllbnQgaWQ9IlNWR0lEXzEwXyIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIHgxPSItMzY0LjY5OTYiIHkxPSIyNTcuNzUwMyIgeDI9Ii0zNjUuMDQxNCIgeTI9IjI2MC43ODkyIiBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDIuNTQ0NSAwIDAgLTIuNTQ0NSAxMDAuMTI3MiAxMDE3LjgxMTcpIj48c3RvcCAgb2Zmc2V0PSIwIiBzdHlsZT0ic3RvcC1jb2xvcjojNjZCQjZBIi8+PHN0b3AgIG9mZnNldD0iMSIgc3R5bGU9InN0b3AtY29sb3I6IzM3OEY0MyIvPjwvbGluZWFyR3JhZGllbnQ+PHBhdGggZmlsbD0idXJsKCNTVkdJRF8xMF8pIiBkPSJNLTgyNS4xLDM1My4yYzAsMC0yLDAuNS00LjMsMC44Yy0yLDAuMy00LjMsMC00LjMsMGMwLjMsNC4zLDEuNSw3LjQsMy4xLDkuN2w3LjktMC44Qy04MjQsMzYwLjgtODI0LjgsMzU3LTgyNS4xLDM1My4yeiIvPjxsaW5lYXJHcmFkaWVudCBpZD0iU1ZHSURfMTFfIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9Ii0zNjUiIHkxPSIyNjQuMjIyMyIgeDI9Ii0zNjUiIHkyPSIyNjEuMTU5NyIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgyLjU0NDUgMCAwIC0yLjU0NDUgMTAwLjEyNzIgMTAxNy44MTE3KSI+PHN0b3AgIG9mZnNldD0iMCIgc3R5bGU9InN0b3AtY29sb3I6IzY2QkI2QSIvPjxzdG9wICBvZmZzZXQ9IjEiIHN0eWxlPSJzdG9wLWNvbG9yOiMzNzhGNDMiLz48L2xpbmVhckdyYWRpZW50PjxwYXRoIGZpbGw9InVybCgjU1ZHSURfMTFfKSIgZD0iTS04MzEuMiwzMzkuOUwtODMxLjIsMzM5LjljLTAuMywwLjgtMC41LDEuNS0wLjgsMmMwLDAuMywwLDAuMy0wLjMsMC41Yy0wLjUsMS41LTAuOCwyLjgtMSw0LjNjMCwwLjMsMCwwLjMsMCwwLjVjMCwwLjgtMC4zLDEuMy0wLjMsMmM1LjEsMCw4LjctMS41LDguNy0xLjVjMC0wLjUsMC0xLDAuMy0xLjV2LTAuM2MwLTAuNSwwLTAuOCwwLjMtMXYtMC4zYzAuMy0xLDAuMy0yLDAuNS0yLjhMLTgzMS4yLDMzOS45eiBNLTgzMy41LDM0OS40TC04MzMuNSwzNDkuNEwtODMzLjUsMzQ5LjRMLTgzMy41LDM0OS40eiIvPjxsaW5lYXJHcmFkaWVudCBpZD0iU1ZHSURfMTJfIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9Ii0zOTIiIHkxPSIyNTkuMjAyNSIgeDI9Ii0zOTIiIHkyPSIyNjMuMDAxMSIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgyLjU0NDUgMCAwIC0yLjU0NDUgMTAwLjEyNzIgMTAxNy44MTE3KSI+PHN0b3AgIG9mZnNldD0iMCIgc3R5bGU9InN0b3AtY29sb3I6IzY2QkI2QSIvPjxzdG9wICBvZmZzZXQ9IjEiIHN0eWxlPSJzdG9wLWNvbG9yOiMzNzhGNDMiLz48L2xpbmVhckdyYWRpZW50PjxwYXRoIGZpbGw9InVybCgjU1ZHSURfMTJfKSIgZD0iTS05MDIuNCwzNTguOGw0LjgsMmMyLjMtMy4zLDMuNi02LjYsNC42LTkuMmwwLjgtMy42bC0yLjMsMC4zQy04OTYuNiwzNTIuNy04OTkuMSwzNTYuMi05MDIuNCwzNTguOHoiLz48bGluZWFyR3JhZGllbnQgaWQ9IlNWR0lEXzEzXyIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIHgxPSItMzc2Ljg2MzciIHkxPSIyNjcuMDM1NSIgeDI9Ii0zNzcuMzQwOSIgeTI9IjI2Ni4yNzU1IiBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDIuNTQ0NSAwIDAgLTIuNTQ0NSAxMDAuMTI3MiAxMDE3LjgxMTcpIj48c3RvcCAgb2Zmc2V0PSIwIiBzdHlsZT0ic3RvcC1jb2xvcjojNjZCQjZBIi8+PHN0b3AgIG9mZnNldD0iMSIgc3R5bGU9InN0b3AtY29sb3I6IzM3OEY0MyIvPjwvbGluZWFyR3JhZGllbnQ+PHBhdGggZmlsbD0idXJsKCNTVkdJRF8xM18pIiBkPSJNLTg1OS4yLDM0M2MxLjMtMy4zLDEuOC01LjksMS44LTcuMXYtMC4zbC0yLjgsMC44Qy04NjAuMiwzMzYuNC04NjAuMiwzMzguMi04NTkuMiwzNDN6Ii8+PGxpbmVhckdyYWRpZW50IGlkPSJTVkdJRF8xNF8iIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiB4MT0iLTM3MC41NTQzIiB5MT0iMjY1LjQ2NDUiIHgyPSItMzcwLjgyMzYiIHkyPSIyNjQuNDQxIiBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDIuNTQ0NSAwIDAgLTIuNTQ0NSAxMDAuMTI3MiAxMDE3LjgxMTcpIj48c3RvcCAgb2Zmc2V0PSIwIiBzdHlsZT0ic3RvcC1jb2xvcjojNjZCQjZBIi8+PHN0b3AgIG9mZnNldD0iMSIgc3R5bGU9InN0b3AtY29sb3I6IzM3OEY0MyIvPjwvbGluZWFyR3JhZGllbnQ+PHBhdGggZmlsbD0idXJsKCNTVkdJRF8xNF8pIiBkPSJNLTg0NC4xLDM0MC43YzAsMC0wLjgsMi4zLDIuMyw1LjFjMC41LTIuMywxLTQuNiwxLTYuNkwtODQ0LjEsMzQwLjd6Ii8+PC9nPjxnPjxyZWN0IHg9Ii02OTcuMyIgeT0iMjkyLjkiIGZpbGw9IiNGRkZGRkYiIHdpZHRoPSIxMDYuOSIgaGVpZ2h0PSIxMDYuOSIvPjxnPjxwYXRoIGZpbGw9IiM2NkJCNkEiIGQ9Ik0tNjQ0LjQsMzQ5LjljMC4zLDAuNSwwLjUsMC44LDAuNSwwLjhjOC43LDEsMjEuMSwwLDMwLjUtMS41Yy01LjMsMTEuNy0xNSwxOS4zLTIzLjksMTkuM2MtMTYuNSwwLTI5LjUtMjAuMS0yOS41LTIwLjFjNS4xLTQuNiwxMy43LTE5LjMsMjYtMTkuM2MxMi4yLDAsMTcuNiw2LjYsMTcuNiw2LjZsMS4zLTIuM2MwLDAtNS45LTIwLjEtMjEuOS0yMC4xYy0xNi4zLDAtMzMuMywyNi41LTQzLjUsMzIuNmMwLDAsMTMuNywzMi44LDQ0LDMyLjhjMjUuNCwwLDMxLjgtMjQuMiwzMy4xLTMwLjNjMy4zLTAuNSw2LjEtMSw4LjEtMS4zYzAuNS0xLjMsMS4zLTMuOCwwLjgtNy4xYy0xMC4yLDMuOC0yNS40LDguNC00My41LDguNEMtNjQ0LjcsMzQ4LjYtNjQ0LjcsMzQ5LjEtNjQ0LjQsMzQ5Ljl6Ii8+PGxpbmVhckdyYWRpZW50IGlkPSJTVkdJRF8xNV8iIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiB4MT0iLTI4MS44NSIgeTE9IjI1Ny41MTg3IiB4Mj0iLTI4MS44NSIgeTI9IjI2Mi42NTExIiBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDIuNTQ0NSAwIDAgLTIuNTQ0NSAxMDAuMTI3MiAxMDE3LjgxMTcpIj48c3RvcCAgb2Zmc2V0PSIwIiBzdHlsZT0ic3RvcC1jb2xvcjojNjZCQjZBIi8+PHN0b3AgIG9mZnNldD0iMSIgc3R5bGU9InN0b3AtY29sb3I6IzM3OEY0MyIvPjwvbGluZWFyR3JhZGllbnQ+PHBhdGggZmlsbD0idXJsKCNTVkdJRF8xNV8pIiBkPSJNLTYxMC4xLDM0OC45bC0zLjEsMC4zYzAsMC4zLTAuMywwLjUtMC41LDAuOGMtMC41LDEtMSwxLjgtMS41LDIuOGMtMC4zLDAuNS0wLjUsMC44LTAuOCwxLjNjLTAuNSwxLTEuMywyLTIsMi44Yy0wLjMsMC4zLTAuMywwLjMtMC41LDAuNWMtMS44LDIuMy0zLjYsNC4zLTUuNiw1LjlsNi40LDIuOEMtNjEyLjYsMzU5LjMtNjEwLjYsMzUxLjctNjEwLjEsMzQ4Ljl6Ii8+PC9nPjwvZz48Zz48Zz48ZGVmcz48Y2lyY2xlIGlkPSJTVkdJRF8xNl8iIGN4PSItNDA3LjMiIGN5PSIzNDYuMyIgcj0iNDIuMiIvPjwvZGVmcz48Y2xpcFBhdGggaWQ9IlNWR0lEXzE3XyI+PHVzZSB4bGluazpocmVmPSIjU1ZHSURfMTZfIiAgb3ZlcmZsb3c9InZpc2libGUiLz48L2NsaXBQYXRoPjxwYXRoIGNsaXAtcGF0aD0idXJsKCNTVkdJRF8xN18pIiBmaWxsPSIjRkZGRkZGIiBkPSJNLTQwMS4xLDM0OS40YzAuMywwLjMsMC41LDAuOCwwLjUsMC44YzcuNCwxLDE4LjEsMCwyNi4yLTEuM2MtNC42LDkuOS0xMywxNi41LTIwLjQsMTYuNWMtMTQuMiwwLTI1LjItMTcuMy0yNS4yLTE3LjNjNC4zLTMuOCwxMS43LTE2LjUsMjIuMS0xNi41czE1LDUuOSwxNSw1LjlsMS4zLTEuOGMwLDAtNC44LTE3LTE4LjgtMTdzLTI4LjUsMjIuNi0zNy4yLDI4YzAsMCwxMiwyOCwzNy43LDI4YzIxLjYsMCwyNy4yLTIwLjksMjguMi0yNmMyLjgtMC41LDUuMy0wLjgsNi45LTFjMC41LTEuMywxLTMuMywwLjgtNi4xYy04LjcsMy4zLTIxLjYsNy4xLTM3LjIsNy4xQy00MDEuNCwzNDguMy00MDEuNCwzNDguOS00MDEuMSwzNDkuNHoiLz48L2c+PC9nPjwvZz48ZyBpZD0iTGF5ZXJfMiI+PHBhdGggZmlsbD0iIzg4ODg4OCIgZD0iTTQ2Ny4zLDIwOS45Yy00LjgsMjQuNC0zMC44LDEyMi42LTEzMy42LDEyMi42Yy0xMjIuNiwwLTE3OC42LTEzMi44LTE3OC42LTEzMi44YzQxLTI0LjksMTEwLjQtMTMyLjMsMTc2LjEtMTMyLjNzODguOCw4MS4yLDg4LjgsODEuMmwtNS42LDguOWMwLDAtMjEuNi0yNy4yLTcxLjItMjcuMnMtODMuNyw1OS44LTEwNC42LDc4LjRjMCwwLDUyLjIsODEuNywxMTkuMyw4MS43YzM2LjEsMCw3NS4xLTMxLjMsOTYuOS03OC40Yy0zOC4yLDUuMy04OC4zLDEwLjItMTIzLjcsNS42YzAsMC0xLjgtMS41LTIuNS0zLjNjLTEtMi4zLTEuMy00LjYtMS4zLTQuNmM3MC4yLDAsMTMwLjUtMTYuNSwxNzEuNS0zMS44QzQ4Ny43LDc3LjYsNDAyLjksMCwzMDAuMSwwYy0xMTAuNCwwLTIwMCw4OS42LTIwMCwyMDBzODkuNiwyMDAsMjAwLDIwMGMxMDguOSwwLDE5Ny41LTg3LDIwMC0xOTUuNEM0OTIuNSwyMDUuOSw0ODEsMjA3LjksNDY3LjMsMjA5Ljl6Ii8+PC9nPjwvc3ZnPg==',
521 'give_cpt_icon' => 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAxOC4wLjAsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+DQo8c3ZnIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeD0iMHB4IiB5PSIwcHgiDQoJIHZpZXdCb3g9IjAgMCAxNTcuMSAxNTcuMiIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgMTU3LjEgMTU3LjI7IiB4bWw6c3BhY2U9InByZXNlcnZlIj4NCjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+DQoJLnN0MHtmaWxsOiM2NkJCNkE7fQ0KCS5zdDF7ZmlsbDojNTQ2RTdBO30NCgkuc3Qye2ZpbGw6dXJsKCNTVkdJRF8xXyk7fQ0KCS5zdDN7ZmlsbDp1cmwoI1NWR0lEXzJfKTt9DQoJLnN0NHtmaWxsOnVybCgjU1ZHSURfM18pO30NCgkuc3Q1e2ZpbGw6dXJsKCNTVkdJRF80Xyk7fQ0KCS5zdDZ7ZmlsbDp1cmwoI1NWR0lEXzVfKTt9DQoJLnN0N3tmaWxsOnVybCgjU1ZHSURfNl8pO30NCgkuc3Q4e2ZpbGw6dXJsKCNTVkdJRF83Xyk7fQ0KCS5zdDl7ZmlsbDp1cmwoI1NWR0lEXzhfKTt9DQoJLnN0MTB7ZmlsbDp1cmwoI1NWR0lEXzlfKTt9DQoJLnN0MTF7ZmlsbDp1cmwoI1NWR0lEXzEwXyk7fQ0KCS5zdDEye2ZpbGw6dXJsKCNTVkdJRF8xMV8pO30NCgkuc3QxM3tmaWxsOnVybCgjU1ZHSURfMTJfKTt9DQoJLnN0MTR7ZmlsbDp1cmwoI1NWR0lEXzEzXyk7fQ0KCS5zdDE1e2ZpbGw6dXJsKCNTVkdJRF8xNF8pO30NCgkuc3QxNntmaWxsOiNGRkZGRkY7fQ0KCS5zdDE3e2ZpbGw6dXJsKCNTVkdJRF8xNV8pO30NCgkuc3QxOHtjbGlwLXBhdGg6dXJsKCNTVkdJRF8xN18pO2ZpbGw6I0ZGRkZGRjt9DQoJLnN0MTl7ZmlsbDojRjFGMkYyO30NCjwvc3R5bGU+DQo8ZyBpZD0iTGF5ZXJfMSI+DQoJPGNpcmNsZSBjbGFzcz0ic3QwIiBjeD0iLTE5OS40IiBjeT0iMTM2LjEiIHI9IjE2LjYiLz4NCgk8Zz4NCgkJPGc+DQoJCQk8cGF0aCBjbGFzcz0ic3QxIiBkPSJNLTM0OC40LDUyLjZ2LTQuOWgxLjljMS41LDAsMi42LDEsMi42LDIuNWMwLDEuNS0xLjEsMi41LTIuNiwyLjVILTM0OC40eiBNLTM0NC45LDUwLjENCgkJCQljMC0wLjktMC41LTEuNS0xLjUtMS41aC0wLjl2My4xaDAuOUMtMzQ1LjUsNTEuNy0zNDQuOSw1MS0zNDQuOSw1MC4xeiIvPg0KCQkJPHBhdGggY2xhc3M9InN0MSIgZD0iTS0zNDIuNiw1Mi42di00LjloMy41djAuOWgtMi40djFoMi40djAuOWgtMi40djEuMWgyLjR2MC45SC0zNDIuNnoiLz4NCgkJCTxwYXRoIGNsYXNzPSJzdDEiIGQ9Ik0tMzMzLjUsNTIuNnYtMy40bC0xLjQsMy40aC0wLjVsLTEuNC0zLjR2My40aC0xdi00LjloMS41bDEuMiwzbDEuMi0zaDEuNXY0LjlILTMzMy41eiIvPg0KCQkJPHBhdGggY2xhc3M9InN0MSIgZD0iTS0zMzEuMSw1MC4xYzAtMS41LDEuMS0yLjUsMi42LTIuNWMxLjUsMCwyLjYsMS4xLDIuNiwyLjVjMCwxLjUtMS4xLDIuNS0yLjYsMi41DQoJCQkJQy0zMzAuMSw1Mi43LTMzMS4xLDUxLjYtMzMxLjEsNTAuMXogTS0zMjcuMSw1MC4xYzAtMC45LTAuNi0xLjYtMS41LTEuNnMtMS41LDAuNy0xLjUsMS42YzAsMC45LDAuNiwxLjYsMS41LDEuNg0KCQkJCVMtMzI3LjEsNTEtMzI3LjEsNTAuMXoiLz4NCgkJCTxwYXRoIGNsYXNzPSJzdDEiIGQ9Ik0tMzI1LDUwLjFjMC0xLjUsMS4xLTIuNSwyLjYtMi41YzEuMSwwLDEuNywwLjYsMi4xLDEuMmwtMC45LDAuNGMtMC4yLTAuNC0wLjYtMC43LTEuMi0wLjcNCgkJCQljLTAuOSwwLTEuNSwwLjctMS41LDEuNmMwLDAuOSwwLjYsMS42LDEuNSwxLjZjMC41LDAsMC45LTAuMywxLjItMC43bDAuOSwwLjRjLTAuNCwwLjYtMSwxLjItMi4xLDEuMg0KCQkJCUMtMzIzLjgsNTIuNy0zMjUsNTEuNi0zMjUsNTAuMXoiLz4NCgkJCTxwYXRoIGNsYXNzPSJzdDEiIGQ9Ik0tMzE2LjMsNTIuNmwtMS0xLjdoLTAuOHYxLjdoLTF2LTQuOWgyLjNjMSwwLDEuNiwwLjcsMS42LDEuNmMwLDAuOS0wLjUsMS4zLTEuMSwxLjVsMS4xLDEuOUgtMzE2LjN6DQoJCQkJIE0tMzE2LjIsNDkuMmMwLTAuNC0wLjMtMC43LTAuNy0wLjdoLTEuMXYxLjNoMS4xQy0zMTYuNiw0OS45LTMxNi4yLDQ5LjctMzE2LjIsNDkuMnoiLz4NCgkJCTxwYXRoIGNsYXNzPSJzdDEiIGQ9Ik0tMzEwLjQsNTIuNmwtMC4zLTAuOGgtMi4xbC0wLjMsMC44aC0xLjJsMS45LTQuOWgxLjNsMS45LDQuOUgtMzEwLjR6IE0tMzExLjgsNDguN2wtMC44LDIuMWgxLjUNCgkJCQlMLTMxMS44LDQ4Ljd6Ii8+DQoJCQk8cGF0aCBjbGFzcz0ic3QxIiBkPSJNLTMwNy42LDUyLjZ2LTRoLTEuNHYtMC45aDMuOXYwLjloLTEuNHY0SC0zMDcuNnoiLz4NCgkJCTxwYXRoIGNsYXNzPSJzdDEiIGQ9Ik0tMzAzLjksNTIuNnYtNC45aDF2NC45SC0zMDMuOXoiLz4NCgkJCTxwYXRoIGNsYXNzPSJzdDEiIGQ9Ik0tMzAxLjUsNTIuNnYtMC44bDIuMy0zLjFoLTIuM3YtMC45aDMuN3YwLjhsLTIuMywzLjJoMi40djAuOUgtMzAxLjV6Ii8+DQoJCQk8cGF0aCBjbGFzcz0ic3QxIiBkPSJNLTI5Ni40LDUyLjZ2LTQuOWgxdjQuOUgtMjk2LjR6Ii8+DQoJCQk8cGF0aCBjbGFzcz0ic3QxIiBkPSJNLTI5MC41LDUyLjZsLTIuMy0zLjJ2My4yaC0xdi00LjloMS4xbDIuMywzLjF2LTMuMWgxdjQuOUgtMjkwLjV6Ii8+DQoJCQk8cGF0aCBjbGFzcz0ic3QxIiBkPSJNLTI4OC4yLDUwLjFjMC0xLjYsMS4yLTIuNSwyLjYtMi41YzEsMCwxLjcsMC41LDIsMS4xbC0wLjksMC41Yy0wLjItMC4zLTAuNi0wLjYtMS4yLTAuNg0KCQkJCWMtMC45LDAtMS41LDAuNy0xLjUsMS42YzAsMC45LDAuNiwxLjYsMS41LDEuNmMwLjQsMCwwLjgtMC4yLDEtMC40di0wLjZoLTEuM3YtMC45aDIuM3YxLjljLTAuNSwwLjYtMS4yLDAuOS0yLjEsMC45DQoJCQkJQy0yODcsNTIuNy0yODguMiw1MS43LTI4OC4yLDUwLjF6Ii8+DQoJCQk8cGF0aCBjbGFzcz0ic3QxIiBkPSJNLTI3OS45LDUwLjFjMC0xLjYsMS4yLTIuNSwyLjYtMi41YzEsMCwxLjcsMC41LDIsMS4xbC0wLjksMC41Yy0wLjItMC4zLTAuNi0wLjYtMS4yLTAuNg0KCQkJCWMtMC45LDAtMS41LDAuNy0xLjUsMS42YzAsMC45LDAuNiwxLjYsMS41LDEuNmMwLjQsMCwwLjgtMC4yLDEtMC40di0wLjZoLTEuM3YtMC45aDIuM3YxLjljLTAuNSwwLjYtMS4yLDAuOS0yLjEsMC45DQoJCQkJQy0yNzguNyw1Mi43LTI3OS45LDUxLjctMjc5LjksNTAuMXoiLz4NCgkJCTxwYXRoIGNsYXNzPSJzdDEiIGQ9Ik0tMjczLjgsNTIuNnYtNC45aDMuNXYwLjloLTIuNHYxaDIuNHYwLjloLTIuNHYxLjFoMi40djAuOUgtMjczLjh6Ii8+DQoJCQk8cGF0aCBjbGFzcz0ic3QxIiBkPSJNLTI2NS42LDUyLjZsLTIuMy0zLjJ2My4yaC0xdi00LjloMS4xbDIuMywzLjF2LTMuMWgxdjQuOUgtMjY1LjZ6Ii8+DQoJCQk8cGF0aCBjbGFzcz0ic3QxIiBkPSJNLTI2Myw1Mi42di00LjloMy41djAuOWgtMi40djFoMi40djAuOWgtMi40djEuMWgyLjR2MC45SC0yNjN6Ii8+DQoJCQk8cGF0aCBjbGFzcz0ic3QxIiBkPSJNLTI1NS40LDUyLjZsLTEtMS43aC0wLjh2MS43aC0xdi00LjloMi4zYzEsMCwxLjYsMC43LDEuNiwxLjZjMCwwLjktMC41LDEuMy0xLjEsMS41bDEuMSwxLjlILTI1NS40eg0KCQkJCSBNLTI1NS4zLDQ5LjJjMC0wLjQtMC4zLTAuNy0wLjctMC43aC0xLjF2MS4zaDEuMUMtMjU1LjYsNDkuOS0yNTUuMyw0OS43LTI1NS4zLDQ5LjJ6Ii8+DQoJCQk8cGF0aCBjbGFzcz0ic3QxIiBkPSJNLTI1My4xLDUwLjFjMC0xLjUsMS4xLTIuNSwyLjYtMi41YzEuNSwwLDIuNiwxLjEsMi42LDIuNWMwLDEuNS0xLjEsMi41LTIuNiwyLjUNCgkJCQlDLTI1Mi4xLDUyLjctMjUzLjEsNTEuNi0yNTMuMSw1MC4xeiBNLTI0OS4xLDUwLjFjMC0wLjktMC42LTEuNi0xLjUtMS42Yy0wLjksMC0xLjUsMC43LTEuNSwxLjZjMCwwLjksMC42LDEuNiwxLjUsMS42DQoJCQkJQy0yNDkuNyw1MS43LTI0OS4xLDUxLTI0OS4xLDUwLjF6Ii8+DQoJCQk8cGF0aCBjbGFzcz0ic3QxIiBkPSJNLTI0Ny4xLDUxLjlsMC42LTAuOGMwLjMsMC40LDAuOSwwLjcsMS42LDAuN2MwLjYsMCwwLjktMC4zLDAuOS0wLjVjMC0wLjktMi44LTAuMy0yLjgtMi4xDQoJCQkJYzAtMC44LDAuNy0xLjUsMS45LTEuNWMwLjgsMCwxLjQsMC4yLDEuOSwwLjdsLTAuNiwwLjhjLTAuNC0wLjQtMC45LTAuNS0xLjQtMC41Yy0wLjQsMC0wLjcsMC4yLTAuNywwLjVjMCwwLjgsMi44LDAuMywyLjgsMi4xDQoJCQkJYzAsMC45LTAuNiwxLjYtMiwxLjZDLTI0NS45LDUyLjctMjQ2LjYsNTIuMy0yNDcuMSw1MS45eiIvPg0KCQkJPHBhdGggY2xhc3M9InN0MSIgZD0iTS0yNDEuOCw1Mi42di00LjloMXY0LjlILTI0MS44eiIvPg0KCQkJPHBhdGggY2xhc3M9InN0MSIgZD0iTS0yMzguMSw1Mi42di00aC0xLjR2LTAuOWgzLjl2MC45aC0xLjR2NEgtMjM4LjF6Ii8+DQoJCQk8cGF0aCBjbGFzcz0ic3QxIiBkPSJNLTIzMyw1Mi42di0ybC0xLjktMi45aDEuMmwxLjIsMmwxLjItMmgxLjJsLTEuOSwyLjl2MkgtMjMzeiIvPg0KCQk8L2c+DQoJCTxnPg0KCQkJPGc+DQoJCQkJPHBhdGggY2xhc3M9InN0MCIgZD0iTS0yMzAuMywxNy40bC0wLjUsMGwwLjEsMC41YzAuOCwzLjksMC4xLDkuNy0yLjksMTMuM2MtMS43LDIuMS0zLjksMy4yLTYuNiwzLjJjLTQuMywwLTUuOS01LjEtNi4xLTEwLjkNCgkJCQkJYzctMS43LDExLjctNi4xLDExLjctMTEuMmMwLTMuNy0xLjEtOS44LTguNC05LjhjLTYuOSwwLTEwLjQsMTAuMy0xMS4xLDE3LjVjLTMuNS0wLjEtNi4xLTEuNy03LjctMy4yYzAuNi0yLjUsMC45LTQuOCwwLjktNi45DQoJCQkJCWMwLTIuOS0yLTQuMi0zLjktNC4yYy0yLjcsMC01LjUsMi42LTUuNSw3LjZjMCwzLDEuMSw1LjUsMy40LDcuM2MtMiw0LjctNS40LDguNy02LjUsMTBjLTAuOS0xLjktMy44LTguOC00LjctMTYuMw0KCQkJCQljMS4xLTMsMS43LTUuNSwxLjctNi43YzAtMS45LTEuMi0zLTMuMi0zYy0yLjcsMC03LDEuNy03LjEsMS44bC0wLjIsMC4xbDAsMC4zYzAsMC4xLDEuMyw2LjEsMi42LDEyLjcNCgkJCQkJYy0yLjUsNC4xLTYuOSwxMC45LTkuMSwxMC45Yy00LDAsMi42LTIwLjUtMC4zLTIxLjJjLTAuMSwwLTAuMiwwLTAuMywwLjFjLTEuNCwwLjktMTcuMSw5LjYtMzgsOS42YzAsMCwwLDAuNCwwLjIsMC44DQoJCQkJCWMwLjEsMC4zLDAuNCwwLjYsMC40LDAuNmM1LjksMC43LDE0LjMtMC4xLDIwLjctMWMtMy43LDcuOS0xMC4yLDEzLjEtMTYuMiwxMy4xYy0xMS4zLDAtMjAtMTMuNy0yMC0xMy43DQoJCQkJCWMzLjUtMy4xLDkuMi0xMy4xLDE3LjYtMTMuMWM4LjMsMCwxMS45LDQuNiwxMS45LDQuNmwwLjktMS41YzAsMC0zLjktMTMuNi0xNC45LTEzLjZjLTExLDAtMjIuNywxOC0yOS41LDIyLjINCgkJCQkJYzAsMCw5LjQsMjIuMiwyOS45LDIyLjJjMTcuMiwwLDIxLjYtMTYuNSwyMi40LTIwLjVjNC4yLTAuNiw3LjEtMS4yLDcuMS0xLjJzLTEuMSw4LjQtMS4xLDExLjljMCwzLjUsMy45LDcuMiw3LjEsNy4yDQoJCQkJCWMyLjcsMCw4LjItNS42LDEyLjItMTIuNGwwLjIsMC44YzIuMSw3LjcsNC43LDExLjcsNy44LDExLjdjMy4xLDAsOC4yLTYuNCwxMS41LTE0LjVjMy4zLDEuNCw3LjIsMS44LDkuNSwxLjkNCgkJCQkJYzAuOSwxMy45LDEyLjUsMTQuMywxMy45LDE0LjNjOC42LDAsMTUuOS02LjIsMTUuOS0xMy41Qy0yMjQuMywxNy41LTIzMC4yLDE3LjQtMjMwLjMsMTcuNHogTS0yNDAuOCwxMS42YzAsMC0wLjEsNC42LTUuMyw2LjkNCgkJCQkJYzAuNS02LjEsMi0xMS42LDMtMTEuNkMtMjQyLDctMjQwLjgsOC43LTI0MC44LDExLjZ6Ii8+DQoJCQkJPHBhdGggY2xhc3M9InN0MCIgZD0iTS0zMDAuNyw2LjFjMCwwLjIsMC4xLDAuMywwLjMsMC40YzQuMSwwLjYsNi44LTAuNyw2LjgtNy4zYzAtNi4yLTYuNC0xLjMtNy42LTAuNA0KCQkJCQljLTAuMSwwLjEtMC4xLDAuMi0wLjEsMC40Qy0zMDAuMiwxLjYtMzAwLjYsNS4xLTMwMC43LDYuMXoiLz4NCgkJCTwvZz4NCgkJCTxsaW5lYXJHcmFkaWVudCBpZD0iU1ZHSURfMV8iIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiB4MT0iLTI4OS41ODQ0IiB5MT0iMjYuNzY4IiB4Mj0iLTI4Mi44ODIzIiB5Mj0iMjQuNTM0Ij4NCgkJCQk8c3RvcCAgb2Zmc2V0PSIwIiBzdHlsZT0ic3RvcC1jb2xvcjojNjZCQjZBIi8+DQoJCQkJPHN0b3AgIG9mZnNldD0iMSIgc3R5bGU9InN0b3AtY29sb3I6IzM3OEY0MyIvPg0KCQkJPC9saW5lYXJHcmFkaWVudD4NCgkJCTxwYXRoIGNsYXNzPSJzdDIiIGQ9Ik0tMjg0LjIsMTkuNGMtMS42LDIuNy00LDYuNC02LjEsOC43YzAuNSwxLjIsMS4xLDIuNywxLjcsMy45YzEuOS0yLjEsMy44LTQuOCw1LjUtNy42TC0yODQuMiwxOS40eiIvPg0KCQkJPGxpbmVhckdyYWRpZW50IGlkPSJTVkdJRF8yXyIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIHgxPSItMjY5LjAxOTQiIHkxPSIyOC40Nzc3IiB4Mj0iLTI2NS4xMzIyIiB5Mj0iMjEuNjQxNiI+DQoJCQkJPHN0b3AgIG9mZnNldD0iMCIgc3R5bGU9InN0b3AtY29sb3I6IzY2QkI2QSIvPg0KCQkJCTxzdG9wICBvZmZzZXQ9IjEiIHN0eWxlPSJzdG9wLWNvbG9yOiMzNzhGNDMiLz4NCgkJCTwvbGluZWFyR3JhZGllbnQ+DQoJCQk8cGF0aCBjbGFzcz0ic3QzIiBkPSJNLTI2NS4yLDIxLjVjLTAuOC0wLjQtMS41LTEtMS41LTFjLTEuNCwzLjEtMy4zLDYtNC44LDcuOWMwLjcsMSwxLjksMi4zLDIuOCwzLjNjMS44LTIuNSwzLjctNS44LDUuMS05LjMNCgkJCQlDLTI2My41LDIyLjMtMjY0LjQsMjItMjY1LjIsMjEuNXoiLz4NCgkJCTxsaW5lYXJHcmFkaWVudCBpZD0iU1ZHSURfM18iIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiB4MT0iLTI0OC42MjU0IiB5MT0iMzEuNjE0NyIgeDI9Ii0yNDkuNDI5NyIgeTI9IjI0LjQ2NTkiPg0KCQkJCTxzdG9wICBvZmZzZXQ9IjAiIHN0eWxlPSJzdG9wLWNvbG9yOiM2NkJCNkEiLz4NCgkJCQk8c3RvcCAgb2Zmc2V0PSIxIiBzdHlsZT0ic3RvcC1jb2xvcjojMzc4RjQzIi8+DQoJCQk8L2xpbmVhckdyYWRpZW50Pg0KCQkJPHBhdGggY2xhc3M9InN0NCIgZD0iTS0yNDYuMiwyMy41YzAsMC0yLDAuNC00LDAuNmMtMiwwLjItMy45LDAuMS0zLjksMC4xYzAuMyw0LDEuNCw2LjgsMi45LDguOWw3LjMtMC42DQoJCQkJQy0yNDUuNCwzMC41LTI0Ni4xLDI3LjEtMjQ2LjIsMjMuNXoiLz4NCgkJCTxsaW5lYXJHcmFkaWVudCBpZD0iU1ZHSURfNF8iIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiB4MT0iLTI0OS43MjY3IiB5MT0iMTYuNDE5IiB4Mj0iLTI0OS43MjY3IiB5Mj0iMjMuNjIzNyI+DQoJCQkJPHN0b3AgIG9mZnNldD0iMCIgc3R5bGU9InN0b3AtY29sb3I6IzY2QkI2QSIvPg0KCQkJCTxzdG9wICBvZmZzZXQ9IjEiIHN0eWxlPSJzdG9wLWNvbG9yOiMzNzhGNDMiLz4NCgkJCTwvbGluZWFyR3JhZGllbnQ+DQoJCQk8cGF0aCBjbGFzcz0ic3Q1IiBkPSJNLTI1MiwxMS4zTC0yNTIsMTEuM2MtMC4yLDAuNi0wLjUsMS4zLTAuNywyYzAsMC4yLTAuMSwwLjMtMC4xLDAuNWMtMC40LDEuMy0wLjcsMi42LTAuOSwzLjkNCgkJCQljMCwwLjItMC4xLDAuMy0wLjEsMC41Yy0wLjEsMC42LTAuMiwxLjItMC4yLDEuOGM0LjcsMCw3LjktMS40LDcuOS0xLjRjMC0wLjUsMC4xLTAuOSwwLjEtMS40YzAtMC4xLDAtMC4yLDAtMC4zDQoJCQkJYzAtMC40LDAuMS0wLjcsMC4xLTEuMWMwLTAuMSwwLTAuMiwwLTAuM2MwLjEtMC45LDAuMy0xLjgsMC40LTIuNkwtMjUyLDExLjN6IE0tMjU0LjEsMTkuOUMtMjU0LjEsMTkuOS0yNTQuMSwxOS45LTI1NC4xLDE5LjkNCgkJCQlMLTI1NC4xLDE5LjlDLTI1NC4xLDE5LjktMjU0LjEsMTkuOS0yNTQuMSwxOS45eiIvPg0KCQkJPGxpbmVhckdyYWRpZW50IGlkPSJTVkdJRF81XyIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIHgxPSItMzEzLjAyNzIiIHkxPSIyOC4yMjc4IiB4Mj0iLTMxMy4wMjcyIiB5Mj0iMTkuMjkxNyI+DQoJCQkJPHN0b3AgIG9mZnNldD0iMCIgc3R5bGU9InN0b3AtY29sb3I6IzY2QkI2QSIvPg0KCQkJCTxzdG9wICBvZmZzZXQ9IjEiIHN0eWxlPSJzdG9wLWNvbG9yOiMzNzhGNDMiLz4NCgkJCTwvbGluZWFyR3JhZGllbnQ+DQoJCQk8cGF0aCBjbGFzcz0ic3Q2IiBkPSJNLTMxNy43LDI4LjZsNC40LDEuOWMyLjEtMywzLjQtNi4xLDQuMi04LjVsMC44LTMuMmwtMi4xLDAuM0MtMzEyLjMsMjMuMS0zMTQuOSwyNi4zLTMxNy43LDI4LjZ6Ii8+DQoJCQk8bGluZWFyR3JhZGllbnQgaWQ9IlNWR0lEXzZfIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9Ii0yNzcuNTIzOSIgeTE9IjkuNzg4IiB4Mj0iLTI3OC42NDYzIiB5Mj0iMTEuNTc1NiI+DQoJCQkJPHN0b3AgIG9mZnNldD0iMCIgc3R5bGU9InN0b3AtY29sb3I6IzY2QkI2QSIvPg0KCQkJCTxzdG9wICBvZmZzZXQ9IjEiIHN0eWxlPSJzdG9wLWNvbG9yOiMzNzhGNDMiLz4NCgkJCTwvbGluZWFyR3JhZGllbnQ+DQoJCQk8cGF0aCBjbGFzcz0ic3Q3IiBkPSJNLTI3Ny45LDE0LjJjMS4xLTMsMS43LTUuNSwxLjctNi43YzAtMC4xLDAtMC4yLDAtMC4zbC0yLjYsMC44Qy0yNzguOCw4LTI3OC43LDkuNy0yNzcuOSwxNC4yeiIvPg0KCQkJPGxpbmVhckdyYWRpZW50IGlkPSJTVkdJRF83XyIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIHgxPSItMjYyLjU1MjkiIHkxPSIxMy41MjQ4IiB4Mj0iLTI2My4xODY2IiB5Mj0iMTUuOTMyNiI+DQoJCQkJPHN0b3AgIG9mZnNldD0iMCIgc3R5bGU9InN0b3AtY29sb3I6IzY2QkI2QSIvPg0KCQkJCTxzdG9wICBvZmZzZXQ9IjEiIHN0eWxlPSJzdG9wLWNvbG9yOiMzNzhGNDMiLz4NCgkJCTwvbGluZWFyR3JhZGllbnQ+DQoJCQk8cGF0aCBjbGFzcz0ic3Q4IiBkPSJNLTI2My45LDEyYzAsMC0wLjcsMi4xLDIuMiw0LjdjMC41LTIuMSwwLjgtNC4yLDAuOS02TC0yNjMuOSwxMnoiLz4NCgkJPC9nPg0KCTwvZz4NCgk8Zz4NCgkJPGc+DQoJCQk8cGF0aCBjbGFzcz0ic3QwIiBkPSJNLTM1Ni44LDEzNi4ybC0wLjIsMGwwLDAuMmMwLjMsMS43LDAsNC4xLTEuMiw1LjdjLTAuNywwLjktMS43LDEuNC0yLjgsMS40Yy0xLjgsMC0yLjUtMi4yLTIuNi00LjYNCgkJCQljMy0wLjcsNS0yLjYsNS00LjhjMC0xLjYtMC41LTQuMi0zLjYtNC4yYy0yLjksMC00LjQsNC40LTQuNyw3LjRjLTEuNSwwLTIuNi0wLjctMy4zLTEuNGMwLjMtMS4xLDAuNC0yLDAuNC0yLjkNCgkJCQljMC0xLjItMC45LTEuOC0xLjYtMS44Yy0xLjIsMC0yLjMsMS4xLTIuMywzLjJjMCwxLjMsMC41LDIuMywxLjUsMy4xYy0wLjksMi0yLjMsMy43LTIuOCw0LjJjLTAuNC0wLjgtMS42LTMuOC0yLTYuOQ0KCQkJCWMwLjUtMS4zLDAuNy0yLjMsMC43LTIuOGMwLTAuOC0wLjUtMS4zLTEuNC0xLjNjLTEuMiwwLTMsMC43LTMsMC44bC0wLjEsMC4xbDAsMC4xYzAsMCwwLjUsMi42LDEuMSw1LjQNCgkJCQljLTEuMSwxLjctMi45LDQuNi0zLjksNC42Yy0xLjcsMCwxLjEtOC43LTAuMS05YzAsMC0wLjEsMC0wLjEsMGMtMC42LDAuNC03LjMsNC4xLTE2LjEsNC4xYzAsMCwwLDAuMiwwLjEsMC4zDQoJCQkJYzAuMSwwLjEsMC4yLDAuMiwwLjIsMC4yYzIuNSwwLjMsNi4xLDAsOC44LTAuNGMtMS42LDMuMy00LjMsNS42LTYuOSw1LjZjLTQuOCwwLTguNS01LjgtOC41LTUuOGMxLjUtMS4zLDMuOS01LjYsNy41LTUuNg0KCQkJCWMzLjUsMCw1LjEsMS45LDUuMSwxLjlsMC40LTAuNmMwLDAtMS43LTUuOC02LjMtNS44cy05LjYsNy43LTEyLjUsOS40YzAsMCw0LDkuNSwxMi43LDkuNWM3LjMsMCw5LjItNyw5LjUtOC43DQoJCQkJYzEuOC0wLjMsMy0wLjUsMy0wLjVzLTAuNCwzLjYtMC40LDUuMXMxLjYsMy4xLDMsMy4xYzEuMiwwLDMuNS0yLjQsNS4yLTUuM2wwLjEsMC4zYzAuOSwzLjMsMiw1LDMuMyw1YzEuMywwLDMuNS0yLjcsNC45LTYuMQ0KCQkJCWMxLjQsMC42LDMuMSwwLjgsNCwwLjhjMC40LDUuOSw1LjMsNi4xLDUuOSw2LjFjMy43LDAsNi44LTIuNiw2LjgtNS43Qy0zNTQuMywxMzYuMy0zNTYuOCwxMzYuMi0zNTYuOCwxMzYuMnogTS0zNjEuMiwxMzMuNw0KCQkJCWMwLDAsMCwyLTIuMywzYzAuMi0yLjYsMC44LTQuOSwxLjMtNC45Qy0zNjEuOCwxMzEuOC0zNjEuMiwxMzIuNS0zNjEuMiwxMzMuN3oiLz4NCgkJCTxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik0tMzg2LjcsMTMxLjRjMCwwLjEsMCwwLjEsMC4xLDAuMmMxLjcsMC4yLDIuOS0wLjMsMi45LTMuMWMwLTIuNi0yLjctMC42LTMuMi0wLjJjMCwwLTAuMSwwLjEsMCwwLjINCgkJCQlDLTM4Ni41LDEyOS41LTM4Ni43LDEzMC45LTM4Ni43LDEzMS40eiIvPg0KCQk8L2c+DQoJCTxsaW5lYXJHcmFkaWVudCBpZD0iU1ZHSURfOF8iIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiB4MT0iLTM4MS45OTkzIiB5MT0iMTQwLjE3NjkiIHgyPSItMzc5LjE1MDQiIHkyPSIxMzkuMjI3MiI+DQoJCQk8c3RvcCAgb2Zmc2V0PSIwIiBzdHlsZT0ic3RvcC1jb2xvcjojNjZCQjZBIi8+DQoJCQk8c3RvcCAgb2Zmc2V0PSIxIiBzdHlsZT0ic3RvcC1jb2xvcjojMzc4RjQzIi8+DQoJCTwvbGluZWFyR3JhZGllbnQ+DQoJCTxwYXRoIGNsYXNzPSJzdDkiIGQ9Ik0tMzc5LjcsMTM3Yy0wLjcsMS4xLTEuNywyLjctMi42LDMuN2MwLjIsMC41LDAuNSwxLjEsMC43LDEuN2MwLjgtMC45LDEuNi0yLDIuNC0zLjJMLTM3OS43LDEzN3oiLz4NCgkJPGxpbmVhckdyYWRpZW50IGlkPSJTVkdJRF85XyIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIHgxPSItMzczLjI1NzUiIHkxPSIxNDAuOTAzNyIgeDI9Ii0zNzEuNjA1MSIgeTI9IjEzNy45OTc4Ij4NCgkJCTxzdG9wICBvZmZzZXQ9IjAiIHN0eWxlPSJzdG9wLWNvbG9yOiM2NkJCNkEiLz4NCgkJCTxzdG9wICBvZmZzZXQ9IjEiIHN0eWxlPSJzdG9wLWNvbG9yOiMzNzhGNDMiLz4NCgkJPC9saW5lYXJHcmFkaWVudD4NCgkJPHBhdGggY2xhc3M9InN0MTAiIGQ9Ik0tMzcxLjYsMTM3LjljLTAuMy0wLjItMC42LTAuNC0wLjYtMC40Yy0wLjYsMS4zLTEuNCwyLjUtMiwzLjRjMC4zLDAuNCwwLjgsMSwxLjIsMS40DQoJCQljMC44LTEuMSwxLjYtMi40LDIuMi00Qy0zNzAuOSwxMzguMy0zNzEuMywxMzguMS0zNzEuNiwxMzcuOXoiLz4NCgkJPGxpbmVhckdyYWRpZW50IGlkPSJTVkdJRF8xMF8iIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiB4MT0iLTM2NC41ODg0IiB5MT0iMTQyLjIzNzIiIHgyPSItMzY0LjkzMDMiIHkyPSIxMzkuMTk4MyI+DQoJCQk8c3RvcCAgb2Zmc2V0PSIwIiBzdHlsZT0ic3RvcC1jb2xvcjojNjZCQjZBIi8+DQoJCQk8c3RvcCAgb2Zmc2V0PSIxIiBzdHlsZT0ic3RvcC1jb2xvcjojMzc4RjQzIi8+DQoJCTwvbGluZWFyR3JhZGllbnQ+DQoJCTxwYXRoIGNsYXNzPSJzdDExIiBkPSJNLTM2My42LDEzOC44YzAsMC0wLjgsMC4yLTEuNywwLjNjLTAuOCwwLjEtMS43LDAtMS43LDBjMC4xLDEuNywwLjYsMi45LDEuMiwzLjhsMy4xLTAuMw0KCQkJQy0zNjMuMiwxNDEuOC0zNjMuNSwxNDAuMy0zNjMuNiwxMzguOHoiLz4NCgkJPGxpbmVhckdyYWRpZW50IGlkPSJTVkdJRF8xMV8iIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiB4MT0iLTM2NS4wNTY2IiB5MT0iMTM1Ljc3NzciIHgyPSItMzY1LjA1NjYiIHkyPSIxMzguODQwMyI+DQoJCQk8c3RvcCAgb2Zmc2V0PSIwIiBzdHlsZT0ic3RvcC1jb2xvcjojNjZCQjZBIi8+DQoJCQk8c3RvcCAgb2Zmc2V0PSIxIiBzdHlsZT0ic3RvcC1jb2xvcjojMzc4RjQzIi8+DQoJCTwvbGluZWFyR3JhZGllbnQ+DQoJCTxwYXRoIGNsYXNzPSJzdDEyIiBkPSJNLTM2NiwxMzMuNkwtMzY2LDEzMy42Yy0wLjEsMC4zLTAuMiwwLjYtMC4zLDAuOGMwLDAuMSwwLDAuMS0wLjEsMC4yYy0wLjIsMC42LTAuMywxLjEtMC40LDEuNw0KCQkJYzAsMC4xLDAsMC4xLDAsMC4yYzAsMC4zLTAuMSwwLjUtMC4xLDAuOGMyLDAsMy40LTAuNiwzLjQtMC42YzAtMC4yLDAtMC40LDAuMS0wLjZjMCwwLDAtMC4xLDAtMC4xYzAtMC4yLDAtMC4zLDAuMS0wLjQNCgkJCWMwLDAsMC0wLjEsMC0wLjFjMC4xLTAuNCwwLjEtMC44LDAuMi0xLjFMLTM2NiwxMzMuNnogTS0zNjYuOSwxMzcuM0MtMzY2LjksMTM3LjMtMzY2LjksMTM3LjMtMzY2LjksMTM3LjNMLTM2Ni45LDEzNy4zDQoJCQlDLTM2Ni45LDEzNy4zLTM2Ni45LDEzNy4zLTM2Ni45LDEzNy4zeiIvPg0KCQk8bGluZWFyR3JhZGllbnQgaWQ9IlNWR0lEXzEyXyIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIHgxPSItMzkxLjk2NDQiIHkxPSIxNDAuNzk3NSIgeDI9Ii0zOTEuOTY0NCIgeTI9IjEzNi45OTg5Ij4NCgkJCTxzdG9wICBvZmZzZXQ9IjAiIHN0eWxlPSJzdG9wLWNvbG9yOiM2NkJCNkEiLz4NCgkJCTxzdG9wICBvZmZzZXQ9IjEiIHN0eWxlPSJzdG9wLWNvbG9yOiMzNzhGNDMiLz4NCgkJPC9saW5lYXJHcmFkaWVudD4NCgkJPHBhdGggY2xhc3M9InN0MTMiIGQ9Ik0tMzk0LDE0MWwxLjksMC44YzAuOS0xLjMsMS40LTIuNiwxLjgtMy42bDAuMy0xLjRsLTAuOSwwLjFDLTM5MS43LDEzOC42LTM5Mi43LDE0MC0zOTQsMTQxeiIvPg0KCQk8bGluZWFyR3JhZGllbnQgaWQ9IlNWR0lEXzEzXyIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIHgxPSItMzc2Ljg3MjYiIHkxPSIxMzIuOTU5IiB4Mj0iLTM3Ny4zNDk4IiB5Mj0iMTMzLjcxODkiPg0KCQkJPHN0b3AgIG9mZnNldD0iMCIgc3R5bGU9InN0b3AtY29sb3I6IzY2QkI2QSIvPg0KCQkJPHN0b3AgIG9mZnNldD0iMSIgc3R5bGU9InN0b3AtY29sb3I6IzM3OEY0MyIvPg0KCQk8L2xpbmVhckdyYWRpZW50Pg0KCQk8cGF0aCBjbGFzcz0ic3QxNCIgZD0iTS0zNzcsMTM0LjhjMC41LTEuMywwLjctMi4zLDAuNy0yLjhjMCwwLDAtMC4xLDAtMC4xbC0xLjEsMC4zQy0zNzcuNCwxMzIuMi0zNzcuNCwxMzIuOS0zNzcsMTM0Ljh6Ii8+DQoJCTxsaW5lYXJHcmFkaWVudCBpZD0iU1ZHSURfMTRfIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9Ii0zNzAuNTA4OCIgeTE9IjEzNC41NDc1IiB4Mj0iLTM3MC43NzgxIiB5Mj0iMTM1LjU3MSI+DQoJCQk8c3RvcCAgb2Zmc2V0PSIwIiBzdHlsZT0ic3RvcC1jb2xvcjojNjZCQjZBIi8+DQoJCQk8c3RvcCAgb2Zmc2V0PSIxIiBzdHlsZT0ic3RvcC1jb2xvcjojMzc4RjQzIi8+DQoJCTwvbGluZWFyR3JhZGllbnQ+DQoJCTxwYXRoIGNsYXNzPSJzdDE1IiBkPSJNLTM3MS4xLDEzMy45YzAsMC0wLjMsMC45LDAuOSwyYzAuMi0wLjksMC40LTEuOCwwLjQtMi42TC0zNzEuMSwxMzMuOXoiLz4NCgk8L2c+DQoJPGc+DQoJCTxyZWN0IHg9Ii0zMTMuNCIgeT0iMTE1LjEiIGNsYXNzPSJzdDE2IiB3aWR0aD0iNDIiIGhlaWdodD0iNDIiLz4NCgkJPGc+DQoJCQk8cGF0aCBjbGFzcz0ic3QwIiBkPSJNLTI5Mi42LDEzNy41YzAuMSwwLjIsMC4yLDAuMywwLjIsMC4zYzMuNCwwLjQsOC4zLDAsMTItMC42Yy0yLjEsNC42LTUuOSw3LjYtOS40LDcuNg0KCQkJCWMtNi41LDAtMTEuNi03LjktMTEuNi03LjljMi0xLjgsNS40LTcuNiwxMC4yLTcuNnM2LjksMi42LDYuOSwyLjZsMC41LTAuOWMwLDAtMi4zLTcuOS04LjYtNy45Yy02LjQsMC0xMy4xLDEwLjQtMTcuMSwxMi44DQoJCQkJYzAsMCw1LjQsMTIuOSwxNy4zLDEyLjljMTAsMCwxMi41LTkuNSwxMy0xMS45YzEuMy0wLjIsMi40LTAuNCwzLjItMC41YzAuMi0wLjUsMC41LTEuNSwwLjMtMi44Yy00LDEuNS0xMCwzLjMtMTcuMSwzLjMNCgkJCQlDLTI5Mi43LDEzNy0yOTIuNywxMzcuMi0yOTIuNiwxMzcuNXoiLz4NCgkJCTxsaW5lYXJHcmFkaWVudCBpZD0iU1ZHSURfMTVfIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9Ii0yODEuNzk2MiIgeTE9IjE0Mi40ODEzIiB4Mj0iLTI4MS43OTYyIiB5Mj0iMTM3LjM0ODkiPg0KCQkJCTxzdG9wICBvZmZzZXQ9IjAiIHN0eWxlPSJzdG9wLWNvbG9yOiM2NkJCNkEiLz4NCgkJCQk8c3RvcCAgb2Zmc2V0PSIxIiBzdHlsZT0ic3RvcC1jb2xvcjojMzc4RjQzIi8+DQoJCQk8L2xpbmVhckdyYWRpZW50Pg0KCQkJPHBhdGggY2xhc3M9InN0MTciIGQ9Ik0tMjc5LjEsMTM3LjFsLTEuMiwwLjFjMCwwLjEtMC4xLDAuMi0wLjIsMC4zYy0wLjIsMC40LTAuNCwwLjctMC42LDEuMWMtMC4xLDAuMi0wLjIsMC4zLTAuMywwLjUNCgkJCQljLTAuMiwwLjQtMC41LDAuOC0wLjgsMS4xYy0wLjEsMC4xLTAuMSwwLjEtMC4yLDAuMmMtMC43LDAuOS0xLjQsMS43LTIuMiwyLjNsMi41LDEuMUMtMjgwLjEsMTQxLjItMjc5LjMsMTM4LjItMjc5LjEsMTM3LjF6Ii8+DQoJCTwvZz4NCgk8L2c+DQoJPGc+DQoJCTxkZWZzPg0KCQkJPGNpcmNsZSBpZD0iU1ZHSURfMTZfIiBjeD0iLTE5OS40IiBjeT0iMTM2LjEiIHI9IjE2LjYiLz4NCgkJPC9kZWZzPg0KCQk8Y2xpcFBhdGggaWQ9IlNWR0lEXzE3XyI+DQoJCQk8dXNlIHhsaW5rOmhyZWY9IiNTVkdJRF8xNl8iICBzdHlsZT0ib3ZlcmZsb3c6dmlzaWJsZTsiLz4NCgkJPC9jbGlwUGF0aD4NCgkJPHBhdGggY2xhc3M9InN0MTgiIGQ9Ik0tMTk3LDEzNy4zYzAuMSwwLjEsMC4yLDAuMywwLjIsMC4zYzIuOSwwLjQsNy4xLDAsMTAuMy0wLjVjLTEuOCwzLjktNS4xLDYuNS04LDYuNWMtNS42LDAtOS45LTYuOC05LjktNi44DQoJCQljMS43LTEuNSw0LjYtNi41LDguNy02LjVzNS45LDIuMyw1LjksMi4zbDAuNS0wLjdjMCwwLTEuOS02LjctNy40LTYuN3MtMTEuMiw4LjktMTQuNiwxMWMwLDAsNC43LDExLDE0LjgsMTENCgkJCWM4LjUsMCwxMC43LTguMiwxMS4xLTEwLjJjMS4xLTAuMiwyLjEtMC4zLDIuNy0wLjRjMC4yLTAuNSwwLjQtMS4zLDAuMy0yLjRjLTMuNCwxLjMtOC41LDIuOC0xNC42LDIuOA0KCQkJQy0xOTcuMSwxMzYuOS0xOTcuMSwxMzcuMS0xOTcsMTM3LjN6Ii8+DQoJPC9nPg0KPC9nPg0KPGcgaWQ9IkxheWVyXzIiPg0KCTxwYXRoIGNsYXNzPSJzdDE5IiBkPSJNMTQ0LjMsODIuNWMtMS45LDkuNi0xMi4xLDQ4LjItNTIuNSw0OC4yYy00OC4yLDAtNzAuMi01Mi4yLTcwLjItNTIuMmMxNi4xLTkuOCw0My40LTUyLDY5LjItNTINCgkJczM0LjksMzEuOSwzNC45LDMxLjlsLTIuMiwzLjVjMCwwLTguNS0xMC43LTI4LTEwLjdTNjIuNiw3NC43LDU0LjQsODJjMCwwLDIwLjUsMzIuMSw0Ni45LDMyLjFjMTQuMiwwLDI5LjUtMTIuMywzOC4xLTMwLjgNCgkJYy0xNSwyLjEtMzQuNyw0LTQ4LjYsMi4yYzAsMC0wLjctMC42LTEtMS4zYy0wLjQtMC45LTAuNS0xLjgtMC41LTEuOGMyNy42LDAsNTEuMy02LjUsNjcuNC0xMi41QzE1Mi4zLDMwLjUsMTE5LDAsNzguNiwwDQoJCUMzNS4yLDAsMCwzNS4yLDAsNzguNmMwLDQzLjQsMzUuMiw3OC42LDc4LjYsNzguNmM0Mi44LDAsNzcuNi0zNC4yLDc4LjYtNzYuOEMxNTQuMiw4MC45LDE0OS43LDgxLjcsMTQ0LjMsODIuNXoiLz4NCjwvZz4NCjwvc3ZnPg0K',
522 ];
523
524 // Return the chosen icon's SVG string
525 return $svgs[ $icon ];
526 }
527
528 /**
529 * Modify Admin Nav Menu Label
530 *
531 * @param object $post_type The current object to add a menu items meta box for.
532 *
533 * @return mixed
534 * @since 1.3
535 */
536 function modify_nav_menu_meta_box_object( $post_type ) {
537 if ( isset( $post_type->name ) && $post_type->name == 'give_forms' ) {
538 $post_type->labels->name = esc_html__( 'Donation Forms', 'give' );
539 }
540
541 return $post_type;
542 }
543
544 add_filter( 'nav_menu_meta_box_object', 'modify_nav_menu_meta_box_object' );
545
546 /**
547 * Show Donation Forms Post Type in Appearance > Menus by default on fresh install.
548 *
549 * @return bool
550 * @todo Remove this, when WordPress Core ticket is resolved (https://core.trac.wordpress.org/ticket/16828).
551 *
552 * @since 1.8.14
553 */
554 function give_donation_metabox_menu() {
555
556 // Get Current Screen.
557 $screen = get_current_screen();
558
559 // Proceed, if current screen is navigation menus.
560 if ( 'nav-menus' === $screen->id && give_is_setting_enabled( give_get_option( 'forms_singular' ) ) && ! get_user_option( 'give_is_donation_forms_menu_updated' ) ) {
561
562 // Return false, if it fails to retrieve hidden meta box list and is not admin.
563 if ( ! is_admin() || ( ! $hidden_meta_boxes = get_user_option( 'metaboxhidden_nav-menus' ) ) ) {
564 return false;
565 }
566
567 // Return false, In case, we don't find 'Donation Form' in hidden meta box list.
568 if ( ! in_array( 'add-post-type-give_forms', $hidden_meta_boxes, true ) ) {
569 return false;
570 }
571
572 // Exclude 'Donation Form' value from hidden meta box's list.
573 $hidden_meta_boxes = array_diff( $hidden_meta_boxes, [ 'add-post-type-give_forms' ] );
574
575 // Get current user ID.
576 $user = wp_get_current_user();
577
578 update_user_option( $user->ID, 'metaboxhidden_nav-menus', $hidden_meta_boxes, true );
579 update_user_option( $user->ID, 'give_is_donation_forms_menu_updated', true, true );
580 }
581 }
582
583 add_action( 'current_screen', 'give_donation_metabox_menu' );
584
585 /**
586 * Array_column backup usage
587 *
588 * This file is part of the array_column library.
589 *
590 * @since : 1.3.0.1
591 *
592 * @copyright Copyright (c) Ben Ramsey (http://benramsey.com)
593 * @license https://opensource.org/licenses/MIT MIT
594 */
595
596 if ( ! function_exists( 'array_column' ) ) {
597 /**
598 * Returns the values from a single column of the input array, identified by
599 * the $columnKey.
600 *
601 * Optionally, you may provide an $indexKey to index the values in the returned
602 * array by the values from the $indexKey column in the input array.
603 *
604 * @param array $input A multi-dimensional array (record set) from which to pull
605 * a column of values.
606 * @param int|string $columnKey The column of values to return. This value may be the
607 * integer key of the column you wish to retrieve, or it
608 * may be the string key name for an associative array.
609 * @param mixed $indexKey (Optional.) The column to use as the index/keys for
610 * the returned array. This value may be the integer key
611 * of the column, or it may be the string key name.
612 *
613 * @return array|boolean|null
614 */
615 function array_column( $input = null, $columnKey = null, $indexKey = null ) {
616 // Using func_get_args() in order to check for proper number of
617 // parameters and trigger errors exactly as the built-in array_column()
618 // does in PHP 5.5.
619 $argc = func_num_args();
620 $params = func_get_args();
621
622 if ( $argc < 2 ) {
623 trigger_error( sprintf( 'array_column() expects at least 2 parameters, %s given.', $argc ), E_USER_WARNING );
624
625 return null;
626 }
627
628 if ( ! is_array( $params[0] ) ) {
629 trigger_error( sprintf( 'array_column() expects parameter 1 to be array, %s given.', gettype( $params[0] ) ), E_USER_WARNING );
630
631 return null;
632 }
633
634 if ( ! is_int( $params[1] ) && ! is_float( $params[1] ) && ! is_string( $params[1] ) && $params[1] !== null && ! ( is_object( $params[1] ) && method_exists( $params[1], '__toString' ) ) ) {
635 trigger_error( 'array_column(): The column key should be either a string or an integer.', E_USER_WARNING );
636
637 return false;
638 }
639
640 if ( isset( $params[2] ) && ! is_int( $params[2] ) && ! is_float( $params[2] ) && ! is_string( $params[2] ) && ! ( is_object( $params[2] ) && method_exists( $params[2], '__toString' ) ) ) {
641 trigger_error( 'array_column(): The index key should be either a string or an integer.', E_USER_WARNING );
642
643 return false;
644 }
645
646 $paramsInput = $params[0];
647 $paramsColumnKey = ( $params[1] !== null ) ? (string) $params[1] : null;
648
649 $paramsIndexKey = null;
650 if ( isset( $params[2] ) ) {
651 if ( is_float( $params[2] ) || is_int( $params[2] ) ) {
652 $paramsIndexKey = (int) $params[2];
653 } else {
654 $paramsIndexKey = (string) $params[2];
655 }
656 }
657
658 $resultArray = [];
659
660 foreach ( $paramsInput as $row ) {
661 $key = $value = null;
662 $keySet = $valueSet = false;
663
664 if ( $paramsIndexKey !== null && array_key_exists( $paramsIndexKey, $row ) ) {
665 $keySet = true;
666 $key = (string) $row[ $paramsIndexKey ];
667 }
668
669 if ( $paramsColumnKey === null ) {
670 $valueSet = true;
671 $value = $row;
672 } elseif ( is_array( $row ) && array_key_exists( $paramsColumnKey, $row ) ) {
673 $valueSet = true;
674 $value = $row[ $paramsColumnKey ];
675 }
676
677 if ( $valueSet ) {
678 if ( $keySet ) {
679 $resultArray[ $key ] = $value;
680 } else {
681 $resultArray[] = $value;
682 }
683 }
684 }
685
686 return $resultArray;
687 }
688 }// End if().
689
690 /**
691 * Determines the receipt visibility status.
692 *
693 * @param int $donation_id Donation ID.
694 *
695 * @return bool Whether the receipt is visible or not.
696 * @since 1.3.2
697 */
698 function give_can_view_receipt( $donation_id ) {
699
700 global $give_receipt_args;
701
702 $donor = false;
703 $can_view_receipt = false;
704
705 // Bail out, if donation id doesn't exist.
706 if ( empty( $donation_id ) ) {
707 return $can_view_receipt;
708 }
709
710 $give_receipt_args['id'] = $donation_id;
711
712 // Add backward compatibility.
713 if ( ! is_numeric( $donation_id ) ) {
714 $give_receipt_args['id'] = give_get_donation_id_by_key( $donation_id );
715 }
716
717 // Return to download receipts from admin panel.
718 if ( current_user_can( 'export_give_reports' ) ) {
719
720 /**
721 * This filter will be used to modify can view receipt response when accessed from admin.
722 *
723 * @since 2.3.1
724 */
725 return apply_filters( 'give_can_admin_view_receipt', true );
726 }
727
728 if ( is_user_logged_in() || current_user_can( 'view_give_sensitive_data' ) ) {
729
730 // Proceed only, if user is logged in or can view sensitive Give data.
731 $donor = Give()->donors->get_donor_by( 'user_id', get_current_user_id() );
732
733 } elseif ( ! is_user_logged_in() ) {
734
735 // Check whether it is purchase session?
736 // This condition is to show receipt to donor after donation.
737 $purchase_session = give_get_purchase_session();
738
739 if (
740 ! empty( $purchase_session )
741 && absint( $purchase_session['donation_id'] ) === absint( $donation_id )
742 ) {
743 $donor = Give()->donors->get_donor_by( 'email', $purchase_session['user_email'] );
744 }
745
746 // Check whether it is receipt access session?
747 $receipt_session = give_get_receipt_session();
748 $email_access_token = ! empty( $_COOKIE['give_nl'] ) ? give_clean( $_COOKIE['give_nl'] ) : false;
749
750 if (
751 ! empty( $receipt_session ) ||
752 (
753 give_is_setting_enabled( give_get_option( 'email_access' ) ) &&
754 ! empty( $email_access_token )
755 )
756 ) {
757 $donor = ! empty( $email_access_token )
758 ? Give()->donors->get_donor_by_token( $email_access_token )
759 : false;
760 }
761 }
762
763 // If donor object exists, compare the donation ids of donor with the donation receipt donor tries to access.
764 if ( is_object( $donor ) ) {
765 $is_donor_donated = in_array( (int) $donation_id, array_map( 'absint', explode( ',', $donor->payment_ids ) ), true );
766 $can_view_receipt = $is_donor_donated ? true : $can_view_receipt;
767
768 if ( ! $is_donor_donated ) {
769 Give()->session->set( 'donor_donation_mismatch', true );
770 }
771 }
772
773 return (bool) apply_filters( 'give_can_view_receipt', $can_view_receipt, $donation_id );
774
775 }
776
777 /**
778 * Fallback for cal_days_in_month
779 *
780 * Fallback in case the calendar extension is not loaded in PHP; Only supports Gregorian calendar
781 */
782 if ( ! function_exists( 'cal_days_in_month' ) ) {
783 /**
784 * cal_days_in_month
785 *
786 * @param int $calendar
787 * @param int $month
788 * @param int $year
789 *
790 * @return bool|string
791 */
792 function cal_days_in_month( $calendar, $month, $year ) {
793 return date( 't', mktime( 0, 0, 0, $month, 1, $year ) );
794 }
795 }
796
797 /**
798 * Get plugin info including status, type, and license validation.
799 *
800 * @return array Plugin info plus status, type, and license validation if
801 * available.
802 * @since 1.8.0
803 *
804 * @todo update this function to query give addon and additional
805 *
806 * This is an enhanced version of get_plugins() that returns the status
807 * (`active` or `inactive`) of all plugins, type of plugin (`add-on` or `other`
808 * and license validation for Give add-ons (`true` or `false`). Does not include
809 * MU plugins.
810 */
811 function give_get_plugins( $args = [] ) {
812 $plugins = get_plugins();
813 $active_plugin_paths = (array) get_option( 'active_plugins', [] );
814
815 if ( is_multisite() ) {
816 $network_activated_plugin_paths = array_keys( get_site_option( 'active_sitewide_plugins', [] ) );
817 $active_plugin_paths = array_merge( $active_plugin_paths, $network_activated_plugin_paths );
818 }
819
820 foreach ( $plugins as $plugin_path => $plugin_data ) {
821 // Is plugin active?
822 if ( in_array( $plugin_path, $active_plugin_paths ) ) {
823 $plugins[ $plugin_path ]['Status'] = 'active';
824 } else {
825 $plugins[ $plugin_path ]['Status'] = 'inactive';
826 }
827
828 $dirname = strtolower( dirname( $plugin_path ) );
829 $plugins[ $plugin_path ]['Dir'] = $dirname;
830 $plugins[ $plugin_path ]['Path'] = $plugin_path;
831
832 // A third party add-on may contain more then one author like sofort, so it is better to compare array.
833 $author = false !== strpos( $plugin_data['Author'], ',' )
834 ? array_map( 'trim', explode( ',', $plugin_data['Author'] ) )
835 : [ $plugin_data['Author'] ];
836
837 // Is the plugin a Give add-on?
838 if (
839 false !== strpos( $dirname, 'give-' )
840 && (
841 false !== strpos( $plugin_data['PluginURI'], 'givewp.com' )
842 || array_intersect( $author, [ 'WordImpress', 'GiveWP' ] )
843 )
844 ) {
845 // Plugin is a Give-addon.
846 $plugins[ $plugin_path ]['Type'] = 'add-on';
847
848 $license_active = Give_License::get_license_by_plugin_dirname( $dirname );
849
850 // Does a valid license exist?
851 $plugins[ $plugin_path ]['License'] = $license_active && 'valid' === $license_active['license'];
852
853 } else {
854 // Plugin is not a Give add-on.
855 $plugins[ $plugin_path ]['Type'] = 'other';
856 }
857 }
858
859 if ( ! empty( $args['only_add_on'] ) ) {
860 $plugins = array_filter(
861 $plugins,
862 static function( $plugin ) {
863 return 'add-on' === $plugin['Type'];
864 }
865 );
866 }
867
868 if ( ! empty( $args['only_premium_add_ons'] ) ) {
869 $premiumAddonsListManger = give( PremiumAddonsListManager::class );
870
871 foreach ( $plugins as $key => $plugin ) {
872 if ( 'add-on' !== $plugin['Type'] ) {
873 unset( $plugins[ $key ] );
874 }
875
876 if ( ! $premiumAddonsListManger->isPremiumAddons( $plugin['PluginURI'] ) ) {
877 unset( $plugins[ $key ] );
878 }
879 }
880 }
881
882 return $plugins;
883 }
884
885 /**
886 * Check if terms enabled or not for form.
887 *
888 * @param $form_id
889 *
890 * @return bool
891 * @since 1.8
892 */
893 function give_is_terms_enabled( $form_id ) {
894 $form_option = give_get_meta( $form_id, '_give_terms_option', true );
895
896 if ( give_is_setting_enabled( $form_option, 'global' ) && give_is_setting_enabled( give_get_option( 'terms' ) ) ) {
897 return true;
898
899 } elseif ( give_is_setting_enabled( $form_option ) ) {
900 return true;
901
902 } else {
903 return false;
904 }
905 }
906
907 /**
908 * Delete donation stats cache.
909 *
910 * @param string|array $date_range Date for stats.
911 * Date value should be in today, yesterday, this_week, last_week, this_month,
912 * last_month, this_quarter, last_quarter, this_year, last_year. For date value other,
913 * all cache will be removed.
914 *
915 * @param array $args
916 *
917 * @return WP_Error|bool
918 * @since 1.8.7
919 *
920 * @todo Resolve stats cache key naming issue. Currently it is difficult to regenerate cache key.
921 */
922 function give_delete_donation_stats( $date_range = '', $args = [] ) {
923
924 // Delete all cache.
925 $status = Give_Cache::delete( Give_Cache::get_options_like( 'give_stats' ) );
926
927 /**
928 * Fire the action when donation stats delete.
929 *
930 * @param string|array $date_range
931 * @param array $args
932 *
933 * @since 1.8.7
934 */
935 do_action( 'give_delete_donation_stats', $status, $date_range, $args );
936
937 return $status;
938 }
939
940 /**
941 * Check if admin creating new donation form or not.
942 *
943 * @return bool
944 * @since 2.0
945 */
946 function give_is_add_new_form_page() {
947 $status = false;
948
949 if ( false !== strpos( $_SERVER['REQUEST_URI'], '/wp-admin/post-new.php?post_type=give_forms' ) ) {
950 $status = true;
951 }
952
953 return $status;
954 }
955
956 /**
957 * Get Form/Payment meta.
958 *
959 * Note: This function will help you to get meta for payment and form.
960 * If you want to get meta for donors then use get_meta of Give_Donor and
961 * If you want to get meta for logs then use get_meta of Give_Logging->logmeta_db.
962 *
963 * @param int $id
964 * @param string $meta_key
965 * @param bool $single
966 * @param bool $default
967 * @param string $meta_type
968 *
969 * @return mixed
970 * @since 1.8.8
971 */
972 function give_get_meta( $id, $meta_key = '', $single = false, $default = false, $meta_type = '' ) {
973 switch ( $meta_type ) {
974 case 'donation':
975 $meta_value = Give()->payment_meta->get_meta( $id, $meta_key, $single );
976 break;
977
978 case 'form':
979 $meta_value = Give()->form_meta->get_meta( $id, $meta_key, $single );
980 break;
981
982 case 'donor':
983 $meta_value = Give()->donor_meta->get_meta( $id, $meta_key, $single );
984 break;
985
986 default:
987 $meta_value = get_post_meta( $id, $meta_key, $single );
988 }
989
990 /**
991 * Filter the meta value
992 *
993 * @since 1.8.8
994 */
995 $meta_value = apply_filters( 'give_get_meta', $meta_value, $id, $meta_key, $default, $meta_type );
996
997 if ( ( empty( $meta_key ) || empty( $meta_value ) ) && $default ) {
998 $meta_value = $default;
999 }
1000
1001 return $meta_value;
1002 }
1003
1004 /**
1005 * Update Form/Payment meta.
1006 *
1007 * @param int $id
1008 * @param string $meta_key
1009 * @param mixed $meta_value
1010 * @param mixed $prev_value
1011 * @param string $meta_type
1012 *
1013 * @return mixed
1014 * @since 1.8.8
1015 */
1016 function give_update_meta( $id, $meta_key, $meta_value, $prev_value = '', $meta_type = '' ) {
1017 switch ( $meta_type ) {
1018 case 'donation':
1019 $status = Give()->payment_meta->update_meta( $id, $meta_key, $meta_value, $prev_value );
1020 break;
1021
1022 case 'form':
1023 $status = Give()->form_meta->update_meta( $id, $meta_key, $meta_value, $prev_value );
1024 break;
1025
1026 case 'donor':
1027 $status = Give()->donor_meta->update_meta( $id, $meta_key, $meta_value, $prev_value );
1028 break;
1029
1030 default:
1031 $status = update_post_meta( $id, $meta_key, $meta_value, $prev_value );
1032 }
1033
1034 /**
1035 * Filter the meta value update status
1036 *
1037 * @since 1.8.8
1038 */
1039 return apply_filters( 'give_update_meta', $status, $id, $meta_key, $meta_value, $meta_type );
1040 }
1041
1042 /**
1043 * Delete Form/Payment meta.
1044 *
1045 * @param int $id
1046 * @param string $meta_key
1047 * @param string $meta_value
1048 * @param string $meta_type
1049 *
1050 * @return mixed
1051 * @since 1.8.8
1052 */
1053 function give_delete_meta( $id, $meta_key, $meta_value = '', $meta_type = '' ) {
1054 switch ( $meta_type ) {
1055 case 'donation':
1056 $status = Give()->payment_meta->delete_meta( $id, $meta_key, $meta_value );
1057 break;
1058
1059 case 'form':
1060 $status = Give()->form_meta->delete_meta( $id, $meta_key, $meta_value );
1061 break;
1062
1063 case 'donor':
1064 $status = Give()->donor_meta->delete_meta( $id, $meta_key, $meta_value );
1065 break;
1066
1067 default:
1068 $status = delete_post_meta( $id, $meta_key, $meta_value );
1069 }
1070
1071 /**
1072 * Filter the meta value delete status
1073 *
1074 * @since 1.8.8
1075 */
1076 return apply_filters( 'give_delete_meta', $status, $id, $meta_key, $meta_value, $meta_type );
1077 }
1078
1079 /**
1080 * Check if the upgrade routine has been run for a specific action
1081 *
1082 * @param string $upgrade_action The upgrade action to check completion for
1083 *
1084 * @return bool If the action has been added to the completed actions array
1085 * @since 1.0
1086 */
1087 function give_has_upgrade_completed( $upgrade_action = '' ) {
1088 // Bailout.
1089 if ( empty( $upgrade_action ) ) {
1090 return false;
1091 }
1092
1093 // Fresh install?
1094 // If fresh install then all upgrades will be consider as completed.
1095 $is_fresh_install = ! Give_Cache_Setting::get_option( 'give_version' );
1096 if ( $is_fresh_install ) {
1097 return true;
1098 }
1099
1100 $completed_upgrades = give_get_completed_upgrades();
1101
1102 return in_array( $upgrade_action, $completed_upgrades );
1103
1104 }
1105
1106 /**
1107 * For use when doing 'stepped' upgrade routines, to see if we need to start somewhere in the middle
1108 *
1109 * @return mixed When nothing to resume returns false, otherwise starts the upgrade where it left off
1110 * @since 1.8
1111 */
1112 function give_maybe_resume_upgrade() {
1113 $doing_upgrade = get_option( 'give_doing_upgrade', false );
1114 if ( empty( $doing_upgrade ) ) {
1115 return false;
1116 }
1117
1118 return $doing_upgrade;
1119 }
1120
1121 /**
1122 * Adds an upgrade action to the completed upgrades array
1123 *
1124 * @param string $upgrade_action The action to add to the completed upgrades array
1125 *
1126 * @return bool If the function was successfully added
1127 * @since 1.0
1128 */
1129 function give_set_upgrade_complete( $upgrade_action = '' ) {
1130
1131 if ( empty( $upgrade_action ) ) {
1132 return false;
1133 }
1134
1135 $completed_upgrades = give_get_completed_upgrades();
1136 $completed_upgrades[] = $upgrade_action;
1137
1138 // Remove any blanks, and only show uniques.
1139 $completed_upgrades = array_unique( array_values( $completed_upgrades ) );
1140
1141 /**
1142 * Fire the action when any upgrade set to complete.
1143 *
1144 * @since 1.8.12
1145 */
1146 do_action( 'give_set_upgrade_completed', $upgrade_action, $completed_upgrades );
1147
1148 return update_option( 'give_completed_upgrades', $completed_upgrades, false );
1149 }
1150
1151 /**
1152 * Get's the array of completed upgrade actions
1153 *
1154 * @return array The array of completed upgrades
1155 * @since 1.0
1156 */
1157 function give_get_completed_upgrades() {
1158 return (array) Give_Cache_Setting::get_option( 'give_completed_upgrades' );
1159 }
1160
1161 /**
1162 * In 2.0 we updated table for log, payment and form.
1163 *
1164 * Note: internal purpose only.
1165 *
1166 * @param string $type Context for table
1167 *
1168 * @return null|array
1169 *
1170 * @since 4.9.0 rename function - PHP 8 compatibility
1171 * @since 2.0
1172 * @global wpdb $wpdb
1173 */
1174 function give_v20_bc_table_details( $type ) {
1175 global $wpdb;
1176 $table = [];
1177
1178 // Bailout.
1179 if ( empty( $type ) ) {
1180 return null;
1181 }
1182
1183 switch ( $type ) {
1184 case 'form':
1185 $table['name'] = $wpdb->formmeta;
1186 $table['column']['id'] = 'form_id';
1187
1188 break;
1189
1190 case 'payment':
1191 $table['name'] = $wpdb->donationmeta;
1192 $table['column']['id'] = Give()->payment_meta->get_meta_type() . '_id';
1193 }
1194
1195 // Backward compatibility.
1196 if ( ! give_has_upgrade_completed( 'v20_move_metadata_into_new_table' ) ) {
1197 $table['name'] = $wpdb->postmeta;
1198 $table['column']['id'] = 'post_id';
1199 }
1200
1201 return $table;
1202 }
1203
1204 /**
1205 * Remove the Give transaction pages from WP search results.
1206 *
1207 * @param WP_Query $query
1208 *
1209 * @since 1.8.13
1210 */
1211 function give_remove_pages_from_search( $query ) {
1212
1213 if ( ! $query->is_admin && $query->is_search && $query->is_main_query() ) {
1214
1215 $transaction_failed = give_get_option( 'failure_page', 0 );
1216 $success_page = give_get_option( 'success_page', 0 );
1217
1218 $args = apply_filters(
1219 'give_remove_pages_from_search',
1220 [
1221 $transaction_failed,
1222 $success_page,
1223 ],
1224 $query
1225 );
1226 $query->set( 'post__not_in', $args );
1227 }
1228 }
1229
1230 add_action( 'pre_get_posts', 'give_remove_pages_from_search', 10, 1 );
1231
1232 /**
1233 * Inserts a new key/value before a key in the array.
1234 *
1235 * @param string $key The key to insert before.
1236 * @param array $array An array to insert in to.
1237 * @param string $new_key The key to insert.
1238 * @param array|string $new_value An value to insert.
1239 *
1240 * @return array The new array if the key exists, the passed array otherwise.
1241 *
1242 * @since 1.8.13
1243 *
1244 * @see array_insert_before()
1245 */
1246 function give_array_insert_before( $key, array &$array, $new_key, $new_value ) {
1247 if ( array_key_exists( $key, $array ) ) {
1248 $new = [];
1249 foreach ( $array as $k => $value ) {
1250 if ( $k === $key ) {
1251 $new[ $new_key ] = $new_value;
1252 }
1253 $new[ $k ] = $value;
1254 }
1255
1256 return $new;
1257 }
1258
1259 return $array;
1260 }
1261
1262 /**
1263 * Inserts a new key/value after a key in the array.
1264 *
1265 * @param string $key The key to insert after.
1266 * @param array $array An array to insert in to.
1267 * @param string $new_key The key to insert.
1268 * @param array|string $new_value An value to insert.
1269 *
1270 * @return array The new array if the key exists, the passed array otherwise.
1271 *
1272 * @since 1.8.13
1273 *
1274 * @see array_insert_before()
1275 */
1276 function give_array_insert_after( $key, array &$array, $new_key, $new_value ) {
1277 if ( array_key_exists( $key, $array ) ) {
1278 $new = [];
1279 foreach ( $array as $k => $value ) {
1280 $new[ $k ] = $value;
1281 if ( $k === $key ) {
1282 $new[ $new_key ] = $new_value;
1283 }
1284 }
1285
1286 return $new;
1287 }
1288
1289 return $array;
1290 }
1291
1292 /**
1293 * Pluck a certain field out of each object in a list.
1294 *
1295 * This has the same functionality and prototype of
1296 * array_column() (PHP 5.5) but also supports objects.
1297 *
1298 * @param array $list List of objects or arrays
1299 * @param int|string $field Field from the object to place instead of the entire object
1300 * @param int|string $index_key Optional. Field from the object to use as keys for the new array.
1301 * Default null.
1302 *
1303 * @return array Array of found values. If `$index_key` is set, an array of found values with keys
1304 * corresponding to `$index_key`. If `$index_key` is null, array keys from the original
1305 * `$list` will be preserved in the results.
1306 * @since 1.8.13
1307 */
1308 function give_list_pluck( $list, $field, $index_key = null ) {
1309
1310 if ( ! $index_key ) {
1311 /**
1312 * This is simple. Could at some point wrap array_column()
1313 * if we knew we had an array of arrays.
1314 */
1315 foreach ( $list as $key => $value ) {
1316 if ( is_object( $value ) ) {
1317 if ( isset( $value->$field ) ) {
1318 $list[ $key ] = $value->$field;
1319 }
1320 } else {
1321 if ( isset( $value[ $field ] ) ) {
1322 $list[ $key ] = $value[ $field ];
1323 }
1324 }
1325 }
1326
1327 return $list;
1328 }
1329
1330 /*
1331 * When index_key is not set for a particular item, push the value
1332 * to the end of the stack. This is how array_column() behaves.
1333 */
1334 $newlist = [];
1335 foreach ( $list as $value ) {
1336 if ( is_object( $value ) ) {
1337 if ( isset( $value->$index_key ) ) {
1338 $newlist[ $value->$index_key ] = $value->$field;
1339 } else {
1340 $newlist[] = $value->$field;
1341 }
1342 } else {
1343 if ( isset( $value[ $index_key ] ) ) {
1344 $newlist[ $value[ $index_key ] ] = $value[ $field ];
1345 } else {
1346 $newlist[] = $value[ $field ];
1347 }
1348 }
1349 }
1350
1351 $list = $newlist;
1352
1353 return $list;
1354 }
1355
1356 /**
1357 * Add meta data field to a donor.
1358 *
1359 * @param int $donor_id Donor ID.
1360 * @param string $meta_key Metadata name.
1361 * @param mixed $meta_value Metadata value. Must be serializable if non-scalar.
1362 * @param bool $unique Optional. Whether the same key should not be added.
1363 * Default false.
1364 *
1365 * @return int|false Meta ID on success, false on failure.
1366 * @since 1.8.13
1367 */
1368 function add_donor_meta( $donor_id, $meta_key, $meta_value, $unique = false ) {
1369 return add_metadata( 'give_customer', $donor_id, $meta_key, $meta_value, $unique );
1370 }
1371
1372 /**
1373 * Remove metadata matching criteria from a Donor meta.
1374 *
1375 * You can match based on the key, or key and value. Removing based on key and
1376 * value, will keep from removing duplicate metadata with the same key. It also
1377 * allows removing all metadata matching key, if needed.
1378 *
1379 * @param int $donor_id Donor ID
1380 * @param string $meta_key Metadata name.
1381 * @param mixed $meta_value Optional. Metadata value.
1382 *
1383 * @return bool True on success, false on failure.
1384 * @since 1.8.13
1385 */
1386 function delete_donor_meta( $donor_id, $meta_key, $meta_value = '' ) {
1387 return delete_metadata( 'give_customer', $donor_id, $meta_key, $meta_value );
1388 }
1389
1390 /**
1391 * Retrieve donor meta field for a donor meta table.
1392 *
1393 * @param int $donor_id Donor ID.
1394 * @param string $key Optional. The meta key to retrieve. By default, returns data for all keys.
1395 * @param bool $single Whether to return a single value.
1396 *
1397 * @return mixed Will be an array if $single is false. Will be value of meta data field if $single
1398 * is true.
1399 * @since 1.8.13
1400 */
1401 function get_donor_meta( $donor_id, $key = '', $single = false ) {
1402 return get_metadata( 'give_customer', $donor_id, $key, $single );
1403 }
1404
1405 /**
1406 * Update customer meta field based on Donor ID.
1407 *
1408 * If the meta field for the donor does not exist, it will be added.
1409 *
1410 * @param int $donor_id Donor ID.
1411 * @param string $meta_key Metadata key.
1412 * @param mixed $meta_value Metadata value.
1413 * @param mixed $prev_value Optional. Previous value to check before removing.
1414 *
1415 * @return int|bool Meta ID if the key didn't exist, true on successful update, false on failure.
1416 * @since 1.8.13
1417 */
1418 function update_donor_meta( $donor_id, $meta_key, $meta_value, $prev_value = '' ) {
1419 return update_metadata( 'give_customer', $donor_id, $meta_key, $meta_value, $prev_value );
1420 }
1421
1422
1423 /**
1424 * Give recalculate income and donation of the donation from ID
1425 *
1426 * @param int $form_id Form id of which recalculation needs to be done.
1427 *
1428 * @return void
1429 * @since 1.8.13
1430 */
1431 function give_recount_form_income_donation( $form_id = 0 ) {
1432 // Check if form id is not empty.
1433 if ( ! empty( $form_id ) ) {
1434 /**
1435 * Filter to modify payment status.
1436 *
1437 * @since 1.8.13
1438 */
1439 $accepted_statuses = apply_filters( 'give_recount_accepted_statuses', [ 'publish' ] );
1440
1441 /**
1442 * Filter to modify args of payment query before recalculating the form total
1443 *
1444 * @since 1.8.13
1445 */
1446 $args = apply_filters(
1447 'give_recount_form_stats_args',
1448 [
1449 'give_forms' => $form_id,
1450 'status' => $accepted_statuses,
1451 'number' => - 1,
1452 'fields' => 'ids',
1453 ]
1454 );
1455
1456 $totals = [
1457 'sales' => 0,
1458 'earnings' => 0,
1459 ];
1460
1461 $payments = new Give_Payments_Query( $args );
1462 $payments = $payments->get_payments();
1463
1464 if ( $payments ) {
1465 foreach ( $payments as $payment ) {
1466 // Ensure acceptable status only.
1467 if ( ! in_array( $payment->post_status, $accepted_statuses ) ) {
1468 continue;
1469 }
1470
1471 // Ensure only payments for this form are counted.
1472 if ( $payment->form_id != $form_id ) {
1473 continue;
1474 }
1475
1476 $totals['sales'] ++;
1477 $totals['earnings'] += $payment->total;
1478
1479 }
1480 }
1481 give_update_meta( $form_id, '_give_form_sales', $totals['sales'] );
1482 give_update_meta( $form_id, '_give_form_earnings', give_sanitize_amount_for_db( $totals['earnings'] ) );
1483 }// End if().
1484 }
1485
1486
1487 /**
1488 * Get attribute string
1489 *
1490 * @param array $attributes
1491 * @param array $default_attributes
1492 *
1493 * @return string
1494 * @since 1.8.17
1495 */
1496 function give_get_attribute_str( $attributes, $default_attributes = [] ) {
1497 $attribute_str = '';
1498
1499 if ( isset( $attributes['attributes'] ) ) {
1500 $attributes = $attributes['attributes'];
1501 }
1502
1503 if ( ! empty( $default_attributes ) ) {
1504 $attributes = wp_parse_args( $attributes, $default_attributes );
1505 }
1506
1507 if ( empty( $attributes ) ) {
1508 return $attribute_str;
1509 }
1510
1511 foreach ( $attributes as $tag => $value ) {
1512 if ( 'value' == $tag ) {
1513 $value = esc_attr( $value );
1514 }
1515
1516 $attribute_str .= " {$tag}=\"{$value}\"";
1517 }
1518
1519 return trim( $attribute_str );
1520 }
1521
1522 /**
1523 * Get the upload dir path
1524 *
1525 * @return string $wp_upload_dir;
1526 * @since 1.8.17
1527 */
1528 function give_get_wp_upload_dir() {
1529 $wp_upload_dir = wp_upload_dir();
1530
1531 return ( ! empty( $wp_upload_dir['path'] ) ? $wp_upload_dir['path'] : false );
1532 }
1533
1534 /**
1535 * Get the data from uploaded JSON file
1536 *
1537 * @param string $file_name filename of the json file that is being uploaded
1538 *
1539 * @return string|bool $file_contents File content
1540 * @since 1.8.17
1541 */
1542 function give_get_core_settings_json( $file_name ) {
1543 $upload_dir = give_get_wp_upload_dir();
1544 $file_path = $upload_dir . '/' . $file_name;
1545
1546 if ( is_wp_error( $file_path ) || empty( $file_path ) ) {
1547 Give_Admin_Settings::add_error( 'give-import-csv', __( 'Please upload or provide a valid JSON file.', 'give' ) );
1548 }
1549
1550 $file_contents = file_get_contents( $file_path );
1551
1552 return $file_contents;
1553 }
1554
1555 /**
1556 * Get number of donation to show when user is not login.
1557 *
1558 * @return int $country The two letter country code for the site's base country
1559 * @since 1.8.17
1560 */
1561 function give_get_limit_display_donations() {
1562 return give_get_option( 'limit_display_donations', 1 );
1563 }
1564
1565 /**
1566 * Add footer to the table when donor is view the donation history page with out login
1567 *
1568 * @since 1.8.17
1569 */
1570 function give_donation_history_table_end() {
1571 $email = Give()->session->get( 'give_email' );
1572 ?>
1573 <tfoot>
1574 <tr>
1575 <td colspan="9999">
1576 <div class="give-security-wrap">
1577 <div class="give-security-column give-security-description-wrap">
1578 <?php
1579 echo sprintf( __( 'For security reasons, please confirm your email address (%s) to view your complete donation history.', 'give' ), $email );
1580 ?>
1581 </div>
1582 <div class="give-security-column give-security-button-wrap">
1583 <a href="#" data-email="<?php echo $email; ?>" id="give-confirm-email-btn"
1584 class="give-confirm-email-btn give-btn">
1585 <?php _e( 'Confirm Email', 'give' ); ?>
1586 </a>
1587 <span><?php _e( 'Email Sent!', 'give' ); ?></span>
1588 </div>
1589 </div>
1590 </td>
1591 </tr>
1592 </tfoot>
1593 <?php
1594 }
1595
1596
1597 /**
1598 * Wrapper for _doing_it_wrong.
1599 *
1600 * @param string $function
1601 * @param string $message
1602 * @param string $version deprecated
1603 *
1604 * @return void
1605 * @since 1.8.18
1606 * @since 2.5.13 Refactor function
1607 */
1608 function give_doing_it_wrong( $function, $message, $version = null ) {
1609 /**
1610 * Fires while calling function incorrectly.
1611 *
1612 * Allow you to hook to incorrect function call.
1613 *
1614 * @param string $function The function that was called.
1615 * @param string $replacement Optional. The function that should have been called.
1616 * @param string $version The plugin version that deprecated the function.
1617 *
1618 * @since 2.5.13
1619 */
1620 do_action( 'give_doing_it_wrong', $function, $message, $version );
1621
1622 $show_errors = current_user_can( 'manage_options' );
1623
1624 // Allow plugin to filter the output error trigger.
1625 if ( WP_DEBUG && apply_filters( 'give_doing_it_wrong_trigger_error', $show_errors ) ) {
1626 trigger_error( sprintf( __( '%1$s was called <strong>incorrectly</strong>. %2$s', 'give' ), $function, $message ) );
1627 trigger_error( print_r( wp_debug_backtrace_summary(), 1 ) );// Limited to previous 1028 characters, but since we only need to move back 1 in stack that should be fine.
1628 }
1629 }
1630
1631
1632 /**
1633 * Remove limit from running php script complete.
1634 *
1635 * @since 1.8.18
1636 */
1637 function give_ignore_user_abort() {
1638 ignore_user_abort( true );
1639
1640 if ( ! give_is_func_disabled( 'set_time_limit' )) {
1641 set_time_limit( 0 );
1642 }
1643 }
1644
1645 /**
1646 * Get post type count.
1647 *
1648 * @param string $post_type
1649 * @param array $args
1650 *
1651 * @return int
1652 * @since 2.0.2
1653 */
1654 function give_get_total_post_type_count( $post_type = '', $args = [] ) {
1655 global $wpdb;
1656 $where = '';
1657
1658 if ( ! $post_type ) {
1659 return 0;
1660 }
1661
1662 // Bulit where query
1663 if ( ! empty( $post_type ) ) {
1664 $where .= ' WHERE';
1665
1666 if ( is_array( $post_type ) ) {
1667 $where .= " post_type='" . implode( "' OR post_type='", $post_type ) . "'";
1668 } else {
1669 $where .= " post_type='{$post_type}'";
1670 }
1671 }
1672
1673 $result = $wpdb->get_var( "SELECT count(ID) FROM {$wpdb->posts}{$where}" );
1674
1675 return absint( $result );
1676 }
1677
1678 /**
1679 * Define a constant if it is not already defined.
1680 *
1681 * @param string $name Constant name.
1682 * @param string $value Value.
1683 *
1684 * @credit WooCommerce
1685 * @since 2.0.5
1686 */
1687 function give_maybe_define_constant( $name, $value ) {
1688 if ( ! defined( $name ) ) {
1689 define( $name, $value );
1690 }
1691 }
1692
1693 /**
1694 * Decode time short tag in string
1695 *
1696 * @param string $string
1697 * @param int $timestamp
1698 *
1699 * @return string
1700 * @since 2.1.0
1701 */
1702 function give_time_do_tags( $string, $timestamp = 0 ) {
1703 $current_time = ! empty( $timestamp ) ? $timestamp : current_time( 'timestamp' );
1704
1705 $formatted_string = str_replace(
1706 [
1707 '{D}',
1708 '{DD}',
1709 '{M}',
1710 '{MM}',
1711 '{YY}',
1712 '{YYYY}',
1713 '{H}',
1714 '{HH}',
1715 '{N}',
1716 '{S}',
1717 ],
1718 [
1719 date( 'j', $current_time ),
1720 date( 'd', $current_time ),
1721 date( 'n', $current_time ),
1722 date( 'm', $current_time ),
1723 date( 'Y', $current_time ),
1724 date( 'Y', $current_time ),
1725 date( 'G', $current_time ),
1726 date( 'H', $current_time ),
1727 date( 's', $current_time ),
1728 ],
1729 $string
1730 );
1731
1732 /**
1733 * Filter the parsed string.
1734 *
1735 * @since 2.1.0
1736 */
1737 return apply_filters( 'give_time_do_tags', $formatted_string, $string, $timestamp );
1738 }
1739
1740
1741 /**
1742 * Check if Company field enabled or not for form or globally.
1743 *
1744 * @param $form_id
1745 *
1746 * @return bool
1747 * @since 2.1
1748 */
1749 function give_is_company_field_enabled( $form_id ) {
1750 $form_setting_val = give_get_meta( $form_id, '_give_company_field', true );
1751 $global_setting_val = give_get_option( 'company_field' );
1752
1753 if ( ! empty( $form_setting_val ) ) {
1754 if ( give_is_setting_enabled( $form_setting_val, [ 'required', 'optional' ] ) ) {
1755 return true;
1756 } elseif ( 'global' === $form_setting_val && give_is_setting_enabled(
1757 $global_setting_val,
1758 [
1759 'required',
1760 'optional',
1761 ]
1762 ) ) {
1763 return true;
1764 } else {
1765 return false;
1766 }
1767 } elseif ( give_is_setting_enabled( $global_setting_val, [ 'required', 'optional' ] ) ) {
1768 return true;
1769
1770 } else {
1771 return false;
1772 }
1773 }
1774
1775 /**
1776 * Check if Last Name field is required
1777 *
1778 * @param $form_id
1779 *
1780 * @return bool
1781 * @since 2.15.0
1782 */
1783 function give_is_last_name_required( $form_id ) {
1784 $form_setting_val = give_get_meta( $form_id, '_give_last_name_field_required', true );
1785 $global_setting_val = give_get_option( 'last_name_field_required' );
1786
1787 if ( ! empty( $form_setting_val ) ) {
1788 if ( 'required' === $form_setting_val ) {
1789 return true;
1790 }
1791
1792 return 'global' === $form_setting_val && 'required' === $global_setting_val;
1793 }
1794
1795 return 'required' === $global_setting_val;
1796 }
1797
1798 /**
1799 * Check if anonymous donation field enabled or not for form or globally.
1800 *
1801 * @param $form_id
1802 *
1803 * @return bool
1804 * @since 2.1
1805 */
1806 function give_is_anonymous_donation_field_enabled( $form_id ) {
1807 $form_setting_val = give_get_meta( $form_id, '_give_anonymous_donation', true, 'global' );
1808 $global_setting_val = give_get_option( 'anonymous_donation', 'disabled' );
1809
1810 if ( ! empty( $form_setting_val ) ) {
1811 if ( give_is_setting_enabled( $form_setting_val ) ) {
1812 return true;
1813 } elseif ( 'global' === $form_setting_val && give_is_setting_enabled( $global_setting_val ) ) {
1814 return true;
1815 } else {
1816 return false;
1817 }
1818 } elseif ( give_is_setting_enabled( $global_setting_val ) ) {
1819 return true;
1820 }
1821
1822 return false;
1823 }
1824
1825 /**
1826 * Check if donor comment field enabled or not for form or globally.
1827 *
1828 * @param $form_id
1829 *
1830 * @return bool
1831 * @since 2.1
1832 */
1833 function give_is_donor_comment_field_enabled( $form_id ) {
1834 $form_setting_val = give_get_meta( $form_id, '_give_donor_comment', true, 'global' );
1835 $global_setting_val = give_get_option( 'donor_comment', 'disabled' );
1836
1837 if ( ! empty( $form_setting_val ) ) {
1838 if ( give_is_setting_enabled( $form_setting_val ) ) {
1839 return true;
1840 } elseif ( 'global' === $form_setting_val && give_is_setting_enabled( $global_setting_val ) ) {
1841 return true;
1842 } else {
1843 return false;
1844 }
1845 } elseif ( give_is_setting_enabled( $global_setting_val ) ) {
1846 return true;
1847 }
1848
1849 return false;
1850
1851 }
1852
1853 /**
1854 * Get add-on user meta value information
1855 * Note: only for internal use.
1856 *
1857 * @param string $banner_addon_name Give add-on name.
1858 *
1859 * @return array
1860 *
1861 * @since 4.9.0 rename function - PHP 8 compatibility
1862 * @since 2.1.0
1863 */
1864 function give_get_active_by_user_meta( $banner_addon_name ) {
1865 global $wpdb;
1866
1867 // Get the option key.
1868 $option_name = Give_Addon_Activation_Banner::get_banner_user_meta_key( $banner_addon_name );
1869 $data = [];
1870
1871 if ( empty( $GLOBALS['give_addon_activated_by_user'] ) ) {
1872 $GLOBALS['give_addon_activated_by_user'] = [];
1873
1874 // Get the meta of activation banner by user.
1875 $activation_banners = $wpdb->get_results(
1876 "
1877 SELECT option_name, option_value
1878 FROM {$wpdb->options}
1879 WHERE option_name LIKE '%_active_by_user%'
1880 AND option_name LIKE '%give_addon%'
1881 ",
1882 ARRAY_A
1883 );
1884
1885 if ( ! empty( $activation_banners ) ) {
1886 $GLOBALS['give_addon_activated_by_user'] = array_combine(
1887 wp_list_pluck( $activation_banners, 'option_name' ),
1888 wp_list_pluck( $activation_banners, 'option_value' )
1889 );
1890 }
1891 }
1892
1893 if ( array_key_exists( $option_name, $GLOBALS['give_addon_activated_by_user'] ) ) {
1894 $data = maybe_unserialize( $GLOBALS['give_addon_activated_by_user'][ $option_name ] );
1895 }
1896
1897 return $data;
1898 }
1899
1900 /**
1901 * Get time interval for which nonce is valid
1902 *
1903 * @return int
1904 * @since 2.1.3
1905 */
1906 function give_get_nonce_life() {
1907 /**
1908 * Filters the lifespan of nonces in seconds.
1909 *
1910 * @see wp-inlucdes/pluggable.php:wp_nonce_tick
1911 */
1912 return (int) apply_filters( 'nonce_life', DAY_IN_SECONDS );
1913 }
1914
1915 /**
1916 * Get nonce field without id
1917 *
1918 * @param string $action
1919 * @param string $name
1920 * @param bool $referer
1921 *
1922 * @return string
1923 * @since 2.1.3
1924 */
1925 function give_get_nonce_field( $action, $name, $referer = false ) {
1926 return str_replace(
1927 "id=\"{$name}\"",
1928 '',
1929 wp_nonce_field( $action, $name, $referer, false )
1930 );
1931 }
1932
1933 /**
1934 * Display/Return a formatted goal for a donation form
1935 *
1936 * @since 3.16.0 Add form_id to the array return
1937 * @since 2.1
1938 *
1939 * @param int|Give_Donate_Form $form Form ID or Form Object.
1940 *
1941 * @return array
1942 */
1943 function give_goal_progress_stats( $form ) {
1944
1945 if ( ! $form instanceof Give_Donate_Form ) {
1946 $form = new Give_Donate_Form( $form );
1947 }
1948
1949 $goal_format = give_get_form_goal_format( $form->ID );
1950
1951 /**
1952 * Filter the form.
1953 * @since 1.8.8
1954 */
1955 $total_goal = apply_filters( 'give_goal_amount_target_output', round( give_maybe_sanitize_amount( $form->goal ), 2 ), $form->ID, $form );
1956
1957 switch ( $goal_format ) {
1958 case 'donation':
1959 /**
1960 * Filter the form donations.
1961 *
1962 * @since 2.1
1963 */
1964 $actual = apply_filters( 'give_goal_donations_raised_output', $form->sales, $form->ID, $form );
1965 break;
1966 case 'donors':
1967 /**
1968 * Filter to modify total number if donor for the donation form.
1969 *
1970 * @param int $donors Total number of donors that donated to the form.
1971 * @param int $form_id Donation Form ID.
1972 * @param Give_Donate_Form $form instances of Give_Donate_Form.
1973 *
1974 * @return int $donors Total number of donors that donated to the form.
1975 * @since 2.1.3
1976 */
1977 $actual = apply_filters( 'give_goal_donors_target_output', give_get_form_donor_count( $form->ID ), $form->ID, $form );
1978 break;
1979 default:
1980 /**
1981 * Filter the form income.
1982 *
1983 * @since 3.16.0 Revert changes implemented on the 3.14.0 version
1984 * @since 3.14.0 Replace "$form->earnings" with (new DonationQuery())->form($form->ID)->sumIntendedAmount()
1985 * @since 1.8.8
1986 */
1987 $actual = apply_filters( 'give_goal_amount_raised_output', $form->earnings, $form->ID, $form );
1988 break;
1989 }
1990
1991 $progress = $total_goal ? round( ( $actual / $total_goal ) * 100, 2 ) : 0;
1992
1993 $stats_array = [
1994 'raw_actual' => $actual,
1995 'raw_goal' => $total_goal,
1996 ];
1997
1998 /**
1999 * Filter the goal progress output
2000 *
2001 * @since 1.8.8
2002 */
2003 $progress = apply_filters( 'give_goal_amount_funded_percentage_output', $progress, $form->ID, $form );
2004
2005 // Define Actual Goal based on the goal format.
2006 switch ( $goal_format ) {
2007 case 'percentage':
2008 $actual = "{$progress}%";
2009 $total_goal = '';
2010 break;
2011
2012 case 'amount' === $goal_format:
2013 $actual = give_currency_filter( give_format_amount( $actual ) );
2014 $total_goal = give_currency_filter( give_format_amount( $total_goal ) );
2015 break;
2016
2017 default:
2018 $actual = give_format_amount( $actual, [ 'decimal' => false ] );
2019 $total_goal = give_format_amount( $total_goal, [ 'decimal' => false ] );
2020 break;
2021 }
2022
2023 $stats_array = array_merge(
2024 [
2025 'progress' => $progress,
2026 'actual' => $actual,
2027 'goal' => $total_goal,
2028 'format' => $goal_format,
2029 'form_id' => $form->ID
2030 ],
2031 $stats_array
2032 );
2033
2034 /**
2035 * Filter the goal stats
2036 *
2037 * @since 2.1
2038 */
2039 return apply_filters( 'give_goal_progress_stats', $stats_array );
2040 }
2041
2042 /**
2043 * Get the admin messages key to show the notices.
2044 *
2045 * @return array $message admin message key.
2046 * @since 2.1.4
2047 */
2048 function give_get_admin_messages_key() {
2049 $messages = empty( $_GET['give-messages'] ) ? [] : give_clean( $_GET['give-messages'] );
2050
2051 // backward compatibility.
2052 if ( ! empty( $_GET['give-message'] ) ) {
2053 $messages[] = give_clean( $_GET['give-message'] );
2054 }
2055
2056 /**
2057 * Filter to modify the admin messages key.
2058 *
2059 * @param array $message admin message key.
2060 *
2061 * @return array $message admin message key.
2062 * @since 2.1.4
2063 */
2064 return (array) apply_filters( 'give_get_admin_messages_key', $messages );
2065 }
2066
2067 /**
2068 * Get User Agent String.
2069 *
2070 * @return array|string
2071 * @since 2.1.4
2072 */
2073 function give_get_user_agent() {
2074
2075 // Get User Agent.
2076 $user_agent = ! empty( $_SERVER['HTTP_USER_AGENT'] ) ? give_clean( $_SERVER['HTTP_USER_AGENT'] ) : ''; // WPCS: input var ok.
2077
2078 return $user_agent;
2079 }
2080
2081 /**
2082 * Set a cookie - wrapper for setcookie using WP constants.
2083 *
2084 * @param string $name Name of the cookie being set.
2085 * @param string $value Value of the cookie.
2086 * @param integer $expire Expiry of the cookie.
2087 * @param bool $secure Whether the cookie should be served only over https.
2088 *
2089 * @since 2.2.0
2090 */
2091 function give_setcookie( $name, $value, $expire = 0, $secure = false ) {
2092 if ( ! headers_sent() ) {
2093 setcookie(
2094 $name,
2095 $value,
2096 $expire,
2097 COOKIEPATH ? COOKIEPATH : '/',
2098 COOKIE_DOMAIN,
2099 $secure,
2100 apply_filters( 'give_cookie_httponly', false, $name, $value, $expire, $secure )
2101 );
2102 }
2103 }
2104
2105 /**
2106 * Get formatted billing address.
2107 *
2108 * @param array $address
2109 *
2110 * @return string Formatted address.
2111 * @since 2.2.0
2112 */
2113 function give_get_formatted_address( $address = [] ) {
2114 $formatted_address = '';
2115
2116 /**
2117 * Address format.
2118 *
2119 * @since 2.2.0
2120 */
2121 $address_format = apply_filters( 'give_address_format_template', "{street_address}\n{city}, {state} {postal_code}\n{country}" );
2122 preg_match_all( '/{([A-z0-9\-\_\ ]+)}/s', $address_format, $matches );
2123
2124 if ( ! empty( $matches ) && ! empty( $address ) ) {
2125 $address_values = [];
2126
2127 foreach ( $matches[1] as $address_tag ) {
2128 $address_values[ $address_tag ] = '';
2129
2130 if ( isset( $address[ $address_tag ] ) ) {
2131 $address_values[ $address_tag ] = $address[ $address_tag ];
2132 }
2133 }
2134
2135 $formatted_address = str_ireplace( $matches[0], $address_values, $address_format );
2136 }
2137
2138 /**
2139 * Give get formatted address.
2140 *
2141 * @param string $formatted_address Formatted address.
2142 * @param string $address_format Format of the address.
2143 *
2144 * @since 2.2.0
2145 */
2146 $formatted_address = apply_filters( 'give_get_formatted_address', $formatted_address, $address_format, $address );
2147
2148 return $formatted_address;
2149 }
2150
2151 /**
2152 * Get safe url for assets
2153 * Note: this function will return url without http protocol
2154 *
2155 * @param string $url URL
2156 *
2157 * @return string
2158 * @since 2.2.0
2159 */
2160 function give_get_safe_asset_url( $url ) {
2161
2162 // Bailout, if empty URL passed.
2163 if ( empty( $url ) ) {
2164 return $url;
2165 }
2166
2167 $schema = parse_url( $url, PHP_URL_SCHEME );
2168 $schema_length = strlen( $schema ) + 1;
2169 $url = substr( $url, $schema_length );
2170
2171 /**
2172 * Fire the filter
2173 *
2174 * @since 2.2.0
2175 */
2176 return apply_filters( 'give_get_safe_asset_url', $url );
2177 }
2178
2179 /**
2180 * Give get formatted date.
2181 * Note: This function does not work well with localize translated date strings
2182 *
2183 * @param string $date Date.
2184 * @param string $format Date Format.
2185 * @param string $current_format Current date Format.
2186 * @param bool $localize
2187 *
2188 * @return string
2189 * @since 2.3.0
2190 */
2191 function give_get_formatted_date( $date, $format = 'Y-m-d', $current_format = '', $localize = false ) {
2192 $current_format = empty( $current_format ) ? give_date_format() : $current_format;
2193 $date_obj = DateTime::createFromFormat( $current_format, $date );
2194 $formatted_date = '';
2195
2196 if ( $date_obj instanceof DateTime ) {
2197 $formatted_date = $localize ?
2198 date_i18n( $format, $date_obj->getTimestamp() ) :
2199 $date_obj->format( $format );
2200 }
2201
2202 /**
2203 * Give get formatted date.
2204 *
2205 * @param string $formatted_date Formatted date.
2206 * @param array
2207 *
2208 * @since 2.3.0
2209 */
2210 return apply_filters( 'give_get_formatted_date', $formatted_date, [ $date, $format, $current_format ] );
2211 }
2212
2213 /**
2214 * This function will be used to fetch the donation receipt link.
2215 *
2216 * @param int $donation_id Donation ID.
2217 *
2218 * @return string
2219 * @since 2.3.1
2220 */
2221 function give_get_receipt_link( $donation_id ) {
2222
2223 return sprintf(
2224 '<a href="%1$s">%2$s</a>',
2225 esc_url( give_get_receipt_url( $donation_id ) ),
2226 esc_html__( 'View the receipt in your browser &raquo;', 'give' )
2227 );
2228
2229 }
2230
2231 /**
2232 * Get receipt_url
2233 *
2234 * @param int $donation_id Donation ID.
2235 *
2236 * @return string
2237 * @since 2.0
2238 */
2239 function give_get_receipt_url( $donation_id ) {
2240
2241 $receipt_url = esc_url_raw(
2242 add_query_arg(
2243 [
2244 'donation_id' => $donation_id,
2245 ],
2246 give_get_history_page_uri()
2247 )
2248 );
2249
2250 return $receipt_url;
2251 }
2252
2253 /**
2254 * Get "View in browser" Receipt Link for email.
2255 *
2256 * @param int $donation_id Donation ID.
2257 *
2258 * @return string
2259 * @since 2.4.1
2260 */
2261 function give_get_view_receipt_link( $donation_id ) {
2262
2263 return sprintf(
2264 '<a href="%1$s">%2$s</a>',
2265 give_get_view_receipt_url( $donation_id ),
2266 esc_html__( 'View the receipt in your browser &raquo;', 'give' )
2267 );
2268
2269 }
2270
2271 /**
2272 * Get "View in browser" Receipt URL for email.
2273 *
2274 * @param int $donation_id Donation ID.
2275 *
2276 * @return string
2277 * @since 2.4.1
2278 */
2279 function give_get_view_receipt_url( $donation_id ) {
2280
2281 $receipt_url = esc_url_raw(
2282 add_query_arg(
2283 [
2284 'action' => 'view_in_browser',
2285 '_give_hash' => give_get_payment_key( $donation_id ),
2286 ],
2287 give_get_history_page_uri()
2288 )
2289 );
2290
2291 return $receipt_url;
2292 }
2293
2294 /**
2295 * This function is used to display donation receipt content based on the parameters.
2296 *
2297 * @param $args
2298 *
2299 * @return bool|mixed
2300 * @since 2.4.1
2301 */
2302 function give_display_donation_receipt( $args ) {
2303
2304 global $give_receipt_args;
2305
2306 $give_receipt_args = $args;
2307
2308 ob_start();
2309
2310 $get_data = give_clean( filter_input_array( INPUT_GET ) );
2311 $donation_id = ! empty( $get_data['donation_id'] ) ? $get_data['donation_id'] : false;
2312 $receipt_type = ! empty( $get_data['receipt_type'] ) ? $get_data['receipt_type'] : false;
2313
2314 $give_receipt_args['id'] = $donation_id;
2315
2316 if ( 'view_in_browser' !== $receipt_type ) {
2317
2318 $email_access = give_get_option( 'email_access' );
2319 $is_email_access = give_is_setting_enabled( $email_access ) && ! Give()->email_access->token_exists;
2320
2321 // No donation id found & Email Access is Turned on.
2322 if ( ! $donation_id ) {
2323
2324 if ( $is_email_access ) {
2325 give_get_template_part( 'email-login-form' );
2326 } else {
2327 echo Give_Notices::print_frontend_notice( $args['error'], false, 'error' );
2328 }
2329
2330 return ob_get_clean();
2331 }
2332
2333 // Donation id provided, but user is logged out. Offer them the ability to login and view the receipt.
2334 if ( ! ( $user_can_view = give_can_view_receipt( $donation_id ) ) ) {
2335
2336 if ( true === Give()->session->get( 'donor_donation_mismatch' ) ) {
2337
2338 /**
2339 * This filter will be used to modify the donor mismatch text for front end error notice.
2340 *
2341 * @since 2.3.1
2342 */
2343 $donor_mismatch_text = apply_filters( 'give_receipt_donor_mismatch_notice_text', __( 'You are trying to access invalid donation receipt. Please try again.', 'give' ) );
2344
2345 echo Give_Notices::print_frontend_notice(
2346 $donor_mismatch_text,
2347 false,
2348 'error'
2349 );
2350
2351 } elseif ( $is_email_access ) {
2352
2353 give_get_template_part( 'email-login-form' );
2354
2355 } else {
2356
2357 global $give_login_redirect;
2358
2359 $give_login_redirect = give_get_current_page_url();
2360
2361 Give_Notices::print_frontend_notice(
2362 apply_filters(
2363 'give_must_be_logged_in_error_message',
2364 __( 'You must be logged in to view this donation receipt.', 'give' )
2365 )
2366 );
2367
2368 give_get_template_part( 'shortcode', 'login' );
2369 }
2370
2371 return ob_get_clean();
2372 }
2373
2374 /**
2375 * Check if the user has permission to view the receipt.
2376 *
2377 * If user is logged in, user ID is compared to user ID of ID stored in payment meta
2378 * or if user is logged out and donation was made as a guest, the donation session is checked for
2379 * or if user is logged in and the user can view sensitive shop data.
2380 */
2381 if ( ! apply_filters( 'give_user_can_view_receipt', $user_can_view, $args ) ) {
2382 return Give_Notices::print_frontend_notice( $args['error'], false, 'error' );
2383 }
2384 } else {
2385 $donation_id = give_get_donation_id_by_key( $get_data['donation_id'] );
2386 $give_receipt_args['id'] = $donation_id;
2387 }
2388
2389 give_get_template_part( 'shortcode', 'receipt' );
2390
2391 return ob_get_clean();
2392 }
2393
2394
2395 /**
2396 * Get plugin add-on readme.txt path
2397 * Note: only for internal use
2398 *
2399 * @param $plugin_slug
2400 * @param bool $by_plugin_name
2401 *
2402 * @return mixed|void
2403 * @since 2.5.0
2404 */
2405 function give_get_addon_readme_url( $plugin_slug, $by_plugin_name = false ) {
2406
2407 if ( $by_plugin_name ) {
2408 $plugin_slug = Give_License::get_short_name( $plugin_slug );
2409 }
2410
2411 $website_url = trailingslashit( Give_License::get_website_url() );
2412
2413 /**
2414 * Filter the addon readme.txt url
2415 *
2416 * @since 2.1.4
2417 */
2418 $url = apply_filters(
2419 'give_addon_readme_file_url',
2420 "{$website_url}downloads/plugins/{$plugin_slug}/readme.txt",
2421 $plugin_slug,
2422 $by_plugin_name
2423 );
2424
2425 return $url;
2426 }
2427
2428 /**
2429 * Refresh all givewp license.
2430 *
2431 * @since 4.15.0 add support for Harbor unified licenses
2432 * @since 4.8.0 Update active license date after license refresh when active license is found
2433 * @since 4.3.0 updated to store platform fee percentage
2434 * @since 2.27.0 delete update_plugins transient instead of invalidate it
2435 * @since 2.5.0
2436 *
2437 * @param bool $wp_check_updates
2438 *
2439 * @access public
2440 * @return array|WP_Error
2441 */
2442 function give_refresh_licenses( $wp_check_updates = true ) {
2443 $give_licenses = get_option( 'give_licenses', [] );
2444 $give_addons = give_get_plugins( [ 'only_premium_add_ons' => true ] );
2445
2446 if ( ! $give_licenses && ! $give_addons ) {
2447 return [];
2448 }
2449
2450 $license_keys = $give_licenses ? implode( ',', array_keys( $give_licenses ) ) : '';
2451
2452 // Exclude addons already licensed via Harbor from the unlicensed list sent to the legacy API.
2453 $legacy_unlicensed_addons = array_filter( $give_addons, static function ( $plugin ) {
2454 return ! $plugin['License'];
2455 } );
2456
2457 $unlicensed_give_addon = $legacy_unlicensed_addons
2458 ? array_values(
2459 array_diff(
2460 array_map(
2461 function ( $plugin_name ) {
2462 return trim( str_replace( 'Give - ', '', $plugin_name ) );
2463 },
2464 wp_list_pluck( $legacy_unlicensed_addons, 'Name', true )
2465 ),
2466 wp_list_pluck( $give_licenses, 'item_name', true )
2467 )
2468 )
2469 : [];
2470
2471 // Nothing for the legacy API to check.
2472 if ( empty( $give_licenses ) && empty( $unlicensed_give_addon ) ) {
2473 return [];
2474 }
2475
2476 $tmp = Give_License::request_license_api(
2477 [
2478 'edd_action' => 'check_licenses',
2479 'licenses' => $license_keys,
2480 'unlicensed' => implode( ',', $unlicensed_give_addon ),
2481 ],
2482 true
2483 );
2484
2485 if ( ! $tmp || is_wp_error( $tmp ) ) {
2486 return [];
2487 }
2488
2489 // Prevent fatal error on WP 4.9.10
2490 // Because wp_list_pluck accept only array or array of array in that version.
2491 // @see https://github.com/impress-org/give/issues/4176
2492 $tmp = json_decode( json_encode( $tmp ), true );
2493
2494 // Remove unlicensed add-on from response.
2495 $tmp_unlicensed = [];
2496 foreach ( $tmp as $key => $data ) {
2497 if ( empty( $data ) ) {
2498 unset( $tmp[ "{$key}" ] );
2499 continue;
2500 }
2501
2502 if ( empty( $data['check_license'] ) ) {
2503 $tmp_unlicensed[ $key ] = $data;
2504 unset( $tmp[ "{$key}" ] );
2505 }
2506 }
2507
2508 $check_licenses = wp_list_pluck( $tmp, 'check_license' );
2509
2510 /* @var stdClass $data */
2511 foreach ( $check_licenses as $key => $data ) {
2512 if ( is_wp_error( $data ) ) {
2513 continue;
2514 }
2515
2516 if ( ! $data['success'] ) {
2517 unset( $give_licenses[ $key ] );
2518 continue;
2519 }
2520
2521 $give_licenses[ $key ] = $data;
2522 }
2523
2524 $tmp_update_plugins = array_merge(
2525 array_filter( wp_list_pluck( $tmp, 'get_version' ) ),
2526 array_filter( wp_list_pluck( $tmp, 'get_versions' ) )
2527 );
2528
2529 if ( $tmp_unlicensed ) {
2530 $tmp_update_plugins = array_merge( $tmp_update_plugins, $tmp_unlicensed );
2531 }
2532
2533 update_option( 'give_licenses', $give_licenses, 'no' );
2534 update_option( 'give_get_versions', $tmp_update_plugins, 'no' );
2535
2536 $refresh = Give_License::refresh_license_status();
2537 $refresh['time'] = time();
2538
2539 update_option( 'give_licenses_refreshed_last_checked', $refresh, 'no' );
2540
2541 $platform_fee_percentage = get_platform_fee_from_licenses();
2542
2543 if (!is_null($platform_fee_percentage)) {
2544 update_option( LicenseOptionKeys::PLATFORM_FEE_PERCENTAGE, $platform_fee_percentage, 'no' );
2545 }
2546
2547 // Update active license date after license refresh when active license is found
2548 $active_license_date = get_active_license_date();
2549
2550 if(!is_null($active_license_date)) {
2551 update_option(LicenseOptionKeys::LAST_ACTIVE_LICENSE_DATE, $active_license_date, 'no');
2552 }
2553
2554 // Tell WordPress to look for updates.
2555 if ( $wp_check_updates ) {
2556 delete_site_transient('update_plugins');
2557 }
2558
2559 return [
2560 'give_licenses' => $give_licenses,
2561 'give_get_versions' => $tmp_update_plugins,
2562 ];
2563 }
2564
2565 /**
2566 * Get platform fee from stored licenses
2567 */
2568 function get_platform_fee_from_licenses(): ?float
2569 {
2570 /** @var LicenseRepository $repository */
2571 $repository = give(LicenseRepository::class);
2572
2573 return $repository->findLowestPlatformFeePercentageFromActiveLicenses();
2574 }
2575
2576 /**
2577 * Set Active License Date
2578 *
2579 * @since 4.8.0
2580 */
2581 function get_active_license_date(): ?int
2582 {
2583 /** @var LicenseRepository $repository */
2584 $repository = give(LicenseRepository::class);
2585
2586 return $repository->getCurrentActiveLicenseDate();
2587 }
2588
2589 /**
2590 * Check add-ons updates
2591 * Note: only for internal use
2592 *
2593 * @param stdClass $_transient_data Plugin updates information
2594 *
2595 * @return stdClass
2596 * @since 2.5.0
2597 */
2598 function give_check_addon_updates( $_transient_data ) {
2599 if ( ! is_object( $_transient_data ) ) {
2600 $_transient_data = new stdClass();
2601 }
2602
2603 $update_plugins = get_option( 'give_get_versions', [] );
2604 $check_licenses = get_option( 'give_licenses', [] );
2605
2606 // Skip legacy update checks when Harbor is managing licensing and no legacy licenses exist.
2607 if ( empty( $check_licenses ) && lw_harbor_is_product_license_active( 'give' ) ) {
2608 return $_transient_data;
2609 }
2610
2611 if ( ! $update_plugins ) {
2612 $data = give_refresh_licenses( false );
2613
2614 if (
2615 empty( $data['give_get_versions'] )
2616 || is_wp_error( $data )
2617 ) {
2618 return $_transient_data;
2619 }
2620
2621 $update_plugins = $data['give_get_versions'];
2622 }
2623
2624 foreach ( $update_plugins as $key => $data ) {
2625 $plugins = ! empty( $check_licenses[ $key ] )
2626 ? ( ! empty( $check_licenses[ $key ]['is_all_access_pass'] ) ? $data : [ $data ] )
2627 : [ $data ];
2628
2629 foreach ( $plugins as $plugin ) {
2630 // This value will be empty if any error occurred when verifying version of add-on.
2631 if ( empty( $plugin['new_version'] ) ) {
2632 continue;
2633 }
2634
2635 $plugin = array_map( 'maybe_unserialize', $plugin );
2636 $tmp_plugin = Give_License::get_plugin_by_slug( $plugin['slug'] );
2637
2638 if ( ! $tmp_plugin ) {
2639 continue;
2640 }
2641
2642 $plugin['plugin'] = $tmp_plugin['Path'];
2643
2644 if ( - 1 !== version_compare( $tmp_plugin['Version'], $plugin['new_version'] ) ) {
2645 $_transient_data->no_update[ $tmp_plugin['Path'] ] = (object) $plugin;
2646 } else {
2647 $_transient_data->response[ $tmp_plugin['Path'] ] = (object) $plugin;
2648 }
2649
2650 $_transient_data->checked[ $tmp_plugin['Path'] ] = $tmp_plugin['Version'];
2651 }
2652 }
2653
2654 $_transient_data->last_checked = time();
2655
2656 return $_transient_data;
2657 }
2658
2659 /**
2660 * Get page by title
2661 *
2662 * @since 2.26.0
2663 *
2664 * @param string $page_title
2665 * @param string $output
2666 * @param string $post_type
2667 *
2668 * @return null|WP_Post
2669 */
2670 function give_get_page_by_title(string $page_title, string $output = OBJECT, string $post_type = 'page')
2671 {
2672 $args = [
2673 'title' => $page_title,
2674 'post_type' => $post_type,
2675 'post_status' => get_post_stati(),
2676 'posts_per_page' => 1,
2677 'update_post_term_cache' => false,
2678 'update_post_meta_cache' => false,
2679 'no_found_rows' => true,
2680 'orderby' => 'post_date ID',
2681 'order' => 'ASC',
2682 ];
2683 $query = new WP_Query($args);
2684 $pages = $query->posts;
2685
2686 if (empty($pages)) {
2687 return null;
2688 }
2689
2690 return get_post($pages[0], $output);
2691 }
2692