Merge pull request #6033 from ATSE-TheBeginners/4297-color-themes

Issue #4297: Color themes
This commit is contained in:
Jonne Haß 2015-06-07 21:10:56 +02:00
commit bb9a15ceac
39 changed files with 334 additions and 19 deletions

View file

@ -29,6 +29,7 @@ Ruby 2.0 is no longer officially supported.
* Destroy Participation when removing interactions with a post [#5852](https://github.com/diaspora/diaspora/pull/5852) * Destroy Participation when removing interactions with a post [#5852](https://github.com/diaspora/diaspora/pull/5852)
## Features ## Features
* Support color themes [#6033](https://github.com/diaspora/diaspora/pull/6033)
# 0.5.1.0 # 0.5.1.0

View file

@ -1,6 +1,4 @@
/* variables */ @import "bootstrap-complete.scss";
@import 'bootstrap-variables'; // our overwrites
@import 'bootstrap/variables'; // the original bootstrap ones
@import 'perfect-scrollbar'; @import 'perfect-scrollbar';

View file

@ -21,6 +21,9 @@ $default-border-radius: 3px;
@include linear-gradient(lighten($color,20%), darken($color,15%)); @include linear-gradient(lighten($color,20%), darken($color,15%));
} }
@mixin header-gradient($color) {
@include linear-gradient(lighten($color, 2%), darken($color, 2%), 0%, 80%);
}
@mixin linear-gradient($from, $to, $start:0%, $end:100%){ @mixin linear-gradient($from, $to, $start:0%, $end:100%){
background: mix($from,$to); background: mix($from,$to);

View file

@ -1,4 +1,4 @@
// Calling this file bootstrap would cause an infinite recursion during asset compilation. // Calling this file bootstrap would cause an infinite recursion during asset compilation.
@import "bootstrap-sprockets"; @import "bootstrap-sprockets";
@import "bootstrap-variables"; @import "bootstrap-variables"; //our overwrites of bootstrap variables
@import "bootstrap"; @import "bootstrap";

View file

@ -0,0 +1,77 @@
/* Raw CSS */
body {
header {
.container {
#nav_badges > #notification_dropdown {
a#mark_all_read_link, .stream_element a {
color: $link-color;
}
div.view_all {
background-color: $main-color;
}
}
#global_search > form > input.ac_input {
background-color: $header-search-color;
border-color: $main-color-dark;
&:hover {
background-color: lighten($header-search-color, 2%);
}
&:focus {
background-color: white;
}
}
ul#user_menu.active {
box-shadow: 0 1px 3px $main-color-dark;
background-color: $main-color-dark;
li {
border-color: darken($main-color-dark, 5%);
&.user-menu-item a:hover {
background-color: lighten($main-color-dark, 15%);
}
}
}
}
}
a,
a.tag,
.btn-link,
#main_stream .stream_element > .media a.author-name,
#hovercard h4 a,
.stream_element .from a.self {
color: $link-color;
&:hover, &:focus {
color: darken($link-color, 10%);
}
}
#publisher_textarea_wrapper > #button_container > span.markdownIndications > a {
color: fade-out($link-color, 0.4);
}
#main_stream .stream_element {
&.post_preview {
background-color: $main-color-essence;
border-color: darken($main-color-essence, 5%);
}
}
#leftNavBar .hoverable:hover { background-color: $main-color-essence }
.poll_form .progress .bar { background-color: $main-color-dark }
#profile_container .profile_header #profile_horizontal_bar ul#profile_nav > li.active {
border-bottom-color: $main-color;
}
.badge { background-color: $main-color }
}

View file

@ -0,0 +1,17 @@
/* Main color(s) */
$main-color: #009900;
/* Shades */
$main-color-essence: fade-out($main-color, 0.8);
$main-color-light: lighten($main-color, 10%);
$main-color-dark: darken($main-color, 20%);
/* Bootstrap Variables */
$btn-primary-bg: $main-color;
$link-color: $main-color;
/* Custom Variables */
$header-background-color: $main-color-dark;
$header-search-color: lighten($header-background-color, 10%);
@import "color_themes/color_theme_override"

View file

@ -0,0 +1,2 @@
@import "style";
@import "application";

View file

