Commit 9e4665dd authored by Sean McGivern's avatar Sean McGivern

Merge branch 'fabsrc/gitlab-ce-2105-add-setting-for-first-day-of-the-week-ee' into 'master'

EE port: add setting for first day of the week

See merge request gitlab-org/gitlab-ee!9465
parents cd27350d 6b3e31bf
...@@ -64,6 +64,7 @@ class DueDateSelect { ...@@ -64,6 +64,7 @@ class DueDateSelect {
this.saveDueDate(true); this.saveDueDate(true);
} }
}, },
firstDay: gon.first_day_of_week,
}); });
calendar.setDate(parsePikadayDate($dueDateInput.val())); calendar.setDate(parsePikadayDate($dueDateInput.val()));
...@@ -183,6 +184,7 @@ export default class DueDateSelectors { ...@@ -183,6 +184,7 @@ export default class DueDateSelectors {
onSelect(dateText) { onSelect(dateText) {
$datePicker.val(calendar.toString(dateText)); $datePicker.val(calendar.toString(dateText));
}, },
firstDay: gon.first_day_of_week,
}); });
calendar.setDate(parsePikadayDate(datePickerVal)); calendar.setDate(parsePikadayDate(datePickerVal));
......
...@@ -46,6 +46,7 @@ export default class IssuableForm { ...@@ -46,6 +46,7 @@ export default class IssuableForm {
parse: dateString => parsePikadayDate(dateString), parse: dateString => parsePikadayDate(dateString),
toString: date => pikadayToString(date), toString: date => pikadayToString(date),
onSelect: dateText => $issuableDueDate.val(calendar.toString(dateText)), onSelect: dateText => $issuableDueDate.val(calendar.toString(dateText)),
firstDay: gon.first_day_of_week,
}); });
calendar.setDate(parsePikadayDate($issuableDueDate.val())); calendar.setDate(parsePikadayDate($issuableDueDate.val()));
} }
......
...@@ -33,6 +33,7 @@ export default function memberExpirationDate(selector = '.js-access-expiration-d ...@@ -33,6 +33,7 @@ export default function memberExpirationDate(selector = '.js-access-expiration-d
toggleClearInput.call($input); toggleClearInput.call($input);
}, },
firstDay: gon.first_day_of_week,
}); });
calendar.setDate(parsePikadayDate($input.val())); calendar.setDate(parsePikadayDate($input.val()));
......
...@@ -43,10 +43,26 @@ document.addEventListener('DOMContentLoaded', () => { ...@@ -43,10 +43,26 @@ document.addEventListener('DOMContentLoaded', () => {
], ],
}); });
const reorderWeekDays = (weekDays, firstDayOfWeek = 0) => {
if (firstDayOfWeek === 0) {
return weekDays;
}
return Object.keys(weekDays).reduce((acc, dayName, idx, arr) => {
const reorderedDayName = arr[(idx + firstDayOfWeek) % arr.length];
return {
...acc,
[reorderedDayName]: weekDays[reorderedDayName],
};
}, {});
};
const hourData = chartData(projectChartData.hour); const hourData = chartData(projectChartData.hour);
responsiveChart($('#hour-chart'), hourData); responsiveChart($('#hour-chart'), hourData);
const dayData = chartData(projectChartData.weekDays); const weekDays = reorderWeekDays(projectChartData.weekDays, gon.first_day_of_week);
const dayData = chartData(weekDays);
responsiveChart($('#weekday-chart'), dayData); responsiveChart($('#weekday-chart'), dayData);
const monthData = chartData(projectChartData.month); const monthData = chartData(projectChartData.month);
......
...@@ -159,7 +159,7 @@ export default class ActivityCalendar { ...@@ -159,7 +159,7 @@ export default class ActivityCalendar {
.append('g') .append('g')
.attr('transform', (group, i) => { .attr('transform', (group, i) => {
_.each(group, (stamp, a) => { _.each(group, (stamp, a) => {
if (a === 0 && stamp.day === 0) { if (a === 0 && stamp.day === this.firstDayOfWeek) {
const month = stamp.date.getMonth(); const month = stamp.date.getMonth();
const x = this.daySizeWithSpace * i + 1 + this.daySizeWithSpace; const x = this.daySizeWithSpace * i + 1 + this.daySizeWithSpace;
const lastMonth = _.last(this.months); const lastMonth = _.last(this.months);
...@@ -205,6 +205,14 @@ export default class ActivityCalendar { ...@@ -205,6 +205,14 @@ export default class ActivityCalendar {
y: 29 + this.dayYPos(5), y: 29 + this.dayYPos(5),
}, },
]; ];
if (this.firstDayOfWeek === 1) {
days.push({
text: 'S',
y: 29 + this.dayYPos(7),
});
}
this.svg this.svg
.append('g') .append('g')
.selectAll('text') .selectAll('text')
......
...@@ -234,7 +234,7 @@ export default class UserTabs { ...@@ -234,7 +234,7 @@ export default class UserTabs {
data, data,
calendarActivitiesPath, calendarActivitiesPath,
utcOffset, utcOffset,
0, gon.first_day_of_week,
monthsAgo, monthsAgo,
); );
} }
......
...@@ -40,6 +40,7 @@ export default { ...@@ -40,6 +40,7 @@ export default {
toString: date => pikadayToString(date), toString: date => pikadayToString(date),
onSelect: this.selected.bind(this), onSelect: this.selected.bind(this),
onClose: this.toggled.bind(this), onClose: this.toggled.bind(this),
firstDay: gon.first_day_of_week,
}); });
this.$el.append(this.calendar.el); this.$el.append(this.calendar.el);
......
...@@ -37,7 +37,7 @@ class Profiles::PreferencesController < Profiles::ApplicationController ...@@ -37,7 +37,7 @@ class Profiles::PreferencesController < Profiles::ApplicationController
end end
def preferences_param_names def preferences_param_names
[:color_scheme_id, :layout, :dashboard, :project_view, :theme_id] [:color_scheme_id, :layout, :dashboard, :project_view, :theme_id, :first_day_of_week]
end end
end end
......
...@@ -150,6 +150,7 @@ module ApplicationSettingsHelper ...@@ -150,6 +150,7 @@ module ApplicationSettingsHelper
:email_author_in_body, :email_author_in_body,
:enabled_git_access_protocol, :enabled_git_access_protocol,
:enforce_terms, :enforce_terms,
:first_day_of_week,
:gitaly_timeout_default, :gitaly_timeout_default,
:gitaly_timeout_medium, :gitaly_timeout_medium,
:gitaly_timeout_fast, :gitaly_timeout_fast,
......
...@@ -43,6 +43,17 @@ module PreferencesHelper ...@@ -43,6 +43,17 @@ module PreferencesHelper
] ]
end end
def first_day_of_week_choices
[
[_('Sunday'), 0],
[_('Monday'), 1]
]
end
def first_day_of_week_choices_with_default
first_day_of_week_choices.unshift([_('System default (%{default})') % { default: default_first_day_of_week }, nil])
end
def user_application_theme def user_application_theme
@user_application_theme ||= Gitlab::Themes.for_user(current_user).css_class @user_application_theme ||= Gitlab::Themes.for_user(current_user).css_class
end end
...@@ -66,6 +77,10 @@ module PreferencesHelper ...@@ -66,6 +77,10 @@ module PreferencesHelper
def excluded_dashboard_choices def excluded_dashboard_choices
['operations'] ['operations']
end end
def default_first_day_of_week
first_day_of_week_choices.rassoc(Gitlab::CurrentSettings.first_day_of_week).first
end
end end
PreferencesHelper.prepend(EE::PreferencesHelper) PreferencesHelper.prepend(EE::PreferencesHelper)
...@@ -250,6 +250,7 @@ class ApplicationSetting < ActiveRecord::Base ...@@ -250,6 +250,7 @@ class ApplicationSetting < ActiveRecord::Base
dsa_key_restriction: 0, dsa_key_restriction: 0,
ecdsa_key_restriction: 0, ecdsa_key_restriction: 0,
ed25519_key_restriction: 0, ed25519_key_restriction: 0,
first_day_of_week: 0,
gitaly_timeout_default: 55, gitaly_timeout_default: 55,
gitaly_timeout_fast: 10, gitaly_timeout_fast: 10,
gitaly_timeout_medium: 30, gitaly_timeout_medium: 30,
......
...@@ -228,6 +228,9 @@ class User < ApplicationRecord ...@@ -228,6 +228,9 @@ class User < ApplicationRecord
delegate :path, to: :namespace, allow_nil: true, prefix: true delegate :path, to: :namespace, allow_nil: true, prefix: true
delegate :notes_filter_for, to: :user_preference delegate :notes_filter_for, to: :user_preference
delegate :set_notes_filter, to: :user_preference delegate :set_notes_filter, to: :user_preference
delegate :first_day_of_week, :first_day_of_week=, to: :user_preference
accepts_nested_attributes_for :user_preference, update_only: true
state_machine :state, initial: :active do state_machine :state, initial: :active do
event :block do event :block do
......
= form_for @application_setting, url: admin_application_settings_path(anchor: 'js-localization-settings'), html: { class: 'fieldset-form' } do |f|
= form_errors(@application_setting)
%fieldset
.form-group
= f.label :first_day_of_week, _('Default first day of the week'), class: 'label-bold'
= f.select :first_day_of_week, first_day_of_week_choices, {}, class: 'form-control'
.form-text.text-muted
= _('Default first day of the week in calendars and date pickers.')
= f.submit _('Save changes'), class: "btn btn-success"
...@@ -56,3 +56,14 @@ ...@@ -56,3 +56,14 @@
= _('Configure Gitaly timeouts.') = _('Configure Gitaly timeouts.')
.settings-content .settings-content
= render 'gitaly' = render 'gitaly'
%section.settings.as-localization.no-animate#js-localization-settings{ class: ('expanded' if expanded_by_default?) }
.settings-header
%h4
= _('Localization')
%button.btn.btn-default.js-settings-toggle{ type: 'button' }
= expanded_by_default? ? _('Collapse') : _('Expand')
%p
= _('Various localization settings.')
.settings-content
= render 'localization'
...@@ -60,5 +60,21 @@ ...@@ -60,5 +60,21 @@
= f.select :project_view, project_view_choices, {}, class: 'form-control' = f.select :project_view, project_view_choices, {}, class: 'form-control'
.form-text.text-muted .form-text.text-muted
Choose what content you want to see on a project’s overview page. Choose what content you want to see on a project’s overview page.
.col-sm-12
%hr
.col-lg-4.profile-settings-sidebar
%h4.prepend-top-0
= _('Localization')
%p
= _('Customize language and region related settings.')
= succeed '.' do
= link_to _('Learn more'), help_page_path('user/profile/preferences', anchor: 'localization'), target: '_blank'
.col-lg-8
.form-group
= f.label :first_day_of_week, class: 'label-bold' do
= _('First day of the week')
= f.select :first_day_of_week, first_day_of_week_choices_with_default, {}, class: 'form-control'
.form-group .form-group
= f.submit 'Save changes', class: 'btn btn-success' = f.submit _('Save changes'), class: 'btn btn-success'
---
title: Add setting for first day of the week
merge_request: 22755
author: Fabian Schneider @fabsrc
type: added
# frozen_string_literal: true
class AddFirstDayOfWeekToUserPreferences < ActiveRecord::Migration
DOWNTIME = false
def change
add_column :user_preferences, :first_day_of_week, :integer
end
end
# frozen_string_literal: true
class AddFirstDayOfWeekToApplicationSettings < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
disable_ddl_transaction!
DOWNTIME = false
def up
add_column_with_default(:application_settings, :first_day_of_week, :integer, default: 0)
end
def down
remove_column(:application_settings, :first_day_of_week)
end
end
...@@ -215,6 +215,7 @@ ActiveRecord::Schema.define(version: 20190204115450) do ...@@ -215,6 +215,7 @@ ActiveRecord::Schema.define(version: 20190204115450) do
t.boolean "protected_ci_variables", default: false, null: false t.boolean "protected_ci_variables", default: false, null: false
t.string "runners_registration_token_encrypted" t.string "runners_registration_token_encrypted"
t.integer "local_markdown_version", default: 0, null: false t.integer "local_markdown_version", default: 0, null: false
t.integer "first_day_of_week", default: 0, null: false
t.index ["custom_project_templates_group_id"], name: "index_application_settings_on_custom_project_templates_group_id", using: :btree t.index ["custom_project_templates_group_id"], name: "index_application_settings_on_custom_project_templates_group_id", using: :btree
t.index ["file_template_project_id"], name: "index_application_settings_on_file_template_project_id", using: :btree t.index ["file_template_project_id"], name: "index_application_settings_on_file_template_project_id", using: :btree
t.index ["usage_stats_set_by_user_id"], name: "index_application_settings_on_usage_stats_set_by_user_id", using: :btree t.index ["usage_stats_set_by_user_id"], name: "index_application_settings_on_usage_stats_set_by_user_id", using: :btree
...@@ -3009,6 +3010,7 @@ ActiveRecord::Schema.define(version: 20190204115450) do ...@@ -3009,6 +3010,7 @@ ActiveRecord::Schema.define(version: 20190204115450) do
t.string "issues_sort" t.string "issues_sort"
t.string "merge_requests_sort" t.string "merge_requests_sort"
t.string "roadmaps_sort" t.string "roadmaps_sort"
t.integer "first_day_of_week"
t.index ["user_id"], name: "index_user_preferences_on_user_id", unique: true, using: :btree t.index ["user_id"], name: "index_user_preferences_on_user_id", unique: true, using: :btree
end end
......
...@@ -57,6 +57,7 @@ Example response: ...@@ -57,6 +57,7 @@ Example response:
"dsa_key_restriction": 0, "dsa_key_restriction": 0,
"ecdsa_key_restriction": 0, "ecdsa_key_restriction": 0,
"ed25519_key_restriction": 0, "ed25519_key_restriction": 0,
"first_day_of_week": 0,
"enforce_terms": true, "enforce_terms": true,
"terms": "Hello world!", "terms": "Hello world!",
"performance_bar_allowed_group_id": 42, "performance_bar_allowed_group_id": 42,
...@@ -119,6 +120,7 @@ Example response: ...@@ -119,6 +120,7 @@ Example response:
"dsa_key_restriction": 0, "dsa_key_restriction": 0,
"ecdsa_key_restriction": 0, "ecdsa_key_restriction": 0,
"ed25519_key_restriction": 0, "ed25519_key_restriction": 0,
"first_day_of_week": 0,
"enforce_terms": true, "enforce_terms": true,
"terms": "Hello world!", "terms": "Hello world!",
"performance_bar_allowed_group_id": 42, "performance_bar_allowed_group_id": 42,
...@@ -184,6 +186,7 @@ are listed in the descriptions of the relevant settings. ...@@ -184,6 +186,7 @@ are listed in the descriptions of the relevant settings.
| `external_authorization_service_timeout` | float | required by: `external_authorization_service_enabled` | **(Premium)** The timeout after which an authorization request is aborted, in seconds. When a request times out, access is denied to the user. (min: 0.001, max: 10, step: 0.001) | | `external_authorization_service_timeout` | float | required by: `external_authorization_service_enabled` | **(Premium)** The timeout after which an authorization request is aborted, in seconds. When a request times out, access is denied to the user. (min: 0.001, max: 10, step: 0.001) |
| `external_authorization_service_url` | string | required by: `external_authorization_service_enabled` | **(Premium)** URL to which authorization requests will be directed | | `external_authorization_service_url` | string | required by: `external_authorization_service_enabled` | **(Premium)** URL to which authorization requests will be directed |
| `file_template_project_id` | integer | no | **(Premium)** The ID of a project to load custom file templates from | | `file_template_project_id` | integer | no | **(Premium)** The ID of a project to load custom file templates from |
| `first_day_of_week` | integer | no | Start day of the week for calendar views and date pickers. Valid values are `0` (default) for Sunday and `1` for Monday. |
| `geo_status_timeout` | integer | no | **(Premium)** The amount of seconds after which a request to get a secondary node status will time out. | | `geo_status_timeout` | integer | no | **(Premium)** The amount of seconds after which a request to get a secondary node status will time out. |
| `gitaly_timeout_default` | integer | no | Default Gitaly timeout, in seconds. This timeout is not enforced for git fetch/push operations or Sidekiq jobs. Set to `0` to disable timeouts. | | `gitaly_timeout_default` | integer | no | Default Gitaly timeout, in seconds. This timeout is not enforced for git fetch/push operations or Sidekiq jobs. Set to `0` to disable timeouts. |
| `gitaly_timeout_fast` | integer | no | Gitaly fast operation timeout, in seconds. Some Gitaly operations are expected to be fast. If they exceed this threshold, there may be a problem with a storage shard and 'failing fast' can help maintain the stability of the GitLab instance. Set to `0` to disable timeouts. | | `gitaly_timeout_fast` | integer | no | Gitaly fast operation timeout, in seconds. Some Gitaly operations are expected to be fast. If they exceed this threshold, there may be a problem with a storage shard and 'failing fast' can help maintain the stability of the GitLab instance. Set to `0` to disable timeouts. |
......
...@@ -90,3 +90,11 @@ You can choose between 3 options: ...@@ -90,3 +90,11 @@ You can choose between 3 options:
- Files and Readme (default) - Files and Readme (default)
- Readme - Readme
- Activity - Activity
## Localization
### First day of the week
The first day of the week can be customised for calendar views and date pickers.
You can choose **Sunday** or **Monday** as the first day of the week. If you select **System Default**, the system-wide default setting will be used.
...@@ -24,6 +24,7 @@ module Gitlab ...@@ -24,6 +24,7 @@ module Gitlab
gon.emoji_sprites_css_path = ActionController::Base.helpers.stylesheet_path('emoji_sprites') gon.emoji_sprites_css_path = ActionController::Base.helpers.stylesheet_path('emoji_sprites')
gon.test_env = Rails.env.test? gon.test_env = Rails.env.test?
gon.suggested_label_colors = LabelsHelper.suggested_colors gon.suggested_label_colors = LabelsHelper.suggested_colors
gon.first_day_of_week = current_user&.first_day_of_week || Gitlab::CurrentSettings.first_day_of_week
if current_user if current_user
gon.current_user_id = current_user.id gon.current_user_id = current_user.id
......
...@@ -2901,6 +2901,9 @@ msgstr "" ...@@ -2901,6 +2901,9 @@ msgstr ""
msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import." msgid "Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import."
msgstr "" msgstr ""
msgid "Customize language and region related settings."
msgstr ""
msgid "Customize your merge request approval settings." msgid "Customize your merge request approval settings."
msgstr "" msgstr ""
...@@ -2973,6 +2976,12 @@ msgstr "" ...@@ -2973,6 +2976,12 @@ msgstr ""
msgid "Default classification label" msgid "Default classification label"
msgstr "" msgstr ""
msgid "Default first day of the week"
msgstr ""
msgid "Default first day of the week in calendars and date pickers."
msgstr ""
msgid "Default: Directly import the Google Code email address or username" msgid "Default: Directly import the Google Code email address or username"
msgstr "" msgstr ""
...@@ -4096,6 +4105,9 @@ msgstr "" ...@@ -4096,6 +4105,9 @@ msgstr ""
msgid "Finished" msgid "Finished"
msgstr "" msgstr ""
msgid "First day of the week"
msgstr ""
msgid "FirstPushedBy|First" msgid "FirstPushedBy|First"
msgstr "" msgstr ""
...@@ -5646,6 +5658,9 @@ msgstr "" ...@@ -5646,6 +5658,9 @@ msgstr ""
msgid "Loading…" msgid "Loading…"
msgstr "" msgstr ""
msgid "Localization"
msgstr ""
msgid "Lock" msgid "Lock"
msgstr "" msgstr ""
...@@ -6123,6 +6138,9 @@ msgstr "" ...@@ -6123,6 +6138,9 @@ msgstr ""
msgid "Modify merge commit" msgid "Modify merge commit"
msgstr "" msgstr ""
msgid "Monday"
msgstr ""
msgid "Monitor your errors by integrating with Sentry" msgid "Monitor your errors by integrating with Sentry"
msgstr "" msgstr ""
...@@ -9118,6 +9136,9 @@ msgstr "" ...@@ -9118,6 +9136,9 @@ msgstr ""
msgid "Suggested change" msgid "Suggested change"
msgstr "" msgstr ""
msgid "Sunday"
msgstr ""
msgid "Support for custom certificates is disabled. Ask your system's administrator to enable it." msgid "Support for custom certificates is disabled. Ask your system's administrator to enable it."
msgstr "" msgstr ""
...@@ -9133,6 +9154,9 @@ msgstr "" ...@@ -9133,6 +9154,9 @@ msgstr ""
msgid "System Info" msgid "System Info"
msgstr "" msgstr ""
msgid "System default (%{default})"
msgstr ""
msgid "System header and footer" msgid "System header and footer"
msgstr "" msgstr ""
...@@ -10349,6 +10373,9 @@ msgstr "" ...@@ -10349,6 +10373,9 @@ msgstr ""
msgid "Various email settings." msgid "Various email settings."
msgstr "" msgstr ""
msgid "Various localization settings."
msgstr ""
msgid "Various settings that affect GitLab performance." msgid "Various settings that affect GitLab performance."
msgstr "" msgstr ""
......
...@@ -42,7 +42,8 @@ describe Profiles::PreferencesController do ...@@ -42,7 +42,8 @@ describe Profiles::PreferencesController do
prefs = { prefs = {
color_scheme_id: '1', color_scheme_id: '1',
dashboard: 'stars', dashboard: 'stars',
theme_id: '2' theme_id: '2',
first_day_of_week: '1'
}.with_indifferent_access }.with_indifferent_access
expect(user).to receive(:assign_attributes).with(ActionController::Parameters.new(prefs).permit!) expect(user).to receive(:assign_attributes).with(ActionController::Parameters.new(prefs).permit!)
......
...@@ -35,6 +35,30 @@ describe PreferencesHelper do ...@@ -35,6 +35,30 @@ describe PreferencesHelper do
end end
end end
describe '#first_day_of_week_choices' do
it 'returns Sunday and Monday as choices' do
expect(helper.first_day_of_week_choices).to eq [
['Sunday', 0],
['Monday', 1]
]
end
end
describe '#first_day_of_week_choices_with_default' do
it 'returns choices including system default' do
expect(helper.first_day_of_week_choices_with_default).to eq [
['System default (Sunday)', nil], ['Sunday', 0], ['Monday', 1]
]
end
it 'returns choices including system default set to Monday' do
stub_application_setting(first_day_of_week: 1)
expect(helper.first_day_of_week_choices_with_default).to eq [
['System default (Monday)', nil], ['Sunday', 0], ['Monday', 1]
]
end
end
describe '#user_application_theme' do describe '#user_application_theme' do
context 'with a user' do context 'with a user' do
it "returns user's theme's css_class" do it "returns user's theme's css_class" do
......
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