diff --git a/CHANGELOG.md b/CHANGELOG.md
index 15ae281d8..1f463a17f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -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.
diff --git a/app/config/cms.php b/app/config/cms.php
index 239b96556..a0d940450 100644
--- a/app/config/cms.php
+++ b/app/config/cms.php
@@ -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
diff --git a/app/config/database.php b/app/config/database.php
index d79cb0441..fa82fbbf3 100644
--- a/app/config/database.php
+++ b/app/config/database.php
@@ -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' => '',
diff --git a/app/start/global.php b/app/start/global.php
index a5b2c6aff..1241255d8 100644
--- a/app/start/global.php
+++ b/app/start/global.php
@@ -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
+ */
});
/*
diff --git a/modules/backend/assets/images/favicon.png b/modules/backend/assets/images/favicon.png
new file mode 100644
index 000000000..037163135
Binary files /dev/null and b/modules/backend/assets/images/favicon.png differ
diff --git a/modules/backend/assets/js/october.triggerapi.js b/modules/backend/assets/js/october.triggerapi.js
index 629de2372..aa5b811da 100644
--- a/modules/backend/assets/js/october.triggerapi.js
+++ b/modules/backend/assets/js/october.triggerapi.js
@@ -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:
*
@@ -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) {
diff --git a/modules/backend/layouts/_head.htm b/modules/backend/layouts/_head.htm
index 2d33dc454..8dceb3af1 100644
--- a/modules/backend/layouts/_head.htm
+++ b/modules/backend/layouts/_head.htm
@@ -1,5 +1,6 @@
+
= $this->pageTitle ?> | October CMS
diff --git a/modules/cms/classes/Controller.php b/modules/cms/classes/Controller.php
index 94017b350..414fa6fae 100644
--- a/modules/cms/classes/Controller.php
+++ b/modules/cms/classes/Controller.php
@@ -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);
}
}
diff --git a/modules/cms/classes/Theme.php b/modules/cms/classes/Theme.php
index d53d5ad9a..ca7b92715 100644
--- a/modules/cms/classes/Theme.php
+++ b/modules/cms/classes/Theme.php
@@ -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;
diff --git a/modules/system/assets/css/updates.css b/modules/system/assets/css/updates.css
index 61f9d8f20..03ea787f4 100644
--- a/modules/system/assets/css/updates.css
+++ b/modules/system/assets/css/updates.css
@@ -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;
diff --git a/modules/system/assets/less/updates.less b/modules/system/assets/less/updates.less
index acb05939c..064effe5b 100644
--- a/modules/system/assets/less/updates.less
+++ b/modules/system/assets/less/updates.less
@@ -18,6 +18,11 @@
border-top: none;
}
+ i {
+ margin-right: 7px;
+ color: @color-text-title;
+ }
+
small {
text-transform: none;
float: right;
diff --git a/modules/system/classes/UpdateManager.php b/modules/system/classes/UpdateManager.php
index f6b0d54c7..b1425d14a 100644
--- a/modules/system/classes/UpdateManager.php
+++ b/modules/system/classes/UpdateManager.php
@@ -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
//
diff --git a/modules/system/console/OctoberUpdate.php b/modules/system/console/OctoberUpdate.php
index 8e733615e..8d95e71ef 100644
--- a/modules/system/console/OctoberUpdate.php
+++ b/modules/system/console/OctoberUpdate.php
@@ -1,6 +1,7 @@
output->writeln('Updating October...');
$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('Found %s new %s!', $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');
diff --git a/modules/system/controllers/Updates.php b/modules/system/controllers/Updates.php
index 6b607b5b7..15039312b 100644
--- a/modules/system/controllers/Updates.php
+++ b/modules/system/controllers/Updates.php
@@ -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;
}
diff --git a/modules/system/controllers/updates/_update_list.htm b/modules/system/controllers/updates/_update_list.htm
index 8b0cf7e52..9949ebaaa 100644
--- a/modules/system/controllers/updates/_update_list.htm
+++ b/modules/system/controllers/updates/_update_list.htm
@@ -13,9 +13,10 @@
+
= e(trans('system::lang.system.name')) ?>
- = e(trans('system::lang.updates.core_build_old', ['build'=>$core['old_build']])) ?>
+ = e(trans('system::lang.updates.core_build_old', ['build'=>$core['old_build']])) ?>
@@ -26,8 +27,23 @@
- $plugin): ?>
+ $theme): ?>
+
+ = e(array_get($theme, 'name', 'Unknown')) ?>
+ = e(trans('system::lang.updates.theme_label')) ?>
+
+
+ - = e(array_get($theme, 'version', 'v1.0.0')) ?>
+ - = e(trans('system::lang.updates.theme_new_install')) ?>
+
+
+
+
+
+ $plugin): ?>
+
+
= e($plugin['name']) ?>
diff --git a/modules/system/lang/en/lang.php b/modules/system/lang/en/lang.php
index 4ac2c3a5b..122912ad8 100644
--- a/modules/system/lang/en/lang.php
+++ b/modules/system/lang/en/lang.php
@@ -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...',