Configs for themes in Display Settings: hide themes options, hide custom themes, specific list of themes (#7173)
* Add configuration to enable or disable choosing themes in Display Settings. Only for Licensed servers. * Add configuration to enable or disable choosing custom themes in Display Settings. Only for Licensed servers. * Add configuration to enable or disable a specific list of themes to choose in Display Settings. Only for Licensed servers. * Added config value and logic for "DefaultTheme" * Fix problem with undefined values when the server is not licensed
Этот коммит содержится в:
@@ -26,6 +26,7 @@ const (
|
|||||||
TRACK_CONFIG_RATE = "config_rate"
|
TRACK_CONFIG_RATE = "config_rate"
|
||||||
TRACK_CONFIG_EMAIL = "config_email"
|
TRACK_CONFIG_EMAIL = "config_email"
|
||||||
TRACK_CONFIG_PRIVACY = "config_privacy"
|
TRACK_CONFIG_PRIVACY = "config_privacy"
|
||||||
|
TRACK_CONFIG_THEME = "config_theme"
|
||||||
TRACK_CONFIG_OAUTH = "config_oauth"
|
TRACK_CONFIG_OAUTH = "config_oauth"
|
||||||
TRACK_CONFIG_LDAP = "config_ldap"
|
TRACK_CONFIG_LDAP = "config_ldap"
|
||||||
TRACK_CONFIG_COMPLIANCE = "config_compliance"
|
TRACK_CONFIG_COMPLIANCE = "config_compliance"
|
||||||
@@ -331,6 +332,13 @@ func trackConfig() {
|
|||||||
"show_full_name": utils.Cfg.PrivacySettings.ShowFullName,
|
"show_full_name": utils.Cfg.PrivacySettings.ShowFullName,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
SendDiagnostic(TRACK_CONFIG_THEME, map[string]interface{}{
|
||||||
|
"enable_theme_selection": *utils.Cfg.ThemeSettings.EnableThemeSelection,
|
||||||
|
"isdefault_default_theme": isDefault(*utils.Cfg.ThemeSettings.DefaultTheme, model.TEAM_SETTINGS_DEFAULT_TEAM_TEXT),
|
||||||
|
"allow_custom_themes": *utils.Cfg.ThemeSettings.AllowCustomThemes,
|
||||||
|
"allowed_themes": len(utils.Cfg.ThemeSettings.AllowedThemes),
|
||||||
|
})
|
||||||
|
|
||||||
SendDiagnostic(TRACK_CONFIG_OAUTH, map[string]interface{}{
|
SendDiagnostic(TRACK_CONFIG_OAUTH, map[string]interface{}{
|
||||||
"enable_gitlab": utils.Cfg.GitLabSettings.Enable,
|
"enable_gitlab": utils.Cfg.GitLabSettings.Enable,
|
||||||
"enable_google": utils.Cfg.GoogleSettings.Enable,
|
"enable_google": utils.Cfg.GoogleSettings.Enable,
|
||||||
|
|||||||
@@ -186,6 +186,12 @@
|
|||||||
"BannerTextColor": "#333333",
|
"BannerTextColor": "#333333",
|
||||||
"AllowBannerDismissal": true
|
"AllowBannerDismissal": true
|
||||||
},
|
},
|
||||||
|
"ThemeSettings": {
|
||||||
|
"EnableThemeSelection": true,
|
||||||
|
"DefaultTheme": "default",
|
||||||
|
"AllowCustomThemes": true,
|
||||||
|
"AllowedThemes": []
|
||||||
|
},
|
||||||
"GitLabSettings": {
|
"GitLabSettings": {
|
||||||
"Enable": false,
|
"Enable": false,
|
||||||
"Secret": "",
|
"Secret": "",
|
||||||
|
|||||||
@@ -132,6 +132,8 @@ const (
|
|||||||
ANNOUNCEMENT_SETTINGS_DEFAULT_BANNER_COLOR = "#f2a93b"
|
ANNOUNCEMENT_SETTINGS_DEFAULT_BANNER_COLOR = "#f2a93b"
|
||||||
ANNOUNCEMENT_SETTINGS_DEFAULT_BANNER_TEXT_COLOR = "#333333"
|
ANNOUNCEMENT_SETTINGS_DEFAULT_BANNER_TEXT_COLOR = "#333333"
|
||||||
|
|
||||||
|
TEAM_SETTINGS_DEFAULT_TEAM_TEXT = "default"
|
||||||
|
|
||||||
ELASTICSEARCH_SETTINGS_DEFAULT_CONNECTION_URL = ""
|
ELASTICSEARCH_SETTINGS_DEFAULT_CONNECTION_URL = ""
|
||||||
ELASTICSEARCH_SETTINGS_DEFAULT_USERNAME = ""
|
ELASTICSEARCH_SETTINGS_DEFAULT_USERNAME = ""
|
||||||
ELASTICSEARCH_SETTINGS_DEFAULT_PASSWORD = ""
|
ELASTICSEARCH_SETTINGS_DEFAULT_PASSWORD = ""
|
||||||
@@ -335,6 +337,13 @@ type AnnouncementSettings struct {
|
|||||||
AllowBannerDismissal *bool
|
AllowBannerDismissal *bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ThemeSettings struct {
|
||||||
|
EnableThemeSelection *bool
|
||||||
|
DefaultTheme *string
|
||||||
|
AllowCustomThemes *bool
|
||||||
|
AllowedThemes []string
|
||||||
|
}
|
||||||
|
|
||||||
type TeamSettings struct {
|
type TeamSettings struct {
|
||||||
SiteName string
|
SiteName string
|
||||||
MaxUsersPerTeam *int
|
MaxUsersPerTeam *int
|
||||||
@@ -500,6 +509,7 @@ type Config struct {
|
|||||||
PrivacySettings PrivacySettings
|
PrivacySettings PrivacySettings
|
||||||
SupportSettings SupportSettings
|
SupportSettings SupportSettings
|
||||||
AnnouncementSettings AnnouncementSettings
|
AnnouncementSettings AnnouncementSettings
|
||||||
|
ThemeSettings ThemeSettings
|
||||||
GitLabSettings SSOSettings
|
GitLabSettings SSOSettings
|
||||||
GoogleSettings SSOSettings
|
GoogleSettings SSOSettings
|
||||||
Office365Settings SSOSettings
|
Office365Settings SSOSettings
|
||||||
@@ -1003,6 +1013,25 @@ func (o *Config) SetDefaults() {
|
|||||||
*o.AnnouncementSettings.AllowBannerDismissal = true
|
*o.AnnouncementSettings.AllowBannerDismissal = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if o.ThemeSettings.EnableThemeSelection == nil {
|
||||||
|
o.ThemeSettings.EnableThemeSelection = new(bool)
|
||||||
|
*o.ThemeSettings.EnableThemeSelection = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if o.ThemeSettings.DefaultTheme == nil {
|
||||||
|
o.ThemeSettings.DefaultTheme = new(string)
|
||||||
|
*o.ThemeSettings.DefaultTheme = TEAM_SETTINGS_DEFAULT_TEAM_TEXT
|
||||||
|
}
|
||||||
|
|
||||||
|
if o.ThemeSettings.AllowCustomThemes == nil {
|
||||||
|
o.ThemeSettings.AllowCustomThemes = new(bool)
|
||||||
|
*o.ThemeSettings.AllowCustomThemes = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if o.ThemeSettings.AllowedThemes == nil {
|
||||||
|
o.ThemeSettings.AllowedThemes = []string{}
|
||||||
|
}
|
||||||
|
|
||||||
if o.LdapSettings.Enable == nil {
|
if o.LdapSettings.Enable == nil {
|
||||||
o.LdapSettings.Enable = new(bool)
|
o.LdapSettings.Enable = new(bool)
|
||||||
*o.LdapSettings.Enable = false
|
*o.LdapSettings.Enable = false
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ type Features struct {
|
|||||||
PasswordRequirements *bool `json:"password_requirements"`
|
PasswordRequirements *bool `json:"password_requirements"`
|
||||||
Elasticsearch *bool `json:"elastic_search"`
|
Elasticsearch *bool `json:"elastic_search"`
|
||||||
Announcement *bool `json:"announcement"`
|
Announcement *bool `json:"announcement"`
|
||||||
|
ThemeManagement *bool `json:"theme_management"`
|
||||||
EmailNotificationContents *bool `json:"email_notification_contents"`
|
EmailNotificationContents *bool `json:"email_notification_contents"`
|
||||||
|
|
||||||
// after we enabled more features for webrtc we'll need to control them with this
|
// after we enabled more features for webrtc we'll need to control them with this
|
||||||
@@ -152,6 +153,11 @@ func (f *Features) SetDefaults() {
|
|||||||
*f.Announcement = true
|
*f.Announcement = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if f.ThemeManagement == nil {
|
||||||
|
f.ThemeManagement = new(bool)
|
||||||
|
*f.ThemeManagement = true
|
||||||
|
}
|
||||||
|
|
||||||
if f.EmailNotificationContents == nil {
|
if f.EmailNotificationContents == nil {
|
||||||
f.EmailNotificationContents = new(bool)
|
f.EmailNotificationContents = new(bool)
|
||||||
*f.EmailNotificationContents = *f.FutureFeatures
|
*f.EmailNotificationContents = *f.FutureFeatures
|
||||||
|
|||||||
@@ -539,7 +539,6 @@ func getClientConfig(c *model.Config) map[string]string {
|
|||||||
props["PluginsEnabled"] = strconv.FormatBool(*c.PluginSettings.Enable)
|
props["PluginsEnabled"] = strconv.FormatBool(*c.PluginSettings.Enable)
|
||||||
|
|
||||||
if IsLicensed() {
|
if IsLicensed() {
|
||||||
|
|
||||||
License := License()
|
License := License()
|
||||||
props["ExperimentalTownSquareIsReadOnly"] = strconv.FormatBool(*c.TeamSettings.ExperimentalTownSquareIsReadOnly)
|
props["ExperimentalTownSquareIsReadOnly"] = strconv.FormatBool(*c.TeamSettings.ExperimentalTownSquareIsReadOnly)
|
||||||
|
|
||||||
@@ -605,6 +604,13 @@ func getClientConfig(c *model.Config) map[string]string {
|
|||||||
props["BannerTextColor"] = *c.AnnouncementSettings.BannerTextColor
|
props["BannerTextColor"] = *c.AnnouncementSettings.BannerTextColor
|
||||||
props["AllowBannerDismissal"] = strconv.FormatBool(*c.AnnouncementSettings.AllowBannerDismissal)
|
props["AllowBannerDismissal"] = strconv.FormatBool(*c.AnnouncementSettings.AllowBannerDismissal)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if *License.Features.ThemeManagement {
|
||||||
|
props["EnableThemeSelection"] = strconv.FormatBool(*c.ThemeSettings.EnableThemeSelection)
|
||||||
|
props["DefaultTheme"] = *c.ThemeSettings.DefaultTheme
|
||||||
|
props["AllowCustomThemes"] = strconv.FormatBool(*c.ThemeSettings.AllowCustomThemes)
|
||||||
|
props["AllowedThemes"] = strings.Join(c.ThemeSettings.AllowedThemes, ",")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return props
|
return props
|
||||||
|
|||||||
@@ -18,8 +18,15 @@ export default class PremadeThemeChooser extends React.Component {
|
|||||||
const theme = this.props.theme;
|
const theme = this.props.theme;
|
||||||
|
|
||||||
const premadeThemes = [];
|
const premadeThemes = [];
|
||||||
|
const allowedThemes = global.mm_config.AllowedThemes ? global.mm_config.AllowedThemes.split(',') : [];
|
||||||
|
const hasAllowedThemes = allowedThemes.length > 1 || (allowedThemes[0] && allowedThemes[0].trim().length > 0);
|
||||||
|
|
||||||
for (const k in Constants.THEMES) {
|
for (const k in Constants.THEMES) {
|
||||||
if (Constants.THEMES.hasOwnProperty(k)) {
|
if (Constants.THEMES.hasOwnProperty(k)) {
|
||||||
|
if (hasAllowedThemes && allowedThemes.indexOf(k) < 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
const premadeTheme = $.extend(true, {}, Constants.THEMES[k]);
|
const premadeTheme = $.extend(true, {}, Constants.THEMES[k]);
|
||||||
|
|
||||||
let activeClass = '';
|
let activeClass = '';
|
||||||
|
|||||||
@@ -595,6 +595,18 @@ export default class UserSettingsDisplay extends React.Component {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let themeSection;
|
||||||
|
if (global.mm_config.EnableThemeSelection !== 'false') {
|
||||||
|
themeSection = (
|
||||||
|
<ThemeSetting
|
||||||
|
selected={this.props.activeSection === 'theme'}
|
||||||
|
updateSection={this.updateSection}
|
||||||
|
setRequireConfirm={this.props.setRequireConfirm}
|
||||||
|
setEnforceFocus={this.props.setEnforceFocus}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className='modal-header'>
|
<div className='modal-header'>
|
||||||
@@ -632,12 +644,7 @@ export default class UserSettingsDisplay extends React.Component {
|
|||||||
/>
|
/>
|
||||||
</h3>
|
</h3>
|
||||||
<div className='divider-dark first'/>
|
<div className='divider-dark first'/>
|
||||||
<ThemeSetting
|
{themeSection}
|
||||||
selected={this.props.activeSection === 'theme'}
|
|
||||||
updateSection={this.updateSection}
|
|
||||||
setRequireConfirm={this.props.setRequireConfirm}
|
|
||||||
setEnforceFocus={this.props.setEnforceFocus}
|
|
||||||
/>
|
|
||||||
<div className='divider-dark'/>
|
<div className='divider-dark'/>
|
||||||
{clockSection}
|
{clockSection}
|
||||||
<div className='divider-dark'/>
|
<div className='divider-dark'/>
|
||||||
|
|||||||
@@ -180,10 +180,11 @@ export default class ThemeSetting extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const displayCustom = this.state.type === 'custom';
|
const displayCustom = this.state.type === 'custom';
|
||||||
|
const allowCustomThemes = global.mm_config.AllowCustomThemes !== 'false';
|
||||||
|
|
||||||
let custom;
|
let custom;
|
||||||
let premade;
|
let premade;
|
||||||
if (displayCustom) {
|
if (displayCustom && allowCustomThemes) {
|
||||||
custom = (
|
custom = (
|
||||||
<div key='customThemeChooser'>
|
<div key='customThemeChooser'>
|
||||||
<CustomThemeChooser
|
<CustomThemeChooser
|
||||||
@@ -208,87 +209,91 @@ export default class ThemeSetting extends React.Component {
|
|||||||
if (this.props.selected) {
|
if (this.props.selected) {
|
||||||
const inputs = [];
|
const inputs = [];
|
||||||
|
|
||||||
inputs.push(
|
if (allowCustomThemes) {
|
||||||
<div
|
inputs.push(
|
||||||
className='radio'
|
<div
|
||||||
key='premadeThemeColorLabel'
|
className='radio'
|
||||||
>
|
key='premadeThemeColorLabel'
|
||||||
<label>
|
>
|
||||||
<input
|
<label>
|
||||||
id='standardThemes'
|
<input
|
||||||
type='radio'
|
id='standardThemes'
|
||||||
name='theme'
|
type='radio'
|
||||||
checked={!displayCustom}
|
name='theme'
|
||||||
onChange={this.updateType.bind(this, 'premade')}
|
checked={!displayCustom}
|
||||||
/>
|
onChange={this.updateType.bind(this, 'premade')}
|
||||||
<FormattedMessage
|
/>
|
||||||
id='user.settings.display.theme.themeColors'
|
<FormattedMessage
|
||||||
defaultMessage='Theme Colors'
|
id='user.settings.display.theme.themeColors'
|
||||||
/>
|
defaultMessage='Theme Colors'
|
||||||
</label>
|
/>
|
||||||
<br/>
|
</label>
|
||||||
</div>
|
<br/>
|
||||||
);
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
inputs.push(premade);
|
inputs.push(premade);
|
||||||
|
|
||||||
inputs.push(
|
if (allowCustomThemes) {
|
||||||
<div
|
inputs.push(
|
||||||
className='radio'
|
<div
|
||||||
key='customThemeColorLabel'
|
className='radio'
|
||||||
>
|
key='customThemeColorLabel'
|
||||||
<label>
|
|
||||||
<input
|
|
||||||
id='customThemes'
|
|
||||||
type='radio'
|
|
||||||
name='theme'
|
|
||||||
checked={displayCustom}
|
|
||||||
onChange={this.updateType.bind(this, 'custom')}
|
|
||||||
/>
|
|
||||||
<FormattedMessage
|
|
||||||
id='user.settings.display.theme.customTheme'
|
|
||||||
defaultMessage='Custom Theme'
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
inputs.push(custom);
|
|
||||||
|
|
||||||
inputs.push(
|
|
||||||
<div key='otherThemes'>
|
|
||||||
<br/>
|
|
||||||
<a
|
|
||||||
id='otherThemes'
|
|
||||||
href='http://docs.mattermost.com/help/settings/theme-colors.html#custom-theme-examples'
|
|
||||||
target='_blank'
|
|
||||||
rel='noopener noreferrer'
|
|
||||||
>
|
>
|
||||||
<FormattedMessage
|
<label>
|
||||||
id='user.settings.display.theme.otherThemes'
|
<input
|
||||||
defaultMessage='See other themes'
|
id='customThemes'
|
||||||
/>
|
type='radio'
|
||||||
</a>
|
name='theme'
|
||||||
</div>
|
checked={displayCustom}
|
||||||
);
|
onChange={this.updateType.bind(this, 'custom')}
|
||||||
|
/>
|
||||||
|
<FormattedMessage
|
||||||
|
id='user.settings.display.theme.customTheme'
|
||||||
|
defaultMessage='Custom Theme'
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
inputs.push(
|
inputs.push(custom);
|
||||||
<div
|
|
||||||
key='importSlackThemeButton'
|
inputs.push(
|
||||||
className='padding-top'
|
<div key='otherThemes'>
|
||||||
>
|
<br/>
|
||||||
<a
|
<a
|
||||||
id='slackImportTheme'
|
id='otherThemes'
|
||||||
className='theme'
|
href='http://docs.mattermost.com/help/settings/theme-colors.html#custom-theme-examples'
|
||||||
onClick={this.handleImportModal}
|
target='_blank'
|
||||||
|
rel='noopener noreferrer'
|
||||||
|
>
|
||||||
|
<FormattedMessage
|
||||||
|
id='user.settings.display.theme.otherThemes'
|
||||||
|
defaultMessage='See other themes'
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
inputs.push(
|
||||||
|
<div
|
||||||
|
key='importSlackThemeButton'
|
||||||
|
className='padding-top'
|
||||||
>
|
>
|
||||||
<FormattedMessage
|
<a
|
||||||
id='user.settings.display.theme.import'
|
id='slackImportTheme'
|
||||||
defaultMessage='Import theme colors from Slack'
|
className='theme'
|
||||||
/>
|
onClick={this.handleImportModal}
|
||||||
</a>
|
>
|
||||||
</div>
|
<FormattedMessage
|
||||||
);
|
id='user.settings.display.theme.import'
|
||||||
|
defaultMessage='Import theme colors from Slack'
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
let allTeamsCheckbox = null;
|
let allTeamsCheckbox = null;
|
||||||
if (this.state.showAllTeamsCheckbox) {
|
if (this.state.showAllTeamsCheckbox) {
|
||||||
|
|||||||
@@ -141,6 +141,12 @@ class PreferenceStore extends EventEmitter {
|
|||||||
return this.getObject(Constants.Preferences.CATEGORY_THEME, '');
|
return this.getObject(Constants.Preferences.CATEGORY_THEME, '');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const k in Constants.THEMES) {
|
||||||
|
if (Constants.THEMES.hasOwnProperty(k) && k === global.mm_config.DefaultTheme) {
|
||||||
|
return Constants.THEMES[k];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return Constants.THEMES.default;
|
return Constants.THEMES.default;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user