Added the theme selector

This commit is contained in:
alekseybobkov 2014-07-19 13:05:50 +11:00
parent 48160088f2
commit c324c80969
14 changed files with 525 additions and 2 deletions

View File

@ -7323,6 +7323,9 @@ body {
.layout > .layout-row > .layout-cell.min-size {
width: 0;
}
.layout > .layout-row > .layout-cell.min-height {
height: 0;
}
.layout > .layout-row > .layout-cell.center {
text-align: center;
}
@ -7363,6 +7366,9 @@ body {
.layout > .layout-row > .layout-cell.min-size {
width: 0;
}
.layout > .layout-row > .layout-cell.min-height {
height: 0;
}
.layout > .layout-row > .layout-cell.center {
text-align: center;
}
@ -7411,6 +7417,9 @@ body {
.layout > .layout-cell.min-size {
width: 0;
}
.layout > .layout-cell.min-height {
height: 0;
}
.layout > .layout-cell.center {
text-align: center;
}
@ -11521,6 +11530,9 @@ ul.status-list li span.status.info {
.control-breadcrumb + .content-tabs {
margin-top: -20px;
}
.control-breadcrumb.no-bottom-margin {
margin-bottom: 0;
}
body.slim-container .control-breadcrumb {
margin-left: 0;
margin-right: 0;

View File

@ -43,6 +43,10 @@
+ .content-tabs {
margin-top: -20px;
}
&.no-bottom-margin {
margin-bottom: 0;
}
}
body.slim-container {

View File

@ -68,6 +68,10 @@ body {
width: 0;
}
&.min-height {
height: 0;
}
&.center {
text-align: center;
}

View File

@ -6,6 +6,7 @@ use BackendMenu;
use BackendAuth;
use Backend\Classes\WidgetManager;
use October\Rain\Support\ModuleServiceProvider;
use System\Classes\SettingsManager;
class ServiceProvider extends ModuleServiceProvider
{
@ -89,6 +90,7 @@ class ServiceProvider extends ModuleServiceProvider
'cms.manage_pages' => ['label' => 'Manage pages', 'tab' => 'Cms'],
'cms.manage_layouts' => ['label' => 'Manage layouts', 'tab' => 'Cms'],
'cms.manage_partials' => ['label' => 'Manage partials', 'tab' => 'Cms'],
'cms.manage_themes' => ['label' => 'Manage themes', 'tab' => 'Cms']
]);
});
@ -99,6 +101,21 @@ class ServiceProvider extends ModuleServiceProvider
$manager->registerFormWidget('Cms\FormWidgets\Components');
});
/*
* Register settings
*/
SettingsManager::instance()->registerCallback(function($manager){
$manager->registerSettingItems('October.Cms', [
'theme' => [
'label' => 'cms::lang.theme.settings_menu',
'description' => 'cms::lang.theme.settings_menu_description',
'category' => 'CMS',
'icon' => 'icon-picture-o',
'url' => Backend::URL('cms/theme'),
'sort' => 200
]
]);
});
}
/**

View File

@ -0,0 +1,112 @@
.theme-selector-layout .layout-cell {
padding: 24px;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
.theme-selector-layout .theme-thumbnail {
width: 288px;
background: #ecf0f1;
border-bottom: 1px solid #e3e7e9;
}
.theme-selector-layout .theme-thumbnail img {
opacity: 0.6;
filter: alpha(opacity=60);
width: 240px;
}
.theme-selector-layout .theme-description {
border-bottom: 1px solid #f2f3f4;
}
.theme-selector-layout .theme-description h3,
.theme-selector-layout .theme-description p {
opacity: 0.6;
filter: alpha(opacity=60);
}
.theme-selector-layout .theme-description h3 {
margin: 0 0 25px 0;
font-size: 28px;
color: #2b3e50;
display: inline-block;
}
.theme-selector-layout .theme-description p.author {
font-size: 13px;
display: inline-block;
color: #808c8d;
}
.theme-selector-layout .theme-description p.description {
color: #2b3e50;
font-size: 14px;
line-height: 180%;
margin-bottom: 30px;
}
.theme-selector-layout .theme-description .controls button i.icon-star {
margin-right: 5px;
color: #f1a84e;
font-size: 16px;
vertical-align: middle;
}
.theme-selector-layout .layout-row.active .theme-thumbnail {
background: #bdc3c7;
border-bottom-color: #bdc3c7;
}
.theme-selector-layout .layout-row.active .thumbnail-container {
position: relative;
}
.theme-selector-layout .layout-row.active .thumbnail-container:after {
content: '';
display: block;
width: 0;
height: 0;
border-top: 14px solid transparent;
border-bottom: 14px solid transparent;
border-left: 15px solid #bdc3c7;
position: absolute;
right: -35px;
top: 50%;
margin-top: -14px;
}
.theme-selector-layout .layout-row.active .theme-description h3,
.theme-selector-layout .layout-row:hover .theme-description h3,
.theme-selector-layout .layout-row.active .theme-description p,
.theme-selector-layout .layout-row:hover .theme-description p {
opacity: 1;
filter: alpha(opacity=100);
}
.theme-selector-layout .layout-row.active .theme-thumbnail img,
.theme-selector-layout .layout-row:hover .theme-thumbnail img {
opacity: 1;
filter: alpha(opacity=100);
}
.theme-selector-layout .layout-row.last .theme-description,
.theme-selector-layout .layout-row.last .theme-thumbnail {
border-bottom: none!important;
}
.theme-selector-layout .find-more-themes {
background: #ecf0f1;
color: #2b3e50;
text-decoration: none;
display: block;
padding: 20px;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
}
.theme-selector-layout .find-more-themes:hover {
background: #1795f1;
color: white;
}
@media (max-width: 768px) {
.theme-selector-layout .layout-cell,
.theme-selector-layout .layout-row {
display: block!important;
width: auto!important;
height: auto!important;
}
.theme-selector-layout .theme-thumbnail img {
width: 100%;
}
.theme-selector-layout .layout-row.links .theme-thumbnail {
background: transparent;
padding: 0;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

@ -0,0 +1,139 @@
@import "../../../backend/assets/less/core/boot.less";
.theme-selector-layout {
.layout-cell {
padding: 24px;
.box-sizing(border-box);
}
.theme-thumbnail {
width: 288px;
background: #ecf0f1;
border-bottom: 1px solid #e3e7e9;
img {
.opacity(0.6);
width: 240px;
}
}
.theme-description {
border-bottom: 1px solid #f2f3f4;
h3, p {
.opacity(0.6);
}
h3 {
margin: 0 0 25px 0;
font-size: 28px;
color: #2b3e50;
display: inline-block;
}
p.author {
font-size: 13px;
display: inline-block;
color: #808c8d;
}
p.description {
color: #2b3e50;
font-size: 14px;
line-height: 180%;
margin-bottom: 30px;
}
.controls {
button i.icon-star {
margin-right: 5px;
color: #f1a84e;
font-size: 16px;
vertical-align: middle;
}
}
}
.layout-row.active {
.theme-thumbnail {
background: #bdc3c7;
border-bottom-color: #bdc3c7;
}
.thumbnail-container {
position: relative;
&:after {
.triangle(right, 15px, 28px, #bdc3c7);
position: absolute;
right: -35px;
top: 50%;
margin-top: -14px;
}
}
}
.layout-row {
&.active, &:hover {
.theme-description {
h3, p {
.opacity(1);
}
}
.theme-thumbnail {
img {
.opacity(1);
}
}
}
&.last {
.theme-description, .theme-thumbnail {
border-bottom: none!important;
}
}
}
.find-more-themes {
background: #ecf0f1;
color: #2b3e50;
text-decoration: none;
display: block;
padding: 20px;
.border-radius(4px);
&:hover {
background: @link-color;
color: white;
}
}
}
//
// Screen specific
//
@media (max-width: @screen-sm) {
.theme-selector-layout {
.layout-cell, .layout-row {
display: block!important;
width: auto!important;
height: auto!important;
}
.theme-thumbnail {
img {
width: 100%;
}
}
.layout-row.links {
.theme-thumbnail {
background: transparent;
padding: 0;
}
}
}
}

View File

@ -4,7 +4,12 @@ use File;
use Lang;
use Event;
use Config;
use URL;
use Cache;
use DB;
use DirectoryIterator;
use System\Classes\SystemException;
use October\Rain\Support\Yaml;
/**
* This class represents the CMS theme.
@ -21,6 +26,11 @@ class Theme
*/
protected $dirName;
/**
* @var mixed Keeps the cached configuration file values.
*/
protected $configCache = null;
/**
* Loads the theme.
*/
@ -84,7 +94,16 @@ class Theme
*/
public static function getActiveTheme()
{
$activeTheme = Config::get('cms.activeTheme');
$configKey = 'cms.activeTheme';
$activeTheme = Config::get($configKey);
$dbResult = DB::table('system_settings')
->where('item', '=', $configKey)
->remember(1440, $configKey)
->pluck('value');
if ($dbResult !== null)
$activeTheme = $dbResult;
$apiResult = Event::fire('cms.activeTheme', [], true);
if ($apiResult !== null)
@ -101,6 +120,20 @@ class Theme
return $theme;
}
/**
* Sets the active theme.
* The active theme code is stored in the database and overrides the configuration cms.activeTheme parameter.
* @param string $code Specifies the active theme code.
*/
public static function setActiveTheme($code)
{
$configKey = 'cms.activeTheme';
DB::table('system_settings')->where('item', '=', $configKey)->delete();
DB::table('system_settings')->insert(['item'=>$configKey, 'value'=>$code]);
Cache::forget($configKey);
}
/**
* Returns the edit theme.
* By default the edit theme is loaded from the cms.editTheme parameter,
@ -130,4 +163,75 @@ class Theme
return $theme;
}
/**
* Returns a list of all themes.
* @return array Returns an array of the Theme objects.
*/
public static function all()
{
$path = base_path().Config::get('cms.themesDir');
$it = new DirectoryIterator($path);
$result = [];
foreach ($it as $fileinfo) {
if ($fileinfo->isDot() ||(substr($fileinfo->getFilename(), 0, 1) == '.'))
continue;
$theme = new self();
$theme->load($fileinfo->getFilename());
$result[] = $theme;
}
return $result;
}
/**
* Reads the theme.yaml file and returns the theme configuration values.
* @return array Returns the parsed configuration file values.
*/
public function getConfig()
{
if ($this->configCache !== null)
return $this->configCache;
$path = $this->getPath().'/theme.yaml';
if (!File::exists($path))
return $this->configCache = [];
return $this->configCache = Yaml::parseFile($path);
}
/**
* Returns a value from the theme configuration file by its name.
* @param string $name Specifies the configuration parameter name.
* @param mixed $default Specifies the default value to return in case if the parameter doesn't exist in the configuration file.
* @return mixed Returns the parameter value or a default value
*/
public function getConfigValue($name, $default = null)
{
$config = $this->getConfig();
if (isset($config[$name]))
return $config[$name];
return $default;
}
/**
* Returns the theme preview image URL.
* If the image file doesn't exist returns the placeholder image URL.
* @return string Returns the image URL.
*/
public function getPreviewImageUrl()
{
$previewPath = '/assets/images/theme-preview.png';
$path = $this->getPath().$previewPath;
if (!File::exists($path))
return URL::asset('modules/cms/assets/images/default-theme-preview.png');
return URL::asset('themes/'.$this->getDirName().$previewPath);
}
}

