ThirstyAffiliates Affiliate Link Manager - Version 3.9.1

Version Description

  • Bug Fix: Incorrect client IP retrieved from HTTP_X_FORWARDED_FOR header
  • Bug Fix: External images not working in TA Image block
Download this release

Release Info

Developer caseproof
Plugin Icon 128x128 ThirstyAffiliates Affiliate Link Manager
Version 3.9.1
Comparing to
See all releases

Code changes from version 3.9 to 3.9.1

Files changed (48) hide show
  1. .htaccess +2 -2
  2. Abstracts/Abstract_Main_Plugin_Class.php +112 -112
  3. Abstracts/index.php +0 -0
  4. Helpers/Helper_Functions.php +670 -660
  5. Helpers/Plugin_Constants.php +164 -164
  6. Helpers/index.php +0 -0
  7. Interfaces/Activatable_Interface.php +0 -0
  8. Interfaces/Deactivatable_Interface.php +22 -22
  9. Interfaces/Initiable_Interface.php +0 -0
  10. Interfaces/Model_Interface.php +0 -0
  11. Interfaces/index.php +0 -0
  12. Models/Affiliate_Link.php +768 -768
  13. Models/Affiliate_Link_Attachment.php +14 -14
  14. Models/Affiliate_Links_CPT.php +1026 -1014
  15. Models/Bootstrap.php +520 -520
  16. Models/Guided_Tour.php +389 -389
  17. Models/Link_Fixer.php +289 -289
  18. Models/Link_Picker.php +710 -710
  19. Models/Marketing.php +0 -0
  20. Models/Migration.php +0 -0
  21. Models/REST_API.php +354 -354
  22. Models/Rewrites_Redirection.php +440 -440
  23. Models/Script_Loader.php +0 -0
  24. Models/Settings.php +2114 -2114
  25. Models/Shortcodes.php +303 -303
  26. Models/Stats_Reporting.php +1077 -1077
  27. Models/index.php +0 -0
  28. css/admin/index.php +0 -0
  29. css/admin/ta-affiliate-link-list.css +29 -29
  30. css/admin/ta-editor.css +46 -46
  31. css/admin/ta-guided-tour.css +11 -11
  32. css/admin/ta-reports.css +255 -255
  33. css/admin/ta-settings.css +113 -113
  34. css/admin/tinymce/editor.css +8 -8
  35. css/index.php +0 -0
  36. css/lib/jquery-tiptip/jquery-tiptip.css +109 -109
  37. css/lib/select2/select2.css +484 -484
  38. css/lib/select2/select2.min.css +1 -1
  39. images/admin-review-notice-logo.png +0 -0
  40. images/index.php +0 -0
  41. images/sidebar.jpg +0 -0
  42. images/spinner-2x.gif +0 -0
  43. images/spinner.gif +0 -0
  44. index.php +0 -0
  45. js/app/advance_link_picker/.eslintrc.json +29 -29
  46. js/app/advance_link_picker/dist/advance-link-picker.css +0 -0
  47. js/app/advance_link_picker/dist/advance-link-picker.js +0 -0
  48. js/app/advance_link_picker/package-lock.json +0 -7403
.htaccess CHANGED
@@ -1,2 +1,2 @@
1
- Options -Indexes
2
-
1
+ Options -Indexes
2
+
Abstracts/Abstract_Main_Plugin_Class.php CHANGED
@@ -1,112 +1,112 @@
1
- <?php
2
- namespace ThirstyAffiliates\Abstracts;
3
-
4
- use ThirstyAffiliates\Interfaces\Model_Interface;
5
-
6
- if ( !defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
7
-
8
- /**
9
- * Abstract class that the main plugin class needs to extend.
10
- *
11
- * @since 3.0.0
12
- */
13
- abstract class Abstract_Main_Plugin_Class {
14
-
15
- /*
16
- |--------------------------------------------------------------------------
17
- | Class Properties
18
- |--------------------------------------------------------------------------
19
- */
20
-
21
- /**
22
- * Property that houses an array of all the "regular models" of the plugin.
23
- *
24
- * @since 3.0.0
25
- * @access protected
26
- * @var array
27
- */
28
- protected $__all_models = array();
29
-
30
- /**
31
- * Property that houses an array of all "public regular models" of the plugin.
32
- * Public models can be accessed and utilized by external entities via the main plugin class.
33
- *
34
- * @since 3.0.0
35
- * @access public
36
- * @var array
37
- */
38
- public $models = array();
39
-
40
- /**
41
- * Property that houses an array of all "public helper classes" of the plugin.
42
- *
43
- * @since 3.0.0
44
- * @access public
45
- * @var array
46
- */
47
- public $helpers = array();
48
-
49
-
50
-
51
-
52
- /*
53
- |--------------------------------------------------------------------------
54
- | Class Methods
55
- |--------------------------------------------------------------------------
56
- */
57
-
58
- /**
59
- * Add a "regular model" to the main plugin class "all models" array.
60
- *
61
- * @since 3.0.0
62
- * @access public
63
- *
64
- * @param Model_Interface $model Regular model.
65
- */
66
- public function add_to_all_plugin_models( Model_Interface $model ) {
67
-
68
- $class_reflection = new \ReflectionClass( $model );
69
- $class_name = $class_reflection->getShortName();
70
-
71
- if ( !array_key_exists( $class_name , $this->__all_models ) )
72
- $this->__all_models[ $class_name ] = $model;
73
-
74
- }
75
-
76
- /**
77
- * Add a "regular model" to the main plugin class "public models" array.
78
- *
79
- * @since 3.0.0
80
- * @access public
81
- *
82
- * @param Model_Interface $model Regular model.
83
- */
84
- public function add_to_public_models( Model_Interface $model ) {
85
-
86
- $class_reflection = new \ReflectionClass( $model );
87
- $class_name = $class_reflection->getShortName();
88
-
89
- if ( !array_key_exists( $class_name , $this->models ) )
90
- $this->models[ $class_name ] = $model;
91
-
92
- }
93
-
94
- /**
95
- * Add a "helper class instance" to the main plugin class "public helpers" array.
96
- *
97
- * @since 3.0.0
98
- * @access public
99
- *
100
- * @param object $helper Helper class instance.
101
- */
102
- public function add_to_public_helpers( $helper ) {
103
-
104
- $class_reflection = new \ReflectionClass( $helper );
105
- $class_name = $class_reflection->getShortName();
106
-
107
- if ( !array_key_exists( $class_name , $this->helpers ) )
108
- $this->helpers[ $class_name ] = $helper;
109
-
110
- }
111
-
112
- }
1
+ <?php
2
+ namespace ThirstyAffiliates\Abstracts;
3
+
4
+ use ThirstyAffiliates\Interfaces\Model_Interface;
5
+
6
+ if ( !defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
7
+
8
+ /**
9
+ * Abstract class that the main plugin class needs to extend.
10
+ *
11
+ * @since 3.0.0
12
+ */
13
+ abstract class Abstract_Main_Plugin_Class {
14
+
15
+ /*
16
+ |--------------------------------------------------------------------------
17
+ | Class Properties
18
+ |--------------------------------------------------------------------------
19
+ */
20
+
21
+ /**
22
+ * Property that houses an array of all the "regular models" of the plugin.
23
+ *
24
+ * @since 3.0.0
25
+ * @access protected
26
+ * @var array
27
+ */
28
+ protected $__all_models = array();
29
+
30
+ /**
31
+ * Property that houses an array of all "public regular models" of the plugin.
32
+ * Public models can be accessed and utilized by external entities via the main plugin class.
33
+ *
34
+ * @since 3.0.0
35
+ * @access public
36
+ * @var array
37
+ */
38
+ public $models = array();
39
+
40
+ /**
41
+ * Property that houses an array of all "public helper classes" of the plugin.
42
+ *
43
+ * @since 3.0.0
44
+ * @access public
45
+ * @var array
46
+ */
47
+ public $helpers = array();
48
+
49
+
50
+
51
+
52
+ /*
53
+ |--------------------------------------------------------------------------
54
+ | Class Methods
55
+ |--------------------------------------------------------------------------
56
+ */
57
+
58
+ /**
59
+ * Add a "regular model" to the main plugin class "all models" array.
60
+ *
61
+ * @since 3.0.0
62
+ * @access public
63
+ *
64
+ * @param Model_Interface $model Regular model.
65
+ */
66
+ public function add_to_all_plugin_models( Model_Interface $model ) {
67
+
68
+ $class_reflection = new \ReflectionClass( $model );
69
+ $class_name = $class_reflection->getShortName();
70
+
71
+ if ( !array_key_exists( $class_name , $this->__all_models ) )
72
+ $this->__all_models[ $class_name ] = $model;
73
+
74
+ }
75
+
76
+ /**
77
+ * Add a "regular model" to the main plugin class "public models" array.
78
+ *
79
+ * @since 3.0.0
80
+ * @access public
81
+ *
82
+ * @param Model_Interface $model Regular model.
83
+ */
84
+ public function add_to_public_models( Model_Interface $model ) {
85
+
86
+ $class_reflection = new \ReflectionClass( $model );
87
+ $class_name = $class_reflection->getShortName();
88
+
89
+ if ( !array_key_exists( $class_name , $this->models ) )
90
+ $this->models[ $class_name ] = $model;
91
+
92
+ }
93
+
94
+ /**
95
+ * Add a "helper class instance" to the main plugin class "public helpers" array.
96
+ *
97
+ * @since 3.0.0
98
+ * @access public
99
+ *
100
+ * @param object $helper Helper class instance.
101
+ */
102
+ public function add_to_public_helpers( $helper ) {
103
+
104
+ $class_reflection = new \ReflectionClass( $helper );
105
+ $class_name = $class_reflection->getShortName();
106
+
107
+ if ( !array_key_exists( $class_name , $this->helpers ) )
108
+ $this->helpers[ $class_name ] = $helper;
109
+
110
+ }
111
+
112
+ }
Abstracts/index.php CHANGED
File without changes
Helpers/Helper_Functions.php CHANGED
@@ -1,660 +1,670 @@
1
- <?php
2
- namespace ThirstyAffiliates\Helpers;
3
-
4
- use ThirstyAffiliates\Abstracts\Abstract_Main_Plugin_Class;
5
-
6
- use ThirstyAffiliates\Models\Affiliate_Link;
7
-
8
- if ( !defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
9
-
10
- /**
11
- * Model that houses all the helper functions of the plugin.
12
- *
13
- * 3.0.0
14
- */
15
- class Helper_Functions {
16
-
17
- /*
18
- |--------------------------------------------------------------------------
19
- | Class Properties
20
- |--------------------------------------------------------------------------
21
- */
22
-
23
- /**
24
- * Property that holds the single main instance of Helper_Functions.
25
- *
26
- * @since 3.0.0
27
- * @access private
28
- * @var Helper_Functions
29
- */
30
- private static $_instance;
31
-
32
- /**
33
- * Model that houses all the plugin constants.
34
- *
35
- * @since 3.0.0
36
- * @access private
37
- * @var Plugin_Constants
38
- */
39
- private $_constants;
40
-
41
- /**
42
- * Property that houses all the saved settings.
43
- *
44
- * @since 3.0.0
45
- * @access private
46
- */
47
- private $_settings = array();
48
-
49
-
50
-
51
-
52
- /*
53
- |--------------------------------------------------------------------------
54
- | Class Methods
55
- |--------------------------------------------------------------------------
56
- */
57
-
58
- /**
59
- * Class constructor.
60
- *
61
- * @since 3.0.0
62
- * @access public
63
- *
64
- * @param Abstract_Main_Plugin_Class $main_plugin Main plugin object.
65
- * @param Plugin_Constants $constants Plugin constants object.
66
- */
67
- public function __construct( Abstract_Main_Plugin_Class $main_plugin , Plugin_Constants $constants ) {
68
-
69
- $this->_constants = $constants;
70
-
71
- $main_plugin->add_to_public_helpers( $this );
72
-
73
- }
74
-
75
- /**
76
- * Ensure that only one instance of this class is loaded or can be loaded ( Singleton Pattern ).
77
- *
78
- * @since 3.0.0
79
- * @access public
80
- *
81
- * @param Abstract_Main_Plugin_Class $main_plugin Main plugin object.
82
- * @param Plugin_Constants $constants Plugin constants object.
83
- * @return Helper_Functions
84
- */
85
- public static function get_instance( Abstract_Main_Plugin_Class $main_plugin , Plugin_Constants $constants ) {
86
-
87
- if ( !self::$_instance instanceof self )
88
- self::$_instance = new self( $main_plugin , $constants );
89
-
90
- return self::$_instance;
91
-
92
- }
93
-
94
-
95
-
96
-
97
- /*
98
- |--------------------------------------------------------------------------
99
- | Helper Functions
100
- |--------------------------------------------------------------------------
101
- */
102
-
103
- /**
104
- * Write data to plugin log file.
105
- *
106
- * @since 3.0.0
107
- * @access public
108
- *
109
- * @param mixed Data to log.
110
- */
111
- public function write_debug_log( $log ) {
112
-
113
- error_log( "\n[" . current_time( 'mysql' ) . "]\n" . $log . "\n--------------------------------------------------\n" , 3 , $this->_constants->LOGS_ROOT_PATH() . 'debug.log' );
114
-
115
- }
116
-
117
- /**
118
- * Check if current user is authorized to manage the plugin on the backend.
119
- *
120
- * @since 3.0.0
121
- * @access public
122
- *
123
- * @param WP_User $user WP_User object.
124
- * @return boolean True if authorized, False otherwise.
125
- */
126
- public function current_user_authorized( $user = null ) {
127
-
128
- // Array of roles allowed to access/utilize the plugin
129
- $admin_roles = apply_filters( 'ucfw_admin_roles' , array( 'administrator' ) );
130
-
131
- if ( is_null( $user ) )
132
- $user = wp_get_current_user();
133
-
134
- if ( $user->ID )
135
- return count( array_intersect( ( array ) $user->roles , $admin_roles ) ) ? true : false;
136
- else
137
- return false;
138
-
139
- }
140
-
141
- /**
142
- * Returns the timezone string for a site, even if it's set to a UTC offset
143
- *
144
- * Adapted from http://www.php.net/manual/en/function.timezone-name-from-abbr.php#89155
145
- *
146
- * Reference:
147
- * http://www.skyverge.com/blog/down-the-rabbit-hole-wordpress-and-timezones/
148
- *
149
- * @since 3.0.0
150
- * @access public
151
- *
152
- * @return string Valid PHP timezone string
153
- */
154
- public function get_site_current_timezone() {
155
-
156
- // if site timezone string exists, return it
157
- if ( $timezone = get_option( 'timezone_string' ) )
158
- return $timezone;
159
-
160
- // get UTC offset, if it isn't set then return UTC
161
- if ( 0 === ( $utc_offset = get_option( 'gmt_offset', 0 ) ) )
162
- return 'UTC';
163
-
164
- return $this->convert_utc_offset_to_timezone( $utc_offset );
165
-
166
- }
167
-
168
- /**
169
- * Conver UTC offset to timezone.
170
- *
171
- * @since 1.2.0
172
- * @access public
173
- *
174
- * @param float|int|string $utc_offset UTC offset.
175
- * @return string valid PHP timezone string
176
- */
177
- public function convert_utc_offset_to_timezone( $utc_offset ) {
178
-
179
- // adjust UTC offset from hours to seconds
180
- $utc_offset *= 3600;
181
-
182
- // attempt to guess the timezone string from the UTC offset
183
- if ( $timezone = timezone_name_from_abbr( '' , $utc_offset , 0 ) )
184
- return $timezone;
185
-
186
- // last try, guess timezone string manually
187
- $is_dst = date( 'I' );
188
-
189
- foreach ( timezone_abbreviations_list() as $abbr )
190
- foreach ( $abbr as $city )
191
- if ( $city[ 'dst' ] == $is_dst && $city[ 'offset' ] == $utc_offset && isset( $city[ 'timezone_id' ] ) )
192
- return $city[ 'timezone_id' ];
193
-
194
- // fallback to UTC
195
- return 'UTC';
196
-
197
- }
198
-
199
- /**
200
- * Get all user roles.
201
- *
202
- * @since 3.0.0
203
- * @access public
204
- *
205
- * @global WP_Roles $wp_roles Core class used to implement a user roles API.
206
- *
207
- * @return array Array of all site registered user roles. User role key as the key and value is user role text.
208
- */
209
- public function get_all_user_roles() {
210
-
211
- global $wp_roles;
212
- return $wp_roles->get_names();
213
-
214
- }
215
-
216
- /**
217
- * Check validity of a save post action.
218
- *
219
- * @since 3.0.0
220
- * @access private
221
- *
222
- * @param int $post_id Id of the coupon post.
223
- * @param string $post_type Post type to check.
224
- * @return bool True if valid save post action, False otherwise.
225
- */
226
- public function check_if_valid_save_post_action( $post_id , $post_type ) {
227
-
228
- if ( get_post_type() != $post_type || empty( $_POST ) || wp_is_post_autosave( $post_id ) || wp_is_post_revision( $post_id ) || !current_user_can( 'edit_page' , $post_id ) )
229
- return false;
230
- else
231
- return true;
232
-
233
- }
234
-
235
- /**
236
- * Get user IP address.
237
- *
238
- * @since 3.0.0
239
- * @since 3.3.2 Added condition to disable IP address collection (for GDRP compliance).
240
- * @access public
241
- *
242
- * @return string User's IP address.
243
- */
244
- public function get_user_ip_address() {
245
-
246
- if ( get_option( 'ta_disable_ip_address_collection' ) === 'yes' )
247
- return;
248
-
249
- if ( ! empty( $_SERVER['HTTP_CLIENT_IP'] ) )
250
- $ip = $_SERVER['HTTP_CLIENT_IP'];
251
- elseif ( ! empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) )
252
- $ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
253
- else
254
- $ip = $_SERVER['REMOTE_ADDR'];
255
-
256
- return apply_filters( 'ta_get_user_ip_address', $ip );
257
- }
258
-
259
- /**
260
- * Get the thirstylink slug set on the settings.
261
- *
262
- * @since 3.0.0
263
- * @access public
264
- *
265
- * @return string $link_prefix Thirstyling link prefix.
266
- */
267
- public function get_thirstylink_link_prefix() {
268
-
269
- $link_prefix = get_option( 'ta_link_prefix' , 'recommends' );
270
-
271
- if ( $link_prefix === 'custom' )
272
- $link_prefix = get_option( 'ta_link_prefix_custom' , 'recommends' );
273
-
274
- return $link_prefix ? $link_prefix : 'recommends';
275
- }
276
-
277
- /**
278
- * Get the affiliate link post default category slug.
279
- *
280
- * @since 3.0.0
281
- * @access public
282
- *
283
- * @param int $link_id Affiliate Link ID.
284
- * @param array $terms Affiliate link categories.
285
- * @return string Affiliate link default category slug.
286
- */
287
- public function get_default_category_slug( $link_id , $terms = array() ) {
288
-
289
- if ( ! is_array( $terms ) || empty( $terms ) )
290
- $terms = get_the_terms( $link_id , Plugin_Constants::AFFILIATE_LINKS_TAX );
291
-
292
- if ( is_wp_error( $terms ) || empty( $terms ) )
293
- return;
294
-
295
- $link_cat_obj = array_shift( $terms );
296
-
297
- return $link_cat_obj->slug;
298
- }
299
-
300
- /**
301
- * Search affiliate links query
302
- *
303
- * @since 3.0.0
304
- * @since 3.6 Add suport for Gutenberg.
305
- * @since 3.7 Add option to add list of affiliate link attached images for Gutenberg.
306
- * @access public
307
- *
308
- * @param string $keyword Search keyword.
309
- * @param int $paged WP_Query paged value.
310
- * @param string $category Affiliate link category to search.
311
- * @param array $exclude List of posts to be excluded.
312
- * @param array $is_gutenberg Toggle if searching for Gutenberg link picker.
313
- * @param array $with_images Toggle if search for affiliate links with images.
314
- * @return array List of affiliate link IDs.
315
- */
316
- public function search_affiliate_links_query( $keyword = '' , $paged = 1 , $category = '' , $exclude = array() , $is_gutenberg = false , $with_images = false ) {
317
-
318
- $args = array(
319
- 'post_type' => Plugin_Constants::AFFILIATE_LINKS_CPT,
320
- 'post_status' => 'publish',
321
- 's' => $keyword,
322
- 'fields' => 'ids',
323
- 'paged' => $paged,
324
- 'post__not_in' => $exclude
325
- );
326
-
327
- if ( $category ) {
328
-
329
- $args[ 'tax_query' ] = array(
330
- array(
331
- 'taxonomy' => Plugin_Constants::AFFILIATE_LINKS_TAX,
332
- 'field' => 'slug',
333
- 'terms' => $category
334
- )
335
- );
336
- }
337
-
338
- if ( $is_gutenberg ) {
339
- unset( $args[ 'fields' ] );
340
- unset( $args[ 'paged' ] );
341
-
342
- $args[ 'posts_per_page' ] = 20;
343
- }
344
-
345
- if ( $with_images ) {
346
-
347
- $args[ 'meta_query' ] = array(
348
- array(
349
- 'key' => Plugin_Constants::META_DATA_PREFIX . 'image_ids',
350
- 'compare' => 'EXISTS'
351
- )
352
- );
353
- }
354
-
355
- $query = new \WP_Query( $args );
356
-
357
- return $is_gutenberg ? array_map( function( $post ) use ( $with_images ) {
358
-
359
- $image_ids = get_post_meta( $post->ID , Plugin_Constants::META_DATA_PREFIX . 'image_ids' , true );
360
-
361
- return array(
362
- 'id' => $post->ID,
363
- 'link' => get_permalink( $post ),
364
- 'title' => $post->post_title,
365
- 'images' => $with_images ? $this->get_image_srcs( $image_ids ) : false
366
- );
367
- } , $query->posts ) : $query->posts;
368
- }
369
-
370
- /**
371
- * Get image and src attribute for list of image ids.
372
- *
373
- * @since 3.7
374
- * @access public
375
- *
376
- * @param array $image_ids List of image ids.
377
- * @param string $size Size of image to output.
378
- * @return array List of image ids with src attribute.
379
- */
380
- public function get_image_srcs( $image_ids , $size = 'thumbnail' ) {
381
-
382
- if ( ! is_array( $image_ids ) || empty( $image_ids ) ) return array();
383
-
384
- return array_map( function( $id ) use ( $size ) {
385
- return array(
386
- 'id' => $id,
387
- 'src' => wp_get_attachment_image_src( $id , $size )[0],
388
- );
389
- } , $image_ids );
390
- }
391
-
392
- /**
393
- * Check if affiliate link needs to be uncloaked.
394
- *
395
- * @deprecated 3.2.0
396
- *
397
- * @since 3.0.0
398
- * @access public
399
- *
400
- * @param Affiliate_Link $thirstylink Thirsty affiliate link object.
401
- * @return boolean Sets to true when affiliate link needs to be uncloaked.
402
- */
403
- public function is_uncloak_link( $thirstylink ) {
404
-
405
- return $thirstylink->is( 'uncloak_link' );
406
- }
407
-
408
- /**
409
- * Error log with a trace.
410
- *
411
- * @since 3.0.0
412
- * @access public
413
- */
414
- public function ta_error_log( $msg ) {
415
-
416
- $trace = debug_backtrace();
417
- $caller = array_shift( $trace );
418
-
419
- error_log( $msg . ' | Trace: ' . $caller[ 'file' ] . ' on line ' . $caller[ 'line' ] );
420
-
421
- }
422
-
423
- /**
424
- * Utility function that determines if a plugin is active or not.
425
- *
426
- * @since 3.0.0
427
- * @access public
428
- *
429
- * @param string $plugin_basename Plugin base name. Ex. woocommerce/woocommerce.php
430
- * @return boolean True if active, false otherwise.
431
- */
432
- public function is_plugin_active( $plugin_basename ) {
433
-
434
- // Makes sure the plugin is defined before trying to use it
435
- if ( !function_exists( 'is_plugin_active' ) )
436
- include_once( ABSPATH . 'wp-admin/includes/plugin.php' );
437
-
438
- return is_plugin_active( $plugin_basename );
439
-
440
- }
441
-
442
- /**
443
- * Send email.
444
- *
445
- * @since 3.0.0
446
- * @access public
447
- *
448
- * @param array $recipients Array of recipients emails.
449
- * @param string $subject Email subject.
450
- * @param string $message Email message.
451
- * @param array $headers Array of email headers.
452
- * @param array $attachments Array of email attachments.
453
- * @return boolean True if email sending is triggered, note it does not mean that the email was received, it just denotes that the email sending is triggered. False if email sending is not triggered.
454
- */
455
- public function send_email( $recipients , $subject , $message , $headers = array() , $attachments = array() ) {
456
-
457
- $from_name = apply_filters( 'ta_email_from_name' , get_bloginfo( 'name' ) );
458
- $from_email = apply_filters( 'ta_email_from_email' , get_option( 'admin_email' ) );
459
-
460
- $headers[] = 'From: ' . $from_name . ' <' . $from_email . '>';
461
- $headers[] = 'Content-Type: text/html; charset=' . get_option( 'blog_charset' );
462
-
463
- return wp_mail( $recipients , $subject , $message , $headers , $attachments );
464
-
465
- }
466
-
467
- /**
468
- * Get Affiliate_Link data object.
469
- *
470
- * @since 3.0.0
471
- * @access public
472
- *
473
- * @param int $id Affiliate_Link post ID.
474
- * @return Affiliate_Link Affiliate Link object.
475
- */
476
- public function get_affiliate_link( $id = 0 ) {
477
-
478
- return new Affiliate_Link( $id );
479
- }
480
-
481
- /**
482
- * Retrieve all categories as an option array list.
483
- *
484
- * @since 3.0.0
485
- * @since 3.4.0 Add support for slug options.
486
- * @access public
487
- *
488
- * @return array List of category options.
489
- */
490
- public function get_all_category_as_options( $use_slug = false ) {
491
-
492
- $options = array();
493
-
494
- $categories = get_terms( array(
495
- 'taxonomy' => Plugin_Constants::AFFILIATE_LINKS_TAX,
496
- 'hide_empty' => false,
497
- ) );
498
-
499
- if ( ! is_wp_error( $categories ) ) {
500
-
501
- foreach( $categories as $category ) {
502
- $key = $use_slug ? $category->slug : $category->term_id;
503
- $options[ $key ] = $category->name;
504
- }
505
-
506
- } else {
507
-
508
- // TODO: Handle error
509
-
510
- }
511
-
512
- return $options;
513
- }
514
-
515
- /**
516
- * Set default term when affiliate link is saved.
517
- *
518
- * @since 3.2.0
519
- * @access public
520
- *
521
- * @param int $post_id Affiliate link post ID.
522
- */
523
- public function save_default_affiliate_link_category( $post_id ) {
524
-
525
- $default_category = Plugin_Constants::DEFAULT_LINK_CATEGORY;
526
- $taxonomy_slug = Plugin_Constants::AFFILIATE_LINKS_TAX;
527
-
528
- if ( get_option( 'ta_disable_cat_auto_select' ) == 'yes' || get_the_terms( $post_id , $taxonomy_slug ) )
529
- return;
530
-
531
- // create the default term if it doesn't exist
532
- if ( ! term_exists( $default_category , $taxonomy_slug ) )
533
- wp_insert_term( $default_category , $taxonomy_slug );
534
-
535
- $default_term = get_term_by( 'name' , $default_category , $taxonomy_slug );
536
-
537
- wp_set_post_terms( $post_id , $default_term->term_id , $taxonomy_slug );
538
- }
539
-
540
- /**
541
- * This function is an alias for WP get_option(), but will return the default value if option value is empty or invalid.
542
- *
543
- * @since 3.2.0
544
- * @access public
545
- *
546
- * @param string $option_name Name of the option of value to fetch.
547
- * @param mixed $default_value Defaut option value.
548
- * @return mixed Option value.
549
- */
550
- public function get_option( $option_name , $default_value = '' ) {
551
-
552
- $option_value = get_option( $option_name , $default_value );
553
-
554
- return ( gettype( $option_value ) === gettype( $default_value ) && $option_value ) ? $option_value : $default_value;
555
- }
556
-
557
- /**
558
- * Get blocked bots from settings or default value.
559
- *
560
- * @since 3.3.2
561
- * @access public
562
- *
563
- * @return array List of blocked bots.
564
- */
565
- public function get_blocked_bots() {
566
-
567
- $bots_string = $this->get_option( 'ta_blocked_bots' , Plugin_Constants::DEFAULT_BLOCKED_BOTS );
568
- return str_replace( ',' , '|' , $bots_string );
569
- }
570
-
571
- /**
572
- * Check if useragent is bot.
573
- *
574
- * @since 3.3.3
575
- * @access public
576
- *
577
- * @return bool True if detected as bot, otherwise false.
578
- */
579
- public function is_user_agent_bot() {
580
-
581
- $user_agent = isset( $_SERVER[ 'HTTP_USER_AGENT' ] ) ? strtolower( $_SERVER[ 'HTTP_USER_AGENT' ] ) : '';
582
- $bots = apply_filters( 'ta_useragent_bots_phrase_list' , $this->get_blocked_bots() );
583
- $pattern = '/' . $bots . '/i';
584
-
585
- return preg_match( $pattern , $user_agent );
586
- }
587
-
588
- /**
589
- * Get screen ID.
590
- *
591
- * @since 3.3.3
592
- * @access public
593
- */
594
- public function get_screen_id( $object_id ) {
595
-
596
- $screen_id = null;
597
-
598
- if ( isset( $_GET[ 'post_type' ] ) && $_GET[ 'post_type' ] == Plugin_Constants::AFFILIATE_LINKS_CPT ) {
599
-
600
- if ( isset( $_GET[ 'taxonomy' ] ) )
601
- $screen_id = 'edit-' . $_GET[ 'taxonomy' ];
602
- elseif ( isset( $_GET[ 'page' ] ) )
603
- $screen_id = 'thirstylink_page_' . $_GET[ 'page' ];
604
- else
605
- $screen_id = 'edit-thirstylink';
606
-
607
- } elseif ( $object_id && get_post_type( $object_id ) === Plugin_Constants::AFFILIATE_LINKS_CPT )
608
- $screen_id = 'thirstylink';
609
-
610
- return apply_filters( 'ta_get_screen_id' , $screen_id );
611
- }
612
-
613
- /**
614
- * Get visistor's browser and device.
615
- *
616
- * @since 3.4.0
617
- * @access public
618
- *
619
- * @return string Browser and device.
620
- */
621
- public function get_visitor_browser_device() {
622
-
623
- if ( get_option( 'ta_disable_browser_device_collection' ) === 'yes' || ! ini_get( "browscap" ) )
624
- return;
625
-
626
- $browser = get_browser( null );
627
- return is_object( $browser ) ? $browser->browser . '|' . $browser->platform . '|' . $browser->device_type : '';
628
- }
629
-
630
- /**
631
- * Check if a page builder is active or not. Currently supports: Beaver Builder and Elementor.
632
- *
633
- * @since 3.4.0
634
- * @access public
635
- */
636
- public function is_page_builder_active() {
637
-
638
- $is_bb_active = class_exists( 'FLBuilderModel' ) && \FLBuilderModel::is_builder_active();
639
- $elementor = class_exists( 'Elementor\Editor' ) && isset( $_GET[ 'action' ] ) && $_GET[ 'action' ] == 'elementor';
640
-
641
- return $is_bb_active || $elementor;
642
- }
643
-
644
- /**
645
- * Get string in between two strins.
646
- * Source: http://www.justin-cook.com/2006/03/31/php-parse-a-string-between-two-strings/
647
- *
648
- * @since 3.6
649
- */
650
- public function get_string_between( $string , $start , $end ) {
651
-
652
- $string = ' ' . $string;
653
- $ini = strpos($string, $start);
654
- if ($ini == 0) return '';
655
- $ini += strlen($start);
656
- $len = strpos($string, $end, $ini) - $ini;
657
- return substr($string, $ini, $len);
658
- }
659
-
660
- }
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace ThirstyAffiliates\Helpers;
3
+
4
+ use ThirstyAffiliates\Abstracts\Abstract_Main_Plugin_Class;
5
+
6
+ use ThirstyAffiliates\Models\Affiliate_Link;
7
+
8
+ if ( !defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
9
+
10
+ /**
11
+ * Model that houses all the helper functions of the plugin.
12
+ *
13
+ * 3.0.0
14
+ */
15
+ class Helper_Functions {
16
+
17
+ /*
18
+ |--------------------------------------------------------------------------
19
+ | Class Properties
20
+ |--------------------------------------------------------------------------
21
+ */
22
+
23
+ /**
24
+ * Property that holds the single main instance of Helper_Functions.
25
+ *
26
+ * @since 3.0.0
27
+ * @access private
28
+ * @var Helper_Functions
29
+ */
30
+ private static $_instance;
31
+
32
+ /**
33
+ * Model that houses all the plugin constants.
34
+ *
35
+ * @since 3.0.0
36
+ * @access private
37
+ * @var Plugin_Constants
38
+ */
39
+ private $_constants;
40
+
41
+ /**
42
+ * Property that houses all the saved settings.
43
+ *
44
+ * @since 3.0.0
45
+ * @access private
46
+ */
47
+ private $_settings = array();
48
+
49
+
50
+
51
+
52
+ /*
53
+ |--------------------------------------------------------------------------
54
+ | Class Methods
55
+ |--------------------------------------------------------------------------
56
+ */
57
+
58
+ /**
59
+ * Class constructor.
60
+ *
61
+ * @since 3.0.0
62
+ * @access public
63
+ *
64
+ * @param Abstract_Main_Plugin_Class $main_plugin Main plugin object.
65
+ * @param Plugin_Constants $constants Plugin constants object.
66
+ */
67
+ public function __construct( Abstract_Main_Plugin_Class $main_plugin , Plugin_Constants $constants ) {
68
+
69
+ $this->_constants = $constants;
70
+
71
+ $main_plugin->add_to_public_helpers( $this );
72
+
73
+ }
74
+
75
+ /**
76
+ * Ensure that only one instance of this class is loaded or can be loaded ( Singleton Pattern ).
77
+ *
78
+ * @since 3.0.0
79
+ * @access public
80
+ *
81
+ * @param Abstract_Main_Plugin_Class $main_plugin Main plugin object.
82
+ * @param Plugin_Constants $constants Plugin constants object.
83
+ * @return Helper_Functions
84
+ */
85
+ public static function get_instance( Abstract_Main_Plugin_Class $main_plugin , Plugin_Constants $constants ) {
86
+
87
+ if ( !self::$_instance instanceof self )
88
+ self::$_instance = new self( $main_plugin , $constants );
89
+
90
+ return self::$_instance;
91
+
92
+ }
93
+
94
+
95
+
96
+
97
+ /*
98
+ |--------------------------------------------------------------------------
99
+ | Helper Functions
100
+ |--------------------------------------------------------------------------
101
+ */
102
+
103
+ /**
104
+ * Write data to plugin log file.
105
+ *
106
+ * @since 3.0.0
107
+ * @access public
108
+ *
109
+ * @param mixed Data to log.
110
+ */
111
+ public function write_debug_log( $log ) {
112
+
113
+ error_log( "\n[" . current_time( 'mysql' ) . "]\n" . $log . "\n--------------------------------------------------\n" , 3 , $this->_constants->LOGS_ROOT_PATH() . 'debug.log' );
114
+
115
+ }
116
+
117
+ /**
118
+ * Check if current user is authorized to manage the plugin on the backend.
119
+ *
120
+ * @since 3.0.0
121
+ * @access public
122
+ *
123
+ * @param WP_User $user WP_User object.
124
+ * @return boolean True if authorized, False otherwise.
125
+ */
126
+ public function current_user_authorized( $user = null ) {
127
+
128
+ // Array of roles allowed to access/utilize the plugin
129
+ $admin_roles = apply_filters( 'ucfw_admin_roles' , array( 'administrator' ) );
130
+
131
+ if ( is_null( $user ) )
132
+ $user = wp_get_current_user();
133
+
134
+ if ( $user->ID )
135
+ return count( array_intersect( ( array ) $user->roles , $admin_roles ) ) ? true : false;
136
+ else
137
+ return false;
138
+
139
+ }
140
+
141
+ /**
142
+ * Returns the timezone string for a site, even if it's set to a UTC offset
143
+ *
144
+ * Adapted from http://www.php.net/manual/en/function.timezone-name-from-abbr.php#89155
145
+ *
146
+ * Reference:
147
+ * http://www.skyverge.com/blog/down-the-rabbit-hole-wordpress-and-timezones/
148
+ *
149
+ * @since 3.0.0
150
+ * @access public
151
+ *
152
+ * @return string Valid PHP timezone string
153
+ */
154
+ public function get_site_current_timezone() {
155
+
156
+ // if site timezone string exists, return it
157
+ if ( $timezone = get_option( 'timezone_string' ) )
158
+ return $timezone;
159
+
160
+ // get UTC offset, if it isn't set then return UTC
161
+ if ( 0 === ( $utc_offset = get_option( 'gmt_offset', 0 ) ) )
162
+ return 'UTC';
163
+
164
+ return $this->convert_utc_offset_to_timezone( $utc_offset );
165
+
166
+ }
167
+
168
+ /**
169
+ * Conver UTC offset to timezone.
170
+ *
171
+ * @since 1.2.0
172
+ * @access public
173
+ *
174
+ * @param float|int|string $utc_offset UTC offset.
175
+ * @return string valid PHP timezone string
176
+ */
177
+ public function convert_utc_offset_to_timezone( $utc_offset ) {
178
+
179
+ // adjust UTC offset from hours to seconds
180
+ $utc_offset *= 3600;
181
+
182
+ // attempt to guess the timezone string from the UTC offset
183
+ if ( $timezone = timezone_name_from_abbr( '' , $utc_offset , 0 ) )
184
+ return $timezone;
185
+
186
+ // last try, guess timezone string manually
187
+ $is_dst = date( 'I' );
188
+
189
+ foreach ( timezone_abbreviations_list() as $abbr )
190
+ foreach ( $abbr as $city )
191
+ if ( $city[ 'dst' ] == $is_dst && $city[ 'offset' ] == $utc_offset && isset( $city[ 'timezone_id' ] ) )
192
+ return $city[ 'timezone_id' ];
193
+
194
+ // fallback to UTC
195
+ return 'UTC';
196
+
197
+ }
198
+
199
+ /**
200
+ * Get all user roles.
201
+ *
202
+ * @since 3.0.0
203
+ * @access public
204
+ *
205
+ * @global WP_Roles $wp_roles Core class used to implement a user roles API.
206
+ *
207
+ * @return array Array of all site registered user roles. User role key as the key and value is user role text.
208
+ */
209
+ public function get_all_user_roles() {
210
+
211
+ global $wp_roles;
212
+ return $wp_roles->get_names();
213
+
214
+ }
215
+
216
+ /**
217
+ * Check validity of a save post action.
218
+ *
219
+ * @since 3.0.0
220
+ * @access private
221
+ *
222
+ * @param int $post_id Id of the coupon post.
223
+ * @param string $post_type Post type to check.
224
+ * @return bool True if valid save post action, False otherwise.
225
+ */
226
+ public function check_if_valid_save_post_action( $post_id , $post_type ) {
227
+
228
+ if ( get_post_type() != $post_type || empty( $_POST ) || wp_is_post_autosave( $post_id ) || wp_is_post_revision( $post_id ) || !current_user_can( 'edit_page' , $post_id ) )
229
+ return false;
230
+ else
231
+ return true;
232
+
233
+ }
234
+
235
+ /**
236
+ * Get user IP address.
237
+ *
238
+ * @since 3.0.0
239
+ * @since 3.3.2 Added condition to disable IP address collection (for GDRP compliance).
240
+ * @access public
241
+ *
242
+ * @return string User's IP address.
243
+ */
244
+ public function get_user_ip_address() {
245
+
246
+ if ( get_option( 'ta_disable_ip_address_collection' ) === 'yes' ) {
247
+ return '';
248
+ }
249
+
250
+ if ( ! empty( $_SERVER['HTTP_CLIENT_IP'] ) ) {
251
+ $ip = $_SERVER['HTTP_CLIENT_IP'];
252
+ } elseif ( ! empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) {
253
+ $ip = explode( ',', $_SERVER['HTTP_X_FORWARDED_FOR'] );
254
+ $ip = trim( $ip[0] );
255
+ } else {
256
+ $ip = $_SERVER['REMOTE_ADDR'];
257
+ }
258
+
259
+ return apply_filters( 'ta_get_user_ip_address', $ip );
260
+ }
261
+
262
+ /**
263
+ * Get the thirstylink slug set on the settings.
264
+ *
265
+ * @since 3.0.0
266
+ * @access public
267
+ *
268
+ * @return string $link_prefix Thirstyling link prefix.
269
+ */
270
+ public function get_thirstylink_link_prefix() {
271
+
272
+ $link_prefix = get_option( 'ta_link_prefix' , 'recommends' );
273
+
274
+ if ( $link_prefix === 'custom' )
275
+ $link_prefix = get_option( 'ta_link_prefix_custom' , 'recommends' );
276
+
277
+ return $link_prefix ? $link_prefix : 'recommends';
278
+ }
279
+
280
+ /**
281
+ * Get the affiliate link post default category slug.
282
+ *
283
+ * @since 3.0.0
284
+ * @access public
285
+ *
286
+ * @param int $link_id Affiliate Link ID.
287
+ * @param array $terms Affiliate link categories.
288
+ * @return string Affiliate link default category slug.
289
+ */
290
+ public function get_default_category_slug( $link_id , $terms = array() ) {
291
+
292
+ if ( ! is_array( $terms ) || empty( $terms ) )
293
+ $terms = get_the_terms( $link_id , Plugin_Constants::AFFILIATE_LINKS_TAX );
294
+
295
+ if ( is_wp_error( $terms ) || empty( $terms ) )
296
+ return;
297
+
298
+ $link_cat_obj = array_shift( $terms );
299
+
300
+ return $link_cat_obj->slug;
301
+ }
302
+
303
+ /**
304
+ * Search affiliate links query
305
+ *
306
+ * @since 3.0.0
307
+ * @since 3.6 Add suport for Gutenberg.
308
+ * @since 3.7 Add option to add list of affiliate link attached images for Gutenberg.
309
+ * @access public
310
+ *
311
+ * @param string $keyword Search keyword.
312
+ * @param int $paged WP_Query paged value.
313
+ * @param string $category Affiliate link category to search.
314
+ * @param array $exclude List of posts to be excluded.
315
+ * @param array $is_gutenberg Toggle if searching for Gutenberg link picker.
316
+ * @param array $with_images Toggle if search for affiliate links with images.
317
+ * @return array List of affiliate link IDs.
318
+ */
319
+ public function search_affiliate_links_query( $keyword = '' , $paged = 1 , $category = '' , $exclude = array() , $is_gutenberg = false , $with_images = false ) {
320
+
321
+ $args = array(
322
+ 'post_type' => Plugin_Constants::AFFILIATE_LINKS_CPT,
323
+ 'post_status' => 'publish',
324
+ 's' => $keyword,
325
+ 'fields' => 'ids',
326
+ 'paged' => $paged,
327
+ 'post__not_in' => $exclude
328
+ );
329
+
330
+ if ( $category ) {
331
+
332
+ $args[ 'tax_query' ] = array(
333
+ array(
334
+ 'taxonomy' => Plugin_Constants::AFFILIATE_LINKS_TAX,
335
+ 'field' => 'slug',
336
+ 'terms' => $category
337
+ )
338
+ );
339
+ }
340
+
341
+ if ( $is_gutenberg ) {
342
+ unset( $args[ 'fields' ] );
343
+ unset( $args[ 'paged' ] );
344
+
345
+ $args[ 'posts_per_page' ] = 20;
346
+ }
347
+
348
+ if ( $with_images ) {
349
+
350
+ $args[ 'meta_query' ] = array(
351
+ array(
352
+ 'key' => Plugin_Constants::META_DATA_PREFIX . 'image_ids',
353
+ 'compare' => 'EXISTS'
354
+ )
355
+ );
356
+ }
357
+
358
+ $query = new \WP_Query( $args );
359
+
360
+ return $is_gutenberg ? array_map( function( $post ) use ( $with_images ) {
361
+
362
+ $image_ids = get_post_meta( $post->ID , Plugin_Constants::META_DATA_PREFIX . 'image_ids' , true );
363
+
364
+ return array(
365
+ 'id' => $post->ID,
366
+ 'link' => get_permalink( $post ),
367
+ 'title' => $post->post_title,
368
+ 'images' => $with_images ? $this->get_image_srcs( $image_ids ) : false
369
+ );
370
+ } , $query->posts ) : $query->posts;
371
+ }
372
+
373
+ /**
374
+ * Get image and src attribute for list of image ids.
375
+ *
376
+ * @since 3.7
377
+ * @access public
378
+ *
379
+ * @param array $image_ids List of image ids.
380
+ * @param string $size Size of image to output.
381
+ * @return array List of image ids with src attribute.
382
+ */
383
+ public function get_image_srcs( $image_ids , $size = 'thumbnail' ) {
384
+
385
+ if ( ! is_array( $image_ids ) || empty( $image_ids ) ) return array();
386
+
387
+ return array_map( function( $id ) use ( $size ) {
388
+ if ( ! is_numeric( $id ) ) {
389
+ return array(
390
+ 'id' => null,
391
+ 'src' => $id
392
+ );
393
+ }
394
+
395
+ return array(
396
+ 'id' => $id,
397
+ 'src' => wp_get_attachment_image_src( $id , $size )[0]
398
+ );
399
+ } , $image_ids );
400
+ }
401
+
402
+ /**
403
+ * Check if affiliate link needs to be uncloaked.
404
+ *
405
+ * @deprecated 3.2.0
406
+ *
407
+ * @since 3.0.0
408
+ * @access public
409
+ *
410
+ * @param Affiliate_Link $thirstylink Thirsty affiliate link object.
411
+ * @return boolean Sets to true when affiliate link needs to be uncloaked.
412
+ */
413
+ public function is_uncloak_link( $thirstylink ) {
414
+
415
+ return $thirstylink->is( 'uncloak_link' );
416
+ }
417
+
418
+ /**
419
+ * Error log with a trace.
420
+ *
421
+ * @since 3.0.0
422
+ * @access public
423
+ */
424
+ public function ta_error_log( $msg ) {
425
+
426
+ $trace = debug_backtrace();
427
+ $caller = array_shift( $trace );
428
+
429
+ error_log( $msg . ' | Trace: ' . $caller[ 'file' ] . ' on line ' . $caller[ 'line' ] );
430
+
431
+ }
432
+
433
+ /**
434
+ * Utility function that determines if a plugin is active or not.
435
+ *
436
+ * @since 3.0.0
437
+ * @access public
438
+ *
439
+ * @param string $plugin_basename Plugin base name. Ex. woocommerce/woocommerce.php
440
+ * @return boolean True if active, false otherwise.
441
+ */
442
+ public function is_plugin_active( $plugin_basename ) {
443
+
444
+ // Makes sure the plugin is defined before trying to use it
445
+ if ( !function_exists( 'is_plugin_active' ) )
446
+ include_once( ABSPATH . 'wp-admin/includes/plugin.php' );
447
+
448
+ return is_plugin_active( $plugin_basename );
449
+
450
+ }
451
+
452
+ /**
453
+ * Send email.
454
+ *
455
+ * @since 3.0.0
456
+ * @access public
457
+ *
458
+ * @param array $recipients Array of recipients emails.
459
+ * @param string $subject Email subject.
460
+ * @param string $message Email message.
461
+ * @param array $headers Array of email headers.
462
+ * @param array $attachments Array of email attachments.
463
+ * @return boolean True if email sending is triggered, note it does not mean that the email was received, it just denotes that the email sending is triggered. False if email sending is not triggered.
464
+ */
465
+ public function send_email( $recipients , $subject , $message , $headers = array() , $attachments = array() ) {
466
+
467
+ $from_name = apply_filters( 'ta_email_from_name' , get_bloginfo( 'name' ) );
468
+ $from_email = apply_filters( 'ta_email_from_email' , get_option( 'admin_email' ) );
469
+
470
+ $headers[] = 'From: ' . $from_name . ' <' . $from_email . '>';
471
+ $headers[] = 'Content-Type: text/html; charset=' . get_option( 'blog_charset' );
472
+
473
+ return wp_mail( $recipients , $subject , $message , $headers , $attachments );
474
+
475
+ }
476
+
477
+ /**
478
+ * Get Affiliate_Link data object.
479
+ *
480
+ * @since 3.0.0
481
+ * @access public
482
+ *
483
+ * @param int $id Affiliate_Link post ID.
484
+ * @return Affiliate_Link Affiliate Link object.
485
+ */
486
+ public function get_affiliate_link( $id = 0 ) {
487
+
488
+ return new Affiliate_Link( $id );
489
+ }
490
+
491
+ /**
492
+ * Retrieve all categories as an option array list.
493
+ *
494
+ * @since 3.0.0
495
+ * @since 3.4.0 Add support for slug options.
496
+ * @access public
497
+ *
498
+ * @return array List of category options.
499
+ */
500
+ public function get_all_category_as_options( $use_slug = false ) {
501
+
502
+ $options = array();
503
+
504
+ $categories = get_terms( array(
505
+ 'taxonomy' => Plugin_Constants::AFFILIATE_LINKS_TAX,
506
+ 'hide_empty' => false,
507
+ ) );
508
+
509
+ if ( ! is_wp_error( $categories ) ) {
510
+
511
+ foreach( $categories as $category ) {
512
+ $key = $use_slug ? $category->slug : $category->term_id;
513
+ $options[ $key ] = $category->name;
514
+ }
515
+
516
+ } else {
517
+
518
+ // TODO: Handle error
519
+
520
+ }
521
+
522
+ return $options;
523
+ }
524
+
525
+ /**
526
+ * Set default term when affiliate link is saved.
527
+ *
528
+ * @since 3.2.0
529
+ * @access public
530
+ *
531
+ * @param int $post_id Affiliate link post ID.
532
+ */
533
+ public function save_default_affiliate_link_category( $post_id ) {
534
+
535
+ $default_category = Plugin_Constants::DEFAULT_LINK_CATEGORY;
536
+ $taxonomy_slug = Plugin_Constants::AFFILIATE_LINKS_TAX;
537
+
538
+ if ( get_option( 'ta_disable_cat_auto_select' ) == 'yes' || get_the_terms( $post_id , $taxonomy_slug ) )
539
+ return;
540
+
541
+ // create the default term if it doesn't exist
542
+ if ( ! term_exists( $default_category , $taxonomy_slug ) )
543
+ wp_insert_term( $default_category , $taxonomy_slug );
544
+
545
+ $default_term = get_term_by( 'name' , $default_category , $taxonomy_slug );
546
+
547
+ wp_set_post_terms( $post_id , $default_term->term_id , $taxonomy_slug );
548
+ }
549
+
550
+ /**
551
+ * This function is an alias for WP get_option(), but will return the default value if option value is empty or invalid.
552
+ *
553
+ * @since 3.2.0
554
+ * @access public
555
+ *
556
+ * @param string $option_name Name of the option of value to fetch.
557
+ * @param mixed $default_value Defaut option value.
558
+ * @return mixed Option value.
559
+ */
560
+ public function get_option( $option_name , $default_value = '' ) {
561
+
562
+ $option_value = get_option( $option_name , $default_value );
563
+
564
+ return ( gettype( $option_value ) === gettype( $default_value ) && $option_value ) ? $option_value : $default_value;
565
+ }
566
+
567
+ /**
568
+ * Get blocked bots from settings or default value.
569
+ *
570
+ * @since 3.3.2
571
+ * @access public
572
+ *
573
+ * @return array List of blocked bots.
574
+ */
575
+ public function get_blocked_bots() {
576
+
577
+ $bots_string = $this->get_option( 'ta_blocked_bots' , Plugin_Constants::DEFAULT_BLOCKED_BOTS );
578
+ return str_replace( ',' , '|' , $bots_string );
579
+ }
580
+
581
+ /**
582
+ * Check if useragent is bot.
583
+ *
584
+ * @since 3.3.3
585
+ * @access public
586
+ *
587
+ * @return bool True if detected as bot, otherwise false.
588
+ */
589
+ public function is_user_agent_bot() {
590
+
591
+ $user_agent = isset( $_SERVER[ 'HTTP_USER_AGENT' ] ) ? strtolower( $_SERVER[ 'HTTP_USER_AGENT' ] ) : '';
592
+ $bots = apply_filters( 'ta_useragent_bots_phrase_list' , $this->get_blocked_bots() );
593
+ $pattern = '/' . $bots . '/i';
594
+
595
+ return preg_match( $pattern , $user_agent );
596
+ }
597
+
598
+ /**
599
+ * Get screen ID.
600
+ *
601
+ * @since 3.3.3
602
+ * @access public
603
+ */
604
+ public function get_screen_id( $object_id ) {
605
+
606
+ $screen_id = null;
607
+
608
+ if ( isset( $_GET[ 'post_type' ] ) && $_GET[ 'post_type' ] == Plugin_Constants::AFFILIATE_LINKS_CPT ) {
609
+
610
+ if ( isset( $_GET[ 'taxonomy' ] ) )
611
+ $screen_id = 'edit-' . $_GET[ 'taxonomy' ];
612
+ elseif ( isset( $_GET[ 'page' ] ) )
613
+ $screen_id = 'thirstylink_page_' . $_GET[ 'page' ];
614
+ else
615
+ $screen_id = 'edit-thirstylink';
616
+
617
+ } elseif ( $object_id && get_post_type( $object_id ) === Plugin_Constants::AFFILIATE_LINKS_CPT )
618
+ $screen_id = 'thirstylink';
619
+
620
+ return apply_filters( 'ta_get_screen_id' , $screen_id );
621
+ }
622
+
623
+ /**
624
+ * Get visistor's browser and device.
625
+ *
626
+ * @since 3.4.0
627
+ * @access public
628
+ *
629
+ * @return string Browser and device.
630
+ */
631
+ public function get_visitor_browser_device() {
632
+
633
+ if ( get_option( 'ta_disable_browser_device_collection' ) === 'yes' || ! ini_get( "browscap" ) )
634
+ return;
635
+
636
+ $browser = get_browser( null );
637
+ return is_object( $browser ) ? $browser->browser . '|' . $browser->platform . '|' . $browser->device_type : '';
638
+ }
639
+
640
+ /**
641
+ * Check if a page builder is active or not. Currently supports: Beaver Builder and Elementor.
642
+ *
643
+ * @since 3.4.0
644
+ * @access public
645
+ */
646
+ public function is_page_builder_active() {
647
+
648
+ $is_bb_active = class_exists( 'FLBuilderModel' ) && \FLBuilderModel::is_builder_active();
649
+ $elementor = class_exists( 'Elementor\Editor' ) && isset( $_GET[ 'action' ] ) && $_GET[ 'action' ] == 'elementor';
650
+
651
+ return $is_bb_active || $elementor;
652
+ }
653
+
654
+ /**
655
+ * Get string in between two strins.
656
+ * Source: http://www.justin-cook.com/2006/03/31/php-parse-a-string-between-two-strings/
657
+ *
658
+ * @since 3.6
659
+ */
660
+ public function get_string_between( $string , $start , $end ) {
661
+
662
+ $string = ' ' . $string;
663
+ $ini = strpos($string, $start);
664
+ if ($ini == 0) return '';
665
+ $ini += strlen($start);
666
+ $len = strpos($string, $end, $ini) - $ini;
667
+ return substr($string, $ini, $len);
668
+ }
669
+
670
+ }
Helpers/Plugin_Constants.php CHANGED
@@ -1,164 +1,164 @@
1
- <?php
2
- namespace ThirstyAffiliates\Helpers;
3
-
4
- use ThirstyAffiliates\Abstracts\Abstract_Main_Plugin_Class;
5
-
6
- if ( !defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
7
-
8
- /**
9
- * Model that houses all the plugin constants.
10
- * Note as much as possible, we need to make this class succinct as the only purpose of this is to house all the constants that is utilized by the plugin.
11
- * Therefore we omit class member comments and minimize comments as much as possible.
12
- * In fact the only verbouse comment here is this comment you are reading right now.
13
- * And guess what, it just got worse coz now this comment takes 5 lines instead of 3.
14
- *
15
- * @since 3.0.0
16
- */
17
- class Plugin_Constants {
18
-
19
- /*
20
- |--------------------------------------------------------------------------
21
- | Class Properties
22
- |--------------------------------------------------------------------------
23
- */
24
-
25
- private static $_instance;
26
-
27
- // Plugin configuration constants
28
- const TOKEN = 'ta';
29
- const INSTALLED_VERSION = 'ta_installed_version';
30
- const VERSION = '3.9';
31
- const TEXT_DOMAIN = 'thirstyaffiliates';
32
- const THEME_TEMPLATE_PATH = 'thirstyaffiliates';
33
- const META_DATA_PREFIX = '_ta_';
34
-
35
- // CPT Taxonomy constants
36
- const AFFILIATE_LINKS_CPT = 'thirstylink';
37
- const AFFILIATE_LINKS_TAX = 'thirstylink-category';
38
- const DEFAULT_LINK_CATEGORY = 'Uncategorized';
39
-
40
- // CRON
41
- const CRON_REQUEST_REVIEW = 'ta_cron_request_review';
42
- const CRON_MIGRATE_OLD_PLUGIN_DATA = 'ta_cron_migrate_old_plugin_data';
43
- const CRON_TAPRO_NOTICE = 'ta_cron_tapro_notice';
44
- const CRON_STATS_TRIMMER = 'ta_cron_stats_trimmer';
45
-
46
- // Options
47
- const SHOW_REQUEST_REVIEW = 'ta_show_request_review';
48
- const REVIEW_REQUEST_RESPONSE = 'ta_request_review_response';
49
- const MIGRATION_COMPLETE_FLAG = 'ta_migration_complete_flag';
50
- const SHOW_TAPRO_NOTICE = 'ta_show_tapro_notice';
51
-
52
- // Settings Constants
53
- const DEFAULT_BLOCKED_BOTS = 'googlebot,bingbot,Slurp,DuckDuckBot,Baiduspider,YandexBot,Sogou,Exabot,facebo,ia_archiver';
54
-
55
- // DB Tables
56
- const LINK_CLICK_DB = 'ta_link_clicks';
57
- const LINK_CLICK_META_DB = 'ta_link_clicks_meta';
58
-
59
- // Help Section
60
- const CLEAN_UP_PLUGIN_OPTIONS = 'ta_clean_up_plugin_options';
61
-
62
-
63
-
64
-
65
- /*
66
- |--------------------------------------------------------------------------
67
- | Class Methods
68
- |--------------------------------------------------------------------------
69
- */
70
-
71
- public function __construct( Abstract_Main_Plugin_Class $main_plugin ) {
72
-
73
- // Path constants
74
- $this->_MAIN_PLUGIN_FILE_PATH = WP_PLUGIN_DIR . DIRECTORY_SEPARATOR . 'thirstyaffiliates' . DIRECTORY_SEPARATOR . 'thirstyaffiliates.php';
75
- $this->_PLUGIN_DIR_PATH = plugin_dir_path( $this->_MAIN_PLUGIN_FILE_PATH );
76
- $this->_PLUGIN_DIR_URL = plugin_dir_url( $this->_MAIN_PLUGIN_FILE_PATH );
77
- $this->_PLUGIN_DIRNAME = plugin_basename( dirname( $this->_MAIN_PLUGIN_FILE_PATH ) );
78
- $this->_PLUGIN_BASENAME = plugin_basename( $this->_MAIN_PLUGIN_FILE_PATH );
79
-
80
- $this->_CSS_ROOT_URL = $this->_PLUGIN_DIR_URL . 'css/';
81
- $this->_IMAGES_ROOT_URL = $this->_PLUGIN_DIR_URL . 'images/';
82
- $this->_JS_ROOT_URL = $this->_PLUGIN_DIR_URL . 'js/';
83
-
84
- $this->_VIEWS_ROOT_PATH = $this->_PLUGIN_DIR_PATH . 'views/';
85
- $this->_TEMPLATES_ROOT_PATH = $this->_PLUGIN_DIR_PATH . 'templates/';
86
- $this->_LOGS_ROOT_PATH = $this->_PLUGIN_DIR_PATH . 'logs/';
87
-
88
- $this->_REDIRECT_TYPES = apply_filters( 'ta_redirect_types' , array(
89
- '301' => __( '301 Permanent' , 'thirstyaffiliates' ),
90
- '302' => __( '302 Temporary' , 'thirstyaffiliates' ),
91
- '307' => __( '307 Temporary (alternative)' , 'thirstyaffiliates' )
92
- ) );
93
-
94
- $main_plugin->add_to_public_helpers( $this );
95
-
96
- }
97
-
98
- public static function get_instance( Abstract_Main_Plugin_Class $main_plugin ) {
99
-
100
- if ( !self::$_instance instanceof self )
101
- self::$_instance = new self( $main_plugin );
102
-
103
- return self::$_instance;
104
-
105
- }
106
-
107
- public function VERSION() {
108
- return self::VERSION;
109
- }
110
-
111
- public function MAIN_PLUGIN_FILE_PATH() {
112
- return $this->_MAIN_PLUGIN_FILE_PATH;
113
- }
114
-
115
- public function PLUGIN_DIR_PATH() {
116
- return $this->_PLUGIN_DIR_PATH;
117
- }
118
-
119
- public function PLUGIN_DIR_URL() {
120
- return $this->_PLUGIN_DIR_URL;
121
- }
122
-
123
- public function PLUGIN_DIRNAME() {
124
- return $this->_PLUGIN_DIRNAME;
125
- }
126
-
127
- public function PLUGIN_BASENAME() {
128
- return $this->_PLUGIN_BASENAME;
129
- }
130
-
131
- public function CSS_ROOT_URL() {
132
- return $this->_CSS_ROOT_URL;
133
- }
134
-
135
- public function IMAGES_ROOT_URL() {
136
- return $this->_IMAGES_ROOT_URL;
137
- }
138
-
139
- public function JS_ROOT_URL() {
140
- return $this->_JS_ROOT_URL;
141
- }
142
-
143
- public function VIEWS_ROOT_PATH() {
144
- return $this->_VIEWS_ROOT_PATH;
145
- }
146
-
147
- public function TEMPLATES_ROOT_PATH() {
148
- return $this->_TEMPLATES_ROOT_PATH;
149
- }
150
-
151
- public function LOGS_ROOT_PATH() {
152
- return $this->_LOGS_ROOT_PATH;
153
- }
154
-
155
- public function REDIRECT_TYPES() {
156
- return $this->_REDIRECT_TYPES;
157
- }
158
-
159
- // HTAccess Module
160
- public function HTACCESS_FILE() {
161
- return ABSPATH . '/.htaccess';
162
- }
163
-
164
- }
1
+ <?php
2
+ namespace ThirstyAffiliates\Helpers;
3
+
4
+ use ThirstyAffiliates\Abstracts\Abstract_Main_Plugin_Class;
5
+
6
+ if ( !defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
7
+
8
+ /**
9
+ * Model that houses all the plugin constants.
10
+ * Note as much as possible, we need to make this class succinct as the only purpose of this is to house all the constants that is utilized by the plugin.
11
+ * Therefore we omit class member comments and minimize comments as much as possible.
12
+ * In fact the only verbouse comment here is this comment you are reading right now.
13
+ * And guess what, it just got worse coz now this comment takes 5 lines instead of 3.
14
+ *
15
+ * @since 3.0.0
16
+ */
17
+ class Plugin_Constants {
18
+
19
+ /*
20
+ |--------------------------------------------------------------------------
21
+ | Class Properties
22
+ |--------------------------------------------------------------------------
23
+ */
24
+
25
+ private static $_instance;
26
+
27
+ // Plugin configuration constants
28
+ const TOKEN = 'ta';
29
+ const INSTALLED_VERSION = 'ta_installed_version';
30
+ const VERSION = '3.9.1';
31
+ const TEXT_DOMAIN = 'thirstyaffiliates';
32
+ const THEME_TEMPLATE_PATH = 'thirstyaffiliates';
33
+ const META_DATA_PREFIX = '_ta_';
34
+
35
+ // CPT Taxonomy constants
36
+ const AFFILIATE_LINKS_CPT = 'thirstylink';
37
+ const AFFILIATE_LINKS_TAX = 'thirstylink-category';
38
+ const DEFAULT_LINK_CATEGORY = 'Uncategorized';
39
+
40
+ // CRON
41
+ const CRON_REQUEST_REVIEW = 'ta_cron_request_review';
42
+ const CRON_MIGRATE_OLD_PLUGIN_DATA = 'ta_cron_migrate_old_plugin_data';
43
+ const CRON_TAPRO_NOTICE = 'ta_cron_tapro_notice';
44
+ const CRON_STATS_TRIMMER = 'ta_cron_stats_trimmer';
45
+
46
+ // Options
47
+ const SHOW_REQUEST_REVIEW = 'ta_show_request_review';
48
+ const REVIEW_REQUEST_RESPONSE = 'ta_request_review_response';
49
+ const MIGRATION_COMPLETE_FLAG = 'ta_migration_complete_flag';
50
+ const SHOW_TAPRO_NOTICE = 'ta_show_tapro_notice';
51
+
52
+ // Settings Constants
53
+ const DEFAULT_BLOCKED_BOTS = 'googlebot,bingbot,Slurp,DuckDuckBot,Baiduspider,YandexBot,Sogou,Exabot,facebo,ia_archiver';
54
+
55
+ // DB Tables
56
+ const LINK_CLICK_DB = 'ta_link_clicks';
57
+ const LINK_CLICK_META_DB = 'ta_link_clicks_meta';
58
+
59
+ // Help Section
60
+ const CLEAN_UP_PLUGIN_OPTIONS = 'ta_clean_up_plugin_options';
61
+
62
+
63
+
64
+
65
+ /*
66
+ |--------------------------------------------------------------------------
67
+ | Class Methods
68
+ |--------------------------------------------------------------------------
69
+ */
70
+
71
+ public function __construct( Abstract_Main_Plugin_Class $main_plugin ) {
72
+
73
+ // Path constants
74
+ $this->_MAIN_PLUGIN_FILE_PATH = WP_PLUGIN_DIR . DIRECTORY_SEPARATOR . 'thirstyaffiliates' . DIRECTORY_SEPARATOR . 'thirstyaffiliates.php';
75
+ $this->_PLUGIN_DIR_PATH = plugin_dir_path( $this->_MAIN_PLUGIN_FILE_PATH );
76
+ $this->_PLUGIN_DIR_URL = plugin_dir_url( $this->_MAIN_PLUGIN_FILE_PATH );
77
+ $this->_PLUGIN_DIRNAME = plugin_basename( dirname( $this->_MAIN_PLUGIN_FILE_PATH ) );
78
+ $this->_PLUGIN_BASENAME = plugin_basename( $this->_MAIN_PLUGIN_FILE_PATH );
79
+
80
+ $this->_CSS_ROOT_URL = $this->_PLUGIN_DIR_URL . 'css/';
81
+ $this->_IMAGES_ROOT_URL = $this->_PLUGIN_DIR_URL . 'images/';
82
+ $this->_JS_ROOT_URL = $this->_PLUGIN_DIR_URL . 'js/';
83
+
84
+ $this->_VIEWS_ROOT_PATH = $this->_PLUGIN_DIR_PATH . 'views/';
85
+ $this->_TEMPLATES_ROOT_PATH = $this->_PLUGIN_DIR_PATH . 'templates/';
86
+ $this->_LOGS_ROOT_PATH = $this->_PLUGIN_DIR_PATH . 'logs/';
87
+
88
+ $this->_REDIRECT_TYPES = apply_filters( 'ta_redirect_types' , array(
89
+ '301' => __( '301 Permanent' , 'thirstyaffiliates' ),
90
+ '302' => __( '302 Temporary' , 'thirstyaffiliates' ),
91
+ '307' => __( '307 Temporary (alternative)' , 'thirstyaffiliates' )
92
+ ) );
93
+
94
+ $main_plugin->add_to_public_helpers( $this );
95
+
96
+ }
97
+
98
+ public static function get_instance( Abstract_Main_Plugin_Class $main_plugin ) {
99
+
100
+ if ( !self::$_instance instanceof self )
101
+ self::$_instance = new self( $main_plugin );
102
+
103
+ return self::$_instance;
104
+
105
+ }
106
+
107
+ public function VERSION() {
108
+ return self::VERSION;
109
+ }
110
+
111
+ public function MAIN_PLUGIN_FILE_PATH() {
112
+ return $this->_MAIN_PLUGIN_FILE_PATH;
113
+ }
114
+
115
+ public function PLUGIN_DIR_PATH() {
116
+ return $this->_PLUGIN_DIR_PATH;
117
+ }
118
+
119
+ public function PLUGIN_DIR_URL() {
120
+ return $this->_PLUGIN_DIR_URL;
121
+ }
122
+
123
+ public function PLUGIN_DIRNAME() {
124
+ return $this->_PLUGIN_DIRNAME;
125
+ }
126
+
127
+ public function PLUGIN_BASENAME() {
128
+ return $this->_PLUGIN_BASENAME;
129
+ }
130
+
131
+ public function CSS_ROOT_URL() {
132
+ return $this->_CSS_ROOT_URL;
133
+ }
134
+
135
+ public function IMAGES_ROOT_URL() {
136
+ return $this->_IMAGES_ROOT_URL;
137
+ }
138
+
139
+ public function JS_ROOT_URL() {
140
+ return $this->_JS_ROOT_URL;
141
+ }
142
+
143
+ public function VIEWS_ROOT_PATH() {
144
+ return $this->_VIEWS_ROOT_PATH;
145
+ }
146
+
147
+ public function TEMPLATES_ROOT_PATH() {
148
+ return $this->_TEMPLATES_ROOT_PATH;
149
+ }
150
+
151
+ public function LOGS_ROOT_PATH() {
152
+ return $this->_LOGS_ROOT_PATH;
153
+ }
154
+
155
+ public function REDIRECT_TYPES() {
156
+ return $this->_REDIRECT_TYPES;
157
+ }
158
+
159
+ // HTAccess Module
160
+ public function HTACCESS_FILE() {
161
+ return ABSPATH . '/.htaccess';
162
+ }
163
+
164
+ }
Helpers/index.php CHANGED
File without changes
Interfaces/Activatable_Interface.php CHANGED
File without changes
Interfaces/Deactivatable_Interface.php CHANGED
@@ -1,22 +1,22 @@
1
- <?php
2
- namespace ThirstyAffiliates\Interfaces;
3
-
4
- if ( !defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
5
-
6
- /**
7
- * Abstraction that provides contract relating to deactivation.
8
- * Any model that needs some sort of deactivation must implement this interface.
9
- *
10
- * @since 3.1.0
11
- */
12
- interface Deactivatable_Interface {
13
-
14
- /**
15
- * Contract for deactivation.
16
- *
17
- * @since 3.1.0
18
- * @access public
19
- */
20
- public function deactivate();
21
-
22
- }
1
+ <?php
2
+ namespace ThirstyAffiliates\Interfaces;
3
+
4
+ if ( !defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
5
+
6
+ /**
7
+ * Abstraction that provides contract relating to deactivation.
8
+ * Any model that needs some sort of deactivation must implement this interface.
9
+ *
10
+ * @since 3.1.0
11
+ */
12
+ interface Deactivatable_Interface {
13
+
14
+ /**
15
+ * Contract for deactivation.
16
+ *
17
+ * @since 3.1.0
18
+ * @access public
19
+ */
20
+ public function deactivate();
21
+
22
+ }
Interfaces/Initiable_Interface.php CHANGED
File without changes
Interfaces/Model_Interface.php CHANGED
File without changes
Interfaces/index.php CHANGED
File without changes
Models/Affiliate_Link.php CHANGED
@@ -1,768 +1,768 @@
1
- <?php
2
-
3
- namespace ThirstyAffiliates\Models;
4
-
5
- use ThirstyAffiliates\Abstracts\Abstract_Main_Plugin_Class;
6
-
7
- use ThirstyAffiliates\Interfaces\Model_Interface;
8
-
9
- use ThirstyAffiliates\Helpers\Plugin_Constants;
10
- use ThirstyAffiliates\Helpers\Helper_Functions;
11
-
12
- /**
13
- * Model that houses the data model of an affiliate link.
14
- *
15
- * @since 3.0.0
16
- */
17
- class Affiliate_Link {
18
-
19
-
20
- /*
21
- |--------------------------------------------------------------------------
22
- | Class Properties
23
- |--------------------------------------------------------------------------
24
- */
25
-
26
- /**
27
- * Model that houses the main plugin object.
28
- *
29
- * @since 3.0.0
30
- * @access private
31
- * @var Abstract_Main_Plugin_Class
32
- */
33
- private $_main_plugin;
34
-
35
- /**
36
- * Model that houses all the plugin constants.
37
- *
38
- * @since 3.0.0
39
- * @access private
40
- * @var Plugin_Constants
41
- */
42
- private $_constants;
43
-
44
- /**
45
- * Property that houses all the helper functions of the plugin.
46
- *
47
- * @since 3.0.0
48
- * @access private
49
- * @var Helper_Functions
50
- */
51
- private $_helper_functions;
52
-
53
- /**
54
- * Stores affiliate link ID.
55
- *
56
- * @since 3.0.0
57
- * @access protected
58
- * @var array
59
- */
60
- protected $id;
61
-
62
- /**
63
- * Stores affiliate link data.
64
- *
65
- * @since 3.0.0
66
- * @access protected
67
- * @var array
68
- */
69
- protected $data = array();
70
-
71
- /**
72
- * Stores affiliate link default data.
73
- *
74
- * @since 3.0.0
75
- * @access protected
76
- * @var array
77
- */
78
- protected $default_data = array(
79
- 'name' => '',
80
- 'slug' => '',
81
- 'date_created' => '',
82
- 'date_modified' => '',
83
- 'status' => '',
84
- 'permalink' => '',
85
- 'destination_url' => '',
86
- 'rel_tags' => '',
87
- 'css_classes' => '',
88
- 'redirect_type' => 'global',
89
- 'no_follow' => 'global',
90
- 'new_window' => 'global',
91
- 'uncloak_link' => 'global',
92
- 'pass_query_str' => 'global',
93
- 'image_ids' => array(),
94
- 'categories' => array(),
95
- 'category_slug' => '',
96
- 'category_slug_id' => 0,
97
- 'inserted_to' => array(),
98
- 'scanned_inserted' => ''
99
- );
100
-
101
- /**
102
- * Stores affiliate link default data.
103
- *
104
- * @since 3.0.0
105
- * @access protected
106
- * @var array
107
- */
108
- protected $extend_data = array();
109
-
110
- /**
111
- * Stores affiliate link post data.
112
- *
113
- * @since 3.0.0
114
- * @access private
115
- * @var object
116
- */
117
- protected $post_data;
118
-
119
- /**
120
- * This is where changes to the $data will be saved.
121
- *
122
- * @since 3.0.0
123
- * @access protected
124
- * @var object
125
- */
126
- protected $changes = array();
127
-
128
- /**
129
- * Stores boolean if the data has been read from the database or not.
130
- *
131
- * @since 3.0.0
132
- * @access protected
133
- * @var object
134
- */
135
- protected $object_is_read = false;
136
-
137
- /**
138
- * List of deprecated properties.
139
- *
140
- * @since 3.3.1
141
- * @access protected
142
- * @var array
143
- */
144
- protected $deprecated_props = array(
145
- 'link_health_issue',
146
- 'link_health_issue_ignored'
147
- );
148
-
149
-
150
-
151
-
152
- /*
153
- |--------------------------------------------------------------------------
154
- | Class Methods
155
- |--------------------------------------------------------------------------
156
- */
157
-
158
- /**
159
- * Class constructor.
160
- *
161
- * @since 3.0.0
162
- * @access public
163
- *
164
- * @param Abstract_Main_Plugin_Class $main_plugin Main plugin object.
165
- * @param Plugin_Constants $constants Plugin constants object.
166
- * @param Helper_Functions $helper_functions Helper functions object.
167
- */
168
- public function __construct( $id = null ) {
169
-
170
- $this->_constants = ThirstyAffiliates()->helpers[ 'Plugin_Constants' ];
171
- $this->_helper_functions = ThirstyAffiliates()->helpers[ 'Helper_Functions' ];
172
- $this->deprecated_props = apply_filters( 'ta_affiliate_link_deprecated_props' , $this->deprecated_props );
173
-
174
- if ( filter_var( $id , FILTER_VALIDATE_INT ) && $id ) {
175
-
176
- $this->extend_data = apply_filters( 'ta_affiliate_link_extended_data' , $this->extend_data , $this->default_data );
177
- $this->data = $this->get_merged_default_extended_data();
178
- $this->id = absint( $id );
179
-
180
- $this->read();
181
-
182
- }
183
-
184
- }
185
-
186
- /**
187
- * Read data from DB and save on instance.
188
- *
189
- * @since 3.0.0
190
- * @since 3.2.2 Optimize method by lessening the metadata fetch into one single function call.
191
- * @access public
192
- */
193
- private function read() {
194
-
195
- $this->post_data = get_post( $this->id );
196
-
197
- if ( ! is_a( $this->post_data , 'WP_Post' ) || $this->object_is_read )
198
- return;
199
-
200
- $this->id = $this->post_data->ID; // set the affiliate link ID
201
- $meta_data = get_metadata( 'post' , $this->id );
202
- $default_data = $this->get_merged_default_extended_data();
203
-
204
- foreach ( $default_data as $prop => $value ) {
205
-
206
- // fetch raw meta data if present.
207
- $raw_data = isset( $meta_data[ Plugin_Constants::META_DATA_PREFIX . $prop ] ) ? maybe_unserialize( $meta_data[ Plugin_Constants::META_DATA_PREFIX . $prop ][0] ) : '';
208
-
209
- switch ( $prop ) {
210
-
211
- case 'name' :
212
- case 'slug' :
213
- case 'status' :
214
- case 'date_created' :
215
- case 'permalink' :
216
- $this->data[ $prop ] = $this->get_post_data_equivalent( $prop );
217
- break;
218
-
219
- case 'no_follow' :
220
- case 'new_window' :
221
- case 'pass_query_str' :
222
- case 'uncloak_link' :
223
- case 'redirect_type' :
224
- $this->data[ $prop ] = ! empty( $raw_data ) ? $raw_data : $default_data[ $prop ];
225
- break;
226
-
227
- case 'rel_tags' :
228
- case 'css_classes' :
229
- $this->data[ $prop ] = ! empty( $raw_data ) ? $raw_data : $this->get_prop_global_option_value( $prop );
230
- break;
231
-
232
- case 'image_ids' :
233
- case 'inserted_to' :
234
- $this->data[ $prop ] = ( is_array( $raw_data ) && ! empty( $raw_data ) ) ? $raw_data : $default_data[ $prop ];
235
- break;
236
-
237
- case 'categories' :
238
- $categories = wp_get_post_terms( $this->id , Plugin_Constants::AFFILIATE_LINKS_TAX );
239
- $this->data[ $prop ] = ! empty( $categories ) ? $categories : $default_data[ $prop ];
240
- break;
241
-
242
- default :
243
- $this->data[ $prop ] = apply_filters( 'ta_read_thirstylink_property' , $raw_data , $prop , $default_data , $this->id , $meta_data );
244
- break;
245
-
246
- }
247
-
248
- }
249
-
250
- $this->object_is_read = true;
251
-
252
- }
253
-
254
-
255
-
256
-
257
- /*
258
- |--------------------------------------------------------------------------
259
- | Data getters
260
- |--------------------------------------------------------------------------
261
- */
262
-
263
- /**
264
- * Get merged $default_data and $extended_data class properties.
265
- *
266
- * @since 3.0.0
267
- * @access public
268
- *
269
- * @return array Data properties.
270
- */
271
- private function get_merged_default_extended_data() {
272
-
273
- return array_merge( $this->default_data , $this->extend_data );
274
-
275
- }
276
-
277
- /**
278
- * Return's the post data equivalent of a certain affiliate link data property.
279
- *
280
- * @since 3.0.0
281
- * @access private
282
- *
283
- * @param string $prop Affiliate link property name.
284
- * @return string WP Post property equivalent.
285
- */
286
- private function get_post_data_equivalent( $prop ) {
287
-
288
- $equivalents = apply_filters( 'ta_affiliate_link_post_data_equivalent' , array(
289
- 'name' => $this->post_data->post_title,
290
- 'slug' => $this->post_data->post_name,
291
- 'permalink' => get_permalink( $this->post_data->ID ),
292
- 'status' => $this->post_data->post_status,
293
- 'date_created' => $this->post_data->post_date,
294
- 'date_modified' => $this->post_data->post_modified,
295
- ) , $this->post_data );
296
-
297
- if ( array_key_exists( $prop , $equivalents ) )
298
- return $equivalents[ $prop ];
299
- else
300
- return;
301
-
302
- }
303
-
304
- /**
305
- * Return data property.
306
- *
307
- * @since 3.0.0
308
- * @since 3.3.1 Make sure deprecated props are ignored.
309
- * @access public
310
- *
311
- * @param string $prop Data property slug.
312
- * @param mixed $default Set property default value (optional).
313
- * @return mixed Property data.
314
- */
315
- public function get_prop( $prop , $default = '' ) {
316
-
317
- $default_data = $this->get_merged_default_extended_data();
318
-
319
- if ( in_array( $prop , $this->deprecated_props ) )
320
- return $default;
321
-
322
- if ( array_key_exists( $prop , $this->data ) && $this->data[ $prop ] )
323
- $return_value = $this->data[ $prop ];
324
- else
325
- $return_value = ( $default ) ? $default : $default_data[ $prop ];
326
-
327
- return $return_value;
328
-
329
- }
330
-
331
- /**
332
- * Get redirect type
333
- *
334
- * @since 3.4.0
335
- * @access public
336
- *
337
- * @param string $default Default redirect type.
338
- * @return string Redirect type.
339
- */
340
- public function get_redirect_type( $default = '' ) {
341
-
342
- $redirect_type = $this->data[ 'redirect_type' ];
343
-
344
- if ( $redirect_type === 'global' )
345
- $redirect_type = get_option( 'ta_link_redirect_type' , $default );
346
- elseif ( ! $redirect_type )
347
- $redirect_type = $default ? $default : $default_data[ 'redirect_type' ];
348
-
349
- return $redirect_type;
350
- }
351
-
352
- /**
353
- * Return Affiliate_Link ID.
354
- *
355
- * @since 3.0.0
356
- * @access public
357
- *
358
- * @return int Affiliate_Link ID.
359
- */
360
- public function get_id() {
361
-
362
- return absint( $this->id );
363
-
364
- }
365
-
366
- /**
367
- * Return changed data property.
368
- *
369
- * @since 3.0.0
370
- * @access public
371
- *
372
- * @param string $prop Data property slug.
373
- * @param mixed $default Set property default value (optional).
374
- * @return mixed Property data.
375
- */
376
- public function get_changed_prop( $prop , $default = '' ) {
377
-
378
- return isset( $this->changes[ $prop ] ) ? $this->changes[ $prop ] : $this->get_prop( $prop , $default );
379
-
380
- }
381
-
382
- /**
383
- * Return affiliate link's WP_Post data.
384
- *
385
- * @since 3.0.0
386
- * @access public
387
- *
388
- * @return object Post data object.
389
- */
390
- public function get_post_data() {
391
-
392
- return $this->post_data;
393
-
394
- }
395
-
396
- /**
397
- * Get the properties global option value.
398
- *
399
- * @since 3.0.0
400
- * @since 3.2.2 Remove 'uncloak' option.
401
- * @since 3.4.0 Remove 'redirect_type' option. Add additional CSS classes option.
402
- * @access public
403
- *
404
- * @param string $prop Name of property.
405
- * @return string Global option value.
406
- */
407
- public function get_prop_global_option_value( $prop , $default = '' ) {
408
-
409
- switch( $prop ) {
410
-
411
- case 'rel_tags' :
412
- $option = 'ta_additional_rel_tags';
413
- break;
414
-
415
- case 'css_classes' :
416
- $option = 'ta_additional_css_classes';
417
- break;
418
- }
419
-
420
- return $option ? get_option( $option , $default ) : $default;
421
- }
422
-
423
- /**
424
- * Get the global value for the uncloak property.
425
- *
426
- * @deprecated 3.2.0
427
- *
428
- * @since 3.0.0
429
- * @access public
430
- *
431
- * @return string Global option value.
432
- */
433
- public function get_global_uncloak_value() {
434
-
435
- return $this->get_toggle_prop_global_value( 'uncloak_link' );
436
- }
437
-
438
- /**
439
- * Get toggle property global value. This function also checks for "category" valued options.
440
- *
441
- * @since 3.2.0
442
- * @access public
443
- *
444
- * @param string $toggle_prop Affiliate linkg toggle property name.
445
- * @return string 'yes' if true, otherwise 'no'.
446
- */
447
- public function get_toggle_prop_global_value( $toggle_prop ) {
448
-
449
- $global_props = apply_filters( 'ta_props_with_global_option' , array(
450
- 'no_follow' => 'ta_no_follow',
451
- 'new_window' => 'ta_new_window',
452
- 'uncloak_link' => '',
453
- 'pass_query_str' => 'ta_pass_query_str'
454
- ) );
455
- $global_props_cat = apply_filters( 'ta_prop_selected_categories_option' , array(
456
- 'no_follow' => 'ta_no_follow_category',
457
- 'new_window' => 'ta_new_window_category',
458
- 'uncloak_link' => 'ta_category_to_uncloak',
459
- 'pass_query_str' => 'ta_pass_query_str_category'
460
- ) );
461
-
462
- if ( ! array_key_exists( $toggle_prop , $global_props ) )
463
- return 'no';
464
-
465
- // get equivalent global value.
466
- $prop_value = $toggle_prop == 'uncloak_link' ? 'category' : get_option( $global_props[ $toggle_prop ] );
467
-
468
- if ( ! $prop_value )
469
- return 'no';
470
-
471
- // if prop value is not set to 'category', then return option value.
472
- if ( $prop_value != 'category' || ! array_key_exists( $toggle_prop , $global_props_cat ) )
473
- return $prop_value;
474
-
475
- $prop_cats = maybe_unserialize( get_option( $global_props_cat[ $toggle_prop ] , array() ) );
476
- $link_cats = $this->get_prop( 'categories' );
477
-
478
- // skip when there are no categories to check (false)
479
- if ( ! is_array( $prop_cats ) || empty( $prop_cats ) || empty( $link_cats ) )
480
- return 'no';
481
-
482
- foreach ( $link_cats as $category ) {
483
-
484
- if ( in_array( $category->term_id , $prop_cats ) )
485
- return 'yes';
486
- }
487
-
488
- return 'no';
489
- }
490
-
491
- /**
492
- * Gets the category slug used for affiliate link (if present).
493
- *
494
- * @since 3.2.2
495
- * @access public
496
- *
497
- * @return string Affiliate link ategory slug.
498
- */
499
- public function get_category_slug() {
500
-
501
- $cat_slug = $this->get_prop( 'category_slug' );
502
- return $cat_slug ? $cat_slug : $this->_helper_functions->get_default_category_slug( $this->get_id() , $this->get_prop( 'categories' ) );
503
- }
504
-
505
-
506
-
507
-
508
- /*
509
- |--------------------------------------------------------------------------
510
- | Data setters
511
- |--------------------------------------------------------------------------
512
- */
513
-
514
- /**
515
- * Set new value to properties and save it to $changes property.
516
- * This stores changes in a special array so we can track what needs to be saved on the DB later.
517
- *
518
- * @since 3.0.0
519
- * @access public
520
- *
521
- * @param string $prop Data property slug.
522
- * @param string $value New property value.
523
- */
524
- public function set_prop( $prop , $value ) {
525
-
526
- $default_data = $this->get_merged_default_extended_data();
527
-
528
- if ( array_key_exists( $prop , $this->data ) ) {
529
-
530
- // permalink property must not be changed
531
- if ( $prop == 'permalink' )
532
- return;
533
-
534
- if ( gettype( $value ) == gettype( $default_data[ $prop ] ) )
535
- $this->changes[ $prop ] = $value;
536
- else {
537
-
538
- // TODO: handle error here.
539
-
540
- }
541
-
542
- } else {
543
-
544
- $this->data[ $prop ] = $value;
545
- $this->changes[ $prop ] = $value;
546
-
547
- }
548
-
549
- }
550
-
551
-
552
-
553
-
554
- /*
555
- |--------------------------------------------------------------------------
556
- | Save (Create / Update) data to DB
557
- |--------------------------------------------------------------------------
558
- */
559
-
560
- /**
561
- * Save data in $changes to the database.
562
- *
563
- * @since 3.0.0
564
- * @access public
565
- *
566
- * @return WP_Error | int On success will return the post ID, otherwise it will return a WP_Error object.
567
- */
568
- public function save() {
569
-
570
- if ( ! empty( $this->changes ) ) {
571
-
572
- $post_metas = array();
573
- $post_data = array(
574
- 'post_title' => $this->get_changed_prop( 'name' ),
575
- 'post_name' => $this->get_changed_prop( 'slug' ),
576
- 'post_status' => $this->get_changed_prop( 'status' , 'publish' ),
577
- 'post_date' => $this->get_changed_prop( 'date_created' , current_time( 'mysql' ) ),
578
- 'post_modified' => $this->get_changed_prop( 'date_modified' , current_time( 'mysql' ) )
579
- );
580
-
581
- foreach ( $this->changes as $prop => $value ) {
582
-
583
- // make sure that property is registered in default data
584
- if ( ! array_key_exists( $prop , $this->get_merged_default_extended_data() ) )
585
- continue;
586
-
587
- if ( in_array( $prop , array( 'permalink' , 'name' , 'slug' , 'status' , 'date_created' , 'date_modified' ) ) )
588
- continue;
589
-
590
- $post_metas[ $prop ] = $value;
591
- }
592
-
593
- // create or update post
594
- if ( $this->id )
595
- $post_id = $this->update( $post_data );
596
- else
597
- $post_id = $this->create( $post_data );
598
-
599
- if ( ! is_wp_error( $post_id ) )
600
- $this->update_metas( $post_id , $post_metas );
601
- else
602
- return $post_id; // Return WP_Error object on error
603
-
604
- do_action( 'ta_save_affiliate_link' , $this->changes , $this );
605
-
606
- // update instance with new changes.
607
- $this->object_is_read = false;
608
- $this->read();
609
-
610
- } else
611
- return new \WP_Error( 'ta_affiliate_link_no_changes' , __( 'Unable to save affiliate link as there are no changes registered on the object yet.' , 'thirstyaffiliates' ) , array( 'changes' => $this->changes , 'affiliate_link' => $this ) );
612
-
613
- return $post_id;
614
- }
615
-
616
- /**
617
- * Create the affiliate link post.
618
- *
619
- * @since 3.0.0
620
- * @access private
621
- *
622
- * @param array $post_data Affiliate link post data.
623
- * @param WP_Error|int WP_Error on error, ID of newly created post otherwise.
624
- */
625
- private function create( $post_data ) {
626
-
627
- $post_data = array_merge( array( 'post_type' => Plugin_Constants::AFFILIATE_LINKS_CPT ) , $post_data );
628
- $this->id = wp_insert_post( $post_data );
629
-
630
- return $this->id;
631
-
632
- }
633
-
634
- /**
635
- * Update the affiliate link post.
636
- *
637
- * @since 3.0.0
638
- * @access private
639
- *
640
- * @param array $post_data Affiliate link post data.
641
- * @return int ID of the updated post upon success. 0 on failure.
642
- */
643
- private function update( $post_data ) {
644
-
645
- $post_data = array_merge( array( 'ID' => $this->id ) , $post_data );
646
- return wp_update_post( $post_data , true );
647
-
648
- }
649
-
650
- /**
651
- * Update/add the affiliate link meta data.
652
- *
653
- * @since 3.0.0
654
- * @access private
655
- *
656
- * @param int $post_id Affiliate link post ID.
657
- * @param array $post_metas Affiliate link meta data.
658
- */
659
- private function update_metas( $post_id , $post_metas ) {
660
-
661
- foreach ( $post_metas as $key => $value )
662
- update_post_meta( $post_id , Plugin_Constants::META_DATA_PREFIX . $key , $value );
663
-
664
- }
665
-
666
-
667
-
668
-
669
- /*
670
- |--------------------------------------------------------------------------
671
- | Utility Functions.
672
- |--------------------------------------------------------------------------
673
- */
674
-
675
- /**
676
- * Conditional function that checks if a property is true for the affiliate link. This function also checks global and category set values.
677
- *
678
- * @since 3.2.0
679
- * @access public
680
- *
681
- * @param string $toggle_prop Affiliate link toggle property to check.
682
- * @return boolean true | false.
683
- */
684
- public function is( $toggle_prop ) {
685
-
686
- // check if global setting for uncloak link is enabled.
687
- if ( $toggle_prop == 'uncloak_link' && get_option( 'ta_uncloak_link_per_link' ) !== 'yes' )
688
- return;
689
-
690
- $prop_value = $this->get_prop( $toggle_prop );
691
-
692
- // if prop value is not set to 'global', then return with the boolean equivalent of its value.
693
- if ( $prop_value != 'global' )
694
- return $prop_value == 'yes' ? true : false;
695
-
696
- // get property global value. Also checks for category selected options.
697
- $prop_value = $this->get_toggle_prop_global_value( $toggle_prop );
698
-
699
- return $prop_value == 'yes' ? true : false;
700
- }
701
-
702
- /**
703
- * Count affiliate link clicks.
704
- *
705
- * @since 3.0.0
706
- * @since 3.1.0 Add $date_offset parameter to count links only to a certain point.
707
- * @access public
708
- *
709
- * @param string $date_offset Date before limit to check
710
- * @return int Total number of clicks.
711
- */
712
- public function count_clicks( $date_offset = '' ) {
713
-
714
- global $wpdb;
715
-
716
- $table_name = $wpdb->prefix . Plugin_Constants::LINK_CLICK_DB;
717
- $link_id = $this->get_id();
718
- $query = "SELECT count(*) from $table_name WHERE link_id = $link_id";
719
- $query .= ( $date_offset && \DateTime::createFromFormat('Y-m-d H:i:s', $date_offset ) !== false ) ? " AND date_clicked > '$date_offset'" : '';
720
- $clicks = $wpdb->get_var( $query );
721
-
722
- return (int) $clicks;
723
- }
724
-
725
- /**
726
- * Scan where links are inserted.
727
- *
728
- * @since 3.2.0
729
- * @since 3.3.3 Improve the query to specify the results by searching using the permalink value, and alternating between the used link prefixes.
730
- * @access public
731
- *
732
- * @return array List of WP_Post IDs where affiliate link is inserted in content.
733
- */
734
- public function scan_where_links_inserted() {
735
-
736
- global $wpdb;
737
-
738
- // prepare the query.
739
- $post_ids = array();
740
- $link_id = $this->get_id();
741
- $cpt_slug = Plugin_Constants::AFFILIATE_LINKS_CPT;
742
- $types = get_post_types( array( 'public' => true ) , 'names' , 'and' );
743
- $types_str = implode( "','" , $types );
744
- $permalink = $this->get_prop( 'permalink' );
745
- $link_prefix = $this->_helper_functions->get_thirstylink_link_prefix();
746
- $link_prefixes = $this->_helper_functions->get_option( 'ta_used_link_prefixes' , array() );
747
- $like_query = array();
748
-
749
- foreach ( $link_prefixes as $prefix )
750
- $like_query[] = str_replace( $link_prefix , $prefix , "post_content LIKE '%$permalink\"%'" );
751
-
752
- $like_query_str = implode( ' OR ' , $like_query );
753
- $query = "SELECT ID FROM $wpdb->posts WHERE ( $like_query_str OR post_content LIKE '%[thirstylink%ids=\"$link_id%' ) AND post_type IN ( '$types_str' ) AND post_status = 'publish'";
754
-
755
- // fetch WP_Post IDs where link is inserted to.
756
- $raw_ids = $wpdb->get_col( $query );
757
-
758
- // save last scanned
759
- update_post_meta( $this->get_id() , Plugin_Constants::META_DATA_PREFIX . 'scanned_inserted' , current_time( 'mysql' , true ) );
760
-
761
- // save to custom meta.
762
- $post_ids = array_map( 'intval' , $raw_ids );
763
- update_post_meta( $this->get_id() , Plugin_Constants::META_DATA_PREFIX . 'inserted_to' , $post_ids );
764
-
765
- return $post_ids;
766
- }
767
-
768
- }
1
+ <?php
2
+
3
+ namespace ThirstyAffiliates\Models;
4
+
5
+ use ThirstyAffiliates\Abstracts\Abstract_Main_Plugin_Class;
6
+
7
+ use ThirstyAffiliates\Interfaces\Model_Interface;
8
+
9
+ use ThirstyAffiliates\Helpers\Plugin_Constants;
10
+ use ThirstyAffiliates\Helpers\Helper_Functions;
11
+
12
+ /**
13
+ * Model that houses the data model of an affiliate link.
14
+ *
15
+ * @since 3.0.0
16
+ */
17
+ class Affiliate_Link {
18
+
19
+
20
+ /*
21
+ |--------------------------------------------------------------------------
22
+ | Class Properties
23
+ |--------------------------------------------------------------------------
24
+ */
25
+
26
+ /**
27
+ * Model that houses the main plugin object.
28
+ *
29
+ * @since 3.0.0
30
+ * @access private
31
+ * @var Abstract_Main_Plugin_Class
32
+ */
33
+ private $_main_plugin;
34
+
35
+ /**
36
+ * Model that houses all the plugin constants.
37
+ *
38
+ * @since 3.0.0
39
+ * @access private
40
+ * @var Plugin_Constants
41
+ */
42
+ private $_constants;
43
+
44
+ /**
45
+ * Property that houses all the helper functions of the plugin.
46
+ *
47
+ * @since 3.0.0
48
+ * @access private
49
+ * @var Helper_Functions
50
+ */
51
+ private $_helper_functions;
52
+
53
+ /**
54
+ * Stores affiliate link ID.
55
+ *
56
+ * @since 3.0.0
57
+ * @access protected
58
+ * @var array
59
+ */
60
+ protected $id;
61
+
62
+ /**
63
+ * Stores affiliate link data.
64
+ *
65
+ * @since 3.0.0
66
+ * @access protected
67
+ * @var array
68
+ */
69
+ protected $data = array();
70
+
71
+ /**
72
+ * Stores affiliate link default data.
73
+ *
74
+ * @since 3.0.0
75
+ * @access protected
76
+ * @var array
77
+ */
78
+ protected $default_data = array(
79
+ 'name' => '',
80
+ 'slug' => '',
81
+ 'date_created' => '',
82
+ 'date_modified' => '',
83
+ 'status' => '',
84
+ 'permalink' => '',
85
+ 'destination_url' => '',
86
+ 'rel_tags' => '',
87
+ 'css_classes' => '',
88
+ 'redirect_type' => 'global',
89
+ 'no_follow' => 'global',
90
+ 'new_window' => 'global',
91
+ 'uncloak_link' => 'global',
92
+ 'pass_query_str' => 'global',
93
+ 'image_ids' => array(),
94
+ 'categories' => array(),
95
+ 'category_slug' => '',
96
+ 'category_slug_id' => 0,
97
+ 'inserted_to' => array(),
98
+ 'scanned_inserted' => ''
99
+ );
100
+
101
+ /**
102
+ * Stores affiliate link default data.
103
+ *
104
+ * @since 3.0.0
105
+ * @access protected
106
+ * @var array
107
+ */
108
+ protected $extend_data = array();
109
+
110
+ /**
111
+ * Stores affiliate link post data.
112
+ *
113
+ * @since 3.0.0
114
+ * @access private
115
+ * @var object
116
+ */
117
+ protected $post_data;
118
+
119
+ /**
120
+ * This is where changes to the $data will be saved.
121
+ *
122
+ * @since 3.0.0
123
+ * @access protected
124
+ * @var object
125
+ */
126
+ protected $changes = array();
127
+
128
+ /**
129
+ * Stores boolean if the data has been read from the database or not.
130
+ *
131
+ * @since 3.0.0
132
+ * @access protected
133
+ * @var object
134
+ */
135
+ protected $object_is_read = false;
136
+
137
+ /**
138
+ * List of deprecated properties.
139
+ *
140
+ * @since 3.3.1
141
+ * @access protected
142
+ * @var array
143
+ */
144
+ protected $deprecated_props = array(
145
+ 'link_health_issue',
146
+ 'link_health_issue_ignored'
147
+ );
148
+
149
+
150
+
151
+
152
+ /*
153
+ |--------------------------------------------------------------------------
154
+ | Class Methods
155
+ |--------------------------------------------------------------------------
156
+ */
157
+
158
+ /**
159
+ * Class constructor.
160
+ *
161
+ * @since 3.0.0
162
+ * @access public
163
+ *
164
+ * @param Abstract_Main_Plugin_Class $main_plugin Main plugin object.
165
+ * @param Plugin_Constants $constants Plugin constants object.
166
+ * @param Helper_Functions $helper_functions Helper functions object.
167
+ */
168
+ public function __construct( $id = null ) {
169
+
170
+ $this->_constants = ThirstyAffiliates()->helpers[ 'Plugin_Constants' ];
171
+ $this->_helper_functions = ThirstyAffiliates()->helpers[ 'Helper_Functions' ];
172
+ $this->deprecated_props = apply_filters( 'ta_affiliate_link_deprecated_props' , $this->deprecated_props );
173
+
174
+ if ( filter_var( $id , FILTER_VALIDATE_INT ) && $id ) {
175
+
176
+ $this->extend_data = apply_filters( 'ta_affiliate_link_extended_data' , $this->extend_data , $this->default_data );
177
+ $this->data = $this->get_merged_default_extended_data();
178
+ $this->id = absint( $id );
179
+
180
+ $this->read();
181
+
182
+ }
183
+
184
+ }
185
+
186
+ /**
187
+ * Read data from DB and save on instance.
188
+ *
189
+ * @since 3.0.0
190
+ * @since 3.2.2 Optimize method by lessening the metadata fetch into one single function call.
191
+ * @access public
192
+ */
193
+ private function read() {
194
+
195
+ $this->post_data = get_post( $this->id );
196
+
197
+ if ( ! is_a( $this->post_data , 'WP_Post' ) || $this->object_is_read )
198
+ return;
199
+
200
+ $this->id = $this->post_data->ID; // set the affiliate link ID
201
+ $meta_data = get_metadata( 'post' , $this->id );
202
+ $default_data = $this->get_merged_default_extended_data();
203
+
204
+ foreach ( $default_data as $prop => $value ) {
205
+
206
+ // fetch raw meta data if present.
207
+ $raw_data = isset( $meta_data[ Plugin_Constants::META_DATA_PREFIX . $prop ] ) ? maybe_unserialize( $meta_data[ Plugin_Constants::META_DATA_PREFIX . $prop ][0] ) : '';
208
+
209
+ switch ( $prop ) {
210
+
211
+ case 'name' :
212
+ case 'slug' :
213
+ case 'status' :
214
+ case 'date_created' :
215
+ case 'permalink' :
216
+ $this->data[ $prop ] = $this->get_post_data_equivalent( $prop );
217
+ break;
218
+
219
+ case 'no_follow' :
220
+ case 'new_window' :
221
+ case 'pass_query_str' :
222
+ case 'uncloak_link' :
223
+ case 'redirect_type' :
224
+ $this->data[ $prop ] = ! empty( $raw_data ) ? $raw_data : $default_data[ $prop ];
225
+ break;
226
+
227
+ case 'rel_tags' :
228
+ case 'css_classes' :
229
+ $this->data[ $prop ] = ! empty( $raw_data ) ? $raw_data : $this->get_prop_global_option_value( $prop );
230
+ break;
231
+
232
+ case 'image_ids' :
233
+ case 'inserted_to' :
234
+ $this->data[ $prop ] = ( is_array( $raw_data ) && ! empty( $raw_data ) ) ? $raw_data : $default_data[ $prop ];
235
+ break;
236
+
237
+ case 'categories' :
238
+ $categories = wp_get_post_terms( $this->id , Plugin_Constants::AFFILIATE_LINKS_TAX );
239
+ $this->data[ $prop ] = ! empty( $categories ) ? $categories : $default_data[ $prop ];
240
+ break;
241
+
242
+ default :
243
+ $this->data[ $prop ] = apply_filters( 'ta_read_thirstylink_property' , $raw_data , $prop , $default_data , $this->id , $meta_data );
244
+ break;
245
+
246
+ }
247
+
248
+ }
249
+
250
+ $this->object_is_read = true;
251
+
252
+ }
253
+
254
+
255
+
256
+
257
+ /*
258
+ |--------------------------------------------------------------------------
259
+ | Data getters
260
+ |--------------------------------------------------------------------------
261
+ */
262
+
263
+ /**
264
+ * Get merged $default_data and $extended_data class properties.
265
+ *
266
+ * @since 3.0.0
267
+ * @access public
268
+ *
269
+ * @return array Data properties.
270
+ */
271
+ private function get_merged_default_extended_data() {
272
+
273
+ return array_merge( $this->default_data , $this->extend_data );
274
+
275
+ }
276
+
277
+ /**
278
+ * Return's the post data equivalent of a certain affiliate link data property.
279
+ *
280
+ * @since 3.0.0
281
+ * @access private
282
+ *
283
+ * @param string $prop Affiliate link property name.
284
+ * @return string WP Post property equivalent.
285
+ */
286
+ private function get_post_data_equivalent( $prop ) {
287
+
288
+ $equivalents = apply_filters( 'ta_affiliate_link_post_data_equivalent' , array(
289
+ 'name' => $this->post_data->post_title,
290
+ 'slug' => $this->post_data->post_name,
291
+ 'permalink' => get_permalink( $this->post_data->ID ),
292
+ 'status' => $this->post_data->post_status,
293
+ 'date_created' => $this->post_data->post_date,
294
+ 'date_modified' => $this->post_data->post_modified,
295
+ ) , $this->post_data );
296
+
297
+ if ( array_key_exists( $prop , $equivalents ) )
298
+ return $equivalents[ $prop ];
299
+ else
300
+ return;
301
+
302
+ }
303
+
304
+ /**
305
+ * Return data property.
306
+ *
307
+ * @since 3.0.0
308
+ * @since 3.3.1 Make sure deprecated props are ignored.
309
+ * @access public
310
+ *
311
+ * @param string $prop Data property slug.
312
+ * @param mixed $default Set property default value (optional).
313
+ * @return mixed Property data.
314
+ */
315
+ public function get_prop( $prop , $default = '' ) {
316
+
317
+ $default_data = $this->get_merged_default_extended_data();
318
+
319
+ if ( in_array( $prop , $this->deprecated_props ) )
320
+ return $default;
321
+
322
+ if ( array_key_exists( $prop , $this->data ) && $this->data[ $prop ] )
323
+ $return_value = $this->data[ $prop ];
324
+ else
325
+ $return_value = ( $default ) ? $default : $default_data[ $prop ];
326
+
327
+ return $return_value;
328
+
329
+ }
330
+
331
+ /**
332
+ * Get redirect type
333
+ *
334
+ * @since 3.4.0
335
+ * @access public
336
+ *
337
+ * @param string $default Default redirect type.
338
+ * @return string Redirect type.
339
+ */
340
+ public function get_redirect_type( $default = '' ) {
341
+
342
+ $redirect_type = $this->data[ 'redirect_type' ];
343
+
344
+ if ( $redirect_type === 'global' )
345
+ $redirect_type = get_option( 'ta_link_redirect_type' , $default );
346
+ elseif ( ! $redirect_type )
347
+ $redirect_type = $default ? $default : $default_data[ 'redirect_type' ];
348
+
349
+ return $redirect_type;
350
+ }
351
+
352
+ /**
353
+ * Return Affiliate_Link ID.
354
+ *
355
+ * @since 3.0.0
356
+ * @access public
357
+ *
358
+ * @return int Affiliate_Link ID.
359
+ */
360
+ public function get_id() {
361
+
362
+ return absint( $this->id );
363
+
364
+ }
365
+
366
+ /**
367
+ * Return changed data property.
368
+ *
369
+ * @since 3.0.0
370
+ * @access public
371
+ *
372
+ * @param string $prop Data property slug.
373
+ * @param mixed $default Set property default value (optional).
374
+ * @return mixed Property data.
375
+ */
376
+ public function get_changed_prop( $prop , $default = '' ) {
377
+
378
+ return isset( $this->changes[ $prop ] ) ? $this->changes[ $prop ] : $this->get_prop( $prop , $default );
379
+
380
+ }
381
+
382
+ /**
383
+ * Return affiliate link's WP_Post data.
384
+ *
385
+ * @since 3.0.0
386
+ * @access public
387
+ *
388
+ * @return object Post data object.
389
+ */
390
+ public function get_post_data() {
391
+
392
+ return $this->post_data;
393
+
394
+ }
395
+
396
+ /**
397
+ * Get the properties global option value.
398
+ *
399
+ * @since 3.0.0
400
+ * @since 3.2.2 Remove 'uncloak' option.
401
+ * @since 3.4.0 Remove 'redirect_type' option. Add additional CSS classes option.
402
+ * @access public
403
+ *
404
+ * @param string $prop Name of property.
405
+ * @return string Global option value.
406
+ */
407
+ public function get_prop_global_option_value( $prop , $default = '' ) {
408
+
409
+ switch( $prop ) {
410
+
411
+ case 'rel_tags' :
412
+ $option = 'ta_additional_rel_tags';
413
+ break;
414
+
415
+ case 'css_classes' :
416
+ $option = 'ta_additional_css_classes';
417
+ break;
418
+ }
419
+
420
+ return $option ? get_option( $option , $default ) : $default;
421
+ }
422
+
423
+ /**
424
+ * Get the global value for the uncloak property.
425
+ *
426
+ * @deprecated 3.2.0
427
+ *
428
+ * @since 3.0.0
429
+ * @access public
430
+ *
431
+ * @return string Global option value.
432
+ */
433
+ public function get_global_uncloak_value() {
434
+
435
+ return $this->get_toggle_prop_global_value( 'uncloak_link' );
436
+ }
437
+
438
+ /**
439
+ * Get toggle property global value. This function also checks for "category" valued options.
440
+ *
441
+ * @since 3.2.0
442
+ * @access public
443
+ *
444
+ * @param string $toggle_prop Affiliate linkg toggle property name.
445
+ * @return string 'yes' if true, otherwise 'no'.
446
+ */
447
+ public function get_toggle_prop_global_value( $toggle_prop ) {
448
+
449
+ $global_props = apply_filters( 'ta_props_with_global_option' , array(
450
+ 'no_follow' => 'ta_no_follow',
451
+ 'new_window' => 'ta_new_window',
452
+ 'uncloak_link' => '',
453
+ 'pass_query_str' => 'ta_pass_query_str'
454
+ ) );
455
+ $global_props_cat = apply_filters( 'ta_prop_selected_categories_option' , array(
456
+ 'no_follow' => 'ta_no_follow_category',
457
+ 'new_window' => 'ta_new_window_category',
458
+ 'uncloak_link' => 'ta_category_to_uncloak',
459
+ 'pass_query_str' => 'ta_pass_query_str_category'
460
+ ) );
461
+
462
+ if ( ! array_key_exists( $toggle_prop , $global_props ) )
463
+ return 'no';
464
+
465
+ // get equivalent global value.
466
+ $prop_value = $toggle_prop == 'uncloak_link' ? 'category' : get_option( $global_props[ $toggle_prop ] );
467
+
468
+ if ( ! $prop_value )
469
+ return 'no';
470
+
471
+ // if prop value is not set to 'category', then return option value.
472
+ if ( $prop_value != 'category' || ! array_key_exists( $toggle_prop , $global_props_cat ) )
473
+ return $prop_value;
474
+
475
+ $prop_cats = maybe_unserialize( get_option( $global_props_cat[ $toggle_prop ] , array() ) );
476
+ $link_cats = $this->get_prop( 'categories' );
477
+
478
+ // skip when there are no categories to check (false)
479
+ if ( ! is_array( $prop_cats ) || empty( $prop_cats ) || empty( $link_cats ) )
480
+ return 'no';
481
+
482
+ foreach ( $link_cats as $category ) {
483
+
484
+ if ( in_array( $category->term_id , $prop_cats ) )
485
+ return 'yes';
486
+ }
487
+
488
+ return 'no';
489
+ }
490
+
491
+ /**
492
+ * Gets the category slug used for affiliate link (if present).
493
+ *
494
+ * @since 3.2.2
495
+ * @access public
496
+ *
497
+ * @return string Affiliate link ategory slug.
498
+ */
499
+ public function get_category_slug() {
500
+
501
+ $cat_slug = $this->get_prop( 'category_slug' );
502
+ return $cat_slug ? $cat_slug : $this->_helper_functions->get_default_category_slug( $this->get_id() , $this->get_prop( 'categories' ) );
503
+ }
504
+
505
+
506
+
507
+
508
+ /*
509
+ |--------------------------------------------------------------------------
510
+ | Data setters
511
+ |--------------------------------------------------------------------------
512
+ */
513
+
514
+ /**
515
+ * Set new value to properties and save it to $changes property.
516
+ * This stores changes in a special array so we can track what needs to be saved on the DB later.
517
+ *
518
+ * @since 3.0.0
519
+ * @access public
520
+ *
521
+ * @param string $prop Data property slug.
522
+ * @param string $value New property value.
523
+ */
524
+ public function set_prop( $prop , $value ) {
525
+
526
+ $default_data = $this->get_merged_default_extended_data();
527
+
528
+ if ( array_key_exists( $prop , $this->data ) ) {
529
+
530
+ // permalink property must not be changed
531
+ if ( $prop == 'permalink' )
532
+ return;
533
+
534
+ if ( gettype( $value ) == gettype( $default_data[ $prop ] ) )
535
+ $this->changes[ $prop ] = $value;
536
+ else {
537
+
538
+ // TODO: handle error here.
539
+
540
+ }
541
+
542
+ } else {
543
+
544
+ $this->data[ $prop ] = $value;
545
+ $this->changes[ $prop ] = $value;
546
+
547
+ }
548
+
549
+ }
550
+
551
+
552
+
553
+
554
+ /*
555
+ |--------------------------------------------------------------------------
556
+ | Save (Create / Update) data to DB
557
+ |--------------------------------------------------------------------------
558
+ */
559
+
560
+ /**
561
+ * Save data in $changes to the database.
562
+ *
563
+ * @since 3.0.0
564
+ * @access public
565
+ *
566
+ * @return WP_Error | int On success will return the post ID, otherwise it will return a WP_Error object.
567
+ */
568
+ public function save() {
569
+
570
+ if ( ! empty( $this->changes ) ) {
571
+
572
+ $post_metas = array();
573
+ $post_data = array(
574
+ 'post_title' => $this->get_changed_prop( 'name' ),
575
+ 'post_name' => $this->get_changed_prop( 'slug' ),
576
+ 'post_status' => $this->get_changed_prop( 'status' , 'publish' ),
577
+ 'post_date' => $this->get_changed_prop( 'date_created' , current_time( 'mysql' ) ),
578
+ 'post_modified' => $this->get_changed_prop( 'date_modified' , current_time( 'mysql' ) )
579
+ );
580
+
581
+ foreach ( $this->changes as $prop => $value ) {
582
+
583
+ // make sure that property is registered in default data
584
+ if ( ! array_key_exists( $prop , $this->get_merged_default_extended_data() ) )
585
+ continue;
586
+
587
+ if ( in_array( $prop , array( 'permalink' , 'name' , 'slug' , 'status' , 'date_created' , 'date_modified' ) ) )
588
+ continue;
589
+
590
+ $post_metas[ $prop ] = $value;
591
+ }
592
+
593
+ // create or update post
594
+ if ( $this->id )
595
+ $post_id = $this->update( $post_data );
596
+ else
597
+ $post_id = $this->create( $post_data );
598
+
599
+ if ( ! is_wp_error( $post_id ) )
600
+ $this->update_metas( $post_id , $post_metas );
601
+ else
602
+ return $post_id; // Return WP_Error object on error
603
+
604
+ do_action( 'ta_save_affiliate_link' , $this->changes , $this );
605
+
606
+ // update instance with new changes.
607
+ $this->object_is_read = false;
608
+ $this->read();
609
+
610
+ } else
611
+ return new \WP_Error( 'ta_affiliate_link_no_changes' , __( 'Unable to save affiliate link as there are no changes registered on the object yet.' , 'thirstyaffiliates' ) , array( 'changes' => $this->changes , 'affiliate_link' => $this ) );
612
+
613
+ return $post_id;
614
+ }
615
+
616
+ /**
617
+ * Create the affiliate link post.
618
+ *
619
+ * @since 3.0.0
620
+ * @access private
621
+ *
622
+ * @param array $post_data Affiliate link post data.
623
+ * @param WP_Error|int WP_Error on error, ID of newly created post otherwise.
624
+ */
625
+ private function create( $post_data ) {
626
+
627
+ $post_data = array_merge( array( 'post_type' => Plugin_Constants::AFFILIATE_LINKS_CPT ) , $post_data );
628
+ $this->id = wp_insert_post( $post_data );
629
+
630
+ return $this->id;
631
+
632
+ }
633
+
634
+ /**
635
+ * Update the affiliate link post.
636
+ *
637
+ * @since 3.0.0
638
+ * @access private
639
+ *
640
+ * @param array $post_data Affiliate link post data.
641
+ * @return int ID of the updated post upon success. 0 on failure.
642
+ */
643
+ private function update( $post_data ) {
644
+
645
+ $post_data = array_merge( array( 'ID' => $this->id ) , $post_data );
646
+ return wp_update_post( $post_data , true );
647
+
648
+ }
649
+
650
+ /**
651
+ * Update/add the affiliate link meta data.
652
+ *
653
+ * @since 3.0.0
654
+ * @access private
655
+ *
656
+ * @param int $post_id Affiliate link post ID.
657
+ * @param array $post_metas Affiliate link meta data.
658
+ */
659
+ private function update_metas( $post_id , $post_metas ) {
660
+
661
+ foreach ( $post_metas as $key => $value )
662
+ update_post_meta( $post_id , Plugin_Constants::META_DATA_PREFIX . $key , $value );
663
+
664
+ }
665
+
666
+
667
+
668
+
669
+ /*
670
+ |--------------------------------------------------------------------------
671
+ | Utility Functions.
672
+ |--------------------------------------------------------------------------
673
+ */
674
+
675
+ /**
676
+ * Conditional function that checks if a property is true for the affiliate link. This function also checks global and category set values.
677
+ *
678
+ * @since 3.2.0
679
+ * @access public
680
+ *
681
+ * @param string $toggle_prop Affiliate link toggle property to check.
682
+ * @return boolean true | false.
683
+ */
684
+ public function is( $toggle_prop ) {
685
+
686
+ // check if global setting for uncloak link is enabled.
687
+ if ( $toggle_prop == 'uncloak_link' && get_option( 'ta_uncloak_link_per_link' ) !== 'yes' )
688
+ return;
689
+
690
+ $prop_value = $this->get_prop( $toggle_prop );
691
+
692
+ // if prop value is not set to 'global', then return with the boolean equivalent of its value.
693
+ if ( $prop_value != 'global' )
694
+ return $prop_value == 'yes' ? true : false;
695
+
696
+ // get property global value. Also checks for category selected options.
697
+ $prop_value = $this->get_toggle_prop_global_value( $toggle_prop );
698
+
699
+ return $prop_value == 'yes' ? true : false;
700
+ }
701
+
702
+ /**
703
+ * Count affiliate link clicks.
704
+ *
705
+ * @since 3.0.0
706
+ * @since 3.1.0 Add $date_offset parameter to count links only to a certain point.
707
+ * @access public
708
+ *
709
+ * @param string $date_offset Date before limit to check
710
+ * @return int Total number of clicks.
711
+ */
712
+ public function count_clicks( $date_offset = '' ) {
713
+
714
+ global $wpdb;
715
+
716
+ $table_name = $wpdb->prefix . Plugin_Constants::LINK_CLICK_DB;
717
+ $link_id = $this->get_id();
718
+ $query = "SELECT count(*) from $table_name WHERE link_id = $link_id";
719
+ $query .= ( $date_offset && \DateTime::createFromFormat('Y-m-d H:i:s', $date_offset ) !== false ) ? " AND date_clicked > '$date_offset'" : '';
720
+ $clicks = $wpdb->get_var( $query );
721
+
722
+ return (int) $clicks;
723
+ }
724
+
725
+ /**
726
+ * Scan where links are inserted.
727
+ *
728
+ * @since 3.2.0
729
+ * @since 3.3.3 Improve the query to specify the results by searching using the permalink value, and alternating between the used link prefixes.
730
+ * @access public
731
+ *
732
+ * @return array List of WP_Post IDs where affiliate link is inserted in content.
733
+ */
734
+ public function scan_where_links_inserted() {
735
+
736
+ global $wpdb;
737
+
738
+ // prepare the query.
739
+ $post_ids = array();
740
+ $link_id = $this->get_id();
741
+ $cpt_slug = Plugin_Constants::AFFILIATE_LINKS_CPT;
742
+ $types = get_post_types( array( 'public' => true ) , 'names' , 'and' );
743
+ $types_str = implode( "','" , $types );
744
+ $permalink = $this->get_prop( 'permalink' );
745
+ $link_prefix = $this->_helper_functions->get_thirstylink_link_prefix();
746
+ $link_prefixes = $this->_helper_functions->get_option( 'ta_used_link_prefixes' , array() );
747
+ $like_query = array();
748
+
749
+ foreach ( $link_prefixes as $prefix )
750
+ $like_query[] = str_replace( $link_prefix , $prefix , "post_content LIKE '%$permalink\"%'" );
751
+
752
+ $like_query_str = implode( ' OR ' , $like_query );
753
+ $query = "SELECT ID FROM $wpdb->posts WHERE ( $like_query_str OR post_content LIKE '%[thirstylink%ids=\"$link_id%' ) AND post_type IN ( '$types_str' ) AND post_status = 'publish'";
754
+
755
+ // fetch WP_Post IDs where link is inserted to.
756
+ $raw_ids = $wpdb->get_col( $query );
757
+
758
+ // save last scanned
759
+ update_post_meta( $this->get_id() , Plugin_Constants::META_DATA_PREFIX . 'scanned_inserted' , current_time( 'mysql' , true ) );
760
+
761
+ // save to custom meta.
762
+ $post_ids = array_map( 'intval' , $raw_ids );
763
+ update_post_meta( $this->get_id() , Plugin_Constants::META_DATA_PREFIX . 'inserted_to' , $post_ids );
764
+
765
+ return $post_ids;
766
+ }
767
+
768
+ }
Models/Affiliate_Link_Attachment.php CHANGED
@@ -152,7 +152,7 @@ class Affiliate_Link_Attachment implements Model_Interface , Initiable_Interface
152
  $response = array( 'status' => 'success' , 'success_msg' => __( 'Attachments successfully added to the affiliate link' , 'thirstyaffiliates' ) , 'added_attachments_markup' => $added_attachments_markup );
153
 
154
  }
155
-
156
  }
157
 
158
  @header( 'Content-Type: application/json; charset=' . get_option( 'blog_charset' ) );
@@ -169,7 +169,7 @@ class Affiliate_Link_Attachment implements Model_Interface , Initiable_Interface
169
  *
170
  * @param array $attachment_ids Array of attachment ids.
171
  * @param int $affiliate_link_id Id of the current affiliate link.
172
- * @return WP_Error | boolean WP_Error instance on failure, boolean true otherwise.
173
  */
174
  public function add_attachments_to_affiliate_link( $attachment_ids , $affiliate_link_id ) {
175
 
@@ -190,7 +190,7 @@ class Affiliate_Link_Attachment implements Model_Interface , Initiable_Interface
190
 
191
  /**
192
  * Add attachments to affiliate link via ajax.
193
- *
194
  * @since 3.0.0
195
  * @access public
196
  */
@@ -207,8 +207,8 @@ class Affiliate_Link_Attachment implements Model_Interface , Initiable_Interface
207
  if ( is_wp_error( $result ) )
208
  $response = array( 'status' => 'fail' , 'error_msg' => $result->get_error_message() );
209
  else
210
- $response = array( 'status' => 'success' , 'success_msg' => __( 'Attachment successfully removed from attachment' , 'thirstyaffiliates' ) );
211
-
212
  }
213
 
214
  @header( 'Content-Type: application/json; charset=' . get_option( 'blog_charset' ) );
@@ -225,23 +225,23 @@ class Affiliate_Link_Attachment implements Model_Interface , Initiable_Interface
225
  *
226
  * @param int $attachment_id Attachment id.
227
  * @param int $affiliate_link_id Affiliate link id.
228
- * @return WP_Error | boolean WP_Error instance on failure, boolean true otherwise.
229
  */
230
  public function remove_attachment_to_affiliate_link( $attachment_id , $affiliate_link_id ) {
231
 
232
  $attachments = get_post_meta( $affiliate_link_id , Plugin_Constants::META_DATA_PREFIX . 'image_ids' , true );
233
  if ( !is_array( $attachments ) )
234
  $attachments = array();
235
-
236
  if ( !in_array( $attachment_id , $attachments ) )
237
  return new \WP_Error( 'ta_invalid_attachment_id' , __( 'Invalid attachment id to remove from an affiliate link' , 'thirstyaffiliates' ) , array( 'attachment_id' => $attachment_id , 'affiliate_link_id' => 'affiliate_link_id' ) );
238
-
239
  $key = array_search( $attachment_id , $attachments );
240
  unset( $attachments[ $key ] );
241
 
242
  update_post_meta( $affiliate_link_id , Plugin_Constants::META_DATA_PREFIX . 'image_ids' , $attachments );
243
 
244
- return true;
245
 
246
  }
247
 
@@ -288,7 +288,7 @@ class Affiliate_Link_Attachment implements Model_Interface , Initiable_Interface
288
 
289
  /**
290
  * AJAX insert external image.
291
- *
292
  * @since 3.4.0
293
  * @access public
294
  */
@@ -322,17 +322,17 @@ class Affiliate_Link_Attachment implements Model_Interface , Initiable_Interface
322
  'markup' => $image_markup
323
  );
324
  }
325
-
326
  }
327
 
328
  @header( 'Content-Type: application/json; charset=' . get_option( 'blog_charset' ) );
329
  echo wp_json_encode( $response );
330
  wp_die();
331
  }
332
-
333
 
334
 
335
-
 
336
  /*
337
  |--------------------------------------------------------------------------
338
  | Implemented Interface Methods
@@ -356,7 +356,7 @@ class Affiliate_Link_Attachment implements Model_Interface , Initiable_Interface
356
 
357
  /**
358
  * Execute model core logic.
359
- *
360
  * @since 3.0.0
361
  * @access public
362
  * @implements ThirstyAffiliates\Interfaces\Model_Interface
152
  $response = array( 'status' => 'success' , 'success_msg' => __( 'Attachments successfully added to the affiliate link' , 'thirstyaffiliates' ) , 'added_attachments_markup' => $added_attachments_markup );
153
 
154
  }
155
+
156
  }
157
 
158
  @header( 'Content-Type: application/json; charset=' . get_option( 'blog_charset' ) );
169
  *
170
  * @param array $attachment_ids Array of attachment ids.
171
  * @param int $affiliate_link_id Id of the current affiliate link.
172
+ * @return \WP_Error | boolean WP_Error instance on failure, boolean true otherwise.
173
  */
174
  public function add_attachments_to_affiliate_link( $attachment_ids , $affiliate_link_id ) {
175
 
190
 
191
  /**
192
  * Add attachments to affiliate link via ajax.
193
+ *
194
  * @since 3.0.0
195
  * @access public
196
  */
207
  if ( is_wp_error( $result ) )
208
  $response = array( 'status' => 'fail' , 'error_msg' => $result->get_error_message() );
209
  else
210
+ $response = array( 'status' => 'success' , 'success_msg' => __( 'Attachment successfully removed from attachment' , 'thirstyaffiliates' ) );
211
+
212
  }
213
 
214
  @header( 'Content-Type: application/json; charset=' . get_option( 'blog_charset' ) );
225
  *
226
  * @param int $attachment_id Attachment id.
227
  * @param int $affiliate_link_id Affiliate link id.
228
+ * @return \WP_Error | boolean WP_Error instance on failure, boolean true otherwise.
229
  */
230
  public function remove_attachment_to_affiliate_link( $attachment_id , $affiliate_link_id ) {
231
 
232
  $attachments = get_post_meta( $affiliate_link_id , Plugin_Constants::META_DATA_PREFIX . 'image_ids' , true );
233
  if ( !is_array( $attachments ) )
234
  $attachments = array();
235
+
236
  if ( !in_array( $attachment_id , $attachments ) )
237
  return new \WP_Error( 'ta_invalid_attachment_id' , __( 'Invalid attachment id to remove from an affiliate link' , 'thirstyaffiliates' ) , array( 'attachment_id' => $attachment_id , 'affiliate_link_id' => 'affiliate_link_id' ) );
238
+
239
  $key = array_search( $attachment_id , $attachments );
240
  unset( $attachments[ $key ] );
241
 
242
  update_post_meta( $affiliate_link_id , Plugin_Constants::META_DATA_PREFIX . 'image_ids' , $attachments );
243
 
244
+ return true;
245
 
246
  }
247
 
288
 
289
  /**
290
  * AJAX insert external image.
291
+ *
292
  * @since 3.4.0
293
  * @access public
294
  */
322
  'markup' => $image_markup
323
  );
324
  }
325
+
326
  }
327
 
328
  @header( 'Content-Type: application/json; charset=' . get_option( 'blog_charset' ) );
329
  echo wp_json_encode( $response );
330
  wp_die();
331
  }
 
332
 
333
 
334
+
335
+
336
  /*
337
  |--------------------------------------------------------------------------
338
  | Implemented Interface Methods
356
 
357
  /**
358
  * Execute model core logic.
359
+ *
360
  * @since 3.0.0
361
  * @access public
362
  * @implements ThirstyAffiliates\Interfaces\Model_Interface
Models/Affiliate_Links_CPT.php CHANGED
@@ -1,1014 +1,1026 @@
1
- <?php
2
-
3
- namespace ThirstyAffiliates\Models;
4
-
5
- use ThirstyAffiliates\Abstracts\Abstract_Main_Plugin_Class;
6
-
7
- use ThirstyAffiliates\Interfaces\Model_Interface;
8
- use ThirstyAffiliates\Interfaces\Initiable_Interface;
9
-
10
- use ThirstyAffiliates\Helpers\Plugin_Constants;
11
- use ThirstyAffiliates\Helpers\Helper_Functions;
12
-
13
- // Data Models
14
- use ThirstyAffiliates\Models\Affiliate_Link;
15
-
16
- /**
17
- * Model that houses the logic of registering the 'thirstylink' custom post type.
18
- *
19
- * @since 3.0.0
20
- */
21
- class Affiliate_Links_CPT implements Model_Interface , Initiable_Interface {
22
-
23
- /*
24
- |--------------------------------------------------------------------------
25
- | Class Properties
26
- |--------------------------------------------------------------------------
27
- */
28
-
29
- /**
30
- * Property that holds the single main instance of Bootstrap.
31
- *
32
- * @since 3.0.0
33
- * @access private
34
- * @var Affiliate_Links_CPT
35
- */
36
- private static $_instance;
37
-
38
- /**
39
- * Model that houses the main plugin object.
40
- *
41
- * @since 3.0.0
42
- * @access private
43
- * @var Abstract_Main_Plugin_Class
44
- */
45
- private $_main_plugin;
46
-
47
- /**
48
- * Model that houses all the plugin constants.
49
- *
50
- * @since 3.0.0
51
- * @access private
52
- * @var Plugin_Constants
53
- */
54
- private $_constants;
55
-
56
- /**
57
- * Property that houses all the helper functions of the plugin.
58
- *
59
- * @since 3.0.0
60
- * @access private
61
- * @var Helper_Functions
62
- */
63
- private $_helper_functions;
64
-
65
- /**
66
- * Property that holds the currently loaded thirstylink post.
67
- *
68
- * @since 3.0.0
69
- * @access private
70
- */
71
- private $_thirstylink;
72
-
73
-
74
-
75
-
76
- /*
77
- |--------------------------------------------------------------------------
78
- | Class Methods
79
- |--------------------------------------------------------------------------
80
- */
81
-
82
- /**
83
- * Class constructor.
84
- *
85
- * @since 3.0.0
86
- * @access public
87
- *
88
- * @param Abstract_Main_Plugin_Class $main_plugin Main plugin object.
89
- * @param Plugin_Constants $constants Plugin constants object.
90
- * @param Helper_Functions $helper_functions Helper functions object.
91
- */
92
- public function __construct( Abstract_Main_Plugin_Class $main_plugin , Plugin_Constants $constants , Helper_Functions $helper_functions ) {
93
-
94
- $this->_constants = $constants;
95
- $this->_helper_functions = $helper_functions;
96
-
97
- $main_plugin->add_to_all_plugin_models( $this );
98
-
99
- }
100
-
101
- /**
102
- * Ensure that only one instance of this class is loaded or can be loaded ( Singleton Pattern ).
103
- *
104
- * @since 3.0.0
105
- * @access public
106
- *
107
- * @param Abstract_Main_Plugin_Class $main_plugin Main plugin object.
108
- * @param Plugin_Constants $constants Plugin constants object.
109
- * @param Helper_Functions $helper_functions Helper functions object.
110
- * @return Affiliate_Links_CPT
111
- */
112
- public static function get_instance( Abstract_Main_Plugin_Class $main_plugin , Plugin_Constants $constants , Helper_Functions $helper_functions ) {
113
-
114
- if ( !self::$_instance instanceof self )
115
- self::$_instance = new self( $main_plugin , $constants , $helper_functions );
116
-
117
- return self::$_instance;
118
-
119
- }
120
-
121
- /**
122
- * Register admin interfaces.
123
- *
124
- * @since 3.3.3
125
- * @access public
126
- *
127
- * @param array $interfaces List of admin interfaces.
128
- * @return array Filtered list of admin interfaces.
129
- */
130
- public function register_admin_interfaces( $interfaces ) {
131
-
132
- $interfaces[ 'edit-thirstylink' ] = 'edit_posts';
133
- $interfaces[ 'thirstylink' ] = 'edit_posts';
134
- $interfaces[ 'edit-thirstylink-category' ] = 'manage_categories';
135
-
136
- return $interfaces;
137
- }
138
-
139
- /**
140
- * Register admin interfaces.
141
- *
142
- * @since 3.3.3
143
- * @access public
144
- *
145
- * @param array $interfaces List of menu items.
146
- * @return array Filtered list of menu items.
147
- */
148
- public function register_admin_menu_items( $menu_items ) {
149
-
150
- $list_slug = 'edit.php?post_type=' . Plugin_Constants::AFFILIATE_LINKS_CPT;
151
- $new_post_slug = 'post-new.php?post_type=' . Plugin_Constants::AFFILIATE_LINKS_CPT;
152
- $link_cat_slug = 'edit-tags.php?taxonomy=' . Plugin_Constants::AFFILIATE_LINKS_TAX . '&post_type=' . Plugin_Constants::AFFILIATE_LINKS_CPT;
153
- $menu_items[ $list_slug ] = 'edit_posts';
154
- $menu_items[ $new_post_slug ] = 'edit_posts';
155
- $menu_items[ $link_cat_slug ] = 'manage_categories';
156
-
157
- return $menu_items;
158
- }
159
-
160
- /**
161
- * Get thirstylink Affiliate_Link object.
162
- *
163
- * @since 3.0.0
164
- * @access private
165
- *
166
- * @param int $post_id Thirstylink post id.
167
- * @return Affiliate_Link object.
168
- */
169
- private function get_thirstylink_post( $post_id ) {
170
-
171
- if ( is_object( $this->_thirstylink ) && $this->_thirstylink->get_id() == $post_id )
172
- return $this->_thirstylink;
173
-
174
- return $this->_thirstylink = new Affiliate_Link( $post_id );
175
-
176
- }
177
-
178
-
179
-
180
-
181
- /*
182
- |--------------------------------------------------------------------------
183
- | Register Post Type and Taxonomy
184
- |--------------------------------------------------------------------------
185
- */
186
-
187
- /**
188
- * Register the 'thirstylink' custom post type.
189
- *
190
- * @since 3.0.0
191
- * @since 3.3.2 Set manage_terms capability to read so we can control visibility natively. see Bootstrap::admin_interface_visibility.
192
- * @access private
193
- */
194
- private function register_thirstylink_custom_post_type() {
195
-
196
- $link_prefix = $this->_helper_functions->get_thirstylink_link_prefix();
197
-
198
- $labels = array(
199
- 'name' => __( 'Affiliate Links' , 'thirstyaffiliates' ),
200
- 'singular_name' => __( 'Affiliate Link' , 'thirstyaffiliates' ),
201
- 'menu_name' => __( 'ThirstyAffiliates' , 'thirstyaffiliates' ),
202
- 'parent_item_colon' => __( 'Parent Affiliate Link' , 'thirstyaffiliates' ),
203
- 'all_items' => __( 'Affiliate Links' , 'thirstyaffiliates' ),
204
- 'view_item' => __( 'View Affiliate Link' , 'thirstyaffiliates' ),
205
- 'add_new_item' => __( 'Add Affiliate Link' , 'thirstyaffiliates' ),
206
- 'add_new' => __( 'New Affiliate Link' , 'thirstyaffiliates' ),
207
- 'edit_item' => __( 'Edit Affiliate Link' , 'thirstyaffiliates' ),
208
- 'update_item' => __( 'Update Affiliate Link' , 'thirstyaffiliates' ),
209
- 'search_items' => __( 'Search Affiliate Links' , 'thirstyaffiliates' ),
210
- 'not_found' => __( 'No Affiliate Link found' , 'thirstyaffiliates' ),
211
- 'not_found_in_trash' => __( 'No Affiliate Links found in Trash' , 'thirstyaffiliates' )
212
- );
213
-
214
- $args = array(
215
- 'label' => __( 'Affiliate Links' , 'thirstyaffiliates' ),
216
- 'description' => __( 'ThirstyAffiliates affiliate links' , 'thirstyaffiliates' ),
217
- 'labels' => $labels,
218
- 'supports' => array( 'title' , 'custom-fields' ),
219
- 'taxonomies' => array(),
220
- 'hierarchical' => true,
221
- 'public' => false,
222
- 'show_ui' => true,
223
- 'show_in_menu' => true,
224
- 'show_in_json' => false,
225
- 'query_var' => true,
226
- 'rewrite' => array(
227
- 'slug' => $link_prefix,
228
- 'with_front' => false,
229
- 'pages' => false
230
- ),
231
- 'show_in_nav_menus' => false,
232
- 'show_in_admin_bar' => true,
233
- 'menu_position' => 26,
234
- 'menu_icon' => 'data:image/svg+xml;base64,' . base64_encode('<svg xmlns="http://www.w3.org/2000/svg" width="16.688" height="9.875" viewBox="0 0 16.688 9.875">
235
- <path id="TA.svg" fill="black" class="cls-1" d="M2.115,15.12H4.847L6.836,7.7H9.777l0.63-2.381H1.821L1.177,7.7H4.118Zm4.758,0H9.829l1.177-1.751h3.782l0.238,1.751h2.858L16.357,5.245H13.7Zm5.5-3.866,1.835-2.816,0.35,2.816H12.378Z" transform="translate(-1.188 -5.25)"/>
236
- </svg>
237
- '),
238
- 'can_export' => true,
239
- 'has_archive' => false,
240
- 'exclude_from_search' => true,
241
- 'publicly_queryable' => true,
242
- 'capability_type' => 'post',
243
- 'show_in_rest' => true,
244
- );
245
-
246
- register_post_type( Plugin_Constants::AFFILIATE_LINKS_CPT , apply_filters( 'ta_affiliate_links_cpt_args' , $args , $labels ) );
247
-
248
- do_action( 'ta_after_register_thirstylink_post_type' , $link_prefix );
249
- }
250
-
251
- /**
252
- * Register the 'thirstylink-category' custom taxonomy.
253
- *
254
- * @since 3.0.0
255
- * @since 3.3.2 Set manage_terms capability to read so we can control visibility natively. see Bootstrap::admin_interface_visibility.
256
- * @access private
257
- */
258
- private function register_thirstylink_category_custom_taxonomy() {
259
-
260
- $labels = array(
261
- 'name' => __( 'Link Categories', 'thirstyaffiliates' ),
262
- 'singular_name' => __( 'Link Category', 'thirstyaffiliates' ),
263
- 'menu_name' => __( 'Link Categories', 'thirstyaffiliates' ),
264
- 'all_items' => __( 'All Categories', 'thirstyaffiliates' ),
265
- 'parent_item' => __( 'Parent Category', 'thirstyaffiliates' ),
266
- 'parent_item_colon' => __( 'Parent Category:', 'thirstyaffiliates' ),
267
- 'new_item_name' => __( 'New Category Name', 'thirstyaffiliates' ),
268
- 'add_new_item' => __( 'Add New Category', 'thirstyaffiliates' ),
269
- 'edit_item' => __( 'Edit Category', 'thirstyaffiliates' ),
270
- 'update_item' => __( 'Update Category', 'thirstyaffiliates' ),
271
- 'view_item' => __( 'View Category', 'thirstyaffiliates' ),
272
- 'separate_items_with_commas' => __( 'Separate items with commas', 'thirstyaffiliates' ),
273
- 'add_or_remove_items' => __( 'Add or remove items', 'thirstyaffiliates' ),
274
- 'choose_from_most_used' => __( 'Choose from the most used', 'thirstyaffiliates' ),
275
- 'popular_items' => __( 'Popular Categories', 'thirstyaffiliates' ),
276
- 'search_items' => __( 'Search Categories', 'thirstyaffiliates' ),
277
- 'not_found' => __( 'Not Found', 'thirstyaffiliates' ),
278
- 'no_terms' => __( 'No items', 'thirstyaffiliates' ),
279
- 'items_list' => __( 'Category list', 'thirstyaffiliates' ),
280
- 'items_list_navigation' => __( 'Category list navigation', 'thirstyaffiliates' )
281
- );
282
-
283
- $args = array(
284
- 'labels' => $labels,
285
- 'hierarchical' => true,
286
- 'public' => false,
287
- 'show_ui' => true,
288
- 'show_admin_column' => true,
289
- 'show_in_nav_menus' => false,
290
- 'show_tagcloud' => false,
291
- 'rewrite' => false,
292
- 'capabilities' => array(
293
- 'manage_terms' => 'read',
294
- )
295
- );
296
-
297
- register_taxonomy( Plugin_Constants::AFFILIATE_LINKS_TAX , Plugin_Constants::AFFILIATE_LINKS_CPT , apply_filters( 'ta_affiliate_link_taxonomy_args' , $args , $labels ) );
298
-
299
- }
300
-
301
-
302
-
303
-
304
- /*
305
- |--------------------------------------------------------------------------
306
- | UI and metabox related functions
307
- |--------------------------------------------------------------------------
308
- */
309
-
310
- /**
311
- * Replace default post type permalink html with affiliate link ID.
312
- *
313
- * @since 3.0.0
314
- * @access public
315
- *
316
- * @param string $html Permalink html.
317
- * @param int $post_id Affiliate Link post id.
318
- * @return string Link ID html.
319
- */
320
- public function replace_permalink_with_id( $html , $post_id ) {
321
-
322
- if ( get_post_type( $post_id ) == Plugin_Constants::AFFILIATE_LINKS_CPT )
323
- return '<span id="link_id">' . __( 'Link ID:' , 'thirstyaffiliates' ) . ' <strong>' . $post_id . '</strong></span>';
324
-
325
- return $html;
326
- }
327
-
328
- /**
329
- * Register metaboxes
330
- *
331
- * @since 3.0.0
332
- * @access public
333
- */
334
- public function register_metaboxes() {
335
-
336
- $normal_metaboxes = apply_filters( 'ta_register_normal_metaboxes' , array(
337
- array(
338
- 'id' => 'ta-urls-metabox',
339
- 'title' => __( 'URLs', 'thirstyaffiliates' ),
340
- 'cb' => array( $this , 'urls_metabox' ),
341
- 'sort' => 10,
342
- 'priority' => 'high'
343
- ),
344
- array(
345
- 'id' => 'ta-attach-images-metabox',
346
- 'title' => __( 'Attach Images', 'thirstyaffiliates' ),
347
- 'cb' => array( $this , 'attach_images_metabox' ),
348
- 'sort' => 20,
349
- 'priority' => 'high'
350
- ),
351
- array(
352
- 'id' => 'ta-inserted-link-scanner-metabox',
353
- 'title' => __( 'Link Inserted Scanner', 'thirstyaffiliates' ),
354
- 'cb' => array( $this , 'inserted_link_scanner_metabox' ),
355
- 'sort' => 99,
356
- 'priority' => 'low'
357
- ),
358
- ) );
359
-
360
- $side_metaboxes = apply_filters( 'ta_register_side_metaboxes' , array(
361
- array(
362
- 'id' => 'ta-save-affiliate-link-metabox-side',
363
- 'title' => __( 'Save Affiliate Link', 'thirstyaffiliates' ),
364
- 'cb' => array( $this , 'save_affiliate_link_metabox' ),
365
- 'sort' => 10,
366
- 'priority' => 'high'
367
- ),
368
- array(
369
- 'id' => 'ta-link-options-metabox',
370
- 'title' => __( 'Link Options', 'thirstyaffiliates' ),
371
- 'cb' => array( $this , 'link_options_metabox' ),
372
- 'sort' => 30,
373
- 'priority' => 'default'
374
- ),
375
- ) );
376
-
377
- // sort metaboxes by priority.
378
- $this->sort_metaboxes( $normal_metaboxes );
379
- $this->sort_metaboxes( $side_metaboxes );
380
-
381
- // register normal metaboxes.
382
- foreach ( $normal_metaboxes as $metabox )
383
- add_meta_box( $metabox[ 'id' ] , $metabox[ 'title' ] , $metabox[ 'cb' ] , Plugin_Constants::AFFILIATE_LINKS_CPT , 'normal' , $metabox[ 'priority' ] );
384
-
385
- // register side metaboxes.
386
- foreach ( $side_metaboxes as $metabox )
387
- add_meta_box( $metabox[ 'id' ] , $metabox[ 'title' ] , $metabox[ 'cb' ] , Plugin_Constants::AFFILIATE_LINKS_CPT , 'side' , $metabox[ 'priority' ] );
388
-
389
- // remove unnecessary metaboxes
390
- remove_meta_box( 'submitdiv', Plugin_Constants::AFFILIATE_LINKS_CPT , 'side' );
391
- remove_meta_box( 'postcustom' , Plugin_Constants::AFFILIATE_LINKS_CPT , 'normal' );
392
- }
393
-
394
- /**
395
- * Function to sort registered metaboxes by priority.
396
- *
397
- * @since 3.2.1
398
- * @access private
399
- *
400
- * @param array $metaboxes Metaboxes list to sort.
401
- */
402
- private function sort_metaboxes( &$metaboxes ) {
403
-
404
- usort( $metaboxes , function( $a , $b ) {
405
- if ( $a[ 'sort' ] == $b[ 'sort' ] ) return 0;
406
- return ( $a[ 'sort' ] > $b[ 'sort' ] ) ? 1 : -1;
407
- } );
408
- }
409
-
410
- /**
411
- * Display "URls" metabox
412
- *
413
- * @since 3.0.0
414
- * @access public
415
- *
416
- * @param WP_Post $post Affiliate link WP_Post object.
417
- */
418
- public function urls_metabox( $post ) {
419
-
420
- $screen = get_current_screen();
421
- $thirstylink = $this->get_thirstylink_post( $post->ID );
422
- $home_link_prefix = home_url( user_trailingslashit( $this->_helper_functions->get_thirstylink_link_prefix() ) );
423
- $default_cat_slug = $this->_helper_functions->get_default_category_slug( $post->ID );
424
-
425
- include_once( $this->_constants->VIEWS_ROOT_PATH() . 'cpt/view-urls-metabox.php' );
426
-
427
- }
428
-
429
- /**
430
- * Display "Attach Images" metabox
431
- *
432
- * @since 3.0.0
433
- * @since 3.4.0 Add support for external images.
434
- * @access public
435
- *
436
- * @param WP_Post $post Affiliate link WP_Post object.
437
- */
438
- public function attach_images_metabox( $post ) {
439
-
440
- $thirstylink = $this->get_thirstylink_post( $post->ID );
441
- $legacy_uploader = get_option( 'ta_legacy_uploader', 'no' );
442
- $attachments = $thirstylink->get_prop( 'image_ids' );
443
-
444
- include_once( $this->_constants->VIEWS_ROOT_PATH() . 'cpt/view-attach-images-metabox.php' );
445
-
446
- }
447
-
448
- /**
449
- * Display link options metabox
450
- *
451
- * @since 3.0.0
452
- * @since 3.4.0 Add global option for redirect type. Add additional CSS classes field.
453
- * @access public
454
- *
455
- * @param WP_Post $post Affiliate link WP_Post object.
456
- */
457
- public function link_options_metabox( $post ) {
458
-
459
- $thirstylink = $this->get_thirstylink_post( $post->ID );
460
- $default_redirect_type = $this->_helper_functions->get_option( 'ta_link_redirect_type' , '302' );
461
- $post_redirect_type = $thirstylink->get_prop( 'redirect_type' );
462
- $redirect_types = $this->_constants->REDIRECT_TYPES();
463
- $global_no_follow = $thirstylink->get_toggle_prop_global_value( 'no_follow' );
464
- $global_new_window = $thirstylink->get_toggle_prop_global_value( 'new_window' );
465
- $global_pass_query_str = $thirstylink->get_toggle_prop_global_value( 'pass_query_str' );
466
- $global_uncloak = $thirstylink->get_toggle_prop_global_value( 'uncloak_link' );
467
- $rel_tags = get_post_meta( $post->ID , Plugin_Constants::META_DATA_PREFIX . 'rel_tags' , true );
468
- $css_classes = get_post_meta( $post->ID , Plugin_Constants::META_DATA_PREFIX . 'css_classes' , true );
469
- $global_rel_tags = get_option( 'ta_additional_rel_tags' );
470
- $global_css_classes = get_option( 'ta_additional_css_classes' );
471
-
472
- include_once( $this->_constants->VIEWS_ROOT_PATH() . 'cpt/view-link-options-metabox.php' );
473
-
474
- }
475
-
476
- /**
477
- * Display "Save Affiliate Link" metabox
478
- *
479
- * @since 3.0.0
480
- * @access public
481
- *
482
- * @param WP_Post $post Affiliate link WP_Post object.
483
- */
484
- public function save_affiliate_link_metabox( $post ) {
485
-
486
- include( $this->_constants->VIEWS_ROOT_PATH() . 'cpt/view-save-affiliate-link-metabox.php' );
487
-
488
- }
489
-
490
- /**
491
- * Display inserted link scanner metabox
492
- *
493
- * @since 3.2.0
494
- * @access public
495
- *
496
- * @param WP_Post $post Affiliate link WP_Post object.
497
- */
498
- public function inserted_link_scanner_metabox( $post ) {
499
-
500
- $thirstylink = $this->get_thirstylink_post( $post->ID );
501
- $spinner_image_src = $this->_constants->IMAGES_ROOT_URL() . 'spinner-2x.gif';
502
- $inserted_to = $thirstylink->get_prop( 'inserted_to' );
503
- $timezone = new \DateTimeZone( $this->_helper_functions->get_site_current_timezone() );
504
- $last_scanned = \DateTime::createFromFormat( 'Y-m-d G:i:s' , $thirstylink->get_prop( 'scanned_inserted' ) );
505
- $not_yet_scanned = __( 'Not yet scanned' , 'thirstyaffiliates' );
506
-
507
- if ( $last_scanned )
508
- $last_scanned->setTimezone( $timezone );
509
-
510
- $last_scanned_txt = $last_scanned !== false ? __( 'Last scanned on:' , 'thirstyaffiliates' ) . ' <em>' . $last_scanned->format( 'F j, Y g:ia' ) . '</em>' : $not_yet_scanned;
511
- $inserted_into_rows_html = $this->get_inserted_into_rows_html( $inserted_to );
512
-
513
- include( $this->_constants->VIEWS_ROOT_PATH() . 'cpt/view-inserted-link-scanner-metabox.php' );
514
- }
515
-
516
- /**
517
- * Get the HTML for inserted into table rows.
518
- *
519
- * @since 3.2.0
520
- * @access private
521
- *
522
- * @param array $inserted_to WP_Post IDs where affiliate link is inserte to.
523
- * @param Affiliate_Link $thirstylink Affiliate link object.
524
- */
525
- private function get_inserted_into_rows_html( $inserted_to ) {
526
-
527
- global $wpdb;
528
-
529
- if ( ! is_array( $inserted_to ) || empty( $inserted_to ) )
530
- return '<tr><td colspan="4">' . __( 'No results found.' , 'thirstyaffiliates' ) . '</td></tr>';
531
-
532
- $inserted_to_str = implode( ',' , $inserted_to );
533
- $results = $wpdb->get_results( "SELECT ID , post_title , post_type FROM $wpdb->posts WHERE ID IN ( $inserted_to_str )" );
534
-
535
- ob_start();
536
- foreach ( $results as $object ) : ?>
537
- <tr>
538
- <td class="id"><?php echo esc_html( $object->ID ); ?></td>
539
- <td class="title"><?php echo mb_strimwidth( esc_html( $object->post_title ) , 0 , 60 , "..." ); ?></td>
540
- <td class="post-type"><?php echo esc_html( $object->post_type ); ?></td>
541
- <td class="actions">
542
- <a class="view" href="<?php echo get_permalink( $object->ID ); ?>" target="_blank"><span class="dashicons dashicons-admin-links"></span></a>
543
- <a class="edit" href="<?php echo get_edit_post_link( $object->ID ); ?>" target="_blank"><span class="dashicons dashicons-edit"></span></a>
544
- </td>
545
- </tr>
546
- <?php endforeach;
547
- return ob_get_clean();
548
- }
549
-
550
-
551
-
552
-
553
- /*
554
- |--------------------------------------------------------------------------
555
- | Save Functions
556
- |--------------------------------------------------------------------------
557
- */
558
-
559
- /**
560
- * Save thirstylink post.
561
- *
562
- * @since 3.0.0
563
- * @since 3.2.2 Make sure post name (slug) is updated.
564
- * @since 3.4.0 Add css_classes to the saved properties.
565
- * @access public
566
- *
567
- * @param int $post_id Affiliate link post ID.
568
- */
569
- public function save_post( $post_id ) {
570
-
571
- if ( ! isset( $_POST[ '_thirstyaffiliates_nonce' ] ) || ! wp_verify_nonce( $_POST['_thirstyaffiliates_nonce'], 'thirsty_affiliates_cpt_nonce' ) )
572
- return;
573
-
574
- // remove save_post hooked action to prevent infinite loop
575
- remove_action( 'save_post' , array( $this , 'save_post' ) );
576
-
577
- $thirstylink = $this->get_thirstylink_post( $post_id );
578
-
579
- // set Properties
580
- $thirstylink->set_prop( 'destination_url' , esc_url_raw( $_POST[ 'ta_destination_url' ] ) );
581
- $thirstylink->set_prop( 'no_follow' , sanitize_text_field( $_POST[ 'ta_no_follow' ] ) );
582
- $thirstylink->set_prop( 'new_window' , sanitize_text_field( $_POST[ 'ta_new_window' ] ) );
583
- $thirstylink->set_prop( 'pass_query_str' , sanitize_text_field( $_POST[ 'ta_pass_query_str' ] ) );
584
- $thirstylink->set_prop( 'redirect_type' , sanitize_text_field( $_POST[ 'ta_redirect_type' ] ) );
585
- $thirstylink->set_prop( 'rel_tags' , sanitize_text_field( $_POST[ 'ta_rel_tags' ] ) );
586
- $thirstylink->set_prop( 'css_classes' , sanitize_text_field( $_POST[ 'ta_css_classes' ] ) );
587
-
588
- if ( isset( $_POST[ 'post_name' ] ) )
589
- $thirstylink->set_prop( 'slug' , sanitize_text_field( $_POST[ 'post_name' ] ) );
590
-
591
- if ( isset( $_POST[ 'ta_uncloak_link' ] ) )
592
- $thirstylink->set_prop( 'uncloak_link' , sanitize_text_field( $_POST[ 'ta_uncloak_link' ] ) );
593
-
594
- if ( isset( $_POST[ 'ta_category_slug' ] ) && $_POST[ 'ta_category_slug' ] ) {
595
-
596
- $category_slug_id = (int) sanitize_text_field( $_POST[ 'ta_category_slug' ] );
597
- $category_slug = get_term( $category_slug_id , Plugin_Constants::AFFILIATE_LINKS_TAX );
598
- $thirstylink->set_prop( 'category_slug_id' , $category_slug_id );
599
- $thirstylink->set_prop( 'category_slug' , $category_slug->slug );
600
-
601
- } else {
602
-
603
- $thirstylink->set_prop( 'category_slug_id' , 0 );
604
- $thirstylink->set_prop( 'category_slug' , '' );
605
- }
606
-
607
- do_action( 'ta_save_affiliate_link_post' , $thirstylink , $post_id );
608
-
609
- // save affiliate link
610
- $thirstylink->save();
611
-
612
- // set default term
613
- $this->_helper_functions->save_default_affiliate_link_category( $post_id );
614
-
615
- // add back save_post hooked action after saving
616
- add_action( 'save_post' , array( $this , 'save_post' ) );
617
-
618
- do_action( 'ta_after_save_affiliate_link_post' , $post_id , $thirstylink );
619
- }
620
-
621
- /**
622
- * Set default term when affiliate link is saved.
623
- *
624
- * @deprecated 3.2.0 Moved to helper functions
625
- *
626
- * @since 3.0.0
627
- * @access public
628
- *
629
- * @param int $post_id Affiliate link post ID.
630
- */
631
- public function save_default_affiliate_link_category( $post_id ) {
632
-
633
- $this->_helper_functions->save_default_affiliate_link_category( $post_id );
634
- }
635
-
636
-
637
-
638
-
639
- /*
640
- |--------------------------------------------------------------------------
641
- | Affiliate Link listing related UI functions
642
- |--------------------------------------------------------------------------
643
- */
644
-
645
- /**
646
- * Add custom column to thirsty link listings (Link ID).
647
- *
648
- * @since 3.0.0
649
- * @access public
650
- *
651
- * @param array $columns Post type listing columns.
652
- * @return array Filtered post type listing columns.
653
- */
654
- public function custom_post_listing_column( $columns ) {
655
-
656
- $updated_columns = array();
657
-
658
- foreach ( $columns as $key => $column ) {
659
-
660
- // add link_id and link_destination column before link categories column
661
- if ( $key == 'taxonomy-thirstylink-category' ) {
662
-
663
- $updated_columns[ 'link_id' ] = __( 'Link ID' , 'thirstyaffiliates' );
664
- $updated_columns[ 'redirect_type' ] = __( 'Redirect Type' , 'thirstyaffiliates' );
665
- $updated_columns[ 'cloaked_url' ] = __( 'Cloaked URL' , 'thirstyaffiliates' );
666
- $updated_columns[ 'link_destination' ] = __( 'Link Destination' , 'thirstyaffiliates' );
667
- }
668
-
669
-
670
- $updated_columns[ $key ] = $column;
671
- }
672
-
673
- return apply_filters( 'ta_post_listing_custom_columns' , $updated_columns );
674
-
675
- }
676
-
677
- /**
678
- * Add custom column to thirsty link listings (Link ID).
679
- *
680
- * @since 3.2.0
681
- * @access public
682
- *
683
- * @param array $columns Post type listing sortable columns.
684
- * @return array Filtered Post type listing sortable columns.
685
- */
686
- public function custom_post_listing_sortable_column( $columns ) {
687
-
688
- $columns[ 'link_id' ] = 'ID';
689
- $columns[ 'redirect_type' ] = '_ta_redirect_type';
690
- $columns[ 'cloaked_url' ] = 'name';
691
- $columns[ 'link_destination' ] = '_ta_destination_url';
692
-
693
- return apply_filters( 'ta_post_listing_sortable_custom_columns' , $columns );
694
- }
695
-
696
- /**
697
- * Add custom sorting support for TA custom columns with post meta values.
698
- *
699
- * @since 3.2.0
700
- * @access public
701
- *
702
- * @param WP_Query $query Main WP_Query instance of the page.
703
- */
704
- public function custom_post_listing_column_custom_sorting( $query ) {
705
-
706
- $post_type = get_post_type();
707
- if ( !$post_type && isset( $_GET[ 'post_type' ] ) )
708
- $post_type = $_GET[ 'post_type' ];
709
-
710
- if ( ! is_admin() || $post_type !== Plugin_Constants::AFFILIATE_LINKS_CPT )
711
- return;
712
-
713
- $meta_key = $query->get( 'orderby' );
714
- $default_keys = array( '_ta_redirect_type', '_ta_destination_url' );
715
- $extend_keys = apply_filters( 'ta_post_listing_custom_sorting_keys' , array() );
716
- $meta_keys = array_unique( array_merge( $default_keys , $extend_keys ) );
717
-
718
-
719
- if ( ! in_array( $meta_key , $meta_keys ) )
720
- return;
721
-
722
- $meta_data = apply_filters( 'ta_post_listing_custom_sorting_metadata' , array(
723
- 'meta_key' => $meta_key,
724
- 'orderby' => 'meta_value',
725
- ) , $meta_key , $meta_keys );
726
-
727
- if ( ! $meta_data[ 'meta_key' ] || ! $meta_data[ 'orderby' ] )
728
- return;
729
-
730
- $query->set( 'meta_key' , $meta_key );
731
- }
732
-
733
- /**
734
- * Add custom column to thirsty link listings (Link ID).
735
- *
736
- * @since 3.0.0
737
- * @since 3.4.0 Update display for redirect type value.
738
- * @access public
739
- *
740
- * @param string $column Current column name.
741
- * @param int $post_id Thirstylink ID.
742
- * @return array
743
- */
744
- public function custom_post_listing_column_value( $column , $post_id ) {
745
-
746
- $thirstylink = $this->get_thirstylink_post( $post_id );
747
- $edit_link = get_edit_post_link( $post_id );
748
- $redirect_type = $thirstylink->get_prop( 'redirect_type' );
749
-
750
- switch ( $column ) {
751
-
752
- case 'link_id' :
753
- echo '<span>' . $post_id . '</span>';
754
- break;
755
-
756
- case 'redirect_type' :
757
- echo $redirect_type;
758
- echo $redirect_type === 'global' ? ' (' . $this->_helper_functions->get_option( 'ta_link_redirect_type' , '302' ) . ')' : '';
759
- break;
760
-
761
- case 'cloaked_url' :
762
- echo '<div class="ta-display-input-wrap">';
763
- echo '<input style="width:100%;" type="text" value="' . $thirstylink->get_prop( 'permalink' ) . '" readonly>';
764
- echo '<a href="' . $edit_link . '"><span class="dashicons dashicons-edit"></span></a>';
765
- echo '</div>';
766
- break;
767
-
768
- case 'link_destination' :
769
- echo '<div class="ta-display-input-wrap">';
770
- echo '<input style="width:100%;" type="text" value="' . $thirstylink->get_prop( 'destination_url' ) . '" readonly>';
771
- echo '<a href="' . $edit_link . '"><span class="dashicons dashicons-edit"></span></a>';
772
- echo '</div>';
773
- break;
774
-
775
- }
776
-
777
- do_action( 'ta_post_listing_custom_columns_value' , $column , $thirstylink );
778
-
779
- }
780
-
781
- /**
782
- * Setup the filter box for the list page so users can filter links by category
783
- *
784
- * @since 3.2.0
785
- * @access public
786
- *
787
- * @global string $typenow Current post type context.
788
- * @global WP_Query $wp_query Object that contains the main query of WP.
789
- */
790
- public function restrict_links_by_category() {
791
-
792
- global $typenow , $wp_query;
793
-
794
- if ( $typenow != Plugin_Constants::AFFILIATE_LINKS_CPT )
795
- return;
796
-
797
- $taxonomy = Plugin_Constants::AFFILIATE_LINKS_TAX;
798
-
799
- wp_dropdown_categories( array(
800
- 'show_option_all' => __( 'Show Link Categories' , 'thirstyaffiliates' ),
801
- 'taxonomy' => $taxonomy,
802
- 'name' => $taxonomy,
803
- 'orderby' => 'name',
804
- 'selected' => ( isset( $wp_query->query[ $taxonomy ] ) ? $wp_query->query[ $taxonomy ] : '' ),
805
- 'hierarchical' => true,
806
- 'depth' => 4,
807
- 'show_count' => true,
808
- 'hide_empty' => true
809
- ) );
810
- }
811
-
812
- /**
813
- * Convert category ID to slug to make the filter by category dropdown work.
814
- *
815
- * @since 3.2.0
816
- * @access public
817
- *
818
- * @global string $typenow Current post type context.
819
- *
820
- * @param WP_Query $query Current page query.
821
- * @return WP_Query Filtered current page query.
822
- */
823
- public function convert_cat_id_to_slug_in_query( $query ) {
824
-
825
- global $typenow;
826
-
827
- if ( $typenow != Plugin_Constants::AFFILIATE_LINKS_CPT )
828
- return;
829
-
830
- $qv = &$query->query_vars;
831
- $taxonomy = Plugin_Constants::AFFILIATE_LINKS_TAX;
832
-
833
- if ( isset( $qv[ $taxonomy ] ) && is_numeric( $qv[ $taxonomy ] ) ) {
834
-
835
- $term = get_term_by( 'id' , $qv[ $taxonomy ] , $taxonomy );
836
- $qv[ $taxonomy ] = is_object( $term ) ? $term->slug : '';
837
-
838
- }
839
-
840
- return $query;
841
- }
842
-
843
-
844
-
845
-
846
- /*
847
- |--------------------------------------------------------------------------
848
- | Affiliate Link Filters
849
- |--------------------------------------------------------------------------
850
- */
851
-
852
- /**
853
- * Add category slug to the permalink.
854
- *
855
- * @since 3.0.0
856
- * @access public
857
- *
858
- * @param string $post_link Thirstylink permalink.
859
- * @param WP_Post $post Thirstylink WP_Post object.
860
- * @return string Thirstylink permalink.
861
- */
862
- public function add_category_slug_to_permalink( $post_link , $post ) {
863
-
864
- $link_prefix = $this->_helper_functions->get_thirstylink_link_prefix();
865
-
866
- if ( get_option( 'ta_show_cat_in_slug' ) !== 'yes' || is_wp_error( $post ) || $post->post_type != 'thirstylink' )
867
- return $post_link;
868
-
869
- $link_cat_id = get_post_meta( $post->ID , '_ta_category_slug_id' , true );
870
- $link_cat = get_post_meta( $post->ID , '_ta_category_slug' , true );
871
-
872
- if ( ! $link_cat && $link_cat_id ) {
873
-
874
- $link_cat_obj = get_term( $link_cat_id , Plugin_Constants::AFFILIATE_LINKS_TAX );
875
- $link_cat = $link_cat_obj->slug;
876
-
877
- } elseif ( ! $link_cat && ! $link_cat_id ) {
878
-
879
- $link_cat = $this->_helper_functions->get_default_category_slug( $post->ID );
880
- }
881
-
882
- if ( ! $link_cat )
883
- return $post_link;
884
-
885
- return home_url( user_trailingslashit( $link_prefix . '/' . $link_cat . '/' . $post->post_name ) );
886
- }
887
-
888
-
889
-
890
-
891
- /*
892
- |--------------------------------------------------------------------------
893
- | AJAX functions.
894
- |--------------------------------------------------------------------------
895
- */
896
-
897
- /**
898
- * Ajax get category slug.
899
- *
900
- * @since 3.0.0
901
- * @access public
902
- */
903
- public function ajax_get_category_slug() {
904
-
905
- if ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX )
906
- $response = array( 'status' => 'fail' , 'error_msg' => __( 'Invalid AJAX call' , 'thirstyaffiliates' ) );
907
- elseif ( ! isset( $_POST[ 'term_id' ] ) )
908
- $response = array( 'status' => 'fail' , 'error_msg' => __( 'Missing required post data' , 'thirstyaffiliates' ) );
909
- else {
910
-
911
- $link_cat_id = (int) sanitize_text_field( $_POST[ 'term_id' ] );
912
- $category = get_term( $link_cat_id , Plugin_Constants::AFFILIATE_LINKS_TAX );
913
-
914
- $response = array( 'status' => 'success' , 'category_slug' => $category->slug );
915
- }
916
-
917
- @header( 'Content-Type: application/json; charset=' . get_option( 'blog_charset' ) );
918
- echo wp_json_encode( $response );
919
- wp_die();
920
- }
921
-
922
- /**
923
- * Ajax link inserted scanner.
924
- *
925
- * @since 3.2.0
926
- * @access public
927
- */
928
- public function ajax_link_inserted_scanner() {
929
-
930
- if ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX )
931
- $response = array( 'status' => 'fail' , 'error_msg' => __( 'Invalid AJAX call' , 'thirstyaffiliates' ) );
932
- elseif ( ! isset( $_POST[ 'link_id' ] ) )
933
- $response = array( 'status' => 'fail' , 'error_msg' => __( 'Missing required post data' , 'thirstyaffiliates' ) );
934
- else {
935
-
936
- $link_id = (int) sanitize_text_field( $_POST[ 'link_id' ] );
937
- $thirstylink = $this->get_thirstylink_post( $link_id );
938
- $inserted_to = $thirstylink->scan_where_links_inserted();
939
- $timezone = new \DateTimeZone( $this->_helper_functions->get_site_current_timezone() );
940
- $last_scanned = \DateTime::createFromFormat( 'Y-m-d G:i:s' , get_post_meta( $link_id , Plugin_Constants::META_DATA_PREFIX . 'scanned_inserted' , true ) , $timezone );
941
-
942
- $response = array(
943
- 'status' => 'success',
944
- 'results_markup' => $this->get_inserted_into_rows_html( $inserted_to ),
945
- 'last_scanned' => __( 'Last scanned on:' , 'thirstyaffiliates' ) . ' <em>' . $last_scanned->format( 'F j, Y g:ia' ) . '</em>'
946
- );
947
- }
948
-
949
- @header( 'Content-Type: application/json; charset=' . get_option( 'blog_charset' ) );
950
- echo wp_json_encode( $response );
951
- wp_die();
952
- }
953
-
954
-
955
-
956
-
957
- /*
958
- |--------------------------------------------------------------------------
959
- | Fulfill implemented interface contracts
960
- |--------------------------------------------------------------------------
961
- */
962
-
963
- /**
964
- * Method that houses codes to be executed on init hook.
965
- *
966
- * @since 3.0.0
967
- * @access public
968
- * @inherit ThirstyAffiliates\Interfaces\Initiable_Interface
969
- */
970
- public function initialize() {
971
-
972
- // cpt and taxonomy
973
- $this->register_thirstylink_custom_post_type();
974
- $this->register_thirstylink_category_custom_taxonomy();
975
-
976
- add_action( 'wp_ajax_ta_get_category_slug' , array( $this , 'ajax_get_category_slug' ) );
977
- add_action( 'wp_ajax_ta_link_inserted_scanner' , array( $this , 'ajax_link_inserted_scanner' ) );
978
- }
979
-
980
- /**
981
- * Execute 'thirstylink' custom post type code.
982
- *
983
- * @since 3.0.0
984
- * @access public
985
- * @inherit ThirstyAffiliates\Interfaces\Model_Interface
986
- */
987
- public function run() {
988
-
989
- // replace permalink with link ID
990
- add_filter( 'get_sample_permalink_html', array( $this , 'replace_permalink_with_id' ), 10 , 2 );
991
-
992
- // metaboxes
993
- add_action( 'add_meta_boxes' , array( $this , 'register_metaboxes' ) );
994
- add_action( 'save_post' , array( $this , 'save_post' ) );
995
-
996
- // custom column
997
- add_filter( 'manage_edit-thirstylink_columns' , array( $this , 'custom_post_listing_column' ) );
998
- add_filter( 'manage_edit-thirstylink_sortable_columns', array( $this , 'custom_post_listing_sortable_column' ) );
999
- add_action( 'manage_thirstylink_posts_custom_column', array( $this , 'custom_post_listing_column_value' ) , 10 , 2 );
1000
- add_action( 'pre_get_posts' , array( $this , 'custom_post_listing_column_custom_sorting' ) );
1001
-
1002
- // filter by category
1003
- add_action( 'restrict_manage_posts' , array( $this , 'restrict_links_by_category' ) );
1004
- add_filter( 'parse_query' , array( $this , 'convert_cat_id_to_slug_in_query' ) );
1005
-
1006
- // filter to add category on permalink
1007
- add_filter( 'post_type_link' , array( $this , 'add_category_slug_to_permalink' ) , 10 , 2 );
1008
-
1009
- // Register admin interface and menus.
1010
- add_filter( 'ta_admin_interfaces' , array( $this , 'register_admin_interfaces' ) );
1011
- add_filter( 'ta_menu_items' , array( $this , 'register_admin_menu_items' ) );
1012
- }
1013
-
1014
- }
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace ThirstyAffiliates\Models;
4
+
5
+ use ThirstyAffiliates\Abstracts\Abstract_Main_Plugin_Class;
6
+
7
+ use ThirstyAffiliates\Interfaces\Model_Interface;
8
+ use ThirstyAffiliates\Interfaces\Initiable_Interface;
9
+
10
+ use ThirstyAffiliates\Helpers\Plugin_Constants;
11
+ use ThirstyAffiliates\Helpers\Helper_Functions;
12
+
13
+ // Data Models
14
+ use ThirstyAffiliates\Models\Affiliate_Link;
15
+
16
+ /**
17
+ * Model that houses the logic of registering the 'thirstylink' custom post type.
18
+ *
19
+ * @since 3.0.0
20
+ */
21
+ class Affiliate_Links_CPT implements Model_Interface , Initiable_Interface {
22
+
23
+ /*
24
+ |--------------------------------------------------------------------------
25
+ | Class Properties
26
+ |--------------------------------------------------------------------------
27
+ */
28
+
29
+ /**
30
+ * Property that holds the single main instance of Bootstrap.
31
+ *
32
+ * @since 3.0.0
33
+ * @access private
34
+ * @var Affiliate_Links_CPT
35
+ */
36
+ private static $_instance;
37
+
38
+ /**
39
+ * Model that houses the main plugin object.
40
+ *
41
+ * @since 3.0.0
42
+ * @access private
43
+ * @var Abstract_Main_Plugin_Class
44
+ */
45
+ private $_main_plugin;
46
+
47
+ /**
48
+ * Model that houses all the plugin constants.
49
+ *
50
+ * @since 3.0.0
51
+ * @access private
52
+ * @var Plugin_Constants
53
+ */
54
+ private $_constants;
55
+
56
+ /**
57
+ * Property that houses all the helper functions of the plugin.
58
+ *
59
+ * @since 3.0.0
60
+ * @access private
61
+ * @var Helper_Functions
62
+ */
63
+ private $_helper_functions;
64
+
65
+ /**
66
+ * Property that holds the currently loaded thirstylink post.
67
+ *
68
+ * @since 3.0.0
69
+ * @access private
70
+ */
71
+ private $_thirstylink;
72
+
73
+
74
+
75
+
76
+ /*
77
+ |--------------------------------------------------------------------------
78
+ | Class Methods
79
+ |--------------------------------------------------------------------------
80
+ */
81
+
82
+ /**
83
+ * Class constructor.
84
+ *
85
+ * @since 3.0.0
86
+ * @access public
87
+ *
88
+ * @param Abstract_Main_Plugin_Class $main_plugin Main plugin object.
89
+ * @param Plugin_Constants $constants Plugin constants object.
90
+ * @param Helper_Functions $helper_functions Helper functions object.
91
+ */
92
+ public function __construct( Abstract_Main_Plugin_Class $main_plugin , Plugin_Constants $constants , Helper_Functions $helper_functions ) {
93
+
94
+ $this->_constants = $constants;
95
+ $this->_helper_functions = $helper_functions;
96
+
97
+ $main_plugin->add_to_all_plugin_models( $this );
98
+
99
+ }
100
+
101
+ /**
102
+ * Ensure that only one instance of this class is loaded or can be loaded ( Singleton Pattern ).
103
+ *
104
+ * @since 3.0.0
105
+ * @access public
106
+ *
107
+ * @param Abstract_Main_Plugin_Class $main_plugin Main plugin object.
108
+ * @param Plugin_Constants $constants Plugin constants object.
109
+ * @param Helper_Functions $helper_functions Helper functions object.
110
+ * @return Affiliate_Links_CPT
111
+ */
112
+ public static function get_instance( Abstract_Main_Plugin_Class $main_plugin , Plugin_Constants $constants , Helper_Functions $helper_functions ) {
113
+
114
+ if ( !self::$_instance instanceof self )
115
+ self::$_instance = new self( $main_plugin , $constants , $helper_functions );
116
+
117
+ return self::$_instance;
118
+
119
+ }
120
+
121
+ /**
122
+ * Register admin interfaces.
123
+ *
124
+ * @since 3.3.3
125
+ * @access public
126
+ *
127
+ * @param array $interfaces List of admin interfaces.
128
+ * @return array Filtered list of admin interfaces.
129
+ */
130
+ public function register_admin_interfaces( $interfaces ) {
131
+
132
+ $interfaces[ 'edit-thirstylink' ] = 'edit_posts';
133
+ $interfaces[ 'thirstylink' ] = 'edit_posts';
134
+ $interfaces[ 'edit-thirstylink-category' ] = 'manage_categories';
135
+
136
+ return $interfaces;
137
+ }
138
+
139
+ /**
140
+ * Register admin interfaces.
141
+ *
142
+ * @since 3.3.3
143
+ * @access public
144
+ *
145
+ * @param array $interfaces List of menu items.
146
+ * @return array Filtered list of menu items.
147
+ */
148
+ public function register_admin_menu_items( $menu_items ) {
149
+
150
+ $list_slug = 'edit.php?post_type=' . Plugin_Constants::AFFILIATE_LINKS_CPT;
151
+ $new_post_slug = 'post-new.php?post_type=' . Plugin_Constants::AFFILIATE_LINKS_CPT;
152
+ $link_cat_slug = 'edit-tags.php?taxonomy=' . Plugin_Constants::AFFILIATE_LINKS_TAX . '&post_type=' . Plugin_Constants::AFFILIATE_LINKS_CPT;
153
+ $menu_items[ $list_slug ] = 'edit_posts';
154
+ $menu_items[ $new_post_slug ] = 'edit_posts';
155
+ $menu_items[ $link_cat_slug ] = 'manage_categories';
156
+
157
+ return $menu_items;
158
+ }
159
+
160
+ /**
161
+ * Get thirstylink Affiliate_Link object.
162
+ *
163
+ * @since 3.0.0
164
+ * @access private
165
+ *
166
+ * @param int $post_id Thirstylink post id.
167
+ * @return Affiliate_Link object.
168
+ */
169
+ private function get_thirstylink_post( $post_id ) {
170
+
171
+ if ( is_object( $this->_thirstylink ) && $this->_thirstylink->get_id() == $post_id )
172
+ return $this->_thirstylink;
173
+
174
+ return $this->_thirstylink = new Affiliate_Link( $post_id );
175
+
176
+ }
177
+
178
+
179
+
180
+
181
+ /*
182
+ |--------------------------------------------------------------------------
183
+ | Register Post Type and Taxonomy
184
+ |--------------------------------------------------------------------------
185
+ */
186
+
187
+ /**
188
+ * Register the 'thirstylink' custom post type.
189
+ *
190
+ * @since 3.0.0
191
+ * @since 3.3.2 Set manage_terms capability to read so we can control visibility natively. see Bootstrap::admin_interface_visibility.
192
+ * @access private
193
+ */
194
+ private function register_thirstylink_custom_post_type() {
195
+
196
+ $link_prefix = $this->_helper_functions->get_thirstylink_link_prefix();
197
+
198
+ $labels = array(
199
+ 'name' => __( 'Affiliate Links' , 'thirstyaffiliates' ),
200
+ 'singular_name' => __( 'Affiliate Link' , 'thirstyaffiliates' ),
201
+ 'menu_name' => __( 'ThirstyAffiliates' , 'thirstyaffiliates' ),
202
+ 'parent_item_colon' => __( 'Parent Affiliate Link' , 'thirstyaffiliates' ),
203
+ 'all_items' => __( 'Affiliate Links' , 'thirstyaffiliates' ),
204
+ 'view_item' => __( 'View Affiliate Link' , 'thirstyaffiliates' ),
205
+ 'add_new_item' => __( 'Add Affiliate Link' , 'thirstyaffiliates' ),
206
+ 'add_new' => __( 'New Affiliate Link' , 'thirstyaffiliates' ),
207
+ 'edit_item' => __( 'Edit Affiliate Link' , 'thirstyaffiliates' ),
208
+ 'update_item' => __( 'Update Affiliate Link' , 'thirstyaffiliates' ),
209
+ 'search_items' => __( 'Search Affiliate Links' , 'thirstyaffiliates' ),
210
+ 'not_found' => __( 'No Affiliate Link found' , 'thirstyaffiliates' ),
211
+ 'not_found_in_trash' => __( 'No Affiliate Links found in Trash' , 'thirstyaffiliates' )
212
+ );
213
+
214
+ $args = array(
215
+ 'label' => __( 'Affiliate Links' , 'thirstyaffiliates' ),
216
+ 'description' => __( 'ThirstyAffiliates affiliate links' , 'thirstyaffiliates' ),
217
+ 'labels' => $labels,
218
+ 'supports' => array( 'title' , 'custom-fields' ),
219
+ 'taxonomies' => array(),
220
+ 'hierarchical' => true,
221
+ 'public' => false,
222
+ 'show_ui' => true,
223
+ 'show_in_menu' => true,
224
+ 'show_in_json' => false,
225
+ 'query_var' => true,
226
+ 'rewrite' => array(
227
+ 'slug' => $link_prefix,
228
+ 'with_front' => false,
229
+ 'pages' => false
230
+ ),
231
+ 'show_in_nav_menus' => false,
232
+ 'show_in_admin_bar' => true,
233
+ 'menu_position' => 26,
234
+ 'menu_icon' => $this->get_menu_icon(),
235
+ 'can_export' => true,
236
+ 'has_archive' => false,
237
+ 'exclude_from_search' => true,
238
+ 'publicly_queryable' => true,
239
+ 'capability_type' => 'post',
240
+ 'show_in_rest' => true,
241
+ );
242
+
243
+ register_post_type( Plugin_Constants::AFFILIATE_LINKS_CPT , apply_filters( 'ta_affiliate_links_cpt_args' , $args , $labels ) );
244
+
245
+ do_action( 'ta_after_register_thirstylink_post_type' , $link_prefix );
246
+ }
247
+
248
+ /**
249
+ * Get the plugin menu icon.
250
+ *
251
+ * @return string
252
+ */
253
+ private function get_menu_icon() {
254
+ $icon = '<svg xmlns="http://www.w3.org/2000/svg" width="16.688" height="9.875" viewBox="0 0 16.688 9.875">
255
+ <path id="TA.svg" fill="%s" class="cls-1" d="M2.115,15.12H4.847L6.836,7.7H9.777l0.63-2.381H1.821L1.177,7.7H4.118Zm4.758,0H9.829l1.177-1.751h3.782l0.238,1.751h2.858L16.357,5.245H13.7Zm5.5-3.866,1.835-2.816,0.35,2.816H12.378Z" transform="translate(-1.188 -5.25)"/>
256
+ </svg>';
257
+
258
+ $fill = isset( $_GET['post_type'] ) && is_string( $_GET['post_type'] ) && $_GET['post_type'] == 'thirstylink' ? '#ffffff' : '#a0a5aa';
259
+
260
+ return 'data:image/svg+xml;base64,' . base64_encode( sprintf( $icon, $fill ) );
261
+ }
262
+
263
+ /**
264
+ * Register the 'thirstylink-category' custom taxonomy.
265
+ *
266
+ * @since 3.0.0
267
+ * @since 3.3.2 Set manage_terms capability to read so we can control visibility natively. see Bootstrap::admin_interface_visibility.
268
+ * @access private
269
+ */
270
+ private function register_thirstylink_category_custom_taxonomy() {
271
+
272
+ $labels = array(
273
+ 'name' => __( 'Link Categories', 'thirstyaffiliates' ),
274
+ 'singular_name' => __( 'Link Category', 'thirstyaffiliates' ),
275
+ 'menu_name' => __( 'Link Categories', 'thirstyaffiliates' ),
276
+ 'all_items' => __( 'All Categories', 'thirstyaffiliates' ),
277
+ 'parent_item' => __( 'Parent Category', 'thirstyaffiliates' ),
278
+ 'parent_item_colon' => __( 'Parent Category:', 'thirstyaffiliates' ),
279
+ 'new_item_name' => __( 'New Category Name', 'thirstyaffiliates' ),
280
+ 'add_new_item' => __( 'Add New Category', 'thirstyaffiliates' ),
281
+ 'edit_item' => __( 'Edit Category', 'thirstyaffiliates' ),
282
+ 'update_item' => __( 'Update Category', 'thirstyaffiliates' ),
283
+ 'view_item' => __( 'View Category', 'thirstyaffiliates' ),
284
+ 'separate_items_with_commas' => __( 'Separate items with commas', 'thirstyaffiliates' ),
285
+ 'add_or_remove_items' => __( 'Add or remove items', 'thirstyaffiliates' ),
286
+ 'choose_from_most_used' => __( 'Choose from the most used', 'thirstyaffiliates' ),
287
+ 'popular_items' => __( 'Popular Categories', 'thirstyaffiliates' ),
288
+ 'search_items' => __( 'Search Categories', 'thirstyaffiliates' ),
289
+ 'not_found' => __( 'Not Found', 'thirstyaffiliates' ),
290
+ 'no_terms' => __( 'No items', 'thirstyaffiliates' ),
291
+ 'items_list' => __( 'Category list', 'thirstyaffiliates' ),
292
+ 'items_list_navigation' => __( 'Category list navigation', 'thirstyaffiliates' )
293
+ );
294
+
295
+ $args = array(
296
+ 'labels' => $labels,
297
+ 'hierarchical' => true,
298
+ 'public' => false,
299
+ 'show_ui' => true,
300
+ 'show_admin_column' => true,
301
+ 'show_in_nav_menus' => false,
302
+ 'show_tagcloud' => false,
303
+ 'rewrite' => false,
304
+ 'capabilities' => array(
305
+ 'manage_terms' => 'read',
306
+ )
307
+ );
308
+
309
+ register_taxonomy( Plugin_Constants::AFFILIATE_LINKS_TAX , Plugin_Constants::AFFILIATE_LINKS_CPT , apply_filters( 'ta_affiliate_link_taxonomy_args' , $args , $labels ) );
310
+
311
+ }
312
+
313
+
314
+
315
+
316
+ /*
317
+ |--------------------------------------------------------------------------
318
+ | UI and metabox related functions
319
+ |--------------------------------------------------------------------------
320
+ */
321
+
322
+ /**
323
+ * Replace default post type permalink html with affiliate link ID.
324
+ *
325
+ * @since 3.0.0
326
+ * @access public
327
+ *
328
+ * @param string $html Permalink html.
329
+ * @param int $post_id Affiliate Link post id.
330
+ * @return string Link ID html.
331
+ */
332
+ public function replace_permalink_with_id( $html , $post_id ) {
333
+
334
+ if ( get_post_type( $post_id ) == Plugin_Constants::AFFILIATE_LINKS_CPT )
335
+ return '<span id="link_id">' . __( 'Link ID:' , 'thirstyaffiliates' ) . ' <strong>' . $post_id . '</strong></span>';
336
+
337
+ return $html;
338
+ }
339
+
340
+ /**
341
+ * Register metaboxes
342
+ *
343
+ * @since 3.0.0
344
+ * @access public
345
+ */
346
+ public function register_metaboxes() {
347
+
348
+ $normal_metaboxes = apply_filters( 'ta_register_normal_metaboxes' , array(
349
+ array(
350
+ 'id' => 'ta-urls-metabox',
351
+ 'title' => __( 'URLs', 'thirstyaffiliates' ),
352
+ 'cb' => array( $this , 'urls_metabox' ),
353
+ 'sort' => 10,
354
+ 'priority' => 'high'
355
+ ),
356
+ array(
357
+ 'id' => 'ta-attach-images-metabox',
358
+ 'title' => __( 'Attach Images', 'thirstyaffiliates' ),
359
+ 'cb' => array( $this , 'attach_images_metabox' ),
360
+ 'sort' => 20,
361
+ 'priority' => 'high'
362
+ ),
363
+ array(
364
+ 'id' => 'ta-inserted-link-scanner-metabox',
365
+ 'title' => __( 'Link Inserted Scanner', 'thirstyaffiliates' ),
366
+ 'cb' => array( $this , 'inserted_link_scanner_metabox' ),
367
+ 'sort' => 99,
368
+ 'priority' => 'low'
369
+ ),
370
+ ) );
371
+
372
+ $side_metaboxes = apply_filters( 'ta_register_side_metaboxes' , array(
373
+ array(
374
+ 'id' => 'ta-save-affiliate-link-metabox-side',
375
+ 'title' => __( 'Save Affiliate Link', 'thirstyaffiliates' ),
376
+ 'cb' => array( $this , 'save_affiliate_link_metabox' ),
377
+ 'sort' => 10,
378
+ 'priority' => 'high'
379
+ ),
380
+ array(
381
+ 'id' => 'ta-link-options-metabox',
382
+ 'title' => __( 'Link Options', 'thirstyaffiliates' ),
383
+ 'cb' => array( $this , 'link_options_metabox' ),
384
+ 'sort' => 30,
385
+ 'priority' => 'default'
386
+ ),
387
+ ) );
388
+
389
+ // sort metaboxes by priority.
390
+ $this->sort_metaboxes( $normal_metaboxes );
391
+ $this->sort_metaboxes( $side_metaboxes );
392
+
393
+ // register normal metaboxes.
394
+ foreach ( $normal_metaboxes as $metabox )
395
+ add_meta_box( $metabox[ 'id' ] , $metabox[ 'title' ] , $metabox[ 'cb' ] , Plugin_Constants::AFFILIATE_LINKS_CPT , 'normal' , $metabox[ 'priority' ] );
396
+
397
+ // register side metaboxes.
398
+ foreach ( $side_metaboxes as $metabox )
399
+ add_meta_box( $metabox[ 'id' ] , $metabox[ 'title' ] , $metabox[ 'cb' ] , Plugin_Constants::AFFILIATE_LINKS_CPT , 'side' , $metabox[ 'priority' ] );
400
+
401
+ // remove unnecessary metaboxes
402
+ remove_meta_box( 'submitdiv', Plugin_Constants::AFFILIATE_LINKS_CPT , 'side' );
403
+ remove_meta_box( 'postcustom' , Plugin_Constants::AFFILIATE_LINKS_CPT , 'normal' );
404
+ }
405
+
406
+ /**
407
+ * Function to sort registered metaboxes by priority.
408
+ *
409
+ * @since 3.2.1
410
+ * @access private
411
+ *
412
+ * @param array $metaboxes Metaboxes list to sort.
413
+ */
414
+ private function sort_metaboxes( &$metaboxes ) {
415
+
416
+ usort( $metaboxes , function( $a , $b ) {
417
+ if ( $a[ 'sort' ] == $b[ 'sort' ] ) return 0;
418
+ return ( $a[ 'sort' ] > $b[ 'sort' ] ) ? 1 : -1;
419
+ } );
420
+ }
421
+
422
+ /**
423
+ * Display "URls" metabox
424
+ *
425
+ * @since 3.0.0
426
+ * @access public
427
+ *
428
+ * @param WP_Post $post Affiliate link WP_Post object.
429
+ */
430
+ public function urls_metabox( $post ) {
431
+
432
+ $screen = get_current_screen();
433
+ $thirstylink = $this->get_thirstylink_post( $post->ID );
434
+ $home_link_prefix = home_url( user_trailingslashit( $this->_helper_functions->get_thirstylink_link_prefix() ) );
435
+ $default_cat_slug = $this->_helper_functions->get_default_category_slug( $post->ID );
436
+
437
+ include_once( $this->_constants->VIEWS_ROOT_PATH() . 'cpt/view-urls-metabox.php' );
438
+
439
+ }
440
+
441
+ /**
442
+ * Display "Attach Images" metabox
443
+ *
444
+ * @since 3.0.0
445
+ * @since 3.4.0 Add support for external images.
446
+ * @access public
447
+ *
448
+ * @param WP_Post $post Affiliate link WP_Post object.
449
+ */
450
+ public function attach_images_metabox( $post ) {
451
+
452
+ $thirstylink = $this->get_thirstylink_post( $post->ID );
453
+ $legacy_uploader = get_option( 'ta_legacy_uploader', 'no' );
454
+ $attachments = $thirstylink->get_prop( 'image_ids' );
455
+
456
+ include_once( $this->_constants->VIEWS_ROOT_PATH() . 'cpt/view-attach-images-metabox.php' );
457
+
458
+ }
459
+
460
+ /**
461
+ * Display link options metabox
462
+ *
463
+ * @since 3.0.0
464
+ * @since 3.4.0 Add global option for redirect type. Add additional CSS classes field.
465
+ * @access public
466
+ *
467
+ * @param WP_Post $post Affiliate link WP_Post object.
468
+ */
469
+ public function link_options_metabox( $post ) {
470
+
471
+ $thirstylink = $this->get_thirstylink_post( $post->ID );
472
+ $default_redirect_type = $this->_helper_functions->get_option( 'ta_link_redirect_type' , '302' );
473
+ $post_redirect_type = $thirstylink->get_prop( 'redirect_type' );
474
+ $redirect_types = $this->_constants->REDIRECT_TYPES();
475
+ $global_no_follow = $thirstylink->get_toggle_prop_global_value( 'no_follow' );
476
+ $global_new_window = $thirstylink->get_toggle_prop_global_value( 'new_window' );
477
+ $global_pass_query_str = $thirstylink->get_toggle_prop_global_value( 'pass_query_str' );
478
+ $global_uncloak = $thirstylink->get_toggle_prop_global_value( 'uncloak_link' );
479
+ $rel_tags = get_post_meta( $post->ID , Plugin_Constants::META_DATA_PREFIX . 'rel_tags' , true );
480
+ $css_classes = get_post_meta( $post->ID , Plugin_Constants::META_DATA_PREFIX . 'css_classes' , true );
481
+ $global_rel_tags = get_option( 'ta_additional_rel_tags' );
482
+ $global_css_classes = get_option( 'ta_additional_css_classes' );
483
+
484
+ include_once( $this->_constants->VIEWS_ROOT_PATH() . 'cpt/view-link-options-metabox.php' );
485
+
486
+ }
487
+
488
+ /**
489
+ * Display "Save Affiliate Link" metabox
490
+ *
491
+ * @since 3.0.0
492
+ * @access public
493
+ *
494
+ * @param WP_Post $post Affiliate link WP_Post object.
495
+ */
496
+ public function save_affiliate_link_metabox( $post ) {
497
+
498
+ include( $this->_constants->VIEWS_ROOT_PATH() . 'cpt/view-save-affiliate-link-metabox.php' );
499
+
500
+ }
501
+
502
+ /**
503
+ * Display inserted link scanner metabox
504
+ *
505
+ * @since 3.2.0
506
+ * @access public
507
+ *
508
+ * @param WP_Post $post Affiliate link WP_Post object.
509
+ */
510
+ public function inserted_link_scanner_metabox( $post ) {
511
+
512
+ $thirstylink = $this->get_thirstylink_post( $post->ID );
513
+ $spinner_image_src = $this->_constants->IMAGES_ROOT_URL() . 'spinner-2x.gif';
514
+ $inserted_to = $thirstylink->get_prop( 'inserted_to' );
515
+ $timezone = new \DateTimeZone( $this->_helper_functions->get_site_current_timezone() );
516
+ $last_scanned = \DateTime::createFromFormat( 'Y-m-d G:i:s' , $thirstylink->get_prop( 'scanned_inserted' ) );
517
+ $not_yet_scanned = __( 'Not yet scanned' , 'thirstyaffiliates' );
518
+
519
+ if ( $last_scanned )
520
+ $last_scanned->setTimezone( $timezone );
521
+
522
+ $last_scanned_txt = $last_scanned !== false ? __( 'Last scanned on:' , 'thirstyaffiliates' ) . ' <em>' . $last_scanned->format( 'F j, Y g:ia' ) . '</em>' : $not_yet_scanned;
523
+ $inserted_into_rows_html = $this->get_inserted_into_rows_html( $inserted_to );
524
+
525
+ include( $this->_constants->VIEWS_ROOT_PATH() . 'cpt/view-inserted-link-scanner-metabox.php' );
526
+ }
527
+
528
+ /**
529
+ * Get the HTML for inserted into table rows.
530
+ *
531
+ * @since 3.2.0
532
+ * @access private
533
+ *
534
+ * @param array $inserted_to WP_Post IDs where affiliate link is inserte to.
535
+ * @param Affiliate_Link $thirstylink Affiliate link object.
536
+ */
537
+ private function get_inserted_into_rows_html( $inserted_to ) {
538
+
539
+ global $wpdb;
540
+
541
+ if ( ! is_array( $inserted_to ) || empty( $inserted_to ) )
542
+ return '<tr><td colspan="4">' . __( 'No results found.' , 'thirstyaffiliates' ) . '</td></tr>';
543
+
544
+ $inserted_to_str = implode( ',' , $inserted_to );
545
+ $results = $wpdb->get_results( "SELECT ID , post_title , post_type FROM $wpdb->posts WHERE ID IN ( $inserted_to_str )" );
546
+
547
+ ob_start();
548
+ foreach ( $results as $object ) : ?>
549
+ <tr>
550
+ <td class="id"><?php echo esc_html( $object->ID ); ?></td>
551
+ <td class="title"><?php echo mb_strimwidth( esc_html( $object->post_title ) , 0 , 60 , "..." ); ?></td>
552
+ <td class="post-type"><?php echo esc_html( $object->post_type ); ?></td>
553
+ <td class="actions">
554
+ <a class="view" href="<?php echo get_permalink( $object->ID ); ?>" target="_blank"><span class="dashicons dashicons-admin-links"></span></a>
555
+ <a class="edit" href="<?php echo get_edit_post_link( $object->ID ); ?>" target="_blank"><span class="dashicons dashicons-edit"></span></a>
556
+ </td>
557
+ </tr>
558
+ <?php endforeach;
559
+ return ob_get_clean();
560
+ }
561
+
562
+
563
+
564
+
565
+ /*
566
+ |--------------------------------------------------------------------------
567
+ | Save Functions
568
+ |--------------------------------------------------------------------------
569
+ */
570
+
571
+ /**
572
+ * Save thirstylink post.
573
+ *
574
+ * @since 3.0.0
575
+ * @since 3.2.2 Make sure post name (slug) is updated.
576
+ * @since 3.4.0 Add css_classes to the saved properties.
577
+ * @access public
578
+ *
579
+ * @param int $post_id Affiliate link post ID.
580
+ */
581
+ public function save_post( $post_id ) {
582
+
583
+ if ( ! isset( $_POST[ '_thirstyaffiliates_nonce' ] ) || ! wp_verify_nonce( $_POST['_thirstyaffiliates_nonce'], 'thirsty_affiliates_cpt_nonce' ) )
584
+ return;
585
+
586
+ // remove save_post hooked action to prevent infinite loop
587
+ remove_action( 'save_post' , array( $this , 'save_post' ) );
588
+
589
+ $thirstylink = $this->get_thirstylink_post( $post_id );
590
+
591
+ // set Properties
592
+ $thirstylink->set_prop( 'destination_url' , esc_url_raw( $_POST[ 'ta_destination_url' ] ) );
593
+ $thirstylink->set_prop( 'no_follow' , sanitize_text_field( $_POST[ 'ta_no_follow' ] ) );
594
+ $thirstylink->set_prop( 'new_window' , sanitize_text_field( $_POST[ 'ta_new_window' ] ) );
595
+ $thirstylink->set_prop( 'pass_query_str' , sanitize_text_field( $_POST[ 'ta_pass_query_str' ] ) );
596
+ $thirstylink->set_prop( 'redirect_type' , sanitize_text_field( $_POST[ 'ta_redirect_type' ] ) );
597
+ $thirstylink->set_prop( 'rel_tags' , sanitize_text_field( $_POST[ 'ta_rel_tags' ] ) );
598
+ $thirstylink->set_prop( 'css_classes' , sanitize_text_field( $_POST[ 'ta_css_classes' ] ) );
599
+
600
+ if ( isset( $_POST[ 'post_name' ] ) )
601
+ $thirstylink->set_prop( 'slug' , sanitize_text_field( $_POST[ 'post_name' ] ) );
602
+
603
+ if ( isset( $_POST[ 'ta_uncloak_link' ] ) )
604
+ $thirstylink->set_prop( 'uncloak_link' , sanitize_text_field( $_POST[ 'ta_uncloak_link' ] ) );
605
+
606
+ if ( isset( $_POST[ 'ta_category_slug' ] ) && $_POST[ 'ta_category_slug' ] ) {
607
+
608
+ $category_slug_id = (int) sanitize_text_field( $_POST[ 'ta_category_slug' ] );
609
+ $category_slug = get_term( $category_slug_id , Plugin_Constants::AFFILIATE_LINKS_TAX );
610
+ $thirstylink->set_prop( 'category_slug_id' , $category_slug_id );
611
+ $thirstylink->set_prop( 'category_slug' , $category_slug->slug );
612
+
613
+ } else {
614
+
615
+ $thirstylink->set_prop( 'category_slug_id' , 0 );
616
+ $thirstylink->set_prop( 'category_slug' , '' );
617
+ }
618
+
619
+ do_action( 'ta_save_affiliate_link_post' , $thirstylink , $post_id );
620
+
621
+ // save affiliate link
622
+ $thirstylink->save();
623
+
624
+ // set default term
625
+ $this->_helper_functions->save_default_affiliate_link_category( $post_id );
626
+
627
+ // add back save_post hooked action after saving
628
+ add_action( 'save_post' , array( $this , 'save_post' ) );
629
+
630
+ do_action( 'ta_after_save_affiliate_link_post' , $post_id , $thirstylink );
631
+ }
632
+
633
+ /**
634
+ * Set default term when affiliate link is saved.
635
+ *
636
+ * @deprecated 3.2.0 Moved to helper functions
637
+ *
638
+ * @since 3.0.0
639
+ * @access public
640
+ *
641
+ * @param int $post_id Affiliate link post ID.
642
+ */
643
+ public function save_default_affiliate_link_category( $post_id ) {
644
+
645
+ $this->_helper_functions->save_default_affiliate_link_category( $post_id );
646
+ }
647
+
648
+
649
+
650
+
651
+ /*
652
+ |--------------------------------------------------------------------------
653
+ | Affiliate Link listing related UI functions
654
+ |--------------------------------------------------------------------------
655
+ */
656
+
657
+ /**
658
+ * Add custom column to thirsty link listings (Link ID).
659
+ *
660
+ * @since 3.0.0
661
+ * @access public
662
+ *
663
+ * @param array $columns Post type listing columns.
664
+ * @return array Filtered post type listing columns.
665
+ */
666
+ public function custom_post_listing_column( $columns ) {
667
+
668
+ $updated_columns = array();
669
+
670
+ foreach ( $columns as $key => $column ) {
671
+
672
+ // add link_id and link_destination column before link categories column
673
+ if ( $key == 'taxonomy-thirstylink-category' ) {
674
+
675
+ $updated_columns[ 'link_id' ] = __( 'Link ID' , 'thirstyaffiliates' );
676
+ $updated_columns[ 'redirect_type' ] = __( 'Redirect Type' , 'thirstyaffiliates' );
677
+ $updated_columns[ 'cloaked_url' ] = __( 'Cloaked URL' , 'thirstyaffiliates' );
678
+ $updated_columns[ 'link_destination' ] = __( 'Link Destination' , 'thirstyaffiliates' );
679
+ }
680
+
681
+
682
+ $updated_columns[ $key ] = $column;
683
+ }
684
+
685
+ return apply_filters( 'ta_post_listing_custom_columns' , $updated_columns );
686
+
687
+ }
688
+
689
+ /**
690
+ * Add custom column to thirsty link listings (Link ID).
691
+ *
692
+ * @since 3.2.0
693
+ * @access public
694
+ *
695
+ * @param array $columns Post type listing sortable columns.
696
+ * @return array Filtered Post type listing sortable columns.
697
+ */
698
+ public function custom_post_listing_sortable_column( $columns ) {
699
+
700
+ $columns[ 'link_id' ] = 'ID';
701
+ $columns[ 'redirect_type' ] = '_ta_redirect_type';
702
+ $columns[ 'cloaked_url' ] = 'name';
703
+ $columns[ 'link_destination' ] = '_ta_destination_url';
704
+
705
+ return apply_filters( 'ta_post_listing_sortable_custom_columns' , $columns );
706
+ }
707
+
708
+ /**
709
+ * Add custom sorting support for TA custom columns with post meta values.
710
+ *
711
+ * @since 3.2.0
712
+ * @access public
713
+ *
714
+ * @param WP_Query $query Main WP_Query instance of the page.
715
+ */
716
+ public function custom_post_listing_column_custom_sorting( $query ) {
717
+
718
+ $post_type = get_post_type();
719
+ if ( !$post_type && isset( $_GET[ 'post_type' ] ) )
720
+ $post_type = $_GET[ 'post_type' ];
721
+
722
+ if ( ! is_admin() || $post_type !== Plugin_Constants::AFFILIATE_LINKS_CPT )
723
+ return;
724
+
725
+ $meta_key = $query->get( 'orderby' );
726
+ $default_keys = array( '_ta_redirect_type', '_ta_destination_url' );
727
+ $extend_keys = apply_filters( 'ta_post_listing_custom_sorting_keys' , array() );
728
+ $meta_keys = array_unique( array_merge( $default_keys , $extend_keys ) );
729
+
730
+
731
+ if ( ! in_array( $meta_key , $meta_keys ) )
732
+ return;
733
+
734
+ $meta_data = apply_filters( 'ta_post_listing_custom_sorting_metadata' , array(
735
+ 'meta_key' => $meta_key,
736
+ 'orderby' => 'meta_value',
737
+ ) , $meta_key , $meta_keys );
738
+
739
+ if ( ! $meta_data[ 'meta_key' ] || ! $meta_data[ 'orderby' ] )
740
+ return;
741
+
742
+ $query->set( 'meta_key' , $meta_key );
743
+ }
744
+
745
+ /**
746
+ * Add custom column to thirsty link listings (Link ID).
747
+ *
748
+ * @since 3.0.0
749
+ * @since 3.4.0 Update display for redirect type value.
750
+ * @access public
751
+ *
752
+ * @param string $column Current column name.
753
+ * @param int $post_id Thirstylink ID.
754
+ * @return array
755
+ */
756
+ public function custom_post_listing_column_value( $column , $post_id ) {
757
+
758
+ $thirstylink = $this->get_thirstylink_post( $post_id );
759
+ $edit_link = get_edit_post_link( $post_id );
760
+ $redirect_type = $thirstylink->get_prop( 'redirect_type' );
761
+
762
+ switch ( $column ) {
763
+
764
+ case 'link_id' :
765
+ echo '<span>' . $post_id . '</span>';
766
+ break;
767
+
768
+ case 'redirect_type' :
769
+ echo $redirect_type;
770
+ echo $redirect_type === 'global' ? ' (' . $this->_helper_functions->get_option( 'ta_link_redirect_type' , '302' ) . ')' : '';
771
+ break;
772
+
773
+ case 'cloaked_url' :
774
+ echo '<div class="ta-display-input-wrap">';
775
+ echo '<input style="width:100%;" type="text" value="' . $thirstylink->get_prop( 'permalink' ) . '" readonly>';
776
+ echo '<a href="' . $edit_link . '"><span class="dashicons dashicons-edit"></span></a>';
777
+ echo '</div>';
778
+ break;
779
+
780
+ case 'link_destination' :
781
+ echo '<div class="ta-display-input-wrap">';
782
+ echo '<input style="width:100%;" type="text" value="' . $thirstylink->get_prop( 'destination_url' ) . '" readonly>';
783
+ echo '<a href="' . $edit_link . '"><span class="dashicons dashicons-edit"></span></a>';
784
+ echo '</div>';
785
+ break;
786
+
787
+ }
788
+
789
+ do_action( 'ta_post_listing_custom_columns_value' , $column , $thirstylink );
790
+
791
+ }
792
+
793
+ /**
794
+ * Setup the filter box for the list page so users can filter links by category
795
+ *
796
+ * @since 3.2.0
797
+ * @access public
798
+ *
799
+ * @global string $typenow Current post type context.
800
+ * @global WP_Query $wp_query Object that contains the main query of WP.
801
+ */
802
+ public function restrict_links_by_category() {
803
+
804
+ global $typenow , $wp_query;
805
+
806
+ if ( $typenow != Plugin_Constants::AFFILIATE_LINKS_CPT )
807
+ return;
808
+
809
+ $taxonomy = Plugin_Constants::AFFILIATE_LINKS_TAX;
810
+
811
+ wp_dropdown_categories( array(
812
+ 'show_option_all' => __( 'Show Link Categories' , 'thirstyaffiliates' ),
813
+ 'taxonomy' => $taxonomy,
814
+ 'name' => $taxonomy,
815
+ 'orderby' => 'name',
816
+ 'selected' => ( isset( $wp_query->query[ $taxonomy ] ) ? $wp_query->query[ $taxonomy ] : '' ),
817
+ 'hierarchical' => true,
818
+ 'depth' => 4,
819
+ 'show_count' => true,
820
+ 'hide_empty' => true
821
+ ) );
822
+ }
823
+
824
+ /**
825
+ * Convert category ID to slug to make the filter by category dropdown work.
826
+ *
827
+ * @since 3.2.0
828
+ * @access public
829
+ *
830
+ * @global string $typenow Current post type context.
831
+ *
832
+ * @param WP_Query $query Current page query.
833
+ * @return WP_Query Filtered current page query.
834
+ */
835
+ public function convert_cat_id_to_slug_in_query( $query ) {
836
+
837
+ global $typenow;
838
+
839
+ if ( $typenow != Plugin_Constants::AFFILIATE_LINKS_CPT )
840
+ return;
841
+
842
+ $qv = &$query->query_vars;
843
+ $taxonomy = Plugin_Constants::AFFILIATE_LINKS_TAX;
844
+
845
+ if ( isset( $qv[ $taxonomy ] ) && is_numeric( $qv[ $taxonomy ] ) ) {
846
+
847
+ $term = get_term_by( 'id' , $qv[ $taxonomy ] , $taxonomy );
848
+ $qv[ $taxonomy ] = is_object( $term ) ? $term->slug : '';
849
+
850
+ }
851
+
852
+ return $query;
853
+ }
854
+
855
+
856
+
857
+
858
+ /*
859
+ |--------------------------------------------------------------------------
860
+ | Affiliate Link Filters
861
+ |--------------------------------------------------------------------------
862
+ */
863
+
864
+ /**
865
+ * Add category slug to the permalink.
866
+ *
867
+ * @since 3.0.0
868
+ * @access public
869
+ *
870
+ * @param string $post_link Thirstylink permalink.
871
+ * @param WP_Post $post Thirstylink WP_Post object.
872
+ * @return string Thirstylink permalink.
873
+ */
874
+ public function add_category_slug_to_permalink( $post_link , $post ) {
875
+
876
+ $link_prefix = $this->_helper_functions->get_thirstylink_link_prefix();
877
+
878
+ if ( get_option( 'ta_show_cat_in_slug' ) !== 'yes' || is_wp_error( $post ) || $post->post_type != 'thirstylink' )
879
+ return $post_link;
880
+
881
+ $link_cat_id = get_post_meta( $post->ID , '_ta_category_slug_id' , true );
882
+ $link_cat = get_post_meta( $post->ID , '_ta_category_slug' , true );
883
+
884
+ if ( ! $link_cat && $link_cat_id ) {
885
+
886
+ $link_cat_obj = get_term( $link_cat_id , Plugin_Constants::AFFILIATE_LINKS_TAX );
887
+ $link_cat = $link_cat_obj->slug;
888
+
889
+ } elseif ( ! $link_cat && ! $link_cat_id ) {
890
+
891
+ $link_cat = $this->_helper_functions->get_default_category_slug( $post->ID );
892
+ }
893
+
894
+ if ( ! $link_cat )
895
+ return $post_link;
896
+
897
+ return home_url( user_trailingslashit( $link_prefix . '/' . $link_cat . '/' . $post->post_name ) );
898
+ }
899
+
900
+
901
+
902
+
903
+ /*
904
+ |--------------------------------------------------------------------------
905
+ | AJAX functions.
906
+ |--------------------------------------------------------------------------
907
+ */
908
+
909
+ /**
910
+ * Ajax get category slug.
911
+ *
912
+ * @since 3.0.0
913
+ * @access public
914
+ */
915
+ public function ajax_get_category_slug() {
916
+
917
+ if ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX )
918
+ $response = array( 'status' => 'fail' , 'error_msg' => __( 'Invalid AJAX call' , 'thirstyaffiliates' ) );
919
+ elseif ( ! isset( $_POST[ 'term_id' ] ) )
920
+ $response = array( 'status' => 'fail' , 'error_msg' => __( 'Missing required post data' , 'thirstyaffiliates' ) );
921
+ else {
922
+
923
+ $link_cat_id = (int) sanitize_text_field( $_POST[ 'term_id' ] );
924
+ $category = get_term( $link_cat_id , Plugin_Constants::AFFILIATE_LINKS_TAX );
925
+
926
+ $response = array( 'status' => 'success' , 'category_slug' => $category->slug );
927
+ }
928
+
929
+ @header( 'Content-Type: application/json; charset=' . get_option( 'blog_charset' ) );
930
+ echo wp_json_encode( $response );
931
+ wp_die();
932
+ }
933
+
934
+ /**
935
+ * Ajax link inserted scanner.
936
+ *
937
+ * @since 3.2.0
938
+ * @access public
939
+ */
940
+ public function ajax_link_inserted_scanner() {
941
+
942
+ if ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX )
943
+ $response = array( 'status' => 'fail' , 'error_msg' => __( 'Invalid AJAX call' , 'thirstyaffiliates' ) );
944
+ elseif ( ! isset( $_POST[ 'link_id' ] ) )
945
+ $response = array( 'status' => 'fail' , 'error_msg' => __( 'Missing required post data' , 'thirstyaffiliates' ) );
946
+ else {
947
+
948
+ $link_id = (int) sanitize_text_field( $_POST[ 'link_id' ] );
949
+ $thirstylink = $this->get_thirstylink_post( $link_id );
950
+ $inserted_to = $thirstylink->scan_where_links_inserted();
951
+ $timezone = new \DateTimeZone( $this->_helper_functions->get_site_current_timezone() );
952
+ $last_scanned = \DateTime::createFromFormat( 'Y-m-d G:i:s' , get_post_meta( $link_id , Plugin_Constants::META_DATA_PREFIX . 'scanned_inserted' , true ) , $timezone );
953
+
954
+ $response = array(
955
+ 'status' => 'success',
956
+ 'results_markup' => $this->get_inserted_into_rows_html( $inserted_to ),
957
+ 'last_scanned' => __( 'Last scanned on:' , 'thirstyaffiliates' ) . ' <em>' . $last_scanned->format( 'F j, Y g:ia' ) . '</em>'
958
+ );
959
+ }
960
+
961
+ @header( 'Content-Type: application/json; charset=' . get_option( 'blog_charset' ) );
962
+ echo wp_json_encode( $response );
963
+ wp_die();
964
+ }
965
+
966
+
967
+
968
+
969
+ /*
970
+ |--------------------------------------------------------------------------
971
+ | Fulfill implemented interface contracts
972
+ |--------------------------------------------------------------------------
973
+ */
974
+
975
+ /**
976
+ * Method that houses codes to be executed on init hook.
977
+ *
978
+ * @since 3.0.0
979
+ * @access public
980
+ * @inherit ThirstyAffiliates\Interfaces\Initiable_Interface
981
+ */
982
+ public function initialize() {
983
+
984
+ // cpt and taxonomy
985
+ $this->register_thirstylink_custom_post_type();
986
+ $this->register_thirstylink_category_custom_taxonomy();
987
+
988
+ add_action( 'wp_ajax_ta_get_category_slug' , array( $this , 'ajax_get_category_slug' ) );
989
+ add_action( 'wp_ajax_ta_link_inserted_scanner' , array( $this , 'ajax_link_inserted_scanner' ) );
990
+ }
991
+
992
+ /**
993
+ * Execute 'thirstylink' custom post type code.
994
+ *
995
+ * @since 3.0.0
996
+ * @access public
997
+ * @inherit ThirstyAffiliates\Interfaces\Model_Interface
998
+ */
999
+ public function run() {
1000
+
1001
+ // replace permalink with link ID
1002
+ add_filter( 'get_sample_permalink_html', array( $this , 'replace_permalink_with_id' ), 10 , 2 );
1003
+
1004
+ // metaboxes
1005
+ add_action( 'add_meta_boxes' , array( $this , 'register_metaboxes' ) );
1006
+ add_action( 'save_post' , array( $this , 'save_post' ) );
1007
+
1008
+ // custom column
1009
+ add_filter( 'manage_edit-thirstylink_columns' , array( $this , 'custom_post_listing_column' ) );
1010
+ add_filter( 'manage_edit-thirstylink_sortable_columns', array( $this , 'custom_post_listing_sortable_column' ) );
1011
+ add_action( 'manage_thirstylink_posts_custom_column', array( $this , 'custom_post_listing_column_value' ) , 10 , 2 );
1012
+ add_action( 'pre_get_posts' , array( $this , 'custom_post_listing_column_custom_sorting' ) );
1013
+
1014
+ // filter by category
1015
+ add_action( 'restrict_manage_posts' , array( $this , 'restrict_links_by_category' ) );
1016
+ add_filter( 'parse_query' , array( $this , 'convert_cat_id_to_slug_in_query' ) );
1017
+
1018
+ // filter to add category on permalink
1019
+ add_filter( 'post_type_link' , array( $this , 'add_category_slug_to_permalink' ) , 10 , 2 );
1020
+
1021
+ // Register admin interface and menus.
1022
+ add_filter( 'ta_admin_interfaces' , array( $this , 'register_admin_interfaces' ) );
1023
+ add_filter( 'ta_menu_items' , array( $this , 'register_admin_menu_items' ) );
1024
+ }
1025
+
1026
+ }
Models/Bootstrap.php CHANGED
@@ -1,520 +1,520 @@
1
- <?php
2
- namespace ThirstyAffiliates\Models;
3
-
4
- use ThirstyAffiliates\Abstracts\Abstract_Main_Plugin_Class;
5
-
6
- use ThirstyAffiliates\Interfaces\Model_Interface;
7
- use ThirstyAffiliates\Interfaces\Activatable_Interface;
8
- use ThirstyAffiliates\Interfaces\Initiable_Interface;
9
- use ThirstyAffiliates\Interfaces\Deactivatable_Interface;
10
-
11
- use ThirstyAffiliates\Helpers\Plugin_Constants;
12
- use ThirstyAffiliates\Helpers\Helper_Functions;
13
-
14
- if ( !defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
15
-
16
- /**
17
- * Model that houses the logic of 'Bootstraping' the plugin.
18
- *
19
- * @since 3.0.0
20
- */
21
- class Bootstrap implements Model_Interface {
22
-
23
- /*
24
- |--------------------------------------------------------------------------
25
- | Class Properties
26
- |--------------------------------------------------------------------------
27
- */
28
-
29
- /**
30
- * Property that holds the single main instance of Bootstrap.
31
- *
32
- * @since 3.0.0
33
- * @access private
34
- * @var Bootstrap
35
- */
36
- private static $_instance;
37
-
38
- /**
39
- * Model that houses all the plugin constants.
40
- *
41
- * @since 3.0.0
42
- * @access private
43
- * @var Plugin_Constants
44
- */
45
- private $_constants;
46
-
47
- /**
48
- * Property that houses all the helper functions of the plugin.
49
- *
50
- * @since 3.0.0
51
- * @access private
52
- * @var Helper_Functions
53
- */
54
- private $_helper_functions;
55
-
56
- /**
57
- * Array of models implementing the ThirstyAffiliates\Interfaces\Activatable_Interface.
58
- *
59
- * @since 3.0.0
60
- * @access private
61
- * @var array
62
- */
63
- private $_activatables;
64
-
65
- /**
66
- * Array of models implementing the ThirstyAffiliates\Interfaces\Initiable_Interface.
67
- *
68
- * @since 3.0.0
69
- * @access private
70
- * @var array
71
- */
72
- private $_initiables;
73
-
74
- /**
75
- * Array of models implementing the ThirstyAffiliates\Interfaces\Deactivatable_Interface.
76
- *
77
- * @since 3.1.0
78
- * @access private
79
- * @var array
80
- */
81
- private $_deactivatables;
82
-
83
-
84
-
85
-
86
- /*
87
- |--------------------------------------------------------------------------
88
- | Class Methods
89
- |--------------------------------------------------------------------------
90
- */
91
-
92
- /**
93
- * Class constructor.
94
- *
95
- * @since 3.0.0
96
- * @since 3.1.0 Add deactivatables interface.
97
- * @access public
98
- *
99
- * @param Abstract_Main_Plugin_Class $main_plugin Main plugin object.
100
- * @param Plugin_Constants $constants Plugin constants object.
101
- * @param Helper_Functions $helper_functions Helper functions object.
102
- * @param array $activatables Array of models implementing ThirstyAffiliates\Interfaces\Activatable_Interface.
103
- * @param array $initiables Array of models implementing ThirstyAffiliates\Interfaces\Initiable_Interface.
104
- * @param array $deactivatables Array of models implementing ThirstyAffiliates\Interfaces\Deactivatable_Interface.
105
- */
106
- public function __construct( Abstract_Main_Plugin_Class $main_plugin , Plugin_Constants $constants , Helper_Functions $helper_functions , array $activatables = array() , array $initiables = array() , array $deactivatables = array() ) {
107
-
108
- $this->_constants = $constants;
109
- $this->_helper_functions = $helper_functions;
110
- $this->_activatables = $activatables;
111
- $this->_initiables = $initiables;
112
- $this->_deactivatables = $deactivatables;
113
-
114
- $main_plugin->add_to_all_plugin_models( $this );
115
-
116
- }
117
-
118
- /**
119
- * Ensure that only one instance of this class is loaded or can be loaded ( Singleton Pattern ).
120
- *
121
- * @since 3.0.0
122
- * @access public
123
- *
124
- * @param Abstract_Main_Plugin_Class $main_plugin Main plugin object.
125
- * @param Plugin_Constants $constants Plugin constants object.
126
- * @param Helper_Functions $helper_functions Helper functions object.
127
- * @param array $activatables Array of models implementing ThirstyAffiliates\Interfaces\Activatable_Interface.
128
- * @param array $initiables Array of models implementing ThirstyAffiliates\Interfaces\Initiable_Interface.
129
- * @return Bootstrap
130
- */
131
- public static function get_instance( Abstract_Main_Plugin_Class $main_plugin , Plugin_Constants $constants , Helper_Functions $helper_functions , array $activatables = array() , array $initiables = array() , array $deactivatables = array() ) {
132
-
133
- if ( !self::$_instance instanceof self )
134
- self::$_instance = new self( $main_plugin , $constants , $helper_functions , $activatables , $initiables , $deactivatables );
135
-
136
- return self::$_instance;
137
-
138
- }
139
-
140
- /**
141
- * Load plugin text domain.
142
- *
143
- * @since 3.0.0
144
- * @access public
145
- */
146
- public function load_plugin_textdomain() {
147
-
148
- load_plugin_textdomain( Plugin_Constants::TEXT_DOMAIN , false , $this->_constants->PLUGIN_DIRNAME() . '/languages' );
149
-
150
- }
151
-
152
- /**
153
- * Method that houses the logic relating to activating the plugin.
154
- *
155
- * @since 3.0.0
156
- * @access public
157
- *
158
- * @global wpdb $wpdb Object that contains a set of functions used to interact with a database.
159
- *
160
- * @param boolean $network_wide Flag that determines whether the plugin has been activated network wid ( on multi site environment ) or not.
161
- */
162
- public function activate_plugin( $network_wide ) {
163
-
164
- // Suppress any errors
165
- @deactivate_plugins( array(
166
- 'thirstyaffiliates-stats/thirstyaffiliates-stats.php',
167
- 'thirstyaffiliates-htaccess-redirect/thirstyaffiliates-htaccess-refactor.bootstrap.php',
168
- 'thirstyaffiliates-google-click-tracking/thirstyaffiliates-google-click-tracking.php',
169
- 'thirstyaffiliates-geolocations/thirstyaffiliates-geolocations.php',
170
- 'thirstyaffiliates-csv-importer/thirstyaffiliates-csv-importer.php',
171
- 'thirstyaffiliates-autolinker/thirstyaffiliates-autolinker.php',
172
- 'thirstyaffiliates-azon-add-on/azon-bootstrap.php',
173
- 'thirstyaffiliates-itunes/thirstyaffiliates-itunes.bootstrap.php'
174
- ) , true , null ); // Deactivate on all sites in the network and do not fire deactivation hooks
175
-
176
- global $wpdb;
177
-
178
- if ( is_multisite() ) {
179
-
180
- if ( $network_wide ) {
181
-
182
- // get ids of all sites
183
- $blog_ids = $wpdb->get_col( "SELECT blog_id FROM $wpdb->blogs" );
184
-
185
- foreach ( $blog_ids as $blog_id ) {
186
-
187
- switch_to_blog( $blog_id );
188
- $this->_activate_plugin( $blog_id );
189
-
190
- }
191
-
192
- restore_current_blog();
193
-
194
- } else
195
- $this->_activate_plugin( $wpdb->blogid ); // activated on a single site, in a multi-site
196
-
197
- } else
198
- $this->_activate_plugin( $wpdb->blogid ); // activated on a single site
199
-
200
- }
201
-
202
- /**
203
- * Method to initialize a newly created site in a multi site set up.
204
- *
205
- * @since 3.0.0
206
- * @access public
207
- *
208
- * @param int $blogid Blog ID of the created blog.
209
- * @param int $user_id User ID of the user creating the blog.
210
- * @param string $domain Domain used for the new blog.
211
- * @param string $path Path to the new blog.
212
- * @param int $site_id Site ID. Only relevant on multi-network installs.
213
- * @param array $meta Meta data. Used to set initial site options.
214
- */
215
- public function new_mu_site_init( $blog_id , $user_id , $domain , $path , $site_id , $meta ) {
216
-
217
- if ( is_plugin_active_for_network( 'thirstyaffiliates/thirstyaffiliates.php' ) ) {
218
-
219
- switch_to_blog( $blog_id );
220
- $this->_activate_plugin( $blog_id );
221
- restore_current_blog();
222
-
223
- }
224
-
225
- }
226
-
227
- /**
228
- * Initialize plugin settings options.
229
- * This is a compromise to my idea of 'Modularity'. Ideally, bootstrap should not take care of plugin settings stuff.
230
- * However due to how WooCommerce do its thing, we need to do it this way. We can't separate settings on its own.
231
- *
232
- * @since 3.0.0
233
- * @access private
234
- */
235
- private function _initialize_plugin_settings_options() {
236
-
237
- // Help settings section options
238
-
239
- // Set initial value of 'no' for the option that sets the option that specify whether to delete the options on plugin uninstall. Optionception.
240
- if ( !get_option( Plugin_Constants::CLEAN_UP_PLUGIN_OPTIONS , false ) )
241
- update_option( Plugin_Constants::CLEAN_UP_PLUGIN_OPTIONS , 'no' );
242
-
243
- // set redirect type default to '302' on activation.
244
- if ( ! get_option( 'ta_link_redirect_type' , false ) )
245
- update_option( 'ta_link_redirect_type' , 302 );
246
-
247
- }
248
-
249
- /**
250
- * Actual function that houses the code to execute on plugin activation.
251
- *
252
- * @since 3.0.0
253
- * @since 3.0.0 Added the ta_activation_date option
254
- * @access private
255
- *
256
- * @param int $blogid Blog ID of the created blog.
257
- */
258
- private function _activate_plugin( $blogid ) {
259
-
260
- // Initialize settings options
261
- $this->_initialize_plugin_settings_options();
262
-
263
- // Create database tables
264
- $this->_create_database_tables();
265
-
266
- // set flush rewrite rules transient so it can be flushed after the CPT and rewrite rules have been registered.
267
- set_transient( 'ta_flush_rewrite_rules' , 'true' , 5 * 60 );
268
-
269
- // Execute 'activate' contract of models implementing ThirstyAffiliates\Interfaces\Activatable_Interface
270
- foreach ( $this->_activatables as $activatable )
271
- if ( $activatable instanceof Activatable_Interface )
272
- $activatable->activate();
273
-
274
- update_option( Plugin_Constants::INSTALLED_VERSION , Plugin_Constants::VERSION ); // Update current installed plugin version
275
- update_option( 'ta_activation_code_triggered' , 'yes' ); // Mark activation code triggered
276
-
277
- }
278
-
279
- /**
280
- * Method that houses the logic relating to deactivating the plugin.
281
- *
282
- * @since 3.0.0
283
- * @access public
284
- *
285
- * @global wpdb $wpdb Object that contains a set of functions used to interact with a database.
286
- *
287
- * @param boolean $network_wide Flag that determines whether the plugin has been activated network wid ( on multi site environment ) or not.
288
- */
289
- public function deactivate_plugin( $network_wide ) {
290
-
291
- global $wpdb;
292
-
293
- // check if it is a multisite network
294
- if ( is_multisite() ) {
295
-
296
- // check if the plugin has been activated on the network or on a single site
297
- if ( $network_wide ) {
298
-
299
- // get ids of all sites
300
- $blog_ids = $wpdb->get_col( "SELECT blog_id FROM $wpdb->blogs" );
301
-
302
- foreach ( $blog_ids as $blog_id ) {
303
-
304
- switch_to_blog( $blog_id );
305
- $this->_deactivate_plugin( $wpdb->blogid );
306
-
307
- }
308
-
309
- restore_current_blog();
310
-
311
- } else
312
- $this->_deactivate_plugin( $wpdb->blogid ); // activated on a single site, in a multi-site
313
-
314
- } else
315
- $this->_deactivate_plugin( $wpdb->blogid ); // activated on a single site
316
-
317
- }
318
-
319
- /**
320
- * Actual method that houses the code to execute on plugin deactivation.
321
- *
322
- * @since 3.0.0
323
- * @access private
324
- *
325
- * @param int $blogid Blog ID of the created blog.
326
- */
327
- private function _deactivate_plugin( $blogid ) {
328
-
329
- // Execute 'deactivate' contract of models implementing ThirstyAffiliates\Interfaces\Deactivatable_Interface
330
- foreach ( $this->_deactivatables as $deactivatable )
331
- if ( $deactivatable instanceof Deactivatable_Interface )
332
- $deactivatable->deactivate();
333
-
334
- flush_rewrite_rules();
335
-
336
- }
337
-
338
- /**
339
- * Create database tables.
340
- *
341
- * @since 3.0.0
342
- * @access private
343
- */
344
- private function _create_database_tables() {
345
-
346
- global $wpdb;
347
-
348
- if ( get_option( 'ta_database_tables_created' ) === 'yes' )
349
- return;
350
-
351
- $link_click_db = $wpdb->prefix . Plugin_Constants::LINK_CLICK_DB;
352
- $link_click_meta_db = $wpdb->prefix . Plugin_Constants::LINK_CLICK_META_DB;
353
- $charset_collate = $wpdb->get_charset_collate();
354
-
355
- // link clicks db sql command
356
- $sql = "CREATE TABLE $link_click_db (
357
- id bigint(20) NOT NULL AUTO_INCREMENT,
358
- link_id bigint(20) NOT NULL,
359
- date_clicked datetime DEFAULT '0000-00-00 00:00:00' NOT NULL,
360
- PRIMARY KEY (id)
361
- ) $charset_collate;\n";
362
-
363
- // link clicks meta db sql command
364
- $sql .= "CREATE TABLE $link_click_meta_db (
365
- id bigint(20) NOT NULL AUTO_INCREMENT,
366
- click_id bigint(20) NOT NULL,
367
- meta_key varchar(255) NULL,
368
- meta_value longtext NULL,
369
- PRIMARY KEY (id)
370
- ) $charset_collate;";
371
-
372
- require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
373
- dbDelta( $sql );
374
-
375
- update_option( 'ta_database_tables_created' , 'yes' );
376
- }
377
-
378
- /**
379
- * Add custom links to the plugin's action links.
380
- *
381
- * @since 3.0.0
382
- * @access public
383
- */
384
- public function plugin_action_links( $links ) {
385
-
386
- $settings_link = admin_url( 'edit.php?post_type=' . Plugin_Constants::AFFILIATE_LINKS_CPT . '&page=thirsty-settings' );
387
-
388
- $new_links = array(
389
- '<a href="' . $settings_link . '">' . __( 'Settings' , 'thirstyaffiliates' ) . '</a>'
390
- );
391
-
392
- return array_merge( $new_links , $links );
393
- }
394
-
395
- /**
396
- * Control admin interface visibility.
397
- *
398
- * @since 3.3.2
399
- * @access public
400
- */
401
- public function admin_interface_visibility() {
402
-
403
- $object_id = isset( $_GET[ 'post' ] ) ? absint( $_GET[ 'post' ] ) : 0;
404
-
405
- if ( ! $screen_id = $this->_helper_functions->get_screen_id( $object_id ) )
406
- return;
407
-
408
- $current_tab = isset( $_GET[ 'tab' ] ) ? $_GET[ 'tab' ] : '';
409
- $post_type = isset( $_GET[ 'post_type' ] ) ? sanitize_text_field( $_GET[ 'post_type' ] ) : '';
410
- $interfaces = apply_filters( 'ta_admin_interfaces' , array() );
411
- $current_interface = $screen_id && isset( $interfaces[ $screen_id ] ) ? $interfaces[ $screen_id ] : null;
412
-
413
- // if interface is not present then don't proceed
414
- if ( ! $current_interface || empty( $current_interface ) ) return;
415
- if ( $current_tab && ! isset( $current_interface[ $current_tab ] ) ) return;
416
-
417
- // get the capability allowed for the interface
418
- if ( is_array( $current_interface ) )
419
- $capability = $current_tab ? $current_interface[ $current_tab ] : array_values( $current_interface )[0];
420
- else
421
- $capability = $current_interface;
422
-
423
- if ( $current_tab === 'ta_import_export_settings' )
424
- $capability = 'manage_options';
425
-
426
- // get error message.
427
- $error_message = apply_filters( 'ta_admin_interface_error_message' , __( "Sorry, you are not allowed to access this page." , 'thirstyaffiliates' ) , $screen_id , $current_tab , $capability , $current_interface );
428
-
429
- // kill page display error message if current user does not have capability.
430
- if ( ( $capability && ! current_user_can( $capability ) ) || ( $object_id && isset( $_GET[ 'post' ] ) && get_current_user_id() != get_post_field( 'post_author' , $object_id ) && ! current_user_can( 'edit_others_posts' ) ) )
431
- wp_die( $error_message );
432
- }
433
-
434
- /**
435
- * Control admin menu items visibility.
436
- *
437
- * @since 3.3.2
438
- * @access public
439
- */
440
- public function admin_menu_items_visibilty() {
441
-
442
- global $submenu;
443
-
444
- $menu_slug = 'edit.php?post_type=' . Plugin_Constants::AFFILIATE_LINKS_CPT;
445
- $menu_items = apply_filters( 'ta_menu_items' , array() );
446
- $main_cap = null;
447
-
448
- if ( ! is_array( $menu_items ) || empty( $menu_items ) ) return;
449
-
450
- foreach ( $menu_items as $submenu_slug => $capability ) {
451
-
452
- if ( $submenu_slug == $menu_slug ) {
453
- $main_cap = $capability;
454
- continue;
455
- }
456
-
457
- if ( $capability && ! current_user_can( $capability ) )
458
- remove_submenu_page( $menu_slug , esc_attr( $submenu_slug ) );
459
- }
460
-
461
- $menu_count = isset( $submenu[ $menu_slug ] ) ? count( $submenu[ $menu_slug ] ) : 0;
462
-
463
- if ( ( ! $main_cap || ! current_user_can( $main_cap ) ) && $menu_count <= 1 )
464
- remove_menu_page( $menu_slug );
465
- }
466
-
467
- /**
468
- * Method that houses codes to be executed on init hook.
469
- *
470
- * @since 3.0.0
471
- * @access public
472
- */
473
- public function initialize() {
474
-
475
- // Execute activation codebase if not yet executed on plugin activation ( Mostly due to plugin dependencies )
476
- if ( get_option( 'ta_activation_code_triggered' , false ) !== 'yes' ) {
477
-
478
- if ( ! function_exists( 'is_plugin_active_for_network' ) )
479
- require_once( ABSPATH . '/wp-admin/includes/plugin.php' );
480
-
481
- $network_wide = is_plugin_active_for_network( 'thirstyaffiliates/thirstyaffiliates.php' );
482
- $this->activate_plugin( $network_wide );
483
-
484
- }
485
-
486
- // Execute 'initialize' contract of models implementing ThirstyAffiliates\Interfaces\Initiable_Interface
487
- foreach ( $this->_initiables as $initiable )
488
- if ( $initiable instanceof Initiable_Interface )
489
- $initiable->initialize();
490
-
491
- }
492
-
493
- /**
494
- * Execute plugin bootstrap code.
495
- *
496
- * @since 3.0.0
497
- * @access public
498
- */
499
- public function run() {
500
-
501
- // Internationalization
502
- add_action( 'plugins_loaded' , array( $this , 'load_plugin_textdomain' ) );
503
-
504
- // Execute plugin activation/deactivation
505
- register_activation_hook( $this->_constants->MAIN_PLUGIN_FILE_PATH() , array( $this , 'activate_plugin' ) );
506
- register_deactivation_hook( $this->_constants->MAIN_PLUGIN_FILE_PATH() , array( $this , 'deactivate_plugin' ) );
507
-
508
- // Execute plugin initialization ( plugin activation ) on every newly created site in a multi site set up
509
- add_action( 'wpmu_new_blog' , array( $this , 'new_mu_site_init' ) , 10 , 6 );
510
-
511
- add_filter( 'plugin_action_links_' . $this->_constants->PLUGIN_BASENAME() , array( $this , 'plugin_action_links' ) );
512
-
513
- // Execute codes that need to run on 'init' hook
514
- add_action( 'init' , array( $this , 'initialize' ) );
515
-
516
- add_action( 'init' , array( $this , 'admin_interface_visibility' ) );
517
- add_action( 'admin_menu' , array( $this , 'admin_menu_items_visibilty' ) , 20 );
518
- }
519
-
520
- }
1
+ <?php
2
+ namespace ThirstyAffiliates\Models;
3
+
4
+ use ThirstyAffiliates\Abstracts\Abstract_Main_Plugin_Class;
5
+
6
+ use ThirstyAffiliates\Interfaces\Model_Interface;
7
+ use ThirstyAffiliates\Interfaces\Activatable_Interface;
8
+ use ThirstyAffiliates\Interfaces\Initiable_Interface;
9
+ use ThirstyAffiliates\Interfaces\Deactivatable_Interface;
10
+
11
+ use ThirstyAffiliates\Helpers\Plugin_Constants;
12
+ use ThirstyAffiliates\Helpers\Helper_Functions;
13
+
14
+ if ( !defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
15
+
16
+ /**
17
+ * Model that houses the logic of 'Bootstraping' the plugin.
18
+ *
19
+ * @since 3.0.0
20
+ */
21
+ class Bootstrap implements Model_Interface {
22
+
23
+ /*
24
+ |--------------------------------------------------------------------------
25
+ | Class Properties
26
+ |--------------------------------------------------------------------------
27
+ */
28
+
29
+ /**
30
+ * Property that holds the single main instance of Bootstrap.
31
+ *
32
+ * @since 3.0.0
33
+ * @access private
34
+ * @var Bootstrap
35
+ */
36
+ private static $_instance;
37
+
38
+ /**
39
+ * Model that houses all the plugin constants.
40
+ *
41
+ * @since 3.0.0
42
+ * @access private
43
+ * @var Plugin_Constants
44
+ */
45
+ private $_constants;
46
+
47
+ /**
48
+ * Property that houses all the helper functions of the plugin.
49
+ *
50
+ * @since 3.0.0
51
+ * @access private
52
+ * @var Helper_Functions
53
+ */
54
+ private $_helper_functions;
55
+
56
+ /**
57
+ * Array of models implementing the ThirstyAffiliates\Interfaces\Activatable_Interface.
58
+ *
59
+ * @since 3.0.0
60
+ * @access private
61
+ * @var array
62
+ */
63
+ private $_activatables;
64
+
65
+ /**
66
+ * Array of models implementing the ThirstyAffiliates\Interfaces\Initiable_Interface.
67
+ *
68
+ * @since 3.0.0
69
+ * @access private
70
+ * @var array
71
+ */
72
+ private $_initiables;
73
+
74
+ /**
75
+ * Array of models implementing the ThirstyAffiliates\Interfaces\Deactivatable_Interface.
76
+ *
77
+ * @since 3.1.0
78
+ * @access private
79
+ * @var array
80
+ */
81
+ private $_deactivatables;
82
+
83
+
84
+
85
+
86
+ /*
87
+ |--------------------------------------------------------------------------
88
+ | Class Methods
89
+ |--------------------------------------------------------------------------
90
+ */
91
+
92
+ /**
93
+ * Class constructor.
94
+ *
95
+ * @since 3.0.0
96
+ * @since 3.1.0 Add deactivatables interface.
97
+ * @access public
98
+ *
99
+ * @param Abstract_Main_Plugin_Class $main_plugin Main plugin object.
100
+ * @param Plugin_Constants $constants Plugin constants object.
101
+ * @param Helper_Functions $helper_functions Helper functions object.
102
+ * @param array $activatables Array of models implementing ThirstyAffiliates\Interfaces\Activatable_Interface.
103
+ * @param array $initiables Array of models implementing ThirstyAffiliates\Interfaces\Initiable_Interface.
104
+ * @param array $deactivatables Array of models implementing ThirstyAffiliates\Interfaces\Deactivatable_Interface.
105
+ */
106
+ public function __construct( Abstract_Main_Plugin_Class $main_plugin , Plugin_Constants $constants , Helper_Functions $helper_functions , array $activatables = array() , array $initiables = array() , array $deactivatables = array() ) {
107
+
108
+ $this->_constants = $constants;
109
+ $this->_helper_functions = $helper_functions;
110
+ $this->_activatables = $activatables;
111
+ $this->_initiables = $initiables;
112
+ $this->_deactivatables = $deactivatables;
113
+
114
+ $main_plugin->add_to_all_plugin_models( $this );
115
+
116
+ }
117
+
118
+ /**
119
+ * Ensure that only one instance of this class is loaded or can be loaded ( Singleton Pattern ).
120
+ *
121
+ * @since 3.0.0
122
+ * @access public
123
+ *
124
+ * @param Abstract_Main_Plugin_Class $main_plugin Main plugin object.
125
+ * @param Plugin_Constants $constants Plugin constants object.
126
+ * @param Helper_Functions $helper_functions Helper functions object.
127
+ * @param array $activatables Array of models implementing ThirstyAffiliates\Interfaces\Activatable_Interface.
128
+ * @param array $initiables Array of models implementing ThirstyAffiliates\Interfaces\Initiable_Interface.
129
+ * @return Bootstrap
130
+ */
131
+ public static function get_instance( Abstract_Main_Plugin_Class $main_plugin , Plugin_Constants $constants , Helper_Functions $helper_functions , array $activatables = array() , array $initiables = array() , array $deactivatables = array() ) {
132
+
133
+ if ( !self::$_instance instanceof self )
134
+ self::$_instance = new self( $main_plugin , $constants , $helper_functions , $activatables , $initiables , $deactivatables );
135
+
136
+ return self::$_instance;
137
+
138
+ }
139
+
140
+ /**
141
+ * Load plugin text domain.
142
+ *
143
+ * @since 3.0.0
144
+ * @access public
145
+ */
146
+ public function load_plugin_textdomain() {
147
+
148
+ load_plugin_textdomain( Plugin_Constants::TEXT_DOMAIN , false , $this->_constants->PLUGIN_DIRNAME() . '/languages' );
149
+
150
+ }
151
+
152
+ /**
153
+ * Method that houses the logic relating to activating the plugin.
154
+ *
155
+ * @since 3.0.0
156
+ * @access public
157
+ *
158
+ * @global wpdb $wpdb Object that contains a set of functions used to interact with a database.
159
+ *
160
+ * @param boolean $network_wide Flag that determines whether the plugin has been activated network wid ( on multi site environment ) or not.
161
+ */
162
+ public function activate_plugin( $network_wide ) {
163
+
164
+ // Suppress any errors
165
+ @deactivate_plugins( array(
166
+ 'thirstyaffiliates-stats/thirstyaffiliates-stats.php',
167
+ 'thirstyaffiliates-htaccess-redirect/thirstyaffiliates-htaccess-refactor.bootstrap.php',
168
+ 'thirstyaffiliates-google-click-tracking/thirstyaffiliates-google-click-tracking.php',
169
+ 'thirstyaffiliates-geolocations/thirstyaffiliates-geolocations.php',
170
+ 'thirstyaffiliates-csv-importer/thirstyaffiliates-csv-importer.php',
171
+ 'thirstyaffiliates-autolinker/thirstyaffiliates-autolinker.php',
172
+ 'thirstyaffiliates-azon-add-on/azon-bootstrap.php',
173
+ 'thirstyaffiliates-itunes/thirstyaffiliates-itunes.bootstrap.php'
174
+ ) , true , null ); // Deactivate on all sites in the network and do not fire deactivation hooks
175
+
176
+ global $wpdb;
177
+
178
+ if ( is_multisite() ) {
179
+
180
+ if ( $network_wide ) {
181
+
182
+ // get ids of all sites
183
+ $blog_ids = $wpdb->get_col( "SELECT blog_id FROM $wpdb->blogs" );
184
+
185
+ foreach ( $blog_ids as $blog_id ) {
186
+
187
+ switch_to_blog( $blog_id );
188
+ $this->_activate_plugin( $blog_id );
189
+
190
+ }
191
+
192
+ restore_current_blog();
193
+
194
+ } else
195
+ $this->_activate_plugin( $wpdb->blogid ); // activated on a single site, in a multi-site
196
+
197
+ } else
198
+ $this->_activate_plugin( $wpdb->blogid ); // activated on a single site
199
+
200
+ }
201
+
202
+ /**
203
+ * Method to initialize a newly created site in a multi site set up.
204
+ *
205
+ * @since 3.0.0
206
+ * @access public
207
+ *
208
+ * @param int $blogid Blog ID of the created blog.
209
+ * @param int $user_id User ID of the user creating the blog.
210
+ * @param string $domain Domain used for the new blog.
211
+ * @param string $path Path to the new blog.
212
+ * @param int $site_id Site ID. Only relevant on multi-network installs.
213
+ * @param array $meta Meta data. Used to set initial site options.
214
+ */
215
+ public function new_mu_site_init( $blog_id , $user_id , $domain , $path , $site_id , $meta ) {
216
+
217
+ if ( is_plugin_active_for_network( 'thirstyaffiliates/thirstyaffiliates.php' ) ) {
218
+
219
+ switch_to_blog( $blog_id );
220
+ $this->_activate_plugin( $blog_id );
221
+ restore_current_blog();
222
+
223
+ }
224
+
225
+ }
226
+
227
+ /**
228
+ * Initialize plugin settings options.
229
+ * This is a compromise to my idea of 'Modularity'. Ideally, bootstrap should not take care of plugin settings stuff.
230
+ * However due to how WooCommerce do its thing, we need to do it this way. We can't separate settings on its own.
231
+ *
232
+ * @since 3.0.0
233
+ * @access private
234
+ */
235
+ private function _initialize_plugin_settings_options() {
236
+
237
+ // Help settings section options
238
+
239
+ // Set initial value of 'no' for the option that sets the option that specify whether to delete the options on plugin uninstall. Optionception.
240
+ if ( !get_option( Plugin_Constants::CLEAN_UP_PLUGIN_OPTIONS , false ) )
241
+ update_option( Plugin_Constants::CLEAN_UP_PLUGIN_OPTIONS , 'no' );
242
+
243
+ // set redirect type default to '302' on activation.
244
+ if ( ! get_option( 'ta_link_redirect_type' , false ) )
245
+ update_option( 'ta_link_redirect_type' , 302 );
246
+
247
+ }
248
+
249
+ /**
250
+ * Actual function that houses the code to execute on plugin activation.
251
+ *
252
+ * @since 3.0.0
253
+ * @since 3.0.0 Added the ta_activation_date option
254
+ * @access private
255
+ *
256
+ * @param int $blogid Blog ID of the created blog.
257
+ */
258
+ private function _activate_plugin( $blogid ) {
259
+
260
+ // Initialize settings options
261
+ $this->_initialize_plugin_settings_options();
262
+
263
+ // Create database tables
264
+ $this->_create_database_tables();
265
+
266
+ // set flush rewrite rules transient so it can be flushed after the CPT and rewrite rules have been registered.
267
+ set_transient( 'ta_flush_rewrite_rules' , 'true' , 5 * 60 );
268
+
269
+ // Execute 'activate' contract of models implementing ThirstyAffiliates\Interfaces\Activatable_Interface
270
+ foreach ( $this->_activatables as $activatable )
271
+ if ( $activatable instanceof Activatable_Interface )
272
+ $activatable->activate();
273
+
274
+ update_option( Plugin_Constants::INSTALLED_VERSION , Plugin_Constants::VERSION ); // Update current installed plugin version
275
+ update_option( 'ta_activation_code_triggered' , 'yes' ); // Mark activation code triggered
276
+
277
+ }
278
+
279
+ /**
280
+ * Method that houses the logic relating to deactivating the plugin.
281
+ *
282
+ * @since 3.0.0
283
+ * @access public
284
+ *
285
+ * @global wpdb $wpdb Object that contains a set of functions used to interact with a database.
286
+ *
287
+ * @param boolean $network_wide Flag that determines whether the plugin has been activated network wid ( on multi site environment ) or not.
288
+ */
289
+ public function deactivate_plugin( $network_wide ) {
290
+
291
+ global $wpdb;
292
+
293
+ // check if it is a multisite network
294
+ if ( is_multisite() ) {
295
+
296
+ // check if the plugin has been activated on the network or on a single site
297
+ if ( $network_wide ) {
298
+
299
+ // get ids of all sites
300
+ $blog_ids = $wpdb->get_col( "SELECT blog_id FROM $wpdb->blogs" );
301
+
302
+ foreach ( $blog_ids as $blog_id ) {
303
+
304
+ switch_to_blog( $blog_id );
305
+ $this->_deactivate_plugin( $wpdb->blogid );
306
+
307
+ }
308
+
309
+ restore_current_blog();
310
+
311
+ } else
312
+ $this->_deactivate_plugin( $wpdb->blogid ); // activated on a single site, in a multi-site
313
+
314
+ } else
315
+ $this->_deactivate_plugin( $wpdb->blogid ); // activated on a single site
316
+
317
+ }
318
+
319
+ /**
320
+ * Actual method that houses the code to execute on plugin deactivation.
321
+ *
322
+ * @since 3.0.0
323
+ * @access private
324
+ *
325
+ * @param int $blogid Blog ID of the created blog.
326
+ */
327
+ private function _deactivate_plugin( $blogid ) {
328
+
329
+ // Execute 'deactivate' contract of models implementing ThirstyAffiliates\Interfaces\Deactivatable_Interface
330
+ foreach ( $this->_deactivatables as $deactivatable )
331
+ if ( $deactivatable instanceof Deactivatable_Interface )
332
+ $deactivatable->deactivate();
333
+
334
+ flush_rewrite_rules();
335
+
336
+ }
337
+
338
+ /**
339
+ * Create database tables.
340
+ *
341
+ * @since 3.0.0
342
+ * @access private
343
+ */
344
+ private function _create_database_tables() {
345
+
346
+ global $wpdb;
347
+
348
+ if ( get_option( 'ta_database_tables_created' ) === 'yes' )
349
+ return;
350
+
351
+ $link_click_db = $wpdb->prefix . Plugin_Constants::LINK_CLICK_DB;
352
+ $link_click_meta_db = $wpdb->prefix . Plugin_Constants::LINK_CLICK_META_DB;
353
+ $charset_collate = $wpdb->get_charset_collate();
354
+
355
+ // link clicks db sql command
356
+ $sql = "CREATE TABLE $link_click_db (
357
+ id bigint(20) NOT NULL AUTO_INCREMENT,
358
+ link_id bigint(20) NOT NULL,
359
+ date_clicked datetime DEFAULT '0000-00-00 00:00:00' NOT NULL,
360
+ PRIMARY KEY (id)
361
+ ) $charset_collate;\n";
362
+
363
+ // link clicks meta db sql command
364
+ $sql .= "CREATE TABLE $link_click_meta_db (
365
+ id bigint(20) NOT NULL AUTO_INCREMENT,
366
+ click_id bigint(20) NOT NULL,
367
+ meta_key varchar(255) NULL,
368
+ meta_value longtext NULL,
369
+ PRIMARY KEY (id)
370
+ ) $charset_collate;";
371
+
372
+ require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
373
+ dbDelta( $sql );
374
+
375
+ update_option( 'ta_database_tables_created' , 'yes' );
376
+ }
377
+
378
+ /**
379
+ * Add custom links to the plugin's action links.
380
+ *
381
+ * @since 3.0.0
382
+ * @access public
383
+ */
384
+ public function plugin_action_links( $links ) {
385
+
386
+ $settings_link = admin_url( 'edit.php?post_type=' . Plugin_Constants::AFFILIATE_LINKS_CPT . '&page=thirsty-settings' );
387
+
388
+ $new_links = array(
389
+ '<a href="' . $settings_link . '">' . __( 'Settings' , 'thirstyaffiliates' ) . '</a>'
390
+ );
391
+
392
+ return array_merge( $new_links , $links );
393
+ }
394
+
395
+ /**
396
+ * Control admin interface visibility.
397
+ *
398
+ * @since 3.3.2
399
+ * @access public
400
+ */
401
+ public function admin_interface_visibility() {
402
+
403
+ $object_id = isset( $_GET[ 'post' ] ) ? absint( $_GET[ 'post' ] ) : 0;
404
+
405
+ if ( ! $screen_id = $this->_helper_functions->get_screen_id( $object_id ) )
406
+ return;
407
+
408
+ $current_tab = isset( $_GET[ 'tab' ] ) ? $_GET[ 'tab' ] : '';
409
+ $post_type = isset( $_GET[ 'post_type' ] ) ? sanitize_text_field( $_GET[ 'post_type' ] ) : '';
410
+ $interfaces = apply_filters( 'ta_admin_interfaces' , array() );
411
+ $current_interface = $screen_id && isset( $interfaces[ $screen_id ] ) ? $interfaces[ $screen_id ] : null;
412
+
413
+ // if interface is not present then don't proceed
414
+ if ( ! $current_interface || empty( $current_interface ) ) return;
415
+ if ( $current_tab && ! isset( $current_interface[ $current_tab ] ) ) return;
416
+
417
+ // get the capability allowed for the interface
418
+ if ( is_array( $current_interface ) )
419
+ $capability = $current_tab ? $current_interface[ $current_tab ] : array_values( $current_interface )[0];
420
+ else
421
+ $capability = $current_interface;
422
+
423
+ if ( $current_tab === 'ta_import_export_settings' )
424
+ $capability = 'manage_options';
425
+
426
+ // get error message.
427
+ $error_message = apply_filters( 'ta_admin_interface_error_message' , __( "Sorry, you are not allowed to access this page." , 'thirstyaffiliates' ) , $screen_id , $current_tab , $capability , $current_interface );
428
+
429
+ // kill page display error message if current user does not have capability.
430
+ if ( ( $capability && ! current_user_can( $capability ) ) || ( $object_id && isset( $_GET[ 'post' ] ) && get_current_user_id() != get_post_field( 'post_author' , $object_id ) && ! current_user_can( 'edit_others_posts' ) ) )
431
+ wp_die( $error_message );
432
+ }
433
+
434
+ /**
435
+ * Control admin menu items visibility.
436
+ *
437
+ * @since 3.3.2
438
+ * @access public
439
+ */
440
+ public function admin_menu_items_visibilty() {
441
+
442
+ global $submenu;
443
+
444
+ $menu_slug = 'edit.php?post_type=' . Plugin_Constants::AFFILIATE_LINKS_CPT;
445
+ $menu_items = apply_filters( 'ta_menu_items' , array() );
446
+ $main_cap = null;
447
+
448
+ if ( ! is_array( $menu_items ) || empty( $menu_items ) ) return;
449
+
450
+ foreach ( $menu_items as $submenu_slug => $capability ) {
451
+
452
+ if ( $submenu_slug == $menu_slug ) {
453
+ $main_cap = $capability;
454
+ continue;
455
+ }
456
+
457
+ if ( $capability && ! current_user_can( $capability ) )
458
+ remove_submenu_page( $menu_slug , esc_attr( $submenu_slug ) );
459
+ }
460
+
461
+ $menu_count = isset( $submenu[ $menu_slug ] ) ? count( $submenu[ $menu_slug ] ) : 0;
462
+
463
+ if ( ( ! $main_cap || ! current_user_can( $main_cap ) ) && $menu_count <= 1 )
464
+ remove_menu_page( $menu_slug );
465
+ }
466
+
467
+ /**
468
+ * Method that houses codes to be executed on init hook.
469
+ *
470
+ * @since 3.0.0
471
+ * @access public
472
+ */
473
+ public function initialize() {
474
+
475
+ // Execute activation codebase if not yet executed on plugin activation ( Mostly due to plugin dependencies )
476
+ if ( get_option( 'ta_activation_code_triggered' , false ) !== 'yes' ) {
477
+
478
+ if ( ! function_exists( 'is_plugin_active_for_network' ) )
479
+ require_once( ABSPATH . '/wp-admin/includes/plugin.php' );
480
+
481
+ $network_wide = is_plugin_active_for_network( 'thirstyaffiliates/thirstyaffiliates.php' );
482
+ $this->activate_plugin( $network_wide );
483
+
484
+ }
485
+
486
+ // Execute 'initialize' contract of models implementing ThirstyAffiliates\Interfaces\Initiable_Interface
487
+ foreach ( $this->_initiables as $initiable )
488
+ if ( $initiable instanceof Initiable_Interface )
489
+ $initiable->initialize();
490
+
491
+ }
492
+
493
+ /**
494
+ * Execute plugin bootstrap code.
495
+ *
496
+ * @since 3.0.0
497
+ * @access public
498
+ */
499
+ public function run() {
500
+
501
+ // Internationalization
502
+ add_action( 'plugins_loaded' , array( $this , 'load_plugin_textdomain' ) );
503
+
504
+ // Execute plugin activation/deactivation
505
+ register_activation_hook( $this->_constants->MAIN_PLUGIN_FILE_PATH() , array( $this , 'activate_plugin' ) );
506
+ register_deactivation_hook( $this->_constants->MAIN_PLUGIN_FILE_PATH() , array( $this , 'deactivate_plugin' ) );
507
+
508
+ // Execute plugin initialization ( plugin activation ) on every newly created site in a multi site set up
509
+ add_action( 'wpmu_new_blog' , array( $this , 'new_mu_site_init' ) , 10 , 6 );
510
+
511
+ add_filter( 'plugin_action_links_' . $this->_constants->PLUGIN_BASENAME() , array( $this , 'plugin_action_links' ) );
512
+
513
+ // Execute codes that need to run on 'init' hook
514
+ add_action( 'init' , array( $this , 'initialize' ) );
515
+
516
+ add_action( 'init' , array( $this , 'admin_interface_visibility' ) );
517
+ add_action( 'admin_menu' , array( $this , 'admin_menu_items_visibilty' ) , 20 );
518
+ }
519
+
520
+ }
Models/Guided_Tour.php CHANGED
@@ -1,389 +1,389 @@
1
- <?php
2
-
3
- namespace ThirstyAffiliates\Models;
4
-
5
- use ThirstyAffiliates\Abstracts\Abstract_Main_Plugin_Class;
6
-
7
- use ThirstyAffiliates\Interfaces\Activatable_Interface;
8
- use ThirstyAffiliates\Interfaces\Initiable_Interface;
9
- use ThirstyAffiliates\Interfaces\Model_Interface;
10
-
11
- use ThirstyAffiliates\Helpers\Plugin_Constants;
12
- use ThirstyAffiliates\Helpers\Helper_Functions;
13
-
14
- /**
15
- * Model that houses the logic for permalink rewrites and affiliate link redirections.
16
- *
17
- * @since 3.0.0
18
- */
19
- class Guided_Tour implements Model_Interface , Activatable_Interface , Initiable_Interface {
20
-
21
- /*
22
- |--------------------------------------------------------------------------
23
- | Class Properties
24
- |--------------------------------------------------------------------------
25
- */
26
-
27
- /**
28
- * Property that holds the single main instance of Shortcodes.
29
- *
30
- * @since 3.0.0
31
- * @access private
32
- * @var Redirection
33
- */
34
- private static $_instance;
35
-
36
- /**
37
- * Model that houses the main plugin object.
38
- *
39
- * @since 3.0.0
40
- * @access private
41
- * @var Redirection
42
- */
43
- private $_main_plugin;
44
-
45
- /**
46
- * Model that houses all the plugin constants.
47
- *
48
- * @since 3.0.0
49
- * @access private
50
- * @var Plugin_Constants
51
- */
52
- private $_constants;
53
-
54
- /**
55
- * Property that houses all the helper functions of the plugin.
56
- *
57
- * @since 3.0.0
58
- * @access private
59
- * @var Helper_Functions
60
- */
61
- private $_helper_functions;
62
-
63
- /**
64
- * Property that urls of the guided tour screens.
65
- *
66
- * @since 3.0.0
67
- * @access private
68
- * @var array
69
- */
70
- private $_urls = array();
71
-
72
- /**
73
- * Property that houses the screens of the guided tour.
74
- *
75
- * @since 3.0.0
76
- * @access private
77
- * @var array
78
- */
79
- private $_screens = array();
80
-
81
-
82
-
83
-
84
- /*
85
- |--------------------------------------------------------------------------
86
- | Class Methods
87
- |--------------------------------------------------------------------------
88
- */
89
-
90
- /**
91
- * Class constructor.
92
- *
93
- * @since 3.0.0
94
- * @access public
95
- *
96
- * @param Abstract_Main_Plugin_Class $main_plugin Main plugin object.
97
- * @param Plugin_Constants $constants Plugin constants object.
98
- * @param Helper_Functions $helper_functions Helper functions object.
99
- */
100
- public function __construct( Abstract_Main_Plugin_Class $main_plugin , Plugin_Constants $constants , Helper_Functions $helper_functions ) {
101
-
102
- $this->_constants = $constants;
103
- $this->_helper_functions = $helper_functions;
104
-
105
- $main_plugin->add_to_all_plugin_models( $this );
106
- }
107
-
108
- /**
109
- * Ensure that only one instance of this class is loaded or can be loaded ( Singleton Pattern ).
110
- *
111
- * @since 3.0.0
112
- * @access public
113
- *
114
- * @param Abstract_Main_Plugin_Class $main_plugin Main plugin object.
115
- * @param Plugin_Constants $constants Plugin constants object.
116
- * @param Helper_Functions $helper_functions Helper functions object.
117
- * @return Redirection
118
- */
119
- public static function get_instance( Abstract_Main_Plugin_Class $main_plugin , Plugin_Constants $constants , Helper_Functions $helper_functions ) {
120
-
121
- if ( !self::$_instance instanceof self )
122
- self::$_instance = new self( $main_plugin , $constants , $helper_functions );
123
-
124
- return self::$_instance;
125
-
126
- }
127
-
128
- /**
129
- * Define guided tour pages.
130
- *
131
- * @since 3.0.0
132
- * @access private
133
- */
134
- private function define_guided_tour_pages() {
135
-
136
- $this->_urls = apply_filters( 'ta_guided_tour_pages' , array(
137
- 'plugin-listing' => admin_url( 'plugins.php' ),
138
- 'affiliate-links-listing' => admin_url( 'edit.php?post_type=thirstylink' ),
139
- 'new-wp-post' => admin_url( 'post-new.php' ),
140
- 'general-settings' => admin_url( 'edit.php?post_type=thirstylink&page=thirsty-settings' ),
141
- 'link-apperance-settings' => admin_url( 'edit.php?post_type=thirstylink&page=thirsty-settings&tab=ta_links_settings' ),
142
- 'modules-settings' => admin_url( 'edit.php?post_type=thirstylink&page=thirsty-settings&tab=ta_modules_settings' ),
143
- 'import-export-settings' => admin_url( 'edit.php?post_type=thirstylink&page=thirsty-settings&tab=ta_import_export_settings' ),
144
- 'help-settings' => admin_url( 'edit.php?post_type=thirstylink&page=thirsty-settings&tab=ta_help_settings' ),
145
- 'new-affiliate-link' => admin_url( 'post-new.php?post_type=thirstylink' ),
146
- ) );
147
-
148
- $this->_screens = apply_filters( 'ta_guided_tours' , array(
149
- 'plugins' => array(
150
- 'elem' => '#menu-posts-thirstylink .menu-top',
151
- 'html' => __( '<h3>Congratulations, you just activated ThirstyAffiliates!</h3>
152
- <p>Would you like to take a tour of the plugin features? It takes less than a minute and you\'ll then know exactly how to use the plugin.</p>', 'thirstyaffiliates' ),
153
- 'prev' => null,
154
- 'next' => $this->_urls[ 'affiliate-links-listing' ],
155
- 'edge' => 'left',
156
- 'align' => 'left',
157
- ),
158
- 'edit-thirstylink' => array(
159
- 'elem' => '#wpbody-content > .wrap > .wp-heading-inline',
160
- 'html' => __( '<h3>ThirstyAffiliates helps you manage affiliate links that you are given from the various affiliate programs you are a member of.</h3>
161
- <p>It lets you hide long, and often confusing looking, affiliate link URLs behind another URL called a redirect. When your visitors click on that new URL they are automatically redirected to your affiliate link.</p>
162
- <p>This is helpful for five reasons:</p>
163
- <ol><li>AESTHETICS: Affiliate links are often long and ugly as mentioned and this can look off putting to visitors. ThirstyAffiliates redirects make them shorter and more attractive to click on like "http://example.com/recommends/some-product-name"</li>
164
- <li>PROTECTION: It hides the affiliate code, so malicious software (malware) cannot sniff out the affiliate code for common affiliate programs and replace it with their own code instead. In this way, it protects your commissions.</li>
165
- <li>CONVENIENCE: If the affiliate program ever changes the link code you’ll only have ONE place to change it on your blog, rather than going through all your content and changing every instance of the link.</li>
166
- <li>CATEGORIZATION: You categorize your affiliate links into logical groups which can make managing them much simpler.</li>
167
- <li>STATISTICS: Finally, because your visitors are travelling via a ThirstyAffiliates redirect, the plugin can provide you with statistics on what is being clicked.</li></ol>
168
- <p>You can now create an “Affiliate Link” in this new section in your dashboard. This view shows you all the affiliate links you are managing with ThirstyAffiliates.</p>', 'thirstyaffiliates' ),
169
- 'prev' => $this->_urls[ 'plugin-listing' ],
170
- 'next' => $this->_urls[ 'new-wp-post' ],
171
- 'edge' => 'top',
172
- 'align' => 'left',
173
- 'width' => 600
174
- ),
175
- 'post' => array(
176
- 'elem' => '#wpbody #insert-media-button',
177
- 'html' => __( '<h3>Affiliate links can be added to your posts easily by clicking on the “TA” button on your editor.</h3>
178
- <p>This works identically to the WordPress link tool, but only searches for affiliate links.</p>
179
- <p>Give it a try by typing in some text, highlighting it, then clicking the “TA” button.</p>
180
- <p>If you need to you can click the cog icon for an advanced search view which is handy for doing more advanced searches and for inserting images pre-wrapped with your affiliate link or via a shortcode instead.</p>', 'thirstyaffiliates' ),
181
- 'prev' => $this->_urls[ 'affiliate-links-listing' ],
182
- 'next' => $this->_urls[ 'general-settings' ],
183
- 'edge' => 'top',
184
- 'align' => 'left',
185
- 'width' => 400
186
- ),
187
- 'thirstylink_page_thirsty-settings' => array(
188
- 'ta_general_settings' => array(
189
- 'elem' => '.nav-tab-wrapper .ta_general_settings',
190
- 'html' => __( '<h3>ThirstyAffiliates has a number of settings that change the way it works, behaves and how your links appear.</h3>
191
- <p>Here are the General settings which are for changing the way you work with ThirstyAffiliates in the backend.</p>', 'thirstyaffiliates' ),
192
- 'prev' => $this->_urls[ 'new-wp-post' ],
193
- 'next' => $this->_urls[ 'link-apperance-settings' ],
194
- 'edge' => 'top',
195
- 'align' => 'left',
196
- ),
197
- 'ta_links_settings' => array(
198
- 'elem' => '.nav-tab-wrapper .ta_links_settings',
199
- 'html' => __( '<h3>One of the most important parts of the settings area is Link Appearance which changes the way your links look to your visitors.</h3>
200
- <p>This includes the Link Prefix setting which is important to decide on before you start using ThirstyAffiliates.</p>
201
- <p>By default, this is set to “recommends” so your links will look like “http://example.com/recommends/some-product-name”.</p>
202
- <p>You can also choose to include the category slug in the URL, change the way ThirstyAffiliates redirects links, add no follow, make links open in a new window and more.</p>', 'thirstyaffiliates' ),
203
- 'prev' => $this->_urls[ 'general-settings' ],
204
- 'next' => $this->_urls[ 'modules-settings' ],
205
- 'edge' => 'top',
206
- 'align' => 'left',
207
- 'width' => 450
208
- ),
209
- 'ta_modules_settings' => array(
210
- 'elem' => '.nav-tab-wrapper .ta_modules_settings',
211
- 'html' => __( '<h3>We built ThirstyAffiliates to be flexible and as such, you can shut down the parts of ThirstyAffiliates that aren’t being used. This can make the plugin faster.</h3>
212
- <p>If you have the Pro add-on each Pro module will show in here as well.</p>', 'thirstyaffiliates' ),
213
- 'prev' => $this->_urls[ 'link-apperance-settings' ],
214
- 'next' => $this->_urls[ 'import-export-settings' ],
215
- 'edge' => 'top',
216
- 'align' => 'left',
217
- ),
218
- 'ta_import_export_settings' => array(
219
- 'elem' => '.nav-tab-wrapper .ta_import_export_settings',
220
- 'html' => __( '<h3>Setting up multiple sites with all running ThirstyAffiliates?</h3>
221
- <p>We’ve made it super simple to configure your additional sites by being able to import and export ThirstyAffiliate settings.</p>
222
- <p>Just copy the Export section and paste it into the Import section on your other site and ThirstyAffiliates will be automatically configured the same way.</p>', 'thirstyaffiliates' ),
223
- 'prev' => $this->_urls[ 'modules-settings' ],
224
- 'next' => $this->_urls[ 'help-settings' ],
225
- 'edge' => 'top',
226
- 'align' => 'right',
227
- ),
228
- 'ta_help_settings' => array(
229
- 'elem' => '.nav-tab-wrapper .ta_help_settings',
230
- 'html' => __( '<h3>Need some help with ThirstyAffiliates?</h3>
231
- <p>We have a growing knowledge base filled with guides, troubleshooting and FAQ.</p>
232
- <p>Our blog is also very active with lots of interesting affiliate marketing topics to help you grow your affiliate marketing empire.</p>', 'thirstyaffiliates' ),
233
- 'prev' => $this->_urls[ 'import-export-settings' ],
234
- 'next' => $this->_urls[ 'new-affiliate-link' ],
235
- 'edge' => 'top',
236
- 'align' => 'right',
237
- )
238
- ),
239
- 'thirstylink' => array(
240
- 'elem' => '#menu-posts-thirstylink',
241
- 'html' => __( '<h3>
This concludes the guide. You are now ready to setup your first affiliate link!</h3>
242
- <p>We also have a Pro add-on for ThirstyAffiliates which contains lots of interesting features for affiliates like:</p>
243
- <ul><li>Automatically link up your affiliate links to keywords in your blog</li>
244
- <li>Get more detailed and advanced reports</li>
245
- <li>Geolocated affiliate links</li>
246
- <li>Google Analytics integration</li>
247
- <li>CSV importing/exporting, Amazon importing, and more</li>
248
- <li>Link event notification admin emails</li>
249
- <li>Automatic link health checker</li></ul>
250
- <p>Want to unlock all of the extra features you see here? The Pro add-on is for you. And we’re adding new features all the time!</p>', 'thirstyaffiliates' ),
251
- 'prev' => $this->_urls[ 'help-settings' ],
252
- 'next' => null,
253
- 'edge' => 'left',
254
- 'align' => 'left',
255
- 'width' => 620,
256
- 'btn_tour_done' => __( 'Check out the current Pro add-on features' , 'thirstyaffiliates' ),
257
- 'btn_tour_done_url' => 'https://thirstyaffiliates.com/pricing?utm_source=Free%20Plugin&utm_medium=Tour&utm_campaign=Pro%20Link'
258
- ),
259
- ) );
260
- }
261
-
262
- /**
263
- * Get current screen.
264
- *
265
- * @since 3.0.0
266
- * @access public
267
- *
268
- * @return array Current guide tour screen.
269
- */
270
- public function get_current_screen() {
271
-
272
- $screen = get_current_screen();
273
- $tab = isset( $_GET[ 'tab' ] ) ? sanitize_text_field( $_GET[ 'tab' ] ) : '';
274
-
275
- if ( ! isset( $this->_screens[ $screen->id ] ) || empty( $this->_screens[ $screen->id ] ) )
276
- return;
277
-
278
- if ( $screen->id == 'thirstylink_page_thirsty-settings' ) {
279
-
280
- if( $tab && isset( $this->_screens[ $screen->id ][ $tab ] ) )
281
- return $this->_screens[ $screen->id ][ $tab ];
282
- elseif ( ! isset( $_GET[ 'tab' ] ) )
283
- return $this->_screens[ $screen->id ][ 'ta_general_settings' ];
284
- else
285
- return array();
286
- }
287
-
288
- return $this->_screens[ $screen->id ];
289
- }
290
-
291
- /**
292
- * Get all guide tour screens.
293
- *
294
- * @since 3.0.0
295
- * @access public
296
- *
297
- * @return array List of all guide tour screens.
298
- */
299
- public function get_screens() {
300
-
301
- return $this->_screens;
302
- }
303
-
304
- /**
305
- * AJAX close guided tour.
306
- *
307
- * @since 3.0.0
308
- * @access public
309
- */
310
- public function ajax_close_guided_tour() {
311
-
312
- if ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX )
313
- $response = array( 'status' => 'fail' , 'error_msg' => __( 'Invalid AJAX call' , 'thirstyaffiliates' ) );
314
- elseif ( ! check_ajax_referer( 'ta-close-guided-tour' , 'nonce' , false ) )
315
- $response = array( 'status' => 'fail' , 'error_msg' => __( 'Security Check Failed' , 'thirstyaffiliates' ) );
316
- else {
317
-
318
- update_option( 'ta_guided_tour_status' , 'close' );
319
- $response = array( 'status' => 'success' );
320
- }
321
-
322
- @header( 'Content-Type: application/json; charset=' . get_option( 'blog_charset' ) );
323
- echo wp_json_encode( $response );
324
- wp_die();
325
- }
326
-
327
- /**
328
- * Set the guided tour status option as 'open' on activation.
329
- *
330
- * @since 3.0.0
331
- * @access private
332
- */
333
- private function set_guided_tour_status_open() {
334
-
335
- if ( get_option( 'ta_guided_tour_status' ) )
336
- return;
337
-
338
- update_option( 'ta_guided_tour_status' , 'open' );
339
- }
340
-
341
-
342
-
343
-
344
- /*
345
- |--------------------------------------------------------------------------
346
- | Implemented Interface Methods
347
- |--------------------------------------------------------------------------
348
- */
349
-
350
- /**
351
- * Execute codes that needs to run plugin activation.
352
- *
353
- * @since 3.0.0
354
- * @access public
355
- * @implements ThirstyAffiliates\Interfaces\Activatable_Interface
356
- */
357
- public function activate() {
358
-
359
- $this->set_guided_tour_status_open();
360
- }
361
-
362
- /**
363
- * Method that houses codes to be executed on init hook.
364
- *
365
- * @since 3.0.0
366
- * @access public
367
- * @inherit ThirstyAffiliates\Interfaces\Initiable_Interface
368
- */
369
- public function initialize() {
370
-
371
- add_action( 'wp_ajax_ta_close_guided_tour' , array( $this , 'ajax_close_guided_tour' ) );
372
- }
373
-
374
- /**
375
- * Execute model.
376
- *
377
- * @implements ThirstyAffiliates\Interfaces\Model_Interface
378
- *
379
- * @since 3.0.0
380
- * @access public
381
- */
382
- public function run() {
383
-
384
- if ( get_option( 'ta_guided_tour_status' ) !== 'open' )
385
- return;
386
-
387
- $this->define_guided_tour_pages();
388
- }
389
- }
1
+ <?php
2
+
3
+ namespace ThirstyAffiliates\Models;
4
+
5
+ use ThirstyAffiliates\Abstracts\Abstract_Main_Plugin_Class;
6
+
7
+ use ThirstyAffiliates\Interfaces\Activatable_Interface;
8
+ use ThirstyAffiliates\Interfaces\Initiable_Interface;
9
+ use ThirstyAffiliates\Interfaces\Model_Interface;
10
+
11
+ use ThirstyAffiliates\Helpers\Plugin_Constants;
12
+ use ThirstyAffiliates\Helpers\Helper_Functions;
13
+
14
+ /**
15
+ * Model that houses the logic for permalink rewrites and affiliate link redirections.
16
+ *
17
+ * @since 3.0.0
18
+ */
19
+ class Guided_Tour implements Model_Interface , Activatable_Interface , Initiable_Interface {
20
+
21
+ /*
22
+ |--------------------------------------------------------------------------
23
+ | Class Properties
24
+ |--------------------------------------------------------------------------
25
+ */
26
+
27
+ /**
28
+ * Property that holds the single main instance of Shortcodes.
29
+ *
30
+ * @since 3.0.0
31
+ * @access private
32
+ * @var Redirection
33
+ */
34
+ private static $_instance;
35
+
36
+ /**
37
+ * Model that houses the main plugin object.
38
+ *
39
+ * @since 3.0.0
40
+ * @access private
41
+ * @var Redirection
42
+ */
43
+ private $_main_plugin;
44
+
45
+ /**
46
+ * Model that houses all the plugin constants.
47
+ *
48
+ * @since 3.0.0
49
+ * @access private
50
+ * @var Plugin_Constants
51
+ */
52
+ private $_constants;
53
+
54
+ /**
55
+ * Property that houses all the helper functions of the plugin.
56
+ *
57
+ * @since 3.0.0
58
+ * @access private
59
+ * @var Helper_Functions
60
+ */
61
+ private $_helper_functions;
62
+
63
+ /**
64
+ * Property that urls of the guided tour screens.
65
+ *
66
+ * @since 3.0.0
67
+ * @access private
68
+ * @var array
69
+ */
70
+ private $_urls = array();
71
+
72
+ /**
73
+ * Property that houses the screens of the guided tour.
74
+ *
75
+ * @since 3.0.0
76
+ * @access private
77
+ * @var array
78
+ */
79
+ private $_screens = array();
80
+
81
+
82
+
83
+
84
+ /*
85
+ |--------------------------------------------------------------------------
86
+ | Class Methods
87
+ |--------------------------------------------------------------------------
88
+ */
89
+
90
+ /**
91
+ * Class constructor.
92
+ *
93
+ * @since 3.0.0
94
+ * @access public
95
+ *
96
+ * @param Abstract_Main_Plugin_Class $main_plugin Main plugin object.
97
+ * @param Plugin_Constants $constants Plugin constants object.
98
+ * @param Helper_Functions $helper_functions Helper functions object.
99
+ */
100
+ public function __construct( Abstract_Main_Plugin_Class $main_plugin , Plugin_Constants $constants , Helper_Functions $helper_functions ) {
101
+
102
+ $this->_constants = $constants;
103
+ $this->_helper_functions = $helper_functions;
104
+
105
+ $main_plugin->add_to_all_plugin_models( $this );
106
+ }
107
+
108
+ /**
109
+ * Ensure that only one instance of this class is loaded or can be loaded ( Singleton Pattern ).
110
+ *
111
+ * @since 3.0.0
112
+ * @access public
113
+ *
114
+ * @param Abstract_Main_Plugin_Class $main_plugin Main plugin object.
115
+ * @param Plugin_Constants $constants Plugin constants object.
116
+ * @param Helper_Functions $helper_functions Helper functions object.
117
+ * @return Redirection
118
+ */
119
+ public static function get_instance( Abstract_Main_Plugin_Class $main_plugin , Plugin_Constants $constants , Helper_Functions $helper_functions ) {
120
+
121
+ if ( !self::$_instance instanceof self )
122
+ self::$_instance = new self( $main_plugin , $constants , $helper_functions );
123
+
124
+ return self::$_instance;
125
+
126
+ }
127
+
128
+ /**
129
+ * Define guided tour pages.
130
+ *
131
+ * @since 3.0.0
132
+ * @access private
133
+ */
134
+ private function define_guided_tour_pages() {
135
+
136
+ $this->_urls = apply_filters( 'ta_guided_tour_pages' , array(
137
+ 'plugin-listing' => admin_url( 'plugins.php' ),
138
+ 'affiliate-links-listing' => admin_url( 'edit.php?post_type=thirstylink' ),
139
+ 'new-wp-post' => admin_url( 'post-new.php' ),
140
+ 'general-settings' => admin_url( 'edit.php?post_type=thirstylink&page=thirsty-settings' ),
141
+ 'link-apperance-settings' => admin_url( 'edit.php?post_type=thirstylink&page=thirsty-settings&tab=ta_links_settings' ),
142
+ 'modules-settings' => admin_url( 'edit.php?post_type=thirstylink&page=thirsty-settings&tab=ta_modules_settings' ),
143
+ 'import-export-settings' => admin_url( 'edit.php?post_type=thirstylink&page=thirsty-settings&tab=ta_import_export_settings' ),
144
+ 'help-settings' => admin_url( 'edit.php?post_type=thirstylink&page=thirsty-settings&tab=ta_help_settings' ),
145
+ 'new-affiliate-link' => admin_url( 'post-new.php?post_type=thirstylink' ),
146
+ ) );
147
+
148
+ $this->_screens = apply_filters( 'ta_guided_tours' , array(
149
+ 'plugins' => array(
150
+ 'elem' => '#menu-posts-thirstylink .menu-top',
151
+ 'html' => __( '<h3>Congratulations, you just activated ThirstyAffiliates!</h3>
152
+ <p>Would you like to take a tour of the plugin features? It takes less than a minute and you\'ll then know exactly how to use the plugin.</p>', 'thirstyaffiliates' ),
153
+ 'prev' => null,
154
+ 'next' => $this->_urls[ 'affiliate-links-listing' ],
155
+ 'edge' => 'left',
156
+ 'align' => 'left',
157
+ ),
158
+ 'edit-thirstylink' => array(
159
+ 'elem' => '#wpbody-content > .wrap > .wp-heading-inline',
160
+ 'html' => __( '<h3>ThirstyAffiliates helps you manage affiliate links that you are given from the various affiliate programs you are a member of.</h3>
161
+ <p>It lets you hide long, and often confusing looking, affiliate link URLs behind another URL called a redirect. When your visitors click on that new URL they are automatically redirected to your affiliate link.</p>
162
+ <p>This is helpful for five reasons:</p>
163
+ <ol><li>AESTHETICS: Affiliate links are often long and ugly as mentioned and this can look off putting to visitors. ThirstyAffiliates redirects make them shorter and more attractive to click on like "http://example.com/recommends/some-product-name"</li>
164
+ <li>PROTECTION: It hides the affiliate code, so malicious software (malware) cannot sniff out the affiliate code for common affiliate programs and replace it with their own code instead. In this way, it protects your commissions.</li>
165
+ <li>CONVENIENCE: If the affiliate program ever changes the link code you’ll only have ONE place to change it on your blog, rather than going through all your content and changing every instance of the link.</li>
166
+ <li>CATEGORIZATION: You categorize your affiliate links into logical groups which can make managing them much simpler.</li>
167
+ <li>STATISTICS: Finally, because your visitors are travelling via a ThirstyAffiliates redirect, the plugin can provide you with statistics on what is being clicked.</li></ol>
168
+ <p>You can now create an “Affiliate Link” in this new section in your dashboard. This view shows you all the affiliate links you are managing with ThirstyAffiliates.</p>', 'thirstyaffiliates' ),
169
+ 'prev' => $this->_urls[ 'plugin-listing' ],
170
+ 'next' => $this->_urls[ 'new-wp-post' ],
171
+ 'edge' => 'top',
172
+ 'align' => 'left',
173
+ 'width' => 600
174
+ ),
175
+ 'post' => array(
176
+ 'elem' => '#wpbody #insert-media-button',
177
+ 'html' => __( '<h3>Affiliate links can be added to your posts easily by clicking on the “TA” button on your editor.</h3>
178
+ <p>This works identically to the WordPress link tool, but only searches for affiliate links.</p>
179
+ <p>Give it a try by typing in some text, highlighting it, then clicking the “TA” button.</p>
180
+ <p>If you need to you can click the cog icon for an advanced search view which is handy for doing more advanced searches and for inserting images pre-wrapped with your affiliate link or via a shortcode instead.</p>', 'thirstyaffiliates' ),
181
+ 'prev' => $this->_urls[ 'affiliate-links-listing' ],
182
+ 'next' => $this->_urls[ 'general-settings' ],
183
+ 'edge' => 'top',
184
+ 'align' => 'left',
185
+ 'width' => 400
186
+ ),
187
+ 'thirstylink_page_thirsty-settings' => array(
188
+ 'ta_general_settings' => array(
189
+ 'elem' => '.nav-tab-wrapper .ta_general_settings',
190
+ 'html' => __( '<h3>ThirstyAffiliates has a number of settings that change the way it works, behaves and how your links appear.</h3>
191
+ <p>Here are the General settings which are for changing the way you work with ThirstyAffiliates in the backend.</p>', 'thirstyaffiliates' ),
192
+ 'prev' => $this->_urls[ 'new-wp-post' ],
193
+ 'next' => $this->_urls[ 'link-apperance-settings' ],
194
+ 'edge' => 'top',
195
+ 'align' => 'left',
196
+ ),
197
+ 'ta_links_settings' => array(
198
+ 'elem' => '.nav-tab-wrapper .ta_links_settings',
199
+ 'html' => __( '<h3>One of the most important parts of the settings area is Link Appearance which changes the way your links look to your visitors.</h3>
200
+ <p>This includes the Link Prefix setting which is important to decide on before you start using ThirstyAffiliates.</p>
201
+ <p>By default, this is set to “recommends” so your links will look like “http://example.com/recommends/some-product-name”.</p>
202
+ <p>You can also choose to include the category slug in the URL, change the way ThirstyAffiliates redirects links, add no follow, make links open in a new window and more.</p>', 'thirstyaffiliates' ),
203
+ 'prev' => $this->_urls[ 'general-settings' ],
204
+ 'next' => $this->_urls[ 'modules-settings' ],
205
+ 'edge' => 'top',
206
+ 'align' => 'left',
207
+ 'width' => 450
208
+ ),
209
+ 'ta_modules_settings' => array(
210
+ 'elem' => '.nav-tab-wrapper .ta_modules_settings',
211
+ 'html' => __( '<h3>We built ThirstyAffiliates to be flexible and as such, you can shut down the parts of ThirstyAffiliates that aren’t being used. This can make the plugin faster.</h3>
212
+ <p>If you have the Pro add-on each Pro module will show in here as well.</p>', 'thirstyaffiliates' ),
213
+ 'prev' => $this->_urls[ 'link-apperance-settings' ],
214
+ 'next' => $this->_urls[ 'import-export-settings' ],
215
+ 'edge' => 'top',
216
+ 'align' => 'left',
217
+ ),
218
+ 'ta_import_export_settings' => array(
219
+ 'elem' => '.nav-tab-wrapper .ta_import_export_settings',
220
+ 'html' => __( '<h3>Setting up multiple sites with all running ThirstyAffiliates?</h3>
221
+ <p>We’ve made it super simple to configure your additional sites by being able to import and export ThirstyAffiliate settings.</p>
222
+ <p>Just copy the Export section and paste it into the Import section on your other site and ThirstyAffiliates will be automatically configured the same way.</p>', 'thirstyaffiliates' ),
223
+ 'prev' => $this->_urls[ 'modules-settings' ],
224
+ 'next' => $this->_urls[ 'help-settings' ],
225
+ 'edge' => 'top',
226
+ 'align' => 'right',
227
+ ),
228
+ 'ta_help_settings' => array(
229
+ 'elem' => '.nav-tab-wrapper .ta_help_settings',
230
+ 'html' => __( '<h3>Need some help with ThirstyAffiliates?</h3>
231
+ <p>We have a growing knowledge base filled with guides, troubleshooting and FAQ.</p>
232
+ <p>Our blog is also very active with lots of interesting affiliate marketing topics to help you grow your affiliate marketing empire.</p>', 'thirstyaffiliates' ),
233
+ 'prev' => $this->_urls[ 'import-export-settings' ],
234
+ 'next' => $this->_urls[ 'new-affiliate-link' ],
235
+ 'edge' => 'top',
236
+ 'align' => 'right',
237
+ )
238
+ ),
239
+ 'thirstylink' => array(
240
+ 'elem' => '#menu-posts-thirstylink',
241
+ 'html' => __( '<h3>
This concludes the guide. You are now ready to setup your first affiliate link!</h3>
242
+ <p>We also have a Pro add-on for ThirstyAffiliates which contains lots of interesting features for affiliates like:</p>
243
+ <ul><li>Automatically link up your affiliate links to keywords in your blog</li>
244
+ <li>Get more detailed and advanced reports</li>
245
+ <li>Geolocated affiliate links</li>
246
+ <li>Google Analytics integration</li>
247
+ <li>CSV importing/exporting, Amazon importing, and more</li>
248
+ <li>Link event notification admin emails</li>
249
+ <li>Automatic link health checker</li></ul>
250
+ <p>Want to unlock all of the extra features you see here? The Pro add-on is for you. And we’re adding new features all the time!</p>', 'thirstyaffiliates' ),
251
+ 'prev' => $this->_urls[ 'help-settings' ],
252
+ 'next' => null,
253
+ 'edge' => 'left',
254
+ 'align' => 'left',
255
+ 'width' => 620,
256
+ 'btn_tour_done' => __( 'Check out the current Pro add-on features' , 'thirstyaffiliates' ),
257
+ 'btn_tour_done_url' => 'https://thirstyaffiliates.com/pricing?utm_source=Free%20Plugin&utm_medium=Tour&utm_campaign=Pro%20Link'
258
+ ),
259
+ ) );
260
+ }
261
+
262
+ /**
263
+ * Get current screen.
264
+ *
265
+ * @since 3.0.0
266
+ * @access public
267
+ *
268
+ * @return array Current guide tour screen.
269
+ */
270
+ public function get_current_screen() {
271
+
272
+ $screen = get_current_screen();
273
+ $tab = isset( $_GET[ 'tab' ] ) ? sanitize_text_field( $_GET[ 'tab' ] ) : '';
274
+
275
+ if ( ! isset( $this->_screens[ $screen->id ] ) || empty( $this->_screens[ $screen->id ] ) )
276
+ return;
277
+
278
+ if ( $screen->id == 'thirstylink_page_thirsty-settings' ) {
279
+
280
+ if( $tab && isset( $this->_screens[ $screen->id ][ $tab ] ) )
281
+ return $this->_screens[ $screen->id ][ $tab ];
282
+ elseif ( ! isset( $_GET[ 'tab' ] ) )
283
+ return $this->_screens[ $screen->id ][ 'ta_general_settings' ];
284
+ else
285
+ return array();
286
+ }
287
+
288
+ return $this->_screens[ $screen->id ];
289
+ }
290
+
291
+ /**
292
+ * Get all guide tour screens.
293
+ *
294
+ * @since 3.0.0
295
+ * @access public
296
+ *
297
+ * @return array List of all guide tour screens.
298
+ */
299
+ public function get_screens() {
300
+
301
+ return $this->_screens;
302
+ }
303
+
304
+ /**
305
+ * AJAX close guided tour.
306
+ *
307
+ * @since 3.0.0
308
+ * @access public
309
+ */
310
+ public function ajax_close_guided_tour() {
311
+
312
+ if ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX )
313
+ $response = array( 'status' => 'fail' , 'error_msg' => __( 'Invalid AJAX call' , 'thirstyaffiliates' ) );
314
+ elseif ( ! check_ajax_referer( 'ta-close-guided-tour' , 'nonce' , false ) )
315
+ $response = array( 'status' => 'fail' , 'error_msg' => __( 'Security Check Failed' , 'thirstyaffiliates' ) );
316
+ else {
317
+
318
+ update_option( 'ta_guided_tour_status' , 'close' );
319
+ $response = array( 'status' => 'success' );
320
+ }
321
+
322
+ @header( 'Content-Type: application/json; charset=' . get_option( 'blog_charset' ) );
323
+ echo wp_json_encode( $response );
324
+ wp_die();
325
+ }
326
+
327
+ /**
328
+ * Set the guided tour status option as 'open' on activation.
329
+ *
330
+ * @since 3.0.0
331
+ * @access private
332
+ */
333
+ private function set_guided_tour_status_open() {
334
+
335
+ if ( get_option( 'ta_guided_tour_status' ) )
336
+ return;
337
+
338
+ update_option( 'ta_guided_tour_status' , 'open' );
339
+ }
340
+
341
+
342
+
343
+
344
+ /*
345
+ |--------------------------------------------------------------------------
346
+ | Implemented Interface Methods
347
+ |--------------------------------------------------------------------------
348
+ */
349
+
350
+ /**
351
+ * Execute codes that needs to run plugin activation.
352
+ *
353
+ * @since 3.0.0
354
+ * @access public
355
+ * @implements ThirstyAffiliates\Interfaces\Activatable_Interface
356
+ */
357
+ public function activate() {
358
+
359
+ $this->set_guided_tour_status_open();
360
+ }
361
+
362
+ /**
363
+ * Method that houses codes to be executed on init hook.
364
+ *
365
+ * @since 3.0.0
366
+ * @access public
367
+ * @inherit ThirstyAffiliates\Interfaces\Initiable_Interface
368
+ */
369
+ public function initialize() {
370
+
371
+ add_action( 'wp_ajax_ta_close_guided_tour' , array( $this , 'ajax_close_guided_tour' ) );
372
+ }
373
+
374
+ /**
375
+ * Execute model.
376
+ *
377
+ * @implements ThirstyAffiliates\Interfaces\Model_Interface
378
+ *
379
+ * @since 3.0.0
380
+ * @access public
381
+ */
382
+ public function run() {
383
+
384
+ if ( get_option( 'ta_guided_tour_status' ) !== 'open' )
385
+ return;
386
+
387
+ $this->define_guided_tour_pages();
388
+ }
389
+ }
Models/Link_Fixer.php CHANGED
@@ -1,289 +1,289 @@
1
- <?php
2
-
3
- namespace ThirstyAffiliates\Models;
4
-
5
- use ThirstyAffiliates\Abstracts\Abstract_Main_Plugin_Class;
6
-
7
- use ThirstyAffiliates\Interfaces\Model_Interface;
8
- use ThirstyAffiliates\Interfaces\Initiable_Interface;
9
-
10
- use ThirstyAffiliates\Helpers\Plugin_Constants;
11
- use ThirstyAffiliates\Helpers\Helper_Functions;
12
-
13
- use ThirstyAffiliates\Models\Affiliate_Link;
14
-
15
- /**
16
- * Model that houses the link fixer logic.
17
- *
18
- * @since 3.0.0
19
- */
20
- class Link_Fixer implements Model_Interface , Initiable_Interface {
21
-
22
- /*
23
- |--------------------------------------------------------------------------
24
- | Class Properties
25
- |--------------------------------------------------------------------------
26
- */
27
-
28
- /**
29
- * Property that holds the single main instance of Bootstrap.
30
- *
31
- * @since 3.0.0
32
- * @access private
33
- * @var Link_Fixer
34
- */
35
- private static $_instance;
36
-
37
- /**
38
- * Model that houses the main plugin object.
39
- *
40
- * @since 3.0.0
41
- * @access private
42
- * @var Abstract_Main_Plugin_Class
43
- */
44
- private $_main_plugin;
45
-
46
- /**
47
- * Model that houses all the plugin constants.
48
- *
49
- * @since 3.0.0
50
- * @access private
51
- * @var Plugin_Constants
52
- */
53
- private $_constants;
54
-
55
- /**
56
- * Property that houses all the helper functions of the plugin.
57
- *
58
- * @since 3.0.0
59
- * @access private
60
- * @var Helper_Functions
61
- */
62
- private $_helper_functions;
63
-
64
-
65
-
66
-
67
- /*
68
- |--------------------------------------------------------------------------
69
- | Class Methods
70
- |--------------------------------------------------------------------------
71
- */
72
-
73
- /**
74
- * Class constructor.
75
- *
76
- * @since 3.0.0
77
- * @access public
78
- *
79
- * @param Abstract_Main_Plugin_Class $main_plugin Main plugin object.
80
- * @param Plugin_Constants $constants Plugin constants object.
81
- * @param Helper_Functions $helper_functions Helper functions object.
82
- */
83
- public function __construct( Abstract_Main_Plugin_Class $main_plugin , Plugin_Constants $constants , Helper_Functions $helper_functions ) {
84
-
85
- $this->_constants = $constants;
86
- $this->_helper_functions = $helper_functions;
87
-
88
- $main_plugin->add_to_all_plugin_models( $this );
89
- $main_plugin->add_to_public_models( $this );
90
-
91
- }
92
-
93
- /**
94
- * Ensure that only one instance of this class is loaded or can be loaded ( Singleton Pattern ).
95
- *
96
- * @since 3.0.0
97
- * @access public
98
- *
99
- * @param Abstract_Main_Plugin_Class $main_plugin Main plugin object.
100
- * @param Plugin_Constants $constants Plugin constants object.
101
- * @param Helper_Functions $helper_functions Helper functions object.
102
- * @return Link_Fixer
103
- */
104
- public static function get_instance( Abstract_Main_Plugin_Class $main_plugin , Plugin_Constants $constants , Helper_Functions $helper_functions ) {
105
-
106
- if ( !self::$_instance instanceof self )
107
- self::$_instance = new self( $main_plugin , $constants , $helper_functions );
108
-
109
- return self::$_instance;
110
-
111
- }
112
-
113
- /**
114
- * Get data of links to be fixed.
115
- *
116
- * @since 3.0.0
117
- * @since 3.2.4 Make sure that link fixer runs using the default language set on WPML when it is active.
118
- * @since 3.3.0 Add data-nojs attribute support.
119
- * @since 3.4.0 Add support for additional classes field. Remove query string on href before processing.
120
- * @access public
121
- *
122
- * @global SitePress $sitepress WPML main plugin object.
123
- *
124
- * @param array $links List of affiliate links to fix.
125
- * @param int $post_id ID of the post currently being viewed.
126
- * @param array $data Affiliate Links data.
127
- * @return array Affiliate Links data.
128
- */
129
- public function get_link_fixer_data( $links , $post_id = 0 , $data = array() ) {
130
-
131
- global $sitepress;
132
-
133
- if ( empty( $links ) )
134
- return $data;
135
-
136
- if ( is_object( $sitepress ) )
137
- $sitepress->switch_lang( $sitepress->get_default_language() );
138
-
139
- $global_rel = get_option( 'ta_additional_rel_tags' );
140
- $global_class = get_option( 'ta_additional_css_classes' );
141
-
142
- foreach( $links as $link ) {
143
-
144
- $href = strtok( esc_url_raw( $link[ 'href' ] ) , '?' );
145
- $class = isset( $link[ 'class' ] ) ? sanitize_text_field( $link[ 'class' ] ) : '';
146
- $key = (int) sanitize_text_field( $link[ 'key' ] );
147
- $link_id = $this->url_to_affiliate_link_id( $href );
148
-
149
- $thirstylink = new Affiliate_Link( $link_id );
150
-
151
- if ( ! $thirstylink->get_id() || get_post_type( $link_id ) !== Plugin_Constants::AFFILIATE_LINKS_CPT )
152
- continue;
153
-
154
- $class = str_replace( 'thirstylinkimg' , 'thirstylink' , $class );
155
- $class .= ( get_option( 'ta_disable_thirsty_link_class' ) !== "yes" && strpos( $class , 'thirstylink' ) === false ) ? ' thirstylink' : '';
156
- $href = ( $thirstylink->is( 'uncloak_link' ) ) ? apply_filters( 'ta_uncloak_link_url' , $thirstylink->get_prop( 'destination_url' ) , $thirstylink ) : $thirstylink->get_prop( 'permalink' );
157
- $rel = $thirstylink->is( 'no_follow' ) ? 'nofollow' : '';
158
- $target = $thirstylink->is( 'new_window' ) ? '_blank' : '';
159
- $title = get_option( 'ta_disable_title_attribute' ) != 'yes' ? esc_attr( str_replace( '"' , '' , $thirstylink->get_prop( 'name' ) ) ) : '';
160
- $title = str_replace( '&#039;' , '\'' , $title );
161
-
162
- // append custom rel tags.
163
- if ( $thirstylink->get_prop( 'rel_tags' ) )
164
- $rel = trim( $rel . ' ' . $thirstylink->get_prop( 'rel_tags' ) );
165
-
166
- // make sure rel attribute entries are unique
167
- $rel_array = explode( ' ' , $rel );
168
- $rel = implode( ' ' , array_unique( $rel_array ) );
169
-
170
- // append custom classes.
171
- if ( $thirstylink->get_prop( 'css_classes' ) )
172
- $class = trim( $class . ' ' . $thirstylink->get_prop( 'css_classes' ) );
173
-
174
- // make sure class attribute entries are unique
175
- $class_array = explode( ' ' , $class );
176
- $class = implode( ' ' , array_unique( $class_array ) );
177
-
178
- // if link is image, then change default class to 'thirstylinkimg'
179
- if ( $link[ 'is_image' ] )
180
- $class = str_replace( 'thirstylink' , 'thirstylinkimg' , $class );
181
-
182
- // if thirstylink class is not the same with global class, then we remove global class.
183
- if ( $thirstylink->get_prop( 'css_classes' ) != $global_class )
184
- $class = str_replace( $global_class , '' , $class ) ;
185
-
186
- $data[] = array(
187
- 'key' => $key,
188
- 'link_id' => $link_id,
189
- 'class' => trim( esc_attr( preg_replace( '!\s+!' , ' ', $class ) ) ),
190
- 'href' => esc_url_raw( $href ),
191
- 'rel' => trim( esc_attr( preg_replace( '!\s+!' , ' ', $rel ) ) ),
192
- 'target' => esc_attr( $target ),
193
- 'title' => $title,
194
- 'nojs' => apply_filters( 'ta_nojs_redirect_attribute' , false , $thirstylink ),
195
- 'pass_qs' => $thirstylink->is( 'pass_query_str' )
196
- );
197
- }
198
-
199
- return $data;
200
- }
201
-
202
- /**
203
- * Get the ID from a given affiliate link URL (replaces url_to_postid function).
204
- *
205
- * @since 3.3.0
206
- * @access private
207
- *
208
- * @param string $href Affilaite link cloaked URL.
209
- * @return int Affiliate link ID.
210
- */
211
- private function url_to_affiliate_link_id( $href ) {
212
-
213
- global $wpdb;
214
-
215
- $link_parts = explode( "/" , $href );
216
- $link_prefix = $this->_helper_functions->get_thirstylink_link_prefix();
217
- $cpt_slug = esc_sql( Plugin_Constants::AFFILIATE_LINKS_CPT );
218
-
219
- // get the key of the link prefix in the url from the $link_parts variable.
220
- $key = (int) array_search( $link_prefix , $link_parts );
221
-
222
- // get the slug from the $link_parts variable based on the position of the link prefix's key.
223
- // if $key + 2 exists, this means that the link has a category slug in it, so we fetch $key + 2, otherwise we fetch $key + 1 (no category slug).
224
- $slug = esc_sql( isset( $link_parts[ $key + 2 ] ) && $link_parts[ $key + 2 ] ? $link_parts[ $key + 2 ] : $link_parts[ $key + 1 ] );
225
-
226
- // fetch the ID based on the post type and slug.
227
- $id = $wpdb->get_var( "SELECT ID FROM $wpdb->posts WHERE post_type = '$cpt_slug' AND post_name = '$slug'" );
228
-
229
- return $id ? absint( $id ) : 0;
230
- }
231
-
232
- /**
233
- * Ajax link fixer.
234
- *
235
- * @since 3.0.0
236
- * @access public
237
- */
238
- public function ajax_link_fixer() {
239
-
240
- if ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX )
241
- $response = array( 'status' => 'fail' , 'error_msg' => __( 'Invalid AJAX call' , 'thirstyaffiliates' ) );
242
- elseif ( ! isset( $_POST[ 'hrefs' ] ) || empty( $_POST[ 'hrefs' ] ) )
243
- $response = array( 'status' => 'fail' , 'error_msg' => __( 'Invalid AJAX call' , 'thirstyaffiliates' ) );
244
- else {
245
-
246
- $links = $_POST[ 'hrefs' ];
247
- $post_id = isset( $_POST[ 'post_id' ] ) ? intval( $_POST[ 'post_id' ] ) : 0;
248
- $response = array(
249
- 'status' => 'success',
250
- 'data' => $this->get_link_fixer_data( $links , $post_id )
251
- );
252
- }
253
-
254
- @header( 'Content-Type: application/json; charset=' . get_option( 'blog_charset' ) );
255
- echo wp_json_encode( $response );
256
- wp_die();
257
- }
258
-
259
-
260
-
261
-
262
- /*
263
- |--------------------------------------------------------------------------
264
- | Fulfill implemented interface contracts
265
- |--------------------------------------------------------------------------
266
- */
267
-
268
- /**
269
- * Execute codes that needs to run on plugin initialization.
270
- *
271
- * @since 3.0.0
272
- * @access public
273
- * @implements ThirstyAffiliates\Interfaces\Initiable_Interface
274
- */
275
- public function initialize() {
276
-
277
- add_action( 'wp_ajax_ta_link_fixer' , array( $this , 'ajax_link_fixer' ) );
278
- add_action( 'wp_ajax_nopriv_ta_link_fixer' , array( $this , 'ajax_link_fixer' ) );
279
- }
280
-
281
- /**
282
- * Execute link picker.
283
- *
284
- * @since 3.0.0
285
- * @access public
286
- */
287
- public function run() {
288
- }
289
- }
1
+ <?php
2
+
3
+ namespace ThirstyAffiliates\Models;
4
+
5
+ use ThirstyAffiliates\Abstracts\Abstract_Main_Plugin_Class;
6
+
7
+ use ThirstyAffiliates\Interfaces\Model_Interface;
8
+ use ThirstyAffiliates\Interfaces\Initiable_Interface;
9
+
10
+ use ThirstyAffiliates\Helpers\Plugin_Constants;
11
+ use ThirstyAffiliates\Helpers\Helper_Functions;
12
+
13
+ use ThirstyAffiliates\Models\Affiliate_Link;
14
+
15
+ /**
16
+ * Model that houses the link fixer logic.
17
+ *
18
+ * @since 3.0.0
19
+ */
20
+ class Link_Fixer implements Model_Interface , Initiable_Interface {
21
+
22
+ /*
23
+ |--------------------------------------------------------------------------
24
+ | Class Properties
25
+ |--------------------------------------------------------------------------
26
+ */
27
+
28
+ /**
29
+ * Property that holds the single main instance of Bootstrap.
30
+ *
31
+ * @since 3.0.0
32
+ * @access private
33
+ * @var Link_Fixer
34
+ */
35
+ private static $_instance;
36
+
37
+ /**
38
+ * Model that houses the main plugin object.
39
+ *
40
+ * @since 3.0.0
41
+ * @access private
42
+ * @var Abstract_Main_Plugin_Class
43
+ */
44
+ private $_main_plugin;
45
+
46
+ /**
47
+ * Model that houses all the plugin constants.
48
+ *
49
+ * @since 3.0.0
50
+ * @access private
51
+ * @var Plugin_Constants
52
+ */
53
+ private $_constants;
54
+
55
+ /**
56
+ * Property that houses all the helper functions of the plugin.
57
+ *
58
+ * @since 3.0.0
59
+ * @access private
60
+ * @var Helper_Functions
61
+ */
62
+ private $_helper_functions;
63
+
64
+
65
+
66
+
67
+ /*
68
+ |--------------------------------------------------------------------------
69
+ | Class Methods
70
+ |--------------------------------------------------------------------------
71
+ */
72
+
73
+ /**
74
+ * Class constructor.
75
+ *
76
+ * @since 3.0.0
77
+ * @access public
78
+ *
79
+ * @param Abstract_Main_Plugin_Class $main_plugin Main plugin object.
80
+ * @param Plugin_Constants $constants Plugin constants object.
81
+ * @param Helper_Functions $helper_functions Helper functions object.
82
+ */
83
+ public function __construct( Abstract_Main_Plugin_Class $main_plugin , Plugin_Constants $constants , Helper_Functions $helper_functions ) {
84
+
85
+ $this->_constants = $constants;
86
+ $this->_helper_functions = $helper_functions;
87
+
88
+ $main_plugin->add_to_all_plugin_models( $this );
89
+ $main_plugin->add_to_public_models( $this );
90
+
91
+ }
92
+
93
+ /**
94
+ * Ensure that only one instance of this class is loaded or can be loaded ( Singleton Pattern ).
95
+ *
96
+ * @since 3.0.0
97
+ * @access public
98
+ *
99
+ * @param Abstract_Main_Plugin_Class $main_plugin Main plugin object.
100
+ * @param Plugin_Constants $constants Plugin constants object.
101
+ * @param Helper_Functions $helper_functions Helper functions object.
102
+ * @return Link_Fixer
103
+ */
104
+ public static function get_instance( Abstract_Main_Plugin_Class $main_plugin , Plugin_Constants $constants , Helper_Functions $helper_functions ) {
105
+
106
+ if ( !self::$_instance instanceof self )
107
+ self::$_instance = new self( $main_plugin , $constants , $helper_functions );
108
+
109
+ return self::$_instance;
110
+
111
+ }
112
+
113
+ /**
114
+ * Get data of links to be fixed.
115
+ *
116
+ * @since 3.0.0
117
+ * @since 3.2.4 Make sure that link fixer runs using the default language set on WPML when it is active.
118
+ * @since 3.3.0 Add data-nojs attribute support.
119
+ * @since 3.4.0 Add support for additional classes field. Remove query string on href before processing.
120
+ * @access public
121
+ *
122
+ * @global SitePress $sitepress WPML main plugin object.
123
+ *
124
+ * @param array $links List of affiliate links to fix.
125
+ * @param int $post_id ID of the post currently being viewed.
126
+ * @param array $data Affiliate Links data.
127
+ * @return array Affiliate Links data.
128
+ */
129
+ public function get_link_fixer_data( $links , $post_id = 0 , $data = array() ) {
130
+
131
+ global $sitepress;
132
+
133
+ if ( empty( $links ) )
134
+ return $data;
135
+
136
+ if ( is_object( $sitepress ) )
137
+ $sitepress->switch_lang( $sitepress->get_default_language() );
138
+
139
+ $global_rel = get_option( 'ta_additional_rel_tags' );
140
+ $global_class = get_option( 'ta_additional_css_classes' );
141
+
142
+ foreach( $links as $link ) {
143
+
144
+ $href = strtok( esc_url_raw( $link[ 'href' ] ) , '?' );
145
+ $class = isset( $link[ 'class' ] ) ? sanitize_text_field( $link[ 'class' ] ) : '';
146
+ $key = (int) sanitize_text_field( $link[ 'key' ] );
147
+ $link_id = $this->url_to_affiliate_link_id( $href );
148
+
149
+ $thirstylink = new Affiliate_Link( $link_id );
150
+
151
+ if ( ! $thirstylink->get_id() || get_post_type( $link_id ) !== Plugin_Constants::AFFILIATE_LINKS_CPT )
152
+ continue;
153
+
154
+ $class = str_replace( 'thirstylinkimg' , 'thirstylink' , $class );
155
+ $class .= ( get_option( 'ta_disable_thirsty_link_class' ) !== "yes" && strpos( $class , 'thirstylink' ) === false ) ? ' thirstylink' : '';
156
+ $href = ( $thirstylink->is( 'uncloak_link' ) ) ? apply_filters( 'ta_uncloak_link_url' , $thirstylink->get_prop( 'destination_url' ) , $thirstylink ) : $thirstylink->get_prop( 'permalink' );
157
+ $rel = $thirstylink->is( 'no_follow' ) ? 'nofollow' : '';
158
+ $target = $thirstylink->is( 'new_window' ) ? '_blank' : '';
159
+ $title = get_option( 'ta_disable_title_attribute' ) != 'yes' ? esc_attr( str_replace( '"' , '' , $thirstylink->get_prop( 'name' ) ) ) : '';
160
+ $title = str_replace( '&#039;' , '\'' , $title );
161
+
162
+ // append custom rel tags.
163
+ if ( $thirstylink->get_prop( 'rel_tags' ) )
164
+ $rel = trim( $rel . ' ' . $thirstylink->get_prop( 'rel_tags' ) );
165
+
166
+ // make sure rel attribute entries are unique
167
+ $rel_array = explode( ' ' , $rel );
168
+ $rel = implode( ' ' , array_unique( $rel_array ) );
169
+
170
+ // append custom classes.
171
+ if ( $thirstylink->get_prop( 'css_classes' ) )
172
+ $class = trim( $class . ' ' . $thirstylink->get_prop( 'css_classes' ) );
173
+
174
+ // make sure class attribute entries are unique
175
+ $class_array = explode( ' ' , $class );
176
+ $class = implode( ' ' , array_unique( $class_array ) );
177
+
178
+ // if link is image, then change default class to 'thirstylinkimg'
179
+ if ( $link[ 'is_image' ] )
180
+ $class = str_replace( 'thirstylink' , 'thirstylinkimg' , $class );
181
+
182
+ // if thirstylink class is not the same with global class, then we remove global class.
183
+ if ( $thirstylink->get_prop( 'css_classes' ) != $global_class )
184
+ $class = str_replace( $global_class , '' , $class ) ;
185
+
186
+ $data[] = array(
187
+ 'key' => $key,
188
+ 'link_id' => $link_id,
189
+ 'class' => trim( esc_attr( preg_replace( '!\s+!' , ' ', $class ) ) ),
190
+ 'href' => esc_url_raw( $href ),
191
+ 'rel' => trim( esc_attr( preg_replace( '!\s+!' , ' ', $rel ) ) ),
192
+ 'target' => esc_attr( $target ),
193
+ 'title' => $title,
194
+ 'nojs' => apply_filters( 'ta_nojs_redirect_attribute' , false , $thirstylink ),
195
+ 'pass_qs' => $thirstylink->is( 'pass_query_str' )
196
+ );
197
+ }
198
+
199
+ return $data;
200
+ }
201
+
202
+ /**
203
+ * Get the ID from a given affiliate link URL (replaces url_to_postid function).
204
+ *
205
+ * @since 3.3.0
206
+ * @access private
207
+ *
208
+ * @param string $href Affilaite link cloaked URL.
209
+ * @return int Affiliate link ID.
210
+ */
211
+ private function url_to_affiliate_link_id( $href ) {
212
+
213
+ global $wpdb;
214
+
215
+ $link_parts = explode( "/" , $href );
216
+ $link_prefix = $this->_helper_functions->get_thirstylink_link_prefix();
217
+ $cpt_slug = esc_sql( Plugin_Constants::AFFILIATE_LINKS_CPT );
218
+
219
+ // get the key of the link prefix in the url from the $link_parts variable.
220
+ $key = (int) array_search( $link_prefix , $link_parts );
221
+
222
+ // get the slug from the $link_parts variable based on the position of the link prefix's key.
223
+ // if $key + 2 exists, this means that the link has a category slug in it, so we fetch $key + 2, otherwise we fetch $key + 1 (no category slug).
224
+ $slug = esc_sql( isset( $link_parts[ $key + 2 ] ) && $link_parts[ $key + 2 ] ? $link_parts[ $key + 2 ] : $link_parts[ $key + 1 ] );
225
+
226
+ // fetch the ID based on the post type and slug.
227
+ $id = $wpdb->get_var( "SELECT ID FROM $wpdb->posts WHERE post_type = '$cpt_slug' AND post_name = '$slug'" );
228
+
229
+ return $id ? absint( $id ) : 0;
230
+ }
231
+
232
+ /**
233
+ * Ajax link fixer.
234
+ *
235
+ * @since 3.0.0
236
+ * @access public
237
+ */
238
+ public function ajax_link_fixer() {
239
+
240
+ if ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX )
241
+ $response = array( 'status' => 'fail' , 'error_msg' => __( 'Invalid AJAX call' , 'thirstyaffiliates' ) );
242
+ elseif ( ! isset( $_POST[ 'hrefs' ] ) || empty( $_POST[ 'hrefs' ] ) )
243
+ $response = array( 'status' => 'fail' , 'error_msg' => __( 'Invalid AJAX call' , 'thirstyaffiliates' ) );
244
+ else {
245
+
246
+ $links = $_POST[ 'hrefs' ];
247
+ $post_id = isset( $_POST[ 'post_id' ] ) ? intval( $_POST[ 'post_id' ] ) : 0;
248
+ $response = array(
249
+ 'status' => 'success',
250
+ 'data' => $this->get_link_fixer_data( $links , $post_id )
251
+ );
252
+ }
253
+
254
+ @header( 'Content-Type: application/json; charset=' . get_option( 'blog_charset' ) );
255
+ echo wp_json_encode( $response );
256
+ wp_die();
257
+ }
258
+
259
+
260
+
261
+
262
+ /*
263
+ |--------------------------------------------------------------------------
264
+ | Fulfill implemented interface contracts
265
+ |--------------------------------------------------------------------------
266
+ */
267
+
268
+ /**
269
+ * Execute codes that needs to run on plugin initialization.
270
+ *
271
+ * @since 3.0.0
272
+ * @access public
273
+ * @implements ThirstyAffiliates\Interfaces\Initiable_Interface
274
+ */
275
+ public function initialize() {
276
+
277
+ add_action( 'wp_ajax_ta_link_fixer' , array( $this , 'ajax_link_fixer' ) );
278
+ add_action( 'wp_ajax_nopriv_ta_link_fixer' , array( $this , 'ajax_link_fixer' ) );
279
+ }
280
+
281
+ /**
282
+ * Execute link picker.
283
+ *
284
+ * @since 3.0.0
285
+ * @access public
286
+ */
287
+ public function run() {
288
+ }
289
+ }
Models/Link_Picker.php CHANGED
@@ -1,710 +1,710 @@
1
- <?php
2
-
3
- namespace ThirstyAffiliates\Models;
4
-
5
- use ThirstyAffiliates\Abstracts\Abstract_Main_Plugin_Class;
6
-
7
- use ThirstyAffiliates\Interfaces\Model_Interface;
8
- use ThirstyAffiliates\Interfaces\Initiable_Interface;
9
-
10
- use ThirstyAffiliates\Helpers\Plugin_Constants;
11
- use ThirstyAffiliates\Helpers\Helper_Functions;
12
-
13
- use ThirstyAffiliates\Models\Affiliate_Link;
14
-
15
- /**
16
- * Model that houses the link picker logic.
17
- *
18
- * @since 3.0.0
19
- */
20
- class Link_Picker implements Model_Interface , Initiable_Interface {
21
-
22
- /*
23
- |--------------------------------------------------------------------------
24
- | Class Properties
25
- |--------------------------------------------------------------------------
26
- */
27
-
28
- /**
29
- * Property that holds the single main instance of Bootstrap.
30
- *
31
- * @since 3.0.0
32
- * @access private
33
- * @var Link_Picker
34
- */
35
- private static $_instance;
36
-
37
- /**
38
- * Model that houses the main plugin object.
39
- *
40
- * @since 3.0.0
41
- * @access private
42
- * @var Abstract_Main_Plugin_Class
43
- */
44
- private $_main_plugin;
45
-
46
- /**
47
- * Model that houses all the plugin constants.
48
- *
49
- * @since 3.0.0
50
- * @access private
51
- * @var Plugin_Constants
52
- */
53
- private $_constants;
54
-
55
- /**
56
- * Property that houses all the helper functions of the plugin.
57
- *
58
- * @since 3.0.0
59
- * @access private
60
- * @var Helper_Functions
61
- */
62
- private $_helper_functions;
63
-
64
-
65
-
66
-
67
- /*
68
- |--------------------------------------------------------------------------
69
- | Class Methods
70
- |--------------------------------------------------------------------------
71
- */
72
-
73
- /**
74
- * Class constructor.
75
- *
76
- * @since 3.0.0
77
- * @access public
78
- *
79
- * @param Abstract_Main_Plugin_Class $main_plugin Main plugin object.
80
- * @param Plugin_Constants $constants Plugin constants object.
81
- * @param Helper_Functions $helper_functions Helper functions object.
82
- */
83
- public function __construct( Abstract_Main_Plugin_Class $main_plugin , Plugin_Constants $constants , Helper_Functions $helper_functions ) {
84
-
85
- $this->_constants = $constants;
86
- $this->_helper_functions = $helper_functions;
87
-
88
- $main_plugin->add_to_all_plugin_models( $this );
89
-
90
- }
91
-
92
- /**
93
- * Ensure that only one instance of this class is loaded or can be loaded ( Singleton Pattern ).
94
- *
95
- * @since 3.0.0
96
- * @access public
97
- *
98
- * @param Abstract_Main_Plugin_Class $main_plugin Main plugin object.
99
- * @param Plugin_Constants $constants Plugin constants object.
100
- * @param Helper_Functions $helper_functions Helper functions object.
101
- * @return Link_Picker
102
- */
103
- public static function get_instance( Abstract_Main_Plugin_Class $main_plugin , Plugin_Constants $constants , Helper_Functions $helper_functions ) {
104
-
105
- if ( !self::$_instance instanceof self )
106
- self::$_instance = new self( $main_plugin , $constants , $helper_functions );
107
-
108
- return self::$_instance;
109
-
110
- }
111
-
112
-
113
-
114
- /*
115
- |--------------------------------------------------------------------------
116
- | Register tinymce buttons and scripts
117
- |--------------------------------------------------------------------------
118
- */
119
-
120
- /**
121
- * Initialize thirsty editor buttons.
122
- *
123
- * @since 3.0.0
124
- * @access public
125
- */
126
- public function init_thirsty_editor_buttons() {
127
-
128
- if ( ! is_admin() || ! current_user_can( 'edit_posts' ) )
129
- return;
130
-
131
- if ( get_option( 'ta_disable_visual_editor_buttons' ) == 'yes' || get_user_option( 'rich_editing' ) != 'true' )
132
- return;
133
-
134
- add_filter( 'mce_external_plugins' , array( $this , 'load_thirsty_mce_plugin' ) );
135
- add_filter( 'mce_buttons' , array( $this , 'register_mce_buttons' ) , 5 );
136
-
137
- }
138
-
139
- /**
140
- * Initialize thirsty editor buttons on page builders.
141
- *
142
- * @since 3.4.0
143
- * @access public
144
- */
145
- public function init_thirsty_editor_buttons_for_page_builders() {
146
-
147
- if ( get_option( 'ta_disable_visual_editor_buttons' ) == 'yes' || get_user_option( 'rich_editing' ) != 'true' )
148
- return;
149
-
150
- if ( ! $this->_helper_functions->is_page_builder_active() || ! current_user_can( 'edit_posts' ) )
151
- return;
152
-
153
- add_filter( 'mce_external_plugins' , array( $this , 'load_thirsty_mce_plugin' ) , 99999 );
154
- add_filter( 'mce_buttons' , array( $this , 'register_mce_buttons' ) , 99 );
155
- }
156
-
157
- /**
158
- * Load Thirsty Affiliate MCE plugin to TinyMCE.
159
- *
160
- * @since 3.0.0
161
- * @access public
162
- *
163
- * @param array $mce_plugins Array of all MCE plugins.
164
- * @return array
165
- */
166
- public function load_thirsty_mce_plugin( $mce_plugins ) {
167
-
168
- $mce_plugins[ 'thirstyaffiliates' ] = $this->_constants->JS_ROOT_URL() . 'lib/thirstymce/editor-plugin.js';
169
-
170
- return $mce_plugins;
171
- }
172
-
173
- /**
174
- * Register Thirsty Affiliate MCE buttons.
175
- *
176
- * @since 3.0.0
177
- * @since 3.4.0 Make sure only one instance of the buttons are loaded.
178
- * @access public
179
- *
180
- * @param array $buttons Array of all MCE buttons.
181
- * @return array
182
- */
183
- public function register_mce_buttons( $buttons ) {
184
-
185
- // if buttons are already present, then skip to prevent duplicate.
186
- if ( in_array( 'thirstyaffiliates_button' , $buttons ) )
187
- return $buttons;
188
-
189
- $enable_link_picker = current_user_can( apply_filters( 'ta_enable_advance_link_picker' , 'edit_posts' ) );
190
- $enable_quick_add = current_user_can( apply_filters( 'ta_enable_quick_add_affiliate_link' , 'publish_posts' ) );
191
-
192
- if ( $enable_link_picker ) array_push( $buttons , 'separator' , 'thirstyaffiliates_button' );
193
- if ( $enable_quick_add ) array_push( $buttons , 'separator' , 'thirstyaffiliates_quickaddlink_button' );
194
-
195
- return $buttons;
196
- }
197
-
198
- /**
199
- * Register WP Editor style.
200
- *
201
- * @since 3.4.0
202
- * @access public
203
- */
204
- public function register_wp_editor_style() {
205
-
206
- add_editor_style( $this->_constants->CSS_ROOT_URL() . 'admin/tinymce/editor.css' );
207
- }
208
-
209
- /**
210
- * Register WP Editor style via mce_css filter (for frontend editors).
211
- *
212
- * @since 3.4.0
213
- * @access public
214
- */
215
- public function register_wp_editor_style_mce_css( $mce_css ) {
216
-
217
- if ( ! empty( $mce_css ) ) $mce_css .= ',';
218
- $mce_css .= $this->_constants->CSS_ROOT_URL() . 'admin/tinymce/editor.css';
219
-
220
- return $mce_css;
221
- }
222
-
223
-
224
-
225
-
226
- /*
227
- |--------------------------------------------------------------------------
228
- | Link Picker methods
229
- |--------------------------------------------------------------------------
230
- */
231
-
232
- /**
233
- * Return
234
- *
235
- * @since 3.0.0
236
- * @since 3.4.0 Add support for additional CSS classes field. Add support for external images.
237
- * @access public
238
- *
239
- * @param array $affiliate_links List of affiliate link IDs.
240
- * @param bool $advanced Boolean check if its advanced or not.
241
- * @param int $post_id ID of the post currently being edited.
242
- * @param string $result_markup Search Affiliate Links result markup.
243
- * @return Search Affiliate Links result markup
244
- */
245
- public function search_affiliate_links_result_markup( $affiliate_links , $advance = false , $post_id = 0 , $result_markup = '' ) {
246
-
247
- if ( is_array( $affiliate_links ) && ! empty( $affiliate_links ) ) {
248
-
249
- foreach( $affiliate_links as $link_id ) {
250
-
251
- $thirstylink = new Affiliate_Link( $link_id );
252
- $rel = $thirstylink->is( 'no_follow' ) ? 'nofollow' : '';
253
- $target = $thirstylink->is( 'new_window' ) ? '_blank' : '';
254
- $class = ( get_option( 'ta_disable_thirsty_link_class' ) !== "yes" ) ? 'thirstylink' : '';
255
- $title = ( get_option( 'ta_disable_title_attribute' ) !== "yes" ) ? $thirstylink->get_prop( 'name' ) : '';
256
- $other_atts = esc_attr( json_encode( apply_filters( 'ta_link_insert_extend_data_attributes' , array() , $thirstylink , $post_id ) ) );
257
-
258
- if ( $thirstylink->get_prop( 'rel_tags' ) )
259
- $rel = trim( $rel . ' ' . $thirstylink->get_prop( 'rel_tags' ) );
260
-
261
- if ( $thirstylink->get_prop( 'css_classes' ) )
262
- $class = trim( $class . ' ' . $thirstylink->get_prop( 'css_classes' ) );
263
-
264
- if ( $advance ) {
265
-
266
- $images = $thirstylink->get_prop( 'image_ids' );
267
- $images_markup = '<span class="images-block">';
268
-
269
- if ( is_array( $images ) && ! empty( $images ) ) {
270
-
271
- $images_markup .= '<span class="label">' . __( 'Select image:' , 'thirstyaffiliates' ) . '</span>';
272
- $images_markup .= '<span class="images">';
273
-
274
- foreach( $images as $image ) {
275
-
276
- if ( filter_var( $image , FILTER_VALIDATE_URL ) )
277
- $images_markup .= '<span class="image"><img src="'. $image .'" width="75" height="75" data-imgid="' . $image . '" data-type="image"></span>';
278
- else
279
- $images_markup .= '<span class="image">' . wp_get_attachment_image( $image , array( 75 , 75 ) , false , array( 'data-imgid' => $image , 'data-type' => 'image' ) ) . '</span>';
280
-
281
- }
282
-
283
- $images_markup .= '</span>';
284
- } else {
285
-
286
- $images_markup .= '<span class="no-images">' . __( 'No images found' , 'thirstyaffiliates' ) . '</span>';
287
- }
288
-
289
- $images_markup .= '</span>';
290
-
291
- $result_markup .= '<li class="thirstylink"
292
- data-linkid="' . $thirstylink->get_id() . '"
293
- data-class="' . esc_attr( $class ) . '"
294
- data-title="' . esc_attr( str_replace( '"' , '' , $title ) ) . '"
295
- data-href="' . esc_url( $thirstylink->get_prop( 'permalink' ) ) . '"
296
- data-rel="' . trim( esc_attr( $rel ) ) . '"
297
- data-target="' . esc_attr( $target ) . '"
298
- data-other-atts="' . $other_atts . '">
299
- <span class="name">' . mb_strimwidth( $thirstylink->get_prop( 'name' ) , 0 , 44 , "..." ) . '</span>
300
- <span class="slug">[' . mb_strimwidth( $thirstylink->get_prop( 'slug' ) , 0 , 35 , "..." ) . ']</span>
301
- <span class="actions">
302
- <button type="button" data-type="normal" class="button insert-link-button dashicons dashicons-admin-links" data-tip="' . __( 'Insert link' , 'thirstyaffiliates' ) . '"></button>
303
- <button type="button" data-type="shortcode" class="button insert-shortcode-button dashicons dashicons-editor-code" data-tip="' . __( 'Insert shortcode' , 'thirstyaffiliates' ) . '"></button>
304
- <button type="button" data-type="image" class="button insert-image-button dashicons dashicons-format-image" data-tip="' . __( 'Insert image' , 'thirstyaffiliates' ) . '"></button>
305
- </span>
306
- ' . $images_markup . '
307
- </li>';
308
- } else {
309
-
310
- $result_markup .= '<li data-class="' . esc_attr( $class ) . '"
311
- data-title="' . esc_attr( str_replace( '"' , '' , $title ) ) . '"
312
- data-href="' . esc_attr( $thirstylink->get_prop( 'permalink' ) ) . '"
313
- data-rel="' . esc_attr( $rel ) . '"
314
- data-target="' . esc_attr( $target ) . '"
315
- data-link-id="' . esc_attr( $thirstylink->get_id() ) . '"
316
- data-link-insertion-type="' . esc_attr( $this->_helper_functions->get_option( 'ta_link_insertion_type' , 'link' ) ) . '"
317
- data-other-atts="' . $other_atts . '">';
318
- $result_markup .= '<strong>' . $link_id . '</strong> : <span>' . $thirstylink->get_prop( 'name' ) . '</span></li>';
319
-
320
- }
321
-
322
- }
323
-
324
- } else
325
- $result_markup .= '<li class="no-links-found">' . __( 'No affiliate links found' , 'thirstyaffiliates' ) . '</li>';
326
-
327
- return $result_markup;
328
- }
329
-
330
- /**
331
- * Search Affiliate Links Query AJAX function
332
- *
333
- * @since 3.0.0
334
- * @since 3.4.0 Add support for category in the search query.
335
- * @access public
336
- */
337
- public function ajax_search_affiliate_links_query() {
338
-
339
- if ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX )
340
- $response = array( 'status' => 'fail' , 'error_msg' => __( 'Invalid AJAX call' , 'thirstyaffiliates' ) );
341
- elseif ( ! isset( $_POST[ 'keyword' ] ) )
342
- $response = array( 'status' => 'fail' , 'error_msg' => __( 'Missing required post data' , 'thirstyaffiliates' ) );
343
- else {
344
-
345
- $paged = ( isset( $_POST[ 'paged' ] ) && $_POST[ 'paged' ] ) ? $_POST[ 'paged' ] : 1;
346
- $category = ( isset( $_POST[ 'category' ] ) && $_POST[ 'category' ] ) ? esc_attr( $_POST[ 'category' ] ) : '';
347
- $exclude = ( isset( $_POST[ 'exclude' ] ) && is_array( $_POST[ 'exclude' ] ) && ! empty( $_POST[ 'exclude' ] ) ) ? $_POST[ 'exclude' ] : array();
348
- $is_gutenberg = isset( $_POST[ 'gutenberg' ] ) && $_POST[ 'gutenberg' ];
349
- $with_images = isset( $_POST[ 'with_images' ] ) && $_POST[ 'with_images' ];
350
- $affiliate_links = $this->_helper_functions->search_affiliate_links_query( $_POST[ 'keyword' ] , $paged , $category , $exclude , $is_gutenberg , $with_images );
351
- $advance = ( isset( $_POST[ 'advance' ] ) && $_POST[ 'advance' ] ) ? true : false;
352
- $post_id = isset( $_POST[ 'post_id' ] ) ? intval( $_POST[ 'post_id' ] ) : 0;
353
-
354
- if ( $is_gutenberg ) {
355
- $response = array( 'status' => 'success' , 'affiliate_links' => $affiliate_links , 'count' => count( $affiliate_links ) );
356
- } else {
357
- $result_markup = $this->search_affiliate_links_result_markup( $affiliate_links , $advance , $post_id );
358
- $response = array( 'status' => 'success' , 'search_query_markup' => $result_markup , 'count' => count( $affiliate_links ) );
359
- }
360
-
361
- }
362
-
363
- @header( 'Content-Type: application/json; charset=' . get_option( 'blog_charset' ) );
364
- echo wp_json_encode( $response );
365
- wp_die();
366
- }
367
-
368
- /**
369
- * AJAX function to display the advance add affiliate link thickbox content.
370
- *
371
- * @since 3.0.0
372
- * @since 3.4.0 Add category field (selectized) in the search form.
373
- * @access public
374
- */
375
- public function ajax_display_advanced_add_affiliate_link() {
376
-
377
- if ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX || ! current_user_can( apply_filters( 'ta_enable_advance_link_picker' , 'edit_posts' ) ) )
378
- wp_die( "You're not allowed to do this." );
379
-
380
- $post_id = isset( $_REQUEST[ 'post_id' ] ) ? intval( $_REQUEST[ 'post_id' ] ) : 0;
381
- $affiliate_links = $this->_helper_functions->search_affiliate_links_query();
382
- $result_markup = $this->search_affiliate_links_result_markup( $affiliate_links , true , $post_id );
383
- $html_editor = isset( $_REQUEST[ 'html_editor' ] ) ? sanitize_text_field( $_REQUEST[ 'html_editor' ] ) : false;
384
-
385
- wp_enqueue_script('editor');
386
- wp_dequeue_script('jquery-ui-core');
387
- wp_dequeue_script('jquery-ui-sortable');
388
- wp_dequeue_script('admin-scripts');
389
- wp_enqueue_style( 'jquery_tiptip' , $this->_constants->CSS_ROOT_URL() . 'lib/jquery-tiptip/jquery-tiptip.css' , array() , $this->_constants->VERSION() , 'all' );
390
- wp_enqueue_style( 'ta_advance_link_picker_css' , $this->_constants->JS_ROOT_URL() . 'app/advance_link_picker/dist/advance-link-picker.css' , array( 'dashicons' ) , $this->_constants->VERSION() , 'all' );
391
- wp_enqueue_style( 'selectize' , $this->_constants->JS_ROOT_URL() . 'lib/selectize/selectize.default.css' , array() , Plugin_Constants::VERSION , 'all' );
392
- wp_enqueue_script( 'jquery_tiptip' , $this->_constants->JS_ROOT_URL() . 'lib/jquery-tiptip/jquery.tipTip.min.js' , array() , $this->_constants->VERSION() );
393
- wp_enqueue_script( 'ta_advance_link_picker_js' , $this->_constants->JS_ROOT_URL() . 'app/advance_link_picker/dist/advance-link-picker.js' , array( 'jquery_tiptip' ) , $this->_constants->VERSION() );
394
- wp_enqueue_script( 'selectize' , $this->_constants->JS_ROOT_URL() . 'lib/selectize/selectize.min.js' , array() , Plugin_Constants::VERSION );
395
-
396
- include( $this->_constants->VIEWS_ROOT_PATH() . 'linkpicker/advance-link-picker.php' );
397
-
398
- wp_die();
399
- }
400
-
401
- /**
402
- * Get image markup by ID.
403
- *
404
- * @since 3.0.0
405
- * @since 3.4.0 Add support for external images.
406
- * @access public
407
- */
408
- public function ajax_get_image_markup_by_id() {
409
-
410
- if ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX )
411
- $response = array( 'status' => 'fail' , 'error_msg' => __( 'Invalid AJAX call' , 'thirstyaffiliates' ) );
412
- elseif ( ! isset( $_REQUEST[ 'imgid' ] ) )
413
- $response = array( 'status' => 'fail' , 'error_msg' => __( 'Missing required post data' , 'thirstyaffiliates' ) );
414
- else {
415
-
416
- if ( filter_var( $_REQUEST[ 'imgid' ] , FILTER_VALIDATE_URL ) ) {
417
-
418
- $image_url = esc_url( $_REQUEST[ 'imgid' ] );
419
- $image_markup = '<img src="' . $image_url . '">';
420
-
421
- } else {
422
-
423
- $image_id = (int) sanitize_text_field( $_REQUEST[ 'imgid' ] );
424
- $image_markup = wp_get_attachment_image( $image_id , 'full' );
425
- }
426
-
427
- $response = array( 'status' => 'success' , 'image_markup' => $image_markup );
428
- }
429
-
430
- @header( 'Content-Type: application/json; charset=' . get_option( 'blog_charset' ) );
431
- echo wp_json_encode( $response );
432
- wp_die();
433
- }
434
-
435
-
436
-
437
-
438
-
439
- /*
440
- |--------------------------------------------------------------------------
441
- | Quick Add Affiliate Link methods
442
- |--------------------------------------------------------------------------
443
- */
444
-
445
- /**
446
- * Display the quick add affiliate link content on the thickbox popup.
447
- *
448
- * @since 3.0.0
449
- * @since 3.4.0 Add global option for redirect type.
450
- * @access public
451
- */
452
- public function ajax_display_quick_add_affiliate_link_thickbox() {
453
-
454
- if ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX || ! current_user_can( apply_filters( 'ta_enable_quick_add_affiliate_link' , 'publish_posts' ) ) )
455
- wp_die( "You're not allowed to do this." );
456
-
457
- $post_id = isset( $_REQUEST[ 'post_id' ] ) ? intval( $_REQUEST[ 'post_id' ] ) : 0;
458
- $redirect_types = $this->_constants->REDIRECT_TYPES();
459
- $selection = sanitize_text_field( $_REQUEST[ 'selection' ] );
460
- $default_redirect_type = get_option( 'ta_link_redirect_type' , '301' );
461
- $global_no_follow = get_option( 'ta_no_follow' ) == 'yes' ? 'yes' : 'no';
462
- $global_new_window = get_option( 'ta_new_window' ) == 'yes' ? 'yes' : 'no';
463
- $html_editor = isset( $_REQUEST[ 'html_editor' ] ) ? sanitize_text_field( $_REQUEST[ 'html_editor' ] ) : false;
464
- $categories = get_terms( Plugin_Constants::AFFILIATE_LINKS_TAX , array(
465
- 'hide_empty' => false,
466
- ) );
467
-
468
- wp_enqueue_style( 'ta_quick_add_affiliate_link_css' , $this->_constants->JS_ROOT_URL() . 'app/quick_add_affiliate_link/dist/quick-add-affiliate-link.css' , array( 'dashicons' ) , $this->_constants->VERSION() , 'all' );
469
- wp_enqueue_style( 'selectize' , $this->_constants->JS_ROOT_URL() . 'lib/selectize/selectize.default.css' , array() , Plugin_Constants::VERSION , 'all' );
470
-
471
- wp_enqueue_script('editor');
472
- wp_dequeue_script('jquery-ui-core');
473
- wp_dequeue_script('jquery-ui-sortable');
474
- wp_dequeue_script('admin-scripts');
475
- wp_enqueue_script( 'ta_quick_add_affiliate_link_js' , $this->_constants->JS_ROOT_URL() . 'app/quick_add_affiliate_link/dist/quick-add-affiliate-link.js' , array() , $this->_constants->VERSION() );
476
- wp_enqueue_script( 'selectize', $this->_constants->JS_ROOT_URL() . 'lib/selectize/selectize.min.js' , array( 'jquery' , 'jquery-ui-core' , 'jquery-ui-sortable' ) , Plugin_Constants::VERSION );
477
-
478
- include( $this->_constants->VIEWS_ROOT_PATH() . 'linkpicker/quick-add-affiliate-link.php' );
479
-
480
- wp_die();
481
-
482
- }
483
-
484
- /**
485
- * Process quick add affiliate link. Create Affiliate link post.
486
- *
487
- * @since 3.0.0
488
- * @access public
489
- */
490
- public function process_quick_add_affiliate_link() {
491
-
492
- $thirstylink = new Affiliate_Link();
493
-
494
- // set Properties
495
- $thirstylink->set_prop( 'name' , sanitize_text_field( $_POST[ 'ta_link_name' ] ) );
496
- $thirstylink->set_prop( 'destination_url' , esc_url_raw( $_POST[ 'ta_destination_url' ] ) );
497
- $thirstylink->set_prop( 'no_follow' , sanitize_text_field( $_POST[ 'ta_no_follow' ] ) );
498
- $thirstylink->set_prop( 'new_window' , sanitize_text_field( $_POST[ 'ta_new_window' ] ) );
499
- $thirstylink->set_prop( 'redirect_type' , sanitize_text_field( $_POST[ 'ta_redirect_type' ] ) );
500
-
501
- add_action( 'ta_save_quick_add_affiliate_link' , $thirstylink );
502
-
503
- // save affiliate link
504
- $thirstylink->save();
505
-
506
- // save categories
507
- $this->quick_add_save_categories( $thirstylink );
508
- $this->_helper_functions->save_default_affiliate_link_category( $thirstylink->get_id() );
509
-
510
- return $thirstylink;
511
- }
512
-
513
- /**
514
- * Quick add: Save categories for affiliate link on.
515
- *
516
- * @since 3.2.0
517
- * @access private
518
- *
519
- * @param Affiliate_Link $thirstylink Affiliate link object.
520
- */
521
- private function quick_add_save_categories( $thirstylink ) {
522
-
523
- $categories = array();
524
- if ( isset( $_POST[ 'ta_link_categories' ] ) )
525
- $categories = array_map( 'intval' , $_POST[ 'ta_link_categories' ] );
526
-
527
- if ( ! empty( $categories ) )
528
- wp_set_post_terms( $thirstylink->get_id() , $categories , Plugin_Constants::AFFILIATE_LINKS_TAX );
529
- }
530
-
531
- /**
532
- * AJAX function to process quick add affiliate link.
533
- *
534
- * @since 3.0.0
535
- * @access public
536
- */
537
- public function ajax_process_quick_add_affiliate_link() {
538
-
539
- if ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX )
540
- $response = array( 'status' => 'fail' , 'error_msg' => __( 'Invalid AJAX call' , 'thirstyaffiliates' ) );
541
- elseif ( ! isset( $_REQUEST[ 'ta_link_name' ] ) || ! isset( $_REQUEST[ 'ta_destination_url' ] ) || ! isset( $_REQUEST[ 'ta_redirect_type' ] ) )
542
- $response = array( 'status' => 'fail' , 'error_msg' => __( 'Missing required post data' , 'thirstyaffiliates' ) );
543
- else {
544
-
545
- $thirstylink = $this->process_quick_add_affiliate_link();
546
- $post_id = isset( $_POST[ 'post_id' ] ) ? intval( sanitize_text_field( $_POST[ 'post_id' ] ) ) : 0;
547
- $rel = $thirstylink->is( 'no_follow' ) ? 'nofollow' : '';
548
- $target = $thirstylink->is( 'new_window' ) ? '_blank' : '';
549
- $class = ( get_option( 'ta_disable_thirsty_link_class' ) !== "yes" ) ? 'thirstylink' : '';
550
- $title = ( get_option( 'ta_disable_title_attribute' ) !== "yes" ) ? $thirstylink->get_prop( 'name' ) : '';
551
-
552
- if ( $thirstylink->get_prop( 'rel_tags' ) )
553
- $rel = trim( $rel . ' ' . $thirstylink->get_prop( 'rel_tags' ) );
554
-
555
- if ( $thirstylink->get_prop( 'css_classes' ) )
556
- $class = trim( $class . ' ' . $thirstylink->get_prop( 'css_classes' ) );
557
-
558
- $response = array(
559
- 'status' => 'success',
560
- 'link_id' => $thirstylink->get_id(),
561
- 'content' => $thirstylink->get_prop( 'name' ),
562
- 'href' => $thirstylink->get_prop( 'permalink' ),
563
- 'class' => $class,
564
- 'title' => str_replace( '"' , '' , $title ),
565
- 'rel' => $rel,
566
- 'target' => $target,
567
- 'link_insertion_type' => $this->_helper_functions->get_option( 'ta_link_insertion_type' , 'link' ),
568
- 'other_atts' => apply_filters( 'ta_link_insert_extend_data_attributes' , array() , $thirstylink , $post_id )
569
- );
570
-
571
- }
572
-
573
- @header( 'Content-Type: application/json; charset=' . get_option( 'blog_charset' ) );
574
- echo wp_json_encode( $response );
575
- wp_die();
576
- }
577
-
578
-
579
-
580
- /*
581
- |--------------------------------------------------------------------------
582
- | Shortcode editor modal interface
583
- |--------------------------------------------------------------------------
584
- */
585
-
586
- /**
587
- * AJAX display shortcode editor form.
588
- *
589
- * @since 3.4.0
590
- * @access public
591
- */
592
- public function ajax_display_shortcode_editor_form() {
593
-
594
- if ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX || ! current_user_can( apply_filters( 'ta_edit_affiliate_link_shortcode' , 'publish_posts' ) ) )
595
- wp_die( "You're not allowed to do this." );
596
-
597
- $post_id = isset( $_REQUEST[ 'post_id' ] ) ? intval( $_REQUEST[ 'post_id' ] ) : 0;
598
-
599
- wp_enqueue_style( 'edit-shortcode' , $this->_constants->JS_ROOT_URL() . 'app/edit_shortcode/dist/edit-shortcode.css' , array( 'dashicons' ) , $this->_constants->VERSION() , 'all' );
600
- wp_enqueue_style( 'selectize' , $this->_constants->JS_ROOT_URL() . 'lib/selectize/selectize.default.css' , array() , Plugin_Constants::VERSION , 'all' );
601
-
602
- wp_enqueue_script('editor');
603
- wp_dequeue_script('jquery-ui-core');
604
- wp_dequeue_script('jquery-ui-sortable');
605
- wp_dequeue_script('admin-scripts');
606
- wp_enqueue_script( 'edit-shortcode' , $this->_constants->JS_ROOT_URL() . 'app/edit_shortcode/dist/edit-shortcode.js' , array() , $this->_constants->VERSION() );
607
- wp_enqueue_script( 'selectize', $this->_constants->JS_ROOT_URL() . 'lib/selectize/selectize.min.js' , array( 'jquery' , 'jquery-ui-core' , 'jquery-ui-sortable' ) , Plugin_Constants::VERSION );
608
-
609
- include( $this->_constants->VIEWS_ROOT_PATH() . 'linkpicker/edit-shortcode.php' );
610
-
611
- wp_die();
612
- }
613
-
614
- /**
615
- * Transform Gutenberg affiliate link <ta> tags to <a> tags.
616
- *
617
- * @since 3.6
618
- * @access public
619
- *
620
- * @global WP_Post $post WP_Post object of currently loaded post.
621
- *
622
- * @param string $content WP_Post content.
623
- * @return string Filtered WP_Post content.
624
- */
625
- public function gutenberg_transform_ta_tags_to_link( $content ) {
626
-
627
- preg_match_all( '/<ta [^>]*>(.*?)<\/ta>/i' , $content , $matches , PREG_OFFSET_CAPTURE );
628
-
629
- if ( isset( $matches[0] ) && ! empty( $matches[0] ) ) {
630
-
631
- $diff = 0;
632
-
633
- foreach ( $matches[0] as $match ) {
634
-
635
- $link_id = $this->_helper_functions->get_string_between( $match[0] , 'linkid="' , '"' );
636
- $href = $this->_helper_functions->get_string_between( $match[0] , 'href="' , '"' );
637
- $text = $this->_helper_functions->get_string_between( $match[0] , '>' , '</ta' );
638
-
639
- if ( $link_id )
640
- $replacement = do_shortcode( '[thirstylink ids="' . $link_id . '"]' . $text . '[/thirstylink]' );
641
- else
642
- $replacement = '<a href="' . $href . '">' . $text . '</a>';
643
-
644
- $match[1] = $match[1] + $diff; // Fix the offset of the match after the last replacement
645
- $content = substr_replace( $content , $replacement , $match[1] , strlen( $match[0] ) );
646
-
647
- // Add the string length difference to the next match's offset
648
- // because all the string positions have moved since the first
649
- // replacement.
650
- $diff = $diff + strlen( $replacement ) - strlen( $match[0] );
651
- }
652
- }
653
-
654
- return $content;
655
- }
656
-
657
-
658
-
659
-
660
- /*
661
- |--------------------------------------------------------------------------
662
- | Fulfill implemented interface contracts
663
- |--------------------------------------------------------------------------
664
- */
665
-
666
- /**
667
- * Method that houses codes to be executed on init hook.
668
- *
669
- * @since 3.0.0
670
- * @access public
671
- * @inherit ThirstyAffiliates\Interfaces\Initiable_Interface
672
- */
673
- public function initialize() {
674
-
675
- // // TinyMCE buttons
676
- $this->init_thirsty_editor_buttons();
677
- $this->init_thirsty_editor_buttons_for_page_builders();
678
-
679
- // Advanced Link Picker hooks
680
- add_action( 'wp_ajax_search_affiliate_links_query' , array( $this , 'ajax_search_affiliate_links_query' ) );
681
- add_action( 'wp_ajax_ta_advanced_add_affiliate_link' , array( $this , 'ajax_display_advanced_add_affiliate_link' ) );
682
- add_action( 'wp_ajax_ta_get_image_markup_by_id' , array( $this , 'ajax_get_image_markup_by_id' ) );
683
-
684
- // Quick Add Affiliate Link hooks
685
- add_action( 'wp_ajax_ta_quick_add_affiliate_link_thickbox' , array( $this , 'ajax_display_quick_add_affiliate_link_thickbox' ) );
686
- add_action( 'wp_ajax_ta_process_quick_add_affiliate_link' , array( $this , 'ajax_process_quick_add_affiliate_link' ) );
687
-
688
- // edit shortcode
689
- add_action( 'wp_ajax_ta_edit_affiliate_link_shortcode' , array( $this , 'ajax_display_shortcode_editor_form' ) );
690
-
691
- }
692
-
693
- /**
694
- * Execute link picker.
695
- *
696
- * @since 3.0.0
697
- * @access public
698
- */
699
- public function run() {
700
-
701
- // TinyMCE buttons
702
- add_action( 'wp' , array( $this , 'init_thirsty_editor_buttons_for_page_builders' ) );
703
-
704
- // WP editor style
705
- add_action( 'admin_init' , array( $this , 'register_wp_editor_style' ) );
706
- add_filter( 'mce_css' , array( $this , 'register_wp_editor_style_mce_css' ) );
707
-
708
- add_filter( 'the_content' , array( $this , 'gutenberg_transform_ta_tags_to_link' ) );
709
- }
710
- }
1
+ <?php
2
+
3
+ namespace ThirstyAffiliates\Models;
4
+
5
+ use ThirstyAffiliates\Abstracts\Abstract_Main_Plugin_Class;
6
+
7
+ use ThirstyAffiliates\Interfaces\Model_Interface;
8
+ use ThirstyAffiliates\Interfaces\Initiable_Interface;
9
+
10
+ use ThirstyAffiliates\Helpers\Plugin_Constants;
11
+ use ThirstyAffiliates\Helpers\Helper_Functions;
12
+
13
+ use ThirstyAffiliates\Models\Affiliate_Link;
14
+
15
+ /**
16
+ * Model that houses the link picker logic.
17
+ *
18
+ * @since 3.0.0
19
+ */
20
+ class Link_Picker implements Model_Interface , Initiable_Interface {
21
+
22
+ /*
23
+ |--------------------------------------------------------------------------
24
+ | Class Properties
25
+ |--------------------------------------------------------------------------
26
+ */
27
+
28
+ /**
29
+ * Property that holds the single main instance of Bootstrap.
30
+ *
31
+ * @since 3.0.0
32
+ * @access private
33
+ * @var Link_Picker
34
+ */
35
+ private static $_instance;
36
+
37
+ /**
38
+ * Model that houses the main plugin object.
39
+ *
40
+ * @since 3.0.0
41
+ * @access private
42
+ * @var Abstract_Main_Plugin_Class
43
+ */
44
+ private $_main_plugin;
45
+
46
+ /**
47
+ * Model that houses all the plugin constants.
48
+ *
49
+ * @since 3.0.0
50
+ * @access private
51
+ * @var Plugin_Constants
52
+ */
53
+ private $_constants;
54
+
55
+ /**
56
+ * Property that houses all the helper functions of the plugin.
57
+ *
58
+ * @since 3.0.0
59
+ * @access private
60
+ * @var Helper_Functions
61
+ */
62
+ private $_helper_functions;
63
+
64
+
65
+
66
+
67
+ /*
68
+ |--------------------------------------------------------------------------
69
+ | Class Methods
70
+ |--------------------------------------------------------------------------
71
+ */
72
+
73
+ /**
74
+ * Class constructor.
75
+ *
76
+ * @since 3.0.0
77
+ * @access public
78
+ *
79
+ * @param Abstract_Main_Plugin_Class $main_plugin Main plugin object.
80
+ * @param Plugin_Constants $constants Plugin constants object.
81
+ * @param Helper_Functions $helper_functions Helper functions object.
82
+ */
83
+ public function __construct( Abstract_Main_Plugin_Class $main_plugin , Plugin_Constants $constants , Helper_Functions $helper_functions ) {
84
+
85
+ $this->_constants = $constants;
86
+ $this->_helper_functions = $helper_functions;
87
+
88
+ $main_plugin->add_to_all_plugin_models( $this );
89
+
90
+ }
91
+
92
+ /**
93
+ * Ensure that only one instance of this class is loaded or can be loaded ( Singleton Pattern ).
94
+ *
95
+ * @since 3.0.0
96
+ * @access public
97
+ *
98
+ * @param Abstract_Main_Plugin_Class $main_plugin Main plugin object.
99
+ * @param Plugin_Constants $constants Plugin constants object.
100
+ * @param Helper_Functions $helper_functions Helper functions object.
101
+ * @return Link_Picker
102
+ */
103
+ public static function get_instance( Abstract_Main_Plugin_Class $main_plugin , Plugin_Constants $constants , Helper_Functions $helper_functions ) {
104
+
105
+ if ( !self::$_instance instanceof self )
106
+ self::$_instance = new self( $main_plugin , $constants , $helper_functions );
107
+
108
+ return self::$_instance;
109
+
110
+ }
111
+
112
+
113
+
114
+ /*
115
+ |--------------------------------------------------------------------------
116
+ | Register tinymce buttons and scripts
117
+ |--------------------------------------------------------------------------
118
+ */
119
+
120
+ /**
121
+ * Initialize thirsty editor buttons.
122
+ *
123
+ * @since 3.0.0
124
+ * @access public
125
+ */
126
+ public function init_thirsty_editor_buttons() {
127
+
128
+ if ( ! is_admin() || ! current_user_can( 'edit_posts' ) )
129
+ return;
130
+
131
+ if ( get_option( 'ta_disable_visual_editor_buttons' ) == 'yes' || get_user_option( 'rich_editing' ) != 'true' )
132
+ return;
133
+
134
+ add_filter( 'mce_external_plugins' , array( $this , 'load_thirsty_mce_plugin' ) );
135
+ add_filter( 'mce_buttons' , array( $this , 'register_mce_buttons' ) , 5 );
136
+
137
+ }
138
+
139
+ /**
140
+ * Initialize thirsty editor buttons on page builders.
141
+ *
142
+ * @since 3.4.0
143
+ * @access public
144
+ */
145
+ public function init_thirsty_editor_buttons_for_page_builders() {
146
+
147
+ if ( get_option( 'ta_disable_visual_editor_buttons' ) == 'yes' || get_user_option( 'rich_editing' ) != 'true' )
148
+ return;
149
+
150
+ if ( ! $this->_helper_functions->is_page_builder_active() || ! current_user_can( 'edit_posts' ) )
151
+ return;
152
+
153
+ add_filter( 'mce_external_plugins' , array( $this , 'load_thirsty_mce_plugin' ) , 99999 );
154
+ add_filter( 'mce_buttons' , array( $this , 'register_mce_buttons' ) , 99 );
155
+ }
156
+
157
+ /**
158
+ * Load Thirsty Affiliate MCE plugin to TinyMCE.
159
+ *
160
+ * @since 3.0.0
161
+ * @access public
162
+ *
163
+ * @param array $mce_plugins Array of all MCE plugins.
164
+ * @return array
165
+ */
166
+ public function load_thirsty_mce_plugin( $mce_plugins ) {
167
+
168
+ $mce_plugins[ 'thirstyaffiliates' ] = $this->_constants->JS_ROOT_URL() . 'lib/thirstymce/editor-plugin.js';
169
+
170
+ return $mce_plugins;
171
+ }
172
+
173
+ /**
174
+ * Register Thirsty Affiliate MCE buttons.
175
+ *
176
+ * @since 3.0.0
177
+ * @since 3.4.0 Make sure only one instance of the buttons are loaded.
178
+ * @access public
179
+ *
180
+ * @param array $buttons Array of all MCE buttons.
181
+ * @return array
182
+ */
183
+ public function register_mce_buttons( $buttons ) {
184
+
185
+ // if buttons are already present, then skip to prevent duplicate.
186
+ if ( in_array( 'thirstyaffiliates_button' , $buttons ) )
187
+ return $buttons;
188
+
189
+ $enable_link_picker = current_user_can( apply_filters( 'ta_enable_advance_link_picker' , 'edit_posts' ) );
190
+ $enable_quick_add = current_user_can( apply_filters( 'ta_enable_quick_add_affiliate_link' , 'publish_posts' ) );
191
+
192
+ if ( $enable_link_picker ) array_push( $buttons , 'separator' , 'thirstyaffiliates_button' );
193
+ if ( $enable_quick_add ) array_push( $buttons , 'separator' , 'thirstyaffiliates_quickaddlink_button' );
194
+
195
+ return $buttons;
196
+ }
197
+
198
+ /**
199
+ * Register WP Editor style.
200
+ *
201
+ * @since 3.4.0
202
+ * @access public
203
+ */
204
+ public function register_wp_editor_style() {
205
+
206
+ add_editor_style( $this->_constants->CSS_ROOT_URL() . 'admin/tinymce/editor.css' );
207
+ }
208
+
209
+ /**
210
+ * Register WP Editor style via mce_css filter (for frontend editors).
211
+ *
212
+ * @since 3.4.0
213
+ * @access public
214
+ */
215
+ public function register_wp_editor_style_mce_css( $mce_css ) {
216
+
217
+ if ( ! empty( $mce_css ) ) $mce_css .= ',';
218
+ $mce_css .= $this->_constants->CSS_ROOT_URL() . 'admin/tinymce/editor.css';
219
+
220
+ return $mce_css;
221
+ }
222
+
223
+
224
+
225
+
226
+ /*
227
+ |--------------------------------------------------------------------------
228
+ | Link Picker methods
229
+ |--------------------------------------------------------------------------
230
+ */
231
+
232
+ /**
233
+ * Return
234
+ *
235
+ * @since 3.0.0
236
+ * @since 3.4.0 Add support for additional CSS classes field. Add support for external images.
237
+ * @access public
238
+ *
239
+ * @param array $affiliate_links List of affiliate link IDs.
240
+ * @param bool $advanced Boolean check if its advanced or not.
241
+ * @param int $post_id ID of the post currently being edited.
242
+ * @param string $result_markup Search Affiliate Links result markup.
243
+ * @return Search Affiliate Links result markup
244
+ */
245
+ public function search_affiliate_links_result_markup( $affiliate_links , $advance = false , $post_id = 0 , $result_markup = '' ) {
246
+
247
+ if ( is_array( $affiliate_links ) && ! empty( $affiliate_links ) ) {
248
+
249
+ foreach( $affiliate_links as $link_id ) {
250
+
251
+ $thirstylink = new Affiliate_Link( $link_id );
252
+ $rel = $thirstylink->is( 'no_follow' ) ? 'nofollow' : '';
253
+ $target = $thirstylink->is( 'new_window' ) ? '_blank' : '';
254
+ $class = ( get_option( 'ta_disable_thirsty_link_class' ) !== "yes" ) ? 'thirstylink' : '';
255
+ $title = ( get_option( 'ta_disable_title_attribute' ) !== "yes" ) ? $thirstylink->get_prop( 'name' ) : '';
256
+ $other_atts = esc_attr( json_encode( apply_filters( 'ta_link_insert_extend_data_attributes' , array() , $thirstylink , $post_id ) ) );
257
+
258
+ if ( $thirstylink->get_prop( 'rel_tags' ) )
259
+ $rel = trim( $rel . ' ' . $thirstylink->get_prop( 'rel_tags' ) );
260
+
261
+ if ( $thirstylink->get_prop( 'css_classes' ) )
262
+ $class = trim( $class . ' ' . $thirstylink->get_prop( 'css_classes' ) );
263
+
264
+ if ( $advance ) {
265
+
266
+ $images = $thirstylink->get_prop( 'image_ids' );
267
+ $images_markup = '<span class="images-block">';
268
+
269
+ if ( is_array( $images ) && ! empty( $images ) ) {
270
+
271
+ $images_markup .= '<span class="label">' . __( 'Select image:' , 'thirstyaffiliates' ) . '</span>';
272
+ $images_markup .= '<span class="images">';
273
+
274
+ foreach( $images as $image ) {
275
+
276
+ if ( filter_var( $image , FILTER_VALIDATE_URL ) )
277
+ $images_markup .= '<span class="image"><img src="'. $image .'" width="75" height="75" data-imgid="' . $image . '" data-type="image"></span>';
278
+ else
279
+ $images_markup .= '<span class="image">' . wp_get_attachment_image( $image , array( 75 , 75 ) , false , array( 'data-imgid' => $image , 'data-type' => 'image' ) ) . '</span>';
280
+
281
+ }
282
+
283
+ $images_markup .= '</span>';
284
+ } else {
285
+
286
+ $images_markup .= '<span class="no-images">' . __( 'No images found' , 'thirstyaffiliates' ) . '</span>';
287
+ }
288
+
289
+ $images_markup .= '</span>';
290
+
291
+ $result_markup .= '<li class="thirstylink"
292
+ data-linkid="' . $thirstylink->get_id() . '"
293
+ data-class="' . esc_attr( $class ) . '"
294
+ data-title="' . esc_attr( str_replace( '"' , '' , $title ) ) . '"
295
+ data-href="' . esc_url( $thirstylink->get_prop( 'permalink' ) ) . '"
296
+ data-rel="' . trim( esc_attr( $rel ) ) . '"
297
+ data-target="' . esc_attr( $target ) . '"
298
+ data-other-atts="' . $other_atts . '">
299
+ <span class="name">' . mb_strimwidth( $thirstylink->get_prop( 'name' ) , 0 , 44 , "..." ) . '</span>
300
+ <span class="slug">[' . mb_strimwidth( $thirstylink->get_prop( 'slug' ) , 0 , 35 , "..." ) . ']</span>
301
+ <span class="actions">
302
+ <button type="button" data-type="normal" class="button insert-link-button dashicons dashicons-admin-links" data-tip="' . __( 'Insert link' , 'thirstyaffiliates' ) . '"></button>
303
+ <button type="button" data-type="shortcode" class="button insert-shortcode-button dashicons dashicons-editor-code" data-tip="' . __( 'Insert shortcode' , 'thirstyaffiliates' ) . '"></button>
304
+ <button type="button" data-type="image" class="button insert-image-button dashicons dashicons-format-image" data-tip="' . __( 'Insert image' , 'thirstyaffiliates' ) . '"></button>
305
+ </span>
306
+ ' . $images_markup . '
307
+ </li>';
308
+ } else {
309
+
310
+ $result_markup .= '<li data-class="' . esc_attr( $class ) . '"
311
+ data-title="' . esc_attr( str_replace( '"' , '' , $title ) ) . '"
312
+ data-href="' . esc_attr( $thirstylink->get_prop( 'permalink' ) ) . '"
313
+ data-rel="' . esc_attr( $rel ) . '"
314
+ data-target="' . esc_attr( $target ) . '"
315
+ data-link-id="' . esc_attr( $thirstylink->get_id() ) . '"
316
+ data-link-insertion-type="' . esc_attr( $this->_helper_functions->get_option( 'ta_link_insertion_type' , 'link' ) ) . '"
317
+ data-other-atts="' . $other_atts . '">';
318
+ $result_markup .= '<strong>' . $link_id . '</strong> : <span>' . $thirstylink->get_prop( 'name' ) . '</span></li>';
319
+
320
+ }
321
+
322
+ }
323
+
324
+ } else
325
+ $result_markup .= '<li class="no-links-found">' . __( 'No affiliate links found' , 'thirstyaffiliates' ) . '</li>';
326
+
327
+ return $result_markup;
328
+ }
329
+
330
+ /**
331
+ * Search Affiliate Links Query AJAX function
332
+ *
333
+ * @since 3.0.0
334
+ * @since 3.4.0 Add support for category in the search query.
335
+ * @access public
336
+ */
337
+ public function ajax_search_affiliate_links_query() {
338
+
339
+ if ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX )
340
+ $response = array( 'status' => 'fail' , 'error_msg' => __( 'Invalid AJAX call' , 'thirstyaffiliates' ) );
341
+ elseif ( ! isset( $_POST[ 'keyword' ] ) )
342
+ $response = array( 'status' => 'fail' , 'error_msg' => __( 'Missing required post data' , 'thirstyaffiliates' ) );
343
+ else {
344
+
345
+ $paged = ( isset( $_POST[ 'paged' ] ) && $_POST[ 'paged' ] ) ? $_POST[ 'paged' ] : 1;
346
+ $category = ( isset( $_POST[ 'category' ] ) && $_POST[ 'category' ] ) ? esc_attr( $_POST[ 'category' ] ) : '';
347
+ $exclude = ( isset( $_POST[ 'exclude' ] ) && is_array( $_POST[ 'exclude' ] ) && ! empty( $_POST[ 'exclude' ] ) ) ? $_POST[ 'exclude' ] : array();
348
+ $is_gutenberg = isset( $_POST[ 'gutenberg' ] ) && $_POST[ 'gutenberg' ];
349
+ $with_images = isset( $_POST[ 'with_images' ] ) && $_POST[ 'with_images' ];
350
+ $affiliate_links = $this->_helper_functions->search_affiliate_links_query( $_POST[ 'keyword' ] , $paged , $category , $exclude , $is_gutenberg , $with_images );
351
+ $advance = ( isset( $_POST[ 'advance' ] ) && $_POST[ 'advance' ] ) ? true : false;
352
+ $post_id = isset( $_POST[ 'post_id' ] ) ? intval( $_POST[ 'post_id' ] ) : 0;
353
+
354
+ if ( $is_gutenberg ) {
355
+ $response = array( 'status' => 'success' , 'affiliate_links' => $affiliate_links , 'count' => count( $affiliate_links ) );
356
+ } else {
357
+ $result_markup = $this->search_affiliate_links_result_markup( $affiliate_links , $advance , $post_id );
358
+ $response = array( 'status' => 'success' , 'search_query_markup' => $result_markup , 'count' => count( $affiliate_links ) );
359
+ }
360
+
361
+ }
362
+
363
+ @header( 'Content-Type: application/json; charset=' . get_option( 'blog_charset' ) );
364
+ echo wp_json_encode( $response );
365
+ wp_die();
366
+ }
367
+
368
+ /**
369
+ * AJAX function to display the advance add affiliate link thickbox content.
370
+ *
371
+ * @since 3.0.0
372
+ * @since 3.4.0 Add category field (selectized) in the search form.
373
+ * @access public
374
+ */
375
+ public function ajax_display_advanced_add_affiliate_link() {
376
+
377
+ if ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX || ! current_user_can( apply_filters( 'ta_enable_advance_link_picker' , 'edit_posts' ) ) )
378
+ wp_die( "You're not allowed to do this." );
379
+
380
+ $post_id = isset( $_REQUEST[ 'post_id' ] ) ? intval( $_REQUEST[ 'post_id' ] ) : 0;
381
+ $affiliate_links = $this->_helper_functions->search_affiliate_links_query();
382
+ $result_markup = $this->search_affiliate_links_result_markup( $affiliate_links , true , $post_id );
383
+ $html_editor = isset( $_REQUEST[ 'html_editor' ] ) ? sanitize_text_field( $_REQUEST[ 'html_editor' ] ) : false;
384
+
385
+ wp_enqueue_script('editor');
386
+ wp_dequeue_script('jquery-ui-core');
387
+ wp_dequeue_script('jquery-ui-sortable');
388
+ wp_dequeue_script('admin-scripts');
389
+ wp_enqueue_style( 'jquery_tiptip' , $this->_constants->CSS_ROOT_URL() . 'lib/jquery-tiptip/jquery-tiptip.css' , array() , $this->_constants->VERSION() , 'all' );
390
+ wp_enqueue_style( 'ta_advance_link_picker_css' , $this->_constants->JS_ROOT_URL() . 'app/advance_link_picker/dist/advance-link-picker.css' , array( 'dashicons' ) , $this->_constants->VERSION() , 'all' );
391
+ wp_enqueue_style( 'selectize' , $this->_constants->JS_ROOT_URL() . 'lib/selectize/selectize.default.css' , array() , Plugin_Constants::VERSION , 'all' );
392
+ wp_enqueue_script( 'jquery_tiptip' , $this->_constants->JS_ROOT_URL() . 'lib/jquery-tiptip/jquery.tipTip.min.js' , array() , $this->_constants->VERSION() );
393
+ wp_enqueue_script( 'ta_advance_link_picker_js' , $this->_constants->JS_ROOT_URL() . 'app/advance_link_picker/dist/advance-link-picker.js' , array( 'jquery_tiptip' ) , $this->_constants->VERSION() );
394
+ wp_enqueue_script( 'selectize' , $this->_constants->JS_ROOT_URL() . 'lib/selectize/selectize.min.js' , array() , Plugin_Constants::VERSION );
395
+
396
+ include( $this->_constants->VIEWS_ROOT_PATH() . 'linkpicker/advance-link-picker.php' );
397
+
398
+ wp_die();
399
+ }
400
+
401
+ /**
402
+ * Get image markup by ID.
403
+ *
404
+ * @since 3.0.0
405
+ * @since 3.4.0 Add support for external images.
406
+ * @access public
407
+ */
408
+ public function ajax_get_image_markup_by_id() {
409
+
410
+ if ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX )
411
+ $response = array( 'status' => 'fail' , 'error_msg' => __( 'Invalid AJAX call' , 'thirstyaffiliates' ) );
412
+ elseif ( ! isset( $_REQUEST[ 'imgid' ] ) )
413
+ $response = array( 'status' => 'fail' , 'error_msg' => __( 'Missing required post data' , 'thirstyaffiliates' ) );
414
+ else {
415
+
416
+ if ( filter_var( $_REQUEST[ 'imgid' ] , FILTER_VALIDATE_URL ) ) {
417
+
418
+ $image_url = esc_url( $_REQUEST[ 'imgid' ] );
419
+ $image_markup = '<img src="' . $image_url . '">';
420
+
421
+ } else {
422
+
423
+ $image_id = (int) sanitize_text_field( $_REQUEST[ 'imgid' ] );
424
+ $image_markup = wp_get_attachment_image( $image_id , 'full' );
425
+ }
426
+
427
+ $response = array( 'status' => 'success' , 'image_markup' => $image_markup );
428
+ }
429
+
430
+ @header( 'Content-Type: application/json; charset=' . get_option( 'blog_charset' ) );
431
+ echo wp_json_encode( $response );
432
+ wp_die();
433
+ }
434
+
435
+
436
+
437
+
438
+
439
+ /*
440
+ |--------------------------------------------------------------------------
441
+ | Quick Add Affiliate Link methods
442
+ |--------------------------------------------------------------------------
443
+ */
444
+
445
+ /**
446
+ * Display the quick add affiliate link content on the thickbox popup.
447
+ *
448
+ * @since 3.0.0
449
+ * @since 3.4.0 Add global option for redirect type.
450
+ * @access public
451
+ */
452
+ public function ajax_display_quick_add_affiliate_link_thickbox() {
453
+
454
+ if ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX || ! current_user_can( apply_filters( 'ta_enable_quick_add_affiliate_link' , 'publish_posts' ) ) )
455
+ wp_die( "You're not allowed to do this." );
456
+
457
+ $post_id = isset( $_REQUEST[ 'post_id' ] ) ? intval( $_REQUEST[ 'post_id' ] ) : 0;
458
+ $redirect_types = $this->_constants->REDIRECT_TYPES();
459
+ $selection = sanitize_text_field( $_REQUEST[ 'selection' ] );
460
+ $default_redirect_type = get_option( 'ta_link_redirect_type' , '301' );
461
+ $global_no_follow = get_option( 'ta_no_follow' ) == 'yes' ? 'yes' : 'no';
462
+ $global_new_window = get_option( 'ta_new_window' ) == 'yes' ? 'yes' : 'no';
463
+ $html_editor = isset( $_REQUEST[ 'html_editor' ] ) ? sanitize_text_field( $_REQUEST[ 'html_editor' ] ) : false;
464
+ $categories = get_terms( Plugin_Constants::AFFILIATE_LINKS_TAX , array(
465
+ 'hide_empty' => false,
466
+ ) );
467
+
468
+ wp_enqueue_style( 'ta_quick_add_affiliate_link_css' , $this->_constants->JS_ROOT_URL() . 'app/quick_add_affiliate_link/dist/quick-add-affiliate-link.css' , array( 'dashicons' ) , $this->_constants->VERSION() , 'all' );
469
+ wp_enqueue_style( 'selectize' , $this->_constants->JS_ROOT_URL() . 'lib/selectize/selectize.default.css' , array() , Plugin_Constants::VERSION , 'all' );
470
+
471
+ wp_enqueue_script('editor');
472
+ wp_dequeue_script('jquery-ui-core');
473
+ wp_dequeue_script('jquery-ui-sortable');
474
+ wp_dequeue_script('admin-scripts');
475
+ wp_enqueue_script( 'ta_quick_add_affiliate_link_js' , $this->_constants->JS_ROOT_URL() . 'app/quick_add_affiliate_link/dist/quick-add-affiliate-link.js' , array() , $this->_constants->VERSION() );
476
+ wp_enqueue_script( 'selectize', $this->_constants->JS_ROOT_URL() . 'lib/selectize/selectize.min.js' , array( 'jquery' , 'jquery-ui-core' , 'jquery-ui-sortable' ) , Plugin_Constants::VERSION );
477
+
478
+ include( $this->_constants->VIEWS_ROOT_PATH() . 'linkpicker/quick-add-affiliate-link.php' );
479
+
480
+ wp_die();
481
+
482
+ }
483
+
484
+ /**
485
+ * Process quick add affiliate link. Create Affiliate link post.
486
+ *
487
+ * @since 3.0.0
488
+ * @access public
489
+ */
490
+ public function process_quick_add_affiliate_link() {
491
+
492
+ $thirstylink = new Affiliate_Link();
493
+
494
+ // set Properties
495
+ $thirstylink->set_prop( 'name' , sanitize_text_field( $_POST[ 'ta_link_name' ] ) );
496
+ $thirstylink->set_prop( 'destination_url' , esc_url_raw( $_POST[ 'ta_destination_url' ] ) );
497
+ $thirstylink->set_prop( 'no_follow' , sanitize_text_field( $_POST[ 'ta_no_follow' ] ) );
498
+ $thirstylink->set_prop( 'new_window' , sanitize_text_field( $_POST[ 'ta_new_window' ] ) );
499
+ $thirstylink->set_prop( 'redirect_type' , sanitize_text_field( $_POST[ 'ta_redirect_type' ] ) );
500
+
501
+ add_action( 'ta_save_quick_add_affiliate_link' , $thirstylink );
502
+
503
+ // save affiliate link
504
+ $thirstylink->save();
505
+
506
+ // save categories
507
+ $this->quick_add_save_categories( $thirstylink );
508
+ $this->_helper_functions->save_default_affiliate_link_category( $thirstylink->get_id() );
509
+
510
+ return $thirstylink;
511
+ }
512
+
513
+ /**
514
+ * Quick add: Save categories for affiliate link on.
515
+ *
516
+ * @since 3.2.0
517
+ * @access private
518
+ *
519
+ * @param Affiliate_Link $thirstylink Affiliate link object.
520
+ */
521
+ private function quick_add_save_categories( $thirstylink ) {
522
+
523
+ $categories = array();
524
+ if ( isset( $_POST[ 'ta_link_categories' ] ) )
525
+ $categories = array_map( 'intval' , $_POST[ 'ta_link_categories' ] );
526
+
527
+ if ( ! empty( $categories ) )
528
+ wp_set_post_terms( $thirstylink->get_id() , $categories , Plugin_Constants::AFFILIATE_LINKS_TAX );
529
+ }
530
+
531
+ /**
532
+ * AJAX function to process quick add affiliate link.
533
+ *
534
+ * @since 3.0.0
535
+ * @access public
536
+ */
537
+ public function ajax_process_quick_add_affiliate_link() {
538
+
539
+ if ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX )
540
+ $response = array( 'status' => 'fail' , 'error_msg' => __( 'Invalid AJAX call' , 'thirstyaffiliates' ) );
541
+ elseif ( ! isset( $_REQUEST[ 'ta_link_name' ] ) || ! isset( $_REQUEST[ 'ta_destination_url' ] ) || ! isset( $_REQUEST[ 'ta_redirect_type' ] ) )
542
+ $response = array( 'status' => 'fail' , 'error_msg' => __( 'Missing required post data' , 'thirstyaffiliates' ) );
543
+ else {
544
+
545
+ $thirstylink = $this->process_quick_add_affiliate_link();
546
+ $post_id = isset( $_POST[ 'post_id' ] ) ? intval( sanitize_text_field( $_POST[ 'post_id' ] ) ) : 0;
547
+ $rel = $thirstylink->is( 'no_follow' ) ? 'nofollow' : '';
548
+ $target = $thirstylink->is( 'new_window' ) ? '_blank' : '';
549
+ $class = ( get_option( 'ta_disable_thirsty_link_class' ) !== "yes" ) ? 'thirstylink' : '';
550
+ $title = ( get_option( 'ta_disable_title_attribute' ) !== "yes" ) ? $thirstylink->get_prop( 'name' ) : '';
551
+
552
+ if ( $thirstylink->get_prop( 'rel_tags' ) )
553
+ $rel = trim( $rel . ' ' . $thirstylink->get_prop( 'rel_tags' ) );
554
+
555
+ if ( $thirstylink->get_prop( 'css_classes' ) )
556
+ $class = trim( $class . ' ' . $thirstylink->get_prop( 'css_classes' ) );
557
+
558
+ $response = array(
559
+ 'status' => 'success',
560
+ 'link_id' => $thirstylink->get_id(),
561
+ 'content' => $thirstylink->get_prop( 'name' ),
562
+ 'href' => $thirstylink->get_prop( 'permalink' ),
563
+ 'class' => $class,
564
+ 'title' => str_replace( '"' , '' , $title ),
565
+ 'rel' => $rel,
566
+ 'target' => $target,
567
+ 'link_insertion_type' => $this->_helper_functions->get_option( 'ta_link_insertion_type' , 'link' ),
568
+ 'other_atts' => apply_filters( 'ta_link_insert_extend_data_attributes' , array() , $thirstylink , $post_id )
569
+ );
570
+
571
+ }
572
+
573
+ @header( 'Content-Type: application/json; charset=' . get_option( 'blog_charset' ) );
574
+ echo wp_json_encode( $response );
575
+ wp_die();
576
+ }
577
+
578
+
579
+
580
+ /*
581
+ |--------------------------------------------------------------------------
582
+ | Shortcode editor modal interface
583
+ |--------------------------------------------------------------------------
584
+ */
585
+
586
+ /**
587
+ * AJAX display shortcode editor form.
588
+ *
589
+ * @since 3.4.0
590
+ * @access public
591
+ */
592
+ public function ajax_display_shortcode_editor_form() {
593
+
594
+ if ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX || ! current_user_can( apply_filters( 'ta_edit_affiliate_link_shortcode' , 'publish_posts' ) ) )
595
+ wp_die( "You're not allowed to do this." );
596
+
597
+ $post_id = isset( $_REQUEST[ 'post_id' ] ) ? intval( $_REQUEST[ 'post_id' ] ) : 0;
598
+
599
+ wp_enqueue_style( 'edit-shortcode' , $this->_constants->JS_ROOT_URL() . 'app/edit_shortcode/dist/edit-shortcode.css' , array( 'dashicons' ) , $this->_constants->VERSION() , 'all' );
600
+ wp_enqueue_style( 'selectize' , $this->_constants->JS_ROOT_URL() . 'lib/selectize/selectize.default.css' , array() , Plugin_Constants::VERSION , 'all' );
601
+
602
+ wp_enqueue_script('editor');
603
+ wp_dequeue_script('jquery-ui-core');
604
+ wp_dequeue_script('jquery-ui-sortable');
605
+ wp_dequeue_script('admin-scripts');
606
+ wp_enqueue_script( 'edit-shortcode' , $this->_constants->JS_ROOT_URL() . 'app/edit_shortcode/dist/edit-shortcode.js' , array() , $this->_constants->VERSION() );
607
+ wp_enqueue_script( 'selectize', $this->_constants->JS_ROOT_URL() . 'lib/selectize/selectize.min.js' , array( 'jquery' , 'jquery-ui-core' , 'jquery-ui-sortable' ) , Plugin_Constants::VERSION );
608
+
609
+ include( $this->_constants->VIEWS_ROOT_PATH() . 'linkpicker/edit-shortcode.php' );
610
+
611
+ wp_die();
612
+ }
613
+
614
+ /**
615
+ * Transform Gutenberg affiliate link <ta> tags to <a> tags.
616
+ *
617
+ * @since 3.6
618
+ * @access public
619
+ *
620
+ * @global WP_Post $post WP_Post object of currently loaded post.
621
+ *
622
+ * @param string $content WP_Post content.
623
+ * @return string Filtered WP_Post content.
624
+ */
625
+ public function gutenberg_transform_ta_tags_to_link( $content ) {
626
+
627
+ preg_match_all( '/<ta [^>]*>(.*?)<\/ta>/i' , $content , $matches , PREG_OFFSET_CAPTURE );
628
+
629
+ if ( isset( $matches[0] ) && ! empty( $matches[0] ) ) {
630
+
631
+ $diff = 0;
632
+
633
+ foreach ( $matches[0] as $match ) {
634
+
635
+ $link_id = $this->_helper_functions->get_string_between( $match[0] , 'linkid="' , '"' );
636
+ $href = $this->_helper_functions->get_string_between( $match[0] , 'href="' , '"' );
637
+ $text = $this->_helper_functions->get_string_between( $match[0] , '>' , '</ta' );
638
+
639
+ if ( $link_id )
640
+ $replacement = do_shortcode( '[thirstylink ids="' . $link_id . '"]' . $text . '[/thirstylink]' );
641
+ else
642
+ $replacement = '<a href="' . $href . '">' . $text . '</a>';
643
+
644
+ $match[1] = $match[1] + $diff; // Fix the offset of the match after the last replacement
645
+ $content = substr_replace( $content , $replacement , $match[1] , strlen( $match[0] ) );
646
+
647
+ // Add the string length difference to the next match's offset
648
+ // because all the string positions have moved since the first
649
+ // replacement.
650
+ $diff = $diff + strlen( $replacement ) - strlen( $match[0] );
651
+ }
652
+ }
653
+
654
+ return $content;
655
+ }
656
+
657
+
658
+
659
+
660
+ /*
661
+ |--------------------------------------------------------------------------
662
+ | Fulfill implemented interface contracts
663
+ |--------------------------------------------------------------------------
664
+ */
665
+
666
+ /**
667
+ * Method that houses codes to be executed on init hook.
668
+ *
669
+ * @since 3.0.0
670
+ * @access public
671
+ * @inherit ThirstyAffiliates\Interfaces\Initiable_Interface
672
+ */
673
+ public function initialize() {
674
+
675
+ // // TinyMCE buttons
676
+ $this->init_thirsty_editor_buttons();
677
+ $this->init_thirsty_editor_buttons_for_page_builders();
678
+
679
+ // Advanced Link Picker hooks
680
+ add_action( 'wp_ajax_search_affiliate_links_query' , array( $this , 'ajax_search_affiliate_links_query' ) );
681
+ add_action( 'wp_ajax_ta_advanced_add_affiliate_link' , array( $this , 'ajax_display_advanced_add_affiliate_link' ) );
682
+ add_action( 'wp_ajax_ta_get_image_markup_by_id' , array( $this , 'ajax_get_image_markup_by_id' ) );
683
+
684
+ // Quick Add Affiliate Link hooks
685
+ add_action( 'wp_ajax_ta_quick_add_affiliate_link_thickbox' , array( $this , 'ajax_display_quick_add_affiliate_link_thickbox' ) );
686
+ add_action( 'wp_ajax_ta_process_quick_add_affiliate_link' , array( $this , 'ajax_process_quick_add_affiliate_link' ) );
687
+
688
+ // edit shortcode
689
+ add_action( 'wp_ajax_ta_edit_affiliate_link_shortcode' , array( $this , 'ajax_display_shortcode_editor_form' ) );
690
+
691
+ }
692
+
693
+ /**
694
+ * Execute link picker.
695
+ *
696
+ * @since 3.0.0
697
+ * @access public
698
+ */
699
+ public function run() {
700
+
701
+ // TinyMCE buttons
702
+ add_action( 'wp' , array( $this , 'init_thirsty_editor_buttons_for_page_builders' ) );
703
+
704
+ // WP editor style
705
+ add_action( 'admin_init' , array( $this , 'register_wp_editor_style' ) );
706
+ add_filter( 'mce_css' , array( $this , 'register_wp_editor_style_mce_css' ) );
707
+
708
+ add_filter( 'the_content' , array( $this , 'gutenberg_transform_ta_tags_to_link' ) );
709
+ }
710
+ }
Models/Marketing.php CHANGED
File without changes
Models/Migration.php CHANGED
File without changes
Models/REST_API.php CHANGED
@@ -1,354 +1,354 @@
1
- <?php
2
-
3
- namespace ThirstyAffiliates\Models;
4
-
5
- use ThirstyAffiliates\Abstracts\Abstract_Main_Plugin_Class;
6
-
7
- use ThirstyAffiliates\Interfaces\Initiable_Interface;
8
- use ThirstyAffiliates\Interfaces\Model_Interface;
9
-
10
- use ThirstyAffiliates\Helpers\Plugin_Constants;
11
- use ThirstyAffiliates\Helpers\Helper_Functions;
12
-
13
- /**
14
- * Model that houses the logic for permalink rewrites and affiliate link redirections.
15
- *
16
- * @since 3.1.0
17
- */
18
- class REST_API implements Model_Interface {
19
-
20
- /*
21
- |--------------------------------------------------------------------------
22
- | Class Properties
23
- |--------------------------------------------------------------------------
24
- */
25
-
26
- /**
27
- * Property that holds the single main instance of Shortcodes.
28
- *
29
- * @since 3.1.0
30
- * @access private
31
- * @var Redirection
32
- */
33
- private static $_instance;
34
-
35
- /**
36
- * Model that houses the main plugin object.
37
- *
38
- * @since 3.1.0
39
- * @access private
40
- * @var Redirection
41
- */
42
- private $_main_plugin;
43
-
44
- /**
45
- * Model that houses all the plugin constants.
46
- *
47
- * @since 3.1.0
48
- * @access private
49
- * @var Plugin_Constants
50
- */
51
- private $_constants;
52
-
53
- /**
54
- * Property that houses all the helper functions of the plugin.
55
- *
56
- * @since 3.1.0
57
- * @access private
58
- * @var Helper_Functions
59
- */
60
- private $_helper_functions;
61
-
62
- /**
63
- * Rest field prefix.
64
- *
65
- * @since 3.1.0
66
- * @access private
67
- * @var string
68
- */
69
- private $_rest_prefix = '_ta_';
70
-
71
- /**
72
- * TA custom fields default data.
73
- *
74
- * @since 3.1.0
75
- * @access private
76
- * @var array
77
- */
78
- private $_ta_fields = array(
79
- 'destination_url' => '',
80
- 'rel_tags' => '',
81
- 'redirect_type' => 'global',
82
- 'no_follow' => 'global',
83
- 'new_window' => 'global',
84
- 'uncloak_link' => 'global',
85
- 'pass_query_str' => 'global',
86
- 'image_ids' => array(),
87
- 'categories' => array(),
88
- 'category_slug' => '',
89
- 'category_slug_id' => 0,
90
- );
91
-
92
-
93
-
94
-
95
- /*
96
- |--------------------------------------------------------------------------
97
- | Class Methods
98
- |--------------------------------------------------------------------------
99
- */
100
-
101
- /**
102
- * Class constructor.
103
- *
104
- * @since 3.1.0
105
- * @access public
106
- *
107
- * @param Abstract_Main_Plugin_Class $main_plugin Main plugin object.
108
- * @param Plugin_Constants $constants Plugin constants object.
109
- * @param Helper_Functions $helper_functions Helper functions object.
110
- */
111
- public function __construct( Abstract_Main_Plugin_Class $main_plugin , Plugin_Constants $constants , Helper_Functions $helper_functions ) {
112
-
113
- $this->_constants = $constants;
114
- $this->_helper_functions = $helper_functions;
115
-
116
- $main_plugin->add_to_all_plugin_models( $this );
117
- }
118
-
119
- /**
120
- * Ensure that only one instance of this class is loaded or can be loaded ( Singleton Pattern ).
121
- *
122
- * @since 3.1.0
123
- * @access public
124
- *
125
- * @param Abstract_Main_Plugin_Class $main_plugin Main plugin object.
126
- * @param Plugin_Constants $constants Plugin constants object.
127
- * @param Helper_Functions $helper_functions Helper functions object.
128
- * @return Redirection
129
- */
130
- public static function get_instance( Abstract_Main_Plugin_Class $main_plugin , Plugin_Constants $constants , Helper_Functions $helper_functions ) {
131
-
132
- if ( !self::$_instance instanceof self )
133
- self::$_instance = new self( $main_plugin , $constants , $helper_functions );
134
-
135
- return self::$_instance;
136
-
137
- }
138
-
139
- /**
140
- * Register ThirstyAffiliates custom fields so they can be accessible on REST.
141
- *
142
- * @since 3.1.0
143
- * @access public
144
- */
145
- public function register_ta_custom_fields_on_rest() {
146
-
147
- $fields = apply_filters( 'ta_register_rest_api_fields' , $this->_ta_fields );
148
-
149
- foreach ( $fields as $meta_key => $default_value )
150
- $this->register_rest_field( $meta_key , $default_value );
151
-
152
-
153
- }
154
-
155
- /**
156
- * Register single field in the REST API.
157
- *
158
- * @since 3.1.0
159
- * @access private
160
- *
161
- * @param string $meta_key Custom field meta key.
162
- * @param mixed $default_value Custom field default value.
163
- */
164
- private function register_rest_field( $meta_key , $default_value ) {
165
-
166
- $field_type = gettype( $default_value );
167
-
168
- register_rest_field( Plugin_Constants::AFFILIATE_LINKS_CPT , $this->_rest_prefix . $meta_key , array(
169
-
170
- // REST field get callback.
171
- 'get_callback' => function( $post_data ) use ( $meta_key , $field_type , $default_value ) {
172
-
173
- if ( $meta_key == 'categories' )
174
- return wp_get_post_terms( $post_data[ 'id' ] , Plugin_Constants::AFFILIATE_LINKS_TAX , array( 'fields' => 'ids' ) );
175
- else {
176
-
177
- $meta_value = get_post_meta( $post_data[ 'id' ] , Plugin_Constants::META_DATA_PREFIX . $meta_key , true );
178
- $meta_value = $this->esc_meta_value( $meta_value , $meta_key , $field_type );
179
- return ( $meta_value && $field_type === gettype( $meta_value ) ) ? $meta_value : $default_value;
180
- }
181
- },
182
-
183
- // REST field update callback.
184
- 'update_callback' => function( $new_value , $post_obj ) use ( $meta_key , $field_type , $default_value ) {
185
-
186
- // Filter to determine if field allows to be updated or not.
187
- if ( apply_filters( 'ta_restapi_field_update_cb' , false , $meta_key , $new_value , $field_type , $default_value ) )
188
- return;
189
-
190
- if ( $meta_key == 'categories' ) {
191
-
192
- if ( ! is_array( $new_value ) || empty( $new_value ) )
193
- return;
194
-
195
- $categories = array_unique( array_map( 'intval' , $new_value ) );
196
- wp_set_post_terms( $post_obj->ID , $categories , Plugin_Constants::AFFILIATE_LINKS_TAX );
197
-
198
- } else
199
- update_post_meta( $post_obj->ID , Plugin_Constants::META_DATA_PREFIX . $meta_key , $this->sanitize_field( $new_value , $meta_key , $field_type , $default_value ) );
200
- },
201
-
202
- // REST field schema.
203
- 'schema' => array(
204
- 'type' => $field_type,
205
- 'context' => array( 'view' , 'edit' )
206
- )
207
-
208
- ) );
209
- }
210
-
211
- /**
212
- * Escape meta value before displaying.
213
- *
214
- * @since 3.1.0
215
- * @access private
216
- *
217
- * @param mixed $meta_value Custom field value.
218
- * @param string $meta_key Custom field meta key.
219
- * @param string $field_type Custom field type.
220
- * @return mixed Escaped custom field value.
221
- */
222
- private function esc_meta_value( $meta_value , $meta_key , $field_type ) {
223
-
224
- // allow TA pro and other third party plugins to escape value while not running the default function.
225
- $escaped_value = apply_filters( 'ta_rest_api_esc_meta_value' , false , $meta_value , $meta_key , $field_type );
226
- if ( $escaped_value !== false ) return $escaped_value;
227
-
228
- switch ( $field_type ) {
229
-
230
- case 'array' :
231
- $sanitize_func = ( $meta_key == 'image_ids' ) ? 'intval' : 'esc_attr';
232
- $escaped_value = is_array( $meta_value ) ? array_map( $sanitize_func , $meta_value ) : $meta_value;
233
- break;
234
-
235
- case 'integer' :
236
- $escaped_value = intval( $meta_value );
237
- break;
238
-
239
- case 'string' :
240
- default :
241
- $escaped_value = ( $meta_key == 'destination_url' ) ? esc_url_raw( $meta_value ) : esc_attr( $meta_value );
242
- break;
243
- }
244
-
245
- return $escaped_value;
246
- }
247
-
248
- /**
249
- * Sanitize updated meta value before saving.
250
- *
251
- * @since 3.1.0
252
- * @access private
253
- *
254
- * @param mixed $field_value Custom field value.
255
- * @param string $meta_key Custom field meta key.
256
- * @param string $field_type Custom field type.
257
- * @param mixed $default_value Custom field default value.
258
- * @return mixed Sanitized custom field value.
259
- */
260
- private function sanitize_field( $field_value , $meta_key , $field_type , $default_value ) {
261
-
262
- // allow TA pro and other third party plugins to sanitize value while not running the default function.
263
- $sanitized_value = apply_filters( 'ta_rest_api_sanitize_field' , false , $field_value , $meta_key , $field_type , $default_value );
264
- if ( $sanitized_value !== false ) return $sanitized_value;
265
-
266
- switch ( $field_type ) {
267
-
268
- case 'array' :
269
- $sanitize_func = ( $meta_key == 'image_ids' ) ? 'intval' : 'sanitize_text_field';
270
- $sanitized_value = ( is_array( $field_value ) && ! empty( $field_value ) ) ? array_unique( array_map( $sanitize_func , $field_value ) ) : $default_value;
271
-
272
- // validate if provided ids are already saved attachments for image_ids field.
273
- if ( $meta_key == 'image_ids' ) {
274
-
275
- $valid_images = array();
276
- foreach ( $sanitized_value as $image_id )
277
- if ( get_post_type( $image_id ) === 'attachment' ) $valid_images[] = $image_id;
278
-
279
- $sanitized_value = $valid_images;
280
- }
281
-
282
- break;
283
-
284
- case 'integer' :
285
- $sanitized_value = ( $field_value ) ? intval( $field_value ) : $default_value;
286
- break;
287
-
288
- case 'string' :
289
- default :
290
- $sanitized_value = ( $field_value ) ? $field_value : $default_value;
291
- $sanitized_value = ( $meta_key == 'destination_url' ) ? esc_url_raw( $field_value ) : sanitize_text_field( $field_value );
292
- break;
293
- }
294
-
295
- return $sanitized_value;
296
- }
297
-
298
- /**
299
- * Sanitize special TA fields.
300
- *
301
- * @since 3.1.0
302
- * @since 3.4.0 Allow global value for redirect type.
303
- * @access public
304
- *
305
- * @param mixed $sanitized_value Value after sanitized. Defaults to boolean false.
306
- * @param mixed $field_value Raw field value.
307
- * @param string $meta_key TA field meta key.
308
- * @param string $field_type TA field variable type.
309
- * @param mixed $default_value TA field default value.
310
- * @return mixed Filtered sanitized field value.
311
- */
312
- public function sanitize_special_fields( $sanitized_value , $field_value , $meta_key , $field_type , $default_value ) {
313
-
314
- $toggle_fields = apply_filters( 'ta_rest_api_sanitize_toggle_fields' , array(
315
- 'no_follow',
316
- 'new_window',
317
- 'uncloak_link',
318
- 'pass_query_str',
319
- ) );
320
-
321
- if ( in_array( $meta_key , $toggle_fields ) )
322
- $allowed_values = array( 'global' , 'yes' , 'no' );
323
- elseif( $meta_key == 'redirect_type' )
324
- $allowed_values = array( 'global' , '301' , '302' , '307' );
325
-
326
- if ( isset( $allowed_values ) && is_array( $allowed_values ) )
327
- $sanitized_value = in_array( $field_value , $allowed_values ) ? sanitize_text_field( $field_value ) : $default_value;
328
-
329
- return $sanitized_value;
330
- }
331
-
332
-
333
-
334
-
335
- /*
336
- |--------------------------------------------------------------------------
337
- | Implemented Interface Methods
338
- |--------------------------------------------------------------------------
339
- */
340
-
341
- /**
342
- * Execute model.
343
- *
344
- * @implements ThirstyAffiliates\Interfaces\Model_Interface
345
- *
346
- * @since 3.1.0
347
- * @access public
348
- */
349
- public function run() {
350
-
351
- add_action( 'rest_api_init' , array( $this , 'register_ta_custom_fields_on_rest' ) , 10 );
352
- add_filter( 'ta_rest_api_sanitize_field' , array( $this , 'sanitize_special_fields' ) , 10 , 5 );
353
- }
354
- }
1
+ <?php
2
+
3
+ namespace ThirstyAffiliates\Models;
4
+
5
+ use ThirstyAffiliates\Abstracts\Abstract_Main_Plugin_Class;
6
+
7
+ use ThirstyAffiliates\Interfaces\Initiable_Interface;
8
+ use ThirstyAffiliates\Interfaces\Model_Interface;
9
+
10
+ use ThirstyAffiliates\Helpers\Plugin_Constants;
11
+ use ThirstyAffiliates\Helpers\Helper_Functions;
12
+
13
+ /**
14
+ * Model that houses the logic for permalink rewrites and affiliate link redirections.
15
+ *
16
+ * @since 3.1.0
17
+ */
18
+ class REST_API implements Model_Interface {
19
+
20
+ /*
21
+ |--------------------------------------------------------------------------
22
+ | Class Properties
23
+ |--------------------------------------------------------------------------
24
+ */
25
+
26
+ /**
27
+ * Property that holds the single main instance of Shortcodes.
28
+ *
29
+ * @since 3.1.0
30
+ * @access private
31
+ * @var Redirection
32
+ */
33
+ private static $_instance;
34
+
35
+ /**
36
+ * Model that houses the main plugin object.
37
+ *
38
+ * @since 3.1.0
39
+ * @access private
40
+ * @var Redirection
41
+ */
42
+ private $_main_plugin;
43
+
44
+ /**
45
+ * Model that houses all the plugin constants.
46
+ *
47
+ * @since 3.1.0
48
+ * @access private
49
+ * @var Plugin_Constants
50
+ */
51
+ private $_constants;
52
+
53
+ /**
54
+ * Property that houses all the helper functions of the plugin.
55
+ *
56
+ * @since 3.1.0
57
+ * @access private
58
+ * @var Helper_Functions
59
+ */
60
+ private $_helper_functions;
61
+
62
+ /**
63
+ * Rest field prefix.
64
+ *
65
+ * @since 3.1.0
66
+ * @access private
67
+ * @var string
68
+ */
69
+ private $_rest_prefix = '_ta_';
70
+
71
+ /**
72
+ * TA custom fields default data.
73
+ *
74
+ * @since 3.1.0
75
+ * @access private
76
+ * @var array
77
+ */
78
+ private $_ta_fields = array(
79
+ 'destination_url' => '',
80
+ 'rel_tags' => '',
81
+ 'redirect_type' => 'global',
82
+ 'no_follow' => 'global',
83
+ 'new_window' => 'global',
84
+ 'uncloak_link' => 'global',
85
+ 'pass_query_str' => 'global',
86
+ 'image_ids' => array(),
87
+ 'categories' => array(),
88
+ 'category_slug' => '',
89
+ 'category_slug_id' => 0,
90
+ );
91
+
92
+
93
+
94
+
95
+ /*
96
+ |--------------------------------------------------------------------------
97
+ | Class Methods
98
+ |--------------------------------------------------------------------------
99
+ */
100
+
101
+ /**
102
+ * Class constructor.
103
+ *
104
+ * @since 3.1.0
105
+ * @access public
106
+ *
107
+ * @param Abstract_Main_Plugin_Class $main_plugin Main plugin object.
108
+ * @param Plugin_Constants $constants Plugin constants object.
109
+ * @param Helper_Functions $helper_functions Helper functions object.
110
+ */
111
+ public function __construct( Abstract_Main_Plugin_Class $main_plugin , Plugin_Constants $constants , Helper_Functions $helper_functions ) {
112
+
113
+ $this->_constants = $constants;
114
+ $this->_helper_functions = $helper_functions;
115
+
116
+ $main_plugin->add_to_all_plugin_models( $this );
117
+ }
118
+
119
+ /**
120
+ * Ensure that only one instance of this class is loaded or can be loaded ( Singleton Pattern ).
121
+ *
122
+ * @since 3.1.0
123
+ * @access public
124
+ *
125
+ * @param Abstract_Main_Plugin_Class $main_plugin Main plugin object.
126
+ * @param Plugin_Constants $constants Plugin constants object.
127
+ * @param Helper_Functions $helper_functions Helper functions object.
128
+ * @return Redirection
129
+ */
130
+ public static function get_instance( Abstract_Main_Plugin_Class $main_plugin , Plugin_Constants $constants , Helper_Functions $helper_functions ) {
131
+
132
+ if ( !self::$_instance instanceof self )
133
+ self::$_instance = new self( $main_plugin , $constants , $helper_functions );
134
+
135
+ return self::$_instance;
136
+
137
+ }
138
+
139
+ /**
140
+ * Register ThirstyAffiliates custom fields so they can be accessible on REST.
141
+ *
142
+ * @since 3.1.0
143
+ * @access public
144
+ */
145
+ public function register_ta_custom_fields_on_rest() {
146
+
147
+ $fields = apply_filters( 'ta_register_rest_api_fields' , $this->_ta_fields );
148
+
149
+ foreach ( $fields as $meta_key => $default_value )
150
+ $this->register_rest_field( $meta_key , $default_value );
151
+
152
+
153
+ }
154
+
155
+ /**
156
+ * Register single field in the REST API.
157
+ *
158
+ * @since 3.1.0
159
+ * @access private
160
+ *
161
+ * @param string $meta_key Custom field meta key.
162
+ * @param mixed $default_value Custom field default value.
163
+ */
164
+ private function register_rest_field( $meta_key , $default_value ) {
165
+
166
+ $field_type = gettype( $default_value );
167
+
168
+ register_rest_field( Plugin_Constants::AFFILIATE_LINKS_CPT , $this->_rest_prefix . $meta_key , array(
169
+
170
+ // REST field get callback.
171
+ 'get_callback' => function( $post_data ) use ( $meta_key , $field_type , $default_value ) {
172
+
173
+ if ( $meta_key == 'categories' )
174
+ return wp_get_post_terms( $post_data[ 'id' ] , Plugin_Constants::AFFILIATE_LINKS_TAX , array( 'fields' => 'ids' ) );
175
+ else {
176
+
177
+ $meta_value = get_post_meta( $post_data[ 'id' ] , Plugin_Constants::META_DATA_PREFIX . $meta_key , true );
178
+ $meta_value = $this->esc_meta_value( $meta_value , $meta_key , $field_type );
179
+ return ( $meta_value && $field_type === gettype( $meta_value ) ) ? $meta_value : $default_value;
180
+ }
181
+ },
182
+
183
+ // REST field update callback.
184
+ 'update_callback' => function( $new_value , $post_obj ) use ( $meta_key , $field_type , $default_value ) {
185
+
186
+ // Filter to determine if field allows to be updated or not.
187
+ if ( apply_filters( 'ta_restapi_field_update_cb' , false , $meta_key , $new_value , $field_type , $default_value ) )
188
+ return;
189
+
190
+ if ( $meta_key == 'categories' ) {
191
+
192
+ if ( ! is_array( $new_value ) || empty( $new_value ) )
193
+ return;
194
+
195
+ $categories = array_unique( array_map( 'intval' , $new_value ) );
196
+ wp_set_post_terms( $post_obj->ID , $categories , Plugin_Constants::AFFILIATE_LINKS_TAX );
197
+
198
+ } else
199
+ update_post_meta( $post_obj->ID , Plugin_Constants::META_DATA_PREFIX . $meta_key , $this->sanitize_field( $new_value , $meta_key , $field_type , $default_value ) );
200
+ },
201
+
202
+ // REST field schema.
203
+ 'schema' => array(
204
+ 'type' => $field_type,
205
+ 'context' => array( 'view' , 'edit' )
206
+ )
207
+
208
+ ) );
209
+ }
210
+
211
+ /**
212
+ * Escape meta value before displaying.
213
+ *
214
+ * @since 3.1.0
215
+ * @access private
216
+ *
217
+ * @param mixed $meta_value Custom field value.
218
+ * @param string $meta_key Custom field meta key.
219
+ * @param string $field_type Custom field type.
220
+ * @return mixed Escaped custom field value.
221
+ */
222
+ private function esc_meta_value( $meta_value , $meta_key , $field_type ) {
223
+
224
+ // allow TA pro and other third party plugins to escape value while not running the default function.
225
+ $escaped_value = apply_filters( 'ta_rest_api_esc_meta_value' , false , $meta_value , $meta_key , $field_type );
226
+ if ( $escaped_value !== false ) return $escaped_value;
227
+
228
+ switch ( $field_type ) {
229
+
230
+ case 'array' :
231
+ $sanitize_func = ( $meta_key == 'image_ids' ) ? 'intval' : 'esc_attr';
232
+ $escaped_value = is_array( $meta_value ) ? array_map( $sanitize_func , $meta_value ) : $meta_value;
233
+ break;
234
+
235
+ case 'integer' :
236
+ $escaped_value = intval( $meta_value );
237
+ break;
238
+
239
+ case 'string' :
240
+ default :
241
+ $escaped_value = ( $meta_key == 'destination_url' ) ? esc_url_raw( $meta_value ) : esc_attr( $meta_value );
242
+ break;
243
+ }
244
+
245
+ return $escaped_value;
246
+ }
247
+
248
+ /**
249
+ * Sanitize updated meta value before saving.
250
+ *
251
+ * @since 3.1.0
252
+ * @access private
253
+ *
254
+ * @param mixed $field_value Custom field value.
255
+ * @param string $meta_key Custom field meta key.
256
+ * @param string $field_type Custom field type.
257
+ * @param mixed $default_value Custom field default value.
258
+ * @return mixed Sanitized custom field value.
259
+ */
260
+ private function sanitize_field( $field_value , $meta_key , $field_type , $default_value ) {
261
+
262
+ // allow TA pro and other third party plugins to sanitize value while not running the default function.
263
+ $sanitized_value = apply_filters( 'ta_rest_api_sanitize_field' , false , $field_value , $meta_key , $field_type , $default_value );
264
+ if ( $sanitized_value !== false ) return $sanitized_value;
265
+
266
+ switch ( $field_type ) {
267
+
268
+ case 'array' :
269
+ $sanitize_func = ( $meta_key == 'image_ids' ) ? 'intval' : 'sanitize_text_field';
270
+ $sanitized_value = ( is_array( $field_value ) && ! empty( $field_value ) ) ? array_unique( array_map( $sanitize_func , $field_value ) ) : $default_value;
271
+
272
+ // validate if provided ids are already saved attachments for image_ids field.
273
+ if ( $meta_key == 'image_ids' ) {
274
+
275
+ $valid_images = array();
276
+ foreach ( $sanitized_value as $image_id )
277
+ if ( get_post_type( $image_id ) === 'attachment' ) $valid_images[] = $image_id;
278
+
279
+ $sanitized_value = $valid_images;
280
+ }
281
+
282
+ break;
283
+
284
+ case 'integer' :
285
+ $sanitized_value = ( $field_value ) ? intval( $field_value ) : $default_value;
286
+ break;
287
+
288
+ case 'string' :
289
+ default :
290
+ $sanitized_value = ( $field_value ) ? $field_value : $default_value;
291
+ $sanitized_value = ( $meta_key == 'destination_url' ) ? esc_url_raw( $field_value ) : sanitize_text_field( $field_value );
292
+ break;
293
+ }
294
+
295
+ return $sanitized_value;
296
+ }
297
+
298
+ /**
299
+ * Sanitize special TA fields.
300
+ *
301
+ * @since 3.1.0
302
+ * @since 3.4.0 Allow global value for redirect type.
303
+ * @access public
304
+ *
305
+ * @param mixed $sanitized_value Value after sanitized. Defaults to boolean false.
306
+ * @param mixed $field_value Raw field value.
307
+ * @param string $meta_key TA field meta key.
308
+ * @param string $field_type TA field variable type.
309
+ * @param mixed $default_value TA field default value.
310
+ * @return mixed Filtered sanitized field value.
311
+ */
312
+ public function sanitize_special_fields( $sanitized_value , $field_value , $meta_key , $field_type , $default_value ) {
313
+
314
+ $toggle_fields = apply_filters( 'ta_rest_api_sanitize_toggle_fields' , array(
315
+ 'no_follow',
316
+ 'new_window',
317
+ 'uncloak_link',
318
+ 'pass_query_str',
319
+ ) );
320
+
321
+ if ( in_array( $meta_key , $toggle_fields ) )
322
+ $allowed_values = array( 'global' , 'yes' , 'no' );
323
+ elseif( $meta_key == 'redirect_type' )
324
+ $allowed_values = array( 'global' , '301' , '302' , '307' );
325
+
326
+ if ( isset( $allowed_values ) && is_array( $allowed_values ) )
327
+ $sanitized_value = in_array( $field_value , $allowed_values ) ? sanitize_text_field( $field_value ) : $default_value;
328
+
329
+ return $sanitized_value;
330
+ }
331
+
332
+
333
+
334
+
335
+ /*
336
+ |--------------------------------------------------------------------------
337
+ | Implemented Interface Methods
338
+ |--------------------------------------------------------------------------
339
+ */
340
+
341
+ /**
342
+ * Execute model.
343
+ *
344
+ * @implements ThirstyAffiliates\Interfaces\Model_Interface
345
+ *
346
+ * @since 3.1.0
347
+ * @access public
348
+ */
349
+ public function run() {
350
+
351
+ add_action( 'rest_api_init' , array( $this , 'register_ta_custom_fields_on_rest' ) , 10 );
352
+ add_filter( 'ta_rest_api_sanitize_field' , array( $this , 'sanitize_special_fields' ) , 10 , 5 );
353
+ }
354
+ }
Models/Rewrites_Redirection.php CHANGED
@@ -1,440 +1,440 @@
1
- <?php
2
-
3
- namespace ThirstyAffiliates\Models;
4
-
5
- use ThirstyAffiliates\Abstracts\Abstract_Main_Plugin_Class;
6
-
7
- use ThirstyAffiliates\Interfaces\Model_Interface;
8
- use ThirstyAffiliates\Interfaces\Deactivatable_Interface;
9
-
10
- use ThirstyAffiliates\Helpers\Plugin_Constants;
11
- use ThirstyAffiliates\Helpers\Helper_Functions;
12
-
13
- /**
14
- * Model that houses the logic for permalink rewrites and affiliate link redirections.
15
- *
16
- * @since 3.0.0
17
- */
18
- class Rewrites_Redirection implements Model_Interface , Deactivatable_Interface {
19
-
20
- /*
21
- |--------------------------------------------------------------------------
22
- | Class Properties
23
- |--------------------------------------------------------------------------
24
- */
25
-
26
- /**
27
- * Property that holds the single main instance of Rewrites_Redirection.
28
- *
29
- * @since 3.0.0
30
- * @access private
31
- * @var Redirection
32
- */
33
- private static $_instance;
34
-
35
- /**
36
- * Model that houses the main plugin object.
37
- *
38
- * @since 3.0.0
39
- * @access private
40
- * @var Redirection
41
- */
42
- private $_main_plugin;
43
-
44
- /**
45
- * Model that houses all the plugin constants.
46
- *
47
- * @since 3.0.0
48
- * @access private
49
- * @var Plugin_Constants
50
- */
51
- private $_constants;
52
-
53
- /**
54
- * Property that houses all the helper functions of the plugin.
55
- *
56
- * @since 3.0.0
57
- * @access private
58
- * @var Helper_Functions
59
- */
60
- private $_helper_functions;
61
-
62
- /**
63
- * Property that holds the currently loaded thirstylink post.
64
- *
65
- * @since 3.0.0
66
- * @access private
67
- */
68
- private $_thirstylink;
69
-
70
-
71
-
72
-
73
- /*
74
- |--------------------------------------------------------------------------
75
- | Class Methods
76
- |--------------------------------------------------------------------------
77
- */
78
-
79
- /**
80
- * Class constructor.
81
- *
82
- * @since 3.0.0
83
- * @access public
84
- *
85
- * @param Abstract_Main_Plugin_Class $main_plugin Main plugin object.
86
- * @param Plugin_Constants $constants Plugin constants object.
87
- * @param Helper_Functions $helper_functions Helper functions object.
88
- */
89
- public function __construct( Abstract_Main_Plugin_Class $main_plugin , Plugin_Constants $constants , Helper_Functions $helper_functions ) {
90
-
91
- $this->_constants = $constants;
92
- $this->_helper_functions = $helper_functions;
93
-
94
- $main_plugin->add_to_all_plugin_models( $this );
95
- $main_plugin->add_to_public_models( $this );
96
-
97
- }
98
-
99
- /**
100
- * Ensure that only one instance of this class is loaded or can be loaded ( Singleton Pattern ).
101
- *
102
- * @since 3.0.0
103
- * @access public
104
- *
105
- * @param Abstract_Main_Plugin_Class $main_plugin Main plugin object.
106
- * @param Plugin_Constants $constants Plugin constants object.
107
- * @param Helper_Functions $helper_functions Helper functions object.
108
- * @return Redirection
109
- */
110
- public static function get_instance( Abstract_Main_Plugin_Class $main_plugin , Plugin_Constants $constants , Helper_Functions $helper_functions ) {
111
-
112
- if ( !self::$_instance instanceof self )
113
- self::$_instance = new self( $main_plugin , $constants , $helper_functions );
114
-
115
- return self::$_instance;
116
-
117
- }
118
-
119
-
120
-
121
-
122
- /*
123
- |--------------------------------------------------------------------------
124
- | Flush Rewrite Rules
125
- |--------------------------------------------------------------------------
126
- */
127
-
128
- /**
129
- * Get thirstylink Affiliate_Link object.
130
- *
131
- * @since 3.0.0
132
- * @access private
133
- *
134
- * @param int $post_id Thirstylink post id.
135
- * @return Affiliate_Link object.
136
- */
137
- private function get_thirstylink_post( $post_id ) {
138
-
139
- if ( is_object( $this->_thirstylink ) && $this->_thirstylink->get_id() == $post_id )
140
- return $this->_thirstylink;
141
-
142
- return $this->_thirstylink = new Affiliate_Link( $post_id );
143
-
144
- }
145
-
146
- /**
147
- * Set ta_flush_rewrite_rules transient value to true if the link prefix value has changed.
148
- *
149
- * @since 3.0.0
150
- * @access public
151
- *
152
- * @param string $new_value Option new value.
153
- * @param string $old_value Option old value.
154
- */
155
- public function set_flush_rewrite_rules_transient( $new_value , $old_value ) {
156
-
157
- if ( $new_value != $old_value )
158
- set_transient( 'ta_flush_rewrite_rules' , 'true' , 5 * 60 );
159
-
160
- return $new_value;
161
-
162
- }
163
-
164
- /**
165
- * Set rewrite tags and rules.
166
- *
167
- * @since 3.0.0
168
- * @access private
169
- *
170
- * @param string $link_prefix Thirstylink post type slug.
171
- */
172
- public function set_rewrites( $link_prefix ) {
173
-
174
- add_rewrite_tag( '%' . $link_prefix . '%' , '([^&]+)' );
175
- add_rewrite_rule( "$link_prefix/([^/]+)?/?$" , 'index.php?thirstylink=$matches[1]' , 'top' );
176
-
177
- if ( get_option( 'ta_show_cat_in_slug' ) === 'yes' ) {
178
-
179
- add_rewrite_tag( '%thirstylink-category%' , '([^&]+)');
180
- add_rewrite_rule( "$link_prefix/([^/]+)?/?([^/]+)?/?" , 'index.php?thirstylink=$matches[2]&thirstylink-category=$matches[1]' , 'top' );
181
- }
182
- }
183
-
184
- /**
185
- * Flush rewrite rules (soft) when the ta_flush_rewrite_rules transient is set to 'true'.
186
- *
187
- * @since 3.0.0
188
- * @access public
189
- */
190
- public function flush_rewrite_rules() {
191
-
192
- if ( 'true' !== get_transient( 'ta_flush_rewrite_rules' ) )
193
- return;
194
-
195
- flush_rewrite_rules( false );
196
- delete_transient( 'ta_flush_rewrite_rules' );
197
-
198
- // block bots on accessing/indexing affiliate links on htaccess
199
- $this->block_bots_to_access_affiliate_links_on_htaccess();
200
- }
201
-
202
-
203
-
204
-
205
- /*
206
- |--------------------------------------------------------------------------
207
- | Redirection Handler
208
- |--------------------------------------------------------------------------
209
- */
210
-
211
- /**
212
- * Handles redirect for thirstylink link urls.
213
- *
214
- * @since 3.0.0
215
- * @since 3.2.2 Add implementation for disabling cache for 301 redirects.
216
- * @since 3.3.2 TA-265 when attachment page is viewed link prefix, set page to 404 via $wp_query object.
217
- * @access public
218
- */
219
- public function redirect_url() {
220
-
221
- global $post , $wp_query;
222
-
223
- if ( is_admin() || ! is_object( $post ) || $post->post_type != Plugin_Constants::AFFILIATE_LINKS_CPT ) {
224
-
225
- if ( is_object( $post ) && $post->post_type === 'attachment' && $this->validate_cloaked_url() )
226
- $wp_query->set_404();
227
-
228
- return;
229
- }
230
-
231
- $thirstylink = $this->get_thirstylink_post( $post->ID );
232
- $redirect_url = html_entity_decode( $thirstylink->get_prop( 'destination_url' ) );
233
- $redirect_type = $thirstylink->get_redirect_type();
234
-
235
- // Apply any filters to the url and redirect type before redirecting
236
- $redirect_url = apply_filters( 'ta_filter_redirect_url' , $redirect_url , $thirstylink , '' );
237
- $redirect_type = apply_filters( 'ta_filter_redirect_type' , $redirect_type , $thirstylink );
238
-
239
- // if cloaked url is invalid, then don't redirect.
240
- if ( ! $this->validate_cloaked_url( $thirstylink ) ) {
241
-
242
- $wp_query->set_404();
243
- remove_action( 'template_redirect' , array( $this , 'redirect_url' ) , 1 );
244
- return;
245
- }
246
-
247
- // perform actions before redirecting
248
- do_action( 'ta_before_link_redirect' , $thirstylink , $redirect_url , $redirect_type );
249
-
250
- if ( $redirect_url && $redirect_type ) {
251
-
252
- // tell browser not to cache 301 redirects (if option is enabled)
253
- if ( $redirect_type == 301 && get_option( 'ta_browser_no_cache_301_redirect' ) == 'yes' ) {
254
- header( 'Cache-Control: no-store, no-cache, must-revalidate' );
255
- header( 'Expires: Thu, 01 Jan 1970 00:00:00 GMT' );
256
- }
257
-
258
- wp_redirect( $redirect_url , intval( $redirect_type ) );
259
- exit;
260
- }
261
-
262
- }
263
-
264
- /**
265
- * Validate the cloaked url.
266
- *
267
- * @since 3.2.2
268
- * @since 3.3.2 improved code so it will always check the link prefix and only check category when it is present and eligible.
269
- * @access private
270
- *
271
- * @return boolean True if cloaked url is valid, false otherwise.
272
- */
273
- private function validate_cloaked_url( $thirstylink = null ) {
274
-
275
- $cat_slug = is_object( $thirstylink ) ? $thirstylink->get_category_slug() : '';
276
- $link_prefix = $this->_helper_functions->get_thirstylink_link_prefix();
277
- $referrer = isset( $_SERVER[ 'REQUEST_URI' ] ) ? $_SERVER[ 'REQUEST_URI' ] : '';
278
- $needle = '/' . $link_prefix . '/';
279
-
280
- // if setting is disabled or category slug is not defined, then return as validated.
281
- if ( get_option( 'ta_show_cat_in_slug' ) == 'yes' && $cat_slug )
282
- $needle .= $cat_slug . '/';
283
-
284
- return strpos( $referrer , $needle ) !== false;
285
- }
286
-
287
- /**
288
- * Pass query strings to destination url when option is enabled on settings.
289
- *
290
- * @since 3.0.0
291
- * @since 3.4.0 Add query string as a parameter to support enhanced JS redirect.
292
- * @access public
293
- *
294
- * @param string $redirect_url Affiliate link destination url.
295
- * @param Affiliate_Link $thirstylink Affiliate link object.
296
- * @param string $query_string Query string.
297
- * @return string Redirect url with query string or not.
298
- */
299
- public function pass_query_string_to_destination_url( $redirect_url , $thirstylink , $query_string = '' ) {
300
-
301
- if ( ! $query_string && isset( $_SERVER[ 'QUERY_STRING' ] ) )
302
- $query_string = $_SERVER[ 'QUERY_STRING' ];
303
-
304
- if ( ! $query_string || ! $thirstylink->is( 'pass_query_str' ) )
305
- return $redirect_url;
306
-
307
- $connector = ( strpos( $redirect_url , '?' ) === false ) ? '?' : '&';
308
-
309
- return $redirect_url . $connector . $query_string;
310
- }
311
-
312
- /**
313
- * Add/Recreate htaccess rule to block bots access to affiliate links.
314
- *
315
- * @since 3.1.0
316
- * @since 3.3.2 Get blocked bots from setting value.
317
- * @since 3.3.7 We only add the htaccess bots blocker content when the ta_enable_bot_crawl_blocker_script setting option is set to "yes".
318
- * @access public
319
- */
320
- public function block_bots_to_access_affiliate_links_on_htaccess() {
321
-
322
- $htaccess = $this->remove_block_bots_htaccess_rules();
323
-
324
- if ( get_option( 'ta_enable_bot_crawl_blocker_script' ) == 'yes' ) {
325
-
326
- $link_prefix = $this->_helper_functions->get_thirstylink_link_prefix();
327
- $bots_list = apply_filters( 'ta_block_bots_on_htaccess' , $this->_helper_functions->get_blocked_bots() );
328
-
329
- // prepare new TA block bots htaccess content.
330
- $block_bots = "\n#BEGIN Block-Bots-ThirstyAffiliates\n";
331
- $block_bots .= "<IfModule mod_rewrite.c>\n";
332
- $block_bots .= "RewriteEngine On\n";
333
- $block_bots .= "RewriteCond %{HTTP_USER_AGENT} (" . $bots_list . ") [NC]\n";
334
- $block_bots .= "RewriteRule ^" . $link_prefix . "/ - [L,F]\n";
335
- $block_bots .= "</IfModule>\n";
336
- $block_bots .= "#END Block-Bots-ThirstyAffiliates\n\n";
337
-
338
- // prepend block bots rules in the htaccess content.
339
- $htaccess = $block_bots . $htaccess;
340
- }
341
-
342
- file_put_contents( $this->_constants->HTACCESS_FILE() , $htaccess );
343
- }
344
-
345
- /**
346
- * Remove ThirstyAffiliates block bots htaccess rules.
347
- *
348
- * @since 3.1.0
349
- * @access public
350
- *
351
- * @param boolean $put_contents Toggle to check if function needs to save htaccess file or not.
352
- * @return string Htaccess content after removing TA block bots rules.
353
- */
354
- public function remove_block_bots_htaccess_rules( $put_contents = false ) {
355
-
356
- $htaccess = file_get_contents( $this->_constants->HTACCESS_FILE() );
357
- $pattern = "/[\n]*#[\s]*BEGIN Block-Bots-ThirstyAffiliates.*?#[\s]*END Block-Bots-ThirstyAffiliates[\n][\n]/is";
358
- $htaccess = preg_replace( $pattern , "" , $htaccess );
359
-
360
- if ( $put_contents )
361
- file_put_contents( $this->_constants->HTACCESS_FILE() , $htaccess );
362
-
363
- return $htaccess;
364
- }
365
-
366
- /**
367
- * Block bots access to affiliate links for non-apache servers.
368
- *
369
- * @since 3.3.3
370
- * @since 3.3.7 We only block bots when the ta_enable_bot_crawl_blocker_script setting option is set to "yes".
371
- * @access public
372
- */
373
- public function block_bots_non_apache_server() {
374
-
375
- global $post;
376
-
377
- $is_apache = strpos( $_SERVER[ 'SERVER_SOFTWARE' ] , 'Apache' ) !== false;
378
-
379
- if ( $is_apache || ! is_object( $post ) || $post->post_type !== Plugin_Constants::AFFILIATE_LINKS_CPT || ! $this->_helper_functions->is_user_agent_bot() )
380
- return;
381
-
382
- $message = apply_filters( 'ta_blocked_bots_non_apache_message' , sprintf( __( "<h1>Forbidden</h1><p>You don't have permission to access %s on this server.</p>" , 'thirstyaffiliates' ) , $_SERVER[ 'REQUEST_URI' ] ) );
383
- header( 'HTTP/1.0 403 Forbidden' );
384
- die( $message );
385
- }
386
-
387
-
388
-
389
-
390
- /*
391
- |--------------------------------------------------------------------------
392
- | Fulfill implemented interface contracts
393
- |--------------------------------------------------------------------------
394
- */
395
-
396
- /**
397
- * Execute codes that needs to run plugin deactivation.
398
- *
399
- * @since 1.0.0
400
- * @access public
401
- * @implements ThirstyAffiliates\Interfaces\Deactivatable_Interface
402
- */
403
- public function deactivate() {
404
-
405
- $this->remove_block_bots_htaccess_rules( true );
406
- }
407
-
408
- /**
409
- * Execute ajax handler.
410
- *
411
- * @since 3.0.0
412
- * @access public
413
- * @inherit ThirstyAffiliates\Interfaces\Model_Interface
414
- */
415
- public function run() {
416
-
417
- // flush rewrite rules
418
- add_filter( 'pre_update_option_ta_link_prefix' , array( $this , 'set_flush_rewrite_rules_transient' ) , 10 , 2 );
419
- add_filter( 'pre_update_option_ta_link_prefix_custom' , array( $this , 'set_flush_rewrite_rules_transient' ) , 10 , 2 );
420
- add_filter( 'pre_update_option_ta_show_cat_in_slug' , array( $this , 'set_flush_rewrite_rules_transient' ) , 10 , 2 );
421
- add_filter( 'pre_update_option_ta_blocked_bots' , array( $this , 'set_flush_rewrite_rules_transient' ) , 10 , 2 );
422
- add_action( 'update_option_ta_enable_bot_crawl_blocker_script' , array( $this , 'set_flush_rewrite_rules_transient' ) , 10 , 2 );
423
- add_action( 'ta_after_register_thirstylink_post_type' , array( $this , 'set_rewrites' ) , 1 , 1 );
424
- add_action( 'ta_after_register_thirstylink_post_type' , array( $this , 'flush_rewrite_rules' ) );
425
-
426
- // redirection handler
427
- add_action( 'template_redirect' , array( $this , 'redirect_url' ) , 1 );
428
-
429
- // filter redirect url before redirecting
430
- add_filter( 'ta_filter_redirect_url' , array( $this , 'pass_query_string_to_destination_url' ) , 10 , 3 );
431
-
432
- // block bots on redirect (for non-apache servers).
433
- if ( get_option( 'ta_enable_bot_crawl_blocker_script' ) == 'yes' )
434
- add_action( 'wp' , array( $this , 'block_bots_non_apache_server' ) );
435
-
436
- // update the ta_enable_bot_crawl_blocker_script setting option if it has no value yet on database.
437
- if ( get_option( 'ta_enable_bot_crawl_blocker_script' , 'not_on_db' ) === 'not_on_db' )
438
- update_option( 'ta_enable_bot_crawl_blocker_script' , '' );
439
- }
440
- }
1
+ <?php
2
+
3
+ namespace ThirstyAffiliates\Models;
4
+
5
+ use ThirstyAffiliates\Abstracts\Abstract_Main_Plugin_Class;
6
+
7
+ use ThirstyAffiliates\Interfaces\Model_Interface;
8
+ use ThirstyAffiliates\Interfaces\Deactivatable_Interface;
9
+
10
+ use ThirstyAffiliates\Helpers\Plugin_Constants;
11
+ use ThirstyAffiliates\Helpers\Helper_Functions;
12
+
13
+ /**
14
+ * Model that houses the logic for permalink rewrites and affiliate link redirections.
15
+ *
16
+ * @since 3.0.0
17
+ */
18
+ class Rewrites_Redirection implements Model_Interface , Deactivatable_Interface {
19
+
20
+ /*
21
+ |--------------------------------------------------------------------------
22
+ | Class Properties
23
+ |--------------------------------------------------------------------------
24
+ */
25
+
26
+ /**
27
+ * Property that holds the single main instance of Rewrites_Redirection.
28
+ *
29
+ * @since 3.0.0
30
+ * @access private
31
+ * @var Redirection
32
+ */
33
+ private static $_instance;
34
+
35
+ /**
36
+ * Model that houses the main plugin object.
37
+ *
38
+ * @since 3.0.0
39
+ * @access private
40
+ * @var Redirection
41
+ */
42
+ private $_main_plugin;
43
+
44
+ /**
45
+ * Model that houses all the plugin constants.
46
+ *
47
+ * @since 3.0.0
48
+ * @access private
49
+ * @var Plugin_Constants
50
+ */
51
+ private $_constants;
52
+
53
+ /**
54
+ * Property that houses all the helper functions of the plugin.
55
+ *
56
+ * @since 3.0.0
57
+ * @access private
58
+ * @var Helper_Functions
59
+ */
60
+ private $_helper_functions;
61
+
62
+ /**
63
+ * Property that holds the currently loaded thirstylink post.
64
+ *
65
+ * @since 3.0.0
66
+ * @access private
67
+ */
68
+ private $_thirstylink;
69
+
70
+
71
+
72
+
73
+ /*
74
+ |--------------------------------------------------------------------------
75
+ | Class Methods
76
+ |--------------------------------------------------------------------------
77
+ */
78
+
79
+ /**
80
+ * Class constructor.
81
+ *
82
+ * @since 3.0.0
83
+ * @access public
84
+ *
85
+ * @param Abstract_Main_Plugin_Class $main_plugin Main plugin object.
86
+ * @param Plugin_Constants $constants Plugin constants object.
87
+ * @param Helper_Functions $helper_functions Helper functions object.
88
+ */
89
+ public function __construct( Abstract_Main_Plugin_Class $main_plugin , Plugin_Constants $constants , Helper_Functions $helper_functions ) {
90
+
91
+ $this->_constants = $constants;
92
+ $this->_helper_functions = $helper_functions;
93
+
94
+ $main_plugin->add_to_all_plugin_models( $this );
95
+ $main_plugin->add_to_public_models( $this );
96
+
97
+ }
98
+
99
+ /**
100
+ * Ensure that only one instance of this class is loaded or can be loaded ( Singleton Pattern ).
101
+ *
102
+ * @since 3.0.0
103
+ * @access public
104
+ *
105
+ * @param Abstract_Main_Plugin_Class $main_plugin Main plugin object.
106
+ * @param Plugin_Constants $constants Plugin constants object.
107
+ * @param Helper_Functions $helper_functions Helper functions object.
108
+ * @return Redirection
109
+ */
110
+ public static function get_instance( Abstract_Main_Plugin_Class $main_plugin , Plugin_Constants $constants , Helper_Functions $helper_functions ) {
111
+
112
+ if ( !self::$_instance instanceof self )
113
+ self::$_instance = new self( $main_plugin , $constants , $helper_functions );
114
+
115
+ return self::$_instance;
116
+
117
+ }
118
+
119
+
120
+
121
+
122
+ /*
123
+ |--------------------------------------------------------------------------
124
+ | Flush Rewrite Rules
125
+ |--------------------------------------------------------------------------
126
+ */
127
+
128
+ /**
129
+ * Get thirstylink Affiliate_Link object.
130
+ *
131
+ * @since 3.0.0
132
+ * @access private
133
+ *
134
+ * @param int $post_id Thirstylink post id.
135
+ * @return Affiliate_Link object.
136
+ */
137
+ private function get_thirstylink_post( $post_id ) {
138
+
139
+ if ( is_object( $this->_thirstylink ) && $this->_thirstylink->get_id() == $post_id )
140
+ return $this->_thirstylink;
141
+
142
+ return $this->_thirstylink = new Affiliate_Link( $post_id );
143
+
144
+ }
145
+
146
+ /**
147
+ * Set ta_flush_rewrite_rules transient value to true if the link prefix value has changed.
148
+ *
149
+ * @since 3.0.0
150
+ * @access public
151
+ *
152
+ * @param string $new_value Option new value.
153
+ * @param string $old_value Option old value.
154
+ */
155
+ public function set_flush_rewrite_rules_transient( $new_value , $old_value ) {
156
+
157
+ if ( $new_value != $old_value )
158
+ set_transient( 'ta_flush_rewrite_rules' , 'true' , 5 * 60 );
159
+
160
+ return $new_value;
161
+
162
+ }
163
+
164
+ /**
165
+ * Set rewrite tags and rules.
166
+ *
167
+ * @since 3.0.0
168
+ * @access private
169
+ *
170
+ * @param string $link_prefix Thirstylink post type slug.
171
+ */
172
+ public function set_rewrites( $link_prefix ) {
173
+
174
+ add_rewrite_tag( '%' . $link_prefix . '%' , '([^&]+)' );
175
+ add_rewrite_rule( "$link_prefix/([^/]+)?/?$" , 'index.php?thirstylink=$matches[1]' , 'top' );
176
+
177
+ if ( get_option( 'ta_show_cat_in_slug' ) === 'yes' ) {
178
+
179
+ add_rewrite_tag( '%thirstylink-category%' , '([^&]+)');
180
+ add_rewrite_rule( "$link_prefix/([^/]+)?/?([^/]+)?/?" , 'index.php?thirstylink=$matches[2]&thirstylink-category=$matches[1]' , 'top' );
181
+ }
182
+ }
183
+
184
+ /**
185
+ * Flush rewrite rules (soft) when the ta_flush_rewrite_rules transient is set to 'true'.
186
+ *
187
+ * @since 3.0.0
188
+ * @access public
189
+ */
190
+ public function flush_rewrite_rules() {
191
+
192
+ if ( 'true' !== get_transient( 'ta_flush_rewrite_rules' ) )
193
+ return;
194
+
195
+ flush_rewrite_rules( false );
196
+ delete_transient( 'ta_flush_rewrite_rules' );
197
+
198
+ // block bots on accessing/indexing affiliate links on htaccess
199
+ $this->block_bots_to_access_affiliate_links_on_htaccess();
200
+ }
201
+
202
+
203
+
204
+
205
+ /*
206
+ |--------------------------------------------------------------------------
207
+ | Redirection Handler
208
+ |--------------------------------------------------------------------------
209
+ */
210
+
211
+ /**
212
+ * Handles redirect for thirstylink link urls.
213
+ *
214
+ * @since 3.0.0
215
+ * @since 3.2.2 Add implementation for disabling cache for 301 redirects.
216
+ * @since 3.3.2 TA-265 when attachment page is viewed link prefix, set page to 404 via $wp_query object.
217
+ * @access public
218
+ */
219
+ public function redirect_url() {
220
+
221
+ global $post , $wp_query;
222
+
223
+ if ( is_admin() || ! is_object( $post ) || $post->post_type != Plugin_Constants::AFFILIATE_LINKS_CPT ) {
224
+
225
+ if ( is_object( $post ) && $post->post_type === 'attachment' && $this->validate_cloaked_url() )
226
+ $wp_query->set_404();
227
+
228
+ return;
229
+ }
230
+
231
+ $thirstylink = $this->get_thirstylink_post( $post->ID );
232
+ $redirect_url = html_entity_decode( $thirstylink->get_prop( 'destination_url' ) );
233
+ $redirect_type = $thirstylink->get_redirect_type();
234
+
235
+ // Apply any filters to the url and redirect type before redirecting
236
+ $redirect_url = apply_filters( 'ta_filter_redirect_url' , $redirect_url , $thirstylink , '' );
237
+ $redirect_type = apply_filters( 'ta_filter_redirect_type' , $redirect_type , $thirstylink );
238
+
239
+ // if cloaked url is invalid, then don't redirect.
240
+ if ( ! $this->validate_cloaked_url( $thirstylink ) ) {
241
+
242
+ $wp_query->set_404();
243
+ remove_action( 'template_redirect' , array( $this , 'redirect_url' ) , 1 );
244
+ return;
245
+ }
246
+
247
+ // perform actions before redirecting
248
+ do_action( 'ta_before_link_redirect' , $thirstylink , $redirect_url , $redirect_type );
249
+
250
+ if ( $redirect_url && $redirect_type ) {
251
+
252
+ // tell browser not to cache 301 redirects (if option is enabled)
253
+ if ( $redirect_type == 301 && get_option( 'ta_browser_no_cache_301_redirect' ) == 'yes' ) {
254
+ header( 'Cache-Control: no-store, no-cache, must-revalidate' );
255
+ header( 'Expires: Thu, 01 Jan 1970 00:00:00 GMT' );
256
+ }
257
+
258
+ wp_redirect( $redirect_url , intval( $redirect_type ) );
259
+ exit;
260
+ }
261
+
262
+ }
263
+
264
+ /**
265
+ * Validate the cloaked url.
266
+ *
267
+ * @since 3.2.2
268
+ * @since 3.3.2 improved code so it will always check the link prefix and only check category when it is present and eligible.
269
+ * @access private
270
+ *
271
+ * @return boolean True if cloaked url is valid, false otherwise.
272
+ */
273
+ private function validate_cloaked_url( $thirstylink = null ) {
274
+
275
+ $cat_slug = is_object( $thirstylink ) ? $thirstylink->get_category_slug() : '';
276
+ $link_prefix = $this->_helper_functions->get_thirstylink_link_prefix();
277
+ $referrer = isset( $_SERVER[ 'REQUEST_URI' ] ) ? $_SERVER[ 'REQUEST_URI' ] : '';
278
+ $needle = '/' . $link_prefix . '/';
279
+
280
+ // if setting is disabled or category slug is not defined, then return as validated.
281
+ if ( get_option( 'ta_show_cat_in_slug' ) == 'yes' && $cat_slug )
282
+ $needle .= $cat_slug . '/';
283
+
284
+ return strpos( $referrer , $needle ) !== false;
285
+ }
286
+
287
+ /**
288
+ * Pass query strings to destination url when option is enabled on settings.
289
+ *
290
+ * @since 3.0.0
291
+ * @since 3.4.0 Add query string as a parameter to support enhanced JS redirect.
292
+ * @access public
293
+ *
294
+ * @param string $redirect_url Affiliate link destination url.
295
+ * @param Affiliate_Link $thirstylink Affiliate link object.
296
+ * @param string $query_string Query string.
297
+ * @return string Redirect url with query string or not.
298
+ */
299
+ public function pass_query_string_to_destination_url( $redirect_url , $thirstylink , $query_string = '' ) {
300
+
301
+ if ( ! $query_string && isset( $_SERVER[ 'QUERY_STRING' ] ) )
302
+ $query_string = $_SERVER[ 'QUERY_STRING' ];
303
+
304
+ if ( ! $query_string || ! $thirstylink->is( 'pass_query_str' ) )
305
+ return $redirect_url;
306
+
307
+ $connector = ( strpos( $redirect_url , '?' ) === false ) ? '?' : '&';
308
+
309
+ return $redirect_url . $connector . $query_string;
310
+ }
311
+
312
+ /**
313
+ * Add/Recreate htaccess rule to block bots access to affiliate links.
314
+ *
315
+ * @since 3.1.0
316
+ * @since 3.3.2 Get blocked bots from setting value.
317
+ * @since 3.3.7 We only add the htaccess bots blocker content when the ta_enable_bot_crawl_blocker_script setting option is set to "yes".
318
+ * @access public
319
+ */
320
+ public function block_bots_to_access_affiliate_links_on_htaccess() {
321
+
322
+ $htaccess = $this->remove_block_bots_htaccess_rules();
323
+
324
+ if ( get_option( 'ta_enable_bot_crawl_blocker_script' ) == 'yes' ) {
325
+
326
+ $link_prefix = $this->_helper_functions->get_thirstylink_link_prefix();
327
+ $bots_list = apply_filters( 'ta_block_bots_on_htaccess' , $this->_helper_functions->get_blocked_bots() );
328
+
329
+ // prepare new TA block bots htaccess content.
330
+ $block_bots = "\n#BEGIN Block-Bots-ThirstyAffiliates\n";
331
+ $block_bots .= "<IfModule mod_rewrite.c>\n";
332
+ $block_bots .= "RewriteEngine On\n";
333
+ $block_bots .= "RewriteCond %{HTTP_USER_AGENT} (" . $bots_list . ") [NC]\n";
334
+ $block_bots .= "RewriteRule ^" . $link_prefix . "/ - [L,F]\n";
335
+ $block_bots .= "</IfModule>\n";
336
+ $block_bots .= "#END Block-Bots-ThirstyAffiliates\n\n";
337
+
338
+ // prepend block bots rules in the htaccess content.
339
+ $htaccess = $block_bots . $htaccess;
340
+ }
341
+
342
+ file_put_contents( $this->_constants->HTACCESS_FILE() , $htaccess );
343
+ }
344
+
345
+ /**
346
+ * Remove ThirstyAffiliates block bots htaccess rules.
347
+ *
348
+ * @since 3.1.0
349
+ * @access public
350
+ *
351
+ * @param boolean $put_contents Toggle to check if function needs to save htaccess file or not.
352
+ * @return string Htaccess content after removing TA block bots rules.
353
+ */
354
+ public function remove_block_bots_htaccess_rules( $put_contents = false ) {
355
+
356
+ $htaccess = file_get_contents( $this->_constants->HTACCESS_FILE() );
357
+ $pattern = "/[\n]*#[\s]*BEGIN Block-Bots-ThirstyAffiliates.*?#[\s]*END Block-Bots-ThirstyAffiliates[\n][\n]/is";
358
+ $htaccess = preg_replace( $pattern , "" , $htaccess );
359
+
360
+ if ( $put_contents )
361
+ file_put_contents( $this->_constants->HTACCESS_FILE() , $htaccess );
362
+
363
+ return $htaccess;
364
+ }
365
+
366
+ /**
367
+ * Block bots access to affiliate links for non-apache servers.
368
+ *
369
+ * @since 3.3.3
370
+ * @since 3.3.7 We only block bots when the ta_enable_bot_crawl_blocker_script setting option is set to "yes".
371
+ * @access public
372
+ */
373
+ public function block_bots_non_apache_server() {
374
+
375
+ global $post;
376
+
377
+ $is_apache = strpos( $_SERVER[ 'SERVER_SOFTWARE' ] , 'Apache' ) !== false;
378
+
379
+ if ( $is_apache || ! is_object( $post ) || $post->post_type !== Plugin_Constants::AFFILIATE_LINKS_CPT || ! $this->_helper_functions->is_user_agent_bot() )
380
+ return;
381
+
382
+ $message = apply_filters( 'ta_blocked_bots_non_apache_message' , sprintf( __( "<h1>Forbidden</h1><p>You don't have permission to access %s on this server.</p>" , 'thirstyaffiliates' ) , $_SERVER[ 'REQUEST_URI' ] ) );
383
+ header( 'HTTP/1.0 403 Forbidden' );
384
+ die( $message );
385
+ }
386
+
387
+
388
+
389
+
390
+ /*
391
+ |--------------------------------------------------------------------------
392
+ | Fulfill implemented interface contracts
393
+ |--------------------------------------------------------------------------
394
+ */
395
+
396
+ /**
397
+ * Execute codes that needs to run plugin deactivation.
398
+ *
399
+ * @since 1.0.0
400
+ * @access public
401
+ * @implements ThirstyAffiliates\Interfaces\Deactivatable_Interface
402
+ */
403
+ public function deactivate() {
404
+
405
+ $this->remove_block_bots_htaccess_rules( true );
406
+ }
407
+
408
+ /**
409
+ * Execute ajax handler.
410
+ *
411
+ * @since 3.0.0
412
+ * @access public
413
+ * @inherit ThirstyAffiliates\Interfaces\Model_Interface
414
+ */
415
+ public function run() {
416
+
417
+ // flush rewrite rules
418
+ add_filter( 'pre_update_option_ta_link_prefix' , array( $this , 'set_flush_rewrite_rules_transient' ) , 10 , 2 );
419
+ add_filter( 'pre_update_option_ta_link_prefix_custom' , array( $this , 'set_flush_rewrite_rules_transient' ) , 10 , 2 );
420
+ add_filter( 'pre_update_option_ta_show_cat_in_slug' , array( $this , 'set_flush_rewrite_rules_transient' ) , 10 , 2 );
421
+ add_filter( 'pre_update_option_ta_blocked_bots' , array( $this , 'set_flush_rewrite_rules_transient' ) , 10 , 2 );
422
+ add_action( 'update_option_ta_enable_bot_crawl_blocker_script' , array( $this , 'set_flush_rewrite_rules_transient' ) , 10 , 2 );
423
+ add_action( 'ta_after_register_thirstylink_post_type' , array( $this , 'set_rewrites' ) , 1 , 1 );
424
+ add_action( 'ta_after_register_thirstylink_post_type' , array( $this , 'flush_rewrite_rules' ) );
425
+
426
+ // redirection handler
427
+ add_action( 'template_redirect' , array( $this , 'redirect_url' ) , 1 );
428
+
429
+ // filter redirect url before redirecting
430
+ add_filter( 'ta_filter_redirect_url' , array( $this , 'pass_query_string_to_destination_url' ) , 10 , 3 );
431
+
432
+ // block bots on redirect (for non-apache servers).
433
+ if ( get_option( 'ta_enable_bot_crawl_blocker_script' ) == 'yes' )
434
+ add_action( 'wp' , array( $this , 'block_bots_non_apache_server' ) );
435
+
436
+ // update the ta_enable_bot_crawl_blocker_script setting option if it has no value yet on database.
437
+ if ( get_option( 'ta_enable_bot_crawl_blocker_script' , 'not_on_db' ) === 'not_on_db' )
438
+ update_option( 'ta_enable_bot_crawl_blocker_script' , '' );
439
+ }
440
+ }
Models/Script_Loader.php CHANGED
File without changes
Models/Settings.php CHANGED
@@ -1,2114 +1,2114 @@
1
- <?php
2
- namespace ThirstyAffiliates\Models;
3
-
4
- use ThirstyAffiliates\Abstracts\Abstract_Main_Plugin_Class;
5
-
6
- use ThirstyAffiliates\Interfaces\Model_Interface;
7
- use ThirstyAffiliates\Interfaces\Activatable_Interface;
8
- use ThirstyAffiliates\Interfaces\Initiable_Interface;
9
-
10
- use ThirstyAffiliates\Helpers\Plugin_Constants;
11
- use ThirstyAffiliates\Helpers\Helper_Functions;
12
-
13
- if ( !defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
14
-
15
- /**
16
- * Model that houses the logic of plugin Settings.
17
- * General Information:
18
- * The Ultimate Settings API ( Of course there will always be room for improvements ).
19
- * Basically we are using parts of the WordPress Settings API ( Only the backend processes )
20
- * But we are using our own custom render codebase.
21
- * The issue with WordPress Settings API is that we need to supply callbacks for each option field we add, so its not really extensible.
22
- * The data supplied on those callbacks are not ideal or not complete too to make a very extensible Settings API.
23
- * So what we did is, Register the settings and settings options in a way that we can utilize WordPress Settings API to handle them on the backend.
24
- * But use our own render codebase so we can make the Settings API very easy to extend.
25
- *
26
- * Important Note:
27
- * Be careful with default values. Default values only take effect if you haven't set the option yet. Meaning the option is not yet registered yet to the options db. ( get_option ).
28
- * Now if you hit save on a settings section with a field that have a default value, and you haven't changed that default value, Alas! it will still not register that option to the options db.
29
- * The reason is the default value and the current value of the options is the same.
30
- * Bug if you modify the value of the option, and hit save, this time, that option will be registered to the options db.
31
- * Then if you set back the value of that option, same as its default, it will still updated that option that is registered to the options db with that value.
32
- * Again remember, default value only kicks in if the option you are trying to get via get_option function is not yet registered to the options db.
33
- *
34
- * Important Note:
35
- * If the option can contain multiple values ( in array form , ex. checkbox and multiselect option types ), the default value must be in array form even if it only contains one value.
36
- *
37
- * Private Model.
38
- *
39
- * @since 3.0.0
40
- */
41
- class Settings implements Model_Interface , Activatable_Interface , Initiable_Interface {
42
-
43
- /*
44
- |--------------------------------------------------------------------------
45
- | Class Properties
46
- |--------------------------------------------------------------------------
47
- */
48
-
49
- /**
50
- * Property that holds the single main instance of Settings.
51
- *
52
- * @since 3.0.0
53
- * @access private
54
- * @var Settings
55
- */
56
- private static $_instance;
57
-
58
- /**
59
- * Model that houses all the plugin constants.
60
- *
61
- * @since 3.0.0
62
- * @access private
63
- * @var Plugin_Constants
64
- */
65
- private $_constants;
66
-
67
- /**
68
- * Property that houses all the helper functions of the plugin.
69
- *
70
- * @since 3.0.0
71
- * @access private
72
- * @var Helper_Functions
73
- */
74
- private $_helper_functions;
75
-
76
- /**
77
- * Property that houses all the supported option field types.
78
- *
79
- * @since 3.0.0
80
- * @access private
81
- * @var array
82
- */
83
- private $_supported_field_types;
84
-
85
- /**
86
- * Property that houses all the supported option field types that do not needed to be registered to the WP Settings API.
87
- * Ex. of this are field types that are for decorative purposes only and has no underlying option data to save.
88
- * Another is type of option fields that perform specialized task and does not need any underlying data to be saved.
89
- *
90
- * @since 3.0.0
91
- * @access public
92
- */
93
- private $_skip_wp_settings_registration;
94
-
95
- /**
96
- * Property that houses all the registered settings sections.
97
- *
98
- *
99
- * @since 3.0.0
100
- * @access private
101
- * @var array
102
- */
103
- private $_settings_sections;
104
-
105
- /**
106
- * Property that houses all the registered options of the registered settings sections.
107
- *
108
- * @since 3.0.0
109
- * @access private
110
- * @var array
111
- */
112
- private $_settings_section_options;
113
-
114
- /**
115
- * Property that holds all plugin options that can be exported.
116
- *
117
- * @since 3.0.0
118
- * @access private
119
- * @var array
120
- */
121
- private $_exportable_options;
122
-
123
- /**
124
- * Property that holds list of post update function callbacks per option if there are any.
125
- *
126
- * @since 3.0.0
127
- * @access private
128
- * @var array
129
- */
130
- private $_post_update_option_cbs = array();
131
-
132
-
133
-
134
-
135
- /*
136
- |--------------------------------------------------------------------------
137
- | Class Methods
138
- |--------------------------------------------------------------------------
139
- */
140
-
141
- /**
142
- * Class constructor.
143
- *
144
- * @since 3.0.0
145
- * @access public
146
- *
147
- * @param Abstract_Main_Plugin_Class $main_plugin Main plugin object.
148
- * @param Plugin_Constants $constants Plugin constants object.
149
- * @param Helper_Functions $helper_functions Helper functions object.
150
- */
151
- public function __construct( Abstract_Main_Plugin_Class $main_plugin , Plugin_Constants $constants , Helper_Functions $helper_functions ) {
152
-
153
- $this->_constants = $constants;
154
- $this->_helper_functions = $helper_functions;
155
-
156
- $main_plugin->add_to_all_plugin_models( $this );
157
-
158
- }
159
-
160
- /**
161
- * Ensure that only one instance of this class is loaded or can be loaded ( Singleton Pattern ).
162
- *
163
- * @since 3.0.0
164
- * @access public
165
- *
166
- * @param Abstract_Main_Plugin_Class $main_plugin Main plugin object.
167
- * @param Plugin_Constants $constants Plugin constants object.
168
- * @param Helper_Functions $helper_functions Helper functions object.
169
- * @return Settings
170
- */
171
- public static function get_instance( Abstract_Main_Plugin_Class $main_plugin , Plugin_Constants $constants , Helper_Functions $helper_functions ) {
172
-
173
- if ( !self::$_instance instanceof self )
174
- self::$_instance = new self( $main_plugin , $constants , $helper_functions );
175
-
176
- return self::$_instance;
177
-
178
- }
179
-
180
- /**
181
- * Register admin interfaces.
182
- *
183
- * @since 3.3.2
184
- * @access public
185
- *
186
- * @param array $interfaces List of admin interfaces.
187
- * @return array Filtered list of admin interfaces.
188
- */
189
- public function register_admin_interfaces( $interfaces ) {
190
-
191
- $interfaces[ 'thirstylink_page_thirsty-settings' ] = apply_filters( 'ta_settings_admin_interface' , array(
192
- 'ta_general_settings' => 'manage_options',
193
- 'ta_links_settings' => 'manage_options',
194
- 'ta_modules_settings' => 'manage_options',
195
- 'ta_help_settings' => 'manage_options',
196
- ) );
197
-
198
- $interfaces[ 'thirstylink_page_thirsty-settings' ][ 'ta_import_export_settings' ] = 'manage_options';
199
-
200
- return $interfaces;
201
- }
202
-
203
- /**
204
- * Register admin interfaces.
205
- *
206
- * @since 3.3.2
207
- * @access public
208
- *
209
- * @param array $interfaces List of menu items.
210
- * @return array Filtered list of menu items.
211
- */
212
- public function register_admin_menu_items( $menu_items ) {
213
-
214
- $menu_items[ 'thirsty-settings' ] = 'manage_options';
215
- return $menu_items;
216
- }
217
-
218
-
219
-
220
-
221
- /*
222
- |--------------------------------------------------------------------------
223
- | Initialize Settings
224
- |--------------------------------------------------------------------------
225
- */
226
-
227
- /**
228
- * Initialize the list of plugin built-in settings sections and its corresponding options.
229
- *
230
- * @since 3.0.0
231
- * @since 3.3.1 Add support for URL input type.
232
- * @since 3.3.5 Hide enhanced javascript redirect setting when statistics module is disabled.
233
- * @access public
234
- */
235
- public function init_settings_sections_and_options() {
236
-
237
- $this->_supported_field_types = apply_filters( 'ta_supported_field_types' , array(
238
- 'text' => array( $this , 'render_text_option_field' ),
239
- 'url' => array( $this , 'render_url_option_field' ),
240
- 'number' => array( $this , 'render_number_option_field' ),
241
- 'textarea' => array( $this , 'render_textarea_option_field' ),
242
- 'checkbox' => array( $this , 'render_checkbox_option_field' ),
243
- 'radio' => array( $this , 'render_radio_option_field' ),
244
- 'select' => array( $this , 'render_select_option_field' ),
245
- 'multiselect' => array( $this , 'render_multiselect_option_field' ),
246
- 'toggle' => array( $this , 'render_toggle_option_field' ),
247
- 'editor' => array( $this , 'render_editor_option_field' ),
248
- 'csv' => array( $this , 'render_csv_option_field' ),
249
- 'key_value' => array( $this , 'render_key_value_option_field' ),
250
- 'link' => array( $this , 'render_link_option_field' ),
251
- 'option_divider' => array( $this , 'render_option_divider_option_field' ),
252
- 'migration_controls' => array( $this , 'render_migration_controls_option_field' ),
253
- 'social_links' => array( $this , 'render_social_links_option_field' ),
254
- 'export_global_settings' => array( $this , 'render_export_global_settings_option_field' ),
255
- 'import_global_settings' => array( $this , 'render_import_global_settings_option_field' )
256
- ) );
257
-
258
- $this->_skip_wp_settings_registration = apply_filters( 'ta_skip_wp_settings_registration' , array(
259
- 'link',
260
- 'option_divider',
261
- 'migration_controls',
262
- 'export_global_settings',
263
- 'import_global_settings'
264
- ) );
265
-
266
- // Toggle options for settings with 'category' as an option.
267
- $toggle_cat_options = array(
268
- 'yes' => __( 'Yes' , 'thirstyaffiliates' ),
269
- 'no' => __( 'No' , 'thirstyaffiliates' ),
270
- 'category' => __( 'Per category' , 'thirstyaffiliates' )
271
- );
272
- $all_categories = $this->_helper_functions->get_all_category_as_options();
273
-
274
- $this->_settings_sections = apply_filters( 'ta_settings_option_sections' , array(
275
- 'ta_general_settings' => array(
276
- 'title' => __( 'General' , 'thirstyaffiliates' ) ,
277
- 'desc' => __( 'Settings that change the general behaviour of ThirstyAffiliates.' , 'thirstyaffiliates' )
278
- ),
279
- 'ta_links_settings' => array(
280
- 'title' => __( 'Link Appearance' , 'thirstyaffiliates' ) ,
281
- 'desc' => __( 'Settings that specifically affect the behaviour & appearance of your affiliate links.' , 'thirstyaffiliates' )
282
- ),
283
- 'ta_modules_settings' => array(
284
- 'title' => __( 'Modules' , 'thirstyaffiliates' ),
285
- 'desc' => __( 'This section allows you to turn certain parts of ThirstyAffiliates on or off. Below are the individual modules and features of the plugin that can be controlled.' , 'thirstyaffiliates' )
286
- ),
287
- 'ta_import_export_settings' => array(
288
- 'title' => __( 'Import/Export' , 'thirstyaffiliates' ),
289
- 'desc' => __( 'Import and Export global ThirstyAffiliates plugin settings from one site to another.' , 'thirstyaffiliates' )
290
- ),
291
- 'ta_help_settings' => array(
292
- 'title' => __( 'Help' , 'thirstyaffiliates' ),
293
- 'desc' => __( 'Links to knowledge base and other utilities.' , 'thirstyaffiliates' )
294
- )
295
- ) );
296
-
297
- $this->_settings_section_options = apply_filters( 'ta_settings_section_options' , array(
298
- 'ta_general_settings' => apply_filters( 'ta_general_settings_options' , array(
299
-
300
- array(
301
- 'id' => 'ta_link_insertion_type',
302
- 'title' => __( 'Default Link Insertion Type' , 'thirstyaffiliates' ),
303
- 'desc' => __( "Determines the default link type when inserting a link using the quick search." , 'thirstyaffiliates' ),
304
- 'type' => 'select',
305
- 'default' => 'link',
306
- 'options' => array(
307
- 'link' => __( 'Link' , 'thirstyaffiliates' ),
308
- 'shortcode' => __( 'Shortcode' , 'thirstyaffiliates' ),
309
- )
310
- ),
311
-
312
- array(
313
- 'id' => 'ta_disable_cat_auto_select',
314
- 'title' => __( 'Disable "uncategorized" category on save?' , 'thirstyaffiliates' ),
315
- 'desc' => __( 'If links are including categories in the URL then by default ThirstyAffiliates will add an "uncategorized" category to apply to non-categorised links during save. If you disable this, it allows you to have some links with categories in the URL and some without.' , 'thirstyaffiliates' ),
316
- 'type' => 'toggle'
317
- ),
318
-
319
- array(
320
- 'id' => 'ta_disable_visual_editor_buttons',
321
- 'title' => __( 'Disable buttons on the Visual editor?' , 'thirstyaffiliates' ),
322
- 'desc' => __( "Hide the ThirstyAffiliates buttons on the Visual editor." , 'thirstyaffiliates' ),
323
- 'type' => 'toggle'
324
- ),
325
-
326
- array(
327
- 'id' => 'ta_disable_text_editor_buttons',
328
- 'title' => __( 'Disable buttons on the Text/Quicktags editor?' , 'thirstyaffiliates' ),
329
- 'desc' => __( "Hide the ThirstyAffiliates buttons on the Text editor." , 'thirstyaffiliates' ),
330
- 'type' => 'toggle'
331
- ),
332
-
333
- array(
334
- 'id' => 'ta_stats_trimer_set_point',
335
- 'title' => __( 'Trim stats older than:' , 'thirstyaffiliates' ),
336
- 'desc' => __( "months (Automatically clean the statistics database records older than a set point. Setting this to 0 will disable it)." , 'thirstyaffiliates' ),
337
- 'type' => 'number',
338
- 'min' => 0,
339
- 'default' => 0,
340
- 'condition_cb' => function() { return get_option( 'ta_enable_stats_reporting_module' ) === 'yes'; }
341
- ),
342
-
343
- array(
344
- 'id' => 'ta_browser_no_cache_301_redirect',
345
- 'title' => __( "Don't cache 301 redirects? (server side redirects)" , 'thirstyaffiliates' ),
346
- 'desc' => __( "By default, browsers caches the 301 redirects. Enabling this option will tell the browser not to cache 301 redirects. Be aware that it is still up to the browser if it will cache it or not." , 'thirstyaffiliates' ),
347
- 'type' => 'toggle'
348
- ),
349
-
350
- array(
351
- 'id' => 'ta_disable_ip_address_collection',
352
- 'title' => __( "Disable IP address collection" , 'thirstyaffiliates' ),
353
- 'desc' => __( "By default ThirstyAffiliates plugin collects visitor's IP address everytime they click an affiliate link as part of the statistics information. By checking this the IP address collection will be disabled, but other information will still be saved." , 'thirstyaffiliates' ),
354
- 'type' => 'toggle'
355
- ),
356
-
357
- array(
358
- 'id' => 'ta_disable_browser_device_collection',
359
- 'title' => __( "Disable browser/device data collection" , 'thirstyaffiliates' ),
360
- 'desc' => __( "As of version 3.4, by default ThirstyAffiliates plugin collects visitor's browser and device used everytime they click an affiliate link as part of the statistics information. By checking this the browser/device collection will be disabled, but other information will still be saved." , 'thirstyaffiliates' ),
361
- 'type' => 'toggle'
362
- ),
363
-
364
- array(
365
- 'id' => 'ta_blocked_bots',
366
- 'title' => __( "Blocked bots" , 'thirstyaffiliates' ),
367
- 'desc' => __( "By default ThirstyAffiliates blocks bots accessing your affiliate links to give you a more appropriate data in the report. Select bots, or enter new ones to block." , 'thirstyaffiliates' ),
368
- 'type' => 'textarea',
369
- 'default' => Plugin_Constants::DEFAULT_BLOCKED_BOTS,
370
-
371
- ),
372
-
373
- array(
374
- 'id' => 'ta_enable_bot_crawl_blocker_script',
375
- 'title' => __( "Enable Bot Crawl Blocker Script" , 'thirstyaffiliates' ),
376
- 'desc' => __( "By enabling this setting, your affiliate links won't redirect for all the <em>blocked bots</em> set above and will send out a 403 forbidden error." , 'thirstyaffiliates' ),
377
- 'type' => 'toggle'
378
- )
379
-
380
- ) ),
381
- 'ta_links_settings' => apply_filters( 'ta_links_settings_options' , array(
382
-
383
- array(
384
- 'id' => 'ta_link_prefix',
385
- 'title' => __( 'Link Prefix' , 'thirstyaffiliates' ),
386
- 'desc' => sprintf( __( "The prefix that comes before your cloaked link's slug. <br>eg. %s/<strong>recommends</strong>/your-affiliate-link-name.<br><br><b>Warning :</b> Changing this setting after you've used links in a post could break those links. Be careful!" , 'thirstyaffiliates' ) , home_url() ),
387
- 'type' => 'select',
388
- 'default' => 'recommends',
389
- 'options' => array(
390
- 'custom' => '-- custom --',
391
- 'recommends' => 'recommends',
392
- 'link' => 'link',
393
- 'go' => 'go',
394
- 'review' => 'review',
395
- 'product' => 'product',
396
- 'suggests' => 'suggests',
397
- 'follow' => 'follow',
398
- 'endorses' => 'endorses',
399
- 'proceed' => 'proceed',
400
- 'fly' => 'fly',
401
- 'goto' => 'goto',
402
- 'get' => 'get',
403
- 'find' => 'find',
404
- 'act' => 'act',
405
- 'click' => 'click',
406
- 'move' => 'move',
407
- 'offer' => 'offer',
408
- 'run' => 'run'
409
- )
410
- ),
411
-
412
- array(
413
- 'id' => 'ta_link_prefix_custom',
414
- 'title' => __( 'Custom Link Prefix' , 'thirstyaffiliates' ),
415
- 'desc' => __( 'Enter your preferred link prefix.' , 'thirstyaffiliates' ),
416
- 'type' => 'text'
417
- ),
418
-
419
- array(
420
- 'id' => 'ta_show_cat_in_slug',
421
- 'title' => __( 'Link Category in URL?' , 'thirstyaffiliates' ),
422
- 'desc' => sprintf( __( "Shows the primary selected category in the url. eg. %s/recommends/<strong>link-category</strong>/your-affiliate-link-name.<br><br><b>Warning :</b> Changing this setting after you've used links in a post could break those links. Be careful!" , 'thirstyaffiliates' ) , home_url() ),
423
- 'type' => 'toggle'
424
- ),
425
-
426
- array(
427
- 'id' => 'ta_enable_javascript_frontend_redirect',
428
- 'title' => __( "Enable Enhanced Javascript Redirect on Frontend" , 'thirstyaffiliates' ),
429
- 'desc' => __( "By default affiliate links are redirected on the server side. Enabling this will set all affiliate links to be redirected via javascript on your website's frontend. This will then improve the accuracy of the link performance report (This will only work when <strong>Statistics</strong> module is enabled)." , 'thirstyaffiliates' ),
430
- 'type' => 'toggle',
431
- 'condition_cb' => function() { return get_option( 'ta_enable_stats_reporting_module' ) === 'yes'; }
432
- ),
433
-
434
- array(
435
- 'id' => 'ta_link_redirect_type',
436
- 'title' => __( 'Link Redirect Type (server side redirects)' , 'thirstyaffiliates' ),
437
- 'desc' => __( "This is the type of redirect ThirstyAffiliates will use to redirect the user to your affiliate link." , 'thirstyaffiliates' ),
438
- 'type' => 'radio',
439
- 'options' => $this->_constants->REDIRECT_TYPES(),
440
- 'default' => '302'
441
- ),
442
-
443
- array(
444
- 'id' => 'ta_no_follow',
445
- 'title' => __( 'Use no follow on links? (server side redirects)' , 'thirstyaffiliates' ),
446
- 'desc' => __( "Add the nofollow attribute to links so search engines don't index them." , 'thirstyaffiliates' ),
447
- 'type' => 'select',
448
- 'options' => $toggle_cat_options,
449
- 'default' => 'no',
450
- 'class' => 'toggle-cat'
451
- ),
452
-
453
- array(
454
- 'id' => 'ta_no_follow_category',
455
- 'title' => __( 'No follow categories (server side redirects)' , 'thirstyaffiliates' ),
456
- 'desc' => __( "The links assigned to the selected category will be set as \"no follow\"." , 'thirstyaffiliates' ),
457
- 'type' => 'multiselect',
458
- 'options' => $all_categories,
459
- 'default' => array(),
460
- 'placeholder' => __( 'Select category...' , 'thirstyaffiliates' ),
461
- 'class' => 'toggle-cat-select toggle-cat-ta_no_follow',
462
- 'required' => true,
463
- ),
464
-
465
- array(
466
- 'id' => 'ta_new_window',
467
- 'title' => __( 'Open links in new window?' , 'thirstyaffiliates' ),
468
- 'desc' => __( "Make links open in a new browser tab by default." , 'thirstyaffiliates' ),
469
- 'type' => 'select',
470
- 'options' => $toggle_cat_options,
471
- 'default' => 'no',
472
- 'class' => 'toggle-cat'
473
- ),
474
-
475
- array(
476
- 'id' => 'ta_new_window_category',
477
- 'title' => __( 'New window categories' , 'thirstyaffiliates' ),
478
- 'desc' => __( "The links assigned to the selected category will be set as \"new window\"." , 'thirstyaffiliates' ),
479
- 'type' => 'multiselect',
480
- 'options' => $all_categories,
481
- 'default' => array(),
482
- 'placeholder' => __( 'Select category...' , 'thirstyaffiliates' ),
483
- 'class' => 'toggle-cat-select toggle-cat-ta_new_window',
484
- 'required' => true,
485
- ),
486
-
487
- array(
488
- 'id' => 'ta_pass_query_str',
489
- 'title' => __( 'Pass query strings to destination url?' , 'thirstyaffiliates' ),
490
- 'desc' => __( "Enabling this option will pass all of the query strings present after the cloaked url to the destination url automatically when redirecting." , 'thirstyaffiliates' ),
491
- 'type' => 'select',
492
- 'options' => $toggle_cat_options,
493
- 'default' => 'no',
494
- 'class' => 'toggle-cat',
495
- ),
496
-
497
- array(
498
- 'id' => 'ta_pass_query_str_category',
499
- 'title' => __( 'Pass query strings categories' , 'thirstyaffiliates' ),
500
- 'desc' => __( "The links assigned to the selected category will be set as \"pass query strings\"." , 'thirstyaffiliates' ),
501
- 'type' => 'multiselect',
502
- 'options' => $all_categories,
503
- 'default' => array(),
504
- 'placeholder' => __( 'Select category...' , 'thirstyaffiliates' ),
505
- 'class' => 'toggle-cat-select toggle-cat-ta_pass_query_str',
506
- 'required' => true,
507
- ),
508
-
509
- array(
510
- 'id' => 'ta_additional_rel_tags',
511
- 'title' => __( 'Additional rel attribute tags' , 'thirstyaffiliates' ),
512
- 'desc' => __( "Allows you to add extra tags into the rel= attribute when links are inserted." , 'thirstyaffiliates' ),
513
- 'type' => 'text'
514
- ),
515
-
516
- array(
517
- 'id' => 'ta_additional_css_classes',
518
- 'title' => __( 'Additional CSS classes' , 'thirstyaffiliates' ),
519
- 'desc' => __( "Allows you to add extra CSS classes when links are inserted." , 'thirstyaffiliates' ),
520
- 'type' => 'text'
521
- ),
522
-
523
- array(
524
- 'id' => 'ta_disable_thirsty_link_class',
525
- 'title' => __( 'Disable ThirstyAffiliates CSS classes?' , 'thirstyaffiliates' ),
526
- 'desc' => __( 'To help with styling a CSS class called "thirstylink" is added links on insertion.<br>Likewise the "thirstylinkimg" class is added to images when using the image insertion type. This option disables the addition these CSS classes.' , 'thirstyaffiliates' ),
527
- 'type' => 'toggle'
528
- ),
529
-
530
- array(
531
- 'id' => 'ta_disable_title_attribute',
532
- 'title' => __( 'Disable title attribute on link insertion?' , 'thirstyaffiliates' ),
533
- 'desc' => __( "Links are automatically output with a title html attribute (by default this shows the title of the affiliate link).<br>This option disables the output of the title attribute on your links." , 'thirstyaffiliates' ),
534
- 'type' => 'toggle'
535
- ),
536
-
537
- array(
538
- 'id' => 'ta_category_to_uncloak',
539
- 'title' => __( 'Select Category to Uncloak' , 'thirstyaffiliates' ),
540
- 'desc' => __( "The links assigned to the selected category will be uncloaked." , 'thirstyaffiliates' ),
541
- 'type' => 'multiselect',
542
- 'options' => $all_categories,
543
- 'default' => array(),
544
- 'condition_cb' => function() { return get_option( 'ta_uncloak_link_per_link' ) === 'yes'; },
545
- 'placeholder' => __( 'Select category...' , 'thirstyaffiliates' )
546
- )
547
-
548
- ) ),
549
-
550
- 'ta_modules_settings' => apply_filters( 'ta_modules_settings_options' , array(
551
-
552
- array(
553
- 'id' => 'ta_enable_stats_reporting_module',
554
- 'title' => __( 'Statistics' , 'thirstyaffiliates' ),
555
- 'desc' => __( "When enabled, ThirstyAffiliates will collect click statistics information about visitors that click on your affiliate links. Also adds a new Reports section." , 'thirstyaffiliates' ),
556
- 'type' => 'toggle',
557
- 'default' => 'yes'
558
- ),
559
-
560
- array(
561
- 'id' => 'ta_enable_link_fixer',
562
- 'title' => __( 'Link Fixer' , 'thirstyaffiliates' ),
563
- 'desc' => __( "Link Fixer is a tiny piece of javascript code that runs on the frontend of your site to fix any outdated/broken affiliate links it detects. It's cache-friendly and runs after page load so it doesn't affect the rendering of content. Changed the settings on your site recently? Enabling Link Fixer means you don't need to update all your previously inserted affiliate links one by one – your visitors will never see an out of date affiliate link again." , 'thirstyaffiliates' ),
564
- 'type' => 'toggle',
565
- 'default' => 'yes',
566
- ),
567
-
568
- array(
569
- 'id' => 'ta_uncloak_link_per_link',
570
- 'title' => __( 'Uncloak Links' , 'thirstyaffiliates' ),
571
- 'desc' => __( "Uncloak Links is a feature to allow uncloaking of specific links on your site. It replaces the cloaked url with the actual destination url which is important for compatibility with some affiliate program with stricter terms (such as Amazon Associates). Once enabled, you will see a new Uncloak Link checkbox on the affiliate link edit screen. It also introduces a new setting under the Links tab for uncloaking whole categories.<br><br><b>Warning : </b>For this feature to work, the <strong>Link Fixer</strong> module needs to be turned on." , 'thirstyaffiliates' ),
572
- 'type' => 'toggle',
573
- 'default' => 'no',
574
- )
575
-
576
- ) ),
577
- 'ta_import_export_settings' => apply_filters( 'ta_import_export_settings_options' , array(
578
-
579
- array(
580
- 'id' => 'ta_import_settings',
581
- 'title' => __( 'Import Global Settings' , 'thirstyaffiliates' ),
582
- 'type' => 'import_global_settings',
583
- 'placeholder' => __( 'Paste settings string here...' , 'thirstyaffiliates' )
584
- ),
585
-
586
- array(
587
- 'id' => 'ta_export_settings',
588
- 'title' => __( 'Export Global Settings' , 'thirstyaffiliates' ),
589
- 'type' => 'export_global_settings'
590
- )
591
-
592
- ) ),
593
- 'ta_help_settings' => apply_filters( 'ta_help_settings_options' , array(
594
-
595
- array(
596
- 'id' => 'ta_knowledge_base_divider', // Even though no option is really saved, we still add id, for the purpose of later when extending this section options, they can search for this specific section divider during array loop
597
- 'title' => __( 'Knowledge Base' , 'thirstyaffiliates' ),
598
- 'type' => 'option_divider'
599
- ),
600
-
601
- array(
602
- 'title' => __( 'Documentation' , 'thirstyaffiliates' ),
603
- 'type' => 'link',
604
- 'link_url' => 'https://thirstyaffiliates.com/knowledge-base/?utm_source=Free%20Plugin&utm_medium=Help&utm_campaign=Knowledge%20Base%20Link',
605
- 'link_text' => __( 'Knowledge Base' , 'thirstyaffiliates' ),
606
- 'desc' => __( 'Guides, troubleshooting, FAQ and more.' , 'thirstyaffiliates' ),
607
- 'id' => 'ta_kb_link',
608
- ),
609
-
610
- array(
611
- 'title' => __( 'Our Blog' , 'thirstyaffiliates' ),
612
- 'type' => 'link',
613
- 'link_url' => 'https://thirstyaffiliates.com/blog?utm_source=Free%20Plugin&utm_medium=Help&utm_campaign=Blog%20Link',
614
- 'link_text' => __( 'ThirstyAffiliates Blog' , 'thirstyaffiliates' ),
615
- 'desc' => __( 'Learn & grow your affiliate marketing – covering increasing sales, generating traffic, optimising your affiliate marketing, interviews & case studies.' , 'thirstyaffiliates' ),
616
- 'id' => 'ta_blog_link',
617
- ),
618
-
619
- array(
620
- 'title' => __( 'Join the Community' , 'thirstyaffiliates' ),
621
- 'type' => 'social_links',
622
- 'id' => 'ta_social_links'
623
- ),
624
-
625
- array(
626
- 'id' => 'ta_other_utilities_divider', // Even though no option is really saved, we still add id, for the purpose of later when extending this section options, they can search for this specific section divider during array loop
627
- 'title' => __( 'Other Utilities' , 'thirstyaffiliates' ),
628
- 'type' => 'option_divider'
629
- ),
630
-
631
- array(
632
- 'title' => __( 'Migrate Old Data' , 'thirstyaffiliates' ),
633
- 'type' => 'migration_controls',
634
- 'desc' => __( 'Migrate old ThirstyAffiliates version 2 data to new version 3 data model.' , 'thirstyaffiliates' ),
635
- 'id' => 'ta_migrate_old_data'
636
- )
637
-
638
- ) )
639
-
640
- ) );
641
-
642
- // Get all the exportable options
643
- foreach ( $this->_settings_section_options as $section_id => $section_options ) {
644
-
645
- foreach ( $section_options as $option ) {
646
-
647
- if ( in_array( $option[ 'type' ] , $this->_skip_wp_settings_registration ) )
648
- continue;
649
-
650
- $this->_exportable_options[ $option[ 'id' ] ] = isset( $option[ 'default' ] ) ? $option[ 'default' ] : '';
651
-
652
- if ( isset( $option[ 'post_update_cb' ] ) && is_callable( $option[ 'post_update_cb' ] ) )
653
- add_action( 'update_option_' . $option[ 'id' ] , $option[ 'post_update_cb' ] , 10 , 3 );
654
-
655
- }
656
-
657
- }
658
-
659
- }
660
-
661
- /**
662
- * Register Settings Section and Options Group to WordPress Settings API.
663
- *
664
- * @since 3.0.0
665
- * @access public
666
- */
667
- public function register_settings_section_and_options_group() {
668
-
669
- foreach ( $this->_settings_sections as $section_id => $section_data ) {
670
-
671
- add_settings_section(
672
- $section_id, // Settings Section ID
673
- $section_data[ 'title' ], // Settings Section Title
674
- function() {}, // Callback. Intentionally Left Empty. We Will Handle UI Rendering.
675
- $section_id . '_options_group' // Options Group
676
- );
677
-
678
- }
679
-
680
- }
681
-
682
- /**
683
- * Register Settings Section Options to WordPress Settings API.
684
- *
685
- * @since 3.0.0
686
- * @access public
687
- */
688
- public function register_settings_section_options() {
689
-
690
- foreach ( $this->_settings_section_options as $section_id => $section_options ) {
691
-
692
- foreach ( $section_options as $option ) {
693
-
694
- if ( !array_key_exists( $option[ 'type' ] , $this->_supported_field_types ) || in_array( $option[ 'type' ] , $this->_skip_wp_settings_registration ) )
695
- continue;
696
-
697
- // Register The Option To The Options Group It Is Scoped With
698
- add_settings_field(
699
- $option[ 'id' ], // Option ID
700
- $option[ 'title' ], // Option Title
701
- function() {}, // Render Callback. Intentionally Left Empty. We Will Handle UI Rendering.
702
- $section_id . '_options_group', // Options Group
703
- $section_id // Settings Section ID
704
- );
705
-
706
- // Register The Actual Settings Option
707
- $args = array();
708
-
709
- if ( isset( $option[ 'data_type' ] ) )
710
- $args[ 'type' ] = $option[ 'data_type' ];
711
-
712
- if ( isset( $option[ 'desc' ] ) )
713
- $args[ 'description' ] = $option[ 'desc' ];
714
-
715
- if ( isset( $option[ 'sanitation_cb' ] ) && is_callable( $option[ 'sanitation_cb' ] ) )
716
- $args[ 'sanitize_callback' ] = $option[ 'sanitation_cb' ];
717
-
718
- if ( isset( $option[ 'show_in_rest' ] ) )
719
- $args[ 'show_in_rest' ] = $option[ 'show_in_rest' ];
720
-
721
- if ( isset( $option[ 'default' ] ) )
722
- $args[ 'default' ] = $option[ 'default' ]; // Important Note: This will be used on "get_option" function automatically if the current option is not registered yet to the options db.
723
-
724
- register_setting( $section_id . '_options_group' , $option[ 'id' ] , $args );
725
-
726
- }
727
-
728
- }
729
-
730
- }
731
-
732
- /**
733
- * Initialize Plugin Settings API.
734
- * We register the plugin settings section and settings section options to WordPress Settings API.
735
- * We let WordPress Settings API handle the backend stuff.
736
- * We will handle the UI rendering.
737
- *
738
- * @since 3.0.0
739
- * @access public
740
- */
741
- public function init_plugin_settings() {
742
-
743
- $this->init_settings_sections_and_options();
744
- $this->register_settings_section_and_options_group();
745
- $this->register_settings_section_options();
746
-
747
- }
748
-
749
- /**
750
- * Add settings page.
751
- *
752
- * @since 3.0.0
753
- * @since 3.2.2 Access to the settings page will now be controlled by the plugin. see Bootstrap::admin_interface_visibility.
754
- * @access public
755
- */
756
- public function add_settings_page() {
757
-
758
- if ( ! current_user_can( 'edit_posts' ) ) return;
759
-
760
- add_submenu_page(
761
- 'edit.php?post_type=thirstylink',
762
- __( 'ThirstyAffiliates Settings' , 'thirstyaffiliates' ),
763
- __( 'Settings' , 'thirstyaffiliates' ),
764
- 'read',
765
- 'thirsty-settings',
766
- array( $this, 'view_settings_page' )
767
- );
768
-
769
- }
770
-
771
- /**
772
- * Settings page view.
773
- *
774
- * @since 3.0.0
775
- * @since 3.3.1 Add a new <div> to wrap the side navigation and form. Add additional action hooks.
776
- * @access public
777
- */
778
- public function view_settings_page() {
779
- ?>
780
-
781
- <div class="wrap ta-settings">
782
-
783
- <h2><?php _e( 'ThirstyAffiliates Settings' , 'thirstyaffiliates' ); ?></h2>
784
-
785
- <?php
786
- settings_errors(); // Show notices based on the outcome of the settings save action
787
- $active_tab = isset( $_GET[ 'tab' ] ) ? $_GET[ 'tab' ] : 'ta_general_settings';
788
- ?>
789
-
790
- <div class="ta-settings-wrapper">
791
-
792
- <?php do_action( 'ta_before_settings_sidenav' ); ?>
793
-
794
- <h2 class="nav-tab-wrapper">
795
- <?php foreach ( $this->_settings_sections as $section_key => $section_data ) { ?>
796
- <a href="?post_type=thirstylink&page=thirsty-settings&tab=<?php echo $section_key; ?>" class="nav-tab <?php echo $active_tab == $section_key ? 'nav-tab-active' : ''; ?> <?php echo $section_key; ?>"><?php echo $section_data[ 'title' ]; ?></a>
797
- <?php } ?>
798
-
799
- <?php if ( ! $this->_helper_functions->is_plugin_active( 'thirstyaffiliates-pro/thirstyaffiliates-pro.php' ) ) : ?>
800
- <a class="tapro-upgrade nav-tab" href="https://thirstyaffiliates.com/pricing/?utm_source=Free%20Plugin&utm_medium=Pro&utm_campaign=Admin%20Settings" target="_blank"><?php _e( 'Pro Features →' , 'thirstyaffiliates' ); ?></a>
801
- <?php endif; ?>
802
- </h2>
803
-
804
- <?php do_action( 'ta_before_settings_form' ); ?>
805
-
806
- <div class="ta-settings-form">
807
-
808
- <form method="post" action="options.php" enctype="multipart/form-data">
809
-
810
- <?php
811
- $this->render_settings_section_nonces( $active_tab );
812
- $this->render_settings_section_header( $active_tab );
813
- $this->render_settings_section_fields( $active_tab );
814
- ?>
815
-
816
- </form>
817
- </div>
818
-
819
- <?php do_action( 'ta_after_settings_form' ); ?>
820
-
821
- </div>
822
-
823
- </div><!--wrap-->
824
-
825
- <?php
826
- }
827
-
828
- /**
829
- * Render all necessary nonces for the current settings section.
830
- *
831
- * @since 3.0.0
832
- * @access public
833
- *
834
- * @param string $active_tab Currently active settings section.
835
- */
836
- public function render_settings_section_nonces( $active_tab ) {
837
-
838
- settings_fields( $active_tab . '_options_group' );
839
-
840
- }
841
-
842
- /**
843
- * Render the current settings section header markup.
844
- *
845
- * @since 3.0.0
846
- * @access public
847
- *
848
- * @param string $active_tab Currently active settings section.
849
- */
850
- public function render_settings_section_header( $active_tab ) {
851
-
852
- if ( ! isset( $this->_settings_sections[ $active_tab ] ) )
853
- return;
854
-
855
- ?>
856
-
857
- <h2><?php echo $this->_settings_sections[ $active_tab ][ 'title' ]; ?></h2>
858
- <p class="desc"><?php echo $this->_settings_sections[ $active_tab ][ 'desc' ]; ?></p>
859
-
860
- <?php
861
-
862
- }
863
-
864
- /**
865
- * Render an option as a hidden field.
866
- * We do this if that option's condition callback failed.
867
- * We don't show it for the end user, but we still need to pass on the form the current data so we don't lost it.
868
- *
869
- * @since 3.0.0
870
- * @access public
871
- *
872
- * @param array $option Array of options data. May vary depending on option type.
873
- */
874
- public function render_option_as_hidden_field( $option ) {
875
-
876
- if ( in_array( $option[ 'type' ] , $this->_skip_wp_settings_registration ) )
877
- return; // This is a decorative option type, no need to render this as a hidden field
878
-
879
- ?>
880
-
881
- <input type="hidden" name="<?php echo esc_attr( $option[ 'id' ] ); ?>" value="<?php echo get_option( $option[ 'id' ] , '' ); ?>">
882
-
883
- <?php
884
-
885
- }
886
-
887
- /**
888
- * Render settings section option fields.
889
- *
890
- * @since 3.0.0
891
- * @access public
892
- *
893
- * @param string $active_tab Currently active settings section.
894
- */
895
- public function render_settings_section_fields( $active_tab ) {
896
-
897
- $no_save_sections = apply_filters( 'ta_render_settings_no_save_section' , array(
898
- 'ta_import_export_settings'
899
- ) );
900
- ?>
901
-
902
- <table class="form-table">
903
- <tbody>
904
- <?php
905
- foreach ( $this->_settings_section_options as $section_id => $section_options ) {
906
-
907
- if ( $section_id !== $active_tab )
908
- continue;
909
-
910
- foreach ( $section_options as $option ) {
911
-
912
- if ( isset( $option[ 'condition_cb' ] ) && is_callable( $option[ 'condition_cb' ] ) && !$option[ 'condition_cb' ]() )
913
- $this->render_option_as_hidden_field( $option ); // Option condition failed. Render it as a hidden field so its value is preserved
914
- else
915
- $this->_supported_field_types[ $option[ 'type' ] ]( $option );
916
-
917
- }
918
-
919
- }
920
- ?>
921
- </tbody>
922
- </table>
923
-
924
- <?php if ( ! in_array( $active_tab , $no_save_sections ) ) : ?>
925
- <p class="submit">
926
- <input name="submit" id="submit" class="button button-primary" value="<?php _e( 'Save Changes' , 'thirstyaffiliates' ); ?>" type="submit">
927
- </p>
928
- <?php endif;
929
-
930
- }
931
-
932
-
933
-
934
-
935
- /*
936
- |--------------------------------------------------------------------------
937
- | Option Field Views
938
- |--------------------------------------------------------------------------
939
- */
940
-
941
- /**
942
- * Render 'text' type option field.
943
- *
944
- * @since 3.0.0
945
- * @access public
946
- *
947
- * @param array $option Array of options data. May vary depending on option type.
948
- */
949
- public function render_text_option_field( $option ) {
950
-
951
- ?>
952
-
953
- <tr valign="top" class="<?php echo esc_attr( $option[ 'id' ] ) . '-row'; ?>">
954
-
955
- <th scope="row"><?php echo sanitize_text_field( $option[ 'title' ] ); ?></th>
956
-
957
- <td class="forminp forminp-<?php echo sanitize_title( $option[ 'type' ] ) ?>">
958
- <input
959
- type = "text"
960
- name = "<?php echo esc_attr( $option[ 'id' ] ); ?>"
961
- id = "<?php echo esc_attr( $option[ 'id' ] ); ?>"
962
- class = "option-field <?php echo isset( $option[ 'class' ] ) ? esc_attr( $option[ 'class' ] ) : ''; ?>"
963
- style = "<?php echo isset( $option[ 'style' ] ) ? $option[ 'style' ] : 'width: 360px;'; ?>"
964
- value = "<?php echo get_option( $option[ 'id' ] ); ?>" >
965
- <br>
966
- <p class="desc"><?php echo isset( $option[ 'desc' ] ) ? $option[ 'desc' ] : ''; ?></p>
967
- </td>
968
-
969
- </tr>
970
-
971
- <?php
972
-
973
- }
974
-
975
- /**
976
- * Render 'url' type option field.
977
- *
978
- * @since 3.3.1
979
- * @access public
980
- *
981
- * @param array $option Array of options data. May vary depending on option type.
982
- */
983
- public function render_url_option_field( $option ) {
984
-
985
- ?>
986
-
987
- <tr valign="top" class="<?php echo esc_attr( $option[ 'id' ] ) . '-row'; ?>">
988
-
989
- <th scope="row"><?php echo sanitize_text_field( $option[ 'title' ] ); ?></th>
990
-
991
- <td class="forminp forminp-<?php echo sanitize_title( $option[ 'type' ] ) ?>">
992
- <input
993
- type = "url"
994
- name = "<?php echo esc_attr( $option[ 'id' ] ); ?>"
995
- id = "<?php echo esc_attr( $option[ 'id' ] ); ?>"
996
- class = "option-field <?php echo isset( $option[ 'class' ] ) ? esc_attr( $option[ 'class' ] ) : ''; ?>"
997
- style = "<?php echo isset( $option[ 'style' ] ) ? $option[ 'style' ] : 'width: 360px;'; ?>"
998
- value = "<?php echo get_option( $option[ 'id' ] ); ?>" >
999
- <br>
1000
- <p class="desc"><?php echo isset( $option[ 'desc' ] ) ? $option[ 'desc' ] : ''; ?></p>
1001
- </td>
1002
-
1003
- </tr>
1004
-
1005
- <?php
1006
-
1007
- }
1008
-
1009
- /**
1010
- * Render 'text' type option field.
1011
- *
1012
- * @since 3.0.0
1013
- * @access public
1014
- *
1015
- * @param array $option Array of options data. May vary depending on option type.
1016
- */
1017
- public function render_number_option_field( $option ) {
1018
-
1019
- ?>
1020
-
1021
- <tr valign="top" class="<?php echo esc_attr( $option[ 'id' ] ) . '-row'; ?>">
1022
-
1023
- <th scope="row"><?php echo sanitize_text_field( $option[ 'title' ] ); ?></th>
1024
-
1025
- <td class="forminp forminp-<?php echo sanitize_title( $option[ 'type' ] ) ?>">
1026
- <input
1027
- type = "number"
1028
- name = "<?php echo esc_attr( $option[ 'id' ] ); ?>"
1029
- id = "<?php echo esc_attr( $option[ 'id' ] ); ?>"
1030
- class = "option-field <?php echo isset( $option[ 'class' ] ) ? esc_attr( $option[ 'class' ] ) : ''; ?>"
1031
- style = "<?php echo isset( $option[ 'style' ] ) ? $option[ 'style' ] : 'width: 100px;'; ?>"
1032
- value = "<?php echo get_option( $option[ 'id' ] ); ?>"
1033
- min = "<?php echo isset( $option[ 'min' ] ) ? $option[ 'min' ] : 0; ?>"
1034
- max = "<?php echo isset( $option[ 'max' ] ) ? $option[ 'max' ] : ''; ?>" >
1035
- <span><?php echo isset( $option[ 'desc' ] ) ? $option[ 'desc' ] : ''; ?></span>
1036
- </td>
1037
-
1038
- </tr>
1039
-
1040
- <?php
1041
-
1042
- }
1043
-
1044
- /**
1045
- * Render 'textarea' type option field.
1046
- *
1047
- * @since 3.0.0
1048
- * @access public
1049
- *
1050
- * @param array $option Array of options data. May vary depending on option type.
1051
- */
1052
- public function render_textarea_option_field( $option ) {
1053
-
1054
- ?>
1055
-
1056
- <tr valign="top" class="<?php echo esc_attr( $option[ 'id' ] ) . '-row'; ?>">
1057
-
1058
- <th scope="row"><?php echo sanitize_text_field( $option[ 'title' ] ); ?></th>
1059
-
1060
- <td class="forminp forminp-<?php echo sanitize_title( $option[ 'type' ] ) ?>">
1061
- <textarea
1062
- name = "<?php echo esc_attr( $option[ 'id' ] ); ?>"
1063
- id = "<?php echo esc_attr( $option[ 'id' ] ); ?>"
1064
- class = "option-field <?php echo isset( $option[ 'class' ] ) ? esc_attr( $option[ 'class' ] ) : ''; ?>"
1065
- cols = "60"
1066
- rows = "8"
1067
- style = "<?php echo isset( $option[ 'style' ] ) ? $option[ 'style' ] : 'width: 360px;'; ?>"><?php echo get_option( $option[ 'id' ] ); ?></textarea>
1068
- <br />
1069
- <p class="desc"><?php echo isset( $option[ 'desc' ] ) ? $option[ 'desc' ] : ''; ?></p>
1070
- </td>
1071
-
1072
- </tr>
1073
-
1074
- <?php
1075
-
1076
- }
1077
-
1078
- /**
1079
- * Render 'checkbox' type option field.
1080
- *
1081
- * @since 3.0.0
1082
- * @access public
1083
- *
1084
- * @param array $option Array of options data. May vary depending on option type.
1085
- */
1086
- public function render_checkbox_option_field( $option ) {
1087
-
1088
- $option_val = get_option( $option[ 'id' ] );
1089
- if ( !is_array( $option_val ) )
1090
- $option_val = array(); ?>
1091
-
1092
- <tr valign="top" class="<?php echo esc_attr( $option[ 'id' ] ) . '-row'; ?>">
1093
- <th scope="row"><?php echo sanitize_text_field( $option[ 'title' ] ); ?></th>
1094
-
1095
- <td class="forminp forminp-<?php echo sanitize_title( $option[ 'type' ] ) ?>">
1096
- <?php foreach ( $option[ 'options' ] as $opt_key => $opt_text ) {
1097
-
1098
- $opt_key_class = str_replace( " " , "-" , $opt_key ); ?>
1099
-
1100
- <input
1101
- type = "checkbox"
1102
- name = "<?php echo esc_attr( $option[ 'id' ] ); ?>[]"
1103
- id = "<?php echo esc_attr( $option[ 'id' ] ); ?>"
1104
- class = "option-field <?php echo isset( $option[ 'class' ] ) ? esc_attr( $option[ 'class' ] ) : ''; ?>"
1105
- style = "<?php echo isset( $option[ 'style' ] ) ? $option[ 'style' ] : ''; ?>"
1106
- value = "<?php echo $opt_key; ?>"
1107
- <?php echo in_array( $opt_key , $option_val ) ? 'checked' : ''; ?>>
1108
-
1109
- <label class="<?php echo esc_attr( $option[ 'id' ] ); ?>"><?php echo $opt_text; ?></label>
1110
- <br>
1111
-
1112
- <?php } ?>
1113
-
1114
- <p class="desc"><?php echo isset( $option[ 'desc' ] ) ? $option[ 'desc' ] : ''; ?></p>
1115
- </td>
1116
-
1117
- <script>
1118
- jQuery( document ).ready( function( $ ) {
1119
-
1120
- $( "label.<?php echo esc_attr( $option[ 'id' ] ); ?>" ).on( "click" , function() {
1121
-
1122
- $( this ).prev( "input[type='checkbox']" ).trigger( "click" );
1123
-
1124
- } );
1125
-
1126
- } );
1127
- </script>
1128
- </tr>
1129
-
1130
- <?php
1131
-
1132
- }
1133
-
1134
- /**
1135
- * Render 'radio' type option field.
1136
- *
1137
- * @since 3.0.0
1138
- * @access public
1139
- *
1140
- * @param array $option Array of options data. May vary depending on option type.
1141
- */
1142
- public function render_radio_option_field( $option ) {
1143
-
1144
- $option_val = get_option( $option[ 'id' ] ); ?>
1145
-
1146
-
1147
- <tr valign="top" class="<?php echo esc_attr( $option[ 'id' ] ) . '-row'; ?>">
1148
- <th scope="row"><?php echo sanitize_text_field( $option[ 'title' ] ); ?></th>
1149
-
1150
- <td class="forminp forminp-<?php echo sanitize_title( $option[ 'type' ] ) ?>">
1151
- <?php foreach ( $option[ 'options' ] as $opt_key => $opt_text ) { ?>
1152
-
1153
- <input
1154
- type = "radio"
1155
- name = "<?php echo esc_attr( $option[ 'id' ] ); ?>"
1156
- id = "<?php echo esc_attr( $option[ 'id' ] ); ?>"
1157
- class = "option-field <?php echo isset( $option[ 'class' ] ) ? esc_attr( $option[ 'class' ] ) : ''; ?>"
1158
- style = "<?php echo isset( $option[ 'style' ] ) ? $option[ 'style' ] : ''; ?>"
1159
- value = "<?php echo $opt_key; ?>"
1160
- <?php echo $opt_key == $option_val ? 'checked' : ''; ?>>
1161
-
1162
- <label class="<?php echo esc_attr( $option[ 'id' ] ); ?>"><?php echo $opt_text; ?></label>
1163
- <br>
1164
-
1165
- <?php } ?>
1166
-
1167
- <p class="desc"><?php echo isset( $option[ 'desc' ] ) ? $option[ 'desc' ] : ''; ?></p>
1168
- </td>
1169
-
1170
- <script>
1171
- jQuery( document ).ready( function( $ ) {
1172
-
1173
- $( "label.<?php echo esc_attr( $option[ 'id' ] ); ?>" ).on( "click" , function() {
1174
-
1175
- $( this ).prev( "input[type='radio']" ).trigger( "click" );
1176
-
1177
- } );
1178
-
1179
- } );
1180
- </script>
1181
- </tr>
1182
-
1183
- <?php
1184
-
1185
- }
1186
-
1187
- /**
1188
- * Render 'select' type option field.
1189
- *
1190
- * @since 3.0.0
1191
- * @access public
1192
- *
1193
- * @param array $option Array of options data. May vary depending on option type.
1194
- */
1195
- public function render_select_option_field( $option ) {
1196
-
1197
- $option_value = $this->_helper_functions->get_option( $option[ 'id' ] , $option[ 'default' ] ); ?>
1198
-
1199
- <tr valign="top" class="<?php echo esc_attr( $option[ 'id' ] ) . '-row'; ?>">
1200
- <th scope="row"><?php echo sanitize_text_field( $option[ 'title' ] ); ?></th>
1201
-
1202
- <td class="forminp forminp-<?php echo sanitize_title( $option[ 'type' ] ) ?> <?php echo isset( $option[ 'class' ] ) ? esc_attr( $option[ 'class' ] ) : ''; ?>">
1203
- <select
1204
- data-placeholder = "<?php echo isset( $option[ 'placeholder' ] ) ? $option[ 'placeholder' ] : 'Choose an option...' ; ?>"
1205
- name = "<?php echo esc_attr( $option[ 'id' ] ); ?>"
1206
- id = "<?php echo esc_attr( $option[ 'id' ] ); ?>"
1207
- class = "option-field selectize-select <?php echo isset( $option[ 'class' ] ) ? esc_attr( $option[ 'class' ] ) : ''; ?>"
1208
- style = "<?php echo isset( $option[ 'style' ] ) ? $option[ 'style' ] : 'width:360px;'; ?>">
1209
-
1210
- <?php foreach ( $option[ 'options' ] as $opt_key => $opt_text ) { ?>
1211
-
1212
- <option value="<?php echo $opt_key; ?>" <?php selected( $option_value , $opt_key ); ?>><?php echo $opt_text; ?></option>
1213
-
1214
- <?php } ?>
1215
- </select>
1216
- <p class="desc"><?php echo isset( $option[ 'desc' ] ) ? $option[ 'desc' ] : ''; ?></p>
1217
- </td>
1218
-
1219
- <script>
1220
- jQuery( document ).ready( function( $ ) {
1221
-
1222
- $( '#<?php echo esc_attr( $option[ 'id' ] ); ?>' ).selectize({
1223
- searchField : 'text'
1224
- });
1225
-
1226
- } );
1227
- </script>
1228
- </tr>
1229
-
1230
- <?php
1231
-
1232
- }
1233
-
1234
- /**
1235
- * Render 'multiselect' type option field.
1236
- *
1237
- * @since 3.0.0
1238
- * @access public
1239
- *
1240
- * @param array $option Array of options data. May vary depending on option type.
1241
- */
1242
- public function render_multiselect_option_field( $option ) {
1243
-
1244
- $option_val = get_option( $option[ 'id' ] );
1245
- if ( !is_array( $option_val ) )
1246
- $option_val = array(); ?>
1247
-
1248
- <tr valign="top" class="<?php echo esc_attr( $option[ 'id' ] ) . '-row'; ?>">
1249
- <th scope="row"><?php echo sanitize_text_field( $option[ 'title' ] ); ?></th>
1250
-
1251
- <td class="forminp forminp-<?php echo sanitize_title( $option[ 'type' ] ) ?>">
1252
- <select
1253
- multiple
1254
- data-placeholder = "<?php echo isset( $option[ 'placeholder' ] ) ? $option[ 'placeholder' ] : 'Choose an option...' ; ?>"
1255
- name = "<?php echo esc_attr( $option[ 'id' ] ); ?>[]"
1256
- id = "<?php echo esc_attr( $option[ 'id' ] ); ?>"
1257
- class = "option-field selectize-select <?php echo isset( $option[ 'class' ] ) ? esc_attr( $option[ 'class' ] ) : ''; ?>"
1258
- style = "<?php echo isset( $option[ 'style' ] ) ? $option[ 'style' ] : 'width:360px;'; ?>"
1259
- <?php echo isset( $option[ 'required' ] ) && $option[ 'required' ] ? 'required' : '' ?>>
1260
-
1261
- <?php foreach ( $option[ 'options' ] as $opt_key => $opt_text ) { ?>
1262
-
1263
- <option value="<?php echo $opt_key; ?>" <?php echo in_array( $opt_key , $option_val ) ? 'selected="selected"' : ''; ?>><?php echo $opt_text; ?></option>
1264
-
1265
- <?php } ?>
1266
- </select>
1267
- <br>
1268
- <p class="desc"><?php echo isset( $option[ 'desc' ] ) ? $option[ 'desc' ] : ''; ?></p>
1269
- </td>
1270
-
1271
- <script>
1272
- jQuery( document ).ready( function( $ ) {
1273
-
1274
- $( '#<?php echo esc_attr( $option[ 'id' ] ); ?>' ).selectize({
1275
- plugins : [ 'remove_button' , 'drag_drop' ]
1276
- });
1277
-
1278
- } );
1279
- </script>
1280
- </tr>
1281
-
1282
- <?php
1283
-
1284
- }
1285
-
1286
- /**
1287
- * Render 'toggle' type option field.
1288
- * Basically a single check box style option field.
1289
- *
1290
- * @since 3.0.0
1291
- * @access public
1292
- *
1293
- * @param array $option Array of options data. May vary depending on option type.
1294
- */
1295
- public function render_toggle_option_field( $option ) {
1296
-
1297
- ?>
1298
-
1299
- <tr valign="top" class="<?php echo esc_attr( $option[ 'id' ] ) . '-row'; ?>">
1300
- <th scope="row"><?php echo sanitize_text_field( $option[ 'title' ] ); ?></th>
1301
-
1302
- <td class="forminp forminp-<?php echo sanitize_title( $option[ 'type' ] ) ?>">
1303
- <input
1304
- type = "checkbox"
1305
- name = "<?php echo esc_attr( $option[ 'id' ] ); ?>"
1306
- id = "<?php echo esc_attr( $option[ 'id' ] ); ?>"
1307
- class = "option-field <?php echo isset( $option[ 'class' ] ) ? esc_attr( $option[ 'class' ] ) : ''; ?>"
1308
- style = "<?php echo isset( $option[ 'style' ] ) ? $option[ 'style' ] : ''; ?>"
1309
- value = "yes"
1310
- <?php echo get_option( $option[ 'id' ] ) === "yes" ? 'checked' : ''; ?>>
1311
- <label class="<?php echo esc_attr( $option[ 'id' ] ); ?>"><?php echo isset( $option[ 'desc' ] ) ? $option[ 'desc' ] : ''; ?></label>
1312
- </td>
1313
-
1314
- <script>
1315
- jQuery( document ).ready( function( $ ) {
1316
-
1317
- $( "label.<?php echo esc_attr( $option[ 'id' ] ); ?>" ).on( "click" , function() {
1318
-
1319
- $( this ).prev( "input[type='checkbox']" ).trigger( "click" );
1320
-
1321
- } );
1322
-
1323
- } );
1324
- </script>
1325
- </tr>
1326
-
1327
- <?php
1328
-
1329
- }
1330
-
1331
- /**
1332
- * Render 'editor' type option field.
1333
- *
1334
- * @since 3.0.0
1335
- * @access public
1336
- *
1337
- * @param array $option Array of options data. May vary depending on option type.
1338
- */
1339
- public function render_editor_option_field( $option ) {
1340
-
1341
- $editor_value = html_entity_decode( get_option( $option[ 'id' ] ) ); ?>
1342
-
1343
- <tr valign="top" class="<?php echo esc_attr( $option[ 'id' ] ) . '-row'; ?>">
1344
- <th scope="row"><?php echo sanitize_text_field( $option[ 'title' ] ); ?></th>
1345
-
1346
- <td class="forminp forminp-<?php echo sanitize_title( $option[ 'type' ] ) ?>">
1347
- <style type="text/css"><?php echo "div#wp-" . $option[ 'id' ] . "-wrap{ width: 70% !important; }"; ?></style>
1348
-
1349
- <?php wp_editor( $editor_value , $option[ 'id' ] , array(
1350
- 'wpautop' => true,
1351
- 'textarea_name' => $option[ 'id' ],
1352
- 'editor_height' => '300'
1353
- ) ); ?>
1354
- <br>
1355
- <p class="desc"><?php echo isset( $option[ 'desc' ] ) ? $option[ 'desc' ] : ''; ?></p>
1356
- </td>
1357
- </tr>
1358
-
1359
- <?php
1360
-
1361
- }
1362
-
1363
- /**
1364
- * Render 'csv' type option field.
1365
- *
1366
- * @since 3.0.0
1367
- * @access public
1368
- *
1369
- * @param array $option Array of options data. May vary depending on option type.
1370
- */
1371
- public function render_csv_option_field( $option ) {
1372
-
1373
- ?>
1374
-
1375
- <tr valign="top" class="<?php echo esc_attr( $option[ 'id' ] ) . '-row'; ?>">
1376
- <th scope="row"><?php echo $option[ 'title' ]; ?></th>
1377
-
1378
- <td class="forminp forminp-<?php echo sanitize_title( $option[ 'type' ] ) ?>">
1379
- <input
1380
- type = "text"
1381
- name = "<?php echo $option[ 'id' ]; ?>"
1382
- id = "<?php echo $option[ 'id' ]; ?>"
1383
- class = "option-field <?php echo isset( $option[ 'class' ] ) ? $option[ 'class' ] : ''; ?>"
1384
- style = "<?php echo isset( $option[ 'style' ] ) ? $option[ 'style' ] : 'width: 360px;'; ?>"
1385
- value = "<?php echo get_option( $option[ 'id' ] ); ?>" >
1386
- <br>
1387
- <p class="desc"><?php echo isset( $option[ 'desc' ] ) ? $option[ 'desc' ] : ''; ?></p>
1388
- </td>
1389
-
1390
- <script>
1391
- jQuery( document ).ready( function( $ ) {
1392
-
1393
- $( '#<?php echo $option[ 'id' ]; ?>' ).selectize( {
1394
- plugins : [ 'restore_on_backspace' , 'remove_button' , 'drag_drop' ],
1395
- delimiter : ',',
1396
- persist : false,
1397
- create : function( input ) {
1398
- return {
1399
- value: input,
1400
- text: input
1401
- }
1402
- }
1403
- } );
1404
-
1405
- } );
1406
- </script>
1407
- </tr>
1408
-
1409
- <?php
1410
-
1411
- }
1412
-
1413
- /**
1414
- * Render 'key_value' type option field. Do not need to be registered to WP Settings API.
1415
- *
1416
- * @since 3.0.0
1417
- * @access public
1418
- *
1419
- * @param array $option Array of options data. May vary depending on option type.
1420
- */
1421
- public function render_key_value_option_field( $option ) {
1422
-
1423
- $option_value = get_option( $option[ 'id' ] );
1424
- if ( !is_array( $option_value ) )
1425
- $option_value = array(); ?>
1426
-
1427
- <tr valign="top" class="<?php echo esc_attr( $option[ 'id' ] ) . '-row'; ?>">
1428
- <th scope="row"><?php echo $option[ 'title' ]; ?></th>
1429
-
1430
- <td class="forminp forminp-<?php echo sanitize_title( $option[ 'type' ] ) ?>">
1431
-
1432
- <div class="key-value-fields-container" data-field-id="<?php echo $option[ 'id' ]; ?>">
1433
-
1434
- <header>
1435
- <span class="key"><?php _e( 'Key' , 'thirstyaffiliates' ); ?></span>
1436
- <span class="value"><?php _e( 'Value' , 'thirstyaffiliates' ); ?></span>
1437
- </header>
1438
-
1439
- <div class="fields">
1440
-
1441
- <?php if ( empty( $option_value ) ) { ?>
1442
-
1443
- <div class="data-set">
1444
- <input type="text" class="field key-field">
1445
- <input type="text" class="field value-field">
1446
- <div class="controls">
1447
- <span class="control add dashicons dashicons-plus-alt" autocomplete="off"></span>
1448
- <span class="control delete dashicons dashicons-dismiss" autocomplete="off"></span>
1449
- </div>
1450
- </div>
1451
-
1452
- <?php } else {
1453
-
1454
- foreach ( $option_value as $key => $val ) { ?>
1455
-
1456
- <div class="data-set">
1457
- <input type="text" class="field key-field" value="<?php echo $key; ?>">
1458
- <input type="text" class="field value-field" value="<?php echo $val; ?>">
1459
- <div class="controls">
1460
- <span class="control add dashicons dashicons-plus-alt" autocomplete="off"></span>
1461
- <span class="control delete dashicons dashicons-dismiss" autocomplete="off"></span>
1462
- </div>
1463
- </div>
1464
-
1465
- <?php }
1466
-
1467
- } ?>
1468
-
1469
- </div>
1470
-
1471
- </div>
1472
-
1473
- <p class="desc"><?php echo isset( $option[ 'desc' ] ) ? $option[ 'desc' ] : ''; ?></p>
1474
-
1475
- </td>
1476
- </tr>
1477
-
1478
- <?php
1479
-
1480
- }
1481
-
1482
- /**
1483
- * Render 'link' type option field. Do not need to be registered to WP Settings API.
1484
- *
1485
- * @since 3.0.0
1486
- * @access public
1487
- *
1488
- * @param array $option Array of options data. May vary depending on option type.
1489
- */
1490
- public function render_link_option_field( $option ) {
1491
-
1492
- ?>
1493
-
1494
- <tr valign="top" class="<?php echo esc_attr( $option[ 'id' ] ) . '-row'; ?>">
1495
- <th scope="row"><?php echo sanitize_text_field( $option[ 'title' ] ); ?></th>
1496
- <td>
1497
- <a id="<?php echo esc_attr( $option[ 'id' ] ); ?>" href="<?php echo $option[ 'link_url' ]; ?>" target="_blank"><?php echo $option[ 'link_text' ]; ?></a>
1498
- <br>
1499
- <p class="desc"><?php echo isset( $option[ 'desc' ] ) ? $option[ 'desc' ] : ''; ?></p>
1500
- </td>
1501
- </tr>
1502
-
1503
- <?php
1504
-
1505
- }
1506
-
1507
- /**
1508
- * Render option divider. Decorative field. Do not need to be registered to WP Settings API.
1509
- *
1510
- * @since 3.0.0
1511
- * @access public
1512
- *
1513
- * @param array $option Array of options data. May vary depending on option type.
1514
- */
1515
- public function render_option_divider_option_field( $option ) {
1516
-
1517
- ?>
1518
-
1519
- <tr valign="top" class="<?php echo esc_attr( $option[ 'id' ] ) . '-row'; ?>">
1520
- <th scope="row" colspan="2">
1521
- <h3><?php echo sanitize_text_field( $option[ 'title' ] ); ?></h3>
1522
- <?php echo isset( $option[ 'markup' ] ) ? $option[ 'markup' ] : ''; ?>
1523
- </th>
1524
- </tr>
1525
-
1526
- <?php
1527
-
1528
- }
1529
-
1530
- /**
1531
- * Render custom "migration_controls" field. Do not need to be registered to WP Settings API.
1532
- *
1533
- * @since 3.0.0
1534
- * @access public
1535
- *
1536
- * @param array $option Array of options data. May vary depending on option type.
1537
- */
1538
- public function render_migration_controls_option_field( $option ) {
1539
-
1540
- $database_processing = apply_filters( 'ta_database_processing' , true ); // Flag to determine if another application is processing the db. ex. data downgrade.
1541
- $processing = "";
1542
- $disabled = false;
1543
-
1544
- if ( get_option( Plugin_Constants::MIGRATION_COMPLETE_FLAG ) === 'no' ) {
1545
-
1546
- $processing = "-processing";
1547
- $disabled = true;
1548
-
1549
- } ?>
1550
-
1551
- <tr valign="top" class="<?php echo esc_attr( $option[ 'id' ] ) . '-row'; ?>">
1552
-
1553
- <th scope="row" class="title_desc"><?php echo sanitize_text_field( $option[ 'title' ] ); ?></th>
1554
-
1555
- <td class="forminp forminp-<?php echo sanitize_title( $option[ 'type' ] ) ?> <?php echo $processing; ?>">
1556
-
1557
- <?php if ( !$database_processing ) { ?>
1558
-
1559
- <p><?php _e( 'Another application is currently processing the database. Please wait for this to complete.' , 'thirstyaffiliates' ); ?></p>
1560
-
1561
- <?php } else { ?>
1562
-
1563
- <input
1564
- <?php echo $disabled ? "disabled" : ""; ?>
1565
- type="button"
1566
- id="<?php echo esc_attr( $option[ 'id' ] ); ?>"
1567
- class="button button-primary"
1568
- style="<?php echo isset( $option[ 'style' ] ) ? esc_attr( $option[ 'style' ] ) : ''; ?>"
1569
- value="<?php _e( 'Migrate' , 'thirstyaffiliates' ); ?>">
1570
-
1571
- <span class="spinner"></span>
1572
- <p class="status"><?php _e( 'Migrating data. Please wait...' , 'thirstyaffiliates' ); ?></p>
1573
-
1574
- <?php } ?>
1575
-
1576
- <br /><br />
1577
- <p class="desc"><?php echo isset( $option[ 'desc' ] ) ? $option[ 'desc' ] : ''; ?></p>
1578
-
1579
- </td>
1580
-
1581
- </tr>
1582
-
1583
- <?php
1584
-
1585
- }
1586
-
1587
- /**
1588
- * Render custom "social_links" field. Do not need to be registered to WP Settings API.
1589
- *
1590
- * @since 3.1.0
1591
- * @access public
1592
- *
1593
- * @param array $option Array of options data. May vary depending on option type.
1594
- */
1595
- public function render_social_links_option_field( $option ) {
1596
-
1597
- ?>
1598
- <tr valign="top" class="<?php echo esc_attr( $option[ 'id' ] ) . '-row'; ?>">
1599
- <th scope="row"><?php echo sanitize_text_field( $option[ 'title' ] ); ?></th>
1600
- <td>
1601
- <ul>
1602
- <li>
1603
- <a href="https://www.facebook.com/thirstyaffiliates/"><?php _e( 'Like us on Facebook' , 'thirstyaffiliates' ); ?></a>
1604
- <iframe src="//www.facebook.com/plugins/like.php?href=https%3A%2F%2Fwww.facebook.com%2Fthirstyaffiliates&amp;send=false&amp;layout=button_count&amp;width=450&amp;show_faces=false&amp;font=arial&amp;colorscheme=light&amp;action=like&amp;height=21" scrolling="no" frameborder="0" style="border:none; overflow:hidden; width:450px; height:21px; vertical-align: bottom;" allowTransparency="true"></iframe>
1605
- </li>
1606
- <li>
1607
- <a href="http://twitter.com/thirstyaff"><?php _e( 'Follow us on Twitter' , 'thirstyaffiliates' ); ?></a>
1608
- <a href="https://twitter.com/thirstyaff" class="twitter-follow-button" data-show-count="true" style="vertical-align: bottom;">Follow @thirstyaff</a><script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?"http":"https";if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+"://platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document, "script", "twitter-wjs");</script>
1609
- </li>
1610
- <li>
1611
- <a href="https://www.linkedin.com/company-beta/2928598/"><?php _e( 'Follow us on Linkedin' , 'thirstyaffiliates' ); ?></a>
1612
- </li>
1613
- <li>
1614
- <a href="https://thirstyaffiliates.com/affiliates?utm_source=Free%20Plugin&utm_medium=Help&utm_campaign=Affiliates%20Link" target="_blank"><?php _e( 'Join Our Affiliate Program' , 'thirstyaffiliates' ); ?></a>
1615
- <?php _e( '(up to 30% commisions)' , 'thirstyaffiliates' ); ?>
1616
- </li>
1617
- </ul>
1618
- </td>
1619
- </tr>
1620
- <?php
1621
- }
1622
-
1623
- /**
1624
- * Render custom "export_global_settings" field. Do not need to be registered to WP Settings API.
1625
- *
1626
- * @since 3.0.0
1627
- * @access public
1628
- *
1629
- * @param array $option Array of options data. May vary depending on option type.
1630
- */
1631
- public function render_export_global_settings_option_field( $option ) {
1632
-
1633
- $global_settings_string = $this->get_global_settings_string(); ?>
1634
-
1635
- <tr valign="top" class="<?php echo esc_attr( $option[ 'id' ] ) . '-row'; ?>">
1636
- <th scope="row" class="title_desc"><?php echo sanitize_text_field( $option[ 'title' ] ); ?></th>
1637
- <td class="forminp forminp-<?php echo sanitize_title( $option[ 'type' ] ) ?>">
1638
- <textarea
1639
- name="<?php echo esc_attr( $option[ 'id' ] ); ?>"
1640
- id="<?php echo esc_attr( $option[ 'id' ] ); ?>"
1641
- style="<?php echo isset( $option[ 'style' ] ) ? esc_attr( $option[ 'style' ] ) : ''; ?>"
1642
- class="<?php echo isset( $option[ 'class' ] ) ? esc_attr( $option[ 'class' ] ) : ''; ?>"
1643
- placeholder="<?php echo isset( $option[ 'placeholder' ] ) ? esc_attr( $option[ 'placeholder' ] ) : ''; ?>"
1644
- autocomplete="off"
1645
- readonly
1646
- rows="10"><?php echo $global_settings_string; ?></textarea>
1647
- <div class="controls">
1648
- <a id="copy-settings-string" data-clipboard-target="#<?php echo esc_attr( $option[ 'id' ] ); ?>"><?php _e( 'Copy' , 'thirstyaffiliates' ); ?></a>
1649
- </div>
1650
- </td>
1651
- </tr>
1652
-
1653
- <?php
1654
-
1655
- }
1656
-
1657
- /**
1658
- * Render custom "import_global_settings" field. Do not need to be registered to WP Settings API.
1659
- *
1660
- * @since 3.0.0
1661
- * @access public
1662
- *
1663
- * @param array $option Array of options data. May vary depending on option type.
1664
- */
1665
- public function render_import_global_settings_option_field( $option ) {
1666
-
1667
- ?>
1668
-
1669
- <tr valign="top" class="<?php echo esc_attr( $option[ 'id' ] ) . '-row'; ?>">
1670
- <th scope="row" class="title_desc">
1671
- <label for="<?php echo esc_attr( $option[ 'id' ] ); ?>"><?php echo sanitize_text_field( $option[ 'title' ] ); ?></label>
1672
- </th>
1673
- <td class="forminp forminp-<?php echo sanitize_title( $option[ 'type' ] ) ?>">
1674
- <textarea
1675
- name="<?php echo esc_attr( $option[ 'id' ] ); ?>"
1676
- id="<?php echo esc_attr( $option[ 'id' ] ); ?>"
1677
- style="<?php echo isset( $option[ 'style' ] ) ? esc_attr( $option[ 'style' ] ) : ''; ?>"
1678
- class="<?php echo isset( $option[ 'class' ] ) ? esc_attr( $option[ 'class' ] ) : ''; ?>"
1679
- placeholder="<?php echo esc_attr( $option[ 'placeholder' ] ); ?>"
1680
- autocomplete="off"
1681
- rows="10"></textarea>
1682
- <p class="desc"><?php echo isset( $option[ 'description' ] ) ? $option[ 'description' ] : ''; ?></p>
1683
- <div class="controls">
1684
- <span class="spinner"></span>
1685
- <input type="button" id="import-setting-button" class="button button-primary" value="<?php _e( 'Import Settings' , 'thirstyaffiliates' ); ?>">
1686
- </di>
1687
- </td>
1688
- </tr>
1689
-
1690
- <?php
1691
-
1692
- }
1693
-
1694
-
1695
-
1696
-
1697
- /*
1698
- |--------------------------------------------------------------------------
1699
- | "key_value" option field type helpers
1700
- |--------------------------------------------------------------------------
1701
- */
1702
-
1703
- /**
1704
- * Load styling relating to 'key_value' field type.
1705
- *
1706
- * @since 3.0.0
1707
- * @access public
1708
- */
1709
- public function load_key_value_option_field_type_styling() {
1710
-
1711
- ?>
1712
-
1713
- <style>
1714
- .key-value-fields-container header span {
1715
- display: inline-block;
1716
- font-weight: 600;
1717
- margin-bottom: 8px;
1718
- }
1719
- .key-value-fields-container header .key {
1720
- width: 144px;
1721
- }
1722
- .key-value-fields-container header .value {
1723
- width: 214px;
1724
- }
1725
- .key-value-fields-container .fields .data-set {
1726
- margin-bottom: 8px;
1727
- }
1728
- .key-value-fields-container .fields .data-set:last-child {
1729
- margin-bottom: 0;
1730
- }
1731
- .key-value-fields-container .fields .data-set .key-field {
1732
- width: 140px;
1733
- margin-left: 0;
1734
- }
1735
- .key-value-fields-container .fields .data-set .value-field {
1736
- width: 215px;
1737
- }
1738
- .key-value-fields-container .fields .data-set .controls {
1739
- display: none;
1740
- }
1741
- .key-value-fields-container .fields .data-set:hover .controls {
1742
- display: inline-block;
1743
- }
1744
- .key-value-fields-container .fields .data-set .controls .control {
1745
- cursor: pointer;
1746
- }
1747
- .key-value-fields-container .fields .data-set .controls .add {
1748
- color: green;
1749
- }
1750
- .key-value-fields-container .fields .data-set .controls .delete {
1751
- color: red;
1752
- }
1753
- </style>
1754
-
1755
- <?php
1756
-
1757
- }
1758
-
1759
- /**
1760
- * Load scripts relating to 'key_value' field type.
1761
- *
1762
- * @since 3.0.0
1763
- * @access public
1764
- */
1765
- public function load_key_value_option_field_type_script() {
1766
-
1767
- ?>
1768
-
1769
- <script>
1770
-
1771
- jQuery( document ).ready( function( $ ) {
1772
-
1773
- // Hide the delete button if only 1 data set is available
1774
- function init_data_set_controls() {
1775
-
1776
- $( ".key-value-fields-container" ).each( function() {
1777
-
1778
- if ( $( this ).find( ".data-set" ).length === 1 )
1779
- $( this ).find( ".data-set .controls .delete" ).css( "display" , "none" );
1780
- else
1781
- $( this ).find( ".data-set .controls .delete" ).removeAttr( "style" );
1782
-
1783
- } );
1784
-
1785
- }
1786
-
1787
- init_data_set_controls();
1788
-
1789
-
1790
- // Attach "add" and "delete" events
1791
- $( ".key-value-fields-container" ).on( "click" , ".controls .add" , function() {
1792
-
1793
- let $data_set = $( this ).closest( '.data-set' );
1794
-
1795
- $data_set.after( "<div class='data-set'>" +
1796
- "<input type='text' class='field key-field' autocomplete='off'> " +
1797
- "<input type='text' class='field value-field' autocomplete='off'>" +
1798
- "<div class='controls'>" +
1799
- "<span class='control add dashicons dashicons-plus-alt'></span>" +
1800
- "<span class='control delete dashicons dashicons-dismiss'></span>" +
1801
- "</div>" +
1802
- "</div>" );
1803
-
1804
- init_data_set_controls();
1805
-
1806
- } );
1807
-
1808
- $( ".key-value-fields-container" ).on( "click" , ".controls .delete" , function() {
1809
-
1810
- let $data_set = $( this ).closest( '.data-set' );
1811
-
1812
- $data_set.remove();
1813
-
1814
- init_data_set_controls();
1815
-
1816
- } );
1817
-
1818
-
1819
- // Construct hidden fields for each of "key_value" option field types upon form submission
1820
- $( "form" ).submit( function() {
1821
-
1822
- $( ".key-value-fields-container" ).each( function() {
1823
-
1824
- var $this = $( this ),
1825
- field_id = $this.attr( "data-field-id" ),
1826
- field_inputs = "";
1827
-
1828
- $this.find( ".data-set" ).each( function() {
1829
-
1830
- var $this = $( this ),
1831
- key_field = $.trim( $this.find( ".key-field" ).val() ),
1832
- value_field = $.trim( $this.find( ".value-field" ).val() );
1833
-
1834
- if ( key_field !== "" && value_field !== "" )
1835
- field_inputs += "<input type='hidden' name='" + field_id + "[" + key_field + "]' value='" + value_field + "'>";
1836
-
1837
- } );
1838
-
1839
- $this.append( field_inputs );
1840
-
1841
- } );
1842
-
1843
- } );
1844
-
1845
- } );
1846
-
1847
- </script>
1848
-
1849
- <?php
1850
-
1851
- }
1852
-
1853
-
1854
-
1855
-
1856
- /*
1857
- |--------------------------------------------------------------------------
1858
- | Settings helper
1859
- |--------------------------------------------------------------------------
1860
- */
1861
-
1862
- /**
1863
- * Get global settings string via ajax.
1864
- *
1865
- * @since 3.0.0
1866
- * @access public
1867
- */
1868
- public function ajax_get_global_settings_string() {
1869
-
1870
- if ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX )
1871
- $response = array( 'status' => 'fail' , 'error_msg' => __( 'Invalid AJAX call' , 'thirstyaffiliates' ) );
1872
- else {
1873
-
1874
- $global_settings_string = $this->get_global_settings_string();
1875
-
1876
- if ( is_wp_error( $global_settings_string ) )
1877
- $response = array( 'status' => 'fail' , 'error_msg' => $global_settings_string->get_error_message() );
1878
- else
1879
- $response = array( 'status' => 'success' , 'global_settings_string' => $global_settings_string );
1880
-
1881
- }
1882
-
1883
- @header( 'Content-Type: application/json; charset=' . get_option( 'blog_charset' ) );
1884
- echo wp_json_encode( $response );
1885
- wp_die();
1886
-
1887
- }
1888
-
1889
- /**
1890
- * Get global settings string.
1891
- *
1892
- * @since 3.0.0
1893
- * @access public
1894
- *
1895
- * @return WP_Error|string WP_Error on error, Base 64 encoded serialized global plugin settings otherwise.
1896
- */
1897
- public function get_global_settings_string() {
1898
-
1899
- if ( !$this->_helper_functions->current_user_authorized() )
1900
- return new \WP_Error( 'ta_unauthorized_operation_export_settings' , __( 'Unauthorized operation. Only authorized accounts can access global plugin settings string' , 'thirstyaffiliates' ) );
1901
-
1902
- $global_settings_arr = array();
1903
- foreach ( $this->_exportable_options as $key => $default )
1904
- $global_settings_arr[ $key ] = get_option( $key , $default );
1905
-
1906
- return base64_encode( serialize( $global_settings_arr ) );
1907
-
1908
- }
1909
-
1910
- /**
1911
- * Import settings via ajax.
1912
- *
1913
- * @access public
1914
- * @since 3.0.0
1915
- */
1916
- public function ajax_import_settings() {
1917
-
1918
- if ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX )
1919
- $response = array( 'status' => 'fail' , 'error_msg' => __( 'Invalid AJAX call' , 'thirstyaffiliates' ) );
1920
- elseif ( !isset( $_POST[ 'ta_settings_string' ] ) )
1921
- $response = array( 'status' => 'fail' , 'error_msg' => __( 'Required parameter not passed' , 'thirstyaffiliates' ) );
1922
- else {
1923
-
1924
- $result = $this->import_settings( filter_var( $_POST[ 'ta_settings_string' ] , FILTER_SANITIZE_STRING ) );
1925
-
1926
- if ( is_wp_error( $result ) )
1927
- $response = array( 'status' => 'fail' , 'error_msg' => $result->get_error_message() );
1928
- else
1929
- $response = array( 'status' => 'success' , 'success_msg' => __( 'Settings successfully imported' , 'thirstyaffiliates' ) );
1930
-
1931
- }
1932
-
1933
- @header( 'Content-Type: application/json; charset=' . get_option( 'blog_charset' ) );
1934
- echo wp_json_encode( $response );
1935
- wp_die();
1936
-
1937
- }
1938
-
1939
- /**
1940
- * Import settings from external global settings string.
1941
- *
1942
- * @since 3.0.0
1943
- * @access public
1944
- *
1945
- * @param string $global_settings_string Settings string.
1946
- * @return WP_Error | boolean WP_Error instance on failure, boolean true otherwise.
1947
- */
1948
- public function import_settings( $global_settings_string ) {
1949
-
1950
- if ( !$this->_helper_functions->current_user_authorized() )
1951
- return new \WP_Error( 'ta_unauthorized_operation_import_settings' , __( 'Unauthorized operation. Only authorized accounts can import settings' , 'thirstyaffiliates' ) );
1952
-
1953
- $settings_arr = @unserialize( base64_decode( $global_settings_string ) );
1954
-
1955
- if ( !is_array( $settings_arr ) )
1956
- return new \WP_Error( 'ta_invalid_global_settings_string' , __( 'Invalid global settings string' , 'thirstyaffiliates' ) , array( 'global_settings_string' => $global_settings_string ) );
1957
- else {
1958
-
1959
- foreach ( $settings_arr as $key => $val ) {
1960
-
1961
- if ( !array_key_exists( $key , $this->_exportable_options ) )
1962
- continue;
1963
-
1964
- update_option( $key , $val );
1965
-
1966
- }
1967
-
1968
- return true;
1969
-
1970
- }
1971
-
1972
- }
1973
-
1974
- /**
1975
- * Post update option callback for link prefix options.
1976
- *
1977
- * @since 3.0.0
1978
- * @access public
1979
- *
1980
- * @param string $old_value Old option value before the update.
1981
- * @param string $value New value saved.
1982
- * @param string $option Option id.
1983
- */
1984
- public function link_prefix_post_update_callback( $value , $old_value , $option ) {
1985
-
1986
- if ( $option === 'ta_link_prefix' && $value === 'custom' )
1987
- return $value;
1988
-
1989
- if ( $option === 'ta_link_prefix_custom' && get_option( 'ta_link_prefix' ) !== 'custom' )
1990
- return $value;
1991
-
1992
- $used_link_prefixes = maybe_unserialize( get_option( 'ta_used_link_prefixes' , array() ) );
1993
- $check_duplicate = array_search( $value , $used_link_prefixes );
1994
-
1995
- if ( $check_duplicate !== false )
1996
- unset( $used_link_prefixes[ $check_duplicate ] );
1997
-
1998
- $used_link_prefixes[] = sanitize_text_field( $value );
1999
- $count = count( $used_link_prefixes );
2000
-
2001
- if ( $count > 10 )
2002
- $used_link_prefixes = array_slice( $used_link_prefixes , $count - 10 , 10 , false );
2003
-
2004
- update_option( 'ta_used_link_prefixes' , array_unique( $used_link_prefixes ) );
2005
-
2006
- return $value;
2007
- }
2008
-
2009
- /**
2010
- * Restrict modules settings.
2011
- *
2012
- * @since 3.6
2013
- * @access public
2014
- */
2015
- public function restrict_module_settings() {
2016
-
2017
- $screen = get_current_screen();
2018
- $tab = isset( $_GET[ 'tab' ] ) ? sanitize_text_field( $_GET[ 'tab' ] ) : null;
2019
-
2020
- if ( $screen->id !== 'thirstylink_page_thirsty-settings' || ! $tab ) return;
2021
-
2022
- $default_values = array();
2023
- $registered = apply_filters( 'ta_modules_settings_options' , array(
2024
- array( 'id' => 'ta_enable_stats_reporting_module' , 'default' => 'yes' ),
2025
- array( 'id' => 'ta_enable_link_fixer' , 'default' => 'yes' ),
2026
- array( 'id' => 'ta_uncloak_link_per_link' , 'default' => 'no' )
2027
- ) );
2028
-
2029
- if ( function_exists( 'array_column' ) )
2030
- $default_values = array_column( $registered , 'default' , 'id' );
2031
- else {
2032
-
2033
- foreach ( $registered as $module )
2034
- $default_values[ $module[ 'id' ] ] = isset( $module[ 'default' ] ) ? $module[ 'default' ] : 'yes';
2035
- }
2036
-
2037
- $modules = array_keys( $default_values );
2038
- $current_module = str_replace( array( 'tap_' , 'amazon_settings_section' , '_settings' ) , array( 'tap_enable_' , 'azon' , '' ) , $tab );
2039
- $current_module = apply_filters( 'ta_restrict_current_module' , $current_module , $tab , $modules );
2040
- $default = in_array( $current_module , $modules ) ? $default_values[ $current_module ] : null;
2041
-
2042
- if ( ! is_null( $default ) && get_option( $current_module , $default ) !== 'yes' )
2043
- wp_die( __( "Sorry, you are not allowed to access this page." , 'thirstyaffiliates' ) );
2044
-
2045
- }
2046
-
2047
-
2048
-
2049
-
2050
- /*
2051
- |--------------------------------------------------------------------------
2052
- | Implemented Interface Methods
2053
- |--------------------------------------------------------------------------
2054
- */
2055
-
2056
- /**
2057
- * Execute codes that needs to run plugin activation.
2058
- *
2059
- * @since 3.0.0
2060
- * @access public
2061
- * @implements ThirstyAffiliates\Interfaces\Activatable_Interface
2062
- */
2063
- public function activate() {
2064
-
2065
- if ( get_option( 'ta_settings_initialized' ) !== 'yes' ) {
2066
-
2067
- update_option( 'ta_link_prefix' , 'recommends' );
2068
- update_option( 'ta_link_prefix_custom' , '' );
2069
- update_option( 'ta_used_link_prefixes' , array( 'recommends' ) );
2070
- update_option( 'ta_enable_javascript_frontend_redirect' , 'yes' );
2071
- update_option( 'ta_show_enable_js_redirect_notice' , 'no' );
2072
- update_option( 'ta_dismiss_marketing_notice_option' , 'no' );
2073
- update_option( 'ta_settings_initialized' , 'yes' );
2074
- }
2075
- }
2076
-
2077
- /**
2078
- * Execute codes that needs to run on plugin initialization.
2079
- *
2080
- * @since 3.0.0
2081
- * @access public
2082
- * @implements ThirstyAffiliates\Interfaces\Initiable_Interface
2083
- */
2084
- public function initialize() {
2085
-
2086
- add_action( 'wp_ajax_ta_get_global_settings_string' , array( $this , 'ajax_get_global_settings_string' ) );
2087
- add_action( 'wp_ajax_ta_import_settings' , array( $this , 'ajax_import_settings' ) );
2088
-
2089
- }
2090
-
2091
- /**
2092
- * Execute model.
2093
- *
2094
- * @implements WordPress_Plugin_Boilerplate\Interfaces\Model_Interface
2095
- *
2096
- * @since 3.0.0
2097
- * @access public
2098
- */
2099
- public function run() {
2100
-
2101
- add_action( 'admin_init' , array( $this , 'init_plugin_settings' ) );
2102
- add_action( 'admin_menu' , array( $this , 'add_settings_page' ) );
2103
- add_action( 'current_screen' , array( $this , 'restrict_module_settings' ) );
2104
-
2105
- add_action( 'ta_before_settings_form' , array( $this , 'load_key_value_option_field_type_styling' ) );
2106
- add_action( 'ta_before_settings_form' , array( $this , 'load_key_value_option_field_type_script' ) );
2107
- add_action( 'pre_update_option_ta_link_prefix' , array( $this , 'link_prefix_post_update_callback' ) , 10 , 3 );
2108
- add_action( 'pre_update_option_ta_link_prefix_custom' , array( $this , 'link_prefix_post_update_callback' ) , 10 , 3 );
2109
-
2110
- add_filter( 'ta_admin_interfaces' , array( $this , 'register_admin_interfaces' ) );
2111
- add_filter( 'ta_menu_items' , array( $this , 'register_admin_menu_items' ) );
2112
- }
2113
-
2114
- }
1
+ <?php
2
+ namespace ThirstyAffiliates\Models;
3
+
4
+ use ThirstyAffiliates\Abstracts\Abstract_Main_Plugin_Class;
5
+
6
+ use ThirstyAffiliates\Interfaces\Model_Interface;
7
+ use ThirstyAffiliates\Interfaces\Activatable_Interface;
8
+ use ThirstyAffiliates\Interfaces\Initiable_Interface;
9
+
10
+ use ThirstyAffiliates\Helpers\Plugin_Constants;
11
+ use ThirstyAffiliates\Helpers\Helper_Functions;
12
+
13
+ if ( !defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
14
+
15
+ /**
16
+ * Model that houses the logic of plugin Settings.
17
+ * General Information:
18
+ * The Ultimate Settings API ( Of course there will always be room for improvements ).
19
+ * Basically we are using parts of the WordPress Settings API ( Only the backend processes )
20
+ * But we are using our own custom render codebase.
21
+ * The issue with WordPress Settings API is that we need to supply callbacks for each option field we add, so its not really extensible.
22
+ * The data supplied on those callbacks are not ideal or not complete too to make a very extensible Settings API.
23
+ * So what we did is, Register the settings and settings options in a way that we can utilize WordPress Settings API to handle them on the backend.
24
+ * But use our own render codebase so we can make the Settings API very easy to extend.
25
+ *
26
+ * Important Note:
27
+ * Be careful with default values. Default values only take effect if you haven't set the option yet. Meaning the option is not yet registered yet to the options db. ( get_option ).
28
+ * Now if you hit save on a settings section with a field that have a default value, and you haven't changed that default value, Alas! it will still not register that option to the options db.
29
+ * The reason is the default value and the current value of the options is the same.
30
+ * Bug if you modify the value of the option, and hit save, this time, that option will be registered to the options db.
31
+ * Then if you set back the value of that option, same as its default, it will still updated that option that is registered to the options db with that value.
32
+ * Again remember, default value only kicks in if the option you are trying to get via get_option function is not yet registered to the options db.
33
+ *
34
+ * Important Note:
35
+ * If the option can contain multiple values ( in array form , ex. checkbox and multiselect option types ), the default value must be in array form even if it only contains one value.
36
+ *
37
+ * Private Model.
38
+ *
39
+ * @since 3.0.0
40
+ */
41
+ class Settings implements Model_Interface , Activatable_Interface , Initiable_Interface {
42
+
43
+ /*
44
+ |--------------------------------------------------------------------------
45
+ | Class Properties
46
+ |--------------------------------------------------------------------------
47
+ */
48
+
49
+ /**
50
+ * Property that holds the single main instance of Settings.
51
+ *
52
+ * @since 3.0.0
53
+ * @access private
54
+ * @var Settings
55
+ */
56
+ private static $_instance;
57
+
58
+ /**
59
+ * Model that houses all the plugin constants.
60
+ *
61
+ * @since 3.0.0
62
+ * @access private
63
+ * @var Plugin_Constants
64
+ */
65
+ private $_constants;
66
+
67
+ /**
68
+ * Property that houses all the helper functions of the plugin.
69
+ *
70
+ * @since 3.0.0
71
+ * @access private
72
+ * @var Helper_Functions
73
+ */
74
+ private $_helper_functions;
75
+
76
+ /**
77
+ * Property that houses all the supported option field types.
78
+ *
79
+ * @since 3.0.0
80
+ * @access private
81
+ * @var array
82
+ */
83
+ private $_supported_field_types;
84
+
85
+ /**
86
+ * Property that houses all the supported option field types that do not needed to be registered to the WP Settings API.
87
+ * Ex. of this are field types that are for decorative purposes only and has no underlying option data to save.
88
+ * Another is type of option fields that perform specialized task and does not need any underlying data to be saved.
89
+ *
90
+ * @since 3.0.0
91
+ * @access public
92
+ */
93
+ private $_skip_wp_settings_registration;
94
+
95
+ /**
96
+ * Property that houses all the registered settings sections.
97
+ *
98
+ *
99
+ * @since 3.0.0
100
+ * @access private
101
+ * @var array
102
+ */
103
+ private $_settings_sections;
104
+
105
+ /**
106
+ * Property that houses all the registered options of the registered settings sections.
107
+ *
108
+ * @since 3.0.0
109
+ * @access private
110
+ * @var array
111
+ */
112
+ private $_settings_section_options;
113
+
114
+ /**
115
+ * Property that holds all plugin options that can be exported.
116
+ *
117
+ * @since 3.0.0
118
+ * @access private
119
+ * @var array
120
+ */
121
+ private $_exportable_options;
122
+
123
+ /**
124
+ * Property that holds list of post update function callbacks per option if there are any.
125
+ *
126
+ * @since 3.0.0
127
+ * @access private
128
+ * @var array
129
+ */
130
+ private $_post_update_option_cbs = array();
131
+
132
+
133
+
134
+
135
+ /*
136
+ |--------------------------------------------------------------------------
137
+ | Class Methods
138
+ |--------------------------------------------------------------------------
139
+ */
140
+
141
+ /**
142
+ * Class constructor.
143
+ *
144
+ * @since 3.0.0
145
+ * @access public
146
+ *
147
+ * @param Abstract_Main_Plugin_Class $main_plugin Main plugin object.
148
+ * @param Plugin_Constants $constants Plugin constants object.
149
+ * @param Helper_Functions $helper_functions Helper functions object.
150
+ */
151
+ public function __construct( Abstract_Main_Plugin_Class $main_plugin , Plugin_Constants $constants , Helper_Functions $helper_functions ) {
152
+
153
+ $this->_constants = $constants;
154
+ $this->_helper_functions = $helper_functions;
155
+
156
+ $main_plugin->add_to_all_plugin_models( $this );
157
+
158
+ }
159
+
160
+ /**
161
+ * Ensure that only one instance of this class is loaded or can be loaded ( Singleton Pattern ).
162
+ *
163
+ * @since 3.0.0
164
+ * @access public
165
+ *
166
+ * @param Abstract_Main_Plugin_Class $main_plugin Main plugin object.
167
+ * @param Plugin_Constants $constants Plugin constants object.
168
+ * @param Helper_Functions $helper_functions Helper functions object.
169
+ * @return Settings
170
+ */
171
+ public static function get_instance( Abstract_Main_Plugin_Class $main_plugin , Plugin_Constants $constants , Helper_Functions $helper_functions ) {
172
+
173
+ if ( !self::$_instance instanceof self )
174
+ self::$_instance = new self( $main_plugin , $constants , $helper_functions );
175
+
176
+ return self::$_instance;
177
+
178
+ }
179
+
180
+ /**
181
+ * Register admin interfaces.
182
+ *
183
+ * @since 3.3.2
184
+ * @access public
185
+ *
186
+ * @param array $interfaces List of admin interfaces.
187
+ * @return array Filtered list of admin interfaces.
188
+ */
189
+ public function register_admin_interfaces( $interfaces ) {
190
+
191
+ $interfaces[ 'thirstylink_page_thirsty-settings' ] = apply_filters( 'ta_settings_admin_interface' , array(
192
+ 'ta_general_settings' => 'manage_options',
193
+ 'ta_links_settings' => 'manage_options',
194
+ 'ta_modules_settings' => 'manage_options',
195
+ 'ta_help_settings' => 'manage_options',
196
+ ) );
197
+
198
+ $interfaces[ 'thirstylink_page_thirsty-settings' ][ 'ta_import_export_settings' ] = 'manage_options';
199
+
200
+ return $interfaces;
201
+ }
202
+
203
+ /**
204
+ * Register admin interfaces.
205
+ *
206
+ * @since 3.3.2
207
+ * @access public
208
+ *
209
+ * @param array $interfaces List of menu items.
210
+ * @return array Filtered list of menu items.
211
+ */
212
+ public function register_admin_menu_items( $menu_items ) {
213
+
214
+ $menu_items[ 'thirsty-settings' ] = 'manage_options';
215
+ return $menu_items;
216
+ }
217
+
218
+
219
+
220
+
221
+ /*
222
+ |--------------------------------------------------------------------------
223
+ | Initialize Settings
224
+ |--------------------------------------------------------------------------
225
+ */
226
+
227
+ /**
228
+ * Initialize the list of plugin built-in settings sections and its corresponding options.
229
+ *
230
+ * @since 3.0.0
231
+ * @since 3.3.1 Add support for URL input type.
232
+ * @since 3.3.5 Hide enhanced javascript redirect setting when statistics module is disabled.
233
+ * @access public
234
+ */
235
+ public function init_settings_sections_and_options() {
236
+
237
+ $this->_supported_field_types = apply_filters( 'ta_supported_field_types' , array(
238
+ 'text' => array( $this , 'render_text_option_field' ),
239
+ 'url' => array( $this , 'render_url_option_field' ),
240
+ 'number' => array( $this , 'render_number_option_field' ),
241
+ 'textarea' => array( $this , 'render_textarea_option_field' ),
242
+ 'checkbox' => array( $this , 'render_checkbox_option_field' ),
243
+ 'radio' => array( $this , 'render_radio_option_field' ),
244
+ 'select' => array( $this , 'render_select_option_field' ),
245
+ 'multiselect' => array( $this , 'render_multiselect_option_field' ),
246
+ 'toggle' => array( $this , 'render_toggle_option_field' ),
247
+ 'editor' => array( $this , 'render_editor_option_field' ),
248
+ 'csv' => array( $this , 'render_csv_option_field' ),
249
+ 'key_value' => array( $this , 'render_key_value_option_field' ),
250
+ 'link' => array( $this , 'render_link_option_field' ),
251
+ 'option_divider' => array( $this , 'render_option_divider_option_field' ),
252
+ 'migration_controls' => array( $this , 'render_migration_controls_option_field' ),
253
+ 'social_links' => array( $this , 'render_social_links_option_field' ),
254
+ 'export_global_settings' => array( $this , 'render_export_global_settings_option_field' ),
255
+ 'import_global_settings' => array( $this , 'render_import_global_settings_option_field' )
256
+ ) );
257
+
258
+ $this->_skip_wp_settings_registration = apply_filters( 'ta_skip_wp_settings_registration' , array(
259
+ 'link',
260
+ 'option_divider',
261
+ 'migration_controls',
262
+ 'export_global_settings',
263
+ 'import_global_settings'
264
+ ) );
265
+
266
+ // Toggle options for settings with 'category' as an option.
267
+ $toggle_cat_options = array(
268
+ 'yes' => __( 'Yes' , 'thirstyaffiliates' ),
269
+ 'no' => __( 'No' , 'thirstyaffiliates' ),
270
+ 'category' => __( 'Per category' , 'thirstyaffiliates' )
271
+ );
272
+ $all_categories = $this->_helper_functions->get_all_category_as_options();
273
+
274
+ $this->_settings_sections = apply_filters( 'ta_settings_option_sections' , array(
275
+ 'ta_general_settings' => array(
276
+ 'title' => __( 'General' , 'thirstyaffiliates' ) ,
277
+ 'desc' => __( 'Settings that change the general behaviour of ThirstyAffiliates.' , 'thirstyaffiliates' )
278
+ ),
279
+ 'ta_links_settings' => array(
280
+ 'title' => __( 'Link Appearance' , 'thirstyaffiliates' ) ,
281
+ 'desc' => __( 'Settings that specifically affect the behaviour & appearance of your affiliate links.' , 'thirstyaffiliates' )
282
+ ),
283
+ 'ta_modules_settings' => array(
284
+ 'title' => __( 'Modules' , 'thirstyaffiliates' ),
285
+ 'desc' => __( 'This section allows you to turn certain parts of ThirstyAffiliates on or off. Below are the individual modules and features of the plugin that can be controlled.' , 'thirstyaffiliates' )
286
+ ),
287
+ 'ta_import_export_settings' => array(
288
+ 'title' => __( 'Import/Export' , 'thirstyaffiliates' ),
289
+ 'desc' => __( 'Import and Export global ThirstyAffiliates plugin settings from one site to another.' , 'thirstyaffiliates' )
290
+ ),
291
+ 'ta_help_settings' => array(
292
+ 'title' => __( 'Help' , 'thirstyaffiliates' ),
293
+ 'desc' => __( 'Links to knowledge base and other utilities.' , 'thirstyaffiliates' )
294
+ )
295
+ ) );
296
+
297
+ $this->_settings_section_options = apply_filters( 'ta_settings_section_options' , array(
298
+ 'ta_general_settings' => apply_filters( 'ta_general_settings_options' , array(
299
+
300
+ array(
301
+ 'id' => 'ta_link_insertion_type',
302
+ 'title' => __( 'Default Link Insertion Type' , 'thirstyaffiliates' ),
303
+ 'desc' => __( "Determines the default link type when inserting a link using the quick search." , 'thirstyaffiliates' ),
304
+ 'type' => 'select',
305
+ 'default' => 'link',
306
+ 'options' => array(
307
+ 'link' => __( 'Link' , 'thirstyaffiliates' ),
308
+ 'shortcode' => __( 'Shortcode' , 'thirstyaffiliates' ),
309
+ )
310
+ ),
311
+
312
+ array(
313
+ 'id' => 'ta_disable_cat_auto_select',
314
+ 'title' => __( 'Disable "uncategorized" category on save?' , 'thirstyaffiliates' ),
315
+ 'desc' => __( 'If links are including categories in the URL then by default ThirstyAffiliates will add an "uncategorized" category to apply to non-categorised links during save. If you disable this, it allows you to have some links with categories in the URL and some without.' , 'thirstyaffiliates' ),
316
+ 'type' => 'toggle'
317
+ ),
318
+
319
+ array(
320
+ 'id' => 'ta_disable_visual_editor_buttons',
321
+ 'title' => __( 'Disable buttons on the Visual editor?' , 'thirstyaffiliates' ),
322
+ 'desc' => __( "Hide the ThirstyAffiliates buttons on the Visual editor." , 'thirstyaffiliates' ),
323
+ 'type' => 'toggle'
324
+ ),
325
+
326
+ array(
327
+ 'id' => 'ta_disable_text_editor_buttons',
328
+ 'title' => __( 'Disable buttons on the Text/Quicktags editor?' , 'thirstyaffiliates' ),
329
+ 'desc' => __( "Hide the ThirstyAffiliates buttons on the Text editor." , 'thirstyaffiliates' ),
330
+ 'type' => 'toggle'
331
+ ),
332
+
333
+ array(
334
+ 'id' => 'ta_stats_trimer_set_point',
335
+ 'title' => __( 'Trim stats older than:' , 'thirstyaffiliates' ),
336
+ 'desc' => __( "months (Automatically clean the statistics database records older than a set point. Setting this to 0 will disable it)." , 'thirstyaffiliates' ),
337
+ 'type' => 'number',
338
+ 'min' => 0,
339
+ 'default' => 0,
340
+ 'condition_cb' => function() { return get_option( 'ta_enable_stats_reporting_module' ) === 'yes'; }
341
+ ),
342
+
343
+ array(
344
+ 'id' => 'ta_browser_no_cache_301_redirect',
345
+ 'title' => __( "Don't cache 301 redirects? (server side redirects)" , 'thirstyaffiliates' ),
346
+ 'desc' => __( "By default, browsers caches the 301 redirects. Enabling this option will tell the browser not to cache 301 redirects. Be aware that it is still up to the browser if it will cache it or not." , 'thirstyaffiliates' ),
347
+ 'type' => 'toggle'
348
+ ),
349
+
350
+ array(
351
+ 'id' => 'ta_disable_ip_address_collection',
352
+ 'title' => __( "Disable IP address collection" , 'thirstyaffiliates' ),
353
+ 'desc' => __( "By default ThirstyAffiliates plugin collects visitor's IP address everytime they click an affiliate link as part of the statistics information. By checking this the IP address collection will be disabled, but other information will still be saved." , 'thirstyaffiliates' ),
354
+ 'type' => 'toggle'
355
+ ),
356
+
357
+ array(
358
+ 'id' => 'ta_disable_browser_device_collection',
359
+ 'title' => __( "Disable browser/device data collection" , 'thirstyaffiliates' ),
360
+ 'desc' => __( "As of version 3.4, by default ThirstyAffiliates plugin collects visitor's browser and device used everytime they click an affiliate link as part of the statistics information. By checking this the browser/device collection will be disabled, but other information will still be saved." , 'thirstyaffiliates' ),
361
+ 'type' => 'toggle'
362
+ ),
363
+
364
+ array(
365
+ 'id' => 'ta_blocked_bots',
366
+ 'title' => __( "Blocked bots" , 'thirstyaffiliates' ),
367
+ 'desc' => __( "By default ThirstyAffiliates blocks bots accessing your affiliate links to give you a more appropriate data in the report. Select bots, or enter new ones to block." , 'thirstyaffiliates' ),
368
+ 'type' => 'textarea',
369
+ 'default' => Plugin_Constants::DEFAULT_BLOCKED_BOTS,
370
+
371
+ ),
372
+
373
+ array(
374
+ 'id' => 'ta_enable_bot_crawl_blocker_script',
375
+ 'title' => __( "Enable Bot Crawl Blocker Script" , 'thirstyaffiliates' ),
376
+ 'desc' => __( "By enabling this setting, your affiliate links won't redirect for all the <em>blocked bots</em> set above and will send out a 403 forbidden error." , 'thirstyaffiliates' ),
377
+ 'type' => 'toggle'
378
+ )
379
+
380
+ ) ),
381
+ 'ta_links_settings' => apply_filters( 'ta_links_settings_options' , array(
382
+
383
+ array(
384
+ 'id' => 'ta_link_prefix',
385
+ 'title' => __( 'Link Prefix' , 'thirstyaffiliates' ),
386
+ 'desc' => sprintf( __( "The prefix that comes before your cloaked link's slug. <br>eg. %s/<strong>recommends</strong>/your-affiliate-link-name.<br><br><b>Warning :</b> Changing this setting after you've used links in a post could break those links. Be careful!" , 'thirstyaffiliates' ) , home_url() ),
387
+ 'type' => 'select',
388
+ 'default' => 'recommends',
389
+ 'options' => array(
390
+ 'custom' => '-- custom --',
391
+ 'recommends' => 'recommends',
392
+ 'link' => 'link',
393
+ 'go' => 'go',
394
+ 'review' => 'review',
395
+ 'product' => 'product',
396
+ 'suggests' => 'suggests',
397
+ 'follow' => 'follow',
398
+ 'endorses' => 'endorses',
399
+ 'proceed' => 'proceed',
400
+ 'fly' => 'fly',
401
+ 'goto' => 'goto',
402
+ 'get' => 'get',
403
+ 'find' => 'find',
404
+ 'act' => 'act',
405
+ 'click' => 'click',
406
+ 'move' => 'move',
407
+ 'offer' => 'offer',
408
+ 'run' => 'run'
409
+ )
410
+ ),
411
+
412
+ array(
413
+ 'id' => 'ta_link_prefix_custom',
414
+ 'title' => __( 'Custom Link Prefix' , 'thirstyaffiliates' ),
415
+ 'desc' => __( 'Enter your preferred link prefix.' , 'thirstyaffiliates' ),
416
+ 'type' => 'text'
417
+ ),
418
+
419
+ array(
420
+ 'id' => 'ta_show_cat_in_slug',
421
+ 'title' => __( 'Link Category in URL?' , 'thirstyaffiliates' ),
422
+ 'desc' => sprintf( __( "Shows the primary selected category in the url. eg. %s/recommends/<strong>link-category</strong>/your-affiliate-link-name.<br><br><b>Warning :</b> Changing this setting after you've used links in a post could break those links. Be careful!" , 'thirstyaffiliates' ) , home_url() ),
423
+ 'type' => 'toggle'
424
+ ),
425
+
426
+ array(
427
+ 'id' => 'ta_enable_javascript_frontend_redirect',
428
+ 'title' => __( "Enable Enhanced Javascript Redirect on Frontend" , 'thirstyaffiliates' ),
429
+ 'desc' => __( "By default affiliate links are redirected on the server side. Enabling this will set all affiliate links to be redirected via javascript on your website's frontend. This will then improve the accuracy of the link performance report (This will only work when <strong>Statistics</strong> module is enabled)." , 'thirstyaffiliates' ),
430
+ 'type' => 'toggle',
431
+ 'condition_cb' => function() { return get_option( 'ta_enable_stats_reporting_module' ) === 'yes'; }
432
+ ),
433
+
434
+ array(
435
+ 'id' => 'ta_link_redirect_type',
436
+ 'title' => __( 'Link Redirect Type (server side redirects)' , 'thirstyaffiliates' ),
437
+ 'desc' => __( "This is the type of redirect ThirstyAffiliates will use to redirect the user to your affiliate link." , 'thirstyaffiliates' ),
438
+ 'type' => 'radio',
439
+ 'options' => $this->_constants->REDIRECT_TYPES(),
440
+ 'default' => '302'
441
+ ),
442
+
443
+ array(
444
+ 'id' => 'ta_no_follow',
445
+ 'title' => __( 'Use no follow on links? (server side redirects)' , 'thirstyaffiliates' ),
446
+ 'desc' => __( "Add the nofollow attribute to links so search engines don't index them." , 'thirstyaffiliates' ),
447
+ 'type' => 'select',
448
+ 'options' => $toggle_cat_options,
449
+ 'default' => 'no',
450
+ 'class' => 'toggle-cat'
451
+ ),
452
+
453
+ array(
454
+ 'id' => 'ta_no_follow_category',
455
+ 'title' => __( 'No follow categories (server side redirects)' , 'thirstyaffiliates' ),
456
+ 'desc' => __( "The links assigned to the selected category will be set as \"no follow\"." , 'thirstyaffiliates' ),
457
+ 'type' => 'multiselect',
458
+ 'options' => $all_categories,
459
+ 'default' => array(),
460
+ 'placeholder' => __( 'Select category...' , 'thirstyaffiliates' ),
461
+ 'class' => 'toggle-cat-select toggle-cat-ta_no_follow',
462
+ 'required' => true,
463
+ ),
464
+
465
+ array(
466
+ 'id' => 'ta_new_window',
467
+ 'title' => __( 'Open links in new window?' , 'thirstyaffiliates' ),
468
+ 'desc' => __( "Make links open in a new browser tab by default." , 'thirstyaffiliates' ),
469
+ 'type' => 'select',
470
+ 'options' => $toggle_cat_options,
471
+ 'default' => 'no',
472
+ 'class' => 'toggle-cat'
473
+ ),
474
+
475
+ array(
476
+ 'id' => 'ta_new_window_category',
477
+ 'title' => __( 'New window categories' , 'thirstyaffiliates' ),
478
+ 'desc' => __( "The links assigned to the selected category will be set as \"new window\"." , 'thirstyaffiliates' ),
479
+ 'type' => 'multiselect',
480
+ 'options' => $all_categories,
481
+ 'default' => array(),
482
+ 'placeholder' => __( 'Select category...' , 'thirstyaffiliates' ),
483
+ 'class' => 'toggle-cat-select toggle-cat-ta_new_window',
484
+ 'required' => true,
485
+ ),
486
+
487
+ array(
488
+ 'id' => 'ta_pass_query_str',
489
+ 'title' => __( 'Pass query strings to destination url?' , 'thirstyaffiliates' ),
490
+ 'desc' => __( "Enabling this option will pass all of the query strings present after the cloaked url to the destination url automatically when redirecting." , 'thirstyaffiliates' ),
491
+ 'type' => 'select',
492
+ 'options' => $toggle_cat_options,
493
+ 'default' => 'no',
494
+ 'class' => 'toggle-cat',
495
+ ),
496
+
497
+ array(
498
+ 'id' => 'ta_pass_query_str_category',
499
+ 'title' => __( 'Pass query strings categories' , 'thirstyaffiliates' ),
500
+ 'desc' => __( "The links assigned to the selected category will be set as \"pass query strings\"." , 'thirstyaffiliates' ),
501
+ 'type' => 'multiselect',
502
+ 'options' => $all_categories,
503
+ 'default' => array(),
504
+ 'placeholder' => __( 'Select category...' , 'thirstyaffiliates' ),
505
+ 'class' => 'toggle-cat-select toggle-cat-ta_pass_query_str',
506
+ 'required' => true,
507
+ ),
508
+
509
+ array(
510
+ 'id' => 'ta_additional_rel_tags',
511
+ 'title' => __( 'Additional rel attribute tags' , 'thirstyaffiliates' ),
512
+ 'desc' => __( "Allows you to add extra tags into the rel= attribute when links are inserted." , 'thirstyaffiliates' ),
513
+ 'type' => 'text'
514
+ ),
515
+
516
+ array(
517
+ 'id' => 'ta_additional_css_classes',
518
+ 'title' => __( 'Additional CSS classes' , 'thirstyaffiliates' ),
519
+ 'desc' => __( "Allows you to add extra CSS classes when links are inserted." , 'thirstyaffiliates' ),
520
+ 'type' => 'text'
521
+ ),
522
+
523
+ array(
524
+ 'id' => 'ta_disable_thirsty_link_class',
525
+ 'title' => __( 'Disable ThirstyAffiliates CSS classes?' , 'thirstyaffiliates' ),
526
+ 'desc' => __( 'To help with styling a CSS class called "thirstylink" is added links on insertion.<br>Likewise the "thirstylinkimg" class is added to images when using the image insertion type. This option disables the addition these CSS classes.' , 'thirstyaffiliates' ),
527
+ 'type' => 'toggle'
528
+ ),
529
+
530
+ array(
531
+ 'id' => 'ta_disable_title_attribute',
532
+ 'title' => __( 'Disable title attribute on link insertion?' , 'thirstyaffiliates' ),
533
+ 'desc' => __( "Links are automatically output with a title html attribute (by default this shows the title of the affiliate link).<br>This option disables the output of the title attribute on your links." , 'thirstyaffiliates' ),
534
+ 'type' => 'toggle'
535
+ ),
536
+
537
+ array(
538
+ 'id' => 'ta_category_to_uncloak',
539
+ 'title' => __( 'Select Category to Uncloak' , 'thirstyaffiliates' ),
540
+ 'desc' => __( "The links assigned to the selected category will be uncloaked." , 'thirstyaffiliates' ),
541
+ 'type' => 'multiselect',
542
+ 'options' => $all_categories,
543
+ 'default' => array(),
544
+ 'condition_cb' => function() { return get_option( 'ta_uncloak_link_per_link' ) === 'yes'; },
545
+ 'placeholder' => __( 'Select category...' , 'thirstyaffiliates' )
546
+ )
547
+
548
+ ) ),
549
+
550
+ 'ta_modules_settings' => apply_filters( 'ta_modules_settings_options' , array(
551
+
552
+ array(
553
+ 'id' => 'ta_enable_stats_reporting_module',
554
+ 'title' => __( 'Statistics' , 'thirstyaffiliates' ),
555
+ 'desc' => __( "When enabled, ThirstyAffiliates will collect click statistics information about visitors that click on your affiliate links. Also adds a new Reports section." , 'thirstyaffiliates' ),
556
+ 'type' => 'toggle',
557
+ 'default' => 'yes'
558
+ ),
559
+
560
+ array(
561
+ 'id' => 'ta_enable_link_fixer',
562
+ 'title' => __( 'Link Fixer' , 'thirstyaffiliates' ),
563
+ 'desc' => __( "Link Fixer is a tiny piece of javascript code that runs on the frontend of your site to fix any outdated/broken affiliate links it detects. It's cache-friendly and runs after page load so it doesn't affect the rendering of content. Changed the settings on your site recently? Enabling Link Fixer means you don't need to update all your previously inserted affiliate links one by one – your visitors will never see an out of date affiliate link again." , 'thirstyaffiliates' ),
564
+ 'type' => 'toggle',
565
+ 'default' => 'yes',
566
+ ),
567
+
568
+ array(
569
+ 'id' => 'ta_uncloak_link_per_link',
570
+ 'title' => __( 'Uncloak Links' , 'thirstyaffiliates' ),
571
+ 'desc' => __( "Uncloak Links is a feature to allow uncloaking of specific links on your site. It replaces the cloaked url with the actual destination url which is important for compatibility with some affiliate program with stricter terms (such as Amazon Associates). Once enabled, you will see a new Uncloak Link checkbox on the affiliate link edit screen. It also introduces a new setting under the Links tab for uncloaking whole categories.<br><br><b>Warning : </b>For this feature to work, the <strong>Link Fixer</strong> module needs to be turned on." , 'thirstyaffiliates' ),
572
+ 'type' => 'toggle',
573
+ 'default' => 'no',
574
+ )
575
+
576
+ ) ),
577
+ 'ta_import_export_settings' => apply_filters( 'ta_import_export_settings_options' , array(
578
+
579
+ array(
580
+ 'id' => 'ta_import_settings',
581
+ 'title' => __( 'Import Global Settings' , 'thirstyaffiliates' ),
582
+ 'type' => 'import_global_settings',
583
+ 'placeholder' => __( 'Paste settings string here...' , 'thirstyaffiliates' )
584
+ ),
585
+
586
+ array(
587
+ 'id' => 'ta_export_settings',
588
+ 'title' => __( 'Export Global Settings' , 'thirstyaffiliates' ),
589
+ 'type' => 'export_global_settings'
590
+ )
591
+
592
+ ) ),
593
+ 'ta_help_settings' => apply_filters( 'ta_help_settings_options' , array(
594
+
595
+ array(
596
+ 'id' => 'ta_knowledge_base_divider', // Even though no option is really saved, we still add id, for the purpose of later when extending this section options, they can search for this specific section divider during array loop
597
+ 'title' => __( 'Knowledge Base' , 'thirstyaffiliates' ),
598
+ 'type' => 'option_divider'
599
+ ),
600
+
601
+ array(
602
+ 'title' => __( 'Documentation' , 'thirstyaffiliates' ),
603
+ 'type' => 'link',
604
+ 'link_url' => 'https://thirstyaffiliates.com/knowledge-base/?utm_source=Free%20Plugin&utm_medium=Help&utm_campaign=Knowledge%20Base%20Link',
605
+ 'link_text' => __( 'Knowledge Base' , 'thirstyaffiliates' ),
606
+ 'desc' => __( 'Guides, troubleshooting, FAQ and more.' , 'thirstyaffiliates' ),
607
+ 'id' => 'ta_kb_link',
608
+ ),
609
+
610
+ array(
611
+ 'title' => __( 'Our Blog' , 'thirstyaffiliates' ),
612
+ 'type' => 'link',
613
+ 'link_url' => 'https://thirstyaffiliates.com/blog?utm_source=Free%20Plugin&utm_medium=Help&utm_campaign=Blog%20Link',
614
+ 'link_text' => __( 'ThirstyAffiliates Blog' , 'thirstyaffiliates' ),
615
+ 'desc' => __( 'Learn & grow your affiliate marketing – covering increasing sales, generating traffic, optimising your affiliate marketing, interviews & case studies.' , 'thirstyaffiliates' ),
616
+ 'id' => 'ta_blog_link',
617
+ ),
618
+
619
+ array(
620
+ 'title' => __( 'Join the Community' , 'thirstyaffiliates' ),
621
+ 'type' => 'social_links',
622
+ 'id' => 'ta_social_links'
623
+ ),
624
+
625
+ array(
626
+ 'id' => 'ta_other_utilities_divider', // Even though no option is really saved, we still add id, for the purpose of later when extending this section options, they can search for this specific section divider during array loop
627
+ 'title' => __( 'Other Utilities' , 'thirstyaffiliates' ),
628
+ 'type' => 'option_divider'
629
+ ),
630
+
631
+ array(
632
+ 'title' => __( 'Migrate Old Data' , 'thirstyaffiliates' ),
633
+ 'type' => 'migration_controls',
634
+ 'desc' => __( 'Migrate old ThirstyAffiliates version 2 data to new version 3 data model.' , 'thirstyaffiliates' ),
635
+ 'id' => 'ta_migrate_old_data'
636
+ )
637
+
638
+ ) )
639
+
640
+ ) );
641
+
642
+ // Get all the exportable options
643
+ foreach ( $this->_settings_section_options as $section_id => $section_options ) {
644
+
645
+ foreach ( $section_options as $option ) {
646
+
647
+ if ( in_array( $option[ 'type' ] , $this->_skip_wp_settings_registration ) )
648
+ continue;
649
+
650
+ $this->_exportable_options[ $option[ 'id' ] ] = isset( $option[ 'default' ] ) ? $option[ 'default' ] : '';
651
+
652
+ if ( isset( $option[ 'post_update_cb' ] ) && is_callable( $option[ 'post_update_cb' ] ) )
653
+ add_action( 'update_option_' . $option[ 'id' ] , $option[ 'post_update_cb' ] , 10 , 3 );
654
+
655
+ }
656
+
657
+ }
658
+
659
+ }
660
+
661
+ /**
662
+ * Register Settings Section and Options Group to WordPress Settings API.
663
+ *
664
+ * @since 3.0.0
665
+ * @access public
666
+ */
667
+ public function register_settings_section_and_options_group() {
668
+
669
+ foreach ( $this->_settings_sections as $section_id => $section_data ) {
670
+
671
+ add_settings_section(
672
+ $section_id, // Settings Section ID
673
+ $section_data[ 'title' ], // Settings Section Title
674
+ function() {}, // Callback. Intentionally Left Empty. We Will Handle UI Rendering.
675
+ $section_id . '_options_group' // Options Group
676
+ );
677
+
678
+ }
679
+
680
+ }
681
+
682
+ /**
683
+ * Register Settings Section Options to WordPress Settings API.
684
+ *
685
+ * @since 3.0.0
686
+ * @access public
687
+ */
688
+ public function register_settings_section_options() {
689
+
690
+ foreach ( $this->_settings_section_options as $section_id => $section_options ) {
691
+
692
+ foreach ( $section_options as $option ) {
693
+
694
+ if ( !array_key_exists( $option[ 'type' ] , $this->_supported_field_types ) || in_array( $option[ 'type' ] , $this->_skip_wp_settings_registration ) )
695
+ continue;
696
+
697
+ // Register The Option To The Options Group It Is Scoped With
698
+ add_settings_field(
699
+ $option[ 'id' ], // Option ID
700
+ $option[ 'title' ], // Option Title
701
+ function() {}, // Render Callback. Intentionally Left Empty. We Will Handle UI Rendering.
702
+ $section_id . '_options_group', // Options Group
703
+ $section_id // Settings Section ID
704
+ );
705
+
706
+ // Register The Actual Settings Option
707
+ $args = array();
708
+
709
+ if ( isset( $option[ 'data_type' ] ) )
710
+ $args[ 'type' ] = $option[ 'data_type' ];
711
+
712
+ if ( isset( $option[ 'desc' ] ) )
713
+ $args[ 'description' ] = $option[ 'desc' ];
714
+
715
+ if ( isset( $option[ 'sanitation_cb' ] ) && is_callable( $option[ 'sanitation_cb' ] ) )
716
+ $args[ 'sanitize_callback' ] = $option[ 'sanitation_cb' ];
717
+
718
+ if ( isset( $option[ 'show_in_rest' ] ) )
719
+ $args[ 'show_in_rest' ] = $option[ 'show_in_rest' ];
720
+
721
+ if ( isset( $option[ 'default' ] ) )
722
+ $args[ 'default' ] = $option[ 'default' ]; // Important Note: This will be used on "get_option" function automatically if the current option is not registered yet to the options db.
723
+
724
+ register_setting( $section_id . '_options_group' , $option[ 'id' ] , $args );
725
+
726
+ }
727
+
728
+ }
729
+
730
+ }
731
+
732
+ /**
733
+ * Initialize Plugin Settings API.
734
+ * We register the plugin settings section and settings section options to WordPress Settings API.
735
+ * We let WordPress Settings API handle the backend stuff.
736
+ * We will handle the UI rendering.
737
+ *
738
+ * @since 3.0.0
739
+ * @access public
740
+ */
741
+ public function init_plugin_settings() {
742
+
743
+ $this->init_settings_sections_and_options();
744
+ $this->register_settings_section_and_options_group();
745
+ $this->register_settings_section_options();
746
+
747
+ }
748
+
749
+ /**
750
+ * Add settings page.
751
+ *
752
+ * @since 3.0.0
753
+ * @since 3.2.2 Access to the settings page will now be controlled by the plugin. see Bootstrap::admin_interface_visibility.
754
+ * @access public
755
+ */
756
+ public function add_settings_page() {
757
+
758
+ if ( ! current_user_can( 'edit_posts' ) ) return;
759
+
760
+ add_submenu_page(
761
+ 'edit.php?post_type=thirstylink',
762
+ __( 'ThirstyAffiliates Settings' , 'thirstyaffiliates' ),
763
+ __( 'Settings' , 'thirstyaffiliates' ),
764
+ 'read',
765
+ 'thirsty-settings',
766
+ array( $this, 'view_settings_page' )
767
+ );
768
+
769
+ }
770
+
771
+ /**
772
+ * Settings page view.
773
+ *
774
+ * @since 3.0.0
775
+ * @since 3.3.1 Add a new <div> to wrap the side navigation and form. Add additional action hooks.
776
+ * @access public
777
+ */
778
+ public function view_settings_page() {
779
+ ?>
780
+
781
+ <div class="wrap ta-settings">
782
+
783
+ <h2><?php _e( 'ThirstyAffiliates Settings' , 'thirstyaffiliates' ); ?></h2>
784
+
785
+ <?php
786
+ settings_errors(); // Show notices based on the outcome of the settings save action
787
+ $active_tab = isset( $_GET[ 'tab' ] ) ? $_GET[ 'tab' ] : 'ta_general_settings';
788
+ ?>
789
+
790
+ <div class="ta-settings-wrapper">
791
+
792
+ <?php do_action( 'ta_before_settings_sidenav' ); ?>
793
+
794
+ <h2 class="nav-tab-wrapper">
795
+ <?php foreach ( $this->_settings_sections as $section_key => $section_data ) { ?>
796
+ <a href="?post_type=thirstylink&page=thirsty-settings&tab=<?php echo $section_key; ?>" class="nav-tab <?php echo $active_tab == $section_key ? 'nav-tab-active' : ''; ?> <?php echo $section_key; ?>"><?php echo $section_data[ 'title' ]; ?></a>
797
+ <?php } ?>
798
+
799
+ <?php if ( ! $this->_helper_functions->is_plugin_active( 'thirstyaffiliates-pro/thirstyaffiliates-pro.php' ) ) : ?>
800
+ <a class="tapro-upgrade nav-tab" href="https://thirstyaffiliates.com/pricing/?utm_source=Free%20Plugin&utm_medium=Pro&utm_campaign=Admin%20Settings" target="_blank"><?php _e( 'Pro Features →' , 'thirstyaffiliates' ); ?></a>
801
+ <?php endif; ?>
802
+ </h2>
803
+
804
+ <?php do_action( 'ta_before_settings_form' ); ?>
805
+
806
+ <div class="ta-settings-form">
807
+
808
+ <form method="post" action="options.php" enctype="multipart/form-data">
809
+
810
+ <?php
811
+ $this->render_settings_section_nonces( $active_tab );
812
+ $this->render_settings_section_header( $active_tab );
813
+ $this->render_settings_section_fields( $active_tab );
814
+ ?>
815
+
816
+ </form>
817
+ </div>
818
+
819
+ <?php do_action( 'ta_after_settings_form' ); ?>
820
+
821
+ </div>
822
+
823
+ </div><!--wrap-->
824
+
825
+ <?php
826
+ }
827
+
828
+ /**
829
+ * Render all necessary nonces for the current settings section.
830
+ *
831
+ * @since 3.0.0
832
+ * @access public
833
+ *
834
+ * @param string $active_tab Currently active settings section.
835
+ */
836
+ public function render_settings_section_nonces( $active_tab ) {
837
+
838
+ settings_fields( $active_tab . '_options_group' );
839
+
840
+ }
841
+
842
+ /**
843
+ * Render the current settings section header markup.
844
+ *
845
+ * @since 3.0.0
846
+ * @access public
847
+ *
848
+ * @param string $active_tab Currently active settings section.
849
+ */
850
+ public function render_settings_section_header( $active_tab ) {
851
+
852
+ if ( ! isset( $this->_settings_sections[ $active_tab ] ) )
853
+ return;
854
+
855
+ ?>
856
+
857
+ <h2><?php echo $this->_settings_sections[ $active_tab ][ 'title' ]; ?></h2>
858
+ <p class="desc"><?php echo $this->_settings_sections[ $active_tab ][ 'desc' ]; ?></p>
859
+
860
+ <?php
861
+
862
+ }
863
+
864
+ /**
865
+ * Render an option as a hidden field.
866
+ * We do this if that option's condition callback failed.
867
+ * We don't show it for the end user, but we still need to pass on the form the current data so we don't lost it.
868
+ *
869
+ * @since 3.0.0
870
+ * @access public
871
+ *
872
+ * @param array $option Array of options data. May vary depending on option type.
873
+ */
874
+ public function render_option_as_hidden_field( $option ) {
875
+
876
+ if ( in_array( $option[ 'type' ] , $this->_skip_wp_settings_registration ) )
877
+ return; // This is a decorative option type, no need to render this as a hidden field
878
+
879
+ ?>
880
+
881
+ <input type="hidden" name="<?php echo esc_attr( $option[ 'id' ] ); ?>" value="<?php echo get_option( $option[ 'id' ] , '' ); ?>">
882
+
883
+ <?php
884
+
885
+ }
886
+
887
+ /**
888
+ * Render settings section option fields.
889
+ *
890
+ * @since 3.0.0
891
+ * @access public
892
+ *
893
+ * @param string $active_tab Currently active settings section.
894
+ */
895
+ public function render_settings_section_fields( $active_tab ) {
896
+
897
+ $no_save_sections = apply_filters( 'ta_render_settings_no_save_section' , array(
898
+ 'ta_import_export_settings'
899
+ ) );
900
+ ?>
901
+
902
+ <table class="form-table">
903
+ <tbody>
904
+ <?php
905
+ foreach ( $this->_settings_section_options as $section_id => $section_options ) {
906
+
907
+ if ( $section_id !== $active_tab )
908
+ continue;
909
+
910
+ foreach ( $section_options as $option ) {
911
+
912
+ if ( isset( $option[ 'condition_cb' ] ) && is_callable( $option[ 'condition_cb' ] ) && !$option[ 'condition_cb' ]() )
913
+ $this->render_option_as_hidden_field( $option ); // Option condition failed. Render it as a hidden field so its value is preserved
914
+ else
915
+ $this->_supported_field_types[ $option[ 'type' ] ]( $option );
916
+
917
+ }
918
+
919
+ }
920
+ ?>
921
+ </tbody>
922
+ </table>
923
+
924
+ <?php if ( ! in_array( $active_tab , $no_save_sections ) ) : ?>
925
+ <p class="submit">
926
+ <input name="submit" id="submit" class="button button-primary" value="<?php _e( 'Save Changes' , 'thirstyaffiliates' ); ?>" type="submit">
927
+ </p>
928
+ <?php endif;
929
+
930
+ }
931
+
932
+
933
+
934
+
935
+ /*
936
+ |--------------------------------------------------------------------------
937
+ | Option Field Views
938
+ |--------------------------------------------------------------------------
939
+ */
940
+
941
+ /**
942
+ * Render 'text' type option field.
943
+ *
944
+ * @since 3.0.0
945
+ * @access public
946
+ *
947
+ * @param array $option Array of options data. May vary depending on option type.
948
+ */
949
+ public function render_text_option_field( $option ) {
950
+
951
+ ?>
952
+
953
+ <tr valign="top" class="<?php echo esc_attr( $option[ 'id' ] ) . '-row'; ?>">
954
+
955
+ <th scope="row"><?php echo sanitize_text_field( $option[ 'title' ] ); ?></th>
956
+
957
+ <td class="forminp forminp-<?php echo sanitize_title( $option[ 'type' ] ) ?>">
958
+ <input
959
+ type = "text"
960
+ name = "<?php echo esc_attr( $option[ 'id' ] ); ?>"
961
+ id = "<?php echo esc_attr( $option[ 'id' ] ); ?>"
962
+ class = "option-field <?php echo isset( $option[ 'class' ] ) ? esc_attr( $option[ 'class' ] ) : ''; ?>"
963
+ style = "<?php echo isset( $option[ 'style' ] ) ? $option[ 'style' ] : 'width: 360px;'; ?>"
964
+ value = "<?php echo get_option( $option[ 'id' ] ); ?>" >
965
+ <br>
966
+ <p class="desc"><?php echo isset( $option[ 'desc' ] ) ? $option[ 'desc' ] : ''; ?></p>
967
+ </td>
968
+
969
+ </tr>
970
+
971
+ <?php
972
+
973
+ }
974
+
975
+ /**
976
+ * Render 'url' type option field.
977
+ *
978
+ * @since 3.3.1
979
+ * @access public
980
+ *
981
+ * @param array $option Array of options data. May vary depending on option type.
982
+ */
983
+ public function render_url_option_field( $option ) {
984
+
985
+ ?>
986
+
987
+ <tr valign="top" class="<?php echo esc_attr( $option[ 'id' ] ) . '-row'; ?>">
988
+
989
+ <th scope="row"><?php echo sanitize_text_field( $option[ 'title' ] ); ?></th>
990
+
991
+ <td class="forminp forminp-<?php echo sanitize_title( $option[ 'type' ] ) ?>">
992
+ <input
993
+ type = "url"
994
+ name = "<?php echo esc_attr( $option[ 'id' ] ); ?>"
995
+ id = "<?php echo esc_attr( $option[ 'id' ] ); ?>"
996
+ class = "option-field <?php echo isset( $option[ 'class' ] ) ? esc_attr( $option[ 'class' ] ) : ''; ?>"
997
+ style = "<?php echo isset( $option[ 'style' ] ) ? $option[ 'style' ] : 'width: 360px;'; ?>"
998
+ value = "<?php echo get_option( $option[ 'id' ] ); ?>" >
999
+ <br>
1000
+ <p class="desc"><?php echo isset( $option[ 'desc' ] ) ? $option[ 'desc' ] : ''; ?></p>
1001
+ </td>
1002
+
1003
+ </tr>
1004
+
1005
+ <?php
1006
+
1007
+ }
1008
+
1009
+ /**
1010
+ * Render 'text' type option field.
1011
+ *
1012
+ * @since 3.0.0
1013
+ * @access public
1014
+ *
1015
+ * @param array $option Array of options data. May vary depending on option type.
1016
+ */
1017
+ public function render_number_option_field( $option ) {
1018
+
1019
+ ?>
1020
+
1021
+ <tr valign="top" class="<?php echo esc_attr( $option[ 'id' ] ) . '-row'; ?>">
1022
+
1023
+ <th scope="row"><?php echo sanitize_text_field( $option[ 'title' ] ); ?></th>
1024
+
1025
+ <td class="forminp forminp-<?php echo sanitize_title( $option[ 'type' ] ) ?>">
1026
+ <input
1027
+ type = "number"
1028
+ name = "<?php echo esc_attr( $option[ 'id' ] ); ?>"
1029
+ id = "<?php echo esc_attr( $option[ 'id' ] ); ?>"
1030
+ class = "option-field <?php echo isset( $option[ 'class' ] ) ? esc_attr( $option[ 'class' ] ) : ''; ?>"
1031
+ style = "<?php echo isset( $option[ 'style' ] ) ? $option[ 'style' ] : 'width: 100px;'; ?>"
1032
+ value = "<?php echo get_option( $option[ 'id' ] ); ?>"
1033
+ min = "<?php echo isset( $option[ 'min' ] ) ? $option[ 'min' ] : 0; ?>"
1034
+ max = "<?php echo isset( $option[ 'max' ] ) ? $option[ 'max' ] : ''; ?>" >
1035
+ <span><?php echo isset( $option[ 'desc' ] ) ? $option[ 'desc' ] : ''; ?></span>
1036
+ </td>
1037
+
1038
+ </tr>
1039
+
1040
+ <?php
1041
+
1042
+ }
1043
+
1044
+ /**
1045
+ * Render 'textarea' type option field.
1046
+ *
1047
+ * @since 3.0.0
1048
+ * @access public
1049
+ *
1050
+ * @param array $option Array of options data. May vary depending on option type.
1051
+ */
1052
+ public function render_textarea_option_field( $option ) {
1053
+
1054
+ ?>
1055
+
1056
+ <tr valign="top" class="<?php echo esc_attr( $option[ 'id' ] ) . '-row'; ?>">
1057
+
1058
+ <th scope="row"><?php echo sanitize_text_field( $option[ 'title' ] ); ?></th>
1059
+
1060
+ <td class="forminp forminp-<?php echo sanitize_title( $option[ 'type' ] ) ?>">
1061
+ <textarea
1062
+ name = "<?php echo esc_attr( $option[ 'id' ] ); ?>"
1063
+ id = "<?php echo esc_attr( $option[ 'id' ] ); ?>"
1064
+ class = "option-field <?php echo isset( $option[ 'class' ] ) ? esc_attr( $option[ 'class' ] ) : ''; ?>"
1065
+ cols = "60"
1066
+ rows = "8"
1067
+ style = "<?php echo isset( $option[ 'style' ] ) ? $option[ 'style' ] : 'width: 360px;'; ?>"><?php echo get_option( $option[ 'id' ] ); ?></textarea>
1068
+ <br />
1069
+ <p class="desc"><?php echo isset( $option[ 'desc' ] ) ? $option[ 'desc' ] : ''; ?></p>
1070
+ </td>
1071
+
1072
+ </tr>
1073
+
1074
+ <?php
1075
+
1076
+ }
1077
+
1078
+ /**
1079
+ * Render 'checkbox' type option field.
1080
+ *
1081
+ * @since 3.0.0
1082
+ * @access public
1083
+ *
1084
+ * @param array $option Array of options data. May vary depending on option type.
1085
+ */
1086
+ public function render_checkbox_option_field( $option ) {
1087
+
1088
+ $option_val = get_option( $option[ 'id' ] );
1089
+ if ( !is_array( $option_val ) )
1090
+ $option_val = array(); ?>
1091
+
1092
+ <tr valign="top" class="<?php echo esc_attr( $option[ 'id' ] ) . '-row'; ?>">
1093
+ <th scope="row"><?php echo sanitize_text_field( $option[ 'title' ] ); ?></th>
1094
+
1095
+ <td class="forminp forminp-<?php echo sanitize_title( $option[ 'type' ] ) ?>">
1096
+ <?php foreach ( $option[ 'options' ] as $opt_key => $opt_text ) {
1097
+
1098
+ $opt_key_class = str_replace( " " , "-" , $opt_key ); ?>
1099
+
1100
+ <input
1101
+ type = "checkbox"
1102
+ name = "<?php echo esc_attr( $option[ 'id' ] ); ?>[]"
1103
+ id = "<?php echo esc_attr( $option[ 'id' ] ); ?>"
1104
+ class = "option-field <?php echo isset( $option[ 'class' ] ) ? esc_attr( $option[ 'class' ] ) : ''; ?>"
1105
+ style = "<?php echo isset( $option[ 'style' ] ) ? $option[ 'style' ] : ''; ?>"
1106
+ value = "<?php echo $opt_key; ?>"
1107
+ <?php echo in_array( $opt_key , $option_val ) ? 'checked' : ''; ?>>
1108
+
1109
+ <label class="<?php echo esc_attr( $option[ 'id' ] ); ?>"><?php echo $opt_text; ?></label>
1110
+ <br>
1111
+
1112
+ <?php } ?>
1113
+
1114
+ <p class="desc"><?php echo isset( $option[ 'desc' ] ) ? $option[ 'desc' ] : ''; ?></p>
1115
+ </td>
1116
+
1117
+ <script>
1118
+ jQuery( document ).ready( function( $ ) {
1119
+
1120
+ $( "label.<?php echo esc_attr( $option[ 'id' ] ); ?>" ).on( "click" , function() {
1121
+
1122
+ $( this ).prev( "input[type='checkbox']" ).trigger( "click" );
1123
+
1124
+ } );
1125
+
1126
+ } );
1127
+ </script>
1128
+ </tr>
1129
+
1130
+ <?php
1131
+
1132
+ }
1133
+
1134
+ /**
1135
+ * Render 'radio' type option field.
1136
+ *
1137
+ * @since 3.0.0
1138
+ * @access public
1139
+ *
1140
+ * @param array $option Array of options data. May vary depending on option type.
1141
+ */
1142
+ public function render_radio_option_field( $option ) {
1143
+
1144
+ $option_val = get_option( $option[ 'id' ] ); ?>
1145
+
1146
+
1147
+ <tr valign="top" class="<?php echo esc_attr( $option[ 'id' ] ) . '-row'; ?>">
1148
+ <th scope="row"><?php echo sanitize_text_field( $option[ 'title' ] ); ?></th>
1149
+
1150
+ <td class="forminp forminp-<?php echo sanitize_title( $option[ 'type' ] ) ?>">
1151
+ <?php foreach ( $option[ 'options' ] as $opt_key => $opt_text ) { ?>
1152
+
1153
+ <input
1154
+ type = "radio"
1155
+ name = "<?php echo esc_attr( $option[ 'id' ] ); ?>"
1156
+ id = "<?php echo esc_attr( $option[ 'id' ] ); ?>"
1157
+ class = "option-field <?php echo isset( $option[ 'class' ] ) ? esc_attr( $option[ 'class' ] ) : ''; ?>"
1158
+ style = "<?php echo isset( $option[ 'style' ] ) ? $option[ 'style' ] : ''; ?>"
1159
+ value = "<?php echo $opt_key; ?>"
1160
+ <?php echo $opt_key == $option_val ? 'checked' : ''; ?>>
1161
+
1162
+ <label class="<?php echo esc_attr( $option[ 'id' ] ); ?>"><?php echo $opt_text; ?></label>
1163
+ <br>
1164
+
1165
+ <?php } ?>
1166
+
1167
+ <p class="desc"><?php echo isset( $option[ 'desc' ] ) ? $option[ 'desc' ] : ''; ?></p>
1168
+ </td>
1169
+
1170
+ <script>
1171
+ jQuery( document ).ready( function( $ ) {
1172
+
1173
+ $( "label.<?php echo esc_attr( $option[ 'id' ] ); ?>" ).on( "click" , function() {
1174
+
1175
+ $( this ).prev( "input[type='radio']" ).trigger( "click" );
1176
+
1177
+ } );
1178
+
1179
+ } );
1180
+ </script>
1181
+ </tr>
1182
+
1183
+ <?php
1184
+
1185
+ }
1186
+
1187
+ /**
1188
+ * Render 'select' type option field.
1189
+ *
1190
+ * @since 3.0.0
1191
+ * @access public
1192
+ *
1193
+ * @param array $option Array of options data. May vary depending on option type.
1194
+ */
1195
+ public function render_select_option_field( $option ) {
1196
+
1197
+ $option_value = $this->_helper_functions->get_option( $option[ 'id' ] , $option[ 'default' ] ); ?>
1198
+
1199
+ <tr valign="top" class="<?php echo esc_attr( $option[ 'id' ] ) . '-row'; ?>">
1200
+ <th scope="row"><?php echo sanitize_text_field( $option[ 'title' ] ); ?></th>
1201
+
1202
+ <td class="forminp forminp-<?php echo sanitize_title( $option[ 'type' ] ) ?> <?php echo isset( $option[ 'class' ] ) ? esc_attr( $option[ 'class' ] ) : ''; ?>">
1203
+ <select
1204
+ data-placeholder = "<?php echo isset( $option[ 'placeholder' ] ) ? $option[ 'placeholder' ] : 'Choose an option...' ; ?>"
1205
+ name = "<?php echo esc_attr( $option[ 'id' ] ); ?>"
1206
+ id = "<?php echo esc_attr( $option[ 'id' ] ); ?>"
1207
+ class = "option-field selectize-select <?php echo isset( $option[ 'class' ] ) ? esc_attr( $option[ 'class' ] ) : ''; ?>"
1208
+ style = "<?php echo isset( $option[ 'style' ] ) ? $option[ 'style' ] : 'width:360px;'; ?>">
1209
+
1210
+ <?php foreach ( $option[ 'options' ] as $opt_key => $opt_text ) { ?>
1211
+
1212
+ <option value="<?php echo $opt_key; ?>" <?php selected( $option_value , $opt_key ); ?>><?php echo $opt_text; ?></option>
1213
+
1214
+ <?php } ?>
1215
+ </select>
1216
+ <p class="desc"><?php echo isset( $option[ 'desc' ] ) ? $option[ 'desc' ] : ''; ?></p>
1217
+ </td>
1218
+
1219
+ <script>
1220
+ jQuery( document ).ready( function( $ ) {
1221
+
1222
+ $( '#<?php echo esc_attr( $option[ 'id' ] ); ?>' ).selectize({
1223
+ searchField : 'text'
1224
+ });
1225
+
1226
+ } );
1227
+ </script>
1228
+ </tr>
1229
+
1230
+ <?php
1231
+
1232
+ }
1233
+
1234
+ /**
1235
+ * Render 'multiselect' type option field.
1236
+ *
1237
+ * @since 3.0.0
1238
+ * @access public
1239
+ *
1240
+ * @param array $option Array of options data. May vary depending on option type.
1241
+ */
1242
+ public function render_multiselect_option_field( $option ) {
1243
+
1244
+ $option_val = get_option( $option[ 'id' ] );
1245
+ if ( !is_array( $option_val ) )
1246
+ $option_val = array(); ?>
1247
+
1248
+ <tr valign="top" class="<?php echo esc_attr( $option[ 'id' ] ) . '-row'; ?>">
1249
+ <th scope="row"><?php echo sanitize_text_field( $option[ 'title' ] ); ?></th>
1250
+
1251
+ <td class="forminp forminp-<?php echo sanitize_title( $option[ 'type' ] ) ?>">
1252
+ <select
1253
+ multiple
1254
+ data-placeholder = "<?php echo isset( $option[ 'placeholder' ] ) ? $option[ 'placeholder' ] : 'Choose an option...' ; ?>"
1255
+ name = "<?php echo esc_attr( $option[ 'id' ] ); ?>[]"
1256
+ id = "<?php echo esc_attr( $option[ 'id' ] ); ?>"
1257
+ class = "option-field selectize-select <?php echo isset( $option[ 'class' ] ) ? esc_attr( $option[ 'class' ] ) : ''; ?>"
1258
+ style = "<?php echo isset( $option[ 'style' ] ) ? $option[ 'style' ] : 'width:360px;'; ?>"
1259
+ <?php echo isset( $option[ 'required' ] ) && $option[ 'required' ] ? 'required' : '' ?>>
1260
+
1261
+ <?php foreach ( $option[ 'options' ] as $opt_key => $opt_text ) { ?>
1262
+
1263
+ <option value="<?php echo $opt_key; ?>" <?php echo in_array( $opt_key , $option_val ) ? 'selected="selected"' : ''; ?>><?php echo $opt_text; ?></option>
1264
+
1265
+ <?php } ?>
1266
+ </select>
1267
+ <br>
1268
+ <p class="desc"><?php echo isset( $option[ 'desc' ] ) ? $option[ 'desc' ] : ''; ?></p>
1269
+ </td>
1270
+
1271
+ <script>
1272
+ jQuery( document ).ready( function( $ ) {
1273
+
1274
+ $( '#<?php echo esc_attr( $option[ 'id' ] ); ?>' ).selectize({
1275
+ plugins : [ 'remove_button' , 'drag_drop' ]
1276
+ });
1277
+
1278
+ } );
1279
+ </script>
1280
+ </tr>
1281
+
1282
+ <?php
1283
+
1284
+ }
1285
+
1286
+ /**
1287
+ * Render 'toggle' type option field.
1288
+ * Basically a single check box style option field.
1289
+ *
1290
+ * @since 3.0.0
1291
+ * @access public
1292
+ *
1293
+ * @param array $option Array of options data. May vary depending on option type.
1294
+ */
1295
+ public function render_toggle_option_field( $option ) {
1296
+
1297
+ ?>
1298
+
1299
+ <tr valign="top" class="<?php echo esc_attr( $option[ 'id' ] ) . '-row'; ?>">
1300
+ <th scope="row"><?php echo sanitize_text_field( $option[ 'title' ] ); ?></th>
1301
+
1302
+ <td class="forminp forminp-<?php echo sanitize_title( $option[ 'type' ] ) ?>">
1303
+ <input
1304
+ type = "checkbox"
1305
+ name = "<?php echo esc_attr( $option[ 'id' ] ); ?>"
1306
+ id = "<?php echo esc_attr( $option[ 'id' ] ); ?>"
1307
+ class = "option-field <?php echo isset( $option[ 'class' ] ) ? esc_attr( $option[ 'class' ] ) : ''; ?>"
1308
+ style = "<?php echo isset( $option[ 'style' ] ) ? $option[ 'style' ] : ''; ?>"
1309
+ value = "yes"
1310
+ <?php echo get_option( $option[ 'id' ] ) === "yes" ? 'checked' : ''; ?>>
1311
+ <label class="<?php echo esc_attr( $option[ 'id' ] ); ?>"><?php echo isset( $option[ 'desc' ] ) ? $option[ 'desc' ] : ''; ?></label>
1312
+ </td>
1313
+
1314
+ <script>
1315
+ jQuery( document ).ready( function( $ ) {
1316
+
1317
+ $( "label.<?php echo esc_attr( $option[ 'id' ] ); ?>" ).on( "click" , function() {
1318
+
1319
+ $( this ).prev( "input[type='checkbox']" ).trigger( "click" );
1320
+
1321
+ } );
1322
+
1323
+ } );
1324
+ </script>
1325
+ </tr>
1326
+
1327
+ <?php
1328
+
1329
+ }
1330
+
1331
+ /**
1332
+ * Render 'editor' type option field.
1333
+ *
1334
+ * @since 3.0.0
1335
+ * @access public
1336
+ *
1337
+ * @param array $option Array of options data. May vary depending on option type.
1338
+ */
1339
+ public function render_editor_option_field( $option ) {
1340
+
1341
+ $editor_value = html_entity_decode( get_option( $option[ 'id' ] ) ); ?>
1342
+
1343
+ <tr valign="top" class="<?php echo esc_attr( $option[ 'id' ] ) . '-row'; ?>">
1344
+ <th scope="row"><?php echo sanitize_text_field( $option[ 'title' ] ); ?></th>
1345
+
1346
+ <td class="forminp forminp-<?php echo sanitize_title( $option[ 'type' ] ) ?>">
1347
+ <style type="text/css"><?php echo "div#wp-" . $option[ 'id' ] . "-wrap{ width: 70% !important; }"; ?></style>
1348
+
1349
+ <?php wp_editor( $editor_value , $option[ 'id' ] , array(
1350
+ 'wpautop' => true,
1351
+ 'textarea_name' => $option[ 'id' ],
1352
+ 'editor_height' => '300'
1353
+ ) ); ?>
1354
+ <br>
1355
+ <p class="desc"><?php echo isset( $option[ 'desc' ] ) ? $option[ 'desc' ] : ''; ?></p>
1356
+ </td>
1357
+ </tr>
1358
+
1359
+ <?php
1360
+
1361
+ }
1362
+
1363
+ /**
1364
+ * Render 'csv' type option field.
1365
+ *
1366
+ * @since 3.0.0
1367
+ * @access public
1368
+ *
1369
+ * @param array $option Array of options data. May vary depending on option type.
1370
+ */
1371
+ public function render_csv_option_field( $option ) {
1372
+
1373
+ ?>
1374
+
1375
+ <tr valign="top" class="<?php echo esc_attr( $option[ 'id' ] ) . '-row'; ?>">
1376
+ <th scope="row"><?php echo $option[ 'title' ]; ?></th>
1377
+
1378
+ <td class="forminp forminp-<?php echo sanitize_title( $option[ 'type' ] ) ?>">
1379
+ <input
1380
+ type = "text"
1381
+ name = "<?php echo $option[ 'id' ]; ?>"
1382
+ id = "<?php echo $option[ 'id' ]; ?>"
1383
+ class = "option-field <?php echo isset( $option[ 'class' ] ) ? $option[ 'class' ] : ''; ?>"
1384
+ style = "<?php echo isset( $option[ 'style' ] ) ? $option[ 'style' ] : 'width: 360px;'; ?>"
1385
+ value = "<?php echo get_option( $option[ 'id' ] ); ?>" >
1386
+ <br>
1387
+ <p class="desc"><?php echo isset( $option[ 'desc' ] ) ? $option[ 'desc' ] : ''; ?></p>
1388
+ </td>
1389
+
1390
+ <script>
1391
+ jQuery( document ).ready( function( $ ) {
1392
+
1393
+ $( '#<?php echo $option[ 'id' ]; ?>' ).selectize( {
1394
+ plugins : [ 'restore_on_backspace' , 'remove_button' , 'drag_drop' ],
1395
+ delimiter : ',',
1396
+ persist : false,
1397
+ create : function( input ) {
1398
+ return {
1399
+ value: input,
1400
+ text: input
1401
+ }
1402
+ }
1403
+ } );
1404
+
1405
+ } );
1406
+ </script>
1407
+ </tr>
1408
+
1409
+ <?php
1410
+
1411
+ }
1412
+
1413
+ /**
1414
+ * Render 'key_value' type option field. Do not need to be registered to WP Settings API.
1415
+ *
1416
+ * @since 3.0.0
1417
+ * @access public
1418
+ *
1419
+ * @param array $option Array of options data. May vary depending on option type.
1420
+ */
1421
+ public function render_key_value_option_field( $option ) {
1422
+
1423
+ $option_value = get_option( $option[ 'id' ] );
1424
+ if ( !is_array( $option_value ) )
1425
+ $option_value = array(); ?>
1426
+
1427
+ <tr valign="top" class="<?php echo esc_attr( $option[ 'id' ] ) . '-row'; ?>">
1428
+ <th scope="row"><?php echo $option[ 'title' ]; ?></th>
1429
+
1430
+ <td class="forminp forminp-<?php echo sanitize_title( $option[ 'type' ] ) ?>">
1431
+
1432
+ <div class="key-value-fields-container" data-field-id="<?php echo $option[ 'id' ]; ?>">
1433
+
1434
+ <header>
1435
+ <span class="key"><?php _e( 'Key' , 'thirstyaffiliates' ); ?></span>
1436
+ <span class="value"><?php _e( 'Value' , 'thirstyaffiliates' ); ?></span>
1437
+ </header>
1438
+
1439
+ <div class="fields">
1440
+
1441
+ <?php if ( empty( $option_value ) ) { ?>
1442
+
1443
+ <div class="data-set">
1444
+ <input type="text" class="field key-field">
1445
+ <input type="text" class="field value-field">
1446
+ <div class="controls">
1447
+ <span class="control add dashicons dashicons-plus-alt" autocomplete="off"></span>
1448
+ <span class="control delete dashicons dashicons-dismiss" autocomplete="off"></span>
1449
+ </div>
1450
+ </div>
1451
+
1452
+ <?php } else {
1453
+
1454
+ foreach ( $option_value as $key => $val ) { ?>
1455
+
1456
+ <div class="data-set">
1457
+ <input type="text" class="field key-field" value="<?php echo $key; ?>">
1458
+ <input type="text" class="field value-field" value="<?php echo $val; ?>">
1459
+ <div class="controls">
1460
+ <span class="control add dashicons dashicons-plus-alt" autocomplete="off"></span>
1461
+ <span class="control delete dashicons dashicons-dismiss" autocomplete="off"></span>
1462
+ </div>
1463
+ </div>
1464
+
1465
+ <?php }
1466
+
1467
+ } ?>
1468
+
1469
+ </div>
1470
+
1471
+ </div>
1472
+
1473
+ <p class="desc"><?php echo isset( $option[ 'desc' ] ) ? $option[ 'desc' ] : ''; ?></p>
1474
+
1475
+ </td>
1476
+ </tr>
1477
+
1478
+ <?php
1479
+
1480
+ }
1481
+
1482
+ /**
1483
+ * Render 'link' type option field. Do not need to be registered to WP Settings API.
1484
+ *
1485
+ * @since 3.0.0
1486
+ * @access public
1487
+ *
1488
+ * @param array $option Array of options data. May vary depending on option type.
1489
+ */
1490
+ public function render_link_option_field( $option ) {
1491
+
1492
+ ?>
1493
+
1494
+ <tr valign="top" class="<?php echo esc_attr( $option[ 'id' ] ) . '-row'; ?>">
1495
+ <th scope="row"><?php echo sanitize_text_field( $option[ 'title' ] ); ?></th>
1496
+ <td>
1497
+ <a id="<?php echo esc_attr( $option[ 'id' ] ); ?>" href="<?php echo $option[ 'link_url' ]; ?>" target="_blank"><?php echo $option[ 'link_text' ]; ?></a>
1498
+ <br>
1499
+ <p class="desc"><?php echo isset( $option[ 'desc' ] ) ? $option[ 'desc' ] : ''; ?></p>
1500
+ </td>
1501
+ </tr>
1502
+
1503
+ <?php
1504
+
1505
+ }
1506
+
1507
+ /**
1508
+ * Render option divider. Decorative field. Do not need to be registered to WP Settings API.
1509
+ *
1510
+ * @since 3.0.0
1511
+ * @access public
1512
+ *
1513
+ * @param array $option Array of options data. May vary depending on option type.
1514
+ */
1515
+ public function render_option_divider_option_field( $option ) {
1516
+
1517
+ ?>
1518
+
1519
+ <tr valign="top" class="<?php echo esc_attr( $option[ 'id' ] ) . '-row'; ?>">
1520
+ <th scope="row" colspan="2">
1521
+ <h3><?php echo sanitize_text_field( $option[ 'title' ] ); ?></h3>
1522
+ <?php echo isset( $option[ 'markup' ] ) ? $option[ 'markup' ] : ''; ?>
1523
+ </th>
1524
+ </tr>
1525
+
1526
+ <?php
1527
+
1528
+ }
1529
+
1530
+ /**
1531
+ * Render custom "migration_controls" field. Do not need to be registered to WP Settings API.
1532
+ *
1533
+ * @since 3.0.0
1534
+ * @access public
1535
+ *
1536
+ * @param array $option Array of options data. May vary depending on option type.
1537
+ */
1538
+ public function render_migration_controls_option_field( $option ) {
1539
+
1540
+ $database_processing = apply_filters( 'ta_database_processing' , true ); // Flag to determine if another application is processing the db. ex. data downgrade.
1541
+ $processing = "";
1542
+ $disabled = false;
1543
+
1544
+ if ( get_option( Plugin_Constants::MIGRATION_COMPLETE_FLAG ) === 'no' ) {
1545
+
1546
+ $processing = "-processing";
1547
+ $disabled = true;
1548
+
1549
+ } ?>
1550
+
1551
+ <tr valign="top" class="<?php echo esc_attr( $option[ 'id' ] ) . '-row'; ?>">
1552
+
1553
+ <th scope="row" class="title_desc"><?php echo sanitize_text_field( $option[ 'title' ] ); ?></th>
1554
+
1555
+ <td class="forminp forminp-<?php echo sanitize_title( $option[ 'type' ] ) ?> <?php echo $processing; ?>">
1556
+
1557
+ <?php if ( !$database_processing ) { ?>
1558
+
1559
+ <p><?php _e( 'Another application is currently processing the database. Please wait for this to complete.' , 'thirstyaffiliates' ); ?></p>
1560
+
1561
+ <?php } else { ?>
1562
+
1563
+ <input
1564
+ <?php echo $disabled ? "disabled" : ""; ?>
1565
+ type="button"
1566
+ id="<?php echo esc_attr( $option[ 'id' ] ); ?>"
1567
+ class="button button-primary"
1568
+ style="<?php echo isset( $option[ 'style' ] ) ? esc_attr( $option[ 'style' ] ) : ''; ?>"
1569
+ value="<?php _e( 'Migrate' , 'thirstyaffiliates' ); ?>">
1570
+
1571
+ <span class="spinner"></span>
1572
+ <p class="status"><?php _e( 'Migrating data. Please wait...' , 'thirstyaffiliates' ); ?></p>
1573
+
1574
+ <?php } ?>
1575
+
1576
+ <br /><br />
1577
+ <p class="desc"><?php echo isset( $option[ 'desc' ] ) ? $option[ 'desc' ] : ''; ?></p>
1578
+
1579
+ </td>
1580
+
1581
+ </tr>
1582
+
1583
+ <?php
1584
+
1585
+ }
1586
+
1587
+ /**
1588
+ * Render custom "social_links" field. Do not need to be registered to WP Settings API.
1589
+ *
1590
+ * @since 3.1.0
1591
+ * @access public
1592
+ *
1593
+ * @param array $option Array of options data. May vary depending on option type.
1594
+ */
1595
+ public function render_social_links_option_field( $option ) {
1596
+
1597
+ ?>
1598
+ <tr valign="top" class="<?php echo esc_attr( $option[ 'id' ] ) . '-row'; ?>">
1599
+ <th scope="row"><?php echo sanitize_text_field( $option[ 'title' ] ); ?></th>
1600
+ <td>
1601
+ <ul>
1602
+ <li>
1603
+ <a href="https://www.facebook.com/thirstyaffiliates/"><?php _e( 'Like us on Facebook' , 'thirstyaffiliates' ); ?></a>
1604
+ <iframe src="//www.facebook.com/plugins/like.php?href=https%3A%2F%2Fwww.facebook.com%2Fthirstyaffiliates&amp;send=false&amp;layout=button_count&amp;width=450&amp;show_faces=false&amp;font=arial&amp;colorscheme=light&amp;action=like&amp;height=21" scrolling="no" frameborder="0" style="border:none; overflow:hidden; width:450px; height:21px; vertical-align: bottom;" allowTransparency="true"></iframe>
1605
+ </li>
1606
+ <li>
1607
+ <a href="http://twitter.com/thirstyaff"><?php _e( 'Follow us on Twitter' , 'thirstyaffiliates' ); ?></a>
1608
+ <a href="https://twitter.com/thirstyaff" class="twitter-follow-button" data-show-count="true" style="vertical-align: bottom;">Follow @thirstyaff</a><script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?"http":"https";if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+"://platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document, "script", "twitter-wjs");</script>
1609
+ </li>
1610
+ <li>
1611
+ <a href="https://www.linkedin.com/company-beta/2928598/"><?php _e( 'Follow us on Linkedin' , 'thirstyaffiliates' ); ?></a>
1612
+ </li>
1613
+ <li>
1614
+ <a href="https://thirstyaffiliates.com/affiliates?utm_source=Free%20Plugin&utm_medium=Help&utm_campaign=Affiliates%20Link" target="_blank"><?php _e( 'Join Our Affiliate Program' , 'thirstyaffiliates' ); ?></a>
1615
+ <?php _e( '(up to 30% commisions)' , 'thirstyaffiliates' ); ?>
1616
+ </li>
1617
+ </ul>
1618
+ </td>
1619
+ </tr>
1620
+ <?php
1621
+ }
1622
+
1623
+ /**
1624
+ * Render custom "export_global_settings" field. Do not need to be registered to WP Settings API.
1625
+ *
1626
+ * @since 3.0.0
1627
+ * @access public
1628
+ *
1629
+ * @param array $option Array of options data. May vary depending on option type.
1630
+ */
1631
+ public function render_export_global_settings_option_field( $option ) {
1632
+
1633
+ $global_settings_string = $this->get_global_settings_string(); ?>
1634
+
1635
+ <tr valign="top" class="<?php echo esc_attr( $option[ 'id' ] ) . '-row'; ?>">
1636
+ <th scope="row" class="title_desc"><?php echo sanitize_text_field( $option[ 'title' ] ); ?></th>
1637
+ <td class="forminp forminp-<?php echo sanitize_title( $option[ 'type' ] ) ?>">
1638
+ <textarea
1639
+ name="<?php echo esc_attr( $option[ 'id' ] ); ?>"
1640
+ id="<?php echo esc_attr( $option[ 'id' ] ); ?>"
1641
+ style="<?php echo isset( $option[ 'style' ] ) ? esc_attr( $option[ 'style' ] ) : ''; ?>"
1642
+ class="<?php echo isset( $option[ 'class' ] ) ? esc_attr( $option[ 'class' ] ) : ''; ?>"
1643
+ placeholder="<?php echo isset( $option[ 'placeholder' ] ) ? esc_attr( $option[ 'placeholder' ] ) : ''; ?>"
1644
+ autocomplete="off"
1645
+ readonly
1646
+ rows="10"><?php echo $global_settings_string; ?></textarea>
1647
+ <div class="controls">
1648
+ <a id="copy-settings-string" data-clipboard-target="#<?php echo esc_attr( $option[ 'id' ] ); ?>"><?php _e( 'Copy' , 'thirstyaffiliates' ); ?></a>
1649
+ </div>
1650
+ </td>
1651
+ </tr>
1652
+
1653
+ <?php
1654
+
1655
+ }
1656
+
1657
+ /**
1658
+ * Render custom "import_global_settings" field. Do not need to be registered to WP Settings API.
1659
+ *
1660
+ * @since 3.0.0
1661
+ * @access public
1662
+ *
1663
+ * @param array $option Array of options data. May vary depending on option type.
1664
+ */
1665
+ public function render_import_global_settings_option_field( $option ) {
1666
+
1667
+ ?>
1668
+
1669
+ <tr valign="top" class="<?php echo esc_attr( $option[ 'id' ] ) . '-row'; ?>">
1670
+ <th scope="row" class="title_desc">
1671
+ <label for="<?php echo esc_attr( $option[ 'id' ] ); ?>"><?php echo sanitize_text_field( $option[ 'title' ] ); ?></label>
1672
+ </th>
1673
+ <td class="forminp forminp-<?php echo sanitize_title( $option[ 'type' ] ) ?>">
1674
+ <textarea
1675
+ name="<?php echo esc_attr( $option[ 'id' ] ); ?>"
1676
+ id="<?php echo esc_attr( $option[ 'id' ] ); ?>"
1677
+ style="<?php echo isset( $option[ 'style' ] ) ? esc_attr( $option[ 'style' ] ) : ''; ?>"
1678
+ class="<?php echo isset( $option[ 'class' ] ) ? esc_attr( $option[ 'class' ] ) : ''; ?>"
1679
+ placeholder="<?php echo esc_attr( $option[ 'placeholder' ] ); ?>"
1680
+ autocomplete="off"
1681
+ rows="10"></textarea>
1682
+ <p class="desc"><?php echo isset( $option[ 'description' ] ) ? $option[ 'description' ] : ''; ?></p>
1683
+ <div class="controls">
1684
+ <span class="spinner"></span>
1685
+ <input type="button" id="import-setting-button" class="button button-primary" value="<?php _e( 'Import Settings' , 'thirstyaffiliates' ); ?>">
1686
+ </div>
1687
+ </td>
1688
+ </tr>
1689
+
1690
+ <?php
1691
+
1692
+ }
1693
+
1694
+
1695
+
1696
+
1697
+ /*
1698
+ |--------------------------------------------------------------------------
1699
+ | "key_value" option field type helpers
1700
+ |--------------------------------------------------------------------------
1701
+ */
1702
+
1703
+ /**
1704
+ * Load styling relating to 'key_value' field type.
1705
+ *
1706
+ * @since 3.0.0
1707
+ * @access public
1708
+ */
1709
+ public function load_key_value_option_field_type_styling() {
1710
+
1711
+ ?>
1712
+
1713
+ <style>
1714
+ .key-value-fields-container header span {
1715
+ display: inline-block;
1716
+ font-weight: 600;
1717
+ margin-bottom: 8px;
1718
+ }
1719
+ .key-value-fields-container header .key {
1720
+ width: 144px;
1721
+ }
1722
+ .key-value-fields-container header .value {
1723
+ width: 214px;
1724
+ }
1725
+ .key-value-fields-container .fields .data-set {
1726
+ margin-bottom: 8px;
1727
+ }
1728
+ .key-value-fields-container .fields .data-set:last-child {
1729
+ margin-bottom: 0;
1730
+ }
1731
+ .key-value-fields-container .fields .data-set .key-field {
1732
+ width: 140px;
1733
+ margin-left: 0;
1734
+ }
1735
+ .key-value-fields-container .fields .data-set .value-field {
1736
+ width: 215px;
1737
+ }
1738
+ .key-value-fields-container .fields .data-set .controls {
1739
+ display: none;
1740
+ }
1741
+ .key-value-fields-container .fields .data-set:hover .controls {
1742
+ display: inline-block;
1743
+ }
1744
+ .key-value-fields-container .fields .data-set .controls .control {
1745
+ cursor: pointer;
1746
+ }
1747
+ .key-value-fields-container .fields .data-set .controls .add {
1748
+ color: green;
1749
+ }
1750
+ .key-value-fields-container .fields .data-set .controls .delete {
1751
+ color: red;
1752
+ }
1753
+ </style>
1754
+
1755
+ <?php
1756
+
1757
+ }
1758
+
1759
+ /**
1760
+ * Load scripts relating to 'key_value' field type.
1761
+ *
1762
+ * @since 3.0.0
1763
+ * @access public
1764
+ */
1765
+ public function load_key_value_option_field_type_script() {
1766
+
1767
+ ?>
1768
+
1769
+ <script>
1770
+
1771
+ jQuery( document ).ready( function( $ ) {
1772
+
1773
+ // Hide the delete button if only 1 data set is available
1774
+ function init_data_set_controls() {
1775
+
1776
+ $( ".key-value-fields-container" ).each( function() {
1777
+
1778
+ if ( $( this ).find( ".data-set" ).length === 1 )
1779
+ $( this ).find( ".data-set .controls .delete" ).css( "display" , "none" );
1780
+ else
1781
+ $( this ).find( ".data-set .controls .delete" ).removeAttr( "style" );
1782
+
1783
+ } );
1784
+
1785
+ }
1786
+
1787
+ init_data_set_controls();
1788
+
1789
+
1790
+ // Attach "add" and "delete" events
1791
+ $( ".key-value-fields-container" ).on( "click" , ".controls .add" , function() {
1792
+
1793
+ let $data_set = $( this ).closest( '.data-set' );
1794
+
1795
+ $data_set.after( "<div class='data-set'>" +
1796
+ "<input type='text' class='field key-field' autocomplete='off'> " +
1797
+ "<input type='text' class='field value-field' autocomplete='off'>" +
1798
+ "<div class='controls'>" +
1799
+ "<span class='control add dashicons dashicons-plus-alt'></span>" +
1800
+ "<span class='control delete dashicons dashicons-dismiss'></span>" +
1801
+ "</div>" +
1802
+ "</div>" );
1803
+
1804
+ init_data_set_controls();
1805
+
1806
+ } );
1807
+
1808
+ $( ".key-value-fields-container" ).on( "click" , ".controls .delete" , function() {
1809
+
1810
+ let $data_set = $( this ).closest( '.data-set' );
1811
+
1812
+ $data_set.remove();
1813
+
1814
+ init_data_set_controls();
1815
+
1816
+ } );
1817
+
1818
+
1819
+ // Construct hidden fields for each of "key_value" option field types upon form submission
1820
+ $( "form" ).submit( function() {
1821
+
1822
+ $( ".key-value-fields-container" ).each( function() {
1823
+
1824
+ var $this = $( this ),
1825
+ field_id = $this.attr( "data-field-id" ),
1826
+ field_inputs = "";
1827
+
1828
+ $this.find( ".data-set" ).each( function() {
1829
+
1830
+ var $this = $( this ),
1831
+ key_field = $.trim( $this.find( ".key-field" ).val() ),
1832
+ value_field = $.trim( $this.find( ".value-field" ).val() );
1833
+
1834
+ if ( key_field !== "" && value_field !== "" )
1835
+ field_inputs += "<input type='hidden' name='" + field_id + "[" + key_field + "]' value='" + value_field + "'>";
1836
+
1837
+ } );
1838
+
1839
+ $this.append( field_inputs );
1840
+
1841
+ } );
1842
+
1843
+ } );
1844
+
1845
+ } );
1846
+
1847
+ </script>
1848
+
1849
+ <?php
1850
+
1851
+ }
1852
+
1853
+
1854
+
1855
+
1856
+ /*
1857
+ |--------------------------------------------------------------------------
1858
+ | Settings helper
1859
+ |--------------------------------------------------------------------------
1860
+ */
1861
+
1862
+ /**
1863
+ * Get global settings string via ajax.
1864
+ *
1865
+ * @since 3.0.0
1866
+ * @access public
1867
+ */
1868
+ public function ajax_get_global_settings_string() {
1869
+
1870
+ if ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX )
1871
+ $response = array( 'status' => 'fail' , 'error_msg' => __( 'Invalid AJAX call' , 'thirstyaffiliates' ) );
1872
+ else {
1873
+
1874
+ $global_settings_string = $this->get_global_settings_string();
1875
+
1876
+ if ( is_wp_error( $global_settings_string ) )
1877
+ $response = array( 'status' => 'fail' , 'error_msg' => $global_settings_string->get_error_message() );
1878
+ else
1879
+ $response = array( 'status' => 'success' , 'global_settings_string' => $global_settings_string );
1880
+
1881
+ }
1882
+
1883
+ @header( 'Content-Type: application/json; charset=' . get_option( 'blog_charset' ) );
1884
+ echo wp_json_encode( $response );
1885
+ wp_die();
1886
+
1887
+ }
1888
+
1889
+ /**
1890
+ * Get global settings string.
1891
+ *
1892
+ * @since 3.0.0
1893
+ * @access public
1894
+ *
1895
+ * @return WP_Error|string WP_Error on error, Base 64 encoded serialized global plugin settings otherwise.
1896
+ */
1897
+ public function get_global_settings_string() {
1898
+
1899
+ if ( !$this->_helper_functions->current_user_authorized() )
1900
+ return new \WP_Error( 'ta_unauthorized_operation_export_settings' , __( 'Unauthorized operation. Only authorized accounts can access global plugin settings string' , 'thirstyaffiliates' ) );
1901
+
1902
+ $global_settings_arr = array();
1903
+ foreach ( $this->_exportable_options as $key => $default )
1904
+ $global_settings_arr[ $key ] = get_option( $key , $default );
1905
+
1906
+ return base64_encode( serialize( $global_settings_arr ) );
1907
+
1908
+ }
1909
+
1910
+ /**
1911
+ * Import settings via ajax.
1912
+ *
1913
+ * @access public
1914
+ * @since 3.0.0
1915
+ */
1916
+ public function ajax_import_settings() {
1917
+
1918
+ if ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX )
1919
+ $response = array( 'status' => 'fail' , 'error_msg' => __( 'Invalid AJAX call' , 'thirstyaffiliates' ) );
1920
+ elseif ( !isset( $_POST[ 'ta_settings_string' ] ) )
1921
+ $response = array( 'status' => 'fail' , 'error_msg' => __( 'Required parameter not passed' , 'thirstyaffiliates' ) );
1922
+ else {
1923
+
1924
+ $result = $this->import_settings( filter_var( $_POST[ 'ta_settings_string' ] , FILTER_SANITIZE_STRING ) );
1925
+
1926
+ if ( is_wp_error( $result ) )
1927
+ $response = array( 'status' => 'fail' , 'error_msg' => $result->get_error_message() );
1928
+ else
1929
+ $response = array( 'status' => 'success' , 'success_msg' => __( 'Settings successfully imported' , 'thirstyaffiliates' ) );
1930
+
1931
+ }
1932
+
1933
+ @header( 'Content-Type: application/json; charset=' . get_option( 'blog_charset' ) );
1934
+ echo wp_json_encode( $response );
1935
+ wp_die();
1936
+
1937
+ }
1938
+
1939
+ /**
1940
+ * Import settings from external global settings string.
1941
+ *
1942
+ * @since 3.0.0
1943
+ * @access public
1944
+ *
1945
+ * @param string $global_settings_string Settings string.
1946
+ * @return WP_Error | boolean WP_Error instance on failure, boolean true otherwise.
1947
+ */
1948
+ public function import_settings( $global_settings_string ) {
1949
+
1950
+ if ( !$this->_helper_functions->current_user_authorized() )
1951
+ return new \WP_Error( 'ta_unauthorized_operation_import_settings' , __( 'Unauthorized operation. Only authorized accounts can import settings' , 'thirstyaffiliates' ) );
1952
+
1953
+ $settings_arr = @unserialize( base64_decode( $global_settings_string ) );
1954
+
1955
+ if ( !is_array( $settings_arr ) )
1956
+ return new \WP_Error( 'ta_invalid_global_settings_string' , __( 'Invalid global settings string' , 'thirstyaffiliates' ) , array( 'global_settings_string' => $global_settings_string ) );
1957
+ else {
1958
+
1959
+ foreach ( $settings_arr as $key => $val ) {
1960
+
1961
+ if ( !array_key_exists( $key , $this->_exportable_options ) )
1962
+ continue;
1963
+
1964
+ update_option( $key , $val );
1965
+
1966
+ }
1967
+
1968
+ return true;
1969
+
1970
+ }
1971
+
1972
+ }
1973
+
1974
+ /**
1975
+ * Post update option callback for link prefix options.
1976
+ *
1977
+ * @since 3.0.0
1978
+ * @access public
1979
+ *
1980
+ * @param string $old_value Old option value before the update.
1981
+ * @param string $value New value saved.
1982
+ * @param string $option Option id.
1983
+ */
1984
+ public function link_prefix_post_update_callback( $value , $old_value , $option ) {
1985
+
1986
+ if ( $option === 'ta_link_prefix' && $value === 'custom' )
1987
+ return $value;
1988
+
1989
+ if ( $option === 'ta_link_prefix_custom' && get_option( 'ta_link_prefix' ) !== 'custom' )
1990
+ return $value;
1991
+
1992
+ $used_link_prefixes = maybe_unserialize( get_option( 'ta_used_link_prefixes' , array() ) );
1993
+ $check_duplicate = array_search( $value , $used_link_prefixes );
1994
+
1995
+ if ( $check_duplicate !== false )
1996
+ unset( $used_link_prefixes[ $check_duplicate ] );
1997
+
1998
+ $used_link_prefixes[] = sanitize_text_field( $value );
1999
+ $count = count( $used_link_prefixes );
2000
+
2001
+ if ( $count > 10 )
2002
+ $used_link_prefixes = array_slice( $used_link_prefixes , $count - 10 , 10 , false );
2003
+
2004
+ update_option( 'ta_used_link_prefixes' , array_unique( $used_link_prefixes ) );
2005
+
2006
+ return $value;
2007
+ }
2008
+
2009
+ /**
2010
+ * Restrict modules settings.
2011
+ *
2012
+ * @since 3.6
2013
+ * @access public
2014
+ */
2015
+ public function restrict_module_settings() {
2016
+
2017
+ $screen = get_current_screen();
2018
+ $tab = isset( $_GET[ 'tab' ] ) ? sanitize_text_field( $_GET[ 'tab' ] ) : null;
2019
+
2020
+ if ( $screen->id !== 'thirstylink_page_thirsty-settings' || ! $tab ) return;
2021
+
2022
+ $default_values = array();
2023
+ $registered = apply_filters( 'ta_modules_settings_options' , array(
2024
+ array( 'id' => 'ta_enable_stats_reporting_module' , 'default' => 'yes' ),
2025
+ array( 'id' => 'ta_enable_link_fixer' , 'default' => 'yes' ),
2026
+ array( 'id' => 'ta_uncloak_link_per_link' , 'default' => 'no' )
2027
+ ) );
2028
+
2029
+ if ( function_exists( 'array_column' ) )
2030
+ $default_values = array_column( $registered , 'default' , 'id' );
2031
+ else {
2032
+
2033
+ foreach ( $registered as $module )
2034
+ $default_values[ $module[ 'id' ] ] = isset( $module[ 'default' ] ) ? $module[ 'default' ] : 'yes';
2035
+ }
2036
+
2037
+ $modules = array_keys( $default_values );
2038
+ $current_module = str_replace( array( 'tap_' , 'amazon_settings_section' , '_settings' ) , array( 'tap_enable_' , 'azon' , '' ) , $tab );
2039
+ $current_module = apply_filters( 'ta_restrict_current_module' , $current_module , $tab , $modules );
2040
+ $default = in_array( $current_module , $modules ) ? $default_values[ $current_module ] : null;
2041
+
2042
+ if ( ! is_null( $default ) && get_option( $current_module , $default ) !== 'yes' )
2043
+ wp_die( __( "Sorry, you are not allowed to access this page." , 'thirstyaffiliates' ) );
2044
+
2045
+ }
2046
+
2047
+
2048
+
2049
+
2050
+ /*
2051
+ |--------------------------------------------------------------------------
2052
+ | Implemented Interface Methods
2053
+ |--------------------------------------------------------------------------
2054
+ */
2055
+
2056
+ /**
2057
+ * Execute codes that needs to run plugin activation.
2058
+ *
2059
+ * @since 3.0.0
2060
+ * @access public
2061
+ * @implements ThirstyAffiliates\Interfaces\Activatable_Interface
2062
+ */
2063
+ public function activate() {
2064
+
2065
+ if ( get_option( 'ta_settings_initialized' ) !== 'yes' ) {
2066
+
2067
+ update_option( 'ta_link_prefix' , 'recommends' );
2068
+ update_option( 'ta_link_prefix_custom' , '' );
2069
+ update_option( 'ta_used_link_prefixes' , array( 'recommends' ) );
2070
+ update_option( 'ta_enable_javascript_frontend_redirect' , 'yes' );
2071
+ update_option( 'ta_show_enable_js_redirect_notice' , 'no' );
2072
+ update_option( 'ta_dismiss_marketing_notice_option' , 'no' );
2073
+ update_option( 'ta_settings_initialized' , 'yes' );
2074
+ }
2075
+ }
2076
+
2077
+ /**
2078
+ * Execute codes that needs to run on plugin initialization.
2079
+ *
2080
+ * @since 3.0.0
2081
+ * @access public
2082
+ * @implements ThirstyAffiliates\Interfaces\Initiable_Interface
2083
+ */
2084
+ public function initialize() {
2085
+
2086
+ add_action( 'wp_ajax_ta_get_global_settings_string' , array( $this , 'ajax_get_global_settings_string' ) );
2087
+ add_action( 'wp_ajax_ta_import_settings' , array( $this , 'ajax_import_settings' ) );
2088
+
2089
+ }
2090
+
2091
+ /**
2092
+ * Execute model.
2093
+ *
2094
+ * @implements WordPress_Plugin_Boilerplate\Interfaces\Model_Interface
2095
+ *
2096
+ * @since 3.0.0
2097
+ * @access public
2098
+ */
2099
+ public function run() {
2100
+
2101
+ add_action( 'admin_init' , array( $this , 'init_plugin_settings' ) );
2102
+ add_action( 'admin_menu' , array( $this , 'add_settings_page' ) );
2103
+ add_action( 'current_screen' , array( $this , 'restrict_module_settings' ) );
2104
+
2105
+ add_action( 'ta_before_settings_form' , array( $this , 'load_key_value_option_field_type_styling' ) );
2106
+ add_action( 'ta_before_settings_form' , array( $this , 'load_key_value_option_field_type_script' ) );
2107
+ add_action( 'pre_update_option_ta_link_prefix' , array( $this , 'link_prefix_post_update_callback' ) , 10 , 3 );
2108
+ add_action( 'pre_update_option_ta_link_prefix_custom' , array( $this , 'link_prefix_post_update_callback' ) , 10 , 3 );
2109
+
2110
+ add_filter( 'ta_admin_interfaces' , array( $this , 'register_admin_interfaces' ) );
2111
+ add_filter( 'ta_menu_items' , array( $this , 'register_admin_menu_items' ) );
2112
+ }
2113
+
2114
+ }
Models/Shortcodes.php CHANGED
@@ -1,303 +1,303 @@
1
- <?php
2
-
3
- namespace ThirstyAffiliates\Models;
4
-
5
- use ThirstyAffiliates\Abstracts\Abstract_Main_Plugin_Class;
6
-
7
- use ThirstyAffiliates\Interfaces\Model_Interface;
8
-
9
- use ThirstyAffiliates\Helpers\Plugin_Constants;
10
- use ThirstyAffiliates\Helpers\Helper_Functions;
11
-
12
- /**
13
- * Model that houses the logic for permalink rewrites and affiliate link redirections.
14
- *
15
- * @since 3.0.0
16
- */
17
- class Shortcodes implements Model_Interface {
18
-
19
- /*
20
- |--------------------------------------------------------------------------
21
- | Class Properties
22
- |--------------------------------------------------------------------------
23
- */
24
-
25
- /**
26
- * Property that holds the single main instance of Shortcodes.
27
- *
28
- * @since 3.0.0
29
- * @access private
30
- * @var Redirection
31
- */
32
- private static $_instance;
33
-
34
- /**
35
- * Model that houses the main plugin object.
36
- *
37
- * @since 3.0.0
38
- * @access private
39
- * @var Redirection
40
- */
41
- private $_main_plugin;
42
-
43
- /**
44
- * Model that houses all the plugin constants.
45
- *
46
- * @since 3.0.0
47
- * @access private
48
- * @var Plugin_Constants
49
- */
50
- private $_constants;
51
-
52
- /**
53
- * Property that houses all the helper functions of the plugin.
54
- *
55
- * @since 3.0.0
56
- * @access private
57
- * @var Helper_Functions
58
- */
59
- private $_helper_functions;
60
-
61
-
62
-
63
-
64
- /*
65
- |--------------------------------------------------------------------------
66
- | Class Methods
67
- |--------------------------------------------------------------------------
68
- */
69
-
70
- /**
71
- * Class constructor.
72
- *
73
- * @since 3.0.0
74
- * @access public
75
- *
76
- * @param Abstract_Main_Plugin_Class $main_plugin Main plugin object.
77
- * @param Plugin_Constants $constants Plugin constants object.
78
- * @param Helper_Functions $helper_functions Helper functions object.
79
- */
80
- public function __construct( Abstract_Main_Plugin_Class $main_plugin , Plugin_Constants $constants , Helper_Functions $helper_functions ) {
81
-
82
- $this->_constants = $constants;
83
- $this->_helper_functions = $helper_functions;
84
-
85
- $main_plugin->add_to_all_plugin_models( $this );
86
-
87
- }
88
-
89
- /**
90
- * Ensure that only one instance of this class is loaded or can be loaded ( Singleton Pattern ).
91
- *
92
- * @since 3.0.0
93
- * @access public
94
- *
95
- * @param Abstract_Main_Plugin_Class $main_plugin Main plugin object.
96
- * @param Plugin_Constants $constants Plugin constants object.
97
- * @param Helper_Functions $helper_functions Helper functions object.
98
- * @return Redirection
99
- */
100
- public static function get_instance( Abstract_Main_Plugin_Class $main_plugin , Plugin_Constants $constants , Helper_Functions $helper_functions ) {
101
-
102
- if ( !self::$_instance instanceof self )
103
- self::$_instance = new self( $main_plugin , $constants , $helper_functions );
104
-
105
- return self::$_instance;
106
-
107
- }
108
-
109
- /**
110
- * Checks if the given ID needs to be uncloaked
111
- *
112
- * @since 3.0.0
113
- * @access public
114
- *
115
- * @param int $link_id Affiliate Link post ID.
116
- * @return boolean.
117
- */
118
- public function is_link_to_be_uncloaked( $link_id ) {
119
-
120
- if ( get_option( 'ta_uncloak_link_per_link' ) == 'yes' ) {
121
-
122
- $links_to_uncloak = maybe_unserialize( get_option( 'ta_links_to_uncloak' , array() ) );
123
-
124
- if ( in_array( $link_id , $links_to_uncloak ) )
125
- return true;
126
-
127
- }
128
-
129
- if ( get_option( 'ta_uncloak_link_per_category' ) == 'yes' && $category_to_uncloak = get_option( 'ta_category_to_uncloak' ) ) {
130
-
131
- if ( has_term( $category_to_uncloak , Plugin_Constants::AFFILIATE_LINKS_TAX , $link_id ) )
132
- return true;
133
- }
134
-
135
- return false;
136
- }
137
-
138
- /**
139
- * thirstylink shortcode.
140
- * example: [thirstylink ids="10,15,18,20"]Affiliate Link[/thirstylink]
141
- *
142
- * @since 3.0.0
143
- * @since 3.3.0 Add data-nojs attribute support.
144
- * @since 3.4.0 Add additional classes field support.
145
- * @access public
146
- *
147
- * @param array $atts Shortcode attributes.
148
- * @param string $content Shortcode content.
149
- * @return string Processed shortcode output.
150
- */
151
- public function thirstylink_shortcode( $atts , $content = '' ) {
152
-
153
- global $post;
154
-
155
- $post_id = is_a( $post , 'WP_Post' ) ? $post->ID : 0;
156
-
157
- $atts = shortcode_atts( array(
158
- 'ids' => '',
159
- 'linkid' => '',
160
- 'linktext' => '',
161
- 'class' => '',
162
- 'rel' => '',
163
- 'target' => '',
164
- 'title' => ''
165
- ), $atts , 'thirstylink' );
166
-
167
- // get all link attributes from $atts
168
- $link_attributes = array_diff_assoc(
169
- $atts,
170
- array(
171
- 'ids' => $atts[ 'ids' ],
172
- 'linkid' => $atts[ 'linkid' ],
173
- 'linktext' => $atts[ 'linktext' ],
174
- )
175
- );
176
-
177
- // get the link ID
178
- if ( ! $atts[ 'linkid' ] ) {
179
-
180
- $ids = isset( $atts[ 'ids' ] ) ? array_map( 'intval' , explode( ',' , $atts[ 'ids' ] ) ) : array();
181
- $key = rand( 0 , count( $ids ) - 1 );
182
- $link_id = $ids[ $key ];
183
- } else
184
- $link_id = (int) $atts[ 'linkid' ];
185
-
186
- $output = '';
187
-
188
- if ( $link_id && get_post_type( $link_id ) == Plugin_Constants::AFFILIATE_LINKS_CPT ) {
189
-
190
- // load thirstylink
191
- $thirstylink = new Affiliate_Link( $link_id );
192
- $uncloak_link = $thirstylink->is( 'uncloak_link' );
193
-
194
- // get the link URL
195
- $link_attributes[ 'href' ] = ( $uncloak_link ) ? apply_filters( 'ta_uncloak_link_url' , $thirstylink->get_prop( 'destination_url' ) , $thirstylink ) : $thirstylink->get_prop( 'permalink' );
196
-
197
- if ( ! $uncloak_link )
198
- $link_attributes[ 'href' ] = $this->clean_url( $link_attributes[ 'href' ] );
199
-
200
- // get link text content default if no value is set
201
- if ( empty( $content ) && $atts[ 'linktext' ] )
202
- $content = $atts[ 'linktext' ]; // backward compatibility to get the link text content.
203
- else if ( empty( $content ) )
204
- $content = $thirstylink->get_prop( 'name' );
205
-
206
- // check for nofollow defaults if no value is set
207
- if ( empty( $link_attributes[ 'rel' ] ) ) {
208
-
209
- $link_attributes[ 'rel' ] = $thirstylink->is( 'no_follow' ) ? 'nofollow' : '';
210
-
211
- if ( $thirstylink->get_prop( 'rel_tags' ) )
212
- $link_attributes[ 'rel' ] = trim( $link_attributes[ 'rel' ] . ' ' . $thirstylink->get_prop( 'rel_tags' ) );
213
- }
214
-
215
- // check for new window defaults if no value is set
216
- if ( empty( $link_attributes[ 'target' ] ) ) {
217
-
218
- $link_attributes[ 'target' ] = $thirstylink->is( 'new_window' ) ? '_blank' : '';
219
- }
220
-
221
- // provide default class value if it is not set
222
- if ( empty( $link_attributes[ 'class' ] ) ){
223
- $thirsty_link_class = strpos( $content , '<img ' ) !== false ? 'thirstylinkimg' : 'thirstylink';
224
- $link_attributes[ 'class' ] = get_option( 'ta_disable_thirsty_link_class' ) !== 'yes' ? $thirsty_link_class : '';
225
-
226
- if ( $thirstylink->get_prop( 'css_classes' ) )
227
- $link_attributes[ 'class' ] = trim( $link_attributes[ 'class' ] . ' ' . $thirstylink->get_prop( 'css_classes' ) );
228
- }
229
-
230
-
231
- // provide default class value if it is not set
232
- if ( empty( $link_attributes[ 'title' ] ) && get_option( 'ta_disable_title_attribute' ) !== 'yes' )
233
- $link_attributes[ 'title' ] = $thirstylink->get_prop( 'name' );
234
-
235
- // remove double quote character on title attribute.
236
- $link_attributes[ 'title' ] = esc_attr( str_replace( '"' , '' , $link_attributes[ 'title' ] ) );
237
-
238
- // add data-link_id attribute if affiliate link is uncloaked.
239
- if ( $uncloak_link )
240
- $link_attributes[ 'data-linkid' ] = $link_id;
241
-
242
- // tag links as "nojs" to disable JS redirect for them.
243
- if ( get_option( 'ta_enable_javascript_frontend_redirect' ) == 'yes' )
244
- $link_attributes[ 'data-nojs' ] = apply_filters( 'ta_nojs_redirect_attribute' , false , $thirstylink );
245
-
246
- // allow the ability to add custom link attributes
247
- $link_attributes = apply_filters( 'ta_link_insert_extend_data_attributes' , $link_attributes , $thirstylink , $post_id );
248
-
249
- // Build the link ready for output
250
- $output .= '<a';
251
-
252
- foreach ( $link_attributes as $name => $value ) {
253
- // Handle square bracket escaping (used for some addons, eg. Google Analytics click tracking)
254
- $value = html_entity_decode( $value );
255
- $value = preg_replace( '/&#91;/' , '[' , $value );
256
- $value = preg_replace( '/&#93;/' , ']' , $value );
257
- $output .= ! empty($value) ? ' ' . $name . '="' . trim( esc_attr( $value ) ) . '"' : '';
258
- }
259
-
260
- $output .= ' data-shortcode="true">' . do_shortcode( $content ) . '</a>';
261
-
262
-
263
- } elseif ( current_user_can( 'edit_published_posts' ) )
264
- $output .= '<span style="color: #0000ff;">' . __( 'SHORTCODE ERROR: ThirstyAffiliates did not detect a valid link id, please check your short code!' , 'thirstyaffiliates' ) . '</span>';
265
- else
266
- $output = $content;
267
-
268
- return $output;
269
- }
270
-
271
- /**
272
- * Remove parameters in the URL added via the get_permalink hook and escape it.
273
- * Note: This is needed for WPML support.
274
- *
275
- * @since 3.2.1
276
- * @access private
277
- *
278
- * @param string $raw_url Raw URL.
279
- * @param string Cleaned URL.
280
- */
281
- private function clean_url( $raw_url ) {
282
-
283
- $parse = parse_url( $raw_url );
284
- extract( $parse );
285
-
286
- // rebuild url excluding parameters.
287
- $url = $scheme . '://' . $host;
288
- $url .= isset( $path ) ? $path : '';
289
-
290
- return esc_url( $url );
291
- }
292
-
293
- /**
294
- * Execute shortcodes class.
295
- *
296
- * @since 3.0.0
297
- * @access public
298
- */
299
- public function run() {
300
-
301
- add_shortcode( 'thirstylink' , array( $this , 'thirstylink_shortcode' ) );
302
- }
303
- }
1
+ <?php
2
+
3
+ namespace ThirstyAffiliates\Models;
4
+
5
+ use ThirstyAffiliates\Abstracts\Abstract_Main_Plugin_Class;
6
+
7
+ use ThirstyAffiliates\Interfaces\Model_Interface;
8
+
9
+ use ThirstyAffiliates\Helpers\Plugin_Constants;
10
+ use ThirstyAffiliates\Helpers\Helper_Functions;
11
+
12
+ /**
13
+ * Model that houses the logic for permalink rewrites and affiliate link redirections.
14
+ *
15
+ * @since 3.0.0
16
+ */
17
+ class Shortcodes implements Model_Interface {
18
+
19
+ /*
20
+ |--------------------------------------------------------------------------
21
+ | Class Properties
22
+ |--------------------------------------------------------------------------
23
+ */
24
+
25
+ /**
26
+ * Property that holds the single main instance of Shortcodes.
27
+ *
28
+ * @since 3.0.0
29
+ * @access private
30
+ * @var Redirection
31
+ */
32
+ private static $_instance;
33
+
34
+ /**
35
+ * Model that houses the main plugin object.
36
+ *
37
+ * @since 3.0.0
38
+ * @access private
39
+ * @var Redirection
40
+ */
41
+ private $_main_plugin;
42
+
43
+ /**
44
+ * Model that houses all the plugin constants.
45
+ *
46
+ * @since 3.0.0
47
+ * @access private
48
+ * @var Plugin_Constants
49
+ */
50
+ private $_constants;
51
+
52
+ /**
53
+ * Property that houses all the helper functions of the plugin.
54
+ *
55
+ * @since 3.0.0
56
+ * @access private
57
+ * @var Helper_Functions
58
+ */
59
+ private $_helper_functions;
60
+
61
+
62
+
63
+
64
+ /*
65
+ |--------------------------------------------------------------------------
66
+ | Class Methods
67
+ |--------------------------------------------------------------------------
68
+ */
69
+
70
+ /**
71
+ * Class constructor.
72
+ *
73
+ * @since 3.0.0
74
+ * @access public
75
+ *
76
+ * @param Abstract_Main_Plugin_Class $main_plugin Main plugin object.
77
+ * @param Plugin_Constants $constants Plugin constants object.
78
+ * @param Helper_Functions $helper_functions Helper functions object.
79
+ */
80
+ public function __construct( Abstract_Main_Plugin_Class $main_plugin , Plugin_Constants $constants , Helper_Functions $helper_functions ) {
81
+
82
+ $this->_constants = $constants;
83
+ $this->_helper_functions = $helper_functions;
84
+
85
+ $main_plugin->add_to_all_plugin_models( $this );
86
+
87
+ }
88
+
89
+ /**
90
+ * Ensure that only one instance of this class is loaded or can be loaded ( Singleton Pattern ).
91
+ *
92
+ * @since 3.0.0
93
+ * @access public
94
+ *
95
+ * @param Abstract_Main_Plugin_Class $main_plugin Main plugin object.
96
+ * @param Plugin_Constants $constants Plugin constants object.
97
+ * @param Helper_Functions $helper_functions Helper functions object.
98
+ * @return Redirection
99
+ */
100
+ public static function get_instance( Abstract_Main_Plugin_Class $main_plugin , Plugin_Constants $constants , Helper_Functions $helper_functions ) {
101
+
102
+ if ( !self::$_instance instanceof self )
103
+ self::$_instance = new self( $main_plugin , $constants , $helper_functions );
104
+
105
+ return self::$_instance;
106
+
107
+ }
108
+
109
+ /**
110
+ * Checks if the given ID needs to be uncloaked
111
+ *
112
+ * @since 3.0.0
113
+ * @access public
114
+ *
115
+ * @param int $link_id Affiliate Link post ID.
116
+ * @return boolean.
117
+ */
118
+ public function is_link_to_be_uncloaked( $link_id ) {
119
+
120
+ if ( get_option( 'ta_uncloak_link_per_link' ) == 'yes' ) {
121
+
122
+ $links_to_uncloak = maybe_unserialize( get_option( 'ta_links_to_uncloak' , array() ) );
123
+
124
+ if ( in_array( $link_id , $links_to_uncloak ) )
125
+ return true;
126
+
127
+ }
128
+
129
+ if ( get_option( 'ta_uncloak_link_per_category' ) == 'yes' && $category_to_uncloak = get_option( 'ta_category_to_uncloak' ) ) {
130
+
131
+ if ( has_term( $category_to_uncloak , Plugin_Constants::AFFILIATE_LINKS_TAX , $link_id ) )
132
+ return true;
133
+ }
134
+
135
+ return false;
136
+ }
137
+
138
+ /**
139
+ * thirstylink shortcode.
140
+ * example: [thirstylink ids="10,15,18,20"]Affiliate Link[/thirstylink]
141
+ *
142
+ * @since 3.0.0
143
+ * @since 3.3.0 Add data-nojs attribute support.
144
+ * @since 3.4.0 Add additional classes field support.
145
+ * @access public
146
+ *
147
+ * @param array $atts Shortcode attributes.
148
+ * @param string $content Shortcode content.
149
+ * @return string Processed shortcode output.
150
+ */
151
+ public function thirstylink_shortcode( $atts , $content = '' ) {
152
+
153
+ global $post;
154
+
155
+ $post_id = is_a( $post , 'WP_Post' ) ? $post->ID : 0;
156
+
157
+ $atts = shortcode_atts( array(
158
+ 'ids' => '',
159
+ 'linkid' => '',
160
+ 'linktext' => '',
161
+ 'class' => '',
162
+ 'rel' => '',
163
+ 'target' => '',
164
+ 'title' => ''
165
+ ), $atts , 'thirstylink' );
166
+
167
+ // get all link attributes from $atts
168
+ $link_attributes = array_diff_assoc(
169
+ $atts,
170
+ array(
171
+ 'ids' => $atts[ 'ids' ],
172
+ 'linkid' => $atts[ 'linkid' ],
173
+ 'linktext' => $atts[ 'linktext' ],
174
+ )
175
+ );
176
+
177
+ // get the link ID
178
+ if ( ! $atts[ 'linkid' ] ) {
179
+
180
+ $ids = isset( $atts[ 'ids' ] ) ? array_map( 'intval' , explode( ',' , $atts[ 'ids' ] ) ) : array();
181
+ $key = rand( 0 , count( $ids ) - 1 );
182
+ $link_id = $ids[ $key ];
183
+ } else
184
+ $link_id = (int) $atts[ 'linkid' ];
185
+
186
+ $output = '';
187
+
188
+ if ( $link_id && get_post_type( $link_id ) == Plugin_Constants::AFFILIATE_LINKS_CPT ) {
189
+
190
+ // load thirstylink
191
+ $thirstylink = new Affiliate_Link( $link_id );
192
+ $uncloak_link = $thirstylink->is( 'uncloak_link' );
193
+
194
+ // get the link URL
195
+ $link_attributes[ 'href' ] = ( $uncloak_link ) ? apply_filters( 'ta_uncloak_link_url' , $thirstylink->get_prop( 'destination_url' ) , $thirstylink ) : $thirstylink->get_prop( 'permalink' );
196
+
197
+ if ( ! $uncloak_link )
198
+ $link_attributes[ 'href' ] = $this->clean_url( $link_attributes[ 'href' ] );
199
+
200
+ // get link text content default if no value is set
201
+ if ( empty( $content ) && $atts[ 'linktext' ] )
202
+ $content = $atts[ 'linktext' ]; // backward compatibility to get the link text content.
203
+ else if ( empty( $content ) )
204
+ $content = $thirstylink->get_prop( 'name' );
205
+
206
+ // check for nofollow defaults if no value is set
207
+ if ( empty( $link_attributes[ 'rel' ] ) ) {
208
+
209
+ $link_attributes[ 'rel' ] = $thirstylink->is( 'no_follow' ) ? 'nofollow' : '';
210
+
211
+ if ( $thirstylink->get_prop( 'rel_tags' ) )
212
+ $link_attributes[ 'rel' ] = trim( $link_attributes[ 'rel' ] . ' ' . $thirstylink->get_prop( 'rel_tags' ) );
213
+ }
214
+
215
+ // check for new window defaults if no value is set
216
+ if ( empty( $link_attributes[ 'target' ] ) ) {
217
+
218
+ $link_attributes[ 'target' ] = $thirstylink->is( 'new_window' ) ? '_blank' : '';
219
+ }
220
+
221
+ // provide default class value if it is not set
222
+ if ( empty( $link_attributes[ 'class' ] ) ){
223
+ $thirsty_link_class = strpos( $content , '<img ' ) !== false ? 'thirstylinkimg' : 'thirstylink';
224
+ $link_attributes[ 'class' ] = get_option( 'ta_disable_thirsty_link_class' ) !== 'yes' ? $thirsty_link_class : '';
225
+
226
+ if ( $thirstylink->get_prop( 'css_classes' ) )
227
+ $link_attributes[ 'class' ] = trim( $link_attributes[ 'class' ] . ' ' . $thirstylink->get_prop( 'css_classes' ) );
228
+ }
229
+
230
+
231
+ // provide default class value if it is not set
232
+ if ( empty( $link_attributes[ 'title' ] ) && get_option( 'ta_disable_title_attribute' ) !== 'yes' )
233
+ $link_attributes[ 'title' ] = $thirstylink->get_prop( 'name' );
234
+
235
+ // remove double quote character on title attribute.
236
+ $link_attributes[ 'title' ] = esc_attr( str_replace( '"' , '' , $link_attributes[ 'title' ] ) );
237
+
238
+ // add data-link_id attribute if affiliate link is uncloaked.
239
+ if ( $uncloak_link )
240
+ $link_attributes[ 'data-linkid' ] = $link_id;
241
+
242
+ // tag links as "nojs" to disable JS redirect for them.
243
+ if ( get_option( 'ta_enable_javascript_frontend_redirect' ) == 'yes' )
244
+ $link_attributes[ 'data-nojs' ] = apply_filters( 'ta_nojs_redirect_attribute' , false , $thirstylink );
245
+
246
+ // allow the ability to add custom link attributes
247
+ $link_attributes = apply_filters( 'ta_link_insert_extend_data_attributes' , $link_attributes , $thirstylink , $post_id );
248
+
249
+ // Build the link ready for output
250
+ $output .= '<a';
251
+
252
+ foreach ( $link_attributes as $name => $value ) {
253
+ // Handle square bracket escaping (used for some addons, eg. Google Analytics click tracking)
254
+ $value = html_entity_decode( $value );
255
+ $value = preg_replace( '/&#91;/' , '[' , $value );
256
+ $value = preg_replace( '/&#93;/' , ']' , $value );
257
+ $output .= ! empty($value) ? ' ' . $name . '="' . trim( esc_attr( $value ) ) . '"' : '';
258
+ }
259
+
260
+ $output .= ' data-shortcode="true">' . do_shortcode( $content ) . '</a>';
261
+
262
+
263
+ } elseif ( current_user_can( 'edit_published_posts' ) )
264
+ $output .= '<span style="color: #0000ff;">' . __( 'SHORTCODE ERROR: ThirstyAffiliates did not detect a valid link id, please check your short code!' , 'thirstyaffiliates' ) . '</span>';
265
+ else
266
+ $output = $content;
267
+
268
+ return $output;
269
+ }
270
+
271
+ /**
272
+ * Remove parameters in the URL added via the get_permalink hook and escape it.
273
+ * Note: This is needed for WPML support.
274
+ *
275
+ * @since 3.2.1
276
+ * @access private
277
+ *
278
+ * @param string $raw_url Raw URL.
279
+ * @param string Cleaned URL.
280
+ */
281
+ private function clean_url( $raw_url ) {
282
+
283
+ $parse = parse_url( $raw_url );
284
+ extract( $parse );
285
+
286
+ // rebuild url excluding parameters.
287
+ $url = $scheme . '://' . $host;
288
+ $url .= isset( $path ) ? $path : '';
289
+
290
+ return esc_url( $url );
291
+ }
292
+
293
+ /**
294
+ * Execute shortcodes class.
295
+ *
296
+ * @since 3.0.0
297
+ * @access public
298
+ */
299
+ public function run() {
300
+
301
+ add_shortcode( 'thirstylink' , array( $this , 'thirstylink_shortcode' ) );
302
+ }
303
+ }
Models/Stats_Reporting.php CHANGED
@@ -1,1077 +1,1077 @@
1
- <?php
2
-
3
- namespace ThirstyAffiliates\Models;
4
-
5
- use ThirstyAffiliates\Abstracts\Abstract_Main_Plugin_Class;
6
-
7
- use ThirstyAffiliates\Interfaces\Model_Interface;
8
- use ThirstyAffiliates\Interfaces\Initiable_Interface;
9
- use ThirstyAffiliates\Interfaces\Activatable_Interface;
10
-
11
- use ThirstyAffiliates\Helpers\Plugin_Constants;
12
- use ThirstyAffiliates\Helpers\Helper_Functions;
13
-
14
- use ThirstyAffiliates\Models\Affiliate_Link;
15
-
16
- /**
17
- * Model that houses the logic for permalink rewrites and affiliate link redirections.
18
- *
19
- * @since 3.0.0
20
- */
21
- class Stats_Reporting implements Model_Interface , Initiable_Interface , Activatable_Interface {
22
-
23
- /*
24
- |--------------------------------------------------------------------------
25
- | Class Properties
26
- |--------------------------------------------------------------------------
27
- */
28
-
29
- /**
30
- * Property that holds the single main instance of Stats_Reporting.
31
- *
32
- * @since 3.0.0
33
- * @access private
34
- * @var Redirection
35
- */
36
- private static $_instance;
37
-
38
- /**
39
- * Model that houses the main plugin object.
40
- *
41
- * @since 3.0.0
42
- * @access private
43
- * @var Redirection
44
- */
45
- private $_main_plugin;
46
-
47
- /**
48
- * Model that houses all the plugin constants.
49
- *
50
- * @since 3.0.0
51
- * @access private
52
- * @var Plugin_Constants
53
- */
54
- private $_constants;
55
-
56
- /**
57
- * Property that houses all the helper functions of the plugin.
58
- *
59
- * @since 3.0.0
60
- * @access private
61
- * @var Helper_Functions
62
- */
63
- private $_helper_functions;
64
-
65
- /**
66
- * Variable to store local browser's zone string.
67
- *
68
- * @since 3.2.2
69
- * @access private
70
- * @var string
71
- */
72
- private $_browser_zone_str;
73
-
74
-
75
-
76
-
77
- /*
78
- |--------------------------------------------------------------------------
79
- | Class Methods
80
- |--------------------------------------------------------------------------
81
- */
82
-
83
- /**
84
- * Class constructor.
85
- *
86
- * @since 3.0.0
87
- * @access public
88
- *
89
- * @param Abstract_Main_Plugin_Class $main_plugin Main plugin object.
90
- * @param Plugin_Constants $constants Plugin constants object.
91
- * @param Helper_Functions $helper_functions Helper functions object.
92
- */
93
- public function __construct( Abstract_Main_Plugin_Class $main_plugin , Plugin_Constants $constants , Helper_Functions $helper_functions ) {
94
-
95
- $this->_constants = $constants;
96
- $this->_helper_functions = $helper_functions;
97
-
98
- $main_plugin->add_to_all_plugin_models( $this );
99
- $main_plugin->add_to_public_models( $this );
100
-
101
- }
102
-
103
- /**
104
- * Ensure that only one instance of this class is loaded or can be loaded ( Singleton Pattern ).
105
- *
106
- * @since 3.0.0
107
- * @access public
108
- *
109
- * @param Abstract_Main_Plugin_Class $main_plugin Main plugin object.
110
- * @param Plugin_Constants $constants Plugin constants object.
111
- * @param Helper_Functions $helper_functions Helper functions object.
112
- * @return Redirection
113
- */
114
- public static function get_instance( Abstract_Main_Plugin_Class $main_plugin , Plugin_Constants $constants , Helper_Functions $helper_functions ) {
115
-
116
- if ( !self::$_instance instanceof self )
117
- self::$_instance = new self( $main_plugin , $constants , $helper_functions );
118
-
119
- return self::$_instance;
120
-
121
- }
122
-
123
- /**
124
- * Update $_browser_zone_str class property value.
125
- *
126
- * @since 3.3.3
127
- * @access public
128
- *
129
- * @param string $timezone Timezone set on browser
130
- */
131
- public function set_browser_zone_str( $timezone ) {
132
-
133
- if ( in_array( $timezone , timezone_identifiers_list() ) )
134
- $this->_browser_zone_str = $timezone;
135
- }
136
-
137
- /**
138
- * Register admin interfaces.
139
- *
140
- * @since 3.3.2
141
- * @access public
142
- *
143
- * @param array $interfaces List of admin interfaces.
144
- * @return array Filtered list of admin interfaces.
145
- */
146
- public function register_admin_interfaces( $interfaces ) {
147
-
148
- $interfaces[ 'thirstylink_page_thirsty-reports' ] = apply_filters( 'ta_reports_admin_interface' , array(
149
- 'link_performance' => 'manage_options'
150
- ) );
151
-
152
- return $interfaces;
153
- }
154
-
155
- /**
156
- * Register admin interfaces.
157
- *
158
- * @since 3.3.2
159
- * @access public
160
- *
161
- * @param array $interfaces List of menu items.
162
- * @return array Filtered list of menu items.
163
- */
164
- public function register_admin_menu_items( $menu_items ) {
165
-
166
- $menu_items[ 'thirsty-reports' ] = 'manage_options';
167
- return $menu_items;
168
- }
169
-
170
-
171
-
172
-
173
- /*
174
- |--------------------------------------------------------------------------
175
- | Data saving
176
- |--------------------------------------------------------------------------
177
- */
178
-
179
- /**
180
- * Save link click data to the database.
181
- *
182
- * @since 3.0.0
183
- * @since 3.1.0 Set to save additional information: $cloaked_url , $redirect_url and $redirect_type.
184
- * @since 3.2.0 Set to save additonal information: keyword.
185
- * @since 3.4.0 Add tracking for browser/device.
186
- * @access private
187
- *
188
- * @global wpdb $wpdb Object that contains a set of functions used to interact with a database.
189
- *
190
- * @param Affiliate_Link $thirstylink Affiliate link object.
191
- * @param string $http_referer HTTP Referrer value.
192
- * @param string cloaked_url Affiliate link cloaked url.
193
- * @param string $redirect_url Link to where user is redirected to.
194
- * @param string $redirect_type Redirect type (301,302, etc.)
195
- * @param string $keyword Affiliate link keyword.
196
- */
197
- private function save_click_data( $thirstylink , $http_referer , $cloaked_url , $redirect_url , $redirect_type , $keyword = '' ) {
198
-
199
- global $wpdb;
200
-
201
- if ( apply_filters( 'ta_filter_before_save_click' , false , $thirstylink , $http_referer ) )
202
- return;
203
-
204
- $link_click_db = $wpdb->prefix . Plugin_Constants::LINK_CLICK_DB;
205
- $link_click_meta_db = $wpdb->prefix . Plugin_Constants::LINK_CLICK_META_DB;
206
-
207
- // insert click entry
208
- $wpdb->insert(
209
- $link_click_db,
210
- array(
211
- 'link_id' => $thirstylink->get_id(),
212
- 'date_clicked' => current_time( 'mysql' , true )
213
- )
214
- );
215
-
216
- // save click meta data
217
- if ( $click_id = $wpdb->insert_id ) {
218
-
219
- $meta_data = apply_filters( 'ta_save_click_data' , array(
220
- 'user_ip_address' => $this->_helper_functions->get_user_ip_address(),
221
- 'http_referer' => $http_referer,
222
- 'cloaked_url' => $cloaked_url,
223
- 'redirect_url' => $redirect_url,
224
- 'redirect_type' => $redirect_type,
225
- 'keyword' => $keyword,
226
- 'browser_device' => $this->_helper_functions->get_visitor_browser_device()
227
- ), $thirstylink );
228
-
229
- foreach ( $meta_data as $key => $value ) {
230
-
231
- // make sure there is a key and a value before saving.
232
- if ( ! $key || ! $value )
233
- continue;
234
-
235
- $wpdb->insert(
236
- $link_click_meta_db,
237
- array(
238
- 'click_id' => $click_id,
239
- 'meta_key' => $key,
240
- 'meta_value' => $value
241
- )
242
- );
243
- }
244
-
245
- }
246
- }
247
-
248
- /**
249
- * Save click data on redirect
250
- *
251
- * @since 3.0.0
252
- * @since 3.1.0 Passed additional 2 parameters: $redirect_url and $redirect type. Updated save_click_data function call to include new required arguments.
253
- * @since 3.3.0 If enhanced javascript redirect in frontend is enabled, then all server redirects are allowed (with this, stats links clicked via "open new tab" will be saved).
254
- * @access public
255
- *
256
- * @param Affiliate_Link $thirstylink Affiliate link object.
257
- * @param string $redirect_url Link to where user is redirected to.
258
- * @param string $redirect_type Redirect type (301,302, etc.)
259
- */
260
- public function save_click_data_on_redirect( $thirstylink , $redirect_url , $redirect_type ) {
261
-
262
- // make sure that this is only runs on the frontend and not on the backend.
263
- if ( is_admin() ) return;
264
-
265
- $link_id = $thirstylink->get_id();
266
- $http_referer = isset( $_SERVER[ 'HTTP_REFERER' ] ) ? $_SERVER[ 'HTTP_REFERER' ] : '';
267
- $query_string = isset( $_SERVER[ 'QUERY_STRING' ] ) ? $_SERVER[ 'QUERY_STRING' ] : '';
268
- $cloaked_url = $query_string ? $thirstylink->get_prop( 'permalink' ) . '?' . $query_string : $thirstylink->get_prop( 'permalink' );
269
-
270
- $same_site = $http_referer && strrpos( 'x' . $http_referer , home_url() );
271
- $admin_referrer = $http_referer && strrpos( 'x' . $http_referer , admin_url() );
272
- $js_redirect_enabled = get_option( 'ta_enable_javascript_frontend_redirect' ) == 'yes';
273
-
274
- // NOTE: this fixes a bug reported on TA-250
275
- if ( $http_referer == $cloaked_url || $admin_referrer ) return;
276
-
277
- // if the refferer is from an external site, then record stat.
278
- if ( ( $same_site && $js_redirect_enabled ) || ! $same_site )
279
- $this->save_click_data( $thirstylink , $http_referer , $cloaked_url , $redirect_url , $redirect_type );
280
- }
281
-
282
- /**
283
- * AJAX save click data on redirect
284
- *
285
- * @since 3.0.0
286
- * @since 3.1.0 Updated save_click_data function call to include new required arguments.
287
- * @since 3.2.0 Updated save_click_data function call to include keyword argument.
288
- * @since 3.3.0 print actual affiliate link redirect url for ehanced javascript redirect support.
289
- * @since 3.4.0 Add query string as parameter to support passing query strings in enhanced JS redirect.
290
- * @access public
291
- */
292
- public function ajax_save_click_data_on_redirect() {
293
-
294
- if ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX )
295
- wp_die();
296
-
297
- $link_id = isset( $_REQUEST[ 'link_id' ] ) ? (int) sanitize_text_field( $_REQUEST[ 'link_id' ] ) : 0;
298
- $http_referer = isset( $_REQUEST[ 'page' ] ) ? esc_url_raw( $_REQUEST[ 'page' ] ) : '';
299
- $cloaked_url = isset( $_REQUEST[ 'href' ] ) ? esc_url_raw( $_REQUEST[ 'href' ] ) : '';
300
- $keyword = isset( $_REQUEST[ 'keyword' ] ) ? sanitize_text_field( $_REQUEST[ 'keyword' ] ) : '';
301
- $query_string = isset( $_REQUEST[ 'qs' ] ) ? sanitize_text_field( $_REQUEST[ 'qs' ] ) : '';
302
-
303
- if ( ! $link_id )
304
- $link_id = url_to_postid( $cloaked_url );
305
-
306
- if ( $link_id ) {
307
-
308
- $thirstylink = new Affiliate_Link( $link_id );
309
- $redirect_url = apply_filters( 'ta_filter_redirect_url' , $thirstylink->get_prop( 'destination_url' ) , $thirstylink , $query_string );
310
- $redirect_type = $thirstylink->get_redirect_type();
311
-
312
- $this->save_click_data( $thirstylink , $http_referer , $cloaked_url , $redirect_url , $redirect_type , $keyword );
313
-
314
- // print actual affiliate link redirect url for enhanced javascript redirect support.
315
- if ( get_option( 'ta_enable_javascript_frontend_redirect' ) == 'yes' )
316
- echo $redirect_url;
317
- }
318
-
319
- wp_die();
320
- }
321
-
322
-
323
-
324
-
325
- /*
326
- |--------------------------------------------------------------------------
327
- | Fetch Report Data
328
- |--------------------------------------------------------------------------
329
- */
330
-
331
- /**
332
- * Fetch link performance data by date range.
333
- *
334
- * @since 3.0.0
335
- * @access public
336
- *
337
- * @global wpdb $wpdb Object that contains a set of functions used to interact with a database.
338
- *
339
- * @param string $start_date Report start date. Format: YYYY-MM-DD hh:mm:ss
340
- * @param string $end_date Report end date. Format: YYYY-MM-DD hh:mm:ss
341
- * @param array $link_ids Affiliate Link post ID
342
- * @return string/array Link click meta data value.
343
- */
344
- public function get_link_performance_data( $start_date , $end_date , $link_ids ) {
345
-
346
- global $wpdb;
347
-
348
- if ( ! is_array( $link_ids ) || empty( $link_ids ) )
349
- return array();
350
-
351
- $link_clicks_db = $wpdb->prefix . Plugin_Constants::LINK_CLICK_DB;
352
- $link_ids_str = implode( ', ' , $link_ids );
353
- $query = "SELECT * FROM $link_clicks_db WHERE date_clicked between '$start_date' and '$end_date' and link_id IN ( $link_ids_str )";
354
-
355
- return $wpdb->get_results( $query );
356
- }
357
-
358
- /**
359
- * Get link click meta by id and key.
360
- *
361
- * @since 3.0.0
362
- * @access public
363
- *
364
- * @param int $click_id Link click ID.
365
- * @param string $meta_key Meta key column value.
366
- * @param boolean $single Return single result or array.
367
- * @return array Link performance data.
368
- */
369
- private function get_click_meta( $click_id , $meta_key , $single = false ) {
370
-
371
- global $wpdb;
372
-
373
- $links_click_meta_db = $wpdb->prefix . Plugin_Constants::LINK_CLICK_META_DB;
374
-
375
- if ( $single ){
376
-
377
- $meta = $wpdb->get_row( "SELECT meta_value FROM $links_click_meta_db WHERE click_id = '$click_id' and meta_key = '$meta_key'" , ARRAY_A );
378
- return array_shift( $meta );
379
-
380
- } else {
381
-
382
- $meta = array();
383
- $raw_data = $wpdb->get_results( "SELECT meta_value FROM $links_click_meta_db WHERE click_id = '$click_id' and meta_key = '$meta_key'" , ARRAY_N );
384
-
385
- foreach ( $raw_data as $data )
386
- $meta[] = array_shift( $data );
387
-
388
- return $meta;
389
- }
390
- }
391
-
392
- /**
393
- * AJAX fetch report by linkid.
394
- *
395
- * @since 3.0.0
396
- * @since 3.1.2 Add total clicks count on the response.
397
- * @access public
398
- */
399
- public function ajax_fetch_report_by_linkid() {
400
-
401
- if ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX )
402
- $response = array( 'status' => 'fail' , 'error_msg' => __( 'Invalid AJAX call' , 'thirstyaffiliates' ) );
403
- elseif ( ! isset( $_POST[ 'link_id' ] ) )
404
- $response = array( 'status' => 'fail' , 'error_msg' => __( 'Missing required post data' , 'thirstyaffiliates' ) );
405
- else {
406
-
407
- // save timezone to use
408
- $timezone = isset( $_POST[ 'timezone' ] ) ? sanitize_text_field( $_POST[ 'timezone' ] ) : '';
409
- $this->set_browser_zone_str( $timezone );
410
-
411
- $link_id = isset( $_POST[ 'link_id' ] ) ? (int) sanitize_text_field( $_POST[ 'link_id' ] ) : 0;
412
- $thirstylink = new Affiliate_Link( $link_id );
413
- $range_txt = isset( $_POST[ 'range' ] ) ? sanitize_text_field( $_POST[ 'range' ] ) : '';
414
- $start_date = isset( $_POST[ 'start_date' ] ) ? sanitize_text_field( $_POST[ 'start_date' ] ) : '';
415
- $end_date = isset( $_POST[ 'end_date' ] ) ? sanitize_text_field( $_POST[ 'end_date' ] ) : '';
416
-
417
- if ( ! $thirstylink->get_id() )
418
- $response = array( 'status' => 'fail' , 'error_msg' => __( 'Selected affiliate link is invalid' , 'thirstyaffiliates' ) );
419
- else {
420
-
421
- $range = $this->get_report_range_details( $range_txt , $start_date , $end_date );
422
- $data = $this->prepare_data_for_flot( $range , array( $link_id ) );
423
-
424
- $response = array(
425
- 'status' => 'success',
426
- 'label' => $thirstylink->get_prop( 'name' ),
427
- 'slug' => $thirstylink->get_prop( 'slug' ),
428
- 'report_data' => $data,
429
- 'total_clicks' => $this->count_total_clicks_from_flot_data( $data )
430
- );
431
- }
432
- }
433
-
434
- @header( 'Content-Type: application/json; charset=' . get_option( 'blog_charset' ) );
435
- echo wp_json_encode( $response );
436
- wp_die();
437
- }
438
-
439
- /**
440
- * AJAX init first report.
441
- *
442
- * @since 3.2.2
443
- * @access public
444
- */
445
- public function ajax_init_first_report() {
446
-
447
- if ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX )
448
- $response = array( 'status' => 'fail' , 'error_msg' => __( 'Invalid AJAX call' , 'thirstyaffiliates' ) );
449
- elseif ( ! isset( $_POST[ 'timezone' ] ) )
450
- $response = array( 'status' => 'fail' , 'error_msg' => __( 'Missing required post data' , 'thirstyaffiliates' ) );
451
- else {
452
-
453
- // save timezone to use
454
- $timezone = isset( $_POST[ 'timezone' ] ) ? sanitize_text_field( $_POST[ 'timezone' ] ) : '';
455
- $this->set_browser_zone_str( $timezone );
456
-
457
- $cpt_slug = Plugin_Constants::AFFILIATE_LINKS_CPT;
458
- $current_range = isset( $_POST[ 'range' ] ) ? sanitize_text_field( $_POST[ 'range' ] ) : '7day';
459
- $start_date = isset( $_POST[ 'start_date' ] ) ? sanitize_text_field( $_POST[ 'start_date' ] ) : '';
460
- $end_date = isset( $_POST[ 'end_date' ] ) ? sanitize_text_field( $_POST[ 'end_date' ] ) : '';
461
- $range = $this->get_report_range_details( $current_range , $start_date , $end_date );
462
-
463
- // get all published affiliate link ids
464
- $query = new \WP_Query( array(
465
- 'post_type' => $cpt_slug,
466
- 'post_status' => 'publish',
467
- 'fields' => 'ids',
468
- 'posts_per_page' => -1
469
- ) );
470
-
471
- $data = $this->prepare_data_for_flot( $range , $query->posts );
472
- $total_clicks = $this->count_total_clicks_from_flot_data( $data );
473
-
474
- $response = array(
475
- 'status' => 'success',
476
- 'flot_data' => $data,
477
- 'total_clicks' => $total_clicks,
478
- );
479
-
480
- }
481
-
482
- @header( 'Content-Type: application/json; charset=' . get_option( 'blog_charset' ) );
483
- echo wp_json_encode( $response );
484
- wp_die();
485
- }
486
-
487
-
488
-
489
-
490
- /*
491
- |--------------------------------------------------------------------------
492
- | Reports Structure
493
- |--------------------------------------------------------------------------
494
- */
495
-
496
- /**
497
- * Get all registered reports.
498
- *
499
- * @since 3.0.0
500
- * @access public
501
- *
502
- * @return array Settings sections.
503
- */
504
- public function get_all_reports() {
505
-
506
- return apply_filters( 'ta_register_reports' , array() );
507
- }
508
-
509
- /**
510
- * Get current loaded report.
511
- *
512
- * @since 3.0.0
513
- * @access public
514
- *
515
- * @param string $tab Current report tab.
516
- * @return array Current loaded report.
517
- */
518
- public function get_current_report( $tab = '' ) {
519
-
520
- if ( ! $tab )
521
- $tab = isset( $_GET[ 'tab' ] ) ? esc_attr( $_GET[ 'tab' ] ) : 'link_performance';
522
-
523
- // get all registered sections and fields
524
- $reports = $this->get_all_reports();
525
-
526
- return isset( $reports[ $tab ] ) ? $reports[ $tab ] : array();
527
- }
528
-
529
- /**
530
- * Register link performance report.
531
- *
532
- * @since 3.0.0
533
- * @access public
534
- *
535
- * @param array $reports Array list of all registered reports.
536
- * @return array Array list of all registered reports.
537
- */
538
- public function register_link_performance_report( $reports ) {
539
-
540
- $reports[ 'link_performance' ] = array(
541
- 'id' => 'ta_link_performance_report',
542
- 'tab' => 'link_performance',
543
- 'name' => __( 'Link Overview' , 'thirstyaffiliates' ),
544
- 'title' => __( 'Link Overview Report' , 'thirstyaffiliates' ),
545
- 'desc' => __( 'Total clicks on affiliate links over a given period.' , 'thirstyaffiliates' ),
546
- 'content' => function() { return $this->get_link_performance_report_content(); }
547
- );
548
-
549
- return $reports;
550
- }
551
-
552
-
553
-
554
-
555
- /*
556
- |--------------------------------------------------------------------------
557
- | Display Report
558
- |--------------------------------------------------------------------------
559
- */
560
-
561
- /**
562
- * Register reports menu page.
563
- *
564
- * @since 3.0.0
565
- * @since 3.2.2 Access to the settings page will now be controlled by the plugin. see Bootstrap::admin_interface_visibility.
566
- *
567
- * @access public
568
- */
569
- public function add_reports_submenu() {
570
-
571
- if ( ! current_user_can( 'edit_posts' ) ) return;
572
-
573
- add_submenu_page(
574
- 'edit.php?post_type=thirstylink',
575
- __( 'ThirstyAffiliates Reports' , 'thirstyaffiliates' ),
576
- __( 'Reports' , 'thirstyaffiliates' ),
577
- 'read',
578
- 'thirsty-reports',
579
- array( $this, 'render_reports' )
580
- );
581
- }
582
-
583
- /**
584
- * Render reports page.
585
- *
586
- * @since 3.0.0
587
- * @access public
588
- */
589
- public function render_reports() {
590
-
591
- // fetch current section
592
- $current_report = $this->get_current_report();
593
- $report_content = is_callable( $current_report[ 'content' ] ) ? $current_report[ 'content' ]() : $current_report[ 'content' ];
594
-
595
- // skip if section data is empty
596
- if ( empty( $current_report ) ) return; ?>
597
-
598
- <div class="ta-settings ta-settings-<?php echo $current_report[ 'tab' ]; ?> wrap">
599
-
600
- <?php $this->render_reports_nav(); ?>
601
-
602
- <h1><?php echo $current_report[ 'title' ]; ?></h1>
603
- <p class="desc"><?php echo $current_report[ 'desc' ]; ?></p>
604
-
605
- <?php echo $report_content; ?>
606
- </div>
607
- <?php
608
- }
609
-
610
- /**
611
- * Render the settings navigation.
612
- *
613
- * @since 3.0.0
614
- * @access public
615
- */
616
- public function render_reports_nav() {
617
-
618
- $reports = $this->get_all_reports();
619
- $current = $this->get_current_report();
620
- $base_url = admin_url( 'edit.php?post_type=thirstylink&page=thirsty-reports' );
621
-
622
- if ( empty( $reports ) ) return; ?>
623
-
624
- <nav class="thirsty-nav-tab">
625
- <?php foreach ( $reports as $report ) : ?>
626
-
627
- <a href="<?php echo $base_url . '&tab=' . $report[ 'tab' ]; ?>" class="tab <?php echo ( $current[ 'tab' ] === $report[ 'tab' ] ) ? 'tab-active' : ''; ?>">
628
- <?php echo $report[ 'name' ]; ?>
629
- </a>
630
-
631
- <?php endforeach; ?>
632
- </nav>
633
-
634
- <?php
635
- }
636
-
637
- /**
638
- * Get Link performance report content.
639
- *
640
- * @since 3.0.0
641
- * @since 3.3.2 Remove report data query on page first load.
642
- * @access public
643
- *
644
- * @return string Link performance report content.
645
- */
646
- public function get_link_performance_report_content() {
647
-
648
- $cpt_slug = Plugin_Constants::AFFILIATE_LINKS_CPT;
649
- $current_range = isset( $_GET[ 'range' ] ) ? sanitize_text_field( $_GET[ 'range' ] ) : '7day';
650
- $start_date = isset( $_GET[ 'start_date' ] ) ? sanitize_text_field( $_GET[ 'start_date' ] ) : '';
651
- $end_date = isset( $_GET[ 'end_date' ] ) ? sanitize_text_field( $_GET[ 'end_date' ] ) : '';
652
- $link_id = isset( $_GET[ 'link_id' ] ) ? sanitize_text_field( $_GET[ 'link_id' ] ) : '';
653
- $range = $this->get_report_range_details( $current_range , $start_date , $end_date );
654
- $range_nav = apply_filters( 'ta_link_performances_report_nav' , array(
655
- 'year' => __( 'Year' , 'thirstyaffiliates' ),
656
- 'last_month' => __( 'Last Month' , 'thirstyaffiliates' ),
657
- 'month' => __( 'This Month' , 'thirstyaffiliates' ),
658
- '7day' => __( 'Last 7 Days' , 'thirstyaffiliates' )
659
- ) );
660
-
661
- // make sure link_id is an affiliate link (published).
662
- // NOTE: when false, this needs to return an empty string as it is used for display.
663
- if ( $link_id ) $link_id = ( get_post_type( $link_id ) == $cpt_slug && get_post_status( $link_id ) == 'publish' ) ? $link_id : '';
664
-
665
- ob_start();
666
- include( $this->_constants->VIEWS_ROOT_PATH() . 'reports/link-performance-report.php' );
667
-
668
- return ob_get_clean();
669
- }
670
-
671
-
672
-
673
-
674
- /*
675
- |--------------------------------------------------------------------------
676
- | Helper methods
677
- |--------------------------------------------------------------------------
678
- */
679
-
680
- /**
681
- * Get report range details.
682
- *
683
- * @since 3.0.0
684
- * @since 3.2.2 Change method of getting timezone sting name.
685
- * @access public
686
- *
687
- * @param string $range Report range type.
688
- * @param string $start_date Starting date of range.
689
- * @param string $end_date Ending date of range.
690
- * @return array Report range details.
691
- */
692
- public function get_report_range_details( $range = '7day' , $start_date = 'now -6 days' , $end_date = 'now' ) {
693
-
694
- $data = array();
695
- $zone_str = $this->get_report_timezone_string();
696
- $timezone = new \DateTimeZone( $zone_str );
697
- $now = new \DateTime( 'now' , $timezone );
698
-
699
- switch ( $range ) {
700
-
701
- case 'year' :
702
- $data[ 'type' ] = 'year';
703
- $data[ 'start_date' ] = new \DateTime( 'first day of January' . date( 'Y' ) , $timezone );
704
- $data[ 'end_date' ] = $now;
705
- break;
706
-
707
- case 'last_month' :
708
- $data[ 'type' ] = 'last_month';
709
- $data[ 'start_date' ] = new \DateTime( 'first day of last month' , $timezone );
710
- $data[ 'end_date' ] = new \DateTime( 'last day of last month' , $timezone );
711
- break;
712
-
713
- case 'month' :
714
- $data[ 'type' ] = 'month';
715
- $data[ 'start_date' ] = new \DateTime( 'first day of this month' , $timezone );
716
- $data[ 'end_date' ] = $now;
717
- $data[ 'start_date' ]->setTime( 0 , 0 , 0 );
718
- break;
719
-
720
- case 'custom' :
721
- $data[ 'type' ] = 'custom';
722
- $data[ 'start_date' ] = new \DateTime( $start_date , $timezone );
723
- $data[ 'end_date' ] = new \DateTime( $end_date . ' 23:59:59' , $timezone );
724
- break;
725
-
726
- case '7day' :
727
- default :
728
- $start_date = new \DateTime( 'now -6 days' , $timezone );
729
-
730
- // set hours, minutes and seconds to zero
731
- $start_date->setTime( 0 , 0 , 0 );
732
- $now->setTime( 23 , 59 , 59 );
733
-
734
- $data[ 'type' ] = '7day';
735
- $data[ 'start_date' ] = $start_date;
736
- $data[ 'end_date' ] = $now;
737
- break;
738
- }
739
-
740
- return apply_filters( 'ta_report_range_data' , $data , $range );
741
- }
742
-
743
- /**
744
- * Prepare data to feed for jQuery flot.
745
- *
746
- * @since 3.0.0
747
- * @since 3.2.2 Change method of getting timezone sting name.
748
- * @since 3.3.3 Set range timezone to UTC before fetching raw data.
749
- * @since 3.3.4 Set date range timezone back to local browser before setting start time to zero.
750
- * @access public
751
- *
752
- * @param array $range Report range details
753
- * @param array $link_ids Affiliate Link post ID
754
- * @return array Processed data for jQuery flot.
755
- */
756
- public function prepare_data_for_flot( $range , $link_ids ) {
757
-
758
- $start_date = $range[ 'start_date' ];
759
- $end_date = $range[ 'end_date' ];
760
- $zone_str = $this->get_report_timezone_string();
761
- $timezone = new \DateTimeZone( $zone_str );
762
- $utc = new \DateTimeZone( 'UTC' );
763
- $flot_data = array();
764
-
765
- $start_date->setTimezone( $timezone );
766
- $end_date->setTimezone( $timezone );
767
-
768
- if ( apply_filters( 'ta_report_set_start_date_time_to_zero' , true , $range ) )
769
- $start_date->setTime( 0 , 0 );
770
-
771
- $start_date->setTimezone( $utc );
772
- $end_date->setTimezone( $utc );
773
-
774
- $raw_data = $this->get_link_performance_data( $start_date->format( 'Y-m-d H:i:s' ) , $end_date->format( 'Y-m-d H:i:s' ) , $link_ids );
775
-
776
- // get number of days difference between start and end
777
- $incrementor = apply_filters( 'ta_report_flot_data_incrementor' , ( 60 * 60 * 24 ) , $range );
778
- $timestamp_diff = ( $start_date->getTimestamp() - $end_date->getTimestamp() );
779
- $days_diff = abs( floor( $timestamp_diff / $incrementor ) );
780
-
781
- // save the timestamp for first day
782
- $timestamp = $start_date->format( 'U' );
783
- $month_time = $this->get_month_first_day_datetime_obj( 'February' );
784
- $next_timestamp = ( $range[ 'type' ] == 'year' ) ? $month_time->format( 'U' ) : $timestamp + $incrementor;
785
- $flot_data[] = array(
786
- 'timestamp' => (int) $timestamp,
787
- 'count' => 0,
788
- 'next_timestamp' => $next_timestamp
789
- );
790
-
791
- if ( $range[ 'type' ] == 'year' ) {
792
-
793
- $months = array( 'February' , 'March' , 'April' , 'May' , 'June' , 'July' , 'August' , 'September' , 'October' , 'November' , 'December' );
794
-
795
- foreach ( $months as $key => $month ) {
796
-
797
- $month_time = $this->get_month_first_day_datetime_obj( $month );
798
- $next_month = isset( $months[ $key + 1 ] ) ? $this->get_month_first_day_datetime_obj( $months[ $key + 1 ] ) : new \DateTime( 'now' , $timezone );
799
-
800
- $flot_data[] = array(
801
- 'timestamp' => $month_time->format( 'U' ),
802
- 'count' => 0,
803
- 'next_timestamp' => $next_month->format( 'U' )
804
- );
805
-
806
- if ( $end_date->format( 'F' ) == $month )
807
- break;
808
- }
809
-
810
- } else {
811
-
812
- // determine timestamps for succeeding days
813
- for ( $x = 1; $x < $days_diff; $x++ ) {
814
-
815
- $timestamp = $next_timestamp;
816
- $next_timestamp = $timestamp + $incrementor;
817
-
818
- $flot_data[] = array(
819
- 'timestamp' => (int) $timestamp,
820
- 'count' => 0,
821
- 'next_timestamp' => $next_timestamp
822
- );
823
-
824
- }
825
- }
826
-
827
- // count each click data and assign to appropriate day.
828
- foreach ( $raw_data as $click_entry ) {
829
-
830
- $click_date = new \DateTime( $click_entry->date_clicked , $utc );
831
- $click_date->setTimezone( $timezone );
832
-
833
- $click_timestamp = (int) $click_date->format( 'U' );
834
-
835
- foreach ( $flot_data as $key => $day_data ) {
836
-
837
- if ( $click_timestamp >= $day_data[ 'timestamp' ] && $click_timestamp < $day_data[ 'next_timestamp' ] ) {
838
- $flot_data[ $key ][ 'count' ] += 1;
839
- continue;
840
- }
841
- }
842
- }
843
-
844
- // convert $flot_data array into non-associative array
845
- foreach ( $flot_data as $key => $day_data ) {
846
-
847
- unset( $day_data[ 'next_timestamp' ] );
848
-
849
- $day_data[ 'timestamp' ] = $day_data[ 'timestamp' ] * 1000;
850
- $flot_data[ $key ] = array_values( $day_data );
851
- }
852
-
853
- return $flot_data;
854
- }
855
-
856
- /**
857
- * Count total clicks from flot data.
858
- *
859
- * @since 3.2.1
860
- * @access public
861
- *
862
- * @param array $data Flot data.
863
- * @param int $total Total click counts (offset).
864
- * @return int Total click clounts.
865
- */
866
- public function count_total_clicks_from_flot_data( $data , $total = 0 ) {
867
-
868
- if ( ! is_array( $data ) || empty( $data ) )
869
- return;
870
-
871
- foreach ( $data as $flot )
872
- $total += intval( $flot[1] );
873
-
874
- return $total;
875
- }
876
-
877
- /**
878
- * Get the DateTime object of the first day of a given month.
879
- *
880
- * @since 3.0.0
881
- * @since 3.2.2 Change method of getting timezone sting name.
882
- * @access public
883
- *
884
- * @param string $month Month full textual representation.
885
- * @return DateTime First day of the given month DateTime object.
886
- */
887
- public function get_month_first_day_datetime_obj( $month ) {
888
-
889
- $zone_str = $this->get_report_timezone_string();
890
- $timezone = new \DateTimeZone( $zone_str );
891
-
892
- return new \DateTime( 'First day of ' . $month . ' ' . date( 'Y' ) , $timezone );
893
- }
894
-
895
- /**
896
- * Schedule stats trimmer cron job.
897
- *
898
- * @since 3.1.0
899
- * @access private
900
- */
901
- private function schedule_stats_trimmer_cron() {
902
-
903
- $zone_str = $this->_helper_functions->get_site_current_timezone();
904
- $timezone = new \DateTimeZone( $zone_str );
905
- $time = new \DateTime( 'first day of next month' , $timezone );
906
-
907
- // clear all scheduled crons so there will always only be one.
908
- wp_clear_scheduled_hook( Plugin_Constants::CRON_STATS_TRIMMER );
909
-
910
- // schedule cron job
911
- wp_schedule_single_event( $time->format( 'U' ) , Plugin_Constants::CRON_STATS_TRIMMER );
912
- }
913
-
914
- /**
915
- * Implement stats trimmer
916
- *
917
- * @since 3.1.0
918
- * @access public
919
- *
920
- * @global wpdb $wpdb Object that contains a set of functions used to interact with a database.
921
- */
922
- public function implement_stats_trimmer() {
923
-
924
- global $wpdb;
925
-
926
- $trim_point = (int) get_option( 'ta_stats_trimer_set_point' , 0 );
927
-
928
- if ( $trim_point > 0 ) {
929
-
930
- $clicks_db = $wpdb->prefix . Plugin_Constants::LINK_CLICK_DB;
931
- $clicks_meta_db = $wpdb->prefix . Plugin_Constants::LINK_CLICK_META_DB;
932
-
933
- // get click ids based on set range.
934
- $query = "SELECT id FROM $clicks_db WHERE date_clicked < DATE_ADD( NOW() , INTERVAL -" . $trim_point . " MONTH )";
935
- $click_ids = $wpdb->get_col( $query );
936
-
937
- // Proceed on deleting data when $click_ids are present
938
- if ( is_array( $click_ids ) && ! empty( $click_ids ) ) {
939
-
940
- $click_ids_string = implode( $click_ids , ',' );
941
-
942
- // delete click data
943
- $wpdb->query( "DELETE FROM $clicks_meta_db WHERE click_id IN ( $click_ids_string )" );
944
- $wpdb->query( "DELETE FROM $clicks_db WHERE id IN ( $click_ids_string )" );
945
- }
946
- }
947
-
948
- // reschedule the cron job
949
- $this->schedule_stats_trimmer_cron();
950
- }
951
-
952
- /**
953
- * Prevent saving click data if useragent is a bot (for non-apache servers).
954
- *
955
- * @since 3.1.0
956
- * @since 3.3.3 Moved code to a helper function (DRY).
957
- * @access public
958
- *
959
- * @param boolean $response Default response of filter.
960
- * @return boolean True if needs to be prevented, false otherwise.
961
- */
962
- public function prevent_save_click_if_useragent_is_bot( $response ) {
963
-
964
- return $this->_helper_functions->is_user_agent_bot();
965
- }
966
-
967
- /**
968
- * Delete stats data when an affiliate link is deleted permanently.
969
- *
970
- * @since 3.0.1
971
- * @access public
972
- *
973
- * @global wpdb $wpdb Object that contains a set of functions used to interact with a database.
974
- *
975
- * @param int $link_id Affiliate link ID.
976
- */
977
- public function delete_stats_data_on_affiliate_link_deletion( $link_id ) {
978
-
979
- global $wpdb;
980
-
981
- if ( Plugin_Constants::AFFILIATE_LINKS_CPT !== get_post_type( $link_id ) )
982
- return;
983
-
984
- $link_click_db = $wpdb->prefix . Plugin_Constants::LINK_CLICK_DB;
985
- $link_click_meta_db = $wpdb->prefix . Plugin_Constants::LINK_CLICK_META_DB;
986
- $click_ids = $wpdb->get_col( "SELECT id FROM $link_click_db WHERE link_id = $link_id" );
987
-
988
- if ( ! is_array( $click_ids ) || empty( $click_ids ) )
989
- return;
990
-
991
- $click_ids_str = implode( ',' , $click_ids );
992
-
993
- // delete click meta records.
994
- $wpdb->query( "DELETE FROM $link_click_meta_db WHERE click_id IN ( $click_ids_str )" );
995
-
996
- // delete click records.
997
- $wpdb->query( "DELETE FROM $link_click_db WHERE id IN ( $click_ids_str )" );
998
- }
999
-
1000
- /**
1001
- * Get timezone to use for the report.
1002
- *
1003
- * @since 3.2.2
1004
- * @since 3.3.3 Made the method public so TAP can utilize it.
1005
- * @access public
1006
- *
1007
- * @return string Timezone string name.
1008
- */
1009
- public function get_report_timezone_string() {
1010
-
1011
- return $this->_browser_zone_str ? $this->_browser_zone_str : $this->_helper_functions->get_site_current_timezone();
1012
- }
1013
-
1014
-
1015
-
1016
-
1017
- /*
1018
- |--------------------------------------------------------------------------
1019
- | Fulfill implemented interface contracts
1020
- |--------------------------------------------------------------------------
1021
- */
1022
-
1023
- /**
1024
- * Execute codes that needs to run plugin activation.
1025
- *
1026
- * @since 3.0.0
1027
- * @access public
1028
- * @implements ThirstyAffiliates\Interfaces\Activatable_Interface
1029
- */
1030
- public function activate() {
1031
-
1032
- $this->schedule_stats_trimmer_cron();
1033
- }
1034
-
1035
- /**
1036
- * Method that houses codes to be executed on init hook.
1037
- *
1038
- * @since 3.0.0
1039
- * @access public
1040
- * @inherit ThirstyAffiliates\Interfaces\Initiable_Interface
1041
- */
1042
- public function initialize() {
1043
-
1044
- // When module is disabled in the settings, then it shouldn't run the whole class.
1045
- if ( get_option( 'ta_enable_stats_reporting_module' , 'yes' ) !== 'yes' )
1046
- return;
1047
-
1048
- add_action( 'wp_ajax_ta_click_data_redirect' , array( $this , 'ajax_save_click_data_on_redirect' ) , 10 );
1049
- add_action( 'wp_ajax_ta_fetch_report_by_linkid' , array( $this , 'ajax_fetch_report_by_linkid' ) , 10 );
1050
- add_action( 'wp_ajax_ta_init_first_report' , array( $this , 'ajax_init_first_report' ) , 10 );
1051
- add_action( 'wp_ajax_nopriv_ta_click_data_redirect' , array( $this , 'ajax_save_click_data_on_redirect' ) , 10 );
1052
- }
1053
-
1054
- /**
1055
- * Execute ajax handler.
1056
- *
1057
- * @since 3.0.0
1058
- * @access public
1059
- * @inherit ThirstyAffiliates\Interfaces\Model_Interface
1060
- */
1061
- public function run() {
1062
-
1063
- // When module is disabled in the settings, then it shouldn't run the whole class.
1064
- if ( get_option( 'ta_enable_stats_reporting_module' , 'yes' ) !== 'yes' )
1065
- return;
1066
-
1067
- add_filter( 'ta_filter_before_save_click' , array( $this , 'prevent_save_click_if_useragent_is_bot' ) , 10 , 1 );
1068
- add_action( 'ta_before_link_redirect' , array( $this , 'save_click_data_on_redirect' ) , 10 , 3 );
1069
- add_action( 'admin_menu' , array( $this , 'add_reports_submenu' ) , 10 );
1070
- add_action( 'ta_register_reports' , array( $this , 'register_link_performance_report' ) , 10 );
1071
- add_action( Plugin_Constants::CRON_STATS_TRIMMER , array( $this , 'implement_stats_trimmer' ) );
1072
- add_action( 'before_delete_post' , array( $this , 'delete_stats_data_on_affiliate_link_deletion' ) , 10 );
1073
-
1074
- add_filter( 'ta_admin_interfaces' , array( $this , 'register_admin_interfaces' ) );
1075
- add_filter( 'ta_menu_items' , array( $this , 'register_admin_menu_items' ) );
1076
- }
1077
- }
1
+ <?php
2
+
3
+ namespace ThirstyAffiliates\Models;
4
+
5
+ use ThirstyAffiliates\Abstracts\Abstract_Main_Plugin_Class;
6
+
7
+ use ThirstyAffiliates\Interfaces\Model_Interface;
8
+ use ThirstyAffiliates\Interfaces\Initiable_Interface;
9
+ use ThirstyAffiliates\Interfaces\Activatable_Interface;
10
+
11
+ use ThirstyAffiliates\Helpers\Plugin_Constants;
12
+ use ThirstyAffiliates\Helpers\Helper_Functions;
13
+
14
+ use ThirstyAffiliates\Models\Affiliate_Link;
15
+
16
+ /**
17
+ * Model that houses the logic for permalink rewrites and affiliate link redirections.
18
+ *
19
+ * @since 3.0.0
20
+ */
21
+ class Stats_Reporting implements Model_Interface , Initiable_Interface , Activatable_Interface {
22
+
23
+ /*
24
+ |--------------------------------------------------------------------------
25
+ | Class Properties
26
+ |--------------------------------------------------------------------------
27
+ */
28
+
29
+ /**
30
+ * Property that holds the single main instance of Stats_Reporting.
31
+ *
32
+ * @since 3.0.0
33
+ * @access private
34
+ * @var Redirection
35
+ */
36
+ private static $_instance;
37
+
38
+ /**
39
+ * Model that houses the main plugin object.
40
+ *
41
+ * @since 3.0.0
42
+ * @access private
43
+ * @var Redirection
44
+ */
45
+ private $_main_plugin;
46
+
47
+ /**
48
+ * Model that houses all the plugin constants.
49
+ *
50
+ * @since 3.0.0
51
+ * @access private
52
+ * @var Plugin_Constants
53
+ */
54
+ private $_constants;
55
+
56
+ /**
57
+ * Property that houses all the helper functions of the plugin.
58
+ *
59
+ * @since 3.0.0
60
+ * @access private
61
+ * @var Helper_Functions
62
+ */
63
+ private $_helper_functions;
64
+
65
+ /**
66
+ * Variable to store local browser's zone string.
67
+ *
68
+ * @since 3.2.2
69
+ * @access private
70
+ * @var string
71
+ */
72
+ private $_browser_zone_str;
73
+
74
+
75
+
76
+
77
+ /*
78
+ |--------------------------------------------------------------------------
79
+ | Class Methods
80
+ |--------------------------------------------------------------------------
81
+ */
82
+
83
+ /**
84
+ * Class constructor.
85
+ *
86
+ * @since 3.0.0
87
+ * @access public
88
+ *
89
+ * @param Abstract_Main_Plugin_Class $main_plugin Main plugin object.
90
+ * @param Plugin_Constants $constants Plugin constants object.
91
+ * @param Helper_Functions $helper_functions Helper functions object.
92
+ */
93
+ public function __construct( Abstract_Main_Plugin_Class $main_plugin , Plugin_Constants $constants , Helper_Functions $helper_functions ) {
94
+
95
+ $this->_constants = $constants;
96
+ $this->_helper_functions = $helper_functions;
97
+
98
+ $main_plugin->add_to_all_plugin_models( $this );
99
+ $main_plugin->add_to_public_models( $this );
100
+
101
+ }
102
+
103
+ /**
104
+ * Ensure that only one instance of this class is loaded or can be loaded ( Singleton Pattern ).
105
+ *
106
+ * @since 3.0.0
107
+ * @access public
108
+ *
109
+ * @param Abstract_Main_Plugin_Class $main_plugin Main plugin object.
110
+ * @param Plugin_Constants $constants Plugin constants object.
111
+ * @param Helper_Functions $helper_functions Helper functions object.
112
+ * @return Redirection
113
+ */
114
+ public static function get_instance( Abstract_Main_Plugin_Class $main_plugin , Plugin_Constants $constants , Helper_Functions $helper_functions ) {
115
+
116
+ if ( !self::$_instance instanceof self )
117
+ self::$_instance = new self( $main_plugin , $constants , $helper_functions );
118
+
119
+ return self::$_instance;
120
+
121
+ }
122
+
123
+ /**
124
+ * Update $_browser_zone_str class property value.
125
+ *
126
+ * @since 3.3.3
127
+ * @access public
128
+ *
129
+ * @param string $timezone Timezone set on browser
130
+ */
131
+ public function set_browser_zone_str( $timezone ) {
132
+
133
+ if ( in_array( $timezone , timezone_identifiers_list() ) )
134
+ $this->_browser_zone_str = $timezone;
135
+ }
136
+
137
+ /**
138
+ * Register admin interfaces.
139
+ *
140
+ * @since 3.3.2
141
+ * @access public
142
+ *
143
+ * @param array $interfaces List of admin interfaces.
144
+ * @return array Filtered list of admin interfaces.
145
+ */
146
+ public function register_admin_interfaces( $interfaces ) {
147
+
148
+ $interfaces[ 'thirstylink_page_thirsty-reports' ] = apply_filters( 'ta_reports_admin_interface' , array(
149
+ 'link_performance' => 'manage_options'
150
+ ) );
151
+
152
+ return $interfaces;
153
+ }
154
+
155
+ /**
156
+ * Register admin interfaces.
157
+ *
158
+ * @since 3.3.2
159
+ * @access public
160
+ *
161
+ * @param array $interfaces List of menu items.
162
+ * @return array Filtered list of menu items.
163
+ */
164
+ public function register_admin_menu_items( $menu_items ) {
165
+
166
+ $menu_items[ 'thirsty-reports' ] = 'manage_options';
167
+ return $menu_items;
168
+ }
169
+
170
+
171
+
172
+
173
+ /*
174
+ |--------------------------------------------------------------------------
175
+ | Data saving
176
+ |--------------------------------------------------------------------------
177
+ */
178
+
179
+ /**
180
+ * Save link click data to the database.
181
+ *
182
+ * @since 3.0.0
183
+ * @since 3.1.0 Set to save additional information: $cloaked_url , $redirect_url and $redirect_type.
184
+ * @since 3.2.0 Set to save additonal information: keyword.
185
+ * @since 3.4.0 Add tracking for browser/device.
186
+ * @access private
187
+ *
188
+ * @global wpdb $wpdb Object that contains a set of functions used to interact with a database.
189
+ *
190
+ * @param Affiliate_Link $thirstylink Affiliate link object.
191
+ * @param string $http_referer HTTP Referrer value.
192
+ * @param string cloaked_url Affiliate link cloaked url.
193
+ * @param string $redirect_url Link to where user is redirected to.
194
+ * @param string $redirect_type Redirect type (301,302, etc.)
195
+ * @param string $keyword Affiliate link keyword.
196
+ */
197
+ private function save_click_data( $thirstylink , $http_referer , $cloaked_url , $redirect_url , $redirect_type , $keyword = '' ) {
198
+
199
+ global $wpdb;
200
+
201
+ if ( apply_filters( 'ta_filter_before_save_click' , false , $thirstylink , $http_referer ) )
202
+ return;
203
+
204
+ $link_click_db = $wpdb->prefix . Plugin_Constants::LINK_CLICK_DB;
205
+ $link_click_meta_db = $wpdb->prefix . Plugin_Constants::LINK_CLICK_META_DB;
206
+
207
+ // insert click entry
208
+ $wpdb->insert(
209
+ $link_click_db,
210
+ array(
211
+ 'link_id' => $thirstylink->get_id(),
212
+ 'date_clicked' => current_time( 'mysql' , true )
213
+ )
214
+ );
215
+
216
+ // save click meta data
217
+ if ( $click_id = $wpdb->insert_id ) {
218
+
219
+ $meta_data = apply_filters( 'ta_save_click_data' , array(
220
+ 'user_ip_address' => $this->_helper_functions->get_user_ip_address(),
221
+ 'http_referer' => $http_referer,
222
+ 'cloaked_url' => $cloaked_url,
223
+ 'redirect_url' => $redirect_url,
224
+ 'redirect_type' => $redirect_type,
225
+ 'keyword' => $keyword,
226
+ 'browser_device' => $this->_helper_functions->get_visitor_browser_device()
227
+ ), $thirstylink );
228
+
229
+ foreach ( $meta_data as $key => $value ) {
230
+
231
+ // make sure there is a key and a value before saving.
232
+ if ( ! $key || ! $value )
233
+ continue;
234
+
235
+ $wpdb->insert(
236
+ $link_click_meta_db,
237
+ array(
238
+ 'click_id' => $click_id,
239
+ 'meta_key' => $key,
240
+ 'meta_value' => $value
241
+ )
242
+ );
243
+ }
244
+
245
+ }
246
+ }
247
+
248
+ /**
249
+ * Save click data on redirect
250
+ *
251
+ * @since 3.0.0
252
+ * @since 3.1.0 Passed additional 2 parameters: $redirect_url and $redirect type. Updated save_click_data function call to include new required arguments.
253
+ * @since 3.3.0 If enhanced javascript redirect in frontend is enabled, then all server redirects are allowed (with this, stats links clicked via "open new tab" will be saved).
254
+ * @access public
255
+ *
256
+ * @param Affiliate_Link $thirstylink Affiliate link object.
257
+ * @param string $redirect_url Link to where user is redirected to.
258
+ * @param string $redirect_type Redirect type (301,302, etc.)
259
+ */
260
+ public function save_click_data_on_redirect( $thirstylink , $redirect_url , $redirect_type ) {
261
+
262
+ // make sure that this is only runs on the frontend and not on the backend.
263
+ if ( is_admin() ) return;
264
+
265
+ $link_id = $thirstylink->get_id();
266
+ $http_referer = isset( $_SERVER[ 'HTTP_REFERER' ] ) ? $_SERVER[ 'HTTP_REFERER' ] : '';
267
+ $query_string = isset( $_SERVER[ 'QUERY_STRING' ] ) ? $_SERVER[ 'QUERY_STRING' ] : '';
268
+ $cloaked_url = $query_string ? $thirstylink->get_prop( 'permalink' ) . '?' . $query_string : $thirstylink->get_prop( 'permalink' );
269
+
270
+ $same_site = $http_referer && strrpos( 'x' . $http_referer , home_url() );
271
+ $admin_referrer = $http_referer && strrpos( 'x' . $http_referer , admin_url() );
272
+ $js_redirect_enabled = get_option( 'ta_enable_javascript_frontend_redirect' ) == 'yes';
273
+
274
+ // NOTE: this fixes a bug reported on TA-250
275
+ if ( $http_referer == $cloaked_url || $admin_referrer ) return;
276
+
277
+ // if the refferer is from an external site, then record stat.
278
+ if ( ( $same_site && $js_redirect_enabled ) || ! $same_site )
279
+ $this->save_click_data( $thirstylink , $http_referer , $cloaked_url , $redirect_url , $redirect_type );
280
+ }
281
+
282
+ /**
283
+ * AJAX save click data on redirect
284
+ *
285
+ * @since 3.0.0
286
+ * @since 3.1.0 Updated save_click_data function call to include new required arguments.
287
+ * @since 3.2.0 Updated save_click_data function call to include keyword argument.
288
+ * @since 3.3.0 print actual affiliate link redirect url for ehanced javascript redirect support.
289
+ * @since 3.4.0 Add query string as parameter to support passing query strings in enhanced JS redirect.
290
+ * @access public
291
+ */
292
+ public function ajax_save_click_data_on_redirect() {
293
+
294
+ if ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX )
295
+ wp_die();
296
+
297
+ $link_id = isset( $_REQUEST[ 'link_id' ] ) ? (int) sanitize_text_field( $_REQUEST[ 'link_id' ] ) : 0;
298
+ $http_referer = isset( $_REQUEST[ 'page' ] ) ? esc_url_raw( $_REQUEST[ 'page' ] ) : '';
299
+ $cloaked_url = isset( $_REQUEST[ 'href' ] ) ? esc_url_raw( $_REQUEST[ 'href' ] ) : '';
300
+ $keyword = isset( $_REQUEST[ 'keyword' ] ) ? sanitize_text_field( $_REQUEST[ 'keyword' ] ) : '';
301
+ $query_string = isset( $_REQUEST[ 'qs' ] ) ? sanitize_text_field( $_REQUEST[ 'qs' ] ) : '';
302
+
303
+ if ( ! $link_id )
304
+ $link_id = url_to_postid( $cloaked_url );
305
+
306
+ if ( $link_id ) {
307
+
308
+ $thirstylink = new Affiliate_Link( $link_id );
309
+ $redirect_url = apply_filters( 'ta_filter_redirect_url' , $thirstylink->get_prop( 'destination_url' ) , $thirstylink , $query_string );
310
+ $redirect_type = $thirstylink->get_redirect_type();
311
+
312
+ $this->save_click_data( $thirstylink , $http_referer , $cloaked_url , $redirect_url , $redirect_type , $keyword );
313
+
314
+ // print actual affiliate link redirect url for enhanced javascript redirect support.
315
+ if ( get_option( 'ta_enable_javascript_frontend_redirect' ) == 'yes' )
316
+ echo $redirect_url;
317
+ }
318
+
319
+ wp_die();
320
+ }
321
+
322
+
323
+
324
+
325
+ /*
326
+ |--------------------------------------------------------------------------
327
+ | Fetch Report Data
328
+ |--------------------------------------------------------------------------
329
+ */
330
+
331
+ /**
332
+ * Fetch link performance data by date range.
333
+ *
334
+ * @since 3.0.0
335
+ * @access public
336
+ *
337
+ * @global wpdb $wpdb Object that contains a set of functions used to interact with a database.
338
+ *
339
+ * @param string $start_date Report start date. Format: YYYY-MM-DD hh:mm:ss
340
+ * @param string $end_date Report end date. Format: YYYY-MM-DD hh:mm:ss
341
+ * @param array $link_ids Affiliate Link post ID
342
+ * @return string/array Link click meta data value.
343
+ */
344
+ public function get_link_performance_data( $start_date , $end_date , $link_ids ) {
345
+
346
+ global $wpdb;
347
+
348
+ if ( ! is_array( $link_ids ) || empty( $link_ids ) )
349
+ return array();
350
+
351
+ $link_clicks_db = $wpdb->prefix . Plugin_Constants::LINK_CLICK_DB;
352
+ $link_ids_str = implode( ', ' , $link_ids );
353
+ $query = "SELECT * FROM $link_clicks_db WHERE date_clicked between '$start_date' and '$end_date' and link_id IN ( $link_ids_str )";
354
+
355
+ return $wpdb->get_results( $query );
356
+ }
357
+
358
+ /**
359
+ * Get link click meta by id and key.
360
+ *
361
+ * @since 3.0.0
362
+ * @access public
363
+ *
364
+ * @param int $click_id Link click ID.
365
+ * @param string $meta_key Meta key column value.
366
+ * @param boolean $single Return single result or array.
367
+ * @return array Link performance data.
368
+ */
369
+ private function get_click_meta( $click_id , $meta_key , $single = false ) {
370
+
371
+ global $wpdb;
372
+
373
+ $links_click_meta_db = $wpdb->prefix . Plugin_Constants::LINK_CLICK_META_DB;
374
+
375
+ if ( $single ){
376
+
377
+ $meta = $wpdb->get_row( "SELECT meta_value FROM $links_click_meta_db WHERE click_id = '$click_id' and meta_key = '$meta_key'" , ARRAY_A );
378
+ return array_shift( $meta );
379
+
380
+ } else {
381
+
382
+ $meta = array();
383
+ $raw_data = $wpdb->get_results( "SELECT meta_value FROM $links_click_meta_db WHERE click_id = '$click_id' and meta_key = '$meta_key'" , ARRAY_N );
384
+
385
+ foreach ( $raw_data as $data )
386
+ $meta[] = array_shift( $data );
387
+
388
+ return $meta;
389
+ }
390
+ }
391
+
392
+ /**
393
+ * AJAX fetch report by linkid.
394
+ *
395
+ * @since 3.0.0
396
+ * @since 3.1.2 Add total clicks count on the response.
397
+ * @access public
398
+ */
399
+ public function ajax_fetch_report_by_linkid() {
400
+
401
+ if ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX )
402
+ $response = array( 'status' => 'fail' , 'error_msg' => __( 'Invalid AJAX call' , 'thirstyaffiliates' ) );
403
+ elseif ( ! isset( $_POST[ 'link_id' ] ) )
404
+ $response = array( 'status' => 'fail' , 'error_msg' => __( 'Missing required post data' , 'thirstyaffiliates' ) );
405
+ else {
406
+
407
+ // save timezone to use
408
+ $timezone = isset( $_POST[ 'timezone' ] ) ? sanitize_text_field( $_POST[ 'timezone' ] ) : '';
409
+ $this->set_browser_zone_str( $timezone );
410
+
411
+ $link_id = isset( $_POST[ 'link_id' ] ) ? (int) sanitize_text_field( $_POST[ 'link_id' ] ) : 0;
412
+ $thirstylink = new Affiliate_Link( $link_id );
413
+ $range_txt = isset( $_POST[ 'range' ] ) ? sanitize_text_field( $_POST[ 'range' ] ) : '';
414
+ $start_date = isset( $_POST[ 'start_date' ] ) ? sanitize_text_field( $_POST[ 'start_date' ] ) : '';
415
+ $end_date = isset( $_POST[ 'end_date' ] ) ? sanitize_text_field( $_POST[ 'end_date' ] ) : '';
416
+
417
+ if ( ! $thirstylink->get_id() )
418
+ $response = array( 'status' => 'fail' , 'error_msg' => __( 'Selected affiliate link is invalid' , 'thirstyaffiliates' ) );
419
+ else {
420
+
421
+ $range = $this->get_report_range_details( $range_txt , $start_date , $end_date );
422
+ $data = $this->prepare_data_for_flot( $range , array( $link_id ) );
423
+
424
+ $response = array(
425
+ 'status' => 'success',
426
+ 'label' => $thirstylink->get_prop( 'name' ),
427
+ 'slug' => $thirstylink->get_prop( 'slug' ),
428
+ 'report_data' => $data,
429
+ 'total_clicks' => $this->count_total_clicks_from_flot_data( $data )
430
+ );
431
+ }
432
+ }
433
+
434
+ @header( 'Content-Type: application/json; charset=' . get_option( 'blog_charset' ) );
435
+ echo wp_json_encode( $response );
436
+ wp_die();
437
+ }
438
+
439
+ /**
440
+ * AJAX init first report.
441
+ *
442
+ * @since 3.2.2
443
+ * @access public
444
+ */
445
+ public function ajax_init_first_report() {
446
+
447
+ if ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX )
448
+ $response = array( 'status' => 'fail' , 'error_msg' => __( 'Invalid AJAX call' , 'thirstyaffiliates' ) );
449
+ elseif ( ! isset( $_POST[ 'timezone' ] ) )
450
+ $response = array( 'status' => 'fail' , 'error_msg' => __( 'Missing required post data' , 'thirstyaffiliates' ) );
451
+ else {
452
+
453
+ // save timezone to use
454
+ $timezone = isset( $_POST[ 'timezone' ] ) ? sanitize_text_field( $_POST[ 'timezone' ] ) : '';
455
+ $this->set_browser_zone_str( $timezone );
456
+
457
+ $cpt_slug = Plugin_Constants::AFFILIATE_LINKS_CPT;
458
+ $current_range = isset( $_POST[ 'range' ] ) ? sanitize_text_field( $_POST[ 'range' ] ) : '7day';
459
+ $start_date = isset( $_POST[ 'start_date' ] ) ? sanitize_text_field( $_POST[ 'start_date' ] ) : '';
460
+ $end_date = isset( $_POST[ 'end_date' ] ) ? sanitize_text_field( $_POST[ 'end_date' ] ) : '';
461
+ $range = $this->get_report_range_details( $current_range , $start_date , $end_date );
462
+
463
+ // get all published affiliate link ids
464
+ $query = new \WP_Query( array(
465
+ 'post_type' => $cpt_slug,
466
+ 'post_status' => 'publish',
467
+ 'fields' => 'ids',
468
+ 'posts_per_page' => -1
469
+ ) );
470
+
471
+ $data = $this->prepare_data_for_flot( $range , $query->posts );
472
+ $total_clicks = $this->count_total_clicks_from_flot_data( $data );
473
+
474
+ $response = array(
475
+ 'status' => 'success',
476
+ 'flot_data' => $data,
477
+ 'total_clicks' => $total_clicks,
478
+ );
479
+
480
+ }
481
+
482
+ @header( 'Content-Type: application/json; charset=' . get_option( 'blog_charset' ) );
483
+ echo wp_json_encode( $response );
484
+ wp_die();
485
+ }
486
+
487
+
488
+
489
+
490
+ /*
491
+ |--------------------------------------------------------------------------
492
+ | Reports Structure
493
+ |--------------------------------------------------------------------------
494
+ */
495
+
496
+ /**
497
+ * Get all registered reports.
498
+ *
499
+ * @since 3.0.0
500
+ * @access public
501
+ *
502
+ * @return array Settings sections.
503
+ */
504
+ public function get_all_reports() {
505
+
506
+ return apply_filters( 'ta_register_reports' , array() );
507
+ }
508
+
509
+ /**
510
+ * Get current loaded report.
511
+ *
512
+ * @since 3.0.0
513
+ * @access public
514
+ *
515
+ * @param string $tab Current report tab.
516
+ * @return array Current loaded report.
517
+ */
518
+ public function get_current_report( $tab = '' ) {
519
+
520
+ if ( ! $tab )
521
+ $tab = isset( $_GET[ 'tab' ] ) ? esc_attr( $_GET[ 'tab' ] ) : 'link_performance';
522
+
523
+ // get all registered sections and fields
524
+ $reports = $this->get_all_reports();
525
+
526
+ return isset( $reports[ $tab ] ) ? $reports[ $tab ] : array();
527
+ }
528
+
529
+ /**
530
+ * Register link performance report.
531
+ *
532
+ * @since 3.0.0
533
+ * @access public
534
+ *
535
+ * @param array $reports Array list of all registered reports.
536
+ * @return array Array list of all registered reports.
537
+ */
538
+ public function register_link_performance_report( $reports ) {
539
+
540
+ $reports[ 'link_performance' ] = array(
541
+ 'id' => 'ta_link_performance_report',
542
+ 'tab' => 'link_performance',
543
+ 'name' => __( 'Link Overview' , 'thirstyaffiliates' ),
544
+ 'title' => __( 'Link Overview Report' , 'thirstyaffiliates' ),
545
+ 'desc' => __( 'Total clicks on affiliate links over a given period.' , 'thirstyaffiliates' ),
546
+ 'content' => function() { return $this->get_link_performance_report_content(); }
547
+ );
548
+
549
+ return $reports;
550
+ }
551
+
552
+
553
+
554
+
555
+ /*
556
+ |--------------------------------------------------------------------------
557
+ | Display Report
558
+ |--------------------------------------------------------------------------
559
+ */
560
+
561
+ /**
562
+ * Register reports menu page.
563
+ *
564
+ * @since 3.0.0
565
+ * @since 3.2.2 Access to the settings page will now be controlled by the plugin. see Bootstrap::admin_interface_visibility.
566
+ *
567
+ * @access public
568
+ */
569
+ public function add_reports_submenu() {
570
+
571
+ if ( ! current_user_can( 'edit_posts' ) ) return;
572
+
573
+ add_submenu_page(
574
+ 'edit.php?post_type=thirstylink',
575
+ __( 'ThirstyAffiliates Reports' , 'thirstyaffiliates' ),
576
+ __( 'Reports' , 'thirstyaffiliates' ),
577
+ 'read',
578
+ 'thirsty-reports',
579
+ array( $this, 'render_reports' )
580
+ );
581
+ }
582
+
583
+ /**
584
+ * Render reports page.
585
+ *
586
+ * @since 3.0.0
587
+ * @access public
588
+ */
589
+ public function render_reports() {
590
+
591
+ // fetch current section
592
+ $current_report = $this->get_current_report();
593
+ $report_content = is_callable( $current_report[ 'content' ] ) ? $current_report[ 'content' ]() : $current_report[ 'content' ];
594
+
595
+ // skip if section data is empty
596
+ if ( empty( $current_report ) ) return; ?>
597
+
598
+ <div class="ta-settings ta-settings-<?php echo $current_report[ 'tab' ]; ?> wrap">
599
+
600
+ <?php $this->render_reports_nav(); ?>
601
+
602
+ <h1><?php echo $current_report[ 'title' ]; ?></h1>
603
+ <p class="desc"><?php echo $current_report[ 'desc' ]; ?></p>
604
+
605
+ <?php echo $report_content; ?>
606
+ </div>
607
+ <?php
608
+ }
609
+
610
+ /**
611
+ * Render the settings navigation.
612
+ *
613
+ * @since 3.0.0
614
+ * @access public
615
+ */
616
+ public function render_reports_nav() {
617
+
618
+ $reports = $this->get_all_reports();
619
+ $current = $this->get_current_report();
620
+ $base_url = admin_url( 'edit.php?post_type=thirstylink&page=thirsty-reports' );
621
+
622
+ if ( empty( $reports ) ) return; ?>
623
+
624
+ <nav class="thirsty-nav-tab">
625
+ <?php foreach ( $reports as $report ) : ?>
626
+
627
+ <a href="<?php echo $base_url . '&tab=' . $report[ 'tab' ]; ?>" class="tab <?php echo ( $current[ 'tab' ] === $report[ 'tab' ] ) ? 'tab-active' : ''; ?>">
628
+ <?php echo $report[ 'name' ]; ?>
629
+ </a>
630
+
631
+ <?php endforeach; ?>
632
+ </nav>
633
+
634
+ <?php
635
+ }
636
+
637
+ /**
638
+ * Get Link performance report content.
639
+ *
640
+ * @since 3.0.0
641
+ * @since 3.3.2 Remove report data query on page first load.
642
+ * @access public
643
+ *
644
+ * @return string Link performance report content.
645
+ */
646
+ public function get_link_performance_report_content() {
647
+
648
+ $cpt_slug = Plugin_Constants::AFFILIATE_LINKS_CPT;
649
+ $current_range = isset( $_GET[ 'range' ] ) ? sanitize_text_field( $_GET[ 'range' ] ) : '7day';
650
+ $start_date = isset( $_GET[ 'start_date' ] ) ? sanitize_text_field( $_GET[ 'start_date' ] ) : '';
651
+ $end_date = isset( $_GET[ 'end_date' ] ) ? sanitize_text_field( $_GET[ 'end_date' ] ) : '';
652
+ $link_id = isset( $_GET[ 'link_id' ] ) ? sanitize_text_field( $_GET[ 'link_id' ] ) : '';
653
+ $range = $this->get_report_range_details( $current_range , $start_date , $end_date );
654
+ $range_nav = apply_filters( 'ta_link_performances_report_nav' , array(
655
+ 'year' => __( 'Year' , 'thirstyaffiliates' ),
656
+ 'last_month' => __( 'Last Month' , 'thirstyaffiliates' ),
657
+ 'month' => __( 'This Month' , 'thirstyaffiliates' ),
658
+ '7day' => __( 'Last 7 Days' , 'thirstyaffiliates' )
659
+ ) );
660
+
661
+ // make sure link_id is an affiliate link (published).
662
+ // NOTE: when false, this needs to return an empty string as it is used for display.
663
+ if ( $link_id ) $link_id = ( get_post_type( $link_id ) == $cpt_slug && get_post_status( $link_id ) == 'publish' ) ? $link_id : '';
664
+
665
+ ob_start();
666
+ include( $this->_constants->VIEWS_ROOT_PATH() . 'reports/link-performance-report.php' );
667
+
668
+ return ob_get_clean();
669
+ }
670
+
671
+
672
+
673
+
674
+ /*
675
+ |--------------------------------------------------------------------------
676
+ | Helper methods
677
+ |--------------------------------------------------------------------------
678
+ */
679
+
680
+ /**
681
+ * Get report range details.
682
+ *
683
+ * @since 3.0.0
684
+ * @since 3.2.2 Change method of getting timezone sting name.
685
+ * @access public
686
+ *
687
+ * @param string $range Report range type.
688
+ * @param string $start_date Starting date of range.
689
+ * @param string $end_date Ending date of range.
690
+ * @return array Report range details.
691
+ */
692
+ public function get_report_range_details( $range = '7day' , $start_date = 'now -6 days' , $end_date = 'now' ) {
693
+
694
+ $data = array();
695
+ $zone_str = $this->get_report_timezone_string();
696
+ $timezone = new \DateTimeZone( $zone_str );
697
+ $now = new \DateTime( 'now' , $timezone );
698
+
699
+ switch ( $range ) {
700
+
701
+ case 'year' :
702
+ $data[ 'type' ] = 'year';
703
+ $data[ 'start_date' ] = new \DateTime( 'first day of January' . date( 'Y' ) , $timezone );
704
+ $data[ 'end_date' ] = $now;
705
+ break;
706
+
707
+ case 'last_month' :
708
+ $data[ 'type' ] = 'last_month';
709
+ $data[ 'start_date' ] = new \DateTime( 'first day of last month' , $timezone );
710
+ $data[ 'end_date' ] = new \DateTime( 'last day of last month' , $timezone );
711
+ break;
712
+
713
+ case 'month' :
714
+ $data[ 'type' ] = 'month';
715
+ $data[ 'start_date' ] = new \DateTime( 'first day of this month' , $timezone );
716
+ $data[ 'end_date' ] = $now;
717
+ $data[ 'start_date' ]->setTime( 0 , 0 , 0 );
718
+ break;
719
+
720
+ case 'custom' :
721
+ $data[ 'type' ] = 'custom';
722
+ $data[ 'start_date' ] = new \DateTime( $start_date , $timezone );
723
+ $data[ 'end_date' ] = new \DateTime( $end_date . ' 23:59:59' , $timezone );
724
+ break;
725
+
726
+ case '7day' :
727
+ default :
728
+ $start_date = new \DateTime( 'now -6 days' , $timezone );
729
+
730
+ // set hours, minutes and seconds to zero
731
+ $start_date->setTime( 0 , 0 , 0 );
732
+ $now->setTime( 23 , 59 , 59 );
733
+
734
+ $data[ 'type' ] = '7day';
735
+ $data[ 'start_date' ] = $start_date;
736
+ $data[ 'end_date' ] = $now;
737
+ break;
738
+ }
739
+
740
+ return apply_filters( 'ta_report_range_data' , $data , $range );
741
+ }
742
+
743
+ /**
744
+ * Prepare data to feed for jQuery flot.
745
+ *
746
+ * @since 3.0.0
747
+ * @since 3.2.2 Change method of getting timezone sting name.
748
+ * @since 3.3.3 Set range timezone to UTC before fetching raw data.
749
+ * @since 3.3.4 Set date range timezone back to local browser before setting start time to zero.
750
+ * @access public
751
+ *
752
+ * @param array $range Report range details
753
+ * @param array $link_ids Affiliate Link post ID
754
+ * @return array Processed data for jQuery flot.
755
+ */
756
+ public function prepare_data_for_flot( $range , $link_ids ) {
757
+
758
+ $start_date = $range[ 'start_date' ];
759
+ $end_date = $range[ 'end_date' ];
760
+ $zone_str = $this->get_report_timezone_string();
761
+ $timezone = new \DateTimeZone( $zone_str );
762
+ $utc = new \DateTimeZone( 'UTC' );
763
+ $flot_data = array();
764
+
765
+ $start_date->setTimezone( $timezone );
766
+ $end_date->setTimezone( $timezone );
767
+
768
+ if ( apply_filters( 'ta_report_set_start_date_time_to_zero' , true , $range ) )
769
+ $start_date->setTime( 0 , 0 );
770
+
771
+ $start_date->setTimezone( $utc );
772
+ $end_date->setTimezone( $utc );
773
+
774
+ $raw_data = $this->get_link_performance_data( $start_date->format( 'Y-m-d H:i:s' ) , $end_date->format( 'Y-m-d H:i:s' ) , $link_ids );
775
+
776
+ // get number of days difference between start and end
777
+ $incrementor = apply_filters( 'ta_report_flot_data_incrementor' , ( 60 * 60 * 24 ) , $range );
778
+ $timestamp_diff = ( $start_date->getTimestamp() - $end_date->getTimestamp() );
779
+ $days_diff = abs( floor( $timestamp_diff / $incrementor ) );
780
+
781
+ // save the timestamp for first day
782
+ $timestamp = $start_date->format( 'U' );
783
+ $month_time = $this->get_month_first_day_datetime_obj( 'February' );
784
+ $next_timestamp = ( $range[ 'type' ] == 'year' ) ? $month_time->format( 'U' ) : $timestamp + $incrementor;
785
+ $flot_data[] = array(
786
+ 'timestamp' => (int) $timestamp,
787
+ 'count' => 0,
788
+ 'next_timestamp' => $next_timestamp
789
+ );
790
+
791
+ if ( $range[ 'type' ] == 'year' ) {
792
+
793
+ $months = array( 'February' , 'March' , 'April' , 'May' , 'June' , 'July' , 'August' , 'September' , 'October' , 'November' , 'December' );
794
+
795
+ foreach ( $months as $key => $month ) {
796
+
797
+ $month_time = $this->get_month_first_day_datetime_obj( $month );
798
+ $next_month = isset( $months[ $key + 1 ] ) ? $this->get_month_first_day_datetime_obj( $months[ $key + 1 ] ) : new \DateTime( 'now' , $timezone );
799
+
800
+ $flot_data[] = array(
801
+ 'timestamp' => $month_time->format( 'U' ),
802
+ 'count' => 0,
803
+ 'next_timestamp' => $next_month->format( 'U' )
804
+ );
805
+
806
+ if ( $end_date->format( 'F' ) == $month )
807
+ break;
808
+ }
809
+
810
+ } else {
811
+
812
+ // determine timestamps for succeeding days
813
+ for ( $x = 1; $x < $days_diff; $x++ ) {
814
+
815
+ $timestamp = $next_timestamp;
816
+ $next_timestamp = $timestamp + $incrementor;
817
+
818
+ $flot_data[] = array(
819
+ 'timestamp' => (int) $timestamp,
820
+ 'count' => 0,
821
+ 'next_timestamp' => $next_timestamp
822
+ );
823
+
824
+ }
825
+ }
826
+
827
+ // count each click data and assign to appropriate day.
828
+ foreach ( $raw_data as $click_entry ) {
829
+
830
+ $click_date = new \DateTime( $click_entry->date_clicked , $utc );
831
+ $click_date->setTimezone( $timezone );
832
+
833
+ $click_timestamp = (int) $click_date->format( 'U' );
834
+
835
+ foreach ( $flot_data as $key => $day_data ) {
836
+
837
+ if ( $click_timestamp >= $day_data[ 'timestamp' ] && $click_timestamp < $day_data[ 'next_timestamp' ] ) {
838
+ $flot_data[ $key ][ 'count' ] += 1;
839
+ continue;
840
+ }
841
+ }
842
+ }
843
+
844
+ // convert $flot_data array into non-associative array
845
+ foreach ( $flot_data as $key => $day_data ) {
846
+
847
+ unset( $day_data[ 'next_timestamp' ] );
848
+
849
+ $day_data[ 'timestamp' ] = $day_data[ 'timestamp' ] * 1000;
850
+ $flot_data[ $key ] = array_values( $day_data );
851
+ }
852
+
853
+ return $flot_data;
854
+ }
855
+
856
+ /**
857
+ * Count total clicks from flot data.
858
+ *
859
+ * @since 3.2.1
860
+ * @access public
861
+ *
862
+ * @param array $data Flot data.
863
+ * @param int $total Total click counts (offset).
864
+ * @return int Total click clounts.
865
+ */
866
+ public function count_total_clicks_from_flot_data( $data , $total = 0 ) {
867
+
868
+ if ( ! is_array( $data ) || empty( $data ) )
869
+ return;
870
+
871
+ foreach ( $data as $flot )
872
+ $total += intval( $flot[1] );
873
+
874
+ return $total;
875
+ }
876
+
877
+ /**
878
+ * Get the DateTime object of the first day of a given month.
879
+ *
880
+ * @since 3.0.0
881
+ * @since 3.2.2 Change method of getting timezone sting name.
882
+ * @access public
883
+ *
884
+ * @param string $month Month full textual representation.
885
+ * @return DateTime First day of the given month DateTime object.
886
+ */
887
+ public function get_month_first_day_datetime_obj( $month ) {
888
+
889
+ $zone_str = $this->get_report_timezone_string();
890
+ $timezone = new \DateTimeZone( $zone_str );
891
+
892
+ return new \DateTime( 'First day of ' . $month . ' ' . date( 'Y' ) , $timezone );
893
+ }
894
+
895
+ /**
896
+ * Schedule stats trimmer cron job.
897
+ *
898
+ * @since 3.1.0
899
+ * @access private
900
+ */
901
+ private function schedule_stats_trimmer_cron() {
902
+
903
+ $zone_str = $this->_helper_functions->get_site_current_timezone();
904
+ $timezone = new \DateTimeZone( $zone_str );
905
+ $time = new \DateTime( 'first day of next month' , $timezone );
906
+
907
+ // clear all scheduled crons so there will always only be one.
908
+ wp_clear_scheduled_hook( Plugin_Constants::CRON_STATS_TRIMMER );
909
+
910
+ // schedule cron job
911
+ wp_schedule_single_event( $time->format( 'U' ) , Plugin_Constants::CRON_STATS_TRIMMER );
912
+ }
913
+
914
+ /**
915
+ * Implement stats trimmer
916
+ *
917
+ * @since 3.1.0
918
+ * @access public
919
+ *
920
+ * @global wpdb $wpdb Object that contains a set of functions used to interact with a database.
921
+ */
922
+ public function implement_stats_trimmer() {
923
+
924
+ global $wpdb;
925
+
926
+ $trim_point = (int) get_option( 'ta_stats_trimer_set_point' , 0 );
927
+
928
+ if ( $trim_point > 0 ) {
929
+
930
+ $clicks_db = $wpdb->prefix . Plugin_Constants::LINK_CLICK_DB;
931
+ $clicks_meta_db = $wpdb->prefix . Plugin_Constants::LINK_CLICK_META_DB;
932
+
933
+ // get click ids based on set range.
934
+ $query = "SELECT id FROM $clicks_db WHERE date_clicked < DATE_ADD( NOW() , INTERVAL -" . $trim_point . " MONTH )";
935
+ $click_ids = $wpdb->get_col( $query );
936
+
937
+ // Proceed on deleting data when $click_ids are present
938
+ if ( is_array( $click_ids ) && ! empty( $click_ids ) ) {
939
+
940
+ $click_ids_string = implode( $click_ids , ',' );
941
+
942
+ // delete click data
943
+ $wpdb->query( "DELETE FROM $clicks_meta_db WHERE click_id IN ( $click_ids_string )" );
944
+ $wpdb->query( "DELETE FROM $clicks_db WHERE id IN ( $click_ids_string )" );
945
+ }
946
+ }
947
+
948
+ // reschedule the cron job
949
+ $this->schedule_stats_trimmer_cron();
950
+ }
951
+
952
+ /**
953
+ * Prevent saving click data if useragent is a bot (for non-apache servers).
954
+ *
955
+ * @since 3.1.0
956
+ * @since 3.3.3 Moved code to a helper function (DRY).
957
+ * @access public
958
+ *
959
+ * @param boolean $response Default response of filter.
960
+ * @return boolean True if needs to be prevented, false otherwise.
961
+ */
962
+ public function prevent_save_click_if_useragent_is_bot( $response ) {
963
+
964
+ return $this->_helper_functions->is_user_agent_bot();
965
+ }
966
+
967
+ /**
968
+ * Delete stats data when an affiliate link is deleted permanently.
969
+ *
970
+ * @since 3.0.1
971
+ * @access public
972
+ *
973
+ * @global wpdb $wpdb Object that contains a set of functions used to interact with a database.
974
+ *
975
+ * @param int $link_id Affiliate link ID.
976
+ */
977
+ public function delete_stats_data_on_affiliate_link_deletion( $link_id ) {
978
+
979
+ global $wpdb;
980
+
981
+ if ( Plugin_Constants::AFFILIATE_LINKS_CPT !== get_post_type( $link_id ) )
982
+ return;
983
+
984
+ $link_click_db = $wpdb->prefix . Plugin_Constants::LINK_CLICK_DB;
985
+ $link_click_meta_db = $wpdb->prefix . Plugin_Constants::LINK_CLICK_META_DB;
986
+ $click_ids = $wpdb->get_col( "SELECT id FROM $link_click_db WHERE link_id = $link_id" );
987
+
988
+ if ( ! is_array( $click_ids ) || empty( $click_ids ) )
989
+ return;
990
+
991
+ $click_ids_str = implode( ',' , $click_ids );
992
+
993
+ // delete click meta records.
994
+ $wpdb->query( "DELETE FROM $link_click_meta_db WHERE click_id IN ( $click_ids_str )" );
995
+
996
+ // delete click records.
997
+ $wpdb->query( "DELETE FROM $link_click_db WHERE id IN ( $click_ids_str )" );
998
+ }
999
+
1000
+ /**
1001
+ * Get timezone to use for the report.
1002
+ *
1003
+ * @since 3.2.2
1004
+ * @since 3.3.3 Made the method public so TAP can utilize it.
1005
+ * @access public
1006
+ *
1007
+ * @return string Timezone string name.
1008
+ */
1009
+ public function get_report_timezone_string() {
1010
+
1011
+ return $this->_browser_zone_str ? $this->_browser_zone_str : $this->_helper_functions->get_site_current_timezone();
1012
+ }
1013
+
1014
+
1015
+
1016
+
1017
+ /*
1018
+ |--------------------------------------------------------------------------
1019
+ | Fulfill implemented interface contracts
1020
+ |--------------------------------------------------------------------------
1021
+ */
1022
+
1023
+ /**
1024
+ * Execute codes that needs to run plugin activation.
1025
+ *
1026
+ * @since 3.0.0
1027
+ * @access public
1028
+ * @implements ThirstyAffiliates\Interfaces\Activatable_Interface
1029
+ */
1030
+ public function activate() {
1031
+
1032
+ $this->schedule_stats_trimmer_cron();
1033
+ }
1034
+
1035
+ /**
1036
+ * Method that houses codes to be executed on init hook.
1037
+ *
1038
+ * @since 3.0.0
1039
+ * @access public
1040
+ * @inherit ThirstyAffiliates\Interfaces\Initiable_Interface
1041
+ */
1042
+ public function initialize() {
1043
+
1044
+ // When module is disabled in the settings, then it shouldn't run the whole class.
1045
+ if ( get_option( 'ta_enable_stats_reporting_module' , 'yes' ) !== 'yes' )
1046
+ return;
1047
+
1048
+ add_action( 'wp_ajax_ta_click_data_redirect' , array( $this , 'ajax_save_click_data_on_redirect' ) , 10 );
1049
+ add_action( 'wp_ajax_ta_fetch_report_by_linkid' , array( $this , 'ajax_fetch_report_by_linkid' ) , 10 );
1050
+ add_action( 'wp_ajax_ta_init_first_report' , array( $this , 'ajax_init_first_report' ) , 10 );
1051
+ add_action( 'wp_ajax_nopriv_ta_click_data_redirect' , array( $this , 'ajax_save_click_data_on_redirect' ) , 10 );
1052
+ }
1053
+
1054
+ /**
1055
+ * Execute ajax handler.
1056
+ *
1057
+ * @since 3.0.0
1058
+ * @access public
1059
+ * @inherit ThirstyAffiliates\Interfaces\Model_Interface
1060
+ */
1061
+ public function run() {
1062
+
1063
+ // When module is disabled in the settings, then it shouldn't run the whole class.
1064
+ if ( get_option( 'ta_enable_stats_reporting_module' , 'yes' ) !== 'yes' )
1065
+ return;
1066
+
1067
+ add_filter( 'ta_filter_before_save_click' , array( $this , 'prevent_save_click_if_useragent_is_bot' ) , 10 , 1 );
1068
+ add_action( 'ta_before_link_redirect' , array( $this , 'save_click_data_on_redirect' ) , 10 , 3 );
1069
+ add_action( 'admin_menu' , array( $this , 'add_reports_submenu' ) , 10 );
1070
+ add_action( 'ta_register_reports' , array( $this , 'register_link_performance_report' ) , 10 );
1071
+ add_action( Plugin_Constants::CRON_STATS_TRIMMER , array( $this , 'implement_stats_trimmer' ) );
1072
+ add_action( 'before_delete_post' , array( $this , 'delete_stats_data_on_affiliate_link_deletion' ) , 10 );
1073
+
1074
+ add_filter( 'ta_admin_interfaces' , array( $this , 'register_admin_interfaces' ) );
1075
+ add_filter( 'ta_menu_items' , array( $this , 'register_admin_menu_items' ) );
1076
+ }
1077
+ }
Models/index.php CHANGED
File without changes
css/admin/index.php CHANGED
File without changes
css/admin/ta-affiliate-link-list.css CHANGED
@@ -1,29 +1,29 @@
1
- .ta-display-input-wrap {
2
- position: relative;
3
- }
4
- .ta-display-input-wrap a {
5
- position: absolute;
6
- top: 2px;
7
- right: -16px;
8
- display: none;
9
- width: 15px;
10
- height: 15px;
11
- background: #0273aa;
12
- color: #fff;
13
- border-radius: 100px;
14
- padding: 4px;
15
- text-align: center;
16
- opacity: 0.85;
17
- }
18
- .ta-display-input-wrap a .dashicons {
19
- font-size: 16px;
20
- line-height: 16px;
21
- width: auto;
22
- height: auto;
23
- }
24
- .ta-display-input-wrap:hover a {
25
- display: block;
26
- }
27
- .ta-display-input-wrap:hover a:hover {
28
- opacity: 1.0;
29
- }
1
+ .ta-display-input-wrap {
2
+ position: relative;
3
+ }
4
+ .ta-display-input-wrap a {
5
+ position: absolute;
6
+ top: 2px;
7
+ right: -16px;
8
+ display: none;
9
+ width: 15px;
10
+ height: 15px;
11
+ background: #0273aa;
12
+ color: #fff;
13
+ border-radius: 100px;
14
+ padding: 4px;
15
+ text-align: center;
16
+ opacity: 0.85;
17
+ }
18
+ .ta-display-input-wrap a .dashicons {
19
+ font-size: 16px;
20
+ line-height: 16px;
21
+ width: auto;
22
+ height: auto;
23
+ }
24
+ .ta-display-input-wrap:hover a {
25
+ display: block;
26
+ }
27
+ .ta-display-input-wrap:hover a:hover {
28
+ opacity: 1.0;
29
+ }
css/admin/ta-editor.css CHANGED
@@ -1,47 +1,47 @@
1
- .mce-container div.wp-thirstylink-input {
2
- float: left;
3
- margin: 2px;
4
- max-width: 694px;
5
- position: relative;
6
- }
7
-
8
- .mce-container div.wp-thirstylink-input input {
9
- width: 300px;
10
- padding: 5px;
11
- -webkit-box-sizing: border-box;
12
- -moz-box-sizing: border-box;
13
- box-sizing: border-box
14
- }
15
- .mce-container div.wp-thirstylink-input .affiliate-link-list {
16
- position: absolute;
17
- top: 100%;
18
- left: 0;
19
- width: 100%;
20
- max-height: 150px;
21
- overflow: auto;
22
- border: 1px solid #eee;
23
- background: #fff;
24
- }
25
- .mce-container div.wp-thirstylink-input .affiliate-link-list li {
26
- padding: 5px;
27
- border-bottom: 1px solid #eee;
28
- cursor: pointer;
29
- }
30
- .mce-container div.wp-thirstylink-input .affiliate-link-list li:hover {
31
- background: #eee;
32
- }
33
- .mce-container div.wp-thirstylink-input .affiliate-link-list li * {
34
- font-size: 13px;
35
- }
36
- .mce-container div.wp-thirstylink-input .affiliate-link-list li:last-child {
37
- border-bottom: 0;
38
- }
39
- .shortcode-preview-toolbar div.shortcode-preview {
40
- display:inline-block;
41
- padding: 6px 8px;
42
- color: #3ca99c;
43
- }
44
- .shortcode-preview-toolbar .mce-btn-group {
45
- border: 0 !important;
46
- padding: 0 !important;
47
  }
1
+ .mce-container div.wp-thirstylink-input {
2
+ float: left;
3
+ margin: 2px;
4
+ max-width: 694px;
5
+ position: relative;
6
+ }
7
+
8
+ .mce-container div.wp-thirstylink-input input {
9
+ width: 300px;
10
+ padding: 5px;
11
+ -webkit-box-sizing: border-box;
12
+ -moz-box-sizing: border-box;
13
+ box-sizing: border-box
14
+ }
15
+ .mce-container div.wp-thirstylink-input .affiliate-link-list {
16
+ position: absolute;
17
+ top: 100%;
18
+ left: 0;
19
+ width: 100%;
20
+ max-height: 150px;
21
+ overflow: auto;
22
+ border: 1px solid #eee;
23
+ background: #fff;
24
+ }
25
+ .mce-container div.wp-thirstylink-input .affiliate-link-list li {
26
+ padding: 5px;
27
+ border-bottom: 1px solid #eee;
28
+ cursor: pointer;
29
+ }
30
+ .mce-container div.wp-thirstylink-input .affiliate-link-list li:hover {
31
+ background: #eee;
32
+ }
33
+ .mce-container div.wp-thirstylink-input .affiliate-link-list li * {
34
+ font-size: 13px;
35
+ }
36
+ .mce-container div.wp-thirstylink-input .affiliate-link-list li:last-child {
37
+ border-bottom: 0;
38
+ }
39
+ .shortcode-preview-toolbar div.shortcode-preview {
40
+ display:inline-block;
41
+ padding: 6px 8px;
42
+ color: #3ca99c;
43
+ }
44
+ .shortcode-preview-toolbar .mce-btn-group {
45
+ border: 0 !important;
46
+ padding: 0 !important;
47
  }
css/admin/ta-guided-tour.css CHANGED
@@ -1,11 +1,11 @@
1
- .ta-tour-buttons {
2
- display: block;
3
- text-align: right;
4
- }
5
- .ta-tour-buttons > .button {
6
- margin-left: 10px;
7
- }
8
- .wp-pointer ul {
9
- padding: 0 15px 0 40px;
10
- list-style: disc;
11
- }
1
+ .ta-tour-buttons {
2
+ display: block;
3
+ text-align: right;
4
+ }
5
+ .ta-tour-buttons > .button {
6
+ margin-left: 10px;
7
+ }
8
+ .wp-pointer ul {
9
+ padding: 0 15px 0 40px;
10
+ list-style: disc;
11
+ }
css/admin/ta-reports.css CHANGED
@@ -1,255 +1,255 @@
1
- /* Top Navigation */
2
- .thirsty-nav-tab {
3
- margin: 20px 0 15px;
4
- position: relative;
5
- }
6
- .thirsty-nav-tab:after {
7
- content: '';
8
- display: block;
9
- position: absolute;
10
- z-index: 1;
11
- bottom: -1px;
12
- width: 100%;
13
- height:1px;
14
- border-top: 1px solid #ccc;
15
- }
16
- .thirsty-nav-tab a.tab {
17
- display: inline-block;
18
- position: relative;
19
- z-index: 2;
20
- padding: 8px 10px;
21
- margin-right: 2px;
22
- background: #e5e5e5;
23
- border: 1px solid #ccc;
24
- text-decoration: none;
25
- font-weight: bold;
26
- color: #111;
27
- font-size: 14px;
28
- }
29
- .thirsty-nav-tab a.tab:hover {
30
- background: #fff;
31
- }
32
- .thirsty-nav-tab a.tab-active {
33
- background: #f1f1f1 !important;
34
- border-bottom-color: #f1f1f1;
35
- }
36
- .thirsty-nav-tab a.tab:first-child {
37
- margin-left: 8px;
38
- }
39
-
40
- /* Range navigation */
41
- .link-performance-report {
42
- position: relative;
43
- background:#fff;
44
- min-height: 300px;
45
- border: 1px solid #dedede;
46
- }
47
- .link-performance-report .stats-range {
48
- position: relative;
49
- background: #f7f7f7;
50
- }
51
- .link-performance-report .stats-range:after {
52
- content: '';
53
- display: table;
54
- position: absolute;
55
- z-index: 1;
56
- bottom: -1px;
57
- border-top: 1px solid #ccc;
58
- width: 100%;
59
- height: 1px;
60
- }
61
- .link-performance-report .stats-range ul {
62
- margin: 0;
63
- padding: 0;
64
- }
65
- .link-performance-report .stats-range ul:after {
66
- content: '';
67
- display: block;
68
- clear: both;
69
- }
70
- .link-performance-report .stats-range ul li {
71
- float: left;
72
- position: relative;
73
- margin: 0;
74
- }
75
- .link-performance-report .stats-range ul li a,
76
- .link-performance-report .stats-range ul li > span {
77
- position:relative;
78
- z-index: 2;
79
- display: block;
80
- padding: 15px 10px;
81
- font-weight: bold;
82
- font-size: 14px;
83
- text-decoration: none;
84
- border-right: 1px solid #dedede;
85
- border-bottom: 1px solid transparent;
86
- }
87
- .link-performance-report .stats-range ul li.current a {
88
- background:#fff;
89
- border-bottom-color: #fff;
90
- color: #555 !important;
91
- }
92
- .link-performance-report .stats-range ul li.custom-range {
93
- width: 400px;
94
- }
95
- .link-performance-report .stats-range ul li.custom-range span {
96
- display: inline-block;
97
- float: left;
98
- color: #555;
99
- border-right: 0;
100
- }
101
- .link-performance-report .stats-range ul li.custom-range:after {
102
- content: '';
103
- display: table;
104
- clear: both;
105
- }
106
-
107
- /* Custom range form */
108
- #custom-date-range {
109
- margin-top: 10px;
110
- }
111
- #custom-date-range .range_datepicker {
112
- float: left;
113
- width: 100px;
114
- text-align: center;
115
- background: transparent;
116
- border: 0;
117
- box-shadow: none;
118
- -moz-box-shadow: none;
119
- -webkit-box-shadow: none;
120
- }
121
- #custom-date-range span {
122
- display: inline-block;
123
- float: left;
124
- margin-top: 3px;
125
- }
126
-
127
- /* Chart layout */
128
- .chart-sidebar {
129
- display: block;
130
- width: 225px;
131
- float: left;
132
- }
133
- .report-chart-wrap {
134
- margin: 10px;
135
- }
136
-
137
- /* Chart legend */
138
- ul.chart-legend {
139
- margin: 15px 0 0 0;
140
- border: 1px solid #dadada;
141
- border-right: 0;
142
- }
143
- ul.chart-legend li {
144
- position: relative;
145
- padding: 10px 15px;
146
- margin: 0;
147
- border-right: 5px solid #ccc;
148
- border-bottom: 1px solid #dadada !important;
149
- }
150
- ul.chart-legend li span {
151
- display: block;
152
- color: #aaa;
153
- font-size: 11px;
154
- }
155
- ul.chart-legend li em.count {
156
- position: absolute;
157
- top: 50%;
158
- right: 10px;
159
- margin-top: -12px;
160
- display: inline-block;
161
- padding: 1px 5px;
162
- background: #565656;
163
- color: #fff;
164
- border-radius: 50px;
165
- font-size: 11px;
166
- font-style: normal;
167
- }
168
- ul.chart-legend li:hover {
169
- background: #efefef;
170
- }
171
- ul.chart-legend li:last-child {
172
- border-bottom: 0 !important;
173
- }
174
- .add-legend {
175
- padding: 7px 12px;
176
- border: 1px solid #dadada;
177
- border-top: 0;
178
- }
179
- .add-legend:after {
180
- content: '';
181
- display: table;
182
- clear: both;
183
- }
184
- .add-legend label {
185
- display: block;
186
- margin-bottom: 5px;
187
- }
188
- .add-legend input {
189
- width: 100%;
190
- padding: 5px;
191
- box-sizing: border-box;
192
- }
193
- .add-legend button {
194
- float: right;
195
- }
196
- .add-legend .input-wrap {
197
- position: relative;
198
- margin-bottom: 5px;
199
- }
200
- .add-legend ul.link-search-result {
201
- position: absolute;
202
- z-index: 2;
203
- top: 100%;
204
- left: 0;
205
- width: 99%;
206
- margin: -2px 0 0 1px;
207
- background: #fff;
208
- border: 1px solid #dadada;
209
- }
210
- .add-legend ul.link-search-result li {
211
- margin: 0;
212
- padding: 3px 5px;
213
- border-bottom: 1px solid #dadada;
214
- white-space: nowrap;
215
- overflow: hidden;
216
- }
217
- .add-legend ul.link-search-result li:hover {
218
- background: #efefef;
219
- cursor: pointer;
220
- }
221
-
222
-
223
- /* Chart placeholder */
224
- .report-chart-placeholder {
225
- height: 600px;
226
- margin-left: 235px;
227
- }
228
- .report-chart-placeholder:after {
229
- content: '';
230
- display: table;
231
- clear: both;
232
- }
233
- body .chart-tooltip {
234
- position: absolute;
235
- padding: 3px 5px;
236
- background: rgba( 0, 0, 0, 0.8 );
237
- color: #fff;
238
- font-size: 11px;
239
- border-radius: 5px;
240
- }
241
-
242
- /* overlay */
243
- .link-performance-report .overlay {
244
- display: none;
245
- position: absolute;
246
- top: 0;
247
- left:0;
248
- background-image: url( '../../images/spinner-2x.gif' );
249
- background-repeat: no-repeat;
250
- background-position: center center;
251
- background-color: rgba(255, 255, 255, 0.64);
252
- width: 100%;
253
- height: 1000px;
254
- z-index: 10;
255
- }
1
+ /* Top Navigation */
2
+ .thirsty-nav-tab {
3
+ margin: 20px 0 15px;
4
+ position: relative;
5
+ }
6
+ .thirsty-nav-tab:after {
7
+ content: '';
8
+ display: block;
9
+ position: absolute;
10
+ z-index: 1;
11
+ bottom: -1px;
12
+ width: 100%;
13
+ height:1px;
14
+ border-top: 1px solid #ccc;
15
+ }
16
+ .thirsty-nav-tab a.tab {
17
+ display: inline-block;
18
+ position: relative;
19
+ z-index: 2;
20
+ padding: 8px 10px;
21
+ margin-right: 2px;
22
+ background: #e5e5e5;
23
+ border: 1px solid #ccc;
24
+ text-decoration: none;
25
+ font-weight: bold;
26
+ color: #111;
27
+ font-size: 14px;
28
+ }
29
+ .thirsty-nav-tab a.tab:hover {
30
+ background: #fff;
31
+ }
32
+ .thirsty-nav-tab a.tab-active {
33
+ background: #f1f1f1 !important;
34
+ border-bottom-color: #f1f1f1;
35
+ }
36
+ .thirsty-nav-tab a.tab:first-child {
37
+ margin-left: 8px;
38
+ }
39
+
40
+ /* Range navigation */
41
+ .link-performance-report {
42
+ position: relative;
43
+ background:#fff;
44
+ min-height: 300px;
45
+ border: 1px solid #dedede;
46
+ }
47
+ .link-performance-report .stats-range {
48
+ position: relative;
49
+ background: #f7f7f7;
50
+ }
51
+ .link-performance-report .stats-range:after {
52
+ content: '';
53
+ display: table;
54
+ position: absolute;
55
+ z-index: 1;
56
+ bottom: -1px;
57
+ border-top: 1px solid #ccc;
58
+ width: 100%;
59
+ height: 1px;
60
+ }
61
+ .link-performance-report .stats-range ul {
62
+ margin: 0;
63
+ padding: 0;
64
+ }
65
+ .link-performance-report .stats-range ul:after {
66
+ content: '';
67
+ display: block;
68
+ clear: both;
69
+ }
70
+ .link-performance-report .stats-range ul li {
71
+ float: left;
72
+ position: relative;
73
+ margin: 0;
74
+ }
75
+ .link-performance-report .stats-range ul li a,
76
+ .link-performance-report .stats-range ul li > span {
77
+ position:relative;
78
+ z-index: 2;
79
+ display: block;
80
+ padding: 15px 10px;
81
+ font-weight: bold;
82
+ font-size: 14px;
83
+ text-decoration: none;
84
+ border-right: 1px solid #dedede;
85
+ border-bottom: 1px solid transparent;
86
+ }
87
+ .link-performance-report .stats-range ul li.current a {
88
+ background:#fff;
89
+ border-bottom-color: #fff;
90
+ color: #555 !important;
91
+ }
92
+ .link-performance-report .stats-range ul li.custom-range {
93
+ width: 400px;
94
+ }
95
+ .link-performance-report .stats-range ul li.custom-range span {
96
+ display: inline-block;
97
+ float: left;
98
+ color: #555;
99
+ border-right: 0;
100
+ }
101
+ .link-performance-report .stats-range ul li.custom-range:after {
102
+ content: '';
103
+ display: table;
104
+ clear: both;
105
+ }
106
+
107
+ /* Custom range form */
108
+ #custom-date-range {
109
+ margin-top: 10px;
110
+ }
111
+ #custom-date-range .range_datepicker {
112
+ float: left;
113
+ width: 100px;
114
+ text-align: center;
115
+ background: transparent;
116
+ border: 0;
117
+ box-shadow: none;
118
+ -moz-box-shadow: none;
119
+ -webkit-box-shadow: none;
120
+ }
121
+ #custom-date-range span {
122
+ display: inline-block;
123
+ float: left;
124
+ margin-top: 3px;
125
+ }
126
+
127
+ /* Chart layout */
128
+ .chart-sidebar {
129
+ display: block;
130
+ width: 225px;
131
+ float: left;
132
+ }
133
+ .report-chart-wrap {
134
+ margin: 10px;
135
+ }
136
+
137
+ /* Chart legend */
138
+ ul.chart-legend {
139
+ margin: 15px 0 0 0;
140
+ border: 1px solid #dadada;
141
+ border-right: 0;
142
+ }
143
+ ul.chart-legend li {
144
+ position: relative;
145
+ padding: 10px 15px;
146
+ margin: 0;
147
+ border-right: 5px solid #ccc;
148
+ border-bottom: 1px solid #dadada !important;
149
+ }
150
+ ul.chart-legend li span {
151
+ display: block;
152
+ color: #aaa;
153
+ font-size: 11px;
154
+ }
155
+ ul.chart-legend li em.count {
156
+ position: absolute;
157
+ top: 50%;
158
+ right: 10px;
159
+ margin-top: -12px;
160
+ display: inline-block;
161
+ padding: 1px 5px;
162
+ background: #565656;
163
+ color: #fff;
164
+ border-radius: 50px;
165
+ font-size: 11px;
166
+ font-style: normal;
167
+ }
168
+ ul.chart-legend li:hover {
169
+ background: #efefef;
170
+ }
171
+ ul.chart-legend li:last-child {
172
+ border-bottom: 0 !important;
173
+ }
174
+ .add-legend {
175
+ padding: 7px 12px;
176
+ border: 1px solid #dadada;
177
+ border-top: 0;
178
+ }
179
+ .add-legend:after {
180
+ content: '';
181
+ display: table;
182
+ clear: both;
183
+ }
184
+ .add-legend label {
185
+ display: block;
186
+ margin-bottom: 5px;
187
+ }
188
+ .add-legend input {
189
+ width: 100%;
190
+ padding: 5px;
191
+ box-sizing: border-box;
192
+ }
193
+ .add-legend button {
194
+ float: right;
195
+ }
196
+ .add-legend .input-wrap {
197
+ position: relative;
198
+ margin-bottom: 5px;
199
+ }
200
+ .add-legend ul.link-search-result {
201
+ position: absolute;
202
+ z-index: 2;
203
+ top: 100%;
204
+ left: 0;
205
+ width: 99%;
206
+ margin: -2px 0 0 1px;
207
+ background: #fff;
208
+ border: 1px solid #dadada;
209
+ }
210
+ .add-legend ul.link-search-result li {
211
+ margin: 0;
212
+ padding: 3px 5px;
213
+ border-bottom: 1px solid #dadada;
214
+ white-space: nowrap;
215
+ overflow: hidden;
216
+ }
217
+ .add-legend ul.link-search-result li:hover {
218
+ background: #efefef;
219
+ cursor: pointer;
220
+ }
221
+
222
+
223
+ /* Chart placeholder */
224
+ .report-chart-placeholder {
225
+ height: 600px;
226
+ margin-left: 235px;
227
+ }
228
+ .report-chart-placeholder:after {
229
+ content: '';
230
+ display: table;
231
+ clear: both;
232
+ }
233
+ body .chart-tooltip {
234
+ position: absolute;
235
+ padding: 3px 5px;
236
+ background: rgba( 0, 0, 0, 0.8 );
237
+ color: #fff;
238
+ font-size: 11px;
239
+ border-radius: 5px;
240
+ }
241
+
242
+ /* overlay */
243
+ .link-performance-report .overlay {
244
+ display: none;
245
+ position: absolute;
246
+ top: 0;
247
+ left:0;
248
+ background-image: url( '../../images/spinner-2x.gif' );
249
+ background-repeat: no-repeat;
250
+ background-position: center center;
251
+ background-color: rgba(255, 255, 255, 0.64);
252
+ width: 100%;
253
+ height: 1000px;
254
+ z-index: 10;
255
+ }
css/admin/ta-settings.css CHANGED
@@ -1,113 +1,113 @@
1
- #wpbody-content {
2
- padding-bottom: 10px;
3
- margin-bottom: 45px;
4
- }
5
- #wpbody-content .ta-settings-wrapper {
6
- position: relative;
7
- }
8
- #wpbody-content .ta-settings-wrapper .nav-tab-wrapper {
9
- float: left;
10
- width: 150px;
11
- border: 0;
12
- margin-left: 20px;
13
- }
14
- #wpbody-content .ta-settings-wrapper .nav-tab-wrapper a {
15
- width: 100%;
16
- float: right;
17
- clear: right;
18
- position: relative;
19
- z-index: 3;
20
- }
21
- #wpbody-content .ta-settings-wrapper .nav-tab-wrapper a.nav-tab-active {
22
- border-right: 1px solid #f1f1f1;
23
- }
24
- #wpbody-content .ta-settings-wrapper .nav-tab-wrapper a:last-child {
25
- border-bottom: 1px solid #ccc;
26
- }
27
- #wpbody-content .ta-settings-wrapper .nav-tab-wrapper a.tapro-upgrade.nav-tab {
28
- color: #fff;
29
- background: #338850;
30
- background: -moz-linear-gradient(left, #338850 0%, #2f867e 51%, #2e6e8a 100%);
31
- background: -webkit-linear-gradient(left, #338850 0%,#2f867e 51%,#2e6e8a 100%);
32
- background: linear-gradient(to right, #338850 0%,#2f867e 51%,#2e6e8a 100%);
33
- filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#338850', endColorstr='#2e6e8a',GradientType=1 );
34
- border: 1px solid #276573 !important;
35
- }
36
- #wpbody-content .ta-settings-wrapper > .ta-settings-form {
37
- position: relative;
38
- float:left;
39
- left: -1px;
40
- padding-left: 20px;
41
- width: -moz-calc(100% - 190px);
42
- width: -webkit-calc(100% - 190px);
43
- width: -o-calc(100% - 190px);
44
- width: calc(100% - 190px);
45
- overflow: hidden;
46
- }
47
- #wpbody-content .ta-settings-wrapper > .ta-settings-form:before {
48
- content: '';
49
- position: absolute;
50
- z-index: 1;
51
- top: 0;
52
- left: 0px;
53
- display: block;
54
- width: 1px;
55
- height: 3000px;
56
- border-left:1px solid #ccc;
57
- }
58
- #wpbody-content .ta-settings:after,
59
- #wpbody-content .ta-settings-wrapper:after,
60
- #wpbody-content .ta-settings .nav-tab-wrapper:after {
61
- content: '';
62
- display: table;
63
- clear: both;
64
- }
65
- .ta-settings .form-table td,
66
- .ta-settings .form-table th {
67
- vertical-align: top;
68
- padding: 20px 10px 20px 0;
69
- }
70
- .ta-settings .form-table .submit-wrapper {
71
- padding: 0;
72
- }
73
- .ta-settings .form-table .forminp-radio label {
74
- margin-top: 0 !important;
75
- line-height: 1em;
76
- }
77
- .ta-settings .form-table .forminp-radio ul {
78
- margin: 5px 0 0;
79
- }
80
- .ta-settings .form-table .forminp-checkbox label {
81
- margin-top: 5px !important;
82
- line-height: 1em;
83
- }
84
- .ta-settings .form-table td .description {
85
- margin-top: 0;
86
- }
87
- tr.ta_link_prefix_custom-row {
88
- display: none;
89
- }
90
- tr.ta_social_links-row ul {
91
- margin: 0;
92
- }
93
- tr.ta_social_links-row ul li {
94
- margin-bottom: 15px;
95
- }
96
- tr.ta_social_links-row ul li:nth-child(1) a {
97
- margin-right: 8px;
98
- }
99
- tr.ta_social_links-row ul li:nth-child(2) a {
100
- margin-right: 10px;
101
- }
102
- td.forminp .selectize-input {
103
- padding: 5px 8px;
104
- }
105
- td.forminp.toggle-cat select,
106
- td.forminp.toggle-cat .selectize-input {
107
- width: 120px !important;
108
- display: inline-block;
109
- }
110
- td.forminp.toggle-cat .desc {
111
- display: inline;
112
- margin-left: 5px;
113
- }
1
+ #wpbody-content {
2
+ padding-bottom: 10px;
3
+ margin-bottom: 45px;
4
+ }
5
+ #wpbody-content .ta-settings-wrapper {
6
+ position: relative;
7
+ }
8
+ #wpbody-content .ta-settings-wrapper .nav-tab-wrapper {
9
+ float: left;
10
+ width: 150px;
11
+ border: 0;
12
+ margin-left: 20px;
13
+ }
14
+ #wpbody-content .ta-settings-wrapper .nav-tab-wrapper a {
15
+ width: 100%;
16
+ float: right;
17
+ clear: right;
18
+ position: relative;
19
+ z-index: 3;
20
+ }
21
+ #wpbody-content .ta-settings-wrapper .nav-tab-wrapper a.nav-tab-active {
22
+ border-right: 1px solid #f1f1f1;
23
+ }
24
+ #wpbody-content .ta-settings-wrapper .nav-tab-wrapper a:last-child {
25
+ border-bottom: 1px solid #ccc;
26
+ }
27
+ #wpbody-content .ta-settings-wrapper .nav-tab-wrapper a.tapro-upgrade.nav-tab {
28
+ color: #fff;
29
+ background: #338850;
30
+ background: -moz-linear-gradient(left, #338850 0%, #2f867e 51%, #2e6e8a 100%);
31
+ background: -webkit-linear-gradient(left, #338850 0%,#2f867e 51%,#2e6e8a 100%);
32
+ background: linear-gradient(to right, #338850 0%,#2f867e 51%,#2e6e8a 100%);
33
+ filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#338850', endColorstr='#2e6e8a',GradientType=1 );
34
+ border: 1px solid #276573 !important;
35
+ }
36
+ #wpbody-content .ta-settings-wrapper > .ta-settings-form {
37
+ position: relative;
38
+ float:left;
39
+ left: -1px;
40
+ padding-left: 20px;
41
+ width: -moz-calc(100% - 190px);
42
+ width: -webkit-calc(100% - 190px);
43
+ width: -o-calc(100% - 190px);
44
+ width: calc(100% - 190px);
45
+ overflow: hidden;
46
+ }
47
+ #wpbody-content .ta-settings-wrapper > .ta-settings-form:before {
48
+ content: '';
49
+ position: absolute;
50
+ z-index: 1;
51
+ top: 0;
52
+ left: 0px;
53
+ display: block;
54
+ width: 1px;
55
+ height: 3000px;
56
+ border-left:1px solid #ccc;
57
+ }
58
+ #wpbody-content .ta-settings:after,
59
+ #wpbody-content .ta-settings-wrapper:after,
60
+ #wpbody-content .ta-settings .nav-tab-wrapper:after {
61
+ content: '';
62
+ display: table;
63
+ clear: both;
64
+ }
65
+ .ta-settings .form-table td,
66
+ .ta-settings .form-table th {
67
+ vertical-align: top;
68
+ padding: 20px 10px 20px 0;
69
+ }
70
+ .ta-settings .form-table .submit-wrapper {
71
+ padding: 0;
72
+ }
73
+ .ta-settings .form-table .forminp-radio label {
74
+ margin-top: 0 !important;
75
+ line-height: 1em;
76
+ }
77
+ .ta-settings .form-table .forminp-radio ul {
78
+ margin: 5px 0 0;
79
+ }
80
+ .ta-settings .form-table .forminp-checkbox label {
81
+ margin-top: 5px !important;
82
+ line-height: 1em;
83
+ }
84
+ .ta-settings .form-table td .description {
85
+ margin-top: 0;
86
+ }
87
+ tr.ta_link_prefix_custom-row {
88
+ display: none;
89
+ }
90
+ tr.ta_social_links-row ul {
91
+ margin: 0;
92
+ }
93
+ tr.ta_social_links-row ul li {
94
+ margin-bottom: 15px;
95
+ }
96
+ tr.ta_social_links-row ul li:nth-child(1) a {
97
+ margin-right: 8px;
98
+ }
99
+ tr.ta_social_links-row ul li:nth-child(2) a {
100
+ margin-right: 10px;
101
+ }
102
+ td.forminp .selectize-input {
103
+ padding: 5px 8px;
104
+ }
105
+ td.forminp.toggle-cat select,
106
+ td.forminp.toggle-cat .selectize-input {
107
+ width: 120px !important;
108
+ display: inline-block;
109
+ }
110
+ td.forminp.toggle-cat .desc {
111
+ display: inline;
112
+ margin-left: 5px;
113
+ }
css/admin/tinymce/editor.css CHANGED
@@ -1,9 +1,9 @@
1
- span.ta-editor-shortcode {
2
- border-bottom: 1px dashed #3faa67;
3
- cursor: pointer;
4
- }
5
- span.ta-editor-shortcode.shortcode-selected {
6
- background: #e5f5ed;
7
- box-shadow: 0 0 2px 3px #e5f5ed;
8
- border-radius: 5px
9
  }
1
+ span.ta-editor-shortcode {
2
+ border-bottom: 1px dashed #3faa67;
3
+ cursor: pointer;
4
+ }
5
+ span.ta-editor-shortcode.shortcode-selected {
6
+ background: #e5f5ed;
7
+ box-shadow: 0 0 2px 3px #e5f5ed;
8
+ border-radius: 5px
9
  }
css/index.php CHANGED
File without changes
css/lib/jquery-tiptip/jquery-tiptip.css CHANGED
@@ -1,109 +1,109 @@
1
- /* TipTip CSS - Version 1.2 */
2
-
3
- #tiptip_holder {
4
- display: none;
5
- position: absolute;
6
- top: 0;
7
- left: 0;
8
- z-index: 99999;
9
- }
10
-
11
- #tiptip_holder.tip_top {
12
- padding-bottom: 5px;
13
- }
14
-
15
- #tiptip_holder.tip_bottom {
16
- padding-top: 5px;
17
- }
18
-
19
- #tiptip_holder.tip_right {
20
- padding-left: 5px;
21
- }
22
-
23
- #tiptip_holder.tip_left {
24
- padding-right: 5px;
25
- }
26
-
27
- #tiptip_content {
28
- font-size: 11px;
29
- color: #fff;
30
- text-shadow: 0 0 2px #000;
31
- padding: 4px 8px;
32
- border: 1px solid rgba(255,255,255,0.25);
33
- background-color: rgb(25,25,25);
34
- background-color: rgba(25,25,25,0.92);
35
- border-radius: 3px;
36
- -webkit-border-radius: 3px;
37
- -moz-border-radius: 3px;
38
- }
39
-
40
- #tiptip_arrow, #tiptip_arrow_inner {
41
- position: absolute;
42
- border-color: transparent;
43
- border-style: solid;
44
- border-width: 6px;
45
- height: 0;
46
- width: 0;
47
- }
48
-
49
- #tiptip_holder.tip_top #tiptip_arrow {
50
- border-top-color: #fff;
51
- border-top-color: rgba(255,255,255,0.35);
52
- }
53
-
54
- #tiptip_holder.tip_bottom #tiptip_arrow {
55
- border-bottom-color: #fff;
56
- border-bottom-color: rgba(255,255,255,0.35);
57
- }
58
-
59
- #tiptip_holder.tip_right #tiptip_arrow {
60
- border-right-color: #fff;
61
- border-right-color: rgba(255,255,255,0.35);
62
- }
63
-
64
- #tiptip_holder.tip_left #tiptip_arrow {
65
- border-left-color: #fff;
66
- border-left-color: rgba(255,255,255,0.35);
67
- }
68
-
69
- #tiptip_holder.tip_top #tiptip_arrow_inner {
70
- margin-top: -7px;
71
- margin-left: -6px;
72
- border-top-color: rgb(25,25,25);
73
- border-top-color: rgba(25,25,25,0.92);
74
- }
75
-
76
- #tiptip_holder.tip_bottom #tiptip_arrow_inner {
77
- margin-top: -5px;
78
- margin-left: -6px;
79
- border-bottom-color: rgb(25,25,25);
80
- border-bottom-color: rgba(25,25,25,0.92);
81
- }
82
-
83
- #tiptip_holder.tip_right #tiptip_arrow_inner {
84
- margin-top: -6px;
85
- margin-left: -5px;
86
- border-right-color: rgb(25,25,25);
87
- border-right-color: rgba(25,25,25,0.92);
88
- }
89
-
90
- #tiptip_holder.tip_left #tiptip_arrow_inner {
91
- margin-top: -6px;
92
- margin-left: -7px;
93
- border-left-color: rgb(25,25,25);
94
- border-left-color: rgba(25,25,25,0.92);
95
- }
96
-
97
- /* Webkit Hacks */
98
- @media screen and (-webkit-min-device-pixel-ratio:0) {
99
- #tiptip_content {
100
- padding: 4px 8px 5px 8px;
101
- background-color: rgba(45,45,45,0.88);
102
- }
103
- #tiptip_holder.tip_bottom #tiptip_arrow_inner {
104
- border-bottom-color: rgba(45,45,45,0.88);
105
- }
106
- #tiptip_holder.tip_top #tiptip_arrow_inner {
107
- border-top-color: rgba(20,20,20,0.92);
108
- }
109
- }
1
+ /* TipTip CSS - Version 1.2 */
2
+
3
+ #tiptip_holder {
4
+ display: none;
5
+ position: absolute;
6
+ top: 0;
7
+ left: 0;
8
+ z-index: 99999;
9
+ }
10
+
11
+ #tiptip_holder.tip_top {
12
+ padding-bottom: 5px;
13
+ }
14
+
15
+ #tiptip_holder.tip_bottom {
16
+ padding-top: 5px;
17
+ }
18
+
19
+ #tiptip_holder.tip_right {
20
+ padding-left: 5px;
21
+ }
22
+
23
+ #tiptip_holder.tip_left {
24
+ padding-right: 5px;
25
+ }
26
+
27
+ #tiptip_content {
28
+ font-size: 11px;
29
+ color: #fff;
30
+ text-shadow: 0 0 2px #000;
31
+ padding: 4px 8px;
32
+ border: 1px solid rgba(255,255,255,0.25);
33
+ background-color: rgb(25,25,25);
34
+ background-color: rgba(25,25,25,0.92);
35
+ border-radius: 3px;
36
+ -webkit-border-radius: 3px;
37
+ -moz-border-radius: 3px;
38
+ }
39
+
40
+ #tiptip_arrow, #tiptip_arrow_inner {
41
+ position: absolute;
42
+ border-color: transparent;
43
+ border-style: solid;
44
+ border-width: 6px;
45
+ height: 0;
46
+ width: 0;
47
+ }
48
+
49
+ #tiptip_holder.tip_top #tiptip_arrow {
50
+ border-top-color: #fff;
51
+ border-top-color: rgba(255,255,255,0.35);
52
+ }
53
+
54
+ #tiptip_holder.tip_bottom #tiptip_arrow {
55
+ border-bottom-color: #fff;
56
+ border-bottom-color: rgba(255,255,255,0.35);
57
+ }
58
+
59
+ #tiptip_holder.tip_right #tiptip_arrow {
60
+ border-right-color: #fff;
61
+ border-right-color: rgba(255,255,255,0.35);
62
+ }
63
+
64
+ #tiptip_holder.tip_left #tiptip_arrow {
65
+ border-left-color: #fff;
66
+ border-left-color: rgba(255,255,255,0.35);
67
+ }
68
+
69
+ #tiptip_holder.tip_top #tiptip_arrow_inner {
70
+ margin-top: -7px;
71
+ margin-left: -6px;
72
+ border-top-color: rgb(25,25,25);
73
+ border-top-color: rgba(25,25,25,0.92);
74
+ }
75
+
76
+ #tiptip_holder.tip_bottom #tiptip_arrow_inner {
77
+ margin-top: -5px;
78
+ margin-left: -6px;
79
+ border-bottom-color: rgb(25,25,25);
80
+ border-bottom-color: rgba(25,25,25,0.92);
81
+ }
82
+
83
+ #tiptip_holder.tip_right #tiptip_arrow_inner {
84
+ margin-top: -6px;
85
+ margin-left: -5px;
86
+ border-right-color: rgb(25,25,25);
87
+ border-right-color: rgba(25,25,25,0.92);
88
+ }
89
+
90
+ #tiptip_holder.tip_left #tiptip_arrow_inner {
91
+ margin-top: -6px;
92
+ margin-left: -7px;
93
+ border-left-color: rgb(25,25,25);
94
+ border-left-color: rgba(25,25,25,0.92);
95
+ }
96
+
97
+ /* Webkit Hacks */
98
+ @media screen and (-webkit-min-device-pixel-ratio:0) {
99
+ #tiptip_content {
100
+ padding: 4px 8px 5px 8px;
101
+ background-color: rgba(45,45,45,0.88);
102
+ }
103
+ #tiptip_holder.tip_bottom #tiptip_arrow_inner {
104
+ border-bottom-color: rgba(45,45,45,0.88);
105
+ }
106
+ #tiptip_holder.tip_top #tiptip_arrow_inner {
107
+ border-top-color: rgba(20,20,20,0.92);
108
+ }
109
+ }
css/lib/select2/select2.css CHANGED
@@ -1,484 +1,484 @@
1
- .select2-container {
2
- box-sizing: border-box;
3
- display: inline-block;
4
- margin: 0;
5
- position: relative;
6
- vertical-align: middle; }
7
- .select2-container .select2-selection--single {
8
- box-sizing: border-box;
9
- cursor: pointer;
10
- display: block;
11
- height: 28px;
12
- user-select: none;
13
- -webkit-user-select: none; }
14
- .select2-container .select2-selection--single .select2-selection__rendered {
15
- display: block;
16
- padding-left: 8px;
17
- padding-right: 20px;
18
- overflow: hidden;
19
- text-overflow: ellipsis;
20
- white-space: nowrap; }
21
- .select2-container .select2-selection--single .select2-selection__clear {
22
- position: relative; }
23
- .select2-container[dir="rtl"] .select2-selection--single .select2-selection__rendered {
24
- padding-right: 8px;
25
- padding-left: 20px; }
26
- .select2-container .select2-selection--multiple {
27
- box-sizing: border-box;
28
- cursor: pointer;
29
- display: block;
30
- min-height: 32px;
31
- user-select: none;
32
- -webkit-user-select: none; }
33
- .select2-container .select2-selection--multiple .select2-selection__rendered {
34
- display: inline-block;
35
- overflow: hidden;
36
- padding-left: 8px;
37
- text-overflow: ellipsis;
38
- white-space: nowrap; }
39
- .select2-container .select2-search--inline {
40
- float: left; }
41
- .select2-container .select2-search--inline .select2-search__field {
42
- box-sizing: border-box;
43
- border: none;
44
- font-size: 100%;
45
- margin-top: 5px;
46
- padding: 0; }
47
- .select2-container .select2-search--inline .select2-search__field::-webkit-search-cancel-button {
48
- -webkit-appearance: none; }
49
-
50
- .select2-dropdown {
51
- background-color: white;
52
- border: 1px solid #aaa;
53
- border-radius: 4px;
54
- box-sizing: border-box;
55
- display: block;
56
- position: absolute;
57
- left: -100000px;
58
- width: 100%;
59
- z-index: 1051; }
60
-
61
- .select2-results {
62
- display: block; }
63
-
64
- .select2-results__options {
65
- list-style: none;
66
- margin: 0;
67
- padding: 0; }
68
-
69
- .select2-results__option {
70
- padding: 6px;
71
- user-select: none;
72
- -webkit-user-select: none; }
73
- .select2-results__option[aria-selected] {
74
- cursor: pointer; }
75
-
76
- .select2-container--open .select2-dropdown {
77
- left: 0; }
78
-
79
- .select2-container--open .select2-dropdown--above {
80
- border-bottom: none;
81
- border-bottom-left-radius: 0;
82
- border-bottom-right-radius: 0; }
83
-
84
- .select2-container--open .select2-dropdown--below {
85
- border-top: none;
86
- border-top-left-radius: 0;
87
- border-top-right-radius: 0; }
88
-
89
- .select2-search--dropdown {
90
- display: block;
91
- padding: 4px; }
92
- .select2-search--dropdown .select2-search__field {
93
- padding: 4px;
94
- width: 100%;
95
- box-sizing: border-box; }
96
- .select2-search--dropdown .select2-search__field::-webkit-search-cancel-button {
97
- -webkit-appearance: none; }
98
- .select2-search--dropdown.select2-search--hide {
99
- display: none; }
100
-
101
- .select2-close-mask {
102
- border: 0;
103
- margin: 0;
104
- padding: 0;
105
- display: block;
106
- position: fixed;
107
- left: 0;
108
- top: 0;
109
- min-height: 100%;
110
- min-width: 100%;
111
- height: auto;
112
- width: auto;
113
- opacity: 0;
114
- z-index: 99;
115
- background-color: #fff;
116
- filter: alpha(opacity=0); }
117
-
118
- .select2-hidden-accessible {
119
- border: 0 !important;
120
- clip: rect(0 0 0 0) !important;
121
- height: 1px !important;
122
- margin: -1px !important;
123
- overflow: hidden !important;
124
- padding: 0 !important;
125
- position: absolute !important;
126
- width: 1px !important; }
127
-
128
- .select2-container--default .select2-selection--single {
129
- background-color: #fff;
130
- border: 1px solid #aaa;
131
- border-radius: 4px; }
132
- .select2-container--default .select2-selection--single .select2-selection__rendered {
133
- color: #444;
134
- line-height: 28px; }
135
- .select2-container--default .select2-selection--single .select2-selection__clear {
136
- cursor: pointer;
137
- float: right;
138
- font-weight: bold; }
139
- .select2-container--default .select2-selection--single .select2-selection__placeholder {
140
- color: #999; }
141
- .select2-container--default .select2-selection--single .select2-selection__arrow {
142
- height: 26px;
143
- position: absolute;
144
- top: 1px;
145
- right: 1px;
146
- width: 20px; }
147
- .select2-container--default .select2-selection--single .select2-selection__arrow b {
148
- border-color: #888 transparent transparent transparent;
149
- border-style: solid;
150
- border-width: 5px 4px 0 4px;
151
- height: 0;
152
- left: 50%;
153
- margin-left: -4px;
154
- margin-top: -2px;
155
- position: absolute;
156
- top: 50%;
157
- width: 0; }
158
-
159
- .select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__clear {
160
- float: left; }
161
-
162
- .select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__arrow {
163
- left: 1px;
164
- right: auto; }
165
-
166
- .select2-container--default.select2-container--disabled .select2-selection--single {
167
- background-color: #eee;
168
- cursor: default; }
169
- .select2-container--default.select2-container--disabled .select2-selection--single .select2-selection__clear {
170
- display: none; }
171
-
172
- .select2-container--default.select2-container--open .select2-selection--single .select2-selection__arrow b {
173
- border-color: transparent transparent #888 transparent;
174
- border-width: 0 4px 5px 4px; }
175
-
176
- .select2-container--default .select2-selection--multiple {
177
- background-color: white;
178
- border: 1px solid #aaa;
179
- border-radius: 4px;
180
- cursor: text; }
181
- .select2-container--default .select2-selection--multiple .select2-selection__rendered {
182
- box-sizing: border-box;
183
- list-style: none;
184
- margin: 0;
185
- padding: 0 5px;
186
- width: 100%; }
187
- .select2-container--default .select2-selection--multiple .select2-selection__rendered li {
188
- list-style: none; }
189
- .select2-container--default .select2-selection--multiple .select2-selection__placeholder {
190
- color: #999;
191
- margin-top: 5px;
192
- float: left; }
193
- .select2-container--default .select2-selection--multiple .select2-selection__clear {
194
- cursor: pointer;
195
- float: right;
196
- font-weight: bold;
197
- margin-top: 5px;
198
- margin-right: 10px; }
199
- .select2-container--default .select2-selection--multiple .select2-selection__choice {
200
- background-color: #e4e4e4;
201
- border: 1px solid #aaa;
202
- border-radius: 4px;
203
- cursor: default;
204
- float: left;
205
- margin-right: 5px;
206
- margin-top: 5px;
207
- padding: 0 5px; }
208
- .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {
209
- color: #999;
210
- cursor: pointer;
211
- display: inline-block;
212
- font-weight: bold;
213
- margin-right: 2px; }
214
- .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {
215
- color: #333; }
216
-
217
- .select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice, .select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__placeholder, .select2-container--default[dir="rtl"] .select2-selection--multiple .select2-search--inline {
218
- float: right; }
219
-
220
- .select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice {
221
- margin-left: 5px;
222
- margin-right: auto; }
223
-
224
- .select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove {
225
- margin-left: 2px;
226
- margin-right: auto; }
227
-
228
- .select2-container--default.select2-container--focus .select2-selection--multiple {
229
- border: solid black 1px;
230
- outline: 0; }
231
-
232
- .select2-container--default.select2-container--disabled .select2-selection--multiple {
233
- background-color: #eee;
234
- cursor: default; }
235
-
236
- .select2-container--default.select2-container--disabled .select2-selection__choice__remove {
237
- display: none; }
238
-
239
- .select2-container--default.select2-container--open.select2-container--above .select2-selection--single, .select2-container--default.select2-container--open.select2-container--above .select2-selection--multiple {
240
- border-top-left-radius: 0;
241
- border-top-right-radius: 0; }
242
-
243
- .select2-container--default.select2-container--open.select2-container--below .select2-selection--single, .select2-container--default.select2-container--open.select2-container--below .select2-selection--multiple {
244
- border-bottom-left-radius: 0;
245
- border-bottom-right-radius: 0; }
246
-
247
- .select2-container--default .select2-search--dropdown .select2-search__field {
248
- border: 1px solid #aaa; }
249
-
250
- .select2-container--default .select2-search--inline .select2-search__field {
251
- background: transparent;
252
- border: none;
253
- outline: 0;
254
- box-shadow: none;
255
- -webkit-appearance: textfield; }
256
-
257
- .select2-container--default .select2-results > .select2-results__options {
258
- max-height: 200px;
259
- overflow-y: auto; }
260
-
261
- .select2-container--default .select2-results__option[role=group] {
262
- padding: 0; }
263
-
264
- .select2-container--default .select2-results__option[aria-disabled=true] {
265
- color: #999; }
266
-
267
- .select2-container--default .select2-results__option[aria-selected=true] {
268
- background-color: #ddd; }
269
-
270
- .select2-container--default .select2-results__option .select2-results__option {
271
- padding-left: 1em; }
272
- .select2-container--default .select2-results__option .select2-results__option .select2-results__group {
273
- padding-left: 0; }
274
- .select2-container--default .select2-results__option .select2-results__option .select2-results__option {
275
- margin-left: -1em;
276
- padding-left: 2em; }
277
- .select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
278
- margin-left: -2em;
279
- padding-left: 3em; }
280
- .select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
281
- margin-left: -3em;
282
- padding-left: 4em; }
283
- .select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
284
- margin-left: -4em;
285
- padding-left: 5em; }
286
- .select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
287
- margin-left: -5em;
288
- padding-left: 6em; }
289
-
290
- .select2-container--default .select2-results__option--highlighted[aria-selected] {
291
- background-color: #5897fb;
292
- color: white; }
293
-
294
- .select2-container--default .select2-results__group {
295
- cursor: default;
296
- display: block;
297
- padding: 6px; }
298
-
299
- .select2-container--classic .select2-selection--single {
300
- background-color: #f7f7f7;
301
- border: 1px solid #aaa;
302
- border-radius: 4px;
303
- outline: 0;
304
- background-image: -webkit-linear-gradient(top, white 50%, #eeeeee 100%);
305
- background-image: -o-linear-gradient(top, white 50%, #eeeeee 100%);
306
- background-image: linear-gradient(to bottom, white 50%, #eeeeee 100%);
307
- background-repeat: repeat-x;
308
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFFFF', endColorstr='#FFEEEEEE', GradientType=0); }
309
- .select2-container--classic .select2-selection--single:focus {
310
- border: 1px solid #5897fb; }
311
- .select2-container--classic .select2-selection--single .select2-selection__rendered {
312
- color: #444;
313
- line-height: 28px; }
314
- .select2-container--classic .select2-selection--single .select2-selection__clear {
315
- cursor: pointer;
316
- float: right;
317
- font-weight: bold;
318
- margin-right: 10px; }
319
- .select2-container--classic .select2-selection--single .select2-selection__placeholder {
320
- color: #999; }
321
- .select2-container--classic .select2-selection--single .select2-selection__arrow {
322
- background-color: #ddd;
323
- border: none;
324
- border-left: 1px solid #aaa;
325
- border-top-right-radius: 4px;
326
- border-bottom-right-radius: 4px;
327
- height: 26px;
328
- position: absolute;
329
- top: 1px;
330
- right: 1px;
331
- width: 20px;
332
- background-image: -webkit-linear-gradient(top, #eeeeee 50%, #cccccc 100%);
333
- background-image: -o-linear-gradient(top, #eeeeee 50%, #cccccc 100%);
334
- background-image: linear-gradient(to bottom, #eeeeee 50%, #cccccc 100%);
335
- background-repeat: repeat-x;
336
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFEEEEEE', endColorstr='#FFCCCCCC', GradientType=0); }
337
- .select2-container--classic .select2-selection--single .select2-selection__arrow b {
338
- border-color: #888 transparent transparent transparent;
339
- border-style: solid;
340
- border-width: 5px 4px 0 4px;
341
- height: 0;
342
- left: 50%;
343
- margin-left: -4px;
344
- margin-top: -2px;
345
- position: absolute;
346
- top: 50%;
347
- width: 0; }
348
-
349
- .select2-container--classic[dir="rtl"] .select2-selection--single .select2-selection__clear {
350
- float: left; }
351
-
352
- .select2-container--classic[dir="rtl"] .select2-selection--single .select2-selection__arrow {
353
- border: none;
354
- border-right: 1px solid #aaa;
355
- border-radius: 0;
356
- border-top-left-radius: 4px;
357
- border-bottom-left-radius: 4px;
358
- left: 1px;
359
- right: auto; }
360
-
361
- .select2-container--classic.select2-container--open .select2-selection--single {
362
- border: 1px solid #5897fb; }
363
- .select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow {
364
- background: transparent;
365
- border: none; }
366
- .select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow b {
367
- border-color: transparent transparent #888 transparent;
368
- border-width: 0 4px 5px 4px; }
369
-
370
- .select2-container--classic.select2-container--open.select2-container--above .select2-selection--single {
371
- border-top: none;
372
- border-top-left-radius: 0;
373
- border-top-right-radius: 0;
374
- background-image: -webkit-linear-gradient(top, white 0%, #eeeeee 50%);
375
- background-image: -o-linear-gradient(top, white 0%, #eeeeee 50%);
376
- background-image: linear-gradient(to bottom, white 0%, #eeeeee 50%);
377
- background-repeat: repeat-x;
378
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFFFF', endColorstr='#FFEEEEEE', GradientType=0); }
379
-
380
- .select2-container--classic.select2-container--open.select2-container--below .select2-selection--single {
381
- border-bottom: none;
382
- border-bottom-left-radius: 0;
383
- border-bottom-right-radius: 0;
384
- background-image: -webkit-linear-gradient(top, #eeeeee 50%, white 100%);
385
- background-image: -o-linear-gradient(top, #eeeeee 50%, white 100%);
386
- background-image: linear-gradient(to bottom, #eeeeee 50%, white 100%);
387
- background-repeat: repeat-x;
388
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFEEEEEE', endColorstr='#FFFFFFFF', GradientType=0); }
389
-
390
- .select2-container--classic .select2-selection--multiple {
391
- background-color: white;
392
- border: 1px solid #aaa;
393
- border-radius: 4px;
394
- cursor: text;
395
- outline: 0; }
396
- .select2-container--classic .select2-selection--multiple:focus {
397
- border: 1px solid #5897fb; }
398
- .select2-container--classic .select2-selection--multiple .select2-selection__rendered {
399
- list-style: none;
400
- margin: 0;
401
- padding: 0 5px; }
402
- .select2-container--classic .select2-selection--multiple .select2-selection__clear {
403
- display: none; }
404
- .select2-container--classic .select2-selection--multiple .select2-selection__choice {
405
- background-color: #e4e4e4;
406
- border: 1px solid #aaa;
407
- border-radius: 4px;
408
- cursor: default;
409
- float: left;
410
- margin-right: 5px;
411
- margin-top: 5px;
412
- padding: 0 5px; }
413
- .select2-container--classic .select2-selection--multiple .select2-selection__choice__remove {
414
- color: #888;
415
- cursor: pointer;
416
- display: inline-block;
417
- font-weight: bold;
418
- margin-right: 2px; }
419
- .select2-container--classic .select2-selection--multiple .select2-selection__choice__remove:hover {
420
- color: #555; }
421
-
422
- .select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice {
423
- float: right; }
424
-
425
- .select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice {
426
- margin-left: 5px;
427
- margin-right: auto; }
428
-
429
- .select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove {
430
- margin-left: 2px;
431
- margin-right: auto; }
432
-
433
- .select2-container--classic.select2-container--open .select2-selection--multiple {
434
- border: 1px solid #5897fb; }
435
-
436
- .select2-container--classic.select2-container--open.select2-container--above .select2-selection--multiple {
437
- border-top: none;
438
- border-top-left-radius: 0;
439
- border-top-right-radius: 0; }
440
-
441
- .select2-container--classic.select2-container--open.select2-container--below .select2-selection--multiple {
442
- border-bottom: none;
443
- border-bottom-left-radius: 0;
444
- border-bottom-right-radius: 0; }
445
-
446
- .select2-container--classic .select2-search--dropdown .select2-search__field {
447
- border: 1px solid #aaa;
448
- outline: 0; }
449
-
450
- .select2-container--classic .select2-search--inline .select2-search__field {
451
- outline: 0;
452
- box-shadow: none; }
453
-
454
- .select2-container--classic .select2-dropdown {
455
- background-color: white;
456
- border: 1px solid transparent; }
457
-
458
- .select2-container--classic .select2-dropdown--above {
459
- border-bottom: none; }
460
-
461
- .select2-container--classic .select2-dropdown--below {
462
- border-top: none; }
463
-
464
- .select2-container--classic .select2-results > .select2-results__options {
465
- max-height: 200px;
466
- overflow-y: auto; }
467
-
468
- .select2-container--classic .select2-results__option[role=group] {
469
- padding: 0; }
470
-
471
- .select2-container--classic .select2-results__option[aria-disabled=true] {
472
- color: grey; }
473
-
474
- .select2-container--classic .select2-results__option--highlighted[aria-selected] {
475
- background-color: #3875d7;
476
- color: white; }
477
-
478
- .select2-container--classic .select2-results__group {
479
- cursor: default;
480
- display: block;
481
- padding: 6px; }
482
-
483
- .select2-container--classic.select2-container--open .select2-dropdown {
484
- border-color: #5897fb; }
1
+ .select2-container {
2
+ box-sizing: border-box;
3
+ display: inline-block;
4
+ margin: 0;
5
+ position: relative;
6
+ vertical-align: middle; }
7
+ .select2-container .select2-selection--single {
8
+ box-sizing: border-box;
9
+ cursor: pointer;
10
+ display: block;
11
+ height: 28px;
12
+ user-select: none;
13
+ -webkit-user-select: none; }
14
+ .select2-container .select2-selection--single .select2-selection__rendered {
15
+ display: block;
16
+ padding-left: 8px;
17
+ padding-right: 20px;
18
+ overflow: hidden;
19
+ text-overflow: ellipsis;
20
+ white-space: nowrap; }
21
+ .select2-container .select2-selection--single .select2-selection__clear {
22
+ position: relative; }
23
+ .select2-container[dir="rtl"] .select2-selection--single .select2-selection__rendered {
24
+ padding-right: 8px;
25
+ padding-left: 20px; }
26
+ .select2-container .select2-selection--multiple {
27
+ box-sizing: border-box;
28
+ cursor: pointer;
29
+ display: block;
30
+ min-height: 32px;
31
+ user-select: none;
32
+ -webkit-user-select: none; }
33
+ .select2-container .select2-selection--multiple .select2-selection__rendered {
34
+ display: inline-block;
35
+ overflow: hidden;
36
+ padding-left: 8px;
37
+ text-overflow: ellipsis;
38
+ white-space: nowrap; }
39
+ .select2-container .select2-search--inline {
40
+ float: left; }
41
+ .select2-container .select2-search--inline .select2-search__field {
42
+ box-sizing: border-box;
43
+ border: none;
44
+ font-size: 100%;
45
+ margin-top: 5px;
46
+ padding: 0; }
47
+ .select2-container .select2-search--inline .select2-search__field::-webkit-search-cancel-button {
48
+ -webkit-appearance: none; }
49
+
50
+ .select2-dropdown {
51
+ background-color: white;
52
+ border: 1px solid #aaa;
53
+ border-radius: 4px;
54
+ box-sizing: border-box;
55
+ display: block;
56
+ position: absolute;
57
+ left: -100000px;
58
+ width: 100%;
59
+ z-index: 1051; }
60
+
61
+ .select2-results {
62
+ display: block; }
63
+
64
+ .select2-results__options {
65
+ list-style: none;
66
+ margin: 0;
67
+ padding: 0; }
68
+
69
+ .select2-results__option {
70
+ padding: 6px;
71
+ user-select: none;
72
+ -webkit-user-select: none; }
73
+ .select2-results__option[aria-selected] {
74
+ cursor: pointer; }
75
+
76
+ .select2-container--open .select2-dropdown {
77
+ left: 0; }
78
+
79
+ .select2-container--open .select2-dropdown--above {
80
+ border-bottom: none;
81
+ border-bottom-left-radius: 0;
82
+ border-bottom-right-radius: 0; }
83
+
84
+ .select2-container--open .select2-dropdown--below {
85
+ border-top: none;
86
+ border-top-left-radius: 0;
87
+ border-top-right-radius: 0; }
88
+
89
+ .select2-search--dropdown {
90
+ display: block;
91
+ padding: 4px; }
92
+ .select2-search--dropdown .select2-search__field {
93
+ padding: 4px;
94
+ width: 100%;
95
+ box-sizing: border-box; }
96
+ .select2-search--dropdown .select2-search__field::-webkit-search-cancel-button {
97
+ -webkit-appearance: none; }
98
+ .select2-search--dropdown.select2-search--hide {
99
+ display: none; }
100
+
101
+ .select2-close-mask {
102
+ border: 0;
103
+ margin: 0;
104
+ padding: 0;
105
+ display: block;
106
+ position: fixed;
107
+ left: 0;
108
+ top: 0;
109
+ min-height: 100%;
110
+ min-width: 100%;
111
+ height: auto;
112
+ width: auto;
113
+ opacity: 0;
114
+ z-index: 99;
115
+ background-color: #fff;
116
+ filter: alpha(opacity=0); }
117
+
118
+ .select2-hidden-accessible {
119
+ border: 0 !important;
120
+ clip: rect(0 0 0 0) !important;
121
+ height: 1px !important;
122
+ margin: -1px !important;
123
+ overflow: hidden !important;
124
+ padding: 0 !important;
125
+ position: absolute !important;
126
+ width: 1px !important; }
127
+
128
+ .select2-container--default .select2-selection--single {
129
+ background-color: #fff;
130
+ border: 1px solid #aaa;
131
+ border-radius: 4px; }
132
+ .select2-container--default .select2-selection--single .select2-selection__rendered {
133
+ color: #444;
134
+ line-height: 28px; }
135
+ .select2-container--default .select2-selection--single .select2-selection__clear {
136
+ cursor: pointer;
137
+ float: right;
138
+ font-weight: bold; }
139
+ .select2-container--default .select2-selection--single .select2-selection__placeholder {
140
+ color: #999; }
141
+ .select2-container--default .select2-selection--single .select2-selection__arrow {
142
+ height: 26px;
143
+ position: absolute;
144
+ top: 1px;
145
+ right: 1px;
146
+ width: 20px; }
147
+ .select2-container--default .select2-selection--single .select2-selection__arrow b {
148
+ border-color: #888 transparent transparent transparent;
149
+ border-style: solid;
150
+ border-width: 5px 4px 0 4px;
151
+ height: 0;
152
+ left: 50%;
153
+ margin-left: -4px;
154
+ margin-top: -2px;
155
+ position: absolute;
156
+ top: 50%;
157
+ width: 0; }
158
+
159
+ .select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__clear {
160
+ float: left; }
161
+
162
+ .select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__arrow {
163
+ left: 1px;
164
+ right: auto; }
165
+
166
+ .select2-container--default.select2-container--disabled .select2-selection--single {
167
+ background-color: #eee;
168
+ cursor: default; }
169
+ .select2-container--default.select2-container--disabled .select2-selection--single .select2-selection__clear {
170
+ display: none; }
171
+
172
+ .select2-container--default.select2-container--open .select2-selection--single .select2-selection__arrow b {
173
+ border-color: transparent transparent #888 transparent;
174
+ border-width: 0 4px 5px 4px; }
175
+
176
+ .select2-container--default .select2-selection--multiple {
177
+ background-color: white;
178
+ border: 1px solid #aaa;
179
+ border-radius: 4px;
180
+ cursor: text; }
181
+ .select2-container--default .select2-selection--multiple .select2-selection__rendered {
182
+ box-sizing: border-box;
183
+ list-style: none;
184
+ margin: 0;
185
+ padding: 0 5px;
186
+ width: 100%; }
187
+ .select2-container--default .select2-selection--multiple .select2-selection__rendered li {
188
+ list-style: none; }
189
+ .select2-container--default .select2-selection--multiple .select2-selection__placeholder {
190
+ color: #999;
191
+ margin-top: 5px;
192
+ float: left; }
193
+ .select2-container--default .select2-selection--multiple .select2-selection__clear {
194
+ cursor: pointer;
195
+ float: right;
196
+ font-weight: bold;
197
+ margin-top: 5px;
198
+ margin-right: 10px; }
199
+ .select2-container--default .select2-selection--multiple .select2-selection__choice {
200
+ background-color: #e4e4e4;
201
+ border: 1px solid #aaa;
202
+ border-radius: 4px;
203
+ cursor: default;
204
+ float: left;
205
+ margin-right: 5px;
206
+ margin-top: 5px;
207
+ padding: 0 5px; }
208
+ .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {
209
+ color: #999;
210
+ cursor: pointer;
211
+ display: inline-block;
212
+ font-weight: bold;
213
+ margin-right: 2px; }
214
+ .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {
215
+ color: #333; }
216
+
217
+ .select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice, .select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__placeholder, .select2-container--default[dir="rtl"] .select2-selection--multiple .select2-search--inline {
218
+ float: right; }
219
+
220
+ .select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice {
221
+ margin-left: 5px;
222
+ margin-right: auto; }
223
+
224
+ .select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove {
225
+ margin-left: 2px;
226
+ margin-right: auto; }
227
+
228
+ .select2-container--default.select2-container--focus .select2-selection--multiple {
229
+ border: solid black 1px;
230
+ outline: 0; }
231
+
232
+ .select2-container--default.select2-container--disabled .select2-selection--multiple {
233
+ background-color: #eee;
234
+ cursor: default; }
235
+
236
+ .select2-container--default.select2-container--disabled .select2-selection__choice__remove {
237
+ display: none; }
238
+
239
+ .select2-container--default.select2-container--open.select2-container--above .select2-selection--single, .select2-container--default.select2-container--open.select2-container--above .select2-selection--multiple {
240
+ border-top-left-radius: 0;
241
+ border-top-right-radius: 0; }
242
+
243
+ .select2-container--default.select2-container--open.select2-container--below .select2-selection--single, .select2-container--default.select2-container--open.select2-container--below .select2-selection--multiple {
244
+ border-bottom-left-radius: 0;
245
+ border-bottom-right-radius: 0; }
246
+
247
+ .select2-container--default .select2-search--dropdown .select2-search__field {
248
+ border: 1px solid #aaa; }
249
+
250
+ .select2-container--default .select2-search--inline .select2-search__field {
251
+ background: transparent;
252
+ border: none;
253
+ outline: 0;
254
+ box-shadow: none;
255
+ -webkit-appearance: textfield; }
256
+
257
+ .select2-container--default .select2-results > .select2-results__options {
258
+ max-height: 200px;
259
+ overflow-y: auto; }
260
+
261
+ .select2-container--default .select2-results__option[role=group] {
262
+ padding: 0; }
263
+
264
+ .select2-container--default .select2-results__option[aria-disabled=true] {
265
+ color: #999; }
266
+
267
+ .select2-container--default .select2-results__option[aria-selected=true] {
268
+ background-color: #ddd; }
269
+
270
+ .select2-container--default .select2-results__option .select2-results__option {
271
+ padding-left: 1em; }
272
+ .select2-container--default .select2-results__option .select2-results__option .select2-results__group {
273
+ padding-left: 0; }
274
+ .select2-container--default .select2-results__option .select2-results__option .select2-results__option {
275
+ margin-left: -1em;
276
+ padding-left: 2em; }
277
+ .select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
278
+ margin-left: -2em;
279
+ padding-left: 3em; }
280
+ .select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
281
+ margin-left: -3em;
282
+ padding-left: 4em; }
283
+ .select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
284
+ margin-left: -4em;
285
+ padding-left: 5em; }
286
+ .select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
287
+ margin-left: -5em;
288
+ padding-left: 6em; }
289
+
290
+ .select2-container--default .select2-results__option--highlighted[aria-selected] {
291
+ background-color: #5897fb;
292
+ color: white; }
293
+
294
+ .select2-container--default .select2-results__group {
295
+ cursor: default;
296
+ display: block;
297
+ padding: 6px; }
298
+
299
+ .select2-container--classic .select2-selection--single {
300
+ background-color: #f7f7f7;
301
+ border: 1px solid #aaa;
302
+ border-radius: 4px;
303
+ outline: 0;
304
+ background-image: -webkit-linear-gradient(top, white 50%, #eeeeee 100%);
305
+ background-image: -o-linear-gradient(top, white 50%, #eeeeee 100%);
306
+ background-image: linear-gradient(to bottom, white 50%, #eeeeee 100%);
307
+ background-repeat: repeat-x;
308
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFFFF', endColorstr='#FFEEEEEE', GradientType=0); }
309
+ .select2-container--classic .select2-selection--single:focus {
310
+ border: 1px solid #5897fb; }
311
+ .select2-container--classic .select2-selection--single .select2-selection__rendered {
312
+ color: #444;
313
+ line-height: 28px; }
314
+ .select2-container--classic .select2-selection--single .select2-selection__clear {
315
+ cursor: pointer;
316
+ float: right;
317
+ font-weight: bold;
318
+ margin-right: 10px; }
319
+ .select2-container--classic .select2-selection--single .select2-selection__placeholder {
320
+ color: #999; }
321
+ .select2-container--classic .select2-selection--single .select2-selection__arrow {
322
+ background-color: #ddd;
323
+ border: none;
324
+ border-left: 1px solid #aaa;
325
+ border-top-right-radius: 4px;
326
+ border-bottom-right-radius: 4px;
327
+ height: 26px;
328
+ position: absolute;
329
+ top: 1px;
330
+ right: 1px;
331
+ width: 20px;
332
+ background-image: -webkit-linear-gradient(top, #eeeeee 50%, #cccccc 100%);
333
+ background-image: -o-linear-gradient(top, #eeeeee 50%, #cccccc 100%);
334
+ background-image: linear-gradient(to bottom, #eeeeee 50%, #cccccc 100%);
335
+ background-repeat: repeat-x;
336
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFEEEEEE', endColorstr='#FFCCCCCC', GradientType=0); }
337
+ .select2-container--classic .select2-selection--single .select2-selection__arrow b {
338
+ border-color: #888 transparent transparent transparent;
339
+ border-style: solid;
340
+ border-width: 5px 4px 0 4px;
341
+ height: 0;
342
+ left: 50%;
343
+ margin-left: -4px;
344
+ margin-top: -2px;
345
+ position: absolute;
346
+ top: 50%;
347
+ width: 0; }
348
+
349
+ .select2-container--classic[dir="rtl"] .select2-selection--single .select2-selection__clear {
350
+ float: left; }
351
+
352
+ .select2-container--classic[dir="rtl"] .select2-selection--single .select2-selection__arrow {
353
+ border: none;
354
+ border-right: 1px solid #aaa;
355
+ border-radius: 0;
356
+ border-top-left-radius: 4px;
357
+ border-bottom-left-radius: 4px;
358
+ left: 1px;
359
+ right: auto; }
360
+
361
+ .select2-container--classic.select2-container--open .select2-selection--single {
362
+ border: 1px solid #5897fb; }
363
+ .select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow {
364
+ background: transparent;
365
+ border: none; }
366
+ .select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow b {
367
+ border-color: transparent transparent #888 transparent;
368
+ border-width: 0 4px 5px 4px; }
369
+
370
+ .select2-container--classic.select2-container--open.select2-container--above .select2-selection--single {
371
+ border-top: none;
372
+ border-top-left-radius: 0;
373
+ border-top-right-radius: 0;
374
+ background-image: -webkit-linear-gradient(top, white 0%, #eeeeee 50%);
375
+ background-image: -o-linear-gradient(top, white 0%, #eeeeee 50%);
376
+ background-image: linear-gradient(to bottom, white 0%, #eeeeee 50%);
377
+ background-repeat: repeat-x;
378
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFFFF', endColorstr='#FFEEEEEE', GradientType=0); }
379
+
380
+ .select2-container--classic.select2-container--open.select2-container--below .select2-selection--single {
381
+ border-bottom: none;
382
+ border-bottom-left-radius: 0;
383
+ border-bottom-right-radius: 0;
384
+ background-image: -webkit-linear-gradient(top, #eeeeee 50%, white 100%);
385
+ background-image: -o-linear-gradient(top, #eeeeee 50%, white 100%);
386
+ background-image: linear-gradient(to bottom, #eeeeee 50%, white 100%);
387
+ background-repeat: repeat-x;
388
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFEEEEEE', endColorstr='#FFFFFFFF', GradientType=0); }
389
+
390
+ .select2-container--classic .select2-selection--multiple {
391
+ background-color: white;
392
+ border: 1px solid #aaa;
393
+ border-radius: 4px;
394
+ cursor: text;
395
+ outline: 0; }
396
+ .select2-container--classic .select2-selection--multiple:focus {
397
+ border: 1px solid #5897fb; }
398
+ .select2-container--classic .select2-selection--multiple .select2-selection__rendered {
399
+ list-style: none;
400
+ margin: 0;
401
+ padding: 0 5px; }
402
+ .select2-container--classic .select2-selection--multiple .select2-selection__clear {
403
+ display: none; }
404
+ .select2-container--classic .select2-selection--multiple .select2-selection__choice {
405
+ background-color: #e4e4e4;
406
+ border: 1px solid #aaa;
407
+ border-radius: 4px;
408
+ cursor: default;
409
+ float: left;
410
+ margin-right: 5px;
411
+ margin-top: 5px;
412
+ padding: 0 5px; }
413
+ .select2-container--classic .select2-selection--multiple .select2-selection__choice__remove {
414
+ color: #888;
415
+ cursor: pointer;
416
+ display: inline-block;
417
+ font-weight: bold;
418
+ margin-right: 2px; }
419
+ .select2-container--classic .select2-selection--multiple .select2-selection__choice__remove:hover {
420
+ color: #555; }
421
+
422
+ .select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice {
423
+ float: right; }
424
+
425
+ .select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice {
426
+ margin-left: 5px;
427
+ margin-right: auto; }
428
+
429
+ .select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove {
430
+ margin-left: 2px;
431
+ margin-right: auto; }
432
+
433
+ .select2-container--classic.select2-container--open .select2-selection--multiple {
434
+ border: 1px solid #5897fb; }
435
+
436
+ .select2-container--classic.select2-container--open.select2-container--above .select2-selection--multiple {
437
+ border-top: none;
438
+ border-top-left-radius: 0;
439
+ border-top-right-radius: 0; }
440
+
441
+ .select2-container--classic.select2-container--open.select2-container--below .select2-selection--multiple {
442
+ border-bottom: none;
443
+ border-bottom-left-radius: 0;
444
+ border-bottom-right-radius: 0; }
445
+
446
+ .select2-container--classic .select2-search--dropdown .select2-search__field {
447
+ border: 1px solid #aaa;
448
+ outline: 0; }
449
+
450
+ .select2-container--classic .select2-search--inline .select2-search__field {
451
+ outline: 0;
452
+ box-shadow: none; }
453
+
454
+ .select2-container--classic .select2-dropdown {
455
+ background-color: white;
456
+ border: 1px solid transparent; }
457
+
458
+ .select2-container--classic .select2-dropdown--above {
459
+ border-bottom: none; }
460
+
461
+ .select2-container--classic .select2-dropdown--below {
462
+ border-top: none; }
463
+
464
+ .select2-container--classic .select2-results > .select2-results__options {
465
+ max-height: 200px;
466
+ overflow-y: auto; }
467
+
468
+ .select2-container--classic .select2-results__option[role=group] {
469
+ padding: 0; }
470
+
471
+ .select2-container--classic .select2-results__option[aria-disabled=true] {
472
+ color: grey; }
473
+
474
+ .select2-container--classic .select2-results__option--highlighted[aria-selected] {
475
+ background-color: #3875d7;
476
+ color: white; }
477
+
478
+ .select2-container--classic .select2-results__group {
479
+ cursor: default;
480
+ display: block;
481
+ padding: 6px; }
482
+
483
+ .select2-container--classic.select2-container--open .select2-dropdown {
484
+ border-color: #5897fb; }
css/lib/select2/select2.min.css CHANGED
@@ -1 +1 @@
1
- .select2-container{box-sizing:border-box;display:inline-block;margin:0;position:relative;vertical-align:middle}.select2-container .select2-selection--single{box-sizing:border-box;cursor:pointer;display:block;height:28px;user-select:none;-webkit-user-select:none}.select2-container .select2-selection--single .select2-selection__rendered{display:block;padding-left:8px;padding-right:20px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.select2-container .select2-selection--single .select2-selection__clear{position:relative}.select2-container[dir="rtl"] .select2-selection--single .select2-selection__rendered{padding-right:8px;padding-left:20px}.select2-container .select2-selection--multiple{box-sizing:border-box;cursor:pointer;display:block;min-height:32px;user-select:none;-webkit-user-select:none}.select2-container .select2-selection--multiple .select2-selection__rendered{display:inline-block;overflow:hidden;padding-left:8px;text-overflow:ellipsis;white-space:nowrap}.select2-container .select2-search--inline{float:left}.select2-container .select2-search--inline .select2-search__field{box-sizing:border-box;border:none;font-size:100%;margin-top:5px;padding:0}.select2-container .select2-search--inline .select2-search__field::-webkit-search-cancel-button{-webkit-appearance:none}.select2-dropdown{background-color:white;border:1px solid #aaa;border-radius:4px;box-sizing:border-box;display:block;position:absolute;left:-100000px;width:100%;z-index:1051}.select2-results{display:block}.select2-results__options{list-style:none;margin:0;padding:0}.select2-results__option{padding:6px;user-select:none;-webkit-user-select:none}.select2-results__option[aria-selected]{cursor:pointer}.select2-container--open .select2-dropdown{left:0}.select2-container--open .select2-dropdown--above{border-bottom:none;border-bottom-left-radius:0;border-bottom-right-radius:0}.select2-container--open .select2-dropdown--below{border-top:none;border-top-left-radius:0;border-top-right-radius:0}.select2-search--dropdown{display:block;padding:4px}.select2-search--dropdown .select2-search__field{padding:4px;width:100%;box-sizing:border-box}.select2-search--dropdown .select2-search__field::-webkit-search-cancel-button{-webkit-appearance:none}.select2-search--dropdown.select2-search--hide{display:none}.select2-close-mask{border:0;margin:0;padding:0;display:block;position:fixed;left:0;top:0;min-height:100%;min-width:100%;height:auto;width:auto;opacity:0;z-index:99;background-color:#fff;filter:alpha(opacity=0)}.select2-hidden-accessible{border:0 !important;clip:rect(0 0 0 0) !important;height:1px !important;margin:-1px !important;overflow:hidden !important;padding:0 !important;position:absolute !important;width:1px !important}.select2-container--default .select2-selection--single{background-color:#fff;border:1px solid #aaa;border-radius:4px}.select2-container--default .select2-selection--single .select2-selection__rendered{color:#444;line-height:28px}.select2-container--default .select2-selection--single .select2-selection__clear{cursor:pointer;float:right;font-weight:bold}.select2-container--default .select2-selection--single .select2-selection__placeholder{color:#999}.select2-container--default .select2-selection--single .select2-selection__arrow{height:26px;position:absolute;top:1px;right:1px;width:20px}.select2-container--default .select2-selection--single .select2-selection__arrow b{border-color:#888 transparent transparent transparent;border-style:solid;border-width:5px 4px 0 4px;height:0;left:50%;margin-left:-4px;margin-top:-2px;position:absolute;top:50%;width:0}.select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__clear{float:left}.select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__arrow{left:1px;right:auto}.select2-container--default.select2-container--disabled .select2-selection--single{background-color:#eee;cursor:default}.select2-container--default.select2-container--disabled .select2-selection--single .select2-selection__clear{display:none}.select2-container--default.select2-container--open .select2-selection--single .select2-selection__arrow b{border-color:transparent transparent #888 transparent;border-width:0 4px 5px 4px}.select2-container--default .select2-selection--multiple{background-color:white;border:1px solid #aaa;border-radius:4px;cursor:text}.select2-container--default .select2-selection--multiple .select2-selection__rendered{box-sizing:border-box;list-style:none;margin:0;padding:0 5px;width:100%}.select2-container--default .select2-selection--multiple .select2-selection__rendered li{list-style:none}.select2-container--default .select2-selection--multiple .select2-selection__placeholder{color:#999;margin-top:5px;float:left}.select2-container--default .select2-selection--multiple .select2-selection__clear{cursor:pointer;float:right;font-weight:bold;margin-top:5px;margin-right:10px}.select2-container--default .select2-selection--multiple .select2-selection__choice{background-color:#e4e4e4;border:1px solid #aaa;border-radius:4px;cursor:default;float:left;margin-right:5px;margin-top:5px;padding:0 5px}.select2-container--default .select2-selection--multiple .select2-selection__choice__remove{color:#999;cursor:pointer;display:inline-block;font-weight:bold;margin-right:2px}.select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover{color:#333}.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice,.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__placeholder,.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-search--inline{float:right}.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice{margin-left:5px;margin-right:auto}.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove{margin-left:2px;margin-right:auto}.select2-container--default.select2-container--focus .select2-selection--multiple{border:solid black 1px;outline:0}.select2-container--default.select2-container--disabled .select2-selection--multiple{background-color:#eee;cursor:default}.select2-container--default.select2-container--disabled .select2-selection__choice__remove{display:none}.select2-container--default.select2-container--open.select2-container--above .select2-selection--single,.select2-container--default.select2-container--open.select2-container--above .select2-selection--multiple{border-top-left-radius:0;border-top-right-radius:0}.select2-container--default.select2-container--open.select2-container--below .select2-selection--single,.select2-container--default.select2-container--open.select2-container--below .select2-selection--multiple{border-bottom-left-radius:0;border-bottom-right-radius:0}.select2-container--default .select2-search--dropdown .select2-search__field{border:1px solid #aaa}.select2-container--default .select2-search--inline .select2-search__field{background:transparent;border:none;outline:0;box-shadow:none;-webkit-appearance:textfield}.select2-container--default .select2-results>.select2-results__options{max-height:200px;overflow-y:auto}.select2-container--default .select2-results__option[role=group]{padding:0}.select2-container--default .select2-results__option[aria-disabled=true]{color:#999}.select2-container--default .select2-results__option[aria-selected=true]{background-color:#ddd}.select2-container--default .select2-results__option .select2-results__option{padding-left:1em}.select2-container--default .select2-results__option .select2-results__option .select2-results__group{padding-left:0}.select2-container--default .select2-results__option .select2-results__option .select2-results__option{margin-left:-1em;padding-left:2em}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-2em;padding-left:3em}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-3em;padding-left:4em}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-4em;padding-left:5em}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-5em;padding-left:6em}.select2-container--default .select2-results__option--highlighted[aria-selected]{background-color:#5897fb;color:white}.select2-container--default .select2-results__group{cursor:default;display:block;padding:6px}.select2-container--classic .select2-selection--single{background-color:#f7f7f7;border:1px solid #aaa;border-radius:4px;outline:0;background-image:-webkit-linear-gradient(top, #fff 50%, #eee 100%);background-image:-o-linear-gradient(top, #fff 50%, #eee 100%);background-image:linear-gradient(to bottom, #fff 50%, #eee 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFFFF', endColorstr='#FFEEEEEE', GradientType=0)}.select2-container--classic .select2-selection--single:focus{border:1px solid #5897fb}.select2-container--classic .select2-selection--single .select2-selection__rendered{color:#444;line-height:28px}.select2-container--classic .select2-selection--single .select2-selection__clear{cursor:pointer;float:right;font-weight:bold;margin-right:10px}.select2-container--classic .select2-selection--single .select2-selection__placeholder{color:#999}.select2-container--classic .select2-selection--single .select2-selection__arrow{background-color:#ddd;border:none;border-left:1px solid #aaa;border-top-right-radius:4px;border-bottom-right-radius:4px;height:26px;position:absolute;top:1px;right:1px;width:20px;background-image:-webkit-linear-gradient(top, #eee 50%, #ccc 100%);background-image:-o-linear-gradient(top, #eee 50%, #ccc 100%);background-image:linear-gradient(to bottom, #eee 50%, #ccc 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFEEEEEE', endColorstr='#FFCCCCCC', GradientType=0)}.select2-container--classic .select2-selection--single .select2-selection__arrow b{border-color:#888 transparent transparent transparent;border-style:solid;border-width:5px 4px 0 4px;height:0;left:50%;margin-left:-4px;margin-top:-2px;position:absolute;top:50%;width:0}.select2-container--classic[dir="rtl"] .select2-selection--single .select2-selection__clear{float:left}.select2-container--classic[dir="rtl"] .select2-selection--single .select2-selection__arrow{border:none;border-right:1px solid #aaa;border-radius:0;border-top-left-radius:4px;border-bottom-left-radius:4px;left:1px;right:auto}.select2-container--classic.select2-container--open .select2-selection--single{border:1px solid #5897fb}.select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow{background:transparent;border:none}.select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow b{border-color:transparent transparent #888 transparent;border-width:0 4px 5px 4px}.select2-container--classic.select2-container--open.select2-container--above .select2-selection--single{border-top:none;border-top-left-radius:0;border-top-right-radius:0;background-image:-webkit-linear-gradient(top, #fff 0%, #eee 50%);background-image:-o-linear-gradient(top, #fff 0%, #eee 50%);background-image:linear-gradient(to bottom, #fff 0%, #eee 50%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFFFF', endColorstr='#FFEEEEEE', GradientType=0)}.select2-container--classic.select2-container--open.select2-container--below .select2-selection--single{border-bottom:none;border-bottom-left-radius:0;border-bottom-right-radius:0;background-image:-webkit-linear-gradient(top, #eee 50%, #fff 100%);background-image:-o-linear-gradient(top, #eee 50%, #fff 100%);background-image:linear-gradient(to bottom, #eee 50%, #fff 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFEEEEEE', endColorstr='#FFFFFFFF', GradientType=0)}.select2-container--classic .select2-selection--multiple{background-color:white;border:1px solid #aaa;border-radius:4px;cursor:text;outline:0}.select2-container--classic .select2-selection--multiple:focus{border:1px solid #5897fb}.select2-container--classic .select2-selection--multiple .select2-selection__rendered{list-style:none;margin:0;padding:0 5px}.select2-container--classic .select2-selection--multiple .select2-selection__clear{display:none}.select2-container--classic .select2-selection--multiple .select2-selection__choice{background-color:#e4e4e4;border:1px solid #aaa;border-radius:4px;cursor:default;float:left;margin-right:5px;margin-top:5px;padding:0 5px}.select2-container--classic .select2-selection--multiple .select2-selection__choice__remove{color:#888;cursor:pointer;display:inline-block;font-weight:bold;margin-right:2px}.select2-container--classic .select2-selection--multiple .select2-selection__choice__remove:hover{color:#555}.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice{float:right}.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice{margin-left:5px;margin-right:auto}.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove{margin-left:2px;margin-right:auto}.select2-container--classic.select2-container--open .select2-selection--multiple{border:1px solid #5897fb}.select2-container--classic.select2-container--open.select2-container--above .select2-selection--multiple{border-top:none;border-top-left-radius:0;border-top-right-radius:0}.select2-container--classic.select2-container--open.select2-container--below .select2-selection--multiple{border-bottom:none;border-bottom-left-radius:0;border-bottom-right-radius:0}.select2-container--classic .select2-search--dropdown .select2-search__field{border:1px solid #aaa;outline:0}.select2-container--classic .select2-search--inline .select2-search__field{outline:0;box-shadow:none}.select2-container--classic .select2-dropdown{background-color:#fff;border:1px solid transparent}.select2-container--classic .select2-dropdown--above{border-bottom:none}.select2-container--classic .select2-dropdown--below{border-top:none}.select2-container--classic .select2-results>.select2-results__options{max-height:200px;overflow-y:auto}.select2-container--classic .select2-results__option[role=group]{padding:0}.select2-container--classic .select2-results__option[aria-disabled=true]{color:grey}.select2-container--classic .select2-results__option--highlighted[aria-selected]{background-color:#3875d7;color:#fff}.select2-container--classic .select2-results__group{cursor:default;display:block;padding:6px}.select2-container--classic.select2-container--open .select2-dropdown{border-color:#5897fb}
1
+ .select2-container{box-sizing:border-box;display:inline-block;margin:0;position:relative;vertical-align:middle}.select2-container .select2-selection--single{box-sizing:border-box;cursor:pointer;display:block;height:28px;user-select:none;-webkit-user-select:none}.select2-container .select2-selection--single .select2-selection__rendered{display:block;padding-left:8px;padding-right:20px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.select2-container .select2-selection--single .select2-selection__clear{position:relative}.select2-container[dir="rtl"] .select2-selection--single .select2-selection__rendered{padding-right:8px;padding-left:20px}.select2-container .select2-selection--multiple{box-sizing:border-box;cursor:pointer;display:block;min-height:32px;user-select:none;-webkit-user-select:none}.select2-container .select2-selection--multiple .select2-selection__rendered{display:inline-block;overflow:hidden;padding-left:8px;text-overflow:ellipsis;white-space:nowrap}.select2-container .select2-search--inline{float:left}.select2-container .select2-search--inline .select2-search__field{box-sizing:border-box;border:none;font-size:100%;margin-top:5px;padding:0}.select2-container .select2-search--inline .select2-search__field::-webkit-search-cancel-button{-webkit-appearance:none}.select2-dropdown{background-color:white;border:1px solid #aaa;border-radius:4px;box-sizing:border-box;display:block;position:absolute;left:-100000px;width:100%;z-index:1051}.select2-results{display:block}.select2-results__options{list-style:none;margin:0;padding:0}.select2-results__option{padding:6px;user-select:none;-webkit-user-select:none}.select2-results__option[aria-selected]{cursor:pointer}.select2-container--open .select2-dropdown{left:0}.select2-container--open .select2-dropdown--above{border-bottom:none;border-bottom-left-radius:0;border-bottom-right-radius:0}.select2-container--open .select2-dropdown--below{border-top:none;border-top-left-radius:0;border-top-right-radius:0}.select2-search--dropdown{display:block;padding:4px}.select2-search--dropdown .select2-search__field{padding:4px;width:100%;box-sizing:border-box}.select2-search--dropdown .select2-search__field::-webkit-search-cancel-button{-webkit-appearance:none}.select2-search--dropdown.select2-search--hide{display:none}.select2-close-mask{border:0;margin:0;padding:0;display:block;position:fixed;left:0;top:0;min-height:100%;min-width:100%;height:auto;width:auto;opacity:0;z-index:99;background-color:#fff;filter:alpha(opacity=0)}.select2-hidden-accessible{border:0 !important;clip:rect(0 0 0 0) !important;height:1px !important;margin:-1px !important;overflow:hidden !important;padding:0 !important;position:absolute !important;width:1px !important}.select2-container--default .select2-selection--single{background-color:#fff;border:1px solid #aaa;border-radius:4px}.select2-container--default .select2-selection--single .select2-selection__rendered{color:#444;line-height:28px}.select2-container--default .select2-selection--single .select2-selection__clear{cursor:pointer;float:right;font-weight:bold}.select2-container--default .select2-selection--single .select2-selection__placeholder{color:#999}.select2-container--default .select2-selection--single .select2-selection__arrow{height:26px;position:absolute;top:1px;right:1px;width:20px}.select2-container--default .select2-selection--single .select2-selection__arrow b{border-color:#888 transparent transparent transparent;border-style:solid;border-width:5px 4px 0 4px;height:0;left:50%;margin-left:-4px;margin-top:-2px;position:absolute;top:50%;width:0}.select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__clear{float:left}.select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__arrow{left:1px;right:auto}.select2-container--default.select2-container--disabled .select2-selection--single{background-color:#eee;cursor:default}.select2-container--default.select2-container--disabled .select2-selection--single .select2-selection__clear{display:none}.select2-container--default.select2-container--open .select2-selection--single .select2-selection__arrow b{border-color:transparent transparent #888 transparent;border-width:0 4px 5px 4px}.select2-container--default .select2-selection--multiple{background-color:white;border:1px solid #aaa;border-radius:4px;cursor:text}.select2-container--default .select2-selection--multiple .select2-selection__rendered{box-sizing:border-box;list-style:none;margin:0;padding:0 5px;width:100%}.select2-container--default .select2-selection--multiple .select2-selection__rendered li{list-style:none}.select2-container--default .select2-selection--multiple .select2-selection__placeholder{color:#999;margin-top:5px;float:left}.select2-container--default .select2-selection--multiple .select2-selection__clear{cursor:pointer;float:right;font-weight:bold;margin-top:5px;margin-right:10px}.select2-container--default .select2-selection--multiple .select2-selection__choice{background-color:#e4e4e4;border:1px solid #aaa;border-radius:4px;cursor:default;float:left;margin-right:5px;margin-top:5px;padding:0 5px}.select2-container--default .select2-selection--multiple .select2-selection__choice__remove{color:#999;cursor:pointer;display:inline-block;font-weight:bold;margin-right:2px}.select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover{color:#333}.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice,.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__placeholder,.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-search--inline{float:right}.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice{margin-left:5px;margin-right:auto}.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove{margin-left:2px;margin-right:auto}.select2-container--default.select2-container--focus .select2-selection--multiple{border:solid black 1px;outline:0}.select2-container--default.select2-container--disabled .select2-selection--multiple{background-color:#eee;cursor:default}.select2-container--default.select2-container--disabled .select2-selection__choice__remove{display:none}.select2-container--default.select2-container--open.select2-container--above .select2-selection--single,.select2-container--default.select2-container--open.select2-container--above .select2-selection--multiple{border-top-left-radius:0;border-top-right-radius:0}.select2-container--default.select2-container--open.select2-container--below .select2-selection--single,.select2-container--default.select2-container--open.select2-container--below .select2-selection--multiple{border-bottom-left-radius:0;border-bottom-right-radius:0}.select2-container--default .select2-search--dropdown .select2-search__field{border:1px solid #aaa}.select2-container--default .select2-search--inline .select2-search__field{background:transparent;border:none;outline:0;box-shadow:none;-webkit-appearance:textfield}.select2-container--default .select2-results>.select2-results__options{max-height:200px;overflow-y:auto}.select2-container--default .select2-results__option[role=group]{padding:0}.select2-container--default .select2-results__option[aria-disabled=true]{color:#999}.select2-container--default .select2-results__option[aria-selected=true]{background-color:#ddd}.select2-container--default .select2-results__option .select2-results__option{padding-left:1em}.select2-container--default .select2-results__option .select2-results__option .select2-results__group{padding-left:0}.select2-container--default .select2-results__option .select2-results__option .select2-results__option{margin-left:-1em;padding-left:2em}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-2em;padding-left:3em}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-3em;padding-left:4em}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-4em;padding-left:5em}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-5em;padding-left:6em}.select2-container--default .select2-results__option--highlighted[aria-selected]{background-color:#5897fb;color:white}.select2-container--default .select2-results__group{cursor:default;display:block;padding:6px}.select2-container--classic .select2-selection--single{background-color:#f7f7f7;border:1px solid #aaa;border-radius:4px;outline:0;background-image:-webkit-linear-gradient(top, #fff 50%, #eee 100%);background-image:-o-linear-gradient(top, #fff 50%, #eee 100%);background-image:linear-gradient(to bottom, #fff 50%, #eee 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFFFF', endColorstr='#FFEEEEEE', GradientType=0)}.select2-container--classic .select2-selection--single:focus{border:1px solid #5897fb}.select2-container--classic .select2-selection--single .select2-selection__rendered{color:#444;line-height:28px}.select2-container--classic .select2-selection--single .select2-selection__clear{cursor:pointer;float:right;font-weight:bold;margin-right:10px}.select2-container--classic .select2-selection--single .select2-selection__placeholder{color:#999}.select2-container--classic .select2-selection--single .select2-selection__arrow{background-color:#ddd;border:none;border-left:1px solid #aaa;border-top-right-radius:4px;border-bottom-right-radius:4px;height:26px;position:absolute;top:1px;right:1px;width:20px;background-image:-webkit-linear-gradient(top, #eee 50%, #ccc 100%);background-image:-o-linear-gradient(top, #eee 50%, #ccc 100%);background-image:linear-gradient(to bottom, #eee 50%, #ccc 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFEEEEEE', endColorstr='#FFCCCCCC', GradientType=0)}.select2-container--classic .select2-selection--single .select2-selection__arrow b{border-color:#888 transparent transparent transparent;border-style:solid;border-width:5px 4px 0 4px;height:0;left:50%;margin-left:-4px;margin-top:-2px;position:absolute;top:50%;width:0}.select2-container--classic[dir="rtl"] .select2-selection--single .select2-selection__clear{float:left}.select2-container--classic[dir="rtl"] .select2-selection--single .select2-selection__arrow{border:none;border-right:1px solid #aaa;border-radius:0;border-top-left-radius:4px;border-bottom-left-radius:4px;left:1px;right:auto}.select2-container--classic.select2-container--open .select2-selection--single{border:1px solid #5897fb}.select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow{background:transparent;border:none}.select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow b{border-color:transparent transparent #888 transparent;border-width:0 4px 5px 4px}.select2-container--classic.select2-container--open.select2-container--above .select2-selection--single{border-top:none;border-top-left-radius:0;border-top-right-radius:0;background-image:-webkit-linear-gradient(top, #fff 0%, #eee 50%);background-image:-o-linear-gradient(top, #fff 0%, #eee 50%);background-image:linear-gradient(to bottom, #fff 0%, #eee 50%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFFFF', endColorstr='#FFEEEEEE', GradientType=0)}.select2-container--classic.select2-container--open.select2-container--below .select2-selection--single{border-bottom:none;border-bottom-left-radius:0;border-bottom-right-radius:0;background-image:-webkit-linear-gradient(top, #eee 50%, #fff 100%);background-image:-o-linear-gradient(top, #eee 50%, #fff 100%);background-image:linear-gradient(to bottom, #eee 50%, #fff 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFEEEEEE', endColorstr='#FFFFFFFF', GradientType=0)}.select2-container--classic .select2-selection--multiple{background-color:white;border:1px solid #aaa;border-radius:4px;cursor:text;outline:0}.select2-container--classic .select2-selection--multiple:focus{border:1px solid #5897fb}.select2-container--classic .select2-selection--multiple .select2-selection__rendered{list-style:none;margin:0;padding:0 5px}.select2-container--classic .select2-selection--multiple .select2-selection__clear{display:none}.select2-container--classic .select2-selection--multiple .select2-selection__choice{background-color:#e4e4e4;border:1px solid #aaa;border-radius:4px;cursor:default;float:left;margin-right:5px;margin-top:5px;padding:0 5px}.select2-container--classic .select2-selection--multiple .select2-selection__choice__remove{color:#888;cursor:pointer;display:inline-block;font-weight:bold;margin-right:2px}.select2-container--classic .select2-selection--multiple .select2-selection__choice__remove:hover{color:#555}.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice{float:right}.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice{margin-left:5px;margin-right:auto}.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove{margin-left:2px;margin-right:auto}.select2-container--classic.select2-container--open .select2-selection--multiple{border:1px solid #5897fb}.select2-container--classic.select2-container--open.select2-container--above .select2-selection--multiple{border-top:none;border-top-left-radius:0;border-top-right-radius:0}.select2-container--classic.select2-container--open.select2-container--below .select2-selection--multiple{border-bottom:none;border-bottom-left-radius:0;border-bottom-right-radius:0}.select2-container--classic .select2-search--dropdown .select2-search__field{border:1px solid #aaa;outline:0}.select2-container--classic .select2-search--inline .select2-search__field{outline:0;box-shadow:none}.select2-container--classic .select2-dropdown{background-color:#fff;border:1px solid transparent}.select2-container--classic .select2-dropdown--above{border-bottom:none}.select2-container--classic .select2-dropdown--below{border-top:none}.select2-container--classic .select2-results>.select2-results__options{max-height:200px;overflow-y:auto}.select2-container--classic .select2-results__option[role=group]{padding:0}.select2-container--classic .select2-results__option[aria-disabled=true]{color:grey}.select2-container--classic .select2-results__option--highlighted[aria-selected]{background-color:#3875d7;color:#fff}.select2-container--classic .select2-results__group{cursor:default;display:block;padding:6px}.select2-container--classic.select2-container--open .select2-dropdown{border-color:#5897fb}
images/admin-review-notice-logo.png CHANGED
File without changes
images/index.php CHANGED
File without changes
images/sidebar.jpg CHANGED
File without changes
images/spinner-2x.gif CHANGED
File without changes
images/spinner.gif CHANGED
File without changes
index.php CHANGED
File without changes
js/app/advance_link_picker/.eslintrc.json CHANGED
@@ -1,30 +1,30 @@
1
- {
2
- "env": {
3
- "browser": true,
4
- "es6": true
5
- },
6
- "extends": "eslint:recommended",
7
- "parserOptions": {
8
- "sourceType": "module"
9
- },
10
- "rules": {
11
- "no-console" : 0,
12
- "indent": 0,
13
- "indent": [
14
- "error",
15
- 4
16
- ],
17
- "linebreak-style": [
18
- "error",
19
- "unix"
20
- ],
21
- "quotes": [
22
- "error",
23
- "double"
24
- ],
25
- "semi": [
26
- "error",
27
- "always"
28
- ]
29
- }
30
  }
1
+ {
2
+ "env": {
3
+ "browser": true,
4
+ "es6": true
5
+ },
6
+ "extends": "eslint:recommended",
7
+ "parserOptions": {
8
+ "sourceType": "module"
9
+ },
10
+ "rules": {
11
+ "no-console" : 0,
12
+ "indent": 0,
13
+ "indent": [
14
+ "error",
15
+ 4
16
+ ],
17
+ "linebreak-style": [
18
+ "error",
19
+ "unix"
20
+ ],
21
+ "quotes": [
22
+ "error",
23
+ "double"
24
+ ],
25
+ "semi": [
26
+ "error",
27
+ "always"
28
+ ]
29
+ }
30
  }
js/app/advance_link_picker/dist/advance-link-picker.css CHANGED
File without changes
js/app/advance_link_picker/dist/advance-link-picker.js CHANGED
File without changes
js/app/advance_link_picker/package-lock.json CHANGED
@@ -1,9586 +1,9586 @@
1
- {
2
- "name": "affiliate_link_page",
3
- "version": "1.0.0",
4
- "lockfileVersion": 1,
5
- "requires": true,
6
- "dependencies": {
7
- "abbrev": {
8
- "version": "1.1.1",
9
- "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
10
- "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
11
- "dev": true
12
- },
13
- "acorn": {
14
- "version": "5.2.1",
15
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.2.1.tgz",
16
- "integrity": "sha512-jG0u7c4Ly+3QkkW18V+NRDN+4bWHdln30NL1ZL2AvFZZmQe/BfopYCtghCKKVBUSetZ4QKcyA0pY6/4Gw8Pv8w==",
17
- "dev": true
18
- },
19
- "acorn-dynamic-import": {
20
- "version": "2.0.2",
21
- "resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-2.0.2.tgz",
22
- "integrity": "sha1-x1K9IQvvZ5UBtsbLf8hPj0cVjMQ=",
23
- "dev": true,
24
- "requires": {
25
- "acorn": "^4.0.3"
26
- },
27
- "dependencies": {
28
- "acorn": {
29
- "version": "4.0.13",
30
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz",
31
- "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=",
32
- "dev": true
33
- }
34
- }
35
- },
36
- "acorn-jsx": {
37
- "version": "3.0.1",
38
- "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz",
39
- "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=",
40
- "dev": true,
41
- "requires": {
42
- "acorn": "^3.0.4"
43
- },
44
- "dependencies": {
45
- "acorn": {
46
- "version": "3.3.0",
47
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz",
48
- "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=",
49
- "dev": true
50
- }
51
- }
52
- },
53
- "ajv": {
54
- "version": "4.11.8",
55
- "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz",
56
- "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=",
57
- "dev": true,
58
- "requires": {
59
- "co": "^4.6.0",
60
- "json-stable-stringify": "^1.0.1"
61
- }
62
- },
63
- "ajv-keywords": {
64
- "version": "1.5.1",
65
- "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-1.5.1.tgz",
66
- "integrity": "sha1-MU3QpLM2j609/NxU7eYXG4htrzw=",
67
- "dev": true
68
- },
69
- "align-text": {
70
- "version": "0.1.4",
71
- "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz",
72
- "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=",
73
- "dev": true,
74
- "requires": {
75
- "kind-of": "^3.0.2",
76
- "longest": "^1.0.1",
77
- "repeat-string": "^1.5.2"
78
- }
79
- },
80
- "alphanum-sort": {
81
- "version": "1.0.2",
82
- "resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz",
83
- "integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=",
84
- "dev": true
85
- },
86
- "amdefine": {
87
- "version": "1.0.1",
88
- "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz",
89
- "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=",
90
- "dev": true
91
- },
92
- "ansi-escapes": {
93
- "version": "1.4.0",
94
- "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz",
95
- "integrity": "sha1-06ioOzGapneTZisT52HHkRQiMG4=",
96
- "dev": true
97
- },
98
- "ansi-regex": {
99
- "version": "2.1.1",
100
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
101
- "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
102
- "dev": true
103
- },
104
- "ansi-styles": {
105
- "version": "2.2.1",
106
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
107
- "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
108
- "dev": true
109
- },
110
- "anymatch": {
111
- "version": "1.3.2",
112
- "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz",
113
- "integrity": "sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA==",
114
- "dev": true,
115
- "requires": {
116
- "micromatch": "^2.1.5",
117
- "normalize-path": "^2.0.0"
118
- }
119
- },
120
- "aproba": {
121
- "version": "1.2.0",
122
- "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
123
- "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==",
124
- "dev": true
125
- },
126
- "are-we-there-yet": {
127
- "version": "1.1.4",
128
- "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz",
129
- "integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=",
130
- "dev": true,
131
- "requires": {
132
- "delegates": "^1.0.0",
133
- "readable-stream": "^2.0.6"
134
- }
135
- },
136
- "argparse": {
137
- "version": "1.0.9",
138
- "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz",
139
- "integrity": "sha1-c9g7wmP4bpf4zE9rrhsOkKfSLIY=",
140
- "dev": true,
141
- "requires": {
142
- "sprintf-js": "~1.0.2"
143
- }
144
- },
145
- "arr-diff": {
146
- "version": "2.0.0",
147
- "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz",
148
- "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=",
149
- "dev": true,
150
- "requires": {
151
- "arr-flatten": "^1.0.1"
152
- }
153
- },
154
- "arr-flatten": {
155
- "version": "1.1.0",
156
- "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz",
157
- "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==",
158
- "dev": true
159
- },
160
- "array-find-index": {
161
- "version": "1.0.2",
162
- "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz",
163
- "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=",
164
- "dev": true
165
- },
166
- "array-union": {
167
- "version": "1.0.2",
168
- "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz",
169
- "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=",
170
- "dev": true,
171
- "requires": {
172
- "array-uniq": "^1.0.1"
173
- }
174
- },
175
- "array-uniq": {
176
- "version": "1.0.3",
177
- "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz",
178
- "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=",
179
- "dev": true
180
- },
181
- "array-unique": {
182
- "version": "0.2.1",
183
- "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz",
184
- "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=",
185
- "dev": true
186
- },
187
- "arrify": {
188
- "version": "1.0.1",
189
- "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz",
190
- "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=",
191
- "dev": true
192
- },
193
- "asn1": {
194
- "version": "0.2.3",
195
- "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz",
196
- "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=",
197
- "dev": true
198
- },
199
- "asn1.js": {
200
- "version": "4.9.2",
201
- "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.9.2.tgz",
202
- "integrity": "sha512-b/OsSjvWEo8Pi8H0zsDd2P6Uqo2TK2pH8gNLSJtNLM2Db0v2QaAZ0pBQJXVjAn4gBuugeVDr7s63ZogpUIwWDg==",
203
- "dev": true,
204
- "requires": {
205
- "bn.js": "^4.0.0",
206
- "inherits": "^2.0.1",
207
- "minimalistic-assert": "^1.0.0"
208
- }
209
- },
210
- "assert": {
211
- "version": "1.4.1",
212
- "resolved": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz",
213
- "integrity": "sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=",
214
- "dev": true,
215
- "requires": {
216
- "util": "0.10.3"
217
- }
218
- },
219
- "assert-plus": {
220
- "version": "0.2.0",
221
- "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz",
222
- "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=",
223
- "dev": true
224
- },
225
- "async": {
226
- "version": "2.6.0",
227
- "resolved": "https://registry.npmjs.org/async/-/async-2.6.0.tgz",
228
- "integrity": "sha512-xAfGg1/NTLBBKlHFmnd7PlmUW9KhVQIUuSrYem9xzFUZy13ScvtyGGejaae9iAVRiRq9+Cx7DPFaAAhCpyxyPw==",
229
- "dev": true,
230
- "requires": {
231
- "lodash": "^4.14.0"
232
- }
233
- },
234
- "async-each": {
235
- "version": "1.0.1",
236
- "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz",
237
- "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=",
238
- "dev": true
239
- },
240
- "async-foreach": {
241
- "version": "0.1.3",
242
- "resolved": "https://registry.npmjs.org/async-foreach/-/async-foreach-0.1.3.tgz",
243
- "integrity": "sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI=",
244
- "dev": true
245
- },
246
- "asynckit": {
247
- "version": "0.4.0",
248
- "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
249
- "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=",
250
- "dev": true
251
- },
252
- "autoprefixer": {
253
- "version": "6.7.7",
254
- "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-6.7.7.tgz",
255
- "integrity": "sha1-Hb0cg1ZY41zj+ZhAmdsAWFx4IBQ=",
256
- "dev": true,
257
- "requires": {
258
- "browserslist": "^1.7.6",
259
- "caniuse-db": "^1.0.30000634",
260
- "normalize-range": "^0.1.2",
261
- "num2fraction": "^1.2.2",
262
- "postcss": "^5.2.16",
263
- "postcss-value-parser": "^3.2.3"
264
- },
265
- "dependencies": {
266
- "browserslist": {
267
- "version": "1.7.7",
268
- "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz",
269
- "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=",
270
- "dev": true,
271
- "requires": {
272
- "caniuse-db": "^1.0.30000639",
273
- "electron-to-chromium": "^1.2.7"
274
- }
275
- }
276
- }
277
- },
278
- "aws-sign2": {
279
- "version": "0.6.0",
280
- "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz",
281
- "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=",
282
- "dev": true
283
- },
284
- "aws4": {
285
- "version": "1.6.0",
286
- "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz",
287
- "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=",
288
- "dev": true
289
- },
290
- "babel-code-frame": {
291
- "version": "6.26.0",
292
- "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz",
293
- "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=",
294
- "dev": true,
295
- "requires": {
296
- "chalk": "^1.1.3",
297
- "esutils": "^2.0.2",
298
- "js-tokens": "^3.0.2"
299
- }
300
- },
301
- "babel-core": {
302
- "version": "6.26.0",
303
- "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.0.tgz",
304
- "integrity": "sha1-rzL3izGm/O8RnIew/Y2XU/A6C7g=",
305
- "dev": true,
306
- "requires": {
307
- "babel-code-frame": "^6.26.0",
308
- "babel-generator": "^6.26.0",
309
- "babel-helpers": "^6.24.1",
310
- "babel-messages": "^6.23.0",
311
- "babel-register": "^6.26.0",
312
- "babel-runtime": "^6.26.0",
313
- "babel-template": "^6.26.0",
314
- "babel-traverse": "^6.26.0",
315
- "babel-types": "^6.26.0",
316
- "babylon": "^6.18.0",
317
- "convert-source-map": "^1.5.0",
318
- "debug": "^2.6.8",
319
- "json5": "^0.5.1",
320
- "lodash": "^4.17.4",
321
- "minimatch": "^3.0.4",
322
- "path-is-absolute": "^1.0.1",
323
- "private": "^0.1.7",
324
- "slash": "^1.0.0",
325
- "source-map": "^0.5.6"
326
- }
327
- },
328
- "babel-generator": {
329
- "version": "6.26.0",
330
- "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.0.tgz",
331
- "integrity": "sha1-rBriAHC3n248odMmlhMFN3TyDcU=",
332
- "dev": true,
333
- "requires": {
334
- "babel-messages": "^6.23.0",
335
- "babel-runtime": "^6.26.0",
336
- "babel-types": "^6.26.0",
337
- "detect-indent": "^4.0.0",
338
- "jsesc": "^1.3.0",
339
- "lodash": "^4.17.4",
340
- "source-map": "^0.5.6",
341
- "trim-right": "^1.0.1"
342
- }
343
- },
344
- "babel-helper-builder-binary-assignment-operator-visitor": {
345
- "version": "6.24.1",
346
- "resolved": "https://registry.npmjs.org/babel-helper-builder-binary-assignment-operator-visitor/-/babel-helper-builder-binary-assignment-operator-visitor-6.24.1.tgz",
347
- "integrity": "sha1-zORReto1b0IgvK6KAsKzRvmlZmQ=",
348
- "dev": true,
349
- "requires": {
350
- "babel-helper-explode-assignable-expression": "^6.24.1",
351
- "babel-runtime": "^6.22.0",
352
- "babel-types": "^6.24.1"
353
- }
354
- },
355
- "babel-helper-call-delegate": {
356
- "version": "6.24.1",
357
- "resolved": "https://registry.npmjs.org/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz",
358
- "integrity": "sha1-7Oaqzdx25Bw0YfiL/Fdb0Nqi340=",
359
- "dev": true,
360
- "requires": {
361
- "babel-helper-hoist-variables": "^6.24.1",
362
- "babel-runtime": "^6.22.0",
363
- "babel-traverse": "^6.24.1",
364
- "babel-types": "^6.24.1"
365
- }
366
- },
367
- "babel-helper-define-map": {
368
- "version": "6.26.0",
369
- "resolved": "https://registry.npmjs.org/babel-helper-define-map/-/babel-helper-define-map-6.26.0.tgz",
370
- "integrity": "sha1-pfVtq0GiX5fstJjH66ypgZ+Vvl8=",
371
- "dev": true,
372
- "requires": {
373
- "babel-helper-function-name": "^6.24.1",
374
- "babel-runtime": "^6.26.0",
375
- "babel-types": "^6.26.0",
376
- "lodash": "^4.17.4"
377
- }
378
- },
379
- "babel-helper-explode-assignable-expression": {
380
- "version": "6.24.1",
381
- "resolved": "https://registry.npmjs.org/babel-helper-explode-assignable-expression/-/babel-helper-explode-assignable-expression-6.24.1.tgz",
382
- "integrity": "sha1-8luCz33BBDPFX3BZLVdGQArCLKo=",
383
- "dev": true,
384
- "requires": {
385
- "babel-runtime": "^6.22.0",
386
- "babel-traverse": "^6.24.1",
387
- "babel-types": "^6.24.1"
388
- }
389
- },
390
- "babel-helper-function-name": {
391
- "version": "6.24.1",
392
- "resolved": "https://registry.npmjs.org/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz",
393
- "integrity": "sha1-00dbjAPtmCQqJbSDUasYOZ01gKk=",
394
- "dev": true,
395
- "requires": {
396
- "babel-helper-get-function-arity": "^6.24.1",
397
- "babel-runtime": "^6.22.0",
398
- "babel-template": "^6.24.1",
399
- "babel-traverse": "^6.24.1",
400
- "babel-types": "^6.24.1"
401
- }
402
- },
403
- "babel-helper-get-function-arity": {
404
- "version": "6.24.1",
405
- "resolved": "https://registry.npmjs.org/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz",
406
- "integrity": "sha1-j3eCqpNAfEHTqlCQj4mwMbG2hT0=",
407
- "dev": true,
408
- "requires": {
409
- "babel-runtime": "^6.22.0",
410
- "babel-types": "^6.24.1"
411
- }
412
- },
413
- "babel-helper-hoist-variables": {
414
- "version": "6.24.1",
415
- "resolved": "https://registry.npmjs.org/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz",
416
- "integrity": "sha1-HssnaJydJVE+rbyZFKc/VAi+enY=",
417
- "dev": true,
418
- "requires": {
419
- "babel-runtime": "^6.22.0",
420
- "babel-types": "^6.24.1"
421
- }
422
- },
423
- "babel-helper-optimise-call-expression": {
424
- "version": "6.24.1",
425
- "resolved": "https://registry.npmjs.org/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz",
426
- "integrity": "sha1-96E0J7qfc/j0+pk8VKl4gtEkQlc=",
427
- "dev": true,
428
- "requires": {
429
- "babel-runtime": "^6.22.0",
430
- "babel-types": "^6.24.1"
431
- }
432
- },
433
- "babel-helper-regex": {
434
- "version": "6.26.0",
435
- "resolved": "https://registry.npmjs.org/babel-helper-regex/-/babel-helper-regex-6.26.0.tgz",
436
- "integrity": "sha1-MlxZ+QL4LyS3T6zu0DY5VPZJXnI=",
437
- "dev": true,
438
- "requires": {
439
- "babel-runtime": "^6.26.0",
440
- "babel-types": "^6.26.0",
441
- "lodash": "^4.17.4"
442
- }
443
- },
444
- "babel-helper-remap-async-to-generator": {
445
- "version": "6.24.1",
446
- "resolved": "https://registry.npmjs.org/babel-helper-remap-async-to-generator/-/babel-helper-remap-async-to-generator-6.24.1.tgz",
447
- "integrity": "sha1-XsWBgnrXI/7N04HxySg5BnbkVRs=",
448
- "dev": true,
449
- "requires": {
450
- "babel-helper-function-name": "^6.24.1",
451
- "babel-runtime": "^6.22.0",
452
- "babel-template": "^6.24.1",
453
- "babel-traverse": "^6.24.1",
454
- "babel-types": "^6.24.1"
455
- }
456
- },
457
- "babel-helper-replace-supers": {
458
- "version": "6.24.1",
459
- "resolved": "https://registry.npmjs.org/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz",
460
- "integrity": "sha1-v22/5Dk40XNpohPKiov3S2qQqxo=",
461
- "dev": true,
462
- "requires": {
463
- "babel-helper-optimise-call-expression": "^6.24.1",
464
- "babel-messages": "^6.23.0",
465
- "babel-runtime": "^6.22.0",
466
- "babel-template": "^6.24.1",
467
- "babel-traverse": "^6.24.1",
468
- "babel-types": "^6.24.1"
469
- }
470
- },
471
- "babel-helpers": {
472
- "version": "6.24.1",
473
- "resolved": "https://registry.npmjs.org/babel-helpers/-/babel-helpers-6.24.1.tgz",
474
- "integrity": "sha1-NHHenK7DiOXIUOWX5Yom3fN2ArI=",
475
- "dev": true,
476
- "requires": {
477
- "babel-runtime": "^6.22.0",
478
- "babel-template": "^6.24.1"
479
- }
480
- },
481
- "babel-loader": {
482
- "version": "7.1.2",
483
- "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-7.1.2.tgz",
484
- "integrity": "sha512-jRwlFbINAeyDStqK6Dd5YuY0k5YuzQUvlz2ZamuXrXmxav3pNqe9vfJ402+2G+OmlJSXxCOpB6Uz0INM7RQe2A==",
485
- "dev": true,
486
- "requires": {
487
- "find-cache-dir": "^1.0.0",
488
- "loader-utils": "^1.0.2",
489
- "mkdirp": "^0.5.1"
490
- }
491
- },
492
- "babel-messages": {
493
- "version": "6.23.0",
494
- "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz",
495
- "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=",
496
- "dev": true,
497
- "requires": {
498
- "babel-runtime": "^6.22.0"
499
- }
500
- },
501
- "babel-plugin-check-es2015-constants": {
502
- "version": "6.22.0",
503
- "resolved": "https://registry.npmjs.org/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz",
504
- "integrity": "sha1-NRV7EBQm/S/9PaP3XH0ekYNbv4o=",
505
- "dev": true,
506
- "requires": {
507
- "babel-runtime": "^6.22.0"
508
- }
509
- },
510
- "babel-plugin-syntax-async-functions": {
511
- "version": "6.13.0",
512
- "resolved": "https://registry.npmjs.org/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz",
513
- "integrity": "sha1-ytnK0RkbWtY0vzCuCHI5HgZHvpU=",
514
- "dev": true
515
- },
516
- "babel-plugin-syntax-exponentiation-operator": {
517
- "version": "6.13.0",
518
- "resolved": "https://registry.npmjs.org/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz",
519
- "integrity": "sha1-nufoM3KQ2pUoggGmpX9BcDF4MN4=",
520
- "dev": true
521
- },
522
- "babel-plugin-syntax-trailing-function-commas": {
523
- "version": "6.22.0",
524
- "resolved": "https://registry.npmjs.org/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz",
525
- "integrity": "sha1-ugNgk3+NBuQBgKQ/4NVhb/9TLPM=",
526
- "dev": true
527
- },
528
- "babel-plugin-transform-async-to-generator": {
529
- "version": "6.24.1",
530
- "resolved": "https://registry.npmjs.org/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.24.1.tgz",
531
- "integrity": "sha1-ZTbjeK/2yx1VF6wOQOs+n8jQh2E=",
532
- "dev": true,
533
- "requires": {
534
- "babel-helper-remap-async-to-generator": "^6.24.1",
535
- "babel-plugin-syntax-async-functions": "^6.8.0",
536
- "babel-runtime": "^6.22.0"
537
- }
538
- },
539
- "babel-plugin-transform-es2015-arrow-functions": {
540
- "version": "6.22.0",
541
- "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz",
542
- "integrity": "sha1-RSaSy3EdX3ncf4XkQM5BufJE0iE=",
543
- "dev": true,
544
- "requires": {
545
- "babel-runtime": "^6.22.0"
546
- }
547
- },
548
- "babel-plugin-transform-es2015-block-scoped-functions": {
549
- "version": "6.22.0",
550
- "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz",
551
- "integrity": "sha1-u8UbSflk1wy42OC5ToICRs46YUE=",
552
- "dev": true,
553
- "requires": {
554
- "babel-runtime": "^6.22.0"
555
- }
556
- },
557
- "babel-plugin-transform-es2015-block-scoping": {
558
- "version": "6.26.0",
559
- "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.26.0.tgz",
560
- "integrity": "sha1-1w9SmcEwjQXBL0Y4E7CgnnOxiV8=",
561
- "dev": true,
562
- "requires": {
563
- "babel-runtime": "^6.26.0",
564
- "babel-template": "^6.26.0",
565
- "babel-traverse": "^6.26.0",
566
- "babel-types": "^6.26.0",
567
- "lodash": "^4.17.4"
568
- }
569
- },
570
- "babel-plugin-transform-es2015-classes": {
571
- "version": "6.24.1",
572
- "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz",
573
- "integrity": "sha1-WkxYpQyclGHlZLSyo7+ryXolhNs=",
574
- "dev": true,
575
- "requires": {
576
- "babel-helper-define-map": "^6.24.1",
577
- "babel-helper-function-name": "^6.24.1",
578
- "babel-helper-optimise-call-expression": "^6.24.1",
579
- "babel-helper-replace-supers": "^6.24.1",
580
- "babel-messages": "^6.23.0",
581
- "babel-runtime": "^6.22.0",
582
- "babel-template": "^6.24.1",
583
- "babel-traverse": "^6.24.1",
584
- "babel-types": "^6.24.1"
585
- }
586
- },
587
- "babel-plugin-transform-es2015-computed-properties": {
588
- "version": "6.24.1",
589
- "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz",
590
- "integrity": "sha1-b+Ko0WiV1WNPTNmZttNICjCBWbM=",
591
- "dev": true,
592
- "requires": {
593
- "babel-runtime": "^6.22.0",
594
- "babel-template": "^6.24.1"
595
- }
596
- },
597
- "babel-plugin-transform-es2015-destructuring": {
598
- "version": "6.23.0",
599
- "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz",
600
- "integrity": "sha1-mXux8auWf2gtKwh2/jWNYOdlxW0=",
601
- "dev": true,
602
- "requires": {
603
- "babel-runtime": "^6.22.0"
604
- }
605
- },
606
- "babel-plugin-transform-es2015-duplicate-keys": {
607
- "version": "6.24.1",
608
- "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz",
609
- "integrity": "sha1-c+s9MQypaePvnskcU3QabxV2Qj4=",
610
- "dev": true,
611
- "requires": {
612
- "babel-runtime": "^6.22.0",
613
- "babel-types": "^6.24.1"
614
- }
615
- },
616
- "babel-plugin-transform-es2015-for-of": {
617
- "version": "6.23.0",
618
- "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz",
619
- "integrity": "sha1-9HyVsrYT3x0+zC/bdXNiPHUkhpE=",
620
- "dev": true,
621
- "requires": {
622
- "babel-runtime": "^6.22.0"
623
- }
624
- },
625
- "babel-plugin-transform-es2015-function-name": {
626
- "version": "6.24.1",
627
- "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz",
628
- "integrity": "sha1-g0yJhTvDaxrw86TF26qU/Y6sqos=",
629
- "dev": true,
630
- "requires": {
631
- "babel-helper-function-name": "^6.24.1",
632
- "babel-runtime": "^6.22.0",
633
- "babel-types": "^6.24.1"
634
- }
635
- },
636
- "babel-plugin-transform-es2015-literals": {
637
- "version": "6.22.0",
638
- "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz",
639
- "integrity": "sha1-T1SgLWzWbPkVKAAZox0xklN3yi4=",
640
- "dev": true,
641
- "requires": {
642
- "babel-runtime": "^6.22.0"
643
- }
644
- },
645
- "babel-plugin-transform-es2015-modules-amd": {
646
- "version": "6.24.1",
647
- "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz",
648
- "integrity": "sha1-Oz5UAXI5hC1tGcMBHEvS8AoA0VQ=",
649
- "dev": true,
650
- "requires": {
651
- "babel-plugin-transform-es2015-modules-commonjs": "^6.24.1",
652
- "babel-runtime": "^6.22.0",
653
- "babel-template": "^6.24.1"
654
- }
655
- },
656
- "babel-plugin-transform-es2015-modules-commonjs": {
657
- "version": "6.26.0",
658
- "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.0.tgz",
659
- "integrity": "sha1-DYOUApt9xqvhqX7xgeAHWN0uXYo=",
660
- "dev": true,
661
- "requires": {
662
- "babel-plugin-transform-strict-mode": "^6.24.1",
663
- "babel-runtime": "^6.26.0",
664
- "babel-template": "^6.26.0",
665
- "babel-types": "^6.26.0"
666
- }
667
- },
668
- "babel-plugin-transform-es2015-modules-systemjs": {
669
- "version": "6.24.1",
670
- "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz",
671
- "integrity": "sha1-/4mhQrkRmpBhlfXxBuzzBdlAfSM=",
672
- "dev": true,
673
- "requires": {
674
- "babel-helper-hoist-variables": "^6.24.1",
675
- "babel-runtime": "^6.22.0",
676
- "babel-template": "^6.24.1"
677
- }
678
- },
679
- "babel-plugin-transform-es2015-modules-umd": {
680
- "version": "6.24.1",
681
- "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz",
682
- "integrity": "sha1-rJl+YoXNGO1hdq22B9YCNErThGg=",
683
- "dev": true,
684
- "requires": {
685
- "babel-plugin-transform-es2015-modules-amd": "^6.24.1",
686
- "babel-runtime": "^6.22.0",
687
- "babel-template": "^6.24.1"
688
- }
689
- },
690
- "babel-plugin-transform-es2015-object-super": {
691
- "version": "6.24.1",
692
- "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz",
693
- "integrity": "sha1-JM72muIcuDp/hgPa0CH1cusnj40=",
694
- "dev": true,
695
- "requires": {
696
- "babel-helper-replace-supers": "^6.24.1",
697
- "babel-runtime": "^6.22.0"
698
- }
699
- },
700
- "babel-plugin-transform-es2015-parameters": {
701
- "version": "6.24.1",
702
- "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz",
703
- "integrity": "sha1-V6w1GrScrxSpfNE7CfZv3wpiXys=",
704
- "dev": true,
705
- "requires": {
706
- "babel-helper-call-delegate": "^6.24.1",
707
- "babel-helper-get-function-arity": "^6.24.1",
708
- "babel-runtime": "^6.22.0",
709
- "babel-template": "^6.24.1",
710
- "babel-traverse": "^6.24.1",
711
- "babel-types": "^6.24.1"
712
- }
713
- },
714
- "babel-plugin-transform-es2015-shorthand-properties": {
715
- "version": "6.24.1",
716
- "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz",
717
- "integrity": "sha1-JPh11nIch2YbvZmkYi5R8U3jiqA=",
718
- "dev": true,
719
- "requires": {
720
- "babel-runtime": "^6.22.0",
721
- "babel-types": "^6.24.1"
722
- }
723
- },
724
- "babel-plugin-transform-es2015-spread": {
725
- "version": "6.22.0",
726
- "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz",
727
- "integrity": "sha1-1taKmfia7cRTbIGlQujdnxdG+NE=",
728
- "dev": true,
729
- "requires": {
730
- "babel-runtime": "^6.22.0"
731
- }
732
- },
733
- "babel-plugin-transform-es2015-sticky-regex": {
734
- "version": "6.24.1",
735
- "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz",
736
- "integrity": "sha1-AMHNsaynERLN8M9hJsLta0V8zbw=",
737
- "dev": true,
738
- "requires": {
739
- "babel-helper-regex": "^6.24.1",
740
- "babel-runtime": "^6.22.0",
741
- "babel-types": "^6.24.1"
742
- }
743
- },
744
- "babel-plugin-transform-es2015-template-literals": {
745
- "version": "6.22.0",
746
- "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz",
747
- "integrity": "sha1-qEs0UPfp+PH2g51taH2oS7EjbY0=",
748
- "dev": true,
749
- "requires": {
750
- "babel-runtime": "^6.22.0"
751
- }
752
- },
753
- "babel-plugin-transform-es2015-typeof-symbol": {
754
- "version": "6.23.0",
755
- "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz",
756
- "integrity": "sha1-3sCfHN3/lLUqxz1QXITfWdzOs3I=",
757
- "dev": true,
758
- "requires": {
759
- "babel-runtime": "^6.22.0"
760
- }
761
- },
762
- "babel-plugin-transform-es2015-unicode-regex": {
763
- "version": "6.24.1",
764
- "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz",
765
- "integrity": "sha1-04sS9C6nMj9yk4fxinxa4frrNek=",
766
- "dev": true,
767
- "requires": {
768
- "babel-helper-regex": "^6.24.1",
769
- "babel-runtime": "^6.22.0",
770
- "regexpu-core": "^2.0.0"
771
- }
772
- },
773
- "babel-plugin-transform-exponentiation-operator": {
774
- "version": "6.24.1",
775
- "resolved": "https://registry.npmjs.org/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.24.1.tgz",
776
- "integrity": "sha1-KrDJx/MJj6SJB3cruBP+QejeOg4=",
777
- "dev": true,
778
- "requires": {
779
- "babel-helper-builder-binary-assignment-operator-visitor": "^6.24.1",
780
- "babel-plugin-syntax-exponentiation-operator": "^6.8.0",
781
- "babel-runtime": "^6.22.0"
782
- }
783
- },
784
- "babel-plugin-transform-regenerator": {
785
- "version": "6.26.0",
786
- "resolved": "https://registry.npmjs.org/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz",
787
- "integrity": "sha1-4HA2lvveJ/Cj78rPi03KL3s6jy8=",
788
- "dev": true,
789
- "requires": {
790
- "regenerator-transform": "^0.10.0"
791
- }
792
- },
793
- "babel-plugin-transform-strict-mode": {
794
- "version": "6.24.1",
795
- "resolved": "https://registry.npmjs.org/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz",
796
- "integrity": "sha1-1fr3qleKZbvlkc9e2uBKDGcCB1g=",
797
- "dev": true,
798
- "requires": {
799
- "babel-runtime": "^6.22.0",
800
- "babel-types": "^6.24.1"
801
- }
802
- },
803
- "babel-preset-env": {
804
- "version": "1.6.1",
805
- "resolved": "https://registry.npmjs.org/babel-preset-env/-/babel-preset-env-1.6.1.tgz",
806
- "integrity": "sha512-W6VIyA6Ch9ePMI7VptNn2wBM6dbG0eSz25HEiL40nQXCsXGTGZSTZu1Iap+cj3Q0S5a7T9+529l/5Bkvd+afNA==",
807
- "dev": true,
808
- "requires": {
809
- "babel-plugin-check-es2015-constants": "^6.22.0",
810
- "babel-plugin-syntax-trailing-function-commas": "^6.22.0",
811
- "babel-plugin-transform-async-to-generator": "^6.22.0",
812
- "babel-plugin-transform-es2015-arrow-functions": "^6.22.0",
813
- "babel-plugin-transform-es2015-block-scoped-functions": "^6.22.0",
814
- "babel-plugin-transform-es2015-block-scoping": "^6.23.0",
815
- "babel-plugin-transform-es2015-classes": "^6.23.0",
816
- "babel-plugin-transform-es2015-computed-properties": "^6.22.0",
817
- "babel-plugin-transform-es2015-destructuring": "^6.23.0",
818
- "babel-plugin-transform-es2015-duplicate-keys": "^6.22.0",
819
- "babel-plugin-transform-es2015-for-of": "^6.23.0",
820
- "babel-plugin-transform-es2015-function-name": "^6.22.0",
821
- "babel-plugin-transform-es2015-literals": "^6.22.0",
822
- "babel-plugin-transform-es2015-modules-amd": "^6.22.0",
823
- "babel-plugin-transform-es2015-modules-commonjs": "^6.23.0",
824
- "babel-plugin-transform-es2015-modules-systemjs": "^6.23.0",
825
- "babel-plugin-transform-es2015-modules-umd": "^6.23.0",
826
- "babel-plugin-transform-es2015-object-super": "^6.22.0",
827
- "babel-plugin-transform-es2015-parameters": "^6.23.0",
828
- "babel-plugin-transform-es2015-shorthand-properties": "^6.22.0",
829
- "babel-plugin-transform-es2015-spread": "^6.22.0",
830
- "babel-plugin-transform-es2015-sticky-regex": "^6.22.0",
831
- "babel-plugin-transform-es2015-template-literals": "^6.22.0",
832
- "babel-plugin-transform-es2015-typeof-symbol": "^6.23.0",
833
- "babel-plugin-transform-es2015-unicode-regex": "^6.22.0",
834
- "babel-plugin-transform-exponentiation-operator": "^6.22.0",
835
- "babel-plugin-transform-regenerator": "^6.22.0",
836
- "browserslist": "^2.1.2",
837
- "invariant": "^2.2.2",
838
- "semver": "^5.3.0"
839
- }
840
- },
841
- "babel-register": {
842
- "version": "6.26.0",
843
- "resolved": "https://registry.npmjs.org/babel-register/-/babel-register-6.26.0.tgz",
844
- "integrity": "sha1-btAhFz4vy0htestFxgCahW9kcHE=",
845
- "dev": true,
846
- "requires": {
847
- "babel-core": "^6.26.0",
848
- "babel-runtime": "^6.26.0",
849
- "core-js": "^2.5.0",
850
- "home-or-tmp": "^2.0.0",
851
- "lodash": "^4.17.4",
852
- "mkdirp": "^0.5.1",
853
- "source-map-support": "^0.4.15"
854
- }
855
- },
856
- "babel-runtime": {
857
- "version": "6.26.0",
858
- "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
859
- "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
860
- "dev": true,
861
- "requires": {
862
- "core-js": "^2.4.0",
863
- "regenerator-runtime": "^0.11.0"
864
- }
865
- },
866
- "babel-template": {
867
- "version": "6.26.0",
868
- "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz",
869
- "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=",
870
- "dev": true,
871
- "requires": {
872
- "babel-runtime": "^6.26.0",
873
- "babel-traverse": "^6.26.0",
874
- "babel-types": "^6.26.0",
875
- "babylon": "^6.18.0",
876
- "lodash": "^4.17.4"
877
- }
878
- },
879
- "babel-traverse": {
880
- "version": "6.26.0",
881
- "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz",
882
- "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=",
883
- "dev": true,
884
- "requires": {
885
- "babel-code-frame": "^6.26.0",
886
- "babel-messages": "^6.23.0",
887
- "babel-runtime": "^6.26.0",
888
- "babel-types": "^6.26.0",
889
- "babylon": "^6.18.0",
890
- "debug": "^2.6.8",
891
- "globals": "^9.18.0",
892
- "invariant": "^2.2.2",
893
- "lodash": "^4.17.4"
894
- }
895
- },
896
- "babel-types": {
897
- "version": "6.26.0",
898
- "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz",
899
- "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=",
900
- "dev": true,
901
- "requires": {
902
- "babel-runtime": "^6.26.0",
903
- "esutils": "^2.0.2",
904
- "lodash": "^4.17.4",
905
- "to-fast-properties": "^1.0.3"
906
- }
907
- },
908
- "babylon": {
909
- "version": "6.18.0",
910
- "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz",
911
- "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==",
912
- "dev": true
913
- },
914
- "balanced-match": {
915
- "version": "1.0.0",
916
- "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
917
- "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
918
- "dev": true
919
- },
920
- "base64-js": {
921
- "version": "1.2.1",
922
- "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.1.tgz",
923
- "integrity": "sha512-dwVUVIXsBZXwTuwnXI9RK8sBmgq09NDHzyR9SAph9eqk76gKK2JSQmZARC2zRC81JC2QTtxD0ARU5qTS25gIGw==",
924
- "dev": true
925
- },
926
- "bcrypt-pbkdf": {
927
- "version": "1.0.1",
928
- "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz",
929
- "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=",
930
- "dev": true,
931
- "optional": true,
932
- "requires": {
933
- "tweetnacl": "^0.14.3"
934
- }
935
- },
936
- "big.js": {
937
- "version": "3.2.0",
938
- "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz",
939
- "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==",
940
- "dev": true
941
- },
942
- "binary-extensions": {
943
- "version": "1.11.0",
944
- "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.11.0.tgz",
945
- "integrity": "sha1-RqoXUftqL5PuXmibsQh9SxTGwgU=",
946
- "dev": true
947
- },
948
- "block-stream": {
949
- "version": "0.0.9",
950
- "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz",
951
- "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=",
952
- "dev": true,
953
- "requires": {
954
- "inherits": "~2.0.0"
955
- }
956
- },
957
- "bn.js": {
958
- "version": "4.11.8",
959
- "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz",
960
- "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==",
961
- "dev": true
962
- },
963
- "boom": {
964
- "version": "2.10.1",
965
- "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz",
966
- "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=",
967
- "dev": true,
968
- "requires": {
969
- "hoek": "2.x.x"
970
- }
971
- },
972
- "brace-expansion": {
973
- "version": "1.1.8",
974
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz",
975
- "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=",
976
- "dev": true,
977
- "requires": {
978
- "balanced-match": "^1.0.0",
979
- "concat-map": "0.0.1"
980
- }
981
- },
982
- "braces": {
983
- "version": "1.8.5",
984
- "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz",
985
- "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=",
986
- "dev": true,
987
- "requires": {
988
- "expand-range": "^1.8.1",
989
- "preserve": "^0.2.0",
990
- "repeat-element": "^1.1.2"
991
- }
992
- },
993
- "brorand": {
994
- "version": "1.1.0",
995
- "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz",
996
- "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=",
997
- "dev": true
998
- },
999
- "browserify-aes": {
1000
- "version": "1.1.1",
1001
- "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.1.1.tgz",
1002
- "integrity": "sha512-UGnTYAnB2a3YuYKIRy1/4FB2HdM866E0qC46JXvVTYKlBlZlnvfpSfY6OKfXZAkv70eJ2a1SqzpAo5CRhZGDFg==",
1003
- "dev": true,
1004
- "requires": {
1005
- "buffer-xor": "^1.0.3",
1006
- "cipher-base": "^1.0.0",
1007
- "create-hash": "^1.1.0",
1008
- "evp_bytestokey": "^1.0.3",
1009
- "inherits": "^2.0.1",
1010
- "safe-buffer": "^5.0.1"
1011
- }
1012
- },
1013
- "browserify-cipher": {
1014
- "version": "1.0.0",
1015
- "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.0.tgz",
1016
- "integrity": "sha1-mYgkSHS/XtTijalWZtzWasj8Njo=",
1017
- "dev": true,
1018
- "requires": {
1019
- "browserify-aes": "^1.0.4",
1020
- "browserify-des": "^1.0.0",
1021
- "evp_bytestokey": "^1.0.0"
1022
- }
1023
- },
1024
- "browserify-des": {
1025
- "version": "1.0.0",
1026
- "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.0.tgz",
1027
- "integrity": "sha1-2qJ3cXRwki7S/hhZQRihdUOXId0=",
1028
- "dev": true,
1029
- "requires": {
1030
- "cipher-base": "^1.0.1",
1031
- "des.js": "^1.0.0",
1032
- "inherits": "^2.0.1"
1033
- }
1034
- },
1035
- "browserify-rsa": {
1036
- "version": "4.0.1",
1037
- "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz",
1038
- "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=",
1039
- "dev": true,
1040
- "requires": {
1041
- "bn.js": "^4.1.0",
1042
- "randombytes": "^2.0.1"
1043
- }
1044
- },
1045
- "browserify-sign": {
1046
- "version": "4.0.4",
1047
- "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz",
1048
- "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=",
1049
- "dev": true,
1050
- "requires": {
1051
- "bn.js": "^4.1.1",
1052
- "browserify-rsa": "^4.0.0",
1053
- "create-hash": "^1.1.0",
1054
- "create-hmac": "^1.1.2",
1055
- "elliptic": "^6.0.0",
1056
- "inherits": "^2.0.1",
1057
- "parse-asn1": "^5.0.0"
1058
- }
1059
- },
1060
- "browserify-zlib": {
1061
- "version": "0.2.0",
1062
- "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz",
1063
- "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==",
1064
- "dev": true,
1065
- "requires": {
1066
- "pako": "~1.0.5"
1067
- }
1068
- },
1069
- "browserslist": {
1070
- "version": "2.9.1",
1071
- "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-2.9.1.tgz",
1072
- "integrity": "sha512-3n3nPdbUqn3nWmsy4PeSQthz2ja1ndpoXta+dwFFNhveGjMg6FXpWYe12vsTpNoXJbzx3j7GZXdtoVIdvh3JbA==",
1073
- "dev": true,
1074
- "requires": {
1075
- "caniuse-lite": "^1.0.30000770",
1076
- "electron-to-chromium": "^1.3.27"
1077
- }
1078
- },
1079
- "buffer": {
1080
- "version": "4.9.1",
1081
- "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz",
1082
- "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=",
1083
- "dev": true,
1084
- "requires": {
1085
- "base64-js": "^1.0.2",
1086
- "ieee754": "^1.1.4",
1087
- "isarray": "^1.0.0"
1088
- }
1089
- },
1090
- "buffer-xor": {
1091
- "version": "1.0.3",
1092
- "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz",
1093
- "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=",
1094
- "dev": true
1095
- },
1096
- "builtin-modules": {
1097
- "version": "1.1.1",
1098
- "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz",
1099
- "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=",
1100
- "dev": true
1101
- },
1102
- "builtin-status-codes": {
1103
- "version": "3.0.0",
1104
- "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz",
1105
- "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=",
1106
- "dev": true
1107
- },
1108
- "caller-path": {
1109
- "version": "0.1.0",
1110
- "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz",
1111
- "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=",
1112
- "dev": true,
1113
- "requires": {
1114
- "callsites": "^0.2.0"
1115
- }
1116
- },
1117
- "callsites": {
1118
- "version": "0.2.0",
1119
- "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz",
1120
- "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=",
1121
- "dev": true
1122
- },
1123
- "camelcase": {
1124
- "version": "2.1.1",
1125
- "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz",
1126
- "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=",
1127
- "dev": true
1128
- },
1129
- "camelcase-keys": {
1130
- "version": "2.1.0",
1131
- "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz",
1132
- "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=",
1133
- "dev": true,
1134
- "requires": {
1135
- "camelcase": "^2.0.0",
1136
- "map-obj": "^1.0.0"
1137
- }
1138
- },
1139
- "caniuse-api": {
1140
- "version": "1.6.1",
1141
- "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-1.6.1.tgz",
1142
- "integrity": "sha1-tTTnxzTE+B7F++isoq0kNUuWLGw=",
1143
- "dev": true,
1144
- "requires": {
1145
- "browserslist": "^1.3.6",
1146
- "caniuse-db": "^1.0.30000529",
1147
- "lodash.memoize": "^4.1.2",
1148
- "lodash.uniq": "^4.5.0"
1149
- },
1150
- "dependencies": {
1151
- "browserslist": {
1152
- "version": "1.7.7",
1153
- "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz",
1154
- "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=",
1155
- "dev": true,
1156
- "requires": {
1157
- "caniuse-db": "^1.0.30000639",
1158
- "electron-to-chromium": "^1.2.7"
1159
- }
1160
- }
1161
- }
1162
- },
1163
- "caniuse-db": {
1164
- "version": "1.0.30000770",
1165
- "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000770.tgz",
1166
- "integrity": "sha1-z2iuHLioL208Nd9Bxi3Glz5HAkQ=",
1167
- "dev": true
1168
- },
1169
- "caniuse-lite": {
1170
- "version": "1.0.30000770",
1171
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000770.tgz",
1172
- "integrity": "sha1-vI5/ULBzJzOQ22qzVzeJCaFOm9s=",
1173
- "dev": true
1174
- },
1175
- "caseless": {
1176
- "version": "0.11.0",
1177
- "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.11.0.tgz",
1178
- "integrity": "sha1-cVuW6phBWTzDMGeSP17GDr2k99c=",
1179
- "dev": true
1180
- },
1181
- "center-align": {
1182
- "version": "0.1.3",
1183
- "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz",
1184
- "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=",
1185
- "dev": true,
1186
- "requires": {
1187
- "align-text": "^0.1.3",
1188
- "lazy-cache": "^1.0.3"
1189
- },
1190
- "dependencies": {
1191
- "lazy-cache": {
1192
- "version": "1.0.4",
1193
- "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz",
1194
- "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=",
1195
- "dev": true
1196
- }
1197
- }
1198
- },
1199
- "chalk": {
1200
- "version": "1.1.3",
1201
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
1202
- "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
1203
- "dev": true,
1204
- "requires": {
1205
- "ansi-styles": "^2.2.1",
1206
- "escape-string-regexp": "^1.0.2",
1207
- "has-ansi": "^2.0.0",
1208
- "strip-ansi": "^3.0.0",
1209
- "supports-color": "^2.0.0"
1210
- }
1211
- },
1212
- "chokidar": {
1213
- "version": "1.7.0",
1214
- "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz",
1215
- "integrity": "sha1-eY5ol3gVHIB2tLNg5e3SjNortGg=",
1216
- "dev": true,
1217
- "requires": {
1218
- "anymatch": "^1.3.0",
1219
- "async-each": "^1.0.0",
1220
- "fsevents": "^1.0.0",
1221
- "glob-parent": "^2.0.0",
1222
- "inherits": "^2.0.1",
1223
- "is-binary-path": "^1.0.0",
1224
- "is-glob": "^2.0.0",
1225
- "path-is-absolute": "^1.0.0",
1226
- "readdirp": "^2.0.0"
1227
- }
1228
- },
1229
- "cipher-base": {
1230
- "version": "1.0.4",
1231
- "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz",
1232
- "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==",
1233
- "dev": true,
1234
- "requires": {
1235
- "inherits": "^2.0.1",
1236
- "safe-buffer": "^5.0.1"
1237
- }
1238
- },
1239
- "circular-json": {
1240
- "version": "0.3.3",
1241
- "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz",
1242
- "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==",
1243
- "dev": true
1244
- },
1245
- "clap": {
1246
- "version": "1.2.3",
1247
- "resolved": "https://registry.npmjs.org/clap/-/clap-1.2.3.tgz",
1248
- "integrity": "sha512-4CoL/A3hf90V3VIEjeuhSvlGFEHKzOz+Wfc2IVZc+FaUgU0ZQafJTP49fvnULipOPcAfqhyI2duwQyns6xqjYA==",
1249
- "dev": true,
1250
- "requires": {
1251
- "chalk": "^1.1.3"
1252
- }
1253
- },
1254
- "cli-cursor": {
1255
- "version": "1.0.2",
1256
- "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz",
1257
- "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=",
1258
- "dev": true,
1259
- "requires": {
1260
- "restore-cursor": "^1.0.1"
1261
- }
1262
- },
1263
- "cli-width": {
1264
- "version": "2.2.0",
1265
- "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz",
1266
- "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=",
1267
- "dev": true
1268
- },
1269
- "cliui": {
1270
- "version": "3.2.0",
1271
- "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz",
1272
- "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=",
1273
- "dev": true,
1274
- "requires": {
1275
- "string-width": "^1.0.1",
1276
- "strip-ansi": "^3.0.1",
1277
- "wrap-ansi": "^2.0.0"
1278
- }
1279
- },
1280
- "clone": {
1281
- "version": "1.0.3",
1282
- "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.3.tgz",
1283
- "integrity": "sha1-KY1+IjFmD0DAA8LtMUDezz9TCF8=",
1284
- "dev": true
1285
- },
1286
- "clone-deep": {
1287
- "version": "0.3.0",
1288
- "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-0.3.0.tgz",
1289
- "integrity": "sha1-NIxhrpzb4O3+BT2R/0zFIdeQ7eg=",
1290
- "dev": true,
1291
- "requires": {
1292
- "for-own": "^1.0.0",
1293
- "is-plain-object": "^2.0.1",
1294
- "kind-of": "^3.2.2",
1295
- "shallow-clone": "^0.1.2"
1296
- }
1297
- },
1298
- "co": {
1299
- "version": "4.6.0",
1300
- "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
1301
- "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=",
1302
- "dev": true
1303
- },
1304
- "coa": {
1305
- "version": "1.0.4",
1306
- "resolved": "https://registry.npmjs.org/coa/-/coa-1.0.4.tgz",
1307
- "integrity": "sha1-qe8VNmDWqGqL3sAomlxoTSF0Mv0=",
1308
- "dev": true,
1309
- "requires": {
1310
- "q": "^1.1.2"
1311
- }
1312
- },
1313
- "code-point-at": {
1314
- "version": "1.1.0",
1315
- "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
1316
- "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
1317
- "dev": true
1318
- },
1319
- "color": {
1320
- "version": "0.11.4",
1321
- "resolved": "https://registry.npmjs.org/color/-/color-0.11.4.tgz",
1322
- "integrity": "sha1-bXtcdPtl6EHNSHkq0e1eB7kE12Q=",
1323
- "dev": true,
1324
- "requires": {
1325
- "clone": "^1.0.2",
1326
- "color-convert": "^1.3.0",
1327
- "color-string": "^0.3.0"
1328
- }
1329
- },
1330
- "color-convert": {
1331
- "version": "1.9.1",
1332
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz",
1333
- "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==",
1334
- "dev": true,
1335
- "requires": {
1336
- "color-name": "^1.1.1"
1337
- }
1338
- },
1339
- "color-name": {
1340
- "version": "1.1.3",
1341
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
1342
- "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
1343
- "dev": true
1344
- },
1345
- "color-string": {
1346
- "version": "0.3.0",
1347
- "resolved": "https://registry.npmjs.org/color-string/-/color-string-0.3.0.tgz",
1348
- "integrity": "sha1-J9RvtnAlxcL6JZk7+/V55HhBuZE=",
1349
- "dev": true,
1350
- "requires": {
1351
- "color-name": "^1.0.0"
1352
- }
1353
- },
1354
- "colormin": {
1355
- "version": "1.1.2",
1356
- "resolved": "https://registry.npmjs.org/colormin/-/colormin-1.1.2.tgz",
1357
- "integrity": "sha1-6i90IKcrlogaOKrlnsEkpvcpgTM=",
1358
- "dev": true,
1359
- "requires": {
1360
- "color": "^0.11.0",
1361
- "css-color-names": "0.0.4",
1362
- "has": "^1.0.1"
1363
- }
1364
- },
1365
- "colors": {
1366
- "version": "1.1.2",
1367
- "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz",
1368
- "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=",
1369
- "dev": true
1370
- },
1371
- "combined-stream": {
1372
- "version": "1.0.5",
1373
- "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz",
1374
- "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=",
1375
- "dev": true,
1376
- "requires": {
1377
- "delayed-stream": "~1.0.0"
1378
- }
1379
- },
1380
- "commander": {
1381
- "version": "2.12.1",
1382
- "resolved": "https://registry.npmjs.org/commander/-/commander-2.12.1.tgz",
1383
- "integrity": "sha512-PCNLExLlI5HiPdaJs4pMXwOTHkSCpNQ1QJH9ykZLKtKEyKu3p9HgmH5l97vM8c0IUz6d54l+xEu2GG9yuYrFzA==",
1384
- "dev": true
1385
- },
1386
- "commondir": {
1387
- "version": "1.0.1",
1388
- "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
1389
- "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=",
1390
- "dev": true
1391
- },
1392
- "concat-map": {
1393
- "version": "0.0.1",
1394
- "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
1395
- "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
1396
- "dev": true
1397
- },
1398
- "concat-stream": {
1399
- "version": "1.6.0",
1400
- "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.0.tgz",
1401
- "integrity": "sha1-CqxmL9Ur54lk1VMvaUeE5wEQrPc=",
1402
- "dev": true,
1403
- "requires": {
1404
- "inherits": "^2.0.3",
1405
- "readable-stream": "^2.2.2",
1406
- "typedarray": "^0.0.6"
1407
- }
1408
- },
1409
- "console-browserify": {
1410
- "version": "1.1.0",
1411
- "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz",
1412
- "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=",
1413
- "dev": true,
1414
- "requires": {
1415
- "date-now": "^0.1.4"
1416
- }
1417
- },
1418
- "console-control-strings": {
1419
- "version": "1.1.0",
1420
- "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
1421
- "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=",
1422
- "dev": true
1423
- },
1424
- "constants-browserify": {
1425
- "version": "1.0.0",
1426
- "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz",
1427
- "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=",
1428
- "dev": true
1429
- },
1430
- "convert-source-map": {
1431
- "version": "1.5.1",
1432
- "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.5.1.tgz",
1433
- "integrity": "sha1-uCeAl7m8IpNl3lxiz1/K7YtVmeU=",
1434
- "dev": true
1435
- },
1436
- "core-js": {
1437
- "version": "2.5.1",
1438
- "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.1.tgz",
1439
- "integrity": "sha1-rmh03GaTd4m4B1T/VCjfZoGcpQs=",
1440
- "dev": true
1441
- },
1442
- "core-util-is": {
1443
- "version": "1.0.2",
1444
- "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
1445
- "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
1446
- "dev": true
1447
- },
1448
- "create-ecdh": {
1449
- "version": "4.0.0",
1450
- "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.0.tgz",
1451
- "integrity": "sha1-iIxyNZbN92EvZJgjPuvXo1MBc30=",
1452
- "dev": true,
1453
- "requires": {
1454
- "bn.js": "^4.1.0",
1455
- "elliptic": "^6.0.0"
1456
- }
1457
- },
1458
- "create-hash": {
1459
- "version": "1.1.3",
1460
- "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.1.3.tgz",
1461
- "integrity": "sha1-YGBCrIuSYnUPSDyt2rD1gZFy2P0=",
1462
- "dev": true,
1463
- "requires": {
1464
- "cipher-base": "^1.0.1",
1465
- "inherits": "^2.0.1",
1466
- "ripemd160": "^2.0.0",
1467
- "sha.js": "^2.4.0"
1468
- }
1469
- },
1470
- "create-hmac": {
1471
- "version": "1.1.6",
1472
- "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.6.tgz",
1473
- "integrity": "sha1-rLniIaThe9sHbpBlfEK5PjcmzwY=",
1474
- "dev": true,
1475
- "requires": {
1476
- "cipher-base": "^1.0.3",
1477
- "create-hash": "^1.1.0",
1478
- "inherits": "^2.0.1",
1479
- "ripemd160": "^2.0.0",
1480
- "safe-buffer": "^5.0.1",
1481
- "sha.js": "^2.4.8"
1482
- }
1483
- },
1484
- "cross-spawn": {
1485
- "version": "3.0.1",
1486
- "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-3.0.1.tgz",
1487
- "integrity": "sha1-ElYDfsufDF9549bvE14wdwGEuYI=",
1488
- "dev": true,
1489
- "requires": {
1490
- "lru-cache": "^4.0.1",
1491
- "which": "^1.2.9"
1492
- }
1493
- },
1494
- "cryptiles": {
1495
- "version": "2.0.5",
1496
- "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz",
1497
- "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=",
1498
- "dev": true,
1499
- "requires": {
1500
- "boom": "2.x.x"
1501
- }
1502
- },
1503
- "crypto-browserify": {
1504
- "version": "3.12.0",
1505
- "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz",
1506
- "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==",
1507
- "dev": true,
1508
- "requires": {
1509
- "browserify-cipher": "^1.0.0",
1510
- "browserify-sign": "^4.0.0",
1511
- "create-ecdh": "^4.0.0",
1512
- "create-hash": "^1.1.0",
1513
- "create-hmac": "^1.1.0",
1514
- "diffie-hellman": "^5.0.0",
1515
- "inherits": "^2.0.1",
1516
- "pbkdf2": "^3.0.3",
1517
- "public-encrypt": "^4.0.0",
1518
- "randombytes": "^2.0.0",
1519
- "randomfill": "^1.0.3"
1520
- }
1521
- },
1522
- "css-color-names": {
1523
- "version": "0.0.4",
1524
- "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz",
1525
- "integrity": "sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=",
1526
- "dev": true
1527
- },
1528
- "css-loader": {
1529
- "version": "0.28.7",
1530
- "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-0.28.7.tgz",
1531
- "integrity": "sha512-GxMpax8a/VgcfRrVy0gXD6yLd5ePYbXX/5zGgTVYp4wXtJklS8Z2VaUArJgc//f6/Dzil7BaJObdSv8eKKCPgg==",
1532
- "dev": true,
1533
- "requires": {
1534
- "babel-code-frame": "^6.11.0",
1535
- "css-selector-tokenizer": "^0.7.0",
1536
- "cssnano": ">=2.6.1 <4",
1537
- "icss-utils": "^2.1.0",
1538
- "loader-utils": "^1.0.2",
1539
- "lodash.camelcase": "^4.3.0",
1540
- "object-assign": "^4.0.1",
1541
- "postcss": "^5.0.6",
1542
- "postcss-modules-extract-imports": "^1.0.0",
1543
- "postcss-modules-local-by-default": "^1.0.1",
1544
- "postcss-modules-scope": "^1.0.0",
1545
- "postcss-modules-values": "^1.1.0",
1546
- "postcss-value-parser": "^3.3.0",
1547
- "source-list-map": "^2.0.0"
1548
- }
1549
- },
1550
- "css-selector-tokenizer": {
1551
- "version": "0.7.0",
1552
- "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.0.tgz",
1553
- "integrity": "sha1-5piEdK6MlTR3v15+/s/OzNnPTIY=",
1554
- "dev": true,
1555
- "requires": {
1556
- "cssesc": "^0.1.0",
1557
- "fastparse": "^1.1.1",
1558
- "regexpu-core": "^1.0.0"
1559
- },
1560
- "dependencies": {
1561
- "regexpu-core": {
1562
- "version": "1.0.0",
1563
- "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-1.0.0.tgz",
1564
- "integrity": "sha1-hqdj9Y7k18L2sQLkdkBQ3n7ZDGs=",
1565
- "dev": true,
1566
- "requires": {
1567
- "regenerate": "^1.2.1",
1568
- "regjsgen": "^0.2.0",
1569
- "regjsparser": "^0.1.4"
1570
- }
1571
- }
1572
- }
1573
- },
1574
- "cssesc": {
1575
- "version": "0.1.0",
1576
- "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-0.1.0.tgz",
1577
- "integrity": "sha1-yBSQPkViM3GgR3tAEJqq++6t27Q=",
1578
- "dev": true
1579
- },
1580
- "cssnano": {
1581
- "version": "3.10.0",
1582
- "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-3.10.0.tgz",
1583
- "integrity": "sha1-Tzj2zqK5sX+gFJDyPx3GjqZcHDg=",
1584
- "dev": true,
1585
- "requires": {
1586
- "autoprefixer": "^6.3.1",
1587
- "decamelize": "^1.1.2",
1588
- "defined": "^1.0.0",
1589
- "has": "^1.0.1",
1590
- "object-assign": "^4.0.1",
1591
- "postcss": "^5.0.14",
1592
- "postcss-calc": "^5.2.0",
1593
- "postcss-colormin": "^2.1.8",
1594
- "postcss-convert-values": "^2.3.4",
1595
- "postcss-discard-comments": "^2.0.4",
1596
- "postcss-discard-duplicates": "^2.0.1",
1597
- "postcss-discard-empty": "^2.0.1",
1598
- "postcss-discard-overridden": "^0.1.1",
1599
- "postcss-discard-unused": "^2.2.1",
1600
- "postcss-filter-plugins": "^2.0.0",
1601
- "postcss-merge-idents": "^2.1.5",
1602
- "postcss-merge-longhand": "^2.0.1",
1603
- "postcss-merge-rules": "^2.0.3",
1604
- "postcss-minify-font-values": "^1.0.2",
1605
- "postcss-minify-gradients": "^1.0.1",
1606
- "postcss-minify-params": "^1.0.4",
1607
- "postcss-minify-selectors": "^2.0.4",
1608
- "postcss-normalize-charset": "^1.1.0",
1609
- "postcss-normalize-url": "^3.0.7",
1610
- "postcss-ordered-values": "^2.1.0",
1611
- "postcss-reduce-idents": "^2.2.2",
1612
- "postcss-reduce-initial": "^1.0.0",
1613
- "postcss-reduce-transforms": "^1.0.3",
1614
- "postcss-svgo": "^2.1.1",
1615
- "postcss-unique-selectors": "^2.0.2",
1616
- "postcss-value-parser": "^3.2.3",
1617
- "postcss-zindex": "^2.0.1"
1618
- }
1619
- },
1620
- "csso": {
1621
- "version": "2.3.2",
1622
- "resolved": "https://registry.npmjs.org/csso/-/csso-2.3.2.tgz",
1623
- "integrity": "sha1-3dUsWHAz9J6Utx/FVWnyUuj/X4U=",
1624
- "dev": true,
1625
- "requires": {
1626
- "clap": "^1.0.9",
1627
- "source-map": "^0.5.3"
1628
- }
1629
- },
1630
- "currently-unhandled": {
1631
- "version": "0.4.1",
1632
- "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz",
1633
- "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=",
1634
- "dev": true,
1635
- "requires": {
1636
- "array-find-index": "^1.0.1"
1637
- }
1638
- },
1639
- "d": {
1640
- "version": "1.0.0",
1641
- "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz",
1642
- "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=",
1643
- "dev": true,
1644
- "requires": {
1645
- "es5-ext": "^0.10.9"
1646
- }
1647
- },
1648
- "dashdash": {
1649
- "version": "1.14.1",
1650
- "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
1651
- "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
1652
- "dev": true,
1653
- "requires": {
1654
- "assert-plus": "^1.0.0"
1655
- },
1656
- "dependencies": {
1657
- "assert-plus": {
1658
- "version": "1.0.0",
1659
- "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
1660
- "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
1661
- "dev": true
1662
- }
1663
- }
1664
- },
1665
- "date-now": {
1666
- "version": "0.1.4",
1667
- "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz",
1668
- "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=",
1669
- "dev": true
1670
- },
1671
- "debug": {
1672
- "version": "2.6.9",
1673
- "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
1674
- "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
1675
- "dev": true,
1676
- "requires": {
1677
- "ms": "2.0.0"
1678
- }
1679
- },
1680
- "decamelize": {
1681
- "version": "1.2.0",
1682
- "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
1683
- "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
1684
- "dev": true
1685
- },
1686
- "deep-is": {
1687
- "version": "0.1.3",
1688
- "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
1689
- "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=",
1690
- "dev": true
1691
- },
1692
- "defined": {
1693
- "version": "1.0.0",
1694
- "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz",
1695
- "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=",
1696
- "dev": true
1697
- },
1698
- "del": {
1699
- "version": "2.2.2",
1700
- "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz",
1701
- "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=",
1702
- "dev": true,
1703
- "requires": {
1704
- "globby": "^5.0.0",
1705
- "is-path-cwd": "^1.0.0",
1706
- "is-path-in-cwd": "^1.0.0",
1707
- "object-assign": "^4.0.1",
1708
- "pify": "^2.0.0",
1709
- "pinkie-promise": "^2.0.0",
1710
- "rimraf": "^2.2.8"
1711
- },
1712
- "dependencies": {
1713
- "pify": {
1714
- "version": "2.3.0",
1715
- "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
1716
- "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
1717
- "dev": true
1718
- }
1719
- }
1720
- },
1721
- "delayed-stream": {
1722
- "version": "1.0.0",
1723
- "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
1724
- "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
1725
- "dev": true
1726
- },
1727
- "delegates": {
1728
- "version": "1.0.0",
1729
- "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
1730
- "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=",
1731
- "dev": true
1732
- },
1733
- "des.js": {
1734
- "version": "1.0.0",
1735
- "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz",
1736
- "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=",
1737
- "dev": true,
1738
- "requires": {
1739
- "inherits": "^2.0.1",
1740
- "minimalistic-assert": "^1.0.0"
1741
- }
1742
- },
1743
- "detect-indent": {
1744
- "version": "4.0.0",
1745
- "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz",
1746
- "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=",
1747
- "dev": true,
1748
- "requires": {
1749
- "repeating": "^2.0.0"
1750
- }
1751
- },
1752
- "diffie-hellman": {
1753
- "version": "5.0.2",
1754
- "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.2.tgz",
1755
- "integrity": "sha1-tYNXOScM/ias9jIJn97SoH8gnl4=",
1756
- "dev": true,
1757
- "requires": {
1758
- "bn.js": "^4.1.0",
1759
- "miller-rabin": "^4.0.0",
1760
- "randombytes": "^2.0.0"
1761
- }
1762
- },
1763
- "doctrine": {
1764
- "version": "2.0.0",
1765
- "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.0.0.tgz",
1766
- "integrity": "sha1-xz2NKQnSIpHhoAejlYBNqLZl/mM=",
1767
- "dev": true,
1768
- "requires": {
1769
- "esutils": "^2.0.2",
1770
- "isarray": "^1.0.0"
1771
- }
1772
- },
1773
- "domain-browser": {
1774
- "version": "1.1.7",
1775
- "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.1.7.tgz",
1776
- "integrity": "sha1-hnqksJP6oF8d4IwG9NeyH9+GmLw=",
1777
- "dev": true
1778
- },
1779
- "ecc-jsbn": {
1780
- "version": "0.1.1",
1781
- "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz",
1782
- "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=",
1783
- "dev": true,
1784
- "optional": true,
1785
- "requires": {
1786
- "jsbn": "~0.1.0"
1787
- }
1788
- },
1789
- "electron-to-chromium": {
1790
- "version": "1.3.27",
1791
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.27.tgz",
1792
- "integrity": "sha1-eOy4o5kGYYe7N07t412ccFZagD0=",
1793
- "dev": true
1794
- },
1795
- "elliptic": {
1796
- "version": "6.4.0",
1797
- "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.0.tgz",
1798
- "integrity": "sha1-ysmvh2LIWDYYcAPI3+GT5eLq5d8=",
1799
- "dev": true,
1800
- "requires": {
1801
- "bn.js": "^4.4.0",
1802
- "brorand": "^1.0.1",
1803
- "hash.js": "^1.0.0",
1804
- "hmac-drbg": "^1.0.0",
1805
- "inherits": "^2.0.1",
1806
- "minimalistic-assert": "^1.0.0",
1807
- "minimalistic-crypto-utils": "^1.0.0"
1808
- }
1809
- },
1810
- "emojis-list": {
1811
- "version": "2.1.0",
1812
- "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz",
1813
- "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=",
1814
- "dev": true
1815
- },
1816
- "enhanced-resolve": {
1817
- "version": "3.4.1",
1818
- "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-3.4.1.tgz",
1819
- "integrity": "sha1-BCHjOf1xQZs9oT0Smzl5BAIwR24=",
1820
- "dev": true,
1821
- "requires": {
1822
- "graceful-fs": "^4.1.2",
1823
- "memory-fs": "^0.4.0",
1824
- "object-assign": "^4.0.1",
1825
- "tapable": "^0.2.7"
1826
- }
1827
- },
1828
- "errno": {
1829
- "version": "0.1.4",
1830
- "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.4.tgz",
1831
- "integrity": "sha1-uJbiOp5ei6M4cfyZar02NfyaHH0=",
1832
- "dev": true,
1833
- "requires": {
1834
- "prr": "~0.0.0"
1835
- }
1836
- },
1837
- "error-ex": {
1838
- "version": "1.3.1",
1839
- "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz",
1840
- "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=",
1841
- "dev": true,
1842
- "requires": {
1843
- "is-arrayish": "^0.2.1"
1844
- }
1845
- },
1846
- "es5-ext": {
1847
- "version": "0.10.37",
1848
- "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.37.tgz",
1849
- "integrity": "sha1-DudB0Ui4AGm6J9AgOTdWryV978M=",
1850
- "dev": true,
1851
- "requires": {
1852
- "es6-iterator": "~2.0.1",
1853
- "es6-symbol": "~3.1.1"
1854
- }
1855
- },
1856
- "es6-iterator": {
1857
- "version": "2.0.3",
1858
- "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz",
1859
- "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=",
1860
- "dev": true,
1861
- "requires": {
1862
- "d": "1",
1863
- "es5-ext": "^0.10.35",
1864
- "es6-symbol": "^3.1.1"
1865
- }
1866
- },
1867
- "es6-map": {
1868
- "version": "0.1.5",
1869
- "resolved": "https://registry.npmjs.org/es6-map/-/es6-map-0.1.5.tgz",
1870
- "integrity": "sha1-kTbgUD3MBqMBaQ8LsU/042TpSfA=",
1871
- "dev": true,
1872
- "requires": {
1873
- "d": "1",
1874
- "es5-ext": "~0.10.14",
1875
- "es6-iterator": "~2.0.1",
1876
- "es6-set": "~0.1.5",
1877
- "es6-symbol": "~3.1.1",
1878
- "event-emitter": "~0.3.5"
1879
- }
1880
- },
1881
- "es6-set": {
1882
- "version": "0.1.5",
1883
- "resolved": "https://registry.npmjs.org/es6-set/-/es6-set-0.1.5.tgz",
1884
- "integrity": "sha1-0rPsXU2ADO2BjbU40ol02wpzzLE=",
1885
- "dev": true,
1886
- "requires": {
1887
- "d": "1",
1888
- "es5-ext": "~0.10.14",
1889
- "es6-iterator": "~2.0.1",
1890
- "es6-symbol": "3.1.1",
1891
- "event-emitter": "~0.3.5"
1892
- }
1893
- },
1894
- "es6-symbol": {
1895
- "version": "3.1.1",
1896
- "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz",
1897
- "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=",
1898
- "dev": true,
1899
- "requires": {
1900
- "d": "1",
1901
- "es5-ext": "~0.10.14"
1902
- }
1903
- },
1904
- "es6-weak-map": {
1905
- "version": "2.0.2",
1906
- "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.2.tgz",
1907
- "integrity": "sha1-XjqzIlH/0VOKH45f+hNXdy+S2W8=",
1908
- "dev": true,
1909
- "requires": {
1910
- "d": "1",
1911
- "es5-ext": "^0.10.14",
1912
- "es6-iterator": "^2.0.1",
1913
- "es6-symbol": "^3.1.1"
1914
- }
1915
- },
1916
- "escape-string-regexp": {
1917
- "version": "1.0.5",
1918
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
1919
- "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
1920
- "dev": true
1921
- },
1922
- "escope": {
1923
- "version": "3.6.0",
1924
- "resolved": "https://registry.npmjs.org/escope/-/escope-3.6.0.tgz",
1925
- "integrity": "sha1-4Bl16BJ4GhY6ba392AOY3GTIicM=",
1926
- "dev": true,
1927
- "requires": {
1928
- "es6-map": "^0.1.3",
1929
- "es6-weak-map": "^2.0.1",
1930
- "esrecurse": "^4.1.0",
1931
- "estraverse": "^4.1.1"
1932
- }
1933
- },
1934
- "eslint": {
1935
- "version": "3.19.0",
1936
- "resolved": "https://registry.npmjs.org/eslint/-/eslint-3.19.0.tgz",
1937
- "integrity": "sha1-yPxiAcf0DdCJQbh8CFdnOGpnmsw=",
1938
- "dev": true,
1939
- "requires": {
1940
- "babel-code-frame": "^6.16.0",
1941
- "chalk": "^1.1.3",
1942
- "concat-stream": "^1.5.2",
1943
- "debug": "^2.1.1",
1944
- "doctrine": "^2.0.0",
1945
- "escope": "^3.6.0",
1946
- "espree": "^3.4.0",
1947
- "esquery": "^1.0.0",
1948
- "estraverse": "^4.2.0",
1949
- "esutils": "^2.0.2",
1950
- "file-entry-cache": "^2.0.0",
1951
- "glob": "^7.0.3",
1952
- "globals": "^9.14.0",
1953
- "ignore": "^3.2.0",
1954
- "imurmurhash": "^0.1.4",
1955
- "inquirer": "^0.12.0",
1956
- "is-my-json-valid": "^2.10.0",
1957
- "is-resolvable": "^1.0.0",
1958
- "js-yaml": "^3.5.1",
1959
- "json-stable-stringify": "^1.0.0",
1960
- "levn": "^0.3.0",
1961
- "lodash": "^4.0.0",
1962
- "mkdirp": "^0.5.0",
1963
- "natural-compare": "^1.4.0",
1964
- "optionator": "^0.8.2",
1965
- "path-is-inside": "^1.0.1",
1966
- "pluralize": "^1.2.1",
1967
- "progress": "^1.1.8",
1968
- "require-uncached": "^1.0.2",
1969
- "shelljs": "^0.7.5",
1970
- "strip-bom": "^3.0.0",
1971
- "strip-json-comments": "~2.0.1",
1972
- "table": "^3.7.8",
1973
- "text-table": "~0.2.0",
1974
- "user-home": "^2.0.0"
1975
- }
1976
- },
1977
- "espree": {
1978
- "version": "3.5.2",
1979
- "resolved": "https://registry.npmjs.org/espree/-/espree-3.5.2.tgz",
1980
- "integrity": "sha512-sadKeYwaR/aJ3stC2CdvgXu1T16TdYN+qwCpcWbMnGJ8s0zNWemzrvb2GbD4OhmJ/fwpJjudThAlLobGbWZbCQ==",
1981
- "dev": true,
1982
- "requires": {
1983
- "acorn": "^5.2.1",
1984
- "acorn-jsx": "^3.0.0"
1985
- }
1986
- },
1987
- "esprima": {
1988
- "version": "2.7.3",
1989
- "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz",
1990
- "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=",
1991
- "dev": true
1992
- },
1993
- "esquery": {
1994
- "version": "1.0.0",
1995
- "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.0.tgz",
1996
- "integrity": "sha1-z7qLV9f7qT8XKYqKAGoEzaE9gPo=",
1997
- "dev": true,
1998
- "requires": {
1999
- "estraverse": "^4.0.0"
2000
- }
2001
- },
2002
- "esrecurse": {
2003
- "version": "4.2.0",
2004
- "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.0.tgz",
2005
- "integrity": "sha1-+pVo2Y04I/mkHZHpAtyrnqblsWM=",
2006
- "dev": true,
2007
- "requires": {
2008
- "estraverse": "^4.1.0",
2009
- "object-assign": "^4.0.1"
2010
- }
2011
- },
2012
- "estraverse": {
2013
- "version": "4.2.0",
2014
- "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz",
2015
- "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=",
2016
- "dev": true
2017
- },
2018
- "esutils": {
2019
- "version": "2.0.2",
2020
- "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz",
2021
- "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=",
2022
- "dev": true
2023
- },
2024
- "event-emitter": {
2025
- "version": "0.3.5",
2026
- "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz",
2027
- "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=",
2028
- "dev": true,
2029
- "requires": {
2030
- "d": "1",
2031
- "es5-ext": "~0.10.14"
2032
- }
2033
- },
2034
- "events": {
2035
- "version": "1.1.1",
2036
- "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz",
2037
- "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=",
2038
- "dev": true
2039
- },
2040
- "evp_bytestokey": {
2041
- "version": "1.0.3",
2042
- "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz",
2043
- "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==",
2044
- "dev": true,
2045
- "requires": {
2046
- "md5.js": "^1.3.4",
2047
- "safe-buffer": "^5.1.1"
2048
- }
2049
- },
2050
- "exit-hook": {
2051
- "version": "1.1.1",
2052
- "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-1.1.1.tgz",
2053
- "integrity": "sha1-8FyiM7SMBdVP/wd2XfhQfpXAL/g=",
2054
- "dev": true
2055
- },
2056
- "expand-brackets": {
2057
- "version": "0.1.5",
2058
- "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz",
2059
- "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=",
2060
- "dev": true,
2061
- "requires": {
2062
- "is-posix-bracket": "^0.1.0"
2063
- }
2064
- },
2065
- "expand-range": {
2066
- "version": "1.8.2",
2067
- "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz",
2068
- "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=",
2069
- "dev": true,
2070
- "requires": {
2071
- "fill-range": "^2.1.0"
2072
- }
2073
- },
2074
- "extend": {
2075
- "version": "3.0.1",
2076
- "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz",
2077
- "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=",
2078
- "dev": true
2079
- },
2080
- "extglob": {
2081
- "version": "0.3.2",
2082
- "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz",
2083
- "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=",
2084
- "dev": true,
2085
- "requires": {
2086
- "is-extglob": "^1.0.0"
2087
- }
2088
- },
2089
- "extract-text-webpack-plugin": {
2090
- "version": "2.1.2",
2091
- "resolved": "https://registry.npmjs.org/extract-text-webpack-plugin/-/extract-text-webpack-plugin-2.1.2.tgz",
2092
- "integrity": "sha1-dW7076gVXDaBgz+8NNpTuUF0bWw=",
2093
- "dev": true,
2094
- "requires": {
2095
- "async": "^2.1.2",
2096
- "loader-utils": "^1.0.2",
2097
- "schema-utils": "^0.3.0",
2098
- "webpack-sources": "^1.0.1"
2099
- }
2100
- },
2101
- "extsprintf": {
2102
- "version": "1.3.0",
2103
- "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
2104
- "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=",
2105
- "dev": true
2106
- },
2107
- "fast-deep-equal": {
2108
- "version": "1.0.0",
2109
- "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz",
2110
- "integrity": "sha1-liVqO8l1WV6zbYLpkp0GDYk0Of8=",
2111
- "dev": true
2112
- },
2113
- "fast-json-stable-stringify": {
2114
- "version": "2.0.0",
2115
- "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz",
2116
- "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=",
2117
- "dev": true
2118
- },
2119
- "fast-levenshtein": {
2120
- "version": "2.0.6",
2121
- "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
2122
- "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
2123
- "dev": true
2124
- },
2125
- "fastparse": {
2126
- "version": "1.1.1",
2127
- "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.1.tgz",
2128
- "integrity": "sha1-0eJkOzipTXWDtHkGDmxK/8lAcfg=",
2129
- "dev": true
2130
- },
2131
- "figures": {
2132
- "version": "1.7.0",
2133
- "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz",
2134
- "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=",
2135
- "dev": true,
2136
- "requires": {
2137
- "escape-string-regexp": "^1.0.5",
2138
- "object-assign": "^4.1.0"
2139
- }
2140
- },
2141
- "file-entry-cache": {
2142
- "version": "2.0.0",
2143
- "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz",
2144
- "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=",
2145
- "dev": true,
2146
- "requires": {
2147
- "flat-cache": "^1.2.1",
2148
- "object-assign": "^4.0.1"
2149
- }
2150
- },
2151
- "filename-regex": {
2152
- "version": "2.0.1",
2153
- "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz",
2154
- "integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=",
2155
- "dev": true
2156
- },
2157
- "fill-range": {
2158
- "version": "2.2.3",
2159
- "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.3.tgz",
2160
- "integrity": "sha1-ULd9/X5Gm8dJJHCWNpn+eoSFpyM=",
2161
- "dev": true,
2162
- "requires": {
2163
- "is-number": "^2.1.0",
2164
- "isobject": "^2.0.0",
2165
- "randomatic": "^1.1.3",
2166
- "repeat-element": "^1.1.2",
2167
- "repeat-string": "^1.5.2"
2168
- },
2169
- "dependencies": {
2170
- "isobject": {
2171
- "version": "2.1.0",
2172
- "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz",
2173
- "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=",
2174
- "dev": true,
2175
- "requires": {
2176
- "isarray": "1.0.0"
2177
- }
2178
- }
2179
- }
2180
- },
2181
- "find-cache-dir": {
2182
- "version": "1.0.0",
2183
- "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-1.0.0.tgz",
2184
- "integrity": "sha1-kojj6ePMN0hxfTnq3hfPcfww7m8=",
2185
- "dev": true,
2186
- "requires": {
2187
- "commondir": "^1.0.1",
2188
- "make-dir": "^1.0.0",
2189
- "pkg-dir": "^2.0.0"
2190
- }
2191
- },
2192
- "find-up": {
2193
- "version": "2.1.0",
2194
- "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
2195
- "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=",
2196
- "dev": true,
2197
- "requires": {
2198
- "locate-path": "^2.0.0"
2199
- }
2200
- },
2201
- "flat-cache": {
2202
- "version": "1.3.0",
2203
- "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.0.tgz",
2204
- "integrity": "sha1-0wMLMrOBVPTjt+nHCfSQ9++XxIE=",
2205
- "dev": true,
2206
- "requires": {
2207
- "circular-json": "^0.3.1",
2208
- "del": "^2.0.2",
2209
- "graceful-fs": "^4.1.2",
2210
- "write": "^0.2.1"
2211
- }
2212
- },
2213
- "flatten": {
2214
- "version": "1.0.2",
2215
- "resolved": "https://registry.npmjs.org/flatten/-/flatten-1.0.2.tgz",
2216
- "integrity": "sha1-2uRqnXj74lKSJYzB54CkHZXAN4I=",
2217
- "dev": true
2218
- },
2219
- "for-in": {
2220
- "version": "1.0.2",
2221
- "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
2222
- "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=",
2223
- "dev": true
2224
- },
2225
- "for-own": {
2226
- "version": "1.0.0",
2227
- "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz",
2228
- "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=",
2229
- "dev": true,
2230
- "requires": {
2231
- "for-in": "^1.0.1"
2232
- }
2233
- },
2234
- "forever-agent": {
2235
- "version": "0.6.1",
2236
- "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
2237
- "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=",
2238
- "dev": true
2239
- },
2240
- "form-data": {
2241
- "version": "2.1.4",
2242
- "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz",
2243
- "integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=",
2244
- "dev": true,
2245
- "requires": {
2246
- "asynckit": "^0.4.0",
2247
- "combined-stream": "^1.0.5",
2248
- "mime-types": "^2.1.12"
2249
- }
2250
- },
2251
- "fs.realpath": {
2252
- "version": "1.0.0",
2253
- "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
2254
- "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
2255
- "dev": true
2256
- },
2257
- "fsevents": {
2258
- "version": "1.1.3",
2259
- "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.1.3.tgz",
2260
- "integrity": "sha512-WIr7iDkdmdbxu/Gh6eKEZJL6KPE74/5MEsf2whTOFNxbIoIixogroLdKYqB6FDav4Wavh/lZdzzd3b2KxIXC5Q==",
2261
- "dev": true,
2262
- "optional": true,
2263
- "requires": {
2264
- "nan": "^2.3.0",
2265
- "node-pre-gyp": "^0.6.39"
2266
- },
2267
- "dependencies": {
2268
- "abbrev": {
2269
- "version": "1.1.0",
2270
- "bundled": true,
2271
- "dev": true,
2272
- "optional": true
2273
- },
2274
- "ajv": {
2275
- "version": "4.11.8",
2276
- "bundled": true,
2277
- "dev": true,
2278
- "optional": true,
2279
- "requires": {
2280
- "co": "^4.6.0",
2281
- "json-stable-stringify": "^1.0.1"
2282
- }
2283
- },
2284
- "ansi-regex": {
2285
- "version": "2.1.1",
2286
- "bundled": true,
2287
- "dev": true
2288
- },
2289
- "aproba": {
2290
- "version": "1.1.1",
2291
- "bundled": true,
2292
- "dev": true,
2293
- "optional": true
2294
- },
2295
- "are-we-there-yet": {
2296
- "version": "1.1.4",
2297
- "bundled": true,
2298
- "dev": true,
2299
- "optional": true,
2300
- "requires": {
2301
- "delegates": "^1.0.0",
2302
- "readable-stream": "^2.0.6"
2303
- }
2304
- },
2305
- "asn1": {
2306
- "version": "0.2.3",
2307
- "bundled": true,
2308
- "dev": true,
2309
- "optional": true
2310
- },
2311
- "assert-plus": {
2312
- "version": "0.2.0",
2313
- "bundled": true,
2314
- "dev": true,
2315
- "optional": true
2316
- },
2317
- "asynckit": {
2318
- "version": "0.4.0",
2319
- "bundled": true,
2320
- "dev": true,
2321
- "optional": true
2322
- },
2323
- "aws-sign2": {
2324
- "version": "0.6.0",
2325
- "bundled": true,
2326
- "dev": true,
2327
- "optional": true
2328
- },
2329
- "aws4": {
2330
- "version": "1.6.0",
2331
- "bundled": true,
2332
- "dev": true,
2333
- "optional": true
2334
- },
2335
- "balanced-match": {
2336
- "version": "0.4.2",
2337
- "bundled": true,
2338
- "dev": true
2339
- },
2340
- "bcrypt-pbkdf": {
2341
- "version": "1.0.1",
2342
- "bundled": true,
2343
- "dev": true,
2344
- "optional": true,
2345
- "requires": {
2346
- "tweetnacl": "^0.14.3"
2347
- }
2348
- },
2349
- "block-stream": {
2350
- "version": "0.0.9",
2351
- "bundled": true,
2352
- "dev": true,
2353
- "requires": {
2354
- "inherits": "~2.0.0"
2355
- }
2356
- },
2357
- "boom": {
2358
- "version": "2.10.1",
2359
- "bundled": true,
2360
- "dev": true,
2361
- "requires": {
2362
- "hoek": "2.x.x"
2363
- }
2364
- },
2365
- "brace-expansion": {
2366
- "version": "1.1.7",
2367
- "bundled": true,
2368
- "dev": true,
2369
- "requires": {
2370
- "balanced-match": "^0.4.1",
2371
- "concat-map": "0.0.1"
2372
- }
2373
- },
2374
- "buffer-shims": {
2375
- "version": "1.0.0",
2376
- "bundled": true,
2377
- "dev": true
2378
- },
2379
- "caseless": {
2380
- "version": "0.12.0",
2381
- "bundled": true,
2382
- "dev": true,
2383
- "optional": true
2384
- },
2385
- "co": {
2386
- "version": "4.6.0",
2387
- "bundled": true,
2388
- "dev": true,
2389
- "optional": true
2390
- },
2391
- "code-point-at": {
2392
- "version": "1.1.0",
2393
- "bundled": true,
2394
- "dev": true
2395
- },
2396
- "combined-stream": {
2397
- "version": "1.0.5",
2398
- "bundled": true,
2399
- "dev": true,
2400
- "requires": {
2401
- "delayed-stream": "~1.0.0"
2402
- }
2403
- },
2404
- "concat-map": {
2405
- "version": "0.0.1",
2406
- "bundled": true,
2407
- "dev": true
2408
- },
2409
- "console-control-strings": {
2410
- "version": "1.1.0",
2411
- "bundled": true,
2412
- "dev": true
2413
- },
2414
- "core-util-is": {
2415
- "version": "1.0.2",
2416
- "bundled": true,
2417
- "dev": true
2418
- },
2419
- "cryptiles": {
2420
- "version": "2.0.5",
2421
- "bundled": true,
2422
- "dev": true,
2423
- "requires": {
2424
- "boom": "2.x.x"
2425
- }
2426
- },
2427
- "dashdash": {
2428
- "version": "1.14.1",
2429
- "bundled": true,
2430
- "dev": true,
2431
- "optional": true,
2432
- "requires": {
2433
- "assert-plus": "^1.0.0"
2434
- },
2435
- "dependencies": {
2436
- "assert-plus": {
2437
- "version": "1.0.0",
2438
- "bundled": true,
2439
- "dev": true,
2440
- "optional": true
2441
- }
2442
- }
2443
- },
2444
- "debug": {
2445
- "version": "2.6.8",
2446
- "bundled": true,
2447
- "dev": true,
2448
- "optional": true,
2449
- "requires": {
2450
- "ms": "2.0.0"
2451
- }
2452
- },
2453
- "deep-extend": {
2454
- "version": "0.4.2",
2455
- "bundled": true,
2456
- "dev": true,
2457
- "optional": true
2458
- },
2459
- "delayed-stream": {
2460
- "version": "1.0.0",
2461
- "bundled": true,
2462
- "dev": true
2463
- },
2464
- "delegates": {
2465
- "version": "1.0.0",
2466
- "bundled": true,
2467
- "dev": true,
2468
- "optional": true
2469
- },
2470
- "detect-libc": {
2471
- "version": "1.0.2",
2472
- "bundled": true,
2473
- "dev": true,
2474
- "optional": true
2475
- },
2476
- "ecc-jsbn": {
2477
- "version": "0.1.1",
2478
- "bundled": true,
2479
- "dev": true,
2480
- "optional": true,
2481
- "requires": {
2482
- "jsbn": "~0.1.0"
2483
- }
2484
- },
2485
- "extend": {
2486
- "version": "3.0.1",
2487
- "bundled": true,
2488
- "dev": true,
2489
- "optional": true
2490
- },
2491
- "extsprintf": {
2492
- "version": "1.0.2",
2493
- "bundled": true,
2494
- "dev": true
2495
- },
2496
- "forever-agent": {
2497
- "version": "0.6.1",
2498
- "bundled": true,
2499
- "dev": true,
2500
- "optional": true
2501
- },
2502
- "form-data": {
2503
- "version": "2.1.4",
2504
- "bundled": true,
2505
- "dev": true,
2506
- "optional": true,
2507
- "requires": {
2508
- "asynckit": "^0.4.0",
2509
- "combined-stream": "^1.0.5",
2510
- "mime-types": "^2.1.12"
2511
- }
2512
- },
2513
- "fs.realpath": {
2514
- "version": "1.0.0",
2515
- "bundled": true,
2516
- "dev": true
2517
- },
2518
- "fstream": {
2519
- "version": "1.0.11",
2520
- "bundled": true,
2521
- "dev": true,
2522
- "requires": {
2523
- "graceful-fs": "^4.1.2",
2524
- "inherits": "~2.0.0",
2525
- "mkdirp": ">=0.5 0",
2526
- "rimraf": "2"
2527
- }
2528
- },
2529
- "fstream-ignore": {
2530
- "version": "1.0.5",
2531
- "bundled": true,
2532
- "dev": true,
2533
- "optional": true,
2534
- "requires": {
2535
- "fstream": "^1.0.0",
2536
- "inherits": "2",
2537
- "minimatch": "^3.0.0"
2538
- }
2539
- },
2540
- "gauge": {
2541
- "version": "2.7.4",
2542
- "bundled": true,
2543
- "dev": true,
2544
- "optional": true,
2545
- "requires": {
2546
- "aproba": "^1.0.3",
2547
- "console-control-strings": "^1.0.0",
2548
- "has-unicode": "^2.0.0",
2549
- "object-assign": "^4.1.0",
2550
- "signal-exit": "^3.0.0",
2551
- "string-width": "^1.0.1",
2552
- "strip-ansi": "^3.0.1",
2553
- "wide-align": "^1.1.0"
2554
- }
2555
- },
2556
- "getpass": {
2557
- "version": "0.1.7",
2558
- "bundled": true,
2559
- "dev": true,
2560
- "optional": true,
2561
- "requires": {
2562
- "assert-plus": "^1.0.0"
2563
- },
2564
- "dependencies": {
2565
- "assert-plus": {
2566
- "version": "1.0.0",
2567
- "bundled": true,
2568
- "dev": true,
2569
- "optional": true
2570
- }
2571
- }
2572
- },
2573
- "glob": {
2574
- "version": "7.1.2",
2575
- "bundled": true,
2576
- "dev": true,
2577
- "requires": {
2578
- "fs.realpath": "^1.0.0",
2579
- "inflight": "^1.0.4",
2580
- "inherits": "2",
2581
- "minimatch": "^3.0.4",
2582
- "once": "^1.3.0",
2583
- "path-is-absolute": "^1.0.0"
2584
- }
2585
- },
2586
- "graceful-fs": {
2587
- "version": "4.1.11",
2588
- "bundled": true,
2589
- "dev": true
2590
- },
2591
- "har-schema": {
2592
- "version": "1.0.5",
2593
- "bundled": true,
2594
- "dev": true,
2595
- "optional": true
2596
- },
2597
- "har-validator": {
2598
- "version": "4.2.1",
2599
- "bundled": true,
2600
- "dev": true,
2601
- "optional": true,
2602
- "requires": {
2603
- "ajv": "^4.9.1",
2604
- "har-schema": "^1.0.5"
2605
- }
2606
- },
2607
- "has-unicode": {
2608
- "version": "2.0.1",
2609
- "bundled": true,
2610
- "dev": true,
2611
- "optional": true
2612
- },
2613
- "hawk": {
2614
- "version": "3.1.3",
2615
- "bundled": true,
2616
- "dev": true,
2617
- "requires": {
2618
- "boom": "2.x.x",
2619
- "cryptiles": "2.x.x",
2620
- "hoek": "2.x.x",
2621
- "sntp": "1.x.x"
2622
- }
2623
- },
2624
- "hoek": {
2625
- "version": "2.16.3",
2626
- "bundled": true,
2627
- "dev": true
2628
- },
2629
- "http-signature": {
2630
- "version": "1.1.1",
2631
- "bundled": true,
2632
- "dev": true,
2633
- "optional": true,
2634
- "requires": {
2635
- "assert-plus": "^0.2.0",
2636
- "jsprim": "^1.2.2",
2637
- "sshpk": "^1.7.0"
2638
- }
2639
- },
2640
- "inflight": {
2641
- "version": "1.0.6",
2642
- "bundled": true,
2643
- "dev": true,
2644
- "requires": {
2645
- "once": "^1.3.0",
2646
- "wrappy": "1"
2647
- }
2648
- },
2649
- "inherits": {
2650
- "version": "2.0.3",
2651
- "bundled": true,
2652
- "dev": true
2653
- },
2654
- "ini": {
2655
- "version": "1.3.4",
2656
- "bundled": true,
2657
- "dev": true,
2658
- "optional": true
2659
- },
2660
- "is-fullwidth-code-point": {
2661
- "version": "1.0.0",
2662
- "bundled": true,
2663
- "dev": true,
2664
- "requires": {
2665
- "number-is-nan": "^1.0.0"
2666
- }
2667
- },
2668
- "is-typedarray": {
2669
- "version": "1.0.0",
2670
- "bundled": true,
2671
- "dev": true,
2672
- "optional": true
2673
- },
2674
- "isarray": {
2675
- "version": "1.0.0",
2676
- "bundled": true,
2677
- "dev": true
2678
- },
2679
- "isstream": {
2680
- "version": "0.1.2",
2681
- "bundled": true,
2682
- "dev": true,
2683
- "optional": true
2684
- },
2685
- "jodid25519": {
2686
- "version": "1.0.2",
2687
- "bundled": true,
2688
- "dev": true,
2689
- "optional": true,
2690
- "requires": {
2691
- "jsbn": "~0.1.0"
2692
- }
2693
- },
2694
- "jsbn": {
2695
- "version": "0.1.1",
2696
- "bundled": true,
2697
- "dev": true,
2698
- "optional": true
2699
- },
2700
- "json-schema": {
2701
- "version": "0.2.3",
2702
- "bundled": true,
2703
- "dev": true,
2704
- "optional": true
2705
- },
2706
- "json-stable-stringify": {
2707
- "version": "1.0.1",
2708
- "bundled": true,
2709
- "dev": true,
2710
- "optional": true,
2711
- "requires": {
2712
- "jsonify": "~0.0.0"
2713
- }
2714
- },
2715
- "json-stringify-safe": {
2716
- "version": "5.0.1",
2717
- "bundled": true,
2718
- "dev": true,
2719
- "optional": true
2720
- },
2721
- "jsonify": {
2722
- "version": "0.0.0",
2723
- "bundled": true,
2724
- "dev": true,
2725
- "optional": true
2726
- },
2727
- "jsprim": {
2728
- "version": "1.4.0",
2729
- "bundled": true,
2730
- "dev": true,
2731
- "optional": true,
2732
- "requires": {
2733
- "assert-plus": "1.0.0",
2734
- "extsprintf": "1.0.2",
2735
- "json-schema": "0.2.3",
2736
- "verror": "1.3.6"
2737
- },
2738
- "dependencies": {
2739
- "assert-plus": {
2740
- "version": "1.0.0",
2741
- "bundled": true,
2742
- "dev": true,
2743
- "optional": true
2744
- }
2745
- }
2746
- },
2747
- "mime-db": {
2748
- "version": "1.27.0",
2749
- "bundled": true,
2750
- "dev": true
2751
- },
2752
- "mime-types": {
2753
- "version": "2.1.15",
2754
- "bundled": true,
2755
- "dev": true,
2756
- "requires": {
2757
- "mime-db": "~1.27.0"
2758
- }
2759
- },
2760
- "minimatch": {
2761
- "version": "3.0.4",
2762
- "bundled": true,
2763
- "dev": true,
2764
- "requires": {
2765
- "brace-expansion": "^1.1.7"
2766
- }
2767
- },
2768
- "minimist": {
2769
- "version": "0.0.8",
2770
- "bundled": true,
2771
- "dev": true
2772
- },
2773
- "mkdirp": {
2774
- "version": "0.5.1",
2775
- "bundled": true,
2776
- "dev": true,
2777
- "requires": {
2778
- "minimist": "0.0.8"
2779
- }
2780
- },
2781
- "ms": {
2782
- "version": "2.0.0",
2783
- "bundled": true,
2784
- "dev": true,
2785
- "optional": true
2786
- },
2787
- "node-pre-gyp": {
2788
- "version": "0.6.39",
2789
- "bundled": true,
2790
- "dev": true,
2791
- "optional": true,
2792
- "requires": {
2793
- "detect-libc": "^1.0.2",
2794
- "hawk": "3.1.3",
2795
- "mkdirp": "^0.5.1",
2796
- "nopt": "^4.0.1",
2797
- "npmlog": "^4.0.2",
2798
- "rc": "^1.1.7",
2799
- "request": "2.81.0",
2800
- "rimraf": "^2.6.1",
2801
- "semver": "^5.3.0",
2802
- "tar": "^2.2.1",
2803
- "tar-pack": "^3.4.0"
2804
- }
2805
- },
2806
- "nopt": {
2807
- "version": "4.0.1",
2808
- "bundled": true,
2809
- "dev": true,
2810
- "optional": true,
2811
- "requires": {
2812
- "abbrev": "1",
2813
- "osenv": "^0.1.4"
2814
- }
2815
- },
2816
- "npmlog": {
2817
- "version": "4.1.0",
2818
- "bundled": true,
2819
- "dev": true,
2820
- "optional": true,
2821
- "requires": {
2822
- "are-we-there-yet": "~1.1.2",
2823
- "console-control-strings": "~1.1.0",
2824
- "gauge": "~2.7.3",
2825
- "set-blocking": "~2.0.0"
2826
- }
2827
- },
2828
- "number-is-nan": {
2829
- "version": "1.0.1",
2830
- "bundled": true,
2831
- "dev": true
2832
- },
2833
- "oauth-sign": {
2834
- "version": "0.8.2",
2835
- "bundled": true,
2836
- "dev": true,
2837
- "optional": true
2838
- },
2839
- "object-assign": {
2840
- "version": "4.1.1",
2841
- "bundled": true,
2842
- "dev": true,
2843
- "optional": true
2844
- },
2845
- "once": {
2846
- "version": "1.4.0",
2847
- "bundled": true,
2848
- "dev": true,
2849
- "requires": {
2850
- "wrappy": "1"
2851
- }
2852
- },
2853
- "os-homedir": {
2854
- "version": "1.0.2",
2855
- "bundled": true,
2856
- "dev": true,
2857
- "optional": true
2858
- },
2859
- "os-tmpdir": {
2860
- "version": "1.0.2",
2861
- "bundled": true,
2862
- "dev": true,
2863
- "optional": true
2864
- },
2865
- "osenv": {
2866
- "version": "0.1.4",
2867
- "bundled": true,
2868
- "dev": true,
2869
- "optional": true,
2870
- "requires": {
2871
- "os-homedir": "^1.0.0",
2872
- "os-tmpdir": "^1.0.0"
2873
- }
2874
- },
2875
- "path-is-absolute": {
2876
- "version": "1.0.1",
2877
- "bundled": true,
2878
- "dev": true
2879
- },
2880
- "performance-now": {
2881
- "version": "0.2.0",
2882
- "bundled": true,
2883
- "dev": true,
2884
- "optional": true
2885
- },
2886
- "process-nextick-args": {
2887
- "version": "1.0.7",
2888
- "bundled": true,
2889
- "dev": true
2890
- },
2891
- "punycode": {
2892
- "version": "1.4.1",
2893
- "bundled": true,
2894
- "dev": true,
2895
- "optional": true
2896
- },
2897
- "qs": {
2898
- "version": "6.4.0",
2899
- "bundled": true,
2900
- "dev": true,
2901
- "optional": true
2902
- },
2903
- "rc": {
2904
- "version": "1.2.1",
2905
- "bundled": true,
2906
- "dev": true,
2907
- "optional": true,
2908
- "requires": {
2909
- "deep-extend": "~0.4.0",
2910
- "ini": "~1.3.0",
2911
- "minimist": "^1.2.0",
2912
- "strip-json-comments": "~2.0.1"
2913
- },
2914
- "dependencies": {
2915
- "minimist": {
2916
- "version": "1.2.0",
2917
- "bundled": true,
2918
- "dev": true,
2919
- "optional": true
2920
- }
2921
- }
2922
- },
2923
- "readable-stream": {
2924
- "version": "2.2.9",
2925
- "bundled": true,
2926
- "dev": true,
2927
- "requires": {
2928
- "buffer-shims": "~1.0.0",
2929
- "core-util-is": "~1.0.0",
2930
- "inherits": "~2.0.1",
2931
- "isarray": "~1.0.0",
2932
- "process-nextick-args": "~1.0.6",
2933
- "string_decoder": "~1.0.0",
2934
- "util-deprecate": "~1.0.1"
2935
- }
2936
- },
2937
- "request": {
2938
- "version": "2.81.0",
2939
- "bundled": true,
2940
- "dev": true,
2941
- "optional": true,
2942
- "requires": {
2943
- "aws-sign2": "~0.6.0",
2944
- "aws4": "^1.2.1",
2945
- "caseless": "~0.12.0",
2946
- "combined-stream": "~1.0.5",
2947
- "extend": "~3.0.0",
2948
- "forever-agent": "~0.6.1",
2949
- "form-data": "~2.1.1",
2950
- "har-validator": "~4.2.1",
2951
- "hawk": "~3.1.3",
2952
- "http-signature": "~1.1.0",
2953
- "is-typedarray": "~1.0.0",
2954
- "isstream": "~0.1.2",
2955
- "json-stringify-safe": "~5.0.1",
2956
- "mime-types": "~2.1.7",
2957
- "oauth-sign": "~0.8.1",
2958
- "performance-now": "^0.2.0",
2959
- "qs": "~6.4.0",
2960
- "safe-buffer": "^5.0.1",
2961
- "stringstream": "~0.0.4",
2962
- "tough-cookie": "~2.3.0",
2963
- "tunnel-agent": "^0.6.0",
2964
- "uuid": "^3.0.0"
2965
- }
2966
- },
2967
- "rimraf": {
2968
- "version": "2.6.1",
2969
- "bundled": true,
2970
- "dev": true,
2971
- "requires": {
2972
- "glob": "^7.0.5"
2973
- }
2974
- },
2975
- "safe-buffer": {
2976
- "version": "5.0.1",
2977
- "bundled": true,
2978
- "dev": true
2979
- },
2980
- "semver": {
2981
- "version": "5.3.0",
2982
- "bundled": true,
2983
- "dev": true,
2984
- "optional": true
2985
- },
2986
- "set-blocking": {
2987
- "version": "2.0.0",
2988
- "bundled": true,
2989
- "dev": true,
2990
- "optional": true
2991
- },
2992
- "signal-exit": {
2993
- "version": "3.0.2",
2994
- "bundled": true,
2995
- "dev": true,
2996
- "optional": true
2997
- },
2998
- "sntp": {
2999
- "version": "1.0.9",
3000
- "bundled": true,
3001
- "dev": true,
3002
- "requires": {
3003
- "hoek": "2.x.x"
3004
- }
3005
- },
3006
- "sshpk": {
3007
- "version": "1.13.0",
3008
- "bundled": true,
3009
- "dev": true,
3010
- "optional": true,
3011
- "requires": {
3012
- "asn1": "~0.2.3",
3013
- "assert-plus": "^1.0.0",
3014
- "bcrypt-pbkdf": "^1.0.0",
3015
- "dashdash": "^1.12.0",
3016
- "ecc-jsbn": "~0.1.1",
3017
- "getpass": "^0.1.1",
3018
- "jodid25519": "^1.0.0",
3019
- "jsbn": "~0.1.0",
3020
- "tweetnacl": "~0.14.0"
3021
- },
3022
- "dependencies": {
3023
- "assert-plus": {
3024
- "version": "1.0.0",
3025
- "bundled": true,
3026
- "dev": true,
3027
- "optional": true
3028
- }
3029
- }
3030
- },
3031
- "string-width": {
3032
- "version": "1.0.2",
3033
- "bundled": true,
3034
- "dev": true,
3035
- "requires": {
3036
- "code-point-at": "^1.0.0",
3037
- "is-fullwidth-code-point": "^1.0.0",
3038
- "strip-ansi": "^3.0.0"
3039
- }
3040
- },
3041
- "string_decoder": {
3042
- "version": "1.0.1",
3043
- "bundled": true,
3044
- "dev": true,
3045
- "requires": {
3046
- "safe-buffer": "^5.0.1"
3047
- }
3048
- },
3049
- "stringstream": {
3050
- "version": "0.0.5",
3051
- "bundled": true,
3052
- "dev": true,
3053
- "optional": true
3054
- },
3055
- "strip-ansi": {
3056
- "version": "3.0.1",
3057
- "bundled": true,
3058
- "dev": true,
3059
- "requires": {
3060
- "ansi-regex": "^2.0.0"
3061
- }
3062
- },
3063
- "strip-json-comments": {
3064
- "version": "2.0.1",
3065
- "bundled": true,
3066
- "dev": true,
3067
- "optional": true
3068
- },
3069
- "tar": {
3070
- "version": "2.2.1",
3071
- "bundled": true,
3072
- "dev": true,
3073
- "requires": {
3074
- "block-stream": "*",
3075
- "fstream": "^1.0.2",
3076
- "inherits": "2"
3077
- }
3078
- },
3079
- "tar-pack": {
3080
- "version": "3.4.0",
3081
- "bundled": true,
3082
- "dev": true,
3083
- "optional": true,
3084
- "requires": {
3085
- "debug": "^2.2.0",
3086
- "fstream": "^1.0.10",
3087
- "fstream-ignore": "^1.0.5",
3088
- "once": "^1.3.3",
3089
- "readable-stream": "^2.1.4",
3090
- "rimraf": "^2.5.1",
3091
- "tar": "^2.2.1",
3092
- "uid-number": "^0.0.6"
3093
- }
3094
- },
3095
- "tough-cookie": {
3096
- "version": "2.3.2",
3097
- "bundled": true,
3098
- "dev": true,
3099
- "optional": true,
3100
- "requires": {
3101
- "punycode": "^1.4.1"
3102
- }
3103
- },
3104
- "tunnel-agent": {
3105
- "version": "0.6.0",
3106
- "bundled": true,
3107
- "dev": true,
3108
- "optional": true,
3109
- "requires": {
3110
- "safe-buffer": "^5.0.1"
3111
- }
3112
- },
3113
- "tweetnacl": {
3114
- "version": "0.14.5",
3115
- "bundled": true,
3116
- "dev": true,
3117
- "optional": true
3118
- },
3119
- "uid-number": {
3120
- "version": "0.0.6",
3121
- "bundled": true,
3122
- "dev": true,
3123
- "optional": true
3124
- },
3125
- "util-deprecate": {
3126
- "version": "1.0.2",
3127
- "bundled": true,
3128
- "dev": true
3129
- },
3130
- "uuid": {
3131
- "version": "3.0.1",
3132
- "bundled": true,
3133
- "dev": true,
3134
- "optional": true
3135
- },
3136
- "verror": {
3137
- "version": "1.3.6",
3138
- "bundled": true,
3139
- "dev": true,
3140
- "optional": true,
3141
- "requires": {
3142
- "extsprintf": "1.0.2"
3143
- }
3144
- },
3145
- "wide-align": {
3146
- "version": "1.1.2",
3147
- "bundled": true,
3148
- "dev": true,
3149
- "optional": true,
3150
- "requires": {
3151
- "string-width": "^1.0.2"
3152
- }
3153
- },
3154
- "wrappy": {
3155
- "version": "1.0.2",
3156
- "bundled": true,
3157
- "dev": true
3158
- }
3159
- }
3160
- },
3161
- "fstream": {
3162
- "version": "1.0.11",
3163
- "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz",
3164
- "integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=",
3165
- "dev": true,
3166
- "requires": {
3167
- "graceful-fs": "^4.1.2",
3168
- "inherits": "~2.0.0",
3169
- "mkdirp": ">=0.5 0",
3170
- "rimraf": "2"
3171
- }
3172
- },
3173
- "function-bind": {
3174
- "version": "1.1.1",
3175
- "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
3176
- "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
3177
- "dev": true
3178
- },
3179
- "gauge": {
3180
- "version": "2.7.4",
3181
- "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
3182
- "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
3183
- "dev": true,
3184
- "requires": {
3185
- "aproba": "^1.0.3",
3186
- "console-control-strings": "^1.0.0",
3187
- "has-unicode": "^2.0.0",
3188
- "object-assign": "^4.1.0",
3189
- "signal-exit": "^3.0.0",
3190
- "string-width": "^1.0.1",
3191
- "strip-ansi": "^3.0.1",
3192
- "wide-align": "^1.1.0"
3193
- }
3194
- },
3195
- "gaze": {
3196
- "version": "1.1.2",
3197
- "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.2.tgz",
3198
- "integrity": "sha1-hHIkZ3rbiHDWeSV+0ziP22HkAQU=",
3199
- "dev": true,
3200
- "requires": {
3201
- "globule": "^1.0.0"
3202
- }
3203
- },
3204
- "generate-function": {
3205
- "version": "2.0.0",
3206
- "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz",
3207
- "integrity": "sha1-aFj+fAlpt9TpCTM3ZHrHn2DfvnQ=",
3208
- "dev": true
3209
- },
3210
- "generate-object-property": {
3211
- "version": "1.2.0",
3212
- "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz",
3213
- "integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=",
3214
- "dev": true,
3215
- "requires": {
3216
- "is-property": "^1.0.0"
3217
- }
3218
- },
3219
- "get-caller-file": {
3220
- "version": "1.0.2",
3221
- "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.2.tgz",
3222
- "integrity": "sha1-9wLmMSfn4jHBYKgMFVSstw1QR+U=",
3223
- "dev": true
3224
- },
3225
- "get-stdin": {
3226
- "version": "4.0.1",
3227
- "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz",
3228
- "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=",
3229
- "dev": true
3230
- },
3231
- "getpass": {
3232
- "version": "0.1.7",
3233
- "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
3234
- "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
3235
- "dev": true,
3236
- "requires": {
3237
- "assert-plus": "^1.0.0"
3238
- },
3239
- "dependencies": {
3240
- "assert-plus": {
3241
- "version": "1.0.0",
3242
- "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
3243
- "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
3244
- "dev": true
3245
- }
3246
- }
3247
- },
3248
- "glob": {
3249
- "version": "7.1.2",
3250
- "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
3251
- "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
3252
- "dev": true,
3253
- "requires": {
3254
- "fs.realpath": "^1.0.0",
3255
- "inflight": "^1.0.4",
3256
- "inherits": "2",
3257
- "minimatch": "^3.0.4",
3258
- "once": "^1.3.0",
3259
- "path-is-absolute": "^1.0.0"
3260
- }
3261
- },
3262
- "glob-base": {
3263
- "version": "0.3.0",
3264
- "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz",
3265
- "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=",
3266
- "dev": true,
3267
- "requires": {
3268
- "glob-parent": "^2.0.0",
3269
- "is-glob": "^2.0.0"
3270
- }
3271
- },
3272
- "glob-parent": {
3273
- "version": "2.0.0",
3274
- "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz",
3275
- "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=",
3276
- "dev": true,
3277
- "requires": {
3278
- "is-glob": "^2.0.0"
3279
- }
3280
- },
3281
- "globals": {
3282
- "version": "9.18.0",
3283
- "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz",
3284
- "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==",
3285
- "dev": true
3286
- },
3287
- "globby": {
3288
- "version": "5.0.0",
3289
- "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz",
3290
- "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=",
3291
- "dev": true,
3292
- "requires": {
3293
- "array-union": "^1.0.1",
3294
- "arrify": "^1.0.0",
3295
- "glob": "^7.0.3",
3296
- "object-assign": "^4.0.1",
3297
- "pify": "^2.0.0",
3298
- "pinkie-promise": "^2.0.0"
3299
- },
3300
- "dependencies": {
3301
- "pify": {
3302
- "version": "2.3.0",
3303
- "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
3304
- "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
3305
- "dev": true
3306
- }
3307
- }
3308
- },
3309
- "globule": {
3310
- "version": "1.2.0",
3311
- "resolved": "https://registry.npmjs.org/globule/-/globule-1.2.0.tgz",
3312
- "integrity": "sha1-HcScaCLdnoovoAuiopUAboZkvQk=",
3313
- "dev": true,
3314
- "requires": {
3315
- "glob": "~7.1.1",
3316
- "lodash": "~4.17.4",
3317
- "minimatch": "~3.0.2"
3318
- }
3319
- },
3320
- "graceful-fs": {
3321
- "version": "4.1.11",
3322
- "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
3323
- "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=",
3324
- "dev": true
3325
- },
3326
- "har-validator": {
3327
- "version": "2.0.6",
3328
- "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-2.0.6.tgz",
3329
- "integrity": "sha1-zcvAgYgmWtEZtqWnyKtw7s+10n0=",
3330
- "dev": true,
3331
- "requires": {
3332
- "chalk": "^1.1.1",
3333
- "commander": "^2.9.0",
3334
- "is-my-json-valid": "^2.12.4",
3335
- "pinkie-promise": "^2.0.0"
3336
- }
3337
- },
3338
- "has": {
3339
- "version": "1.0.1",
3340
- "resolved": "https://registry.npmjs.org/has/-/has-1.0.1.tgz",
3341
- "integrity": "sha1-hGFzP1OLCDfJNh45qauelwTcLyg=",
3342
- "dev": true,
3343
- "requires": {
3344
- "function-bind": "^1.0.2"
3345
- }
3346
- },
3347
- "has-ansi": {
3348
- "version": "2.0.0",
3349
- "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
3350
- "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
3351
- "dev": true,
3352
- "requires": {
3353
- "ansi-regex": "^2.0.0"
3354
- }
3355
- },
3356
- "has-flag": {
3357
- "version": "1.0.0",
3358
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz",
3359
- "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=",
3360
- "dev": true
3361
- },
3362
- "has-unicode": {
3363
- "version": "2.0.1",
3364
- "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
3365
- "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=",
3366
- "dev": true
3367
- },
3368
- "hash-base": {
3369
- "version": "2.0.2",
3370
- "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-2.0.2.tgz",
3371
- "integrity": "sha1-ZuodhW206KVHDK32/OI65SRO8uE=",
3372
- "dev": true,
3373
- "requires": {
3374
- "inherits": "^2.0.1"
3375
- }
3376
- },
3377
- "hash.js": {
3378
- "version": "1.1.3",
3379
- "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.3.tgz",
3380
- "integrity": "sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA==",
3381
- "dev": true,
3382
- "requires": {
3383
- "inherits": "^2.0.3",
3384
- "minimalistic-assert": "^1.0.0"
3385
- }
3386
- },
3387
- "hawk": {
3388
- "version": "3.1.3",
3389
- "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz",
3390
- "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=",
3391
- "dev": true,
3392
- "requires": {
3393
- "boom": "2.x.x",
3394
- "cryptiles": "2.x.x",
3395
- "hoek": "2.x.x",
3396
- "sntp": "1.x.x"
3397
- }
3398
- },
3399
- "hmac-drbg": {
3400
- "version": "1.0.1",
3401
- "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
3402
- "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=",
3403
- "dev": true,
3404
- "requires": {
3405
- "hash.js": "^1.0.3",
3406
- "minimalistic-assert": "^1.0.0",
3407
- "minimalistic-crypto-utils": "^1.0.1"
3408
- }
3409
- },
3410
- "hoek": {
3411
- "version": "2.16.3",
3412
- "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz",
3413
- "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=",
3414
- "dev": true
3415
- },
3416
- "home-or-tmp": {
3417
- "version": "2.0.0",
3418
- "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz",
3419
- "integrity": "sha1-42w/LSyufXRqhX440Y1fMqeILbg=",
3420
- "dev": true,
3421
- "requires": {
3422
- "os-homedir": "^1.0.0",
3423
- "os-tmpdir": "^1.0.1"
3424
- }
3425
- },
3426
- "hosted-git-info": {
3427
- "version": "2.5.0",
3428
- "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.5.0.tgz",
3429
- "integrity": "sha512-pNgbURSuab90KbTqvRPsseaTxOJCZBD0a7t+haSN33piP9cCM4l0CqdzAif2hUqm716UovKB2ROmiabGAKVXyg==",
3430
- "dev": true
3431
- },
3432
- "html-comment-regex": {
3433
- "version": "1.1.1",
3434
- "resolved": "https://registry.npmjs.org/html-comment-regex/-/html-comment-regex-1.1.1.tgz",
3435
- "integrity": "sha1-ZouTd26q5V696POtRkswekljYl4=",
3436
- "dev": true
3437
- },
3438
- "http-signature": {
3439
- "version": "1.1.1",
3440
- "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz",
3441
- "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=",
3442
- "dev": true,
3443
- "requires": {
3444
- "assert-plus": "^0.2.0",
3445
- "jsprim": "^1.2.2",
3446
- "sshpk": "^1.7.0"
3447
- }
3448
- },
3449
- "https-browserify": {
3450
- "version": "1.0.0",
3451
- "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz",
3452
- "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=",
3453
- "dev": true
3454
- },
3455
- "icss-replace-symbols": {
3456
- "version": "1.1.0",
3457
- "resolved": "https://registry.npmjs.org/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz",
3458
- "integrity": "sha1-Bupvg2ead0njhs/h/oEq5dsiPe0=",
3459
- "dev": true
3460
- },
3461
- "icss-utils": {
3462
- "version": "2.1.0",
3463
- "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-2.1.0.tgz",
3464
- "integrity": "sha1-g/Cg7DeL8yRheLbCrZE28TWxyWI=",
3465
- "dev": true,
3466
- "requires": {
3467
- "postcss": "^6.0.1"
3468
- },
3469
- "dependencies": {
3470
- "ansi-styles": {
3471
- "version": "3.2.0",
3472
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz",
3473
- "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==",
3474
- "dev": true,
3475
- "requires": {
3476
- "color-convert": "^1.9.0"
3477
- }
3478
- },
3479
- "chalk": {
3480
- "version": "2.3.0",
3481
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.0.tgz",
3482
- "integrity": "sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==",
3483
- "dev": true,
3484
- "requires": {
3485
- "ansi-styles": "^3.1.0",
3486
- "escape-string-regexp": "^1.0.5",
3487
- "supports-color": "^4.0.0"
3488
- }
3489
- },
3490
- "has-flag": {
3491
- "version": "2.0.0",
3492
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz",
3493
- "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=",
3494
- "dev": true
3495
- },
3496
- "postcss": {
3497
- "version": "6.0.14",
3498
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.14.tgz",
3499
- "integrity": "sha512-NJ1z0f+1offCgadPhz+DvGm5Mkci+mmV5BqD13S992o0Xk9eElxUfPPF+t2ksH5R/17gz4xVK8KWocUQ5o3Rog==",
3500
- "dev": true,
3501
- "requires": {
3502
- "chalk": "^2.3.0",
3503
- "source-map": "^0.6.1",
3504
- "supports-color": "^4.4.0"
3505
- }
3506
- },
3507
- "source-map": {
3508
- "version": "0.6.1",
3509
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
3510
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
3511
- "dev": true
3512
- },
3513
- "supports-color": {
3514
- "version": "4.5.0",
3515
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz",
3516
- "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=",
3517
- "dev": true,
3518
- "requires": {
3519
- "has-flag": "^2.0.0"
3520
- }
3521
- }
3522
- }
3523
- },
3524
- "ieee754": {
3525
- "version": "1.1.8",
3526
- "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz",
3527
- "integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q=",
3528
- "dev": true
3529
- },
3530
- "ignore": {
3531
- "version": "3.3.7",
3532
- "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.7.tgz",
3533
- "integrity": "sha512-YGG3ejvBNHRqu0559EOxxNFihD0AjpvHlC/pdGKd3X3ofe+CoJkYazwNJYTNebqpPKN+VVQbh4ZFn1DivMNuHA==",
3534
- "dev": true
3535
- },
3536
- "imurmurhash": {
3537
- "version": "0.1.4",
3538
- "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
3539
- "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=",
3540
- "dev": true
3541
- },
3542
- "in-publish": {
3543
- "version": "2.0.0",
3544
- "resolved": "https://registry.npmjs.org/in-publish/-/in-publish-2.0.0.tgz",
3545
- "integrity": "sha1-4g/146KvwmkDILbcVSaCqcf631E=",
3546
- "dev": true
3547
- },
3548
- "indent-string": {
3549
- "version": "2.1.0",
3550
- "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz",
3551
- "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=",
3552
- "dev": true,
3553
- "requires": {
3554
- "repeating": "^2.0.0"
3555
- }
3556
- },
3557
- "indexes-of": {
3558
- "version": "1.0.1",
3559
- "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz",
3560
- "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=",
3561
- "dev": true
3562
- },
3563
- "indexof": {
3564
- "version": "0.0.1",
3565
- "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz",
3566
- "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=",
3567
- "dev": true
3568
- },
3569
- "inflight": {
3570
- "version": "1.0.6",
3571
- "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
3572
- "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
3573
- "dev": true,
3574
- "requires": {
3575
- "once": "^1.3.0",
3576
- "wrappy": "1"
3577
- }
3578
- },
3579
- "inherits": {
3580
- "version": "2.0.3",
3581
- "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
3582
- "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
3583
- "dev": true
3584
- },
3585
- "inquirer": {
3586
- "version": "0.12.0",
3587
- "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-0.12.0.tgz",
3588
- "integrity": "sha1-HvK/1jUE3wvHV4X/+MLEHfEvB34=",
3589
- "dev": true,
3590
- "requires": {
3591
- "ansi-escapes": "^1.1.0",
3592
- "ansi-regex": "^2.0.0",
3593
- "chalk": "^1.0.0",
3594
- "cli-cursor": "^1.0.1",
3595
- "cli-width": "^2.0.0",
3596
- "figures": "^1.3.5",
3597
- "lodash": "^4.3.0",
3598
- "readline2": "^1.0.1",
3599
- "run-async": "^0.1.0",
3600
- "rx-lite": "^3.1.2",
3601
- "string-width": "^1.0.1",
3602
- "strip-ansi": "^3.0.0",
3603
- "through": "^2.3.6"
3604
- }
3605
- },
3606
- "interpret": {
3607
- "version": "1.0.4",
3608
- "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.0.4.tgz",
3609
- "integrity": "sha1-ggzdWIuGj/sZGoCVBtbJyPISsbA=",
3610
- "dev": true
3611
- },
3612
- "invariant": {
3613
- "version": "2.2.2",
3614
- "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.2.tgz",
3615
- "integrity": "sha1-nh9WrArNtr8wMwbzOL47IErmA2A=",
3616
- "dev": true,
3617
- "requires": {
3618
- "loose-envify": "^1.0.0"
3619
- }
3620
- },
3621
- "invert-kv": {
3622
- "version": "1.0.0",
3623
- "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz",
3624
- "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=",
3625
- "dev": true
3626
- },
3627
- "is-absolute-url": {
3628
- "version": "2.1.0",
3629
- "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-2.1.0.tgz",
3630
- "integrity": "sha1-UFMN+4T8yap9vnhS6Do3uTufKqY=",
3631
- "dev": true
3632
- },
3633
- "is-arrayish": {
3634
- "version": "0.2.1",
3635
- "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
3636
- "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
3637
- "dev": true
3638
- },
3639
- "is-binary-path": {
3640
- "version": "1.0.1",
3641
- "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz",
3642
- "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=",
3643
- "dev": true,
3644
- "requires": {
3645
- "binary-extensions": "^1.0.0"
3646
- }
3647
- },
3648
- "is-buffer": {
3649
- "version": "1.1.6",
3650
- "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
3651
- "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
3652
- "dev": true
3653
- },
3654
- "is-builtin-module": {
3655
- "version": "1.0.0",
3656
- "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz",
3657
- "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=",
3658
- "dev": true,
3659
- "requires": {
3660
- "builtin-modules": "^1.0.0"
3661
- }
3662
- },
3663
- "is-dotfile": {
3664
- "version": "1.0.3",
3665
- "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz",
3666
- "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=",
3667
- "dev": true
3668
- },
3669
- "is-equal-shallow": {
3670
- "version": "0.1.3",
3671
- "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz",
3672
- "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=",
3673
- "dev": true,
3674
- "requires": {
3675
- "is-primitive": "^2.0.0"
3676
- }
3677
- },
3678
- "is-extendable": {
3679
- "version": "0.1.1",
3680
- "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
3681
- "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=",
3682
- "dev": true
3683
- },
3684
- "is-extglob": {
3685
- "version": "1.0.0",
3686
- "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz",
3687
- "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=",
3688
- "dev": true
3689
- },
3690
- "is-finite": {
3691
- "version": "1.0.2",
3692
- "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz",
3693
- "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=",
3694
- "dev": true,
3695
- "requires": {
3696
- "number-is-nan": "^1.0.0"
3697
- }
3698
- },
3699
- "is-fullwidth-code-point": {
3700
- "version": "1.0.0",
3701
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
3702
- "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
3703
- "dev": true,
3704
- "requires": {
3705
- "number-is-nan": "^1.0.0"
3706
- }
3707
- },
3708
- "is-glob": {
3709
- "version": "2.0.1",
3710
- "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz",
3711
- "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=",
3712
- "dev": true,
3713
- "requires": {
3714
- "is-extglob": "^1.0.0"
3715
- }
3716
- },
3717
- "is-my-json-valid": {
3718
- "version": "2.16.1",
3719
- "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.16.1.tgz",
3720
- "integrity": "sha512-ochPsqWS1WXj8ZnMIV0vnNXooaMhp7cyL4FMSIPKTtnV0Ha/T19G2b9kkhcNsabV9bxYkze7/aLZJb/bYuFduQ==",
3721
- "dev": true,
3722
- "requires": {
3723
- "generate-function": "^2.0.0",
3724
- "generate-object-property": "^1.1.0",
3725
- "jsonpointer": "^4.0.0",
3726
- "xtend": "^4.0.0"
3727
- }
3728
- },
3729
- "is-number": {
3730
- "version": "2.1.0",
3731
- "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz",
3732
- "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=",
3733
- "dev": true,
3734
- "requires": {
3735
- "kind-of": "^3.0.2"
3736
- }
3737
- },
3738
- "is-path-cwd": {
3739
- "version": "1.0.0",
3740
- "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz",
3741
- "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=",
3742
- "dev": true
3743
- },
3744
- "is-path-in-cwd": {
3745
- "version": "1.0.0",
3746
- "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz",
3747
- "integrity": "sha1-ZHdYK4IU1gI0YJRWcAO+ip6sBNw=",
3748
- "dev": true,
3749
- "requires": {
3750
- "is-path-inside": "^1.0.0"
3751
- }
3752
- },
3753
- "is-path-inside": {
3754
- "version": "1.0.0",
3755
- "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.0.tgz",
3756
- "integrity": "sha1-/AbloWg/vaE95mev9xe7wQpI838=",
3757
- "dev": true,
3758
- "requires": {
3759
- "path-is-inside": "^1.0.1"
3760
- }
3761
- },
3762
- "is-plain-obj": {
3763
- "version": "1.1.0",
3764
- "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz",
3765
- "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=",
3766
- "dev": true
3767
- },
3768
- "is-plain-object": {
3769
- "version": "2.0.4",
3770
- "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
3771
- "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
3772
- "dev": true,
3773
- "requires": {
3774
- "isobject": "^3.0.1"
3775
- }
3776
- },
3777
- "is-posix-bracket": {
3778
- "version": "0.1.1",
3779
- "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz",
3780
- "integrity": "sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=",
3781
- "dev": true
3782
- },
3783
- "is-primitive": {
3784
- "version": "2.0.0",
3785
- "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz",
3786
- "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=",
3787
- "dev": true
3788
- },
3789
- "is-property": {
3790
- "version": "1.0.2",
3791
- "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz",
3792
- "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=",
3793
- "dev": true
3794
- },
3795
- "is-resolvable": {
3796
- "version": "1.0.0",
3797
- "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.0.0.tgz",
3798
- "integrity": "sha1-jfV8YeouPFAUCNEA+wE8+NbgzGI=",
3799
- "dev": true,
3800
- "requires": {
3801
- "tryit": "^1.0.1"
3802
- }
3803
- },
3804
- "is-svg": {
3805
- "version": "2.1.0",
3806
- "resolved": "https://registry.npmjs.org/is-svg/-/is-svg-2.1.0.tgz",
3807
- "integrity": "sha1-z2EJDaDZ77yrhyLeum8DIgjbsOk=",
3808
- "dev": true,
3809
- "requires": {
3810
- "html-comment-regex": "^1.1.0"
3811
- }
3812
- },
3813
- "is-typedarray": {
3814
- "version": "1.0.0",
3815
- "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
3816
- "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=",
3817
- "dev": true
3818
- },
3819
- "is-utf8": {
3820
- "version": "0.2.1",
3821
- "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz",
3822
- "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=",
3823
- "dev": true
3824
- },
3825
- "isarray": {
3826
- "version": "1.0.0",
3827
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
3828
- "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
3829
- "dev": true
3830
- },
3831
- "isexe": {
3832
- "version": "2.0.0",
3833
- "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
3834
- "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
3835
- "dev": true
3836
- },
3837
- "isobject": {
3838
- "version": "3.0.1",
3839
- "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
3840
- "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
3841
- "dev": true
3842
- },
3843
- "isstream": {
3844
- "version": "0.1.2",
3845
- "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
3846
- "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=",
3847
- "dev": true
3848
- },
3849
- "jquery": {
3850
- "version": "3.2.1",
3851
- "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.2.1.tgz",
3852
- "integrity": "sha1-XE2d5lKvbNCncBVKYxu6ErAVx4c="
3853
- },
3854
- "js-base64": {
3855
- "version": "2.3.2",
3856
- "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.3.2.tgz",
3857
- "integrity": "sha512-Y2/+DnfJJXT1/FCwUebUhLWb3QihxiSC42+ctHLGogmW2jPY6LCapMdFZXRvVP2z6qyKW7s6qncE/9gSqZiArw==",
3858
- "dev": true
3859
- },
3860
- "js-tokens": {
3861
- "version": "3.0.2",
3862
- "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz",
3863
- "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=",
3864
- "dev": true
3865
- },
3866
- "js-yaml": {
3867
- "version": "3.7.0",
3868
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.7.0.tgz",
3869
- "integrity": "sha1-XJZ93YN6m/3KXy3oQlOr6KHAO4A=",
3870
- "dev": true,
3871
- "requires": {
3872
- "argparse": "^1.0.7",
3873
- "esprima": "^2.6.0"
3874
- }
3875
- },
3876
- "jsbn": {
3877
- "version": "0.1.1",
3878
- "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
3879
- "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=",
3880
- "dev": true,
3881
- "optional": true
3882
- },
3883
- "jsesc": {
3884
- "version": "1.3.0",
3885
- "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz",
3886
- "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=",
3887
- "dev": true
3888
- },
3889
- "json-loader": {
3890
- "version": "0.5.7",
3891
- "resolved": "https://registry.npmjs.org/json-loader/-/json-loader-0.5.7.tgz",
3892
- "integrity": "sha512-QLPs8Dj7lnf3e3QYS1zkCo+4ZwqOiF9d/nZnYozTISxXWCfNs9yuky5rJw4/W34s7POaNlbZmQGaB5NiXCbP4w==",
3893
- "dev": true
3894
- },
3895
- "json-schema": {
3896
- "version": "0.2.3",
3897
- "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
3898
- "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=",
3899
- "dev": true
3900
- },
3901
- "json-schema-traverse": {
3902
- "version": "0.3.1",
3903
- "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz",
3904
- "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=",
3905
- "dev": true
3906
- },
3907
- "json-stable-stringify": {
3908
- "version": "1.0.1",
3909
- "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz",
3910
- "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=",
3911
- "dev": true,
3912
- "requires": {
3913
- "jsonify": "~0.0.0"
3914
- }
3915
- },
3916
- "json-stringify-safe": {
3917
- "version": "5.0.1",
3918
- "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
3919
- "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=",
3920
- "dev": true
3921
- },
3922
- "json5": {
3923
- "version": "0.5.1",
3924
- "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz",
3925
- "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=",
3926
- "dev": true
3927
- },
3928
- "jsonify": {
3929
- "version": "0.0.0",
3930
- "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz",
3931
- "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=",
3932
- "dev": true
3933
- },
3934
- "jsonpointer": {
3935
- "version": "4.0.1",
3936
- "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.0.1.tgz",
3937
- "integrity": "sha1-T9kss04OnbPInIYi7PUfm5eMbLk=",
3938
- "dev": true
3939
- },
3940
- "jsprim": {
3941
- "version": "1.4.1",
3942
- "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
3943
- "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=",
3944
- "dev": true,
3945
- "requires": {
3946
- "assert-plus": "1.0.0",
3947
- "extsprintf": "1.3.0",
3948
- "json-schema": "0.2.3",
3949
- "verror": "1.10.0"
3950
- },
3951
- "dependencies": {
3952
- "assert-plus": {
3953
- "version": "1.0.0",
3954
- "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
3955
- "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
3956
- "dev": true
3957
- }
3958
- }
3959
- },
3960
- "kind-of": {
3961
- "version": "3.2.2",
3962
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
3963
- "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
3964
- "dev": true,
3965
- "requires": {
3966
- "is-buffer": "^1.1.5"
3967
- }
3968
- },
3969
- "lazy-cache": {
3970
- "version": "0.2.7",
3971
- "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-0.2.7.tgz",
3972
- "integrity": "sha1-f+3fLctu23fRHvHRF6tf/fCrG2U=",
3973
- "dev": true
3974
- },
3975
- "lcid": {
3976
- "version": "1.0.0",
3977
- "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz",
3978
- "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=",
3979
- "dev": true,
3980
- "requires": {
3981
- "invert-kv": "^1.0.0"
3982
- }
3983
- },
3984
- "levn": {
3985
- "version": "0.3.0",
3986
- "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
3987
- "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=",
3988
- "dev": true,
3989
- "requires": {
3990
- "prelude-ls": "~1.1.2",
3991
- "type-check": "~0.3.2"
3992
- }
3993
- },
3994
- "load-json-file": {
3995
- "version": "1.1.0",
3996
- "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
3997
- "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=",
3998
- "dev": true,
3999
- "requires": {
4000
- "graceful-fs": "^4.1.2",
4001
- "parse-json": "^2.2.0",
4002
- "pify": "^2.0.0",
4003
- "pinkie-promise": "^2.0.0",
4004
- "strip-bom": "^2.0.0"
4005
- },
4006
- "dependencies": {
4007
- "pify": {
4008
- "version": "2.3.0",
4009
- "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
4010
- "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
4011
- "dev": true
4012
- },
4013
- "strip-bom": {
4014
- "version": "2.0.0",
4015
- "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz",
4016
- "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=",
4017
- "dev": true,
4018
- "requires": {
4019
- "is-utf8": "^0.2.0"
4020
- }
4021
- }
4022
- }
4023
- },
4024
- "loader-runner": {
4025
- "version": "2.3.0",
4026
- "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.3.0.tgz",
4027
- "integrity": "sha1-9IKuqC1UPgeSFwDVpG7yb9rGuKI=",
4028
- "dev": true
4029
- },
4030
- "loader-utils": {
4031
- "version": "1.1.0",
4032
- "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz",
4033
- "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=",
4034
- "dev": true,
4035
- "requires": {
4036
- "big.js": "^3.1.3",
4037
- "emojis-list": "^2.0.0",
4038
- "json5": "^0.5.0"
4039
- }
4040
- },
4041
- "locate-path": {
4042
- "version": "2.0.0",
4043
- "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz",
4044
- "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=",
4045
- "dev": true,
4046
- "requires": {
4047
- "p-locate": "^2.0.0",
4048
- "path-exists": "^3.0.0"
4049
- }
4050
- },
4051
- "lodash": {
4052
- "version": "4.17.4",
4053
- "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz",
4054
- "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=",
4055
- "dev": true
4056
- },
4057
- "lodash.assign": {
4058
- "version": "4.2.0",
4059
- "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz",
4060
- "integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=",
4061
- "dev": true
4062
- },
4063
- "lodash.camelcase": {
4064
- "version": "4.3.0",
4065
- "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
4066
- "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=",
4067
- "dev": true
4068
- },
4069
- "lodash.clonedeep": {
4070
- "version": "4.5.0",
4071
- "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
4072
- "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=",
4073
- "dev": true
4074
- },
4075
- "lodash.memoize": {
4076
- "version": "4.1.2",
4077
- "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
4078
- "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=",
4079
- "dev": true
4080
- },
4081
- "lodash.mergewith": {
4082
- "version": "4.6.0",
4083
- "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.0.tgz",
4084
- "integrity": "sha1-FQzwoWeR9ZA7iJHqsVRgknS96lU=",
4085
- "dev": true
4086
- },
4087
- "lodash.tail": {
4088
- "version": "4.1.1",
4089
- "resolved": "https://registry.npmjs.org/lodash.tail/-/lodash.tail-4.1.1.tgz",
4090
- "integrity": "sha1-0jM6NtnncXyK0vfKyv7HwytERmQ=",
4091
- "dev": true
4092
- },
4093
- "lodash.uniq": {
4094
- "version": "4.5.0",
4095
- "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
4096
- "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=",
4097
- "dev": true
4098
- },
4099
- "longest": {
4100
- "version": "1.0.1",
4101
- "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz",
4102
- "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=",
4103
- "dev": true
4104
- },
4105
- "loose-envify": {
4106
- "version": "1.3.1",
4107
- "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz",
4108
- "integrity": "sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg=",
4109
- "dev": true,
4110
- "requires": {
4111
- "js-tokens": "^3.0.0"
4112
- }
4113
- },
4114
- "loud-rejection": {
4115
- "version": "1.6.0",
4116
- "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz",
4117
- "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=",
4118
- "dev": true,
4119
- "requires": {
4120
- "currently-unhandled": "^0.4.1",
4121
- "signal-exit": "^3.0.0"
4122
- }
4123
- },
4124
- "lru-cache": {
4125
- "version": "4.1.1",
4126
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.1.tgz",
4127
- "integrity": "sha512-q4spe4KTfsAS1SUHLO0wz8Qiyf1+vMIAgpRYioFYDMNqKfHQbg+AVDH3i4fvpl71/P1L0dBl+fQi+P37UYf0ew==",
4128
- "dev": true,
4129
- "requires": {
4130
- "pseudomap": "^1.0.2",
4131
- "yallist": "^2.1.2"
4132
- }
4133
- },
4134
- "macaddress": {
4135
- "version": "0.2.8",
4136
- "resolved": "https://registry.npmjs.org/macaddress/-/macaddress-0.2.8.tgz",
4137
- "integrity": "sha1-WQTcU3w57G2+/q6QIycTX6hRHxI=",
4138
- "dev": true
4139
- },
4140
- "make-dir": {
4141
- "version": "1.1.0",
4142
- "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.1.0.tgz",
4143
- "integrity": "sha512-0Pkui4wLJ7rxvmfUvs87skoEaxmu0hCUApF8nonzpl7q//FWp9zu8W61Scz4sd/kUiqDxvUhtoam2efDyiBzcA==",
4144
- "dev": true,
4145
- "requires": {
4146
- "pify": "^3.0.0"
4147
- }
4148
- },
4149
- "map-obj": {
4150
- "version": "1.0.1",
4151
- "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz",
4152
- "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=",
4153
- "dev": true
4154
- },
4155
- "math-expression-evaluator": {
4156
- "version": "1.2.17",
4157
- "resolved": "https://registry.npmjs.org/math-expression-evaluator/-/math-expression-evaluator-1.2.17.tgz",
4158
- "integrity": "sha1-3oGf282E3M2PrlnGrreWFbnSZqw=",
4159
- "dev": true
4160
- },
4161
- "md5.js": {
4162
- "version": "1.3.4",
4163
- "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.4.tgz",
4164
- "integrity": "sha1-6b296UogpawYsENA/Fdk1bCdkB0=",
4165
- "dev": true,
4166
- "requires": {
4167
- "hash-base": "^3.0.0",
4168
- "inherits": "^2.0.1"
4169
- },
4170
- "dependencies": {
4171
- "hash-base": {
4172
- "version": "3.0.4",
4173
- "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz",
4174
- "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=",
4175
- "dev": true,
4176
- "requires": {
4177
- "inherits": "^2.0.1",
4178
- "safe-buffer": "^5.0.1"
4179
- }
4180
- }
4181
- }
4182
- },
4183
- "memory-fs": {
4184
- "version": "0.4.1",
4185
- "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz",
4186
- "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=",
4187
- "dev": true,
4188
- "requires": {
4189
- "errno": "^0.1.3",
4190
- "readable-stream": "^2.0.1"
4191
- }
4192
- },
4193
- "meow": {
4194
- "version": "3.7.0",
4195
- "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz",
4196
- "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=",
4197
- "dev": true,
4198
- "requires": {
4199
- "camelcase-keys": "^2.0.0",
4200
- "decamelize": "^1.1.2",
4201
- "loud-rejection": "^1.0.0",
4202
- "map-obj": "^1.0.1",
4203
- "minimist": "^1.1.3",
4204
- "normalize-package-data": "^2.3.4",
4205
- "object-assign": "^4.0.1",
4206
- "read-pkg-up": "^1.0.1",
4207
- "redent": "^1.0.0",
4208
- "trim-newlines": "^1.0.0"
4209
- },
4210
- "dependencies": {
4211
- "minimist": {
4212
- "version": "1.2.0",
4213
- "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
4214
- "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
4215
- "dev": true
4216
- }
4217
- }
4218
- },
4219
- "micromatch": {
4220
- "version": "2.3.11",
4221
- "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz",
4222
- "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=",
4223
- "dev": true,
4224
- "requires": {
4225
- "arr-diff": "^2.0.0",
4226
- "array-unique": "^0.2.1",
4227
- "braces": "^1.8.2",
4228
- "expand-brackets": "^0.1.4",
4229
- "extglob": "^0.3.1",
4230
- "filename-regex": "^2.0.0",
4231
- "is-extglob": "^1.0.0",
4232
- "is-glob": "^2.0.1",
4233
- "kind-of": "^3.0.2",
4234
- "normalize-path": "^2.0.1",
4235
- "object.omit": "^2.0.0",
4236
- "parse-glob": "^3.0.4",
4237
- "regex-cache": "^0.4.2"
4238
- }
4239
- },
4240
- "miller-rabin": {
4241
- "version": "4.0.1",
4242
- "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz",
4243
- "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==",
4244
- "dev": true,
4245
- "requires": {
4246
- "bn.js": "^4.0.0",
4247
- "brorand": "^1.0.1"
4248
- }
4249
- },
4250
- "mime-db": {
4251
- "version": "1.30.0",
4252
- "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.30.0.tgz",
4253
- "integrity": "sha1-dMZD2i3Z1qRTmZY0ZbJtXKfXHwE=",
4254
- "dev": true
4255
- },
4256
- "mime-types": {
4257
- "version": "2.1.17",
4258
- "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz",
4259
- "integrity": "sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo=",
4260
- "dev": true,
4261
- "requires": {
4262
- "mime-db": "~1.30.0"
4263
- }
4264
- },
4265
- "minimalistic-assert": {
4266
- "version": "1.0.0",
4267
- "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz",
4268
- "integrity": "sha1-cCvi3aazf0g2vLP121ZkG2Sh09M=",
4269
- "dev": true
4270
- },
4271
- "minimalistic-crypto-utils": {
4272
- "version": "1.0.1",
4273
- "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz",
4274
- "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=",
4275
- "dev": true
4276
- },
4277
- "minimatch": {
4278
- "version": "3.0.4",
4279
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
4280
- "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
4281
- "dev": true,
4282
- "requires": {
4283
- "brace-expansion": "^1.1.7"
4284
- }
4285
- },
4286
- "minimist": {
4287
- "version": "0.0.8",
4288
- "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
4289
- "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
4290
- "dev": true
4291
- },
4292
- "mixin-object": {
4293
- "version": "2.0.1",
4294
- "resolved": "https://registry.npmjs.org/mixin-object/-/mixin-object-2.0.1.tgz",
4295
- "integrity": "sha1-T7lJRB2rGCVA8f4DW6YOGUel5X4=",
4296
- "dev": true,
4297
- "requires": {
4298
- "for-in": "^0.1.3",
4299
- "is-extendable": "^0.1.1"
4300
- },
4301
- "dependencies": {
4302
- "for-in": {
4303
- "version": "0.1.8",
4304
- "resolved": "https://registry.npmjs.org/for-in/-/for-in-0.1.8.tgz",
4305
- "integrity": "sha1-2Hc5COMSVhCZUrH9ubP6hn0ndeE=",
4306
- "dev": true
4307
- }
4308
- }
4309
- },
4310
- "mkdirp": {
4311
- "version": "0.5.1",
4312
- "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
4313
- "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
4314
- "dev": true,
4315
- "requires": {
4316
- "minimist": "0.0.8"
4317
- }
4318
- },
4319
- "ms": {
4320
- "version": "2.0.0",
4321
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
4322
- "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
4323
- "dev": true
4324
- },
4325
- "mute-stream": {
4326
- "version": "0.0.5",
4327
- "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.5.tgz",
4328
- "integrity": "sha1-j7+rsKmKJT0xhDMfno3rc3L6xsA=",
4329
- "dev": true
4330
- },
4331
- "nan": {
4332
- "version": "2.8.0",
4333
- "resolved": "https://registry.npmjs.org/nan/-/nan-2.8.0.tgz",
4334
- "integrity": "sha1-7XFfP+neArV6XmJS2QqWZ14fCFo=",
4335
- "dev": true
4336
- },
4337
- "natural-compare": {
4338
- "version": "1.4.0",
4339
- "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
4340
- "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=",
4341
- "dev": true
4342
- },
4343
- "node-gyp": {
4344
- "version": "3.6.2",
4345
- "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.6.2.tgz",
4346
- "integrity": "sha1-m/vlRWIoYoSDjnUOrAUpWFP6HGA=",
4347
- "dev": true,
4348
- "requires": {
4349
- "fstream": "^1.0.0",
4350
- "glob": "^7.0.3",
4351
- "graceful-fs": "^4.1.2",
4352
- "minimatch": "^3.0.2",
4353
- "mkdirp": "^0.5.0",
4354
- "nopt": "2 || 3",
4355
- "npmlog": "0 || 1 || 2 || 3 || 4",
4356
- "osenv": "0",
4357
- "request": "2",
4358
- "rimraf": "2",
4359
- "semver": "~5.3.0",
4360
- "tar": "^2.0.0",
4361
- "which": "1"
4362
- },
4363
- "dependencies": {
4364
- "semver": {
4365
- "version": "5.3.0",
4366
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz",
4367
- "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=",
4368
- "dev": true
4369
- }
4370
- }
4371
- },
4372
- "node-libs-browser": {
4373
- "version": "2.1.0",
4374
- "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.1.0.tgz",
4375
- "integrity": "sha512-5AzFzdoIMb89hBGMZglEegffzgRg+ZFoUmisQ8HI4j1KDdpx13J0taNp2y9xPbur6W61gepGDDotGBVQ7mfUCg==",
4376
- "dev": true,
4377
- "requires": {
4378
- "assert": "^1.1.1",
4379
- "browserify-zlib": "^0.2.0",
4380
- "buffer": "^4.3.0",
4381
- "console-browserify": "^1.1.0",
4382
- "constants-browserify": "^1.0.0",
4383
- "crypto-browserify": "^3.11.0",
4384
- "domain-browser": "^1.1.1",
4385
- "events": "^1.0.0",
4386
- "https-browserify": "^1.0.0",
4387
- "os-browserify": "^0.3.0",
4388
- "path-browserify": "0.0.0",
4389
- "process": "^0.11.10",
4390
- "punycode": "^1.2.4",
4391
- "querystring-es3": "^0.2.0",
4392
- "readable-stream": "^2.3.3",
4393
- "stream-browserify": "^2.0.1",
4394
- "stream-http": "^2.7.2",
4395
- "string_decoder": "^1.0.0",
4396
- "timers-browserify": "^2.0.4",
4397
- "tty-browserify": "0.0.0",
4398
- "url": "^0.11.0",
4399
- "util": "^0.10.3",
4400
- "vm-browserify": "0.0.4"
4401
- }
4402
- },
4403
- "node-sass": {
4404
- "version": "4.7.2",
4405
- "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.7.2.tgz",
4406
- "integrity": "sha512-CaV+wLqZ7//Jdom5aUFCpGNoECd7BbNhjuwdsX/LkXBrHl8eb1Wjw4HvWqcFvhr5KuNgAk8i/myf/MQ1YYeroA==",
4407
- "dev": true,
4408
- "requires": {
4409
- "async-foreach": "^0.1.3",
4410
- "chalk": "^1.1.1",
4411
- "cross-spawn": "^3.0.0",
4412
- "gaze": "^1.0.0",
4413
- "get-stdin": "^4.0.1",
4414
- "glob": "^7.0.3",
4415
- "in-publish": "^2.0.0",
4416
- "lodash.assign": "^4.2.0",
4417
- "lodash.clonedeep": "^4.3.2",
4418
- "lodash.mergewith": "^4.6.0",
4419
- "meow": "^3.7.0",
4420
- "mkdirp": "^0.5.1",
4421
- "nan": "^2.3.2",
4422
- "node-gyp": "^3.3.1",
4423
- "npmlog": "^4.0.0",
4424
- "request": "~2.79.0",
4425
- "sass-graph": "^2.2.4",
4426
- "stdout-stream": "^1.4.0",
4427
- "true-case-path": "^1.0.2"
4428
- }
4429
- },
4430
- "nopt": {
4431
- "version": "3.0.6",
4432
- "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz",
4433
- "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=",
4434
- "dev": true,
4435
- "requires": {
4436
- "abbrev": "1"
4437
- }
4438
- },
4439
- "normalize-package-data": {
4440
- "version": "2.4.0",
4441
- "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz",
4442
- "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==",
4443
- "dev": true,
4444
- "requires": {
4445
- "hosted-git-info": "^2.1.4",
4446
- "is-builtin-module": "^1.0.0",
4447
- "semver": "2 || 3 || 4 || 5",
4448
- "validate-npm-package-license": "^3.0.1"
4449
- }
4450
- },
4451
- "normalize-path": {
4452
- "version": "2.1.1",
4453
- "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
4454
- "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=",
4455
- "dev": true,
4456
- "requires": {
4457
- "remove-trailing-separator": "^1.0.1"
4458
- }
4459
- },
4460
- "normalize-range": {
4461
- "version": "0.1.2",
4462
- "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz",
4463
- "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=",
4464
- "dev": true
4465
- },
4466
- "normalize-url": {
4467
- "version": "1.9.1",
4468
- "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-1.9.1.tgz",
4469
- "integrity": "sha1-LMDWazHqIwNkWENuNiDYWVTGbDw=",
4470
- "dev": true,
4471
- "requires": {
4472
- "object-assign": "^4.0.1",
4473
- "prepend-http": "^1.0.0",
4474
- "query-string": "^4.1.0",
4475
- "sort-keys": "^1.0.0"
4476
- }
4477
- },
4478
- "npm": {
4479
- "version": "6.3.0",
4480
- "resolved": "https://registry.npmjs.org/npm/-/npm-6.3.0.tgz",
4481
- "integrity": "sha512-oDtLFo3wXue/xe3pU/oks9VHS5501OAWlYrZrApZkFv7l2LXk+9CfPMbjbfZWK7Jqlc1jbNcJMkB6KZC7K/vEA==",
4482
- "requires": {
4483
- "JSONStream": "^1.3.3",
4484
- "abbrev": "~1.1.1",
4485
- "ansicolors": "~0.3.2",
4486
- "ansistyles": "~0.1.3",
4487
- "aproba": "~1.2.0",
4488
- "archy": "~1.0.0",
4489
- "bin-links": "^1.1.2",
4490
- "bluebird": "~3.5.1",
4491
- "byte-size": "^4.0.3",
4492
- "cacache": "^11.1.0",
4493
- "call-limit": "~1.1.0",
4494
- "chownr": "~1.0.1",
4495
- "cli-columns": "^3.1.2",
4496
- "cli-table3": "^0.5.0",
4497
- "cmd-shim": "~2.0.2",
4498
- "columnify": "~1.5.4",
4499
- "config-chain": "~1.1.11",
4500
- "debuglog": "*",
4501
- "detect-indent": "~5.0.0",
4502
- "detect-newline": "^2.1.0",
4503
- "dezalgo": "~1.0.3",
4504
- "editor": "~1.0.0",
4505
- "figgy-pudding": "^3.2.0",
4506
- "find-npm-prefix": "^1.0.2",
4507
- "fs-vacuum": "~1.2.10",
4508
- "fs-write-stream-atomic": "~1.0.10",
4509
- "gentle-fs": "^2.0.1",
4510
- "glob": "~7.1.2",
4511
- "graceful-fs": "~4.1.11",
4512
- "has-unicode": "~2.0.1",
4513
- "hosted-git-info": "^2.6.0",
4514
- "iferr": "^1.0.0",
4515
- "imurmurhash": "*",
4516
- "inflight": "~1.0.6",
4517
- "inherits": "~2.0.3",
4518
- "ini": "^1.3.5",
4519
- "init-package-json": "^1.10.3",
4520
- "is-cidr": "^2.0.6",
4521
- "json-parse-better-errors": "^1.0.2",
4522
- "lazy-property": "~1.0.0",
4523
- "libcipm": "^2.0.0",
4524
- "libnpmhook": "^4.0.1",
4525
- "libnpx": "^10.2.0",
4526
- "lock-verify": "^2.0.2",
4527
- "lockfile": "^1.0.4",
4528
- "lodash._baseindexof": "*",
4529
- "lodash._baseuniq": "~4.6.0",
4530
- "lodash._bindcallback": "*",
4531
- "lodash._cacheindexof": "*",
4532
- "lodash._createcache": "*",
4533
- "lodash._getnative": "*",
4534
- "lodash.clonedeep": "~4.5.0",
4535
- "lodash.restparam": "*",
4536
- "lodash.union": "~4.6.0",
4537
- "lodash.uniq": "~4.5.0",
4538
- "lodash.without": "~4.4.0",
4539
- "lru-cache": "^4.1.3",
4540
- "meant": "~1.0.1",
4541
- "mississippi": "^3.0.0",
4542
- "mkdirp": "~0.5.1",
4543
- "move-concurrently": "^1.0.1",
4544
- "node-gyp": "^3.7.0",
4545
- "nopt": "~4.0.1",
4546
- "normalize-package-data": "~2.4.0",
4547
- "npm-audit-report": "^1.3.1",
4548
- "npm-cache-filename": "~1.0.2",
4549
- "npm-install-checks": "~3.0.0",
4550
- "npm-lifecycle": "^2.0.3",
4551
- "npm-package-arg": "^6.1.0",
4552
- "npm-packlist": "~1.1.10",
4553
- "npm-pick-manifest": "^2.1.0",
4554
- "npm-profile": "^3.0.2",
4555
- "npm-registry-client": "^8.5.1",
4556
- "npm-registry-fetch": "^1.1.0",
4557
- "npm-user-validate": "~1.0.0",
4558
- "npmlog": "~4.1.2",
4559
- "once": "~1.4.0",
4560
- "opener": "~1.4.3",
4561
- "osenv": "^0.1.5",
4562
- "pacote": "^8.1.6",
4563
- "path-is-inside": "~1.0.2",
4564
- "promise-inflight": "~1.0.1",
4565
- "qrcode-terminal": "^0.12.0",
4566
- "query-string": "^6.1.0",
4567
- "qw": "~1.0.1",
4568
- "read": "~1.0.7",
4569
- "read-cmd-shim": "~1.0.1",
4570
- "read-installed": "~4.0.3",
4571
- "read-package-json": "^2.0.13",
4572
- "read-package-tree": "^5.2.1",
4573
- "readable-stream": "^2.3.6",
4574
- "readdir-scoped-modules": "*",
4575
- "request": "^2.81.0",
4576
- "retry": "^0.12.0",
4577
- "rimraf": "~2.6.2",
4578
- "safe-buffer": "^5.1.2",
4579
- "semver": "^5.5.0",
4580
- "sha": "~2.0.1",
4581
- "slide": "~1.1.6",
4582
- "sorted-object": "~2.0.1",
4583
- "sorted-union-stream": "~2.1.3",
4584
- "ssri": "^6.0.0",
4585
- "stringify-package": "^1.0.0",
4586
- "tar": "^4.4.4",
4587
- "text-table": "~0.2.0",
4588
- "tiny-relative-date": "^1.3.0",
4589
- "uid-number": "0.0.6",
4590
- "umask": "~1.1.0",
4591
- "unique-filename": "~1.1.0",
4592
- "unpipe": "~1.0.0",
4593
- "update-notifier": "^2.5.0",
4594
- "uuid": "^3.3.2",
4595
- "validate-npm-package-license": "^3.0.3",
4596
- "validate-npm-package-name": "~3.0.0",
4597
- "which": "^1.3.1",
4598
- "worker-farm": "^1.6.0",
4599
- "write-file-atomic": "^2.3.0"
4600
- },
4601
- "dependencies": {
4602
- "JSONStream": {
4603
- "version": "1.3.3",
4604
- "bundled": true,
4605
- "requires": {
4606
- "jsonparse": "^1.2.0",
4607
- "through": ">=2.2.7 <3"
4608
- }
4609
- },
4610
- "abbrev": {
4611
- "version": "1.1.1",
4612
- "bundled": true
4613
- },
4614
- "agent-base": {
4615
- "version": "4.2.0",
4616
- "bundled": true,
4617
- "requires": {
4618
- "es6-promisify": "^5.0.0"
4619
- }
4620
- },
4621
- "agentkeepalive": {
4622
- "version": "3.4.1",
4623
- "bundled": true,
4624
- "requires": {
4625
- "humanize-ms": "^1.2.1"
4626
- }
4627
- },
4628
- "ansi-align": {
4629
- "version": "2.0.0",
4630
- "bundled": true,
4631
- "requires": {
4632
- "string-width": "^2.0.0"
4633
- }
4634
- },
4635
- "ansi-regex": {
4636
- "version": "2.1.1",
4637
- "bundled": true
4638
- },
4639
- "ansi-styles": {
4640
- "version": "3.2.1",
4641
- "bundled": true,
4642
- "requires": {
4643
- "color-convert": "^1.9.0"
4644
- }
4645
- },
4646
- "ansicolors": {
4647
- "version": "0.3.2",
4648
- "bundled": true
4649
- },
4650
- "ansistyles": {
4651
- "version": "0.1.3",
4652
- "bundled": true
4653
- },
4654
- "aproba": {
4655
- "version": "1.2.0",
4656
- "bundled": true
4657
- },
4658
- "archy": {
4659
- "version": "1.0.0",
4660
- "bundled": true
4661
- },
4662
- "are-we-there-yet": {
4663
- "version": "1.1.4",
4664
- "bundled": true,
4665
- "requires": {
4666
- "delegates": "^1.0.0",
4667
- "readable-stream": "^2.0.6"
4668
- }
4669
- },
4670
- "asap": {
4671
- "version": "2.0.6",
4672
- "bundled": true
4673
- },
4674
- "asn1": {
4675
- "version": "0.2.3",
4676
- "bundled": true
4677
- },
4678
- "assert-plus": {
4679
- "version": "0.2.0",
4680
- "bundled": true
4681
- },
4682
- "asynckit": {
4683
- "version": "0.4.0",
4684
- "bundled": true
4685
- },
4686
- "aws-sign2": {
4687
- "version": "0.6.0",
4688
- "bundled": true
4689
- },
4690
- "aws4": {
4691
- "version": "1.7.0",
4692
- "bundled": true
4693
- },
4694
- "balanced-match": {
4695
- "version": "1.0.0",
4696
- "bundled": true
4697
- },
4698
- "bcrypt-pbkdf": {
4699
- "version": "1.0.2",
4700
- "bundled": true,
4701
- "optional": true,
4702
- "requires": {
4703
- "tweetnacl": "^0.14.3"
4704
- }
4705
- },
4706
- "bin-links": {
4707
- "version": "1.1.2",
4708
- "bundled": true,
4709
- "requires": {
4710
- "bluebird": "^3.5.0",
4711
- "cmd-shim": "^2.0.2",
4712
- "gentle-fs": "^2.0.0",
4713
- "graceful-fs": "^4.1.11",
4714
- "write-file-atomic": "^2.3.0"
4715
- }
4716
- },
4717
- "block-stream": {
4718
- "version": "0.0.9",
4719
- "bundled": true,
4720
- "requires": {
4721
- "inherits": "~2.0.0"
4722
- }
4723
- },
4724
- "bluebird": {
4725
- "version": "3.5.1",
4726
- "bundled": true
4727
- },
4728
- "boom": {
4729
- "version": "2.10.1",
4730
- "bundled": true,
4731
- "requires": {
4732
- "hoek": "2.x.x"
4733
- }
4734
- },
4735
- "boxen": {
4736
- "version": "1.3.0",
4737
- "bundled": true,
4738
- "requires": {
4739
- "ansi-align": "^2.0.0",
4740
- "camelcase": "^4.0.0",
4741
- "chalk": "^2.0.1",
4742
- "cli-boxes": "^1.0.0",
4743
- "string-width": "^2.0.0",
4744
- "term-size": "^1.2.0",
4745
- "widest-line": "^2.0.0"
4746
- }
4747
- },
4748
- "brace-expansion": {
4749
- "version": "1.1.11",
4750
- "bundled": true,
4751
- "requires": {
4752
- "balanced-match": "^1.0.0",
4753
- "concat-map": "0.0.1"
4754
- }
4755
- },
4756
- "buffer-from": {
4757
- "version": "1.0.0",
4758
- "bundled": true
4759
- },
4760
- "builtin-modules": {
4761
- "version": "1.1.1",
4762
- "bundled": true
4763
- },
4764
- "builtins": {
4765
- "version": "1.0.3",
4766
- "bundled": true
4767
- },
4768
- "byline": {
4769
- "version": "5.0.0",
4770
- "bundled": true
4771
- },
4772
- "byte-size": {
4773
- "version": "4.0.3",
4774
- "bundled": true
4775
- },
4776
- "cacache": {
4777
- "version": "11.1.0",
4778
- "bundled": true,
4779
- "requires": {
4780
- "bluebird": "^3.5.1",
4781
- "chownr": "^1.0.1",
4782
- "figgy-pudding": "^3.1.0",
4783
- "glob": "^7.1.2",
4784
- "graceful-fs": "^4.1.11",
4785
- "lru-cache": "^4.1.3",
4786
- "mississippi": "^3.0.0",
4787
- "mkdirp": "^0.5.1",
4788
- "move-concurrently": "^1.0.1",
4789
- "promise-inflight": "^1.0.1",
4790
- "rimraf": "^2.6.2",
4791
- "ssri": "^6.0.0",
4792
- "unique-filename": "^1.1.0",
4793
- "y18n": "^4.0.0"
4794
- }
4795
- },
4796
- "call-limit": {
4797
- "version": "1.1.0",
4798
- "bundled": true
4799
- },
4800
- "camelcase": {
4801
- "version": "4.1.0",
4802
- "bundled": true
4803
- },
4804
- "capture-stack-trace": {
4805
- "version": "1.0.0",
4806
- "bundled": true
4807
- },
4808
- "caseless": {
4809
- "version": "0.12.0",
4810
- "bundled": true
4811
- },
4812
- "chalk": {
4813
- "version": "2.4.1",
4814
- "bundled": true,
4815
- "requires": {
4816
- "ansi-styles": "^3.2.1",
4817
- "escape-string-regexp": "^1.0.5",
4818
- "supports-color": "^5.3.0"
4819
- }
4820
- },
4821
- "chownr": {
4822
- "version": "1.0.1",
4823
- "bundled": true
4824
- },
4825
- "ci-info": {
4826
- "version": "1.1.3",
4827
- "bundled": true
4828
- },
4829
- "cidr-regex": {
4830
- "version": "2.0.9",
4831
- "bundled": true,
4832
- "requires": {
4833
- "ip-regex": "^2.1.0"
4834
- }
4835
- },
4836
- "cli-boxes": {
4837
- "version": "1.0.0",
4838
- "bundled": true
4839
- },
4840
- "cli-columns": {
4841
- "version": "3.1.2",
4842
- "bundled": true,
4843
- "requires": {
4844
- "string-width": "^2.0.0",
4845
- "strip-ansi": "^3.0.1"
4846
- }
4847
- },
4848
- "cli-table3": {
4849
- "version": "0.5.0",
4850
- "bundled": true,
4851
- "requires": {
4852
- "colors": "^1.1.2",
4853
- "object-assign": "^4.1.0",
4854
- "string-width": "^2.1.1"
4855
- }
4856
- },
4857
- "cliui": {
4858
- "version": "4.1.0",
4859
- "bundled": true,
4860
- "requires": {
4861
- "string-width": "^2.1.1",
4862
- "strip-ansi": "^4.0.0",
4863
- "wrap-ansi": "^2.0.0"
4864
- },
4865
- "dependencies": {
4866
- "ansi-regex": {
4867
- "version": "3.0.0",
4868
- "bundled": true
4869
- },
4870
- "strip-ansi": {
4871
- "version": "4.0.0",
4872
- "bundled": true,
4873
- "requires": {
4874
- "ansi-regex": "^3.0.0"
4875
- }
4876
- }
4877
- }
4878
- },
4879
- "clone": {
4880
- "version": "1.0.4",
4881
- "bundled": true
4882
- },
4883
- "cmd-shim": {
4884
- "version": "2.0.2",
4885
- "bundled": true,
4886
- "requires": {
4887
- "graceful-fs": "^4.1.2",
4888
- "mkdirp": "~0.5.0"
4889
- }
4890
- },
4891
- "co": {
4892
- "version": "4.6.0",
4893
- "bundled": true
4894
- },
4895
- "code-point-at": {
4896
- "version": "1.1.0",
4897
- "bundled": true
4898
- },
4899
- "color-convert": {
4900
- "version": "1.9.1",
4901
- "bundled": true,
4902
- "requires": {
4903
- "color-name": "^1.1.1"
4904
- }
4905
- },
4906
- "color-name": {
4907
- "version": "1.1.3",
4908
- "bundled": true
4909
- },
4910
- "colors": {
4911
- "version": "1.3.0",
4912
- "bundled": true,
4913
- "optional": true
4914
- },
4915
- "columnify": {
4916
- "version": "1.5.4",
4917
- "bundled": true,
4918
- "requires": {
4919
- "strip-ansi": "^3.0.0",
4920
- "wcwidth": "^1.0.0"
4921
- }
4922
- },
4923
- "combined-stream": {
4924
- "version": "1.0.6",
4925
- "bundled": true,
4926
- "requires": {
4927
- "delayed-stream": "~1.0.0"
4928
- }
4929
- },
4930
- "concat-map": {
4931
- "version": "0.0.1",
4932
- "bundled": true
4933
- },
4934
- "concat-stream": {
4935
- "version": "1.6.2",
4936
- "bundled": true,
4937
- "requires": {
4938
- "buffer-from": "^1.0.0",
4939
- "inherits": "^2.0.3",
4940
- "readable-stream": "^2.2.2",
4941
- "typedarray": "^0.0.6"
4942
- }
4943
- },
4944
- "config-chain": {
4945
- "version": "1.1.11",
4946
- "bundled": true,
4947
- "requires": {
4948
- "ini": "^1.3.4",
4949
- "proto-list": "~1.2.1"
4950
- }
4951
- },
4952
- "configstore": {
4953
- "version": "3.1.2",
4954
- "bundled": true,
4955
- "requires": {
4956
- "dot-prop": "^4.1.0",
4957
- "graceful-fs": "^4.1.2",
4958
- "make-dir": "^1.0.0",
4959
- "unique-string": "^1.0.0",
4960
- "write-file-atomic": "^2.0.0",
4961
- "xdg-basedir": "^3.0.0"
4962
- }
4963
- },
4964
- "console-control-strings": {
4965
- "version": "1.1.0",
4966
- "bundled": true
4967
- },
4968
- "copy-concurrently": {
4969
- "version": "1.0.5",
4970
- "bundled": true,
4971
- "requires": {
4972
- "aproba": "^1.1.1",
4973
- "fs-write-stream-atomic": "^1.0.8",
4974
- "iferr": "^0.1.5",
4975
- "mkdirp": "^0.5.1",
4976
- "rimraf": "^2.5.4",
4977
- "run-queue": "^1.0.0"
4978
- },
4979
- "dependencies": {
4980
- "iferr": {
4981
- "version": "0.1.5",
4982
- "bundled": true
4983
- }
4984
- }
4985
- },
4986
- "core-util-is": {
4987
- "version": "1.0.2",
4988
- "bundled": true
4989
- },
4990
- "create-error-class": {
4991
- "version": "3.0.2",
4992
- "bundled": true,
4993
- "requires": {
4994
- "capture-stack-trace": "^1.0.0"
4995
- }
4996
- },
4997
- "cross-spawn": {
4998
- "version": "5.1.0",
4999
- "bundled": true,
5000
- "requires": {
5001
- "lru-cache": "^4.0.1",
5002
- "shebang-command": "^1.2.0",
5003
- "which": "^1.2.9"
5004
- }
5005
- },
5006
- "cryptiles": {
5007
- "version": "2.0.5",
5008
- "bundled": true,
5009
- "requires": {
5010
- "boom": "2.x.x"
5011
- }
5012
- },
5013
- "crypto-random-string": {
5014
- "version": "1.0.0",
5015
- "bundled": true
5016
- },
5017
- "cyclist": {
5018
- "version": "0.2.2",
5019
- "bundled": true
5020
- },
5021
- "dashdash": {
5022
- "version": "1.14.1",
5023
- "bundled": true,
5024
- "requires": {
5025
- "assert-plus": "^1.0.0"
5026
- },
5027
- "dependencies": {
5028
- "assert-plus": {
5029
- "version": "1.0.0",
5030
- "bundled": true
5031
- }
5032
- }
5033
- },
5034
- "debug": {
5035
- "version": "3.1.0",
5036
- "bundled": true,
5037
- "requires": {
5038
- "ms": "2.0.0"
5039
- },
5040
- "dependencies": {
5041
- "ms": {
5042
- "version": "2.0.0",
5043
- "bundled": true
5044
- }
5045
- }
5046
- },
5047
- "debuglog": {
5048
- "version": "1.0.1",
5049
- "bundled": true
5050
- },
5051
- "decamelize": {
5052
- "version": "1.2.0",
5053
- "bundled": true
5054
- },
5055
- "decode-uri-component": {
5056
- "version": "0.2.0",
5057
- "bundled": true
5058
- },
5059
- "deep-extend": {
5060
- "version": "0.5.1",
5061
- "bundled": true
5062
- },
5063
- "defaults": {
5064
- "version": "1.0.3",
5065
- "bundled": true,
5066
- "requires": {
5067
- "clone": "^1.0.2"
5068
- }
5069
- },
5070
- "delayed-stream": {
5071
- "version": "1.0.0",
5072
- "bundled": true
5073
- },
5074
- "delegates": {
5075
- "version": "1.0.0",
5076
- "bundled": true
5077
- },
5078
- "detect-indent": {
5079
- "version": "5.0.0",
5080
- "bundled": true
5081
- },
5082
- "detect-newline": {
5083
- "version": "2.1.0",
5084
- "bundled": true
5085
- },
5086
- "dezalgo": {
5087
- "version": "1.0.3",
5088
- "bundled": true,
5089
- "requires": {
5090
- "asap": "^2.0.0",
5091
- "wrappy": "1"
5092
- }
5093
- },
5094
- "dot-prop": {
5095
- "version": "4.2.0",
5096
- "bundled": true,
5097
- "requires": {
5098
- "is-obj": "^1.0.0"
5099
- }
5100
- },
5101
- "dotenv": {
5102
- "version": "5.0.1",
5103
- "bundled": true
5104
- },
5105
- "duplexer3": {
5106
- "version": "0.1.4",
5107
- "bundled": true
5108
- },
5109
- "duplexify": {
5110
- "version": "3.6.0",
5111
- "bundled": true,
5112
- "requires": {
5113
- "end-of-stream": "^1.0.0",
5114
- "inherits": "^2.0.1",
5115
- "readable-stream": "^2.0.0",
5116
- "stream-shift": "^1.0.0"
5117
- }
5118
- },
5119
- "ecc-jsbn": {
5120
- "version": "0.1.1",
5121
- "bundled": true,
5122
- "optional": true,
5123
- "requires": {
5124
- "jsbn": "~0.1.0"
5125
- }
5126
- },
5127
- "editor": {
5128
- "version": "1.0.0",
5129
- "bundled": true
5130
- },
5131
- "encoding": {
5132
- "version": "0.1.12",
5133
- "bundled": true,
5134
- "requires": {
5135
- "iconv-lite": "~0.4.13"
5136
- }
5137
- },
5138
- "end-of-stream": {
5139
- "version": "1.4.1",
5140
- "bundled": true,
5141
- "requires": {
5142
- "once": "^1.4.0"
5143
- }
5144
- },
5145
- "err-code": {
5146
- "version": "1.1.2",
5147
- "bundled": true
5148
- },
5149
- "errno": {
5150
- "version": "0.1.7",
5151
- "bundled": true,
5152
- "requires": {
5153
- "prr": "~1.0.1"
5154
- }
5155
- },
5156
- "es6-promise": {
5157
- "version": "4.2.4",
5158
- "bundled": true
5159
- },
5160
- "es6-promisify": {
5161
- "version": "5.0.0",
5162
- "bundled": true,
5163
- "requires": {
5164
- "es6-promise": "^4.0.3"
5165
- }
5166
- },
5167
- "escape-string-regexp": {
5168
- "version": "1.0.5",
5169
- "bundled": true
5170
- },
5171
- "execa": {
5172
- "version": "0.7.0",
5173
- "bundled": true,
5174
- "requires": {
5175
- "cross-spawn": "^5.0.1",
5176
- "get-stream": "^3.0.0",
5177
- "is-stream": "^1.1.0",
5178
- "npm-run-path": "^2.0.0",
5179
- "p-finally": "^1.0.0",
5180
- "signal-exit": "^3.0.0",
5181
- "strip-eof": "^1.0.0"
5182
- }
5183
- },
5184
- "extend": {
5185
- "version": "3.0.1",
5186
- "bundled": true
5187
- },
5188
- "extsprintf": {
5189
- "version": "1.3.0",
5190
- "bundled": true
5191
- },
5192
- "figgy-pudding": {
5193
- "version": "3.2.0",
5194
- "bundled": true
5195
- },
5196
- "find-npm-prefix": {
5197
- "version": "1.0.2",
5198
- "bundled": true
5199
- },
5200
- "find-up": {
5201
- "version": "2.1.0",
5202
- "bundled": true,
5203
- "requires": {
5204
- "locate-path": "^2.0.0"
5205
- }
5206
- },
5207
- "flush-write-stream": {
5208
- "version": "1.0.3",
5209
- "bundled": true,
5210
- "requires": {
5211
- "inherits": "^2.0.1",
5212
- "readable-stream": "^2.0.4"
5213
- }
5214
- },
5215
- "forever-agent": {
5216
- "version": "0.6.1",
5217
- "bundled": true
5218
- },
5219
- "form-data": {
5220
- "version": "2.1.4",
5221
- "bundled": true,
5222
- "requires": {
5223
- "asynckit": "^0.4.0",
5224
- "combined-stream": "^1.0.5",
5225
- "mime-types": "^2.1.12"
5226
- }
5227
- },
5228
- "from2": {
5229
- "version": "2.3.0",
5230
- "bundled": true,
5231
- "requires": {
5232
- "inherits": "^2.0.1",
5233
- "readable-stream": "^2.0.0"
5234
- }
5235
- },
5236
- "fs-minipass": {
5237
- "version": "1.2.5",
5238
- "bundled": true,
5239
- "requires": {
5240
- "minipass": "^2.2.1"
5241
- }
5242
- },
5243
- "fs-vacuum": {
5244
- "version": "1.2.10",
5245
- "bundled": true,
5246
- "requires": {
5247
- "graceful-fs": "^4.1.2",
5248
- "path-is-inside": "^1.0.1",
5249
- "rimraf": "^2.5.2"
5250
- }
5251
- },
5252
- "fs-write-stream-atomic": {
5253
- "version": "1.0.10",
5254
- "bundled": true,
5255
- "requires": {
5256
- "graceful-fs": "^4.1.2",
5257
- "iferr": "^0.1.5",
5258
- "imurmurhash": "^0.1.4",
5259
- "readable-stream": "1 || 2"
5260
- },
5261
- "dependencies": {
5262
- "iferr": {
5263
- "version": "0.1.5",
5264
- "bundled": true
5265
- }
5266
- }
5267
- },
5268
- "fs.realpath": {
5269
- "version": "1.0.0",
5270
- "bundled": true
5271
- },
5272
- "fstream": {
5273
- "version": "1.0.11",
5274
- "bundled": true,
5275
- "requires": {
5276
- "graceful-fs": "^4.1.2",
5277
- "inherits": "~2.0.0",
5278
- "mkdirp": ">=0.5 0",
5279
- "rimraf": "2"
5280
- }
5281
- },
5282
- "gauge": {
5283
- "version": "2.7.4",
5284
- "bundled": true,
5285
- "requires": {
5286
- "aproba": "^1.0.3",
5287
- "console-control-strings": "^1.0.0",
5288
- "has-unicode": "^2.0.0",
5289
- "object-assign": "^4.1.0",
5290
- "signal-exit": "^3.0.0",
5291
- "string-width": "^1.0.1",
5292
- "strip-ansi": "^3.0.1",
5293
- "wide-align": "^1.1.0"
5294
- },
5295
- "dependencies": {
5296
- "string-width": {
5297
- "version": "1.0.2",
5298
- "bundled": true,
5299
- "requires": {
5300
- "code-point-at": "^1.0.0",
5301
- "is-fullwidth-code-point": "^1.0.0",
5302
- "strip-ansi": "^3.0.0"
5303
- }
5304
- }
5305
- }
5306
- },
5307
- "genfun": {
5308
- "version": "4.0.1",
5309
- "bundled": true
5310
- },
5311
- "gentle-fs": {
5312
- "version": "2.0.1",
5313
- "bundled": true,
5314
- "requires": {
5315
- "aproba": "^1.1.2",
5316
- "fs-vacuum": "^1.2.10",
5317
- "graceful-fs": "^4.1.11",
5318
- "iferr": "^0.1.5",
5319
- "mkdirp": "^0.5.1",
5320
- "path-is-inside": "^1.0.2",
5321
- "read-cmd-shim": "^1.0.1",
5322
- "slide": "^1.1.6"
5323
- },
5324
- "dependencies": {
5325
- "iferr": {
5326
- "version": "0.1.5",
5327
- "bundled": true
5328
- }
5329
- }
5330
- },
5331
- "get-caller-file": {
5332
- "version": "1.0.2",
5333
- "bundled": true
5334
- },
5335
- "get-stream": {
5336
- "version": "3.0.0",
5337
- "bundled": true
5338
- },
5339
- "getpass": {
5340
- "version": "0.1.7",
5341
- "bundled": true,
5342
- "requires": {
5343
- "assert-plus": "^1.0.0"
5344
- },
5345
- "dependencies": {
5346
- "assert-plus": {
5347
- "version": "1.0.0",
5348
- "bundled": true
5349
- }
5350
- }
5351
- },
5352
- "glob": {
5353
- "version": "7.1.2",
5354
- "bundled": true,
5355
- "requires": {
5356
- "fs.realpath": "^1.0.0",
5357
- "inflight": "^1.0.4",
5358
- "inherits": "2",
5359
- "minimatch": "^3.0.4",
5360
- "once": "^1.3.0",
5361
- "path-is-absolute": "^1.0.0"
5362
- }
5363
- },
5364
- "global-dirs": {
5365
- "version": "0.1.1",
5366
- "bundled": true,
5367
- "requires": {
5368
- "ini": "^1.3.4"
5369
- }
5370
- },
5371
- "got": {
5372
- "version": "6.7.1",
5373
- "bundled": true,
5374
- "requires": {
5375
- "create-error-class": "^3.0.0",
5376
- "duplexer3": "^0.1.4",
5377
- "get-stream": "^3.0.0",
5378
- "is-redirect": "^1.0.0",
5379
- "is-retry-allowed": "^1.0.0",
5380
- "is-stream": "^1.0.0",
5381
- "lowercase-keys": "^1.0.0",
5382
- "safe-buffer": "^5.0.1",
5383
- "timed-out": "^4.0.0",
5384
- "unzip-response": "^2.0.1",
5385
- "url-parse-lax": "^1.0.0"
5386
- }
5387
- },
5388
- "graceful-fs": {
5389
- "version": "4.1.11",
5390
- "bundled": true
5391
- },
5392
- "har-schema": {
5393
- "version": "1.0.5",
5394
- "bundled": true
5395
- },
5396
- "har-validator": {
5397
- "version": "4.2.1",
5398
- "bundled": true,
5399
- "requires": {
5400
- "ajv": "^4.9.1",
5401
- "har-schema": "^1.0.5"
5402
- },
5403
- "dependencies": {
5404
- "ajv": {
5405
- "version": "4.11.8",
5406
- "bundled": true,
5407
- "requires": {
5408
- "co": "^4.6.0",
5409
- "json-stable-stringify": "^1.0.1"
5410
- }
5411
- }
5412
- }
5413
- },
5414
- "has-flag": {
5415
- "version": "3.0.0",
5416
- "bundled": true
5417
- },
5418
- "has-unicode": {
5419
- "version": "2.0.1",
5420
- "bundled": true
5421
- },
5422
- "hawk": {
5423
- "version": "3.1.3",
5424
- "bundled": true,
5425
- "requires": {
5426
- "boom": "2.x.x",
5427
- "cryptiles": "2.x.x",
5428
- "hoek": "2.x.x",
5429
- "sntp": "1.x.x"
5430
- }
5431
- },
5432
- "hoek": {
5433
- "version": "2.16.3",
5434
- "bundled": true
5435
- },
5436
- "hosted-git-info": {
5437
- "version": "2.6.0",
5438
- "bundled": true
5439
- },
5440
- "http-cache-semantics": {
5441
- "version": "3.8.1",
5442
- "bundled": true
5443
- },
5444
- "http-proxy-agent": {
5445
- "version": "2.1.0",
5446
- "bundled": true,
5447
- "requires": {
5448
- "agent-base": "4",
5449
- "debug": "3.1.0"
5450
- }
5451
- },
5452
- "http-signature": {
5453
- "version": "1.1.1",
5454
- "bundled": true,
5455
- "requires": {
5456
- "assert-plus": "^0.2.0",
5457
- "jsprim": "^1.2.2",
5458
- "sshpk": "^1.7.0"
5459
- }
5460
- },
5461
- "https-proxy-agent": {
5462
- "version": "2.2.1",
5463
- "bundled": true,
5464
- "requires": {
5465
- "agent-base": "^4.1.0",
5466
- "debug": "^3.1.0"
5467
- }
5468
- },
5469
- "humanize-ms": {
5470
- "version": "1.2.1",
5471
- "bundled": true,
5472
- "requires": {
5473
- "ms": "^2.0.0"
5474
- }
5475
- },
5476
- "iconv-lite": {
5477
- "version": "0.4.23",
5478
- "bundled": true,
5479
- "requires": {
5480
- "safer-buffer": ">= 2.1.2 < 3"
5481
- }
5482
- },
5483
- "iferr": {
5484
- "version": "1.0.0",
5485
- "bundled": true
5486
- },
5487
- "ignore-walk": {
5488
- "version": "3.0.1",
5489
- "bundled": true,
5490
- "requires": {
5491
- "minimatch": "^3.0.4"
5492
- }
5493
- },
5494
- "import-lazy": {
5495
- "version": "2.1.0",
5496
- "bundled": true
5497
- },
5498
- "imurmurhash": {
5499
- "version": "0.1.4",
5500
- "bundled": true
5501
- },
5502
- "inflight": {
5503
- "version": "1.0.6",
5504
- "bundled": true,
5505
- "requires": {
5506
- "once": "^1.3.0",
5507
- "wrappy": "1"
5508
- }
5509
- },
5510
- "inherits": {
5511
- "version": "2.0.3",
5512
- "bundled": true
5513
- },
5514
- "ini": {
5515
- "version": "1.3.5",
5516
- "bundled": true
5517
- },
5518
- "init-package-json": {
5519
- "version": "1.10.3",
5520
- "bundled": true,
5521
- "requires": {
5522
- "glob": "^7.1.1",
5523
- "npm-package-arg": "^4.0.0 || ^5.0.0 || ^6.0.0",
5524
- "promzard": "^0.3.0",
5525
- "read": "~1.0.1",
5526
- "read-package-json": "1 || 2",
5527
- "semver": "2.x || 3.x || 4 || 5",
5528
- "validate-npm-package-license": "^3.0.1",
5529
- "validate-npm-package-name": "^3.0.0"
5530
- }
5531
- },
5532
- "invert-kv": {
5533
- "version": "1.0.0",
5534
- "bundled": true
5535
- },
5536
- "ip": {
5537
- "version": "1.1.5",
5538
- "bundled": true
5539
- },
5540
- "ip-regex": {
5541
- "version": "2.1.0",
5542
- "bundled": true
5543
- },
5544
- "is-builtin-module": {
5545
- "version": "1.0.0",
5546
- "bundled": true,
5547
- "requires": {
5548
- "builtin-modules": "^1.0.0"
5549
- }
5550
- },
5551
- "is-ci": {
5552
- "version": "1.1.0",
5553
- "bundled": true,
5554
- "requires": {
5555
- "ci-info": "^1.0.0"
5556
- }
5557
- },
5558
- "is-cidr": {
5559
- "version": "2.0.6",
5560
- "bundled": true,
5561
- "requires": {
5562
- "cidr-regex": "^2.0.8"
5563
- }
5564
- },
5565
- "is-fullwidth-code-point": {
5566
- "version": "1.0.0",
5567
- "bundled": true,
5568
- "requires": {
5569
- "number-is-nan": "^1.0.0"
5570
- }
5571
- },
5572
- "is-installed-globally": {
5573
- "version": "0.1.0",
5574
- "bundled": true,
5575
- "requires": {
5576
- "global-dirs": "^0.1.0",
5577
- "is-path-inside": "^1.0.0"
5578
- }
5579
- },
5580
- "is-npm": {
5581
- "version": "1.0.0",
5582
- "bundled": true
5583
- },
5584
- "is-obj": {
5585
- "version": "1.0.1",
5586
- "bundled": true
5587
- },
5588
- "is-path-inside": {
5589
- "version": "1.0.1",
5590
- "bundled": true,
5591
- "requires": {
5592
- "path-is-inside": "^1.0.1"
5593
- }
5594
- },
5595
- "is-redirect": {
5596
- "version": "1.0.0",
5597
- "bundled": true
5598
- },
5599
- "is-retry-allowed": {
5600
- "version": "1.1.0",
5601
- "bundled": true
5602
- },
5603
- "is-stream": {
5604
- "version": "1.1.0",
5605
- "bundled": true
5606
- },
5607
- "is-typedarray": {
5608
- "version": "1.0.0",
5609
- "bundled": true
5610
- },
5611
- "isarray": {
5612
- "version": "1.0.0",
5613
- "bundled": true
5614
- },
5615
- "isexe": {
5616
- "version": "2.0.0",
5617
- "bundled": true
5618
- },
5619
- "isstream": {
5620
- "version": "0.1.2",
5621
- "bundled": true
5622
- },
5623
- "jsbn": {
5624
- "version": "0.1.1",
5625
- "bundled": true,
5626
- "optional": true
5627
- },
5628
- "json-parse-better-errors": {
5629
- "version": "1.0.2",
5630
- "bundled": true
5631
- },
5632
- "json-schema": {
5633
- "version": "0.2.3",
5634
- "bundled": true
5635
- },
5636
- "json-stable-stringify": {
5637
- "version": "1.0.1",
5638
- "bundled": true,
5639
- "requires": {
5640
- "jsonify": "~0.0.0"
5641
- }
5642
- },
5643
- "json-stringify-safe": {
5644
- "version": "5.0.1",
5645
- "bundled": true
5646
- },
5647
- "jsonify": {
5648
- "version": "0.0.0",
5649
- "bundled": true
5650
- },
5651
- "jsonparse": {
5652
- "version": "1.3.1",
5653
- "bundled": true
5654
- },
5655
- "jsprim": {
5656
- "version": "1.4.1",
5657
- "bundled": true,
5658
- "requires": {
5659
- "assert-plus": "1.0.0",
5660
- "extsprintf": "1.3.0",
5661
- "json-schema": "0.2.3",
5662
- "verror": "1.10.0"
5663
- },
5664
- "dependencies": {
5665
- "assert-plus": {
5666
- "version": "1.0.0",
5667
- "bundled": true
5668
- }
5669
- }
5670
- },
5671
- "latest-version": {
5672
- "version": "3.1.0",
5673
- "bundled": true,
5674
- "requires": {
5675
- "package-json": "^4.0.0"
5676
- }
5677
- },
5678
- "lazy-property": {
5679
- "version": "1.0.0",
5680
- "bundled": true
5681
- },
5682
- "lcid": {
5683
- "version": "1.0.0",
5684
- "bundled": true,
5685
- "requires": {
5686
- "invert-kv": "^1.0.0"
5687
- }
5688
- },
5689
- "libcipm": {
5690
- "version": "2.0.0",
5691
- "bundled": true,
5692
- "requires": {
5693
- "bin-links": "^1.1.2",
5694
- "bluebird": "^3.5.1",
5695
- "find-npm-prefix": "^1.0.2",
5696
- "graceful-fs": "^4.1.11",
5697
- "lock-verify": "^2.0.2",
5698
- "npm-lifecycle": "^2.0.3",
5699
- "npm-logical-tree": "^1.2.1",
5700
- "npm-package-arg": "^6.1.0",
5701
- "pacote": "^8.1.6",
5702
- "protoduck": "^5.0.0",
5703
- "read-package-json": "^2.0.13",
5704
- "rimraf": "^2.6.2",
5705
- "worker-farm": "^1.6.0"
5706
- }
5707
- },
5708
- "libnpmhook": {
5709
- "version": "4.0.1",
5710
- "bundled": true,
5711
- "requires": {
5712
- "figgy-pudding": "^3.1.0",
5713
- "npm-registry-fetch": "^3.0.0"
5714
- },
5715
- "dependencies": {
5716
- "npm-registry-fetch": {
5717
- "version": "3.1.1",
5718
- "bundled": true,
5719
- "requires": {
5720
- "bluebird": "^3.5.1",
5721
- "figgy-pudding": "^3.1.0",
5722
- "lru-cache": "^4.1.2",
5723
- "make-fetch-happen": "^4.0.0",
5724
- "npm-package-arg": "^6.0.0"
5725
- }
5726
- }
5727
- }
5728
- },
5729
- "libnpx": {
5730
- "version": "10.2.0",
5731
- "bundled": true,
5732
- "requires": {
5733
- "dotenv": "^5.0.1",
5734
- "npm-package-arg": "^6.0.0",
5735
- "rimraf": "^2.6.2",
5736
- "safe-buffer": "^5.1.0",
5737
- "update-notifier": "^2.3.0",
5738
- "which": "^1.3.0",
5739
- "y18n": "^4.0.0",
5740
- "yargs": "^11.0.0"
5741
- }
5742
- },
5743
- "locate-path": {
5744
- "version": "2.0.0",
5745
- "bundled": true,
5746
- "requires": {
5747
- "p-locate": "^2.0.0",
5748
- "path-exists": "^3.0.0"
5749
- }
5750
- },
5751
- "lock-verify": {
5752
- "version": "2.0.2",
5753
- "bundled": true,
5754
- "requires": {
5755
- "npm-package-arg": "^5.1.2 || 6",
5756
- "semver": "^5.4.1"
5757
- }
5758
- },
5759
- "lockfile": {
5760
- "version": "1.0.4",
5761
- "bundled": true,
5762
- "requires": {
5763
- "signal-exit": "^3.0.2"
5764
- }
5765
- },
5766
- "lodash._baseindexof": {
5767
- "version": "3.1.0",
5768
- "bundled": true
5769
- },
5770
- "lodash._baseuniq": {
5771
- "version": "4.6.0",
5772
- "bundled": true,
5773
- "requires": {
5774
- "lodash._createset": "~4.0.0",
5775
- "lodash._root": "~3.0.0"
5776
- }
5777
- },
5778
- "lodash._bindcallback": {
5779
- "version": "3.0.1",
5780
- "bundled": true
5781
- },
5782
- "lodash._cacheindexof": {
5783
- "version": "3.0.2",
5784
- "bundled": true
5785
- },
5786
- "lodash._createcache": {
5787
- "version": "3.1.2",
5788
- "bundled": true,
5789
- "requires": {
5790
- "lodash._getnative": "^3.0.0"
5791
- }
5792
- },
5793
- "lodash._createset": {
5794
- "version": "4.0.3",
5795
- "bundled": true
5796
- },
5797
- "lodash._getnative": {
5798
- "version": "3.9.1",
5799
- "bundled": true
5800
- },
5801
- "lodash._root": {
5802
- "version": "3.0.1",
5803
- "bundled": true
5804
- },
5805
- "lodash.clonedeep": {
5806
- "version": "4.5.0",
5807
- "bundled": true
5808
- },
5809
- "lodash.restparam": {
5810
- "version": "3.6.1",
5811
- "bundled": true
5812
- },
5813
- "lodash.union": {
5814
- "version": "4.6.0",
5815
- "bundled": true
5816
- },
5817
- "lodash.uniq": {
5818
- "version": "4.5.0",
5819
- "bundled": true
5820
- },
5821
- "lodash.without": {
5822
- "version": "4.4.0",
5823
- "bundled": true
5824
- },
5825
- "lowercase-keys": {
5826
- "version": "1.0.1",
5827
- "bundled": true
5828
- },
5829
- "lru-cache": {
5830
- "version": "4.1.3",
5831
- "bundled": true,
5832
- "requires": {
5833
- "pseudomap": "^1.0.2",
5834
- "yallist": "^2.1.2"
5835
- }
5836
- },
5837
- "make-dir": {
5838
- "version": "1.3.0",
5839
- "bundled": true,
5840
- "requires": {
5841
- "pify": "^3.0.0"
5842
- }
5843
- },
5844
- "make-fetch-happen": {
5845
- "version": "4.0.1",
5846
- "bundled": true,
5847
- "requires": {
5848
- "agentkeepalive": "^3.4.1",
5849
- "cacache": "^11.0.1",
5850
- "http-cache-semantics": "^3.8.1",
5851
- "http-proxy-agent": "^2.1.0",
5852
- "https-proxy-agent": "^2.2.1",
5853
- "lru-cache": "^4.1.2",
5854
- "mississippi": "^3.0.0",
5855
- "node-fetch-npm": "^2.0.2",
5856
- "promise-retry": "^1.1.1",
5857
- "socks-proxy-agent": "^4.0.0",
5858
- "ssri": "^6.0.0"
5859
- }
5860
- },
5861
- "meant": {
5862
- "version": "1.0.1",
5863
- "bundled": true
5864
- },
5865
- "mem": {
5866
- "version": "1.1.0",
5867
- "bundled": true,
5868
- "requires": {
5869
- "mimic-fn": "^1.0.0"
5870
- }
5871
- },
5872
- "mime-db": {
5873
- "version": "1.33.0",
5874
- "bundled": true
5875
- },
5876
- "mime-types": {
5877
- "version": "2.1.18",
5878
- "bundled": true,
5879
- "requires": {
5880
- "mime-db": "~1.33.0"
5881
- }
5882
- },
5883
- "mimic-fn": {
5884
- "version": "1.2.0",
5885
- "bundled": true
5886
- },
5887
- "minimatch": {
5888
- "version": "3.0.4",
5889
- "bundled": true,
5890
- "requires": {
5891
- "brace-expansion": "^1.1.7"
5892
- }
5893
- },
5894
- "minimist": {
5895
- "version": "0.0.8",
5896
- "bundled": true
5897
- },
5898
- "minipass": {
5899
- "version": "2.3.3",
5900
- "bundled": true,
5901
- "requires": {
5902
- "safe-buffer": "^5.1.2",
5903
- "yallist": "^3.0.0"
5904
- },
5905
- "dependencies": {
5906
- "yallist": {
5907
- "version": "3.0.2",
5908
- "bundled": true
5909
- }
5910
- }
5911
- },
5912
- "minizlib": {
5913
- "version": "1.1.0",
5914
- "bundled": true,
5915
- "requires": {
5916
- "minipass": "^2.2.1"
5917
- }
5918
- },
5919
- "mississippi": {
5920
- "version": "3.0.0",
5921
- "bundled": true,
5922
- "requires": {
5923
- "concat-stream": "^1.5.0",
5924
- "duplexify": "^3.4.2",
5925
- "end-of-stream": "^1.1.0",
5926
- "flush-write-stream": "^1.0.0",
5927
- "from2": "^2.1.0",
5928
- "parallel-transform": "^1.1.0",
5929
- "pump": "^3.0.0",
5930
- "pumpify": "^1.3.3",
5931
- "stream-each": "^1.1.0",
5932
- "through2": "^2.0.0"
5933
- }
5934
- },
5935
- "mkdirp": {
5936
- "version": "0.5.1",
5937
- "bundled": true,
5938
- "requires": {
5939
- "minimist": "0.0.8"
5940
- }
5941
- },
5942
- "move-concurrently": {
5943
- "version": "1.0.1",
5944
- "bundled": true,
5945
- "requires": {
5946
- "aproba": "^1.1.1",
5947
- "copy-concurrently": "^1.0.0",
5948
- "fs-write-stream-atomic": "^1.0.8",
5949
- "mkdirp": "^0.5.1",
5950
- "rimraf": "^2.5.4",
5951
- "run-queue": "^1.0.3"
5952
- }
5953
- },
5954
- "ms": {
5955
- "version": "2.1.1",
5956
- "bundled": true
5957
- },
5958
- "mute-stream": {
5959
- "version": "0.0.7",
5960
- "bundled": true
5961
- },
5962
- "node-fetch-npm": {
5963
- "version": "2.0.2",
5964
- "bundled": true,
5965
- "requires": {
5966
- "encoding": "^0.1.11",
5967
- "json-parse-better-errors": "^1.0.0",
5968
- "safe-buffer": "^5.1.1"
5969
- }
5970
- },
5971
- "node-gyp": {
5972
- "version": "3.7.0",
5973
- "bundled": true,
5974
- "requires": {
5975
- "fstream": "^1.0.0",
5976
- "glob": "^7.0.3",
5977
- "graceful-fs": "^4.1.2",
5978
- "mkdirp": "^0.5.0",
5979
- "nopt": "2 || 3",
5980
- "npmlog": "0 || 1 || 2 || 3 || 4",
5981
- "osenv": "0",
5982
- "request": ">=2.9.0 <2.82.0",
5983
- "rimraf": "2",
5984
- "semver": "~5.3.0",
5985
- "tar": "^2.0.0",
5986
- "which": "1"
5987
- },
5988
- "dependencies": {
5989
- "nopt": {
5990
- "version": "3.0.6",
5991
- "bundled": true,
5992
- "requires": {
5993
- "abbrev": "1"
5994
- }
5995
- },
5996
- "semver": {
5997
- "version": "5.3.0",
5998
- "bundled": true
5999
- },
6000
- "tar": {
6001
- "version": "2.2.1",
6002
- "bundled": true,
6003
- "requires": {
6004
- "block-stream": "*",
6005
- "fstream": "^1.0.2",
6006
- "inherits": "2"
6007
- }
6008
- }
6009
- }
6010
- },
6011
- "nopt": {
6012
- "version": "4.0.1",
6013
- "bundled": true,
6014
- "requires": {
6015
- "abbrev": "1",
6016
- "osenv": "^0.1.4"
6017
- }
6018
- },
6019
- "normalize-package-data": {
6020
- "version": "2.4.0",
6021
- "bundled": true,
6022
- "requires": {
6023
- "hosted-git-info": "^2.1.4",
6024
- "is-builtin-module": "^1.0.0",
6025
- "semver": "2 || 3 || 4 || 5",
6026
- "validate-npm-package-license": "^3.0.1"
6027
- }
6028
- },
6029
- "npm-audit-report": {
6030
- "version": "1.3.1",
6031
- "bundled": true,
6032
- "requires": {
6033
- "cli-table3": "^0.5.0",
6034
- "console-control-strings": "^1.1.0"
6035
- }
6036
- },
6037
- "npm-bundled": {
6038
- "version": "1.0.3",
6039
- "bundled": true
6040
- },
6041
- "npm-cache-filename": {
6042
- "version": "1.0.2",
6043
- "bundled": true
6044
- },
6045
- "npm-install-checks": {
6046
- "version": "3.0.0",
6047
- "bundled": true,
6048
- "requires": {
6049
- "semver": "^2.3.0 || 3.x || 4 || 5"
6050
- }
6051
- },
6052
- "npm-lifecycle": {
6053
- "version": "2.0.3",
6054
- "bundled": true,
6055
- "requires": {
6056
- "byline": "^5.0.0",
6057
- "graceful-fs": "^4.1.11",
6058
- "node-gyp": "^3.6.2",
6059
- "resolve-from": "^4.0.0",
6060
- "slide": "^1.1.6",
6061
- "uid-number": "0.0.6",
6062
- "umask": "^1.1.0",
6063
- "which": "^1.3.0"
6064
- }
6065
- },
6066
- "npm-logical-tree": {
6067
- "version": "1.2.1",
6068
- "bundled": true
6069
- },
6070
- "npm-package-arg": {
6071
- "version": "6.1.0",
6072
- "bundled": true,
6073
- "requires": {
6074
- "hosted-git-info": "^2.6.0",
6075
- "osenv": "^0.1.5",
6076
- "semver": "^5.5.0",
6077
- "validate-npm-package-name": "^3.0.0"
6078
- }
6079
- },
6080
- "npm-packlist": {
6081
- "version": "1.1.10",
6082
- "bundled": true,
6083
- "requires": {
6084
- "ignore-walk": "^3.0.1",
6085
- "npm-bundled": "^1.0.1"
6086
- }
6087
- },
6088
- "npm-pick-manifest": {
6089
- "version": "2.1.0",
6090
- "bundled": true,
6091
- "requires": {
6092
- "npm-package-arg": "^6.0.0",
6093
- "semver": "^5.4.1"
6094
- }
6095
- },
6096
- "npm-profile": {
6097
- "version": "3.0.2",
6098
- "bundled": true,
6099
- "requires": {
6100
- "aproba": "^1.1.2 || 2",
6101
- "make-fetch-happen": "^2.5.0 || 3 || 4"
6102
- }
6103
- },
6104
- "npm-registry-client": {
6105
- "version": "8.5.1",
6106
- "bundled": true,
6107
- "requires": {
6108
- "concat-stream": "^1.5.2",
6109
- "graceful-fs": "^4.1.6",
6110
- "normalize-package-data": "~1.0.1 || ^2.0.0",
6111
- "npm-package-arg": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0",
6112
- "npmlog": "2 || ^3.1.0 || ^4.0.0",
6113
- "once": "^1.3.3",
6114
- "request": "^2.74.0",
6115
- "retry": "^0.10.0",
6116
- "safe-buffer": "^5.1.1",
6117
- "semver": "2 >=2.2.1 || 3.x || 4 || 5",
6118
- "slide": "^1.1.3",
6119
- "ssri": "^5.2.4"
6120
- },
6121
- "dependencies": {
6122
- "retry": {
6123
- "version": "0.10.1",
6124
- "bundled": true
6125
- },
6126
- "ssri": {
6127
- "version": "5.3.0",
6128
- "bundled": true,
6129
- "requires": {
6130
- "safe-buffer": "^5.1.1"
6131
- }
6132
- }
6133
- }
6134
- },
6135
- "npm-registry-fetch": {
6136
- "version": "1.1.0",
6137
- "bundled": true,
6138
- "requires": {
6139
- "bluebird": "^3.5.1",
6140
- "figgy-pudding": "^2.0.1",
6141
- "lru-cache": "^4.1.2",
6142
- "make-fetch-happen": "^3.0.0",
6143
- "npm-package-arg": "^6.0.0",
6144
- "safe-buffer": "^5.1.1"
6145
- },
6146
- "dependencies": {
6147
- "cacache": {
6148
- "version": "10.0.4",
6149
- "bundled": true,
6150
- "requires": {
6151
- "bluebird": "^3.5.1",
6152
- "chownr": "^1.0.1",
6153
- "glob": "^7.1.2",
6154
- "graceful-fs": "^4.1.11",
6155
- "lru-cache": "^4.1.1",
6156
- "mississippi": "^2.0.0",
6157
- "mkdirp": "^0.5.1",
6158
- "move-concurrently": "^1.0.1",
6159
- "promise-inflight": "^1.0.1",
6160
- "rimraf": "^2.6.2",
6161
- "ssri": "^5.2.4",
6162
- "unique-filename": "^1.1.0",
6163
- "y18n": "^4.0.0"
6164
- },
6165
- "dependencies": {
6166
- "mississippi": {
6167
- "version": "2.0.0",
6168
- "bundled": true,
6169
- "requires": {
6170
- "concat-stream": "^1.5.0",
6171
- "duplexify": "^3.4.2",
6172
- "end-of-stream": "^1.1.0",
6173
- "flush-write-stream": "^1.0.0",
6174
- "from2": "^2.1.0",
6175
- "parallel-transform": "^1.1.0",
6176
- "pump": "^2.0.1",
6177
- "pumpify": "^1.3.3",
6178
- "stream-each": "^1.1.0",
6179
- "through2": "^2.0.0"
6180
- }
6181
- }
6182
- }
6183
- },
6184
- "figgy-pudding": {
6185
- "version": "2.0.1",
6186
- "bundled": true
6187
- },
6188
- "make-fetch-happen": {
6189
- "version": "3.0.0",
6190
- "bundled": true,
6191
- "requires": {
6192
- "agentkeepalive": "^3.4.1",
6193
- "cacache": "^10.0.4",
6194
- "http-cache-semantics": "^3.8.1",
6195
- "http-proxy-agent": "^2.1.0",
6196
- "https-proxy-agent": "^2.2.0",
6197
- "lru-cache": "^4.1.2",
6198
- "mississippi": "^3.0.0",
6199
- "node-fetch-npm": "^2.0.2",
6200
- "promise-retry": "^1.1.1",
6201
- "socks-proxy-agent": "^3.0.1",
6202
- "ssri": "^5.2.4"
6203
- }
6204
- },
6205
- "pump": {
6206
- "version": "2.0.1",
6207
- "bundled": true,
6208
- "requires": {
6209
- "end-of-stream": "^1.1.0",
6210
- "once": "^1.3.1"
6211
- }
6212
- },
6213
- "smart-buffer": {
6214
- "version": "1.1.15",
6215
- "bundled": true
6216
- },
6217
- "socks": {
6218
- "version": "1.1.10",
6219
- "bundled": true,
6220
- "requires": {
6221
- "ip": "^1.1.4",
6222
- "smart-buffer": "^1.0.13"
6223
- }
6224
- },
6225
- "socks-proxy-agent": {
6226
- "version": "3.0.1",
6227
- "bundled": true,
6228
- "requires": {
6229
- "agent-base": "^4.1.0",
6230
- "socks": "^1.1.10"
6231
- }
6232
- },
6233
- "ssri": {
6234
- "version": "5.3.0",
6235
- "bundled": true,
6236
- "requires": {
6237
- "safe-buffer": "^5.1.1"
6238
- }
6239
- }
6240
- }
6241
- },
6242
- "npm-run-path": {
6243
- "version": "2.0.2",
6244
- "bundled": true,
6245
- "requires": {
6246
- "path-key": "^2.0.0"
6247
- }
6248
- },
6249
- "npm-user-validate": {
6250
- "version": "1.0.0",
6251
- "bundled": true
6252
- },
6253
- "npmlog": {
6254
- "version": "4.1.2",
6255
- "bundled": true,
6256
- "requires": {
6257
- "are-we-there-yet": "~1.1.2",
6258
- "console-control-strings": "~1.1.0",
6259
- "gauge": "~2.7.3",
6260
- "set-blocking": "~2.0.0"
6261
- }
6262
- },
6263
- "number-is-nan": {
6264
- "version": "1.0.1",
6265
- "bundled": true
6266
- },
6267
- "oauth-sign": {
6268
- "version": "0.8.2",
6269
- "bundled": true
6270
- },
6271
- "object-assign": {
6272
- "version": "4.1.1",
6273
- "bundled": true
6274
- },
6275
- "once": {
6276
- "version": "1.4.0",
6277
- "bundled": true,
6278
- "requires": {
6279
- "wrappy": "1"
6280
- }
6281
- },
6282
- "opener": {
6283
- "version": "1.4.3",
6284
- "bundled": true
6285
- },
6286
- "os-homedir": {
6287
- "version": "1.0.2",
6288
- "bundled": true
6289
- },
6290
- "os-locale": {
6291
- "version": "2.1.0",
6292
- "bundled": true,
6293
- "requires": {
6294
- "execa": "^0.7.0",
6295
- "lcid": "^1.0.0",
6296
- "mem": "^1.1.0"
6297
- }
6298
- },
6299
- "os-tmpdir": {
6300
- "version": "1.0.2",
6301
- "bundled": true
6302
- },
6303
- "osenv": {
6304
- "version": "0.1.5",
6305
- "bundled": true,
6306
- "requires": {
6307
- "os-homedir": "^1.0.0",
6308
- "os-tmpdir": "^1.0.0"
6309
- }
6310
- },
6311
- "p-finally": {
6312
- "version": "1.0.0",
6313
- "bundled": true
6314
- },
6315
- "p-limit": {
6316
- "version": "1.2.0",
6317
- "bundled": true,
6318
- "requires": {
6319
- "p-try": "^1.0.0"
6320
- }
6321
- },
6322
- "p-locate": {
6323
- "version": "2.0.0",
6324
- "bundled": true,
6325
- "requires": {
6326
- "p-limit": "^1.1.0"
6327
- }
6328
- },
6329
- "p-try": {
6330
- "version": "1.0.0",
6331
- "bundled": true
6332
- },
6333
- "package-json": {
6334
- "version": "4.0.1",
6335
- "bundled": true,
6336
- "requires": {
6337
- "got": "^6.7.1",
6338
- "registry-auth-token": "^3.0.1",
6339
- "registry-url": "^3.0.3",
6340
- "semver": "^5.1.0"
6341
- }
6342
- },
6343
- "pacote": {
6344
- "version": "8.1.6",
6345
- "bundled": true,
6346
- "requires": {
6347
- "bluebird": "^3.5.1",
6348
- "cacache": "^11.0.2",
6349
- "get-stream": "^3.0.0",
6350
- "glob": "^7.1.2",
6351
- "lru-cache": "^4.1.3",
6352
- "make-fetch-happen": "^4.0.1",
6353
- "minimatch": "^3.0.4",
6354
- "minipass": "^2.3.3",
6355
- "mississippi": "^3.0.0",
6356
- "mkdirp": "^0.5.1",
6357
- "normalize-package-data": "^2.4.0",
6358
- "npm-package-arg": "^6.1.0",
6359
- "npm-packlist": "^1.1.10",
6360
- "npm-pick-manifest": "^2.1.0",
6361
- "osenv": "^0.1.5",
6362
- "promise-inflight": "^1.0.1",
6363
- "promise-retry": "^1.1.1",
6364
- "protoduck": "^5.0.0",
6365
- "rimraf": "^2.6.2",
6366
- "safe-buffer": "^5.1.2",
6367
- "semver": "^5.5.0",
6368
- "ssri": "^6.0.0",
6369
- "tar": "^4.4.3",
6370
- "unique-filename": "^1.1.0",
6371
- "which": "^1.3.0"
6372
- }
6373
- },
6374
- "parallel-transform": {
6375
- "version": "1.1.0",
6376
- "bundled": true,
6377
- "requires": {
6378
- "cyclist": "~0.2.2",
6379
- "inherits": "^2.0.3",
6380
- "readable-stream": "^2.1.5"
6381
- }
6382
- },
6383
- "path-exists": {
6384
- "version": "3.0.0",
6385
- "bundled": true
6386
- },
6387
- "path-is-absolute": {
6388
- "version": "1.0.1",
6389
- "bundled": true
6390
- },
6391
- "path-is-inside": {
6392
- "version": "1.0.2",
6393
- "bundled": true
6394
- },
6395
- "path-key": {
6396
- "version": "2.0.1",
6397
- "bundled": true
6398
- },
6399
- "performance-now": {
6400
- "version": "0.2.0",
6401
- "bundled": true
6402
- },
6403
- "pify": {
6404
- "version": "3.0.0",
6405
- "bundled": true
6406
- },
6407
- "prepend-http": {
6408
- "version": "1.0.4",
6409
- "bundled": true
6410
- },
6411
- "process-nextick-args": {
6412
- "version": "2.0.0",
6413
- "bundled": true
6414
- },
6415
- "promise-inflight": {
6416
- "version": "1.0.1",
6417
- "bundled": true
6418
- },
6419
- "promise-retry": {
6420
- "version": "1.1.1",
6421
- "bundled": true,
6422
- "requires": {
6423
- "err-code": "^1.0.0",
6424
- "retry": "^0.10.0"
6425
- },
6426
- "dependencies": {
6427
- "retry": {
6428
- "version": "0.10.1",
6429
- "bundled": true
6430
- }
6431
- }
6432
- },
6433
- "promzard": {
6434
- "version": "0.3.0",
6435
- "bundled": true,
6436
- "requires": {
6437
- "read": "1"
6438
- }
6439
- },
6440
- "proto-list": {
6441
- "version": "1.2.4",
6442
- "bundled": true
6443
- },
6444
- "protoduck": {
6445
- "version": "5.0.0",
6446
- "bundled": true,
6447
- "requires": {
6448
- "genfun": "^4.0.1"
6449
- }
6450
- },
6451
- "prr": {
6452
- "version": "1.0.1",
6453
- "bundled": true
6454
- },
6455
- "pseudomap": {
6456
- "version": "1.0.2",
6457
- "bundled": true
6458
- },
6459
- "pump": {
6460
- "version": "3.0.0",
6461
- "bundled": true,
6462
- "requires": {
6463
- "end-of-stream": "^1.1.0",
6464
- "once": "^1.3.1"
6465
- }
6466
- },
6467
- "pumpify": {
6468
- "version": "1.5.1",
6469
- "bundled": true,
6470
- "requires": {
6471
- "duplexify": "^3.6.0",
6472
- "inherits": "^2.0.3",
6473
- "pump": "^2.0.0"
6474
- },
6475
- "dependencies": {
6476
- "pump": {
6477
- "version": "2.0.1",
6478
- "bundled": true,
6479
- "requires": {
6480
- "end-of-stream": "^1.1.0",
6481
- "once": "^1.3.1"
6482
- }
6483
- }
6484
- }
6485
- },
6486
- "punycode": {
6487
- "version": "1.4.1",
6488
- "bundled": true
6489
- },
6490
- "qrcode-terminal": {
6491
- "version": "0.12.0",
6492
- "bundled": true
6493
- },
6494
- "qs": {
6495
- "version": "6.4.0",
6496
- "bundled": true
6497
- },
6498
- "query-string": {
6499
- "version": "6.1.0",
6500
- "bundled": true,
6501
- "requires": {
6502
- "decode-uri-component": "^0.2.0",
6503
- "strict-uri-encode": "^2.0.0"
6504
- }
6505
- },
6506
- "qw": {
6507
- "version": "1.0.1",
6508
- "bundled": true
6509
- },
6510
- "rc": {
6511
- "version": "1.2.7",
6512
- "bundled": true,
6513
- "requires": {
6514
- "deep-extend": "^0.5.1",
6515
- "ini": "~1.3.0",
6516
- "minimist": "^1.2.0",
6517
- "strip-json-comments": "~2.0.1"
6518
- },
6519
- "dependencies": {
6520
- "minimist": {
6521
- "version": "1.2.0",
6522
- "bundled": true
6523
- }
6524
- }
6525
- },
6526
- "read": {
6527
- "version": "1.0.7",
6528
- "bundled": true,
6529
- "requires": {
6530
- "mute-stream": "~0.0.4"
6531
- }
6532
- },
6533
- "read-cmd-shim": {
6534
- "version": "1.0.1",
6535
- "bundled": true,
6536
- "requires": {
6537
- "graceful-fs": "^4.1.2"
6538
- }
6539
- },
6540
- "read-installed": {
6541
- "version": "4.0.3",
6542
- "bundled": true,
6543
- "requires": {
6544
- "debuglog": "^1.0.1",
6545
- "graceful-fs": "^4.1.2",
6546
- "read-package-json": "^2.0.0",
6547
- "readdir-scoped-modules": "^1.0.0",
6548
- "semver": "2 || 3 || 4 || 5",
6549
- "slide": "~1.1.3",
6550
- "util-extend": "^1.0.1"
6551
- }
6552
- },
6553
- "read-package-json": {
6554
- "version": "2.0.13",
6555
- "bundled": true,
6556
- "requires": {
6557
- "glob": "^7.1.1",
6558
- "graceful-fs": "^4.1.2",
6559
- "json-parse-better-errors": "^1.0.1",
6560
- "normalize-package-data": "^2.0.0",
6561
- "slash": "^1.0.0"
6562
- }
6563
- },
6564
- "read-package-tree": {
6565
- "version": "5.2.1",
6566
- "bundled": true,
6567
- "requires": {
6568
- "debuglog": "^1.0.1",
6569
- "dezalgo": "^1.0.0",
6570
- "once": "^1.3.0",
6571
- "read-package-json": "^2.0.0",
6572
- "readdir-scoped-modules": "^1.0.0"
6573
- }
6574
- },
6575
- "readable-stream": {
6576
- "version": "2.3.6",
6577
- "bundled": true,
6578
- "requires": {
6579
- "core-util-is": "~1.0.0",
6580
- "inherits": "~2.0.3",
6581
- "isarray": "~1.0.0",
6582
- "process-nextick-args": "~2.0.0",
6583
- "safe-buffer": "~5.1.1",
6584
- "string_decoder": "~1.1.1",
6585
- "util-deprecate": "~1.0.1"
6586
- }
6587
- },
6588
- "readdir-scoped-modules": {
6589
- "version": "1.0.2",
6590
- "bundled": true,
6591
- "requires": {
6592
- "debuglog": "^1.0.1",
6593
- "dezalgo": "^1.0.0",
6594
- "graceful-fs": "^4.1.2",
6595
- "once": "^1.3.0"
6596
- }
6597
- },
6598
- "registry-auth-token": {
6599
- "version": "3.3.2",
6600
- "bundled": true,
6601
- "requires": {
6602
- "rc": "^1.1.6",
6603
- "safe-buffer": "^5.0.1"
6604
- }
6605
- },
6606
- "registry-url": {
6607
- "version": "3.1.0",
6608
- "bundled": true,
6609
- "requires": {
6610
- "rc": "^1.0.1"
6611
- }
6612
- },
6613
- "request": {
6614
- "version": "2.81.0",
6615
- "bundled": true,
6616
- "requires": {
6617
- "aws-sign2": "~0.6.0",
6618
- "aws4": "^1.2.1",
6619
- "caseless": "~0.12.0",
6620
- "combined-stream": "~1.0.5",
6621
- "extend": "~3.0.0",
6622
- "forever-agent": "~0.6.1",
6623
- "form-data": "~2.1.1",
6624
- "har-validator": "~4.2.1",
6625
- "hawk": "~3.1.3",
6626
- "http-signature": "~1.1.0",
6627
- "is-typedarray": "~1.0.0",
6628
- "isstream": "~0.1.2",
6629
- "json-stringify-safe": "~5.0.1",
6630
- "mime-types": "~2.1.7",
6631
- "oauth-sign": "~0.8.1",
6632
- "performance-now": "^0.2.0",
6633
- "qs": "~6.4.0",
6634
- "safe-buffer": "^5.0.1",
6635
- "stringstream": "~0.0.4",
6636
- "tough-cookie": "~2.3.0",
6637
- "tunnel-agent": "^0.6.0",
6638
- "uuid": "^3.0.0"
6639
- }
6640
- },
6641
- "require-directory": {
6642
- "version": "2.1.1",
6643
- "bundled": true
6644
- },
6645
- "require-main-filename": {
6646
- "version": "1.0.1",
6647
- "bundled": true
6648
- },
6649
- "resolve-from": {
6650
- "version": "4.0.0",
6651
- "bundled": true
6652
- },
6653
- "retry": {
6654
- "version": "0.12.0",
6655
- "bundled": true
6656
- },
6657
- "rimraf": {
6658
- "version": "2.6.2",
6659
- "bundled": true,
6660
- "requires": {
6661
- "glob": "^7.0.5"
6662
- }
6663
- },
6664
- "run-queue": {
6665
- "version": "1.0.3",
6666
- "bundled": true,
6667
- "requires": {
6668
- "aproba": "^1.1.1"
6669
- }
6670
- },
6671
- "safe-buffer": {
6672
- "version": "5.1.2",
6673
- "bundled": true
6674
- },
6675
- "safer-buffer": {
6676
- "version": "2.1.2",
6677
- "bundled": true
6678
- },
6679
- "semver": {
6680
- "version": "5.5.0",
6681
- "bundled": true
6682
- },
6683
- "semver-diff": {
6684
- "version": "2.1.0",
6685
- "bundled": true,
6686
- "requires": {
6687
- "semver": "^5.0.3"
6688
- }
6689
- },
6690
- "set-blocking": {
6691
- "version": "2.0.0",
6692
- "bundled": true
6693
- },
6694
- "sha": {
6695
- "version": "2.0.1",
6696
- "bundled": true,
6697
- "requires": {
6698
- "graceful-fs": "^4.1.2",
6699
- "readable-stream": "^2.0.2"
6700
- }
6701
- },
6702
- "shebang-command": {
6703
- "version": "1.2.0",
6704
- "bundled": true,
6705
- "requires": {
6706
- "shebang-regex": "^1.0.0"
6707
- }
6708
- },
6709
- "shebang-regex": {
6710
- "version": "1.0.0",
6711
- "bundled": true
6712
- },
6713
- "signal-exit": {
6714
- "version": "3.0.2",
6715
- "bundled": true
6716
- },
6717
- "slash": {
6718
- "version": "1.0.0",
6719
- "bundled": true
6720
- },
6721
- "slide": {
6722
- "version": "1.1.6",
6723
- "bundled": true
6724
- },
6725
- "smart-buffer": {
6726
- "version": "4.0.1",
6727
- "bundled": true
6728
- },
6729
- "sntp": {
6730
- "version": "1.0.9",
6731
- "bundled": true,
6732
- "requires": {
6733
- "hoek": "2.x.x"
6734
- }
6735
- },
6736
- "socks": {
6737
- "version": "2.2.0",
6738
- "bundled": true,
6739
- "requires": {
6740
- "ip": "^1.1.5",
6741
- "smart-buffer": "^4.0.1"
6742
- }
6743
- },
6744
- "socks-proxy-agent": {
6745
- "version": "4.0.1",
6746
- "bundled": true,
6747
- "requires": {
6748
- "agent-base": "~4.2.0",
6749
- "socks": "~2.2.0"
6750
- }
6751
- },
6752
- "sorted-object": {
6753
- "version": "2.0.1",
6754
- "bundled": true
6755
- },
6756
- "sorted-union-stream": {
6757
- "version": "2.1.3",
6758
- "bundled": true,
6759
- "requires": {
6760
- "from2": "^1.3.0",
6761
- "stream-iterate": "^1.1.0"
6762
- },
6763
- "dependencies": {
6764
- "from2": {
6765
- "version": "1.3.0",
6766
- "bundled": true,
6767
- "requires": {
6768
- "inherits": "~2.0.1",
6769
- "readable-stream": "~1.1.10"
6770
- }
6771
- },
6772
- "isarray": {
6773
- "version": "0.0.1",
6774
- "bundled": true
6775
- },
6776
- "readable-stream": {
6777
- "version": "1.1.14",
6778
- "bundled": true,
6779
- "requires": {
6780
- "core-util-is": "~1.0.0",
6781
- "inherits": "~2.0.1",
6782
- "isarray": "0.0.1",
6783
- "string_decoder": "~0.10.x"
6784
- }
6785
- },
6786
- "string_decoder": {
6787
- "version": "0.10.31",
6788
- "bundled": true
6789
- }
6790
- }
6791
- },
6792
- "spdx-correct": {
6793
- "version": "3.0.0",
6794
- "bundled": true,
6795
- "requires": {
6796
- "spdx-expression-parse": "^3.0.0",
6797
- "spdx-license-ids": "^3.0.0"
6798
- }
6799
- },
6800
- "spdx-exceptions": {
6801
- "version": "2.1.0",
6802
- "bundled": true
6803
- },
6804
- "spdx-expression-parse": {
6805
- "version": "3.0.0",
6806
- "bundled": true,
6807
- "requires": {
6808
- "spdx-exceptions": "^2.1.0",
6809
- "spdx-license-ids": "^3.0.0"
6810
- }
6811
- },
6812
- "spdx-license-ids": {
6813
- "version": "3.0.0",
6814
- "bundled": true
6815
- },
6816
- "sshpk": {
6817
- "version": "1.14.2",
6818
- "bundled": true,
6819
- "requires": {
6820
- "asn1": "~0.2.3",
6821
- "assert-plus": "^1.0.0",
6822
- "bcrypt-pbkdf": "^1.0.0",
6823
- "dashdash": "^1.12.0",
6824
- "ecc-jsbn": "~0.1.1",
6825
- "getpass": "^0.1.1",
6826
- "jsbn": "~0.1.0",
6827
- "safer-buffer": "^2.0.2",
6828
- "tweetnacl": "~0.14.0"
6829
- },
6830
- "dependencies": {
6831
- "assert-plus": {
6832
- "version": "1.0.0",
6833
- "bundled": true
6834
- }
6835
- }
6836
- },
6837
- "ssri": {
6838
- "version": "6.0.0",
6839
- "bundled": true
6840
- },
6841
- "stream-each": {
6842
- "version": "1.2.2",
6843
- "bundled": true,
6844
- "requires": {
6845
- "end-of-stream": "^1.1.0",
6846
- "stream-shift": "^1.0.0"
6847
- }
6848
- },
6849
- "stream-iterate": {
6850
- "version": "1.2.0",
6851
- "bundled": true,
6852
- "requires": {
6853
- "readable-stream": "^2.1.5",
6854
- "stream-shift": "^1.0.0"
6855
- }
6856
- },
6857
- "stream-shift": {
6858
- "version": "1.0.0",
6859
- "bundled": true
6860
- },
6861
- "strict-uri-encode": {
6862
- "version": "2.0.0",
6863
- "bundled": true
6864
- },
6865
- "string-width": {
6866
- "version": "2.1.1",
6867
- "bundled": true,
6868
- "requires": {
6869
- "is-fullwidth-code-point": "^2.0.0",
6870
- "strip-ansi": "^4.0.0"
6871
- },
6872
- "dependencies": {
6873
- "ansi-regex": {
6874
- "version": "3.0.0",
6875
- "bundled": true
6876
- },
6877
- "is-fullwidth-code-point": {
6878
- "version": "2.0.0",
6879
- "bundled": true
6880
- },
6881
- "strip-ansi": {
6882
- "version": "4.0.0",
6883
- "bundled": true,
6884
- "requires": {
6885
- "ansi-regex": "^3.0.0"
6886
- }
6887
- }
6888
- }
6889
- },
6890
- "string_decoder": {
6891
- "version": "1.1.1",
6892
- "bundled": true,
6893
- "requires": {
6894
- "safe-buffer": "~5.1.0"
6895
- }
6896
- },
6897
- "stringify-package": {
6898
- "version": "1.0.0",
6899
- "bundled": true
6900
- },
6901
- "stringstream": {
6902
- "version": "0.0.6",
6903
- "bundled": true
6904
- },
6905
- "strip-ansi": {
6906
- "version": "3.0.1",
6907
- "bundled": true,
6908
- "requires": {
6909
- "ansi-regex": "^2.0.0"
6910
- }
6911
- },
6912
- "strip-eof": {
6913
- "version": "1.0.0",
6914
- "bundled": true
6915
- },
6916
- "strip-json-comments": {
6917
- "version": "2.0.1",
6918
- "bundled": true
6919
- },
6920
- "supports-color": {
6921
- "version": "5.4.0",
6922
- "bundled": true,
6923
- "requires": {
6924
- "has-flag": "^3.0.0"
6925
- }
6926
- },
6927
- "tar": {
6928
- "version": "4.4.4",
6929
- "bundled": true,
6930
- "requires": {
6931
- "chownr": "^1.0.1",
6932
- "fs-minipass": "^1.2.5",
6933
- "minipass": "^2.3.3",
6934
- "minizlib": "^1.1.0",
6935
- "mkdirp": "^0.5.0",
6936
- "safe-buffer": "^5.1.2",
6937
- "yallist": "^3.0.2"
6938
- },
6939
- "dependencies": {
6940
- "yallist": {
6941
- "version": "3.0.2",
6942
- "bundled": true
6943
- }
6944
- }
6945
- },
6946
- "term-size": {
6947
- "version": "1.2.0",
6948
- "bundled": true,
6949
- "requires": {
6950
- "execa": "^0.7.0"
6951
- }
6952
- },
6953
- "text-table": {
6954
- "version": "0.2.0",
6955
- "bundled": true
6956
- },
6957
- "through": {
6958
- "version": "2.3.8",
6959
- "bundled": true
6960
- },
6961
- "through2": {
6962
- "version": "2.0.3",
6963
- "bundled": true,
6964
- "requires": {
6965
- "readable-stream": "^2.1.5",
6966
- "xtend": "~4.0.1"
6967
- }
6968
- },
6969
- "timed-out": {
6970
- "version": "4.0.1",
6971
- "bundled": true
6972
- },
6973
- "tiny-relative-date": {
6974
- "version": "1.3.0",
6975
- "bundled": true
6976
- },
6977
- "tough-cookie": {
6978
- "version": "2.3.4",
6979
- "bundled": true,
6980
- "requires": {
6981
- "punycode": "^1.4.1"
6982
- }
6983
- },
6984
- "tunnel-agent": {
6985
- "version": "0.6.0",
6986
- "bundled": true,
6987
- "requires": {
6988
- "safe-buffer": "^5.0.1"
6989
- }
6990
- },
6991
- "tweetnacl": {
6992
- "version": "0.14.5",
6993
- "bundled": true,
6994
- "optional": true
6995
- },
6996
- "typedarray": {
6997
- "version": "0.0.6",
6998
- "bundled": true
6999
- },
7000
- "uid-number": {
7001
- "version": "0.0.6",
7002
- "bundled": true
7003
- },
7004
- "umask": {
7005
- "version": "1.1.0",
7006
- "bundled": true
7007
- },
7008
- "unique-filename": {
7009
- "version": "1.1.0",
7010
- "bundled": true,
7011
- "requires": {
7012
- "unique-slug": "^2.0.0"
7013
- }
7014
- },
7015
- "unique-slug": {
7016
- "version": "2.0.0",
7017
- "bundled": true,
7018
- "requires": {
7019
- "imurmurhash": "^0.1.4"
7020
- }
7021
- },
7022
- "unique-string": {
7023
- "version": "1.0.0",
7024
- "bundled": true,
7025
- "requires": {
7026
- "crypto-random-string": "^1.0.0"
7027
- }
7028
- },
7029
- "unpipe": {
7030
- "version": "1.0.0",
7031
- "bundled": true
7032
- },
7033
- "unzip-response": {
7034
- "version": "2.0.1",
7035
- "bundled": true
7036
- },
7037
- "update-notifier": {
7038
- "version": "2.5.0",
7039
- "bundled": true,
7040
- "requires": {
7041
- "boxen": "^1.2.1",
7042
- "chalk": "^2.0.1",
7043
- "configstore": "^3.0.0",
7044
- "import-lazy": "^2.1.0",
7045
- "is-ci": "^1.0.10",
7046
- "is-installed-globally": "^0.1.0",
7047
- "is-npm": "^1.0.0",
7048
- "latest-version": "^3.0.0",
7049
- "semver-diff": "^2.0.0",
7050
- "xdg-basedir": "^3.0.0"
7051
- }
7052
- },
7053
- "url-parse-lax": {
7054
- "version": "1.0.0",
7055
- "bundled": true,
7056
- "requires": {
7057
- "prepend-http": "^1.0.1"
7058
- }
7059
- },
7060
- "util-deprecate": {
7061
- "version": "1.0.2",
7062
- "bundled": true
7063
- },
7064
- "util-extend": {
7065
- "version": "1.0.3",
7066
- "bundled": true
7067
- },
7068
- "uuid": {
7069
- "version": "3.3.2",
7070
- "bundled": true
7071
- },
7072
- "validate-npm-package-license": {
7073
- "version": "3.0.3",
7074
- "bundled": true,
7075
- "requires": {
7076
- "spdx-correct": "^3.0.0",
7077
- "spdx-expression-parse": "^3.0.0"
7078
- }
7079
- },
7080
- "validate-npm-package-name": {
7081
- "version": "3.0.0",
7082
- "bundled": true,
7083
- "requires": {
7084
- "builtins": "^1.0.3"
7085
- }
7086
- },
7087
- "verror": {
7088
- "version": "1.10.0",
7089
- "bundled": true,
7090
- "requires": {
7091
- "assert-plus": "^1.0.0",
7092
- "core-util-is": "1.0.2",
7093
- "extsprintf": "^1.2.0"
7094
- },
7095
- "dependencies": {
7096
- "assert-plus": {
7097
- "version": "1.0.0",
7098
- "bundled": true
7099
- }
7100
- }
7101
- },
7102
- "wcwidth": {
7103
- "version": "1.0.1",
7104
- "bundled": true,
7105
- "requires": {
7106
- "defaults": "^1.0.3"
7107
- }
7108
- },
7109
- "which": {
7110
- "version": "1.3.1",
7111
- "bundled": true,
7112
- "requires": {
7113
- "isexe": "^2.0.0"
7114
- }
7115
- },
7116
- "which-module": {
7117
- "version": "2.0.0",
7118
- "bundled": true
7119
- },
7120
- "wide-align": {
7121
- "version": "1.1.2",
7122
- "bundled": true,
7123
- "requires": {
7124
- "string-width": "^1.0.2"
7125
- },
7126
- "dependencies": {
7127
- "string-width": {
7128
- "version": "1.0.2",
7129
- "bundled": true,
7130
- "requires": {
7131
- "code-point-at": "^1.0.0",
7132
- "is-fullwidth-code-point": "^1.0.0",
7133
- "strip-ansi": "^3.0.0"
7134
- }
7135
- }
7136
- }
7137
- },
7138
- "widest-line": {
7139
- "version": "2.0.0",
7140
- "bundled": true,
7141
- "requires": {
7142
- "string-width": "^2.1.1"
7143
- }
7144
- },
7145
- "worker-farm": {
7146
- "version": "1.6.0",
7147
- "bundled": true,
7148
- "requires": {
7149
- "errno": "~0.1.7"
7150
- }
7151
- },
7152
- "wrap-ansi": {
7153
- "version": "2.1.0",
7154
- "bundled": true,
7155
- "requires": {
7156
- "string-width": "^1.0.1",
7157
- "strip-ansi": "^3.0.1"
7158
- },
7159
- "dependencies": {
7160
- "string-width": {
7161
- "version": "1.0.2",
7162
- "bundled": true,
7163
- "requires": {
7164
- "code-point-at": "^1.0.0",
7165
- "is-fullwidth-code-point": "^1.0.0",
7166
- "strip-ansi": "^3.0.0"
7167
- }
7168
- }
7169
- }
7170
- },
7171
- "wrappy": {
7172
- "version": "1.0.2",
7173
- "bundled": true
7174
- },
7175
- "write-file-atomic": {
7176
- "version": "2.3.0",
7177
- "bundled": true,
7178
- "requires": {
7179
- "graceful-fs": "^4.1.11",
7180
- "imurmurhash": "^0.1.4",
7181
- "signal-exit": "^3.0.2"
7182
- }
7183
- },
7184
- "xdg-basedir": {
7185
- "version": "3.0.0",
7186
- "bundled": true
7187
- },
7188
- "xtend": {
7189
- "version": "4.0.1",
7190
- "bundled": true
7191
- },
7192
- "y18n": {
7193
- "version": "4.0.0",
7194
- "bundled": true
7195
- },
7196
- "yallist": {
7197
- "version": "2.1.2",
7198
- "bundled": true
7199
- },
7200
- "yargs": {
7201
- "version": "11.0.0",
7202
- "bundled": true,
7203
- "requires": {
7204
- "cliui": "^4.0.0",
7205
- "decamelize": "^1.1.1",
7206
- "find-up": "^2.1.0",
7207
- "get-caller-file": "^1.0.1",
7208
- "os-locale": "^2.0.0",
7209
- "require-directory": "^2.1.1",
7210
- "require-main-filename": "^1.0.1",
7211
- "set-blocking": "^2.0.0",
7212
- "string-width": "^2.0.0",
7213
- "which-module": "^2.0.0",
7214
- "y18n": "^3.2.1",
7215
- "yargs-parser": "^9.0.2"
7216
- },
7217
- "dependencies": {
7218
- "y18n": {
7219
- "version": "3.2.1",
7220
- "bundled": true
7221
- }
7222
- }
7223
- },
7224
- "yargs-parser": {
7225
- "version": "9.0.2",
7226
- "bundled": true,
7227
- "requires": {
7228
- "camelcase": "^4.1.0"
7229
- }
7230
- }
7231
- }
7232
- },
7233
- "npmlog": {
7234
- "version": "4.1.2",
7235
- "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
7236
- "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
7237
- "dev": true,
7238
- "requires": {
7239
- "are-we-there-yet": "~1.1.2",
7240
- "console-control-strings": "~1.1.0",
7241
- "gauge": "~2.7.3",
7242
- "set-blocking": "~2.0.0"
7243
- }
7244
- },
7245
- "num2fraction": {
7246
- "version": "1.2.2",
7247
- "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz",
7248
- "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=",
7249
- "dev": true
7250
- },
7251
- "number-is-nan": {
7252
- "version": "1.0.1",
7253
- "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
7254
- "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
7255
- "dev": true
7256
- },
7257
- "oauth-sign": {
7258
- "version": "0.8.2",
7259
- "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz",
7260
- "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=",
7261
- "dev": true
7262
- },
7263
- "object-assign": {
7264
- "version": "4.1.1",
7265
- "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
7266
- "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
7267
- "dev": true
7268
- },
7269
- "object.omit": {
7270
- "version": "2.0.1",
7271
- "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz",
7272
- "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=",
7273
- "dev": true,
7274
- "requires": {
7275
- "for-own": "^0.1.4",
7276
- "is-extendable": "^0.1.1"
7277
- },
7278
- "dependencies": {
7279
- "for-own": {
7280
- "version": "0.1.5",
7281
- "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz",
7282
- "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=",
7283
- "dev": true,
7284
- "requires": {
7285
- "for-in": "^1.0.1"
7286
- }
7287
- }
7288
- }
7289
- },
7290
- "once": {
7291
- "version": "1.4.0",
7292
- "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
7293
- "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
7294
- "dev": true,
7295
- "requires": {
7296
- "wrappy": "1"
7297
- }
7298
- },
7299
- "onetime": {
7300
- "version": "1.1.0",
7301
- "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz",
7302
- "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=",
7303
- "dev": true
7304
- },
7305
- "optionator": {
7306
- "version": "0.8.2",
7307
- "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz",
7308
- "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=",
7309
- "dev": true,
7310
- "requires": {
7311
- "deep-is": "~0.1.3",
7312
- "fast-levenshtein": "~2.0.4",
7313
- "levn": "~0.3.0",
7314
- "prelude-ls": "~1.1.2",
7315
- "type-check": "~0.3.2",
7316
- "wordwrap": "~1.0.0"
7317
- }
7318
- },
7319
- "os-browserify": {
7320
- "version": "0.3.0",
7321
- "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz",
7322
- "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=",
7323
- "dev": true
7324
- },
7325
- "os-homedir": {
7326
- "version": "1.0.2",
7327
- "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
7328
- "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=",
7329
- "dev": true
7330
- },
7331
- "os-locale": {
7332
- "version": "1.4.0",
7333
- "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz",
7334
- "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=",
7335
- "dev": true,
7336
- "requires": {
7337
- "lcid": "^1.0.0"
7338
- }
7339
- },
7340
- "os-tmpdir": {
7341
- "version": "1.0.2",
7342
- "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
7343
- "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
7344
- "dev": true
7345
- },
7346
- "osenv": {
7347
- "version": "0.1.4",
7348
- "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.4.tgz",
7349
- "integrity": "sha1-Qv5tWVPfBsgGS+bxdsPQWqqjRkQ=",
7350
- "dev": true,
7351
- "requires": {
7352
- "os-homedir": "^1.0.0",
7353
- "os-tmpdir": "^1.0.0"
7354
- }
7355
- },
7356
- "p-limit": {
7357
- "version": "1.1.0",
7358
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.1.0.tgz",
7359
- "integrity": "sha1-sH/y2aXYi+yAYDWJWiurZqJ5iLw=",
7360
- "dev": true
7361
- },
7362
- "p-locate": {
7363
- "version": "2.0.0",
7364
- "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz",
7365
- "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=",
7366
- "dev": true,
7367
- "requires": {
7368
- "p-limit": "^1.1.0"
7369
- }
7370
- },
7371
- "pako": {
7372
- "version": "1.0.6",
7373
- "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.6.tgz",
7374
- "integrity": "sha512-lQe48YPsMJAig+yngZ87Lus+NF+3mtu7DVOBu6b/gHO1YpKwIj5AWjZ/TOS7i46HD/UixzWb1zeWDZfGZ3iYcg==",
7375
- "dev": true
7376
- },
7377
- "parse-asn1": {
7378
- "version": "5.1.0",
7379
- "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.0.tgz",
7380
- "integrity": "sha1-N8T5t+06tlx0gXtfJICTf7+XxxI=",
7381
- "dev": true,
7382
- "requires": {
7383
- "asn1.js": "^4.0.0",
7384
- "browserify-aes": "^1.0.0",
7385
- "create-hash": "^1.1.0",
7386
- "evp_bytestokey": "^1.0.0",
7387
- "pbkdf2": "^3.0.3"
7388
- }
7389
- },
7390
- "parse-glob": {
7391
- "version": "3.0.4",
7392
- "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz",
7393
- "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=",
7394
- "dev": true,
7395
- "requires": {
7396
- "glob-base": "^0.3.0",
7397
- "is-dotfile": "^1.0.0",
7398
- "is-extglob": "^1.0.0",
7399
- "is-glob": "^2.0.0"
7400
- }
7401
- },
7402
- "parse-json": {
7403
-