Merge branch 'develop' into ux

This commit is contained in:
alekseybobkov 2014-07-25 16:19:11 +11:00
commit 1085d20f8c
16 changed files with 236 additions and 28 deletions

View File

@ -1,7 +1,10 @@
* **Build 125** (2014-07-xx)
* **Build 125** (2014-07-24)
- Theme support added.
- Added new Theme picker to the backend via Settings > Front-end theme
- New shorthand method for `$this->getClassExtension('Backend.Behaviors.FormController')` becomes `$this->asExtension('FormController')`.
- Buttons inside a popup support new `data-popup-load-indicator` attribute.
- Added a new config item to disable core updates completely (see config cms.disableCoreUpdates).
- Added a unique alternate favicon to the Back-end area.
* **Build 124** (2014-07-17)
- Improvements to Twig functions and filters.

View File

@ -33,6 +33,20 @@ return array(
*/
'disablePlugins' => [],
/*
|--------------------------------------------------------------------------
| Prevents application updates
|--------------------------------------------------------------------------
|
| If using composer or git to download updates to the core files, set this
| value to 'true' to prevent the update gateway from trying to download
| these files again as part of the application update process. Plugins
| and themes will still be downloaded.
|
*/
'disableCoreUpdates' => false,
/*
|--------------------------------------------------------------------------
| Back-end URI prefix

View File

@ -55,6 +55,7 @@ return array(
'mysql' => array(
'driver' => 'mysql',
'host' => 'localhost',
'port' => '',
'database' => 'database',
'username' => 'root',
'password' => '',
@ -66,6 +67,7 @@ return array(
'pgsql' => array(
'driver' => 'pgsql',
'host' => 'localhost',
'port' => '',
'database' => 'database',
'username' => 'root',
'password' => '',
@ -77,6 +79,7 @@ return array(
'sqlsrv' => array(
'driver' => 'sqlsrv',
'host' => 'localhost',
'port' => '',
'database' => 'database',
'username' => 'root',
'password' => '',

View File

@ -48,7 +48,10 @@ Log::useFiles(storage_path().'/logs/system.log');
App::error(function(Exception $exception, $code)
{
Log::error($exception);
/*
* October uses a custom error handler, see
* System\Classes\ErrorHandler::handleException
*/
});
/*

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

View File

@ -8,11 +8,14 @@
* Supported data attributes:
* - data-trigger-type, values: display, hide, enable, disable
* - data-trigger: a CSS selector for elements that trigger the action (checkboxes)
* - data-trigger-condition, values: checked (more conditions to add later) - determines the condition the elements
* specified in the data-trigger should satisfy in order the condition to be considered as "true".
* - data-trigger-condition, values:
* - checked: determines the condition the elements specified in the data-trigger
* should satisfy in order the condition to be considered as "true".
* - value[somevalue]: determines if the value of data-trigger equals the specified value (somevalue)
* the condition is considered "true".
*
* Example: <input type="button" class="btn disabled"
* data-trigger-type="enable"
* data-trigger-type="enable"
* data-trigger="#cblist input[type=checkbox]"
* data-trigger-condition="checked" ... >
*
@ -28,7 +31,7 @@
var TriggerOn = function (element, options) {
var $el = this.$el = $(element);
this.options = options || {};
if (this.options.triggerCondition === false)
@ -40,10 +43,20 @@
if (this.options.triggerType === false)
throw new Error('Trigger type is not specified.')
if (this.options.triggerCondition == 'checked')
this.triggerCondition = this.options.triggerCondition
if (this.options.triggerCondition.indexOf('value') == 0) {
var match = this.options.triggerCondition.match(/[^[\]]+(?=])/g)
if (match) {
this.triggerConditionValue = match
this.triggerCondition = 'value'
}
}
if (this.triggerCondition == 'checked' || this.triggerCondition == 'value')
$(document).on('change', this.options.trigger, $.proxy(this.onConditionChanged, this))
var self = this;
var self = this
$el.on('oc.triggerOn.update', function(e){
e.stopPropagation()
self.onConditionChanged()
@ -53,8 +66,12 @@
}
TriggerOn.prototype.onConditionChanged = function() {
if (this.options.triggerCondition == 'checked')
this.updateTarget($(this.options.trigger + ':checked').length > 0);
if (this.triggerCondition == 'checked') {
this.updateTarget($(this.options.trigger + ':checked').length > 0)
}
else if (this.triggerCondition == 'value') {
this.updateTarget($(this.options.trigger).val() == this.triggerConditionValue)
}
}
TriggerOn.prototype.updateTarget = function(status) {

View File

@ -1,5 +1,6 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1, user-scalable=0">
<link rel="icon" type="image/png" href="<?= URL::asset('modules/backend/assets/images/favicon.png') ?>" />
<title data-title-template="<?= empty($this->pageTitleTemplate) ? '%s | October CMS' : e($this->pageTitleTemplate) ?>">
<?= $this->pageTitle ?> | October CMS
</title>

View File

@ -400,7 +400,14 @@ class Controller extends BaseController
return Response::make($responseContents, 406);
}
catch (Exception $ex) {
return Response::make($ex->getMessage(), 500);
/*
* Display a "dumbed down" error if custom page is activated
* otherwise display a more detailed error.
*/
if (Config::get('cms.customErrorPage', false))
return Response::make($ex->getMessage(), 500);
return Response::make(sprintf('"%s" on line %s of %s', $ex->getMessage(), $ex->getLine(), $ex->getFile()), 500);
}
}

