Jupiter X Core - Version 1.9.0

Version Description

Download this release

Release Info

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

Code changes from version 1.8.0 to 1.9.0

Files changed (92) hide show
  1. includes/admin/options.php +62 -0
  2. includes/admin/tgmpa/tgmpa-plugin-list.php +185 -0
  3. includes/admin/update-plugins/class-update-plugins.php +102 -0
  4. includes/compiler/class-compiler.php +1019 -1019
  5. includes/compiler/functions.php +411 -411
  6. includes/compiler/preprocess-aliases.ini +277 -277
  7. includes/compiler/vendors/js-minifier.php +395 -395
  8. includes/compiler/vendors/lessc.php +3694 -3694
  9. includes/control-panel/class-image-sizes.php +0 -95
  10. includes/control-panel/functions.php +158 -112
  11. includes/control-panel/includes/class-browser.php +1193 -0
  12. includes/control-panel/includes/class-customizer-option.php +33 -0
  13. includes/control-panel/includes/class-db-manager.php +691 -0
  14. includes/control-panel/includes/class-export-import-content.php +1224 -0
  15. includes/control-panel/includes/class-filesystem.php +613 -613
  16. includes/control-panel/includes/class-helpers.php +510 -506
  17. includes/control-panel/includes/class-image-sizes.php +98 -0
  18. includes/control-panel/includes/class-install-plugins.php +506 -0
  19. includes/control-panel/{install-template.php → includes/class-install-template.php} +1851 -1856
  20. includes/control-panel/{class-settings.php → includes/class-settings.php} +118 -120
  21. includes/control-panel/includes/class-system-status.php +378 -0
  22. includes/control-panel/includes/class-validator.php +166 -0
  23. includes/control-panel/includes/importer/class-jupiterx-importer.php +155 -0
  24. includes/control-panel/includes/importer/class-logger-serversentevents.php +57 -0
  25. includes/control-panel/includes/importer/class-logger.php +138 -0
  26. includes/control-panel/includes/importer/class-wxr-import-info.php +35 -0
  27. includes/control-panel/includes/importer/class-wxr-importer.php +2076 -0
  28. includes/control-panel/includes/logic-messages.php +456 -0
  29. includes/control-panel/views/export-import-content.php +70 -0
  30. includes/control-panel/views/image-sizes.php +64 -64
  31. includes/control-panel/views/install-plugins.php +56 -0
  32. includes/control-panel/views/install-templates.php +61 -73
  33. includes/control-panel/views/settings.php +103 -102
  34. includes/control-panel/views/system-status.php +929 -0
  35. includes/custom-fields/title-bar.php +33 -0
  36. includes/customizer/api/classes/class-multilingual.php +448 -0
  37. includes/customizer/api/classes/class-status.php +64 -0
  38. includes/customizer/api/customizer.php +295 -0
  39. includes/customizer/api/includes/base/class-control.php +243 -0
  40. includes/customizer/api/includes/base/class-group-control.php +264 -0
  41. includes/customizer/api/includes/base/class-input-group.php +103 -0
  42. includes/customizer/api/includes/class-autoloader.php +119 -0
  43. includes/customizer/api/includes/class-templates.php +163 -0
  44. includes/customizer/api/includes/control/class-alert.php +89 -0
  45. includes/customizer/api/includes/control/class-box-model.php +210 -0
  46. includes/customizer/api/includes/control/class-child-popup.php +115 -0
  47. includes/customizer/api/includes/control/class-choose.php +124 -0
  48. includes/customizer/api/includes/control/class-color.php +66 -0
  49. includes/customizer/api/includes/control/class-divider.php +71 -0
  50. includes/customizer/api/includes/control/class-exceptions.php +88 -0
  51. includes/customizer/api/includes/control/class-font.php +74 -0
  52. includes/customizer/api/includes/control/class-fonts.php +81 -0
  53. includes/customizer/api/includes/control/class-image.php +53 -0
  54. includes/customizer/api/includes/control/class-input.php +176 -0
  55. includes/customizer/api/includes/control/class-label.php +86 -0
  56. includes/customizer/api/includes/control/class-multicheck.php +108 -0
  57. includes/customizer/api/includes/control/class-popup.php +102 -0
  58. includes/customizer/api/includes/control/class-position.php +70 -0
  59. includes/customizer/api/includes/control/class-pro-box.php +107 -0
  60. includes/customizer/api/includes/control/class-radio-image.php +78 -0
  61. includes/customizer/api/includes/control/class-select.php +97 -0
  62. includes/customizer/api/includes/control/class-template.php +97 -0
  63. includes/customizer/api/includes/control/class-text.php +142 -0
  64. includes/customizer/api/includes/control/class-textarea.php +45 -0
  65. includes/customizer/api/includes/control/class-toggle.php +48 -0
  66. includes/customizer/api/includes/control/group/class-background.php +350 -0
  67. includes/customizer/api/includes/control/group/class-border.php +107 -0
  68. includes/customizer/api/includes/control/group/class-box-shadow.php +136 -0
  69. includes/customizer/api/includes/control/group/class-typography.php +204 -0
  70. includes/customizer/api/includes/section/class-link.php +103 -0
  71. includes/customizer/api/includes/section/class-pane.php +84 -0
  72. includes/customizer/api/includes/section/class-popup.php +258 -0
  73. includes/customizer/api/init.php +156 -0
  74. includes/customizer/api/modules/compiler/class-compiler.php +67 -0
  75. includes/customizer/api/modules/compiler/class-get-variables.php +182 -0
  76. includes/customizer/api/modules/kirki-extend/base/class-output.php +77 -0
  77. includes/customizer/api/modules/kirki-extend/class-kirki-extend.php +201 -0
  78. includes/customizer/api/modules/kirki-extend/output/class-background.php +103 -0
  79. includes/customizer/api/modules/kirki-extend/output/class-border.php +109 -0
  80. includes/customizer/api/modules/kirki-extend/output/class-box-model.php +86 -0
  81. includes/customizer/api/modules/kirki-extend/output/class-box-shadow.php +50 -0
  82. includes/customizer/api/modules/kirki-extend/output/class-input.php +62 -0
  83. includes/customizer/api/modules/kirki-extend/output/class-typography.php +73 -0
  84. includes/customizer/api/modules/post-message/class-post-message.php +67 -0
  85. includes/customizer/functions.php +268 -15
  86. includes/customizer/settings/404/popup.php +42 -0
  87. includes/customizer/settings/404/settings.php +24 -0
  88. includes/customizer/settings/blog-archive/popup.php +34 -0
  89. includes/customizer/settings/blog-archive/settings.php +34 -0
  90. includes/customizer/settings/blog-single/author-box.php +15 -0
  91. includes/customizer/settings/blog-single/avatar.php +65 -0
  92. includes/customizer/settings/blog-single/featured-image.php +227 -0
