PluginProbe
VikBooking Hotel Booking Engine & PMS / 1.8.16
VikBooking Hotel Booking Engine & PMS v1.8.16
1.8.16 1.8.15 1.8.14 1.8.13 1.8.12 1.8.11 1.8.10 1.8.9 1.8.6 1.8.7 1.8.8 trunk 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.7 1.6.8 1.6.9 1.7.0 1.7.1 1.7.2 All 37 releases
← All changes | admin/controllers/sampledata.php +506 -0 1.6.0 → 1.8.16 View file →
@@ -1,0 +1,506 @@
1 +<?php
2 +/**
3 + * @package VikBooking
4 + * @subpackage core
5 + * @author E4J s.r.l.
6 + * @copyright Copyright (C) 2019 E4J s.r.l. All Rights Reserved.
7 + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU/GPL
8 + * @link https://vikwp.com
9 + */
10 +
11 +// No direct access
12 +defined('ABSPATH') or die('No script kiddies please!');
13 +
14 +JLoader::import('adapter.mvc.controllers.admin');
15 +
16 +/**
17 + * VikBooking Sampledata controller.
18 + *
19 + * @since 1.3.9
20 + * @see JControllerAdmin
21 + */
22 +class VikBookingControllerSampledata extends JControllerAdmin
23 +{
24 + /**
25 + * AJAX endpoint to load the sample data available.
26 + * JSON encoded string outputted upon result.
27 + *
28 + * @return void
29 + */
30 + public function load()
31 + {
32 + $data = $this->getSampleData();
33 +
34 + echo json_encode($data);
35 + exit;
36 + }
37 +
38 + /**
39 + * AJAX endpoint to install one sample data ID.
40 + * JSON encoded result outputted or exception thrown.
41 + *
42 + * @return void
43 + *
44 + * @throws Exception
45 + */
46 + public function install()
47 + {
48 + $sample_data_id = VikRequest::getInt('sample_data_id', 0, 'request');
49 + if (empty($sample_data_id)) {
50 + throw new Exception('Empty Sample Data ID', 500);
51 + }
52 +
53 + // load all sample data available
54 + $data = $this->getSampleData();
55 +
56 + $sample_data_obj = null;
57 +
58 + foreach ($data as $sdata) {
59 + if (!is_object($sdata) || empty($sdata->id)) {
60 + continue;
61 + }
62 + if ((int)$sdata->id == $sample_data_id) {
63 + $sample_data_obj = new JRegistry($sdata);
64 + break;
65 + }
66 + }
67 +
68 + if (!$sample_data_obj) {
69 + throw new Exception(sprintf('Sample Data ID [%s] not found', $sample_data_id), 404);
70 + }
71 +
72 + // response object
73 + $response = new stdClass;
74 + $response->status = 0;
75 + $response->error = '';
76 +
77 + // download and install selected sample data
78 + try {
79 + $res = $this->downloadSampleData($sample_data_obj);
80 + if ($res) {
81 + $response->status = 1;
82 + }
83 + } catch (Exception $e) {
84 + $response->error = $e->getMessage();
85 + }
86 +
87 + if ($response->status) {
88 + // silently add shortcodes to new WordPress pages
89 + $this->addShortcodesToPages($sample_data_obj);
90 + }
91 +
92 + echo json_encode($response);
93 + exit;
94 + }
95 +
96 + /**
97 + * Returns a list of supported sample data.
98 + *
99 + * @return array A list of sample data.
100 + */
101 + private function getSampleData()
102 + {
103 + // build transient key
104 + $transient = 'vikbooking_sampledata_' . md5(VIKBOOKING_SOFTWARE_VERSION);
105 +
106 + // get cached sample data list
107 + $data = get_transient($transient);
108 +
109 + if ($data) {
110 + // return cached transient
111 + $data = json_decode($data);
112 +
113 + return is_array($data) ? $data : array();
114 + }
115 +
116 + // default empty list
117 + $data = array();
118 +
119 + // instantiate HTTP transport
120 + $http = new JHttp();
121 +
122 + // build end-point URI
123 + $uri = 'https://vikwp.com/api/?task=sampledata.list';
124 +
125 + // build post data
126 + $post = array(
127 + 'sku' => 'vbo',
128 + 'version' => VIKBOOKING_SOFTWARE_VERSION,
129 + );
130 +
131 + // load sample data from server
132 + $response = $http->post($uri, $post);
133 +
134 + if ($response->code == 200) {
135 + // decode response
136 + $data = json_decode($response->body);
137 +
138 + // cache sample data list for an hour
139 + set_transient($transient, json_encode($data), HOUR_IN_SECONDS);
140 + }
141 +
142 + return $data;
143 + }
144 +
145 + /**
146 + * Downloads and installs one sample data.
147 + *
148 + * @param JRegistry $data The sample data registry object.
149 + *
150 + * @return boolean
151 + *
152 + * @uses installSampleData()
153 + */
154 + private function downloadSampleData($data)
155 + {
156 + // instantiate HTTP transport
157 + $http = new JHttp();
158 +
159 + // get selected sample data
160 + $id = $data->get('id');
161 +
162 + JLoader::import('adapter.filesystem.folder');
163 +
164 + // get temporary dir
165 + $tmp = get_temp_dir();
166 +
167 + // clean temporary path
168 + $tmp = rtrim(JPath::clean($tmp), DIRECTORY_SEPARATOR);
169 +
170 + // make sure the folder exists
171 + if (!is_dir($tmp)) {
172 + throw new Exception(sprintf('Temporary folder [%s] does not exist', $tmp), 404);
173 + }
174 +
175 + // make sure the temporary folder is writable
176 + if (!wp_is_writable($tmp)) {
177 + throw new Exception(sprintf('Temporary folder [%s] is not writable', $tmp), 403);
178 + }
179 +
180 + // download end-point
181 + $url = 'https://vikwp.com/api/?task=sampledata.download';
182 +
183 + // init HTTP transport
184 + $http = new JHttp();
185 +
186 + // build sample data file name
187 + $packname = 'sampledata-' . uniqid();
188 +
189 + // build request headers
190 + $headers = array(
191 + // turn on stream to push body within a file
192 + 'stream' => true,
193 + // define the filepath in which the data will be pushed
194 + 'filename' => $tmp . DIRECTORY_SEPARATOR . $packname . '.zip',
195 + // make sure the request is non blocking
196 + 'blocking' => true,
197 + // force timeout to 60 seconds
198 + 'timeout' => 60,
199 + );
200 +
201 + // build post data
202 + $body = array(
203 + 'id' => $id,
204 + );
205 +
206 + // make connection with VikWP server
207 + $response = $http->post($url, $body, $headers);
208 +
209 + if ($response->code != 200) {
210 + // raise error returned by VikWP
211 + throw new Exception($response->body, $response->code);
212 + }
213 +
214 + // make sure the file has been saved
215 + if (!JFile::exists($headers['filename'])) {
216 + throw new Exception('ZIP package could not be saved on disk', 404);
217 + }
218 +
219 + // create destination folder for extracted elements
220 + $dest = $tmp . DIRECTORY_SEPARATOR . $packname;
221 +
222 + // make sure the destination folder doesn't exist
223 + if (JFolder::exists($dest)) {
224 + // remove it before proceeding with the extraction
225 + JFolder::delete($dest);
226 + }
227 +
228 + // import archive class handler
229 + JLoader::import('adapter.filesystem.archive');
230 +
231 + // the package was downloaded successfully, let's extract it (onto TMP folder)
232 + $extracted = JArchive::extract($headers['filename'], $dest);
233 +
234 + // we no longer need the archive
235 + JFile::delete($headers['filename']);
236 +
237 + if (!$extracted) {
238 + // an error occurred while extracting the files
239 + throw new Exception(sprintf('Cannot extract files to [%s]', $tmp), 500);
240 + }
241 +
242 + // make sure the folder is intact
243 + if (!JFolder::exists($dest)) {
244 + // impossible to access the extracted elements
245 + throw new Exception(sprintf('Cannot access extracted elements from [%s] folder', $dest), 404);
246 + }
247 +
248 + $error = null;
249 +
250 + try {
251 + // run sample data installation
252 + $this->installSampleData($dest);
253 + } catch (Exception $e) {
254 + // safely catch error to finalize process
255 + $error = $e;
256 + }
257 +
258 + // process complete, clean up the temporary folder before exiting
259 + JFolder::delete($dest);
260 +
261 + // in case of error, propagate it after deleting the extracted folder
262 + if ($error) {
263 + throw $error;
264 + }
265 +
266 + return true;
267 + }
268 +
269 + /**
270 + * Interprets the manifest contained within the folder
271 + * to install the sample data.
272 + *
273 + * @param string $folder The sample data folder.
274 + *
275 + * @return void
276 + *
277 + * @throws Exception
278 + *
279 + * @uses uninstallShortcodes()
280 + * @uses installSqlRole()
281 + * @uses installFilesRole()
282 + */
283 + private function installSampleData($folder)
284 + {
285 + // load manifest.json file
286 + if (!is_file($folder . DIRECTORY_SEPARATOR . 'manifest.json')) {
287 + // missing JSON manifest
288 + throw new Exception('Manifest file not found', 404);
289 + }
290 +
291 + // open manifest file in read mode
292 + $manifestFile = fopen($folder . DIRECTORY_SEPARATOR . 'manifest.json', 'r');
293 +
294 + $manifest = '';
295 +
296 + // load manifest content
297 + while (!feof($manifestFile)) {
298 + $manifest .= fread($manifestFile, 8192);
299 + }
300 +
301 + // close file
302 + fclose($manifestFile);
303 +
304 + // decode JSON
305 + $manifest = json_decode($manifest);
306 +
307 + if (!$manifest) {
308 + // unable to JSON decode manifest
309 + throw new Exception('Manifest file contains an invalid JSON', 500);
310 + }
311 +
312 + $dbo = JFactory::getDbo();
313 +
314 + // look for uninstall queries
315 + if (isset($manifest->uninstall)) {
316 + // iterate queries to clean any existing records
317 + foreach ($manifest->uninstall as $q) {
318 + try {
319 + // check if we are uninstalling the shortcodes
320 + if (preg_match("/vikbooking_wpshortcodes/i", $q)) {
321 + // uninstall the existing shortcodes
322 + $this->uninstallShortcodes();
323 + }
324 +
325 + // launch query
326 + $dbo->setQuery($q);
327 + $dbo->execute();
328 + } catch (Exception $e) {
329 + // malformed query, suppress error and go ahead
330 + if (VIKBOOKING_DEBUG) {
331 + // propagate error in case of debug mode enabled
332 + throw $e;
333 + }
334 + }
335 + }
336 + }
337 +
338 + // look for installers
339 + if (isset($manifest->installers)) {
340 + // iterate installers
341 + foreach ($manifest->installers as $install) {
342 + try {
343 + // switch case role to invoke the proper installation method
344 + switch ($install->role) {
345 + case 'sql':
346 + case 'insert':
347 + $this->installSqlRole($install->data);
348 + break;
349 +
350 + case 'media':
351 + case 'folder':
352 + $this->installFilesRole($install->data->destination, $install->data->files, $folder);
353 + break;
354 + }
355 + } catch (Exception $e) {
356 + // malformed role, suppress error and go ahead
357 + if (VIKBOOKING_DEBUG) {
358 + // propagate error in case of debug mode enabled
359 + throw $e;
360 + }
361 + }
362 + }
363 + }
364 + }
365 +
366 + /**
367 + * Executes the specified queries.
368 + *
369 + * @param mixed $queries Either a query string or an array.
370 + *
371 + * @return void
372 + */
373 + private function installSqlRole($queries)
374 + {
375 + if (!is_array($queries)) {
376 + $queries = array($queries);
377 + }
378 +
379 + $dbo = JFactory::getDbo();
380 +
381 + // iterate queries one by one
382 + foreach ($queries as $q) {
383 + $dbo->setQuery($q);
384 + $dbo->execute();
385 + }
386 + }
387 +
388 + /**
389 + * Moves the files into the related folders.
390 + *
391 + * @param string $dest The destination folder.
392 + * @param mixed $files Either a file or an array.
393 + * @param string $dir The current directory.
394 + *
395 + * @return void
396 + */
397 + private function installFilesRole($dest, $files, $dir)
398 + {
399 + // load update manager class for backup/mirroring of files
400 + VikBookingLoader::import('update.manager');
401 +
402 + // set destination by prepending plugin base path
403 + $dest = VIKBOOKING_BASE . DIRECTORY_SEPARATOR . str_replace('/', DIRECTORY_SEPARATOR, $dest);
404 +
405 + // make sure to always parse an array
406 + if (!is_array($files)) {
407 + $files = array($files);
408 + }
409 +
410 + // copy files and create a backup copy from each file
411 + foreach ($files as $file) {
412 + // fetch file path
413 + $path = JPath::clean($dir . DIRECTORY_SEPARATOR . $file);
414 +
415 + // fetch destination file path
416 + $destFile = JPath::clean($dest . DIRECTORY_SEPARATOR . basename($file));
417 +
418 + // copy file in its destination
419 + $res = JFile::copy($path, $destFile);
420 +
421 + if ($res) {
422 + // trigger mirroring action for new file uploaded
423 + VikBookingUpdateManager::triggerUploadBackup($destFile);
424 + }
425 + }
426 + }
427 +
428 + /**
429 + * Uninstalls all the pages that have been assigned
430 + * to the existing shortcodes.
431 + *
432 + * @return void
433 + */
434 + private function uninstallShortcodes()
435 + {
436 + // get shortcode admin model
437 + $model = JModel::getInstance('vikbooking', 'shortcodes');
438 +
439 + // get all existing shortcodes
440 + $shortcodes = $model->all(array('createdon', 'post_id'));
441 +
442 + // iterate all shortcodes found
443 + foreach ($shortcodes as $shortcode) {
444 + // make sure the shortcode has been assigned to a post
445 + if ($shortcode->post_id) {
446 + // get post details
447 + $post = get_post((int) $shortcode->post_id);
448 +
449 + // convert shortcode creation date
450 + $shortcode->createdon = new JDate($shortcode->createdon);
451 + // convert post creation date
452 + $post->post_date_gmt = new JDate($post->post_date_gmt);
453 +
454 + // compare ephocs and make sure the post was not created before the shortcode
455 + if ((int) $shortcode->createdon->format('U') <= (int) $post->post_date_gmt->format('U')) {
456 + // permanently delete post
457 + wp_delete_post($post->ID, $force_delete = true);
458 + }
459 + }
460 + }
461 + }
462 +
463 +
464 + /**
465 + * Finalizes the installation of a sample data by creating one
466 + * new WordPress page/post for each Shortcode installed.
467 + *
468 + * @param JRegistry $data The (optional) sample data chosen registry object.
469 + *
470 + * @return boolean
471 + */
472 + private function addShortcodesToPages($data = null)
473 + {
474 + $model = JModel::getInstance('vikbooking', 'shortcodes');
475 +
476 + $shortcodes = $model->all();
477 +
478 + if (!is_array($shortcodes) || !count($shortcodes)) {
479 + return false;
480 + }
481 +
482 + foreach ($shortcodes as $item) {
483 + if (!empty($item->post_id)) {
484 + // shortcode already linked to a page
485 + continue;
486 + }
487 +
488 + /**
489 + * Add a new page (we allow a WP_ERROR to be returned in case of failure).
490 + * This should automatically trigger the hook that we use to link the Shortcode
491 + * to the new page/post ID, and so there's no need to update the item.
492 + */
493 + $new_page_id = wp_insert_post(array(
494 + 'post_title' => (!empty($item->name) ? $item->name : JText::translate($item->title)),
495 + 'post_content' => $item->shortcode,
496 + 'post_status' => 'publish',
497 + 'post_type' => 'page',
498 + ), true);
499 +
500 + // we ignore if the page was created or if an error occurred
501 + }
502 +
503 + return true;
504 + }
505 +
506 +}