View File

@ -175,7 +175,7 @@ class Theme
$result = [];
foreach ($it as $fileinfo) {
if ($fileinfo->isDot() || (substr($fileinfo->getFilename(), 0, 1) == '.'))
if (!$fileinfo->isDir() || $fileinfo->isDot())
continue;
$theme = new static;

View File

@ -14,6 +14,10 @@
.control-updatelist h5:first-of-type {
border-top: none;
}
.control-updatelist h5 i {
margin-right: 7px;
color: #405261;
}
.control-updatelist h5 small {
text-transform: none;
float: right;

View File

@ -18,6 +18,11 @@
border-top: none;
}
i {
margin-right: 7px;
color: @color-text-title;
}
small {
text-transform: none;
float: right;

View File

@ -206,6 +206,16 @@ class UpdateManager
}
$result['plugins'] = $plugins;
/*
* Strip out themes that have been installed before
*/
$themes = [];
foreach (array_get($result, 'themes', []) as $code => $info) {
if (!$this->isThemeInstalled($code))
$themes[$code] = $info;
}
$result['themes'] = $themes;
Parameters::set('system::update.count', array_get($result, 'update', 0));
return $result;
@ -433,6 +443,58 @@ class UpdateManager
@unlink($filePath);
}
//
// Themes
//
/**
* Downloads a theme from the update server.
* @param string $name Theme name.
* @param string $hash Expected file hash.
* @return self
*/
public function downloadTheme($name, $hash)
{
$fileCode = $name . $hash;
$this->requestServerFile('theme/get', $fileCode, $hash, ['name' => $name]);
}
/**
* Extracts a theme after it has been downloaded.
*/
public function extractTheme($name, $hash)
{
$fileCode = $name . $hash;
$filePath = $this->getFilePath($fileCode);
if (!Zip::extract($filePath, $this->baseDirectory . '/themes/'))
throw new ApplicationException(Lang::get('system::lang.zip.extract_failed', ['file' => $filePath]));
$this->setThemeInstalled($name);
@unlink($filePath);
}
/**
* Checks if a theme has ever been installed before.
* @param string $name Theme code
* @return boolean
*/
public function isThemeInstalled($name)
{
return array_key_exists($name, Parameters::get('system::theme.history', []));
}
/**
* Flags a theme as being installed, so it is not downloaded twice.
* @param string $name Theme code
*/
public function setThemeInstalled($name)
{
$history = Parameters::get('system::theme.history', []);
$history[$name] = Carbon::now()->timestamp;
Parameters::set('system::theme.history', $history);
}
//
// Notes
//

View File

