/** * Compile LESS to CSS, appending data from styles and parsing urls * @param string $less_output LESS code * @param string $dirname absolute path where compiled file will be saved (to parse URLs correctly) * @param array $data style data * @param string $prepend_prefix prefix to prepend all selectors (for widget mode) * @param string $area Area (C/A) to get setting for * @return string CSS code */ public function customCompile($less_output, $dirname, $data = array(), $prepend_prefix = '', $area = AREA) { // Apply all Custom styles styles $less_output .= Styles::factory(fn_get_theme_path('[theme]', $area))->getLess($data); // Inject Bootstrap fluid variables $less_output .= self::getLayoutStyleVariables(); if (!empty($prepend_prefix)) { $less_output = $prepend_prefix . " {\n" . $less_output . "\n}"; } $output = $this->parse($less_output); // Remove "body" definition if (!empty($prepend_prefix)) { $output = str_replace($prepend_prefix . ' body', $prepend_prefix, $output); } return Less::parseUrls($output, $dirname, fn_get_theme_path('[themes]/[theme]/media')); }
/** * Compile LESS to CSS, appending data from styles and parsing urls * @param string $less_output LESS code * @param string $dirname absolute path where compiled file will be saved (to parse URLs correctly) * @param array $data style data * @param string $prepend_prefix prefix to prepend all selectors (for widget mode) * @param string $area current working area * @return string CSS code */ public function customCompile($less_output, $dirname, $data = array(), $prepend_prefix = '', $area = AREA) { // Apply all Custom styles styles if ($area == 'C') { $less_output .= "\n" . Styles::factory(fn_get_theme_path('[theme]', $area))->getLess($data); // Inject Bootstrap fluid variables $less_output .= self::getLayoutStyleVariables(); } if (!empty($prepend_prefix)) { $less_output = $prepend_prefix . " {\n" . $less_output . "\n}"; } if (false) { // is not implemented completely $output = self::parseWithNodeJs($less_output, $area); } else { $output = !empty($less_output) ? $this->parse($less_output) : ''; } // Remove "body" definition if (!empty($prepend_prefix)) { $output = str_replace($prepend_prefix . ' body', $prepend_prefix, $output); } return Less::parseUrls($output, $dirname, fn_get_theme_path('[themes]/[theme]/media', $area)); }
/** * Gets style LESS code * @param array $current_style style data to override current style data * @return string style LESS code */ public function getLess($current_style = array()) { $custom_less = ''; $style = $this->get(Registry::get('runtime.layout.style_id')); if (!empty($style['less'])) { $custom_less = $style['less']; $custom_less .= "\n" . $style['custom_css']; } if (!empty($current_style)) { $custom_less .= Less::arrayToLessVars($current_style); } return $custom_less; }
$css_content = ''; } $data = array(); // FIXME: Bacward preset compatibility if (!empty($_REQUEST['preset_id'])) { $_REQUEST['style_id'] = $_REQUEST['preset_id']; } // If theme ID passed, set default theme if (!empty($_REQUEST['style_id'])) { fn_theme_editor_set_style($_REQUEST['style_id']); // If theme elements passed, get them } elseif (!empty($_REQUEST['style']['data'])) { $data = $_REQUEST['style']['data']; $data = Styles::factory($theme_name)->processCopy('', $data); } $less = new Less(); $import_path[] = Registry::get('config.dir.root') . fn_get_theme_path('/[relative]/[theme]/css'); $import_path[] = Registry::get('config.dir.root') . fn_get_theme_path('/[relative]/[theme]/css/tygh'); $less->setImportDir($import_path); $content = $css_content . $less->customCompile($less_content, Registry::get('config.dir.root'), $data); // remove external fonts to avoid flickering when styles are reloaded //$content = preg_replace("/@font-face \{.*?\}/s", '', $content); } header('content-type: text/css'); fn_echo($content); exit; } elseif ($mode == 'duplicate') { // FIXME: Bacward preset compatibility if (!empty($_REQUEST['preset_id'])) { $_REQUEST['style_id'] = $_REQUEST['preset_id']; }
/** * Merges css and less files * * @param array $files Array with style files * @param string $styles Style code * @param string $prepend_prefix Prepend prefix * @param array $params additional params */ function fn_merge_styles($files, $styles = '', $prepend_prefix = '', $params = array(), $area = AREA) { $prefix = !empty($prepend_prefix) ? 'embedded' : 'standalone'; $make_rtl = false; if (fn_is_rtl_language()) { $prefix .= '-rtl'; $make_rtl = true; } $output = ''; $less_output = ''; $less_reflection = array(); $compiled_less = ''; $compiled_css = ''; $relative_path = fn_get_theme_path('[relative]/[theme]/css', $area); $hashes = array(); $names = array_map(function ($v) { return !empty($v['relative']) ? $v['relative'] : false; }, $files); // Check file changes if (Development::isEnabled('compile_check') || Debugger::isActive()) { $dir_root = Registry::get('config.dir.root'); foreach ($names as $index => $name) { if (file_exists($dir_root . '/' . $name)) { $hashes[] = $name . filemtime($dir_root . '/' . $name); } } } $hashes[] = md5(implode('|', $names)); $hashes[] = md5($styles); if ($area == 'C') { $hashes[] = Registry::get('runtime.layout.layout_id'); $hashes[] = Registry::get('runtime.layout.style_id'); } arsort($hashes); $hash = md5(implode(',', $hashes) . PRODUCT_VERSION) . fn_get_storage_data('cache_id'); $filename = $prefix . '.' . $hash . '.css'; $theme_manifest = Themes::factory(fn_get_theme_path('[theme]', 'C'))->getManifest(); if (!Storage::instance('assets')->isExist($relative_path . '/' . $filename)) { Debugger::checkpoint('Before styles compilation'); foreach ($files as $src) { $m_prefix = ''; $m_suffix = ''; if (!empty($src['media'])) { $m_prefix = "\n@media " . $src['media'] . " {\n"; $m_suffix = "\n}\n"; } if (strpos($src['file'], '.css') !== false) { $output .= "\n" . $m_prefix . fn_get_contents($src['file']) . $m_suffix; } elseif ($area != 'C' || empty($theme_manifest['converted_to_css'])) { $less_output_chunk = ''; if (file_exists($src['file'])) { if ($area == 'C' && (empty($theme_manifest['parent_theme']) || $theme_manifest['parent_theme'] == 'basic')) { $less_output_chunk = "\n" . $m_prefix . fn_get_contents($src['file']) . $m_suffix; } else { $less_output_chunk = "\n" . $m_prefix . '@import "' . str_replace($relative_path . '/', '', $src['relative']) . '";' . $m_suffix; } } if (!empty($params['reflect_less'])) { if (preg_match('{/addons/([^/]+)/}is', $src['relative'], $m)) { $less_reflection['output']['addons'][$m[1]] .= $less_output_chunk; } else { $less_reflection['output']['main'] .= $less_output_chunk; } } $less_output .= $less_output_chunk; } } $header = str_replace('[files]', implode("\n", $names), Registry::get('config.js_css_cache_msg')); if (!empty($styles)) { $less_output .= $styles; } // Prepend all styles with prefix if (!empty($prepend_prefix)) { $less_output = $output . "\n" . $less_output; $output = ''; } if (!empty($output)) { $compiled_css = Less::parseUrls($output, Storage::instance('assets')->getAbsolutePath($relative_path), fn_get_theme_path('[themes]/[theme]/media', $area)); } if (!empty($theme_manifest['converted_to_css']) && $area == 'C') { $theme_css_path = fn_get_theme_path('[themes]/[theme]', $area) . '/css'; $pcl_filepath = $theme_css_path . '/' . Themes::$compiled_less_filename; if (file_exists($pcl_filepath)) { $compiled_css .= fn_get_contents($pcl_filepath); } list($installed_addons) = fn_get_addons(array('type' => 'active')); foreach ($installed_addons as $addon) { $addon_pcl_filpath = $theme_css_path . "/addons/{$addon['addon']}/" . Themes::$compiled_less_filename; if (file_exists($pcl_filepath)) { $compiled_css .= fn_get_contents($addon_pcl_filpath); } } } if (!empty($less_output)) { $less = new Less(); if (!empty($params['compressed'])) { $less->setFormatter('compressed'); } $less->setImportDir($relative_path); try { $compiled_less = $less->customCompile($less_output, Storage::instance('assets')->getAbsolutePath($relative_path), array(), $prepend_prefix, $area); } catch (Exception $e) { $skip_save = true; $shift = 4; $message = '<div style="border: 2px solid red; padding: 5px;">LESS ' . $e->getMessage(); if (preg_match("/line: (\\d+)/", $message, $m)) { $lo = explode("\n", $less_output); $message .= '<br /><br /><pre>' . implode("\n", array_splice($lo, intval($m[1]) - $shift, $shift * 2)) . '</pre>'; } $message .= '</div>'; fn_set_notification('E', __('error'), $message); } } if (empty($skip_save)) { $compiled_content = $compiled_css . "\n" . $compiled_less; // Move all @import links to the Top of the file. if (preg_match_all('/@import url.*?;/', $compiled_content, $imports)) { $compiled_content = preg_replace('/@import url.*?;/', '', $compiled_content); foreach ($imports[0] as $import_link) { $compiled_content = $import_link . "\n" . $compiled_content; } } if ($make_rtl) { $compiled_content = \CSSJanus::transform($compiled_content); $compiled_content = "body {\ndirection: rtl;\n}\n" . $compiled_content; } Storage::instance('assets')->put($relative_path . '/' . $filename, array('contents' => $header . $compiled_content, 'compress' => false, 'caching' => true)); if (!empty($params['use_scheme'])) { fn_put_contents(fn_get_cache_path(false) . 'theme_editor/' . $filename, $output . '#LESS#' . $less_output); } if (!empty($params['reflect_less'])) { $less_reflection['import_dirs'] = array($relative_path); fn_put_contents(fn_get_cache_path(false) . 'less_reflection.json', json_encode($less_reflection)); } } Debugger::checkpoint('After styles compilation'); } $url = Storage::instance('assets')->getUrl($relative_path . '/' . $filename); return $url; }
public static function convertPresets401To402() { $theme_name = Registry::get('config.base_theme'); $schema_path = fn_get_theme_path('[themes]/' . $theme_name . '/styles/schema.json', 'C'); $schema = file_get_contents($schema_path); if (!empty($schema)) { $schema = json_decode($schema, true); } db_query('ALTER TABLE ?:bm_layouts CHANGE `preset_id` `preset_id` varchar(64) NOT NULL default ""'); $presets = db_get_array('SELECT * FROM ?:theme_presets'); foreach ($presets as $preset) { $preset['name'] = self::_formPresetName($preset['name']); //We should rename default preset to satori in order to prevent default preset creation. if ($preset['name'] == 'default') { $preset['name'] = 'satori'; } db_query('UPDATE ?:bm_layouts SET preset_id = ?s WHERE preset_id = ?i', $preset['name'], $preset['preset_id']); $preset_path = fn_get_theme_path('[themes]/' . $theme_name . '/styles/data/', 'C'); if (file_exists($preset_path . $preset['name'] . '.less')) { continue; } $preset_data = unserialize($preset['data']); $less = array(); foreach ($preset_data as $section => $fields) { foreach ($fields as $field_id => $value) { switch ($section) { case 'general': $less[$field_id] = empty($value) ? $schema[$section]['fields'][$field_id]['off'] : $schema[$section]['fields'][$field_id]['on']; break; case 'colors': $less[$field_id] = $value; break; case 'fonts': $less[$field_id] = $value['family']; if (!empty($value['size'])) { $field_name = $schema[$section]['fields'][$field_id]['properties']['size']['match']; $field_value = $value['size'] . $schema[$section]['fields'][$field_id]['properties']['size']['unit']; $less[$field_name] = $field_value; } if (!empty($value['style'])) { foreach ($value['style'] as $style_type => $style_value) { $field_name = $schema[$section]['fields'][$field_id]['properties']['style'][$style_type]['match']; $field_value = $schema[$section]['fields'][$field_id]['properties']['style'][$style_type]['property']; $less[$field_name] = $field_value; } } break; case 'backgrounds': $value['transparent'] = isset($value['transparent']) ? $value['transparent'] : false; $value['full_width'] = isset($value['full_width']) ? $value['full_width'] : false; foreach ($value as $bg_name => $bg_value) { switch ($bg_name) { case 'color': $field_name = $schema[$section]['fields'][$field_id]['properties']['color']['match']; $less[$field_name] = $bg_value; break; case 'gradient': $field_name = $schema[$section]['fields'][$field_id]['gradient']['match']; $less[$field_name] = $bg_value; break; case 'image_data': $less[$schema[$section]['fields'][$field_id]['properties']['pattern']] = !empty($bg_value) ? 'url("' . $bg_value . '")' : 'transparent'; break; case 'repeat': $field_name = $schema[$section]['fields'][$field_id]['properties']['repeat']; if (!empty($field_name)) { $less[$field_name] = $bg_value; } break; case 'attachment': $field_name = $schema[$section]['fields'][$field_id]['properties']['attachment']; if (!empty($field_name)) { $less[$field_name] = $bg_value; } break; case 'full_width': if (!isset($schema[$section]['fields'][$field_id]['copies'])) { break; } foreach ($schema[$section]['fields'][$field_id]['copies']['full_width'] as $copies) { if (!empty($value['full_width'])) { if (!empty($copies['inverse'])) { $less[$copies['match']] = $copies['default']; } elseif (isset($less[$copies['source']])) { $less[$copies['match']] = $less[$copies['source']]; } } else { if (empty($copies['inverse'])) { $less[$copies['match']] = $copies['default']; } } } break; case 'transparent': if (!isset($schema[$section]['fields'][$field_id]['copies'])) { break; } foreach ($schema[$section]['fields'][$field_id]['copies']['transparent'] as $copies) { if (!empty($value['transparent'])) { if (!empty($copies['inverse'])) { $less[$copies['match']] = $copies['default']; } elseif (isset($less[$copies['source']])) { $less[$copies['match']] = $less[$copies['source']]; } } else { if (empty($copies['inverse'])) { $less[$copies['match']] = $copies['default']; } } } break; case 'image_name': break; default: fn_print_r('Unprocessed background property: ' . $bg_name); } } break; default: fn_print_r('Error: Section ' . $section . ' was not processed'); } } } $less = Less::arrayToLessVars($less); file_put_contents(fn_get_theme_path('[themes]/' . $theme_name . '/styles/data/' . $preset['name'] . '.less', 'C'), $less); } db_query('DROP TABLE IF EXISTS ?:theme_presets'); return true; }