/** * 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)); }
if (!empty($from_default_layout) && !empty($layout_id)) { $repo_dest = fn_get_theme_path('[themes]/' . $from_default_layout['theme_name'], 'C'); $layout_path = fn_normalize_path($repo_dest . '/layouts/' . $from_default_layout['filename']); $exim = Exim::instance(Registry::get('runtime.company_id'), $layout_id, fn_get_theme_path('[theme]', 'C')); $structure = $exim->getStructure($layout_path); if (!empty($structure)) { foreach ($layout_data as $key => $val) { if (!empty($structure->layout->{$key})) { $structure->layout->{$key} = $val; } } if (!isset($layout_data['is_default'])) { $structure->layout->is_default = 0; } $exim->import($structure, array('import_style' => 'update')); fn_create_theme_logos_by_layout_id($layout_data['theme_name'], $layout_id, Registry::get('runtime.company_id'), false, Styles::factory($layout_data['theme_name'])->getDefault()); } } fn_clear_cache('assets', 'design/'); return array(CONTROLLER_STATUS_OK, fn_url('block_manager.manage?s_layout=' . $layout_id)); } if ($mode == 'update_block') { $description = array(); if (!empty($_REQUEST['block_data']['description'])) { $_REQUEST['block_data']['description']['lang_code'] = DESCR_SL; $description = $_REQUEST['block_data']['description']; } if (!empty($_REQUEST['block_data']['content_data'])) { $_REQUEST['block_data']['content_data']['lang_code'] = DESCR_SL; if (isset($_REQUEST['block_data']['content'])) { $_REQUEST['block_data']['content_data']['content'] = $_REQUEST['block_data']['content'];
function fn_theme_editor($params, $lang_code = CART_LANGUAGE) { $view = Tygh::$app['view']; $theme_name = Registry::get('runtime.layout.theme_name'); $layout_id = Registry::get('runtime.layout.layout_id'); if (!Registry::get('runtime.layout.style_id')) { $default_style_id = Styles::factory($theme_name)->getDefault(); db_query('UPDATE ?:bm_layouts SET style_id = ?s WHERE layout_id = ?i', $default_style_id, $layout_id); Registry::set('runtime.layout.style_id', $default_style_id); } $style_id = Registry::get('runtime.layout.style_id'); // Backward presets compatibility Registry::set('runtime.layout.preset_id', $style_id); // get current style $current_style = Styles::factory($theme_name)->get($style_id, array('parse' => true)); // get all styles $styles_list = Styles::factory($theme_name)->getList(); $schema = Styles::factory($theme_name)->getSchema(); $sections = array('te_general' => 'theme_editor.general', 'te_logos' => 'theme_editor.logos', 'te_colors' => 'theme_editor.colors', 'te_fonts' => 'theme_editor.fonts', 'te_backgrounds' => 'theme_editor.backgrounds', 'te_css' => 'theme_editor.css'); foreach ($sections as $section_id => $section) { if ($section_id == 'te_logos') { // Logos is hardcoded section, no need to define it in schema continue; } $section_id = str_replace('te_', '', $section_id); if (!isset($schema[$section_id])) { unset($sections['te_' . $section_id]); } } if (empty($params['selected_section']) || !isset($sections[$params['selected_section']])) { reset($sections); $params['selected_section'] = key($sections); } $theme = Themes::factory($theme_name); $theme_manifest = $theme->getManifest(); if (!empty($theme_manifest['converted_to_css'])) { if (empty($params['selected_css_file'])) { $params['selected_css_file'] = Themes::$compiled_less_filename; } $view->assign('selected_css_file', $params['selected_css_file']); $view->assign('css_files_list', $theme->getCssFilesList()); $view->assign('css_content', $theme->getCssContents($params['selected_css_file'])); } $view->assign('cse_logo_types', fn_get_logo_types()); $view->assign('cse_logos', fn_get_logos(Registry::get('runtime.company_id'))); $view->assign('selected_section', $params['selected_section']); $view->assign('te_sections', $sections); $view->assign('current_style', $current_style); $view->assign('props_schema', $schema); $view->assign('theme_patterns', Patterns::instance()->get($style_id)); $view->assign('styles_list', $styles_list); // FIXME: Backward compatibility $view->assign('presets_list', $styles_list); $view->assign('current_preset', $current_style); $view->assign('manifest', Styles::factory($theme_name)->getManifest()); $view->assign('theme_manifest', $theme_manifest); $view->assign('layouts', Layout::instance()->getList(array('theme_name' => $theme_name))); $view->assign('layout_data', Layout::instance()->get($layout_id)); $view->assign('theme_url', fn_url(empty($params['theme_url']) ? '' : $params['theme_url'])); }
/** * Updates or creates layout * @param array $layout_data layout data * @param int $layout_id layout ID to update, zero to create * @return int ID of updated/created layout */ public function update($layout_data, $layout_id = 0) { if (fn_allowed_for('ULTIMATE')) { if (empty($layout_data['company_id'])) { $layout_data['company_id'] = $this->_company_id; } } $theme_name = empty($layout_data['theme_name']) ? fn_get_theme_path('[theme]', 'C', $this->_company_id, false) : $layout_data['theme_name']; $available_styles = Styles::factory($theme_name)->getList(array('short_info' => true)); if (empty($layout_id)) { if (!empty($layout_data['from_layout_id'])) { $layout_data['style_id'] = Styles::factory($theme_name)->getStyle($layout_data['from_layout_id']); } if (!empty($layout_data['style_id']) && !isset($available_styles[$layout_data['style_id']])) { unset($layout_data['style_id']); } if (empty($layout_data['style_id'])) { $layout_data['style_id'] = Styles::factory($theme_name)->getDefault(); } $layout_id = db_query("INSERT INTO ?:bm_layouts ?e", $layout_data); // Create logos $types = fn_get_logo_types(); foreach ($types as $type => $data) { if (!empty($data['for_layout'])) { $object_ids[$type] = fn_create_logo(array('type' => $type, 'layout_id' => $layout_id), !empty($layout_data['company_id']) ? $layout_data['company_id'] : 0); } } } else { if (isset($layout_data['style_id']) && !isset($available_styles[$layout_data['style_id']])) { $layout_data['style_id'] = Styles::factory($theme_name)->getDefault(); } db_query("UPDATE ?:bm_layouts SET ?u WHERE layout_id = ?i", $layout_data, $layout_id); } if (!empty($layout_data['is_default'])) { $this->setDefault($layout_id); } if (!empty($layout_data['from_layout_id'])) { $this->copyById($layout_data['from_layout_id'], $layout_id); } return $layout_id; }
} Tygh::$app['view']->assign('logo_types', fn_get_logo_types(true)); } $restored_company_data = fn_restore_post_data('company_data'); if (!empty($restored_company_data) && $mode == 'add') { if (!empty($restored_company_data['shippings'])) { $restored_company_data['shippings'] = implode(',', $restored_company_data['shippings']); } $company_data = fn_array_merge($company_data, $restored_company_data); } if (fn_allowed_for('ULTIMATE')) { if ($mode == 'update') { $available_themes = fn_get_available_themes(fn_get_theme_path('[theme]', 'C', $company_id)); $theme_name = fn_get_theme_path('[theme]', 'C', $company_id); $layout = Layout::instance($company_id)->getDefault($theme_name); $style = Styles::factory($theme_name)->get($layout['style_id']); Tygh::$app['view']->assign('current_style', $style); Tygh::$app['view']->assign('theme_info', $available_themes['current']); } $countries_list = fn_get_simple_countries(); if (!empty($company_data['countries_list'])) { if (!is_array($company_data['countries_list'])) { $company_countries = explode(',', $company_data['countries_list']); } else { $company_countries = $company_data['countries_list']; } $_countries = array(); foreach ($company_countries as $code) { if (isset($countries_list[$code])) { $_countries[$code] = $countries_list[$code]; unset($countries_list[$code]);
/** * Gets all available themes: from repo and installed one * @param string $theme_name current theme * @return type */ function fn_get_available_themes($theme_name) { $default_theme = Registry::get('config.base_theme'); $repo_path = fn_get_theme_path('[repo]', 'C'); $rel_repo_path = str_replace(Registry::get('config.dir.root'), '', $repo_path); $installed_path = fn_get_theme_path('[themes]', 'C'); $rel_installed_path = fn_get_theme_path('/[relative]', 'C'); $themes = array('repo' => fn_get_dir_contents($repo_path, true), 'installed' => fn_get_dir_contents($installed_path, true)); sort($themes['repo']); sort($themes['installed']); $themes_list = array(); foreach ($themes as $type => $_themes) { foreach ($_themes as $v) { // FIXME: Forbid to install the basic theme but continue to support it if ($type == 'repo' && $v == 'basic') { continue; } $dir = $type == 'repo' ? $repo_path : $installed_path; $rel_dir = $type == 'repo' ? $rel_repo_path : $rel_installed_path; if (file_exists($dir . '/' . $v . '/' . THEME_MANIFEST)) { $manifest_content = fn_get_contents($dir . '/' . $v . '/' . THEME_MANIFEST); $themes_list[$type][$v] = json_decode($manifest_content, true); } elseif (file_exists($dir . '/' . $v . '/' . THEME_MANIFEST_INI)) { $themes_list[$type][$v] = parse_ini_file($dir . '/' . $v . '/' . THEME_MANIFEST_INI); } if (file_exists($dir . '/' . $v . '/customer_screenshot.png')) { $themes_list[$type][$v]['screenshot'] = Registry::get('config.current_location') . $rel_dir . '/' . $v . '/customer_screenshot.png'; } // Check if the theme has styles. $params = array('short_info' => true); $themes_list[$type][$v]['styles'] = Styles::factory($v)->getList($params); } } $themes_list['current'] = $themes_list['installed'][$theme_name]; $themes_list['current']['theme_name'] = $theme_name; return $themes_list; }
private static function _installTheme($company_id = null) { $theme_name = 'basic'; $style = 'satori'; fn_install_theme($theme_name, $company_id); $layout = Layout::instance($company_id)->getDefault($theme_name); Styles::factory($theme_name)->setStyle($layout['layout_id'], $style); }
/** * Updates or creates layout * @param array $layout_data layout data * @param int $layout_id layout ID to update, zero to create * @return int ID of updated/created layout */ public function update($layout_data, $layout_id = 0) { $create = empty($layout_id); if (fn_allowed_for('ULTIMATE')) { if (empty($layout_data['company_id'])) { $layout_data['company_id'] = $this->_company_id; } } $theme_name = empty($layout_data['theme_name']) ? fn_get_theme_path('[theme]', 'C', $this->_company_id, false) : $layout_data['theme_name']; $available_styles = Styles::factory($theme_name)->getList(array('short_info' => true)); /** * Performs actions before updating layout * * @param object $this Layout object * @param integer $layout_id layout ID * @param array $layout_data layout data * @param boolean $create create/update flag */ fn_set_hook('layout_update_pre', $this, $layout_id, $layout_data, $create); // Create layout if (empty($layout_id)) { $company_id = !empty($layout_data['company_id']) ? $layout_data['company_id'] : 0; if (!empty($layout_data['from_layout_id'])) { $layout_data['style_id'] = Styles::factory($theme_name)->getStyle($layout_data['from_layout_id']); } if (!empty($layout_data['style_id']) && !isset($available_styles[$layout_data['style_id']])) { unset($layout_data['style_id']); } if (empty($layout_data['style_id'])) { $layout_data['style_id'] = Styles::factory($theme_name)->getDefault(); } $layout_id = db_query("INSERT INTO ?:bm_layouts ?e", $layout_data); } else { if (isset($layout_data['style_id']) && !isset($available_styles[$layout_data['style_id']])) { $layout_data['style_id'] = Styles::factory($theme_name)->getDefault(); } $old_layout_data = $this->get($layout_id); if ($old_layout_data['is_default'] == 1 && empty($layout_data['is_default'])) { $layout_data['is_default'] = 1; } db_query('UPDATE ?:bm_layouts SET ?u WHERE layout_id = ?i', $layout_data, $layout_id); } if (!empty($layout_data['is_default'])) { $this->setDefault($layout_id); } if (!empty($layout_data['from_layout_id'])) { $this->copyById($layout_data['from_layout_id'], $layout_id); } return $layout_id; }
/** * Fetch frontend styles * * @param array Params * * @return string Frontend styles */ protected function fetchFrontendStyles($params = array()) { fn_clear_cache('assets', 'design/'); $style_id = Registry::get('runtime.layout.style_id'); if (empty($style_id)) { Registry::set('runtime.layout.style_id', Styles::factory($this->theme_name)->getDefault()); } $view = \Tygh::$app['view']; $view->setArea('C'); $view->assign('use_scheme', true); $view->assign('include_dropdown', true); foreach ($params as $key => $val) { $view->assign($key, $val); } $ret = $view->fetch('common/styles.tpl'); $view->setArea(AREA); return $ret; }
/** * Init layout * * @param array $params request parameters * @return boolean always true */ function fn_init_layout($params) { if (fn_allowed_for('ULTIMATE')) { if (!Registry::get('runtime.company_id')) { return array(INIT_STATUS_OK); } } $key_name = 'stored_layout' . (Embedded::isEnabled() ? '_embedded' : ''); $stored_layout = fn_get_session_data($key_name); if (!empty($params['s_layout'])) { $stored_layout = $params['s_layout']; fn_set_session_data($key_name, $params['s_layout']); } // Replace default theme with selected for current area if (!empty($stored_layout)) { $layout = Layout::instance()->get($stored_layout); if (!isset($layout['theme_name']) || $layout['theme_name'] != fn_get_theme_path('[theme]', 'C')) { unset($layout); } } if (empty($layout)) { $layout = Layout::instance()->getDefault(); // get default } $available_styles = Styles::factory($layout['theme_name'])->getList(array('short_info' => true)); if (!isset($available_styles[$layout['style_id']])) { $layout['style_id'] = Styles::factory($layout['theme_name'])->getDefault(); } Registry::set('runtime.layout', $layout); return array(INIT_STATUS_OK); }
Registry::get('view')->assign('layout', $layout); foreach ($available_themes['installed'] as $theme_id => $theme) { $layouts_params = array('theme_name' => $theme_id); $available_themes['installed'][$theme_id]['layouts'] = Layout::instance()->getList($layouts_params); if ($theme_id == $theme_name) { $available_themes['current']['layouts'] = $available_themes['installed'][$theme_id]['layouts']; } } Registry::get('view')->assign('available_themes', $available_themes); Registry::get('view')->assign('dev_modes', Development::get()); } elseif ($mode == 'styles') { if ($action == 'update_status') { $theme = Themes::factory(fn_get_theme_path('[theme]', 'C')); $theme_manifest = $theme->getManifest(); if (empty($theme_manifest['converted_to_css'])) { Styles::factory(fn_get_theme_path('[theme]', 'C'))->setStyle($_REQUEST['id'], $_REQUEST['status']); // Delete compiled CSS file fn_clear_cache('statics'); } else { $layout = Layout::instance(Registry::get('runtime.company_id'))->getDefault(); fn_set_notification('E', __('error'), __('theme_editor.error_theme_converted_to_css', array('[url]' => fn_url("customization.update_mode?type=theme_editor&status=enable&s_layout={$layout['layout_id']}")))); } } return array(CONTROLLER_STATUS_OK, 'themes.manage'); } elseif ($mode == 'update_dev_mode') { if (!empty($_REQUEST['dev_mode'])) { if (!empty($_REQUEST['state'])) { Development::enable($_REQUEST['dev_mode']); } else { Development::disable($_REQUEST['dev_mode']); }
/** * 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); $theme_path = fn_get_theme_path('[theme]', 'C'); $theme = Themes::factory($theme_path); $theme_manifest = $theme->getManifest(); // Check file changes if (Development::isEnabled('compile_check') || Debugger::isActive()) { $dir_root = Registry::get('config.dir.root'); $css_path = $dir_root . DIRECTORY_SEPARATOR . $relative_path; $tracked_files = array(); $css_files = fn_get_dir_contents($css_path, false, true, array('css', 'less'), '', true); foreach ($css_files as $file) { $tracked_files[$file] = $css_path . DIRECTORY_SEPARATOR . $file; } foreach ($names as $index => $name) { if (file_exists($dir_root . '/' . $name)) { $tracked_files[$name] = $dir_root . '/' . $name; } } if ($area == 'C') { $style_id = Registry::get('runtime.layout.style_id'); if ($style_id) { /** @var Styles $style */ $style = Styles::factory(fn_get_theme_path('[theme]', $area)); $less_file = $style->getStyleFile($style_id); $css_file = $style->getStyleFile($style_id, 'css'); if (file_exists($less_file)) { $tracked_files['less_' . $style_id] = $less_file; } if (file_exists($css_file)) { $tracked_files['css_' . $style_id] = $css_file; } } } foreach ($tracked_files as $key => $file) { $hashes[] = $key . filemtime($file); } } $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'; 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; }