View File

@ -0,0 +1,50 @@
<?php namespace Cms\Controllers;
use Lang;
use Config;
use BackendMenu;
use Input;
use Backend\Classes\Controller;
use Cms\Classes\Theme as CmsTheme;
/**
* Theme selector controller
*
* @package october\backend
* @author Alexey Bobkov, Samuel Georges
*
*/
class Theme extends Controller
{
public $requiredPermissions = ['cms.manage_themes'];
public $bodyClass = 'slim-container';
/**
* Constructor.
*/
public function __construct()
{
parent::__construct();
$this->addJs('/modules/cms/assets/js/themeselector/themeselector.js', 'core');
$this->addCss('/modules/cms/assets/css/october.theme-selector.css', 'core');
$this->pageTitle = Lang::get('cms::lang.theme.settings_menu');
BackendMenu::setContext('October.System', 'system', 'settings');
}
public function index()
{
}
public function index_onSetActiveTheme()
{
CmsTheme::setActiveTheme(Input::get('theme'));
return [
'#theme-list' => $this->makePartial('theme_list')
];
}
}

View File

@ -0,0 +1,51 @@
<?php
$themes = Cms\Classes\Theme::all();
$activeTheme = Cms\Classes\Theme::getActiveTheme();
$cnt = count($themes);
foreach ($themes as $index=>$theme):
$isLast = $index == $cnt-1;
$isActive = $activeTheme->getDirName() == $theme->getDirName();
$author = $theme->getConfigValue('author');
?>
<div class="layout-row <?= $isLast ? 'last' : null ?> min-size <?= $isActive ? 'active' : null ?>">
<div class="layout-cell min-height theme-thumbnail">
<div class="thumbnail-container"><img src="<?= $theme->getPreviewImageUrl() ?>"/></div>
</div>
<div class="layout-cell min-height theme-description">
<h3><?= e($theme->getConfigValue('name', $theme->getDirName())) ?></h3>
<?php if (strlen($author)): ?>
<p class="author">by <a href="<?= e($theme->getConfigValue('author-url', '#')) ?>"><?= e($author) ?></a></p>
<?php endif ?>
<p class="description"><?= e($theme->getConfigValue('description', 'The theme description is not provided.')) ?></p>
<div class="controls">
<?php if ($isActive): ?>
<button
type="submit"
disabled
class="btn btn-disabled">
<i class="icon-star"></i>
<?= e(trans('cms::lang.theme.active_btn')) ?>
</button>
<?php else: ?>
<button
type="submit"
data-request="onSetActiveTheme"
data-request-data="theme: '<?= e($theme->getDirName()) ?>'"
data-stripe-load-indicator
class="btn btn-primary">
<?= e(trans('cms::lang.theme.activate_btn')) ?>
</button>
<?php endif ?>
</div>
</div>
</div>
<?php endforeach ?>
<div class="layout-row links">
<div class="layout-cell theme-thumbnail">
</div>
<div class="layout-cell theme-description">
<a class="find-more-themes" href="http://octobercms.com/themes"><?= e(trans('cms::lang.theme.find_more_themes')) ?></a>
</div>
</div>

