Skip to content

Allow for user specific themes #5668

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Jan 9, 2019
1 change: 1 addition & 0 deletions cmd/admin.go
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,7 @@ func runCreateUser(c *cli.Context) error {
IsActive: true,
IsAdmin: c.Bool("admin"),
MustChangePassword: changePassword,
Theme: setting.UI.DefaultTheme,
}); err != nil {
return fmt.Errorf("CreateUser: %v", err)
}
Expand Down
2 changes: 2 additions & 0 deletions custom/conf/app.ini.sample
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ MAX_DISPLAY_FILE_SIZE = 8388608
SHOW_USER_EMAIL = true
; Set the default theme for the Gitea install
DEFAULT_THEME = gitea
; All available themes
THEMES = gitea,arc-green

[ui.admin]
; Number of users that are displayed on one page
Expand Down
4 changes: 3 additions & 1 deletion models/migrations/migrations.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import (
"github.com/Unknwon/com"
"github.com/go-xorm/xorm"
gouuid "github.com/satori/go.uuid"
"gopkg.in/ini.v1"
ini "gopkg.in/ini.v1"

"code.gitea.io/gitea/modules/generate"
"code.gitea.io/gitea/modules/log"
Expand Down Expand Up @@ -206,6 +206,8 @@ var migrations = []Migration{
NewMigration("clear nonused data which not deleted when user was deleted", clearNonusedData),
// v76 -> v77
NewMigration("add pull request rebase with merge commit", addPullRequestRebaseWithMerge),
// v77 -> v78
NewMigration("add theme to users", addUserDefaultTheme),
}

// Migrate database to current version
Expand Down
17 changes: 17 additions & 0 deletions models/migrations/v77.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package migrations

import (
"github.com/go-xorm/xorm"
)

func addUserDefaultTheme(x *xorm.Engine) error {
type User struct {
Theme string `xorm:"VARCHAR(30)"`
}

return x.Sync2(new(User))
}
15 changes: 15 additions & 0 deletions models/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ type User struct {

// Preferences
DiffViewStyle string `xorm:"NOT NULL DEFAULT ''"`
Theme string `xorm:"NOT NULL DEFAULT ''"`
}

// BeforeUpdate is invoked from XORM before updating this object.
Expand All @@ -165,6 +166,13 @@ func (u *User) BeforeUpdate() {
u.Description = base.TruncateString(u.Description, 255)
}

// AfterLoad is invoked from XORM after filling all the fields of this object.
func (u *User) AfterLoad() {
if u.Theme == "" {
u.Theme = setting.UI.DefaultTheme
}
}

// SetLastLogin set time to last login
func (u *User) SetLastLogin() {
u.LastLoginUnix = util.TimeStampNow()
Expand All @@ -176,6 +184,12 @@ func (u *User) UpdateDiffViewStyle(style string) error {
return UpdateUserCols(u, "diff_view_style")
}

// UpdateTheme updates a users' theme irrespective of the site wide theme
func (u *User) UpdateTheme(themeName string) error {
u.Theme = themeName
return UpdateUserCols(u, "theme")
}

// getEmail returns an noreply email, if the user has set to keep his
// email address private, otherwise the primary email address.
func (u *User) getEmail() string {
Expand Down Expand Up @@ -777,6 +791,7 @@ func CreateUser(u *User) (err error) {
u.HashPassword(u.Passwd)
u.AllowCreateOrganization = setting.Service.DefaultAllowCreateOrganization
u.MaxRepoCreation = -1
u.Theme = setting.UI.DefaultTheme

if _, err = sess.Insert(u); err != nil {
return err
Expand Down
26 changes: 25 additions & 1 deletion modules/auth/user_form.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (
"code.gitea.io/gitea/modules/setting"

"github.com/go-macaron/binding"
"gopkg.in/macaron.v1"
macaron "gopkg.in/macaron.v1"
)

// InstallForm form for installation page
Expand Down Expand Up @@ -189,6 +189,30 @@ func (f *AddEmailForm) Validate(ctx *macaron.Context, errs binding.Errors) bindi
return validate(errs, ctx.Data, f, ctx.Locale)
}

// UpdateThemeForm form for updating a users' theme
type UpdateThemeForm struct {
Theme string `binding:"Required;MaxSize(30)"`
}

// Validate validates the field
func (f *UpdateThemeForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
return validate(errs, ctx.Data, f, ctx.Locale)
}

// IsThemeExists checks if the theme is a theme available in the config.
func (f UpdateThemeForm) IsThemeExists() bool {
var exists bool

for _, v := range setting.UI.Themes {
if strings.ToLower(v) == strings.ToLower(f.Theme) {
exists = true
break
}
}

return exists
}

// ChangePasswordForm form for changing password
type ChangePasswordForm struct {
OldPassword string `form:"old_password" binding:"MaxSize(255)"`
Expand Down
1 change: 1 addition & 0 deletions modules/setting/defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ var (
defaultLangs = strings.Split("en-US,zh-CN,zh-HK,zh-TW,de-DE,fr-FR,nl-NL,lv-LV,ru-RU,uk-UA,ja-JP,es-ES,pt-BR,pl-PL,bg-BG,it-IT,fi-FI,tr-TR,cs-CZ,sr-SP,sv-SE,ko-KR", ",")
defaultLangNames = strings.Split("English,简体中文,繁體中文(香港),繁體中文(台灣),Deutsch,français,Nederlands,latviešu,русский,Українська,日本語,español,português do Brasil,polski,български,italiano,suomi,Türkçe,čeština,српски,svenska,한국어", ",")
defaultPullRequestWorkInProgressPrefixes = strings.Split("WIP:,[WIP]", ",")
defaultThemes = strings.Split("gitea", "arc-green")
)
8 changes: 5 additions & 3 deletions modules/setting/setting.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ import (
"github.com/go-macaron/session"
_ "github.com/go-macaron/session/redis" // redis plugin for store session
"github.com/go-xorm/core"
"github.com/kballard/go-shellquote"
"github.com/mcuadros/go-version"
"gopkg.in/ini.v1"
shellquote "github.com/kballard/go-shellquote"
version "github.com/mcuadros/go-version"
ini "gopkg.in/ini.v1"
"strk.kbt.io/projects/go/libravatar"
)

Expand Down Expand Up @@ -303,6 +303,7 @@ var (
MaxDisplayFileSize int64
ShowUserEmail bool
DefaultTheme string
Themes []string

Admin struct {
UserPagingNum int
Expand All @@ -329,6 +330,7 @@ var (
ThemeColorMetaTag: `#6cc644`,
MaxDisplayFileSize: 8388608,
DefaultTheme: `gitea`,
Themes: []string{`gitea`, `arc-green`},
Admin: struct {
UserPagingNum int
RepoPagingNum int
Expand Down
6 changes: 6 additions & 0 deletions options/locale/locale_en-US.ini
Original file line number Diff line number Diff line change
Expand Up @@ -355,13 +355,15 @@ password_username_disabled = Non-local users are not allowed to change their use
full_name = Full Name
website = Website
location = Location
update_theme = Update Theme
update_profile = Update Profile
update_profile_success = Your profile has been updated.
change_username = Your username has been changed.
change_username_prompt = Note: username changes also change your account URL.
continue = Continue
cancel = Cancel
language = Language
ui = Theme

lookup_avatar_by_mail = Look Up Avatar by Email Address
federated_avatar_lookup = Federated Avatar Lookup
Expand All @@ -382,14 +384,18 @@ password_change_disabled = Non-local users can not update their password through

emails = Email Addresses
manage_emails = Manage Email Addresses
manage_themes = Select default theme
manage_openid = Manage OpenID Addresses
email_desc = Your primary email address will be used for notifications and other operations.
theme_desc = This will be your default theme across the site.
primary = Primary
primary_email = Make Primary
delete_email = Remove
email_deletion = Remove Email Address
email_deletion_desc = The email address and related information will be removed from your account. Git commits by this email address will remain unchanged. Continue?
email_deletion_success = The email address has been removed.
theme_update_success = Your theme was updated.
theme_update_error = The selected theme does not exist.
openid_deletion = Remove OpenID Address
openid_deletion_desc = Removing this OpenID address from your account will prevent you from signing in with it. Continue?
openid_deletion_success = The OpenID address has been removed.
Expand Down
4 changes: 3 additions & 1 deletion routers/routes/routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ import (
"github.com/go-macaron/toolbox"
"github.com/prometheus/client_golang/prometheus"
"github.com/tstranex/u2f"
"gopkg.in/macaron.v1"
macaron "gopkg.in/macaron.v1"
)

// NewMacaron initializes Macaron instance.
Expand Down Expand Up @@ -243,6 +243,7 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Post("/email", bindIgnErr(auth.AddEmailForm{}), userSetting.EmailPost)
m.Post("/email/delete", userSetting.DeleteEmail)
m.Post("/delete", userSetting.DeleteAccount)
m.Post("/theme", bindIgnErr(auth.UpdateThemeForm{}), userSetting.UpdateUIThemePost)
})
m.Group("/security", func() {
m.Get("", userSetting.Security)
Expand Down Expand Up @@ -292,6 +293,7 @@ func RegisterRoutes(m *macaron.Macaron) {
})
}, reqSignIn, func(ctx *context.Context) {
ctx.Data["PageIsUserSettings"] = true
ctx.Data["AllThemes"] = setting.UI.Themes
})

m.Group("/user", func() {
Expand Down
28 changes: 28 additions & 0 deletions routers/user/setting/account.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,34 @@ func DeleteAccount(ctx *context.Context) {
}
}

// UpdateUIThemePost is used to update users' specific theme
func UpdateUIThemePost(ctx *context.Context, form auth.UpdateThemeForm) {

ctx.Data["Title"] = ctx.Tr("settings")
ctx.Data["PageIsSettingsAccount"] = true

if ctx.HasError() {
ctx.Redirect(setting.AppSubURL + "/user/settings/account")
return
}

if !form.IsThemeExists() {
ctx.Flash.Error(ctx.Tr("settings.theme_update_error"))
ctx.Redirect(setting.AppSubURL + "/user/settings/account")
return
}

if err := ctx.User.UpdateTheme(form.Theme); err != nil {
ctx.Flash.Error(ctx.Tr("settings.theme_update_error"))
ctx.Redirect(setting.AppSubURL + "/user/settings/account")
return
}

log.Trace("Update user theme: %s", ctx.User.Name)
ctx.Flash.Success(ctx.Tr("settings.theme_update_success"))
ctx.Redirect(setting.AppSubURL + "/user/settings/account")
}

func loadAccountData(ctx *context.Context) {
emails, err := models.GetEmailAddresses(ctx.User.ID)
if err != nil {
Expand Down
8 changes: 6 additions & 2 deletions templates/base/head.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<meta http-equiv="x-ua-compatible" content="ie=edge">
<title>{{if .Title}}{{.Title}} - {{end}}{{AppName}}</title>
<link rel="manifest" href="{{AppSubUrl}}/manifest.json">

<script>
if ('serviceWorker' in navigator) {
window.addEventListener('load', function() {
Expand Down Expand Up @@ -147,7 +147,11 @@
<meta property="og:url" content="{{AppUrl}}" />
<meta property="og:description" content="{{MetaDescription}}">
{{end}}
{{if ne DefaultTheme "gitea"}}
{{if .IsSigned }}
{{ if ne .SignedUser.Theme "gitea" }}
<link rel="stylesheet" href="{{AppSubUrl}}/css/theme-{{.SignedUser.Theme}}.css">
{{end}}
{{else if ne DefaultTheme "gitea"}}
<link rel="stylesheet" href="{{AppSubUrl}}/css/theme-{{DefaultTheme}}.css">
{{end}}
{{template "custom/header" .}}
Expand Down
10 changes: 7 additions & 3 deletions templates/pwa/serviceworker_js.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,14 @@ var urlsToCache = [
'{{AppSubUrl}}/vendor/plugins/jquery.minicolors/jquery.minicolors.css',
'{{AppSubUrl}}/vendor/plugins/jquery.datetimepicker/jquery.datetimepicker.css',
'{{AppSubUrl}}/vendor/plugins/dropzone/dropzone.css',
{{if ne DefaultTheme "gitea"}}
'{{AppSubUrl}}/css/theme-{{DefaultTheme}}.css',
{{if .IsSigned }}
{{ if ne .SignedUser.Theme "gitea" }}
'{{AppSubUrl}}/css/theme-{{.SignedUser.Theme}}.css'
{{end}}
{{else if ne DefaultTheme "gitea"}}
'{{AppSubUrl}}/css/theme-{{DefaultTheme}}.css'
{{end}}

// img
'{{AppSubUrl}}/img/gitea-sm.png',
'{{AppSubUrl}}/img/gitea-lg.png',
Expand Down
38 changes: 38 additions & 0 deletions templates/user/settings/account.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,44 @@
</form>
</div>

<h4 class="ui top attached header">
{{.i18n.Tr "settings.manage_themes"}}
</h4>
<div class="ui attached segment">
<div class="ui email list">
<div class="item">
{{.i18n.Tr "settings.theme_desc"}}
</div>

<form class="ui form" action="{{.Link}}/theme" method="post">
{{.CsrfTokenHtml}}
<div class="field">
<label for="ui">{{.i18n.Tr "settings.ui"}}</label>
<div class="ui selection dropdown" id="ui">
<input name="theme" type="hidden" value="{{.SignedUser.Theme}}">
<i class="dropdown icon"></i>
<div class="text">
{{range $i,$a := .AllThemes}}
{{if eq $.SignedUser.Theme $a}}{{$a}}{{end}}
{{end}}
</div>

<div class="menu">
{{range $i,$a := .AllThemes}}
<div class="item{{if eq $.SignedUser.Theme $a}} active selected{{end}}" data-value="{{$a}}">
{{$a}}
</div>
{{end}}
</div>
</div>
</div>

<div class="field">
<button class="ui green button">{{$.i18n.Tr "settings.update_theme"}}</button>
</div>
</form>
</div>
</div>
<h4 class="ui top attached warning header">
{{.i18n.Tr "settings.delete_account"}}
</h4>
Expand Down