includes/admin/options.php ADDED
@@ -0,0 +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
+
includes/admin/tgmpa/tgmpa-plugin-list.php ADDED
@@ -0,0 +1,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
+ ],
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
+ }
includes/admin/update-plugins/class-update-plugins.php ADDED
@@ -0,0 +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
+ }
includes/compiler/class-compiler.php CHANGED
@@ -1,1019 +1,1019 @@
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
- ';}' => '}', // Strip optional semicolons.
905
- ',\n' => ',', // Don't wrap multiple selectors.
906
- '\n}' => '}', // Don't wrap closing braces.
907
- '} ' => "}\n", // Put each rule on it's own line.
908
- '\n' => '', // Remove all line breaks.
909
- );
910
-
911
- $search = array_keys( $replace );
912
-
913
- return trim( str_replace( $search, $replace, $content ) );
914
- }
915
-
916
- /**
917
- * Check if the given fragment is a callable.
918
- *
919
- * @since 1.0.0
920
- *
921
- * @param mixed $fragment Given fragment to check.
922
- *
923
- * @return bool
924
- */
925
- private function is_function( $fragment ) {
926
- return ( is_array( $fragment ) || is_callable( $fragment ) );
927
- }
928
-
929
- /**
930
- * Kill it :(
931
- *
932
- * @since 1.0.0
933
- *
934
- * @return void
935
- */
936
- private function kill() {
937
-
938
- // Send report if set.
939
- if ( jupiterx_get( 'jupiterx_send_compiler_report' ) ) { // @codingStandardsIgnoreLine
940
- // $this->report(); // @codingStandardsIgnoreLine
941
- }
942
-
943
- $html = jupiterx_output( 'jupiterx_compiler_error_title_text', sprintf(
944
- '<h2>%s</h2>',
945
- __( 'Not cool, Jupiter cannot work its magic :(', 'jupiterx-core' )
946
- ) );
947
-
948
- $html .= jupiterx_output( 'jupiterx_compiler_error_message_text', sprintf(
949
- '<p>%s</p>',
950
- __( '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' )
951
- ) );
952
-
953
- $html .= jupiterx_output( 'jupiterx_compiler_error_contact_text', sprintf(
954
- '<a class="button" href="https://themes.artbees.net/support/" target="_blank">%s</a>',
955
- __( 'Contact Jupiter Support', 'jupiterx-core' )
956
- ) );
957
-
958
- $html .= jupiterx_output( 'jupiterx_compiler_error_report_text', sprintf(
959
- '<p style="margin-top: 12px; font-size: 12px;"><a href="' . add_query_arg( 'jupiterx_send_compiler_report', true ) . '">%1$s</a>. %2$s</p>',
960
- __( 'Send us an automatic report', 'jupiterx-core' ),
961
- __( 'We respect your time and understand you might not be able to contact us.', 'jupiterx-core' )
962
- ) );
963
-
964
- wp_die( wp_kses_post( $html ) );
965
- }
966
-
967
- /**
968
- * Send report.
969
- *
970
- * @since 1.0.0
971
- *
972
- * @todo Decide if we want to use and change the report recipient.
973
- *
974
- * @return void
975
- * @SuppressWarnings(PHPMD.UnusedPrivateMethod)
976
- */
977
- private function report() {
978
- // Send report.
979
- wp_mail(
980
- 'hello@getjupiter.io',
981
- 'Compiler error',
982
- 'Compiler error reported by ' . home_url(),
983
- array(
984
- 'MIME-Version: 1.0' . "\r\n",
985
- 'Content-type: text/html; charset=utf-8' . "\r\n",
986
- "X-Mailer: PHP \r\n",
987
- 'From: ' . wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES ) . ' < ' . get_option( 'admin_email' ) . '>' . "\r\n",
988
- 'Reply-To: ' . get_option( 'admin_email' ) . "\r\n",
989
- )
990
- );
991
-
992
- // Die and display message.
993
- $message = jupiterx_output(
994
- 'jupiterx_compiler_report_error_text',
995
- sprintf(
996
- '<p>%s<p>',
997
- __( 'Thanks for your contribution by reporting this issue. We hope to hear from you again.', 'jupiterx-core' )
998
- )
999
- );
1000
-
1001
- wp_die( wp_kses_post( $message ) );
1002
- }
1003
-
1004
- /**
1005
- * Get the property's value.
1006
- *
1007
- * @since 1.0.0
1008
- *
1009
- * @param string $property Name of the property to get.
1010
- *
1011
- * @return mixed
1012
- */
1013
- public function __get( $property ) {
1014
-
1015
- if ( property_exists( $this, $property ) ) {
1016
- return $this->{$property};
1017
- }
1018
- }
1019
- }
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
+ ';}' => '}', // Strip optional semicolons.
905
+ ',\n' => ',', // Don't wrap multiple selectors.
906
+ '\n}' => '}', // Don't wrap closing braces.
907
+ '} ' => "}\n", // Put each rule on it's own line.
908
+ '\n' => '', // Remove all line breaks.
909
+ );
910
+
911
+ $search = array_keys( $replace );
912
+
913
+ return trim( str_replace( $search, $replace, $content ) );
914
+ }
915
+
916
+ /**
917
+ * Check if the given fragment is a callable.
918
+ *
919
+ * @since 1.0.0
920
+ *
921
+ * @param mixed $fragment Given fragment to check.
922
+ *
923
+ * @return bool
924
+ */
925
+ private function is_function( $fragment ) {
926
+ return ( is_array( $fragment ) || is_callable( $fragment ) );
927
+ }
928
+
929
+ /**
930
+ * Kill it :(
931
+ *
932
+ * @since 1.0.0
933
+ *
934
+ * @return void
935
+ */
936
+ private function kill() {
937
+
938
+ // Send report if set.
939
+ if ( jupiterx_get( 'jupiterx_send_compiler_report' ) ) { // @codingStandardsIgnoreLine
940
+ // $this->report(); // @codingStandardsIgnoreLine
941
+ }
942
+
943
+ $html = jupiterx_output( 'jupiterx_compiler_error_title_text', sprintf(
944
+ '<h2>%s</h2>',
945
+ __( 'Not cool, Jupiter cannot work its magic :(', 'jupiterx-core' )
946
+ ) );
947
+
948
+ $html .= jupiterx_output( 'jupiterx_compiler_error_message_text', sprintf(
949
+ '<p>%s</p>',
950
+ __( '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' )
951
+ ) );
952
+
953
+ $html .= jupiterx_output( 'jupiterx_compiler_error_contact_text', sprintf(
954
+ '<a class="button" href="https://themes.artbees.net/support/" target="_blank">%s</a>',
955
+ __( 'Contact Jupiter Support', 'jupiterx-core' )
956
+ ) );
957
+
958
+ $html .= jupiterx_output( 'jupiterx_compiler_error_report_text', sprintf(
959
+ '<p style="margin-top: 12px; font-size: 12px;"><a href="' . add_query_arg( 'jupiterx_send_compiler_report', true ) . '">%1$s</a>. %2$s</p>',
960
+ __( 'Send us an automatic report', 'jupiterx-core' ),
961
+ __( 'We respect your time and understand you might not be able to contact us.', 'jupiterx-core' )
962
+ ) );
963
+
964
+ wp_die( wp_kses_post( $html ) );
965
+ }
966
+
967
+ /**
968
+ * Send report.
969
+ *
970
+ * @since 1.0.0
971
+ *
972
+ * @todo Decide if we want to use and change the report recipient.
973
+ *
974
+ * @return void
975
+ * @SuppressWarnings(PHPMD.UnusedPrivateMethod)
976
+ */
977
+ private function report() {
978
+ // Send report.
979
+ wp_mail(
980
+ 'hello@getjupiter.io',
981
+ 'Compiler error',
982
+ 'Compiler error reported by ' . home_url(),
983
+ array(
984
+ 'MIME-Version: 1.0' . "\r\n",
985
+ 'Content-type: text/html; charset=utf-8' . "\r\n",
986
+ "X-Mailer: PHP \r\n",
987
+ 'From: ' . wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES ) . ' < ' . get_option( 'admin_email' ) . '>' . "\r\n",
988
+ 'Reply-To: ' . get_option( 'admin_email' ) . "\r\n",
989
+ )
990
+ );
991
+
992
+ // Die and display message.
993
+ $message = jupiterx_output(
994
+ 'jupiterx_compiler_report_error_text',
995
+ sprintf(
996
+ '<p>%s<p>',
997
+ __( 'Thanks for your contribution by reporting this issue. We hope to hear from you again.', 'jupiterx-core' )
998
+ )
999
+ );
1000
+
1001
+ wp_die( wp_kses_post( $message ) );
1002
+ }
1003
+
1004
+ /**
1005
+ * Get the property's value.
1006
+ *
1007
+ * @since 1.0.0
1008
+ *
1009
+ * @param string $property Name of the property to get.
1010
+ *
1011
+ * @return mixed
1012
+ */
1013
+ public function __get( $property ) {
1014
+
1015
+ if ( property_exists( $this, $property ) ) {
1016
+ return $this->{$property};
1017
+ }
1018
+ }
1019
+ }
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