@ -1,6 +1,7 @@
<?php namespace System\Console;
use Str;
use Config;
use Illuminate\Console\Command;
use System\Classes\UpdateManager;
use Symfony\Component\Console\Input\InputOption;
@ -35,9 +36,29 @@ class OctoberUpdate extends Command
$this->output->writeln('<info>Updating October...</info>');
$manager = UpdateManager::instance()->resetNotes();
$forceUpdate = $this->option('force');
$pluginsOnly = $this->option('plugins');
$coreOnly = $this->option('core');
/*
* Check for disabilities
*/
$disableCore = $disablePlugins = $disableThemes = false;
if ($this->option('plugins')) {
$disableCore = true;
$disableThemes = true;
}
if ($this->option('core')) {
$disablePlugins = true;
$disableThemes = true;
}
if (Config::get('cms.disableCoreUpdates', false)) {
$disableCore = true;
}
/*
* Perform update
*/
$updateList = $manager->requestUpdateList($forceUpdate);
$updates = (int)array_get($updateList, 'update', 0);
@ -49,7 +70,7 @@ class OctoberUpdate extends Command
$this->output->writeln(sprintf('<info>Found %s new %s!</info>', $updates, Str::plural('update', $updates)));
}
$coreHash = $pluginsOnly ? null : array_get($updateList, 'core.hash');
$coreHash = $disableCore ? null : array_get($updateList, 'core.hash');
$coreBuild = array_get($updateList, 'core.build');
if ($coreHash) {
@ -57,7 +78,7 @@ class OctoberUpdate extends Command
$manager->downloadCore($coreHash);
}
$plugins = $coreOnly ? [] : array_get($updateList, 'plugins');
$plugins = $disablePlugins ? [] : array_get($updateList, 'plugins');
foreach ($plugins as $code => $plugin) {
$pluginName = array_get($plugin, 'name');
$pluginHash = array_get($plugin, 'hash');

View File

@ -4,6 +4,7 @@ use Str;
use Lang;
use File;
use Flash;
use Config;
use Backend;
use Redirect;
use BackendMenu;
@ -31,6 +32,11 @@ class Updates extends Controller
public $listConfig = ['list' => 'config_list.yaml', 'manage' => 'config_manage_list.yaml'];
/**
* @var boolean If set to true, core updates will not be downloaded or extracted.
*/
protected $disableCoreUpdates = false;
public function __construct()
{
parent::__construct();
@ -38,6 +44,8 @@ class Updates extends Controller
$this->addCss('/modules/system/assets/css/updates.css', 'core');
BackendMenu::setContext('October.System', 'system', 'updates');
$this->disableCoreUpdates = Config::get('cms.disableCoreUpdates', false);
}
/**
@ -98,10 +106,12 @@ class Updates extends Controller
switch ($stepCode) {
case 'downloadCore':
if ($this->disableCoreUpdates) return;
$manager->downloadCore(post('hash'));
break;
case 'extractCore':
if ($this->disableCoreUpdates) return;
$manager->extractCore(post('hash'), post('build'));
break;
@ -109,10 +119,18 @@ class Updates extends Controller
$manager->downloadPlugin(post('name'), post('hash'));
break;
case 'downloadTheme':
$manager->downloadTheme(post('name'), post('hash'));
break;
case 'extractPlugin':
$manager->extractPlugin(post('name'), post('hash'));
break;
case 'extractTheme':
$manager->extractTheme(post('name'), post('hash'));
break;
case 'completeUpdate':
$manager->update();
Flash::success(Lang::get('system::lang.updates.update_success'));
@ -148,7 +166,8 @@ class Updates extends Controller
$this->vars['core'] = array_get($result, 'core', false);
$this->vars['hasUpdates'] = array_get($result, 'update', false);
$this->vars['updateList'] = array_get($result, 'plugins', []);
$this->vars['pluginList'] = array_get($result, 'plugins', []);
$this->vars['themeList'] = array_get($result, 'themes', []);
}
catch (Exception $ex) {
$this->handleError($ex);
@ -176,10 +195,16 @@ class Updates extends Controller
$plugins[$code] = array_get($plugin, 'hash', null);
}
$themes = [];
$themeList = array_get($result, 'themes', []);
foreach ($themeList as $code => $theme) {
$themes[$code] = array_get($theme, 'hash', null);
}
/*
* Update steps
*/
$updateSteps = $this->buildUpdateSteps($core, $plugins);
$updateSteps = $this->buildUpdateSteps($core, $plugins, $themes);
/*
* Finish up
@ -204,18 +229,20 @@ class Updates extends Controller
public function onApplyUpdates()
{
try {
$plugins = post('plugins', []);
if (!is_array($plugins))
$plugins = [];
$coreHash = post('hash');
$coreBuild = post('build');
$core = [$coreHash, $coreBuild];
$plugins = post('plugins', []);
if (!is_array($plugins)) $plugins = [];
$themes = post('themes', []);
if (!is_array($themes)) $themes = [];
/*
* Update steps
*/
$updateSteps = $this->buildUpdateSteps($core, $plugins);
$updateSteps = $this->buildUpdateSteps($core, $plugins, $themes);
/*
* Finish up
@ -234,13 +261,16 @@ class Updates extends Controller
return $this->makePartial('execute');
}
private function buildUpdateSteps($core, $plugins)
private function buildUpdateSteps($core, $plugins, $themes)
{
if (!is_array($core))
$core = [null, null];
if (!is_array($plugins))
$plugins = [];
if (!is_array($core))
$core = [null, null];
if (!is_array($themes))
$themes = [];
$updateSteps = [];
list($coreHash, $coreBuild) = $core;
@ -265,6 +295,15 @@ class Updates extends Controller
];
}
foreach ($themes as $name => $hash) {
$updateSteps[] = [
'code' => 'downloadTheme',
'label' => Lang::get('system::lang.updates.theme_downloading', compact('name')),
'name' => $name,
'hash' => $hash
];
}
/*
* Extract
*/
@ -286,6 +325,15 @@ class Updates extends Controller
];
}
foreach ($themes as $name => $hash) {
$updateSteps[] = [
'code' => 'extractTheme',
'label' => Lang::get('system::lang.updates.theme_extracting', compact('name')),
'name' => $name,
'hash' => $hash
];
}
return $updateSteps;
}

View File

@ -13,9 +13,10 @@
<?php if ($core): ?>
<h5>
<i class="icon-cube"></i>
<?= e(trans('system::lang.system.name')) ?>
<?php if ($core['old_build']): ?>
<?= e(trans('system::lang.updates.core_build_old', ['build'=>$core['old_build']])) ?>
<small><?= e(trans('system::lang.updates.core_build_old', ['build'=>$core['old_build']])) ?></small>
<?php endif ?>
</h5>
<dl>
@ -26,8 +27,23 @@
<input type="hidden" name="build" value="<?= e($core['build']) ?>" />
<?php endif ?>
<?php foreach ($updateList as $code => $plugin): ?>
<?php foreach ($themeList as $code => $theme): ?>
<h5>
<i class="icon-picture-o"></i>
<?= e(array_get($theme, 'name', 'Unknown')) ?>
<small><?= e(trans('system::lang.updates.theme_label')) ?></small>
</h5>
<dl>
<dt><?= e(array_get($theme, 'version', 'v1.0.0')) ?></dt>
<dd><?= e(trans('system::lang.updates.theme_new_install')) ?></dd>
</dl>
<input type="hidden" name="themes[<?= e($code) ?>]" value="<?= e($theme['hash']) ?>" />
<?php endforeach ?>
<?php foreach ($pluginList as $code => $plugin): ?>
<h5>
<i class="icon-puzzle-piece"></i>
<?= e($plugin['name']) ?>
<?php if ($plugin['old_version']): ?>

View File

@ -138,6 +138,10 @@ return [
'plugin_version_none' => 'New plugin',
'plugin_version_old' => 'Current v:version',
'plugin_version_new' => 'v:version',
'theme_label' => 'Theme',
'theme_new_install' => 'New theme installation.',
'theme_downloading' => 'Downloading theme: :name',
'theme_extracting' => 'Unpacking theme: :name',
'update_label' => 'Update software',
'update_completing' => 'Finishing update process',
'update_loading' => 'Loading available updates...',