Jupiter X Core - Version 1.11.0

Version Description

Download this release

Release Info

Developer artbees
Plugin Icon wp plugin Jupiter X Core
Version 1.11.0
Comparing to
See all releases

Code changes from version 1.10.1 to 1.11.0

Files changed (46) hide show
  1. assets/images/warning-badge.svg +0 -0
  2. includes/admin/options.php +62 -62
  3. includes/admin/tgmpa/tgmpa-plugin-list.php +203 -185
  4. includes/admin/update-plugins/class-update-plugins.php +102 -102
  5. includes/compiler/class-compiler.php +1018 -1018
  6. includes/compiler/functions.php +411 -411
  7. includes/compiler/preprocess-aliases.ini +277 -277
  8. includes/compiler/vendors/js-minifier.php +395 -395
  9. includes/compiler/vendors/lessc.php +3694 -3694
  10. includes/control-panel/functions.php +159 -158
  11. includes/control-panel/includes/class-browser.php +1193 -1193
  12. includes/control-panel/includes/class-customizer-option.php +33 -33
  13. includes/control-panel/includes/class-db-manager.php +691 -691
  14. includes/control-panel/includes/class-db-php-manager.php +250 -0
  15. includes/control-panel/includes/class-export-import-content.php +80 -0
  16. includes/control-panel/includes/class-filesystem.php +613 -613
  17. includes/control-panel/includes/class-helpers.php +510 -510
  18. includes/control-panel/includes/class-image-sizes.php +152 -98
  19. includes/control-panel/includes/class-install-plugins.php +492 -506
  20. includes/control-panel/includes/class-install-template.php +43 -19
  21. includes/control-panel/includes/class-settings.php +118 -118
  22. includes/control-panel/includes/class-system-status.php +378 -378
  23. includes/control-panel/includes/class-validator.php +166 -166
  24. includes/control-panel/includes/importer/class-jupiterx-importer.php +155 -155
  25. includes/control-panel/includes/importer/class-logger-serversentevents.php +57 -57
  26. includes/control-panel/includes/importer/class-logger.php +138 -138
  27. includes/control-panel/includes/importer/class-wxr-import-info.php +35 -35
  28. includes/control-panel/includes/importer/class-wxr-importer.php +2076 -2076
  29. includes/control-panel/includes/logic-messages.php +456 -456
  30. includes/control-panel/views/export-import-content.php +77 -70
  31. includes/control-panel/views/image-sizes.php +68 -64
  32. includes/control-panel/views/install-plugins.php +62 -56
  33. includes/control-panel/views/install-templates.php +61 -61
  34. includes/control-panel/views/settings.php +104 -103
  35. includes/control-panel/views/system-status.php +929 -929
  36. includes/custom-fields/title-bar.php +33 -33
  37. includes/customizer/api/classes/class-multilingual.php +448 -448
  38. includes/customizer/api/classes/class-status.php +64 -64
  39. includes/customizer/api/customizer.php +295 -295
  40. includes/customizer/api/includes/base/class-control.php +243 -243
  41. includes/customizer/api/includes/base/class-group-control.php +264 -264
  42. includes/customizer/api/includes/base/class-input-group.php +103 -103
  43. includes/customizer/api/includes/class-autoloader.php +119 -119
  44. includes/customizer/api/includes/class-templates.php +163 -163
  45. includes/customizer/api/includes/control/class-alert.php +89 -89
  46. includes/customizer/api/includes/control/class-box-model.php +126 -210
assets/images/warning-badge.svg CHANGED
File without changes
includes/admin/options.php CHANGED
@@ -1,62 +1,62 @@
1
- <?php
2
- /**
3
- * Add Jupiter X admin options.
4
- *
5
- * @package JupiterX_Core\Admin
6
- *
7
- * @since 1.9.0
8
- */
9
-
10
- add_filter( 'upload_mimes', 'jupiterx_add_extra_mime_types' );
11
-
12
- if ( ! function_exists( 'jupiterx_add_extra_mime_types' ) ) {
13
- /**
14
- * Add more mime type.
15
- *
16
- * @since 1.9.0
17
- *
18
- * @param array $mimes Current array of mime types..
19
- *
20
- * @return array Updated array of mime types.
21
- */
22
- function jupiterx_add_extra_mime_types( $mimes ) {
23
-
24
- if ( ! empty( jupiterx_get_option( 'svg_support' ) ) ) {
25
- $mimes['svg'] = 'image/svg+xml';
26
- }
27
-
28
- $mimes['zip'] = 'application/zip';
29
-
30
- return $mimes;
31
- }
32
- }
33
-
34
- add_filter( 'wp_check_filetype_and_ext', 'jupiterx_fix_filetype_check', 10, 4 );
35
- /**
36
- * Fix the mime type filtering issue.
37
- *
38
- * @since 1.9.0
39
- *
40
- * @param array $data file data.
41
- * @param string $file Full path to the file.
42
- * @param string $filename The name of the file (may differ from $file due to $file being in a tmp.
43
- * @param array $mimes Key is the file extension with value as the mime type.
44
- * @return array Filetype data.
45
- *
46
- * @SuppressWarnings(PHPMD.UnusedFormalParameter)
47
- */
48
- function jupiterx_fix_filetype_check( $data, $file, $filename, $mimes ) {
49
- if ( ! empty( $data['ext'] ) && ! empty( $data['type'] ) ) {
50
- return $data;
51
- }
52
-
53
- $wp_filetype = wp_check_filetype( $filename, $mimes );
54
-
55
- if ( 'svg' === $wp_filetype['ext'] || 'svgz' === $wp_filetype['ext'] ) {
56
- $data['ext'] = $wp_filetype['ext'];
57
- $data['type'] = 'image/svg+xml';
58
- }
59
-
60
- return $data;
61
- }
62
-
1
+ <?php
2
+ /**
3
+ * Add Jupiter X admin options.
4
+ *
5
+ * @package JupiterX_Core\Admin
6
+ *
7
+ * @since 1.9.0
8
+ */
9
+
10
+ add_filter( 'upload_mimes', 'jupiterx_add_extra_mime_types' );
11
+
12
+ if ( ! function_exists( 'jupiterx_add_extra_mime_types' ) ) {
13
+ /**
14
+ * Add more mime type.
15
+ *
16
+ * @since 1.9.0
17
+ *
18
+ * @param array $mimes Current array of mime types..
19
+ *
20
+ * @return array Updated array of mime types.
21
+ */
22
+ function jupiterx_add_extra_mime_types( $mimes ) {
23
+
24
+ if ( ! empty( jupiterx_get_option( 'svg_support' ) ) ) {
25
+ $mimes['svg'] = 'image/svg+xml';
26
+ }
27
+
28
+ $mimes['zip'] = 'application/zip';
29
+
30
+ return $mimes;
31
+ }
32
+ }
33
+
34
+ add_filter( 'wp_check_filetype_and_ext', 'jupiterx_fix_filetype_check', 10, 4 );
35
+ /**
36
+ * Fix the mime type filtering issue.
37
+ *
38
+ * @since 1.9.0
39
+ *
40
+ * @param array $data file data.
41
+ * @param string $file Full path to the file.
42
+ * @param string $filename The name of the file (may differ from $file due to $file being in a tmp.
43
+ * @param array $mimes Key is the file extension with value as the mime type.
44
+ * @return array Filetype data.
45
+ *
46
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
47
+ */
48
+ function jupiterx_fix_filetype_check( $data, $file, $filename, $mimes ) {
49
+ if ( ! empty( $data['ext'] ) && ! empty( $data['type'] ) ) {
50
+ return $data;
51
+ }
52
+
53
+ $wp_filetype = wp_check_filetype( $filename, $mimes );
54
+
55
+ if ( 'svg' === $wp_filetype['ext'] || 'svgz' === $wp_filetype['ext'] ) {
56
+ $data['ext'] = $wp_filetype['ext'];
57
+ $data['type'] = 'image/svg+xml';
58
+ }
59
+
60
+ return $data;
61
+ }
62
+
includes/admin/tgmpa/tgmpa-plugin-list.php CHANGED
@@ -1,185 +1,203 @@
1
- <?php
2
- /**
3
- * Add Jupiter X pro plugins.
4
- *
5
- * @package JupiterX_Core\Admin
6
- *
7
- * @since 1.9.0
8
- */
9
-
10
- add_filter( 'jupiterx_tgmpa_plugins', 'jupiterx_pro_plugins' );
11
-
12
- /**
13
- * Add Jupiter X Pro plugins.
14
- *
15
- * @since 1.9.0
16
- *
17
- * @param array $plugins Array of free Jupiter x plugins.
18
- * @return array Array af free and pro plugins.
19
- */
20
- function jupiterx_pro_plugins( $plugins ) {
21
-
22
- $pro_plugins = [
23
- [
24
- 'name' => __( 'Raven', 'jupiterx-core' ),
25
- 'slug' => 'raven',
26
- 'required' => false,
27
- 'force_activation' => false,
28
- 'force_deactivation' => false,
29
- 'pro' => true,
30
- 'source' => 'external',
31
- ],
32
- [
33
- 'name' => __( 'Jupiter Donut', 'jupiterx-core' ),
34
- 'slug' => 'jupiter-donut',
35
- 'required' => false,
36
- 'force_activation' => false,
37
- 'force_deactivation' => false,
38
- 'pro' => true,
39
- 'source' => 'external',
40
- ],
41
- [
42
- 'name' => __( 'Jet Elements', 'jupiterx-core' ),
43
- 'slug' => 'jet-elements',
44
- 'required' => false,
45
- 'force_activation' => false,
46
- 'force_deactivation' => false,
47
- 'pro' => true,
48
- 'source' => 'external',
49
- ],
50
- [
51
- 'name' => __( 'Jet Blog', 'jupiterx-core' ),
52
- 'slug' => 'jet-blog',
53
- 'required' => false,
54
- 'force_activation' => false,
55
- 'force_deactivation' => false,
56
- 'pro' => true,
57
- 'source' => 'external',
58
- ],
59
- [
60
- 'name' => __( 'Jet Menu', 'jupiterx-core' ),
61
- 'slug' => 'jet-menu',
62
- 'required' => false,
63
- 'force_activation' => false,
64
- 'force_deactivation' => false,
65
- 'pro' => true,
66
- 'source' => 'external',
67
- ],
68
- [
69
- 'name' => __( 'Jet Popup', 'jupiterx-core' ),
70
- 'slug' => 'jet-popup',
71
- 'required' => false,
72
- 'force_activation' => false,
73
- 'force_deactivation' => false,
74
- 'pro' => true,
75
- 'source' => 'external',
76
- ],
77
- [
78
- 'name' => __( 'Jet Tabs', 'jupiterx-core' ),
79
- 'slug' => 'jet-tabs',
80
- 'required' => false,
81
- 'force_activation' => false,
82
- 'force_deactivation' => false,
83
- 'pro' => true,
84
- 'source' => 'external',
85
- ],
86
- [
87
- 'name' => __( 'Jet WooBuilder', 'jupiterx-core' ),
88
- 'slug' => 'jet-woo-builder',
89
- 'required' => false,
90
- 'force_activation' => false,
91
- 'force_deactivation' => false,
92
- 'pro' => true,
93
- 'source' => 'external',
94
- ],
95
- [
96
- 'name' => __( 'Jet Tricks', 'jupiterx-core' ),
97
- 'slug' => 'jet-tricks',
98
- 'required' => false,
99
- 'force_activation' => false,
100
- 'force_deactivation' => false,
101
- 'pro' => true,
102
- 'source' => 'external',
103
- ],
104
- [
105
- 'name' => __( 'Jet Engine', 'jupiterx-core' ),
106
- 'slug' => 'jet-engine',
107
- 'required' => false,
108
- 'force_activation' => false,
109
- 'force_deactivation' => false,
110
- 'pro' => true,
111
- 'source' => 'external',
112
- ],
113
- [
114
- 'name' => __( 'Jet SmartFilters', 'jupiterx-core' ),
115
- 'slug' => 'jet-smart-filters',
116
- 'required' => false,
117
- 'force_activation' => false,
118
- 'force_deactivation' => false,
119
- 'pro' => true,
120
- 'source' => 'external',
121
- ],
122
- [
123
- 'name' => __( 'Advanced Custom Fields PRO', 'jupiterx-core' ),
124
- 'slug' => 'advanced-custom-fields-pro',
125
- 'required' => false,
126
- 'force_activation' => false,
127
- 'force_deactivation' => false,
128
- 'pro' => true,
129
- 'source' => 'external',
130
- ],
131
- [
132
- 'name' => __( 'Slider Revolution', 'jupiterx-core' ),
133
- 'slug' => 'revslider',
134
- 'required' => false,
135
- 'force_activation' => false,
136
- 'force_deactivation' => false,
137
- 'pro' => true,
138
- 'source' => 'external',
139
- ],
140
- [
141
- 'name' => __( 'Master Slider', 'jupiterx-core' ),
142
- 'slug' => 'masterslider',
143
- 'required' => false,
144
- 'force_activation' => false,
145
- 'force_deactivation' => false,
146
- 'pro' => true,
147
- 'source' => 'external',
148
- ],
149
- [
150
- 'name' => __( 'Layer Slider', 'jupiterx-core' ),
151
- 'slug' => 'layerslider',
152
- 'required' => false,
153
- 'force_activation' => false,
154
- 'force_deactivation' => false,
155
- 'pro' => true,
156
- 'source' => 'external',
157
- ],
158
- [
159
- 'name' => __( 'WPBakery Page Builder', 'jupiterx-core' ),
160
- 'slug' => 'raven',
161
- 'required' => false,
162
- 'force_activation' => false,
163
- 'force_deactivation' => false,
164
- 'pro' => true,
165
- 'source' => 'external',
166
- ],
167
- [
168
- 'name' => __( 'Customizer Reset', 'jupiterx-core' ),
169
- 'slug' => 'customizer-reset-by-wpzoom',
170
- 'required' => false,
171
- 'force_activation' => false,
172
- 'force_deactivation' => false,
173
- 'pro' => false,
174
- ],
175
- [
176
- 'name' => __( 'Customizer Export/Import', 'jupiterx-core' ),
177
- 'slug' => 'customizer-export-import',
178
- 'required' => false,
179
- 'force_activation' => false,
180
- 'force_deactivation' => false,
181
- 'pro' => false,
182
- ],
183
- ];
184
- return array_merge( $pro_plugins, $plugins );
185
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Add Jupiter X pro plugins.
4
+ *
5
+ * @package JupiterX_Core\Admin
6
+ *
7
+ * @since 1.9.0
8
+ */
9
+
10
+ add_filter( 'jupiterx_tgmpa_plugins', 'jupiterx_pro_plugins' );
11
+
12
+ /**
13
+ * Add Jupiter X Pro plugins.
14
+ *
15
+ * @since 1.9.0
16
+ *
17
+ * @param array $plugins Array of free Jupiter x plugins.
18
+ * @return array Array af free and pro plugins.
19
+ */
20
+ function jupiterx_pro_plugins( $plugins ) {
21
+
22
+ $pro_plugins = [
23
+ [
24
+ 'name' => __( 'Raven', 'jupiterx-core' ),
25
+ 'slug' => 'raven',
26
+ 'required' => false,
27
+ 'force_activation' => false,
28
+ 'force_deactivation' => false,
29
+ 'pro' => true,
30
+ 'source' => 'external',
31
+ 'label_type' => __( 'Optional', 'jupiterx-core' ),
32
+ ],
33
+ [
34
+ 'name' => __( 'Jupiter Donut', 'jupiterx-core' ),
35
+ 'slug' => 'jupiter-donut',
36
+ 'required' => false,
37
+ 'force_activation' => false,
38
+ 'force_deactivation' => false,
39
+ 'pro' => true,
40
+ 'source' => 'external',
41
+ 'label_type' => __( 'Optional', 'jupiterx-core' ),
42
+ ],
43
+ [
44
+ 'name' => __( 'Jet Elements', 'jupiterx-core' ),
45
+ 'slug' => 'jet-elements',
46
+ 'required' => false,
47
+ 'force_activation' => false,
48
+ 'force_deactivation' => false,
49
+ 'pro' => true,
50
+ 'source' => 'external',
51
+ 'label_type' => __( 'Optional', 'jupiterx-core' ),
52
+ ],
53
+ [
54
+ 'name' => __( 'Jet Blog', 'jupiterx-core' ),
55
+ 'slug' => 'jet-blog',
56
+ 'required' => false,
57
+ 'force_activation' => false,
58
+ 'force_deactivation' => false,
59
+ 'pro' => true,
60
+ 'source' => 'external',
61
+ 'label_type' => __( 'Optional', 'jupiterx-core' ),
62
+ ],
63
+ [
64
+ 'name' => __( 'Jet Menu', 'jupiterx-core' ),
65
+ 'slug' => 'jet-menu',
66
+ 'required' => false,
67
+ 'force_activation' => false,
68
+ 'force_deactivation' => false,
69
+ 'pro' => true,
70
+ 'source' => 'external',
71
+ 'label_type' => __( 'Optional', 'jupiterx-core' ),
72
+ ],
73
+ [
74
+ 'name' => __( 'Jet Popup', 'jupiterx-core' ),
75
+ 'slug' => 'jet-popup',
76
+ 'required' => false,
77
+ 'force_activation' => false,
78
+ 'force_deactivation' => false,
79
+ 'pro' => true,
80
+ 'source' => 'external',
81
+ 'label_type' => __( 'Optional', 'jupiterx-core' ),
82
+ ],
83
+ [
84
+ 'name' => __( 'Jet Tabs', 'jupiterx-core' ),
85
+ 'slug' => 'jet-tabs',
86
+ 'required' => false,
87
+ 'force_activation' => false,
88
+ 'force_deactivation' => false,
89
+ 'pro' => true,
90
+ 'source' => 'external',
91
+ 'label_type' => __( 'Optional', 'jupiterx-core' ),
92
+ ],
93
+ [
94
+ 'name' => __( 'Jet WooBuilder', 'jupiterx-core' ),
95
+ 'slug' => 'jet-woo-builder',
96
+ 'required' => false,
97
+ 'force_activation' => false,
98
+ 'force_deactivation' => false,
99
+ 'pro' => true,
100
+ 'source' => 'external',
101
+ 'label_type' => __( 'Optional', 'jupiterx-core' ),
102
+ ],
103
+ [
104
+ 'name' => __( 'Jet Tricks', 'jupiterx-core' ),
105
+ 'slug' => 'jet-tricks',
106
+ 'required' => false,
107
+ 'force_activation' => false,
108
+ 'force_deactivation' => false,
109
+ 'pro' => true,
110
+ 'source' => 'external',
111
+ 'label_type' => __( 'Optional', 'jupiterx-core' ),
112
+ ],
113
+ [
114
+ 'name' => __( 'Jet Engine', 'jupiterx-core' ),
115
+ 'slug' => 'jet-engine',
116
+ 'required' => false,
117
+ 'force_activation' => false,
118
+ 'force_deactivation' => false,
119
+ 'pro' => true,
120
+ 'source' => 'external',
121
+ 'label_type' => __( 'Optional', 'jupiterx-core' ),
122
+ ],
123
+ [
124
+ 'name' => __( 'Jet SmartFilters', 'jupiterx-core' ),
125
+ 'slug' => 'jet-smart-filters',
126
+ 'required' => false,
127
+ 'force_activation' => false,
128
+ 'force_deactivation' => false,
129
+ 'pro' => true,
130
+ 'source' => 'external',
131
+ 'label_type' => __( 'Optional', 'jupiterx-core' ),
132
+ ],
133
+ [
134
+ 'name' => __( 'Advanced Custom Fields PRO', 'jupiterx-core' ),
135
+ 'slug' => 'advanced-custom-fields-pro',
136
+ 'required' => false,
137
+ 'force_activation' => false,
138
+ 'force_deactivation' => false,
139
+ 'pro' => true,
140
+ 'source' => 'external',
141
+ 'label_type' => __( 'Optional', 'jupiterx-core' ),
142
+ ],
143
+ [
144
+ 'name' => __( 'Slider Revolution', 'jupiterx-core' ),
145
+ 'slug' => 'revslider',
146
+ 'required' => false,
147
+ 'force_activation' => false,
148
+ 'force_deactivation' => false,
149
+ 'pro' => true,
150
+ 'source' => 'external',
151
+ 'label_type' => __( 'Optional', 'jupiterx-core' ),
152
+ ],
153
+ [
154
+ 'name' => __( 'Master Slider', 'jupiterx-core' ),
155
+ 'slug' => 'masterslider',
156
+ 'required' => false,
157
+ 'force_activation' => false,
158
+ 'force_deactivation' => false,
159
+ 'pro' => true,
160
+ 'source' => 'external',
161
+ 'label_type' => __( 'Optional', 'jupiterx-core' ),
162
+ ],
163
+ [
164
+ 'name' => __( 'Layer Slider', 'jupiterx-core' ),
165
+ 'slug' => 'layerslider',
166
+ 'required' => false,
167
+ 'force_activation' => false,
168
+ 'force_deactivation' => false,
169
+ 'pro' => true,
170
+ 'source' => 'external',
171
+ 'label_type' => __( 'Optional', 'jupiterx-core' ),
172
+ ],
173
+ [
174
+ 'name' => __( 'WPBakery Page Builder', 'jupiterx-core' ),
175
+ 'slug' => 'raven',
176
+ 'required' => false,
177
+ 'force_activation' => false,
178
+ 'force_deactivation' => false,
179
+ 'pro' => true,
180
+ 'source' => 'external',
181
+ 'label_type' => __( 'Optional', 'jupiterx-core' ),
182
+ ],
183
+ [
184
+ 'name' => __( 'Customizer Reset', 'jupiterx-core' ),
185
+ 'slug' => 'customizer-reset-by-wpzoom',
186
+ 'required' => false,
187
+ 'force_activation' => false,
188
+ 'force_deactivation' => false,
189
+ 'pro' => false,
190
+ 'label_type' => __( 'Optional', 'jupiterx-core' ),
191
+ ],
192
+ [
193
+ 'name' => __( 'Customizer Export/Import', 'jupiterx-core' ),
194
+ 'slug' => 'customizer-export-import',
195
+ 'required' => false,
196
+ 'force_activation' => false,
197
+ 'force_deactivation' => false,
198
+ 'pro' => false,
199
+ 'label_type' => __( 'Optional', 'jupiterx-core' ),
200
+ ],
201
+ ];
202
+ return array_merge( $pro_plugins, $plugins );
203
+ }
includes/admin/update-plugins/class-update-plugins.php CHANGED
@@ -1,102 +1,102 @@
1
- <?php
2
- /**
3
- * JupiterX_Core_Update_Plugins class filters the update plugins.
4
- *
5
- * @package JupiterX_Core\Admin
6
- *
7
- * @since 1.9.0
8
- */
9
-
10
- if ( ! class_exists( 'JupiterX_Core_Update_Plugins' ) ) {
11
- /**
12
- * Filter Update Plugins.
13
- *
14
- * @since 1.9.0
15
- */
16
- class JupiterX_Core_Update_Plugins {
17
-
18
- /**
19
- * Constructor.
20
- *
21
- * @since 1.9.0
22
- */
23
- public function __construct() {
24
- add_filter( 'pre_set_site_transient_update_plugins', [ $this, 'update_plugins' ], 999, 1 );
25
- }
26
-
27
- /**
28
- * Filter updates for managed pro plugins.
29
- *
30
- * @SuppressWarnings(PHPMD.NPathComplexity)
31
- *
32
- * @since 1.9.0
33
- *
34
- * @param array $transient Transient object.
35
- * @return object
36
- */
37
- public function update_plugins( $transient ) {
38
- if ( ! is_object( $transient ) ) {
39
- return $transient;
40
- }
41
-
42
- if ( ! isset( $transient->response ) ) {
43
- return $transient;
44
- }
45
-
46
- if ( ! function_exists( 'jupiterx_get_managed_plugins' ) ) {
47
- return $transient;
48
- }
49
-
50
- $force_check = ! empty( jupiterx_get( 'force-check' ) );
51
- $installed_plugins = $this->get_plugins();
52
- $managed_plugins = jupiterx_get_managed_plugins( $force_check );
53
-
54
- foreach ( $managed_plugins as $managed_plugin ) {
55
- if ( empty( $managed_plugin->source ) || 'false' === $managed_plugin->pro ) {
56
- continue;
57
- }
58
-
59
- foreach ( $installed_plugins as $basename => $installed_plugin ) {
60
- if ( strpos( $basename, $managed_plugin->slug ) === false ) {
61
- continue;
62
- }
63
-
64
- if ( version_compare( $managed_plugin->version, $installed_plugin['Version'] ) <= 0 ) {
65
- unset( $transient->response[ $basename ] );
66
-
67
- continue;
68
- }
69
-
70
- $update = new stdClass();
71
-
72
- $update->slug = $managed_plugin->slug;
73
- $update->plugin = $basename;
74
- $update->new_version = $managed_plugin->version;
75
- $update->url = false;
76
- $update->package = $managed_plugin->source;
77
-
78
- $transient->response[ $basename ] = $update;
79
- }
80
- }
81
-
82
- return $transient;
83
- }
84
-
85
- /**
86
- * Wrapper around the core WP get_plugins function, making sure it's actually available.
87
- *
88
- * @since 1.9.0
89
- *
90
- * @return array Array of installed plugins with plugin information.
91
- */
92
- public function get_plugins() {
93
- if ( ! function_exists( 'get_plugins' ) ) {
94
- require_once ABSPATH . 'wp-admin/includes/plugin.php';
95
- }
96
-
97
- return get_plugins();
98
- }
99
- }
100
-
101
- new JupiterX_Core_Update_Plugins();
102
- }
1
+ <?php
2
+ /**
3
+ * JupiterX_Core_Update_Plugins class filters the update plugins.
4
+ *
5
+ * @package JupiterX_Core\Admin
6
+ *
7
+ * @since 1.9.0
8
+ */
9
+
10
+ if ( ! class_exists( 'JupiterX_Core_Update_Plugins' ) ) {
11
+ /**
12
+ * Filter Update Plugins.
13
+ *
14
+ * @since 1.9.0
15
+ */
16
+ class JupiterX_Core_Update_Plugins {
17
+
18
+ /**
19
+ * Constructor.
20
+ *
21
+ * @since 1.9.0
22
+ */
23
+ public function __construct() {
24
+ add_filter( 'pre_set_site_transient_update_plugins', [ $this, 'update_plugins' ], 999, 1 );
25
+ }
26
+
27
+ /**
28
+ * Filter updates for managed pro plugins.
29
+ *
30
+ * @SuppressWarnings(PHPMD.NPathComplexity)
31
+ *
32
+ * @since 1.9.0
33
+ *
34
+ * @param array $transient Transient object.
35
+ * @return object
36
+ */
37
+ public function update_plugins( $transient ) {
38
+ if ( ! is_object( $transient ) ) {
39
+ return $transient;
40
+ }
41
+
42
+ if ( ! isset( $transient->response ) ) {
43
+ return $transient;
44
+ }
45
+
46
+ if ( ! function_exists( 'jupiterx_get_managed_plugins' ) ) {
47
+ return $transient;
48
+ }
49
+
50
+ $force_check = ! empty( jupiterx_get( 'force-check' ) );
51
+ $installed_plugins = $this->get_plugins();
52
+ $managed_plugins = jupiterx_get_managed_plugins( $force_check );
53
+
54
+ foreach ( $managed_plugins as $managed_plugin ) {
55
+ if ( empty( $managed_plugin->source ) || 'false' === $managed_plugin->pro ) {
56
+ continue;
57
+ }
58
+
59
+ foreach ( $installed_plugins as $basename => $installed_plugin ) {
60
+ if ( strpos( $basename, $managed_plugin->slug ) === false ) {
61
+ continue;
62
+ }
63
+
64
+ if ( version_compare( $managed_plugin->version, $installed_plugin['Version'] ) <= 0 ) {
65
+ unset( $transient->response[ $basename ] );
66
+
67
+ continue;
68
+ }
69
+
70
+ $update = new stdClass();
71
+
72
+ $update->slug = $managed_plugin->slug;
73
+ $update->plugin = $basename;
74
+ $update->new_version = $managed_plugin->version;
75
+ $update->url = false;
76
+ $update->package = $managed_plugin->source;
77
+
78
+ $transient->response[ $basename ] = $update;
79
+ }
80
+ }
81
+
82
+ return $transient;
83
+ }
84
+
85
+ /**
86
+ * Wrapper around the core WP get_plugins function, making sure it's actually available.
87
+ *
88
+ * @since 1.9.0
89
+ *
90
+ * @return array Array of installed plugins with plugin information.
91
+ */
92
+ public function get_plugins() {
93
+ if ( ! function_exists( 'get_plugins' ) ) {
94
+ require_once ABSPATH . 'wp-admin/includes/plugin.php';
95
+ }
96
+
97
+ return get_plugins();
98
+ }
99
+ }
100
+
101
+ new JupiterX_Core_Update_Plugins();
102
+ }
includes/compiler/class-compiler.php CHANGED
@@ -1,1018 +1,1018 @@
1
- <?php
2
- /**
3
- * This class compiles and minifies CSS, LESS and JS.
4
- *
5
- * @package JupiterX\Framework\API\Compiler
6
- *
7
- * @since 1.0.0
8
- */
9
-
10
- /**
11
- * Compiles and minifies CSS, LESS and JS.
12
- *
13
- * @since 1.0.0
14
- * @ignore
15
- * @access private
16
- *
17
- * @package JupiterX\Framework\API\Compiler
18
- * @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
19
- * @SuppressWarnings(PHPMD.ExcessiveClassLength)
20
- */
21
- final class _JupiterX_Compiler {
22
-
23
- /**
24
- * Compiler's runtime configuration parameters.
25
- *
26
- * @var array
27
- */
28
- private $config;
29
-
30
- /**
31
- * Cache dir.
32
- *
33
- * @var string
34
- */
35
- private $dir;
36
-
37
- /**
38
- * Cache url.
39
- *
40
- * @var string
41
- */
42
- private $url;
43
-
44
- /**
45
- * The fragment currently being processed.
46
- *
47
- * @var string
48
- */
49
- private $current_fragment;
50
-
51
- /**
52
- * The compiled content.
53
- *
54
- * @var string
55
- */
56
- private $compiled_content;
57
-
58
- /**
59
- * Compiled content's filename.
60
- *
61
- * @var string
62
- */
63
- private $filename;
64
-
65
- /**
66
- * Create a new Compiler.
67
- *
68
- * @since 1.0.0
69
- *
70
- * @param array $config Runtime configuration parameters for the Compiler.
71
- */
72
- public function __construct( array $config ) {
73
- $this->config = $this->init_config( $config );
74
- $this->dir = jupiterx_get_compiler_dir( is_admin() ) . $this->config['id'];
75
- $this->url = jupiterx_get_compiler_url( is_admin() ) . $this->config['id'];
76
- }
77
-
78
- /**
79
- * Run the compiler.
80
- *
81
- * @since 1.0.0
82
- *
83
- * @return void
84
- */
85
- public function run_compiler() {
86
- // Modify the WP Filesystem method.
87
- add_filter( 'filesystem_method', array( $this, 'modify_filesystem_method' ) );
88
-
89
- $this->set_fragments();
90
- $this->set_filename();
91
-
92
- if ( ! $this->cache_file_exist() ) {
93
- $this->filesystem();
94
- $this->maybe_make_dir();
95
- $this->combine_fragments();
96
- $this->cache_file();
97
- }
98
-
99
- if ( $this->config['enqueue'] ) {
100
- $this->enqueue_file();
101
- }
102
-
103
- // Keep it safe and reset the WP Filesystem method.
104
- remove_filter( 'filesystem_method', array( $this, 'modify_filesystem_method' ) );
105
- }
106
-
107
- /**
108
- * Callback to set the WP Filesystem method.
109
- *
110
- * @since 1.0.0
111
- *
112
- * @return string
113
- */
114
- public function modify_filesystem_method() {
115
- return 'direct';
116
- }
117
-
118
- /**
119
- * Initialise the WP Filesystem.
120
- *
121
- * @since 1.0.0
122
- *
123
- * @return bool|void
124
- */
125
- public function filesystem() {
126
-
127
- // If the WP_Filesystem is not already loaded, load it.
128
- if ( ! function_exists( 'WP_Filesystem' ) ) {
129
- require_once ABSPATH . '/wp-admin/includes/file.php';
130
- }
131
-
132
- // If the WP_Filesystem is not initialized or is not set to WP_Filesystem_Direct, then initialize it.
133
- if ( $this->is_wp_filesystem_direct() ) {
134
- return true;
135
- }
136
-
137
- // Initialize the filesystem.
138
- $response = WP_Filesystem();
139
-
140
- // If the filesystem did not initialize, then generate a report and exit.
141
- if ( true !== $response || ! $this->is_wp_filesystem_direct() ) {
142
- return $this->kill();
143
- }
144
-
145
- return true;
146
- }
147
-
148
- /**
149
- * Check if the filesystem is set to "direct".
150
- *
151
- * @since 1.0.0
152
- *
153
- * @return bool
154
- */
155
- private function is_wp_filesystem_direct() {
156
- return isset( $GLOBALS['wp_filesystem'] ) && is_a( $GLOBALS['wp_filesystem'], 'WP_Filesystem_Direct' );
157
- }
158
-
159
- /**
160
- * Make directory.
161
- *
162
- * @since 1.0.0
163
- *
164
- * @return bool
165
- */
166
- private function maybe_make_dir() {
167
-
168
- if ( ! @is_dir( $this->dir ) ) { // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- This is a valid use case.
169
- wp_mkdir_p( $this->dir );
170
- }
171
-
172
- return is_writable( $this->dir );
173
- }
174
-
175
- /**
176
- * Set class fragments.
177
- *
178
- * @since 1.0.0
179
- *
180
- * @return void
181
- */
182
- public function set_fragments() {
183
- global $_jupiterx_compiler_added_fragments;
184
-
185
- $added_fragments = jupiterx_get( $this->config['id'], $_jupiterx_compiler_added_fragments[ $this->config['format'] ] );
186
-
187
- if ( $added_fragments ) {
188
- $this->config['fragments'] = array_merge( $this->config['fragments'], $added_fragments );
189
- }
190
-
191
- /**
192
- * Filter the compiler fragment files.
193
- *
194
- * The dynamic portion of the hook name, $this->config['id'], refers to the compiler id used as a reference.
195
- *
196
- * @since 1.0.0
197
- *
198
- * @param array $fragments An array of fragment files.
199
- */
200
- $this->config['fragments'] = apply_filters( 'jupiterx_compiler_fragments_' . $this->config['id'], $this->config['fragments'] );
201
- }
202
-
203
- /**
204
- * Set the filename for the compiled asset.
205
- *
206
- * @since 1.0.0
207
- *
208
- * @return void
209
- */
210
- public function set_filename() {
211
- $hash = $this->hash( $this->config );
212
-
213
- if ( empty( _jupiterx_get_cache_busting() ) ) {
214
- $this->config['version'] = $hash;
215
-
216
- $hash = 'style';
217
-
218
- if ( 'script' === $this->config['type'] ) {
219
- $hash = 'script';
220
- }
221
- }
222
-
223
- $this->filename = $hash . '.' . $this->get_extension();
224
- }
225
-
226
- /**
227
- * Hash the given array.
228
- *
229
- * @since 1.0.0
230
- *
231
- * @param array $given_array Given array to be hashed.
232
- *
233
- * @return string
234
- */
235
- public function hash( array $given_array ) {
236
- return substr( md5( @serialize( $given_array ) ), 0, 7 ); // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged, WordPress.PHP.DiscouragedPHPFunctions.serialize_serialize -- Valid use case.
237
- }
238
-
239
- /**
240
- * Checks if the file exists on the filesystem, meaning it's been cached.
241
- *
242
- * @since 1.0.0
243
- *
244
- * @return bool
245
- */
246
- public function cache_file_exist() {
247
- $filename = $this->get_filename();
248
-
249
- if ( ( _jupiterx_is_compiler_dev_mode() ) || is_preview() ) {
250
- return false;
251
- }
252
-
253
- if ( empty( $filename ) ) {
254
- return false;
255
- }
256
-
257
- return file_exists( $filename );
258
- }
259
-
260
- /**
261
- * Get the absolute path of the cached and compiled file.
262
- *
263
- * @since 1.0.0
264
- *
265
- * @return string
266
- */
267
- public function get_filename() {
268
- if ( isset( $this->filename ) ) {
269
- return $this->dir . '/' . $this->filename;
270
- }
271
-
272
- return '';
273
- }
274
-
275
- /**
276
- * Create cached file.
277
- *
278
- * @since 1.0.0
279
- *
280
- * @return bool
281
- */
282
- public function cache_file() {
283
- $filename = $this->get_filename();
284
-
285
- if ( empty( $filename ) ) {
286
- return false;
287
- }
288
-
289
- // It is safe to access the filesystem because we made sure it was set.
290
- return $GLOBALS['wp_filesystem']->put_contents( $filename, $this->compiled_content, FS_CHMOD_FILE );
291
- }
292
-
293
- /**
294
- * Enqueue cached file.
295
- *
296
- * @since 1.0.0
297
- *
298
- * @return void|bool
299
- */
300
- private function enqueue_file() {
301
-
302
- // Enqueue CSS file.
303
- if ( 'style' === $this->config['type'] ) {
304
- return wp_enqueue_style(
305
- $this->config['id'],
306
- $this->get_url(),
307
- $this->config['dependencies'],
308
- $this->config['version']
309
- );
310
- }
311
-
312
- // Enqueue JS file.
313
- if ( 'script' === $this->config['type'] ) {
314
- return wp_enqueue_script(
315
- $this->config['id'],
316
- $this->get_url(),
317
- $this->config['dependencies'],
318
- $this->config['version'],
319
- $this->config['in_footer']
320
- );
321
- }
322
-
323
- return false;
324
- }
325
-
326
- /**
327
- * Get cached file url.
328
- *
329
- * @since 1.0.0
330
- *
331
- * @return string
332
- */
333
- public function get_url() {
334
- $url = trailingslashit( $this->url ) . $this->filename;
335
-
336
- if ( is_ssl() ) {
337
- $url = str_replace( 'http://', 'https://', $url );
338
- }
339
-
340
- return $url;
341
- }
342
-
343
- /**
344
- * Get the file extension from the configured "type".
345
- *
346
- * @since 1.0.0
347
- *
348
- * @return string|null
349
- */
350
- public function get_extension() {
351
-
352
- if ( 'style' === $this->config['type'] ) {
353
- return 'css';
354
- }
355
-
356
- if ( 'script' === $this->config['type'] ) {
357
- return 'js';
358
- }
359
- }
360
-
361
- /**
362
- * Combine content of the fragments.
363
- *
364
- * @since 1.0.0
365
- *
366
- * @return void
367
- */
368
- public function combine_fragments() {
369
- $content = '';
370
-
371
- // Loop through fragments.
372
- foreach ( $this->config['fragments'] as $fragment ) {
373
-
374
- // Stop here if the fragment is empty.
375
- if ( empty( $fragment ) ) {
376
- continue;
377
- }
378
-
379
- $fragment_content = $this->get_content( $fragment );
380
-
381
- // Stop here if no content or content is an html page.
382
- if ( ! $fragment_content || preg_match( '#^\s*\<#', $fragment_content ) ) {
383
- continue;
384
- }
385
-
386
- // Continue processing style.
387
- if ( 'style' === $this->config['type'] ) {
388
- $fragment_content = $this->replace_css_url( $fragment_content );
389
- $fragment_content = $this->add_content_media_query( $fragment_content );
390
- }
391
-
392
- // If there's content, start a new line.
393
- if ( $content ) {
394
- $content .= "\n\n";
395
- }
396
-
397
- $content .= $fragment_content;
398
- }
399
-
400
- $this->compiled_content = ! empty( $content ) ? $this->format_content( $content ) : '';
401
- }
402
-
403
- /**
404
- * Get the fragment's content.
405
- *
406
- * @since 1.0.0
407
- *
408
- * @param string|callable $fragment The given fragment from which to get the content.
409
- *
410
- * @return bool|string
411
- */
412
- private function get_content( $fragment ) {
413
- // Set the current fragment used by other functions.
414
- $this->current_fragment = $fragment;
415
-
416
- // If the fragment is callable, call it to get the content.
417
- if ( $this->is_function( $fragment ) ) {
418
- return $this->get_function_content();
419
- }
420
-
421
- $content = $this->get_internal_content();
422
-
423
- // Try remote content if the internal content returned false.
424
- if ( empty( $content ) ) {
425
- $content = $this->get_remote_content();
426
- }
427
-
428
- return $content;
429
- }
430
-
431
- /**
432
- * Get internal file content.
433
- *
434
- * @since 1.0.0
435
- *
436
- * @return string|bool
437
- */
438
- public function get_internal_content() {
439
- $fragment = $this->current_fragment;
440
-
441
- if ( ! file_exists( $fragment ) ) {
442
-
443
- // Replace URL with path.
444
- $fragment = jupiterx_url_to_path( $fragment );
445
-
446
- // Stop here if it isn't a valid file.
447
- if ( ! file_exists( $fragment ) || 0 === @filesize( $fragment ) ) { // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Valid use case.
448
- return false;
449
- }
450
- }
451
-
452
- // It is safe to access the filesystem because we made sure it was set.
453
- return $GLOBALS['wp_filesystem']->get_contents( $fragment );
454
- }
455
-
456
- /**
457
- * Get external file content.
458
- *
459
- * @since 1.0.0
460
- *
461
- * @return string|bool
462
- */
463
- public function get_remote_content() {
464
- $fragment = $this->current_fragment;
465
-
466
- if ( empty( $fragment ) ) {
467
- return false;
468
- }
469
-
470
- // For a relative URL, add http: to it.
471
- if ( substr( $fragment, 0, 2 ) === '//' ) {
472
- $fragment = 'http:' . $fragment;
473
- } elseif ( substr( $fragment, 0, 1 ) === '/' ) { // Add domain if it is local but could not be fetched as a file.
474
- $fragment = site_url( $fragment );
475
- }
476
-
477
- $request = wp_remote_get( $fragment );
478
-
479
- if ( is_wp_error( $request ) ) {
480
- return '';
481
- }
482
-
483
- // If no content was received and the URL is not https, then convert the URL to SSL and retry.
484
- if (
485
- ( ! isset( $request['body'] ) || 200 !== $request['response']['code'] ) &&
486
- ( substr( $fragment, 0, 8 ) !== 'https://' )
487
- ) {
488
- $fragment = str_replace( 'http://', 'https://', $fragment );
489
- $request = wp_remote_get( $fragment );
490
-
491
- if ( is_wp_error( $request ) ) {
492
- return '';
493
- }
494
- }
495
-
496
- if ( ( ! isset( $request['body'] ) || 200 !== $request['response']['code'] ) ) {
497
- return false;
498
- }
499
-
500
- return wp_remote_retrieve_body( $request );
501
- }
502
-
503
- /**
504
- * Get function content.
505
- *
506
- * @since 1.0.0
507
- *
508
- * @return string|bool
509
- */
510
- public function get_function_content() {
511
-
512
- if ( ! is_callable( $this->current_fragment ) ) {
513
- return false;
514
- }
515
-
516
- return call_user_func( $this->current_fragment );
517
- }
518
-
519
- /**
520
- * Wrap content in query.
521
- *
522
- * @since 1.0.0
523
- *
524
- * @param string $content Given content to process.
525
- *
526
- * @return string
527
- */
528
- public function add_content_media_query( $content ) {
529
-
530
- // Ignore if the fragment is a function.
531
- if ( $this->is_function( $this->current_fragment ) ) {
532
- return $content;
533
- }
534
-
535
- $query = wp_parse_url( $this->current_fragment, PHP_URL_QUERY );
536
-
537
- // Bail out if there are no query args or no media query.
538
- if ( empty( $query ) || false === stripos( $query, 'jupiterx_compiler_media_query' ) ) {
539
- return $content;
540
- }
541
-
542
- // Wrap the content in the query.
543
- return sprintf(
544
- "@media %s {\n%s\n}\n",
545
- jupiterx_get( 'jupiterx_compiler_media_query', wp_parse_args( $query ) ),
546
- $content
547
- );
548
- }
549
-
550
- /**
551
- * Format CSS, LESS and JS content.
552
- *
553
- * @since 1.0.0
554
- *
555
- * @param string $content Given content to process.
556
- *
557
- * @return string
558
- */
559
- public function format_content( $content ) {
560
-
561
- if ( 'style' === $this->config['type'] ) {
562
-
563
- if ( 'less' === $this->config['format'] ) {
564
-
565
- if ( ! class_exists( 'JupiterX_Lessc' ) ) {
566
- jupiterx_core()->load_files( [ 'compiler/vendors/lessc' ] );
567
- }
568
-
569
- $parser = new JupiterX_Lessc();
570
-
571
- $parser = $this->register_less_functions( $parser );
572
-
573
- $parser->setVariables( $this->config['variables'] );
574
-
575
- $content = $parser->compile( $content );
576
- }
577
-
578
- if ( ! function_exists( 'jupiterx_parse_css' ) ) {
579
- return $content;
580
- }
581
-
582
- $content = jupiterx_parse_css( $content );
583
-
584
- if ( ! _jupiterx_is_compiler_dev_mode() ) {
585
- $content = $this->minify_style( $content );
586
- }
587
-
588
- return $content;
589
- }
590
-
591
- if ( 'script' === $this->config['type'] && ! _jupiterx_is_compiler_dev_mode() && $this->config['minify_js'] ) {
592
-
593
- if ( ! class_exists( 'JSMin' ) ) {
594
- jupiterx_core()->load_files( [ 'compiler/vendors/js-minifier' ] );
595
- }
596
-
597
- $js_min = new JSMin( $content );
598
- return $js_min->min();
599
- }
600
-
601
- return $content;
602
- }
603
-
604
- /**
605
- * Replace CSS URL shortcuts with a valid URL.
606
- *
607
- * @since 1.0.0
608
- *
609
- * @param string $content Given content to process.
610
- *
611
- * @return string
612
- */
613
- public function replace_css_url( $content ) {
614
- return preg_replace_callback(
615
- '#url\s*\(\s*[\'"]*?([^\'"\)]+)[\'"]*\s*\)#i',
616
- array( $this, 'replace_css_url_callback' ),
617
- $content
618
- );
619
- }
620
-
621
- /**
622
- * Convert any CSS URL relative paths to absolute URLs.
623
- *
624
- * @since 1.0.0
625
- *
626
- * @param array $matches Matches to process, where 0 is the CSS' URL() and 1 is the URI.
627
- *
628
- * @return string
629
- */
630
- public function replace_css_url_callback( $matches ) {
631
-
632
- // If the URI is absolute, bail out and return the CSS.
633
- if ( _jupiterx_is_uri( $matches[1] ) ) {
634
- return $matches[0];
635
- }
636
-
637
- $base = $this->current_fragment;
638
-
639
- // Separate the placeholders and path.
640
- $paths = explode( '../', $matches[1] );
641
-
642
- /**
643
- * Walk backwards through each of the the fragment's directories, one-by-one. The `foreach` loop
644
- * provides us with a performant way to walk the fragment back to its base path based upon the
645
- * number of placeholders.
646
- */
647
- foreach ( $paths as $path ) {
648
- $base = dirname( $base );
649
- }
650
-
651
- // Make sure it is a valid base.
652
- if ( '.' === $base ) {
653
- $base = '';
654
- }
655
-
656
- // Rebuild the URL and make sure it is valid using the jupiterx_path_to_url function.
657
- $url = jupiterx_path_to_url( trailingslashit( $base ) . ltrim( end( $paths ), '/\\' ) );
658
-
659
- // Return the rebuilt path converted to an URL.
660
- return 'url("' . $url . '")';
661
- }
662
-
663
- /**
664
- * Register LESS_PHP functions.
665
- *
666
- * @since 1.0.0
667
- *
668
- * @param object $parser The LESS parser.
669
- *
670
- * @todo Refactoring is required.
671
- *
672
- * @return object
673
- */
674
- private function register_less_functions( $parser ) {
675
- $parser->registerFunction( 'jupiterx_value', function( $arg ) {
676
- $output = '';
677
-
678
- if ( isset( $arg[2][1][2][0] ) ) {
679
- $output = $arg[2][1][2][0]; // Default.
680
- }
681
-
682
- if ( ! empty( $arg[2][0][2][1][1] ) ) {
683
- return $arg[2][0][2][1][1]; // E.g. ~"@{@{var}-width}".
684
- }
685
-
686
- if ( ! empty( $arg[2][0][1] ) ) {
687
- $value = $arg[2][0][1]; // E.g. @text-size.
688
- $unit = empty( $arg[2][0][2] ) ? '' : $arg[2][0][2]; // E.g. @text-size unit.
689
-
690
- return $value . $unit;
691
- }
692
-
693
- return $output;
694
- } );
695
-
696
- $parser->registerFunction( 'jupiterx_value_pattern', function( $arg ) {
697
- if ( 0 === strlen( $arg[2][0][1] ) ) {
698
- return '';
699
- }
700
-
701
- list($type, $value, $unit) = $arg[2][0];
702
-
703
- $format = $arg[2][1][2][0];
704
-
705
- // When value is 0px, parser automatically remove px (but not %) from it.
706
- if ( 0 == $arg[2][0][1] ) { // @codingStandardsIgnoreLine
707
- $unit = '%';
708
- }
709
-
710
- return sprintf( $format, $value . $unit );
711
- } );
712
-
713
- $parser->registerFunction( 'jupiterx_replace', function( $args ) {
714
- list( $string, $search, $replace ) = $args[2];
715
-
716
- // Arrange if string is from a variable use the true condition. e.g. @{var-name}.
717
- $string = isset( $string[2][1][1] ) ? $string[2][1][1] : $string[2][0];
718
- $search = $search[2][0];
719
- $replace = $replace[2][0];
720
-
721
- return str_replace( $search, $replace, $string );
722
- } );
723
-
724
- return $parser;
725
- }
726
-
727
- /**
728
- * Initialize the configuration.
729
- *
730
- * @since 1.0.0
731
- *
732
- * @param array $config Runtime configuration parameters for the Compiler.
733
- *
734
- * @return array
735
- */
736
- private function init_config( array $config ) {
737
- // Fix dependencies, if "depedencies" is specified.
738
- if ( isset( $config['depedencies'] ) ) {
739
- $config['dependencies'] = $config['depedencies'];
740
- unset( $config['depedencies'] );
741
- }
742
-
743
- $defaults = [
744
- 'id' => false,
745
- 'type' => false,
746
- 'format' => false,
747
- 'fragments' => [],
748
- 'variables' => apply_filters( 'jupiterx_compiler_less_variables', [] ),
749
- 'dependencies' => false,
750
- 'in_footer' => false,
751
- 'minify_js' => true,
752
- 'version' => JUPITERX_VERSION,
753
- 'enqueue' => true,
754
- ];
755
-
756
- return array_merge( $defaults, $config );
757
- }
758
-
759
- /**
760
- * Get the fragments' modification times.
761
- *
762
- * @since 1.0.0
763
- *
764
- * @return array
765
- * @SuppressWarnings(PHPMD.UnusedPrivateMethod)
766
- */
767
- private function get_fragments_filemtime() {
768
- $fragments_filemtime = array();
769
-
770
- foreach ( $this->config['fragments'] as $index => $fragment ) {
771
-
772
- // Skip this one if the fragment is a function.
773
- if ( $this->is_function( $fragment ) ) {
774
- if ( ! is_callable( $fragment ) ) {
775
- continue;
776
- }
777
-
778
- $fragments_filemtime[ $index ] = $this->hash( [ call_user_func( $fragment ) ] );
779
-
780
- }
781
-
782
- if ( file_exists( $fragment ) ) {
783
- $fragments_filemtime[ $index ] = @filemtime( $fragment ); // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Valid use case.
784
- }
785
- }
786
-
787
- return $fragments_filemtime;
788
- }
789
-
790
- /**
791
- * Get the new hash for the given fragments' modification times.
792
- *
793
- * @since 1.0.0
794
- *
795
- * @param string $hash The original hash to modify.
796
- * @param array $fragments_filemtime Array of fragments' modification times.
797
- *
798
- * @return string
799
- * @SuppressWarnings(PHPMD.UnusedPrivateMethod)
800
- */
801
- private function get_new_hash( $hash, array $fragments_filemtime ) {
802
-
803
- if ( empty( $fragments_filemtime ) ) {
804
- return $hash;
805
- }
806
-
807
- // Set filemtime hash.
808
- $_hash = $this->hash( $fragments_filemtime );
809
-
810
- $this->remove_modified_files( $hash, $_hash );
811
-
812
- // Set the new hash which will trigger a new compiling.
813
- return $hash . '-' . $_hash;
814
- }
815
-
816
- /**
817
- * Remove any modified files. A file is considered modified when:
818
- *
819
- * 1. It has both a base hash and filemtime hash, separated by '-'.
820
- * 2. Its base hash matches the given hash.
821
- * 3. Its filemtime hash does not match the given filemtime hash.
822
- *
823
- * @since 1.0.0
824
- *
825
- * @param string $hash Base hash.
826
- * @param string $filemtime_hash The filemtime hash (from hashing the fragments).
827
- *
828
- * @return void
829
- */
830
- private function remove_modified_files( $hash, $filemtime_hash ) {
831
-
832
- if ( ! is_dir( $this->dir ) ) {
833
- return;
834
- }
835
-
836
- $items = @scandir( $this->dir ); // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Valid use case.
837
- unset( $items[0], $items[1] );
838
-
839
- if ( empty( $items ) ) {
840
- return;
841
- }
842
-
843
- foreach ( $items as $item ) {
844
-
845
- // Skip this one if it's a directory.
846
- if ( @is_dir( $item ) ) { // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Valid use case.
847
- continue;
848
- }
849
-
850
- // Skip this one if it's not the same type.
851
- if ( pathinfo( $item, PATHINFO_EXTENSION ) !== $this->get_extension() ) {
852
- continue;
853
- }
854
-
855
- // Skip this one if it does not have a '-' in the filename.
856
- if ( strpos( $item, '-' ) === false ) {
857
- continue;
858
- }
859
-
860
- $hash_parts = explode( '-', pathinfo( $item, PATHINFO_FILENAME ) );
861
-
862
- // Skip this one if it does not match the given base hash.
863
- if ( $hash_parts[0] !== $hash ) {
864
- continue;
865
- }
866
-
867
- // Skip this one if it does match the given filemtime's hash.
868
- if ( $hash_parts[1] === $filemtime_hash ) {
869
- continue;
870
- }
871
-
872
- // Clean up other modified files.
873
- @unlink( $this->dir . '/' . $item ); // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Valid use case.
874
- }
875
- }
876
-
877
- /**
878
- * Minify the CSS.
879
- *
880
- * @since 1.0.0
881
- *
882
- * @param string $content Given content to process.
883
- *
884
- * @return string
885
- */
886
- private function minify_style( $content ) {
887
- $replace = array(
888
- '/([^\r\n{}]+)(,(?=[^}]*{)|\s*{)}/' => '', // Strip empty selectors.
889
- '/@media\s\(.*\).*{}/' => '', // Strip empty @media.
890
- '#/\*.*?\*/#s' => '', // Strip comments.
891
- '#\s\s+#' => ' ', // Strip excess whitespace.
892
- );
893
-
894
- $search = array_keys( $replace );
895
- $content = preg_replace( $search, $replace, $content );
896
-
897
- $replace = array(
898
- ': ' => ':',
899
- '; ' => ';',
900
- ' {' => '{',
901
- ' }' => '}',
902
- ', ' => ',',
903
- '{ ' => '{',
904
- ',\n' => ',', // Don't wrap multiple selectors.
905
- '\n}' => '}', // Don't wrap closing braces.
906
- '} ' => "}\n", // Put each rule on it's own line.
907
- '\n' => '', // Remove all line breaks.
908
- );
909
-
910
- $search = array_keys( $replace );
911
-
912
- return trim( str_replace( $search, $replace, $content ) );
913
- }
914
-
915
- /**
916
- * Check if the given fragment is a callable.
917
- *
918
- * @since 1.0.0
919
- *
920
- * @param mixed $fragment Given fragment to check.
921
- *
922
- * @return bool
923
- */
924
- private function is_function( $fragment ) {
925
- return ( is_array( $fragment ) || is_callable( $fragment ) );
926
- }
927
-
928
- /**
929
- * Kill it :(
930
- *
931
- * @since 1.0.0
932
- *
933
- * @return void
934
- */
935
- private function kill() {
936
-
937
- // Send report if set.
938
- if ( jupiterx_get( 'jupiterx_send_compiler_report' ) ) { // @codingStandardsIgnoreLine
939
- // $this->report(); // @codingStandardsIgnoreLine
940
- }
941
-
942
- $html = jupiterx_output( 'jupiterx_compiler_error_title_text', sprintf(
943
- '<h2>%s</h2>',
944
- __( 'Not cool, Jupiter cannot work its magic :(', 'jupiterx-core' )
945
- ) );
946
-
947
- $html .= jupiterx_output( 'jupiterx_compiler_error_message_text', sprintf(
948
- '<p>%s</p>',
949
- __( 'Your current install or file permission prevents Jupiter from working its magic. Please get in touch with Jupiter support. We will gladly get you started within 24 - 48 hours (working days).', 'jupiterx-core' )
950
- ) );
951
-
952
- $html .= jupiterx_output( 'jupiterx_compiler_error_contact_text', sprintf(
953
- '<a class="button" href="https://themes.artbees.net/support/" target="_blank">%s</a>',
954
- __( 'Contact Jupiter Support', 'jupiterx-core' )
955
- ) );
956
-
957
- $html .= jupiterx_output( 'jupiterx_compiler_error_report_text', sprintf(
958
- '<p style="margin-top: 12px; font-size: 12px;"><a href="' . add_query_arg( 'jupiterx_send_compiler_report', true ) . '">%1$s</a>. %2$s</p>',
959
- __( 'Send us an automatic report', 'jupiterx-core' ),
960
- __( 'We respect your time and understand you might not be able to contact us.', 'jupiterx-core' )
961
- ) );
962
-
963
- wp_die( wp_kses_post( $html ) );
964
- }
965
-
966
- /**
967
- * Send report.
968
- *
969
- * @since 1.0.0
970
- *
971
- * @todo Decide if we want to use and change the report recipient.
972
- *
973
- * @return void
974
- * @SuppressWarnings(PHPMD.UnusedPrivateMethod)
975
- */
976
- private function report() {
977
- // Send report.
978
- wp_mail(
979
- 'hello@getjupiter.io',
980
- 'Compiler error',
981
- 'Compiler error reported by ' . home_url(),
982
- array(
983
- 'MIME-Version: 1.0' . "\r\n",
984
- 'Content-type: text/html; charset=utf-8' . "\r\n",
985
- "X-Mailer: PHP \r\n",
986
- 'From: ' . wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES ) . ' < ' . get_option( 'admin_email' ) . '>' . "\r\n",
987
- 'Reply-To: ' . get_option( 'admin_email' ) . "\r\n",
988
- )
989
- );
990
-
991
- // Die and display message.
992
- $message = jupiterx_output(
993
- 'jupiterx_compiler_report_error_text',
994
- sprintf(
995
- '<p>%s<p>',
996
- __( 'Thanks for your contribution by reporting this issue. We hope to hear from you again.', 'jupiterx-core' )
997
- )
998
- );
999
-
1000
- wp_die( wp_kses_post( $message ) );
1001
- }
1002
-
1003
- /**
1004
- * Get the property's value.
1005
- *
1006
- * @since 1.0.0
1007
- *
1008
- * @param string $property Name of the property to get.
1009
- *
1010
- * @return mixed
1011
- */
1012
- public function __get( $property ) {
1013
-
1014
- if ( property_exists( $this, $property ) ) {
1015
- return $this->{$property};
1016
- }
1017
- }
1018
- }
1
+ <?php
2
+ /**
3
+ * This class compiles and minifies CSS, LESS and JS.
4
+ *
5
+ * @package JupiterX\Framework\API\Compiler
6
+ *
7
+ * @since 1.0.0
8
+ */
9
+
10
+ /**
11
+ * Compiles and minifies CSS, LESS and JS.
12
+ *
13
+ * @since 1.0.0
14
+ * @ignore
15
+ * @access private
16
+ *
17
+ * @package JupiterX\Framework\API\Compiler
18
+ * @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
19
+ * @SuppressWarnings(PHPMD.ExcessiveClassLength)
20
+ */
21
+ final class _JupiterX_Compiler {
22
+
23
+ /**
24
+ * Compiler's runtime configuration parameters.
25
+ *
26
+ * @var array
27
+ */
28
+ private $config;
29
+
30
+ /**
31
+ * Cache dir.
32
+ *
33
+ * @var string
34
+ */
35
+ private $dir;
36
+
37
+ /**
38
+ * Cache url.
39
+ *
40
+ * @var string
41
+ */
42
+ private $url;
43
+
44
+ /**
45
+ * The fragment currently being processed.
46
+ *
47
+ * @var string
48
+ */
49
+ private $current_fragment;
50
+
51
+ /**
52
+ * The compiled content.
53
+ *
54
+ * @var string
55
+ */
56
+ private $compiled_content;
57
+
58
+ /**
59
+ * Compiled content's filename.
60
+ *
61
+ * @var string
62
+ */
63
+ private $filename;
64
+
65
+ /**
66
+ * Create a new Compiler.
67
+ *
68
+ * @since 1.0.0
69
+ *
70
+ * @param array $config Runtime configuration parameters for the Compiler.
71
+ */
72
+ public function __construct( array $config ) {
73
+ $this->config = $this->init_config( $config );
74
+ $this->dir = jupiterx_get_compiler_dir( is_admin() ) . $this->config['id'];
75
+ $this->url = jupiterx_get_compiler_url( is_admin() ) . $this->config['id'];
76
+ }
77
+
78
+ /**
79
+ * Run the compiler.
80
+ *
81
+ * @since 1.0.0
82
+ *
83
+ * @return void
84
+ */
85
+ public function run_compiler() {
86
+ // Modify the WP Filesystem method.
87
+ add_filter( 'filesystem_method', array( $this, 'modify_filesystem_method' ) );
88
+
89
+ $this->set_fragments();
90
+ $this->set_filename();
91
+
92
+ if ( ! $this->cache_file_exist() ) {
93
+ $this->filesystem();
94
+ $this->maybe_make_dir();
95
+ $this->combine_fragments();
96
+ $this->cache_file();
97
+ }
98
+
99
+ if ( $this->config['enqueue'] ) {
100
+ $this->enqueue_file();
101
+ }
102
+
103
+ // Keep it safe and reset the WP Filesystem method.
104
+ remove_filter( 'filesystem_method', array( $this, 'modify_filesystem_method' ) );
105
+ }
106
+
107
+ /**
108
+ * Callback to set the WP Filesystem method.
109
+ *
110
+ * @since 1.0.0
111
+ *
112
+ * @return string
113
+ */
114
+ public function modify_filesystem_method() {
115
+ return 'direct';
116
+ }
117
+
118
+ /**
119
+ * Initialise the WP Filesystem.
120
+ *
121
+ * @since 1.0.0
122
+ *
123
+ * @return bool|void
124
+ */
125
+ public function filesystem() {
126
+
127
+ // If the WP_Filesystem is not already loaded, load it.
128
+ if ( ! function_exists( 'WP_Filesystem' ) ) {
129
+ require_once ABSPATH . '/wp-admin/includes/file.php';
130
+ }
131
+
132
+ // If the WP_Filesystem is not initialized or is not set to WP_Filesystem_Direct, then initialize it.
133
+ if ( $this->is_wp_filesystem_direct() ) {
134
+ return true;
135
+ }
136
+
137
+ // Initialize the filesystem.
138
+ $response = WP_Filesystem();
139
+
140
+ // If the filesystem did not initialize, then generate a report and exit.
141
+ if ( true !== $response || ! $this->is_wp_filesystem_direct() ) {
142
+ return $this->kill();
143
+ }
144
+
145
+ return true;
146
+ }
147
+
148
+ /**
149
+ * Check if the filesystem is set to "direct".
150
+ *
151
+ * @since 1.0.0
152
+ *
153
+ * @return bool
154
+ */
155
+ private function is_wp_filesystem_direct() {
156
+ return isset( $GLOBALS['wp_filesystem'] ) && is_a( $GLOBALS['wp_filesystem'], 'WP_Filesystem_Direct' );
157
+ }
158
+
159
+ /**
160
+ * Make directory.
161
+ *
162
+ * @since 1.0.0
163
+ *
164
+ * @return bool
165
+ */
166
+ private function maybe_make_dir() {
167
+
168
+ if ( ! @is_dir( $this->dir ) ) { // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- This is a valid use case.
169
+ wp_mkdir_p( $this->dir );
170
+ }
171
+
172
+ return is_writable( $this->dir );
173
+ }
174
+
175
+ /**
176
+ * Set class fragments.
177
+ *
178
+ * @since 1.0.0
179
+ *
180
+ * @return void
181
+ */
182
+ public function set_fragments() {
183
+ global $_jupiterx_compiler_added_fragments;
184
+
185
+ $added_fragments = jupiterx_get( $this->config['id'], $_jupiterx_compiler_added_fragments[ $this->config['format'] ] );
186
+
187
+ if ( $added_fragments ) {
188
+ $this->config['fragments'] = array_merge( $this->config['fragments'], $added_fragments );
189
+ }
190
+
191
+ /**
192
+ * Filter the compiler fragment files.
193
+ *
194
+ * The dynamic portion of the hook name, $this->config['id'], refers to the compiler id used as a reference.
195
+ *
196
+ * @since 1.0.0
197
+ *
198
+ * @param array $fragments An array of fragment files.
199
+ */
200
+ $this->config['fragments'] = apply_filters( 'jupiterx_compiler_fragments_' . $this->config['id'], $this->config['fragments'] );
201
+ }
202
+
203
+ /**
204
+ * Set the filename for the compiled asset.
205
+ *
206
+ * @since 1.0.0
207
+ *
208
+ * @return void
209
+ */
210
+ public function set_filename() {
211
+ $hash = $this->hash( $this->config );
212
+
213
+ if ( empty( _jupiterx_get_cache_busting() ) ) {
214
+ $this->config['version'] = $hash;
215
+
216
+ $hash = 'style';
217
+
218
+ if ( 'script' === $this->config['type'] ) {
219
+ $hash = 'script';
220
+ }
221
+ }
222
+
223
+ $this->filename = $hash . '.' . $this->get_extension();
224
+ }
225
+
226
+ /**
227
+ * Hash the given array.
228
+ *
229
+ * @since 1.0.0
230
+ *
231
+ * @param array $given_array Given array to be hashed.
232
+ *
233
+ * @return string
234
+ */
235
+ public function hash( array $given_array ) {
236
+ return substr( md5( @serialize( $given_array ) ), 0, 7 ); // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged, WordPress.PHP.DiscouragedPHPFunctions.serialize_serialize -- Valid use case.
237
+ }
238
+
239
+ /**
240
+ * Checks if the file exists on the filesystem, meaning it's been cached.
241
+ *
242
+ * @since 1.0.0
243
+ *
244
+ * @return bool
245
+ */
246
+ public function cache_file_exist() {
247
+ $filename = $this->get_filename();
248
+
249
+ if ( ( _jupiterx_is_compiler_dev_mode() ) || is_preview() ) {
250
+ return false;
251
+ }
252
+
253
+ if ( empty( $filename ) ) {
254
+ return false;
255
+ }
256
+
257
+ return file_exists( $filename );
258
+ }
259
+
260
+ /**
261
+ * Get the absolute path of the cached and compiled file.
262
+ *
263
+ * @since 1.0.0
264
+ *
265
+ * @return string
266
+ */
267
+ public function get_filename() {
268
+ if ( isset( $this->filename ) ) {
269
+ return $this->dir . '/' . $this->filename;
270
+ }
271
+
272
+ return '';
273
+ }
274
+
275
+ /**
276
+ * Create cached file.
277
+ *
278
+ * @since 1.0.0
279
+ *
280
+ * @return bool
281
+ */
282
+ public function cache_file() {
283
+ $filename = $this->get_filename();
284
+
285
+ if ( empty( $filename ) ) {
286
+ return false;
287
+ }
288
+
289
+ // It is safe to access the filesystem because we made sure it was set.
290
+ return $GLOBALS['wp_filesystem']->put_contents( $filename, $this->compiled_content, FS_CHMOD_FILE );
291
+ }
292
+
293
+ /**
294
+ * Enqueue cached file.
295
+ *
296
+ * @since 1.0.0
297
+ *
298
+ * @return void|bool
299
+ */
300
+ private function enqueue_file() {
301
+
302
+ // Enqueue CSS file.
303
+ if ( 'style' === $this->config['type'] ) {
304
+ return wp_enqueue_style(
305
+ $this->config['id'],
306
+ $this->get_url(),
307
+ $this->config['dependencies'],
308
+ $this->config['version']
309
+ );
310
+ }
311
+
312
+ // Enqueue JS file.
313
+ if ( 'script' === $this->config['type'] ) {
314
+ return wp_enqueue_script(
315
+ $this->config['id'],
316
+ $this->get_url(),
317
+ $this->config['dependencies'],
318
+ $this->config['version'],
319
+ $this->config['in_footer']
320
+ );
321
+ }
322
+
323
+ return false;
324
+ }
325
+
326
+ /**
327
+ * Get cached file url.
328
+ *
329
+ * @since 1.0.0
330
+ *
331
+ * @return string
332
+ */
333
+ public function get_url() {
334
+ $url = trailingslashit( $this->url ) . $this->filename;
335
+
336
+ if ( is_ssl() ) {
337
+ $url = str_replace( 'http://', 'https://', $url );
338
+ }
339
+
340
+ return $url;
341
+ }
342
+
343
+ /**
344
+ * Get the file extension from the configured "type".
345
+ *
346
+ * @since 1.0.0
347
+ *
348
+ * @return string|null
349
+ */
350
+ public function get_extension() {
351
+
352
+ if ( 'style' === $this->config['type'] ) {
353
+ return 'css';
354
+ }
355
+
356
+ if ( 'script' === $this->config['type'] ) {
357
+ return 'js';
358
+ }
359
+ }
360
+
361
+ /**
362
+ * Combine content of the fragments.
363
+ *
364
+ * @since 1.0.0
365
+ *
366
+ * @return void
367
+ */
368
+ public function combine_fragments() {
369
+ $content = '';
370
+
371
+ // Loop through fragments.
372
+ foreach ( $this->config['fragments'] as $fragment ) {
373
+
374
+ // Stop here if the fragment is empty.
375
+ if ( empty( $fragment ) ) {
376
+ continue;
377
+ }
378
+
379
+ $fragment_content = $this->get_content( $fragment );
380
+
381
+ // Stop here if no content or content is an html page.
382
+ if ( ! $fragment_content || preg_match( '#^\s*\<#', $fragment_content ) ) {
383
+ continue;
384
+ }
385
+
386
+ // Continue processing style.
387
+ if ( 'style' === $this->config['type'] ) {
388
+ $fragment_content = $this->replace_css_url( $fragment_content );
389
+ $fragment_content = $this->add_content_media_query( $fragment_content );
390
+ }
391
+
392
+ // If there's content, start a new line.
393
+ if ( $content ) {
394
+ $content .= "\n\n";
395
+ }
396
+
397
+ $content .= $fragment_content;
398
+ }
399
+
400
+ $this->compiled_content = ! empty( $content ) ? $this->format_content( $content ) : '';
401
+ }
402
+
403
+ /**
404
+ * Get the fragment's content.
405
+ *
406
+ * @since 1.0.0
407
+ *
408
+ * @param string|callable $fragment The given fragment from which to get the content.
409
+ *
410
+ * @return bool|string
411
+ */
412
+ private function get_content( $fragment ) {
413
+ // Set the current fragment used by other functions.
414
+ $this->current_fragment = $fragment;
415
+
416
+ // If the fragment is callable, call it to get the content.
417
+ if ( $this->is_function( $fragment ) ) {
418
+ return $this->get_function_content();
419
+ }
420
+
421
+ $content = $this->get_internal_content();
422
+
423
+ // Try remote content if the internal content returned false.
424
+ if ( empty( $content ) ) {
425
+ $content = $this->get_remote_content();
426
+ }
427
+
428
+ return $content;
429
+ }
430
+
431
+ /**
432
+ * Get internal file content.
433
+ *
434
+ * @since 1.0.0
435
+ *
436
+ * @return string|bool
437
+ */
438
+ public function get_internal_content() {
439
+ $fragment = $this->current_fragment;
440
+
441
+ if ( ! file_exists( $fragment ) ) {
442
+
443
+ // Replace URL with path.
444
+ $fragment = jupiterx_url_to_path( $fragment );
445
+
446
+ // Stop here if it isn't a valid file.
447
+ if ( ! file_exists( $fragment ) || 0 === @filesize( $fragment ) ) { // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Valid use case.
448
+ return false;
449
+ }
450
+ }
451
+
452
+ // It is safe to access the filesystem because we made sure it was set.
453
+ return $GLOBALS['wp_filesystem']->get_contents( $fragment );
454
+ }
455
+
456
+ /**
457
+ * Get external file content.
458
+ *
459
+ * @since 1.0.0
460
+ *
461
+ * @return string|bool
462
+ */
463
+ public function get_remote_content() {
464
+ $fragment = $this->current_fragment;
465
+
466
+ if ( empty( $fragment ) ) {
467
+ return false;
468
+ }
469
+
470
+ // For a relative URL, add http: to it.
471
+ if ( substr( $fragment, 0, 2 ) === '//' ) {
472
+ $fragment = 'http:' . $fragment;
473
+ } elseif ( substr( $fragment, 0, 1 ) === '/' ) { // Add domain if it is local but could not be fetched as a file.
474
+ $fragment = site_url( $fragment );
475
+ }
476
+
477
+ $request = wp_remote_get( $fragment );
478
+
479
+ if ( is_wp_error( $request ) ) {
480
+ return '';
481
+ }
482
+
483
+ // If no content was received and the URL is not https, then convert the URL to SSL and retry.
484
+ if (
485
+ ( ! isset( $request['body'] ) || 200 !== $request['response']['code'] ) &&
486
+ ( substr( $fragment, 0, 8 ) !== 'https://' )
487
+ ) {
488
+ $fragment = str_replace( 'http://', 'https://', $fragment );
489
+ $request = wp_remote_get( $fragment );
490
+
491
+ if ( is_wp_error( $request ) ) {
492
+ return '';
493
+ }
494
+ }
495
+
496
+ if ( ( ! isset( $request['body'] ) || 200 !== $request['response']['code'] ) ) {
497
+ return false;
498
+ }
499
+
500
+ return wp_remote_retrieve_body( $request );
501
+ }
502
+
503
+ /**
504
+ * Get function content.
505
+ *
506
+ * @since 1.0.0
507
+ *
508
+ * @return string|bool
509
+ */
510
+ public function get_function_content() {
511
+
512
+ if ( ! is_callable( $this->current_fragment ) ) {
513
+ return false;
514
+ }
515
+
516
+ return call_user_func( $this->current_fragment );
517
+ }
518
+
519
+ /**
520
+ * Wrap content in query.
521
+ *
522
+ * @since 1.0.0
523
+ *
524
+ * @param string $content Given content to process.
525
+ *
526
+ * @return string
527
+ */
528
+ public function add_content_media_query( $content ) {
529
+
530
+ // Ignore if the fragment is a function.
531
+ if ( $this->is_function( $this->current_fragment ) ) {
532
+ return $content;
533
+ }
534
+
535
+ $query = wp_parse_url( $this->current_fragment, PHP_URL_QUERY );
536
+
537
+ // Bail out if there are no query args or no media query.
538
+ if ( empty( $query ) || false === stripos( $query, 'jupiterx_compiler_media_query' ) ) {
539
+ return $content;
540
+ }
541
+
542
+ // Wrap the content in the query.
543
+ return sprintf(
544
+ "@media %s {\n%s\n}\n",
545
+ jupiterx_get( 'jupiterx_compiler_media_query', wp_parse_args( $query ) ),
546
+ $content
547
+ );
548
+ }
549
+
550
+ /**
551
+ * Format CSS, LESS and JS content.
552
+ *
553
+ * @since 1.0.0
554
+ *
555
+ * @param string $content Given content to process.
556
+ *
557
+ * @return string
558
+ */
559
+ public function format_content( $content ) {
560
+
561
+ if ( 'style' === $this->config['type'] ) {
562
+
563
+ if ( 'less' === $this->config['format'] ) {
564
+
565
+ if ( ! class_exists( 'JupiterX_Lessc' ) ) {
566
+ jupiterx_core()->load_files( [ 'compiler/vendors/lessc' ] );
567
+ }
568
+
569
+ $parser = new JupiterX_Lessc();
570
+
571
+ $parser = $this->register_less_functions( $parser );
572
+
573
+ $parser->setVariables( $this->config['variables'] );
574
+
575
+ $content = $parser->compile( $content );
576
+ }
577
+
578
+ if ( ! function_exists( 'jupiterx_parse_css' ) ) {
579
+ return $content;
580
+ }
581
+
582
+ $content = jupiterx_parse_css( $content );
583
+
584
+ if ( ! _jupiterx_is_compiler_dev_mode() ) {
585
+ $content = $this->minify_style( $content );
586
+ }
587
+
588
+ return $content;
589
+ }
590
+
591
+ if ( 'script' === $this->config['type'] && ! _jupiterx_is_compiler_dev_mode() && $this->config['minify_js'] ) {
592
+
593
+ if ( ! class_exists( 'JSMin' ) ) {
594
+ jupiterx_core()->load_files( [ 'compiler/vendors/js-minifier' ] );
595
+ }
596
+
597
+ $js_min = new JSMin( $content );
598
+ return $js_min->min();
599
+ }
600
+
601
+ return $content;
602
+ }
603
+
604
+ /**
605
+ * Replace CSS URL shortcuts with a valid URL.
606
+ *
607
+ * @since 1.0.0
608
+ *
609
+ * @param string $content Given content to process.
610
+ *
611
+ * @return string
612
+ */
613
+ public function replace_css_url( $content ) {
614
+ return preg_replace_callback(
615
+ '#url\s*\(\s*[\'"]*?([^\'"\)]+)[\'"]*\s*\)#i',
616
+ array( $this, 'replace_css_url_callback' ),
617
+ $content
618
+ );
619
+ }
620
+
621
+ /**
622
+ * Convert any CSS URL relative paths to absolute URLs.
623
+ *
624
+ * @since 1.0.0
625
+ *
626
+ * @param array $matches Matches to process, where 0 is the CSS' URL() and 1 is the URI.
627
+ *
628
+ * @return string
629
+ */
630
+ public function replace_css_url_callback( $matches ) {
631
+
632
+ // If the URI is absolute, bail out and return the CSS.
633
+ if ( _jupiterx_is_uri( $matches[1] ) ) {
634
+ return $matches[0];
635
+ }
636
+
637
+ $base = $this->current_fragment;
638
+
639
+ // Separate the placeholders and path.
640
+ $paths = explode( '../', $matches[1] );
641
+
642
+ /**
643
+ * Walk backwards through each of the the fragment's directories, one-by-one. The `foreach` loop
644
+ * provides us with a performant way to walk the fragment back to its base path based upon the
645
+ * number of placeholders.
646
+ */
647
+ foreach ( $paths as $path ) {
648
+ $base = dirname( $base );
649
+ }
650
+
651
+ // Make sure it is a valid base.
652
+ if ( '.' === $base ) {
653
+ $base = '';
654
+ }
655
+
656
+ // Rebuild the URL and make sure it is valid using the jupiterx_path_to_url function.
657
+ $url = jupiterx_path_to_url( trailingslashit( $base ) . ltrim( end( $paths ), '/\\' ) );
658
+
659
+ // Return the rebuilt path converted to an URL.
660
+ return 'url("' . $url . '")';
661
+ }
662
+
663
+ /**
664
+ * Register LESS_PHP functions.
665
+ *
666
+ * @since 1.0.0
667
+ *
668
+ * @param object $parser The LESS parser.
669
+ *
670
+ * @todo Refactoring is required.
671
+ *
672
+ * @return object
673
+ */
674
+ private function register_less_functions( $parser ) {
675
+ $parser->registerFunction( 'jupiterx_value', function( $arg ) {
676
+ $output = '';
677
+
678
+ if ( isset( $arg[2][1][2][0] ) ) {
679
+ $output = $arg[2][1][2][0]; // Default.
680
+ }
681
+
682
+ if ( ! empty( $arg[2][0][2][1][1] ) ) {
683
+ return $arg[2][0][2][1][1]; // E.g. ~"@{@{var}-width}".
684
+ }
685
+
686
+ if ( ! empty( $arg[2][0][1] ) ) {
687
+ $value = $arg[2][0][1]; // E.g. @text-size.
688
+ $unit = empty( $arg[2][0][2] ) ? '' : $arg[2][0][2]; // E.g. @text-size unit.
689
+
690
+ return $value . $unit;
691
+ }
692
+
693
+ return $output;
694
+ } );
695
+
696
+ $parser->registerFunction( 'jupiterx_value_pattern', function( $arg ) {
697
+ if ( 0 === strlen( $arg[2][0][1] ) ) {
698
+ return '';
699
+ }
700
+
701
+ list($type, $value, $unit) = $arg[2][0];
702
+
703
+ $format = $arg[2][1][2][0];
704
+
705
+ // When value is 0px, parser automatically remove px (but not %) from it.
706
+ if ( 0 == $arg[2][0][1] ) { // @codingStandardsIgnoreLine
707
+ $unit = '%';
708
+ }
709
+
710
+ return sprintf( $format, $value . $unit );
711
+ } );
712
+
713
+ $parser->registerFunction( 'jupiterx_replace', function( $args ) {
714
+ list( $string, $search, $replace ) = $args[2];
715
+
716
+ // Arrange if string is from a variable use the true condition. e.g. @{var-name}.
717
+ $string = isset( $string[2][1][1] ) ? $string[2][1][1] : $string[2][0];
718
+ $search = $search[2][0];
719
+ $replace = $replace[2][0];
720
+
721
+ return str_replace( $search, $replace, $string );
722
+ } );
723
+
724
+ return $parser;
725
+ }
726
+
727
+ /**
728
+ * Initialize the configuration.
729
+ *
730
+ * @since 1.0.0
731
+ *
732
+ * @param array $config Runtime configuration parameters for the Compiler.
733
+ *
734
+ * @return array
735
+ */
736
+ private function init_config( array $config ) {
737
+ // Fix dependencies, if "depedencies" is specified.
738
+ if ( isset( $config['depedencies'] ) ) {
739
+ $config['dependencies'] = $config['depedencies'];
740
+ unset( $config['depedencies'] );
741
+ }
742
+
743
+ $defaults = [
744
+ 'id' => false,
745
+ 'type' => false,
746
+ 'format' => false,
747
+ 'fragments' => [],
748
+ 'variables' => apply_filters( 'jupiterx_compiler_less_variables', [] ),
749
+ 'dependencies' => false,
750
+ 'in_footer' => false,
751
+ 'minify_js' => true,
752
+ 'version' => JUPITERX_VERSION,
753
+ 'enqueue' => true,
754
+ ];
755
+
756
+ return array_merge( $defaults, $config );
757
+ }
758
+
759
+ /**
760
+ * Get the fragments' modification times.
761
+ *
762
+ * @since 1.0.0
763
+ *
764
+ * @return array
765
+ * @SuppressWarnings(PHPMD.UnusedPrivateMethod)
766
+ */
767
+ private function get_fragments_filemtime() {
768
+ $fragments_filemtime = array();
769
+
770
+ foreach ( $this->config['fragments'] as $index => $fragment ) {
771
+
772
+ // Skip this one if the fragment is a function.
773
+ if ( $this->is_function( $fragment ) ) {
774
+ if ( ! is_callable( $fragment ) ) {
775
+ continue;
776
+ }
777
+
778
+ $fragments_filemtime[ $index ] = $this->hash( [ call_user_func( $fragment ) ] );
779
+
780
+ }
781
+
782
+ if ( file_exists( $fragment ) ) {
783
+ $fragments_filemtime[ $index ] = @filemtime( $fragment ); // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Valid use case.
784
+ }
785
+ }
786
+
787
+ return $fragments_filemtime;
788
+ }
789
+
790
+ /**
791
+ * Get the new hash for the given fragments' modification times.
792
+ *
793
+ * @since 1.0.0
794
+ *
795
+ * @param string $hash The original hash to modify.
796
+ * @param array $fragments_filemtime Array of fragments' modification times.
797
+ *
798
+ * @return string
799
+ * @SuppressWarnings(PHPMD.UnusedPrivateMethod)
800
+ */
801
+ private function get_new_hash( $hash, array $fragments_filemtime ) {
802
+
803
+ if ( empty( $fragments_filemtime ) ) {
804
+ return $hash;
805
+ }
806
+
807
+ // Set filemtime hash.
808
+ $_hash = $this->hash( $fragments_filemtime );
809
+
810
+ $this->remove_modified_files( $hash, $_hash );
811
+
812
+ // Set the new hash which will trigger a new compiling.
813
+ return $hash . '-' . $_hash;
814
+ }
815
+
816
+ /**
817
+ * Remove any modified files. A file is considered modified when:
818
+ *
819
+ * 1. It has both a base hash and filemtime hash, separated by '-'.
820
+ * 2. Its base hash matches the given hash.
821
+ * 3. Its filemtime hash does not match the given filemtime hash.
822
+ *
823
+ * @since 1.0.0
824
+ *
825
+ * @param string $hash Base hash.
826
+ * @param string $filemtime_hash The filemtime hash (from hashing the fragments).
827
+ *
828
+ * @return void
829
+ */
830
+ private function remove_modified_files( $hash, $filemtime_hash ) {
831
+
832
+ if ( ! is_dir( $this->dir ) ) {
833
+ return;
834
+ }
835
+
836
+ $items = @scandir( $this->dir ); // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Valid use case.
837
+ unset( $items[0], $items[1] );
838
+
839
+ if ( empty( $items ) ) {
840
+ return;
841
+ }
842
+
843
+ foreach ( $items as $item ) {
844
+
845
+ // Skip this one if it's a directory.
846
+ if ( @is_dir( $item ) ) { // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Valid use case.
847
+ continue;
848
+ }
849
+
850
+ // Skip this one if it's not the same type.
851
+ if ( pathinfo( $item, PATHINFO_EXTENSION ) !== $this->get_extension() ) {
852
+ continue;
853
+ }
854
+
855
+ // Skip this one if it does not have a '-' in the filename.
856
+ if ( strpos( $item, '-' ) === false ) {
857
+ continue;
858
+ }
859
+
860
+ $hash_parts = explode( '-', pathinfo( $item, PATHINFO_FILENAME ) );
861
+
862
+ // Skip this one if it does not match the given base hash.
863
+ if ( $hash_parts[0] !== $hash ) {
864
+ continue;
865
+ }
866
+
867
+ // Skip this one if it does match the given filemtime's hash.
868
+ if ( $hash_parts[1] === $filemtime_hash ) {
869
+ continue;
870
+ }
871
+
872
+ // Clean up other modified files.
873
+ @unlink( $this->dir . '/' . $item ); // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Valid use case.
874
+ }
875
+ }
876
+
877
+ /**
878
+ * Minify the CSS.
879
+ *
880
+ * @since 1.0.0
881
+ *
882
+ * @param string $content Given content to process.
883
+ *
884
+ * @return string
885
+ */
886
+ private function minify_style( $content ) {
887
+ $replace = array(
888
+ '/([^\r\n{}]+)(,(?=[^}]*{)|\s*{)}/' => '', // Strip empty selectors.
889
+ '/@media\s\(.*\).*{}/' => '', // Strip empty @media.
890
+ '#/\*.*?\*/#s' => '', // Strip comments.
891
+ '#\s\s+#' => ' ', // Strip excess whitespace.
892
+ );
893
+
894
+ $search = array_keys( $replace );
895
+ $content = preg_replace( $search, $replace, $content );
896
+
897
+ $replace = array(
898
+ ': ' => ':',
899
+ '; ' => ';',
900
+ ' {' => '{',
901
+ ' }' => '}',
902
+ ', ' => ',',
903
+ '{ ' => '{',
904
+ ',\n' => ',', // Don't wrap multiple selectors.
905
+ '\n}' => '}', // Don't wrap closing braces.
906
+ '} ' => "}\n", // Put each rule on it's own line.
907
+ '\n' => '', // Remove all line breaks.
908
+ );
909
+
910
+ $search = array_keys( $replace );
911
+
912
+ return trim( str_replace( $search, $replace, $content ) );
913
+ }
914
+
915
+ /**
916
+ * Check if the given fragment is a callable.
917
+ *
918
+ * @since 1.0.0
919
+ *
920
+ * @param mixed $fragment Given fragment to check.
921
+ *
922
+ * @return bool
923
+ */
924
+ private function is_function( $fragment ) {
925
+ return ( is_array( $fragment ) || is_callable( $fragment ) );
926
+ }
927
+
928
+ /**
929
+ * Kill it :(
930
+ *
931
+ * @since 1.0.0
932
+ *
933
+ * @return void
934
+ */
935
+ private function kill() {
936
+
937
+ // Send report if set.
938
+ if ( jupiterx_get( 'jupiterx_send_compiler_report' ) ) { // @codingStandardsIgnoreLine
939
+ // $this->report(); // @codingStandardsIgnoreLine
940
+ }
941
+
942
+ $html = jupiterx_output( 'jupiterx_compiler_error_title_text', sprintf(
943
+ '<h2>%s</h2>',
944
+ __( 'Not cool, Jupiter cannot work its magic :(', 'jupiterx-core' )
945
+ ) );
946
+
947
+ $html .= jupiterx_output( 'jupiterx_compiler_error_message_text', sprintf(
948
+ '<p>%s</p>',
949
+ __( 'Your current install or file permission prevents Jupiter from working its magic. Please get in touch with Jupiter support. We will gladly get you started within 24 - 48 hours (working days).', 'jupiterx-core' )
950
+ ) );
951
+
952
+ $html .= jupiterx_output( 'jupiterx_compiler_error_contact_text', sprintf(
953
+ '<a class="button" href="https://themes.artbees.net/support/" target="_blank">%s</a>',
954
+ __( 'Contact Jupiter Support', 'jupiterx-core' )
955
+ ) );
956
+
957
+ $html .= jupiterx_output( 'jupiterx_compiler_error_report_text', sprintf(
958
+ '<p style="margin-top: 12px; font-size: 12px;"><a href="' . add_query_arg( 'jupiterx_send_compiler_report', true ) . '">%1$s</a>. %2$s</p>',
959
+ __( 'Send us an automatic report', 'jupiterx-core' ),
960
+ __( 'We respect your time and understand you might not be able to contact us.', 'jupiterx-core' )
961
+ ) );
962
+
963
+ wp_die( wp_kses_post( $html ) );
964
+ }
965
+
966
+ /**
967
+ * Send report.
968
+ *
969
+ * @since 1.0.0
970
+ *
971
+ * @todo Decide if we want to use and change the report recipient.
972
+ *
973
+ * @return void
974
+ * @SuppressWarnings(PHPMD.UnusedPrivateMethod)
975
+ */
976
+ private function report() {
977
+ // Send report.
978
+ wp_mail(
979
+ 'hello@getjupiter.io',
980
+ 'Compiler error',
981
+ 'Compiler error reported by ' . home_url(),
982
+ array(
983
+ 'MIME-Version: 1.0' . "\r\n",
984
+ 'Content-type: text/html; charset=utf-8' . "\r\n",
985
+ "X-Mailer: PHP \r\n",
986
+ 'From: ' . wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES ) . ' < ' . get_option( 'admin_email' ) . '>' . "\r\n",
987
+ 'Reply-To: ' . get_option( 'admin_email' ) . "\r\n",
988
+ )
989
+ );
990
+
991
+ // Die and display message.
992
+ $message = jupiterx_output(
993
+ 'jupiterx_compiler_report_error_text',
994
+ sprintf(
995
+ '<p>%s<p>',
996
+ __( 'Thanks for your contribution by reporting this issue. We hope to hear from you again.', 'jupiterx-core' )
997
+ )
998
+ );
999
+
1000
+ wp_die( wp_kses_post( $message ) );
1001
+ }
1002
+
1003
+ /**
1004
+ * Get the property's value.
1005
+ *
1006
+ * @since 1.0.0
1007
+ *
1008
+ * @param string $property Name of the property to get.
1009
+ *
1010
+ * @return mixed
1011
+ */
1012
+ public function __get( $property ) {
1013
+
1014
+ if ( property_exists( $this, $property ) ) {
1015
+ return $this->{$property};
1016
+ }
1017
+ }
1018
+ }
includes/compiler/functions.php CHANGED
@@ -1,411 +1,411 @@
1
- <?php
2
- /**
3
- * Compile and cache CSS, LESS and JS files.
4
- *
5
- * The Jupiter Compiler compiles multiple internal or external CSS, LESS and JS files on a
6
- * per page basis. LESS content will automatically be converted to CSS.
7
- *
8
- * Internal file changes are automatically detected if development mode is enabled.
9
- * Third party enqueued styles and scripts can be compiled and cached into a single file.
10
- *
11
- * @package API\Compiler
12
- */
13
-
14
- /**
15
- * Compile CSS fragments and enqueue compiled file.
16
- *
17
- * This function should be used in a similar fashion to
18
- * {@link http://codex.wordpress.org/Function_Reference/wp_enqueue_script wp_enqueue_script()}.
19
- *
20
- * Fragments can be added to the compiler using {@see jupiterx_compiler_add_fragment()}.
21
- *
22
- * @since 1.0.0
23
- *
24
- * @param string $id A unique string used as a reference. Similar to the WordPress scripts
25
- * $handle argument.
26
- * @param string|array $fragments File(s) absolute path. Internal or external file(s) url accepted but may increase
27
- * compiling time.
28
- * @param array $args {
29
- * Optional. Array of arguments used by the compiler.
30
- *
31
- * @type array $depedencies An array of registered handles this script depends on. Default false.
32
- * }
33
- *
34
- * @return object Compiler object.
35
- */
36
- function jupiterx_compile_css_fragments( $id, $fragments, $args = array() ) {
37
-
38
- if ( empty( $fragments ) ) {
39
- return false;
40
- }
41
-
42
- $params = array(
43
- 'id' => $id,
44
- 'type' => 'style',
45
- 'format' => 'css',
46
- 'fragments' => (array) $fragments,
47
- );
48
-
49
- $compiler = new _JupiterX_Compiler( array_merge( $params, $args ) );
50
- $compiler->run_compiler();
51
-
52
- return $compiler;
53
- }
54
-
55
- /**
56
- * Compile LESS fragments, convert to CSS and enqueue compiled file.
57
- *
58
- * This function should be used in a similar fashion to
59
- * {@link http://codex.wordpress.org/Function_Reference/wp_enqueue_script wp_enqueue_script()}.
60
- *
61
- * Fragments can be added to the compiler using {@see jupiterx_compiler_add_fragment()}.
62
- *
63
- * @since 1.0.0
64
- *
65
- * @param string $id The compiler ID. Similar to the WordPress scripts $handle argument.
66
- * @param string|array $fragments File(s) absolute path. Internal or external file(s) url accepted but may increase
67
- * compiling time.
68
- * @param array $args {
69
- * Optional. Array of arguments used by the compiler.
70
- *
71
- * @type array $depedencies An array of registered handles this script depends on. Default false.
72
- * }
73
- *
74
- * @return object Compiler object.
75
- */
76
- function jupiterx_compile_less_fragments( $id, $fragments, $args = array() ) {
77
-
78
- if ( empty( $fragments ) ) {
79
- return false;
80
- }
81
-
82
- $params = array(
83
- 'id' => $id,
84
- 'type' => 'style',
85
- 'format' => 'less',
86
- 'fragments' => (array) $fragments,
87
- );
88
-
89
- $compiler = new _JupiterX_Compiler( array_merge( $params, $args ) );
90
- $compiler->run_compiler();
91
-
92
- return $compiler;
93
- }
94
-
95
- /**
96
- * Compile LESS fragments, convert to CSS and enqueue compiled file.
97
- *
98
- * This function should be used in a similar fashion to
99
- * {@link http://codex.wordpress.org/Function_Reference/wp_enqueue_script wp_enqueue_script()}.
100
- *
101
- * Fragments can be added to the compiler using {@see jupiterx_compiler_add_fragment()}.
102
- *
103
- * @since 1.0.0
104
- *
105
- * @param string $id The compiler ID. Similar to the WordPress scripts $handle argument.
106
- * @param string|array $fragments File(s) absolute path. Internal or external file(s) url accepted but may increase
107
- * compiling time.
108
- * @param array $args {
109
- * Optional. Array of arguments used by the compiler.
110
- *
111
- * @type array $depedencies An array of registered handles this script depends on. Default false.
112
- * }
113
- *
114
- * @return object Compiler object.
115
- */
116
- function jupiterx_compile_scss_fragments( $id, $fragments, $args = array() ) {
117
-
118
- if ( empty( $fragments ) ) {
119
- return false;
120
- }
121
-
122
- $params = array(
123
- 'id' => $id,
124
- 'type' => 'style',
125
- 'format' => 'scss',
126
- 'fragments' => (array) $fragments,
127
- );
128
-
129
- $compiler = new _JupiterX_Compiler( array_merge( $params, $args ) );
130
- $compiler->run_compiler();
131
-
132
- return $compiler;
133
- }
134
-
135
- /**
136
- * Compile JS fragments and enqueue compiled file.
137
- *
138
- * This function should be used in a similar fashion to
139
- * {@link http://codex.wordpress.org/Function_Reference/wp_enqueue_script wp_enqueue_script()}.
140
- *
141
- * Fragments can be added to the compiler using {@see jupiterx_compiler_add_fragment()}.
142
- *
143
- * @since 1.0.0
144
- *
145
- * @param string $id The compiler ID. Similar to the WordPress scripts $handle argument.
146
- * @param string|array $fragments File(s) absolute path. Internal or external file(s) URL accepted but may increase
147
- * compiling time.
148
- * @param array $args {
149
- * Optional. Array of arguments used by the compiler.
150
- *
151
- * @type array $depedencies An array of registered handles this script depends on. Default false.
152
- * @type bool $in_footer Whether to enqueue the script before </head> or before </body>. Default false.
153
- * @type bool $minify_js Whether the JavaScript should be minified or not. Be aware that minifying
154
- * the JavaScript can considerably slow down the process of compiling files.
155
- * Default false.
156
- * }
157
- *
158
- * @return object Compiler object.
159
- */
160
- function jupiterx_compile_js_fragments( $id, $fragments, $args = array() ) {
161
-
162
- if ( empty( $fragments ) ) {
163
- return false;
164
- }
165
-
166
- $params = array(
167
- 'id' => $id,
168
- 'type' => 'script',
169
- 'format' => 'js',
170
- 'fragments' => (array) $fragments,
171
- );
172
-
173
- $compiler = new _JupiterX_Compiler( array_merge( $params, $args ) );
174
- $compiler->run_compiler();
175
-
176
- return $compiler;
177
- }
178
-
179
- /**
180
- * Add CSS, LESS or JS fragments to a compiler.
181
- *
182
- * This function should be used in a similar fashion to
183
- * {@link http://codex.wordpress.org/Function_Reference/wp_enqueue_script wp_enqueue_script()}.
184
- *
185
- * @since 1.0.0
186
- *
187
- * @param string $id The compiler ID. Similar to the WordPress scripts $handle argument.
188
- * @param string|array $fragments File(s) absolute path. Internal or external file(s) url accepted but may increase
189
- * compiling time.
190
- * @param string $format Compiler format the fragments should be added to. Accepts 'css',
191
- * 'less' or 'js'.
192
- *
193
- * @return void|bool
194
- * @SuppressWarnings(PHPMD.ElseExpression)
195
- */
196
- function jupiterx_compiler_add_fragment( $id, $fragments, $format ) {
197
-
198
- if ( empty( $fragments ) ) {
199
- return false;
200
- }
201
-
202
- global $_jupiterx_compiler_added_fragments;
203
-
204
- foreach ( (array) $fragments as $key => $fragment ) {
205
-
206
- // Stop here if the format isn't valid.
207
- if ( ! isset( $_jupiterx_compiler_added_fragments[ $format ] ) ) {
208
- continue;
209
- }
210
-
211
- // Register a new compiler ID if it doesn't exist and add fragment.
212
- if ( ! isset( $_jupiterx_compiler_added_fragments[ $format ][ $id ] ) ) {
213
- $_jupiterx_compiler_added_fragments[ $format ][ $id ] = array( $fragment );
214
- } else { // Add fragment to existing compiler.
215
- $_jupiterx_compiler_added_fragments[ $format ][ $id ][] = $fragment;
216
- }
217
- }
218
- }
219
-
220
- /**
221
- * Flush cached compiler files.
222
- *
223
- * Each compiler has its own folder which contains the cached CSS and JS files. The file format
224
- * of the cached file can be specified if needed.
225
- *
226
- * @since 1.0.0
227
- *
228
- * @param string $id The compiler ID. Similar to the WordPress scripts $handle argument.
229
- * @param string|bool $file_format Optional. Define which file format(s) should be removed. Both CSS and JS
230
- * files will be removed if set to false. Accepts 'false', 'css' or 'js'.
231
- * @param bool $admin Optional. Whether it is an admin compiler or not.
232
- *
233
- * @return void|bool
234
- */
235
- function jupiterx_flush_compiler( $id, $file_format = false, $admin = false ) {
236
- static $jupiterx_flushed = false;
237
-
238
- $cache_dir = jupiterx_get_compiler_dir( $admin );
239
-
240
- // Always flush Jupiter' global cache.
241
- if ( ! $jupiterx_flushed ) {
242
- $jupiterx_flushed = true;
243
-
244
- jupiterx_flush_compiler( 'jupiterx', $file_format, $admin );
245
- }
246
-
247
- $dir = trailingslashit( $cache_dir ) . $id;
248
-
249
- // Stop here if directory doesn't exist.
250
- if ( ! is_dir( $dir ) ) {
251
- return;
252
- }
253
-
254
- // Remove only the specified file format.
255
- if ( $file_format ) {
256
- $items = scandir( $dir );
257
- unset( $items[0], $items[1] );
258
-
259
- foreach ( $items as $item ) {
260
- if ( false !== stripos( $item, '.' . $file_format ) ) {
261
- @unlink( trailingslashit( $dir ) . $item ); // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Valid use case.
262
- }
263
- }
264
-
265
- return;
266
- }
267
-
268
- // Remove all file formats.
269
- jupiterx_remove_dir( $dir );
270
-
271
- jupiterx_flush_cache_plugins();
272
- }
273
-
274
- /**
275
- * Flush admin cached compiler files.
276
- *
277
- * This function is a shortcut of {@see jupiterx_flush_compiler()}.
278
- *
279
- * @since 1.0.0
280
- *
281
- * @param string $id The compiler ID. Similar to the WordPress scripts $handle argument.
282
- * @param string|bool $file_format Optional. Define which file formats should be removed. Both CSS and JS
283
- * files will be removed if set to false. Accepts 'false', 'css' or 'js'.
284
- *
285
- * @return void
286
- */
287
- function jupiterx_flush_admin_compiler( $id, $file_format = false ) {
288
- jupiterx_flush_compiler( $id, $file_format, true );
289
- }
290
-
291
- /**
292
- * Flush cache plugins
293
- *
294
- * @since 1.0.0
295
- *
296
- * @return void
297
- */
298
- function jupiterx_flush_cache_plugins() {
299
-
300
- if ( function_exists( 'w3tc_pgcache_flush' ) ) {
301
- w3tc_pgcache_flush();
302
- }
303
-
304
- if ( function_exists( 'wp_cache_clear_cache' ) ) {
305
- wp_cache_clear_cache();
306
- }
307
-
308
- if ( function_exists( 'rocket_clean_domain' ) ) {
309
- rocket_clean_domain();
310
- }
311
-
312
- if ( class_exists( 'WpFastestCache' ) ) {
313
- $GLOBALS['wp_fastest_cache']->deleteCache();
314
- }
315
-
316
- if ( class_exists( 'autoptimizeCache' ) ) {
317
- autoptimizeCache::clearall();
318
- }
319
- }
320
-
321
- /**
322
- * Get absolute path to the Jupiter' compiler directory.
323
- *
324
- * @since 1.0.0
325
- *
326
- * @param bool $is_admin Optional. When true, gets the admin compiler directory. Default is false.
327
- *
328
- * @return string
329
- */
330
- function jupiterx_get_compiler_dir( $is_admin = false ) {
331
- $wp_upload_dir = wp_upload_dir();
332
- $suffix = $is_admin ? 'jupiterx/admin-compiler/' : 'jupiterx/compiler/';
333
-
334
- /**
335
- * Deprecated. Filter the Jupiter compiler directory.
336
- *
337
- * This filter is deprecated for security and compatibility purposes.
338
- *
339
- * @since 1.0.0
340
- * @deprecated 1.3.0
341
- */
342
- apply_filters( 'jupiterx_compiler_dir', false, $is_admin );
343
-
344
- return wp_normalize_path( trailingslashit( $wp_upload_dir['basedir'] ) . $suffix );
345
- }
346
-
347
- /**
348
- * Get absolute URL to the Jupiter' compiler directory.
349
- *
350
- * @since 1.0.0
351
- *
352
- * @param bool $is_admin Optional. When true, gets the admin compiler directory. Default is false.
353
- *
354
- * @return string
355
- */
356
- function jupiterx_get_compiler_url( $is_admin = false ) {
357
- $wp_upload_dir = wp_upload_dir();
358
- $suffix = $is_admin ? 'jupiterx/admin-compiler/' : 'jupiterx/compiler/';
359
-
360
- return trailingslashit( $wp_upload_dir['baseurl'] ) . $suffix;
361
- }
362
-
363
- /**
364
- * Check if development mode is enabled.
365
- *
366
- * Takes legacy constant into consideration.
367
- *
368
- * @since 1.0.0
369
- * @ignore
370
- * @access private
371
- *
372
- * @return bool
373
- */
374
- function _jupiterx_is_compiler_dev_mode() {
375
-
376
- if ( defined( 'JUPITERX_COMPILER_DEV_MODE' ) ) {
377
- return JUPITERX_COMPILER_DEV_MODE;
378
- }
379
-
380
- return jupiterx_get_option( 'dev_mode', false );
381
- }
382
-
383
- /**
384
- * Get cache busting method.
385
- *
386
- * @since 1.0.0
387
- * @access private
388
- *
389
- * @return bool
390
- */
391
- function _jupiterx_get_cache_busting() {
392
- return jupiterx_get_option( 'cache_busting', true );
393
- }
394
-
395
- /**
396
- * Initialize added fragments global.
397
- *
398
- * @since 1.0.0
399
- * @ignore
400
- * @access private
401
- */
402
- global $_jupiterx_compiler_added_fragments;
403
-
404
- if ( ! isset( $_jupiterx_compiler_added_fragments ) ) {
405
- $_jupiterx_compiler_added_fragments = array(
406
- 'css' => array(),
407
- 'less' => array(),
408
- 'scss' => array(),
409
- 'js' => array(),
410
- );
411
- }
1
+ <?php
2
+ /**
3
+ * Compile and cache CSS, LESS and JS files.
4
+ *
5
+ * The Jupiter Compiler compiles multiple internal or external CSS, LESS and JS files on a
6
+ * per page basis. LESS content will automatically be converted to CSS.
7
+ *
8
+ * Internal file changes are automatically detected if development mode is enabled.
9
+ * Third party enqueued styles and scripts can be compiled and cached into a single file.
10
+ *
11
+ * @package API\Compiler
12
+ */
13
+
14
+ /**
15
+ * Compile CSS fragments and enqueue compiled file.
16
+ *
17
+ * This function should be used in a similar fashion to
18
+ * {@link http://codex.wordpress.org/Function_Reference/wp_enqueue_script wp_enqueue_script()}.
19
+ *
20
+ * Fragments can be added to the compiler using {@see jupiterx_compiler_add_fragment()}.
21
+ *
22
+ * @since 1.0.0
23
+ *
24
+ * @param string $id A unique string used as a reference. Similar to the WordPress scripts
25
+ * $handle argument.
26
+ * @param string|array $fragments File(s) absolute path. Internal or external file(s) url accepted but may increase
27
+ * compiling time.
28
+ * @param array $args {
29
+ * Optional. Array of arguments used by the compiler.
30
+ *
31
+ * @type array $depedencies An array of registered handles this script depends on. Default false.
32
+ * }
33
+ *
34
+ * @return object Compiler object.
35
+ */
36
+ function jupiterx_compile_css_fragments( $id, $fragments, $args = array() ) {
37
+
38
+ if ( empty( $fragments ) ) {
39
+ return false;
40
+ }
41
+
42
+ $params = array(
43
+ 'id' => $id,
44
+ 'type' => 'style',
45
+ 'format' => 'css',
46
+ 'fragments' => (array) $fragments,
47
+ );
48
+
49
+ $compiler = new _JupiterX_Compiler( array_merge( $params, $args ) );
50
+ $compiler->run_compiler();
51
+
52
+ return $compiler;
53
+ }
54
+
55
+ /**
56
+ * Compile LESS fragments, convert to CSS and enqueue compiled file.
57
+ *
58
+ * This function should be used in a similar fashion to
59
+ * {@link http://codex.wordpress.org/Function_Reference/wp_enqueue_script wp_enqueue_script()}.
60
+ *
61
+ * Fragments can be added to the compiler using {@see jupiterx_compiler_add_fragment()}.
62
+ *
63
+ * @since 1.0.0
64
+ *
65
+ * @param string $id The compiler ID. Similar to the WordPress scripts $handle argument.
66
+ * @param string|array $fragments File(s) absolute path. Internal or external file(s) url accepted but may increase
67
+ * compiling time.
68
+ * @param array $args {
69
+ * Optional. Array of arguments used by the compiler.
70
+ *
71
+ * @type array $depedencies An array of registered handles this script depends on. Default false.
72
+ * }
73
+ *
74
+ * @return object Compiler object.
75
+ */
76
+ function jupiterx_compile_less_fragments( $id, $fragments, $args = array() ) {
77
+
78
+ if ( empty( $fragments ) ) {
79
+ return false;
80
+ }
81
+
82
+ $params = array(
83
+ 'id' => $id,
84
+ 'type' => 'style',
85
+ 'format' => 'less',
86
+ 'fragments' => (array) $fragments,
87
+ );
88
+
89
+ $compiler = new _JupiterX_Compiler( array_merge( $params, $args ) );
90
+ $compiler->run_compiler();
91
+
92
+ return $compiler;
93
+ }
94
+
95
+ /**
96
+ * Compile LESS fragments, convert to CSS and enqueue compiled file.
97
+ *
98
+ * This function should be used in a similar fashion to
99
+ * {@link http://codex.wordpress.org/Function_Reference/wp_enqueue_script wp_enqueue_script()}.
100
+ *
101
+ * Fragments can be added to the compiler using {@see jupiterx_compiler_add_fragment()}.
102
+ *
103
+ * @since 1.0.0
104
+ *
105
+ * @param string $id The compiler ID. Similar to the WordPress scripts $handle argument.
106
+ * @param string|array $fragments File(s) absolute path. Internal or external file(s) url accepted but may increase
107
+ * compiling time.
108
+ * @param array $args {
109
+ * Optional. Array of arguments used by the compiler.
110
+ *
111
+ * @type array $depedencies An array of registered handles this script depends on. Default false.
112
+ * }
113
+ *
114
+ * @return object Compiler object.
115
+ */
116
+ function jupiterx_compile_scss_fragments( $id, $fragments, $args = array() ) {
117
+
118
+ if ( empty( $fragments ) ) {
119
+ return false;
120
+ }
121
+
122
+ $params = array(
123
+ 'id' => $id,
124
+ 'type' => 'style',
125
+ 'format' => 'scss',
126
+ 'fragments' => (array) $fragments,
127
+ );
128
+
129
+ $compiler = new _JupiterX_Compiler( array_merge( $params, $args ) );
130
+ $compiler->run_compiler();
131
+
132
+ return $compiler;
133
+ }
134
+
135
+ /**
136
+ * Compile JS fragments and enqueue compiled file.
137
+ *
138
+ * This function should be used in a similar fashion to
139
+ * {@link http://codex.wordpress.org/Function_Reference/wp_enqueue_script wp_enqueue_script()}.
140
+ *
141
+ * Fragments can be added to the compiler using {@see jupiterx_compiler_add_fragment()}.
142
+ *
143
+ * @since 1.0.0
144
+ *
145
+ * @param string $id The compiler ID. Similar to the WordPress scripts $handle argument.
146
+ * @param string|array $fragments File(s) absolute path. Internal or external file(s) URL accepted but may increase
147
+ * compiling time.
148
+ * @param array $args {
149
+ * Optional. Array of arguments used by the compiler.
150
+ *
151
+ * @type array $depedencies An array of registered handles this script depends on. Default false.
152
+ * @type bool $in_footer Whether to enqueue the script before </head> or before </body>. Default false.
153
+ * @type bool $minify_js Whether the JavaScript should be minified or not. Be aware that minifying
154
+ * the JavaScript can considerably slow down the process of compiling files.
155
+ * Default false.
156
+ * }
157
+ *
158
+ * @return object Compiler object.
159
+ */
160
+ function jupiterx_compile_js_fragments( $id, $fragments, $args = array() ) {
161
+
162
+ if ( empty( $fragments ) ) {
163
+ return false;
164
+ }
165
+
166
+ $params = array(
167
+ 'id' => $id,
168
+ 'type' => 'script',
169
+ 'format' => 'js',
170
+ 'fragments' => (array) $fragments,
171
+ );
172
+
173
+ $compiler = new _JupiterX_Compiler( array_merge( $params, $args ) );
174
+ $compiler->run_compiler();
175
+
176
+ return $compiler;
177
+ }
178
+
179
+ /**
180
+ * Add CSS, LESS or JS fragments to a compiler.
181
+ *
182
+ * This function should be used in a similar fashion to
183
+ * {@link http://codex.wordpress.org/Function_Reference/wp_enqueue_script wp_enqueue_script()}.
184
+ *
185
+ * @since 1.0.0
186
+ *
187
+ * @param string $id The compiler ID. Similar to the WordPress scripts $handle argument.
188
+ * @param string|array $fragments File(s) absolute path. Internal or external file(s) url accepted but may increase
189
+ * compiling time.
190
+ * @param string $format Compiler format the fragments should be added to. Accepts 'css',
191
+ * 'less' or 'js'.
192
+ *
193
+ * @return void|bool
194
+ * @SuppressWarnings(PHPMD.ElseExpression)
195
+ */
196
+ function jupiterx_compiler_add_fragment( $id, $fragments, $format ) {
197
+
198
+ if ( empty( $fragments ) ) {
199
+ return false;
200
+ }
201
+
202
+ global $_jupiterx_compiler_added_fragments;
203
+
204
+ foreach ( (array) $fragments as $key => $fragment ) {
205
+
206
+ // Stop here if the format isn't valid.
207
+ if ( ! isset( $_jupiterx_compiler_added_fragments[ $format ] ) ) {
208
+ continue;
209
+ }
210
+
211
+ // Register a new compiler ID if it doesn't exist and add fragment.
212
+ if ( ! isset( $_jupiterx_compiler_added_fragments[ $format ][ $id ] ) ) {
213
+ $_jupiterx_compiler_added_fragments[ $format ][ $id ] = array( $fragment );
214
+ } else { // Add fragment to existing compiler.
215
+ $_jupiterx_compiler_added_fragments[ $format ][ $id ][] = $fragment;
216
+ }
217
+ }
218
+ }
219
+
220
+ /**
221
+ * Flush cached compiler files.
222
+ *
223
+ * Each compiler has its own folder which contains the cached CSS and JS files. The file format
224
+ * of the cached file can be specified if needed.
225
+ *
226
+ * @since 1.0.0
227
+ *
228
+ * @param string $id The compiler ID. Similar to the WordPress scripts $handle argument.
229
+ * @param string|bool $file_format Optional. Define which file format(s) should be removed. Both CSS and JS
230
+ * files will be removed if set to false. Accepts 'false', 'css' or 'js'.
231
+ * @param bool $admin Optional. Whether it is an admin compiler or not.
232
+ *
233
+ * @return void|bool
234
+ */
235
+ function jupiterx_flush_compiler( $id, $file_format = false, $admin = false ) {
236
+ static $jupiterx_flushed = false;
237
+
238
+ $cache_dir = jupiterx_get_compiler_dir( $admin );
239
+
240
+ // Always flush Jupiter' global cache.
241
+ if ( ! $jupiterx_flushed ) {
242
+ $jupiterx_flushed = true;
243
+
244
+ jupiterx_flush_compiler( 'jupiterx', $file_format, $admin );
245
+ }
246
+
247
+ $dir = trailingslashit( $cache_dir ) . $id;
248
+
249
+ // Stop here if directory doesn't exist.
250
+ if ( ! is_dir( $dir ) ) {
251
+ return;
252
+ }
253
+
254
+ // Remove only the specified file format.
255
+ if ( $file_format ) {
256
+ $items = scandir( $dir );
257
+ unset( $items[0], $items[1] );
258
+
259
+ foreach ( $items as $item ) {
260
+ if ( false !== stripos( $item, '.' . $file_format ) ) {
261
+ @unlink( trailingslashit( $dir ) . $item ); // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Valid use case.
262
+ }
263
+ }
264
+
265
+ return;
266
+ }
267
+
268
+ // Remove all file formats.
269
+ jupiterx_remove_dir( $dir );
270
+
271
+ jupiterx_flush_cache_plugins();
272
+ }
273
+
274
+ /**
275
+ * Flush admin cached compiler files.
276
+ *
277
+ * This function is a shortcut of {@see jupiterx_flush_compiler()}.
278
+ *
279
+ * @since 1.0.0
280
+ *
281
+ * @param string $id The compiler ID. Similar to the WordPress scripts $handle argument.
282
+ * @param string|bool $file_format Optional. Define which file formats should be removed. Both CSS and JS
283
+ * files will be removed if set to false. Accepts 'false', 'css' or 'js'.
284
+ *
285
+ * @return void
286
+ */
287
+ function jupiterx_flush_admin_compiler( $id, $file_format = false ) {
288
+ jupiterx_flush_compiler( $id, $file_format, true );
289
+ }
290
+
291
+ /**
292
+ * Flush cache plugins
293
+ *
294
+ * @since 1.0.0
295
+ *
296
+ * @return void
297
+ */
298
+ function jupiterx_flush_cache_plugins() {
299
+
300
+ if ( function_exists( 'w3tc_pgcache_flush' ) ) {
301
+ w3tc_pgcache_flush();
302
+ }
303
+
304
+ if ( function_exists( 'wp_cache_clear_cache' ) ) {
305
+ wp_cache_clear_cache();
306
+ }
307
+
308
+ if ( function_exists( 'rocket_clean_domain' ) ) {
309
+ rocket_clean_domain();
310
+ }
311
+
312
+ if ( class_exists( 'WpFastestCache' ) ) {
313
+ $GLOBALS['wp_fastest_cache']->deleteCache();
314
+ }
315
+
316
+ if ( class_exists( 'autoptimizeCache' ) ) {
317
+ autoptimizeCache::clearall();
318
+ }
319
+ }
320
+
321
+ /**
322
+ * Get absolute path to the Jupiter' compiler directory.
323
+ *
324
+ * @since 1.0.0
325
+ *
326
+ * @param bool $is_admin Optional. When true, gets the admin compiler directory. Default is false.
327
+ *
328
+ * @return string
329
+ */
330
+ function jupiterx_get_compiler_dir( $is_admin = false ) {
331
+ $wp_upload_dir = wp_upload_dir();
332
+ $suffix = $is_admin ? 'jupiterx/admin-compiler/' : 'jupiterx/compiler/';
333
+
334
+ /**
335
+ * Deprecated. Filter the Jupiter compiler directory.
336
+ *
337
+ * This filter is deprecated for security and compatibility purposes.
338
+ *
339
+ * @since 1.0.0
340
+ * @deprecated 1.3.0
341
+ */
342
+ apply_filters( 'jupiterx_compiler_dir', false, $is_admin );
343
+
344
+ return wp_normalize_path( trailingslashit( $wp_upload_dir['basedir'] ) . $suffix );
345
+ }
346
+
347
+ /**
348
+ * Get absolute URL to the Jupiter' compiler directory.
349
+ *
350
+ * @since 1.0.0
351
+ *
352
+ * @param bool $is_admin Optional. When true, gets the admin compiler directory. Default is false.
353
+ *
354
+ * @return string
355
+ */
356
+ function jupiterx_get_compiler_url( $is_admin = false ) {
357
+ $wp_upload_dir = wp_upload_dir();
358
+ $suffix = $is_admin ? 'jupiterx/admin-compiler/' : 'jupiterx/compiler/';
359
+
360
+ return trailingslashit( $wp_upload_dir['baseurl'] ) . $suffix;
361
+ }
362
+
363
+ /**
364
+ * Check if development mode is enabled.
365
+ *
366
+ * Takes legacy constant into consideration.
367
+ *
368
+ * @since 1.0.0
369
+ * @ignore
370
+ * @access private
371
+ *
372
+ * @return bool
373
+ */
374
+ function _jupiterx_is_compiler_dev_mode() {
375
+
376
+ if ( defined( 'JUPITERX_COMPILER_DEV_MODE' ) ) {
377
+ return JUPITERX_COMPILER_DEV_MODE;
378
+ }
379
+
380
+ return jupiterx_get_option( 'dev_mode', false );
381
+ }
382
+
383
+ /**
384
+ * Get cache busting method.
385
+ *
386
+ * @since 1.0.0
387
+ * @access private
388
+ *
389
+ * @return bool
390
+ */
391
+ function _jupiterx_get_cache_busting() {
392
+ return jupiterx_get_option( 'cache_busting', true );
393
+ }
394
+
395
+ /**
396
+ * Initialize added fragments global.
397
+ *
398
+ * @since 1.0.0
399
+ * @ignore
400
+ * @access private
401
+ */
402
+ global $_jupiterx_compiler_added_fragments;
403
+
404
+ if ( ! isset( $_jupiterx_compiler_added_fragments ) ) {
405
+ $_jupiterx_compiler_added_fragments = array(
406
+ 'css' => array(),
407
+ 'less' => array(),
408
+ 'scss' => array(),
409
+ 'js' => array(),
410
+ );
411
+ }
includes/compiler/preprocess-aliases.ini CHANGED
@@ -1,277 +1,277 @@
1
- ;----------------------------------------------------------------
2
- ;
3
- ; Add or delete aliases to suit your needs.
4
- ; Modified for Jupiter X.
5
- ;
6
- ; Sources:
7
- ; http://developer.mozilla.org/en-US/docs/CSS/CSS_Reference
8
- ; http://caniuse.com/#cats=CSS
9
- ;
10
- ;----------------------------------------------------------------
11
-
12
- ; Property aliases.
13
-
14
- [properties]
15
-
16
- ; Animations.
17
- animation[] = -webkit-animation
18
- animation-delay[] = -webkit-animation-delay
19
- animation-direction[] = -webkit-animation-direction
20
- animation-duration[] = -webkit-animation-duration
21
- animation-fill-mode[] = -webkit-animation-fill-mode
22
- animation-iteration-count[] = -webkit-animation-iteration-count
23
- animation-name[] = -webkit-animation-name
24
- animation-play-state[] = -webkit-animation-play-state
25
- animation-timing-function[] = -webkit-animation-timing-function
26
-
27
- ; Backface visibility.
28
- backface-visibility[] = -webkit-backface-visibility
29
-
30
- ; Border-image.
31
- border-image[] = -webkit-border-image
32
-
33
- ; Box decoration.
34
- box-decoration-break[] = -webkit-box-decoration-break
35
-
36
- ; Box shadow.
37
- ; box-shadow[] = -webkit-box-shadow
38
-
39
- ; Box sizing.
40
- ; box-sizing[] = -webkit-box-sizing
41
- ; box-sizing[] = -moz-box-sizing
42
-
43
- ; Columns.
44
- columns[] = -webkit-columns
45
- columns[] = -moz-columns
46
- column-count[] = -webkit-column-count
47
- column-count[] = -moz-column-count
48
- column-fill[] = -webkit-column-fill
49
- column-fill[] = -moz-column-fill
50
- column-gap[] = -webkit-column-gap
51
- column-gap[] = -moz-column-gap
52
- column-rule[] = -webkit-column-rule
53
- column-rule[] = -moz-column-rule
54
- column-rule-style[] = -webkit-column-rule-style
55
- column-rule-style[] = -moz-column-rule-style
56
- column-rule-width[] = -webkit-column-rule-width
57
- column-rule-width[] = -moz-column-rule-width
58
- column-rule-style[] = -webkit-column-rule-style
59
- column-rule-style[] = -moz-column-rule-style
60
- column-rule-color[] = -webkit-column-rule-color
61
- column-rule-color[] = -moz-column-rule-color
62
- column-span[] = -webkit-column-span
63
- column-span[] = -moz-column-span
64
- column-width[] = -webkit-column-width
65
- column-width[] = -moz-column-width
66
-
67
- ; Filter.
68
- filter[] = -webkit-filter
69
-
70
- ; Flexbox (2012).
71
- ;
72
- ; Merges two similar versions of the flexbox spec:
73
- ; - September 2012 (for non IE): http://www.w3.org/TR/2012/CR-css3-flexbox-20120918
74
- ; - March 2012 (for IE10): http://www.w3.org/TR/2012/WD-css3-flexbox-20120322
75
- ;
76
- ; The early 2012 spec mostly differs only in syntax to the later one, with the notable
77
- ; exception of not supporting seperate properties for <flex-grow>, <flex-shrink>
78
- ; and <flex-basis>. These properties are available in both 2012 implementations via
79
- ; <flex> shorthand.
80
- ;
81
- ; Support for the early 2012 syntax implemented in IE10 is achieved here in part with
82
- ; property aliases, and in part with declaration aliases later in this file.
83
- ;
84
- ; align-content[] = -webkit-align-content
85
- ; align-items[] = -webkit-align-items
86
- ; align-self[] = -webkit-align-self
87
- ; flex[] = -webkit-flex
88
- flex[] = -ms-flexbox
89
- ; flex-basis[] = -webkit-flex-basis
90
- ; flex-direction[] = -webkit-flex-direction
91
- flex-direction[] = -ms-flex-direction
92
- ; flex-flow[] = -webkit-flex-flow
93
- flex-flow[] = -ms-flex-flow
94
- ; flex-grow[] = -webkit-flex-grow
95
- ; flex-shrink[] = -webkit-flex-shrink
96
- ; flex-wrap[] = -webkit-flex-wrap
97
- ; flex-wrap[] = -ms-flex-wrap
98
- ; justify-content[] = -webkit-justify-content
99
- ; order[] = -webkit-order
100
- order[] = -ms-flex-order
101
-
102
- ; Hyphens.
103
- hyphens[] = -webkit-hyphens
104
- hyphens[] = -moz-hyphens
105
- hyphens[] = -ms-hyphens
106
-
107
- ; Outline radius.
108
- outline-radius[] = -moz-outline-radius
109
- outline-top-left-radius[] = -moz-outline-radius-topleft
110
- outline-top-right-radius[] = -moz-outline-radius-topright
111
- outline-bottom-left-radius[] = -moz-outline-radius-bottomleft
112
- outline-bottom-right-radius[] = -moz-outline-radius-bottomright
113
-
114
- ; Perspective.
115
- perspective[] = -webkit-perspective
116
- perspective-origin[] = -webkit-perspective-origin
117
-
118
- ; Shapes
119
- shape-image-threshold[] = -webkit-shape-image-threshold
120
- shape-outside[] = -webkit-shape-outside
121
- shape-margin[] = -webkit-shape-margin
122
-
123
- ; Tab size.
124
- tab-size[] = -moz-tab-size
125
- tab-size[] = -o-tab-size
126
-
127
- ; Text align last.
128
- text-align-last[] = -webkit-text-align-last
129
- text-align-last[] = -moz-text-align-last
130
-
131
- ; Text decoration.
132
- text-decoration-color[] = -moz-text-decoration-color
133
- text-decoration-line[] = -moz-text-decoration-line
134
- text-decoration-style[] = -moz-text-decoration-style
135
-
136
- ; Text overflow (Opera mini support).
137
- text-overflow[] = -o-text-overflow
138
-
139
- ; Transforms.
140
- transform[] = -webkit-transform
141
- ; transform[] = -ms-transform
142
- transform-origin[] = -webkit-transform-origin
143
- ; transform-origin[] = -ms-transform-origin
144
- transform-style[] = -webkit-transform-style
145
- ; transform-style[] = -ms-transform-style
146
-
147
- ; Transitions.
148
- ; transition[] = -webkit-transition
149
- ; transition-delay[] = -webkit-transition-delay
150
- ; transition-duration[] = -webkit-transition-duration
151
- ; transition-property[] = -webkit-transition-property
152
- ; transition-timing-function[] = -webkit-transition-timing-function
153
-
154
- ; User select (non standard).
155
- user-select[] = -webkit-user-select
156
- user-select[] = -moz-user-select
157
- user-select[] = -ms-user-select
158
-
159
-
160
- ;----------------------------------------------------------------
161
- ; Declaration aliases.
162
-
163
- [declarations]
164
-
165
- ; Flexbox (2012).
166
- display:flex[] = display:-ms-flexbox
167
- ; display:flex[] = display:-webkit-flex
168
- display:inline-flex[] = display:-ms-inline-flexbox
169
- ; display:inline-flex[] = display:-webkit-inline-flex
170
-
171
- ; Flexbox (early 2012).
172
- align-content:flex-start[] = -ms-flex-line-pack:start
173
- align-content:flex-end[] = -ms-flex-line-pack:end
174
- align-content:center[] = -ms-flex-line-pack:center
175
- align-content:space-between[] = -ms-flex-line-pack:justify
176
- align-content:space-around[] = -ms-flex-line-pack:distribute
177
- align-content:stretch[] = -ms-flex-line-pack:stretch
178
-
179
- align-items:flex-start[] = -ms-flex-align:start
180
- align-items:flex-end[] = -ms-flex-align:end
181
- align-items:center[] = -ms-flex-align:center
182
- align-items:baseline[] = -ms-flex-align:baseline
183
- align-items:stretch[] = -ms-flex-align:stretch
184
-
185
- align-self:auto[] = -ms-flex-item-align:auto
186
- align-self:flex-start[] = -ms-flex-item-align:start
187
- align-self:flex-end[] = -ms-flex-item-align:end
188
- align-self:center[] = -ms-flex-item-align:center
189
- align-self:baseline[] = -ms-flex-item-align:baseline
190
- align-self:stretch[] = -ms-flex-item-align:stretch
191
-
192
- justify-content:flex-start[] = -ms-flex-pack:start
193
- justify-content:flex-end[] = -ms-flex-pack:end
194
- justify-content:center[] = -ms-flex-pack:center
195
- justify-content:space-between[] = -ms-flex-pack:justify
196
- justify-content:space-around[] = -ms-flex-pack:distribute
197
-
198
- ; Cursor values (non-standard).
199
- cursor:zoom-in[] = cursor:-webkit-zoom-in
200
- cursor:zoom-in[] = cursor:-moz-zoom-in
201
- cursor:zoom-out[] = cursor:-webkit-zoom-out
202
- cursor:zoom-out[] = cursor:-moz-zoom-out
203
- cursor:grab[] = cursor:-webkit-grab
204
- cursor:grab[] = cursor:-moz-grab
205
- cursor:grabbing[] = cursor:-webkit-grabbing
206
- cursor:grabbing[] = cursor:-moz-grabbing
207
-
208
- ; Experimental width values.
209
- width:max-content[] = width:intrinsic
210
- width:max-content[] = width:-webkit-max-content
211
- width:max-content[] = width:-moz-max-content
212
- width:min-content[] = width:-webkit-min-content
213
- width:min-content[] = width:-moz-min-content
214
- width:available[] = width:-webkit-available
215
- width:available[] = width:-moz-available
216
- width:fit-content[] = width:-webkit-fit-content
217
- width:fit-content[] = width:-moz-fit-content
218
-
219
- max-width:max-content[] = max-width:intrinsic
220
- max-width:max-content[] = max-width:-webkit-max-content
221
- max-width:max-content[] = max-width:-moz-max-content
222
- max-width:min-content[] = max-width:-webkit-min-content
223
- max-width:min-content[] = max-width:-moz-min-content
224
- max-width:available[] = max-width:-webkit-available
225
- max-width:available[] = max-width:-moz-available
226
- max-width:fit-content[] = max-width:-webkit-fit-content
227
- max-width:fit-content[] = max-width:-moz-fit-content
228
-
229
- min-width:max-content[] = min-width:intrinsic
230
- min-width:max-content[] = min-width:-webkit-max-content
231
- min-width:max-content[] = min-width:-moz-max-content
232
- min-width:min-content[] = min-width:-webkit-min-content
233
- min-width:min-content[] = min-width:-moz-min-content
234
- min-width:available[] = min-width:-webkit-available
235
- min-width:available[] = min-width:-moz-available
236
- min-width:fit-content[] = min-width:-webkit-fit-content
237
- min-width:fit-content[] = min-width:-moz-fit-content
238
-
239
- ; Appearance (non-standard).
240
- appearance:none[] = -webkit-appearance:none
241
- appearance:none[] = -moz-appearance:none
242
-
243
- position:sticky[] = position:-webkit-sticky
244
-
245
-
246
- ;----------------------------------------------------------------
247
- ; Function aliases.
248
-
249
- [functions]
250
-
251
- ; Calc.
252
- ; calc[] = -webkit-calc÷
253
-
254
-
255
- [functions.gradients]
256
-
257
- ; Gradients.
258
- ; linear-gradient[] = -webkit-linear-gradient
259
- ; radial-gradient[] = -webkit-radial-gradient
260
-
261
- ; Repeating gradients.
262
- ; repeating-linear-gradient[] = -webkit-repeating-linear-gradient
263
- ; repeating-radial-gradient[] = -webkit-repeating-radial-gradient
264
-
265
-
266
- ;----------------------------------------------------------------
267
- ; @rule aliases.
268
-
269
- [at-rules]
270
-
271
- ; Keyframes.
272
- keyframes[] = -webkit-keyframes
273
-
274
- ; Viewport.
275
- viewport[] = -webkit-viewport
276
- viewport[] = -ms-viewport
277
- viewport[] = -o-viewport
1
+ ;----------------------------------------------------------------
2
+ ;
3
+ ; Add or delete aliases to suit your needs.
4
+ ; Modified for Jupiter X.
5
+ ;
6
+ ; Sources:
7
+ ; http://developer.mozilla.org/en-US/docs/CSS/CSS_Reference
8
+ ; http://caniuse.com/#cats=CSS
9
+ ;
10
+ ;----------------------------------------------------------------
11
+
12
+ ; Property aliases.
13
+
14
+ [properties]
15
+
16
+ ; Animations.
17
+ animation[] = -webkit-animation
18
+ animation-delay[] = -webkit-animation-delay
19
+ animation-direction[] = -webkit-animation-direction
20
+ animation-duration[] = -webkit-animation-duration
21
+ animation-fill-mode[] = -webkit-animation-fill-mode
22
+ animation-iteration-count[] = -webkit-animation-iteration-count
23
+ animation-name[] = -webkit-animation-name
24
+ animation-play-state[] = -webkit-animation-play-state
25
+ animation-timing-function[] = -webkit-animation-timing-function
26
+
27
+ ; Backface visibility.
28
+ backface-visibility[] = -webkit-backface-visibility
29
+
30
+ ; Border-image.
31
+ border-image[] = -webkit-border-image
32
+
33
+ ; Box decoration.
34
+ box-decoration-break[] = -webkit-box-decoration-break
35
+
36
+ ; Box shadow.
37
+ ; box-shadow[] = -webkit-box-shadow
38
+
39
+ ; Box sizing.
40
+ ; box-sizing[] = -webkit-box-sizing
41
+ ; box-sizing[] = -moz-box-sizing
42
+
43
+ ; Columns.
44
+ columns[] = -webkit-columns
45
+ columns[] = -moz-columns
46
+ column-count[] = -webkit-column-count
47
+ column-count[] = -moz-column-count
48
+ column-fill[] = -webkit-column-fill
49
+ column-fill[] = -moz-column-fill
50
+ column-gap[] = -webkit-column-gap
51
+ column-gap[] = -moz-column-gap
52
+ column-rule[] = -webkit-column-rule
53
+ column-rule[] = -moz-column-rule
54
+ column-rule-style[] = -webkit-column-rule-style
55
+ column-rule-style[] = -moz-column-rule-style
56
+ column-rule-width[] = -webkit-column-rule-width
57
+ column-rule-width[] = -moz-column-rule-width
58
+ column-rule-style[] = -webkit-column-rule-style
59
+ column-rule-style[] = -moz-column-rule-style
60
+ column-rule-color[] = -webkit-column-rule-color
61
+ column-rule-color[] = -moz-column-rule-color
62
+ column-span[] = -webkit-column-span
63
+ column-span[] = -moz-column-span
64
+ column-width[] = -webkit-column-width
65
+ column-width[] = -moz-column-width
66
+
67
+ ; Filter.
68
+ filter[] = -webkit-filter
69
+
70
+ ; Flexbox (2012).
71
+ ;
72
+ ; Merges two similar versions of the flexbox spec:
73
+ ; - September 2012 (for non IE): http://www.w3.org/TR/2012/CR-css3-flexbox-20120918
74
+ ; - March 2012 (for IE10): http://www.w3.org/TR/2012/WD-css3-flexbox-20120322
75
+ ;
76
+ ; The early 2012 spec mostly differs only in syntax to the later one, with the notable
77
+ ; exception of not supporting seperate properties for <flex-grow>, <flex-shrink>
78
+ ; and <flex-basis>. These properties are available in both 2012 implementations via
79
+ ; <flex> shorthand.
80
+ ;
81
+ ; Support for the early 2012 syntax implemented in IE10 is achieved here in part with
82
+ ; property aliases, and in part with declaration aliases later in this file.
83
+ ;
84
+ ; align-content[] = -webkit-align-content
85
+ ; align-items[] = -webkit-align-items
86
+ ; align-self[] = -webkit-align-self
87
+ ; flex[] = -webkit-flex
88
+ flex[] = -ms-flexbox
89
+ ; flex-basis[] = -webkit-flex-basis
90
+ ; flex-direction[] = -webkit-flex-direction
91
+ flex-direction[] = -ms-flex-direction
92
+ ; flex-flow[] = -webkit-flex-flow
93
+ flex-flow[] = -ms-flex-flow
94
+ ; flex-grow[] = -webkit-flex-grow
95
+ ; flex-shrink[] = -webkit-flex-shrink
96
+ ; flex-wrap[] = -webkit-flex-wrap
97
+ ; flex-wrap[] = -ms-flex-wrap
98
+ ; justify-content[] = -webkit-justify-content
99
+ ; order[] = -webkit-order
100
+ order[] = -ms-flex-order
101
+
102
+ ; Hyphens.
103
+ hyphens[] = -webkit-hyphens
104
+ hyphens[] = -moz-hyphens
105
+ hyphens[] = -ms-hyphens
106
+
107
+ ; Outline radius.
108
+ outline-radius[] = -moz-outline-radius
109
+ outline-top-left-radius[] = -moz-outline-radius-topleft
110
+ outline-top-right-radius[] = -moz-outline-radius-topright
111
+ outline-bottom-left-radius[] = -moz-outline-radius-bottomleft
112
+ outline-bottom-right-radius[] = -moz-outline-radius-bottomright
113
+
114
+ ; Perspective.
115
+ perspective[] = -webkit-perspective
116
+ perspective-origin[] = -webkit-perspective-origin
117
+
118
+ ; Shapes
119
+ shape-image-threshold[] = -webkit-shape-image-threshold
120
+ shape-outside[] = -webkit-shape-outside
121
+ shape-margin[] = -webkit-shape-margin
122
+
123
+ ; Tab size.
124
+ tab-size[] = -moz-tab-size
125
+ tab-size[] = -o-tab-size
126
+
127
+ ; Text align last.
128
+ text-align-last[] = -webkit-text-align-last
129
+ text-align-last[] = -moz-text-align-last
130
+
131
+ ; Text decoration.
132
+ text-decoration-color[] = -moz-text-decoration-color
133
+ text-decoration-line[] = -moz-text-decoration-line
134
+ text-decoration-style[] = -moz-text-decoration-style
135
+
136
+ ; Text overflow (Opera mini support).
137
+ text-overflow[] = -o-text-overflow
138
+
139
+ ; Transforms.
140
+ transform[] = -webkit-transform
141
+ ; transform[] = -ms-transform
142
+ transform-origin[] = -webkit-transform-origin
143
+ ; transform-origin[] = -ms-transform-origin
144
+ transform-style[] = -webkit-transform-style
145
+ ; transform-style[] = -ms-transform-style
146
+
147
+ ; Transitions.
148
+ ; transition[] = -webkit-transition
149
+ ; transition-delay[] = -webkit-transition-delay
150
+ ; transition-duration[] = -webkit-transition-duration
151
+ ; transition-property[] = -webkit-transition-property
152
+ ; transition-timing-function[] = -webkit-transition-timing-function
153
+
154
+ ; User select (non standard).
155
+ user-select[] = -webkit-user-select
156
+ user-select[] = -moz-user-select
157
+ user-select[] = -ms-user-select
158
+
159
+
160
+ ;----------------------------------------------------------------
161
+ ; Declaration aliases.
162
+
163
+ [declarations]
164
+
165
+ ; Flexbox (2012).
166
+ display:flex[] = display:-ms-flexbox
167
+ ; display:flex[] = display:-webkit-flex
168
+ display:inline-flex[] = display:-ms-inline-flexbox
169
+ ; display:inline-flex[] = display:-webkit-inline-flex
170
+
171
+ ; Flexbox (early 2012).
172
+ align-content:flex-start[] = -ms-flex-line-pack:start
173
+ align-content:flex-end[] = -ms-flex-line-pack:end
174
+ align-content:center[] = -ms-flex-line-pack:center
175
+ align-content:space-between[] = -ms-flex-line-pack:justify
176
+ align-content:space-around[] = -ms-flex-line-pack:distribute
177
+ align-content:stretch[] = -ms-flex-line-pack:stretch
178
+
179
+ align-items:flex-start[] = -ms-flex-align:start
180
+ align-items:flex-end[] = -ms-flex-align:end
181
+ align-items:center[] = -ms-flex-align:center
182
+ align-items:baseline[] = -ms-flex-align:baseline
183
+ align-items:stretch[] = -ms-flex-align:stretch
184
+
185
+ align-self:auto[] = -ms-flex-item-align:auto
186
+ align-self:flex-start[] = -ms-flex-item-align:start
187
+ align-self:flex-end[] = -ms-flex-item-align:end
188
+ align-self:center[] = -ms-flex-item-align:center
189
+ align-self:baseline[] = -ms-flex-item-align:baseline
190
+ align-self:stretch[] = -ms-flex-item-align:stretch
191
+
192
+ justify-content:flex-start[] = -ms-flex-pack:start
193
+ justify-content:flex-end[] = -ms-flex-pack:end
194
+ justify-content:center[] = -ms-flex-pack:center
195
+ justify-content:space-between[] = -ms-flex-pack:justify
196
+ justify-content:space-around[] = -ms-flex-pack:distribute
197
+
198
+ ; Cursor values (non-standard).
199
+ cursor:zoom-in[] = cursor:-webkit-zoom-in
200
+ cursor:zoom-in[] = cursor:-moz-zoom-in
201
+ cursor:zoom-out[] = cursor:-webkit-zoom-out
202
+ cursor:zoom-out[] = cursor:-moz-zoom-out
203
+ cursor:grab[] = cursor:-webkit-grab
204
+ cursor:grab[] = cursor:-moz-grab
205
+ cursor:grabbing[] = cursor:-webkit-grabbing
206
+ cursor:grabbing[] = cursor:-moz-grabbing
207
+
208
+ ; Experimental width values.
209
+ width:max-content[] = width:intrinsic
210
+ width:max-content[] = width:-webkit-max-content
211
+ width:max-content[] = width:-moz-max-content
212
+ width:min-content[] = width:-webkit-min-content
213
+ width:min-content[] = width:-moz-min-content
214
+ width:available[] = width:-webkit-available
215
+ width:available[] = width:-moz-available
216
+ width:fit-content[] = width:-webkit-fit-content
217
+ width:fit-content[] = width:-moz-fit-content
218
+
219
+ max-width:max-content[] = max-width:intrinsic
220
+ max-width:max-content[] = max-width:-webkit-max-content
221
+ max-width:max-content[] = max-width:-moz-max-content
222
+ max-width:min-content[] = max-width:-webkit-min-content
223
+ max-width:min-content[] = max-width:-moz-min-content
224
+ max-width:available[] = max-width:-webkit-available
225
+ max-width:available[] = max-width:-moz-available
226
+ max-width:fit-content[] = max-width:-webkit-fit-content
227
+ max-width:fit-content[] = max-width:-moz-fit-content
228
+
229
+ min-width:max-content[] = min-width:intrinsic
230
+ min-width:max-content[] = min-width:-webkit-max-content
231
+ min-width:max-content[] = min-width:-moz-max-content
232
+ min-width:min-content[] = min-width:-webkit-min-content
233
+ min-width:min-content[] = min-width:-moz-min-content
234
+ min-width:available[] = min-width:-webkit-available
235
+ min-width:available[] = min-width:-moz-available
236
+ min-width:fit-content[] = min-width:-webkit-fit-content
237
+ min-width:fit-content[] = min-width:-moz-fit-content
238
+
239
+ ; Appearance (non-standard).
240
+ appearance:none[] = -webkit-appearance:none
241
+ appearance:none[] = -moz-appearance:none
242
+
243
+ position:sticky[] = position:-webkit-sticky
244
+
245
+
246
+ ;----------------------------------------------------------------
247
+ ; Function aliases.
248
+
249
+ [functions]
250
+
251
+ ; Calc.
252
+ ; calc[] = -webkit-calc÷
253
+
254
+
255
+ [functions.gradients]
256
+
257
+ ; Gradients.
258
+ ; linear-gradient[] = -webkit-linear-gradient
259
+ ; radial-gradient[] = -webkit-radial-gradient
260
+
261
+ ; Repeating gradients.
262
+ ; repeating-linear-gradient[] = -webkit-repeating-linear-gradient
263
+ ; repeating-radial-gradient[] = -webkit-repeating-radial-gradient
264
+
265
+
266
+ ;----------------------------------------------------------------
267
+ ; @rule aliases.
268
+
269
+ [at-rules]
270
+
271
+ ; Keyframes.
272
+ keyframes[] = -webkit-keyframes
273
+
274
+ ; Viewport.
275
+ viewport[] = -webkit-viewport
276
+ viewport[] = -ms-viewport
277
+ viewport[] = -o-viewport
includes/compiler/vendors/js-minifier.php CHANGED
@@ -1,396 +1,396 @@
1
- <?php
2
- /**
3
- * JavaScript compressor, minifies JavaScript.
4
- * Based on JSMin (https://github.com/mrclay/minify, Ryan Grove <ryan@wonko.com>, Stephen Clay <steve@mrclay.org>, BSD License)
5
- *
6
- * @ignore
7
- */
8
- class JSMin {
9
-
10
- const ORD_LF = 10;
11
- const ORD_SPACE = 32;
12
- const ACTION_KEEP_A = 1;
13
- const ACTION_DELETE_A = 2;
14
- const ACTION_DELETE_A_B = 3;
15
-
16
- protected $a = "\n";
17
- protected $b = '';
18
- protected $input = '';
19
- protected $inputIndex = 0;
20
- protected $inputLength = 0;
21
- protected $lookAhead = null;
22
- protected $output = '';
23
- protected $lastByteOut = '';
24
- protected $keptComment = '';
25
-
26
- /**
27
- * @param string $input
28
- */
29
- public function __construct($input)
30
- {
31
- $this->input = $input;
32
- }
33
-
34
- /**
35
- * Perform minification, return result
36
- *
37
- * @return string
38
- */
39
- public function min()
40
- {
41
- if ($this->output !== '') { // min already run
42
- return $this->output;
43
- }
44
-
45
- $mbIntEnc = null;
46
- if (function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2)) {
47
- $mbIntEnc = mb_internal_encoding();
48
- mb_internal_encoding('8bit');
49
- }
50
- $this->input = str_replace("\r\n", "\n", $this->input);
51
- $this->inputLength = strlen($this->input);
52
-
53
- $this->action(self::ACTION_DELETE_A_B);
54
-
55
- while ($this->a !== null) {
56
- // determine next command
57
- $command = self::ACTION_KEEP_A; // default
58
- if ($this->a === ' ') {
59
- if (($this->lastByteOut === '+' || $this->lastByteOut === '-')
60
- && ($this->b === $this->lastByteOut)) {
61
- // Don't delete this space. If we do, the addition/subtraction
62
- // could be parsed as a post-increment
63
- } elseif (! $this->isAlphaNum($this->b)) {
64
- $command = self::ACTION_DELETE_A;
65
- }
66
- } elseif ($this->a === "\n") {
67
- if ($this->b === ' ') {
68
- $command = self::ACTION_DELETE_A_B;
69
-
70
- // in case of mbstring.func_overload & 2, must check for null b,
71
- // otherwise mb_strpos will give WARNING
72
- } elseif ($this->b === null
73
- || (false === strpos('{[(+-!~', $this->b)
74
- && ! $this->isAlphaNum($this->b))) {
75
- $command = self::ACTION_DELETE_A;
76
- }
77
- } elseif (! $this->isAlphaNum($this->a)) {
78
- if ($this->b === ' '
79
- || ($this->b === "\n"
80
- && (false === strpos('}])+-"\'', $this->a)))) {
81
- $command = self::ACTION_DELETE_A_B;
82
- }
83
- }
84
- $this->action($command);
85
- }
86
- $this->output = trim($this->output);
87
-
88
- if ($mbIntEnc !== null) {
89
- mb_internal_encoding($mbIntEnc);
90
- }
91
- return $this->output;
92
- }
93
-
94
- /**
95
- * ACTION_KEEP_A = Output A. Copy B to A. Get the next B.
96
- * ACTION_DELETE_A = Copy B to A. Get the next B.
97
- * ACTION_DELETE_A_B = Get the next B.
98
- *
99
- * @param int $command
100
- * @throws JSMin_UnterminatedRegExpException|JSMin_UnterminatedStringException
101
- */
102
- protected function action($command)
103
- {
104
- // make sure we don't compress "a + ++b" to "a+++b", etc.
105
- if ($command === self::ACTION_DELETE_A_B
106
- && $this->b === ' '
107
- && ($this->a === '+' || $this->a === '-')) {
108
- // Note: we're at an addition/substraction operator; the inputIndex
109
- // will certainly be a valid index
110
- if ($this->input[$this->inputIndex] === $this->a) {
111
- // This is "+ +" or "- -". Don't delete the space.
112
- $command = self::ACTION_KEEP_A;
113
- }
114
- }
115
-
116
- switch ($command) {
117
- case self::ACTION_KEEP_A: // 1
118
- $this->output .= $this->a;
119
-
120
- if ($this->keptComment) {
121
- $this->output = rtrim($this->output, "\n");
122
- $this->output .= $this->keptComment;
123
- $this->keptComment = '';
124
- }
125
-
126
- $this->lastByteOut = $this->a;
127
-
128
- // fallthrough intentional
129
- case self::ACTION_DELETE_A: // 2
130
- $this->a = $this->b;
131
- if ($this->a === "'" || $this->a === '"') { // string literal
132
- $str = $this->a; // in case needed for exception
133
- for(;;) {
134
- $this->output .= $this->a;
135
- $this->lastByteOut = $this->a;
136
-
137
- $this->a = $this->get();
138
- if ($this->a === $this->b) { // end quote
139
- break;
140
- }
141
- if ($this->isEOF($this->a)) {
142
- $byte = $this->inputIndex - 1;
143
- throw new JSMin_UnterminatedStringException(
144
- "JSMin: Unterminated String at byte {$byte}: {$str}");
145
- }
146
- $str .= $this->a;
147
- if ($this->a === '\\') {
148
- $this->output .= $this->a;
149
- $this->lastByteOut = $this->a;
150
-
151
- $this->a = $this->get();
152
- $str .= $this->a;
153
- }
154
- }
155
- }
156
-
157
- // fallthrough intentional
158
- case self::ACTION_DELETE_A_B: // 3
159
- $this->b = $this->next();
160
- if ($this->b === '/' && $this->isRegexpLiteral()) {
161
- $this->output .= $this->a . $this->b;
162
- $pattern = '/'; // keep entire pattern in case we need to report it in the exception
163
- for(;;) {
164
- $this->a = $this->get();
165
- $pattern .= $this->a;
166
- if ($this->a === '[') {
167
- for(;;) {
168
- $this->output .= $this->a;
169
- $this->a = $this->get();
170
- $pattern .= $this->a;
171
- if ($this->a === ']') {
172
- break;
173
- }
174
- if ($this->a === '\\') {
175
- $this->output .= $this->a;
176
- $this->a = $this->get();
177
- $pattern .= $this->a;
178
- }
179
- if ($this->isEOF($this->a)) {
180
- throw new JSMin_UnterminatedRegExpException(
181
- "JSMin: Unterminated set in RegExp at byte "
182
- . $this->inputIndex .": {$pattern}");
183
- }
184
- }
185
- }
186
-
187
- if ($this->a === '/') { // end pattern
188
- break; // while (true)
189
- } elseif ($this->a === '\\') {
190
- $this->output .= $this->a;
191
- $this->a = $this->get();
192
- $pattern .= $this->a;
193
- } elseif ($this->isEOF($this->a)) {
194
- $byte = $this->inputIndex - 1;
195
- throw new JSMin_UnterminatedRegExpException(
196
- "JSMin: Unterminated RegExp at byte {$byte}: {$pattern}");
197
- }
198
- $this->output .= $this->a;
199
- $this->lastByteOut = $this->a;
200
- }
201
- $this->b = $this->next();
202
- }
203
- // end case ACTION_DELETE_A_B
204
- }
205
- }
206
-
207
- /**
208
- * @return bool
209
- */
210
- protected function isRegexpLiteral()
211
- {
212
- if (false !== strpos("(,=:[!&|?+-~*{;", $this->a)) {
213
- // we obviously aren't dividing
214
- return true;
215
- }
216
-
217
- // we have to check for a preceding keyword, and we don't need to pattern
218
- // match over the whole output.
219
- $recentOutput = substr($this->output, -10);
220
-
221
- // check if return/typeof directly precede a pattern without a space
222
- foreach (array('return', 'typeof') as $keyword) {
223
- if ($this->a !== substr($keyword, -1)) {
224
- // certainly wasn't keyword
225
- continue;
226
- }
227
- if (preg_match("~(^|[\\s\\S])" . substr($keyword, 0, -1) . "$~", $recentOutput, $m)) {
228
- if ($m[1] === '' || !$this->isAlphaNum($m[1])) {
229
- return true;
230
- }
231
- }
232
- }
233
-
234
- // check all keywords
235
- if ($this->a === ' ' || $this->a === "\n") {
236
- if (preg_match('~(^|[\\s\\S])(?:case|else|in|return|typeof)$~', $recentOutput, $m)) {
237
- if ($m[1] === '' || !$this->isAlphaNum($m[1])) {
238
- return true;
239
- }
240
- }
241
- }
242
-
243
- return false;
244
- }
245
-
246
- /**
247
- * Return the next character from stdin. Watch out for lookahead. If the character is a control character,
248
- * translate it to a space or linefeed.
249
- *
250
- * @return string
251
- */
252
- protected function get()
253
- {
254
- $c = $this->lookAhead;
255
- $this->lookAhead = null;
256
- if ($c === null) {
257
- // getc(stdin)
258
- if ($this->inputIndex < $this->inputLength) {
259
- $c = $this->input[$this->inputIndex];
260
- $this->inputIndex += 1;
261
- } else {
262
- $c = null;
263
- }
264
- }
265
- if (ord($c) >= self::ORD_SPACE || $c === "\n" || $c === null) {
266
- return $c;
267
- }
268
- if ($c === "\r") {
269
- return "\n";
270
- }
271
- return ' ';
272
- }
273
-
274
- /**
275
- * Does $a indicate end of input?
276
- *
277
- * @param string $a
278
- * @return bool
279
- */
280
- protected function isEOF($a)
281
- {
282
- return ord($a) <= self::ORD_LF;
283
- }
284
-
285
- /**
286
- * Get next char (without getting it). If is ctrl character, translate to a space or newline.
287
- *
288
- * @return string
289
- */
290
- protected function peek()
291
- {
292
- $this->lookAhead = $this->get();
293
- return $this->lookAhead;
294
- }
295
-
296
- /**
297
- * Return true if the character is a letter, digit, underscore, dollar sign, or non-ASCII character.
298
- *
299
- * @param string $c
300
- *
301
- * @return bool
302
- */
303
- protected function isAlphaNum($c)
304
- {
305
- return (preg_match('/^[a-z0-9A-Z_\\$\\\\]$/', $c) || ord($c) > 126);
306
- }
307
-
308
- /**
309
- * Consume a single line comment from input (possibly retaining it)
310
- */
311
- protected function consumeSingleLineComment()
312
- {
313
- $comment = '';
314
- while (true) {
315
- $get = $this->get();
316
- $comment .= $get;
317
- if (ord($get) <= self::ORD_LF) { // end of line reached
318
- // if IE conditional comment
319
- if (preg_match('/^\\/@(?:cc_on|if|elif|else|end)\\b/', $comment)) {
320
- $this->keptComment .= "/{$comment}";
321
- }
322
- return;
323
- }
324
- }
325
- }
326
-
327
- /**
328
- * Consume a multiple line comment from input (possibly retaining it)
329
- *
330
- * @throws JSMin_UnterminatedCommentException
331
- */
332
- protected function consumeMultipleLineComment()
333
- {
334
- $this->get();
335
- $comment = '';
336
- for(;;) {
337
- $get = $this->get();
338
- if ($get === '*') {
339
- if ($this->peek() === '/') { // end of comment reached
340
- $this->get();
341
- if (0 === strpos($comment, '!')) {
342
- // preserved by YUI Compressor
343
- if (!$this->keptComment) {
344
- // don't prepend a newline if two comments right after one another
345
- $this->keptComment = "\n";
346
- }
347
- $this->keptComment .= "/*!" . substr($comment, 1) . "*/\n";
348
- } else if (preg_match('/^@(?:cc_on|if|elif|else|end)\\b/', $comment)) {
349
- // IE conditional
350
- $this->keptComment .= "/*{$comment}*/";
351
- }
352
- return;
353
- }
354
- } elseif ($get === null) {
355
- throw new JSMin_UnterminatedCommentException(
356
- "JSMin: Unterminated comment at byte {$this->inputIndex}: /*{$comment}");
357
- }
358
- $comment .= $get;
359
- }
360
- }
361
-
362
- /**
363
- * Get the next character, skipping over comments. Some comments may be preserved.
364
- *
365
- * @return string
366
- */
367
- protected function next()
368
- {
369
- $get = $this->get();
370
- if ($get === '/') {
371
- switch ($this->peek()) {
372
- case '/':
373
- $this->consumeSingleLineComment();
374
- $get = "\n";
375
- break;
376
- case '*':
377
- $this->consumeMultipleLineComment();
378
- $get = ' ';
379
- break;
380
- }
381
- }
382
- return $get;
383
- }
384
- }
385
- /**
386
- * @ignore
387
- */
388
- class JSMin_UnterminatedStringException extends Exception {}
389
- /**
390
- * @ignore
391
- */
392
- class JSMin_UnterminatedCommentException extends Exception {}
393
- /**
394
- * @ignore
395
- */
396
  class JSMin_UnterminatedRegExpException extends Exception {}
1
+ <?php
2
+ /**
3
+ * JavaScript compressor, minifies JavaScript.
4
+ * Based on JSMin (https://github.com/mrclay/minify, Ryan Grove <ryan@wonko.com>, Stephen Clay <steve@mrclay.org>, BSD License)
5
+ *
6
+ * @ignore
7
+ */
8
+ class JSMin {
9
+
10
+ const ORD_LF = 10;
11
+ const ORD_SPACE = 32;
12
+ const ACTION_KEEP_A = 1;
13
+ const ACTION_DELETE_A = 2;
14
+ const ACTION_DELETE_A_B = 3;
15
+
16
+ protected $a = "\n";
17
+ protected $b = '';
18
+ protected $input = '';
19
+ protected $inputIndex = 0;
20
+ protected $inputLength = 0;
21
+ protected $lookAhead = null;
22
+ protected $output = '';
23
+ protected $lastByteOut = '';
24
+ protected $keptComment = '';
25
+
26
+ /**
27
+ * @param string $input
28
+ */
29
+ public function __construct($input)
30
+ {
31
+ $this->input = $input;
32
+ }
33
+
34
+ /**
35
+ * Perform minification, return result
36
+ *
37
+ * @return string
38
+ */
39
+ public function min()
40
+ {
41
+ if ($this->output !== '') { // min already run
42
+ return $this->output;
43
+ }
44
+
45
+ $mbIntEnc = null;
46
+ if (function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2)) {
47
+ $mbIntEnc = mb_internal_encoding();
48
+ mb_internal_encoding('8bit');
49
+ }
50
+ $this->input = str_replace("\r\n", "\n", $this->input);
51
+ $this->inputLength = strlen($this->input);
52
+
53
+ $this->action(self::ACTION_DELETE_A_B);
54
+
55
+ while ($this->a !== null) {
56
+ // determine next command
57
+ $command = self::ACTION_KEEP_A; // default
58
+ if ($this->a === ' ') {
59
+ if (($this->lastByteOut === '+' || $this->lastByteOut === '-')
60
+ && ($this->b === $this->lastByteOut)) {
61
+ // Don't delete this space. If we do, the addition/subtraction
62
+ // could be parsed as a post-increment
63
+ } elseif (! $this->isAlphaNum($this->b)) {
64
+ $command = self::ACTION_DELETE_A;
65
+ }
66
+ } elseif ($this->a === "\n") {
67
+ if ($this->b === ' ') {
68
+ $command = self::ACTION_DELETE_A_B;
69
+
70
+ // in case of mbstring.func_overload & 2, must check for null b,
71
+ // otherwise mb_strpos will give WARNING
72
+ } elseif ($this->b === null
73
+ || (false === strpos('{[(+-!~', $this->b)
74
+ && ! $this->isAlphaNum($this->b))) {
75
+ $command = self::ACTION_DELETE_A;
76
+ }
77
+ } elseif (! $this->isAlphaNum($this->a)) {
78
+ if ($this->b === ' '
79
+ || ($this->b === "\n"
80
+ && (false === strpos('}])+-"\'', $this->a)))) {
81
+ $command = self::ACTION_DELETE_A_B;
82
+ }
83
+ }
84
+ $this->action($command);
85
+ }
86
+ $this->output = trim($this->output);
87
+
88
+ if ($mbIntEnc !== null) {
89
+ mb_internal_encoding($mbIntEnc);
90
+ }
91
+ return $this->output;
92
+ }
93
+
94
+ /**
95
+ * ACTION_KEEP_A = Output A. Copy B to A. Get the next B.
96
+ * ACTION_DELETE_A = Copy B to A. Get the next B.
97
+ * ACTION_DELETE_A_B = Get the next B.
98
+ *
99
+ * @param int $command
100
+ * @throws JSMin_UnterminatedRegExpException|JSMin_UnterminatedStringException
101
+ */
102
+ protected function action($command)
103
+ {
104
+ // make sure we don't compress "a + ++b" to "a+++b", etc.
105
+ if ($command === self::ACTION_DELETE_A_B
106
+ && $this->b === ' '
107
+ && ($this->a === '+' || $this->a === '-')) {
108
+ // Note: we're at an addition/substraction operator; the inputIndex
109
+ // will certainly be a valid index
110
+ if ($this->input[$this->inputIndex] === $this->a) {
111
+ // This is "+ +" or "- -". Don't delete the space.
112
+ $command = self::ACTION_KEEP_A;
113
+ }
114
+ }
115
+
116
+ switch ($command) {
117
+ case self::ACTION_KEEP_A: // 1
118
+ $this->output .= $this->a;
119
+
120
+ if ($this->keptComment) {
121
+ $this->output = rtrim($this->output, "\n");
122
+ $this->output .= $this->keptComment;
123
+ $this->keptComment = '';
124
+ }
125
+
126
+ $this->lastByteOut = $this->a;
127
+
128
+ // fallthrough intentional
129
+ case self::ACTION_DELETE_A: // 2
130
+ $this->a = $this->b;
131
+ if ($this->a === "'" || $this->a === '"') { // string literal
132
+ $str = $this->a; // in case needed for exception
133
+ for(;;) {
134
+ $this->output .= $this->a;
135
+ $this->lastByteOut = $this->a;
136
+
137
+ $this->a = $this->get();
138
+ if ($this->a === $this->b) { // end quote
139
+ break;
140
+ }
141
+ if ($this->isEOF($this->a)) {
142
+ $byte = $this->inputIndex - 1;
143
+ throw new JSMin_UnterminatedStringException(
144
+ "JSMin: Unterminated String at byte {$byte}: {$str}");
145
+ }
146
+ $str .= $this->a;
147
+ if ($this->a === '\\') {
148
+ $this->output .= $this->a;
149
+ $this->lastByteOut = $this->a;
150
+
151
+ $this->a = $this->get();
152
+ $str .= $this->a;
153
+ }
154
+ }
155
+ }
156
+
157
+ // fallthrough intentional
158
+ case self::ACTION_DELETE_A_B: // 3
159
+ $this->b = $this->next();
160
+ if ($this->b === '/' && $this->isRegexpLiteral()) {
161
+ $this->output .= $this->a . $this->b;
162
+ $pattern = '/'; // keep entire pattern in case we need to report it in the exception
163
+ for(;;) {
164
+ $this->a = $this->get();
165
+ $pattern .= $this->a;
166
+ if ($this->a === '[') {
167
+ for(;;) {
168
+ $this->output .= $this->a;
169
+ $this->a = $this->get();
170
+ $pattern .= $this->a;
171
+ if ($this->a === ']') {
172
+ break;
173
+ }
174
+ if ($this->a === '\\') {
175
+ $this->output .= $this->a;
176
+ $this->a = $this->get();
177
+ $pattern .= $this->a;
178
+ }
179
+ if ($this->isEOF($this->a)) {
180
+ throw new JSMin_UnterminatedRegExpException(
181
+ "JSMin: Unterminated set in RegExp at byte "
182
+ . $this->inputIndex .": {$pattern}");
183
+ }
184
+ }
185
+ }
186
+
187
+ if ($this->a === '/') { // end pattern
188
+ break; // while (true)
189
+ } elseif ($this->a === '\\') {
190
+ $this->output .= $this->a;
191
+ $this->a = $this->get();
192
+ $pattern .= $this->a;
193
+ } elseif ($this->isEOF($this->a)) {
194
+ $byte = $this->inputIndex - 1;
195
+ throw new JSMin_UnterminatedRegExpException(
196
+ "JSMin: Unterminated RegExp at byte {$byte}: {$pattern}");
197
+ }
198
+ $this->output .= $this->a;
199
+ $this->lastByteOut = $this->a;
200
+ }
201
+ $this->b = $this->next();
202
+ }
203
+ // end case ACTION_DELETE_A_B
204
+ }
205
+ }
206
+
207
+ /**
208
+ * @return bool
209
+ */
210
+ protected function isRegexpLiteral()
211
+ {
212
+ if (false !== strpos("(,=:[!&|?+-~*{;", $this->a)) {
213
+ // we obviously aren't dividing
214
+ return true;
215
+ }
216
+
217
+ // we have to check for a preceding keyword, and we don't need to pattern
218
+ // match over the whole output.
219
+ $recentOutput = substr($this->output, -10);
220
+
221
+ // check if return/typeof directly precede a pattern without a space
222
+ foreach (array('return', 'typeof') as $keyword) {
223
+ if ($this->a !== substr($keyword, -1)) {
224
+ // certainly wasn't keyword
225
+ continue;
226
+ }
227
+ if (preg_match("~(^|[\\s\\S])" . substr($keyword, 0, -1) . "$~", $recentOutput, $m)) {
228
+ if ($m[1] === '' || !$this->isAlphaNum($m[1])) {
229
+ return true;
230
+ }
231
+ }
232
+ }
233
+
234
+ // check all keywords
235
+ if ($this->a === ' ' || $this->a === "\n") {
236
+ if (preg_match('~(^|[\\s\\S])(?:case|else|in|return|typeof)$~', $recentOutput, $m)) {
237
+ if ($m[1] === '' || !$this->isAlphaNum($m[1])) {
238
+ return true;
239
+ }
240
+ }
241
+ }
242
+
243
+ return false;
244
+ }
245
+
246
+ /**
247
+ * Return the next character from stdin. Watch out for lookahead. If the character is a control character,
248
+ * translate it to a space or linefeed.
249
+ *
250
+ * @return string
251
+ */
252
+ protected function get()
253
+ {
254
+ $c = $this->lookAhead;
255
+ $this->lookAhead = null;
256
+ if ($c === null) {
257
+ // getc(stdin)
258
+ if ($this->inputIndex < $this->inputLength) {
259
+ $c = $this->input[$this->inputIndex];
260
+ $this->inputIndex += 1;
261
+ } else {
262
+ $c = null;
263
+ }
264
+ }
265
+ if (ord($c) >= self::ORD_SPACE || $c === "\n" || $c === null) {
266
+ return $c;
267
+ }
268
+ if ($c === "\r") {
269
+ return "\n";
270
+ }
271
+ return ' ';
272
+ }
273
+
274
+ /**
275
+ * Does $a indicate end of input?
276
+ *
277
+ * @param string $a
278
+ * @return bool
279
+ */
280
+ protected function isEOF($a)
281
+ {
282
+ return ord($a) <= self::ORD_LF;
283
+ }
284
+
285
+ /**
286
+ * Get next char (without getting it). If is ctrl character, translate to a space or newline.
287
+ *
288
+ * @return string
289
+ */
290
+ protected function peek()
291
+ {
292
+ $this->lookAhead = $this->get();
293
+ return $this->lookAhead;
294
+ }
295
+
296
+ /**
297
+ * Return true if the character is a letter, digit, underscore, dollar sign, or non-ASCII character.
298
+ *
299
+ * @param string $c
300
+ *
301
+ * @return bool
302
+ */
303
+ protected function isAlphaNum($c)
304
+ {
305
+ return (preg_match('/^[a-z0-9A-Z_\\$\\\\]$/', $c) || ord($c) > 126);
306
+ }
307
+
308
+ /**
309
+ * Consume a single line comment from input (possibly retaining it)
310
+ */
311
+ protected function consumeSingleLineComment()
312
+ {
313
+ $comment = '';
314
+ while (true) {
315
+ $get = $this->get();
316
+ $comment .= $get;
317
+ if (ord($get) <= self::ORD_LF) { // end of line reached
318
+ // if IE conditional comment
319
+ if (preg_match('/^\\/@(?:cc_on|if|elif|else|end)\\b/', $comment)) {
320
+ $this->keptComment .= "/{$comment}";
321
+ }
322
+ return;
323
+ }
324
+ }
325
+ }
326
+
327
+ /**
328
+ * Consume a multiple line comment from input (possibly retaining it)
329
+ *
330
+ * @throws JSMin_UnterminatedCommentException
331
+ */
332
+ protected function consumeMultipleLineComment()
333
+ {
334
+ $this->get();
335
+ $comment = '';
336
+ for(;;) {
337
+ $get = $this->get();
338
+ if ($get === '*') {
339
+ if ($this->peek() === '/') { // end of comment reached
340
+ $this->get();
341
+ if (0 === strpos($comment, '!')) {
342
+ // preserved by YUI Compressor
343
+ if (!$this->keptComment) {
344
+ // don't prepend a newline if two comments right after one another
345
+ $this->keptComment = "\n";
346
+ }
347
+ $this->keptComment .= "/*!" . substr($comment, 1) . "*/\n";
348
+ } else if (preg_match('/^@(?:cc_on|if|elif|else|end)\\b/', $comment)) {
349
+ // IE conditional
350
+ $this->keptComment .= "/*{$comment}*/";
351
+ }
352
+ return;
353
+ }
354
+ } elseif ($get === null) {
355
+ throw new JSMin_UnterminatedCommentException(
356
+ "JSMin: Unterminated comment at byte {$this->inputIndex}: /*{$comment}");
357
+ }
358
+ $comment .= $get;
359
+ }
360
+ }
361
+
362
+ /**
363
+ * Get the next character, skipping over comments. Some comments may be preserved.
364
+ *
365
+ * @return string
366
+ */
367
+ protected function next()
368
+ {
369
+ $get = $this->get();
370
+ if ($get === '/') {
371
+ switch ($this->peek()) {
372
+ case '/':
373
+ $this->consumeSingleLineComment();
374
+ $get = "\n";
375
+ break;
376
+ case '*':
377
+ $this->consumeMultipleLineComment();
378
+ $get = ' ';
379
+ break;
380
+ }
381
+ }
382
+ return $get;
383
+ }
384
+ }
385
+ /**
386
+ * @ignore
387
+ */
388
+ class JSMin_UnterminatedStringException extends Exception {}
389
+ /**
390
+ * @ignore
391
+ */
392
+ class JSMin_UnterminatedCommentException extends Exception {}
393
+ /**
394
+ * @ignore
395
+ */
396
  class JSMin_UnterminatedRegExpException extends Exception {}
includes/compiler/vendors/lessc.php CHANGED
@@ -1,3694 +1,3694 @@
1
- <?php
2
- /**
3
- * lessphp v0.4.0
4
- * http://leafo.net/lessphp
5
- *
6
- * LESS css compiler, adapted from http://lesscss.org
7
- *
8
- * Copyright 2012, Leaf Corcoran <leafot@gmail.com>
9
- * Licensed under MIT or GPLv3, see LICENSE
10
- *
11
- * @ignore
12
- */
13
-
14
-
15
- /**
16
- * The less compiler and parser.
17
- *
18
- * Converting LESS to CSS is a three stage process. The incoming file is parsed
19
- * by `lessc_parser` into a syntax tree, then it is compiled into another tree
20
- * representing the CSS structure by `lessc`. The CSS tree is fed into a
21
- * formatter, like `lessc_formatter` which then outputs CSS as a string.
22
- *
23
- * During the first compile, all values are *reduced*, which means that their
24
- * types are brought to the lowest form before being dump as strings. This
25
- * handles math equations, variable dereferences, and the like.
26
- *
27
- * The `parse` function of `lessc` is the entry point.
28
- *
29
- * In summary:
30
- *
31
- * The `lessc` class creates an intstance of the parser, feeds it LESS code,
32
- * then transforms the resulting tree to a CSS tree. This class also holds the
33
- * evaluation context, such as all available mixins and variables at any given
34
- * time.
35
- *
36
- * The `lessc_parser` class is only concerned with parsing its input.
37
- *
38
- * The `lessc_formatter` takes a CSS tree, and dumps it to a formatted string,
39
- * handling things like indentation.
40
- *
41
- * @ignore
42
- */
43
- class JupiterX_Lessc {
44
- static public $VERSION = "v0.4.0";
45
- static protected $TRUE = array("keyword", "true");
46
- static protected $FALSE = array("keyword", "false");
47
-
48
- protected $libFunctions = array();
49
- protected $registeredVars = array();
50
- protected $preserveComments = false;
51
-
52
- public $vPrefix = '@'; // prefix of abstract properties
53
- public $mPrefix = '$'; // prefix of abstract blocks
54
- public $parentSelector = '&';
55
-
56
- public $importDisabled = false;
57
- public $importDir = '';
58
-
59
- protected $numberPrecision = null;
60
-
61
- protected $allParsedFiles = array();
62
-
63
- // set to the parser that generated the current line when compiling
64
- // so we know how to create error messages
65
- protected $sourceParser = null;
66
- protected $sourceLoc = null;
67
-
68
- static public $defaultValue = array("keyword", "");
69
-
70
- static protected $nextImportId = 0; // uniquely identify imports
71
-
72
- // attempts to find the path of an import url, returns null for css files
73
- protected function findImport($url) {
74
- foreach ((array)$this->importDir as $dir) {
75
- $full = $dir.(substr($dir, -1) != '/' ? '/' : '').$url;
76
- if ($this->fileExists($file = $full.'.less') || $this->fileExists($file = $full)) {
77
- return $file;
78
- }
79
- }
80
-
81
- return null;
82
- }
83
-
84
- protected function fileExists($name) {
85
- return is_file($name);
86
- }
87
-
88
- static public function compressList($items, $delim) {
89
- if (!isset($items[1]) && isset($items[0])) return $items[0];
90
- else return array('list', $delim, $items);
91
- }
92
-
93
- static public function preg_quote($what) {
94
- return preg_quote($what, '/');
95
- }
96
-
97
- protected function tryImport($importPath, $parentBlock, $out) {
98
- if ($importPath[0] == "function" && $importPath[1] == "url") {
99
- $importPath = $this->flattenList($importPath[2]);
100
- }
101
-
102
- $str = $this->coerceString($importPath);
103
- if ($str === null) return false;
104
-
105
- $url = $this->compileValue($this->lib_unquote($str));
106
-
107
- // don't import if it ends in css
108
- if (substr_compare($url, '.css', -4, 4) === 0) return false;
109
-
110
- $realPath = $this->findImport($url);
111
-
112
- if ($realPath === null) return false;
113
-
114
- if ($this->importDisabled) {
115
- return array(false, "/* import disabled */");
116
- }
117
-
118
- if (isset($this->allParsedFiles[realpath($realPath)])) {
119
- return array(false, null);
120
- }
121
-
122
- $this->addParsedFile($realPath);
123
- $parser = $this->makeParser($realPath);
124
- $root = $parser->parse($GLOBALS['wp_filesystem']->get_contents($realPath));
125
-
126
- // set the parents of all the block props
127
- foreach ($root->props as $prop) {
128
- if ($prop[0] == "block") {
129
- $prop[1]->parent = $parentBlock;
130
- }
131
- }
132
-
133
- // copy mixins into scope, set their parents
134
- // bring blocks from import into current block
135
- // TODO: need to mark the source parser these came from this file
136
- foreach ($root->children as $childName => $child) {
137
- if (isset($parentBlock->children[$childName])) {
138
- $parentBlock->children[$childName] = array_merge(
139
- $parentBlock->children[$childName],
140
- $child);
141
- } else {
142
- $parentBlock->children[$childName] = $child;
143
- }
144
- }
145
-
146
- $pi = pathinfo($realPath);
147
- $dir = $pi["dirname"];
148
-
149
- list($top, $bottom) = $this->sortProps($root->props, true);
150
- $this->compileImportedProps($top, $parentBlock, $out, $parser, $dir);
151
-
152
- return array(true, $bottom, $parser, $dir);
153
- }
154
-
155
- protected function compileImportedProps($props, $block, $out, $sourceParser, $importDir) {
156
- $oldSourceParser = $this->sourceParser;
157
-
158
- $oldImport = $this->importDir;
159
-
160
- // TODO: this is because the importDir api is stupid
161
- $this->importDir = (array)$this->importDir;
162
- array_unshift($this->importDir, $importDir);
163
-
164
- foreach ($props as $prop) {
165
- $this->compileProp($prop, $block, $out);
166
- }
167
-
168
- $this->importDir = $oldImport;
169
- $this->sourceParser = $oldSourceParser;
170
- }
171
-
172
- /**
173
- * Recursively compiles a block.
174
- *
175
- * A block is analogous to a CSS block in most cases. A single LESS document
176
- * is encapsulated in a block when parsed, but it does not have parent tags
177
- * so all of it's children appear on the root level when compiled.
178
- *
179
- * Blocks are made up of props and children.
180
- *
181
- * Props are property instructions, array tuples which describe an action
182
- * to be taken, eg. write a property, set a variable, mixin a block.
183
- *
184
- * The children of a block are just all the blocks that are defined within.
185
- * This is used to look up mixins when performing a mixin.
186
- *
187
- * Compiling the block involves pushing a fresh environment on the stack,
188
- * and iterating through the props, compiling each one.
189
- *
190
- * See lessc::compileProp()
191
- *
192
- */
193
- protected function compileBlock($block) {
194
- switch ($block->type) {
195
- case "root":
196
- $this->compileRoot($block);
197
- break;
198
- case null:
199
- $this->compileCSSBlock($block);
200
- break;
201
- case "media":
202
- $this->compileMedia($block);
203
- break;
204
- case "directive":
205
- $name = "@" . $block->name;
206
- if (!empty($block->value)) {
207
- $name .= " " . $this->compileValue($this->reduce($block->value));
208
- }
209
-
210
- $this->compileNestedBlock($block, array($name));
211
- break;
212
- default:
213
- $this->throwError("unknown block type: $block->type\n");
214
- }
215
- }
216
-
217
- protected function compileCSSBlock($block) {
218
- $env = $this->pushEnv();
219
-
220
- $selectors = $this->compileSelectors($block->tags);
221
- $env->selectors = $this->multiplySelectors($selectors);
222
- $out = $this->makeOutputBlock(null, $env->selectors);
223
-
224
- $this->scope->children[] = $out;
225
- $this->compileProps($block, $out);
226
-
227
- $block->scope = $env; // mixins carry scope with them!
228
- $this->popEnv();
229
- }
230
-
231
- protected function compileMedia($media) {
232
- $env = $this->pushEnv($media);
233
- $parentScope = $this->mediaParent($this->scope);
234
-
235
- $query = $this->compileMediaQuery($this->multiplyMedia($env));
236
-
237
- $this->scope = $this->makeOutputBlock($media->type, array($query));
238
- $parentScope->children[] = $this->scope;
239
-
240
- $this->compileProps($media, $this->scope);
241
-
242
- if (count($this->scope->lines) > 0) {
243
- $orphanSelelectors = $this->findClosestSelectors();
244
- if (!is_null($orphanSelelectors)) {
245
- $orphan = $this->makeOutputBlock(null, $orphanSelelectors);
246
- $orphan->lines = $this->scope->lines;
247
- array_unshift($this->scope->children, $orphan);
248
- $this->scope->lines = array();
249
- }
250
- }
251
-
252
- $this->scope = $this->scope->parent;
253
- $this->popEnv();
254
- }
255
-
256
- protected function mediaParent($scope) {
257
- while (!empty($scope->parent)) {
258
- if (!empty($scope->type) && $scope->type != "media") {
259
- break;
260
- }
261
- $scope = $scope->parent;
262
- }
263
-
264
- return $scope;
265
- }
266
-
267
- protected function compileNestedBlock($block, $selectors) {
268
- $this->pushEnv($block);
269
- $this->scope = $this->makeOutputBlock($block->type, $selectors);
270
- $this->scope->parent->children[] = $this->scope;
271
-
272
- $this->compileProps($block, $this->scope);
273
-
274
- $this->scope = $this->scope->parent;
275
- $this->popEnv();
276
- }
277
-
278
- protected function compileRoot($root) {
279
- $this->pushEnv();
280
- $this->scope = $this->makeOutputBlock($root->type);
281
- $this->compileProps($root, $this->scope);
282
- $this->popEnv();
283
- }
284
-
285
- protected function compileProps($block, $out) {
286
- foreach ($this->sortProps($block->props) as $prop) {
287
- $this->compileProp($prop, $block, $out);
288
- }
289
-
290
- $out->lines = array_values(array_unique($out->lines));
291
- }
292
-
293
- protected function sortProps($props, $split = false) {
294
- $vars = array();
295
- $imports = array();
296
- $other = array();
297
-
298
- foreach ($props as $prop) {
299
- switch ($prop[0]) {
300
- case "assign":
301
- if (isset($prop[1][0]) && $prop[1][0] == $this->vPrefix) {
302
- $vars[] = $prop;
303
- } else {
304
- $other[] = $prop;
305
- }
306
- break;
307
- case "import":
308
- $id = self::$nextImportId++;
309
- $prop[] = $id;
310
- $imports[] = $prop;
311
- $other[] = array("import_mixin", $id);
312
- break;
313
- default:
314
- $other[] = $prop;
315
- }
316
- }
317
-
318
- if ($split) {
319
- return array(array_merge($vars, $imports), $other);
320
- } else {
321
- return array_merge($vars, $imports, $other);
322
- }
323
- }
324
-
325
- protected function compileMediaQuery($queries) {
326
- $compiledQueries = array();
327
- foreach ($queries as $query) {
328
- $parts = array();
329
- foreach ($query as $q) {
330
- switch ($q[0]) {
331
- case "mediaType":
332
- $parts[] = implode(" ", array_slice($q, 1));
333
- break;
334
- case "mediaExp":
335
- if (isset($q[2])) {
336
- $parts[] = "($q[1]: " .
337
- $this->compileValue($this->reduce($q[2])) . ")";
338
- } else {
339
- $parts[] = "($q[1])";
340
- }
341
- break;
342
- case "variable":
343
- $parts[] = $this->compileValue($this->reduce($q));
344
- break;
345
- }
346
- }
347
-
348
- if (count($parts) > 0) {
349
- $compiledQueries[] = implode(" and ", $parts);
350
- }
351
- }
352
-
353
- $out = "@media";
354
- if (!empty($parts)) {
355
- $out .= " " .
356
- implode($this->formatter->selectorSeparator, $compiledQueries);
357
- }
358
- return $out;
359
- }
360
-
361
- protected function multiplyMedia($env, $childQueries = null) {
362
- if (is_null($env) ||
363
- !empty($env->block->type) && $env->block->type != "media")
364
- {
365
- return $childQueries;
366
- }
367
-
368
- // plain old block, skip
369
- if (empty($env->block->type)) {
370
- return $this->multiplyMedia($env->parent, $childQueries);
371
- }
372
-
373
- $out = array();
374
- $queries = $env->block->queries;
375
- if (is_null($childQueries)) {
376
- $out = $queries;
377
- } else {
378
- foreach ($queries as $parent) {
379
- foreach ($childQueries as $child) {
380
- $out[] = array_merge($parent, $child);
381
- }
382
- }
383
- }
384
-
385
- return $this->multiplyMedia($env->parent, $out);
386
- }
387
-
388
- protected function expandParentSelectors(&$tag, $replace) {
389
- $parts = explode("$&$", $tag);
390
- $count = 0;
391
- foreach ($parts as &$part) {
392
- $part = str_replace($this->parentSelector, $replace, $part, $c);
393
- $count += $c;
394
- }
395
- $tag = implode($this->parentSelector, $parts);
396
- return $count;
397
- }
398
-
399
- protected function findClosestSelectors() {
400
- $env = $this->env;
401
- $selectors = null;
402
- while ($env !== null) {
403
- if (isset($env->selectors)) {
404
- $selectors = $env->selectors;
405
- break;
406
- }
407
- $env = $env->parent;
408
- }
409
-
410
- return $selectors;
411
- }
412
-
413
-
414
- // multiply $selectors against the nearest selectors in env
415
- protected function multiplySelectors($selectors) {
416
- // find parent selectors
417
-
418
- $parentSelectors = $this->findClosestSelectors();
419
- if (is_null($parentSelectors)) {
420
- // kill parent reference in top level selector
421
- foreach ($selectors as &$s) {
422
- $this->expandParentSelectors($s, "");
423
- }
424
-
425
- return $selectors;
426
- }
427
-
428
- $out = array();
429
- foreach ($parentSelectors as $parent) {
430
- foreach ($selectors as $child) {
431
- $count = $this->expandParentSelectors($child, $parent);
432
-
433
- // don't prepend the parent tag if & was used
434
- if ($count > 0) {
435
- $out[] = trim($child);
436
- } else {
437
- $out[] = trim($parent . ' ' . $child);
438
- }
439
- }
440
- }
441
-
442
- return $out;
443
- }
444
-
445
- // reduces selector expressions
446
- protected function compileSelectors($selectors) {
447
- $out = array();
448
-
449
- foreach ($selectors as $s) {
450
- if (is_array($s)) {
451
- list(, $value) = $s;
452
- $out[] = trim($this->compileValue($this->reduce($value)));
453
- } else {
454
- $out[] = $s;
455
- }
456
- }
457
-
458
- return $out;
459
- }
460
-
461
- protected function eq($left, $right) {
462
- return $left == $right;
463
- }
464
-
465
- protected function patternMatch($block, $orderedArgs, $keywordArgs) {
466
- // match the guards if it has them
467
- // any one of the groups must have all its guards pass for a match
468
- if (!empty($block->guards)) {
469
- $groupPassed = false;
470
- foreach ($block->guards as $guardGroup) {
471
- foreach ($guardGroup as $guard) {
472
- $this->pushEnv();
473
- $this->zipSetArgs($block->args, $orderedArgs, $keywordArgs);
474
-
475
- $negate = false;
476
- if ($guard[0] == "negate") {
477
- $guard = $guard[1];
478
- $negate = true;
479
- }
480
-
481
- $passed = $this->reduce($guard) == self::$TRUE;
482
- if ($negate) $passed = !$passed;
483
-
484
- $this->popEnv();
485
-
486
- if ($passed) {
487
- $groupPassed = true;
488
- } else {
489
- $groupPassed = false;
490
- break;
491
- }
492
- }
493
-
494
- if ($groupPassed) break;
495
- }
496
-
497
- if (!$groupPassed) {
498
- return false;
499
- }
500
- }
501
-
502
- if (empty($block->args)) {
503
- return $block->isVararg || empty($orderedArgs) && empty($keywordArgs);
504
- }
505
-
506
- $remainingArgs = $block->args;
507
- if ($keywordArgs) {
508
- $remainingArgs = array();
509
- foreach ($block->args as $arg) {
510
- if ($arg[0] == "arg" && isset($keywordArgs[$arg[1]])) {
511
- continue;
512
- }
513
-
514
- $remainingArgs[] = $arg;
515
- }
516
- }
517
-
518
- $i = -1; // no args
519
- // try to match by arity or by argument literal
520
- foreach ($remainingArgs as $i => $arg) {
521
- switch ($arg[0]) {
522
- case "lit":
523
- if (empty($orderedArgs[$i]) || !$this->eq($arg[1], $orderedArgs[$i])) {
524
- return false;
525
- }
526
- break;
527
- case "arg":
528
- // no arg and no default value
529
- if (!isset($orderedArgs[$i]) && !isset($arg[2])) {
530
- return false;
531
- }
532
- break;
533
- case "rest":
534
- $i--; // rest can be empty
535
- break 2;
536
- }
537
- }
538
-
539
- if ($block->isVararg) {
540
- return true; // not having enough is handled above
541
- } else {
542
- $numMatched = $i + 1;
543
- // greater than becuase default values always match
544
- return $numMatched >= count($orderedArgs);
545
- }
546
- }
547
-
548
- protected function patternMatchAll($blocks, $orderedArgs, $keywordArgs, $skip=array()) {
549
- $matches = null;
550
- foreach ($blocks as $block) {
551
- // skip seen blocks that don't have arguments
552
- if (isset($skip[$block->id]) && !isset($block->args)) {
553
- continue;
554
- }
555
-
556
- if ($this->patternMatch($block, $orderedArgs, $keywordArgs)) {
557
- $matches[] = $block;
558
- }
559
- }
560
-
561
- return $matches;
562
- }
563
-
564
- // attempt to find blocks matched by path and args
565
- protected function findBlocks($searchIn, $path, $orderedArgs, $keywordArgs, $seen=array()) {
566
- if ($searchIn == null) return null;
567
- if (isset($seen[$searchIn->id])) return null;
568
- $seen[$searchIn->id] = true;
569
-
570
- $name = $path[0];
571
-
572
- if (isset($searchIn->children[$name])) {
573
- $blocks = $searchIn->children[$name];
574
- if (count($path) == 1) {
575
- $matches = $this->patternMatchAll($blocks, $orderedArgs, $keywordArgs, $seen);
576
- if (!empty($matches)) {
577
- // This will return all blocks that match in the closest
578
- // scope that has any matching block, like lessjs
579
- return $matches;
580
- }
581
- } else {
582
- $matches = array();
583
- foreach ($blocks as $subBlock) {
584
- $subMatches = $this->findBlocks($subBlock,
585
- array_slice($path, 1), $orderedArgs, $keywordArgs, $seen);
586
-
587
- if (!is_null($subMatches)) {
588
- foreach ($subMatches as $sm) {
589
- $matches[] = $sm;
590
- }
591
- }
592
- }
593
-
594
- return count($matches) > 0 ? $matches : null;
595
- }
596
- }
597
- if ($searchIn->parent === $searchIn) return null;
598
- return $this->findBlocks($searchIn->parent, $path, $orderedArgs, $keywordArgs, $seen);
599
- }
600
-
601
- // sets all argument names in $args to either the default value
602
- // or the one passed in through $values
603
- protected function zipSetArgs($args, $orderedValues, $keywordValues) {
604
- $assignedValues = array();
605
-
606
- $i = 0;
607
- foreach ($args as $a) {
608
- if ($a[0] == "arg") {
609
- if (isset($keywordValues[$a[1]])) {
610
- // has keyword arg
611
- $value = $keywordValues[$a[1]];
612
- } elseif (isset($orderedValues[$i])) {
613
- // has ordered arg
614
- $value = $orderedValues[$i];
615
- $i++;
616
- } elseif (isset($a[2])) {
617
- // has default value
618
- $value = $a[2];
619
- } else {
620
- $this->throwError("Failed to assign arg " . $a[1]);
621
- $value = null; // :(
622
- }
623
-
624
- $value = $this->reduce($value);
625
- $this->set($a[1], $value);
626
- $assignedValues[] = $value;
627
- } else {
628
- // a lit
629
- $i++;
630
- }
631
- }
632
-
633
- // check for a rest
634
- $last = end($args);
635
- if ($last[0] == "rest") {
636
- $rest = array_slice($orderedValues, count($args) - 1);
637
- $this->set($last[1], $this->reduce(array("list", " ", $rest)));
638
- }
639
-
640
- // wow is this the only true use of PHP's + operator for arrays?
641
- $this->env->arguments = $assignedValues + $orderedValues;
642
- }
643
-
644
- // compile a prop and update $lines or $blocks appropriately
645
- protected function compileProp($prop, $block, $out) {
646
- // set error position context
647
- $this->sourceLoc = isset($prop[-1]) ? $prop[-1] : -1;
648
-
649
- switch ($prop[0]) {
650
- case 'assign':
651
- list(, $name, $value) = $prop;
652
- if ($name[0] == $this->vPrefix) {
653
- $this->set($name, $value);
654
- } else {
655
- $out->lines[] = $this->formatter->property($name,
656
- $this->compileValue($this->reduce($value)));
657
- }
658
- break;
659
- case 'block':
660
- list(, $child) = $prop;
661
- $this->compileBlock($child);
662
- break;
663
- case 'mixin':
664
- list(, $path, $args, $suffix) = $prop;
665
-
666
- $orderedArgs = array();
667
- $keywordArgs = array();
668
- foreach ((array)$args as $arg) {
669
- $argval = null;
670
- switch ($arg[0]) {
671
- case "arg":
672
- if (!isset($arg[2])) {
673
- $orderedArgs[] = $this->reduce(array("variable", $arg[1]));
674
- } else {
675
- $keywordArgs[$arg[1]] = $this->reduce($arg[2]);
676
- }
677
- break;
678
-
679
- case "lit":
680
- $orderedArgs[] = $this->reduce($arg[1]);
681
- break;
682
- default:
683
- $this->throwError("Unknown arg type: " . $arg[0]);
684
- }
685
- }
686
-
687
- $mixins = $this->findBlocks($block, $path, $orderedArgs, $keywordArgs);
688
-
689
- if ($mixins === null) {
690
- break; // throw error here??
691
- }
692
-
693
- foreach ($mixins as $mixin) {
694
- if ($mixin === $block && !$orderedArgs) {
695
- continue;
696
- }
697
-
698
- $haveScope = false;
699
- if (isset($mixin->parent->scope)) {
700
- $haveScope = true;
701
- $mixinParentEnv = $this->pushEnv();
702
- $mixinParentEnv->storeParent = $mixin->parent->scope;
703
- }
704
-
705
- $haveArgs = false;
706
- if (isset($mixin->args)) {
707
- $haveArgs = true;
708
- $this->pushEnv();
709
- $this->zipSetArgs($mixin->args, $orderedArgs, $keywordArgs);
710
- }
711
-
712
- $oldParent = $mixin->parent;
713
- if ($mixin != $block) $mixin->parent = $block;
714
-
715
- foreach ($this->sortProps($mixin->props) as $subProp) {
716
- if ($suffix !== null &&
717
- $subProp[0] == "assign" &&
718
- is_string($subProp[1]) &&
719
- $subProp[1]{0} != $this->vPrefix)
720
- {
721
- $subProp[2] = array(
722
- 'list', ' ',
723
- array($subProp[2], array('keyword', $suffix))
724
- );
725
- }
726
-
727
- $this->compileProp($subProp, $mixin, $out);
728
- }
729
-
730
- $mixin->parent = $oldParent;
731
-
732
- if ($haveArgs) $this->popEnv();
733
- if ($haveScope) $this->popEnv();
734
- }
735
-
736
- break;
737
- case 'raw':
738
- $out->lines[] = $prop[1];
739
- break;
740
- case "directive":
741
- list(, $name, $value) = $prop;
742
- $out->lines[] = "@$name " . $this->compileValue($this->reduce($value)).';';
743
- break;
744
- case "comment":
745
- $out->lines[] = $prop[1];
746
- break;
747
- case "import";
748
- list(, $importPath, $importId) = $prop;
749
- $importPath = $this->reduce($importPath);
750
-
751
- if (!isset($this->env->imports)) {
752
- $this->env->imports = array();
753
- }
754
-
755
- $result = $this->tryImport($importPath, $block, $out);
756
-
757
- $this->env->imports[$importId] = $result === false ?
758
- array(false, "@import " . $this->compileValue($importPath).";") :
759
- $result;
760
-
761
- break;
762
- case "import_mixin":
763
- list(,$importId) = $prop;
764
- $import = $this->env->imports[$importId];
765
- if ($import[0] === false) {
766
- if (isset($import[1])) {
767
- $out->lines[] = $import[1];
768
- }
769
- } else {
770
- list(, $bottom, $parser, $importDir) = $import;
771
- $this->compileImportedProps($bottom, $block, $out, $parser, $importDir);
772
- }
773
-
774
- break;
775
- default:
776
- $this->throwError("unknown op: {$prop[0]}\n");
777
- }
778
- }
779
-
780
-
781
- /**
782
- * Compiles a primitive value into a CSS property value.
783
- *
784
- * Values in lessphp are typed by being wrapped in arrays, their format is
785
- * typically:
786
- *
787
- * array(type, contents [, additional_contents]*)
788
- *
789
- * The input is expected to be reduced. This function will not work on
790
- * things like expressions and variables.
791
- */
792
- protected function compileValue($value) {
793
- switch ($value[0]) {
794
- case 'list':
795
- // [1] - delimiter
796
- // [2] - array of values
797
- return implode($value[1], array_map(array($this, 'compileValue'), $value[2]));
798
- case 'raw_color':
799
- if (!empty($this->formatter->compressColors)) {
800
- return $this->compileValue($this->coerceColor($value));
801
- }
802
- return $value[1];
803
- case 'keyword':
804
- // [1] - the keyword
805
- return $value[1];
806
- case 'number':
807
- list(, $num, $unit) = $value;
808
- // [1] - the number
809
- // [2] - the unit
810
- if ($this->numberPrecision !== null) {
811
- $num = round($num, $this->numberPrecision);
812
- }
813
- return $num . $unit;
814
- case 'string':
815
- // [1] - contents of string (includes quotes)
816
- list(, $delim, $content) = $value;
817
- foreach ($content as &$part) {
818
- if (is_array($part)) {
819
- $part = $this->compileValue($part);
820
- }
821
- }
822
- return $delim . implode($content) . $delim;
823
- case 'color':
824
- // [1] - red component (either number or a %)
825
- // [2] - green component
826
- // [3] - blue component
827
- // [4] - optional alpha component
828
- list(, $r, $g, $b) = $value;
829
- $r = round($r);
830
- $g = round($g);
831
- $b = round($b);
832
-
833
- if (count($value) == 5 && $value[4] != 1) { // rgba
834
- return 'rgba('.$r.','.$g.','.$b.','.$value[4].')';
835
- }
836
-
837
- $h = sprintf("#%02x%02x%02x", $r, $g, $b);
838
-
839
- if (!empty($this->formatter->compressColors)) {
840
- // Converting hex color to short notation (e.g. #003399 to #039)
841
- if ($h[1] === $h[2] && $h[3] === $h[4] && $h[5] === $h[6]) {
842
- $h = '#' . $h[1] . $h[3] . $h[5];
843
- }
844
- }
845
-
846
- return $h;
847
-
848
- case 'function':
849
- list(, $name, $args) = $value;
850
- return $name.'('.$this->compileValue($args).')';
851
- default: // assumed to be unit
852
- $this->throwError("unknown value type: $value[0]");
853
- }
854
- }
855
-
856
- protected function lib_pow($args) {
857
- list($base, $exp) = $this->assertArgs($args, 2, "pow");
858
- return pow($this->assertNumber($base), $this->assertNumber($exp));
859
- }
860
-
861
- protected function lib_pi() {
862
- return pi();
863
- }
864
-
865
- protected function lib_mod($args) {
866
- list($a, $b) = $this->assertArgs($args, 2, "mod");
867
- return $this->assertNumber($a) % $this->assertNumber($b);
868
- }
869
-
870
- protected function lib_tan($num) {
871
- return tan($this->assertNumber($num));
872
- }
873
-
874
- protected function lib_sin($num) {
875
- return sin($this->assertNumber($num));
876
- }
877
-
878
- protected function lib_cos($num) {
879
- return cos($this->assertNumber($num));
880
- }
881
-
882
- protected function lib_atan($num) {
883
- $num = atan($this->assertNumber($num));
884
- return array("number", $num, "rad");
885
- }
886
-
887
- protected function lib_asin($num) {
888
- $num = asin($this->assertNumber($num));
889
- return array("number", $num, "rad");
890
- }
891
-
892
- protected function lib_acos($num) {
893
- $num = acos($this->assertNumber($num));
894
- return array("number", $num, "rad");
895
- }
896
-
897
- protected function lib_sqrt($num) {
898
- return sqrt($this->assertNumber($num));
899
- }
900
-
901
- protected function lib_extract($value) {
902
- list($list, $idx) = $this->assertArgs($value, 2, "extract");
903
- $idx = $this->assertNumber($idx);
904
- // 1 indexed
905
- if ($list[0] == "list" && isset($list[2][$idx - 1])) {
906
- return $list[2][$idx - 1];
907
- }
908
- }
909
-
910
- protected function lib_isnumber($value) {
911
- return $this->toBool($value[0] == "number");
912
- }
913
-
914
- protected function lib_isstring($value) {
915
- return $this->toBool($value[0] == "string");
916
- }
917
-
918
- protected function lib_iscolor($value) {
919
- return $this->toBool($this->coerceColor($value));
920
- }
921
-
922
- protected function lib_iskeyword($value) {
923
- return $this->toBool($value[0] == "keyword");
924
- }
925
-
926
- protected function lib_ispixel($value) {
927
- return $this->toBool($value[0] == "number" && $value[2] == "px");
928
- }
929
-
930
- protected function lib_ispercentage($value) {
931
- return $this->toBool($value[0] == "number" && $value[2] == "%");
932
- }
933
-
934
- protected function lib_isem($value) {
935
- return $this->toBool($value[0] == "number" && $value[2] == "em");
936
- }
937
-
938
- protected function lib_isrem($value) {
939
- return $this->toBool($value[0] == "number" && $value[2] == "rem");
940
- }
941
-
942
- protected function lib_rgbahex($color) {
943
- $color = $this->coerceColor($color);
944
- if (is_null($color))
945
- $this->throwError("color expected for rgbahex");
946
-
947
- return sprintf("#%02x%02x%02x%02x",
948
- isset($color[4]) ? $color[4]*255 : 255,
949
- $color[1],$color[2], $color[3]);
950
- }
951
-
952
- protected function lib_argb($color){
953
- return $this->lib_rgbahex($color);
954
- }
955
-
956
- // utility func to unquote a string
957
- // use func_get_arg to prevent Theme Checker triggering unrelated translation warning.
958
- protected function lib_e() {
959
- $arg = func_get_arg(0);
960
- switch ($arg[0]) {
961
- case "list":
962
- $items = $arg[2];
963
- if (isset($items[0])) {
964
- return $this->lib_unquote($items[0]);
965
- }
966
- return self::$defaultValue;
967
- case "string":
968
- $arg[1] = "";
969
- return $arg;
970
- case "keyword":
971
- return $arg;
972
- default:
973
- return array("keyword", $this->compileValue($arg));
974
- }
975
- }
976
-
977
- // use func_get_arg to prevent Theme Checker triggering unrelated translation warning.
978
- protected function lib_unquote($arg) {
979
- return $this->lib_e(func_get_arg(0));
980
- }
981
-
982
- protected function lib__sprintf($args) {
983
- if ($args[0] != "list") return $args;
984
- $values = $args[2];
985
- $string = array_shift($values);
986
- $template = $this->compileValue($this->lib_unquote($string));
987
-
988
- $i = 0;
989
- if (preg_match_all('/%[dsa]/', $template, $m)) {
990
- foreach ($m[0] as $match) {
991
- $val = isset($values[$i]) ?
992
- $this->reduce($values[$i]) : array('keyword', '');
993
-
994
- // lessjs compat, renders fully expanded color, not raw color
995
- if ($color = $this->coerceColor($val)) {
996
- $val = $color;
997
- }
998
-
999
- $i++;
1000
- $rep = $this->compileValue($this->lib_unquote($val));
1001
- $template = preg_replace('/'.self::preg_quote($match).'/',
1002
- $rep, $template, 1);
1003
- }
1004
- }
1005
-
1006
- $d = $string[0] == "string" ? $string[1] : '"';
1007
- return array("string", $d, array($template));
1008
- }
1009
-
1010
- protected function lib_floor($arg) {
1011
- $value = $this->assertNumber($arg);
1012
- return array("number", floor($value), $arg[2]);
1013
- }
1014
-
1015
- protected function lib_ceil($arg) {
1016
- $value = $this->assertNumber($arg);
1017
- return array("number", ceil($value), $arg[2]);
1018
- }
1019
-
1020
- protected function lib_round($arg) {
1021
- $value = $this->assertNumber($arg);
1022
- return array("number", round($value), $arg[2]);
1023
- }
1024
-
1025
- protected function lib_unit($arg) {
1026
- if ($arg[0] == "list") {
1027
- list($number, $newUnit) = $arg[2];
1028
- return array("number", $this->assertNumber($number),
1029
- $this->compileValue($this->lib_unquote($newUnit)));
1030
- } else {
1031
- return array("number", $this->assertNumber($arg), "");
1032
- }
1033
- }
1034
-
1035
- /**
1036
- * Helper function to get arguments for color manipulation functions.
1037
- * takes a list that contains a color like thing and a percentage
1038
- */
1039
- protected function colorArgs($args) {
1040
- if ($args[0] != 'list' || count($args[2]) < 2) {
1041
- return array(array('color', 0, 0, 0), 0);
1042
- }
1043
- list($color, $delta) = $args[2];
1044
- $color = $this->assertColor($color);
1045
- $delta = floatval($delta[1]);
1046
-
1047
- return array($color, $delta);
1048
- }
1049
-
1050
- protected function lib_darken($args) {
1051
- list($color, $delta) = $this->colorArgs($args);
1052
-
1053
- $hsl = $this->toHSL($color);
1054
- $hsl[3] = $this->clamp($hsl[3] - $delta, 100);
1055
- return $this->toRGB($hsl);
1056
- }
1057
-
1058
- protected function lib_lighten($args) {
1059
- list($color, $delta) = $this->colorArgs($args);
1060
-
1061
- $hsl = $this->toHSL($color);
1062
- $hsl[3] = $this->clamp($hsl[3] + $delta, 100);
1063
- return $this->toRGB($hsl);
1064
- }
1065
-
1066
- protected function lib_saturate($args) {
1067
- list($color, $delta) = $this->colorArgs($args);
1068
-
1069
- $hsl = $this->toHSL($color);
1070
- $hsl[2] = $this->clamp($hsl[2] + $delta, 100);
1071
- return $this->toRGB($hsl);
1072
- }
1073
-
1074
- protected function lib_desaturate($args) {
1075
- list($color, $delta) = $this->colorArgs($args);
1076
-
1077
- $hsl = $this->toHSL($color);
1078
- $hsl[2] = $this->clamp($hsl[2] - $delta, 100);
1079
- return $this->toRGB($hsl);
1080
- }
1081
-
1082
- protected function lib_spin($args) {
1083
- list($color, $delta) = $this->colorArgs($args);
1084
-
1085
- $hsl = $this->toHSL($color);
1086
-
1087
- $hsl[1] = $hsl[1] + $delta % 360;
1088
- if ($hsl[1] < 0) $hsl[1] += 360;
1089
-
1090
- return $this->toRGB($hsl);
1091
- }
1092
-
1093
- protected function lib_fadeout($args) {
1094
- list($color, $delta) = $this->colorArgs($args);
1095
- $color[4] = $this->clamp((isset($color[4]) ? $color[4] : 1) - $delta/100);
1096
- return $color;
1097
- }
1098
-
1099
- protected function lib_fadein($args) {
1100
- list($color, $delta) = $this->colorArgs($args);
1101
- $color[4] = $this->clamp((isset($color[4]) ? $color[4] : 1) + $delta/100);
1102
- return $color;
1103
- }
1104
-
1105
- protected function lib_hue($color) {
1106
- $hsl = $this->toHSL($this->assertColor($color));
1107
- return round($hsl[1]);
1108
- }
1109
-
1110
- protected function lib_saturation($color) {
1111
- $hsl = $this->toHSL($this->assertColor($color));
1112
- return round($hsl[2]);
1113
- }
1114
-
1115
- protected function lib_lightness($color) {
1116
- $hsl = $this->toHSL($this->assertColor($color));
1117
- return round($hsl[3]);
1118
- }
1119
-
1120
- // get the alpha of a color
1121
- // defaults to 1 for non-colors or colors without an alpha
1122
- protected function lib_alpha($value) {
1123
- if (!is_null($color = $this->coerceColor($value))) {
1124
- return isset($color[4]) ? $color[4] : 1;
1125
- }
1126
- }
1127
-
1128
- // set the alpha of the color
1129
- protected function lib_fade($args) {
1130
- list($color, $alpha) = $this->colorArgs($args);
1131
- $color[4] = $this->clamp($alpha / 100.0);
1132
- return $color;
1133
- }
1134
-
1135
- protected function lib_percentage($arg) {
1136
- $num = $this->assertNumber($arg);
1137
- return array("number", $num*100, "%");
1138
- }
1139
-
1140
- // mixes two colors by weight
1141
- // mix(@color1, @color2, [@weight: 50%]);
1142
- // http://sass-lang.com/docs/yardoc/Sass/Script/Functions.html#mix-instance_method
1143
- protected function lib_mix($args) {
1144
- if ($args[0] != "list" || count($args[2]) < 2)
1145
- $this->throwError("mix expects (color1, color2, weight)");
1146
-
1147
- list($first, $second) = $args[2];
1148
- $first = $this->assertColor($first);
1149
- $second = $this->assertColor($second);
1150
-
1151
- $first_a = $this->lib_alpha($first);
1152
- $second_a = $this->lib_alpha($second);
1153
-
1154
- if (isset($args[2][2])) {
1155
- $weight = $args[2][2][1] / 100.0;
1156
- } else {
1157
- $weight = 0.5;
1158
- }
1159
-
1160
- $w = $weight * 2 - 1;
1161
- $a = $first_a - $second_a;
1162
-
1163
- $w1 = (($w * $a == -1 ? $w : ($w + $a)/(1 + $w * $a)) + 1) / 2.0;
1164
- $w2 = 1.0 - $w1;
1165
-
1166
- $new = array('color',
1167
- $w1 * $first[1] + $w2 * $second[1],
1168
- $w1 * $first[2] + $w2 * $second[2],
1169
- $w1 * $first[3] + $w2 * $second[3],
1170
- );
1171
-
1172
- if ($first_a != 1.0 || $second_a != 1.0) {
1173
- $new[] = $first_a * $weight + $second_a * ($weight - 1);
1174
- }
1175
-
1176
- return $this->fixColor($new);
1177
- }
1178
-
1179
- protected function lib_contrast($args) {
1180
- if ($args[0] != 'list' || count($args[2]) < 3) {
1181
- return array(array('color', 0, 0, 0), 0);
1182
- }
1183
-
1184
- list($inputColor, $darkColor, $lightColor) = $args[2];
1185
-
1186
- $inputColor = $this->assertColor($inputColor);
1187
- $darkColor = $this->assertColor($darkColor);
1188
- $lightColor = $this->assertColor($lightColor);
1189
- $hsl = $this->toHSL($inputColor);
1190
-
1191
- if ($hsl[3] > 50) {
1192
- return $darkColor;
1193
- }
1194
-
1195
- return $lightColor;
1196
- }
1197
-
1198
- protected function assertColor($value, $error = "expected color value") {
1199
- $color = $this->coerceColor($value);
1200
- if (is_null($color)) $this->throwError($error);
1201
- return $color;
1202
- }
1203
-
1204
- protected function assertNumber($value, $error = "expecting number") {
1205
- if ($value[0] == "number") return $value[1];
1206
- $this->throwError($error);
1207
- }
1208
-
1209
- protected function assertArgs($value, $expectedArgs, $name="") {
1210
- if ($expectedArgs == 1) {
1211
- return $value;
1212
- } else {
1213
- if ($value[0] !== "list" || $value[1] != ",") $this->throwError("expecting list");
1214
- $values = $value[2];
1215
- $numValues = count($values);
1216
- if ($expectedArgs != $numValues) {
1217
- if ($name) {
1218
- $name = $name . ": ";
1219
- }
1220
-
1221
- $this->throwError("${name}expecting $expectedArgs arguments, got $numValues");
1222
- }
1223
-
1224
- return $values;
1225
- }
1226
- }
1227
-
1228
- protected function toHSL($color) {
1229
- if ($color[0] == 'hsl') return $color;
1230
-
1231
- $r = $color[1] / 255;
1232
- $g = $color[2] / 255;
1233
- $b = $color[3] / 255;
1234
-
1235
- $min = min($r, $g, $b);
1236
- $max = max($r, $g, $b);
1237
-
1238
- $L = ($min + $max) / 2;
1239
- if ($min == $max) {
1240
- $S = $H = 0;
1241
- } else {
1242
- if ($L < 0.5)
1243
- $S = ($max - $min)/($max + $min);
1244
- else
1245
- $S = ($max - $min)/(2.0 - $max - $min);
1246
-
1247
- if ($r == $max) $H = ($g - $b)/($max - $min);
1248
- elseif ($g == $max) $H = 2.0 + ($b - $r)/($max - $min);
1249
- elseif ($b == $max) $H = 4.0 + ($r - $g)/($max - $min);
1250
-
1251
- }
1252
-
1253
- $out = array('hsl',
1254
- ($H < 0 ? $H + 6 : $H)*60,
1255
- $S*100,
1256
- $L*100,
1257
- );
1258
-
1259
- if (count($color) > 4) $out[] = $color[4]; // copy alpha
1260
- return $out;
1261
- }
1262
-
1263
- protected function toRGB_helper($comp, $temp1, $temp2) {
1264
- if ($comp < 0) $comp += 1.0;
1265
- elseif ($comp > 1) $comp -= 1.0;
1266
-
1267
- if (6 * $comp < 1) return $temp1 + ($temp2 - $temp1) * 6 * $comp;
1268
- if (2 * $comp < 1) return $temp2;
1269
- if (3 * $comp < 2) return $temp1 + ($temp2 - $temp1)*((2/3) - $comp) * 6;
1270
-
1271
- return $temp1;
1272
- }
1273
-
1274
- /**
1275
- * Converts a hsl array into a color value in rgb.
1276
- * Expects H to be in range of 0 to 360, S and L in 0 to 100
1277
- */
1278
- protected function toRGB($color) {
1279
- if ($color[0] == 'color') return $color;
1280
-
1281
- $H = $color[1] / 360;
1282
- $S = $color[2] / 100;
1283
- $L = $color[3] / 100;
1284
-
1285
- if ($S == 0) {
1286
- $r = $g = $b = $L;
1287
- } else {
1288
- $temp2 = $L < 0.5 ?
1289
- $L*(1.0 + $S) :
1290
- $L + $S - $L * $S;
1291
-
1292
- $temp1 = 2.0 * $L - $temp2;
1293
-
1294
- $r = $this->toRGB_helper($H + 1/3, $temp1, $temp2);
1295
- $g = $this->toRGB_helper($H, $temp1, $temp2);
1296
- $b = $this->toRGB_helper($H - 1/3, $temp1, $temp2);
1297
- }
1298
-
1299
- // $out = array('color', round($r*255), round($g*255), round($b*255));
1300
- $out = array('color', $r*255, $g*255, $b*255);
1301
- if (count($color) > 4) $out[] = $color[4]; // copy alpha
1302
- return $out;
1303
- }
1304
-
1305
- protected function clamp($v, $max = 1, $min = 0) {
1306
- return min($max, max($min, $v));
1307
- }
1308
-
1309
- /**
1310
- * Convert the rgb, rgba, hsl color literals of function type
1311
- * as returned by the parser into values of color type.
1312
- */
1313
- protected function funcToColor($func) {
1314
- $fname = $func[1];
1315
- if ($func[2][0] != 'list') return false; // need a list of arguments
1316
- $rawComponents = $func[2][2];
1317
-
1318
- if ($fname == 'hsl' || $fname == 'hsla') {
1319
- $hsl = array('hsl');
1320
- $i = 0;
1321
- foreach ($rawComponents as $c) {
1322
- $val = $this->reduce($c);
1323
- $val = isset($val[1]) ? floatval($val[1]) : 0;
1324
-
1325
- if ($i == 0) $clamp = 360;
1326
- elseif ($i < 3) $clamp = 100;
1327
- else $clamp = 1;
1328
-
1329
- $hsl[] = $this->clamp($val, $clamp);
1330
- $i++;
1331
- }
1332
-
1333
- while (count($hsl) < 4) $hsl[] = 0;
1334
- return $this->toRGB($hsl);
1335
-
1336
- } elseif ($fname == 'rgb' || $fname == 'rgba') {
1337
- $components = array();
1338
- $i = 1;
1339
- foreach ($rawComponents as $c) {
1340
- $c = $this->reduce($c);
1341
- if ($i < 4) {
1342
- if ($c[0] == "number" && $c[2] == "%") {
1343
- $components[] = 255 * ($c[1] / 100);
1344
- } else {
1345
- $components[] = floatval($c[1]);
1346
- }
1347
- } elseif ($i == 4) {
1348
- if ($c[0] == "number" && $c[2] == "%") {
1349
- $components[] = 1.0 * ($c[1] / 100);
1350
- } else {
1351
- $components[] = floatval($c[1]);
1352
- }
1353
- } else break;
1354
-
1355
- $i++;
1356
- }
1357
- while (count($components) < 3) $components[] = 0;
1358
- array_unshift($components, 'color');
1359
- return $this->fixColor($components);
1360
- }
1361
-
1362
- return false;
1363
- }
1364
-
1365
- protected function reduce($value, $forExpression = false) {
1366
- switch ($value[0]) {
1367
- case "interpolate":
1368
- $reduced = $this->reduce($value[1]);
1369
- $var = $this->compileValue($reduced);
1370
- $res = $this->reduce(array("variable", $this->vPrefix . $var));
1371
-
1372
- if ($res[0] == "raw_color") {
1373
- $res = $this->coerceColor($res);
1374
- }
1375
-
1376
- if (empty($value[2])) $res = $this->lib_unquote($res);
1377
-
1378
- return $res;
1379
- case "variable":
1380
- $key = $value[1];
1381
- if (is_array($key)) {
1382
- $key = $this->reduce($key);
1383
- $key = $this->vPrefix . $this->compileValue($this->lib_unquote($key));
1384
- }
1385
-
1386
- $seen =& $this->env->seenNames;
1387
-
1388
- if (!empty($seen[$key])) {
1389
- $this->throwError("infinite loop detected: $key");
1390
- }
1391
-
1392
- $seen[$key] = true;
1393
- $out = $this->reduce($this->get($key, self::$defaultValue));
1394
- $seen[$key] = false;
1395
- return $out;
1396
- case "list":
1397
- foreach ($value[2] as &$item) {
1398
- $item = $this->reduce($item, $forExpression);
1399
- }
1400
- return $value;
1401
- case "expression":
1402
- return $this->evaluate($value);
1403
- case "string":
1404
- foreach ($value[2] as &$part) {
1405
- if (is_array($part)) {
1406
- $strip = $part[0] == "variable";
1407
- $part = $this->reduce($part);
1408
- if ($strip) $part = $this->lib_unquote($part);
1409
- }
1410
- }
1411
- return $value;
1412
- case "escape":
1413
- list(,$inner) = $value;
1414
- return $this->lib_unquote($this->reduce($inner));
1415
- case "function":
1416
- $color = $this->funcToColor($value);
1417
- if ($color) return $color;
1418
-
1419
- list(, $name, $args) = $value;
1420
- if ($name == "%") $name = "_sprintf";
1421
- $f = isset($this->libFunctions[$name]) ?
1422
- $this->libFunctions[$name] : array($this, 'lib_'.$name);
1423
-
1424
- if (is_callable($f)) {
1425
- if ($args[0] == 'list')
1426
- $args = self::compressList($args[2], $args[1]);
1427
-
1428
- $ret = call_user_func($f, $this->reduce($args, true), $this);
1429
-
1430
- if (is_null($ret)) {
1431
- return array("string", "", array(
1432
- $name, "(", $args, ")"
1433
- ));
1434
- }
1435
-
1436
- // convert to a typed value if the result is a php primitive
1437
- if (is_numeric($ret)) $ret = array('number', $ret, "");
1438
- elseif (!is_array($ret)) $ret = array('keyword', $ret);
1439
-
1440
- return $ret;
1441
- }
1442
-
1443
- // plain function, reduce args
1444
- $value[2] = $this->reduce($value[2]);
1445
- return $value;
1446
- case "unary":
1447
- list(, $op, $exp) = $value;
1448
- $exp = $this->reduce($exp);
1449
-
1450
- if ($exp[0] == "number") {
1451
- switch ($op) {
1452
- case "+":
1453
- return $exp;
1454
- case "-":
1455
- $exp[1] *= -1;
1456
- return $exp;
1457
- }
1458
- }
1459
- return array("string", "", array($op, $exp));
1460
- }
1461
-
1462
- if ($forExpression) {
1463
- switch ($value[0]) {
1464
- case "keyword":
1465
- if ($color = $this->coerceColor($value)) {
1466
- return $color;
1467
- }
1468
- break;
1469
- case "raw_color":
1470
- return $this->coerceColor($value);
1471
- }
1472
- }
1473
-
1474
- return $value;
1475
- }
1476
-
1477
-
1478
- // coerce a value for use in color operation
1479
- protected function coerceColor($value) {
1480
- switch($value[0]) {
1481
- case 'color': return $value;
1482
- case 'raw_color':
1483
- $c = array("color", 0, 0, 0);
1484
- $colorStr = substr($value[1], 1);
1485
- $num = hexdec($colorStr);
1486
- $width = strlen($colorStr) == 3 ? 16 : 256;
1487
-
1488
- for ($i = 3; $i > 0; $i--) { // 3 2 1
1489
- $t = $num % $width;
1490
- $num /= $width;
1491
-
1492
- $c[$i] = $t * (256/$width) + $t * floor(16/$width);
1493
- }
1494
-
1495
- return $c;
1496
- case 'keyword':
1497
- $name = $value[1];
1498
- if (isset(self::$cssColors[$name])) {
1499
- $rgba = explode(',', self::$cssColors[$name]);
1500
-
1501
- if(isset($rgba[3]))
1502
- return array('color', $rgba[0], $rgba[1], $rgba[2], $rgba[3]);
1503
-
1504
- return array('color', $rgba[0], $rgba[1], $rgba[2]);
1505
- }
1506
- return null;
1507
- }
1508
- }
1509
-
1510
- // make something string like into a string
1511
- protected function coerceString($value) {
1512
- switch ($value[0]) {
1513
- case "string":
1514
- return $value;
1515
- case "keyword":
1516
- return array("string", "", array($value[1]));
1517
- }
1518
- return null;
1519
- }
1520
-
1521
- // turn list of length 1 into value type
1522
- protected function flattenList($value) {
1523
- if ($value[0] == "list" && count($value[2]) == 1) {
1524
- return $this->flattenList($value[2][0]);
1525
- }
1526
- return $value;
1527
- }
1528
-
1529
- protected function toBool($a) {
1530
- if ($a) return self::$TRUE;
1531
- else return self::$FALSE;
1532
- }
1533
-
1534
- // evaluate an expression
1535
- protected function evaluate($exp) {
1536
- list(, $op, $left, $right, $whiteBefore, $whiteAfter) = $exp;
1537
-
1538
- $left = $this->reduce($left, true);
1539
- $right = $this->reduce($right, true);
1540
-
1541
- if ($leftColor = $this->coerceColor($left)) {
1542
- $left = $leftColor;
1543
- }
1544
-
1545
- if ($rightColor = $this->coerceColor($right)) {
1546
- $right = $rightColor;
1547
- }
1548
-
1549
- $ltype = $left[0];
1550
- $rtype = $right[0];
1551
-
1552
- // operators that work on all types
1553
- if ($op == "and") {
1554
- return $this->toBool($left == self::$TRUE && $right == self::$TRUE);
1555
- }
1556
-
1557
- if ($op == "=") {
1558
- return $this->toBool($this->eq($left, $right) );
1559
- }
1560
-
1561
- if ($op == "+" && !is_null($str = $this->stringConcatenate($left, $right))) {
1562
- return $str;
1563
- }
1564
-
1565
- // type based operators
1566
- $fname = "op_${ltype}_${rtype}";
1567
- if (is_callable(array($this, $fname))) {
1568
- $out = $this->$fname($op, $left, $right);
1569
- if (!is_null($out)) return $out;
1570
- }
1571
-
1572
- // make the expression look it did before being parsed
1573
- $paddedOp = $op;
1574
- if ($whiteBefore) $paddedOp = " " . $paddedOp;
1575
- if ($whiteAfter) $paddedOp .= " ";
1576
-
1577
- return array("string", "", array($left, $paddedOp, $right));
1578
- }
1579
-
1580
- protected function stringConcatenate($left, $right) {
1581
- if ($strLeft = $this->coerceString($left)) {
1582
- if ($right[0] == "string") {
1583
- $right[1] = "";
1584
- }
1585
- $strLeft[2][] = $right;
1586
- return $strLeft;
1587
- }
1588
-
1589
- if ($strRight = $this->coerceString($right)) {
1590
- array_unshift($strRight[2], $left);
1591
- return $strRight;
1592
- }
1593
- }
1594
-
1595
-
1596
- // make sure a color's components don't go out of bounds
1597
- protected function fixColor($c) {
1598
- foreach (range(1, 3) as $i) {
1599
- if ($c[$i] < 0) $c[$i] = 0;
1600
- if ($c[$i] > 255) $c[$i] = 255;
1601
- }
1602
-
1603
- return $c;
1604
- }
1605
-
1606
- protected function op_number_color($op, $lft, $rgt) {
1607
- if ($op == '+' || $op == '*') {
1608
- return $this->op_color_number($op, $rgt, $lft);
1609
- }
1610
- }
1611
-
1612
- protected function op_color_number($op, $lft, $rgt) {
1613
- if ($rgt[0] == '%') $rgt[1] /= 100;
1614
-
1615
- return $this->op_color_color($op, $lft,
1616
- array_fill(1, count($lft) - 1, $rgt[1]));
1617
- }
1618
-
1619
- protected function op_color_color($op, $left, $right) {
1620
- $out = array('color');
1621
- $max = count($left) > count($right) ? count($left) : count($right);
1622
- foreach (range(1, $max - 1) as $i) {
1623
- $lval = isset($left[$i]) ? $left[$i] : 0;
1624
- $rval = isset($right[$i]) ? $right[$i] : 0;
1625
- switch ($op) {
1626
- case '+':
1627
- $out[] = $lval + $rval;
1628
- break;
1629
- case '-':
1630
- $out[] = $lval - $rval;
1631
- break;
1632
- case '*':
1633
- $out[] = $lval * $rval;
1634
- break;
1635
- case '%':
1636
- $out[] = $lval % $rval;
1637
- break;
1638
- case '/':
1639
- if ($rval == 0) $this->throwError("evaluate error: can't divide by zero");
1640
- $out[] = $lval / $rval;
1641
- break;
1642
- default:
1643
- $this->throwError('evaluate error: color op number failed on op '.$op);
1644
- }
1645
- }
1646
- return $this->fixColor($out);
1647
- }
1648
-
1649
- function lib_red($color){
1650
- $color = $this->coerceColor($color);
1651
- if (is_null($color)) {
1652
- $this->throwError('color expected for red()');
1653
- }
1654
-
1655
- return $color[1];
1656
- }
1657
-
1658
- function lib_green($color){
1659
- $color = $this->coerceColor($color);
1660
- if (is_null($color)) {
1661
- $this->throwError('color expected for green()');
1662
- }
1663
-
1664
- return $color[2];
1665
- }
1666
-
1667
- function lib_blue($color){
1668
- $color = $this->coerceColor($color);
1669
- if (is_null($color)) {
1670
- $this->throwError('color expected for blue()');
1671
- }
1672
-
1673
- return $color[3];
1674
- }
1675
-
1676
-
1677
- // operator on two numbers
1678
- protected function op_number_number($op, $left, $right) {
1679
- $unit = empty($left[2]) ? $right[2] : $left[2];
1680
-
1681
- $value = 0;
1682
- switch ($op) {
1683
- case '+':
1684
- $value = $left[1] + $right[1];
1685
- break;
1686
- case '*':
1687
- $value = $left[1] * $right[1];
1688
- break;
1689
- case '-':
1690
- $value = $left[1] - $right[1];
1691
- break;
1692
- case '%':
1693
- $value = $left[1] % $right[1];
1694
- break;
1695
- case '/':
1696
- if ($right[1] == 0) $this->throwError('parse error: divide by zero');
1697
- $value = $left[1] / $right[1];
1698
- break;
1699
- case '<':
1700
- return $this->toBool($left[1] < $right[1]);
1701
- case '>':
1702
- return $this->toBool($left[1] > $right[1]);
1703
- case '>=':
1704
- return $this->toBool($left[1] >= $right[1]);
1705
- case '=<':
1706
- return $this->toBool($left[1] <= $right[1]);
1707
- default:
1708
- $this->throwError('parse error: unknown number operator: '.$op);
1709
- }
1710
-
1711
- return array("number", $value, $unit);
1712
- }
1713
-
1714
-
1715
- /* environment functions */
1716
-
1717
- protected function makeOutputBlock($type, $selectors = null) {
1718
- $b = new stdclass;
1719
- $b->lines = array();
1720
- $b->children = array();
1721
- $b->selectors = $selectors;
1722
- $b->type = $type;
1723
- $b->parent = $this->scope;
1724
- return $b;
1725
- }
1726
-
1727
- // the state of execution
1728
- protected function pushEnv($block = null) {
1729
- $e = new stdclass;
1730
- $e->parent = $this->env;
1731
- $e->store = array();
1732
- $e->block = $block;
1733
-
1734
- $this->env = $e;
1735
- return $e;
1736
- }
1737
-
1738
- // pop something off the stack
1739
- protected function popEnv() {
1740
- $old = $this->env;
1741
- $this->env = $this->env->parent;
1742
- return $old;
1743
- }
1744
-
1745
- // set something in the current env
1746
- protected function set($name, $value) {
1747
- $this->env->store[$name] = $value;
1748
- }
1749
-
1750
-
1751
- // get the highest occurrence entry for a name
1752
- protected function get($name, $default=null) {
1753
- $current = $this->env;
1754
-
1755
- $isArguments = $name == $this->vPrefix . 'arguments';
1756
- while ($current) {
1757
- if ($isArguments && isset($current->arguments)) {
1758
- return array('list', ' ', $current->arguments);
1759
- }
1760
-
1761
- if (isset($current->store[$name]))
1762
- return $current->store[$name];
1763
- else {
1764
- $current = isset($current->storeParent) ?
1765
- $current->storeParent : $current->parent;
1766
- }
1767
- }
1768
-
1769
- return $default;
1770
- }
1771
-
1772
- // inject array of unparsed strings into environment as variables
1773
- protected function injectVariables($args) {
1774
- $this->pushEnv();
1775
- $parser = new lessc_parser($this, __METHOD__);
1776
- foreach ($args as $name => $strValue) {
1777
- if ($name{0} != '@') $name = '@'.$name;
1778
- $parser->count = 0;
1779
- $parser->buffer = (string)$strValue;
1780
- if (!$parser->propertyValue($value)) {
1781
- throw new Exception("failed to parse passed in variable $name: $strValue");
1782
- }
1783
-
1784
- $this->set($name, $value);
1785
- }
1786
- }
1787
-
1788
- /**
1789
- * Initialize any static state, can initialize parser for a file
1790
- * $opts isn't used yet
1791
- */
1792
- public function __construct($fname = null) {
1793
- if ($fname !== null) {
1794
- // used for deprecated parse method
1795
- $this->_parseFile = $fname;
1796
- }
1797
- }
1798
-
1799
- public function compile($string, $name = null) {
1800
- $locale = setlocale(LC_NUMERIC, 0);
1801
- setlocale(LC_NUMERIC, "C");
1802
-
1803
- $this->parser = $this->makeParser($name);
1804
- $root = $this->parser->parse($string);
1805
-
1806
- $this->env = null;
1807
- $this->scope = null;
1808
-
1809
- $this->formatter = $this->newFormatter();
1810
-
1811
- if (!empty($this->registeredVars)) {
1812
- $this->injectVariables($this->registeredVars);
1813
- }
1814
-
1815
- $this->sourceParser = $this->parser; // used for error messages
1816
- $this->compileBlock($root);
1817
-
1818
- ob_start();
1819
- $this->formatter->block($this->scope);
1820
- $out = ob_get_clean();
1821
- setlocale(LC_NUMERIC, $locale);
1822
- return $out;
1823
- }
1824
-
1825
- public function compileFile($fname, $outFname = null) {
1826
- if (!is_readable($fname)) {
1827
- throw new Exception('load error: failed to find '.$fname);
1828
- }
1829
-
1830
- $pi = pathinfo($fname);
1831
-
1832
- $oldImport = $this->importDir;
1833
-
1834
- $this->importDir = (array)$this->importDir;
1835
- $this->importDir[] = $pi['dirname'].'/';
1836
-
1837
- $this->addParsedFile($fname);
1838
-
1839
- $out = $this->compile($GLOBALS['wp_filesystem']->get_contents($fname), $fname);
1840
-
1841
- $this->importDir = $oldImport;
1842
-
1843
- if ($outFname !== null) {
1844
- return $GLOBALS['wp_filesystem']->put_contents($outFname, $out);
1845
- }
1846
-
1847
- return $out;
1848
- }
1849
-
1850
- // compile only if changed input has changed or output doesn't exist
1851
- public function checkedCompile($in, $out) {
1852
- if (!is_file($out) || filemtime($in) > filemtime($out)) {
1853
- $this->compileFile($in, $out);
1854
- return true;
1855
- }
1856
- return false;
1857
- }
1858
-
1859
- /**
1860
- * Execute lessphp on a .less file or a lessphp cache structure
1861
- *
1862
- * The lessphp cache structure contains information about a specific
1863
- * less file having been parsed. It can be used as a hint for future
1864
- * calls to determine whether or not a rebuild is required.
1865
- *
1866
- * The cache structure contains two important keys that may be used
1867
- * externally:
1868
- *
1869
- * compiled: The final compiled CSS
1870
- * updated: The time (in seconds) the CSS was last compiled
1871
- *
1872
- * The cache structure is a plain-ol' PHP associative array and can
1873
- * be serialized and unserialized without a hitch.
1874
- *
1875
- * @param mixed $in Input
1876
- * @param bool $force Force rebuild?
1877
- * @return array lessphp cache structure
1878
- */
1879
- public function cachedCompile($in, $force = false) {
1880
- // assume no root
1881
- $root = null;
1882
-
1883
- if (is_string($in)) {
1884
- $root = $in;
1885
- } elseif (is_array($in) and isset($in['root'])) {
1886
- if ($force or ! isset($in['files'])) {
1887
- // If we are forcing a recompile or if for some reason the
1888
- // structure does not contain any file information we should
1889
- // specify the root to trigger a rebuild.
1890
- $root = $in['root'];
1891
- } elseif (isset($in['files']) and is_array($in['files'])) {
1892
- foreach ($in['files'] as $fname => $ftime ) {
1893
- if (!file_exists($fname) or filemtime($fname) > $ftime) {
1894
- // One of the files we knew about previously has changed
1895
- // so we should look at our incoming root again.
1896
- $root = $in['root'];
1897
- break;
1898
- }
1899
- }
1900
- }
1901
- } else {
1902
- // TODO: Throw an exception? We got neither a string nor something
1903
- // that looks like a compatible lessphp cache structure.
1904
- return null;
1905
- }
1906
-
1907
- if ($root !== null) {
1908
- // If we have a root value which means we should rebuild.
1909
- $out = array();
1910
- $out['root'] = $root;
1911
- $out['compiled'] = $this->compileFile($root);
1912
- $out['files'] = $this->allParsedFiles();
1913
- $out['updated'] = time();
1914
- return $out;
1915
- } else {
1916
- // No changes, pass back the structure
1917
- // we were given initially.
1918
- return $in;
1919
- }
1920
-
1921
- }
1922
-
1923
- // parse and compile buffer
1924
- // This is deprecated
1925
- public function parse($str = null, $initialVariables = null) {
1926
- if (is_array($str)) {
1927
- $initialVariables = $str;
1928
- $str = null;
1929
- }
1930
-
1931
- $oldVars = $this->registeredVars;
1932
- if ($initialVariables !== null) {
1933
- $this->setVariables($initialVariables);
1934
- }
1935
-
1936
- if ($str == null) {
1937
- if (empty($this->_parseFile)) {
1938
- throw new exception("nothing to parse");
1939
- }
1940
-
1941
- $out = $this->compileFile($this->_parseFile);
1942
- } else {
1943
- $out = $this->compile($str);
1944
- }
1945
-
1946
- $this->registeredVars = $oldVars;
1947
- return $out;
1948
- }
1949
-
1950
- protected function makeParser($name) {
1951
- $parser = new lessc_parser($this, $name);
1952
- $parser->writeComments = $this->preserveComments;
1953
-
1954
- return $parser;
1955
- }
1956
-
1957
- public function setFormatter($name) {
1958
- $this->formatterName = $name;
1959
- }
1960
-
1961
- protected function newFormatter() {
1962
- $className = "lessc_formatter_lessjs";
1963
- if (!empty($this->formatterName)) {
1964
- if (!is_string($this->formatterName))
1965
- return $this->formatterName;
1966
- $className = "lessc_formatter_$this->formatterName";
1967
- }
1968
-
1969
- return new $className;
1970
- }
1971
-
1972
- public function setPreserveComments($preserve) {
1973
- $this->preserveComments = $preserve;
1974
- }
1975
-
1976
- public function registerFunction($name, $func) {
1977
- $this->libFunctions[$name] = $func;
1978
- }
1979
-
1980
- public function unregisterFunction($name) {
1981
- unset($this->libFunctions[$name]);
1982
- }
1983
-
1984
- public function setVariables($variables) {
1985
- $this->registeredVars = array_merge($this->registeredVars, $variables);
1986
- }
1987
-
1988
- public function unsetVariable($name) {
1989
- unset($this->registeredVars[$name]);
1990
- }
1991
-
1992
- public function setImportDir($dirs) {
1993
- $this->importDir = (array)$dirs;
1994
- }
1995
-
1996
- public function addImportDir($dir) {
1997
- $this->importDir = (array)$this->importDir;
1998
- $this->importDir[] = $dir;
1999
- }
2000
-
2001
- public function allParsedFiles() {
2002
- return $this->allParsedFiles;
2003
- }
2004
-
2005
- protected function addParsedFile($file) {
2006
- $this->allParsedFiles[realpath($file)] = filemtime($file);
2007
- }
2008
-
2009
- /**
2010
- * Uses the current value of $this->count to show line and line number
2011
- */
2012
- protected function throwError($msg = null) {
2013
- if ($this->sourceLoc >= 0) {
2014
- $this->sourceParser->throwError($msg, $this->sourceLoc);
2015
- }
2016
- throw new exception($msg);
2017
- }
2018
-
2019
- // compile file $in to file $out if $in is newer than $out
2020
- // returns true when it compiles, false otherwise
2021
- public static function ccompile($in, $out, $less = null) {
2022
- if ($less === null) {
2023
- $less = new self;
2024
- }
2025
- return $less->checkedCompile($in, $out);
2026
- }
2027
-
2028
- public static function cexecute($in, $force = false, $less = null) {
2029
- if ($less === null) {
2030
- $less = new self;
2031
- }
2032
- return $less->cachedCompile($in, $force);
2033
- }
2034
-
2035
- static protected $cssColors = array(
2036
- 'aliceblue' => '240,248,255',
2037
- 'antiquewhite' => '250,235,215',
2038
- 'aqua' => '0,255,255',
2039
- 'aquamarine' => '127,255,212',
2040
- 'azure' => '240,255,255',
2041
- 'beige' => '245,245,220',
2042
- 'bisque' => '255,228,196',
2043
- 'black' => '0,0,0',
2044
- 'blanchedalmond' => '255,235,205',
2045
- 'blue' => '0,0,255',
2046
- 'blueviolet' => '138,43,226',
2047
- 'brown' => '165,42,42',
2048
- 'burlywood' => '222,184,135',
2049
- 'cadetblue' => '95,158,160',
2050
- 'chartreuse' => '127,255,0',
2051
- 'chocolate' => '210,105,30',
2052
- 'coral' => '255,127,80',
2053
- 'cornflowerblue' => '100,149,237',
2054
- 'cornsilk' => '255,248,220',
2055
- 'crimson' => '220,20,60',
2056
- 'cyan' => '0,255,255',
2057
- 'darkblue' => '0,0,139',
2058
- 'darkcyan' => '0,139,139',
2059
- 'darkgoldenrod' => '184,134,11',
2060
- 'darkgray' => '169,169,169',
2061
- 'darkgreen' => '0,100,0',
2062
- 'darkgrey' => '169,169,169',
2063
- 'darkkhaki' => '189,183,107',
2064
- 'darkmagenta' => '139,0,139',
2065
- 'darkolivegreen' => '85,107,47',
2066
- 'darkorange' => '255,140,0',
2067
- 'darkorchid' => '153,50,204',
2068
- 'darkred' => '139,0,0',
2069
- 'darksalmon' => '233,150,122',
2070
- 'darkseagreen' => '143,188,143',
2071
- 'darkslateblue' => '72,61,139',
2072
- 'darkslategray' => '47,79,79',
2073
- 'darkslategrey' => '47,79,79',
2074
- 'darkturquoise' => '0,206,209',
2075
- 'darkviolet' => '148,0,211',
2076
- 'deeppink' => '255,20,147',
2077
- 'deepskyblue' => '0,191,255',
2078
- 'dimgray' => '105,105,105',
2079
- 'dimgrey' => '105,105,105',
2080
- 'dodgerblue' => '30,144,255',
2081
- 'firebrick' => '178,34,34',
2082
- 'floralwhite' => '255,250,240',
2083
- 'forestgreen' => '34,139,34',
2084
- 'fuchsia' => '255,0,255',
2085
- 'gainsboro' => '220,220,220',
2086
- 'ghostwhite' => '248,248,255',
2087
- 'gold' => '255,215,0',
2088
- 'goldenrod' => '218,165,32',
2089
- 'gray' => '128,128,128',
2090
- 'green' => '0,128,0',
2091
- 'greenyellow' => '173,255,47',
2092
- 'grey' => '128,128,128',
2093
- 'honeydew' => '240,255,240',
2094
- 'hotpink' => '255,105,180',
2095
- 'indianred' => '205,92,92',
2096
- 'indigo' => '75,0,130',
2097
- 'ivory' => '255,255,240',
2098
- 'khaki' => '240,230,140',
2099
- 'lavender' => '230,230,250',
2100
- 'lavenderblush' => '255,240,245',
2101
- 'lawngreen' => '124,252,0',
2102
- 'lemonchiffon' => '255,250,205',
2103
- 'lightblue' => '173,216,230',
2104
- 'lightcoral' => '240,128,128',
2105
- 'lightcyan' => '224,255,255',
2106
- 'lightgoldenrodyellow' => '250,250,210',
2107
- 'lightgray' => '211,211,211',
2108
- 'lightgreen' => '144,238,144',
2109
- 'lightgrey' => '211,211,211',
2110
- 'lightpink' => '255,182,193',
2111
- 'lightsalmon' => '255,160,122',
2112
- 'lightseagreen' => '32,178,170',
2113
- 'lightskyblue' => '135,206,250',
2114
- 'lightslategray' => '119,136,153',
2115
- 'lightslategrey' => '119,136,153',
2116
- 'lightsteelblue' => '176,196,222',
2117
- 'lightyellow' => '255,255,224',
2118
- 'lime' => '0,255,0',
2119
- 'limegreen' => '50,205,50',
2120
- 'linen' => '250,240,230',
2121
- 'magenta' => '255,0,255',
2122
- 'maroon' => '128,0,0',
2123
- 'mediumaquamarine' => '102,205,170',
2124
- 'mediumblue' => '0,0,205',
2125
- 'mediumorchid' => '186,85,211',
2126
- 'mediumpurple' => '147,112,219',
2127
- 'mediumseagreen' => '60,179,113',
2128
- 'mediumslateblue' => '123,104,238',
2129
- 'mediumspringgreen' => '0,250,154',
2130
- 'mediumturquoise' => '72,209,204',
2131
- 'mediumvioletred' => '199,21,133',
2132
- 'midnightblue' => '25,25,112',
2133
- 'mintcream' => '245,255,250',
2134
- 'mistyrose' => '255,228,225',
2135
- 'moccasin' => '255,228,181',
2136
- 'navajowhite' => '255,222,173',
2137
- 'navy' => '0,0,128',
2138
- 'oldlace' => '253,245,230',
2139
- 'olive' => '128,128,0',
2140
- 'olivedrab' => '107,142,35',
2141
- 'orange' => '255,165,0',
2142
- 'orangered' => '255,69,0',
2143
- 'orchid' => '218,112,214',
2144
- 'palegoldenrod' => '238,232,170',
2145
- 'palegreen' => '152,251,152',
2146
- 'paleturquoise' => '175,238,238',
2147
- 'palevioletred' => '219,112,147',
2148
- 'papayawhip' => '255,239,213',
2149
- 'peachpuff' => '255,218,185',
2150
- 'peru' => '205,133,63',
2151
- 'pink' => '255,192,203',
2152
- 'plum' => '221,160,221',
2153
- 'powderblue' => '176,224,230',
2154
- 'purple' => '128,0,128',
2155
- 'red' => '255,0,0',
2156
- 'rosybrown' => '188,143,143',
2157
- 'royalblue' => '65,105,225',
2158
- 'saddlebrown' => '139,69,19',
2159
- 'salmon' => '250,128,114',
2160
- 'sandybrown' => '244,164,96',
2161
- 'seagreen' => '46,139,87',
2162
- 'seashell' => '255,245,238',
2163
- 'sienna' => '160,82,45',
2164
- 'silver' => '192,192,192',
2165
- 'skyblue' => '135,206,235',
2166
- 'slateblue' => '106,90,205',
2167
- 'slategray' => '112,128,144',
2168
- 'slategrey' => '112,128,144',
2169
- 'snow' => '255,250,250',
2170
- 'springgreen' => '0,255,127',
2171
- 'steelblue' => '70,130,180',
2172
- 'tan' => '210,180,140',
2173
- 'teal' => '0,128,128',
2174
- 'thistle' => '216,191,216',
2175
- 'tomato' => '255,99,71',
2176
- 'transparent' => '0,0,0,0',
2177
- 'turquoise' => '64,224,208',
2178
- 'violet' => '238,130,238',
2179
- 'wheat' => '245,222,179',
2180
- 'white' => '255,255,255',
2181
- 'whitesmoke' => '245,245,245',
2182
- 'yellow' => '255,255,0',
2183
- 'yellowgreen' => '154,205,50'
2184
- );
2185
- }
2186
-
2187
- // responsible for taking a string of LESS code and converting it into a
2188
- // syntax tree
2189
- /**
2190
- * @ignore
2191
- */
2192
- class lessc_parser {
2193
- static protected $nextBlockId = 0; // used to uniquely identify blocks
2194
-
2195
- static protected $precedence = array(
2196
- '=<' => 0,
2197
- '>=' => 0,
2198
- '=' => 0,
2199
- '<' => 0,
2200
- '>' => 0,
2201
-
2202
- '+' => 1,
2203
- '-' => 1,
2204
- '*' => 2,
2205
- '/' => 2,
2206
- '%' => 2,
2207
- );
2208
-
2209
- static protected $whitePattern;
2210
- static protected $commentMulti;
2211
-
2212
- static protected $commentSingle = "//";
2213
- static protected $commentMultiLeft = "/*";
2214
- static protected $commentMultiRight = "*/";
2215
-
2216
- // regex string to match any of the operators
2217
- static protected $operatorString;
2218
-
2219
- // these properties will supress division unless it's inside parenthases
2220
- static protected $supressDivisionProps =
2221
- array('/border-radius$/i', '/^font$/i');
2222
-
2223
- protected $blockDirectives = array("font-face", "keyframes", "page", "-moz-document", "viewport", "-moz-viewport", "-o-viewport", "-ms-viewport");
2224
- protected $lineDirectives = array("charset");
2225
-
2226
- /**
2227
- * if we are in parens we can be more liberal with whitespace around
2228
- * operators because it must evaluate to a single value and thus is less
2229
- * ambiguous.
2230
- *
2231
- * Consider:
2232
- * property1: 10 -5; // is two numbers, 10 and -5
2233
- * property2: (10 -5); // should evaluate to 5
2234
- */
2235
- protected $inParens = false;
2236
-
2237
- // caches preg escaped literals
2238
- static protected $literalCache = array();
2239
-
2240
- public function __construct($lessc, $sourceName = null) {
2241
- $this->eatWhiteDefault = true;
2242
- // reference to less needed for vPrefix, mPrefix, and parentSelector
2243
- $this->lessc = $lessc;
2244
-
2245
- $this->sourceName = $sourceName; // name used for error messages
2246
-
2247
- $this->writeComments = false;
2248
-
2249
- if (!self::$operatorString) {
2250
- self::$operatorString =
2251
- '('.implode('|', array_map(array('JupiterX_Lessc', 'preg_quote'),
2252
- array_keys(self::$precedence))).')';
2253
-
2254
- $commentSingle = JupiterX_Lessc::preg_quote(self::$commentSingle);
2255
- $commentMultiLeft = JupiterX_Lessc::preg_quote(self::$commentMultiLeft);
2256
- $commentMultiRight = JupiterX_Lessc::preg_quote(self::$commentMultiRight);
2257
-
2258
- self::$commentMulti = $commentMultiLeft.'.*?'.$commentMultiRight;
2259
- self::$whitePattern = '/'.$commentSingle.'[^\n]*\s*|('.self::$commentMulti.')\s*|\s+/Ais';
2260
- }
2261
- }
2262
-
2263
- public function parse($buffer) {
2264
- $this->count = 0;
2265
- $this->line = 1;
2266
-
2267
- $this->env = null; // block stack
2268
- $this->buffer = $this->writeComments ? $buffer : $this->removeComments($buffer);
2269
- $this->pushSpecialBlock("root");
2270
- $this->eatWhiteDefault = true;
2271
- $this->seenComments = array();
2272
-
2273
- // trim whitespace on head
2274
- // if (preg_match('/^\s+/', $this->buffer, $m)) {
2275
- // $this->line += substr_count($m[0], "\n");
2276
- // $this->buffer = ltrim($this->buffer);
2277
- // }
2278
- $this->whitespace();
2279
-
2280
- // parse the entire file
2281
- $lastCount = $this->count;
2282
- while (false !== $this->parseChunk());
2283
-
2284
- if ($this->count != strlen($this->buffer))
2285
- $this->throwError();
2286
-
2287
- // TODO report where the block was opened
2288
- if (!is_null($this->env->parent))
2289
- throw new exception('parse error: unclosed block');
2290
-
2291
- return $this->env;
2292
- }
2293
-
2294
- /**
2295
- * Parse a single chunk off the head of the buffer and append it to the
2296
- * current parse environment.
2297
- * Returns false when the buffer is empty, or when there is an error.
2298
- *
2299
- * This function is called repeatedly until the entire document is
2300
- * parsed.
2301
- *
2302
- * This parser is most similar to a recursive descent parser. Single
2303
- * functions represent discrete grammatical rules for the language, and
2304
- * they are able to capture the text that represents those rules.
2305
- *
2306
- * Consider the function lessc::keyword(). (all parse functions are
2307
- * structured the same)
2308
- *
2309
- * The function takes a single reference argument. When calling the
2310
- * function it will attempt to match a keyword on the head of the buffer.
2311
- * If it is successful, it will place the keyword in the referenced
2312
- * argument, advance the position in the buffer, and return true. If it
2313
- * fails then it won't advance the buffer and it will return false.
2314
- *
2315
- * All of these parse functions are powered by lessc::match(), which behaves
2316
- * the same way, but takes a literal regular expression. Sometimes it is
2317
- * more convenient to use match instead of creating a new function.
2318
- *
2319
- * Because of the format of the functions, to parse an entire string of
2320
- * grammatical rules, you can chain them together using &&.
2321
- *
2322
- * But, if some of the rules in the chain succeed before one fails, then
2323
- * the buffer position will be left at an invalid state. In order to
2324
- * avoid this, lessc::seek() is used to remember and set buffer positions.
2325
- *
2326
- * Before parsing a chain, use $s = $this->seek() to remember the current
2327
- * position into $s. Then if a chain fails, use $this->seek($s) to
2328
- * go back where we started.
2329
- */
2330
- protected function parseChunk() {
2331
- if (empty($this->buffer)) return false;
2332
- $s = $this->seek();
2333
-
2334
- // setting a property
2335
- if ($this->keyword($key) && $this->assign() &&
2336
- $this->propertyValue($value, $key) && $this->end())
2337
- {
2338
- $this->append(array('assign', $key, $value), $s);
2339
- return true;
2340
- } else {
2341
- $this->seek($s);
2342
- }
2343
-
2344
-
2345
- // look for special css blocks
2346
- if ($this->literal('@', false)) {
2347
- $this->count--;
2348
-
2349
- // media
2350
- if ($this->literal('@media')) {
2351
- if (($this->mediaQueryList($mediaQueries) || true)
2352
- && $this->literal('{'))
2353
- {
2354
- $media = $this->pushSpecialBlock("media");
2355
- $media->queries = is_null($mediaQueries) ? array() : $mediaQueries;
2356
- return true;
2357
- } else {
2358
- $this->seek($s);
2359
- return false;
2360
- }
2361
- }
2362
-
2363
- if ($this->literal("@", false) && $this->keyword($dirName)) {
2364
- if ($this->isDirective($dirName, $this->blockDirectives)) {
2365
- if (($this->openString("{", $dirValue, null, array(";")) || true) &&
2366
- $this->literal("{"))
2367
- {
2368
- $dir = $this->pushSpecialBlock("directive");
2369
- $dir->name = $dirName;
2370
- if (isset($dirValue)) $dir->value = $dirValue;
2371
- return true;
2372
- }
2373
- } elseif ($this->isDirective($dirName, $this->lineDirectives)) {
2374
- if ($this->propertyValue($dirValue) && $this->end()) {
2375
- $this->append(array("directive", $dirName, $dirValue));
2376
- return true;
2377
- }
2378
- }
2379
- }
2380
-
2381
- $this->seek($s);
2382
- }
2383
-
2384
- // setting a variable
2385
- if ($this->variable($var) && $this->assign() &&
2386
- $this->propertyValue($value) && $this->end())
2387
- {
2388
- $this->append(array('assign', $var, $value), $s);
2389
- return true;
2390
- } else {
2391
- $this->seek($s);
2392
- }
2393
-
2394
- if ($this->import($importValue)) {
2395
- $this->append($importValue, $s);
2396
- return true;
2397
- }
2398
-
2399
- // opening parametric mixin
2400
- if ($this->tag($tag, true) && $this->argumentDef($args, $isVararg) &&
2401
- ($this->guards($guards) || true) &&
2402
- $this->literal('{'))
2403
- {
2404
- $block = $this->pushBlock($this->fixTags(array($tag)));
2405
- $block->args = $args;
2406
- $block->isVararg = $isVararg;
2407
- if (!empty($guards)) $block->guards = $guards;
2408
- return true;
2409
- } else {
2410
- $this->seek($s);
2411
- }
2412
-
2413
- // opening a simple block
2414
- if ($this->tags($tags) && $this->literal('{')) {
2415
- $tags = $this->fixTags($tags);
2416
- $this->pushBlock($tags);
2417
- return true;
2418
- } else {
2419
- $this->seek($s);
2420
- }
2421
-
2422
- // closing a block
2423
- if ($this->literal('}', false)) {
2424
- try {
2425
- $block = $this->pop();
2426
- } catch (exception $e) {
2427
- $this->seek($s);
2428
- $this->throwError($e->getMessage());
2429
- }
2430
-
2431
- $hidden = false;
2432
- if (is_null($block->type)) {
2433
- $hidden = true;
2434
- if (!isset($block->args)) {
2435
- foreach ($block->tags as $tag) {
2436
- if (!is_string($tag) || $tag{0} != $this->lessc->mPrefix) {
2437
- $hidden = false;
2438
- break;
2439
- }
2440
- }
2441
- }
2442
-
2443
- foreach ($block->tags as $tag) {
2444
- if (is_string($tag)) {
2445
- $this->env->children[$tag][] = $block;
2446
- }
2447
- }
2448
- }
2449
-
2450
- if (!$hidden) {
2451
- $this->append(array('block', $block), $s);
2452
- }
2453
-
2454
- // this is done here so comments aren't bundled into he block that
2455
- // was just closed
2456
- $this->whitespace();
2457
- return true;
2458
- }
2459
-
2460
- // mixin
2461
- if ($this->mixinTags($tags) &&
2462
- ($this->argumentDef($argv, $isVararg) || true) &&
2463
- ($this->keyword($suffix) || true) && $this->end())
2464
- {
2465
- $tags = $this->fixTags($tags);
2466
- $this->append(array('mixin', $tags, $argv, $suffix), $s);
2467
- return true;
2468
- } else {
2469
- $this->seek($s);
2470
- }
2471
-
2472
- // spare ;
2473
- if ($this->literal(';')) return true;
2474
-
2475
- return false; // got nothing, throw error
2476
- }
2477
-
2478
- protected function isDirective($dirname, $directives) {
2479
- // TODO: cache pattern in parser
2480
- $pattern = implode("|",
2481
- array_map(array("JupiterX_Lessc", "preg_quote"), $directives));
2482
- $pattern = '/^(-[a-z-]+-)?(' . $pattern . ')$/i';
2483
-
2484
- return preg_match($pattern, $dirname);
2485
- }
2486
-
2487
- protected function fixTags($tags) {
2488
- // move @ tags out of variable namespace
2489
- foreach ($tags as &$tag) {
2490
- if ($tag{0} == $this->lessc->vPrefix)
2491
- $tag[0] = $this->lessc->mPrefix;
2492
- }
2493
- return $tags;
2494
- }
2495
-
2496
- // a list of expressions
2497
- protected function expressionList(&$exps) {
2498
- $values = array();
2499
-
2500
- while ($this->expression($exp)) {
2501
- $values[] = $exp;
2502
- }
2503
-
2504
- if (count($values) == 0) return false;
2505
-
2506
- $exps = JupiterX_Lessc::compressList($values, ' ');
2507
- return true;
2508
- }
2509
-
2510
- /**
2511
- * Attempt to consume an expression.
2512
- * @link http://en.wikipedia.org/wiki/Operator-precedence_parser#Pseudo-code
2513
- */
2514
- protected function expression(&$out) {
2515
- if ($this->value($lhs)) {
2516
- $out = $this->expHelper($lhs, 0);
2517
-
2518
- // look for / shorthand
2519
- if (!empty($this->env->supressedDivision)) {
2520
- unset($this->env->supressedDivision);
2521
- $s = $this->seek();
2522
- if ($this->literal("/") && $this->value($rhs)) {
2523
- $out = array("list", "",
2524
- array($out, array("keyword", "/"), $rhs));
2525
- } else {
2526
- $this->seek($s);
2527
- }
2528
- }
2529
-
2530
- return true;
2531
- }
2532
- return false;
2533
- }
2534
-
2535
- /**
2536
- * recursively parse infix equation with $lhs at precedence $minP
2537
- */
2538
- protected function expHelper($lhs, $minP) {
2539
- $this->inExp = true;
2540
- $ss = $this->seek();
2541
-
2542
- while (true) {
2543
- $whiteBefore = isset($this->buffer[$this->count - 1]) &&
2544
- ctype_space($this->buffer[$this->count - 1]);
2545
-
2546
- // If there is whitespace before the operator, then we require
2547
- // whitespace after the operator for it to be an expression
2548
- $needWhite = $whiteBefore && !$this->inParens;
2549
-
2550
- if ($this->match(self::$operatorString.($needWhite ? '\s' : ''), $m) && self::$precedence[$m[1]] >= $minP) {
2551
- if (!$this->inParens && isset($this->env->currentProperty) && $m[1] == "/" && empty($this->env->supressedDivision)) {
2552
- foreach (self::$supressDivisionProps as $pattern) {
2553
- if (preg_match($pattern, $this->env->currentProperty)) {
2554
- $this->env->supressedDivision = true;
2555
- break 2;
2556
- }
2557
- }
2558
- }
2559
-
2560
-
2561
- $whiteAfter = isset($this->buffer[$this->count - 1]) &&
2562
- ctype_space($this->buffer[$this->count - 1]);
2563
-
2564
- if (!$this->value($rhs)) break;
2565
-
2566
- // peek for next operator to see what to do with rhs
2567
- if ($this->peek(self::$operatorString, $next) && self::$precedence[$next[1]] > self::$precedence[$m[1]]) {
2568
- $rhs = $this->expHelper($rhs, self::$precedence[$next[1]]);
2569
- }
2570
-
2571
- $lhs = array('expression', $m[1], $lhs, $rhs, $whiteBefore, $whiteAfter);
2572
- $ss = $this->seek();
2573
-
2574
- continue;
2575
- }
2576
-
2577
- break;
2578
- }
2579
-
2580
- $this->seek($ss);
2581
-
2582
- return $lhs;
2583
- }
2584
-
2585
- // consume a list of values for a property
2586
- public function propertyValue(&$value, $keyName = null) {
2587
- $values = array();
2588
-
2589
- if ($keyName !== null) $this->env->currentProperty = $keyName;
2590
-
2591
- $s = null;
2592
- while ($this->expressionList($v)) {
2593
- $values[] = $v;
2594
- $s = $this->seek();
2595
- if (!$this->literal(',')) break;
2596
- }
2597
-
2598
- if ($s) $this->seek($s);
2599
-
2600
- if ($keyName !== null) unset($this->env->currentProperty);
2601
-
2602
- if (count($values) == 0) return false;
2603
-
2604
- $value = JupiterX_Lessc::compressList($values, ', ');
2605
- return true;
2606
- }
2607
-
2608
- protected function parenValue(&$out) {
2609
- $s = $this->seek();
2610
-
2611
- // speed shortcut
2612
- if (isset($this->buffer[$this->count]) && $this->buffer[$this->count] != "(") {
2613
- return false;
2614
- }
2615
-
2616
- $inParens = $this->inParens;
2617
- if ($this->literal("(") &&
2618
- ($this->inParens = true) && $this->expression($exp) &&
2619
- $this->literal(")"))
2620
- {
2621
- $out = $exp;
2622
- $this->inParens = $inParens;
2623
- return true;
2624
- } else {
2625
- $this->inParens = $inParens;
2626
- $this->seek($s);
2627
- }
2628
-
2629
- return false;
2630
- }
2631
-
2632
- // a single value
2633
- protected function value(&$value) {
2634
- $s = $this->seek();
2635
-
2636
- // speed shortcut
2637
- if (isset($this->buffer[$this->count]) && $this->buffer[$this->count] == "-") {
2638
- // negation
2639
- if ($this->literal("-", false) &&
2640
- (($this->variable($inner) && $inner = array("variable", $inner)) ||
2641
- $this->unit($inner) ||
2642
- $this->parenValue($inner)))
2643
- {
2644
- $value = array("unary", "-", $inner);
2645
- return true;
2646
- } else {
2647
- $this->seek($s);
2648
- }
2649
- }
2650
-
2651
- if ($this->parenValue($value)) return true;
2652
- if ($this->unit($value)) return true;
2653
- if ($this->color($value)) return true;
2654
- if ($this->func($value)) return true;
2655
- if ($this->string($value)) return true;
2656
-
2657
- if ($this->keyword($word)) {
2658
- $value = array('keyword', $word);
2659
- return true;
2660
- }
2661
-
2662
- // try a variable
2663
- if ($this->variable($var)) {
2664
- $value = array('variable', $var);
2665
- return true;
2666
- }
2667
-
2668
- // unquote string (should this work on any type?
2669
- if ($this->literal("~") && $this->string($str)) {
2670
- $value = array("escape", $str);
2671
- return true;
2672
- } else {
2673
- $this->seek($s);
2674
- }
2675
-
2676
- // css hack: \0
2677
- if ($this->literal('\\') && $this->match('([0-9]+)', $m)) {
2678
- $value = array('keyword', '\\'.$m[1]);
2679
- return true;
2680
- } else {
2681
- $this->seek($s);
2682
- }
2683
-
2684
- return false;
2685
- }
2686
-
2687
- // an import statement
2688
- protected function import(&$out) {
2689
- $s = $this->seek();
2690
- if (!$this->literal('@import')) return false;
2691
-
2692
- // @import "something.css" media;
2693
- // @import url("something.css") media;
2694
- // @import url(something.css) media;
2695
-
2696
- if ($this->propertyValue($value)) {
2697
- $out = array("import", $value);
2698
- return true;
2699
- }
2700
- }
2701
-
2702
- protected function mediaQueryList(&$out) {
2703
- if ($this->genericList($list, "mediaQuery", ",", false)) {
2704
- $out = $list[2];
2705
- return true;
2706
- }
2707
- return false;
2708
- }
2709
-
2710
- protected function mediaQuery(&$out) {
2711
- $s = $this->seek();
2712
-
2713
- $expressions = null;
2714
- $parts = array();
2715
-
2716
- if (($this->literal("only") && ($only = true) || $this->literal("not") && ($not = true) || true) && $this->keyword($mediaType)) {
2717
- $prop = array("mediaType");
2718
- if (isset($only)) $prop[] = "only";
2719
- if (isset($not)) $prop[] = "not";
2720
- $prop[] = $mediaType;
2721
- $parts[] = $prop;
2722
- } else {
2723
- $this->seek($s);
2724
- }
2725
-
2726
-
2727
- if (!empty($mediaType) && !$this->literal("and")) {
2728
- // ~
2729
- } else {
2730
- $this->genericList($expressions, "mediaExpression", "and", false);
2731
- if (is_array($expressions)) $parts = array_merge($parts, $expressions[2]);
2732
- }
2733
-
2734
- if (count($parts) == 0) {
2735
- $this->seek($s);
2736
- return false;
2737
- }
2738
-
2739
- $out = $parts;
2740
- return true;
2741
- }
2742
-
2743
- protected function mediaExpression(&$out) {
2744
- $s = $this->seek();
2745
- $value = null;
2746
- if ($this->literal("(") &&
2747
- $this->keyword($feature) &&
2748
- ($this->literal(":") && $this->expression($value) || true) &&
2749
- $this->literal(")"))
2750
- {
2751
- $out = array("mediaExp", $feature);
2752
- if ($value) $out[] = $value;
2753
- return true;
2754
- } elseif ($this->variable($variable)) {
2755
- $out = array('variable', $variable);
2756
- return true;
2757
- }
2758
-
2759
- $this->seek($s);
2760
- return false;
2761
- }
2762
-
2763
- // an unbounded string stopped by $end
2764
- protected function openString($end, &$out, $nestingOpen=null, $rejectStrs = null) {
2765
- $oldWhite = $this->eatWhiteDefault;
2766
- $this->eatWhiteDefault = false;
2767
-
2768
- $stop = array("'", '"', "@{", $end);
2769
- $stop = array_map(array("JupiterX_Lessc", "preg_quote"), $stop);
2770
- // $stop[] = self::$commentMulti;
2771
-
2772
- if (!is_null($rejectStrs)) {
2773
- $stop = array_merge($stop, $rejectStrs);
2774
- }
2775
-
2776
- $patt = '(.*?)('.implode("|", $stop).')';
2777
-
2778
- $nestingLevel = 0;
2779
-
2780
- $content = array();
2781
- while ($this->match($patt, $m, false)) {
2782
- if (!empty($m[1])) {
2783
- $content[] = $m[1];
2784
- if ($nestingOpen) {
2785
- $nestingLevel += substr_count($m[1], $nestingOpen);
2786
- }
2787
- }
2788
-
2789
- $tok = $m[2];
2790
-
2791
- $this->count-= strlen($tok);
2792
- if ($tok == $end) {
2793
- if ($nestingLevel == 0) {
2794
- break;
2795
- } else {
2796
- $nestingLevel--;
2797
- }
2798
- }
2799
-
2800
- if (($tok == "'" || $tok == '"') && $this->string($str)) {
2801
- $content[] = $str;
2802
- continue;
2803
- }
2804
-
2805
- if ($tok == "@{" && $this->interpolation($inter)) {
2806
- $content[] = $inter;
2807
- continue;
2808
- }
2809
-
2810
- if (!empty($rejectStrs) && in_array($tok, $rejectStrs)) {
2811
- break;
2812
- }
2813
-
2814
- $content[] = $tok;
2815
- $this->count+= strlen($tok);
2816
- }
2817
-
2818
- $this->eatWhiteDefault = $oldWhite;
2819
-
2820
- if (count($content) == 0) return false;
2821
-
2822
- // trim the end
2823
- if (is_string(end($content))) {
2824
- $content[count($content) - 1] = rtrim(end($content));
2825
- }
2826
-
2827
- $out = array("string", "", $content);
2828
- return true;
2829
- }
2830
-
2831
- protected function string(&$out) {
2832
- $s = $this->seek();
2833
- if ($this->literal('"', false)) {
2834
- $delim = '"';
2835
- } elseif ($this->literal("'", false)) {
2836
- $delim = "'";
2837
- } else {
2838
- return false;
2839
- }
2840
-
2841
- $content = array();
2842
-
2843
- // look for either ending delim , escape, or string interpolation
2844
- $patt = '([^\n]*?)(@\{|\\\\|' .
2845
- JupiterX_Lessc::preg_quote($delim).')';
2846
-
2847
- $oldWhite = $this->eatWhiteDefault;
2848
- $this->eatWhiteDefault = false;
2849
-
2850
- while ($this->match($patt, $m, false)) {
2851
- $content[] = $m[1];
2852
- if ($m[2] == "@{") {
2853
- $this->count -= strlen($m[2]);
2854
- if ($this->interpolation($inter, false)) {
2855
- $content[] = $inter;
2856
- } else {
2857
- $this->count += strlen($m[2]);
2858
- $content[] = "@{"; // ignore it
2859
- }
2860
- } elseif ($m[2] == '\\') {
2861
- $content[] = $m[2];
2862
- if ($this->literal($delim, false)) {
2863
- $content[] = $delim;
2864
- }
2865
- } else {
2866
- $this->count -= strlen($delim);
2867
- break; // delim
2868
- }
2869
- }
2870
-
2871
- $this->eatWhiteDefault = $oldWhite;
2872
-
2873
- if ($this->literal($delim)) {
2874
- $out = array("string", $delim, $content);
2875
- return true;
2876
- }
2877
-
2878
- $this->seek($s);
2879
- return false;
2880
- }
2881
-
2882
- protected function interpolation(&$out) {
2883
- $oldWhite = $this->eatWhiteDefault;
2884
- $this->eatWhiteDefault = true;
2885
-
2886
- $s = $this->seek();
2887
- if ($this->literal("@{") &&
2888
- $this->openString("}", $interp, null, array("'", '"', ";")) &&
2889
- $this->literal("}", false))
2890
- {
2891
- $out = array("interpolate", $interp);
2892
- $this->eatWhiteDefault = $oldWhite;
2893
- if ($this->eatWhiteDefault) $this->whitespace();
2894
- return true;
2895
- }
2896
-
2897
- $this->eatWhiteDefault = $oldWhite;
2898
- $this->seek($s);
2899
- return false;
2900
- }
2901
-
2902
- protected function unit(&$unit) {
2903
- // speed shortcut
2904
- if (isset($this->buffer[$this->count])) {
2905
- $char = $this->buffer[$this->count];
2906
- if (!ctype_digit($char) && $char != ".") return false;
2907
- }
2908
-
2909
- if ($this->match('([0-9]+(?:\.[0-9]*)?|\.[0-9]+)([%a-zA-Z]+)?', $m)) {
2910
- $unit = array("number", $m[1], empty($m[2]) ? "" : $m[2]);
2911
- return true;
2912
- }
2913
- return false;
2914
- }
2915
-
2916
- // a # color
2917
- protected function color(&$out) {
2918
- if ($this->match('(#(?:[0-9a-f]{8}|[0-9a-f]{6}|[0-9a-f]{3}))', $m)) {
2919
- if (strlen($m[1]) > 7) {
2920
- $out = array("string", "", array($m[1]));
2921
- } else {
2922
- $out = array("raw_color", $m[1]);
2923
- }
2924
- return true;
2925
- }
2926
-
2927
- return false;
2928
- }
2929
-
2930
- // consume an argument definition list surrounded by ()
2931
- // each argument is a variable name with optional value
2932
- // or at the end a ... or a variable named followed by ...
2933
- // arguments are separated by , unless a ; is in the list, then ; is the
2934
- // delimiter.
2935
- protected function argumentDef(&$args, &$isVararg) {
2936
- $s = $this->seek();
2937
- if (!$this->literal('(')) return false;
2938
-
2939
- $values = array();
2940
- $delim = ",";
2941
- $method = "expressionList";
2942
-
2943
- $isVararg = false;
2944
- while (true) {
2945
- if ($this->literal("...")) {
2946
- $isVararg = true;
2947
- break;
2948
- }
2949
-
2950
- if ($this->$method($value)) {
2951
- if ($value[0] == "variable") {
2952
- $arg = array("arg", $value[1]);
2953
- $ss = $this->seek();
2954
-
2955
- if ($this->assign() && $this->$method($rhs)) {
2956
- $arg[] = $rhs;
2957
- } else {
2958
- $this->seek($ss);
2959
- if ($this->literal("...")) {
2960
- $arg[0] = "rest";
2961
- $isVararg = true;
2962
- }
2963
- }
2964
-
2965
- $values[] = $arg;
2966
- if ($isVararg) break;
2967
- continue;
2968
- } else {
2969
- $values[] = array("lit", $value);
2970
- }
2971
- }
2972
-
2973
-
2974
- if (!$this->literal($delim)) {
2975
- if ($delim == "," && $this->literal(";")) {
2976
- // found new delim, convert existing args
2977
- $delim = ";";
2978
- $method = "propertyValue";
2979
-
2980
- // transform arg list
2981
- if (isset($values[1])) { // 2 items
2982
- $newList = array();
2983
- foreach ($values as $i => $arg) {
2984
- switch($arg[0]) {
2985
- case "arg":
2986
- if ($i) {
2987
- $this->throwError("Cannot mix ; and , as delimiter types");
2988
- }
2989
- $newList[] = $arg[2];
2990
- break;
2991
- case "lit":
2992
- $newList[] = $arg[1];
2993
- break;
2994
- case "rest":
2995
- $this->throwError("Unexpected rest before semicolon");
2996
- }
2997
- }
2998
-
2999
- $newList = array("list", ", ", $newList);
3000
-
3001
- switch ($values[0][0]) {
3002
- case "arg":
3003
- $newArg = array("arg", $values[0][1], $newList);
3004
- break;
3005
- case "lit":
3006
- $newArg = array("lit", $newList);
3007
- break;
3008
- }
3009
-
3010
- } elseif ($values) { // 1 item
3011
- $newArg = $values[0];
3012
- }
3013
-
3014
- if ($newArg) {
3015
- $values = array($newArg);
3016
- }
3017
- } else {
3018
- break;
3019
- }
3020
- }
3021
- }
3022
-
3023
- if (!$this->literal(')')) {
3024
- $this->seek($s);
3025
- return false;
3026
- }
3027
-
3028
- $args = $values;
3029
-
3030
- return true;
3031
- }
3032
-
3033
- // consume a list of tags
3034
- // this accepts a hanging delimiter
3035
- protected function tags(&$tags, $simple = false, $delim = ',') {
3036
- $tags = array();
3037
- while ($this->tag($tt, $simple)) {
3038
- $tags[] = $tt;
3039
- if (!$this->literal($delim)) break;
3040
- }
3041
- if (count($tags) == 0) return false;
3042
-
3043
- return true;
3044
- }
3045
-
3046
- // list of tags of specifying mixin path
3047
- // optionally separated by > (lazy, accepts extra >)
3048
- protected function mixinTags(&$tags) {
3049
- $s = $this->seek();
3050
- $tags = array();
3051
- while ($this->tag($tt, true)) {
3052
- $tags[] = $tt;
3053
- $this->literal(">");
3054
- }
3055
-
3056
- if (count($tags) == 0) return false;
3057
-
3058
- return true;
3059
- }
3060
-
3061
- // a bracketed value (contained within in a tag definition)
3062
- protected function tagBracket(&$parts, &$hasExpression) {
3063
- // speed shortcut
3064
- if (isset($this->buffer[$this->count]) && $this->buffer[$this->count] != "[") {
3065
- return false;
3066
- }
3067
-
3068
- $s = $this->seek();
3069
-
3070
- $hasInterpolation = false;
3071
-
3072
- if ($this->literal("[", false)) {
3073
- $attrParts = array("[");
3074
- // keyword, string, operator
3075
- while (true) {
3076
- if ($this->literal("]", false)) {
3077
- $this->count--;
3078
- break; // get out early
3079
- }
3080
-
3081
- if ($this->match('\s+', $m)) {
3082
- $attrParts[] = " ";
3083
- continue;
3084
- }
3085
- if ($this->string($str)) {
3086
- // escape parent selector, (yuck)
3087
- foreach ($str[2] as &$chunk) {
3088
- $chunk = str_replace($this->lessc->parentSelector, "$&$", $chunk);
3089
- }
3090
-
3091
- $attrParts[] = $str;
3092
- $hasInterpolation = true;
3093
- continue;
3094
- }
3095
-
3096
- if ($this->keyword($word)) {
3097
- $attrParts[] = $word;
3098
- continue;
3099
- }
3100
-
3101
- if ($this->interpolation($inter, false)) {
3102
- $attrParts[] = $inter;
3103
- $hasInterpolation = true;
3104
- continue;
3105
- }
3106
-
3107
- // operator, handles attr namespace too
3108
- if ($this->match('[|-~\$\*\^=]+', $m)) {
3109
- $attrParts[] = $m[0];
3110
- continue;
3111
- }
3112
-
3113
- break;
3114
- }
3115
-
3116
- if ($this->literal("]", false)) {
3117
- $attrParts[] = "]";
3118
- foreach ($attrParts as $part) {
3119
- $parts[] = $part;
3120
- }
3121
- $hasExpression = $hasExpression || $hasInterpolation;
3122
- return true;
3123
- }
3124
- $this->seek($s);
3125
- }
3126
-
3127
- $this->seek($s);
3128
- return false;
3129
- }
3130
-
3131
- // a space separated list of selectors
3132
- protected function tag(&$tag, $simple = false) {
3133
- if ($simple)
3134
- $chars = '^@,:;{}\][>\(\) "\'';
3135
- else
3136
- $chars = '^@,;{}["\'';
3137
-
3138
- $s = $this->seek();
3139
-
3140
- $hasExpression = false;
3141
- $parts = array();
3142
- while ($this->tagBracket($parts, $hasExpression));
3143
-
3144
- $oldWhite = $this->eatWhiteDefault;
3145
- $this->eatWhiteDefault = false;
3146
-
3147
- while (true) {
3148
- if ($this->match('(['.$chars.'0-9]['.$chars.']*)', $m)) {
3149
- $parts[] = $m[1];
3150
- if ($simple) break;
3151
-
3152
- while ($this->tagBracket($parts, $hasExpression));
3153
- continue;
3154
- }
3155
-
3156
- if (isset($this->buffer[$this->count]) && $this->buffer[$this->count] == "@") {
3157
- if ($this->interpolation($interp)) {
3158
- $hasExpression = true;
3159
- $interp[2] = true; // don't unescape
3160
- $parts[] = $interp;
3161
- continue;
3162
- }
3163
-
3164
- if ($this->literal("@")) {
3165
- $parts[] = "@";
3166
- continue;
3167
- }
3168
- }
3169
-
3170
- if ($this->unit($unit)) { // for keyframes
3171
- $parts[] = $unit[1];
3172
- $parts[] = $unit[2];
3173
- continue;
3174
- }
3175
-
3176
- break;
3177
- }
3178
-
3179
- $this->eatWhiteDefault = $oldWhite;
3180
- if (!$parts) {
3181
- $this->seek($s);
3182
- return false;
3183
- }
3184
-
3185
- if ($hasExpression) {
3186
- $tag = array("exp", array("string", "", $parts));
3187
- } else {
3188
- $tag = trim(implode($parts));
3189
- }
3190
-
3191
- $this->whitespace();
3192
- return true;
3193
- }
3194
-
3195
- // a css function
3196
- protected function func(&$func) {
3197
- $s = $this->seek();
3198
-
3199
- if ($this->match('(%|[\w\-_][\w\-_:\.]+|[\w_])', $m) && $this->literal('(')) {
3200
- $fname = $m[1];
3201
-
3202
- $sPreArgs = $this->seek();
3203
-
3204
- $args = array();
3205
- while (true) {
3206
- $ss = $this->seek();
3207
- // this ugly nonsense is for ie filter properties
3208
- if ($this->keyword($name) && $this->literal('=') && $this->expressionList($value)) {
3209
- $args[] = array("string", "", array($name, "=", $value));
3210
- } else {
3211
- $this->seek($ss);
3212
- if ($this->expressionList($value)) {
3213
- $args[] = $value;
3214
- }
3215
- }
3216
-
3217
- if (!$this->literal(',')) break;
3218
- }
3219
- $args = array('list', ',', $args);
3220
-
3221
- if ($this->literal(')')) {
3222
- $func = array('function', $fname, $args);
3223
- return true;
3224
- } elseif ($fname == 'url') {
3225
- // couldn't parse and in url? treat as string
3226
- $this->seek($sPreArgs);
3227
- if ($this->openString(")", $string) && $this->literal(")")) {
3228
- $func = array('function', $fname, $string);
3229
- return true;
3230
- }
3231
- }
3232
- }
3233
-
3234
- $this->seek($s);
3235
- return false;
3236
- }
3237
-
3238
- // consume a less variable
3239
- protected function variable(&$name) {
3240
- $s = $this->seek();
3241
- if ($this->literal($this->lessc->vPrefix, false) &&
3242
- ($this->variable($sub) || $this->keyword($name)))
3243
- {
3244
- if (!empty($sub)) {
3245
- $name = array('variable', $sub);
3246
- } else {
3247
- $name = $this->lessc->vPrefix.$name;
3248
- }
3249
- return true;
3250
- }
3251
-
3252
- $name = null;
3253
- $this->seek($s);
3254
- return false;
3255
- }
3256
-
3257
- /**
3258
- * Consume an assignment operator
3259
- * Can optionally take a name that will be set to the current property name
3260
- */
3261
- protected function assign($name = null) {
3262
- if ($name) $this->currentProperty = $name;
3263
- return $this->literal(':') || $this->literal('=');
3264
- }
3265
-
3266
- // consume a keyword
3267
- protected function keyword(&$word) {
3268
- if ($this->match('([\w_\-\*!"][\w\-_"]*)', $m)) {
3269
- $word = $m[1];
3270
- return true;
3271
- }
3272
- return false;
3273
- }
3274
-
3275
- // consume an end of statement delimiter
3276
- protected function end() {
3277
- if ($this->literal(';')) {
3278
- return true;
3279
- } elseif ($this->count == strlen($this->buffer) || $this->buffer[$this->count] == '}') {
3280
- // if there is end of file or a closing block next then we don't need a ;
3281
- return true;
3282
- }
3283
- return false;
3284
- }
3285
-
3286
- protected function guards(&$guards) {
3287
- $s = $this->seek();
3288
-
3289
- if (!$this->literal("when")) {
3290
- $this->seek($s);
3291
- return false;
3292
- }
3293
-
3294
- $guards = array();
3295
-
3296
- while ($this->guardGroup($g)) {
3297
- $guards[] = $g;
3298
- if (!$this->literal(",")) break;
3299
- }
3300
-
3301
- if (count($guards) == 0) {
3302
- $guards = null;
3303
- $this->seek($s);
3304
- return false;
3305
- }
3306
-
3307
- return true;
3308
- }
3309
-
3310
- // a bunch of guards that are and'd together
3311
- // TODO rename to guardGroup
3312
- protected function guardGroup(&$guardGroup) {
3313
- $s = $this->seek();
3314
- $guardGroup = array();
3315
- while ($this->guard($guard)) {
3316
- $guardGroup[] = $guard;
3317
- if (!$this->literal("and")) break;
3318
- }
3319
-
3320
- if (count($guardGroup) == 0) {
3321
- $guardGroup = null;
3322
- $this->seek($s);
3323
- return false;
3324
- }
3325
-
3326
- return true;
3327
- }
3328
-
3329
- protected function guard(&$guard) {
3330
- $s = $this->seek();
3331
- $negate = $this->literal("not");
3332
-
3333
- if ($this->literal("(") && $this->expression($exp) && $this->literal(")")) {
3334
- $guard = $exp;
3335
- if ($negate) $guard = array("negate", $guard);
3336
- return true;
3337
- }
3338
-
3339
- $this->seek($s);
3340
- return false;
3341
- }
3342
-
3343
- /* raw parsing functions */
3344
-
3345
- protected function literal($what, $eatWhitespace = null) {
3346
- if ($eatWhitespace === null) $eatWhitespace = $this->eatWhiteDefault;
3347
-
3348
- // shortcut on single letter
3349
- if (!isset($what[1]) && isset($this->buffer[$this->count])) {
3350
- if ($this->buffer[$this->count] == $what) {
3351
- if (!$eatWhitespace) {
3352
- $this->count++;
3353
- return true;
3354
- }
3355
- // goes below...
3356
- } else {
3357
- return false;
3358
- }
3359
- }
3360
-
3361
- if (!isset(self::$literalCache[$what])) {
3362
- self::$literalCache[$what] = JupiterX_Lessc::preg_quote($what);
3363
- }
3364
-
3365
- return $this->match(self::$literalCache[$what], $m, $eatWhitespace);
3366
- }
3367
-
3368
- protected function genericList(&$out, $parseItem, $delim="", $flatten=true) {
3369
- $s = $this->seek();
3370
- $items = array();
3371
- while ($this->$parseItem($value)) {
3372
- $items[] = $value;
3373
- if ($delim) {
3374
- if (!$this->literal($delim)) break;
3375
- }
3376
- }
3377
-
3378
- if (count($items) == 0) {
3379
- $this->seek($s);
3380
- return false;
3381
- }
3382
-
3383
- if ($flatten && count($items) == 1) {
3384
- $out = $items[0];
3385
- } else {
3386
- $out = array("list", $delim, $items);
3387
- }
3388
-
3389
- return true;
3390
- }
3391
-
3392
-
3393
- // advance counter to next occurrence of $what
3394
- // $until - don't include $what in advance
3395
- // $allowNewline, if string, will be used as valid char set
3396
- protected function to($what, &$out, $until = false, $allowNewline = false) {
3397
- if (is_string($allowNewline)) {
3398
- $validChars = $allowNewline;
3399
- } else {
3400
- $validChars = $allowNewline ? "." : "[^\n]";
3401
- }
3402
- if (!$this->match('('.$validChars.'*?)'.JupiterX_Lessc::preg_quote($what), $m, !$until)) return false;
3403
- if ($until) $this->count -= strlen($what); // give back $what
3404
- $out = $m[1];
3405
- return true;
3406
- }
3407
-
3408
- // try to match something on head of buffer
3409
- protected function match($regex, &$out, $eatWhitespace = null) {
3410
- if ($eatWhitespace === null) $eatWhitespace = $this->eatWhiteDefault;
3411
-
3412
- $r = '/'.$regex.($eatWhitespace && !$this->writeComments ? '\s*' : '').'/Ais';
3413
- if (preg_match($r, $this->buffer, $out, null, $this->count)) {
3414
- $this->count += strlen($out[0]);
3415
- if ($eatWhitespace && $this->writeComments) $this->whitespace();
3416
- return true;
3417
- }
3418
- return false;
3419
- }
3420
-
3421
- // match some whitespace
3422
- protected function whitespace() {
3423
- if ($this->writeComments) {
3424
- $gotWhite = false;
3425
- while (preg_match(self::$whitePattern, $this->buffer, $m, null, $this->count)) {
3426
- if (isset($m[1]) && empty($this->commentsSeen[$this->count])) {
3427
- $this->append(array("comment", $m[1]));
3428
- $this->commentsSeen[$this->count] = true;
3429
- }
3430
- $this->count += strlen($m[0]);
3431
- $gotWhite = true;
3432
- }
3433
- return $gotWhite;
3434
- } else {
3435
- $this->match("", $m);
3436
- return strlen($m[0]) > 0;
3437
- }
3438
- }
3439
-
3440
- // match something without consuming it
3441
- protected function peek($regex, &$out = null, $from=null) {
3442
- if (is_null($from)) $from = $this->count;
3443
- $r = '/'.$regex.'/Ais';
3444
- $result = preg_match($r, $this->buffer, $out, null, $from);
3445
-
3446
- return $result;
3447
- }
3448
-
3449
- // seek to a spot in the buffer or return where we are on no argument
3450
- protected function seek($where = null) {
3451
- if ($where === null) return $this->count;
3452
- else $this->count = $where;
3453
- return true;
3454
- }
3455
-
3456
- /* misc functions */
3457
-
3458
- public function throwError($msg = "parse error", $count = null) {
3459
- $count = is_null($count) ? $this->count : $count;
3460
-
3461
- $line = $this->line +
3462
- substr_count(substr($this->buffer, 0, $count), "\n");
3463
-
3464
- if (!empty($this->sourceName)) {
3465
- $loc = "$this->sourceName on line $line";
3466
- } else {
3467
- $loc = "line: $line";
3468
- }
3469
-
3470
- // TODO this depends on $this->count
3471
- if ($this->peek("(.*?)(\n|$)", $m, $count)) {
3472
- throw new exception("$msg: failed at `$m[1]` $loc");
3473
- } else {
3474
- throw new exception("$msg: $loc");
3475
- }
3476
- }
3477
-
3478
- protected function pushBlock($selectors=null, $type=null) {
3479
- $b = new stdclass;
3480
- $b->parent = $this->env;
3481
-
3482
- $b->type = $type;
3483
- $b->id = self::$nextBlockId++;
3484
-
3485
- $b->isVararg = false; // TODO: kill me from here
3486
- $b->tags = $selectors;
3487
-
3488
- $b->props = array();
3489
- $b->children = array();
3490
-
3491
- $this->env = $b;
3492
- return $b;
3493
- }
3494
-
3495
- // push a block that doesn't multiply tags
3496
- protected function pushSpecialBlock($type) {
3497
- return $this->pushBlock(null, $type);
3498
- }
3499
-
3500
- // append a property to the current block
3501
- protected function append($prop, $pos = null) {
3502
- if ($pos !== null) $prop[-1] = $pos;
3503
- $this->env->props[] = $prop;
3504
- }
3505
-
3506
- // pop something off the stack
3507
- protected function pop() {
3508
- $old = $this->env;
3509
- $this->env = $this->env->parent;
3510
- return $old;
3511
- }
3512
-
3513
- // remove comments from $text
3514
- // todo: make it work for all functions, not just url
3515
- protected function removeComments($text) {
3516
- $look = array(
3517
- 'url(', '//', '/*', '"', "'"
3518
- );
3519
-
3520
- $out = '';
3521
- $min = null;
3522
- while (true) {
3523
- // find the next item
3524
- foreach ($look as $token) {
3525
- $pos = strpos($text, $token);
3526
- if ($pos !== false) {
3527
- if (!isset($min) || $pos < $min[1]) $min = array($token, $pos);
3528
- }
3529
- }
3530
-
3531
- if (is_null($min)) break;
3532
-
3533
- $count = $min[1];
3534
- $skip = 0;
3535
- $newlines = 0;
3536
- switch ($min[0]) {
3537
- case 'url(':
3538
- if (preg_match('/url\(.*?\)/', $text, $m, 0, $count))
3539
- $count += strlen($m[0]) - strlen($min[0]);
3540
- break;
3541
- case '"':
3542
- case "'":
3543
- if (preg_match('/'.$min[0].'.*?(?<!\\\\)'.$min[0].'/', $text, $m, 0, $count))
3544
- $count += strlen($m[0]) - 1;
3545
- break;
3546
- case '//':
3547
- $skip = strpos($text, "\n", $count);
3548
- if ($skip === false) $skip = strlen($text) - $count;
3549
- else $skip -= $count;
3550
- break;
3551
- case '/*':
3552
- if (preg_match('/\/\*.*?\*\//s', $text, $m, 0, $count)) {
3553
- $skip = strlen($m[0]);
3554
- $newlines = substr_count($m[0], "\n");
3555
- }
3556
- break;
3557
- }
3558
-
3559
- if ($skip == 0) $count += strlen($min[0]);
3560
-
3561
- $out .= substr($text, 0, $count).str_repeat("\n", $newlines);
3562
- $text = substr($text, $count + $skip);
3563
-
3564
- $min = null;
3565
- }
3566
-
3567
- return $out.$text;
3568
- }
3569
-
3570
- }
3571
-
3572
- /**
3573
- * @ignore
3574
- */
3575
- class lessc_formatter_classic {
3576
- public $indentChar = " ";
3577
-
3578
- public $break = "\n";
3579
- public $open = " {";
3580
- public $close = "}";
3581
- public $selectorSeparator = ", ";
3582
- public $assignSeparator = ":";
3583
-
3584
- public $openSingle = " { ";
3585
- public $closeSingle = " }";
3586
-
3587
- public $disableSingle = false;
3588
- public $breakSelectors = false;
3589
-
3590
- public $compressColors = false;
3591
-
3592
- public function __construct() {
3593
- $this->indentLevel = 0;
3594
- }
3595
-
3596
- public function indentStr($n = 0) {
3597
- return str_repeat($this->indentChar, max($this->indentLevel + $n, 0));
3598
- }
3599
-
3600
- public function property($name, $value) {
3601
- return $name . $this->assignSeparator . $value . ";";
3602
- }
3603
-
3604
- protected function isEmpty($block) {
3605
- if (empty($block->lines)) {
3606
- foreach ($block->children as $child) {
3607
- if (!$this->isEmpty($child)) return false;
3608
- }
3609
-
3610
- return true;
3611
- }
3612
- return false;
3613
- }
3614
-
3615
- public function block($block) {
3616
- if ($this->isEmpty($block)) return;
3617
-
3618
- $inner = $pre = $this->indentStr();
3619
-
3620
- $isSingle = !$this->disableSingle &&
3621
- is_null($block->type) && count($block->lines) == 1;
3622
-
3623
- if (!empty($block->selectors)) {
3624
- $this->indentLevel++;
3625
-
3626
- if ($this->breakSelectors) {
3627
- $selectorSeparator = $this->selectorSeparator . $this->break . $pre;
3628
- } else {
3629
- $selectorSeparator = $this->selectorSeparator;
3630
- }
3631
-
3632
- echo $pre .
3633
- implode($selectorSeparator, $block->selectors);
3634
- if ($isSingle) {
3635
- echo $this->openSingle;
3636
- $inner = "";
3637
- } else {
3638
- echo $this->open . $this->break;
3639
- $inner = $this->indentStr();
3640
- }
3641
-
3642
- }
3643
-
3644
- if (!empty($block->lines)) {
3645
- $glue = $this->break.$inner;
3646
- echo $inner . implode($glue, $block->lines);
3647
- if (!$isSingle && !empty($block->children)) {
3648
- echo $this->break;
3649
- }
3650
- }
3651
-
3652
- foreach ($block->children as $child) {
3653
- $this->block($child);
3654
- }
3655
-
3656
- if (!empty($block->selectors)) {
3657
- if (!$isSingle && empty($block->children)) echo $this->break;
3658
-
3659
- if ($isSingle) {
3660
- echo $this->closeSingle . $this->break;
3661
- } else {
3662
- echo $pre . $this->close . $this->break;
3663
- }
3664
-
3665
- $this->indentLevel--;
3666
- }
3667
- }
3668
- }
3669
-
3670
- /**
3671
- * @ignore
3672
- */
3673
- class lessc_formatter_compressed extends lessc_formatter_classic {
3674
- public $disableSingle = true;
3675
- public $open = "{";
3676
- public $selectorSeparator = ",";
3677
- public $assignSeparator = ":";
3678
- public $break = "";
3679
- public $compressColors = true;
3680
-
3681
- public function indentStr($n = 0) {
3682
- return "";
3683
- }
3684
- }
3685
-
3686
- /**
3687
- * @ignore
3688
- */
3689
- class lessc_formatter_lessjs extends lessc_formatter_classic {
3690
- public $disableSingle = true;
3691
- public $breakSelectors = true;
3692
- public $assignSeparator = ": ";
3693
- public $selectorSeparator = ",";
3694
- }
1
+ <?php
2
+ /**
3
+ * lessphp v0.4.0
4
+ * http://leafo.net/lessphp
5
+ *
6
+ * LESS css compiler, adapted from http://lesscss.org
7
+ *
8
+ * Copyright 2012, Leaf Corcoran <leafot@gmail.com>
9
+ * Licensed under MIT or GPLv3, see LICENSE
10
+ *
11
+ * @ignore
12
+ */
13
+
14
+
15
+ /**
16
+ * The less compiler and parser.
17
+ *
18
+ * Converting LESS to CSS is a three stage process. The incoming file is parsed
19
+ * by `lessc_parser` into a syntax tree, then it is compiled into another tree
20
+ * representing the CSS structure by `lessc`. The CSS tree is fed into a
21
+ * formatter, like `lessc_formatter` which then outputs CSS as a string.
22
+ *
23
+ * During the first compile, all values are *reduced*, which means that their
24
+ * types are brought to the lowest form before being dump as strings. This
25
+ * handles math equations, variable dereferences, and the like.
26
+ *
27
+ * The `parse` function of `lessc` is the entry point.
28
+ *
29
+ * In summary:
30
+ *
31
+ * The `lessc` class creates an intstance of the parser, feeds it LESS code,
32
+ * then transforms the resulting tree to a CSS tree. This class also holds the
33
+ * evaluation context, such as all available mixins and variables at any given
34
+ * time.
35
+ *
36
+ * The `lessc_parser` class is only concerned with parsing its input.
37
+ *
38
+ * The `lessc_formatter` takes a CSS tree, and dumps it to a formatted string,
39
+ * handling things like indentation.
40
+ *
41
+ * @ignore
42
+ */
43
+ class JupiterX_Lessc {
44
+ static public $VERSION = "v0.4.0";
45
+ static protected $TRUE = array("keyword", "true");
46
+ static protected $FALSE = array("keyword", "false");
47
+
48
+ protected $libFunctions = array();
49
+ protected $registeredVars = array();
50
+ protected $preserveComments = false;
51
+
52
+ public $vPrefix = '@'; // prefix of abstract properties
53
+ public $mPrefix = '$'; // prefix of abstract blocks
54
+ public $parentSelector = '&';
55
+
56
+ public $importDisabled = false;
57
+ public $importDir = '';
58
+
59
+ protected $numberPrecision = null;
60
+
61
+ protected $allParsedFiles = array();
62
+
63
+ // set to the parser that generated the current line when compiling
64
+ // so we know how to create error messages
65
+ protected $sourceParser = null;
66
+ protected $sourceLoc = null;
67
+
68
+ static public $defaultValue = array("keyword", "");
69
+
70
+ static protected $nextImportId = 0; // uniquely identify imports
71
+
72
+ // attempts to find the path of an import url, returns null for css files
73
+ protected function findImport($url) {
74
+ foreach ((array)$this->importDir as $dir) {
75
+ $full = $dir.(substr($dir, -1) != '/' ? '/' : '').$url;
76
+ if ($this->fileExists($file = $full.'.less') || $this->fileExists($file = $full)) {
77
+ return $file;
78
+ }
79
+ }
80
+
81
+ return null;
82
+ }
83
+
84
+ protected function fileExists($name) {
85
+ return is_file($name);
86
+ }
87
+
88
+ static public function compressList($items, $delim) {
89
+ if (!isset($items[1]) && isset($items[0])) return $items[0];
90
+ else return array('list', $delim, $items);
91
+ }
92
+
93
+ static public function preg_quote($what) {
94
+ return preg_quote($what, '/');
95
+ }
96
+
97
+ protected function tryImport($importPath, $parentBlock, $out) {
98
+ if ($importPath[0] == "function" && $importPath[1] == "url") {
99
+ $importPath = $this->flattenList($importPath[2]);
100
+ }
101
+
102
+ $str = $this->coerceString($importPath);
103
+ if ($str === null) return false;
104
+
105
+ $url = $this->compileValue($this->lib_unquote($str));
106
+
107
+ // don't import if it ends in css
108
+ if (substr_compare($url, '.css', -4, 4) === 0) return false;
109
+
110
+ $realPath = $this->findImport($url);
111
+
112
+ if ($realPath === null) return false;
113
+
114
+ if ($this->importDisabled) {
115
+ return array(false, "/* import disabled */");
116
+ }
117
+
118
+ if (isset($this->allParsedFiles[realpath($realPath)])) {
119
+ return array(false, null);
120
+ }
121
+
122
+ $this->addParsedFile($realPath);
123
+ $parser = $this->makeParser($realPath);
124
+ $root = $parser->parse($GLOBALS['wp_filesystem']->get_contents($realPath));
125
+
126
+ // set the parents of all the block props
127
+ foreach ($root->props as $prop) {
128
+ if ($prop[0] == "block") {
129
+ $prop[1]->parent = $parentBlock;
130
+ }
131
+ }
132
+
133
+ // copy mixins into scope, set their parents
134
+ // bring blocks from import into current block
135
+ // TODO: need to mark the source parser these came from this file
136
+ foreach ($root->children as $childName => $child) {
137
+ if (isset($parentBlock->children[$childName])) {
138
+ $parentBlock->children[$childName] = array_merge(
139
+ $parentBlock->children[$childName],
140
+ $child);
141
+ } else {
142
+ $parentBlock->children[$childName] = $child;
143
+ }
144
+ }
145
+
146
+ $pi = pathinfo($realPath);
147
+ $dir = $pi["dirname"];
148
+
149
+ list($top, $bottom) = $this->sortProps($root->props, true);
150
+ $this->compileImportedProps($top, $parentBlock, $out, $parser, $dir);
151
+
152
+ return array(true, $bottom, $parser, $dir);
153
+ }
154
+
155
+ protected function compileImportedProps($props, $block, $out, $sourceParser, $importDir) {
156
+ $oldSourceParser = $this->sourceParser;
157
+
158
+ $oldImport = $this->importDir;
159
+
160
+ // TODO: this is because the importDir api is stupid
161
+ $this->importDir = (array)$this->importDir;
162
+ array_unshift($this->importDir, $importDir);
163
+
164
+ foreach ($props as $prop) {
165
+ $this->compileProp($prop, $block, $out);
166
+ }
167
+
168
+ $this->importDir = $oldImport;
169
+ $this->sourceParser = $oldSourceParser;
170
+ }
171
+
172
+ /**
173
+ * Recursively compiles a block.
174
+ *
175
+ * A block is analogous to a CSS block in most cases. A single LESS document
176
+ * is encapsulated in a block when parsed, but it does not have parent tags
177
+ * so all of it's children appear on the root level when compiled.
178
+ *
179
+ * Blocks are made up of props and children.
180
+ *
181
+ * Props are property instructions, array tuples which describe an action
182
+ * to be taken, eg. write a property, set a variable, mixin a block.
183
+ *
184
+ * The children of a block are just all the blocks that are defined within.
185
+ * This is used to look up mixins when performing a mixin.
186
+ *
187
+ * Compiling the block involves pushing a fresh environment on the stack,
188
+ * and iterating through the props, compiling each one.
189
+ *
190
+ * See lessc::compileProp()
191
+ *
192
+ */
193
+ protected function compileBlock($block) {
194
+ switch ($block->type) {
195
+ case "root":
196
+ $this->compileRoot($block);
197
+ break;
198
+ case null:
199
+ $this->compileCSSBlock($block);
200
+ break;
201
+ case "media":
202
+ $this->compileMedia($block);
203
+ break;
204
+ case "directive":
205
+ $name = "@" . $block->name;
206
+ if (!empty($block->value)) {
207
+ $name .= " " . $this->compileValue($this->reduce($block->value));
208
+ }
209
+
210
+ $this->compileNestedBlock($block, array($name));
211
+ break;
212
+ default:
213
+ $this->throwError("unknown block type: $block->type\n");
214
+ }
215
+ }
216
+
217
+ protected function compileCSSBlock($block) {
218
+ $env = $this->pushEnv();
219
+
220
+ $selectors = $this->compileSelectors($block->tags);
221
+ $env->selectors = $this->multiplySelectors($selectors);
222
+ $out = $this->makeOutputBlock(null, $env->selectors);
223
+
224
+ $this->scope->children[] = $out;
225
+ $this->compileProps($block, $out);
226
+
227
+ $block->scope = $env; // mixins carry scope with them!
228
+ $this->popEnv();
229
+ }
230
+
231
+ protected function compileMedia($media) {
232
+ $env = $this->pushEnv($media);
233
+ $parentScope = $this->mediaParent($this->scope);
234
+
235
+ $query = $this->compileMediaQuery($this->multiplyMedia($env));
236
+
237
+ $this->scope = $this->makeOutputBlock($media->type, array($query));
238
+ $parentScope->children[] = $this->scope;
239
+
240
+ $this->compileProps($media, $this->scope);
241
+
242
+ if (count($this->scope->lines) > 0) {
243
+ $orphanSelelectors = $this->findClosestSelectors();
244
+ if (!is_null($orphanSelelectors)) {
245
+ $orphan = $this->makeOutputBlock(null, $orphanSelelectors);
246
+ $orphan->lines = $this->scope->lines;
247
+ array_unshift($this->scope->children, $orphan);
248
+ $this->scope->lines = array();
249
+ }
250
+ }
251
+
252
+ $this->scope = $this->scope->parent;
253
+ $this->popEnv();
254
+ }
255
+
256
+ protected function mediaParent($scope) {
257
+ while (!empty($scope->parent)) {
258
+ if (!empty($scope->type) && $scope->type != "media") {
259
+ break;
260
+ }
261
+ $scope = $scope->parent;
262
+ }
263
+
264
+ return $scope;
265
+ }
266
+
267
+ protected function compileNestedBlock($block, $selectors) {
268
+ $this->pushEnv($block);
269
+ $this->scope = $this->makeOutputBlock($block->type, $selectors);
270
+ $this->scope->parent->children[] = $this->scope;
271
+
272
+ $this->compileProps($block, $this->scope);
273
+
274
+ $this->scope = $this->scope->parent;
275
+ $this->popEnv();
276
+ }
277
+
278
+ protected function compileRoot($root) {
279
+ $this->pushEnv();
280
+ $this->scope = $this->makeOutputBlock($root->type);
281
+ $this->compileProps($root, $this->scope);
282
+ $this->popEnv();
283
+ }
284
+
285
+ protected function compileProps($block, $out) {
286
+ foreach ($this->sortProps($block->props) as $prop) {
287
+ $this->compileProp($prop, $block, $out);
288
+ }
289
+
290
+ $out->lines = array_values(array_unique($out->lines));
291
+ }
292
+
293
+ protected function sortProps($props, $split = false) {
294
+ $vars = array();
295
+ $imports = array();
296
+ $other = array();
297
+
298
+ foreach ($props as $prop) {
299
+ switch ($prop[0]) {
300
+ case "assign":
301
+ if (isset($prop[1][0]) && $prop[1][0] == $this->vPrefix) {
302
+ $vars[] = $prop;
303
+ } else {
304
+ $other[] = $prop;
305
+ }
306
+ break;
307
+ case "import":
308
+ $id = self::$nextImportId++;
309
+ $prop[] = $id;
310
+ $imports[] = $prop;
311
+ $other[] = array("import_mixin", $id);
312
+ break;
313
+ default:
314
+ $other[] = $prop;
315
+ }
316
+ }
317
+
318
+ if ($split) {
319
+ return array(array_merge($vars, $imports), $other);
320
+ } else {
321
+ return array_merge($vars, $imports, $other);
322
+ }
323
+ }
324
+
325
+ protected function compileMediaQuery($queries) {
326
+ $compiledQueries = array();
327
+ foreach ($queries as $query) {
328
+ $parts = array();
329
+ foreach ($query as $q) {
330
+ switch ($q[0]) {
331
+ case "mediaType":
332
+ $parts[] = implode(" ", array_slice($q, 1));
333
+ break;
334
+ case "mediaExp":
335
+ if (isset($q[2])) {
336
+ $parts[] = "($q[1]: " .
337
+ $this->compileValue($this->reduce($q[2])) . ")";
338
+ } else {
339
+ $parts[] = "($q[1])";
340
+ }
341
+ break;
342
+ case "variable":
343
+ $parts[] = $this->compileValue($this->reduce($q));
344
+ break;
345
+ }
346
+ }
347
+
348
+ if (count($parts) > 0) {
349
+ $compiledQueries[] = implode(" and ", $parts);
350
+ }
351
+ }
352
+
353
+ $out = "@media";
354
+ if (!empty($parts)) {
355
+ $out .= " " .
356
+ implode($this->formatter->selectorSeparator, $compiledQueries);
357
+ }
358
+ return $out;
359
+ }
360
+
361
+ protected function multiplyMedia($env, $childQueries = null) {
362
+ if (is_null($env) ||
363
+ !empty($env->block->type) && $env->block->type != "media")
364
+ {
365
+ return $childQueries;
366
+ }
367
+
368
+ // plain old block, skip
369
+ if (empty($env->block->type)) {
370
+ return $this->multiplyMedia($env->parent, $childQueries);
371
+ }
372
+
373
+ $out = array();
374
+ $queries = $env->block->queries;
375
+ if (is_null($childQueries)) {
376
+ $out = $queries;
377
+ } else {
378
+ foreach ($queries as $parent) {
379
+ foreach ($childQueries as $child) {
380
+ $out[] = array_merge($parent, $child);
381
+ }
382
+ }
383
+ }
384
+
385
+ return $this->multiplyMedia($env->parent, $out);
386
+ }
387
+
388
+ protected function expandParentSelectors(&$tag, $replace) {
389
+ $parts = explode("$&$", $tag);
390
+ $count = 0;
391
+ foreach ($parts as &$part) {
392
+ $part = str_replace($this->parentSelector, $replace, $part, $c);
393
+ $count += $c;
394
+ }
395
+ $tag = implode($this->parentSelector, $parts);
396
+ return $count;
397
+ }
398
+
399
+ protected function findClosestSelectors() {
400
+ $env = $this->env;
401
+ $selectors = null;
402
+ while ($env !== null) {
403
+ if (isset($env->selectors)) {
404
+ $selectors = $env->selectors;
405
+ break;
406
+ }
407
+ $env = $env->parent;
408
+ }
409
+
410
+ return $selectors;
411
+ }
412
+
413
+
414
+ // multiply $selectors against the nearest selectors in env
415
+ protected function multiplySelectors($selectors) {
416
+ // find parent selectors
417
+
418
+ $parentSelectors = $this->findClosestSelectors();
419
+ if (is_null($parentSelectors)) {
420
+ // kill parent reference in top level selector
421
+ foreach ($selectors as &$s) {
422
+ $this->expandParentSelectors($s, "");
423
+ }
424
+
425
+ return $selectors;
426
+ }
427
+
428
+ $out = array();
429
+ foreach ($parentSelectors as $parent) {
430
+ foreach ($selectors as $child) {
431
+ $count = $this->expandParentSelectors($child, $parent);
432
+
433
+ // don't prepend the parent tag if & was used
434
+ if ($count > 0) {
435
+ $out[] = trim($child);
436
+ } else {
437
+ $out[] = trim($parent . ' ' . $child);
438
+ }
439
+ }
440
+ }
441
+
442
+ return $out;
443
+ }
444
+
445
+ // reduces selector expressions
446
+ protected function compileSelectors($selectors) {
447
+ $out = array();
448
+
449
+ foreach ($selectors as $s) {
450
+ if (is_array($s)) {
451
+ list(, $value) = $s;
452
+ $out[] = trim($this->compileValue($this->reduce($value)));
453
+ } else {
454
+ $out[] = $s;
455
+ }
456
+ }
457
+
458
+ return $out;
459
+ }
460
+
461
+ protected function eq($left, $right) {
462
+ return $left == $right;
463
+ }
464
+
465
+ protected function patternMatch($block, $orderedArgs, $keywordArgs) {
466
+ // match the guards if it has them
467
+ // any one of the groups must have all its guards pass for a match
468
+ if (!empty($block->guards)) {
469
+ $groupPassed = false;
470
+ foreach ($block->guards as $guardGroup) {
471
+ foreach ($guardGroup as $guard) {
472
+ $this->pushEnv();
473
+ $this->zipSetArgs($block->args, $orderedArgs, $keywordArgs);
474
+
475
+ $negate = false;
476
+ if ($guard[0] == "negate") {
477
+ $guard = $guard[1];
478
+ $negate = true;
479
+ }
480
+
481
+ $passed = $this->reduce($guard) == self::$TRUE;
482
+ if ($negate) $passed = !$passed;
483
+
484
+ $this->popEnv();
485
+
486
+ if ($passed) {
487
+ $groupPassed = true;
488
+ } else {
489
+ $groupPassed = false;
490
+ break;
491
+ }
492
+ }
493
+
494
+ if ($groupPassed) break;
495
+ }
496
+
497
+ if (!$groupPassed) {
498
+ return false;
499
+ }
500
+ }
501
+
502
+ if (empty($block->args)) {
503
+ return $block->isVararg || empty($orderedArgs) && empty($keywordArgs);
504
+ }
505
+
506
+ $remainingArgs = $block->args;
507
+ if ($keywordArgs) {
508
+ $remainingArgs = array();
509
+ foreach ($block->args as $arg) {
510
+ if ($arg[0] == "arg" && isset($keywordArgs[$arg[1]])) {
511
+ continue;
512
+ }
513
+
514
+ $remainingArgs[] = $arg;
515
+ }
516
+ }
517
+
518
+ $i = -1; // no args
519
+ // try to match by arity or by argument literal
520
+ foreach ($remainingArgs as $i => $arg) {
521
+ switch ($arg[0]) {
522
+ case "lit":
523
+ if (empty($orderedArgs[$i]) || !$this->eq($arg[1], $orderedArgs[$i])) {
524
+ return false;
525
+ }
526
+ break;
527
+ case "arg":
528
+ // no arg and no default value
529
+ if (!isset($orderedArgs[$i]) && !isset($arg[2])) {
530
+ return false;
531
+ }
532
+ break;
533
+ case "rest":
534
+ $i--; // rest can be empty
535
+ break 2;
536
+ }
537
+ }
538
+
539
+ if ($block->isVararg) {
540
+ return true; // not having enough is handled above
541
+ } else {
542
+ $numMatched = $i + 1;
543
+ // greater than becuase default values always match
544
+ return $numMatched >= count($orderedArgs);
545
+ }
546
+ }
547
+
548
+ protected function patternMatchAll($blocks, $orderedArgs, $keywordArgs, $skip=array()) {
549
+ $matches = null;
550
+ foreach ($blocks as $block) {
551
+ // skip seen blocks that don't have arguments
552
+ if (isset($skip[$block->id]) && !isset($block->args)) {
553
+ continue;
554
+ }
555
+
556
+ if ($this->patternMatch($block, $orderedArgs, $keywordArgs)) {
557
+ $matches[] = $block;
558
+ }
559
+ }
560
+
561
+ return $matches;
562
+ }
563
+
564
+ // attempt to find blocks matched by path and args
565
+ protected function findBlocks($searchIn, $path, $orderedArgs, $keywordArgs, $seen=array()) {
566
+ if ($searchIn == null) return null;
567
+ if (isset($seen[$searchIn->id])) return null;
568
+ $seen[$searchIn->id] = true;
569
+
570
+ $name = $path[0];
571
+
572
+ if (isset($searchIn->children[$name])) {
573
+ $blocks = $searchIn->children[$name];
574
+ if (count($path) == 1) {
575
+ $matches = $this->patternMatchAll($blocks, $orderedArgs, $keywordArgs, $seen);
576
+ if (!empty($matches)) {
577
+ // This will return all blocks that match in the closest
578
+ // scope that has any matching block, like lessjs
579
+ return $matches;
580
+ }
581
+ } else {
582
+ $matches = array();
583
+ foreach ($blocks as $subBlock) {
584
+ $subMatches = $this->findBlocks($subBlock,
585
+ array_slice($path, 1), $orderedArgs, $keywordArgs, $seen);
586
+
587
+ if (!is_null($subMatches)) {
588
+ foreach ($subMatches as $sm) {
589
+ $matches[] = $sm;
590
+ }
591
+ }
592
+ }
593
+
594
+ return count($matches) > 0 ? $matches : null;
595
+ }
596
+ }
597
+ if ($searchIn->parent === $searchIn) return null;
598
+ return $this->findBlocks($searchIn->parent, $path, $orderedArgs, $keywordArgs, $seen);
599
+ }
600
+
601
+ // sets all argument names in $args to either the default value
602
+ // or the one passed in through $values
603
+ protected function zipSetArgs($args, $orderedValues, $keywordValues) {
604
+ $assignedValues = array();
605
+
606
+ $i = 0;
607
+ foreach ($args as $a) {
608
+ if ($a[0] == "arg") {
609
+ if (isset($keywordValues[$a[1]])) {
610
+ // has keyword arg
611
+ $value = $keywordValues[$a[1]];
612
+ } elseif (isset($orderedValues[$i])) {
613
+ // has ordered arg
614
+ $value = $orderedValues[$i];
615
+ $i++;
616
+ } elseif (isset($a[2])) {
617
+ // has default value
618
+ $value = $a[2];
619
+ } else {
620
+ $this->throwError("Failed to assign arg " . $a[1]);
621
+ $value = null; // :(
622
+ }
623
+
624
+ $value = $this->reduce($value);
625
+ $this->set($a[1], $value);
626
+ $assignedValues[] = $value;
627
+ } else {
628
+ // a lit
629
+ $i++;
630
+ }
631
+ }
632
+
633
+ // check for a rest
634
+ $last = end($args);
635
+ if ($last[0] == "rest") {
636
+ $rest = array_slice($orderedValues, count($args) - 1);
637
+ $this->set($last[1], $this->reduce(array("list", " ", $rest)));
638
+ }
639
+
640
+ // wow is this the only true use of PHP's + operator for arrays?
641
+ $this->env->arguments = $assignedValues + $orderedValues;
642
+ }
643
+
644
+ // compile a prop and update $lines or $blocks appropriately
645
+ protected function compileProp($prop, $block, $out) {
646
+ // set error position context
647
+ $this->sourceLoc = isset($prop[-1]) ? $prop[-1] : -1;
648
+
649
+ switch ($prop[0]) {
650
+ case 'assign':
651
+ list(, $name, $value) = $prop;
652
+ if ($name[0] == $this->vPrefix) {
653
+ $this->set($name, $value);
654
+ } else {
655
+ $out->lines[] = $this->formatter->property($name,
656
+ $this->compileValue($this->reduce($value)));
657
+ }
658
+ break;
659
+ case 'block':
660
+ list(, $child) = $prop;
661
+ $this->compileBlock($child);
662
+ break;
663
+ case 'mixin':
664
+ list(, $path, $args, $suffix) = $prop;
665
+
666
+ $orderedArgs = array();
667
+ $keywordArgs = array();
668
+ foreach ((array)$args as $arg) {
669
+ $argval = null;
670
+ switch ($arg[0]) {
671
+ case "arg":
672
+ if (!isset($arg[2])) {
673
+ $orderedArgs[] = $this->reduce(array("variable", $arg[1]));
674
+ } else {
675
+ $keywordArgs[$arg[1]] = $this->reduce($arg[2]);
676
+ }
677
+ break;
678
+
679
+ case "lit":
680
+ $orderedArgs[] = $this->reduce($arg[1]);
681
+ break;
682
+ default:
683
+ $this->throwError("Unknown arg type: " . $arg[0]);
684
+ }
685
+ }
686
+
687
+ $mixins = $this->findBlocks($block, $path, $orderedArgs, $keywordArgs);
688
+
689
+ if ($mixins === null) {
690
+ break; // throw error here??
691
+ }
692
+
693
+ foreach ($mixins as $mixin) {
694
+ if ($mixin === $block && !$orderedArgs) {
695
+ continue;
696
+ }
697
+
698
+ $haveScope = false;
699
+ if (isset($mixin->parent->scope)) {
700
+ $haveScope = true;
701
+ $mixinParentEnv = $this->pushEnv();
702
+ $mixinParentEnv->storeParent = $mixin->parent->scope;
703
+ }
704
+
705
+ $haveArgs = false;
706
+ if (isset($mixin->args)) {
707
+ $haveArgs = true;
708
+ $this->pushEnv();
709
+ $this->zipSetArgs($mixin->args, $orderedArgs, $keywordArgs);
710
+ }
711
+
712
+ $oldParent = $mixin->parent;
713
+ if ($mixin != $block) $mixin->parent = $block;
714
+
715
+ foreach ($this->sortProps($mixin->props) as $subProp) {
716
+ if ($suffix !== null &&
717
+ $subProp[0] == "assign" &&
718
+ is_string($subProp[1]) &&
719
+ $subProp[1]{0} != $this->vPrefix)
720
+ {
721
+ $subProp[2] = array(
722
+ 'list', ' ',
723
+ array($subProp[2], array('keyword', $suffix))
724
+ );
725
+ }
726
+
727
+ $this->compileProp($subProp, $mixin, $out);
728
+ }
729
+
730
+ $mixin->parent = $oldParent;
731
+
732
+ if ($haveArgs) $this->popEnv();
733
+ if ($haveScope) $this->popEnv();
734
+ }
735
+
736
+ break;
737
+ case 'raw':
738
+ $out->lines[] = $prop[1];
739
+ break;
740
+ case "directive":
741
+ list(, $name, $value) = $prop;
742
+ $out->lines[] = "@$name " . $this->compileValue($this->reduce($value)).';';
743
+ break;
744
+ case "comment":
745
+ $out->lines[] = $prop[1];
746
+ break;
747
+ case "import";
748
+ list(, $importPath, $importId) = $prop;
749
+ $importPath = $this->reduce($importPath);
750
+
751
+ if (!isset($this->env->imports)) {
752
+ $this->env->imports = array();
753
+ }
754
+
755
+ $result = $this->tryImport($importPath, $block, $out);
756
+
757
+ $this->env->imports[$importId] = $result === false ?
758
+ array(false, "@import " . $this->compileValue($importPath).";") :
759
+ $result;
760
+
761
+ break;
762
+ case "import_mixin":
763
+ list(,$importId) = $prop;
764
+ $import = $this->env->imports[$importId];
765
+ if ($import[0] === false) {
766
+ if (isset($import[1])) {
767
+ $out->lines[] = $import[1];
768
+ }
769
+ } else {
770
+ list(, $bottom, $parser, $importDir) = $import;
771
+ $this->compileImportedProps($bottom, $block, $out, $parser, $importDir);
772
+ }
773
+
774
+ break;
775
+ default:
776
+ $this->throwError("unknown op: {$prop[0]}\n");
777
+ }
778
+ }
779
+
780
+
781
+ /**
782
+ * Compiles a primitive value into a CSS property value.
783
+ *
784
+ * Values in lessphp are typed by being wrapped in arrays, their format is
785
+ * typically:
786
+ *
787
+ * array(type, contents [, additional_contents]*)
788
+ *
789
+ * The input is expected to be reduced. This function will not work on
790
+ * things like expressions and variables.
791
+ */
792
+ protected function compileValue($value) {
793
+ switch ($value[0]) {
794
+ case 'list':
795
+ // [1] - delimiter
796
+ // [2] - array of values
797
+ return implode($value[1], array_map(array($this, 'compileValue'), $value[2]));
798
+ case 'raw_color':
799
+ if (!empty($this->formatter->compressColors)) {
800
+ return $this->compileValue($this->coerceColor($value));
801
+ }
802
+ return $value[1];
803
+ case 'keyword':
804
+ // [1] - the keyword
805
+ return $value[1];
806
+ case 'number':
807
+ list(, $num, $unit) = $value;
808
+ // [1] - the number
809
+ // [2] - the unit
810
+ if ($this->numberPrecision !== null) {
811
+ $num = round($num, $this->numberPrecision);
812
+ }
813
+ return $num . $unit;
814
+ case 'string':
815
+ // [1] - contents of string (includes quotes)
816
+ list(, $delim, $content) = $value;
817
+ foreach ($content as &$part) {
818
+ if (is_array($part)) {
819
+ $part = $this->compileValue($part);
820
+ }
821
+ }
822
+ return $delim . implode($content) . $delim;
823
+ case 'color':
824
+ // [1] - red component (either number or a %)
825
+ // [2] - green component
826
+ // [3] - blue component
827
+ // [4] - optional alpha component
828
+ list(, $r, $g, $b) = $value;
829
+ $r = round($r);
830
+ $g = round($g);
831
+ $b = round($b);
832
+
833
+ if (count($value) == 5 && $value[4] != 1) { // rgba
834
+ return 'rgba('.$r.','.$g.','.$b.','.$value[4].')';
835
+ }
836
+
837
+ $h = sprintf("#%02x%02x%02x", $r, $g, $b);
838
+
839
+ if (!empty($this->formatter->compressColors)) {
840
+ // Converting hex color to short notation (e.g. #003399 to #039)
841
+ if ($h[1] === $h[2] && $h[3] === $h[4] && $h[5] === $h[6]) {
842
+ $h = '#' . $h[1] . $h[3] . $h[5];
843
+ }
844
+ }
845
+
846
+ return $h;
847
+
848
+ case 'function':
849
+ list(, $name, $args) = $value;
850
+ return $name.'('.$this->compileValue($args).')';
851
+ default: // assumed to be unit
852
+ $this->throwError("unknown value type: $value[0]");
853
+ }
854
+ }
855
+
856
+ protected function lib_pow($args) {
857
+ list($base, $exp) = $this->assertArgs($args, 2, "pow");
858
+ return pow($this->assertNumber($base), $this->assertNumber($exp));
859
+ }
860
+
861
+ protected function lib_pi() {
862
+ return pi();
863
+ }
864
+
865
+ protected function lib_mod($args) {
866
+ list($a, $b) = $this->assertArgs($args, 2, "mod");
867
+ return $this->assertNumber($a) % $this->assertNumber($b);
868
+ }
869
+
870
+ protected function lib_tan($num) {
871
+ return tan($this->assertNumber($num));
872
+ }
873
+
874
+ protected function lib_sin($num) {
875
+ return sin($this->assertNumber($num));
876
+ }
877
+
878
+ protected function lib_cos($num) {
879
+ return cos($this->assertNumber($num));
880
+ }
881
+
882
+ protected function lib_atan($num) {
883
+ $num = atan($this->assertNumber($num));
884
+ return array("number", $num, "rad");
885
+ }
886
+
887
+ protected function lib_asin($num) {
888
+ $num = asin($this->assertNumber($num));
889
+ return array("number", $num, "rad");
890
+ }
891
+
892
+ protected function lib_acos($num) {
893
+ $num = acos($this->assertNumber($num));
894
+ return array("number", $num, "rad");
895
+ }
896
+
897
+ protected function lib_sqrt($num) {
898
+ return sqrt($this->assertNumber($num));
899
+ }
900
+
901
+ protected function lib_extract($value) {
902
+ list($list, $idx) = $this->assertArgs($value, 2, "extract");
903
+ $idx = $this->assertNumber($idx);
904
+ // 1 indexed
905
+ if ($list[0] == "list" && isset($list[2][$idx - 1])) {
906
+ return $list[2][$idx - 1];
907
+ }
908
+ }
909
+
910
+ protected function lib_isnumber($value) {
911
+ return $this->toBool($value[0] == "number");
912
+ }
913
+
914
+ protected function lib_isstring($value) {
915
+ return $this->toBool($value[0] == "string");
916
+ }
917
+
918
+ protected function lib_iscolor($value) {
919
+ return $this->toBool($this->coerceColor($value));
920
+ }
921
+
922
+ protected function lib_iskeyword($value) {
923
+ return $this->toBool($value[0] == "keyword");
924
+ }
925
+
926
+ protected function lib_ispixel($value) {
927
+ return $this->toBool($value[0] == "number" && $value[2] == "px");
928
+ }
929
+
930
+ protected function lib_ispercentage($value) {
931
+ return $this->toBool($value[0] == "number" && $value[2] == "%");
932
+ }
933
+
934
+ protected function lib_isem($value) {
935
+ return $this->toBool($value[0] == "number" && $value[2] == "em");
936
+ }
937
+
938
+ protected function lib_isrem($value) {
939
+ return $this->toBool($value[0] == "number" && $value[2] == "rem");
940
+ }
941
+
942
+ protected function lib_rgbahex($color) {
943
+ $color = $this->coerceColor($color);
944
+ if (is_null($color))
945
+ $this->throwError("color expected for rgbahex");
946
+
947
+ return sprintf("#%02x%02x%02x%02x",
948
+ isset($color[4]) ? $color[4]*255 : 255,
949
+ $color[1],$color[2], $color[3]);
950
+ }
951
+
952
+ protected function lib_argb($color){
953
+ return $this->lib_rgbahex($color);
954
+ }
955
+
956
+ // utility func to unquote a string
957
+ // use func_get_arg to prevent Theme Checker triggering unrelated translation warning.
958
+ protected function lib_e() {
959
+ $arg = func_get_arg(0);
960
+ switch ($arg[0]) {
961
+ case "list":
962
+ $items = $arg[2];
963
+ if (isset($items[0])) {
964
+ return $this->lib_unquote($items[0]);
965
+ }
966
+ return self::$defaultValue;
967
+ case "string":
968
+ $arg[1] = "";
969
+ return $arg;
970
+ case "keyword":
971
+ return $arg;
972
+ default:
973
+ return array("keyword", $this->compileValue($arg));
974
+ }
975
+ }
976
+
977
+ // use func_get_arg to prevent Theme Checker triggering unrelated translation warning.
978
+ protected function lib_unquote($arg) {
979
+ return $this->lib_e(func_get_arg(0));
980
+ }
981
+
982
+ protected function lib__sprintf($args) {
983
+ if ($args[0] != "list") return $args;
984
+ $values = $args[2];
985
+ $string = array_shift($values);
986
+ $template = $this->compileValue($this->lib_unquote($string));
987
+
988
+ $i = 0;
989
+ if (preg_match_all('/%[dsa]/', $template, $m)) {
990
+ foreach ($m[0] as $match) {
991
+ $val = isset($values[$i]) ?
992
+ $this->reduce($values[$i]) : array('keyword', '');
993
+
994
+ // lessjs compat, renders fully expanded color, not raw color
995
+ if ($color = $this->coerceColor($val)) {
996
+ $val = $color;
997
+ }
998
+
999
+ $i++;
1000
+ $rep = $this->compileValue($this->lib_unquote($val));
1001
+ $template = preg_replace('/'.self::preg_quote($match).'/',
1002
+ $rep, $template, 1);
1003
+ }
1004
+ }
1005
+
1006
+ $d = $string[0] == "string" ? $string[1] : '"';
1007
+ return array("string", $d, array($template));
1008
+ }
1009
+
1010
+ protected function lib_floor($arg) {
1011
+ $value = $this->assertNumber($arg);
1012
+ return array("number", floor($value), $arg[2]);
1013
+ }
1014
+
1015
+ protected function lib_ceil($arg) {
1016
+ $value = $this->assertNumber($arg);
1017
+ return array("number", ceil($value), $arg[2]);
1018
+ }
1019
+
1020
+ protected function lib_round($arg) {
1021
+ $value = $this->assertNumber($arg);
1022
+ return array("number", round($value), $arg[2]);
1023
+ }
1024
+
1025
+ protected function lib_unit($arg) {
1026
+ if ($arg[0] == "list") {
1027
+ list($number, $newUnit) = $arg[2];
1028
+ return array("number", $this->assertNumber($number),
1029
+ $this->compileValue($this->lib_unquote($newUnit)));
1030
+ } else {
1031
+ return array("number", $this->assertNumber($arg), "");
1032
+ }
1033
+ }
1034
+
1035
+ /**
1036
+ * Helper function to get arguments for color manipulation functions.
1037
+ * takes a list that contains a color like thing and a percentage
1038
+ */
1039
+ protected function colorArgs($args) {
1040
+ if ($args[0] != 'list' || count($args[2]) < 2) {
1041
+ return array(array('color', 0, 0, 0), 0);
1042
+ }
1043
+ list($color, $delta) = $args[2];
1044
+ $color = $this->assertColor($color);
1045
+ $delta = floatval($delta[1]);
1046
+
1047
+ return array($color, $delta);
1048
+ }
1049
+
1050
+ protected function lib_darken($args) {
1051
+ list($color, $delta) = $this->colorArgs($args);
1052
+
1053
+ $hsl = $this->toHSL($color);
1054
+ $hsl[3] = $this->clamp($hsl[3] - $delta, 100);
1055
+ return $this->toRGB($hsl);
1056
+ }
1057
+
1058
+ protected function lib_lighten($args) {
1059
+ list($color, $delta) = $this->colorArgs($args);
1060
+
1061
+ $hsl = $this->toHSL($color);
1062
+ $hsl[3] = $this->clamp($hsl[3] + $delta, 100);
1063
+ return $this->toRGB($hsl);
1064
+ }
1065
+
1066
+ protected function lib_saturate($args) {
1067
+ list($color, $delta) = $this->colorArgs($args);
1068
+
1069
+ $hsl = $this->toHSL($color);
1070
+ $hsl[2] = $this->clamp($hsl[2] + $delta, 100);
1071
+ return $this->toRGB($hsl);
1072
+ }
1073
+
1074
+ protected function lib_desaturate($args) {
1075
+ list($color, $delta) = $this->colorArgs($args);
1076
+
1077
+ $hsl = $this->toHSL($color);
1078
+ $hsl[2] = $this->clamp($hsl[2] - $delta, 100);
1079
+ return $this->toRGB($hsl);
1080
+ }
1081
+
1082
+ protected function lib_spin($args) {
1083
+ list($color, $delta) = $this->colorArgs($args);
1084
+
1085
+ $hsl = $this->toHSL($color);
1086
+
1087
+ $hsl[1] = $hsl[1] + $delta % 360;
1088
+ if ($hsl[1] < 0) $hsl[1] += 360;
1089
+
1090
+ return $this->toRGB($hsl);
1091
+ }
1092
+
1093
+ protected function lib_fadeout($args) {
1094
+ list($color, $delta) = $this->colorArgs($args);
1095
+ $color[4] = $this->clamp((isset($color[4]) ? $color[4] : 1) - $delta/100);
1096
+ return $color;
1097
+ }
1098
+
1099
+ protected function lib_fadein($args) {
1100
+ list($color, $delta) = $this->colorArgs($args);
1101
+ $color[4] = $this->clamp((isset($color[4]) ? $color[4] : 1) + $delta/100);
1102
+ return $color;
1103
+ }
1104
+
1105
+ protected function lib_hue($color) {
1106
+ $hsl = $this->toHSL($this->assertColor($color));
1107
+ return round($hsl[1]);
1108
+ }
1109
+
1110
+ protected function lib_saturation($color) {
1111
+ $hsl = $this->toHSL($this->assertColor($color));
1112
+ return round($hsl[2]);
1113
+ }
1114
+
1115
+ protected function lib_lightness($color) {
1116
+ $hsl = $this->toHSL($this->assertColor($color));
1117
+ return round($hsl[3]);
1118
+ }
1119
+
1120
+ // get the alpha of a color
1121
+ // defaults to 1 for non-colors or colors without an alpha
1122
+ protected function lib_alpha($value) {
1123
+ if (!is_null($color = $this->coerceColor($value))) {
1124
+ return isset($color[4]) ? $color[4] : 1;
1125
+ }
1126
+ }
1127
+
1128
+ // set the alpha of the color
1129
+ protected function lib_fade($args) {
1130
+ list($color, $alpha) = $this->colorArgs($args);
1131
+ $color[4] = $this->clamp($alpha / 100.0);
1132
+ return $color;
1133
+ }
1134
+
1135
+ protected function lib_percentage($arg) {
1136
+ $num = $this->assertNumber($arg);
1137
+ return array("number", $num*100, "%");
1138
+ }
1139
+
1140
+ // mixes two colors by weight
1141
+ // mix(@color1, @color2, [@weight: 50%]);
1142
+ // http://sass-lang.com/docs/yardoc/Sass/Script/Functions.html#mix-instance_method
1143
+ protected function lib_mix($args) {
1144
+ if ($args[0] != "list" || count($args[2]) < 2)
1145
+ $this->throwError("mix expects (color1, color2, weight)");
1146
+
1147
+ list($first, $second) = $args[2];
1148
+ $first = $this->assertColor($first);
1149
+ $second = $this->assertColor($second);
1150
+
1151
+ $first_a = $this->lib_alpha($first);
1152
+ $second_a = $this->lib_alpha($second);
1153
+
1154
+ if (isset($args[2][2])) {
1155
+ $weight = $args[2][2][1] / 100.0;
1156
+ } else {
1157
+ $weight = 0.5;
1158
+ }
1159
+
1160
+ $w = $weight * 2 - 1;
1161
+ $a = $first_a - $second_a;
1162
+
1163
+ $w1 = (($w * $a == -1 ? $w : ($w + $a)/(1 + $w * $a)) + 1) / 2.0;
1164
+ $w2 = 1.0 - $w1;
1165
+
1166
+ $new = array('color',
1167
+ $w1 * $first[1] + $w2 * $second[1],
1168
+ $w1 * $first[2] + $w2 * $second[2],
1169
+ $w1 * $first[3] + $w2 * $second[3],
1170
+ );
1171
+
1172
+ if ($first_a != 1.0 || $second_a != 1.0) {
1173
+ $new[] = $first_a * $weight + $second_a * ($weight - 1);
1174
+ }
1175
+
1176
+ return $this->fixColor($new);
1177
+ }
1178
+
1179
+ protected function lib_contrast($args) {
1180
+ if ($args[0] != 'list' || count($args[2]) < 3) {
1181
+ return array(array('color', 0, 0, 0), 0);
1182
+ }
1183
+
1184
+ list($inputColor, $darkColor, $lightColor) = $args[2];
1185
+
1186
+ $inputColor = $this->assertColor($inputColor);
1187
+ $darkColor = $this->assertColor($darkColor);
1188
+ $lightColor = $this->assertColor($lightColor);
1189
+ $hsl = $this->toHSL($inputColor);
1190
+
1191
+ if ($hsl[3] > 50) {
1192
+ return $darkColor;
1193
+ }
1194
+
1195
+ return $lightColor;
1196
+ }
1197
+
1198
+ protected function assertColor($value, $error = "expected color value") {
1199
+ $color = $this->coerceColor($value);
1200
+ if (is_null($color)) $this->throwError($error);
1201
+ return $color;
1202
+ }
1203
+
1204
+ protected function assertNumber($value, $error = "expecting number") {
1205
+ if ($value[0] == "number") return $value[1];
1206
+ $this->throwError($error);
1207
+ }
1208
+
1209
+ protected function assertArgs($value, $expectedArgs, $name="") {
1210
+ if ($expectedArgs == 1) {
1211
+ return $value;
1212
+ } else {
1213
+ if ($value[0] !== "list" || $value[1] != ",") $this->throwError("expecting list");
1214
+ $values = $value[2];
1215
+ $numValues = count($values);
1216
+ if ($expectedArgs != $numValues) {
1217
+ if ($name) {
1218
+ $name = $name . ": ";
1219
+ }
1220
+
1221
+ $this->throwError("${name}expecting $expectedArgs arguments, got $numValues");
1222
+ }
1223
+
1224
+ return $values;
1225
+ }
1226
+ }
1227
+
1228
+ protected function toHSL($color) {
1229
+ if ($color[0] == 'hsl') return $color;
1230
+
1231
+ $r = $color[1] / 255;
1232
+ $g = $color[2] / 255;
1233
+ $b = $color[3] / 255;
1234
+
1235
+ $min = min($r, $g, $b);
1236
+ $max = max($r, $g, $b);
1237
+
1238
+ $L = ($min + $max) / 2;
1239
+ if ($min == $max) {
1240
+ $S = $H = 0;
1241
+ } else {
1242
+ if ($L < 0.5)
1243
+ $S = ($max - $min)/($max + $min);
1244
+ else
1245
+ $S = ($max - $min)/(2.0 - $max - $min);
1246
+
1247
+ if ($r == $max) $H = ($g - $b)/($max - $min);
1248
+ elseif ($g == $max) $H = 2.0 + ($b - $r)/($max - $min);
1249
+ elseif ($b == $max) $H = 4.0 + ($r - $g)/($max - $min);
1250
+
1251
+ }
1252
+
1253
+ $out = array('hsl',
1254
+ ($H < 0 ? $H + 6 : $H)*60,
1255
+ $S*100,
1256
+ $L*100,
1257
+ );
1258
+
1259
+ if (count($color) > 4) $out[] = $color[4]; // copy alpha
1260
+ return $out;
1261
+ }
1262
+
1263
+ protected function toRGB_helper($comp, $temp1, $temp2) {
1264
+ if ($comp < 0) $comp += 1.0;
1265
+ elseif ($comp > 1) $comp -= 1.0;
1266
+
1267
+ if (6 * $comp < 1) return $temp1 + ($temp2 - $temp1) * 6 * $comp;
1268
+ if (2 * $comp < 1) return $temp2;
1269
+ if (3 * $comp < 2) return $temp1 + ($temp2 - $temp1)*((2/3) - $comp) * 6;
1270
+
1271
+ return $temp1;
1272
+ }
1273
+
1274
+ /**
1275
+ * Converts a hsl array into a color value in rgb.
1276
+ * Expects H to be in range of 0 to 360, S and L in 0 to 100
1277
+ */
1278
+ protected function toRGB($color) {
1279
+ if ($color[0] == 'color') return $color;
1280
+
1281
+ $H = $color[1] / 360;
1282
+ $S = $color[2] / 100;
1283
+ $L = $color[3] / 100;
1284
+
1285
+ if ($S == 0) {
1286
+ $r = $g = $b = $L;
1287
+ } else {
1288
+ $temp2 = $L < 0.5 ?
1289
+ $L*(1.0 + $S) :
1290
+ $L + $S - $L * $S;
1291
+
1292
+ $temp1 = 2.0 * $L - $temp2;
1293
+
1294
+ $r = $this->toRGB_helper($H + 1/3, $temp1, $temp2);
1295
+ $g = $this->toRGB_helper($H, $temp1, $temp2);
1296
+ $b = $this->toRGB_helper($H - 1/3, $temp1, $temp2);
1297
+ }
1298
+
1299
+ // $out = array('color', round($r*255), round($g*255), round($b*255));
1300
+ $out = array('color', $r*255, $g*255, $b*255);
1301
+ if (count($color) > 4) $out[] = $color[4]; // copy alpha
1302
+ return $out;
1303
+ }
1304
+
1305
+ protected function clamp($v, $max = 1, $min = 0) {
1306
+ return min($max, max($min, $v));
1307
+ }
1308
+
1309
+ /**
1310
+ * Convert the rgb, rgba, hsl color literals of function type
1311
+ * as returned by the parser into values of color type.
1312
+ */
1313
+ protected function funcToColor($func) {
1314
+ $fname = $func[1];
1315
+ if ($func[2][0] != 'list') return false; // need a list of arguments
1316
+ $rawComponents = $func[2][2];
1317
+
1318
+ if ($fname == 'hsl' || $fname == 'hsla') {
1319
+ $hsl = array('hsl');
1320
+ $i = 0;
1321
+ foreach ($rawComponents as $c) {
1322
+ $val = $this->reduce($c);
1323
+ $val = isset($val[1]) ? floatval($val[1]) : 0;
1324
+
1325
+ if ($i == 0) $clamp = 360;
1326
+ elseif ($i < 3) $clamp = 100;
1327
+ else $clamp = 1;
1328
+
1329
+ $hsl[] = $this->clamp($val, $clamp);
1330
+ $i++;
1331
+ }
1332
+
1333
+ while (count($hsl) < 4) $hsl[] = 0;
1334
+ return $this->toRGB($hsl);
1335
+
1336
+ } elseif ($fname == 'rgb' || $fname == 'rgba') {
1337
+ $components = array();
1338
+ $i = 1;
1339
+ foreach ($rawComponents as $c) {
1340
+ $c = $this->reduce($c);
1341
+ if ($i < 4) {
1342
+ if ($c[0] == "number" && $c[2] == "%") {
1343
+ $components[] = 255 * ($c[1] / 100);
1344
+ } else {
1345
+ $components[] = floatval($c[1]);
1346
+ }
1347
+ } elseif ($i == 4) {
1348
+ if ($c[0] == "number" && $c[2] == "%") {
1349
+ $components[] = 1.0 * ($c[1] / 100);
1350
+ } else {
1351
+ $components[] = floatval($c[1]);
1352
+ }
1353
+ } else break;
1354
+
1355
+ $i++;
1356
+ }
1357
+ while (count($components) < 3) $components[] = 0;
1358
+ array_unshift($components, 'color');
1359
+ return $this->fixColor($components);
1360
+ }
1361
+
1362
+ return false;
1363
+ }
1364
+
1365
+ protected function reduce($value, $forExpression = false) {
1366
+ switch ($value[0]) {
1367
+ case "interpolate":
1368
+ $reduced = $this->reduce($value[1]);
1369
+ $var = $this->compileValue($reduced);
1370
+ $res = $this->reduce(array("variable", $this->vPrefix . $var));
1371
+
1372
+ if ($res[0] == "raw_color") {
1373
+ $res = $this->coerceColor($res);
1374
+ }
1375
+
1376
+ if (empty($value[2])) $res = $this->lib_unquote($res);
1377
+
1378
+ return $res;
1379
+ case "variable":
1380
+ $key = $value[1];
1381
+ if (is_array($key)) {
1382
+ $key = $this->reduce($key);
1383
+ $key = $this->vPrefix . $this->compileValue($this->lib_unquote($key));
1384
+ }
1385
+
1386
+ $seen =& $this->env->seenNames;
1387
+
1388
+ if (!empty($seen[$key])) {
1389
+ $this->throwError("infinite loop detected: $key");
1390
+ }
1391
+
1392
+ $seen[$key] = true;
1393
+ $out = $this->reduce($this->get($key, self::$defaultValue));
1394
+ $seen[$key] = false;
1395
+ return $out;
1396
+ case "list":
1397
+ foreach ($value[2] as &$item) {
1398
+ $item = $this->reduce($item, $forExpression);
1399
+ }
1400
+ return $value;
1401
+ case "expression":
1402
+ return $this->evaluate($value);
1403
+ case "string":
1404
+ foreach ($value[2] as &$part) {
1405
+ if (is_array($part)) {
1406
+ $strip = $part[0] == "variable";
1407
+ $part = $this->reduce($part);
1408
+ if ($strip) $part = $this->lib_unquote($part);
1409
+ }
1410
+ }
1411
+ return $value;
1412
+ case "escape":
1413
+ list(,$inner) = $value;
1414
+ return $this->lib_unquote($this->reduce($inner));
1415
+ case "function":
1416
+ $color = $this->funcToColor($value);
1417
+ if ($color) return $color;
1418
+
1419
+ list(, $name, $args) = $value;
1420
+ if ($name == "%") $name = "_sprintf";
1421
+ $f = isset($this->libFunctions[$name]) ?
1422
+ $this->libFunctions[$name] : array($this, 'lib_'.$name);
1423
+
1424
+ if (is_callable($f)) {
1425
+ if ($args[0] == 'list')
1426
+ $args = self::compressList($args[2], $args[1]);
1427
+
1428
+ $ret = call_user_func($f, $this->reduce($args, true), $this);
1429
+
1430
+ if (is_null($ret)) {
1431
+ return array("string", "", array(
1432
+ $name, "(", $args, ")"
1433
+ ));
1434
+ }
1435
+
1436
+ // convert to a typed value if the result is a php primitive
1437
+ if (is_numeric($ret)) $ret = array('number', $ret, "");
1438
+ elseif (!is_array($ret)) $ret = array('keyword', $ret);
1439
+
1440
+ return $ret;
1441
+ }
1442
+
1443
+ // plain function, reduce args
1444
+ $value[2] = $this->reduce($value[2]);
1445
+ return $value;
1446
+ case "unary":
1447
+ list(, $op, $exp) = $value;
1448
+ $exp = $this->reduce($exp);
1449
+
1450
+ if ($exp[0] == "number") {
1451
+ switch ($op) {
1452
+ case "+":
1453
+ return $exp;
1454
+ case "-":
1455
+ $exp[1] *= -1;
1456
+ return $exp;
1457
+ }
1458
+ }
1459
+ return array("string", "", array($op, $exp));
1460
+ }
1461
+
1462
+ if ($forExpression) {
1463
+ switch ($value[0]) {
1464
+ case "keyword":
1465
+ if ($color = $this->coerceColor($value)) {
1466
+ return $color;
1467
+ }
1468
+ break;
1469
+ case "raw_color":
1470
+ return $this->coerceColor($value);
1471
+ }
1472
+ }
1473
+
1474
+ return $value;
1475
+ }
1476
+
1477
+
1478
+ // coerce a value for use in color operation
1479
+ protected function coerceColor($value) {
1480
+ switch($value[0]) {
1481
+ case 'color': return $value;
1482
+ case 'raw_color':
1483
+ $c = array("color", 0, 0, 0);
1484
+ $colorStr = substr($value[1], 1);
1485
+ $num = hexdec($colorStr);
1486
+ $width = strlen($colorStr) == 3 ? 16 : 256;
1487
+
1488
+ for ($i = 3; $i > 0; $i--) { // 3 2 1
1489
+ $t = $num % $width;
1490
+ $num /= $width;
1491
+
1492
+ $c[$i] = $t * (256/$width) + $t * floor(16/$width);
1493
+ }
1494
+
1495
+ return $c;
1496
+ case 'keyword':
1497
+ $name = $value[1];
1498
+ if (isset(self::$cssColors[$name])) {
1499
+ $rgba = explode(',', self::$cssColors[$name]);
1500
+
1501
+ if(isset($rgba[3]))
1502
+ return array('color', $rgba[0], $rgba[1], $rgba[2], $rgba[3]);
1503
+
1504
+ return array('color', $rgba[0], $rgba[1], $rgba[2]);
1505
+ }
1506
+ return null;
1507
+ }
1508
+ }
1509
+
1510
+ // make something string like into a string
1511
+ protected function coerceString($value) {
1512
+ switch ($value[0]) {
1513
+ case "string":
1514
+ return $value;
1515
+ case "keyword":
1516
+ return array("string", "", array($value[1]));
1517
+ }
1518
+ return null;
1519
+ }
1520
+
1521
+ // turn list of length 1 into value type
1522
+ protected function flattenList($value) {
1523
+ if ($value[0] == "list" && count($value[2]) == 1) {
1524
+ return $this->flattenList($value[2][0]);
1525
+ }
1526
+ return $value;
1527
+ }
1528
+
1529
+ protected function toBool($a) {
1530
+ if ($a) return self::$TRUE;
1531
+ else return self::$FALSE;
1532
+ }
1533
+
1534
+ // evaluate an expression
1535
+ protected function evaluate($exp) {
1536
+ list(, $op, $left, $right, $whiteBefore, $whiteAfter) = $exp;
1537
+
1538
+ $left = $this->reduce($left, true);
1539
+ $right = $this->reduce($right, true);
1540
+
1541
+ if ($leftColor = $this->coerceColor($left)) {
1542
+ $left = $leftColor;
1543
+ }
1544
+
1545
+ if ($rightColor = $this->coerceColor($right)) {
1546
+ $right = $rightColor;
1547
+ }
1548
+
1549
+ $ltype = $left[0];
1550
+ $rtype = $right[0];
1551
+
1552
+ // operators that work on all types
1553
+ if ($op == "and") {
1554
+ return $this->toBool($left == self::$TRUE && $right == self::$TRUE);
1555
+ }
1556
+
1557
+ if ($op == "=") {
1558
+ return $this->toBool($this->eq($left, $right) );
1559
+ }
1560
+
1561
+ if ($op == "+" && !is_null($str = $this->stringConcatenate($left, $right))) {
1562
+ return $str;
1563
+ }
1564
+
1565
+ // type based operators
1566
+ $fname = "op_${ltype}_${rtype}";
1567
+ if (is_callable(array($this, $fname))) {
1568
+ $out = $this->$fname($op, $left, $right);
1569
+ if (!is_null($out)) return $out;
1570
+ }
1571
+
1572
+ // make the expression look it did before being parsed
1573
+ $paddedOp = $op;
1574
+ if ($whiteBefore) $paddedOp = " " . $paddedOp;
1575
+ if ($whiteAfter) $paddedOp .= " ";
1576
+
1577
+ return array("string", "", array($left, $paddedOp, $right));
1578
+ }
1579
+
1580
+ protected function stringConcatenate($left, $right) {
1581
+ if ($strLeft = $this->coerceString($left)) {
1582
+ if ($right[0] == "string") {
1583
+ $right[1] = "";
1584
+ }
1585
+ $strLeft[2][] = $right;
1586
+ return $strLeft;
1587
+ }
1588
+
1589
+ if ($strRight = $this->coerceString($right)) {
1590
+ array_unshift($strRight[2], $left);
1591
+ return $strRight;
1592
+ }
1593
+ }
1594
+
1595
+
1596
+ // make sure a color's components don't go out of bounds
1597
+ protected function fixColor($c) {
1598
+ foreach (range(1, 3) as $i) {
1599
+ if ($c[$i] < 0) $c[$i] = 0;
1600
+ if ($c[$i] > 255) $c[$i] = 255;
1601
+ }
1602
+
1603
+ return $c;
1604
+ }
1605
+
1606
+ protected function op_number_color($op, $lft, $rgt) {
1607
+ if ($op == '+' || $op == '*') {
1608
+ return $this->op_color_number($op, $rgt, $lft);
1609
+ }
1610
+ }
1611
+
1612
+ protected function op_color_number($op, $lft, $rgt) {
1613
+ if ($rgt[0] == '%') $rgt[1] /= 100;
1614
+
1615
+ return $this->op_color_color($op, $lft,
1616
+ array_fill(1, count($lft) - 1, $rgt[1]));
1617
+ }
1618
+
1619
+ protected function op_color_color($op, $left, $right) {
1620
+ $out = array('color');
1621
+ $max = count($left) > count($right) ? count($left) : count($right);
1622
+ foreach (range(1, $max - 1) as $i) {
1623
+ $lval = isset($left[$i]) ? $left[$i] : 0;
1624
+ $rval = isset($right[$i]) ? $right[$i] : 0;
1625
+ switch ($op) {
1626
+ case '+':
1627
+ $out[] = $lval + $rval;
1628
+ break;
1629
+ case '-':
1630
+ $out[] = $lval - $rval;
1631
+ break;
1632
+ case '*':
1633
+ $out[] = $lval * $rval;
1634
+ break;
1635
+ case '%':
1636
+ $out[] = $lval % $rval;
1637
+ break;
1638
+ case '/':
1639
+ if ($rval == 0) $this->throwError("evaluate error: can't divide by zero");
1640
+ $out[] = $lval / $rval;
1641
+ break;
1642
+ default:
1643
+ $this->throwError('evaluate error: color op number failed on op '.$op);
1644
+ }
1645
+ }
1646
+ return $this->fixColor($out);
1647
+ }
1648
+
1649
+ function lib_red($color){
1650
+ $color = $this->coerceColor($color);
1651
+ if (is_null($color)) {
1652
+ $this->throwError('color expected for red()');
1653
+ }
1654
+
1655
+ return $color[1];
1656
+ }
1657
+
1658
+ function lib_green($color){
1659
+ $color = $this->coerceColor($color);
1660
+ if (is_null($color)) {
1661
+ $this->throwError('color expected for green()');
1662
+ }
1663
+
1664
+ return $color[2];
1665
+ }
1666
+
1667
+ function lib_blue($color){
1668
+ $color = $this->coerceColor($color);
1669
+ if (is_null($color)) {
1670
+ $this->throwError('color expected for blue()');
1671
+ }
1672
+
1673
+ return $color[3];
1674
+ }
1675
+
1676
+
1677
+ // operator on two numbers
1678
+ protected function op_number_number($op, $left, $right) {
1679
+ $unit = empty($left[2]) ? $right[2] : $left[2];
1680
+
1681
+ $value = 0;
1682
+ switch ($op) {
1683
+ case '+':
1684
+ $value = $left[1] + $right[1];
1685
+ break;
1686
+ case '*':
1687
+ $value = $left[1] * $right[1];
1688
+ break;
1689
+ case '-':
1690
+ $value = $left[1] - $right[1];
1691
+ break;
1692
+ case '%':
1693
+ $value = $left[1] % $right[1];
1694
+ break;
1695
+ case '/':
1696
+ if ($right[1] == 0) $this->throwError('parse error: divide by zero');
1697
+ $value = $left[1] / $right[1];
1698
+ break;
1699
+ case '<':
1700
+ return $this->toBool($left[1] < $right[1]);
1701
+ case '>':
1702
+ return $this->toBool($left[1] > $right[1]);
1703
+ case '>=':
1704
+ return $this->toBool($left[1] >= $right[1]);
1705
+ case '=<':
1706
+ return $this->toBool($left[1] <= $right[1]);
1707
+ default:
1708
+ $this->throwError('parse error: unknown number operator: '.$op);
1709
+ }
1710
+
1711
+ return array("number", $value, $unit);
1712
+ }
1713
+
1714
+
1715
+ /* environment functions */
1716
+
1717
+ protected function makeOutputBlock($type, $selectors = null) {
1718
+ $b = new stdclass;
1719
+ $b->lines = array();
1720
+ $b->children = array();
1721
+ $b->selectors = $selectors;
1722
+ $b->type = $type;
1723
+ $b->parent = $this->scope;
1724
+ return $b;
1725
+ }
1726
+
1727
+ // the state of execution
1728
+ protected function pushEnv($block = null) {
1729
+ $e = new stdclass;
1730
+ $e->parent = $this->env;
1731
+ $e->store = array();
1732
+ $e->block = $block;
1733
+
1734
+ $this->env = $e;
1735
+ return $e;
1736
+ }
1737
+
1738
+ // pop something off the stack
1739
+ protected function popEnv() {
1740
+ $old = $this->env;
1741
+ $this->env = $this->env->parent;
1742
+ return $old;
1743
+ }
1744
+
1745
+ // set something in the current env
1746
+ protected function set($name, $value) {
1747
+ $this->env->store[$name] = $value;
1748
+ }
1749
+
1750
+
1751
+ // get the highest occurrence entry for a name
1752
+ protected function get($name, $default=null) {
1753
+ $current = $this->env;
1754
+
1755
+ $isArguments = $name == $this->vPrefix . 'arguments';
1756
+ while ($current) {
1757
+ if ($isArguments && isset($current->arguments)) {
1758
+ return array('list', ' ', $current->arguments);
1759
+ }
1760
+
1761
+ if (isset($current->store[$name]))
1762
+ return $current->store[$name];
1763
+ else {
1764
+ $current = isset($current->storeParent) ?
1765
+ $current->storeParent : $current->parent;
1766
+ }
1767
+ }
1768
+
1769
+ return $default;
1770
+ }
1771
+
1772
+ // inject array of unparsed strings into environment as variables
1773
+ protected function injectVariables($args) {
1774
+ $this->pushEnv();
1775
+ $parser = new lessc_parser($this, __METHOD__);
1776
+ foreach ($args as $name => $strValue) {
1777
+ if ($name{0} != '@') $name = '@'.$name;
1778
+ $parser->count = 0;
1779
+ $parser->buffer = (string)$strValue;
1780
+ if (!$parser->propertyValue($value)) {
1781
+ throw new Exception("failed to parse passed in variable $name: $strValue");
1782
+ }
1783
+
1784
+ $this->set($name, $value);
1785
+ }
1786
+ }
1787
+
1788
+ /**
1789
+ * Initialize any static state, can initialize parser for a file
1790
+ * $opts isn't used yet
1791
+ */
1792
+ public function __construct($fname = null) {
1793
+ if ($fname !== null) {
1794
+ // used for deprecated parse method
1795
+ $this->_parseFile = $fname;
1796
+ }
1797
+ }
1798
+
1799
+ public function compile($string, $name = null) {
1800
+ $locale = setlocale(LC_NUMERIC, 0);
1801
+ setlocale(LC_NUMERIC, "C");
1802
+
1803
+ $this->parser = $this->makeParser($name);
1804
+ $root = $this->parser->parse($string);
1805
+
1806
+ $this->env = null;
1807
+ $this->scope = null;
1808
+
1809
+ $this->formatter = $this->newFormatter();
1810
+
1811
+ if (!empty($this->registeredVars)) {
1812
+ $this->injectVariables($this->registeredVars);
1813
+ }
1814
+
1815
+ $this->sourceParser = $this->parser; // used for error messages
1816
+ $this->compileBlock($root);
1817
+
1818
+ ob_start();
1819
+ $this->formatter->block($this->scope);
1820
+ $out = ob_get_clean();
1821
+ setlocale(LC_NUMERIC, $locale);
1822
+ return $out;
1823
+ }
1824
+
1825
+ public function compileFile($fname, $outFname = null) {
1826
+ if (!is_readable($fname)) {
1827
+ throw new Exception('load error: failed to find '.$fname);
1828
+ }
1829
+
1830
+ $pi = pathinfo($fname);
1831
+
1832
+ $oldImport = $this->importDir;
1833
+
1834
+ $this->importDir = (array)$this->importDir;
1835
+ $this->importDir[] = $pi['dirname'].'/';
1836
+
1837
+ $this->addParsedFile($fname);
1838
+
1839
+ $out = $this->compile($GLOBALS['wp_filesystem']->get_contents($fname), $fname);
1840
+
1841
+ $this->importDir = $oldImport;
1842
+
1843
+ if ($outFname !== null) {
1844
+ return $GLOBALS['wp_filesystem']->put_contents($outFname, $out);
1845
+ }
1846
+
1847
+ return $out;
1848
+ }
1849
+
1850
+ // compile only if changed input has changed or output doesn't exist
1851
+ public function checkedCompile($in, $out) {
1852
+ if (!is_file($out) || filemtime($in) > filemtime($out)) {
1853
+ $this->compileFile($in, $out);
1854
+ return true;
1855
+ }
1856
+ return false;
1857
+ }
1858
+
1859
+ /**
1860
+ * Execute lessphp on a .less file or a lessphp cache structure
1861
+ *
1862
+ * The lessphp cache structure contains information about a specific
1863
+ * less file having been parsed. It can be used as a hint for future
1864
+ * calls to determine whether or not a rebuild is required.
1865
+ *
1866
+ * The cache structure contains two important keys that may be used
1867
+ * externally:
1868
+ *
1869
+ * compiled: The final compiled CSS
1870
+ * updated: The time (in seconds) the CSS was last compiled
1871
+ *
1872
+ * The cache structure is a plain-ol' PHP associative array and can
1873
+ * be serialized and unserialized without a hitch.
1874
+ *
1875
+ * @param mixed $in Input
1876
+ * @param bool $force Force rebuild?
1877
+ * @return array lessphp cache structure
1878
+ */
1879
+ public function cachedCompile($in, $force = false) {
1880
+ // assume no root
1881
+ $root = null;
1882
+
1883
+ if (is_string($in)) {
1884
+ $root = $in;
1885
+ } elseif (is_array($in) and isset($in['root'])) {
1886
+ if ($force or ! isset($in['files'])) {
1887
+ // If we are forcing a recompile or if for some reason the
1888
+ // structure does not contain any file information we should
1889
+ // specify the root to trigger a rebuild.
1890
+ $root = $in['root'];
1891
+ } elseif (isset($in['files']) and is_array($in['files'])) {
1892
+ foreach ($in['files'] as $fname => $ftime ) {
1893
+ if (!file_exists($fname) or filemtime($fname) > $ftime) {
1894
+ // One of the files we knew about previously has changed
1895
+ // so we should look at our incoming root again.
1896
+ $root = $in['root'];
1897
+ break;
1898
+ }
1899
+ }
1900
+ }
1901
+ } else {
1902
+ // TODO: Throw an exception? We got neither a string nor something
1903
+ // that looks like a compatible lessphp cache structure.
1904
+ return null;
1905
+ }
1906
+
1907
+ if ($root !== null) {
1908
+ // If we have a root value which means we should rebuild.
1909
+ $out = array();
1910
+ $out['root'] = $root;
1911
+ $out['compiled'] = $this->compileFile($root);
1912
+ $out['files'] = $this->allParsedFiles();
1913
+ $out['updated'] = time();
1914
+ return $out;
1915
+ } else {
1916
+ // No changes, pass back the structure
1917
+ // we were given initially.
1918
+ return $in;
1919
+ }
1920
+
1921
+ }
1922
+
1923
+ // parse and compile buffer
1924
+ // This is deprecated
1925
+ public function parse($str = null, $initialVariables = null) {
1926
+ if (is_array($str)) {
1927
+ $initialVariables = $str;
1928
+ $str = null;
1929
+ }
1930
+
1931
+ $oldVars = $this->registeredVars;
1932
+ if ($initialVariables !== null) {
1933
+ $this->setVariables($initialVariables);
1934
+ }
1935
+
1936
+ if ($str == null) {
1937
+ if (empty($this->_parseFile)) {
1938
+ throw new exception("nothing to parse");
1939
+ }
1940
+
1941
+ $out = $this->compileFile($this->_parseFile);
1942
+ } else {
1943
+ $out = $this->compile($str);
1944
+ }
1945
+
1946
+ $this->registeredVars = $oldVars;
1947
+ return $out;
1948
+ }
1949
+
1950
+ protected function makeParser($name) {
1951
+ $parser = new lessc_parser($this, $name);
1952
+ $parser->writeComments = $this->preserveComments;
1953
+
1954
+ return $parser;
1955
+ }
1956
+
1957
+ public function setFormatter($name) {
1958
+ $this->formatterName = $name;
1959
+ }
1960
+
1961
+ protected function newFormatter() {
1962
+ $className = "lessc_formatter_lessjs";
1963
+ if (!empty($this->formatterName)) {
1964
+ if (!is_string($this->formatterName))
1965
+ return $this->formatterName;
1966
+ $className = "lessc_formatter_$this->formatterName";
1967
+ }
1968
+
1969
+ return new $className;
1970
+ }
1971
+
1972
+ public function setPreserveComments($preserve) {
1973
+ $this->preserveComments = $preserve;
1974
+ }
1975
+
1976
+ public function registerFunction($name, $func) {
1977
+ $this->libFunctions[$name] = $func;
1978
+ }
1979
+
1980
+ public function unregisterFunction($name) {
1981
+ unset($this->libFunctions[$name]);
1982
+ }
1983
+
1984
+ public function setVariables($variables) {
1985
+ $this->registeredVars = array_merge($this->registeredVars, $variables);
1986
+ }
1987
+
1988
+ public function unsetVariable($name) {
1989
+ unset($this->registeredVars[$name]);
1990
+ }
1991
+
1992
+ public function setImportDir($dirs) {
1993
+ $this->importDir = (array)$dirs;
1994
+ }
1995
+
1996
+ public function addImportDir($dir) {
1997
+ $this->importDir = (array)$this->importDir;
1998
+ $this->importDir[] = $dir;
1999
+ }
2000
+
2001
+ public function allParsedFiles() {
2002
+ return $this->allParsedFiles;
2003
+ }
2004
+
2005
+ protected function addParsedFile($file) {
2006
+ $this->allParsedFiles[realpath($file)] = filemtime($file);
2007
+ }
2008
+
2009
+ /**
2010
+ * Uses the current value of $this->count to show line and line number
2011
+ */
2012
+ protected function throwError($msg = null) {
2013
+ if ($this->sourceLoc >= 0) {
2014
+ $this->sourceParser->throwError($msg, $this->sourceLoc);
2015
+ }
2016
+ throw new exception($msg);
2017
+ }
2018
+
2019
+ // compile file $in to file $out if $in is newer than $out
2020
+ // returns true when it compiles, false otherwise
2021
+ public static function ccompile($in, $out, $less = null) {
2022
+ if ($less === null) {
2023
+ $less = new self;
2024
+ }
2025
+ return $less->checkedCompile($in, $out);
2026
+ }
2027
+
2028
+ public static function cexecute($in, $force = false, $less = null) {
2029
+ if ($less === null) {
2030
+ $less = new self;
2031
+ }
2032
+ return $less->cachedCompile($in, $force);
2033
+ }
2034
+
2035
+ static protected $cssColors = array(
2036
+ 'aliceblue' => '240,248,255',
2037
+ 'antiquewhite' => '250,235,215',
2038
+ 'aqua' => '0,255,255',
2039
+ 'aquamarine' => '127,255,212',
2040
+ 'azure' => '240,255,255',
2041
+ 'beige' => '245,245,220',
2042
+ 'bisque' => '255,228,196',
2043
+ 'black' => '0,0,0',
2044
+ 'blanchedalmond' => '255,235,205',
2045
+ 'blue' => '0,0,255',
2046
+ 'blueviolet' => '138,43,226',
2047
+ 'brown' => '165,42,42',
2048
+ 'burlywood' => '222,184,135',
2049
+ 'cadetblue' => '95,158,160',
2050
+ 'chartreuse' => '127,255,0',
2051
+ 'chocolate' => '210,105,30',
2052
+ 'coral' => '255,127,80',
2053
+ 'cornflowerblue' => '100,149,237',
2054
+ 'cornsilk' => '255,248,220',
2055
+ 'crimson' => '220,20,60',
2056
+ 'cyan' => '0,255,255',
2057
+ 'darkblue' => '0,0,139',
2058
+ 'darkcyan' => '0,139,139',
2059
+ 'darkgoldenrod' => '184,134,11',
2060
+ 'darkgray' => '169,169,169',
2061
+ 'darkgreen' => '0,100,0',
2062
+ 'darkgrey' => '169,169,169',
2063
+ 'darkkhaki' => '189,183,107',
2064
+ 'darkmagenta' => '139,0,139',
2065
+ 'darkolivegreen' => '85,107,47',
2066
+ 'darkorange' => '255,140,0',
2067
+ 'darkorchid' => '153,50,204',
2068
+ 'darkred' => '139,0,0',
2069
+ 'darksalmon' => '233,150,122',
2070
+ 'darkseagreen' => '143,188,143',
2071
+ 'darkslateblue' => '72,61,139',
2072
+ 'darkslategray' => '47,79,79',
2073
+ 'darkslategrey' => '47,79,79',
2074
+ 'darkturquoise' => '0,206,209',
2075
+ 'darkviolet' => '148,0,211',
2076
+ 'deeppink' => '255,20,147',
2077
+ 'deepskyblue' => '0,191,255',
2078
+ 'dimgray' => '105,105,105',
2079
+ 'dimgrey' => '105,105,105',
2080
+ 'dodgerblue' => '30,144,255',
2081
+ 'firebrick' => '178,34,34',
2082
+ 'floralwhite' => '255,250,240',
2083
+ 'forestgreen' => '34,139,34',
2084
+ 'fuchsia' => '255,0,255',
2085
+ 'gainsboro' => '220,220,220',
2086
+ 'ghostwhite' => '248,248,255',
2087
+ 'gold' => '255,215,0',
2088
+ 'goldenrod' => '218,165,32',
2089
+ 'gray' => '128,128,128',
2090
+ 'green' => '0,128,0',
2091
+ 'greenyellow' => '173,255,47',
2092
+ 'grey' => '128,128,128',
2093
+ 'honeydew' => '240,255,240',
2094
+ 'hotpink' => '255,105,180',
2095
+ 'indianred' => '205,92,92',
2096
+ 'indigo' => '75,0,130',
2097
+ 'ivory' => '255,255,240',
2098
+ 'khaki' => '240,230,140',
2099
+ 'lavender' => '230,230,250',
2100
+ 'lavenderblush' => '255,240,245',
2101
+ 'lawngreen' => '124,252,0',
2102
+ 'lemonchiffon' => '255,250,205',
2103
+ 'lightblue' => '173,216,230',
2104
+ 'lightcoral' => '240,128,128',
2105
+ 'lightcyan' => '224,255,255',
2106
+ 'lightgoldenrodyellow' => '250,250,210',
2107
+ 'lightgray' => '211,211,211',
2108
+ 'lightgreen' => '144,238,144',
2109
+ 'lightgrey' => '211,211,211',
2110
+ 'lightpink' => '255,182,193',
2111
+ 'lightsalmon' => '255,160,122',
2112
+ 'lightseagreen' => '32,178,170',
2113
+ 'lightskyblue' => '135,206,250',
2114
+ 'lightslategray' => '119,136,153',
2115
+ 'lightslategrey' => '119,136,153',
2116
+ 'lightsteelblue' => '176,196,222',
2117
+ 'lightyellow' => '255,255,224',
2118
+ 'lime' => '0,255,0',
2119
+ 'limegreen' => '50,205,50',
2120
+ 'linen' => '250,240,230',
2121
+ 'magenta' => '255,0,255',
2122
+ 'maroon' => '128,0,0',
2123
+ 'mediumaquamarine' => '102,205,170',
2124
+ 'mediumblue' => '0,0,205',
2125
+ 'mediumorchid' => '186,85,211',
2126
+ 'mediumpurple' => '147,112,219',
2127
+ 'mediumseagreen' => '60,179,113',
2128
+ 'mediumslateblue' => '123,104,238',
2129
+ 'mediumspringgreen' => '0,250,154',
2130
+ 'mediumturquoise' => '72,209,204',
2131
+ 'mediumvioletred' => '199,21,133',
2132
+ 'midnightblue' => '25,25,112',
2133
+ 'mintcream' => '245,255,250',
2134
+ 'mistyrose' => '255,228,225',
2135
+ 'moccasin' => '255,228,181',
2136
+ 'navajowhite' => '255,222,173',
2137
+ 'navy' => '0,0,128',
2138
+ 'oldlace' => '253,245,230',
2139
+ 'olive' => '128,128,0',
2140
+ 'olivedrab' => '107,142,35',
2141
+ 'orange' => '255,165,0',
2142
+ 'orangered' => '255,69,0',
2143
+ 'orchid' => '218,112,214',
2144
+ 'palegoldenrod' => '238,232,170',
2145
+ 'palegreen' => '152,251,152',
2146
+ 'paleturquoise' => '175,238,238',
2147
+ 'palevioletred' => '219,112,147',
2148
+ 'papayawhip' => '255,239,213',
2149
+ 'peachpuff' => '255,218,185',
2150
+ 'peru' => '205,133,63',
2151
+ 'pink' => '255,192,203',
2152
+ 'plum' => '221,160,221',
2153
+ 'powderblue' => '176,224,230',
2154
+ 'purple' => '128,0,128',
2155
+ 'red' => '255,0,0',
2156
+ 'rosybrown' => '188,143,143',
2157
+ 'royalblue' => '65,105,225',
2158
+ 'saddlebrown' => '139,69,19',
2159
+ 'salmon' => '250,128,114',
2160
+ 'sandybrown' => '244,164,96',
2161
+ 'seagreen' => '46,139,87',
2162
+ 'seashell' => '255,245,238',
2163
+ 'sienna' => '160,82,45',
2164
+ 'silver' => '192,192,192',
2165
+ 'skyblue' => '135,206,235',
2166
+ 'slateblue' => '106,90,205',
2167
+ 'slategray' => '112,128,144',
2168
+ 'slategrey' => '112,128,144',
2169
+ 'snow' => '255,250,250',
2170
+ 'springgreen' => '0,255,127',
2171
+ 'steelblue' => '70,130,180',
2172
+ 'tan' => '210,180,140',
2173
+ 'teal' => '0,128,128',
2174
+ 'thistle' => '216,191,216',
2175
+ 'tomato' => '255,99,71',
2176
+ 'transparent' => '0,0,0,0',
2177
+ 'turquoise' => '64,224,208',
2178
+ 'violet' => '238,130,238',
2179
+ 'wheat' => '245,222,179',
2180
+ 'white' => '255,255,255',
2181
+ 'whitesmoke' => '245,245,245',
2182
+ 'yellow' => '255,255,0',
2183
+ 'yellowgreen' => '154,205,50'
2184
+ );
2185
+ }
2186
+
2187
+ // responsible for taking a string of LESS code and converting it into a
2188
+ // syntax tree
2189
+ /**
2190
+ * @ignore
2191
+ */
2192
+ class lessc_parser {
2193
+ static protected $nextBlockId = 0; // used to uniquely identify blocks
2194
+
2195
+ static protected $precedence = array(
2196
+ '=<' => 0,
2197
+ '>=' => 0,
2198
+ '=' => 0,
2199
+ '<' => 0,
2200
+ '>' => 0,
2201
+
2202
+ '+' => 1,
2203
+ '-' => 1,
2204
+ '*' => 2,
2205
+ '/' => 2,
2206
+ '%' => 2,
2207
+ );
2208
+
2209
+ static protected $whitePattern;
2210
+ static protected $commentMulti;
2211
+
2212
+ static protected $commentSingle = "//";
2213
+ static protected $commentMultiLeft = "/*";
2214
+ static protected $commentMultiRight = "*/";
2215
+
2216
+ // regex string to match any of the operators
2217
+ static protected $operatorString;
2218
+
2219
+ // these properties will supress division unless it's inside parenthases
2220
+ static protected $supressDivisionProps =
2221
+ array('/border-radius$/i', '/^font$/i');
2222
+
2223
+ protected $blockDirectives = array("font-face", "keyframes", "page", "-moz-document", "viewport", "-moz-viewport", "-o-viewport", "-ms-viewport");
2224
+ protected $lineDirectives = array("charset");
2225
+
2226
+ /**
2227
+ * if we are in parens we can be more liberal with whitespace around
2228
+ * operators because it must evaluate to a single value and thus is less
2229
+ * ambiguous.
2230
+ *
2231
+ * Consider:
2232
+ * property1: 10 -5; // is two numbers, 10 and -5
2233
+ * property2: (10 -5); // should evaluate to 5
2234
+ */
2235
+ protected $inParens = false;
2236
+
2237
+ // caches preg escaped literals
2238
+ static protected $literalCache = array();
2239
+
2240
+ public function __construct($lessc, $sourceName = null) {
2241
+ $this->eatWhiteDefault = true;
2242
+ // reference to less needed for vPrefix, mPrefix, and parentSelector
2243
+ $this->lessc = $lessc;
2244
+
2245
+ $this->sourceName = $sourceName; // name used for error messages
2246
+
2247
+ $this->writeComments = false;
2248
+
2249
+ if (!self::$operatorString) {
2250
+ self::$operatorString =
2251
+ '('.implode('|', array_map(array('JupiterX_Lessc', 'preg_quote'),
2252
+ array_keys(self::$precedence))).')';
2253
+
2254
+ $commentSingle = JupiterX_Lessc::preg_quote(self::$commentSingle);
2255
+ $commentMultiLeft = JupiterX_Lessc::preg_quote(self::$commentMultiLeft);
2256
+ $commentMultiRight = JupiterX_Lessc::preg_quote(self::$commentMultiRight);
2257
+
2258
+ self::$commentMulti = $commentMultiLeft.'.*?'.$commentMultiRight;
2259
+ self::$whitePattern = '/'.$commentSingle.'[^\n]*\s*|('.self::$commentMulti.')\s*|\s+/Ais';
2260
+ }
2261
+ }
2262
+
2263
+ public function parse($buffer) {
2264
+ $this->count = 0;
2265
+ $this->line = 1;
2266
+
2267
+ $this->env = null; // block stack
2268
+ $this->buffer = $this->writeComments ? $buffer : $this->removeComments($buffer);
2269
+ $this->pushSpecialBlock("root");
2270
+ $this->eatWhiteDefault = true;
2271
+ $this->seenComments = array();
2272
+
2273
+ // trim whitespace on head
2274
+ // if (preg_match('/^\s+/', $this->buffer, $m)) {
2275
+ // $this->line += substr_count($m[0], "\n");
2276
+ // $this->buffer = ltrim($this->buffer);
2277
+ // }
2278
+ $this->whitespace();
2279
+
2280
+ // parse the entire file
2281
+ $lastCount = $this->count;
2282
+ while (false !== $this->parseChunk());
2283
+
2284
+ if ($this->count != strlen($this->buffer))
2285
+ $this->throwError();
2286
+
2287
+ // TODO report where the block was opened
2288
+ if (!is_null($this->env->parent))
2289
+ throw new exception('parse error: unclosed block');
2290
+
2291
+ return $this->env;
2292
+ }
2293
+
2294
+ /**
2295
+ * Parse a single chunk off the head of the buffer and append it to the
2296
+ * current parse environment.
2297
+ * Returns false when the buffer is empty, or when there is an error.
2298
+ *
2299
+ * This function is called repeatedly until the entire document is
2300
+ * parsed.
2301
+ *
2302
+ * This parser is most similar to a recursive descent parser. Single
2303
+ * functions represent discrete grammatical rules for the language, and
2304
+ * they are able to capture the text that represents those rules.
2305
+ *
2306
+ * Consider the function lessc::keyword(). (all parse functions are
2307
+ * structured the same)
2308
+ *
2309
+ * The function takes a single reference argument. When calling the
2310
+ * function it will attempt to match a keyword on the head of the buffer.
2311
+ * If it is successful, it will place the keyword in the referenced
2312
+ * argument, advance the position in the buffer, and return true. If it
2313
+ * fails then it won't advance the buffer and it will return false.
2314
+ *
2315
+ * All of these parse functions are powered by lessc::match(), which behaves
2316
+ * the same way, but takes a literal regular expression. Sometimes it is
2317
+ * more convenient to use match instead of creating a new function.
2318
+ *
2319
+ * Because of the format of the functions, to parse an entire string of
2320
+ * grammatical rules, you can chain them together using &&.
2321
+ *
2322
+ * But, if some of the rules in the chain succeed before one fails, then
2323
+ * the buffer position will be left at an invalid state. In order to
2324
+ * avoid this, lessc::seek() is used to remember and set buffer positions.
2325
+ *
2326
+ * Before parsing a chain, use $s = $this->seek() to remember the current
2327
+ * position into $s. Then if a chain fails, use $this->seek($s) to
2328
+ * go back where we started.
2329
+ */
2330
+ protected function parseChunk() {
2331
+ if (empty($this->buffer)) return false;
2332
+ $s = $this->seek();
2333
+
2334
+ // setting a property
2335
+ if ($this->keyword($key) && $this->assign() &&
2336
+ $this->propertyValue($value, $key) && $this->end())
2337
+ {
2338
+ $this->append(array('assign', $key, $value), $s);
2339
+ return true;
2340
+ } else {
2341
+ $this->seek($s);
2342
+ }
2343
+
2344
+
2345
+ // look for special css blocks
2346
+ if ($this->literal('@', false)) {
2347
+ $this->count--;
2348
+
2349
+ // media
2350
+ if ($this->literal('@media')) {
2351
+ if (($this->mediaQueryList($mediaQueries) || true)
2352
+ && $this->literal('{'))
2353
+ {
2354
+ $media = $this->pushSpecialBlock("media");
2355
+ $media->queries = is_null($mediaQueries) ? array() : $mediaQueries;
2356
+ return true;
2357
+ } else {
2358
+ $this->seek($s);
2359
+ return false;
2360
+ }
2361
+ }
2362
+
2363
+ if ($this->literal("@", false) && $this->keyword($dirName)) {
2364
+ if ($this->isDirective($dirName, $this->blockDirectives)) {
2365
+ if (($this->openString("{", $dirValue, null, array(";")) || true) &&
2366
+ $this->literal("{"))
2367
+ {
2368
+ $dir = $this->pushSpecialBlock("directive");
2369
+ $dir->name = $dirName;
2370
+ if (isset($dirValue)) $dir->value = $dirValue;
2371
+ return true;
2372
+ }
2373
+ } elseif ($this->isDirective($dirName, $this->lineDirectives)) {
2374
+ if ($this->propertyValue($dirValue) && $this->end()) {
2375
+ $this->append(array("directive", $dirName, $dirValue));
2376
+ return true;
2377
+ }
2378
+ }
2379
+ }
2380
+
2381
+ $this->seek($s);
2382
+ }
2383
+
2384
+ // setting a variable
2385
+ if ($this->variable($var) && $this->assign() &&
2386
+ $this->propertyValue($value) && $this->end())
2387
+ {
2388
+ $this->append(array('assign', $var, $value), $s);
2389
+ return true;
2390
+ } else {
2391
+ $this->seek($s);
2392
+ }
2393
+
2394
+ if ($this->import($importValue)) {
2395
+ $this->append($importValue, $s);
2396
+ return true;
2397
+ }
2398
+
2399
+ // opening parametric mixin
2400
+ if ($this->tag($tag, true) && $this->argumentDef($args, $isVararg) &&
2401
+ ($this->guards($guards) || true) &&
2402
+ $this->literal('{'))
2403
+ {
2404
+ $block = $this->pushBlock($this->fixTags(array($tag)));
2405
+ $block->args = $args;
2406
+ $block->isVararg = $isVararg;
2407
+ if (!empty($guards)) $block->guards = $guards;
2408
+ return true;
2409
+ } else {
2410
+ $this->seek($s);
2411
+ }
2412
+
2413
+ // opening a simple block
2414
+ if ($this->tags($tags) && $this->literal('{')) {
2415
+ $tags = $this->fixTags($tags);
2416
+ $this->pushBlock($tags);
2417
+ return true;
2418
+ } else {
2419
+ $this->seek($s);
2420
+ }
2421
+
2422
+ // closing a block
2423
+ if ($this->literal('}', false)) {
2424
+ try {
2425
+ $block = $this->pop();
2426
+ } catch (exception $e) {
2427
+ $this->seek($s);
2428
+ $this->throwError($e->getMessage());
2429
+ }
2430
+
2431
+ $hidden = false;
2432
+ if (is_null($block->type)) {
2433
+ $hidden = true;
2434
+ if (!isset($block->args)) {
2435
+ foreach ($block->tags as $tag) {
2436
+ if (!is_string($tag) || $tag{0} != $this->lessc->mPrefix) {
2437
+ $hidden = false;
2438
+ break;
2439
+ }
2440
+ }
2441
+ }
2442
+
2443
+ foreach ($block->tags as $tag) {
2444
+ if (is_string($tag)) {
2445
+ $this->env->children[$tag][] = $block;
2446
+ }
2447
+ }
2448
+ }
2449
+
2450
+ if (!$hidden) {
2451
+ $this->append(array('block', $block), $s);
2452
+ }
2453
+
2454
+ // this is done here so comments aren't bundled into he block that
2455
+ // was just closed
2456
+ $this->whitespace();
2457
+ return true;
2458
+ }
2459
+
2460
+ // mixin
2461
+ if ($this->mixinTags($tags) &&
2462
+ ($this->argumentDef($argv, $isVararg) || true) &&
2463
+ ($this->keyword($suffix) || true) && $this->end())
2464
+ {
2465
+ $tags = $this->fixTags($tags);
2466
+ $this->append(array('mixin', $tags, $argv, $suffix), $s);
2467
+ return true;
2468
+ } else {
2469
+ $this->seek($s);
2470
+ }
2471
+
2472
+ // spare ;
2473
+ if ($this->literal(';')) return true;
2474
+
2475
+ return false; // got nothing, throw error
2476
+ }
2477
+
2478
+ protected function isDirective($dirname, $directives) {
2479
+ // TODO: cache pattern in parser
2480
+ $pattern = implode("|",
2481
+ array_map(array("JupiterX_Lessc", "preg_quote"), $directives));
2482
+ $pattern = '/^(-[a-z-]+-)?(' . $pattern . ')$/i';
2483
+
2484
+ return preg_match($pattern, $dirname);
2485
+ }
2486
+
2487
+ protected function fixTags($tags) {
2488
+ // move @ tags out of variable namespace
2489
+ foreach ($tags as &$tag) {
2490
+ if ($tag{0} == $this->lessc->vPrefix)
2491
+ $tag[0] = $this->lessc->mPrefix;
2492
+ }
2493
+ return $tags;
2494
+ }
2495
+
2496
+ // a list of expressions
2497
+ protected function expressionList(&$exps) {
2498
+ $values = array();
2499
+
2500
+ while ($this->expression($exp)) {
2501
+ $values[] = $exp;
2502
+ }
2503
+
2504
+ if (count($values) == 0) return false;
2505
+
2506
+ $exps = JupiterX_Lessc::compressList($values, ' ');
2507
+ return true;
2508
+ }
2509
+
2510
+ /**
2511
+ * Attempt to consume an expression.
2512
+ * @link http://en.wikipedia.org/wiki/Operator-precedence_parser#Pseudo-code
2513
+ */
2514
+ protected function expression(&$out) {
2515
+ if ($this->value($lhs)) {
2516
+ $out = $this->expHelper($lhs, 0);
2517
+
2518
+ // look for / shorthand
2519
+ if (!empty($this->env->supressedDivision)) {
2520
+ unset($this->env->supressedDivision);
2521
+ $s = $this->seek();
2522
+ if ($this->literal("/") && $this->value($rhs)) {
2523
+ $out = array("list", "",
2524
+ array($out, array("keyword", "/"), $rhs));
2525
+ } else {
2526
+ $this->seek($s);
2527
+ }
2528
+ }
2529
+
2530
+ return true;
2531
+ }
2532
+ return false;
2533
+ }
2534
+
2535
+ /**
2536
+ * recursively parse infix equation with $lhs at precedence $minP
2537
+ */
2538
+ protected function expHelper($lhs, $minP) {
2539
+ $this->inExp = true;
2540
+ $ss = $this->seek();
2541
+
2542
+ while (true) {
2543
+ $whiteBefore = isset($this->buffer[$this->count - 1]) &&
2544
+ ctype_space($this->buffer[$this->count - 1]);
2545
+
2546
+ // If there is whitespace before the operator, then we require
2547
+ // whitespace after the operator for it to be an expression
2548
+ $needWhite = $whiteBefore && !$this->inParens;
2549
+
2550
+ if ($this->match(self::$operatorString.($needWhite ? '\s' : ''), $m) && self::$precedence[$m[1]] >= $minP) {
2551
+ if (!$this->inParens && isset($this->env->currentProperty) && $m[1] == "/" && empty($this->env->supressedDivision)) {
2552
+ foreach (self::$supressDivisionProps as $pattern) {
2553
+ if (preg_match($pattern, $this->env->currentProperty)) {
2554
+ $this->env->supressedDivision = true;
2555
+ break 2;
2556
+ }
2557
+ }
2558
+ }
2559
+
2560
+
2561
+ $whiteAfter = isset($this->buffer[$this->count - 1]) &&
2562
+ ctype_space($this->buffer[$this->count - 1]);
2563
+
2564
+ if (!$this->value($rhs)) break;
2565
+
2566
+ // peek for next operator to see what to do with rhs
2567
+ if ($this->peek(self::$operatorString, $next) && self::$precedence[$next[1]] > self::$precedence[$m[1]]) {
2568
+ $rhs = $this->expHelper($rhs, self::$precedence[$next[1]]);
2569
+ }
2570
+
2571
+ $lhs = array('expression', $m[1], $lhs, $rhs, $whiteBefore, $whiteAfter);
2572
+ $ss = $this->seek();
2573
+
2574
+ continue;
2575
+ }
2576
+
2577
+ break;
2578
+ }
2579
+
2580
+ $this->seek($ss);
2581
+
2582
+ return $lhs;
2583
+ }
2584
+
2585
+ // consume a list of values for a property
2586
+ public function propertyValue(&$value, $keyName = null) {
2587
+ $values = array();
2588
+
2589
+ if ($keyName !== null) $this->env->currentProperty = $keyName;
2590
+
2591
+ $s = null;
2592
+ while ($this->expressionList($v)) {
2593
+ $values[] = $v;
2594
+ $s = $this->seek();
2595
+ if (!$this->literal(',')) break;
2596
+ }
2597
+
2598
+ if ($s) $this->seek($s);
2599
+
2600
+ if ($keyName !== null) unset($this->env->currentProperty);
2601
+
2602
+ if (count($values) == 0) return false;
2603
+
2604
+ $value = JupiterX_Lessc::compressList($values, ', ');
2605
+ return true;
2606
+ }
2607
+
2608
+ protected function parenValue(&$out) {
2609
+ $s = $this->seek();
2610
+
2611
+ // speed shortcut
2612
+ if (isset($this->buffer[$this->count]) && $this->buffer[$this->count] != "(") {
2613
+ return false;
2614
+ }
2615
+
2616
+ $inParens = $this->inParens;
2617
+ if ($this->literal("(") &&
2618
+ ($this->inParens = true) && $this->expression($exp) &&
2619
+ $this->literal(")"))
2620
+ {
2621
+ $out = $exp;
2622
+ $this->inParens = $inParens;
2623
+ return true;
2624
+ } else {
2625
+ $this->inParens = $inParens;
2626
+ $this->seek($s);
2627
+ }
2628
+
2629
+ return false;
2630
+ }
2631
+
2632
+ // a single value
2633
+ protected function value(&$value) {
2634
+ $s = $this->seek();
2635
+
2636
+ // speed shortcut
2637
+ if (isset($this->buffer[$this->count]) && $this->buffer[$this->count] == "-") {
2638
+ // negation
2639
+ if ($this->literal("-", false) &&
2640
+ (($this->variable($inner) && $inner = array("variable", $inner)) ||
2641
+ $this->unit($inner) ||
2642
+ $this->parenValue($inner)))
2643
+ {
2644
+ $value = array("unary", "-", $inner);
2645
+ return true;
2646
+ } else {
2647
+ $this->seek($s);
2648
+ }
2649
+ }
2650
+
2651
+ if ($this->parenValue($value)) return true;
2652
+ if ($this->unit($value)) return true;
2653
+ if ($this->color($value)) return true;
2654
+ if ($this->func($value)) return true;
2655
+ if ($this->string($value)) return true;
2656
+
2657
+ if ($this->keyword($word)) {
2658
+ $value = array('keyword', $word);
2659
+ return true;
2660
+ }
2661
+
2662
+ // try a variable
2663
+ if ($this->variable($var)) {
2664
+ $value = array('variable', $var);
2665
+ return true;
2666
+ }
2667
+
2668
+ // unquote string (should this work on any type?
2669
+ if ($this->literal("~") && $this->string($str)) {
2670
+ $value = array("escape", $str);
2671
+ return true;
2672
+ } else {
2673
+ $this->seek($s);
2674
+ }
2675
+
2676
+ // css hack: \0
2677
+ if ($this->literal('\\') && $this->match('([0-9]+)', $m)) {
2678
+ $value = array('keyword', '\\'.$m[1]);
2679
+ return true;
2680
+ } else {
2681
+ $this->seek($s);
2682
+ }
2683
+
2684
+ return false;
2685
+ }
2686
+
2687
+ // an import statement
2688
+ protected function import(&$out) {
2689
+ $s = $this->seek();
2690
+ if (!$this->literal('@import')) return false;
2691
+
2692
+ // @import "something.css" media;
2693
+ // @import url("something.css") media;
2694
+ // @import url(something.css) media;
2695
+
2696
+ if ($this->propertyValue($value)) {
2697
+ $out = array("import", $value);
2698
+ return true;
2699
+ }
2700
+ }
2701
+
2702
+ protected function mediaQueryList(&$out) {
2703
+ if ($this->genericList($list, "mediaQuery", ",", false)) {
2704
+ $out = $list[2];
2705
+ return true;
2706
+ }
2707
+ return false;
2708
+ }
2709
+
2710
+ protected function mediaQuery(&$out) {
2711
+ $s = $this->seek();
2712
+
2713
+ $expressions = null;
2714
+ $parts = array();
2715
+
2716
+ if (($this->literal("only") && ($only = true) || $this->literal("not") && ($not = true) || true) && $this->keyword($mediaType)) {
2717
+ $prop = array("mediaType");
2718
+ if (isset($only)) $prop[] = "only";
2719
+ if (isset($not)) $prop[] = "not";
2720
+ $prop[] = $mediaType;
2721
+ $parts[] = $prop;
2722
+ } else {
2723
+ $this->seek($s);
2724
+ }
2725
+
2726
+
2727
+ if (!empty($mediaType) && !$this->literal("and")) {
2728
+ // ~
2729
+ } else {
2730
+ $this->genericList($expressions, "mediaExpression", "and", false);
2731
+ if (is_array($expressions)) $parts = array_merge($parts, $expressions[2]);
2732
+ }
2733
+
2734
+ if (count($parts) == 0) {
2735
+ $this->seek($s);
2736
+ return false;
2737
+ }
2738
+
2739
+ $out = $parts;
2740
+ return true;
2741
+ }
2742
+
2743
+ protected function mediaExpression(&$out) {
2744
+ $s = $this->seek();
2745
+ $value = null;
2746
+ if ($this->literal("(") &&
2747
+ $this->keyword($feature) &&
2748
+ ($this->literal(":") && $this->expression($value) || true) &&
2749
+ $this->literal(")"))
2750
+ {
2751
+ $out = array("mediaExp", $feature);
2752
+ if ($value) $out[] = $value;
2753
+ return true;
2754
+ } elseif ($this->variable($variable)) {
2755
+ $out = array('variable', $variable);
2756
+ return true;
2757
+ }
2758
+
2759
+ $this->seek($s);
2760
+ return false;
2761
+ }
2762
+
2763
+ // an unbounded string stopped by $end
2764
+ protected function openString($end, &$out, $nestingOpen=null, $rejectStrs = null) {
2765
+ $oldWhite = $this->eatWhiteDefault;
2766
+ $this->eatWhiteDefault = false;
2767
+
2768
+ $stop = array("'", '"', "@{", $end);
2769
+ $stop = array_map(array("JupiterX_Lessc", "preg_quote"), $stop);
2770
+ // $stop[] = self::$commentMulti;
2771
+
2772
+ if (!is_null($rejectStrs)) {
2773
+ $stop = array_merge($stop, $rejectStrs);
2774
+ }
2775
+
2776
+ $patt = '(.*?)('.implode("|", $stop).')';
2777
+
2778
+ $nestingLevel = 0;
2779
+
2780
+ $content = array();
2781
+ while ($this->match($patt, $m, false)) {
2782
+ if (!empty($m[1])) {
2783
+ $content[] = $m[1];
2784
+ if ($nestingOpen) {
2785
+ $nestingLevel += substr_count($m[1], $nestingOpen);
2786
+ }
2787
+ }
2788
+
2789
+ $tok = $m[2];
2790
+
2791
+ $this->count-= strlen($tok);
2792
+ if ($tok == $end) {
2793
+ if ($nestingLevel == 0) {
2794
+ break;
2795
+ } else {
2796
+ $nestingLevel--;
2797
+ }
2798
+ }
2799
+
2800
+ if (($tok == "'" || $tok == '"') && $this->string($str)) {
2801
+ $content[] = $str;
2802
+ continue;
2803
+ }
2804
+
2805
+ if ($tok == "@{" && $this->interpolation($inter)) {
2806
+ $content[] = $inter;
2807
+ continue;
2808
+ }
2809
+
2810
+ if (!empty($rejectStrs) && in_array($tok, $rejectStrs)) {
2811
+ break;
2812
+ }
2813
+
2814
+ $content[] = $tok;
2815
+ $this->count+= strlen($tok);
2816
+ }
2817
+
2818
+ $this->eatWhiteDefault = $oldWhite;
2819
+
2820
+ if (count($content) == 0) return false;
2821
+
2822
+ // trim the end
2823
+ if (is_string(end($content))) {
2824
+ $content[count($content) - 1] = rtrim(end($content));
2825
+ }
2826
+
2827
+ $out = array("string", "", $content);
2828
+ return true;
2829
+ }
2830
+
2831
+ protected function string(&$out) {
2832
+ $s = $this->seek();
2833
+ if ($this->literal('"', false)) {
2834
+ $delim = '"';
2835
+ } elseif ($this->literal("'", false)) {
2836
+ $delim = "'";
2837
+ } else {
2838
+ return false;
2839
+ }
2840
+
2841
+ $content = array();
2842
+
2843
+ // look for either ending delim , escape, or string interpolation
2844
+ $patt = '([^\n]*?)(@\{|\\\\|' .
2845
+ JupiterX_Lessc::preg_quote($delim).')';
2846
+
2847
+ $oldWhite = $this->eatWhiteDefault;
2848
+ $this->eatWhiteDefault = false;
2849
+
2850
+ while ($this->match($patt, $m, false)) {
2851
+ $content[] = $m[1];
2852
+ if ($m[2] == "@{") {
2853
+ $this->count -= strlen($m[2]);
2854
+ if ($this->interpolation($inter, false)) {
2855
+ $content[] = $inter;
2856
+ } else {
2857
+ $this->count += strlen($m[2]);
2858
+ $content[] = "@{"; // ignore it
2859
+ }
2860
+ } elseif ($m[2] == '\\') {
2861
+ $content[] = $m[2];
2862
+ if ($this->literal($delim, false)) {
2863
+ $content[] = $delim;
2864
+ }
2865
+ } else {
2866
+ $this->count -= strlen($delim);
2867
+ break; // delim
2868
+ }
2869
+ }
2870
+
2871
+ $this->eatWhiteDefault = $oldWhite;
2872
+
2873
+ if ($this->literal($delim)) {
2874
+ $out = array("string", $delim, $content);
2875
+ return true;
2876
+ }
2877
+
2878
+ $this->seek($s);
2879
+ return false;
2880
+ }
2881
+
2882
+ protected function interpolation(&$out) {
2883
+ $oldWhite = $this->eatWhiteDefault;
2884
+ $this->eatWhiteDefault = true;
2885
+
2886
+ $s = $this->seek();
2887
+ if ($this->literal("@{") &&
2888
+ $this->openString("}", $interp, null, array("'", '"', ";")) &&
2889
+ $this->literal("}", false))
2890
+ {
2891
+ $out = array("interpolate", $interp);
2892
+ $this->eatWhiteDefault = $oldWhite;
2893
+ if ($this->eatWhiteDefault) $this->whitespace();
2894
+ return true;
2895
+ }
2896
+
2897
+ $this->eatWhiteDefault = $oldWhite;
2898
+ $this->seek($s);
2899
+ return false;
2900
+ }
2901
+
2902
+ protected function unit(&$unit) {
2903
+ // speed shortcut
2904
+ if (isset($this->buffer[$this->count])) {
2905
+ $char = $this->buffer[$this->count];
2906
+ if (!ctype_digit($char) && $char != ".") return false;
2907
+ }
2908
+
2909
+ if ($this->match('([0-9]+(?:\.[0-9]*)?|\.[0-9]+)([%a-zA-Z]+)?', $m)) {
2910
+ $unit = array("number", $m[1], empty($m[2]) ? "" : $m[2]);
2911
+ return true;
2912
+ }
2913
+ return false;
2914
+ }
2915
+
2916
+ // a # color
2917
+ protected function color(&$out) {
2918
+ if ($this->match('(#(?:[0-9a-f]{8}|[0-9a-f]{6}|[0-9a-f]{3}))', $m)) {
2919
+ if (strlen($m[1]) > 7) {
2920
+ $out = array("string", "", array($m[1]));
2921
+ } else {
2922
+ $out = array("raw_color", $m[1]);
2923
+ }
2924
+ return true;
2925
+ }
2926
+
2927
+ return false;
2928
+ }
2929
+
2930
+ // consume an argument definition list surrounded by ()
2931
+ // each argument is a variable name with optional value
2932
+ // or at the end a ... or a variable named followed by ...
2933
+ // arguments are separated by , unless a ; is in the list, then ; is the
2934
+ // delimiter.
2935
+ protected function argumentDef(&$args, &$isVararg) {
2936
+ $s = $this->seek();
2937
+ if (!$this->literal('(')) return false;
2938
+
2939
+ $values = array();
2940
+ $delim = ",";
2941
+ $method = "expressionList";
2942
+
2943
+ $isVararg = false;
2944
+ while (true) {
2945
+ if ($this->literal("...")) {
2946
+ $isVararg = true;
2947
+ break;
2948
+ }
2949
+
2950
+ if ($this->$method($value)) {
2951
+ if ($value[0] == "variable") {
2952
+ $arg = array("arg", $value[1]);
2953
+ $ss = $this->seek();
2954
+
2955
+ if ($this->assign() && $this->$method($rhs)) {
2956
+ $arg[] = $rhs;
2957
+ } else {
2958
+ $this->seek($ss);
2959
+ if ($this->literal("...")) {
2960
+ $arg[0] = "rest";
2961
+ $isVararg = true;
2962
+ }
2963
+ }
2964
+
2965
+ $values[] = $arg;
2966
+ if ($isVararg) break;
2967
+ continue;
2968
+ } else {
2969
+ $values[] = array("lit", $value);
2970
+ }
2971
+ }
2972
+
2973
+
2974
+ if (!$this->literal($delim)) {
2975
+ if ($delim == "," && $this->literal(";")) {
2976
+ // found new delim, convert existing args
2977
+ $delim = ";";
2978
+ $method = "propertyValue";
2979
+
2980
+ // transform arg list
2981
+ if (isset($values[1])) { // 2 items
2982
+ $newList = array();
2983
+ foreach ($values as $i => $arg) {
2984
+ switch($arg[0]) {
2985
+ case "arg":
2986
+ if ($i) {
2987
+ $this->throwError("Cannot mix ; and , as delimiter types");
2988
+ }
2989
+ $newList[] = $arg[2];
2990
+ break;
2991
+ case "lit":
2992
+ $newList[] = $arg[1];
2993
+ break;
2994
+ case "rest":
2995
+ $this->throwError("Unexpected rest before semicolon");
2996
+ }
2997
+ }
2998
+
2999
+ $newList = array("list", ", ", $newList);
3000
+
3001
+ switch ($values[0][0]) {
3002
+ case "arg":
3003
+ $newArg = array("arg", $values[0][1], $newList);
3004
+ break;
3005
+ case "lit":
3006
+ $newArg = array("lit", $newList);
3007
+ break;
3008
+ }
3009
+
3010
+ } elseif ($values) { // 1 item
3011
+ $newArg = $values[0];
3012
+ }
3013
+
3014
+ if ($newArg) {
3015
+ $values = array($newArg);
3016
+ }
3017
+ } else {
3018
+ break;
3019
+ }
3020
+ }
3021
+ }
3022
+
3023
+ if (!$this->literal(')')) {
3024
+ $this->seek($s);
3025
+ return false;
3026
+ }
3027
+
3028
+ $args = $values;
3029
+
3030
+ return true;
3031
+ }
3032
+
3033
+ // consume a list of tags
3034
+ // this accepts a hanging delimiter
3035
+ protected function tags(&$tags, $simple = false, $delim = ',') {
3036
+ $tags = array();
3037
+ while ($this->tag($tt, $simple)) {
3038
+ $tags[] = $tt;
3039
+ if (!$this->literal($delim)) break;
3040
+ }
3041
+ if (count($tags) == 0) return false;
3042
+
3043
+ return true;
3044
+ }
3045
+
3046
+ // list of tags of specifying mixin path
3047
+ // optionally separated by > (lazy, accepts extra >)
3048
+ protected function mixinTags(&$tags) {
3049
+ $s = $this->seek();
3050
+ $tags = array();
3051
+ while ($this->tag($tt, true)) {
3052
+ $tags[] = $tt;
3053
+ $this->literal(">");
3054
+ }
3055
+
3056
+ if (count($tags) == 0) return false;
3057
+
3058
+ return true;
3059
+ }
3060
+
3061
+ // a bracketed value (contained within in a tag definition)
3062
+ protected function tagBracket(&$parts, &$hasExpression) {
3063
+ // speed shortcut
3064
+ if (isset($this->buffer[$this->count]) && $this->buffer[$this->count] != "[") {
3065
+ return false;
3066
+ }
3067
+
3068
+ $s = $this->seek();
3069
+
3070
+ $hasInterpolation = false;
3071
+
3072
+ if ($this->literal("[", false)) {
3073
+ $attrParts = array("[");
3074
+ // keyword, string, operator
3075
+ while (true) {
3076
+ if ($this->literal("]", false)) {
3077
+ $this->count--;
3078
+ break; // get out early
3079
+ }
3080
+
3081
+ if ($this->match('\s+', $m)) {
3082
+ $attrParts[] = " ";
3083
+ continue;
3084
+ }
3085
+ if ($this->string($str)) {
3086
+ // escape parent selector, (yuck)
3087
+ foreach ($str[2] as &$chunk) {
3088
+ $chunk = str_replace($this->lessc->parentSelector, "$&$", $chunk);
3089
+ }
3090
+
3091
+ $attrParts[] = $str;
3092
+ $hasInterpolation = true;
3093
+ continue;
3094
+ }
3095
+
3096
+ if ($this->keyword($word)) {
3097
+ $attrParts[] = $word;
3098
+ continue;
3099
+ }
3100
+
3101
+ if ($this->interpolation($inter, false)) {
3102
+ $attrParts[] = $inter;
3103
+ $hasInterpolation = true;
3104
+ continue;
3105
+ }
3106
+
3107
+ // operator, handles attr namespace too
3108
+ if ($this->match('[|-~\$\*\^=]+', $m)) {
3109
+ $attrParts[] = $m[0];
3110
+ continue;
3111
+ }
3112
+
3113
+ break;
3114
+ }
3115
+
3116
+ if ($this->literal("]", false)) {
3117
+ $attrParts[] = "]";
3118
+ foreach ($attrParts as $part) {
3119
+ $parts[] = $part;
3120
+ }
3121
+ $hasExpression = $hasExpression || $hasInterpolation;
3122
+ return true;
3123
+ }
3124
+ $this->seek($s);
3125
+ }
3126
+
3127
+ $this->seek($s);
3128
+ return false;
3129
+ }
3130
+
3131
+ // a space separated list of selectors
3132
+ protected function tag(&$tag, $simple = false) {
3133
+ if ($simple)
3134
+ $chars = '^@,:;{}\][>\(\) "\'';
3135
+ else
3136
+ $chars = '^@,;{}["\'';
3137
+
3138
+ $s = $this->seek();
3139
+
3140
+ $hasExpression = false;
3141
+ $parts = array();
3142
+ while ($this->tagBracket($parts, $hasExpression));
3143
+
3144
+ $oldWhite = $this->eatWhiteDefault;
3145
+ $this->eatWhiteDefault = false;
3146
+
3147
+ while (true) {
3148
+ if ($this->match('(['.$chars.'0-9]['.$chars.']*)', $m)) {
3149
+ $parts[] = $m[1];
3150
+ if ($simple) break;
3151
+
3152
+ while ($this->tagBracket($parts, $hasExpression));
3153
+ continue;
3154
+ }
3155
+
3156
+ if (isset($this->buffer[$this->count]) && $this->buffer[$this->count] == "@") {
3157
+ if ($this->interpolation($interp)) {
3158
+ $hasExpression = true;
3159
+ $interp[2] = true; // don't unescape
3160
+ $parts[] = $interp;
3161
+ continue;
3162
+ }
3163
+
3164
+ if ($this->literal("@")) {
3165
+ $parts[] = "@";
3166
+ continue;
3167
+ }
3168
+ }
3169
+
3170
+ if ($this->unit($unit)) { // for keyframes
3171
+ $parts[] = $unit[1];
3172
+ $parts[] = $unit[2];
3173
+ continue;
3174
+ }
3175
+
3176
+ break;
3177
+ }
3178
+
3179
+ $this->eatWhiteDefault = $oldWhite;
3180
+ if (!$parts) {
3181
+ $this->seek($s);
3182
+ return false;
3183
+ }
3184
+
3185
+ if ($hasExpression) {
3186
+ $tag = array("exp", array("string", "", $parts));
3187
+ } else {
3188
+ $tag = trim(implode($parts));
3189
+ }
3190
+
3191
+ $this->whitespace();
3192
+ return true;
3193
+ }
3194
+
3195
+ // a css function
3196
+ protected function func(&$func) {
3197
+ $s = $this->seek();
3198
+
3199
+ if ($this->match('(%|[\w\-_][\w\-_:\.]+|[\w_])', $m) && $this->literal('(')) {
3200
+ $fname = $m[1];
3201
+
3202
+ $sPreArgs = $this->seek();
3203
+
3204
+ $args = array();
3205
+ while (true) {
3206
+ $ss = $this->seek();
3207
+ // this ugly nonsense is for ie filter properties
3208
+ if ($this->keyword($name) && $this->literal('=') && $this->expressionList($value)) {
3209
+ $args[] = array("string", "", array($name, "=", $value));
3210
+ } else {
3211
+ $this->seek($ss);
3212
+ if ($this->expressionList($value)) {
3213
+ $args[] = $value;
3214
+ }
3215
+ }
3216
+
3217
+ if (!$this->literal(',')) break;
3218
+ }
3219
+ $args = array('list', ',', $args);
3220
+
3221
+ if ($this->literal(')')) {
3222
+ $func = array('function', $fname, $args);
3223
+ return true;
3224
+ } elseif ($fname == 'url') {
3225
+ // couldn't parse and in url? treat as string
3226
+ $this->seek($sPreArgs);
3227
+ if ($this->openString(")", $string) && $this->literal(")")) {
3228
+ $func = array('function', $fname, $string);
3229
+ return true;
3230
+ }
3231
+ }
3232
+ }
3233
+
3234
+ $this->seek($s);
3235
+ return false;
3236
+ }
3237
+
3238
+ // consume a less variable
3239
+ protected function variable(&$name) {
3240
+ $s = $this->seek();
3241
+ if ($this->literal($this->lessc->vPrefix, false) &&
3242
+ ($this->variable($sub) || $this->keyword($name)))
3243
+ {
3244
+ if (!empty($sub)) {
3245
+ $name = array('variable', $sub);
3246
+ } else {
3247
+ $name = $this->lessc->vPrefix.$name;
3248
+ }
3249
+ return true;
3250
+ }
3251
+
3252
+ $name = null;
3253
+ $this->seek($s);
3254
+ return false;
3255
+ }
3256
+
3257
+ /**
3258
+ * Consume an assignment operator
3259
+ * Can optionally take a name that will be set to the current property name
3260
+ */
3261
+ protected function assign($name = null) {
3262
+ if ($name) $this->currentProperty = $name;
3263
+ return $this->literal(':') || $this->literal('=');
3264
+ }
3265
+
3266
+ // consume a keyword
3267
+ protected function keyword(&$word) {
3268
+ if ($this->match('([\w_\-\*!"][\w\-_"]*)', $m)) {
3269
+ $word = $m[1];
3270
+ return true;
3271
+ }
3272
+ return false;
3273
+ }
3274
+
3275
+ // consume an end of statement delimiter
3276
+ protected function end() {
3277
+ if ($this->literal(';')) {
3278
+ return true;
3279
+ } elseif ($this->count == strlen($this->buffer) || $this->buffer[$this->count] == '}') {
3280
+ // if there is end of file or a closing block next then we don't need a ;
3281
+ return true;
3282
+ }
3283
+ return false;
3284
+ }
3285
+
3286
+ protected function guards(&$guards) {
3287
+ $s = $this->seek();
3288
+
3289
+ if (!$this->literal("when")) {
3290
+ $this->seek($s);
3291
+ return false;
3292
+ }
3293
+
3294
+ $guards = array();
3295
+
3296
+ while ($this->guardGroup($g)) {
3297
+ $guards[] = $g;
3298
+ if (!$this->literal(",")) break;
3299
+ }
3300
+
3301
+ if (count($guards) == 0) {
3302
+ $guards = null;
3303
+ $this->seek($s);
3304
+ return false;
3305
+ }
3306
+
3307
+ return true;
3308
+ }
3309
+
3310
+ // a bunch of guards that are and'd together
3311
+ // TODO rename to guardGroup
3312
+ protected function guardGroup(&$guardGroup) {
3313
+ $s = $this->seek();
3314
+ $guardGroup = array();
3315
+ while ($this->guard($guard)) {
3316
+ $guardGroup[] = $guard;
3317
+ if (!$this->literal("and")) break;
3318
+ }
3319
+
3320
+ if (count($guardGroup) == 0) {
3321
+ $guardGroup = null;
3322
+ $this->seek($s);
3323
+ return false;
3324
+ }
3325
+
3326
+ return true;
3327
+ }
3328
+
3329
+ protected function guard(&$guard) {
3330
+ $s = $this->seek();
3331
+ $negate = $this->literal("not");
3332
+
3333
+ if ($this->literal("(") && $this->expression($exp) && $this->literal(")")) {
3334
+ $guard = $exp;
3335
+ if ($negate) $guard = array("negate", $guard);
3336
+ return true;
3337
+ }
3338
+
3339
+ $this->seek($s);
3340
+ return false;
3341
+ }
3342
+
3343
+ /* raw parsing functions */
3344
+
3345
+ protected function literal($what, $eatWhitespace = null) {
3346
+ if ($eatWhitespace === null) $eatWhitespace = $this->eatWhiteDefault;
3347
+
3348
+ // shortcut on single letter
3349
+ if (!isset($what[1]) && isset($this->buffer[$this->count])) {
3350
+ if ($this->buffer[$this->count] == $what) {
3351
+ if (!$eatWhitespace) {
3352
+ $this->count++;
3353
+ return true;
3354
+ }
3355
+ // goes below...
3356
+ } else {
3357
+ return false;
3358
+ }
3359
+ }
3360
+
3361
+ if (!isset(self::$literalCache[$what])) {
3362
+ self::$literalCache[$what] = JupiterX_Lessc::preg_quote($what);
3363
+ }
3364
+
3365
+ return $this->match(self::$literalCache[$what], $m, $eatWhitespace);
3366
+ }
3367
+
3368
+ protected function genericList(&$out, $parseItem, $delim="", $flatten=true) {
3369
+ $s = $this->seek();
3370
+ $items = array();
3371
+ while ($this->$parseItem($value)) {
3372
+ $items[] = $value;
3373
+ if ($delim) {
3374
+ if (!$this->literal($delim)) break;
3375
+ }
3376
+ }
3377
+
3378
+ if (count($items) == 0) {
3379
+ $this->seek($s);
3380
+ return false;
3381
+ }
3382
+
3383
+ if ($flatten && count($items) == 1) {
3384
+ $out = $items[0];
3385
+ } else {
3386
+ $out = array("list", $delim, $items);
3387
+ }
3388
+
3389
+ return true;
3390
+ }
3391
+
3392
+
3393
+ // advance counter to next occurrence of $what
3394
+ // $until - don't include $what in advance
3395
+ // $allowNewline, if string, will be used as valid char set
3396
+ protected function to($what, &$out, $until = false, $allowNewline = false) {
3397
+ if (is_string($allowNewline)) {
3398
+ $validChars = $allowNewline;
3399
+ } else {
3400
+ $validChars = $allowNewline ? "." : "[^\n]";
3401
+ }
3402
+ if (!$this->match('('.$validChars.'*?)'.JupiterX_Lessc::preg_quote($what), $m, !$until)) return false;
3403
+ if ($until) $this->count -= strlen($what); // give back $what
3404
+ $out = $m[1];
3405
+ return true;
3406
+ }
3407
+
3408
+ // try to match something on head of buffer
3409
+ protected function match($regex, &$out, $eatWhitespace = null) {
3410
+ if ($eatWhitespace === null) $eatWhitespace = $this->eatWhiteDefault;
3411
+
3412
+ $r = '/'.$regex.($eatWhitespace && !$this->writeComments ? '\s*' : '').'/Ais';
3413
+ if (preg_match($r, $this->buffer, $out, null, $this->count)) {
3414
+ $this->count += strlen($out[0]);
3415
+ if ($eatWhitespace && $this->writeComments) $this->whitespace();
3416
+ return true;
3417
+ }
3418
+ return false;
3419
+ }
3420
+
3421
+ // match some whitespace
3422
+ protected function whitespace() {
3423
+ if ($this->writeComments) {
3424
+ $gotWhite = false;
3425
+ while (preg_match(self::$whitePattern, $this->buffer, $m, null, $this->count)) {
3426
+ if (isset($m[1]) && empty($this->commentsSeen[$this->count])) {
3427
+ $this->append(array("comment", $m[1]));
3428
+ $this->commentsSeen[$this->count] = true;
3429
+ }
3430
+ $this->count += strlen($m[0]);
3431
+ $gotWhite = true;
3432
+ }
3433
+ return $gotWhite;
3434
+ } else {
3435
+ $this->match("", $m);
3436
+ return strlen($m[0]) > 0;
3437
+ }
3438
+ }
3439
+
3440
+ // match something without consuming it
3441
+ protected function peek($regex, &$out = null, $from=null) {
3442
+ if (is_null($from)) $from = $this->count;
3443
+ $r = '/'.$regex.'/Ais';
3444
+ $result = preg_match($r, $this->buffer, $out, null, $from);
3445
+
3446
+ return $result;
3447
+ }
3448
+
3449
+ // seek to a spot in the buffer or return where we are on no argument
3450
+ protected function seek($where = null) {
3451
+ if ($where === null) return $this->count;
3452
+ else $this->count = $where;
3453
+ return true;
3454
+ }
3455
+
3456
+ /* misc functions */
3457
+
3458
+ public function throwError($msg = "parse error", $count = null) {
3459
+ $count = is_null($count) ? $this->count : $count;
3460
+
3461
+ $line = $this->line +
3462
+ substr_count(substr($this->buffer, 0, $count), "\n");
3463
+
3464
+ if (!empty($this->sourceName)) {
3465
+ $loc = "$this->sourceName on line $line";
3466
+ } else {
3467
+ $loc = "line: $line";
3468
+ }
3469
+
3470
+ // TODO this depends on $this->count
3471
+ if ($this->peek("(.*?)(\n|$)", $m, $count)) {
3472
+ throw new exception("$msg: failed at `$m[1]` $loc");
3473
+ } else {
3474
+ throw new exception("$msg: $loc");
3475
+ }
3476
+ }
3477
+
3478
+ protected function pushBlock($selectors=null, $type=null) {
3479
+ $b = new stdclass;
3480
+ $b->parent = $this->env;
3481
+
3482
+ $b->type = $type;
3483
+ $b->id = self::$nextBlockId++;
3484
+
3485
+ $b->isVararg = false; // TODO: kill me from here
3486
+ $b->tags = $selectors;
3487
+
3488
+ $b->props = array();
3489
+ $b->children = array();
3490
+
3491
+ $this->env = $b;
3492
+ return $b;
3493
+ }
3494
+
3495
+ // push a block that doesn't multiply tags
3496
+ protected function pushSpecialBlock($type) {
3497
+ return $this->pushBlock(null, $type);
3498
+ }
3499
+
3500
+ // append a property to the current block
3501
+ protected function append($prop, $pos = null) {
3502
+ if ($pos !== null) $prop[-1] = $pos;
3503
+ $this->env->props[] = $prop;
3504
+ }
3505
+
3506
+ // pop something off the stack
3507
+ protected function pop() {
3508
+ $old = $this->env;
3509
+ $this->env = $this->env->parent;
3510
+ return $old;
3511
+ }
3512
+
3513
+ // remove comments from $text
3514
+ // todo: make it work for all functions, not just url
3515
+ protected function removeComments($text) {
3516
+ $look = array(
3517
+ 'url(', '//', '/*', '"', "'"
3518
+ );
3519
+
3520
+ $out = '';
3521
+ $min = null;
3522
+ while (true) {
3523
+ // find the next item
3524
+ foreach ($look as $token) {
3525
+ $pos = strpos($text, $token);
3526
+ if ($pos !== false) {
3527
+ if (!isset($min) || $pos < $min[1]) $min = array($token, $pos);
3528
+ }
3529
+ }
3530
+
3531
+ if (is_null($min)) break;
3532
+
3533
+ $count = $min[1];
3534
+ $skip = 0;
3535
+ $newlines = 0;
3536
+ switch ($min[0]) {
3537
+ case 'url(':
3538
+ if (preg_match('/url\(.*?\)/', $text, $m, 0, $count))
3539
+ $count += strlen($m[0]) - strlen($min[0]);
3540
+ break;
3541
+ case '"':
3542
+ case "'":
3543
+ if (preg_match('/'.$min[0].'.*?(?<!\\\\)'.$min[0].'/', $text, $m, 0, $count))
3544
+ $count += strlen($m[0]) - 1;
3545
+ break;
3546
+ case '//':
3547
+ $skip = strpos($text, "\n", $count);
3548
+ if ($skip === false) $skip = strlen($text) - $count;
3549
+ else $skip -= $count;
3550
+ break;
3551
+ case '/*':
3552
+ if (preg_match('/\/\*.*?\*\//s', $text, $m, 0, $count)) {
3553
+ $skip = strlen($m[0]);
3554
+ $newlines = substr_count($m[0], "\n");
3555
+ }
3556
+ break;
3557
+ }
3558
+
3559
+ if ($skip == 0) $count += strlen($min[0]);
3560
+
3561
+ $out .= substr($text, 0, $count).str_repeat("\n", $newlines);
3562
+ $text = substr($text, $count + $skip);
3563
+
3564
+ $min = null;
3565
+ }
3566
+
3567
+ return $out.$text;
3568
+ }
3569
+
3570
+ }
3571
+
3572
+ /**
3573
+ * @ignore
3574
+ */
3575
+ class lessc_formatter_classic {
3576
+ public $indentChar = " ";
3577
+
3578
+ public $break = "\n";
3579
+ public $open = " {";
3580
+ public $close = "}";
3581
+ public $selectorSeparator = ", ";
3582
+ public $assignSeparator = ":";
3583
+
3584
+ public $openSingle = " { ";
3585
+ public $closeSingle = " }";
3586
+
3587
+ public $disableSingle = false;
3588
+ public $breakSelectors = false;
3589
+
3590
+ public $compressColors = false;
3591
+
3592
+ public function __construct() {
3593
+ $this->indentLevel = 0;
3594
+ }
3595
+
3596
+ public function indentStr($n = 0) {
3597
+ return str_repeat($this->indentChar, max($this->indentLevel + $n, 0));
3598
+ }
3599
+
3600
+ public function property($name, $value) {
3601
+ return $name . $this->assignSeparator . $value . ";";
3602
+ }
3603
+
3604
+ protected function isEmpty($block) {
3605
+ if (empty($block->lines)) {
3606
+ foreach ($block->children as $child) {
3607
+ if (!$this->isEmpty($child)) return false;
3608
+ }
3609
+
3610
+ return true;
3611
+ }
3612
+ return false;
3613
+ }
3614
+
3615
+ public function block($block) {
3616
+ if ($this->isEmpty($block)) return;
3617
+
3618
+ $inner = $pre = $this->indentStr();
3619
+
3620
+ $isSingle = !$this->disableSingle &&
3621
+ is_null($block->type) && count($block->lines) == 1;
3622
+
3623
+ if (!empty($block->selectors)) {
3624
+ $this->indentLevel++;
3625
+
3626
+ if ($this->breakSelectors) {
3627
+ $selectorSeparator = $this->selectorSeparator . $this->break . $pre;
3628
+ } else {
3629
+ $selectorSeparator = $this->selectorSeparator;
3630
+ }
3631
+
3632
+ echo $pre .
3633
+ implode($selectorSeparator, $block->selectors);
3634
+ if ($isSingle) {
3635
+ echo $this->openSingle;
3636
+ $inner = "";
3637
+ } else {
3638
+ echo $this->open . $this->break;
3639
+ $inner = $this->indentStr();
3640
+ }
3641
+
3642
+ }
3643
+
3644
+ if (!empty($block->lines)) {
3645
+ $glue = $this->break.$inner;
3646
+ echo $inner . implode($glue, $block->lines);
3647
+ if (!$isSingle && !empty($block->children)) {
3648
+ echo $this->break;
3649
+ }
3650
+ }
3651
+
3652
+ foreach ($block->children as $child) {
3653
+ $this->block($child);
3654
+ }
3655
+
3656
+ if (!empty($block->selectors)) {
3657
+ if (!$isSingle && empty($block->children)) echo $this->break;
3658
+
3659
+ if ($isSingle) {
3660
+ echo $this->closeSingle . $this->break;
3661
+ } else {
3662
+ echo $pre . $this->close . $this->break;
3663
+ }
3664
+
3665
+ $this->indentLevel--;
3666
+ }
3667
+ }
3668
+ }
3669
+
3670
+ /**
3671
+ * @ignore
3672
+ */
3673
+ class lessc_formatter_compressed extends lessc_formatter_classic {
3674
+ public $disableSingle = true;
3675
+ public $open = "{";
3676
+ public $selectorSeparator = ",";
3677
+ public $assignSeparator = ":";
3678
+ public $break = "";
3679
+ public $compressColors = true;
3680
+
3681
+ public function indentStr($n = 0) {
3682
+ return "";
3683
+ }
3684
+ }
3685
+
3686
+ /**
3687
+ * @ignore
3688
+ */
3689
+ class lessc_formatter_lessjs extends lessc_formatter_classic {
3690
+ public $disableSingle = true;
3691
+ public $breakSelectors = true;
3692
+ public $assignSeparator = ": ";
3693
+ public $selectorSeparator = ",";
3694
+ }
includes/control-panel/functions.php CHANGED
@@ -1,158 +1,159 @@
1
- <?php
2
- /**
3
- * The Jupiter Control Panel component.
4
- *
5
- * @package JupiterX_Core\Control_Panel
6
- */
7
-
8
- /**
9
- * Run on control panel init.
10
- *
11
- * @since 1.2.0
12
- */
13
- add_action( 'jupiterx_control_panel_init', function() {
14
- jupiterx_core()->load_files( [
15
- 'control-panel/includes/class-customizer-option',
16
- 'control-panel/includes/class-filesystem',
17
- 'control-panel/includes/class-validator',
18
- 'control-panel/includes/class-helpers',
19
- 'control-panel/includes/logic-messages',
20
- 'control-panel/includes/class-image-sizes',
21
- 'control-panel/includes/class-settings',
22
- 'control-panel/includes/class-db-manager',
23
- 'control-panel/includes/class-install-template',
24
- 'control-panel/includes/class-install-plugins',
25
- 'control-panel/includes/class-export-import-content',
26
- 'control-panel/includes/class-system-status',
27
- ] );
28
- } );
29
-
30
- /**
31
- * Get started quick guide.
32
- *
33
- * @since 1.2.0
34
- */
35
- add_action( 'jupiterx_control_panel_get_started', function() {
36
- ?>
37
- <h6><?php esc_html_e( 'Get started:', 'jupiterx-core' ); ?></h6>
38
- <iframe class="mb-4" width="400" height="225" src="https://www.youtube.com/embed/fnlzOHECEDo?modestbranding=1" frameborder="0" allowfullscreen></iframe>
39
- <?php
40
- } );
41
-
42
- add_filter( 'jupiterx_control_panel_sections', 'jupiterx_add_control_panel_sections' );
43
- /**
44
- * Add sections to control panel.
45
- *
46
- * @since 1.9.0
47
- */
48
- function jupiterx_add_control_panel_sections( $sections ) {
49
-
50
- $sections['image_size'] = [
51
- 'title' => __( 'Image Sizes' , 'jupiterx-core' ),
52
- 'href' => 'image-sizes',
53
- 'condition' => defined( 'JUPITERX_CONTROL_PANEL_IMAGE_SIZES' ) && JUPITERX_CONTROL_PANEL_IMAGE_SIZES,
54
- 'order' => 40,
55
- ];
56
-
57
- $sections['system_status'] = [
58
- 'title' => __( 'System Status' , 'jupiterx-core' ),
59
- 'href' => 'system-status',
60
- 'condition' => defined( 'JUPITERX_CONTROL_PANEL_SYSTEM_STATUS' ) && JUPITERX_CONTROL_PANEL_SYSTEM_STATUS,
61
- 'order' => 50,
62
- ];
63
-
64
- $sections['settings'] = [
65
- 'title' => __( 'Settings' , 'jupiterx-core' ),
66
- 'href' => 'settings',
67
- 'condition' => defined( 'JUPITERX_CONTROL_PANEL_SETTINGS' ) && JUPITERX_CONTROL_PANEL_SETTINGS,
68
- 'order' => 70,
69
- ];
70
-
71
- $sections['export_import'] = [
72
- 'title' => __( 'Export/Import' , 'jupiterx-core' ),
73
- 'href' => 'export-import',
74
- 'condition' => defined( 'JUPITERX_CONTROL_PANEL_EXPORT_IMPORT' ) && JUPITERX_CONTROL_PANEL_EXPORT_IMPORT,
75
- 'order' => 75,
76
- ];
77
-
78
- return $sections;
79
- }
80
-
81
- /**
82
- * Core additional settings.
83
- *
84
- * @since 1.2.0
85
- */
86
- add_action( 'jupiterx_control_panel_after_theme_settings', function() {
87
- ?>
88
- <div class="form-group col-md-6">
89
- <label for="jupiterx-cp-settings-svg-support">
90
- <?php esc_html_e( 'SVG Support', 'jupiterx-core' ); ?>
91
- </label>
92
- <input type="hidden" name="jupiterx_svg_support" value="0">
93
- <div class="jupiterx-switch">
94
- <input type="checkbox" id="jupiterx-cp-settings-svg-support" name="jupiterx_svg_support" value="1" <?php echo esc_attr( ( empty( jupiterx_get_option( 'svg_support' ) ) ) ? '' : 'checked' ); ?>>
95
- <label for="jupiterx-cp-settings-svg-support"></label>
96
- </div>
97
- <small class="form-text text-muted">
98
- <?php esc_html_e( 'Enable this option to upload SVG to WordPress Media Library.', 'jupiterx-core' ); ?>
99
- </small>
100
- </div>
101
- <div class="col-md-12">
102
- <hr />
103
- </div>
104
- <div class="form-group col-md-6">
105
- <label for="jupiterx-cp-settings-google-analytics-id"><?php esc_html_e( 'Google Analytics ID', 'jupiterx-core' ); ?></label>
106
- <?php jupiterx_the_help_link( 'http://help.artbees.net/how-to-s/theme-options/adding-google-analytics-code-into-jupiter-x', esc_html__( 'Adding Google Analytics code into Jupiter X', 'jupiterx-core' ) ); ?>
107
- <input type="text" class="jupiterx-form-control" id="jupiterx-cp-settings-google-analytics-id" value="<?php echo esc_attr( jupiterx_get_option( 'google_analytics_id' ) ); ?>" name="jupiterx_google_analytics_id" placeholder="UA-45******-*">
108
- </div>
109
- <div class="form-group col-md-6">
110
- <label for="jupiterx-cp-settings-google-analytics-anonymization"><?php esc_html_e( 'IP Anonymization', 'jupiterx-core' ); ?></label>
111
- <input type="hidden" name="jupiterx_google_analytics_anonymization" value="0">
112
- <div class="jupiterx-switch">
113
- <input type="checkbox" id="jupiterx-cp-settings-google-analytics-anonymization" name="jupiterx_google_analytics_anonymization" value="1" <?php echo esc_attr( jupiterx_get_option( 'google_analytics_anonymization', true ) ? 'checked' : '' ); ?>>
114
- <label for="jupiterx-cp-settings-google-analytics-anonymization"></label>
115
- </div>
116
- <small class="form-text text-muted"><?php esc_html_e( 'Enable IP Anonymization for Google Analytics.', 'jupiterx-core' ); ?></small>
117
- </div>
118
- <div class="form-group col-md-12">
119
- <label for="jupiterx-cp-settings-adobe-project-id"><?php esc_html_e( 'Adobe Fonts Project ID', 'jupiterx-core' ); ?></label>
120
- <?php jupiterx_the_help_link( 'http://help.artbees.net/how-to-s/typography/using-adobe-fonts-formerly-typekit-in-jupiter-x', esc_html__( 'Using Adobe fonts (formerly Typekit) in Jupiter X', 'jupiterx-core' ) ); ?>
121
- <?php jupiterx_pro_badge(); ?>
122
- <input <?php echo esc_attr( ( ! jupiterx_is_pro() ) ? 'disabled' : '' ); ?> type="text" class="jupiterx-form-control" id="jupiterx-cp-settings-adobe-project-id" value="<?php echo esc_attr( jupiterx_get_option( 'adobe_fonts_project_id' ) ); ?>" name="jupiterx_adobe_fonts_project_id" placeholder="ezv****">
123
- </div>
124
- <div class="col-md-12"><hr></div>
125
- <div class="form-group col-md-6">
126
- <label for="jupiterx-cp-settings-tracking-codes-after-head">
127
- <?php /* translators: %s: html */ ?>
128
- <?php printf( esc_html__( 'Tracking Codes After %s Tag', 'jupiterx-core' ), '<code>&#x3C;head&#x3E;</code>' ); ?>
129
- <?php jupiterx_pro_badge(); ?>
130
- </label>
131
- <textarea <?php echo esc_attr( ( ! jupiterx_is_pro() ) ? 'disabled' : '' ); ?> class="jupiterx-form-control" rows="7" id="jupiterx-cp-settings-tracking-codes-after-head" name="jupiterx_tracking_codes_after_head" rows="3"><?php echo esc_html( stripslashes( jupiterx_get_option( 'tracking_codes_after_head' ) ) ); ?></textarea>
132
- </div>
133
- <div class="form-group col-md-6">
134
- <label for="jupiterx-cp-settings-tracking-codes-before-head">
135
- <?php /* translators: %s: html */ ?>
136
- <?php printf( esc_html__( 'Tracking Codes Before %s Tag', 'jupiterx-core' ), '<code>&#x3C;/head&#x3E;</code>' ); ?>
137
- <?php jupiterx_pro_badge(); ?>
138
- </label>
139
- <textarea <?php echo esc_attr( ( ! jupiterx_is_pro() ) ? 'disabled' : '' ); ?> class="jupiterx-form-control" rows="7" id="jupiterx-cp-settings-tracking-codes-before-head" name="jupiterx_tracking_codes_before_head" rows="3"><?php echo esc_html( stripslashes( jupiterx_get_option( 'tracking_codes_before_head' ) ) ); ?></textarea>
140
- </div>
141
- <div class="form-group col-md-6">
142
- <label for="jupiterx-cp-settings-tracking-codes-after-body">
143
- <?php /* translators: %s: html */ ?>
144
- <?php printf( esc_html__( 'Tracking Codes After %s Tag', 'jupiterx-core' ), '<code>&#x3C;body&#x3E;</code>' ); ?>
145
- <?php jupiterx_pro_badge(); ?>
146
- </label>
147
- <textarea <?php echo esc_attr( ( ! jupiterx_is_pro() ) ? 'disabled' : '' ); ?> class="jupiterx-form-control" rows="7" id="jupiterx-cp-settings-tracking-codes-after-body" name="jupiterx_tracking_codes_after_body" rows="3"><?php echo esc_html( stripslashes( jupiterx_get_option( 'tracking_codes_after_body' ) ) ); ?></textarea>
148
- </div>
149
- <div class="form-group col-md-6">
150
- <label for="jupiterx-cp-settings-tracking-codes-before-body">
151
- <?php /* translators: %s: html */ ?>
152
- <?php printf( esc_html__( 'Tracking Codes Before %s Tag', 'jupiterx-core' ), '<code>&#x3C;/body&#x3E;</code>' ); ?>
153
- <?php jupiterx_pro_badge(); ?>
154
- </label>
155
- <textarea <?php echo esc_attr( ( ! jupiterx_is_pro() ) ? 'disabled' : '' ); ?> class="jupiterx-form-control" rows="7" id="jupiterx-cp-settings-tracking-codes-before-body" name="jupiterx_tracking_codes_before_body" rows="3"><?php echo esc_html( stripslashes( jupiterx_get_option( 'tracking_codes_before_body' ) ) ); ?></textarea>
156
- </div>
157
- <?php
158
- } );
 
1
+ <?php
2
+ /**
3
+ * The Jupiter Control Panel component.
4
+ *
5
+ * @package JupiterX_Core\Control_Panel
6
+ */
7
+
8
+ /**
9
+ * Run on control panel init.
10
+ *
11
+ * @since 1.2.0
12
+ */
13
+ add_action( 'jupiterx_control_panel_init', function() {
14
+ jupiterx_core()->load_files( [
15
+ 'control-panel/includes/class-customizer-option',
16
+ 'control-panel/includes/class-filesystem',
17
+ 'control-panel/includes/class-validator',
18
+ 'control-panel/includes/class-helpers',
19
+ 'control-panel/includes/logic-messages',
20
+ 'control-panel/includes/class-image-sizes',
21
+ 'control-panel/includes/class-settings',
22
+ 'control-panel/includes/class-db-manager',
23
+ 'control-panel/includes/class-install-template',
24
+ 'control-panel/includes/class-install-plugins',
25
+ 'control-panel/includes/class-export-import-content',
26
+ 'control-panel/includes/class-system-status',
27
+ 'control-panel/includes/class-db-php-manager',
28
+ ] );
29
+ } );
30
+
31
+ /**
32
+ * Get started quick guide.
33
+ *
34
+ * @since 1.2.0
35
+ */
36
+ add_action( 'jupiterx_control_panel_get_started', function() {
37
+ ?>
38
+ <h6><?php esc_html_e( 'Get started:', 'jupiterx-core' ); ?></h6>
39
+ <iframe class="mb-4" width="400" height="225" src="https://www.youtube.com/embed/fnlzOHECEDo?modestbranding=1" frameborder="0" allowfullscreen></iframe>
40
+ <?php
41
+ } );
42
+
43
+ add_filter( 'jupiterx_control_panel_sections', 'jupiterx_add_control_panel_sections' );
44
+ /**
45
+ * Add sections to control panel.
46
+ *
47
+ * @since 1.9.0
48
+ */
49
+ function jupiterx_add_control_panel_sections( $sections ) {
50
+
51
+ $sections['image_size'] = [
52
+ 'title' => __( 'Image Sizes' , 'jupiterx-core' ),
53
+ 'href' => 'image-sizes',
54
+ 'condition' => defined( 'JUPITERX_CONTROL_PANEL_IMAGE_SIZES' ) && JUPITERX_CONTROL_PANEL_IMAGE_SIZES,
55
+ 'order' => 40,
56
+ ];
57
+
58
+ $sections['system_status'] = [
59
+ 'title' => __( 'System Status' , 'jupiterx-core' ),
60
+ 'href' => 'system-status',
61
+ 'condition' => defined( 'JUPITERX_CONTROL_PANEL_SYSTEM_STATUS' ) && JUPITERX_CONTROL_PANEL_SYSTEM_STATUS,
62
+ 'order' => 50,
63
+ ];
64
+
65
+ $sections['settings'] = [
66
+ 'title' => __( 'Settings' , 'jupiterx-core' ),
67
+ 'href' => 'settings',
68
+ 'condition' => defined( 'JUPITERX_CONTROL_PANEL_SETTINGS' ) && JUPITERX_CONTROL_PANEL_SETTINGS,
69
+ 'order' => 70,
70
+ ];
71
+
72
+ $sections['export_import'] = [
73
+ 'title' => __( 'Export/Import' , 'jupiterx-core' ),
74
+ 'href' => 'export-import',
75
+ 'condition' => defined( 'JUPITERX_CONTROL_PANEL_EXPORT_IMPORT' ) && JUPITERX_CONTROL_PANEL_EXPORT_IMPORT,
76
+ 'order' => 75,
77
+ ];
78
+
79
+ return $sections;
80
+ }
81
+
82
+ /**
83
+ * Core additional settings.
84
+ *
85
+ * @since 1.2.0
86
+ */
87
+ add_action( 'jupiterx_control_panel_after_theme_settings', function() {
88
+ ?>
89
+ <div class="form-group col-md-6">
90
+ <label for="jupiterx-cp-settings-svg-support">
91
+ <?php esc_html_e( 'SVG Support', 'jupiterx-core' ); ?>
92
+ </label>
93
+ <input type="hidden" name="jupiterx_svg_support" value="0">
94
+ <div class="jupiterx-switch">
95
+ <input type="checkbox" id="jupiterx-cp-settings-svg-support" name="jupiterx_svg_support" value="1" <?php echo esc_attr( ( empty( jupiterx_get_option( 'svg_support' ) ) ) ? '' : 'checked' ); ?>>
96
+ <label for="jupiterx-cp-settings-svg-support"></label>
97
+ </div>
98
+ <small class="form-text text-muted">
99
+ <?php esc_html_e( 'Enable this option to upload SVG to WordPress Media Library.', 'jupiterx-core' ); ?>
100
+ </small>
101
+ </div>
102
+ <div class="col-md-12">
103
+ <hr />
104
+ </div>
105
+ <div class="form-group col-md-6">
106
+ <label for="jupiterx-cp-settings-google-analytics-id"><?php esc_html_e( 'Google Analytics ID', 'jupiterx-core' ); ?></label>
107
+ <?php jupiterx_the_help_link( 'http://help.artbees.net/how-to-s/theme-options/adding-google-analytics-code-into-jupiter-x', esc_html__( 'Adding Google Analytics code into Jupiter X', 'jupiterx-core' ) ); ?>
108
+ <input type="text" class="jupiterx-form-control" id="jupiterx-cp-settings-google-analytics-id" value="<?php echo esc_attr( jupiterx_get_option( 'google_analytics_id' ) ); ?>" name="jupiterx_google_analytics_id" placeholder="UA-45******-*">
109
+ </div>
110
+ <div class="form-group col-md-6">
111
+ <label for="jupiterx-cp-settings-google-analytics-anonymization"><?php esc_html_e( 'IP Anonymization', 'jupiterx-core' ); ?></label>
112
+ <input type="hidden" name="jupiterx_google_analytics_anonymization" value="0">
113
+ <div class="jupiterx-switch">
114
+ <input type="checkbox" id="jupiterx-cp-settings-google-analytics-anonymization" name="jupiterx_google_analytics_anonymization" value="1" <?php echo esc_attr( jupiterx_get_option( 'google_analytics_anonymization', true ) ? 'checked' : '' ); ?>>
115
+ <label for="jupiterx-cp-settings-google-analytics-anonymization"></label>
116
+ </div>
117
+ <small class="form-text text-muted"><?php esc_html_e( 'Enable IP Anonymization for Google Analytics.', 'jupiterx-core' ); ?></small>
118
+ </div>
119
+ <div class="form-group col-md-12">
120
+ <label for="jupiterx-cp-settings-adobe-project-id"><?php esc_html_e( 'Adobe Fonts Project ID', 'jupiterx-core' ); ?></label>
121
+ <?php jupiterx_the_help_link( 'http://help.artbees.net/how-to-s/typography/using-adobe-fonts-formerly-typekit-in-jupiter-x', esc_html__( 'Using Adobe fonts (formerly Typekit) in Jupiter X', 'jupiterx-core' ) ); ?>
122
+ <?php jupiterx_pro_badge(); ?>
123
+ <input <?php echo esc_attr( ( ! jupiterx_is_pro() ) ? 'disabled' : '' ); ?> type="text" class="jupiterx-form-control" id="jupiterx-cp-settings-adobe-project-id" value="<?php echo esc_attr( jupiterx_get_option( 'adobe_fonts_project_id' ) ); ?>" name="jupiterx_adobe_fonts_project_id" placeholder="ezv****">
124
+ </div>
125
+ <div class="col-md-12"><hr></div>
126
+ <div class="form-group col-md-6">
127
+ <label for="jupiterx-cp-settings-tracking-codes-after-head">
128
+ <?php /* translators: %s: html */ ?>
129
+ <?php printf( esc_html__( 'Tracking Codes After %s Tag', 'jupiterx-core' ), '<code>&#x3C;head&#x3E;</code>' ); ?>
130
+ <?php jupiterx_pro_badge(); ?>
131
+ </label>
132
+ <textarea <?php echo esc_attr( ( ! jupiterx_is_pro() ) ? 'disabled' : '' ); ?> class="jupiterx-form-control" rows="7" id="jupiterx-cp-settings-tracking-codes-after-head" name="jupiterx_tracking_codes_after_head" rows="3"><?php echo esc_html( stripslashes( jupiterx_get_option( 'tracking_codes_after_head' ) ) ); ?></textarea>
133
+ </div>
134
+ <div class="form-group col-md-6">
135
+ <label for="jupiterx-cp-settings-tracking-codes-before-head">
136
+ <?php /* translators: %s: html */ ?>
137
+ <?php printf( esc_html__( 'Tracking Codes Before %s Tag', 'jupiterx-core' ), '<code>&#x3C;/head&#x3E;</code>' ); ?>
138
+ <?php jupiterx_pro_badge(); ?>
139
+ </label>
140
+ <textarea <?php echo esc_attr( ( ! jupiterx_is_pro() ) ? 'disabled' : '' ); ?> class="jupiterx-form-control" rows="7" id="jupiterx-cp-settings-tracking-codes-before-head" name="jupiterx_tracking_codes_before_head" rows="3"><?php echo esc_html( stripslashes( jupiterx_get_option( 'tracking_codes_before_head' ) ) ); ?></textarea>
141
+ </div>
142
+ <div class="form-group col-md-6">
143
+ <label for="jupiterx-cp-settings-tracking-codes-after-body">
144
+ <?php /* translators: %s: html */ ?>
145
+ <?php printf( esc_html__( 'Tracking Codes After %s Tag', 'jupiterx-core' ), '<code>&#x3C;body&#x3E;</code>' ); ?>
146
+ <?php jupiterx_pro_badge(); ?>
147
+ </label>
148
+ <textarea <?php echo esc_attr( ( ! jupiterx_is_pro() ) ? 'disabled' : '' ); ?> class="jupiterx-form-control" rows="7" id="jupiterx-cp-settings-tracking-codes-after-body" name="jupiterx_tracking_codes_after_body" rows="3"><?php echo esc_html( stripslashes( jupiterx_get_option( 'tracking_codes_after_body' ) ) ); ?></textarea>
149
+ </div>
150
+ <div class="form-group col-md-6">
151
+ <label for="jupiterx-cp-settings-tracking-codes-before-body">
152
+ <?php /* translators: %s: html */ ?>
153
+ <?php printf( esc_html__( 'Tracking Codes Before %s Tag', 'jupiterx-core' ), '<code>&#x3C;/body&#x3E;</code>' ); ?>
154
+ <?php jupiterx_pro_badge(); ?>
155
+ </label>
156
+ <textarea <?php echo esc_attr( ( ! jupiterx_is_pro() ) ? 'disabled' : '' ); ?> class="jupiterx-form-control" rows="7" id="jupiterx-cp-settings-tracking-codes-before-body" name="jupiterx_tracking_codes_before_body" rows="3"><?php echo esc_html( stripslashes( jupiterx_get_option( 'tracking_codes_before_body' ) ) ); ?></textarea>
157
+ </div>
158
+ <?php
159
+ } );
includes/control-panel/includes/class-browser.php CHANGED
@@ -1,1193 +1,1193 @@
1
- <?php
2
- /**
3
- * Browser detection class
4
- *
5
- * @author Original Author: Chris Schuld (http://chrisschuld.com/)
6
- * @author Modifications for EDD: Chris Christoff
7
- * @version 1.9
8
- * Usage:
9
- * $browser = new Browser();
10
- * if( $browser->getBrowser() == Browser::BROWSER_FIREFOX && $browser->getVersion() >= 2 ) {
11
- * echo 'You have Firefox version 2 or greater';
12
- * }
13
- * User agents sampled from: http://www.useragentstring.com/
14
- * Based on original work from Gary White (http://apptools.com/phptools/browser/
15
- * CHANGELOG:
16
- * 2012-12-26 (v1.9c by Chris Christoff):
17
- * + Changed vars to publics
18
- * 2012-12-23 (v1.9b by Chris Christoff):
19
- * + Removed the browser string return and added spacing.
20
- * + Also removed return HTML formatting.
21
- * 2012-12-23 (v1.9a by Chris Christoff):
22
- * + Split user string and add formatting so we can print a nicely
23
- * formatted user agent string
24
- * 2010-08-20 (v1.9):
25
- * + Added MSN Explorer Browser (legacy)
26
- * + Added Bing/MSN Robot (Thanks Rob MacDonald)
27
- * + Added the Android Platform (PLATFORM_ANDROID)
28
- * + Fixed issue with Android 1.6/2.2 (Thanks Tom Hirashima)
29
- * 2010-04-27 (v1.8):
30
- * + Added iPad Support
31
- * 2010-03-07 (v1.7):
32
- * + *MAJOR* Rebuild (preg_match and other "slow" routine removal(s))
33
- * + Almost allof Gary's original code has been replaced
34
- * + Large PHPUNIT testing environment created to validate new releases and additions
35
- * + Added FreeBSD Platform
36
- * + Added OpenBSD Platform
37
- * + Added NetBSD Platform
38
- * + Added SunOS Platform
39
- * + Added OpenSolaris Platform
40
- * + Added support of the Iceweazel Browser
41
- * + Added isChromeFrame() call to check if chromeframe is in use
42
- * + Moved the Opera check in front of the Firefox check due to legacy Opera User Agents
43
- * + Added the __toString() method (Thanks Deano)
44
- * 2009-11-15:
45
- * + Updated the checkes for Firefox
46
- * + Added the NOKIA platform
47
- * + Added Checks for the NOKIA brower(s)
48
- * 2009-11-08:
49
- * + PHP 5.3 Support
50
- * + Added support for BlackBerry OS and BlackBerry browser
51
- * + Added support for the Opera Mini browser
52
- * + Added additional documenation
53
- * + Added support for isRobot() and isMobile()
54
- * + Added support for Opera version 10
55
- * + Added support for deprecated Netscape Navigator version 9
56
- * + Added support for IceCat
57
- * + Added support for Shiretoko
58
- * 2010-04-27 (v1.8):
59
- * + Added iPad Support
60
- * 2009-08-18:
61
- * + Updated to support PHP 5.3 - removed all deprecated function calls
62
- * + Updated to remove all double quotes (") -- converted to single quotes (')
63
- * 2009-04-27:
64
- * + Updated the IE check to remove a typo and bug (thanks John)
65
- * 2009-04-22:
66
- * + Added detection for GoogleBot
67
- * + Added detection for the W3C Validator.
68
- * + Added detection for Yahoo! Slurp
69
- * 2009-03-14:
70
- * + Added detection for iPods.
71
- * + Added Platform detection for iPhones
72
- * + Added Platform detection for iPods
73
- * 2009-02-16: (Rick Hale)
74
- * + Added version detection for Android phones.
75
- * 2008-12-09:
76
- * + Removed unused constant
77
- * 2008-11-07:
78
- * + Added Google's Chrome to the detection list
79
- * + Added isBrowser(string) to the list of functions special thanks to
80
- * Daniel 'mavrick' Lang for the function concept (http://mavrick.id.au)
81
- * Gary White noted: "Since browser detection is so unreliable, I am
82
- * no longer maintaining this script. You are free to use and or
83
- * modify/update it as you want, however the author assumes no
84
- * responsibility for the accuracy of the detected values."
85
- * Anyone experienced with Gary's script might be interested in these notes:
86
- * Added class constants
87
- * Added detection and version detection for Google's Chrome
88
- * Updated the version detection for Amaya
89
- * Updated the version detection for Firefox
90
- * Updated the version detection for Lynx
91
- * Updated the version detection for WebTV
92
- * Updated the version detection for NetPositive
93
- * Updated the version detection for IE
94
- * Updated the version detection for OmniWeb
95
- * Updated the version detection for iCab
96
- * Updated the version detection for Safari
97
- * Updated Safari to remove mobile devices (iPhone)
98
- * Added detection for iPhone
99
- * Added detection for robots
100
- * Added detection for mobile devices
101
- * Added detection for BlackBerry
102
- * Removed Netscape checks (matches heavily with firefox & mozilla)
103
- */
104
-
105
-
106
- // Exit if accessed directly
107
- if ( ! defined( 'ABSPATH' ) ) {
108
- exit;
109
- }
110
-
111
- if ( ! class_exists( 'Browser' ) ) {
112
-
113
- /**
114
- * Browser detection class
115
- *
116
- * @author Chris Schuld
117
- * @since 1.0
118
- */
119
- class Browser {
120
- public $_agent = '';
121
- public $_browser_name = '';
122
- public $_version = '';
123
- public $_platform = '';
124
- public $_os = '';
125
- public $_is_aol = false;
126
- public $_is_mobile = false;
127
- public $_is_robot = false;
128
- public $_aol_version = '';
129
-
130
- public $BROWSER_UNKNOWN = 'unknown';
131
- public $VERSION_UNKNOWN = 'unknown';
132
-
133
- public $BROWSER_OPERA = 'Opera'; // Http://www.opera.com/
134
- public $BROWSER_OPERA_MINI = 'Opera Mini'; // Http://www.opera.com/mini/
135
- public $BROWSER_WEBTV = 'WebTV'; // Http://www.webtv.net/pc/
136
- public $BROWSER_IE = 'Internet Explorer'; // Http://www.microsoft.com/ie/
137
- public $BROWSER_POCKET_IE = 'Pocket Internet Explorer'; // Http://en.wikipedia.org/wiki/Internet_Explorer_Mobile
138
- public $BROWSER_KONQUEROR = 'Konqueror'; // Http://www.konqueror.org/
139
- public $BROWSER_ICAB = 'iCab'; // Http://www.icab.de/
140
- public $BROWSER_OMNIWEB = 'OmniWeb'; // Http://www.omnigroup.com/applications/omniweb/
141
- public $BROWSER_FIREBIRD = 'Firebird'; // Http://www.ibphoenix.com/
142
- public $BROWSER_FIREFOX = 'Firefox'; // Http://www.mozilla.com/en-US/firefox/firefox.html
143
- public $BROWSER_ICEWEASEL = 'Iceweasel'; // Http://www.geticeweasel.org/
144
- public $BROWSER_SHIRETOKO = 'Shiretoko'; // Http://wiki.mozilla.org/Projects/shiretoko
145
- public $BROWSER_MOZILLA = 'Mozilla'; // Http://www.mozilla.com/en-US/
146
- public $BROWSER_AMAYA = 'Amaya'; // Http://www.w3.org/Amaya/
147
- public $BROWSER_LYNX = 'Lynx'; // Http://en.wikipedia.org/wiki/Lynx
148
- public $BROWSER_SAFARI = 'Safari'; // Http://apple.com
149
- public $BROWSER_IPHONE = 'iPhone'; // Http://apple.com
150
- public $BROWSER_IPOD = 'iPod'; // Http://apple.com
151
- public $BROWSER_IPAD = 'iPad'; // Http://apple.com
152
- public $BROWSER_CHROME = 'Chrome'; // Http://www.google.com/chrome
153
- public $BROWSER_ANDROID = 'Android'; // Http://www.android.com/
154
- public $BROWSER_GOOGLEBOT = 'GoogleBot'; // Http://en.wikipedia.org/wiki/Googlebot
155
- public $BROWSER_SLURP = 'Yahoo! Slurp'; // Http://en.wikipedia.org/wiki/Yahoo!_Slurp
156
- public $BROWSER_W3CVALIDATOR = 'W3C Validator'; // Http://validator.w3.org/
157
- public $BROWSER_BLACKBERRY = 'BlackBerry'; // Http://www.blackberry.com/
158
- public $BROWSER_ICECAT = 'IceCat'; // Http://en.wikipedia.org/wiki/GNU_IceCat
159
- public $BROWSER_NOKIA_S60 = 'Nokia S60 OSS Browser'; // Http://en.wikipedia.org/wiki/Web_Browser_for_S60
160
- public $BROWSER_NOKIA = 'Nokia Browser'; // * all other WAP-based browsers on the Nokia Platform
161
- public $BROWSER_MSN = 'MSN Browser'; // Http://explorer.msn.com/
162
- public $BROWSER_MSNBOT = 'MSN Bot'; // Http://search.msn.com/msnbot.htm
163
- // Http://en.wikipedia.org/wiki/Msnbot (used for Bing as well)
164
- public $BROWSER_NETSCAPE_NAVIGATOR = 'Netscape Navigator'; // Http://browser.netscape.com/ (DEPRECATED)
165
- public $BROWSER_GALEON = 'Galeon'; // Http://galeon.sourceforge.net/ (DEPRECATED)
166
- public $BROWSER_NETPOSITIVE = 'NetPositive'; // Http://en.wikipedia.org/wiki/NetPositive (DEPRECATED)
167
- public $BROWSER_PHOENIX = 'Phoenix'; // Http://en.wikipedia.org/wiki/History_of_Mozilla_Firefox (DEPRECATED)
168
-
169
- public $PLATFORM_UNKNOWN = 'unknown';
170
- public $PLATFORM_WINDOWS = 'Windows';
171
- public $PLATFORM_WINDOWS_CE = 'Windows CE';
172
- public $PLATFORM_APPLE = 'Apple';
173
- public $PLATFORM_LINUX = 'Linux';
174
- public $PLATFORM_OS2 = 'OS/2';
175
- public $PLATFORM_BEOS = 'BeOS';
176
- public $PLATFORM_IPHONE = 'iPhone';
177
- public $PLATFORM_IPOD = 'iPod';
178
- public $PLATFORM_IPAD = 'iPad';
179
- public $PLATFORM_BLACKBERRY = 'BlackBerry';
180
- public $PLATFORM_NOKIA = 'Nokia';
181
- public $PLATFORM_FREEBSD = 'FreeBSD';
182
- public $PLATFORM_OPENBSD = 'OpenBSD';
183
- public $PLATFORM_NETBSD = 'NetBSD';
184
- public $PLATFORM_SUNOS = 'SunOS';
185
- public $PLATFORM_OPENSOLARIS = 'OpenSolaris';
186
- public $PLATFORM_ANDROID = 'Android';
187
-
188
- public $OPERATING_SYSTEM_UNKNOWN = 'unknown';
189
-
190
- function __construct( $useragent = '' ) {
191
- $this->reset();
192
-
193
- if ( $useragent != '' ) {
194
- $this->setUserAgent( $useragent );
195
- } else {
196
- $this->determine();
197
- }
198
- }
199
-
200
- /**
201
- * Reset all properties
202
- */
203
- function reset() {
204
- $this->_agent = isset( $_SERVER['HTTP_USER_AGENT'] ) ? $_SERVER['HTTP_USER_AGENT'] : '';
205
- $this->_browser_name = $this->BROWSER_UNKNOWN;
206
- $this->_version = $this->VERSION_UNKNOWN;
207
- $this->_platform = $this->PLATFORM_UNKNOWN;
208
- $this->_os = $this->OPERATING_SYSTEM_UNKNOWN;
209
- $this->_is_aol = false;
210
- $this->_is_mobile = false;
211
- $this->_is_robot = false;
212
- $this->_aol_version = $this->VERSION_UNKNOWN;
213
- }
214
-
215
- /**
216
- * Check to see if the specific browser is valid
217
- *
218
- * @param string $browserName
219
- *
220
- * @return True if the browser is the specified browser
221
- */
222
- function isBrowser( $browserName ) {
223
- return ( 0 == strcasecmp( $this->_browser_name, trim( $browserName ) ) );
224
- }
225
-
226
- /**
227
- * The name of the browser. All return types are from the class contants
228
- *
229
- * @return string Name of the browser
230
- */
231
- function getBrowser() {
232
- return $this->_browser_name;
233
- }
234
-
235
- /**
236
- * Set the name of the browser
237
- *
238
- * @param $browser The name of the Browser
239
- */
240
- function setBrowser( $browser ) {
241
- return $this->_browser_name = $browser;
242
- }
243
-
244
- /**
245
- * The name of the platform. All return types are from the class contants
246
- *
247
- * @return string Name of the browser
248
- */
249
- function getPlatform() {
250
- return $this->_platform;
251
- }
252
-
253
- /**
254
- * Set the name of the platform
255
- *
256
- * @param $platform The name of the Platform
257
- */
258
- function setPlatform( $platform ) {
259
- return $this->_platform = $platform;
260
- }
261
-
262
- /**
263
- * The version of the browser.
264
- *
265
- * @return string Version of the browser (will only contain alpha-numeric characters and a period)
266
- */
267
- function getVersion() {
268
- return $this->_version;
269
- }
270
-
271
- /**
272
- * Set the version of the browser
273
- *
274
- * @param $version The version of the Browser
275
- */
276
- function setVersion( $version ) {
277
- $this->_version = preg_replace( '/[^0-9,.,a-z,A-Z-]/', '', $version );
278
- }
279
-
280
- /**
281
- * The version of AOL.
282
- *
283
- * @return string Version of AOL (will only contain alpha-numeric characters and a period)
284
- */
285
- function getAolVersion() {
286
- return $this->_aol_version;
287
- }
288
-
289
- /**
290
- * Set the version of AOL
291
- *
292
- * @param $version The version of AOL
293
- */
294
- function setAolVersion( $version ) {
295
- $this->_aol_version = preg_replace( '/[^0-9,.,a-z,A-Z]/', '', $version );
296
- }
297
-
298
- /**
299
- * Is the browser from AOL?
300
- *
301
- * @return boolean True if the browser is from AOL otherwise false
302
- */
303
- function isAol() {
304
- return $this->_is_aol;
305
- }
306
-
307
- /**
308
- * Is the browser from a mobile device?
309
- *
310
- * @return boolean True if the browser is from a mobile device otherwise false
311
- */
312
- function isMobile() {
313
- return $this->_is_mobile;
314
- }
315
-
316
- /**
317
- * Is the browser from a robot (ex Slurp,GoogleBot)?
318
- *
319
- * @return boolean True if the browser is from a robot otherwise false
320
- */
321
- function isRobot() {
322
- return $this->_is_robot;
323
- }
324
-
325
- /**
326
- * Set the browser to be from AOL
327
- *
328
- * @param $isAol
329
- */
330
- function setAol( $isAol ) {
331
- $this->_is_aol = $isAol;
332
- }
333
-
334
- /**
335
- * Set the Browser to be mobile
336
- *
337
- * @param boolean $value is the browser a mobile brower or not
338
- */
339
- function setMobile( $value = true ) {
340
- $this->_is_mobile = $value;
341
- }
342
-
343
- /**
344
- * Set the Browser to be a robot
345
- *
346
- * @param boolean $value is the browser a robot or not
347
- */
348
- function setRobot( $value = true ) {
349
- $this->_is_robot = $value;
350
- }
351
-
352
- /**
353
- * Get the user agent value in use to determine the browser
354
- *
355
- * @return string The user agent from the HTTP header
356
- */
357
- function getUserAgent() {
358
- return $this->_agent;
359
- }
360
-
361
- /**
362
- * Set the user agent value (the construction will use the HTTP header value - this will overwrite it)
363
- *
364
- * @param $agent_string The value for the User Agent
365
- */
366
- function setUserAgent( $agent_string ) {
367
- $this->reset();
368
- $this->_agent = $agent_string;
369
- $this->determine();
370
- }
371
-
372
- /**
373
- * Used to determine if the browser is actually "chromeframe"
374
- *
375
- * @since 1.7
376
- * @return boolean True if the browser is using chromeframe
377
- */
378
- function isChromeFrame() {
379
- return ( strpos( $this->_agent, 'chromeframe' ) !== false );
380
- }
381
-
382
- /**
383
- * Returns a formatted string with a summary of the details of the browser.
384
- *
385
- * @return string formatted string with a summary of the browser
386
- */
387
- function __toString() {
388
- $text1 = $this->getUserAgent(); // Grabs the UA string
389
- $UAline1 = substr( $text1, 0, 32 ); // The first line we print should only be the first 32 characters of the UA string
390
- $text2 = $this->getUserAgent(); // Now we grab it again and save it to a string
391
- $towrapUA = str_replace( $UAline1, '', $text2 ); // The rest of the printoff (other than first line) is equivalent
392
- // to the whole string minus the part we printed off. IE
393
- // User Agent: thefirst32charactersfromUAline1
394
- // the rest of it is now stored in
395
- // $text2 to be printed off
396
- // But we need to add spaces before each line that is split other than line 1
397
- $space = '';
398
- for ( $i = 0; $i < 25; $i ++ ) {
399
- $space .= ' ';
400
- }
401
-
402
- // Now we split the remaining string of UA ($text2) into lines that are prefixed by spaces for formatting
403
- $wordwrapped = chunk_split( $towrapUA, 32, "\n $space" );
404
-
405
- return "Platform: {$this->getPlatform()} \n" .
406
- "Browser Name: {$this->getBrowser()} \n" .
407
- "Browser Version: {$this->getVersion()} \n" .
408
- "User Agent String: $UAline1 \n\t\t\t " .
409
- "$wordwrapped";
410
- }
411
-
412
- /**
413
- * Protected routine to calculate and determine what the browser is in use (including platform)
414
- */
415
- function determine() {
416
- $this->checkPlatform();
417
- $this->checkBrowsers();
418
- $this->checkForAol();
419
- }
420
-
421
- /**
422
- * Protected routine to determine the browser type
423
- *
424
- * @return boolean True if the browser was detected otherwise false
425
- */
426
- function checkBrowsers() {
427
- return (
428
- // Well-known, well-used
429
- // Special Notes:
430
- // (1) Opera must be checked before FireFox due to the odd
431
- // user agents used in some older versions of Opera
432
- // (2) WebTV is strapped onto Internet Explorer so we must
433
- // check for WebTV before IE
434
- // (3) (deprecated) Galeon is based on Firefox and needs to be
435
- // tested before Firefox is tested
436
- // (4) OmniWeb is based on Safari so OmniWeb check must occur
437
- // before Safari
438
- // (5) Netscape 9+ is based on Firefox so Netscape checks
439
- // before FireFox are necessary
440
- $this->checkBrowserWebTv() ||
441
- $this->checkBrowserInternetExplorer() ||
442
- $this->checkBrowserOpera() ||
443
- $this->checkBrowserGaleon() ||
444
- $this->checkBrowserNetscapeNavigator9Plus() ||
445
- $this->checkBrowserFirefox() ||
446
- $this->checkBrowserChrome() ||
447
- $this->checkBrowserOmniWeb() ||
448
-
449
- // Common mobile
450
- $this->checkBrowserAndroid() ||
451
- $this->checkBrowseriPad() ||
452
- $this->checkBrowseriPod() ||
453
- $this->checkBrowseriPhone() ||
454
- $this->checkBrowserBlackBerry() ||
455
- $this->checkBrowserNokia() ||
456
-
457
- // Common bots
458
- $this->checkBrowserGoogleBot() ||
459
- $this->checkBrowserMSNBot() ||
460
- $this->checkBrowserSlurp() ||
461
-
462
- // WebKit base check (post mobile and others)
463
- $this->checkBrowserSafari() ||
464
-
465
- // Everyone else
466
- $this->checkBrowserNetPositive() ||
467
- $this->checkBrowserFirebird() ||
468
- $this->checkBrowserKonqueror() ||
469
- $this->checkBrowserIcab() ||
470
- $this->checkBrowserPhoenix() ||
471
- $this->checkBrowserAmaya() ||
472
- $this->checkBrowserLynx() ||
473
-
474
- $this->checkBrowserShiretoko() ||
475
- $this->checkBrowserIceCat() ||
476
- $this->checkBrowserW3CValidator() ||
477
- $this->checkBrowserMozilla() /* Mozilla is such an open standard that you must check it last */
478
- );
479
- }
480
-
481
- /**
482
- * Determine if the user is using a BlackBerry (last updated 1.7)
483
- *
484
- * @return boolean True if the browser is the BlackBerry browser otherwise false
485
- */
486
- function checkBrowserBlackBerry() {
487
- if ( stripos( $this->_agent, 'blackberry' ) !== false ) {
488
- $aresult = explode( '/', stristr( $this->_agent, 'BlackBerry' ) );
489
- $aversion = explode( ' ', $aresult[1] );
490
- $this->setVersion( $aversion[0] );
491
- $this->_browser_name = $this->BROWSER_BLACKBERRY;
492
- $this->setMobile( true );
493
-
494
- return true;
495
- }
496
-
497
- return false;
498
- }
499
-
500
- /**
501
- * Determine if the user is using an AOL User Agent (last updated 1.7)
502
- *
503
- * @return boolean True if the browser is from AOL otherwise false
504
- */
505
- function checkForAol() {
506
- $this->setAol( false );
507
- $this->setAolVersion( $this->VERSION_UNKNOWN );
508
-
509
- if ( stripos( $this->_agent, 'aol' ) !== false ) {
510
- $aversion = explode( ' ', stristr( $this->_agent, 'AOL' ) );
511
- $this->setAol( true );
512
- $this->setAolVersion( preg_replace( '/[^0-9\.a-z]/i', '', $aversion[1] ) );
513
-
514
- return true;
515
- }
516
-
517
- return false;
518
- }
519
-
520
- /**
521
- * Determine if the browser is the GoogleBot or not (last updated 1.7)
522
- *
523
- * @return boolean True if the browser is the GoogletBot otherwise false
524
- */
525
- function checkBrowserGoogleBot() {
526
- if ( stripos( $this->_agent, 'googlebot' ) !== false ) {
527
- $aresult = explode( '/', stristr( $this->_agent, 'googlebot' ) );
528
- $aversion = explode( ' ', $aresult[1] );
529
- $this->setVersion( str_replace( ';', '', $aversion[0] ) );
530
- $this->_browser_name = $this->BROWSER_GOOGLEBOT;
531
- $this->setRobot( true );
532
-
533
- return true;
534
- }
535
-
536
- return false;
537
- }
538
-
539
- /**
540
- * Determine if the browser is the MSNBot or not (last updated 1.9)
541
- *
542
- * @return boolean True if the browser is the MSNBot otherwise false
543
- */
544
- function checkBrowserMSNBot() {
545
- if ( stripos( $this->_agent, 'msnbot' ) !== false ) {
546
- $aresult = explode( '/', stristr( $this->_agent, 'msnbot' ) );
547
- $aversion = explode( ' ', $aresult[1] );
548
- $this->setVersion( str_replace( ';', '', $aversion[0] ) );
549
- $this->_browser_name = $this->BROWSER_MSNBOT;
550
- $this->setRobot( true );
551
-
552
- return true;
553
- }
554
-
555
- return false;
556
- }
557
-
558
- /**
559
- * Determine if the browser is the W3C Validator or not (last updated 1.7)
560
- *
561
- * @return boolean True if the browser is the W3C Validator otherwise false
562
- */
563
- function checkBrowserW3CValidator() {
564
- if ( stripos( $this->_agent, 'W3C-checklink' ) !== false ) {
565
- $aresult = explode( '/', stristr( $this->_agent, 'W3C-checklink' ) );
566
- $aversion = explode( ' ', $aresult[1] );
567
- $this->setVersion( $aversion[0] );
568
- $this->_browser_name = $this->BROWSER_W3CVALIDATOR;
569
-
570
- return true;
571
- } elseif ( stripos( $this->_agent, 'W3C_Validator' ) !== false ) {
572
- // Some of the Validator versions do not delineate w/ a slash - add it back in
573
- $ua = str_replace( 'W3C_Validator ', 'W3C_Validator/', $this->_agent );
574
- $aresult = explode( '/', stristr( $ua, 'W3C_Validator' ) );
575
- $aversion = explode( ' ', $aresult[1] );
576
- $this->setVersion( $aversion[0] );
577
- $this->_browser_name = $this->BROWSER_W3CVALIDATOR;
578
-
579
- return true;
580
- }
581
-
582
- return false;
583
- }
584
-
585
- /**
586
- * Determine if the browser is the Yahoo! Slurp Robot or not (last updated 1.7)
587
- *
588
- * @return boolean True if the browser is the Yahoo! Slurp Robot otherwise false
589
- */
590
- function checkBrowserSlurp() {
591
- if ( stripos( $this->_agent, 'slurp' ) !== false ) {
592
- $aresult = explode( '/', stristr( $this->_agent, 'Slurp' ) );
593
- $aversion = explode( ' ', $aresult[1] );
594
- $this->setVersion( $aversion[0] );
595
- $this->_browser_name = $this->BROWSER_SLURP;
596
- $this->setRobot( true );
597
- $this->setMobile( false );
598
-
599
- return true;
600
- }
601
-
602
- return false;
603
- }
604
-
605
- /**
606
- * Determine if the browser is Internet Explorer or not (last updated 1.7)
607
- *
608
- * @return boolean True if the browser is Internet Explorer otherwise false
609
- */
610
- function checkBrowserInternetExplorer() {
611
-
612
- // Test for v1 - v1.5 IE
613
- if ( stripos( $this->_agent, 'microsoft internet explorer' ) !== false ) {
614
- $this->setBrowser( $this->BROWSER_IE );
615
- $this->setVersion( '1.0' );
616
- $aresult = stristr( $this->_agent, '/' );
617
- if ( preg_match( '/308|425|426|474|0b1/i', $aresult ) ) {
618
- $this->setVersion( '1.5' );
619
- }
620
-
621
- return true;
622
- // Test for versions > 1.5
623
- } elseif ( stripos( $this->_agent, 'msie' ) !== false && stripos( $this->_agent, 'opera' ) === false ) {
624
- // See if the browser is the odd MSN Explorer
625
- if ( stripos( $this->_agent, 'msnb' ) !== false ) {
626
- $aresult = explode( ' ', stristr( str_replace( ';', '; ', $this->_agent ), 'MSN' ) );
627
- $this->setBrowser( $this->BROWSER_MSN );
628
- $this->setVersion( str_replace( array( '(', ')', ';' ), '', $aresult[1] ) );
629
-
630
- return true;
631
- }
632
- $aresult = explode( ' ', stristr( str_replace( ';', '; ', $this->_agent ), 'msie' ) );
633
- $this->setBrowser( $this->BROWSER_IE );
634
- $this->setVersion( str_replace( array( '(', ')', ';' ), '', $aresult[1] ) );
635
-
636
- return true;
637
- // Test for Pocket IE
638
- } elseif ( stripos( $this->_agent, 'mspie' ) !== false || stripos( $this->_agent, 'pocket' ) !== false ) {
639
- $aresult = explode( ' ', stristr( $this->_agent, 'mspie' ) );
640
- $this->setPlatform( $this->PLATFORM_WINDOWS_CE );
641
- $this->setBrowser( $this->BROWSER_POCKET_IE );
642
- $this->setMobile( true );
643
-
644
- if ( stripos( $this->_agent, 'mspie' ) !== false ) {
645
- $this->setVersion( $aresult[1] );
646
- } else {
647
- $aversion = explode( '/', $this->_agent );
648
- $this->setVersion( $aversion[1] );
649
- }
650
-
651
- return true;
652
- }// End if().
653
-
654
- return false;
655
- }
656
-
657
- /**
658
- * Determine if the browser is Opera or not (last updated 1.7)
659
- *
660
- * @return boolean True if the browser is Opera otherwise false
661
- */
662
- function checkBrowserOpera() {
663
- if ( stripos( $this->_agent, 'opera mini' ) !== false ) {
664
- $resultant = stristr( $this->_agent, 'opera mini' );
665
- if ( preg_match( '/\//', $resultant ) ) {
666
- $aresult = explode( '/', $resultant );
667
- $aversion = explode( ' ', $aresult[1] );
668
- $this->setVersion( $aversion[0] );
669
- } else {
670
- $aversion = explode( ' ', stristr( $resultant, 'opera mini' ) );
671
- $this->setVersion( $aversion[1] );
672
- }
673
- $this->_browser_name = $this->BROWSER_OPERA_MINI;
674
- $this->setMobile( true );
675
-
676
- return true;
677
- } elseif ( stripos( $this->_agent, 'opera' ) !== false ) {
678
- $resultant = stristr( $this->_agent, 'opera' );
679
- if ( preg_match( '/Version\/(10.*)$/', $resultant, $matches ) ) {
680
- $this->setVersion( $matches[1] );
681
- } elseif ( preg_match( '/\//', $resultant ) ) {
682
- $aresult = explode( '/', str_replace( '(', ' ', $resultant ) );
683
- $aversion = explode( ' ', $aresult[1] );
684
- $this->setVersion( $aversion[0] );
685
- } else {
686
- $aversion = explode( ' ', stristr( $resultant, 'opera' ) );
687
- $this->setVersion( isset( $aversion[1] ) ? $aversion[1] : '' );
688
- }
689
- $this->_browser_name = $this->BROWSER_OPERA;
690
-
691
- return true;
692
- }
693
-
694
- return false;
695
- }
696
-
697
- /**
698
- * Determine if the browser is Chrome or not (last updated 1.7)
699
- *
700
- * @return boolean True if the browser is Chrome otherwise false
701
- */
702
- function checkBrowserChrome() {
703
- if ( stripos( $this->_agent, 'Chrome' ) !== false ) {
704
- $aresult = explode( '/', stristr( $this->_agent, 'Chrome' ) );
705
- $aversion = explode( ' ', $aresult[1] );
706
- $this->setVersion( $aversion[0] );
707
- $this->setBrowser( $this->BROWSER_CHROME );
708
-
709
- return true;
710
- }
711
-
712
- return false;
713
- }
714
-
715
-
716
- /**
717
- * Determine if the browser is WebTv or not (last updated 1.7)
718
- *
719
- * @return boolean True if the browser is WebTv otherwise false
720
- */
721
- function checkBrowserWebTv() {
722
- if ( stripos( $this->_agent, 'webtv' ) !== false ) {
723
- $aresult = explode( '/', stristr( $this->_agent, 'webtv' ) );
724
- $aversion = explode( ' ', $aresult[1] );
725
- $this->setVersion( $aversion[0] );
726
- $this->setBrowser( $this->BROWSER_WEBTV );
727
-
728
- return true;
729
- }
730
-
731
- return false;
732
- }
733
-
734
- /**
735
- * Determine if the browser is NetPositive or not (last updated 1.7)
736
- *
737
- * @return boolean True if the browser is NetPositive otherwise false
738
- */
739
- function checkBrowserNetPositive() {
740
- if ( stripos( $this->_agent, 'NetPositive' ) !== false ) {
741
- $aresult = explode( '/', stristr( $this->_agent, 'NetPositive' ) );
742
- $aversion = explode( ' ', $aresult[1] );
743
- $this->setVersion( str_replace( array( '(', ')', ';' ), '', $aversion[0] ) );
744
- $this->setBrowser( $this->BROWSER_NETPOSITIVE );
745
-
746
- return true;
747
- }
748
-
749
- return false;
750
- }
751
-
752
- /**
753
- * Determine if the browser is Galeon or not (last updated 1.7)
754
- *
755
- * @return boolean True if the browser is Galeon otherwise false
756
- */
757
- function checkBrowserGaleon() {
758
- if ( stripos( $this->_agent, 'galeon' ) !== false ) {
759
- $aresult = explode( ' ', stristr( $this->_agent, 'galeon' ) );
760
- $aversion = explode( '/', $aresult[0] );
761
- $this->setVersion( $aversion[1] );
762
- $this->setBrowser( $this->BROWSER_GALEON );
763
-
764
- return true;
765
- }
766
-
767
- return false;
768
- }
769
-
770
- /**
771
- * Determine if the browser is Konqueror or not (last updated 1.7)
772
- *
773
- * @return boolean True if the browser is Konqueror otherwise false
774
- */
775
- function checkBrowserKonqueror() {
776
- if ( stripos( $this->_agent, 'Konqueror' ) !== false ) {
777
- $aresult = explode( ' ', stristr( $this->_agent, 'Konqueror' ) );
778
- $aversion = explode( '/', $aresult[0] );
779
- $this->setVersion( $aversion[1] );
780
- $this->setBrowser( $this->BROWSER_KONQUEROR );
781
-
782
- return true;
783
- }
784
-
785
- return false;
786
- }
787
-
788
- /**
789
- * Determine if the browser is iCab or not (last updated 1.7)
790
- *
791
- * @return boolean True if the browser is iCab otherwise false
792
- */
793
- function checkBrowserIcab() {
794
- if ( stripos( $this->_agent, 'icab' ) !== false ) {
795
- $aversion = explode( ' ', stristr( str_replace( '/', ' ', $this->_agent ), 'icab' ) );
796
- $this->setVersion( $aversion[1] );
797
- $this->setBrowser( $this->BROWSER_ICAB );
798
-
799
- return true;
800
- }
801
-
802
- return false;
803
- }
804
-
805
- /**
806
- * Determine if the browser is OmniWeb or not (last updated 1.7)
807
- *
808
- * @return boolean True if the browser is OmniWeb otherwise false
809
- */
810
- function checkBrowserOmniWeb() {
811
- if ( stripos( $this->_agent, 'omniweb' ) !== false ) {
812
- $aresult = explode( '/', stristr( $this->_agent, 'omniweb' ) );
813
- $aversion = explode( ' ', isset( $aresult[1] ) ? $aresult[1] : '' );
814
- $this->setVersion( $aversion[0] );
815
- $this->setBrowser( $this->BROWSER_OMNIWEB );
816
-
817
- return true;
818
- }
819
-
820
- return false;
821
- }
822
-
823
- /**
824
- * Determine if the browser is Phoenix or not (last updated 1.7)
825
- *
826
- * @return boolean True if the browser is Phoenix otherwise false
827
- */
828
- function checkBrowserPhoenix() {
829
- if ( stripos( $this->_agent, 'Phoenix' ) !== false ) {
830
- $aversion = explode( '/', stristr( $this->_agent, 'Phoenix' ) );
831
- $this->setVersion( $aversion[1] );
832
- $this->setBrowser( $this->BROWSER_PHOENIX );
833
-
834
- return true;
835
- }
836
-
837
- return false;
838
- }
839
-
840
- /**
841
- * Determine if the browser is Firebird or not (last updated 1.7)
842
- *
843
- * @return boolean True if the browser is Firebird otherwise false
844
- */
845
- function checkBrowserFirebird() {
846
- if ( stripos( $this->_agent, 'Firebird' ) !== false ) {
847
- $aversion = explode( '/', stristr( $this->_agent, 'Firebird' ) );
848
- $this->setVersion( $aversion[1] );
849
- $this->setBrowser( $this->BROWSER_FIREBIRD );
850
-
851
- return true;
852
- }
853
-
854
- return false;
855
- }
856
-
857
- /**
858
- * Determine if the browser is Netscape Navigator 9+ or not (last updated 1.7)
859
- * NOTE: (http://browser.netscape.com/ - Official support ended on March 1st, 2008)
860
- *
861
- * @return boolean True if the browser is Netscape Navigator 9+ otherwise false
862
- */
863
- function checkBrowserNetscapeNavigator9Plus() {
864
- if ( stripos( $this->_agent, 'Firefox' ) !== false && preg_match( '/Navigator\/([^ ]*)/i', $this->_agent, $matches ) ) {
865
- $this->setVersion( $matches[1] );
866
- $this->setBrowser( $this->BROWSER_NETSCAPE_NAVIGATOR );
867
-
868
- return true;
869
- } elseif ( stripos( $this->_agent, 'Firefox' ) === false && preg_match( '/Netscape6?\/([^ ]*)/i', $this->_agent, $matches ) ) {
870
- $this->setVersion( $matches[1] );
871
- $this->setBrowser( $this->BROWSER_NETSCAPE_NAVIGATOR );
872
-
873
- return true;
874
- }
875
-
876
- return false;
877
- }
878
-
879
- /**
880
- * Determine if the browser is Shiretoko or not (https://wiki.mozilla.org/Projects/shiretoko) (last updated 1.7)
881
- *
882
- * @return boolean True if the browser is Shiretoko otherwise false
883
- */
884
- function checkBrowserShiretoko() {
885
- if ( stripos( $this->_agent, 'Mozilla' ) !== false && preg_match( '/Shiretoko\/([^ ]*)/i', $this->_agent, $matches ) ) {
886
- $this->setVersion( $matches[1] );
887
- $this->setBrowser( $this->BROWSER_SHIRETOKO );
888
-
889
- return true;
890
- }
891
-
892
- return false;
893
- }
894
-
895
- /**
896
- * Determine if the browser is Ice Cat or not (http://en.wikipedia.org/wiki/GNU_IceCat) (last updated 1.7)
897
- *
898
- * @return boolean True if the browser is Ice Cat otherwise false
899
- */
900
- function checkBrowserIceCat() {
901
- if ( stripos( $this->_agent, 'Mozilla' ) !== false && preg_match( '/IceCat\/([^ ]*)/i', $this->_agent, $matches ) ) {
902
- $this->setVersion( $matches[1] );
903
- $this->setBrowser( $this->BROWSER_ICECAT );
904
-
905
- return true;
906
- }
907
-
908
- return false;
909
- }
910
-
911
- /**
912
- * Determine if the browser is Nokia or not (last updated 1.7)
913
- *
914
- * @return boolean True if the browser is Nokia otherwise false
915
- */
916
- function checkBrowserNokia() {
917
- if ( preg_match( '/Nokia([^\/]+)\/([^ SP]+)/i', $this->_agent, $matches ) ) {
918
- $this->setVersion( $matches[2] );
919
- if ( stripos( $this->_agent, 'Series60' ) !== false || strpos( $this->_agent, 'S60' ) !== false ) {
920
- $this->setBrowser( $this->BROWSER_NOKIA_S60 );
921
- } else {
922
- $this->setBrowser( $this->BROWSER_NOKIA );
923
- }
924
- $this->setMobile( true );
925
-
926
- return true;
927
- }
928
-
929
- return false;
930
- }
931
-
932
- /**
933
- * Determine if the browser is Firefox or not (last updated 1.7)
934
- *
935
- * @return boolean True if the browser is Firefox otherwise false
936
- */
937
- function checkBrowserFirefox() {
938
- if ( stripos( $this->_agent, 'safari' ) === false ) {
939
- if ( preg_match( '/Firefox[\/ \(]([^ ;\)]+)/i', $this->_agent, $matches ) ) {
940
- $this->setVersion( $matches[1] );
941
- $this->setBrowser( $this->BROWSER_FIREFOX );
942
-
943
- return true;
944
- } elseif ( preg_match( '/Firefox$/i', $this->_agent, $matches ) ) {
945
- $this->setVersion( '' );
946
- $this->setBrowser( $this->BROWSER_FIREFOX );
947
-
948
- return true;
949
- }
950
- }
951
-
952
- return false;
953
- }
954
-
955
- /**
956
- * Determine if the browser is Firefox or not (last updated 1.7)
957
- *
958
- * @return boolean True if the browser is Firefox otherwise false
959
- */
960
- function checkBrowserIceweasel() {
961
- if ( stripos( $this->_agent, 'Iceweasel' ) !== false ) {
962
- $aresult = explode( '/', stristr( $this->_agent, 'Iceweasel' ) );
963
- $aversion = explode( ' ', $aresult[1] );
964
- $this->setVersion( $aversion[0] );
965
- $this->setBrowser( $this->BROWSER_ICEWEASEL );
966
-
967
- return true;
968
- }
969
-
970
- return false;
971
- }
972
-
973
- /**
974
- * Determine if the browser is Mozilla or not (last updated 1.7)
975
- *
976
- * @return boolean True if the browser is Mozilla otherwise false
977
- */
978
- function checkBrowserMozilla() {
979
- if ( stripos( $this->_agent, 'mozilla' ) !== false && preg_match( '/rv:[0-9].[0-9][a-b]?/i', $this->_agent ) && stripos( $this->_agent, 'netscape' ) === false ) {
980
- $aversion = explode( ' ', stristr( $this->_agent, 'rv:' ) );
981
- preg_match( '/rv:[0-9].[0-9][a-b]?/i', $this->_agent, $aversion );
982
- $this->setVersion( str_replace( 'rv:', '', $aversion[0] ) );
983
- $this->setBrowser( $this->BROWSER_MOZILLA );
984
-
985
- return true;
986
- } elseif ( stripos( $this->_agent, 'mozilla' ) !== false && preg_match( '/rv:[0-9]\.[0-9]/i', $this->_agent ) && stripos( $this->_agent, 'netscape' ) === false ) {
987
- $aversion = explode( '', stristr( $this->_agent, 'rv:' ) );
988
- $this->setVersion( str_replace( 'rv:', '', $aversion[0] ) );
989
- $this->setBrowser( $this->BROWSER_MOZILLA );
990
-
991
- return true;
992
- } elseif ( stripos( $this->_agent, 'mozilla' ) !== false && preg_match( '/mozilla\/([^ ]*)/i', $this->_agent, $matches ) && stripos( $this->_agent, 'netscape' ) === false ) {
993
- $this->setVersion( $matches[1] );
994
- $this->setBrowser( $this->BROWSER_MOZILLA );
995
-
996
- return true;
997
- }
998
-
999
- return false;
1000
- }
1001
-
1002
- /**
1003
- * Determine if the browser is Lynx or not (last updated 1.7)
1004
- *
1005
- * @return boolean True if the browser is Lynx otherwise false
1006
- */
1007
- function checkBrowserLynx() {
1008
- if ( stripos( $this->_agent, 'lynx' ) !== false ) {
1009
- $aresult = explode( '/', stristr( $this->_agent, 'Lynx' ) );
1010
- $aversion = explode( ' ', ( isset( $aresult[1] ) ? $aresult[1] : '' ) );
1011
- $this->setVersion( $aversion[0] );
1012
- $this->setBrowser( $this->BROWSER_LYNX );
1013
-
1014
- return true;
1015
- }
1016
-
1017
- return false;
1018
- }
1019
-
1020
- /**
1021
- * Determine if the browser is Amaya or not (last updated 1.7)
1022
- *
1023
- * @return boolean True if the browser is Amaya otherwise false
1024
- */
1025
- function checkBrowserAmaya() {
1026
- if ( stripos( $this->_agent, 'amaya' ) !== false ) {
1027
- $aresult = explode( '/', stristr( $this->_agent, 'Amaya' ) );
1028
- $aversion = explode( ' ', $aresult[1] );
1029
- $this->setVersion( $aversion[0] );
1030
- $this->setBrowser( $this->BROWSER_AMAYA );
1031
-
1032
- return true;
1033
- }
1034
-
1035
- return false;
1036
- }
1037
-
1038
- /**
1039
- * Determine if the browser is Safari or not (last updated 1.7)
1040
- *
1041
- * @return boolean True if the browser is Safari otherwise false
1042
- */
1043
- function checkBrowserSafari() {
1044
- if ( stripos( $this->_agent, 'Safari' ) !== false && stripos( $this->_agent, 'iPhone' ) === false && stripos( $this->_agent, 'iPod' ) === false ) {
1045
- $aresult = explode( '/', stristr( $this->_agent, 'Version' ) );
1046
- if ( isset( $aresult[1] ) ) {
1047
- $aversion = explode( ' ', $aresult[1] );
1048
- $this->setVersion( $aversion[0] );
1049
- } else {
1050
- $this->setVersion( $this->VERSION_UNKNOWN );
1051
- }
1052
- $this->setBrowser( $this->BROWSER_SAFARI );
1053
-
1054
- return true;
1055
- }
1056
-
1057
- return false;
1058
- }
1059
-
1060
- /**
1061
- * Determine if the browser is iPhone or not (last updated 1.7)
1062
- *
1063
- * @return boolean True if the browser is iPhone otherwise false
1064
- */
1065
- function checkBrowseriPhone() {
1066
- if ( stripos( $this->_agent, 'iPhone' ) !== false ) {
1067
- $aresult = explode( '/', stristr( $this->_agent, 'Version' ) );
1068
- if ( isset( $aresult[1] ) ) {
1069
- $aversion = explode( ' ', $aresult[1] );
1070
- $this->setVersion( $aversion[0] );
1071
- } else {
1072
- $this->setVersion( $this->VERSION_UNKNOWN );
1073
- }
1074
- $this->setMobile( true );
1075
- $this->setBrowser( $this->BROWSER_IPHONE );
1076
-
1077
- return true;
1078
- }
1079
-
1080
- return false;
1081
- }
1082
-
1083
- /**
1084
- * Determine if the browser is iPod or not (last updated 1.7)
1085
- *
1086
- * @return boolean True if the browser is iPod otherwise false
1087
- */
1088
- function checkBrowseriPad() {
1089
- if ( stripos( $this->_agent, 'iPad' ) !== false ) {
1090
- $aresult = explode( '/', stristr( $this->_agent, 'Version' ) );
1091
- if ( isset( $aresult[1] ) ) {
1092
- $aversion = explode( ' ', $aresult[1] );
1093
- $this->setVersion( $aversion[0] );
1094
- } else {
1095
- $this->setVersion( $this->VERSION_UNKNOWN );
1096
- }
1097
- $this->setMobile( true );
1098
- $this->setBrowser( $this->BROWSER_IPAD );
1099
-
1100
- return true;
1101
- }
1102
-
1103
- return false;
1104
- }
1105
-
1106
- /**
1107
- * Determine if the browser is iPod or not (last updated 1.7)
1108
- *
1109
- * @return boolean True if the browser is iPod otherwise false
1110
- */
1111
- function checkBrowseriPod() {
1112
- if ( stripos( $this->_agent, 'iPod' ) !== false ) {
1113
- $aresult = explode( '/', stristr( $this->_agent, 'Version' ) );
1114
- if ( isset( $aresult[1] ) ) {
1115
- $aversion = explode( ' ', $aresult[1] );
1116
- $this->setVersion( $aversion[0] );
1117
- } else {
1118
- $this->setVersion( $this->VERSION_UNKNOWN );
1119
- }
1120
- $this->setMobile( true );
1121
- $this->setBrowser( $this->BROWSER_IPOD );
1122
-
1123
- return true;
1124
- }
1125
-
1126
- return false;
1127
- }
1128
-
1129
- /**
1130
- * Determine if the browser is Android or not (last updated 1.7)
1131
- *
1132
- * @return boolean True if the browser is Android otherwise false
1133
- */
1134
- function checkBrowserAndroid() {
1135
- if ( stripos( $this->_agent, 'Android' ) !== false ) {
1136
- $aresult = explode( ' ', stristr( $this->_agent, 'Android' ) );
1137
- if ( isset( $aresult[1] ) ) {
1138
- $aversion = explode( ' ', $aresult[1] );
1139
- $this->setVersion( $aversion[0] );
1140
- } else {
1141
- $this->setVersion( $this->VERSION_UNKNOWN );
1142
- }
1143
- $this->setMobile( true );
1144
- $this->setBrowser( $this->BROWSER_ANDROID );
1145
-
1146
- return true;
1147
- }
1148
-
1149
- return false;
1150
- }
1151
-
1152
- /**
1153
- * Determine the user's platform (last updated 1.7)
1154
- */
1155
- function checkPlatform() {
1156
- if ( stripos( $this->_agent, 'windows' ) !== false ) {
1157
- $this->_platform = $this->PLATFORM_WINDOWS;
1158
- } elseif ( stripos( $this->_agent, 'iPad' ) !== false ) {
1159
- $this->_platform = $this->PLATFORM_IPAD;
1160
- } elseif ( stripos( $this->_agent, 'iPod' ) !== false ) {
1161
- $this->_platform = $this->PLATFORM_IPOD;
1162
- } elseif ( stripos( $this->_agent, 'iPhone' ) !== false ) {
1163
- $this->_platform = $this->PLATFORM_IPHONE;
1164
- } elseif ( stripos( $this->_agent, 'mac' ) !== false ) {
1165
- $this->_platform = $this->PLATFORM_APPLE;
1166
- } elseif ( stripos( $this->_agent, 'android' ) !== false ) {
1167
- $this->_platform = $this->PLATFORM_ANDROID;
1168
- } elseif ( stripos( $this->_agent, 'linux' ) !== false ) {
1169
- $this->_platform = $this->PLATFORM_LINUX;
1170
- } elseif ( stripos( $this->_agent, 'Nokia' ) !== false ) {
1171
- $this->_platform = $this->PLATFORM_NOKIA;
1172
- } elseif ( stripos( $this->_agent, 'BlackBerry' ) !== false ) {
1173
- $this->_platform = $this->PLATFORM_BLACKBERRY;
1174
- } elseif ( stripos( $this->_agent, 'FreeBSD' ) !== false ) {
1175
- $this->_platform = $this->PLATFORM_FREEBSD;
1176
- } elseif ( stripos( $this->_agent, 'OpenBSD' ) !== false ) {
1177
- $this->_platform = $this->PLATFORM_OPENBSD;
1178
- } elseif ( stripos( $this->_agent, 'NetBSD' ) !== false ) {
1179
- $this->_platform = $this->PLATFORM_NETBSD;
1180
- } elseif ( stripos( $this->_agent, 'OpenSolaris' ) !== false ) {
1181
- $this->_platform = $this->PLATFORM_OPENSOLARIS;
1182
- } elseif ( stripos( $this->_agent, 'SunOS' ) !== false ) {
1183
- $this->_platform = $this->PLATFORM_SUNOS;
1184
- } elseif ( stripos( $this->_agent, 'OS\/2' ) !== false ) {
1185
- $this->_platform = $this->PLATFORM_OS2;
1186
- } elseif ( stripos( $this->_agent, 'BeOS' ) !== false ) {
1187
- $this->_platform = $this->PLATFORM_BEOS;
1188
- } elseif ( stripos( $this->_agent, 'win' ) !== false ) {
1189
- $this->_platform = $this->PLATFORM_WINDOWS;
1190
- }
1191
- }
1192
- }
1193
- }// End if().
1
+ <?php
2
+ /**
3
+ * Browser detection class
4
+ *
5
+ * @author Original Author: Chris Schuld (http://chrisschuld.com/)
6
+ * @author Modifications for EDD: Chris Christoff
7
+ * @version 1.9
8
+ * Usage:
9
+ * $browser = new Browser();
10
+ * if( $browser->getBrowser() == Browser::BROWSER_FIREFOX && $browser->getVersion() >= 2 ) {
11
+ * echo 'You have Firefox version 2 or greater';
12
+ * }
13
+ * User agents sampled from: http://www.useragentstring.com/
14
+ * Based on original work from Gary White (http://apptools.com/phptools/browser/
15
+ * CHANGELOG:
16
+ * 2012-12-26 (v1.9c by Chris Christoff):
17
+ * + Changed vars to publics
18
+ * 2012-12-23 (v1.9b by Chris Christoff):
19
+ * + Removed the browser string return and added spacing.
20
+ * + Also removed return HTML formatting.
21
+ * 2012-12-23 (v1.9a by Chris Christoff):
22
+ * + Split user string and add formatting so we can print a nicely
23
+ * formatted user agent string
24
+ * 2010-08-20 (v1.9):
25
+ * + Added MSN Explorer Browser (legacy)
26
+ * + Added Bing/MSN Robot (Thanks Rob MacDonald)
27
+ * + Added the Android Platform (PLATFORM_ANDROID)
28
+ * + Fixed issue with Android 1.6/2.2 (Thanks Tom Hirashima)
29
+ * 2010-04-27 (v1.8):
30
+ * + Added iPad Support
31
+ * 2010-03-07 (v1.7):
32
+ * + *MAJOR* Rebuild (preg_match and other "slow" routine removal(s))
33
+ * + Almost allof Gary's original code has been replaced
34
+ * + Large PHPUNIT testing environment created to validate new releases and additions
35
+ * + Added FreeBSD Platform
36
+ * + Added OpenBSD Platform
37
+ * + Added NetBSD Platform
38
+ * + Added SunOS Platform
39
+ * + Added OpenSolaris Platform
40
+ * + Added support of the Iceweazel Browser
41
+ * + Added isChromeFrame() call to check if chromeframe is in use
42
+ * + Moved the Opera check in front of the Firefox check due to legacy Opera User Agents
43
+ * + Added the __toString() method (Thanks Deano)
44
+ * 2009-11-15:
45
+ * + Updated the checkes for Firefox
46
+ * + Added the NOKIA platform
47
+ * + Added Checks for the NOKIA brower(s)
48
+ * 2009-11-08:
49
+ * + PHP 5.3 Support
50
+ * + Added support for BlackBerry OS and BlackBerry browser
51
+ * + Added support for the Opera Mini browser
52
+ * + Added additional documenation
53
+ * + Added support for isRobot() and isMobile()
54
+ * + Added support for Opera version 10
55
+ * + Added support for deprecated Netscape Navigator version 9
56
+ * + Added support for IceCat
57
+ * + Added support for Shiretoko
58
+ * 2010-04-27 (v1.8):
59
+ * + Added iPad Support
60
+ * 2009-08-18:
61
+ * + Updated to support PHP 5.3 - removed all deprecated function calls
62
+ * + Updated to remove all double quotes (") -- converted to single quotes (')
63
+ * 2009-04-27:
64
+ * + Updated the IE check to remove a typo and bug (thanks John)
65
+ * 2009-04-22:
66
+ * + Added detection for GoogleBot
67
+ * + Added detection for the W3C Validator.
68
+ * + Added detection for Yahoo! Slurp
69
+ * 2009-03-14:
70
+ * + Added detection for iPods.
71
+ * + Added Platform detection for iPhones
72
+ * + Added Platform detection for iPods
73
+ * 2009-02-16: (Rick Hale)
74
+ * + Added version detection for Android phones.
75
+ * 2008-12-09:
76
+ * + Removed unused constant
77
+ * 2008-11-07:
78
+ * + Added Google's Chrome to the detection list
79
+ * + Added isBrowser(string) to the list of functions special thanks to
80
+ * Daniel 'mavrick' Lang for the function concept (http://mavrick.id.au)
81
+ * Gary White noted: "Since browser detection is so unreliable, I am
82
+ * no longer maintaining this script. You are free to use and or
83
+ * modify/update it as you want, however the author assumes no
84
+ * responsibility for the accuracy of the detected values."
85
+ * Anyone experienced with Gary's script might be interested in these notes:
86
+ * Added class constants
87
+ * Added detection and version detection for Google's Chrome
88
+ * Updated the version detection for Amaya
89
+ * Updated the version detection for Firefox
90
+ * Updated the version detection for Lynx
91
+ * Updated the version detection for WebTV
92
+ * Updated the version detection for NetPositive
93
+ * Updated the version detection for IE
94
+ * Updated the version detection for OmniWeb
95
+ * Updated the version detection for iCab
96
+ * Updated the version detection for Safari
97
+ * Updated Safari to remove mobile devices (iPhone)
98
+ * Added detection for iPhone
99
+ * Added detection for robots
100
+ * Added detection for mobile devices
101
+ * Added detection for BlackBerry
102
+ * Removed Netscape checks (matches heavily with firefox & mozilla)
103
+ */
104
+
105
+
106
+ // Exit if accessed directly
107
+ if ( ! defined( 'ABSPATH' ) ) {
108
+ exit;
109
+ }
110
+
111
+ if ( ! class_exists( 'Browser' ) ) {
112
+
113
+ /**
114
+ * Browser detection class
115
+ *
116
+ * @author Chris Schuld
117
+ * @since 1.0
118
+ */
119
+ class Browser {
120
+ public $_agent = '';
121
+ public $_browser_name = '';
122
+ public $_version = '';
123
+ public $_platform = '';
124
+ public $_os = '';
125
+ public $_is_aol = false;
126
+ public $_is_mobile = false;
127
+ public $_is_robot = false;
128
+ public $_aol_version = '';
129
+
130
+ public $BROWSER_UNKNOWN = 'unknown';
131
+ public $VERSION_UNKNOWN = 'unknown';
132
+
133
+ public $BROWSER_OPERA = 'Opera'; // Http://www.opera.com/
134
+ public $BROWSER_OPERA_MINI = 'Opera Mini'; // Http://www.opera.com/mini/
135
+ public $BROWSER_WEBTV = 'WebTV'; // Http://www.webtv.net/pc/
136
+ public $BROWSER_IE = 'Internet Explorer'; // Http://www.microsoft.com/ie/
137
+ public $BROWSER_POCKET_IE = 'Pocket Internet Explorer'; // Http://en.wikipedia.org/wiki/Internet_Explorer_Mobile
138
+ public $BROWSER_KONQUEROR = 'Konqueror'; // Http://www.konqueror.org/
139
+ public $BROWSER_ICAB = 'iCab'; // Http://www.icab.de/
140
+ public $BROWSER_OMNIWEB = 'OmniWeb'; // Http://www.omnigroup.com/applications/omniweb/
141
+ public $BROWSER_FIREBIRD = 'Firebird'; // Http://www.ibphoenix.com/
142
+ public $BROWSER_FIREFOX = 'Firefox'; // Http://www.mozilla.com/en-US/firefox/firefox.html
143
+ public $BROWSER_ICEWEASEL = 'Iceweasel'; // Http://www.geticeweasel.org/
144
+ public $BROWSER_SHIRETOKO = 'Shiretoko'; // Http://wiki.mozilla.org/Projects/shiretoko
145
+ public $BROWSER_MOZILLA = 'Mozilla'; // Http://www.mozilla.com/en-US/
146
+ public $BROWSER_AMAYA = 'Amaya'; // Http://www.w3.org/Amaya/
147
+ public $BROWSER_LYNX = 'Lynx'; // Http://en.wikipedia.org/wiki/Lynx
148
+ public $BROWSER_SAFARI = 'Safari'; // Http://apple.com
149
+ public $BROWSER_IPHONE = 'iPhone'; // Http://apple.com
150
+ public $BROWSER_IPOD = 'iPod'; // Http://apple.com
151
+ public $BROWSER_IPAD = 'iPad'; // Http://apple.com
152
+ public $BROWSER_CHROME = 'Chrome'; // Http://www.google.com/chrome
153
+ public $BROWSER_ANDROID = 'Android'; // Http://www.android.com/
154
+ public $BROWSER_GOOGLEBOT = 'GoogleBot'; // Http://en.wikipedia.org/wiki/Googlebot
155
+ public $BROWSER_SLURP = 'Yahoo! Slurp'; // Http://en.wikipedia.org/wiki/Yahoo!_Slurp
156
+ public $BROWSER_W3CVALIDATOR = 'W3C Validator'; // Http://validator.w3.org/
157
+ public $BROWSER_BLACKBERRY = 'BlackBerry'; // Http://www.blackberry.com/
158
+ public $BROWSER_ICECAT = 'IceCat'; // Http://en.wikipedia.org/wiki/GNU_IceCat
159
+ public $BROWSER_NOKIA_S60 = 'Nokia S60 OSS Browser'; // Http://en.wikipedia.org/wiki/Web_Browser_for_S60
160
+ public $BROWSER_NOKIA = 'Nokia Browser'; // * all other WAP-based browsers on the Nokia Platform
161
+ public $BROWSER_MSN = 'MSN Browser'; // Http://explorer.msn.com/
162
+ public $BROWSER_MSNBOT = 'MSN Bot'; // Http://search.msn.com/msnbot.htm
163
+ // Http://en.wikipedia.org/wiki/Msnbot (used for Bing as well)
164
+ public $BROWSER_NETSCAPE_NAVIGATOR = 'Netscape Navigator'; // Http://browser.netscape.com/ (DEPRECATED)
165
+ public $BROWSER_GALEON = 'Galeon'; // Http://galeon.sourceforge.net/ (DEPRECATED)
166
+ public $BROWSER_NETPOSITIVE = 'NetPositive'; // Http://en.wikipedia.org/wiki/NetPositive (DEPRECATED)
167
+ public $BROWSER_PHOENIX = 'Phoenix'; // Http://en.wikipedia.org/wiki/History_of_Mozilla_Firefox (DEPRECATED)
168
+
169
+ public $PLATFORM_UNKNOWN = 'unknown';
170
+ public $PLATFORM_WINDOWS = 'Windows';
171
+ public $PLATFORM_WINDOWS_CE = 'Windows CE';
172
+ public $PLATFORM_APPLE = 'Apple';
173
+ public $PLATFORM_LINUX = 'Linux';
174
+ public $PLATFORM_OS2 = 'OS/2';
175
+ public $PLATFORM_BEOS = 'BeOS';
176
+ public $PLATFORM_IPHONE = 'iPhone';
177
+ public $PLATFORM_IPOD = 'iPod';
178
+ public $PLATFORM_IPAD = 'iPad';
179
+ public $PLATFORM_BLACKBERRY = 'BlackBerry';
180
+ public $PLATFORM_NOKIA = 'Nokia';
181
+ public $PLATFORM_FREEBSD = 'FreeBSD';
182
+ public $PLATFORM_OPENBSD = 'OpenBSD';
183
+ public $PLATFORM_NETBSD = 'NetBSD';
184
+ public $PLATFORM_SUNOS = 'SunOS';
185
+ public $PLATFORM_OPENSOLARIS = 'OpenSolaris';
186
+ public $PLATFORM_ANDROID = 'Android';
187
+
188
+ public $OPERATING_SYSTEM_UNKNOWN = 'unknown';
189
+
190
+ function __construct( $useragent = '' ) {
191
+ $this->reset();
192
+
193
+ if ( $useragent != '' ) {
194
+ $this->setUserAgent( $useragent );
195
+ } else {
196
+ $this->determine();
197
+ }
198
+ }
199
+
200
+ /**
201
+ * Reset all properties
202
+ */
203
+ function reset() {
204
+ $this->_agent = isset( $_SERVER['HTTP_USER_AGENT'] ) ? $_SERVER['HTTP_USER_AGENT'] : '';
205
+ $this->_browser_name = $this->BROWSER_UNKNOWN;
206
+ $this->_version = $this->VERSION_UNKNOWN;
207
+ $this->_platform = $this->PLATFORM_UNKNOWN;
208
+ $this->_os = $this->OPERATING_SYSTEM_UNKNOWN;
209
+ $this->_is_aol = false;
210
+ $this->_is_mobile = false;
211
+ $this->_is_robot = false;
212
+ $this->_aol_version = $this->VERSION_UNKNOWN;
213
+ }
214
+
215
+ /**
216
+ * Check to see if the specific browser is valid
217
+ *
218
+ * @param string $browserName
219
+ *
220
+ * @return True if the browser is the specified browser
221
+ */
222
+ function isBrowser( $browserName ) {
223
+ return ( 0 == strcasecmp( $this->_browser_name, trim( $browserName ) ) );
224
+ }
225
+
226
+ /**
227
+ * The name of the browser. All return types are from the class contants
228
+ *
229
+ * @return string Name of the browser
230
+ */
231
+ function getBrowser() {
232
+ return $this->_browser_name;
233
+ }
234
+
235
+ /**
236
+ * Set the name of the browser
237
+ *
238
+ * @param $browser The name of the Browser
239
+ */
240
+ function setBrowser( $browser ) {
241
+ return $this->_browser_name = $browser;
242
+ }
243
+
244
+ /**
245
+ * The name of the platform. All return types are from the class contants
246
+ *
247
+ * @return string Name of the browser
248
+ */
249
+ function getPlatform() {
250
+ return $this->_platform;
251
+ }
252
+
253
+ /**
254
+ * Set the name of the platform
255
+ *
256
+ * @param $platform The name of the Platform
257
+ */
258
+ function setPlatform( $platform ) {
259
+ return $this->_platform = $platform;
260
+ }
261
+
262
+ /**
263
+ * The version of the browser.
264
+ *
265
+ * @return string Version of the browser (will only contain alpha-numeric characters and a period)
266
+ */
267
+ function getVersion() {
268
+ return $this->_version;
269
+ }
270
+
271
+ /**
272
+ * Set the version of the browser
273
+ *
274
+ * @param $version The version of the Browser
275
+ */
276
+ function setVersion( $version ) {
277
+ $this->_version = preg_replace( '/[^0-9,.,a-z,A-Z-]/', '', $version );
278
+ }
279
+
280
+ /**
281
+ * The version of AOL.
282
+ *
283
+ * @return string Version of AOL (will only contain alpha-numeric characters and a period)
284
+ */
285
+ function getAolVersion() {
286
+ return $this->_aol_version;
287
+ }
288
+
289
+ /**
290
+ * Set the version of AOL
291
+ *
292
+ * @param $version The version of AOL
293
+ */
294
+ function setAolVersion( $version ) {
295
+ $this->_aol_version = preg_replace( '/[^0-9,.,a-z,A-Z]/', '', $version );
296
+ }
297
+
298
+ /**
299
+ * Is the browser from AOL?
300
+ *
301
+ * @return boolean True if the browser is from AOL otherwise false
302
+ */
303
+ function isAol() {
304
+ return $this->_is_aol;
305
+ }
306
+
307
+ /**
308
+ * Is the browser from a mobile device?
309
+ *
310
+ * @return boolean True if the browser is from a mobile device otherwise false
311
+ */
312
+ function isMobile() {
313
+ return $this->_is_mobile;
314
+ }
315
+
316
+ /**
317
+ * Is the browser from a robot (ex Slurp,GoogleBot)?
318
+ *
319
+ * @return boolean True if the browser is from a robot otherwise false
320
+ */
321
+ function isRobot() {
322
+ return $this->_is_robot;
323
+ }
324
+
325
+ /**
326
+ * Set the browser to be from AOL
327
+ *
328
+ * @param $isAol
329
+ */
330
+ function setAol( $isAol ) {
331
+ $this->_is_aol = $isAol;
332
+ }
333
+
334
+ /**
335
+ * Set the Browser to be mobile
336
+ *
337
+ * @param boolean $value is the browser a mobile brower or not
338
+ */
339
+ function setMobile( $value = true ) {
340
+ $this->_is_mobile = $value;
341
+ }
342
+
343
+ /**
344
+ * Set the Browser to be a robot
345
+ *
346
+ * @param boolean $value is the browser a robot or not
347
+ */
348
+ function setRobot( $value = true ) {
349
+ $this->_is_robot = $value;
350
+ }
351
+
352
+ /**
353
+ * Get the user agent value in use to determine the browser
354
+ *
355
+ * @return string The user agent from the HTTP header
356
+ */
357
+ function getUserAgent() {
358
+ return $this->_agent;
359
+ }
360
+
361
+ /**
362
+ * Set the user agent value (the construction will use the HTTP header value - this will overwrite it)
363
+ *
364
+ * @param $agent_string The value for the User Agent
365
+ */
366
+ function setUserAgent( $agent_string ) {
367
+ $this->reset();
368
+ $this->_agent = $agent_string;
369
+ $this->determine();
370
+ }
371
+
372
+ /**
373
+ * Used to determine if the browser is actually "chromeframe"
374
+ *
375
+ * @since 1.7
376
+ * @return boolean True if the browser is using chromeframe
377
+ */
378
+ function isChromeFrame() {
379
+ return ( strpos( $this->_agent, 'chromeframe' ) !== false );
380
+ }
381
+
382
+ /**
383
+ * Returns a formatted string with a summary of the details of the browser.
384
+ *
385
+ * @return string formatted string with a summary of the browser
386
+ */
387
+ function __toString() {
388
+ $text1 = $this->getUserAgent(); // Grabs the UA string
389
+ $UAline1 = substr( $text1, 0, 32 ); // The first line we print should only be the first 32 characters of the UA string
390
+ $text2 = $this->getUserAgent(); // Now we grab it again and save it to a string
391
+ $towrapUA = str_replace( $UAline1, '', $text2 ); // The rest of the printoff (other than first line) is equivalent
392
+ // to the whole string minus the part we printed off. IE
393
+ // User Agent: thefirst32charactersfromUAline1
394
+ // the rest of it is now stored in
395
+ // $text2 to be printed off
396
+ // But we need to add spaces before each line that is split other than line 1
397
+ $space = '';
398
+ for ( $i = 0; $i < 25; $i ++ ) {
399
+ $space .= ' ';
400
+ }
401
+
402
+ // Now we split the remaining string of UA ($text2) into lines that are prefixed by spaces for formatting
403
+ $wordwrapped = chunk_split( $towrapUA, 32, "\n $space" );
404
+
405
+ return "Platform: {$this->getPlatform()} \n" .
406
+ "Browser Name: {$this->getBrowser()} \n" .
407
+ "Browser Version: {$this->getVersion()} \n" .
408
+ "User Agent String: $UAline1 \n\t\t\t " .
409
+ "$wordwrapped";
410
+ }
411
+
412
+ /**
413
+ * Protected routine to calculate and determine what the browser is in use (including platform)
414
+ */
415
+ function determine() {
416
+ $this->checkPlatform();
417
+ $this->checkBrowsers();
418
+ $this->checkForAol();
419
+ }
420
+
421
+ /**
422
+ * Protected routine to determine the browser type
423
+ *
424
+ * @return boolean True if the browser was detected otherwise false
425
+ */
426
+ function checkBrowsers() {
427
+ return (
428
+ // Well-known, well-used
429
+ // Special Notes:
430
+ // (1) Opera must be checked before FireFox due to the odd
431
+ // user agents used in some older versions of Opera
432
+ // (2) WebTV is strapped onto Internet Explorer so we must
433
+ // check for WebTV before IE
434
+ // (3) (deprecated) Galeon is based on Firefox and needs to be
435
+ // tested before Firefox is tested
436
+ // (4) OmniWeb is based on Safari so OmniWeb check must occur
437
+ // before Safari
438
+ // (5) Netscape 9+ is based on Firefox so Netscape checks
439
+ // before FireFox are necessary
440
+ $this->checkBrowserWebTv() ||
441
+ $this->checkBrowserInternetExplorer() ||
442
+ $this->checkBrowserOpera() ||
443
+ $this->checkBrowserGaleon() ||
444
+ $this->checkBrowserNetscapeNavigator9Plus() ||
445
+ $this->checkBrowserFirefox() ||
446
+ $this->checkBrowserChrome() ||
447
+ $this->checkBrowserOmniWeb() ||
448
+
449
+ // Common mobile
450
+ $this->checkBrowserAndroid() ||
451
+ $this->checkBrowseriPad() ||
452
+ $this->checkBrowseriPod() ||
453
+ $this->checkBrowseriPhone() ||
454
+ $this->checkBrowserBlackBerry() ||
455
+ $this->checkBrowserNokia() ||
456
+
457
+ // Common bots
458
+ $this->checkBrowserGoogleBot() ||
459
+ $this->checkBrowserMSNBot() ||
460
+ $this->checkBrowserSlurp() ||
461
+
462
+ // WebKit base check (post mobile and others)
463
+ $this->checkBrowserSafari() ||
464
+
465
+ // Everyone else
466
+ $this->checkBrowserNetPositive() ||
467
+ $this->checkBrowserFirebird() ||
468
+ $this->checkBrowserKonqueror() ||
469
+ $this->checkBrowserIcab() ||
470
+ $this->checkBrowserPhoenix() ||
471
+ $this->checkBrowserAmaya() ||
472
+ $this->checkBrowserLynx() ||
473
+
474
+ $this->checkBrowserShiretoko() ||
475
+ $this->checkBrowserIceCat() ||
476
+ $this->checkBrowserW3CValidator() ||
477
+ $this->checkBrowserMozilla() /* Mozilla is such an open standard that you must check it last */
478
+ );
479
+ }
480
+
481
+ /**
482
+ * Determine if the user is using a BlackBerry (last updated 1.7)
483
+ *
484
+ * @return boolean True if the browser is the BlackBerry browser otherwise false
485
+ */
486
+ function checkBrowserBlackBerry() {
487
+ if ( stripos( $this->_agent, 'blackberry' ) !== false ) {
488
+ $aresult = explode( '/', stristr( $this->_agent, 'BlackBerry' ) );
489
+ $aversion = explode( ' ', $aresult[1] );
490
+ $this->setVersion( $aversion[0] );
491
+ $this->_browser_name = $this->BROWSER_BLACKBERRY;
492
+ $this->setMobile( true );
493
+
494
+ return true;
495
+ }
496
+
497
+ return false;
498
+ }
499
+
500
+ /**
501
+ * Determine if the user is using an AOL User Agent (last updated 1.7)
502
+ *
503
+ * @return boolean True if the browser is from AOL otherwise false
504
+ */
505
+ function checkForAol() {
506
+ $this->setAol( false );
507
+ $this->setAolVersion( $this->VERSION_UNKNOWN );
508
+
509
+ if ( stripos( $this->_agent, 'aol' ) !== false ) {
510
+ $aversion = explode( ' ', stristr( $this->_agent, 'AOL' ) );
511
+ $this->setAol( true );
512
+ $this->setAolVersion( preg_replace( '/[^0-9\.a-z]/i', '', $aversion[1] ) );
513
+
514
+ return true;
515
+ }
516
+
517
+ return false;
518
+ }
519
+
520
+ /**
521
+ * Determine if the browser is the GoogleBot or not (last updated 1.7)
522
+ *
523
+ * @return boolean True if the browser is the GoogletBot otherwise false
524
+ */
525
+ function checkBrowserGoogleBot() {
526
+ if ( stripos( $this->_agent, 'googlebot' ) !== false ) {
527
+ $aresult = explode( '/', stristr( $this->_agent, 'googlebot' ) );
528
+ $aversion = explode( ' ', $aresult[1] );
529
+ $this->setVersion( str_replace( ';', '', $aversion[0] ) );
530
+ $this->_browser_name = $this->BROWSER_GOOGLEBOT;
531
+ $this->setRobot( true );
532
+
533
+ return true;
534
+ }
535
+
536
+ return false;
537
+ }
538
+
539
+ /**
540
+ * Determine if the browser is the MSNBot or not (last updated 1.9)
541
+ *
542
+ * @return boolean True if the browser is the MSNBot otherwise false
543
+ */
544
+ function checkBrowserMSNBot() {
545
+ if ( stripos( $this->_agent, 'msnbot' ) !== false ) {
546
+ $aresult = explode( '/', stristr( $this->_agent, 'msnbot' ) );
547
+ $aversion = explode( ' ', $aresult[1] );
548
+ $this->setVersion( str_replace( ';', '', $aversion[0] ) );
549
+ $this->_browser_name = $this->BROWSER_MSNBOT;
550
+ $this->setRobot( true );
551
+
552
+ return true;
553
+ }
554
+
555
+ return false;
556
+ }
557
+
558
+ /**
559
+ * Determine if the browser is the W3C Validator or not (last updated 1.7)
560
+ *
561
+ * @return boolean True if the browser is the W3C Validator otherwise false
562
+ */
563
+ function checkBrowserW3CValidator() {
564
+ if ( stripos( $this->_agent, 'W3C-checklink' ) !== false ) {
565
+ $aresult = explode( '/', stristr( $this->_agent, 'W3C-checklink' ) );
566
+ $aversion = explode( ' ', $aresult[1] );
567
+ $this->setVersion( $aversion[0] );
568
+ $this->_browser_name = $this->BROWSER_W3CVALIDATOR;
569
+
570
+ return true;
571
+ } elseif ( stripos( $this->_agent, 'W3C_Validator' ) !== false ) {
572
+ // Some of the Validator versions do not delineate w/ a slash - add it back in
573
+ $ua = str_replace( 'W3C_Validator ', 'W3C_Validator/', $this->_agent );
574
+ $aresult = explode( '/', stristr( $ua, 'W3C_Validator' ) );
575
+ $aversion = explode( ' ', $aresult[1] );
576
+ $this->setVersion( $aversion[0] );
577
+ $this->_browser_name = $this->BROWSER_W3CVALIDATOR;
578
+
579
+ return true;
580
+ }
581
+
582
+ return false;
583
+ }
584
+
585
+ /**
586
+ * Determine if the browser is the Yahoo! Slurp Robot or not (last updated 1.7)
587
+ *
588
+ * @return boolean True if the browser is the Yahoo! Slurp Robot otherwise false
589
+ */
590
+ function checkBrowserSlurp() {
591
+ if ( stripos( $this->_agent, 'slurp' ) !== false ) {
592
+ $aresult = explode( '/', stristr( $this->_agent, 'Slurp' ) );
593
+ $aversion = explode( ' ', $aresult[1] );
594
+ $this->setVersion( $aversion[0] );
595
+ $this->_browser_name = $this->BROWSER_SLURP;
596
+ $this->setRobot( true );
597
+ $this->setMobile( false );
598
+
599
+ return true;
600
+ }
601
+
602
+ return false;
603
+ }
604
+
605
+ /**
606
+ * Determine if the browser is Internet Explorer or not (last updated 1.7)
607
+ *
608
+ * @return boolean True if the browser is Internet Explorer otherwise false
609
+ */
610
+ function checkBrowserInternetExplorer() {
611
+
612
+ // Test for v1 - v1.5 IE
613
+ if ( stripos( $this->_agent, 'microsoft internet explorer' ) !== false ) {
614
+ $this->setBrowser( $this->BROWSER_IE );
615
+ $this->setVersion( '1.0' );
616
+ $aresult = stristr( $this->_agent, '/' );
617
+ if ( preg_match( '/308|425|426|474|0b1/i', $aresult ) ) {
618
+ $this->setVersion( '1.5' );
619
+ }
620
+
621
+ return true;
622
+ // Test for versions > 1.5
623
+ } elseif ( stripos( $this->_agent, 'msie' ) !== false && stripos( $this->_agent, 'opera' ) === false ) {
624
+ // See if the browser is the odd MSN Explorer
625
+ if ( stripos( $this->_agent, 'msnb' ) !== false ) {
626
+ $aresult = explode( ' ', stristr( str_replace( ';', '; ', $this->_agent ), 'MSN' ) );
627
+ $this->setBrowser( $this->BROWSER_MSN );
628
+ $this->setVersion( str_replace( array( '(', ')', ';' ), '', $aresult[1] ) );
629
+
630
+ return true;
631
+ }
632
+ $aresult = explode( ' ', stristr( str_replace( ';', '; ', $this->_agent ), 'msie' ) );
633
+ $this->setBrowser( $this->BROWSER_IE );
634
+ $this->setVersion( str_replace( array( '(', ')', ';' ), '', $aresult[1] ) );
635
+
636
+ return true;
637
+ // Test for Pocket IE
638
+ } elseif ( stripos( $this->_agent, 'mspie' ) !== false || stripos( $this->_agent, 'pocket' ) !== false ) {
639
+ $aresult = explode( ' ', stristr( $this->_agent, 'mspie' ) );
640
+ $this->setPlatform( $this->PLATFORM_WINDOWS_CE );
641
+ $this->setBrowser( $this->BROWSER_POCKET_IE );
642
+ $this->setMobile( true );
643
+
644
+ if ( stripos( $this->_agent, 'mspie' ) !== false ) {
645
+ $this->setVersion( $aresult[1] );
646
+ } else {
647
+ $aversion = explode( '/', $this->_agent );
648
+ $this->setVersion( $aversion[1] );
649
+ }
650
+
651
+ return true;
652
+ }// End if().
653
+
654
+ return false;
655
+ }
656
+
657
+ /**
658
+ * Determine if the browser is Opera or not (last updated 1.7)
659
+ *
660
+ * @return boolean True if the browser is Opera otherwise false
661
+ */
662
+ function checkBrowserOpera() {
663
+ if ( stripos( $this->_agent, 'opera mini' ) !== false ) {
664
+ $resultant = stristr( $this->_agent, 'opera mini' );
665
+ if ( preg_match( '/\//', $resultant ) ) {
666
+ $aresult = explode( '/', $resultant );
667
+ $aversion = explode( ' ', $aresult[1] );
668
+ $this->setVersion( $aversion[0] );
669
+ } else {
670
+ $aversion = explode( ' ', stristr( $resultant, 'opera mini' ) );
671
+ $this->setVersion( $aversion[1] );
672
+ }
673
+ $this->_browser_name = $this->BROWSER_OPERA_MINI;
674
+ $this->setMobile( true );
675
+
676
+ return true;
677
+ } elseif ( stripos( $this->_agent, 'opera' ) !== false ) {
678
+ $resultant = stristr( $this->_agent, 'opera' );
679
+ if ( preg_match( '/Version\/(10.*)$/', $resultant, $matches ) ) {
680
+ $this->setVersion( $matches[1] );
681
+ } elseif ( preg_match( '/\//', $resultant ) ) {
682
+ $aresult = explode( '/', str_replace( '(', ' ', $resultant ) );
683
+ $aversion = explode( ' ', $aresult[1] );
684
+ $this->setVersion( $aversion[0] );
685
+ } else {
686
+ $aversion = explode( ' ', stristr( $resultant, 'opera' ) );
687
+ $this->setVersion( isset( $aversion[1] ) ? $aversion[1] : '' );
688
+ }
689
+ $this->_browser_name = $this->BROWSER_OPERA;
690
+
691
+ return true;
692
+ }
693
+
694
+ return false;
695
+ }
696
+
697
+ /**
698
+ * Determine if the browser is Chrome or not (last updated 1.7)
699
+ *
700
+ * @return boolean True if the browser is Chrome otherwise false
701
+ */
702
+ function checkBrowserChrome() {
703
+ if ( stripos( $this->_agent, 'Chrome' ) !== false ) {
704
+ $aresult = explode( '/', stristr( $this->_agent, 'Chrome' ) );
705
+ $aversion = explode( ' ', $aresult[1] );
706
+ $this->setVersion( $aversion[0] );
707
+ $this->setBrowser( $this->BROWSER_CHROME );
708
+
709
+ return true;
710
+ }
711
+
712
+ return false;
713
+ }
714
+
715
+
716
+ /**
717
+ * Determine if the browser is WebTv or not (last updated 1.7)
718
+ *
719
+ * @return boolean True if the browser is WebTv otherwise false
720
+ */
721
+ function checkBrowserWebTv() {
722
+ if ( stripos( $this->_agent, 'webtv' ) !== false ) {
723
+ $aresult = explode( '/', stristr( $this->_agent, 'webtv' ) );
724
+ $aversion = explode( ' ', $aresult[1] );
725
+ $this->setVersion( $aversion[0] );
726
+ $this->setBrowser( $this->BROWSER_WEBTV );
727
+
728
+ return true;
729
+ }
730
+
731
+ return false;
732
+ }
733
+
734
+ /**
735
+ * Determine if the browser is NetPositive or not (last updated 1.7)
736
+ *
737
+ * @return boolean True if the browser is NetPositive otherwise false
738
+ */
739
+ function checkBrowserNetPositive() {
740
+ if ( stripos( $this->_agent, 'NetPositive' ) !== false ) {
741
+ $aresult = explode( '/', stristr( $this->_agent, 'NetPositive' ) );
742
+ $aversion = explode( ' ', $aresult[1] );
743
+ $this->setVersion( str_replace( array( '(', ')', ';' ), '', $aversion[0] ) );
744
+ $this->setBrowser( $this->BROWSER_NETPOSITIVE );
745
+
746
+ return true;
747
+ }
748
+
749
+ return false;
750
+ }
751
+
752
+ /**
753
+ * Determine if the browser is Galeon or not (last updated 1.7)
754
+ *
755
+ * @return boolean True if the browser is Galeon otherwise false
756
+ */
757
+ function checkBrowserGaleon() {
758
+ if ( stripos( $this->_agent, 'galeon' ) !== false ) {
759
+ $aresult = explode( ' ', stristr( $this->_agent, 'galeon' ) );
760
+ $aversion = explode( '/', $aresult[0] );
761
+ $this->setVersion( $aversion[1] );
762
+ $this->setBrowser( $this->BROWSER_GALEON );
763
+
764
+ return true;
765
+ }
766
+
767
+ return false;
768
+ }
769
+
770
+ /**
771
+ * Determine if the browser is Konqueror or not (last updated 1.7)
772
+ *
773
+ * @return boolean True if the browser is Konqueror otherwise false
774
+ */
775
+ function checkBrowserKonqueror() {
776
+ if ( stripos( $this->_agent, 'Konqueror' ) !== false ) {
777
+ $aresult = explode( ' ', stristr( $this->_agent, 'Konqueror' ) );
778
+ $aversion = explode( '/', $aresult[0] );
779
+ $this->setVersion( $aversion[1] );
780
+ $this->setBrowser( $this->BROWSER_KONQUEROR );
781
+
782
+ return true;
783
+ }
784
+
785
+ return false;
786
+ }
787
+
788
+ /**
789
+ * Determine if the browser is iCab or not (last updated 1.7)
790
+ *
791
+ * @return boolean True if the browser is iCab otherwise false
792
+ */
793
+ function checkBrowserIcab() {
794
+ if ( stripos( $this->_agent, 'icab' ) !== false ) {
795
+ $aversion = explode( ' ', stristr( str_replace( '/', ' ', $this->_agent ), 'icab' ) );
796
+ $this->setVersion( $aversion[1] );
797
+ $this->setBrowser( $this->BROWSER_ICAB );
798
+
799
+ return true;
800
+ }
801
+
802
+ return false;
803
+ }
804
+
805
+ /**
806
+ * Determine if the browser is OmniWeb or not (last updated 1.7)
807
+ *
808
+ * @return boolean True if the browser is OmniWeb otherwise false
809
+ */
810
+ function checkBrowserOmniWeb() {
811
+ if ( stripos( $this->_agent, 'omniweb' ) !== false ) {
812
+ $aresult = explode( '/', stristr( $this->_agent, 'omniweb' ) );
813
+ $aversion = explode( ' ', isset( $aresult[1] ) ? $aresult[1] : '' );
814
+ $this->setVersion( $aversion[0] );
815
+ $this->setBrowser( $this->BROWSER_OMNIWEB );
816
+
817
+ return true;
818
+ }
819
+
820
+ return false;
821
+ }
822
+
823
+ /**
824
+ * Determine if the browser is Phoenix or not (last updated 1.7)
825
+ *
826
+ * @return boolean True if the browser is Phoenix otherwise false
827
+ */
828
+ function checkBrowserPhoenix() {
829
+ if ( stripos( $this->_agent, 'Phoenix' ) !== false ) {
830
+ $aversion = explode( '/', stristr( $this->_agent, 'Phoenix' ) );
831
+ $this->setVersion( $aversion[1] );
832
+ $this->setBrowser( $this->BROWSER_PHOENIX );
833
+
834
+ return true;
835
+ }
836
+
837
+ return false;
838
+ }
839
+
840
+ /**
841
+ * Determine if the browser is Firebird or not (last updated 1.7)
842
+ *
843
+ * @return boolean True if the browser is Firebird otherwise false
844
+ */
845
+ function checkBrowserFirebird() {
846
+ if ( stripos( $this->_agent, 'Firebird' ) !== false ) {
847
+ $aversion = explode( '/', stristr( $this->_agent, 'Firebird' ) );
848
+ $this->setVersion( $aversion[1] );
849
+ $this->setBrowser( $this->BROWSER_FIREBIRD );
850
+
851
+ return true;
852
+ }
853
+
854
+ return false;
855
+ }
856
+
857
+ /**
858
+ * Determine if the browser is Netscape Navigator 9+ or not (last updated 1.7)
859
+ * NOTE: (http://browser.netscape.com/ - Official support ended on March 1st, 2008)
860
+ *
861
+ * @return boolean True if the browser is Netscape Navigator 9+ otherwise false
862
+ */
863
+ function checkBrowserNetscapeNavigator9Plus() {
864
+ if ( stripos( $this->_agent, 'Firefox' ) !== false && preg_match( '/Navigator\/([^ ]*)/i', $this->_agent, $matches ) ) {
865
+ $this->setVersion( $matches[1] );
866
+ $this->setBrowser( $this->BROWSER_NETSCAPE_NAVIGATOR );
867
+
868
+ return true;
869
+ } elseif ( stripos( $this->_agent, 'Firefox' ) === false && preg_match( '/Netscape6?\/([^ ]*)/i', $this->_agent, $matches ) ) {
870
+ $this->setVersion( $matches[1] );
871
+ $this->setBrowser( $this->BROWSER_NETSCAPE_NAVIGATOR );
872
+
873
+ return true;
874
+ }
875
+
876
+ return false;
877
+ }
878
+
879
+ /**
880
+ * Determine if the browser is Shiretoko or not (https://wiki.mozilla.org/Projects/shiretoko) (last updated 1.7)
881
+ *
882
+ * @return boolean True if the browser is Shiretoko otherwise false
883
+ */
884
+ function checkBrowserShiretoko() {
885
+ if ( stripos( $this->_agent, 'Mozilla' ) !== false && preg_match( '/Shiretoko\/([^ ]*)/i', $this->_agent, $matches ) ) {
886
+ $this->setVersion( $matches[1] );
887
+ $this->setBrowser( $this->BROWSER_SHIRETOKO );
888
+
889
+ return true;
890
+ }
891
+
892
+ return false;
893
+ }
894
+
895
+ /**
896
+ * Determine if the browser is Ice Cat or not (http://en.wikipedia.org/wiki/GNU_IceCat) (last updated 1.7)
897
+ *
898
+ * @return boolean True if the browser is Ice Cat otherwise false
899
+ */
900
+ function checkBrowserIceCat() {
901
+ if ( stripos( $this->_agent, 'Mozilla' ) !== false && preg_match( '/IceCat\/([^ ]*)/i', $this->_agent, $matches ) ) {
902
+ $this->setVersion( $matches[1] );
903
+ $this->setBrowser( $this->BROWSER_ICECAT );
904
+
905
+ return true;
906
+ }
907
+
908
+ return false;
909
+ }
910
+
911
+ /**
912
+ * Determine if the browser is Nokia or not (last updated 1.7)
913
+ *
914
+ * @return boolean True if the browser is Nokia otherwise false
915
+ */
916
+ function checkBrowserNokia() {
917
+ if ( preg_match( '/Nokia([^\/]+)\/([^ SP]+)/i', $this->_agent, $matches ) ) {
918
+ $this->setVersion( $matches[2] );
919
+ if ( stripos( $this->_agent, 'Series60' ) !== false || strpos( $this->_agent, 'S60' ) !== false ) {
920
+ $this->setBrowser( $this->BROWSER_NOKIA_S60 );
921
+ } else {
922
+ $this->setBrowser( $this->BROWSER_NOKIA );
923
+ }
924
+ $this->setMobile( true );
925
+
926
+ return true;
927
+ }
928
+
929
+ return false;
930
+ }
931
+
932
+ /**
933
+ * Determine if the browser is Firefox or not (last updated 1.7)
934
+ *
935
+ * @return boolean True if the browser is Firefox otherwise false
936
+ */
937
+ function checkBrowserFirefox() {
938
+ if ( stripos( $this->_agent, 'safari' ) === false ) {
939
+ if ( preg_match( '/Firefox[\/ \(]([^ ;\)]+)/i', $this->_agent, $matches ) ) {
940
+ $this->setVersion( $matches[1] );
941
+ $this->setBrowser( $this->BROWSER_FIREFOX );
942
+
943
+ return true;
944
+ } elseif ( preg_match( '/Firefox$/i', $this->_agent, $matches ) ) {
945
+ $this->setVersion( '' );
946
+ $this->setBrowser( $this->BROWSER_FIREFOX );
947
+
948
+ return true;
949
+ }
950
+ }
951
+
952
+ return false;
953
+ }
954
+
955
+ /**
956
+ * Determine if the browser is Firefox or not (last updated 1.7)
957
+ *
958
+ * @return boolean True if the browser is Firefox otherwise false
959
+ */
960
+ function checkBrowserIceweasel() {
961
+ if ( stripos( $this->_agent, 'Iceweasel' ) !== false ) {
962
+ $aresult = explode( '/', stristr( $this->_agent, 'Iceweasel' ) );
963
+ $aversion = explode( ' ', $aresult[1] );
964
+ $this->setVersion( $aversion[0] );
965
+ $this->setBrowser( $this->BROWSER_ICEWEASEL );
966
+
967
+ return true;
968
+ }
969
+
970
+ return false;
971
+ }
972
+
973
+ /**
974
+ * Determine if the browser is Mozilla or not (last updated 1.7)
975
+ *
976
+ * @return boolean True if the browser is Mozilla otherwise false
977
+ */
978
+ function checkBrowserMozilla() {
979
+ if ( stripos( $this->_agent, 'mozilla' ) !== false && preg_match( '/rv:[0-9].[0-9][a-b]?/i', $this->_agent ) && stripos( $this->_agent, 'netscape' ) === false ) {
980
+ $aversion = explode( ' ', stristr( $this->_agent, 'rv:' ) );
981
+ preg_match( '/rv:[0-9].[0-9][a-b]?/i', $this->_agent, $aversion );
982
+ $this->setVersion( str_replace( 'rv:', '', $aversion[0] ) );
983
+ $this->setBrowser( $this->BROWSER_MOZILLA );
984
+
985
+ return true;
986
+ } elseif ( stripos( $this->_agent, 'mozilla' ) !== false && preg_match( '/rv:[0-9]\.[0-9]/i', $this->_agent ) && stripos( $this->_agent, 'netscape' ) === false ) {
987
+ $aversion = explode( '', stristr( $this->_agent, 'rv:' ) );
988
+ $this->setVersion( str_replace( 'rv:', '', $aversion[0] ) );
989
+ $this->setBrowser( $this->BROWSER_MOZILLA );
990
+
991
+ return true;
992
+ } elseif ( stripos( $this->_agent, 'mozilla' ) !== false && preg_match( '/mozilla\/([^ ]*)/i', $this->_agent, $matches ) && stripos( $this->_agent, 'netscape' ) === false ) {
993
+ $this->setVersion( $matches[1] );
994
+ $this->setBrowser( $this->BROWSER_MOZILLA );
995
+
996
+ return true;
997
+ }
998
+
999
+ return false;
1000
+ }
1001
+
1002
+ /**
1003
+ * Determine if the browser is Lynx or not (last updated 1.7)
1004
+ *
1005
+ * @return boolean True if the browser is Lynx otherwise false
1006
+ */
1007
+ function checkBrowserLynx() {
1008
+ if ( stripos( $this->_agent, 'lynx' ) !== false ) {
1009
+ $aresult = explode( '/', stristr( $this->_agent, 'Lynx' ) );
1010
+ $aversion = explode( ' ', ( isset( $aresult[1] ) ? $aresult[1] : '' ) );
1011
+ $this->setVersion( $aversion[0] );
1012
+ $this->setBrowser( $this->BROWSER_LYNX );
1013
+
1014
+ return true;
1015
+ }
1016
+
1017
+ return false;
1018
+ }
1019
+
1020
+ /**
1021
+ * Determine if the browser is Amaya or not (last updated 1.7)
1022
+ *
1023
+ * @return boolean True if the browser is Amaya otherwise false
1024
+ */
1025
+ function checkBrowserAmaya() {
1026
+ if ( stripos( $this->_agent, 'amaya' ) !== false ) {
1027
+ $aresult = explode( '/', stristr( $this->_agent, 'Amaya' ) );
1028
+ $aversion = explode( ' ', $aresult[1] );
1029
+ $this->setVersion( $aversion[0] );
1030
+ $this->setBrowser( $this->BROWSER_AMAYA );
1031
+
1032
+ return true;
1033
+ }
1034
+
1035
+ return false;
1036
+ }
1037
+
1038
+ /**
1039
+ * Determine if the browser is Safari or not (last updated 1.7)
1040
+ *
1041
+ * @return boolean True if the browser is Safari otherwise false
1042
+ */
1043
+ function checkBrowserSafari() {
1044
+ if ( stripos( $this->_agent, 'Safari' ) !== false && stripos( $this->_agent, 'iPhone' ) === false && stripos( $this->_agent, 'iPod' ) === false ) {
1045
+ $aresult = explode( '/', stristr( $this->_agent, 'Version' ) );
1046
+ if ( isset( $aresult[1] ) ) {
1047
+ $aversion = explode( ' ', $aresult[1] );
1048
+ $this->setVersion( $aversion[0] );
1049
+ } else {
1050
+ $this->setVersion( $this->VERSION_UNKNOWN );
1051
+ }
1052
+ $this->setBrowser( $this->BROWSER_SAFARI );
1053
+
1054
+ return true;
1055
+ }
1056
+
1057
+ return false;
1058
+ }
1059
+
1060
+ /**
1061
+ * Determine if the browser is iPhone or not (last updated 1.7)
1062
+ *
1063
+ * @return boolean True if the browser is iPhone otherwise false
1064
+ */
1065
+ function checkBrowseriPhone() {
1066
+ if ( stripos( $this->_agent, 'iPhone' ) !== false ) {
1067
+ $aresult = explode( '/', stristr( $this->_agent, 'Version' ) );
1068
+ if ( isset( $aresult[1] ) ) {
1069
+ $aversion = explode( ' ', $aresult[1] );
1070
+ $this->setVersion( $aversion[0] );
1071
+ } else {
1072
+ $this->setVersion( $this->VERSION_UNKNOWN );
1073
+ }
1074
+ $this->setMobile( true );
1075
+ $this->setBrowser( $this->BROWSER_IPHONE );
1076
+
1077
+ return true;
1078
+ }
1079
+
1080
+ return false;
1081
+ }
1082
+
1083
+ /**
1084
+ * Determine if the browser is iPod or not (last updated 1.7)
1085
+ *
1086
+ * @return boolean True if the browser is iPod otherwise false
1087
+ */
1088
+ function checkBrowseriPad() {
1089
+ if ( stripos( $this->_agent, 'iPad' ) !== false ) {
1090
+ $aresult = explode( '/', stristr( $this->_agent, 'Version' ) );
1091
+ if ( isset( $aresult[1] ) ) {
1092
+ $aversion = explode( ' ', $aresult[1] );
1093
+ $this->setVersion( $aversion[0] );
1094
+ } else {
1095
+ $this->setVersion( $this->VERSION_UNKNOWN );
1096
+ }
1097
+ $this->setMobile( true );
1098
+ $this->setBrowser( $this->BROWSER_IPAD );
1099
+
1100
+ return true;
1101
+ }
1102
+
1103
+ return false;
1104
+ }
1105
+
1106
+ /**
1107
+ * Determine if the browser is iPod or not (last updated 1.7)
1108
+ *
1109
+ * @return boolean True if the browser is iPod otherwise false
1110
+ */
1111
+ function checkBrowseriPod() {
1112
+ if ( stripos( $this->_agent, 'iPod' ) !== false ) {
1113
+ $aresult = explode( '/', stristr( $this->_agent, 'Version' ) );
1114
+ if ( isset( $aresult[1] ) ) {
1115
+ $aversion = explode( ' ', $aresult[1] );
1116
+ $this->setVersion( $aversion[0] );
1117
+ } else {
1118
+ $this->setVersion( $this->VERSION_UNKNOWN );
1119
+ }
1120
+ $this->setMobile( true );
1121
+ $this->setBrowser( $this->BROWSER_IPOD );
1122
+
1123
+ return true;
1124
+ }
1125
+
1126
+ return false;
1127
+ }
1128
+
1129
+ /**
1130
+ * Determine if the browser is Android or not (last updated 1.7)
1131
+ *
1132
+ * @return boolean True if the browser is Android otherwise false
1133
+ */
1134
+ function checkBrowserAndroid() {
1135
+ if ( stripos( $this->_agent, 'Android' ) !== false ) {
1136
+ $aresult = explode( ' ', stristr( $this->_agent, 'Android' ) );
1137
+ if ( isset( $aresult[1] ) ) {
1138
+ $aversion = explode( ' ', $aresult[1] );
1139
+ $this->setVersion( $aversion[0] );
1140
+ } else {
1141
+ $this->setVersion( $this->VERSION_UNKNOWN );
1142
+ }
1143
+ $this->setMobile( true );
1144
+ $this->setBrowser( $this->BROWSER_ANDROID );
1145
+
1146
+ return true;
1147
+ }
1148
+
1149
+ return false;
1150
+ }
1151
+
1152
+ /**
1153
+ * Determine the user's platform (last updated 1.7)
1154
+ */
1155
+ function checkPlatform() {
1156
+ if ( stripos( $this->_agent, 'windows' ) !== false ) {
1157
+ $this->_platform = $this->PLATFORM_WINDOWS;
1158
+ } elseif ( stripos( $this->_agent, 'iPad' ) !== false ) {
1159
+ $this->_platform = $this->PLATFORM_IPAD;
1160
+ } elseif ( stripos( $this->_agent, 'iPod' ) !== false ) {
1161
+ $this->_platform = $this->PLATFORM_IPOD;
1162
+ } elseif ( stripos( $this->_agent, 'iPhone' ) !== false ) {
1163
+ $this->_platform = $this->PLATFORM_IPHONE;
1164
+ } elseif ( stripos( $this->_agent, 'mac' ) !== false ) {
1165
+ $this->_platform = $this->PLATFORM_APPLE;
1166
+ } elseif ( stripos( $this->_agent, 'android' ) !== false ) {
1167
+ $this->_platform = $this->PLATFORM_ANDROID;
1168
+ } elseif ( stripos( $this->_agent, 'linux' ) !== false ) {
1169
+ $this->_platform = $this->PLATFORM_LINUX;
1170
+ } elseif ( stripos( $this->_agent, 'Nokia' ) !== false ) {
1171
+ $this->_platform = $this->PLATFORM_NOKIA;
1172
+ } elseif ( stripos( $this->_agent, 'BlackBerry' ) !== false ) {
1173
+ $this->_platform = $this->PLATFORM_BLACKBERRY;
1174
+ } elseif ( stripos( $this->_agent, 'FreeBSD' ) !== false ) {
1175
+ $this->_platform = $this->PLATFORM_FREEBSD;
1176
+ } elseif ( stripos( $this->_agent, 'OpenBSD' ) !== false ) {
1177
+ $this->_platform = $this->PLATFORM_OPENBSD;
1178
+ } elseif ( stripos( $this->_agent, 'NetBSD' ) !== false ) {
1179
+ $this->_platform = $this->PLATFORM_NETBSD;
1180
+ } elseif ( stripos( $this->_agent, 'OpenSolaris' ) !== false ) {
1181
+ $this->_platform = $this->PLATFORM_OPENSOLARIS;
1182
+ } elseif ( stripos( $this->_agent, 'SunOS' ) !== false ) {
1183
+ $this->_platform = $this->PLATFORM_SUNOS;
1184
+ } elseif ( stripos( $this->_agent, 'OS\/2' ) !== false ) {
1185
+ $this->_platform = $this->PLATFORM_OS2;
1186
+ } elseif ( stripos( $this->_agent, 'BeOS' ) !== false ) {
1187
+ $this->_platform = $this->PLATFORM_BEOS;
1188
+ } elseif ( stripos( $this->_agent, 'win' ) !== false ) {
1189
+ $this->_platform = $this->PLATFORM_WINDOWS;
1190
+ }
1191
+ }
1192
+ }
1193
+ }// End if().
includes/control-panel/includes/class-customizer-option.php CHANGED
@@ -1,33 +1,33 @@
1
- <?php
2
-
3
- /**
4
- * A class that extends WP_Customize_Setting so we can access
5
- * the protected updated method when importing options.
6
- *
7
- * @since 0.3
8
- */
9
-
10
- require_once ABSPATH . 'wp-includes/class-wp-customize-setting.php';
11
-
12
- if ( ! class_exists( 'JupiterX_Customizer_Option' ) ) {
13
-
14
- /**
15
- * Customizer option class.
16
- *
17
- * @since 1.9.0
18
- */
19
- final class JupiterX_Customizer_Option extends WP_Customize_Setting {
20
-
21
- /**
22
- * Import an option value for this setting.
23
- *
24
- * @since 0.3
25
- * @param mixed $value The option value.
26
- * @return void
27
- */
28
- public function import( $value )
29
- {
30
- $this->update( $value );
31
- }
32
- }
33
- }
1
+ <?php
2
+
3
+ /**
4
+ * A class that extends WP_Customize_Setting so we can access
5
+ * the protected updated method when importing options.
6
+ *
7
+ * @since 0.3
8
+ */
9
+
10
+ require_once ABSPATH . 'wp-includes/class-wp-customize-setting.php';
11
+
12
+ if ( ! class_exists( 'JupiterX_Customizer_Option' ) ) {
13
+
14
+ /**
15
+ * Customizer option class.
16
+ *
17
+ * @since 1.9.0
18
+ */
19
+ final class JupiterX_Customizer_Option extends WP_Customize_Setting {
20
+
21
+ /**
22
+ * Import an option value for this setting.
23
+ *
24
+ * @since 0.3
25
+ * @param mixed $value The option value.
26
+ * @return void
27
+ */
28
+ public function import( $value )
29
+ {
30
+ $this->update( $value );
31
+ }
32
+ }
33
+ }
includes/control-panel/includes/class-db-manager.php CHANGED
@@ -1,691 +1,691 @@
1
- <?php
2
- /**
3
- * Handles database for restoring or creating backups.
4
- *
5
- * @package JupiterX_Core\Control_Panel\Database_Manager
6
- *
7
- * @since 1.9.0
8
- */
9
-
10
- if ( ! class_exists( 'JupiterX_Control_Panel_Database_Manager' ) ) {
11
- /**
12
- * Database manager class.
13
- *
14
- * @since 1.9.0
15
- */
16
- class JupiterX_Control_Panel_Database_Manager {
17
-
18
- /**
19
- * @var object
20
- */
21
- public $errors;
22
-
23
- /**
24
- * @var string
25
- */
26
- private $basedir;
27
-
28
- /**
29
- * @var string
30
- */
31
- private $backup_dir;
32
-
33
- /**
34
- * @var string
35
- */
36
- private $dir_prefix;
37
-
38
- /**
39
- * @var string
40
- */
41
- private $baseurl;
42
-
43
- /**
44
- * @var string
45
- */
46
- private $backup_url;
47
-
48
- /**
49
- * @var object
50
- */
51
- private $jupiterx_filesystem;
52
-
53
- /*====================== MAIN SECTION ============================*/
54
- /**
55
- * The class constructor
56
- *
57
- * @param $dir_prefix
58
- */
59
- public function __construct( $dir_prefix = 'jupiterx_backups' ) {
60
- $this->errors = new WP_Error();
61
- $this->dir_prefix = $dir_prefix;
62
- $this->init();
63
- }
64
-
65
- /**
66
- * Initialize the FS
67
- *
68
- * @var boolean
69
- */
70
- private function init() {
71
-
72
- $wp_upload_dir = wp_upload_dir();
73
-
74
- if ( ! empty( $wp_upload_dir['error'] ) ) {
75
- $this->errors->add( 'unable_to_create_upload_directory', $wp_upload_dir['error'] );
76
-
77
- return false;
78
- }
79
-
80
- $this->basedir = $wp_upload_dir['basedir'];
81
-
82
- $this->baseurl = $wp_upload_dir['baseurl'];
83
-
84
- $this->jupiterx_filesystem = new JupiterX_Filesystem(
85
- [
86
- 'context' => $this->basedir,
87
- ]
88
- );
89
-
90
- if ( $this->jupiterx_filesystem->get_error_code() ) {
91
- $this->errors->add( $this->jupiterx_filesystem->get_error_code(), $this->jupiterx_filesystem->get_error_message() );
92
-
93
- return false;
94
- }
95
-
96
- $this->backup_dir = trailingslashit( $this->basedir ) . $this->dir_prefix;
97
-
98
- $this->backup_url = trailingslashit( $this->baseurl ) . $this->dir_prefix;
99
-
100
- // Create index html file
101
- if ( ! $this->jupiterx_filesystem->exists( trailingslashit( $this->backup_dir ) . 'index.html' ) ) {
102
- $this->jupiterx_filesystem->touch( trailingslashit( $this->backup_dir ) . 'index.html' );
103
- }
104
-
105
- if ( ! $this->jupiterx_filesystem->exists( trailingslashit( $this->backup_dir ) . '.htaccess' ) ) {
106
- $htaccess_content = '<IfModule mod_rewrite.c>' . "\n" .
107
- 'RewriteEngine On' . "\n" .
108
- 'RewriteCond %{REQUEST_FILENAME} ^.*(sql|zip)$' . "\n" .
109
- 'RewriteRule . - [R=403,L]' . "\n" .
110
- '</IfModule>' . "\n";
111
- $this->jupiterx_filesystem->put_contents( trailingslashit( $this->backup_dir ) . '.htaccess', $htaccess_content );
112
- }
113
-
114
- return true;
115
- }
116
-
117
- /**
118
- * Backup current site database
119
- *
120
- * @return boolean
121
- */
122
- public function backup_db() {
123
- // Do not execute if there was as error in initialization
124
- if ( is_wp_error( $this->errors ) && $this->errors->get_error_code() ) {
125
- return false;
126
- }
127
-
128
- $file_name = 'dump-' . current_time( 'timestamp' ) . '-' . md5( uniqid() );
129
-
130
- $dump_file_name = $file_name . '.sql';
131
- $dump_file_path = $this->get_backup_dir( $dump_file_name );
132
-
133
- $zip_file_name = $file_name . '.zip';
134
- $zip_file_path = $this->get_backup_dir( $zip_file_name );
135
-
136
- $dumped = $this->dump_db( $dump_file_path );
137
-
138
- if ( ! $dumped ) {
139
- $this->errors->add( 'can_not_create_backup_db_file', __( 'Can not create backup db file.', 'jupiterx-core' ) );
140
-
141
- return false;
142
- }
143
-
144
- if ( $this->jupiterx_filesystem->zip(
145
- [
146
- $dump_file_name => $dump_file_path,
147
- ], $zip_file_path
148
- ) ) {
149
- $this->jupiterx_filesystem->delete( $dump_file_path );
150
- }
151
-
152
- return true;
153
- }
154
-
155
- /**
156
- * Backup current site media records
157
- *
158
- * @return boolean
159
- */
160
- public function backup_media_records() {
161
- // Do not execute if there was as error in initialization
162
- if ( is_wp_error( $this->errors ) && $this->errors->get_error_code() ) {
163
- return false;
164
- }
165
-
166
- $file_name = 'media-' . current_time( 'timestamp' ) . '-' . md5( uniqid() );
167
-
168
- $dump_file_name = $file_name . '.sql';
169
- $dump_file_path = $this->get_backup_dir( $dump_file_name );
170
-
171
- $dumped = $this->dump_media_records( $dump_file_path );
172
-
173
- if ( ! $dumped ) {
174
- $this->errors->add( 'can_not_create_media_records_backup_db_file', __( 'Can not create media records backup file.', 'jupiterx-core' ) );
175
-
176
- return false;
177
- }
178
-
179
- return true;
180
- }
181
-
182
- /**
183
- * Restore current site database
184
- *
185
- * @return boolean
186
- */
187
- public function restore_latest_db() {
188
- // Do not execute if there was as error in initialization
189
- if ( is_wp_error( $this->errors ) && $this->errors->get_error_code() ) {
190
- return false;
191
- }
192
-
193
- require_once ABSPATH . 'wp-admin/includes/upgrade.php';
194
-
195
- global $wpdb;
196
-
197
- $wpdb->suppress_errors = false;
198
- $wpdb->show_errors = false;
199
-
200
- /* BEGIN: Get the list of backup files and sort them by created date */
201
- $list_of_backups = $this->list_of_backups();
202
-
203
- /* BEGIN: Get the lastest backup file by date */
204
- $latest_backup_file = end( $list_of_backups );
205
-
206
- $regExp = '/dump-(\d+)-(.*)\.(zip|sql)/';
207
-
208
- if ( preg_match( $regExp, $latest_backup_file['full_path'], $matches ) ) {
209
- if ( 'zip' == $matches[3] ) {
210
- $unzipfile = $this->jupiterx_filesystem->unzip( $latest_backup_file['full_path'], $this->get_backup_dir() );
211
-
212
- if ( ! $unzipfile ) {
213
- $this->errors->add( 'error_unzipping_backup_file', __( 'There was an error unzipping the backup file.', 'jupiterx-core' ) );
214
-
215
- return false;
216
- } else if ( is_wp_error( $unzipfile ) ) {
217
- $this->errors->add( $unzipfile->get_error_code(), $unzipfile->get_error_message() );
218
-
219
- return false;
220
- }
221
-
222
- $database_sql_file = $this->get_backup_dir( basename( $latest_backup_file['full_path'], '.zip' ) . '.sql' );
223
- } else {
224
- $database_sql_file = $latest_backup_file['full_path'];
225
- }
226
- } else {
227
- $this->errors->add( 'invalid_backup_file_type', __( 'Invalid backup file.', 'jupiterx-core' ) );
228
-
229
- return false;
230
- }
231
-
232
- /* Check if sql backup file exists and readable */
233
- if ( ! $this->jupiterx_filesystem->exists( $database_sql_file ) || ! $this->jupiterx_filesystem->is_readable( $database_sql_file ) ) {
234
- $this->errors->add( 'backup_file_is_not_exists_or_readable', __( 'The backup file is not exists or not readable.', 'jupiterx-core' ) );
235
-
236
- return false;
237
- }
238
-
239
- /* Define DB Name and error message */
240
- $database_name = DB_NAME;
241
-
242
- /* BEGIN: Create the Database */
243
- $sql = "CREATE DATABASE IF NOT EXISTS `$database_name`";
244
- $wpdb->query( $sql );
245
-
246
- if ( ! empty( $wpdb->last_error ) ) {
247
- $this->errors->add( 'wpdb_last_error', $wpdb->last_error );
248
-
249
- return false;
250
- }
251
-
252
- /* BEGIN: Retrieve All Tables from the Database */
253
- $tables = $this->get_tables();
254
-
255
- /* BEGIN: Drop All Tables from the Database */
256
- foreach ( $tables as $table ) {
257
- $wpdb->query( "DROP TABLE IF EXISTS `$database_name`.`$table`" );
258
- }
259
-
260
- $sql_query = '';
261
-
262
- $file_contents = explode( "\n", $this->jupiterx_filesystem->get_contents( $database_sql_file ) );
263
-
264
- if ( ! empty( $file_contents ) ) {
265
- foreach ( $file_contents as $line ) {
266
- // Skip it if it's a comment or empty line
267
- if ( empty( $line ) || $line === "\n" || substr( $line, 0, 2 ) == '--' ) {
268
- continue;
269
- }
270
-
271
- // Contcat the sql query string
272
- $sql_query .= $line . "\n";
273
-
274
- // If it has a semicolon at the end, it's the end of the query
275
- if ( substr( trim( $line ), -1, 1 ) == ';' ) {
276
- if ( strpos( $sql_query, 'CREATE TABLE' ) ) {
277
- // Run drop table query
278
- $drop_table_legth = strpos( $sql_query, ' (', 0 );
279
- $drop_table = substr( $sql_query, 0, $drop_table_legth );
280
- $drop_table_if_exists = str_replace( 'CREATE TABLE', 'DROP TABLE IF EXISTS `' . $database_name . '`.', $drop_table );
281
- $wpdb->query( $drop_table_if_exists );
282
-
283
- // Run create table query
284
- dbDelta( $sql_query );
285
- } else {
286
- // Run insert record query
287
- $wpdb->query( $sql_query );
288
- }
289
-
290
- $sql_query = '';
291
- }
292
- }
293
- }
294
-
295
- if ( ! empty( $wpdb->last_error ) ) {
296
- $this->errors->add( 'wpdb_last_error', $wpdb->last_error );
297
-
298
- return false;
299
- }
300
-
301
- // Delete others backup files.
302
- $list_of_backups = $this->list_of_backups();
303
-
304
- if ( ! empty( $list_of_backups ) && is_array( $list_of_backups ) ) {
305
- foreach ( $list_of_backups as $list_of_backup ) {
306
- $this->jupiterx_filesystem->delete( $list_of_backup['full_path'] );
307
- }
308
- }
309
-
310
- return true;
311
- }
312
-
313
- /**
314
- * restore current site media records
315
- *
316
- * @return boolean
317
- */
318
- public function restore_media_records() {
319
- // Do not execute if there was as error in initialization
320
- if ( is_wp_error( $this->errors ) && $this->errors->get_error_code() ) {
321
- return false;
322
- }
323
-
324
- global $wpdb;
325
-
326
- $wpdb->suppress_errors = false;
327
- $wpdb->show_errors = false;
328
-
329
- $list_of_backups = $this->list_of_backups( 'media', 'sql' );
330
-
331
- if ( empty( $list_of_backups ) ) {
332
- return true;
333
- }
334
-
335
- $latest_backup = end( $list_of_backups );
336
-
337
- if ( ! isset( $latest_backup['full_path'] ) || ! $this->jupiterx_filesystem->exists( $latest_backup['full_path'] ) || ! $this->jupiterx_filesystem->is_readable( $latest_backup['full_path'] ) ) {
338
- $this->errors->add( 'media_records_backup_not_exists_or_readable', __( 'Media records backup file is not exists or not readable', 'jupiterx-core' ) );
339
-
340
- return false;
341
- }
342
-
343
- $sql_query = '';
344
-
345
- $file_contents = explode( "\n", $this->jupiterx_filesystem->get_contents( $latest_backup['full_path'] ) );
346
-
347
- if ( ! empty( $file_contents ) ) {
348
- $max_id = ( $wpdb->get_var( "SELECT MAX(ID) as id FROM $wpdb->posts" ) + 1 );
349
-
350
- foreach ( $file_contents as $line ) {
351
- // Skip it if it's empty line
352
- if ( empty( $line ) || $line === "\n" ) {
353
- continue;
354
- }
355
-
356
- // If it has a semicolon at the end, it's the end of the query
357
- if ( substr( trim( $line ), -1, 1 ) == ';' ) {
358
- // Replace with new POST ID
359
- $sql_query = str_replace( [ 'increament_id', 'meta_id' ], [ $max_id, 'NULL' ], $line );
360
-
361
- // Run insert record query
362
- $wpdb->query( $sql_query );
363
- }
364
-
365
- if ( 0 === strpos( $line, '---END-QUERY---' ) ) {
366
- $max_id = ( $wpdb->get_var( "SELECT MAX(ID) as id FROM $wpdb->posts" ) + 1 );
367
- }
368
- }
369
- }
370
-
371
- if ( ! empty( $wpdb->last_error ) ) {
372
- $this->errors->add( 'wpdb_last_error', $wpdb->last_error );
373
-
374
- return false;
375
- }
376
-
377
- $this->jupiterx_filesystem->delete( $latest_backup['full_path'] );
378
-
379
- return true;
380
- }
381
-
382
-
383
- /**
384
- * Get current backups data stored
385
- *
386
- * @return array
387
- */
388
- public function is_restore_db() {
389
- /* BEGIN: Get the list of backup files and sort them by created date */
390
- $list_of_backups = $this->list_of_backups();
391
-
392
- $result = [
393
- 'list_of_backups' => $list_of_backups,
394
- 'latest_backup_file' => end( $list_of_backups ),
395
- ];
396
-
397
- return $result;
398
- }
399
-
400
- /*====================== HELPERS ============================*/
401
-
402
- /**
403
- * Get all errors
404
- *
405
- * @return object
406
- */
407
- public function get_errors() {
408
- return $this->errors;
409
- }
410
-
411
- /**
412
- * Get error code
413
- *
414
- * @return string
415
- */
416
- public function get_error_code() {
417
- return is_wp_error( $this->errors ) && $this->errors->get_error_code() ? $this->errors->get_error_code() : false;
418
- }
419
-
420
- /**
421
- * Get error message
422
- *
423
- * @return string
424
- */
425
- public function get_error_message() {
426
- return is_wp_error( $this->errors ) && $this->errors->get_error_code() ? $this->errors->get_error_message() : false;
427
- }
428
-
429
- /**
430
- * Get backup directory
431
- *
432
- * @param $append
433
- * @return string
434
- */
435
- public function get_backup_dir( $append = '' ) {
436
- if ( ! empty( $append ) ) {
437
- return trailingslashit( $this->backup_dir ) . ltrim( $append, '/' );
438
- } else {
439
- return $this->backup_dir;
440
- }
441
- }
442
-
443
- /**
444
- * Get backup url
445
- *
446
- * @param $append
447
- * @return mixed
448
- */
449
- public function get_backup_url( $append = '' ) {
450
- if ( ! empty( $append ) ) {
451
- return trailingslashit( $this->backup_url ) . ltrim( $append, '/' );
452
- } else {
453
- return $this->backup_url;
454
- }
455
- }
456
-
457
- /**
458
- * Get list of avalibale backups
459
- *
460
- * @param $prefix
461
- * @param $file_ext
462
- * @return array
463
- */
464
- public function list_of_backups( $prefix = 'dump', $file_ext = 'zip,sql' ) {
465
- $backup_list = [];
466
-
467
- $files = glob( $this->get_backup_dir( $prefix . '-*.{' . $file_ext . '}' ), GLOB_BRACE );
468
-
469
- if ( $files ) {
470
- ksort( $files );
471
- $file_exts = explode( ',', $file_ext );
472
- $regExp = '/' . $prefix . '-(\d+)-(.*)\.(' . implode( '|', $file_exts ) . ')/';
473
- foreach ( $files as $file ) {
474
- if ( preg_match( $regExp, $file, $matches ) ) {
475
- $backup_list[] = [
476
- 'full_path' => $this->get_backup_dir( $matches[0] ),
477
- 'full_url' => $this->get_backup_url( $matches[0] ),
478
- 'name' => $matches[0],
479
- 'ext' => $matches[3],
480
- 'created_date' => date( 'Y-m-d H:i:s', $matches[1] ),
481
- ];
482
- }
483
- }
484
- }
485
-
486
- return $backup_list;
487
- }
488
-
489
- /*====================== HELPERS ABOUT DUMPING ============================*/
490
-
491
- /**
492
- * Get database tables for current site
493
- *
494
- * @author Sofyan Sitorus <sofyan@artbees.net>
495
- */
496
- public function get_tables() {
497
- global $wpdb;
498
- $exclude_tables = [
499
- $wpdb->base_prefix . 'users',
500
- $wpdb->base_prefix . 'usermeta',
501
- $wpdb->prefix . 'woocommerce_sessions',
502
- $wpdb->prefix . 'woocommerce_attribute_taxonomies',
503
- ];
504
- $multi_site_tables = [
505
- $wpdb->base_prefix . 'blogs',
506
- $wpdb->base_prefix . 'blog_versions',
507
- $wpdb->base_prefix . 'signups',
508
- $wpdb->base_prefix . 'site',
509
- $wpdb->base_prefix . 'sitemeta',
510
- $wpdb->base_prefix . 'sitecategories',
511
- $wpdb->base_prefix . 'registration_log',
512
- ];
513
- $current_site_tables = [];
514
- $current_blog_id = get_current_blog_id();
515
- $tables = $wpdb->get_results( 'SHOW FULL TABLES', ARRAY_N );
516
- foreach ( $tables as $table ) {
517
- if ( isset( $table[1] ) && 'VIEW' == $table[1] ) {
518
- continue;
519
- }
520
-
521
- if ( in_array( $table[0], $exclude_tables, true ) ) {
522
- continue;
523
- }
524
-
525
- if ( is_multisite() ) {
526
- if ( in_array( $table[0], $multi_site_tables, true ) ) {
527
- continue;
528
- }
529
-
530
- if ( is_main_site( $current_blog_id ) ) {
531
- $regex = '/^' . $wpdb->prefix . '([0-9])+/i';
532
- if ( preg_match( $regex, $table[0] ) ) {
533
- continue;
534
- }
535
- }
536
-
537
- if ( 0 === strpos( $table[0], $wpdb->prefix ) ) {
538
- $current_site_tables[] = $table[0];
539
- }
540
- } else {
541
- $current_site_tables[] = $table[0];
542
- }
543
- }
544
-
545
- return $current_site_tables;
546
- }
547
-
548
- /**
549
- * Export current site data to local disk
550
- *
551
- * @param $dump_file_path
552
- * @return boolean
553
- */
554
- private function dump_db( $dump_file_path ) {
555
- global $wpdb;
556
-
557
- $is_success = $this->jupiterx_filesystem->put_contents( $dump_file_path, '', 0777 );
558
-
559
- if ( $is_success ) {
560
- /* BEGIN : Prevent saving backup plugin settings in the database dump */
561
- $options_backup = get_option( 'wp_db_backup_backups' );
562
- $settings_backup = get_option( 'wp_db_backup_options' );
563
- delete_option( 'wp_db_backup_backups' );
564
- delete_option( 'wp_db_backup_options' );
565
- /* END : Prevent saving backup plugin settings in the database dump */
566
-
567
- $tables_exclude = get_option( 'wp_db_exclude_table' );
568
- $tables = $this->get_tables();
569
-
570
- if ( $tables ) {
571
- $output = '';
572
-
573
- foreach ( $tables as $table ) {
574
- if ( empty( $tables_exclude ) || ( ! ( in_array( $table, $tables_exclude, true ) ) ) ) {
575
- // Create table SQL syntax
576
- $create_table = $wpdb->get_row( 'SHOW CREATE TABLE ' . $table, ARRAY_N );
577
- $output .= "\n\n" . $create_table[1] . ";\n\n";
578
-
579
- // Insert records SQL syntax
580
- $result = $wpdb->get_results( "SELECT * FROM {$table}", ARRAY_N );
581
- $result_count = count( $result );
582
-
583
- if ( $result ) {
584
- for ( $i = 0; $i < $result_count; $i++ ) {
585
- $row = $result[ $i ];
586
-
587
- $output .= 'INSERT INTO ' . $table . ' VALUES(';
588
-
589
- $row = array_map( [ $wpdb, '_real_escape' ], $row );
590
-
591
- $output .= '"' . implode( '","', $row ) . '"';
592
-
593
- $output .= ");\n";
594
- }
595
- }
596
-
597
- $output .= "\n";
598
- }
599
- }
600
-
601
- $this->jupiterx_filesystem->put_contents( $dump_file_path, $output );
602
-
603
- $wpdb->flush();
604
- }
605
-
606
- /* BEGIN : Prevent saving backup plugin settings in the database dump */
607
- add_option( 'wp_db_backup_backups', $options_backup );
608
- add_option( 'wp_db_backup_options', $settings_backup );
609
- /* END : Prevent saving backup plugin settings in the database dump */
610
-
611
- return $this->jupiterx_filesystem->chmod( $dump_file_path, 0664 );
612
- }
613
-
614
- return $is_success;
615
- }
616
-
617
- /**
618
- * Export current site media record to local disk
619
- *
620
- * @param $dump_file_path
621
- * @return boolean
622
- */
623
- private function dump_media_records( $dump_file_path ) {
624
- global $wpdb;
625
-
626
- $result = $wpdb->get_results( "SELECT * FROM $wpdb->posts where post_type='attachment'", ARRAY_N );
627
-
628
- $is_success = $this->jupiterx_filesystem->put_contents( $dump_file_path, '', 0777 );
629
-
630
- if ( $is_success ) {
631
- $result = $wpdb->get_results( "SELECT * FROM $wpdb->posts where post_type='attachment'", ARRAY_N );
632
-
633
- // Insert media records SQL syntax
634
- if ( $result ) {
635
- $result_count = count( $result );
636
-
637
- $output = '';
638
-
639
- for ( $i = 0; $i < $result_count; $i++ ) {
640
- $row = $result[ $i ];
641
-
642
- $output .= 'INSERT INTO ' . $wpdb->posts . ' VALUES(';
643
-
644
- $row[0] = 'increament_id';
645
-
646
- $row = array_map( [ $wpdb, '_real_escape' ], $row );
647
-
648
- $output .= '"' . implode( '","', $row ) . '"';
649
-
650
- $output .= ');' . "\n\n";
651
-
652
- $wpdb->flush();
653
-
654
- $postmeta_result = $wpdb->get_results( "SELECT * FROM $wpdb->postmeta where post_id = {$result[$i][0]}", ARRAY_N );
655
-
656
- // Insert media meta records SQL syntax
657
- if ( $postmeta_result ) {
658
- $postmeta_result_count = count( $postmeta_result );
659
-
660
- for ( $j = 0; $j < $postmeta_result_count; $j++ ) {
661
- $postmeta_row = $postmeta_result[ $j ];
662
-
663
- $output .= 'INSERT INTO ' . $wpdb->postmeta . ' VALUES(';
664
-
665
- $postmeta_row[0] = 'meta_id';
666
-
667
- $postmeta_row[1] = 'increament_id';
668
-
669
- $postmeta_row = array_map( [ $wpdb, '_real_escape' ], $postmeta_row );
670
-
671
- $output .= '"' . implode( '","', $postmeta_row ) . '"';
672
-
673
- $output .= ');' . "\n";
674
- }
675
-
676
- $output .= "\n\n" . '---END-QUERY---' . "\n\n";
677
- }
678
-
679
- $output .= "\n\n\n";
680
- }
681
-
682
- $this->jupiterx_filesystem->put_contents( $dump_file_path, $output );
683
- }
684
-
685
- return $this->jupiterx_filesystem->chmod( $dump_file_path, 0664 );
686
- }
687
-
688
- return $is_success;
689
- }
690
- }
691
- }
1
+ <?php
2
+ /**
3
+ * Handles database for restoring or creating backups.
4
+ *
5
+ * @package JupiterX_Core\Control_Panel\Database_Manager
6
+ *
7
+ * @since 1.9.0
8
+ */
9
+
10
+ if ( ! class_exists( 'JupiterX_Control_Panel_Database_Manager' ) ) {
11
+ /**
12
+ * Database manager class.
13
+ *
14
+ * @since 1.9.0
15
+ */
16
+ class JupiterX_Control_Panel_Database_Manager {
17
+
18
+ /**
19
+ * @var object
20
+ */
21
+ public $errors;
22
+
23
+ /**
24
+ * @var string
25
+ */
26
+ private $basedir;
27
+
28
+ /**
29
+ * @var string
30
+ */
31
+ private $backup_dir;
32
+
33
+ /**
34
+ * @var string
35
+ */
36
+ private $dir_prefix;
37
+
38
+ /**
39
+ * @var string
40
+ */
41
+ private $baseurl;
42
+
43
+ /**
44
+ * @var string
45
+ */
46
+ private $backup_url;
47
+
48
+ /**
49
+ * @var object
50
+ */
51
+ private $jupiterx_filesystem;
52
+
53
+ /*====================== MAIN SECTION ============================*/
54
+ /**
55
+ * The class constructor
56
+ *
57
+ * @param $dir_prefix
58
+ */
59
+ public function __construct( $dir_prefix = 'jupiterx_backups' ) {
60
+ $this->errors = new WP_Error();
61
+ $this->dir_prefix = $dir_prefix;
62
+ $this->init();
63
+ }
64
+
65
+ /**
66
+ * Initialize the FS
67
+ *
68
+ * @var boolean
69
+ */
70
+ private function init() {
71
+
72
+ $wp_upload_dir = wp_upload_dir();
73
+
74
+ if ( ! empty( $wp_upload_dir['error'] ) ) {
75
+ $this->errors->add( 'unable_to_create_upload_directory', $wp_upload_dir['error'] );
76
+
77
+ return false;
78
+ }
79
+
80
+ $this->basedir = $wp_upload_dir['basedir'];
81
+
82
+ $this->baseurl = $wp_upload_dir['baseurl'];
83
+
84
+ $this->jupiterx_filesystem = new JupiterX_Filesystem(
85
+ [
86
+ 'context' => $this->basedir,
87
+ ]
88
+ );
89
+
90
+ if ( $this->jupiterx_filesystem->get_error_code() ) {
91
+ $this->errors->add( $this->jupiterx_filesystem->get_error_code(), $this->jupiterx_filesystem->get_error_message() );
92
+
93
+ return false;
94
+ }
95
+
96
+ $this->backup_dir = trailingslashit( $this->basedir ) . $this->dir_prefix;
97
+
98
+ $this->backup_url = trailingslashit( $this->baseurl ) . $this->dir_prefix;
99
+
100
+ // Create index html file
101
+ if ( ! $this->jupiterx_filesystem->exists( trailingslashit( $this->backup_dir ) . 'index.html' ) ) {
102
+ $this->jupiterx_filesystem->touch( trailingslashit( $this->backup_dir ) . 'index.html' );
103
+ }
104
+
105
+ if ( ! $this->jupiterx_filesystem->exists( trailingslashit( $this->backup_dir ) . '.htaccess' ) ) {
106
+ $htaccess_content = '<IfModule mod_rewrite.c>' . "\n" .
107
+ 'RewriteEngine On' . "\n" .
108
+ 'RewriteCond %{REQUEST_FILENAME} ^.*(sql|zip)$' . "\n" .
109
+ 'RewriteRule . - [R=403,L]' . "\n" .
110
+ '</IfModule>' . "\n";
111
+ $this->jupiterx_filesystem->put_contents( trailingslashit( $this->backup_dir ) . '.htaccess', $htaccess_content );
112
+ }
113
+
114
+ return true;
115
+ }
116
+
117
+ /**
118
+ * Backup current site database
119
+ *
120
+ * @return boolean
121
+ */
122
+ public function backup_db() {
123
+ // Do not execute if there was as error in initialization
124
+ if ( is_wp_error( $this->errors ) && $this->errors->get_error_code() ) {
125
+ return false;
126
+ }
127
+
128
+ $file_name = 'dump-' . current_time( 'timestamp' ) . '-' . md5( uniqid() );
129
+
130
+ $dump_file_name = $file_name . '.sql';
131
+ $dump_file_path = $this->get_backup_dir( $dump_file_name );
132
+
133
+ $zip_file_name = $file_name . '.zip';
134
+ $zip_file_path = $this->get_backup_dir( $zip_file_name );
135
+
136
+ $dumped = $this->dump_db( $dump_file_path );
137
+
138
+ if ( ! $dumped ) {
139
+ $this->errors->add( 'can_not_create_backup_db_file', __( 'Can not create backup db file.', 'jupiterx-core' ) );
140
+
141
+ return false;
142
+ }
143
+
144
+ if ( $this->jupiterx_filesystem->zip(
145
+ [
146
+ $dump_file_name => $dump_file_path,
147
+ ], $zip_file_path
148
+ ) ) {
149
+ $this->jupiterx_filesystem->delete( $dump_file_path );
150
+ }
151
+
152
+ return true;
153
+ }
154
+
155
+ /**
156
+ * Backup current site media records
157
+ *
158
+ * @return boolean
159
+ */
160
+ public function backup_media_records() {
161
+ // Do not execute if there was as error in initialization
162
+ if ( is_wp_error( $this->errors ) && $this->errors->get_error_code() ) {
163
+ return false;
164
+ }
165
+
166
+ $file_name = 'media-' . current_time( 'timestamp' ) . '-' . md5( uniqid() );
167
+
168
+ $dump_file_name = $file_name . '.sql';
169
+ $dump_file_path = $this->get_backup_dir( $dump_file_name );
170
+
171
+ $dumped = $this->dump_media_records( $dump_file_path );
172
+
173
+ if ( ! $dumped ) {
174
+ $this->errors->add( 'can_not_create_media_records_backup_db_file', __( 'Can not create media records backup file.', 'jupiterx-core' ) );
175
+
176
+ return false;
177
+ }
178
+
179
+ return true;
180
+ }
181
+
182
+ /**
183
+ * Restore current site database
184
+ *
185
+ * @return boolean
186
+ */
187
+ public function restore_latest_db() {
188
+ // Do not execute if there was as error in initialization
189
+ if ( is_wp_error( $this->errors ) && $this->errors->get_error_code() ) {
190
+ return false;
191
+ }
192
+
193
+ require_once ABSPATH . 'wp-admin/includes/upgrade.php';
194
+
195
+ global $wpdb;
196
+
197
+ $wpdb->suppress_errors = false;
198
+ $wpdb->show_errors = false;
199
+
200
+ /* BEGIN: Get the list of backup files and sort them by created date */
201
+ $list_of_backups = $this->list_of_backups();
202
+
203
+ /* BEGIN: Get the lastest backup file by date */
204
+ $latest_backup_file = end( $list_of_backups );
205
+
206
+ $regExp = '/dump-(\d+)-(.*)\.(zip|sql)/';
207
+
208
+ if ( preg_match( $regExp, $latest_backup_file['full_path'], $matches ) ) {
209
+ if ( 'zip' == $matches[3] ) {
210
+ $unzipfile = $this->jupiterx_filesystem->unzip( $latest_backup_file['full_path'], $this->get_backup_dir() );
211
+
212
+ if ( ! $unzipfile ) {
213
+ $this->errors->add( 'error_unzipping_backup_file', __( 'There was an error unzipping the backup file.', 'jupiterx-core' ) );
214
+
215
+ return false;
216
+ } else if ( is_wp_error( $unzipfile ) ) {
217
+ $this->errors->add( $unzipfile->get_error_code(), $unzipfile->get_error_message() );
218
+
219
+ return false;
220
+ }
221
+
222
+ $database_sql_file = $this->get_backup_dir( basename( $latest_backup_file['full_path'], '.zip' ) . '.sql' );
223
+ } else {
224
+ $database_sql_file = $latest_backup_file['full_path'];
225
+ }
226
+ } else {
227
+ $this->errors->add( 'invalid_backup_file_type', __( 'Invalid backup file.', 'jupiterx-core' ) );
228
+
229
+ return false;
230
+ }
231
+
232
+ /* Check if sql backup file exists and readable */
233
+ if ( ! $this->jupiterx_filesystem->exists( $database_sql_file ) || ! $this->jupiterx_filesystem->is_readable( $database_sql_file ) ) {
234
+ $this->errors->add( 'backup_file_is_not_exists_or_readable', __( 'The backup file is not exists or not readable.', 'jupiterx-core' ) );
235
+
236
+ return false;
237
+ }
238
+
239
+ /* Define DB Name and error message */
240
+ $database_name = DB_NAME;
241
+
242
+ /* BEGIN: Create the Database */
243
+ $sql = "CREATE DATABASE IF NOT EXISTS `$database_name`";
244
+ $wpdb->query( $sql );
245
+
246
+ if ( ! empty( $wpdb->last_error ) ) {
247
+ $this->errors->add( 'wpdb_last_error', $wpdb->last_error );
248
+
249
+ return false;
250
+ }
251
+
252
+ /* BEGIN: Retrieve All Tables from the Database */
253
+ $tables = $this->get_tables();
254
+
255
+ /* BEGIN: Drop All Tables from the Database */
256
+ foreach ( $tables as $table ) {
257
+ $wpdb->query( "DROP TABLE IF EXISTS `$database_name`.`$table`" );
258
+ }
259
+
260
+ $sql_query = '';
261
+
262
+ $file_contents = explode( "\n", $this->jupiterx_filesystem->get_contents( $database_sql_file ) );
263
+
264
+ if ( ! empty( $file_contents ) ) {
265
+ foreach ( $file_contents as $line ) {
266
+ // Skip it if it's a comment or empty line
267
+ if ( empty( $line ) || $line === "\n" || substr( $line, 0, 2 ) == '--' ) {
268
+ continue;
269
+ }
270
+
271
+ // Contcat the sql query string
272
+ $sql_query .= $line . "\n";
273
+
274
+ // If it has a semicolon at the end, it's the end of the query
275
+ if ( substr( trim( $line ), -1, 1 ) == ';' ) {
276
+ if ( strpos( $sql_query, 'CREATE TABLE' ) ) {
277
+ // Run drop table query
278
+ $drop_table_legth = strpos( $sql_query, ' (', 0 );
279
+ $drop_table = substr( $sql_query, 0, $drop_table_legth );
280
+ $drop_table_if_exists = str_replace( 'CREATE TABLE', 'DROP TABLE IF EXISTS `' . $database_name . '`.', $drop_table );
281
+ $wpdb->query( $drop_table_if_exists );
282
+
283
+ // Run create table query
284
+ dbDelta( $sql_query );
285
+ } else {
286
+ // Run insert record query
287
+ $wpdb->query( $sql_query );
288
+ }
289
+
290
+ $sql_query = '';
291
+ }
292
+ }
293
+ }
294
+
295
+ if ( ! empty( $wpdb->last_error ) ) {
296
+ $this->errors->add( 'wpdb_last_error', $wpdb->last_error );
297
+
298
+ return false;
299
+ }
300
+
301
+ // Delete others backup files.
302
+ $list_of_backups = $this->list_of_backups();
303
+
304
+ if ( ! empty( $list_of_backups ) && is_array( $list_of_backups ) ) {
305
+ foreach ( $list_of_backups as $list_of_backup ) {
306
+ $this->jupiterx_filesystem->delete( $list_of_backup['full_path'] );
307
+ }
308
+ }
309
+
310
+ return true;
311
+ }
312
+
313
+ /**
314
+ * restore current site media records
315
+ *
316
+ * @return boolean
317
+ */
318
+ public function restore_media_records() {
319
+ // Do not execute if there was as error in initialization
320
+ if ( is_wp_error( $this->errors ) && $this->errors->get_error_code() ) {
321
+ return false;
322
+ }
323
+
324
+ global $wpdb;
325
+
326
+ $wpdb->suppress_errors = false;
327
+ $wpdb->show_errors = false;
328
+
329
+ $list_of_backups = $this->list_of_backups( 'media', 'sql' );
330
+
331
+ if ( empty( $list_of_backups ) ) {
332
+ return true;
333
+ }
334
+
335
+ $latest_backup = end( $list_of_backups );
336
+
337
+ if ( ! isset( $latest_backup['full_path'] ) || ! $this->jupiterx_filesystem->exists( $latest_backup['full_path'] ) || ! $this->jupiterx_filesystem->is_readable( $latest_backup['full_path'] ) ) {
338
+ $this->errors->add( 'media_records_backup_not_exists_or_readable', __( 'Media records backup file is not exists or not readable', 'jupiterx-core' ) );
339
+
340
+ return false;
341
+ }
342
+
343
+ $sql_query = '';
344
+
345
+ $file_contents = explode( "\n", $this->jupiterx_filesystem->get_contents( $latest_backup['full_path'] ) );
346
+
347
+ if ( ! empty( $file_contents ) ) {
348
+ $max_id = ( $wpdb->get_var( "SELECT MAX(ID) as id FROM $wpdb->posts" ) + 1 );
349
+
350
+ foreach ( $file_contents as $line ) {
351
+ // Skip it if it's empty line
352
+ if ( empty( $line ) || $line === "\n" ) {
353
+ continue;
354
+ }
355
+
356
+ // If it has a semicolon at the end, it's the end of the query
357
+ if ( substr( trim( $line ), -1, 1 ) == ';' ) {
358
+ // Replace with new POST ID
359
+ $sql_query = str_replace( [ 'increament_id', 'meta_id' ], [ $max_id, 'NULL' ], $line );
360
+
361
+ // Run insert record query
362
+ $wpdb->query( $sql_query );
363
+ }
364
+
365
+ if ( 0 === strpos( $line, '---END-QUERY---' ) ) {
366
+ $max_id = ( $wpdb->get_var( "SELECT MAX(ID) as id FROM $wpdb->posts" ) + 1 );
367
+ }
368
+ }
369
+ }
370
+
371
+ if ( ! empty( $wpdb->last_error ) ) {
372
+ $this->errors->add( 'wpdb_last_error', $wpdb->last_error );
373
+
374
+ return false;
375
+ }
376
+
377
+ $this->jupiterx_filesystem->delete( $latest_backup['full_path'] );
378
+
379
+ return true;
380
+ }
381
+
382
+
383
+ /**
384
+ * Get current backups data stored
385
+ *
386
+ * @return array
387
+ */
388
+ public function is_restore_db() {
389
+ /* BEGIN: Get the list of backup files and sort them by created date */
390
+ $list_of_backups = $this->list_of_backups();
391
+
392
+ $result = [
393
+ 'list_of_backups' => $list_of_backups,
394
+ 'latest_backup_file' => end( $list_of_backups ),
395
+ ];
396
+
397
+ return $result;
398
+ }
399
+
400
+ /*====================== HELPERS ============================*/
401
+
402
+ /**
403
+ * Get all errors
404
+ *
405
+ * @return object
406
+ */
407
+ public function get_errors() {
408
+ return $this->errors;
409
+ }
410
+
411
+ /**
412
+ * Get error code
413
+ *
414
+ * @return string
415
+ */
416
+ public function get_error_code() {
417
+ return is_wp_error( $this->errors ) && $this->errors->get_error_code() ? $this->errors->get_error_code() : false;
418
+ }
419
+
420
+ /**
421
+ * Get error message
422
+ *
423
+ * @return string
424
+ */
425
+ public function get_error_message() {
426
+ return is_wp_error( $this->errors ) && $this->errors->get_error_code() ? $this->errors->get_error_message() : false;
427
+ }
428
+
429
+ /**
430
+ * Get backup directory
431
+ *
432
+ * @param $append
433
+ * @return string
434
+ */
435
+ public function get_backup_dir( $append = '' ) {
436
+ if ( ! empty( $append ) ) {
437
+ return trailingslashit( $this->backup_dir ) . ltrim( $append, '/' );
438
+ } else {
439
+ return $this->backup_dir;
440
+ }
441
+ }
442
+
443
+ /**
444
+ * Get backup url
445
+ *
446
+ * @param $append
447
+ * @return mixed
448
+ */
449
+ public function get_backup_url( $append = '' ) {
450
+ if ( ! empty( $append ) ) {
451
+ return trailingslashit( $this->backup_url ) . ltrim( $append, '/' );
452
+ } else {
453
+ return $this->backup_url;
454
+ }
455
+ }
456
+
457
+ /**
458
+ * Get list of avalibale backups
459
+ *
460
+ * @param $prefix
461
+ * @param $file_ext
462
+ * @return array
463
+ */
464
+ public function list_of_backups( $prefix = 'dump', $file_ext = 'zip,sql' ) {
465
+ $backup_list = [];
466
+
467
+ $files = glob( $this->get_backup_dir( $prefix . '-*.{' . $file_ext . '}' ), GLOB_BRACE );
468
+
469
+ if ( $files ) {
470
+ ksort( $files );
471
+ $file_exts = explode( ',', $file_ext );
472
+ $regExp = '/' . $prefix . '-(\d+)-(.*)\.(' . implode( '|', $file_exts ) . ')/';
473
+ foreach ( $files as $file ) {
474
+ if ( preg_match( $regExp, $file, $matches ) ) {
475
+ $backup_list[] = [
476
+ 'full_path' => $this->get_backup_dir( $matches[0] ),
477
+ 'full_url' => $this->get_backup_url( $matches[0] ),
478
+ 'name' => $matches[0],
479
+ 'ext' => $matches[3],
480
+ 'created_date' => date( 'Y-m-d H:i:s', $matches[1] ),
481
+ ];
482
+ }
483
+ }
484
+ }
485
+
486
+ return $backup_list;
487
+ }
488
+
489
+ /*====================== HELPERS ABOUT DUMPING ============================*/
490
+
491
+ /**
492
+ * Get database tables for current site
493
+ *
494
+ * @author Sofyan Sitorus <sofyan@artbees.net>
495
+ */
496
+ public function get_tables() {
497
+ global $wpdb;
498
+ $exclude_tables = [
499
+ $wpdb->base_prefix . 'users',
500
+ $wpdb->base_prefix . 'usermeta',
501
+ $wpdb->prefix . 'woocommerce_sessions',
502
+ $wpdb->prefix . 'woocommerce_attribute_taxonomies',
503
+ ];
504
+ $multi_site_tables = [
505
+ $wpdb->base_prefix . 'blogs',
506
+ $wpdb->base_prefix . 'blog_versions',
507
+ $wpdb->base_prefix . 'signups',
508
+ $wpdb->base_prefix . 'site',
509
+ $wpdb->base_prefix . 'sitemeta',
510
+ $wpdb->base_prefix . 'sitecategories',
511
+ $wpdb->base_prefix . 'registration_log',
512
+ ];
513
+ $current_site_tables = [];
514
+ $current_blog_id = get_current_blog_id();
515
+ $tables = $wpdb->get_results( 'SHOW FULL TABLES', ARRAY_N );
516
+ foreach ( $tables as $table ) {
517
+ if ( isset( $table[1] ) && 'VIEW' == $table[1] ) {
518
+ continue;
519
+ }
520
+
521
+ if ( in_array( $table[0], $exclude_tables, true ) ) {
522
+ continue;
523
+ }
524
+
525
+ if ( is_multisite() ) {
526
+ if ( in_array( $table[0], $multi_site_tables, true ) ) {
527
+ continue;
528
+ }
529
+
530
+ if ( is_main_site( $current_blog_id ) ) {
531
+ $regex = '/^' . $wpdb->prefix . '([0-9])+/i';
532
+ if ( preg_match( $regex, $table[0] ) ) {
533
+ continue;
534
+ }
535
+ }
536
+
537
+ if ( 0 === strpos( $table[0], $wpdb->prefix ) ) {
538
+ $current_site_tables[] = $table[0];
539
+ }
540
+ } else {
541
+ $current_site_tables[] = $table[0];
542
+ }
543
+ }
544
+
545
+ return $current_site_tables;
546
+ }
547
+
548
+ /**
549
+ * Export current site data to local disk
550
+ *
551
+ * @param $dump_file_path
552
+ * @return boolean
553
+ */
554
+ private function dump_db( $dump_file_path ) {
555
+ global $wpdb;
556
+
557
+ $is_success = $this->jupiterx_filesystem->put_contents( $dump_file_path, '', 0777 );
558
+
559
+ if ( $is_success ) {
560
+ /* BEGIN : Prevent saving backup plugin settings in the database dump */
561
+ $options_backup = get_option( 'wp_db_backup_backups' );
562
+ $settings_backup = get_option( 'wp_db_backup_options' );
563
+ delete_option( 'wp_db_backup_backups' );
564
+ delete_option( 'wp_db_backup_options' );
565
+ /* END : Prevent saving backup plugin settings in the database dump */
566
+
567
+ $tables_exclude = get_option( 'wp_db_exclude_table' );
568
+ $tables = $this->get_tables();
569
+
570
+ if ( $tables ) {
571
+ $output = '';
572
+
573
+ foreach ( $tables as $table ) {
574
+ if ( empty( $tables_exclude ) || ( ! ( in_array( $table, $tables_exclude, true ) ) ) ) {
575
+ // Create table SQL syntax
576
+ $create_table = $wpdb->get_row( 'SHOW CREATE TABLE ' . $table, ARRAY_N );
577
+ $output .= "\n\n" . $create_table[1] . ";\n\n";
578
+
579
+ // Insert records SQL syntax
580
+ $result = $wpdb->get_results( "SELECT * FROM {$table}", ARRAY_N );
581
+ $result_count = count( $result );
582
+
583
+ if ( $result ) {
584
+ for ( $i = 0; $i < $result_count; $i++ ) {
585
+ $row = $result[ $i ];
586
+
587
+ $output .= 'INSERT INTO ' . $table . ' VALUES(';
588
+
589
+ $row = array_map( [ $wpdb, '_real_escape' ], $row );
590
+
591
+ $output .= '"' . implode( '","', $row ) . '"';
592
+
593
+ $output .= ");\n";
594
+ }
595
+ }
596
+
597
+ $output .= "\n";
598
+ }
599
+ }
600
+
601
+ $this->jupiterx_filesystem->put_contents( $dump_file_path, $output );
602
+
603
+ $wpdb->flush();
604
+ }
605
+
606
+ /* BEGIN : Prevent saving backup plugin settings in the database dump */
607
+ add_option( 'wp_db_backup_backups', $options_backup );
608
+ add_option( 'wp_db_backup_options', $settings_backup );
609
+ /* END : Prevent saving backup plugin settings in the database dump */
610
+
611
+ return $this->jupiterx_filesystem->chmod( $dump_file_path, 0664 );
612
+ }
613
+
614
+ return $is_success;
615
+ }
616
+
617
+ /**
618
+ * Export current site media record to local disk
619
+ *
620
+ * @param $dump_file_path
621
+ * @return boolean
622
+ */
623
+ private function dump_media_records( $dump_file_path ) {
624
+ global $wpdb;
625
+
626
+ $result = $wpdb->get_results( "SELECT * FROM $wpdb->posts where post_type='attachment'", ARRAY_N );
627
+
628
+ $is_success = $this->jupiterx_filesystem->put_contents( $dump_file_path, '', 0777 );
629
+
630
+ if ( $is_success ) {
631
+ $result = $wpdb->get_results( "SELECT * FROM $wpdb->posts where post_type='attachment'", ARRAY_N );
632
+
633
+ // Insert media records SQL syntax
634
+ if ( $result ) {
635
+ $result_count = count( $result );
636
+
637
+ $output = '';
638
+
639
+ for ( $i = 0; $i < $result_count; $i++ ) {
640
+ $row = $result[ $i ];
641
+
642
+ $output .= 'INSERT INTO ' . $wpdb->posts . ' VALUES(';
643
+
644
+ $row[0] = 'increament_id';
645
+
646
+ $row = array_map( [ $wpdb, '_real_escape' ], $row );
647
+
648
+ $output .= '"' . implode( '","', $row ) . '"';
649
+
650
+ $output .= ');' . "\n\n";
651
+
652
+ $wpdb->flush();
653
+
654
+ $postmeta_result = $wpdb->get_results( "SELECT * FROM $wpdb->postmeta where post_id = {$result[$i][0]}", ARRAY_N );
655
+
656
+ // Insert media meta records SQL syntax
657
+ if ( $postmeta_result ) {
658
+ $postmeta_result_count = count( $postmeta_result );
659
+
660
+ for ( $j = 0; $j < $postmeta_result_count; $j++ ) {
661
+ $postmeta_row = $postmeta_result[ $j ];
662
+
663
+ $output .= 'INSERT INTO ' . $wpdb->postmeta . ' VALUES(';
664
+
665
+ $postmeta_row[0] = 'meta_id';
666
+
667
+ $postmeta_row[1] = 'increament_id';
668
+
669
+ $postmeta_row = array_map( [ $wpdb, '_real_escape' ], $postmeta_row );
670
+
671
+ $output .= '"' . implode( '","', $postmeta_row ) . '"';
672
+
673
+ $output .= ');' . "\n";
674
+ }
675
+
676
+ $output .= "\n\n" . '---END-QUERY---' . "\n\n";
677
+ }
678
+
679
+ $output .= "\n\n\n";
680
+ }
681
+
682
+ $this->jupiterx_filesystem->put_contents( $dump_file_path, $output );
683
+ }
684
+
685
+ return $this->jupiterx_filesystem->chmod( $dump_file_path, 0664 );
686
+ }
687
+
688
+ return $is_success;
689
+ }
690
+ }
691
+ }
includes/control-panel/includes/class-db-php-manager.php ADDED
@@ -0,0 +1,250 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Handle database management using PHP.
4
+ *
5
+ * @package JupiterX_Core\Control_Panel\PHP_Database_Manager
6
+ *
7
+ * @since 1.11.0
8
+ */
9
+
10
+ /**
11
+ * PHP database manager.
12
+ *
13
+ * @since 1.11.0
14
+ */
15
+ class JupiterX_Control_Panel_PHP_DB_Manager {
16
+
17
+ /**
18
+ * Set DB max time execution.
19
+ */
20
+ const DB_MAX_TIME = 5000;
21
+
22
+ /**
23
+ * Custom table prefix.
24
+ */
25
+ const TABLE_PREFIX = 'jx_';
26
+
27
+ /**
28
+ * Construct class.
29
+ */
30
+ public function __constructor() {}
31
+
32
+ /**
33
+ * Dump database tables.
34
+ *
35
+ * @param string $file File path.
36
+ *
37
+ * @return boolean|string Dump success.
38
+ */
39
+ public function dump_tables( $file, $pick_tables = [] ) {
40
+ try {
41
+ if ( empty( $pick_tables ) ) {
42
+ return true;
43
+ }
44
+
45
+ global $wpdb;
46
+
47
+ // Set DB wait time out.
48
+ $wpdb->query( 'SET session wait_timeout = ' . self::DB_MAX_TIME );
49
+
50
+ // Attempt to create file.
51
+ if ( ( $handle = fopen( $file, 'w+' ) ) === false ) {
52
+ throw new Exception( esc_html__( 'Can\'t open: ', 'jupiterx-core' ) . $file );
53
+ }
54
+
55
+ // Get tables.
56
+ $tables = $wpdb->get_col( "SHOW FULL TABLES WHERE Table_Type != 'VIEW'" );
57
+ $pick_tables = count( $pick_tables ) ? $pick_tables : null;
58
+ $query_limit = 100;
59
+
60
+ // Filter tables to dump.
61
+ if ( is_array( $pick_tables ) ) {
62
+ foreach ( $tables as $key => $table ) {
63
+ if ( ! in_array( $tables[ $key ], $pick_tables ) ) {
64
+ unset( $tables[ $key ] );
65
+ }
66
+ }
67
+ }
68
+
69
+ // Add file headers.
70
+ $sql_header = "/* JUPITERX-TABLES (PHP DUMP) MYSQL SCRIPT CREATED ON : " . @date( "Y-m-d H:i:s" ) . " */\n\n";
71
+ $sql_header .= "SET FOREIGN_KEY_CHECKS = 0;\n\n";
72
+ fwrite( $handle, $sql_header );
73
+
74
+ // All tables must be created before inserts due to foreign key constraints.
75
+ foreach ( $tables as $table ) {
76
+ $count = 1;
77
+ $rewrite_table = str_replace( $this->get_table_prefix(), self::TABLE_PREFIX, $table, $count );
78
+
79
+ // Drop table.
80
+ $drop_table_query = 'DROP TABLE IF EXISTS `' . $rewrite_table . '`';
81
+ fwrite( $handle, "/* DROP TABLE: {$rewrite_table} */\n" );
82
+ fwrite( $handle, "{$drop_table_query};\n\n" );
83
+
84
+ // Create table.
85
+ $create = $wpdb->get_row( "SHOW CREATE TABLE `{$table}`", ARRAY_N );
86
+ $create_table_query = str_replace( 'CREATE TABLE `' . $table . '`', 'CREATE TABLE `' . $rewrite_table . '`', $create[1], $count );
87
+ $create_table_query = trim( preg_replace( '/\s+/', ' ', $create_table_query ) );
88
+ fwrite( $handle, "/* CREATE TABLE: {$rewrite_table} */\n" );
89
+ fwrite( $handle, "{$create_table_query};\n\n" );
90
+ }
91
+
92
+ // Create insert in 100 row increments to better handle memory.
93
+ foreach ( $tables as $table ) {
94
+ $count = 1;
95
+ $row_count = $wpdb->get_var( "SELECT Count(*) FROM `{$table}`" );
96
+ $rewrite_table = str_replace( $this->get_table_prefix(), self::TABLE_PREFIX, $table, $count );
97
+
98
+ if ( $row_count > $query_limit ) {
99
+ $row_count = ceil( $row_count / $query_limit );
100
+ } else if ( $row_count > 0 ) {
101
+ $row_count = 1;
102
+ }
103
+
104
+ if ( $row_count >= 1 ) {
105
+ fwrite( $handle, "/* INSERT TABLE DATA: {$rewrite_table} */\n" );
106
+ }
107
+
108
+ for ( $i = 0; $i < $row_count; $i++ ) {
109
+ $sql = '';
110
+ $limit = $i * $query_limit;
111
+ $query = "SELECT * FROM `{$table}` LIMIT {$limit}, {$query_limit}";
112
+ $rows = $wpdb->get_results( $query, ARRAY_A );
113
+
114
+ if ( '' !== $wpdb->last_error ) {
115
+ throw new Exception( esc_html__( 'Please contact your database administrator to fix the error. Error: ', 'jupiterx-core' ) . $wpdb->last_error );
116
+ return;
117
+ }
118
+
119
+ if ( is_array( $rows ) ) {
120
+ foreach ( $rows as $row ) {
121
+ $sql .= "INSERT INTO `{$rewrite_table}` VALUES(";
122
+ $num_values = count( $row );
123
+ $num_counter = 1;
124
+
125
+ foreach ( $row as $value ) {
126
+ if ( is_null( $value ) || ! isset( $value ) ) {
127
+ ( $num_values == $num_counter ) ? $sql .= 'NULL' : $sql .= 'NULL, ';
128
+ } else {
129
+ ( $num_values == $num_counter )
130
+ ? $sql .= '"' . $this->esc_sql( $value, true ) . '"'
131
+ : $sql .= '"' . $this->esc_sql( $value, true ) . '", ';
132
+ }
133
+ $num_counter++;
134
+ }
135
+ $sql .= ");\n";
136
+ }
137
+ fwrite( $handle, $sql );
138
+ }
139
+ }
140
+
141
+ $sql = null;
142
+ $rows = null;
143
+ fwrite( $handle, "\n" );
144
+ }
145
+
146
+ $sql_footer = "SET FOREIGN_KEY_CHECKS = 1;\n";
147
+ fwrite( $handle, $sql_footer );
148
+ $wpdb->flush();
149
+ fclose( $handle );
150
+ return true;
151
+ } catch ( Exception $e ) {
152
+ return $e->getMessage();
153
+ }
154
+ }
155
+
156
+ /**
157
+ * Import to database.
158
+ *
159
+ * @return boolean|string Import success.
160
+ */
161
+ public function import_tables( $file ) {
162
+ try {
163
+ if ( ! file_exists( $file ) ) {
164
+ throw new Exception( esc_html__( 'File not exist: ', 'jupiterx-core' ) . $file );
165
+ }
166
+
167
+ // Attempt to read file.
168
+ if ( ( $handle = fopen( $file, 'r' ) ) === false ) {
169
+ throw new Exception( esc_html__( 'Can\'t read: ', 'jupiterx-core' ) . $file );
170
+ }
171
+
172
+ global $wpdb;
173
+
174
+ while ( ! feof( $handle ) ) {
175
+ $line = fgets( $handle );
176
+
177
+ // Set allowed queries to run.
178
+ if ( preg_match( '/^\s*(?:SET.?|DROP TABLE.?|CREATE TABLE.?|INSERT INTO.?)\s+/is', $line ) ) {
179
+ $line = $this->replace_table_prefix( $line );
180
+
181
+ $wpdb->query( $line );
182
+ if ( '' !== $wpdb->last_error ) {
183
+ error_log( $wpdb->last_error );
184
+ }
185
+ }
186
+ }
187
+
188
+ $wpdb->flush();
189
+ fclose( $handle );
190
+ return true;
191
+ } catch ( Exception $e ) {
192
+ return $e->getMessage();
193
+ }
194
+ }
195
+
196
+ /**
197
+ * Get table prefix.
198
+ *
199
+ * @return string Table prefix.
200
+ */
201
+ public function get_table_prefix() {
202
+ global $wpdb;
203
+
204
+ return $wpdb->prefix;
205
+ }
206
+
207
+ /**
208
+ * Escape SQL.
209
+ *
210
+ * @see https://make.wordpress.org/core/2017/10/31/changed-behaviour-of-esc_sql-in-wordpress-4-8-3
211
+ *
212
+ * @param string $data Data.
213
+ * @param boolean $placeholder_escape Escape placeholder.
214
+ *
215
+ * @return string Escaped data.
216
+ */
217
+ private function esc_sql( $data, $placeholder_escape = false ) {
218
+ global $wpdb;
219
+
220
+ if ( $placeholder_escape ) {
221
+ return $wpdb->remove_placeholder_escape( @esc_sql( $data ) );
222
+ } else {
223
+ return @esc_sql( $data );
224
+ }
225
+ }
226
+
227
+ /**
228
+ * Replace SQL line table prefix.
229
+ *
230
+ * @param string $line File line.
231
+ *
232
+ * @return string Updated line.
233
+ */
234
+ private function replace_table_prefix( $line ) {
235
+ $count = 1;
236
+
237
+ if ( preg_match( '/CREATE TABLE `(.*?)`/', $line, $matches ) ) {
238
+ $rewrite_table = str_replace( self::TABLE_PREFIX, $this->get_table_prefix(), $matches[1], $count );
239
+ $line = str_replace( 'CREATE TABLE `' . $matches[1] . '`', 'CREATE TABLE `' . $rewrite_table . '`', $line, $count );
240
+ } elseif ( preg_match( '/INSERT INTO `(.*?)`/', $line, $matches ) ) {
241
+ $rewrite_table = str_replace( self::TABLE_PREFIX, $this->get_table_prefix(), $matches[1], $count );
242
+ $line = str_replace( 'INSERT INTO `' . $matches[1] . '`', 'INSERT INTO `' . $rewrite_table . '`', $line, $count );
243
+ } else if ( preg_match( '/DROP TABLE IF EXISTS `(.*?)`/', $line, $matches ) ) {
244
+ $rewrite_table = str_replace( self::TABLE_PREFIX, $this->get_table_prefix(), $matches[1], $count );
245
+ $line = str_replace( 'DROP TABLE IF EXISTS `' . $matches[1] . '`', 'DROP TABLE IF EXISTS `' . $rewrite_table . '`', $line, $count );
246
+ }
247
+
248
+ return $line;
249
+ }
250
+ }
includes/control-panel/includes/class-export-import-content.php CHANGED
@@ -175,6 +175,10 @@ if ( ! class_exists( 'JupiterX_Control_Panel_Export_Import' ) ) {
175
  $this->export_settings();
176
  break;
177
 
 
 
 
 
178
  case 'End':
179
  $this->export_end();
180
  break;
@@ -483,6 +487,44 @@ if ( ! class_exists( 'JupiterX_Control_Panel_Export_Import' ) ) {
483
  }
484
  }
485
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
486
  /**
487
  * Export customizer mods.
488
  *
@@ -1218,6 +1260,44 @@ if ( ! class_exists( 'JupiterX_Control_Panel_Export_Import' ) ) {
1218
 
1219
  return false;
1220
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1221
  }
1222
 
1223
  new JupiterX_Control_Panel_Export_Import();
175
  $this->export_settings();
176
  break;
177
 
178
+ case 'Custom Tables':
179
+ $this->export_custom_tables();
180
+ break;
181
+
182
  case 'End':
183
  $this->export_end();
184
  break;
487
  }
488
  }
489
 
490
+ /**
491
+ * Export custom tables.
492
+ *
493
+ * @since 1.11.0
494
+ */
495
+ private function export_custom_tables() {
496
+ try {
497
+ $db_manager = new JupiterX_Control_Panel_PHP_DB_Manager();
498
+
499
+ $supported_plugins = $this->get_supported_plugins();
500
+
501
+ $supported_tables = array_filter( $this->get_supported_tables(), function( $plugin ) use ( $supported_plugins ) {
502
+ return in_array( $plugin, $supported_plugins, true );
503
+ }, ARRAY_FILTER_USE_KEY );
504
+
505
+ $tables = [];
506
+
507
+ // Prepare table names.
508
+ foreach ( $supported_tables as $plugin_tables ) {
509
+ foreach ( $plugin_tables as $table ) {
510
+ array_push( $tables, $db_manager->get_table_prefix() . $table );
511
+ }
512
+ }
513
+
514
+ if ( ! empty( $tables ) ) {
515
+ $dump_tables = $db_manager->dump_tables( $this->folder['export_dir'] . '/tables.sql', $tables );
516
+
517
+ if ( $dump_tables !== true ) {
518
+ throw new Exception( $dump_tables );
519
+ }
520
+ }
521
+
522
+ return wp_send_json_success( [ 'step' => 'Custom Tables' ] );
523
+ } catch ( Exception $e ) {
524
+ return wp_send_json_error( $e->getMessage() );
525
+ }
526
+ }
527
+
528
  /**
529
  * Export customizer mods.
530
  *
1260
 
1261
  return false;
1262
  }
1263
+
1264
+ /**
1265
+ * Get supported plugins.
1266
+ *
1267
+ * @since 1.11.0
1268
+ *
1269
+ * @return array Supported plugins.
1270
+ */
1271
+ private function get_supported_plugins() {
1272
+ $active_plugins = get_option( 'active_plugins' );
1273
+
1274
+ if ( is_multisite() ) {
1275
+ $active_plugins = array_merge( $active_plugins, get_site_option( 'active_sitewide_plugins' ) );
1276
+ }
1277
+
1278
+ $plugins = [];
1279
+
1280
+ foreach ( $active_plugins as $plugin ) {
1281
+ $plugins[] = substr( $plugin, 0, strrpos( $plugin, '/' ) );
1282
+ }
1283
+
1284
+ $supported_plugins = array_intersect( $plugins, $this->supported_plugins );
1285
+
1286
+ return $supported_plugins;
1287
+ }
1288
+
1289
+ /**
1290
+ * Get supported tables to export.
1291
+ *
1292
+ * @since 1.11.0
1293
+ *
1294
+ * @return array Supported tables.
1295
+ */
1296
+ private function get_supported_tables() {
1297
+ return [
1298
+ 'jet-engine' => [ 'jet_post_types', 'jet_taxonomies' ],
1299
+ ];
1300
+ }
1301
  }
1302
 
1303
  new JupiterX_Control_Panel_Export_Import();
includes/control-panel/includes/class-filesystem.php CHANGED
@@ -1,613 +1,613 @@
1
- <?php
2
-
3
- /**
4
- * Class helper as wrapper of built-in WP wp_filesystem object
5
- *
6
- * @version 1.0.0
7
- * @author Sofyan Sitorus <sofyan@artbees.net>
8
- *
9
- * @since 5.7
10
- */
11
-
12
- require_once ABSPATH . 'wp-admin/includes/file.php';
13
- require_once ABSPATH . 'wp-admin/includes/template.php';
14
- require_once ABSPATH . 'wp-admin/includes/class-wp-filesystem-base.php';
15
-
16
-
17
- if ( ! class_exists( 'JupiterX_Filesystem' ) ) {
18
-
19
- /**
20
- * Filesystem class.
21
- *
22
- * @since 1.7.0
23
- */
24
- class JupiterX_Filesystem {
25
-
26
- /**
27
- * @var array
28
- */
29
- private $options = [];
30
-
31
- /**
32
- * @var array
33
- */
34
- public $errors = [];
35
-
36
- /**
37
- * @var null
38
- */
39
- public $wp_filesystem = null;
40
-
41
- /**
42
- * @var array
43
- */
44
- private $creds_data = [];
45
-
46
- /**
47
- * @var boolean
48
- */
49
- private $initialized = false;
50
-
51
- /**
52
- * Constructor
53
- *
54
- * @param (array) $args The arguments for the object options. Default: []
55
- * @param (boolean) $init Whether to initialise the object instantly. Default: false.
56
- * @param (boolean) $force Whether to force create new instance of $wp_filesystem object. Default: false.
57
- */
58
- public function __construct( $args = [], $init = false, $force = false ) {
59
- $this->errors = new WP_Error();
60
-
61
- $args = wp_parse_args(
62
- (array) $args, [
63
- 'form_post' => '', // (string) The URL to post the form to. Default: ''.
64
- 'type' => '', // (string) Chosen type of filesystem. Default: ''.
65
- 'error' => false, // (boolean) Whether the current request has failed to connect. Default: false.
66
- 'context' => '', // (string) Full path to the directory that is tested for being writable. Default: WP_CONTENT_DIR.
67
- 'extra_fields' => null, // (array) Extra POST fields in array key value pair format. Default: null.
68
- 'allow_relaxed_file_ownership' => false, // (boolean) Whether to allow Group/World writable. Default: false.
69
- 'override' => true, // (boolean) Whether to override some built-in function with custom function. Default: true.
70
- ]
71
- );
72
-
73
- foreach ( $args as $key => $value ) {
74
- $this->setOption( $key, $value );
75
- }
76
-
77
- if ( $init ) {
78
- $this->init( $force );
79
- }
80
- }
81
-
82
- /**
83
- * Initialize the wp_filesystem object
84
- *
85
- * @param (boolean) $force Whether to force create new instance of $wp_filesystem object. Default: false.
86
- * @return boolean
87
- */
88
- public function init( $force = false ) {
89
-
90
- global $wp_filesystem;
91
-
92
- $this->initialized = true;
93
-
94
- if ( ! $force && $wp_filesystem && $wp_filesystem instanceof WP_Filesystem_Base ) {
95
-
96
- $this->wp_filesystem = $wp_filesystem;
97
- return true;
98
-
99
- } else {
100
-
101
- $this->creds_data = request_filesystem_credentials(
102
- $this->getOption( 'form_post' ),
103
- $this->getOption( 'type' ),
104
- $this->getOption( 'error' ),
105
- $this->getOption( 'context' ),
106
- $this->getOption( 'extra_fields' ),
107
- $this->getOption( 'allow_relaxed_file_ownership' )
108
- );
109
-
110
- if ( ! WP_Filesystem( $this->creds_data, $this->getOption( 'context' ), $this->getOption( 'allow_relaxed_file_ownership' ) ) ) {
111
-
112
- if ( isset( $wp_filesystem->errors ) && is_wp_error( $wp_filesystem->errors ) && $wp_filesystem->errors->get_error_code() ) {
113
- $this->add_error( $wp_filesystem->errors->get_error_code(), $wp_filesystem->errors->get_error_message(), $wp_filesystem->errors->get_error_data() );
114
- } else {
115
- $this->add_error( 'unable_to_connect_to_filesystem', __( 'Unable to connect to the filesystem. Please confirm your credentials.', 'jupiterx-core' ), $this->creds_data );
116
- }
117
-
118
- return false;
119
- } else {
120
-
121
- $this->wp_filesystem = $wp_filesystem;
122
-
123
- return true;
124
- }
125
- }
126
- }
127
-
128
- /**
129
- * Magic method to call the wp_filesystem method
130
- *
131
- * @param string $method
132
- * @param array $args
133
- * @return mixed
134
- * @see wp-admin/includes/class-wp-filesystem-base.php for complete methods available
135
- */
136
- public function __call( $method, $args ) {
137
-
138
- // Try to initialize the wp_filesystem object
139
- if ( ! $this->initialized ) {
140
- $this->init();
141
- }
142
-
143
- // Stop execution if wp_filesystem objetc is empty
144
- if ( ! $this->wp_filesystem || ! $this->wp_filesystem instanceof WP_Filesystem_Base ) {
145
- return false;
146
- }
147
-
148
- // Do the magic, Abracadabra...!
149
- switch ( $method ) {
150
- case 'mkdir':
151
- case 'put_contents':
152
- case 'copy':
153
- case 'is_writable':
154
- if ( $this->getOption( 'override' ) ) {
155
- $result = call_user_func_array( [ $this, $method . '_override' ], $args );
156
- } else {
157
- $result = call_user_func_array( [ $this->wp_filesystem, $method ], $args );
158
- }
159
-
160
- break;
161
-
162
- case 'unzip':
163
- $result = call_user_func_array( [ $this, $method . '_custom' ], $args );
164
-
165
- break;
166
-
167
- default:
168
- if ( ! is_callable( [ $this->wp_filesystem, $method ] ) ) {
169
- $this->add_error( 'invalid_wp_filesystem_method_' . $method, __( 'Invalid method for $wp_filesystem object!', 'jupiterx-core' ) );
170
- $result = false;
171
- } else {
172
- $result = call_user_func_array( [ $this->wp_filesystem, $method ], $args );
173
- }
174
-
175
- break;
176
- }
177
-
178
- $this->setErrors();
179
-
180
- return $result;
181
- }
182
-
183
- /**
184
- * Create directory recursively
185
- *
186
- * @param (string) $path
187
- * @param (boolean) $chmod
188
- * @param (boolean) $chown
189
- * @param (boolean) $chgrp
190
- * @return boolean
191
- */
192
- private function mkdir_override( $path, $chmod = false, $chown = false, $chgrp = false ) {
193
- // Check if $path already exists
194
- if ( $this->is_dir( $path ) ) {
195
- return true;
196
- } else {
197
-
198
- // If file exists has same name with directory, delete it
199
- if ( $this->exists( $path ) ) {
200
- $this->delete( $path );
201
- }
202
-
203
- $path = str_replace( '\\', '/', $path );
204
-
205
- // Split path as folder chunks.
206
- $folders = explode( '/', trim( $path, '/' ) );
207
-
208
- $path_prefix = str_replace( implode( '/', $folders ), '', rtrim( $path, '/' ) );
209
-
210
- if ( $folders ) {
211
- $new_folders = [];
212
-
213
- // Check directories nested, create new if not exixts.
214
- foreach ( $folders as $folder ) {
215
- $new_folders[] = trim( $folder, '/' );
216
- $new_folder = $path_prefix . implode( '/', $new_folders );
217
-
218
- // Ignore folders outside open_basedir.
219
- if ( $this->is_open_basedir_restricted( $new_folder ) ) {
220
- continue;
221
- }
222
-
223
- // Skip if $new_folder already exists
224
- if ( $this->is_dir( $new_folder ) ) {
225
- continue;
226
- }
227
-
228
- // If file exists has same name with $new_folder, delete it
229
- if ( $this->exists( $new_folder ) ) {
230
- $this->delete( $new_folder );
231
- }
232
-
233
- // Create the $new_folder
234
- if ( ! $this->wp_filesystem->mkdir( $new_folder, $chmod, $chown, $chgrp ) ) {
235
- $this->add_error( 'can_not_create_directory', sprintf( __( 'Can\'t create directory %s', 'jupiterx-core' ), $new_folder ) );
236
- return false;
237
- }
238
- }
239
- }
240
- return true;
241
- }
242
- }
243
-
244
- /**
245
- * Write contents to a file
246
- *
247
- * @param (string) $file
248
- * @param (string) $contents
249
- * @param (boolean) $mode
250
- * @return boolean
251
- */
252
- private function put_contents_override( $file, $contents, $mode = false ) {
253
-
254
- if ( $this->is_dir( $file ) ) {
255
- $this->add_error( 'directory_exists_has_same_name', sprintf( __( 'A directory exists has same name %s', 'jupiterx-core' ), $new_folder ) );
256
- return false;
257
- }
258
-
259
- $path = dirname( $file );
260
-
261
- if ( ! $this->is_dir( $path ) ) {
262
- $this->mkdir( $path );
263
- }
264
-
265
- return $this->wp_filesystem->put_contents( $file, $contents, $mode );
266
- }
267
-
268
- /**
269
- * Copy file
270
- *
271
- * @param (string) $source
272
- * @param (string) $destination
273
- * @param (boolean) $overwrite
274
- * @param (boolean) $mode
275
- * @return boolean
276
- */
277
- private function copy_override( $source, $destination, $overwrite = true, $mode = false ) {
278
- if ( ! $overwrite && $this->exists( $destination ) ) {
279
- $this->add_error( 'file_already_exists', sprintf( __( 'File already exists %s', 'jupiterx-core' ), $new_folder ) );
280
- return false;
281
- }
282
-
283
- if ( ! $this->exists( $source ) ) {
284
- $this->add_error( 'copy_source_file_not_exists', sprintf( __( 'Copy source file not exists: %s', 'jupiterx-core' ), $source ) );
285
- return false;
286
- }
287
-
288
- if ( ! $this->is_file( $source ) ) {
289
- $this->add_error( 'copy_source_file_not_valid', sprintf( __( 'Copy source file not valid: %s', 'jupiterx-core' ), $source ) );
290
- return false;
291
- }
292
-
293
- if ( ! $this->is_readable( $source ) ) {
294
- $this->add_error( 'copy_source_file_not_readable', sprintf( __( 'Copy source file not readable: %s', 'jupiterx-core' ), $source ) );
295
- return false;
296
- }
297
-
298
- $content = $this->get_contents( $source );
299
-
300
- if ( false === $content ) {
301
- return false;
302
- }
303
-
304
- return $this->put_contents( $destination, $content, $mode );
305
- }
306
-
307
- /**
308
- * Check if file or directory is writable
309
- *
310
- * @param (string) $file
311
- * @return boolean
312
- */
313
- private function is_writable_override( $file ) {
314
-
315
- if ( $this->is_dir( $file ) ) {
316
- $temp_file = trailingslashit( $file ) . time() . '-' . uniqid() . '.tmp';
317
-
318
- $this->put_contents( $temp_file, '' );
319
-
320
- $is_writable = $this->wp_filesystem->is_writable( $temp_file );
321
-
322
- $this->delete( $temp_file );
323
-
324
- return $is_writable;
325
- } else {
326
- // Create the file if not exists
327
- if ( ! $this->exists( $file ) ) {
328
- $this->put_contents( $file, '' );
329
- }
330
- return $this->wp_filesystem->is_writable( $file );
331
- }
332
- }
333
-
334
- /**
335
- * Create zip file of a folder and paste to a destination.
336
- *
337
- * @since 1.0.3
338
- *
339
- * @param string $folder Target folder to zip.
340
- * @param string $destination Destination to save the zip.
341
- * @param string $enclose Enclose all files inside a folder name.
342
- *
343
- * @return boolean Zipping status.
344
- */
345
- public function zip_folder( $folder, $destination, $enclose ) {
346
- if ( ! $this->exists( $folder ) ) {
347
- return false;
348
- }
349
-
350
- $temp_file = tempnam( WP_CONTENT_DIR, 'zip' );
351
-
352
- $zip = new ZipArchive();
353
-
354
- $zip->open( $temp_file );
355
-
356
- $files = new RecursiveIteratorIterator( new RecursiveDirectoryIterator( $folder ) );
357
-
358
- foreach ( $files as $name => $file ) {
359
- if ( $file->isDir() ) {
360
- continue;
361
- }
362
-
363
- $file_path = $file->getRealPath();
364
-
365
- $relative_path = substr( $file_path, strlen( $folder ) + 1 );
366
-
367
- if ( ! empty( $enclose ) ) {
368
- $relative_path = $enclose . '/' . $relative_path;
369
- }
370
-
371
- $zip->addFile( $file_path, $relative_path );
372
- }
373
-
374
- $zip->close();
375
-
376
- // Copy the temp file to directory.
377
- $this->copy( $temp_file, $destination );
378
-
379
- // Delete temp file.
380
- $this->delete( $temp_file );
381
-
382
- // Check to make sure the file exists.
383
- return $this->exists( $destination );
384
- }
385
-
386
- /**
387
- * Extract zip file.
388
- *
389
- * @param [type] $source The source zip file.
390
- * @param [type] $destination The destination path.
391
- * @return [type] [description]
392
- */
393
- public function unzip_custom( $source, $destination ) {
394
- if ( ! $this->exists( $source ) ) {
395
- $this->add_error( 'zip_source_file_not_exists', sprintf( __( 'Zip source file not exists: %s', 'jupiterx-core' ), $source ) );
396
- return false;
397
- }
398
-
399
- if ( ! $this->is_file( $source ) ) {
400
- $this->add_error( 'zip_source_file_not_valid', sprintf( __( 'Zip source file not valid: %s', 'jupiterx-core' ), $source ) );
401
- return false;
402
- }
403
-
404
- if ( ! $this->is_readable( $source ) ) {
405
- $this->add_error( 'zip_source_file_not_readable', sprintf( __( 'Zip source file not readable: %s', 'jupiterx-core' ), $source ) );
406
- return false;
407
- }
408
-
409
- // Check $destination is valid
410
- if ( ! $this->is_dir( $destination ) ) {
411
-
412
- // If file exists has same name with $destination, delete it
413
- if ( $this->exists( $destination ) ) {
414
- $this->delete( $destination );
415
- }
416
-
417
- // Try create new $destination path
418
- if ( ! $this->mkdir( $destination ) ) {
419
- $this->add_error( 'fail_create_unzip_destination_directory', sprintf( __( 'Failed to create unzip destination directory: %s', 'jupiterx-core' ), $destination ) );
420
- return false;
421
- }
422
- }
423
-
424
- // Check $destination is writable
425
- if ( ! $this->is_writable( $destination ) ) {
426
- $this->add_error( 'unzip_destination_not_writable', sprintf( __( 'Unzip destination is not writable: %s', 'jupiterx-core' ), $destination ) );
427
- return false;
428
- }
429
-
430
- global $wp_filesystem;
431
-
432
- $wp_filesystem = $this->wp_filesystem;
433
-
434
- $unzip_file = unzip_file( $source, $destination );
435
-
436
- if ( is_wp_error( $unzip_file ) ) {
437
- $this->add_error( $unzip_file->get_error_code(), $unzip_file->get_error_message() );
438
- return false;
439
- } elseif ( ! $unzip_file ) {
440
- $this->add_error( 'failed_unzipping_file', sprintf( __( 'Failed unzipping file: %s', 'jupiterx-core' ), $source ) );
441
- return false;
442
- }
443
- return true;
444
- }
445
-
446
- /**
447
- * Set options data
448
- *
449
- * @param (string) $key
450
- * @param (string) $value
451
- * @return void
452
- */
453
- public function setOption( $key, $value ) {
454
- switch ( $key ) {
455
- case 'context':
456
- if ( ! empty( $value ) ) {
457
- $value = is_dir( $value ) ? $value : dirname( $value );
458
- $value = untrailingslashit( $value );
459
- }
460
- break;
461
- }
462
- $this->options[ $key ] = $value;
463
- }
464
-
465
- /**
466
- * Get options data
467
- *
468
- * @param (string) $key
469
- * @param (null|string) $default
470
- * @return mixed
471
- */
472
- public function getOption( $key = null, $default = null ) {
473
- if ( null === $key ) {
474
- return $this->options;
475
- } else {
476
- return isset( $this->options[ $key ] ) ? $this->options[ $key ] : $default;
477
- }
478
- }
479
-
480
- /**
481
- * Delete options data
482
- *
483
- * @param (string) $key
484
- * @return void
485
- */
486
- public function deleteOption( $key ) {
487
- unset( $this->options[ $key ] );
488
- }
489
-
490
- /**
491
- * Set errors data taken from the wp_filesystem errors
492
- *
493
- * @return void
494
- */
495
- private function setErrors() {
496
- if ( isset( $this->wp_filesystem->errors ) && is_wp_error( $this->wp_filesystem->errors ) && $this->wp_filesystem->errors->get_error_code() ) {
497
- $this->add_error( $this->wp_filesystem->errors->get_error_code(), $this->wp_filesystem->errors->get_error_message() );
498
- }
499
- }
500
-
501
- /**
502
- * Get current connection method
503
- *
504
- * @return mixed
505
- */
506
- public function getConnectionMethod() {
507
- return isset( $this->wp_filesystem->method ) ? $this->wp_filesystem->method : false;
508
- }
509
-
510
- /**
511
- * Get credentials data
512
- *
513
- * @return mixed
514
- */
515
- public function getCredsData() {
516
- return $this->creds_data;
517
- }
518
-
519
- /**
520
- * Get all errors
521
- *
522
- * @return object Will return the WP_Error object
523
- */
524
- public function get_errors() {
525
- return $this->errors;
526
- }
527
-
528
- /**
529
- * Retrieve all error codes
530
- *
531
- * @return array
532
- */
533
- public function get_error_codes() {
534
- return $this->errors->get_error_codes();
535
- }
536
-
537
- /**
538
- * Retrieve first error code available
539
- *
540
- * @return string, int or Empty if there is no error codes
541
- */
542
- public function get_error_code() {
543
- return $this->errors->get_error_code();
544
- }
545
-
546
- /**
547
- * Retrieve all error messages or error messages matching code
548
- *
549
- * @param (string) $code
550
- * @return array
551
- */
552
- public function get_error_messages( $code = '' ) {
553
- return $this->errors->get_error_messages( $code );
554
- }
555
-
556
- /**
557
- * Get the first error message available or error message matching code
558
- *
559
- * @param (string) $code
560
- * @return string
561
- */
562
- public function get_error_message( $code = '' ) {
563
- return $this->errors->get_error_message( $code );
564
- }
565
-
566
- /**
567
- * Append more error messages to list of error messages.
568
- *
569
- * @param (string) $code
570
- * @param (string) $message
571
- * @param (array) $data
572
- * @return void
573
- */
574
- public function add_error( $code, $message, $data = '' ) {
575
- // Log the error
576
- if ( defined( 'WP_DEBUG_LOG' ) && WP_DEBUG_LOG ) {
577
- error_log( 'JupiterX_Filesystem Error Code: ' . $code );
578
- error_log( 'JupiterX_Filesystem Error Message: ' . $message );
579
- if ( $data && ! is_resource( $data ) ) {
580
- error_log( 'JupiterX_Filesystem Error Data: ' . wp_json_encode( $data ) );
581
- }
582
- }
583
- $this->errors->add( $code, $message, $data );
584
- }
585
-
586
- /**
587
- * Check path is outside open_basedir paths.
588
- *
589
- * @param string $path
590
- * @return boolean
591
- */
592
- public function is_open_basedir_restricted( $path ) {
593
- $open_basedir_paths = ini_get('open_basedir');
594
-
595
- if ( empty( $open_basedir_paths ) ) {
596
- return false;
597
- }
598
-
599
- $open_basedir_paths = explode( PATH_SEPARATOR, $open_basedir_paths );
600
-
601
- foreach ( $open_basedir_paths as $open_basedir_path ) {
602
- if (
603
- strpos( $open_basedir_path, $path ) === 0 &&
604
- $open_basedir_path !== $path
605
- ) {
606
- return true;
607
- }
608
- }
609
-
610
- return false;
611
- }
612
- }
613
- }
1
+ <?php
2
+
3
+ /**
4
+ * Class helper as wrapper of built-in WP wp_filesystem object
5
+ *
6
+ * @version 1.0.0
7
+ * @author Sofyan Sitorus <sofyan@artbees.net>
8
+ *
9
+ * @since 5.7
10
+ */
11
+
12
+ require_once ABSPATH . 'wp-admin/includes/file.php';
13
+ require_once ABSPATH . 'wp-admin/includes/template.php';
14
+ require_once ABSPATH . 'wp-admin/includes/class-wp-filesystem-base.php';
15
+
16
+
17
+ if ( ! class_exists( 'JupiterX_Filesystem' ) ) {
18
+
19
+ /**
20
+ * Filesystem class.
21
+ *
22
+ * @since 1.7.0
23
+ */
24
+ class JupiterX_Filesystem {
25
+
26
+ /**
27
+ * @var array
28
+ */
29
+ private $options = [];
30
+
31
+ /**
32
+ * @var array
33
+ */
34
+ public $errors = [];
35
+
36
+ /**
37
+ * @var null
38
+ */
39
+ public $wp_filesystem = null;
40
+
41
+ /**
42
+ * @var array
43
+ */
44
+ private $creds_data = [];
45
+
46
+ /**
47
+ * @var boolean
48
+ */
49
+ private $initialized = false;
50
+
51
+ /**
52
+ * Constructor
53
+ *
54
+ * @param (array) $args The arguments for the object options. Default: []
55
+ * @param (boolean) $init Whether to initialise the object instantly. Default: false.
56
+ * @param (boolean) $force Whether to force create new instance of $wp_filesystem object. Default: false.
57
+ */
58
+ public function __construct( $args = [], $init = false, $force = false ) {
59
+ $this->errors = new WP_Error();
60
+
61
+ $args = wp_parse_args(
62
+ (array) $args, [
63
+ 'form_post' => '', // (string) The URL to post the form to. Default: ''.
64
+ 'type' => '', // (string) Chosen type of filesystem. Default: ''.
65
+ 'error' => false, // (boolean) Whether the current request has failed to connect. Default: false.
66
+ 'context' => '', // (string) Full path to the directory that is tested for being writable. Default: WP_CONTENT_DIR.
67
+ 'extra_fields' => null, // (array) Extra POST fields in array key value pair format. Default: null.
68
+ 'allow_relaxed_file_ownership' => false, // (boolean) Whether to allow Group/World writable. Default: false.
69
+ 'override' => true, // (boolean) Whether to override some built-in function with custom function. Default: true.
70
+ ]
71
+ );
72
+
73
+ foreach ( $args as $key => $value ) {
74
+ $this->setOption( $key, $value );
75
+ }
76
+
77
+ if ( $init ) {
78
+ $this->init( $force );
79
+ }
80
+ }
81
+
82
+ /**
83
+ * Initialize the wp_filesystem object
84
+ *
85
+ * @param (boolean) $force Whether to force create new instance of $wp_filesystem object. Default: false.
86
+ * @return boolean
87
+ */
88
+ public function init( $force = false ) {
89
+
90
+ global $wp_filesystem;
91
+
92
+ $this->initialized = true;
93
+
94
+ if ( ! $force && $wp_filesystem && $wp_filesystem instanceof WP_Filesystem_Base ) {
95
+
96
+ $this->wp_filesystem = $wp_filesystem;
97
+ return true;
98
+
99
+ } else {
100
+
101
+ $this->creds_data = request_filesystem_credentials(
102
+ $this->getOption( 'form_post' ),
103
+ $this->getOption( 'type' ),
104
+ $this->getOption( 'error' ),
105
+ $this->getOption( 'context' ),
106
+ $this->getOption( 'extra_fields' ),
107
+ $this->getOption( 'allow_relaxed_file_ownership' )
108
+ );
109
+
110
+ if ( ! WP_Filesystem( $this->creds_data, $this->getOption( 'context' ), $this->getOption( 'allow_relaxed_file_ownership' ) ) ) {
111
+
112
+ if ( isset( $wp_filesystem->errors ) && is_wp_error( $wp_filesystem->errors ) && $wp_filesystem->errors->get_error_code() ) {
113
+ $this->add_error( $wp_filesystem->errors->get_error_code(), $wp_filesystem->errors->get_error_message(), $wp_filesystem->errors->get_error_data() );
114
+ } else {
115
+ $this->add_error( 'unable_to_connect_to_filesystem', __( 'Unable to connect to the filesystem. Please confirm your credentials.', 'jupiterx-core' ), $this->creds_data );
116
+ }
117
+
118
+ return false;
119
+ } else {
120
+
121
+ $this->wp_filesystem = $wp_filesystem;
122
+
123
+ return true;
124
+ }
125
+ }
126
+ }
127
+
128
+ /**
129
+ * Magic method to call the wp_filesystem method
130
+ *
131
+ * @param string $method
132
+ * @param array $args
133
+ * @return mixed
134
+ * @see wp-admin/includes/class-wp-filesystem-base.php for complete methods available
135
+ */
136
+ public function __call( $method, $args ) {
137
+
138
+ // Try to initialize the wp_filesystem object
139
+ if ( ! $this->initialized ) {
140
+ $this->init();
141
+ }
142
+
143
+ // Stop execution if wp_filesystem objetc is empty
144
+ if ( ! $this->wp_filesystem || ! $this->wp_filesystem instanceof WP_Filesystem_Base ) {
145
+ return false;
146
+ }
147
+
148
+ // Do the magic, Abracadabra...!
149
+ switch ( $method ) {
150
+ case 'mkdir':
151
+ case 'put_contents':
152
+ case 'copy':
153
+ case 'is_writable':
154
+ if ( $this->getOption( 'override' ) ) {
155
+ $result = call_user_func_array( [ $this, $method . '_override' ], $args );
156
+ } else {
157
+ $result = call_user_func_array( [ $this->wp_filesystem, $method ], $args );
158
+ }
159
+
160
+ break;
161
+
162
+ case 'unzip':
163
+ $result = call_user_func_array( [ $this, $method . '_custom' ], $args );
164
+
165
+ break;
166
+
167
+ default:
168
+ if ( ! is_callable( [ $this->wp_filesystem, $method ] ) ) {
169
+ $this->add_error( 'invalid_wp_filesystem_method_' . $method, __( 'Invalid method for $wp_filesystem object!', 'jupiterx-core' ) );
170
+ $result = false;
171
+ } else {
172
+ $result = call_user_func_array( [ $this->wp_filesystem, $method ], $args );
173
+ }
174
+
175
+ break;
176
+ }
177
+
178
+ $this->setErrors();
179
+
180
+ return $result;
181
+ }
182
+
183
+ /**
184
+ * Create directory recursively
185
+ *
186
+ * @param (string) $path
187
+ * @param (boolean) $chmod
188
+ * @param (boolean) $chown
189
+ * @param (boolean) $chgrp
190
+ * @return boolean
191
+ */
192
+ private function mkdir_override( $path, $chmod = false, $chown = false, $chgrp = false ) {
193
+ // Check if $path already exists
194
+ if ( $this->is_dir( $path ) ) {
195
+ return true;
196
+ } else {
197
+
198
+ // If file exists has same name with directory, delete it
199
+ if ( $this->exists( $path ) ) {
200
+ $this->delete( $path );
201
+ }
202
+
203
+ $path = str_replace( '\\', '/', $path );
204
+
205
+ // Split path as folder chunks.
206
+ $folders = explode( '/', trim( $path, '/' ) );
207
+
208
+ $path_prefix = str_replace( implode( '/', $folders ), '', rtrim( $path, '/' ) );
209
+
210
+ if ( $folders ) {
211
+ $new_folders = [];
212
+
213
+ // Check directories nested, create new if not exixts.
214
+ foreach ( $folders as $folder ) {
215
+ $new_folders[] = trim( $folder, '/' );
216
+ $new_folder = $path_prefix . implode( '/', $new_folders );
217
+
218
+ // Ignore folders outside open_basedir.
219
+ if ( $this->is_open_basedir_restricted( $new_folder ) ) {
220
+ continue;
221
+ }
222
+
223
+ // Skip if $new_folder already exists
224
+ if ( $this->is_dir( $new_folder ) ) {
225
+ continue;
226
+ }
227
+
228
+ // If file exists has same name with $new_folder, delete it
229
+ if ( $this->exists( $new_folder ) ) {
230
+ $this->delete( $new_folder );
231
+ }
232
+
233
+ // Create the $new_folder
234
+ if ( ! $this->wp_filesystem->mkdir( $new_folder, $chmod, $chown, $chgrp ) ) {
235
+ $this->add_error( 'can_not_create_directory', sprintf( __( 'Can\'t create directory %s', 'jupiterx-core' ), $new_folder ) );
236
+ return false;
237
+ }
238
+ }
239
+ }
240
+ return true;
241
+ }
242
+ }
243
+
244
+ /**
245
+ * Write contents to a file
246
+ *
247
+ * @param (string) $file
248
+ * @param (string) $contents
249
+ * @param (boolean) $mode
250
+ * @return boolean
251
+ */
252
+ private function put_contents_override( $file, $contents, $mode = false ) {
253
+
254
+ if ( $this->is_dir( $file ) ) {
255
+ $this->add_error( 'directory_exists_has_same_name', sprintf( __( 'A directory exists has same name %s', 'jupiterx-core' ), $new_folder ) );
256
+ return false;
257
+ }
258
+
259
+ $path = dirname( $file );
260
+
261
+ if ( ! $this->is_dir( $path ) ) {
262
+ $this->mkdir( $path );
263
+ }
264
+
265
+ return $this->wp_filesystem->put_contents( $file, $contents, $mode );
266
+ }
267
+
268
+ /**
269
+ * Copy file
270
+ *
271
+ * @param (string) $source
272
+ * @param (string) $destination
273
+ * @param (boolean) $overwrite
274
+ * @param (boolean) $mode
275
+ * @return boolean
276
+ */
277
+ private function copy_override( $source, $destination, $overwrite = true, $mode = false ) {
278
+ if ( ! $overwrite && $this->exists( $destination ) ) {
279
+ $this->add_error( 'file_already_exists', sprintf( __( 'File already exists %s', 'jupiterx-core' ), $new_folder ) );
280
+ return false;
281
+ }
282
+
283
+ if ( ! $this->exists( $source ) ) {
284
+ $this->add_error( 'copy_source_file_not_exists', sprintf( __( 'Copy source file not exists: %s', 'jupiterx-core' ), $source ) );
285
+ return false;
286
+ }
287
+
288
+ if ( ! $this->is_file( $source ) ) {
289
+ $this->add_error( 'copy_source_file_not_valid', sprintf( __( 'Copy source file not valid: %s', 'jupiterx-core' ), $source ) );
290
+ return false;
291
+ }
292
+
293
+ if ( ! $this->is_readable( $source ) ) {
294
+ $this->add_error( 'copy_source_file_not_readable', sprintf( __( 'Copy source file not readable: %s', 'jupiterx-core' ), $source ) );
295
+ return false;
296
+ }
297
+
298
+ $content = $this->get_contents( $source );
299
+
300
+ if ( false === $content ) {
301
+ return false;
302
+ }
303
+
304
+ return $this->put_contents( $destination, $content, $mode );
305
+ }
306
+
307
+ /**
308
+ * Check if file or directory is writable
309
+ *
310
+ * @param (string) $file
311
+ * @return boolean
312
+ */
313
+ private function is_writable_override( $file ) {
314
+
315
+ if ( $this->is_dir( $file ) ) {
316
+ $temp_file = trailingslashit( $file ) . time() . '-' . uniqid() . '.tmp';
317
+
318
+ $this->put_contents( $temp_file, '' );
319
+
320
+ $is_writable = $this->wp_filesystem->is_writable( $temp_file );
321
+
322
+ $this->delete( $temp_file );
323
+
324
+ return $is_writable;
325
+ } else {
326
+ // Create the file if not exists
327
+ if ( ! $this->exists( $file ) ) {
328
+ $this->put_contents( $file, '' );
329
+ }
330
+ return $this->wp_filesystem->is_writable( $file );
331
+ }
332
+ }
333
+
334
+ /**
335
+ * Create zip file of a folder and paste to a destination.
336
+ *
337
+ * @since 1.0.3
338
+ *
339
+ * @param string $folder Target folder to zip.
340
+ * @param string $destination Destination to save the zip.
341
+ * @param string $enclose Enclose all files inside a folder name.
342
+ *
343
+ * @return boolean Zipping status.
344
+ */
345
+ public function zip_folder( $folder, $destination, $enclose ) {
346
+ if ( ! $this->exists( $folder ) ) {
347
+ return false;
348
+ }
349
+
350
+ $temp_file = tempnam( WP_CONTENT_DIR, 'zip' );
351
+
352
+ $zip = new ZipArchive();
353
+
354
+ $zip->open( $temp_file );
355
+
356
+ $files = new RecursiveIteratorIterator( new RecursiveDirectoryIterator( $folder ) );
357
+
358
+ foreach ( $files as $name => $file ) {
359
+ if ( $file->isDir() ) {
360
+ continue;
361
+ }
362
+
363
+ $file_path = $file->getRealPath();
364
+
365
+ $relative_path = substr( $file_path, strlen( $folder ) + 1 );
366
+
367
+ if ( ! empty( $enclose ) ) {
368
+ $relative_path = $enclose . '/' . $relative_path;
369
+ }
370
+
371
+ $zip->addFile( $file_path, $relative_path );
372
+ }
373
+
374
+ $zip->close();
375
+
376
+ // Copy the temp file to directory.
377
+ $this->copy( $temp_file, $destination );
378
+
379
+ // Delete temp file.
380
+ $this->delete( $temp_file );
381
+
382
+ // Check to make sure the file exists.
383
+ return $this->exists( $destination );
384
+ }
385
+
386
+ /**
387
+ * Extract zip file.
388
+ *
389
+ * @param [type] $source The source zip file.
390
+ * @param [type] $destination The destination path.
391
+ * @return [type] [description]
392
+ */
393
+ public function unzip_custom( $source, $destination ) {
394
+ if ( ! $this->exists( $source ) ) {
395
+ $this->add_error( 'zip_source_file_not_exists', sprintf( __( 'Zip source file not exists: %s', 'jupiterx-core' ), $source ) );
396
+ return false;
397
+ }
398
+
399
+ if ( ! $this->is_file( $source ) ) {
400
+ $this->add_error( 'zip_source_file_not_valid', sprintf( __( 'Zip source file not valid: %s', 'jupiterx-core' ), $source ) );
401
+ return false;
402
+ }
403
+
404
+ if ( ! $this->is_readable( $source ) ) {
405
+ $this->add_error( 'zip_source_file_not_readable', sprintf( __( 'Zip source file not readable: %s', 'jupiterx-core' ), $source ) );
406
+ return false;
407
+ }
408
+
409
+ // Check $destination is valid
410
+ if ( ! $this->is_dir( $destination ) ) {
411
+
412
+ // If file exists has same name with $destination, delete it
413
+ if ( $this->exists( $destination ) ) {
414
+ $this->delete( $destination );
415
+ }
416
+
417
+ // Try create new $destination path
418
+ if ( ! $this->mkdir( $destination ) ) {
419
+ $this->add_error( 'fail_create_unzip_destination_directory', sprintf( __( 'Failed to create unzip destination directory: %s', 'jupiterx-core' ), $destination ) );
420
+ return false;
421
+ }
422
+ }
423
+
424
+ // Check $destination is writable
425
+ if ( ! $this->is_writable( $destination ) ) {
426
+ $this->add_error( 'unzip_destination_not_writable', sprintf( __( 'Unzip destination is not writable: %s', 'jupiterx-core' ), $destination ) );
427
+ return false;
428
+ }
429
+
430
+ global $wp_filesystem;
431
+
432
+ $wp_filesystem = $this->wp_filesystem;
433
+
434
+ $unzip_file = unzip_file( $source, $destination );
435
+
436
+ if ( is_wp_error( $unzip_file ) ) {
437
+ $this->add_error( $unzip_file->get_error_code(), $unzip_file->get_error_message() );
438
+ return false;
439
+ } elseif ( ! $unzip_file ) {
440
+ $this->add_error( 'failed_unzipping_file', sprintf( __( 'Failed unzipping file: %s', 'jupiterx-core' ), $source ) );
441
+ return false;
442
+ }
443
+ return true;
444
+ }
445
+
446
+ /**
447
+ * Set options data
448
+ *
449
+ * @param (string) $key
450
+ * @param (string) $value
451
+ * @return void
452
+ */
453
+ public function setOption( $key, $value ) {
454
+ switch ( $key ) {
455
+ case 'context':
456
+ if ( ! empty( $value ) ) {
457
+ $value = is_dir( $value ) ? $value : dirname( $value );
458
+ $value = untrailingslashit( $value );
459
+ }
460
+ break;
461
+ }
462
+ $this->options[ $key ] = $value;
463
+ }
464
+
465
+ /**
466
+ * Get options data
467
+ *
468
+ * @param (string) $key
469
+ * @param (null|string) $default
470
+ * @return mixed
471
+ */
472
+ public function getOption( $key = null, $default = null ) {
473
+ if ( null === $key ) {
474
+ return $this->options;
475
+ } else {
476
+ return isset( $this->options[ $key ] ) ? $this->options[ $key ] : $default;
477
+ }
478
+ }
479
+
480
+ /**
481
+ * Delete options data
482
+ *
483
+ * @param (string) $key
484
+ * @return void
485
+ */
486
+ public function deleteOption( $key ) {
487
+ unset( $this->options[ $key ] );
488
+ }
489
+
490
+ /**
491
+ * Set errors data taken from the wp_filesystem errors
492
+ *
493
+ * @return void
494
+ */
495
+ private function setErrors() {
496
+ if ( isset( $this->wp_filesystem->errors ) && is_wp_error( $this->wp_filesystem->errors ) && $this->wp_filesystem->errors->get_error_code() ) {
497
+ $this->add_error( $this->wp_filesystem->errors->get_error_code(), $this->wp_filesystem->errors->get_error_message() );
498
+ }
499
+ }
500
+
501
+ /**
502
+ * Get current connection method
503
+ *
504
+ * @return mixed
505
+ */
506
+ public function getConnectionMethod() {
507
+ return isset( $this->wp_filesystem->method ) ? $this->wp_filesystem->method : false;
508
+ }
509
+
510
+ /**
511
+ * Get credentials data
512
+ *
513
+ * @return mixed
514
+ */
515
+ public function getCredsData() {
516
+ return $this->creds_data;
517
+ }
518
+
519
+ /**
520
+ * Get all errors
521
+ *
522
+ * @return object Will return the WP_Error object
523
+ */
524
+ public function get_errors() {
525
+ return $this->errors;
526
+ }
527
+
528
+ /**
529
+ * Retrieve all error codes
530
+ *
531
+ * @return array
532
+ */
533
+ public function get_error_codes() {
534
+ return $this->errors->get_error_codes();
535
+ }
536
+
537
+ /**
538
+ * Retrieve first error code available
539
+ *
540
+ * @return string, int or Empty if there is no error codes
541
+ */
542
+ public function get_error_code() {
543
+ return $this->errors->get_error_code();
544
+ }
545
+
546
+ /**
547
+ * Retrieve all error messages or error messages matching code
548
+ *
549
+ * @param (string) $code
550
+ * @return array
551
+ */
552
+ public function get_error_messages( $code = '' ) {
553
+ return $this->errors->get_error_messages( $code );
554
+ }
555
+
556
+ /**
557
+ * Get the first error message available or error message matching code
558
+ *
559
+ * @param (string) $code
560
+ * @return string
561
+ */
562
+ public function get_error_message( $code = '' ) {
563
+ return $this->errors->get_error_message( $code );
564
+ }
565
+
566
+ /**
567
+ * Append more error messages to list of error messages.
568
+ *
569
+ * @param (string) $code
570
+ * @param (string) $message
571
+ * @param (array) $data
572
+ * @return void
573
+ */
574
+ public function add_error( $code, $message, $data = '' ) {
575
+ // Log the error
576
+ if ( defined( 'WP_DEBUG_LOG' ) && WP_DEBUG_LOG ) {
577
+ error_log( 'JupiterX_Filesystem Error Code: ' . $code );
578
+ error_log( 'JupiterX_Filesystem Error Message: ' . $message );
579
+ if ( $data && ! is_resource( $data ) ) {
580
+ error_log( 'JupiterX_Filesystem Error Data: ' . wp_json_encode( $data ) );
581
+ }
582
+ }
583
+ $this->errors->add( $code, $message, $data );
584
+ }
585
+
586
+ /**
587
+ * Check path is outside open_basedir paths.
588
+ *
589
+ * @param string $path
590
+ * @return boolean
591
+ */
592
+ public function is_open_basedir_restricted( $path ) {
593
+ $open_basedir_paths = ini_get('open_basedir');
594
+
595
+ if ( empty( $open_basedir_paths ) ) {
596
+ return false;
597
+ }
598
+
599
+ $open_basedir_paths = explode( PATH_SEPARATOR, $open_basedir_paths );
600
+
601
+ foreach ( $open_basedir_paths as $open_basedir_path ) {
602
+ if (
603
+ strpos( $open_basedir_path, $path ) === 0 &&
604
+ $open_basedir_path !== $path
605
+ ) {
606
+ return true;
607
+ }
608
+ }
609
+
610
+ return false;
611
+ }
612
+ }
613
+ }
includes/control-panel/includes/class-helpers.php CHANGED
@@ -1,510 +1,510 @@
1
- <?php
2
- if ( ! class_exists( 'JupiterX_Control_Panel_Helpers' ) ) {
3
- /**
4
- * Helper functions class.
5
- *
6
- * @since 1.7.0
7
- */
8
- class JupiterX_Control_Panel_Helpers {
9
-
10
- /**
11
- * Method that is resposible to unzip compress files .
12
- * it used native WordPress functions.
13
- *
14
- * @since 1.0.0
15
- * @author Artbees <info@artbees.net>
16
- *
17
- * @param str $zip_path compress file absolute path.
18
- * @param str $dest_path Where should it be uncompressed.
19
- *
20
- * @return bool will return boolean status of action
21
- */
22
- public static function un_zip( $zip_path, $dest_path ) {
23
-
24
- $zip_path = realpath( $zip_path );
25
- $dest_path = realpath( $dest_path );
26
-
27
- $jupiterx_filesystem = new JupiterX_Filesystem(
28
- array(
29
- 'context' => $dest_path,
30
- )
31
- );
32
-
33
- if ( $jupiterx_filesystem->get_error_code() ) {
34
- throw new Exception( $jupiterx_filesystem->get_error_message() );
35
- return false;
36
- }
37
-
38
- if ( ! $jupiterx_filesystem->exists( $zip_path ) ) {
39
- throw new Exception( __( 'Zip file that you are looking for is not exist', 'jupiterx-core' ) );
40
- return false;
41
- }
42
-
43
- if ( ! $jupiterx_filesystem->exists( $dest_path ) ) {
44
- if ( ! $jupiterx_filesystem->mkdir( $dest_path ) ) {
45
- throw new Exception( __( 'Unzip destination path not exist', 'jupiterx-core' ) );
46
- return false;
47
- }
48
- }
49
-
50
- if ( ! $jupiterx_filesystem->is_writable( $dest_path ) ) {
51
- throw new Exception( __( 'Unzip destination is not writable , Please resolve this issue first.', 'jupiterx-core' ) );
52
- return false;
53
- }
54
-
55
- $unzipfile = unzip_file( $zip_path, $dest_path );
56
- if ( is_wp_error( $unzipfile ) ) {
57
- throw new Exception( $unzipfile->get_error_message(), 1 );
58
- return false;
59
- }
60
- return true;
61
- }
62
- /**
63
- * You can create a directory using this helper , it will check the dest directory for if its writable or not then
64
- * try to create new one
65
- *
66
- * @since 1.0.0
67
- * @author Artbees <info@artbees.net>
68
- *
69
- * @param str $path path of directory that need to be created.
70
- * @param int $perm permission of new directory , default is : 0775.
71
- *
72
- * @return bool will return boolean status of action , all message is setted to $this->message()
73
- */
74
- public static function check_perm_and_create( $path, $perm = 0775 ) {
75
-
76
- $jupiterx_filesystem = new JupiterX_Filesystem(
77
- array(
78
- 'context' => $path,
79
- )
80
- );
81
-
82
- if ( $jupiterx_filesystem->get_error_code() ) {
83
- throw new Exception( $jupiterx_filesystem->get_error_message() );
84
- return false;
85
- }
86
-
87
- if ( $jupiterx_filesystem->exists( $path ) ) {
88
- if ( ! $jupiterx_filesystem->is_writable( $path ) ) {
89
- throw new Exception( sprintf( __( '%s directory is not writable', 'jupiterx-core' ), $path ) );
90
- return false;
91
- }
92
- return true;
93
- } else {
94
- if ( ! $jupiterx_filesystem->mkdir( $path, $perm ) ) {
95
- throw new Exception( sprintf( __( 'Can\'t create directory %s', 'jupiterx-core' ), $path ) );
96
- return false;
97
- }
98
- return true;
99
- }
100
- }
101
- /**
102
- * This method is resposible to download file from url and save it on server.
103
- * it will check if curl is available or not and then decide to use curl or file_get_content
104
- *
105
- * @since 1.0.0
106
- * @author Artbees <info@artbees.net>
107
- *
108
- * @param string $url url of file (http://yahoo.com/test-plugin.zip).
109
- * @param string $file_name name of the fire that should be create at destination directory.
110
- * @param string $dest_directory absolute path of directory that file save on it.
111
- *
112
- * @return bool will return action status
113
- */
114
- public static function upload_from_url( $url, $file_name, $dest_directory, $remote_args = [] ) {
115
- set_time_limit( 0 );
116
-
117
- try {
118
- self::check_perm_and_create( $dest_directory );
119
- } catch ( Exception $e ) {
120
- throw new Exception( sprintf( __( 'Destination directory is not ready for upload . {%s}', 'jupiterx-core' ), $dest_directory ) );
121
- return false;
122
- }
123
-
124
-
125
- $response = wp_remote_get( $url, array_merge( [ 'timeout' => 120 ], $remote_args ) );
126
-
127
- if ( is_wp_error( $response ) ) {
128
- throw new Exception( $response->get_error_message() );
129
- return false;
130
- }
131
-
132
- $response_body = wp_remote_retrieve_body( $response );
133
-
134
- if ( is_wp_error( $response_body ) ) {
135
- throw new Exception( $response_body->get_error_message() );
136
- return false;
137
- }
138
-
139
- $jupiterx_filesystem = new JupiterX_Filesystem(
140
- array(
141
- 'context' => $dest_directory,
142
- )
143
- );
144
-
145
- if ( $jupiterx_filesystem->get_error_code() ) {
146
- throw new Exception( $jupiterx_filesystem->get_error_message() );
147
- return false;
148
- }
149
-
150
- if ( ! $jupiterx_filesystem->put_contents( $dest_directory . $file_name, $response_body ) ) {
151
- throw new Exception( sprintf( __( "Can't write file to {%s}", 'jupiterx-core' ), $dest_directory . $file_name ) );
152
- return false;
153
- }
154
-
155
- return $dest_directory . $file_name;
156
- }
157
-
158
- /**
159
- * This method is resposible to delete a directory or file.
160
- * if the path is pointing to a directory it will remove all the includes file recursivly and then remove directory at last step
161
- * if the path is pointing to a file it will remove it
162
- *
163
- * @since 1.0.0
164
- * @author Artbees <info@artbees.net>
165
- *
166
- * @param str $dir for example (/var/www/jupiter/wp-content/plugins).
167
- *
168
- * @return bool true or false
169
- */
170
- public static function delete_file_and_dir( $dir ) {
171
-
172
- if ( empty( $dir ) == true || strlen( $dir ) < 2 ) {
173
- return false;
174
- }
175
-
176
- $dir = realpath( $dir );
177
-
178
- $jupiterx_filesystem = new JupiterX_Filesystem(
179
- array(
180
- 'context' => $dir,
181
- )
182
- );
183
-
184
- if ( $jupiterx_filesystem->get_error_code() ) {
185
- return false;
186
- }
187
-
188
- if ( ! $jupiterx_filesystem->exists( $dir ) ) {
189
- return true;
190
- }
191
-
192
- if ( $jupiterx_filesystem->is_dir( $dir ) ) {
193
- return $jupiterx_filesystem->rmdir( $dir, true );
194
- } else {
195
- return $jupiterx_filesystem->delete( $dir );
196
- }
197
-
198
- }
199
-
200
-
201
- /**
202
- * Prevents cache.
203
- * Deletes cache files and transients.
204
- *
205
- * @since 1.0.0
206
- */
207
-
208
- public static function prevent_cache_plugins() {
209
- if ( function_exists( 'w3tc_pgcache_flush' ) ) {
210
- w3tc_pgcache_flush();
211
- // W3 Total Cache: Page cache flushed.
212
- } elseif ( function_exists( 'wp_cache_clear_cache' ) ) {
213
- wp_cache_clear_cache();
214
- // WP Super Cache: Page cache cleared.
215
- } elseif ( function_exists( 'rocket_clean_domain' ) ) {
216
- rocket_clean_domain();
217
- // WP Rocket: Domain cache purged.
218
- }
219
-
220
- if ( ! defined( 'DONOTCACHEPAGE' ) ) {
221
- define( 'DONOTCACHEPAGE', true );
222
- }
223
-
224
- if ( ! defined( 'DONOTCACHCEOBJECT' ) ) {
225
- define( 'DONOTCACHCEOBJECT', true );
226
- }
227
-
228
- if ( ! defined( 'DONOTMINIFY' ) ) {
229
- define( 'DONOTMINIFY', true );
230
- }
231
-
232
- if ( ! defined( 'DONOTCACHEDB' ) ) {
233
- define( 'DONOTCACHEDB', true );
234
- }
235
-
236
- if ( ! defined( 'DONOTCDN' ) ) {
237
- define( 'DONOTCDN', true );
238
- }
239
- }
240
-
241
- /**
242
- * Safely and securely get file from server.
243
- * It attempts to read file using WordPress native file read functions
244
- * If it fails, we use wp_remote_get. if the site is ssl enabled, we try to convert it http as some servers may fail to get file
245
- *
246
- * @author Artbees <info@artbees.net>
247
- *
248
- * @param $file_url string its directory URL.
249
- * @param $file_dir string its directory Path.
250
- *
251
- * @return $wp_file_body string
252
- */
253
- public static function getFileBody( $file_uri, $file_dir ) {
254
-
255
- $wp_remote_get_file_body = '';
256
- $file_dir = realpath( $file_dir );
257
-
258
- $jupiterx_filesystem = new JupiterX_Filesystem(
259
- array(
260
- 'context' => $file_dir,
261
- )
262
- );
263
-
264
- if ( $jupiterx_filesystem->get_error_code() ) {
265
- throw new Exception( $jupiterx_filesystem->get_error_message() );
266
- return false;
267
- }
268
-
269
- $wp_get_file_body = $jupiterx_filesystem->get_contents( $file_dir );
270
- if ( false == $wp_get_file_body ) {
271
- $wp_remote_get_file = wp_remote_get( $file_uri );
272
-
273
- if ( is_array( $wp_remote_get_file ) && array_key_exists( 'body', $wp_remote_get_file ) ) {
274
- $wp_remote_get_file_body = $wp_remote_get_file['body'];
275
-
276
- } elseif ( is_numeric( strpos( $file_uri, 'https://' ) ) ) {
277
-
278
- $file_uri = str_replace( 'https://', 'http://', $file_uri );
279
- $wp_remote_get_file = wp_remote_get( $file_uri );
280
-
281
- if ( ! is_array( $wp_remote_get_file ) || ! array_key_exists( 'body', $wp_remote_get_file ) ) {
282
- throw new Exception( __( 'SSL connection error. Code: template-assets-get', 'jupiterx-core' ) );
283
- return false;
284
- }
285
-
286
- $wp_remote_get_file_body = $wp_remote_get_file['body'];
287
- }
288
-
289
- $wp_file_body = $wp_remote_get_file_body;
290
-
291
- } else {
292
- $wp_file_body = $wp_get_file_body;
293
- }
294
- return $wp_file_body;
295
- }
296
-
297
- /**
298
- * Check if the request is done through a localhost.
299
- *
300
- * @author Artbees <info@artbees.net>
301
- *
302
- * @return boolean
303
- */
304
- public static function is_localhost() {
305
- return ('127.0.0.1' == $_SERVER['REMOTE_ADDR'] || 'localhost' == $_SERVER['REMOTE_ADDR'] || '::1' == $_SERVER['REMOTE_ADDR']) ? 1 : 0;
306
- }
307
-
308
- /**
309
- * Convert alphabetical bit size to numericals
310
- *
311
- * @author Artbees <info@artbees.net>
312
- *
313
- * @return number
314
- */
315
- public static function let_to_num( $size ) {
316
- $l = substr( $size, -1 );
317
- $ret = substr( $size, 0, -1 );
318
-
319
- switch ( strtoupper( $l ) ) {
320
- case 'P':
321
- $ret *= 1024;
322
- case 'T':
323
- $ret *= 1024;
324
- case 'G':
325
- $ret *= 1024;
326
- case 'M':
327
- $ret *= 1024;
328
- case 'K':
329
- $ret *= 1024;
330
- }
331
-
332
- return $ret;
333
- }
334
-
335
- /**
336
- * Convert boolean value to a string value (e.g. from true to 'true')
337
- *
338
- * @author Artbees <info@artbees.net>
339
- *
340
- * @return String
341
- */
342
- public static function make_bool_string( $var ) {
343
- if ( false == $var || 'false' == $var || 0 == $var || '0' == $var || '' == $var || empty( $var ) ) {
344
- return 'false';
345
- }
346
- return 'true';
347
- }
348
-
349
- /**
350
- * It will create a compress file from list of files
351
- *
352
- * @author Artbees <info@artbees.net>
353
- *
354
- * @param array $files for example : array('preload-images/5.jpg','kwicks/ringo.gif','rod.jpg','reddit.gif');.
355
- * @param string $destination name of the file or full address of destination for example : my-archive.zip.
356
- * @param boolean $overwrite if destionation exist , should it overwrite the compress file ?.
357
- *
358
- * @return boolean true if completed and false if something goes wrong
359
- */
360
- public static function zip( $files = array(), $destination = '', $overwrite = false ) {
361
-
362
- $jupiterx_filesystem = new JupiterX_Filesystem(
363
- array(
364
- 'context' => $destination,
365
- )
366
- );
367
-
368
- if ( $jupiterx_filesystem->get_error_code() ) {
369
- return false;
370
- }
371
-
372
- // if the zip file already exists and overwrite is false, return false.
373
- if ( $jupiterx_filesystem->exists( $destination ) && ! $overwrite ) {
374
- return false;
375
- }
376
-
377
- $valid_files = array();
378
-
379
- // if files were passed in...
380
- if ( is_array( $files ) ) {
381
- // cycle through each file.
382
- foreach ( $files as $file_name => $file_path ) {
383
- // make sure the file exists.
384
- if ( $jupiterx_filesystem->exists( $file_path ) ) {
385
- $valid_files[ $file_name ] = $file_path;
386
- }
387
- }
388
- }
389
- // if we have good files...
390
- if ( count( $valid_files ) ) {
391
-
392
- $temp_file = tempnam( sys_get_temp_dir(), 'zip' );
393
-
394
- if ( class_exists( 'ZipArchive', false ) ) {
395
- $zip = new ZipArchive();
396
-
397
- // Try open the temp file.
398
- $zip->open( $temp_file );
399
-
400
- // add the files to archive.
401
- foreach ( $valid_files as $file_name => $file_path ) {
402
- $zip->addFile( $file_path, $file_name );
403
- }
404
-
405
- // close the zip -- done!
406
- $zip->close();
407
-
408
- } else {
409
-
410
- mbstring_binary_safe_encoding();
411
-
412
- require_once ABSPATH . 'wp-admin/includes/class-pclzip.php';
413
-
414
- $zip = new PclZip( $temp_file );
415
-
416
- foreach ( $valid_files as $file_name => $file_path ) {
417
- $zip->create( $file_path, $file_name );
418
- }
419
-
420
- reset_mbstring_encoding();
421
- }
422
-
423
- // add the files to archive.
424
- foreach ( $valid_files as $file_name => $file_path ) {
425
- $zip->addFile( $file_path, $file_name );
426
- }
427
-
428
- // debug
429
- // echo 'The zip archive contains ',$zip->numFiles,' files with a status of ',$zip->status;
430
- // close the zip -- done!
431
- $zip->close();
432
-
433
- // Copy the temp file to destination.
434
- $jupiterx_filesystem->copy( $temp_file, $destination, true, 0644 );
435
-
436
- // Try delete the temp file.
437
- $jupiterx_filesystem->delete( $temp_file );
438
-
439
- // check to make sure the file exists.
440
- return $jupiterx_filesystem->exists( $destination );
441
-
442
- }
443
- return false;
444
- }
445
-
446
- public static function search_multdim( $array, $key, $value ) {
447
- return (array_search( $value, array_column( $array, $key ) ));
448
- }
449
- /**
450
- * It will check wether wordpress-importer plugin is exist in plugin directory or not.
451
- * if exist it will return the WordPress importer file
452
- * if not it will use jupiter version
453
- *
454
- * @author Artbees <info@artbees.net>
455
- * @copyright Artbees LTD (c)
456
- * @link https://artbees.net
457
- * @since Version 5.5
458
- */
459
- public static function include_wordpress_importer() {
460
-
461
- if ( ! class_exists( 'WP_Importer' ) ) {
462
- defined( 'WP_LOAD_IMPORTERS' ) || define( 'WP_LOAD_IMPORTERS', true );
463
- include ABSPATH . '/wp-admin/includes/class-wp-importer.php';
464
- }
465
-
466
- if ( ! class_exists( 'JupiterX_WXR_Importer' ) ) {
467
- jupiterx_core()->load_files( [
468
- 'control-panel/includes/importer/class-logger',
469
- 'control-panel/includes/importer/class-logger-serversentevents',
470
- 'control-panel/includes/importer/class-wxr-import-info',
471
- 'control-panel/includes/importer/class-wxr-importer',
472
- ] );
473
- }
474
-
475
- if ( ! class_exists( 'JupiterX_Importer' ) ) {
476
- jupiterx_core()->load_files( [
477
- 'control-panel/includes/importer/class-jupiterx-importer',
478
- ] );
479
- }
480
-
481
- return true;
482
- }
483
- /**
484
- * It will return permission of directory
485
- *
486
- * @author Artbees <info@artbees.net>
487
- *
488
- * @param string $path Full path of directory.
489
- *
490
- * @return int
491
- */
492
- public static function get_perm( $path ) {
493
- return substr( sprintf( '%o', fileperms( ABSPATH . $path ) ), -4 );
494
- }
495
-
496
- /**
497
- * Convert Bytes to MegaBytes.
498
- *
499
- * @access public
500
- * @static
501
- * @since 1.10.0
502
- *
503
- * @param [type] $bytes
504
- * @return void
505
- */
506
- public static function bytes_to_mb( $bytes ) {
507
- return round( $bytes / ( 1024 * 1024 ), 2 );
508
- }
509
- }
510
- }
1
+ <?php
2
+ if ( ! class_exists( 'JupiterX_Control_Panel_Helpers' ) ) {
3
+ /**
4
+ * Helper functions class.
5
+ *
6
+ * @since 1.7.0
7
+ */
8
+ class JupiterX_Control_Panel_Helpers {
9
+
10
+ /**
11
+ * Method that is resposible to unzip compress files .
12
+ * it used native WordPress functions.
13
+ *
14
+ * @since 1.0.0
15
+ * @author Artbees <info@artbees.net>
16
+ *
17
+ * @param str $zip_path compress file absolute path.
18
+ * @param str $dest_path Where should it be uncompressed.
19
+ *
20
+ * @return bool will return boolean status of action
21
+ */
22
+ public static function un_zip( $zip_path, $dest_path ) {
23
+
24
+ $zip_path = realpath( $zip_path );
25
+ $dest_path = realpath( $dest_path );
26
+
27
+ $jupiterx_filesystem = new JupiterX_Filesystem(
28
+ array(
29
+ 'context' => $dest_path,
30
+ )
31
+ );
32
+
33
+ if ( $jupiterx_filesystem->get_error_code() ) {
34
+ throw new Exception( $jupiterx_filesystem->get_error_message() );
35
+ return false;
36
+ }
37
+
38
+ if ( ! $jupiterx_filesystem->exists( $zip_path ) ) {
39
+ throw new Exception( __( 'Zip file that you are looking for is not exist', 'jupiterx-core' ) );
40
+ return false;
41
+ }
42
+
43
+ if ( ! $jupiterx_filesystem->exists( $dest_path ) ) {
44
+ if ( ! $jupiterx_filesystem->mkdir( $dest_path ) ) {
45
+ throw new Exception( __( 'Unzip destination path not exist', 'jupiterx-core' ) );
46
+ return false;
47
+ }
48
+ }
49
+
50
+ if ( ! $jupiterx_filesystem->is_writable( $dest_path ) ) {
51
+ throw new Exception( __( 'Unzip destination is not writable , Please resolve this issue first.', 'jupiterx-core' ) );
52
+ return false;
53
+ }
54
+
55
+ $unzipfile = unzip_file( $zip_path, $dest_path );
56
+ if ( is_wp_error( $unzipfile ) ) {
57
+ throw new Exception( $unzipfile->get_error_message(), 1 );
58
+ return false;
59
+ }
60
+ return true;
61
+ }
62
+ /**
63
+ * You can create a directory using this helper , it will check the dest directory for if its writable or not then
64
+ * try to create new one
65
+ *
66
+ * @since 1.0.0
67
+ * @author Artbees <info@artbees.net>
68
+ *
69
+ * @param str $path path of directory that need to be created.
70
+ * @param int $perm permission of new directory , default is : 0775.
71
+ *
72
+ * @return bool will return boolean status of action , all message is setted to $this->message()
73
+ */
74
+ public static function check_perm_and_create( $path, $perm = 0775 ) {
75
+
76
+ $jupiterx_filesystem = new JupiterX_Filesystem(
77
+ array(
78
+ 'context' => $path,
79
+ )
80
+ );
81
+
82
+ if ( $jupiterx_filesystem->get_error_code() ) {
83
+ throw new Exception( $jupiterx_filesystem->get_error_message() );
84
+ return false;
85
+ }
86
+
87
+ if ( $jupiterx_filesystem->exists( $path ) ) {
88
+ if ( ! $jupiterx_filesystem->is_writable( $path ) ) {
89
+ throw new Exception( sprintf( __( '%s directory is not writable', 'jupiterx-core' ), $path ) );
90
+ return false;
91
+ }
92
+ return true;
93
+ } else {
94
+ if ( ! $jupiterx_filesystem->mkdir( $path, $perm ) ) {
95
+ throw new Exception( sprintf( __( 'Can\'t create directory %s', 'jupiterx-core' ), $path ) );
96
+ return false;
97
+ }
98
+ return true;
99
+ }
100
+ }
101
+ /**
102
+ * This method is resposible to download file from url and save it on server.
103
+ * it will check if curl is available or not and then decide to use curl or file_get_content
104
+ *
105
+ * @since 1.0.0
106
+ * @author Artbees <info@artbees.net>
107
+ *
108
+ * @param string $url url of file (http://yahoo.com/test-plugin.zip).
109
+ * @param string $file_name name of the fire that should be create at destination directory.
110
+ * @param string $dest_directory absolute path of directory that file save on it.
111
+ *
112
+ * @return bool will return action status
113
+ */
114
+ public static function upload_from_url( $url, $file_name, $dest_directory, $remote_args = [] ) {
115
+ set_time_limit( 0 );
116
+
117
+ try {
118
+ self::check_perm_and_create( $dest_directory );
119
+ } catch ( Exception $e ) {
120
+ throw new Exception( sprintf( __( 'Destination directory is not ready for upload . {%s}', 'jupiterx-core' ), $dest_directory ) );
121
+ return false;
122
+ }
123
+
124
+
125
+ $response = wp_remote_get( $url, array_merge( [ 'timeout' => 120 ], $remote_args ) );
126
+
127
+ if ( is_wp_error( $response ) ) {
128
+ throw new Exception( $response->get_error_message() );
129
+ return false;
130
+ }
131
+
132
+ $response_body = wp_remote_retrieve_body( $response );
133
+
134
+ if ( is_wp_error( $response_body ) ) {
135
+ throw new Exception( $response_body->get_error_message() );
136
+ return false;
137
+ }
138
+
139
+ $jupiterx_filesystem = new JupiterX_Filesystem(
140
+ array(
141
+ 'context' => $dest_directory,
142
+ )
143
+ );
144
+
145
+ if ( $jupiterx_filesystem->get_error_code() ) {
146
+ throw new Exception( $jupiterx_filesystem->get_error_message() );
147
+ return false;
148
+ }
149
+
150
+ if ( ! $jupiterx_filesystem->put_contents( $dest_directory . $file_name, $response_body ) ) {
151
+ throw new Exception( sprintf( __( "Can't write file to {%s}", 'jupiterx-core' ), $dest_directory . $file_name ) );
152
+ return false;
153
+ }
154
+
155
+ return $dest_directory . $file_name;
156
+ }
157
+
158
+ /**
159
+ * This method is resposible to delete a directory or file.
160
+ * if the path is pointing to a directory it will remove all the includes file recursivly and then remove directory at last step
161
+ * if the path is pointing to a file it will remove it
162
+ *
163
+ * @since 1.0.0
164
+ * @author Artbees <info@artbees.net>
165
+ *
166
+ * @param str $dir for example (/var/www/jupiter/wp-content/plugins).
167
+ *
168
+ * @return bool true or false
169
+ */
170
+ public static function delete_file_and_dir( $dir ) {
171
+
172
+ if ( empty( $dir ) == true || strlen( $dir ) < 2 ) {
173
+ return false;
174
+ }
175
+
176
+ $dir = realpath( $dir );
177
+
178
+ $jupiterx_filesystem = new JupiterX_Filesystem(
179
+ array(
180
+ 'context' => $dir,
181
+ )
182
+ );
183
+
184
+ if ( $jupiterx_filesystem->get_error_code() ) {
185
+ return false;
186
+ }
187
+
188
+ if ( ! $jupiterx_filesystem->exists( $dir ) ) {
189
+ return true;
190
+ }
191
+
192
+ if ( $jupiterx_filesystem->is_dir( $dir ) ) {
193
+ return $jupiterx_filesystem->rmdir( $dir, true );
194
+ } else {
195
+ return $jupiterx_filesystem->delete( $dir );
196
+ }
197
+
198
+ }
199
+
200
+
201
+ /**
202
+ * Prevents cache.
203
+ * Deletes cache files and transients.
204
+ *
205
+ * @since 1.0.0
206
+ */
207
+
208
+ public static function prevent_cache_plugins() {
209
+ if ( function_exists( 'w3tc_pgcache_flush' ) ) {
210
+ w3tc_pgcache_flush();
211
+ // W3 Total Cache: Page cache flushed.
212
+ } elseif ( function_exists( 'wp_cache_clear_cache' ) ) {
213
+ wp_cache_clear_cache();
214
+ // WP Super Cache: Page cache cleared.
215
+ } elseif ( function_exists( 'rocket_clean_domain' ) ) {
216
+ rocket_clean_domain();
217
+ // WP Rocket: Domain cache purged.
218
+ }
219
+
220
+ if ( ! defined( 'DONOTCACHEPAGE' ) ) {
221
+ define( 'DONOTCACHEPAGE', true );
222
+ }
223
+
224
+ if ( ! defined( 'DONOTCACHCEOBJECT' ) ) {
225
+ define( 'DONOTCACHCEOBJECT', true );
226
+ }
227
+
228
+ if ( ! defined( 'DONOTMINIFY' ) ) {
229
+ define( 'DONOTMINIFY', true );
230
+ }
231
+
232
+ if ( ! defined( 'DONOTCACHEDB' ) ) {
233
+ define( 'DONOTCACHEDB', true );
234
+ }
235
+
236
+ if ( ! defined( 'DONOTCDN' ) ) {
237
+ define( 'DONOTCDN', true );
238
+ }
239
+ }
240
+
241
+ /**
242
+ * Safely and securely get file from server.
243
+ * It attempts to read file using WordPress native file read functions
244
+ * If it fails, we use wp_remote_get. if the site is ssl enabled, we try to convert it http as some servers may fail to get file
245
+ *
246
+ * @author Artbees <info@artbees.net>
247
+ *
248
+ * @param $file_url string its directory URL.
249
+ * @param $file_dir string its directory Path.
250
+ *
251
+ * @return $wp_file_body string
252
+ */
253
+ public static function getFileBody( $file_uri, $file_dir ) {
254
+
255
+ $wp_remote_get_file_body = '';
256
+ $file_dir = realpath( $file_dir );
257
+
258
+ $jupiterx_filesystem = new JupiterX_Filesystem(
259
+ array(
260
+ 'context' => $file_dir,
261
+ )
262
+ );
263
+
264
+ if ( $jupiterx_filesystem->get_error_code() ) {
265
+ throw new Exception( $jupiterx_filesystem->get_error_message() );
266
+ return false;
267
+ }
268
+
269
+ $wp_get_file_body = $jupiterx_filesystem->get_contents( $file_dir );
270
+ if ( false == $wp_get_file_body ) {
271
+ $wp_remote_get_file = wp_remote_get( $file_uri );
272
+
273
+ if ( is_array( $wp_remote_get_file ) && array_key_exists( 'body', $wp_remote_get_file ) ) {
274
+ $wp_remote_get_file_body = $wp_remote_get_file['body'];
275
+
276
+ } elseif ( is_numeric( strpos( $file_uri, 'https://' ) ) ) {
277
+
278
+ $file_uri = str_replace( 'https://', 'http://', $file_uri );
279
+ $wp_remote_get_file = wp_remote_get( $file_uri );
280
+
281
+ if ( ! is_array( $wp_remote_get_file ) || ! array_key_exists( 'body', $wp_remote_get_file ) ) {
282
+ throw new Exception( __( 'SSL connection error. Code: template-assets-get', 'jupiterx-core' ) );
283
+ return false;
284
+ }
285
+
286
+ $wp_remote_get_file_body = $wp_remote_get_file['body'];
287
+ }
288
+
289
+ $wp_file_body = $wp_remote_get_file_body;
290
+
291
+ } else {
292
+ $wp_file_body = $wp_get_file_body;
293
+ }
294
+ return $wp_file_body;
295
+ }
296
+
297
+ /**
298
+ * Check if the request is done through a localhost.
299
+ *
300
+ * @author Artbees <info@artbees.net>
301
+ *
302
+ * @return boolean
303
+ */
304
+ public static function is_localhost() {
305
+ return ('127.0.0.1' == $_SERVER['REMOTE_ADDR'] || 'localhost' == $_SERVER['REMOTE_ADDR'] || '::1' == $_SERVER['REMOTE_ADDR']) ? 1 : 0;
306
+ }
307
+
308
+ /**
309
+ * Convert alphabetical bit size to numericals
310
+ *
311
+ * @author Artbees <info@artbees.net>
312
+ *
313
+ * @return number
314
+ */
315
+ public static function let_to_num( $size ) {
316
+ $l = substr( $size, -1 );
317
+ $ret = substr( $size, 0, -1 );
318
+
319
+ switch ( strtoupper( $l ) ) {
320
+ case 'P':
321
+ $ret *= 1024;
322
+ case 'T':
323
+ $ret *= 1024;
324
+ case 'G':
325
+ $ret *= 1024;
326
+ case 'M':
327
+ $ret *= 1024;
328
+ case 'K':
329
+ $ret *= 1024;
330
+ }
331
+
332
+ return $ret;
333
+ }
334
+
335
+ /**
336
+ * Convert boolean value to a string value (e.g. from true to 'true')
337
+ *
338
+ * @author Artbees <info@artbees.net>
339
+ *
340
+ * @return String
341
+ */
342
+ public static function make_bool_string( $var ) {
343
+ if ( false == $var || 'false' == $var || 0 == $var || '0' == $var || '' == $var || empty( $var ) ) {
344
+ return 'false';
345
+ }
346
+ return 'true';
347
+ }
348
+
349
+ /**
350
+ * It will create a compress file from list of files
351
+ *
352
+ * @author Artbees <info@artbees.net>
353
+ *
354
+ * @param array $files for example : array('preload-images/5.jpg','kwicks/ringo.gif','rod.jpg','reddit.gif');.
355
+ * @param string $destination name of the file or full address of destination for example : my-archive.zip.
356
+ * @param boolean $overwrite if destionation exist , should it overwrite the compress file ?.
357
+ *
358
+ * @return boolean true if completed and false if something goes wrong
359
+ */
360
+ public static function zip( $files = array(), $destination = '', $overwrite = false ) {
361
+
362
+ $jupiterx_filesystem = new JupiterX_Filesystem(
363
+ array(
364
+ 'context' => $destination,
365
+ )
366
+ );
367
+
368
+ if ( $jupiterx_filesystem->get_error_code() ) {
369
+ return false;
370
+ }
371
+
372
+ // if the zip file already exists and overwrite is false, return false.
373
+ if ( $jupiterx_filesystem->exists( $destination ) && ! $overwrite ) {
374
+ return false;
375
+ }
376
+
377
+ $valid_files = array();
378
+
379
+ // if files were passed in...
380
+ if ( is_array( $files ) ) {
381
+ // cycle through each file.
382
+ foreach ( $files as $file_name => $file_path ) {
383
+ // make sure the file exists.
384
+ if ( $jupiterx_filesystem->exists( $file_path ) ) {
385
+ $valid_files[ $file_name ] = $file_path;
386
+ }
387
+ }
388
+ }
389
+ // if we have good files...
390
+ if ( count( $valid_files ) ) {
391
+
392
+ $temp_file = tempnam( sys_get_temp_dir(), 'zip' );
393
+
394
+ if ( class_exists( 'ZipArchive', false ) ) {
395
+ $zip = new ZipArchive();
396
+
397
+ // Try open the temp file.
398
+ $zip->open( $temp_file );
399
+
400
+ // add the files to archive.
401
+ foreach ( $valid_files as $file_name => $file_path ) {
402
+ $zip->addFile( $file_path, $file_name );
403
+ }
404
+
405
+ // close the zip -- done!
406
+ $zip->close();
407
+
408
+ } else {
409
+
410
+ mbstring_binary_safe_encoding();
411
+
412
+ require_once ABSPATH . 'wp-admin/includes/class-pclzip.php';
413
+
414
+ $zip = new PclZip( $temp_file );
415
+
416
+ foreach ( $valid_files as $file_name => $file_path ) {
417
+ $zip->create( $file_path, $file_name );
418
+ }
419
+
420
+ reset_mbstring_encoding();
421
+ }
422
+
423
+ // add the files to archive.
424
+ foreach ( $valid_files as $file_name => $file_path ) {
425
+ $zip->addFile( $file_path, $file_name );
426
+ }
427
+
428
+ // debug
429
+ // echo 'The zip archive contains ',$zip->numFiles,' files with a status of ',$zip->status;
430
+ // close the zip -- done!
431
+ $zip->close();
432
+
433
+ // Copy the temp file to destination.
434
+ $jupiterx_filesystem->copy( $temp_file, $destination, true, 0644 );
435
+
436
+ // Try delete the temp file.
437
+ $jupiterx_filesystem->delete( $temp_file );
438
+
439
+ // check to make sure the file exists.
440
+ return $jupiterx_filesystem->exists( $destination );
441
+
442
+ }
443
+ return false;
444
+ }
445
+
446
+ public static function search_multdim( $array, $key, $value ) {
447
+ return (array_search( $value, array_column( $array, $key ) ));
448
+ }
449
+ /**
450
+ * It will check wether wordpress-importer plugin is exist in plugin directory or not.
451
+ * if exist it will return the WordPress importer file
452
+ * if not it will use jupiter version
453
+ *
454
+ * @author Artbees <info@artbees.net>
455
+ * @copyright Artbees LTD (c)
456
+ * @link https://artbees.net
457
+ * @since Version 5.5
458
+ */
459
+ public static function include_wordpress_importer() {
460
+
461
+ if ( ! class_exists( 'WP_Importer' ) ) {
462
+ defined( 'WP_LOAD_IMPORTERS' ) || define( 'WP_LOAD_IMPORTERS', true );
463
+ include ABSPATH . '/wp-admin/includes/class-wp-importer.php';
464
+ }
465
+
466
+ if ( ! class_exists( 'JupiterX_WXR_Importer' ) ) {
467
+ jupiterx_core()->load_files( [
468
+ 'control-panel/includes/importer/class-logger',
469
+ 'control-panel/includes/importer/class-logger-serversentevents',
470
+ 'control-panel/includes/importer/class-wxr-import-info',
471
+ 'control-panel/includes/importer/class-wxr-importer',
472
+ ] );
473
+ }
474
+
475
+ if ( ! class_exists( 'JupiterX_Importer' ) ) {
476
+ jupiterx_core()->load_files( [
477
+ 'control-panel/includes/importer/class-jupiterx-importer',
478
+ ] );
479
+ }
480
+
481
+ return true;
482
+ }
483
+ /**
484
+ * It will return permission of directory
485
+ *
486
+ * @author Artbees <info@artbees.net>
487
+ *
488
+ * @param string $path Full path of directory.
489
+ *
490
+ * @return int
491
+ */
492
+ public static function get_perm( $path ) {
493
+ return substr( sprintf( '%o', fileperms( ABSPATH . $path ) ), -4 );
494
+ }
495
+
496
+ /**
497
+ * Convert Bytes to MegaBytes.
498
+ *
499
+ * @access public
500
+ * @static
501
+ * @since 1.10.0
502
+ *
503
+ * @param [type] $bytes
504
+ * @return void
505
+ */
506
+ public static function bytes_to_mb( $bytes ) {
507
+ return round( $bytes / ( 1024 * 1024 ), 2 );
508
+ }
509
+ }
510
+ }
includes/control-panel/includes/class-image-sizes.php CHANGED
@@ -1,98 +1,152 @@
1
- <?php
2
- /**
3
- * This class provides the methods to Store and retrieve Image sizes from database.
4
- *
5
- * @package JupiterX_Core\Control_Panel\Image_Sizes
6
- *
7
- * @since 1.2.0
8
- */
9
-
10
-
11
- if ( ! class_exists( 'JupiterX_Control_Panel_Image_Sizes' ) ) {
12
- /**
13
- * Store and retrieve Image sizes.
14
- *
15
- * @since 1.2.0
16
- */
17
- class JupiterX_Control_Panel_Image_Sizes {
18
-
19
- /**
20
- * Return list of the stored image sizes.
21
- *
22
- * If empty, it will return default sample size.
23
- *
24
- * @since 1.2.0
25
- *
26
- * @return array
27
- */
28
- public static function get_available_image_sizes() {
29
- $options = get_option( JUPITERX_IMAGE_SIZE_OPTION );
30
-
31
- if ( empty( $options ) ) {
32
- $options = [
33
- [
34
- 'size_w' => 500,
35
- 'size_h' => 500,
36
- 'size_n' => 'Image Size 500x500',
37
- 'size_c' => 'on',
38
- ],
39
- ];
40
- }
41
-
42
- return $options;
43
- }
44
-
45
- /**
46
- * Class constructor.
47
- *
48
- * @since 1.2.0
49
- */
50
- public function __construct() {
51
- add_filter( 'jupiterx_control_panel_pane_image_sizes', [ $this, 'view' ] );
52
- add_action( 'wp_ajax_jupiterx_save_image_sizes', [ $this, 'save_image_size' ] );
53
- }
54
-
55
- /**
56
- * Image sizes HTML directory.
57
- *
58
- * @since 1.2.0
59
- *
60
- * @return string
61
- */
62
- public function view() {
63
- return jupiterx_core()->plugin_dir() . 'includes/control-panel/views/image-sizes.php';
64
- }
65
-
66
- /**
67
- * Process image sizes data passed via admin-ajax.php and store it in wp_options table.
68
- *
69
- * @since 1.2.0
70
- */
71
- public function save_image_size() {
72
- check_ajax_referer( 'ajax-image-sizes-options', 'security' );
73
-
74
- $options = [];
75
-
76
- if ( empty( $_POST['options'] ) ) {
77
- wp_send_json_error( esc_html__( 'Options are not valid.', 'jupiterx-core' ) );
78
- }
79
-
80
- // phpcs:disable
81
- $options = array_map( 'sanitize_text_field', $_POST['options'] );
82
- // phpcs:enable
83
-
84
- $options_array = [];
85
-
86
- foreach ( $options as $sizes ) {
87
- parse_str( $sizes, $output );
88
- $options_array[] = $output;
89
- }
90
-
91
- update_option( JUPITERX_IMAGE_SIZE_OPTION, $options_array );
92
-
93
- wp_die( 1 );
94
- }
95
- }
96
- }
97
-
98
- new JupiterX_Control_Panel_Image_Sizes();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * This class provides the methods to Store and retrieve Image sizes from database.
4
+ *
5
+ * @package JupiterX_Core\Control_Panel\Image_Sizes
6
+ *
7
+ * @since 1.2.0
8
+ */
9
+
10
+
11
+ if ( ! class_exists( 'JupiterX_Control_Panel_Image_Sizes' ) ) {
12
+ /**
13
+ * Store and retrieve Image sizes.
14
+ *
15
+ * @since 1.2.0
16
+ */
17
+ class JupiterX_Control_Panel_Image_Sizes {
18
+
19
+ /**
20
+ * Array of custom image sizes.
21
+ *
22
+ * @since 1.11.0
23
+ * @access public
24
+ *
25
+ * @var array
26
+ */
27
+ protected static $default_options = [
28
+ [
29
+ 'size_w' => 500,
30
+ 'size_h' => 500,
31
+ 'size_n' => 'Image Size 500x500',
32
+ 'size_c' => 'on',
33
+ 'default' => true,
34
+ 'id' => 1,
35
+ ],
36
+ ];
37
+
38
+ /**
39
+ * Class constructor.
40
+ *
41
+ * @since 1.2.0
42
+ */
43
+ public function __construct() {
44
+ add_filter( 'jupiterx_control_panel_pane_image_sizes', [ $this, 'view' ] );
45
+ add_action( 'wp_ajax_jupiterx_save_image_sizes', [ $this, 'save_image_size' ] );
46
+ }
47
+
48
+ /**
49
+ * Return list of the stored image sizes.
50
+ *
51
+ * If empty, it will return default sample size.
52
+ *
53
+ * @since 1.2.0
54
+ *
55
+ * @return array
56
+ */
57
+ public static function get_available_image_sizes() {
58
+ $options = get_option( JUPITERX_IMAGE_SIZE_OPTION );
59
+
60
+ if ( empty( $options ) ) {
61
+ $options = [];
62
+ }
63
+
64
+ $existing_default_options = [];
65
+
66
+ foreach ( $options as $option ) {
67
+ if ( ! empty( $option['default'] ) ) {
68
+ $existing_default_options[] = intval( $option['id'] );
69
+ }
70
+ }
71
+
72
+ $deleted_default_options = get_option( 'jupiterx_image_sizes_deleted' );
73
+
74
+ if ( false === $deleted_default_options ) {
75
+ $deleted_default_options = [];
76
+ }
77
+
78
+ foreach ( self::$default_options as $default_option ) {
79
+ if (
80
+ in_array( $default_option['id'], $deleted_default_options, true ) ||
81
+ in_array( $default_option['id'], $existing_default_options, true )
82
+ ) {
83
+ continue;
84
+ }
85
+
86
+ array_unshift( $options, $default_option );
87
+ }
88
+
89
+ return $options;
90
+ }
91
+
92
+ /**
93
+ * Image sizes HTML directory.
94
+ *
95
+ * @since 1.2.0
96
+ *
97
+ * @return string
98
+ */
99
+ public function view() {
100
+ return jupiterx_core()->plugin_dir() . 'includes/control-panel/views/image-sizes.php';
101
+ }
102
+
103
+ /**
104
+ * Process image sizes data passed via admin-ajax.php and store it in wp_options table.
105
+ *
106
+ * @since 1.2.0
107
+ */
108
+ public function save_image_size() {
109
+ check_ajax_referer( 'ajax-image-sizes-options', 'security' );
110
+
111
+ $options = [];
112
+
113
+ if ( ! empty( $_POST['options'] ) ) {
114
+ $options = $_POST['options'];
115
+ }
116
+
117
+ // phpcs:disable
118
+ $options = array_map( 'sanitize_text_field', $options );
119
+ // phpcs:enable
120
+
121
+ $options_array = [];
122
+ $default_options = [];
123
+
124
+ foreach ( $options as $sizes ) {
125
+ parse_str( $sizes, $output );
126
+
127
+ if ( ! empty( $output['default'] ) ) {
128
+ $default_options[] = intval( $output['id'] );
129
+ }
130
+
131
+ $options_array[] = $output;
132
+ }
133
+
134
+ $deleted_default_options = [];
135
+
136
+ foreach (self::$default_options as $default_option) {
137
+ if ( in_array( $default_option['id'], $default_options, true ) ) {
138
+ continue;
139
+ }
140
+
141
+ $deleted_default_options[] = $default_option['id'];
142
+ }
143
+
144
+ update_option( 'jupiterx_image_sizes_deleted', $deleted_default_options );
145
+ update_option( JUPITERX_IMAGE_SIZE_OPTION, $options_array );
146
+
147
+ wp_die( 1 );
148
+ }
149
+ }
150
+ }
151
+
152
+ new JupiterX_Control_Panel_Image_Sizes();
includes/control-panel/includes/class-install-plugins.php CHANGED
@@ -1,506 +1,492 @@
1
- <?php
2
- /**
3
- * This class is responsible to manage all jupiters plugin.
4
- *
5
- * @package JupiterX_Core\Control_Panel
6
- */
7
-
8
- class JupiterX_Control_Panel_Install_Plugins {
9
-
10
- protected $tgmpa;
11
- protected $api_url = 'http://artbees.net/api/v2/';
12
- protected $theme_name = 'JupiterX';
13
- private static $instance = null;
14
-
15
- /**
16
- * Class constructor.
17
- *
18
- * @since 1.9.0
19
- */
20
- public function __construct() {
21
- if ( ! class_exists( 'TGM_Plugin_Activation' ) ) {
22
- return;
23
- }
24
-
25
- $menu_items_access = get_site_option( 'menu_items' );
26
- if ( is_multisite() && ! isset( $menu_items_access['plugins'] ) && ! current_user_can( 'manage_network_plugins' ) ) {
27
- return;
28
- }
29
-
30
- $this->tgmpa = isset( $GLOBALS['tgmpa'] ) ? $GLOBALS['tgmpa'] : TGM_Plugin_Activation::get_instance();
31
-
32
- add_filter( 'jupiterx_control_panel_pane_install_plugins', [ $this, 'view' ] );
33
-
34
- add_action( 'init', [ $this, 'cache' ] );
35
-
36
- add_action( 'wp_ajax_abb_get_plugins', [ $this, 'get_plugins_for_frontend' ] );
37
- add_action( 'wp_ajax_abb_deactivate_plugin', [ $this, 'deactivate' ] );
38
- add_action( 'wp_ajax_abb_update_plugin_checker', [ $this, 'plugin_conflict_checker' ] );
39
- }
40
-
41
- /**
42
- * Load view from Jupiter X Core plugin.
43
- *
44
- * @since 1.9.0
45
- */
46
- public function view() {
47
- return jupiterx_core()->plugin_dir() . 'includes/control-panel/views/install-plugins.php';
48
- }
49
-
50
- /**
51
- * Get class instance.
52
- *
53
- * @since 1.9.0
54
- */
55
- public static function get_instance() {
56
- if ( ! self::$instance ) {
57
- self::$instance = new self;
58
- }
59
-
60
- return self::$instance;
61
- }
62
-
63
- /**
64
- * Send a json list of plugins and their data and activation limit status for front-end usage.
65
- *
66
- * @since 1.9.0
67
- */
68
- public function get_plugins_for_frontend() {
69
- $plugins = $this->get_all_plugins();
70
-
71
- $plugins = $this->update_plugins_status( $plugins );
72
-
73
- ksort( $plugins );
74
-
75
- $limit = $this->plugins_threshold();
76
-
77
- return wp_send_json( [ 'plugins' => $plugins, 'limit' => $limit ] );
78
- }
79
-
80
- /**
81
- * Get full list of plugins.
82
- * 12h cache.
83
- *
84
- * @since 1.9.0
85
- */
86
- public function get_all_plugins() {
87
- $plugins = get_transient( 'jupiterx_list_of_plugins' );
88
-
89
- if ( ! $plugins ) {
90
- $plugins = $this->get_free_plugins() + $this->get_pro_plugins();
91
- set_transient( 'jupiterx_list_of_plugins' , $plugins, 12 * HOUR_IN_SECONDS );
92
- }
93
-
94
- return $plugins;
95
- }
96
-
97
- /**
98
- * Get list of pro plugins from Artbees API.
99
- *
100
- * @since 1.9.0
101
- */
102
- public function get_pro_plugins() {
103
- return $this->get_plugins_from_api( [ 'slug', 'basename', 'version', 'name', 'desc', 'more_link', 'img_url', 'required', 'pro', 'is_callable' ] );
104
- }
105
-
106
- /**
107
- * Filter tgmpa plugins to extract list of free plugins.
108
- *
109
- * @since 1.9.0
110
- */
111
- public function get_free_plugins_raw() {
112
- return array_filter(
113
- $this->tgmpa->plugins,
114
- function( $plugin_info, $plugin ) {
115
- return 'repo' === $plugin_info['source'];
116
- },
117
- ARRAY_FILTER_USE_BOTH
118
- );
119
- }
120
-
121
- /**
122
- * Get list of free plugins.
123
- * List gets update to add needed info like icon and version from WordPress repo.
124
- *
125
- * @since 1.9.0
126
- */
127
- public function get_free_plugins() {
128
-
129
- $plugins = $this->get_free_plugins_raw();
130
-
131
- foreach ( $plugins as $plugin => $plugin_info ) {
132
- $wp_plugin_info = $this->get_plugin_info_from_wp( $plugin, [ 'icons' => true, 'short_description' => true ] );
133
-
134
- $plugins[ $plugin ]['version'] = $wp_plugin_info->version;
135
- $plugins[ $plugin ]['desc'] = $wp_plugin_info->short_description;
136
- $plugins[ $plugin ]['img_url'] = isset( $wp_plugin_info->icons['1x'] ) ? $wp_plugin_info->icons['1x'] : $wp_plugin_info->icons['default'];
137
- }
138
-
139
- return $plugins;
140
- }
141
-
142
- /**
143
- * Get a list of slugs of free plugins.
144
- *
145
- * @since 1.9.0
146
- */
147
- public function get_free_plugins_slug() {
148
- $free_plugins = $this->get_free_plugins();
149
-
150
- return array_column( $plugins, 'slug' );
151
- }
152
-
153
- /**
154
- * Get detail of given plugin from WordPress repo.
155
- * Used to get version, icon and plugin description.
156
- *
157
- * @since 1.9.0
158
- *
159
- * @param string $plugin_slug Plugin slug from plugin header.
160
- * @param array $fields Needed extra information.
161
- */
162
- public function get_plugin_info_from_wp( $plugin_slug = '', $fields = [] ) {
163
-
164
- if ( empty( $plugin_slug ) ) {
165
- return;
166
- }
167
-
168
- if ( ! function_exists( 'plugins_api' ) ) {
169
- require_once ABSPATH . 'wp-admin/includes/plugin-install.php';
170
- }
171
-
172
- $default_fields = [
173
- 'short_description' => false,
174
- 'description' => false,
175
- 'sections' => false,
176
- 'tested' => false,
177
- 'requires' => false,
178
- 'rating' => false,
179
- 'downloaded' => false,
180
- 'downloadlink' => false,
181
- 'last_updated' => false,
182
- 'added' => false,
183
- 'tags' => false,
184
- 'compatibility' => false,
185
- 'homepage' => false,
186
- 'versions' => false,
187
- 'donate_link' => false,
188
- 'reviews' => false,
189
- 'banners' => false,
190
- 'icons' => false,
191
- 'active_installs' => false,
192
- 'group' => false,
193
- 'contributors' => false,
194
- ];
195
-
196
- $fields = wp_parse_args( $fields, $default_fields );
197
-
198
- // @todo Set cache.
199
- $plugin_info = plugins_api(
200
- 'plugin_information',
201
- [
202
- 'slug' => $plugin_slug,
203
- 'fields' => $fields,
204
- ]
205
- );
206
-
207
- if ( is_wp_error( $plugin_info ) ) {
208
- return [];
209
- }
210
-
211
- return $plugin_info;
212
- }
213
-
214
- /**
215
- * Get custom list of plugins from Artbees API.
216
- *
217
- * @param array $fields Needed information.
218
- */
219
- public function get_plugins_from_api( $fields = [] ) {
220
-
221
- $plugin_info = [];
222
-
223
- $free_plugins = array_column( $this->get_free_plugins(), 'slug' );
224
-
225
- $response = wp_remote_get(
226
- $this->api_url . 'tools/plugin-custom-list',
227
- [
228
- 'sslverify' => false,
229
- 'headers' =>
230
- [
231
- 'domain' => $_SERVER['SERVER_NAME'],
232
- 'theme-name' => $this->theme_name,
233
- 'list-of-attr' => wp_json_encode( $fields ),
234
- 'from' => 0,
235
- 'count' => 0,
236
- ],
237
- ]
238
- );
239
-
240
- $plugins = json_decode( wp_remote_retrieve_body( $response ), true );
241
- $plugins = $plugins['data'];
242
-
243
- $plugins = array_filter(
244
- $plugins,
245
- function( $v, $k ) use ( $free_plugins ) {
246
- return ! in_array( $v['slug'], $free_plugins, true );
247
- },
248
- ARRAY_FILTER_USE_BOTH
249
- );
250
-
251
- $corrected_plugins_list = [];
252
-
253
- foreach ( $plugins as $key => $plugin ) {
254
- $corrected_plugins_list[ $plugin['slug'] ] = $plugin;
255
- }
256
-
257
- return $corrected_plugins_list;
258
- }
259
-
260
- /**
261
- * Check number of activated plugins in two different groups.
262
- *
263
- * @since 1.9.0
264
- *
265
- * @return bool $threshold Wether we are meeting threshold or not.
266
- */
267
- public function plugins_threshold() {
268
-
269
- $plugins = get_option('active_plugins');
270
- $threshold = [];
271
-
272
- if ( count( $plugins ) >= 20 ) {
273
- $threshold[] = 'num';
274
- }
275
-
276
- $sliders = [
277
- 'LayerSlider/layerslider.php',
278
- 'masterslider/masterslider.php',
279
- 'revslider/revslider.php',
280
- ];
281
-
282
- if ( count( array_intersect( $plugins, $sliders ) ) >= 1 ) {
283
- $threshold[] = 'sliders';
284
- }
285
-
286
- $jet_plugins = [
287
- 'jet-blog/jet-blog.php',
288
- 'jet-elements/jet-elements.php',
289
- 'jet-engine/jet-engine.php',
290
- 'jet-menu/jet-menu.php',
291
- 'jet-popup/jet-popup.php',
292
- 'jet-smart-filters/jet-smart-filters.php',
293
- 'jet-tabs/jet-tabs.php',
294
- 'jet-tricks/jet-tricks.php',
295
- 'jet-woo-builder/jet-woo-builder.php',
296
- ];
297
-
298
- if ( count( array_intersect( $plugins, $jet_plugins ) ) >= 4 ) {
299
- $threshold[] = 'jet-plugins';
300
- }
301
-
302
- return implode( $threshold, ',' );
303
- }
304
-
305
- /**
306
- * Delete plugin information transient to load updated one.
307
- * Usage: add force-check=1 to the URL of control panel.
308
- *
309
- * @since 1.9.0
310
- */
311
- public function cache() {
312
- if ( jupiterx_get( 'force-check', false ) ) {
313
- delete_transient( 'jupiterx_list_of_plugins' );
314
- }
315
- }
316
-
317
- /**
318
- * Update plugin information to add activation, installation and update status to plugin data.
319
- * URL used to add activation/installation URL using TGMPA.
320
- *
321
- * @since 1.9.0
322
- *
323
- * @param array $plugins List of plugins.
324
- */
325
- public function update_plugins_status( $plugins = [] ) {
326
-
327
- foreach ( $plugins as $slug => $plugin ) {
328
-
329
- if ( ! isset( $plugins[ $slug ]['basename'] ) || empty( $plugins[ $slug ]['basename'] ) ) {
330
- $plugins[ $slug ]['basename'] = $this->find_plugin_path( $slug );
331
- }
332
-
333
- $plugins[ $slug ]['update_needed'] = false;
334
- $plugins[ $slug ]['installed'] = false;
335
- $plugins[ $slug ]['active'] = false;
336
- $plugins[ $slug ]['network_active'] = false;
337
- $plugins[ $slug ]['install_disabled'] = false;
338
-
339
- if ( is_plugin_active_for_network( $plugins[ $slug ]['basename'] ) ) {
340
- if ( ! current_user_can( 'manage_network_plugins' ) ) {
341
- unset( $plugins[ $slug ] );
342
- continue;
343
- }
344
-
345
- $plugins[ $slug ]['network_active'] = true;
346
- }
347
-
348
- if ( $this->tgmpa->does_plugin_have_update( $slug ) ) {
349
- $plugins[ $slug ]['update_needed'] = true;
350
- $plugins[ $slug ]['update_url'] = $this->get_tgmpa_action_url( $slug, 'update' );
351
- }
352
-
353
- if ( $this->tgmpa->is_plugin_active( $slug ) ) {
354
- $plugins[ $slug ]['active'] = true;
355
- $plugins[ $slug ]['installed'] = true;
356
- } elseif ( $this->tgmpa->is_plugin_installed( $slug ) ) {
357
- $plugins[ $slug ]['installed'] = true;
358
- }
359
-
360
- if ( ! jupiterx_is_pro() && 'true' === $plugins[ $slug ]['pro'] && ! $plugins[ $slug ]['installed'] ) {
361
- $plugins[ $slug ]['pro'] = true;
362
- } else {
363
- unset( $plugins[ $slug ]['pro'] );
364
- }
365
-
366
- if ( ! $plugins[ $slug ]['installed'] && ( is_multisite() && ! current_user_can( 'manage_network_plugins' ) ) ) {
367
- $plugins[ $slug ]['install_disabled'] = true;
368
- }
369
-
370
- if ( ! $plugins[ $slug ]['installed'] && ! $plugins[ $slug ]['install_disabled'] ) {
371
- $plugins[ $slug ]['url'] = $this->get_tgmpa_action_url( $slug, 'install' );
372
- } elseif ( $plugins[ $slug ]['installed'] && ! $plugins[ $slug ]['active'] ) {
373
- $plugins[ $slug ]['url'] = $this->get_tgmpa_action_url( $slug, 'activate' );
374
- }
375
-
376
- if ( $plugins[ $slug ]['installed'] ) {
377
- $plugin_data = get_plugin_data( trailingslashit( WP_PLUGIN_DIR ) . $this->find_plugin_path( $slug ) );
378
- $plugins[ $slug ]['version'] = $plugin_data['Version'];
379
- }
380
-
381
- }
382
-
383
- return $plugins;
384
- }
385
-
386
- /**
387
- * Get plugin basename by plugin slug.
388
- * Works only for installed plugins.
389
- *
390
- * @since 1.9.0
391
- *
392
- * @param string $plugin_slug
393
- */
394
- public function find_plugin_path( $plugin_slug = '' ) {
395
-
396
- $plugins = get_plugins();
397
- foreach ( $plugins as $plugin_address => $plugin_data ) {
398
-
399
- // Extract slug from address
400
- if ( strlen( $plugin_address ) == basename( $plugin_address ) ) {
401
- $slug = strtolower( str_replace( '.php', '', $plugin_address ) );
402
- } else {
403
- $slug = strtolower( str_replace( '/' . basename( $plugin_address ), '', $plugin_address ) );
404
- }
405
- // Check if slug exists
406
- if ( strtolower( $plugin_slug ) == $slug ) {
407
- return $plugin_address;
408
- }
409
- }
410
-
411
- return false;
412
- }
413
-
414
- /**
415
- * Get installation/activation URL of a plugin using TGMPA.
416
- *
417
- * @since 1.9.0
418
- *
419
- * @param string $slug Plugin slug.
420
- * @param string $action install/activate
421
- */
422
- public function get_tgmpa_action_url( $slug = '', $action = '' ) {
423
- if ( ! in_array( $action, [ 'install', 'activate', 'update' ], true ) ) {
424
- wp_send_json_error( [ 'message' => esc_html__( 'Action is not valid.', 'jupiterx-core' ) ] );
425
- }
426
-
427
- $nonce_url = wp_nonce_url(
428
- add_query_arg(
429
- [
430
- 'plugin' => urlencode( $slug ),
431
- 'tgmpa-' . $action => $action . '-plugin',
432
- ],
433
- admin_url( 'themes.php?page=tgmpa-install-plugins' )
434
- ),
435
- 'tgmpa-' . $action,
436
- 'tgmpa-nonce'
437
- );
438
-
439
- return $nonce_url;
440
- }
441
-
442
- /**
443
- * Deactivate plugin using native WordPress functionalities.
444
- *
445
- * @since 1.9.0
446
- */
447
- public function deactivate() {
448
- if ( ! isset( $_POST['slug'] ) ) {
449
- wp_send_json_error( [ 'message' => esc_html__( 'Can\'t deactivate plugin', 'jupiterx-core' ) ] );
450
- }
451
-
452
- $plugin = $this->find_plugin_path( sanitize_text_field( $_POST['slug'] ) );
453
-
454
- if ( ! current_user_can( 'activate_plugin', $plugin ) ) {
455
- wp_send_json_error( esc_html__( 'Sorry, you are not allowed to deactivate this plugin.', 'jupiterx-core' ) );
456
- }
457
-
458
- deactivate_plugins( $plugin );
459
-
460
- wp_send_json_success( esc_html__( 'Deactivated Successfully.', 'jupiterx-core' ) );
461
- }
462
-
463
- /**
464
- * Check for possible conflicts with Themes & Plugins for a specific plugin.
465
- *
466
- * @since 1.9.0
467
- *
468
- * @return void
469
- */
470
- public function plugin_conflict_checker() {
471
- if ( ! function_exists( 'get_plugins' ) ) {
472
- require_once ABSPATH . 'wp-admin/includes/plugin.php';
473
- }
474
-
475
- $plugin_data = wp_unslash( $_POST['plugin'] );
476
-
477
- if ( empty( $plugin_data ) ) {
478
- wp_json_send_error( __( 'Plugin data is missing', 'jupiterx' ) );
479
- }
480
-
481
- if ( 'wp-repo' === $plugin_data['version'] ) {
482
- $wp_updated_plugins = get_site_transient('update_plugins');
483
-
484
- if (
485
- empty( $wp_updated_plugins ) &&
486
- empty( $wp_updated_plugins->response[ $plugin_data['basename'] ] )
487
- ) {
488
- wp_send_json_success();
489
- }
490
-
491
- $plugin_data['version'] = $wp_updated_plugins
492
- ->response[ $plugin_data['basename'] ]
493
- ->new_version;
494
- }
495
-
496
- $conflicts = jupiterx_get_plugin_conflicts( $plugin_data, get_plugins() );
497
-
498
- if ( count( $conflicts['plugins'] ) > 0 || count( $conflicts['themes'] ) > 0 ) {
499
- wp_send_json_error( $conflicts );
500
- }
501
-
502
- wp_send_json_success();
503
- }
504
- }
505
-
506
- JupiterX_Control_Panel_Install_Plugins::get_instance();
1
+ <?php
2
+ /**
3
+ * This class is responsible to manage all jupiters plugin.
4
+ *
5
+ * @package JupiterX_Core\Control_Panel
6
+ */
7
+
8
+ class JupiterX_Control_Panel_Install_Plugins {
9
+
10
+ protected $tgmpa;
11
+ protected $api_url = 'http://artbees.net/api/v2/';
12
+ protected $theme_name = 'JupiterX';
13
+ private static $instance = null;
14
+
15
+ /**
16
+ * Class constructor.
17
+ *
18
+ * @since 1.9.0
19
+ */
20
+ public function __construct() {
21
+ if ( ! class_exists( 'TGM_Plugin_Activation' ) ) {
22
+ return;
23
+ }
24
+
25
+ $menu_items_access = get_site_option( 'menu_items' );
26
+ if ( is_multisite() && ! isset( $menu_items_access['plugins'] ) && ! current_user_can( 'manage_network_plugins' ) ) {
27
+ return;
28
+ }
29
+
30
+ $this->tgmpa = isset( $GLOBALS['tgmpa'] ) ? $GLOBALS['tgmpa'] : TGM_Plugin_Activation::get_instance();
31
+
32
+ add_filter( 'jupiterx_control_panel_pane_install_plugins', [ $this, 'view' ] );
33
+
34
+ add_action( 'wp_ajax_abb_get_plugins', [ $this, 'get_plugins_for_frontend' ] );
35
+ add_action( 'wp_ajax_abb_deactivate_plugin', [ $this, 'deactivate' ] );
36
+ add_action( 'wp_ajax_abb_update_plugin_checker', [ $this, 'plugin_conflict_checker' ] );
37
+ }
38
+
39
+ /**
40
+ * Load view from Jupiter X Core plugin.
41
+ *
42
+ * @since 1.9.0
43
+ */
44
+ public function view() {
45
+ return jupiterx_core()->plugin_dir() . 'includes/control-panel/views/install-plugins.php';
46
+ }
47
+
48
+ /**
49
+ * Get class instance.
50
+ *
51
+ * @since 1.9.0
52
+ */
53
+ public static function get_instance() {
54
+ if ( ! self::$instance ) {
55
+ self::$instance = new self;
56
+ }
57
+
58
+ return self::$instance;
59
+ }
60
+
61
+ public function is_update_available() {
62
+ $plugins = $this->get_all_plugins();
63
+ $plugins = $this->update_plugins_status( $plugins );
64
+
65
+ foreach ( $plugins as $plugin => $data ) {
66
+ if ( $data['update_needed'] ) {
67
+ return true;
68
+ }
69
+ }
70
+ return false;
71
+ }
72
+
73
+ /**
74
+ * Send a json list of plugins and their data and activation limit status for front-end usage.
75
+ *
76
+ * @since 1.9.0
77
+ */
78
+ public function get_plugins_for_frontend() {
79
+ $plugins = $this->get_all_plugins();
80
+ $plugins = $this->update_plugins_status( $plugins );
81
+
82
+ ksort( $plugins );
83
+
84
+ $limit = $this->plugins_threshold();
85
+
86
+ return wp_send_json( [ 'plugins' => $plugins, 'limit' => $limit ] );
87
+ }
88
+
89
+ /**
90
+ * Get full list of plugins.
91
+ * 12h cache.
92
+ *
93
+ * @since 1.9.0
94
+ */
95
+ public function get_all_plugins() {
96
+ $plugins = $this->get_free_plugins() + $this->get_pro_plugins();
97
+
98
+ return $plugins;
99
+ }
100
+
101
+ /**
102
+ * Get list of pro plugins from Artbees API.
103
+ *
104
+ * @since 1.9.0
105
+ */
106
+ public function get_pro_plugins() {
107
+ return $this->get_plugins_from_api( [ 'slug', 'basename', 'version', 'name', 'desc', 'more_link', 'img_url', 'required', 'pro', 'is_callable' ] );
108
+ }
109
+
110
+ /**
111
+ * Filter tgmpa plugins to extract list of free plugins.
112
+ *
113
+ * @since 1.9.0
114
+ */
115
+ public function get_free_plugins_raw() {
116
+ return array_filter(
117
+ $this->tgmpa->plugins,
118
+ function( $plugin_info, $plugin ) {
119
+ return 'repo' === $plugin_info['source'];
120
+ },
121
+ ARRAY_FILTER_USE_BOTH
122
+ );
123
+ }
124
+
125
+ /**
126
+ * Get list of free plugins.
127
+ * List gets update to add needed info like icon and version from WordPress repo.
128
+ *
129
+ * @since 1.9.0
130
+ */
131
+ public function get_free_plugins() {
132
+ $plugins = $this->get_free_plugins_raw();
133
+
134
+ foreach ( $plugins as $plugin => $plugin_info ) {
135
+ $wp_plugin_info = $this->get_plugin_info_from_wp( $plugin, [ 'icons' => true, 'short_description' => true ] );
136
+
137
+ $plugins[ $plugin ]['version'] = $wp_plugin_info->version;
138
+ $plugins[ $plugin ]['desc'] = $wp_plugin_info->short_description;
139
+ $plugins[ $plugin ]['img_url'] = isset( $wp_plugin_info->icons['1x'] ) ? $wp_plugin_info->icons['1x'] : $wp_plugin_info->icons['default'];
140
+ }
141
+
142
+ return $plugins;
143
+ }
144
+
145
+ /**
146
+ * Get a list of slugs of free plugins.
147
+ *
148
+ * @since 1.9.0
149
+ */
150
+ public function get_free_plugins_slug() {
151
+ $free_plugins = $this->get_free_plugins();
152
+
153
+ return array_column( $plugins, 'slug' );
154
+ }
155
+
156
+ /**
157
+ * Get detail of given plugin from WordPress repo.
158
+ * Used to get version, icon and plugin description.
159
+ *
160
+ * @since 1.9.0
161
+ *
162
+ * @param string $plugin_slug Plugin slug from plugin header.
163
+ * @param array $fields Needed extra information.
164
+ */
165
+ public function get_plugin_info_from_wp( $plugin_slug = '', $fields = [] ) {
166
+
167
+ if ( empty( $plugin_slug ) ) {
168
+ return;
169
+ }
170
+
171
+ if ( ! function_exists( 'plugins_api' ) ) {
172
+ require_once ABSPATH . 'wp-admin/includes/plugin-install.php';
173
+ }
174
+
175
+ $default_fields = [
176
+ 'short_description' => false,
177
+ 'description' => false,
178
+ 'sections' => false,
179
+ 'tested' => false,
180
+ 'requires' => false,
181
+ 'rating' => false,
182
+ 'downloaded' => false,
183
+ 'downloadlink' => false,
184
+ 'last_updated' => false,
185
+ 'added' => false,
186
+ 'tags' => false,
187
+ 'compatibility' => false,
188
+ 'homepage' => false,
189
+ 'versions' => false,
190
+ 'donate_link' => false,
191
+ 'reviews' => false,
192
+ 'banners' => false,
193
+ 'icons' => false,
194
+ 'active_installs' => false,
195
+ 'group' => false,
196
+ 'contributors' => false,
197
+ ];
198
+
199
+ $fields = wp_parse_args( $fields, $default_fields );
200
+
201
+ $plugin_info = plugins_api(
202
+ 'plugin_information',
203
+ [
204
+ 'slug' => $plugin_slug,
205
+ 'fields' => $fields,
206
+ ]
207
+ );
208
+
209
+ if ( is_wp_error( $plugin_info ) ) {
210
+ return [];
211
+ }
212
+
213
+ return $plugin_info;
214
+ }
215
+
216
+ /**
217
+ * Get custom list of plugins from Artbees API.
218
+ *
219
+ * @param array $fields Needed information.
220
+ */
221
+ public function get_plugins_from_api( $fields = [] ) {
222
+ $free_plugins = array_column( $this->get_free_plugins(), 'slug' );
223
+
224
+ $response = wp_remote_get(
225
+ $this->api_url . 'tools/plugin-custom-list',
226
+ [
227
+ 'sslverify' => false,
228
+ 'headers' =>
229
+ [
230
+ 'domain' => $_SERVER['SERVER_NAME'],
231
+ 'theme-name' => $this->theme_name,
232
+ 'list-of-attr' => wp_json_encode( $fields ),
233
+ 'from' => 0,
234
+ 'count' => 0,
235
+ ],
236
+ ]
237
+ );
238
+
239
+ $plugins = json_decode( wp_remote_retrieve_body( $response ), true );
240
+ $plugins = $plugins['data'];
241
+
242
+ $plugins = array_filter(
243
+ $plugins,
244
+ function( $v, $k ) use ( $free_plugins ) {
245
+ return ! in_array( $v['slug'], $free_plugins, true );
246
+ },
247
+ ARRAY_FILTER_USE_BOTH
248
+ );
249
+
250
+ $corrected_plugins_list = [];
251
+
252
+ foreach ( $plugins as $key => $plugin ) {
253
+ $corrected_plugins_list[ $plugin['slug'] ] = $plugin;
254
+ }
255
+
256
+ return $corrected_plugins_list;
257
+ }
258
+
259
+ /**
260
+ * Check number of activated plugins in two different groups.
261
+ *
262
+ * @since 1.9.0
263
+ *
264
+ * @return bool $threshold Wether we are meeting threshold or not.
265
+ */
266
+ public function plugins_threshold() {
267
+
268
+ $plugins = get_option('active_plugins');
269
+ $threshold = [];
270
+
271
+ if ( count( $plugins ) >= 20 ) {
272
+ $threshold[] = 'num';
273
+ }
274
+
275
+ $sliders = [
276
+ 'LayerSlider/layerslider.php',
277
+ 'masterslider/masterslider.php',
278
+ 'revslider/revslider.php',
279
+ ];
280
+
281
+ if ( count( array_intersect( $plugins, $sliders ) ) >= 1 ) {
282
+ $threshold[] = 'sliders';
283
+ }
284
+
285
+ $jet_plugins = [
286
+ 'jet-blog/jet-blog.php',
287
+ 'jet-elements/jet-elements.php',
288
+ 'jet-engine/jet-engine.php',
289
+ 'jet-menu/jet-menu.php',
290
+ 'jet-popup/jet-popup.php',
291
+ 'jet-smart-filters/jet-smart-filters.php',
292
+ 'jet-tabs/jet-tabs.php',
293
+ 'jet-tricks/jet-tricks.php',
294
+ 'jet-woo-builder/jet-woo-builder.php',
295
+ ];
296
+
297
+ if ( count( array_intersect( $plugins, $jet_plugins ) ) >= 4 ) {
298
+ $threshold[] = 'jet-plugins';
299
+ }
300
+
301
+ return implode( $threshold, ',' );
302
+ }
303
+
304
+ /**
305
+ * Update plugin information to add activation, installation and update status to plugin data.
306
+ * URL used to add activation/installation URL using TGMPA.
307
+ *
308
+ * @since 1.9.0
309
+ *
310
+ * @param array $plugins List of plugins.
311
+ */
312
+ public function update_plugins_status( $plugins = [] ) {
313
+
314
+ foreach ( $plugins as $slug => $plugin ) {
315
+
316
+ if ( ! isset( $plugins[ $slug ]['basename'] ) || empty( $plugins[ $slug ]['basename'] ) ) {
317
+ $plugins[ $slug ]['basename'] = $this->find_plugin_path( $slug );
318
+ }
319
+
320
+ $plugins[ $slug ]['update_needed'] = false;
321
+ $plugins[ $slug ]['installed'] = false;
322
+ $plugins[ $slug ]['active'] = false;
323
+ $plugins[ $slug ]['network_active'] = false;
324
+ $plugins[ $slug ]['install_disabled'] = false;
325
+
326
+ if ( is_plugin_active_for_network( $plugins[ $slug ]['basename'] ) ) {
327
+ if ( ! current_user_can( 'manage_network_plugins' ) ) {
328
+ unset( $plugins[ $slug ] );
329
+ continue;
330
+ }
331
+
332
+ $plugins[ $slug ]['network_active'] = true;
333
+ }
334
+
335
+ if ( $this->tgmpa->is_plugin_active( $slug ) ) {
336
+ $plugins[ $slug ]['active'] = true;
337
+ $plugins[ $slug ]['installed'] = true;
338
+ } elseif ( $this->tgmpa->is_plugin_installed( $slug ) ) {
339
+ $plugins[ $slug ]['installed'] = true;
340
+ }
341
+
342
+ if ( ! jupiterx_is_pro() && 'true' === $plugins[ $slug ]['pro'] && ! $plugins[ $slug ]['installed'] ) {
343
+ $plugins[ $slug ]['pro'] = true;
344
+ } else {
345
+ unset( $plugins[ $slug ]['pro'] );
346
+ }
347
+
348
+ if ( ! $plugins[ $slug ]['installed'] && ( is_multisite() && ! current_user_can( 'manage_network_plugins' ) ) ) {
349
+ $plugins[ $slug ]['install_disabled'] = true;
350
+ }
351
+
352
+ if ( ! $plugins[ $slug ]['installed'] && ! $plugins[ $slug ]['install_disabled'] ) {
353
+ $plugins[ $slug ]['url'] = $this->get_tgmpa_action_url( $slug, 'install' );
354
+ } else {
355
+ $plugins[ $slug ]['url'] = $this->get_tgmpa_action_url( $slug, 'activate' );
356
+ }
357
+
358
+ if ( $plugins[ $slug ]['installed'] ) {
359
+ $plugin_data = get_plugin_data( trailingslashit( WP_PLUGIN_DIR ) . $this->find_plugin_path( $slug ) );
360
+ $plugins[ $slug ]['version'] = $plugin_data['Version'];
361
+
362
+ if ( $this->tgmpa->does_plugin_have_update( $slug ) ) {
363
+ $plugins[ $slug ]['update_needed'] = true;
364
+ $plugins[ $slug ]['update_url'] = $this->get_tgmpa_action_url( $slug, 'update' );
365
+ }
366
+ }
367
+ }
368
+
369
+ return $plugins;
370
+ }
371
+
372
+ /**
373
+ * Get plugin basename by plugin slug.
374
+ * Works only for installed plugins.
375
+ *
376
+ * @since 1.9.0
377
+ *
378
+ * @param string $plugin_slug
379
+ */
380
+ public function find_plugin_path( $plugin_slug = '' ) {
381
+
382
+ $plugins = get_plugins();
383
+ foreach ( $plugins as $plugin_address => $plugin_data ) {
384
+
385
+ // Extract slug from address
386
+ if ( strlen( $plugin_address ) == basename( $plugin_address ) ) {
387
+ $slug = strtolower( str_replace( '.php', '', $plugin_address ) );
388
+ } else {
389
+ $slug = strtolower( str_replace( '/' . basename( $plugin_address ), '', $plugin_address ) );
390
+ }
391
+ // Check if slug exists
392
+ if ( strtolower( $plugin_slug ) == $slug ) {
393
+ return $plugin_address;
394
+ }
395
+ }
396
+
397
+ return false;
398
+ }
399
+
400
+ /**
401
+ * Get installation/activation URL of a plugin using TGMPA.
402
+ *
403
+ * @since 1.9.0
404
+ *
405
+ * @param string $slug Plugin slug.
406
+ * @param string $action install/activate
407
+ */
408
+ public function get_tgmpa_action_url( $slug = '', $action = '' ) {
409
+ if ( ! in_array( $action, [ 'install', 'activate', 'update' ], true ) ) {
410
+ wp_send_json_error( [ 'message' => esc_html__( 'Action is not valid.', 'jupiterx-core' ) ] );
411
+ }
412
+
413
+ $nonce_url = wp_nonce_url(
414
+ add_query_arg(
415
+ [
416
+ 'plugin' => urlencode( $slug ),
417
+ 'tgmpa-' . $action => $action . '-plugin',
418
+ ],
419
+ admin_url( 'themes.php?page=tgmpa-install-plugins' )
420
+ ),
421
+ 'tgmpa-' . $action,
422
+ 'tgmpa-nonce'
423
+ );
424
+
425
+ return $nonce_url;
426
+ }
427
+
428
+ /**
429
+ * Deactivate plugin using native WordPress functionalities.
430
+ *
431
+ * @since 1.9.0
432
+ */
433
+ public function deactivate() {
434
+ if ( ! isset( $_POST['slug'] ) ) {
435
+ wp_send_json_error( [ 'message' => esc_html__( 'Can\'t deactivate plugin', 'jupiterx-core' ) ] );
436
+ }
437
+
438
+ $plugin = $this->find_plugin_path( sanitize_text_field( $_POST['slug'] ) );
439
+
440
+ if ( ! current_user_can( 'activate_plugin', $plugin ) ) {
441
+ wp_send_json_error( esc_html__( 'Sorry, you are not allowed to deactivate this plugin.', 'jupiterx-core' ) );
442
+ }
443
+
444
+ deactivate_plugins( $plugin );
445
+
446
+ wp_send_json_success( esc_html__( 'Deactivated Successfully.', 'jupiterx-core' ) );
447
+ }
448
+
449
+ /**
450
+ * Check for possible conflicts with Themes & Plugins for a specific plugin.
451
+ *
452
+ * @since 1.9.0
453
+ *
454
+ * @return void
455
+ */
456
+ public function plugin_conflict_checker() {
457
+ if ( ! function_exists( 'get_plugins' ) ) {
458
+ require_once ABSPATH . 'wp-admin/includes/plugin.php';
459
+ }
460
+
461
+ $plugin_data = wp_unslash( $_POST['plugin'] );
462
+
463
+ if ( empty( $plugin_data ) ) {
464
+ wp_json_send_error( __( 'Plugin data is missing', 'jupiterx' ) );
465
+ }
466
+
467
+ if ( 'wp-repo' === $plugin_data['version'] ) {
468
+ $wp_updated_plugins = get_site_transient('update_plugins');
469
+
470
+ if (
471
+ empty( $wp_updated_plugins ) &&
472
+ empty( $wp_updated_plugins->response[ $plugin_data['basename'] ] )
473
+ ) {
474
+ wp_send_json_success();
475
+ }
476
+
477
+ $plugin_data['version'] = $wp_updated_plugins
478
+ ->response[ $plugin_data['basename'] ]
479
+ ->new_version;
480
+ }
481
+
482
+ $conflicts = jupiterx_get_plugin_conflicts( $plugin_data, get_plugins() );
483
+
484
+ if ( count( $conflicts['plugins'] ) > 0 || count( $conflicts['themes'] ) > 0 ) {
485
+ wp_send_json_error( $conflicts );
486
+ }
487
+
488
+ wp_send_json_success();
489
+ }
490
+ }
491
+
492
+ JupiterX_Control_Panel_Install_Plugins::get_instance();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
includes/control-panel/includes/class-install-template.php CHANGED
@@ -270,6 +270,9 @@ if ( ! class_exists( 'JupiterX_Control_Panel_Install_Template' ) ) {
270
  case 'activate_plugins':
271
  $this->activateRequiredPlugins( $template_name );
272
  break;
 
 
 
273
  case 'theme_content':
274
  $this->importThemeContent( $template_name, $import_media, $partial_import );
275
  break;
@@ -674,29 +677,20 @@ if ( ! class_exists( 'JupiterX_Control_Panel_Install_Template' ) ) {
674
  public function activateRequiredPlugins( $template_name ) {
675
 
676
  $template_settings = $this->getSettingsData( $template_name );
677
- $actions = [];
678
- $tgmpa_url = $this->tgmpa->get_tgmpa_url();
679
  $template_plugins = $template_settings['options']['jupiterx_support_plugins'];
680
 
681
- $template_plugins = array_diff( $template_plugins, ['jupiterx-pro', 'advanced-custom-fields-pro'] );
682
-
683
  $template_plugins[] = 'advanced-custom-fields';
684
 
685
- $actions['activate'] = [
686
- 'url' => $tgmpa_url,
687
- 'plugin' => $template_plugins,
688
- 'tgmpa-page' => $this->tgmpa->menu,
689
- 'plugin_status' => 'all',
690
- '_wpnonce' => wp_create_nonce( 'bulk-plugins' ),
691
- 'action' => 'tgmpa-bulk-activate',
692
- 'action2' => - 1,
693
- 'message' => esc_html__( 'Activating', 'jupiterx-core' ),
694
- ];
695
-
696
- $actions['url'] = $tgmpa_url;
697
- $actions['status'] = true;
698
 
699
- wp_send_json( $actions );
 
 
700
  }
701
 
702
 
@@ -1658,7 +1652,7 @@ if ( ! class_exists( 'JupiterX_Control_Panel_Install_Template' ) ) {
1658
  public function get_template_psd_link() {
1659
  $template_name = sanitize_text_field( $_POST['template_name'] );
1660
  try {
1661
- $response = $this->getTemplateDownloadLink( $template_name, 'download-psd' );
1662
  $this->message(
1663
  'Successfull', true, array(
1664
  'psd_link' => $response,
@@ -1778,6 +1772,36 @@ if ( ! class_exists( 'JupiterX_Control_Panel_Install_Template' ) ) {
1778
  return true;
1779
  }
1780
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1781
  /**
1782
  * Reusable wrapper method for WP remote getter.
1783
  *
270
  case 'activate_plugins':
271
  $this->activateRequiredPlugins( $template_name );
272
  break;
273
+ case 'custom_tables':
274
+ $this->import_custom_tables( $template_name );
275
+ break;
276
  case 'theme_content':
277
  $this->importThemeContent( $template_name, $import_media, $partial_import );
278
  break;
677
  public function activateRequiredPlugins( $template_name ) {
678
 
679
  $template_settings = $this->getSettingsData( $template_name );
 
 
680
  $template_plugins = $template_settings['options']['jupiterx_support_plugins'];
681
 
682
+ $template_plugins = array_diff( $template_plugins, [ 'jupiterx-pro', 'advanced-custom-fields-pro' ] );
 
683
  $template_plugins[] = 'advanced-custom-fields';
684
 
685
+ foreach ( $template_plugins as $slug ) {
686
+ if ( isset( $this->tgmpa->plugins[ $slug ] ) ) {
687
+ activate_plugin( $this->tgmpa->plugins[ $slug ]['file_path'] );
688
+ }
689
+ }
 
 
 
 
 
 
 
 
690
 
691
+ wp_send_json( [
692
+ 'status' => true,
693
+ ] );
694
  }
695
 
696
 
1652
  public function get_template_psd_link() {
1653
  $template_name = sanitize_text_field( $_POST['template_name'] );
1654
  try {
1655
+ $response = $this->getTemplateDownloadLink( $template_name . ' jupiterx', 'download-psd' );
1656
  $this->message(
1657
  'Successfull', true, array(
1658
  'psd_link' => $response,
1772
  return true;
1773
  }
1774
 
1775
+ /**
1776
+ * Import templates's custom tables.
1777
+ *
1778
+ * @since 1.11.0
1779
+ *
1780
+ * @param string $template_name Template name.
1781
+ */
1782
+ public function import_custom_tables( $template_name ) {
1783
+ $this->reinitializeData( $template_name );
1784
+
1785
+ try {
1786
+ $template_name = sanitize_title( $template_name );
1787
+ $import_path = $this->getBasePath() . $template_name;
1788
+ $file = $import_path . '/tables.sql';
1789
+ $db_manager = new JupiterX_Control_Panel_PHP_DB_Manager();
1790
+
1791
+ if ( file_exists( $file ) ) {
1792
+ $import_tables = $db_manager->import_tables( $file );
1793
+
1794
+ if ( $import_tables !== true ) {
1795
+ throw new Exception( $import_tables );
1796
+ }
1797
+ }
1798
+
1799
+ $this->message( 'Custom tables are imported.', true );
1800
+ } catch ( Exception $e ) {
1801
+ $this->message( $e->getMessage(), false );
1802
+ }
1803
+ }
1804
+
1805
  /**
1806
  * Reusable wrapper method for WP remote getter.
1807
  *
includes/control-panel/includes/class-settings.php CHANGED
@@ -1,118 +1,118 @@
1
- <?php
2
- /**
3
- * Settings API: JupiterX_Control_Panel_Settings base class
4
- *
5
- * @package JupiterX_Core\Framework\Control_Panel\Settings
6
- *
7
- * @since 1.4.0
8
- */
9
-
10
- if ( ! class_exists( 'JupiterX_Control_Panel_Settings' ) ) {
11
- /**
12
- * Settings.
13
- *
14
- * @since 1.4.0
15
- */
16
- class JupiterX_Control_Panel_Settings {
17
-
18
- /**
19
- * Constructor.
20
- *
21
- * @since 1.4.0
22
- */
23
- public function __construct() {
24
- add_filter( 'jupiterx_control_panel_pane_settings', [ $this, 'view' ] );
25
- add_action( 'wp_ajax_jupiterx_cp_settings', [ $this, 'ajax_handler' ] );
26
- }
27
-
28
- /**
29
- * Settings HTML path.
30
- *
31
- * @since 1.4.0
32
- *
33
- * @return string
34
- */
35
- public function view() {
36
- return jupiterx_core()->plugin_dir() . 'includes/control-panel/views/settings.php';
37
- }
38
-
39
- /**
40
- * Map the requests to proper methods.
41
- *
42
- * @since 1.4.0
43
- */
44
- public function ajax_handler() {
45
- check_ajax_referer( 'jupiterx_control_panel', 'nonce' );
46
-
47
- $type = jupiterx_post( 'type' );
48
-
49
- if ( ! $type ) {
50
- wp_send_json_error(
51
- __( 'Type param is missing.', 'jupiterx-core' )
52
- );
53
- }
54
-
55
- if ( 'flush' === $type ) {
56
- $this->flush();
57
- }
58
-
59
- if ( 'save' === $type ) {
60
- $this->save();
61
- }
62
-
63
- wp_send_json_error(
64
- /* translators: Function request type to initialize. */
65
- sprintf( esc_html__( 'Type param (%s) is not valid.', 'jupiterx-core' ), $type )
66
- );
67
- }
68
-
69
- /**
70
- * Flush assets cache.
71
- *
72
- * @since 1.4.0
73
- */
74
- public function flush() {
75
-
76
- jupiterx_core_flush_cache();
77
-
78
- wp_send_json_success( esc_html__( 'Assets flushed successfully.', 'jupiterx-core' ) );
79
- }
80
-
81
- /**
82
- * Save settings.
83
- *
84
- * @since 1.4.0
85
- */
86
- public function save() {
87
- $fields = jupiterx_post( 'fields' );
88
-
89
- if ( ! $fields ) {
90
- wp_send_json_error( esc_html__( 'Fields param is missing.', 'jupiterx-core' ) );
91
- }
92
-
93
- if ( ! jupiterx_is_pro() ) {
94
- $pro_fields = [
95
- 'jupiterx_adobe_fonts_project_id',
96
- 'jupiterx_tracking_codes_after_head',
97
- 'jupiterx_tracking_codes_before_head',
98
- 'jupiterx_tracking_codes_after_body',
99
- 'jupiterx_tracking_codes_before_body',
100
- ];
101
-
102
- foreach ( $pro_fields as $name ) {
103
- unset( $fields[ $name ] );
104
- }
105
- }
106
-
107
- foreach ( $fields as $name => $value ) {
108
- $name = preg_replace( '/(jupiterx|artbees)_/', '', $name, 1 );
109
- jupiterx_update_option( $name, $value );
110
- }
111
-
112
- wp_send_json_success( esc_html__( 'Settings saved successfully.', 'jupiterx-core' ) );
113
- }
114
-
115
- }
116
-
117
- new JupiterX_Control_Panel_Settings();
118
- }
1
+ <?php
2
+ /**
3
+ * Settings API: JupiterX_Control_Panel_Settings base class
4
+ *
5
+ * @package JupiterX_Core\Framework\Control_Panel\Settings
6
+ *
7
+ * @since 1.4.0
8
+ */
9
+
10
+ if ( ! class_exists( 'JupiterX_Control_Panel_Settings' ) ) {
11
+ /**
12
+ * Settings.
13
+ *
14
+ * @since 1.4.0
15
+ */
16
+ class JupiterX_Control_Panel_Settings {
17
+
18
+ /**
19
+ * Constructor.
20
+ *
21
+ * @since 1.4.0
22
+ */
23
+ public function __construct() {
24
+ add_filter( 'jupiterx_control_panel_pane_settings', [ $this, 'view' ] );
25
+ add_action( 'wp_ajax_jupiterx_cp_settings', [ $this, 'ajax_handler' ] );
26
+ }
27
+
28
+ /**
29
+ * Settings HTML path.
30
+ *
31
+ * @since 1.4.0
32
+ *
33
+ * @return string
34
+ */
35
+ public function view() {
36
+ return jupiterx_core()->plugin_dir() . 'includes/control-panel/views/settings.php';
37
+ }
38
+
39
+ /**
40
+ * Map the requests to proper methods.
41
+ *
42
+ * @since 1.4.0
43
+ */
44
+ public function ajax_handler() {
45
+ check_ajax_referer( 'jupiterx_control_panel', 'nonce' );
46
+
47
+ $type = jupiterx_post( 'type' );
48
+
49
+ if ( ! $type ) {
50
+ wp_send_json_error(
51
+ __( 'Type param is missing.', 'jupiterx-core' )
52
+ );
53
+ }
54
+
55
+ if ( 'flush' === $type ) {
56
+ $this->flush();
57
+ }
58
+
59
+ if ( 'save' === $type ) {
60
+ $this->save();
61
+ }
62
+
63
+ wp_send_json_error(
64
+ /* translators: Function request type to initialize. */
65
+ sprintf( esc_html__( 'Type param (%s) is not valid.', 'jupiterx-core' ), $type )
66
+ );
67
+ }
68
+
69
+ /**
70
+ * Flush assets cache.
71
+ *
72
+ * @since 1.4.0
73
+ */
74
+ public function flush() {
75
+
76
+ jupiterx_core_flush_cache();
77
+
78
+ wp_send_json_success( esc_html__( 'Assets flushed successfully.', 'jupiterx-core' ) );
79
+ }
80
+
81
+ /**
82
+ * Save settings.
83
+ *
84
+ * @since 1.4.0
85
+ */
86
+ public function save() {
87
+ $fields = jupiterx_post( 'fields' );
88
+
89
+ if ( ! $fields ) {
90
+ wp_send_json_error( esc_html__( 'Fields param is missing.', 'jupiterx-core' ) );
91
+ }
92
+
93
+ if ( ! jupiterx_is_pro() ) {
94
+ $pro_fields = [
95
+ 'jupiterx_adobe_fonts_project_id',
96
+ 'jupiterx_tracking_codes_after_head',
97
+ 'jupiterx_tracking_codes_before_head',
98
+ 'jupiterx_tracking_codes_after_body',
99
+ 'jupiterx_tracking_codes_before_body',
100
+ ];
101
+
102
+ foreach ( $pro_fields as $name ) {
103
+ unset( $fields[ $name ] );
104
+ }
105
+ }
106
+
107
+ foreach ( $fields as $name => $value ) {
108
+ $name = preg_replace( '/(jupiterx|artbees)_/', '', $name, 1 );
109
+ jupiterx_update_option( $name, $value );
110
+ }
111
+
112
+ wp_send_json_success( esc_html__( 'Settings saved successfully.', 'jupiterx-core' ) );
113
+ }
114
+
115
+ }
116
+
117
+ new JupiterX_Control_Panel_Settings();
118
+ }
includes/control-panel/includes/class-system-status.php CHANGED
@@ -1,378 +1,378 @@
1
- <?php
2
- /**
3
- * This class provides the list of system status values
4
- *
5
- * @package JupiterX\Framework\Admin
6
- *
7
- * @since 1.0.0
8
- */
9
-
10
- /**
11
- * Show list of system critical data
12
- *
13
- * @since 1.0.0
14
- * @ignore
15
- * @access private
16
- */
17
- if ( ! class_exists( 'JupiterX_Control_Panel_System_Status' ) ) {
18
- class JupiterX_Control_Panel_System_Status {
19
-
20
-
21
- /**
22
- * JupiterX_Control_Panel_System_Status constructor.
23
- *
24
- * @since 1.0.0
25
- */
26
- public function __construct() {
27
- add_filter( 'jupiterx_control_panel_pane_system_status', [ $this, 'view' ] );
28
- add_action( 'wp_ajax_jupiterx_cp_system_status', [ $this, 'ajax_handler' ] );
29
- add_action( 'wp_ajax_jupiterx_cp_cleanup_mods', [ $this, 'cleanup_mods' ] );
30
- }
31
-
32
- /**
33
- * Load system status.
34
- *
35
- * @since 1.9.0
36
- *
37
- * @return string
38
- */
39
- public function view() {
40
- return jupiterx_core()->plugin_dir() . 'includes/control-panel/views/system-status.php';
41
- }
42
-
43
- /**
44
- * Clean up theme mods data and languages specific theme mods.
45
- * This will removes all unwanted data (integer keys) from theme mods in multiple requests to avoid timeout.
46
- *
47
- * @since 1.4.0
48
- *
49
- * @return void
50
- */
51
- public function cleanup_mods() {
52
- $theme_slug = get_option( 'stylesheet' );
53
-
54
- if ( ! wp_verify_nonce( $_POST['nonce'], 'jupiterx_mods_cleanup' ) ) {
55
- wp_send_json_error( [ 'message' => __( 'Nonce can\'t be verified', 'jupiterx-core' ) ] );
56
- }
57
-
58
- $mods = get_option( 'theme_mods_' . $theme_slug );
59
- $mods_size = sizeof( $mods );
60
-
61
- $i = 0; // Index.
62
- $j = 0; // Numeric keys.
63
- foreach ( $mods as $key => $value ) {
64
- if ( is_numeric( $key ) ) {
65
- unset( $mods[ $key ] );
66
- $j++;
67
- }
68
-
69
- $i++;
70
- // Remove bunch of unwanted data.
71
- if ( $j >= 1000 ) {
72
- update_option( 'theme_mods_' . $theme_slug, $mods );
73
- wp_send_json_success();
74
- // If unwanted data length is less than 1000, remove all of them.
75
- } elseif ( $i >= $mods_size ) {
76
- update_option( 'theme_mods_' . $theme_slug, $mods );
77
- }
78
- }
79
-
80
- $multilingual = new CoreCustomizerMultilingual();
81
- $theme_slug = get_option( 'template' );
82
- $option_prefix = str_replace( '-', '_', $theme_slug );
83
- $option_name = $option_prefix . $multilingual::get_option_key();
84
- $languages = $multilingual::get_languages_list();
85
- $k = 0; // Index.
86
- $l = 0; // Numeric keys.
87
- foreach ( $languages as $language ) {
88
- $lang_option_name = $option_name . $language['slug'];
89
- $lang_option = get_option( $lang_option_name );
90
- $lang_mods = isset( $lang_option['mods'] ) ? $lang_option['mods'] : [];
91
- $lang_mode_size = sizeof( $lang_mods );
92
-
93
- foreach ( $lang_mods as $key => $value ) {
94
- if ( is_numeric( $key ) ) {
95
- unset( $lang_mods[ $key ] );
96
- $l++;
97
- }
98
-
99
- $k++;
100
- // Remove bunch of unwanted data.
101
- if ( $l >= 1000 ) {
102
- $lang_option['mods'] = $lang_mods;
103
- update_option( $lang_option_name, $lang_option );
104
- wp_send_json_success();
105
- // If unwanted data length is less than 1000, remove all of them.
106
- } elseif( $k >= $lang_mode_size ) {
107
- $lang_option['mods'] = $lang_mods;
108
- update_option( $lang_option_name, $lang_option );
109
- wp_send_json_error();
110
- }
111
- }
112
- }
113
-
114
- wp_send_json_error();
115
- }
116
-
117
- /**
118
- * Handles AJAX requests.
119
- *
120
- * @since 1.3.0
121
- */
122
- public function ajax_handler() {
123
- check_ajax_referer( 'jupiterx_control_panel', 'nonce' );
124
-
125
- $type = jupiterx_post( 'type' );
126
-
127
- if ( ! $type ) {
128
- wp_send_json_error( esc_html__( 'Type param is missing.', 'jupiterx-core' ) );
129
- }
130
-
131
- $this->$type();
132
-
133
- wp_send_json_error(
134
- sprintf( esc_html__( 'Type param (%s) is not valid.', 'jupiterx-core' ), $type )
135
- );
136
- }
137
-
138
- /**
139
- * Checks whether HTTP requests are blocked.
140
- *
141
- * @see test_http_requests() in Health Check plugin.
142
- * @since 1.3.0
143
- */
144
- private function http_requests() {
145
- $blocked = false;
146
- $hosts = [];
147
-
148
- if ( defined( 'WP_HTTP_BLOCK_EXTERNAL' ) ) {
149
- $blocked = true;
150
- }
151
-
152
- if ( defined( 'WP_ACCESSIBLE_HOSTS' ) ) {
153
- $hosts = explode( ',', WP_ACCESSIBLE_HOSTS );
154
- }
155
-
156
- if ( $blocked && 0 === sizeof( $hosts ) ) {
157
- wp_send_json_error( esc_html__( 'HTTP requests have been blocked by the WP_HTTP_BLOCK_EXTERNAL constant, with no allowed hosts.', 'jupiterx-core' ) );
158
- }
159
-
160
- if ( $blocked && 0 < sizeof( $hosts ) ) {
161
- wp_send_json_error(
162
- sprintf(
163
- esc_html__( 'HTTP requests have been blocked by the WP_HTTP_BLOCK_EXTERNAL constant, with some hosts whitelisted: %s.', 'jupiterx-core' ),
164
- implode( ',', $hosts )
165
- )
166
- );
167
- }
168
-
169
- if ( ! $blocked ) {
170
- wp_send_json_success();
171
- }
172
- }
173
-
174
- /**
175
- * Checks whether artbees.net is accessible.
176
- *
177
- * @since 1.3.0
178
- */
179
- private function artbees_server() {
180
- $response = wp_remote_get( 'https://artbees.net', array(
181
- 'timeout' => 10,
182
- ) );
183
-
184
- if ( is_wp_error( $response ) ) {
185
- wp_send_json_error( $response->get_error_message() );
186
- }
187
-
188
- wp_send_json_success();
189
- }
190
-
191
- /**
192
- * Create an array of system status
193
- *
194
- * @since 1.0.0
195
- *
196
- * @return array
197
- */
198
- public static function compile_system_status() {
199
- global $wpdb;
200
-
201
- $sysinfo = array();
202
- $upload_dir = wp_upload_dir();
203
-
204
- $sysinfo['home_url'] = esc_url( home_url( '/' ) );
205
- $sysinfo['site_url'] = esc_url( site_url( '/' ) );
206
-
207
- $sysinfo['wp_content_url'] = WP_CONTENT_URL;
208
- $sysinfo['wp_upload_dir'] = $upload_dir['basedir'];
209
- $sysinfo['wp_upload_url'] = $upload_dir['baseurl'];
210
- $sysinfo['wp_ver'] = get_bloginfo( 'version' );
211
- $sysinfo['wp_multisite'] = is_multisite();
212
- $sysinfo['permalink_structure'] = get_option( 'permalink_structure' ) ? get_option( 'permalink_structure' ) : 'Default';
213
- $sysinfo['front_page_display'] = get_option( 'show_on_front' );
214
- if ( 'page' == $sysinfo['front_page_display'] ) {
215
- $front_page_id = get_option( 'page_on_front' );
216
- $blog_page_id = get_option( 'page_for_posts' );
217
-
218
- $sysinfo['front_page'] = 0 != $front_page_id ? get_the_title( $front_page_id ) . ' (#' . $front_page_id . ')' : 'Unset';
219
- $sysinfo['posts_page'] = 0 != $blog_page_id ? get_the_title( $blog_page_id ) . ' (#' . $blog_page_id . ')' : 'Unset';
220
- }
221
-
222
- $sysinfo['wp_mem_limit']['raw'] = JupiterX_Control_Panel_Helpers::let_to_num( WP_MEMORY_LIMIT );
223
- $sysinfo['wp_mem_limit']['size'] = size_format( $sysinfo['wp_mem_limit']['raw'] );
224
-
225
- $sysinfo['db_table_prefix'] = 'Length: ' . strlen( $wpdb->prefix ) . ' - Status: ' . (strlen( $wpdb->prefix ) > 16 ? 'ERROR: Too long' : 'Acceptable');
226
-
227
- $sysinfo['wp_debug'] = 'false';
228
- if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
229
- $sysinfo['wp_debug'] = 'true';
230
- }
231
-
232
- $sysinfo['wp_lang'] = get_locale();
233
-
234
- $sysinfo['wp_writable'] = get_home_path();
235
- $sysinfo['wp_content_writable'] = WP_CONTENT_DIR;
236
- $sysinfo['wp_uploads_writable'] = $sysinfo['wp_upload_dir'];
237
- $sysinfo['wp_plugins_writable'] = WP_PLUGIN_DIR;
238
- $sysinfo['wp_themes_writable'] = get_theme_root();
239
-
240
- if ( ! class_exists( 'Browser' ) ) {
241
- jupiterx_core()->load_files( [
242
- 'control-panel/includes/class-browser',
243
- ] );
244
- }
245
-
246
- $browser = new Browser();
247
-
248
- $sysinfo['browser'] = array(
249
- 'agent' => $browser->getUserAgent(),
250
- 'browser' => $browser->getBrowser(),
251
- 'version' => $browser->getVersion(),
252
- 'platform' => $browser->getPlatform(),
253
- );
254
-
255
- $sysinfo['server_info'] = esc_html( $_SERVER['SERVER_SOFTWARE'] );
256
- $sysinfo['localhost'] = JupiterX_Control_Panel_Helpers::make_bool_string( JupiterX_Control_Panel_Helpers::is_localhost() );
257
- $sysinfo['php_ver'] = function_exists( 'phpversion' ) ? esc_html( phpversion() ) : 'phpversion() function does not exist.';
258
- $sysinfo['abspath'] = ABSPATH;
259
-
260
- if ( function_exists( 'ini_get' ) ) {
261
- $sysinfo['php_mem_limit']['raw'] = JupiterX_Control_Panel_Helpers::let_to_num( ini_get( 'memory_limit' ) );
262
- $sysinfo['php_mem_limit']['size'] = size_format( $sysinfo['php_mem_limit']['raw'] );
263
- $sysinfo['php_post_max_size'] = size_format( JupiterX_Control_Panel_Helpers::let_to_num( ini_get( 'post_max_size' ) ) );
264
- $sysinfo['php_time_limit'] = ini_get( 'max_execution_time' );
265
- $sysinfo['php_max_input_var'] = ini_get( 'max_input_vars' );
266
- $sysinfo['suhosin_request_max_vars'] = ini_get( 'suhosin.request.max_vars' );
267
- $sysinfo['suhosin_post_max_vars'] = ini_get( 'suhosin.post.max_vars' );
268
- $sysinfo['php_display_errors'] = JupiterX_Control_Panel_Helpers::make_bool_string( ini_get( 'display_errors' ) );
269
- }
270
-
271
- $sysinfo['suhosin_installed'] = extension_loaded( 'suhosin' );
272
- $sysinfo['mysql_ver'] = $wpdb->db_version();
273
- $sysinfo['max_upload_size'] = size_format( wp_max_upload_size() );
274
-
275
- $sysinfo['def_tz_is_utc'] = 'true';
276
- if ( date_default_timezone_get() !== 'UTC' ) {
277
- $sysinfo['def_tz_is_utc'] = 'false';
278
- }
279
-
280
- $sysinfo['fsockopen_curl'] = 'false';
281
- if ( function_exists( 'fsockopen' ) || function_exists( 'curl_init' ) ) {
282
- $sysinfo['fsockopen_curl'] = 'true';
283
- }
284
-
285
- $sysinfo['soap_client'] = 'false';
286
- if ( class_exists( 'SoapClient' ) ) {
287
- $sysinfo['soap_client'] = 'true';
288
- }
289
-
290
- $sysinfo['dom_document'] = 'false';
291
- if ( class_exists( 'DOMDocument' ) ) {
292
- $sysinfo['dom_document'] = 'true';
293
- }
294
-
295
- $sysinfo['gzip'] = 'false';
296
- if ( is_callable( 'gzopen' ) ) {
297
- $sysinfo['gzip'] = 'true';
298
- }
299
-
300
- $sysinfo['mbstring'] = 'false';
301
-
302
- if ( extension_loaded( 'mbstring' ) && function_exists( 'mb_eregi' ) && function_exists( 'mb_ereg_match' ) ) {
303
- $sysinfo['mbstring'] = 'true';
304
- }
305
-
306
- $sysinfo['simplexml'] = 'false';
307
-
308
- if ( class_exists( 'SimpleXMLElement' ) && function_exists( 'simplexml_load_string' ) ) {
309
- $sysinfo['simplexml'] = 'true';
310
- }
311
-
312
- $sysinfo['phpxml'] = 'false';
313
-
314
- if ( function_exists( 'xml_parse' ) ) {
315
- $sysinfo['phpxml'] = 'true';
316
- }
317
-
318
- $active_plugins = (array) get_option( 'active_plugins', array() );
319
-
320
- if ( is_multisite() ) {
321
- $active_plugins = array_merge( $active_plugins, get_site_option( 'active_sitewide_plugins', array() ) );
322
- }
323
-
324
- $sysinfo['plugins'] = array();
325
-
326
- foreach ( $active_plugins as $plugin ) {
327
- $plugin_data = @get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin );
328
- $plugin_name = esc_html( $plugin_data['Name'] );
329
-
330
- $sysinfo['plugins'][ $plugin_name ] = $plugin_data;
331
- }
332
-
333
- $active_theme = wp_get_theme();
334
-
335
- $sysinfo['theme']['name'] = $active_theme->Name;
336
- $sysinfo['theme']['version'] = $active_theme->Version;
337
- $sysinfo['theme']['author_uri'] = $active_theme->{'Author URI'};
338
- $sysinfo['theme']['is_child'] = JupiterX_Control_Panel_Helpers::make_bool_string( is_child_theme() );
339
-
340
- if ( is_child_theme() ) {
341
- $parent_theme = wp_get_theme( $active_theme->Template );
342
-
343
- $sysinfo['theme']['parent_name'] = $parent_theme->Name;
344
- $sysinfo['theme']['parent_version'] = $parent_theme->Version;
345
- $sysinfo['theme']['parent_author_uri'] = $parent_theme->{'Author URI'};
346
- }
347
-
348
- return $sysinfo;
349
- }
350
-
351
- /**
352
- * Create an array of system status warnings.
353
- *
354
- * @since 1.9.0
355
- *
356
- * @return array
357
- */
358
- public static function compile_system_status_warnings() {
359
- $helper = JupiterX_Control_Panel_Helpers::class;
360
- $sysinfo = self::compile_system_status();
361
- $warnings = [];
362
- $link = '<a href="https://themes.artbees.net/docs/jupiter-x-server-requirements/" target="_blank">' . __( 'Read More', 'jupiterx-core' ) . '</a>';
363
-
364
- if ( $helper::bytes_to_mb( $sysinfo['wp_mem_limit']['raw'] ) < 256 ) {
365
- $warnings['wp_mem_limit']['message'] = __( 'Insufficient memory. You need at least 256MB of memory.', 'jupiterx-core' );
366
- $warnings['wp_mem_limit']['message'] .= $link;
367
- }
368
-
369
- if ( $helper::bytes_to_mb( $sysinfo['php_mem_limit']['raw'] ) < 256 ) {
370
- $warnings['php_mem_limit']['message'] = __( 'Insufficient memory. You need at least 256MB of memory.', 'jupiterx-core' );
371
- $warnings['php_mem_limit']['message'] .= $link;
372
- }
373
-
374
- return $warnings;
375
- }
376
- }
377
- new JupiterX_Control_Panel_System_Status();
378
- }
1
+ <?php
2
+ /**
3
+ * This class provides the list of system status values
4
+ *
5
+ * @package JupiterX\Framework\Admin
6
+ *
7
+ * @since 1.0.0
8
+ */
9
+
10
+ /**
11
+ * Show list of system critical data
12
+ *
13
+ * @since 1.0.0
14
+ * @ignore
15
+ * @access private
16
+ */
17
+ if ( ! class_exists( 'JupiterX_Control_Panel_System_Status' ) ) {
18
+ class JupiterX_Control_Panel_System_Status {
19
+
20
+
21
+ /**
22
+ * JupiterX_Control_Panel_System_Status constructor.
23
+ *
24
+ * @since 1.0.0
25
+ */
26
+ public function __construct() {
27
+ add_filter( 'jupiterx_control_panel_pane_system_status', [ $this, 'view' ] );
28
+ add_action( 'wp_ajax_jupiterx_cp_system_status', [ $this, 'ajax_handler' ] );
29
+ add_action( 'wp_ajax_jupiterx_cp_cleanup_mods', [ $this, 'cleanup_mods' ] );
30
+ }
31
+
32
+ /**
33
+ * Load system status.
34
+ *
35
+ * @since 1.9.0
36
+ *
37
+ * @return string
38
+ */
39
+ public function view() {
40
+ return jupiterx_core()->plugin_dir() . 'includes/control-panel/views/system-status.php';
41
+ }
42
+
43
+ /**
44
+ * Clean up theme mods data and languages specific theme mods.
45
+ * This will removes all unwanted data (integer keys) from theme mods in multiple requests to avoid timeout.
46
+ *
47
+ * @since 1.4.0
48
+ *
49
+ * @return void
50
+ */
51
+ public function cleanup_mods() {
52
+ $theme_slug = get_option( 'stylesheet' );
53
+
54
+ if ( ! wp_verify_nonce( $_POST['nonce'], 'jupiterx_mods_cleanup' ) ) {
55
+ wp_send_json_error( [ 'message' => __( 'Nonce can\'t be verified', 'jupiterx-core' ) ] );
56
+ }
57
+
58
+ $mods = get_option( 'theme_mods_' . $theme_slug );
59
+ $mods_size = sizeof( $mods );
60
+
61
+ $i = 0; // Index.
62
+ $j = 0; // Numeric keys.
63
+ foreach ( $mods as $key => $value ) {
64
+ if ( is_numeric( $key ) ) {
65
+ unset( $mods[ $key ] );
66
+ $j++;
67
+ }
68
+
69
+ $i++;
70
+ // Remove bunch of unwanted data.
71
+ if ( $j >= 1000 ) {
72
+ update_option( 'theme_mods_' . $theme_slug, $mods );
73
+ wp_send_json_success();
74
+ // If unwanted data length is less than 1000, remove all of them.
75
+ } elseif ( $i >= $mods_size ) {
76
+ update_option( 'theme_mods_' . $theme_slug, $mods );
77
+ }
78
+ }
79
+
80
+ $multilingual = new CoreCustomizerMultilingual();
81
+ $theme_slug = get_option( 'template' );
82
+ $option_prefix = str_replace( '-', '_', $theme_slug );
83
+ $option_name = $option_prefix . $multilingual::get_option_key();
84
+ $languages = $multilingual::get_languages_list();
85
+ $k = 0; // Index.
86
+ $l = 0; // Numeric keys.
87
+ foreach ( $languages as $language ) {
88
+ $lang_option_name = $option_name . $language['slug'];
89
+ $lang_option = get_option( $lang_option_name );
90
+ $lang_mods = isset( $lang_option['mods'] ) ? $lang_option['mods'] : [];
91
+ $lang_mode_size = sizeof( $lang_mods );
92
+
93
+ foreach ( $lang_mods as $key => $value ) {
94
+ if ( is_numeric( $key ) ) {
95
+ unset( $lang_mods[ $key ] );
96
+ $l++;
97
+ }
98
+
99
+ $k++;
100
+ // Remove bunch of unwanted data.
101
+ if ( $l >= 1000 ) {
102
+ $lang_option['mods'] = $lang_mods;
103
+ update_option( $lang_option_name, $lang_option );
104
+ wp_send_json_success();
105
+ // If unwanted data length is less than 1000, remove all of them.
106
+ } elseif( $k >= $lang_mode_size ) {
107
+ $lang_option['mods'] = $lang_mods;
108
+ update_option( $lang_option_name, $lang_option );
109
+ wp_send_json_error();
110
+ }
111
+ }
112
+ }
113
+
114
+ wp_send_json_error();
115
+ }
116
+
117
+ /**
118
+ * Handles AJAX requests.
119
+ *
120
+ * @since 1.3.0
121
+ */
122
+ public function ajax_handler() {
123
+ check_ajax_referer( 'jupiterx_control_panel', 'nonce' );
124
+
125
+ $type = jupiterx_post( 'type' );
126
+
127
+ if ( ! $type ) {
128
+ wp_send_json_error( esc_html__( 'Type param is missing.', 'jupiterx-core' ) );
129
+ }
130
+
131
+ $this->$type();
132
+
133
+ wp_send_json_error(
134
+ sprintf( esc_html__( 'Type param (%s) is not valid.', 'jupiterx-core' ), $type )
135
+ );
136
+ }
137
+
138
+ /**
139
+ * Checks whether HTTP requests are blocked.
140
+ *
141
+ * @see test_http_requests() in Health Check plugin.
142
+ * @since 1.3.0
143
+ */
144
+ private function http_requests() {
145
+ $blocked = false;
146
+ $hosts = [];
147
+
148
+ if ( defined( 'WP_HTTP_BLOCK_EXTERNAL' ) ) {
149
+ $blocked = true;
150
+ }
151
+
152
+ if ( defined( 'WP_ACCESSIBLE_HOSTS' ) ) {
153
+ $hosts = explode( ',', WP_ACCESSIBLE_HOSTS );
154
+ }
155
+
156
+ if ( $blocked && 0 === sizeof( $hosts ) ) {
157
+ wp_send_json_error( esc_html__( 'HTTP requests have been blocked by the WP_HTTP_BLOCK_EXTERNAL constant, with no allowed hosts.', 'jupiterx-core' ) );
158
+ }
159
+
160
+ if ( $blocked && 0 < sizeof( $hosts ) ) {
161
+ wp_send_json_error(
162
+ sprintf(
163
+ esc_html__( 'HTTP requests have been blocked by the WP_HTTP_BLOCK_EXTERNAL constant, with some hosts whitelisted: %s.', 'jupiterx-core' ),
164
+ implode( ',', $hosts )
165
+ )
166
+ );
167
+ }
168
+
169
+ if ( ! $blocked ) {
170
+ wp_send_json_success();
171
+ }
172
+ }
173
+
174
+ /**
175
+ * Checks whether artbees.net is accessible.
176
+ *
177
+ * @since 1.3.0
178
+ */
179
+ private function artbees_server() {
180
+ $response = wp_remote_get( 'https://artbees.net', array(
181
+ 'timeout' => 10,
182
+ ) );
183
+
184
+ if ( is_wp_error( $response ) ) {
185
+ wp_send_json_error( $response->get_error_message() );
186
+ }
187
+
188
+ wp_send_json_success();
189
+ }
190
+
191
+ /**
192
+ * Create an array of system status
193
+ *
194
+ * @since 1.0.0
195
+ *
196
+ * @return array
197
+ */
198
+ public static function compile_system_status() {
199
+ global $wpdb;
200
+
201
+ $sysinfo = array();
202
+ $upload_dir = wp_upload_dir();
203
+
204
+ $sysinfo['home_url'] = esc_url( home_url( '/' ) );
205
+ $sysinfo['site_url'] = esc_url( site_url( '/' ) );
206
+
207
+ $sysinfo['wp_content_url'] = WP_CONTENT_URL;
208
+ $sysinfo['wp_upload_dir'] = $upload_dir['basedir'];
209
+ $sysinfo['wp_upload_url'] = $upload_dir['baseurl'];
210
+ $sysinfo['wp_ver'] = get_bloginfo( 'version' );
211
+ $sysinfo['wp_multisite'] = is_multisite();
212
+ $sysinfo['permalink_structure'] = get_option( 'permalink_structure' ) ? get_option( 'permalink_structure' ) : 'Default';
213
+ $sysinfo['front_page_display'] = get_option( 'show_on_front' );
214
+ if ( 'page' == $sysinfo['front_page_display'] ) {
215
+ $front_page_id = get_option( 'page_on_front' );
216
+ $blog_page_id = get_option( 'page_for_posts' );
217
+
218
+ $sysinfo['front_page'] = 0 != $front_page_id ? get_the_title( $front_page_id ) . ' (#' . $front_page_id . ')' : 'Unset';
219
+ $sysinfo['posts_page'] = 0 != $blog_page_id ? get_the_title( $blog_page_id ) . ' (#' . $blog_page_id . ')' : 'Unset';
220
+ }
221
+
222
+ $sysinfo['wp_mem_limit']['raw'] = JupiterX_Control_Panel_Helpers::let_to_num( WP_MEMORY_LIMIT );
223
+ $sysinfo['wp_mem_limit']['size'] = size_format( $sysinfo['wp_mem_limit']['raw'] );
224
+
225
+ $sysinfo['db_table_prefix'] = 'Length: ' . strlen( $wpdb->prefix ) . ' - Status: ' . (strlen( $wpdb->prefix ) > 16 ? 'ERROR: Too long' : 'Acceptable');
226
+
227
+ $sysinfo['wp_debug'] = 'false';
228
+ if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
229
+ $sysinfo['wp_debug'] = 'true';
230
+ }
231
+
232
+ $sysinfo['wp_lang'] = get_locale();
233
+
234
+ $sysinfo['wp_writable'] = get_home_path();
235
+ $sysinfo['wp_content_writable'] = WP_CONTENT_DIR;
236
+ $sysinfo['wp_uploads_writable'] = $sysinfo['wp_upload_dir'];
237
+ $sysinfo['wp_plugins_writable'] = WP_PLUGIN_DIR;
238
+ $sysinfo['wp_themes_writable'] = get_theme_root();
239
+
240
+ if ( ! class_exists( 'Browser' ) ) {
241
+ jupiterx_core()->load_files( [
242
+ 'control-panel/includes/class-browser',
243
+ ] );
244
+ }
245
+
246
+ $browser = new Browser();
247
+
248
+ $sysinfo['browser'] = array(
249
+ 'agent' => $browser->getUserAgent(),
250
+ 'browser' => $browser->getBrowser(),
251
+ 'version' => $browser->getVersion(),
252
+ 'platform' => $browser->getPlatform(),
253
+ );
254
+
255
+ $sysinfo['server_info'] = esc_html( $_SERVER['SERVER_SOFTWARE'] );
256
+ $sysinfo['localhost'] = JupiterX_Control_Panel_Helpers::make_bool_string( JupiterX_Control_Panel_Helpers::is_localhost() );
257
+ $sysinfo['php_ver'] = function_exists( 'phpversion' ) ? esc_html( phpversion() ) : 'phpversion() function does not exist.';
258
+ $sysinfo['abspath'] = ABSPATH;
259
+
260
+ if ( function_exists( 'ini_get' ) ) {
261
+ $sysinfo['php_mem_limit']['raw'] = JupiterX_Control_Panel_Helpers::let_to_num( ini_get( 'memory_limit' ) );
262
+ $sysinfo['php_mem_limit']['size'] = size_format( $sysinfo['php_mem_limit']['raw'] );
263
+ $sysinfo['php_post_max_size'] = size_format( JupiterX_Control_Panel_Helpers::let_to_num( ini_get( 'post_max_size' ) ) );
264
+ $sysinfo['php_time_limit'] = ini_get( 'max_execution_time' );
265
+ $sysinfo['php_max_input_var'] = ini_get( 'max_input_vars' );
266
+ $sysinfo['suhosin_request_max_vars'] = ini_get( 'suhosin.request.max_vars' );
267
+ $sysinfo['suhosin_post_max_vars'] = ini_get( 'suhosin.post.max_vars' );
268
+ $sysinfo['php_display_errors'] = JupiterX_Control_Panel_Helpers::make_bool_string( ini_get( 'display_errors' ) );
269
+ }
270
+
271
+ $sysinfo['suhosin_installed'] = extension_loaded( 'suhosin' );
272
+ $sysinfo['mysql_ver'] = $wpdb->db_version();
273
+ $sysinfo['max_upload_size'] = size_format( wp_max_upload_size() );
274
+
275
+ $sysinfo['def_tz_is_utc'] = 'true';
276
+ if ( date_default_timezone_get() !== 'UTC' ) {
277
+ $sysinfo['def_tz_is_utc'] = 'false';
278
+ }
279
+
280
+ $sysinfo['fsockopen_curl'] = 'false';
281
+ if ( function_exists( 'fsockopen' ) || function_exists( 'curl_init' ) ) {
282
+ $sysinfo['fsockopen_curl'] = 'true';
283
+ }
284
+
285
+ $sysinfo['soap_client'] = 'false';
286
+ if ( class_exists( 'SoapClient' ) ) {
287
+ $sysinfo['soap_client'] = 'true';
288
+ }
289
+
290
+ $sysinfo['dom_document'] = 'false';
291
+ if ( class_exists( 'DOMDocument' ) ) {
292
+ $sysinfo['dom_document'] = 'true';
293
+ }
294
+
295
+ $sysinfo['gzip'] = 'false';
296
+ if ( is_callable( 'gzopen' ) ) {
297
+ $sysinfo['gzip'] = 'true';
298
+ }
299
+
300
+ $sysinfo['mbstring'] = 'false';
301
+
302
+ if ( extension_loaded( 'mbstring' ) && function_exists( 'mb_eregi' ) && function_exists( 'mb_ereg_match' ) ) {
303
+ $sysinfo['mbstring'] = 'true';
304
+ }
305
+
306
+ $sysinfo['simplexml'] = 'false';
307
+
308
+ if ( class_exists( 'SimpleXMLElement' ) && function_exists( 'simplexml_load_string' ) ) {
309
+ $sysinfo['simplexml'] = 'true';
310
+ }
311
+
312
+ $sysinfo['phpxml'] = 'false';
313
+
314
+ if ( function_exists( 'xml_parse' ) ) {
315
+ $sysinfo['phpxml'] = 'true';
316
+ }
317
+
318
+ $active_plugins = (array) get_option( 'active_plugins', array() );
319
+
320
+ if ( is_multisite() ) {
321
+ $active_plugins = array_merge( $active_plugins, get_site_option( 'active_sitewide_plugins', array() ) );
322
+ }
323
+
324
+ $sysinfo['plugins'] = array();
325
+
326
+ foreach ( $active_plugins as $plugin ) {
327
+ $plugin_data = @get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin );
328
+ $plugin_name = esc_html( $plugin_data['Name'] );
329
+
330
+ $sysinfo['plugins'][ $plugin_name ] = $plugin_data;
331
+ }
332
+
333
+ $active_theme = wp_get_theme();
334
+
335
+ $sysinfo['theme']['name'] = $active_theme->Name;
336
+ $sysinfo['theme']['version'] = $active_theme->Version;
337
+ $sysinfo['theme']['author_uri'] = $active_theme->{'Author URI'};
338
+ $sysinfo['theme']['is_child'] = JupiterX_Control_Panel_Helpers::make_bool_string( is_child_theme() );
339
+
340
+ if ( is_child_theme() ) {
341
+ $parent_theme = wp_get_theme( $active_theme->Template );
342
+
343
+ $sysinfo['theme']['parent_name'] = $parent_theme->Name;
344
+ $sysinfo['theme']['parent_version'] = $parent_theme->Version;
345
+ $sysinfo['theme']['parent_author_uri'] = $parent_theme->{'Author URI'};
346
+ }
347
+
348
+ return $sysinfo;
349
+ }
350
+
351
+ /**
352
+ * Create an array of system status warnings.
353
+ *
354
+ * @since 1.9.0
355
+ *
356
+ * @return array
357
+ */
358
+ public static function compile_system_status_warnings() {
359
+ $helper = JupiterX_Control_Panel_Helpers::class;
360
+ $sysinfo = self::compile_system_status();
361
+ $warnings = [];
362
+ $link = '<a href="https://themes.artbees.net/docs/jupiter-x-server-requirements/" target="_blank">' . __( 'Read More', 'jupiterx-core' ) . '</a>';
363
+
364
+ if ( $helper::bytes_to_mb( $sysinfo['wp_mem_limit']['raw'] ) < 256 ) {
365
+ $warnings['wp_mem_limit']['message'] = __( 'Insufficient memory. You need at least 256MB of memory.', 'jupiterx-core' );
366
+ $warnings['wp_mem_limit']['message'] .= $link;
367
+ }
368
+
369
+ if ( $helper::bytes_to_mb( $sysinfo['php_mem_limit']['raw'] ) < 256 ) {
370
+ $warnings['php_mem_limit']['message'] = __( 'Insufficient memory. You need at least 256MB of memory.', 'jupiterx-core' );
371
+ $warnings['php_mem_limit']['message'] .= $link;
372
+ }
373
+
374
+ return $warnings;
375
+ }
376
+ }
377
+ new JupiterX_Control_Panel_System_Status();
378
+ }
includes/control-panel/includes/class-validator.php CHANGED
@@ -1,166 +1,166 @@
1
- <?php
2
- if ( ! class_exists( 'JupiterX_Control_Panel_Validator' ) ) {
3
- /**
4
- * Control panel validator class.
5
- *
6
- * @since 1.9.0
7
- */
8
- class JupiterX_Control_Panel_Validator {
9
-
10
- private $value;
11
- public function setValue( $value = '' ) {
12
- $this->value = $value;
13
- return $this;
14
- }
15
- public function getValue() {
16
- return $this->value;
17
- }
18
-
19
- private $field_name;
20
- public function setFieldName( $field_name = '' ) {
21
- $this->field_name = $field_name;
22
- return $this;
23
- }
24
- public function getFieldName() {
25
- return $this->field_name;
26
- }
27
-
28
- private $message;
29
- public function setMessage( $message, $append = true, $param = '' ) {
30
- $message = str_replace( '{param}', $param, str_replace( '{field_name}', $this->getFieldName(), $message ) );
31
- if ( $append == true ) {
32
- $this->message .= $message;
33
- } else {
34
- $this->message = $message;
35
- }
36
- return $this;
37
- }
38
- public function getMessage() {
39
- return $this->message;
40
- }
41
- public function clearMessage() {
42
- $this->message = '';
43
- return $this;
44
- }
45
- public function checkErrorExistence() {
46
- if ( $this->message == '' ) {
47
- return false;
48
- } else {
49
- return true;
50
- }
51
- }
52
- public function run( $config ) {
53
- $this->clearMessage();
54
-
55
- $field_name = $this->getFieldName();
56
- if ( empty( $field_name ) == true || $field_name == '' ) {
57
- $this->setMessage( 'You must pass a field name for executing validation', false );
58
- return false;
59
- }
60
-
61
- preg_match_all( '/(.*?):\s?(.*?)(,|$)/', $config, $validators );
62
- $validators = array_combine( array_map( 'trim', $validators[1] ), $validators[2] );
63
-
64
- if ( is_array( $validators ) === false || count( $validators ) == 0 ) {
65
- $this->setMessage( 'You must pass an string of validations' );
66
- return false;
67
- }
68
-
69
- if ( array_key_exists( 'required', $validators ) ) {
70
- $this->requiredCheck();
71
- }
72
-
73
- if ( array_key_exists( 'string', $validators ) ) {
74
- $this->stringCheck();
75
- }
76
-
77
- if ( array_key_exists( 'int', $validators ) ) {
78
- $this->intCheck();
79
- }
80
-
81
- if ( array_key_exists( 'min_len', $validators ) ) {
82
- $this->minLenCheck( $validators['min_len'] );
83
- }
84
-
85
- if ( array_key_exists( 'max_len', $validators ) ) {
86
- $this->maxLenCheck( $validators['max_len'] );
87
- }
88
-
89
- if ( array_key_exists( 'exact_len', $validators ) ) {
90
- $this->exactLenCheck( $validators['exact_len'] );
91
- }
92
-
93
- if ( array_key_exists( 'array', $validators ) ) {
94
- $this->arrayCheck();
95
- }
96
-
97
- if ( $this->checkErrorExistence() ) {
98
- return false;
99
- } else {
100
- return true;
101
- }
102
- }
103
-
104
- private function requiredCheck() {
105
- $value = $this->getValue();
106
- if ( empty( $value ) == true || $value == '' ) {
107
- $this->setMessage( 'The {field_name} field is required.' );
108
- return false;
109
- }
110
- return true;
111
- }
112
-
113
- private function arrayCheck() {
114
- $value = $this->getValue();
115
- if ( is_array( $value ) === false || count( $value ) < 1 ) {
116
- $this->setMessage( 'The {field_name} field must be an array with at least one element.' );
117
- return false;
118
- }
119
- return true;
120
- }
121
-
122
- private function stringCheck() {
123
- $value = $this->getValue();
124
- if ( is_string( $value ) === false ) {
125
- $this->setMessage( 'The {field_name} field must have an string value.' );
126
- return false;
127
- }
128
- return true;
129
- }
130
-
131
- private function intCheck() {
132
- $value = $this->getValue();
133
- if ( is_int( $value ) == false ) {
134
- $this->setMessage( 'The {field_name} field must contain an integer.' );
135
- return false;
136
- }
137
- return true;
138
- }
139
-
140
- private function minLenCheck( $min_len ) {
141
- $value = $this->getValue();
142
- if ( strlen( $value ) < $min_len ) {
143
- $this->setMessage( 'The {field_name} field must be at least {param} characters in length.', true, $min_len );
144
- return false;
145
- }
146
- return true;
147
- }
148
- private function maxLenCheck( $max_len ) {
149
- $value = $this->getValue();
150
- if ( strlen( $value ) > $max_len ) {
151
- $this->setMessage( 'The {field_name} field cannot exceed {param} characters in length.', true, $max_len );
152
- return false;
153
- }
154
- return true;
155
- }
156
-
157
- private function exactLenCheck( $exact_len ) {
158
- $value = $this->getValue();
159
- if ( strlen( $value ) != $exact_len ) {
160
- $this->setMessage( 'The {field_name} field must be exactly {param} characters in length.', true, $exact_len );
161
- return false;
162
- }
163
- return true;
164
- }
165
- }
166
- }
1
+ <?php
2
+ if ( ! class_exists( 'JupiterX_Control_Panel_Validator' ) ) {
3
+ /**
4
+ * Control panel validator class.
5
+ *
6
+ * @since 1.9.0
7
+ */
8
+ class JupiterX_Control_Panel_Validator {
9
+
10
+ private $value;
11
+ public function setValue( $value = '' ) {
12
+ $this->value = $value;
13
+ return $this;
14
+ }
15
+ public function getValue() {
16
+ return $this->value;
17
+ }
18
+
19
+ private $field_name;
20
+ public function setFieldName( $field_name = '' ) {
21
+ $this->field_name = $field_name;
22
+ return $this;
23
+ }
24
+ public function getFieldName() {
25
+ return $this->field_name;
26
+ }
27
+
28
+ private $message;
29
+ public function setMessage( $message, $append = true, $param = '' ) {
30
+ $message = str_replace( '{param}', $param, str_replace( '{field_name}', $this->getFieldName(), $message ) );
31
+ if ( $append == true ) {
32
+ $this->message .= $message;
33
+ } else {
34
+ $this->message = $message;
35
+ }
36
+ return $this;
37
+ }
38
+ public function getMessage() {
39
+ return $this->message;
40
+ }
41
+ public function clearMessage() {
42
+ $this->message = '';
43
+ return $this;
44
+ }
45
+ public function checkErrorExistence() {
46
+ if ( $this->message == '' ) {
47
+ return false;
48
+ } else {
49
+ return true;
50
+ }
51
+ }
52
+ public function run( $config ) {
53
+ $this->clearMessage();
54
+
55
+ $field_name = $this->getFieldName();
56
+ if ( empty( $field_name ) == true || $field_name == '' ) {
57
+ $this->setMessage( 'You must pass a field name for executing validation', false );
58
+ return false;
59
+ }
60
+
61
+ preg_match_all( '/(.*?):\s?(.*?)(,|$)/', $config, $validators );
62
+ $validators = array_combine( array_map( 'trim', $validators[1] ), $validators[2] );
63
+
64
+ if ( is_array( $validators ) === false || count( $validators ) == 0 ) {
65
+ $this->setMessage( 'You must pass an string of validations' );
66
+ return false;
67
+ }
68
+
69
+ if ( array_key_exists( 'required', $validators ) ) {
70
+ $this->requiredCheck();
71
+ }
72
+
73
+ if ( array_key_exists( 'string', $validators ) ) {
74
+ $this->stringCheck();
75
+ }
76
+
77
+ if ( array_key_exists( 'int', $validators ) ) {
78
+ $this->intCheck();
79
+ }
80
+
81
+ if ( array_key_exists( 'min_len', $validators ) ) {
82
+ $this->minLenCheck( $validators['min_len'] );
83
+ }
84
+
85
+ if ( array_key_exists( 'max_len', $validators ) ) {
86
+ $this->maxLenCheck( $validators['max_len'] );
87
+ }
88
+
89
+ if ( array_key_exists( 'exact_len', $validators ) ) {
90
+ $this->exactLenCheck( $validators['exact_len'] );
91
+ }
92
+
93
+ if ( array_key_exists( 'array', $validators ) ) {
94
+ $this->arrayCheck();
95
+ }
96
+
97
+ if ( $this->checkErrorExistence() ) {
98
+ return false;
99
+ } else {
100
+ return true;
101
+ }
102
+ }
103
+
104
+ private function requiredCheck() {
105
+ $value = $this->getValue();
106
+ if ( empty( $value ) == true || $value == '' ) {
107
+ $this->setMessage( 'The {field_name} field is required.' );
108
+ return false;
109
+ }
110
+ return true;
111
+ }
112
+
113
+ private function arrayCheck() {
114
+ $value = $this->getValue();
115
+ if ( is_array( $value ) === false || count( $value ) < 1 ) {
116
+ $this->setMessage( 'The {field_name} field must be an array with at least one element.' );
117
+ return false;
118
+ }
119
+ return true;
120
+ }
121
+
122
+ private function stringCheck() {
123
+ $value = $this->getValue();
124
+ if ( is_string( $value ) === false ) {
125
+ $this->setMessage( 'The {field_name} field must have an string value.' );
126
+ return false;
127
+ }
128
+ return true;
129
+ }
130
+
131
+ private function intCheck() {
132
+ $value = $this->getValue();
133
+ if ( is_int( $value ) == false ) {
134
+ $this->setMessage( 'The {field_name} field must contain an integer.' );
135
+ return false;
136
+ }
137
+ return true;
138
+ }
139
+
140
+ private function minLenCheck( $min_len ) {
141
+ $value = $this->getValue();
142
+ if ( strlen( $value ) < $min_len ) {
143
+ $this->setMessage( 'The {field_name} field must be at least {param} characters in length.', true, $min_len );
144
+ return false;
145
+ }
146
+ return true;
147
+ }
148
+ private function maxLenCheck( $max_len ) {
149
+ $value = $this->getValue();
150
+ if ( strlen( $value ) > $max_len ) {
151
+ $this->setMessage( 'The {field_name} field cannot exceed {param} characters in length.', true, $max_len );
152
+ return false;
153
+ }
154
+ return true;
155
+ }
156
+
157
+ private function exactLenCheck( $exact_len ) {
158
+ $value = $this->getValue();
159
+ if ( strlen( $value ) != $exact_len ) {
160
+ $this->setMessage( 'The {field_name} field must be exactly {param} characters in length.', true, $exact_len );
161
+ return false;
162
+ }
163
+ return true;
164
+ }
165
+ }
166
+ }
includes/control-panel/includes/importer/class-jupiterx-importer.php CHANGED
@@ -1,155 +1,155 @@
1
- <?php
2
- /**
3
- * This class extends JupiterX_WXR_Importer.
4
- *
5
- * @package JupiterX\Framework\Control_Panel\Importer
6
- *
7
- * @since 1.1.0
8
- */
9
-
10
- /**
11
- * JupiterX Importer.
12
- *
13
- * @since 1.1.0
14
- * @package JupiterX\Framework\Control_Panel\Importer
15
- */
16
- class JupiterX_Importer extends JupiterX_WXR_Importer {
17
-
18
- /**
19
- * Term meta.
20
- *
21
- * @var array
22
- */
23
- protected $jupiterx_meta = [];
24
-
25
- /**
26
- * Import pages only.
27
- *
28
- * @var boolean
29
- */
30
- protected $partial_import;
31
-
32
- /**
33
- * Constructor.
34
- *
35
- * @param array $options The JupiterX_WXR_Importer options.
36
- */
37
- public function __construct( $options = [], $partial_import = false ) {
38
- parent::__construct( $options, $partial_import );
39
-
40
- add_action( 'wp_import_insert_term', [ $this, 'jupiterx_process_term_meta' ] );
41
- }
42
-
43
- /**
44
- * Parse term node.
45
- *
46
- * It's a copy from the parent class. There's no way to access `$node` variable through
47
- * actions so it's necessary to override it.
48
- *
49
- * The addition is `_jupiterx_parse_term_meta_node` method call.
50
- *
51
- * @since 1.1.0
52
- */
53
- protected function parse_term_node( $node, $type = 'term' ) {
54
- $data = array();
55
- $meta = array();
56
-
57
- $tag_name = array(
58
- 'id' => 'wp:term_id',
59
- 'taxonomy' => 'wp:term_taxonomy',
60
- 'slug' => 'wp:term_slug',
61
- 'parent' => 'wp:term_parent',
62
- 'name' => 'wp:term_name',
63
- 'description' => 'wp:term_description',
64
- );
65
- $taxonomy = null;
66
-
67
- // Special casing!
68
- switch ( $type ) {
69
- case 'category':
70
- $tag_name['slug'] = 'wp:category_nicename';
71
- $tag_name['parent'] = 'wp:category_parent';
72
- $tag_name['name'] = 'wp:cat_name';
73
- $tag_name['description'] = 'wp:category_description';
74
- $tag_name['taxonomy'] = null;
75
-
76
- $data['taxonomy'] = 'category';
77
- break;
78
-
79
- case 'tag':
80
- $tag_name['slug'] = 'wp:tag_slug';
81
- $tag_name['parent'] = null;
82
- $tag_name['name'] = 'wp:tag_name';
83
- $tag_name['description'] = 'wp:tag_description';
84
- $tag_name['taxonomy'] = null;
85
-
86
- $data['taxonomy'] = 'post_tag';
87
- break;
88
- }
89
-
90
- foreach ( $node->childNodes as $child ) {
91
- // We only care about child elements
92
- if ( $child->nodeType !== XML_ELEMENT_NODE ) {
93
- continue;
94
- }
95
-
96
- $key = array_search( $child->tagName, $tag_name );
97
- if ( $key ) {
98
- $data[ $key ] = $child->textContent;
99
- }
100
- }
101
-
102
- $this->_jupiterx_parse_term_meta_node( $node->childNodes );
103
-
104
- if ( empty( $data['taxonomy'] ) ) {
105
- return null;
106
- }
107
-
108
- // Compatibility with WXR 1.0
109
- if ( $data['taxonomy'] === 'tag' ) {
110
- $data['taxonomy'] = 'post_tag';
111
- }
112
-
113
- return compact( 'data', 'meta' );
114
- }
115
-
116
- /**
117
- * Parse term meta node.
118
- *
119
- * @since 1.1.0
120
- */
121
- private function _jupiterx_parse_term_meta_node( $childNodes ) {
122
- $this->jupiterx_meta = [];
123
-
124
- foreach ( $childNodes as $child ) {
125
-
126
- if ( $child->nodeType !== XML_ELEMENT_NODE ) {
127
- continue;
128
- }
129
-
130
- if ( 'wp:termmeta' !== $child->tagName ) {
131
- continue;
132
- }
133
-
134
- $meta_node = $this->parse_meta_node( $child );
135
-
136
- $this->jupiterx_meta[ $meta_node['key'] ] = $meta_node['value'];
137
- }
138
- }
139
-
140
- /**
141
- * Process term meta.
142
- *
143
- * @since 1.1.0
144
- */
145
- public function jupiterx_process_term_meta( $term_id ) {
146
-
147
- if ( empty( $this->jupiterx_meta ) ) {
148
- return;
149
- }
150
-
151
- foreach( $this->jupiterx_meta as $term_key => $term_val ) {
152
- update_term_meta( $term_id, $term_key, $term_val );
153
- }
154
- }
155
- }
1
+ <?php
2
+ /**
3
+ * This class extends JupiterX_WXR_Importer.
4
+ *
5
+ * @package JupiterX\Framework\Control_Panel\Importer
6
+ *
7
+ * @since 1.1.0
8
+ */
9
+
10
+ /**
11
+ * JupiterX Importer.
12
+ *
13
+ * @since 1.1.0
14
+ * @package JupiterX\Framework\Control_Panel\Importer
15
+ */
16
+ class JupiterX_Importer extends JupiterX_WXR_Importer {
17
+
18
+ /**
19
+ * Term meta.
20
+ *
21
+ * @var array
22
+ */
23
+ protected $jupiterx_meta = [];
24
+
25
+ /**
26
+ * Import pages only.
27
+ *
28
+ * @var boolean
29
+ */
30
+ protected $partial_import;
31
+
32
+ /**
33
+ * Constructor.
34
+ *
35
+ * @param array $options The JupiterX_WXR_Importer options.
36
+ */
37
+ public function __construct( $options = [], $partial_import = false ) {
38
+ parent::__construct( $options, $partial_import );
39
+
40
+ add_action( 'wp_import_insert_term', [ $this, 'jupiterx_process_term_meta' ] );
41
+ }
42
+
43
+ /**
44
+ * Parse term node.
45
+ *
46
+ * It's a copy from the parent class. There's no way to access `$node` variable through
47
+ * actions so it's necessary to override it.
48
+ *
49
+ * The addition is `_jupiterx_parse_term_meta_node` method call.
50
+ *
51
+ * @since 1.1.0
52
+ */
53
+ protected function parse_term_node( $node, $type = 'term' ) {
54
+ $data = array();
55
+ $meta = array();
56
+
57
+ $tag_name = array(
58
+ 'id' => 'wp:term_id',
59
+ 'taxonomy' => 'wp:term_taxonomy',
60
+ 'slug' => 'wp:term_slug',
61
+ 'parent' => 'wp:term_parent',
62
+ 'name' => 'wp:term_name',
63
+ 'description' => 'wp:term_description',
64
+ );
65
+ $taxonomy = null;
66
+
67
+ // Special casing!
68
+ switch ( $type ) {
69
+ case 'category':
70
+ $tag_name['slug'] = 'wp:category_nicename';
71
+ $tag_name['parent'] = 'wp:category_parent';
72
+ $tag_name['name'] = 'wp:cat_name';
73
+ $tag_name['description'] = 'wp:category_description';
74
+ $tag_name['taxonomy'] = null;
75
+
76
+ $data['taxonomy'] = 'category';
77
+ break;
78
+
79
+ case 'tag':
80
+ $tag_name['slug'] = 'wp:tag_slug';
81
+ $tag_name['parent'] = null;
82
+ $tag_name['name'] = 'wp:tag_name';
83
+ $tag_name['description'] = 'wp:tag_description';
84
+ $tag_name['taxonomy'] = null;
85
+
86
+ $data['taxonomy'] = 'post_tag';
87
+ break;
88
+ }
89
+
90
+ foreach ( $node->childNodes as $child ) {
91
+ // We only care about child elements
92
+ if ( $child->nodeType !== XML_ELEMENT_NODE ) {
93
+ continue;
94
+ }
95
+
96
+ $key = array_search( $child->tagName, $tag_name );
97
+ if ( $key ) {
98
+ $data[ $key ] = $child->textContent;
99
+ }
100
+ }
101
+
102
+ $this->_jupiterx_parse_term_meta_node( $node->childNodes );
103
+
104
+ if ( empty( $data['taxonomy'] ) ) {
105
+ return null;
106
+ }
107
+
108
+ // Compatibility with WXR 1.0
109
+ if ( $data['taxonomy'] === 'tag' ) {
110
+ $data['taxonomy'] = 'post_tag';
111
+ }
112
+
113
+ return compact( 'data', 'meta' );
114
+ }
115
+
116
+ /**
117
+ * Parse term meta node.
118
+ *
119
+ * @since 1.1.0
120
+ */
121
+ private function _jupiterx_parse_term_meta_node( $childNodes ) {
122
+ $this->jupiterx_meta = [];
123
+
124
+ foreach ( $childNodes as $child ) {
125
+
126
+ if ( $child->nodeType !== XML_ELEMENT_NODE ) {
127
+ continue;
128
+ }
129
+
130
+ if ( 'wp:termmeta' !== $child->tagName ) {
131
+ continue;
132
+ }
133
+
134
+ $meta_node = $this->parse_meta_node( $child );
135
+
136
+ $this->jupiterx_meta[ $meta_node['key'] ] = $meta_node['value'];
137
+ }
138
+ }
139
+
140
+ /**
141
+ * Process term meta.
142
+ *
143
+ * @since 1.1.0
144
+ */
145
+ public function jupiterx_process_term_meta( $term_id ) {
146
+
147
+ if ( empty( $this->jupiterx_meta ) ) {
148
+ return;
149
+ }
150
+
151
+ foreach( $this->jupiterx_meta as $term_key => $term_val ) {
152
+ update_term_meta( $term_id, $term_key, $term_val );
153
+ }
154
+ }
155
+ }
includes/control-panel/includes/importer/class-logger-serversentevents.php CHANGED
@@ -1,57 +1,57 @@
1
- <?php
2
- /**
3
- * WP Importer Logger Server Sent Event class.
4
- *
5
- * For use to save the server log of importing process in Jupiter.
6
- *
7
- * @package Jupiter
8
- * @subpackage Template Import
9
- * @since 6.0.3
10
- */
11
-
12
- /**
13
- * Store server log while importing process.
14
- *
15
- * @since 6.0.3
16
- *
17
- * @see https://github.com/humanmade/WordPress-Importer/blob/master/class-logger-serversentevents.php
18
- *
19
- * @codingStandardsIgnoreFile
20
- * @SuppressWarnings(PHPMD.CyclomaticComplexity)
21
- */
22
- class JupiterX_Importer_Logger_ServerSentEvents extends JupiterX_Importer_Logger {
23
- /**
24
- * Logs with an arbitrary level.
25
- *
26
- * @param mixed $level
27
- * @param string $message
28
- * @param array $context
29
- * @return null
30
- */
31
- public function log( $level, $message, array $context = array() ) {
32
- $data = compact( 'level', 'message' );
33
-
34
- switch ( $level ) {
35
- case 'emergency':
36
- case 'alert':
37
- case 'critical':
38
- case 'error':
39
- case 'warning':
40
- case 'notice':
41
- case 'info':
42
- echo "event: log\n";
43
- echo 'data: ' . wp_json_encode( $data ) . "\n\n";
44
- flush();
45
- break;
46
-
47
- case 'debug':
48
- if ( defined( 'IMPORT_DEBUG' ) && IMPORT_DEBUG ) {
49
- echo "event: log\n";
50
- echo 'data: ' . wp_json_encode( $data ) . "\n\n";
51
- flush();
52
- break;
53
- }
54
- break;
55
- }
56
- }
57
- }
1
+ <?php
2
+ /**
3
+ * WP Importer Logger Server Sent Event class.
4
+ *
5
+ * For use to save the server log of importing process in Jupiter.
6
+ *
7
+ * @package Jupiter
8
+ * @subpackage Template Import
9
+ * @since 6.0.3
10
+ */
11
+
12
+ /**
13
+ * Store server log while importing process.
14
+ *
15
+ * @since 6.0.3
16
+ *
17
+ * @see https://github.com/humanmade/WordPress-Importer/blob/master/class-logger-serversentevents.php
18
+ *
19
+ * @codingStandardsIgnoreFile
20
+ * @SuppressWarnings(PHPMD.CyclomaticComplexity)
21
+ */
22
+ class JupiterX_Importer_Logger_ServerSentEvents extends JupiterX_Importer_Logger {
23
+ /**
24
+ * Logs with an arbitrary level.
25
+ *
26
+ * @param mixed $level
27
+ * @param string $message
28
+ * @param array $context
29
+ * @return null
30
+ */
31
+ public function log( $level, $message, array $context = array() ) {
32
+ $data = compact( 'level', 'message' );
33
+
34
+ switch ( $level ) {
35
+ case 'emergency':
36
+ case 'alert':
37
+ case 'critical':
38
+ case 'error':
39
+ case 'warning':
40
+ case 'notice':
41
+ case 'info':
42
+ echo "event: log\n";
43
+ echo 'data: ' . wp_json_encode( $data ) . "\n\n";
44
+ flush();
45
+ break;
46
+
47
+ case 'debug':
48
+ if ( defined( 'IMPORT_DEBUG' ) && IMPORT_DEBUG ) {
49
+ echo "event: log\n";
50
+ echo 'data: ' . wp_json_encode( $data ) . "\n\n";
51
+ flush();
52
+ break;
53
+ }
54
+ break;
55
+ }
56
+ }
57
+ }
includes/control-panel/includes/importer/class-logger.php CHANGED
@@ -1,138 +1,138 @@
1
- <?php
2
-
3
- /**
4
- * Describes a logger instance
5
- *
6
- * Based on PSR-3: http://www.php-fig.org/psr/psr-3/
7
- *
8
- * The message MUST be a string or object implementing __toString().
9
- *
10
- * The message MAY contain placeholders in the form: {foo} where foo
11
- * will be replaced by the context data in key "foo".
12
- *
13
- * The context array can contain arbitrary data, the only assumption that
14
- * can be made by implementors is that if an Exception instance is given
15
- * to produce a stack trace, it MUST be in a key named "exception".
16
- *
17
- * See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md
18
- * for the full interface specification.
19
- *
20
- * @codingStandardsIgnoreFile
21
- */
22
- class JupiterX_Importer_Logger {
23
- /**
24
- * System is unusable.
25
- *
26
- * @param string $message
27
- * @param array $context
28
- * @return null
29
- */
30
- public function emergency( $message, array $context = array() ) {
31
- return $this->log( 'emergency', $message, $context );
32
- }
33
-
34
- /**
35
- * Action must be taken immediately.
36
- *
37
- * Example: Entire website down, database unavailable, etc. This should
38
- * trigger the SMS alerts and wake you up.
39
- *
40
- * @param string $message
41
- * @param array $context
42
- * @return null
43
- */
44
- public function alert( $message, array $context = array() ) {
45
- return $this->log( 'alert', $message, $context );
46
- }
47
-
48
- /**
49
- * Critical conditions.
50
- *
51
- * Example: Application component unavailable, unexpected exception.
52
- *
53
- * @param string $message
54
- * @param array $context
55
- * @return null
56
- */
57
- public function critical( $message, array $context = array() ) {
58
- return $this->log( 'critical', $message, $context );
59
- }
60
-
61
- /**
62
- * Runtime errors that do not require immediate action but should typically
63
- * be logged and monitored.
64
- *
65
- * @param string $message
66
- * @param array $context
67
- * @return null
68
- */
69
- public function error( $message, array $context = array()) {
70
- return $this->log( 'error', $message, $context );
71
- }
72
-
73
- /**
74
- * Exceptional occurrences that are not errors.
75
- *
76
- * Example: Use of deprecated APIs, poor use of an API, undesirable things
77
- * that are not necessarily wrong.
78
- *
79
- * @param string $message
80
- * @param array $context
81
- * @return null
82
- */
83
- public function warning( $message, array $context = array() ) {
84
- return $this->log( 'warning', $message, $context );
85
- }
86
-
87
- /**
88
- * Normal but significant events.
89
- *
90
- * @param string $message
91
- * @param array $context
92
- * @return null
93
- */
94
- public function notice( $message, array $context = array() ) {
95
- return $this->log( 'notice', $message, $context );
96
- }
97
-
98
- /**
99
- * Interesting events.
100
- *
101
- * Example: User logs in, SQL logs.
102
- *
103
- * @param string $message
104
- * @param array $context
105
- * @return null
106
- */
107
- public function info( $message, array $context = array() ) {
108
- return $this->log( 'info', $message, $context );
109
- }
110
-
111
- /**
112
- * Detailed debug information.
113
- *
114
- * @param string $message
115
- * @param array $context
116
- * @return null
117
- */
118
- public function debug( $message, array $context = array() ) {
119
- return $this->log( 'debug', $message, $context );
120
- }
121
-
122
- /**
123
- * Logs with an arbitrary level.
124
- *
125
- * @param mixed $level
126
- * @param string $message
127
- * @param array $context
128
- * @return null
129
- */
130
- public function log( $level, $message, array $context = array() ) {
131
- $this->messages[] = array(
132
- 'timestamp' => time(),
133
- 'level' => $level,
134
- 'message' => $message,
135
- 'context' => $context,
136
- );
137
- }
138
- }
1
+ <?php
2
+
3
+ /**
4
+ * Describes a logger instance
5
+ *
6
+ * Based on PSR-3: http://www.php-fig.org/psr/psr-3/
7
+ *
8
+ * The message MUST be a string or object implementing __toString().
9
+ *
10
+ * The message MAY contain placeholders in the form: {foo} where foo
11
+ * will be replaced by the context data in key "foo".
12
+ *
13
+ * The context array can contain arbitrary data, the only assumption that
14
+ * can be made by implementors is that if an Exception instance is given
15
+ * to produce a stack trace, it MUST be in a key named "exception".
16
+ *
17
+ * See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md
18
+ * for the full interface specification.
19
+ *
20
+ * @codingStandardsIgnoreFile
21
+ */
22
+ class JupiterX_Importer_Logger {
23
+ /**
24
+ * System is unusable.
25
+ *
26
+ * @param string $message
27
+ * @param array $context
28
+ * @return null
29
+ */
30
+ public function emergency( $message, array $context = array() ) {
31
+ return $this->log( 'emergency', $message, $context );
32
+ }
33
+
34
+ /**
35
+ * Action must be taken immediately.
36
+ *
37
+ * Example: Entire website down, database unavailable, etc. This should
38
+ * trigger the SMS alerts and wake you up.
39
+ *
40
+ * @param string $message
41
+ * @param array $context
42
+ * @return null
43
+ */
44
+ public function alert( $message, array $context = array() ) {
45
+ return $this->log( 'alert', $message, $context );
46
+ }
47
+
48
+ /**
49
+ * Critical conditions.
50
+ *
51
+ * Example: Application component unavailable, unexpected exception.
52
+ *
53
+ * @param string $message
54
+ * @param array $context
55
+ * @return null
56
+ */
57
+ public function critical( $message, array $context = array() ) {
58
+ return $this->log( 'critical', $message, $context );
59
+ }
60
+
61
+ /**
62
+ * Runtime errors that do not require immediate action but should typically
63
+ * be logged and monitored.
64
+ *
65
+ * @param string $message
66
+ * @param array $context
67
+ * @return null
68
+ */
69
+ public function error( $message, array $context = array()) {
70
+ return $this->log( 'error', $message, $context );
71
+ }
72
+
73
+ /**
74
+ * Exceptional occurrences that are not errors.
75
+ *
76
+ * Example: Use of deprecated APIs, poor use of an API, undesirable things
77
+ * that are not necessarily wrong.
78
+ *
79
+ * @param string $message
80
+ * @param array $context
81
+ * @return null
82
+ */
83
+ public function warning( $message, array $context = array() ) {
84
+ return $this->log( 'warning', $message, $context );
85
+ }
86
+
87
+ /**
88
+ * Normal but significant events.
89
+ *
90
+ * @param string $message
91
+ * @param array $context
92
+ * @return null
93
+ */
94
+ public function notice( $message, array $context = array() ) {
95
+ return $this->log( 'notice', $message, $context );
96
+ }
97
+
98
+ /**
99
+ * Interesting events.
100
+ *
101
+ * Example: User logs in, SQL logs.
102
+ *
103
+ * @param string $message
104
+ * @param array $context
105
+ * @return null
106
+ */
107
+ public function info( $message, array $context = array() ) {
108
+ return $this->log( 'info', $message, $context );
109
+ }
110
+
111
+ /**
112
+ * Detailed debug information.
113
+ *
114
+ * @param string $message
115
+ * @param array $context
116
+ * @return null
117
+ */
118
+ public function debug( $message, array $context = array() ) {
119
+ return $this->log( 'debug', $message, $context );
120
+ }
121
+
122
+ /**
123
+ * Logs with an arbitrary level.
124
+ *
125
+ * @param mixed $level
126
+ * @param string $message
127
+ * @param array $context
128
+ * @return null
129
+ */
130
+ public function log( $level, $message, array $context = array() ) {
131
+ $this->messages[] = array(
132
+ 'timestamp' => time(),
133
+ 'level' => $level,
134
+ 'message' => $message,
135
+ 'context' => $context,
136
+ );
137
+ }
138
+ }
includes/control-panel/includes/importer/class-wxr-import-info.php CHANGED
@@ -1,35 +1,35 @@
1
- <?php
2
- /**
3
- * WXR Import Info class.
4
- *
5
- * As an abstraction of importing site info.
6
- *
7
- * @package Jupiter
8
- * @subpackage Template Import
9
- * @since 6.0.3
10
- */
11
-
12
- /**
13
- * Initialize base variable of importing info.
14
- *
15
- * @since 6.0.3
16
- *
17
- * @see https://github.com/humanmade/WordPress-Importer/blob/master/class-wxr-import-info.php
18
- *
19
- * @codingStandardsIgnoreFile
20
- */
21
- class JupiterX_WXR_Import_Info {
22
- public $home;
23
- public $siteurl;
24
-
25
- public $title;
26
-
27
- public $users = array();
28
- public $post_count = 0;
29
- public $media_count = 0;
30
- public $comment_count = 0;
31
- public $term_count = 0;
32
-
33
- public $generator = '';
34
- public $version;
35
- }
1
+ <?php
2
+ /**
3
+ * WXR Import Info class.
4
+ *
5
+ * As an abstraction of importing site info.
6
+ *
7
+ * @package Jupiter
8
+ * @subpackage Template Import
9
+ * @since 6.0.3
10
+ */
11
+
12
+ /**
13
+ * Initialize base variable of importing info.
14
+ *
15
+ * @since 6.0.3
16
+ *
17
+ * @see https://github.com/humanmade/WordPress-Importer/blob/master/class-wxr-import-info.php
18
+ *
19
+ * @codingStandardsIgnoreFile
20
+ */
21
+ class JupiterX_WXR_Import_Info {
22
+ public $home;
23
+ public $siteurl;
24
+
25
+ public $title;
26
+
27
+ public $users = array();
28
+ public $post_count = 0;
29
+ public $media_count = 0;
30
+ public $comment_count = 0;
31
+ public $term_count = 0;
32
+
33
+ public $generator = '';
34
+ public $version;
35
+ }
includes/control-panel/includes/importer/class-wxr-importer.php CHANGED
@@ -1,2076 +1,2076 @@
1
- <?php
2
- /**
3
- * WXR Importer class.
4
- *
5
- * Main class of WordPress Importer v2 to handle import process.
6
- *
7
- * @package Jupiter
8
- * @subpackage Template Import
9
- * @since 6.0.3
10
- */
11
-
12
- /**
13
- * The main class of WordPress Importer v2.
14
- *
15
- * @since 6.0.3
16
- *
17
- * @see https://github.com/humanmade/WordPress-Importer/blob/master/class-wxr-importer.php
18
- *
19
- * @codingStandardsIgnoreFile
20
- * @SuppressWarnings(PHPMD)
21
- * - Temporary suppress warning. Worry if we change the content of the class or change how
22
- * the logical conditions work, it will cause some issues.
23
- */
24
- class JupiterX_WXR_Importer extends WP_Importer {
25
- /**
26
- * Maximum supported WXR version
27
- */
28
- const MAX_WXR_VERSION = 1.2;
29
-
30
- /**
31
- * Regular expression for checking if a post references an attachment
32
- *
33
- * Note: This is a quick, weak check just to exclude text-only posts. More
34
- * vigorous checking is done later to verify.
35
- */
36
- const REGEX_HAS_ATTACHMENT_REFS = '!
37
- (
38
- # Match anything with an image or attachment class
39
- class=[\'"].*?\b(wp-image-\d+|attachment-[\w\-]+)\b
40
- |
41
- # Match anything that looks like an upload URL
42
- src=[\'"][^\'"]*(
43
- [0-9]{4}/[0-9]{2}/[^\'"]+\.(jpg|jpeg|png|gif)
44
- |
45
- content/uploads[^\'"]+
46
- )[\'"]
47
- )!ix';
48
-
49
- /**
50
- * Version of WXR we're importing.
51
- *
52
- * Defaults to 1.0 for compatibility. Typically overridden by a
53
- * `<wp:wxr_version>` tag at the start of the file.
54
- *
55
- * @var string
56
- */
57
- protected $version = '1.0';
58
-
59
- // information to import from WXR file
60
- protected $categories = array();
61
- protected $tags = array();
62
- protected $base_url = '';
63
-
64
- // TODO: REMOVE THESE
65
- protected $processed_terms = array();
66
- protected $processed_posts = array();
67
- protected $processed_menu_items = array();
68
- protected $menu_item_orphans = array();
69
- protected $missing_menu_items = array();
70
-
71
- // NEW STYLE
72
- protected $mapping = array();
73
- protected $requires_remapping = array();
74
- protected $exists = array();
75
- protected $user_slug_override = array();
76
-
77
- protected $url_remap = array();
78
- protected $featured_images = array();
79
-
80
- /**
81
- * Logger instance.
82
- *
83
- * @var JupiterX_Importer_Logger
84
- */
85
- protected $logger;
86
-
87
- /**
88
- * Import pages only.
89
- *
90
- * @var boolean
91
- */
92
- protected $partial_import;
93
-
94
- /**
95
- * Constructor
96
- *
97
- * @param array $options {
98
- * @var bool $prefill_existing_posts Should we prefill `post_exists` calls? (True prefills and uses more memory, false checks once per imported post and takes longer. Default is true.)
99
- * @var bool $prefill_existing_comments Should we prefill `comment_exists` calls? (True prefills and uses more memory, false checks once per imported comment and takes longer. Default is true.)
100
- * @var bool $prefill_existing_terms Should we prefill `term_exists` calls? (True prefills and uses more memory, false checks once per imported term and takes longer. Default is true.)
101
- * @var bool $update_attachment_guids Should attachment GUIDs be updated to the new URL? (True updates the GUID, which keeps compatibility with v1, false doesn't update, and allows deduplication and reimporting. Default is false.)
102
- * @var bool $fetch_attachments Fetch attachments from the remote server. (True fetches and creates attachment posts, false skips attachments. Default is false.)
103
- * @var bool $aggressive_url_search Should we search/replace for URLs aggressively? (True searches all posts' content for old URLs and replaces, false checks for `<img class="wp-image-*">` only. Default is false.)
104
- * @var int $default_author User ID to use if author is missing or invalid. (Default is null, which leaves posts unassigned.)
105
- * }
106
- */
107
- public function __construct( $options = array(), $partial_import = false ) {
108
- $this->partial_import = $partial_import;
109
-
110
- // Initialize some important variables
111
- $empty_types = array(
112
- 'post' => array(),
113
- 'comment' => array(),
114
- 'term' => array(),
115
- 'user' => array(),
116
- );
117
-
118
- $this->mapping = $empty_types;
119
- $this->mapping['user_slug'] = array();
120
- $this->mapping['term_id'] = array();
121
- $this->requires_remapping = $empty_types;
122
- $this->exists = $empty_types;
123
-
124
- $this->options = wp_parse_args( $options, array(
125
- 'prefill_existing_posts' => true,
126
- 'prefill_existing_comments' => true,
127
- 'prefill_existing_terms' => true,
128
- 'update_attachment_guids' => false,
129
- 'fetch_attachments' => false,
130
- 'aggressive_url_search' => false,
131
- 'default_author' => null,
132
- ) );
133
- }
134
-
135
- public function set_logger( $logger ) {
136
- $this->logger = $logger;
137
- }
138
-
139
- /**
140
- * Get a stream reader for the file.
141
- *
142
- * @param string $file Path to the XML file.
143
- * @return XMLReader|WP_Error Reader instance on success, error otherwise.
144
- */
145
- protected function get_reader( $file ) {
146
- // Avoid loading external entities for security
147
- $old_value = null;
148
- if ( function_exists( 'libxml_disable_entity_loader' ) ) {
149
- // $old_value = libxml_disable_entity_loader( true );
150
- }
151
-
152
- $reader = new XMLReader();
153
- $status = $reader->open( $file );
154
-
155
- if ( ! is_null( $old_value ) ) {
156
- // libxml_disable_entity_loader( $old_value );
157
- }
158
-
159
- if ( ! $status ) {
160
- return new WP_Error( 'wxr_importer.cannot_parse', __( 'Could not open the file for parsing', 'jupiterx-core' ) );
161
- }
162
-
163
- return $reader;
164
- }
165
-
166
- /**
167
- * The main controller for the actual import stage.
168
- *
169
- * @param string $file Path to the WXR file for importing
170
- */
171
- public function get_preliminary_information( $file ) {
172
- // Let's run the actual importer now, woot
173
- $reader = $this->get_reader( $file );
174
- if ( is_wp_error( $reader ) ) {
175
- return $reader;
176
- }
177
-
178
- // Set the version to compatibility mode first
179
- $this->version = '1.0';
180
-
181
- // Start parsing!
182
- $data = new JupiterX_WXR_Import_Info();
183
- while ( $reader->read() ) {
184
- // Only deal with element opens
185
- if ( $reader->nodeType !== XMLReader::ELEMENT ) {
186
- continue;
187
- }
188
-
189
- switch ( $reader->name ) {
190
- case 'wp:wxr_version':
191
- // Upgrade to the correct version
192
- $this->version = $reader->readString();
193
-
194
- if ( version_compare( $this->version, self::MAX_WXR_VERSION, '>' ) ) {
195
- $this->logger->warning( sprintf(
196
- __( 'This WXR file (version %s) is newer than the importer (version %s) and may not be supported. Please consider updating.', 'jupiterx-core' ),
197
- $this->version,
198
- self::MAX_WXR_VERSION
199
- ) );
200
- }
201
-
202
- // Handled everything in this node, move on to the next
203
- $reader->next();
204
- break;
205
-
206
- case 'generator':
207
- $data->generator = $reader->readString();
208
- $reader->next();
209
- break;
210
-
211
- case 'title':
212
- $data->title = $reader->readString();
213
- $reader->next();
214
- break;
215
-
216
- case 'wp:base_site_url':
217
- $data->siteurl = $reader->readString();
218
- $reader->next();
219
- break;
220
-
221
- case 'wp:base_blog_url':
222
- $data->home = $reader->readString();
223
- $reader->next();
224
- break;
225
-
226
- case 'item':
227
- $node = $reader->expand();
228
- $parsed = $this->parse_post_node( $node );
229
- if ( is_wp_error( $parsed ) ) {
230
- $this->log_error( $parsed );
231
-
232
- // Skip the rest of this post
233
- $reader->next();
234
- break;
235
- }
236
-
237
- if ( $parsed['data']['post_type'] === 'attachment' ) {
238
- $data->media_count++;
239
- } else {
240
- $data->post_count++;
241
- }
242
- $data->comment_count += count( $parsed['comments'] );
243
-
244
- // Handled everything in this node, move on to the next
245
- $reader->next();
246
- break;
247
-
248
- case 'wp:category':
249
- case 'wp:tag':
250
- case 'wp:term':
251
- $data->term_count++;
252
-
253
- // Handled everything in this node, move on to the next
254
- $reader->next();
255
- break;
256
- }
257
- }
258
-
259
- $data->version = $this->version;
260
-
261
- return $data;
262
- }
263
-
264
- /**
265
- * The main controller for the actual import stage.
266
- *
267
- * @param string $file Path to the WXR file for importing
268
- */
269
- public function import( $file ) {
270
- add_filter( 'import_post_meta_key', array( $this, 'is_valid_meta_key' ) );
271
- add_filter( 'http_request_timeout', array( &$this, 'bump_request_timeout' ) );
272
-
273
- $result = $this->import_start( $file );
274
- if ( is_wp_error( $result ) ) {
275
- return $result;
276
- }
277
-
278
- // Let's run the actual importer now, woot
279
- $reader = $this->get_reader( $file );
280
- if ( is_wp_error( $reader ) ) {
281
- return $reader;
282
- }
283
-
284
- // Set the version to compatibility mode first
285
- $this->version = '1.0';
286
-
287
- // Reset other variables
288
- $this->base_url = '';
289
-
290
- // Start parsing!
291
- while ( $reader->read() ) {
292
- // Only deal with element opens
293
- if ( $reader->nodeType !== XMLReader::ELEMENT ) {
294
- continue;
295
- }
296
-
297
- if ( $reader->name !== 'item' && $this->partial_import ) {
298
- continue;
299
- }
300
-
301
- switch ( $reader->name ) {
302
- case 'wp:wxr_version':
303
- // Upgrade to the correct version
304
- $this->version = $reader->readString();
305
-
306
- if ( version_compare( $this->version, self::MAX_WXR_VERSION, '>' ) ) {
307
- $this->logger->warning( sprintf(
308
- __( 'This WXR file (version %s) is newer than the importer (version %s) and may not be supported. Please consider updating.', 'jupiterx-core' ),
309
- $this->version,
310
- self::MAX_WXR_VERSION
311
- ) );
312
- }
313
-
314
- // Handled everything in this node, move on to the next
315
- $reader->next();
316
- break;
317
-
318
- case 'wp:base_site_url':
319
- $this->base_url = $reader->readString();
320
-
321
- // Handled everything in this node, move on to the next
322
- $reader->next();
323
- break;
324
-
325
- case 'item':
326
- $node = $reader->expand();
327
- $parsed = $this->parse_post_node( $node );
328
- if ( is_wp_error( $parsed ) ) {
329
- $this->log_error( $parsed );
330
-
331
- // Skip the rest of this post
332
- $reader->next();
333
- break;
334
- }
335
-
336
- if (
337
- in_array( $parsed['data']['post_type'], [ 'nav_menu_item' ], true ) &&
338
- $this->partial_import
339
- ) {
340
- break;
341
- }
342
-
343
- $this->process_post( $parsed['data'], $parsed['meta'], $parsed['comments'], $parsed['terms'] );
344
-
345
- // Handled everything in this node, move on to the next
346
- $reader->next();
347
- break;
348
-
349
- case 'wp:category':
350
- $node = $reader->expand();
351
-
352
- $parsed = $this->parse_term_node( $node, 'category' );
353
- if ( is_wp_error( $parsed ) ) {
354
- $this->log_error( $parsed );
355
-
356
- // Skip the rest of this post
357
- $reader->next();
358
- break;
359
- }
360
-
361
- $status = $this->process_term( $parsed['data'], $parsed['meta'] );
362
-
363
- // Handled everything in this node, move on to the next
364
- $reader->next();
365
- break;
366
-
367
- case 'wp:tag':
368
- $node = $reader->expand();
369
-
370
- $parsed = $this->parse_term_node( $node, 'tag' );
371
- if ( is_wp_error( $parsed ) ) {
372
- $this->log_error( $parsed );
373
-
374
- // Skip the rest of this post
375
- $reader->next();
376
- break;
377
- }
378
-
379
- $status = $this->process_term( $parsed['data'], $parsed['meta'] );
380
-
381
- // Handled everything in this node, move on to the next
382
- $reader->next();
383
- break;
384
-
385
- case 'wp:term':
386
- $node = $reader->expand();
387
-
388
- $parsed = $this->parse_term_node( $node );
389
- if ( is_wp_error( $parsed ) ) {
390
- $this->log_error( $parsed );
391
-
392
- // Skip the rest of this post
393
- $reader->next();
394
- break;
395
- }
396
-
397
- $status = $this->process_term( $parsed['data'], $parsed['meta'] );
398
-
399
- // Handled everything in this node, move on to the next
400
- $reader->next();
401
- break;
402
-
403
- default:
404
- // Skip this node, probably handled by something already
405
- break;
406
- }
407
- }
408
-
409
- // Now that we've done the main processing, do any required
410
- // post-processing and remapping.
411
- $this->post_process();
412
-
413
- if ( $this->options['aggressive_url_search'] ) {
414
- $this->replace_attachment_urls_in_content();
415
- }
416
- // $this->remap_featured_images();
417
-
418
- $this->import_end();
419
- }
420
-
421
- /**
422
- * Log an error instance to the logger.
423
- *
424
- * @param WP_Error $error Error instance to log.
425
- */
426
- protected function log_error( WP_Error $error ) {
427
- $this->logger->warning( $error->get_error_message() );
428
-
429
- // Log the data as debug info too
430
- $data = $error->get_error_data();
431
- if ( ! empty( $data ) ) {
432
- $this->logger->debug( var_export( $data, true ) );
433
- }
434
- }
435
-
436
- /**
437
- * Parses the WXR file and prepares us for the task of processing parsed data
438
- *
439
- * @param string $file Path to the WXR file for importing
440
- */
441
- protected function import_start( $file ) {
442
- if ( ! is_file( $file ) ) {
443
- return new WP_Error( 'wxr_importer.file_missing', __( 'The file does not exist, please try again.', 'jupiterx-core' ) );
444
- }
445
-
446
- // Suspend bunches of stuff in WP core
447
- wp_defer_term_counting( true );
448
- wp_defer_comment_counting( true );
449
- wp_suspend_cache_invalidation( true );
450
-
451
- // Prefill exists calls if told to
452
- if ( $this->options['prefill_existing_posts'] ) {
453
- $this->prefill_existing_posts();
454
- }
455
- if ( $this->options['prefill_existing_comments'] ) {
456
- $this->prefill_existing_comments();
457
- }
458
- if ( $this->options['prefill_existing_terms'] ) {
459
- $this->prefill_existing_terms();
460
- }
461
-
462
- /**
463
- * Begin the import.
464
- *
465
- * Fires before the import process has begun. If you need to suspend
466
- * caching or heavy processing on hooks, do so here.
467
- */
468
- do_action( 'import_start' );
469
- }
470
-
471
- /**
472
- * Performs post-import cleanup of files and the cache
473
- */
474
- protected function import_end() {
475
- // Re-enable stuff in core
476
- wp_suspend_cache_invalidation( false );
477
- wp_cache_flush();
478
- foreach ( get_taxonomies() as $tax ) {
479
- delete_option( "{$tax}_children" );
480
- _get_term_hierarchy( $tax );
481
- }
482
-
483
- wp_defer_term_counting( false );
484
- wp_defer_comment_counting( false );
485
-
486
- /**
487
- * Complete the import.
488
- *
489
- * Fires after the import process has finished. If you need to update
490
- * your cache or re-enable processing, do so here.
491
- */
492
- do_action( 'import_end' );
493
- }
494
-
495
- /**
496
- * Set the user mapping.
497
- *
498
- * @param array $mapping List of map arrays (containing `old_slug`, `old_id`, `new_id`)
499
- */
500
- public function set_user_mapping( $mapping ) {
501
- foreach ( $mapping as $map ) {
502
- if ( empty( $map['old_slug'] ) || empty( $map['old_id'] ) || empty( $map['new_id'] ) ) {
503
- $this->logger->warning( __( 'Invalid author mapping', 'jupiterx-core' ) );
504
- $this->logger->debug( var_export( $map, true ) );
505
- continue;
506
- }
507
-
508
- $old_slug = $map['old_slug'];
509
- $old_id = $map['old_id'];
510
- $new_id = $map['new_id'];
511
-
512
- $this->mapping['user'][ $old_id ] = $new_id;
513
- $this->mapping['user_slug'][ $old_slug ] = $new_id;
514
- }
515
- }
516
-
517
- /**
518
- * Set the user slug overrides.
519
- *
520
- * Allows overriding the slug in the import with a custom/renamed version.
521
- *
522
- * @param string[] $overrides Map of old slug to new slug.
523
- */
524
- public function set_user_slug_overrides( $overrides ) {
525
- foreach ( $overrides as $original => $renamed ) {
526
- $this->user_slug_override[ $original ] = $renamed;
527
- }
528
- }
529
-
530
- /**
531
- * Parse a post node into post data.
532
- *
533
- * @param DOMElement $node Parent node of post data (typically `item`).
534
- * @return array|WP_Error Post data array on success, error otherwise.
535
- */
536
- protected function parse_post_node( $node ) {
537
- $data = array();
538
- $meta = array();
539
- $comments = array();
540
- $terms = array();
541
-
542
- foreach ( $node->childNodes as $child ) {
543
- // We only care about child elements
544
- if ( $child->nodeType !== XML_ELEMENT_NODE ) {
545
- continue;
546
- }
547
-
548
- switch ( $child->tagName ) {
549
- case 'wp:post_type':
550
- $data['post_type'] = $child->textContent;
551
- break;
552
-
553
- case 'title':
554
- $data['post_title'] = $child->textContent;
555
- break;
556
-
557
- case 'guid':
558
- $data['guid'] = $child->textContent;
559
- break;
560
-
561
- case 'dc:creator':
562
- $data['post_author'] = $child->textContent;
563
- break;
564
-
565
- case 'content:encoded':
566
- $data['post_content'] = $child->textContent;
567
- break;
568
-
569
- case 'excerpt:encoded':
570
- $data['post_excerpt'] = $child->textContent;
571
- break;
572
-
573
- case 'wp:post_id':
574
- $data['post_id'] = $child->textContent;
575
- break;
576
-
577
- case 'wp:post_date':
578
- $data['post_date'] = $child->textContent;
579
- break;
580
-
581
- case 'wp:post_date_gmt':
582
- $data['post_date_gmt'] = $child->textContent;
583
- break;
584
-
585
- case 'wp:comment_status':
586
- $data['comment_status'] = $child->textContent;
587
- break;
588
-
589
- case 'wp:ping_status':
590
- $data['ping_status'] = $child->textContent;
591
- break;
592
-
593
- case 'wp:post_name':
594
- $data['post_name'] = $child->textContent;
595
- break;
596
-
597
- case 'wp:status':
598
- $data['post_status'] = $child->textContent;
599
-
600
- if ( $data['post_status'] === 'auto-draft' ) {
601
- // Bail now
602
- return new WP_Error(
603
- 'wxr_importer.post.cannot_import_draft',
604
- __( 'Cannot import auto-draft posts', 'jupiterx-core' ),
605
- $data
606
- );
607
- }
608
- break;
609
-
610
- case 'wp:post_parent':
611
- $data['post_parent'] = $child->textContent;
612
- break;
613
-
614
- case 'wp:menu_order':
615
- $data['menu_order'] = $child->textContent;
616
- break;
617
-
618
- case 'wp:post_password':
619
- $data['post_password'] = $child->textContent;
620
- break;
621
-
622
- case 'wp:is_sticky':
623
- $data['is_sticky'] = $child->textContent;
624
- break;
625
-
626
- case 'wp:attachment_url':
627
- $data['attachment_url'] = $child->textContent;
628
- break;
629
-
630
- case 'wp:postmeta':
631
- $meta_item = $this->parse_meta_node( $child );
632
- if ( ! empty( $meta_item ) ) {
633
- $meta[] = $meta_item;
634
- }
635
- break;
636
-
637
- case 'wp:comment':
638
- $comment_item = $this->parse_comment_node( $child );
639
- if ( ! empty( $comment_item ) ) {
640
- $comments[] = $comment_item;
641
- }
642
- break;
643
-
644
- case 'category':
645
- $term_item = $this->parse_category_node( $child );
646
- if ( ! empty( $term_item ) ) {
647
- $terms[] = $term_item;
648
- }
649
- break;
650
- }
651
- }
652
-
653
- return compact( 'data', 'meta', 'comments', 'terms' );
654
- }
655
-
656
- /**
657
- * Create new posts based on import information
658
- *
659
- * Posts marked as having a parent which doesn't exist will become top level items.
660
- * Doesn't create a new post if: the post type doesn't exist, the given post ID
661
- * is already noted as imported or a post with the same title and date already exists.
662
- * Note that new/updated terms, comments and meta are imported for the last of the above.
663
- */
664
- protected function process_post( $data, $meta, $comments, $terms ) {
665
- /**
666
- * Pre-process post data.
667
- *
668
- * @param array $data Post data. (Return empty to skip.)
669
- * @param array $meta Meta data.
670
- * @param array $comments Comments on the post.
671
- * @param array $terms Terms on the post.
672
- */
673
- $data = apply_filters( 'wxr_importer.pre_process.post', $data, $meta, $comments, $terms );
674
- if ( empty( $data ) ) {
675
- return false;
676
- }
677
-
678
- $original_id = isset( $data['post_id'] ) ? (int) $data['post_id'] : 0;
679
- $parent_id = isset( $data['post_parent'] ) ? (int) $data['post_parent'] : 0;
680
- $author_id = isset( $data['post_author'] ) ? (int) $data['post_author'] : 0;
681
-
682
- // Have we already processed this?
683
- if ( isset( $this->mapping['post'][ $original_id ] ) ) {
684
- return;
685
- }
686
-
687
- $post_type_object = get_post_type_object( $data['post_type'] );
688
-
689
- // Is this type even valid?
690
- if ( ! $post_type_object ) {
691
- $this->logger->warning( sprintf(
692
- __( 'Failed to import "%s": Invalid post type %s', 'jupiterx-core' ),
693
- $data['post_title'],
694
- $data['post_type']
695
- ) );
696
- return false;
697
- }
698
-
699
- $post_exists = $this->post_exists( $data ) && ! $this->partial_import; // allow post duplication on partial import
700
- if ( $post_exists ) {
701
- $this->logger->info( sprintf(
702
- __( '%s "%s" already exists.', 'jupiterx-core' ),
703
- $post_type_object->labels->singular_name,
704
- $data['post_title']
705
- ) );
706
-
707
- /**
708
- * Post processing already imported.
709
- *
710
- * @param array $data Raw data imported for the post.
711
- */
712
- do_action( 'wxr_importer.process_already_imported.post', $data );
713
-
714
- // Even though this post already exists, new comments might need importing
715
- $this->process_comments( $comments, $original_id, $data, $post_exists );
716
-
717
- return false;
718
- }
719
-
720
- // Map the parent post, or mark it as one we need to fix
721
- $requires_remapping = false;
722
- if ( $parent_id ) {
723
- if ( isset( $this->mapping['post'][ $parent_id ] ) ) {
724
- $data['post_parent'] = $this->mapping['post'][ $parent_id ];
725
- } else {
726
- $meta[] = array( 'key' => '_wxr_import_parent', 'value' => $parent_id );
727
- $requires_remapping = true;
728
-
729
- $data['post_parent'] = 0;
730
- }
731
- }
732
-
733
- // Map the author, or mark it as one we need to fix
734
- $author = sanitize_user( $data['post_author'], true );
735
- if ( empty( $author ) ) {
736
- // Missing or invalid author, use default if available.
737
- $data['post_author'] = $this->options['default_author'];
738
- } elseif ( isset( $this->mapping['user_slug'][ $author ] ) ) {
739
- $data['post_author'] = $this->mapping['user_slug'][ $author ];
740
- } else {
741
- $meta[] = array( 'key' => '_wxr_import_user_slug', 'value' => $author );
742
- $requires_remapping = true;
743
-
744
- $data['post_author'] = (int) get_current_user_id();
745
- }
746
-
747
- // Does the post look like it contains attachment images?
748
- if ( preg_match( self::REGEX_HAS_ATTACHMENT_REFS, $data['post_content'] ) ) {
749
- $meta[] = array( 'key' => '_wxr_import_has_attachment_refs', 'value' => true );
750
- $requires_remapping = true;
751
- }
752
-
753
- // Whitelist to just the keys we allow
754
- $postdata = array(
755
- 'import_id' => $data['post_id'],
756
- );
757
- $allowed = array(
758
- 'post_author' => true,
759
- 'post_date' => true,
760
- 'post_date_gmt' => true,
761
- 'post_content' => true,
762
- 'post_excerpt' => true,
763
- 'post_title' => true,
764
- 'post_status' => true,
765
- 'post_name' => true,
766
- 'comment_status' => true,
767
- 'ping_status' => true,
768
- 'guid' => true,
769
- 'post_parent' => true,
770
- 'menu_order' => true,
771
- 'post_type' => true,
772
- 'post_password' => true,
773
- );
774
- foreach ( $data as $key => $value ) {
775
- if ( ! isset( $allowed[ $key ] ) ) {
776
- continue;
777
- }
778
-
779
- $postdata[ $key ] = $data[ $key ];
780
- }
781
-
782
- $postdata = apply_filters( 'wp_import_post_data_processed', $postdata, $data );
783
-
784
- if ( 'attachment' === $postdata['post_type'] ) {
785
- if ( ! $this->options['fetch_attachments'] ) {
786
- $this->logger->notice( sprintf(
787
- __( 'Skipping attachment "%s", fetching attachments disabled', 'jupiterx-core' ),
788
- $data['post_title']
789
- ) );
790
- /**
791
- * Post processing skipped.
792
- *
793
- * @param array $data Raw data imported for the post.
794
- * @param array $meta Raw meta data, already processed by {@see process_post_meta}.
795
- */
796
- do_action( 'wxr_importer.process_skipped.post', $data, $meta );
797
- return false;
798
- }
799
- $remote_url = ! empty( $data['attachment_url'] ) ? $data['attachment_url'] : $data['guid'];
800
- $post_id = $this->process_attachment( $postdata, $meta, $remote_url );
801
- } else {
802
- $post_id = wp_insert_post( $postdata, true );
803
- do_action( 'wp_import_insert_post', $post_id, $original_id, $postdata, $data );
804
- }
805
-
806
- if ( is_wp_error( $post_id ) ) {
807
- $this->logger->error( sprintf(
808
- __( 'Failed to import "%s" (%s)', 'jupiterx-core' ),
809
- $data['post_title'],
810
- $post_type_object->labels->singular_name
811
- ) );
812
- $this->logger->debug( $post_id->get_error_message() );
813
-
814
- /**
815
- * Post processing failed.
816
- *
817
- * @param WP_Error $post_id Error object.
818
- * @param array $data Raw data imported for the post.
819
- * @param array $meta Raw meta data, already processed by {@see process_post_meta}.
820
- * @param array $comments Raw comment data, already processed by {@see process_comments}.
821
- * @param array $terms Raw term data, already processed.
822
- */
823
- do_action( 'wxr_importer.process_failed.post', $post_id, $data, $meta, $comments, $terms );
824
- return false;
825
- }
826
-
827
- // Ensure stickiness is handled correctly too
828
- if ( $data['is_sticky'] === '1' ) {
829
- stick_post( $post_id );
830
- }
831
-
832
- // map pre-import ID to local ID
833
- $this->mapping['post'][ $original_id ] = (int) $post_id;
834
- if ( $requires_remapping ) {
835
- $this->requires_remapping['post'][ $post_id ] = true;
836
- }
837
- $this->mark_post_exists( $data, $post_id );
838
-
839
- $this->logger->info( sprintf(
840
- __( 'Imported "%s" (%s)', 'jupiterx-core' ),
841
- $data['post_title'],
842
- $post_type_object->labels->singular_name
843
- ) );
844
- $this->logger->debug( sprintf(
845
- __( 'Post %d remapped to %d', 'jupiterx-core' ),
846
- $original_id,
847
- $post_id
848
- ) );
849
-
850
- // Handle the terms too
851
- $terms = apply_filters( 'wp_import_post_terms', $terms, $post_id, $data );
852
-
853
- if ( ! empty( $terms ) ) {
854
- $term_ids = array();
855
- foreach ( $terms as $term ) {
856
- $taxonomy = $term['taxonomy'];
857
- $key = sha1( $taxonomy . ':' . $term['slug'] );
858
-
859
- if ( isset( $this->mapping['term'][ $key ] ) ) {
860
- $term_ids[ $taxonomy ][] = (int) $this->mapping['term'][ $key ];
861
- } else {
862
- $meta[] = array( 'key' => '_wxr_import_term', 'value' => $term );
863
- $requires_remapping = true;
864
- }
865
- }
866
-
867
- foreach ( $term_ids as $tax => $ids ) {
868
- $tt_ids = wp_set_post_terms( $post_id, $ids, $tax );
869
- do_action( 'wp_import_set_post_terms', $tt_ids, $ids, $tax, $post_id, $data );
870
- }
871
- }
872
-
873
- $this->process_comments( $comments, $post_id, $data );
874
- $this->process_post_meta( $meta, $post_id, $data );
875
-
876
- if ( 'nav_menu_item' === $data['post_type'] ) {
877
- $this->process_menu_item_meta( $post_id, $data, $meta );
878
- }
879
-
880
- /**
881
- * Post processing completed.
882
- *
883
- * @param int $post_id New post ID.
884
- * @param array $data Raw data imported for the post.
885
- * @param array $meta Raw meta data, already processed by {@see process_post_meta}.
886
- * @param array $comments Raw comment data, already processed by {@see process_comments}.
887
- * @param array $terms Raw term data, already processed.
888
- */
889
- do_action( 'wxr_importer.processed.post', $post_id, $data, $meta, $comments, $terms );
890
- }
891
-
892
- /**
893
- * Attempt to create a new menu item from import data
894
- *
895
- * Fails for draft, orphaned menu items and those without an associated nav_menu
896
- * or an invalid nav_menu term. If the post type or term object which the menu item
897
- * represents doesn't exist then the menu item will not be imported (waits until the
898
- * end of the import to retry again before discarding).
899
- *
900
- * @param array $item Menu item details from WXR file
901
- */
902
- protected function process_menu_item_meta( $post_id, $data, $meta ) {
903
-
904
- $item_type = get_post_meta( $post_id, '_menu_item_type', true );
905
- $original_object_id = get_post_meta( $post_id, '_menu_item_object_id', true );
906
- $object_id = null;
907
-
908
- $this->logger->debug( sprintf( 'Processing menu item %s', $item_type ) );
909
-
910
- $requires_remapping = false;
911
- switch ( $item_type ) {
912
- case 'taxonomy':
913
- if ( isset( $this->mapping['term_id'][ $original_object_id ] ) ) {
914
- $object_id = $this->mapping['term_id'][ $original_object_id ];
915
- } else {
916
- add_post_meta( $post_id, '_wxr_import_menu_item', wp_slash( $original_object_id ) );
917
- $requires_remapping = true;
918
- }
919
- break;
920
-
921
- case 'post_type':
922
- if ( isset( $this->mapping['post'][ $original_object_id ] ) ) {
923
- $object_id = $this->mapping['post'][ $original_object_id ];
924
- } else {
925
- add_post_meta( $post_id, '_wxr_import_menu_item', wp_slash( $original_object_id ) );
926
- $requires_remapping = true;
927
- }
928
- break;
929
-
930
- case 'custom':
931
- // Custom refers to itself, wonderfully easy.
932
- $object_id = $post_id;
933
- break;
934
-
935
- default:
936
- // associated object is missing or not imported yet, we'll retry later
937
- $this->missing_menu_items[] = $item;
938
- $this->logger->debug( 'Unknown menu item type' );
939
- break;
940
- }
941
-
942
- if ( $requires_remapping ) {
943
- $this->requires_remapping['post'][ $post_id ] = true;
944
- }
945
-
946
- if ( empty( $object_id ) ) {
947
- // Nothing needed here.
948
- return;
949
- }
950
-
951
- $this->logger->debug( sprintf( 'Menu item %d mapped to %d', $original_object_id, $object_id ) );
952
- update_post_meta( $post_id, '_menu_item_object_id', wp_slash( $object_id ) );
953
- }
954
-
955
- /**
956
- * If fetching attachments is enabled then attempt to create a new attachment
957
- *
958
- * @param array $post Attachment post details from WXR
959
- * @param string $url URL to fetch attachment from
960
- * @return int|WP_Error Post ID on success, WP_Error otherwise
961
- */
962
- protected function process_attachment( $post, $meta, $remote_url ) {
963
- // try to use _wp_attached file for upload folder placement to ensure the same location as the export site
964
- // e.g. location is 2003/05/image.jpg but the attachment post_date is 2010/09, see media_handle_upload()
965
- $post['upload_date'] = $post['post_date'];
966
- foreach ( $meta as $meta_item ) {
967
- if ( $meta_item['key'] !== '_wp_attached_file' ) {
968
- continue;
969
- }
970
-
971
- if ( preg_match( '%^[0-9]{4}/[0-9]{2}%', $meta_item['value'], $matches ) ) {
972
- $post['upload_date'] = $matches[0];
973
- }
974
- break;
975
- }
976
-
977
- // if the URL is absolute, but does not contain address, then upload it assuming base_site_url
978
- if ( preg_match( '|^/[\w\W]+$|', $remote_url ) ) {
979
- $remote_url = rtrim( $this->base_url, '/' ) . $remote_url;
980
- }
981
-
982
- $upload = $this->fetch_remote_file( $remote_url, $post );
983
- if ( is_wp_error( $upload ) ) {
984
- return $upload;
985
- }
986
-
987
- $info = wp_check_filetype( $upload['file'] );
988
- if ( ! $info ) {
989
- return new WP_Error( 'attachment_processing_error', __( 'Invalid file type', 'jupiterx-core' ) );
990
- }
991
-
992
- $post['post_mime_type'] = $info['type'];
993
-
994
- // WP really likes using the GUID for display. Allow updating it.
995
- // See https://core.trac.wordpress.org/ticket/33386
996
- if ( $this->options['update_attachment_guids'] ) {
997
- $post['guid'] = $upload['url'];
998
- }
999
-
1000
- // as per wp-admin/includes/upload.php
1001
- $post_id = wp_insert_attachment( $post, $upload['file'] );
1002
- if ( is_wp_error( $post_id ) ) {
1003
- return $post_id;
1004
- }
1005
-
1006
- $attachment_metadata = wp_generate_attachment_metadata( $post_id, $upload['file'] );
1007
- wp_update_attachment_metadata( $post_id, $attachment_metadata );
1008
-
1009
- // Map this image URL later if we need to
1010
- $this->url_remap[ $remote_url ] = $upload['url'];
1011
-
1012
- // If we have a HTTPS URL, ensure the HTTP URL gets replaced too
1013
- if ( substr( $remote_url, 0, 8 ) === 'https://' ) {
1014
- $insecure_url = 'http' . substr( $remote_url, 5 );
1015
- $this->url_remap[ $insecure_url ] = $upload['url'];
1016
- }
1017
-
1018
- if ( $this->options['aggressive_url_search'] ) {
1019
- // remap resized image URLs, works by stripping the extension and remapping the URL stub.
1020
- /*if ( preg_match( '!^image/!', $info['type'] ) ) {
1021
- $parts = pathinfo( $remote_url );
1022
- $name = basename( $parts['basename'], ".{$parts['extension']}" ); // PATHINFO_FILENAME in PHP 5.2
1023
-
1024
- $parts_new = pathinfo( $upload['url'] );
1025
- $name_new = basename( $parts_new['basename'], ".{$parts_new['extension']}" );
1026
-
1027
- $this->url_remap[$parts['dirname'] . '/' . $name] = $parts_new['dirname'] . '/' . $name_new;
1028
- }*/
1029
- }
1030
-
1031
- return $post_id;
1032
- }
1033
-
1034
- /**
1035
- * Parse a meta node into meta data.
1036
- *
1037
- * @param DOMElement $node Parent node of meta data (typically `wp:postmeta` or `wp:commentmeta`).
1038
- * @return array|null Meta data array on success, or null on error.
1039
- */
1040
- protected function parse_meta_node( $node ) {
1041
- foreach ( $node->childNodes as $child ) {
1042
- // We only care about child elements
1043
- if ( $child->nodeType !== XML_ELEMENT_NODE ) {
1044
- continue;
1045
- }
1046
-
1047
- switch ( $child->tagName ) {
1048
- case 'wp:meta_key':
1049
- $key = $child->textContent;
1050
- break;
1051
-
1052
- case 'wp:meta_value':
1053
- $value = $child->textContent;
1054
- break;
1055
- }
1056
- }
1057
-
1058
- if ( empty( $key ) ) {
1059
- return null;
1060
- }
1061
-
1062
- return compact( 'key', 'value' );
1063
- }
1064
-
1065
- /**
1066
- * Process and import post meta items.
1067
- *
1068
- * @param array $meta List of meta data arrays
1069
- * @param int $post_id Post to associate with
1070
- * @param array $post Post data
1071
- * @return int|WP_Error Number of meta items imported on success, error otherwise.
1072
- */
1073
- protected function process_post_meta( $meta, $post_id, $post ) {
1074
- if ( empty( $meta ) ) {
1075
- return true;
1076
- }
1077
-
1078
- foreach ( $meta as $meta_item ) {
1079
- /**
1080
- * Pre-process post meta data.
1081
- *
1082
- * @param array $meta_item Meta data. (Return empty to skip.)
1083
- * @param int $post_id Post the meta is attached to.
1084
- */
1085
- $meta_item = apply_filters( 'wxr_importer.pre_process.post_meta', $meta_item, $post_id );
1086
- if ( empty( $meta_item ) ) {
1087
- return false;
1088
- }
1089
-
1090
- $key = apply_filters( 'import_post_meta_key', $meta_item['key'], $post_id, $post );
1091
- $value = false;
1092
-
1093
- if ( '_edit_last' === $key ) {
1094
- $value = intval( $meta_item['value'] );
1095
- if ( ! isset( $this->mapping['user'][ $value ] ) ) {
1096
- // Skip!
1097
- continue;
1098
- }
1099
-
1100
- $value = $this->mapping['user'][ $value ];
1101
- }
1102
-
1103
- if ( $key ) {
1104
- // export gets meta straight from the DB so could have a serialized string
1105
- if ( ! $value ) {
1106
- $value = maybe_unserialize( $meta_item['value'] );
1107
- }
1108
-
1109
- add_post_meta( $post_id, $key, $value );
1110
- do_action( 'import_post_meta', $post_id, $key, $value );
1111
-
1112
- // if the post has a featured image, take note of this in case of remap
1113
- if ( '_thumbnail_id' === $key ) {
1114
- $this->featured_images[ $post_id ] = (int) $value;
1115
- }
1116
- }
1117
- }
1118
-
1119
- return true;
1120
- }
1121
-
1122
- /**
1123
- * Parse a comment node into comment data.
1124
- *
1125
- * @param DOMElement $node Parent node of comment data (typically `wp:comment`).
1126
- * @return array Comment data array.
1127
- */
1128
- protected function parse_comment_node( $node ) {
1129
- $data = array(
1130
- 'commentmeta' => array(),
1131
- );
1132
-
1133
- foreach ( $node->childNodes as $child ) {
1134
- // We only care about child elements
1135
- if ( $child->nodeType !== XML_ELEMENT_NODE ) {
1136
- continue;
1137
- }
1138
-
1139
- switch ( $child->tagName ) {
1140
- case 'wp:comment_id':
1141
- $data['comment_id'] = $child->textContent;
1142
- break;
1143
- case 'wp:comment_author':
1144
- $data['comment_author'] = $child->textContent;
1145
- break;
1146
-
1147
- case 'wp:comment_author_email':
1148
- $data['comment_author_email'] = $child->textContent;
1149
- break;
1150
-
1151
- case 'wp:comment_author_IP':
1152
- $data['comment_author_IP'] = $child->textContent;
1153
- break;
1154
-
1155
- case 'wp:comment_author_url':
1156
- $data['comment_author_url'] = $child->textContent;
1157
- break;
1158
-
1159
- case 'wp:comment_user_id':
1160
- $data['comment_user_id'] = $child->textContent;
1161
- break;
1162
-
1163
- case 'wp:comment_date':
1164
- $data['comment_date'] = $child->textContent;
1165
- break;
1166
-
1167
- case 'wp:comment_date_gmt':
1168
- $data['comment_date_gmt'] = $child->textContent;
1169
- break;
1170
-
1171
- case 'wp:comment_content':
1172
- $data['comment_content'] = $child->textContent;
1173
- break;
1174
-
1175
- case 'wp:comment_approved':
1176
- $data['comment_approved'] = $child->textContent;
1177
- break;
1178
-
1179
- case 'wp:comment_type':
1180
- $data['comment_type'] = $child->textContent;
1181
- break;
1182
-
1183
- case 'wp:comment_parent':
1184
- $data['comment_parent'] = $child->textContent;
1185
- break;
1186
-
1187
- case 'wp:commentmeta':
1188
- $meta_item = $this->parse_meta_node( $child );
1189
- if ( ! empty( $meta_item ) ) {
1190
- $data['commentmeta'][] = $meta_item;
1191
- }
1192
- break;
1193
- }
1194
- }
1195
-
1196
- return $data;
1197
- }
1198
-
1199
- /**
1200
- * Process and import comment data.
1201
- *
1202
- * @param array $comments List of comment data arrays.
1203
- * @param int $post_id Post to associate with.
1204
- * @param array $post Post data.
1205
- * @return int|WP_Error Number of comments imported on success, error otherwise.
1206
- */
1207
- protected function process_comments( $comments, $post_id, $post, $post_exists = false ) {
1208
-
1209
- $comments = apply_filters( 'wp_import_post_comments', $comments, $post_id, $post );
1210
- if ( empty( $comments ) ) {
1211
- return 0;
1212
- }
1213
-
1214
- $num_comments = 0;
1215
-
1216
- // Sort by ID to avoid excessive remapping later
1217
- usort( $comments, array( $this, 'sort_comments_by_id' ) );
1218
-
1219
- foreach ( $comments as $key => $comment ) {
1220
- /**
1221
- * Pre-process comment data
1222
- *
1223
- * @param array $comment Comment data. (Return empty to skip.)
1224
- * @param int $post_id Post the comment is attached to.
1225
- */
1226
- $comment = apply_filters( 'wxr_importer.pre_process.comment', $comment, $post_id );
1227
- if ( empty( $comment ) ) {
1228
- return false;
1229
- }
1230
-
1231
- $original_id = isset( $comment['comment_id'] ) ? (int) $comment['comment_id'] : 0;
1232
- $parent_id = isset( $comment['comment_parent'] ) ? (int) $comment['comment_parent'] : 0;
1233
- $author_id = isset( $comment['comment_user_id'] ) ? (int) $comment['comment_user_id'] : 0;
1234
-
1235
- // if this is a new post we can skip the comment_exists() check
1236
- // TODO: Check comment_exists for performance
1237
- if ( $post_exists ) {
1238
- $existing = $this->comment_exists( $comment );
1239
- if ( $existing ) {
1240
-
1241
- /**
1242
- * Comment processing already imported.
1243
- *
1244
- * @param array $comment Raw data imported for the comment.
1245
- */
1246
- do_action( 'wxr_importer.process_already_imported.comment', $comment );
1247
-
1248
- $this->mapping['comment'][ $original_id ] = $existing;
1249
- continue;
1250
- }
1251
- }
1252
-
1253
- // Remove meta from the main array
1254
- $meta = isset( $comment['commentmeta'] ) ? $comment['commentmeta'] : array();
1255
- unset( $comment['commentmeta'] );
1256
-
1257
- // Map the parent comment, or mark it as one we need to fix
1258
- $requires_remapping = false;
1259
- if ( $parent_id ) {
1260
- if ( isset( $this->mapping['comment'][ $parent_id ] ) ) {
1261
- $comment['comment_parent'] = $this->mapping['comment'][ $parent_id ];
1262
- } else {
1263
- // Prepare for remapping later
1264
- $meta[] = array( 'key' => '_wxr_import_parent', 'value' => $parent_id );
1265
- $requires_remapping = true;
1266
-
1267
- // Wipe the parent for now
1268
- $comment['comment_parent'] = 0;
1269
- }
1270
- }
1271
-
1272
- // Map the author, or mark it as one we need to fix
1273
- if ( $author_id ) {
1274
- if ( isset( $this->mapping['user'][ $author_id ] ) ) {
1275
- $comment['user_id'] = $this->mapping['user'][ $author_id ];
1276
- } else {
1277
- // Prepare for remapping later
1278
- $meta[] = array( 'key' => '_wxr_import_user', 'value' => $author_id );
1279
- $requires_remapping = true;
1280
-
1281
- // Wipe the user for now
1282
- $comment['user_id'] = 0;
1283
- }
1284
- }
1285
-
1286
- // Run standard core filters
1287
- $comment['comment_post_ID'] = $post_id;
1288
- $comment = wp_filter_comment( $comment );
1289
-
1290
- // wp_insert_comment expects slashed data
1291
- $comment_id = wp_insert_comment( wp_slash( $comment ) );
1292
- $this->mapping['comment'][ $original_id ] = $comment_id;
1293
- if ( $requires_remapping ) {
1294
- $this->requires_remapping['comment'][ $comment_id ] = true;
1295
- }
1296
- $this->mark_comment_exists( $comment, $comment_id );
1297
-
1298
- /**
1299
- * Comment has been imported.
1300
- *
1301
- * @param int $comment_id New comment ID
1302
- * @param array $comment Comment inserted (`comment_id` item refers to the original ID)
1303
- * @param int $post_id Post parent of the comment
1304
- * @param array $post Post data
1305
- */
1306
- do_action( 'wp_import_insert_comment', $comment_id, $comment, $post_id, $post );
1307
-
1308
- // Process the meta items
1309
- foreach ( $meta as $meta_item ) {
1310
- $value = maybe_unserialize( $meta_item['value'] );
1311
- add_comment_meta( $comment_id, wp_slash( $meta_item['key'] ), wp_slash( $value ) );
1312
- }
1313
-
1314
- /**
1315
- * Post processing completed.
1316
- *
1317
- * @param int $post_id New post ID.
1318
- * @param array $comment Raw data imported for the comment.
1319
- * @param array $meta Raw meta data, already processed by {@see process_post_meta}.
1320
- * @param array $post_id Parent post ID.
1321
- */
1322
- do_action( 'wxr_importer.processed.comment', $comment_id, $comment, $meta, $post_id );
1323
-
1324
- $num_comments++;
1325
- }
1326
-
1327
- return $num_comments;
1328
- }
1329
-
1330
- protected function parse_category_node( $node ) {
1331
- $data = array(
1332
- // Default taxonomy to "category", since this is a `<category>` tag
1333
- 'taxonomy' => 'category',
1334
- );
1335
- $meta = array();
1336
-
1337
- if ( $node->hasAttribute( 'domain' ) ) {
1338
- $data['taxonomy'] = $node->getAttribute( 'domain' );
1339
- }
1340
- if ( $node->hasAttribute( 'nicename' ) ) {
1341
- $data['slug'] = $node->getAttribute( 'nicename' );
1342
- }
1343
-
1344
- $data['name'] = $node->textContent;
1345
-
1346
- if ( empty( $data['slug'] ) ) {
1347
- return null;
1348
- }
1349
-
1350
- // Just for extra compatibility
1351
- if ( $data['taxonomy'] === 'tag' ) {
1352
- $data['taxonomy'] = 'post_tag';
1353
- }
1354
-
1355
- return $data;
1356
- }
1357
-
1358
- /**
1359
- * Callback for `usort` to sort comments by ID
1360
- *
1361
- * @param array $a Comment data for the first comment
1362
- * @param array $b Comment data for the second comment
1363
- * @return int
1364
- */
1365
- public static function sort_comments_by_id( $a, $b ) {
1366
- if ( empty( $a['comment_id'] ) ) {
1367
- return 1;
1368
- }
1369
-
1370
- if ( empty( $b['comment_id'] ) ) {
1371
- return -1;
1372
- }
1373
-
1374
- return $a['comment_id'] - $b['comment_id'];
1375
- }
1376
-
1377
- protected function parse_term_node( $node, $type = 'term' ) {
1378
- $data = array();
1379
- $meta = array();
1380
-
1381
- $tag_name = array(
1382
- 'id' => 'wp:term_id',
1383
- 'taxonomy' => 'wp:term_taxonomy',
1384
- 'slug' => 'wp:term_slug',
1385
- 'parent' => 'wp:term_parent',
1386
- 'name' => 'wp:term_name',
1387
- 'description' => 'wp:term_description',
1388
- );
1389
- $taxonomy = null;
1390
-
1391
- // Special casing!
1392
- switch ( $type ) {
1393
- case 'category':
1394
- $tag_name['slug'] = 'wp:category_nicename';
1395
- $tag_name['parent'] = 'wp:category_parent';
1396
- $tag_name['name'] = 'wp:cat_name';
1397
- $tag_name['description'] = 'wp:category_description';
1398
- $tag_name['taxonomy'] = null;
1399
-
1400
- $data['taxonomy'] = 'category';
1401
- break;
1402
-
1403
- case 'tag':
1404
- $tag_name['slug'] = 'wp:tag_slug';
1405
- $tag_name['parent'] = null;
1406
- $tag_name['name'] = 'wp:tag_name';
1407
- $tag_name['description'] = 'wp:tag_description';
1408
- $tag_name['taxonomy'] = null;
1409
-
1410
- $data['taxonomy'] = 'post_tag';
1411
- break;
1412
- }
1413
-
1414
- foreach ( $node->childNodes as $child ) {
1415
- // We only care about child elements
1416
- if ( $child->nodeType !== XML_ELEMENT_NODE ) {
1417
- continue;
1418
- }
1419
-
1420
- $key = array_search( $child->tagName, $tag_name );
1421
- if ( $key ) {
1422
- $data[ $key ] = $child->textContent;
1423
- }
1424
- }
1425
-
1426
- if ( empty( $data['taxonomy'] ) ) {
1427
- return null;
1428
- }
1429
-
1430
- // Compatibility with WXR 1.0
1431
- if ( $data['taxonomy'] === 'tag' ) {
1432
- $data['taxonomy'] = 'post_tag';
1433
- }
1434
-
1435
- return compact( 'data', 'meta' );
1436
- }
1437
-
1438
- protected function process_term( $data, $meta ) {
1439
- /**
1440
- * Pre-process term data.
1441
- *
1442
- * @param array $data Term data. (Return empty to skip.)
1443
- * @param array $meta Meta data.
1444
- */
1445
- $data = apply_filters( 'wxr_importer.pre_process.term', $data, $meta );
1446
- if ( empty( $data ) ) {
1447
- return false;
1448
- }
1449
-
1450
- $original_id = isset( $data['id'] ) ? (int) $data['id'] : 0;
1451
- $parent_id = isset( $data['parent'] ) ? (int) $data['parent'] : 0;
1452
-
1453
- $mapping_key = sha1( $data['taxonomy'] . ':' . $data['slug'] );
1454
- $existing = $this->term_exists( $data );
1455
- if ( $existing ) {
1456
-
1457
- /**
1458
- * Term processing already imported.
1459
- *
1460
- * @param array $data Raw data imported for the term.
1461
- */
1462
- do_action( 'wxr_importer.process_already_imported.term', $data );
1463
-
1464
- $this->mapping['term'][ $mapping_key ] = $existing;
1465
- $this->mapping['term_id'][ $original_id ] = $existing;
1466
- return false;
1467
- }
1468
-
1469
- // WP really likes to repeat itself in export files
1470
- if ( isset( $this->mapping['term'][ $mapping_key ] ) ) {
1471
- return false;
1472
- }
1473
-
1474
- $termdata = array();
1475
- $allowed = array(
1476
- 'slug' => true,
1477
- 'description' => true,
1478
- );
1479
-
1480
- // Map the parent comment, or mark it as one we need to fix
1481
- // TODO: add parent mapping and remapping
1482
- /*$requires_remapping = false;
1483
- if ( $parent_id ) {
1484
- if ( isset( $this->mapping['term'][ $parent_id ] ) ) {
1485
- $data['parent'] = $this->mapping['term'][ $parent_id ];
1486
- } else {
1487
- // Prepare for remapping later
1488
- $meta[] = array( 'key' => '_wxr_import_parent', 'value' => $parent_id );
1489
- $requires_remapping = true;
1490
-
1491
- // Wipe the parent for now
1492
- $data['parent'] = 0;
1493
- }
1494
- }*/
1495
-
1496
- foreach ( $data as $key => $value ) {
1497
- if ( ! isset( $allowed[ $key ] ) ) {
1498
- continue;
1499
- }
1500
-
1501
- $termdata[ $key ] = $data[ $key ];
1502
- }
1503
-
1504
- $result = wp_insert_term( $data['name'], $data['taxonomy'], $termdata );
1505
- if ( is_wp_error( $result ) ) {
1506
- $this->logger->warning( sprintf(
1507
- __( 'Failed to import %s %s', 'jupiterx-core' ),
1508
- $data['taxonomy'],
1509
- $data['name']
1510
- ) );
1511
- $this->logger->debug( $result->get_error_message() );
1512
- do_action( 'wp_import_insert_term_failed', $result, $data );
1513
-
1514
- /**
1515
- * Term processing failed.
1516
- *
1517
- * @param WP_Error $result Error object.
1518
- * @param array $data Raw data imported for the term.
1519
- * @param array $meta Meta data supplied for the term.
1520
- */
1521
- do_action( 'wxr_importer.process_failed.term', $result, $data, $meta );
1522
- return false;
1523
- }
1524
-
1525
- $term_id = $result['term_id'];
1526
-
1527
- $this->mapping['term'][ $mapping_key ] = $term_id;
1528
- $this->mapping['term_id'][ $original_id ] = $term_id;
1529
-
1530
- $this->logger->info( sprintf(
1531
- __( 'Imported "%s" (%s)', 'jupiterx-core' ),
1532
- $data['name'],
1533
- $data['taxonomy']
1534
- ) );
1535
- $this->logger->debug( sprintf(
1536
- __( 'Term %d remapped to %d', 'jupiterx-core' ),
1537
- $original_id,
1538
- $term_id
1539
- ) );
1540
-
1541
- do_action( 'wp_import_insert_term', $term_id, $data );
1542
-
1543
- /**
1544
- * Term processing completed.
1545
- *
1546
- * @param int $term_id New term ID.
1547
- * @param array $data Raw data imported for the term.
1548
- */
1549
- do_action( 'wxr_importer.processed.term', $term_id, $data );
1550
- }
1551
-
1552
- /**
1553
- * Attempt to download a remote file attachment
1554
- *
1555
- * @param string $url URL of item to fetch
1556
- * @param array $post Attachment details
1557
- * @return array|WP_Error Local file location details on success, WP_Error otherwise
1558
- */
1559
- protected function fetch_remote_file( $url, $post ) {
1560
- // extract the file name and extension from the url
1561
- $file_name = basename( $url );
1562
-
1563
- // get placeholder file in the upload dir with a unique, sanitized filename
1564
- $upload = wp_upload_bits( $file_name, 0, '', $post['upload_date'] );
1565
- if ( $upload['error'] ) {
1566
- return new WP_Error( 'upload_dir_error', $upload['error'] );
1567
- }
1568
-
1569
- // fetch the remote url and write it to the placeholder file
1570
- $response = wp_remote_get( $url, array(
1571
- 'stream' => true,
1572
- 'filename' => $upload['file'],
1573
- ) );
1574
-
1575
- // request failed
1576
- if ( is_wp_error( $response ) ) {
1577
- unlink( $upload['file'] );
1578
- return $response;
1579
- }
1580
-
1581
- $code = (int) wp_remote_retrieve_response_code( $response );
1582
-
1583
- // make sure the fetch was successful
1584
- if ( $code !== 200 ) {
1585
- unlink( $upload['file'] );
1586
- return new WP_Error(
1587
- 'import_file_error',
1588
- sprintf(
1589
- __( 'Remote server returned %1$d %2$s for %3$s', 'jupiterx-core' ),
1590
- $code,
1591
- get_status_header_desc( $code ),
1592
- $url
1593
- )
1594
- );
1595
- }
1596
-
1597
- $filesize = filesize( $upload['file'] );
1598
- $headers = wp_remote_retrieve_headers( $response );
1599
-
1600
- if ( isset( $headers['content-length'] ) && $filesize !== (int) $headers['content-length'] ) {
1601
- unlink( $upload['file'] );
1602
- return new WP_Error( 'import_file_error', __( 'Remote file is incorrect size', 'jupiterx-core' ) );
1603
- }
1604
-
1605
- if ( 0 === $filesize ) {
1606
- unlink( $upload['file'] );
1607
- return new WP_Error( 'import_file_error', __( 'Zero size file downloaded', 'jupiterx-core' ) );
1608
- }
1609
-
1610
- $max_size = (int) $this->max_attachment_size();
1611
- if ( ! empty( $max_size ) && $filesize > $max_size ) {
1612
- unlink( $upload['file'] );
1613
- $message = sprintf( __( 'Remote file is too large, limit is %s', 'jupiterx-core' ), size_format( $max_size ) );
1614
- return new WP_Error( 'import_file_error', $message );
1615
- }
1616
-
1617
- return $upload;
1618
- }
1619
-
1620
- protected function post_process() {
1621
- // Time to tackle any left-over bits
1622
- if ( ! empty( $this->requires_remapping['post'] ) ) {
1623
- $this->post_process_posts( $this->requires_remapping['post'] );
1624
- }
1625
- if ( ! empty( $this->requires_remapping['comment'] ) ) {
1626
- $this->post_process_comments( $this->requires_remapping['comment'] );
1627
- }
1628
- }
1629
-
1630
- protected function post_process_posts( $todo ) {
1631
- foreach ( $todo as $post_id => $_ ) {
1632
- $this->logger->debug( sprintf(
1633
- // Note: title intentionally not used to skip extra processing
1634
- // for when debug logging is off
1635
- __( 'Running post-processing for post %d', 'jupiterx-core' ),
1636
- $post_id
1637
- ) );
1638
-
1639
- $data = array();
1640
-
1641
- $parent_id = get_post_meta( $post_id, '_wxr_import_parent', true );
1642
- if ( ! empty( $parent_id ) ) {
1643
- // Have we imported the parent now?
1644
- if ( isset( $this->mapping['post'][ $parent_id ] ) ) {
1645
- $data['post_parent'] = $this->mapping['post'][ $parent_id ];
1646
- } else {
1647
- $this->logger->warning( sprintf(
1648
- __( 'Could not find the post parent for "%s" (post #%d)', 'jupiterx-core' ),
1649
- get_the_title( $post_id ),
1650
- $post_id
1651
- ) );
1652
- $this->logger->debug( sprintf(
1653
- __( 'Post %d was imported with parent %d, but could not be found', 'jupiterx-core' ),
1654
- $post_id,
1655
- $parent_id
1656
- ) );
1657
- }
1658
- }
1659
-
1660
- $author_slug = get_post_meta( $post_id, '_wxr_import_user_slug', true );
1661
- if ( ! empty( $author_slug ) ) {
1662
- // Have we imported the user now?
1663
- if ( isset( $this->mapping['user_slug'][ $author_slug ] ) ) {
1664
- $data['post_author'] = $this->mapping['user_slug'][ $author_slug ];
1665
- } else {
1666
- $this->logger->warning( sprintf(
1667
- __( 'Could not find the author for "%s" (post #%d)', 'jupiterx-core' ),
1668
- get_the_title( $post_id ),
1669
- $post_id
1670
- ) );
1671
- $this->logger->debug( sprintf(
1672
- __( 'Post %d was imported with author "%s", but could not be found', 'jupiterx-core' ),
1673
- $post_id,
1674
- $author_slug
1675
- ) );
1676
- }
1677
- }
1678
-
1679
- $has_attachments = get_post_meta( $post_id, '_wxr_import_has_attachment_refs', true );
1680
- if ( ! empty( $has_attachments ) ) {
1681
- $post = get_post( $post_id );
1682
- $content = $post->post_content;
1683
-
1684
- // Replace all the URLs we've got
1685
- $new_content = str_replace( array_keys( $this->url_remap ), $this->url_remap, $content );
1686
- if ( $new_content !== $content ) {
1687
- $data['post_content'] = $new_content;
1688
- }
1689
- }
1690
-
1691
- if ( get_post_type( $post_id ) === 'nav_menu_item' ) {
1692
- $this->post_process_menu_item( $post_id );
1693
- }
1694
-
1695
- // Do we have updates to make?
1696
- if ( empty( $data ) ) {
1697
- $this->logger->debug( sprintf(
1698
- __( 'Post %d was marked for post-processing, but none was required.', 'jupiterx-core' ),
1699
- $post_id
1700
- ) );
1701
- continue;
1702
- }
1703
-
1704
- // Run the update
1705
- $data['ID'] = $post_id;
1706
- $result = wp_update_post( $data, true );
1707
- if ( is_wp_error( $result ) ) {
1708
- $this->logger->warning( sprintf(
1709
- __( 'Could not update "%s" (post #%d) with mapped data', 'jupiterx-core' ),
1710
- get_the_title( $post_id ),
1711
- $post_id
1712
- ) );
1713
- $this->logger->debug( $result->get_error_message() );
1714
- continue;
1715
- }
1716
-
1717
- // Clear out our temporary meta keys
1718
- delete_post_meta( $post_id, '_wxr_import_parent' );
1719
- delete_post_meta( $post_id, '_wxr_import_user_slug' );
1720
- delete_post_meta( $post_id, '_wxr_import_has_attachment_refs' );
1721
- }
1722
- }
1723
-
1724
- protected function post_process_menu_item( $post_id ) {
1725
- $menu_object_id = get_post_meta( $post_id, '_wxr_import_menu_item', true );
1726
- if ( empty( $menu_object_id ) ) {
1727
- // No processing needed!
1728
- return;
1729
- }
1730
-
1731
- $menu_item_type = get_post_meta( $post_id, '_menu_item_type', true );
1732
- switch ( $menu_item_type ) {
1733
- case 'taxonomy':
1734
- if ( isset( $this->mapping['term_id'][ $menu_object_id ] ) ) {
1735
- $menu_object = $this->mapping['term_id'][ $menu_object_id ];
1736
- }
1737
- break;
1738
-
1739
- case 'post_type':
1740
- if ( isset( $this->mapping['post'][ $menu_object_id ] ) ) {
1741
- $menu_object = $this->mapping['post'][ $menu_object_id ];
1742
- }
1743
- break;
1744
-
1745
- default:
1746
- // Cannot handle this.
1747
- return;
1748
- }
1749
-
1750
- if ( ! empty( $menu_object ) ) {
1751
- update_post_meta( $post_id, '_menu_item_object_id', wp_slash( $menu_object ) );
1752
- } else {
1753
- $this->logger->warning( sprintf(
1754
- __( 'Could not find the menu object for "%s" (post #%d)', 'jupiterx-core' ),
1755
- get_the_title( $post_id ),
1756
- $post_id
1757
- ) );
1758
- $this->logger->debug( sprintf(
1759
- __( 'Post %d was imported with object "%d" of type "%s", but could not be found', 'jupiterx-core' ),
1760
- $post_id,
1761
- $menu_object_id,
1762
- $menu_item_type
1763
- ) );
1764
- }
1765
-
1766
- delete_post_meta( $post_id, '_wxr_import_menu_item' );
1767
- }
1768
-
1769
-
1770
- protected function post_process_comments( $todo ) {
1771
- foreach ( $todo as $comment_id => $_ ) {
1772
- $data = array();
1773
-
1774
- $parent_id = get_comment_meta( $comment_id, '_wxr_import_parent', true );
1775
- if ( ! empty( $parent_id ) ) {
1776
- // Have we imported the parent now?
1777
- if ( isset( $this->mapping['comment'][ $parent_id ] ) ) {
1778
- $data['comment_parent'] = $this->mapping['comment'][ $parent_id ];
1779
- } else {
1780
- $this->logger->warning( sprintf(
1781
- __( 'Could not find the comment parent for comment #%d', 'jupiterx-core' ),
1782
- $comment_id
1783
- ) );
1784
- $this->logger->debug( sprintf(
1785
- __( 'Comment %d was imported with parent %d, but could not be found', 'jupiterx-core' ),
1786
- $comment_id,
1787
- $parent_id
1788
- ) );
1789
- }
1790
- }
1791
-
1792
- $author_id = get_comment_meta( $comment_id, '_wxr_import_user', true );
1793
- if ( ! empty( $author_id ) ) {
1794
- // Have we imported the user now?
1795
- if ( isset( $this->mapping['user'][ $author_id ] ) ) {
1796
- $data['user_id'] = $this->mapping['user'][ $author_id ];
1797
- } else {
1798
- $this->logger->warning( sprintf(
1799
- __( 'Could not find the author for comment #%d', 'jupiterx-core' ),
1800
- $comment_id
1801
- ) );
1802
- $this->logger->debug( sprintf(
1803
- __( 'Comment %d was imported with author %d, but could not be found', 'jupiterx-core' ),
1804
- $comment_id,
1805
- $author_id
1806
- ) );
1807
- }
1808
- }
1809
-
1810
- // Do we have updates to make?
1811
- if ( empty( $data ) ) {
1812
- continue;
1813
- }
1814
-
1815
- // Run the update
1816
- $data['comment_ID'] = $comment_ID;
1817
- $result = wp_update_comment( wp_slash( $data ) );
1818
- if ( empty( $result ) ) {
1819
- $this->logger->warning( sprintf(
1820
- __( 'Could not update comment #%d with mapped data', 'jupiterx-core' ),
1821
- $comment_id
1822
- ) );
1823
- continue;
1824
- }
1825
-
1826
- // Clear out our temporary meta keys
1827
- delete_comment_meta( $comment_id, '_wxr_import_parent' );
1828
- delete_comment_meta( $comment_id, '_wxr_import_user' );
1829
- }
1830
- }
1831
-
1832
- /**
1833
- * Use stored mapping information to update old attachment URLs
1834
- */
1835
- protected function replace_attachment_urls_in_content() {
1836
- global $wpdb;
1837
- // make sure we do the longest urls first, in case one is a substring of another
1838
- uksort( $this->url_remap, array( $this, 'cmpr_strlen' ) );
1839
-
1840
- foreach ( $this->url_remap as $from_url => $to_url ) {
1841
- // remap urls in post_content
1842
- $query = $wpdb->prepare( "UPDATE {$wpdb->posts} SET post_content = REPLACE(post_content, %s, %s)", $from_url, $to_url );
1843
- $wpdb->query( $query );
1844
-
1845
- // remap enclosure urls
1846
- $query = $wpdb->prepare( "UPDATE {$wpdb->postmeta} SET meta_value = REPLACE(meta_value, %s, %s) WHERE meta_key='enclosure'", $from_url, $to_url );
1847
- $result = $wpdb->query( $query );
1848
- }
1849
- }
1850
-
1851
- /**
1852
- * Update _thumbnail_id meta to new, imported attachment IDs
1853
- */
1854
- function remap_featured_images() {
1855
- // cycle through posts that have a featured image
1856
- foreach ( $this->featured_images as $post_id => $value ) {
1857
- if ( isset( $this->processed_posts[ $value ] ) ) {
1858
- $new_id = $this->processed_posts[ $value ];
1859
-
1860
- // only update if there's a difference
1861
- if ( $new_id !== $value ) {
1862
- update_post_meta( $post_id, '_thumbnail_id', $new_id );
1863
- }
1864
- }
1865
- }
1866
- }
1867
-
1868
- /**
1869
- * Decide if the given meta key maps to information we will want to import
1870
- *
1871
- * @param string $key The meta key to check
1872
- * @return string|bool The key if we do want to import, false if not
1873
- */
1874
- public function is_valid_meta_key( $key ) {
1875
- // skip attachment metadata since we'll regenerate it from scratch
1876
- // skip _edit_lock as not relevant for import
1877
- if ( in_array( $key, array( '_wp_attached_file', '_wp_attachment_metadata', '_edit_lock' ) ) ) {
1878
- return false;
1879
- }
1880
-
1881
- return $key;
1882
- }
1883
-
1884
- /**
1885
- * Decide what the maximum file size for downloaded attachments is.
1886
- * Default is 0 (unlimited), can be filtered via import_attachment_size_limit
1887
- *
1888
- * @return int Maximum attachment file size to import
1889
- */
1890
- protected function max_attachment_size() {
1891
- return apply_filters( 'import_attachment_size_limit', 0 );
1892
- }
1893
-
1894
- /**
1895
- * Added to http_request_timeout filter to force timeout at 60 seconds during import
1896
- *
1897
- * @access protected
1898
- * @return int 60
1899
- */
1900
- function bump_request_timeout($val) {
1901
- return 60;
1902
- }
1903
-
1904
- // return the difference in length between two strings
1905
- function cmpr_strlen( $a, $b ) {
1906
- return strlen( $b ) - strlen( $a );
1907
- }
1908
-
1909
- /**
1910
- * Prefill existing post data.
1911
- *
1912
- * This preloads all GUIDs into memory, allowing us to avoid hitting the
1913
- * database when we need to check for existence. With larger imports, this
1914
- * becomes prohibitively slow to perform SELECT queries on each.
1915
- *
1916
- * By preloading all this data into memory, it's a constant-time lookup in
1917
- * PHP instead. However, this does use a lot more memory, so for sites doing
1918
- * small imports onto a large site, it may be a better tradeoff to use
1919
- * on-the-fly checking instead.
1920
- */
1921
- protected function prefill_existing_posts() {
1922
- global $wpdb;
1923
- $posts = $wpdb->get_results( "SELECT ID, guid FROM {$wpdb->posts}" );
1924
-
1925
- foreach ( $posts as $item ) {
1926
- $this->exists['post'][ wp_specialchars_decode( $item->guid ) ] = $item->ID;
1927
- }
1928
- }
1929
-
1930
- /**
1931
- * Does the post exist?
1932
- *
1933
- * @param array $data Post data to check against.
1934
- * @return int|bool Existing post ID if it exists, false otherwise.
1935
- */
1936
- protected function post_exists( $data ) {
1937
- // Constant-time lookup if we prefilled
1938
- $exists_key = wp_specialchars_decode( $data['guid'] );
1939
-
1940
- if ( $this->options['prefill_existing_posts'] ) {
1941
- return isset( $this->exists['post'][ $exists_key ] ) ? $this->exists['post'][ $exists_key ] : false;
1942
- }
1943
-
1944
- // No prefilling, but might have already handled it
1945
- if ( isset( $this->exists['post'][ $exists_key ] ) ) {
1946
- return $this->exists['post'][ $exists_key ];
1947
- }
1948
-
1949
- // Still nothing, try post_exists, and cache it
1950
- $exists = post_exists( $data['post_title'], $data['post_content'], $data['post_date'] );
1951
- $this->exists['post'][ $exists_key ] = $exists;
1952
-
1953
- return $exists;
1954
- }
1955
-
1956
- /**
1957
- * Mark the post as existing.
1958
- *
1959
- * @param array $data Post data to mark as existing.
1960
- * @param int $post_id Post ID.
1961
- */
1962
- protected function mark_post_exists( $data, $post_id ) {
1963
- $exists_key = $data['guid'];
1964
- $this->exists['post'][ $exists_key ] = $post_id;
1965
- }
1966
-
1967
- /**
1968
- * Prefill existing comment data.
1969
- *
1970
- * @see self::prefill_existing_posts() for justification of why this exists.
1971
- */
1972
- protected function prefill_existing_comments() {
1973
- global $wpdb;
1974
- $posts = $wpdb->get_results( "SELECT comment_ID, comment_author, comment_date FROM {$wpdb->comments}" );
1975
-
1976
- foreach ( $posts as $item ) {
1977
- $exists_key = sha1( $item->comment_author . ':' . $item->comment_date );
1978
- $this->exists['comment'][ $exists_key ] = $item->comment_ID;
1979
- }
1980
- }
1981
-
1982
- /**
1983
- * Does the comment exist?
1984
- *
1985
- * @param array $data Comment data to check against.
1986
- * @return int|bool Existing comment ID if it exists, false otherwise.
1987
- */
1988
- protected function comment_exists( $data ) {
1989
- $exists_key = sha1( $data['comment_author'] . ':' . $data['comment_date'] );
1990
-
1991
- // Constant-time lookup if we prefilled
1992
- if ( $this->options['prefill_existing_comments'] ) {
1993
- return isset( $this->exists['comment'][ $exists_key ] ) ? $this->exists['comment'][ $exists_key ] : false;
1994
- }
1995
-
1996
- // No prefilling, but might have already handled it
1997
- if ( isset( $this->exists['comment'][ $exists_key ] ) ) {
1998
- return $this->exists['comment'][ $exists_key ];
1999
- }
2000
-
2001
- // Still nothing, try comment_exists, and cache it
2002
- $exists = comment_exists( $data['comment_author'], $data['comment_date'] );
2003
- $this->exists['comment'][ $exists_key ] = $exists;
2004
-
2005
- return $exists;
2006
- }
2007
-
2008
- /**
2009
- * Mark the comment as existing.
2010
- *
2011
- * @param array $data Comment data to mark as existing.
2012
- * @param int $comment_id Comment ID.
2013
- */
2014
- protected function mark_comment_exists( $data, $comment_id ) {
2015
- $exists_key = sha1( $data['comment_author'] . ':' . $data['comment_date'] );
2016
- $this->exists['comment'][ $exists_key ] = $comment_id;
2017
- }
2018
-
2019
- /**
2020
- * Prefill existing term data.
2021
- *
2022
- * @see self::prefill_existing_posts() for justification of why this exists.
2023
- */
2024
- protected function prefill_existing_terms() {
2025
- global $wpdb;
2026
- $query = "SELECT t.term_id, tt.taxonomy, t.slug FROM {$wpdb->terms} AS t";
2027
- $query .= " JOIN {$wpdb->term_taxonomy} AS tt ON t.term_id = tt.term_id";
2028
- $terms = $wpdb->get_results( $query );
2029
-
2030
- foreach ( $terms as $item ) {
2031
- $exists_key = sha1( $item->taxonomy . ':' . $item->slug );
2032
- $this->exists['term'][ $exists_key ] = $item->term_id;
2033
- }
2034
- }
2035
-
2036
- /**
2037
- * Does the term exist?
2038
- *
2039
- * @param array $data Term data to check against.
2040
- * @return int|bool Existing term ID if it exists, false otherwise.
2041
- */
2042
- protected function term_exists( $data ) {
2043
- $exists_key = sha1( $data['taxonomy'] . ':' . $data['slug'] );
2044
-
2045
- // Constant-time lookup if we prefilled
2046
- if ( $this->options['prefill_existing_terms'] ) {
2047
- return isset( $this->exists['term'][ $exists_key ] ) ? $this->exists['term'][ $exists_key ] : false;
2048
- }
2049
-
2050
- // No prefilling, but might have already handled it
2051
- if ( isset( $this->exists['term'][ $exists_key ] ) ) {
2052
- return $this->exists['term'][ $exists_key ];
2053
- }
2054
-
2055
- // Still nothing, try comment_exists, and cache it
2056
- $exists = term_exists( $data['slug'], $data['taxonomy'] );
2057
- if ( is_array( $exists ) ) {
2058
- $exists = $exists['term_id'];
2059
- }
2060
-
2061
- $this->exists['term'][ $exists_key ] = $exists;
2062
-
2063
- return $exists;
2064
- }
2065
-
2066
- /**
2067
- * Mark the term as existing.
2068
- *
2069
- * @param array $data Term data to mark as existing.
2070
- * @param int $term_id Term ID.
2071
- */
2072
- protected function mark_term_exists( $data, $term_id ) {
2073
- $exists_key = sha1( $data['taxonomy'] . ':' . $data['slug'] );
2074
- $this->exists['term'][ $exists_key ] = $term_id;
2075
- }
2076
- }
1
+ <?php
2
+ /**
3
+ * WXR Importer class.
4
+ *
5
+ * Main class of WordPress Importer v2 to handle import process.
6
+ *
7
+ * @package Jupiter
8
+ * @subpackage Template Import
9
+ * @since 6.0.3
10
+ */
11
+
12
+ /**
13
+ * The main class of WordPress Importer v2.
14
+ *
15
+ * @since 6.0.3
16
+ *
17
+ * @see https://github.com/humanmade/WordPress-Importer/blob/master/class-wxr-importer.php
18
+ *
19
+ * @codingStandardsIgnoreFile
20
+ * @SuppressWarnings(PHPMD)
21
+ * - Temporary suppress warning. Worry if we change the content of the class or change how
22
+ * the logical conditions work, it will cause some issues.
23
+ */
24
+ class JupiterX_WXR_Importer extends WP_Importer {
25
+ /**
26
+ * Maximum supported WXR version
27
+ */
28
+ const MAX_WXR_VERSION = 1.2;
29
+
30
+ /**
31
+ * Regular expression for checking if a post references an attachment
32
+ *
33
+ * Note: This is a quick, weak check just to exclude text-only posts. More
34
+ * vigorous checking is done later to verify.
35
+ */
36
+ const REGEX_HAS_ATTACHMENT_REFS = '!
37
+ (
38
+ # Match anything with an image or attachment class
39
+ class=[\'"].*?\b(wp-image-\d+|attachment-[\w\-]+)\b
40
+ |
41
+ # Match anything that looks like an upload URL
42
+ src=[\'"][^\'"]*(
43
+ [0-9]{4}/[0-9]{2}/[^\'"]+\.(jpg|jpeg|png|gif)
44
+ |
45
+ content/uploads[^\'"]+
46
+ )[\'"]
47
+ )!ix';
48
+
49
+ /**
50
+ * Version of WXR we're importing.
51
+ *
52
+ * Defaults to 1.0 for compatibility. Typically overridden by a
53
+ * `<wp:wxr_version>` tag at the start of the file.
54
+ *
55
+ * @var string
56
+ */
57
+ protected $version = '1.0';
58
+
59
+ // information to import from WXR file
60
+ protected $categories = array();
61
+ protected $tags = array();
62
+ protected $base_url = '';
63
+
64
+ // TODO: REMOVE THESE
65
+ protected $processed_terms = array();
66
+ protected $processed_posts = array();
67
+ protected $processed_menu_items = array();
68
+ protected $menu_item_orphans = array();
69
+ protected $missing_menu_items = array();
70
+
71
+ // NEW STYLE
72
+ protected $mapping = array();
73
+ protected $requires_remapping = array();
74
+ protected $exists = array();
75
+ protected $user_slug_override = array();
76
+
77
+ protected $url_remap = array();
78
+ protected $featured_images = array();
79
+
80
+ /**
81
+ * Logger instance.
82
+ *
83
+ * @var JupiterX_Importer_Logger
84
+ */
85
+ protected $logger;
86
+
87
+ /**
88
+ * Import pages only.
89
+ *
90
+ * @var boolean
91
+ */
92
+ protected $partial_import;
93
+
94
+ /**
95
+ * Constructor
96
+ *
97
+ * @param array $options {
98
+ * @var bool $prefill_existing_posts Should we prefill `post_exists` calls? (True prefills and uses more memory, false checks once per imported post and takes longer. Default is true.)
99
+ * @var bool $prefill_existing_comments Should we prefill `comment_exists` calls? (True prefills and uses more memory, false checks once per imported comment and takes longer. Default is true.)
100
+ * @var bool $prefill_existing_terms Should we prefill `term_exists` calls? (True prefills and uses more memory, false checks once per imported term and takes longer. Default is true.)
101
+ * @var bool $update_attachment_guids Should attachment GUIDs be updated to the new URL? (True updates the GUID, which keeps compatibility with v1, false doesn't update, and allows deduplication and reimporting. Default is false.)
102
+ * @var bool $fetch_attachments Fetch attachments from the remote server. (True fetches and creates attachment posts, false skips attachments. Default is false.)
103
+ * @var bool $aggressive_url_search Should we search/replace for URLs aggressively? (True searches all posts' content for old URLs and replaces, false checks for `<img class="wp-image-*">` only. Default is false.)
104
+ * @var int $default_author User ID to use if author is missing or invalid. (Default is null, which leaves posts unassigned.)
105
+ * }
106
+ */
107
+ public function __construct( $options = array(), $partial_import = false ) {
108
+ $this->partial_import = $partial_import;
109
+
110
+ // Initialize some important variables
111
+ $empty_types = array(
112
+ 'post' => array(),
113
+ 'comment' => array(),
114
+ 'term' => array(),
115
+ 'user' => array(),
116
+ );
117
+
118
+ $this->mapping = $empty_types;
119
+ $this->mapping['user_slug'] = array();
120
+ $this->mapping['term_id'] = array();
121
+ $this->requires_remapping = $empty_types;
122
+ $this->exists = $empty_types;
123
+
124
+ $this->options = wp_parse_args( $options, array(
125
+ 'prefill_existing_posts' => true,
126
+ 'prefill_existing_comments' => true,
127
+ 'prefill_existing_terms' => true,
128
+ 'update_attachment_guids' => false,
129
+ 'fetch_attachments' => false,
130
+ 'aggressive_url_search' => false,
131
+ 'default_author' => null,
132
+ ) );
133
+ }
134
+
135
+ public function set_logger( $logger ) {
136
+ $this->logger = $logger;
137
+ }
138
+
139
+ /**
140
+ * Get a stream reader for the file.
141
+ *
142
+ * @param string $file Path to the XML file.
143
+ * @return XMLReader|WP_Error Reader instance on success, error otherwise.
144
+ */
145
+ protected function get_reader( $file ) {
146
+ // Avoid loading external entities for security
147
+ $old_value = null;
148
+ if ( function_exists( 'libxml_disable_entity_loader' ) ) {
149
+ // $old_value = libxml_disable_entity_loader( true );
150
+ }
151
+
152
+ $reader = new XMLReader();
153
+ $status = $reader->open( $file );
154
+
155
+ if ( ! is_null( $old_value ) ) {
156
+ // libxml_disable_entity_loader( $old_value );
157
+ }
158
+
159
+ if ( ! $status ) {
160
+ return new WP_Error( 'wxr_importer.cannot_parse', __( 'Could not open the file for parsing', 'jupiterx-core' ) );
161
+ }
162
+
163
+ return $reader;
164
+ }
165
+
166
+ /**
167
+ * The main controller for the actual import stage.
168
+ *
169
+ * @param string $file Path to the WXR file for importing
170
+ */
171
+ public function get_preliminary_information( $file ) {
172
+ // Let's run the actual importer now, woot
173
+ $reader = $this->get_reader( $file );
174
+ if ( is_wp_error( $reader ) ) {
175
+ return $reader;
176
+ }
177
+
178
+ // Set the version to compatibility mode first
179
+ $this->version = '1.0';
180
+
181
+ // Start parsing!
182
+ $data = new JupiterX_WXR_Import_Info();
183
+ while ( $reader->read() ) {
184
+ // Only deal with element opens
185
+ if ( $reader->nodeType !== XMLReader::ELEMENT ) {
186
+ continue;
187
+ }
188
+
189
+ switch ( $reader->name ) {
190
+ case 'wp:wxr_version':
191
+ // Upgrade to the correct version
192
+ $this->version = $reader->readString();
193
+
194
+ if ( version_compare( $this->version, self::MAX_WXR_VERSION, '>' ) ) {
195
+ $this->logger->warning( sprintf(
196
+ __( 'This WXR file (version %s) is newer than the importer (version %s) and may not be supported. Please consider updating.', 'jupiterx-core' ),
197
+ $this->version,
198
+ self::MAX_WXR_VERSION
199
+ ) );
200
+ }
201
+
202
+ // Handled everything in this node, move on to the next
203
+ $reader->next();
204
+ break;
205
+
206
+ case 'generator':
207
+ $data->generator = $reader->readString();
208
+ $reader->next();
209
+ break;
210
+
211
+ case 'title':
212
+ $data->title = $reader->readString();
213
+ $reader->next();
214
+ break;
215
+
216
+ case 'wp:base_site_url':
217
+ $data->siteurl = $reader->readString();
218
+ $reader->next();
219
+ break;
220
+
221
+ case 'wp:base_blog_url':
222
+ $data->home = $reader->readString();
223
+ $reader->next();
224
+ break;
225
+
226
+ case 'item':
227
+ $node = $reader->expand();
228
+ $parsed = $this->parse_post_node( $node );
229
+ if ( is_wp_error( $parsed ) ) {
230
+ $this->log_error( $parsed );
231
+
232
+ // Skip the rest of this post
233
+ $reader->next();
234
+ break;
235
+ }
236
+
237
+ if ( $parsed['data']['post_type'] === 'attachment' ) {
238
+ $data->media_count++;
239
+ } else {
240
+ $data->post_count++;
241
+ }
242
+ $data->comment_count += count( $parsed['comments'] );
243
+
244
+ // Handled everything in this node, move on to the next
245
+ $reader->next();
246
+ break;
247
+
248
+ case 'wp:category':
249
+ case 'wp:tag':
250
+ case 'wp:term':
251
+ $data->term_count++;
252
+
253
+ // Handled everything in this node, move on to the next
254
+ $reader->next();
255
+ break;
256
+ }
257
+ }
258
+
259
+ $data->version = $this->version;
260
+
261
+ return $data;
262
+ }
263
+
264
+ /**
265
+ * The main controller for the actual import stage.
266
+ *
267
+ * @param string $file Path to the WXR file for importing
268
+ */
269
+ public function import( $file ) {
270
+ add_filter( 'import_post_meta_key', array( $this, 'is_valid_meta_key' ) );
271
+ add_filter( 'http_request_timeout', array( &$this, 'bump_request_timeout' ) );
272
+
273
+ $result = $this->import_start( $file );
274
+ if ( is_wp_error( $result ) ) {
275
+ return $result;
276
+ }
277
+
278
+ // Let's run the actual importer now, woot
279
+ $reader = $this->get_reader( $file );
280
+ if ( is_wp_error( $reader ) ) {
281
+ return $reader;
282
+ }
283
+
284
+ // Set the version to compatibility mode first
285
+ $this->version = '1.0';
286
+
287
+ // Reset other variables
288
+ $this->base_url = '';
289
+
290
+ // Start parsing!
291
+ while ( $reader->read() ) {
292
+ // Only deal with element opens
293
+ if ( $reader->nodeType !== XMLReader::ELEMENT ) {
294
+ continue;
295
+ }
296
+
297
+ if ( $reader->name !== 'item' && $this->partial_import ) {
298
+ continue;
299
+ }
300
+
301
+ switch ( $reader->name ) {
302
+ case 'wp:wxr_version':
303
+ // Upgrade to the correct version
304
+ $this->version = $reader->readString();
305
+
306
+ if ( version_compare( $this->version, self::MAX_WXR_VERSION, '>' ) ) {
307
+ $this->logger->warning( sprintf(
308
+ __( 'This WXR file (version %s) is newer than the importer (version %s) and may not be supported. Please consider updating.', 'jupiterx-core' ),
309
+ $this->version,
310
+ self::MAX_WXR_VERSION
311
+ ) );
312
+ }
313
+
314
+ // Handled everything in this node, move on to the next
315
+ $reader->next();
316
+ break;
317
+
318
+ case 'wp:base_site_url':
319
+ $this->base_url = $reader->readString();
320
+
321
+ // Handled everything in this node, move on to the next
322
+ $reader->next();
323
+ break;
324
+
325
+ case 'item':
326
+ $node = $reader->expand();
327
+ $parsed = $this->parse_post_node( $node );
328
+ if ( is_wp_error( $parsed ) ) {
329
+ $this->log_error( $parsed );
330
+
331
+ // Skip the rest of this post
332
+ $reader->next();
333
+ break;
334
+ }
335
+
336
+ if (
337
+ in_array( $parsed['data']['post_type'], [ 'nav_menu_item' ], true ) &&
338
+ $this->partial_import
339
+ ) {
340
+ break;
341
+ }
342
+
343
+ $this->process_post( $parsed['data'], $parsed['meta'], $parsed['comments'], $parsed['terms'] );
344
+
345
+ // Handled everything in this node, move on to the next
346
+ $reader->next();
347
+ break;
348
+
349
+ case 'wp:category':
350
+ $node = $reader->expand();
351
+
352
+ $parsed = $this->parse_term_node( $node, 'category' );
353
+ if ( is_wp_error( $parsed ) ) {
354
+ $this->log_error( $parsed );
355
+
356
+ // Skip the rest of this post
357
+ $reader->next();
358
+ break;
359
+ }
360
+
361
+ $status = $this->process_term( $parsed['data'], $parsed['meta'] );
362
+
363
+ // Handled everything in this node, move on to the next
364
+ $reader->next();
365
+ break;
366
+
367
+ case 'wp:tag':
368
+ $node = $reader->expand();
369
+
370
+ $parsed = $this->parse_term_node( $node, 'tag' );
371
+ if ( is_wp_error( $parsed ) ) {
372
+ $this->log_error( $parsed );
373
+
374
+ // Skip the rest of this post
375
+ $reader->next();
376
+ break;
377
+ }
378
+
379
+ $status = $this->process_term( $parsed['data'], $parsed['meta'] );
380
+
381
+ // Handled everything in this node, move on to the next
382
+ $reader->next();
383
+ break;
384
+
385
+ case 'wp:term':
386
+ $node = $reader->expand();
387
+
388
+ $parsed = $this->parse_term_node( $node );
389
+ if ( is_wp_error( $parsed ) ) {
390
+ $this->log_error( $parsed );
391
+
392
+ // Skip the rest of this post
393
+ $reader->next();
394
+ break;
395
+ }
396
+
397
+ $status = $this->process_term( $parsed['data'], $parsed['meta'] );
398
+
399
+ // Handled everything in this node, move on to the next
400
+ $reader->next();
401
+ break;
402
+
403
+ default:
404
+ // Skip this node, probably handled by something already
405
+ break;
406
+ }
407
+ }
408
+
409
+ // Now that we've done the main processing, do any required
410
+ // post-processing and remapping.
411
+ $this->post_process();
412
+
413
+ if ( $this->options['aggressive_url_search'] ) {
414
+ $this->replace_attachment_urls_in_content();
415
+ }
416
+ // $this->remap_featured_images();
417
+
418
+ $this->import_end();
419
+ }
420
+
421
+ /**
422
+ * Log an error instance to the logger.
423
+ *
424
+ * @param WP_Error $error Error instance to log.
425
+ */
426
+ protected function log_error( WP_Error $error ) {
427
+ $this->logger->warning( $error->get_error_message() );
428
+
429
+ // Log the data as debug info too
430
+ $data = $error->get_error_data();
431
+ if ( ! empty( $data ) ) {
432
+ $this->logger->debug( var_export( $data, true ) );
433
+ }
434
+ }
435
+
436
+ /**
437
+ * Parses the WXR file and prepares us for the task of processing parsed data
438
+ *
439
+ * @param string $file Path to the WXR file for importing
440
+ */
441
+ protected function import_start( $file ) {
442
+ if ( ! is_file( $file ) ) {
443
+ return new WP_Error( 'wxr_importer.file_missing', __( 'The file does not exist, please try again.', 'jupiterx-core' ) );
444
+ }
445
+
446
+ // Suspend bunches of stuff in WP core
447
+ wp_defer_term_counting( true );
448
+ wp_defer_comment_counting( true );
449
+ wp_suspend_cache_invalidation( true );
450
+
451
+ // Prefill exists calls if told to
452
+ if ( $this->options['prefill_existing_posts'] ) {
453
+ $this->prefill_existing_posts();
454
+ }
455
+ if ( $this->options['prefill_existing_comments'] ) {
456
+ $this->prefill_existing_comments();
457
+ }
458
+ if ( $this->options['prefill_existing_terms'] ) {
459
+ $this->prefill_existing_terms();
460
+ }
461
+
462
+ /**
463
+ * Begin the import.
464
+ *
465
+ * Fires before the import process has begun. If you need to suspend
466
+ * caching or heavy processing on hooks, do so here.
467
+ */
468
+ do_action( 'import_start' );
469
+ }
470
+
471
+ /**
472
+ * Performs post-import cleanup of files and the cache
473
+ */
474
+ protected function import_end() {
475
+ // Re-enable stuff in core
476
+ wp_suspend_cache_invalidation( false );
477
+ wp_cache_flush();
478
+ foreach ( get_taxonomies() as $tax ) {
479
+ delete_option( "{$tax}_children" );
480
+ _get_term_hierarchy( $tax );
481
+ }
482
+
483
+ wp_defer_term_counting( false );
484
+ wp_defer_comment_counting( false );
485
+
486
+ /**
487
+ * Complete the import.
488
+ *
489
+ * Fires after the import process has finished. If you need to update
490
+ * your cache or re-enable processing, do so here.
491
+ */
492
+ do_action( 'import_end' );
493
+ }
494
+
495
+ /**
496
+ * Set the user mapping.
497
+ *
498
+ * @param array $mapping List of map arrays (containing `old_slug`, `old_id`, `new_id`)
499
+ */
500
+ public function set_user_mapping( $mapping ) {
501
+ foreach ( $mapping as $map ) {
502
+ if ( empty( $map['old_slug'] ) || empty( $map['old_id'] ) || empty( $map['new_id'] ) ) {
503
+ $this->logger->warning( __( 'Invalid author mapping', 'jupiterx-core' ) );
504
+ $this->logger->debug( var_export( $map, true ) );
505
+ continue;
506
+ }
507
+
508
+ $old_slug = $map['old_slug'];
509
+ $old_id = $map['old_id'];
510
+ $new_id = $map['new_id'];
511
+
512
+ $this->mapping['user'][ $old_id ] = $new_id;
513
+ $this->mapping['user_slug'][ $old_slug ] = $new_id;
514
+ }
515
+ }
516
+
517
+ /**
518
+ * Set the user slug overrides.
519
+ *
520
+ * Allows overriding the slug in the import with a custom/renamed version.
521
+ *
522
+ * @param string[] $overrides Map of old slug to new slug.
523
+ */
524
+ public function set_user_slug_overrides( $overrides ) {
525
+ foreach ( $overrides as $original => $renamed ) {
526
+ $this->user_slug_override[ $original ] = $renamed;
527
+ }
528
+ }
529
+
530
+ /**
531
+ * Parse a post node into post data.
532
+ *
533
+ * @param DOMElement $node Parent node of post data (typically `item`).
534
+ * @return array|WP_Error Post data array on success, error otherwise.
535
+ */
536
+ protected function parse_post_node( $node ) {
537
+ $data = array();
538
+ $meta = array();
539
+ $comments = array();
540
+ $terms = array();
541
+
542
+ foreach ( $node->childNodes as $child ) {
543
+ // We only care about child elements
544
+ if ( $child->nodeType !== XML_ELEMENT_NODE ) {
545
+ continue;
546
+ }
547
+
548
+ switch ( $child->tagName ) {
549
+ case 'wp:post_type':
550
+ $data['post_type'] = $child->textContent;
551
+ break;
552
+
553
+ case 'title':
554
+ $data['post_title'] = $child->textContent;
555
+ break;
556
+
557
+ case 'guid':
558
+ $data['guid'] = $child->textContent;
559
+ break;
560
+
561
+ case 'dc:creator':
562
+ $data['post_author'] = $child->textContent;
563
+ break;
564
+
565
+ case 'content:encoded':
566
+ $data['post_content'] = $child->textContent;
567
+ break;
568
+
569
+ case 'excerpt:encoded':
570
+ $data['post_excerpt'] = $child->textContent;
571
+ break;
572
+
573
+ case 'wp:post_id':
574
+ $data['post_id'] = $child->textContent;
575
+ break;
576
+
577
+ case 'wp:post_date':
578
+ $data['post_date'] = $child->textContent;
579
+ break;
580
+
581
+ case 'wp:post_date_gmt':
582
+ $data['post_date_gmt'] = $child->textContent;
583
+ break;
584
+
585
+ case 'wp:comment_status':
586
+ $data['comment_status'] = $child->textContent;
587
+ break;
588
+
589
+ case 'wp:ping_status':
590
+ $data['ping_status'] = $child->textContent;
591
+ break;
592
+
593
+ case 'wp:post_name':
594
+ $data['post_name'] = $child->textContent;
595
+ break;
596
+
597
+ case 'wp:status':
598
+ $data['post_status'] = $child->textContent;
599
+
600
+ if ( $data['post_status'] === 'auto-draft' ) {
601
+ // Bail now
602
+ return new WP_Error(
603
+ 'wxr_importer.post.cannot_import_draft',
604
+ __( 'Cannot import auto-draft posts', 'jupiterx-core' ),
605
+ $data
606
+ );
607
+ }
608
+ break;
609
+
610
+ case 'wp:post_parent':
611
+ $data['post_parent'] = $child->textContent;
612
+ break;
613
+
614
+ case 'wp:menu_order':
615
+ $data['menu_order'] = $child->textContent;
616
+ break;
617
+
618
+ case 'wp:post_password':
619
+ $data['post_password'] = $child->textContent;
620
+ break;
621
+
622
+ case 'wp:is_sticky':
623
+ $data['is_sticky'] = $child->textContent;
624
+ break;
625
+
626
+ case 'wp:attachment_url':
627
+ $data['attachment_url'] = $child->textContent;
628
+ break;
629
+
630
+ case 'wp:postmeta':
631
+ $meta_item = $this->parse_meta_node( $child );
632
+ if ( ! empty( $meta_item ) ) {
633
+ $meta[] = $meta_item;
634
+ }
635
+ break;
636
+
637
+ case 'wp:comment':
638
+ $comment_item = $this->parse_comment_node( $child );
639
+ if ( ! empty( $comment_item ) ) {
640
+ $comments[] = $comment_item;
641
+ }
642
+ break;
643
+
644
+ case 'category':
645
+ $term_item = $this->parse_category_node( $child );
646
+ if ( ! empty( $term_item ) ) {
647
+ $terms[] = $term_item;
648
+ }
649
+ break;
650
+ }
651
+ }
652
+
653
+ return compact( 'data', 'meta', 'comments', 'terms' );
654
+ }
655
+
656
+ /**
657
+ * Create new posts based on import information
658
+ *
659
+ * Posts marked as having a parent which doesn't exist will become top level items.
660
+ * Doesn't create a new post if: the post type doesn't exist, the given post ID
661
+ * is already noted as imported or a post with the same title and date already exists.
662
+ * Note that new/updated terms, comments and meta are imported for the last of the above.
663
+ */
664
+ protected function process_post( $data, $meta, $comments, $terms ) {
665
+ /**
666
+ * Pre-process post data.
667
+ *
668
+ * @param array $data Post data. (Return empty to skip.)
669
+ * @param array $meta Meta data.
670
+ * @param array $comments Comments on the post.
671
+ * @param array $terms Terms on the post.
672
+ */
673
+ $data = apply_filters( 'wxr_importer.pre_process.post', $data, $meta, $comments, $terms );
674
+ if ( empty( $data ) ) {
675
+ return false;
676
+ }
677
+
678
+ $original_id = isset( $data['post_id'] ) ? (int) $data['post_id'] : 0;
679
+ $parent_id = isset( $data['post_parent'] ) ? (int) $data['post_parent'] : 0;
680
+ $author_id = isset( $data['post_author'] ) ? (int) $data['post_author'] : 0;
681
+
682
+ // Have we already processed this?
683
+ if ( isset( $this->mapping['post'][ $original_id ] ) ) {
684
+ return;
685
+ }
686
+
687
+ $post_type_object = get_post_type_object( $data['post_type'] );
688
+
689
+ // Is this type even valid?
690
+ if ( ! $post_type_object ) {
691
+ $this->logger->warning( sprintf(
692
+ __( 'Failed to import "%s": Invalid post type %s', 'jupiterx-core' ),
693
+ $data['post_title'],
694
+ $data['post_type']
695
+ ) );
696
+ return false;
697
+ }
698
+
699
+ $post_exists = $this->post_exists( $data ) && ! $this->partial_import; // allow post duplication on partial import
700
+ if ( $post_exists ) {
701
+ $this->logger->info( sprintf(
702
+ __( '%s "%s" already exists.', 'jupiterx-core' ),
703
+ $post_type_object->labels->singular_name,
704
+ $data['post_title']
705
+ ) );
706
+
707
+ /**
708
+ * Post processing already imported.
709
+ *
710
+ * @param array $data Raw data imported for the post.
711
+ */
712
+ do_action( 'wxr_importer.process_already_imported.post', $data );
713
+
714
+ // Even though this post already exists, new comments might need importing
715
+ $this->process_comments( $comments, $original_id, $data, $post_exists );
716
+
717
+ return false;
718
+ }
719
+
720
+ // Map the parent post, or mark it as one we need to fix
721
+ $requires_remapping = false;
722
+ if ( $parent_id ) {
723
+ if ( isset( $this->mapping['post'][ $parent_id ] ) ) {
724
+ $data['post_parent'] = $this->mapping['post'][ $parent_id ];
725
+ } else {
726
+ $meta[] = array( 'key' => '_wxr_import_parent', 'value' => $parent_id );
727
+ $requires_remapping = true;
728
+
729
+ $data['post_parent'] = 0;
730
+ }
731
+ }
732
+
733
+ // Map the author, or mark it as one we need to fix
734
+ $author = sanitize_user( $data['post_author'], true );
735
+ if ( empty( $author ) ) {
736
+ // Missing or invalid author, use default if available.
737
+ $data['post_author'] = $this->options['default_author'];
738
+ } elseif ( isset( $this->mapping['user_slug'][ $author ] ) ) {
739
+ $data['post_author'] = $this->mapping['user_slug'][ $author ];
740
+ } else {
741
+ $meta[] = array( 'key' => '_wxr_import_user_slug', 'value' => $author );
742
+ $requires_remapping = true;
743
+
744
+ $data['post_author'] = (int) get_current_user_id();
745
+ }
746
+
747
+ // Does the post look like it contains attachment images?
748
+ if ( preg_match( self::REGEX_HAS_ATTACHMENT_REFS, $data['post_content'] ) ) {
749
+ $meta[] = array( 'key' => '_wxr_import_has_attachment_refs', 'value' => true );
750
+ $requires_remapping = true;
751
+ }
752
+
753
+ // Whitelist to just the keys we allow
754
+ $postdata = array(
755
+ 'import_id' => $data['post_id'],
756
+ );
757
+ $allowed = array(
758
+ 'post_author' => true,
759
+ 'post_date' => true,
760
+ 'post_date_gmt' => true,
761
+ 'post_content' => true,
762
+ 'post_excerpt' => true,
763
+ 'post_title' => true,
764
+ 'post_status' => true,
765
+ 'post_name' => true,
766
+ 'comment_status' => true,
767
+ 'ping_status' => true,
768
+ 'guid' => true,
769
+ 'post_parent' => true,
770
+ 'menu_order' => true,
771
+ 'post_type' => true,
772
+ 'post_password' => true,
773
+ );
774
+ foreach ( $data as $key => $value ) {
775
+ if ( ! isset( $allowed[ $key ] ) ) {
776
+ continue;
777
+ }
778
+
779
+ $postdata[ $key ] = $data[ $key ];
780
+ }
781
+
782
+ $postdata = apply_filters( 'wp_import_post_data_processed', $postdata, $data );
783
+
784
+ if ( 'attachment' === $postdata['post_type'] ) {
785
+ if ( ! $this->options['fetch_attachments'] ) {
786
+ $this->logger->notice( sprintf(
787
+ __( 'Skipping attachment "%s", fetching attachments disabled', 'jupiterx-core' ),
788
+ $data['post_title']
789
+ ) );
790
+ /**
791
+ * Post processing skipped.
792
+ *
793
+ * @param array $data Raw data imported for the post.
794
+ * @param array $meta Raw meta data, already processed by {@see process_post_meta}.
795
+ */
796
+ do_action( 'wxr_importer.process_skipped.post', $data, $meta );
797
+ return false;
798
+ }
799
+ $remote_url = ! empty( $data['attachment_url'] ) ? $data['attachment_url'] : $data['guid'];
800
+ $post_id = $this->process_attachment( $postdata, $meta, $remote_url );
801
+ } else {
802
+ $post_id = wp_insert_post( $postdata, true );
803
+ do_action( 'wp_import_insert_post', $post_id, $original_id, $postdata, $data );
804
+ }
805
+
806
+ if ( is_wp_error( $post_id ) ) {
807
+ $this->logger->error( sprintf(
808
+ __( 'Failed to import "%s" (%s)', 'jupiterx-core' ),
809
+ $data['post_title'],
810
+ $post_type_object->labels->singular_name
811
+ ) );
812
+ $this->logger->debug( $post_id->get_error_message() );
813
+
814
+ /**
815
+ * Post processing failed.
816
+ *
817
+ * @param WP_Error $post_id Error object.
818
+ * @param array $data Raw data imported for the post.
819
+ * @param array $meta Raw meta data, already processed by {@see process_post_meta}.
820
+ * @param array $comments Raw comment data, already processed by {@see process_comments}.
821
+ * @param array $terms Raw term data, already processed.
822
+ */
823
+ do_action( 'wxr_importer.process_failed.post', $post_id, $data, $meta, $comments, $terms );
824
+ return false;
825
+ }
826
+
827
+ // Ensure stickiness is handled correctly too
828
+ if ( $data['is_sticky'] === '1' ) {
829
+ stick_post( $post_id );
830
+ }
831
+
832
+ // map pre-import ID to local ID
833
+ $this->mapping['post'][ $original_id ] = (int) $post_id;
834
+ if ( $requires_remapping ) {
835
+ $this->requires_remapping['post'][ $post_id ] = true;
836
+ }
837
+ $this->mark_post_exists( $data, $post_id );
838
+
839
+ $this->logger->info( sprintf(
840
+ __( 'Imported "%s" (%s)', 'jupiterx-core' ),
841
+ $data['post_title'],
842
+ $post_type_object->labels->singular_name
843
+ ) );
844
+ $this->logger->debug( sprintf(
845
+ __( 'Post %d remapped to %d', 'jupiterx-core' ),
846
+ $original_id,
847
+ $post_id
848
+ ) );
849
+
850
+ // Handle the terms too
851
+ $terms = apply_filters( 'wp_import_post_terms', $terms, $post_id, $data );
852
+
853
+ if ( ! empty( $terms ) ) {
854
+ $term_ids = array();
855
+ foreach ( $terms as $term ) {
856
+ $taxonomy = $term['taxonomy'];
857
+ $key = sha1( $taxonomy . ':' . $term['slug'] );
858
+
859
+ if ( isset( $this->mapping['term'][ $key ] ) ) {
860
+ $term_ids[ $taxonomy ][] = (int) $this->mapping['term'][ $key ];
861
+ } else {
862
+ $meta[] = array( 'key' => '_wxr_import_term', 'value' => $term );
863
+ $requires_remapping = true;
864
+ }
865
+ }
866
+
867
+ foreach ( $term_ids as $tax => $ids ) {
868
+ $tt_ids = wp_set_post_terms( $post_id, $ids, $tax );
869
+ do_action( 'wp_import_set_post_terms', $tt_ids, $ids, $tax, $post_id, $data );
870
+ }
871
+ }
872
+
873
+ $this->process_comments( $comments, $post_id, $data );
874
+ $this->process_post_meta( $meta, $post_id, $data );
875
+
876
+ if ( 'nav_menu_item' === $data['post_type'] ) {
877
+ $this->process_menu_item_meta( $post_id, $data, $meta );
878
+ }
879
+
880
+ /**
881
+ * Post processing completed.
882
+ *
883
+ * @param int $post_id New post ID.
884
+ * @param array $data Raw data imported for the post.
885
+ * @param array $meta Raw meta data, already processed by {@see process_post_meta}.
886
+ * @param array $comments Raw comment data, already processed by {@see process_comments}.
887
+ * @param array $terms Raw term data, already processed.
888
+ */
889
+ do_action( 'wxr_importer.processed.post', $post_id, $data, $meta, $comments, $terms );
890
+ }
891
+
892
+ /**
893
+ * Attempt to create a new menu item from import data
894
+ *
895
+ * Fails for draft, orphaned menu items and those without an associated nav_menu
896
+ * or an invalid nav_menu term. If the post type or term object which the menu item
897
+ * represents doesn't exist then the menu item will not be imported (waits until the
898
+ * end of the import to retry again before discarding).
899
+ *
900
+ * @param array $item Menu item details from WXR file
901
+ */
902
+ protected function process_menu_item_meta( $post_id, $data, $meta ) {
903
+
904
+ $item_type = get_post_meta( $post_id, '_menu_item_type', true );
905
+ $original_object_id = get_post_meta( $post_id, '_menu_item_object_id', true );
906
+ $object_id = null;
907
+
908
+ $this->logger->debug( sprintf( 'Processing menu item %s', $item_type ) );
909
+
910
+ $requires_remapping = false;
911
+ switch ( $item_type ) {
912
+ case 'taxonomy':
913
+ if ( isset( $this->mapping['term_id'][ $original_object_id ] ) ) {
914
+ $object_id = $this->mapping['term_id'][ $original_object_id ];
915
+ } else {
916
+ add_post_meta( $post_id, '_wxr_import_menu_item', wp_slash( $original_object_id ) );
917
+ $requires_remapping = true;
918
+ }
919
+ break;
920
+
921
+ case 'post_type':
922
+ if ( isset( $this->mapping['post'][ $original_object_id ] ) ) {
923
+ $object_id = $this->mapping['post'][ $original_object_id ];
924
+ } else {
925
+ add_post_meta( $post_id, '_wxr_import_menu_item', wp_slash( $original_object_id ) );
926
+ $requires_remapping = true;
927
+ }
928
+ break;
929
+
930
+ case 'custom':
931
+ // Custom refers to itself, wonderfully easy.
932
+ $object_id = $post_id;
933
+ break;
934
+
935
+ default:
936
+ // associated object is missing or not imported yet, we'll retry later
937
+ $this->missing_menu_items[] = $item;
938
+ $this->logger->debug( 'Unknown menu item type' );
939
+ break;
940
+ }
941
+
942
+ if ( $requires_remapping ) {
943
+ $this->requires_remapping['post'][ $post_id ] = true;
944
+ }
945
+
946
+ if ( empty( $object_id ) ) {
947
+ // Nothing needed here.
948
+ return;
949
+ }
950
+
951
+ $this->logger->debug( sprintf( 'Menu item %d mapped to %d', $original_object_id, $object_id ) );
952
+ update_post_meta( $post_id, '_menu_item_object_id', wp_slash( $object_id ) );
953
+ }
954
+
955
+ /**
956
+ * If fetching attachments is enabled then attempt to create a new attachment
957
+ *
958
+ * @param array $post Attachment post details from WXR
959
+ * @param string $url URL to fetch attachment from
960
+ * @return int|WP_Error Post ID on success, WP_Error otherwise
961
+ */
962
+ protected function process_attachment( $post, $meta, $remote_url ) {
963
+ // try to use _wp_attached file for upload folder placement to ensure the same location as the export site
964
+ // e.g. location is 2003/05/image.jpg but the attachment post_date is 2010/09, see media_handle_upload()
965
+ $post['upload_date'] = $post['post_date'];
966
+ foreach ( $meta as $meta_item ) {
967
+ if ( $meta_item['key'] !== '_wp_attached_file' ) {
968
+ continue;
969
+ }
970
+
971
+ if ( preg_match( '%^[0-9]{4}/[0-9]{2}%', $meta_item['value'], $matches ) ) {
972
+ $post['upload_date'] = $matches[0];
973
+ }
974
+ break;
975
+ }
976
+
977
+ // if the URL is absolute, but does not contain address, then upload it assuming base_site_url
978
+ if ( preg_match( '|^/[\w\W]+$|', $remote_url ) ) {
979
+ $remote_url = rtrim( $this->base_url, '/' ) . $remote_url;
980
+ }
981
+
982
+ $upload = $this->fetch_remote_file( $remote_url, $post );
983
+ if ( is_wp_error( $upload ) ) {
984
+ return $upload;
985
+ }
986
+
987
+ $info = wp_check_filetype( $upload['file'] );
988
+ if ( ! $info ) {
989
+ return new WP_Error( 'attachment_processing_error', __( 'Invalid file type', 'jupiterx-core' ) );
990
+ }
991
+
992
+ $post['post_mime_type'] = $info['type'];
993
+
994
+ // WP really likes using the GUID for display. Allow updating it.
995
+ // See https://core.trac.wordpress.org/ticket/33386
996
+ if ( $this->options['update_attachment_guids'] ) {
997
+ $post['guid'] = $upload['url'];
998
+ }
999
+
1000
+ // as per wp-admin/includes/upload.php
1001
+ $post_id = wp_insert_attachment( $post, $upload['file'] );
1002
+ if ( is_wp_error( $post_id ) ) {
1003
+ return $post_id;
1004
+ }
1005
+
1006
+ $attachment_metadata = wp_generate_attachment_metadata( $post_id, $upload['file'] );
1007
+ wp_update_attachment_metadata( $post_id, $attachment_metadata );
1008
+
1009
+ // Map this image URL later if we need to
1010
+ $this->url_remap[ $remote_url ] = $upload['url'];
1011
+
1012
+ // If we have a HTTPS URL, ensure the HTTP URL gets replaced too
1013
+ if ( substr( $remote_url, 0, 8 ) === 'https://' ) {
1014
+ $insecure_url = 'http' . substr( $remote_url, 5 );
1015
+ $this->url_remap[ $insecure_url ] = $upload['url'];
1016
+ }
1017
+
1018
+ if ( $this->options['aggressive_url_search'] ) {
1019
+ // remap resized image URLs, works by stripping the extension and remapping the URL stub.
1020
+ /*if ( preg_match( '!^image/!', $info['type'] ) ) {
1021
+ $parts = pathinfo( $remote_url );
1022
+ $name = basename( $parts['basename'], ".{$parts['extension']}" ); // PATHINFO_FILENAME in PHP 5.2
1023
+
1024
+ $parts_new = pathinfo( $upload['url'] );
1025
+ $name_new = basename( $parts_new['basename'], ".{$parts_new['extension']}" );
1026
+
1027
+ $this->url_remap[$parts['dirname'] . '/' . $name] = $parts_new['dirname'] . '/' . $name_new;
1028
+ }*/
1029
+ }
1030
+
1031
+ return $post_id;
1032
+ }
1033
+
1034
+ /**
1035
+ * Parse a meta node into meta data.
1036
+ *
1037
+ * @param DOMElement $node Parent node of meta data (typically `wp:postmeta` or `wp:commentmeta`).
1038
+ * @return array|null Meta data array on success, or null on error.
1039
+ */
1040
+ protected function parse_meta_node( $node ) {
1041
+ foreach ( $node->childNodes as $child ) {
1042
+ // We only care about child elements
1043
+ if ( $child->nodeType !== XML_ELEMENT_NODE ) {
1044
+ continue;
1045
+ }
1046
+
1047
+ switch ( $child->tagName ) {
1048
+ case 'wp:meta_key':
1049
+ $key = $child->textContent;
1050
+ break;
1051
+
1052
+ case 'wp:meta_value':
1053
+ $value = $child->textContent;
1054
+ break;
1055
+ }
1056
+ }
1057
+
1058
+ if ( empty( $key ) ) {
1059
+ return null;
1060
+ }
1061
+
1062
+ return compact( 'key', 'value' );
1063
+ }
1064
+
1065
+ /**
1066
+ * Process and import post meta items.
1067
+ *
1068
+ * @param array $meta List of meta data arrays
1069
+ * @param int $post_id Post to associate with
1070
+ * @param array $post Post data
1071
+ * @return int|WP_Error Number of meta items imported on success, error otherwise.
1072
+ */
1073
+ protected function process_post_meta( $meta, $post_id, $post ) {
1074
+ if ( empty( $meta ) ) {
1075
+ return true;
1076
+ }
1077
+
1078
+ foreach ( $meta as $meta_item ) {
1079
+ /**
1080
+ * Pre-process post meta data.
1081
+ *
1082
+ * @param array $meta_item Meta data. (Return empty to skip.)
1083
+ * @param int $post_id Post the meta is attached to.
1084
+ */
1085
+ $meta_item = apply_filters( 'wxr_importer.pre_process.post_meta', $meta_item, $post_id );
1086
+ if ( empty( $meta_item ) ) {
1087
+ return false;
1088
+ }
1089
+
1090
+ $key = apply_filters( 'import_post_meta_key', $meta_item['key'], $post_id, $post );
1091
+ $value = false;
1092
+
1093
+ if ( '_edit_last' === $key ) {
1094
+ $value = intval( $meta_item['value'] );
1095
+ if ( ! isset( $this->mapping['user'][ $value ] ) ) {
1096
+ // Skip!
1097
+ continue;
1098
+ }
1099
+
1100
+ $value = $this->mapping['user'][ $value ];
1101
+ }
1102
+
1103
+ if ( $key ) {
1104
+ // export gets meta straight from the DB so could have a serialized string
1105
+ if ( ! $value ) {
1106
+ $value = maybe_unserialize( $meta_item['value'] );
1107
+ }
1108
+
1109
+ add_post_meta( $post_id, $key, $value );
1110
+ do_action( 'import_post_meta', $post_id, $key, $value );
1111
+
1112
+ // if the post has a featured image, take note of this in case of remap
1113
+ if ( '_thumbnail_id' === $key ) {
1114
+ $this->featured_images[ $post_id ] = (int) $value;
1115
+ }
1116
+ }
1117
+ }
1118
+
1119
+ return true;
1120
+ }
1121
+
1122
+ /**
1123
+ * Parse a comment node into comment data.
1124
+ *
1125
+ * @param DOMElement $node Parent node of comment data (typically `wp:comment`).
1126
+ * @return array Comment data array.
1127
+ */
1128
+ protected function parse_comment_node( $node ) {
1129
+ $data = array(
1130
+ 'commentmeta' => array(),
1131
+ );
1132
+
1133
+ foreach ( $node->childNodes as $child ) {
1134
+ // We only care about child elements
1135
+ if ( $child->nodeType !== XML_ELEMENT_NODE ) {
1136
+ continue;
1137
+ }
1138
+
1139
+ switch ( $child->tagName ) {
1140
+ case 'wp:comment_id':
1141
+ $data['comment_id'] = $child->textContent;
1142
+ break;
1143
+ case 'wp:comment_author':
1144
+ $data['comment_author'] = $child->textContent;
1145
+ break;
1146
+
1147
+ case 'wp:comment_author_email':
1148
+ $data['comment_author_email'] = $child->textContent;
1149
+ break;
1150
+
1151
+ case 'wp:comment_author_IP':
1152
+ $data['comment_author_IP'] = $child->textContent;
1153
+ break;
1154
+
1155
+ case 'wp:comment_author_url':
1156
+ $data['comment_author_url'] = $child->textContent;
1157
+ break;
1158
+
1159
+ case 'wp:comment_user_id':
1160
+ $data['comment_user_id'] = $child->textContent;
1161
+ break;
1162
+
1163
+ case 'wp:comment_date':
1164
+ $data['comment_date'] = $child->textContent;
1165
+ break;
1166
+
1167
+ case 'wp:comment_date_gmt':
1168
+ $data['comment_date_gmt'] = $child->textContent;
1169
+ break;
1170
+
1171
+ case 'wp:comment_content':
1172
+ $data['comment_content'] = $child->textContent;
1173
+ break;
1174
+
1175
+ case 'wp:comment_approved':
1176
+ $data['comment_approved'] = $child->textContent;
1177
+ break;
1178
+
1179
+ case 'wp:comment_type':
1180
+ $data['comment_type'] = $child->textContent;
1181
+ break;
1182
+
1183
+ case 'wp:comment_parent':
1184
+ $data['comment_parent'] = $child->textContent;
1185
+ break;
1186
+
1187
+ case 'wp:commentmeta':
1188
+ $meta_item = $this->parse_meta_node( $child );
1189
+ if ( ! empty( $meta_item ) ) {
1190
+ $data['commentmeta'][] = $meta_item;
1191
+ }
1192
+ break;
1193
+ }
1194
+ }
1195
+
1196
+ return $data;
1197
+ }
1198
+
1199
+ /**
1200
+ * Process and import comment data.
1201
+ *
1202
+ * @param array $comments List of comment data arrays.
1203
+ * @param int $post_id Post to associate with.
1204
+ * @param array $post Post data.
1205
+ * @return int|WP_Error Number of comments imported on success, error otherwise.
1206
+ */
1207
+ protected function process_comments( $comments, $post_id, $post, $post_exists = false ) {
1208
+
1209
+ $comments = apply_filters( 'wp_import_post_comments', $comments, $post_id, $post );
1210
+ if ( empty( $comments ) ) {
1211
+ return 0;
1212
+ }
1213
+
1214
+ $num_comments = 0;
1215
+
1216
+ // Sort by ID to avoid excessive remapping later
1217
+ usort( $comments, array( $this, 'sort_comments_by_id' ) );
1218
+
1219
+ foreach ( $comments as $key => $comment ) {
1220
+ /**
1221
+ * Pre-process comment data
1222
+ *
1223
+ * @param array $comment Comment data. (Return empty to skip.)
1224
+ * @param int $post_id Post the comment is attached to.
1225
+ */
1226
+ $comment = apply_filters( 'wxr_importer.pre_process.comment', $comment, $post_id );
1227
+ if ( empty( $comment ) ) {
1228
+ return false;
1229
+ }
1230
+
1231
+ $original_id = isset( $comment['comment_id'] ) ? (int) $comment['comment_id'] : 0;
1232
+ $parent_id = isset( $comment['comment_parent'] ) ? (int) $comment['comment_parent'] : 0;
1233
+ $author_id = isset( $comment['comment_user_id'] ) ? (int) $comment['comment_user_id'] : 0;
1234
+
1235
+ // if this is a new post we can skip the comment_exists() check
1236
+ // TODO: Check comment_exists for performance
1237
+ if ( $post_exists ) {
1238
+ $existing = $this->comment_exists( $comment );
1239
+ if ( $existing ) {
1240
+
1241
+ /**
1242
+ * Comment processing already imported.
1243
+ *
1244
+ * @param array $comment Raw data imported for the comment.
1245
+ */
1246
+ do_action( 'wxr_importer.process_already_imported.comment', $comment );
1247
+
1248
+ $this->mapping['comment'][ $original_id ] = $existing;
1249
+ continue;
1250
+ }
1251
+ }
1252
+
1253
+ // Remove meta from the main array
1254
+ $meta = isset( $comment['commentmeta'] ) ? $comment['commentmeta'] : array();
1255
+ unset( $comment['commentmeta'] );
1256
+
1257
+ // Map the parent comment, or mark it as one we need to fix
1258
+ $requires_remapping = false;
1259
+ if ( $parent_id ) {
1260
+ if ( isset( $this->mapping['comment'][ $parent_id ] ) ) {
1261
+ $comment['comment_parent'] = $this->mapping['comment'][ $parent_id ];
1262
+ } else {
1263
+ // Prepare for remapping later
1264
+ $meta[] = array( 'key' => '_wxr_import_parent', 'value' => $parent_id );
1265
+ $requires_remapping = true;
1266
+
1267
+ // Wipe the parent for now
1268
+ $comment['comment_parent'] = 0;
1269
+ }
1270
+ }
1271
+
1272
+ // Map the author, or mark it as one we need to fix
1273
+ if ( $author_id ) {
1274
+ if ( isset( $this->mapping['user'][ $author_id ] ) ) {
1275
+ $comment['user_id'] = $this->mapping['user'][ $author_id ];
1276
+ } else {
1277
+ // Prepare for remapping later
1278
+ $meta[] = array( 'key' => '_wxr_import_user', 'value' => $author_id );
1279
+ $requires_remapping = true;
1280
+
1281
+ // Wipe the user for now
1282
+ $comment['user_id'] = 0;
1283
+ }
1284
+ }
1285
+
1286
+ // Run standard core filters
1287
+ $comment['comment_post_ID'] = $post_id;
1288
+ $comment = wp_filter_comment( $comment );
1289
+
1290
+ // wp_insert_comment expects slashed data
1291
+ $comment_id = wp_insert_comment( wp_slash( $comment ) );
1292
+ $this->mapping['comment'][ $original_id ] = $comment_id;
1293
+ if ( $requires_remapping ) {
1294
+ $this->requires_remapping['comment'][ $comment_id ] = true;
1295
+ }
1296
+ $this->mark_comment_exists( $comment, $comment_id );
1297
+
1298
+ /**
1299
+ * Comment has been imported.
1300
+ *
1301
+ * @param int $comment_id New comment ID
1302
+ * @param array $comment Comment inserted (`comment_id` item refers to the original ID)
1303
+ * @param int $post_id Post parent of the comment
1304
+ * @param array $post Post data
1305
+ */
1306
+ do_action( 'wp_import_insert_comment', $comment_id, $comment, $post_id, $post );
1307
+
1308
+ // Process the meta items
1309
+ foreach ( $meta as $meta_item ) {
1310
+ $value = maybe_unserialize( $meta_item['value'] );
1311
+ add_comment_meta( $comment_id, wp_slash( $meta_item['key'] ), wp_slash( $value ) );
1312
+ }
1313
+
1314
+ /**
1315
+ * Post processing completed.
1316
+ *
1317
+ * @param int $post_id New post ID.
1318
+ * @param array $comment Raw data imported for the comment.
1319
+ * @param array $meta Raw meta data, already processed by {@see process_post_meta}.
1320
+ * @param array $post_id Parent post ID.
1321
+ */
1322
+ do_action( 'wxr_importer.processed.comment', $comment_id, $comment, $meta, $post_id );
1323
+
1324
+ $num_comments++;
1325
+ }
1326
+
1327
+ return $num_comments;
1328
+ }
1329
+
1330
+ protected function parse_category_node( $node ) {
1331
+ $data = array(
1332
+ // Default taxonomy to "category", since this is a `<category>` tag
1333
+ 'taxonomy' => 'category',
1334
+ );
1335
+ $meta = array();
1336
+
1337
+ if ( $node->hasAttribute( 'domain' ) ) {
1338
+ $data['taxonomy'] = $node->getAttribute( 'domain' );
1339
+ }
1340
+ if ( $node->hasAttribute( 'nicename' ) ) {
1341
+ $data['slug'] = $node->getAttribute( 'nicename' );
1342
+ }
1343
+
1344
+ $data['name'] = $node->textContent;
1345
+
1346
+ if ( empty( $data['slug'] ) ) {
1347
+ return null;
1348
+ }
1349
+
1350
+ // Just for extra compatibility
1351
+ if ( $data['taxonomy'] === 'tag' ) {
1352
+ $data['taxonomy'] = 'post_tag';
1353
+ }
1354
+
1355
+ return $data;
1356
+ }
1357
+
1358
+ /**
1359
+ * Callback for `usort` to sort comments by ID
1360
+ *
1361
+ * @param array $a Comment data for the first comment
1362
+ * @param array $b Comment data for the second comment
1363
+ * @return int
1364
+ */
1365
+ public static function sort_comments_by_id( $a, $b ) {
1366
+ if ( empty( $a['comment_id'] ) ) {
1367
+ return 1;
1368
+ }
1369
+
1370
+ if ( empty( $b['comment_id'] ) ) {
1371
+ return -1;
1372
+ }
1373
+
1374
+ return $a['comment_id'] - $b['comment_id'];
1375
+ }
1376
+
1377
+ protected function parse_term_node( $node, $type = 'term' ) {
1378
+ $data = array();
1379
+ $meta = array();
1380
+
1381
+ $tag_name = array(
1382
+ 'id' => 'wp:term_id',
1383
+ 'taxonomy' => 'wp:term_taxonomy',
1384
+ 'slug' => 'wp:term_slug',
1385
+ 'parent' => 'wp:term_parent',
1386
+ 'name' => 'wp:term_name',
1387
+ 'description' => 'wp:term_description',
1388
+ );
1389
+ $taxonomy = null;
1390
+
1391
+ // Special casing!
1392
+ switch ( $type ) {
1393
+ case 'category':
1394
+ $tag_name['slug'] = 'wp:category_nicename';
1395
+ $tag_name['parent'] = 'wp:category_parent';
1396
+ $tag_name['name'] = 'wp:cat_name';
1397
+ $tag_name['description'] = 'wp:category_description';
1398
+ $tag_name['taxonomy'] = null;
1399
+
1400
+ $data['taxonomy'] = 'category';
1401
+ break;
1402
+
1403
+ case 'tag':
1404
+ $tag_name['slug'] = 'wp:tag_slug';
1405
+ $tag_name['parent'] = null;
1406
+ $tag_name['name'] = 'wp:tag_name';
1407
+ $tag_name['description'] = 'wp:tag_description';
1408
+ $tag_name['taxonomy'] = null;
1409
+
1410
+ $data['taxonomy'] = 'post_tag';
1411
+ break;
1412
+ }
1413
+
1414
+ foreach ( $node->childNodes as $child ) {
1415
+ // We only care about child elements
1416
+ if ( $child->nodeType !== XML_ELEMENT_NODE ) {
1417
+ continue;
1418
+ }
1419
+
1420
+ $key = array_search( $child->tagName, $tag_name );
1421
+ if ( $key ) {
1422
+ $data[ $key ] = $child->textContent;
1423
+ }
1424
+ }
1425
+
1426
+ if ( empty( $data['taxonomy'] ) ) {
1427
+ return null;
1428
+ }
1429
+
1430
+ // Compatibility with WXR 1.0
1431
+ if ( $data['taxonomy'] === 'tag' ) {
1432
+ $data['taxonomy'] = 'post_tag';
1433
+ }
1434
+
1435
+ return compact( 'data', 'meta' );
1436
+ }
1437
+
1438
+ protected function process_term( $data, $meta ) {
1439
+ /**
1440
+ * Pre-process term data.
1441
+ *
1442
+ * @param array $data Term data. (Return empty to skip.)
1443
+ * @param array $meta Meta data.
1444
+ */
1445
+ $data = apply_filters( 'wxr_importer.pre_process.term', $data, $meta );
1446
+ if ( empty( $data ) ) {
1447
+ return false;
1448
+ }
1449
+
1450
+ $original_id = isset( $data['id'] ) ? (int) $data['id'] : 0;
1451
+ $parent_id = isset( $data['parent'] ) ? (int) $data['parent'] : 0;
1452
+
1453
+ $mapping_key = sha1( $data['taxonomy'] . ':' . $data['slug'] );
1454
+ $existing = $this->term_exists( $data );
1455
+ if ( $existing ) {
1456
+
1457
+ /**
1458
+ * Term processing already imported.
1459
+ *
1460
+ * @param array $data Raw data imported for the term.
1461
+ */
1462
+ do_action( 'wxr_importer.process_already_imported.term', $data );
1463
+
1464
+ $this->mapping['term'][ $mapping_key ] = $existing;
1465
+ $this->mapping['term_id'][ $original_id ] = $existing;
1466
+ return false;
1467
+ }
1468
+
1469
+ // WP really likes to repeat itself in export files
1470
+ if ( isset( $this->mapping['term'][ $mapping_key ] ) ) {
1471
+ return false;
1472
+ }
1473
+
1474
+ $termdata = array();
1475
+ $allowed = array(
1476
+ 'slug' => true,
1477
+ 'description' => true,
1478
+ );
1479
+
1480
+ // Map the parent comment, or mark it as one we need to fix
1481
+ // TODO: add parent mapping and remapping
1482
+ /*$requires_remapping = false;
1483
+ if ( $parent_id ) {
1484
+ if ( isset( $this->mapping['term'][ $parent_id ] ) ) {
1485
+ $data['parent'] = $this->mapping['term'][ $parent_id ];
1486
+ } else {
1487
+ // Prepare for remapping later
1488
+ $meta[] = array( 'key' => '_wxr_import_parent', 'value' => $parent_id );
1489
+ $requires_remapping = true;
1490
+
1491
+ // Wipe the parent for now
1492
+ $data['parent'] = 0;
1493
+ }
1494
+ }*/
1495
+
1496
+ foreach ( $data as $key => $value ) {
1497
+ if ( ! isset( $allowed[ $key ] ) ) {
1498
+ continue;
1499
+ }
1500
+
1501
+ $termdata[ $key ] = $data[ $key ];
1502
+ }
1503
+
1504
+ $result = wp_insert_term( $data['name'], $data['taxonomy'], $termdata );
1505
+ if ( is_wp_error( $result ) ) {
1506
+ $this->logger->warning( sprintf(
1507
+ __( 'Failed to import %s %s', 'jupiterx-core' ),
1508
+ $data['taxonomy'],
1509
+ $data['name']
1510
+ ) );
1511
+ $this->logger->debug( $result->get_error_message() );
1512
+ do_action( 'wp_import_insert_term_failed', $result, $data );
1513
+
1514
+ /**
1515
+ * Term processing failed.
1516
+ *
1517
+ * @param WP_Error $result Error object.
1518
+ * @param array $data Raw data imported for the term.
1519
+ * @param array $meta Meta data supplied for the term.
1520
+ */
1521
+ do_action( 'wxr_importer.process_failed.term', $result, $data, $meta );
1522
+ return false;
1523
+ }
1524
+
1525
+ $term_id = $result['term_id'];
1526
+
1527
+ $this->mapping['term'][ $mapping_key ] = $term_id;
1528
+ $this->mapping['term_id'][ $original_id ] = $term_id;
1529
+
1530
+ $this->logger->info( sprintf(
1531
+ __( 'Imported "%s" (%s)', 'jupiterx-core' ),
1532
+ $data['name'],
1533
+ $data['taxonomy']
1534
+ ) );
1535
+ $this->logger->debug( sprintf(
1536
+ __( 'Term %d remapped to %d', 'jupiterx-core' ),
1537
+ $original_id,
1538
+ $term_id
1539
+ ) );
1540
+
1541
+ do_action( 'wp_import_insert_term', $term_id, $data );
1542
+
1543
+ /**
1544
+ * Term processing completed.
1545
+ *
1546
+ * @param int $term_id New term ID.
1547
+ * @param array $data Raw data imported for the term.
1548
+ */
1549
+ do_action( 'wxr_importer.processed.term', $term_id, $data );
1550
+ }
1551
+
1552
+ /**
1553
+ * Attempt to download a remote file attachment
1554
+ *
1555
+ * @param string $url URL of item to fetch
1556
+ * @param array $post Attachment details
1557
+ * @return array|WP_Error Local file location details on success, WP_Error otherwise
1558
+ */
1559
+ protected function fetch_remote_file( $url, $post ) {
1560
+ // extract the file name and extension from the url
1561
+ $file_name = basename( $url );
1562
+
1563
+ // get placeholder file in the upload dir with a unique, sanitized filename
1564
+ $upload = wp_upload_bits( $file_name, 0, '', $post['upload_date'] );
1565
+ if ( $upload['error'] ) {
1566
+ return new WP_Error( 'upload_dir_error', $upload['error'] );
1567
+ }
1568
+
1569
+ // fetch the remote url and write it to the placeholder file
1570
+ $response = wp_remote_get( $url, array(
1571
+ 'stream' => true,
1572
+ 'filename' => $upload['file'],
1573
+ ) );
1574
+
1575
+ // request failed
1576
+ if ( is_wp_error( $response ) ) {
1577
+ unlink( $upload['file'] );
1578
+ return $response;
1579
+ }
1580
+
1581
+ $code = (int) wp_remote_retrieve_response_code( $response );
1582
+
1583
+ // make sure the fetch was successful
1584
+ if ( $code !== 200 ) {
1585
+ unlink( $upload['file'] );
1586
+ return new WP_Error(
1587
+ 'import_file_error',
1588
+ sprintf(
1589
+ __( 'Remote server returned %1$d %2$s for %3$s', 'jupiterx-core' ),
1590
+ $code,
1591
+ get_status_header_desc( $code ),
1592
+ $url
1593
+ )
1594
+ );
1595
+ }
1596
+
1597
+ $filesize = filesize( $upload['file'] );
1598
+ $headers = wp_remote_retrieve_headers( $response );
1599
+
1600
+ if ( isset( $headers['content-length'] ) && $filesize !== (int) $headers['content-length'] ) {
1601
+ unlink( $upload['file'] );
1602
+ return new WP_Error( 'import_file_error', __( 'Remote file is incorrect size', 'jupiterx-core' ) );
1603
+ }
1604
+
1605
+ if ( 0 === $filesize ) {
1606
+ unlink( $upload['file'] );
1607
+ return new WP_Error( 'import_file_error', __( 'Zero size file downloaded', 'jupiterx-core' ) );
1608
+ }
1609
+
1610
+ $max_size = (int) $this->max_attachment_size();
1611
+ if ( ! empty( $max_size ) && $filesize > $max_size ) {
1612
+ unlink( $upload['file'] );
1613
+ $message = sprintf( __( 'Remote file is too large, limit is %s', 'jupiterx-core' ), size_format( $max_size ) );
1614
+ return new WP_Error( 'import_file_error', $message );
1615
+ }
1616
+
1617
+ return $upload;
1618
+ }
1619
+
1620
+ protected function post_process() {
1621
+ // Time to tackle any left-over bits
1622
+ if ( ! empty( $this->requires_remapping['post'] ) ) {
1623
+ $this->post_process_posts( $this->requires_remapping['post'] );
1624
+ }
1625
+ if ( ! empty( $this->requires_remapping['comment'] ) ) {
1626
+ $this->post_process_comments( $this->requires_remapping['comment'] );
1627
+ }
1628
+ }
1629
+
1630
+ protected function post_process_posts( $todo ) {
1631
+ foreach ( $todo as $post_id => $_ ) {
1632
+ $this->logger->debug( sprintf(
1633
+ // Note: title intentionally not used to skip extra processing
1634
+ // for when debug logging is off
1635
+ __( 'Running post-processing for post %d', 'jupiterx-core' ),
1636
+ $post_id
1637
+ ) );
1638
+
1639
+ $data = array();
1640
+
1641
+ $parent_id = get_post_meta( $post_id, '_wxr_import_parent', true );
1642
+ if ( ! empty( $parent_id ) ) {
1643
+ // Have we imported the parent now?
1644
+ if ( isset( $this->mapping['post'][ $parent_id ] ) ) {
1645
+ $data['post_parent'] = $this->mapping['post'][ $parent_id ];
1646
+ } else {
1647
+ $this->logger->warning( sprintf(
1648
+ __( 'Could not find the post parent for "%s" (post #%d)', 'jupiterx-core' ),
1649
+ get_the_title( $post_id ),
1650
+ $post_id
1651
+ ) );
1652
+ $this->logger->debug( sprintf(
1653
+ __( 'Post %d was imported with parent %d, but could not be found', 'jupiterx-core' ),
1654
+ $post_id,
1655
+ $parent_id
1656
+ ) );
1657
+ }
1658
+ }
1659
+
1660
+ $author_slug = get_post_meta( $post_id, '_wxr_import_user_slug', true );
1661
+ if ( ! empty( $author_slug ) ) {
1662
+ // Have we imported the user now?
1663
+ if ( isset( $this->mapping['user_slug'][ $author_slug ] ) ) {
1664
+ $data['post_author'] = $this->mapping['user_slug'][ $author_slug ];
1665
+ } else {
1666
+ $this->logger->warning( sprintf(
1667
+ __( 'Could not find the author for "%s" (post #%d)', 'jupiterx-core' ),
1668
+ get_the_title( $post_id ),
1669
+ $post_id
1670
+ ) );
1671
+ $this->logger->debug( sprintf(
1672
+ __( 'Post %d was imported with author "%s", but could not be found', 'jupiterx-core' ),
1673
+ $post_id,
1674
+ $author_slug
1675
+ ) );
1676
+ }
1677
+ }
1678
+
1679
+ $has_attachments = get_post_meta( $post_id, '_wxr_import_has_attachment_refs', true );
1680
+ if ( ! empty( $has_attachments ) ) {
1681
+ $post = get_post( $post_id );
1682
+ $content = $post->post_content;
1683
+
1684
+ // Replace all the URLs we've got
1685
+ $new_content = str_replace( array_keys( $this->url_remap ), $this->url_remap, $content );
1686
+ if ( $new_content !== $content ) {
1687
+ $data['post_content'] = $new_content;
1688
+ }
1689
+ }
1690
+
1691
+ if ( get_post_type( $post_id ) === 'nav_menu_item' ) {
1692
+ $this->post_process_menu_item( $post_id );
1693
+ }
1694
+
1695
+ // Do we have updates to make?
1696
+ if ( empty( $data ) ) {
1697
+ $this->logger->debug( sprintf(
1698
+ __( 'Post %d was marked for post-processing, but none was required.', 'jupiterx-core' ),
1699
+ $post_id
1700
+ ) );
1701
+ continue;
1702
+ }
1703
+
1704
+ // Run the update
1705
+ $data['ID'] = $post_id;
1706
+ $result = wp_update_post( $data, true );
1707
+ if ( is_wp_error( $result ) ) {
1708
+ $this->logger->warning( sprintf(
1709
+ __( 'Could not update "%s" (post #%d) with mapped data', 'jupiterx-core' ),
1710
+ get_the_title( $post_id ),
1711
+ $post_id
1712
+ ) );
1713
+ $this->logger->debug( $result->get_error_message() );
1714
+ continue;
1715
+ }
1716
+
1717
+ // Clear out our temporary meta keys
1718
+ delete_post_meta( $post_id, '_wxr_import_parent' );
1719
+ delete_post_meta( $post_id, '_wxr_import_user_slug' );
1720
+ delete_post_meta( $post_id, '_wxr_import_has_attachment_refs' );
1721
+ }
1722
+ }
1723
+
1724
+ protected function post_process_menu_item( $post_id ) {
1725
+ $menu_object_id = get_post_meta( $post_id, '_wxr_import_menu_item', true );
1726
+ if ( empty( $menu_object_id ) ) {
1727
+ // No processing needed!
1728
+ return;
1729
+ }
1730
+
1731
+ $menu_item_type = get_post_meta( $post_id, '_menu_item_type', true );
1732
+ switch ( $menu_item_type ) {
1733
+ case 'taxonomy':
1734
+ if ( isset( $this->mapping['term_id'][ $menu_object_id ] ) ) {
1735
+ $menu_object = $this->mapping['term_id'][ $menu_object_id ];
1736
+ }
1737
+ break;
1738
+
1739
+ case 'post_type':
1740
+ if ( isset( $this->mapping['post'][ $menu_object_id ] ) ) {
1741
+ $menu_object = $this->mapping['post'][ $menu_object_id ];
1742
+ }
1743
+ break;
1744
+
1745
+ default:
1746
+ // Cannot handle this.
1747
+ return;
1748
+ }
1749
+
1750
+ if ( ! empty( $menu_object ) ) {
1751
+ update_post_meta( $post_id, '_menu_item_object_id', wp_slash( $menu_object ) );
1752
+ } else {
1753
+ $this->logger->warning( sprintf(
1754
+ __( 'Could not find the menu object for "%s" (post #%d)', 'jupiterx-core' ),
1755
+ get_the_title( $post_id ),
1756
+ $post_id
1757
+ ) );
1758
+ $this->logger->debug( sprintf(
1759
+ __( 'Post %d was imported with object "%d" of type "%s", but could not be found', 'jupiterx-core' ),
1760
+ $post_id,
1761
+ $menu_object_id,
1762
+ $menu_item_type
1763
+ ) );
1764
+ }
1765
+
1766
+ delete_post_meta( $post_id, '_wxr_import_menu_item' );
1767
+ }
1768
+
1769
+
1770
+ protected function post_process_comments( $todo ) {
1771
+ foreach ( $todo as $comment_id => $_ ) {
1772
+ $data = array();
1773
+
1774
+ $parent_id = get_comment_meta( $comment_id, '_wxr_import_parent', true );
1775
+ if ( ! empty( $parent_id ) ) {
1776
+ // Have we imported the parent now?
1777
+ if ( isset( $this->mapping['comment'][ $parent_id ] ) ) {
1778
+ $data['comment_parent'] = $this->mapping['comment'][ $parent_id ];
1779
+ } else {
1780
+ $this->logger->warning( sprintf(
1781
+ __( 'Could not find the comment parent for comment #%d', 'jupiterx-core' ),
1782
+ $comment_id
1783
+ ) );
1784
+ $this->logger->debug( sprintf(
1785
+ __( 'Comment %d was imported with parent %d, but could not be found', 'jupiterx-core' ),
1786
+ $comment_id,
1787
+ $parent_id
1788
+ ) );
1789
+ }
1790
+ }
1791
+
1792
+ $author_id = get_comment_meta( $comment_id, '_wxr_import_user', true );
1793
+ if ( ! empty( $author_id ) ) {
1794
+ // Have we imported the user now?
1795
+ if ( isset( $this->mapping['user'][ $author_id ] ) ) {
1796
+ $data['user_id'] = $this->mapping['user'][ $author_id ];
1797
+ } else {
1798
+ $this->logger->warning( sprintf(
1799
+ __( 'Could not find the author for comment #%d', 'jupiterx-core' ),
1800
+ $comment_id
1801
+ ) );
1802
+ $this->logger->debug( sprintf(
1803
+ __( 'Comment %d was imported with author %d, but could not be found', 'jupiterx-core' ),
1804
+ $comment_id,
1805
+ $author_id
1806
+ ) );
1807
+ }
1808
+ }
1809
+
1810
+ // Do we have updates to make?
1811
+ if ( empty( $data ) ) {
1812
+ continue;
1813
+ }
1814
+
1815
+ // Run the update
1816
+ $data['comment_ID'] = $comment_ID;
1817
+ $result = wp_update_comment( wp_slash( $data ) );
1818
+ if ( empty( $result ) ) {
1819
+ $this->logger->warning( sprintf(
1820
+ __( 'Could not update comment #%d with mapped data', 'jupiterx-core' ),
1821
+ $comment_id
1822
+ ) );
1823
+ continue;
1824
+ }
1825
+
1826
+ // Clear out our temporary meta keys
1827
+ delete_comment_meta( $comment_id, '_wxr_import_parent' );
1828
+ delete_comment_meta( $comment_id, '_wxr_import_user' );
1829
+ }
1830
+ }
1831
+
1832
+ /**
1833
+ * Use stored mapping information to update old attachment URLs
1834
+ */
1835
+ protected function replace_attachment_urls_in_content() {
1836
+ global $wpdb;
1837
+ // make sure we do the longest urls first, in case one is a substring of another
1838
+ uksort( $this->url_remap, array( $this, 'cmpr_strlen' ) );
1839
+
1840
+ foreach ( $this->url_remap as $from_url => $to_url ) {
1841
+ // remap urls in post_content
1842
+ $query = $wpdb->prepare( "UPDATE {$wpdb->posts} SET post_content = REPLACE(post_content, %s, %s)", $from_url, $to_url );
1843
+ $wpdb->query( $query );
1844
+
1845
+ // remap enclosure urls
1846
+ $query = $wpdb->prepare( "UPDATE {$wpdb->postmeta} SET meta_value = REPLACE(meta_value, %s, %s) WHERE meta_key='enclosure'", $from_url, $to_url );
1847
+ $result = $wpdb->query( $query );
1848
+ }
1849
+ }
1850
+
1851
+ /**
1852
+ * Update _thumbnail_id meta to new, imported attachment IDs
1853
+ */
1854
+ function remap_featured_images() {
1855
+ // cycle through posts that have a featured image
1856
+ foreach ( $this->featured_images as $post_id => $value ) {
1857
+ if ( isset( $this->processed_posts[ $value ] ) ) {
1858
+ $new_id = $this->processed_posts[ $value ];
1859
+
1860
+ // only update if there's a difference
1861
+ if ( $new_id !== $value ) {
1862
+ update_post_meta( $post_id, '_thumbnail_id', $new_id );
1863
+ }
1864
+ }
1865
+ }
1866
+ }
1867
+
1868
+ /**
1869
+ * Decide if the given meta key maps to information we will want to import
1870
+ *
1871
+ * @param string $key The meta key to check
1872
+ * @return string|bool The key if we do want to import, false if not
1873
+ */
1874
+ public function is_valid_meta_key( $key ) {
1875
+ // skip attachment metadata since we'll regenerate it from scratch
1876
+ // skip _edit_lock as not relevant for import
1877
+ if ( in_array( $key, array( '_wp_attached_file', '_wp_attachment_metadata', '_edit_lock' ) ) ) {
1878
+ return false;
1879
+ }
1880
+
1881
+ return $key;
1882
+ }
1883
+
1884
+ /**
1885
+ * Decide what the maximum file size for downloaded attachments is.
1886
+ * Default is 0 (unlimited), can be filtered via import_attachment_size_limit
1887
+ *
1888
+ * @return int Maximum attachment file size to import
1889
+ */
1890
+ protected function max_attachment_size() {
1891
+ return apply_filters( 'import_attachment_size_limit', 0 );
1892
+ }
1893
+
1894
+ /**
1895
+ * Added to http_request_timeout filter to force timeout at 60 seconds during import
1896
+ *
1897
+ * @access protected
1898
+ * @return int 60
1899
+ */
1900
+ function bump_request_timeout($val) {
1901
+ return 60;
1902
+ }
1903
+
1904
+ // return the difference in length between two strings
1905
+ function cmpr_strlen( $a, $b ) {
1906
+ return strlen( $b ) - strlen( $a );
1907
+ }
1908
+
1909
+ /**
1910
+ * Prefill existing post data.
1911
+ *
1912
+ * This preloads all GUIDs into memory, allowing us to avoid hitting the
1913
+ * database when we need to check for existence. With larger imports, this
1914
+ * becomes prohibitively slow to perform SELECT queries on each.
1915
+ *
1916
+ * By preloading all this data into memory, it's a constant-time lookup in
1917
+ * PHP instead. However, this does use a lot more memory, so for sites doing
1918
+ * small imports onto a large site, it may be a better tradeoff to use
1919
+ * on-the-fly checking instead.
1920
+ */
1921
+ protected function prefill_existing_posts() {
1922
+ global $wpdb;
1923
+ $posts = $wpdb->get_results( "SELECT ID, guid FROM {$wpdb->posts}" );
1924
+
1925
+ foreach ( $posts as $item ) {
1926
+ $this->exists['post'][ wp_specialchars_decode( $item->guid ) ] = $item->ID;
1927
+ }
1928
+ }
1929
+
1930
+ /**
1931
+ * Does the post exist?
1932
+ *
1933
+ * @param array $data Post data to check against.
1934
+ * @return int|bool Existing post ID if it exists, false otherwise.
1935
+ */
1936
+ protected function post_exists( $data ) {
1937
+ // Constant-time lookup if we prefilled
1938
+ $exists_key = wp_specialchars_decode( $data['guid'] );
1939
+
1940
+ if ( $this->options['prefill_existing_posts'] ) {
1941
+ return isset( $this->exists['post'][ $exists_key ] ) ? $this->exists['post'][ $exists_key ] : false;
1942
+ }
1943
+
1944
+ // No prefilling, but might have already handled it
1945
+ if ( isset( $this->exists['post'][ $exists_key ] ) ) {
1946
+ return $this->exists['post'][ $exists_key ];
1947
+ }
1948
+
1949
+ // Still nothing, try post_exists, and cache it
1950
+ $exists = post_exists( $data['post_title'], $data['post_content'], $data['post_date'] );
1951
+ $this->exists['post'][ $exists_key ] = $exists;
1952
+
1953
+ return $exists;
1954
+ }
1955
+
1956
+ /**
1957
+ * Mark the post as existing.
1958
+ *
1959
+ * @param array $data Post data to mark as existing.
1960
+ * @param int $post_id Post ID.
1961
+ */
1962
+ protected function mark_post_exists( $data, $post_id ) {
1963
+ $exists_key = $data['guid'];
1964
+ $this->exists['post'][ $exists_key ] = $post_id;
1965
+ }
1966
+
1967
+ /**
1968
+ * Prefill existing comment data.
1969
+ *
1970
+ * @see self::prefill_existing_posts() for justification of why this exists.
1971
+ */
1972
+ protected function prefill_existing_comments() {
1973
+ global $wpdb;
1974
+ $posts = $wpdb->get_results( "SELECT comment_ID, comment_author, comment_date FROM {$wpdb->comments}" );
1975
+
1976
+ foreach ( $posts as $item ) {
1977
+ $exists_key = sha1( $item->comment_author . ':' . $item->comment_date );
1978
+ $this->exists['comment'][ $exists_key ] = $item->comment_ID;
1979
+ }
1980
+ }
1981
+
1982
+ /**
1983
+ * Does the comment exist?
1984
+ *
1985
+ * @param array $data Comment data to check against.
1986
+ * @return int|bool Existing comment ID if it exists, false otherwise.
1987
+ */
1988
+ protected function comment_exists( $data ) {
1989
+ $exists_key = sha1( $data['comment_author'] . ':' . $data['comment_date'] );
1990
+
1991
+ // Constant-time lookup if we prefilled
1992
+ if ( $this->options['prefill_existing_comments'] ) {
1993
+ return isset( $this->exists['comment'][ $exists_key ] ) ? $this->exists['comment'][ $exists_key ] : false;
1994
+ }
1995
+
1996
+ // No prefilling, but might have already handled it
1997
+ if ( isset( $this->exists['comment'][ $exists_key ] ) ) {
1998
+ return $this->exists['comment'][ $exists_key ];
1999
+ }
2000
+
2001
+ // Still nothing, try comment_exists, and cache it
2002
+ $exists = comment_exists( $data['comment_author'], $data['comment_date'] );
2003
+ $this->exists['comment'][ $exists_key ] = $exists;
2004
+
2005
+ return $exists;
2006
+ }
2007
+
2008
+ /**
2009
+ * Mark the comment as existing.
2010
+ *
2011
+ * @param array $data Comment data to mark as existing.
2012
+ * @param int $comment_id Comment ID.
2013
+ */
2014
+ protected function mark_comment_exists( $data, $comment_id ) {
2015
+ $exists_key = sha1( $data['comment_author'] . ':' . $data['comment_date'] );
2016
+ $this->exists['comment'][ $exists_key ] = $comment_id;
2017
+ }
2018
+
2019
+ /**
2020
+ * Prefill existing term data.
2021
+ *
2022
+ * @see self::prefill_existing_posts() for justification of why this exists.
2023
+ */
2024
+ protected function prefill_existing_terms() {
2025
+ global $wpdb;
2026
+ $query = "SELECT t.term_id, tt.taxonomy, t.slug FROM {$wpdb->terms} AS t";
2027
+ $query .= " JOIN {$wpdb->term_taxonomy} AS tt ON t.term_id = tt.term_id";
2028
+ $terms = $wpdb->get_results( $query );
2029
+
2030
+ foreach ( $terms as $item ) {
2031
+ $exists_key = sha1( $item->taxonomy . ':' . $item->slug );
2032
+ $this->exists['term'][ $exists_key ] = $item->term_id;
2033
+ }
2034
+ }
2035
+
2036
+ /**
2037
+ * Does the term exist?
2038
+ *
2039
+ * @param array $data Term data to check against.
2040
+ * @return int|bool Existing term ID if it exists, false otherwise.
2041
+ */
2042
+ protected function term_exists( $data ) {
2043
+ $exists_key = sha1( $data['taxonomy'] . ':' . $data['slug'] );
2044
+
2045
+ // Constant-time lookup if we prefilled
2046
+ if ( $this->options['prefill_existing_terms'] ) {
2047
+ return isset( $this->exists['term'][ $exists_key ] ) ? $this->exists['term'][ $exists_key ] : false;
2048
+ }
2049
+
2050
+ // No prefilling, but might have already handled it
2051
+ if ( isset( $this->exists['term'][ $exists_key ] ) ) {
2052
+ return $this->exists['term'][ $exists_key ];
2053
+ }
2054
+
2055
+ // Still nothing, try comment_exists, and cache it
2056
+ $exists = term_exists( $data['slug'], $data['taxonomy'] );
2057
+ if ( is_array( $exists ) ) {
2058
+ $exists = $exists['term_id'];
2059
+ }
2060
+
2061
+ $this->exists['term'][ $exists_key ] = $exists;
2062
+
2063
+ return $exists;
2064
+ }
2065
+
2066
+ /**
2067
+ * Mark the term as existing.
2068
+ *
2069
+ * @param array $data Term data to mark as existing.
2070
+ * @param int $term_id Term ID.
2071
+ */
2072
+ protected function mark_term_exists( $data, $term_id ) {
2073
+ $exists_key = sha1( $data['taxonomy'] . ':' . $data['slug'] );
2074
+ $this->exists['term'][ $exists_key ] = $term_id;
2075
+ }
2076
+ }
includes/control-panel/includes/logic-messages.php CHANGED
@@ -1,456 +1,456 @@
1
- <?php
2
- if ( ! function_exists( 'jupiterx_logic_message_helper' ) ) {
3
- /**
4
- * This function is responsible to store every message we use in system , this way we can translate them or convert them
5
- * to meaningfull messages in easy steps.
6
- * also this way we can translate all third-party messages too
7
- * We can pass some parameters in the message as long as we follow this format:
8
- * array(
9
- * '{param} is activated in {param}.', // Message will be displayed
10
- * 'Artbees Captcha', // Will replace first {param}
11
- * 'Jupiter', // Will replace second {param}
12
- * 'Etc...', // Will replace third {param} (if exist), etc.
13
- * )
14
- *
15
- * @author Reza Marandi <ross@artbees.net>
16
- *
17
- * @copyright Artbees LTD (c)
18
- * @link https://artbees.net
19
- * @version 5.5
20
- * @package jupiter
21
- *
22
- * @param string $which_page Which page we should look for the message (for example : plugin-management , template-management , ..)
23
- * @param string $which_string Which message we are looking for ?
24
- * @param string $which_type Which message type is suitable to return ? (user_msg , system_msg)
25
- *
26
- * @return string replaced and prepared message
27
- */
28
- function jupiterx_logic_message_helper( $which_page, $which_string, $which_type = 'user_msg' ) {
29
- if ( empty( $which_page ) || empty( $which_string ) ) {
30
- return null;
31
- }
32
-
33
- /*====================== FILESYSTEM ============================*/
34
- $filesystem_messages = array(
35
- array(
36
- 'sys_msg' => 'No filesystem credentials found!',
37
- 'user_msg' => __( '', 'jupiterx-core' ),
38
- ),
39
- );
40
-
41
- /*====================== TEST CASES ============================*/
42
- $test_cases_messages = array(
43
- array(
44
- 'sys_msg' => 'error 1 in 0x0x111',
45
- 'user_msg' => __( '', 'jupiterx-core' ),
46
- ),
47
- array(
48
- 'sys_msg' => 'error 2 in 0x0x112',
49
- 'user_msg' => __( '', 'jupiterx-core' ),
50
- ),
51
- array(
52
- 'sys_msg' => 'error 3 in 0x0x113',
53
- 'user_msg' => __( '', 'jupiterx-core' ),
54
- ),
55
- array(
56
- 'sys_msg' => 'error 4 in 0x0x114 {param}',
57
- 'user_msg' => __( '', 'jupiterx-core' ),
58
- ),
59
- array(
60
- 'sys_msg' => 'error 5 in {param} 0x0x114 {param} you can say {param}',
61
- 'user_msg' => __( '', 'jupiterx-core' ),
62
- ),
63
- );
64
- /*====================== PLUGIN MANAGEMENT ============================*/
65
- $plugin_management_messages = array(
66
- array(
67
- 'sys_msg' => 'Can not find plugin path in deactivate plugin func',
68
- 'user_msg' => __( 'Whoops! We seem to be having some problems. Please contact Support and supply the number shown and we\'ll be happy to figure this out for you. (code 200)', 'jupiterx-core' ),
69
- ),
70
- array(
71
- 'sys_msg' => 'Can not find plugin path in activate plugin func',
72
- 'user_msg' => __( 'Whoops! We seem to be having some problems. Please contact Support and supply the number shown and we\'ll be happy to figure this out for you. (code 201)', 'jupiterx-core' ),
73
- ),
74
- array(
75
- 'sys_msg' => 'Plugin successfully added and activated.',
76
- 'user_msg' => __( 'Your plugin has been successfully added and activated.', 'jupiterx-core' ),
77
- ),
78
- array(
79
- 'sys_msg' => 'Plugin directory is not writable.',
80
- 'user_msg' => __( 'Whoops! We seem to be having some problems. Please contact Support and supply the number shown and we\'ll be happy to figure this out for you. (code 202)', 'jupiterx-core' ),
81
- ),
82
- array(
83
- 'sys_msg' => 'Successfull',
84
- 'user_msg' => __( 'Congratulations,your operation has been done successfully', 'jupiterx-core' ),
85
- ),
86
- array(
87
- 'sys_msg' => 'Plugins source not found or it have invalid url',
88
- 'user_msg' => __( 'Whoops! We seem to be having some problems. Please contact Support and supply the number shown and we\'ll be happy to figure this out for you. (code 203)', 'jupiterx-core' ),
89
- ),
90
- array(
91
- 'sys_msg' => 'Can not find {param} plugin path.',
92
- 'user_msg' => __( 'Whoops! We seem to be having some problems. Please contact Support and supply the number shown and we\'ll be happy to figure this out for you. (code 204)', 'jupiterx-core' ),
93
- ),
94
- array(
95
- 'sys_msg' => 'Plugin parent directory is not writable - RPPM01x01.',
96
- 'user_msg' => __( 'Whoops! We seem to be having some problems. Please contact Support and supply the number shown and we\'ll be happy to figure this out for you. (code 205)', 'jupiterx-core' ),
97
- ),
98
- array(
99
- 'sys_msg' => 'Plugin directory is not writable - RPPM01x01',
100
- 'user_msg' => __( 'Whoops! We seem to be having some problems. Please contact Support and supply the number shown and we\'ll be happy to figure this out for you. (code 206)', 'jupiterx-core' ),
101
- ),
102
- array(
103
- 'sys_msg' => 'Can not remove directory of plugin - RPPM01x01-Directory',
104
- 'user_msg' => __( 'Whoops! We seem to be having some problems. Please contact Support and supply the number shown and we\'ll be happy to figure this out for you. (code 207)', 'jupiterx-core' ),
105
- ),
106
- array(
107
- 'sys_msg' => 'Can not remove directory of plugin - RPPM01x01-File',
108
- 'user_msg' => __( 'Whoops! We seem to be having some problems. Please contact Support and supply the number shown and we\'ll be happy to figure this out for you. (code 208)', 'jupiterx-core' ),
109
- ),
110
- array(
111
- 'sys_msg' => 'Plugin you want to remove is not exist - RPPM01x01',
112
- 'user_msg' => __( 'Whoops! We seem to be having some problems. Please contact Support and supply the number shown and we\'ll be happy to figure this out for you. (code 209)', 'jupiterx-core' ),
113
- ),
114
- array(
115
- 'sys_msg' => 'Plugin you want to remove is still activated , deactive it first - RPPM01x01.',
116
- 'user_msg' => __( 'To remove your plugin, please deactivate it first and then try again.', 'jupiterx-core' ),
117
- ),
118
- array(
119
- 'sys_msg' => 'Can not find plugin head file name.',
120
- 'user_msg' => __( 'Whoops! We seem to be having some problems. Please contact Support and supply the number shown and we\'ll be happy to figure this out for you. (code 210)', 'jupiterx-core' ),
121
- ),
122
- array(
123
- 'sys_msg' => 'Plugin successfully Updated.',
124
- 'user_msg' => __( 'Your plugin have been successfully updated.', 'jupiterx-core' ),
125
- ),
126
- array(
127
- 'sys_msg' => 'Plugin successfully Removed.',
128
- 'user_msg' => __( 'Your plugin have been successfully removed.', 'jupiterx-core' ),
129
- ),
130
- array(
131
- 'sys_msg' => 'Plugin list is not an array , use install method instead.',
132
- 'user_msg' => __( 'Whoops! We seem to be having some problems. Please contact Support and supply the number shown and we\'ll be happy to figure this out for you. (code 211)', 'jupiterx-core' ),
133
- ),
134
- array(
135
- 'sys_msg' => 'The plugin ({param}) you are looking for is not exist.',
136
- 'user_msg' => __( 'Whoops! We seem to be having some problems. Please contact Support and supply the number shown and we\'ll be happy to figure this out for you. (code 212)', 'jupiterx-core' ),
137
- ),
138
- array(
139
- 'sys_msg' => 'Plugin list is empty',
140
- 'user_msg' => __( 'Whoops! We seem to be having some problems. Please contact Support and supply the number shown and we\'ll be happy to figure this out for you. (code 213)', 'jupiterx-core' ),
141
- ),
142
- array(
143
- 'sys_msg' => '{param} plugins installed successfully',
144
- 'user_msg' => __( 'Whoops! We seem to be having some problems. Please contact Support and supply the number shown and we\'ll be happy to figure this out for you. (code 214)', 'jupiterx-core' ),
145
- ),
146
- array(
147
- 'sys_msg' => 'Plugin you want to remove is not exist - RPPM01x01.',
148
- 'user_msg' => __( 'Whoops! We seem to be having some problems. Please contact Support and supply the number shown and we\'ll be happy to figure this out for you. (code 215)', 'jupiterx-core' ),
149
- ),
150
- array(
151
- 'sys_msg' => 'Undefined',
152
- 'user_msg' => __( 'Whoops! We seem to be having some problems. Please contact Support and supply the number shown and we\'ll be happy to figure this out for you. (code 216)', 'jupiterx-core' ),
153
- ),
154
- );
155
-
156
- /*====================== ADDON MANAGEMENT ============================*/
157
- $addon_management_messages = array(
158
- array(
159
- 'sys_msg' => 'System problem , please call support',
160
- 'user_msg' => __( '', 'jupiterx-core' ),
161
- ),
162
- array(
163
- 'sys_msg' => 'Successfull',
164
- 'user_msg' => __( '', 'jupiterx-core' ),
165
- ),
166
- array(
167
- 'sys_msg' => 'System problem at installing , please call support',
168
- 'user_msg' => __( '', 'jupiterx-core' ),
169
- ),
170
- array(
171
- 'sys_msg' => 'Add-on activated successfully',
172
- 'user_msg' => __( '', 'jupiterx-core' ),
173
- ),
174
- array(
175
- 'sys_msg' => 'The Add-On you are looking for is not exist.',
176
- 'user_msg' => __( '', 'jupiterx-core' ),
177
- ),
178
- array(
179
- 'sys_msg' => 'The Add-On directory is not writable , Change the permission first.',
180
- 'user_msg' => __( '', 'jupiterx-core' ),
181
- ),
182
- array(
183
- 'sys_msg' => 'The Add-On removal process was not successfull.',
184
- 'user_msg' => __( '', 'jupiterx-core' ),
185
- ),
186
- array(
187
- 'sys_msg' => 'Add-on removed successfully',
188
- 'user_msg' => __( '', 'jupiterx-core' ),
189
- ),
190
- array(
191
- 'sys_msg' => 'Add-on you are looking for is not exist in API side',
192
- 'user_msg' => __( '', 'jupiterx-core' ),
193
- ),
194
- array(
195
- 'sys_msg' => 'You have latest version of this add-on.',
196
- 'user_msg' => __( '', 'jupiterx-core' ),
197
- ),
198
- array(
199
- 'sys_msg' => 'Add-on updated successfully',
200
- 'user_msg' => __( '', 'jupiterx-core' ),
201
- ),
202
- );
203
-
204
- /*====================== TEMPLATE MANAGEMENT ============================*/
205
- $template_management_messages = array(
206
- array(
207
- 'sys_msg' => 'System problem at installing , please contact support',
208
- 'user_msg' => __( '', 'jupiterx-core' ),
209
- ),
210
- array(
211
- 'sys_msg' => 'Choose template first',
212
- 'user_msg' => __( '', 'jupiterx-core' ),
213
- ),
214
- array(
215
- 'sys_msg' => 'Template assets are not completely exist - p1, Contact support.',
216
- 'user_msg' => __( '', 'jupiterx-core' ),
217
- ),
218
- array(
219
- 'sys_msg' => 'System problem , please contact support',
220
- 'user_msg' => __( '', 'jupiterx-core' ),
221
- ),
222
- array(
223
- 'sys_msg' => 'Successfull',
224
- 'user_msg' => __( '', 'jupiterx-core' ),
225
- ),
226
- array(
227
- 'sys_msg' => 'Template list is not what we expected',
228
- 'user_msg' => __( '', 'jupiterx-core' ),
229
- ),
230
- array(
231
- 'sys_msg' => 'Database reseted',
232
- 'user_msg' => __( '', 'jupiterx-core' ),
233
- ),
234
- array(
235
- 'sys_msg' => 'Choose one template first',
236
- 'user_msg' => __( '', 'jupiterx-core' ),
237
- ),
238
- array(
239
- 'sys_msg' => 'Template source URL is not validate',
240
- 'user_msg' => __( '', 'jupiterx-core' ),
241
- ),
242
- array(
243
- 'sys_msg' => 'Uploaded to server',
244
- 'user_msg' => __( '', 'jupiterx-core' ),
245
- ),
246
- array(
247
- 'sys_msg' => 'Cannot delete template zip file',
248
- 'user_msg' => __( '', 'jupiterx-core' ),
249
- ),
250
- array(
251
- 'sys_msg' => 'Completed',
252
- 'user_msg' => __( '', 'jupiterx-core' ),
253
- ),
254
- array(
255
- 'sys_msg' => 'Template assets are not completely exist - p2, Contact support.',
256
- 'user_msg' => __( '', 'jupiterx-core' ),
257
- ),
258
- array(
259
- 'sys_msg' => 'Plugin set have wrong structure',
260
- 'user_msg' => __( '', 'jupiterx-core' ),
261
- ),
262
- array(
263
- 'sys_msg' => '{param} plugins are installed.',
264
- 'user_msg' => __( '', 'jupiterx-core' ),
265
- ),
266
- array(
267
- 'sys_msg' => 'Template contents were imported.',
268
- 'user_msg' => __( '', 'jupiterx-core' ),
269
- ),
270
- array(
271
- 'sys_msg' => 'Navigation locations is configured.',
272
- 'user_msg' => __( '', 'jupiterx-core' ),
273
- ),
274
- array(
275
- 'sys_msg' => 'Default homepage and Shop Page is configured.',
276
- 'user_msg' => __( '', 'jupiterx-core' ),
277
- ),
278
- array(
279
- 'sys_msg' => 'Shop Page is configured.',
280
- 'user_msg' => __( '', 'jupiterx-core' ),
281
- ),
282
- array(
283
- 'sys_msg' => 'Default homepage is configured.',
284
- 'user_msg' => __( '', 'jupiterx-core' ),
285
- ),
286
- array(
287
- 'sys_msg' => 'Setup pages completed.',
288
- 'user_msg' => __( '', 'jupiterx-core' ),
289
- ),
290
- array(
291
- 'sys_msg' => 'Template options is empty',
292
- 'user_msg' => __( '', 'jupiterx-core' ),
293
- ),
294
- array(
295
- 'sys_msg' => 'Widgets are imported.',
296
- 'user_msg' => __( '', 'jupiterx-core' ),
297
- ),
298
- array(
299
- 'sys_msg' => 'Can not remove source files',
300
- 'user_msg' => __( '', 'jupiterx-core' ),
301
- ),
302
- array(
303
- 'sys_msg' => 'Widget data could not be read. Please try a different file.',
304
- 'user_msg' => __( '', 'jupiterx-core' ),
305
- ),
306
- array(
307
- 'sys_msg' => 'Sidebar does not exist in theme (using Inactive)',
308
- 'user_msg' => __( '', 'jupiterx-core' ),
309
- ),
310
- array(
311
- 'sys_msg' => 'Site does not support widget',
312
- 'user_msg' => __( '', 'jupiterx-core' ),
313
- ),
314
- array(
315
- 'sys_msg' => 'Widget already exists',
316
- 'user_msg' => __( '', 'jupiterx-core' ),
317
- ),
318
- array(
319
- 'sys_msg' => 'Imported',
320
- 'user_msg' => __( '', 'jupiterx-core' ),
321
- ),
322
- array(
323
- 'sys_msg' => 'Imported to Inactive',
324
- 'user_msg' => __( '', 'jupiterx-core' ),
325
- ),
326
- array(
327
- 'sys_msg' => 'Successfull',
328
- 'user_msg' => __( '', 'jupiterx-core' ),
329
- ),
330
- array(
331
- 'sys_msg' => 'LayerSlider is not installed , install it first',
332
- 'user_msg' => __( '', 'jupiterx-core' ),
333
- ),
334
- array(
335
- 'sys_msg' => 'LayerSlider is installed but not activated , activate it first',
336
- 'user_msg' => __( '', 'jupiterx-core' ),
337
- ),
338
- array(
339
- 'sys_msg' => 'Backup created.',
340
- 'user_msg' => __( '', 'jupiterx-core' ),
341
- ),
342
- array(
343
- 'sys_msg' => 'Not enough max_execution_time.',
344
- 'user_msg' => __( '' , 'jupiterx-core' ),
345
- ),
346
- array(
347
- 'sys_msg' => 'Not enough memory_limit.',
348
- 'user_msg' => __( '' , 'jupiterx-core' ),
349
- ),
350
- );
351
-
352
- /*====================== DB MANAGEMENT ============================*/
353
- $db_management_messages = array(
354
- array(
355
- 'sys_msg' => 'Can not create backup db file.',
356
- 'user_msg' => '',
357
- ),
358
- array(
359
- 'sys_msg' => 'Backup file is not created in right approach , try again.',
360
- 'user_msg' => '',
361
- ),
362
- array(
363
- 'sys_msg' => 'Backup Successfuly created',
364
- 'user_msg' => '',
365
- ),
366
- array(
367
- 'sys_msg' => 'Can not create index , Securesection',
368
- 'user_msg' => '',
369
- ),
370
- array(
371
- 'sys_msg' => 'Can not create htaccess , Securesection',
372
- 'user_msg' => '',
373
- ),
374
- );
375
-
376
- /*====================== DECISION LOGIC ============================*/
377
- switch ( $which_page ) {
378
- case 'filesystem':
379
- $data = $filesystem_messages;
380
- break;
381
- case 'test-cases':
382
- $data = $test_cases_messages;
383
- break;
384
- case 'plugin-management':
385
- $data = $plugin_management_messages;
386
- break;
387
- case 'addon-management':
388
- $data = $addon_management_messages;
389
- break;
390
- case 'template-management':
391
- $data = $template_management_messages;
392
- break;
393
- case 'db-management':
394
- $data = $db_management_messages;
395
- break;
396
- case 'theme-options':
397
- $data = $theme_options_messages;
398
- break;
399
- }
400
-
401
- // Check if the which_string is array
402
- $which_param = array();
403
- if ( is_array( $which_string ) ) {
404
- $which_param = $which_string;
405
- if ( ! empty( $which_param[0] ) ) {
406
- // Set back which_string as string, then remove it from which_param
407
- $which_string = $which_param[0];
408
- array_shift( $which_param );
409
- }
410
- }
411
-
412
- $message_key = JupiterX_Control_Panel_Helpers::search_multdim( $data, 'sys_msg', $which_string );
413
- if ( $message_key === false ) {
414
- // LOG THIS SECTION
415
- // DESC : This error message is not defined
416
- return jupiterx_logic_message_output( $which_string, $which_param );
417
- }
418
- if ( ! empty( $data[ $message_key ]['user_msg'] ) && $which_type == 'user_msg' ) {
419
- return jupiterx_logic_message_output( $data[ $message_key ]['user_msg'], $which_param );
420
- } else {
421
- // LOG THIS SECTION
422
- // DESC : User message is not defined , DEFINE IT (Translate Department);
423
- return jupiterx_logic_message_output( $data[ $message_key ]['sys_msg'], $which_param );
424
- }
425
- }
426
- }
427
-
428
- if ( ! function_exists( 'jupiterx_logic_message_output' ) ) {
429
- /**
430
- * Check the message format before return it. If the message need some parameter to pass, check the parameter
431
- * then replace it with the correct ones.
432
- *
433
- * @param string $message Message will be displayed
434
- * @param array $param Parameter to pass
435
- * @return string Output of the message in text format
436
- */
437
- function jupiterx_logic_message_output( $message = null, $param = array() ) {
438
- if ( $message == null ) {
439
- return 'Undefined';
440
- }
441
-
442
- // Check if param is not empty
443
- if ( ! empty( $param ) ) {
444
- // Count {param} that need to be replaced
445
- $count = substr_count( $message, '{param}' );
446
- if ( count( $param ) < $count ) {
447
- $param = array_pad( $param, $count, '{param}' );
448
- }
449
-
450
- $message = str_replace( '{param}', '%s', $message );
451
- $message = vsprintf( $message, $param );
452
- }
453
-
454
- return $message;
455
- }
456
- }
1
+ <?php
2
+ if ( ! function_exists( 'jupiterx_logic_message_helper' ) ) {
3
+ /**
4
+ * This function is responsible to store every message we use in system , this way we can translate them or convert them
5
+ * to meaningfull messages in easy steps.
6
+ * also this way we can translate all third-party messages too
7
+ * We can pass some parameters in the message as long as we follow this format:
8
+ * array(
9
+ * '{param} is activated in {param}.', // Message will be displayed
10
+ * 'Artbees Captcha', // Will replace first {param}
11
+ * 'Jupiter', // Will replace second {param}
12
+ * 'Etc...', // Will replace third {param} (if exist), etc.
13
+ * )
14
+ *
15
+ * @author Reza Marandi <ross@artbees.net>
16
+ *
17
+ * @copyright Artbees LTD (c)
18
+ * @link https://artbees.net
19
+ * @version 5.5
20
+ * @package jupiter
21
+ *
22
+ * @param string $which_page Which page we should look for the message (for example : plugin-management , template-management , ..)
23
+ * @param string $which_string Which message we are looking for ?
24
+ * @param string $which_type Which message type is suitable to return ? (user_msg , system_msg)
25
+ *
26
+ * @return string replaced and prepared message
27
+ */
28
+ function jupiterx_logic_message_helper( $which_page, $which_string, $which_type = 'user_msg' ) {
29
+ if ( empty( $which_page ) || empty( $which_string ) ) {
30
+ return null;
31
+ }
32
+
33
+ /*====================== FILESYSTEM ============================*/
34
+ $filesystem_messages = array(
35
+ array(
36
+ 'sys_msg' => 'No filesystem credentials found!',
37
+ 'user_msg' => __( '', 'jupiterx-core' ),
38
+ ),
39
+ );
40
+
41
+ /*====================== TEST CASES ============================*/
42
+ $test_cases_messages = array(
43
+ array(
44
+ 'sys_msg' => 'error 1 in 0x0x111',
45
+ 'user_msg' => __( '', 'jupiterx-core' ),
46
+ ),
47
+ array(
48
+ 'sys_msg' => 'error 2 in 0x0x112',
49
+ 'user_msg' => __( '', 'jupiterx-core' ),
50
+ ),
51
+ array(
52
+ 'sys_msg' => 'error 3 in 0x0x113',
53
+ 'user_msg' => __( '', 'jupiterx-core' ),
54
+ ),
55
+ array(
56
+ 'sys_msg' => 'error 4 in 0x0x114 {param}',
57
+ 'user_msg' => __( '', 'jupiterx-core' ),
58
+ ),
59
+ array(
60
+ 'sys_msg' => 'error 5 in {param} 0x0x114 {param} you can say {param}',
61
+ 'user_msg' => __( '', 'jupiterx-core' ),
62
+ ),
63
+ );
64
+ /*====================== PLUGIN MANAGEMENT ============================*/
65
+ $plugin_management_messages = array(
66
+ array(
67
+ 'sys_msg' => 'Can not find plugin path in deactivate plugin func',
68
+ 'user_msg' => __( 'Whoops! We seem to be having some problems. Please contact Support and supply the number shown and we\'ll be happy to figure this out for you. (code 200)', 'jupiterx-core' ),
69
+ ),
70
+ array(
71
+ 'sys_msg' => 'Can not find plugin path in activate plugin func',
72
+ 'user_msg' => __( 'Whoops! We seem to be having some problems. Please contact Support and supply the number shown and we\'ll be happy to figure this out for you. (code 201)', 'jupiterx-core' ),
73
+ ),
74
+ array(
75
+ 'sys_msg' => 'Plugin successfully added and activated.',
76
+ 'user_msg' => __( 'Your plugin has been successfully added and activated.', 'jupiterx-core' ),
77
+ ),
78
+ array(
79
+ 'sys_msg' => 'Plugin directory is not writable.',
80
+ 'user_msg' => __( 'Whoops! We seem to be having some problems. Please contact Support and supply the number shown and we\'ll be happy to figure this out for you. (code 202)', 'jupiterx-core' ),
81
+ ),
82
+ array(
83
+ 'sys_msg' => 'Successfull',
84
+ 'user_msg' => __( 'Congratulations,your operation has been done successfully', 'jupiterx-core' ),
85
+ ),
86
+ array(
87
+ 'sys_msg' => 'Plugins source not found or it have invalid url',
88
+ 'user_msg' => __( 'Whoops! We seem to be having some problems. Please contact Support and supply the number shown and we\'ll be happy to figure this out for you. (code 203)', 'jupiterx-core' ),
89
+ ),
90
+ array(
91
+ 'sys_msg' => 'Can not find {param} plugin path.',
92
+ 'user_msg' => __( 'Whoops! We seem to be having some problems. Please contact Support and supply the number shown and we\'ll be happy to figure this out for you. (code 204)', 'jupiterx-core' ),
93
+ ),
94
+ array(
95
+ 'sys_msg' => 'Plugin parent directory is not writable - RPPM01x01.',
96
+ 'user_msg' => __( 'Whoops! We seem to be having some problems. Please contact Support and supply the number shown and we\'ll be happy to figure this out for you. (code 205)', 'jupiterx-core' ),
97
+ ),
98
+ array(
99
+ 'sys_msg' => 'Plugin directory is not writable - RPPM01x01',
100
+ 'user_msg' => __( 'Whoops! We seem to be having some problems. Please contact Support and supply the number shown and we\'ll be happy to figure this out for you. (code 206)', 'jupiterx-core' ),
101
+ ),
102
+ array(
103
+ 'sys_msg' => 'Can not remove directory of plugin - RPPM01x01-Directory',
104
+ 'user_msg' => __( 'Whoops! We seem to be having some problems. Please contact Support and supply the number shown and we\'ll be happy to figure this out for you. (code 207)', 'jupiterx-core' ),
105
+ ),
106
+ array(
107
+ 'sys_msg' => 'Can not remove directory of plugin - RPPM01x01-File',
108
+ 'user_msg' => __( 'Whoops! We seem to be having some problems. Please contact Support and supply the number shown and we\'ll be happy to figure this out for you. (code 208)', 'jupiterx-core' ),
109
+ ),
110
+ array(
111
+ 'sys_msg' => 'Plugin you want to remove is not exist - RPPM01x01',
112
+ 'user_msg' => __( 'Whoops! We seem to be having some problems. Please contact Support and supply the number shown and we\'ll be happy to figure this out for you. (code 209)', 'jupiterx-core' ),
113
+ ),
114
+ array(
115
+ 'sys_msg' => 'Plugin you want to remove is still activated , deactive it first - RPPM01x01.',
116
+ 'user_msg' => __( 'To remove your plugin, please deactivate it first and then try again.', 'jupiterx-core' ),
117
+ ),
118
+ array(
119
+ 'sys_msg' => 'Can not find plugin head file name.',
120
+ 'user_msg' => __( 'Whoops! We seem to be having some problems. Please contact Support and supply the number shown and we\'ll be happy to figure this out for you. (code 210)', 'jupiterx-core' ),
121
+ ),
122
+ array(
123
+ 'sys_msg' => 'Plugin successfully Updated.',
124
+ 'user_msg' => __( 'Your plugin have been successfully updated.', 'jupiterx-core' ),
125
+ ),
126
+ array(
127
+ 'sys_msg' => 'Plugin successfully Removed.',
128
+ 'user_msg' => __( 'Your plugin have been successfully removed.', 'jupiterx-core' ),
129
+ ),
130
+ array(
131
+ 'sys_msg' => 'Plugin list is not an array , use install method instead.',
132
+ 'user_msg' => __( 'Whoops! We seem to be having some problems. Please contact Support and supply the number shown and we\'ll be happy to figure this out for you. (code 211)', 'jupiterx-core' ),
133
+ ),
134
+ array(
135
+ 'sys_msg' => 'The plugin ({param}) you are looking for is not exist.',
136
+ 'user_msg' => __( 'Whoops! We seem to be having some problems. Please contact Support and supply the number shown and we\'ll be happy to figure this out for you. (code 212)', 'jupiterx-core' ),
137
+ ),
138
+ array(
139
+ 'sys_msg' => 'Plugin list is empty',
140
+ 'user_msg' => __( 'Whoops! We seem to be having some problems. Please contact Support and supply the number shown and we\'ll be happy to figure this out for you. (code 213)', 'jupiterx-core' ),
141
+ ),
142
+ array(
143
+ 'sys_msg' => '{param} plugins installed successfully',
144
+ 'user_msg' => __( 'Whoops! We seem to be having some problems. Please contact Support and supply the number shown and we\'ll be happy to figure this out for you. (code 214)', 'jupiterx-core' ),
145
+ ),
146
+ array(
147
+ 'sys_msg' => 'Plugin you want to remove is not exist - RPPM01x01.',
148
+ 'user_msg' => __( 'Whoops! We seem to be having some problems. Please contact Support and supply the number shown and we\'ll be happy to figure this out for you. (code 215)', 'jupiterx-core' ),
149
+ ),
150
+ array(
151
+ 'sys_msg' => 'Undefined',
152
+ 'user_msg' => __( 'Whoops! We seem to be having some problems. Please contact Support and supply the number shown and we\'ll be happy to figure this out for you. (code 216)', 'jupiterx-core' ),
153
+ ),
154
+ );
155
+
156
+ /*====================== ADDON MANAGEMENT ============================*/
157
+ $addon_management_messages = array(
158
+ array(
159
+ 'sys_msg' => 'System problem , please call support',
160
+ 'user_msg' => __( '', 'jupiterx-core' ),
161
+ ),
162
+ array(
163
+ 'sys_msg' => 'Successfull',
164
+ 'user_msg' => __( '', 'jupiterx-core' ),
165
+ ),
166
+ array(
167
+ 'sys_msg' => 'System problem at installing , please call support',
168
+ 'user_msg' => __( '', 'jupiterx-core' ),
169
+ ),
170
+ array(
171
+ 'sys_msg' => 'Add-on activated successfully',
172
+ 'user_msg' => __( '', 'jupiterx-core' ),
173
+ ),
174
+ array(
175
+ 'sys_msg' => 'The Add-On you are looking for is not exist.',
176
+ 'user_msg' => __( '', 'jupiterx-core' ),
177
+ ),
178
+ array(
179
+ 'sys_msg' => 'The Add-On directory is not writable , Change the permission first.',
180
+ 'user_msg' => __( '', 'jupiterx-core' ),
181
+ ),
182
+ array(
183
+ 'sys_msg' => 'The Add-On removal process was not successfull.',
184
+ 'user_msg' => __( '', 'jupiterx-core' ),
185
+ ),
186
+ array(
187
+ 'sys_msg' => 'Add-on removed successfully',
188
+ 'user_msg' => __( '', 'jupiterx-core' ),
189
+ ),
190
+ array(
191
+ 'sys_msg' => 'Add-on you are looking for is not exist in API side',
192
+ 'user_msg' => __( '', 'jupiterx-core' ),
193
+ ),
194
+ array(
195
+ 'sys_msg' => 'You have latest version of this add-on.',
196
+ 'user_msg' => __( '', 'jupiterx-core' ),
197
+ ),
198
+ array(
199
+ 'sys_msg' => 'Add-on updated successfully',
200
+ 'user_msg' => __( '', 'jupiterx-core' ),
201
+ ),
202
+ );
203
+
204
+ /*====================== TEMPLATE MANAGEMENT ============================*/
205
+ $template_management_messages = array(
206
+ array(
207
+ 'sys_msg' => 'System problem at installing , please contact support',
208
+ 'user_msg' => __( '', 'jupiterx-core' ),
209
+ ),
210
+ array(
211
+ 'sys_msg' => 'Choose template first',
212
+ 'user_msg' => __( '', 'jupiterx-core' ),
213
+ ),
214
+ array(
215
+ 'sys_msg' => 'Template assets are not completely exist - p1, Contact support.',
216
+ 'user_msg' => __( '', 'jupiterx-core' ),
217
+ ),
218
+ array(
219
+ 'sys_msg' => 'System problem , please contact support',
220
+ 'user_msg' => __( '', 'jupiterx-core' ),
221
+ ),
222
+ array(
223
+ 'sys_msg' => 'Successfull',
224
+ 'user_msg' => __( '', 'jupiterx-core' ),
225
+ ),
226
+ array(
227
+ 'sys_msg' => 'Template list is not what we expected',
228
+ 'user_msg' => __( '', 'jupiterx-core' ),
229
+ ),
230
+ array(
231
+ 'sys_msg' => 'Database reseted',
232
+ 'user_msg' => __( '', 'jupiterx-core' ),
233
+ ),
234
+ array(
235
+ 'sys_msg' => 'Choose one template first',
236
+ 'user_msg' => __( '', 'jupiterx-core' ),
237
+ ),
238
+ array(
239
+ 'sys_msg' => 'Template source URL is not validate',
240
+ 'user_msg' => __( '', 'jupiterx-core' ),
241
+ ),
242
+ array(
243
+ 'sys_msg' => 'Uploaded to server',
244
+ 'user_msg' => __( '', 'jupiterx-core' ),
245
+ ),
246
+ array(
247
+ 'sys_msg' => 'Cannot delete template zip file',
248
+ 'user_msg' => __( '', 'jupiterx-core' ),
249
+ ),
250
+ array(
251
+ 'sys_msg' => 'Completed',
252
+ 'user_msg' => __( '', 'jupiterx-core' ),
253
+ ),
254
+ array(
255
+ 'sys_msg' => 'Template assets are not completely exist - p2, Contact support.',
256
+ 'user_msg' => __( '', 'jupiterx-core' ),
257
+ ),
258
+ array(
259
+ 'sys_msg' => 'Plugin set have wrong structure',
260
+ 'user_msg' => __( '', 'jupiterx-core' ),
261
+ ),
262
+ array(
263
+ 'sys_msg' => '{param} plugins are installed.',
264
+ 'user_msg' => __( '', 'jupiterx-core' ),
265
+ ),
266
+ array(
267
+ 'sys_msg' => 'Template contents were imported.',
268
+ 'user_msg' => __( '', 'jupiterx-core' ),
269
+ ),
270
+ array(
271
+ 'sys_msg' => 'Navigation locations is configured.',
272
+ 'user_msg' => __( '', 'jupiterx-core' ),
273
+ ),
274
+ array(
275
+ 'sys_msg' => 'Default homepage and Shop Page is configured.',
276
+ 'user_msg' => __( '', 'jupiterx-core' ),
277
+ ),
278
+ array(
279
+ 'sys_msg' => 'Shop Page is configured.',
280
+ 'user_msg' => __( '', 'jupiterx-core' ),
281
+ ),
282
+ array(
283
+ 'sys_msg' => 'Default homepage is configured.',
284
+ 'user_msg' => __( '', 'jupiterx-core' ),
285
+ ),
286
+ array(
287
+ 'sys_msg' => 'Setup pages completed.',
288
+ 'user_msg' => __( '', 'jupiterx-core' ),
289
+ ),
290
+ array(
291
+ 'sys_msg' => 'Template options is empty',
292
+ 'user_msg' => __( '', 'jupiterx-core' ),
293
+ ),
294
+ array(
295
+ 'sys_msg' => 'Widgets are imported.',
296
+ 'user_msg' => __( '', 'jupiterx-core' ),
297
+ ),
298
+ array(
299
+ 'sys_msg' => 'Can not remove source files',
300
+ 'user_msg' => __( '', 'jupiterx-core' ),
301
+ ),
302
+ array(
303
+ 'sys_msg' => 'Widget data could not be read. Please try a different file.',
304
+ 'user_msg' => __( '', 'jupiterx-core' ),
305
+ ),
306
+ array(
307
+ 'sys_msg' => 'Sidebar does not exist in theme (using Inactive)',
308
+ 'user_msg' => __( '', 'jupiterx-core' ),
309
+ ),
310
+ array(
311
+ 'sys_msg' => 'Site does not support widget',
312
+ 'user_msg' => __( '', 'jupiterx-core' ),
313
+ ),
314
+ array(
315
+ 'sys_msg' => 'Widget already exists',
316
+ 'user_msg' => __( '', 'jupiterx-core' ),
317
+ ),
318
+ array(
319
+ 'sys_msg' => 'Imported',
320
+ 'user_msg' => __( '', 'jupiterx-core' ),
321
+ ),
322
+ array(
323
+ 'sys_msg' => 'Imported to Inactive',
324
+ 'user_msg' => __( '', 'jupiterx-core' ),
325
+ ),
326
+ array(
327
+ 'sys_msg' => 'Successfull',
328
+ 'user_msg' => __( '', 'jupiterx-core' ),
329
+ ),
330
+ array(
331
+ 'sys_msg' => 'LayerSlider is not installed , install it first',
332
+ 'user_msg' => __( '', 'jupiterx-core' ),
333
+ ),
334
+ array(
335
+ 'sys_msg' => 'LayerSlider is installed but not activated , activate it first',
336
+ 'user_msg' => __( '', 'jupiterx-core' ),
337
+ ),
338
+ array(
339
+ 'sys_msg' => 'Backup created.',
340
+ 'user_msg' => __( '', 'jupiterx-core' ),
341
+ ),
342
+ array(
343
+ 'sys_msg' => 'Not enough max_execution_time.',
344
+ 'user_msg' => __( '' , 'jupiterx-core' ),
345
+ ),
346
+ array(
347
+ 'sys_msg' => 'Not enough memory_limit.',
348
+ 'user_msg' => __( '' , 'jupiterx-core' ),
349
+ ),
350
+ );
351
+
352
+ /*====================== DB MANAGEMENT ============================*/
353
+ $db_management_messages = array(
354
+ array(
355
+ 'sys_msg' => 'Can not create backup db file.',
356
+ 'user_msg' => '',
357
+ ),
358
+ array(
359
+ 'sys_msg' => 'Backup file is not created in right approach , try again.',
360
+ 'user_msg' => '',
361
+ ),
362
+ array(
363
+ 'sys_msg' => 'Backup Successfuly created',
364
+ 'user_msg' => '',
365
+ ),
366
+ array(
367
+ 'sys_msg' => 'Can not create index , Securesection',
368
+ 'user_msg' => '',
369
+ ),
370
+ array(
371
+ 'sys_msg' => 'Can not create htaccess , Securesection',
372
+ 'user_msg' => '',
373
+ ),
374
+ );
375
+
376
+ /*====================== DECISION LOGIC ============================*/
377
+ switch ( $which_page ) {
378
+ case 'filesystem':
379
+ $data = $filesystem_messages;
380
+ break;
381
+ case 'test-cases':
382
+ $data = $test_cases_messages;
383
+ break;
384
+ case 'plugin-management':
385
+ $data = $plugin_management_messages;
386
+ break;
387
+ case 'addon-management':
388
+ $data = $addon_management_messages;
389
+ break;
390
+ case 'template-management':
391
+ $data = $template_management_messages;
392
+ break;
393
+ case 'db-management':
394
+ $data = $db_management_messages;
395
+ break;
396
+ case 'theme-options':
397
+ $data = $theme_options_messages;
398
+ break;
399
+ }
400
+
401
+ // Check if the which_string is array
402
+ $which_param = array();
403
+ if ( is_array( $which_string ) ) {
404
+ $which_param = $which_string;
405
+ if ( ! empty( $which_param[0] ) ) {
406
+ // Set back which_string as string, then remove it from which_param
407
+ $which_string = $which_param[0];
408
+ array_shift( $which_param );
409
+ }
410
+ }
411
+
412
+ $message_key = JupiterX_Control_Panel_Helpers::search_multdim( $data, 'sys_msg', $which_string );
413
+ if ( $message_key === false ) {
414
+ // LOG THIS SECTION
415
+ // DESC : This error message is not defined
416
+ return jupiterx_logic_message_output( $which_string, $which_param );
417
+ }
418
+ if ( ! empty( $data[ $message_key ]['user_msg'] ) && $which_type == 'user_msg' ) {
419
+ return jupiterx_logic_message_output( $data[ $message_key ]['user_msg'], $which_param );
420
+ } else {
421
+ // LOG THIS SECTION
422
+ // DESC : User message is not defined , DEFINE IT (Translate Department);
423
+ return jupiterx_logic_message_output( $data[ $message_key ]['sys_msg'], $which_param );
424
+ }
425
+ }
426
+ }
427
+
428
+ if ( ! function_exists( 'jupiterx_logic_message_output' ) ) {
429
+ /**
430
+ * Check the message format before return it. If the message need some parameter to pass, check the parameter
431
+ * then replace it with the correct ones.
432
+ *
433
+ * @param string $message Message will be displayed
434
+ * @param array $param Parameter to pass
435
+ * @return string Output of the message in text format
436
+ */
437
+ function jupiterx_logic_message_output( $message = null, $param = array() ) {
438
+ if ( $message == null ) {
439
+ return 'Undefined';
440
+ }
441
+
442
+ // Check if param is not empty
443
+ if ( ! empty( $param ) ) {
444
+ // Count {param} that need to be replaced
445
+ $count = substr_count( $message, '{param}' );
446
+ if ( count( $param ) < $count ) {
447
+ $param = array_pad( $param, $count, '{param}' );
448
+ }
449
+
450
+ $message = str_replace( '{param}', '%s', $message );
451
+ $message = vsprintf( $message, $param );
452
+ }
453
+
454
+ return $message;
455
+ }
456
+ }
includes/control-panel/views/export-import-content.php CHANGED
@@ -1,70 +1,77 @@
1
- <?php
2
- if ( ! JUPITERX_CONTROL_PANEL_EXPORT_IMPORT ) {
3
- return;
4
- }
5
- ?>
6
- <div class="jupiterx-cp-pane-box bootstrap-wrapper">
7
- <div class="alert alert-danger"><?php printf( __( 'The Export/Import feature is deprecated. To migrate/backup your website use <a href="%s" target="_blank">3rd-party plugins</a>.', 'jupiterx-core' ), 'https://wordpress.org/plugins/tags/backup/' ) ?></div>
8
- <h3><?php esc_html_e( 'Export', 'jupiterx-core' ); ?></h3>
9
- <div class="jupiterx-cp-export-wrap jupiterx-card mb-5">
10
- <form class="jupiterx-cp-export-form">
11
- <div class="jupiterx-card-body">
12
- <div class="jupiterx-form-group">
13
- <label for="filename">File Name</label>
14
- <input type="text" class="jupiterx-form-control" id="filename" name="filename" placeholder="<?php echo get_bloginfo( 'name' )?>-jupiterx">
15
- </div>
16
- <br>
17
- <div class="custom-control custom-checkbox">
18
- <input checked="" class="custom-control-input" id="site-content" name="site-content" type="checkbox" value="Content">
19
- <label class="custom-control-label" for="site-content">
20
- <?php esc_html_e( 'Site Content', 'jupiterx-core' ); ?>
21
- </label>
22
- </input>
23
- </div>
24
- <div class="custom-control custom-checkbox">
25
- <input checked="" class="custom-control-input" id="site-widgets" name="site-widgets" type="checkbox" value="Widgets">
26
- <label class="custom-control-label" for="site-widgets">
27
- <?php esc_html_e( 'Widgets', 'jupiterx-core' ); ?>
28
- </label>
29
- </input>
30
- </div>
31
- <div class="custom-control custom-checkbox">
32
- <input checked="" class="custom-control-input" id="site-settings" name="site-settings" type="checkbox" value="Settings">
33
- <label class="custom-control-label" for="site-settings">
34
- <?php esc_html_e( 'Settings', 'jupiterx-core' ); ?>
35
- </label>
36
- </input>
37
- </div>
38
- </div>
39
- <hr style="margin: 0;">
40
- <div class="jupiterx-card-body">
41
- <button class="btn btn-primary jupiterx-cp-export-btn" type="submit">
42
- <?php esc_html_e( 'Export', 'jupiterx-core' ); ?>
43
- </button>
44
- </div>
45
- </form>
46
- </div>
47
- <h3><?php esc_html_e( 'Import', 'jupiterx-core' ); ?></h3>
48
- <div class="jupiterx-cp-import-wrap jupiterx-card">
49
- <div class="jupiterx-card-body">
50
- <div class="jupiterx-upload-wrap w-75">
51
- <div class="input-group">
52
- <input class="jupiterx-form-control" placeholder="Select a Zip file" type="text">
53
- <div class="input-group-append">
54
- <a class="btn btn-secondary jupiterx-cp-import-upload-btn" href="#">
55
- <?php esc_html_e( 'Upload', 'jupiterx-core' ); ?>
56
- </a>
57
- </div>
58
- </input>
59
- </div>
60
- </div>
61
- </div>
62
- <hr style="margin: 0;">
63
- <div class="jupiterx-card-body">
64
- <button class="btn btn-primary jupiterx-cp-import-btn" type="submit">
65
- <?php esc_html_e( 'Import', 'jupiterx-core' ); ?>
66
- </button>
67
- </div>
68
-
69
- </div>
70
- </div>
 
 
 
 
 
 
 
1
+ <?php
2
+ if ( ! JUPITERX_CONTROL_PANEL_EXPORT_IMPORT ) {
3
+ return;
4
+ }
5
+ ?>
6
+ <div class="jupiterx-cp-pane-box bootstrap-wrapper">
7
+ <div class="alert alert-danger"><?php printf( __( 'The Export/Import feature is deprecated. To migrate/backup your website use <a href="%s" target="_blank">3rd-party plugins</a>.', 'jupiterx-core' ), 'https://wordpress.org/plugins/tags/backup/' ) ?></div>
8
+ <h3><?php esc_html_e( 'Export', 'jupiterx-core' ); ?></h3>
9
+ <div class="jupiterx-cp-export-wrap jupiterx-card mb-5">
10
+ <form class="jupiterx-cp-export-form">
11
+ <div class="jupiterx-card-body">
12
+ <div class="jupiterx-form-group">
13
+ <label for="filename">File Name</label>
14
+ <input type="text" class="jupiterx-form-control" id="filename" name="filename" placeholder="<?php echo get_bloginfo( 'name' )?>-jupiterx">
15
+ </div>
16
+ <br>
17
+ <div class="custom-control custom-checkbox">
18
+ <input checked="" class="custom-control-input" id="site-content" name="site-content" type="checkbox" value="Content">
19
+ <label class="custom-control-label" for="site-content">
20
+ <?php esc_html_e( 'Site Content', 'jupiterx-core' ); ?>
21
+ </label>
22
+ </input>
23
+ </div>
24
+ <div class="custom-control custom-checkbox">
25
+ <input checked="" class="custom-control-input" id="site-widgets" name="site-widgets" type="checkbox" value="Widgets">
26
+ <label class="custom-control-label" for="site-widgets">
27
+ <?php esc_html_e( 'Widgets', 'jupiterx-core' ); ?>
28
+ </label>
29
+ </input>
30
+ </div>
31
+ <div class="custom-control custom-checkbox">
32
+ <input checked="" class="custom-control-input" id="site-settings" name="site-settings" type="checkbox" value="Settings">
33
+ <label class="custom-control-label" for="site-settings">
34
+ <?php esc_html_e( 'Settings', 'jupiterx-core' ); ?>
35
+ </label>
36
+ </input>
37
+ </div>
38
+ <div class="custom-control custom-checkbox">
39
+ <input checked="" class="custom-control-input" id="custom-tables" name="custom-tables" type="checkbox" value="Custom Tables">
40
+ <label class="custom-control-label" for="custom-tables">
41
+ <?php esc_html_e( 'Custom Tables', 'jupiterx-core' ); ?>
42
+ </label>
43
+ </input>
44
+ </div>
45
+ </div>
46
+ <hr style="margin: 0;">
47
+ <div class="jupiterx-card-body">
48
+ <button class="btn btn-primary jupiterx-cp-export-btn" type="submit">
49
+ <?php esc_html_e( 'Export', 'jupiterx-core' ); ?>
50
+ </button>
51
+ </div>
52
+ </form>
53
+ </div>
54
+ <h3><?php esc_html_e( 'Import', 'jupiterx-core' ); ?></h3>
55
+ <div class="jupiterx-cp-import-wrap jupiterx-card">
56
+ <div class="jupiterx-card-body">
57
+ <div class="jupiterx-upload-wrap w-75">
58
+ <div class="input-group">
59
+ <input class="jupiterx-form-control" placeholder="Select a Zip file" type="text">
60
+ <div class="input-group-append">
61
+ <a class="btn btn-secondary jupiterx-cp-import-upload-btn" href="#">
62
+ <?php esc_html_e( 'Upload', 'jupiterx-core' ); ?>
63
+ </a>
64
+ </div>
65
+ </input>
66
+ </div>
67
+ </div>
68
+ </div>
69
+ <hr style="margin: 0;">
70
+ <div class="jupiterx-card-body">
71
+ <button class="btn btn-primary jupiterx-cp-import-btn" type="submit">
72
+ <?php esc_html_e( 'Import', 'jupiterx-core' ); ?>
73
+ </button>
74
+ </div>
75
+
76
+ </div>
77
+ </div>
includes/control-panel/views/image-sizes.php CHANGED
@@ -1,64 +1,68 @@
1
- <?php
2
- /**
3
- * Image sizes template.
4
- *
5
- * @package JupiterX_Core\Control_Panel\Image_Sizes
6
- *
7
- * @since 1.2.0
8
- */
9
-
10
- if ( ! JUPITERX_CONTROL_PANEL_IMAGE_SIZES ) {
11
- return;
12
- }
13
- ?>
14
- <div class="jupiterx-cp-pane-box" id="jupiterx-cp-image-sizes">
15
- <h3>
16
- <?php esc_html_e( 'Image Sizes', 'jupiterx-core' ); ?>
17
- <?php jupiterx_the_help_link( 'http://help.artbees.net/customizer/configuring-images/custom-size-for-images', esc_html__( 'Custom size for images', 'jupiterx-core' ) ); ?>
18
- </h3>
19
- <button type="button" class="btn btn-primary image-size-add-new-btn js__cp-clist-add-item">
20
- <?php esc_html_e( 'Add a New Size', 'jupiterx-core' ); ?>
21
- </button>
22
- <div class="jupiterx-wrap jupiterx-img-size-wrap">
23
- <div class="jupiterx-img-size">
24
- <div class="jupiterx-img-size-list js__jupiterx-img-size-list clearfix mb-3">
25
- <?php
26
- foreach ( JupiterX_Control_Panel_Image_Sizes::get_available_image_sizes() as $size ) :
27
- if ( ! empty( $size['size_n'] ) && ! empty( $size['size_w'] ) && ! empty( $size['size_h'] ) ) :
28
- ?>
29
- <div class="jupiterx-img-size-item js__cp-image-size-item">
30
- <div class="jupiterx-img-size-item-inner jupiterx-card">
31
- <div class="jupiterx-card-body fetch-input-data">
32
- <div class="js__size-name mb-3">
33
- <strong><?php esc_html_e( 'Name', 'jupiterx-core' ); ?>:</strong> <?php echo wp_kses_post( $size['size_n'] ); ?>
34
- </div>
35
- <div class="js__size-dimension mb-3">
36
- <strong><?php esc_html_e( 'Size', 'jupiterx-core' ); ?>:</strong> <?php echo wp_kses_post( $size['size_w'] ); ?>px <?php echo wp_kses_post( $size['size_h'] ); ?>px
37
- </div>
38
- <div class="js__size-crop mb-3">
39
- <strong><?php esc_html_e( 'Crop', 'jupiterx-core' ); ?>:</strong>
40
- <?php if ( 'on' === $size['size_c'] ) : ?>
41
- <span class="status-state status-true"></span>
42
- <?php else : ?>
43
- <span class="status-state status-false"></span>
44
- <?php endif; ?>
45
- </div>
46
- <button type="button" class="btn btn-outline-success js__cp-clist-edit-item mr-1"><?php esc_html_e( 'Edit', 'jupiterx-core' ); ?></button>
47
- <button type="button" class="btn btn-outline-danger js__cp-clist-remove-item"><?php esc_html_e( 'Remove', 'jupiterx-core' ); ?></button>
48
- <input name="size_n" type="hidden" value="<?php echo esc_attr( $size['size_n'] ); ?>" />
49
- <input name="size_w" type="hidden" value="<?php echo esc_attr( $size['size_w'] ); ?>" />
50
- <input name="size_h" type="hidden" value="<?php echo esc_attr( $size['size_h'] ); ?>" />
51
- <input name="size_c" type="hidden" value="<?php echo esc_attr( $size['size_c'] ); ?>" />
52
- </div>
53
- </div>
54
- </div>
55
- <?php
56
- endif;
57
- endforeach;
58
- ?>
59
- </div>
60
- <div class="clearfix"></div>
61
- </div>
62
- <?php wp_nonce_field( 'ajax-image-sizes-options', 'security' ); ?>
63
- </div>
64
- </div>
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Image sizes template.
4
+ *
5
+ * @package JupiterX_Core\Control_Panel\Image_Sizes
6
+ *
7
+ * @since 1.2.0
8
+ */
9
+
10
+ if ( ! JUPITERX_CONTROL_PANEL_IMAGE_SIZES ) {
11
+ return;
12
+ }
13
+ ?>
14
+ <div class="jupiterx-cp-pane-box" id="jupiterx-cp-image-sizes">
15
+ <h3>
16
+ <?php esc_html_e( 'Image Sizes', 'jupiterx-core' ); ?>
17
+ <?php jupiterx_the_help_link( 'http://help.artbees.net/customizer/configuring-images/custom-size-for-images', esc_html__( 'Custom size for images', 'jupiterx-core' ) ); ?>
18
+ </h3>
19
+ <button type="button" class="btn btn-primary image-size-add-new-btn js__cp-clist-add-item">
20
+ <?php esc_html_e( 'Add a New Size', 'jupiterx-core' ); ?>
21
+ </button>
22
+ <div class="jupiterx-wrap jupiterx-img-size-wrap">
23
+ <div class="jupiterx-img-size">
24
+ <div class="jupiterx-img-size-list js__jupiterx-img-size-list clearfix mb-3">
25
+ <?php
26
+ foreach ( JupiterX_Control_Panel_Image_Sizes::get_available_image_sizes() as $size ) :
27
+ if ( ! empty( $size['size_n'] ) && ! empty( $size['size_w'] ) && ! empty( $size['size_h'] ) ) :
28
+ ?>
29
+ <div class="jupiterx-img-size-item js__cp-image-size-item">
30
+ <div class="jupiterx-img-size-item-inner jupiterx-card">
31
+ <div class="jupiterx-card-body fetch-input-data">
32
+ <div class="js__size-name mb-3">
33
+ <strong><?php esc_html_e( 'Name', 'jupiterx-core' ); ?>:</strong> <?php echo wp_kses_post( $size['size_n'] ); ?>
34
+ </div>
35
+ <div class="js__size-dimension mb-3">
36
+ <strong><?php esc_html_e( 'Size', 'jupiterx-core' ); ?>:</strong> <?php echo wp_kses_post( $size['size_w'] ); ?>px <?php echo wp_kses_post( $size['size_h'] ); ?>px
37
+ </div>
38
+ <div class="js__size-crop mb-3">
39
+ <strong><?php esc_html_e( 'Crop', 'jupiterx-core' ); ?>:</strong>
40
+ <?php if ( 'on' === $size['size_c'] ) : ?>
41
+ <span class="status-state status-true"></span>
42
+ <?php else : ?>
43
+ <span class="status-state status-false"></span>
44
+ <?php endif; ?>
45
+ </div>
46
+ <button type="button" class="btn btn-outline-success js__cp-clist-edit-item mr-1"><?php esc_html_e( 'Edit', 'jupiterx-core' ); ?></button>
47
+ <button type="button" class="btn btn-outline-danger js__cp-clist-remove-item"><?php esc_html_e( 'Remove', 'jupiterx-core' ); ?></button>
48
+ <input name="size_n" type="hidden" value="<?php echo esc_attr( $size['size_n'] ); ?>" />
49
+ <input name="size_w" type="hidden" value="<?php echo esc_attr( $size['size_w'] ); ?>" />
50
+ <input name="size_h" type="hidden" value="<?php echo esc_attr( $size['size_h'] ); ?>" />
51
+ <input name="size_c" type="hidden" value="<?php echo esc_attr( $size['size_c'] ); ?>" />
52
+ <?php if ( ! empty( $size['default'] ) ) : ?>
53
+ <input name="default" type="hidden" value="true" />
54
+ <input name="id" type="hidden" value="<?php echo esc_attr( $size['id'] ); ?>" />
55
+ <?php endif; ?>
56
+ </div>
57
+ </div>
58
+ </div>
59
+ <?php
60
+ endif;
61
+ endforeach;
62
+ ?>
63
+ </div>
64
+ <div class="clearfix"></div>
65
+ </div>
66
+ <?php wp_nonce_field( 'ajax-image-sizes-options', 'security' ); ?>
67
+ </div>
68
+ </div>
includes/control-panel/views/install-plugins.php CHANGED
@@ -1,56 +1,62 @@
1
- <?php
2
- if ( ! JUPITERX_CONTROL_PANEL_PLUGINS ) {
3
- return;
4
- }
5
-
6
- $menu_items_access = get_site_option( 'menu_items' );
7
-
8
- if ( is_multisite() && ! isset( $menu_items_access['plugins'] ) && ! current_user_can( 'manage_network_plugins' ) ) : ?>
9
- <div class="jupiterx-cp-pane-box" id="jupiterx-no-plugins">
10
- <div class="alert alert-warning" role="alert">
11
- <?php esc_html_e( 'Now you are using a sub site which it\'s plugin functionalities are disabled by Network admin.', 'jupiterx' ); ?>
12
- <?php esc_html_e( 'To have a full control over plugins, please go to My Sites > Network Admin > Settings and check Plugins option of "Enable administration menus" option. If you don\'t have access to the mentioned page, please contact Network Admin.', 'jupiterx' ); ?>
13
- </div>
14
- </div>
15
- <?php
16
- return;
17
- endif;
18
-
19
- $invalid = validate_active_plugins();
20
-
21
- if ( ! empty( $invalid ) ) {
22
- foreach ( $invalid as $plugin_file => $error ) {
23
- echo '<div id="message" class="error"><p>';
24
- printf(
25
- /* translators: 1: plugin file, 2: error message */
26
- __( 'The plugin %1$s has been <strong>deactivated</strong> due to an error: %2$s', 'jupiterx' ),
27
- '<code>' . esc_html( $plugin_file ) . '</code>',
28
- $error->get_error_message()
29
- );
30
- echo '</p></div>';
31
- }
32
- }
33
- ?>
34
- <div class="jupiterx-cp-pane-box" id="jupiterx-cp-plugins">
35
- <div class="jupiterx-cp-plugins-list">
36
- <div class="jupiterx-cp-plugins-header d-flex">
37
- <h3 class="mb-0">
38
- <?php esc_html_e( 'Plugins', 'jupiterx' ); ?>
39
- <?php jupiterx_the_help_link( 'http://help.artbees.net/getting-started/plugins/installing-the-bundled-plugins', __( 'Installing the Bundled Plugins.', 'jupiterx' ) ); ?>
40
- </h3>
41
- <div class="btn-group jupiterx-cp-plugins-filter disabled" role="group">
42
- <button type="button" class="btn btn-secondary" data-filter="all" disabled><?php esc_html_e( 'All', 'jupiterx' ); ?></button>
43
- <button type="button" class="btn btn-outline-secondary" data-filter="active" disabled><?php esc_html_e( 'Active', 'jupiterx' ); ?></button>
44
- <button type="button" class="btn btn-outline-secondary" data-filter="inactive" disabled><?php esc_html_e( 'Inactive', 'jupiterx' ); ?></button>
45
- <?php if ( ! is_multisite() ) : ?>
46
- <button type="button" class="btn btn-outline-secondary" data-filter="update" disabled><?php esc_html_e( 'Update available', 'jupiterx' ); ?></button>
47
- <?php endif; ?>
48
- </div>
49
- </div>
50
- <div id="js__jupiterx-plugins" class="d-flex">
51
- <svg class="jupiterx-spinner" width="50px" height="50px" viewBox="0 0 66 66" xmlns="http://www.w3.org/2000/svg">
52
- <circle class="jupiterx-spinner-path" fill="none" stroke-width="6" stroke-linecap="round" cx="33" cy="33" r="30"></circle>
53
- </svg>
54
- </div>
55
- </div>
56
- </div>
 
 
 
 
 
 
1
+ <?php
2
+ if ( ! JUPITERX_CONTROL_PANEL_PLUGINS ) {
3
+ return;
4
+ }
5
+
6
+ $menu_items_access = get_site_option( 'menu_items' );
7
+
8
+ if ( is_multisite() && ! isset( $menu_items_access['plugins'] ) && ! current_user_can( 'manage_network_plugins' ) ) : ?>
9
+ <div class="jupiterx-cp-pane-box" id="jupiterx-no-plugins">
10
+ <div class="alert alert-warning" role="alert">
11
+ <?php esc_html_e( 'Now you are using a sub site which it\'s plugin functionalities are disabled by Network admin.', 'jupiterx' ); ?>
12
+ <?php esc_html_e( 'To have a full control over plugins, please go to My Sites > Network Admin > Settings and check Plugins option of "Enable administration menus" option. If you don\'t have access to the mentioned page, please contact Network Admin.', 'jupiterx' ); ?>
13
+ </div>
14
+ </div>
15
+ <?php
16
+ return;
17
+ endif;
18
+
19
+ $invalid = validate_active_plugins();
20
+
21
+ if ( ! empty( $invalid ) ) {
22
+ foreach ( $invalid as $plugin_file => $error ) {
23
+ echo '<div id="message" class="error"><p>';
24
+ printf(
25
+ /* translators: 1: plugin file, 2: error message */
26
+ __( 'The plugin %1$s has been <strong>deactivated</strong> due to an error: %2$s', 'jupiterx' ),
27
+ '<code>' . esc_html( $plugin_file ) . '</code>',
28
+ $error->get_error_message()
29
+ );
30
+ echo '</p></div>';
31
+ }
32
+ }
33
+ ?>
34
+ <div class="jupiterx-cp-pane-box" id="jupiterx-cp-plugins">
35
+ <div class="jupiterx-cp-plugins-list">
36
+ <div class="jupiterx-cp-plugins-header d-flex">
37
+ <h3 class="mb-0">
38
+ <?php esc_html_e( 'Plugins', 'jupiterx' ); ?>
39
+ <?php jupiterx_the_help_link( 'http://help.artbees.net/getting-started/plugins/installing-the-bundled-plugins', __( 'Installing the Bundled Plugins.', 'jupiterx' ) ); ?>
40
+ </h3>
41
+ <div class="btn-group jupiterx-cp-plugins-filter disabled" role="group">
42
+ <button type="button" class="btn btn-secondary" data-filter="all" disabled><?php esc_html_e( 'All', 'jupiterx' ); ?></button>
43
+ <button type="button" class="btn btn-outline-secondary" data-filter="active" disabled><?php esc_html_e( 'Active', 'jupiterx' ); ?></button>
44
+ <button type="button" class="btn btn-outline-secondary" data-filter="inactive" disabled><?php esc_html_e( 'Inactive', 'jupiterx' ); ?></button>
45
+ <?php
46
+ $is_update_available = false;
47
+ if ( class_exists( 'JupiterX_Control_Panel_Install_Plugins' ) ) {
48
+ $plugin_manager = new JupiterX_Control_Panel_Install_Plugins();
49
+ $is_update_available = $plugin_manager->is_update_available();
50
+ }
51
+ if ( $is_update_available && ! is_multisite() ) : ?>
52
+ <button type="button" class="btn btn-outline-secondary" data-filter="update" disabled><?php esc_html_e( 'Update available', 'jupiterx' ); ?></button>
53
+ <?php endif; ?>
54
+ </div>
55
+ </div>
56
+ <div id="js__jupiterx-plugins" class="d-flex">
57
+ <svg class="jupiterx-spinner" width="50px" height="50px" viewBox="0 0 66 66" xmlns="http://www.w3.org/2000/svg">
58
+ <circle class="jupiterx-spinner-path" fill="none" stroke-width="6" stroke-linecap="round" cx="33" cy="33" r="30"></circle>
59
+ </svg>
60
+ </div>
61
+ </div>
62
+ </div>
includes/control-panel/views/install-templates.php CHANGED
@@ -1,61 +1,61 @@
1
- <?php
2
-
3
- if ( ! JUPITERX_CONTROL_PANEL_TEMPLATES ) {
4
- return;
5
- }
6
-
7
- $menu_items_access = get_site_option( 'menu_items' );
8
-
9
- if ( is_multisite() && ! is_super_admin() ) : ?>
10
- <div class="jupiterx-cp-pane-box" id="jupiterx-no-templates">
11
- <div class="alert alert-warning" role="alert">
12
-
13
- <?php esc_html_e( 'Template installation is only allowed for user with Super Admin role. Please contact your website\'s administrator.', 'jupiterx-core' ); ?>
14
- <a href="https://themes.artbees.net/docs/installing-a-template/" target="_blank" >
15
- <?php esc_html_e( 'Read More', 'jupiterx-core' ); ?>
16
- </a>
17
- </div>
18
- </div>
19
- <?php
20
- endif;
21
-
22
- $installed_template_data_attr = '';
23
- $installed_template_id = jupiterx_get_option( 'template_installed_id', '' );
24
- $installed_template_data_attr .= ' data-installed-template-id="' . esc_attr( $installed_template_id ) . '"';
25
- $installed_template = jupiterx_get_option( 'template_installed', '' );
26
- $installed_template_data_attr .= ' data-installed-template="' . esc_attr( $installed_template ) . '"';
27
- wp_print_request_filesystem_credentials_modal();
28
- ?>
29
- <div class="jupiterx-cp-pane-box" id="jupiterx-cp-templates">
30
-
31
- <!-- Restore Button wrap -->
32
- <div id="js__restore-template-wrap" class="jupiterx-restore-template-wrap">
33
- <a class="btn btn-primary jupiterx-button--restore-backup" id="js__restore-template-btn" href="#" data-content="" data-toggle="popover" data-placement="bottom">
34
- <?php esc_html_e( 'Restore from Last Backup', 'jupiterx-core' ); ?>
35
- </a>
36
- </div>
37
- <!-- End of Restore Button wrap -->
38
-
39
- <!-- Installed Template wrap -->
40
- <div id="js__installed-template-wrap" class="jupiterx-cp-installed-template">
41
- <h3>
42
- <?php esc_html_e( 'Installed Template', 'jupiterx-core' ); ?>
43
- <?php jupiterx_the_help_link( 'http://help.artbees.net/getting-started/templates/installing-a-template', esc_html__( 'Installing a Template', 'jupiterx-core' ) ); ?>
44
- </h3>
45
- <div id="js__installed-template" <?php echo $installed_template_data_attr; ?>></div>
46
- <div class="clearfix"></div>
47
- </div>
48
- <!-- End of installed template -->
49
-
50
- <div class="jupiterx-cp-install-template">
51
- <h3>
52
- <?php esc_html_e( 'Templates', 'jupiterx-core' ); ?>
53
- <?php jupiterx_the_help_link( 'http://help.artbees.net/getting-started/templates/installing-a-template', esc_html__( 'Installing a Template', 'jupiterx-core' ) ); ?>
54
- </h3>
55
- <?php
56
- jupiterx_templates()->html( [
57
- 'posts_per_page' => 12,
58
- ] );
59
- ?>
60
- </div>
61
- </div>
1
+ <?php
2
+
3
+ if ( ! JUPITERX_CONTROL_PANEL_TEMPLATES ) {
4
+ return;
5
+ }
6
+
7
+ $menu_items_access = get_site_option( 'menu_items' );
8
+
9
+ if ( is_multisite() && ! is_super_admin() ) : ?>
10
+ <div class="jupiterx-cp-pane-box" id="jupiterx-no-templates">
11
+ <div class="alert alert-warning" role="alert">
12
+
13
+ <?php esc_html_e( 'Template installation is only allowed for user with Super Admin role. Please contact your website\'s administrator.', 'jupiterx-core' ); ?>
14
+ <a href="https://themes.artbees.net/docs/installing-a-template/" target="_blank" >
15
+ <?php esc_html_e( 'Read More', 'jupiterx-core' ); ?>
16
+ </a>
17
+ </div>
18
+ </div>
19
+ <?php
20
+ endif;
21
+
22
+ $installed_template_data_attr = '';
23
+ $installed_template_id = jupiterx_get_option( 'template_installed_id', '' );
24
+ $installed_template_data_attr .= ' data-installed-template-id="' . esc_attr( $installed_template_id ) . '"';
25
+ $installed_template = jupiterx_get_option( 'template_installed', '' );
26
+ $installed_template_data_attr .= ' data-installed-template="' . esc_attr( $installed_template ) . '"';
27
+ wp_print_request_filesystem_credentials_modal();
28
+ ?>
29
+ <div class="jupiterx-cp-pane-box" id="jupiterx-cp-templates">
30
+
31
+ <!-- Restore Button wrap -->
32
+ <div id="js__restore-template-wrap" class="jupiterx-restore-template-wrap">
33
+ <a class="btn btn-primary jupiterx-button--restore-backup" id="js__restore-template-btn" href="#" data-content="" data-toggle="popover" data-placement="bottom">
34
+ <?php esc_html_e( 'Restore from Last Backup', 'jupiterx-core' ); ?>
35
+ </a>
36
+ </div>
37
+ <!-- End of Restore Button wrap -->
38
+
39
+ <!-- Installed Template wrap -->
40
+ <div id="js__installed-template-wrap" class="jupiterx-cp-installed-template">
41
+ <h3>
42
+ <?php esc_html_e( 'Installed Template', 'jupiterx-core' ); ?>
43
+ <?php jupiterx_the_help_link( 'http://help.artbees.net/getting-started/templates/installing-a-template', esc_html__( 'Installing a Template', 'jupiterx-core' ) ); ?>
44
+ </h3>
45
+ <div id="js__installed-template" <?php echo $installed_template_data_attr; ?>></div>
46
+ <div class="clearfix"></div>
47
+ </div>
48
+ <!-- End of installed template -->
49
+
50
+ <div class="jupiterx-cp-install-template">
51
+ <h3>
52
+ <?php esc_html_e( 'Templates', 'jupiterx-core' ); ?>
53
+ <?php jupiterx_the_help_link( 'http://help.artbees.net/getting-started/templates/installing-a-template', esc_html__( 'Installing a Template', 'jupiterx-core' ) ); ?>
54
+ </h3>
55
+ <?php
56
+ jupiterx_templates()->html( [
57
+ 'posts_per_page' => 12,
58
+ ] );
59
+ ?>
60
+ </div>
61
+ </div>
includes/control-panel/views/settings.php CHANGED
@@ -1,103 +1,104 @@
1
- <?php
2
- /**
3
- * Settings template.
4
- *
5
- * @package JupiterX_Core\Control_Panel\Settings
6
- *
7
- * @since 1.4.0
8
- */
9
-
10
- if ( ! JUPITERX_CONTROL_PANEL_SETTINGS ) {
11
- return;
12
- }
13
- ?>
14
- <div class="jupiterx-cp-pane-box bootstrap-wrapper">
15
- <h3><?php esc_html_e( 'Settings', 'jupiterx-core' ); ?></h3>
16
- <div class="jupiterx-cp-export-wrap">
17
- <form class="jupiterx-cp-settings-form" action="#">
18
- <div class="form-row">
19
- <div class="form-group col-md-12">
20
- <button type="button" class="btn btn-secondary jupiterx-cp-settings-flush"><?php esc_html_e( 'Flush Assets Cache', 'jupiterx-core' ); ?></button>
21
- <span class="jupiterx-cp-settings-flush-feedback text-muted ml-2 d-none"><?php esc_html_e( 'Flushing...', 'jupiterx-core' ); ?></span>
22
- <small class="form-text text-muted"><?php esc_html_e( 'Clear CSS, Javascript and images cached files. New cached versions will be compiled/created on page load.', 'jupiterx-core' ); ?></small>
23
- </div>
24
- <div class="col-md-12"><hr></div>
25
- <div class="form-group col-md-6">
26
- <label for="jupiterx-cp-settings-dev-mode"><?php esc_html_e( 'Development Mode', 'jupiterx-core' ); ?></label>
27
- <input type="hidden" name="jupiterx_dev_mode" value="0">
28
- <div class="jupiterx-switch">
29
- <input type="checkbox" id="jupiterx-cp-settings-dev-mode" name="jupiterx_dev_mode" value="1" <?php checked( jupiterx_get_option( 'dev_mode' ), true ); ?>>
30
- <label for="jupiterx-cp-settings-dev-mode"></label>
31
- </div>
32
- <small class="form-text text-muted"><?php esc_html_e( 'This option should be enabled while your website is in development.', 'jupiterx-core' ); ?></small>
33
- </div>
34
- <div class="form-group col-md-6">
35
- <label for="jupiterx-cp-settings-cache-busting"><?php esc_html_e( 'Cache Busting', 'jupiterx-core' ); ?></label>
36
- <input type="hidden" name="jupiterx_cache_busting" value="0">
37
- <div class="jupiterx-switch">
38
- <input type="checkbox" id="jupiterx-cp-settings-cache-busting" name="jupiterx_cache_busting" value="1" <?php checked( jupiterx_get_option( 'cache_busting', true ) ); ?>>
39
- <label for="jupiterx-cp-settings-cache-busting"></label>
40
- </div>
41
- <small class="form-text text-muted"><?php esc_html_e( 'Enable cache busting technique.', 'jupiterx-core' ); ?></small>
42
- </div>
43
- <?php do_action( 'jupiterx_control_panel_after_theme_settings' ); ?>
44
- <div class="col-md-12"><hr></div>
45
- <div class="form-group col-md-12">
46
- <label class="m-0"><?php esc_html_e( 'Custom Post Types', 'jupiterx-core' ); ?></label>
47
- <small class="form-text text-muted mb-2"><?php esc_html_e( 'Enable Jupiter features (customizer, meta options, etc.) for these post types.', 'jupiterx-core' ); ?></small>
48
- <input type="hidden" name="jupiterx_post_types" value="">
49
- <?php $post_types = jupiterx_get_custom_post_types( 'objects' ); ?>
50
- <?php if ( ! empty( $post_types ) ) : ?>
51
- <?php foreach ( $post_types as $id => $post_type ) : ?>
52
- <?php echo '<div class="custom-control custom-checkbox">'; ?>
53
- <?php echo '<input type="checkbox" class="custom-control-input" name="jupiterx_post_types[]" ' . ( ( in_array( $post_type->name, jupiterx_get_option( 'post_types' ), true ) ) ? 'checked="checked"' : '' ) . ' value="' . esc_attr( $post_type->name ) . '" id="jupiterx_post_type_' . esc_attr( $post_type->name ) . '">'; ?>
54
- <?php echo '<label class="custom-control-label" for="jupiterx_post_type_' . esc_attr( $post_type->name ) . '">' . $post_type->label . '</label>'; ?>
55
- <?php echo '</div>'; ?>
56
- <?php endforeach; ?>
57
- <?php else : ?>
58
- <div class="jupiterx-settings-no-post-type">
59
- <i class="jupiterx-icon-info-circle"></i><?php esc_html_e( 'No custom post type was found.', 'jupiterx-core' ); ?>
60
- </div>
61
- <?php endif; ?>
62
- </div>
63
- <?php do_action( 'jupiterx_control_panel_settings_white_label' ); ?>
64
- <?php if ( jupiterx_is_premium() && class_exists( 'Jupiter_Donut' ) ) : ?>
65
- <div class="col-md-12"><hr></div>
66
- <h5 class="col-md-12 mb-3">Donut Plugin</h5>
67
- <div class="form-group col-md-6">
68
- <label for="jupiterx-cp-settings-donut-twitter-consumer-key"><?php esc_html_e( 'Twitter Consumer Key', 'jupiterx-core' ); ?></label>
69
- <input type="text" class="jupiterx-form-control" id="jupiterx-cp-settings-donut-twitter-consumer-key" value="<?php echo esc_attr( jupiterx_get_option( 'donut_twitter_consumer_key' ) ); ?>" name="jupiterx_donut_twitter_consumer_key">
70
- </div>
71
- <div class="form-group col-md-6">
72
- <label for="jupiterx-cp-settings-donut-twitter-consumer-secret"><?php esc_html_e( 'Twitter Consumer Secret', 'jupiterx-core' ); ?></label>
73
- <input type="text" class="jupiterx-form-control" id="jupiterx-cp-settings-donut-twitter-consumer-secret" value="<?php echo esc_attr( jupiterx_get_option( 'donut_twitter_consumer_secret' ) ); ?>" name="jupiterx_donut_twitter_consumer_secret">
74
- </div>
75
- <div class="form-group col-md-6">
76
- <label for="jupiterx-cp-settings-donut-twitter-access-token"><?php esc_html_e( 'Twitter Access Token', 'jupiterx-core' ); ?></label>
77
- <input type="text" class="jupiterx-form-control" id="jupiterx-cp-settings-donut-twitter-access-token" value="<?php echo esc_attr( jupiterx_get_option( 'donut_twitter_access_token' ) ); ?>" name="jupiterx_donut_twitter_access_token">
78
- </div>
79
- <div class="form-group col-md-6">
80
- <label for="jupiterx-cp-settings-donut-twitter-access-token-secret"><?php esc_html_e( 'Twitter Access Token Secret', 'jupiterx-core' ); ?></label>
81
- <input type="text" class="jupiterx-form-control" id="jupiterx-cp-settings-donut-twitter-access-token-secret" value="<?php echo esc_attr( jupiterx_get_option( 'donut_twitter_access_token_secret' ) ); ?>" name="jupiterx_donut_twitter_access_token_secret">
82
- </div>
83
- <div class="form-group col-md-6">
84
- <label for="jupiterx-cp-settings-donut-mailchimp-api-key"><?php esc_html_e( 'MailChimp API Key', 'jupiterx-core' ); ?></label>
85
- <input type="text" class="jupiterx-form-control" id="jupiterx-cp-settings-donut-mailchimp-api-key" value="<?php echo esc_attr( jupiterx_get_option( 'donut_mailchimp_api_key' ) ); ?>" name="jupiterx_donut_mailchimp_api_key">
86
- </div>
87
- <div class="form-group col-md-6">
88
- <label for="jupiterx-cp-settings-donut-mailchimp-list-id"><?php esc_html_e( 'Mailchimp List ID', 'jupiterx-core' ); ?></label>
89
- <input type="text" class="jupiterx-form-control" id="jupiterx-cp-settings-donut-mailchimp-list-id" value="<?php echo esc_attr( jupiterx_get_option( 'donut_mailchimp_list_id' ) ); ?>" name="jupiterx_donut_mailchimp_list_id">
90
- </div>
91
- <div class="form-group col-md-6">
92
- <label for="jupiterx-cp-settings-donut-google-maps-api-key"><?php esc_html_e( 'Google Maps API Key', 'jupiterx-core' ); ?></label>
93
- <input type="text" class="jupiterx-form-control" id="jupiterx-cp-settings-donut-google-maps-api-key" value="<?php echo esc_attr( jupiterx_get_option( 'donut_google_maps_api_key' ) ); ?>" name="jupiterx_donut_google_maps_api_key">
94
- </div>
95
- <?php endif; ?>
96
- </div>
97
- <div class="mt-2">
98
- <button type="submit" class="btn btn-primary"><?php esc_html_e( 'Save Settings', 'jupiterx-core' ); ?></button>
99
- <span class="jupiterx-cp-settings-save-feedback text-muted ml-2 d-none"><?php esc_html_e( 'Saving...', 'jupiterx-core' ); ?></span>
100
- </div>
101
- </form>
102
- </div>
103
- </div>
 
1
+ <?php
2
+ /**
3
+ * Settings template.
4
+ *
5
+ * @package JupiterX_Core\Control_Panel\Settings
6
+ *
7
+ * @since 1.4.0
8
+ */
9
+
10
+ if ( ! JUPITERX_CONTROL_PANEL_SETTINGS ) {
11
+ return;
12
+ }
13
+ ?>
14
+ <div class="jupiterx-cp-pane-box bootstrap-wrapper">
15
+ <h3><?php esc_html_e( 'Settings', 'jupiterx-core' ); ?></h3>
16
+ <div class="jupiterx-cp-export-wrap">
17
+ <form class="jupiterx-cp-settings-form" action="#">
18
+ <div class="form-row">
19
+ <div class="form-group col-md-12">
20
+ <button type="button" class="btn btn-secondary jupiterx-cp-settings-flush"><?php esc_html_e( 'Flush Assets Cache', 'jupiterx-core' ); ?></button>
21
+ <span class="jupiterx-cp-settings-flush-feedback text-muted ml-2 d-none"><?php esc_html_e( 'Flushing...', 'jupiterx-core' ); ?></span>
22
+ <small class="form-text text-muted"><?php esc_html_e( 'Clear CSS, Javascript and images cached files. New cached versions will be compiled/created on page load.', 'jupiterx-core' ); ?></small>
23
+ </div>
24
+ <div class="col-md-12"><hr></div>
25
+ <div class="form-group col-md-6">
26
+ <label for="jupiterx-cp-settings-dev-mode"><?php esc_html_e( 'Development Mode', 'jupiterx-core' ); ?></label>
27
+ <input type="hidden" name="jupiterx_dev_mode" value="0">
28
+ <div class="jupiterx-switch">
29
+ <input type="checkbox" id="jupiterx-cp-settings-dev-mode" name="jupiterx_dev_mode" value="1" <?php checked( jupiterx_get_option( 'dev_mode' ), true ); ?>>
30
+ <label for="jupiterx-cp-settings-dev-mode"></label>
31
+ </div>
32
+ <small class="form-text text-muted"><?php esc_html_e( 'This option should be enabled while your website is in development.', 'jupiterx-core' ); ?></small>
33
+ </div>
34
+ <div class="form-group col-md-6">
35
+ <label for="jupiterx-cp-settings-cache-busting"><?php esc_html_e( 'Cache Busting', 'jupiterx-core' ); ?></label>
36
+ <input type="hidden" name="jupiterx_cache_busting" value="0">
37
+ <div class="jupiterx-switch">
38
+ <input type="checkbox" id="jupiterx-cp-settings-cache-busting" name="jupiterx_cache_busting" value="1" <?php checked( jupiterx_get_option( 'cache_busting', true ) ); ?>>
39
+ <label for="jupiterx-cp-settings-cache-busting"></label>
40
+ </div>
41
+ <small class="form-text text-muted"><?php esc_html_e( 'Enable cache busting technique.', 'jupiterx-core' ); ?></small>
42
+ </div>
43
+ <?php do_action( 'jupiterx_control_panel_after_theme_settings' ); ?>
44
+ <div class="col-md-12"><hr></div>
45
+ <div class="form-group col-md-12">
46
+ <label class="m-0"><?php esc_html_e( 'Custom Post Types', 'jupiterx-core' ); ?></label>
47
+ <small class="form-text text-muted mb-2"><?php esc_html_e( 'Enable Jupiter features (customizer, meta options, etc.) for these post types.', 'jupiterx-core' ); ?></small>
48
+ <input type="hidden" name="jupiterx_post_types" value="">
49
+ <?php $post_types = jupiterx_get_custom_post_types( 'objects' ); ?>
50
+ <?php $supported_post_types = jupiterx_get_option( 'post_types' ); ?>
51
+ <?php if ( ! empty( $post_types ) ) : ?>
52
+ <?php foreach ( $post_types as $id => $post_type ) : ?>
53
+ <?php echo '<div class="custom-control custom-checkbox">'; ?>
54
+ <?php echo '<input type="checkbox" class="custom-control-input" name="jupiterx_post_types[]" ' . ( ( is_array( $supported_post_types ) && in_array( $post_type->name, jupiterx_get_option( 'post_types' ), true ) ) ? 'checked="checked"' : '' ) . ' value="' . esc_attr( $post_type->name ) . '" id="jupiterx_post_type_' . esc_attr( $post_type->name ) . '">'; ?>
55
+ <?php echo '<label class="custom-control-label" for="jupiterx_post_type_' . esc_attr( $post_type->name ) . '">' . $post_type->label . '</label>'; ?>
56
+ <?php echo '</div>'; ?>
57
+ <?php endforeach; ?>
58
+ <?php else : ?>
59
+ <div class="jupiterx-settings-no-post-type">
60
+ <i class="jupiterx-icon-info-circle"></i><?php esc_html_e( 'No custom post type was found.', 'jupiterx-core' ); ?>
61
+ </div>
62
+ <?php endif; ?>
63
+ </div>
64
+ <?php do_action( 'jupiterx_control_panel_settings_white_label' ); ?>
65
+ <?php if ( jupiterx_is_premium() && class_exists( 'Jupiter_Donut' ) ) : ?>
66
+ <div class="col-md-12"><hr></div>
67
+ <h5 class="col-md-12 mb-3">Donut Plugin</h5>
68
+ <div class="form-group col-md-6">
69
+ <label for="jupiterx-cp-settings-donut-twitter-consumer-key"><?php esc_html_e( 'Twitter Consumer Key', 'jupiterx-core' ); ?></label>
70
+ <input type="text" class="jupiterx-form-control" id="jupiterx-cp-settings-donut-twitter-consumer-key" value="<?php echo esc_attr( jupiterx_get_option( 'donut_twitter_consumer_key' ) ); ?>" name="jupiterx_donut_twitter_consumer_key">
71
+ </div>
72
+ <div class="form-group col-md-6">
73
+ <label for="jupiterx-cp-settings-donut-twitter-consumer-secret"><?php esc_html_e( 'Twitter Consumer Secret', 'jupiterx-core' ); ?></label>
74
+ <input type="text" class="jupiterx-form-control" id="jupiterx-cp-settings-donut-twitter-consumer-secret" value="<?php echo esc_attr( jupiterx_get_option( 'donut_twitter_consumer_secret' ) ); ?>" name="jupiterx_donut_twitter_consumer_secret">
75
+ </div>
76
+ <div class="form-group col-md-6">
77
+ <label for="jupiterx-cp-settings-donut-twitter-access-token"><?php esc_html_e( 'Twitter Access Token', 'jupiterx-core' ); ?></label>
78
+ <input type="text" class="jupiterx-form-control" id="jupiterx-cp-settings-donut-twitter-access-token" value="<?php echo esc_attr( jupiterx_get_option( 'donut_twitter_access_token' ) ); ?>" name="jupiterx_donut_twitter_access_token">
79
+ </div>
80
+ <div class="form-group col-md-6">
81
+ <label for="jupiterx-cp-settings-donut-twitter-access-token-secret"><?php esc_html_e( 'Twitter Access Token Secret', 'jupiterx-core' ); ?></label>
82
+ <input type="text" class="jupiterx-form-control" id="jupiterx-cp-settings-donut-twitter-access-token-secret" value="<?php echo esc_attr( jupiterx_get_option( 'donut_twitter_access_token_secret' ) ); ?>" name="jupiterx_donut_twitter_access_token_secret">
83
+ </div>
84
+ <div class="form-group col-md-6">
85
+ <label for="jupiterx-cp-settings-donut-mailchimp-api-key"><?php esc_html_e( 'MailChimp API Key', 'jupiterx-core' ); ?></label>
86
+ <input type="text" class="jupiterx-form-control" id="jupiterx-cp-settings-donut-mailchimp-api-key" value="<?php echo esc_attr( jupiterx_get_option( 'donut_mailchimp_api_key' ) ); ?>" name="jupiterx_donut_mailchimp_api_key">
87
+ </div>
88
+ <div class="form-group col-md-6">
89
+ <label for="jupiterx-cp-settings-donut-mailchimp-list-id"><?php esc_html_e( 'Mailchimp List ID', 'jupiterx-core' ); ?></label>
90
+ <input type="text" class="jupiterx-form-control" id="jupiterx-cp-settings-donut-mailchimp-list-id" value="<?php echo esc_attr( jupiterx_get_option( 'donut_mailchimp_list_id' ) ); ?>" name="jupiterx_donut_mailchimp_list_id">
91
+ </div>
92
+ <div class="form-group col-md-6">
93
+ <label for="jupiterx-cp-settings-donut-google-maps-api-key"><?php esc_html_e( 'Google Maps API Key', 'jupiterx-core' ); ?></label>
94
+ <input type="text" class="jupiterx-form-control" id="jupiterx-cp-settings-donut-google-maps-api-key" value="<?php echo esc_attr( jupiterx_get_option( 'donut_google_maps_api_key' ) ); ?>" name="jupiterx_donut_google_maps_api_key">
95
+ </div>
96
+ <?php endif; ?>
97
+ </div>
98
+ <div class="mt-2">
99
+ <button type="submit" class="btn btn-primary"><?php esc_html_e( 'Save Settings', 'jupiterx-core' ); ?></button>
100
+ <span class="jupiterx-cp-settings-save-feedback text-muted ml-2 d-none"><?php esc_html_e( 'Saving...', 'jupiterx-core' ); ?></span>
101
+ </div>
102
+ </form>
103
+ </div>
104
+ </div>
includes/control-panel/views/system-status.php CHANGED
@@ -1,929 +1,929 @@
1
- <?php
2
- if ( ! JUPITERX_CONTROL_PANEL_SYSTEM_STATUS ) {
3
- return;
4
- }
5
-
6
- $sysinfo = JupiterX_Control_Panel_System_Status::compile_system_status();
7
- $sysinfo_warnings = JupiterX_Control_Panel_System_Status::compile_system_status_warnings();
8
- ?>
9
- <div class="jupiterx-cp-pane-box" id="jupiterx-cp-system-status">
10
- <h3>
11
- <?php esc_html_e( 'System Status', 'jupiterx-core' ); ?>
12
- <?php jupiterx_the_help_link( 'https://themes.artbees.net/docs/checking-server-requirements-x-theme/', __( 'Checking Server Requirements', 'jupiterx-core' ) ); ?>
13
- </h3>
14
-
15
- <a class="btn btn-primary jupiterx-button--get-system-report" href="#">
16
- <?php esc_html_e( 'Get System Report', 'jupiterx-core' ); ?>
17
- </a>
18
-
19
- <div id="jupiterx-textarea--get-system-report">
20
- <textarea readonly="readonly" onclick="this.focus();this.select()"></textarea>
21
- </div>
22
- <br>
23
- <table class="table" cellspacing="0">
24
- <thead class="thead-light">
25
- <tr>
26
- <th colspan="3" data-export-label="WordPress Environment">
27
- <?php esc_html_e( 'WordPress Environment', 'jupiterx-core' ); ?>
28
- </th>
29
- </tr>
30
- </thead>
31
- <tbody>
32
- <tr>
33
- <td data-export-label="Home URL">
34
- <?php esc_html_e( 'Home URL', 'jupiterx-core' ); ?>:
35
- </td>
36
- <td class="help">
37
- <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'The URL of your site\'s homepage.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
38
- </td>
39
-
40
- <td><code><?php echo wp_kses_post( $sysinfo['home_url'] ); ?></code></td>
41
- </tr>
42
- <tr>
43
- <td data-export-label="Site URL">
44
- <?php esc_html_e( 'Site URL', 'jupiterx-core' ); ?>:
45
- </td>
46
- <td class="help">
47
- <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'The root URL of your site.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
48
- </td>
49
- <td>
50
- <code><?php echo esc_url( $sysinfo['site_url'] ); ?></code>
51
- </td>
52
- </tr>
53
- <tr>
54
- <td data-export-label="WP Content URL">
55
- <?php esc_html_e( 'WP Content URL', 'jupiterx-core' ); ?>:
56
- </td>
57
- <td class="help">
58
- <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'The URL of WordPress\'s content directory.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
59
- </td>
60
- <td>
61
- <code><?php echo esc_url( $sysinfo['wp_content_url'] ); ?></code>
62
- </td>
63
- </tr>
64
- <tr>
65
- <td data-export-label="WP Upload Path">
66
- <?php esc_html_e( 'WP Upload Path', 'jupiterx-core' ); ?>:
67
- </td>
68
- <td class="help">
69
- <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'The absolute path to WordPress\'s upload directory.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
70
- </td>
71
- <td>
72
- <code><?php echo esc_url( $sysinfo['wp_upload_dir'] ); ?></code>
73
- </td>
74
- </tr>
75
- <tr>
76
- <td data-export-label="WP Upload URL">
77
- <?php esc_html_e( 'WP Upload URL', 'jupiterx-core' ); ?>:
78
- </td>
79
- <td class="help">
80
- <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'The URL of WordPress\'s upload directory.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
81
- </td>
82
- <td>
83
- <code><?php echo esc_url( $sysinfo['wp_upload_url'] ); ?></code>
84
- </td>
85
- </tr>
86
- <tr>
87
- <td data-export-label="WP Version">
88
- <?php esc_html_e( 'WP Version', 'jupiterx-core' ); ?>:
89
- </td>
90
- <td class="help">
91
- <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'The version of WordPress installed on your site.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
92
- </td>
93
- <td>
94
- <?php bloginfo( 'version' ); ?>
95
- </td>
96
- </tr>
97
- <tr>
98
- <td data-export-label="WP Multisite">
99
- <?php esc_html_e( 'WP Multisite', 'jupiterx-core' ); ?>:
100
- </td>
101
- <td class="help">
102
- <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'Whether or not you have WordPress Multisite enabled.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
103
- </td>
104
- <td>
105
- <?php if ( false == $sysinfo['wp_multisite'] ) : ?>
106
- <span class="status-invisible">False</span><span class="status-state status-false"></span>
107
- <?php else : ?>
108
- <span class="status-invisible">True</span><span class="status-state status-true"></span>
109
- <?php endif; ?>
110
- </td>
111
- </tr>
112
- <tr>
113
- <td data-export-label="Permalink Structure">
114
- <?php esc_html_e( 'Permalink Structure', 'jupiterx-core' ); ?>:
115
- </td>
116
- <td class="help">
117
- <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'The current permalink structure as defined in WordPress Settings->Permalinks.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
118
- </td>
119
- <td>
120
- <code><?php echo esc_html( $sysinfo['permalink_structure'] ); ?></code>
121
- </td>
122
- </tr>
123
- <?php $sof = $sysinfo['front_page_display']; ?>
124
- <tr>
125
- <td data-export-label="Front Page Display">
126
- <?php esc_html_e( 'Front Page Display', 'jupiterx-core' ); ?>:
127
- </td>
128
- <td class="help">
129
- <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'The current Reading mode of WordPress.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
130
- </td>
131
- <td><?php echo esc_html( $sof ); ?></td>
132
- </tr>
133
-
134
- <?php
135
- if ( 'page' == $sof ) {
136
- ?>
137
- <tr>
138
- <td data-export-label="Front Page">
139
- <?php esc_html_e( 'Front Page', 'jupiterx-core' ); ?>:
140
- </td>
141
- <td class="help">
142
- <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'The currently selected page which acts as the site\'s Front Page.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
143
- </td>
144
- <td>
145
- <?php echo esc_html( $sysinfo['front_page'] ); ?>
146
- </td>
147
- </tr>
148
- <tr>
149
- <td data-export-label="Posts Page">
150
- <?php esc_html_e( 'Posts Page', 'jupiterx-core' ); ?>:
151
- </td>
152
- <td class="help">
153
- <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'The currently selected page in where blog posts are displayed.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
154
- </td>
155
- <td>
156
- <?php echo esc_html( $sysinfo['posts_page'] ); ?>
157
- </td>
158
- </tr>
159
- <?php
160
- }
161
- ?>
162
- <tr class="<?php esc_attr_e( isset( $sysinfo_warnings['wp_mem_limit'] ) ? 'jupiterx-sysinfo-warning' : '' ); ?>">
163
- <td data-export-label="WP Memory Limit">
164
- <?php esc_html_e( 'WP Memory Limit', 'jupiterx-core' ); ?>:
165
- </td>
166
- <td class="help">
167
- <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'The maximum amount of memory (RAM) that your site can use at one time.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
168
- </td>
169
- <td>
170
- <span class="jupiterx-sysinfo-value">
171
- <?php echo esc_html( $sysinfo['wp_mem_limit']['size'] ); ?>
172
- </span>
173
- <?php if ( isset( $sysinfo_warnings['wp_mem_limit'] ) ): ?>
174
- <span class="jupiterx-sysinfo-warning-msg">
175
- <i class="jupiterx-icon-info-circle"></i>
176
- <?php echo $sysinfo_warnings['wp_mem_limit']['message']; ?>
177
- </span>
178
- <?php endif; ?>
179
- </td>
180
- </tr>
181
- <tr>
182
- <td data-export-label="Database Table Prefix">
183
- <?php esc_html_e( 'Database Table Prefix', 'jupiterx-core' ); ?>:
184
- </td>
185
- <td class="help">
186
- <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'The prefix structure of the current WordPress database.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
187
- </td>
188
- <td>
189
- <?php echo esc_html( $sysinfo['db_table_prefix'] ); ?>
190
- </td>
191
- </tr>
192
- <tr>
193
- <td data-export-label="WP Debug Mode">
194
- <?php esc_html_e( 'WP Debug Mode', 'jupiterx-core' ); ?>:
195
- </td>
196
- <td class="help">
197
- <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'Displays whether or not WordPress is in Debug Mode.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
198
- </td>
199
- <td>
200
- <?php if ( 'false' == $sysinfo['wp_debug'] ) : ?>
201
- <span class="status-invisible">False</span><span class="status-state status-false"></span>
202
- <?php else : ?>
203
- <span class="status-invisible">True</span><span class="status-state status-true"></span>
204
- <?php endif; ?>
205
- </td>
206
- </tr>
207
- <tr>
208
- <td data-export-label="Language">
209
- <?php esc_html_e( 'Language', 'jupiterx-core' ); ?>:
210
- </td>
211
- <td class="help">
212
- <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'The current language used by WordPress. Default = English', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
213
- </td>
214
- <td>
215
- <?php echo esc_html( $sysinfo['wp_lang'] ); ?>
216
- </td>
217
- </tr>
218
- <tr>
219
- <td data-export-label="The Main WP Directory">
220
- <?php esc_html_e( 'The Main WP Directory', 'jupiterx-core' ); ?>:
221
- </td>
222
- <td class="help">
223
- <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'Check if main WP directory is writable.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
224
- </td>
225
- <td>
226
- <?php if ( wp_is_writable( $sysinfo['wp_writable'] ) ) : ?>
227
- <span class="status-invisible">True</span><span class="status-state status-true"></span>
228
- <span><?php esc_html_e( 'Writable', 'jupiterx-core' ); ?></span>
229
- <?php else : ?>
230
- <span class="status-invisible">False</span><span class="status-state status-false"></span>
231
- <span><?php printf( __( 'Make sure <code>%s</code> directory is writable.', 'jupiterx-core' ), $sysinfo['wp_writable'] ); ?></span>
232
- <?php endif; ?>
233
- </td>
234
- </tr>
235
- <tr>
236
- <td data-export-label="The wp-content Directory">
237
- <?php esc_html_e( 'The wp-content Directory', 'jupiterx-core' ); ?>:
238
- </td>
239
- <td class="help">
240
- <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'Check if wp-content directory is writable.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
241
- </td>
242
- <td>
243
- <?php if ( wp_is_writable( $sysinfo['wp_content_writable'] ) ) : ?>
244
- <span class="status-invisible">True</span><span class="status-state status-true"></span>
245
- <span><?php esc_html_e( 'Writable', 'jupiterx-core' ); ?></span>
246
- <?php else : ?>
247
- <span class="status-invisible">False</span><span class="status-state status-false"></span>
248
- <span><?php printf( __( 'Make sure <code>%s</code> directory is writable.', 'jupiterx-core' ), $sysinfo['wp_content_writable'] ); ?></span>
249
- <?php endif; ?>
250
- </td>
251
- </tr>
252
- <tr>
253
- <td data-export-label="The uploads Directory">
254
- <?php esc_html_e( 'The uploads Directory', 'jupiterx-core' ); ?>:
255
- </td>
256
- <td class="help">
257
- <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'Check if uploads directory is writable.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
258
- </td>
259
- <td>
260
- <?php if ( wp_is_writable( $sysinfo['wp_uploads_writable'] ) ) : ?>
261
- <span class="status-invisible">True</span><span class="status-state status-true"></span>
262
- <span><?php esc_html_e( 'Writable', 'jupiterx-core' ); ?></span>
263
- <?php else : ?>
264
- <span class="status-invisible">False</span><span class="status-state status-false"></span>
265
- <span><?php printf( __( 'Make sure <code>%s</code> directory is writable.', 'jupiterx-core' ), $sysinfo['wp_uploads_writable'] ); ?></span>
266
- <?php endif; ?>
267
- </td>
268
- </tr>
269
- <tr>
270
- <td data-export-label="The plugins Directory">
271
- <?php esc_html_e( 'The plugins Directory', 'jupiterx-core' ); ?>:
272
- </td>
273
- <td class="help">
274
- <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'Check if plugins directory is writable.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
275
- </td>
276
- <td>
277
- <?php if ( wp_is_writable( $sysinfo['wp_plugins_writable'] ) ) : ?>
278
- <span class="status-invisible">True</span><span class="status-state status-true"></span>
279
- <span><?php esc_html_e( 'Writable', 'jupiterx-core' ); ?></span>
280
- <?php else : ?>
281
- <span class="status-invisible">False</span><span class="status-state status-false"></span>
282
- <span><?php printf( __( 'Make sure <code>%s</code> directory is writable.', 'jupiterx-core' ), $sysinfo['wp_plugins_writable'] ); ?></span>
283
- <?php endif; ?>
284
- </td>
285
- </tr>
286
- <tr>
287
- <td data-export-label="The themes Directory">
288
- <?php esc_html_e( 'The themes Directory', 'jupiterx-core' ); ?>:
289
- </td>
290
- <td class="help">
291
- <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'Check if themes directory is writable.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
292
- </td>
293
- <td>
294
- <?php if ( wp_is_writable( $sysinfo['wp_themes_writable'] ) ) : ?>
295
- <span class="status-invisible">True</span><span class="status-state status-true"></span>
296
- <span><?php esc_html_e( 'Writable', 'jupiterx-core' ); ?></span>
297
- <?php else : ?>
298
- <span class="status-invisible">False</span><span class="status-state status-false"></span>
299
- <span><?php printf( __( 'Make sure <code>%s</code> directory is writable.', 'jupiterx-core' ), $sysinfo['wp_themes_writable'] ); ?></span>
300
- <?php endif; ?>
301
- </td>
302
- </tr>
303
- </tbody>
304
- </table>
305
- <br><br>
306
- <table class="table" cellspacing="0">
307
- <thead class="thead-light">
308
- <tr>
309
- <th colspan="3" data-export-label="Theme"><?php esc_html_e( 'Theme', 'jupiterx-core' ); ?></th>
310
- </tr>
311
- </thead>
312
- <tbody>
313
- <tr>
314
- <td data-export-label="Name"><?php esc_html_e( 'Name', 'jupiterx-core' ); ?>:</td>
315
- <td class="help">
316
- <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'The name of the current active theme.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
317
- </td>
318
- <td><?php echo esc_html( $sysinfo['theme']['name'] ); ?></td>
319
- </tr>
320
- <tr>
321
- <td data-export-label="Version"><?php esc_html_e( 'Version', 'jupiterx-core' ); ?>:</td>
322
- <td class="help">
323
- <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'The installed version of the current active theme.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
324
- </td>
325
- <td>
326
- <?php echo esc_html( $sysinfo['theme']['version'] ); ?>
327
- </td>
328
- </tr>
329
- <tr>
330
- <td data-export-label="Author URL"><?php esc_html_e( 'Author URL', 'jupiterx-core' ); ?>:</td>
331
- <td class="help">
332
- <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'The theme developers URL.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
333
- </td>
334
- <td><?php echo esc_url( $sysinfo['theme']['author_uri'] ); ?></td>
335
- </tr>
336
- <tr>
337
- <td data-export-label="Child Theme"><?php esc_html_e( 'Child Theme', 'jupiterx-core' ); ?>:</td>
338
- <td class="help">
339
- <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'Displays whether or not the current theme is a child theme.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
340
- </td>
341
- <td>
342
- <?php if ( is_child_theme() ) : ?>
343
- <span class="status-invisible">True</span><span class="status-state status-true"></span>
344
- <?php else : ?>
345
- <span class="status-invisible">False</span><span class="status-state status-false"></span>
346
- <?php endif; ?>
347
- </td>
348
- </tr>
349
- <?php if ( is_child_theme() ) : ?>
350
- <tr>
351
- <td data-export-label="Parent Theme Name"><?php esc_html_e( 'Parent Theme Name', 'jupiterx-core' ); ?>:
352
- </td>
353
- <td class="help">
354
- <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'The name of the parent theme.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
355
- </td>
356
- <td><?php echo esc_html( $sysinfo['theme']['parent_name'] ); ?></td>
357
- </tr>
358
- <tr>
359
- <td data-export-label="Parent Theme Version">
360
- <?php esc_html_e( 'Parent Theme Version', 'jupiterx-core' ); ?>:
361
- </td>
362
- <td class="help">
363
- <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'The installed version of the parent theme.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
364
- </td>
365
- <td><?php echo esc_html( $sysinfo['theme']['parent_version'] ); ?></td>
366
- </tr>
367
- <tr>
368
- <td data-export-label="Parent Theme Author URL">
369
- <?php esc_html_e( 'Parent Theme Author URL', 'jupiterx-core' ); ?>:
370
- </td>
371
- <td class="help">
372
- <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'The parent theme developers URL.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
373
- </td>
374
- <td><?php echo esc_url( $sysinfo['theme']['parent_author_uri'] ); ?></td>
375
- </tr>
376
- <?php endif; ?>
377
- </tbody>
378
- </table>
379
- <br><br>
380
-
381
- <table class="table" cellspacing="0">
382
- <thead class="thead-light">
383
- <tr>
384
- <th colspan="3" data-export-label="Browser">
385
- <?php esc_html_e( 'Browser', 'jupiterx-core' ); ?>
386
- </th>
387
- </tr>
388
- </thead>
389
- <tbody>
390
- <tr>
391
- <td data-export-label="Browser Info">
392
- <?php esc_html_e( 'Browser Info', 'jupiterx-core' ); ?>:
393
- </td>
394
- <td class="help">
395
- <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'Information about web browser current in use.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
396
- </td>
397
- <td>
398
- <?php
399
- foreach ( $sysinfo['browser'] as $key => $value ) {
400
- echo '<strong>' . esc_html( ucfirst( $key ) ) . '</strong>: ' . esc_html( $value ) . '<br/>';
401
- }
402
- ?>
403
- </td>
404
- </tr>
405
- </tbody>
406
- </table>
407
- <br><br>
408
-
409
-
410
-
411
- <table class="table" cellspacing="0">
412
- <thead class="thead-light">
413
- <tr>
414
- <th colspan="3" data-export-label="Server Environment">
415
- <?php esc_html_e( 'Server Environment', 'jupiterx-core' ); ?>
416
- </th>
417
- </tr>
418
- </thead>
419
- <tbody>
420
- <tr>
421
- <td data-export-label="Server Info">
422
- <?php esc_html_e( 'Server Info', 'jupiterx-core' ); ?>:
423
- </td>
424
- <td class="help">
425
- <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'Information about the web server that is currently hosting your site.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
426
- </td>
427
- <td>
428
- <?php echo esc_html( $sysinfo['server_info'] ); ?>
429
- </td>
430
- </tr>
431
- <tr>
432
- <td data-export-label="Localhost Environment">
433
- <?php esc_html_e( 'Localhost Environment', 'jupiterx-core' ); ?>:
434
- </td>
435
- <td class="help">
436
- <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'Is the server running in a localhost environment.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
437
- </td>
438
- <td>
439
- <?php if ( 'true' == $sysinfo['localhost'] ) : ?>
440
- <span class="status-invisible">True</span><span class="status-state status-true"></span>
441
- <?php else : ?>
442
- <span class="status-invisible">False</span><span class="status-state status-false"></span>
443
- <?php endif; ?>
444
- </td>
445
- </tr>
446
- <tr>
447
- <td data-export-label="PHP Version">
448
- <?php esc_html_e( 'PHP Version', 'jupiterx-core' ); ?>:
449
- </td>
450
- <td class="help">
451
- <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'The version of PHP installed on your hosting server.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
452
- </td>
453
- <td>
454
- <?php echo esc_html( $sysinfo['php_ver'] ); ?>
455
- </td>
456
- </tr>
457
- <tr>
458
- <td data-export-label="ABSPATH">
459
- <?php esc_html_e( 'ABSPATH', 'jupiterx-core' ); ?>:
460
- </td>
461
- <td class="help">
462
- <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'The ABSPATH variable on the server.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
463
- </td>
464
- <td>
465
- <?php echo '<code>' . esc_html( $sysinfo['abspath'] ) . '</code>'; ?>
466
- </td>
467
- </tr>
468
-
469
- <?php
470
- if ( function_exists( 'ini_get' ) ) {
471
- ?>
472
- <tr class="<?php esc_attr_e( isset( $sysinfo_warnings['php_mem_limit'] ) ? 'jupiterx-sysinfo-warning' : '' ); ?>">
473
- <td data-export-label="PHP Memory Limit"><?php esc_html_e( 'PHP Memory Limit', 'jupiterx-core' ); ?>:</td>
474
- <td class="help">
475
- <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'The largest filesize that can be contained in one post.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
476
- </td>
477
- <td>
478
- <span class="jupiterx-sysinfo-value">
479
- <?php echo esc_html( $sysinfo['php_mem_limit']['size'] ); ?>
480
- </span>
481
- <?php if ( isset( $sysinfo_warnings['php_mem_limit'] ) ): ?>
482
- <span class="jupiterx-sysinfo-warning-msg">
483
- <i class="jupiterx-icon-info-circle"></i>
484
- <?php echo $sysinfo_warnings['php_mem_limit']['message']; ?>
485
- </span>
486
- <?php endif; ?>
487
- </td>
488
- </tr>
489
- <tr>
490
- <td data-export-label="PHP Post Max Size"><?php esc_html_e( 'PHP Post Max Size', 'jupiterx-core' ); ?>:</td>
491
- <td class="help">
492
- <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'The largest filesize that can be contained in one post.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
493
- </td>
494
- <td><?php echo esc_html( $sysinfo['php_post_max_size'] ); ?></td>
495
- </tr>
496
- <tr>
497
- <td data-export-label="PHP Time Limit"><?php esc_html_e( 'PHP Time Limit', 'jupiterx-core' ); ?>:</td>
498
- <td class="help">
499
- <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'max_execution_time : The amount of time (in seconds) that your site will spend on a single operation before timing out (to avoid server lockups).', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
500
- </td>
501
- <td><?php echo esc_html( $sysinfo['php_time_limit'] ); ?></td>
502
- </tr>
503
-
504
- <tr>
505
- <td data-export-label="PHP Max Input Vars"><?php esc_html_e( 'PHP Max Input Vars', 'jupiterx-core' ); ?>:</td>
506
- <td class="help">
507
- <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'The maximum number of variables your server can use for a single function to avoid overloads.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
508
- </td>
509
- <td><?php echo esc_html( $sysinfo['php_max_input_var'] ); ?></td>
510
- </tr>
511
-
512
- <?php
513
- if ( true == $sysinfo['suhosin_installed'] ) {
514
- ?>
515
- <tr>
516
- <td data-export-label="PHP Max Input Vars"><?php esc_html_e( 'Suhosin Max Request Vars', 'jupiterx-core' ); ?>:</td>
517
- <td class="help">
518
- <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'The maximum number of variables your server running Suhosin can use for a single function to avoid overloads.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
519
- </td>
520
- <td><?php echo esc_html( $sysinfo['suhosin_request_max_vars'] ); ?></td>
521
- </tr>
522
- <tr>
523
- <td data-export-label="PHP Max Input Vars"><?php esc_html_e( 'Suhosin Max Post Vars', 'jupiterx-core' ); ?>:</td>
524
- <td class="help">
525
- <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'The maximum number of variables your server running Suhosin can use for a single function to avoid overloads.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
526
- </td>
527
- <td><?php echo esc_html( $sysinfo['suhosin_post_max_vars'] ); ?></td>
528
- </tr>
529
- <?php
530
- }
531
- ?>
532
- <tr>
533
- <td data-export-label="PHP Display Errors"><?php esc_html_e( 'PHP Display Errors', 'jupiterx-core' ); ?>:</td>
534
- <td class="help">
535
- <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'Determines if PHP will display errors within the browser.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
536
- </td>
537
- <td>
538
- <?php if ( 'false' == $sysinfo['php_display_errors'] ) : ?>
539
- <span class="status-invisible">False</span><span class="status-state status-false"></span>
540
- <?php else : ?>
541
- <span class="status-invisible">True</span><span class="status-state status-true"></span>
542
- <?php endif; ?>
543
- </td>
544
- </tr>
545
- <?php
546
- }
547
- ?>
548
- <tr>
549
- <td data-export-label="SUHOSIN Installed"><?php esc_html_e( 'SUHOSIN Installed', 'jupiterx-core' ); ?>:</td>
550
- <td class="help">
551
- <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'Suhosin is an advanced protection system for PHP installations. It was designed to protect your servers on the one hand against a number of well known problems in PHP applications and on the other hand against potential unknown vulnerabilities within these applications or the PHP core itself. If enabled on your server, Suhosin may need to be configured to increase its data submission limits.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
552
- </td>
553
- <td>
554
- <?php if ( false == $sysinfo['suhosin_installed'] ) : ?>
555
- <span class="status-invisible">False</span><span class="status-state status-false"></span>
556
- <?php else : ?>
557
- <span class="status-invisible">True</span><span class="status-state status-true"></span>
558
- <?php endif; ?>
559
- </td>
560
- </tr>
561
-
562
- <tr>
563
- <td data-export-label="MySQL Version"><?php esc_html_e( 'MySQL Version', 'jupiterx-core' ); ?>:</td>
564
- <td class="help">
565
- <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'The version of MySQL installed on your hosting server.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
566
- </td>
567
- <td><?php echo esc_html( $sysinfo['mysql_ver'] ); ?></td>
568
- </tr>
569
- <tr>
570
- <td data-export-label="Max Upload Size"><?php esc_html_e( 'Max Upload Size', 'jupiterx-core' ); ?>:</td>
571
- <td class="help">
572
- <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'The largest filesize that can be uploaded to your WordPress installation.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
573
- </td>
574
- <td><?php echo esc_html( $sysinfo['max_upload_size'] ); ?></td>
575
- </tr>
576
- <tr>
577
- <td data-export-label="Default Timezone is UTC">
578
- <?php esc_html_e( 'Default Timezone is UTC', 'jupiterx-core' ); ?>:
579
- </td>
580
- <td class="help">
581
- <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'The default timezone for your server.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
582
- </td>
583
- <td>
584
- <?php if ( 'false' == $sysinfo['def_tz_is_utc'] ) : ?>
585
- <span class="status-invisible">False</span><span class="status-state status-false"></span>
586
- <?php sprintf( __( 'Default timezone is %s - it should be UTC', 'jupiterx-core' ), esc_html( date_default_timezone_get() ) ); ?>
587
- <?php else : ?>
588
- <span class="status-invisible">True</span><span class="status-state status-true"></span>
589
- <?php endif; ?>
590
- </td>
591
- </tr>
592
- <tr>
593
-
594
- <td data-export-label="PHP XML">
595
- <?php esc_html_e( 'PHP XML', 'jupiterx-core' ); ?>:
596
- </td>
597
- <td class="help">
598
- <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'Theme requires PHP XML Library to be installed.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
599
- </td>
600
- <td>
601
- <?php if ( 'false' == $sysinfo['phpxml'] ) : ?>
602
- <span class="status-invisible">False</span><span class="status-state status-false"></span>
603
- <?php else : ?>
604
- <span class="status-invisible">True</span><span class="status-state status-true"></span>
605
- <?php endif; ?>
606
- </td>
607
- </tr>
608
- <tr>
609
- <td data-export-label="MBString">
610
- <?php esc_html_e( 'MBString', 'jupiterx-core' ); ?>:
611
- </td>
612
- <td class="help">
613
- <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'Theme requires MBString PHP Library to be installed.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
614
- </td>
615
- <td>
616
- <?php if ( 'false' == $sysinfo['mbstring'] ) : ?>
617
- <span class="status-invisible">False</span><span class="status-state status-false"></span>
618
- <?php else : ?>
619
- <span class="status-invisible">True</span><span class="status-state status-true"></span>
620
- <?php endif; ?>
621
- </td>
622
- </tr>
623
- <tr>
624
- <td data-export-label="SimpleXML">
625
- <?php esc_html_e( 'SimpleXML', 'jupiterx-core' ); ?>:
626
- </td>
627
- <td class="help">
628
- <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'Theme requires SimpleXML PHP Library to be installed.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
629
- </td>
630
- <td>
631
- <?php if ( 'false' == $sysinfo['simplexml'] ) : ?>
632
- <span class="status-invisible">False</span><span class="status-state status-false"></span>
633
- <?php else : ?>
634
- <span class="status-invisible">True</span><span class="status-state status-true"></span>
635
- <?php endif; ?>
636
- </td>
637
- </tr>
638
- <?php
639
- $posting = array();
640
-
641
- $posting['fsockopen_curl']['name'] = esc_html__( 'Fsockopen/cURL', 'jupiterx-core' );
642
- $posting['fsockopen_curl']['help'] = esc_attr__( 'Used when communicating with remote services with PHP.', 'jupiterx-core' );
643
-
644
- if ( 'true' == $sysinfo['fsockopen_curl'] ) {
645
- $posting['fsockopen_curl']['success'] = true;
646
- } else {
647
- $posting['fsockopen_curl']['success'] = false;
648
- $posting['fsockopen_curl']['note'] = esc_html__( 'Your server does not have fsockopen or cURL enabled - cURL is used to communicate with other servers. Please contact your hosting provider.', 'jupiterx-core' );
649
- }
650
-
651
- $posting['soap_client']['name'] = esc_html__( 'SoapClient', 'jupiterx-core' );
652
- $posting['soap_client']['help'] = esc_attr__( 'Some webservices like shipping use SOAP to get information from remote servers, for example, live shipping quotes from FedEx require SOAP to be installed.', 'jupiterx-core' );
653
-
654
- if ( true == $sysinfo['soap_client'] ) {
655
- $posting['soap_client']['success'] = true;
656
- } else {
657
- $posting['soap_client']['success'] = false;
658
- $posting['soap_client']['note'] = sprintf( __( 'Your server does not have the <a href="%s">SOAP Client</a> class enabled - some gateway plugins which use SOAP may not work as expected.', 'jupiterx-core' ), 'http://php.net/manual/en/class.soapclient.php' );
659
- }
660
-
661
- $posting['dom_document']['name'] = esc_html__( 'DOMDocument', 'jupiterx-core' );
662
- $posting['dom_document']['help'] = esc_attr__( 'HTML/Multipart emails use DOMDocument to generate inline CSS in templates.', 'jupiterx-core' );
663
-
664
- if ( true == $sysinfo['dom_document'] ) {
665
- $posting['dom_document']['success'] = true;
666
- } else {
667
- $posting['dom_document']['success'] = false;
668
- $posting['dom_document']['note'] = sprintf( __( 'Your server does not have the <a href="%s">DOMDocument</a> class enabled - HTML/Multipart emails, and also some extensions, will not work without DOMDocument.', 'jupiterx-core' ), 'http://php.net/manual/en/class.domdocument.php' );
669
- }
670
-
671
-
672
- $posting['gzip']['name'] = esc_html__( 'GZip', 'jupiterx-core' );
673
- $posting['gzip']['help'] = esc_attr__( 'GZip (gzopen) is used to open the GEOIP database from MaxMind.', 'jupiterx-core' );
674
-
675
- if ( true == $sysinfo['gzip'] ) {
676
- $posting['gzip']['success'] = true;
677
- } else {
678
- $posting['gzip']['success'] = false;
679
- $posting['gzip']['note'] = sprintf( __( 'Your server does not support the <a href="%s">gzopen</a> function - this is required to use the GeoIP database from MaxMind. The API fallback will be used instead for geolocation.', 'jupiterx-core' ), 'http://php.net/manual/en/zlib.installation.php' );
680
- }
681
-
682
- // Zip Archive.
683
- $posting['zip_archive']['name'] = esc_html__( 'Zip Archive', 'jupiterx-core' );
684
- $posting['zip_archive']['help'] = esc_attr__( 'Used to read or write ZIP compressed archives and the files inside them.', 'jupiterx-core' );
685
-
686
- if ( class_exists( 'ZipArchive' ) ) {
687
- $posting['zip_archive']['success'] = true;
688
- } else {
689
- $posting['zip_archive']['note'] = esc_html__( 'ZipArchive library is missing. Install the Zip extension. Contact your hosting provider.', 'jupiterx-core' );
690
- $posting['zip_archive']['success'] = false;
691
- }
692
-
693
- // Iconv.
694
- $posting['iconv']['name'] = esc_html__( 'Iconv', 'jupiterx-core' );
695
- $posting['iconv']['help'] = esc_attr__( 'Used in CSS parser to handle the character set conversion.', 'jupiterx-core' );
696
-
697
- if ( extension_loaded( 'iconv' ) ) {
698
- $posting['iconv']['success'] = true;
699
- } else {
700
- $posting['iconv']['note'] = esc_html__( 'Iconv library is missing. Install the iconv extension. Contact your hosting provider.', 'jupiterx-core' );
701
- $posting['iconv']['success'] = false;
702
- }
703
-
704
- // Echo the fields.
705
- foreach ( $posting as $post ) {
706
- $mark = ! empty( $post['success'] ) ? 'yes' : 'error';
707
- ?>
708
- <tr>
709
- <td data-export-label="<?php echo esc_html( $post['name'] ); ?>">
710
- <?php echo esc_html( $post['name'] ); ?>:
711
- </td>
712
- <td class="help">
713
- <a class="jupiterx-tooltip" data-content="<?php echo isset( $post['help'] ) ? wp_kses_post( $post['help'] ) : ''; ?>" href="#" data-toggle="popover" data-placement="top"></a>
714
- </td>
715
- <td>
716
- <?php echo ! empty( $post['success'] ) ? '<span class="status-invisible">True</span><span class="status-state status-true"></span>' : '<span class="status-invisible">False</span><span class="status-state status-false"></span>'; ?>
717
- <?php echo ! empty( $post['note'] ) ? wp_kses_data( $post['note'] ) : ''; ?>
718
- </td>
719
- </tr>
720
- <?php
721
- }
722
- ?>
723
- <tr data-jupiterx-ajax="http_requests">
724
- <td data-export-label="HTTP Requests">
725
- <?php esc_html_e( 'HTTP Requests', 'jupiterx-core' ); ?>:
726
- </td>
727
- <td class="help">
728
- <a class="jupiterx-tooltip" data-content="<?php esc_attr_e( 'Check if HTTP requests (get, post and ...) are working properly.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
729
- </td>
730
- <td>
731
- <span class="status-state"><span class="spinner is-active"></span></span>
732
- <span class="status-text"></span>
733
- </td>
734
- </tr>
735
- <tr data-jupiterx-ajax="artbees_server">
736
- <td data-export-label="Communication with artbees.net">
737
- <?php esc_html_e( 'Communication with artbees.net', 'jupiterx-core' ); ?>:
738
- </td>
739
- <td class="help">
740
- <a class="jupiterx-tooltip" data-content="<?php esc_attr_e( 'Check if you have proper access to artbees.net server.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
741
- </td>
742
- <td>
743
- <span class="status-state"><span class="spinner is-active"></span></span>
744
- <span class="status-text"></span>
745
- </td>
746
- </tr>
747
- </tbody>
748
- </table>
749
- <br><br>
750
-
751
- <table class="table" cellspacing="0">
752
- <thead class="thead-light">
753
- <tr>
754
- <th colspan="3" data-export-label="Database">
755
- <?php esc_html_e( 'Database', 'jupiterx-core' ); ?>
756
- </th>
757
- </tr>
758
- </thead>
759
- <tbody>
760
- <tr>
761
- <td data-export-label="Database Info">
762
- <?php esc_html_e( 'Database Size', 'jupiterx-core' ); ?>:
763
- </td>
764
- <td class="help">
765
- <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'Information about database.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
766
- </td>
767
- <td>
768
- <?php
769
-
770
- global $wpdb;
771
- $total_size = 0;
772
- $data_usage = 0;
773
- $index_usage = 0;
774
-
775
- foreach ( $wpdb->tables('all') as $table ) {
776
- $sql = $wpdb->prepare( "SHOW TABLE STATUS LIKE %s", $table );
777
- $results = $wpdb->get_results( $sql, ARRAY_A );
778
-
779
- if ( ! isset( $results[0] ) ) {
780
- continue;
781
- }
782
-
783
- $data_usage += $results[0]['Data_length'];
784
- $index_usage += $results[0]['Index_length'];
785
- }
786
-
787
- $total_size = round( ($data_usage + $index_usage) / ( 1024 * 1024 ), 2 );
788
- echo $total_size . 'MB';
789
- ?>
790
- </td>
791
- </tr>
792
-
793
- <?php
794
-
795
- $prefix = $wpdb->prefix;
796
-
797
- $tables = [
798
- 'options',
799
- 'links',
800
- 'commentmeta',
801
- 'term_relationships',
802
- 'postmeta',
803
- 'posts',
804
- 'term_taxonomy',
805
- 'terms',
806
- 'comments',
807
- 'termmeta',
808
- ];
809
-
810
- $users_tables = [
811
- 'usermeta',
812
- 'users ',
813
- ];
814
-
815
- $multisite_only_tables = [
816
- 'blogs',
817
- 'blogs_versions ',
818
- 'registration_log',
819
- 'signups',
820
- 'site',
821
- 'sitemeta',
822
- ];
823
-
824
- // Order of conditions should not be changed.
825
- if ( ! is_multisite() ) {
826
- $tables = array_merge( $tables, $users_tables );
827
- }
828
-
829
- if ( is_multisite() && is_super_admin() ) {
830
- $tables = array_merge( $tables, $users_tables, $multisite_only_tables );
831
- }
832
-
833
- foreach ( $tables as $key => $wp_table ) {
834
- $tables[ $key ] = $wpdb->prefix . $wp_table;
835
- }
836
-
837
- foreach ( $tables as $table ) {
838
- $sql = $wpdb->prepare( "SHOW TABLE STATUS LIKE %s", $table );
839
- $results = $wpdb->get_results( $sql, ARRAY_A );
840
-
841
- if ( ! isset( $results[0] ) ) {
842
- continue;
843
- }
844
-
845
- $data_usage = $results[0]['Data_length'];
846
- $index_usage = $results[0]['Index_length'];
847
- ?>
848
- <tr>
849
- <td data-export-label="<?php echo esc_attr( $table ); ?> Info">
850
- <?php echo esc_html( $table ); ?>:
851
- </td>
852
- <td></td>
853
- <td>
854
- <?php
855
- $total_size = round( ($data_usage + $index_usage) / ( 1024 * 1024 ), 2 );
856
- echo $total_size . ' MB';
857
- ?>
858
- </td>
859
- </tr>
860
- <?php
861
- }
862
-
863
- ?>
864
- </tbody>
865
- <tr>
866
- <td data-export-label="Theme Mods Info">
867
- <?php esc_html_e( 'Theme Mods Size', 'jupiterx-core' ); ?>:
868
- </td>
869
- <td class="help">
870
- <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'Size of customizer options.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
871
- </td>
872
- <td>
873
- <?php
874
- $mods = get_theme_mods();
875
- $has_numeric_value = false;
876
-
877
- foreach ( $mods as $key => $value ) {
878
- if ( is_numeric( $key ) ) {
879
- $has_numeric_value = true;
880
- break;
881
- }
882
- }
883
- ?>
884
- <span class="jupiterx-cp-inline-text"><?php echo round( mb_strlen( serialize( get_theme_mods() ) ) / ( 1024 ), 2 ) . ' KB'; ?></span>
885
- <?php if ( $has_numeric_value ) : ?>
886
- <span><a href="#" id="jupiterx-mods-cleanup" class="button button-secondary" data-nonce="<?php echo esc_attr( wp_create_nonce( 'jupiterx_mods_cleanup' ) ); ?>"> <?php esc_html_e( 'Cleanup', 'jupiterx-core' ); ?></a></span>
887
- <?php endif; ?>
888
- </td>
889
- </tr>
890
- </table>
891
- <br><br>
892
-
893
- <table class="table" cellspacing="0">
894
- <thead class="thead-light">
895
- <tr>
896
- <th colspan="3" data-export-label="Active Plugins (<?php echo esc_html( count( (array) get_option( 'active_plugins' ) ) ); ?>)">
897
- <?php esc_html_e( 'Active Plugins', 'jupiterx-core' ); ?> (<?php echo esc_html( count( (array) get_option( 'active_plugins' ) ) ); ?>)
898
- </th>
899
- </tr>
900
- </thead>
901
- <tbody>
902
- <?php
903
- foreach ( $sysinfo['plugins'] as $name => $plugin_data ) {
904
-
905
- if ( ! empty( $plugin_data['Name'] ) ) {
906
- $plugin_name = esc_html( $plugin_data['Name'] );
907
-
908
- if ( ! empty( $plugin_data['PluginURI'] ) ) {
909
- $plugin_name = '<a href="' . esc_url( $plugin_data['PluginURI'] ) . '" title="' . esc_attr__( 'Visit plugin homepage', 'jupiterx-core' ) . '">' . esc_html( $plugin_name ) . '</a>';
910
- }
911
- ?>
912
- <tr>
913
- <td><?php echo wp_kses_post( $plugin_name ); ?></td>
914
- <td class="help">
915
- <a class="jupiterx-tooltip" data-content="<?php echo esc_attr( strip_tags( $plugin_data['Description'] ) ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
916
- </td>
917
- <td>
918
- <?php echo sprintf( _x( 'by %s', 'by author', 'jupiterx-core' ), wp_kses_post( $plugin_data['Author'] ) ) . ' &ndash; ' . esc_html( $plugin_data['Version'] ); ?>
919
- </td>
920
- </tr>
921
- <?php
922
- }
923
- }
924
- ?>
925
- </tbody>
926
- </table>
927
-
928
- </div>
929
- </div>
1
+ <?php
2
+ if ( ! JUPITERX_CONTROL_PANEL_SYSTEM_STATUS ) {
3
+ return;
4
+ }
5
+
6
+ $sysinfo = JupiterX_Control_Panel_System_Status::compile_system_status();
7
+ $sysinfo_warnings = JupiterX_Control_Panel_System_Status::compile_system_status_warnings();
8
+ ?>
9
+ <div class="jupiterx-cp-pane-box" id="jupiterx-cp-system-status">
10
+ <h3>
11
+ <?php esc_html_e( 'System Status', 'jupiterx-core' ); ?>
12
+ <?php jupiterx_the_help_link( 'https://themes.artbees.net/docs/checking-server-requirements-x-theme/', __( 'Checking Server Requirements', 'jupiterx-core' ) ); ?>
13
+ </h3>
14
+
15
+ <a class="btn btn-primary jupiterx-button--get-system-report" href="#">
16
+ <?php esc_html_e( 'Get System Report', 'jupiterx-core' ); ?>
17
+ </a>
18
+
19
+ <div id="jupiterx-textarea--get-system-report">
20
+ <textarea readonly="readonly" onclick="this.focus();this.select()"></textarea>
21
+ </div>
22
+ <br>
23
+ <table class="table" cellspacing="0">
24
+ <thead class="thead-light">
25
+ <tr>
26
+ <th colspan="3" data-export-label="WordPress Environment">
27
+ <?php esc_html_e( 'WordPress Environment', 'jupiterx-core' ); ?>
28
+ </th>
29
+ </tr>
30
+ </thead>
31
+ <tbody>
32
+ <tr>
33
+ <td data-export-label="Home URL">
34
+ <?php esc_html_e( 'Home URL', 'jupiterx-core' ); ?>:
35
+ </td>
36
+ <td class="help">
37
+ <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'The URL of your site\'s homepage.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
38
+ </td>
39
+
40
+ <td><code><?php echo wp_kses_post( $sysinfo['home_url'] ); ?></code></td>
41
+ </tr>
42
+ <tr>
43
+ <td data-export-label="Site URL">
44
+ <?php esc_html_e( 'Site URL', 'jupiterx-core' ); ?>:
45
+ </td>
46
+ <td class="help">
47
+ <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'The root URL of your site.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
48
+ </td>
49
+ <td>
50
+ <code><?php echo esc_url( $sysinfo['site_url'] ); ?></code>
51
+ </td>
52
+ </tr>
53
+ <tr>
54
+ <td data-export-label="WP Content URL">
55
+ <?php esc_html_e( 'WP Content URL', 'jupiterx-core' ); ?>:
56
+ </td>
57
+ <td class="help">
58
+ <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'The URL of WordPress\'s content directory.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
59
+ </td>
60
+ <td>
61
+ <code><?php echo esc_url( $sysinfo['wp_content_url'] ); ?></code>
62
+ </td>
63
+ </tr>
64
+ <tr>
65
+ <td data-export-label="WP Upload Path">
66
+ <?php esc_html_e( 'WP Upload Path', 'jupiterx-core' ); ?>:
67
+ </td>
68
+ <td class="help">
69
+ <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'The absolute path to WordPress\'s upload directory.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
70
+ </td>
71
+ <td>
72
+ <code><?php echo esc_url( $sysinfo['wp_upload_dir'] ); ?></code>
73
+ </td>
74
+ </tr>
75
+ <tr>
76
+ <td data-export-label="WP Upload URL">
77
+ <?php esc_html_e( 'WP Upload URL', 'jupiterx-core' ); ?>:
78
+ </td>
79
+ <td class="help">
80
+ <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'The URL of WordPress\'s upload directory.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
81
+ </td>
82
+ <td>
83
+ <code><?php echo esc_url( $sysinfo['wp_upload_url'] ); ?></code>
84
+ </td>
85
+ </tr>
86
+ <tr>
87
+ <td data-export-label="WP Version">
88
+ <?php esc_html_e( 'WP Version', 'jupiterx-core' ); ?>:
89
+ </td>
90
+ <td class="help">
91
+ <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'The version of WordPress installed on your site.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
92
+ </td>
93
+ <td>
94
+ <?php bloginfo( 'version' ); ?>
95
+ </td>
96
+ </tr>
97
+ <tr>
98
+ <td data-export-label="WP Multisite">
99
+ <?php esc_html_e( 'WP Multisite', 'jupiterx-core' ); ?>:
100
+ </td>
101
+ <td class="help">
102
+ <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'Whether or not you have WordPress Multisite enabled.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
103
+ </td>
104
+ <td>
105
+ <?php if ( false == $sysinfo['wp_multisite'] ) : ?>
106
+ <span class="status-invisible">False</span><span class="status-state status-false"></span>
107
+ <?php else : ?>
108
+ <span class="status-invisible">True</span><span class="status-state status-true"></span>
109
+ <?php endif; ?>
110
+ </td>
111
+ </tr>
112
+ <tr>
113
+ <td data-export-label="Permalink Structure">
114
+ <?php esc_html_e( 'Permalink Structure', 'jupiterx-core' ); ?>:
115
+ </td>
116
+ <td class="help">
117
+ <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'The current permalink structure as defined in WordPress Settings->Permalinks.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
118
+ </td>
119
+ <td>
120
+ <code><?php echo esc_html( $sysinfo['permalink_structure'] ); ?></code>
121
+ </td>
122
+ </tr>
123
+ <?php $sof = $sysinfo['front_page_display']; ?>
124
+ <tr>
125
+ <td data-export-label="Front Page Display">
126
+ <?php esc_html_e( 'Front Page Display', 'jupiterx-core' ); ?>:
127
+ </td>
128
+ <td class="help">
129
+ <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'The current Reading mode of WordPress.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
130
+ </td>
131
+ <td><?php echo esc_html( $sof ); ?></td>
132
+ </tr>
133
+
134
+ <?php
135
+ if ( 'page' == $sof ) {
136
+ ?>
137
+ <tr>
138
+ <td data-export-label="Front Page">
139
+ <?php esc_html_e( 'Front Page', 'jupiterx-core' ); ?>:
140
+ </td>
141
+ <td class="help">
142
+ <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'The currently selected page which acts as the site\'s Front Page.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
143
+ </td>
144
+ <td>
145
+ <?php echo esc_html( $sysinfo['front_page'] ); ?>
146
+ </td>
147
+ </tr>
148
+ <tr>
149
+ <td data-export-label="Posts Page">
150
+ <?php esc_html_e( 'Posts Page', 'jupiterx-core' ); ?>:
151
+ </td>
152
+ <td class="help">
153
+ <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'The currently selected page in where blog posts are displayed.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
154
+ </td>
155
+ <td>
156
+ <?php echo esc_html( $sysinfo['posts_page'] ); ?>
157
+ </td>
158
+ </tr>
159
+ <?php
160
+ }
161
+ ?>
162
+ <tr class="<?php esc_attr_e( isset( $sysinfo_warnings['wp_mem_limit'] ) ? 'jupiterx-sysinfo-warning' : '' ); ?>">
163
+ <td data-export-label="WP Memory Limit">
164
+ <?php esc_html_e( 'WP Memory Limit', 'jupiterx-core' ); ?>:
165
+ </td>
166
+ <td class="help">
167
+ <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'The maximum amount of memory (RAM) that your site can use at one time.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
168
+ </td>
169
+ <td>
170
+ <span class="jupiterx-sysinfo-value">
171
+ <?php echo esc_html( $sysinfo['wp_mem_limit']['size'] ); ?>
172
+ </span>
173
+ <?php if ( isset( $sysinfo_warnings['wp_mem_limit'] ) ): ?>
174
+ <span class="jupiterx-sysinfo-warning-msg">
175
+ <i class="jupiterx-icon-info-circle"></i>
176
+ <?php echo $sysinfo_warnings['wp_mem_limit']['message']; ?>
177
+ </span>
178
+ <?php endif; ?>
179
+ </td>
180
+ </tr>
181
+ <tr>
182
+ <td data-export-label="Database Table Prefix">
183
+ <?php esc_html_e( 'Database Table Prefix', 'jupiterx-core' ); ?>:
184
+ </td>
185
+ <td class="help">
186
+ <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'The prefix structure of the current WordPress database.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
187
+ </td>
188
+ <td>
189
+ <?php echo esc_html( $sysinfo['db_table_prefix'] ); ?>
190
+ </td>
191
+ </tr>
192
+ <tr>
193
+ <td data-export-label="WP Debug Mode">
194
+ <?php esc_html_e( 'WP Debug Mode', 'jupiterx-core' ); ?>:
195
+ </td>
196
+ <td class="help">
197
+ <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'Displays whether or not WordPress is in Debug Mode.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
198
+ </td>
199
+ <td>
200
+ <?php if ( 'false' == $sysinfo['wp_debug'] ) : ?>
201
+ <span class="status-invisible">False</span><span class="status-state status-false"></span>
202
+ <?php else : ?>
203
+ <span class="status-invisible">True</span><span class="status-state status-true"></span>
204
+ <?php endif; ?>
205
+ </td>
206
+ </tr>
207
+ <tr>
208
+ <td data-export-label="Language">
209
+ <?php esc_html_e( 'Language', 'jupiterx-core' ); ?>:
210
+ </td>
211
+ <td class="help">
212
+ <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'The current language used by WordPress. Default = English', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
213
+ </td>
214
+ <td>
215
+ <?php echo esc_html( $sysinfo['wp_lang'] ); ?>
216
+ </td>
217
+ </tr>
218
+ <tr>
219
+ <td data-export-label="The Main WP Directory">
220
+ <?php esc_html_e( 'The Main WP Directory', 'jupiterx-core' ); ?>:
221
+ </td>
222
+ <td class="help">
223
+ <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'Check if main WP directory is writable.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
224
+ </td>
225
+ <td>
226
+ <?php if ( wp_is_writable( $sysinfo['wp_writable'] ) ) : ?>
227
+ <span class="status-invisible">True</span><span class="status-state status-true"></span>
228
+ <span><?php esc_html_e( 'Writable', 'jupiterx-core' ); ?></span>
229
+ <?php else : ?>
230
+ <span class="status-invisible">False</span><span class="status-state status-false"></span>
231
+ <span><?php printf( __( 'Make sure <code>%s</code> directory is writable.', 'jupiterx-core' ), $sysinfo['wp_writable'] ); ?></span>
232
+ <?php endif; ?>
233
+ </td>
234
+ </tr>
235
+ <tr>
236
+ <td data-export-label="The wp-content Directory">
237
+ <?php esc_html_e( 'The wp-content Directory', 'jupiterx-core' ); ?>:
238
+ </td>
239
+ <td class="help">
240
+ <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'Check if wp-content directory is writable.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
241
+ </td>
242
+ <td>
243
+ <?php if ( wp_is_writable( $sysinfo['wp_content_writable'] ) ) : ?>
244
+ <span class="status-invisible">True</span><span class="status-state status-true"></span>
245
+ <span><?php esc_html_e( 'Writable', 'jupiterx-core' ); ?></span>
246
+ <?php else : ?>
247
+ <span class="status-invisible">False</span><span class="status-state status-false"></span>
248
+ <span><?php printf( __( 'Make sure <code>%s</code> directory is writable.', 'jupiterx-core' ), $sysinfo['wp_content_writable'] ); ?></span>
249
+ <?php endif; ?>
250
+ </td>
251
+ </tr>
252
+ <tr>
253
+ <td data-export-label="The uploads Directory">
254
+ <?php esc_html_e( 'The uploads Directory', 'jupiterx-core' ); ?>:
255
+ </td>
256
+ <td class="help">
257
+ <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'Check if uploads directory is writable.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
258
+ </td>
259
+ <td>
260
+ <?php if ( wp_is_writable( $sysinfo['wp_uploads_writable'] ) ) : ?>
261
+ <span class="status-invisible">True</span><span class="status-state status-true"></span>
262
+ <span><?php esc_html_e( 'Writable', 'jupiterx-core' ); ?></span>
263
+ <?php else : ?>
264
+ <span class="status-invisible">False</span><span class="status-state status-false"></span>
265
+ <span><?php printf( __( 'Make sure <code>%s</code> directory is writable.', 'jupiterx-core' ), $sysinfo['wp_uploads_writable'] ); ?></span>
266
+ <?php endif; ?>
267
+ </td>
268
+ </tr>
269
+ <tr>
270
+ <td data-export-label="The plugins Directory">
271
+ <?php esc_html_e( 'The plugins Directory', 'jupiterx-core' ); ?>:
272
+ </td>
273
+ <td class="help">
274
+ <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'Check if plugins directory is writable.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
275
+ </td>
276
+ <td>
277
+ <?php if ( wp_is_writable( $sysinfo['wp_plugins_writable'] ) ) : ?>
278
+ <span class="status-invisible">True</span><span class="status-state status-true"></span>
279
+ <span><?php esc_html_e( 'Writable', 'jupiterx-core' ); ?></span>
280
+ <?php else : ?>
281
+ <span class="status-invisible">False</span><span class="status-state status-false"></span>
282
+ <span><?php printf( __( 'Make sure <code>%s</code> directory is writable.', 'jupiterx-core' ), $sysinfo['wp_plugins_writable'] ); ?></span>
283
+ <?php endif; ?>
284
+ </td>
285
+ </tr>
286
+ <tr>
287
+ <td data-export-label="The themes Directory">
288
+ <?php esc_html_e( 'The themes Directory', 'jupiterx-core' ); ?>:
289
+ </td>
290
+ <td class="help">
291
+ <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'Check if themes directory is writable.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
292
+ </td>
293
+ <td>
294
+ <?php if ( wp_is_writable( $sysinfo['wp_themes_writable'] ) ) : ?>
295
+ <span class="status-invisible">True</span><span class="status-state status-true"></span>
296
+ <span><?php esc_html_e( 'Writable', 'jupiterx-core' ); ?></span>
297
+ <?php else : ?>
298
+ <span class="status-invisible">False</span><span class="status-state status-false"></span>
299
+ <span><?php printf( __( 'Make sure <code>%s</code> directory is writable.', 'jupiterx-core' ), $sysinfo['wp_themes_writable'] ); ?></span>
300
+ <?php endif; ?>
301
+ </td>
302
+ </tr>
303
+ </tbody>
304
+ </table>
305
+ <br><br>
306
+ <table class="table" cellspacing="0">
307
+ <thead class="thead-light">
308
+ <tr>
309
+ <th colspan="3" data-export-label="Theme"><?php esc_html_e( 'Theme', 'jupiterx-core' ); ?></th>
310
+ </tr>
311
+ </thead>
312
+ <tbody>
313
+ <tr>
314
+ <td data-export-label="Name"><?php esc_html_e( 'Name', 'jupiterx-core' ); ?>:</td>
315
+ <td class="help">
316
+ <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'The name of the current active theme.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
317
+ </td>
318
+ <td><?php echo esc_html( $sysinfo['theme']['name'] ); ?></td>
319
+ </tr>
320
+ <tr>
321
+ <td data-export-label="Version"><?php esc_html_e( 'Version', 'jupiterx-core' ); ?>:</td>
322
+ <td class="help">
323
+ <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'The installed version of the current active theme.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
324
+ </td>
325
+ <td>
326
+ <?php echo esc_html( $sysinfo['theme']['version'] ); ?>
327
+ </td>
328
+ </tr>
329
+ <tr>
330
+ <td data-export-label="Author URL"><?php esc_html_e( 'Author URL', 'jupiterx-core' ); ?>:</td>
331
+ <td class="help">
332
+ <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'The theme developers URL.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
333
+ </td>
334
+ <td><?php echo esc_url( $sysinfo['theme']['author_uri'] ); ?></td>
335
+ </tr>
336
+ <tr>
337
+ <td data-export-label="Child Theme"><?php esc_html_e( 'Child Theme', 'jupiterx-core' ); ?>:</td>
338
+ <td class="help">
339
+ <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'Displays whether or not the current theme is a child theme.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
340
+ </td>
341
+ <td>
342
+ <?php if ( is_child_theme() ) : ?>
343
+ <span class="status-invisible">True</span><span class="status-state status-true"></span>
344
+ <?php else : ?>
345
+ <span class="status-invisible">False</span><span class="status-state status-false"></span>
346
+ <?php endif; ?>
347
+ </td>
348
+ </tr>
349
+ <?php if ( is_child_theme() ) : ?>
350
+ <tr>
351
+ <td data-export-label="Parent Theme Name"><?php esc_html_e( 'Parent Theme Name', 'jupiterx-core' ); ?>:
352
+ </td>
353
+ <td class="help">
354
+ <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'The name of the parent theme.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
355
+ </td>
356
+ <td><?php echo esc_html( $sysinfo['theme']['parent_name'] ); ?></td>
357
+ </tr>
358
+ <tr>
359
+ <td data-export-label="Parent Theme Version">
360
+ <?php esc_html_e( 'Parent Theme Version', 'jupiterx-core' ); ?>:
361
+ </td>
362
+ <td class="help">
363
+ <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'The installed version of the parent theme.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
364
+ </td>
365
+ <td><?php echo esc_html( $sysinfo['theme']['parent_version'] ); ?></td>
366
+ </tr>
367
+ <tr>
368
+ <td data-export-label="Parent Theme Author URL">
369
+ <?php esc_html_e( 'Parent Theme Author URL', 'jupiterx-core' ); ?>:
370
+ </td>
371
+ <td class="help">
372
+ <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'The parent theme developers URL.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
373
+ </td>
374
+ <td><?php echo esc_url( $sysinfo['theme']['parent_author_uri'] ); ?></td>
375
+ </tr>
376
+ <?php endif; ?>
377
+ </tbody>
378
+ </table>
379
+ <br><br>
380
+
381
+ <table class="table" cellspacing="0">
382
+ <thead class="thead-light">
383
+ <tr>
384
+ <th colspan="3" data-export-label="Browser">
385
+ <?php esc_html_e( 'Browser', 'jupiterx-core' ); ?>
386
+ </th>
387
+ </tr>
388
+ </thead>
389
+ <tbody>
390
+ <tr>
391
+ <td data-export-label="Browser Info">
392
+ <?php esc_html_e( 'Browser Info', 'jupiterx-core' ); ?>:
393
+ </td>
394
+ <td class="help">
395
+ <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'Information about web browser current in use.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
396
+ </td>
397
+ <td>
398
+ <?php
399
+ foreach ( $sysinfo['browser'] as $key => $value ) {
400
+ echo '<strong>' . esc_html( ucfirst( $key ) ) . '</strong>: ' . esc_html( $value ) . '<br/>';
401
+ }
402
+ ?>
403
+ </td>
404
+ </tr>
405
+ </tbody>
406
+ </table>
407
+ <br><br>
408
+
409
+
410
+
411
+ <table class="table" cellspacing="0">
412
+ <thead class="thead-light">
413
+ <tr>
414
+ <th colspan="3" data-export-label="Server Environment">
415
+ <?php esc_html_e( 'Server Environment', 'jupiterx-core' ); ?>
416
+ </th>
417
+ </tr>
418
+ </thead>
419
+ <tbody>
420
+ <tr>
421
+ <td data-export-label="Server Info">
422
+ <?php esc_html_e( 'Server Info', 'jupiterx-core' ); ?>:
423
+ </td>
424
+ <td class="help">
425
+ <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'Information about the web server that is currently hosting your site.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
426
+ </td>
427
+ <td>
428
+ <?php echo esc_html( $sysinfo['server_info'] ); ?>
429
+ </td>
430
+ </tr>
431
+ <tr>
432
+ <td data-export-label="Localhost Environment">
433
+ <?php esc_html_e( 'Localhost Environment', 'jupiterx-core' ); ?>:
434
+ </td>
435
+ <td class="help">
436
+ <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'Is the server running in a localhost environment.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
437
+ </td>
438
+ <td>
439
+ <?php if ( 'true' == $sysinfo['localhost'] ) : ?>
440
+ <span class="status-invisible">True</span><span class="status-state status-true"></span>
441
+ <?php else : ?>
442
+ <span class="status-invisible">False</span><span class="status-state status-false"></span>
443
+ <?php endif; ?>
444
+ </td>
445
+ </tr>
446
+ <tr>
447
+ <td data-export-label="PHP Version">
448
+ <?php esc_html_e( 'PHP Version', 'jupiterx-core' ); ?>:
449
+ </td>
450
+ <td class="help">
451
+ <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'The version of PHP installed on your hosting server.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
452
+ </td>
453
+ <td>
454
+ <?php echo esc_html( $sysinfo['php_ver'] ); ?>
455
+ </td>
456
+ </tr>
457
+ <tr>
458
+ <td data-export-label="ABSPATH">
459
+ <?php esc_html_e( 'ABSPATH', 'jupiterx-core' ); ?>:
460
+ </td>
461
+ <td class="help">
462
+ <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'The ABSPATH variable on the server.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
463
+ </td>
464
+ <td>
465
+ <?php echo '<code>' . esc_html( $sysinfo['abspath'] ) . '</code>'; ?>
466
+ </td>
467
+ </tr>
468
+
469
+ <?php
470
+ if ( function_exists( 'ini_get' ) ) {
471
+ ?>
472
+ <tr class="<?php esc_attr_e( isset( $sysinfo_warnings['php_mem_limit'] ) ? 'jupiterx-sysinfo-warning' : '' ); ?>">
473
+ <td data-export-label="PHP Memory Limit"><?php esc_html_e( 'PHP Memory Limit', 'jupiterx-core' ); ?>:</td>
474
+ <td class="help">
475
+ <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'The largest filesize that can be contained in one post.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
476
+ </td>
477
+ <td>
478
+ <span class="jupiterx-sysinfo-value">
479
+ <?php echo esc_html( $sysinfo['php_mem_limit']['size'] ); ?>
480
+ </span>
481
+ <?php if ( isset( $sysinfo_warnings['php_mem_limit'] ) ): ?>
482
+ <span class="jupiterx-sysinfo-warning-msg">
483
+ <i class="jupiterx-icon-info-circle"></i>
484
+ <?php echo $sysinfo_warnings['php_mem_limit']['message']; ?>
485
+ </span>
486
+ <?php endif; ?>
487
+ </td>
488
+ </tr>
489
+ <tr>
490
+ <td data-export-label="PHP Post Max Size"><?php esc_html_e( 'PHP Post Max Size', 'jupiterx-core' ); ?>:</td>
491
+ <td class="help">
492
+ <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'The largest filesize that can be contained in one post.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
493
+ </td>
494
+ <td><?php echo esc_html( $sysinfo['php_post_max_size'] ); ?></td>
495
+ </tr>
496
+ <tr>
497
+ <td data-export-label="PHP Time Limit"><?php esc_html_e( 'PHP Time Limit', 'jupiterx-core' ); ?>:</td>
498
+ <td class="help">
499
+ <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'max_execution_time : The amount of time (in seconds) that your site will spend on a single operation before timing out (to avoid server lockups).', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
500
+ </td>
501
+ <td><?php echo esc_html( $sysinfo['php_time_limit'] ); ?></td>
502
+ </tr>
503
+
504
+ <tr>
505
+ <td data-export-label="PHP Max Input Vars"><?php esc_html_e( 'PHP Max Input Vars', 'jupiterx-core' ); ?>:</td>
506
+ <td class="help">
507
+ <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'The maximum number of variables your server can use for a single function to avoid overloads.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
508
+ </td>
509
+ <td><?php echo esc_html( $sysinfo['php_max_input_var'] ); ?></td>
510
+ </tr>
511
+
512
+ <?php
513
+ if ( true == $sysinfo['suhosin_installed'] ) {
514
+ ?>
515
+ <tr>
516
+ <td data-export-label="PHP Max Input Vars"><?php esc_html_e( 'Suhosin Max Request Vars', 'jupiterx-core' ); ?>:</td>
517
+ <td class="help">
518
+ <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'The maximum number of variables your server running Suhosin can use for a single function to avoid overloads.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
519
+ </td>
520
+ <td><?php echo esc_html( $sysinfo['suhosin_request_max_vars'] ); ?></td>
521
+ </tr>
522
+ <tr>
523
+ <td data-export-label="PHP Max Input Vars"><?php esc_html_e( 'Suhosin Max Post Vars', 'jupiterx-core' ); ?>:</td>
524
+ <td class="help">
525
+ <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'The maximum number of variables your server running Suhosin can use for a single function to avoid overloads.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
526
+ </td>
527
+ <td><?php echo esc_html( $sysinfo['suhosin_post_max_vars'] ); ?></td>
528
+ </tr>
529
+ <?php
530
+ }
531
+ ?>
532
+ <tr>
533
+ <td data-export-label="PHP Display Errors"><?php esc_html_e( 'PHP Display Errors', 'jupiterx-core' ); ?>:</td>
534
+ <td class="help">
535
+ <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'Determines if PHP will display errors within the browser.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
536
+ </td>
537
+ <td>
538
+ <?php if ( 'false' == $sysinfo['php_display_errors'] ) : ?>
539
+ <span class="status-invisible">False</span><span class="status-state status-false"></span>
540
+ <?php else : ?>
541
+ <span class="status-invisible">True</span><span class="status-state status-true"></span>
542
+ <?php endif; ?>
543
+ </td>
544
+ </tr>
545
+ <?php
546
+ }
547
+ ?>
548
+ <tr>
549
+ <td data-export-label="SUHOSIN Installed"><?php esc_html_e( 'SUHOSIN Installed', 'jupiterx-core' ); ?>:</td>
550
+ <td class="help">
551
+ <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'Suhosin is an advanced protection system for PHP installations. It was designed to protect your servers on the one hand against a number of well known problems in PHP applications and on the other hand against potential unknown vulnerabilities within these applications or the PHP core itself. If enabled on your server, Suhosin may need to be configured to increase its data submission limits.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
552
+ </td>
553
+ <td>
554
+ <?php if ( false == $sysinfo['suhosin_installed'] ) : ?>
555
+ <span class="status-invisible">False</span><span class="status-state status-false"></span>
556
+ <?php else : ?>
557
+ <span class="status-invisible">True</span><span class="status-state status-true"></span>
558
+ <?php endif; ?>
559
+ </td>
560
+ </tr>
561
+
562
+ <tr>
563
+ <td data-export-label="MySQL Version"><?php esc_html_e( 'MySQL Version', 'jupiterx-core' ); ?>:</td>
564
+ <td class="help">
565
+ <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'The version of MySQL installed on your hosting server.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
566
+ </td>
567
+ <td><?php echo esc_html( $sysinfo['mysql_ver'] ); ?></td>
568
+ </tr>
569
+ <tr>
570
+ <td data-export-label="Max Upload Size"><?php esc_html_e( 'Max Upload Size', 'jupiterx-core' ); ?>:</td>
571
+ <td class="help">
572
+ <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'The largest filesize that can be uploaded to your WordPress installation.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
573
+ </td>
574
+ <td><?php echo esc_html( $sysinfo['max_upload_size'] ); ?></td>
575
+ </tr>
576
+ <tr>
577
+ <td data-export-label="Default Timezone is UTC">
578
+ <?php esc_html_e( 'Default Timezone is UTC', 'jupiterx-core' ); ?>:
579
+ </td>
580
+ <td class="help">
581
+ <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'The default timezone for your server.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
582
+ </td>
583
+ <td>
584
+ <?php if ( 'false' == $sysinfo['def_tz_is_utc'] ) : ?>
585
+ <span class="status-invisible">False</span><span class="status-state status-false"></span>
586
+ <?php sprintf( __( 'Default timezone is %s - it should be UTC', 'jupiterx-core' ), esc_html( date_default_timezone_get() ) ); ?>
587
+ <?php else : ?>
588
+ <span class="status-invisible">True</span><span class="status-state status-true"></span>
589
+ <?php endif; ?>
590
+ </td>
591
+ </tr>
592
+ <tr>
593
+
594
+ <td data-export-label="PHP XML">
595
+ <?php esc_html_e( 'PHP XML', 'jupiterx-core' ); ?>:
596
+ </td>
597
+ <td class="help">
598
+ <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'Theme requires PHP XML Library to be installed.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
599
+ </td>
600
+ <td>
601
+ <?php if ( 'false' == $sysinfo['phpxml'] ) : ?>
602
+ <span class="status-invisible">False</span><span class="status-state status-false"></span>
603
+ <?php else : ?>
604
+ <span class="status-invisible">True</span><span class="status-state status-true"></span>
605
+ <?php endif; ?>
606
+ </td>
607
+ </tr>
608
+ <tr>
609
+ <td data-export-label="MBString">
610
+ <?php esc_html_e( 'MBString', 'jupiterx-core' ); ?>:
611
+ </td>
612
+ <td class="help">
613
+ <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'Theme requires MBString PHP Library to be installed.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
614
+ </td>
615
+ <td>
616
+ <?php if ( 'false' == $sysinfo['mbstring'] ) : ?>
617
+ <span class="status-invisible">False</span><span class="status-state status-false"></span>
618
+ <?php else : ?>
619
+ <span class="status-invisible">True</span><span class="status-state status-true"></span>
620
+ <?php endif; ?>
621
+ </td>
622
+ </tr>
623
+ <tr>
624
+ <td data-export-label="SimpleXML">
625
+ <?php esc_html_e( 'SimpleXML', 'jupiterx-core' ); ?>:
626
+ </td>
627
+ <td class="help">
628
+ <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'Theme requires SimpleXML PHP Library to be installed.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
629
+ </td>
630
+ <td>
631
+ <?php if ( 'false' == $sysinfo['simplexml'] ) : ?>
632
+ <span class="status-invisible">False</span><span class="status-state status-false"></span>
633
+ <?php else : ?>
634
+ <span class="status-invisible">True</span><span class="status-state status-true"></span>
635
+ <?php endif; ?>
636
+ </td>
637
+ </tr>
638
+ <?php
639
+ $posting = array();
640
+
641
+ $posting['fsockopen_curl']['name'] = esc_html__( 'Fsockopen/cURL', 'jupiterx-core' );
642
+ $posting['fsockopen_curl']['help'] = esc_attr__( 'Used when communicating with remote services with PHP.', 'jupiterx-core' );
643
+
644
+ if ( 'true' == $sysinfo['fsockopen_curl'] ) {
645
+ $posting['fsockopen_curl']['success'] = true;
646
+ } else {
647
+ $posting['fsockopen_curl']['success'] = false;
648
+ $posting['fsockopen_curl']['note'] = esc_html__( 'Your server does not have fsockopen or cURL enabled - cURL is used to communicate with other servers. Please contact your hosting provider.', 'jupiterx-core' );
649
+ }
650
+
651
+ $posting['soap_client']['name'] = esc_html__( 'SoapClient', 'jupiterx-core' );
652
+ $posting['soap_client']['help'] = esc_attr__( 'Some webservices like shipping use SOAP to get information from remote servers, for example, live shipping quotes from FedEx require SOAP to be installed.', 'jupiterx-core' );
653
+
654
+ if ( true == $sysinfo['soap_client'] ) {
655
+ $posting['soap_client']['success'] = true;
656
+ } else {
657
+ $posting['soap_client']['success'] = false;
658
+ $posting['soap_client']['note'] = sprintf( __( 'Your server does not have the <a href="%s">SOAP Client</a> class enabled - some gateway plugins which use SOAP may not work as expected.', 'jupiterx-core' ), 'http://php.net/manual/en/class.soapclient.php' );
659
+ }
660
+
661
+ $posting['dom_document']['name'] = esc_html__( 'DOMDocument', 'jupiterx-core' );
662
+ $posting['dom_document']['help'] = esc_attr__( 'HTML/Multipart emails use DOMDocument to generate inline CSS in templates.', 'jupiterx-core' );
663
+
664
+ if ( true == $sysinfo['dom_document'] ) {
665
+ $posting['dom_document']['success'] = true;
666
+ } else {
667
+ $posting['dom_document']['success'] = false;
668
+ $posting['dom_document']['note'] = sprintf( __( 'Your server does not have the <a href="%s">DOMDocument</a> class enabled - HTML/Multipart emails, and also some extensions, will not work without DOMDocument.', 'jupiterx-core' ), 'http://php.net/manual/en/class.domdocument.php' );
669
+ }
670
+
671
+
672
+ $posting['gzip']['name'] = esc_html__( 'GZip', 'jupiterx-core' );
673
+ $posting['gzip']['help'] = esc_attr__( 'GZip (gzopen) is used to open the GEOIP database from MaxMind.', 'jupiterx-core' );
674
+
675
+ if ( true == $sysinfo['gzip'] ) {
676
+ $posting['gzip']['success'] = true;
677
+ } else {
678
+ $posting['gzip']['success'] = false;
679
+ $posting['gzip']['note'] = sprintf( __( 'Your server does not support the <a href="%s">gzopen</a> function - this is required to use the GeoIP database from MaxMind. The API fallback will be used instead for geolocation.', 'jupiterx-core' ), 'http://php.net/manual/en/zlib.installation.php' );
680
+ }
681
+
682
+ // Zip Archive.
683
+ $posting['zip_archive']['name'] = esc_html__( 'Zip Archive', 'jupiterx-core' );
684
+ $posting['zip_archive']['help'] = esc_attr__( 'Used to read or write ZIP compressed archives and the files inside them.', 'jupiterx-core' );
685
+
686
+ if ( class_exists( 'ZipArchive' ) ) {
687
+ $posting['zip_archive']['success'] = true;
688
+ } else {
689
+ $posting['zip_archive']['note'] = esc_html__( 'ZipArchive library is missing. Install the Zip extension. Contact your hosting provider.', 'jupiterx-core' );
690
+ $posting['zip_archive']['success'] = false;
691
+ }
692
+
693
+ // Iconv.
694
+ $posting['iconv']['name'] = esc_html__( 'Iconv', 'jupiterx-core' );
695
+ $posting['iconv']['help'] = esc_attr__( 'Used in CSS parser to handle the character set conversion.', 'jupiterx-core' );
696
+
697
+ if ( extension_loaded( 'iconv' ) ) {
698
+ $posting['iconv']['success'] = true;
699
+ } else {
700
+ $posting['iconv']['note'] = esc_html__( 'Iconv library is missing. Install the iconv extension. Contact your hosting provider.', 'jupiterx-core' );
701
+ $posting['iconv']['success'] = false;
702
+ }
703
+
704
+ // Echo the fields.
705
+ foreach ( $posting as $post ) {
706
+ $mark = ! empty( $post['success'] ) ? 'yes' : 'error';
707
+ ?>
708
+ <tr>
709
+ <td data-export-label="<?php echo esc_html( $post['name'] ); ?>">
710
+ <?php echo esc_html( $post['name'] ); ?>:
711
+ </td>
712
+ <td class="help">
713
+ <a class="jupiterx-tooltip" data-content="<?php echo isset( $post['help'] ) ? wp_kses_post( $post['help'] ) : ''; ?>" href="#" data-toggle="popover" data-placement="top"></a>
714
+ </td>
715
+ <td>
716
+ <?php echo ! empty( $post['success'] ) ? '<span class="status-invisible">True</span><span class="status-state status-true"></span>' : '<span class="status-invisible">False</span><span class="status-state status-false"></span>'; ?>
717
+ <?php echo ! empty( $post['note'] ) ? wp_kses_data( $post['note'] ) : ''; ?>
718
+ </td>
719
+ </tr>
720
+ <?php
721
+ }
722
+ ?>
723
+ <tr data-jupiterx-ajax="http_requests">
724
+ <td data-export-label="HTTP Requests">
725
+ <?php esc_html_e( 'HTTP Requests', 'jupiterx-core' ); ?>:
726
+ </td>
727
+ <td class="help">
728
+ <a class="jupiterx-tooltip" data-content="<?php esc_attr_e( 'Check if HTTP requests (get, post and ...) are working properly.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
729
+ </td>
730
+ <td>
731
+ <span class="status-state"><span class="spinner is-active"></span></span>
732
+ <span class="status-text"></span>
733
+ </td>
734
+ </tr>
735
+ <tr data-jupiterx-ajax="artbees_server">
736
+ <td data-export-label="Communication with artbees.net">
737
+ <?php esc_html_e( 'Communication with artbees.net', 'jupiterx-core' ); ?>:
738
+ </td>
739
+ <td class="help">
740
+ <a class="jupiterx-tooltip" data-content="<?php esc_attr_e( 'Check if you have proper access to artbees.net server.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
741
+ </td>
742
+ <td>
743
+ <span class="status-state"><span class="spinner is-active"></span></span>
744
+ <span class="status-text"></span>
745
+ </td>
746
+ </tr>
747
+ </tbody>
748
+ </table>
749
+ <br><br>
750
+
751
+ <table class="table" cellspacing="0">
752
+ <thead class="thead-light">
753
+ <tr>
754
+ <th colspan="3" data-export-label="Database">
755
+ <?php esc_html_e( 'Database', 'jupiterx-core' ); ?>
756
+ </th>
757
+ </tr>
758
+ </thead>
759
+ <tbody>
760
+ <tr>
761
+ <td data-export-label="Database Info">
762
+ <?php esc_html_e( 'Database Size', 'jupiterx-core' ); ?>:
763
+ </td>
764
+ <td class="help">
765
+ <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'Information about database.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
766
+ </td>
767
+ <td>
768
+ <?php
769
+
770
+ global $wpdb;
771
+ $total_size = 0;
772
+ $data_usage = 0;
773
+ $index_usage = 0;
774
+
775
+ foreach ( $wpdb->tables('all') as $table ) {
776
+ $sql = $wpdb->prepare( "SHOW TABLE STATUS LIKE %s", $table );
777
+ $results = $wpdb->get_results( $sql, ARRAY_A );
778
+
779
+ if ( ! isset( $results[0] ) ) {
780
+ continue;
781
+ }
782
+
783
+ $data_usage += $results[0]['Data_length'];
784
+ $index_usage += $results[0]['Index_length'];
785
+ }
786
+
787
+ $total_size = round( ($data_usage + $index_usage) / ( 1024 * 1024 ), 2 );
788
+ echo $total_size . 'MB';
789
+ ?>
790
+ </td>
791
+ </tr>
792
+
793
+ <?php
794
+
795
+ $prefix = $wpdb->prefix;
796
+
797
+ $tables = [
798
+ 'options',
799
+ 'links',
800
+ 'commentmeta',
801
+ 'term_relationships',
802
+ 'postmeta',
803
+ 'posts',
804
+ 'term_taxonomy',
805
+ 'terms',
806
+ 'comments',
807
+ 'termmeta',
808
+ ];
809
+
810
+ $users_tables = [
811
+ 'usermeta',
812
+ 'users',
813
+ ];
814
+
815
+ $multisite_only_tables = [
816
+ 'blogs',
817
+ 'blogs_versions ',
818
+ 'registration_log',
819
+ 'signups',
820
+ 'site',
821
+ 'sitemeta',
822
+ ];
823
+
824
+ // Order of conditions should not be changed.
825
+ if ( ! is_multisite() ) {
826
+ $tables = array_merge( $tables, $users_tables );
827
+ }
828
+
829
+ if ( is_multisite() && is_super_admin() ) {
830
+ $tables = array_merge( $tables, $users_tables, $multisite_only_tables );
831
+ }
832
+
833
+ foreach ( $tables as $key => $wp_table ) {
834
+ $tables[ $key ] = $wpdb->prefix . $wp_table;
835
+ }
836
+
837
+ foreach ( $tables as $table ) {
838
+ $sql = $wpdb->prepare( "SHOW TABLE STATUS LIKE %s", $table );
839
+ $results = $wpdb->get_results( $sql, ARRAY_A );
840
+
841
+ if ( ! isset( $results[0] ) ) {
842
+ continue;
843
+ }
844
+
845
+ $data_usage = $results[0]['Data_length'];
846
+ $index_usage = $results[0]['Index_length'];
847
+ ?>
848
+ <tr>
849
+ <td data-export-label="<?php echo esc_attr( $table ); ?> Info">
850
+ <?php echo esc_html( $table ); ?>:
851
+ </td>
852
+ <td></td>
853
+ <td>
854
+ <?php
855
+ $total_size = round( ($data_usage + $index_usage) / ( 1024 * 1024 ), 2 );
856
+ echo $total_size . ' MB';
857
+ ?>
858
+ </td>
859
+ </tr>
860
+ <?php
861
+ }
862
+
863
+ ?>
864
+ </tbody>
865
+ <tr>
866
+ <td data-export-label="Theme Mods Info">
867
+ <?php esc_html_e( 'Theme Mods Size', 'jupiterx-core' ); ?>:
868
+ </td>
869
+ <td class="help">
870
+ <a class="jupiterx-tooltip" data-content="<?php echo esc_attr__( 'Size of customizer options.', 'jupiterx-core' ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
871
+ </td>
872
+ <td>
873
+ <?php
874
+ $mods = get_theme_mods();
875
+ $has_numeric_value = false;
876
+
877
+ foreach ( $mods as $key => $value ) {
878
+ if ( is_numeric( $key ) ) {
879
+ $has_numeric_value = true;
880
+ break;
881
+ }
882
+ }
883
+ ?>
884
+ <span class="jupiterx-cp-inline-text"><?php echo round( mb_strlen( serialize( get_theme_mods() ) ) / ( 1024 ), 2 ) . ' KB'; ?></span>
885
+ <?php if ( $has_numeric_value ) : ?>
886
+ <span><a href="#" id="jupiterx-mods-cleanup" class="button button-secondary" data-nonce="<?php echo esc_attr( wp_create_nonce( 'jupiterx_mods_cleanup' ) ); ?>"> <?php esc_html_e( 'Cleanup', 'jupiterx-core' ); ?></a></span>
887
+ <?php endif; ?>
888
+ </td>
889
+ </tr>
890
+ </table>
891
+ <br><br>
892
+
893
+ <table class="table" cellspacing="0">
894
+ <thead class="thead-light">
895
+ <tr>
896
+ <th colspan="3" data-export-label="Active Plugins (<?php echo esc_html( count( (array) get_option( 'active_plugins' ) ) ); ?>)">
897
+ <?php esc_html_e( 'Active Plugins', 'jupiterx-core' ); ?> (<?php echo esc_html( count( (array) get_option( 'active_plugins' ) ) ); ?>)
898
+ </th>
899
+ </tr>
900
+ </thead>
901
+ <tbody>
902
+ <?php
903
+ foreach ( $sysinfo['plugins'] as $name => $plugin_data ) {
904
+
905
+ if ( ! empty( $plugin_data['Name'] ) ) {
906
+ $plugin_name = esc_html( $plugin_data['Name'] );
907
+
908
+ if ( ! empty( $plugin_data['PluginURI'] ) ) {
909
+ $plugin_name = '<a href="' . esc_url( $plugin_data['PluginURI'] ) . '" title="' . esc_attr__( 'Visit plugin homepage', 'jupiterx-core' ) . '">' . esc_html( $plugin_name ) . '</a>';
910
+ }
911
+ ?>
912
+ <tr>
913
+ <td><?php echo wp_kses_post( $plugin_name ); ?></td>
914
+ <td class="help">
915
+ <a class="jupiterx-tooltip" data-content="<?php echo esc_attr( strip_tags( $plugin_data['Description'] ) ); ?>" href="#" data-toggle="popover" data-placement="top"></a>
916
+ </td>
917
+ <td>
918
+ <?php echo sprintf( _x( 'by %s', 'by author', 'jupiterx-core' ), wp_kses_post( $plugin_data['Author'] ) ) . ' &ndash; ' . esc_html( $plugin_data['Version'] ); ?>
919
+ </td>
920
+ </tr>
921
+ <?php
922
+ }
923
+ }
924
+ ?>
925
+ </tbody>
926
+ </table>
927
+
928
+ </div>
929
+ </div>
includes/custom-fields/title-bar.php CHANGED
@@ -1,33 +1,33 @@
1
- <?php
2
- /**
3
- * Add Jupiter Post Options > Title Bar meta options.
4
- *
5
- * @package JupiterX_Core\Custom_fields
6
- *
7
- * @since 1.9.0
8
- */
9
-
10
- add_action( 'jupiterx_custom_field_post_types', 'jupiterx_add_title_bar_field' );
11
- /**
12
- * Add title bar field to the new page/post/portfolio. Add due to prevent content creation rule.
13
- *
14
- * @since 1.9.0
15
- *
16
- * @return void
17
- */
18
- function jupiterx_add_title_bar_field() {
19
- if ( ! class_exists( 'acf' ) ) {
20
- return;
21
- }
22
- $key = 'field_jupiterx_post_title_bar';
23
- $parent = 'group_jupiterx_post';
24
-
25
- acf_add_local_field( [
26
- 'key' => "{$key}_subtitle",
27
- 'parent' => $parent,
28
- 'label' => __( 'Subtitle', 'jupiterx-core' ),
29
- 'name' => 'jupiterx_title_bar_subtitle',
30
- 'type' => 'textarea',
31
- 'rows' => '3',
32
- ] );
33
- }
1
+ <?php
2
+ /**
3
+ * Add Jupiter Post Options > Title Bar meta options.
4
+ *
5
+ * @package JupiterX_Core\Custom_fields
6
+ *
7
+ * @since 1.9.0
8
+ */
9
+
10
+ add_action( 'jupiterx_custom_field_post_types', 'jupiterx_add_title_bar_field' );
11
+ /**
12
+ * Add title bar field to the new page/post/portfolio. Add due to prevent content creation rule.
13
+ *
14
+ * @since 1.9.0
15
+ *
16
+ * @return void
17
+ */
18
+ function jupiterx_add_title_bar_field() {
19
+ if ( ! class_exists( 'acf' ) ) {
20
+ return;
21
+ }
22
+ $key = 'field_jupiterx_post_title_bar';
23
+ $parent = 'group_jupiterx_post';
24
+
25
+ acf_add_local_field( [
26
+ 'key' => "{$key}_subtitle",
27
+ 'parent' => $parent,
28
+ 'label' => __( 'Subtitle', 'jupiterx-core' ),
29
+ 'name' => 'jupiterx_title_bar_subtitle',
30
+ 'type' => 'textarea',
31
+ 'rows' => '3',
32
+ ] );
33
+ }
includes/customizer/api/classes/class-multilingual.php CHANGED
@@ -1,448 +1,448 @@
1
- <?php
2
-
3
- // phpcs:ignoreFile
4
- /**
5
- * If Polylang is active:
6
- * - save and retrieve customizer setting per language.
7
- * - on front-page, set options and theme mod for the selected language.
8
- *
9
- * Inspired by https://github.com/fastlinemedia/customizer-export-import
10
- *
11
- * @package JupiterX\Framework\API\Customizer
12
- */
13
-
14
- if ( ! function_exists( 'pll_current_language' ) && ! class_exists( 'SitePress' ) ) {
15
- return;
16
- }
17
-
18
- /**
19
- * Functionality for multilingual customizer.
20
- *
21
- * @since 1.0.0
22
- *
23
- * @package JupiterX\Framework\API\Customizer
24
- * @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
25
- */
26
- class CoreCustomizerMultilingual {
27
-
28
- public static function init() {
29
- $self = new self();
30
-
31
- // Disable detect browser language, will return default language instead.
32
- add_filter( 'pll_preferred_language', '__return_false' );
33
-
34
- add_action( 'customize_controls_enqueue_scripts', [ __CLASS__, 'add_lang_to_customizer_previewer' ], 9 );
35
- add_action( 'wp_before_admin_bar_render', [ __CLASS__, 'on_wp_before_admin_bar_render' ], 100 );
36
- add_action( 'admin_menu', [ __CLASS__, 'on_admin_menu' ], 100 );
37
- add_action( 'after_setup_theme', [ __CLASS__, 'remove_filters' ], 5 );
38
-
39
- $theme_stylesheet_slug = get_option( 'stylesheet' );
40
- $option_types = [ 'blogname', 'blogdescription', 'site_icon' ];
41
-
42
- if ( ! ( defined('DOING_AJAX') && DOING_AJAX && isset( $_POST['action'] ) && in_array( $_POST['action'], [ 'jupiterx_cp_cleanup_mods', 'abb_install_template_procedure' ], true ) ) ) {
43
- // Get theme mod options.
44
- add_filter( 'option_theme_mods_' . $theme_stylesheet_slug, [ $self, 'on_option_theme_mods_get' ], 10, 1 );
45
- // Update theme mod options.
46
- add_filter( 'pre_update_option_theme_mods_' . $theme_stylesheet_slug, [ $self, 'on_option_theme_mods_update' ], 10, 2 );
47
- }
48
-
49
- foreach ( $option_types as $option_type ) {
50
- add_filter( 'pre_option_' . $option_type, [ $self, 'on_wp_option_get' ], 10, 3 ); // get_option hook.
51
- add_filter( 'pre_update_option_' . $option_type, [ $self, 'on_wp_option_update' ], 10, 3 ); // update_option hook.
52
- }
53
-
54
- return $self;
55
- }
56
-
57
- /**
58
- * Remove bloginfo update filters. As we save options per language in this class, we don't need WPML functionality.
59
- *
60
- * @since 1.0.0
61
- *
62
- * @return void
63
- */
64
- public static function remove_filters() {
65
- global $WPML_String_Translation; // @phpcs:ignore
66
- remove_filter( 'pre_update_option_blogname', [ $WPML_String_Translation, 'pre_update_option_blogname' ], 5 ); // @phpcs:ignore
67
- remove_filter( 'pre_update_option_blogdescription', [ $WPML_String_Translation, 'pre_update_option_blogdescription' ], 5 ); // @phpcs:ignore
68
- }
69
-
70
- /**
71
- * Get current language.
72
- *
73
- * @since 1.0.0
74
- *
75
- * @return string|bool $language|false Current language or false when none of Polylang & WPML are active.
76
- */
77
- public static function get_language() {
78
- if ( function_exists( 'pll_current_language' ) ) {
79
- $language = pll_current_language();
80
-
81
- if ( ! $language ) {
82
- $language = pll_default_language();
83
- }
84
-
85
- return $language;
86
- }
87
-
88
- if ( class_exists( 'SitePress' ) && defined( 'ICL_LANGUAGE_CODE' ) ) {
89
- return ICL_LANGUAGE_CODE;
90
- }
91
-
92
- return false;
93
- }
94
-
95
- /**
96
- * Get a list of active languages with extra parameters like name and slug.
97
- *
98
- * @since 1.0.0
99
- *
100
- * @return array|bool $languages|false List of active languages or false when none of Polylang & WPML are active.
101
- */
102
- public static function get_languages_list() {
103
- if ( function_exists( 'pll_current_language' ) ) {
104
- return get_option( '_transient_pll_languages_list' );
105
- }
106
-
107
- if ( class_exists( 'SitePress' ) && defined( 'ICL_LANGUAGE_CODE' ) ) {
108
- $list = icl_get_languages( 'skip_missing=1' );
109
- $languages = [];
110
-
111
- foreach ( $list as $language ) {
112
- $temp = [];
113
- $temp['name'] = $language['native_name'];
114
- $temp['slug'] = $language['code'];
115
- $languages[] = $temp;
116
- }
117
-
118
- return $languages;
119
- }
120
-
121
- return false;
122
- }
123
-
124
- /**
125
- * Get a proper option key per plugin.
126
- *
127
- * @since 1.0.0
128
- *
129
- * @return string|bool Option key or false when none of Polylang & WPML are active.
130
- */
131
- public static function get_option_key() {
132
- if ( function_exists( 'pll_current_language' ) ) {
133
- return '_customizer_polylang_settings_';
134
- }
135
-
136
- if ( class_exists( 'SitePress' ) && defined( 'ICL_LANGUAGE_CODE' ) ) {
137
- return '_customizer_wpml_settings_';
138
- }
139
-
140
- return false;
141
- }
142
-
143
- /**
144
- * Get home URL of current language.
145
- *
146
- * @param string $language current language.
147
- *
148
- * @since 1.0.0
149
- *
150
- * @return string|bool Home URL of current language or false when none of Polylang & WPML are active.
151
- */
152
- public static function get_home_url( $language ) {
153
- if ( function_exists( 'pll_current_language' ) ) {
154
- return pll_home_url( $language );
155
- }
156
-
157
- if ( class_exists( 'SitePress' ) ) {
158
- global $sitepress;
159
- return $sitepress->language_url( $language );
160
- }
161
-
162
- return false;
163
- }
164
-
165
- /**
166
- * Helper to fetch custom customizer db content.
167
- *
168
- * @since 1.3.0
169
- *
170
- * @return mixed Customizer array or false.
171
- */
172
- protected function get_custom_customizer_option() {
173
- $current_language = self::get_language();
174
- $theme_slug = get_option( 'template' );
175
- $option_prefix = str_replace( '-', '_', $theme_slug );
176
- $option_name = $option_prefix . self::get_option_key() . $current_language;
177
-
178
- return get_option( $option_name, false );
179
- }
180
-
181
- /**
182
- * Helper to update custom customizer db content.
183
- *
184
- * @since 1.3.0
185
- *
186
- * @param mixed $data Data to insert.
187
- *
188
- * @return bool Success.
189
- */
190
- protected function update_custom_customizer_option( $data ) {
191
- $current_language = self::get_language();
192
- $theme_slug = get_option( 'template' );
193
- $option_prefix = str_replace( '-', '_', $theme_slug );
194
- $option_name = $option_prefix . self::get_option_key() . $current_language;
195
-
196
- return update_option( $option_name, $data );
197
- }
198
-
199
- /**
200
- * Helper
201
- *
202
- * @since 1.3.0
203
- *
204
- * @return bool If the current language is the default language.
205
- */
206
- protected function current_lang_not_default() {
207
- if ( class_exists( 'SitePress' ) ) {
208
- global $sitepress;
209
- return $sitepress->get_current_language() !== $sitepress->get_default_language();
210
- }
211
-
212
- return pll_current_language() !== pll_default_language();
213
- }
214
-
215
- /**
216
- * Check the custom db field on get_option hook to be able to return custom language value.
217
- * If the current language is default, then return from default wp option
218
- *
219
- * @since 1.3.0
220
- *
221
- * @param bool $pre_option This is false. If something else is returned wp exits the check in db and uses this value.
222
- * @param string $option Option name asked for.
223
- * @param mixed $default Default value, second args when asking for options.
224
- *
225
- * @return mixed
226
- * @SuppressWarnings(PHPMD.UnusedFormalParameter)
227
- */
228
- public function on_wp_option_get( $pre_option, $option, $default ) {
229
-
230
- // If not the default language, then skip the custom check and wp will the use default options.
231
- if ( $this->current_lang_not_default() ) {
232
- $data = $this->get_custom_customizer_option();
233
-
234
- // Found the custom option. Move on.
235
- if ( is_array( $data ) && isset( $data['options'] ) && isset( $data['options'][ $option ] ) ) {
236
- return $data['options'][ $option ];
237
- }
238
- }
239
-
240
- return $default;
241
- }
242
-
243
- /**
244
- * Update the custom db field on get_option hook.
245
- * If the current language is not default, then return old value to prevent from saving to default wp option.
246
- *
247
- * @since 1.3.0
248
- *
249
- * @param mixed $value The new, unserialized option value.
250
- * @param mixed $old_value The old option value.
251
- * @param string $option Option name.
252
- *
253
- * @return mixed
254
- */
255
- public function on_wp_option_update( $value, $old_value, $option ) {
256
- // Fetch custom option db field.
257
- $data = $this->get_custom_customizer_option();
258
- $theme_slug = get_option( 'template' );
259
- // If false, the field hasn't been created yet, so it must be created.
260
- if ( false === $data ) {
261
- $data = [
262
- 'template' => $theme_slug,
263
- 'mods' => [],
264
- 'options' => [],
265
- ];
266
- }
267
-
268
- // Make sure the options array exists. We are going to use it soon.
269
- if ( ! isset( $data['options'] ) ) {
270
- $data['options'] = [];
271
- }
272
-
273
- $data['options'][ $option ] = $value;
274
-
275
- // Update option value in custom db field. (Not necessary to save for default language since it uses default wp option fields for values when get option).
276
- $this->update_custom_customizer_option( $data );
277
-
278
- // If the current language is not the default language, prevent saving to option table by passing the old value back. It will then exit after the filter.
279
- if ( $this->current_lang_not_default() ) {
280
- return $old_value;
281
- }
282
-
283
- return $value;
284
- }
285
-
286
- /**
287
- * Check the custom db field on get_option customizer field option name hook to be able to return custom language value.
288
- * Parse arguments with default wp customizer values to make sure all are present in the return.
289
- *
290
- * @since 1.3.0
291
- *
292
- * @param array $value The customizer settings.
293
- *
294
- * @return array
295
- */
296
- public function on_option_theme_mods_get( $value ) {
297
- $data = $this->get_custom_customizer_option();
298
-
299
- if ( isset( $data['mods'] ) && is_array( $data['mods'] ) && ! empty( $data['mods'] ) ) {
300
- $value = wp_parse_args( $data['mods'], $value );
301
- }
302
-
303
- return $value;
304
- }
305
-
306
- /**
307
- * Update custom customizer option.
308
- * If the current language is not default, then return old value to prevent from saving to customizer wp option.
309
- *
310
- * @since 1.3.0
311
- *
312
- * @param mixed $value The new, unserialized option value.
313
- * @param mixed $old_value The old option value.
314
- */
315
- public function on_option_theme_mods_update( $value, $old_value ) {
316
-
317
- $current_data = $this->get_custom_customizer_option();
318
- $theme_slug = get_option( 'template' );
319
-
320
- $data = [
321
- 'template' => $theme_slug,
322
- 'mods' => isset( $current_data['mods'] ) ? $current_data['mods'] : [],
323
- 'options' => isset( $current_data['options'] ) ? $current_data['options'] : [],
324
- ];
325
-
326
- if ( is_array( $value ) && ! empty( $value ) ) {
327
- foreach ( $value as $key => $val ) {
328
- $data['mods'][ $key ] = $val;
329
- }
330
- }
331
- $this->update_custom_customizer_option( $data );
332
-
333
- if ( $this->current_lang_not_default() ) {
334
- return $old_value;
335
- }
336
-
337
- return $value;
338
- }
339
-
340
- /**
341
- * If Polylang activated, set the preview url and add select language control
342
- *
343
- * @author soderlind
344
- * @version 1.0.0
345
- * @link https://gist.github.com/soderlind/1908634f5eb0c1f69428666dd2a291d0
346
- *
347
- * @since 1.0.0
348
- */
349
- public static function add_lang_to_customizer_previewer() {
350
- $languages = self::get_languages_list();
351
-
352
- if ( ! $languages ) {
353
- return;
354
- }
355
-
356
- $handle = 'dss-add-lang-to-template';
357
- $js_path_url = trailingslashit( apply_filters( 'scp_js_path_url', get_stylesheet_directory_uri() . '/js/' ) );
358
- $src = $js_path_url . 'customizer-multilingual.js';
359
- $deps = [ 'customize-controls' ];
360
- wp_enqueue_script( $handle, $src, $deps, JUPITERX_VERSION, true );
361
- $language = ( empty( $_REQUEST['lang'] ) ) ? self::get_language() : $_REQUEST['lang']; // @phpcs:ignore
362
-
363
- if ( empty( $language ) ) {
364
- $language = self::default_language();
365
- }
366
-
367
- $url = add_query_arg( 'lang', $language, self::get_home_url( $language ) );
368
-
369
- wp_add_inline_script(
370
- $handle,
371
- sprintf(
372
- 'JupiterXCustomizerMultilingual.init( %s );', wp_json_encode(
373
- [
374
- 'url' => $url,
375
- 'languages' => $languages,
376
- 'current_language' => $language,
377
- 'switcher_text' => __( 'Language:', 'jupiterx-core' ),
378
- ]
379
- )
380
- ), 'after'
381
- );
382
- }
383
-
384
- /**
385
- * Append lang="contrycode" to the customizer url in the adminbar
386
- *
387
- * @since 1.0.0
388
- *
389
- * @return void
390
- */
391
- public static function on_wp_before_admin_bar_render() {
392
- global $wp_admin_bar;
393
- $customize_node = $wp_admin_bar->get_node( 'customize' );
394
- if ( ! empty( $customize_node ) ) {
395
- $customize_node->href = add_query_arg( 'lang', self::get_language(), $customize_node->href );
396
- $wp_admin_bar->add_node( $customize_node );
397
- }
398
- }
399
-
400
- /**
401
- * Append lang="contrycode" to the customizer url in the Admin->Apperance->Customize menu
402
- *
403
- * @since 1.0.0
404
- *
405
- * @return void
406
- */
407
- public static function on_admin_menu() {
408
- global $menu, $submenu;
409
- $parent = 'themes.php';
410
- if ( ! isset( $submenu[ $parent ] ) ) {
411
- return;
412
- }
413
- foreach ( $submenu[ $parent ] as $k => $d ) {
414
- if ( 'customize' === $d['1'] ) {
415
- $submenu[ $parent ][ $k ]['2'] = add_query_arg( 'lang', self::get_language(), $submenu[ $parent ][ $k ]['2'] ); // @phpcs:ignore
416
- break;
417
- }
418
- }
419
- }
420
-
421
- }
422
-
423
- CoreCustomizerMultilingual::init();
424
-
425
- if ( class_exists( 'WP_Customize_Setting' ) ) {
426
- /**
427
- * A class that extends WP_Customize_Setting so we can access
428
- * the protected updated method when importing options.
429
- *
430
- * @since 0.3
431
- */
432
- final class Customizermultilingialoption extends WP_Customize_Setting { // @phpcs:ignore
433
-
434
-
435
- /**
436
- * Import an option value for this setting.
437
- *
438
- * @since 0.3
439
- *
440
- * @param mixed $value The option value.
441
- *
442
- * @return void
443
- */
444
- public function import( $value ) {
445
- $this->update( $value );
446
- }
447
- }
448
- }
1
+ <?php
2
+
3
+ // phpcs:ignoreFile
4
+ /**
5
+ * If Polylang is active:
6
+ * - save and retrieve customizer setting per language.
7
+ * - on front-page, set options and theme mod for the selected language.
8
+ *
9
+ * Inspired by https://github.com/fastlinemedia/customizer-export-import
10
+ *
11
+ * @package JupiterX\Framework\API\Customizer
12
+ */
13
+
14
+ if ( ! function_exists( 'pll_current_language' ) && ! class_exists( 'SitePress' ) ) {
15
+ return;
16
+ }
17
+
18
+ /**
19
+ * Functionality for multilingual customizer.
20
+ *
21
+ * @since 1.0.0
22
+ *
23
+ * @package JupiterX\Framework\API\Customizer
24
+ * @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
25
+ */
26
+ class CoreCustomizerMultilingual {
27
+
28
+ public static function init() {
29
+ $self = new self();
30
+
31
+ // Disable detect browser language, will return default language instead.
32
+ add_filter( 'pll_preferred_language', '__return_false' );
33
+
34
+ add_action( 'customize_controls_enqueue_scripts', [ __CLASS__, 'add_lang_to_customizer_previewer' ], 9 );
35
+ add_action( 'wp_before_admin_bar_render', [ __CLASS__, 'on_wp_before_admin_bar_render' ], 100 );
36
+ add_action( 'admin_menu', [ __CLASS__, 'on_admin_menu' ], 100 );
37
+ add_action( 'after_setup_theme', [ __CLASS__, 'remove_filters' ], 5 );
38
+
39
+ $theme_stylesheet_slug = get_option( 'stylesheet' );
40
+ $option_types = [ 'blogname', 'blogdescription', 'site_icon' ];
41
+
42
+ if ( ! ( defined('DOING_AJAX') && DOING_AJAX && isset( $_POST['action'] ) && in_array( $_POST['action'], [ 'jupiterx_cp_cleanup_mods', 'abb_install_template_procedure' ], true ) ) ) {
43
+ // Get theme mod options.
44
+ add_filter( 'option_theme_mods_' . $theme_stylesheet_slug, [ $self, 'on_option_theme_mods_get' ], 10, 1 );
45
+ // Update theme mod options.
46
+ add_filter( 'pre_update_option_theme_mods_' . $theme_stylesheet_slug, [ $self, 'on_option_theme_mods_update' ], 10, 2 );
47
+ }
48
+
49
+ foreach ( $option_types as $option_type ) {
50
+ add_filter( 'pre_option_' . $option_type, [ $self, 'on_wp_option_get' ], 10, 3 ); // get_option hook.
51
+ add_filter( 'pre_update_option_' . $option_type, [ $self, 'on_wp_option_update' ], 10, 3 ); // update_option hook.
52
+ }
53
+
54
+ return $self;
55
+ }
56
+
57
+ /**
58
+ * Remove bloginfo update filters. As we save options per language in this class, we don't need WPML functionality.
59
+ *
60
+ * @since 1.0.0
61
+ *
62
+ * @return void
63
+ */
64
+ public static function remove_filters() {
65
+ global $WPML_String_Translation; // @phpcs:ignore
66
+ remove_filter( 'pre_update_option_blogname', [ $WPML_String_Translation, 'pre_update_option_blogname' ], 5 ); // @phpcs:ignore
67
+ remove_filter( 'pre_update_option_blogdescription', [ $WPML_String_Translation, 'pre_update_option_blogdescription' ], 5 ); // @phpcs:ignore
68
+ }
69
+
70
+ /**
71
+ * Get current language.
72
+ *
73
+ * @since 1.0.0
74
+ *
75
+ * @return string|bool $language|false Current language or false when none of Polylang & WPML are active.
76
+ */
77
+ public static function get_language() {
78
+ if ( function_exists( 'pll_current_language' ) ) {
79
+ $language = pll_current_language();
80
+
81
+ if ( ! $language ) {
82
+ $language = pll_default_language();
83
+ }
84
+
85
+ return $language;
86
+ }
87
+
88
+ if ( class_exists( 'SitePress' ) && defined( 'ICL_LANGUAGE_CODE' ) ) {
89
+ return ICL_LANGUAGE_CODE;
90
+ }
91
+
92
+ return false;
93
+ }
94
+
95
+ /**
96
+ * Get a list of active languages with extra parameters like name and slug.
97
+ *
98
+ * @since 1.0.0
99
+ *
100
+ * @return array|bool $languages|false List of active languages or false when none of Polylang & WPML are active.
101
+ */
102
+ public static function get_languages_list() {
103
+ if ( function_exists( 'pll_current_language' ) ) {
104
+ return get_option( '_transient_pll_languages_list' );
105
+ }
106
+
107
+ if ( class_exists( 'SitePress' ) && defined( 'ICL_LANGUAGE_CODE' ) ) {
108
+ $list = icl_get_languages( 'skip_missing=1' );
109
+ $languages = [];
110
+
111
+ foreach ( $list as $language ) {
112
+ $temp = [];
113
+ $temp['name'] = $language['native_name'];
114
+ $temp['slug'] = $language['code'];
115
+ $languages[] = $temp;
116
+ }
117
+
118
+ return $languages;
119
+ }
120
+
121
+ return false;
122
+ }
123
+
124
+ /**
125
+ * Get a proper option key per plugin.
126
+ *
127
+ * @since 1.0.0
128
+ *
129
+ * @return string|bool Option key or false when none of Polylang & WPML are active.
130
+ */
131
+ public static function get_option_key() {
132
+ if ( function_exists( 'pll_current_language' ) ) {
133
+ return '_customizer_polylang_settings_';
134
+ }
135
+
136
+ if ( class_exists( 'SitePress' ) && defined( 'ICL_LANGUAGE_CODE' ) ) {
137
+ return '_customizer_wpml_settings_';
138
+ }
139
+
140
+ return false;
141
+ }
142
+
143
+ /**
144
+ * Get home URL of current language.
145
+ *
146
+ * @param string $language current language.
147
+ *
148
+ * @since 1.0.0
149
+ *
150
+ * @return string|bool Home URL of current language or false when none of Polylang & WPML are active.
151
+ */
152
+ public static function get_home_url( $language ) {
153
+ if ( function_exists( 'pll_current_language' ) ) {
154
+ return pll_home_url( $language );
155
+ }
156
+
157
+ if ( class_exists( 'SitePress' ) ) {
158
+ global $sitepress;
159
+ return $sitepress->language_url( $language );
160
+ }
161
+
162
+ return false;
163
+ }
164
+
165
+ /**
166
+ * Helper to fetch custom customizer db content.
167
+ *
168
+ * @since 1.3.0
169
+ *
170
+ * @return mixed Customizer array or false.
171
+ */
172
+ protected function get_custom_customizer_option() {
173
+ $current_language = self::get_language();
174
+ $theme_slug = get_option( 'template' );
175
+ $option_prefix = str_replace( '-', '_', $theme_slug );
176
+ $option_name = $option_prefix . self::get_option_key() . $current_language;
177
+
178
+ return get_option( $option_name, false );
179
+ }
180
+
181
+ /**
182
+ * Helper to update custom customizer db content.
183
+ *
184
+ * @since 1.3.0
185
+ *
186
+ * @param mixed $data Data to insert.
187
+ *
188
+ * @return bool Success.
189
+ */
190
+ protected function update_custom_customizer_option( $data ) {
191
+ $current_language = self::get_language();
192
+ $theme_slug = get_option( 'template' );
193
+ $option_prefix = str_replace( '-', '_', $theme_slug );
194
+ $option_name = $option_prefix . self::get_option_key() . $current_language;
195
+
196
+ return update_option( $option_name, $data );
197
+ }
198
+
199
+ /**
200
+ * Helper
201
+ *
202
+ * @since 1.3.0
203
+ *
204
+ * @return bool If the current language is the default language.
205
+ */
206
+ protected function current_lang_not_default() {
207
+ if ( class_exists( 'SitePress' ) ) {
208
+ global $sitepress;
209
+ return $sitepress->get_current_language() !== $sitepress->get_default_language();
210
+ }
211
+
212
+ return pll_current_language() !== pll_default_language();
213
+ }
214
+
215
+ /**
216
+ * Check the custom db field on get_option hook to be able to return custom language value.
217
+ * If the current language is default, then return from default wp option
218
+ *
219
+ * @since 1.3.0
220
+ *
221
+ * @param bool $pre_option This is false. If something else is returned wp exits the check in db and uses this value.
222
+ * @param string $option Option name asked for.
223
+ * @param mixed $default Default value, second args when asking for options.
224
+ *
225
+ * @return mixed
226
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
227
+ */
228
+ public function on_wp_option_get( $pre_option, $option, $default ) {
229
+
230
+ // If not the default language, then skip the custom check and wp will the use default options.
231
+ if ( $this->current_lang_not_default() ) {
232
+ $data = $this->get_custom_customizer_option();
233
+
234
+ // Found the custom option. Move on.
235
+ if ( is_array( $data ) && isset( $data['options'] ) && isset( $data['options'][ $option ] ) ) {
236
+ return $data['options'][ $option ];
237
+ }
238
+ }
239
+
240
+ return $default;
241
+ }
242
+
243
+ /**
244
+ * Update the custom db field on get_option hook.
245
+ * If the current language is not default, then return old value to prevent from saving to default wp option.
246
+ *
247
+ * @since 1.3.0
248
+ *
249
+ * @param mixed $value The new, unserialized option value.
250
+ * @param mixed $old_value The old option value.
251
+ * @param string $option Option name.
252
+ *
253
+ * @return mixed
254
+ */
255
+ public function on_wp_option_update( $value, $old_value, $option ) {
256
+ // Fetch custom option db field.
257
+ $data = $this->get_custom_customizer_option();
258
+ $theme_slug = get_option( 'template' );
259
+ // If false, the field hasn't been created yet, so it must be created.
260
+ if ( false === $data ) {
261
+ $data = [
262
+ 'template' => $theme_slug,
263
+ 'mods' => [],
264
+ 'options' => [],
265
+ ];
266
+ }
267
+
268
+ // Make sure the options array exists. We are going to use it soon.
269
+ if ( ! isset( $data['options'] ) ) {
270
+ $data['options'] = [];
271
+ }
272
+
273
+ $data['options'][ $option ] = $value;
274
+
275
+ // Update option value in custom db field. (Not necessary to save for default language since it uses default wp option fields for values when get option).
276
+ $this->update_custom_customizer_option( $data );
277
+
278
+ // If the current language is not the default language, prevent saving to option table by passing the old value back. It will then exit after the filter.
279
+ if ( $this->current_lang_not_default() ) {
280
+ return $old_value;
281
+ }
282
+
283
+ return $value;
284
+ }
285
+
286
+ /**
287
+ * Check the custom db field on get_option customizer field option name hook to be able to return custom language value.
288
+ * Parse arguments with default wp customizer values to make sure all are present in the return.
289
+ *
290
+ * @since 1.3.0
291
+ *
292
+ * @param array $value The customizer settings.
293
+ *
294
+ * @return array
295
+ */
296
+ public function on_option_theme_mods_get( $value ) {
297
+ $data = $this->get_custom_customizer_option();
298
+
299
+ if ( isset( $data['mods'] ) && is_array( $data['mods'] ) && ! empty( $data['mods'] ) ) {
300
+ $value = wp_parse_args( $data['mods'], $value );
301
+ }
302
+
303
+ return $value;
304
+ }
305
+
306
+ /**
307
+ * Update custom customizer option.
308
+ * If the current language is not default, then return old value to prevent from saving to customizer wp option.
309
+ *
310
+ * @since 1.3.0
311
+ *
312
+ * @param mixed $value The new, unserialized option value.
313
+ * @param mixed $old_value The old option value.
314
+ */
315
+ public function on_option_theme_mods_update( $value, $old_value ) {
316
+
317
+ $current_data = $this->get_custom_customizer_option();
318
+ $theme_slug = get_option( 'template' );
319
+
320
+ $data = [
321
+ 'template' => $theme_slug,
322
+ 'mods' => isset( $current_data['mods'] ) ? $current_data['mods'] : [],
323
+ 'options' => isset( $current_data['options'] ) ? $current_data['options'] : [],
324
+ ];
325
+
326
+ if ( is_array( $value ) && ! empty( $value ) ) {
327
+ foreach ( $value as $key => $val ) {
328
+ $data['mods'][ $key ] = $val;
329
+ }
330
+ }
331
+ $this->update_custom_customizer_option( $data );
332
+
333
+ if ( $this->current_lang_not_default() ) {
334
+ return $old_value;
335
+ }
336
+
337
+ return $value;
338
+ }
339
+
340
+ /**
341
+ * If Polylang activated, set the preview url and add select language control
342
+ *
343
+ * @author soderlind
344
+ * @version 1.0.0
345
+ * @link https://gist.github.com/soderlind/1908634f5eb0c1f69428666dd2a291d0
346
+ *
347
+ * @since 1.0.0
348
+ */
349
+ public static function add_lang_to_customizer_previewer() {
350
+ $languages = self::get_languages_list();
351
+
352
+ if ( ! $languages ) {
353
+ return;
354
+ }
355
+
356
+ $handle = 'dss-add-lang-to-template';
357
+ $js_path_url = trailingslashit( apply_filters( 'scp_js_path_url', get_stylesheet_directory_uri() . '/js/' ) );
358
+ $src = $js_path_url . 'customizer-multilingual.js';
359
+ $deps = [ 'customize-controls' ];
360
+ wp_enqueue_script( $handle, $src, $deps, JUPITERX_VERSION, true );
361
+ $language = ( empty( $_REQUEST['lang'] ) ) ? self::get_language() : $_REQUEST['lang']; // @phpcs:ignore
362
+
363
+ if ( empty( $language ) ) {
364
+ $language = self::default_language();
365
+ }
366
+
367
+ $url = add_query_arg( 'lang', $language, self::get_home_url( $language ) );
368
+
369
+ wp_add_inline_script(
370
+ $handle,
371
+ sprintf(
372
+ 'JupiterXCustomizerMultilingual.init( %s );', wp_json_encode(
373
+ [
374
+ 'url' => $url,
375
+ 'languages' => $languages,
376
+ 'current_language' => $language,
377
+ 'switcher_text' => __( 'Language:', 'jupiterx-core' ),
378
+ ]
379
+ )
380
+ ), 'after'
381
+ );
382
+ }
383
+
384
+ /**
385
+ * Append lang="contrycode" to the customizer url in the adminbar
386
+ *
387
+ * @since 1.0.0
388
+ *
389
+ * @return void
390
+ */
391
+ public static function on_wp_before_admin_bar_render() {
392
+ global $wp_admin_bar;
393
+ $customize_node = $wp_admin_bar->get_node( 'customize' );
394
+ if ( ! empty( $customize_node ) ) {
395
+ $customize_node->href = add_query_arg( 'lang', self::get_language(), $customize_node->href );
396
+ $wp_admin_bar->add_node( $customize_node );
397
+ }
398
+ }
399
+
400
+ /**
401
+ * Append lang="contrycode" to the customizer url in the Admin->Apperance->Customize menu
402
+ *
403
+ * @since 1.0.0
404
+ *
405
+ * @return void
406
+ */
407
+ public static function on_admin_menu() {
408
+ global $menu, $submenu;
409
+ $parent = 'themes.php';
410
+ if ( ! isset( $submenu[ $parent ] ) ) {
411
+ return;
412
+ }
413
+ foreach ( $submenu[ $parent ] as $k => $d ) {
414
+ if ( 'customize' === $d['1'] ) {
415
+ $submenu[ $parent ][ $k ]['2'] = add_query_arg( 'lang', self::get_language(), $submenu[ $parent ][ $k ]['2'] ); // @phpcs:ignore
416
+ break;
417
+ }
418
+ }
419
+ }
420
+
421
+ }
422
+
423
+ CoreCustomizerMultilingual::init();
424
+
425
+ if ( class_exists( 'WP_Customize_Setting' ) ) {
426
+ /**
427
+ * A class that extends WP_Customize_Setting so we can access
428
+ * the protected updated method when importing options.
429
+ *
430
+ * @since 0.3
431
+ */
432
+ final class Customizermultilingialoption extends WP_Customize_Setting { // @phpcs:ignore
433
+
434
+
435
+ /**
436
+ * Import an option value for this setting.
437
+ *
438
+ * @since 0.3
439
+ *
440
+ * @param mixed $value The option value.
441
+ *
442
+ * @return void
443
+ */
444
+ public function import( $value ) {
445
+ $this->update( $value );
446
+ }
447
+ }
448
+ }
includes/customizer/api/classes/class-status.php CHANGED
@@ -1,64 +1,64 @@
1
- <?php
2
- /**
3
- * This class handles status debug utils for customizer.
4
- *
5
- * @package JupiterX\Framework\API\Customizer
6
- *
7
- * @since 1.0.0
8
- */
9
-
10
- // Exit if accessed directly.
11
- if ( ! defined( 'ABSPATH' ) ) {
12
- exit;
13
- }
14
-
15
- /**
16
- * Run status debug.
17
- *
18
- * @since 1.0.0
19
- * @ignore
20
- * @access private
21
- *
22
- * @package JupiterX\Framework\API\Customizer
23
- */
24
- final class JupiterX_Customizer_Status {
25
-
26
- /**
27
- * Construct the class.
28
- *
29
- * @since 1.0.0
30
- */
31
- public function __construct() {
32
- add_action( 'init', [ $this, 'render_settings_table' ] );
33
- }
34
-
35
- /**
36
- * Construct the class.
37
- *
38
- * @since 1.0.0
39
- */
40
- public function render_settings_table() {
41
- $exclude_type = [ 'jupiterx-divider', 'jupiterx-label', 'jupiterx-child-popup' ];
42
-
43
- ?>
44
- <table class="table">
45
- <thead>
46
- <tr>
47
- <th><?php esc_html_e( 'Customizer Settings Name', 'jupiterx-core' ); ?></th>
48
- <th><?php esc_html_e( 'Control Type', 'jupiterx-core' ); ?></th>
49
- </tr>
50
- </thead>
51
- <tbody>
52
- <?php foreach ( JupiterX_Customizer::$settings as $id => $setting ) : ?>
53
- <?php if ( ! in_array( $setting['type'], $exclude_type, true ) ) : ?>
54
- <tr>
55
- <td><?php echo esc_html( $id ); // @codingStandardsIgnoreLine ?></td>
56
- <td><?php echo esc_html( $setting['type'] ); // @codingStandardsIgnoreLine ?></td>
57
- </tr>
58
- <?php endif; ?>
59
- <?php endforeach; ?>
60
- </tbody>
61
- </table>
62
- <?php
63
- }
64
- }
1
+ <?php
2
+ /**
3
+ * This class handles status debug utils for customizer.
4
+ *
5
+ * @package JupiterX\Framework\API\Customizer
6
+ *
7
+ * @since 1.0.0
8
+ */
9
+
10
+ // Exit if accessed directly.
11
+ if ( ! defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+
15
+ /**
16
+ * Run status debug.
17
+ *
18
+ * @since 1.0.0
19
+ * @ignore
20
+ * @access private
21
+ *
22
+ * @package JupiterX\Framework\API\Customizer
23
+ */
24
+ final class JupiterX_Customizer_Status {
25
+
26
+ /**
27
+ * Construct the class.
28
+ *
29
+ * @since 1.0.0
30
+ */
31
+ public function __construct() {
32
+ add_action( 'init', [ $this, 'render_settings_table' ] );
33
+ }
34
+
35
+ /**
36
+ * Construct the class.
37
+ *
38
+ * @since 1.0.0
39
+ */
40
+ public function render_settings_table() {
41
+ $exclude_type = [ 'jupiterx-divider', 'jupiterx-label', 'jupiterx-child-popup' ];
42
+
43
+ ?>
44
+ <table class="table">
45
+ <thead>
46
+ <tr>
47
+ <th><?php esc_html_e( 'Customizer Settings Name', 'jupiterx-core' ); ?></th>
48
+ <th><?php esc_html_e( 'Control Type', 'jupiterx-core' ); ?></th>
49
+ </tr>
50
+ </thead>
51
+ <tbody>
52
+ <?php foreach ( JupiterX_Customizer::$settings as $id => $setting ) : ?>
53
+ <?php if ( ! in_array( $setting['type'], $exclude_type, true ) ) : ?>
54
+ <tr>
55
+ <td><?php echo esc_html( $id ); // @codingStandardsIgnoreLine ?></td>
56
+ <td><?php echo esc_html( $setting['type'] ); // @codingStandardsIgnoreLine ?></td>
57
+ </tr>
58
+ <?php endif; ?>
59
+ <?php endforeach; ?>
60
+ </tbody>
61
+ </table>
62
+ <?php
63
+ }
64
+ }
includes/customizer/api/customizer.php CHANGED
@@ -1,295 +1,295 @@
1
- <?php
2
- /**
3
- * This class handles API for customizer.
4
- *
5
- * @package JupiterX\Framework\API\Customizer
6
- *
7
- * @since 1.0.0
8
- */
9
-
10
- // Exit if accessed directly.
11
- if ( ! defined( 'ABSPATH' ) ) {
12
- exit;
13
- }
14
-
15
- if ( ! class_exists( 'JupiterX_Customizer' ) ) {
16
- /**
17
- * Customizer wrapper class.
18
- *
19
- * @since 1.0.0
20
- * @ignore
21
- * @access private
22
- *
23
- * @package JupiterX\Framework\API\Customizer
24
- */
25
- final class JupiterX_Customizer {
26
-
27
- /**
28
- * Registered panels.
29
- *
30
- * @since 1.0.0
31
- *
32
- * @var array
33
- */
34
- public static $panels = [];
35
-
36
- /**
37
- * Registered sections.
38
- *
39
- * @since 1.0.0
40
- *
41
- * @var array
42
- */
43
- public static $sections = [];
44
-
45
- /**
46
- * Registered settings.
47
- *
48
- * @since 1.0.0
49
- *
50
- * @var array
51
- */
52
- public static $settings = [];
53
-
54
- /**
55
- * Configuration ID.
56
- *
57
- * Defined for Kirki.
58
- *
59
- * @since 1.0.0
60
- *
61
- * @var string
62
- */
63
- public static $config_id = 'jupiterx';
64
-
65
- /**
66
- * Section types.
67
- *
68
- * @since 1.0.0
69
- *
70
- * @var array
71
- */
72
- public static $section_types = [
73
- 'kirki-popup' => 'JupiterX_Customizer_Section_Popup',
74
- 'kirki-pane' => 'JupiterX_Customizer_Section_Pane',
75
- 'kirki-jupiterx-link' => 'JupiterX_Customizer_Section_Link',
76
- ];
77
-
78
- /**
79
- * Control types.
80
- *
81
- * @since 1.0.0
82
- *
83
- * @var array
84
- */
85
- public static $control_types = [
86
- 'jupiterx-input' => 'JupiterX_Customizer_Control_Input',
87
- 'jupiterx-text' => 'JupiterX_Customizer_Control_Text',
88
- 'jupiterx-textarea' => 'JupiterX_Customizer_Control_Textarea',
89
- 'jupiterx-select' => 'JupiterX_Customizer_Control_Select',
90
- 'jupiterx-toggle' => 'JupiterX_Customizer_Control_Toggle',
91
- 'jupiterx-choose' => 'JupiterX_Customizer_Control_Choose',
92
- 'jupiterx-multicheck' => 'JupiterX_Customizer_Control_Multicheck',
93
- 'jupiterx-divider' => 'JupiterX_Customizer_Control_Divider',
94
- 'jupiterx-label' => 'JupiterX_Customizer_Control_Label',
95
- 'jupiterx-alert' => 'JupiterX_Customizer_Control_Alert',
96
- 'jupiterx-position' => 'JupiterX_Customizer_Control_Position',
97
- 'jupiterx-color' => 'JupiterX_Customizer_Control_Color',
98
- 'jupiterx-image' => 'JupiterX_Customizer_Control_Image',
99
- 'jupiterx-radio-image' => 'JupiterX_Customizer_Control_Radio_Image',
100
- 'jupiterx-child-popup' => 'JupiterX_Customizer_Control_Child_Popup',
101
- 'jupiterx-popup' => 'JupiterX_Customizer_Control_Popup',
102
- 'jupiterx-box-model' => 'JupiterX_Customizer_Control_Box_Model',
103
- 'jupiterx-fonts' => 'JupiterX_Customizer_Control_Fonts',
104
- 'jupiterx-font' => 'JupiterX_Customizer_Control_Font',
105
- 'jupiterx-exceptions' => 'JupiterX_Customizer_Control_Exceptions',
106
- 'jupiterx-template' => 'JupiterX_Customizer_Control_Template',
107
- 'jupiterx-pro-box' => 'JupiterX_Customizer_Control_PRO_Box',
108
- ];
109
-
110
- /**
111
- * Group control types.
112
- *
113
- * @since 1.0.0
114
- *
115
- * @var array
116
- */
117
- public static $group_control_types = [
118
- 'jupiterx-background' => 'JupiterX_Customizer_Group_Control_Background',
119
- 'jupiterx-box-shadow' => 'JupiterX_Customizer_Group_Control_Box_Shadow',
120
- 'jupiterx-typography' => 'JupiterX_Customizer_Group_Control_Typography',
121
- 'jupiterx-border' => 'JupiterX_Customizer_Group_Control_Border',
122
- ];
123
-
124
- /**
125
- * Responsive devices media query.
126
- *
127
- * @since 1.0.0
128
- *
129
- * @var array
130
- */
131
- public static $responsive_devices = [
132
- 'desktop' => 'global',
133
- 'tablet' => '@media (max-width: 767.98px)',
134
- 'mobile' => '@media (max-width: 575.98px)',
135
- ];
136
-
137
- /**
138
- * Store panel.
139
- *
140
- * @since 1.0.0
141
- *
142
- * @param string $id ID of the panel.
143
- * @param array $args Arguments of the panel.
144
- */
145
- public static function add_panel( $id = '', $args = [] ) {
146
- if ( empty( $id ) ) {
147
- return;
148
- }
149
-
150
- /**
151
- * Run action before section added.
152
- *
153
- * @since 1.3.0
154
- */
155
- do_action( "{$id}_before_panel" );
156
-
157
- // Add panel to stack.
158
- self::$panels[ $id ] = $args;
159
-
160
- /**
161
- * Run action after panel added.
162
- *
163
- * @since 1.3.0
164
- */
165
- do_action( "{$id}_after_panel" );
166
- }
167
-
168
- /**
169
- * Store section.
170
- *
171
- * @since 1.0.0
172
- *
173
- * @param string $id ID of the section.
174
- * @param array $args Arguments of the section.
175
- */
176
- public static function add_section( $id = '', $args = [] ) {
177
- if ( empty( $id ) ) {
178
- return;
179
- }
180
-
181
- /**
182
- * Run action before section added.
183
- *
184
- * @since 1.3.0
185
- */
186
- do_action( "{$id}_before_section" );
187
-
188
- /**
189
- * Add section to stack.
190
- */
191
- self::$sections[ $id ] = array_merge( [ 'priority' => 160 ], $args );
192
-
193
- /**
194
- * Run action after section added.
195
- *
196
- * @since 1.3.0
197
- */
198
- do_action( "{$id}_after_section" );
199
- }
200
-
201
- /**
202
- * Update section.
203
- *
204
- * @since 1.3.0
205
- *
206
- * @param string $id Section ID.
207
- * @param array $args Section arguments.
208
- */
209
- public static function update_section( $id, $args = [] ) {
210
- if ( ! isset( self::$sections[ $id ] ) ) {
211
- return;
212
- }
213
-
214
- $section = self::$sections[ $id ];
215
-
216
- self::$sections[ $id ] = array_merge( $section, $args );
217
- }
218
-
219
- /**
220
- * Store settings.
221
- *
222
- * @since 1.0.0
223
- *
224
- * @param array $args Arguments of the field.
225
- */
226
- public static function add_field( $args = [] ) {
227
- if ( ! isset( $args['type'] ) && ! isset( $args['settings'] ) ) {
228
- return;
229
- }
230
-
231
- /**
232
- * Run action before field added.
233
- *
234
- * @since 1.3.0
235
- */
236
- do_action( "{$args['settings']}_before_field" );
237
-
238
- /**
239
- * Add the field to stack.
240
- */
241
- self::$settings[ $args['settings'] ] = $args;
242
-
243
- /**
244
- * Run action after field added.
245
- *
246
- * @since 1.3.0
247
- */
248
- do_action( "{$args['settings']}_after_field" );
249
- }
250
-
251
- /**
252
- * Add responsive field.
253
- *
254
- * @since 1.0.0
255
- *
256
- * @param array $args Arguments of the field.
257
- */
258
- public static function add_responsive_field( $args = [] ) {
259
- $args['responsive'] = true;
260
-
261
- self::add_field( $args );
262
- }
263
-
264
- /**
265
- * Update field.
266
- *
267
- * @since 1.3.0
268
- *
269
- * @param string $id Field ID.
270
- * @param array $args Field arguments.
271
- */
272
- public static function update_field( $id, $args = [] ) {
273
- if ( ! isset( self::$settings[ $id ] ) ) {
274
- return;
275
- }
276
-
277
- $settings = self::$settings[ $id ];
278
-
279
- self::$settings[ $id ] = array_merge( $settings, $args );
280
- }
281
-
282
- /**
283
- * Remove field.
284
- *
285
- * @since 1.3.0
286
- *
287
- * @param string $id ID of the field.
288
- */
289
- public static function remove_field( $id ) {
290
- if ( isset( self::$settings[ $id ] ) ) {
291
- unset( self::$settings[ $id ] );
292
- }
293
- }
294
- }
295
- }
1
+ <?php
2
+ /**
3
+ * This class handles API for customizer.
4
+ *
5
+ * @package JupiterX\Framework\API\Customizer
6
+ *
7
+ * @since 1.0.0
8
+ */
9
+
10
+ // Exit if accessed directly.
11
+ if ( ! defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+
15
+ if ( ! class_exists( 'JupiterX_Customizer' ) ) {
16
+ /**
17
+ * Customizer wrapper class.
18
+ *
19
+ * @since 1.0.0
20
+ * @ignore
21
+ * @access private
22
+ *
23
+ * @package JupiterX\Framework\API\Customizer
24
+ */
25
+ final class JupiterX_Customizer {
26
+
27
+ /**
28
+ * Registered panels.
29
+ *
30
+ * @since 1.0.0
31
+ *
32
+ * @var array
33
+ */
34
+ public static $panels = [];
35
+
36
+ /**
37
+ * Registered sections.
38
+ *
39
+ * @since 1.0.0
40
+ *
41
+ * @var array
42
+ */
43
+ public static $sections = [];
44
+
45
+ /**
46
+ * Registered settings.
47
+ *
48
+ * @since 1.0.0
49
+ *
50
+ * @var array
51
+ */
52
+ public static $settings = [];
53
+
54
+ /**
55
+ * Configuration ID.
56
+ *
57
+ * Defined for Kirki.
58
+ *
59
+ * @since 1.0.0
60
+ *
61
+ * @var string
62
+ */
63
+ public static $config_id = 'jupiterx';
64
+
65
+ /**
66
+ * Section types.
67
+ *
68
+ * @since 1.0.0
69
+ *
70
+ * @var array
71
+ */
72
+ public static $section_types = [
73
+ 'kirki-popup' => 'JupiterX_Customizer_Section_Popup',
74
+ 'kirki-pane' => 'JupiterX_Customizer_Section_Pane',
75
+ 'kirki-jupiterx-link' => 'JupiterX_Customizer_Section_Link',
76
+ ];
77
+
78
+ /**
79
+ * Control types.
80
+ *
81
+ * @since 1.0.0
82
+ *
83
+ * @var array
84
+ */
85
+ public static $control_types = [
86
+ 'jupiterx-input' => 'JupiterX_Customizer_Control_Input',
87
+ 'jupiterx-text' => 'JupiterX_Customizer_Control_Text',
88
+ 'jupiterx-textarea' => 'JupiterX_Customizer_Control_Textarea',
89
+ 'jupiterx-select' => 'JupiterX_Customizer_Control_Select',
90
+ 'jupiterx-toggle' => 'JupiterX_Customizer_Control_Toggle',
91
+ 'jupiterx-choose' => 'JupiterX_Customizer_Control_Choose',
92
+ 'jupiterx-multicheck' => 'JupiterX_Customizer_Control_Multicheck',
93
+ 'jupiterx-divider' => 'JupiterX_Customizer_Control_Divider',
94
+ 'jupiterx-label' => 'JupiterX_Customizer_Control_Label',
95
+ 'jupiterx-alert' => 'JupiterX_Customizer_Control_Alert',
96
+ 'jupiterx-position' => 'JupiterX_Customizer_Control_Position',
97
+ 'jupiterx-color' => 'JupiterX_Customizer_Control_Color',
98
+ 'jupiterx-image' => 'JupiterX_Customizer_Control_Image',
99
+ 'jupiterx-radio-image' => 'JupiterX_Customizer_Control_Radio_Image',
100
+ 'jupiterx-child-popup' => 'JupiterX_Customizer_Control_Child_Popup',
101
+ 'jupiterx-popup' => 'JupiterX_Customizer_Control_Popup',
102
+ 'jupiterx-box-model' => 'JupiterX_Customizer_Control_Box_Model',
103
+ 'jupiterx-fonts' => 'JupiterX_Customizer_Control_Fonts',
104
+ 'jupiterx-font' => 'JupiterX_Customizer_Control_Font',
105
+ 'jupiterx-exceptions' => 'JupiterX_Customizer_Control_Exceptions',
106
+ 'jupiterx-template' => 'JupiterX_Customizer_Control_Template',
107
+ 'jupiterx-pro-box' => 'JupiterX_Customizer_Control_PRO_Box',
108
+ ];
109
+
110
+ /**
111
+ * Group control types.
112
+ *
113
+ * @since 1.0.0
114
+ *
115
+ * @var array
116
+ */
117
+ public static $group_control_types = [
118
+ 'jupiterx-background' => 'JupiterX_Customizer_Group_Control_Background',
119
+ 'jupiterx-box-shadow' => 'JupiterX_Customizer_Group_Control_Box_Shadow',
120
+ 'jupiterx-typography' => 'JupiterX_Customizer_Group_Control_Typography',
121
+ 'jupiterx-border' => 'JupiterX_Customizer_Group_Control_Border',
122
+ ];
123
+
124
+ /**
125
+ * Responsive devices media query.
126
+ *
127
+ * @since 1.0.0
128
+ *
129
+ * @var array
130
+ */
131
+ public static $responsive_devices = [
132
+ 'desktop' => 'global',
133
+ 'tablet' => '@media (max-width: 767.98px)',
134
+ 'mobile' => '@media (max-width: 575.98px)',
135
+ ];
136
+
137
+ /**
138
+ * Store panel.
139
+ *
140
+ * @since 1.0.0
141
+ *
142
+ * @param string $id ID of the panel.
143
+ * @param array $args Arguments of the panel.
144
+ */
145
+ public static function add_panel( $id = '', $args = [] ) {
146
+ if ( empty( $id ) ) {
147
+ return;
148
+ }
149
+
150
+ /**
151
+ * Run action before section added.
152
+ *
153
+ * @since 1.3.0
154
+ */
155
+ do_action( "{$id}_before_panel" );
156
+
157
+ // Add panel to stack.
158
+ self::$panels[ $id ] = $args;
159
+
160
+ /**
161
+ * Run action after panel added.
162
+ *
163
+ * @since 1.3.0
164
+ */
165
+ do_action( "{$id}_after_panel" );
166
+ }
167
+
168
+ /**
169
+ * Store section.
170
+ *
171
+ * @since 1.0.0
172
+ *
173
+ * @param string $id ID of the section.
174
+ * @param array $args Arguments of the section.
175
+ */
176
+ public static function add_section( $id = '', $args = [] ) {
177
+ if ( empty( $id ) ) {
178
+ return;
179
+ }
180
+
181
+ /**
182
+ * Run action before section added.
183
+ *
184
+ * @since 1.3.0
185
+ */
186
+ do_action( "{$id}_before_section" );
187
+
188
+ /**
189
+ * Add section to stack.
190
+ */
191
+ self::$sections[ $id ] = array_merge( [ 'priority' => 160 ], $args );
192
+
193
+ /**
194
+ * Run action after section added.
195
+ *
196
+ * @since 1.3.0
197
+ */
198
+ do_action( "{$id}_after_section" );
199
+ }
200
+
201
+ /**
202
+ * Update section.
203
+ *
204
+ * @since 1.3.0
205
+ *
206
+ * @param string $id Section ID.
207
+ * @param array $args Section arguments.
208
+ */
209
+ public static function update_section( $id, $args = [] ) {
210
+ if ( ! isset( self::$sections[ $id ] ) ) {
211
+ return;
212
+ }
213
+
214
+ $section = self::$sections[ $id ];
215
+
216
+ self::$sections[ $id ] = array_merge( $section, $args );
217
+ }
218
+
219
+ /**
220
+ * Store settings.
221
+ *
222
+ * @since 1.0.0
223
+ *
224
+ * @param array $args Arguments of the field.
225
+ */
226
+ public static function add_field( $args = [] ) {
227
+ if ( ! isset( $args['type'] ) && ! isset( $args['settings'] ) ) {
228
+ return;
229
+ }
230
+
231
+ /**
232
+ * Run action before field added.
233
+ *
234
+ * @since 1.3.0
235
+ */
236
+ do_action( "{$args['settings']}_before_field" );
237
+
238
+ /**
239
+ * Add the field to stack.
240
+ */
241
+ self::$settings[ $args['settings'] ] = $args;
242
+
243
+ /**
244
+ * Run action after field added.
245
+ *
246
+ * @since 1.3.0
247
+ */
248
+ do_action( "{$args['settings']}_after_field" );
249
+ }
250
+
251
+ /**
252
+ * Add responsive field.
253
+ *
254
+ * @since 1.0.0
255
+ *
256
+ * @param array $args Arguments of the field.
257
+ */
258
+ public static function add_responsive_field( $args = [] ) {
259
+ $args['responsive'] = true;
260
+
261
+ self::add_field( $args );
262
+ }
263
+
264
+ /**
265
+ * Update field.
266
+ *
267
+ * @since 1.3.0
268
+ *
269
+ * @param string $id Field ID.
270
+ * @param array $args Field arguments.
271
+ */
272
+ public static function update_field( $id, $args = [] ) {
273
+ if ( ! isset( self::$settings[ $id ] ) ) {
274
+ return;
275
+ }
276
+
277
+ $settings = self::$settings[ $id ];
278
+
279
+ self::$settings[ $id ] = array_merge( $settings, $args );
280
+ }
281
+
282
+ /**
283
+ * Remove field.
284
+ *
285
+ * @since 1.3.0
286
+ *
287
+ * @param string $id ID of the field.
288
+ */
289
+ public static function remove_field( $id ) {
290
+ if ( isset( self::$settings[ $id ] ) ) {
291
+ unset( self::$settings[ $id ] );
292
+ }
293
+ }
294
+ }
295
+ }
includes/customizer/api/includes/base/class-control.php CHANGED
@@ -1,243 +1,243 @@
1
- <?php
2
- /**
3
- * This class is a common class for all the controls.
4
- *
5
- * @package JupiterX\Framework\API\Customizer
6
- *
7
- * @since 1.0.0
8
- */
9
-
10
- // Exit if accessed directly.
11
- if ( ! defined( 'ABSPATH' ) ) {
12
- exit;
13
- }
14
-
15
- /**
16
- * Control base class.
17
- *
18
- * This is a special section for rendering tabs and child popups controls inside the Popup section.
19
- *
20
- * @since 1.0.0
21
- * @ignore
22
- * @access private
23
- *
24
- * @package JupiterX\Framework\API\Customizer
25
- */
26
- class JupiterX_Customizer_Base_Control extends WP_Customize_Control {
27
-
28
- /**
29
- * Column size of the control.
30
- *
31
- * @since 1.0.0
32
- *
33
- * @var string
34
- */
35
- public $column = '12';
36
-
37
- /**
38
- * Empty label.
39
- *
40
- * @since 1.0.0
41
- *
42
- * @var string
43
- */
44
- public $label_empty = false;
45
-
46
- /**
47
- * Used to automatically generate all CSS output.
48
- *
49
- * @since 1.0.0
50
- *
51
- * @var array
52
- */
53
- public $output = [];
54
-
55
- /**
56
- * Whitelisting the "required" argument.
57
- *
58
- * @since 1.0.0
59
- *
60
- * @var array
61
- */
62
- public $required = [];
63
-
64
- /**
65
- * Attributes for the control wrapper.
66
- *
67
- * @since 1.0.0
68
- *
69
- * @var array
70
- */
71
- public $control_attrs = [];
72
-
73
- /**
74
- * Responsive control.
75
- *
76
- * @since 1.0.0
77
- *
78
- * @var array
79
- */
80
- public $responsive = false;
81
-
82
- /**
83
- * Refresh the parameters passed to the JavaScript via JSON.
84
- *
85
- * @since 1.0.0
86
- */
87
- public function to_json() {
88
- parent::to_json();
89
-
90
- $this->json['default'] = $this->setting->default;
91
-
92
- if ( isset( $this->default ) ) {
93
- $this->json['default'] = $this->default;
94
- }
95
-
96
- // Add this to whitelist `active_callback` from Kirki.
97
- $this->json['required'] = $this->required;
98
-
99
- // Output.
100
- $this->json['output'] = $this->output;
101
-
102
- // Responsive.
103
- $this->json['responsive'] = (bool) $this->responsive;
104
-
105
- // Value.
106
- $this->json['value'] = $this->value();
107
-
108
- // Choices.
109
- $this->json['choices'] = $this->choices;
110
-
111
- // The link.
112
- $this->json['link'] = $this->get_link();
113
-
114
- // The ID.
115
- $this->json['id'] = $this->id;
116
-
117
- // Empty label.
118
- $this->json['labelEmpty'] = $this->label_empty;
119
-
120
- // Input attributes.
121
- $this->json['inputAttrs'] = '';
122
-
123
- foreach ( $this->input_attrs as $attr => $value ) {
124
- $this->json['inputAttrs'] .= $attr . '="' . esc_attr( $value ) . '" ';
125
- }
126
-
127
- // Control wrapper attributes.
128
- $this->json['controlAttrs'] = '';
129
-
130
- foreach ( $this->control_attrs as $attr => $value ) {
131
- $this->json['controlAttrs'] .= $attr . '="' . esc_attr( $value ) . '" ';
132
- }
133
- }
134
-
135
- /**
136
- * Renders the control wrapper and calls $this->render_content() for the internals.
137
- *
138
- * @since 1.0.0
139
- */
140
- protected function render() {
141
- $id = 'customize-control-' . str_replace( [ '[', ']' ], [ '-', '' ], $this->id );
142
- $class = sprintf( 'jupiterx-col-%1$s customize-control customize-control-%2$s', $this->column, $this->type );
143
-
144
- if ( $this->responsive ) {
145
- $class .= ' customize-control-responsive';
146
- }
147
-
148
- printf( '<li id="%s" class="%s">', esc_attr( $id ), esc_attr( $class ) );
149
- $this->render_content();
150
- echo '</li>';
151
- }
152
-
153
- /**
154
- * Render the control's content.
155
- *
156
- * Allows the content to be overridden without having to rewrite the wrapper in `$this::render()`.
157
- *
158
- * Supports basic input types `text`, `checkbox`, `textarea`, `radio`, `select` and `dropdown-pages`.
159
- * Additional input types such as `email`, `url`, `number`, `hidden` and `date` are supported implicitly.
160
- *
161
- * Control content can alternately be rendered in JS. See WP_Customize_Control::print_template().
162
- *
163
- * @since 1.0.0
164
- */
165
- protected function render_content() {}
166
-
167
- /**
168
- * An Underscore (JS) template for this control's content (but not its container).
169
- *
170
- * Class variables for this control class are available in the `data` JS object;
171
- * export custom variables by overriding {@see WP_Customize_Control::to_json()}.
172
- *
173
- * @see WP_Customize_Control::print_template()
174
- *
175
- * Preferably not to overwrite this function, use the `control_template` function
176
- * to render the control template. Overwriting this only allows for some special
177
- * occasion or if your control doesn't need to render the label and description.
178
- *
179
- * @since 1.0.0
180
- */
181
- protected function content_template() {
182
- ?>
183
- <# viewport = [ 'global' ] #>
184
- <# if ( data.responsive ) { #>
185
- <#
186
- viewport = [ 'desktop', 'tablet', 'mobile' ]
187
- responsive = {
188
- link: data.link,
189
- id: data.id,
190
- property: ! _.isUndefined( data.property ) ? data.property : '',
191
- value: data.value || {}
192
- }
193
- #>
194
- <div class="jupiterx-responsive-control">
195
- <# } #>
196
- <# if ( data.label ) { #>
197
- <span class="customize-control-title">{{ data.label }}</span>
198
- <# } else if ( data.labelEmpty ) { #>
199
- <span class="customize-control-title">&nbsp;</span>
200
- <# } #>
201
- <# if ( data.description ) { #>
202
- <span class="description customize-control-description">{{ data.description }}</span>
203
- <# } #>
204
- <# _.each( viewport, function ( device ) { #>
205
- <# if ( data.responsive ) { #>
206
- <#
207
- data.link = responsive.link + ' data-setting-viewport-link="' + device + '"'
208
- data.id = responsive.id + '_' + device
209
- data.value = responsive.value[device] || ''
210
- #>
211
- <div class="jupiterx-viewport jupiterx-viewport-{{ device }}">
212
- <# } #>
213
- <?php $this->control_template(); ?>
214
- <# if ( data.responsive ) { #>
215
- </div>
216
- <# } #>
217
- <# } ) #>
218
- <# if ( data.responsive ) { #>
219
- <div class="jupiterx-responsive-switcher">
220
- <div class="jupiterx-responsive-switcher-buttons">
221
- <# _.each( viewport, function ( device ) { #>
222
- <a class="jupiterx-responsive-switcher-button jupiterx-responsive-switcher-{{ device }}" data-device="{{ device }}">
223
- <span class="jupiterx-responsive-switcher-icon">
224
- <img src="<?php echo esc_url( JupiterX_Customizer_Utils::get_assets_url() ); ?>/img/device-{{ device }}.svg">
225
- </span>
226
- </a>
227
- <# } ) #>
228
- </div>
229
- </div>
230
- </div>
231
- <# } #>
232
- <?php
233
- }
234
-
235
- /**
236
- * An Underscore (JS) template for control wrapper.
237
- *
238
- * Use to create the control template.
239
- *
240
- * @since 1.0.0
241
- */
242
- protected function control_template() {}
243
- }
1
+ <?php
2
+ /**
3
+ * This class is a common class for all the controls.
4
+ *
5
+ * @package JupiterX\Framework\API\Customizer
6
+ *
7
+ * @since 1.0.0
8
+ */
9
+
10
+ // Exit if accessed directly.
11
+ if ( ! defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+
15
+ /**
16
+ * Control base class.
17
+ *
18
+ * This is a special section for rendering tabs and child popups controls inside the Popup section.
19
+ *
20
+ * @since 1.0.0
21
+ * @ignore
22
+ * @access private
23
+ *
24
+ * @package JupiterX\Framework\API\Customizer
25
+ */
26
+ class JupiterX_Customizer_Base_Control extends WP_Customize_Control {
27
+
28
+ /**
29
+ * Column size of the control.
30
+ *
31
+ * @since 1.0.0
32
+ *
33
+ * @var string
34
+ */
35
+ public $column = '12';
36
+
37
+ /**
38
+ * Empty label.
39
+ *
40
+ * @since 1.0.0
41
+ *
42
+ * @var string
43
+ */
44
+ public $label_empty = false;
45
+
46
+ /**
47
+ * Used to automatically generate all CSS output.
48
+ *
49
+ * @since 1.0.0
50
+ *
51
+ * @var array
52
+ */
53
+ public $output = [];
54
+
55
+ /**
56
+ * Whitelisting the "required" argument.
57
+ *
58
+ * @since 1.0.0
59
+ *
60
+ * @var array
61
+ */
62
+ public $required = [];
63
+
64
+ /**
65
+ * Attributes for the control wrapper.
66
+ *
67
+ * @since 1.0.0
68
+ *
69
+ * @var array
70
+ */
71
+ public $control_attrs = [];
72
+
73
+ /**
74
+ * Responsive control.
75
+ *
76
+ * @since 1.0.0
77
+ *
78
+ * @var array
79
+ */
80
+ public $responsive = false;
81
+
82
+ /**
83
+ * Refresh the parameters passed to the JavaScript via JSON.
84
+ *
85
+ * @since 1.0.0
86
+ */
87
+ public function to_json() {
88
+ parent::to_json();
89
+
90
+ $this->json['default'] = $this->setting->default;
91
+
92
+ if ( isset( $this->default ) ) {
93
+ $this->json['default'] = $this->default;
94
+ }
95
+
96
+ // Add this to whitelist `active_callback` from Kirki.
97
+ $this->json['required'] = $this->required;
98
+
99
+ // Output.
100
+ $this->json['output'] = $this->output;
101
+
102
+ // Responsive.
103
+ $this->json['responsive'] = (bool) $this->responsive;
104
+
105
+ // Value.
106
+ $this->json['value'] = $this->value();
107
+
108
+ // Choices.
109
+ $this->json['choices'] = $this->choices;
110
+
111
+ // The link.
112
+ $this->json['link'] = $this->get_link();
113
+
114
+ // The ID.
115
+ $this->json['id'] = $this->id;
116
+
117
+ // Empty label.
118
+ $this->json['labelEmpty'] = $this->label_empty;
119
+
120
+ // Input attributes.
121
+ $this->json['inputAttrs'] = '';
122
+
123
+ foreach ( $this->input_attrs as $attr => $value ) {
124
+ $this->json['inputAttrs'] .= $attr . '="' . esc_attr( $value ) . '" ';
125
+ }
126
+
127
+ // Control wrapper attributes.
128
+ $this->json['controlAttrs'] = '';
129
+
130
+ foreach ( $this->control_attrs as $attr => $value ) {
131
+ $this->json['controlAttrs'] .= $attr . '="' . esc_attr( $value ) . '" ';
132
+ }
133
+ }
134
+
135
+ /**
136
+ * Renders the control wrapper and calls $this->render_content() for the internals.
137
+ *
138
+ * @since 1.0.0
139
+ */
140
+ protected function render() {
141
+ $id = 'customize-control-' . str_replace( [ '[', ']' ], [ '-', '' ], $this->id );
142
+ $class = sprintf( 'jupiterx-col-%1$s customize-control customize-control-%2$s', $this->column, $this->type );
143
+
144
+ if ( $this->responsive ) {
145
+ $class .= ' customize-control-responsive';
146
+ }
147
+
148
+ printf( '<li id="%s" class="%s">', esc_attr( $id ), esc_attr( $class ) );
149
+ $this->render_content();
150
+ echo '</li>';
151
+ }
152
+
153
+ /**
154
+ * Render the control's content.
155
+ *
156
+ * Allows the content to be overridden without having to rewrite the wrapper in `$this::render()`.
157
+ *
158
+ * Supports basic input types `text`, `checkbox`, `textarea`, `radio`, `select` and `dropdown-pages`.
159
+ * Additional input types such as `email`, `url`, `number`, `hidden` and `date` are supported implicitly.
160
+ *
161
+ * Control content can alternately be rendered in JS. See WP_Customize_Control::print_template().
162
+ *
163
+ * @since 1.0.0
164
+ */
165
+ protected function render_content() {}
166
+
167
+ /**
168
+ * An Underscore (JS) template for this control's content (but not its container).
169
+ *
170
+ * Class variables for this control class are available in the `data` JS object;
171
+ * export custom variables by overriding {@see WP_Customize_Control::to_json()}.
172
+ *
173
+ * @see WP_Customize_Control::print_template()
174
+ *
175
+ * Preferably not to overwrite this function, use the `control_template` function
176
+ * to render the control template. Overwriting this only allows for some special
177
+ * occasion or if your control doesn't need to render the label and description.
178
+ *
179
+ * @since 1.0.0
180
+ */
181
+ protected function content_template() {
182
+ ?>
183
+ <# viewport = [ 'global' ] #>
184
+ <# if ( data.responsive ) { #>
185
+ <#
186
+ viewport = [ 'desktop', 'tablet', 'mobile' ]
187
+ responsive = {
188
+ link: data.link,
189
+ id: data.id,
190
+ property: ! _.isUndefined( data.property ) ? data.property : '',
191
+ value: data.value || {}
192
+ }
193
+ #>
194
+ <div class="jupiterx-responsive-control">
195
+ <# } #>
196
+ <# if ( data.label ) { #>
197
+ <span class="customize-control-title">{{ data.label }}</span>
198
+ <# } else if ( data.labelEmpty ) { #>
199
+ <span class="customize-control-title">&nbsp;</span>
200
+ <# } #>
201
+ <# if ( data.description ) { #>
202
+ <span class="description customize-control-description">{{ data.description }}</span>
203
+ <# } #>
204
+ <# _.each( viewport, function ( device ) { #>
205
+ <# if ( data.responsive ) { #>
206
+ <#
207
+ data.link = responsive.link + ' data-setting-viewport-link="' + device + '"'
208
+ data.id = responsive.id + '_' + device
209
+ data.value = responsive.value[device] || ''
210
+ #>
211
+ <div class="jupiterx-viewport jupiterx-viewport-{{ device }}">
212
+ <# } #>
213
+ <?php $this->control_template(); ?>
214
+ <# if ( data.responsive ) { #>
215
+ </div>
216
+ <# } #>
217
+ <# } ) #>
218
+ <# if ( data.responsive ) { #>
219
+ <div class="jupiterx-responsive-switcher">
220
+ <div class="jupiterx-responsive-switcher-buttons">
221
+ <# _.each( viewport, function ( device ) { #>
222
+ <a class="jupiterx-responsive-switcher-button jupiterx-responsive-switcher-{{ device }}" data-device="{{ device }}">
223
+ <span class="jupiterx-responsive-switcher-icon">
224
+ <img src="<?php echo esc_url( JupiterX_Customizer_Utils::get_assets_url() ); ?>/img/device-{{ device }}.svg">
225
+ </span>
226
+ </a>
227
+ <# } ) #>
228
+ </div>
229
+ </div>
230
+ </div>
231
+ <# } #>
232
+ <?php
233
+ }
234
+
235
+ /**
236
+ * An Underscore (JS) template for control wrapper.
237
+ *
238
+ * Use to create the control template.
239
+ *
240
+ * @since 1.0.0
241
+ */
242
+ protected function control_template() {}
243
+ }
includes/customizer/api/includes/base/class-group-control.php CHANGED
@@ -1,264 +1,264 @@
1
- <?php
2
- /**
3
- * This class is a common class for all the group controls.
4
- *
5
- * @package JupiterX\Framework\API\Customizer
6
- *
7
- * @since 1.0.0
8
- */
9
-
10
- // Exit if accessed directly.
11
- if ( ! defined( 'ABSPATH' ) ) {
12
- exit;
13
- }
14
-
15
- /**
16
- * Group control base class.
17
- *
18
- * @since 1.0.0
19
- * @ignore
20
- * @access private
21
- *
22
- * @package JupiterX\Framework\API\Customizer
23
- */
24
- class JupiterX_Customizer_Base_Group_Control extends JupiterX_Customizer_Base_Control {
25
-
26
- /**
27
- * Fields for this control.
28
- *
29
- * @since 1.0.0
30
- *
31
- * @var string
32
- */
33
- public $fields = [];
34
-
35
- /**
36
- * Exclude fields for this control.
37
- *
38
- * @since 1.0.0
39
- *
40
- * @var string
41
- */
42
- public $exclude = [];
43
-
44
- /**
45
- * Include fields for this control.
46
- *
47
- * @since 1.0.0
48
- *
49
- * @var string
50
- */
51
- public $include = [];
52
-
53
- /**
54
- * Use to add, edit or remove field arguments.
55
- *
56
- * @since 1.0.4
57
- *
58
- * @var array
59
- */
60
- public $filter_fields = [];
61
-
62
- /**
63
- * Refresh the parameters passed to the JavaScript via JSON.
64
- *
65
- * @since 1.0.0
66
- */
67
- public function to_json() {
68
- parent::to_json();
69
-
70
- // Set the fields for this control.
71
- $this->set_fields();
72
-
73
- // Include fields.
74
- $this->include_fields();
75
-
76
- // Exclude fields.
77
- $this->exclude_fields();
78
-
79
- // Filter fields.
80
- $this->filter_fields();
81
-
82
- // Get fields.
83
- $this->json['fields'] = $this->get_fields();
84
- }
85
-
86
- /**
87
- * Set the fields for this control.
88
- *
89
- * @since 1.0.0
90
- */
91
- protected function set_fields() {}
92
-
93
- /**
94
- * Add field.
95
- *
96
- * @since 1.0.0
97
- *
98
- * @param string $property Property name of field.
99
- * @param array $args Field arguments.
100
- */
101
- final protected function add_field( $property, $args ) {
102
- $this->fields[ $property ] = array_merge( $args, [
103
- 'id' => sprintf( '%1$s_%2$s', $this->id, $property ),
104
- 'property' => $property,
105
- 'responsive' => $this->responsive && isset( $args['responsive'] ) ? true : false,
106
- ] );
107
- }
108
-
109
- /**
110
- * Update field.
111
- *
112
- * @since 1.0.0
113
- *
114
- * @param string $id ID of the field.
115
- * @param array $args Arguments of the field.
116
- */
117
- final protected function update_field( $id = '', $args ) {
118
- if ( ! isset( $this->fields[ $id ] ) ) {
119
- return;
120
- }
121
-
122
- $field = array_merge( $this->fields[ $id ], $args );
123
-
124
- $this->fields[ $id ] = $field;
125
- }
126
-
127
- /**
128
- * Get the fields for this control.
129
- *
130
- * @since 1.0.0
131
- */
132
- final protected function get_fields() {
133
- foreach ( $this->fields as $property => $field ) {
134
- if ( ! $field['responsive'] ) {
135
- $this->fields[ $property ]['value'] = $this->get_control_value( $property, $field );
136
- }
137
-
138
- if ( $field['responsive'] ) {
139
- $this->fields[ $property ]['value'] = $this->get_responsive_control_value( $property, $field );
140
- }
141
- }
142
-
143
- return $this->fields;
144
- }
145
-
146
- /**
147
- * Include fields.
148
- *
149
- * @since 1.0.0
150
- */
151
- protected function include_fields() {}
152
-
153
- /**
154
- * Exclude fields.
155
- *
156
- * @since 1.0.0
157
- */
158
- protected function exclude_fields() {
159
- if ( empty( $this->exclude ) ) {
160
- return;
161
- }
162
-
163
- $exclude = $this->exclude;
164
-
165
- $this->fields = array_filter( $this->fields, function( $field ) use ( $exclude ) {
166
- return ! in_array( $field['property'], $exclude, true );
167
- } );
168
- }
169
-
170
- /**
171
- * Filter fields before ppassing to JSON.
172
- *
173
- * @since 1.0.4
174
- */
175
- protected function filter_fields() {
176
- if ( empty( $this->filter_fields ) || ! is_array( $this->filter_fields ) ) {
177
- return;
178
- }
179
-
180
- foreach ( $this->filter_fields as $property => $args ) {
181
- if ( isset( $this->fields[ $property ] ) ) {
182
- $this->fields[ $property ] = array_merge( $this->fields[ $property ], $args );
183
- }
184
- }
185
- }
186
-
187
- /**
188
- * Get the control value.
189
- *
190
- * @since 1.0.0
191
- *
192
- * @param array $property Field property name.
193
- * @param array $field Control field.
194
- *
195
- * @return array Property value.
196
- */
197
- final protected function get_control_value( $property, $field ) {
198
- if ( $this->responsive && isset( $this->json['value']['desktop'][ $property ] ) ) {
199
- return $this->json['value']['desktop'][ $property ];
200
- }
201
-
202
- if ( isset( $this->json['value'][ $property ] ) ) {
203
- return $this->json['value'][ $property ];
204
- }
205
-
206
- if ( isset( $field['default'] ) ) {
207
- return $field['default'];
208
- }
209
-
210
- return null;
211
- }
212
-
213
- /**
214
- * Get the responsive control value.
215
- *
216
- * @since 1.0.0
217
- *
218
- * @param array $property Field property name.
219
- * @param array $field Control field.
220
- *
221
- * @return array Property value.
222
- */
223
- final protected function get_responsive_control_value( $property, $field ) {
224
- $value = [];
225
-
226
- foreach ( JupiterX_Customizer::$responsive_devices as $device => $media_query ) {
227
- if ( isset( $this->json['value'][ $device ][ $property ] ) ) {
228
- $value[ $device ] = $this->json['value'][ $device ][ $property ];
229
- continue;
230
- }
231
-
232
- if ( isset( $field['default'][ $device ] ) ) {
233
- $value[ $device ] = $field['default'][ $device ];
234
- continue;
235
- }
236
- }
237
-
238
- return $value;
239
- }
240
-
241
- /**
242
- * Render the control's static content.
243
- *
244
- * @since 1.0.0
245
- */
246
- final protected function render_content() {
247
- ?>
248
- <?php if ( ! empty( $this->label ) ) : ?>
249
- <span class="customize-control-title"><?php echo wp_kses_post( $this->label ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></span>
250
- <?php endif; ?>
251
- <?php if ( ! empty( $this->description ) ) : ?>
252
- <span class="description customize-control-description"><?php echo wp_kses_post( $this->description ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></span>
253
- <?php endif; ?>
254
- <ul class="jupiterx-group-controls jupiterx-row"></ul>
255
- <?php
256
- }
257
-
258
- /**
259
- * Don't render a JS template for this.
260
- *
261
- * @since 1.0.0
262
- */
263
- final protected function content_template() {}
264
- }
1
+ <?php
2
+ /**
3
+ * This class is a common class for all the group controls.
4
+ *
5
+ * @package JupiterX\Framework\API\Customizer
6
+ *
7
+ * @since 1.0.0
8
+ */
9
+
10
+ // Exit if accessed directly.
11
+ if ( ! defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+
15
+ /**
16
+ * Group control base class.
17
+ *
18
+ * @since 1.0.0
19
+ * @ignore
20
+ * @access private
21
+ *
22
+ * @package JupiterX\Framework\API\Customizer
23
+ */
24
+ class JupiterX_Customizer_Base_Group_Control extends JupiterX_Customizer_Base_Control {
25
+
26
+ /**
27
+ * Fields for this control.
28
+ *
29
+ * @since 1.0.0
30
+ *
31
+ * @var string
32
+ */
33
+ public $fields = [];
34
+
35
+ /**
36
+ * Exclude fields for this control.
37
+ *
38
+ * @since 1.0.0
39
+ *
40
+ * @var string
41
+ */
42
+ public $exclude = [];
43
+
44
+ /**
45
+ * Include fields for this control.
46
+ *
47
+ * @since 1.0.0
48
+ *
49
+ * @var string
50
+ */
51
+ public $include = [];
52
+
53
+ /**
54
+ * Use to add, edit or remove field arguments.
55
+ *
56
+ * @since 1.0.4
57
+ *
58
+ * @var array
59
+ */
60
+ public $filter_fields = [];
61
+
62
+ /**
63
+ * Refresh the parameters passed to the JavaScript via JSON.
64
+ *
65
+ * @since 1.0.0
66
+ */
67
+ public function to_json() {
68
+ parent::to_json();
69
+
70
+ // Set the fields for this control.
71
+ $this->set_fields();
72
+
73
+ // Include fields.
74
+ $this->include_fields();
75
+
76
+ // Exclude fields.
77
+ $this->exclude_fields();
78
+
79
+ // Filter fields.
80
+ $this->filter_fields();
81
+
82
+ // Get fields.
83
+ $this->json['fields'] = $this->get_fields();
84
+ }
85
+
86
+ /**
87
+ * Set the fields for this control.
88
+ *
89
+ * @since 1.0.0
90
+ */
91
+ protected function set_fields() {}
92
+
93
+ /**
94
+ * Add field.
95
+ *
96
+ * @since 1.0.0
97
+ *
98
+ * @param string $property Property name of field.
99
+ * @param array $args Field arguments.
100
+ */
101
+ final protected function add_field( $property, $args ) {
102
+ $this->fields[ $property ] = array_merge( $args, [
103
+ 'id' => sprintf( '%1$s_%2$s', $this->id, $property ),
104
+ 'property' => $property,
105
+ 'responsive' => $this->responsive && isset( $args['responsive'] ) ? true : false,
106
+ ] );
107
+ }
108
+
109
+ /**
110
+ * Update field.
111
+ *
112
+ * @since 1.0.0
113
+ *
114
+ * @param string $id ID of the field.
115
+ * @param array $args Arguments of the field.
116
+ */
117
+ final protected function update_field( $id = '', $args ) {
118
+ if ( ! isset( $this->fields[ $id ] ) ) {
119
+ return;
120
+ }
121
+
122
+ $field = array_merge( $this->fields[ $id ], $args );
123
+
124
+ $this->fields[ $id ] = $field;
125
+ }
126
+
127
+ /**
128
+ * Get the fields for this control.
129
+ *
130
+ * @since 1.0.0
131
+ */
132
+ final protected function get_fields() {
133
+ foreach ( $this->fields as $property => $field ) {
134
+ if ( ! $field['responsive'] ) {
135
+ $this->fields[ $property ]['value'] = $this->get_control_value( $property, $field );
136
+ }
137
+
138
+ if ( $field['responsive'] ) {
139
+ $this->fields[ $property ]['value'] = $this->get_responsive_control_value( $property, $field );
140
+ }
141
+ }
142
+
143
+ return $this->fields;
144
+ }
145
+
146
+ /**
147
+ * Include fields.
148
+ *
149
+ * @since 1.0.0
150
+ */
151
+ protected function include_fields() {}
152
+
153
+ /**
154
+ * Exclude fields.
155
+ *
156
+ * @since 1.0.0
157
+ */
158
+ protected function exclude_fields() {
159
+ if ( empty( $this->exclude ) ) {
160
+ return;
161
+ }
162
+
163
+ $exclude = $this->exclude;
164
+
165
+ $this->fields = array_filter( $this->fields, function( $field ) use ( $exclude ) {
166
+ return ! in_array( $field['property'], $exclude, true );
167
+ } );
168
+ }
169
+
170
+ /**
171
+ * Filter fields before ppassing to JSON.
172
+ *
173
+ * @since 1.0.4
174
+ */
175
+ protected function filter_fields() {
176
+ if ( empty( $this->filter_fields ) || ! is_array( $this->filter_fields ) ) {
177
+ return;
178
+ }
179
+
180
+ foreach ( $this->filter_fields as $property => $args ) {
181
+ if ( isset( $this->fields[ $property ] ) ) {
182
+ $this->fields[ $property ] = array_merge( $this->fields[ $property ], $args );
183
+ }
184
+ }
185
+ }
186
+
187
+ /**
188
+ * Get the control value.
189
+ *
190
+ * @since 1.0.0
191
+ *
192
+ * @param array $property Field property name.
193
+ * @param array $field Control field.
194
+ *
195
+ * @return array Property value.
196
+ */
197
+ final protected function get_control_value( $property, $field ) {
198
+ if ( $this->responsive && isset( $this->json['value']['desktop'][ $property ] ) ) {
199
+ return $this->json['value']['desktop'][ $property ];
200
+ }
201
+
202
+ if ( isset( $this->json['value'][ $property ] ) ) {
203
+ return $this->json['value'][ $property ];
204
+ }
205
+
206
+ if ( isset( $field['default'] ) ) {
207
+ return $field['default'];
208
+ }
209
+
210
+ return null;
211
+ }
212
+
213
+ /**
214
+ * Get the responsive control value.
215
+ *
216
+ * @since 1.0.0
217
+ *
218
+ * @param array $property Field property name.
219
+ * @param array $field Control field.
220
+ *
221
+ * @return array Property value.
222
+ */
223
+ final protected function get_responsive_control_value( $property, $field ) {
224
+ $value = [];
225
+
226
+ foreach ( JupiterX_Customizer::$responsive_devices as $device => $media_query ) {
227
+ if ( isset( $this->json['value'][ $device ][ $property ] ) ) {
228
+ $value[ $device ] = $this->json['value'][ $device ][ $property ];
229
+ continue;
230
+ }
231
+
232
+ if ( isset( $field['default'][ $device ] ) ) {
233
+ $value[ $device ] = $field['default'][ $device ];
234
+ continue;
235
+ }
236
+ }
237
+
238
+ return $value;
239
+ }
240
+
241
+ /**
242
+ * Render the control's static content.
243
+ *
244
+ * @since 1.0.0
245
+ */
246
+ final protected function render_content() {
247
+ ?>
248
+ <?php if ( ! empty( $this->label ) ) : ?>
249
+ <span class="customize-control-title"><?php echo wp_kses_post( $this->label ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></span>
250
+ <?php endif; ?>
251
+ <?php if ( ! empty( $this->description ) ) : ?>
252
+ <span class="description customize-control-description"><?php echo wp_kses_post( $this->description ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></span>
253
+ <?php endif; ?>
254
+ <ul class="jupiterx-group-controls jupiterx-row"></ul>
255
+ <?php
256
+ }
257
+
258
+ /**
259
+ * Don't render a JS template for this.
260
+ *
261
+ * @since 1.0.0
262
+ */
263
+ final protected function content_template() {}
264
+ }
includes/customizer/api/includes/base/class-input-group.php CHANGED
@@ -1,103 +1,103 @@
1
- <?php
2
- /**
3
- * Base class for control with input group icon, text and unit.
4
- *
5
- * @package JupiterX\Framework\API\Customizer
6
- *
7
- * @since 1.0.0
8
- */
9
-
10
- // Exit if accessed directly.
11
- if ( ! defined( 'ABSPATH' ) ) {
12
- exit;
13
- }
14
-
15
- /**
16
- * Base input group class.
17
- *
18
- * @since 1.0.0
19
- * @ignore
20
- * @access private
21
- *
22
- * @package JupiterX\Framework\API\Customizer
23
- */
24
- class JupiterX_Customizer_Base_Input_Group extends JupiterX_Customizer_Base_Control {
25
-
26
- /**
27
- * Control's input group text.
28
- *
29
- * @since 1.0.0
30
- *
31
- * @var string
32
- */
33
- public $text = '';
34
-
35
- /**
36
- * Control's input group icon.
37
- *
38
- * @since 1.0.0
39
- *
40
- * @var string
41
- */
42
- public $icon = '';
43
-
44
- /**
45
- * Refresh the parameters passed to the JavaScript via JSON.
46
- *
47
- * @since 1.0.0
48
- */
49
- public function to_json() {
50
- parent::to_json();
51
-
52
- $this->json['text'] = $this->text;
53
- $this->json['icon'] = $this->icon;
54
- }
55
-
56
- /**
57
- * An Underscore (JS) template for control wrapper.
58
- *
59
- * Use to create the control template.
60
- *
61
- * @since 1.0.0
62
- */
63
- protected function control_template() {
64
- ?>
65
- <#
66
- hasText = ! _.isUndefined( data.text ) && ! _.isEmpty( data.text )
67
- hasIcon = ! _.isUndefined( data.icon ) && ! _.isEmpty( data.icon )
68
- controlClass = 'jupiterx-control ' + data.type + '-control'
69
- controlClass += ( hasIcon || hasText ) ? ' jupiterx-input-group' : ''
70
- controlClass += ( hasIcon ) ? ' has-icon' : ''
71
- controlClass += ( hasText ) ? ' has-text' : ''
72
- #>
73
- <div class="{{ controlClass }}" {{{ data.controlAttrs }}}>
74
- <?php
75
- $this->group_prefix_template();
76
- $this->group_field_template();
77
- ?>
78
- </div>
79
- <?php
80
- }
81
-
82
- /**
83
- * An Underscore (JS) template for field prefix.
84
- *
85
- * @since 1.0.0
86
- */
87
- protected function group_prefix_template() {
88
- ?>
89
- <# if ( hasText ) { #>
90
- <span class="jupiterx-input-group-text">{{ data.text }}</span>
91
- <# } else if ( hasIcon ) { #>
92
- <span class="jupiterx-input-group-icon"><img src="<?php echo esc_url( JupiterX_Customizer_Utils::get_assets_url() ); ?>/img/{{ data.icon }}.svg" /></span>
93
- <# } #>
94
- <?php
95
- }
96
-
97
- /**
98
- * An Underscore (JS) template for control field.
99
- *
100
- * @since 1.0.0
101
- */
102
- protected function group_field_template() {}
103
- }
1
+ <?php
2
+ /**
3
+ * Base class for control with input group icon, text and unit.
4
+ *
5
+ * @package JupiterX\Framework\API\Customizer
6
+ *
7
+ * @since 1.0.0
8
+ */
9
+
10
+ // Exit if accessed directly.
11
+ if ( ! defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+
15
+ /**
16
+ * Base input group class.
17
+ *
18
+ * @since 1.0.0
19
+ * @ignore
20
+ * @access private
21
+ *
22
+ * @package JupiterX\Framework\API\Customizer
23
+ */
24
+ class JupiterX_Customizer_Base_Input_Group extends JupiterX_Customizer_Base_Control {
25
+
26
+ /**
27
+ * Control's input group text.
28
+ *
29
+ * @since 1.0.0
30
+ *
31
+ * @var string
32
+ */
33
+ public $text = '';
34
+
35
+ /**
36
+ * Control's input group icon.
37
+ *
38
+ * @since 1.0.0
39
+ *
40
+ * @var string
41
+ */
42
+ public $icon = '';
43
+
44
+ /**
45
+ * Refresh the parameters passed to the JavaScript via JSON.
46
+ *
47
+ * @since 1.0.0
48
+ */
49
+ public function to_json() {
50
+ parent::to_json();
51
+
52
+ $this->json['text'] = $this->text;
53
+ $this->json['icon'] = $this->icon;
54
+ }
55
+
56
+ /**
57
+ * An Underscore (JS) template for control wrapper.
58
+ *
59
+ * Use to create the control template.
60
+ *
61
+ * @since 1.0.0
62
+ */
63
+ protected function control_template() {
64
+ ?>
65
+ <#
66
+ hasText = ! _.isUndefined( data.text ) && ! _.isEmpty( data.text )
67
+ hasIcon = ! _.isUndefined( data.icon ) && ! _.isEmpty( data.icon )
68
+ controlClass = 'jupiterx-control ' + data.type + '-control'
69
+ controlClass += ( hasIcon || hasText ) ? ' jupiterx-input-group' : ''
70
+ controlClass += ( hasIcon ) ? ' has-icon' : ''
71
+ controlClass += ( hasText ) ? ' has-text' : ''
72
+ #>
73
+ <div class="{{ controlClass }}" {{{ data.controlAttrs }}}>
74
+ <?php
75
+ $this->group_prefix_template();
76
+ $this->group_field_template();
77
+ ?>
78
+ </div>
79
+ <?php
80
+ }
81
+
82
+ /**
83
+ * An Underscore (JS) template for field prefix.
84
+ *
85
+ * @since 1.0.0
86
+ */
87
+ protected function group_prefix_template() {
88
+ ?>
89
+ <# if ( hasText ) { #>
90
+ <span class="jupiterx-input-group-text">{{ data.text }}</span>
91
+ <# } else if ( hasIcon ) { #>
92
+ <span class="jupiterx-input-group-icon"><img src="<?php echo esc_url( JupiterX_Customizer_Utils::get_assets_url() ); ?>/img/{{ data.icon }}.svg" /></span>
93
+ <# } #>
94
+ <?php
95
+ }
96
+
97
+ /**
98
+ * An Underscore (JS) template for control field.
99
+ *
100
+ * @since 1.0.0
101
+ */
102
+ protected function group_field_template() {}
103
+ }
includes/customizer/api/includes/class-autoloader.php CHANGED
@@ -1,119 +1,119 @@
1
- <?php
2
- /**
3
- * This class handles customizer function.
4
- *
5
- * @package JupiterX\Framework\API\Customizer
6
- *
7
- * @since 1.0.0
8
- *
9
- * @todo Enhance the Customizer autoloading code.
10
- */
11
-
12
- // Exit if accessed directly.
13
- if ( ! defined( 'ABSPATH' ) ) {
14
- exit;
15
- }
16
-
17
- /**
18
- * Extends WordPress customizer capability.
19
- *
20
- * @since 1.0.0
21
- * @ignore
22
- * @access private
23
- *
24
- * @package JupiterX\Framework\API\Customizer
25
- */
26
- final class JupiterX_Core_Customizer_Autoloader {
27
-
28
- /**
29
- * Directory maps.
30
- *
31
- * @since 1.0.0
32
- *
33
- * @var array
34
- */
35
- public $dir_map = [
36
- 'base' => 'includes/base',
37
- 'control' => 'includes/control',
38
- 'group-control' => 'includes/control/group',
39
- 'output' => 'includes/output',
40
- 'section' => 'includes/section',
41
- 'kirki-extend-base' => 'modules/kirki-extend/base',
42
- 'kirki-extend-output' => 'modules/kirki-extend/output',
43
- 'kirki-extend' => 'modules/kirki-extend',
44
- 'get-variables' => 'modules/compiler',
45
- 'compiler' => 'modules/compiler',
46
- 'post-message' => 'modules/post-message',
47
- 'elementor' => 'modules/elementor',
48
- ];
49
-
50
- /**
51
- * Construct the class.
52
- *
53
- * @since 1.0.0
54
- */
55
- public function __construct() {
56
- spl_autoload_register( [ $this, 'autoload' ] );
57
- }
58
-
59
- /**
60
- * Customizer autoload function.
61
- *
62
- * @since 1.0.0
63
- *
64
- * @param string $class_name The class name from spl_autoload_register.
65
- */
66
- public function autoload( $class_name ) {
67
- if ( class_exists( $class_name ) || 0 !== stripos( $class_name, 'JupiterX_Customizer_' ) ) {
68
- return;
69
- }
70
-
71
- // Assumed file name.
72
- $file_name = strtolower( str_replace( [ 'JupiterX_Customizer_', '_' ], [ '', '-' ], $class_name ) );
73
-
74
- // Get path attributes.
75
- $path = $this->get_path( $file_name );
76
-
77
- // Create full path.
78
- $full_path = sprintf( '%1$s/%2$s', $path['dir'], $path['name'] );
79
-
80
- if ( file_exists( $full_path ) ) {
81
- include_once $full_path;
82
- }
83
- }
84
-
85
- /**
86
- * Get the file.
87
- *
88
- * @since 1.0.0
89
- *
90
- * @param string $file_name Assumed file name.
91
- *
92
- * @return array Path attributes.
93
- */
94
- public function get_path( $file_name ) {
95
- $path = [
96
- 'dir' => 'includes',
97
- 'name' => $file_name,
98
- ];
99
-
100
- // Change dir and file name if found in directory map.
101
- foreach ( $this->dir_map as $key => $dir ) {
102
- if ( 0 === stripos( $file_name, $key ) ) {
103
- $path = [
104
- 'dir' => $dir,
105
- 'name' => str_replace( $key . '-', '', $file_name ),
106
- ];
107
- break;
108
- }
109
- }
110
-
111
- return [
112
- 'dir' => wp_normalize_path( JUPITERX_CORE_CUSTOMIZER_PATH . $path['dir'] ),
113
- 'name' => sprintf( 'class-%1$s.php', $path['name'] ),
114
- ];
115
- }
116
- }
117
-
118
- // Initialize autoloader.
119
- new JupiterX_Core_Customizer_Autoloader();
1
+ <?php
2
+ /**
3
+ * This class handles customizer function.
4
+ *
5
+ * @package JupiterX\Framework\API\Customizer
6
+ *
7
+ * @since 1.0.0
8
+ *
9
+ * @todo Enhance the Customizer autoloading code.
10
+ */
11
+
12
+ // Exit if accessed directly.
13
+ if ( ! defined( 'ABSPATH' ) ) {
14
+ exit;
15
+ }
16
+
17
+ /**
18
+ * Extends WordPress customizer capability.
19
+ *
20
+ * @since 1.0.0
21
+ * @ignore
22
+ * @access private
23
+ *
24
+ * @package JupiterX\Framework\API\Customizer
25
+ */
26
+ final class JupiterX_Core_Customizer_Autoloader {
27
+
28
+ /**
29
+ * Directory maps.
30
+ *
31
+ * @since 1.0.0
32
+ *
33
+ * @var array
34
+ */
35
+ public $dir_map = [
36
+ 'base' => 'includes/base',
37
+ 'control' => 'includes/control',
38
+ 'group-control' => 'includes/control/group',
39
+ 'output' => 'includes/output',
40
+ 'section' => 'includes/section',
41
+ 'kirki-extend-base' => 'modules/kirki-extend/base',
42
+ 'kirki-extend-output' => 'modules/kirki-extend/output',
43
+ 'kirki-extend' => 'modules/kirki-extend',
44
+ 'get-variables' => 'modules/compiler',
45
+ 'compiler' => 'modules/compiler',
46
+ 'post-message' => 'modules/post-message',
47
+ 'elementor' => 'modules/elementor',
48
+ ];
49
+
50
+ /**
51
+ * Construct the class.
52
+ *
53
+ * @since 1.0.0
54
+ */
55
+ public function __construct() {
56
+ spl_autoload_register( [ $this, 'autoload' ] );
57
+ }
58
+
59
+ /**
60
+ * Customizer autoload function.
61
+ *
62
+ * @since 1.0.0
63
+ *
64
+ * @param string $class_name The class name from spl_autoload_register.
65
+ */
66
+ public function autoload( $class_name ) {
67
+ if ( class_exists( $class_name ) || 0 !== stripos( $class_name, 'JupiterX_Customizer_' ) ) {
68
+ return;
69
+ }
70
+
71
+ // Assumed file name.
72
+ $file_name = strtolower( str_replace( [ 'JupiterX_Customizer_', '_' ], [ '', '-' ], $class_name ) );
73
+
74
+ // Get path attributes.
75
+ $path = $this->get_path( $file_name );
76
+
77
+ // Create full path.
78
+ $full_path = sprintf( '%1$s/%2$s', $path['dir'], $path['name'] );
79
+
80
+ if ( file_exists( $full_path ) ) {
81
+ include_once $full_path;
82
+ }
83
+ }
84
+
85
+ /**
86
+ * Get the file.
87
+ *
88
+ * @since 1.0.0
89
+ *
90
+ * @param string $file_name Assumed file name.
91
+ *
92
+ * @return array Path attributes.
93
+ */
94
+ public function get_path( $file_name ) {
95
+ $path = [
96
+ 'dir' => 'includes',
97
+ 'name' => $file_name,
98
+ ];
99
+
100
+ // Change dir and file name if found in directory map.
101
+ foreach ( $this->dir_map as $key => $dir ) {
102
+ if ( 0 === stripos( $file_name, $key ) ) {
103
+ $path = [
104
+ 'dir' => $dir,
105
+ 'name' => str_replace( $key . '-', '', $file_name ),
106
+ ];
107
+ break;
108
+ }
109
+ }
110
+
111
+ return [
112
+ 'dir' => wp_normalize_path( JUPITERX_CORE_CUSTOMIZER_PATH . $path['dir'] ),
113
+ 'name' => sprintf( 'class-%1$s.php', $path['name'] ),
114
+ ];
115
+ }
116
+ }
117
+
118
+ // Initialize autoloader.
119
+ new JupiterX_Core_Customizer_Autoloader();
includes/customizer/api/includes/class-templates.php CHANGED
@@ -1,163 +1,163 @@
1
- <?php
2
- /**
3
- * This class handles printing custom templates in Customizer preview.
4
- *
5
- * @package JupiterX\Framework\API\Customizer
6
- *
7
- * @since 1.0.0
8
- */
9
-
10
- // Exit if accessed directly.
11
- if ( ! defined( 'ABSPATH' ) ) {
12
- exit;
13
- }
14
-
15
- /**
16
- * Print custom templates.
17
- *
18
- * @since 1.0.0
19
- * @ignore
20
- * @access private
21
- *
22
- * @package JupiterX\Framework\API\Customizer
23
- */
24
- final class JupiterX_Core_Customizer_Templates {
25
-
26
- /**
27
- * Construct the class.
28
- *
29
- * @since 1.0.0
30
- */
31
- public function __construct() {
32
- add_action( 'customize_controls_print_footer_scripts', [ $this, 'render_templates' ], 0 );
33
- }
34
-
35
- /**
36
- * Print templates in Customizer page.
37
- *
38
- * @since 1.0.0
39
- * @SuppressWarnings(PHPMD.ElseExpression)
40
- */
41
- public function render_templates() {
42
- ?>
43
- <script type="text/html" id="tmpl-customize-jupiterx-popup-content">
44
- <div id="customize-jupiterx-popup-content" class="jupiterx-popup">
45
- <div id="customize-jupiterx-popup-controls" class="jupiterx-popup-container"></div>
46
- </div>
47
- </script>
48
-
49
- <script type="text/html" id="tmpl-customize-jupiterx-popup-child">
50
- <div class="jupiterx-popup-child">
51
- <div class="jupiterx-child-popup active">
52
- <# if ( data.title ) { #>
53
- <div class="jupiterx-child-popup-header">
54
- <h3 class="jupiterx-child-popup-title">{{{ data.title }}}</h3>
55
- <div class="jupiterx-child-popup-header-buttons">
56
- <button class="jupiterx-child-popup-button jupiterx-child-popup-close">
57
- <span class="dashicons dashicons-no"></span>
58
- <span class="screen-reader-text"><?php esc_html_e( 'Close', 'jupiterx-core' ); ?></span>
59
- </button>
60
- </div>
61
- </div>
62
- <# } #>
63
- <div class="jupiterx-child-popup-content"></div>
64
- </div>
65
- </div>
66
- </script>
67
-
68
- <script type="text/html" id="tmpl-customize-jupiterx-fonts-control-preview">
69
- <div class="jupiterx-fonts-control-preview" data-font-family="{{ data.name }}">
70
- <span class="jupiterx-fonts-control-preview-family">{{{ data.name }}}</span>
71
- <h3 class="jupiterx-fonts-control-preview-sample" style="font-family: {{ data.value || data.name }};"><?php esc_html_e( 'The spectate before us was indeed sublime.', 'jupiterx-core' ); ?></h3>
72
- <button class="jupiterx-fonts-control-preview-remove">
73
- <img src="<?php echo esc_url( JupiterX_Customizer_Utils::get_assets_url() ); ?>/img/x-white.svg" />
74
- <span class="screen-reader-text"><?php esc_html_e( 'Remove', 'jupiterx-core' ); ?></span>
75
- </button>
76
- </div>
77
- </script>
78
-
79
- <script type="text/html" id="tmpl-customize-jupiterx-fonts-control-selector">
80
- <div class="jupiterx-fonts-control-popup jupiterx-popup-child">
81
- <div class="jupiterx-child-popup active">
82
- <div class="jupiterx-child-popup-content">
83
- <div class="jupiterx-fonts-control-selector">
84
- <div class="jupiterx-fonts-control-selector-preview">
85
- <h3 class="jupiterx-fonts-control-selector-sample"><?php esc_html_e( 'The spectate before us was indeed sublime.', 'jupiterx-core' ); ?></h3>
86
- </div>
87
- <span class="customize-control-title"><?php esc_html_e( 'Select a Font Family', 'jupiterx-core' ); ?></span>
88
- <div class="jupiterx-fonts-control-selector-group">
89
- <div class="jupiterx-fonts-control-selector-families">
90
- <div class="jupiterx-control jupiterx-select-control">
91
- <select class="jupiterx-select-control-field">
92
- <# _.each( data.fontFamilies, function( props, name ) { #>
93
- <# type = props.type || props #>
94
- <# value = props.value || name #>
95
- <option data-type="{{ type }}" value="{{ value }}">{{{ name }}}</option>
96
- <# } ); #>
97
- </select>
98
- </div>
99
- </div>
100
- <div class="jupiterx-fonts-control-selector-filters">
101
- <div class="jupiterx-control jupiterx-select-control">
102
- <select class="jupiterx-select-control-field">
103
- <option value=""><?php esc_html_e( 'All Fonts', 'jupiterx-core' ); ?></option>
104
- <# _.each( data.fontTypes, function( fontName, fontType ) { #>
105
- <# disabled = fontName.indexOf('jupiterx-pro-badge') != -1 ? 'disabled' : '' #>
106
- <option value="{{ fontType }}" {{ disabled }}><span>{{ fontName }}</span></option>
107
- <# } ); #>
108
- </select>
109
- </div>
110
- </div>
111
- </div>
112
- <div class="jupiterx-fonts-control-selector-buttons">
113
- <button class="jupiterx-fonts-control-selector-cancel jupiterx-button jupiterx-button-danger">
114
- <img src="<?php echo esc_url( JupiterX_Customizer_Utils::get_icon_url( 'x' ) ); ?>">
115
- <span class="screen-reader-text"><?php esc_html_e( 'Cancel', 'jupiterx-core' ); ?></span>
116
- </button>
117
- <button class="jupiterx-fonts-control-selector-submit jupiterx-button">
118
- <img src="<?php echo esc_url( JupiterX_Customizer_Utils::get_icon_url( 'check' ) ); ?>">
119
- <span class="screen-reader-text"><?php esc_html_e( 'Submit', 'jupiterx-core' ); ?></span>
120
- </button>
121
- </div>
122
- </div>
123
- </div>
124
- </div>
125
- </div>
126
- </script>
127
-
128
- <script type="text/html" id="tmpl-customize-jupiterx-exceptions-control-group">
129
- <div class="jupiterx-exceptions-control-group">
130
- <h3>{{{ data.text }}}</h3>
131
- <button class="jupiterx-exceptions-control-remove jupiterx-button jupiterx-button-outline jupiterx-button-danger jupiterx-button-small" data-id="{{ data.id }}"><?php esc_html_e( 'Remove', 'jupiterx-core' ); ?></button>
132
- <ul class="jupiterx-row jupiterx-group-controls"></ul>
133
- </div>
134
- </script>
135
-
136
- <script type="text/html" id="tmpl-customize-jupiterx-pro-preview-lightbox">
137
- <div class="jupiterx-pro-preview">
138
- <div class="jupiterx-pro-preview-header">
139
- <a class="jupiterx-pro-preview-back" href="#"><span class="jupiterx-icon-arrow-left-solid"></span> <?php esc_html_e( 'Back', 'jupiterx-core' ); ?></a>
140
- <?php if ( jupiterx_is_premium() ) : ?>
141
- <span>
142
- <span class="jupiterx-pro-preview-modal-description"><?php esc_html_e( 'Activate Jupiter X to unlock this feature', 'jupiterx-core' ); ?></span>
143
- <a href="<?php echo esc_attr( jupiterx_upgrade_link( 'customizer' ) ); ?>" class="jupiterx-pro-preview-upgrade jupiterx-upgrade-modal-trigger" target="_blank"><?php esc_html_e( 'Activate Now', 'jupiterx-core' ); ?></a>
144
- </span>
145
- <?php else : ?>
146
- <a class="jupiterx-pro-preview-upgrade" href="<?php echo esc_attr( jupiterx_upgrade_link( 'customizer' ) ); ?>" target="_blank"><?php esc_html_e( 'Upgrade to Jupiter X Pro', 'jupiterx-core' ); ?></a>
147
- <?php endif; ?>
148
- </div>
149
- <div class="jupiterx-pro-preview-content">
150
- <div class="jupiterx-pro-preview-container">
151
- <# if ( data.preview ) { #>
152
- <img class="jupiterx-pro-preview-image" src="{{ data.preview }}" />
153
- <# } #>
154
- </div>
155
- </div>
156
- </div>
157
- </script>
158
- <?php
159
- }
160
- }
161
-
162
- // Initialize.
163
- new JupiterX_Core_Customizer_Templates();
1
+ <?php
2
+ /**
3
+ * This class handles printing custom templates in Customizer preview.
4
+ *
5
+ * @package JupiterX\Framework\API\Customizer
6
+ *
7
+ * @since 1.0.0
8
+ */
9
+
10
+ // Exit if accessed directly.
11
+ if ( ! defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+
15
+ /**
16
+ * Print custom templates.
17
+ *
18
+ * @since 1.0.0
19
+ * @ignore
20
+ * @access private
21
+ *
22
+ * @package JupiterX\Framework\API\Customizer
23
+ */
24
+ final class JupiterX_Core_Customizer_Templates {
25
+
26
+ /**
27
+ * Construct the class.
28
+ *
29
+ * @since 1.0.0
30
+ */
31
+ public function __construct() {
32
+ add_action( 'customize_controls_print_footer_scripts', [ $this, 'render_templates' ], 0 );
33
+ }
34
+
35
+ /**
36
+ * Print templates in Customizer page.
37
+ *
38
+ * @since 1.0.0
39
+ * @SuppressWarnings(PHPMD.ElseExpression)
40
+ */
41
+ public function render_templates() {
42
+ ?>
43
+ <script type="text/html" id="tmpl-customize-jupiterx-popup-content">
44
+ <div id="customize-jupiterx-popup-content" class="jupiterx-popup">
45
+ <div id="customize-jupiterx-popup-controls" class="jupiterx-popup-container"></div>
46
+ </div>
47
+ </script>
48
+
49
+ <script type="text/html" id="tmpl-customize-jupiterx-popup-child">
50
+ <div class="jupiterx-popup-child">
51
+ <div class="jupiterx-child-popup active">
52
+ <# if ( data.title ) { #>
53
+ <div class="jupiterx-child-popup-header">
54
+ <h3 class="jupiterx-child-popup-title">{{{ data.title }}}</h3>
55
+ <div class="jupiterx-child-popup-header-buttons">
56
+ <button class="jupiterx-child-popup-button jupiterx-child-popup-close">
57
+ <span class="dashicons dashicons-no"></span>
58
+ <span class="screen-reader-text"><?php esc_html_e( 'Close', 'jupiterx-core' ); ?></span>
59
+ </button>
60
+ </div>
61
+ </div>
62
+ <# } #>
63
+ <div class="jupiterx-child-popup-content"></div>
64
+ </div>
65
+ </div>
66
+ </script>
67
+
68
+ <script type="text/html" id="tmpl-customize-jupiterx-fonts-control-preview">
69
+ <div class="jupiterx-fonts-control-preview" data-font-family="{{ data.name }}">
70
+ <span class="jupiterx-fonts-control-preview-family">{{{ data.name }}}</span>
71
+ <h3 class="jupiterx-fonts-control-preview-sample" style="font-family: {{ data.value || data.name }};"><?php esc_html_e( 'The spectate before us was indeed sublime.', 'jupiterx-core' ); ?></h3>
72
+ <button class="jupiterx-fonts-control-preview-remove">
73
+ <img src="<?php echo esc_url( JupiterX_Customizer_Utils::get_assets_url() ); ?>/img/x-white.svg" />
74
+ <span class="screen-reader-text"><?php esc_html_e( 'Remove', 'jupiterx-core' ); ?></span>
75
+ </button>
76
+ </div>
77
+ </script>
78
+
79
+ <script type="text/html" id="tmpl-customize-jupiterx-fonts-control-selector">
80
+ <div class="jupiterx-fonts-control-popup jupiterx-popup-child">
81
+ <div class="jupiterx-child-popup active">
82
+ <div class="jupiterx-child-popup-content">
83
+ <div class="jupiterx-fonts-control-selector">
84
+ <div class="jupiterx-fonts-control-selector-preview">
85
+ <h3 class="jupiterx-fonts-control-selector-sample"><?php esc_html_e( 'The spectate before us was indeed sublime.', 'jupiterx-core' ); ?></h3>
86
+ </div>
87
+ <span class="customize-control-title"><?php esc_html_e( 'Select a Font Family', 'jupiterx-core' ); ?></span>
88
+ <div class="jupiterx-fonts-control-selector-group">
89
+ <div class="jupiterx-fonts-control-selector-families">
90
+ <div class="jupiterx-control jupiterx-select-control">
91
+ <select class="jupiterx-select-control-field">
92
+ <# _.each( data.fontFamilies, function( props, name ) { #>
93
+ <# type = props.type || props #>
94
+ <# value = props.value || name #>
95
+ <option data-type="{{ type }}" value="{{ value }}">{{{ name }}}</option>
96
+ <# } ); #>
97
+ </select>
98
+ </div>
99
+ </div>
100
+ <div class="jupiterx-fonts-control-selector-filters">
101
+ <div class="jupiterx-control jupiterx-select-control">
102
+ <select class="jupiterx-select-control-field">
103
+ <option value=""><?php esc_html_e( 'All Fonts', 'jupiterx-core' ); ?></option>
104
+ <# _.each( data.fontTypes, function( fontName, fontType ) { #>
105
+ <# disabled = fontName.indexOf('jupiterx-pro-badge') != -1 ? 'disabled' : '' #>
106
+ <option value="{{ fontType }}" {{ disabled }}><span>{{ fontName }}</span></option>
107
+ <# } ); #>
108
+ </select>
109
+ </div>
110
+ </div>
111
+ </div>
112
+ <div class="jupiterx-fonts-control-selector-buttons">
113
+ <button class="jupiterx-fonts-control-selector-cancel jupiterx-button jupiterx-button-danger">
114
+ <img src="<?php echo esc_url( JupiterX_Customizer_Utils::get_icon_url( 'x' ) ); ?>">
115
+ <span class="screen-reader-text"><?php esc_html_e( 'Cancel', 'jupiterx-core' ); ?></span>
116
+ </button>
117
+ <button class="jupiterx-fonts-control-selector-submit jupiterx-button">
118
+ <img src="<?php echo esc_url( JupiterX_Customizer_Utils::get_icon_url( 'check' ) ); ?>">
119
+ <span class="screen-reader-text"><?php esc_html_e( 'Submit', 'jupiterx-core' ); ?></span>
120
+ </button>
121
+ </div>
122
+ </div>
123
+ </div>
124
+ </div>
125
+ </div>
126
+ </script>
127
+
128
+ <script type="text/html" id="tmpl-customize-jupiterx-exceptions-control-group">
129
+ <div class="jupiterx-exceptions-control-group">
130
+ <h3>{{{ data.text }}}</h3>
131
+ <button class="jupiterx-exceptions-control-remove jupiterx-button jupiterx-button-outline jupiterx-button-danger jupiterx-button-small" data-id="{{ data.id }}"><?php esc_html_e( 'Remove', 'jupiterx-core' ); ?></button>
132
+ <ul class="jupiterx-row jupiterx-group-controls"></ul>
133
+ </div>
134
+ </script>
135
+
136
+ <script type="text/html" id="tmpl-customize-jupiterx-pro-preview-lightbox">
137
+ <div class="jupiterx-pro-preview">
138
+ <div class="jupiterx-pro-preview-header">
139
+ <a class="jupiterx-pro-preview-back" href="#"><span class="jupiterx-icon-arrow-left-solid"></span> <?php esc_html_e( 'Back', 'jupiterx-core' ); ?></a>
140
+ <?php if ( jupiterx_is_premium() ) : ?>
141
+ <span>
142
+ <span class="jupiterx-pro-preview-modal-description"><?php esc_html_e( 'Activate Jupiter X to unlock this feature', 'jupiterx-core' ); ?></span>
143
+ <a href="<?php echo esc_attr( jupiterx_upgrade_link( 'customizer' ) ); ?>" class="jupiterx-pro-preview-upgrade jupiterx-upgrade-modal-trigger" target="_blank"><?php esc_html_e( 'Activate Now', 'jupiterx-core' ); ?></a>
144
+ </span>
145
+ <?php else : ?>
146
+ <a class="jupiterx-pro-preview-upgrade" href="<?php echo esc_attr( jupiterx_upgrade_link( 'customizer' ) ); ?>" target="_blank"><?php esc_html_e( 'Upgrade to Jupiter X Pro', 'jupiterx-core' ); ?></a>
147
+ <?php endif; ?>
148
+ </div>
149
+ <div class="jupiterx-pro-preview-content">
150
+ <div class="jupiterx-pro-preview-container">
151
+ <# if ( data.preview ) { #>
152
+ <img class="jupiterx-pro-preview-image" src="{{ data.preview }}" />
153
+ <# } #>
154
+ </div>
155
+ </div>
156
+ </div>
157
+ </script>
158
+ <?php
159
+ }
160
+ }
161
+
162
+ // Initialize.
163
+ new JupiterX_Core_Customizer_Templates();
includes/customizer/api/includes/control/class-alert.php CHANGED
@@ -1,89 +1,89 @@
1
- <?php
2
- /**
3
- * Handles alert control class.
4
- *
5
- * @package JupiterX\Framework\API\Customizer
6
- *
7
- * @since 1.0.0
8
- */
9
-
10
- // Exit if accessed directly.
11
- if ( ! defined( 'ABSPATH' ) ) {
12
- exit;
13
- }
14
-
15
- /**
16
- * Alert control class.
17
- *
18
- * @since 1.0.0
19
- * @ignore
20
- * @access private
21
- *
22
- * @package JupiterX\Framework\API\Customizer
23
- */
24
- class JupiterX_Customizer_Control_Alert extends JupiterX_Customizer_Base_Control {
25
-
26
- /**
27
- * Control's type.
28
- *
29
- * @since 1.0.0
30
- *
31
- * @var string
32
- */
33
- public $type = 'jupiterx-alert';
34
-
35
- /**
36
- * Control's alert type.
37
- *
38
- * @since 1.0.0
39
- *
40
- * @var string
41
- */
42
- public $jupiterx_type = 'warning';
43
-
44
- /**
45
- * Control's alert url.
46
- *
47
- * @since 1.0.0
48
- *
49
- * @var string
50
- */
51
- public $jupiterx_url = '#';
52
-
53
- /**
54
- * Refresh the parameters passed to the JavaScript via JSON.
55
- *
56
- * @since 1.0.0
57
- */
58
- public function to_json() {
59
- parent::to_json();
60
-
61
- $this->json['jupiterxType'] = $this->jupiterx_type;
62
- $this->json['jupiterxUrl'] = $this->jupiterx_url;
63
- }
64
-
65
- /**
66
- * An Underscore (JS) template for this control's content (but not its container).
67
- *
68
- * Class variables for this control class are available in the `data` JS object;
69
- * export custom variables by overriding {@see WP_Customize_Control::to_json()}.
70
- *
71
- * @see WP_Customize_Control::print_template()
72
- *
73
- * @since 1.0.0
74
- */
75
- protected function content_template() {
76
- ?>
77
- <# type = data.jupiterxType ? 'jupiterx-alert-control-' + data.jupiterxType : '' #>
78
- <div class="jupiterx-control jupiterx-alert-control {{ type }}" role="alert">
79
- <span class="dashicons dashicons-warning"></span>
80
- <span class="jupiterx-alert-control-text">
81
- {{ data.label }}
82
- <a class="jupiterx-alert-control-link" href="{{ data.jupiterxUrl }}" target="_blank">
83
- <?php esc_html_e( 'Learn more', 'jupiterx-core' ); ?>
84
- </a>
85
- </span>
86
- </div>
87
- <?php
88
- }
89
- }
1
+ <?php
2
+ /**
3
+ * Handles alert control class.
4
+ *
5
+ * @package JupiterX\Framework\API\Customizer
6
+ *
7
+ * @since 1.0.0
8
+ */
9
+
10
+ // Exit if accessed directly.
11
+ if ( ! defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+
15
+ /**
16
+ * Alert control class.
17
+ *
18
+ * @since 1.0.0
19
+ * @ignore
20
+ * @access private
21
+ *
22
+ * @package JupiterX\Framework\API\Customizer
23
+ */
24
+ class JupiterX_Customizer_Control_Alert extends JupiterX_Customizer_Base_Control {
25
+
26
+ /**
27
+ * Control's type.
28
+ *
29
+ * @since 1.0.0
30
+ *
31
+ * @var string
32
+ */
33
+ public $type = 'jupiterx-alert';
34
+
35
+ /**
36
+ * Control's alert type.
37
+ *
38
+ * @since 1.0.0
39
+ *
40
+ * @var string
41
+ */
42
+ public $jupiterx_type = 'warning';
43
+
44
+ /**
45
+ * Control's alert url.
46
+ *
47
+ * @since 1.0.0
48
+ *
49
+ * @var string
50
+ */
51
+ public $jupiterx_url = '#';
52
+
53
+ /**
54
+ * Refresh the parameters passed to the JavaScript via JSON.
55
+ *
56
+ * @since 1.0.0
57
+ */
58
+ public function to_json() {
59
+ parent::to_json();
60
+
61
+ $this->json['jupiterxType'] = $this->jupiterx_type;
62
+ $this->json['jupiterxUrl'] = $this->jupiterx_url;
63
+ }
64
+
65
+ /**
66
+ * An Underscore (JS) template for this control's content (but not its container).
67
+ *
68
+ * Class variables for this control class are available in the `data` JS object;
69
+ * export custom variables by overriding {@see WP_Customize_Control::to_json()}.
70
+ *
71
+ * @see WP_Customize_Control::print_template()
72
+ *
73
+ * @since 1.0.0
74
+ */
75
+ protected function content_template() {
76
+ ?>
77
+ <# type = data.jupiterxType ? 'jupiterx-alert-control-' + data.jupiterxType : '' #>
78
+ <div class="jupiterx-control jupiterx-alert-control {{ type }}" role="alert">
79
+ <span class="dashicons dashicons-warning"></span>
80
+ <span class="jupiterx-alert-control-text">
81
+ {{ data.label }}
82
+ <a class="jupiterx-alert-control-link" href="{{ data.jupiterxUrl }}" target="_blank">
83
+ <?php esc_html_e( 'Learn more', 'jupiterx-core' ); ?>
84
+ </a>
85
+ </span>
86
+ </div>
87
+ <?php
88
+ }
89
+ }
includes/customizer/api/includes/control/class-box-model.php CHANGED
@@ -1,210 +1,210 @@
1
- <?php
2
- /**
3
- * Handles box model control class.
4
- *
5
- * @package JupiterX\Framework\API\Customizer
6
- *
7
- * @since 1.0.0
8
- */
9
-
10
- // Exit if accessed directly.
11
- if ( ! defined( 'ABSPATH' ) ) {
12
- exit;
13
- }
14
-
15
- /**
16
- * Box model control class.
17
- *
18
- * @since 1.0.0
19
- * @ignore
20
- * @access private
21
- *
22
- * @package JupiterX\Framework\API\Customizer
23
- */
24
- class JupiterX_Customizer_Control_Box_Model extends JupiterX_Customizer_Base_Control {
25
-
26
- /**
27
- * Control's type.
28
- *
29
- * @since 1.0.0
30
- *
31
- * @var string
32
- */
33
- public $type = 'jupiterx-box-model';
34
-
35
- /**
36
- * Control's exclude box model parts.
37
- *
38
- * @since 1.0.0
39
- *
40
- * @var string
41
- */
42
- public $exclude = [];
43
-
44
- /**
45
- * Control's disable box model parts.
46
- *
47
- * @since 1.0.0
48
- *
49
- * @var string
50
- */
51
- public $disable = [];
52
-
53
- /**
54
- * Control's unit.
55
- *
56
- * @since 1.0.0
57
- *
58
- * @var string
59
- */
60
- public $units = [
61
- 'px',
62
- '%',
63
- 'em',
64
- 'rem',
65
- ];
66
-
67
- /**
68
- * Control's global default unitt.
69
- *
70
- * @since 1.0.0
71
- *
72
- * @var string
73
- */
74
- public static $default_unit = 'rem';
75
-
76
- /**
77
- * Refresh the parameters passed to the JavaScript via JSON.
78
- *
79
- * @since 1.0.0
80
- */
81
- public function to_json() {
82
- parent::to_json();
83
-
84
- $this->json['exclude'] = $this->exclude;
85
- $this->json['disable'] = $this->disable;
86
- $this->json['units'] = $this->units;
87
- $this->json['default_unit'] = self::$default_unit;
88
- }
89
-
90
- /**
91
- * An Underscore (JS) template for control wrapper.
92
- *
93
- * Use to create the control template.
94
- *
95
- * @since 1.0.0
96
- */
97
- protected function control_template() {
98
-
99
- ?>
100
- <#
101
- sides = [
102
- 'top',
103
- 'right',
104
- 'bottom',
105
- 'left'
106
- ]
107
- properties = {
108
- margin: {
109
- title: '<?php esc_attr_e( 'Margin', 'jupiterx-core' ); ?>',
110
- min: -1000,
111
- },
112
- padding: {
113
- title: '<?php esc_attr_e( 'Padding', 'jupiterx-core' ); ?>',
114
- min: 0,
115
- }
116
- }
117
- units = data.units
118
- selectorClass = _.isArray( units ) && 1 === _.size( units ) ? 'disabled' : ''
119
- #>
120
- <div class="jupiterx-control jupiterx-box-model-control">
121
- <# _.each( properties, function ( props, key ) { #>
122
- <# if ( data.exclude.indexOf( key ) < 0 ) {
123
- unitValue = _.isEmpty( data.value[ key + '_unit' ] ) ? data.default_unit : data.value[ key + '_unit' ]
124
- step = 'px' == unitValue ? 1 : .1 #>
125
- <div class="jupiterx-box-model-control-property jupiterx-box-model-control-property-{{ key }}">
126
- <span class="jupiterx-box-model-control-title">{{ props.title }}</span>
127
- <# _.each( sides, function ( side ) {
128
- propertyName = key + '_' + side
129
- value = ! _.isUndefined( data.value[ propertyName ] ) ? data.value[ propertyName ] : ''
130
- disable = _.indexOf( data.disable, propertyName.replace( '_', '-' ) ) !== -1 ? 'disabled' : '' #>
131
- <input class="jupiterx-box-model-control-input jupiterx-box-model-control-{{ side }}" min="{{ props.min }}" {{{ data.inputAttrs }}} type="text" value="{{ value }}" {{ disable }} step="{{ step }}" placeholder="-" {{{ data.link }}} data-setting-property-link="{{ propertyName }}" />
132
- <# } ) #>
133
- <# } #>
134
- <# } ) #>
135
- <# _.each( properties, function ( title, key ) { #>
136
- <# if ( data.exclude.indexOf( key ) < 0 ) { #>
137
- </div>
138
- <# } #>
139
- <# } ) #>
140
- <div class="jupiterx-unit-selector-wrapper">
141
- <# _.each( properties, function ( props, key ) { #>
142
- <# if ( data.exclude.indexOf( key ) < 0 ) { #>
143
- <# unitValue = _.isEmpty( data.value[ key + '_unit' ] ) ? data.default_unit : data.value[ key + '_unit' ] #>
144
- <div class="jupiterx-control-units-container">
145
- <input type="hidden" value="{{ unitValue }}" {{{ data.link }}} data-setting-property-link="{{key + '_unit'}}" />
146
- <span class="jupiterx-unit-selector-label">{{key}}</span>
147
- <ul class="jupiterx-control-unit-selector" data-inputs=".jupiterx-box-model-control-property-{{ key }} > input">
148
- <li class="jupiterx-control-unit selected-unit {{ selectorClass }}">{{ unitValue }}</li>
149
- <# _.each( units, function ( unit ) { #>
150
- <li class="jupiterx-control-unit">{{ unit }}</li>
151
- <# } ) #>
152
- </ul>
153
- </div>
154
- <# } #>
155
- <# } ) #>
156
- </div>
157
- </div>
158
- <?php
159
- }
160
-
161
- /**
162
- * Format CSS value from theme mod array value.
163
- *
164
- * @since 1.0.0
165
- *
166
- * @param array $value The field's value.
167
- * @param array $args The field's arguments.
168
- *
169
- * @return array The formatted properties.
170
- */
171
- public static function format_properties( $value, $args ) {
172
- $args = array_merge( [ 'exclude' => [] ], $args );
173
-
174
- $positions = [ 'top', 'right', 'bottom', 'left' ];
175
-
176
- $vars = [];
177
-
178
- if ( ! in_array( 'margin', $args['exclude'], true ) ) {
179
- $margin_unit = isset( $value['margin_unit'] ) ? $value['margin_unit'] : self::$default_unit;
180
-
181
- foreach ( $positions as $position ) {
182
- // Accepts non-numeric value such as 'auto'.
183
- if ( array_key_exists( 'margin_' . $position, $value ) ) {
184
- $property_value = $value[ 'margin_' . $position ];
185
- $unit = is_numeric( $property_value ) && 0 !== $property_value ? $margin_unit : '';
186
- $position = jupiterx_get_direction( $position );
187
-
188
- $vars[ 'margin-' . $position ] = $property_value . $unit;
189
- }
190
- }
191
- }
192
-
193
- if ( ! in_array( 'padding', $args['exclude'], true ) ) {
194
- $padding_unit = isset( $value['padding_unit'] ) ? $value['padding_unit'] : self::$default_unit;
195
-
196
- foreach ( $positions as $position ) {
197
- // Does not accept any value that is not numeric.
198
- if ( array_key_exists( 'padding_' . $position, $value ) && is_numeric( $value[ 'padding_' . $position ] ) ) {
199
- $property_value = $value[ 'padding_' . $position ];
200
- $unit = 0 !== $property_value ? $padding_unit : '';
201
- $position = jupiterx_get_direction( $position );
202
-
203
- $vars[ 'padding-' . $position ] = $property_value . $unit;
204
- }
205
- }
206
- }
207
-
208
- return $vars;
209
- }
210
- }
1
+ <?php
2
+ /**
3
+ * Handles box model control class.
4
+ *
5
+ * @package JupiterX\Framework\API\Customizer
6
+ *
7
+ * @since 1.0.0
8
+ */
9
+
10
+ // Exit if accessed directly.
11
+ if ( ! defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+
15
+ /**
16
+ * Box model control class.
17
+ *
18
+ * @since 1.0.0
19
+ * @ignore
20
+ * @access private
21
+ *
22
+ * @package JupiterX\Framework\API\Customizer
23
+ */
24
+ class JupiterX_Customizer_Control_Box_Model extends JupiterX_Customizer_Base_Control {
25
+
26
+ /**
27
+ * Control's type.
28
+ *
29
+ * @since 1.0.0
30
+ *
31
+ * @var string
32
+ */
33
+ public $type = 'jupiterx-box-model';
34
+
35
+ /**
36
+ * Control's exclude box model parts.
37
+ *
38
+ * @since 1.0.0
39
+ *
40
+ * @var string
41
+ */
42
+ public $exclude = [];
43
+
44
+ /**
45
+ * Control's disable box model parts.
46
+ *
47
+ * @since 1.0.0
48
+ *
49
+ * @var string
50
+ */
51
+ public $disable = [];
52
+
53
+ /**
54
+ * Control's unit.
55
+ *
56
+ * @since 1.0.0
57
+ *
58
+ * @var string
59
+ */
60
+ public $units = [
61
+ 'px',
62
+ '%',
63
+ 'em',
64
+ 'rem',
65
+ ];
66
+
67
+ /**
68
+ * Control's global default unitt.
69
+ *
70
+ * @since 1.0.0
71
+ *
72
+ * @var string
73
+ */
74
+ public static $default_unit = 'rem';
75
+
76
+ /**
77
+ * Refresh the parameters passed to the JavaScript via JSON.
78
+ *
79
+ * @since 1.0.0
80
+ */
81
+ public function to_json() {
82
+ parent::to_json();
83
+
84
+ $this->json['exclude'] = $this->exclude;
85
+ $this->json['disable'] = $this->disable;
86
+ $this->json['units'] = $this->units;
87
+ $this->json['default_unit'] = self::$default_unit;
88
+ }
89
+
90
+ /**
91
+ * An Underscore (JS) template for control wrapper.
92
+ *
93
+ * Use to create the control template.
94
+ *
95
+ * @since 1.0.0
96
+ */
97
+ protected function control_template() {
98
+
99
+ ?>
100
+ <#
101
+ sides = [
102
+ 'top',
103
+ 'right',
104
+ 'bottom',
105
+ 'left'
106
+ ]
107
+ properties = {
108
+ margin: {
109
+ title: '<?php esc_attr_e( 'Margin', 'jupiterx-core' ); ?>',
110
+ min: -1000,
111
+ },
112
+ padding: {
113
+ title: '<?php esc_attr_e( 'Padding', 'jupiterx-core' ); ?>',
114
+ min: 0,
115
+ }
116
+ }
117
+ units = data.units
118
+ selectorClass = _.isArray( units ) && 1 === _.size( units ) ? 'disabled' : ''
119
+ #>
120
+ <div class="jupiterx-control jupiterx-box-model-control">
121
+ <# _.each( properties, function ( props, key ) { #>
122
+ <# if ( data.exclude.indexOf( key ) < 0 ) {
123
+ unitValue = _.isEmpty( data.value[ key + '_unit' ] ) ? data.default_unit : data.value[ key + '_unit' ]
124
+ step = 'px' == unitValue ? 1 : .1 #>
125
+ <div class="jupiterx-box-model-control-property jupiterx-box-model-control-property-{{ key }}">
126
+ <span class="jupite