Issues (1282)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

includes/admin/class-addon-activation-banner.php (8 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * Give Activation Banner
4
 *
5
 * @author  GiveWP
6
 * @version 1.0
7
 */
8
9
// Exit if accessed directly.
10
if ( ! defined( 'ABSPATH' ) ) {
11
	exit;
12
}
13
14
global $give_addons;
15
16
/**
17
 * Class Give_Addon_Activation_Banner
18
 *
19
 * @since  2.1.0 Added pleasing interface when multiple add-ons are activated.
20
 */
21
class Give_Addon_Activation_Banner {
22
23
	/**
24
	 * Class constructor.
25
	 *
26
	 * @since  1.0
27
	 * @access public
28
	 *
29
	 * @param array $_banner_details {
30
	 *                               'file'              => __FILE__, // (required) Directory path to the main plugin file
31
	 *                               'name'              => __( 'Authorize.net Gateway', 'give-authorize' ), // (required) Name of the Add-on
32
	 *                               'version'           => GIVE_AUTHORIZE_VERSION, // (required)The most current version
33
	 *                               'documentation_url' => 'http://docs.givewp.com/addon-authorize',// (required)
34
	 *                               'support_url'       => 'https://givewp.com/support/', // (required)Location of Add-on settings page, leave blank to hide
35
	 *                               'testing'           => false, // (required) Never leave as "true" in production!!!
36
	 *                               }
37
	 */
38
	function __construct( $_banner_details ) {
0 ignored issues
show
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
39
		global $give_addons, $pagenow;
40
41
		// Append add-on information to the global variable.
42
		$give_addons[] = $_banner_details;
43
44
		if ( 'plugins.php' === $pagenow ) {
45
46
			// Get the current user.
47
			$current_user  = wp_get_current_user();
48
			$this->user_id = $current_user->ID;
49
50
			// Set up hooks.
51
			$this->init();
52
53
			// Store user id who activated plugin.
54
			$this->add_addon_activate_meta();
55
56
			// Check if notice callback is already hooked.
57
			if ( ! $this->is_banner_notice_hooked() ) {
58
				// If multiple add-on are activated then show activation banner in tab view.
59
				add_action( 'admin_notices', array( $this, 'addon_activation_banner_notices' ), 10 );
60
			}
61
		}
62
	}
63
64
	/**
65
	 * Get the meta key name.
66
	 *
67
	 * @since 2.1
68
	 * @param string $addon_banner_key
69
	 *
70
	 * @return string
71
	 */
72
	public static function get_banner_user_meta_key( $addon_banner_key ) {
73
		$addon_slug = sanitize_text_field( $addon_banner_key );
74
75
		return "give_addon_{$addon_slug}_active_by_user";
76
	}
77
78
	/**
79
	 * Set up WordPress filters to hook into WP's update process.
80
	 *
81
	 * @since  1.0
82
	 * @access public
83
	 *
84
	 * @return void
85
	 */
86
	public function init() {
87
		// Get the current page to add the notice to
88
		add_action( 'current_screen', array( $this, 'give_addon_notice_ignore' ) );
89
90
		// Get the Give add-ons.
91
		$give_addons = $this->get_plugin_file_names();
92
93
		if ( ! empty( $give_addons ) ) {
94
95
			// Go through each of the add-on and hook deactivate action.
96
			foreach ( $give_addons as $addon_name => $give_addon ) {
97
98
				// Testing?
99
				if ( true === $give_addon['testing'] ) {
100
					$nag_meta_key = 'give_addon_activation_ignore_' . $addon_name;
101
					delete_user_meta( $this->user_id, $nag_meta_key );
102
				}
103
104
				// Add deactivate hook.
105
				add_action( 'deactivate_' . $give_addon['plugin_main_file'], array( $this, 'remove_addon_activate_meta' ) );
106
			}
107
		}
108
	}
109
110
	/**
111
	 * Get plugin file name.
112
	 *
113
	 * @since   1.8
114
	 * @access  private
115
	 * @return mixed
116
	 */
117
	private function get_plugin_file_names() {
118
		global $give_addons;
119
120
		// Get recently activated plugins.
121
		$active_plugins = get_option( 'active_plugins' );
122
123
		$file_names = array();
124
125
		if ( empty( $give_addons ) ) {
126
			return $file_names;
127
		}
128
129
		// Go through each addon and get the plugin file url.
130
		foreach ( $give_addons as $give_addon ) {
131
			$file_name = '';
132
			$file_path = explode( '/plugins/', $give_addon['file'] );
133
			if ( $file_path ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $file_path of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
134
				$file_path = array_pop( $file_path );
135
				$file_name = current( explode( '/', $file_path ) );
136
			}
137
138
			if ( ! empty( $file_name ) ) {
139
				foreach ( $active_plugins as $plugin ) {
140
					if ( false !== strpos( $plugin, $file_name ) ) {
141
						$add_on_key                     = sanitize_title( $give_addon['name'] );
142
						$give_addon['plugin_main_file'] = $plugin; // Include plugin file.
143
						$file_names[ $add_on_key ]      = $give_addon;
144
						break;
145
					}
146
				}
147
			}
148
		}
149
150
		return $file_names;
151
	}
152
153
	/**
154
	 * Setup user id to option
155
	 *
156
	 * @since  1.8
157
	 * @access private
158
	 */
159
	private function add_addon_activate_meta() {
160
		// Get all activated add-ons.
161
		$give_addons = $this->get_plugin_file_names();
162
163
		if ( ! empty( $give_addons ) ) {
164
165
			// Go through each add-ons and add meta data.
166
			foreach ( $give_addons as $banner_addon_name => $addon ) {
167
168
				// User meta key.
169
				$user_id = __give_get_active_by_user_meta( $banner_addon_name );
170
171
				if ( ! $user_id ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $user_id of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
172
					$option_key = self::get_banner_user_meta_key( $banner_addon_name );
173
174
					// store user id who activated add-on.
175
					update_option( $option_key, $this->user_id, false );
176
177
					// Update global cache.
178
					$GLOBALS['give_addon_activated_by_user'][$option_key] = $this->user_id;
179
				}
180
			}
181
		}
182
	}
183
184
	/**
185
	 * Check if the addon_activation_banner_notices function has already been hooked to admin_notice.
186
	 *
187
	 * @since 2.1.0
188
	 *
189
	 * @return bool
190
	 */
191
	public function is_banner_notice_hooked() {
192
		global $wp_filter;
193
		$notice_already_hooked = false;
194
195
		if ( isset( $wp_filter['admin_notices']->callbacks[10] ) ) {
196
			// Get all of the hooks.
197
			$admin_notice_callbacks = array_keys( $wp_filter['admin_notices']->callbacks[10] );
198
199
			if ( ! empty( $admin_notice_callbacks ) ) {
200
				foreach ( $admin_notice_callbacks as $key ) {
201
					//If the key is found in your string, set $found to true
202
					if ( false !== strpos( $key, 'addon_activation_banner_notices' ) ) {
203
						$notice_already_hooked = true;
204
					}
205
				}
206
			}
207
		}
208
209
		return $notice_already_hooked;
210
	}
211
212
	/**
213
	 * Get the add-on banner notices.
214
	 *
215
	 * @since 2.1.0
216
	 */
217
	public function addon_activation_banner_notices() {
218
		global $pagenow, $give_addons;
219
220
		// Bailout.
221
		if ( 'plugins.php' !== $pagenow ) {
222
			return false;
223
		}
224
225
		// Store the add-ons of which activation banner should be shown.
226
		$addon_to_display = array();
227
228
		// Get recently activated add-ons.
229
		$recent_activated = $this->get_recently_activated_addons();
230
		$latest_addon     = array();
231
232
		// Get the plugin folder name, because many give-addon not sending proper plugin_file.
233
		if ( ! empty( $recent_activated ) ) {
234
			foreach ( $recent_activated as $recent_addon ) {
235
				// Get the add-on folder name.
236
				$latest_addon[] = substr( $recent_addon, 0, strpos( $recent_addon, '/' ) );
237
			}
238
		}
239
240
		// Go through each of the give add-on.
241
		foreach ( $give_addons as $addon ) {
242
			$addon_sanitized_name = sanitize_title( $addon['name'] );
243
244
			// Get the add-on dismiss status.
245
			$add_on_state = get_user_meta( $this->user_id, "give_addon_activation_ignore_{$addon_sanitized_name}", true );
246
247
			// Get the option key.
248
			$activate_by_user = (int) __give_get_active_by_user_meta( $addon_sanitized_name );
249
250
			// Remove plugin file and get the Add-on's folder name only.
251
			$file_path = $this->get_plugin_folder_name( $addon['file'] );
252
253
			// If add-on were never dismissed.
254
			if ( 'true' !== $add_on_state && $this->user_id === $activate_by_user ) {
255
				if ( ! empty( $latest_addon ) && ( in_array( $file_path, $latest_addon, true ) || empty( $latest_addon ) ) ) {
256
					$addon_to_display[] = $addon;
257
				}
258
			}
259
		}
260
261
		if ( ! empty( $addon_to_display ) ) {
262
			ob_start();
263
264
			// Output inline styles here because there's no reason
265
			// to enqueued them after the alert is dismissed.
266
			$this->print_css_js();
0 ignored issues
show
The call to the method Give_Addon_Activation_Banner::print_css_js() seems un-needed as the method has no side-effects.

PHP Analyzer performs a side-effects analysis of your code. A side-effect is basically anything that might be visible after the scope of the method is left.

Let’s take a look at an example:

class User
{
    private $email;

    public function getEmail()
    {
        return $this->email;
    }

    public function setEmail($email)
    {
        $this->email = $email;
    }
}

If we look at the getEmail() method, we can see that it has no side-effect. Whether you call this method or not, no future calls to other methods are affected by this. As such code as the following is useless:

$user = new User();
$user->getEmail(); // This line could safely be removed as it has no effect.

On the hand, if we look at the setEmail(), this method _has_ side-effects. In the following case, we could not remove the method call:

$user = new User();
$user->setEmail('email@domain'); // This line has a side-effect (it changes an
                                 // instance variable).
Loading history...
267
			?>
268
			<div class="<?php echo ( 1 !== count( $addon_to_display ) ) ? 'give-alert-tab-wrapper' : ''; ?> updated give-addon-alert give-notice">
269
				<?php
270
				// If multiple add-on are activated.
271
				if ( 1 !== count( $addon_to_display ) ) {
272
					?>
273
					<div class="give-vertical-tab">
274
						<div class="give-addon-tab-list">
275
							<ul class="give-alert-addon-list">
276
								<?php
277
								$is_first = true;
278 View Code Duplication
								foreach ( $addon_to_display as $banner ) {
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
279
									?>
280
									<li class="give-tab-list<?php echo ( true === $is_first ) ? ' active' : ''; ?>" id="give-addon-<?php echo esc_attr( basename( $banner['file'], '.php' ) ); ?>">
281
										<a href="#"><?php echo esc_html( $banner['name'] ); ?></a>
282
									</li>
283
									<?php
284
									$is_first = false;
285
								}
286
								$is_first = true;
287
								?>
288
							</ul>
289
						</div>
290
						<div class="give-right-side-block">
291 View Code Duplication
							<?php foreach ( $addon_to_display as $banner ) : ?>
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
292
								<div class="give-tab-details <?php echo ( true === $is_first ) ? ' active' : ''; ?> " id="give-addon-<?php echo esc_attr( basename( $banner['file'], '.php' ) ); ?>">
293
									<?php
294
										// Render single add banner.
295
										$this->render_single_addon_banner( $banner, false );
296
										$is_first = false;
297
									?>
298
								</div>
299
							<?php endforeach; ?>
300
						</div>
301
					</div>
302
					<?php
303
				} else {
304
					$this->render_single_addon_banner( $addon_to_display[0], true );
305
				}
306
				?>
307
			</div>
308
			<?php
309
			$notice_html = ob_get_clean();
310
311
			// Register notice.
312
			Give()->notices->register_notice( array(
313
				'id'               => 'give_add_on_activation_notice',
314
				'type'             => 'updated',
315
				'description_html' => $notice_html,
316
				'show'             => true,
317
			) );
318
		}
319
	}
320
321
	/**
322
	 * Render single banner activation
323
	 *
324
	 * @since 2.1.0
325
	 *
326
	 * @param array $banner_arr Banner options.
327
	 * @param bool  $is_single  Is single.
328
	 */
329
	private function render_single_addon_banner( $banner_arr, $is_single ) {
330
		// Get all give add-on.
331
		$give_addons = give_get_plugins();
332
333
		// Plugin main file.
334
		$plugin_file = $banner_arr['file'];
335
336
		// Get the plugin main file.
337
		foreach ( $give_addons as $main_file => $addon ) {
338
			// Plugin should be activated.
339
			if ( ! is_plugin_active( $main_file ) ) {
340
				continue;
341
			}
342
343
			if (
344
				isset( $banner_arr['name'] )
345
				&& 'add-on' === $addon['Type']
346
				&& $this->get_plugin_folder_name( $main_file ) === $this->get_plugin_folder_name( $plugin_file )
347
			) {
348
				$plugin_file = WP_PLUGIN_DIR . '/' . $main_file;
349
				break;
350
			}
351
		}
352
353
		// Create dismiss URL.
354
		$dismiss_url = $is_single
355
			? admin_url( 'plugins.php?give_addon_activation_ignore=1&give_addon=' . sanitize_title( $banner_arr['name'] ) )
356
			: admin_url( 'plugins.php?give_addon_activation_ignore=1&give_addon=all' );
357
358
		// Get the add-on details.
359
		$plugin_data = get_plugin_data( $plugin_file );
360
		?>
361
		<img src="<?php echo esc_url( GIVE_PLUGIN_URL . 'assets/dist/images/give-icon-full-circle.svg' ); ?>" class="give-logo" />
362
		<div class="give-alert-message">
363
			<h3>
364
				<?php
365
				printf(
366
					/* translators: %s: Add-on name */
367
					'%s<span>%s</span>',
368
					__( 'New Give Add-on Activated: ', 'give' ),
369
					esc_html( $banner_arr['name'] )
370
				);
371
				?>
372
			</h3>
373
			<a href="<?php echo esc_url( $dismiss_url ); ?>" class="dismiss">
374
				<span class="dashicons dashicons-dismiss"></span>
375
			</a>
376
			<div class="alert-actions">
377
				<?php
378
				//Point them to your settings page.
379
				if ( ! empty( $plugin_data['Description'] ) ) {
380
					?>
381
					<span class="give-addon-description">
382
					<em><?php echo esc_html( strip_tags( $plugin_data['Description'] ) ); ?></em></span><br />
383
					<?php
384
				}
385 View Code Duplication
				if ( isset( $banner_arr['settings_url'] ) ) {
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
386
					printf(
387
						'<a href="%s"><span class="dashicons dashicons-admin-settings"></span>%s</a>',
388
						esc_url( $banner_arr['settings_url'] ),
389
						esc_html__( 'Go to Settings', 'give' )
390
					);
391
				}
392
				// Show them how to configure the Addon.
393
				if ( isset( $banner_arr['documentation_url'] ) ) {
394
					printf(
395
						'<a href="%s" target="_blank"><span class="dashicons dashicons-media-text"></span>%s</a>',
396
						esc_url( $banner_arr['documentation_url'] ),
397
						sprintf(
398
							/* translators: %s: Add-on name */
399
							esc_html__( 'Documentation: %s Add-on', 'give' ),
400
							esc_html( $banner_arr['name'] )
401
						)
402
					);
403
				}
404
405
				//Let them signup for plugin updates
406 View Code Duplication
				if ( isset( $banner_arr['support_url'] ) ) {
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
407
					printf(
408
						'<a href="%s" target="_blank"><span class="dashicons dashicons-sos"></span>%s</a>',
409
						esc_url( $banner_arr['support_url'] ),
410
						esc_html__( 'Get Support', 'give' )
411
					);
412
				}
413
				?>
414
			</div>
415
		</div>
416
		<?php
417
	}
418
419
	/**
420
	 * Ignore Nag.
421
	 *
422
	 * This is the action that allows the user to dismiss the banner it basically sets a tag to their user meta data
423
	 *
424
	 * @since  1.0
425
	 * @access public
426
	 */
427
	public function give_addon_notice_ignore() {
428
		/**
429
		 * If user clicks to ignore the notice, add that to their user meta the banner then checks whether this tag exists already or not.
430
		 * See here: http://codex.wordpress.org/Function_Reference/add_user_meta
431
		 */
432
		if (
433
			isset( $_GET['give_addon'], $_GET['give_addon_activation_ignore'] )
434
			&& '1' === $_GET['give_addon_activation_ignore']
435
		) {
436
			// Get the value of the 'give_addon' query string.
437
			$addon_query_arg    = sanitize_text_field( wp_unslash( $_GET['give_addon'] ) );
438
			$deactivated_addons = array();
439
440
			// If All add-on requested to dismiss.
441
			if ( 'all' === $addon_query_arg ) {
442
				// Get all activated add-ons.
443
				$give_addons = $this->get_plugin_file_names();
444
445
				if ( ! empty( $give_addons ) ) {
446
					$deactivated_addons = array_keys( $give_addons );
447
				}
448
			} else {
449
				// Store the addon to deactivate.
450
				$deactivated_addons[] = $addon_query_arg;
451
			}
452
453
			if ( ! empty( $deactivated_addons ) ) {
454
				foreach ( $deactivated_addons as $addon ) {
455
					// Record it user meta.
456
					add_user_meta( $this->user_id, "give_addon_activation_ignore_{$addon}", 'true', true );
457
				}
458
			}
459
		}
460
	}
461
462
	/**
463
	 * Delete user id from option if plugin deactivated.
464
	 *
465
	 * @since  1.8
466
	 * @since  2.1.0 Added support for multiple addons.
467
	 * @access public
468
	 */
469
	public function remove_addon_activate_meta() {
470
		// Get the hook name and then grab the plugin file from it.
471
		$plugin_file = str_replace( 'deactivate_', '', current_action() );
472
473
		// Get all activated add-ons.
474
		$give_addons = $this->get_plugin_file_names();
475
476
		if ( ! empty( $give_addons ) ) {
477
			foreach ( $give_addons as $banner_addon_name => $addon ) {
478
				if ( $plugin_file === $addon['plugin_main_file'] ) {
479
480
					// Get the user meta key.
481
					$user_id = (int) __give_get_active_by_user_meta( $banner_addon_name );
482
483
					if ( $user_id ) {
484
						// Get user meta for this add-on.
485
						$nag_meta_key = "give_addon_activation_ignore_{$banner_addon_name}";
486
487
						// Delete plugin activation option key.
488
						delete_option( self::get_banner_user_meta_key( $banner_addon_name ) );
489
						// Delete user meta of plugin activation.
490
						delete_user_meta( $user_id, $nag_meta_key );
491
					}
492
				}
493
			}
494
		}
495
	}
496
497
	/**
498
	 * Get list of add-on last activated.
499
	 *
500
	 * @since 2.1.0
501
	 *
502
	 * @return mixed|array
503
	 */
504
	public function get_recently_activated_addons() {
505
		return give_get_recently_activated_addons();
506
	}
507
508
	/**
509
	 * Get the addon's folder name.
510
	 *
511
	 * @since 2.1.0
512
	 *
513
	 * @param string $main_file Plugin Main File.
514
	 *
515
	 * @return bool|mixed|string
516
	 */
517
	public function get_plugin_folder_name( $main_file ) {
518
		// Remove plugin file and get the Add-on's folder name only.
519
		$file_path       = explode( '/plugins/', $main_file );
520
		$addon_file_path = array_pop( $file_path );
521
		$addon_file_path = substr( $addon_file_path, 0, strpos( $addon_file_path, '/' ) );
522
523
		return $addon_file_path;
524
	}
525
526
	/**
527
	 * Add activation banner css and js .
528
	 *
529
	 * @since  1.8.16
530
	 * @since  2.1.0 Added JS code for multiple add-on.
531
	 * @access private
532
	 */
533
	private function print_css_js() {
534
		?>
535
		<style>
536
			div.give-addon-alert.updated {
537
				padding: 20px;
538
				position: relative;
539
				border-color: #66BB6A;
540
				min-height: 85px;
541
			}
542
543
			div.give-alert-message {
544
				margin-left: 108px;
545
			}
546
547
			div.give-addon-alert img.give-logo {
548
				max-width: 85px;
549
				float: left;
550
			}
551
552
			div.give-addon-alert h3 {
553
				margin: -5px 0 10px;
554
				font-size: 22px;
555
				font-weight: 400;
556
				line-height: 30px;
557
			}
558
559
			div.give-addon-alert h3 span {
560
				font-weight: 700;
561
				color: #66BB6A;
562
			}
563
564
			div.give-addon-alert a {
565
				color: #66BB6A;
566
			}
567
568
			div.give-addon-alert .alert-actions a {
569
				margin-right: 2em;
570
			}
571
572
			div.give-addon-alert .alert-actions a {
573
				text-decoration: underline;
574
			}
575
576
			div.give-addon-alert .alert-actions a:hover {
577
				color: #555555;
578
			}
579
580
			div.give-addon-alert .alert-actions a span {
581
				text-decoration: none;
582
				margin-right: 5px;
583
			}
584
585
			div.give-addon-alert .dismiss {
586
				position: absolute;
587
				right: 0px;
588
				height: 99%;
589
				top: 23%;
590
				margin-top: -10px;
591
				outline: none;
592
				box-shadow: none;
593
				text-decoration: none;
594
				color: #AAA;
595
			}
596
597
			div.give-addon-alert .dismiss {
598
				position: absolute;
599
				right: 20px;
600
				height: 100%;
601
				top: 50%;
602
				margin-top: -10px;
603
				outline: none;
604
				box-shadow: none;
605
				text-decoration: none;
606
				color: #AAA;
607
			}
608
609
			div.give-alert-tab-wrapper .dismiss {
610
				right: 0px !important;
611
				height: 99% !important;
612
				top: 23% !important;
613
			}
614
615
			div.give-addon-alert .dismiss:hover {
616
				color: #333;
617
			}
618
619
			ul.give-alert-addon-list {
620
				min-width: 220px;
621
				display: inline-block;
622
				float: left;
623
				max-width: 250px;
624
				padding: 0;
625
				margin: 0;
626
				max-height: 146px;
627
				overflow: hidden;
628
			}
629
630
			div.give-addon-alert .give-addon-description {
631
				padding: 1px;
632
				display: inline-block;
633
				color: #777;
634
				margin-bottom: 12px;
635
			}
636
637
			div.give-alert-tab-wrapper .give-right-side-block {
638
				width: calc(100% - 250px);
639
				display: inline-block;
640
				float: left;
641
				background: #fff;
642
				position: relative;
643
			}
644
645
			div.give-vertical-tab {
646
				width: 100%;
647
			}
648
649
			ul.give-alert-addon-list li {
650
				display: block;
651
				border: 1px solid #d1d1d18f;
652
				border-width: 1px 0px 0px 0px;
653
				margin: 0;
654
			}
655
656
			ul.give-alert-addon-list li a.inactivate {
657
				cursor: default;
658
			}
659
660
			ul.give-alert-addon-list li a {
661
				display: block;
662
				font-weight: bold;
663
				color: #a3a3a3;
664
				text-decoration: none;
665
				padding: 15px 10px 15px;
666
				box-shadow: inset -6px 0px 18px 0px rgba(204, 204, 204, 0.36);
667
				-moz-box-shadow: inset -6px 0px 18px 0px rgba(204, 204, 204, 0.36);
668
				-webkit-box-shadow: inset -6px 0px 18px 0px rgba(204, 204, 204, 0.36);
669
				-ms-box-shadow: inset -6px 0px 18px 0px rgba(204, 204, 204, 0.36);
670
				-o-box-shadow: inset -6px 0px 18px 0px rgba(204, 204, 204, 0.36);
671
			}
672
673
			ul.give-alert-addon-list li.give-tab-list.active a {
674
				color: #5f6c74;
675
				box-shadow: none;
676
			}
677
678
			div.updated.give-addon-alert.give-notice.give-alert-tab-wrapper {
679
				display: inline-block;
680
				width: 100%;
681
			}
682
683
			.give-alert-tab-wrapper .give-tab-details {
684
				display: none;
685
				min-height: 100px;
686
				position: absolute;
687
				top: 0;
688
				left: 0;
689
				padding: 20px 20px 20px 40px;
690
			}
691
692
			.give-alert-tab-wrapper .give-tab-details.active {
693
				display: block;
694
				z-index: 1;
695
				position: relative;
696
			}
697
698
			.give-alert-tab-wrapper.give-addon-alert img.give-logo {
699
				max-width: 80px;
700
			}
701
702
			.give-alert-tab-wrapper .give-alert-message {
703
				margin-left: 100px;
704
				padding-top: 10px;
705
			}
706
707
			ul.give-alert-addon-list li.give-tab-list.active {
708
				background: #fff;
709
			}
710
711
			ul.give-alert-addon-list li.give-tab-list:last-child {
712
				border-bottom: 0px solid #ccc;
713
			}
714
715
			ul.give-alert-addon-list li.give-tab-list:first-child {
716
				border-top: 0 none;
717
			}
718
719
			.give-alert-tab-wrapper {
720
				padding: 0 !important;
721
			}
722
723
			ul.give-alert-addon-list::-webkit-scrollbar {
724
				height: 10px;
725
				width: 10px;
726
				border-radius: 4px;
727
				transition: all 0.3s ease;
728
				background: rgba(158, 158, 158, 0.15);
729
			}
730
731
			ul.give-alert-addon-list::-webkit-scrollbar-thumb {
732
				background: #939395;
733
				border-radius: 4px;
734
			}
735
736
			/** Responsiveness */
737
			@media screen and (max-width: 767px) {
738
				.give-alert-tab-wrapper .give-tab-details {
739
					padding: 20px 40px 20px 20px;
740
				}
741
742
				.give-alert-tab-wrapper .give-right-side-block {
743
					width: 100%;
744
				}
745
746
				.give-alert-tab-wrapper ul.give-alert-addon-list {
747
					min-width: 100%;
748
				}
749
			}
750
		</style>
751
752
		<!-- Start of the Give Add-on tab JS -->
753
		<script type="text/javascript">
754
					jQuery( document ).ready( function( $ ) {
755
						$( '.give-alert-tab-wrapper' ).on( 'click', '.give-tab-list', function() {
756
							if ( $( this ).find( 'a' ).hasClass( 'inactivate' ) ) {
757
								return false;
758
							}
759
760
							var
761
								clicked_tab = $( this ).attr( 'id' ),
762
								addon_tab_wrapper = $( this ).closest( '.give-alert-tab-wrapper' );
763
764
							// Remove 'active' class from all tab list.
765
							$( '.give-alert-addon-list li' ).removeClass( 'active' );
766
							// Add active class to the selected tab.
767
							$( this ).addClass( 'active' );
768
							// Remove 'active' class from the details.
769
							addon_tab_wrapper.find( '.give-tab-details' ).removeClass( 'active' );
770
							addon_tab_wrapper.find( '.give-right-side-block .give-tab-details#' + clicked_tab ).addClass( 'active' );
771
772
							return false;
773
						} );
774
775
						var add_on_tabs = $( '.give-alert-addon-list' );
776
777
						add_on_tabs
778
							.mouseout( function() {
779
								$( this ).css( 'overflow', 'hidden' );
780
							} )
781
							.mouseover( function() {
782
								$( this ).css( 'overflow', 'auto' );
783
							} );
784
785
						// Prevent default click event of the add-on.
786
						add_on_tabs.find( 'li a' ).on( 'click', function( e ) {
787
							e.preventDefault();
788
						} );
789
790
						// If total length of the add-on is 2.
791
						if ( 2 === add_on_tabs.find( 'li' ).length ) {
792
							var li = $( 'li.give-tab-list' );
793
							li.last().clone().prependTo( 'ul.give-alert-addon-list' );
794
							li.last().removeAttr( 'id' ).find( 'a' ).addClass( 'inactivate' ).html( '&nbsp;' );
795
							$( '.give-tab-list:first' ).trigger( 'click' );
796
						}
797
					} );
798
		</script>
799
		<!-- End of the Give Add-on tab JS -->
800
		<?php
801
	}
802
}
803