@ -0,0 +1,2 @@
@import "style";
@import "mobile/mobile";

View file

@ -0,0 +1,17 @@
/* Main color(s) */
$main-color: #1034A6;
/* Shades */
$main-color-essence: fade-out($main-color, 0.8);
$main-color-light: lighten($main-color, 10%);
$main-color-dark: darken($main-color, 15%);
/* Bootstrap Variables */
$btn-primary-bg: $main-color;
$link-color: $main-color;
/* Custom Variables */
$header-background-color: $main-color-dark;
$header-search-color: lighten($header-background-color, 15%);
@import "color_themes/color_theme_override"

View file

@ -0,0 +1,2 @@
@import "style";
@import "application";

View file

@ -0,0 +1,2 @@
@import "style";
@import "mobile/mobile";

View file

@ -0,0 +1,17 @@
/* Main color(s) */
$main-color: #FF00FF;
/* Shades */
$main-color-essence: fade-out($main-color, 0.8);
$main-color-light: lighten($main-color, 10%);
$main-color-dark: darken($main-color, 30%);
/* Bootstrap Variables */
$btn-primary-bg: darken($main-color, 5%);
$link-color: $main-color;
/* Custom Variables */
$header-background-color: $main-color-dark;
$header-search-color: lighten($header-background-color, 10%);
@import "color_themes/color_theme_override"

View file

@ -0,0 +1,2 @@
@import "style";
@import "application";

View file

@ -0,0 +1,2 @@
@import "style";
@import "mobile/mobile";

View file

@ -0,0 +1,8 @@
/* Main color(s) */
$main-color: #585858;
/* Shades */
$main-color-dark: darken($main-color, 15%);
/* Variables */
$header-background-color: $main-color-dark;

View file

@ -0,0 +1,2 @@
@import "style";
@import "application";

View file

@ -0,0 +1,2 @@
@import "style";
@import "mobile/mobile";

View file

@ -4,8 +4,8 @@
} }
body > header { body > header {
@include header-gradient($header-background-color);
box-shadow: 0 1px 3px rgba(0,0,0,0.9); box-shadow: 0 1px 3px rgba(0,0,0,0.9);
background: image-url('header-bg.png') rgb(40,35,35);
z-index: 1001; z-index: 1001;
padding: 6px 0; padding: 6px 0;
color: #CCC; color: #CCC;
@ -15,7 +15,7 @@ body > header {
min-width: 620px; min-width: 620px;
top: 0; top: 0;
left: 0; left: 0;
border-bottom: 1px solid #000; border-bottom: 1px solid darken($header-background-color, 8%);
> div > div.container { > div > div.container {
height: 26px; height: 26px;

View file

@ -8,9 +8,9 @@ header {
height: 45px; height: 45px;
top: 0px; top: 0px;
z-index: 10; z-index: 10;
background: image-url('header-bg-long.jpg') rgb(40,35,35); @include header-gradient($header-background-color);
box-shadow: 0 1px 2px #333; box-shadow: 0 1px 2px #333;
border-bottom: 1px solid #222; border-bottom: 1px solid darken($header-background-color, 8%);
} }
#main_nav { #main_nav {

View file

@ -917,7 +917,7 @@ form#update_profile_form {
.submit_block { margin-bottom: 20px; } .submit_block { margin-bottom: 20px; }
} }
select#user_language, #user_auto_follow_back_aspect_id, #aspect_ids_ { select#user_language, select#user_color_theme, #user_auto_follow_back_aspect_id, #aspect_ids_ {
padding: 3px; padding: 3px;
} }

View file

@ -69,6 +69,12 @@ class UsersController < ApplicationController
else else
flash[:error] = I18n.t 'users.update.follow_settings_not_changed' flash[:error] = I18n.t 'users.update.follow_settings_not_changed'
end end
elsif u[:color_theme]
if @user.update_attributes(u)
flash[:notice] = I18n.t "users.update.color_theme_changed"
else
flash[:error] = I18n.t "users.update.color_theme_not_changed"
end
end end
end end
set_email_preferences set_email_preferences
@ -181,6 +187,7 @@ class UsersController < ApplicationController
:password, :password,
:password_confirmation, :password_confirmation,
:language, :language,
:color_theme,
:disable_mail, :disable_mail,
:invitation_service, :invitation_service,
:invitation_identifier, :invitation_identifier,

