PluginProbe ʕ •ᴥ•ʔ
Kirki – Freeform Page Builder, Website Builder & Customizer / 6.0.13
Kirki – Freeform Page Builder, Website Builder & Customizer v6.0.13
6.1.1 6.1.0 6.0.14 6.0.13 6.0.12 6.0.11 6.0.10 6.0.9 6.0.8 6.0.7 6.0.6 6.0.5 6.0.4 6.0.3 6.0.2 6.0.1 3.1.3 3.1.4 3.1.5 3.1.6 3.1.7 3.1.8 3.1.9 4.0.19 4.0.20 4.0.21 4.0.22 4.0.23 4.0.24 4.1 4.2.0 5.0.0 5.1.0 5.1.1 5.2.0 5.2.1 5.2.2 5.2.3 6.0.0 trunk 3.0.40 3.0.41 3.0.42 3.0.43 3.0.44 3.0.45 3.1.0 3.1.1 3.1.2
kirki / app / Services / AppsService.php
kirki / app / Services Last commit date
AppsService.php 2 weeks ago CollaborationCommentService.php 2 weeks ago CollaborationService.php 2 weeks ago CollectionItemService.php 2 weeks ago CollectionService.php 2 weeks ago EditorService.php 2 weeks ago FontService.php 2 weeks ago MediaService.php 2 weeks ago PageService.php 2 weeks ago PageSettingsService.php 2 weeks ago UtilityPageService.php 2 weeks ago
AppsService.php
280 lines
1 <?php
2
3 namespace Kirki\App\Services;
4
5 defined('ABSPATH') || exit;
6
7 use Exception;
8 use Kirki\App\Constants\GlobalDataKeys;
9 use Kirki\App\DTO\Apps\AppDTO;
10 use Kirki\App\DTO\Apps\InstalledAppDTO;
11 use Kirki\App\Supports\Facades\GlobalData;
12 use Kirki\App\Supports\FileHandler;
13 use Kirki\App\Supports\FilterHooks;
14 use Kirki\Framework\Supports\Facades\DB;
15 use Kirki\Framework\Supports\Facades\File;
16 use Kirki\Framework\Supports\Facades\Http;
17 use Throwable;
18
19 use function Kirki\Framework\collection;
20
21 class AppsService
22 {
23 /**
24 * @return AppDTO[]
25 */
26 public function get_all_apps()
27 {
28 $apps = Http::get(KIRKI_APPS_BASE_URL . '/apps.json', [
29 'sslverify' => false
30 ]);
31
32 if ($apps->failed()) {
33 throw new Exception(esc_html__('Failed to get apps.', 'kirki'));
34 }
35
36 return AppDTO::from_list($apps->json());
37 }
38
39 /**
40 * @return InstalledAppDTO[]
41 */
42 public function get_installed_apps()
43 {
44 $apps = GlobalData::get(GlobalDataKeys::INSTALLED_APPS);
45
46 return InstalledAppDTO::from_list(is_array($apps) ? $apps : []);
47 }
48
49 /**
50 * @return array
51 */
52 public function get_app_settings(string $app_slug)
53 {
54 $settings = GlobalData::get(GlobalDataKeys::APP_SETTINGS . $app_slug) ?? [];
55
56 return FilterHooks::kirki_apps_configuration($app_slug, $settings);
57 }
58
59 /**
60 * @param string $app_slug
61 * @param array $settings
62 * @return array
63 */
64 public function save_app_settings(string $app_slug, array $settings)
65 {
66 GlobalData::update(GlobalDataKeys::APP_SETTINGS . $app_slug, $settings);
67
68 return $settings;
69 }
70
71 public function install_app(AppDTO $payload)
72 {
73 $app_slug = preg_replace('/[^a-zA-Z0-9\-]/', '', $payload->slug);
74 $src = $payload->src;
75 $version = $payload->version;
76
77 $installed_apps = $this->get_installed_apps();
78
79 $is_already_installed = collection($installed_apps)->some(function(InstalledAppDTO $app) use ($app_slug) {
80 return $app->app_slug === $app_slug;
81 });
82
83 if ($is_already_installed) {
84 throw new Exception(esc_html__('This app is already installed. Please check the kirki-apps directory in wp-content folders.', 'kirki'));
85 }
86
87 // Define the remote zip URL and generate a unique file name
88 $remote_zip_url = KIRKI_APPS_BASE_URL . $src;
89 $file_name_new = uniqid('', true) . '.zip'; // 'random.ext'
90
91 $zip_file_path = FileHandler::download_zip_from_remote($remote_zip_url, $file_name_new);
92
93 if ($zip_file_path === false) {
94 throw new Exception(esc_html__('Failed to download the app zip file. Please check the remote URL.', 'kirki'));
95 }
96
97 // Get the WordPress wp-content directory
98 $base_upload_dir = WP_CONTENT_DIR; // Absolute path to 'uploads'
99 $temp_folder_path = $base_upload_dir . '/kirki-apps';
100
101 if (!File::is_writable($base_upload_dir)) {
102 File::delete($zip_file_path);
103 throw new Exception(esc_html__('The wp-content directory is not writable. Please check folder permissions.', 'kirki'));
104 }
105
106 if (File::missing($base_upload_dir)) {
107 if (!File::make_dir($temp_folder_path)) {
108 File::delete($zip_file_path);
109 throw new Exception(esc_html__('Failed to create directory: kirki-apps. Please check folder permissions.', 'kirki'));
110 }
111 }
112
113 $result = FileHandler::extract_zip_file($zip_file_path, $temp_folder_path);
114
115 if ($result === false) {
116 File::delete($zip_file_path);
117 throw new Exception(esc_html__('Failed to extract the app zip file. Please ensure the file is valid.', 'kirki'));
118 }
119
120 try {
121 $installed_apps = collection($installed_apps)->push(InstalledAppDTO::from_array([
122 'app_slug' => $app_slug,
123 'version' => $version,
124 ]))->map(fn(InstalledAppDTO $app) => $app->to_array())->to_array();
125
126 GlobalData::update(GlobalDataKeys::INSTALLED_APPS, $installed_apps);
127
128 File::delete($zip_file_path);
129
130 return [
131 'app_slug' => $app_slug,
132 'version' => $version
133 ];
134 } catch (Throwable $throwable) {
135 File::delete($zip_file_path);
136 throw new Exception(
137 sprintf(
138 /* translators: 1: error message */
139 esc_html__('An error occurred during the app installation: %s', 'kirki'),
140 esc_html($throwable->getMessage())
141 )
142 );
143 }
144 }
145
146 public function update_app(AppDTO $payload)
147 {
148 $app_slug = preg_replace('/[^a-zA-Z0-9\-]/', '', $payload->slug);
149 $src = $payload->src;
150 $new_version = $payload->version;
151
152 $installed_apps = $this->get_installed_apps();
153
154 if (empty($installed_apps)) {
155 throw new Exception(esc_html__('No apps are currently installed.', 'kirki'));
156 }
157
158 $is_installed = collection($installed_apps)->some(function(InstalledAppDTO $app) use ($app_slug) {
159 return $app->app_slug === $app_slug;
160 });
161
162 if (!$is_installed) {
163 throw new Exception(esc_html__('App not found in installed apps.', 'kirki'));
164 }
165
166 $base_upload_dir = WP_CONTENT_DIR;
167 $apps_folder = $base_upload_dir . '/kirki-apps';
168 $app_folder = $apps_folder . '/' . $app_slug;
169 $backup_folder = $app_folder . '_backup_' . time();
170
171 // Create backup of current app
172 if (!File::move($app_folder, $backup_folder)) {
173 throw new Exception(esc_html__('Failed to create backup of current app version.', 'kirki'));
174 }
175
176 $remote_zip_url = KIRKI_APPS_BASE_URL . $src;
177 $file_name_new = uniqid( '', true ) . '.zip';
178 $zip_file_path = FileHandler::download_zip_from_remote($remote_zip_url, $file_name_new);
179
180 if ($zip_file_path === false) {
181 // Restore backup
182 File::move($backup_folder, $app_folder);
183 throw new Exception(esc_html__('Failed to download the new app version.', 'kirki'));
184 }
185
186 $result = FileHandler::extract_zip_file($zip_file_path, $apps_folder);
187
188 File::delete($zip_file_path);
189
190 if ($result === false) {
191 // Restore backup
192 File::move($backup_folder, $app_folder);
193 throw new Exception(esc_html__('Failed to extract the new app version.', 'kirki'));
194 }
195
196
197 try {
198 $installed_apps = collection($installed_apps)->map(function(InstalledAppDTO $app) use ($app_slug, $new_version) {
199 if ($app->app_slug === $app_slug) {
200 $app->version = $new_version;
201 }
202
203 return $app->to_array();
204 })->to_array();
205
206 GlobalData::update(GlobalDataKeys::INSTALLED_APPS, $installed_apps);
207 } catch (Throwable $throwable) {
208 if (File::exists($app_folder)) {
209 File::delete($app_folder);
210 }
211
212 if (File::exists($backup_folder)) {
213 File::move($backup_folder, $app_folder);
214 }
215
216 throw new Exception(
217 sprintf(
218 /* translators: 1: error message */
219 esc_html__('An error occurred during the app update: %s', 'kirki'),
220 esc_html($throwable->getMessage())
221 )
222 );
223 }
224
225 File::delete($backup_folder);
226
227 return [
228 'app_slug' => $app_slug,
229 'version' => $new_version
230 ];
231 }
232
233 public function delete_app(string $app_slug)
234 {
235 $installed_apps = $this->get_installed_apps();
236
237 if (empty($installed_apps)) {
238 throw new Exception(esc_html__('No apps are currently installed.', 'kirki'));
239 }
240
241 $is_installed = collection($installed_apps)->some(function(InstalledAppDTO $app) use ($app_slug) {
242 return $app->app_slug === $app_slug;
243 });
244
245 if (!$is_installed) {
246 throw new Exception(esc_html__('App not found in installed apps.', 'kirki'));
247 }
248
249 $base_upload_dir = WP_CONTENT_DIR; // Absolute path to 'uploads'
250 $app_folder = $base_upload_dir . '/kirki-apps/' . $app_slug;
251
252 $installed_apps = collection($installed_apps)->filter(function(InstalledAppDTO $app) use ($app_slug) {
253 return $app->app_slug !== $app_slug;
254 })->map(fn(InstalledAppDTO $app) => $app->to_array())->values()->to_array();
255
256 DB::begin_transaction();
257
258 try {
259 GlobalData::update(GlobalDataKeys::INSTALLED_APPS, $installed_apps);
260 $this->save_app_settings($app_slug, []);
261
262 if (!File::delete($app_folder)) {
263 throw new Exception(esc_html__('Failed to delete the app folder.', 'kirki'));
264 }
265
266 DB::commit();
267
268 return true;
269 } catch (Throwable $throwable) {
270 DB::rollback();
271 throw new Exception(
272 sprintf(
273 /* translators: 1: error message */
274 esc_html__('An error occurred during the app deletion: %s', 'kirki'),
275 esc_html($throwable->getMessage())
276 )
277 );
278 }
279 }
280 }