requirement from code review backport from EE
This commit is contained in:
@@ -17,7 +17,7 @@ import (
|
||||
type userUpdatePayload struct {
|
||||
Username string `validate:"required" example:"bob"`
|
||||
Password string `validate:"required" example:"cg9Wgky3"`
|
||||
Usertheme string `example:"dark"`
|
||||
UserTheme string `example:"dark"`
|
||||
// User role (1 for administrator account and 2 for regular account)
|
||||
Role int `validate:"required" enums:"1,2" example:"2"`
|
||||
}
|
||||
@@ -105,8 +105,8 @@ func (handler *Handler) userUpdate(w http.ResponseWriter, r *http.Request) *http
|
||||
user.Role = portainer.UserRole(payload.Role)
|
||||
}
|
||||
|
||||
if payload.Usertheme != "" {
|
||||
user.Usertheme = payload.Usertheme
|
||||
if payload.UserTheme != "" {
|
||||
user.UserTheme = payload.UserTheme
|
||||
}
|
||||
|
||||
err = handler.DataStore.User().UpdateUser(user.ID, user)
|
||||
|
||||
+1
-1
@@ -991,7 +991,7 @@ type (
|
||||
Username string `json:"Username" example:"bob"`
|
||||
Password string `json:"Password,omitempty" example:"passwd"`
|
||||
// User Theme
|
||||
Usertheme string `json:"Usertheme" example:"dark"`
|
||||
UserTheme string `example:"dark"`
|
||||
// User role (1 for administrator account and 2 for regular account)
|
||||
Role UserRole `json:"Role" example:"1"`
|
||||
|
||||
|
||||
+49
-13
@@ -84,6 +84,7 @@
|
||||
--text-multiselect-button-color: #fff;
|
||||
--text-multiselect-item-color: #fff;
|
||||
--text-sidebar-list-color: #fff;
|
||||
--text-boxselector-wrapper-color: #fff;
|
||||
|
||||
--border-color: #383838;
|
||||
--border-widget-color: #212121;
|
||||
@@ -117,6 +118,8 @@
|
||||
--button-close-color: #fff;
|
||||
--button-opacity: 0.6;
|
||||
--button-opacity-hover: 0.3;
|
||||
--shadow-box-color: none;
|
||||
--shadow-boxselector-color: none;
|
||||
}
|
||||
|
||||
:root[theme='highcontrast'] {
|
||||
@@ -135,6 +138,18 @@
|
||||
--bg-boxselector-wrapper-disabled-color: #194973;
|
||||
--bg-dropdown-menu-color: #000;
|
||||
--bg-codemirror-selected-color: #383838;
|
||||
--bg-row-header-color: #000;
|
||||
--bg-sidebar-wrapper-color: #000;
|
||||
--bg-motd-body-color: #000;
|
||||
--bg-blocklist-hover-color: #000;
|
||||
--bg-blocklist-item-selected-color: #000;
|
||||
--bg-input-group-addon-color: #000;
|
||||
--bg-table-color: #000;
|
||||
--bg-codemirror-gutters-color: #000;
|
||||
--bg-codemirror-color: #000;
|
||||
--bg-codemirror-selected-color: #383838;
|
||||
--bg-log-viewer-color: #000;
|
||||
--bg-log-line-selected-color: #383838;
|
||||
|
||||
--text-main-color: #fff;
|
||||
--text-body-color: #fff;
|
||||
@@ -142,11 +157,20 @@
|
||||
--text-widget-header-color: #ffffff;
|
||||
--text-link-color: #3ea6ff;
|
||||
--text-link-hover-color: #3ea6ff;
|
||||
--text-code-color: #fff;
|
||||
--text-danger-color: #ff00e0;
|
||||
--text-code-color: #000;
|
||||
--text-form-control-color: #fff;
|
||||
--text-blocklist-hover-color: #3ea5ff;
|
||||
--text-danger-color: #ff00e0;
|
||||
--text-dropdown-menu-color: #fff;
|
||||
--text-boxselector-wrapper-color: #fff;
|
||||
--text-dashboard-item-color: #41a6ff;
|
||||
--text-form-section-title-color: #fff;
|
||||
--text-muted-color: #fff;
|
||||
--text-tooltip-color: #fff;
|
||||
--text-blocklist-item-selected-color: #3ea6ff;
|
||||
--text-input-group-addon-color: #fff;
|
||||
--text-sidebar-list-color: #fff;
|
||||
--text-codemirror-color: #fff;
|
||||
--text-log-viewer-color: #fff;
|
||||
|
||||
--border-color: rgba(255, 255, 255, 0.8);
|
||||
--border-widget-color: #fff;
|
||||
@@ -157,13 +181,18 @@
|
||||
--border-datatable-top-color: rgba(255, 255, 255, 0.8);
|
||||
--border-sidebar-high-contrast: 1px solid #3ea6ff;
|
||||
--border-code-high-contrast: 1px solid #fff;
|
||||
--border-boxselector-wrapper: 3px solid #337ab7;
|
||||
--border-boxselector-wrapper-hover: 3px solid #90ccff;
|
||||
--border-panel-color: #fff;
|
||||
--border-input-group-addon-color: rgb(54, 54, 54);
|
||||
|
||||
--hover-sidebar-color: #3ea6ff;
|
||||
--hover-sidebar-color: #000;
|
||||
--shadow-box-color: none;
|
||||
--shadow-boxselector-color: none;
|
||||
}
|
||||
|
||||
:root[theme='light'] {
|
||||
:root {
|
||||
--bg-card-color: #f6f6f6;
|
||||
--bg-main-color: #fff;
|
||||
--bg-body-color: #f3f3f3;
|
||||
@@ -395,7 +424,6 @@ fieldset[disabled] .form-control {
|
||||
code {
|
||||
color: var(--text-code-color);
|
||||
background-color: var(--bg-code-color);
|
||||
border: var(--border-code-high-contrast);
|
||||
}
|
||||
|
||||
.nav-tabs > li.active > a,
|
||||
@@ -528,6 +556,12 @@ json-tree .branch-preview {
|
||||
.pagination > li > span:focus {
|
||||
background-color: var(--bg-pagination-hover-color);
|
||||
border-color: var(--border-pagination-hover-color);
|
||||
}
|
||||
|
||||
.pagination > li > a:hover,
|
||||
.pagination > li > span:hover,
|
||||
.pagination > li > a:focus,
|
||||
.pagination > li > span:focus {
|
||||
color: var(--text-pagination-span-hover-color);
|
||||
}
|
||||
|
||||
@@ -545,14 +579,6 @@ json-tree .branch-preview {
|
||||
background-color: var(--bg-motd-body-color) !important;
|
||||
}
|
||||
|
||||
.multiSelect .checkboxLayer {
|
||||
background-color: var(--bg-multiselect-checkbox-color);
|
||||
}
|
||||
|
||||
.multiSelect .multiSelectItem {
|
||||
color: var(--text-multiselect-item-color);
|
||||
}
|
||||
|
||||
.panel-body {
|
||||
background-color: var(--bg-panel-body-color) !important;
|
||||
}
|
||||
@@ -564,6 +590,16 @@ json-tree .branch-preview {
|
||||
.theme-information .col-sm-12 {
|
||||
padding-left: 0px;
|
||||
padding-right: 0px;
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.theme-panel {
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.summary {
|
||||
color: var(--text-summary-color);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
/* Overide Vendor CSS */
|
||||
|
||||
+1
-1
@@ -76,7 +76,7 @@
|
||||
<tr
|
||||
dir-paginate-end
|
||||
ng-show="$ctrl.itemCanExpand(value) && value.Expanded"
|
||||
ng-style="{ background: value.Highlighted ? 'var(--bg-item-highlighted-color)' : 'var(--bg-item-highlighted-null-color)' }"
|
||||
ng-class="{ 'datatable-highlighted': value.Highlighted, 'datatable-unhighlighted': !value.Highlighted }"
|
||||
>
|
||||
<td colspan="1"></td>
|
||||
<td colspan="1">
|
||||
|
||||
@@ -171,16 +171,7 @@
|
||||
allow-checkbox="true"
|
||||
>
|
||||
</tr>
|
||||
<tr
|
||||
dir-paginate-end
|
||||
ng-show="item.Expanded"
|
||||
ng-repeat="it in item.Subs"
|
||||
style="background: var(--bg-item-highlighted-color);"
|
||||
network-row-content
|
||||
item="it"
|
||||
parent-ctrl="$ctrl"
|
||||
>
|
||||
</tr>
|
||||
<tr dir-paginate-end ng-show="item.Expanded" ng-repeat="it in item.Subs" class="datatable-highlighted" network-row-content item="it" parent-ctrl="$ctrl"> </tr>
|
||||
<tr ng-if="!$ctrl.dataset">
|
||||
<td colspan="9" class="text-center text-muted">Loading...</td>
|
||||
</tr>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<div style="background-color: var(--bg-item-highlighted-color); padding: 2px;">
|
||||
<div class="service-datatable">
|
||||
<table class="table table-condensed table-hover nowrap-cells">
|
||||
<thead style="background-color: #e7f6ff;">
|
||||
<tr>
|
||||
|
||||
+3
-8
@@ -105,8 +105,7 @@
|
||||
<!-- dir-paginate-start track by $index -->
|
||||
<tr
|
||||
dir-paginate-start="item in ($ctrl.state.filteredDataSet = ($ctrl.dataset | filter:$ctrl.state.textFilter | filter: $ctrl.isDisplayed | orderBy:$ctrl.state.orderBy:$ctrl.state.reverseOrder | itemsPerPage: $ctrl.state.paginatedItemLimit: $ctrl.tableKey))"
|
||||
ng-class="{ active: item.Checked }"
|
||||
ng-style="{ background: item.Highlighted ? 'var(--bg-item-highlighted-color)' : '' }"
|
||||
ng-class="{ active: item.Checked, 'datatable-highlighted': item.Highlighted }"
|
||||
ng-click="$ctrl.expandItem(item, !item.Expanded)"
|
||||
pagination-id="$ctrl.tableKey"
|
||||
>
|
||||
@@ -177,11 +176,7 @@
|
||||
</td>
|
||||
</tr>
|
||||
<!-- sub rows -->
|
||||
<tr
|
||||
ng-show="item.Expanded"
|
||||
ng-repeat-start="port in item.Ports"
|
||||
ng-style="{ background: item.Highlighted ? 'var(--bg-item-highlighted-color)' : 'var(--bg-item-highlighted-null-color)' }"
|
||||
>
|
||||
<tr ng-show="item.Expanded" ng-repeat-start="port in item.Ports" ng-class="{ 'datatable-highlighted': item.Highlighted, 'datatable-unhighlighted': !item.Highlighted }">
|
||||
<td ng-if="!$ctrl.portHasIngressRules(port)"></td>
|
||||
<td ng-if="!$ctrl.portHasIngressRules(port)">-</td>
|
||||
<td ng-if="!$ctrl.portHasIngressRules(port)">-</td>
|
||||
@@ -198,7 +193,7 @@
|
||||
ng-show="item.Expanded"
|
||||
ng-repeat-end
|
||||
ng-repeat="rule in port.IngressRules"
|
||||
ng-style="{ background: item.Highlighted ? 'var(--bg-item-highlighted-color)' : 'var(--bg-item-highlighted-null-color)' }"
|
||||
ng-class="{ 'datatable-highlighted': item.Highlighted, 'datatable-unhighlighted': !item.Highlighted }"
|
||||
>
|
||||
<td></td>
|
||||
<td>-</td>
|
||||
|
||||
+2
-3
@@ -109,8 +109,7 @@
|
||||
<tbody>
|
||||
<tr
|
||||
dir-paginate-start="item in ($ctrl.state.filteredDataSet = ($ctrl.dataset | filter:$ctrl.state.textFilter | filter: $ctrl.isDisplayed | orderBy:$ctrl.state.orderBy:$ctrl.state.reverseOrder | itemsPerPage: $ctrl.state.paginatedItemLimit: $ctrl.tableKey))"
|
||||
ng-class="{ active: item.Checked }"
|
||||
ng-style="{ background: item.Highlighted ? 'var(--bg-item-highlighted-color)' : '' }"
|
||||
ng-class="{ active: item.Checked, 'datatable-highlighted': item.Highlighted }"
|
||||
ng-click="$ctrl.expandItem(item, !item.Expanded)"
|
||||
pagination-id="$ctrl.tableKey"
|
||||
>
|
||||
@@ -145,7 +144,7 @@
|
||||
dir-paginate-end
|
||||
ng-show="item.Expanded"
|
||||
ng-repeat="app in item.Applications"
|
||||
ng-style="{ background: item.Highlighted ? 'var(--bg-item-highlighted-color)' : 'var(--bg-item-highlighted-null-color)' }"
|
||||
ng-class="{ 'datatable-highlighted': item.Highlighted, 'datatable-unhighlighted': !item.Highlighted }"
|
||||
>
|
||||
<td></td>
|
||||
<td colspan="4">
|
||||
|
||||
+7
-8
@@ -75,8 +75,7 @@
|
||||
<tbody>
|
||||
<tr
|
||||
dir-paginate-start="item in ($ctrl.state.filteredDataSet = ($ctrl.dataset | filter:$ctrl.state.textFilter | filter: $ctrl.isDisplayed | orderBy:$ctrl.state.orderBy:$ctrl.state.reverseOrder | itemsPerPage: $ctrl.state.paginatedItemLimit: $ctrl.tableKey))"
|
||||
ng-class="{ active: item.Checked }"
|
||||
ng-style="{ background: item.Highlighted ? 'var(--bg-item-highlighted-color)' : '' }"
|
||||
ng-class="{ active: item.Checked, 'datatable-highlighted': item.Highlighted }"
|
||||
ng-click="$ctrl.expandItem(item, !item.Expanded)"
|
||||
pagination-id="$ctrl.tableKey"
|
||||
>
|
||||
@@ -96,7 +95,7 @@
|
||||
ng-if="$ctrl.isAdmin"
|
||||
ng-show="item.Expanded"
|
||||
ng-repeat="taint in item.UnmetTaints"
|
||||
ng-style="{ background: item.Highlighted ? 'var(--bg-item-highlighted-color)' : 'var(--bg-item-highlighted-null-color)' }"
|
||||
ng-class="{ 'datatable-highlighted': item.Highlighted, 'datatable-unhighlighted': !item.Highlighted }"
|
||||
>
|
||||
<td colspan="2">
|
||||
This application is missing a toleration for the taint <code>{{ taint.Key }}{{ taint.Value ? '=' + taint.Value : '' }}:{{ taint.Effect }}</code>
|
||||
@@ -107,7 +106,7 @@
|
||||
<tr
|
||||
ng-if="!$ctrl.isAdmin && item.UnmetTaints.length"
|
||||
ng-show="item.Expanded"
|
||||
ng-style="{ background: item.Highlighted ? 'var(--bg-item-highlighted-color)' : 'var(--bg-item-highlighted-null-color)' }"
|
||||
ng-class="{ 'datatable-highlighted': item.Highlighted, 'datatable-unhighlighted': !item.Highlighted }"
|
||||
>
|
||||
<td colspan="2">
|
||||
Placement constraint not respected for that node.
|
||||
@@ -119,7 +118,7 @@
|
||||
ng-if="$ctrl.isAdmin"
|
||||
ng-show="item.Expanded"
|
||||
ng-repeat="label in item.UnmatchedNodeSelectorLabels"
|
||||
ng-style="{ background: item.Highlighted ? 'var(--bg-item-highlighted-color)' : 'var(--bg-item-highlighted-null-color)' }"
|
||||
ng-class="{ 'datatable-highlighted': item.Highlighted, 'datatable-unhighlighted': !item.Highlighted }"
|
||||
>
|
||||
<td colspan="2">
|
||||
This application can only be scheduled on a node where the label <code>{{ label.key }}</code> is set to <code>{{ label.value }}</code>
|
||||
@@ -130,7 +129,7 @@
|
||||
<tr
|
||||
ng-if="!$ctrl.isAdmin && (item.UnmatchedNodeSelectorLabels.length || item.UnmatchedNodeAffinities.length)"
|
||||
ng-show="item.Expanded"
|
||||
ng-style="{ background: item.Highlighted ? 'var(--bg-item-highlighted-color)' : 'var(--bg-item-highlighted-null-color)' }"
|
||||
ng-class="{ 'datatable-highlighted': item.Highlighted, 'datatable-unhighlighted': !item.Highlighted }"
|
||||
>
|
||||
<td colspan="2">
|
||||
Placement label not respected for that node.
|
||||
@@ -141,7 +140,7 @@
|
||||
<tr
|
||||
ng-if="$ctrl.isAdmin"
|
||||
ng-show="item.Expanded && item.UnmatchedNodeAffinities.length"
|
||||
ng-style="{ background: item.Highlighted ? 'var(--bg-item-highlighted-color)' : 'var(--bg-item-highlighted-null-color)' }"
|
||||
ng-class="{ 'datatable-highlighted': item.Highlighted, 'datatable-unhighlighted': !item.Highlighted }"
|
||||
>
|
||||
<td colspan="2">
|
||||
This application can only be scheduled on nodes respecting one of the following labels combination:
|
||||
@@ -152,7 +151,7 @@
|
||||
ng-if="$ctrl.isAdmin"
|
||||
ng-show="item.Expanded"
|
||||
ng-repeat="aff in item.UnmatchedNodeAffinities"
|
||||
ng-style="{ background: item.Highlighted ? 'var(--bg-item-highlighted-color)' : 'var(--bg-item-highlighted-null-color)' }"
|
||||
ng-class="{ 'datatable-highlighted': item.Highlighted, 'datatable-unhighlighted': !item.Highlighted }"
|
||||
>
|
||||
<td></td>
|
||||
<td>
|
||||
|
||||
+2
-3
@@ -73,8 +73,7 @@
|
||||
<tbody>
|
||||
<tr
|
||||
dir-paginate-start="item in ($ctrl.state.filteredDataSet = ($ctrl.dataset | filter:$ctrl.state.textFilter | orderBy:$ctrl.state.orderBy:$ctrl.state.reverseOrder | itemsPerPage: $ctrl.state.paginatedItemLimit: $ctrl.tableKey))"
|
||||
ng-class="{ active: item.Checked }"
|
||||
ng-style="{ background: item.Highlighted ? 'var(--bg-item-highlighted-color)' : '' }"
|
||||
ng-class="{ active: item.Checked, 'datatable-highlighted': item.Highlighted }"
|
||||
ng-click="$ctrl.expandItem(item, !item.Expanded)"
|
||||
pagination-id="$ctrl.tableKey"
|
||||
>
|
||||
@@ -88,7 +87,7 @@
|
||||
dir-paginate-end
|
||||
ng-show="item.Expanded"
|
||||
ng-repeat="path in item.Paths"
|
||||
ng-style="{ background: item.Highlighted ? 'var(--bg-item-highlighted-color)' : 'var(--bg-item-highlighted-null-color)' }"
|
||||
ng-class="{ 'datatable-highlighted': item.Highlighted, 'datatable-unhighlighted': !item.Highlighted }"
|
||||
>
|
||||
<td>
|
||||
<a style="margin-left: 15px;" ng-href="http://{{ path.Host ? path.Host : path.IP }}{{ path.Path }}" target="_blank">
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
<li ng-repeat="summary in $ctrl.state.resources" ng-if="summary.action && summary.kind && summary.name">
|
||||
{{ summary.action }}
|
||||
{{ $ctrl.getArticle(summary.kind, summary.action) }}
|
||||
<span style="color: black; font-weight: 700;">{{ summary.kind }}</span> named <code>{{ summary.name }}</code>
|
||||
<span class="summary">{{ summary.kind }}</span> named <code>{{ summary.name }}</code>
|
||||
<span ng-if="summary.type">
|
||||
of type <code>{{ summary.type }}</code></span
|
||||
>
|
||||
|
||||
@@ -82,8 +82,7 @@
|
||||
<tbody>
|
||||
<tr
|
||||
dir-paginate-start="item in ($ctrl.state.filteredDataSet = ($ctrl.dataset | filter:$ctrl.state.textFilter | orderBy:$ctrl.state.orderBy:$ctrl.state.reverseOrder | itemsPerPage: $ctrl.state.paginatedItemLimit: $ctrl.tableKey))"
|
||||
ng-class="{ active: item.Checked }"
|
||||
ng-style="{ background: item.Highlighted ? 'var(--bg-item-highlighted-color)' : '' }"
|
||||
ng-class="{ active: item.Checked, 'datatable-highlighted': item.Highlighted }"
|
||||
ng-click="$ctrl.expandItem(item, !item.Expanded)"
|
||||
pagination-id="$ctrl.tableKey"
|
||||
>
|
||||
@@ -99,7 +98,7 @@
|
||||
dir-paginate-end
|
||||
ng-show="item.Expanded"
|
||||
ng-repeat="vol in item.Volumes"
|
||||
ng-style="{ background: item.Highlighted ? 'var(--bg-item-highlighted-color)' : 'var(--bg-item-highlighted-null-color)' }"
|
||||
ng-class="{ 'datatable-highlighted': item.Highlighted, 'datatable-unhighlighted': !item.Highlighted }"
|
||||
>
|
||||
<td></td>
|
||||
<td>
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="searchBar" style="border-top: 2px solid var(--border-searchbar-color);">
|
||||
<div class="searchBar">
|
||||
<i class="fa fa-search searchIcon" aria-hidden="true"></i>
|
||||
<input
|
||||
type="text"
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="searchBar" style="border-top: 2px solid var(--border-searchbar-color);">
|
||||
<div class="searchBar">
|
||||
<i class="fa fa-search searchIcon" aria-hidden="true"></i>
|
||||
<input
|
||||
type="text"
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
export default class ThemeSettingsController {
|
||||
/* @ngInject */
|
||||
constructor($async, $state, Authentication, ThemeManager, StateManager, UserService, Notifications) {
|
||||
this.$async = $async;
|
||||
this.$state = $state;
|
||||
this.Authentication = Authentication;
|
||||
this.ThemeManager = ThemeManager;
|
||||
this.StateManager = StateManager;
|
||||
this.UserService = UserService;
|
||||
this.Notifications = Notifications;
|
||||
|
||||
this.state = {
|
||||
userId: null,
|
||||
userTheme: '',
|
||||
};
|
||||
}
|
||||
|
||||
/** Theme Settings Panel */
|
||||
setTheme(theme) {
|
||||
this.ThemeManager.setTheme(theme);
|
||||
}
|
||||
|
||||
async updateTheme() {
|
||||
try {
|
||||
await this.UserService.updateUserTheme(this.state.userId, this.state.userTheme);
|
||||
this.Notifications.success('Success', 'User theme successfully updated');
|
||||
this.$state.reload();
|
||||
} catch (err) {
|
||||
this.Notifications.error('Failure', err, 'Unable to update user theme');
|
||||
}
|
||||
}
|
||||
|
||||
$onInit() {
|
||||
return this.$async(async () => {
|
||||
this.state.userId = await this.Authentication.getUserDetails().ID;
|
||||
const data = await this.UserService.user(this.state.userId);
|
||||
this.state.userTheme = data.UserTheme || 'light';
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
<information-panel class="theme-information" title-text="Information">
|
||||
<span class="small text-muted">
|
||||
<p>
|
||||
<i class="fa fa-flask orange-icon" aria-hidden="true" style="margin-right: 2px;"></i>
|
||||
Dark and High-contrast theme are experimental. Some UI components might not display properly.
|
||||
</p>
|
||||
</span>
|
||||
</information-panel>
|
||||
<rd-widget>
|
||||
<rd-widget-header icon="fa-palette" title-text="Change user theme"></rd-widget-header>
|
||||
<rd-widget-body>
|
||||
<form class="theme-panel">
|
||||
<!-- Theme -->
|
||||
<div class="boxselector_wrapper">
|
||||
<div>
|
||||
<input type="radio" id="light_theme" ng-model="$ctrl.state.userTheme" value="light" ng-click="$ctrl.setTheme('light')" />
|
||||
<label for="light_theme">
|
||||
<div class="boxselector_header">
|
||||
Light Theme
|
||||
</div>
|
||||
<div><i class="fas fa-sun" style="font-size: 50px; padding: 20px;"></i></div>
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<input type="radio" id="dark_theme" ng-model="$ctrl.state.userTheme" value="dark" ng-click="$ctrl.setTheme('dark')" />
|
||||
<label for="dark_theme">
|
||||
<div class="boxselector_header">
|
||||
Dark Theme
|
||||
</div>
|
||||
<div><i class="fas fa-moon" style="font-size: 50px; padding: 20px;"></i></div>
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<input type="radio" id="highcontrast_theme" ng-model="$ctrl.state.userTheme" value="highcontrast" ng-click="$ctrl.setTheme('highcontrast')" />
|
||||
<label for="highcontrast_theme">
|
||||
<div class="boxselector_header">
|
||||
High Contrast Theme
|
||||
</div>
|
||||
<div><i class="fas fa-adjust" style="font-size: 50px; padding: 20px;"></i></div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<button ng-click="$ctrl.updateTheme()" class="btn btn-primary btn-sm">Update theme</button>
|
||||
<!-- !Theme -->
|
||||
</form>
|
||||
</rd-widget-body>
|
||||
</rd-widget>
|
||||
@@ -0,0 +1,7 @@
|
||||
import angular from 'angular';
|
||||
import controller from './theme-settings.controller';
|
||||
|
||||
angular.module('portainer.app').component('themeSettings', {
|
||||
templateUrl: './theme-settings.html',
|
||||
controller,
|
||||
});
|
||||
@@ -2,7 +2,7 @@ export function UserViewModel(data) {
|
||||
this.Id = data.Id;
|
||||
this.Username = data.Username;
|
||||
this.Role = data.Role;
|
||||
this.Usertheme = data.Usertheme;
|
||||
this.UserTheme = data.UserTheme;
|
||||
if (data.Role === 1) {
|
||||
this.RoleName = 'administrator';
|
||||
} else {
|
||||
|
||||
@@ -92,11 +92,7 @@ angular.module('portainer.app').factory('UserService', [
|
||||
};
|
||||
|
||||
service.updateUserTheme = function (id, userTheme) {
|
||||
var payload = {
|
||||
Usertheme: userTheme,
|
||||
};
|
||||
|
||||
return Users.updateTheme({ id: id }, payload).$promise;
|
||||
return Users.updateTheme({ id }, { userTheme }).$promise;
|
||||
};
|
||||
|
||||
service.userMemberships = function (id) {
|
||||
|
||||
@@ -84,20 +84,11 @@ angular.module('portainer.app').factory('Authentication', [
|
||||
return user;
|
||||
}
|
||||
|
||||
async function setDefaultTheme() {
|
||||
async function setUserTheme() {
|
||||
const data = await UserService.user(user.ID);
|
||||
|
||||
ThemeManager.setTheme('light');
|
||||
if (!data.Usertheme || data.Usertheme === 'light') {
|
||||
ThemeManager.setTheme('light');
|
||||
StateManager.updateTheme('light');
|
||||
} else if (data.Usertheme === 'dark') {
|
||||
ThemeManager.setTheme('dark');
|
||||
StateManager.updateTheme('dark');
|
||||
} else if (data.Usertheme === 'highcontrast') {
|
||||
ThemeManager.setTheme('highcontrast');
|
||||
StateManager.updateTheme('highcontrast');
|
||||
}
|
||||
// Initialize user theme base on Usertheme from database
|
||||
const userTheme = data.UserTheme;
|
||||
ThemeManager.setTheme(userTheme);
|
||||
}
|
||||
|
||||
async function setUser(jwt) {
|
||||
@@ -106,7 +97,7 @@ angular.module('portainer.app').factory('Authentication', [
|
||||
user.username = tokenPayload.username;
|
||||
user.ID = tokenPayload.id;
|
||||
user.role = tokenPayload.role;
|
||||
await setDefaultTheme();
|
||||
await setUserTheme();
|
||||
}
|
||||
|
||||
function isAdmin() {
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
angular.module('portainer.app').factory('ThemeManager', [
|
||||
/* @ngInject */
|
||||
function ThemeManagerFactory() {
|
||||
var service = {};
|
||||
|
||||
service.setTheme = function (theme) {
|
||||
document.documentElement.setAttribute('theme', theme);
|
||||
};
|
||||
|
||||
return service;
|
||||
},
|
||||
]);
|
||||
@@ -0,0 +1,23 @@
|
||||
angular.module('portainer.app').service('ThemeManager', ThemeManager);
|
||||
|
||||
/* @ngInject */
|
||||
|
||||
export function ThemeManager(StateManager) {
|
||||
return {
|
||||
setTheme,
|
||||
defaultTheme,
|
||||
};
|
||||
|
||||
function setTheme(theme) {
|
||||
if (!theme) {
|
||||
document.documentElement.removeAttribute('theme');
|
||||
} else {
|
||||
document.documentElement.setAttribute('theme', theme);
|
||||
}
|
||||
StateManager.updateTheme(theme);
|
||||
}
|
||||
|
||||
function defaultTheme() {
|
||||
document.documentElement.removeAttribute('theme');
|
||||
}
|
||||
}
|
||||
@@ -78,53 +78,6 @@
|
||||
</form>
|
||||
</rd-widget-body>
|
||||
</rd-widget>
|
||||
<br />
|
||||
<information-panel class="theme-information" title-text="Information">
|
||||
<span class="small text-muted">
|
||||
<p>
|
||||
<i class="fa fa-flask orange-icon" aria-hidden="true" style="margin-right: 2px;"></i>
|
||||
Dark and High-contrast theme are experimental. Some UI components might not display properly.
|
||||
</p>
|
||||
</span>
|
||||
</information-panel>
|
||||
<rd-widget>
|
||||
<rd-widget-header icon="fa-palette" title-text="Change user theme"></rd-widget-header>
|
||||
<rd-widget-body>
|
||||
<form class="form-horizontal" style="margin-top: 15px;">
|
||||
<!-- Theme -->
|
||||
<div class="boxselector_wrapper">
|
||||
<div>
|
||||
<input type="radio" id="backup_file" ng-model="formValues.userTheme" value="light" ng-click="setTheme('light')" />
|
||||
<label for="backup_file">
|
||||
<div class="boxselector_header">
|
||||
Light Theme
|
||||
</div>
|
||||
<div><i class="fas fa-sun" style="font-size: 50px; padding: 20px;"></i></div>
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<input type="radio" id="backup_s3" ng-model="formValues.userTheme" value="dark" ng-click="setTheme('dark')" />
|
||||
<label for="backup_s3">
|
||||
<div class="boxselector_header">
|
||||
Dark Theme
|
||||
</div>
|
||||
<div><i class="fas fa-moon" style="font-size: 50px; padding: 20px;"></i></div>
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<input type="radio" id="backup_s4" ng-model="formValues.userTheme" value="highcontrast" ng-click="setTheme('highcontrast')" />
|
||||
<label for="backup_s4">
|
||||
<div class="boxselector_header">
|
||||
High Contrast Theme
|
||||
</div>
|
||||
<div><i class="fas fa-adjust" style="font-size: 50px; padding: 20px;"></i></div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<button ng-click="updateTheme()" class="btn btn-primary btn-sm">Update theme</button>
|
||||
<!-- !Theme -->
|
||||
</form>
|
||||
</rd-widget-body>
|
||||
</rd-widget>
|
||||
<theme-settings></theme-settings>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -29,7 +29,7 @@ class LogoutController {
|
||||
const performApiLogout = this.$transition$.params().performApiLogout;
|
||||
const settings = await this.SettingsService.publicSettings();
|
||||
|
||||
this.ThemeManager.setTheme('light');
|
||||
this.ThemeManager.defaultTheme();
|
||||
|
||||
try {
|
||||
await this.Authentication.logout(performApiLogout);
|
||||
|
||||
@@ -4,7 +4,7 @@ angular.module('portainer.app').controller('MainController', [
|
||||
'StateManager',
|
||||
'EndpointProvider',
|
||||
'ThemeManager',
|
||||
function ($scope, LocalStorage, StateManager, EndpointProvider, ThemeManager) {
|
||||
function ($scope, LocalStorage, StateManager, EndpointProvider) {
|
||||
/**
|
||||
* Sidebar Toggle & Cookie Control
|
||||
*/
|
||||
@@ -13,11 +13,6 @@ angular.module('portainer.app').controller('MainController', [
|
||||
return window.innerWidth;
|
||||
};
|
||||
|
||||
// Portainer Default Theme
|
||||
$scope.theme = 'light';
|
||||
|
||||
ThemeManager.setTheme($scope.theme);
|
||||
|
||||
$scope.applicationState = StateManager.getState();
|
||||
$scope.endpointState = EndpointProvider.endpoint();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user