View file

@ -45,8 +45,8 @@ module LayoutHelper
end end
end end
def include_base_css_framework def include_color_theme(view="desktop")
stylesheet_link_tag('bootstrap-complete') stylesheet_link_tag "#{current_color_theme}/#{view}", media: "all"
end end
def old_browser_js_support def old_browser_js_support

View file

@ -6,4 +6,39 @@ module UsersHelper
def owner_image_link def owner_image_link
person_image_link(current_user.person, :size => :thumb_small) person_image_link(current_user.person, :size => :thumb_small)
end end
# Returns the path of the current color theme so that it
# can be loaded in app/views/layouts/application.html.haml
# and app/views/layouts/application.mobile.haml. If the user
# is not signed in or has not specified a color theme, the
# default (original) color theme is loaded.
#
# @example if user is not signed in
# current_color_theme #=> "color_themes/original"
# @example if user Alice has not selected a color theme
# current_color_theme #=> "color_themes/original"
# @example if user Alice has selected a "magenta" theme
# current_color_theme #=> "color_themes/magenta"
def current_color_theme
if user_signed_in?
color_theme = current_user.color_theme
end
color_theme ||= AppConfig.settings.default_color_theme
"color_themes/#{color_theme}"
end
# Returns an array of the color themes available, as
# specified from AVAILABLE_COLOR_THEMES in
# config/initializers/color_themes.rb.
#
# @example if AVAILABLE_COLOR_THEMES = {"original"=>"Original dark", "dark_green" => "Dark green"}
# available_color_themes
# #=> [["Original dark", "original"], ["Dark green", "dark_green"]]
def available_color_themes
opts = []
AVAILABLE_COLOR_THEMES.map do |theme_code, theme_name|
opts << [theme_name, theme_code]
end
opts
end
end end

View file

@ -23,12 +23,14 @@ class User < ActiveRecord::Base
before_validation :strip_and_downcase_username before_validation :strip_and_downcase_username
before_validation :set_current_language, :on => :create before_validation :set_current_language, :on => :create
before_validation :set_default_color_theme, on: :create
validates :username, :presence => true, :uniqueness => true validates :username, :presence => true, :uniqueness => true
validates_format_of :username, :with => /\A[A-Za-z0-9_]+\z/ validates_format_of :username, :with => /\A[A-Za-z0-9_]+\z/
validates_length_of :username, :maximum => 32 validates_length_of :username, :maximum => 32
validates_exclusion_of :username, :in => AppConfig.settings.username_blacklist validates_exclusion_of :username, :in => AppConfig.settings.username_blacklist
validates_inclusion_of :language, :in => AVAILABLE_LANGUAGE_CODES validates_inclusion_of :language, :in => AVAILABLE_LANGUAGE_CODES
validates :color_theme, inclusion: {in: AVAILABLE_COLOR_THEME_CODES}, allow_blank: true
validates_format_of :unconfirmed_email, :with => Devise.email_regexp, :allow_blank => true validates_format_of :unconfirmed_email, :with => Devise.email_regexp, :allow_blank => true
validates_presence_of :person, :unless => proc {|user| user.invitation_token.present?} validates_presence_of :person, :unless => proc {|user| user.invitation_token.present?}
@ -198,6 +200,10 @@ class User < ActiveRecord::Base
self.language = I18n.locale.to_s if self.language.blank? self.language = I18n.locale.to_s if self.language.blank?
end end
def set_default_color_theme
self.color_theme ||= AppConfig.settings.default_color_theme
end
# This override allows a user to enter either their email address or their username into the username field. # This override allows a user to enter either their email address or their username into the username field.
# @return [User] The user that matches the username/email condition. # @return [User] The user that matches the username/email condition.
# @return [nil] if no user matches that condition. # @return [nil] if no user matches that condition.
@ -428,6 +434,8 @@ class User < ActiveRecord::Base
self.email = opts[:email] self.email = opts[:email]
self.language = opts[:language] self.language = opts[:language]
self.language ||= I18n.locale.to_s self.language ||= I18n.locale.to_s
self.color_theme = opts[:color_theme]
self.color_theme ||= AppConfig.settings.default_color_theme
self.valid? self.valid?
errors = self.errors errors = self.errors
errors.delete :person errors.delete :person

