Add branding section in admin panel

This commit is contained in:
RainLoop Team 2014-01-19 00:49:14 +04:00
parent 6ecb7006f8
commit 8890de4a86
19 changed files with 290 additions and 99 deletions

View file

@ -181,6 +181,7 @@ module.exports = function (grunt) {
"dev/Admin/General.js",
"dev/Admin/Login.js",
"dev/Admin/Branding.js",
"dev/Admin/Contacts.js",
"dev/Admin/Domains.js",
"dev/Admin/Security.js",

View file

@ -1,19 +1,19 @@
RainLoop Webmail (PHP)
==================
## About
Simple, modern & fast web-based email client
Modest system requirements, decent performance, simple installation and upgrade, no database required - all these make RainLoop Webmail a perfect choice for your email solution.
For more information about the product, check [http://rainloop.net](http://rainloop.net).
Information about installing the product, check the [documentation page](http://rainloop.net/docs/installation/).
## License
**Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported (CC BY-NC-SA 3.0)**
http://creativecommons.org/licenses/by-nc-sa/3.0/
Copyright (c) 2013 Rainloop Team
RainLoop Webmail (PHP)
==================
## About
Simple, modern & fast web-based email client
Modest system requirements, decent performance, simple installation and upgrade, no database required - all these make RainLoop Webmail a perfect choice for your email solution.
For more information about the product, check [http://rainloop.net](http://rainloop.net).
Information about installing the product, check the [documentation page](http://rainloop.net/docs/installation/).
## License
**Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported (CC BY-NC-SA 3.0)**
http://creativecommons.org/licenses/by-nc-sa/3.0/
Copyright (c) 2014 Rainloop Team

70
dev/Admin/Branding.js Normal file
View file

@ -0,0 +1,70 @@
/* RainLoop Webmail (c) RainLoop Team | Licensed under CC BY-NC-SA 3.0 */
/**
* @constructor
*/
function AdminBranding()
{
this.title = ko.observable(RL.settingsGet('Title'));
this.title.trigger = ko.observable(Enums.SaveSettingsStep.Idle);
this.loadingDesc = ko.observable(RL.settingsGet('LoadingDescription'));
this.loadingDesc.trigger = ko.observable(Enums.SaveSettingsStep.Idle);
this.loginLogo = ko.observable(RL.settingsGet('LoginLogo'));
this.loginLogo.trigger = ko.observable(Enums.SaveSettingsStep.Idle);
this.loginDescription = ko.observable(RL.settingsGet('LoginDescription'));
this.loginDescription.trigger = ko.observable(Enums.SaveSettingsStep.Idle);
this.loginCss = ko.observable(RL.settingsGet('LoginCss'));
this.loginCss.trigger = ko.observable(Enums.SaveSettingsStep.Idle);
}
Utils.addSettingsViewModel(AdminBranding, 'AdminSettingsBranding', 'Branding', 'branding');
AdminBranding.prototype.onBuild = function ()
{
var self = this;
_.delay(function () {
var
f1 = Utils.settingsSaveHelperSimpleFunction(self.title.trigger, self),
f2 = Utils.settingsSaveHelperSimpleFunction(self.loadingDesc.trigger, self),
f3 = Utils.settingsSaveHelperSimpleFunction(self.loginLogo.trigger, self),
f4 = Utils.settingsSaveHelperSimpleFunction(self.loginDescription.trigger, self),
f5 = Utils.settingsSaveHelperSimpleFunction(self.loginCss.trigger, self)
;
self.title.subscribe(function (sValue) {
RL.remote().saveAdminConfig(f1, {
'Title': Utils.trim(sValue)
});
});
self.loadingDesc.subscribe(function (sValue) {
RL.remote().saveAdminConfig(f2, {
'LoadingDescription': Utils.trim(sValue)
});
});
self.loginLogo.subscribe(function (sValue) {
RL.remote().saveAdminConfig(f3, {
'LoginLogo': Utils.trim(sValue)
});
});
self.loginDescription.subscribe(function (sValue) {
RL.remote().saveAdminConfig(f4, {
'LoginDescription': Utils.trim(sValue)
});
});
self.loginCss.subscribe(function (sValue) {
RL.remote().saveAdminConfig(f5, {
'LoginCss': Utils.trim(sValue)
});
});
}, 50);
};

View file

@ -19,9 +19,6 @@ function AdminGeneral()
this.allowAdditionalAccounts = oData.allowAdditionalAccounts;
this.allowIdentities = oData.allowIdentities;
this.title = ko.observable(RL.settingsGet('Title'));
this.loadingDesc = ko.observable(RL.settingsGet('LoadingDescription'));
this.themesOptions = ko.computed(function () {
return _.map(oData.themes(), function (sTheme) {
return {
@ -37,10 +34,8 @@ function AdminGeneral()
this.weakPassword = !!RL.settingsGet('WeakPassword');
this.titleTrigger = ko.observable(Enums.SaveSettingsStep.Idle);
this.languageTrigger = ko.observable(Enums.SaveSettingsStep.Idle);
this.themeTrigger = ko.observable(Enums.SaveSettingsStep.Idle);
this.loadingDescTrigger = ko.observable(Enums.SaveSettingsStep.Idle);
}
Utils.addSettingsViewModel(AdminGeneral, 'AdminSettingsGeneral', 'General', 'general', true);
@ -51,24 +46,10 @@ AdminGeneral.prototype.onBuild = function ()
_.delay(function () {
var
f1 = Utils.settingsSaveHelperSimpleFunction(self.titleTrigger, self),
f2 = Utils.settingsSaveHelperSimpleFunction(self.languageTrigger, self),
f3 = Utils.settingsSaveHelperSimpleFunction(self.themeTrigger, self),
f4 = Utils.settingsSaveHelperSimpleFunction(self.loadingDescTrigger, self)
f3 = Utils.settingsSaveHelperSimpleFunction(self.themeTrigger, self)
;
self.title.subscribe(function (sValue) {
RL.remote().saveAdminConfig(f1, {
'Title': Utils.trim(sValue)
});
});
self.loadingDesc.subscribe(function (sValue) {
RL.remote().saveAdminConfig(f4, {
'LoadingDescription': Utils.trim(sValue)
});
});
self.language.subscribe(function (sValue) {
RL.remote().saveAdminConfig(f2, {
'Language': Utils.trim(sValue)

View file

@ -17,7 +17,17 @@
display: inline-block;
vertical-align: middle;
text-align: center;
width: 380px;
/*width: 380px;*/
.descWrapper {
margin-bottom: 10px;
.desc {
font-size: 18px;
padding: 2px;
}
}
.loginForm {
background-color: #efefef;

View file

@ -15,7 +15,9 @@ function LoginViewModel()
this.password = ko.observable('');
this.signMe = ko.observable(false);
this.logoMain = ko.observable('RainLoop');
this.logoImg = Utils.trim(RL.settingsGet('LoginLogo'));
this.loginDescription = Utils.trim(RL.settingsGet('LoginDescription'));
this.logoCss = Utils.trim(RL.settingsGet('LoginCss'));
this.emailError = ko.observable(false);
this.loginError = ko.observable(false);

View file

@ -1,8 +1,8 @@
{
"name": "RainLoop",
"title": "RainLoop Webmail",
"version": "1.6.1",
"release": "645",
"version": "1.6.2",
"release": "659",
"description": "Simple, modern & fast web-based email client",
"homepage": "http://rainloop.net",
"main": "Gruntfile.js",

View file

@ -897,6 +897,9 @@ class Actions
'Email' => '',
'Title' => $oConfig->Get('webmail', 'title', ''),
'LoadingDescription' => $oConfig->Get('webmail', 'loading_description', ''),
'LoginLogo' => $oConfig->Get('branding', 'login_logo', ''),
'LoginDescription' => $oConfig->Get('branding', 'login_desc', ''),
'LoginCss' => $oConfig->Get('branding', 'login_css', ''),
'Token' => $oConfig->Get('security', 'csrf_protection', false) ? \RainLoop\Utils::GetCsrfToken() : '',
'InIframe' => (bool) $oConfig->Get('labs', 'in_iframe', false),
'AllowAdminPanel' => (bool) $oConfig->Get('security', 'allow_admin_panel', true),
@ -1949,6 +1952,10 @@ class Actions
$this->setConfigFromParams($oConfig, 'Title', 'webmail', 'title', 'string');
$this->setConfigFromParams($oConfig, 'LoadingDescription', 'webmail', 'loading_description', 'string');
$this->setConfigFromParams($oConfig, 'LoginLogo', 'branding', 'login_logo', 'string');
$this->setConfigFromParams($oConfig, 'LoginDescription', 'branding', 'login_desc', 'string');
$this->setConfigFromParams($oConfig, 'LoginCss', 'branding', 'login_css', 'string');
$this->setConfigFromParams($oConfig, 'TokenProtection', 'security', 'csrf_protection', 'bool');
$this->setConfigFromParams($oConfig, 'EnabledPlugins', 'plugins', 'enable', 'bool');

View file

@ -81,6 +81,12 @@ class Application extends \RainLoop\Config\AbstractConfig
0 for unlimited.')
),
'branding' => array(
'login_logo' => array(''),
'login_desc' => array(''),
'login_css' => array(''),
),
'contacts' => array(
'enable' => array(false, 'Enable contacts'),
'allow_sharing' => array(true),

View file

@ -53,6 +53,11 @@
}
}
.thm-login-desc .desc {
color: @loading-color !important;
text-shadow: @loading-text-shadow !important;
}
.thm-login {
color: @login-color !important;
border: @login-border !important;

View file

@ -0,0 +1,56 @@
<div class="b-admin-branding">
<div class="form-horizontal">
<div class="legend">
Branding
</div>
<div class="control-group">
<label class="control-label">
Page Title
</label>
<div class="controls">
<input type="text" class="span5" data-bind="value: title, saveTrigger: title.trigger" />
<div data-bind="saveTrigger: title.trigger"></div>
</div>
</div>
<div class="control-group">
<label class="control-label">
Loading Description
</label>
<div class="controls">
<input type="text" class="span5" data-bind="value: loadingDesc, saveTrigger: loadingDesc.trigger" />
<div data-bind="saveTrigger: loadingDesc.trigger"></div>
</div>
</div>
<br />
<div class="legend">
Login Screen
</div>
<div class="control-group">
<label class="control-label">
Logo
</label>
<div class="controls">
<input type="text" class="span5" placeholder="http://" data-bind="value: loginLogo, saveTrigger: loginLogo.trigger" />
<div data-bind="saveTrigger: loginLogo.trigger"></div>
</div>
</div>
<div class="control-group">
<label class="control-label">
Description
</label>
<div class="controls">
<input type="text" class="span5" data-bind="value: loginDescription, saveTrigger: loginDescription.trigger" />
<div data-bind="saveTrigger: loginDescription.trigger"></div>
</div>
</div>
<div class="control-group">
<label class="control-label">
Custom CSS
</label>
<div class="controls">
<textarea class="input-xxlarge" data-bind="value: loginCss, saveTrigger: loginCss.trigger" rows="7" spellcheck="true"></textarea>
<div style="vertical-align: top;" data-bind="saveTrigger: loginCss.trigger"></div>
</div>
</div>
</div>
</div>

View file

@ -14,9 +14,9 @@
General
</div>
<div class="control-group">
<label class="control-label">
<label class="control-label">
Language
</label>
</label>
<div class="controls">
<label class="flag-selector">
<span class="flag-wrapper">
@ -28,32 +28,14 @@
</div>
</div>
<div class="control-group">
<label class="control-label">
<label class="control-label">
Theme
</label>
</label>
<div class="controls">
<select class="span2" data-bind="options: themesOptions, value: mainTheme, optionsText: 'optText', optionsValue: 'optValue', saveTrigger: themeTrigger"></select>
<div data-bind="saveTrigger: themeTrigger"></div>
</div>
</div>
<div class="control-group">
<label class="control-label">
Title
</label>
<div class="controls">
<input type="text" class="span5" data-bind="value: title, saveTrigger: titleTrigger" />
<div data-bind="saveTrigger: titleTrigger"></div>
</div>
</div>
<div class="control-group">
<label class="control-label">
Loading Description
</label>
<div class="controls">
<input type="text" class="span5" data-bind="value: loadingDesc, saveTrigger: loadingDescTrigger" />
<div data-bind="saveTrigger: loadingDescTrigger"></div>
</div>
</div>
<div class="control-group">
<div class="controls">
<label data-bind="click: function () { allowLanguagesOnSettings(!allowLanguagesOnSettings()); }">

View file

@ -1,6 +1,17 @@
<style data-bind="text: logoCss"></style>
<div class="b-login-content">
<div class="loginFormWrapper">
<center>
{{INCLUDE/BeforeLogo/PLACE}}
<!-- ko if: logoImg -->
<div class="logoWrapper">
<img class="logoImg" data-bind="attr: {'src': logoImg }" />
</div>
<!-- /ko -->
<div class="descWrapper thm-login-desc" data-bind="visible: '' !== loginDescription">
<span class="desc" data-bind="text: loginDescription"></span>
</div>
{{INCLUDE/AfterLogo/PLACE}}
<div class="alert" data-bind="visible: '' !== submitError()">
<button type="button" class="close" data-bind="click: function () { submitError('') }">&times;</button>
<span data-bind="text: submitError"></span>

View file

@ -6197,7 +6197,15 @@ html.rl-no-preview-pane #rl-right .ui-resizable-handle {
display: inline-block;
vertical-align: middle;
text-align: center;
width: 380px;
/*width: 380px;*/
}
.b-login-content .loginFormWrapper .descWrapper {
margin-bottom: 10px;
}
.b-login-content .loginFormWrapper .descWrapper .desc {
font-size: 18px;
padding: 2px;
}
.b-login-content .loginFormWrapper .loginForm {
background-color: #efefef;

File diff suppressed because one or more lines are too long

View file

@ -5248,9 +5248,6 @@ function AdminGeneral()
this.allowAdditionalAccounts = oData.allowAdditionalAccounts;
this.allowIdentities = oData.allowIdentities;
this.title = ko.observable(RL.settingsGet('Title'));
this.loadingDesc = ko.observable(RL.settingsGet('LoadingDescription'));
this.themesOptions = ko.computed(function () {
return _.map(oData.themes(), function (sTheme) {
return {
@ -5266,10 +5263,8 @@ function AdminGeneral()
this.weakPassword = !!RL.settingsGet('WeakPassword');
this.titleTrigger = ko.observable(Enums.SaveSettingsStep.Idle);
this.languageTrigger = ko.observable(Enums.SaveSettingsStep.Idle);
this.themeTrigger = ko.observable(Enums.SaveSettingsStep.Idle);
this.loadingDescTrigger = ko.observable(Enums.SaveSettingsStep.Idle);
}
Utils.addSettingsViewModel(AdminGeneral, 'AdminSettingsGeneral', 'General', 'general', true);
@ -5280,24 +5275,10 @@ AdminGeneral.prototype.onBuild = function ()
_.delay(function () {
var
f1 = Utils.settingsSaveHelperSimpleFunction(self.titleTrigger, self),
f2 = Utils.settingsSaveHelperSimpleFunction(self.languageTrigger, self),
f3 = Utils.settingsSaveHelperSimpleFunction(self.themeTrigger, self),
f4 = Utils.settingsSaveHelperSimpleFunction(self.loadingDescTrigger, self)
f3 = Utils.settingsSaveHelperSimpleFunction(self.themeTrigger, self)
;
self.title.subscribe(function (sValue) {
RL.remote().saveAdminConfig(f1, {
'Title': Utils.trim(sValue)
});
});
self.loadingDesc.subscribe(function (sValue) {
RL.remote().saveAdminConfig(f4, {
'LoadingDescription': Utils.trim(sValue)
});
});
self.language.subscribe(function (sValue) {
RL.remote().saveAdminConfig(f2, {
'Language': Utils.trim(sValue)
@ -5400,6 +5381,75 @@ AdminLogin.prototype.onBuild = function ()
}, 50);
};
/**
* @constructor
*/
function AdminBranding()
{
this.title = ko.observable(RL.settingsGet('Title'));
this.title.trigger = ko.observable(Enums.SaveSettingsStep.Idle);
this.loadingDesc = ko.observable(RL.settingsGet('LoadingDescription'));
this.loadingDesc.trigger = ko.observable(Enums.SaveSettingsStep.Idle);
this.loginLogo = ko.observable(RL.settingsGet('LoginLogo'));
this.loginLogo.trigger = ko.observable(Enums.SaveSettingsStep.Idle);
this.loginDescription = ko.observable(RL.settingsGet('LoginDescription'));
this.loginDescription.trigger = ko.observable(Enums.SaveSettingsStep.Idle);
this.loginCss = ko.observable(RL.settingsGet('LoginCss'));
this.loginCss.trigger = ko.observable(Enums.SaveSettingsStep.Idle);
}
Utils.addSettingsViewModel(AdminBranding, 'AdminSettingsBranding', 'Branding', 'branding');
AdminBranding.prototype.onBuild = function ()
{
var self = this;
_.delay(function () {
var
f1 = Utils.settingsSaveHelperSimpleFunction(self.title.trigger, self),
f2 = Utils.settingsSaveHelperSimpleFunction(self.loadingDesc.trigger, self),
f3 = Utils.settingsSaveHelperSimpleFunction(self.loginLogo.trigger, self),
f4 = Utils.settingsSaveHelperSimpleFunction(self.loginDescription.trigger, self),
f5 = Utils.settingsSaveHelperSimpleFunction(self.loginCss.trigger, self)
;
self.title.subscribe(function (sValue) {
RL.remote().saveAdminConfig(f1, {
'Title': Utils.trim(sValue)
});
});
self.loadingDesc.subscribe(function (sValue) {
RL.remote().saveAdminConfig(f2, {
'LoadingDescription': Utils.trim(sValue)
});
});
self.loginLogo.subscribe(function (sValue) {
RL.remote().saveAdminConfig(f3, {
'LoginLogo': Utils.trim(sValue)
});
});
self.loginDescription.subscribe(function (sValue) {
RL.remote().saveAdminConfig(f4, {
'LoginDescription': Utils.trim(sValue)
});
});
self.loginCss.subscribe(function (sValue) {
RL.remote().saveAdminConfig(f5, {
'LoginCss': Utils.trim(sValue)
});
});
}, 50);
};
/**
* @constructor
*/

File diff suppressed because one or more lines are too long

View file

@ -10738,7 +10738,9 @@ function LoginViewModel()
this.password = ko.observable('');
this.signMe = ko.observable(false);
this.logoMain = ko.observable('RainLoop');
this.logoImg = Utils.trim(RL.settingsGet('LoginLogo'));
this.loginDescription = Utils.trim(RL.settingsGet('LoginDescription'));
this.logoCss = Utils.trim(RL.settingsGet('LoginCss'));
this.emailError = ko.observable(false);
this.loginError = ko.observable(false);

File diff suppressed because one or more lines are too long