PluginProbe
Vision – Interactive Image Map with Hotspots Builder / 1.8.2
Vision – Interactive Image Map with Hotspots Builder v1.8.2
1.12.2 1.12.1 1.12.0 1.11.0 1.6.0 1.6.1 1.6.2 1.7.1 1.7.2 1.7.3 1.8.1 1.8.2 1.8.3 1.8.4 1.8.5 1.8.6 1.9.0 1.9.1 1.9.2 1.9.3 1.9.4 1.9.5 1.9.6 1.9.7 1.9.8 All 33 releases
vision / includes / plugin.php

plugin.php in Vision – Interactive Image Map with Hotspots Builder 1.8.2, at includes/plugin.php

1,307 lines 50.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 defined('ABSPATH') || exit;
3
4 class Vision_Builder {
5 private $pluginBasename = NULL;
6
7 private $ajax_action_item_update = NULL;
8 private $ajax_action_item_update_status = NULL;
9 private $ajax_action_settings_update = NULL;
10 private $ajax_action_settings_get = NULL;
11 private $ajax_action_delete_data = NULL;
12 private $ajax_action_modal = NULL;
13
14 private $vision_map_id = null;
15 private $vision_map_version = null;
16 private $shortcodes = [];
17
18 function __construct($pluginBasename) {
19 $this->pluginBasename = $pluginBasename;
20 }
21
22 function run() {
23 $upload_dir = wp_upload_dir();
24 $plugin_url = plugin_dir_url(dirname(__FILE__));
25
26 define('VISION_PLUGIN_UPLOAD_DIR', wp_normalize_path($upload_dir['basedir'] . '/vision'));
27 define('VISION_PLUGIN_UPLOAD_URL', set_url_scheme($upload_dir['baseurl'] . '/vision/'));
28
29 define('VISION_PLUGIN_PLAN', 'lite');
30
31 $user = wp_get_current_user(); //is_super_admin()
32 $allowed_roles = $this->getAllowedRoles();
33 if((array_intersect($allowed_roles, $user->roles) || current_user_can('manage_options')) && is_admin()) {
34 $this->ajax_action_item_update = 'vision_ajax_item_update';
35 $this->ajax_action_item_update_status = 'vision_ajax_item_update_status';
36 $this->ajax_action_settings_update = 'vision_ajax_settings_update';
37 $this->ajax_action_settings_get = 'vision_ajax_settings_get';
38 $this->ajax_action_delete_data = 'vision_ajax_delete_data';
39 $this->ajax_action_modal = 'vision_ajax_modal';
40
41 load_plugin_textdomain('vision', false, dirname(dirname(plugin_basename(__FILE__))) . '/languages/');
42
43 add_action('admin_menu', [$this, 'admin_menu']);
44 add_action('admin_footer', [$this, 'admin_footer']);
45 add_action('admin_notices', [$this, 'admin_notices']);
46 add_action('in_admin_header', [$this, 'in_admin_header']);
47 add_action('wp_loaded', [$this, 'page_redirects']);
48
49 // important, because ajax has another url
50 add_action('wp_ajax_' . $this->ajax_action_item_update, [$this, 'ajax_item_update']);
51 add_action('wp_ajax_' . $this->ajax_action_item_update_status, [$this, 'ajax_item_update_status']);
52 add_action('wp_ajax_' . $this->ajax_action_settings_update, [$this, 'ajax_settings_update']);
53 add_action('wp_ajax_' . $this->ajax_action_settings_get, [$this, 'ajax_settings_get']);
54 add_action('wp_ajax_' . $this->ajax_action_delete_data, [$this, 'ajax_delete_data']);
55 add_action('wp_ajax_' . $this->ajax_action_modal, [$this, 'ajax_modal']);
56 } else {
57 add_shortcode(VISION_SHORTCODE_NAME, [$this, 'shortcode']);
58 }
59
60 // only logged users with right roles can preview a vision map
61 if(array_intersect($allowed_roles, $user->roles) || current_user_can('manage_options')) {
62 add_filter('do_parse_request', [$this, 'do_parse_request'], 10, 3);
63 }
64
65 add_action('rest_api_init', array($this, 'rest_api_init'));
66 }
67
68 function rest_api_init() {
69 register_rest_route(
70 VISION_PLUGIN_REST_URL, '/item/(?P<id>\d+)',
71 [
72 'methods' => 'GET',
73 'callback' => [$this, 'rest_api_get_item'],
74 'permission_callback' => [$this, 'rest_api_permissions_check']
75 ]
76 );
77 }
78
79 function rest_api_get_item($request) {
80 $id = intval( $request->get_param('id') );
81 $preview = boolval( $request->get_param('preview') );
82
83 global $wpdb;
84 $table = $wpdb->prefix . VISION_PLUGIN_NAME;
85
86 // phpcs:disable WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
87 $sql = $wpdb->prepare("SELECT * FROM {$table} WHERE id=%d AND NOT deleted", $id);
88 $item = $wpdb->get_row($sql, OBJECT);
89 // phpcs:enable
90
91 $config = null;
92 if($item->active) {
93 $config = unserialize($item->config);
94 } else if($preview) {
95 $user = wp_get_current_user();
96 $allowed_roles = $this->getAllowedRoles();
97
98 if(array_intersect($allowed_roles, $user->roles) || current_user_can('manage_options')) {
99 $config = unserialize($item->config);
100 }
101 }
102
103 if($config) {
104 return new WP_REST_Response($config);
105 }
106 return new WP_REST_Response(null, 404);
107 }
108
109 function rest_api_permissions_check() {
110 return true;
111 }
112
113 function filesystem_method() {
114 return 'direct';
115 }
116
117 function request_filesystem_credentials() {
118 return true;
119 }
120
121 function getFileSystem() {
122 global $wp_filesystem;
123 $result = true;
124
125 if(!$wp_filesystem) {
126 require_once(ABSPATH . '/wp-admin/includes/file.php');
127
128 add_filter('filesystem_method', [$this, 'filesystem_method']);
129 add_filter('request_filesystem_credentials', [$this, 'request_filesystem_credentials']);
130
131 $credentials = request_filesystem_credentials(site_url(), '', true, false, null);
132
133 $result = WP_Filesystem($credentials);
134
135 remove_filter('filesystem_method', [$this, 'filesystem_method']);
136 remove_filter('request_filesystem_credentials', [$this, 'request_filesystem_credentials']);
137 }
138
139 if($result)
140 return $wp_filesystem;
141 return null;
142 }
143
144 function joinPaths() {
145 $paths = [];
146
147 foreach(func_get_args() as $arg) {
148 if($arg !== '') {
149 $paths[] = $arg;
150 }
151 }
152
153 return preg_replace('#/+#','/',join('/', $paths));
154 }
155
156 function joinUrls() {
157 $urls = [];
158
159 foreach(func_get_args() as $arg) {
160 if($arg !== '') {
161 $urls[] = $arg;
162 }
163 }
164
165 return preg_replace('/([^:])(\/{2,})/','$1/',join('/', $urls));
166 }
167
168 function IsNullOrEmptyString($str) {
169 return(!isset($str) || trim($str)==='');
170 }
171
172 function getAllowedRoles() {
173 $allowed_roles = ['administrator'];
174
175 $settings_key = 'vision_settings';
176 $settings_value = get_option($settings_key);
177 if($settings_value) {
178 $settings = unserialize($settings_value);
179 if(is_array($settings->roles)) $allowed_roles = array_merge($allowed_roles, $settings->roles);
180 }
181
182 return $allowed_roles;
183 }
184
185 function getLoaderGlobals($timestamp) {
186 $plugin_url = plugin_dir_url(dirname(__FILE__));
187
188 $globals = [
189 'plan' => VISION_PLUGIN_PLAN,
190 'version' => $timestamp,
191 'effects_url' => $plugin_url . 'assets/css/vision-effects.css',
192 'theme_base_url' => $plugin_url . 'assets/themes/',
193 'plugin_base_url' => $plugin_url . 'assets/vendor/vision/',
194 'plugin_upload_base_url' => VISION_PLUGIN_UPLOAD_URL,
195 'plugin_version' => VISION_PLUGIN_VERSION,
196 'ssl' => is_ssl(),
197 'api' => [
198 'nonce' => wp_create_nonce( 'wp_rest' ),
199 'url' => esc_url_raw( rest_url( VISION_PLUGIN_REST_URL ) )
200 ]
201 ];
202
203 return $globals;
204 }
205
206 function embedLoader($in_footer, $timestamp) {
207 $plugin_url = plugin_dir_url(dirname(__FILE__));
208 wp_enqueue_script('vision_loader', $plugin_url . 'assets/js/loader.js', ['jquery'], VISION_PLUGIN_VERSION, $in_footer);
209 wp_localize_script('vision_loader', 'vision_globals', $this->getLoaderGlobals($timestamp));
210 }
211
212 /**
213 * generate main css text
214 */
215 function getMainCss($itemData, $itemId) {
216 $upload_dir = wp_upload_dir();
217
218 // create main css
219 $main_css = '';
220 $main_css .= '.vision-map-' . $itemId . ' {' . PHP_EOL;
221
222 $main_css .= (!$this->IsNullOrEmptyString($itemData->background->color) ? 'background-color:' . $itemData->background->color . ';' . PHP_EOL : '');
223 if(!$this->IsNullOrEmptyString($itemData->background->image->url)) {
224 $imageUrl = ($itemData->background->image->relative ? $upload_dir['baseurl'] : '') . $itemData->background->image->url;
225 $main_css .= 'background-image:url(' . $imageUrl . ');' . PHP_EOL;
226 }
227 $main_css .= ($itemData->background->size ? 'background-size:' . $itemData->background->size . ';' . PHP_EOL : '');
228 $main_css .= ($itemData->background->repeat ? 'background-repeat:' . $itemData->background->repeat . ';' . PHP_EOL : '');
229 $main_css .= ($itemData->background->position ? 'background-position:' . $itemData->background->position . ';' . PHP_EOL : '');
230
231 $main_css .= '}' . PHP_EOL;
232
233 $layerId = 0;
234 foreach($itemData->layers as $layerKey => $layer) {
235 if(!$layer->visible) {
236 continue;
237 }
238
239 $layerId++;
240 $layerSelector = '.vision-map-' . $itemId . ' .vision-layers [data-layer-id="' . $layer->id . '"] .vision-body';
241
242 // main
243 $main_css .= $layerSelector . ' {' . PHP_EOL;
244 switch($layer->type) {
245 case 'link': {
246 $main_css .= ($layer->link->normalColor ? 'background-color:' . $layer->link->normalColor . ';' . PHP_EOL : '');
247 $main_css .= ($layer->link->radius != NULL ? 'border-radius:' . $layer->link->radius . ';' . PHP_EOL : '');
248 } break;
249 case 'image': {
250 $main_css .= (!$this->IsNullOrEmptyString($layer->image->background->color) ? 'background-color:' . $layer->image->background->color . ';' . PHP_EOL : '');
251 if(!$this->IsNullOrEmptyString($layer->image->background->file->url)) {
252 $imageUrl = ($layer->image->background->file->relative ? $upload_dir['baseurl'] : '') . $layer->image->background->file->url;
253 $main_css .= 'background-image:url(' . $imageUrl . ');' . PHP_EOL;
254 }
255 $main_css .= ($layer->image->background->size ? 'background-size:' . $layer->image->background->size . ';' . PHP_EOL : '');
256 $main_css .= ($layer->image->background->repeat ? 'background-repeat:' . $layer->image->background->repeat . ';' . PHP_EOL : '');
257 $main_css .= ($layer->image->background->position ? 'background-position:' . $layer->image->background->position . ';' . PHP_EOL : '');
258 } break;
259 case 'text': {
260 $main_css .= (!$this->IsNullOrEmptyString($layer->text->background->color) ? 'background-color:' . $layer->text->background->color . ';' . PHP_EOL : '');
261 if(!$this->IsNullOrEmptyString($layer->text->background->file->url)) {
262 $imageUrl = ($layer->text->background->file->relative ? $upload_dir['baseurl'] : '') . $layer->text->background->file->url;
263 $main_css .= 'background-image:url(' . $imageUrl . ');' . PHP_EOL;
264 }
265 $main_css .= ($layer->text->background->size ? 'background-size:' . $layer->text->background->size . ';' . PHP_EOL : '');
266 $main_css .= ($layer->text->background->repeat ? 'background-repeat:' . $layer->text->background->repeat . ';' . PHP_EOL : '');
267 $main_css .= ($layer->text->background->position ? 'background-position:' . $layer->text->background->position . ';' . PHP_EOL : '');
268
269 $main_css .= ($layer->text->font ? 'font-family:"' . str_replace('+', ' ', $layer->text->font) . '",sans-serif;' . PHP_EOL : '');
270 $main_css .= ($layer->text->color ? 'color:' . $layer->text->color . ';' . PHP_EOL : '');
271 $main_css .= ($layer->text->size != NULL ? 'font-size:' . $layer->text->size . 'px;' . PHP_EOL : '');
272 $main_css .= ($layer->text->lineHeight != NULL ? 'line-height:' . $layer->text->lineHeight . 'px;' . PHP_EOL : '');
273 $main_css .= ($layer->text->align ? 'text-align:' . $layer->text->align . ';' . PHP_EOL : '');
274 $main_css .= ($layer->text->letterSpacing != NULL ? 'letter-spacing:' . $layer->text->letterSpacing . 'px;' . PHP_EOL : '');
275 } break;
276 }
277 $main_css .= '}' . PHP_EOL;
278
279 if($layer->type == 'link') {
280 $main_css .= $layerSelector . ':hover {' . PHP_EOL;
281 $main_css .= ($layer->link->hoverColor ? 'background-color:' . $layer->link->hoverColor . ';' . PHP_EOL : '');
282 $main_css .= '}' . PHP_EOL;
283 }
284 }
285
286 return $main_css;
287 }
288
289 /**
290 * Shortcode output for the plugin
291 */
292 function shortcode($atts) {
293 extract(shortcode_atts(['id'=>0, 'slug'=>NULL, 'class'=>NULL], $atts, VISION_SHORTCODE_NAME));
294
295 if(!$id && !$slug) {
296 return '<p>' . esc_html__('Error: invalid vision identifier attribute', 'vision') . '</p>';
297 }
298
299 $id = intval($id, 10);
300 $slug = sanitize_key($slug);
301 $class = sanitize_text_field($class);
302
303 global $wpdb;
304 $table = $wpdb->prefix . VISION_PLUGIN_NAME;
305 $upload_dir = wp_upload_dir();
306
307 // phpcs:disable WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
308 $sql = ($id ? $wpdb->prepare("SELECT * FROM {$table} WHERE id=%d AND NOT deleted", $id) : $wpdb->prepare("SELECT * FROM {$table} WHERE slug=%s AND NOT deleted LIMIT 0, 1", $slug));
309 $item = $wpdb->get_row($sql, OBJECT);
310 // phpcs:enable
311
312 $preview = filter_input(INPUT_GET, 'preview', FILTER_SANITIZE_NUMBER_INT);
313
314 if($item && ($item->active || (!$item->active && $preview == 1))) {
315 $version = strtotime(mysql2date('d M Y H:i:s', $item->modified));
316 $itemData = unserialize($item->data);
317 $id = $item->id;
318 $id_postfix = strtolower(wp_generate_password(5, false)); // generate unique postfix for $id to avoid clashes with multiple same shortcode use
319 $id_element = 'vision-' . $id . '-' . $id_postfix;
320
321 array_push($this->shortcodes, ['id' => $item->id, 'version' => $version]);
322
323 if(sizeof($this->shortcodes) == 1) {
324 $this->embedLoader(true, $version);
325 }
326
327 ob_start(); // turn on buffering
328
329 echo '<!-- vision begin -->' . PHP_EOL;
330 echo '<div ';
331 echo (property_exists($itemData, 'containerId') && $itemData->containerId ? 'id="' . esc_attr($itemData->containerId) . '" ':'');
332 echo 'class="vision-map vision-map-' . esc_attr($id . ($class ? ' ' . $class : '')) . '"';
333 echo 'data-json-src="'. esc_url_raw( rest_url( VISION_PLUGIN_REST_URL ) ) . '/item/' . esc_attr($item->id) . ($preview ? '?preview=1' : '') . '" ';
334 echo 'data-item-id="' . esc_attr($item->id) . '" ';
335 echo 'tabindex="1" ';
336 echo '>' . PHP_EOL;
337 if (property_exists($itemData, 'image')) {
338 $upload_dir = wp_upload_dir();
339 $imageUrl = ($itemData->image->relative ? $upload_dir['baseurl'] : '') . $itemData->image->url;
340 echo "<img src='" . esc_url($imageUrl). "' class='vision-img-placeholder' width='100%'>";
341 }
342
343 //=============================================
344 // STORE BEGIN
345 echo '<div class="vision-store" style="display:none;">' . PHP_EOL;
346 echo '<div class="vision-layers-data">' . PHP_EOL;
347 foreach($itemData->layers as $layerKey => $layer) {
348 if(!$layer->visible) {
349 continue;
350 }
351
352 //=============================================
353 // LAYER BEGIN
354 echo '<div class="vision-layer" data-layer-id="' . esc_attr($layer->id) . '">';
355
356 if($layer->contentData) {
357 echo do_shortcode($layer->contentData);
358 }
359
360 if($layer->type == 'text') {
361 echo wp_kses_post($layer->text->data);
362 }
363
364 echo '</div>' . PHP_EOL;
365 // LAYER END
366 //=============================================
367 }
368 echo '</div>' . PHP_EOL;
369
370 echo '<div class="vision-tooltips-data">' . PHP_EOL;
371 foreach($itemData->layers as $layerKey => $layer) {
372 if(!$layer->visible) {
373 continue;
374 }
375
376 //=============================================
377 // TOOLTIP BEGIN
378 echo '<div class="vision-data" data-layer-id="' . esc_attr($layer->id) . '">';
379 echo do_shortcode($layer->tooltip->data);
380 echo '</div>' . PHP_EOL;
381 // TOOLTIP END
382 //=============================================
383 }
384 echo '</div>' . PHP_EOL;
385
386 echo '<div class="vision-popovers-data">' . PHP_EOL;
387 foreach($itemData->layers as $layerKey => $layer) {
388 if(!$layer->visible) {
389 continue;
390 }
391
392 //=============================================
393 // POPOVER BEGIN
394 echo '<div class="vision-data" data-layer-id="' . esc_attr($layer->id) . '">';
395 echo do_shortcode($layer->popover->data);
396 echo '</div>' . PHP_EOL;
397 // POPOVER END
398 //=============================================
399 }
400 echo '</div>' . PHP_EOL;
401
402 echo '</div>' . PHP_EOL;
403 // STORE END
404 //=============================================
405
406 echo '</div>' . PHP_EOL;
407 echo '<!-- vision end -->' . PHP_EOL;
408
409 $output = ob_get_contents(); // get the buffered content into a var
410 ob_end_clean(); // clean buffer
411
412 return $output;
413 }
414
415 return '<p>' . esc_html__('Error: the vision item can’t be found', 'vision') . '</p>';
416 }
417
418 /**
419 * Run a filter to obtain some custom url settings, compare them to the current url
420 * and if a match is found the custom callback is fired, the custom view is loaded
421 * and request is stopped.
422 */
423 function do_parse_request($result) {
424 if(current_filter() !== 'do_parse_request') {
425 return $result;
426 }
427
428 if(preg_match('/vision\/map\/([a-z0-9_-]+)/', $_SERVER['REQUEST_URI'], $matches)) {
429 $preview = filter_input(INPUT_GET, 'preview', FILTER_SANITIZE_NUMBER_INT);
430
431 global $wpdb;
432 $table = $wpdb->prefix . VISION_PLUGIN_NAME;
433 $shortcode = false;
434
435 if(is_numeric($matches[1])) {
436 $vision_map_id = $matches[1];
437
438 if($vision_map_id != null) {
439 // phpcs:disable WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
440 $sql = $wpdb->prepare("SELECT * FROM {$table} WHERE id=%d AND NOT deleted", $vision_map_id);
441 $item = $wpdb->get_row($sql, OBJECT);
442 // phpcs:enable
443
444 if($item && ($item->active || (!$item->active && $preview == 1))) {
445 $this->vision_map_id = $item->id;
446 $this->vision_map_version = strtotime(mysql2date('Y-m-d H:i:s', $item->modified));
447 $shortcode = true;
448 }
449 }
450 } else {
451 $vision_map_slug = $matches[1];
452
453 if($vision_map_slug != null) {
454 // phpcs:disable WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
455 $sql = $wpdb->prepare("SELECT * FROM {$table} WHERE slug=%s AND NOT deleted", $vision_map_slug);
456 $item = $wpdb->get_row($sql, OBJECT);
457 // phpcs:enable
458
459 if($item && ($item->active || (!$item->active && $preview == 1))) {
460 $this->vision_map_id = $item->id;
461 $this->vision_map_version = strtotime(mysql2date('Y-m-d H:i:s', $item->modified));
462
463 $shortcode = true;
464 }
465 }
466 }
467
468 if($shortcode) {
469 require_once(plugin_dir_path(dirname(__FILE__)) . 'includes/page-preview.php');
470 exit();
471 }
472 }
473
474 return $result;
475 }
476
477 /**
478 * Prepare upload directory
479 */
480 function admin_notices() {
481 $page = sanitize_key(filter_input(INPUT_GET, 'page', FILTER_DEFAULT));
482 if(!($page==='vision' || $page==='vision_item')) {
483 return;
484 }
485
486 if(!file_exists(VISION_PLUGIN_UPLOAD_DIR)) {
487 wp_mkdir_p(VISION_PLUGIN_UPLOAD_DIR);
488 }
489
490 if(!file_exists(VISION_PLUGIN_UPLOAD_DIR)) {
491 echo '<div class="notice notice-error is-dismissible">';
492 echo '<p>' . esc_html__('The plugin upload directory could not be created', 'vision') . '</p>';
493 echo '<p>' . esc_html__('Please run the following commands in order to make the directory', 'vision') . '<br>';
494 echo '<b>mkdir ' . esc_attr(VISION_PLUGIN_UPLOAD_DIR) . '</b><br>';
495 echo '<b>chmod 777 ' . esc_attr(VISION_PLUGIN_UPLOAD_DIR) . '</b></p>';
496 echo '</div>';
497 return;
498 }
499
500 if(!wp_is_writable(VISION_PLUGIN_UPLOAD_DIR)) {
501 echo '<div class="notice notice-error is-dismissible">';
502 echo '<p>' . esc_html__('The plugin upload directory is not writable, therefore the css and js files cannot be saved.', 'vision') . '</p>';
503 echo '<p>' . esc_html__('Please run the following commands in order to make the directory', 'vision') . '<br>';
504 echo '<b>chmod 777 ' . esc_attr(VISION_PLUGIN_UPLOAD_DIR) . '</b></p>';
505 echo '</div>';
506 return;
507 }
508
509 if(!file_exists(VISION_PLUGIN_UPLOAD_DIR . '/' . 'index.php')) {
510 $data = '<?php' . PHP_EOL . '// silence is golden' . PHP_EOL . '?>';
511
512 $wp_filesystem = $this->getFileSystem();
513 $wp_filesystem->put_contents(VISION_PLUGIN_UPLOAD_DIR . '/' . 'index.php', $data);
514 }
515 }
516
517 /**
518 * Fires at the beginning of the content section in an admin page
519 */
520 function in_admin_header() {
521 $page = sanitize_key(filter_input(INPUT_GET, 'page', FILTER_DEFAULT));
522
523 if(!(($page==='vision') || ($page==='vision_item') || ($page==='vision_settings'))) {
524 return;
525 }
526
527 remove_all_actions('admin_notices');
528 remove_all_actions('all_admin_notices');
529 add_action('admin_notices', [$this, 'admin_notices']);
530 }
531
532 /**
533 * Register the administration menu for this plugin into the WordPress Dashboard menu.
534 */
535 function admin_menu() {
536 // add "edit_posts" if we want to give access to author, editor and contributor roles
537 add_menu_page(esc_html__('Vision', 'vision'), esc_html__('Vision', 'vision'), 'read', 'vision', [$this, 'admin_menu_page_items'], 'dashicons-format-image');
538 add_submenu_page('vision', esc_html__('Vision', 'vision'), esc_html__('All Items', 'vision'), 'read', 'vision', [$this, 'admin_menu_page_items']);
539 add_submenu_page('vision', esc_html__('Vision', 'vision'), esc_html__('Add New', 'vision'), 'read', 'vision_item', [$this, 'admin_menu_page_item']);
540 add_submenu_page('vision', esc_html__('Vision', 'vision'), esc_html__('Settings', 'vision'), 'manage_options', 'vision_settings', [$this, 'admin_menu_page_settings']);
541
542 add_submenu_page('vision', esc_html__('Vision', 'vision'), esc_html__('Upgrade to Pro', 'vision'), 'manage_options', 'vision_upgrade_to_pro', [$this, 'admin_menu_page_upgrade_to_pro']);
543
544 }
545
546 function admin_footer() {
547 if(get_current_screen() && get_current_screen()->base !== 'plugins') {
548 return;
549 }
550
551 $globals = [
552 'token' => $this->get_token(),
553 'ajax' => [
554 'url' => VISION_FEEDBACK_URL
555 ]
556 ];
557
558 wp_enqueue_style('vision-feedback', VISION_PLUGIN_URL . 'assets/css/feedback.css', [], VISION_PLUGIN_VERSION);
559 wp_enqueue_script('vision-feedback', VISION_PLUGIN_URL . 'assets/js/feedback.js', ['jquery'], VISION_PLUGIN_VERSION, false);
560 wp_localize_script('vision-feedback', 'vision_feedback_globals', $globals);
561
562 require_once(plugin_dir_path(dirname(__FILE__)) . 'templates/feedback.php');
563 }
564
565 function get_token() {
566 global $wp_version;
567 $current_user = wp_get_current_user();
568
569 $data = [
570 'plugin_name' => VISION_PLUGIN_NAME,
571 'plugin_version' => VISION_PLUGIN_VERSION,
572 'wordpress' => $wp_version,
573 'php' => PHP_VERSION,
574 'email' => $current_user->user_email,
575 'site' => trim(str_replace(['http://', 'https://'], '', get_site_url()), '/')
576 ];
577 return base64_encode(wp_json_encode($data));
578 }
579
580 /**
581 * Custom redirects
582 */
583 function page_redirects() {
584 $page = sanitize_key(filter_input(INPUT_GET, 'page', FILTER_DEFAULT));
585
586 if($page==='vision') {
587 $action = sanitize_key(filter_input(INPUT_GET, 'action', FILTER_DEFAULT));
588 if($action) {
589 $url = remove_query_arg(['action', 'id', '_wpnonce'], $_SERVER['REQUEST_URI']);
590 header('Refresh:0; url="' . $url . '"', true, 303);
591 //wp_redirect($url); // does not work delete and dublicate operations on XAMPP
592 }
593 }
594 }
595
596 /**
597 * Show admin menu items page
598 */
599 function admin_menu_page_items() {
600 $page = sanitize_key(filter_input(INPUT_GET, 'page', FILTER_DEFAULT));
601
602 if($page==='vision') {
603 $plugin_url = plugin_dir_url( dirname(__FILE__) );
604 $upload_dir = wp_upload_dir();
605
606 wp_enqueue_style('vision_admin', $plugin_url . 'assets/css/admin.css', [], VISION_PLUGIN_VERSION, 'all' );
607 wp_enqueue_style('vision_lucide', $plugin_url . 'assets/vendor/lucide/lucide.css', [], VISION_PLUGIN_VERSION, 'all' );
608 wp_enqueue_script('vision_admin', $plugin_url . 'assets/js/admin.js', ['jquery'], VISION_PLUGIN_VERSION, false );
609
610 // global settings to help ajax work
611 $globals = [
612 'plan' => VISION_PLUGIN_PLAN,
613 'msg_pro_title' => esc_html__('Available only in Pro version', 'vision'),
614 'upload_url' => $upload_dir['baseurl'],
615 'ajax_url' => admin_url('admin-ajax.php'),
616 'ajax_nonce' => wp_create_nonce('vision_ajax' ),
617 'ajax_msg_error' => esc_html__('Uncaught Error', 'vision') //Look at the console (F12 or Ctrl+Shift+I, Console tab) for more information
618 ];
619 $globals['ajax_action_update'] = $this->ajax_action_item_update_status;
620
621 require_once(plugin_dir_path( dirname(__FILE__) ) . 'includes/list-table-items.php');
622 require_once(plugin_dir_path( dirname(__FILE__) ) . 'includes/page-items.php');
623
624 wp_localize_script('vision_admin', 'vision_globals', $globals);
625 }
626 }
627
628 /**
629 * Show admin menu item page
630 */
631 function admin_menu_page_item() {
632 $page = sanitize_key(filter_input(INPUT_GET, 'page', FILTER_DEFAULT));
633 if($page==='vision_item') {
634 $plugin_url = plugin_dir_url(dirname(__FILE__));
635 $upload_dir = wp_upload_dir();
636
637 wp_enqueue_style('vision_admin', $plugin_url . 'assets/css/admin.css', [], VISION_PLUGIN_VERSION, 'all' );
638 wp_enqueue_style('vision_lucide', $plugin_url . 'assets/vendor/lucide/lucide.css', [], VISION_PLUGIN_VERSION, 'all' );
639 wp_enqueue_style('vision_vision_effects', $plugin_url . 'assets/css/vision-effects.css', [], VISION_PLUGIN_VERSION, 'all' );
640
641 wp_enqueue_script('vision_ace', $plugin_url . 'assets/vendor/ace/ace.js', [], VISION_PLUGIN_VERSION, false );
642 wp_enqueue_script('vision_url', $plugin_url . 'assets/vendor/url/url.js', [], VISION_PLUGIN_VERSION, false );
643 wp_enqueue_script('vision_admin', $plugin_url . 'assets/js/admin.js', ['jquery'], VISION_PLUGIN_VERSION, false );
644
645 wp_enqueue_media();
646
647 // global settings to help ajax work
648 $globals = [
649 'plan' => VISION_PLUGIN_PLAN,
650 'msg_pro_title' => esc_html__('Available only in Pro version', 'vision'),
651 'msg_edit_text' => esc_html__('Press Enter to edit text', 'vision'),
652 'msg_custom_js_error' => esc_html__('Custom js code error', 'vision'),
653 'msg_layer_id_error' => esc_html__('The layer ID should be unique', 'vision'),
654 'wp_base_url' => get_site_url(),
655 'upload_base_url' => $upload_dir['baseurl'],
656 'plugin_base_url' => $plugin_url,
657 'ajax_url' => admin_url('admin-ajax.php'),
658 'ajax_nonce' => wp_create_nonce('vision_ajax'),
659 'ajax_msg_error' => esc_html__('Uncaught Error', 'vision') //Look at the console (F12 or Ctrl+Shift+I, Console tab) for more information
660 ];
661
662 $id = filter_input(INPUT_GET, 'id', FILTER_SANITIZE_NUMBER_INT);
663
664 $globals['ajax_action_get'] = $this->ajax_action_settings_get;
665 $globals['ajax_action_update'] = $this->ajax_action_item_update;
666 $globals['ajax_action_modal'] = $this->ajax_action_modal;
667 $globals['ajax_item_id'] = $id;
668 $globals['settings'] = NULL;
669 $globals['config'] = NULL;
670
671 $settings_key = 'vision_settings';
672 $settings_value = get_option($settings_key);
673 if($settings_value) {
674 $globals['settings'] = unserialize($settings_value); // json_encode(unserialize($settings_value)) problem with double quotes
675 }
676
677 // get item data from DB
678 if($id) {
679 global $wpdb;
680 $table = $wpdb->prefix . VISION_PLUGIN_NAME;
681
682 // phpcs:disable WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
683 $query = $wpdb->prepare("SELECT * FROM {$table} WHERE id=%s", $id);
684 $item = $wpdb->get_row($query, OBJECT);
685 // phpcs:enable
686
687 if($item) {
688 $globals['config'] = unserialize($item->data); // json_encode(unserialize($item->data)) problem with double quotes
689 }
690 } else {
691 // new item
692 $item = (object) [
693 'author' => get_current_user_id(),
694 'editor' => get_current_user_id(),
695 'created' => current_time('mysql', 1),
696 'modified' => current_time('mysql', 1)
697 ];
698 }
699
700 require_once( plugin_dir_path( dirname(__FILE__) ) . 'includes/page-item.php' );
701
702 // set global settings
703 wp_localize_script('vision_admin', 'vision_globals', $globals);
704 }
705 }
706
707 /**
708 * Show admin menu settings page
709 */
710 function admin_menu_page_settings() {
711 $page = sanitize_key(filter_input(INPUT_GET, 'page', FILTER_DEFAULT));
712 if($page==='vision_settings') {
713 $plugin_url = plugin_dir_url(dirname(__FILE__));
714
715 wp_enqueue_style('vision_admin', $plugin_url . 'assets/css/admin.css', [], VISION_PLUGIN_VERSION, 'all' );
716 wp_enqueue_style('vision_lucide', $plugin_url . 'assets/vendor/lucide/lucide.css', [], VISION_PLUGIN_VERSION, 'all' );
717 wp_enqueue_script('vision_admin', $plugin_url . 'assets/js/admin.js', ['jquery'], VISION_PLUGIN_VERSION, false );
718
719 // global settings to help ajax work
720 $globals = [
721 'plan' => VISION_PLUGIN_PLAN,
722 'msg_pro_title' => esc_html__('Available only in Pro version', 'vision'),
723 'ajax_url' => admin_url('admin-ajax.php'),
724 'ajax_nonce' => wp_create_nonce('vision_ajax' ),
725 'ajax_msg_error' => esc_html__('Uncaught Error', 'vision') //Look at the console (F12 or Ctrl+Shift+I, Console tab) for more information
726 ];
727
728 $globals['ajax_action_update'] = $this->ajax_action_settings_update;
729 $globals['ajax_action_get'] = $this->ajax_action_settings_get;
730 $globals['ajax_action_modal'] = $this->ajax_action_modal;
731 $globals['ajax_action_delete_data'] = $this->ajax_action_delete_data;
732 $globals['config'] = NULL;
733
734 // read settings
735 $settings_key = 'vision_settings';
736 $settings_value = get_option($settings_key);
737 if($settings_value) {
738 $globals['config'] = wp_json_encode(unserialize($settings_value));
739 }
740
741 require_once(plugin_dir_path( dirname(__FILE__) ) . 'includes/page-settings.php' );
742
743 wp_localize_script('vision_admin', 'vision_globals', $globals);
744 }
745 }
746
747 /**
748 * Show admin menu upgrade to pro page
749 */
750 function admin_menu_page_upgrade_to_pro() {
751 $page = sanitize_key(filter_input(INPUT_GET, 'page', FILTER_DEFAULT));
752 if($page==='vision_upgrade_to_pro') {
753 echo '<script>window.location = "https://1.envato.market/getvision"</script>';
754 }
755 }
756
757 /**
758 * Ajax update item state
759 */
760 function ajax_item_update_status() {
761 $error = false;
762 $data = [];
763 $config = filter_input(INPUT_POST, 'config', FILTER_UNSAFE_RAW);
764
765 if(check_ajax_referer('vision_ajax', 'nonce', false)) {
766 global $wpdb;
767 $table = $wpdb->prefix . VISION_PLUGIN_NAME;
768
769 $config = json_decode($config);
770 $result = false;
771
772 if(isset($config->id) && isset($config->active)) {
773 // phpcs:disable WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
774 $query = $wpdb->prepare("SELECT * FROM {$table} WHERE id=%s", $config->id);
775 $item = $wpdb->get_row($query, OBJECT );
776 // phpcs:enable
777
778 if($item && (current_user_can('manage_options') || get_current_user_id()==$item->author) ) {
779 $itemData = unserialize($item->data);
780 $itemData->active = $config->active;
781
782 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
783 $result = $wpdb->update(
784 $table,
785 ['active' => $itemData->active, 'data' => serialize($itemData)],
786 ['id' => $config->id]
787 );
788 }
789 }
790
791 if($result) {
792 $data['id'] = $config->id;
793 $data['msg'] = esc_html__('The item was successfully updated', 'vision');
794 } else {
795 $error = true;
796 $data['msg'] = esc_html__('The operation failed, can\'t update item', 'vision');
797 }
798 } else {
799 $error = true;
800 $data['msg'] = esc_html__('The operation failed', 'vision');
801 }
802
803 if($error) {
804 wp_send_json_error($data);
805 } else {
806 wp_send_json_success($data);
807 }
808
809 wp_die(); // this is required to terminate immediately and return a proper response
810 }
811
812 /**
813 * Ajax update item data
814 */
815 function ajax_item_update() {
816 $error = false;
817 $data = [];
818
819 if(check_ajax_referer('vision_ajax', 'nonce', false)) {
820 global $wpdb;
821 $table = $wpdb->prefix . VISION_PLUGIN_NAME;
822
823 $inputId = filter_input(INPUT_POST, 'id', FILTER_UNSAFE_RAW);
824 $inputData = filter_input(INPUT_POST, 'data', FILTER_UNSAFE_RAW);
825 $inputConfig = filter_input(INPUT_POST, 'config', FILTER_UNSAFE_RAW);
826 $itemData = json_decode($inputData);
827 $itemConfig = json_decode($inputConfig);
828 $flag = true;
829
830 if(VISION_PLUGIN_PLAN == 'lite') {
831 // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
832 $rowcount = $wpdb->get_var("SELECT COUNT(*) FROM {$table}");
833
834 if(!($rowcount == 0 || ($rowcount == 1 && $inputId))) {
835 $flag = false;
836 $error = true;
837 $data['msg'] = esc_html__('The operation failed, you can work only with one item. To create more, buy the pro version.', 'vision');
838 }
839 }
840
841 if($flag) {
842 $itemConfig->modified = current_time('mysql', 1);
843
844 if($inputId) {
845 $result = false;
846
847 // phpcs:disable WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
848 $query = $wpdb->prepare("SELECT * FROM {$table} WHERE id=%s", $inputId);
849 $item = $wpdb->get_row($query, OBJECT);
850 // phpcs:enable
851
852 if($item && (current_user_can('manage_options') || get_current_user_id()==$item->author) ) {
853 $itemData->slug = sanitize_title(($itemData->slug ? $itemData->slug : $itemData->title));
854
855 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
856 $result = $wpdb->update(
857 $table,
858 [
859 'title' => $itemData->title,
860 'slug' => $itemData->slug,
861 'active' => $itemData->active,
862 'data' => serialize($itemData),
863 'config' => serialize($itemConfig),
864 //'author' => get_current_user_id(),
865 'editor' => get_current_user_id(),
866 //'date' => NULL,
867 'modified' => current_time('mysql', 1)
868 ],
869 ['id' => $inputId]
870 );
871 }
872
873 if($result) {
874 $data['id'] = $inputId;
875 $data['msg'] = esc_html__('The item was successfully updated', 'vision');
876 } else {
877 $error = true;
878 $data['msg'] = esc_html__('The operation failed, can\'t update item', 'vision');
879 }
880 } else {
881 $itemData->slug = sanitize_title(($itemData->slug ? $itemData->slug : $itemData->title));
882
883 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
884 $result = $wpdb->insert(
885 $table,
886 [
887 'title' => $itemData->title,
888 'slug' => $itemData->slug,
889 'active' => $itemData->active,
890 'data' => serialize($itemData),
891 'config' => serialize($itemConfig),
892 'author' => get_current_user_id(),
893 'editor' => get_current_user_id(),
894 'created' => current_time('mysql', 1),
895 'modified' => current_time('mysql', 1)
896 ]);
897
898 if($result) {
899 $data['id'] = $inputId = $wpdb->insert_id;
900 $data['msg'] = esc_html__('The item was successfully created', 'vision');
901 } else {
902 $error = true;
903 $data['msg'] = esc_html__('The operation failed, can\'t create item', 'vision');
904 }
905 }
906 }
907
908 // [filemanager] create an external file
909 if(!$error && wp_is_writable(VISION_PLUGIN_UPLOAD_DIR)) {
910 $file_json = 'config.json';
911 $file_main_css = 'main.css';
912 $file_custom_css = 'custom.css';
913 $file_root_path = VISION_PLUGIN_UPLOAD_DIR . '/' . $inputId . '/';
914
915 $wp_filesystem = $this->getFileSystem();
916 if(!$wp_filesystem->is_dir($file_root_path)) {
917 $wp_filesystem->mkdir($file_root_path);
918 }
919
920 if(file_exists($file_root_path . $file_json)) {
921 wp_delete_file($file_root_path . $file_json);
922 }
923
924 $wp_filesystem->put_contents($file_root_path . $file_main_css, $this->getMainCss($itemData, $inputId));
925 $wp_filesystem->put_contents($file_root_path . $file_custom_css, $itemData->customCSS->data);
926 }
927 } else {
928 $error = true;
929 $data['msg'] = esc_html__('The operation failed', 'vision');
930 }
931
932 if($error) {
933 wp_send_json_error($data);
934 } else {
935 wp_send_json_success($data);
936 }
937
938 wp_die(); // this is required to terminate immediately and return a proper response
939 }
940
941 /**
942 * Ajax update settings data
943 */
944 function ajax_settings_update() {
945 $error = false;
946 $data = [];
947 $config = filter_input(INPUT_POST, 'config', FILTER_UNSAFE_RAW);
948
949 if(check_ajax_referer('vision_ajax', 'nonce', false)) {
950 $settings_key = 'vision_settings';
951 $settings_value = serialize(json_decode($config));
952 $result = false;
953
954 if(get_option($settings_key) == false) {
955 $autoload = 'no';
956 $result = add_option($settings_key, $settings_value, "", $autoload);
957 } else {
958 $old_settings_value = get_option($settings_key);
959 if($old_settings_value === $settings_value) {
960 $result = true;
961 } else {
962 $result = update_option($settings_key, $settings_value);
963 }
964 }
965
966 if($result) {
967 $data['msg'] = esc_html__('The settings were successfully updated', 'vision');
968 } else {
969 $error = true;
970 $data['msg'] = esc_html__('The operation failed, can\'t update settings', 'vision');
971 }
972 }
973
974 if($error) {
975 wp_send_json_error($data);
976 } else {
977 wp_send_json_success($data);
978 }
979
980 wp_die(); // this is required to terminate immediately and return a proper response
981 }
982
983 /**
984 * Ajax settings get data
985 */
986 function ajax_settings_get() {
987 $error = false;
988 $data = [];
989 $type = sanitize_key(filter_input(INPUT_POST, 'type', FILTER_DEFAULT));
990
991 if(check_ajax_referer('vision_ajax', 'nonce', false)) {
992 switch($type) {
993 case 'roles': {
994 $data['list'] = [];
995
996 $roles = wp_roles()->roles;
997 foreach($roles as $key => $role) {
998 if(array_key_exists('read', $role['capabilities'])) {
999 array_push($data['list'], ['id' => $key, 'name' => translate_user_role($role['name'])]);
1000 }
1001 }
1002 }
1003 break;
1004 case 'themes': {
1005 $data['list'] = [];
1006
1007 $files = glob(plugin_dir_path( dirname(__FILE__) ) . 'assets/themes/*.css');
1008 foreach($files as $file) {
1009 $filename = basename($file, '.css');
1010 array_push($data['list'], ['id' => $filename, 'title' => str_replace('-', ' ', $filename)]);
1011 }
1012 }
1013 break;
1014 case 'editor-themes': {
1015 $data['list'] = [];
1016
1017 $files = glob(plugin_dir_path( dirname(__FILE__) ) . 'assets/vendor/ace/theme-*.js');
1018 foreach($files as $file) {
1019 $filename = str_replace('theme-','',basename($file, '.js'));
1020 array_push($data['list'], ['id' => $filename, 'title' => str_replace('_', ' ', $filename)]);
1021 }
1022 }
1023 break;
1024 case 'fonts': {
1025 $data['list'] = array(
1026 array('fontname' => 'none'),
1027 array('fontname' => 'Aclonica'),
1028 array('fontname' => 'Allan'),
1029 array('fontname' => 'Annie+Use+Your+Telescope'),
1030 array('fontname' => 'Anonymous+Pro'),
1031 array('fontname' => 'Allerta+Stencil'),
1032 array('fontname' => 'Allerta'),
1033 array('fontname' => 'Amaranth'),
1034 array('fontname' => 'Anton'),
1035 array('fontname' => 'Architects+Daughter'),
1036 array('fontname' => 'Arimo'),
1037 array('fontname' => 'Artifika'),
1038 array('fontname' => 'Arvo'),
1039 array('fontname' => 'Asset'),
1040 array('fontname' => 'Astloch'),
1041 array('fontname' => 'Bangers'),
1042 array('fontname' => 'Bentham'),
1043 array('fontname' => 'Bevan'),
1044 array('fontname' => 'Bigshot+One'),
1045 array('fontname' => 'Bowlby+One'),
1046 array('fontname' => 'Bowlby+One+SC'),
1047 array('fontname' => 'Brawler'),
1048 array('fontname' => 'Cabin'),
1049 array('fontname' => 'Calligraffitti'),
1050 array('fontname' => 'Candal'),
1051 array('fontname' => 'Cantarell'),
1052 array('fontname' => 'Cardo'),
1053 array('fontname' => 'Carter One'),
1054 array('fontname' => 'Caudex'),
1055 array('fontname' => 'Cedarville+Cursive'),
1056 array('fontname' => 'Cherry+Cream+Soda'),
1057 array('fontname' => 'Chewy'),
1058 array('fontname' => 'Coda'),
1059 array('fontname' => 'Coming+Soon'),
1060 array('fontname' => 'Copse'),
1061 array('fontname' => 'Cousine'),
1062 array('fontname' => 'Covered+By+Your+Grace'),
1063 array('fontname' => 'Crafty+Girls'),
1064 array('fontname' => 'Crimson+Text'),
1065 array('fontname' => 'Crushed'),
1066 array('fontname' => 'Cuprum'),
1067 array('fontname' => 'Damion'),
1068 array('fontname' => 'Dancing+Script'),
1069 array('fontname' => 'Dawning+of+a+New+Day'),
1070 array('fontname' => 'Didact+Gothic'),
1071 array('fontname' => 'Droid+Sans'),
1072 array('fontname' => 'Droid+Sans+Mono'),
1073 array('fontname' => 'Droid+Serif'),
1074 array('fontname' => 'EB+Garamond'),
1075 array('fontname' => 'Expletus+Sans'),
1076 array('fontname' => 'Fontdiner+Swanky'),
1077 array('fontname' => 'Forum'),
1078 array('fontname' => 'Francois+One'),
1079 array('fontname' => 'Geo'),
1080 array('fontname' => 'Give+You+Glory'),
1081 array('fontname' => 'Goblin+One'),
1082 array('fontname' => 'Goudy+Bookletter+1911'),
1083 array('fontname' => 'Gravitas+One'),
1084 array('fontname' => 'Gruppo'),
1085 array('fontname' => 'Hammersmith+One'),
1086 array('fontname' => 'Holtwood+One+SC'),
1087 array('fontname' => 'Homemade+Apple'),
1088 array('fontname' => 'Inconsolata'),
1089 array('fontname' => 'Indie+Flower'),
1090 array('fontname' => 'IM+Fell+DW+Pica'),
1091 array('fontname' => 'IM+Fell+DW+Pica+SC'),
1092 array('fontname' => 'IM+Fell+Double+Pica'),
1093 array('fontname' => 'IM+Fell+Double+Pica+SC'),
1094 array('fontname' => 'IM+Fell+English'),
1095 array('fontname' => 'IM+Fell+English+SC'),
1096 array('fontname' => 'IM+Fell+French+Canon'),
1097 array('fontname' => 'IM+Fell+French+Canon+SC'),
1098 array('fontname' => 'IM+Fell+Great+Primer'),
1099 array('fontname' => 'IM+Fell+Great+Primer+SC'),
1100 array('fontname' => 'Irish+Grover'),
1101 array('fontname' => 'Irish+Growler'),
1102 array('fontname' => 'Istok+Web'),
1103 array('fontname' => 'Josefin+Sans'),
1104 array('fontname' => 'Josefin+Slab'),
1105 array('fontname' => 'Judson'),
1106 array('fontname' => 'Jura'),
1107 array('fontname' => 'Just+Another+Hand'),
1108 array('fontname' => 'Just+Me+Again+Down+Here'),
1109 array('fontname' => 'Kameron'),
1110 array('fontname' => 'Kenia'),
1111 array('fontname' => 'Kranky'),
1112 array('fontname' => 'Kreon'),
1113 array('fontname' => 'Kristi'),
1114 array('fontname' => 'La+Belle+Aurore'),
1115 array('fontname' => 'Lato'),
1116 array('fontname' => 'League+Script'),
1117 array('fontname' => 'Lekton'),
1118 array('fontname' => 'Limelight'),
1119 array('fontname' => 'Lobster'),
1120 array('fontname' => 'Lobster Two'),
1121 array('fontname' => 'Lora'),
1122 array('fontname' => 'Love+Ya+Like+A+Sister'),
1123 array('fontname' => 'Loved+by+the+King'),
1124 array('fontname' => 'Luckiest+Guy'),
1125 array('fontname' => 'Maiden+Orange'),
1126 array('fontname' => 'Mako'),
1127 array('fontname' => 'Maven+Pro'),
1128 array('fontname' => 'Meddon'),
1129 array('fontname' => 'MedievalSharp'),
1130 array('fontname' => 'Megrim'),
1131 array('fontname' => 'Merriweather'),
1132 array('fontname' => 'Metrophobic'),
1133 array('fontname' => 'Michroma'),
1134 array('fontname' => 'Miltonian+Tattoo'),
1135 array('fontname' => 'Miltonian'),
1136 array('fontname' => 'Modern Antiqua'),
1137 array('fontname' => 'Monofett'),
1138 array('fontname' => 'Molengo'),
1139 array('fontname' => 'Mountains of Christmas'),
1140 array('fontname' => 'Muli'),
1141 array('fontname' => 'Neucha'),
1142 array('fontname' => 'Neuton'),
1143 array('fontname' => 'News+Cycle'),
1144 array('fontname' => 'Nixie+One'),
1145 array('fontname' => 'Nobile'),
1146 array('fontname' => 'Nova+Cut'),
1147 array('fontname' => 'Nova+Flat'),
1148 array('fontname' => 'Nova+Mono'),
1149 array('fontname' => 'Nova+Oval'),
1150 array('fontname' => 'Nova+Round'),
1151 array('fontname' => 'Nova+Script'),
1152 array('fontname' => 'Nova+Slim'),
1153 array('fontname' => 'Nova+Square'),
1154 array('fontname' => 'Nunito'),
1155 array('fontname' => 'OFL+Sorts+Mill+Goudy+TT'),
1156 array('fontname' => 'Old+Standard+TT'),
1157 array('fontname' => 'Open+Sans'),
1158 array('fontname' => 'Orbitron'),
1159 array('fontname' => 'Oswald'),
1160 array('fontname' => 'Over+the+Rainbow'),
1161 array('fontname' => 'Reenie+Beanie'),
1162 array('fontname' => 'Pacifico'),
1163 array('fontname' => 'Patrick+Hand'),
1164 array('fontname' => 'Paytone+One'),
1165 array('fontname' => 'Permanent+Marker'),
1166 array('fontname' => 'Philosopher'),
1167 array('fontname' => 'Play'),
1168 array('fontname' => 'Playfair+Display'),
1169 array('fontname' => 'Podkova'),
1170 array('fontname' => 'PT+Sans'),
1171 array('fontname' => 'PT+Sans+Narrow'),
1172 array('fontname' => 'PT+Serif'),
1173 array('fontname' => 'PT+Serif Caption'),
1174 array('fontname' => 'Puritan'),
1175 array('fontname' => 'Quattrocento'),
1176 array('fontname' => 'Quattrocento+Sans'),
1177 array('fontname' => 'Radley'),
1178 array('fontname' => 'Redressed'),
1179 array('fontname' => 'Rock+Salt'),
1180 array('fontname' => 'Rokkitt'),
1181 array('fontname' => 'Ruslan+Display'),
1182 array('fontname' => 'Schoolbell'),
1183 array('fontname' => 'Shadows+Into+Light'),
1184 array('fontname' => 'Shanti'),
1185 array('fontname' => 'Sigmar+One'),
1186 array('fontname' => 'Six+Caps'),
1187 array('fontname' => 'Slackey'),
1188 array('fontname' => 'Smythe'),
1189 array('fontname' => 'Special+Elite'),
1190 array('fontname' => 'Stardos+Stencil'),
1191 array('fontname' => 'Sue+Ellen+Francisco'),
1192 array('fontname' => 'Sunshiney'),
1193 array('fontname' => 'Swanky+and+Moo+Moo'),
1194 array('fontname' => 'Syncopate'),
1195 array('fontname' => 'Tangerine'),
1196 array('fontname' => 'Tenor+Sans'),
1197 array('fontname' => 'Terminal+Dosis+Light'),
1198 array('fontname' => 'The+Girl+Next+Door'),
1199 array('fontname' => 'Tinos'),
1200 array('fontname' => 'Ubuntu'),
1201 array('fontname' => 'Ultra'),
1202 array('fontname' => 'Unkempt'),
1203 array('fontname' => 'UnifrakturMaguntia'),
1204 array('fontname' => 'Varela'),
1205 array('fontname' => 'Varela Round'),
1206 array('fontname' => 'Vibur'),
1207 array('fontname' => 'Vollkorn'),
1208 array('fontname' => 'VT323'),
1209 array('fontname' => 'Waiting+for+the+Sunrise'),
1210 array('fontname' => 'Wallpoet'),
1211 array('fontname' => 'Walter+Turncoat'),
1212 array('fontname' => 'Wire+One'),
1213 array('fontname' => 'Yanone+Kaffeesatz'),
1214 array('fontname' => 'Yeseva+One'),
1215 array('fontname' => 'Zeyada')
1216 );
1217 }
1218 break;
1219 default: {
1220 $error = true;
1221 $data['msg'] = esc_html__('The operation failed', 'vision');
1222 }
1223 break;
1224 }
1225 } else {
1226 $error = true;
1227 $data['msg'] = esc_html__('The operation failed', 'vision');
1228 }
1229
1230 if($error) {
1231 wp_send_json_error($data);
1232 } else {
1233 wp_send_json_success($data);
1234 }
1235
1236 wp_die(); // this is required to terminate immediately and return a proper response
1237 }
1238
1239 /**
1240 * Ajax delete all data from tables
1241 */
1242 function ajax_delete_data() {
1243 $error = true;
1244 $data = [];
1245 $data['msg'] = esc_html__('The operation failed, can\'t delete data', 'vision');
1246
1247 if(check_ajax_referer('vision_ajax', 'nonce', false)) {
1248 global $wpdb;
1249 $table = $wpdb->prefix . VISION_PLUGIN_NAME;
1250
1251 // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
1252 foreach($wpdb->get_results("SELECT id FROM {$table}") as $key => $item) {
1253 // [filemanager] delete file
1254 if(wp_is_writable(VISION_PLUGIN_UPLOAD_DIR)) {
1255 $file_json = 'config.json';
1256 $file_main_css = 'main.css';
1257 $file_custom_css = 'custom.css';
1258 $file_root_path = VISION_PLUGIN_UPLOAD_DIR . '/' . $item->id . '/';
1259
1260 if(file_exists($file_root_path . $file_json)) {
1261 wp_delete_file($file_root_path . $file_json);
1262 }
1263 wp_delete_file($file_root_path . $file_main_css);
1264 wp_delete_file($file_root_path . $file_custom_css);
1265
1266 $wp_filesystem = $this->getFileSystem();
1267 if($wp_filesystem->is_dir($file_root_path)) {
1268 $wp_filesystem->rmdir($file_root_path);
1269 }
1270 }
1271 }
1272
1273 // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
1274 $result = $wpdb->query("TRUNCATE TABLE {$table}");
1275
1276 if($result) {
1277 $error = false;
1278 $data['msg'] = esc_html__('All data deleted', 'vision');
1279 }
1280 }
1281
1282 if($error) {
1283 wp_send_json_error($data);
1284 } else {
1285 wp_send_json_success($data);
1286 }
1287
1288 wp_die(); // this is required to terminate immediately and return a proper response
1289 }
1290
1291 /**
1292 * Ajax settings get data
1293 */
1294 function ajax_modal() {
1295 if(check_ajax_referer('vision_ajax', 'nonce', false)) {
1296 $modalName = sanitize_file_name(filter_input(INPUT_GET, 'name', FILTER_DEFAULT));
1297 $modalPath = plugin_dir_path( dirname(__FILE__) ) . 'includes/modal-' . $modalName . '.php';
1298
1299 if(file_exists($modalPath)) {
1300 require_once( $modalPath );
1301 }
1302 }
1303
1304 wp_die(); // this is required to terminate immediately and return a proper response
1305 }
1306 }
1307 ?>