View file

@ -20,8 +20,7 @@
= chartbeat_head_block = chartbeat_head_block
= include_mixpanel = include_mixpanel
= include_base_css_framework = include_color_theme
= stylesheet_link_tag 'application', media: 'all'
- if rtl? - if rtl?
= stylesheet_link_tag :rtl, media: 'all' = stylesheet_link_tag :rtl, media: 'all'

View file

@ -37,7 +37,7 @@
/ Stylesheets / Stylesheets
= stylesheet_link_tag 'mobile/mobile', :format => 'all' = include_color_theme "mobile"
= yield(:custom_css) = yield(:custom_css)

View file

@ -61,6 +61,14 @@
%hr %hr
%h3= t(".change_color_theme")
= form_for "user", url: user_path, html: {method: :put} do |f|
.form-inline.clearfix
= f.select :color_theme, available_color_themes, {}, {class: "form-control form-group"}
= f.submit t(".change_color_theme"), class: "btn btn-primary pull-right"
%hr
%h3#stream-preferences %h3#stream-preferences
= t('.stream_preferences') = t('.stream_preferences')
= form_for current_user, url: user_path, html: { method: :put } do |f| = form_for current_user, url: user_path, html: { method: :put } do |f|

View file

@ -81,12 +81,12 @@ module Diaspora
templates.js templates.js
validation.js validation.js
bootstrap-complete.css
error_pages.css error_pages.css
admin.css admin.css
mobile/mobile.css
rtl.css rtl.css
home.css home.css
color_themes/*/desktop.css
color_themes/*/mobile.css
} }
# Version of your assets, change this if you want to expire all your assets # Version of your assets, change this if you want to expire all your assets

5
config/color_themes.yml Normal file
View file

@ -0,0 +1,5 @@
available:
original: "Original Dark"
dark_green: "Dark Green"
magenta: "Magenta"
egyptian_blue: "Egyptian Blue"

View file

@ -143,6 +143,7 @@ defaults:
warn_days: 30 warn_days: 30
limit_removals_to_per_day: 100 limit_removals_to_per_day: 100
source_url: source_url:
default_color_theme: "original"
services: services:
facebook: facebook:
enable: false enable: false

View file

@ -533,6 +533,13 @@ configuration: ## Section
## If not set your pod will provide a downloadable archive. ## If not set your pod will provide a downloadable archive.
#source_url: 'https://example.org/username/diaspora' #source_url: 'https://example.org/username/diaspora'
## Default color theme
## You can change which color theme is displayed when a user is not signed in
## or has not selected any color theme from the available ones. You simply have
## to enter the name of the theme's folder in "app/assets/stylesheets/color_themes/".
## ("original" for the theme in "app/assets/stylesheets/color_themes/original/", for
## example).
#default_color_theme: "original"
## Posting from Diaspora to external services (all are disabled by default). ## Posting from Diaspora to external services (all are disabled by default).
services: ## Section services: ## Section

View file

@ -0,0 +1,19 @@
# Generate the path to the .yml file with the available color themes.
color_themes_file = Rails.root.join("config/color_themes.yml")
# Check in case config/color_themes.yml does not exist.
if color_themes_file.exist?
# Load the file specified by the generated path.
color_themes = YAML.load_file(color_themes_file)
# If the file contains one or more color themes, include them in AVAILABLE_COLOR_THEMES,
# else include the original theme.
AVAILABLE_COLOR_THEMES =
if color_themes["available"].length > 0
color_themes["available"]
else
{"original" => "Original Dark"}
end
else
AVAILABLE_COLOR_THEMES = {"original" => "Original Dark"}
end
# Get all codes from available themes into a separate variable, so that they can be called easier.
AVAILABLE_COLOR_THEME_CODES = AVAILABLE_COLOR_THEMES.keys

View file

