BigW Consortium Gitlab

Commit f2421b2b by Sean McGivern

Merge branch '35012-navigation-add-option-to-change-navigation-color-palette' into 'master'

Add option to change navigation color palette Closes #35012 See merge request !13619
parents 780dd6c6 26264625
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
@import "framework/flash"; @import "framework/flash";
@import "framework/forms"; @import "framework/forms";
@import "framework/gfm"; @import "framework/gfm";
@import "framework/gitlab-theme";
@import "framework/header"; @import "framework/header";
@import "framework/highlight"; @import "framework/highlight";
@import "framework/issue_box"; @import "framework/issue_box";
......
/**
* Styles the GitLab application with a specific color theme
*/
@mixin gitlab-theme($color-100, $color-200, $color-500, $color-700, $color-800, $color-900, $color-alternate) {
// Header
header.navbar-gitlab-new {
background: linear-gradient(to right, $color-900, $color-800);
.navbar-collapse {
color: $color-200;
}
.container-fluid {
.navbar-toggle {
border-left: 1px solid lighten($color-700, 10%);
}
}
.navbar-sub-nav,
.navbar-nav {
> li {
> a:hover,
> a:focus {
background-color: rgba($color-200, .2);
}
&.active > a,
&.dropdown.open > a {
color: $color-900;
background-color: $color-alternate;
svg {
fill: currentColor;
}
}
&.line-separator {
border-left: 1px solid rgba($color-200, .2);
}
}
}
.navbar-sub-nav {
color: $color-200;
}
.nav {
> li {
color: $color-200;
> a {
svg {
fill: $color-200;
}
&.header-user-dropdown-toggle {
.header-user-avatar {
border-color: $color-200;
}
}
&:hover,
&:focus {
@media (min-width: $screen-sm-min) {
background-color: rgba($color-200, .2);
}
svg {
fill: currentColor;
}
}
}
&.active > a,
&.dropdown.open > a {
color: $color-900;
background-color: $color-alternate;
&:hover {
svg {
fill: $color-900;
}
}
}
.impersonated-user,
.impersonated-user:hover {
svg {
fill: $color-900;
}
}
}
}
}
.title {
> a {
&:hover,
&:focus {
background-color: rgba($color-200, .2);
}
}
}
.search {
form {
background-color: rgba($color-200, .2);
&:hover {
background-color: rgba($color-200, .3);
}
}
.location-badge {
color: $color-100;
background-color: rgba($color-200, .1);
border-right: 1px solid $color-800;
}
.search-input::placeholder {
color: rgba($color-200, .8);
}
.search-input-wrap {
.search-icon,
.clear-icon {
color: rgba($color-200, .8);
}
}
&.search-active {
form {
background-color: $white-light;
}
.location-badge {
color: $gl-text-color;
}
.search-input-wrap {
.search-icon {
color: rgba($color-200, .8);
}
}
}
}
.btn-sign-in {
background-color: $color-100;
color: $color-900;
}
// Sidebar
.nav-sidebar li.active {
box-shadow: inset 4px 0 0 $color-700;
> a {
color: $color-900;
}
svg {
fill: $color-900;
}
}
}
body {
&.ui_indigo {
@include gitlab-theme($indigo-100, $indigo-200, $indigo-500, $indigo-700, $indigo-800, $indigo-900, $white-light);
}
&.ui_dark {
@include gitlab-theme($theme-gray-100, $theme-gray-200, $theme-gray-500, $theme-gray-700, $theme-gray-800, $theme-gray-900, $white-light);
}
&.ui_blue {
@include gitlab-theme($theme-blue-100, $theme-blue-200, $theme-blue-500, $theme-blue-700, $theme-blue-800, $theme-blue-900, $white-light);
}
&.ui_green {
@include gitlab-theme($theme-green-100, $theme-green-200, $theme-green-500, $theme-green-700, $theme-green-800, $theme-green-900, $white-light);
}
&.ui_light {
@include gitlab-theme($theme-gray-900, $theme-gray-700, $theme-gray-800, $theme-gray-700, $theme-gray-700, $theme-gray-100, $theme-gray-700);
header.navbar-gitlab-new {
background: $theme-gray-100;
box-shadow: 0 2px 0 0 $border-color;
.logo-text svg {
fill: $theme-gray-900;
}
.navbar-sub-nav,
.navbar-nav {
> li {
> a:hover,
> a:focus {
color: $theme-gray-900;
}
&.active > a {
color: $white-light;
&:hover {
color: $white-light;
}
}
}
}
.container-fluid {
.navbar-toggle,
.navbar-toggle:hover {
color: $theme-gray-700;
border-left: 1px solid $theme-gray-200;
}
}
}
.search {
form {
background-color: $white-light;
box-shadow: inset 0 0 0 1px $border-color;
&:hover {
background-color: $white-light;
box-shadow: inset 0 0 0 1px $blue-100;
.location-badge {
box-shadow: inset 0 0 0 1px $blue-100;
}
}
}
.search-input-wrap {
.search-icon {
color: $theme-gray-200;
}
}
.location-badge {
color: $theme-gray-700;
box-shadow: inset 0 0 0 1px $border-color;
background-color: $nav-badge-bg;
border-right: 0;
}
}
.nav-sidebar li.active {
> a {
color: $theme-gray-900;
}
svg {
fill: $theme-gray-900;
}
}
}
}
...@@ -111,7 +111,6 @@ header { ...@@ -111,7 +111,6 @@ header {
svg { svg {
height: 16px; height: 16px;
width: 23px; width: 23px;
fill: currentColor;
} }
} }
......
...@@ -74,6 +74,8 @@ $red-700: #a62d19; ...@@ -74,6 +74,8 @@ $red-700: #a62d19;
$red-800: #8b2615; $red-800: #8b2615;
$red-900: #711e11; $red-900: #711e11;
// GitLab themes
$indigo-50: #f7f7ff; $indigo-50: #f7f7ff;
$indigo-100: #ebebfa; $indigo-100: #ebebfa;
$indigo-200: #d1d1f0; $indigo-200: #d1d1f0;
...@@ -86,6 +88,43 @@ $indigo-800: #393982; ...@@ -86,6 +88,43 @@ $indigo-800: #393982;
$indigo-900: #292961; $indigo-900: #292961;
$indigo-950: #1a1a40; $indigo-950: #1a1a40;
$theme-gray-50: #fafafa;
$theme-gray-100: #f2f2f2;
$theme-gray-200: #dfdfdf;
$theme-gray-300: #cccccc;
$theme-gray-400: #bababa;
$theme-gray-500: #a7a7a7;
$theme-gray-600: #949494;
$theme-gray-700: #707070;
$theme-gray-800: #4f4f4f;
$theme-gray-900: #2e2e2e;
$theme-gray-950: #1f1f1f;
$theme-blue-50: #f4f8fc;
$theme-blue-100: #e6edf5;
$theme-blue-200: #c8d7e6;
$theme-blue-300: #97b3cf;
$theme-blue-400: #648cb4;
$theme-blue-500: #4a79a8;
$theme-blue-600: #3e6fa0;
$theme-blue-700: #305c88;
$theme-blue-800: #25496e;
$theme-blue-900: #1a3652;
$theme-blue-950: #0f2235;
$theme-green-50: #f2faf6;
$theme-green-100: #e4f3ea;
$theme-green-200: #c0dfcd;
$theme-green-300: #8ac2a1;
$theme-green-400: #52a274;
$theme-green-500: #35935c;
$theme-green-600: #288a50;
$theme-green-700: #1c7441;
$theme-green-800: #145d33;
$theme-green-900: #0d4524;
$theme-green-950: #072d16;
$black: #000; $black: #000;
$black-transparent: rgba(0, 0, 0, 0.3); $black-transparent: rgba(0, 0, 0, 0.3);
$almost-black: #242424; $almost-black: #242424;
......
...@@ -9,10 +9,20 @@ ...@@ -9,10 +9,20 @@
header.navbar-gitlab-new { header.navbar-gitlab-new {
color: $white-light; color: $white-light;
background: linear-gradient(to right, $indigo-900, $indigo-800);
border-bottom: 0; border-bottom: 0;
min-height: $new-navbar-height; min-height: $new-navbar-height;
.logo-text {
line-height: initial;
svg {
width: 55px;
height: 14px;
margin: 0;
fill: $white-light;
}
}
.header-content { .header-content {
display: -webkit-flex; display: -webkit-flex;
display: flex; display: flex;
...@@ -38,10 +48,10 @@ header.navbar-gitlab-new { ...@@ -38,10 +48,10 @@ header.navbar-gitlab-new {
img { img {
height: 28px; height: 28px;
margin-right: 10px; margin-right: 8px;
} }
> a { a {
display: -webkit-flex; display: -webkit-flex;
display: flex; display: flex;
align-items: center; align-items: center;
...@@ -54,22 +64,6 @@ header.navbar-gitlab-new { ...@@ -54,22 +64,6 @@ header.navbar-gitlab-new {
margin-right: 8px; margin-right: 8px;
} }
} }
.logo-text {
line-height: initial;
svg {
width: 55px;
height: 14px;
margin: 0;
fill: $white-light;
}
}
&:hover,
&:focus {
background-color: rgba($indigo-200, .2);
}
} }
} }
...@@ -106,7 +100,6 @@ header.navbar-gitlab-new { ...@@ -106,7 +100,6 @@ header.navbar-gitlab-new {
.navbar-collapse { .navbar-collapse {
padding-left: 0; padding-left: 0;
color: $indigo-200;
box-shadow: 0; box-shadow: 0;
@media (max-width: $screen-xs-max) { @media (max-width: $screen-xs-max) {
...@@ -132,7 +125,6 @@ header.navbar-gitlab-new { ...@@ -132,7 +125,6 @@ header.navbar-gitlab-new {
font-size: 14px; font-size: 14px;
text-align: center; text-align: center;
color: currentColor; color: currentColor;
border-left: 1px solid lighten($indigo-700, 10%);
&:hover, &:hover,
&:focus, &:focus,
...@@ -167,42 +159,27 @@ header.navbar-gitlab-new { ...@@ -167,42 +159,27 @@ header.navbar-gitlab-new {
will-change: color; will-change: color;
margin: 4px 2px; margin: 4px 2px;
padding: 6px 8px; padding: 6px 8px;
color: $indigo-200;
height: 32px; height: 32px;
@media (max-width: $screen-xs-max) { @media (max-width: $screen-xs-max) {
padding: 0; padding: 0;
} }
svg {
fill: $indigo-200;
}
&.header-user-dropdown-toggle { &.header-user-dropdown-toggle {
margin-left: 2px; margin-left: 2px;
.header-user-avatar { .header-user-avatar {
border-color: $indigo-200;
margin-right: 0; margin-right: 0;
} }
} }
}
.header-new-dropdown-toggle {
margin-right: 0;
}
> a:hover, &:hover,
> a:focus { &:focus {
text-decoration: none; text-decoration: none;
outline: 0; outline: 0;
opacity: 1; opacity: 1;
color: $white-light; color: $white-light;
@media (min-width: $screen-sm-min) {
background-color: rgba($indigo-200, .2);
}
svg { svg {
fill: currentColor; fill: currentColor;
} }
...@@ -213,6 +190,11 @@ header.navbar-gitlab-new { ...@@ -213,6 +190,11 @@ header.navbar-gitlab-new {
} }
} }
} }
}
.header-new-dropdown-toggle {
margin-right: 0;
}
.impersonated-user, .impersonated-user,
.impersonated-user:hover { .impersonated-user:hover {
...@@ -220,10 +202,6 @@ header.navbar-gitlab-new { ...@@ -220,10 +202,6 @@ header.navbar-gitlab-new {
background-color: $white-light; background-color: $white-light;
border-top-right-radius: 0; border-top-right-radius: 0;
border-bottom-right-radius: 0; border-bottom-right-radius: 0;
svg {
fill: $indigo-900;
}
} }
.impersonation-btn, .impersonation-btn,
...@@ -241,8 +219,6 @@ header.navbar-gitlab-new { ...@@ -241,8 +219,6 @@ header.navbar-gitlab-new {
&.active > a, &.active > a,
&.dropdown.open > a { &.dropdown.open > a {
color: $indigo-900;
background-color: $white-light;
svg { svg {
fill: currentColor; fill: currentColor;
...@@ -256,7 +232,6 @@ header.navbar-gitlab-new { ...@@ -256,7 +232,6 @@ header.navbar-gitlab-new {
display: -webkit-flex; display: -webkit-flex;
display: flex; display: flex;
margin: 0 0 0 6px; margin: 0 0 0 6px;
color: $indigo-200;
.dropdown-chevron { .dropdown-chevron {
position: relative; position: relative;
...@@ -274,17 +249,6 @@ header.navbar-gitlab-new { ...@@ -274,17 +249,6 @@ header.navbar-gitlab-new {
text-decoration: none; text-decoration: none;
outline: 0; outline: 0;
color: $white-light; color: $white-light;
background-color: rgba($indigo-200, .2);
svg {
fill: currentColor;
}
}
&.active > a,
&.dropdown.open > a {
color: $indigo-900;
background-color: $white-light;
svg { svg {
fill: currentColor; fill: currentColor;
...@@ -309,7 +273,6 @@ header.navbar-gitlab-new { ...@@ -309,7 +273,6 @@ header.navbar-gitlab-new {
} }
&.line-separator { &.line-separator {
border-left: 1px solid rgba($indigo-200, .2);
margin: 8px; margin: 8px;
} }
} }
...@@ -339,17 +302,14 @@ header.navbar-gitlab-new { ...@@ -339,17 +302,14 @@ header.navbar-gitlab-new {
height: 32px; height: 32px;
border: 0; border: 0;
border-radius: $border-radius-default; border-radius: $border-radius-default;
background-color: rgba($indigo-200, .2);
transition: border-color ease-in-out 0.15s, background-color ease-in-out 0.15s; transition: border-color ease-in-out 0.15s, background-color ease-in-out 0.15s;
&:hover { &:hover {
background-color: rgba($indigo-200, .3);
box-shadow: none; box-shadow: none;
} }
} }
&.search-active form { &.search-active form {
background-color: $white-light;
box-shadow: none; box-shadow: none;
.search-input { .search-input {
...@@ -377,43 +337,26 @@ header.navbar-gitlab-new { ...@@ -377,43 +337,26 @@ header.navbar-gitlab-new {
} }
.search-input::placeholder { .search-input::placeholder {
color: rgba($indigo-200, .8);
transition: color ease-in-out 0.15s; transition: color ease-in-out 0.15s;
} }
.location-badge { .location-badge {
font-size: 12px; font-size: 12px;
color: $indigo-100;
background-color: rgba($indigo-200, .1);
will-change: color;
margin: -4px 4px -4px -4px; margin: -4px 4px -4px -4px;
line-height: 25px; line-height: 25px;
padding: 4px 8px; padding: 4px 8px;
border-radius: 2px 0 0 2px; border-radius: 2px 0 0 2px;
border-right: 1px solid $indigo-800;
height: 32px; height: 32px;
transition: border-color ease-in-out 0.15s; transition: border-color ease-in-out 0.15s;
} }
.search-input-wrap {
.search-icon,
.clear-icon {
color: rgba($indigo-200, .8);
}
}
&.search-active { &.search-active {
.location-badge { .location-badge {
color: $gl-text-color;
background-color: $nav-badge-bg; background-color: $nav-badge-bg;
border-color: $border-color; border-color: $border-color;
} }
.search-input-wrap { .search-input-wrap {
.search-icon {
color: rgba($indigo-200, .8);
}
.clear-icon { .clear-icon {
color: $white-light; color: $white-light;
} }
...@@ -517,8 +460,6 @@ header.navbar-gitlab-new { ...@@ -517,8 +460,6 @@ header.navbar-gitlab-new {
.btn-sign-in { .btn-sign-in {
margin-top: 3px; margin-top: 3px;
background-color: $indigo-100;
color: $indigo-900;
font-weight: $gl-font-weight-bold; font-weight: $gl-font-weight-bold;
&:hover { &:hover {
......
...@@ -155,16 +155,9 @@ $new-sidebar-collapsed-width: 50px; ...@@ -155,16 +155,9 @@ $new-sidebar-collapsed-width: 50px;
} }
li.active { li.active {
box-shadow: inset 4px 0 0 $active-border;
> a { > a {
color: $active-color;
font-weight: $gl-font-weight-bold; font-weight: $gl-font-weight-bold;
} }
svg {
fill: $active-color;
}
} }
@media (max-width: $screen-xs-max) { @media (max-width: $screen-xs-max) {
......
@mixin application-theme-preview($color-1, $color-2, $color-3, $color-4) {
.one {
background-color: $color-1;
border-top-left-radius: $border-radius-default;
}
.two {
background-color: $color-2;
border-top-right-radius: $border-radius-default;
}
.three {
background-color: $color-3;
border-bottom-left-radius: $border-radius-default;
}
.four {
background-color: $color-4;
border-bottom-right-radius: $border-radius-default;
}
}
.application-theme {
label {
margin-right: 20px;
text-align: center;
}
.preview {
font-size: 0;
margin-bottom: 10px;
&.indigo {
@include application-theme-preview($indigo-900, $indigo-700, $indigo-800, $indigo-500);
}
&.dark {
@include application-theme-preview($theme-gray-900, $theme-gray-700, $theme-gray-800, $theme-gray-600);
}
&.light {
@include application-theme-preview($theme-gray-600, $theme-gray-200, $theme-gray-400, $theme-gray-100);
}
&.blue {
@include application-theme-preview($theme-blue-900, $theme-blue-700, $theme-blue-800, $theme-blue-500);
}
&.green {
@include application-theme-preview($theme-green-900, $theme-green-700, $theme-green-800, $theme-green-500);
}
}
.preview-row {
display: block;
}
.quadrant {
display: inline-block;
height: 50px;
width: 80px;
}
}
.syntax-theme { .syntax-theme {
label { label {
margin-right: 20px; margin-right: 20px;
......
...@@ -211,6 +211,7 @@ class Admin::UsersController < Admin::ApplicationController ...@@ -211,6 +211,7 @@ class Admin::UsersController < Admin::ApplicationController
:provider, :provider,
:remember_me, :remember_me,
:skype, :skype,
:theme_id,
:twitter, :twitter,
:username, :username,
:website_url :website_url
......
...@@ -35,7 +35,8 @@ class Profiles::PreferencesController < Profiles::ApplicationController ...@@ -35,7 +35,8 @@ class Profiles::PreferencesController < Profiles::ApplicationController
:color_scheme_id, :color_scheme_id,
:layout, :layout,
:dashboard, :dashboard,
:project_view :project_view,
:theme_id
) )
end end
end end
...@@ -40,6 +40,10 @@ module PreferencesHelper ...@@ -40,6 +40,10 @@ module PreferencesHelper
] ]
end end
def user_application_theme
@user_application_theme ||= Gitlab::Themes.for_user(current_user).css_class
end
def user_color_scheme def user_color_scheme
Gitlab::ColorSchemes.for_user(current_user).css_class Gitlab::ColorSchemes.for_user(current_user).css_class
end end
......
...@@ -35,6 +35,7 @@ class User < ActiveRecord::Base ...@@ -35,6 +35,7 @@ class User < ActiveRecord::Base
default_value_for :project_view, :files default_value_for :project_view, :files
default_value_for :notified_of_own_activity, false default_value_for :notified_of_own_activity, false
default_value_for :preferred_language, I18n.default_locale default_value_for :preferred_language, I18n.default_locale
default_value_for :theme_id, gitlab_config.default_theme
attr_encrypted :otp_secret, attr_encrypted :otp_secret,
key: Gitlab::Application.secrets.otp_key_base, key: Gitlab::Application.secrets.otp_key_base,
......
!!! 5 !!! 5
%html{ lang: I18n.locale, class: page_class } %html{ lang: I18n.locale, class: page_class }
= render "layouts/head" = render "layouts/head"
%body{ class: @body_class, data: { page: body_data_page, project: "#{@project.path if @project}", group: "#{@group.path if @group}", find_file: find_file_path } } %body{ class: "#{user_application_theme} #{@body_class}", data: { page: body_data_page, project: "#{@project.path if @project}", group: "#{@group.path if @group}", find_file: find_file_path } }
= render "layouts/init_auto_complete" if @gfm_form = render "layouts/init_auto_complete" if @gfm_form
= render 'peek/bar' = render 'peek/bar'
= render "layouts/header/default" = render "layouts/header/default"
......
...@@ -3,6 +3,26 @@ ...@@ -3,6 +3,26 @@
= render 'profiles/head' = render 'profiles/head'
= form_for @user, url: profile_preferences_path, remote: true, method: :put, html: { class: 'row prepend-top-default js-preferences-form' } do |f| = form_for @user, url: profile_preferences_path, remote: true, method: :put, html: { class: 'row prepend-top-default js-preferences-form' } do |f|
.col-lg-4.application-theme
%h4.prepend-top-0
GitLab navigation theme
%p Customize the appearance of the application header and navigation sidebar.
.col-lg-8.application-theme
- Gitlab::Themes.each do |theme|
= label_tag do
.preview{ class: theme.name.downcase }
.preview-row
.quadrant.one
.quadrant.two
.preview-row
.quadrant.three
.quadrant.four
= f.radio_button :theme_id, theme.id
= theme.name
.col-sm-12
%hr
.col-lg-4.profile-settings-sidebar .col-lg-4.profile-settings-sidebar
%h4.prepend-top-0 %h4.prepend-top-0
Syntax highlighting theme Syntax highlighting theme
......
// Remove body class for any previous theme, re-add current one
$('body').removeClass('<%= Gitlab::Themes.body_classes %>')
$('body').addClass('<%= user_application_theme %>')
// Toggle container-fluid class // Toggle container-fluid class
if ('<%= current_user.layout %>' === 'fluid') { if ('<%= current_user.layout %>' === 'fluid') {
$('.content-wrapper .container-fluid').removeClass('container-limited') $('.content-wrapper .container-fluid').removeClass('container-limited')
......
---
title: Add option in preferences to change navigation theme color
merge_request:
author:
type: added
...@@ -76,6 +76,13 @@ production: &base ...@@ -76,6 +76,13 @@ production: &base
# default_can_create_group: false # default: true # default_can_create_group: false # default: true
# username_changing_enabled: false # default: true - User can change her username/namespace # username_changing_enabled: false # default: true - User can change her username/namespace
## Default theme ID
## 1 - Indigo
## 2 - Dark
## 3 - Light
## 4 - Blue
## 5 - Green
# default_theme: 1 # default: 1
## Automatic issue closing ## Automatic issue closing
# If a commit message matches this regular expression, all issues referenced from the matched text will be closed. # If a commit message matches this regular expression, all issues referenced from the matched text will be closed.
......
...@@ -232,6 +232,7 @@ Settings['gitlab'] ||= Settingslogic.new({}) ...@@ -232,6 +232,7 @@ Settings['gitlab'] ||= Settingslogic.new({})
Settings.gitlab['default_projects_limit'] ||= 100000 Settings.gitlab['default_projects_limit'] ||= 100000
Settings.gitlab['default_branch_protection'] ||= 2 Settings.gitlab['default_branch_protection'] ||= 2
Settings.gitlab['default_can_create_group'] = true if Settings.gitlab['default_can_create_group'].nil? Settings.gitlab['default_can_create_group'] = true if Settings.gitlab['default_can_create_group'].nil?
Settings.gitlab['default_theme'] = Gitlab::Themes::APPLICATION_DEFAULT if Settings.gitlab['default_theme'].nil?
Settings.gitlab['host'] ||= ENV['GITLAB_HOST'] || 'localhost' Settings.gitlab['host'] ||= ENV['GITLAB_HOST'] || 'localhost'
Settings.gitlab['ssh_host'] ||= Settings.gitlab.host Settings.gitlab['ssh_host'] ||= Settings.gitlab.host
Settings.gitlab['https'] = false if Settings.gitlab['https'].nil? Settings.gitlab['https'] = false if Settings.gitlab['https'].nil?
......
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class AddThemeIdToUsers < ActiveRecord::Migration
DOWNTIME = false
def change
add_column :users, :theme_id, :integer, limit: 2
end
end
...@@ -1596,6 +1596,7 @@ ActiveRecord::Schema.define(version: 20170905112933) do ...@@ -1596,6 +1596,7 @@ ActiveRecord::Schema.define(version: 20170905112933) do
t.boolean "notified_of_own_activity" t.boolean "notified_of_own_activity"
t.string "preferred_language" t.string "preferred_language"
t.string "rss_token" t.string "rss_token"
t.integer "theme_id", limit: 2
end end
add_index "users", ["admin"], name: "index_users_on_admin", using: :btree add_index "users", ["admin"], name: "index_users_on_admin", using: :btree
......
...@@ -32,6 +32,7 @@ Parameters: ...@@ -32,6 +32,7 @@ Parameters:
"twitter": "", "twitter": "",
"website_url": "", "website_url": "",
"email": "john@example.com", "email": "john@example.com",
"theme_id": 2,
"color_scheme_id": 1, "color_scheme_id": 1,
"projects_limit": 10, "projects_limit": 10,
"current_sign_in_at": null, "current_sign_in_at": null,
......
...@@ -39,6 +39,7 @@ Example response: ...@@ -39,6 +39,7 @@ Example response:
"twitter": "", "twitter": "",
"website_url": "", "website_url": "",
"email": "john@example.com", "email": "john@example.com",
"theme_id": 1,
"color_scheme_id": 1, "color_scheme_id": 1,
"projects_limit": 10, "projects_limit": 10,
"current_sign_in_at": "2015-07-07T07:10:58.392Z", "current_sign_in_at": "2015-07-07T07:10:58.392Z",
......
...@@ -72,6 +72,7 @@ GET /users ...@@ -72,6 +72,7 @@ GET /users
"organization": "", "organization": "",
"last_sign_in_at": "2012-06-01T11:41:01Z", "last_sign_in_at": "2012-06-01T11:41:01Z",
"confirmed_at": "2012-05-23T09:05:22Z", "confirmed_at": "2012-05-23T09:05:22Z",
"theme_id": 1,
"last_activity_on": "2012-05-23", "last_activity_on": "2012-05-23",
"color_scheme_id": 2, "color_scheme_id": 2,
"projects_limit": 100, "projects_limit": 100,
...@@ -105,6 +106,7 @@ GET /users ...@@ -105,6 +106,7 @@ GET /users
"organization": "", "organization": "",
"last_sign_in_at": null, "last_sign_in_at": null,
"confirmed_at": "2012-05-30T16:53:06.148Z", "confirmed_at": "2012-05-30T16:53:06.148Z",
"theme_id": 1,
"last_activity_on": "2012-05-23", "last_activity_on": "2012-05-23",
"color_scheme_id": 3, "color_scheme_id": 3,
"projects_limit": 100, "projects_limit": 100,
...@@ -215,6 +217,7 @@ Parameters: ...@@ -215,6 +217,7 @@ Parameters:
"organization": "", "organization": "",
"last_sign_in_at": "2012-06-01T11:41:01Z", "last_sign_in_at": "2012-06-01T11:41:01Z",
"confirmed_at": "2012-05-23T09:05:22Z", "confirmed_at": "2012-05-23T09:05:22Z",
"theme_id": 1,
"last_activity_on": "2012-05-23", "last_activity_on": "2012-05-23",
"color_scheme_id": 2, "color_scheme_id": 2,
"projects_limit": 100, "projects_limit": 100,
...@@ -341,6 +344,7 @@ GET /user ...@@ -341,6 +344,7 @@ GET /user
"organization": "", "organization": "",
"last_sign_in_at": "2012-06-01T11:41:01Z", "last_sign_in_at": "2012-06-01T11:41:01Z",
"confirmed_at": "2012-05-23T09:05:22Z", "confirmed_at": "2012-05-23T09:05:22Z",
"theme_id": 1,
"last_activity_on": "2012-05-23", "last_activity_on": "2012-05-23",
"color_scheme_id": 2, "color_scheme_id": 2,
"projects_limit": 100, "projects_limit": 100,
...@@ -387,6 +391,7 @@ GET /user ...@@ -387,6 +391,7 @@ GET /user
"organization": "", "organization": "",
"last_sign_in_at": "2012-06-01T11:41:01Z", "last_sign_in_at": "2012-06-01T11:41:01Z",
"confirmed_at": "2012-05-23T09:05:22Z", "confirmed_at": "2012-05-23T09:05:22Z",
"theme_id": 1,
"last_activity_on": "2012-05-23", "last_activity_on": "2012-05-23",
"color_scheme_id": 2, "color_scheme_id": 2,
"projects_limit": 100, "projects_limit": 100,
......
...@@ -45,7 +45,7 @@ module API ...@@ -45,7 +45,7 @@ module API
expose :confirmed_at expose :confirmed_at
expose :last_activity_on expose :last_activity_on
expose :email expose :email
expose :color_scheme_id, :projects_limit, :current_sign_in_at expose :theme_id, :color_scheme_id, :projects_limit, :current_sign_in_at
expose :identities, using: Entities::Identity expose :identities, using: Entities::Identity
expose :can_create_group?, as: :can_create_group expose :can_create_group?, as: :can_create_group
expose :can_create_project?, as: :can_create_project expose :can_create_project?, as: :can_create_project
......
module Gitlab
# Module containing GitLab's application theme definitions and helper methods
# for accessing them.
module Themes
extend self
# Theme ID used when no `default_theme` configuration setting is provided.
APPLICATION_DEFAULT = 1
# Struct class representing a single Theme
Theme = Struct.new(:id, :name, :css_class)
# All available Themes
THEMES = [
Theme.new(1, 'Indigo', 'ui_indigo'),
Theme.new(2, 'Dark', 'ui_dark'),
Theme.new(3, 'Light', 'ui_light'),
Theme.new(4, 'Blue', 'ui_blue'),
Theme.new(5, 'Green', 'ui_green')
].freeze
# Convenience method to get a space-separated String of all the theme
# classes that might be applied to the `body` element
#
# Returns a String
def body_classes
THEMES.collect(&:css_class).uniq.join(' ')
end
# Get a Theme by its ID
#
# If the ID is invalid, returns the default Theme.
#
# id - Integer ID
#
# Returns a Theme
def by_id(id)
THEMES.detect { |t| t.id == id } || default
end
# Returns the number of defined Themes
def count
THEMES.size
end
# Get the default Theme
#
# Returns a Theme
def default
by_id(default_id)
end
# Iterate through each Theme
#
# Yields the Theme object
def each(&block)
THEMES.each(&block)
end
# Get the Theme for the specified user, or the default
#
# user - User record
#
# Returns a Theme
def for_user(user)
if user
by_id(user.theme_id)
else
default
end
end
private
def default_id
@default_id ||= begin
id = Gitlab.config.gitlab.default_theme.to_i
theme_ids = THEMES.map(&:id)
theme_ids.include?(id) ? id : APPLICATION_DEFAULT
end
end
end
end
...@@ -25,7 +25,8 @@ describe Profiles::PreferencesController do ...@@ -25,7 +25,8 @@ describe Profiles::PreferencesController do
def go(params: {}, format: :js) def go(params: {}, format: :js)
params.reverse_merge!( params.reverse_merge!(
color_scheme_id: '1', color_scheme_id: '1',
dashboard: 'stars' dashboard: 'stars',
theme_id: '1'
) )
patch :update, user: params, format: format patch :update, user: params, format: format
...@@ -40,7 +41,8 @@ describe Profiles::PreferencesController do ...@@ -40,7 +41,8 @@ describe Profiles::PreferencesController do
it "changes the user's preferences" do it "changes the user's preferences" do
prefs = { prefs = {
color_scheme_id: '1', color_scheme_id: '1',
dashboard: 'stars' dashboard: 'stars',
theme_id: '2'
}.with_indifferent_access }.with_indifferent_access
expect(user).to receive(:assign_attributes).with(prefs) expect(user).to receive(:assign_attributes).with(prefs)
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
"organization", "organization",
"last_sign_in_at", "last_sign_in_at",
"confirmed_at", "confirmed_at",
"theme_id",
"color_scheme_id", "color_scheme_id",
"projects_limit", "projects_limit",
"current_sign_in_at", "current_sign_in_at",
......
require 'spec_helper' require 'spec_helper'
describe PreferencesHelper do describe PreferencesHelper do
describe 'dashboard_choices' do describe '#dashboard_choices' do
it 'raises an exception when defined choices may be missing' do it 'raises an exception when defined choices may be missing' do
expect(User).to receive(:dashboards).and_return(foo: 'foo') expect(User).to receive(:dashboards).and_return(foo: 'foo')
expect { helper.dashboard_choices }.to raise_error(RuntimeError) expect { helper.dashboard_choices }.to raise_error(RuntimeError)
...@@ -26,7 +26,33 @@ describe PreferencesHelper do ...@@ -26,7 +26,33 @@ describe PreferencesHelper do
end end
end end
describe 'user_color_scheme' do describe '#user_application_theme' do
context 'with a user' do
it "returns user's theme's css_class" do
stub_user(theme_id: 3)
expect(helper.user_application_theme).to eq 'ui_light'
end
it 'returns the default when id is invalid' do
stub_user(theme_id: Gitlab::Themes.count + 5)
allow(Gitlab.config.gitlab).to receive(:default_theme).and_return(1)
expect(helper.user_application_theme).to eq 'ui_indigo'
end
end
context 'without a user' do
it 'returns the default theme' do
stub_user
expect(helper.user_application_theme).to eq Gitlab::Themes.default.css_class
end
end
end
describe '#user_color_scheme' do
context 'with a user' do context 'with a user' do
it "returns user's scheme's css_class" do it "returns user's scheme's css_class" do
allow(helper).to receive(:current_user) allow(helper).to receive(:current_user)
......
...@@ -215,9 +215,17 @@ end ...@@ -215,9 +215,17 @@ end
# to a specific version of the database where said table is still present. # to a specific version of the database where said table is still present.
# #
describe Gitlab::BackgroundMigration::MigrateEventsToPushEventPayloads, :migration, schema: 20170825154015 do describe Gitlab::BackgroundMigration::MigrateEventsToPushEventPayloads, :migration, schema: 20170825154015 do
let(:user_class) do
Class.new(ActiveRecord::Base) do
self.table_name = 'users'
end
end
let(:migration) { described_class.new } let(:migration) { described_class.new }
let(:project) { create(:project_empty_repo) } let(:user_class) { table(:users) }
let(:author) { create(:user) } let(:author) { build(:user).becomes(user_class).tap(&:save!).becomes(User) }
let(:namespace) { create(:namespace, owner: author) }
let(:project) { create(:project_empty_repo, namespace: namespace, creator: author) }
# We can not rely on FactoryGirl as the state of Event may change in ways that # We can not rely on FactoryGirl as the state of Event may change in ways that
# the background migration does not expect, hence we use the Event class of # the background migration does not expect, hence we use the Event class of
......
require 'spec_helper'
describe Gitlab::Themes, lib: true do
describe '.body_classes' do
it 'returns a space-separated list of class names' do
css = described_class.body_classes
expect(css).to include('ui_indigo')
expect(css).to include(' ui_dark ')
expect(css).to include(' ui_blue')
end
end
describe '.by_id' do
it 'returns a Theme by its ID' do
expect(described_class.by_id(1).name).to eq 'Indigo'
expect(described_class.by_id(3).name).to eq 'Light'
end
end
describe '.default' do
it 'returns the default application theme' do
allow(described_class).to receive(:default_id).and_return(2)
expect(described_class.default.id).to eq 2
end
it 'prevents an infinite loop when configuration default is invalid' do
default = described_class::APPLICATION_DEFAULT
themes = described_class::THEMES
config = double(default_theme: 0).as_null_object
allow(Gitlab).to receive(:config).and_return(config)
expect(described_class.default.id).to eq default
config = double(default_theme: themes.size + 5).as_null_object
allow(Gitlab).to receive(:config).and_return(config)
expect(described_class.default.id).to eq default
end
end
describe '.each' do
it 'passes the block to the THEMES Array' do
ids = []
described_class.each { |theme| ids << theme.id }
expect(ids).not_to be_empty
end
end
end
...@@ -2,6 +2,8 @@ require 'spec_helper' ...@@ -2,6 +2,8 @@ require 'spec_helper'
require Rails.root.join('db', 'post_migrate', '20170607121233_convert_custom_notification_settings_to_columns') require Rails.root.join('db', 'post_migrate', '20170607121233_convert_custom_notification_settings_to_columns')
describe ConvertCustomNotificationSettingsToColumns, :migration do describe ConvertCustomNotificationSettingsToColumns, :migration do
let(:user_class) { table(:users) }
let(:settings_params) do let(:settings_params) do
[ [
{ level: 0, events: [:new_note] }, # disabled, single event { level: 0, events: [:new_note] }, # disabled, single event
...@@ -19,7 +21,7 @@ describe ConvertCustomNotificationSettingsToColumns, :migration do ...@@ -19,7 +21,7 @@ describe ConvertCustomNotificationSettingsToColumns, :migration do
events[event] = true events[event] = true
end end
user = create(:user) user = build(:user).becomes(user_class).tap(&:save!)
create_params = { user_id: user.id, level: params[:level], events: events } create_params = { user_id: user.id, level: params[:level], events: events }
notification_setting = described_class::NotificationSetting.create(create_params) notification_setting = described_class::NotificationSetting.create(create_params)
...@@ -35,7 +37,7 @@ describe ConvertCustomNotificationSettingsToColumns, :migration do ...@@ -35,7 +37,7 @@ describe ConvertCustomNotificationSettingsToColumns, :migration do
events[event] = true events[event] = true
end end
user = create(:user) user = build(:user).becomes(user_class).tap(&:save!)
create_params = events.merge(user_id: user.id, level: params[:level]) create_params = events.merge(user_id: user.id, level: params[:level])
notification_setting = described_class::NotificationSetting.create(create_params) notification_setting = described_class::NotificationSetting.create(create_params)
......
...@@ -716,6 +716,7 @@ describe User do ...@@ -716,6 +716,7 @@ describe User do
it "applies defaults to user" do it "applies defaults to user" do
expect(user.projects_limit).to eq(Gitlab.config.gitlab.default_projects_limit) expect(user.projects_limit).to eq(Gitlab.config.gitlab.default_projects_limit)
expect(user.can_create_group).to eq(Gitlab.config.gitlab.default_can_create_group) expect(user.can_create_group).to eq(Gitlab.config.gitlab.default_can_create_group)
expect(user.theme_id).to eq(Gitlab.config.gitlab.default_theme)
expect(user.external).to be_falsey expect(user.external).to be_falsey
end end
end end
...@@ -726,6 +727,7 @@ describe User do ...@@ -726,6 +727,7 @@ describe User do
it "applies defaults to user" do it "applies defaults to user" do
expect(user.projects_limit).to eq(123) expect(user.projects_limit).to eq(123)
expect(user.can_create_group).to be_falsey expect(user.can_create_group).to be_falsey
expect(user.theme_id).to eq(1)
end end
end end
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
"skype":"aertert", "skype":"aertert",
"linkedin":"", "linkedin":"",
"twitter":"", "twitter":"",
"color_scheme_id":2, "theme_id":2,"color_scheme_id":2,
"state":"active", "state":"active",
"created_at":"2012-12-21T13:02:20Z", "created_at":"2012-12-21T13:02:20Z",
"extern_uid":null, "extern_uid":null,
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
"skype":"aertert", "skype":"aertert",
"linkedin":"", "linkedin":"",
"twitter":"", "twitter":"",
"color_scheme_id":2, "theme_id":2,"color_scheme_id":2,
"state":"active", "state":"active",
"created_at":"2012-12-21T13:02:20Z", "created_at":"2012-12-21T13:02:20Z",
"extern_uid":null, "extern_uid":null,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment