PluginProbe
Microthemer Lite – Visual Editor to Customize CSS / trunk
Microthemer Lite – Visual Editor to Customize CSS vtrunk
trunk 5.0.0.2
microthemer / src / PackTrait.php

PackTrait.php in Microthemer Lite – Visual Editor to Customize CSS trunk, at src/PackTrait.php

1,731 lines 60.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Microthemer;
4
5 /*
6 * Pack import/export and design-pack file IO uses direct PHP filesystem APIs under wp-content/micro-themes.
7 * WP_Filesystem FTP credential prompts are unsuitable for these admin AJAX flows.
8 *
9 * phpcs:disable WordPress.WP.AlternativeFunctions.file_system_operations_fopen
10 * phpcs:disable WordPress.WP.AlternativeFunctions.file_system_operations_fwrite
11 * phpcs:disable WordPress.WP.AlternativeFunctions.file_system_operations_fclose
12 * phpcs:disable WordPress.WP.AlternativeFunctions.file_system_operations_fread
13 * phpcs:disable WordPress.WP.AlternativeFunctions.file_system_operations_mkdir
14 * phpcs:disable WordPress.WP.AlternativeFunctions.file_system_operations_rmdir
15 * phpcs:disable WordPress.WP.AlternativeFunctions.file_system_operations_chmod
16 * phpcs:disable WordPress.WP.AlternativeFunctions.file_system_operations_touch
17 * phpcs:disable WordPress.WP.AlternativeFunctions.file_system_operations_is_writable
18 * phpcs:disable WordPress.WP.AlternativeFunctions.file_system_operations_is_writeable
19 * phpcs:disable WordPress.WP.AlternativeFunctions.unlink_unlink
20 * phpcs:disable WordPress.WP.AlternativeFunctions.rename_rename
21 */
22 trait PackTrait {
23
24 /**
25 * Handles the 'Manage Micro Themes' admin page.
26 */
27 function manage_micro_themes_page() {
28
29 // only run code if it's the manage themes page
30 if ( isset($_GET["page"]) and $_GET["page"] == $this->microthemespage ) {
31
32 if (!current_user_can('manage_options')){
33 wp_die('Access denied');
34 }
35
36 // handle zip upload
37 if (isset($_POST['tvr_upload_micro_submit'])) {
38 check_admin_referer('tvr_upload_micro_submit');
39 $this->process_uploaded_zip();
40 }
41
42
43 // notify that design pack was successfully deleted (operation done via ajax on single pack page)
44 if (!empty($_GET['mt_action']) and $_GET['mt_action'] == 'tvr_delete_ok') {
45 check_admin_referer('tvr_delete_ok');
46 $this->log(
47 esc_html__('Design pack deleted', 'microthemer'),
48 '<p>' . esc_html__('The design pack was successfully deleted.', 'microthemer') . '</p>',
49 'notice'
50 );
51 }
52
53 // handle edit micro selection
54 if (isset($_POST['tvr_edit_micro_submit'])) {
55 check_admin_referer('tvr_edit_micro_submit');
56 $pref_array = array();
57 $pref_array["theme_in_focus"] = sanitize_file_name(wp_unslash($_POST["preferences"]["theme_in_focus"]));
58 $this->savePreferences($pref_array);
59 }
60
61 // activate theme
62 if (
63 !empty($_GET['mt_action']) and
64 $_GET['mt_action'] == 'tvr_activate_micro_theme') {
65 check_admin_referer('tvr_activate_micro_theme');
66 $theme_name = $this->preferences['theme_in_focus'];
67 $json_file = $this->micro_root_dir . $theme_name . '/config.json';
68 $this->load_json_file($json_file, $theme_name);
69 // update the revisions DB field
70 $user_action = sprintf(
71 /* translators: %s: item name */
72 esc_html__('%s Activated', 'microthemer'),
73 '<i>' . $this->readable_name($theme_name) . '</i>'
74 );
75 if (!$this->updateRevisions($this->options, $user_action)) {
76 $this->log('', '', 'error', 'revisions');
77 }
78 }
79 // deactivate theme
80 if (
81 !empty($_GET['mt_action']) and
82 $_GET['mt_action'] == 'tvr_deactivate_micro_theme') {
83 check_admin_referer('tvr_deactivate_micro_theme');
84 $pref_array = array();
85 $pref_array['active_theme'] = '';
86 if ($this->savePreferences($pref_array)) {
87 $this->log(
88 esc_html__('Item deactivated', 'microthemer'),
89 '<p>' .
90 sprintf(
91 /* translators: %s: item name */
92 esc_html__('%s was deactivated.', 'microthemer'),
93 '<i>'.$this->readable_name($this->preferences['theme_in_focus']).'</i>' )
94 . '</p>',
95 'notice'
96 );
97 }
98 }
99
100 // include manage micro interface (both loader and themer plugins need this)
101 include $this->thisplugindir . 'includes/tvr-manage-micro-themes.php';
102 }
103 }
104
105 // Check a file path matches up against known server-path
106 function checkExpectedPath($constructedPath, $serverPath){
107
108 if (strpos($constructedPath, '..') !== false) {
109 return false;
110 }
111
112 $realBase = realpath($serverPath);
113 $realTarget = realpath($constructedPath);
114
115 return $realBase !== false
116 && $realTarget !== false
117 && strpos($realTarget, $realBase) === 0;
118 }
119
120 /**
121 * Handles the 'Manage Single Pack' admin page.
122 */
123 function manage_single_page() {
124 // only run code on preferences page
125 if( isset($_GET["page"]) and $_GET["page"] == $this->managesinglepage ) {
126
127 if (!current_user_can('manage_options')){
128 wp_die('Access denied');
129 }
130
131 // handle zip upload
132 if (isset($_POST['tvr_upload_micro_submit'])) {
133 check_admin_referer('tvr_upload_micro_submit');
134 $this->process_uploaded_zip();
135 }
136
137 // update meta.txt
138 if (isset($_POST['tvr_edit_meta_submit'])) {
139 check_admin_referer('tvr_edit_meta_submit');
140 $this->update_meta_file($this->micro_root_dir . $this->preferences['theme_in_focus'] . '/meta.txt');
141 }
142
143 // update readme.txt
144 if (isset($_POST['tvr_edit_readme_submit'])) {
145 check_admin_referer('tvr_edit_readme_submit');
146 $this->update_readme_file($this->micro_root_dir . $this->preferences['theme_in_focus'] . '/readme.txt');
147 }
148
149 // upload a file
150 if (isset($_POST['tvr_upload_file_submit'])) {
151 check_admin_referer('tvr_upload_file_submit');
152 $this->handle_file_upload();
153 }
154
155 // delete a file
156 if (
157 !empty($_GET['mt_action']) and
158 $_GET['mt_action'] == 'tvr_delete_micro_file') {
159 check_admin_referer('tvr_delete_micro_file');
160
161 $raw_file = isset($_GET['file']) ? sanitize_text_field(wp_unslash($_GET['file'])) : '';
162 $location = isset($_GET['location']) ? sanitize_key(wp_unslash($_GET['location'])) : '';
163
164 // Preserve the existing basename: sanitize_file_name() can rename valid files and make deletion miss its target.
165 $file_to_delete = basename($raw_file);
166 $base_dir = $this->micro_root_dir . $this->preferences['theme_in_focus'] . '/';
167 $full_path = $base_dir . $file_to_delete;
168
169 if (!$this->checkExpectedPath($full_path, $base_dir)) {
170 return;
171 }
172
173 $delete_ok = true;
174 // remove the file from the media library
175 if ($location == 'library'){
176 // This logic remains the same, as it uses WordPress's secure media deletion functions
177 // which operate on post IDs, not file paths from user input.
178 global $wpdb;
179 $img_path = esc_url_raw($raw_file);
180 $results = $wpdb->get_results(
181 $wpdb->prepare(
182 "SELECT ID FROM {$wpdb->posts} WHERE guid = %s AND post_type = 'attachment'",
183 $img_path
184 )
185 );
186 foreach ( $results as $row ) {
187 if ( false === wp_delete_attachment( $row->ID )) {
188 $delete_ok = false;
189 }
190 }
191 }
192 // Regular delete of a pack file
193 else {
194 // It's now safe to perform the unlink operation on the validated path.
195 if ( !is_file($full_path) || !unlink($full_path) ) {
196 $delete_ok = false;
197 } else {
198 // remove from file_structure array
199 if (!$this->is_screenshot($file_to_delete)){
200 $key = $file_to_delete;
201 } else {
202 $key = 'screenshot';
203 // delete the screenshot-small too
204 $thumb_path = str_replace('screenshot', 'screenshot-small', $full_path);
205 if (is_file($thumb_path)){
206 unlink($thumb_path);
207 unset($this->file_structure[$this->preferences['theme_in_focus']][basename($thumb_path)]);
208 }
209 }
210 unset($this->file_structure[$this->preferences['theme_in_focus']][$key]);
211 }
212 }
213
214 if ($delete_ok){
215 $this->log(
216 esc_html__('File deleted', 'microthemer'),
217 '<p>' . sprintf( /* translators: %s: file name */ esc_html__('%s was successfully deleted.', 'microthemer'), '<b>' . esc_html($file_to_delete) . '</b>' ) . '</p>',
218 'notice'
219 );
220 // update paths in json file
221 $json_config_file = $this->micro_root_dir . $this->preferences['theme_in_focus'] . '/config.json';
222 // We need the relative path for replacement.
223 $root_rel_path = $this->root_rel($this->micro_root_url . $this->preferences['theme_in_focus'] . '/' . $file_to_delete, false, true);
224 $this->replace_json_paths($json_config_file, array($root_rel_path => ''));
225 } else {
226 $this->log(
227 esc_html__('File delete failed', 'microthemer'),
228 '<p>' . sprintf( /* translators: %s: file name */ esc_html__('%s was not deleted.', 'microthemer'), '<b>' . esc_html($file_to_delete) . '</b>' ) . '</p>'
229 );
230 }
231 }
232
233 // include manage file
234 include $this->thisplugindir . 'includes/tvr-manage-single.php';
235 }
236 }
237
238 // process posted zip file
239 function process_uploaded_zip() {
240 if ($_FILES['upload_micro']['error'] == 0) {
241 $this->handle_zip_package();
242 }
243 // there was an error - save in global message
244 else {
245 $this->log_file_upload_error($_FILES['upload_micro']['error']);
246 }
247 }
248
249 // write settings to .json file
250 function update_json_file($theme, $context = '', $export_full = false, $preferences = false) {
251
252 $theme = sanitize_file_name(sanitize_title($theme));
253
254 // create micro theme of 'new' has been requested
255 if ($context == 'new') {
256 // Check for micro theme with same name
257 if ($alt_name = $this->rename_if_required($this->micro_root_dir, $theme)) {
258 $theme = $alt_name; // $alt_name is false if no rename was required
259 }
260 if (!$this->create_micro_theme($theme, 'export', ''))
261 return false;
262 }
263
264 // json file
265 $json_file = $this->micro_root_dir.$theme.'/config.json';
266 $task = file_exists($json_file) ? 'updated' : 'created';
267
268 // simple test - the json file was not being overwritten for one user, so delete if it exists
269 if ($task === 'updated'){
270 unlink($json_file);
271 }
272
273 // Create new file if it doesn't already exist
274 if (!file_exists($json_file)) {
275
276 // create directory if it doesn't exist - not doing so caused fopen issues after pack delete,
277 // unless page refreshed
278 $dir = dirname($json_file);
279 if (!is_dir($dir)){
280 if ( !wp_mkdir_p($dir) ) {
281 $this->log(
282 /* translators: %s: design pack / theme folder name */
283 sprintf( esc_html__('/micro-themes/%s create directory error', 'microthemer'), $theme ),
284 '<p>' . sprintf(
285 /* translators: %s: directory path */
286 esc_html__('WordPress was not able to create the directory: %s', 'microthemer'),
287 $this->root_rel($dir)
288 ) . $this->permissionshelp . '</p>'
289 );
290 return false;
291 }
292 }
293
294 if (!$write_file = @fopen($json_file, 'w')) { // this creates a blank file for writing
295 $this->log(
296 esc_html__('Create json error', 'microthemer'),
297 '<p>' . esc_html__('WordPress does not have permission to create: ', 'microthemer')
298 . $this->root_rel($json_file) . $this->permissionshelp.'</p>'
299 );
300 return false;
301 }
302 }
303
304 // check if json file is writable
305 if (!is_writable($json_file)){
306 $this->log(
307 esc_html__('Write json error', 'microthemer'),
308 '<p>' . esc_html__('WordPress does not have "write" permission for: ', 'microthemer')
309 . $this->root_rel($json_file) . '. '.$this->permissionshelp.'</p>'
310 );
311 return false;
312 }
313
314 // copy full options to var for filtering
315 $json_data = $this->options;
316
317 // include the user's current media queries form importing back
318 $json_data['non_section']['active_queries'] = $this->preferences['m_queries'];
319
320
321 // Include snippets and npm dependencies
322 if ($this->supportContent()){
323
324 if (!empty($this->serialised_post['export_sections']['active_snippets'])){
325
326 $json_data['non_section']['active_snippets'] = $this->contentMethod('getSnippets', array(-1, 0, ARRAY_A));
327
328 // We want to combine npm packages with existing instead of overwriting
329 $json_data['non_section']['combine_preferences']['npm_dependencies'] = $this->preferences['npm_dependencies'];
330
331 }
332 }
333
334
335 // unless full, loop through full options - removing sections
336 if (!$export_full){
337
338 foreach ($this->options as $section_name => $array) {
339
340 // if the section wasn't selected, remove it from json data var (along with the view_state var)
341 if ( empty($this->serialised_post['export_sections'])
342 or (!array_key_exists($section_name, $this->serialised_post['export_sections']) )
343 and $section_name != 'non_section') {
344
345 // remove the regular section data and view states
346 unset($json_data[$section_name]);
347 unset($json_data['non_section']['view_state'][$section_name]);
348
349 // need to remove all media query settings for unchecked sections too
350 if (!empty($json_data['non_section']['m_query']) and
351 is_array($json_data['non_section']['m_query'])) {
352 foreach ($json_data['non_section']['m_query'] as $m_key => $array) {
353 unset($json_data['non_section']['m_query'][$m_key][$section_name]);
354 }
355 }
356
357 // and all of the important values
358 if (!empty($json_data['non_section']['important']['m_query']) and
359 is_array($json_data['non_section']['important']['m_query'])) {
360 foreach ($json_data['non_section']['important']['m_query'] as $m_key => $array) {
361 unset($json_data['non_section']['important']['m_query'][$m_key][$section_name]);
362 }
363 }
364 }
365 }
366 }
367
368 // include preferences in export if passed in
369 if ($preferences){
370 $json_data['non_section']['exported_preferences'] = $preferences;
371 }
372
373 // set hand-coded css to nothing if not marked for export
374 if ( empty($this->serialised_post['export_sections']['hand_coded_css'])) {
375 $json_data['non_section']['hand_coded_css'] = '';
376 }
377
378 // set js to nothing if not marked for export
379 if ( empty($this->serialised_post['export_sections']['js'])) {
380 $json_data['non_section']['js'] = '';
381 }
382
383 // create debug selective export file if specified at top of script
384 if ($this->debug_selective_export) {
385 $data = '';
386 $debug_file = $this->debug_dir . 'debug-selective-export.txt';
387 $write_file = @fopen($debug_file, 'w');
388 $data.= esc_html__('The Selectively Exported Options', 'microthemer') . "\n\n";
389 $data.= print_r($json_data, true);
390 $data.= "\n\n" . esc_html__('The Full Options', 'microthemer') . "\n\n";
391 $data.= print_r($this->options, true);
392 fwrite($write_file, $data);
393 fclose($write_file);
394 }
395
396 // write data to json file
397 if ($data = wp_json_encode($json_data)) {
398 // the file will be created if it doesn't exist. otherwise it is overwritten.
399 $write_file = @fopen($json_file, 'w');
400 fwrite($write_file, $data);
401 fclose($write_file);
402 // report
403 if ($task == 'updated'){
404 $this->log(
405 esc_html__('Settings exported', 'microthemer'),
406 '<p>' . esc_html__('Your settings were successfully exported to: ',
407 'microthemer') . '<b>'.$theme.'</b></p>',
408 'notice'
409 );
410 }
411 }
412 else {
413 $this->log(
414 esc_html__('Encode json error', 'microthemer'),
415 '<p>' . esc_html__('WordPress failed to convert your settings into json.', 'microthemer') . '</p>'
416 );
417 }
418
419 return $theme; // sanitised theme name
420 }
421
422 // load .json file - or json data if already got
423 function load_json_file($json_file, $theme_name, $context = '', $data = false) {
424
425 $isMerge = $context == __('Merge', 'microthemer');
426
427 // if json data wasn't passed in to function, get it
428 if ( !$data ){
429
430 // bail if file is missing or cannot read
431 if ( !$data = $this->get_file_data( $json_file ) ) {
432 return false;
433 }
434 }
435
436 // convert to array
437 if (!$json_array = $this->json('decode', $data)) {
438 return false;
439 }
440
441 // json decode was successful
442
443 // if the export included workspace settings, save preferences and remove from data
444 // this is insurance agaist upgrade problems
445 if (!empty($json_array['non_section']['exported_preferences'])){
446 update_option($this->preferencesName, $json_array['non_section']['exported_preferences']);
447 unset($json_array['non_section']['exported_preferences']);
448 }
449
450 // replace mq keys, add new to the UI, add css units if necessary.
451 $filtered_json = $this->filter_incoming_data('import', $json_array, $isMerge);
452
453 // merge the arrays if merge (must come after key analysis/replacements)
454 if ($isMerge or $context == esc_attr__('Raw CSS', 'microthemer')) {
455 $filtered_json = $this->merge($this->options, $filtered_json);
456 } else {
457 // Only update theme_in_focus if it's not a merge
458 $pref_array['theme_in_focus'] = $theme_name;
459 $this->savePreferences($pref_array);
460 }
461
462 // updates options var, save settings, and update stylesheet
463 $this->options = $filtered_json;
464 $this->saveUiOptions2($this->options);
465 $this->update_assets($theme_name, $context);
466
467 // import success
468 $this->log(
469 esc_html__('Settings were imported', 'microthemer'),
470 '<p>' . esc_html__('The design pack settings were successfully imported.', 'microthemer') . '</p>',
471 'notice'
472 );
473 }
474
475 // manage packs UI header
476 function manage_packs_header($page){
477 ?>
478 <ul class="pack-manage-options">
479 <li class="upload">
480 <form name='upload_micro_form' id="upload-micro-form" method="post" enctype="multipart/form-data"
481 action="<?php echo esc_attr('admin.php?page='. $page);?>" >
482 <?php wp_nonce_field('tvr_upload_micro_submit'); ?>
483 <input id="upload_pack_input" type="file" name="upload_micro" />
484 <input class="tvr-button upload-pack" type="submit" name="tvr_upload_micro_submit"
485 value="<?php esc_attr_e('Upload design pack', 'microthemer'); ?>" title="<?php esc_attr_e('Upload a new design pack', 'microthemer'); ?>" />
486 </form>
487 </li>
488 </ul>
489 <?php
490 }
491
492 // Get design packs directories and count
493 function get_design_packs($packs){
494 $count = 0;
495 $valid_packs = array();
496 $exclude = array('sass', 'scss');
497 foreach($packs as $name => $item){
498 if (is_array($item) && !in_array($name, $exclude)){
499 ++$count;
500 $valid_packs[$name] = $item;
501 }
502 }
503 return array(
504 'count' => $count,
505 'directories' => $valid_packs
506 );
507 }
508
509 // output meta spans and logs tmpl for manage pages
510 function manage_packs_meta(){
511 ?>
512 <span id="ajaxUrl" rel="<?php echo esc_url($this->wp_ajax_url); ?>"></span>
513 <span id="delete-ok" rel='admin.php?page=<?php echo esc_attr($this->microthemespage);?>&mt_action=tvr_delete_ok&_wpnonce=<?php echo esc_attr(wp_create_nonce('tvr_delete_ok')); ?>'></span>
514 <span id="zip-folder" rel="<?php echo esc_url($this->thispluginurl.'zip-exports/'); ?>"></span>
515 <?php
516 }
517
518 // Pack pagination UI
519 function pack_pagination($page, $total_pages, $total_packs, $start, $end) {
520
521 ?>
522 <ul class="tvr-pagination">
523 <?php
524 $i = $total_pages;
525 while ($i >= 1){
526 echo '
527 <li class="page-item">';
528 if ($i == $page) {
529 echo '<span>'.(int)$i.'</span>';
530 } else {
531 echo '<a href="admin.php?page='. esc_attr($this->microthemespage) . '&packs_page='.(int)$i.'">'.(int)$i.'</a>';
532 }
533 echo '
534 </li>';
535 --$i;
536 }
537
538 if ($end < 1) {
539 $start = 0;
540 }
541
542 echo '<li class="displaying-x">' .
543 /* translators: 1: start index, 2: end index, 3: total count */
544 sprintf(esc_html__('Displaying %1$s - %2$s of %3$s', 'microthemer'), (int)$start, (int)$end, (int)$total_packs) . '</li>';
545
546 if (!empty($this->preferences['theme_in_focus']) and $total_packs > 0){
547 $url = 'admin.php?page=' . $this->managesinglepage . '&design_pack=' . $this->preferences['theme_in_focus'];
548 $name = $this->readable_name($this->preferences['theme_in_focus']);
549 ?>
550 <li class="last-modified" rel="<?php echo esc_attr($this->preferences['theme_in_focus']); ?>">
551 <?php esc_html_e('Last modified design pack: ', 'microthemer'); ?><a title="<?php /* translators: %s: design pack name */ printf(esc_attr__('Edit %s', 'microthemer'), esc_attr($name)); ?>"
552 href="<?php echo esc_url($url); ?>"><?php echo esc_html($name); ?>
553 </a>
554 </li>
555 <?php
556 }
557 ?>
558 </ul>
559 <?php
560 }
561
562 // create micro theme/pack directory
563 function create_micro_theme($micro_name, $action, $temp_zipfile) {
564 // sanitize dir name
565 $name = sanitize_file_name( $micro_name );
566 $error = false;
567 // extra bit need for zip uploads (removes .zip)
568 if ($action == 'unzip') {
569 $name = substr($name, 0, -4);
570 }
571 // check for micro-themes folder and create if doesn't exist
572 $error = !$this->setup_micro_themes_dir() ? true : false;
573
574 // check if the micro-themes folder is writable
575 if ( !is_writeable( $this->micro_root_dir ) ) {
576 $this->log(
577 esc_html__('/micro-themes write error', 'microthemer'),
578 '<p>' . sprintf(
579 /* translators: %s: directory path */
580 esc_html__('The directory %s is not writable.', 'microthemer'),
581 $this->root_rel($this->micro_root_dir)
582 ) . $this->permissionshelp . '</p>'
583 );
584 $error = true;
585 }
586 // Check for micro theme with same name
587 if ($alt_name = $this->rename_if_required($this->micro_root_dir, $name)) {
588 $name = $alt_name; // $alt_name is false if no rename was required
589 }
590 // abs path
591 $this_micro_abs = $this->micro_root_dir . $name;
592 // Create new micro theme folder
593 if ( !wp_mkdir_p ( $this_micro_abs ) ) {
594 $this->log(
595 esc_html__('design pack create error', 'microthemer'),
596 '<p>' . sprintf(
597 /* translators: %s: directory path */
598 esc_html__('WordPress was not able to create the %s directory.', 'microthemer'), $this->root_rel($this_micro_abs)
599 ). '</p>'
600 );
601 $error = true;
602 }
603 // Check folder permission
604 if ( !is_writeable( $this_micro_abs ) ) {
605 $this->log(
606 esc_html__('design pack write error', 'microthemer'),
607 '<p>' . sprintf(
608 /* translators: %s: directory path */
609 esc_html__('The directory %s is not writable.', 'microthemer'), $this->root_rel($this_micro_abs)
610 ) . $this->permissionshelp . '</p>'
611 );
612 $error = true;
613 }
614
615 // unzip if required
616 if ($action == 'unzip') {
617 // extract the files
618 $this->extract_files($this_micro_abs, $temp_zipfile);
619 // get the final name of the design pack from the meta file
620 $name = $this->rename_from_meta($this_micro_abs . '/meta.txt', $name);
621 if ($name){
622 // import bg images to media library and update paths if any are found
623 $json_config_file = $this->micro_root_dir . $name . '/config.json';
624 $this->import_pack_images_to_library($json_config_file, $name);
625 }
626 // add the dir to the file structure array
627 $this->file_structure[$name] = $this->dir_loop($this->micro_root_dir . $name);
628 ksort($this->file_structure);
629 }
630
631 // if creating blank shell or exporting UI settings, need to create meta.txt and readme.txt
632 if ($action == 'export') {
633 // set the theme name value
634 $_POST['theme_meta']['Name'] = $this->readable_name($name);
635 $this->update_meta_file($this_micro_abs . '/meta.txt');
636 $this->update_readme_file($this_micro_abs . '/readme.txt');
637
638 }
639 // update the theme_in_focus value in the preferences table
640 $this->savePreferences(
641 array(
642 'theme_in_focus' => $name,
643 )
644 );
645
646 // if still no error, the action worked
647 if ($error != true) {
648 if ($action == 'create') {
649 $this->log(
650 esc_html__('Design pack created', 'microthemer'),
651 '<p>' . esc_html__('The design pack directory was successfully created on the server.', 'microthemer') . '</p>',
652 'notice'
653 );
654 }
655 if ($action == 'unzip') {
656 $this->log(
657 esc_html__('Design pack installed', 'microthemer'),
658 '<p>' . esc_html__('The design pack was successfully uploaded and extracted. You can import it into your workspace any time using the', 'microthemer') .
659 ' <span class="show-parent-dialog link" rel="import-from-pack">' . esc_html__('import option', 'microthemer') . '</span>'.
660 '<span id="update-packs-list" rel="' . $this->readable_name($name) . '"></span>.</p>',
661 'notice'
662 );
663 }
664 if ($action == 'export') {
665 $this->log(
666 esc_html__('Settings exported', 'microthemer'),
667 '<p>' . esc_html__('Your settings were successfully exported as a design pack directory on the server.', 'microthemer') . '</p>',
668 'notice'
669 );
670 }
671 }
672 return true;
673 }
674
675 // rename zip from meta.txt name value
676 function rename_from_meta($meta_file, $name){
677 $orig_name = $name;
678 if (is_file($meta_file) and is_readable($meta_file)) {
679 $meta_info = $this->read_meta_file($meta_file);
680 $name = strtolower(sanitize_file_name( $meta_info['Name'] ));
681 // rename the directory if it doesn't already have the correct name
682 if ($orig_name != $name){
683 if ($alt_name = $this->rename_if_required($this->micro_root_dir, $name)) {
684 $name = $alt_name; // $alt_name is false if no rename was required
685 }
686 rename($this->micro_root_dir . $orig_name, $this->micro_root_dir . $name);
687 }
688 return $name;
689 } else {
690 // no meta file error
691 $this->log(
692 esc_html__('Missing meta file', 'microthemer'),
693 '<p>' . sprintf(
694 /* translators: %s: file name */
695 esc_html__('The zip file doesn\'t contain a necessary %s file or it could not be read.', 'microthemer'),
696 $this->root_rel($meta_file)
697 ) . '</p>'
698 );
699 return false;
700 }
701 }
702
703 // read the data from a file into a string
704 function get_file_data($file){
705 if (!is_file($file)){
706 $this->log(
707 esc_html__('File doesn\'t exist', 'microthemer'),
708 '<p>' . sprintf(
709 /* translators: %s: file path */
710 esc_html__('%s does not exist on the server.', 'microthemer'),
711 $this->root_rel($file)
712 ) . '</p>'
713 );
714 return false;
715 }
716 if (!is_readable($file)){
717 $this->log(
718 esc_html__('File not readable', 'microthemer'),
719 '<p>' . sprintf(
720 /* translators: %s: file path */
721 esc_html__(' %s could not be read.', 'microthemer'),
722 $this->root_rel($file)
723 ) . '</p>'
724 . $this->permissionshelp
725 );
726 return false;
727 }
728 $fh = @fopen($file, 'r');
729 $data = fread($fh, filesize($file));
730 fclose($fh);
731 return $data;
732 }
733
734 // get image paths from the config.json file
735 function get_image_paths($data){
736 $img_array = array();
737
738 // look for images
739 preg_match_all('/"(background_image|list_style_image|border_image_src|mask_image)":"([^none][A-Za-z0-9 _\-\.\\/&\(\)\[\]!\{\}\?:=]+)"/',
740 $data,
741 $img_array,
742 PREG_PATTERN_ORDER);
743
744 // ensure $img_array only contains unique images
745 foreach ($img_array[2] as $key => $config_img_path) {
746
747 // if it's not unique, remove
748 if (!empty($already_got[$config_img_path])){
749 unset($img_array[2][$key]);
750 }
751 $already_got[$config_img_path] = 1;
752 }
753
754 if (count($img_array[2]) > 0) {
755 return $img_array;
756 } else {
757 return false;
758 }
759 }
760
761 // get media library images linked to from the config.json file
762 function get_linked_library_images($json_config_file){
763 // get config data
764 if (!$data = $this->get_file_data($json_config_file)) {
765 return false;
766 }
767
768 // get images from the config file that should be imported
769 if (!$img_array = $this->get_image_paths($data)) {
770 return false;
771 }
772
773 // loop through the image array, remove any images not in the media library
774 foreach ($img_array[2] as $key => $config_img_path) {
775 // has uploads path and doesn't also exist in pack dir (yet to be moved) - may be an unnecessary check
776 if (strpos($config_img_path, '/uploads/')!== false and !is_file($this->micro_root_dir . $config_img_path)){
777 $library_images[] = $config_img_path;
778 }
779 }
780 if (is_array($library_images)){
781 return $library_images;
782 } else {
783 return false;
784 }
785 }
786
787 // import images in a design pack to the media library and update image paths in config.json
788 function import_pack_images_to_library($json_config_file, $name, $data = false, $remote_images = false){
789
790 // reset imported images
791 $this->imported_images = array();
792
793 // get config data if not passed in
794 if (!$data) {
795 if (!$data = $this->get_file_data($json_config_file)) {
796 return false;
797 }
798 }
799
800 // get images from the config file if not passed in
801 if (!$remote_images) {
802 if (!$img_array = $this->get_image_paths($data)) {
803 return false;
804 }
805 $img_array = $img_array[2];
806 } else {
807 $img_array = $remote_images;
808 }
809
810
811 // loop through the image array
812 foreach ($img_array as $key => $img_path) {
813
814 $just_image_name = basename($img_path);
815
816 // if remote image found in stylesheet downloaded to /tmp dir
817 if ($remote_images){
818 $tmp_image = $img_path; // C:/
819 $orig_config_path = $key; // url
820 } else {
821 // else pack image found in zip
822 $tmp_image = $this->micro_root_dir . $name . '/' . $just_image_name; // C:/
823 $orig_config_path = $img_path; // url
824 }
825
826 // import the file to the media library if it exists
827 if (file_exists($tmp_image)) {
828 $this->imported_images[$just_image_name]['orig_config_path'] = $orig_config_path;
829
830 // note import_image_to_library() updates 'success' and 'new_config_path'
831 $id = $this->import_image_to_library($tmp_image, $just_image_name);
832
833 // report wp error if problem
834 if ( $id === 0 or is_wp_error($id) ) {
835 if (is_wp_error($id)){
836 $wp_error = '<p>'. $id->get_error_message() . '</p>';
837 } else {
838 $wp_error = '';
839 }
840 $this->log(
841 esc_html__('Move to media library failed', 'microthemer'),
842 '<p>' . sprintf(
843 /* translators: %s: file name */
844 esc_html__('%s was not imported due to an error.', 'microthemer'),
845 $this->root_rel($tmp_image)
846 ) . '</p>'
847 . $wp_error
848 );
849 }
850 }
851 }
852
853 // first report successfully moved images
854 $moved_list =
855 '<ul>';
856 $moved = false;
857 foreach ($this->imported_images as $just_image_name => $array){
858 if (!empty($array['success'])){
859 $moved_list.= '
860 <li>
861 '.$just_image_name.'
862 </li>';
863 $moved = true;
864 // also update the json data string
865 $replacements[$array['orig_config_path']] = $array['new_config_path'];
866 }
867 }
868 $moved_list.=
869 '</ul>';
870
871 // move was successful, update paths
872 if ($moved){
873 $this->log(
874 esc_html__('Images transferred to media library', 'microthemer'),
875 '<p>' . esc_html__('The following images were transferred from the design pack to your WordPress media library:', 'microthemer') . '</p>'
876 . $moved_list,
877 'notice'
878 );
879 // update paths in json file
880 return $this->replace_json_paths($json_config_file, $replacements, $data, $remote_images);
881 }
882 }
883
884 // update paths in json file
885 function replace_json_paths($json_config_file, $replacements, $data = false, $remote_images = false){
886
887 if (!$data){
888 if (!$data = $this->get_file_data($json_config_file)) {
889 return false;
890 }
891 }
892
893 // replace paths in string
894 $replacement_occurred = false;
895 foreach ($replacements as $orig => $new){
896 if (strpos($data, $orig) !== false){
897 $replacement_occurred = true;
898 $data = str_replace($orig, $new, $data);
899 }
900 }
901 if (!$replacement_occurred){
902 return false;
903 }
904
905 // just return updated json data if loading css stylesheet
906 if ($remote_images){
907 $this->log(
908 esc_html__('Image paths updated', 'microthemer'),
909 '<p>' . esc_html__('Images paths were successfully updated to reflect the new location or deletion of an image(s).', 'microthemer') . '</p>',
910 'notice'
911 );
912 return $data;
913 }
914
915 // update the config.json image paths for images successfully moved to the library
916 if (is_writable($json_config_file)) {
917 if ($write_file = @fopen($json_config_file, 'w')) {
918 if (fwrite($write_file, $data)) {
919 fclose($write_file);
920 $this->log(
921 esc_html__('Images paths updated', 'microthemer'),
922 '<p>' . esc_html__('Images paths were successfully updated to reflect the new location or deletion of an image(s).', 'microthemer') . '</p>',
923 'notice'
924 );
925 return true;
926 }
927 else {
928 $this->log(
929 esc_html__('Image paths failed to update.', 'microthemer'),
930 '<p>' . sprintf(/* translators: %s: application name */ esc_html__('Images paths could not be updated to reflect the new location of the images transferred to your media library. This happened because %s could not rewrite the config.json file.', 'microthemer'), $this->appName) . '</p>' . $this->permissionshelp
931 );
932 return false;
933 }
934 }
935 }
936 }
937
938 //Handle an individual file import.
939 function import_image_to_library($file, $just_image_name, $post_id = 0, $import_date = false) {
940 set_time_limit(60);
941 // Initially, Base it on the -current- time.
942 $time = current_time('mysql', 1);
943 // A writable uploads dir will pass this test. Again, there's no point overriding this one.
944 if ( ! ( ( $uploads = wp_upload_dir($time) ) && false === $uploads['error'] ) ) {
945 $this->log(
946 esc_html__('Uploads folder error', 'microthemer'),
947 $uploads['error']
948 );
949 return 0;
950 }
951
952 $wp_filetype = wp_check_filetype( $file, null );
953 $type = $ext = false;
954 extract( $wp_filetype );
955 if ( ( !$type || !$ext ) && !current_user_can( 'unfiltered_upload' ) ) {
956 $this->log(
957 esc_html__('Wrong file type', 'microthemer'),
958 '<p>' . esc_html__('Sorry, this file type is not permitted for security reasons.', 'microthemer') . '</p>'
959 );
960 return 0;
961 }
962
963 //Is the file already in the uploads folder?
964 if ( preg_match('|^' . preg_quote(str_replace('\\', '/', $uploads['basedir'])) . '(.*)$|i', $file, $mat) ) {
965 $filename = basename($file);
966 $new_file = $file;
967
968 $url = $uploads['baseurl'] . $mat[1];
969
970 $attachment = get_posts(array( 'post_type' => 'attachment', 'meta_key' => '_wp_attached_file', 'meta_value' => ltrim($mat[1], '/') ));
971 if ( !empty($attachment) ) {
972 $this->log(
973 esc_html__('Image already in library', 'microthemer'),
974 '<p>' . sprintf(
975 /* translators: %s: file name */
976 esc_html__('%s already exists in the WordPress media library and was therefore not moved', 'microthemer'),
977 $filename
978 ) . '</p>',
979 'warning'
980 );
981 return 0;
982 }
983 //OK, Its in the uploads folder, But NOT in WordPress's media library.
984 } else {
985 $filename = wp_unique_filename( $uploads['path'], basename($file));
986
987 // copy the file to the uploads dir
988 $new_file = $uploads['path'] . '/' . $filename;
989 if ( false === @rename( $file, $new_file ) ) {
990 $this->log(
991 esc_html__('Move to library failed', 'microthemer'),
992 '<p>' . sprintf(
993 /* translators: 1: source file name, 2: destination path */
994 esc_html__('%1$s could not be moved to %2$s', 'microthemer'),
995 $filename,
996 $uploads['path']
997 ) . '</p>',
998 'warning'
999 );
1000 return 0;
1001 }
1002
1003
1004 // Set correct file permissions
1005 $stat = stat( dirname( $new_file ));
1006 $perms = $stat['mode'] & 0000666;
1007 @ chmod( $new_file, $perms );
1008 // Compute the URL
1009 $url = $uploads['url'] . '/' . $filename;
1010 }
1011
1012 //Apply upload filters
1013 $return = apply_filters( 'wp_handle_upload', array( 'file' => $new_file, 'url' => $url, 'type' => $type ) );
1014 $new_file = $return['file'];
1015 $url = $return['url'];
1016 $type = $return['type'];
1017
1018 $title = preg_replace('!\.[^.]+$!', '', basename($new_file));
1019 $content = '';
1020
1021 // update the array for replacing paths in config.json
1022 $this->imported_images[$just_image_name]['success'] = true;
1023 $this->imported_images[$just_image_name]['new_config_path'] = $this->root_rel($url, false, true);
1024
1025 // use image exif/iptc data for title and caption defaults if possible
1026 if ( $image_meta = @wp_read_image_metadata($new_file) ) {
1027 if ( '' != trim($image_meta['caption']) )
1028 $content = trim($image_meta['caption']);
1029 }
1030
1031 if ( $time ) {
1032 $post_date_gmt = $time;
1033 $post_date = $time;
1034 } else {
1035 $post_date = current_time('mysql');
1036 $post_date_gmt = current_time('mysql', 1);
1037 }
1038
1039 // Construct the attachment array
1040 $attachment = array(
1041 'post_mime_type' => $type,
1042 'guid' => $url,
1043 'post_parent' => $post_id,
1044 'post_title' => $title,
1045 'post_name' => $title,
1046 'post_content' => $content,
1047 'post_date' => $post_date,
1048 'post_date_gmt' => $post_date_gmt
1049 );
1050
1051 $attachment = apply_filters('afs-import_details', $attachment, $file, $post_id, $import_date);
1052
1053 //Win32 fix:
1054 $new_file = str_replace( strtolower(str_replace('\\', '/', $uploads['basedir'])), $uploads['basedir'], $new_file);
1055
1056 // Save the data
1057 $id = wp_insert_attachment($attachment, $new_file, $post_id);
1058 if ( !is_wp_error($id) ) {
1059 $data = wp_generate_attachment_metadata( $id, $new_file );
1060 wp_update_attachment_metadata( $id, $data );
1061 }
1062
1063 return $id;
1064 }
1065
1066 // handle zip package
1067 function handle_zip_package() {
1068
1069 $temp_zipfile = isset($_FILES['upload_micro']['tmp_name']) ? $_FILES['upload_micro']['tmp_name'] : '';
1070 $filename = isset($_FILES['upload_micro']['name'])
1071 ? sanitize_file_name(wp_unslash($_FILES['upload_micro']['name']))
1072 : ''; // it won't be this name for long
1073
1074 // check the actual file extension rather than the client-supplied MIME type
1075 $filetype = wp_check_filetype($filename, array('zip' => 'application/zip'));
1076
1077 if (!$temp_zipfile || !is_uploaded_file($temp_zipfile) || $filetype['ext'] !== 'zip'){
1078 if ($temp_zipfile){
1079 @unlink($temp_zipfile); // del temp file
1080 }
1081 $this->log(
1082 esc_html__('Faulty zip file', 'microthemer'),
1083 '<p>' . esc_html__('The uploaded file was faulty or was not a zip file.', 'microthemer') . '</p>'
1084 );
1085 return false;
1086 }
1087
1088 $this->create_micro_theme($filename, 'unzip', $temp_zipfile);
1089 }
1090
1091 // read meta data from file
1092 function read_meta_file($meta_file) {
1093 // create default meta.txt file if it doesn't exist
1094 if (!is_file($meta_file)) {
1095 $_POST['theme_meta']['Name'] = $this->readable_name($this->preferences['theme_in_focus']);
1096 $this->update_meta_file($this->micro_root_dir . $this->preferences['theme_in_focus'].'/meta.txt');
1097 }
1098 if (is_file($meta_file)) {
1099 // check if it's readable
1100 if ( is_readable($meta_file) ) {
1101 //disable wptexturize
1102 remove_filter('get_theme_data', 'wptexturize');
1103 return $this->flx_get_theme_data( $meta_file );
1104 }
1105 else {
1106 $abs_meta_path = $this->micro_root_dir . $this->preferences['theme_in_focus'].'/meta.txt';
1107
1108 $this->log(
1109 esc_html__('Read meta.txt error', 'microthemer'),
1110 '<p>' . esc_html__('WordPress does not have permission to read: ', 'microthemer') .
1111 $this->root_rel($abs_meta_path) . '. '.$this->permissionshelp.'</p>'
1112 );
1113 return false;
1114 }
1115 }
1116 }
1117
1118 // read readme.txt data from file
1119 function read_readme_file($readme_file) {
1120
1121 // create default readme file if it doesn't exist
1122 if (!is_file($readme_file)) {
1123 $this->update_readme_file($this->micro_root_dir . $this->preferences['theme_in_focus'].'/readme.txt');
1124 }
1125 if (is_file($readme_file)) {
1126 // check if it's readable
1127 if ( is_readable($readme_file) ) {
1128 $fh = @fopen($readme_file, 'r');
1129 $length = filesize($readme_file);
1130 if ($length == 0) {
1131 $length = 1;
1132 }
1133 $data = fread($fh, $length);
1134 fclose($fh);
1135 return $data;
1136 }
1137 else {
1138 $abs_readme_path = $this->micro_root_dir . $this->preferences['theme_in_focus'].'/readme.txt';
1139 $this->log(
1140 esc_html__('Read readme.txt error', 'microthemer'),
1141 '<p>' . esc_html__('WordPress does not have permission to read: ', 'microthemer'),
1142 $this->root_rel($abs_readme_path) . '. '.$this->permissionshelp.'</p>'
1143 );
1144 return false;
1145 }
1146 }
1147 }
1148
1149 // adapted WordPress function for reading and formattings a template file
1150 function flx_get_theme_data( $theme_file ) {
1151 $default_headers = array(
1152 'Name' => 'Theme Name',
1153 'PackType' => 'Pack Type',
1154 'URI' => 'Theme URI',
1155 'Description' => 'Description',
1156 'Author' => 'Author',
1157 'AuthorURI' => 'Author URI',
1158 'Version' => 'Version',
1159 'Template' => 'Template',
1160 'Status' => 'Status',
1161 'Tags' => 'Tags'
1162 );
1163 // define allowed tags
1164 $themes_allowed_tags = array(
1165 'a' => array(
1166 'href' => array(),'title' => array()
1167 ),
1168 'abbr' => array(
1169 'title' => array()
1170 ),
1171 'acronym' => array(
1172 'title' => array()
1173 ),
1174 'code' => array(),
1175 'em' => array(),
1176 'strong' => array()
1177 );
1178 // get_file_data() - WP 2.8 compatibility function created for this
1179 $theme_data = get_file_data( $theme_file, $default_headers, 'theme' );
1180 $theme_data['Name'] = $theme_data['Title'] = wp_kses( $theme_data['Name'], $themes_allowed_tags );
1181 $theme_data['PackType'] = wp_kses( $theme_data['PackType'], $themes_allowed_tags );
1182 $theme_data['URI'] = esc_url( $theme_data['URI'] );
1183 $theme_data['Description'] = wp_kses( $theme_data['Description'], $themes_allowed_tags );
1184 $theme_data['AuthorURI'] = esc_url( $theme_data['AuthorURI'] );
1185 $theme_data['Template'] = wp_kses( $theme_data['Template'], $themes_allowed_tags );
1186 $theme_data['Version'] = wp_kses( $theme_data['Version'], $themes_allowed_tags );
1187 if ( empty($theme_data['Status']) )
1188 $theme_data['Status'] = 'publish';
1189 else
1190 $theme_data['Status'] = wp_kses( $theme_data['Status'], $themes_allowed_tags );
1191
1192 if ( empty($theme_data['Tags']) )
1193 $theme_data['Tags'] = array();
1194 else
1195 $theme_data['Tags'] = array_map( 'trim', explode( ',', wp_kses( $theme_data['Tags'], array() ) ) );
1196
1197 if ( empty($theme_data['Author']) ) {
1198 $theme_data['Author'] = $theme_data['AuthorName'] = __('Anonymous', 'microthemer');
1199 } else {
1200 $theme_data['AuthorName'] = wp_kses( $theme_data['Author'], $themes_allowed_tags );
1201 if ( empty( $theme_data['AuthorURI'] ) ) {
1202 $theme_data['Author'] = $theme_data['AuthorName'];
1203 } else {
1204 $theme_data['Author'] = sprintf( '<a href="%s" title="%s">%s</a>', $theme_data['AuthorURI'], esc_html__( 'Visit author homepage', 'microthemer' ), $theme_data['AuthorName'] );
1205 }
1206 }
1207 return $theme_data;
1208 }
1209
1210 // delete theme/pack
1211 function tvr_delete_micro_theme($dir_name) {
1212 $error = false;
1213
1214 // loop through files if they exist
1215 if (isset($this->file_structure[$dir_name]) && is_array($this->file_structure[$dir_name])) {
1216
1217 $serverPath = $this->micro_root_dir;
1218 $constructedPath = $serverPath . $dir_name;
1219 if (!$this->checkExpectedPath($constructedPath, $serverPath)){
1220 return false;
1221 }
1222
1223 foreach ($this->file_structure[$dir_name] as $file => $oneOrFileName) {
1224
1225 // there is an odd inconsistency with screenshot key referring to a filename
1226 // rather than the key being the file name
1227 $file = $oneOrFileName == 1 ? $file : $oneOrFileName;
1228
1229 if (!unlink($this->micro_root_dir . $dir_name.'/'.$file)) {
1230 $this->log(
1231 esc_html__('File delete error', 'microthemer'),
1232 '<p>' . esc_html__('Unable to delete: ', 'microthemer') .
1233 $this->root_rel($this->micro_root_dir .
1234 $dir_name.'/'.$file) . print_r($this->file_structure, true). '</p>'
1235 );
1236 $error = true;
1237 }
1238 }
1239 }
1240 if ($error != true) {
1241 $this->log(
1242 'Files successfully deleted',
1243 '<p>' . sprintf(
1244 /* translators: %s: directory path */
1245 esc_html__('All files within %s were successfully deleted.', 'microthemer'),
1246 $this->readable_name($dir_name)
1247 ) . '</p>',
1248 'dev-notice'
1249 );
1250 // attempt to delete empty directory
1251 if (!rmdir($this->micro_root_dir . $dir_name)) {
1252 $this->log(
1253 esc_html__('Delete directory error', 'microthemer'),
1254 '<p>' . sprintf(
1255 /* translators: %s: directory path */
1256 esc_html__('The empty directory: %s could not be deleted.', 'microthemer'),
1257 $this->readable_name($dir_name)
1258 ) . '</p>'
1259 );
1260 $error = true;
1261 }
1262 else {
1263 $this->log(
1264 esc_html__('Directory successfully deleted', 'microthemer'),
1265 '<p>' . sprintf(
1266 /* translators: %s: directory path */
1267 esc_html__('%s was successfully deleted.', 'microthemer'),
1268 $this->readable_name($dir_name)
1269 ) . '</p>',
1270 'notice'
1271 );
1272
1273 // reset the theme_in_focus value in the preferences table
1274 $pref_array['theme_in_focus'] = '';
1275 if (!$this->savePreferences($pref_array)) {
1276 // not much cause for a message
1277 }
1278
1279
1280 if ($error){
1281 return false;
1282 } else {
1283 return true;
1284 }
1285 }
1286 }
1287 }
1288
1289 // update the meta file
1290 function update_meta_file($meta_file) {
1291
1292 // check if the micro theme dir needs to be renamed
1293 if (isset($_POST['prev_micro_name']) and ($_POST['prev_micro_name'] != $_POST['theme_meta']['Name'])) {
1294 $orig_name = $this->micro_root_dir . $this->preferences['theme_in_focus'];
1295 $new_theme_in_focus = sanitize_file_name(sanitize_title($_POST['theme_meta']['Name']));
1296 // need to do unique dir check here too
1297 // Check for micro theme with same name
1298 if ($alt_name = $this->rename_if_required($this->micro_root_dir, $new_theme_in_focus)) {
1299 $new_theme_in_focus = $alt_name;
1300 // The dir had to be automatically renamed so update the visible name
1301 $_POST['theme_meta']['Name'] = $this->readable_name($new_theme_in_focus);
1302 }
1303 $new_name = $this->micro_root_dir . $new_theme_in_focus;
1304 // if the directory is writable
1305 if (is_writable($orig_name)) {
1306 if (rename($orig_name, $new_name)) {
1307 // if rename is successful...
1308
1309 // the meta file will have a different location now
1310 $meta_file = str_replace($this->preferences['theme_in_focus'], $new_theme_in_focus, $meta_file);
1311
1312 // update the files array directory key
1313 $cache = $this->file_structure[$this->preferences['theme_in_focus']];
1314 $this->file_structure[$new_theme_in_focus] = $cache;
1315 unset($this->file_structure[$this->preferences['theme_in_focus']]);
1316
1317 // update the value in the preferences table
1318 $pref_array = array();
1319 $pref_array['theme_in_focus'] = $new_theme_in_focus;
1320 if ($this->savePreferences($pref_array)) {
1321 $this->log(
1322 esc_html__('Design pack renamed', 'microthemer'),
1323 '<p>' . esc_html__('The design pack directory was successfully renamed on the server.', 'microthemer') . '</p>',
1324 'notice'
1325 );
1326 }
1327 }
1328 else {
1329 $this->log(
1330 esc_html__('Directory rename error', 'microthemer'),
1331 '<p>' . sprintf(
1332 /* translators: %s: directory path */
1333 esc_html__('The directory %s could not be renamed for some reason.', 'microthemer'),
1334 $this->root_rel($orig_name)
1335 ) . '</p>'
1336 );
1337 }
1338 }
1339 else {
1340 $this->log(
1341 esc_html__('Directory rename error', 'microthemer'),
1342 '<p>' . sprintf(
1343 /* translators: 1: directory path, 2: new theme name */
1344 esc_html__('WordPress does not have permission to rename the directory %1$s to match your new theme name "%2$s".', 'microthemer'),
1345 $this->root_rel($orig_name),
1346 esc_html($this->readable_name(sanitize_text_field(wp_unslash($_POST["theme_meta"]["Name"]))))
1347 ) . $this->permissionshelp.'.</p>'
1348 );
1349 }
1350 }
1351
1352
1353 // Create new file if it doesn't already exist
1354 if (!file_exists($meta_file)) {
1355 if (!$write_file = @fopen($meta_file, 'w')) {
1356 $this->log(
1357 /* translators: %s: file name */
1358 sprintf( esc_html__('Create %s error', 'microthemer'), 'meta.txt' ),
1359 '<p>' . sprintf(/* translators: %s: file path */ esc_html__('WordPress does not have permission to create: %s', 'microthemer'), $this->root_rel($meta_file) . '. '.$this->permissionshelp ) . '</p>'
1360 );
1361 }
1362 else {
1363 fclose($write_file);
1364 }
1365 $task = 'created';
1366 // set post variables if undefined (might be following initial export)
1367
1368 if (!isset($_POST['theme_meta']['Description'])) {
1369
1370 $current_user = wp_get_current_user();
1371 $_POST['theme_meta']['Description'] = "";
1372 $_POST['theme_meta']['PackType'] ='';
1373 $_POST['theme_meta']['Author'] = $current_user->display_name;
1374 $_POST['theme_meta']['AuthorURI'] = '';
1375 $_POST['theme_meta']['Template'] = '';
1376 $_POST['theme_meta']['Version'] = '1.0';
1377 $_POST['theme_meta']['Tags'] = '';
1378
1379 }
1380 }
1381 else {
1382 $task = 'updated';
1383 }
1384
1385 // check if it's writable - // need to remove carriage returns
1386 if ( is_writable($meta_file) ) {
1387
1388 $Name = !empty($_POST["theme_meta"]["Name"]) ? sanitize_text_field(wp_unslash($_POST["theme_meta"]["Name"])) : "";
1389 // PackType is a human label (e.g. "Theme Skin") — sanitize_key() would collapse it.
1390 $PackType = !empty($_POST["theme_meta"]["PackType"]) ? sanitize_text_field(wp_unslash($_POST["theme_meta"]["PackType"])) : "";
1391 $Description = !empty($_POST["theme_meta"]["Description"]) ? sanitize_textarea_field(wp_unslash($_POST["theme_meta"]["Description"])) : "";
1392 $Author = !empty($_POST["theme_meta"]["Author"]) ? sanitize_text_field(wp_unslash($_POST["theme_meta"]["Author"])) : "";
1393 $AuthorURI = !empty($_POST["theme_meta"]["AuthorURI"]) ? esc_url_raw(wp_unslash($_POST["theme_meta"]["AuthorURI"])) : "";
1394 $Template = !empty($_POST["theme_meta"]["Template"]) ? sanitize_text_field(wp_unslash($_POST["theme_meta"]["Template"])) : "";
1395 $Version = !empty($_POST["theme_meta"]["Version"]) ? sanitize_text_field(wp_unslash($_POST["theme_meta"]["Version"])) : "";
1396 $Tags = !empty($_POST["theme_meta"]["Tags"]) ? sanitize_text_field(wp_unslash($_POST["theme_meta"]["Tags"])) : "";
1397
1398 // $Name..$Tags are already wp_unslash()'d above; stripslashes() here would double-unslash
1399 // and eat literal backslashes a pack author typed (e.g. Windows-style paths in Description).
1400 $data = '/*
1401 Theme Name: '.wp_strip_all_tags($Name).'
1402 Pack Type: '.wp_strip_all_tags($PackType).'
1403 Description: '.wp_strip_all_tags(str_replace(array("\n", "\r"), array(" ", ""), $Description)).'
1404 Author: '.wp_strip_all_tags($Author).'
1405 Author URI: '.wp_strip_all_tags($AuthorURI).'
1406 Template: '.wp_strip_all_tags($Template).'
1407 Version: '.wp_strip_all_tags($Version).'
1408 Tags: '.wp_strip_all_tags($Tags).'
1409 DateCreated: '.gmdate('Y-m-d').'
1410 */';
1411
1412 // the file will be created if it doesn't exist. otherwise it is overwritten.
1413 $write_file = @fopen($meta_file, 'w');
1414 fwrite($write_file, $data);
1415 fclose($write_file);
1416 // success message
1417 $this->log(
1418 'meta.txt '.$task,
1419 '<p>' . sprintf( /* translators: 1: file name, 2: action performed (created/updated) */ esc_html__('The %1$s file for the design pack was %2$s', 'microthemer'), 'meta.txt', $task ) . '</p>',
1420 'dev-notice'
1421 );
1422 }
1423 else {
1424 $this->log(
1425 /* translators: %s: file name */
1426 sprintf( esc_html__('Write %s error', 'microthemer'), 'meta.txt'),
1427 '<p>' . esc_html__('WordPress does not have "write" permission for: ', 'microthemer') .
1428 $this->root_rel($meta_file) . '. '.$this->permissionshelp.'</p>'
1429 );
1430 }
1431 }
1432
1433 // update the readme file
1434 function update_readme_file($readme_file) {
1435 // Create new file if it doesn't already exist
1436 if (!file_exists($readme_file)) {
1437 if (!$write_file = @fopen($readme_file, 'w')) {
1438 $this->log(
1439 /* translators: %s: file name */
1440 sprintf( esc_html__('Create %s error', 'microthemer'), 'readme.txt'),
1441 '<p>' . sprintf(
1442 /* translators: %s: file path */
1443 esc_html__('WordPress does not have permission to create: %s', 'microthemer'),
1444 $this->root_rel($readme_file) . '. '.$this->permissionshelp
1445 ) . '</p>'
1446 );
1447 }
1448 else {
1449 fclose($write_file);
1450 }
1451 $task = 'created';
1452 // set post variable if undefined (might be defined if theme dir has been
1453 // created manually and then user is submitting readme info for the first time)
1454 if (!isset($_POST['tvr_theme_readme'])) {
1455 $_POST['tvr_theme_readme'] = '';
1456 }
1457 }
1458 else {
1459 $task = 'updated';
1460 }
1461 // check if it's writable
1462 if ( is_writable($readme_file) ) {
1463 // Pack instructions support basic post HTML, but do not need executable or embedded markup.
1464 $data = wp_kses_post(wp_unslash($_POST["tvr_theme_readme"]));
1465 // the file will be created if it doesn't exist. otherwise it is overwritten.
1466 $write_file = @fopen($readme_file, 'w');
1467 fwrite($write_file, $data);
1468 fclose($write_file);
1469 // success message
1470 $this->log(
1471 'readme.txt '.$task,
1472 '<p>' . sprintf(
1473 /* translators: 1: file name, 2: action performed (created/updated) */
1474 esc_html__('The %1$s file for the design pack was %2$s', 'microthemer'),
1475 'readme.txt', $task
1476 ) . '</p>',
1477 'dev-notice'
1478 );
1479 }
1480 else {
1481 $this->log(
1482 /* translators: %s: file name */
1483 sprintf( esc_html__('Write %s error', 'microthemer'), 'readme.txt'),
1484 '<p>' . esc_html__('WordPress does not have "write" permission for: ', 'microthemer') .
1485 $this->root_rel($readme_file) . '. '.$this->permissionshelp.'</p>'
1486 );
1487 }
1488 }
1489
1490 // handle file upload to a pack
1491 function handle_file_upload() {
1492
1493 // if no error
1494 if ($_FILES['upload_file']['error'] == 0) {
1495 $file = sanitize_file_name(wp_unslash($_FILES["upload_file"]["name"]));
1496 // check if the file has a valid extension
1497 if ($this->is_acceptable($file)) {
1498 $dest_dir = $this->micro_root_dir . $this->preferences['theme_in_focus'].'/';
1499 // check if the directory is writable
1500 if (is_writeable($dest_dir) ) {
1501 // copy file if safe
1502 if (is_uploaded_file($_FILES['upload_file']['tmp_name'])
1503 and copy($_FILES['upload_file']['tmp_name'], $dest_dir . $file)) {
1504 $this->log(
1505 esc_html__('File successfully uploaded', 'microthemer'),
1506 '<p>' . wp_kses(
1507 sprintf(
1508 /* translators: %s: file name */
1509 __('<b>%s</b> was successfully uploaded.', 'microthemer'),
1510 htmlentities($file)
1511 ),
1512 array( 'b' => array() )
1513 ) . '</p>',
1514 'notice'
1515 );
1516 // update the file_structure array
1517 $this->file_structure[$this->preferences['theme_in_focus']][$file] = 1;
1518
1519 // resize file if it's a screeshot
1520 if ($this->is_screenshot($file)) {
1521 $img_full_path = $dest_dir . $file;
1522 // get the screenshot size, resize if too big
1523 list($width, $height) = getimagesize($img_full_path);
1524 if ($width > 896 or $height > 513){
1525 $this->wp_resize(
1526 $img_full_path,
1527 896,
1528 513,
1529 $img_full_path);
1530 }
1531 // now do thumbnail
1532 $thumbnail = $dest_dir . 'screenshot-small.'. $this->get_extension($file);
1533 $root_rel_thumb = $this->root_rel($thumbnail);
1534 if (!$final_dimensions = $this->wp_resize(
1535 $img_full_path,
1536 145,
1537 83,
1538 $thumbnail)) {
1539 $this->log(
1540 esc_html__('Screenshot thumbnail error', 'microthemer'),
1541 '<p>' . wp_kses(
1542 sprintf(
1543 /* translators: %s: image file path */
1544 __('Could not resize <b>%s</b> to thumbnail proportions.', 'microthemer'),
1545 $root_rel_thumb
1546 ),
1547 array( 'b' => array() )
1548 ) . $img_full_path .
1549 esc_html__(' thumb: ', 'microthemer') .$thumbnail.'</p>'
1550 );
1551 }
1552 else {
1553 // update the file_structure array
1554 $file = basename($thumbnail);
1555 $this->file_structure[$this->preferences['theme_in_focus']][$file] = 1;
1556 $this->log(
1557 esc_html__('Screenshot thumbnail successfully created', 'microthemer'),
1558 '<p>' . sprintf(
1559 /* translators: %s: thumbnail file path */
1560 esc_html__('%s was successfully created.', 'microthemer'),
1561 $root_rel_thumb
1562 ) . '</p>',
1563 'notice'
1564 );
1565 }
1566 }
1567
1568
1569 }
1570 }
1571 // it's not writable
1572 else {
1573 $this->log(
1574 esc_html__('Write to directory error', 'microthemer'),
1575 '<p>'. esc_html__('WordPress does not have "Write" permission to the directory: ', 'microthemer') .
1576 $this->root_rel($dest_dir) . '. '.$this->permissionshelp.'.</p>'
1577 );
1578 }
1579 }
1580 else {
1581 $this->log(
1582 esc_html__('Invalid file type', 'microthemer'),
1583 '<p>' . esc_html__('You have uploaded a file type that is not allowed.', 'microthemer') . '</p>'
1584 );
1585
1586 }
1587 }
1588 // there was an error - save in global message
1589 else {
1590 $this->log_file_upload_error($_FILES['upload_file']['error']);
1591 }
1592 }
1593
1594 // log file upload problem
1595 function log_file_upload_error($error){
1596 switch ($error) {
1597 case 1:
1598 $this->log(
1599 esc_html__('File upload limit reached', 'microthemer'),
1600 '<p>' . esc_html__('The file you uploaded reached your "upload_max_filesize" limit. This is a PHP setting on your server.', 'microthemer') . '</p>'
1601 );
1602 break;
1603 case 2:
1604 $this->log(
1605 esc_html__('File size too big', 'microthemer'),
1606 '<p>' . esc_html__('The file you uploaded reached your "max_file_size" limit. This is a PHP setting on your server.', 'microthemer') . '</p>'
1607 );
1608 break;
1609 case 3:
1610 $this->log(
1611 esc_html__('Partial upload', 'microthemer'),
1612 '<p>' . esc_html__('The file you uploaded only partially uploaded.', 'microthemer') . '</p>'
1613 );
1614 break;
1615 case 4:
1616 $this->log(
1617 esc_html__('No file uploaded', 'microthemer'),
1618 '<p>' . esc_html__('No file was detected for upload.', 'microthemer') . '</p>'
1619 );
1620 break;
1621 }
1622 }
1623
1624 // resize image using wordpress functions
1625 function wp_resize($path, $w, $h, $dest, $crop = true){
1626 // ... (rest of the function is the same)
1627 $image = wp_get_image_editor( $path );
1628 if ( ! is_wp_error( $image ) ) {
1629 $image->resize( $w, $h, $crop );
1630 $image->save( $dest );
1631 return true;
1632 } else {
1633 return false;
1634 }
1635 }
1636
1637 // resize image (legacy/fallback)
1638 function resize($img, $max_width, $max_height, $newfilename) {
1639 //Check if GD extension is loaded
1640 if (!extension_loaded('gd') && !extension_loaded('gd2')) {
1641 $this->log(
1642 esc_html__('GD not loaded', 'microthemer'),
1643 '<p>' . esc_html__('The PHP extension GD is not loaded.', 'microthemer') . '</p>'
1644 );
1645 return false;
1646 }
1647 //Get Image size info
1648 $imgInfo = getimagesize($img);
1649 switch ($imgInfo[2]) {
1650 case 1: $im = imagecreatefromgif($img); break;
1651 case 2: $im = imagecreatefromjpeg($img); break;
1652 case 3: $im = imagecreatefrompng($img); break;
1653 default:
1654 $this->log(
1655 esc_html__('File type error', 'microthemer'),
1656 '<p>' . esc_html__('Unsuported file type. Are you sure you uploaded an image?', 'microthemer') . '</p>'
1657 );
1658
1659 return false; break;
1660 }
1661 // orig dimensions
1662 $width = $imgInfo[0];
1663 $height = $imgInfo[1];
1664 // set proportional max_width and max_height if one or the other isn't specified
1665 if ( empty($max_width)) {
1666 $max_width = round($width/($height/$max_height));
1667 }
1668 if ( empty($max_height)) {
1669 $max_height = round($height/($width/$max_width));
1670 }
1671 // abort if user tries to enlarge a pic
1672 if (($max_width > $width) or ($max_height > $height)) {
1673 $this->log(
1674 esc_html__('Dimensions too big', 'microthemer'),
1675 '<p>' . sprintf(
1676 /* translators: 1: requested width, 2: requested height, 3: original width, 4: original height */
1677 esc_html__('The resize dimensions you specified (%1$s x %2$s) are bigger than the original image (%3$s x %4$s). This is not allowed.', 'microthemer'),
1678 $max_width, $max_height, $width, $height
1679 ) . '</p>'
1680 );
1681 return false;
1682 }
1683
1684 // proportional resizing
1685 $x_ratio = $max_width / $width;
1686 $y_ratio = $max_height / $height;
1687 if (($width <= $max_width) && ($height <= $max_height)) {
1688 $tn_width = $width;
1689 $tn_height = $height;
1690 }
1691 else if (($x_ratio * $height) < $max_height) {
1692 $tn_height = ceil($x_ratio * $height);
1693 $tn_width = $max_width;
1694 }
1695 else {
1696 $tn_width = ceil($y_ratio * $width);
1697 $tn_height = $max_height;
1698 }
1699 // for compatibility
1700 $nWidth = $tn_width;
1701 $nHeight = $tn_height;
1702 $final_dimensions['w'] = $nWidth;
1703 $final_dimensions['h'] = $nHeight;
1704 $newImg = imagecreatetruecolor($nWidth, $nHeight);
1705 /* Check if this image is PNG or GIF, then set if Transparent*/
1706 if(($imgInfo[2] == 1) or ($imgInfo[2]==3)) {
1707 imagealphablending($newImg, false);
1708 imagesavealpha($newImg,true);
1709 $transparent = imagecolorallocatealpha($newImg, 255, 255, 255, 127);
1710 imagefilledrectangle($newImg, 0, 0, $nWidth, $nHeight, $transparent);
1711 }
1712 imagecopyresampled($newImg, $im, 0, 0, 0, 0, $nWidth, $nHeight, $imgInfo[0], $imgInfo[1]);
1713 // Generate the file, and rename it to $newfilename
1714 switch ($imgInfo[2]) {
1715 case 1: imagegif($newImg,$newfilename); break;
1716 case 2: imagejpeg($newImg,$newfilename); break;
1717 case 3: imagepng($newImg,$newfilename); break;
1718 default:
1719 $this->log(
1720 esc_html__('Image resize failed', 'microthemer'),
1721 '<p>' . esc_html__('Your image could not be resized.', 'microthemer') . '</p>'
1722 );
1723 return false;
1724 break;
1725 }
1726 return $final_dimensions;
1727 }
1728
1729 }
1730 // phpcs:enable
1731