Child Theme Configurator - Version 1.6.3

Version Description

  • New Feature: Better child theme handling for multisite. Moved interface to Themes menu and check for network enabled.
  • Fix: Restrict child theme configurator access to install_themes capability
  • Fix: Preview links to Themes admin if not network enabled to prevent 'Cheatin, uh?' error.
Download this release

Release Info

Developer lilaeamedia
Plugin Icon 128x128 Child Theme Configurator
Version 1.6.3
Comparing to
See all releases

Code changes from version 1.6.2.1 to 1.6.3

child-theme-configurator.php CHANGED
@@ -1,23 +1,23 @@
1
  <?php
2
  // Exit if accessed directly
3
- if ( !defined('ABSPATH')) exit;
4
 
5
  /*
6
  Plugin Name: Child Theme Configurator
7
  Plugin URI: http://www.lilaeamedia.com/plugins/child-theme-configurator/
8
- Description: Create a Child Theme from any installed Theme. Each CSS selector, rule and value can then be searched, previewed and modified.
9
- Version: 1.6.2.1
10
  Author: Lilaea Media
11
  Author URI: http://www.lilaeamedia.com/
12
  Text Domain: chld_thm_cfg
13
  Domain Path: /lang
14
  License: GPLv2
15
- Copyright (C) 2014 Lilaea Media
16
  */
17
 
18
- defined( 'LF' ) or define( 'LF', "\n");
19
  defined( 'CHLD_THM_CFG_OPTIONS' ) or define( 'CHLD_THM_CFG_OPTIONS', 'chld_thm_cfg_options' );
20
- defined( 'CHLD_THM_CFG_VERSION' ) or define( 'CHLD_THM_CFG_VERSION', '1.6.2.1' );
21
  defined( 'CHLD_THM_CFG_MIN_WP_VERSION' ) or define( 'CHLD_THM_CFG_MIN_WP_VERSION', '3.7' );
22
  defined( 'CHLD_THM_CFG_MAX_SELECTORS' ) or define( 'CHLD_THM_CFG_MAX_SELECTORS', '50000' );
23
  defined( 'CHLD_THM_CFG_MAX_RECURSE_LOOPS' ) or define( 'CHLD_THM_CFG_MAX_RECURSE_LOOPS', '1000' );
@@ -25,23 +25,26 @@ if ( !defined('ABSPATH')) exit;
25
 
26
  class ChildThemeConfigurator {
27
  static function init() {
28
- $lang_dir = dirname(__FILE__) . '/lang';
29
- load_plugin_textdomain('chld_thm_cfg', FALSE, $lang_dir, $lang_dir);
 
30
  global $wp_version;
31
- if (version_compare($wp_version, CHLD_THM_CFG_MIN_WP_VERSION) < 0):
32
- add_action('admin_notices', 'ChildthemeConfigurator::version_notice');
33
  return;
34
  endif;
35
  // setup admin hooks
36
- add_action( 'admin_menu', 'ChildThemeConfigurator::admin' );
37
- add_action( 'wp_ajax_ctc_update', 'ChildThemeConfigurator::save' );
38
- add_action( 'wp_ajax_ctc_query', 'ChildThemeConfigurator::query' );
 
 
39
  }
40
  static function ctc() {
41
  // create admin object
42
  global $chld_thm_cfg;
43
  if ( !isset( $chld_thm_cfg ) ):
44
- include_once( dirname(__FILE__) . '/includes/class-ctc.php' );
45
  $chld_thm_cfg = new ChildThemeConfiguratorAdmin( __FILE__ );
46
  endif;
47
  return $chld_thm_cfg;
@@ -54,16 +57,31 @@ if ( !defined('ABSPATH')) exit;
54
  // ajax read
55
  self::ctc()->ajax_query_css();
56
  }
 
 
 
 
 
 
 
 
 
 
57
  static function admin() {
58
- // setup admin page
59
  $hook = add_management_page(
60
- __( 'Child Theme Configurator', 'chld_thm_cfg' ),
61
- __( 'Child Themes', 'chld_thm_cfg' ),
62
- 'edit_theme_options',
63
- CHLD_THM_CFG_MENU,
64
- 'ChildThemeConfigurator::render'
65
  );
66
- add_action('load-' . $hook, 'ChildThemeConfigurator::page_init');
 
 
 
 
 
 
67
  }
68
  static function page_init() {
69
  // start admin controller
@@ -75,14 +93,14 @@ if ( !defined('ABSPATH')) exit;
75
  }
76
  static function version_notice() {
77
  deactivate_plugins( plugin_basename( __FILE__ ) );
78
- unset($_GET['activate']);
79
- echo '<div class="update-nag"><p>' . sprintf(__('Child Theme Configurator requires WordPress version %s or later.', 'chld_thm_cfg'), CHLD_THM_CFG_MIN_WP_VERSION) . '</p></div>' . LF;
80
  }
81
  }
82
 
83
  if ( is_admin() ) ChildThemeConfigurator::init();
84
 
85
- register_uninstall_hook( __FILE__ , 'chld_thm_cfg_delete_plugin' );
86
 
87
  function chld_thm_cfg_delete_plugin() {
88
  delete_option( CHLD_THM_CFG_OPTIONS );
1
  <?php
2
  // Exit if accessed directly
3
+ if ( !defined( 'ABSPATH' ) ) exit;
4
 
5
  /*
6
  Plugin Name: Child Theme Configurator
7
  Plugin URI: http://www.lilaeamedia.com/plugins/child-theme-configurator/
8
+ Description: Create a Child Theme and customize the stylesheet as you wish. Search, preview and modify any selector, rule or value using this fast CSS editor.
9
+ Version: 1.6.3
10
  Author: Lilaea Media
11
  Author URI: http://www.lilaeamedia.com/
12
  Text Domain: chld_thm_cfg
13
  Domain Path: /lang
14
  License: GPLv2
15
+ Copyright (C) 2014-2015 Lilaea Media
16
  */
17
 
18
+ defined( 'LF' ) or define( 'LF', "\n" );
19
  defined( 'CHLD_THM_CFG_OPTIONS' ) or define( 'CHLD_THM_CFG_OPTIONS', 'chld_thm_cfg_options' );
20
+ defined( 'CHLD_THM_CFG_VERSION' ) or define( 'CHLD_THM_CFG_VERSION', '1.6.3' );
21
  defined( 'CHLD_THM_CFG_MIN_WP_VERSION' ) or define( 'CHLD_THM_CFG_MIN_WP_VERSION', '3.7' );
22
  defined( 'CHLD_THM_CFG_MAX_SELECTORS' ) or define( 'CHLD_THM_CFG_MAX_SELECTORS', '50000' );
23
  defined( 'CHLD_THM_CFG_MAX_RECURSE_LOOPS' ) or define( 'CHLD_THM_CFG_MAX_RECURSE_LOOPS', '1000' );
25
 
26
  class ChildThemeConfigurator {
27
  static function init() {
28
+ // initialize languages
29
+ load_plugin_textdomain( 'chld_thm_cfg', FALSE, basename( dirname( __FILE__ ) ) . '/lang' );
30
+ // verify WP version support
31
  global $wp_version;
32
+ if ( version_compare( $wp_version, CHLD_THM_CFG_MIN_WP_VERSION ) < 0 ):
33
+ add_action( 'admin_notices', 'ChildthemeConfigurator::version_notice' );
34
  return;
35
  endif;
36
  // setup admin hooks
37
+ add_action( 'network_admin_menu', 'ChildThemeConfigurator::network_admin' );
38
+ add_action( 'admin_menu', 'ChildThemeConfigurator::admin' );
39
+ // setup ajax actions
40
+ add_action( 'wp_ajax_ctc_update', 'ChildThemeConfigurator::save' );
41
+ add_action( 'wp_ajax_ctc_query', 'ChildThemeConfigurator::query' );
42
  }
43
  static function ctc() {
44
  // create admin object
45
  global $chld_thm_cfg;
46
  if ( !isset( $chld_thm_cfg ) ):
47
+ include_once( dirname( __FILE__ ) . '/includes/class-ctc.php' );
48
  $chld_thm_cfg = new ChildThemeConfiguratorAdmin( __FILE__ );
49
  endif;
50
  return $chld_thm_cfg;
57
  // ajax read
58
  self::ctc()->ajax_query_css();
59
  }
60
+ static function network_admin() {
61
+ $hook = add_theme_page(
62
+ __( 'Child Theme Configurator', 'chld_thm_cfg' ),
63
+ __( 'Child Themes', 'chld_thm_cfg' ),
64
+ 'install_themes',
65
+ CHLD_THM_CFG_MENU,
66
+ 'ChildThemeConfigurator::render'
67
+ );
68
+ add_action( 'load-' . $hook, 'ChildThemeConfigurator::page_init' );
69
+ }
70
  static function admin() {
 
71
  $hook = add_management_page(
72
+ __( 'Child Theme Configurator', 'chld_thm_cfg' ),
73
+ __( 'Child Themes', 'chld_thm_cfg' ),
74
+ 'install_themes',
75
+ CHLD_THM_CFG_MENU,
76
+ 'ChildThemeConfigurator::render'
77
  );
78
+ add_filter( 'plugin_action_links_' . plugin_basename( __FILE__ ), 'ChildThemeConfigurator::action_links' );
79
+ add_action( 'load-' . $hook, 'ChildThemeConfigurator::page_init' );
80
+ }
81
+ static function action_links( $actions ) {
82
+ $actions[] = '<a href="' . admin_url( 'tools.php?page=' . CHLD_THM_CFG_MENU ). '">'
83
+ . __( 'Child Themes', 'chld_thm_cfg' ) . '</a>' . LF;
84
+ return $actions;
85
  }
86
  static function page_init() {
87
  // start admin controller
93
  }
94
  static function version_notice() {
95
  deactivate_plugins( plugin_basename( __FILE__ ) );
96
+ unset( $_GET['activate'] );
97
+ echo '<div class="update-nag"><p>' . sprintf( __( 'Child Theme Configurator requires WordPress version %s or later.', 'chld_thm_cfg' ), CHLD_THM_CFG_MIN_WP_VERSION ) . '</p></div>' . LF;
98
  }
99
  }
100
 
101
  if ( is_admin() ) ChildThemeConfigurator::init();
102
 
103
+ register_uninstall_hook( __FILE__, 'chld_thm_cfg_delete_plugin' );
104
 
105
  function chld_thm_cfg_delete_plugin() {
106
  delete_option( CHLD_THM_CFG_OPTIONS );
includes/class-ctc-css.php CHANGED
@@ -1,18 +1,18 @@
1
  <?php
2
  // Exit if accessed directly
3
- if ( !defined('ABSPATH')) exit;
4
 
5
  /*
6
  Class: ChildThemeConfiguratorCSS
7
  Plugin URI: http://www.lilaeamedia.com/plugins/child-theme-configurator/
8
  Description: Handles all CSS output, parsing, normalization
9
- Version: 1.6.2.1
10
  Author: Lilaea Media
11
  Author URI: http://www.lilaeamedia.com/
12
  Text Domain: chld_thm_cfg
13
  Domain Path: /lang
14
  License: GPLv2
15
- Copyright (C) 2014 Lilaea Media
16
  */
17
  class ChildThemeConfiguratorCSS {
18
  // data dictionaries
@@ -104,68 +104,68 @@ class ChildThemeConfiguratorCSS {
104
  $this->sel_ndx = array();
105
  $this->val_ndx = array();
106
  $this->parntss = array();
107
- $this->imports = array('child' => array(), 'parnt' => array());
108
  }
109
-
110
  function ctc() {
111
  global $chld_thm_cfg;
112
  return $chld_thm_cfg;
113
  }
114
-
115
  function read_config() {
116
 
117
- if ($configarray = get_option(CHLD_THM_CFG_OPTIONS . '_configvars')):
118
- foreach ($this->configvars as $configkey)
119
- if (isset($configarray[$configkey]))
120
  $this->{$configkey} = $configarray[$configkey];
121
- foreach ($this->dicts as $configkey):
122
- if ($configarray = get_option(CHLD_THM_CFG_OPTIONS . '_' . $configkey))
123
  $this->{$configkey} = $configarray;
124
  endforeach;
125
  else:
126
  return FALSE;
127
  endif;
128
  }
129
-
130
  function save_config() {
131
 
132
  $configarray = array();
133
- foreach ($this->configvars as $configkey)
134
  $configarray[$configkey] = $this->{$configkey};
135
- update_option(CHLD_THM_CFG_OPTIONS . '_configvars', $configarray);
136
- foreach ($this->dicts as $configkey)
137
- update_option(CHLD_THM_CFG_OPTIONS . '_' . $configkey, $this->{$configkey});
138
  // remove pre 1.5.4 options
139
- delete_option(CHLD_THM_CFG_OPTIONS);
140
  }
141
  /*
142
  * get_prop
143
- * Getter interface (data sliced different ways depending on objname)
144
  */
145
- function get_prop($objname, $params = null) {
146
- switch ($objname):
147
  case 'imports':
148
- return $this->obj_to_utf8(is_array($this->imports['child']) ? (current($this->imports['child']) == 1 ? array_keys($this->imports['child']) : array_keys(array_flip($this->imports['child']))) : array());
149
  case 'sel_ndx':
150
- return $this->obj_to_utf8($this->denorm_sel_ndx(empty($params['key'])?null:$params['key']));
151
  case 'rule_val':
152
- return empty($params['key']) ? array() : $this->denorm_rule_val($params['key']);
153
  case 'val_qry':
154
- if (isset($params['rule']) && isset($this->dict_rule[$params['rule']])):
155
- return empty($params['key']) ?
156
- array() : $this->denorm_val_query($params['key'], $params['rule']);
157
  endif;
158
  case 'sel_val':
159
- return empty($params['key']) ?
160
- array() : $this->denorm_sel_val($params['key']);
161
  case 'rule':
162
- return $this->obj_to_utf8(array_flip($this->dict_rule));
163
  case 'child':
164
  return $this->child;
165
  case 'parnt':
166
  return $this->parnt;
167
  case 'parntss':
168
- return isset($this->parntss) ? $this->parntss : array();
169
  case 'configtype':
170
  return $this->configtype;
171
  case 'child_name':
@@ -176,18 +176,18 @@ class ChildThemeConfiguratorCSS {
176
  return $this->child_version;
177
  case 'preview':
178
  $this->styles = '';
179
- if (empty($params['key']) || 'child' == $params['key']):
180
- $this->read_stylesheet('child');
181
  else:
182
- if (isset($this->parntss)):
183
- foreach ($this->parntss as $template):
184
  $this->styles .= '/*** BEGIN ' . $template . ' ***/' . LF;
185
- $this->read_stylesheet('parnt', $template);
186
  $this->styles .= '/*** END ' . $template . ' ***/' . LF;
187
  endforeach;
188
  endif;
189
  $this->styles .= '/*** BEGIN style.css ***/' . LF;
190
- $this->read_stylesheet('parnt');
191
  $this->styles .= '/*** END style.css ***/' . LF;
192
  endif;
193
  $this->normalize_css();
@@ -200,55 +200,56 @@ class ChildThemeConfiguratorCSS {
200
  * set_prop
201
  * Setter interface (scalar values only)
202
  */
203
- function set_prop($prop, $value) {
204
- if (is_scalar($this->{$prop}))
205
  $this->{$prop} = $value;
206
  else return FALSE;
207
  }
208
-
209
  function normalize_css() {
210
- if (preg_match("/(\}[\w\#\.]|; *\})/", $this->styles)): // prettify compressed CSS
211
- $this->styles = preg_replace("/\*\/\s*/s", "*/\n", $this->styles); // end comment
212
- $this->styles = preg_replace("/\{\s*/s", " {\n ", $this->styles); // open brace
213
- $this->styles = preg_replace("/;\s*/s", ";\n ", $this->styles); // semicolon
214
- $this->styles = preg_replace("/\s*\}\s*/s", "\n}\n", $this->styles); // close brace
215
  endif;
216
  }
217
-
218
  function get_css_header() {
219
- $parnt = $this->get_prop('parnt');
220
  return '/*' . LF
221
- . 'Theme Name: ' . $this->get_prop('child_name') . LF
222
  . 'Template: ' . $parnt . LF
223
- . 'Author: ' . $this->get_prop('author'). LF
224
- . 'Version: ' . $this->get_prop('version') . LF
225
- . 'Updated: ' . current_time('mysql') . LF
226
  . '*/' . LF . LF
227
  . '@charset "UTF-8";' . LF . LF
228
- . ('import' == $this->enqueue ? '@import url(\'../' . $parnt . '/style.css\');' . LF : '');
229
  }
230
-
231
- function get_child_target($file = 'style.css') {
232
- return trailingslashit(get_theme_root()) . trailingslashit($this->get_prop('child')) . $file;
233
  }
234
- function get_parent_source($file = 'style.css') {
235
- return trailingslashit(get_theme_root()) . trailingslashit($this->get_prop('parnt')) . $file;
 
236
  }
237
 
238
  /*
239
  * update_arrays
240
  * accepts CSS properties as raw strings and normilizes into
241
  * CTC object arrays, creating update cache in the process.
242
- * Update cache is returned to UI via AJAX to refresh page
243
  */
244
- function update_arrays($template, $query, $sel, $rule = null, $value = null, $important = 0, $seq = null) {
245
 
246
  // normalize selector styling
247
- $sel = implode(', ', preg_split('#\s*,\s*#s', trim($sel)));
248
  // add selector and query to index
249
- if (!isset($this->dict_query[$query])) $this->dict_query[$query] = ++$this->querykey;
250
- if (!isset($this->dict_sel[$sel])) $this->dict_sel[$sel] = ++$this->selkey;
251
- if (!isset($this->sel_ndx[$this->dict_query[$query]][$this->dict_sel[$sel]])):
252
  // increment key number
253
  $this->sel_ndx[$this->dict_query[$query]][$this->dict_sel[$sel]] = ++$this->qskey;
254
 
@@ -256,7 +257,7 @@ class ChildThemeConfiguratorCSS {
256
  $this->dict_qs[$this->qskey]['q'] = $this->dict_query[$query];
257
  // tell the UI to update a single cached query/selector lookup by passing 'qsid' as the key
258
  // (normally the entire array is replaced):
259
- if ($this->ctc()->cache_updates)
260
  $this->ctc()->updates[] = array(
261
  'obj' => 'sel_ndx',
262
  'key' => 'qsid',
@@ -268,14 +269,14 @@ class ChildThemeConfiguratorCSS {
268
  );
269
  endif;
270
  // update sequence for this selector if this is a later instance to keep cascade priority
271
- if (!isset($this->dict_seq[$this->qskey]))
272
  $this->dict_seq[$this->qskey] = $this->qskey;
273
  // set data and value
274
- if ($rule):
275
- if (!isset($this->dict_rule[$rule])):
276
  $this->dict_rule[$rule] = ++$this->rulekey;
277
  // tell the UI to update a single cached rule:
278
- if ($this->ctc()->cache_updates):
279
  $this->ctc()->updates[] = array(
280
  'obj' => 'rule',
281
  'key' => $this->rulekey,
@@ -285,24 +286,24 @@ class ChildThemeConfiguratorCSS {
285
  endif;
286
  $qsid = $this->sel_ndx[$this->dict_query[$query]][$this->dict_sel[$sel]];
287
  $ruleid = $this->dict_rule[$rule];
288
- if (!isset($this->dict_val[$value])):
289
  $this->dict_val[$value] = ++$this->valkey;
290
  endif;
291
  $this->val_ndx[$qsid][$ruleid][$template] = $this->dict_val[$value];
292
  // set the important flag for this value
293
  $this->val_ndx[$qsid][$ruleid]['i_' . $template] = $important;
294
  // tell the UI to add a single cached query/selector data array:
295
- if ($this->ctc()->cache_updates):
296
  $updatearr = array(
297
  'obj' => 'sel_val',
298
  'key' => $qsid,
299
- 'data' => $this->denorm_sel_val($qsid),
300
  );
301
  $this->ctc()->updates[] = $updatearr;
302
  endif;
303
- if (isset($seq)): // this is a renamed selector
304
  $this->dict_seq[$qsid] = $seq;
305
- if ($this->ctc()->cache_updates):
306
  $this->ctc()->updates[] = array(
307
  'obj' => 'rewrite',
308
  'key' => $qsid,
@@ -312,72 +313,75 @@ class ChildThemeConfiguratorCSS {
312
  endif;
313
  endif;
314
  }
315
-
316
- function read_stylesheet($template = 'child', $file = 'style.css') {
317
 
318
  // these conditions support revert/restore option in 1.6.0+
319
- if ('all' == $file) return;
320
- elseif ('' == $file) $file = 'style.css';
321
  // end revert/restore conditions
322
 
323
- $source = $this->get_prop($template);
324
- $configtype = $this->get_prop('configtype');
325
- if (empty($source) || !is_scalar($source)) return FALSE;
326
  $themedir = get_theme_root() . '/' . $source;
327
- $stylesheet = apply_filters('chld_thm_cfg_' . $template, $themedir . '/' . $file , $this);
328
 
329
  // read stylesheet
330
- if ($stylesheet_verified = $this->is_file_ok($stylesheet, 'read')):
331
- $import_url = preg_replace('%^' . preg_quote($themedir) . '/%', '', $stylesheet_verified);
332
- $this->styles .= @file_get_contents($stylesheet_verified) . "\n";
333
  endif;
334
  }
335
-
 
 
 
336
  function recurse_directory( $rootdir, $ext = 'css', $all = FALSE ) {
337
  if ( !$this->is_file_ok( $rootdir, 'search' ) ) return array(); // make sure we are only recursing theme and plugin files
338
  $files = array();
339
- $dirs = array($rootdir);
340
  $loops = 0;
341
  if ( 'img' == $ext ):
342
 
343
- $ext = '(' . implode('|', array_keys($this->ctc()->imgmimes)) . ')';
344
  endif;
345
- while(count($dirs) && $loops < CHLD_THM_CFG_MAX_RECURSE_LOOPS ):
346
  $loops++;
347
- $dir = array_shift($dirs);
348
- if ($handle = opendir($dir)):
349
- while (FALSE !== ($file = readdir($handle))):
350
- if (preg_match("/^\./", $file)) continue;
351
- $filepath = trailingslashit($dir) . $file;
352
- if (is_dir($filepath)):
353
- array_unshift($dirs, $filepath);
354
- if ($all):
355
  $files[] = $filepath;
356
  endif;
357
- elseif (is_file($filepath) && ($all || preg_match("/\.".$ext."$/i", $filepath))):
358
  $files[] = $filepath;
359
  endif;
360
  endwhile;
361
- closedir($handle);
362
  endif;
363
  endwhile;
364
  return $files;
365
  }
366
-
367
- function convert_parent_rel_url_to_abs_url($url) {
368
- $source = $this->get_prop('parnt');
369
  $spliton = '%[/\\\\]%';
370
- $dirname = dirname($url);
371
- $dirs = preg_split($spliton, $dirname);
372
  $dds = '';
373
  $themeuri = get_theme_root_uri();
374
- while (count($dirs)):
375
- $thisdir = array_pop($dirs);
376
- $upone = implode('/', $dirs);
377
  $dds .= '\.\.\/';
378
  $regex = '%url\([\'" ]*' . $dds . '(.+?)[\'" ]*\)%';
379
- $fullurl = $themeuri . '/' . $source . '/' . $upone . ('' == $upone ? '' : '/' );
380
- $this->styles = preg_replace($regex, "url(" . $fullurl . "$1)", $this->styles);
381
  endwhile;
382
  }
383
  /*
@@ -386,94 +390,94 @@ class ChildThemeConfiguratorCSS {
386
  */
387
  function parse_post_data() {
388
  $this->cache_updates = TRUE;
389
- if (isset($_POST['ctc_new_selectors'])):
390
- $this->styles = $this->parse_css_input(LF . $_POST['ctc_new_selectors']);
391
- $this->parse_css('child',
392
- (isset($_POST['ctc_sel_ovrd_query'])?trim($_POST['ctc_sel_ovrd_query']):null), FALSE);
393
- elseif (isset($_POST['ctc_child_imports'])):
394
  $this->imports['child'] = array();
395
- $this->styles = $this->parse_css_input($_POST['ctc_child_imports']);
396
- $this->parse_css('child');
397
  else:
398
- $newselector = isset($_POST['ctc_rewrite_selector']) ? $this->sanitize($this->parse_css_input($_POST['ctc_rewrite_selector'])) : NULL;
399
  // set the custom sequence value
400
- foreach (preg_grep('#^ctc_ovrd_child_seq_#', array_keys($_POST)) as $post_key):
401
- if (preg_match('#^ctc_ovrd_child_seq_(\d+)$#', $post_key, $matches)):
402
  $qsid = $matches[1];
403
- $this->dict_seq[$qsid] = intval($_POST[$post_key]);
404
  endif;
405
  endforeach;
406
  $parts = array();
407
- foreach (preg_grep('#^ctc_(ovrd|\d+)_child#', array_keys($_POST)) as $post_key):
408
- if (preg_match('#^ctc_(ovrd|\d+)_child_([\w\-]+?)_(\d+?)(_(.+))?$#', $post_key, $matches)):
409
  $valid = $matches[1];
410
  $rule = $matches[2];
411
- if (null == $rule || !isset($this->dict_rule[$rule])) continue;
412
  $ruleid = $this->dict_rule[$rule];
413
  $qsid = $matches[3];
414
- $value = $this->normalize_color($this->sanitize($this->parse_css_input($_POST[$post_key])));
415
- $important = $this->is_important($value);
416
- if (!empty($_POST['ctc_' . $valid . '_child_' . $rule . '_i_' . $qsid])) $important = 1;
417
 
418
- $selarr = $this->denorm_query_sel($qsid);
419
- if (!empty($matches[5])):
420
  $parts[$qsid][$rule][$matches[5]] = $value;
421
  $parts[$qsid][$rule]['important'] = $important;
422
  $parts[$qsid][$rule]['query'] = $selarr['query'];
423
  $parts[$qsid][$rule]['selector'] = $selarr['selector'];
424
  else:
425
- if ($newselector && $newselector != $selarr['selector']):
426
  // If this is a renamed selector, add new selector to array
427
  // and clear original child selector values.
428
  // Passing the sequence in the last argument serves two purposes:
429
  // 1. sets sequence for new renamed selector.
430
  // 2. tells the update_arrays function to flag this as a
431
  // renamed selector to pass back in result array.
432
- $this->update_arrays('child', $selarr['query'], $newselector,
433
- $rule, trim($value), $important, $this->dict_seq[$qsid]);
434
- $this->update_arrays('child', $selarr['query'], $selarr['selector'], $rule, '');
435
  else:
436
  // Otherwise, just update with the new values:
437
- $this->update_arrays('child', $selarr['query'], $selarr['selector'],
438
- $rule, trim($value), $important);
439
  endif;
440
  endif;
441
  endif;
442
  endforeach;
443
- foreach ($parts as $qsid => $rule_arr):
444
- foreach ($rule_arr as $rule => $rule_part):
445
- if ('background' == $rule):
446
  $value = $rule_part['background_url'];
447
- elseif ('background-image' == $rule):
448
- if (empty($rule_part['background_url'])):
449
- if (empty($rule_part['background_color2'])):
450
  $value = '';
451
  else:
452
- $value = implode(':', array(
453
  $rule_part['background_origin'],
454
  $rule_part['background_color1'], '0%',
455
  $rule_part['background_color2'], '100%'
456
- ));
457
  endif;
458
  else:
459
  $value = $rule_part['background_url'];
460
  endif;
461
- elseif (preg_match('#^border(\-(top|right|bottom|left))?$#', $rule)):
462
- $value = implode(' ', array(
463
  $rule_part['border_width'],
464
  $rule_part['border_style'],
465
  $rule_part['border_color']
466
- ));
467
  else:
468
  $value = '';
469
  endif;
470
- if ($newselector && $newselector != $rule_part['selector']):
471
- $this->update_arrays('child', $rule_part['query'], $newselector,
472
- $rule, trim($value), $rule_part['important'], $this->dict_seq[$qsid]);
473
- $this->update_arrays('child', $rule_part['query'], $rule_part['selector'], $rule, '');
474
  else:
475
- $this->update_arrays('child', $rule_part['query'], $rule_part['selector'],
476
- $rule, trim($value), $rule_part['important']);
477
  endif;
478
  endforeach;
479
  endforeach;
@@ -485,130 +489,132 @@ class ChildThemeConfiguratorCSS {
485
  * Normalize raw user CSS input so that the parser can read it.
486
  * TODO: this is a stub for future use
487
  */
488
- function parse_css_input($styles) {
489
- return $this->repl_octal(stripslashes($this->esc_octal($styles)));
490
  }
491
-
492
- function sanitize($styles) {
493
- return sanitize_text_field(preg_replace( '/[^[:print:]]/', '', $styles));
494
  }
495
-
496
- function esc_octal($styles){
497
- return preg_replace("#(['\"])\\\\([0-9a-f]{4})(['\"])#i", "$1##bs##$2$3", $styles);
498
  }
499
- function repl_octal($styles) {
500
- return str_replace("##bs##", "\\", $styles);
 
501
  }
502
  /*
503
  * parse_css_file
504
  * reads stylesheet to get WordPress meta data and passes rest to parse_css
505
  */
506
- function parse_css_file($template, $file = 'style.css') {
507
 
508
  $this->ctc()->cache_updates = FALSE;
509
  $this->styles = ''; // reset styles
510
- $this->read_stylesheet($template, $file);
511
  // get theme name
512
  $regex = '#Theme Name:\s*(.+?)\n#i';
513
- preg_match($regex, $this->styles, $matches);
514
- $child_name = $this->get_prop('child_name');
515
- if (!empty($matches[1]) && 'child' == $template && empty($child_name)) $this->set_prop('child_name', $matches[1]);
516
- $this->parse_css($template);
517
  }
518
 
519
  /*
520
  * parse_css
521
- * accepts raw CSS as text and parses into separate properties
522
  */
523
- function parse_css($template, $basequery = null, $parse_imports = TRUE) {
524
- if (FALSE === strpos($basequery, '@')):
525
  $basequery = 'base';
526
  endif;
527
  $ruleset = array();
528
  // ignore commented code
529
- $this->styles = preg_replace('#\/\*.*?\*\/#s', '', $this->styles);
530
  // space brace to ensure correct matching
531
- $this->styles = preg_replace('#(\{\s*)#', "$1\n", $this->styles);
532
  // get all imports
533
- if ($parse_imports):
534
 
535
  $regex = '#(\@import.+?);#';
536
- preg_match_all($regex, $this->styles, $matches);
537
- foreach (preg_grep('#' . $this->get_prop('parnt') . '\/style\.css#', $matches[1], PREG_GREP_INVERT) as $import)
538
  $this->imports[$template][$import] = 1;
539
- if ($this->ctc()->cache_updates):
540
  $this->ctc()->updates[] = array(
541
  'obj' => 'imports',
542
- 'data' => array_keys($this->imports[$template]),
543
  );
544
  endif;
545
  endif;
546
  // break into @ segments
547
- foreach (array(
548
  '#(\@media[^\{]+?)\{(\s*?)\}#', // get an placehoder (empty) media queries
549
  '#(\@media[^\{]+?)\{(.*?\})?\s*?\}#s', // get all other media queries
550
- ) as $regex): // (((?!\@media).) backreference too memory intensive - rolled back in v 1.4.8.1
551
- preg_match_all($regex, $this->styles, $matches);
552
- foreach ($matches[1] as $segment):
553
- $ruleset[trim($segment)] = array_shift($matches[2]) . (isset($ruleset[trim($segment)])?$ruleset[trim($segment)]:'');
554
  endforeach;
555
  // stripping rulesets leaves base styles
556
- $this->styles = preg_replace($regex, '', $this->styles);
557
  endforeach;
558
  $ruleset[$basequery] = $this->styles;
559
- foreach ($ruleset as $query => $segment):
560
  // make sure there is semicolon before closing brace
561
- $segment = preg_replace('#(\})#', ";$1", $segment);
 
562
  $regex = '#\s([\[\.\#\:\w][\w\-\s\(\)\[\]\'\^\*\.\#\+:,"=>]+?)\s*\{(.*?)\}#s'; //
563
- preg_match_all($regex, $segment, $matches);
564
- foreach($matches[1] as $sel):
565
- $stuff = array_shift($matches[2]);
566
- $this->update_arrays($template, $query, $sel);
567
  // handle base64 data
568
  $stuff = preg_replace( '#data:([^;]+?);([^\)]+?)\)#s', "data:$1%%semi%%$2)", $stuff );
569
- foreach (explode(';', $stuff) as $ruleval):
570
- if ($this->qskey > CHLD_THM_CFG_MAX_SELECTORS) break;
571
- if (FALSE === strpos($ruleval, ':')) continue;
572
- list($rule, $value) = explode(':', $ruleval, 2);
573
- $rule = trim($rule);
574
- $rule = preg_replace_callback("/[^\w\-]/", array($this, 'to_ascii'), $rule);
575
  // handle base64 data
576
- $value = trim(str_replace('%%semi%%', ';', $value));
577
 
578
  $rules = $values = array();
579
  // save important flag
580
- $important = $this->is_important($value);
581
  // normalize color
582
- $value = $this->normalize_color($value);
583
  // normalize font
584
- if ('font' == $rule):
585
- $this->normalize_font($value, $rules, $values);
586
  // normalize background
587
- elseif('background' == $rule):
588
- $this->normalize_background($value, $rules, $values);
589
  // normalize margin/padding
590
- elseif ('margin' == $rule || 'padding' == $rule):
591
- $this->normalize_margin_padding($rule, $value, $rules, $values);
592
  else:
593
  $rules[] = $rule;
594
  $values[] = $value;
595
  endif;
596
- foreach ($rules as $rule):
597
- $value = trim(array_shift($values));
598
  // normalize zero values
599
- $value = preg_replace('#\b0(px|r?em)#', '0', $value);
600
  // normalize gradients
601
- if (FALSE !== strpos($value, 'gradient')):
602
- if (FALSE !== strpos($rule, 'filter')):
603
  $rule = 'background-image';
604
  continue; // treat as background-image, we'll add filter rule later
605
  endif;
606
- if (FALSE !== strpos($value, 'webkit-gradient')) continue; // bail on legacy webkit, we'll add it later
607
- $value = $this->encode_gradient($value);
608
  endif;
609
  // normalize common vendor prefixes
610
- $rule = preg_replace('#(\-(o|ms|moz|webkit)\-)?(' . implode('|', $this->vendorrule) . ')#', "$3", $rule);
611
- $this->update_arrays($template, $query, $sel, $rule, $value, $important);
612
  endforeach;
613
  endforeach;
614
  endforeach;
@@ -622,73 +628,73 @@ class ChildThemeConfiguratorCSS {
622
  * @media query blocks are sorted using internal heuristics (see sort_queries)
623
  * New selectors are appended to the end of each media query block.
624
  */
625
- function write_css($backup = FALSE) {
626
  // write new stylesheet
627
- $output = apply_filters('chld_thm_cfg_css_header', $this->get_css_header(), $this);
628
- $imports = $this->get_prop('imports');
629
- if (!empty($imports)):
630
- foreach ($imports as $import):
631
  $output .= $import . ';' . LF;
632
  endforeach;
633
  endif;
634
  $output .= LF;
635
  // turn the dictionaries into indexes (value => id into id => value):
636
- $rulearr = array_flip($this->dict_rule);
637
- $valarr = array_flip($this->dict_val);
638
- $selarr = array_flip($this->dict_sel);
639
- foreach ($this->sort_queries() as $query => $sort_order):
640
  $has_selector = 0;
641
  $sel_output = '';
642
  $selectors = $this->sel_ndx[$this->dict_query[$query]];
643
- uasort($selectors, array($this, 'cmp_seq'));
644
- if ('base' != $query) $sel_output .= $query . ' {' . LF;
645
- foreach ($selectors as $selid => $qsid):
646
  $has_value = 0;
647
  $sel = $selarr[$selid];
648
- if (!empty($this->val_ndx[$qsid])):
649
  $shorthand = array();
650
- foreach ($this->val_ndx[$qsid] as $ruleid => $valid):
651
- if (isset($valid['child'])
652
- && isset($valarr[$valid['child']])
653
  && '' !== $valarr[$valid['child']]
654
- && (!isset($valid['parnt']) || $valid['parnt'] != $valid['child'])):
655
- if (! $has_value):
656
- $sel_output .= isset($this->dict_seq[$qsid])?'/*' . $this->dict_seq[$qsid] . '*/' . LF:'';
657
  // show load order
658
  $sel_output .= $sel . ' {' . LF;
659
  $has_value = 1;
660
  $has_selector = 1;
661
  endif;
662
- $important_parnt = empty($valid['i_parnt']) ? 0 : 1;
663
- $important = isset($valid['i_child']) ? $valid['i_child'] : $important_parnt;
664
- $sel_output .= $this->add_vendor_rules($rulearr[$ruleid], $valarr[$valid['child']], $shorthand, $important);
665
  endif;
666
  endforeach;
667
- $sel_output .= $this->encode_shorthand($shorthand); // . ($important ? ' !important' : '');
668
- if ($has_value):
669
  $sel_output .= '}' . LF;
670
  endif;
671
  endif;
672
  endforeach;
673
- if ('base' != $query) $sel_output .= '}' . LF;
674
- if ($has_selector) $output .= $sel_output;
675
  endforeach;
676
- $stylesheet = apply_filters('chld_thm_cfg_target', $this->get_child_target(), $this);
677
- if ($stylesheet_verified = $this->is_file_ok($stylesheet, 'write')):
678
  global $wp_filesystem; // this was initialized earlier;
679
  // backup current stylesheet
680
- if ($backup && is_file($stylesheet_verified)):
681
- $timestamp = date('YmdHis', current_time('timestamp'));
682
- $bakfile = preg_replace("/\.css$/", '', $stylesheet_verified) . '-' . $timestamp . '.css';
683
  // don't write new stylesheet if backup fails
684
- if (!$wp_filesystem->copy($this->ctc()->fspath($stylesheet_verified), $this->ctc()->fspath($bakfile))) return FALSE;
685
  endif;
686
  // write new stylesheet:
687
  // try direct write first, then wp_filesystem write
688
  // stylesheet must already exist and be writable by web server
689
- if (FALSE !== @file_put_contents($stylesheet_verified, $output)): //is_writable($stylesheet_verified) &&
690
  return TRUE;
691
- elseif (FALSE !== $wp_filesystem->put_contents($this->ctc()->fspath($stylesheet_verified), $output)):
692
  return TRUE;
693
  endif;
694
  endif;
@@ -701,37 +707,37 @@ class ChildThemeConfiguratorCSS {
701
  * These are based on commonly used practices and not all vendor prefixed are supported
702
  * TODO: verify this logic against vendor and W3C documentation
703
  */
704
- function add_vendor_rules($rule, $value, &$shorthand, $important = 0) {
705
  $rules = '';
706
- if ('filter' == $rule && (FALSE !== strpos($value, 'progid:DXImageTransform.Microsoft.Gradient'))) return;
707
  $importantstr = $important ? ' !important' : '';
708
- if (preg_match("/^(margin|padding)\-(top|right|bottom|left)$/", $rule, $matches)):
709
  $shorthand[$matches[1]][$matches[2]] = $value . $importantstr;
710
  return '';
711
- elseif (preg_match('/^(' . implode('|', $this->vendorrule) . ')$/', $rule)):
712
- foreach(array('moz', 'webkit', 'o') as $prefix):
713
  $rules .= ' -' . $prefix . '-' . $rule . ': ' . $value . $importantstr . ';' . LF;
714
  endforeach;
715
  $rules .= ' ' . $rule . ': ' . $value . $importantstr . ';' . LF;
716
- elseif ('background-image' == $rule):
717
  // gradient?
718
 
719
- if ($gradient = $this->decode_gradient($value)):
720
  // standard gradient
721
- foreach(array('moz', 'webkit', 'o', 'ms') as $prefix):
722
  $rules .= ' background-image: -' . $prefix . '-' . 'linear-gradient(' . $gradient['origin'] . ', '
723
  . $gradient['color1'] . ', ' . $gradient['color2'] . ')' . $importantstr . ';' . LF;
724
  endforeach;
725
  // W3C standard gradient
726
  // rotate origin 90 degrees
727
- if (preg_match('/(\d+)deg/', $gradient['origin'], $matches)):
728
- $org = (90 - $matches[1]) . 'deg';
729
  else:
730
- foreach (preg_split("/\s+/", $gradient['origin']) as $dir):
731
- $dir = strtolower($dir);
732
- $dirs[] = ('top' == $dir ? 'bottom' : ('bottom' == $dir ? 'top' : ('left' == $dir ? 'right' : ('right' == $dir ? 'left' : $dir))));
733
  endforeach;
734
- $org = 'to ' . implode(' ', $dirs);
735
  endif;
736
  $rules .= ' background-image: linear-gradient(' . $org . ', '
737
  . $gradient['color1'] . ', ' . $gradient['color2'] . ')' . $importantstr . ';' . LF;
@@ -740,16 +746,16 @@ class ChildThemeConfiguratorCSS {
740
  // '-webkit-gradient(linear,' .$origin . ', ' . $color1 . ', '. $color2 . ')';
741
 
742
  // MS filter gradient
743
- $type = (in_array($gradient['origin'], array('left', 'right', '0deg', '180deg')) ? 1 : 0);
744
- $color1 = preg_replace("/^#/", '#00', $gradient['color1']);
745
  $rules .= ' filter: progid:DXImageTransform.Microsoft.Gradient(GradientType=' . $type . ', StartColorStr="'
746
- . strtoupper($color1) . '", EndColorStr="' . strtoupper($gradient['color2']) . '")' . $importantstr . ';' . LF;
747
  else:
748
  // url or other value
749
  $rules .= ' ' . $rule . ': ' . $value . $importantstr . ';' . LF;
750
  endif;
751
  else:
752
- $rule = preg_replace_callback("/\d+/", array($this, 'from_ascii'), $rule);
753
  $rules .= ' ' . $rule . ': ' . $value . $importantstr . ';' . LF;
754
  endif;
755
  return $rules;
@@ -760,43 +766,43 @@ class ChildThemeConfiguratorCSS {
760
  * parses background shorthand value and returns
761
  * normalized rule/value pairs for each property
762
  */
763
- function normalize_background($value, &$rules, &$values){
764
- if (FALSE !== strpos($value, 'gradient')):
765
  // only supporting linear syntax
766
- if (preg_match('#(linear\-|Microsoft\.)#', $value)):
767
  $values[] = $value;
768
  $rules[] = 'background-image';
769
  endif;
770
  else:
771
  $regex = '#(url *\([^\)]+\))#';
772
- if (preg_match($regex, $value, $matches)) $url = $matches[1];
773
- $parts = preg_split($regex, $value);
774
 
775
- if (count($parts) == 1):
776
  // this is a named color or single hex color or none
777
- $part = str_replace(' ', '', $parts[0]);
778
  $rules[] = 'none' == $part ? 'background' : 'background-color';
779
  $values[] = $part;
780
  else:
781
  $rules[] = 'background-image';
782
  $values[] = $url;
783
- if (!empty($parts[0]) && '' !== $parts[0]):
784
  $rules[] = 'background-color';
785
- $values[] = trim($parts[0]);
786
  endif;
787
  $position = array();
788
- foreach(preg_split('/ +/', trim($parts[1])) as $part):
789
- if ('' === $part) continue; // empty($part) ||
790
- if (FALSE === strpos($part, 'repeat')):
791
  $position[] = $part;
792
  else:
793
  $rules[] = 'background-repeat';
794
  $values[] = $part;
795
  endif;
796
  endforeach;
797
- if (count($position)):
798
  $rules[] = 'background-position';
799
- $values[] = implode(' ', $position);
800
  endif;
801
  endif;
802
  endif;
@@ -807,26 +813,26 @@ class ChildThemeConfiguratorCSS {
807
  * parses font shorthand value and returns
808
  * normalized rule/value pairs for each property
809
  */
810
- function normalize_font($value, &$rules, &$values) {
811
  $regex = '#^((\d+|bold|normal) )?((italic|normal) )?(([\d\.]+(px|r?em|%))[\/ ])?(([\d\.]+(px|r?em|%)?) )?(.+)$#is';
812
- preg_match($regex, $value, $parts);
813
- if (!empty($parts[2])):
814
  $rules[] = 'font-weight';
815
  $values[] = $parts[2];
816
  endif;
817
- if (!empty($parts[4])):
818
  $rules[] = 'font-style';
819
  $values[] = $parts[4];
820
  endif;
821
- if (!empty($parts[6])):
822
  $rules[] = 'font-size';
823
  $values[] = $parts[6];
824
  endif;
825
- if (!empty($parts[9])):
826
  $rules[] = 'line-height';
827
  $values[] = $parts[9];
828
  endif;
829
- if (!empty($parts[11])):
830
  $rules[] = 'font-family';
831
  $values[] = $parts[11];
832
  endif;
@@ -838,11 +844,11 @@ class ChildThemeConfiguratorCSS {
838
  * normalized rule/value pairs for each property
839
  * TODO: reassemble into shorthand when writing CSS file
840
  */
841
- function normalize_margin_padding($rule, $value, &$rules, &$values) {
842
- $parts = preg_split("/ +/", trim($value));
843
- if (!isset($parts[1])) $parts[1] = $parts[0];
844
- if (!isset($parts[2])) $parts[2] = $parts[0];
845
- if (!isset($parts[3])) $parts[3] = $parts[1];
846
  $rules[0] = $rule . '-top';
847
  $values[0] = $parts[0];
848
  $rules[1] = $rule . '-right';
@@ -853,49 +859,53 @@ class ChildThemeConfiguratorCSS {
853
  $values[3] = $parts[3];
854
  }
855
 
856
- function encode_shorthand($shorthand) {
 
 
 
 
857
  $rules = '';
858
  $importantstr = ' !important';
859
- foreach (array_keys($shorthand) as $key):
860
  $important = array();
861
  $rule = array();
862
  $importantct = 0;
863
  // which sides do we have and are they important?
864
- foreach($shorthand[$key] as $side => $val):
865
  $ict = 0;
866
- $rule[$side] = trim(preg_replace('/'.$importantstr.'/', '', $val, 1, $ict));
867
  $important[$side] = $ict;
868
  $importantct += $ict;
869
  endforeach;
870
  // shorthand must have 4 explicit values and all must have same priority
871
- if (4 == count($rule) && (0 == $importantct || 4 == $importantct )):
872
  // let's try to condense the values into as few as possible, starting with the top value
873
  $parts = array();
874
  $parts[0] = $rule['top'];
875
  // if left is not the same as right, we must use all 4 values
876
- if ($rule['left'] !== $rule['right']):
877
  $parts[3] = $rule['left'];
878
  $parts[2] = $rule['bottom'];
879
  $parts[1] = $rule['right'];
880
  endif;
881
  // if top is not the same as bottom, we must use at least 3 values
882
- if ($rule['bottom'] !== $rule['top']):
883
  $parts[2] = $rule['bottom'];
884
  $parts[1] = $rule['right'];
885
  endif;
886
  // if top is not the same as right, we must use at least 2 values
887
- if ($rule['right'] !== $rule['top']):
888
  $parts[1] = $rule['right'];
889
  endif;
890
  // the order of the sides is critical: top right bottom left
891
- ksort($parts);
892
- $shorthandstr = implode(' ', $parts);
893
  // if important counter is > 0, it must be == 4, add flag
894
- $rules .= ' ' . $key . ': ' . $shorthandstr . ($importantct ? ' ' . $importantstr : '') . ';' . LF;
895
  else:
896
  // otherwise return separate rule for each side
897
- foreach ($rule as $side => $value):
898
- $rules .= ' ' . $key . '-' . $side . ': ' . $value . ($important[$side] ? $importantstr : '') . ';' . LF;
899
  endforeach;
900
  endif;
901
  endforeach;
@@ -908,75 +918,76 @@ class ChildThemeConfiguratorCSS {
908
  * Currently only supports two-color linear gradients with no inner stops.
909
  * TODO: legacy webkit? more gradients?
910
  */
911
- function encode_gradient($value) {
 
912
  $regex = '#gradient[^\)]*?\((((top|bottom|left|right)?( (top|bottom|left|right))?|\d+deg),)?([^\)]*[\'"]?(\#\w{3,8}|rgba?\([\d, ]+?\)|hsla?\([\d%, ]+?\))( \d+%)?)([^\)]*[\'"]?(\#\w{3,8}|rgba?\([\d, ]+?\)|hsla?\([\d%, ]+?\))( \d+%)?)([^\)]*gradienttype=[\'"]?(\d)[\'"]?)?[^\)]*\)#i';
913
  $param = $parts = array();
914
- preg_match($regex, $value, $parts);
915
- if (empty($parts[13])):
916
- if (empty($parts[2])):
917
  $param[0] = 'top';
918
  else:
919
- $param[0] = trim($parts[2]);
920
  endif;
921
- if (empty($parts[8])):
922
  $param[2] = '0%';
923
  else:
924
- $param[2] = trim($parts[8]);
925
  endif;
926
- if (empty($parts[11])):
927
  $param[4] = '100%';
928
  else:
929
- $param[4] = trim($parts[11]);
930
  endif;
931
- elseif('0' == $parts[13]):
932
  $param[0] = 'top';
933
  $param[2] = '0%';
934
  $param[4] = '100%';
935
- elseif ('1' == $parts[13]):
936
  $param[0] = 'left';
937
  $param[2] = '0%';
938
  $param[4] = '100%';
939
  endif;
940
- if (isset($parts[7]) && isset($parts[10])):
941
  $param[1] = $parts[7];
942
  $param[3] = $parts[10];
943
- ksort($param);
944
- return implode(':', $param);
945
  else: return $value;
946
  endif;
947
  }
948
 
949
  /*
950
  * decode_border
951
- * De-normalize CTC border syntax into separate properties.
952
  */
953
- function decode_border($value) {
954
- if (preg_match('#^(0|none)#i', $value)):
955
  $parts[0] = $value;
956
  $parts[1] = $parts[2] = '';
957
  else:
958
- $parts = preg_split('#\s+#', $value, 3);
959
  endif;
960
  return array(
961
- 'width' => empty($parts[0])?'':$parts[0],
962
- 'style' => empty($parts[1])?'':$parts[1],
963
- 'color' => empty($parts[2])?'':$parts[2],
964
  );
965
  }
966
 
967
  /*
968
  * decode_gradient
969
- * Decode CTC gradient syntax into separate properties.
970
  */
971
- function decode_gradient($value) {
972
- $parts = explode(':', $value, 5);
973
- if (!preg_match('#(url|none)#i', $value) && 5 == count($parts)):
974
  return array(
975
- 'origin' => empty($parts[0]) ? '' : $parts[0],
976
- 'color1' => empty($parts[1]) ? '' : $parts[1],
977
- 'stop1' => empty($parts[2]) ? '' : $parts[2],
978
- 'color2' => empty($parts[3]) ? '' : $parts[3],
979
- 'stop2' => empty($parts[4]) ? '' : $parts[4],
980
  );
981
  endif;
982
  return FALSE;
@@ -986,13 +997,13 @@ class ChildThemeConfiguratorCSS {
986
  * denorm_rule_val
987
  * Return array of unique values corresponding to specific rule
988
  */
989
- function denorm_rule_val($ruleid) {
990
  $rule_sel_arr = array();
991
- $val_arr = array_flip($this->dict_val);
992
- foreach ($this->val_ndx as $selid => $rules):
993
- if (!isset($rules[$ruleid])) continue;
994
- foreach ($rules[$ruleid] as $theme => $val):
995
- if (!isset($val_arr[$val]) || '' === $val_arr[$val]) continue;
996
  $rule_sel_arr[$val] = $val_arr[$val];
997
  endforeach;
998
  endforeach;
@@ -1004,15 +1015,15 @@ class ChildThemeConfiguratorCSS {
1004
  * Return array of queries, selectors, rules, and values corresponding to
1005
  * specific rule/value combo grouped by query, selector
1006
  */
1007
- function denorm_val_query($valid, $rule) {
1008
  $value_query_arr = array();
1009
- foreach ($this->val_ndx as $qsid => $rules):
1010
- foreach ($rules as $ruleid => $values):
1011
- if ($ruleid != $this->dict_rule[$rule]) continue;
1012
- foreach ($values as $name => $val):
1013
- if ('i' == $name || $val != $valid) continue;
1014
- $selarr = $this->denorm_query_sel($qsid);
1015
- $valarr = $this->denorm_sel_val($qsid);
1016
  $value_query_arr[$rule][$selarr['query']][$qsid] = $valarr;
1017
  endforeach;
1018
  endforeach;
@@ -1024,10 +1035,10 @@ class ChildThemeConfiguratorCSS {
1024
  * denorm_query_sel
1025
  * Return id, query and selector values of a specific qsid (query-selector ID)
1026
  */
1027
- function denorm_query_sel($qsid) {
1028
- $queryarr = array_flip($this->dict_query);
1029
- $selarr = array_flip($this->dict_sel);
1030
- $this->dict_seq[$qsid] = isset($this->dict_seq[$qsid]) ? $this->dict_seq[$qsid] : $qsid;
1031
  return array(
1032
  'id' => $qsid,
1033
  'query' => $queryarr[$this->dict_qs[$qsid]['q']],
@@ -1041,16 +1052,16 @@ class ChildThemeConfiguratorCSS {
1041
  * Return array of rules, and values matching specific qsid (query-selector ID)
1042
  * grouped by query, selector
1043
  */
1044
- function denorm_sel_val($qsid) {
1045
- $selarr = $this->denorm_query_sel($qsid);
1046
- $valarr = array_flip($this->dict_val);
1047
- $rulearr = array_flip($this->dict_rule);
1048
- if (isset($this->val_ndx[$qsid]) && is_array($this->val_ndx[$qsid])):
1049
- foreach ($this->val_ndx[$qsid] as $ruleid => $values):
1050
- foreach ($values as $name => $val):
1051
- if ('i_parnt' == $name || 'i_child' == $name):
1052
- $selarr['value'][$rulearr[$ruleid]][$name] = (empty($val) ? 0 : 1);
1053
- elseif (!isset($valarr[$val]) || '' === $valarr[$val]):
1054
  continue;
1055
  else:
1056
  $selarr['value'][$rulearr[$ruleid]][$name] = $valarr[$val];
@@ -1066,16 +1077,16 @@ class ChildThemeConfiguratorCSS {
1066
  * denorm_sel_ndx
1067
  * Return denormalized array containing query and selector heirarchy
1068
  */
1069
- function denorm_sel_ndx($query = null) {
1070
  $sel_ndx_norm = array();
1071
- $queryarr = array_flip($this->dict_query);
1072
- $selarr = array_flip($this->dict_sel);
1073
- foreach($this->sel_ndx as $queryid => $sel):
1074
- foreach($sel as $selid => $qsid):
1075
  $sel_ndx_norm[$queryarr[$queryid]][$selarr[$selid]] = $qsid;
1076
  endforeach;
1077
  endforeach;
1078
- return empty($query) ? $sel_ndx_norm : $sel_ndx_norm[$query];
1079
  }
1080
 
1081
  /*
@@ -1083,9 +1094,9 @@ class ChildThemeConfiguratorCSS {
1083
  * Strip important flag from value ref and return boolean
1084
  * Value is updated because it is a ref
1085
  */
1086
- function is_important(&$value) {
1087
  $important = 0;
1088
- $value = trim(str_ireplace('!important', '', $value, $important));
1089
  return $important;
1090
  }
1091
 
@@ -1099,29 +1110,29 @@ class ChildThemeConfiguratorCSS {
1099
  */
1100
  function sort_queries() {
1101
  $queries = array();
1102
- $queryarr = array_flip($this->dict_query);
1103
- foreach (array_keys($this->sel_ndx) as $queryid):
1104
  $query = $queryarr[$queryid];
1105
- if ('base' == $query):
1106
  $queries['base'] = -999999;
1107
  continue;
1108
  endif;
1109
- if (preg_match("/((min|max)(\-device)?\-width)\s*:\s*(\d+)/", $query, $matches)):
1110
  $queries[$query] = 'min-width' == $matches[1] ? $matches[4] : -$matches[4];
1111
  else:
1112
  $queries[$query] = $queryid - 10000;
1113
  endif;
1114
  endforeach;
1115
- asort($queries);
1116
  return $queries;
1117
  }
1118
 
1119
  // sort selectors based on dict_seq if exists, otherwise qsid
1120
- function cmp_seq($a, $b) {
1121
- $cmpa = isset($this->dict_seq[$a])?$this->dict_seq[$a]:$a;
1122
- $cmpb = isset($this->dict_seq[$b])?$this->dict_seq[$b]:$b;
1123
- if ($cmpa == $cmpb) return 0;
1124
- return ($cmpa < $cmpb) ? -1 : 1;
1125
  }
1126
 
1127
  /*
@@ -1130,52 +1141,54 @@ class ChildThemeConfiguratorCSS {
1130
  * flattens to array
1131
  * and stringifies NULLs
1132
  */
1133
- function obj_to_utf8($data) {
1134
 
1135
- if (is_object($data)) {
1136
- $data = get_object_vars($data);
1137
  }
1138
- if (is_array($data)) {
1139
- return array_map(array(&$this, __FUNCTION__), $data);
1140
  }
1141
  else {
1142
- return is_null( $data ) ? '' : utf8_encode($data);
1143
  }
1144
  }
1145
-
1146
- function to_ascii($matches) {
1147
- return ord($matches[0]);
1148
  }
1149
-
1150
- function from_ascii($matches) {
1151
- return chr($matches[0]);
1152
  }
1153
 
1154
  /* is_file_ok
1155
  * verify file exists and is in valid location
1156
  */
1157
- function is_file_ok($stylesheet, $permission = 'read') {
1158
  // remove any ../ manipulations
1159
- $stylesheet = preg_replace("%\.\./%", '/', $stylesheet);
1160
- if ('read' == $permission && !is_file($stylesheet)) return FALSE;
1161
- if ('search' == $permission && !is_dir($stylesheet)) return FALSE;
1162
  // sanity check for php files
1163
- //if (!preg_match('%' . preg_quote($ext) . '$%', $stylesheet)) return FALSE;
1164
  // check if in themes dir;
1165
- if (preg_match('%^' . preg_quote(get_theme_root()) . '%', $stylesheet)) return $stylesheet;
1166
  // check if in plugins dir
1167
- if (preg_match('%^' . preg_quote(WP_PLUGIN_DIR) . '%', $stylesheet)) return $stylesheet;
1168
  return FALSE;
1169
  }
1170
-
1171
- function normalize_color($value) {
1172
- $value = preg_replace_callback( "/#([0-9A-F]{3}([0-9A-F]{3})?)/i", array($this, 'tolower'), $value );
 
 
1173
  $value = preg_replace( "/#([0-9A-F])\\1([0-9A-F])\\2([0-9A-F])\\3/i", "#$1$2$3", $value );
1174
  return $value;
1175
  }
1176
-
1177
- function tolower($matches) {
1178
- return '#' . strtolower($matches[1]);
1179
  }
1180
  }
1181
  ?>
1
  <?php
2
  // Exit if accessed directly
3
+ if ( !defined( 'ABSPATH' ) ) exit;
4
 
5
  /*
6
  Class: ChildThemeConfiguratorCSS
7
  Plugin URI: http://www.lilaeamedia.com/plugins/child-theme-configurator/
8
  Description: Handles all CSS output, parsing, normalization
9
+ Version: 1.6.3
10
  Author: Lilaea Media
11
  Author URI: http://www.lilaeamedia.com/
12
  Text Domain: chld_thm_cfg
13
  Domain Path: /lang
14
  License: GPLv2
15
+ Copyright (C) 2014-2015 Lilaea Media
16
  */
17
  class ChildThemeConfiguratorCSS {
18
  // data dictionaries
104
  $this->sel_ndx = array();
105
  $this->val_ndx = array();
106
  $this->parntss = array();
107
+ $this->imports = array( 'child' => array(), 'parnt' => array() );
108
  }
109
+ // helper function to globalize ctc object
110
  function ctc() {
111
  global $chld_thm_cfg;
112
  return $chld_thm_cfg;
113
  }
114
+ // loads current ctc config data into local memory
115
  function read_config() {
116
 
117
+ if ( $configarray = get_option( CHLD_THM_CFG_OPTIONS . '_configvars' ) ):
118
+ foreach ( $this->configvars as $configkey )
119
+ if ( isset( $configarray[$configkey] ) )
120
  $this->{$configkey} = $configarray[$configkey];
121
+ foreach ( $this->dicts as $configkey ):
122
+ if ( $configarray = get_option( CHLD_THM_CFG_OPTIONS . '_' . $configkey ) )
123
  $this->{$configkey} = $configarray;
124
  endforeach;
125
  else:
126
  return FALSE;
127
  endif;
128
  }
129
+ // writes ctc config data to options api
130
  function save_config() {
131
 
132
  $configarray = array();
133
+ foreach ( $this->configvars as $configkey )
134
  $configarray[$configkey] = $this->{$configkey};
135
+ update_option( CHLD_THM_CFG_OPTIONS . '_configvars', $configarray );
136
+ foreach ( $this->dicts as $configkey )
137
+ update_option( CHLD_THM_CFG_OPTIONS . '_' . $configkey, $this->{$configkey} );
138
  // remove pre 1.5.4 options
139
+ delete_option( CHLD_THM_CFG_OPTIONS );
140
  }
141
  /*
142
  * get_prop
143
+ * Getter interface (data sliced different ways depending on objname )
144
  */
145
+ function get_prop( $objname, $params = NULL ) {
146
+ switch ( $objname ):
147
  case 'imports':
148
+ return $this->obj_to_utf8( is_array( $this->imports['child'] ) ? ( current( $this->imports['child'] ) == 1 ? array_keys( $this->imports['child'] ) : array_keys( array_flip( $this->imports['child'] ) ) ) : array() );
149
  case 'sel_ndx':
150
+ return $this->obj_to_utf8( $this->denorm_sel_ndx( empty( $params['key'] ) ? NULL : $params['key'] ) );
151
  case 'rule_val':
152
+ return empty( $params['key'] ) ? array() : $this->denorm_rule_val( $params['key'] );
153
  case 'val_qry':
154
+ if ( isset( $params['rule'] ) && isset( $this->dict_rule[$params['rule']] ) ):
155
+ return empty( $params['key'] ) ?
156
+ array() : $this->denorm_val_query( $params['key'], $params['rule'] );
157
  endif;
158
  case 'sel_val':
159
+ return empty( $params['key'] ) ?
160
+ array() : $this->denorm_sel_val( $params['key'] );
161
  case 'rule':
162
+ return $this->obj_to_utf8( array_flip( $this->dict_rule ) );
163
  case 'child':
164
  return $this->child;
165
  case 'parnt':
166
  return $this->parnt;
167
  case 'parntss':
168
+ return isset( $this->parntss ) ? $this->parntss : array();
169
  case 'configtype':
170
  return $this->configtype;
171
  case 'child_name':
176
  return $this->child_version;
177
  case 'preview':
178
  $this->styles = '';
179
+ if ( empty( $params['key'] ) || 'child' == $params['key'] ):
180
+ $this->read_stylesheet( 'child' );
181
  else:
182
+ if ( isset( $this->parntss ) ):
183
+ foreach ( $this->parntss as $template ):
184
  $this->styles .= '/*** BEGIN ' . $template . ' ***/' . LF;
185
+ $this->read_stylesheet( 'parnt', $template );
186
  $this->styles .= '/*** END ' . $template . ' ***/' . LF;
187
  endforeach;
188
  endif;
189
  $this->styles .= '/*** BEGIN style.css ***/' . LF;
190
+ $this->read_stylesheet( 'parnt' );
191
  $this->styles .= '/*** END style.css ***/' . LF;
192
  endif;
193
  $this->normalize_css();
200
  * set_prop
201
  * Setter interface (scalar values only)
202
  */
203
+ function set_prop( $prop, $value ) {
204
+ if ( is_scalar( $this->{$prop} ) )
205
  $this->{$prop} = $value;
206
  else return FALSE;
207
  }
208
+ // formats css string for accurate parsing
209
  function normalize_css() {
210
+ if ( preg_match( "/(\}[\w\#\.]|; *\})/", $this->styles ) ): // prettify compressed CSS
211
+ $this->styles = preg_replace( "/\*\/\s*/s", "*/\n", $this->styles ); // end comment
212
+ $this->styles = preg_replace( "/\{\s*/s", " {\n ", $this->styles ); // open brace
213
+ $this->styles = preg_replace( "/;\s*/s", ";\n ", $this->styles ); // semicolon
214
+ $this->styles = preg_replace( "/\s*\}\s*/s", "\n}\n", $this->styles ); // close brace
215
  endif;
216
  }
217
+ // creates header comments for stylesheet
218
  function get_css_header() {
219
+ $parnt = $this->get_prop( 'parnt' );
220
  return '/*' . LF
221
+ . 'Theme Name: ' . $this->get_prop( 'child_name' ) . LF
222
  . 'Template: ' . $parnt . LF
223
+ . 'Author: ' . $this->get_prop( 'author' ). LF
224
+ . 'Version: ' . $this->get_prop( 'version' ) . LF
225
+ . 'Updated: ' . current_time( 'mysql' ) . LF
226
  . '*/' . LF . LF
227
  . '@charset "UTF-8";' . LF . LF
228
+ . ( 'import' == $this->enqueue ? '@import url(\'../' . $parnt . '/style.css\');' . LF : '' );
229
  }
230
+ // formats file path for child theme file
231
+ function get_child_target( $file = 'style.css' ) {
232
+ return trailingslashit( get_theme_root() ) . trailingslashit( $this->get_prop( 'child' ) ) . $file;
233
  }
234
+ // formats file path for parent theme file
235
+ function get_parent_source( $file = 'style.css' ) {
236
+ return trailingslashit( get_theme_root() ) . trailingslashit( $this->get_prop( 'parnt' ) ) . $file;
237
  }
238
 
239
  /*
240
  * update_arrays
241
  * accepts CSS properties as raw strings and normilizes into
242
  * CTC object arrays, creating update cache in the process.
243
+ * Update cache is returned to UI via AJAX to refresh page.
244
  */
245
+ function update_arrays( $template, $query, $sel, $rule = NULL, $value = NULL, $important = 0, $seq = NULL ) {
246
 
247
  // normalize selector styling
248
+ $sel = implode( ', ', preg_split( '#\s*,\s*#s', trim( $sel ) ) );
249
  // add selector and query to index
250
+ if ( !isset( $this->dict_query[$query] ) ) $this->dict_query[$query] = ++$this->querykey;
251
+ if ( !isset( $this->dict_sel[$sel] ) ) $this->dict_sel[$sel] = ++$this->selkey;
252
+ if ( !isset( $this->sel_ndx[$this->dict_query[$query]][$this->dict_sel[$sel]] ) ):
253
  // increment key number
254
  $this->sel_ndx[$this->dict_query[$query]][$this->dict_sel[$sel]] = ++$this->qskey;
255
 
257
  $this->dict_qs[$this->qskey]['q'] = $this->dict_query[$query];
258
  // tell the UI to update a single cached query/selector lookup by passing 'qsid' as the key
259
  // (normally the entire array is replaced):
260
+ if ( $this->ctc()->cache_updates )
261
  $this->ctc()->updates[] = array(
262
  'obj' => 'sel_ndx',
263
  'key' => 'qsid',
269
  );
270
  endif;
271
  // update sequence for this selector if this is a later instance to keep cascade priority
272
+ if ( !isset( $this->dict_seq[$this->qskey] ) )
273
  $this->dict_seq[$this->qskey] = $this->qskey;
274
  // set data and value
275
+ if ( $rule ):
276
+ if ( !isset( $this->dict_rule[$rule] ) ):
277
  $this->dict_rule[$rule] = ++$this->rulekey;
278
  // tell the UI to update a single cached rule:
279
+ if ( $this->ctc()->cache_updates ):
280
  $this->ctc()->updates[] = array(
281
  'obj' => 'rule',
282
  'key' => $this->rulekey,
286
  endif;
287
  $qsid = $this->sel_ndx[$this->dict_query[$query]][$this->dict_sel[$sel]];
288
  $ruleid = $this->dict_rule[$rule];
289
+ if ( !isset( $this->dict_val[$value] ) ):
290
  $this->dict_val[$value] = ++$this->valkey;
291
  endif;
292
  $this->val_ndx[$qsid][$ruleid][$template] = $this->dict_val[$value];
293
  // set the important flag for this value
294
  $this->val_ndx[$qsid][$ruleid]['i_' . $template] = $important;
295
  // tell the UI to add a single cached query/selector data array:
296
+ if ( $this->ctc()->cache_updates ):
297
  $updatearr = array(
298
  'obj' => 'sel_val',
299
  'key' => $qsid,
300
+ 'data' => $this->denorm_sel_val( $qsid ),
301
  );
302
  $this->ctc()->updates[] = $updatearr;
303
  endif;
304
+ if ( isset( $seq ) ): // this is a renamed selector
305
  $this->dict_seq[$qsid] = $seq;
306
+ if ( $this->ctc()->cache_updates ):
307
  $this->ctc()->updates[] = array(
308
  'obj' => 'rewrite',
309
  'key' => $qsid,
313
  endif;
314
  endif;
315
  }
316
+ // loads raw css file into local memory
317
+ function read_stylesheet( $template = 'child', $file = 'style.css' ) {
318
 
319
  // these conditions support revert/restore option in 1.6.0+
320
+ if ( 'all' == $file ) return;
321
+ elseif ( '' == $file ) $file = 'style.css';
322
  // end revert/restore conditions
323
 
324
+ $source = $this->get_prop( $template );
325
+ $configtype = $this->get_prop( 'configtype' );
326
+ if ( empty( $source ) || !is_scalar( $source ) ) return FALSE;
327
  $themedir = get_theme_root() . '/' . $source;
328
+ $stylesheet = apply_filters( 'chld_thm_cfg_' . $template, $themedir . '/' . $file , $this );
329
 
330
  // read stylesheet
331
+ if ( $stylesheet_verified = $this->is_file_ok( $stylesheet, 'read' ) ):
332
+ $import_url = preg_replace( '%^' . preg_quote( $themedir ) . '/%', '', $stylesheet_verified );
333
+ $this->styles .= @file_get_contents( $stylesheet_verified ) . "\n";
334
  endif;
335
  }
336
+ /* recurse_directory
337
+ * searches filesystem for valid files based on parameters and returns array of filepaths.
338
+ * Because it requires logic specific to CTC, core WP recurse function is not used.
339
+ */
340
  function recurse_directory( $rootdir, $ext = 'css', $all = FALSE ) {
341
  if ( !$this->is_file_ok( $rootdir, 'search' ) ) return array(); // make sure we are only recursing theme and plugin files
342
  $files = array();
343
+ $dirs = array( $rootdir );
344
  $loops = 0;
345
  if ( 'img' == $ext ):
346
 
347
+ $ext = '(' . implode( '|', array_keys( $this->ctc()->imgmimes ) ) . ')';
348
  endif;
349
+ while( count( $dirs ) && $loops < CHLD_THM_CFG_MAX_RECURSE_LOOPS ):
350
  $loops++;
351
+ $dir = array_shift( $dirs );
352
+ if ( $handle = opendir( $dir ) ):
353
+ while ( FALSE !== ( $file = readdir( $handle ) ) ):
354
+ if ( preg_match( "/^\./", $file ) ) continue;
355
+ $filepath = trailingslashit( $dir ) . $file;
356
+ if ( is_dir( $filepath ) ):
357
+ array_unshift( $dirs, $filepath );
358
+ if ( $all ):
359
  $files[] = $filepath;
360
  endif;
361
+ elseif ( is_file( $filepath ) && ( $all || preg_match( "/\.".$ext."$/i", $filepath ) ) ):
362
  $files[] = $filepath;
363
  endif;
364
  endwhile;
365
+ closedir( $handle );
366
  endif;
367
  endwhile;
368
  return $files;
369
  }
370
+ // allows images to be used between parent and child themes
371
+ function convert_parent_rel_url_to_abs_url( $url ) {
372
+ $source = $this->get_prop( 'parnt' );
373
  $spliton = '%[/\\\\]%';
374
+ $dirname = dirname( $url );
375
+ $dirs = preg_split( $spliton, $dirname );
376
  $dds = '';
377
  $themeuri = get_theme_root_uri();
378
+ while ( count( $dirs ) ):
379
+ $thisdir = array_pop( $dirs );
380
+ $upone = implode( '/', $dirs );
381
  $dds .= '\.\.\/';
382
  $regex = '%url\([\'" ]*' . $dds . '(.+?)[\'" ]*\)%';
383
+ $fullurl = $themeuri . '/' . $source . '/' . $upone . ( '' == $upone ? '' : '/' );
384
+ $this->styles = preg_replace( $regex, "url(" . $fullurl . "$1)", $this->styles );
385
  endwhile;
386
  }
387
  /*
390
  */
391
  function parse_post_data() {
392
  $this->cache_updates = TRUE;
393
+ if ( isset( $_POST['ctc_new_selectors'] ) ):
394
+ $this->styles = $this->parse_css_input( LF . $_POST['ctc_new_selectors'] );
395
+ $this->parse_css( 'child',
396
+ ( isset( $_POST['ctc_sel_ovrd_query'] )?trim( $_POST['ctc_sel_ovrd_query'] ):NULL ), FALSE );
397
+ elseif ( isset( $_POST['ctc_child_imports'] ) ):
398
  $this->imports['child'] = array();
399
+ $this->styles = $this->parse_css_input( $_POST['ctc_child_imports'] );
400
+ $this->parse_css( 'child' );
401
  else:
402
+ $newselector = isset( $_POST['ctc_rewrite_selector'] ) ? $this->sanitize( $this->parse_css_input( $_POST['ctc_rewrite_selector'] ) ) : NULL;
403
  // set the custom sequence value
404
+ foreach ( preg_grep( '#^ctc_ovrd_child_seq_#', array_keys( $_POST ) ) as $post_key ):
405
+ if ( preg_match( '#^ctc_ovrd_child_seq_(\d+)$#', $post_key, $matches ) ):
406
  $qsid = $matches[1];
407
+ $this->dict_seq[$qsid] = intval( $_POST[$post_key] );
408
  endif;
409
  endforeach;
410
  $parts = array();
411
+ foreach ( preg_grep( '#^ctc_(ovrd|\d+)_child#', array_keys( $_POST ) ) as $post_key ):
412
+ if ( preg_match( '#^ctc_(ovrd|\d+)_child_([\w\-]+?)_(\d+?)(_(.+))?$#', $post_key, $matches ) ):
413
  $valid = $matches[1];
414
  $rule = $matches[2];
415
+ if ( NULL == $rule || !isset( $this->dict_rule[$rule] ) ) continue;
416
  $ruleid = $this->dict_rule[$rule];
417
  $qsid = $matches[3];
418
+ $value = $this->normalize_color( $this->sanitize( $this->parse_css_input( $_POST[$post_key] ) ) );
419
+ $important = $this->is_important( $value );
420
+ if ( !empty( $_POST['ctc_' . $valid . '_child_' . $rule . '_i_' . $qsid] ) ) $important = 1;
421
 
422
+ $selarr = $this->denorm_query_sel( $qsid );
423
+ if ( !empty( $matches[5] ) ):
424
  $parts[$qsid][$rule][$matches[5]] = $value;
425
  $parts[$qsid][$rule]['important'] = $important;
426
  $parts[$qsid][$rule]['query'] = $selarr['query'];
427
  $parts[$qsid][$rule]['selector'] = $selarr['selector'];
428
  else:
429
+ if ( $newselector && $newselector != $selarr['selector'] ):
430
  // If this is a renamed selector, add new selector to array
431
  // and clear original child selector values.
432
  // Passing the sequence in the last argument serves two purposes:
433
  // 1. sets sequence for new renamed selector.
434
  // 2. tells the update_arrays function to flag this as a
435
  // renamed selector to pass back in result array.
436
+ $this->update_arrays( 'child', $selarr['query'], $newselector,
437
+ $rule, trim( $value ), $important, $this->dict_seq[$qsid] );
438
+ $this->update_arrays( 'child', $selarr['query'], $selarr['selector'], $rule, '' );
439
  else:
440
  // Otherwise, just update with the new values:
441
+ $this->update_arrays( 'child', $selarr['query'], $selarr['selector'],
442
+ $rule, trim( $value ), $important );
443
  endif;
444
  endif;
445
  endif;
446
  endforeach;
447
+ foreach ( $parts as $qsid => $rule_arr ):
448
+ foreach ( $rule_arr as $rule => $rule_part ):
449
+ if ( 'background' == $rule ):
450
  $value = $rule_part['background_url'];
451
+ elseif ( 'background-image' == $rule ):
452
+ if ( empty( $rule_part['background_url'] ) ):
453
+ if ( empty( $rule_part['background_color2'] ) ):
454
  $value = '';
455
  else:
456
+ $value = implode( ':', array(
457
  $rule_part['background_origin'],
458
  $rule_part['background_color1'], '0%',
459
  $rule_part['background_color2'], '100%'
460
+ ) );
461
  endif;
462
  else:
463
  $value = $rule_part['background_url'];
464
  endif;
465
+ elseif ( preg_match( '#^border(\-(top|right|bottom|left))?$#', $rule ) ):
466
+ $value = implode( ' ', array(
467
  $rule_part['border_width'],
468
  $rule_part['border_style'],
469
  $rule_part['border_color']
470
+ ) );
471
  else:
472
  $value = '';
473
  endif;
474
+ if ( $newselector && $newselector != $rule_part['selector'] ):
475
+ $this->update_arrays( 'child', $rule_part['query'], $newselector,
476
+ $rule, trim( $value ), $rule_part['important'], $this->dict_seq[$qsid] );
477
+ $this->update_arrays( 'child', $rule_part['query'], $rule_part['selector'], $rule, '' );
478
  else:
479
+ $this->update_arrays( 'child', $rule_part['query'], $rule_part['selector'],
480
+ $rule, trim( $value ), $rule_part['important'] );
481
  endif;
482
  endforeach;
483
  endforeach;
489
  * Normalize raw user CSS input so that the parser can read it.
490
  * TODO: this is a stub for future use
491
  */
492
+ function parse_css_input( $styles ) {
493
+ return $this->repl_octal( stripslashes( $this->esc_octal( $styles ) ) );
494
  }
495
+ // strips non printables and potential commands
496
+ function sanitize( $styles ) {
497
+ return sanitize_text_field( preg_replace( '/[^[:print:]]/', '', $styles ) );
498
  }
499
+ // escapes octal values in input to allow for specific ascii strings in content rule
500
+ function esc_octal( $styles ){
501
+ return preg_replace( "#(['\"])\\\\([0-9a-f]{4})(['\"])#i", "$1##bs##$2$3", $styles );
502
  }
503
+ // unescapes octal values for writing specific ascii strings in content rule
504
+ function repl_octal( $styles ) {
505
+ return str_replace( "##bs##", "\\", $styles );
506
  }
507
  /*
508
  * parse_css_file
509
  * reads stylesheet to get WordPress meta data and passes rest to parse_css
510
  */
511
+ function parse_css_file( $template, $file = 'style.css' ) {
512
 
513
  $this->ctc()->cache_updates = FALSE;
514
  $this->styles = ''; // reset styles
515
+ $this->read_stylesheet( $template, $file );
516
  // get theme name
517
  $regex = '#Theme Name:\s*(.+?)\n#i';
518
+ preg_match( $regex, $this->styles, $matches );
519
+ $child_name = $this->get_prop( 'child_name' );
520
+ if ( !empty( $matches[1] ) && 'child' == $template && empty( $child_name ) ) $this->set_prop( 'child_name', $matches[1] );
521
+ $this->parse_css( $template );
522
  }
523
 
524
  /*
525
  * parse_css
526
+ * Accepts raw CSS as text and parses into individual properties.
527
  */
528
+ function parse_css( $template, $basequery = NULL, $parse_imports = TRUE ) {
529
+ if ( FALSE === strpos( $basequery, '@' ) ):
530
  $basequery = 'base';
531
  endif;
532
  $ruleset = array();
533
  // ignore commented code
534
+ $this->styles = preg_replace( '#\/\*.*?\*\/#s', '', $this->styles );
535
  // space brace to ensure correct matching
536
+ $this->styles = preg_replace( '#(\{\s*)#', "$1\n", $this->styles );
537
  // get all imports
538
+ if ( $parse_imports ):
539
 
540
  $regex = '#(\@import.+?);#';
541
+ preg_match_all( $regex, $this->styles, $matches );
542
+ foreach ( preg_grep( '#' . $this->get_prop( 'parnt' ) . '\/style\.css#', $matches[1], PREG_GREP_INVERT ) as $import )
543
  $this->imports[$template][$import] = 1;
544
+ if ( $this->ctc()->cache_updates ):
545
  $this->ctc()->updates[] = array(
546
  'obj' => 'imports',
547
+ 'data' => array_keys( $this->imports[$template] ),
548
  );
549
  endif;
550
  endif;
551
  // break into @ segments
552
+ foreach ( array(
553
  '#(\@media[^\{]+?)\{(\s*?)\}#', // get an placehoder (empty) media queries
554
  '#(\@media[^\{]+?)\{(.*?\})?\s*?\}#s', // get all other media queries
555
+ ) as $regex ): // (((?!\@media).) backreference too memory intensive - rolled back in v 1.4.8.1
556
+ preg_match_all( $regex, $this->styles, $matches );
557
+ foreach ( $matches[1] as $segment ):
558
+ $ruleset[trim( $segment )] = array_shift( $matches[2] ) . ( isset( $ruleset[trim( $segment )] )?$ruleset[trim( $segment )]:'' );
559
  endforeach;
560
  // stripping rulesets leaves base styles
561
+ $this->styles = preg_replace( $regex, '', $this->styles );
562
  endforeach;
563
  $ruleset[$basequery] = $this->styles;
564
+ foreach ( $ruleset as $query => $segment ):
565
  // make sure there is semicolon before closing brace
566
+ $segment = preg_replace( '#(\})#', ";$1", $segment );
567
+ // parses selectors and corresponding rules
568
  $regex = '#\s([\[\.\#\:\w][\w\-\s\(\)\[\]\'\^\*\.\#\+:,"=>]+?)\s*\{(.*?)\}#s'; //
569
+ preg_match_all( $regex, $segment, $matches );
570
+ foreach( $matches[1] as $sel ):
571
+ $stuff = array_shift( $matches[2] );
572
+ $this->update_arrays( $template, $query, $sel );
573
  // handle base64 data
574
  $stuff = preg_replace( '#data:([^;]+?);([^\)]+?)\)#s', "data:$1%%semi%%$2)", $stuff );
575
+ foreach ( explode( ';', $stuff ) as $ruleval ):
576
+ if ( $this->qskey > CHLD_THM_CFG_MAX_SELECTORS ) break;
577
+ if ( FALSE === strpos( $ruleval, ':' ) ) continue;
578
+ list( $rule, $value ) = explode( ':', $ruleval, 2 );
579
+ $rule = trim( $rule );
580
+ $rule = preg_replace_callback( "/[^\w\-]/", array( $this, 'to_ascii' ), $rule );
581
  // handle base64 data
582
+ $value = trim( str_replace( '%%semi%%', ';', $value ) );
583
 
584
  $rules = $values = array();
585
  // save important flag
586
+ $important = $this->is_important( $value );
587
  // normalize color
588
+ $value = $this->normalize_color( $value );
589
  // normalize font
590
+ if ( 'font' == $rule ):
591
+ $this->normalize_font( $value, $rules, $values );
592
  // normalize background
593
+ elseif( 'background' == $rule ):
594
+ $this->normalize_background( $value, $rules, $values );
595
  // normalize margin/padding
596
+ elseif ( 'margin' == $rule || 'padding' == $rule ):
597
+ $this->normalize_margin_padding( $rule, $value, $rules, $values );
598
  else:
599
  $rules[] = $rule;
600
  $values[] = $value;
601
  endif;
602
+ foreach ( $rules as $rule ):
603
+ $value = trim( array_shift( $values ) );
604
  // normalize zero values
605
+ $value = preg_replace( '#\b0(px|r?em)#', '0', $value );
606
  // normalize gradients
607
+ if ( FALSE !== strpos( $value, 'gradient' ) ):
608
+ if ( FALSE !== strpos( $rule, 'filter' ) ):
609
  $rule = 'background-image';
610
  continue; // treat as background-image, we'll add filter rule later
611
  endif;
612
+ if ( FALSE !== strpos( $value, 'webkit-gradient' ) ) continue; // bail on legacy webkit, we'll add it later
613
+ $value = $this->encode_gradient( $value );
614
  endif;
615
  // normalize common vendor prefixes
616
+ $rule = preg_replace( '#(\-(o|ms|moz|webkit)\-)?(' . implode( '|', $this->vendorrule ) . ')#', "$3", $rule );
617
+ $this->update_arrays( $template, $query, $sel, $rule, $value, $important );
618
  endforeach;
619
  endforeach;
620
  endforeach;
628
  * @media query blocks are sorted using internal heuristics (see sort_queries)
629
  * New selectors are appended to the end of each media query block.
630
  */
631
+ function write_css( $backup = FALSE ) {
632
  // write new stylesheet
633
+ $output = apply_filters( 'chld_thm_cfg_css_header', $this->get_css_header(), $this );
634
+ $imports = $this->get_prop( 'imports' );
635
+ if ( !empty( $imports ) ):
636
+ foreach ( $imports as $import ):
637
  $output .= $import . ';' . LF;
638
  endforeach;
639
  endif;
640
  $output .= LF;
641
  // turn the dictionaries into indexes (value => id into id => value):
642
+ $rulearr = array_flip( $this->dict_rule );
643
+ $valarr = array_flip( $this->dict_val );
644
+ $selarr = array_flip( $this->dict_sel );
645
+ foreach ( $this->sort_queries() as $query => $sort_order ):
646
  $has_selector = 0;
647
  $sel_output = '';
648
  $selectors = $this->sel_ndx[$this->dict_query[$query]];
649
+ uasort( $selectors, array( $this, 'cmp_seq' ) );
650
+ if ( 'base' != $query ) $sel_output .= $query . ' {' . LF;
651
+ foreach ( $selectors as $selid => $qsid ):
652
  $has_value = 0;
653
  $sel = $selarr[$selid];
654
+ if ( !empty( $this->val_ndx[$qsid] ) ):
655
  $shorthand = array();
656
+ foreach ( $this->val_ndx[$qsid] as $ruleid => $valid ):
657
+ if ( isset( $valid['child'] )
658
+ && isset( $valarr[$valid['child']] )
659
  && '' !== $valarr[$valid['child']]
660
+ && ( !isset( $valid['parnt'] ) || $valid['parnt'] != $valid['child'] ) ):
661
+ if ( ! $has_value ):
662
+ $sel_output .= isset( $this->dict_seq[$qsid] )?'/*' . $this->dict_seq[$qsid] . '*/' . LF:'';
663
  // show load order
664
  $sel_output .= $sel . ' {' . LF;
665
  $has_value = 1;
666
  $has_selector = 1;
667
  endif;
668
+ $important_parnt = empty( $valid['i_parnt'] ) ? 0 : 1;
669
+ $important = isset( $valid['i_child'] ) ? $valid['i_child'] : $important_parnt;
670
+ $sel_output .= $this->add_vendor_rules( $rulearr[$ruleid], $valarr[$valid['child']], $shorthand, $important );
671
  endif;
672
  endforeach;
673
+ $sel_output .= $this->encode_shorthand( $shorthand ); // . ( $important ? ' !important' : '' );
674
+ if ( $has_value ):
675
  $sel_output .= '}' . LF;
676
  endif;
677
  endif;
678
  endforeach;
679
+ if ( 'base' != $query ) $sel_output .= '}' . LF;
680
+ if ( $has_selector ) $output .= $sel_output;
681
  endforeach;
682
+ $stylesheet = apply_filters( 'chld_thm_cfg_target', $this->get_child_target(), $this );
683
+ if ( $stylesheet_verified = $this->is_file_ok( $stylesheet, 'write' ) ):
684
  global $wp_filesystem; // this was initialized earlier;
685
  // backup current stylesheet
686
+ if ( $backup && is_file( $stylesheet_verified ) ):
687
+ $timestamp = date( 'YmdHis', current_time( 'timestamp' ) );
688
+ $bakfile = preg_replace( "/\.css$/", '', $stylesheet_verified ) . '-' . $timestamp . '.css';
689
  // don't write new stylesheet if backup fails
690
+ if ( !$wp_filesystem->copy( $this->ctc()->fspath( $stylesheet_verified ), $this->ctc()->fspath( $bakfile ) ) ) return FALSE;
691
  endif;
692
  // write new stylesheet:
693
  // try direct write first, then wp_filesystem write
694
  // stylesheet must already exist and be writable by web server
695
+ if ( FALSE !== @file_put_contents( $stylesheet_verified, $output ) ): //is_writable( $stylesheet_verified ) &&
696
  return TRUE;
697
+ elseif ( FALSE !== $wp_filesystem->put_contents( $this->ctc()->fspath( $stylesheet_verified ), $output ) ):
698
  return TRUE;
699
  endif;
700
  endif;
707
  * These are based on commonly used practices and not all vendor prefixed are supported
708
  * TODO: verify this logic against vendor and W3C documentation
709
  */
710
+ function add_vendor_rules( $rule, $value, &$shorthand, $important = 0 ) {
711
  $rules = '';
712
+ if ( 'filter' == $rule && ( FALSE !== strpos( $value, 'progid:DXImageTransform.Microsoft.Gradient' ) ) ) return;
713
  $importantstr = $important ? ' !important' : '';
714
+ if ( preg_match( "/^(margin|padding)\-(top|right|bottom|left)$/", $rule, $matches ) ):
715
  $shorthand[$matches[1]][$matches[2]] = $value . $importantstr;
716
  return '';
717
+ elseif ( preg_match( '/^(' . implode( '|', $this->vendorrule ) . ')$/', $rule ) ):
718
+ foreach( array( 'moz', 'webkit', 'o' ) as $prefix ):
719
  $rules .= ' -' . $prefix . '-' . $rule . ': ' . $value . $importantstr . ';' . LF;
720
  endforeach;
721
  $rules .= ' ' . $rule . ': ' . $value . $importantstr . ';' . LF;
722
+ elseif ( 'background-image' == $rule ):
723
  // gradient?
724
 
725
+ if ( $gradient = $this->decode_gradient( $value ) ):
726
  // standard gradient
727
+ foreach( array( 'moz', 'webkit', 'o', 'ms' ) as $prefix ):
728
  $rules .= ' background-image: -' . $prefix . '-' . 'linear-gradient(' . $gradient['origin'] . ', '
729
  . $gradient['color1'] . ', ' . $gradient['color2'] . ')' . $importantstr . ';' . LF;
730
  endforeach;
731
  // W3C standard gradient
732
  // rotate origin 90 degrees
733
+ if ( preg_match( '/(\d+)deg/', $gradient['origin'], $matches ) ):
734
+ $org = ( 90 - $matches[1] ) . 'deg';
735
  else:
736
+ foreach ( preg_split( "/\s+/", $gradient['origin'] ) as $dir ):
737
+ $dir = strtolower( $dir );
738
+ $dirs[] = ( 'top' == $dir ? 'bottom' : ( 'bottom' == $dir ? 'top' : ( 'left' == $dir ? 'right' : ( 'right' == $dir ? 'left' : $dir ) ) ) );
739
  endforeach;
740
+ $org = 'to ' . implode( ' ', $dirs );
741
  endif;
742
  $rules .= ' background-image: linear-gradient(' . $org . ', '
743
  . $gradient['color1'] . ', ' . $gradient['color2'] . ')' . $importantstr . ';' . LF;
746
  // '-webkit-gradient(linear,' .$origin . ', ' . $color1 . ', '. $color2 . ')';
747
 
748
  // MS filter gradient
749
+ $type = ( in_array( $gradient['origin'], array( 'left', 'right', '0deg', '180deg' ) ) ? 1 : 0 );
750
+ $color1 = preg_replace( "/^#/", '#00', $gradient['color1'] );
751
  $rules .= ' filter: progid:DXImageTransform.Microsoft.Gradient(GradientType=' . $type . ', StartColorStr="'
752
+ . strtoupper( $color1 ) . '", EndColorStr="' . strtoupper( $gradient['color2'] ) . '")' . $importantstr . ';' . LF;
753
  else:
754
  // url or other value
755
  $rules .= ' ' . $rule . ': ' . $value . $importantstr . ';' . LF;
756
  endif;
757
  else:
758
+ $rule = preg_replace_callback( "/\d+/", array( $this, 'from_ascii' ), $rule );
759
  $rules .= ' ' . $rule . ': ' . $value . $importantstr . ';' . LF;
760
  endif;
761
  return $rules;
766
  * parses background shorthand value and returns
767
  * normalized rule/value pairs for each property
768
  */
769
+ function normalize_background( $value, &$rules, &$values ){
770
+ if ( FALSE !== strpos( $value, 'gradient' ) ):
771
  // only supporting linear syntax
772
+ if ( preg_match( '#(linear\-|Microsoft\.)#', $value ) ):
773
  $values[] = $value;
774
  $rules[] = 'background-image';
775
  endif;
776
  else:
777
  $regex = '#(url *\([^\)]+\))#';
778
+ if ( preg_match( $regex, $value, $matches ) ) $url = $matches[1];
779
+ $parts = preg_split( $regex, $value );
780
 
781
+ if ( count( $parts ) == 1 ):
782
  // this is a named color or single hex color or none
783
+ $part = str_replace( ' ', '', $parts[0] );
784
  $rules[] = 'none' == $part ? 'background' : 'background-color';
785
  $values[] = $part;
786
  else:
787
  $rules[] = 'background-image';
788
  $values[] = $url;
789
+ if ( !empty( $parts[0] ) && '' !== $parts[0] ):
790
  $rules[] = 'background-color';
791
+ $values[] = trim( $parts[0] );
792
  endif;
793
  $position = array();
794
+ foreach( preg_split( '/ +/', trim( $parts[1] ) ) as $part ):
795
+ if ( '' === $part ) continue; // empty( $part ) ||
796
+ if ( FALSE === strpos( $part, 'repeat' ) ):
797
  $position[] = $part;
798
  else:
799
  $rules[] = 'background-repeat';
800
  $values[] = $part;
801
  endif;
802
  endforeach;
803
+ if ( count( $position ) ):
804
  $rules[] = 'background-position';
805
+ $values[] = implode( ' ', $position );
806
  endif;
807
  endif;
808
  endif;
813
  * parses font shorthand value and returns
814
  * normalized rule/value pairs for each property
815
  */
816
+ function normalize_font( $value, &$rules, &$values ) {
817
  $regex = '#^((\d+|bold|normal) )?((italic|normal) )?(([\d\.]+(px|r?em|%))[\/ ])?(([\d\.]+(px|r?em|%)?) )?(.+)$#is';
818
+ preg_match( $regex, $value, $parts );
819
+ if ( !empty( $parts[2] ) ):
820
  $rules[] = 'font-weight';
821
  $values[] = $parts[2];
822
  endif;
823
+ if ( !empty( $parts[4] ) ):
824
  $rules[] = 'font-style';
825
  $values[] = $parts[4];
826
  endif;
827
+ if ( !empty( $parts[6] ) ):
828
  $rules[] = 'font-size';
829
  $values[] = $parts[6];
830
  endif;
831
+ if ( !empty( $parts[9] ) ):
832
  $rules[] = 'line-height';
833
  $values[] = $parts[9];
834
  endif;
835
+ if ( !empty( $parts[11] ) ):
836
  $rules[] = 'font-family';
837
  $values[] = $parts[11];
838
  endif;
844
  * normalized rule/value pairs for each property
845
  * TODO: reassemble into shorthand when writing CSS file
846
  */
847
+ function normalize_margin_padding( $rule, $value, &$rules, &$values ) {
848
+ $parts = preg_split( "/ +/", trim( $value ) );
849
+ if ( !isset( $parts[1] ) ) $parts[1] = $parts[0];
850
+ if ( !isset( $parts[2] ) ) $parts[2] = $parts[0];
851
+ if ( !isset( $parts[3] ) ) $parts[3] = $parts[1];
852
  $rules[0] = $rule . '-top';
853
  $values[0] = $parts[0];
854
  $rules[1] = $rule . '-right';
859
  $values[3] = $parts[3];
860
  }
861
 
862
+ /*
863
+ * encode_shorthand
864
+ * converts CTC long syntax into CSS shorthand
865
+ */
866
+ function encode_shorthand( $shorthand ) {
867
  $rules = '';
868
  $importantstr = ' !important';
869
+ foreach ( array_keys( $shorthand ) as $key ):
870
  $important = array();
871
  $rule = array();
872
  $importantct = 0;
873
  // which sides do we have and are they important?
874
+ foreach( $shorthand[$key] as $side => $val ):
875
  $ict = 0;
876
+ $rule[$side] = trim( preg_replace( '/'.$importantstr.'/', '', $val, 1, $ict ) );
877
  $important[$side] = $ict;
878
  $importantct += $ict;
879
  endforeach;
880
  // shorthand must have 4 explicit values and all must have same priority
881
+ if ( 4 == count( $rule ) && ( 0 == $importantct || 4 == $importantct ) ):
882
  // let's try to condense the values into as few as possible, starting with the top value
883
  $parts = array();
884
  $parts[0] = $rule['top'];
885
  // if left is not the same as right, we must use all 4 values
886
+ if ( $rule['left'] !== $rule['right'] ):
887
  $parts[3] = $rule['left'];
888
  $parts[2] = $rule['bottom'];
889
  $parts[1] = $rule['right'];
890
  endif;
891
  // if top is not the same as bottom, we must use at least 3 values
892
+ if ( $rule['bottom'] !== $rule['top'] ):
893
  $parts[2] = $rule['bottom'];
894
  $parts[1] = $rule['right'];
895
  endif;
896
  // if top is not the same as right, we must use at least 2 values
897
+ if ( $rule['right'] !== $rule['top'] ):
898
  $parts[1] = $rule['right'];
899
  endif;
900
  // the order of the sides is critical: top right bottom left
901
+ ksort( $parts );
902
+ $shorthandstr = implode( ' ', $parts );
903
  // if important counter is > 0, it must be == 4, add flag
904
+ $rules .= ' ' . $key . ': ' . $shorthandstr . ( $importantct ? ' ' . $importantstr : '' ) . ';' . LF;
905
  else:
906
  // otherwise return separate rule for each side
907
+ foreach ( $rule as $side => $value ):
908
+ $rules .= ' ' . $key . '-' . $side . ': ' . $value . ( $important[$side] ? $importantstr : '' ) . ';' . LF;
909
  endforeach;
910
  endif;
911
  endforeach;
918
  * Currently only supports two-color linear gradients with no inner stops.
919
  * TODO: legacy webkit? more gradients?
920
  */
921
+ function encode_gradient( $value ) {
922
+ // don't try this at home, kids
923
  $regex = '#gradient[^\)]*?\((((top|bottom|left|right)?( (top|bottom|left|right))?|\d+deg),)?([^\)]*[\'"]?(\#\w{3,8}|rgba?\([\d, ]+?\)|hsla?\([\d%, ]+?\))( \d+%)?)([^\)]*[\'"]?(\#\w{3,8}|rgba?\([\d, ]+?\)|hsla?\([\d%, ]+?\))( \d+%)?)([^\)]*gradienttype=[\'"]?(\d)[\'"]?)?[^\)]*\)#i';
924
  $param = $parts = array();
925
+ preg_match( $regex, $value, $parts );
926
+ if ( empty( $parts[13] ) ):
927
+ if ( empty( $parts[2] ) ):
928
  $param[0] = 'top';
929
  else:
930
+ $param[0] = trim( $parts[2] );
931
  endif;
932
+ if ( empty( $parts[8] ) ):
933
  $param[2] = '0%';
934
  else:
935
+ $param[2] = trim( $parts[8] );
936
  endif;
937
+ if ( empty( $parts[11] ) ):
938
  $param[4] = '100%';
939
  else:
940
+ $param[4] = trim( $parts[11] );
941
  endif;
942
+ elseif( '0' == $parts[13] ):
943
  $param[0] = 'top';
944
  $param[2] = '0%';
945
  $param[4] = '100%';
946
+ elseif ( '1' == $parts[13] ):
947
  $param[0] = 'left';
948
  $param[2] = '0%';
949
  $param[4] = '100%';
950
  endif;
951
+ if ( isset( $parts[7] ) && isset( $parts[10] ) ):
952
  $param[1] = $parts[7];
953
  $param[3] = $parts[10];
954
+ ksort( $param );
955
+ return implode( ':', $param );
956
  else: return $value;
957
  endif;
958
  }
959
 
960
  /*
961
  * decode_border
962
+ * De-normalize CTC border syntax into individual properties.
963
  */
964
+ function decode_border( $value ) {
965
+ if ( preg_match( '#^(0|none)#i', $value ) ):
966
  $parts[0] = $value;
967
  $parts[1] = $parts[2] = '';
968
  else:
969
+ $parts = preg_split( '#\s+#', $value, 3 );
970
  endif;
971
  return array(
972
+ 'width' => empty( $parts[0] ) ? '' : $parts[0],
973
+ 'style' => empty( $parts[1] ) ? '' : $parts[1],
974
+ 'color' => empty( $parts[2] ) ? '' : $parts[2],
975
  );
976
  }
977
 
978
  /*
979
  * decode_gradient
980
+ * Decode CTC gradient syntax into individual properties.
981
  */
982
+ function decode_gradient( $value ) {
983
+ $parts = explode( ':', $value, 5 );
984
+ if ( !preg_match( '#(url|none)#i', $value ) && 5 == count( $parts ) ):
985
  return array(
986
+ 'origin' => empty( $parts[0] ) ? '' : $parts[0],
987
+ 'color1' => empty( $parts[1] ) ? '' : $parts[1],
988
+ 'stop1' => empty( $parts[2] ) ? '' : $parts[2],
989
+ 'color2' => empty( $parts[3] ) ? '' : $parts[3],
990
+ 'stop2' => empty( $parts[4] ) ? '' : $parts[4],
991
  );
992
  endif;
993
  return FALSE;
997
  * denorm_rule_val
998
  * Return array of unique values corresponding to specific rule
999
  */
1000
+ function denorm_rule_val( $ruleid ) {
1001
  $rule_sel_arr = array();
1002
+ $val_arr = array_flip( $this->dict_val );
1003
+ foreach ( $this->val_ndx as $selid => $rules ):
1004
+ if ( !isset( $rules[$ruleid] ) ) continue;
1005
+ foreach ( $rules[$ruleid] as $theme => $val ):
1006
+ if ( !isset( $val_arr[$val] ) || '' === $val_arr[$val] ) continue;
1007
  $rule_sel_arr[$val] = $val_arr[$val];
1008
  endforeach;
1009
  endforeach;
1015
  * Return array of queries, selectors, rules, and values corresponding to
1016
  * specific rule/value combo grouped by query, selector
1017
  */
1018
+ function denorm_val_query( $valid, $rule ) {
1019
  $value_query_arr = array();
1020
+ foreach ( $this->val_ndx as $qsid => $rules ):
1021
+ foreach ( $rules as $ruleid => $values ):
1022
+ if ( $ruleid != $this->dict_rule[$rule] ) continue;
1023
+ foreach ( $values as $name => $val ):
1024
+ if ( 'i' == $name || $val != $valid ) continue;
1025
+ $selarr = $this->denorm_query_sel( $qsid );
1026
+ $valarr = $this->denorm_sel_val( $qsid );
1027
  $value_query_arr[$rule][$selarr['query']][$qsid] = $valarr;
1028
  endforeach;
1029
  endforeach;
1035
  * denorm_query_sel
1036
  * Return id, query and selector values of a specific qsid (query-selector ID)
1037
  */
1038
+ function denorm_query_sel( $qsid ) {
1039
+ $queryarr = array_flip( $this->dict_query );
1040
+ $selarr = array_flip( $this->dict_sel );
1041
+ $this->dict_seq[$qsid] = isset( $this->dict_seq[$qsid] ) ? $this->dict_seq[$qsid] : $qsid;
1042
  return array(
1043
  'id' => $qsid,
1044
  'query' => $queryarr[$this->dict_qs[$qsid]['q']],
1052
  * Return array of rules, and values matching specific qsid (query-selector ID)
1053
  * grouped by query, selector
1054
  */
1055
+ function denorm_sel_val( $qsid ) {
1056
+ $selarr = $this->denorm_query_sel( $qsid );
1057
+ $valarr = array_flip( $this->dict_val );
1058
+ $rulearr = array_flip( $this->dict_rule );
1059
+ if ( isset( $this->val_ndx[$qsid] ) && is_array( $this->val_ndx[$qsid] ) ):
1060
+ foreach ( $this->val_ndx[$qsid] as $ruleid => $values ):
1061
+ foreach ( $values as $name => $val ):
1062
+ if ( 'i_parnt' == $name || 'i_child' == $name ):
1063
+ $selarr['value'][$rulearr[$ruleid]][$name] = ( empty( $val ) ? 0 : 1 );
1064
+ elseif ( !isset( $valarr[$val] ) || '' === $valarr[$val] ):
1065
  continue;
1066
  else:
1067
  $selarr['value'][$rulearr[$ruleid]][$name] = $valarr[$val];
1077
  * denorm_sel_ndx
1078
  * Return denormalized array containing query and selector heirarchy
1079
  */
1080
+ function denorm_sel_ndx( $query = NULL ) {
1081
  $sel_ndx_norm = array();
1082
+ $queryarr = array_flip( $this->dict_query );
1083
+ $selarr = array_flip( $this->dict_sel );
1084
+ foreach( $this->sel_ndx as $queryid => $sel ):
1085
+ foreach( $sel as $selid => $qsid ):
1086
  $sel_ndx_norm[$queryarr[$queryid]][$selarr[$selid]] = $qsid;
1087
  endforeach;
1088
  endforeach;
1089
+ return empty( $query ) ? $sel_ndx_norm : $sel_ndx_norm[$query];
1090
  }
1091
 
1092
  /*
1094
  * Strip important flag from value ref and return boolean
1095
  * Value is updated because it is a ref
1096
  */
1097
+ function is_important( &$value ) {
1098
  $important = 0;
1099
+ $value = trim( str_ireplace( '!important', '', $value, $important ) );
1100
  return $important;
1101
  }
1102
 
1110
  */
1111
  function sort_queries() {
1112
  $queries = array();
1113
+ $queryarr = array_flip( $this->dict_query );
1114
+ foreach ( array_keys( $this->sel_ndx ) as $queryid ):
1115
  $query = $queryarr[$queryid];
1116
+ if ( 'base' == $query ):
1117
  $queries['base'] = -999999;
1118
  continue;
1119
  endif;
1120
+ if ( preg_match( "/((min|max)(\-device)?\-width)\s*:\s*(\d+)/", $query, $matches ) ):
1121
  $queries[$query] = 'min-width' == $matches[1] ? $matches[4] : -$matches[4];
1122
  else:
1123
  $queries[$query] = $queryid - 10000;
1124
  endif;
1125
  endforeach;
1126
+ asort( $queries );
1127
  return $queries;
1128
  }
1129
 
1130
  // sort selectors based on dict_seq if exists, otherwise qsid
1131
+ function cmp_seq( $a, $b ) {
1132
+ $cmpa = isset( $this->dict_seq[$a] ) ? $this->dict_seq[$a] : $a;
1133
+ $cmpb = isset( $this->dict_seq[$b] ) ? $this->dict_seq[$b] : $b;
1134
+ if ( $cmpa == $cmpb ) return 0;
1135
+ return ( $cmpa < $cmpb ) ? -1 : 1;
1136
  }
1137
 
1138
  /*
1141
  * flattens to array
1142
  * and stringifies NULLs
1143
  */
1144
+ function obj_to_utf8( $data ) {
1145
 
1146
+ if ( is_object( $data ) ) {
1147
+ $data = get_object_vars( $data );
1148
  }
1149
+ if ( is_array( $data ) ) {
1150
+ return array_map( array( &$this, __FUNCTION__ ), $data );
1151
  }
1152
  else {
1153
+ return is_null( $data ) ? '' : utf8_encode( $data );
1154
  }
1155
  }
1156
+ // convert ascii character into decimal value
1157
+ function to_ascii( $matches ) {
1158
+ return ord( $matches[0] );
1159
  }
1160
+ // convert decimal value into ascii character
1161
+ function from_ascii( $matches ) {
1162
+ return chr( $matches[0] );
1163
  }
1164
 
1165
  /* is_file_ok
1166
  * verify file exists and is in valid location
1167
  */
1168
+ function is_file_ok( $stylesheet, $permission = 'read' ) {
1169
  // remove any ../ manipulations
1170
+ $stylesheet = preg_replace( "%\.\./%", '/', $stylesheet );
1171
+ if ( 'read' == $permission && !is_file( $stylesheet ) ) return FALSE;
1172
+ if ( 'search' == $permission && !is_dir( $stylesheet ) ) return FALSE;
1173
  // sanity check for php files
1174
+ //if ( !preg_match( '%' . preg_quote( $ext ) . '$%', $stylesheet ) ) return FALSE;
1175
  // check if in themes dir;
1176
+ if ( preg_match( '%^' . preg_quote( get_theme_root() ) . '%', $stylesheet ) ) return $stylesheet;
1177
  // check if in plugins dir
1178
+ if ( preg_match( '%^' . preg_quote( WP_PLUGIN_DIR ) . '%', $stylesheet ) ) return $stylesheet;
1179
  return FALSE;
1180
  }
1181
+ /* normalize_color
1182
+ * Sets hex string to lowercase and shortens to 3 char format if possible
1183
+ */
1184
+ function normalize_color( $value ) {
1185
+ $value = preg_replace_callback( "/#([0-9A-F]{3}([0-9A-F]{3})?)/i", array( $this, 'tolower' ), $value );
1186
  $value = preg_replace( "/#([0-9A-F])\\1([0-9A-F])\\2([0-9A-F])\\3/i", "#$1$2$3", $value );
1187
  return $value;
1188
  }
1189
+ // callback for normalize_color regex
1190
+ function tolower( $matches ) {
1191
+ return '#' . strtolower( $matches[1] );
1192
  }
1193
  }
1194
  ?>
includes/class-ctc-ui.php CHANGED
@@ -1,20 +1,20 @@
1
  <?php
2
  // Exit if accessed directly
3
- if ( !defined('ABSPATH')) exit;
4
  /*
5
  Class: Child_Theme_Configurator_UI
6
  Plugin URI: http://www.lilaeamedia.com/plugins/child-theme-configurator/
7
  Description: Handles the plugin User Interface
8
- Version: 1.6.2.1
9
  Author: Lilaea Media
10
  Author URI: http://www.lilaeamedia.com/
11
  Text Domain: chld_thm_cfg
12
  Domain Path: /lang
13
  License: GPLv2
14
- Copyright (C) 2014 Lilaea Media
15
  */
16
  class ChildThemeConfiguratorUI {
17
-
18
  function ctc() {
19
  global $chld_thm_cfg;
20
  return $chld_thm_cfg;
@@ -23,71 +23,71 @@ class ChildThemeConfiguratorUI {
23
  function render() {
24
  $css = $this->ctc()->css;
25
  $themes = $this->ctc()->themes;
26
- $parent = isset($_GET['ctc_parent']) ? sanitize_text_field($_GET['ctc_parent']) : $css->get_prop('parnt');
27
- $child = $css->get_prop('child');
28
- $configtype = $css->get_prop('configtype');
29
- if (empty($configtype)) $configtype = 'theme';
30
- $hidechild = (count($themes['child']) ? '' : 'style="display:none"');
31
- $enqueueset = 'theme' != $configtype || isset($css->enqueue);
32
- $mustimport = $this->parent_stylesheet_check($parent);
33
- $imports = $css->get_prop('imports');
34
  $id = 0;
35
  $this->ctc()->fs_method = get_filesystem_method();
36
  add_thickbox();
37
- add_action('chld_thm_cfg_related_links', array($this, 'lilaea_plug'));
38
- include ($this->ctc()->pluginPath .'/includes/forms/main.php');
39
  }
40
 
41
- function parent_stylesheet_check($parent) {
42
- $file = trailingslashit(get_theme_root()) . trailingslashit($parent) . 'header.php';
43
  $regex = '/<link[^>]+?stylesheet_ur[li]/is';
44
- if (file_exists($file)):
45
- $contents = file_get_contents($file);
46
- if (preg_match($regex, $contents)) return TRUE;
47
  endif;
48
  return FALSE;
49
  }
50
 
51
 
52
- function render_theme_menu($template = 'child', $selected = NULL) {
53
  ?>
54
  <select class="ctc-select" id="ctc_theme_<?php echo $template; ?>" name="ctc_theme_<?php echo $template; ?>" style="visibility:hidden"><?php
55
- foreach ($this->ctc()->themes[$template] as $slug => $theme)
56
- echo '<option value="' . $slug . '"' . ($slug == $selected ? ' selected' : '') . '>'
57
- . esc_attr($theme['Name']) . '</option>' . LF; ?>
58
  </select>
59
  <div style="display:none">
60
  <?php
61
- foreach ($this->ctc()->themes[$template] as $slug => $theme)
62
- include ($this->ctc()->pluginPath . 'includes/forms/themepreview.php'); ?>
63
  </div>
64
  <?php
65
  }
66
 
67
- function render_file_form($template = 'parnt') {
68
  global $wp_filesystem;
69
- if ($theme = $this->ctc()->css->get_prop($template)):
70
- $themeroot = trailingslashit(get_theme_root()) . trailingslashit($theme);
71
- $files = $this->ctc()->get_files($theme);
72
  $counter = 0;
73
- sort($files);
74
  ob_start();
75
- foreach ($files as $file):
76
- $templatefile = preg_replace('%\.php$%', '', $file);
77
- $excludes = implode("|", (array) apply_filters('chld_thm_cfg_template_excludes', $this->ctc()->excludes));
78
- if ('parnt' == $template && (preg_match('%^(' . $excludes . ')\w*\/%',$templatefile)
79
- || 'functions' == basename($templatefile))) continue;
80
  include ( $this->ctc()->pluginPath . 'includes/forms/file.php' );
81
  endforeach;
82
- if ('child' == $template && ($backups = $this->ctc()->get_files($theme, 'backup'))):
83
- foreach ( $backups as $backup => $label):
84
- $templatefile = preg_replace('%\.css$%', '', $backup);
85
  include ( $this->ctc()->pluginPath . 'includes/forms/backup.php' );
86
  endforeach;
87
  endif;
88
  $inputs = ob_get_contents();
89
  ob_end_clean();
90
- if ($counter):
91
  include ( $this->ctc()->pluginPath . 'includes/forms/fileform.php' );
92
  endif;
93
  endif;
@@ -95,29 +95,29 @@ class ChildThemeConfiguratorUI {
95
 
96
  function render_image_form() {
97
 
98
- if ($theme = $this->ctc()->css->get_prop('child')):
99
- $themeuri = trailingslashit(get_theme_root_uri()) . trailingslashit($theme);
100
- $files = $this->ctc()->get_files($theme, 'img');
101
 
102
  $counter = 0;
103
- sort($files);
104
  ob_start();
105
- foreach ($files as $file):
106
- $templatefile = preg_replace('/^images\//', '', $file);
107
  include( $this->ctc()->pluginPath . 'includes/forms/image.php' );
108
  endforeach;
109
  $inputs = ob_get_contents();
110
  ob_end_clean();
111
- if ($counter) include( $this->ctc()->pluginPath . 'includes/forms/images.php' );
112
  endif;
113
  }
114
 
115
  function get_theme_screenshot() {
116
 
117
- foreach (array_keys($this->ctc()->imgmimes) as $extreg):
118
- foreach (explode('|', $extreg) as $ext):
119
- if ($screenshot = $this->ctc()->css->is_file_ok($this->ctc()->css->get_child_target('screenshot.' . $ext))):
120
- $screenshot = trailingslashit(get_theme_root_uri()) . $this->ctc()->theme_basename('', $screenshot);
121
  return $screenshot . '?' . time();
122
  endif;
123
  endforeach;
@@ -127,52 +127,60 @@ class ChildThemeConfiguratorUI {
127
 
128
  function settings_errors() {
129
 
130
- if (count($this->ctc()->errors)):
131
  echo '<div class="error"><ul>' . LF;
132
- foreach ($this->ctc()->errors as $err):
133
  echo '<li>' . $err . '</li>' . LF;
134
  endforeach;
135
  echo '</ul></div>' . LF;
136
- elseif (isset($_GET['updated'])):
137
  echo '<div class="updated">' . LF;
138
- if ( 8 == $_GET['updated']):
139
- echo '<p>' . __('Child Theme files modified successfully.', 'chld_thm_cfg') . '</p>' . LF;
140
  else:
141
- echo '<p>' . apply_filters('chld_thm_cfg_update_msg', sprintf(__('Child Theme <strong>%s</strong> has been generated successfully.
142
- ', 'chld_thm_cfg'),
143
- $this->ctc()->css->get_prop('child_name')), $this->ctc()) . LF
144
- . '<strong>' . __('IMPORTANT:', 'chld_thm_cfg') . ' <a href="' . admin_url('/customize.php?theme=' . $this->ctc()->css->get_prop('child')) . '" title="' . __('Live Preview', 'chld_thm_cfg') . '" class="ctc-live-preview">' . __('Test your child theme', 'chld_thm_cfg') . '</a> ' . __('before activating.', 'chld_thm_cfg') . '</strong></p>';
145
- endif;
 
 
 
 
 
 
 
 
146
  echo '</div>' . LF;
147
  endif;
148
  }
149
 
150
  function render_help_content() {
151
  global $wp_version;
152
- if ( version_compare( $wp_version, '3.3') >= 0 ) {
153
 
154
  $screen = get_current_screen();
155
 
156
  // load help content via output buffer so we can use plain html for updates
157
  // then use regex to parse for help tab parameter values
158
 
159
- $regex_sidebar = '/' . preg_quote('<!-- BEGIN sidebar -->') . '(.*?)' . preg_quote('<!-- END sidebar -->') . '/s';
160
- $regex_tab = '/' . preg_quote('<!-- BEGIN tab -->') . '\s*<h\d id="(.*?)">(.*?)<\/h\d>(.*?)' . preg_quote('<!-- END tab -->') . '/s';
161
  ob_start();
162
  // stub for multiple languages future release
163
- include($this->ctc()->pluginPath . 'includes/help/help_en_US.php');
164
  $help_raw = ob_get_contents();
165
  ob_end_clean();
166
  // parse raw html for tokens
167
- preg_match($regex_sidebar, $help_raw, $sidebar);
168
- preg_match_all($regex_tab, $help_raw, $tabs);
169
 
170
  // Add help tabs
171
- if (isset($tabs[1])):
172
- while(count($tabs[1])):
173
- $id = array_shift($tabs[1]);
174
- $title = array_shift($tabs[2]);
175
- $content = array_shift($tabs[3]);
176
  $screen->add_help_tab( array(
177
  'id' => $id,
178
  'title' => $title,
@@ -180,13 +188,13 @@ class ChildThemeConfiguratorUI {
180
  ) );
181
  endwhile;
182
  endif;
183
- if (isset($sidebar[1]))
184
- $screen->set_help_sidebar($sidebar[1]);
185
 
186
  }
187
  }
188
  function lilaea_plug() {
189
- include ($this->ctc()->pluginPath . 'includes/forms/related.php');
190
  }
191
  }
192
  ?>
1
  <?php
2
  // Exit if accessed directly
3
+ if ( !defined( 'ABSPATH' ) ) exit;
4
  /*
5
  Class: Child_Theme_Configurator_UI
6
  Plugin URI: http://www.lilaeamedia.com/plugins/child-theme-configurator/
7
  Description: Handles the plugin User Interface
8
+ Version: 1.6.3
9
  Author: Lilaea Media
10
  Author URI: http://www.lilaeamedia.com/
11
  Text Domain: chld_thm_cfg
12
  Domain Path: /lang
13
  License: GPLv2
14
+ Copyright (C) 2014-2015 Lilaea Media
15
  */
16
  class ChildThemeConfiguratorUI {
17
+ // helper function to globalize ctc object
18
  function ctc() {
19
  global $chld_thm_cfg;
20
  return $chld_thm_cfg;
23
  function render() {
24
  $css = $this->ctc()->css;
25
  $themes = $this->ctc()->themes;
26
+ $parent = isset( $_GET['ctc_parent'] ) ? sanitize_text_field( $_GET['ctc_parent'] ) : $css->get_prop( 'parnt' );
27
+ $child = $css->get_prop( 'child' );
28
+ $configtype = $css->get_prop( 'configtype' );
29
+ if ( empty( $configtype ) ) $configtype = 'theme';
30
+ $hidechild = ( count( $themes['child'] ) ? '' : 'style="display:none"' );
31
+ $enqueueset = 'theme' != $configtype || isset( $css->enqueue );
32
+ $mustimport = $this->parent_stylesheet_check( $parent );
33
+ $imports = $css->get_prop( 'imports' );
34
  $id = 0;
35
  $this->ctc()->fs_method = get_filesystem_method();
36
  add_thickbox();
37
+ add_action( 'chld_thm_cfg_related_links', array( $this, 'lilaea_plug' ) );
38
+ include ( $this->ctc()->pluginPath .'/includes/forms/main.php' );
39
  }
40
 
41
+ function parent_stylesheet_check( $parent ) {
42
+ $file = trailingslashit( get_theme_root() ) . trailingslashit( $parent ) . 'header.php';
43
  $regex = '/<link[^>]+?stylesheet_ur[li]/is';
44
+ if ( file_exists( $file ) ):
45
+ $contents = file_get_contents( $file );
46
+ if ( preg_match( $regex, $contents ) ) return TRUE;
47
  endif;
48
  return FALSE;
49
  }
50
 
51
 
52
+ function render_theme_menu( $template = 'child', $selected = NULL ) {
53
  ?>
54
  <select class="ctc-select" id="ctc_theme_<?php echo $template; ?>" name="ctc_theme_<?php echo $template; ?>" style="visibility:hidden"><?php
55
+ foreach ( $this->ctc()->themes[$template] as $slug => $theme )
56
+ echo '<option value="' . $slug . '"' . ( $slug == $selected ? ' selected' : '' ) . '>'
57
+ . esc_attr( $theme['Name'] ) . '</option>' . LF; ?>
58
  </select>
59
  <div style="display:none">
60
  <?php
61
+ foreach ( $this->ctc()->themes[$template] as $slug => $theme )
62
+ include ( $this->ctc()->pluginPath . 'includes/forms/themepreview.php' ); ?>
63
  </div>
64
  <?php
65
  }
66
 
67
+ function render_file_form( $template = 'parnt' ) {
68
  global $wp_filesystem;
69
+ if ( $theme = $this->ctc()->css->get_prop( $template ) ):
70
+ $themeroot = trailingslashit( get_theme_root() ) . trailingslashit( $theme );
71
+ $files = $this->ctc()->get_files( $theme );
72
  $counter = 0;
73
+ sort( $files );
74
  ob_start();
75
+ foreach ( $files as $file ):
76
+ $templatefile = preg_replace( '%\.php$%', '', $file );
77
+ $excludes = implode( "|", ( array ) apply_filters( 'chld_thm_cfg_template_excludes', $this->ctc()->excludes ) );
78
+ if ( 'parnt' == $template && ( preg_match( '%^(' . $excludes . ' )\w*\/%',$templatefile )
79
+ || 'functions' == basename( $templatefile ) ) ) continue;
80
  include ( $this->ctc()->pluginPath . 'includes/forms/file.php' );
81
  endforeach;
82
+ if ( 'child' == $template && ( $backups = $this->ctc()->get_files( $theme, 'backup' ) ) ):
83
+ foreach ( $backups as $backup => $label ):
84
+ $templatefile = preg_replace( '%\.css$%', '', $backup );
85
  include ( $this->ctc()->pluginPath . 'includes/forms/backup.php' );
86
  endforeach;
87
  endif;
88
  $inputs = ob_get_contents();
89
  ob_end_clean();
90
+ if ( $counter ):
91
  include ( $this->ctc()->pluginPath . 'includes/forms/fileform.php' );
92
  endif;
93
  endif;
95
 
96
  function render_image_form() {
97
 
98
+ if ( $theme = $this->ctc()->css->get_prop( 'child' ) ):
99
+ $themeuri = trailingslashit( get_theme_root_uri() ) . trailingslashit( $theme );
100
+ $files = $this->ctc()->get_files( $theme, 'img' );
101
 
102
  $counter = 0;
103
+ sort( $files );
104
  ob_start();
105
+ foreach ( $files as $file ):
106
+ $templatefile = preg_replace( '/^images\//', '', $file );
107
  include( $this->ctc()->pluginPath . 'includes/forms/image.php' );
108
  endforeach;
109
  $inputs = ob_get_contents();
110
  ob_end_clean();
111
+ if ( $counter ) include( $this->ctc()->pluginPath . 'includes/forms/images.php' );
112
  endif;
113
  }
114
 
115
  function get_theme_screenshot() {
116
 
117
+ foreach ( array_keys( $this->ctc()->imgmimes ) as $extreg ):
118
+ foreach ( explode( '|', $extreg ) as $ext ):
119
+ if ( $screenshot = $this->ctc()->css->is_file_ok( $this->ctc()->css->get_child_target( 'screenshot.' . $ext ) ) ):
120
+ $screenshot = trailingslashit( get_theme_root_uri() ) . $this->ctc()->theme_basename( '', $screenshot );
121
  return $screenshot . '?' . time();
122
  endif;
123
  endforeach;
127
 
128
  function settings_errors() {
129
 
130
+ if ( count( $this->ctc()->errors ) ):
131
  echo '<div class="error"><ul>' . LF;
132
+ foreach ( $this->ctc()->errors as $err ):
133
  echo '<li>' . $err . '</li>' . LF;
134
  endforeach;
135
  echo '</ul></div>' . LF;
136
+ elseif ( isset( $_GET['updated'] ) ):
137
  echo '<div class="updated">' . LF;
138
+ if ( 8 == $_GET['updated'] ):
139
+ echo '<p>' . __( 'Child Theme files modified successfully.', 'chld_thm_cfg' ) . '</p>' . LF;
140
  else:
141
+ $child_theme = wp_get_theme( $this->ctc()->css->get_prop( 'child' ) );
142
+ echo '<p>' . apply_filters( 'chld_thm_cfg_update_msg', sprintf( __( 'Child Theme <strong>%s</strong> has been generated successfully.
143
+ ', 'chld_thm_cfg' ), $child_theme->Name ), $this->ctc() ) . LF;
144
+ if ( ! $this->ctc()->css->get_prop( 'configtype' ) || $this->ctc()->css->get_prop( 'configtype' ) == 'theme' ):
145
+ echo '<strong>' . __( 'IMPORTANT:', 'chld_thm_cfg' ) . LF;
146
+ if ( is_multisite() && !$child_theme->is_allowed() ):
147
+ echo 'You must <a href="' . network_admin_url( '/themes.php' ) . '" title="' . __( 'Go to Themes', 'chld_thm_cfg' ) . '" class="ctc-live-preview">' . __( 'Network enable', 'chld_thm_cfg' ) . '</a> ' . __( 'your child theme.', 'chld_thm_cfg' );
148
+ else:
149
+ echo '<a href="' . admin_url( '/customize.php?theme=' . $this->ctc()->css->get_prop( 'child' ) ) . '" title="' . __( 'Live Preview', 'chld_thm_cfg' ) . '" class="ctc-live-preview">' . __( 'Test your child theme', 'chld_thm_cfg' ) . '</a> ' . __( 'before activating.', 'chld_thm_cfg' );
150
+ endif;
151
+ echo '</strong></p>' . LF;
152
+ endif;
153
+ endif;
154
  echo '</div>' . LF;
155
  endif;
156
  }
157
 
158
  function render_help_content() {
159
  global $wp_version;
160
+ if ( version_compare( $wp_version, '3.3' ) >= 0 ) {
161
 
162
  $screen = get_current_screen();
163
 
164
  // load help content via output buffer so we can use plain html for updates
165
  // then use regex to parse for help tab parameter values
166
 
167
+ $regex_sidebar = '/' . preg_quote( '<!-- BEGIN sidebar -->' ) . '(.*?)' . preg_quote( '<!-- END sidebar -->' ) . '/s';
168
+ $regex_tab = '/' . preg_quote( '<!-- BEGIN tab -->' ) . '\s*<h\d id="(.*?)">(.*?)<\/h\d>(.*?)' . preg_quote( '<!-- END tab -->' ) . '/s';
169
  ob_start();
170
  // stub for multiple languages future release
171
+ include( $this->ctc()->pluginPath . 'includes/help/help_en_US.php' );
172
  $help_raw = ob_get_contents();
173
  ob_end_clean();
174
  // parse raw html for tokens
175
+ preg_match( $regex_sidebar, $help_raw, $sidebar );
176
+ preg_match_all( $regex_tab, $help_raw, $tabs );
177
 
178
  // Add help tabs
179
+ if ( isset( $tabs[1] ) ):
180
+ while( count( $tabs[1] ) ):
181
+ $id = array_shift( $tabs[1] );
182
+ $title = array_shift( $tabs[2] );
183
+ $content = array_shift( $tabs[3] );
184
  $screen->add_help_tab( array(
185
  'id' => $id,
186
  'title' => $title,
188
  ) );
189
  endwhile;
190
  endif;
191
+ if ( isset( $sidebar[1] ) )
192
+ $screen->set_help_sidebar( $sidebar[1] );
193
 
194
  }
195
  }
196
  function lilaea_plug() {
197
+ include ( $this->ctc()->pluginPath . 'includes/forms/related.php' );
198
  }
199
  }
200
  ?>
includes/class-ctc.php CHANGED
@@ -1,18 +1,18 @@
1
  <?php
2
  // Exit if accessed directly
3
- if ( !defined('ABSPATH')) exit;
4
 
5
  /*
6
  Class: Child_Theme_Configurator
7
  Plugin URI: http://www.lilaeamedia.com/plugins/child-theme-configurator/
8
  Description: Main Controller Class
9
- Version: 1.6.2.1
10
  Author: Lilaea Media
11
  Author URI: http://www.lilaeamedia.com/
12
  Text Domain: chld_thm_cfg
13
  Domain Path: /lang
14
  License: GPLv2
15
- Copyright (C) 2014 Lilaea Media
16
  */
17
  class ChildThemeConfiguratorAdmin {
18
 
@@ -79,72 +79,72 @@ class ChildThemeConfiguratorAdmin {
79
  var $cache_updates = TRUE;
80
  var $swatch_text = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.';
81
 
82
- function __construct($file) {
83
  $this->pluginPath = trailingslashit( dirname( $file ) );
84
- $this->pluginURL = plugin_dir_url($file);
85
  $this->menuName = CHLD_THM_CFG_MENU; // backward compatability for plugins extension
86
  }
87
  function render() {
88
  $this->ui->render();
89
  }
90
  function enqueue_scripts() {
91
- wp_enqueue_style('chld-thm-cfg-admin', $this->pluginURL . 'css/chld-thm-cfg.css', array(), '1.6.2.1');
92
 
93
  // we need to use local jQuery UI Widget/Menu/Selectmenu 1.11.2 because selectmenu is not included in < 1.11.2
94
  // this will be updated in a later release to use WP Core scripts when it is widely adopted
95
- if (!wp_script_is('jquery-ui-selectmenu', 'registered')): // selectmenu.min.js
96
- wp_enqueue_script('jquery-ui-selectmenu', $this->pluginURL . 'js/selectmenu.min.js', array('jquery','jquery-ui-core','jquery-ui-position'), FALSE, TRUE);
97
  endif;
98
- wp_enqueue_script('ctc-thm-cfg-ctcgrad', $this->pluginURL . 'js/ctcgrad.min.js', array('jquery'), FALSE, TRUE);
99
- wp_enqueue_script('chld-thm-cfg-admin', $this->pluginURL . 'js/chld-thm-cfg.min.js',
100
  array(
101
  'jquery-ui-autocomplete',
102
  'jquery-ui-selectmenu',
103
  'wp-color-picker',
104
  ), FALSE, TRUE );
105
- $localize_array = apply_filters('chld_thm_cfg_localize_script', array(
106
  'ssl' => is_ssl(),
107
  'homeurl' => get_home_url(),
108
  'ajaxurl' => admin_url( 'admin-ajax.php' ),
109
  'theme_uri' => get_theme_root_uri(),
110
  'page' => CHLD_THM_CFG_MENU,
111
  'themes' => $this->themes,
112
- 'source' => apply_filters('chld_thm_cfg_source_uri', get_theme_root_uri() . '/'
113
- . $this->css->get_prop('parnt') . '/style.css', $this->css),
114
- 'target' => apply_filters('chld_thm_cfg_target_uri', get_theme_root_uri() . '/'
115
- . $this->css->get_prop('child') . '/style.css', $this->css),
116
- 'parnt' => $this->css->get_prop('parnt'),
117
- 'child' => $this->css->get_prop('child'),
118
- 'addl_css' => $this->css->get_prop('parntss'),
119
- 'imports' => $this->css->get_prop('imports'),
120
- 'rule' => $this->css->get_prop('rule'),
121
- 'sel_ndx' => $this->css->get_prop('sel_ndx'),
122
  'val_qry' => array(),
123
  'rule_val' => array(),
124
  'sel_val' => array(),
125
  'field_labels' => array(
126
- '_background_url' => __('URL/None', 'chld_thm_cfg'),
127
- '_background_origin' => __('Origin', 'chld_thm_cfg'),
128
- '_background_color1' => __('Color 1', 'chld_thm_cfg'),
129
- '_background_color2' => __('Color 2', 'chld_thm_cfg'),
130
- '_border_width' => __('Width/None', 'chld_thm_cfg'),
131
- '_border_style' => __('Style', 'chld_thm_cfg'),
132
- '_border_color' => __('Color', 'chld_thm_cfg'),
133
  ),
134
- 'load_txt' => __('Are you sure? This will replace your current settings.', 'chld_thm_cfg'),
135
  'swatch_txt' => $this->swatch_text,
136
- 'swatch_label' => __('Sample', 'chld_thm_cfg'),
137
- 'important_label' => __('<span style="font-size:10px">!</span>', 'chld_thm_cfg'),
138
- 'selector_txt' => __('Selectors', 'chld_thm_cfg'),
139
- 'close_txt' => __('Close', 'chld_thm_cfg'),
140
- 'edit_txt' => __('Edit', 'chld_thm_cfg'),
141
- 'cancel_txt' => __('Cancel', 'chld_thm_cfg'),
142
- 'rename_txt' => __('Rename', 'chld_thm_cfg'),
143
- 'css_fail_txt' => __('The stylesheet cannot be displayed.', 'chld_thm_cfg'),
144
- 'child_only_txt' => __('(Child Only)', 'chld_thm_cfg'),
145
- 'inval_theme_txt' => __('Please enter a valid Child Theme', 'chld_thm_cfg'),
146
- 'inval_name_txt' => __('Please enter a valid Child Theme name', 'chld_thm_cfg'),
147
- 'theme_exists_txt' => __('<strong>%s</strong> exists. Please enter a different Child Theme', 'chld_thm_cfg'),
148
  ) );
149
  wp_localize_script(
150
  'chld-thm-cfg-admin',
@@ -157,19 +157,19 @@ class ChildThemeConfiguratorAdmin {
157
  // allows fonts and other externals to be previewed
158
  // loads early not to conflict with admin stylesheets
159
  $regex = "/\@import *(url)? *\( *['\"]?((https?:\/\/)?(.+?))['\"]? *\).*$/";
160
- if ($imports = $this->css->get_prop('imports')):
161
  $count = 1;
162
- foreach ($imports as $import):
163
- preg_match($regex, $import, $matches);
164
- if (empty($matches[3]) && !empty($matches[4])): // relative filepath
165
  $url = get_stylesheet_directory_uri();
166
- preg_replace("#\.\./#", '', $matches[4], -1, $count);
167
- for($i = 0; $i < $count; $i++):
168
- $url = dirname($url);
169
  endfor;
170
- $import = preg_replace($regex, '@import url(' . trailingslashit($url) . $matches[4] . ')', $import);
171
  endif;
172
- wp_enqueue_style('chld-thm-cfg-admin' . ++$count, preg_replace($regex, "$2", $import));
173
  endforeach;
174
  endif;
175
  }
@@ -179,102 +179,106 @@ class ChildThemeConfiguratorAdmin {
179
  $this->load_config();
180
  do_action( 'chld_thm_cfg_forms', $this ); // hook for custom forms
181
  $this->write_config();
182
- include_once($this->pluginPath . 'includes/class-ctc-ui.php');
183
  $this->ui = new ChildThemeConfiguratorUI();
184
  $this->ui->render_help_content();
185
- add_action('admin_enqueue_scripts', array($this, 'enqueue_scripts'),999);
186
  $this->load_imports();
187
  }
188
 
189
  function get_themes() {
190
  // create cache of theme info
191
- $this->themes = array('child' => array(), 'parnt' => array());
192
- foreach (wp_get_themes() as $theme):
 
193
  $group = $theme->parent() ? 'child' : 'parnt';
 
194
  $slug = $theme->get_stylesheet();
 
195
  $this->themes[$group][$slug] = array(
196
- 'Name' => $theme->get('Name'),
197
- 'Author' => $theme->get('Author'),
198
- 'Version' => $theme->get('Version'),
199
  'screenshot' => $theme->get_screenshot(),
 
200
  );
201
  endforeach;
202
  }
203
 
204
- function validate_post($action = 'ctc_update', $noncefield = '_wpnonce') {
205
  // security: request must be post, user must have permission, referrer must be local and nonce must match
206
- return ('POST' == $_SERVER['REQUEST_METHOD']
207
- && current_user_can('edit_theme_options')
208
- && ($this->is_ajax ? check_ajax_referer( $action, $noncefield, FALSE ) : check_admin_referer($action, $noncefield, FALSE )));
209
  }
210
 
211
  function ajax_save_postdata() {
212
  $this->is_ajax = TRUE;
213
- if ($this->validate_post()):
214
  $this->verify_creds();
215
  $this->load_config();
216
  $this->css->parse_post_data();
217
  $this->css->write_css();
218
- $result = $this->css->obj_to_utf8($this->updates);
219
  $this->css->save_config();
220
  // send all updates back to browser to update cache
221
- die(json_encode($result));
222
  else:
223
- die(0);
224
  endif;
225
  }
226
 
227
  function ajax_query_css() {
228
  $this->is_ajax = TRUE;
229
- if ($this->validate_post()):
230
  $this->load_config();
231
  $regex = "/^ctc_query_/";
232
- foreach(preg_grep($regex, array_keys($_POST)) as $key):
233
- $name = preg_replace($regex, '', $key);
234
- $param[$name] = sanitize_text_field($_POST[$key]);
235
  endforeach;
236
- if (!empty($param['obj'])):
237
  $result = array(
238
  array(
239
- 'key' => isset($param['key'])?$param['key']:'',
240
  'obj' => $param['obj'],
241
- 'data' => $this->css->get_prop($param['obj'], $param),
242
  ),
243
  );
244
- die(json_encode($result));
245
  endif;
246
  endif;
247
- die(0);
248
  }
249
 
250
  function load_config() {
251
- include_once($this->pluginPath . 'includes/class-ctc-css.php');
252
  $this->css = new ChildThemeConfiguratorCSS();
253
  // if not new format or themes do not exist reinitialize
254
  if ( FALSE === $this->css->read_config()
255
- || ! $this->check_theme_exists($this->css->get_prop('child'))
256
- || ! $this->check_theme_exists($this->css->get_prop('parnt')) ):
257
  $this->css = new ChildThemeConfiguratorCSS();
258
  $parent = get_template();
259
- $this->css->set_prop('parnt', $parent);
260
  endif;
261
- if ('GET' == strtoupper($_SERVER['REQUEST_METHOD'])):
262
- if ($this->css->get_prop('child')):
263
  // get filesystem credentials if available
264
  $this->verify_creds();
265
- $stylesheet = $this->css->get_child_target('style.css');
266
  // check file permissions
267
- if (!is_writable($stylesheet) && !$this->fs):
268
- add_action('admin_notices', array($this, 'writable_notice'));
269
  endif;
270
  // check for first run, enqueue flag will be null for anyone coming from < 1.6.0
271
- if (!isset($this->css->enqueue))
272
- add_action('admin_notices', array($this, 'enqueue_notice'));
273
  endif;
274
  // check if file ownership is messed up from old version or other plugin
275
 
276
- if (fileowner($this->css->get_child_target('')) != fileowner(get_theme_root())):
277
- add_action('admin_notices', array($this, 'owner_notice'));
278
  endif;
279
  endif;
280
  }
@@ -286,36 +290,36 @@ class ChildThemeConfiguratorAdmin {
286
  */
287
  function write_config() {
288
  // make sure this is a post
289
- if ('POST' == strtoupper($_SERVER['REQUEST_METHOD'])):
290
  // see if a valid action was passed
291
- foreach ($this->actionfields as $action):
292
  if ( in_array( 'ctc_' . $action, array_keys( $_POST ) ) ):
293
  $actionfield = $action;
294
  break;
295
  endif;
296
  endforeach;
297
- if (empty($actionfield)) return FALSE;
298
 
299
  // make sure post passes security checkpoint
300
  $this->errors = array();
301
- if ($this->validate_post()):
302
  // zip export does not require filesystem access so check that first
303
  if ( 'export_child_zip' == $actionfield ):
304
  $this->export_zip();
305
  // if we get here the zip failed
306
- $this->errors[] = __('Zip file creation failed.', 'chld_thm_cfg');
307
  // all other actions require filesystem access
308
  else:
309
  // handle uploaded file before checking filesystem
310
- if ( 'theme_image_submit' == $actionfield && isset($_FILES['ctc_theme_image'])):
311
- $this->handle_file_upload('ctc_theme_image', $this->imgmimes);
312
- elseif ( 'theme_screenshot_submit' == $actionfield && isset($_FILES['ctc_theme_screenshot'])):
313
- $this->handle_file_upload('ctc_theme_screenshot', $this->imgmimes);
314
  endif;
315
  // now we need to check filesystem access
316
- $args = preg_grep("/nonce/", array_keys($_POST), PREG_GREP_INVERT);
317
- $this->verify_creds($args);
318
- if ($this->fs):
319
  $msg = FALSE;
320
  // we have filesystem access so proceed with specific actions
321
  switch( $actionfield ):
@@ -327,8 +331,8 @@ class ChildThemeConfiguratorAdmin {
327
  case 'parnt_templates_submit':
328
  // copy parent templates to child
329
  if ( isset( $_POST['ctc_file_parnt'] ) ):
330
- foreach ($_POST['ctc_file_parnt'] as $file):
331
- $this->copy_parent_file(sanitize_text_field($file));
332
  endforeach;
333
  $msg = '8&tab=file_options';
334
  endif;
@@ -337,12 +341,12 @@ class ChildThemeConfiguratorAdmin {
337
  case 'child_templates_submit':
338
  // delete child theme files
339
  if ( isset( $_POST['ctc_file_child'] ) ):
340
- if (in_array('functions', $_POST['ctc_file_child'])):
341
- $this->errors[] = __('The Functions file is required and cannot be deleted.', 'chld_thm_cfg');
342
  else:
343
- foreach ($_POST['ctc_file_child'] as $file):
344
- $this->delete_child_file(sanitize_text_field($file),
345
- (0 === strpos($file, 'style') ? 'css' : 'php'));
346
  endforeach;
347
  $msg = '8&tab=file_options';
348
  endif;
@@ -352,8 +356,8 @@ class ChildThemeConfiguratorAdmin {
352
  case 'image_submit':
353
  // delete child theme images
354
  if ( isset( $_POST['ctc_img'] ) ):
355
- foreach ($_POST['ctc_img'] as $file):
356
- $this->delete_child_file('images/' . sanitize_text_field($file), 'img');
357
  endforeach;
358
  $msg = '8&tab=file_options';
359
  endif;
@@ -361,9 +365,9 @@ class ChildThemeConfiguratorAdmin {
361
 
362
  case 'templates_writable_submit':
363
  // make specific files writable ( systems not running suExec )
364
- if ( isset($_POST['ctc_file_child'] ) ):
365
- foreach ($_POST['ctc_file_child'] as $file):
366
- $this->set_writable(sanitize_text_field($file), (0 === strpos($file, 'style') ? 'css' : 'php'));
367
  endforeach;
368
  $msg = '8&tab=file_options';
369
  endif;
@@ -383,22 +387,22 @@ class ChildThemeConfiguratorAdmin {
383
 
384
  case 'theme_image_submit':
385
  // move uploaded child theme images (now we have filesystem access)
386
- if ( isset($_POST['movefile'] ) ):
387
- $this->move_file_upload('images');
388
  $msg = '8&tab=file_options';
389
  endif;
390
  break;
391
 
392
  case 'theme_screenshot_submit':
393
  // move uploaded child theme screenshot (now we have filesystem access)
394
- if ( isset($_POST['movefile'] ) ):
395
  // remove old screenshot
396
- foreach(array_keys($this->imgmimes) as $extreg):
397
- foreach (explode('|', $extreg) as $ext):
398
- $this->delete_child_file('screenshot', $ext);
399
  endforeach;
400
  endforeach;
401
- $this->move_file_upload('');
402
  $msg = '8&tab=file_options';
403
  endif;
404
  break;
@@ -409,22 +413,22 @@ class ChildThemeConfiguratorAdmin {
409
  endswitch;
410
  endif; // end filesystem condition
411
  endif; // end zip export condition
412
- if (empty($this->errors) && empty($this->fs_prompt))
413
  // no errors so we redirect with confirmation message
414
- $this->update_redirect($msg);
415
  // otherwise fail gracefully
416
  $msg = NULL;
417
  return FALSE;
418
  endif; // end post validation condition
419
  // if you end up here you are not welcome
420
  $msg = NULL;
421
- $this->errors[] = __('You do not have permission to configure child themes.', 'chld_thm_cfg');
422
  endif; // end request method condition
423
  return FALSE;
424
  }
425
 
426
- function check_theme_exists($theme) {
427
- return in_array($theme, array_keys(wp_get_themes()));
428
  }
429
 
430
  /***
@@ -433,126 +437,126 @@ class ChildThemeConfiguratorAdmin {
433
  function setup_child_theme() {
434
 
435
  // sanitize and extract config fields into local vars
436
- foreach ($this->configfields as $configfield):
437
- $varparts = explode('_', $configfield);
438
- $varname = end($varparts);
439
- ${$varname} = empty($_POST['ctc_' . $configfield]) ? '' : sanitize_text_field($_POST['ctc_' . $configfield]);
440
  endforeach;
441
 
442
  // check that all requirements are met
443
- if ($parnt):
444
- if (! $this->check_theme_exists($parnt)):
445
- $this->errors[] = sprintf(__('%s does not exist. Please select a valid Parent Theme', 'chld_thm_cfg'), $parnt);
446
  endif;
447
  else:
448
- $this->errors[] = __('Please select a valid Parent Theme', 'chld_thm_cfg');
449
  endif;
450
  // if this is a shiny brand new child theme certain rules apply
451
- if ('new' == $type):
452
- if (empty($template) && empty($name)):
453
- $this->errors[] = __('Please enter a valid Child Theme template name', 'chld_thm_cfg');
454
  else:
455
  $configtype = 'theme'; // no custom stylesheets until style.css exists!
456
- $child = strtolower(preg_replace("%[^\w\-]%", '', empty($template) ? $name : $template));
457
- if ($this->check_theme_exists($child)):
458
- $this->errors[] = sprintf(__('<strong>%s</strong> exists. Please enter a different Child Theme template name', 'chld_thm_cfg'), $child);
459
  else:
460
- add_action( 'chld_thm_cfg_addl_files', array( &$this, 'add_base_files' ), 10, 2);
461
- add_action( 'chld_thm_cfg_addl_files', array( &$this, 'copy_screenshot' ), 10, 2);
462
- add_action( 'chld_thm_cfg_addl_files', array( &$this, 'enqueue_parent_css' ), 15, 2);
463
  endif;
464
  endif;
465
  // check if we have additional files from a plugin extension. if so, we have to override
466
  // function to support wp_filesystem requirements
467
- elseif (empty($configtype) || 'theme' == $configtype):
468
  // no configtype means this is either from this plugin or a plugin after 1.5.0
469
- add_action( 'chld_thm_cfg_addl_files', array( &$this, 'add_base_files' ), 10, 2);
470
- add_action( 'chld_thm_cfg_addl_files', array( &$this, 'copy_screenshot' ), 10, 2);
471
- add_action( 'chld_thm_cfg_addl_files', array( &$this, 'enqueue_parent_css' ), 15, 2);
472
- elseif(has_action( 'chld_thm_cfg_addl_files' )):
473
  // action exists so we have to hijack it to use new filesystem checks
474
  remove_all_actions( 'chld_thm_cfg_addl_files' );
475
  // back compat for plugins extension
476
- add_action('chld_thm_cfg_addl_files', array( &$this, 'write_addl_files' ), 10, 2);
477
  endif;
478
 
479
- if (empty($name)):
480
- $name = ucfirst($child);
481
  endif;
482
 
483
- if (empty($child)):
484
- $this->errors[] = __('Please enter a valid Child Theme directory', 'chld_thm_cfg');
485
  endif;
486
 
487
- if (FALSE === $this->verify_child_dir($child)):
488
- $this->errors[] = __('Your theme directories are not writable.', 'chld_thm_cfg');
489
- add_action('admin_notices', array($this, 'writable_notice'));
490
  endif;
491
 
492
  // if no errors so far, we are good to create child theme
493
- if (empty($this->errors)):
494
  $this->css = new ChildThemeConfiguratorCSS();
495
- $this->css->set_prop('parnt', $parnt);
496
- $this->css->set_prop('child', $child);
497
- $this->css->set_prop('child_name', $name);
498
- $this->css->set_prop('child_author', $author);
499
- $this->css->set_prop('child_version', strlen($version) ? $version : '1.0');
500
- $this->css->set_prop('configtype', $configtype);
501
 
502
  // hook for add'l plugin files and subdirectories
503
- do_action('chld_thm_cfg_addl_files', $this);
504
 
505
  // always parse parent stylesheet
506
- $this->css->parse_css_file('parnt');
507
 
508
- // parse child stylesheet, backup or skip (to reset)
509
- $this->css->parse_css_file('child', $revert);
510
 
511
  // parse additional stylesheets
512
- if (isset($_POST['ctc_additional_css']) && is_array($_POST['ctc_additional_css'])):
513
  $this->css->parentss = array();
514
- foreach ($_POST['ctc_additional_css'] as $file):
515
- $this->css->parse_css_file('parnt', $file);
516
  $this->css->parntss[] = $file;
517
  endforeach;
518
  endif;
519
 
520
  // try to write new stylsheet. If it fails send alert.
521
- if (FALSE === $this->css->write_css(isset($_POST['ctc_backup']))):
522
- $this->errors[] = __('Your stylesheet is not writable.', 'chld_thm_cfg');
523
- add_action('admin_notices', array($this, 'writable_notice'));
524
  return FALSE;
525
  endif;
526
 
527
  // copy parent theme mods option set
528
- if (isset($_POST['ctc_parent_mods'])):
529
  // we can copy settings from parent to child even if neither is currently active
530
  // so we need cases for active parent, active child or neither
531
 
532
  // get active theme
533
  $active_theme = get_stylesheet();
534
  // create temp array from parent settings
535
- $child_mods = get_option('theme_mods_' . $parnt);
536
- if ($active_theme == $parnt):
537
  // if parent theme is active, get widgets from active sidebars_widgets array
538
  $child_widgets = retrieve_widgets();
539
  else:
540
  // otherwise get widgets from parent theme mods
541
  $child_widgets = $child_mods['sidebars_widgets']['data'];
542
  endif;
543
- if ($active_theme == $child):
544
  // if child theme is active, remove widgets from temp array
545
- unset($child_mods['sidebars_widgets']);
546
  // copy temp array to child mods
547
- update_option('theme_mods_' . $child, $child_mods);
548
  // copy widgets to active sidebars_widgets array
549
- wp_set_sidebars_widgets($child_widgets);
550
  else:
551
  // otherwise copy widgets to temp array with time stamp
552
  $child_mods['sidebars_widgets']['data'] = $child_widgets;
553
  $child_mods['sidebars_widgets']['time'] = time();
554
  // copy temp array to child theme mods
555
- update_option('theme_mods_' . $child, $child_mods);
556
  endif;
557
  endif;
558
 
@@ -560,75 +564,80 @@ class ChildThemeConfiguratorAdmin {
560
  $this->css->save_config();
561
 
562
  // hook for add'l plugin options
563
- do_action('chld_thm_cfg_addl_options', $this); // hook for add'l plugin options
564
 
565
  // return message id 1, which says new child theme created successfull;
566
- return 1; //isset($_POST['ctc_scan_subdirs']) ? '9&tab=import_options' : 1;
567
  endif;
568
  return FALSE;
569
  }
570
  /*
571
  * TODO: this is a stub for future use
572
  */
573
- function sanitize_options($input) {
574
  return $input;
575
  }
576
 
577
- function update_redirect($msg = 1) {
578
- if (empty($this->is_ajax)):
579
- wp_safe_redirect(admin_url('tools.php?page=' . CHLD_THM_CFG_MENU . '&updated=' . $msg));
 
 
 
 
 
580
  die();
581
  endif;
582
  }
583
 
584
- function verify_child_dir($path) {
585
- if (!$this->fs) return FALSE; // return if no filesystem access
586
  global $wp_filesystem;
587
- $themedir = $wp_filesystem->find_folder(get_theme_root());
588
- if (! $wp_filesystem->is_writable($themedir)) return FALSE;
589
- $childparts = explode('/', $this->normalize_path($path));
590
- while (count($childparts)):
591
- $subdir = array_shift($childparts);
592
- if (empty($subdir)) continue;
593
- $themedir = trailingslashit($themedir) . $subdir;
594
- if (! $wp_filesystem->is_dir($themedir)):
595
- if (! $wp_filesystem->mkdir($themedir, FS_CHMOD_DIR)):
596
  return FALSE;
597
  endif;
598
- elseif (! $wp_filesystem->is_writable($themedir)):
599
  return FALSE;
600
  endif;
601
  endwhile;
602
  return TRUE;
603
  }
604
 
605
- function add_base_files($obj){
606
  // add functions.php file
607
  $contents = "<?php
608
  // Exit if accessed directly
609
- if ( !defined('ABSPATH')) exit;
610
  ";
611
- if (FALSE === $this->write_child_file('functions.php', $contents)
612
- || FALSE === $this->write_child_file('style.css', $this->css->get_css_header())) return FALSE;
613
  }
614
 
615
  function enqueue_parent_code(){
616
- return explode("\n", apply_filters('chld_thm_cfg_enqueue_parent', "
617
- if (!function_exists('chld_thm_cfg_parent_css')):
618
  function chld_thm_cfg_parent_css() {
619
- wp_enqueue_style('chld_thm_cfg_parent', get_template_directory_uri() . '/style.css');
620
  }
621
  endif;
622
- add_action('wp_enqueue_scripts', 'chld_thm_cfg_parent_css');
623
- "));
624
  }
625
 
626
- function enqueue_parent_css($obj) {
627
- if (isset($_POST['ctc_parent_enqueue']))
628
- $this->css->enqueue = sanitize_text_field($_POST['ctc_parent_enqueue']);
629
  $marker = 'ENQUEUE PARENT ACTION';
630
  $insertion = 'enqueue' == $this->css->enqueue ? $this->enqueue_parent_code() : array();
631
- if ($filename = $this->css->is_file_ok($this->css->get_child_target('functions.php'), 'write')):
632
  $this->insert_with_markers( $filename, $marker, $insertion );
633
  endif;
634
  }
@@ -638,20 +647,20 @@ add_action('wp_enqueue_scripts', 'chld_thm_cfg_parent_css');
638
  * but it does not use wp_filesystem API!!!???
639
  */
640
  function insert_with_markers( $filename, $marker, $insertion ) {
641
- if (!$this->fs) return FALSE; // return if no filesystem access
642
  global $wp_filesystem;
643
- if(!$wp_filesystem->exists($this->fspath($filename))):
644
  $markerdata = FALSE;
645
  else:
646
  // get_contents_array returns extra linefeeds so just split it ourself
647
- $markerdata = explode("\n", $wp_filesystem->get_contents( $this->fspath( $filename ) ) );
648
  endif;
649
  $newfile = '';
650
  $foundit = false;
651
  if ( $markerdata ) {
652
  $state = true;
653
  foreach ( $markerdata as $n => $markerline ) {
654
- if (strpos($markerline, '// BEGIN ' . $marker) !== false)
655
  $state = false;
656
  if ( $state ):
657
  if ( $n + 1 < count( $markerdata ) )
@@ -659,9 +668,9 @@ add_action('wp_enqueue_scripts', 'chld_thm_cfg_parent_css');
659
  else
660
  $newfile .= "{$markerline}";
661
  endif;
662
- if (strpos($markerline, '// END ' . $marker) !== false):
663
  $newfile .= "// BEGIN {$marker}\n";
664
- if ( is_array( $insertion ))
665
  foreach ( $insertion as $insertline )
666
  $newfile .= "{$insertline}\n";
667
  $newfile .= "// END {$marker}\n";
@@ -670,264 +679,264 @@ add_action('wp_enqueue_scripts', 'chld_thm_cfg_parent_css');
670
  endif;
671
  }
672
  }
673
- if (!$foundit) {
674
  $newfile .= "\n// BEGIN {$marker}\n";
675
  foreach ( $insertion as $insertline )
676
  $newfile .= "{$insertline}\n";
677
  $newfile .= "// END {$marker}\n";
678
  }
679
- if (FALSE === $wp_filesystem->put_contents($this->fspath($filename), $newfile)) return FALSE;
680
  }
681
 
682
- function write_child_file($file, $contents) {
683
- if (!$this->fs) return FALSE; // return if no filesystem access
684
  global $wp_filesystem;
685
- $file = $this->fspath($this->css->is_file_ok($this->css->get_child_target($file), 'write'));
686
- if ($file && !$wp_filesystem->exists($file)):
687
- if (FALSE === $wp_filesystem->put_contents($file, $contents)) return FALSE;
688
  endif;
689
  }
690
 
691
- function copy_screenshot($obj) {
692
  // always copy screenshot
693
- $this->copy_parent_file('screenshot');
694
  }
695
 
696
- function copy_parent_file($file, $ext = 'php') {
697
- if (!$this->fs) return FALSE; // return if no filesystem access
698
  global $wp_filesystem;
699
  $parent_file = NULL;
700
- if ('screenshot' == $file):
701
- foreach (array_keys($this->imgmimes) as $extreg):
702
- foreach(explode('|', $extreg) as $ext):
703
- if ($parent_file = $this->css->is_file_ok($this->css->get_parent_source('screenshot.' . $ext))) break;
704
  endforeach;
705
- if ($parent_file):
706
- $parent_file = $this->fspath($parent_file);
707
  break;
708
  endif;
709
  endforeach;
710
  else:
711
- $parent_file = $this->fspath($this->css->is_file_ok($this->css->get_parent_source($file . '.' . $ext)));
712
  endif;
713
- // get child theme + file + ext (passing empty string and full child path to theme_basename )
714
- $child_file = $this->css->get_child_target($file . '.' . $ext);
715
  // return true if file already exists
716
- if ($wp_filesystem->exists($this->fspath($child_file))) return TRUE;
717
- $child_dir = dirname($this->theme_basename('', $child_file));
718
- if ($parent_file // sanity check
719
  && $child_file // sanity check
720
- && $this->verify_child_dir($child_dir) //create child subdir if necessary
721
- && $wp_filesystem->copy($parent_file, $this->fspath($child_file), FS_CHMOD_FILE)) return TRUE;
722
- $this->errors[] = __('Could not copy file.', 'chld_thm_cfg');
723
  }
724
 
725
- function delete_child_file($file, $ext = 'php') {
726
- if (!$this->fs) return FALSE; // return if no filesystem access
727
  global $wp_filesystem;
728
  // verify file is in child theme and exists before removing.
729
- $file = ('img' == $ext ? $file : $file . '.' . $ext);
730
- $child_file = $this->fspath($this->css->is_file_ok($this->css->get_child_target($file), 'write'));
731
- if ($wp_filesystem->exists($child_file)):
732
- if (!$wp_filesystem->delete($child_file)) return FALSE;
733
  endif;
734
  }
735
 
736
- function get_files($theme, $type = 'template') {
737
- if (!isset($this->files[$theme])):
738
  $this->files[$theme] = array();
739
- $imgext = '(' . implode('|', array_keys($this->imgmimes)) . ')';
740
- foreach ($this->css->recurse_directory(
741
- trailingslashit(get_theme_root()) . $theme, '', TRUE) as $file):
742
- $file = $this->theme_basename($theme, $file);
743
- if (preg_match("/^style\-(\d+)\.css$/", $file, $matches)):
744
- $date = date_i18n('D, j M Y g:i A', strtotime($matches[1]));
745
  $this->files[$theme]['backup'][$file] = $date;
746
- elseif (preg_match("/\.php$/", $file)):
747
  $this->files[$theme]['template'][] = $file;
748
- elseif (preg_match("/\.css$/", $file) && 'style.css' != $file):
749
  $this->files[$theme]['stylesheet'][] = $file;
750
- elseif (preg_match("/^images\/.+?\." . $imgext . "$/", $file)):
751
  $this->files[$theme]['img'][] = $file;
752
  endif;
753
  endforeach;
754
  endif;
755
- return isset($this->files[$theme][$type]) ? $this->files[$theme][$type] : array();
756
  }
757
 
758
- function theme_basename($theme, $file) {
759
  // if no theme passed, returns theme + file
760
- $themedir = trailingslashit(get_theme_root()) . ('' == $theme ? '' : trailingslashit($theme));
761
- return preg_replace('%^' . preg_quote($themedir) . '%', '', $file);
762
  }
763
 
764
- function uploads_basename($file) {
765
- $file = $this->normalize_path($file);
766
  $uplarr = wp_upload_dir();
767
- $upldir = trailingslashit($uplarr['basedir']);
768
- return preg_replace('%^' . preg_quote($upldir) . '%', '', $file);
769
  }
770
 
771
- function uploads_fullpath($file) {
772
- $file = $this->normalize_path($file);
773
  $uplarr = wp_upload_dir();
774
- $upldir = trailingslashit($uplarr['basedir']);
775
  return $upldir . $file;
776
  }
777
 
778
  function serialize_postarrays() {
779
- foreach ($this->postarrays as $field)
780
- if (isset($_POST[$field]) && is_array($_POST[$field]))
781
- $_POST[$field] = implode("%%", $_POST[$field]);
782
  }
783
 
784
  function unserialize_postarrays() {
785
- foreach ($this->postarrays as $field)
786
- if (isset($_POST[$field]) && !is_array($_POST[$field]))
787
- $_POST[$field] = explode("%%", $_POST[$field]);
788
  }
789
 
790
- function set_writable($file = NULL) {
791
 
792
- $file = isset($file) ? $this->css->get_child_target($file . '.php') : apply_filters('chld_thm_cfg_target', $this->css->get_child_target(), $this->css);
793
- if ($this->fs): // filesystem access
794
  global $wp_filesystem;
795
- if ($file && $wp_filesystem->chmod($this->fspath($file), 0666)) return;
796
  endif;
797
- $this->errors[] = __('Could not set write permissions.', 'chld_thm_cfg');
798
- add_action('admin_notices', array($this, 'writable_notice'));
799
  return FALSE;
800
  }
801
 
802
  function unset_writable() {
803
- if (!$this->fs) return FALSE; // return if no filesystem access
804
  global $wp_filesystem;
805
- $dir = untrailingslashit($this->css->get_child_target(''));
806
- $child = $this->theme_basename('', $dir);
807
- $newchild = untrailingslashit($child) . '-new';
808
- $themedir = trailingslashit(get_theme_root());
809
- $fsthemedir = $this->fspath($themedir);
810
  // is child theme owned by user?
811
- if (fileowner($dir) == fileowner(ABSPATH)):
812
  $copy = FALSE;
813
- $wp_filesystem->chmod($dir);
814
- // recursive chmod (as user)
815
  // WP_Filesystem RECURSIVE CHMOD IS FLAWED! IT SETS ALL CHILDREN TO PERM OF OUTERMOST DIR
816
- //if ($wp_filesystem->chmod($this->fspath($dir), FALSE, TRUE)):
817
  //endif;
818
  else:
819
  $copy = TRUE;
820
  endif;
821
- // n -> copy entire folder (as user)
822
- $files = $this->css->recurse_directory($dir, NULL, TRUE);
823
  $errors = array();
824
- foreach ($files as $file):
825
- $childfile = $this->theme_basename($child, $this->normalize_path($file));
826
- $newfile = trailingslashit($newchild) . $childfile;
827
- $childpath = $fsthemedir . trailingslashit($child) . $childfile;
828
  $newpath = $fsthemedir . $newfile;
829
- if ($copy):
830
- if ($this->verify_child_dir(is_dir($file)?$newfile:dirname($newfile))):
831
- if (is_file($file) && !$wp_filesystem->copy($childpath, $newpath)):
832
  $errors[] = 'could not copy ' . $newpath;
833
  endif;
834
  else:
835
  $errors[] = 'invalid dir: ' . $newfile;
836
  endif;
837
  else:
838
- $wp_filesystem->chmod($this->fspath($file));
839
  endif;
840
  endforeach;
841
- if ($copy):
842
- // verify copy (as webserver)
843
- $newfiles = $this->css->recurse_directory(trailingslashit($themedir) . $newchild, NULL, TRUE);
844
  $deleteddirs = $deletedfiles = 0;
845
- if (count($newfiles) == count($files)):
846
- // rename old (as webserver)
847
- if (!$wp_filesystem->exists(trailingslashit($fsthemedir) . $child . '-old'))
848
- $wp_filesystem->move(trailingslashit($fsthemedir) . $child, trailingslashit($fsthemedir) . $child . '-old');
849
- // rename new (as user)
850
- if (!$wp_filesystem->exists(trailingslashit($fsthemedir) . $child))
851
- $wp_filesystem->move(trailingslashit($fsthemedir) . $newchild, trailingslashit($fsthemedir) . $child);
852
- // remove old files (as webserver)
853
- $oldfiles = $this->css->recurse_directory(trailingslashit($themedir) . $child . '-old', NULL, TRUE);
854
- array_unshift($oldfiles, trailingslashit($themedir) . $child . '-old');
855
- foreach (array_reverse($oldfiles) as $file):
856
- if ($wp_filesystem->delete($this->fspath($file)) || (is_dir($file) && @rmdir($file)) || (is_file($file) && @unlink($file))):
857
  $deletedfiles++;
858
  endif;
859
  endforeach;
860
- if ($deletedfiles != count($oldfiles)):
861
- $errors[] = 'deleted: ' . $deletedfiles . ' != ' . count($oldfiles) . ' files';
862
  endif;
863
  else:
864
  $errors[] = 'newfiles != files';
865
  endif;
866
  endif;
867
- if (count($errors)):
868
- $this->errors[] = __('There were errors while resetting permissions.', 'chld_thm_cfg') ;
869
- add_action('admin_notices', array($this, 'writable_notice'));
870
  endif;
871
  }
872
 
873
- function handle_file_upload($field, $childdir = NULL, $mimes = NULL){
874
  $uploadedfile = $_FILES[$field];
875
  $upload_overrides = array(
876
  'test_form' => FALSE,
877
- 'mimes' => (is_array($mimes) ? $mimes : NULL)
878
  );
879
  if ( ! function_exists( 'wp_handle_upload' ) ) require_once( ABSPATH . 'wp-admin/includes/file.php' );
880
  $movefile = wp_handle_upload( $uploadedfile, $upload_overrides );
881
- if (isset($movefile['error'])):
882
  $this->errors[] = $movefile['error'];
883
  return FALSE;
884
  endif;
885
- $_POST['movefile'] = $this->uploads_basename($movefile['file']);
886
  }
887
 
888
- function move_file_upload($subdir = 'images') {
889
- if (!$this->fs) return FALSE; // return if no filesystem access
890
  global $wp_filesystem;
891
- $source_file = sanitize_text_field($_POST['movefile']);
892
- $target_file = ('' == $subdir ?
893
- preg_replace("%^.+(\.\w+)$%", "screenshot$1", basename($source_file)) :
894
- trailingslashit($subdir) . basename($source_file));
895
- if (FALSE !== $this->verify_child_dir(trailingslashit($this->css->get_prop('child')) . $subdir)):
896
- $source_path = $this->fspath($this->uploads_fullpath($source_file));
897
- if ($target_path = $this->css->is_file_ok($this->css->get_child_target($target_file), 'write')):
898
- $target_path = $this->fspath($target_path);
899
- if ($wp_filesystem->exists($source_path)):
900
- if ($wp_filesystem->move($source_path, $target_path)) return TRUE;
901
  endif;
902
  endif;
903
  endif;
904
 
905
- $this->errors[] = __('Could not upload file.', 'chld_thm_cfg');
906
  }
907
 
908
  function export_zip() {
909
- if (($child = $this->css->get_prop('child'))
910
- && ($dir = $this->css->is_file_ok(dirname($this->css->get_child_target()), 'search'))
911
- && ($version = preg_replace("%[^\w\.\-]%", '', $this->css->get_prop('version')))):
912
  // use php system upload dir to store temp files so that we can use pclzip
913
- $tmpdir = ini_get('upload_tmp_dir') ? ini_get('upload_tmp_dir') : sys_get_temp_dir();
914
- $file = trailingslashit($tmpdir) . $child . '-' . $version . '.zip';
915
  mbstring_binary_safe_encoding();
916
 
917
- require_once(ABSPATH . 'wp-admin/includes/class-pclzip.php');
918
 
919
- $archive = new PclZip($file);
920
- if ($archive->create($dir, PCLZIP_OPT_REMOVE_PATH, dirname($dir)) == 0) return FALSE;
921
  reset_mbstring_encoding();
922
  header( 'Content-Description: File Transfer' );
923
  header( 'Content-Type: application/octet-stream' );
924
- header( 'Content-Length: ' . filesize($file) );
925
- header( 'Content-Disposition: attachment; filename=' . basename($file) );
926
- header( 'Expires: 0');
927
- header( 'Cache-Control: must-revalidate');
928
- header( 'Pragma: public');
929
- readfile($file);
930
- unlink($file);
931
  die();
932
  endif;
933
  }
@@ -935,22 +944,24 @@ add_action('wp_enqueue_scripts', 'chld_thm_cfg_parent_css');
935
  /*
936
  *
937
  */
938
- function verify_creds($args = array()) {
939
  $this->fs_prompt = $this->fs = FALSE;
940
  //fs prompt does not support arrays as post data - serialize arrays
941
  $this->serialize_postarrays();
942
  // generate callback url
943
- $url = wp_nonce_url('tools.php?page=' . CHLD_THM_CFG_MENU, 'ctc_update', '_wpnonce');
 
 
944
  // buffer output so we can process prior to http header
945
  ob_start();
946
- if ($creds = request_filesystem_credentials($url, '', FALSE, FALSE, $args)):
947
  // check filesystem permission if direct or ftp creds exist
948
- if ( WP_Filesystem($creds) )
949
  // login ok
950
  $this->fs = TRUE;
951
  else
952
  // incorrect credentials, get form with error flag
953
- $creds = request_filesystem_credentials($url, '', TRUE, FALSE, $args);
954
  else:
955
  // no credentials, initialize unpriveledged filesystem object
956
  WP_Filesystem();
@@ -966,33 +977,33 @@ add_action('wp_enqueue_scripts', 'chld_thm_cfg_parent_css');
966
  /*
967
  * convert 'direct' filepath into wp_filesystem filepath
968
  */
969
- function fspath($file){
970
- if (! $this->fs) return FALSE; // return if no filesystem access
971
  global $wp_filesystem;
972
- if (is_dir($file)):
973
  $dir = $file;
974
  $base = '';
975
  else:
976
- $dir = dirname($file);
977
- $base = basename($file);
978
  endif;
979
- $fsdir = $wp_filesystem->find_folder($dir);
980
- return trailingslashit($fsdir) . $base;
981
  }
982
 
983
  function writable_notice() {
984
  ?> <div class="update-nag">
985
- <p><?php _e( 'Child Theme Configurator is unable to write to the stylesheet. This can be resolved using one of the following options:<ol>', 'chld_thm_cfg');
986
- if (isset($_SERVER['SERVER_SOFTWARE']) && preg_match('%unix%i',$_SERVER['SERVER_SOFTWARE'])):
987
- _e('<li>Temporarily make the stylesheet writable by clicking the button below. You should change this back when you are finished editing for security by clicking "Make read-only" under the "Files" tab.</li>', 'chld_thm_cfg');
988
  ?><form action="?page=<?php echo CHLD_THM_CFG_MENU; ?>" method="post"><?php wp_nonce_field( 'ctc_update' ); ?>
989
- <input name="ctc_set_writable" class="button" type="submit" value="<?php _e('Temporarily make stylesheet writable', 'chld_thm_cfg'); ?>"/></form><?php endif;
990
- _e('<li><a target="_blank" href="http://codex.wordpress.org/Editing_wp-config.php#WordPress_Upgrade_Constants" title="Editin wp-config.php">Add your FTP/SSH credentials to the WordPress config file</a>.</li>', 'chld_thm_cfg');
991
- if (isset($_SERVER['SERVER_SOFTWARE']) && preg_match('%iis%i',$_SERVER['SERVER_SOFTWARE']))
992
- _e('<li><a target="_blank" href="http://technet.microsoft.com/en-us/library/cc771170" title="Setting Application Pool Identity">Assign WordPress to an application pool that has write permissions</a> (Windows IIS systems).</li>', 'chld_thm_cfg');
993
- _e('<li><a target="_blank" href="http://codex.wordpress.org/Changing_File_Permissions" title="Changing File Permissions">Set the stylesheet write permissions on the server manually</a> (not recommended).</li>', 'chld_thm_cfg');
994
- if (isset($_SERVER['SERVER_SOFTWARE']) && preg_match('%unix%i',$_SERVER['SERVER_SOFTWARE']))
995
- _e('<li>Run PHP under Apache with suEXEC (contact your web host).</li>', 'chld_thm_cfg') ?>
996
  </ol></p>
997
  </div>
998
  <?php
@@ -1000,45 +1011,45 @@ add_action('wp_enqueue_scripts', 'chld_thm_cfg_parent_css');
1000
  function owner_notice() {
1001
  ?>
1002
  <div class="update-nag">
1003
- <p><?php _e( 'This Child Theme is not owned by your website account. It may have been created by a prior version of this plugin or by another program. Moving forward, it must be owned by your website account to make changes. Child Theme Configurator will attempt to correct this when you click the button below.', 'chld_thm_cfg') ?></p>
1004
  <form action="?page=<?php echo CHLD_THM_CFG_MENU; ?>" method="post"><?php wp_nonce_field( 'ctc_update' ); ?>
1005
- <input name="ctc_reset_permission" class="button" type="submit" value="<?php _e('Correct Child Theme Permissions', 'chld_thm_cfg'); ?>"/></form> </div>
1006
  <?php
1007
  }
1008
 
1009
  function enqueue_notice() {
1010
  ?>
1011
  <div class="update-nag">
1012
- <p><?php _e( 'Child Theme Configurator has changed the way it handles the parent stylesheet. Please set your preferences below and click "Generate Child Theme Files" to update your configuration.', 'chld_thm_cfg') ?></p>
1013
  </div>
1014
  <?php
1015
  }
1016
 
1017
  // back compatibility function for plugins extension
1018
- function write_addl_files($chld_thm_cfg) {
1019
  global $chld_thm_cfg_plugins;
1020
- if (!is_object($chld_thm_cfg_plugins) || !$chld_thm_cfg->fs) return FALSE;
1021
- $configtype = $chld_thm_cfg->css->get_prop('configtype');
1022
- if ('theme' == $configtype || !($def = $chld_thm_cfg_plugins->defs->get_def($configtype))) return FALSE;
1023
- $child = trailingslashit($chld_thm_cfg->css->get_prop('child'));
1024
- if (isset($def['addl']) && is_array($def['addl']) && count($def['addl'])):
1025
- foreach ($def['addl'] as $path => $type):
1026
 
1027
  // sanitize the crap out of the target data -- it will be used to create paths
1028
- $path = $this->normalize_path(preg_replace("%[^\w\\//\-]%", '', sanitize_text_field($child . $path)));
1029
- if (('dir' == $type && FALSE === $chld_thm_cfg->verify_child_dir($path))
1030
- || ('dir' != $type && FALSE === $chld_thm_cfg->write_child_file($path, ''))):
1031
  $chld_thm_cfg->errors[] =
1032
- __('Your theme directories are not writable.', 'chld_thm_cfg_plugins');
1033
  endif;
1034
  endforeach;
1035
  endif;
1036
  // write main def file
1037
- if (isset($def['target'])):
1038
- $path = $this->normalize_path(preg_replace("%[^\w\\//\-\.]%", '', sanitize_text_field($def['target']))); //$child .
1039
- if (FALSE === $chld_thm_cfg->write_child_file($path, '')):
1040
  $chld_thm_cfg->errors[] =
1041
- __('Your stylesheet is not writable.', 'chld_thm_cfg_plugins');
1042
  return FALSE;
1043
  endif;
1044
  endif;
1
  <?php
2
  // Exit if accessed directly
3
+ if ( !defined( 'ABSPATH' ) ) exit;
4
 
5
  /*
6
  Class: Child_Theme_Configurator
7
  Plugin URI: http://www.lilaeamedia.com/plugins/child-theme-configurator/
8
  Description: Main Controller Class
9
+ Version: 1.6.3
10
  Author: Lilaea Media
11
  Author URI: http://www.lilaeamedia.com/
12
  Text Domain: chld_thm_cfg
13
  Domain Path: /lang
14
  License: GPLv2
15
+ Copyright (C) 2014-2015 Lilaea Media
16
  */
17
  class ChildThemeConfiguratorAdmin {
18
 
79
  var $cache_updates = TRUE;
80
  var $swatch_text = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.';
81
 
82
+ function __construct( $file ) {
83
  $this->pluginPath = trailingslashit( dirname( $file ) );
84
+ $this->pluginURL = plugin_dir_url( $file );
85
  $this->menuName = CHLD_THM_CFG_MENU; // backward compatability for plugins extension
86
  }
87
  function render() {
88
  $this->ui->render();
89
  }
90
  function enqueue_scripts() {
91
+ wp_enqueue_style( 'chld-thm-cfg-admin', $this->pluginURL . 'css/chld-thm-cfg.css', array(), '1.6.3' );
92
 
93
  // we need to use local jQuery UI Widget/Menu/Selectmenu 1.11.2 because selectmenu is not included in < 1.11.2
94
  // this will be updated in a later release to use WP Core scripts when it is widely adopted
95
+ if ( !wp_script_is( 'jquery-ui-selectmenu', 'registered' ) ): // selectmenu.min.js
96
+ wp_enqueue_script( 'jquery-ui-selectmenu', $this->pluginURL . 'js/selectmenu.min.js', array( 'jquery','jquery-ui-core','jquery-ui-position' ), FALSE, TRUE );
97
  endif;
98
+ wp_enqueue_script( 'ctc-thm-cfg-ctcgrad', $this->pluginURL . 'js/ctcgrad.min.js', array( 'jquery' ), FALSE, TRUE );
99
+ wp_enqueue_script( 'chld-thm-cfg-admin', $this->pluginURL . 'js/chld-thm-cfg.min.js',
100
  array(
101
  'jquery-ui-autocomplete',
102
  'jquery-ui-selectmenu',
103
  'wp-color-picker',
104
  ), FALSE, TRUE );
105
+ $localize_array = apply_filters( 'chld_thm_cfg_localize_script', array(
106
  'ssl' => is_ssl(),
107
  'homeurl' => get_home_url(),
108
  'ajaxurl' => admin_url( 'admin-ajax.php' ),
109
  'theme_uri' => get_theme_root_uri(),
110
  'page' => CHLD_THM_CFG_MENU,
111
  'themes' => $this->themes,
112
+ 'source' => apply_filters( 'chld_thm_cfg_source_uri', get_theme_root_uri() . '/'
113
+ . $this->css->get_prop( 'parnt' ) . '/style.css', $this->css ),
114
+ 'target' => apply_filters( 'chld_thm_cfg_target_uri', get_theme_root_uri() . '/'
115
+ . $this->css->get_prop( 'child' ) . '/style.css', $this->css ),
116
+ 'parnt' => $this->css->get_prop( 'parnt' ),
117
+ 'child' => $this->css->get_prop( 'child' ),
118
+ 'addl_css' => $this->css->get_prop( 'parntss' ),
119
+ 'imports' => $this->css->get_prop( 'imports' ),
120
+ 'rule' => $this->css->get_prop( 'rule' ),
121
+ 'sel_ndx' => $this->css->get_prop( 'sel_ndx' ),
122
  'val_qry' => array(),
123
  'rule_val' => array(),
124
  'sel_val' => array(),
125
  'field_labels' => array(
126
+ '_background_url' => __( 'URL/None', 'chld_thm_cfg' ),
127
+ '_background_origin' => __( 'Origin', 'chld_thm_cfg' ),
128
+ '_background_color1' => __( 'Color 1', 'chld_thm_cfg' ),
129
+ '_background_color2' => __( 'Color 2', 'chld_thm_cfg' ),
130
+ '_border_width' => __( 'Width/None', 'chld_thm_cfg' ),
131
+ '_border_style' => __( 'Style', 'chld_thm_cfg' ),
132
+ '_border_color' => __( 'Color', 'chld_thm_cfg' ),
133
  ),
134
+ 'load_txt' => __( 'Are you sure? This will replace your current settings.', 'chld_thm_cfg' ),
135
  'swatch_txt' => $this->swatch_text,
136
+ 'swatch_label' => __( 'Sample', 'chld_thm_cfg' ),
137
+ 'important_label' => __( '<span style="font-size:10px">!</span>', 'chld_thm_cfg' ),
138
+ 'selector_txt' => __( 'Selectors', 'chld_thm_cfg' ),
139
+ 'close_txt' => __( 'Close', 'chld_thm_cfg' ),
140
+ 'edit_txt' => __( 'Edit', 'chld_thm_cfg' ),
141
+ 'cancel_txt' => __( 'Cancel', 'chld_thm_cfg' ),
142
+ 'rename_txt' => __( 'Rename', 'chld_thm_cfg' ),
143
+ 'css_fail_txt' => __( 'The stylesheet cannot be displayed.', 'chld_thm_cfg' ),
144
+ 'child_only_txt' => __( '(Child Only)', 'chld_thm_cfg' ),
145
+ 'inval_theme_txt' => __( 'Please enter a valid Child Theme', 'chld_thm_cfg' ),
146
+ 'inval_name_txt' => __( 'Please enter a valid Child Theme name', 'chld_thm_cfg' ),
147
+ 'theme_exists_txt' => __( '<strong>%s</strong> exists. Please enter a different Child Theme', 'chld_thm_cfg' ),
148
  ) );
149
  wp_localize_script(
150
  'chld-thm-cfg-admin',
157
  // allows fonts and other externals to be previewed
158
  // loads early not to conflict with admin stylesheets
159
  $regex = "/\@import *(url)? *\( *['\"]?((https?:\/\/)?(.+?))['\"]? *\).*$/";
160
+ if ( $imports = $this->css->get_prop( 'imports' ) ):
161
  $count = 1;
162
+ foreach ( $imports as $import ):
163
+ preg_match( $regex, $import, $matches );
164
+ if ( empty( $matches[3] ) && !empty( $matches[4] ) ): // relative filepath
165
  $url = get_stylesheet_directory_uri();
166
+ preg_replace( "#\.\./#", '', $matches[4], -1, $count );
167
+ for( $i = 0; $i < $count; $i++ ):
168
+ $url = dirname( $url );
169
  endfor;
170
+ $import = preg_replace( $regex, '@import url(' . trailingslashit( $url ) . $matches[4] . ')', $import );
171
  endif;
172
+ wp_enqueue_style( 'chld-thm-cfg-admin' . ++$count, preg_replace( $regex, "$2", $import ) );
173
  endforeach;
174
  endif;
175
  }
179
  $this->load_config();
180
  do_action( 'chld_thm_cfg_forms', $this ); // hook for custom forms
181
  $this->write_config();
182
+ include_once( $this->pluginPath . 'includes/class-ctc-ui.php' );
183
  $this->ui = new ChildThemeConfiguratorUI();
184
  $this->ui->render_help_content();
185
+ add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ),999 );
186
  $this->load_imports();
187
  }
188
 
189
  function get_themes() {
190
  // create cache of theme info
191
+ $this->themes = array( 'child' => array(), 'parnt' => array() );
192
+ foreach ( wp_get_themes() as $theme ):
193
+ // organize into parent and child themes
194
  $group = $theme->parent() ? 'child' : 'parnt';
195
+ // get the theme slug
196
  $slug = $theme->get_stylesheet();
197
+ // add theme to themes array
198
  $this->themes[$group][$slug] = array(
199
+ 'Name' => $theme->get( 'Name' ),
200
+ 'Author' => $theme->get( 'Author' ),
201
+ 'Version' => $theme->get( 'Version' ),
202
  'screenshot' => $theme->get_screenshot(),
203
+ 'allowed' => $theme->is_allowed(),
204
  );
205
  endforeach;
206
  }
207
 
208
+ function validate_post( $action = 'ctc_update', $noncefield = '_wpnonce' ) {
209
  // security: request must be post, user must have permission, referrer must be local and nonce must match
210
+ return ( 'POST' == $_SERVER['REQUEST_METHOD']
211
+ && current_user_can( 'install_themes' ) // ( 'edit_theme_options' )
212
+ && ( $this->is_ajax ? check_ajax_referer( $action, $noncefield, FALSE ) : check_admin_referer( $action, $noncefield, FALSE ) ) );
213
  }
214
 
215
  function ajax_save_postdata() {
216
  $this->is_ajax = TRUE;
217
+ if ( $this->validate_post() ):
218
  $this->verify_creds();
219
  $this->load_config();
220
  $this->css->parse_post_data();
221
  $this->css->write_css();
222
+ $result = $this->css->obj_to_utf8( $this->updates );
223
  $this->css->save_config();
224
  // send all updates back to browser to update cache
225
+ die( json_encode( $result ) );
226
  else:
227
+ die( 0 );
228
  endif;
229
  }
230
 
231
  function ajax_query_css() {
232
  $this->is_ajax = TRUE;
233
+ if ( $this->validate_post() ):
234
  $this->load_config();
235
  $regex = "/^ctc_query_/";
236
+ foreach( preg_grep( $regex, array_keys( $_POST ) ) as $key ):
237
+ $name = preg_replace( $regex, '', $key );
238
+ $param[$name] = sanitize_text_field( $_POST[$key] );
239
  endforeach;
240
+ if ( !empty( $param['obj'] ) ):
241
  $result = array(
242
  array(
243
+ 'key' => isset( $param['key'] ) ? $param['key'] : '',
244
  'obj' => $param['obj'],
245
+ 'data' => $this->css->get_prop( $param['obj'], $param ),
246
  ),
247
  );
248
+ die( json_encode( $result ) );
249
  endif;
250
  endif;
251
+ die( 0 );
252
  }
253
 
254
  function load_config() {
255
+ include_once( $this->pluginPath . 'includes/class-ctc-css.php' );
256
  $this->css = new ChildThemeConfiguratorCSS();
257
  // if not new format or themes do not exist reinitialize
258
  if ( FALSE === $this->css->read_config()
259
+ || ! $this->check_theme_exists( $this->css->get_prop( 'child' ) )
260
+ || ! $this->check_theme_exists( $this->css->get_prop( 'parnt' ) ) ):
261
  $this->css = new ChildThemeConfiguratorCSS();
262
  $parent = get_template();
263
+ $this->css->set_prop( 'parnt', $parent );
264
  endif;
265
+ if ( 'GET' == strtoupper( $_SERVER['REQUEST_METHOD'] ) ):
266
+ if ( $this->css->get_prop( 'child' ) ):
267
  // get filesystem credentials if available
268
  $this->verify_creds();
269
+ $stylesheet = $this->css->get_child_target( 'style.css' );
270
  // check file permissions
271
+ if ( !is_writable( $stylesheet ) && !$this->fs ):
272
+ add_action( 'admin_notices', array( $this, 'writable_notice' ) );
273
  endif;
274
  // check for first run, enqueue flag will be null for anyone coming from < 1.6.0
275
+ if ( !isset( $this->css->enqueue ) )
276
+ add_action( 'admin_notices', array( $this, 'enqueue_notice' ) );
277
  endif;
278
  // check if file ownership is messed up from old version or other plugin
279
 
280
+ if ( fileowner( $this->css->get_child_target( '' ) ) != fileowner( get_theme_root() ) ):
281
+ add_action( 'admin_notices', array( $this, 'owner_notice' ) );
282
  endif;
283
  endif;
284
  }
290
  */
291
  function write_config() {
292
  // make sure this is a post
293
+ if ( 'POST' == strtoupper( $_SERVER['REQUEST_METHOD'] ) ):
294
  // see if a valid action was passed
295
+ foreach ( $this->actionfields as $action ):
296
  if ( in_array( 'ctc_' . $action, array_keys( $_POST ) ) ):
297
  $actionfield = $action;
298
  break;
299
  endif;
300
  endforeach;
301
+ if ( empty( $actionfield ) ) return FALSE;
302
 
303
  // make sure post passes security checkpoint
304
  $this->errors = array();
305
+ if ( $this->validate_post() ):
306
  // zip export does not require filesystem access so check that first
307
  if ( 'export_child_zip' == $actionfield ):
308
  $this->export_zip();
309
  // if we get here the zip failed
310
+ $this->errors[] = __( 'Zip file creation failed.', 'chld_thm_cfg' );
311
  // all other actions require filesystem access
312
  else:
313
  // handle uploaded file before checking filesystem
314
+ if ( 'theme_image_submit' == $actionfield && isset( $_FILES['ctc_theme_image'] ) ):
315
+ $this->handle_file_upload( 'ctc_theme_image', $this->imgmimes );
316
+ elseif ( 'theme_screenshot_submit' == $actionfield && isset( $_FILES['ctc_theme_screenshot'] ) ):
317
+ $this->handle_file_upload( 'ctc_theme_screenshot', $this->imgmimes );
318
  endif;
319
  // now we need to check filesystem access
320
+ $args = preg_grep( "/nonce/", array_keys( $_POST ), PREG_GREP_INVERT );
321
+ $this->verify_creds( $args );
322
+ if ( $this->fs ):
323
  $msg = FALSE;
324
  // we have filesystem access so proceed with specific actions
325
  switch( $actionfield ):
331
  case 'parnt_templates_submit':
332
  // copy parent templates to child
333
  if ( isset( $_POST['ctc_file_parnt'] ) ):
334
+ foreach ( $_POST['ctc_file_parnt'] as $file ):
335
+ $this->copy_parent_file( sanitize_text_field( $file ) );
336
  endforeach;
337
  $msg = '8&tab=file_options';
338
  endif;
341
  case 'child_templates_submit':
342
  // delete child theme files
343
  if ( isset( $_POST['ctc_file_child'] ) ):
344
+ if ( in_array( 'functions', $_POST['ctc_file_child'] ) ):
345
+ $this->errors[] = __( 'The Functions file is required and cannot be deleted.', 'chld_thm_cfg' );
346
  else:
347
+ foreach ( $_POST['ctc_file_child'] as $file ):
348
+ $this->delete_child_file( sanitize_text_field( $file ),
349
+ ( 0 === strpos( $file, 'style' ) ? 'css' : 'php' ) );
350
  endforeach;
351
  $msg = '8&tab=file_options';
352
  endif;
356
  case 'image_submit':
357
  // delete child theme images
358
  if ( isset( $_POST['ctc_img'] ) ):
359
+ foreach ( $_POST['ctc_img'] as $file ):
360
+ $this->delete_child_file( 'images/' . sanitize_text_field( $file ), 'img' );
361
  endforeach;
362
  $msg = '8&tab=file_options';
363
  endif;
365
 
366
  case 'templates_writable_submit':
367
  // make specific files writable ( systems not running suExec )
368
+ if ( isset( $_POST['ctc_file_child'] ) ):
369
+ foreach ( $_POST['ctc_file_child'] as $file ):
370
+ $this->set_writable( sanitize_text_field( $file ), ( 0 === strpos( $file, 'style' ) ? 'css' : 'php' ) );
371
  endforeach;
372
  $msg = '8&tab=file_options';
373
  endif;
387
 
388
  case 'theme_image_submit':
389
  // move uploaded child theme images (now we have filesystem access)
390
+ if ( isset( $_POST['movefile'] ) ):
391
+ $this->move_file_upload( 'images' );
392
  $msg = '8&tab=file_options';
393
  endif;
394
  break;
395
 
396
  case 'theme_screenshot_submit':
397
  // move uploaded child theme screenshot (now we have filesystem access)
398
+ if ( isset( $_POST['movefile'] ) ):
399
  // remove old screenshot
400
+ foreach( array_keys( $this->imgmimes ) as $extreg ):
401
+ foreach ( explode( '|', $extreg ) as $ext ):
402
+ $this->delete_child_file( 'screenshot', $ext );
403
  endforeach;
404
  endforeach;
405
+ $this->move_file_upload( '' );
406
  $msg = '8&tab=file_options';
407
  endif;
408
  break;
413
  endswitch;
414
  endif; // end filesystem condition
415
  endif; // end zip export condition
416
+ if ( empty( $this->errors ) && empty( $this->fs_prompt ) )
417
  // no errors so we redirect with confirmation message
418
+ $this->update_redirect( $msg );
419
  // otherwise fail gracefully
420
  $msg = NULL;
421
  return FALSE;
422
  endif; // end post validation condition
423
  // if you end up here you are not welcome
424
  $msg = NULL;
425
+ $this->errors[] = __( 'You do not have permission to configure child themes.', 'chld_thm_cfg' );
426
  endif; // end request method condition
427
  return FALSE;
428
  }
429
 
430
+ function check_theme_exists( $theme ) {
431
+ return in_array( $theme, array_keys( wp_get_themes() ) );
432
  }
433
 
434
  /***
437
  function setup_child_theme() {
438
 
439
  // sanitize and extract config fields into local vars
440
+ foreach ( $this->configfields as $configfield ):
441
+ $varparts = explode( '_', $configfield );
442
+ $varname = end( $varparts );
443
+ ${$varname} = empty( $_POST['ctc_' . $configfield] ) ? '' : sanitize_text_field( $_POST['ctc_' . $configfield] );
444
  endforeach;
445
 
446
  // check that all requirements are met
447
+ if ( $parnt ):
448
+ if ( ! $this->check_theme_exists( $parnt ) ):
449
+ $this->errors[] = sprintf( __( '%s does not exist. Please select a valid Parent Theme', 'chld_thm_cfg' ), $parnt );
450
  endif;
451
  else:
452
+ $this->errors[] = __( 'Please select a valid Parent Theme', 'chld_thm_cfg' );
453
  endif;
454
  // if this is a shiny brand new child theme certain rules apply
455
+ if ( 'new' == $type ):
456
+ if ( empty( $template ) && empty( $name ) ):
457
+ $this->errors[] = __( 'Please enter a valid Child Theme template name', 'chld_thm_cfg' );
458
  else:
459
  $configtype = 'theme'; // no custom stylesheets until style.css exists!
460
+ $child = strtolower( preg_replace( "%[^\w\-]%", '', empty( $template ) ? $name : $template ) );
461
+ if ( $this->check_theme_exists( $child ) ):
462
+ $this->errors[] = sprintf( __( '<strong>%s</strong> exists. Please enter a different Child Theme template name', 'chld_thm_cfg' ), $child );
463
  else:
464
+ add_action( 'chld_thm_cfg_addl_files', array( &$this, 'add_base_files' ), 10, 2 );
465
+ add_action( 'chld_thm_cfg_addl_files', array( &$this, 'copy_screenshot' ), 10, 2 );
466
+ add_action( 'chld_thm_cfg_addl_files', array( &$this, 'enqueue_parent_css' ), 15, 2 );
467
  endif;
468
  endif;
469
  // check if we have additional files from a plugin extension. if so, we have to override
470
  // function to support wp_filesystem requirements
471
+ elseif ( empty( $configtype ) || 'theme' == $configtype ):
472
  // no configtype means this is either from this plugin or a plugin after 1.5.0
473
+ add_action( 'chld_thm_cfg_addl_files', array( &$this, 'add_base_files' ), 10, 2 );
474
+ add_action( 'chld_thm_cfg_addl_files', array( &$this, 'copy_screenshot' ), 10, 2 );
475
+ add_action( 'chld_thm_cfg_addl_files', array( &$this, 'enqueue_parent_css' ), 15, 2 );
476
+ elseif( has_action( 'chld_thm_cfg_addl_files' ) ):
477
  // action exists so we have to hijack it to use new filesystem checks
478
  remove_all_actions( 'chld_thm_cfg_addl_files' );
479
  // back compat for plugins extension
480
+ add_action( 'chld_thm_cfg_addl_files', array( &$this, 'write_addl_files' ), 10, 2 );
481
  endif;
482
 
483
+ if ( empty( $name ) ):
484
+ $name = ucfirst( $child );
485
  endif;
486
 
487
+ if ( empty( $child ) ):
488
+ $this->errors[] = __( 'Please enter a valid Child Theme directory', 'chld_thm_cfg' );
489
  endif;
490
 
491
+ if ( FALSE === $this->verify_child_dir( $child ) ):
492
+ $this->errors[] = __( 'Your theme directories are not writable.', 'chld_thm_cfg' );
493
+ add_action( 'admin_notices', array( $this, 'writable_notice' ) );
494
  endif;
495
 
496
  // if no errors so far, we are good to create child theme
497
+ if ( empty( $this->errors ) ):
498
  $this->css = new ChildThemeConfiguratorCSS();
499
+ $this->css->set_prop( 'parnt', $parnt );
500
+ $this->css->set_prop( 'child', $child );
501
+ $this->css->set_prop( 'child_name', $name );
502
+ $this->css->set_prop( 'child_author', $author );
503
+ $this->css->set_prop( 'child_version', strlen( $version ) ? $version : '1.0' );
504
+ $this->css->set_prop( 'configtype', $configtype );
505
 
506
  // hook for add'l plugin files and subdirectories
507
+ do_action( 'chld_thm_cfg_addl_files', $this );
508
 
509
  // always parse parent stylesheet
510
+ $this->css->parse_css_file( 'parnt' );
511
 
512
+ // parse child stylesheet, backup or skip ( to reset )
513
+ $this->css->parse_css_file( 'child', $revert );
514
 
515
  // parse additional stylesheets
516
+ if ( isset( $_POST['ctc_additional_css'] ) && is_array( $_POST['ctc_additional_css'] ) ):
517
  $this->css->parentss = array();
518
+ foreach ( $_POST['ctc_additional_css'] as $file ):
519
+ $this->css->parse_css_file( 'parnt', $file );
520
  $this->css->parntss[] = $file;
521
  endforeach;
522
  endif;
523
 
524
  // try to write new stylsheet. If it fails send alert.
525
+ if ( FALSE === $this->css->write_css( isset( $_POST['ctc_backup'] ) ) ):
526
+ $this->errors[] = __( 'Your stylesheet is not writable.', 'chld_thm_cfg' );
527
+ add_action( 'admin_notices', array( $this, 'writable_notice' ) );
528
  return FALSE;
529
  endif;
530
 
531
  // copy parent theme mods option set
532
+ if ( isset( $_POST['ctc_parent_mods'] ) ):
533
  // we can copy settings from parent to child even if neither is currently active
534
  // so we need cases for active parent, active child or neither
535
 
536
  // get active theme
537
  $active_theme = get_stylesheet();
538
  // create temp array from parent settings
539
+ $child_mods = get_option( 'theme_mods_' . $parnt );
540
+ if ( $active_theme == $parnt ):
541
  // if parent theme is active, get widgets from active sidebars_widgets array
542
  $child_widgets = retrieve_widgets();
543
  else:
544
  // otherwise get widgets from parent theme mods
545
  $child_widgets = $child_mods['sidebars_widgets']['data'];
546
  endif;
547
+ if ( $active_theme == $child ):
548
  // if child theme is active, remove widgets from temp array
549
+ unset( $child_mods['sidebars_widgets'] );
550
  // copy temp array to child mods
551
+ update_option( 'theme_mods_' . $child, $child_mods );
552
  // copy widgets to active sidebars_widgets array
553
+ wp_set_sidebars_widgets( $child_widgets );
554
  else:
555
  // otherwise copy widgets to temp array with time stamp
556
  $child_mods['sidebars_widgets']['data'] = $child_widgets;
557
  $child_mods['sidebars_widgets']['time'] = time();
558
  // copy temp array to child theme mods
559
+ update_option( 'theme_mods_' . $child, $child_mods );
560
  endif;
561
  endif;
562
 
564
  $this->css->save_config();
565
 
566
  // hook for add'l plugin options
567
+ do_action( 'chld_thm_cfg_addl_options', $this ); // hook for add'l plugin options
568
 
569
  // return message id 1, which says new child theme created successfull;
570
+ return 1; //isset( $_POST['ctc_scan_subdirs'] ) ? '9&tab=import_options' : 1;
571
  endif;
572
  return FALSE;
573
  }
574
  /*
575
  * TODO: this is a stub for future use
576
  */
577
+ function sanitize_options( $input ) {
578
  return $input;
579
  }
580
 
581
+ function update_redirect( $msg = 1 ) {
582
+ if ( empty( $this->is_ajax ) ):
583
+
584
+ wp_safe_redirect(
585
+ ( is_multisite() ?
586
+ network_admin_url( 'themes.php?page=' . CHLD_THM_CFG_MENU . '&updated=' . $msg ) :
587
+ admin_url( 'tools.php?page=' . CHLD_THM_CFG_MENU . '&updated=' . $msg ) )
588
+ );
589
  die();
590
  endif;
591
  }
592
 
593
+ function verify_child_dir( $path ) {
594
+ if ( !$this->fs ) return FALSE; // return if no filesystem access
595
  global $wp_filesystem;
596
+ $themedir = $wp_filesystem->find_folder( get_theme_root() );
597
+ if ( ! $wp_filesystem->is_writable( $themedir ) ) return FALSE;
598
+ $childparts = explode( '/', $this->normalize_path( $path ) );
599
+ while ( count( $childparts ) ):
600
+ $subdir = array_shift( $childparts );
601
+ if ( empty( $subdir ) ) continue;
602
+ $themedir = trailingslashit( $themedir ) . $subdir;
603
+ if ( ! $wp_filesystem->is_dir( $themedir ) ):
604
+ if ( ! $wp_filesystem->mkdir( $themedir, FS_CHMOD_DIR ) ):
605
  return FALSE;
606
  endif;
607
+ elseif ( ! $wp_filesystem->is_writable( $themedir ) ):
608
  return FALSE;
609
  endif;
610
  endwhile;
611
  return TRUE;
612
  }
613
 
614
+ function add_base_files( $obj ){
615
  // add functions.php file
616
  $contents = "<?php
617
  // Exit if accessed directly
618
+ if ( !defined( 'ABSPATH' ) ) exit;
619
  ";
620
+ if ( FALSE === $this->write_child_file( 'functions.php', $contents )
621
+ || FALSE === $this->write_child_file( 'style.css', $this->css->get_css_header() ) ) return FALSE;
622
  }
623
 
624
  function enqueue_parent_code(){
625
+ return explode( "\n", apply_filters( 'chld_thm_cfg_enqueue_parent', "
626
+ if ( !function_exists( 'chld_thm_cfg_parent_css' ) ):
627
  function chld_thm_cfg_parent_css() {
628
+ wp_enqueue_style( 'chld_thm_cfg_parent', get_template_directory_uri() . '/style.css' );
629
  }
630
  endif;
631
+ add_action( 'wp_enqueue_scripts', 'chld_thm_cfg_parent_css' );
632
+ " ) );
633
  }
634
 
635
+ function enqueue_parent_css( $obj ) {
636
+ if ( isset( $_POST['ctc_parent_enqueue'] ) )
637
+ $this->css->enqueue = sanitize_text_field( $_POST['ctc_parent_enqueue'] );
638
  $marker = 'ENQUEUE PARENT ACTION';
639
  $insertion = 'enqueue' == $this->css->enqueue ? $this->enqueue_parent_code() : array();
640
+ if ( $filename = $this->css->is_file_ok( $this->css->get_child_target( 'functions.php' ), 'write' ) ):
641
  $this->insert_with_markers( $filename, $marker, $insertion );
642
  endif;
643
  }
647
  * but it does not use wp_filesystem API!!!???
648
  */
649
  function insert_with_markers( $filename, $marker, $insertion ) {
650
+ if ( !$this->fs ) return FALSE; // return if no filesystem access
651
  global $wp_filesystem;
652
+ if( !$wp_filesystem->exists( $this->fspath( $filename ) ) ):
653
  $markerdata = FALSE;
654
  else:
655
  // get_contents_array returns extra linefeeds so just split it ourself
656
+ $markerdata = explode( "\n", $wp_filesystem->get_contents( $this->fspath( $filename ) ) );
657
  endif;
658
  $newfile = '';
659
  $foundit = false;
660
  if ( $markerdata ) {
661
  $state = true;
662
  foreach ( $markerdata as $n => $markerline ) {
663
+ if ( strpos( $markerline, '// BEGIN ' . $marker ) !== false )
664
  $state = false;
665
  if ( $state ):
666
  if ( $n + 1 < count( $markerdata ) )
668
  else
669
  $newfile .= "{$markerline}";
670
  endif;
671
+ if ( strpos( $markerline, '// END ' . $marker ) !== false ):
672
  $newfile .= "// BEGIN {$marker}\n";
673
+ if ( is_array( $insertion ) )
674
  foreach ( $insertion as $insertline )
675
  $newfile .= "{$insertline}\n";
676
  $newfile .= "// END {$marker}\n";
679
  endif;
680
  }
681
  }
682
+ if ( !$foundit ) {
683
  $newfile .= "\n// BEGIN {$marker}\n";
684
  foreach ( $insertion as $insertline )
685
  $newfile .= "{$insertline}\n";
686
  $newfile .= "// END {$marker}\n";
687
  }
688
+ if ( FALSE === $wp_filesystem->put_contents( $this->fspath( $filename ), $newfile ) ) return FALSE;
689
  }
690
 
691
+ function write_child_file( $file, $contents ) {
692
+ if ( !$this->fs ) return FALSE; // return if no filesystem access
693
  global $wp_filesystem;
694
+ $file = $this->fspath( $this->css->is_file_ok( $this->css->get_child_target( $file ), 'write' ) );
695
+ if ( $file && !$wp_filesystem->exists( $file ) ):
696
+ if ( FALSE === $wp_filesystem->put_contents( $file, $contents ) ) return FALSE;
697
  endif;
698
  }
699
 
700
+ function copy_screenshot( $obj ) {
701
  // always copy screenshot
702
+ $this->copy_parent_file( 'screenshot' );
703
  }
704
 
705
+ function copy_parent_file( $file, $ext = 'php' ) {
706
+ if ( !$this->fs ) return FALSE; // return if no filesystem access
707
  global $wp_filesystem;
708
  $parent_file = NULL;
709
+ if ( 'screenshot' == $file ):
710
+ foreach ( array_keys( $this->imgmimes ) as $extreg ):
711
+ foreach( explode( '|', $extreg ) as $ext ):
712
+ if ( $parent_file = $this->css->is_file_ok( $this->css->get_parent_source( 'screenshot.' . $ext ) ) ) break;
713
  endforeach;
714
+ if ( $parent_file ):
715
+ $parent_file = $this->fspath( $parent_file );
716
  break;
717
  endif;
718
  endforeach;
719
  else:
720
+ $parent_file = $this->fspath( $this->css->is_file_ok( $this->css->get_parent_source( $file . '.' . $ext ) ) );
721
  endif;
722
+ // get child theme + file + ext ( passing empty string and full child path to theme_basename )
723
+ $child_file = $this->css->get_child_target( $file . '.' . $ext );
724
  // return true if file already exists
725
+ if ( $wp_filesystem->exists( $this->fspath( $child_file ) ) ) return TRUE;
726
+ $child_dir = dirname( $this->theme_basename( '', $child_file ) );
727
+ if ( $parent_file // sanity check
728
  && $child_file // sanity check
729
+ && $this->verify_child_dir( $child_dir ) //create child subdir if necessary
730
+ && $wp_filesystem->copy( $parent_file, $this->fspath( $child_file ), FS_CHMOD_FILE ) ) return TRUE;
731
+ $this->errors[] = __( 'Could not copy file.', 'chld_thm_cfg' );
732
  }
733
 
734
+ function delete_child_file( $file, $ext = 'php' ) {
735
+ if ( !$this->fs ) return FALSE; // return if no filesystem access
736
  global $wp_filesystem;
737
  // verify file is in child theme and exists before removing.
738
+ $file = ( 'img' == $ext ? $file : $file . '.' . $ext );
739
+ $child_file = $this->fspath( $this->css->is_file_ok( $this->css->get_child_target( $file ), 'write' ) );
740
+ if ( $wp_filesystem->exists( $child_file ) ):
741
+ if ( !$wp_filesystem->delete( $child_file ) ) return FALSE;
742
  endif;
743
  }
744
 
745
+ function get_files( $theme, $type = 'template' ) {
746
+ if ( !isset( $this->files[$theme] ) ):
747
  $this->files[$theme] = array();
748
+ $imgext = '(' . implode( '|', array_keys( $this->imgmimes ) ) . ')';
749
+ foreach ( $this->css->recurse_directory(
750
+ trailingslashit( get_theme_root() ) . $theme, '', TRUE ) as $file ):
751
+ $file = $this->theme_basename( $theme, $file );
752
+ if ( preg_match( "/^style\-(\d+)\.css$/", $file, $matches ) ):
753
+ $date = date_i18n( 'D, j M Y g:i A', strtotime( $matches[1] ) );
754
  $this->files[$theme]['backup'][$file] = $date;
755
+ elseif ( preg_match( "/\.php$/", $file ) ):
756
  $this->files[$theme]['template'][] = $file;
757
+ elseif ( preg_match( "/\.css$/", $file ) && 'style.css' != $file ):
758
  $this->files[$theme]['stylesheet'][] = $file;
759
+ elseif ( preg_match( "/^images\/.+?\." . $imgext . "$/", $file ) ):
760
  $this->files[$theme]['img'][] = $file;
761
  endif;
762
  endforeach;
763
  endif;
764
+ return isset( $this->files[$theme][$type] ) ? $this->files[$theme][$type] : array();
765
  }
766
 
767
+ function theme_basename( $theme, $file ) {
768
  // if no theme passed, returns theme + file
769
+ $themedir = trailingslashit( get_theme_root() ) . ( '' == $theme ? '' : trailingslashit( $theme ) );
770
+ return preg_replace( '%^' . preg_quote( $themedir ) . '%', '', $file );
771
  }
772
 
773
+ function uploads_basename( $file ) {
774
+ $file = $this->normalize_path( $file );
775
  $uplarr = wp_upload_dir();
776
+ $upldir = trailingslashit( $uplarr['basedir'] );
777
+ return preg_replace( '%^' . preg_quote( $upldir ) . '%', '', $file );
778
  }
779
 
780
+ function uploads_fullpath( $file ) {
781
+ $file = $this->normalize_path( $file );
782
  $uplarr = wp_upload_dir();
783
+ $upldir = trailingslashit( $uplarr['basedir'] );
784
  return $upldir . $file;
785
  }
786
 
787
  function serialize_postarrays() {
788
+ foreach ( $this->postarrays as $field )
789
+ if ( isset( $_POST[$field] ) && is_array( $_POST[$field] ) )
790
+ $_POST[$field] = implode( "%%", $_POST[$field] );
791
  }
792
 
793
  function unserialize_postarrays() {
794
+ foreach ( $this->postarrays as $field )
795
+ if ( isset( $_POST[$field] ) && !is_array( $_POST[$field] ) )
796
+ $_POST[$field] = explode( "%%", $_POST[$field] );
797
  }
798
 
799
+ function set_writable( $file = NULL ) {
800
 
801
+ $file = isset( $file ) ? $this->css->get_child_target( $file . '.php' ) : apply_filters( 'chld_thm_cfg_target', $this->css->get_child_target(), $this->css );
802
+ if ( $this->fs ): // filesystem access
803
  global $wp_filesystem;
804
+ if ( $file && $wp_filesystem->chmod( $this->fspath( $file ), 0666 ) ) return;
805
  endif;
806
+ $this->errors[] = __( 'Could not set write permissions.', 'chld_thm_cfg' );
807
+ add_action( 'admin_notices', array( $this, 'writable_notice' ) );
808
  return FALSE;
809
  }
810
 
811
  function unset_writable() {
812
+ if ( !$this->fs ) return FALSE; // return if no filesystem access
813
  global $wp_filesystem;
814
+ $dir = untrailingslashit( $this->css->get_child_target( '' ) );
815
+ $child = $this->theme_basename( '', $dir );
816
+ $newchild = untrailingslashit( $child ) . '-new';
817
+ $themedir = trailingslashit( get_theme_root() );
818
+ $fsthemedir = $this->fspath( $themedir );
819
  // is child theme owned by user?
820
+ if ( fileowner( $dir ) == fileowner( ABSPATH ) ):
821
  $copy = FALSE;
822
+ $wp_filesystem->chmod( $dir );
823
+ // recursive chmod ( as user )
824
  // WP_Filesystem RECURSIVE CHMOD IS FLAWED! IT SETS ALL CHILDREN TO PERM OF OUTERMOST DIR
825
+ //if ( $wp_filesystem->chmod( $this->fspath( $dir ), FALSE, TRUE ) ):
826
  //endif;
827
  else:
828
  $copy = TRUE;
829
  endif;
830
+ // n -> copy entire folder ( as user )
831
+ $files = $this->css->recurse_directory( $dir, NULL, TRUE );
832
  $errors = array();
833
+ foreach ( $files as $file ):
834
+ $childfile = $this->theme_basename( $child, $this->normalize_path( $file ) );
835
+ $newfile = trailingslashit( $newchild ) . $childfile;
836
+ $childpath = $fsthemedir . trailingslashit( $child ) . $childfile;
837
  $newpath = $fsthemedir . $newfile;
838
+ if ( $copy ):
839
+ if ( $this->verify_child_dir( is_dir( $file ) ? $newfile : dirname( $newfile ) ) ):
840
+ if ( is_file( $file ) && !$wp_filesystem->copy( $childpath, $newpath ) ):
841
  $errors[] = 'could not copy ' . $newpath;
842
  endif;
843
  else:
844
  $errors[] = 'invalid dir: ' . $newfile;
845
  endif;
846
  else:
847
+ $wp_filesystem->chmod( $this->fspath( $file ) );
848
  endif;
849
  endforeach;
850
+ if ( $copy ):
851
+ // verify copy ( as webserver )
852
+ $newfiles = $this->css->recurse_directory( trailingslashit( $themedir ) . $newchild, NULL, TRUE );
853
  $deleteddirs = $deletedfiles = 0;
854
+ if ( count( $newfiles ) == count( $files ) ):
855
+ // rename old ( as webserver )
856
+ if ( !$wp_filesystem->exists( trailingslashit( $fsthemedir ) . $child . '-old' ) )
857
+ $wp_filesystem->move( trailingslashit( $fsthemedir ) . $child, trailingslashit( $fsthemedir ) . $child . '-old' );
858
+ // rename new ( as user )
859
+ if ( !$wp_filesystem->exists( trailingslashit( $fsthemedir ) . $child ) )
860
+ $wp_filesystem->move( trailingslashit( $fsthemedir ) . $newchild, trailingslashit( $fsthemedir ) . $child );
861
+ // remove old files ( as webserver )
862
+ $oldfiles = $this->css->recurse_directory( trailingslashit( $themedir ) . $child . '-old', NULL, TRUE );
863
+ array_unshift( $oldfiles, trailingslashit( $themedir ) . $child . '-old' );
864
+ foreach ( array_reverse( $oldfiles ) as $file ):
865
+ if ( $wp_filesystem->delete( $this->fspath( $file ) ) || ( is_dir( $file ) && @rmdir( $file ) ) || ( is_file( $file ) && @unlink( $file ) ) ):
866
  $deletedfiles++;
867
  endif;
868
  endforeach;
869
+ if ( $deletedfiles != count( $oldfiles ) ):
870
+ $errors[] = 'deleted: ' . $deletedfiles . ' != ' . count( $oldfiles ) . ' files';
871
  endif;
872
  else:
873
  $errors[] = 'newfiles != files';
874
  endif;
875
  endif;
876
+ if ( count( $errors ) ):
877
+ $this->errors[] = __( 'There were errors while resetting permissions.', 'chld_thm_cfg' ) ;
878
+ add_action( 'admin_notices', array( $this, 'writable_notice' ) );
879
  endif;
880
  }
881
 
882
+ function handle_file_upload( $field, $childdir = NULL, $mimes = NULL ){
883
  $uploadedfile = $_FILES[$field];
884
  $upload_overrides = array(
885
  'test_form' => FALSE,
886
+ 'mimes' => ( is_array( $mimes ) ? $mimes : NULL )
887
  );
888
  if ( ! function_exists( 'wp_handle_upload' ) ) require_once( ABSPATH . 'wp-admin/includes/file.php' );
889
  $movefile = wp_handle_upload( $uploadedfile, $upload_overrides );
890
+ if ( isset( $movefile['error'] ) ):
891
  $this->errors[] = $movefile['error'];
892
  return FALSE;
893
  endif;
894
+ $_POST['movefile'] = $this->uploads_basename( $movefile['file'] );
895
  }
896
 
897
+ function move_file_upload( $subdir = 'images' ) {
898
+ if ( !$this->fs ) return FALSE; // return if no filesystem access
899
  global $wp_filesystem;
900
+ $source_file = sanitize_text_field( $_POST['movefile'] );
901
+ $target_file = ( '' == $subdir ?
902
+ preg_replace( "%^.+(\.\w+)$%", "screenshot$1", basename( $source_file ) ) :
903
+ trailingslashit( $subdir ) . basename( $source_file ) );
904
+ if ( FALSE !== $this->verify_child_dir( trailingslashit( $this->css->get_prop( 'child' ) ) . $subdir ) ):
905
+ $source_path = $this->fspath( $this->uploads_fullpath( $source_file ) );
906
+ if ( $target_path = $this->css->is_file_ok( $this->css->get_child_target( $target_file ), 'write' ) ):
907
+ $target_path = $this->fspath( $target_path );
908
+ if ( $wp_filesystem->exists( $source_path ) ):
909
+ if ( $wp_filesystem->move( $source_path, $target_path ) ) return TRUE;
910
  endif;
911
  endif;
912
  endif;
913
 
914
+ $this->errors[] = __( 'Could not upload file.', 'chld_thm_cfg' );
915
  }
916
 
917
  function export_zip() {
918
+ if ( ( $child = $this->css->get_prop( 'child' ) )
919
+ && ( $dir = $this->css->is_file_ok( dirname( $this->css->get_child_target() ), 'search' ) )
920
+ && ( $version = preg_replace( "%[^\w\.\-]%", '', $this->css->get_prop( 'version' ) ) ) ):
921
  // use php system upload dir to store temp files so that we can use pclzip
922
+ $tmpdir = ini_get( 'upload_tmp_dir' ) ? ini_get( 'upload_tmp_dir' ) : sys_get_temp_dir();
923
+ $file = trailingslashit( $tmpdir ) . $child . '-' . $version . '.zip';
924
  mbstring_binary_safe_encoding();
925
 
926
+ require_once( ABSPATH . 'wp-admin/includes/class-pclzip.php' );
927
 
928
+ $archive = new PclZip( $file );
929
+ if ( $archive->create( $dir, PCLZIP_OPT_REMOVE_PATH, dirname( $dir ) ) == 0 ) return FALSE;
930
  reset_mbstring_encoding();
931
  header( 'Content-Description: File Transfer' );
932
  header( 'Content-Type: application/octet-stream' );
933
+ header( 'Content-Length: ' . filesize( $file ) );
934
+ header( 'Content-Disposition: attachment; filename=' . basename( $file ) );
935
+ header( 'Expires: 0' );
936
+ header( 'Cache-Control: must-revalidate' );
937
+ header( 'Pragma: public' );
938
+ readfile( $file );
939
+ unlink( $file );
940
  die();
941
  endif;
942
  }
944
  /*
945
  *
946
  */
947
+ function verify_creds( $args = array() ) {
948
  $this->fs_prompt = $this->fs = FALSE;
949
  //fs prompt does not support arrays as post data - serialize arrays
950
  $this->serialize_postarrays();
951
  // generate callback url
952
+ $url = is_multisite() ? network_admin_url( 'themes.php?page=' . CHLD_THM_CFG_MENU ) :
953
+ admin_url( 'tools.php?page=' . CHLD_THM_CFG_MENU );
954
+ $nonce_url = wp_nonce_url( $url, 'ctc_update', '_wpnonce' );
955
  // buffer output so we can process prior to http header
956
  ob_start();
957
+ if ( $creds = request_filesystem_credentials( $nonce_url, '', FALSE, FALSE, $args ) ):
958
  // check filesystem permission if direct or ftp creds exist
959
+ if ( WP_Filesystem( $creds ) )
960
  // login ok
961
  $this->fs = TRUE;
962
  else
963
  // incorrect credentials, get form with error flag
964
+ $creds = request_filesystem_credentials( $nonce_url, '', TRUE, FALSE, $args );
965
  else:
966
  // no credentials, initialize unpriveledged filesystem object
967
  WP_Filesystem();
977
  /*
978
  * convert 'direct' filepath into wp_filesystem filepath
979
  */
980
+ function fspath( $file ){
981
+ if ( ! $this->fs ) return FALSE; // return if no filesystem access
982
  global $wp_filesystem;
983
+ if ( is_dir( $file ) ):
984
  $dir = $file;
985
  $base = '';
986
  else:
987
+ $dir = dirname( $file );
988
+ $base = basename( $file );
989
  endif;
990
+ $fsdir = $wp_filesystem->find_folder( $dir );
991
+ return trailingslashit( $fsdir ) . $base;
992
  }
993
 
994
  function writable_notice() {
995
  ?> <div class="update-nag">
996
+ <p><?php _e( 'Child Theme Configurator is unable to write to the stylesheet. This can be resolved using one of the following options:<ol>', 'chld_thm_cfg' );
997
+ if ( isset( $_SERVER['SERVER_SOFTWARE'] ) && preg_match( '%unix%i',$_SERVER['SERVER_SOFTWARE'] ) ):
998
+ _e( '<li>Temporarily make the stylesheet writable by clicking the button below. You should change this back when you are finished editing for security by clicking "Make read-only" under the "Files" tab.</li>', 'chld_thm_cfg' );
999
  ?><form action="?page=<?php echo CHLD_THM_CFG_MENU; ?>" method="post"><?php wp_nonce_field( 'ctc_update' ); ?>
1000
+ <input name="ctc_set_writable" class="button" type="submit" value="<?php _e( 'Temporarily make stylesheet writable', 'chld_thm_cfg' ); ?>"/></form><?php endif;
1001
+ _e( '<li><a target="_blank" href="http://codex.wordpress.org/Editing_wp-config.php#WordPress_Upgrade_Constants" title="Editin wp-config.php">Add your FTP/SSH credentials to the WordPress config file</a>.</li>', 'chld_thm_cfg' );
1002
+ if ( isset( $_SERVER['SERVER_SOFTWARE'] ) && preg_match( '%iis%i',$_SERVER['SERVER_SOFTWARE'] ) )
1003
+ _e( '<li><a target="_blank" href="http://technet.microsoft.com/en-us/library/cc771170" title="Setting Application Pool Identity">Assign WordPress to an application pool that has write permissions</a> (Windows IIS systems).</li>', 'chld_thm_cfg' );
1004
+ _e( '<li><a target="_blank" href="http://codex.wordpress.org/Changing_File_Permissions" title="Changing File Permissions">Set the stylesheet write permissions on the server manually</a> (not recommended).</li>', 'chld_thm_cfg' );
1005
+ if ( isset( $_SERVER['SERVER_SOFTWARE'] ) && preg_match( '%unix%i',$_SERVER['SERVER_SOFTWARE'] ) )
1006
+ _e( '<li>Run PHP under Apache with suEXEC (contact your web host).</li>', 'chld_thm_cfg' ) ?>
1007
  </ol></p>
1008
  </div>
1009
  <?php
1011
  function owner_notice() {
1012
  ?>
1013
  <div class="update-nag">
1014
+ <p><?php _e( 'This Child Theme is not owned by your website account. It may have been created by a prior version of this plugin or by another program. Moving forward, it must be owned by your website account to make changes. Child Theme Configurator will attempt to correct this when you click the button below.', 'chld_thm_cfg' ) ?></p>
1015
  <form action="?page=<?php echo CHLD_THM_CFG_MENU; ?>" method="post"><?php wp_nonce_field( 'ctc_update' ); ?>
1016
+ <input name="ctc_reset_permission" class="button" type="submit" value="<?php _e( 'Correct Child Theme Permissions', 'chld_thm_cfg' ); ?>"/></form> </div>
1017
  <?php
1018
  }
1019
 
1020
  function enqueue_notice() {
1021
  ?>
1022
  <div class="update-nag">
1023
+ <p><?php _e( 'Child Theme Configurator has changed the way it handles the parent stylesheet. Please set your preferences below and click "Generate Child Theme Files" to update your configuration.', 'chld_thm_cfg' ) ?></p>
1024
  </div>
1025
  <?php
1026
  }
1027
 
1028
  // back compatibility function for plugins extension
1029
+ function write_addl_files( $chld_thm_cfg ) {
1030
  global $chld_thm_cfg_plugins;
1031
+ if ( !is_object( $chld_thm_cfg_plugins ) || !$chld_thm_cfg->fs ) return FALSE;
1032
+ $configtype = $chld_thm_cfg->css->get_prop( 'configtype' );
1033
+ if ( 'theme' == $configtype || !( $def = $chld_thm_cfg_plugins->defs->get_def( $configtype ) ) ) return FALSE;
1034
+ $child = trailingslashit( $chld_thm_cfg->css->get_prop( 'child' ) );
1035
+ if ( isset( $def['addl'] ) && is_array( $def['addl'] ) && count( $def['addl'] ) ):
1036
+ foreach ( $def['addl'] as $path => $type ):
1037
 
1038
  // sanitize the crap out of the target data -- it will be used to create paths
1039
+ $path = $this->normalize_path( preg_replace( "%[^\w\\//\-]%", '', sanitize_text_field( $child . $path ) ) );
1040
+ if ( ( 'dir' == $type && FALSE === $chld_thm_cfg->verify_child_dir( $path ) )
1041
+ || ( 'dir' != $type && FALSE === $chld_thm_cfg->write_child_file( $path, '' ) ) ):
1042
  $chld_thm_cfg->errors[] =
1043
+ __( 'Your theme directories are not writable.', 'chld_thm_cfg_plugins' );
1044
  endif;
1045
  endforeach;
1046
  endif;
1047
  // write main def file
1048
+ if ( isset( $def['target'] ) ):
1049
+ $path = $this->normalize_path( preg_replace( "%[^\w\\//\-\.]%", '', sanitize_text_field( $def['target'] ) ) ); //$child .
1050
+ if ( FALSE === $chld_thm_cfg->write_child_file( $path, '' ) ):
1051
  $chld_thm_cfg->errors[] =
1052
+ __( 'Your stylesheet is not writable.', 'chld_thm_cfg_plugins' );
1053
  return FALSE;
1054
  endif;
1055
  endif;
includes/forms/at-import.php CHANGED
@@ -1,5 +1,5 @@
1
  <?php
2
- if (!defined('ABSPATH')) exit;
3
  // @imports Panel
4
  ?>
5
 
@@ -11,17 +11,17 @@ if (!defined('ABSPATH')) exit;
11
  <div class="ctc-input-cell">
12
  <div class="ctc-textarea-button-cell" id="ctc_save_imports_cell">
13
  <input type="button" class="button ctc-save-input" id="ctc_save_imports"
14
- name="ctc_save_imports" value="<?php _e('Save', 'chld_thm_cfg'); ?>" disabled />
15
  </div>
16
  <strong>
17
- <?php _e('@import Statements', 'chld_thm_cfg'); ?>
18
  </strong>
19
  </div>
20
  <div class="ctc-input-cell-wide">
21
  <textarea id="ctc_child_imports" name="ctc_child_imports" wrap="off"><?php
22
- if (!empty($imports)):
23
- foreach ($imports as $import):
24
- echo esc_textarea($import . ';' . LF);
25
  endforeach;
26
  endif; ?>
27
  </textarea>
1
  <?php
2
+ if ( !defined( 'ABSPATH' ) ) exit;
3
  // @imports Panel
4
  ?>
5
 
11
  <div class="ctc-input-cell">
12
  <div class="ctc-textarea-button-cell" id="ctc_save_imports_cell">
13
  <input type="button" class="button ctc-save-input" id="ctc_save_imports"
14
+ name="ctc_save_imports" value="<?php _e( 'Save', 'chld_thm_cfg' ); ?>" disabled />
15
  </div>
16
  <strong>
17
+ <?php _e( '@import Statements', 'chld_thm_cfg' ); ?>
18
  </strong>
19
  </div>
20
  <div class="ctc-input-cell-wide">
21
  <textarea id="ctc_child_imports" name="ctc_child_imports" wrap="off"><?php
22
+ if ( !empty( $imports ) ):
23
+ foreach ( $imports as $import ):
24
+ echo esc_textarea( $import . ';' . LF );
25
  endforeach;
26
  endif; ?>
27
  </textarea>
includes/forms/backup.php CHANGED
@@ -1,8 +1,8 @@
1
  <?php
2
- if (!defined('ABSPATH')) exit;
3
  // Backup Input Cell
4
  ?>
5
- <label class="ctc-input-cell smaller<?php echo 'child' == $template && !$this->ctc()->fs && is_writable($themeroot . $backup) ? ' writable' : ''; ?>">
6
  <input class="ctc_checkbox" id="ctc_file_<?php echo $template . '_' . ++$counter; ?>"
7
  name="ctc_file_<?php echo $template; ?>[]" type="checkbox"
8
- value="<?php echo $templatefile; ?>" /><?php echo __('Backup', 'chld_thm_cfg') . ' ' . $label; ?></label>
1
  <?php
2
+ if ( !defined( 'ABSPATH' ) ) exit;
3
  // Backup Input Cell
4
  ?>
5
+ <label class="ctc-input-cell smaller<?php echo 'child' == $template && !$this->ctc()->fs && is_writable( $themeroot . $backup ) ? ' writable' : ''; ?>">
6
  <input class="ctc_checkbox" id="ctc_file_<?php echo $template . '_' . ++$counter; ?>"
7
  name="ctc_file_<?php echo $template; ?>[]" type="checkbox"
8
+ value="<?php echo $templatefile; ?>" /><?php echo __( 'Backup', 'chld_thm_cfg' ) . ' ' . $label; ?></label>
includes/forms/file.php CHANGED
@@ -1,8 +1,8 @@
1
  <?php
2
- if (!defined('ABSPATH')) exit;
3
  // File Input Cell
4
  ?>
5
- <label class="ctc-input-cell smaller<?php echo 'child' == $template && !$this->ctc()->fs && is_writable($themeroot . $file) ? ' writable' : ''; ?>">
6
  <input class="ctc_checkbox" id="ctc_file_<?php echo $template . '_' . ++$counter; ?>"
7
  name="ctc_file_<?php echo $template; ?>[]" type="checkbox"
8
  value="<?php echo $templatefile; ?>" />
1
  <?php
2
+ if ( !defined( 'ABSPATH' ) ) exit;
3
  // File Input Cell
4
  ?>
5
+ <label class="ctc-input-cell smaller<?php echo 'child' == $template && !$this->ctc()->fs && is_writable( $themeroot . $file ) ? ' writable' : ''; ?>">
6
  <input class="ctc_checkbox" id="ctc_file_<?php echo $template . '_' . ++$counter; ?>"
7
  name="ctc_file_<?php echo $template; ?>[]" type="checkbox"
8
  value="<?php echo $templatefile; ?>" />
includes/forms/fileform.php CHANGED
@@ -1,16 +1,17 @@
1
  <?php
2
- if (!defined('ABSPATH')) exit;
3
  // Files Section
4
  // This include is used for both parent template section and the child files section
5
 
6
- if (defined('DISALLOW_FILE_EDIT') && DISALLOW_FILE_EDIT):
7
- $linktext = __('The Theme editor has been disabled. Template files must be edited offline.', 'chld_thm_cfg');
8
  $editorlink = '';
9
  $editorlinkend = '';
10
  else:
11
- $linktext = __('Click here to edit template files using the Theme Editor', 'chld_thm_cfg');
12
- $editorlink = '<a href="' . admin_url('theme-editor.php?theme=' . $this->ctc()->css->get_prop('child'))
13
- . ('parnt' == $template ? '&file=functions.php' : '') . '" title="' . $linktext . '">';
 
14
  $editorlinkend = '</a>';
15
  endif;
16
  ?>
@@ -18,17 +19,17 @@ endif;
18
  <form id="ctc_<?php echo $template; ?>_templates_form" method="post" action="?page=<?php echo CHLD_THM_CFG_MENU; ?>&amp;tab=file_options">
19
  <?php wp_nonce_field( 'ctc_update' ); ?>
20
  <div class="ctc-input-cell"> <strong>
21
- <?php echo 'parnt' == $template ? __('Parent Templates', 'chld_thm_cfg') : __('Child Theme Files', 'chld_thm_cfg'); ?>
22
  </strong>
23
  <?php
24
- if ('parnt' == $template): ?>
25
  <p class="howto">
26
- <?php _e('Copy PHP template files from the parent theme by selecting them here.', 'chld_thm_cfg'); ?>
27
  </p>
28
  <p><strong>
29
- <?php _e('CAUTION: If your child theme is active, the child theme version of the file will be used instead of the parent immediately after it is copied.', 'chld_thm_cfg');?>
30
  </strong></p>
31
- <p class="howto"> <?php echo sprintf(__('The %s file is generated separately and cannot be copied here.', 'chld_thm_cfg'),
32
  $editorlink . '<code>functions.php</code>' . $editorlinkend
33
  );
34
  else: ?>
@@ -37,8 +38,8 @@ else: ?>
37
  </p>
38
  <p class="howto">
39
  <?php
40
- echo ($this->ctc()->fs ?
41
- __( 'Delete child theme templates by selecting them here.', 'chld_thm_cfg') :
42
  __( 'Delete child theme templates or make them writable by selecting them here. Writable files are displayed in <span style="color:red">red</span>.', 'chld_thm_cfg' )
43
  ); ?>
44
  </p>
@@ -49,14 +50,14 @@ else: ?>
49
  <div class="ctc-input-cell-wide"> <?php echo $inputs; ?></div>
50
  <div class="ctc-input-cell"> <strong>&nbsp;</strong> </div>
51
  <div class="ctc-input-cell-wide" style="margin-top:10px;margin-bottom:10px">
52
- <?php if ('child' == $template && !$this->ctc()->fs): ?>
53
  <input class="ctc_submit button button-primary" id="ctc_templates_writable_submit"
54
  name="ctc_templates_writable_submit" type="submit"
55
- value="<?php _e('Make Selected Writable', 'chld_thm_cfg'); ?>" />&nbsp; &nbsp;
56
  <?php endif; ?>
57
  <input class="ctc_submit button button-primary" id="ctc_<?php echo $template; ?>_templates_submit"
58
  name="ctc_<?php echo $template; ?>_templates_submit" type="submit"
59
- value="<?php echo ('parnt' == $template ? __('Copy Selected to Child Theme', 'chld_thm_cfg') : __('Delete Selected', 'chld_thm_cfg')); ?>" />
60
  </div>
61
  </form>
62
  </div>
1
  <?php
2
+ if ( !defined( 'ABSPATH' ) ) exit;
3
  // Files Section
4
  // This include is used for both parent template section and the child files section
5
 
6
+ if ( defined( 'DISALLOW_FILE_EDIT' ) && DISALLOW_FILE_EDIT ):
7
+ $linktext = __( 'The Theme editor has been disabled. Template files must be edited offline.', 'chld_thm_cfg' );
8
  $editorlink = '';
9
  $editorlinkend = '';
10
  else:
11
+ $linktext = __( 'Click here to edit template files using the Theme Editor', 'chld_thm_cfg' );
12
+ $adminbase = 'theme-editor.php?theme=' . $this->ctc()->css->get_prop( 'child' )
13
+ . ( 'parnt' == $template ? '&file=functions.php' : '' );
14
+ $editorlink = '<a href="' . ( is_multisite() ? network_admin_url( $adminbase ) : admin_url( $adminbase ) ) . '" title="' . $linktext . '">';
15
  $editorlinkend = '</a>';
16
  endif;
17
  ?>
19
  <form id="ctc_<?php echo $template; ?>_templates_form" method="post" action="?page=<?php echo CHLD_THM_CFG_MENU; ?>&amp;tab=file_options">
20
  <?php wp_nonce_field( 'ctc_update' ); ?>
21
  <div class="ctc-input-cell"> <strong>
22
+ <?php echo 'parnt' == $template ? __( 'Parent Templates', 'chld_thm_cfg' ) : __( 'Child Theme Files', 'chld_thm_cfg' ); ?>
23
  </strong>
24
  <?php
25
+ if ( 'parnt' == $template ): ?>
26
  <p class="howto">
27
+ <?php _e( 'Copy PHP template files from the parent theme by selecting them here.', 'chld_thm_cfg' ); ?>
28
  </p>
29
  <p><strong>
30
+ <?php _e( 'CAUTION: If your child theme is active, the child theme version of the file will be used instead of the parent immediately after it is copied.', 'chld_thm_cfg' );?>
31
  </strong></p>
32
+ <p class="howto"> <?php echo sprintf( __( 'The %s file is generated separately and cannot be copied here.', 'chld_thm_cfg' ),
33
  $editorlink . '<code>functions.php</code>' . $editorlinkend
34
  );
35
  else: ?>
38
  </p>
39
  <p class="howto">
40
  <?php
41
+ echo ( $this->ctc()->fs ?
42
+ __( 'Delete child theme templates by selecting them here.', 'chld_thm_cfg' ) :
43
  __( 'Delete child theme templates or make them writable by selecting them here. Writable files are displayed in <span style="color:red">red</span>.', 'chld_thm_cfg' )
44
  ); ?>
45
  </p>
50
  <div class="ctc-input-cell-wide"> <?php echo $inputs; ?></div>
51
  <div class="ctc-input-cell"> <strong>&nbsp;</strong> </div>
52
  <div class="ctc-input-cell-wide" style="margin-top:10px;margin-bottom:10px">
53
+ <?php if ( 'child' == $template && !$this->ctc()->fs ): ?>
54
  <input class="ctc_submit button button-primary" id="ctc_templates_writable_submit"
55
  name="ctc_templates_writable_submit" type="submit"
56
+ value="<?php _e( 'Make Selected Writable', 'chld_thm_cfg' ); ?>" />&nbsp; &nbsp;
57
  <?php endif; ?>
58
  <input class="ctc_submit button button-primary" id="ctc_<?php echo $template; ?>_templates_submit"
59
  name="ctc_<?php echo $template; ?>_templates_submit" type="submit"
60
+ value="<?php echo ( 'parnt' == $template ? __( 'Copy Selected to Child Theme', 'chld_thm_cfg' ) : __( 'Delete Selected', 'chld_thm_cfg' ) ); ?>" />
61
  </div>
62
  </form>
63
  </div>
includes/forms/files.php CHANGED
@@ -1,35 +1,35 @@
1
  <?php
2
- if (!defined('ABSPATH')) exit;
3
  // Files Panel
4
  ?>
5
 
6
  <div id="file_options_panel"
7
  class="ctc-option-panel<?php echo 'file_options' == $active_tab ? ' ctc-option-panel-active' : ''; ?>" <?php echo $hidechild; ?>>
8
- <?php $this->render_file_form('parnt'); ?>
9
- <?php $this->render_file_form('child'); ?>
10
  <?php $this->render_image_form(); ?>
11
  <div class="ctc-input-row clearfix" id="input_row_theme_image">
12
  <form id="ctc_theme_image_form" method="post" action="?page=<?php echo CHLD_THM_CFG_MENU; ?>" enctype="multipart/form-data">
13
  <?php wp_nonce_field( 'ctc_update' ); ?>
14
  <div class="ctc-input-cell"> <strong>
15
- <?php _e('Upload New Child Theme Image', 'chld_thm_cfg'); ?>
16
  </strong>
17
  <p class="howto">
18
- <?php _e('Theme images reside under the <code>images</code> directory in your child theme and are meant for stylesheet use only. Use the Media Library for content images.', 'chld_thm_cfg'); ?>
19
  </p>
20
  </div>
21
  <div class="ctc-input-cell-wide">
22
  <input type="file" id="ctc_theme_image" name="ctc_theme_image" value="" />
23
  <input class="ctc_submit button button-primary" id="ctc_theme_image_submit"
24
  name="ctc_theme_image_submit" type="submit"
25
- value="<?php _e('Upload', 'chld_thm_cfg'); ?>" />
26
  </div>
27
  </form>
28
  </div>
29
- <?php if ($screenshot = $this->get_theme_screenshot()): ?>
30
  <div class="ctc-input-row clearfix" id="input_row_screenshot_view">
31
  <div class="ctc-input-cell"> <strong>
32
- <?php _e('Child Theme Screenshot', 'chld_thm_cfg'); ?>
33
  </strong> </div>
34
  <div class="ctc-input-cell-wide"> <a href="<?php echo $screenshot; ?>" class="thickbox"><img src="<?php echo $screenshot; ?>" height="150" width="200" style="max-height:150px;max-width:200px;width:auto;height:auto" /></a> </div>
35
  </div>
@@ -38,17 +38,17 @@ if (!defined('ABSPATH')) exit;
38
  <form id="ctc_screenshot_form" method="post" action="?page=<?php echo CHLD_THM_CFG_MENU; ?>" enctype="multipart/form-data">
39
  <?php wp_nonce_field( 'ctc_update' ); ?>
40
  <div class="ctc-input-cell"> <strong>
41
- <?php _e('Upload New Screenshot', 'chld_thm_cfg'); ?>
42
  </strong>
43
  <p class="howto">
44
- <?php _e('The theme screenshot should be a 4:3 ratio (e.g., 880px x 660px) JPG, PNG or GIF. It will be renamed <code>screenshot</code>.', 'chld_thm_cfg'); ?>
45
  </p>
46
  </div>
47
  <div class="ctc-input-cell-wide">
48
  <input type="file" id="ctc_theme_screenshot" name="ctc_theme_screenshot" value="" />
49
  <input class="ctc_submit button button-primary" id="ctc_theme_screenshot_submit"
50
  name="ctc_theme_screenshot_submit" type="submit"
51
- value="<?php _e('Upload', 'chld_thm_cfg'); ?>" />
52
  </div>
53
  </form>
54
  </div>
@@ -56,30 +56,30 @@ if (!defined('ABSPATH')) exit;
56
  <form id="ctc_export_form" method="post" action="?page=<?php echo CHLD_THM_CFG_MENU; ?>">
57
  <?php wp_nonce_field( 'ctc_update' ); ?>
58
  <div class="ctc-input-cell"> <strong>
59
- <?php _e('Export Child Theme as Zip Archive', 'chld_thm_cfg'); ?>
60
  </strong> </div>
61
  <div class="ctc-input-cell-wide">
62
  <input class="ctc_submit button button-primary" id="ctc_export_child_zip"
63
  name="ctc_export_child_zip" type="submit"
64
- value="<?php _e('Export', 'chld_thm_cfg'); ?>" />
65
  </div>
66
  </form>
67
  </div>
68
- <?php if ('direct' != $this->ctc()->fs_method): ?>
69
  <div class="ctc-input-row clearfix" id="input_row_permissions">
70
  <form id="ctc_permission_form" method="post" action="?page=<?php echo CHLD_THM_CFG_MENU; ?>">
71
  <?php wp_nonce_field( 'ctc_update' ); ?>
72
  <div class="ctc-input-cell"> <strong>
73
- <?php _e('Secure Child Theme', 'chld_thm_cfg'); ?>
74
  </strong>
75
  <p class="howto">
76
- <?php _e('Attempt to reset child theme permissions to user ownership and read-only access.', 'chld_thm_cfg'); ?>
77
  </p>
78
  </div>
79
  <div class="ctc-input-cell-wide">
80
  <input class="ctc_submit button button-primary" id="ctc_reset_permission"
81
  name="ctc_reset_permission" type="submit"
82
- value="<?php _e('Make read-only', 'chld_thm_cfg'); ?>" />
83
  </div>
84
  </form>
85
  </div>
1
  <?php
2
+ if ( !defined( 'ABSPATH' ) ) exit;
3
  // Files Panel
4
  ?>
5
 
6
  <div id="file_options_panel"
7
  class="ctc-option-panel<?php echo 'file_options' == $active_tab ? ' ctc-option-panel-active' : ''; ?>" <?php echo $hidechild; ?>>
8
+ <?php $this->render_file_form( 'parnt' ); ?>
9
+ <?php $this->render_file_form( 'child' ); ?>
10
  <?php $this->render_image_form(); ?>
11
  <div class="ctc-input-row clearfix" id="input_row_theme_image">
12
  <form id="ctc_theme_image_form" method="post" action="?page=<?php echo CHLD_THM_CFG_MENU; ?>" enctype="multipart/form-data">
13
  <?php wp_nonce_field( 'ctc_update' ); ?>
14
  <div class="ctc-input-cell"> <strong>
15
+ <?php _e( 'Upload New Child Theme Image', 'chld_thm_cfg' ); ?>
16
  </strong>
17
  <p class="howto">
18
+ <?php _e( 'Theme images reside under the <code>images</code> directory in your child theme and are meant for stylesheet use only. Use the Media Library for content images.', 'chld_thm_cfg' ); ?>
19
  </p>
20
  </div>
21
  <div class="ctc-input-cell-wide">
22
  <input type="file" id="ctc_theme_image" name="ctc_theme_image" value="" />
23
  <input class="ctc_submit button button-primary" id="ctc_theme_image_submit"
24
  name="ctc_theme_image_submit" type="submit"
25
+ value="<?php _e( 'Upload', 'chld_thm_cfg' ); ?>" />
26
  </div>
27
  </form>
28
  </div>
29
+ <?php if ( $screenshot = $this->get_theme_screenshot() ): ?>
30
  <div class="ctc-input-row clearfix" id="input_row_screenshot_view">
31
  <div class="ctc-input-cell"> <strong>
32
+ <?php _e( 'Child Theme Screenshot', 'chld_thm_cfg' ); ?>
33
  </strong> </div>
34
  <div class="ctc-input-cell-wide"> <a href="<?php echo $screenshot; ?>" class="thickbox"><img src="<?php echo $screenshot; ?>" height="150" width="200" style="max-height:150px;max-width:200px;width:auto;height:auto" /></a> </div>
35
  </div>
38
  <form id="ctc_screenshot_form" method="post" action="?page=<?php echo CHLD_THM_CFG_MENU; ?>" enctype="multipart/form-data">
39
  <?php wp_nonce_field( 'ctc_update' ); ?>
40
  <div class="ctc-input-cell"> <strong>
41
+ <?php _e( 'Upload New Screenshot', 'chld_thm_cfg' ); ?>
42
  </strong>
43
  <p class="howto">
44
+ <?php _e( 'The theme screenshot should be a 4:3 ratio (e.g., 880px x 660px) JPG, PNG or GIF. It will be renamed <code>screenshot</code>.', 'chld_thm_cfg' ); ?>
45
  </p>
46
  </div>
47
  <div class="ctc-input-cell-wide">
48
  <input type="file" id="ctc_theme_screenshot" name="ctc_theme_screenshot" value="" />
49
  <input class="ctc_submit button button-primary" id="ctc_theme_screenshot_submit"
50
  name="ctc_theme_screenshot_submit" type="submit"
51
+ value="<?php _e( 'Upload', 'chld_thm_cfg' ); ?>" />
52
  </div>
53
  </form>
54
  </div>
56
  <form id="ctc_export_form" method="post" action="?page=<?php echo CHLD_THM_CFG_MENU; ?>">
57
  <?php wp_nonce_field( 'ctc_update' ); ?>
58
  <div class="ctc-input-cell"> <strong>
59
+ <?php _e( 'Export Child Theme as Zip Archive', 'chld_thm_cfg' ); ?>
60
  </strong> </div>
61
  <div class="ctc-input-cell-wide">
62
  <input class="ctc_submit button button-primary" id="ctc_export_child_zip"
63
  name="ctc_export_child_zip" type="submit"
64
+ value="<?php _e( 'Export', 'chld_thm_cfg' ); ?>" />
65
  </div>
66
  </form>
67
  </div>
68
+ <?php if ( 'direct' != $this->ctc()->fs_method ): ?>
69
  <div class="ctc-input-row clearfix" id="input_row_permissions">
70
  <form id="ctc_permission_form" method="post" action="?page=<?php echo CHLD_THM_CFG_MENU; ?>">
71
  <?php wp_nonce_field( 'ctc_update' ); ?>
72
  <div class="ctc-input-cell"> <strong>
73
+ <?php _e( 'Secure Child Theme', 'chld_thm_cfg' ); ?>
74
  </strong>
75
  <p class="howto">
76
+ <?php _e( 'Attempt to reset child theme permissions to user ownership and read-only access.', 'chld_thm_cfg' ); ?>
77
  </p>
78
  </div>
79
  <div class="ctc-input-cell-wide">
80
  <input class="ctc_submit button button-primary" id="ctc_reset_permission"
81
  name="ctc_reset_permission" type="submit"
82
+ value="<?php _e( 'Make read-only', 'chld_thm_cfg' ); ?>" />
83
  </div>
84
  </form>
85
  </div>
includes/forms/image.php CHANGED
@@ -1,5 +1,5 @@
1
  <?php
2
- if (!defined('ABSPATH')) exit;
3
  // Image Input Cell
4
  ?>
5
  <div class="ctc-input-cell" style="height:100px">
1
  <?php
2
+ if ( !defined( 'ABSPATH' ) ) exit;
3
  // Image Input Cell
4
  ?>
5
  <div class="ctc-input-cell" style="height:100px">
includes/forms/images.php CHANGED
@@ -1,15 +1,15 @@
1
  <?php
2
- if (!defined('ABSPATH')) exit;
3
  // Images Section
4
  ?>
5
  <div class="ctc-input-row clearfix" id="input_row_images">
6
  <form id="ctc_image_form" method="post" action="?page=<?php echo CHLD_THM_CFG_MENU; ?>">
7
  <?php wp_nonce_field( 'ctc_update' ); ?>
8
  <div class="ctc-input-cell"> <strong>
9
- <?php _e('Child Theme Images', 'chld_thm_cfg'); ?>
10
  </strong>
11
  <p class="howto">
12
- <?php _e('Delete child theme images by selecting them here.', 'chld_thm_cfg');?>
13
  </p>
14
  </div>
15
  <div class="ctc-input-cell-wide"> <?php echo $inputs; ?> </div>
@@ -17,7 +17,7 @@ if (!defined('ABSPATH')) exit;
17
  <div class="ctc-input-cell-wide" style="margin-top:10px;margin-bottom:10px">
18
  <input class="ctc_submit button button-primary" id="ctc_image_submit"
19
  name="ctc_image_submit" type="submit"
20
- value="<?php _e('Delete Selected', 'chld_thm_cfg'); ?>" disabled />
21
  </div>
22
  </form>
23
  </div>
1
  <?php
2
+ if ( !defined( 'ABSPATH' ) ) exit;
3
  // Images Section
4
  ?>
5
  <div class="ctc-input-row clearfix" id="input_row_images">
6
  <form id="ctc_image_form" method="post" action="?page=<?php echo CHLD_THM_CFG_MENU; ?>">
7
  <?php wp_nonce_field( 'ctc_update' ); ?>
8
  <div class="ctc-input-cell"> <strong>
9
+ <?php _e( 'Child Theme Images', 'chld_thm_cfg' ); ?>
10
  </strong>
11
  <p class="howto">
12
+ <?php _e( 'Delete child theme images by selecting them here.', 'chld_thm_cfg' );?>
13
  </p>
14
  </div>
15
  <div class="ctc-input-cell-wide"> <?php echo $inputs; ?> </div>
17
  <div class="ctc-input-cell-wide" style="margin-top:10px;margin-bottom:10px">
18
  <input class="ctc_submit button button-primary" id="ctc_image_submit"
19
  name="ctc_image_submit" type="submit"
20
+ value="<?php _e( 'Delete Selected', 'chld_thm_cfg' ); ?>" disabled />
21
  </div>
22
  </form>
23
  </div>
includes/forms/main.php CHANGED
@@ -1,5 +1,5 @@
1
  <?php
2
- if (!defined('ABSPATH')) exit;
3
  // main CTC Page
4
  ?>
5
  <style type="text/css">
@@ -9,20 +9,20 @@ if (!defined('ABSPATH')) exit;
9
  }
10
 
11
  .ctc-status-icon.failure {
12
- background:url(<?php echo admin_url( 'images/no.png');
13
  ?>) no-repeat;
14
  }
15
 
16
  .ctc-exit {
17
- background:#f9f9f9 url(<?php echo includes_url('images/xit-2x.gif');
18
  ?>) left top no-repeat;
19
  }
20
  </style>
21
  <div class="wrap">
22
- <?php do_action('chld_thm_cfg_related_links'); ?>
23
- <h2><?php echo __('Child Theme Configurator', 'chld_thm_cfg') . ' ' . __('version', 'chld_thm_cfg') . ' ' . CHLD_THM_CFG_VERSION; ?></h2>
24
  <?php
25
- if ('POST' == $_SERVER['REQUEST_METHOD'] && !$this->ctc()->fs):
26
  echo $this->ctc()->fs_prompt;
27
  else: ?>
28
  <div id="ctc_error_notice">
@@ -36,7 +36,7 @@ else: ?>
36
  <div class="ctc-option-panel-container">
37
  <?php
38
  include ( $this->ctc()->pluginPath . 'includes/forms/parent-child.php' );
39
- if ($enqueueset):
40
  include ( $this->ctc()->pluginPath . 'includes/forms/rule-value.php' );
41
  include ( $this->ctc()->pluginPath . 'includes/forms/query-selector.php' );
42
  include ( $this->ctc()->pluginPath . 'includes/forms/at-import.php' ); ?>
@@ -45,10 +45,10 @@ else: ?>
45
  <div id="view_parnt_options_panel"
46
  class="ctc-option-panel<?php echo 'view_parnt_options' == $active_tab ? ' ctc-option-panel-active' : ''; ?>" <?php echo $hidechild; ?>> </div>
47
  <?php
48
- if ('' == $hidechild):
49
  include ( $this->ctc()->pluginPath . 'includes/forms/files.php' );
50
  endif;
51
- do_action('chld_thm_cfg_panels', $this->ctc(), $active_tab, $hidechild);
52
  endif; ?>
53
  </div>
54
  <?php
1
  <?php
2
+ if ( !defined( 'ABSPATH' ) ) exit;
3
  // main CTC Page
4
  ?>
5
  <style type="text/css">
9
  }
10
 
11
  .ctc-status-icon.failure {
12
+ background:url(<?php echo admin_url( 'images/no.png' );
13
  ?>) no-repeat;
14
  }
15
 
16
  .ctc-exit {
17
+ background:#f9f9f9 url(<?php echo includes_url( 'images/xit-2x.gif' );
18
  ?>) left top no-repeat;
19
  }
20
  </style>
21
  <div class="wrap">
22
+ <?php do_action( 'chld_thm_cfg_related_links' ); ?>
23
+ <h2><?php echo __( 'Child Theme Configurator', 'chld_thm_cfg' ) . ' ' . __( 'version', 'chld_thm_cfg' ) . ' ' . CHLD_THM_CFG_VERSION; ?></h2>
24
  <?php
25
+ if ( 'POST' == $_SERVER['REQUEST_METHOD'] && !$this->ctc()->fs ):
26
  echo $this->ctc()->fs_prompt;
27
  else: ?>
28
  <div id="ctc_error_notice">
36
  <div class="ctc-option-panel-container">
37
  <?php
38
  include ( $this->ctc()->pluginPath . 'includes/forms/parent-child.php' );
39
+ if ( $enqueueset ):
40
  include ( $this->ctc()->pluginPath . 'includes/forms/rule-value.php' );
41
  include ( $this->ctc()->pluginPath . 'includes/forms/query-selector.php' );
42
  include ( $this->ctc()->pluginPath . 'includes/forms/at-import.php' ); ?>
45
  <div id="view_parnt_options_panel"
46
  class="ctc-option-panel<?php echo 'view_parnt_options' == $active_tab ? ' ctc-option-panel-active' : ''; ?>" <?php echo $hidechild; ?>> </div>
47
  <?php
48
+ if ( '' == $hidechild ):
49
  include ( $this->ctc()->pluginPath . 'includes/forms/files.php' );
50
  endif;
51
+ do_action( 'chld_thm_cfg_panels', $this->ctc(), $active_tab, $hidechild );
52
  endif; ?>
53
  </div>
54
  <?php
includes/forms/parent-child.php CHANGED
@@ -1,5 +1,5 @@
1
  <?php
2
- if (!defined('ABSPATH')) exit;
3
  // Parent/Child Panel
4
  ?>
5
 
@@ -7,8 +7,8 @@ if (!defined('ABSPATH')) exit;
7
  <form id="ctc_load_form" method="post" action="?page=<?php echo CHLD_THM_CFG_MENU; ?>">
8
  <?php
9
  wp_nonce_field( 'ctc_update' );
10
- if (has_action('chld_thm_cfg_controls')):
11
- if ('' == $hidechild) do_action('chld_thm_cfg_controls', $this->ctc());
12
  $themeonly = 'style="display:none"';
13
  else:
14
  $themeonly = '';
@@ -17,159 +17,159 @@ if (!defined('ABSPATH')) exit;
17
  <div class="ctc-theme-only" <?php echo 'theme' != $configtype ? $themeonly : ''; ?>>
18
  <div class="ctc-input-row clearfix" id="input_row_parnt">
19
  <div class="ctc-input-cell"> <strong>
20
- <?php _e('Parent Theme', 'chld_thm_cfg'); ?>
21
  </strong> </div>
22
  <div class="ctc-input-cell">
23
- <?php $this->render_theme_menu('parnt', $parent); ?>
24
  </div>
25
  </div>
26
  <div class="ctc-input-row clearfix" id="input_row_child">
27
  <div class="ctc-input-cell"> <strong>
28
- <?php _e('Child Theme', 'chld_thm_cfg'); ?>
29
  </strong> </div>
30
  <div class="ctc-input-cell">
31
  <input class="ctc-radio" id="ctc_child_type_new" name="ctc_child_type" type="radio" value="new"
32
- <?php echo (!empty($hidechild) ? 'checked' : ''); ?>
33
  <?php echo $hidechild;?> />
34
  <label for="ctc_child_type_new">
35
- <?php _e('Create New Child Theme', 'chld_thm_cfg'); ?>
36
  </label>
37
  </div>
38
  <div class="ctc-input-cell">
39
  <input class="ctc-radio" id="ctc_child_type_existing" name="ctc_child_type" type="radio" value="existing"
40
- <?php echo (empty($hidechild) ? 'checked' : ''); ?>
41
  <?php echo $hidechild; ?>/>
42
  &nbsp;
43
  <label for="ctc_child_type_existing" <?php echo $hidechild;?>>
44
- <?php _e('Use Existing Child Theme', 'chld_thm_cfg'); ?>
45
  </label>
46
  </div>
47
  <div class="ctc-input-cell" style="clear:both"> <strong>&nbsp;</strong> </div>
48
  <div class="ctc-input-cell" >
49
- <input class="ctc_text" id="ctc_child_template" name="ctc_child_template" type="text" placeholder="<?php _e('Theme Slug', 'chld_thm_cfg'); ?>" autocomplete="off"/>
50
  </div>
51
- <?php if ('' == $hidechild): ?>
52
  <div class="ctc-input-cell">
53
- <?php $this->render_theme_menu('child', $child); ?>
54
  </div>
55
  <?php endif; ?>
56
  </div>
57
  <div class="ctc-input-row clearfix" id="input_row_child_name">
58
  <div class="ctc-input-cell"> <strong>
59
- <?php _e('Child Theme Name', 'chld_thm_cfg'); ?>
60
  </strong> </div>
61
  <div class="ctc-input-cell">
62
  <input class="ctc_text" id="ctc_child_name" name="ctc_child_name" type="text"
63
- value="<?php echo esc_attr($css->get_prop('child_name')); ?>" placeholder="<?php _e('Theme Name', 'chld_thm_cfg'); ?>" autocomplete="off" />
64
  </div>
65
  </div>
66
  </div>
67
  <div class="ctc-input-row clearfix">
68
  <div class="ctc-input-cell"> <strong>
69
- <?php _e('Author', 'chld_thm_cfg'); ?>
70
  </strong> </div>
71
  <div class="ctc-input-cell">
72
  <input class="ctc_text" id="ctc_child_author" name="ctc_child_author" type="text"
73
- value="<?php echo esc_attr($css->get_prop('author')); ?>" placeholder="<?php _e('Author', 'chld_thm_cfg'); ?>" autocomplete="off" />
74
  </div>
75
  </div>
76
  <div class="ctc-input-row clearfix">
77
  <div class="ctc-input-cell"> <strong>
78
- <?php _e('Version', 'chld_thm_cfg'); ?>
79
  </strong> </div>
80
  <div class="ctc-input-cell">
81
  <input class="ctc_text" id="ctc_child_version" name="ctc_child_version" type="text"
82
- value="<?php echo esc_attr($css->get_prop('version')); ?>" placeholder="<?php _e('Version', 'chld_thm_cfg'); ?>" autocomplete="off" />
83
  </div>
84
  </div>
85
  <div class="ctc-theme-only" <?php echo 'theme' != $configtype ? $themeonly : ''; ?>>
86
  <div class="ctc-input-row clearfix">
87
  <div class="ctc-input-cell"> <strong>
88
- <?php _e('Copy Parent Theme Menus, Widgets and other Options', 'chld_thm_cfg'); ?>
89
  </strong> </div>
90
  <div class="ctc-input-cell">
91
  <input class="ctc_checkbox" id="ctc_parent_mods" name="ctc_parent_mods" type="checkbox"
92
  value="1" />
93
  </div>
94
  <div class="ctc-input-cell"> <strong>
95
- <?php _e('NOTE:', 'chld_thm_cfg'); ?>
96
  </strong>
97
- <?php _e( 'This will overwrite child theme options you may have already set.', 'chld_thm_cfg'); ?>
98
  </div>
99
  </div>
100
- <?php if ('' == $hidechild): ?>
101
  <div class="ctc-input-row clearfix">
102
  <div class="ctc-input-cell"> <strong>
103
- <?php _e('Backup current stylesheet', 'chld_thm_cfg'); ?>
104
  </strong> </div>
105
  <div class="ctc-input-cell">
106
  <input class="ctc_checkbox" id="ctc_backup" name="ctc_backup" type="checkbox"
107
  value="1" />
108
  </div>
109
  <div class="ctc-input-cell"> <strong>
110
- <?php _e('NOTE:', 'chld_thm_cfg'); ?>
111
  </strong>
112
- <?php _e( 'This creates a copy of the current stylesheet before applying changes. You can remove old backup files using the Files tab.', 'chld_thm_cfg'); ?>
113
  </div>
114
  </div>
115
  <?php
116
  endif;
117
- $parent_handling = (isset($css->enqueue) ? $css->enqueue : ($mustimport ? 'import' : 'enqueue'));
118
  ?>
119
  <div class="ctc-input-row clearfix">
120
  <div class="ctc-input-cell"> <strong>
121
- <?php _e('Parent stylesheet handling:', 'chld_thm_cfg'); ?>
122
  </strong> </div>
123
  <div class="ctc-input-cell">
124
  <label>
125
  <input class="ctc_radio" id="ctc_parent_enqueue_enqueue" name="ctc_parent_enqueue" type="radio"
126
- value="enqueue" <?php checked('enqueue', $parent_handling); ?>/>
127
- <?php _e('&lt;link&gt; (default)', 'chld_thm_cfg'); ?>
128
  </label>
129
  <br/>
130
  <label>
131
  <input class="ctc_radio" id="ctc_parent_enqueue_import" name="ctc_parent_enqueue" type="radio"
132
- value="import" <?php checked('import', $parent_handling); ?>/>
133
- <?php _e('@import', 'chld_thm_cfg'); ?>
134
  </label>
135
  <br/>
136
  <label>
137
  <input class="ctc_radio" id="ctc_parent_enqueue_none" name="ctc_parent_enqueue" type="radio"
138
- value="none" <?php checked('none', $parent_handling); ?>/>
139
- <?php _e('none (handled by theme)', 'chld_thm_cfg'); ?>
140
  </label>
141
  </div>
142
  <div class="ctc-input-cell"> <strong>
143
- <?php _e('NOTE:', 'chld_thm_cfg'); ?>
144
- <?php if ($mustimport) _e('This theme does not currently enqueue the stylesheet and must use the @import option to render correctly.', 'chld_thm_cfg'); ?>
145
  </strong>
146
- <?php _e( "Select @import for older themes that do not enqueue the stylesheet. Select 'none' if core styles are automatically loaded for child themes. Select '&lt;link&gt;' if unsure.", 'chld_thm_cfg'); ?>
147
  </div>
148
  </div>
149
- <?php if ('' == $hidechild): ?>
150
  <div class="ctc-input-row clearfix">
151
  <div class="ctc-input-cell ctc-section-toggle" id="ctc_revert_css"> <strong>
152
- <?php _e('Reset/Restore from backup:', 'chld_thm_cfg'); ?>
153
  </strong> </div>
154
  <div class="ctc-input-cell-wide ctc-section-toggle-content" id="ctc_revert_css_content">
155
  <label>
156
  <input class="ctc_checkbox" id="ctc_revert_none" name="ctc_revert" type="radio"
157
  value="" checked="" />
158
- <?php _e('Leave unchanged', 'chld_thm_cfg');?>
159
  </label>
160
  <br/>
161
  <label>
162
  <input class="ctc_checkbox" id="ctc_revert_all" name="ctc_revert" type="radio"
163
  value="all" />
164
- <?php _e('Reset all', 'chld_thm_cfg');?>
165
  </label>
166
  <br/>
167
  <?php
168
- foreach ($this->ctc()->get_files($child, 'backup') as $backup => $label): ?>
169
  <label>
170
  <input class="ctc_checkbox" id="ctc_revert_<?php echo $backup; ?>" name="ctc_revert" type="radio"
171
  value="<?php echo $backup; ?>" />
172
- <?php echo __('Restore backup from', 'chld_thm_cfg') . ' ' . $label; ?></label>
173
  <br/>
174
  <?php
175
  endforeach;
@@ -177,18 +177,18 @@ if (!defined('ABSPATH')) exit;
177
  </div>
178
  </div>
179
  <?php endif;
180
- $stylesheets = $this->ctc()->get_files($parent, 'stylesheet');
181
- if (count($stylesheets)):?>
182
  <div class="ctc-input-row clearfix">
183
  <div class="ctc-input-cell ctc-section-toggle" id="ctc_additional_css_files"> <strong>
184
- <?php _e('Parse additional stylesheets:', 'chld_thm_cfg'); ?>
185
  </strong> </div>
186
  <div class="ctc-input-cell-wide ctc-section-toggle-content" id="ctc_additional_css_files_content">
187
  <p style="margin-top:0">
188
- <?php _e('Stylesheets that are currently being loaded by the parent theme are automatically selected below (except for Bootstrap stylesheets which add a large amount data to the configuration). To further reduce overhead, select only the additional stylesheets you wish to customize.', 'chld_thm_cfg'); ?>
189
  </p>
190
  <?php
191
- foreach ($stylesheets as $stylesheet): ?>
192
  <div class="ctc-input-cell">
193
  <label>
194
  <input class="ctc_checkbox" name="ctc_additional_css[]" type="checkbox"
@@ -204,7 +204,7 @@ if (!defined('ABSPATH')) exit;
204
  <div class="ctc-input-cell"> <strong>&nbsp;</strong> </div>
205
  <div class="ctc-input-cell">
206
  <input class="ctc_submit button button-primary" id="ctc_load_styles" name="ctc_load_styles" type="submit"
207
- value="<?php _e('Generate/Rebuild Child Theme Files', 'chld_thm_cfg'); ?>" disabled />
208
  </div>
209
  </div>
210
  </form>
1
  <?php
2
+ if ( !defined( 'ABSPATH' ) ) exit;
3
  // Parent/Child Panel
4
  ?>
5
 
7
  <form id="ctc_load_form" method="post" action="?page=<?php echo CHLD_THM_CFG_MENU; ?>">
8
  <?php
9
  wp_nonce_field( 'ctc_update' );
10
+ if ( has_action( 'chld_thm_cfg_controls' ) ):
11
+ if ( '' == $hidechild ) do_action( 'chld_thm_cfg_controls', $this->ctc() );
12
  $themeonly = 'style="display:none"';
13
  else:
14
  $themeonly = '';
17
  <div class="ctc-theme-only" <?php echo 'theme' != $configtype ? $themeonly : ''; ?>>
18
  <div class="ctc-input-row clearfix" id="input_row_parnt">
19
  <div class="ctc-input-cell"> <strong>
20
+ <?php _e( 'Parent Theme', 'chld_thm_cfg' ); ?>
21
  </strong> </div>
22
  <div class="ctc-input-cell">
23
+ <?php $this->render_theme_menu( 'parnt', $parent ); ?>
24
  </div>
25
  </div>
26
  <div class="ctc-input-row clearfix" id="input_row_child">
27
  <div class="ctc-input-cell"> <strong>
28
+ <?php _e( 'Child Theme', 'chld_thm_cfg' ); ?>
29
  </strong> </div>
30
  <div class="ctc-input-cell">
31
  <input class="ctc-radio" id="ctc_child_type_new" name="ctc_child_type" type="radio" value="new"
32
+ <?php echo ( !empty( $hidechild ) ? 'checked' : '' ); ?>
33
  <?php echo $hidechild;?> />
34
  <label for="ctc_child_type_new">
35
+ <?php _e( 'Create New Child Theme', 'chld_thm_cfg' ); ?>
36
  </label>
37
  </div>
38
  <div class="ctc-input-cell">
39
  <input class="ctc-radio" id="ctc_child_type_existing" name="ctc_child_type" type="radio" value="existing"
40
+ <?php echo ( empty( $hidechild ) ? 'checked' : '' ); ?>
41
  <?php echo $hidechild; ?>/>
42
  &nbsp;
43
  <label for="ctc_child_type_existing" <?php echo $hidechild;?>>
44
+ <?php _e( 'Use Existing Child Theme', 'chld_thm_cfg' ); ?>
45
  </label>
46
  </div>
47
  <div class="ctc-input-cell" style="clear:both"> <strong>&nbsp;</strong> </div>
48
  <div class="ctc-input-cell" >
49
+ <input class="ctc_text" id="ctc_child_template" name="ctc_child_template" type="text" placeholder="<?php _e( 'Theme Slug', 'chld_thm_cfg' ); ?>" autocomplete="off"/>
50
  </div>
51
+ <?php if ( '' == $hidechild ): ?>
52
  <div class="ctc-input-cell">
53
+ <?php $this->render_theme_menu( 'child', $child ); ?>
54
  </div>
55
  <?php endif; ?>
56
  </div>
57
  <div class="ctc-input-row clearfix" id="input_row_child_name">
58
  <div class="ctc-input-cell"> <strong>
59
+ <?php _e( 'Child Theme Name', 'chld_thm_cfg' ); ?>
60
  </strong> </div>
61
  <div class="ctc-input-cell">
62
  <input class="ctc_text" id="ctc_child_name" name="ctc_child_name" type="text"
63
+ value="<?php echo esc_attr( $css->get_prop( 'child_name' ) ); ?>" placeholder="<?php _e( 'Theme Name', 'chld_thm_cfg' ); ?>" autocomplete="off" />
64
  </div>
65
  </div>
66
  </div>
67
  <div class="ctc-input-row clearfix">
68
  <div class="ctc-input-cell"> <strong>
69
+ <?php _e( 'Author', 'chld_thm_cfg' ); ?>
70
  </strong> </div>
71
  <div class="ctc-input-cell">
72
  <input class="ctc_text" id="ctc_child_author" name="ctc_child_author" type="text"
73
+ value="<?php echo esc_attr( $css->get_prop( 'author' ) ); ?>" placeholder="<?php _e( 'Author', 'chld_thm_cfg' ); ?>" autocomplete="off" />
74
  </div>
75
  </div>
76
  <div class="ctc-input-row clearfix">
77
  <div class="ctc-input-cell"> <strong>
78
+ <?php _e( 'Version', 'chld_thm_cfg' ); ?>
79
  </strong> </div>
80
  <div class="ctc-input-cell">
81
  <input class="ctc_text" id="ctc_child_version" name="ctc_child_version" type="text"
82
+ value="<?php echo esc_attr( $css->get_prop( 'version' ) ); ?>" placeholder="<?php _e( 'Version', 'chld_thm_cfg' ); ?>" autocomplete="off" />
83
  </div>
84
  </div>
85
  <div class="ctc-theme-only" <?php echo 'theme' != $configtype ? $themeonly : ''; ?>>
86
  <div class="ctc-input-row clearfix">
87
  <div class="ctc-input-cell"> <strong>
88
+ <?php _e( 'Copy Parent Theme Menus, Widgets and other Options', 'chld_thm_cfg' ); ?>
89
  </strong> </div>
90
  <div class="ctc-input-cell">
91
  <input class="ctc_checkbox" id="ctc_parent_mods" name="ctc_parent_mods" type="checkbox"
92
  value="1" />
93
  </div>
94
  <div class="ctc-input-cell"> <strong>
95
+ <?php _e( 'NOTE:', 'chld_thm_cfg' ); ?>
96
  </strong>
97
+ <?php _e( 'This will overwrite child theme options you may have already set.', 'chld_thm_cfg' ); ?>
98
  </div>
99
  </div>
100
+ <?php if ( '' == $hidechild ): ?>
101
  <div class="ctc-input-row clearfix">
102
  <div class="ctc-input-cell"> <strong>
103
+ <?php _e( 'Backup current stylesheet', 'chld_thm_cfg' ); ?>
104
  </strong> </div>
105
  <div class="ctc-input-cell">
106
  <input class="ctc_checkbox" id="ctc_backup" name="ctc_backup" type="checkbox"
107
  value="1" />
108
  </div>
109
  <div class="ctc-input-cell"> <strong>
110
+ <?php _e( 'NOTE:', 'chld_thm_cfg' ); ?>
111
  </strong>
112
+ <?php _e( 'This creates a copy of the current stylesheet before applying changes. You can remove old backup files using the Files tab.', 'chld_thm_cfg' ); ?>
113
  </div>
114
  </div>
115
  <?php
116
  endif;
117
+ $parent_handling = ( isset( $css->enqueue ) ? $css->enqueue : ( $mustimport ? 'import' : 'enqueue' ) );
118
  ?>
119
  <div class="ctc-input-row clearfix">
120
  <div class="ctc-input-cell"> <strong>
121
+ <?php _e( 'Parent stylesheet handling:', 'chld_thm_cfg' ); ?>
122
  </strong> </div>
123
  <div class="ctc-input-cell">
124
  <label>
125
  <input class="ctc_radio" id="ctc_parent_enqueue_enqueue" name="ctc_parent_enqueue" type="radio"
126
+ value="enqueue" <?php checked( 'enqueue', $parent_handling ); ?>/>
127
+ <?php _e( '&lt;link&gt; (default)', 'chld_thm_cfg' ); ?>
128
  </label>
129
  <br/>
130
  <label>
131
  <input class="ctc_radio" id="ctc_parent_enqueue_import" name="ctc_parent_enqueue" type="radio"
132
+ value="import" <?php checked( 'import', $parent_handling ); ?>/>
133
+ <?php _e( '@import', 'chld_thm_cfg' ); ?>
134
  </label>
135
  <br/>
136
  <label>
137
  <input class="ctc_radio" id="ctc_parent_enqueue_none" name="ctc_parent_enqueue" type="radio"
138
+ value="none" <?php checked( 'none', $parent_handling ); ?>/>
139
+ <?php _e( 'none (handled by theme)', 'chld_thm_cfg' ); ?>
140
  </label>
141
  </div>
142
  <div class="ctc-input-cell"> <strong>
143
+ <?php _e( 'NOTE:', 'chld_thm_cfg' ); ?>
144
+ <?php if ( $mustimport ) _e( 'This theme does not currently enqueue the stylesheet and must use the @import option to render correctly.', 'chld_thm_cfg' ); ?>
145
  </strong>
146
+ <?php _e( "Select @import for older themes that do not enqueue the stylesheet. Select 'none' if core styles are automatically loaded for child themes. Select '&lt;link&gt;' if unsure.", 'chld_thm_cfg' ); ?>
147
  </div>
148
  </div>
149
+ <?php if ( '' == $hidechild ): ?>
150
  <div class="ctc-input-row clearfix">
151
  <div class="ctc-input-cell ctc-section-toggle" id="ctc_revert_css"> <strong>
152
+ <?php _e( 'Reset/Restore from backup:', 'chld_thm_cfg' ); ?>
153
  </strong> </div>
154
  <div class="ctc-input-cell-wide ctc-section-toggle-content" id="ctc_revert_css_content">
155
  <label>
156
  <input class="ctc_checkbox" id="ctc_revert_none" name="ctc_revert" type="radio"
157
  value="" checked="" />
158
+ <?php _e( 'Leave unchanged', 'chld_thm_cfg' );?>
159
  </label>
160
  <br/>
161
  <label>
162
  <input class="ctc_checkbox" id="ctc_revert_all" name="ctc_revert" type="radio"
163
  value="all" />
164
+ <?php _e( 'Reset all', 'chld_thm_cfg' );?>
165
  </label>
166
  <br/>
167
  <?php
168
+ foreach ( $this->ctc()->get_files( $child, 'backup' ) as $backup => $label ): ?>
169
  <label>
170
  <input class="ctc_checkbox" id="ctc_revert_<?php echo $backup; ?>" name="ctc_revert" type="radio"
171
  value="<?php echo $backup; ?>" />
172
+ <?php echo __( 'Restore backup from', 'chld_thm_cfg' ) . ' ' . $label; ?></label>
173
  <br/>
174
  <?php
175
  endforeach;
177
  </div>
178
  </div>
179
  <?php endif;
180
+ $stylesheets = $this->ctc()->get_files( $parent, 'stylesheet' );
181
+ if ( count( $stylesheets ) ):?>
182
  <div class="ctc-input-row clearfix">
183
  <div class="ctc-input-cell ctc-section-toggle" id="ctc_additional_css_files"> <strong>
184
+ <?php _e( 'Parse additional stylesheets:', 'chld_thm_cfg' ); ?>
185
  </strong> </div>
186
  <div class="ctc-input-cell-wide ctc-section-toggle-content" id="ctc_additional_css_files_content">
187
  <p style="margin-top:0">
188
+ <?php _e( 'Stylesheets that are currently being loaded by the parent theme are automatically selected below (except for Bootstrap stylesheets which add a large amount data to the configuration). To further reduce overhead, select only the additional stylesheets you wish to customize.', 'chld_thm_cfg' ); ?>
189
  </p>
190
  <?php
191
+ foreach ( $stylesheets as $stylesheet ): ?>
192
  <div class="ctc-input-cell">
193
  <label>
194
  <input class="ctc_checkbox" name="ctc_additional_css[]" type="checkbox"
204
  <div class="ctc-input-cell"> <strong>&nbsp;</strong> </div>
205
  <div class="ctc-input-cell">
206
  <input class="ctc_submit button button-primary" id="ctc_load_styles" name="ctc_load_styles" type="submit"
207
+ value="<?php _e( 'Generate/Rebuild Child Theme Files', 'chld_thm_cfg' ); ?>" disabled />
208
  </div>
209
  </div>
210
  </form>
includes/forms/query-selector.php CHANGED
@@ -1,5 +1,5 @@
1
  <?php
2
- if (!defined('ABSPATH')) exit;
3
  // Query/Selector Panel
4
  ?>
5
 
@@ -8,7 +8,7 @@ if (!defined('ABSPATH')) exit;
8
  <form id="ctc_query_selector_form" method="post" action="?page=<?php echo CHLD_THM_CFG_MENU; ?>">
9
  <div class="ctc-input-row clearfix" id="input_row_query">
10
  <div class="ctc-input-cell"> <strong>
11
- <?php _e('Query', 'chld_thm_cfg'); ?>
12
  </strong> </div>
13
  <div class="ctc-input-cell" id="ctc_sel_ovrd_query_selected">&nbsp;</div>
14
  <div class="ctc-input-cell">
@@ -19,7 +19,7 @@ if (!defined('ABSPATH')) exit;
19
  </div>
20
  <div class="ctc-input-row clearfix" id="input_row_selector">
21
  <div class="ctc-input-cell"> <strong>
22
- <?php _e('Selector', 'chld_thm_cfg'); ?>
23
  </strong> <a href="#" class="ctc-rewrite-toggle"></a></div>
24
  <div class="ctc-input-cell" id="ctc_sel_ovrd_selector_selected">&nbsp;</div>
25
  <div class="ctc-input-cell">
@@ -32,7 +32,7 @@ if (!defined('ABSPATH')) exit;
32
  <div class="ctc-selector-row clearfix" id="ctc_sel_ovrd_rule_inputs_container" style="display:none">
33
  <div class="ctc-input-row clearfix">
34
  <div class="ctc-input-cell"><strong>
35
- <?php _e('Sample', 'chld_thm_cfg'); ?>
36
  </strong></div>
37
  <div class="ctc-input-cell clearfix" style="max-height:150px;overflow:hidden">
38
  <div class="ctc-swatch" id="ctc_child_all_0_swatch"><?php echo $this->ctc()->swatch_text; ?></div>
@@ -40,26 +40,26 @@ if (!defined('ABSPATH')) exit;
40
  <div id="ctc_status_sel_val"></div>
41
  <div class="ctc-input-cell ctc-button-cell" id="ctc_save_query_selector_cell">
42
  <input type="button" class="button button-primary ctc-save-input" id="ctc_save_query_selector"
43
- name="ctc_save_query_selector" value="<?php _e('Save', 'chld_thm_cfg'); ?>" disabled />
44
  <input type="hidden" id="ctc_sel_ovrd_qsid"
45
  name="ctc_sel_ovrd_qsid" value="" />
46
  </div>
47
  </div>
48
  <div class="ctc-input-row clearfix" id="ctc_sel_ovrd_rule_header" style="display:none">
49
  <div class="ctc-input-cell"> <strong>
50
- <?php _e('Rule', 'chld_thm_cfg'); ?>
51
  </strong> </div>
52
  <div class="ctc-input-cell"> <strong>
53
- <?php _e('Parent Value', 'chld_thm_cfg'); ?>
54
  </strong> </div>
55
  <div class="ctc-input-cell"> <strong>
56
- <?php _e('Child Value', 'chld_thm_cfg'); ?>
57
  </strong> </div>
58
  </div>
59
  <div id="ctc_sel_ovrd_rule_inputs" style="display:none"> </div>
60
  <div class="ctc-input-row clearfix" id="ctc_sel_ovrd_new_rule" style="display:none">
61
  <div class="ctc-input-cell"> <strong>
62
- <?php _e('New Rule', 'chld_thm_cfg'); ?>
63
  </strong> </div>
64
  <div class="ctc-input-cell">
65
  <div class="ui-widget">
@@ -69,7 +69,7 @@ if (!defined('ABSPATH')) exit;
69
  </div>
70
  <div class="ctc-input-row clearfix" id="input_row_selector">
71
  <div class="ctc-input-cell"> <strong>
72
- <?php _e('Order', 'chld_thm_cfg'); ?>
73
  </strong> </div>
74
  <div class="ctc-input-cell" id="ctc_child_load_order_container">&nbsp;</div>
75
  </div>
@@ -78,12 +78,12 @@ if (!defined('ABSPATH')) exit;
78
  <div class="ctc-input-cell">
79
  <div class="ctc-textarea-button-cell" id="ctc_save_query_selector_cell">
80
  <input type="button" class="button ctc-save-input" id="ctc_save_new_selectors"
81
- name="ctc_save_new_selectors" value="<?php _e('Save', 'chld_thm_cfg'); ?>" disabled />
82
  </div>
83
  <strong>
84
- <?php _e('Raw CSS', 'chld_thm_cfg'); ?>
85
  </strong>
86
- <p><?php _e('Use to enter shorthand CSS or new @media queries and selectors.', 'chld_thm_cfg');?></p><p><?php _e('Values entered here are merged into existing child styles or added to the child stylesheet if they do not exist in the parent.', 'chld_thm_cfg'); ?></p>
87
  </div>
88
  <div class="ctc-input-cell-wide">
89
  <textarea id="ctc_new_selectors" name="ctc_new_selectors" wrap="off"></textarea>
1
  <?php
2
+ if ( !defined( 'ABSPATH' ) ) exit;
3
  // Query/Selector Panel
4
  ?>
5
 
8
  <form id="ctc_query_selector_form" method="post" action="?page=<?php echo CHLD_THM_CFG_MENU; ?>">
9
  <div class="ctc-input-row clearfix" id="input_row_query">
10
  <div class="ctc-input-cell"> <strong>
11
+ <?php _e( 'Query', 'chld_thm_cfg' ); ?>
12
  </strong> </div>
13
  <div class="ctc-input-cell" id="ctc_sel_ovrd_query_selected">&nbsp;</div>
14
  <div class="ctc-input-cell">
19
  </div>
20
  <div class="ctc-input-row clearfix" id="input_row_selector">
21
  <div class="ctc-input-cell"> <strong>
22
+ <?php _e( 'Selector', 'chld_thm_cfg' ); ?>
23
  </strong> <a href="#" class="ctc-rewrite-toggle"></a></div>
24
  <div class="ctc-input-cell" id="ctc_sel_ovrd_selector_selected">&nbsp;</div>
25
  <div class="ctc-input-cell">
32
  <div class="ctc-selector-row clearfix" id="ctc_sel_ovrd_rule_inputs_container" style="display:none">
33
  <div class="ctc-input-row clearfix">
34
  <div class="ctc-input-cell"><strong>
35
+ <?php _e( 'Sample', 'chld_thm_cfg' ); ?>
36
  </strong></div>
37
  <div class="ctc-input-cell clearfix" style="max-height:150px;overflow:hidden">
38
  <div class="ctc-swatch" id="ctc_child_all_0_swatch"><?php echo $this->ctc()->swatch_text; ?></div>
40
  <div id="ctc_status_sel_val"></div>
41
  <div class="ctc-input-cell ctc-button-cell" id="ctc_save_query_selector_cell">
42
  <input type="button" class="button button-primary ctc-save-input" id="ctc_save_query_selector"
43
+ name="ctc_save_query_selector" value="<?php _e( 'Save', 'chld_thm_cfg' ); ?>" disabled />
44
  <input type="hidden" id="ctc_sel_ovrd_qsid"
45
  name="ctc_sel_ovrd_qsid" value="" />
46
  </div>
47
  </div>
48
  <div class="ctc-input-row clearfix" id="ctc_sel_ovrd_rule_header" style="display:none">
49
  <div class="ctc-input-cell"> <strong>
50
+ <?php _e( 'Rule', 'chld_thm_cfg' ); ?>
51
  </strong> </div>
52
  <div class="ctc-input-cell"> <strong>
53
+ <?php _e( 'Parent Value', 'chld_thm_cfg' ); ?>
54
  </strong> </div>
55
  <div class="ctc-input-cell"> <strong>
56
+ <?php _e( 'Child Value', 'chld_thm_cfg' ); ?>
57
  </strong> </div>
58
  </div>
59
  <div id="ctc_sel_ovrd_rule_inputs" style="display:none"> </div>
60
  <div class="ctc-input-row clearfix" id="ctc_sel_ovrd_new_rule" style="display:none">
61
  <div class="ctc-input-cell"> <strong>
62
+ <?php _e( 'New Rule', 'chld_thm_cfg' ); ?>
63
  </strong> </div>
64
  <div class="ctc-input-cell">
65
  <div class="ui-widget">
69
  </div>
70
  <div class="ctc-input-row clearfix" id="input_row_selector">
71
  <div class="ctc-input-cell"> <strong>
72
+ <?php _e( 'Order', 'chld_thm_cfg' ); ?>
73
  </strong> </div>
74
  <div class="ctc-input-cell" id="ctc_child_load_order_container">&nbsp;</div>
75
  </div>
78
  <div class="ctc-input-cell">
79
  <div class="ctc-textarea-button-cell" id="ctc_save_query_selector_cell">
80
  <input type="button" class="button ctc-save-input" id="ctc_save_new_selectors"
81
+ name="ctc_save_new_selectors" value="<?php _e( 'Save', 'chld_thm_cfg' ); ?>" disabled />
82
  </div>
83
  <strong>
84
+ <?php _e( 'Raw CSS', 'chld_thm_cfg' ); ?>
85
  </strong>
86
+ <p><?php _e( 'Use to enter shorthand CSS or new @media queries and selectors.', 'chld_thm_cfg' );?></p><p><?php _e( 'Values entered here are merged into existing child styles or added to the child stylesheet if they do not exist in the parent.', 'chld_thm_cfg' ); ?></p>
87
  </div>
88
  <div class="ctc-input-cell-wide">
89
  <textarea id="ctc_new_selectors" name="ctc_new_selectors" wrap="off"></textarea>
includes/forms/related.php CHANGED
@@ -1 +1 @@
1
- <span style="float:right;margin-left:2em;margin-top:-6px;text-shadow:1px 2px 0 #fff"><strong><?php _e('New user?','chld_thm_cfg'); ?> <span style="color:#F1823B"><?php _e('Click help', 'chld_thm_cfg'); ?></span></strong> <i class="dashicons dashicons-arrow-right-alt" style="color:#F1823B"></i></span><a href="http://www.lilaeamedia.com/plugins/child-theme-plugin-styles/" target="_blank" title="<?php _e('Get CTC for Plugins and other tools', 'chld_thm_cfg'); ?>" style="float:right"><img src="<?php echo $this->ctc()->pluginURL; ?>/css/lilaea-logo.png" height="36" width="145" alt="<?php _e('Lilaea Media - Responsive Tools for a Mobile World', 'chld_thm_cfg'); ?>" /></a>
1
+ <span style="float:right;margin-left:2em;margin-top:-6px;text-shadow:1px 2px 0 #fff"><strong><?php _e( 'New user?','chld_thm_cfg' ); ?> <span style="color:#F1823B"><?php _e( 'Click help', 'chld_thm_cfg' ); ?></span></strong> <i class="dashicons dashicons-arrow-right-alt" style="color:#F1823B"></i></span><a href="http://www.lilaeamedia.com/plugins/child-theme-plugin-styles/" target="_blank" title="<?php _e( 'Get CTC for Plugins and other tools', 'chld_thm_cfg' ); ?>" style="float:right"><img src="<?php echo $this->ctc()->pluginURL; ?>/css/lilaea-logo.png" height="36" width="145" alt="<?php _e( 'Lilaea Media - Responsive Tools for a Mobile World', 'chld_thm_cfg' ); ?>" /></a>
includes/forms/rule-value.php CHANGED
@@ -1,5 +1,5 @@
1
  <?php
2
- if (!defined('ABSPATH')) exit;
3
  // Rule/Value Panel
4
  ?>
5
 
@@ -9,7 +9,7 @@ if (!defined('ABSPATH')) exit;
9
  <?php wp_nonce_field( 'ctc_update' ); ?>
10
  <div class="ctc-input-row clearfix" id="ctc_input_row_rule_menu">
11
  <div class="ctc-input-cell"> <strong>
12
- <?php _e('Rule', 'chld_thm_cfg'); ?>
13
  </strong> </div>
14
  <div class="ctc-input-cell" id="ctc_rule_menu_selected">&nbsp;</div>
15
  <div id="ctc_status_rule_val"></div>
@@ -22,13 +22,13 @@ if (!defined('ABSPATH')) exit;
22
  </div>
23
  <div class="ctc-input-row clearfix" id="ctc_input_row_rule_header" style="display:none">
24
  <div class="ctc-input-cell"> <strong>
25
- <?php _e('Value', 'chld_thm_cfg'); ?>
26
  </strong> </div>
27
  <div class="ctc-input-cell"> <strong>
28
- <?php _e('Sample', 'chld_thm_cfg'); ?>
29
  </strong> </div>
30
  <div class="ctc-input-cell"> <strong>
31
- <?php _e('Selectors', 'chld_thm_cfg'); ?>
32
  </strong> </div>
33
  </div>
34
  <div class="ctc-rule-value-input-container clearfix" id="ctc_rule_value_inputs" style="display:none"> </div>
1
  <?php
2
+ if ( !defined( 'ABSPATH' ) ) exit;
3
  // Rule/Value Panel
4
  ?>
5
 
9
  <?php wp_nonce_field( 'ctc_update' ); ?>
10
  <div class="ctc-input-row clearfix" id="ctc_input_row_rule_menu">
11
  <div class="ctc-input-cell"> <strong>
12
+ <?php _e( 'Rule', 'chld_thm_cfg' ); ?>
13
  </strong> </div>
14
  <div class="ctc-input-cell" id="ctc_rule_menu_selected">&nbsp;</div>
15
  <div id="ctc_status_rule_val"></div>
22
  </div>
23
  <div class="ctc-input-row clearfix" id="ctc_input_row_rule_header" style="display:none">
24
  <div class="ctc-input-cell"> <strong>
25
+ <?php _e( 'Value', 'chld_thm_cfg' ); ?>
26
  </strong> </div>
27
  <div class="ctc-input-cell"> <strong>
28
+ <?php _e( 'Sample', 'chld_thm_cfg' ); ?>
29
  </strong> </div>
30
  <div class="ctc-input-cell"> <strong>
31
+ <?php _e( 'Selectors', 'chld_thm_cfg' ); ?>
32
  </strong> </div>
33
  </div>
34
  <div class="ctc-rule-value-input-container clearfix" id="ctc_rule_value_inputs" style="display:none"> </div>
includes/forms/tabs.php CHANGED
@@ -1,5 +1,5 @@
1
  <?php
2
- if (!defined('ABSPATH')) exit;
3
  // Tabs Bar
4
 
5
  $active_tab = isset( $_GET[ 'tab' ] ) ? $_GET[ 'tab' ] : 'parent_child_options';
@@ -8,33 +8,33 @@ $active_tab = isset( $_GET[ 'tab' ] ) ? $_GET[ 'tab' ] : 'parent_child_options';
8
  <h2 class="nav-tab-wrapper">
9
  <a id="parent_child_options" href=""
10
  class="nav-tab<?php echo 'parent_child_options' == $active_tab ? ' nav-tab-active' : ''; ?>">
11
- <?php _e('Parent/Child', 'chld_thm_cfg'); ?>
12
  </a>
13
- <?php if ($enqueueset): ?>
14
  <!----><a id="query_selector_options" href=""
15
  class="nav-tab<?php echo 'query_selector_options' == $active_tab ? ' nav-tab-active' : ''; ?>" <?php echo $hidechild; ?>>
16
- <?php _e('Query/Selector', 'chld_thm_cfg'); ?>
17
  </a><!----><a id="rule_value_options" href=""
18
  class="nav-tab<?php echo 'rule_value_options' == $active_tab ? ' nav-tab-active' : ''; ?>" <?php echo $hidechild; ?>>
19
- <?php _e('Rule/Value', 'chld_thm_cfg'); ?>
20
  </a><!----><a id="import_options" href=""
21
  class="nav-tab<?php echo 'import_options' == $active_tab ? ' nav-tab-active' : ''; ?>" <?php echo $hidechild; ?>>
22
- <?php _e('@import', 'chld_thm_cfg'); ?>
23
  </a><!----><a id="view_child_options" href=""
24
  class="nav-tab<?php echo 'view_child_options' == $active_tab ? ' nav-tab-active' : ''; ?>" <?php echo $hidechild; ?>>
25
- <?php _e('Child CSS', 'chld_thm_cfg'); ?>
26
  </a><!----><a id="view_parnt_options" href=""
27
  class="nav-tab<?php echo 'view_parnt_options' == $active_tab ? ' nav-tab-active' : ''; ?>" <?php echo $hidechild; ?>>
28
- <?php _e('Parent CSS', 'chld_thm_cfg'); ?>
29
  </a>
30
  <?php
31
- if ('' == $hidechild):
32
  ?>
33
  <a id="file_options" href=""
34
  class="nav-tab<?php echo 'file_options' == $active_tab ? ' nav-tab-active' : ''; ?>" <?php echo $hidechild; ?>>
35
- <?php _e('Files', 'chld_thm_cfg'); ?>
36
  </a>
37
  <?php
38
  endif;
39
  endif;
40
- do_action('chld_thm_cfg_tabs', $this->ctc(), $active_tab, $hidechild); ?>
1
  <?php
2
+ if ( !defined( 'ABSPATH' ) ) exit;
3
  // Tabs Bar
4
 
5
  $active_tab = isset( $_GET[ 'tab' ] ) ? $_GET[ 'tab' ] : 'parent_child_options';
8
  <h2 class="nav-tab-wrapper">
9
  <a id="parent_child_options" href=""
10
  class="nav-tab<?php echo 'parent_child_options' == $active_tab ? ' nav-tab-active' : ''; ?>">
11
+ <?php _e( 'Parent/Child', 'chld_thm_cfg' ); ?>
12
  </a>
13
+ <?php if ( $enqueueset ): ?>
14
  <!----><a id="query_selector_options" href=""
15
  class="nav-tab<?php echo 'query_selector_options' == $active_tab ? ' nav-tab-active' : ''; ?>" <?php echo $hidechild; ?>>
16
+ <?php _e( 'Query/Selector', 'chld_thm_cfg' ); ?>
17
  </a><!----><a id="rule_value_options" href=""
18
  class="nav-tab<?php echo 'rule_value_options' == $active_tab ? ' nav-tab-active' : ''; ?>" <?php echo $hidechild; ?>>
19
+ <?php _e( 'Rule/Value', 'chld_thm_cfg' ); ?>
20
  </a><!----><a id="import_options" href=""
21
  class="nav-tab<?php echo 'import_options' == $active_tab ? ' nav-tab-active' : ''; ?>" <?php echo $hidechild; ?>>
22
+ <?php _e( '@import', 'chld_thm_cfg' ); ?>
23
  </a><!----><a id="view_child_options" href=""
24
  class="nav-tab<?php echo 'view_child_options' == $active_tab ? ' nav-tab-active' : ''; ?>" <?php echo $hidechild; ?>>
25
+ <?php _e( 'Child CSS', 'chld_thm_cfg' ); ?>
26
  </a><!----><a id="view_parnt_options" href=""
27
  class="nav-tab<?php echo 'view_parnt_options' == $active_tab ? ' nav-tab-active' : ''; ?>" <?php echo $hidechild; ?>>
28
+ <?php _e( 'Parent CSS', 'chld_thm_cfg' ); ?>
29
  </a>
30
  <?php
31
+ if ( '' == $hidechild ):
32
  ?>
33
  <a id="file_options" href=""
34
  class="nav-tab<?php echo 'file_options' == $active_tab ? ' nav-tab-active' : ''; ?>" <?php echo $hidechild; ?>>
35
+ <?php _e( 'Files', 'chld_thm_cfg' ); ?>
36
  </a>
37
  <?php
38
  endif;
39
  endif;
40
+ do_action( 'chld_thm_cfg_tabs', $this->ctc(), $active_tab, $hidechild ); ?>
includes/forms/themepreview.php CHANGED
@@ -1,5 +1,5 @@
1
  <?php
2
- if (!defined('ABSPATH')) exit;
3
  // Theme Preview
4
  // Renders localized version of theme preview that is merged into
5
  // jQuery selectmenu object for parent and child theme options
@@ -9,11 +9,15 @@ if (!defined('ABSPATH')) exit;
9
  <div class="ctc-theme-option-left"><img src="<?php echo $theme['screenshot']; ?>" class="ctc-theme-option-image"/></div>
10
  <div class="ctc-theme-option-right">
11
  <h3 class="theme-name"><?php echo $theme['Name']; ?></h3>
12
- <?php _e('Version: ', 'chld_thm_cfg'); echo esc_attr($theme['Version']);?>
13
  <br/>
14
- <?php _e('By: ', 'chld_thm_cfg'); echo esc_attr($theme['Author']);?>
15
- <br/>
16
- <a href="<?php echo admin_url('/customize.php?theme=' . $slug);?>" title="<?php _e('Live Preview', 'chld_thm_cfg'); ?>" class="ctc-live-preview">
17
- <?php _e('Live Preview', 'chld_thm_cfg');?>
18
- </a></div>
 
 
 
 
19
  </div>
1
  <?php
2
+ if ( !defined( 'ABSPATH' ) ) exit;
3
  // Theme Preview
4
  // Renders localized version of theme preview that is merged into
5
  // jQuery selectmenu object for parent and child theme options
9
  <div class="ctc-theme-option-left"><img src="<?php echo $theme['screenshot']; ?>" class="ctc-theme-option-image"/></div>
10
  <div class="ctc-theme-option-right">
11
  <h3 class="theme-name"><?php echo $theme['Name']; ?></h3>
12
+ <?php _e( 'Version: ', 'chld_thm_cfg' ); echo esc_attr( $theme['Version'] );?>
13
  <br/>
14
+ <?php _e( 'By: ', 'chld_thm_cfg' ); echo esc_attr( $theme['Author'] );?>
15
+ <br/><?php if ( !is_multisite() || $theme['allowed'] ): ?>
16
+ <a href="<?php echo admin_url( '/customize.php?theme=' . $slug );?>" title="<?php _e( 'Preview', 'chld_thm_cfg' );
17
+ if ( is_multisite() ) _e(' in default Site', 'chld_thm_cfg'); ?>" class="ctc-live-preview" target="_blank">
18
+ <?php _e( 'Live Preview', 'chld_thm_cfg' ); ?>
19
+ </a><?php else: ?>
20
+ <a href="<?php echo network_admin_url( '/themes.php?theme=' . $slug );?>" title="<?php _e( 'Go to Themes', 'chld_thm_cfg' ); ?>" class="ctc-live-preview">
21
+ <?php _e( 'Not Network Enabled', 'chld_thm_cfg' );?>
22
+ </a><?php endif; ?></div>
23
  </div>
includes/help/help_en_US.php CHANGED
@@ -1,5 +1,5 @@
1
  <?php
2
- if (!defined('ABSPATH')) exit;
3
  // Help Content
4
  ?>
5
  <!-- BEGIN tab -->
@@ -93,7 +93,7 @@ if (!defined('ABSPATH')) exit;
93
  <li>Click "Live Preview" below the new Child Theme to see it in action.</li>
94
  <li>When you are ready to take the Child Theme live, click "Activate."</li>
95
  </ol>
96
- <p>You can also click the Child or Parent CSS tab to reference the stylesheet code.</p>
97
  <!-- END tab -->
98
  <!-- BEGIN tab -->
99
  <h3 id="ctc_permissions">File Permissions</h3>
@@ -142,15 +142,6 @@ if (!defined('ABSPATH')) exit;
142
  <h5 id="web_fonts">How do I add Web Fonts?</h5>
143
  <p>The easiest method is to paste the @import code provided by <a href="http://www.google.com/fonts" title="Google Fonts">Google</a>, <a href="http://www.fontsquirrel.com/" title="Font Squirrel">Font Squirrel</a> or any other Web Font site into the @import tab. The fonts will then be available to use as a value of the <strong>font-family</strong> rule. Be sure you understand the license for any embedded fonts. </p>
144
  <p>You can also create a secondary stylesheet that contains @font-face rules and import it using the @import tab. </p>
145
- <h5 id="multisite">Does it work with Multi site?</h5>
146
- <strong>NOTE: Only users with "edit_theme_options" capability will have access to the Child Theme Configurator.</strong> Using with WordPress Network sites requires additional steps:
147
- <ol class="instructions"><li>Install as Network Admin and Network Enable the Plugin.</li>
148
- <li>Go to the site you want to customize.</li>
149
- <li>Go to Tools > Child Themes and configure a child theme for the parent theme you want to use
150
- <li>Go back to Network Admin and Network Enable the new Child theme.</li>
151
- <li>Go back the site and activate the child theme.</li></ol>
152
- Now you can edit your child theme from Tools > Child Themes. </p>
153
-
154
  <h5 id="plugin">Does it work with plugins?</h5>
155
  <p>We offer a premium extension to let you easily modify styles for any WordPress Plugin installed on your website. The Child Theme Configurator Plugin Extension scans your plugins and allows you to create custom stylesheets in your Child Theme. <a href="http://www.lilaeamedia.com/plugins/child-theme-plugin-styles/" title="Take Control of your Plugin Styles with Child Theme Configurator Plugin Extension for WordPress">Learn more <i class="genericon genericon-next"></i></a>
156
  <h5 id="doesnt_work">Why doesn't this work with my (insert theme vendor here) theme?</h5>
@@ -158,9 +149,6 @@ Now you can edit your child theme from Tools > Child Themes. </p>
158
  <p>Contact the vendor directly to ask for this core functionality. It is our opinion that ALL themes (especially commercial ones) must pass the Theme Unit Tests outlined by WordPress.org. </p>
159
  <h5 id="missing_parent">Why doesn't the Parent Theme have any styles when I "View Parent CSS"?</h5>
160
  <p>Your Parent theme is probably using a separate location for the stylesheets. Select individual stylesheets from the "Parse Additional Stylesheets" section of the Parent/Child tab and click "Generate Child Theme Files" again. </p>
161
- <h5 id="where_is_it">Where is it in the Admin?</h5>
162
- <p><strong>NOTE: Only users with "edit_theme_options" capability will have access to the Child Theme Configurator.</strong> The Child Theme Configurator can be found under the "Tools" menu in the WordPress Admin. Click "Child Themes" to get started.</p>
163
- <p>Click the "Help" tab at the top right for a quick reference. </p>
164
  <h5 id="performance">Will this slow down my site?</h5>
165
  Once the child theme stylesheet is created, CTC adds very little overhead to the front-end since all of the functionality is in the admin.
166
 
1
  <?php
2
+ if ( !defined( 'ABSPATH' ) ) exit;
3
  // Help Content
4
  ?>
5
  <!-- BEGIN tab -->
93
  <li>Click "Live Preview" below the new Child Theme to see it in action.</li>
94
  <li>When you are ready to take the Child Theme live, click "Activate."</li>
95
  </ol>
96
+ <p><strong>MULTISITE USERS:</strong> You must Network Enable your child theme before you can use Live Preview. Go to "Themes" in the Network Admin.</p>
97
  <!-- END tab -->
98
  <!-- BEGIN tab -->
99
  <h3 id="ctc_permissions">File Permissions</h3>
142
  <h5 id="web_fonts">How do I add Web Fonts?</h5>
143
  <p>The easiest method is to paste the @import code provided by <a href="http://www.google.com/fonts" title="Google Fonts">Google</a>, <a href="http://www.fontsquirrel.com/" title="Font Squirrel">Font Squirrel</a> or any other Web Font site into the @import tab. The fonts will then be available to use as a value of the <strong>font-family</strong> rule. Be sure you understand the license for any embedded fonts. </p>
144
  <p>You can also create a secondary stylesheet that contains @font-face rules and import it using the @import tab. </p>
 
 
 
 
 
 
 
 
 
145
  <h5 id="plugin">Does it work with plugins?</h5>
146
  <p>We offer a premium extension to let you easily modify styles for any WordPress Plugin installed on your website. The Child Theme Configurator Plugin Extension scans your plugins and allows you to create custom stylesheets in your Child Theme. <a href="http://www.lilaeamedia.com/plugins/child-theme-plugin-styles/" title="Take Control of your Plugin Styles with Child Theme Configurator Plugin Extension for WordPress">Learn more <i class="genericon genericon-next"></i></a>
147
  <h5 id="doesnt_work">Why doesn't this work with my (insert theme vendor here) theme?</h5>
149
  <p>Contact the vendor directly to ask for this core functionality. It is our opinion that ALL themes (especially commercial ones) must pass the Theme Unit Tests outlined by WordPress.org. </p>
150
  <h5 id="missing_parent">Why doesn't the Parent Theme have any styles when I "View Parent CSS"?</h5>
151
  <p>Your Parent theme is probably using a separate location for the stylesheets. Select individual stylesheets from the "Parse Additional Stylesheets" section of the Parent/Child tab and click "Generate Child Theme Files" again. </p>
 
 
 
152
  <h5 id="performance">Will this slow down my site?</h5>
153
  Once the child theme stylesheet is created, CTC adds very little overhead to the front-end since all of the functionality is in the admin.
154
 
js/chld-thm-cfg.js CHANGED
@@ -2,11 +2,11 @@
2
  * Script: chld-thm-cfg.js
3
  * Plugin URI: http://www.lilaeamedia.com/plugins/child-theme-configurator/
4
  * Description: Handles jQuery, AJAX and other UI
5
- * Version: 1.6.2.1
6
  * Author: Lilaea Media
7
  * Author URI: http://www.lilaeamedia.com/
8
  * License: GPLv2
9
- * Copyright (C) 2014 Lilaea Media
10
  */
11
  jQuery(document).ready(function($){
12
 
@@ -874,6 +874,7 @@ jQuery(document).ready(function($){
874
  additional;
875
  if (ctc_is_empty(template)) return;
876
  $.get(url, function(data){
 
877
  while (additional = regex.exec(data)){
878
  if ('style.css' == additional[1]) break; // bail after main stylesheet
879
  if (additional[1].match(/bootstrap/)) continue; // don't autoselect Bootstrap stylesheets
2
  * Script: chld-thm-cfg.js
3
  * Plugin URI: http://www.lilaeamedia.com/plugins/child-theme-configurator/
4
  * Description: Handles jQuery, AJAX and other UI
5
+ * Version: 1.6.3
6
  * Author: Lilaea Media
7
  * Author URI: http://www.lilaeamedia.com/
8
  * License: GPLv2
9
+ * Copyright (C) 2014-2015 Lilaea Media
10
  */
11
  jQuery(document).ready(function($){
12
 
874
  additional;
875
  if (ctc_is_empty(template)) return;
876
  $.get(url, function(data){
877
+ //console.log(data);
878
  while (additional = regex.exec(data)){
879
  if ('style.css' == additional[1]) break; // bail after main stylesheet
880
  if (additional[1].match(/bootstrap/)) continue; // don't autoselect Bootstrap stylesheets
readme.txt CHANGED
@@ -4,7 +4,7 @@ Donate link: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_i
4
  Tags: child theme, customize, CSS, responsive, css editor, theme generator, stylesheet, customizer
5
  Requires at least: 3.9
6
  Tested up to: 4.1
7
- Stable tag: 1.6.2.1
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
@@ -29,16 +29,16 @@ When you are ready, just activate the Child Theme and your WordPress site takes
29
  * Update themes without losing customizations
30
  * Easily copy widgets, menus and other options to a child theme
31
  * Save hours of development time
 
32
  * Make modifications above and beyond the theme Customizer
33
  * Enqueue (link) parent theme stylesheet instead of using @import
34
  * Export Child Theme as Zip Archive
35
  * Import web fonts and use them in place of theme fonts
36
  * Identify and override exact selectors from the parent theme
37
  * Change specific colors, backgrounds, font styles, etc., without changing other elements
38
- * Automatically create and preview CSS3 gradients
39
- * Automatically generate cross-browser and vendor-prefixed rules
40
  * Preview style changes before committing to them
41
- * Add and modify individual @media queries
42
  * Uses WP Filesystem API – will not create files you cannot remove
43
  * Nag-free, no-strings-attached user experience
44
 
@@ -80,6 +80,8 @@ Learn more at http://www.lilaeamedia.com/plugins/intelliwidget-responsive-menu
80
  * Browse for the zip file, select and click "Install."
81
 
82
  3. In the WordPress Admin, go to "Plugins > Installed Plugins." Locate "Child Theme Configurator" in the list and click "Activate."
 
 
83
 
84
  = 10 Easy Steps to Create a Child Theme =
85
 
@@ -109,6 +111,20 @@ Learn more at http://www.lilaeamedia.com/plugins/intelliwidget-responsive-menu
109
 
110
  == Frequently Asked Questions ==
111
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
112
  = HELP! I changed a file and now I am unable to access my website or login to wp-admin to fix it! =
113
 
114
  To back out of a broken child theme you have to manually rename the offending theme directory name (via FTP, SSH or your web host control panel file manager) so that WordPress can’t find it. WordPress will then throw an error and revert back to the default theme (currently twenty-fourteen).
@@ -144,18 +160,6 @@ The easiest method is to paste the @import code provided by Google, Font Squirre
144
 
145
  You can also create a secondary stylesheet that contains @font-face rules and import it using the @import tab.
146
 
147
- = Does it work with Multi site? =
148
-
149
- Using with WordPress Network sites requires additional steps:
150
-
151
- 1. Install as Network Admin and Network Enable the Plugin.
152
- 2. Go to the site you want to customize.
153
- 3. Go to Tools > Child Themes and configure a child theme for the parent theme you want to use
154
- 4. Go back to Network Admin and Network Enable the new Child theme.
155
- 5. Go back the site and activate the child theme.
156
-
157
- Now you can edit your child theme from Tools > Child Themes. NOTE: Only users with "edit_theme_options" capability will have access to the Child Theme Configurator.
158
-
159
  = Does it work with plugins? =
160
 
161
  We offer a premium extension that brings the CSS editing power of Child Theme Configurator to any WordPress Plugin installed on your website. The Child Theme Configurator Plugin Extension scans your plugins and creates custom CSS in your Child Theme. Learn more at http://www.lilaeamedia.com/plugins/child-theme-plugin-styles
@@ -182,12 +186,6 @@ Once the child theme stylesheet is created, CTC adds very little overhead to the
182
 
183
  Check the appropriate additional stylesheets under "Scan Parent Theme for additional stylesheets" on the Parent/Child tab and load the Child Theme again. CTC tries to identify these files by fetching a page from the parent theme, but you may need to set them manually.
184
 
185
- = Where is it in the Admin? =
186
-
187
- The Child Theme Configurator can be found under the "Tools" menu in the WordPress Admin. Click "Child Themes" to get started. NOTE: Only users with "edit_theme_options" capability will have access to the Child Theme Configurator.
188
-
189
- Click the "Help" tab at the top right for a quick reference.
190
-
191
  = Where are the styles? The configurator doesn't show anything! =
192
 
193
  All of the styles are loaded dynamically. You must start typing in the text boxes to select styles to edit.
@@ -207,7 +205,7 @@ You can make any manual changes you wish to the stylesheet. Just make sure you i
207
 
208
  No. This is the point of using child themes. Changes to the parent theme are automatically inherited by the child theme.
209
 
210
- A child theme is not a "copy" of the parent theme. It is a special feature of WordPress that let's you override specific styles and functions leaving the rest of the theme intact. The only time you need to make changes after an upgrade is if the parent removes or changes style or function names. Quality themes should identify any deprecated functions or styles in the upgrade notes so that child theme users can make adjustments accordingly.
211
 
212
  = Where are the .php files? =
213
 
@@ -261,6 +259,10 @@ https://www.youtube.com/watch?v=iBiiAgsK4G4
261
  7. Files tab
262
 
263
  == Changelog ==
 
 
 
 
264
 
265
  = 1.6.2.1 =
266
  * Fix: Regular expression introduced in version 1.6.2 parses selectors incorrectly.
@@ -300,7 +302,7 @@ https://www.youtube.com/watch?v=iBiiAgsK4G4
300
  * Added regex filter for non-printable (e.g., null) characters in input strings
301
 
302
  = 1.5.2.2 =
303
- * Fixed a bug introduced in v1.5.2(.1) that copied all of the parent styles to the child stylesheet. This should only be an issue for 'background-image' styles that reference images in the parent theme and do not have child theme overrides. If you need to remove all styles generated by this bug, install the development version, otherwise delete the redundant child values or just wait for the v1.5.3 release.
304
  * Rolled back changes to the javascript controller that introduced a number of type errors.
305
  * Tweaked preview ajax call to handle ssl.
306
 
@@ -312,12 +314,10 @@ https://www.youtube.com/watch?v=iBiiAgsK4G4
312
  * Added copy option to Parent/Child tab to assign menu locations, sidebars/widgets, custom header, background, and other options to the new Child Theme.
313
 
314
  = 1.5.0 =
315
- * We have completely refactored CTC to use the WP_Filesystem API.
316
- * If your web host is configured to use suExec (meaning it runs under the user of the web account being accessed), the changes will be completely transparent.
317
- * Other configurations will now require user credentials to add, remove or update Child Theme files.
318
- * To make things easier we added the ability for you to make the files writable while editing and then make them read-only when you are done.
319
  * You can also set your credentials in wp-config.php: http://codex.wordpress.org/Editing_wp-config.php#WordPress_Upgrade_Constants
320
- * Contact us at http://www.lilaeamedia.com/about/contact if you have any questions.
321
 
322
  = 1.4.8 =
323
  * Removed backreference in main CSS parser regex due to high memory usage.
@@ -442,15 +442,12 @@ https://www.youtube.com/watch?v=iBiiAgsK4G4
442
  * Clean up Parent/Child form UI and validation
443
  * Streamlined UI overall
444
 
445
- = 1.0.1 =
446
- * Updates to Readme.txt
447
-
448
  = 1.0.0 =
449
  * Initial release.
450
 
451
  == Upgrade Notice ==
452
 
453
- v.1.6.2.1 Fixes a bug in the stylesheet parser that was an unintended consequence of a "fix" introduced in v.1.6.2.
454
 
455
  == Override Parent Styles ==
456
 
@@ -527,9 +524,11 @@ Some themes (particularly commercial themes) do not correctly load parent templa
527
  2. Click "Live Preview" (theme customizer) below the new Child Theme to see it in action.
528
  3. When you are ready to take the Child Theme live, click "Activate."
529
 
 
 
530
  == Caveats ==
531
 
532
- * No legacy webkit-gradient. The Child Theme Configurator plugin does not support the legacy webkit gradient. If there is a demand, we will add it in a future release, but most Chrome and Safari users should have upgraded by now.
533
  * Only two-color gradients. The Child Theme Configurator plugin is powerful, but we have simplified the gradient interface. You can use any gradient you want as long as it has two colors and no intermediate stops.
534
  * No @font-face rules. The Child Theme Configurator plugin only supports @media and @import. If you need other @rules, put them in a separate stylesheet and import them into the Child Theme stylesheet.
535
  * Not all CSS rules are supported. The Child Theme Configurator plugin works with the vast majority of CSS rules, however we’ve left out some of the more obscure options.
@@ -539,4 +538,4 @@ Some themes (particularly commercial themes) do not correctly load parent templa
539
 
540
  Go to http://www.lilaeamedia.com/plugins/child-theme-configurator
541
 
542
- Copyright: (C) 2014 Lilaea Media
4
  Tags: child theme, customize, CSS, responsive, css editor, theme generator, stylesheet, customizer
5
  Requires at least: 3.9
6
  Tested up to: 4.1
7
+ Stable tag: 1.6.3
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
29
  * Update themes without losing customizations
30
  * Easily copy widgets, menus and other options to a child theme
31
  * Save hours of development time
32
+ * Multisite compatible - great for network sites
33
  * Make modifications above and beyond the theme Customizer
34
  * Enqueue (link) parent theme stylesheet instead of using @import
35
  * Export Child Theme as Zip Archive
36
  * Import web fonts and use them in place of theme fonts
37
  * Identify and override exact selectors from the parent theme
38
  * Change specific colors, backgrounds, font styles, etc., without changing other elements
39
+ * Automatically generate cross-browser and vendor-prefixed rules and CSS gradients
 
40
  * Preview style changes before committing to them
41
+ * Customize @media queries for responsive design
42
  * Uses WP Filesystem API – will not create files you cannot remove
43
  * Nag-free, no-strings-attached user experience
44
 
80
  * Browse for the zip file, select and click "Install."
81
 
82
  3. In the WordPress Admin, go to "Plugins > Installed Plugins." Locate "Child Theme Configurator" in the list and click "Activate."
83
+
84
+ 4. Navigate to Tools > Child Themes (multisite users go to Network Admin > Themes > Child Themes).
85
 
86
  = 10 Easy Steps to Create a Child Theme =
87
 
111
 
112
  == Frequently Asked Questions ==
113
 
114
+ = Where is Child Theme Configurator in the Admin? =
115
+
116
+ For most users the Child Theme Configurator can be found under "Tools > Child Themes."
117
+
118
+ WordPress Multisite (Network) users go to "Network Admin > Themes > Child Themes."
119
+
120
+ NOTE: Only users with "install_themes" capability will have access to the Child Theme Configurator.
121
+
122
+ Click the "Help" tab at the top right for a quick reference.
123
+
124
+ = Does it work with Multisite? =
125
+
126
+ Yes. Go to "Network Admin > Themes > Child Themes." Child themes must be "Network enabled" to preview and activate for Network sites.
127
+
128
  = HELP! I changed a file and now I am unable to access my website or login to wp-admin to fix it! =
129
 
130
  To back out of a broken child theme you have to manually rename the offending theme directory name (via FTP, SSH or your web host control panel file manager) so that WordPress can’t find it. WordPress will then throw an error and revert back to the default theme (currently twenty-fourteen).
160
 
161
  You can also create a secondary stylesheet that contains @font-face rules and import it using the @import tab.
162
 
 
 
 
 
 
 
 
 
 
 
 
 
163
  = Does it work with plugins? =
164
 
165
  We offer a premium extension that brings the CSS editing power of Child Theme Configurator to any WordPress Plugin installed on your website. The Child Theme Configurator Plugin Extension scans your plugins and creates custom CSS in your Child Theme. Learn more at http://www.lilaeamedia.com/plugins/child-theme-plugin-styles
186
 
187
  Check the appropriate additional stylesheets under "Scan Parent Theme for additional stylesheets" on the Parent/Child tab and load the Child Theme again. CTC tries to identify these files by fetching a page from the parent theme, but you may need to set them manually.
188
 
 
 
 
 
 
 
189
  = Where are the styles? The configurator doesn't show anything! =
190
 
191
  All of the styles are loaded dynamically. You must start typing in the text boxes to select styles to edit.
205
 
206
  No. This is the point of using child themes. Changes to the parent theme are automatically inherited by the child theme.
207
 
208
+ A child theme is not a "copy" of the parent theme. It is a special feature of WordPress that let's you override specific styles and functions leaving the rest of the theme intact. Quality themes should identify any deprecated functions or styles in the upgrade notes so that child theme users can make adjustments accordingly.
209
 
210
  = Where are the .php files? =
211
 
259
  7. Files tab
260
 
261
  == Changelog ==
262
+ = 1.6.3 =
263
+ * New Feature: Better child theme handling for multisite. Moved interface to Themes menu and check for network enabled.
264
+ * Fix: Restrict child theme configurator access to install_themes capability
265
+ * Fix: Preview links to Themes admin if not network enabled to prevent 'Cheatin, uh?' error.
266
 
267
  = 1.6.2.1 =
268
  * Fix: Regular expression introduced in version 1.6.2 parses selectors incorrectly.
302
  * Added regex filter for non-printable (e.g., null) characters in input strings
303
 
304
  = 1.5.2.2 =
305
+ * Fixed a bug introduced in v1.5.2(.1) that copied all of the parent styles to the child stylesheet. This should only be an issue for 'background-image' styles that reference images in the parent theme and do not have child theme overrides.
306
  * Rolled back changes to the javascript controller that introduced a number of type errors.
307
  * Tweaked preview ajax call to handle ssl.
308
 
314
  * Added copy option to Parent/Child tab to assign menu locations, sidebars/widgets, custom header, background, and other options to the new Child Theme.
315
 
316
  = 1.5.0 =
317
+ * Refactored CTC to use the WP_Filesystem API.
318
+ * Non suExec configurations will now require user credentials to add, remove or update Child Theme files.
319
+ * Added the ability for you to make the files writable while editing and then make them read-only when you are done.
 
320
  * You can also set your credentials in wp-config.php: http://codex.wordpress.org/Editing_wp-config.php#WordPress_Upgrade_Constants
 
321
 
322
  = 1.4.8 =
323
  * Removed backreference in main CSS parser regex due to high memory usage.
442
  * Clean up Parent/Child form UI and validation
443
  * Streamlined UI overall
444
 
 
 
 
445
  = 1.0.0 =
446
  * Initial release.
447
 
448
  == Upgrade Notice ==
449
 
450
+ v.1.6.3 **IMPORTANT:** As of v.1.6.3, only users with "install_themes" capability have access to the Child Theme Configurator. This resolves permission issues with both multisite and single site installs. **MULTISITE USERS:** The Admin HAS MOVED to Network Admin > Themes > Child Themes for better handling for multisite (network) installs. See changelog for details.
451
 
452
  == Override Parent Styles ==
453
 
524
  2. Click "Live Preview" (theme customizer) below the new Child Theme to see it in action.
525
  3. When you are ready to take the Child Theme live, click "Activate."
526
 
527
+ **MULTISITE USERS:** You must Network Enable your child theme before you can use Live Preview. Go to "Themes" in the Network Admin.
528
+
529
  == Caveats ==
530
 
531
+ * The Child Theme Configurator plugin does not support the legacy webkit gradient.
532
  * Only two-color gradients. The Child Theme Configurator plugin is powerful, but we have simplified the gradient interface. You can use any gradient you want as long as it has two colors and no intermediate stops.
533
  * No @font-face rules. The Child Theme Configurator plugin only supports @media and @import. If you need other @rules, put them in a separate stylesheet and import them into the Child Theme stylesheet.
534
  * Not all CSS rules are supported. The Child Theme Configurator plugin works with the vast majority of CSS rules, however we’ve left out some of the more obscure options.
538
 
539
  Go to http://www.lilaeamedia.com/plugins/child-theme-configurator
540
 
541
+ Copyright: (C) 2014-2015 Lilaea Media