@ -1323,6 +1323,7 @@ en:
current_password_expl: "the one you sign in with..." current_password_expl: "the one you sign in with..."
character_minimum_expl: "must be at least six characters" character_minimum_expl: "must be at least six characters"
change_language: "Change language" change_language: "Change language"
change_color_theme: "Change color theme"
close_account_text: "Close account" close_account_text: "Close account"
stream_preferences: "Stream preferences" stream_preferences: "Stream preferences"
show_community_spotlight: "Show “community spotlight” in stream" show_community_spotlight: "Show “community spotlight” in stream"
@ -1401,6 +1402,8 @@ en:
unconfirmed_email_not_changed: "Email change failed" unconfirmed_email_not_changed: "Email change failed"
follow_settings_changed: "Follow settings changed" follow_settings_changed: "Follow settings changed"
follow_settings_not_changed: "Follow settings change failed." follow_settings_not_changed: "Follow settings change failed."
color_theme_changed: "Color theme successfully changed."
color_theme_not_changed: "An error occurred while changing the color theme."
public: public:
does_not_exist: "User %{username} does not exist!" does_not_exist: "User %{username} does not exist!"
confirm_email: confirm_email:

View file

@ -0,0 +1,9 @@
class EnableColorThemes < ActiveRecord::Migration
def up
add_column(:users, :color_theme, :string)
end
def down
remove_column(:users, :color_theme)
end
end

View file

@ -566,6 +566,7 @@ ActiveRecord::Schema.define(version: 20150531005120) do
t.string "exported_photos_file", limit: 255 t.string "exported_photos_file", limit: 255
t.datetime "exported_photos_at" t.datetime "exported_photos_at"
t.boolean "exporting_photos", default: false t.boolean "exporting_photos", default: false
t.string "color_theme", limit: 255
end end
add_index "users", ["authentication_token"], name: "index_users_on_authentication_token", unique: true, using: :btree add_index "users", ["authentication_token"], name: "index_users_on_authentication_token", unique: true, using: :btree

View file

@ -152,6 +152,17 @@ describe UsersController, :type => :controller do
end end
end end
describe "color_theme" do
it "allow the user to change his color theme" do
old_color_theme = "original"
@user.color_theme = old_color_theme
@user.save
put(:update, id: @user.id, user: {color_theme: "dark_green"})
@user.reload
expect(@user.color_theme).not_to eq(old_color_theme)
end
end
describe 'email' do describe 'email' do
it 'disallow the user to change his new (unconfirmed) mail when it is the same as the old' do it 'disallow the user to change his new (unconfirmed) mail when it is the same as the old' do
@user.email = "my@newemail.com" @user.email = "my@newemail.com"

View file

@ -0,0 +1,42 @@
require "spec_helper"
describe UsersHelper, :type => :helper do
include Devise::TestHelpers
describe "#current_color_theme" do
describe "if user is not signed in" do
before do
def user_signed_in?
false
end
end
it "returns the default color theme" do
expect(current_color_theme).to eq("color_themes/#{AppConfig.settings.default_color_theme}")
end
end
describe "if user is signed in" do
before do
@user = User.new
def user_signed_in?
true
end
def current_user
@user
end
end
it "returns the default color theme if user has not selected any theme" do
expect(current_color_theme).to eq("color_themes/#{AppConfig.settings.default_color_theme}")
end
it "returns the color theme selected by the user if there is a selected one" do
selected_theme = "test_theme"
@user.color_theme = selected_theme
expect(current_color_theme).to eq("color_themes/#{selected_theme}")
end
end
end
end

View file

@ -27,9 +27,7 @@ src_files:
# - stylesheets/*.css # - stylesheets/*.css
# #
stylesheets: stylesheets:
- assets/bootstrap.css - assets/color_themes/original/desktop.css
- assets/default.css
- assets/application.css
# helpers # helpers
# #

View file

@ -364,6 +364,13 @@ describe User, :type => :model do
expect(user.language).to eq('de') expect(user.language).to eq('de')
end end
end end
describe "of color_theme" do
it "requires availability" do
alice.color_theme = "some invalid theme"
expect(alice).not_to be_valid
end
end
end end
@ -1009,6 +1016,7 @@ describe User, :type => :model do
unconfirmed_email unconfirmed_email
confirm_email_token confirm_email_token
last_seen last_seen
color_theme
}.sort) }.sort)
end end
end end