View File

@ -0,0 +1,21 @@
<?= Block::put('body') ?>
<div class="layout">
<div class="layout-row min-size">
<div class="control-breadcrumb no-bottom-margin">
<ul>
<li><a href="<?= Backend::url('system/settings') ?>"><?= e(trans('system::lang.settings.menu_label')) ?></a></li>
<li><?= e($this->pageTitle) ?></li>
</ul>
</div>
</div>
<div class="layout-row">
<?= Form::open(['onsubmit'=>'return false']) ?>
<div class="layout theme-selector-layout" id="theme-list">
<?= $this->makePartial('theme_list') ?>
</div>
<?= Form::close() ?>
</div>
</div>
<?= Block::endPut() ?>

View File

@ -20,7 +20,12 @@ return [
'not_set' => "The edit theme is not set.",
'not_found' => "The edit theme is not found.",
'not_match' => "The object you're trying to access doesn't belong to the theme being edited. Please reload the page."
]
],
'settings_menu' => 'Front-end theme',
'settings_menu_description' => 'Preview the list of installed themes and select an active theme.',
'find_more_themes' => 'Find more themes on OctoberCMS Theme Marketplace.',
'activate_btn' => 'Activate',
'active_btn' => 'Activate',
],
'page' => [
'not_found' => [

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

4
themes/demo/theme.yaml Normal file
View File

@ -0,0 +1,4 @@
name: Demo
description: Demo OctoberCMS theme. Demonstrates the basic concepts of the front-end theming: layouts, pages, partials, components, content blocks, AJAX framework.
author: OctoberCMS
author-url: 'http://octobercms.com'