Commit b4523ebb authored by Dmitriy Zaporozhets's avatar Dmitriy Zaporozhets

Merge branch 'ce-to-ee'

Signed-off-by: default avatarDmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>

Conflicts:
	app/views/profiles/accounts/show.html.haml
parents 00c3c4c5 a209e62e
Please view this file on the master branch, on stable branches it's out of date. Please view this file on the master branch, on stable branches it's out of date.
v 7.13.0 (unreleased)
- Support commenting on diffs in side-by-side mode (Stan Hu)
- Fix JavaScript error when clicking on the comment button on a diff line that has a comment already (Stan Hu)
- Remove project visibility icons from dashboard projects list
- Rename "Design" profile settings page to "Preferences".
- Allow users to customize their default Dashboard page.
- Update ssl_ciphers in Nginx example to remove DHE settings. This will deny forward secrecy for Android 2.3.7, Java 6 and OpenSSL 0.9.8
v 7.12.0 (unreleased) v 7.12.0 (unreleased)
- Fix post-receive errors on a push when an external issue tracker is configured (Stan Hu)
- Update oauth button logos for Twitter and Google to recommended assets - Update oauth button logos for Twitter and Google to recommended assets
- Fix hooks for web based events with external issue references (Daniel Gerhardt)
- Update browser gem to version 0.8.0 for IE11 support (Stan Hu) - Update browser gem to version 0.8.0 for IE11 support (Stan Hu)
- Fix timeout when rendering file with thousands of lines. - Fix timeout when rendering file with thousands of lines.
- Add "Remember me" checkbox to LDAP signin form. - Add "Remember me" checkbox to LDAP signin form.
- Add session expiration delay configuration through UI application settings
- Fix external issue tracker hook/test for HTTPS URLs (Daniel Gerhardt)
- Don't notify users mentioned in code blocks or blockquotes. - Don't notify users mentioned in code blocks or blockquotes.
- Omit link to generate labels if user does not have access to create them (Stan Hu) - Omit link to generate labels if user does not have access to create them (Stan Hu)
- Show warning when a comment will add 10 or more people to the discussion. - Show warning when a comment will add 10 or more people to the discussion.
......
source "https://rubygems.org" source "https://rubygems.org"
gem "rails", "~> 4.1.0" gem 'rails', '4.1.11'
# Default values for AR models # Default values for AR models
gem "default_value_for", "~> 3.0.0" gem "default_value_for", "~> 3.0.0"
...@@ -95,7 +95,7 @@ gem "seed-fu" ...@@ -95,7 +95,7 @@ gem "seed-fu"
gem 'html-pipeline', '~> 1.11.0' gem 'html-pipeline', '~> 1.11.0'
gem 'task_list', '1.0.2', require: 'task_list/railtie' gem 'task_list', '1.0.2', require: 'task_list/railtie'
gem 'github-markup' gem 'github-markup'
gem 'redcarpet', '~> 3.2.3' gem 'redcarpet', '~> 3.3.0'
gem 'RedCloth' gem 'RedCloth'
gem 'rdoc', '~>3.6' gem 'rdoc', '~>3.6'
gem 'org-ruby', '= 0.9.12' gem 'org-ruby', '= 0.9.12'
...@@ -194,7 +194,7 @@ gem 'font-awesome-rails', '~> 4.2' ...@@ -194,7 +194,7 @@ gem 'font-awesome-rails', '~> 4.2'
gem 'gitlab_emoji', '~> 0.1' gem 'gitlab_emoji', '~> 0.1'
gem 'gon', '~> 5.0.0' gem 'gon', '~> 5.0.0'
gem 'jquery-atwho-rails', '~> 1.0.0' gem 'jquery-atwho-rails', '~> 1.0.0'
gem 'jquery-rails', '3.1.2' gem 'jquery-rails', '3.1.3'
gem 'jquery-scrollto-rails' gem 'jquery-scrollto-rails'
gem 'jquery-ui-rails' gem 'jquery-ui-rails'
gem 'nprogress-rails' gem 'nprogress-rails'
......
...@@ -4,31 +4,31 @@ GEM ...@@ -4,31 +4,31 @@ GEM
CFPropertyList (2.3.1) CFPropertyList (2.3.1)
RedCloth (4.2.9) RedCloth (4.2.9)
ace-rails-ap (2.0.1) ace-rails-ap (2.0.1)
actionmailer (4.1.9) actionmailer (4.1.11)
actionpack (= 4.1.9) actionpack (= 4.1.11)
actionview (= 4.1.9) actionview (= 4.1.11)
mail (~> 2.5, >= 2.5.4) mail (~> 2.5, >= 2.5.4)
actionpack (4.1.9) actionpack (4.1.11)
actionview (= 4.1.9) actionview (= 4.1.11)
activesupport (= 4.1.9) activesupport (= 4.1.11)
rack (~> 1.5.2) rack (~> 1.5.2)
rack-test (~> 0.6.2) rack-test (~> 0.6.2)
actionview (4.1.9) actionview (4.1.11)
activesupport (= 4.1.9) activesupport (= 4.1.11)
builder (~> 3.1) builder (~> 3.1)
erubis (~> 2.7.0) erubis (~> 2.7.0)
activemodel (4.1.9) activemodel (4.1.11)
activesupport (= 4.1.9) activesupport (= 4.1.11)
builder (~> 3.1) builder (~> 3.1)
activerecord (4.1.9) activerecord (4.1.11)
activemodel (= 4.1.9) activemodel (= 4.1.11)
activesupport (= 4.1.9) activesupport (= 4.1.11)
arel (~> 5.0.0) arel (~> 5.0.0)
activeresource (4.0.0) activeresource (4.0.0)
activemodel (~> 4.0) activemodel (~> 4.0)
activesupport (~> 4.0) activesupport (~> 4.0)
rails-observers (~> 0.1.1) rails-observers (~> 0.1.1)
activesupport (4.1.9) activesupport (4.1.11)
i18n (~> 0.6, >= 0.6.9) i18n (~> 0.6, >= 0.6.9)
json (~> 1.7, >= 1.7.7) json (~> 1.7, >= 1.7.7)
minitest (~> 5.1) minitest (~> 5.1)
...@@ -332,7 +332,7 @@ GEM ...@@ -332,7 +332,7 @@ GEM
inflecto (0.0.2) inflecto (0.0.2)
ipaddress (0.8.0) ipaddress (0.8.0)
jquery-atwho-rails (1.0.1) jquery-atwho-rails (1.0.1)
jquery-rails (3.1.2) jquery-rails (3.1.3)
railties (>= 3.0, < 5.0) railties (>= 3.0, < 5.0)
thor (>= 0.14, < 2.0) thor (>= 0.14, < 2.0)
jquery-scrollto-rails (1.4.3) jquery-scrollto-rails (1.4.3)
...@@ -342,7 +342,7 @@ GEM ...@@ -342,7 +342,7 @@ GEM
turbolinks turbolinks
jquery-ui-rails (4.2.1) jquery-ui-rails (4.2.1)
railties (>= 3.2.16) railties (>= 3.2.16)
json (1.8.2) json (1.8.3)
jwt (0.1.13) jwt (0.1.13)
multi_json (>= 1.5) multi_json (>= 1.5)
kaminari (0.15.1) kaminari (0.15.1)
...@@ -367,7 +367,7 @@ GEM ...@@ -367,7 +367,7 @@ GEM
mini_portile (0.6.2) mini_portile (0.6.2)
minitest (5.3.5) minitest (5.3.5)
mousetrap-rails (1.4.6) mousetrap-rails (1.4.6)
multi_json (1.10.1) multi_json (1.11.1)
multi_xml (0.5.5) multi_xml (0.5.5)
multipart-post (1.2.0) multipart-post (1.2.0)
mysql2 (0.3.16) mysql2 (0.3.16)
...@@ -450,7 +450,7 @@ GEM ...@@ -450,7 +450,7 @@ GEM
quiet_assets (1.0.2) quiet_assets (1.0.2)
railties (>= 3.1, < 5.0) railties (>= 3.1, < 5.0)
racc (1.4.10) racc (1.4.10)
rack (1.5.3) rack (1.5.4)
rack-accept (0.4.5) rack-accept (0.4.5)
rack (>= 0.4) rack (>= 0.4)
rack-attack (4.3.0) rack-attack (4.3.0)
...@@ -470,21 +470,21 @@ GEM ...@@ -470,21 +470,21 @@ GEM
rack rack
rack-test (0.6.3) rack-test (0.6.3)
rack (>= 1.0) rack (>= 1.0)
rails (4.1.9) rails (4.1.11)
actionmailer (= 4.1.9) actionmailer (= 4.1.11)
actionpack (= 4.1.9) actionpack (= 4.1.11)
actionview (= 4.1.9) actionview (= 4.1.11)
activemodel (= 4.1.9) activemodel (= 4.1.11)
activerecord (= 4.1.9) activerecord (= 4.1.11)
activesupport (= 4.1.9) activesupport (= 4.1.11)
bundler (>= 1.3.0, < 2.0) bundler (>= 1.3.0, < 2.0)
railties (= 4.1.9) railties (= 4.1.11)
sprockets-rails (~> 2.0) sprockets-rails (~> 2.0)
rails-observers (0.1.2) rails-observers (0.1.2)
activemodel (~> 4.0) activemodel (~> 4.0)
railties (4.1.9) railties (4.1.11)
actionpack (= 4.1.9) actionpack (= 4.1.11)
activesupport (= 4.1.9) activesupport (= 4.1.11)
rake (>= 0.8.7) rake (>= 0.8.7)
thor (>= 0.18.1, < 2.0) thor (>= 0.18.1, < 2.0)
rainbow (2.0.0) rainbow (2.0.0)
...@@ -500,7 +500,7 @@ GEM ...@@ -500,7 +500,7 @@ GEM
trollop trollop
rdoc (3.12.2) rdoc (3.12.2)
json (~> 1.4) json (~> 1.4)
redcarpet (3.2.3) redcarpet (3.3.1)
redis (3.1.0) redis (3.1.0)
redis-actionpack (4.0.0) redis-actionpack (4.0.0)
actionpack (~> 4) actionpack (~> 4)
...@@ -634,7 +634,7 @@ GEM ...@@ -634,7 +634,7 @@ GEM
multi_json (~> 1.0) multi_json (~> 1.0)
rack (~> 1.0) rack (~> 1.0)
tilt (~> 1.1, != 1.3.0) tilt (~> 1.1, != 1.3.0)
sprockets-rails (2.2.4) sprockets-rails (2.3.1)
actionpack (>= 3.0) actionpack (>= 3.0)
activesupport (>= 3.0) activesupport (>= 3.0)
sprockets (>= 2.8, < 4.0) sprockets (>= 2.8, < 4.0)
...@@ -779,7 +779,7 @@ DEPENDENCIES ...@@ -779,7 +779,7 @@ DEPENDENCIES
html-pipeline (~> 1.11.0) html-pipeline (~> 1.11.0)
httparty httparty
jquery-atwho-rails (~> 1.0.0) jquery-atwho-rails (~> 1.0.0)
jquery-rails (= 3.1.2) jquery-rails (= 3.1.3)
jquery-scrollto-rails jquery-scrollto-rails
jquery-turbolinks jquery-turbolinks
jquery-ui-rails jquery-ui-rails
...@@ -810,10 +810,10 @@ DEPENDENCIES ...@@ -810,10 +810,10 @@ DEPENDENCIES
rack-cors rack-cors
rack-mini-profiler rack-mini-profiler
rack-oauth2 (~> 1.0.5) rack-oauth2 (~> 1.0.5)
rails (~> 4.1.0) rails (= 4.1.11)
raphael-rails (~> 2.1.2) raphael-rails (~> 2.1.2)
rdoc (~> 3.6) rdoc (~> 3.6)
redcarpet (~> 3.2.3) redcarpet (~> 3.3.0)
redis-rails redis-rails
request_store request_store
rerun (~> 0.10.0) rerun (~> 0.10.0)
......
...@@ -55,7 +55,7 @@ class Dispatcher ...@@ -55,7 +55,7 @@ class Dispatcher
when 'projects:merge_requests:index' when 'projects:merge_requests:index'
shortcut_handler = new ShortcutsNavigation() shortcut_handler = new ShortcutsNavigation()
MergeRequests.init() MergeRequests.init()
when 'dashboard:show' when 'dashboard:show', 'root:show'
new Dashboard() new Dashboard()
new Activities() new Activities()
when 'dashboard:projects:starred' when 'dashboard:projects:starred'
......
...@@ -8,11 +8,12 @@ ...@@ -8,11 +8,12 @@
class @Notes class @Notes
@interval: null @interval: null
constructor: (notes_url, note_ids, last_fetched_at) -> constructor: (notes_url, note_ids, last_fetched_at, view) ->
@notes_url = notes_url @notes_url = notes_url
@notes_url = gon.relative_url_root + @notes_url if gon.relative_url_root? @notes_url = gon.relative_url_root + @notes_url if gon.relative_url_root?
@note_ids = note_ids @note_ids = note_ids
@last_fetched_at = last_fetched_at @last_fetched_at = last_fetched_at
@view = view
@noteable_url = document.URL @noteable_url = document.URL
@initRefresh() @initRefresh()
@setupMainTargetNoteForm() @setupMainTargetNoteForm()
...@@ -131,6 +132,8 @@ class @Notes ...@@ -131,6 +132,8 @@ class @Notes
isNewNote: (note) -> isNewNote: (note) ->
$.inArray(note.id, @note_ids) == -1 $.inArray(note.id, @note_ids) == -1
isParallelView: ->
@view == 'parallel'
### ###
Render note in discussion area. Render note in discussion area.
...@@ -391,6 +394,7 @@ class @Notes ...@@ -391,6 +394,7 @@ class @Notes
setupDiscussionNoteForm: (dataHolder, form) => setupDiscussionNoteForm: (dataHolder, form) =>
# setup note target # setup note target
form.attr "rel", dataHolder.data("discussionId") form.attr "rel", dataHolder.data("discussionId")
form.find("#line_type").val dataHolder.data("lineType")
form.find("#note_commit_id").val dataHolder.data("commitId") form.find("#note_commit_id").val dataHolder.data("commitId")
form.find("#note_line_code").val dataHolder.data("lineCode") form.find("#note_line_code").val dataHolder.data("lineCode")
form.find("#note_noteable_type").val dataHolder.data("noteableType") form.find("#note_noteable_type").val dataHolder.data("noteableType")
...@@ -411,19 +415,40 @@ class @Notes ...@@ -411,19 +415,40 @@ class @Notes
form = $(".js-new-note-form") form = $(".js-new-note-form")
row = $(link).closest("tr") row = $(link).closest("tr")
nextRow = row.next() nextRow = row.next()
hasNotes = nextRow.is(".notes_holder")
# does it already have notes? addForm = false
if nextRow.is(".notes_holder") targetContent = ".notes_content"
replyButton = nextRow.find(".js-discussion-reply-button") rowCssToAdd = "<tr class=\"notes_holder js-temp-notes-holder\"><td class=\"notes_line\" colspan=\"2\"></td><td class=\"notes_content\"></td></tr>"
if replyButton.length > 0
$.proxy(@replyToDiscussionNote, replyButton).call() # In parallel view, look inside the correct left/right pane
if @isParallelView()
lineType = $(link).data("lineType")
targetContent += "." + lineType
rowCssToAdd = "<tr class=\"notes_holder js-temp-notes-holder\"><td class=\"notes_line\"></td><td class=\"notes_content parallel old\"></td><td class=\"notes_line\"></td><td class=\"notes_content parallel new\"></td></tr>"
if hasNotes
notesContent = nextRow.find(targetContent)
if notesContent.length
replyButton = notesContent.find(".js-discussion-reply-button:visible")
if replyButton.length
e.target = replyButton[0]
$.proxy(@replyToDiscussionNote, replyButton[0], e).call()
else
# In parallel view, the form may not be present in one of the panes
noteForm = notesContent.find(".js-discussion-note-form")
if noteForm.length == 0
addForm = true
else else
# add a notes row and insert the form # add a notes row and insert the form
row.after "<tr class=\"notes_holder js-temp-notes-holder\"><td class=\"notes_line\" colspan=\"2\"></td><td class=\"notes_content\"></td></tr>" row.after rowCssToAdd
form.clone().appendTo row.next().find(".notes_content") addForm = true
if addForm
newForm = form.clone()
newForm.appendTo row.next().find(targetContent)
# show the form # show the form
@setupDiscussionNoteForm $(link), row.next().find("form") @setupDiscussionNoteForm $(link), newForm
### ###
Called in response to "cancel" on a diff note form. Called in response to "cancel" on a diff note form.
......
class @Profile class @Profile
constructor: -> constructor: ->
$('.edit_user .application-theme input, .edit_user .code-preview-theme input').click -> # Automatically submit the Preferences form when any of its radio buttons change
# Submit the form $('.js-preferences-form').on 'change.preference', 'input[type=radio]', ->
$('.edit_user').submit() $(this).parents('form').submit()
new Flash("Appearance settings saved", "notice")
$('.update-username form').on 'ajax:before', -> $('.update-username form').on 'ajax:before', ->
$('.loading-gif').show() $('.loading-gif').show()
...@@ -18,7 +16,6 @@ class @Profile ...@@ -18,7 +16,6 @@ class @Profile
$('.update-notifications').on 'ajax:complete', -> $('.update-notifications').on 'ajax:complete', ->
$(this).find('.btn-save').enable() $(this).find('.btn-save').enable()
$('.js-choose-user-avatar-button').bind "click", -> $('.js-choose-user-avatar-button').bind "click", ->
form = $(this).closest("form") form = $(this).closest("form")
form.find(".js-user-avatar-input").click() form.find(".js-user-avatar-input").click()
......
...@@ -35,26 +35,26 @@ ...@@ -35,26 +35,26 @@
*/ */
@import "font-awesome"; @import "font-awesome";
/**
* UI themes:
*/
@import "themes/**/*";
/** /**
* Generic css (forms, nav etc): * Generic css (forms, nav etc):
*/ */
@import "generic/*"; @import "generic/**/*";
/** /**
* Page specific styles (issues, projects etc): * Page specific styles (issues, projects etc):
*/ */
@import "pages/*"; @import "pages/**/*";
/** /**
* Code highlight * Code highlight
*/ */
@import "highlight/*"; @import "highlight/**/*";
/**
* UI themes:
*/
@import "themes/*";
/** /**
* Styles for JS behaviors. * Styles for JS behaviors.
......
...@@ -98,16 +98,16 @@ header { ...@@ -98,16 +98,16 @@ header {
height: $header-height; height: $header-height;
.title { .title {
position: relative;
float: left;
margin: 0; margin: 0;
margin-left: 35px; padding: 0 15px 0 35px;
overflow: hidden;
font-size: 18px; font-size: 18px;
line-height: $header-height; line-height: $header-height;
font-weight: bold; font-weight: bold;
color: #444; color: #444;
text-overflow: ellipsis;
@include str-truncated(37%); vertical-align: top;
white-space: nowrap;
a { a {
color: #444; color: #444;
...@@ -116,6 +116,10 @@ header { ...@@ -116,6 +116,10 @@ header {
} }
} }
} }
.navbar-collapse {
float: right;
}
} }
.search { .search {
...@@ -167,10 +171,6 @@ header { ...@@ -167,10 +171,6 @@ header {
} }
@media (max-width: $screen-md-max) { @media (max-width: $screen-md-max) {
header .container .title {
max-width: 43%;
}
.header-collapsed, .header-expanded { .header-collapsed, .header-expanded {
@include collapsed-header; @include collapsed-header;
} }
...@@ -190,7 +190,6 @@ header { ...@@ -190,7 +190,6 @@ header {
font-size: 18px; font-size: 18px;
.title { .title {
max-width: 70%;
} }
.navbar-nav { .navbar-nav {
......
...@@ -61,8 +61,7 @@ ...@@ -61,8 +61,7 @@
} }
.container .title { .container .title {
margin-left: 15px !important; padding-left: 15px !important;
max-width: 70% !important;
} }
.issue-info, .merge-request-info { .issue-info, .merge-request-info {
......
...@@ -28,10 +28,6 @@ ...@@ -28,10 +28,6 @@
font-size: 14px; font-size: 14px;
line-height: 24px; line-height: 24px;
.str-truncated {
max-width: 76%;
}
a { a {
display: block; display: block;
padding: 8px 15px; padding: 8px 15px;
......
...@@ -17,67 +17,6 @@ ...@@ -17,67 +17,6 @@
} }
} }
/*
* Appearance settings
*
*/
.themes_opts {
label {
margin-right: 20px;
text-align: center;
.prev {
height: 80px;
width: 160px;
margin-bottom: 10px;
@include border-radius(4px);
&.classic {
background: #31363e;
}
&.default {
background: #888888;
}
&.modern {
background: #009871;
}
&.gray {
background: #373737;
}
&.violet {
background: #548;
}
&.blue {
background: #2980b9;
}
}
}
}
.code_highlight_opts {
margin-top: 10px;
label {
margin-right: 20px;
text-align: center;
.prev {
width: 160px;
margin-bottom: 10px;
img {
max-width: 100%;
@include border-radius(4px);
}
}
}
}
.oauth-buttons { .oauth-buttons {
.btn-group { .btn-group {
margin-right: 10px; margin-right: 10px;
......
.application-theme {
label {
margin-right: 20px;
text-align: center;
.preview {
@include border-radius(4px);
height: 80px;
margin-bottom: 10px;
width: 160px;
&.ui_blue {
background: $theme-blue;
}
&.ui_charcoal {
background: $theme-charcoal;
}
&.ui_graphite {
background: $theme-graphite;
}
&.ui_gray {
background: $theme-gray;
}
&.ui_green {
background: $theme-green;
}
&.ui_violet {
background: $theme-violet;
}
}
}
}
.syntax-theme {
label {
margin-right: 20px;
text-align: center;
.preview {
margin-bottom: 10px;
width: 160px;
img {
@include border-radius(4px);
max-width: 100%;
}
}
}
}
/**
* Styles the GitLab application with a specific color theme
*
* $color-light -
* $color -
* $color-darker -
* $color-dark -
*/
@mixin gitlab-theme($color-light, $color, $color-darker, $color-dark) { @mixin gitlab-theme($color-light, $color, $color-darker, $color-dark) {
header { header {
&.navbar-gitlab { &.navbar-gitlab {
...@@ -77,3 +85,36 @@ ...@@ -77,3 +85,36 @@
} }
} }
} }
$theme-blue: #2980B9;
$theme-charcoal: #474D57;
$theme-graphite: #888888;
$theme-gray: #373737;
$theme-green: #019875;
$theme-violet: #554488;
body {
&.ui_blue {
@include gitlab-theme(#BECDE9, $theme-blue, #1970A9, #096099);
}
&.ui_charcoal {
@include gitlab-theme(#979DA7, $theme-charcoal, #373D47, #24272D);
}
&.ui_graphite {
@include gitlab-theme(#CCCCCC, $theme-graphite, #777777, #666666);
}
&.ui_gray {
@include gitlab-theme(#979797, $theme-gray, #272727, #222222);
}
&.ui_green {
@include gitlab-theme(#AADDCC, $theme-green, #018865, #017855);
}
&.ui_violet {
@include gitlab-theme(#9988CC, $theme-violet, #443366, #332255);
}
}
/**
* This file represent some UI that can be changed
* during web app restyle or theme select.
*
*/
.ui_basic {
@include gitlab-theme(#CCCCCC, #888888, #777777, #666666);
}
/**
* Blue GitLab UI theme
*/
.ui_blue {
@include gitlab-theme(#BECDE9, #2980b9, #1970a9, #096099);
}
/**
* Violet GitLab UI theme
*/
.ui_color {
@include gitlab-theme(#98C, #548, #436, #325);
}
/**
* Gray GitLab UI theme
*/
.ui_gray {
@include gitlab-theme(#979797, #373737, #272727, #222222);
}
/**
* Classic GitLab UI theme
*/
.ui_mars {
@include gitlab-theme(#979DA7, #474D57, #373D47, #24272D);
}
/**
* Modern GitLab UI theme
*/
.ui_modern {
@include gitlab-theme(#ADC, #019875, #018865, #017855);
}
...@@ -41,6 +41,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController ...@@ -41,6 +41,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
:help_text, :help_text,
:after_sign_out_path, :after_sign_out_path,
:max_attachment_size, :max_attachment_size,
:session_expire_delay,
:default_project_visibility, :default_project_visibility,
:default_snippet_visibility, :default_snippet_visibility,
:restricted_signup_domains_raw, :restricted_signup_domains_raw,
......
class Profiles::PreferencesController < Profiles::ApplicationController
before_action :user
def show
end
def update
begin
if @user.update_attributes(preferences_params)
flash[:notice] = 'Preferences saved.'
else
flash[:alert] = 'Failed to save preferences.'
end
rescue ArgumentError => e
# Raised when `dashboard` is given an invalid value.
flash[:alert] = "Failed to save preferences (#{e.message})."
end
respond_to do |format|
format.html { redirect_to profile_preferences_path }
format.js
end
end
private
def user
@user = current_user
end
def preferences_params
params.require(:user).permit(
:color_scheme_id,
:dashboard,
:theme_id
)
end
end
...@@ -8,9 +8,6 @@ class ProfilesController < Profiles::ApplicationController ...@@ -8,9 +8,6 @@ class ProfilesController < Profiles::ApplicationController
def show def show
end end
def design
end
def applications def applications
@applications = current_user.oauth_applications @applications = current_user.oauth_applications
@authorized_tokens = current_user.oauth_authorized_tokens @authorized_tokens = current_user.oauth_authorized_tokens
...@@ -29,7 +26,6 @@ class ProfilesController < Profiles::ApplicationController ...@@ -29,7 +26,6 @@ class ProfilesController < Profiles::ApplicationController
respond_to do |format| respond_to do |format|
format.html { redirect_to :back } format.html { redirect_to :back }
format.js
end end
end end
...@@ -65,10 +61,21 @@ class ProfilesController < Profiles::ApplicationController ...@@ -65,10 +61,21 @@ class ProfilesController < Profiles::ApplicationController
def user_params def user_params
params.require(:user).permit( params.require(:user).permit(
:email, :password, :password_confirmation, :bio, :name, :avatar,
:username, :skype, :linkedin, :twitter, :website_url, :bio,
:color_scheme_id, :theme_id, :avatar, :hide_no_ssh_key, :email,
:hide_no_password, :location, :public_email :hide_no_password,
:hide_no_ssh_key,
:linkedin,
:location,
:name,
:password,
:password_confirmation,
:public_email,
:skype,
:twitter,
:username,
:website_url
) )
end end
end end
...@@ -77,11 +77,24 @@ class Projects::NotesController < Projects::ApplicationController ...@@ -77,11 +77,24 @@ class Projects::NotesController < Projects::ApplicationController
end end
def note_to_discussion_html(note) def note_to_discussion_html(note)
if params[:view] == 'parallel'
template = "projects/notes/_diff_notes_with_reply_parallel"
locals =
if params[:line_type] == 'old'
{ notes_left: [note], notes_right: [] }
else
{ notes_left: [], notes_right: [note] }
end
else
template = "projects/notes/_diff_notes_with_reply"
locals = { notes: [note] }
end
render_to_string( render_to_string(
"projects/notes/_diff_notes_with_reply", template,
layout: false, layout: false,
formats: [:html], formats: [:html],
locals: { notes: [note] } locals: locals
) )
end end
......
# RootController
#
# This controller exists solely to handle requests to `root_url`. When a user is
# logged in and has customized their `dashboard` setting, they will be
# redirected to their preferred location.
#
# For users who haven't customized the setting, we simply delegate to
# `DashboardController#show`, which is the default.
class RootController < DashboardController
before_action :redirect_to_custom_dashboard, only: [:show]
def show
super
end
private
def redirect_to_custom_dashboard
return unless current_user
case current_user.dashboard
when 'stars'
redirect_to starred_dashboard_projects_path
else
return
end
end
end
...@@ -2,26 +2,6 @@ require 'digest/md5' ...@@ -2,26 +2,6 @@ require 'digest/md5'
require 'uri' require 'uri'
module ApplicationHelper module ApplicationHelper
COLOR_SCHEMES = {
1 => 'white',
2 => 'dark',
3 => 'solarized-light',
4 => 'solarized-dark',
5 => 'monokai',
}
COLOR_SCHEMES.default = 'white'
# Helper method to access the COLOR_SCHEMES
#
# The keys are the `color_scheme_ids`
# The values are the `name` of the scheme.
#
# The preview images are `name-scheme-preview.png`
# The stylesheets should use the css class `.name`
def color_schemes
COLOR_SCHEMES.freeze
end
# Check if a particular controller is the current one # Check if a particular controller is the current one
# #
# args - One or more controller names to check # args - One or more controller names to check
...@@ -138,18 +118,6 @@ module ApplicationHelper ...@@ -138,18 +118,6 @@ module ApplicationHelper
Emoji.names.to_s Emoji.names.to_s
end end
def app_theme
Gitlab::Theme.css_class_by_id(current_user.try(:theme_id))
end
def theme_type
Gitlab::Theme.type_css_class_by_id(current_user.try(:theme_id))
end
def user_color_scheme_class
COLOR_SCHEMES[current_user.try(:color_scheme_id)] if defined?(current_user)
end
# Define whenever show last push event # Define whenever show last push event
# with suggestion to create MR # with suggestion to create MR
def show_last_push_widget?(event) def show_last_push_widget?(event)
......
...@@ -2,6 +2,7 @@ require 'nokogiri' ...@@ -2,6 +2,7 @@ require 'nokogiri'
module GitlabMarkdownHelper module GitlabMarkdownHelper
include Gitlab::Markdown include Gitlab::Markdown
include PreferencesHelper
# Use this in places where you would normally use link_to(gfm(...), ...). # Use this in places where you would normally use link_to(gfm(...), ...).
# #
......
...@@ -47,7 +47,7 @@ module NotesHelper ...@@ -47,7 +47,7 @@ module NotesHelper
}.to_json }.to_json
end end
def link_to_new_diff_note(line_code) def link_to_new_diff_note(line_code, line_type = nil)
discussion_id = Note.build_discussion_id( discussion_id = Note.build_discussion_id(
@comments_target[:noteable_type], @comments_target[:noteable_type],
@comments_target[:noteable_id] || @comments_target[:commit_id], @comments_target[:noteable_id] || @comments_target[:commit_id],
...@@ -59,7 +59,8 @@ module NotesHelper ...@@ -59,7 +59,8 @@ module NotesHelper
noteable_id: @comments_target[:noteable_id], noteable_id: @comments_target[:noteable_id],
commit_id: @comments_target[:commit_id], commit_id: @comments_target[:commit_id],
line_code: line_code, line_code: line_code,
discussion_id: discussion_id discussion_id: discussion_id,
line_type: line_type
} }
button_tag(class: 'btn add-diff-note js-add-diff-note-button', button_tag(class: 'btn add-diff-note js-add-diff-note-button',
...@@ -69,7 +70,7 @@ module NotesHelper ...@@ -69,7 +70,7 @@ module NotesHelper
end end
end end
def link_to_reply_diff(note) def link_to_reply_diff(note, line_type = nil)
return unless current_user return unless current_user
data = { data = {
...@@ -77,7 +78,8 @@ module NotesHelper ...@@ -77,7 +78,8 @@ module NotesHelper
noteable_id: note.noteable_id, noteable_id: note.noteable_id,
commit_id: note.commit_id, commit_id: note.commit_id,
line_code: note.line_code, line_code: note.line_code,
discussion_id: note.discussion_id discussion_id: note.discussion_id,
line_type: line_type
} }
button_tag class: 'btn reply-btn js-discussion-reply-button', button_tag class: 'btn reply-btn js-discussion-reply-button',
......
# Helper methods for per-User preferences
module PreferencesHelper
COLOR_SCHEMES = {
1 => 'white',
2 => 'dark',
3 => 'solarized-light',
4 => 'solarized-dark',
5 => 'monokai',
}
COLOR_SCHEMES.default = 'white'
# Helper method to access the COLOR_SCHEMES
#
# The keys are the `color_scheme_ids`
# The values are the `name` of the scheme.
#
# The preview images are `name-scheme-preview.png`
# The stylesheets should use the css class `.name`
def color_schemes
COLOR_SCHEMES.freeze
end
# Maps `dashboard` values to more user-friendly option text
DASHBOARD_CHOICES = {
projects: 'Your Projects (default)',
stars: 'Starred Projects'
}.with_indifferent_access.freeze
# Returns an Array usable by a select field for more user-friendly option text
def dashboard_choices
defined = User.dashboards
if defined.size != DASHBOARD_CHOICES.size
# Ensure that anyone adding new options updates this method too
raise RuntimeError, "`User` defines #{defined.size} dashboard choices," +
" but `DASHBOARD_CHOICES` defined #{DASHBOARD_CHOICES.size}."
else
defined.map do |key, _|
# Use `fetch` so `KeyError` gets raised when a key is missing
[DASHBOARD_CHOICES.fetch(key), key]
end
end
end
def user_application_theme
theme = Gitlab::Themes.by_id(current_user.try(:theme_id))
theme.css_class
end
def user_color_scheme_class
COLOR_SCHEMES[current_user.try(:color_scheme_id)] if defined?(current_user)
end
end
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
# help_text :text # help_text :text
# restricted_visibility_levels :text # restricted_visibility_levels :text
# max_attachment_size :integer default(10), not null # max_attachment_size :integer default(10), not null
# session_expire_delay :integer default(10080), not null
# default_project_visibility :integer # default_project_visibility :integer
# default_snippet_visibility :integer # default_snippet_visibility :integer
# restricted_signup_domains :text # restricted_signup_domains :text
...@@ -28,6 +29,10 @@ class ApplicationSetting < ActiveRecord::Base ...@@ -28,6 +29,10 @@ class ApplicationSetting < ActiveRecord::Base
serialize :restricted_signup_domains, Array serialize :restricted_signup_domains, Array
attr_accessor :restricted_signup_domains_raw attr_accessor :restricted_signup_domains_raw
validates :session_expire_delay,
presence: true,
numericality: { only_integer: true, greater_than_or_equal_to: 0 }
validates :home_page_url, validates :home_page_url,
allow_blank: true, allow_blank: true,
format: { with: /\A#{URI.regexp(%w(http https))}\z/, message: "should be a valid url" }, format: { with: /\A#{URI.regexp(%w(http https))}\z/, message: "should be a valid url" },
...@@ -62,6 +67,7 @@ class ApplicationSetting < ActiveRecord::Base ...@@ -62,6 +67,7 @@ class ApplicationSetting < ActiveRecord::Base
sign_in_text: Settings.extra['sign_in_text'], sign_in_text: Settings.extra['sign_in_text'],
restricted_visibility_levels: Settings.gitlab['restricted_visibility_levels'], restricted_visibility_levels: Settings.gitlab['restricted_visibility_levels'],
max_attachment_size: Settings.gitlab['max_attachment_size'], max_attachment_size: Settings.gitlab['max_attachment_size'],
session_expire_delay: Settings.gitlab['session_expire_delay'],
default_project_visibility: Settings.gitlab.default_projects_features['visibility_level'], default_project_visibility: Settings.gitlab.default_projects_features['visibility_level'],
default_snippet_visibility: Settings.gitlab.default_projects_features['visibility_level'], default_snippet_visibility: Settings.gitlab.default_projects_features['visibility_level'],
restricted_signup_domains: Settings.gitlab['restricted_signup_domains'] restricted_signup_domains: Settings.gitlab['restricted_signup_domains']
......
...@@ -31,7 +31,7 @@ class Note < ActiveRecord::Base ...@@ -31,7 +31,7 @@ class Note < ActiveRecord::Base
participant :author, :mentioned_users participant :author, :mentioned_users
belongs_to :project belongs_to :project
belongs_to :noteable, polymorphic: true belongs_to :noteable, polymorphic: true, touch: true
belongs_to :author, class_name: "User" belongs_to :author, class_name: "User"
delegate :name, to: :project, prefix: true delegate :name, to: :project, prefix: true
......
...@@ -752,4 +752,12 @@ class Project < ActiveRecord::Base ...@@ -752,4 +752,12 @@ class Project < ActiveRecord::Base
errors.add(:base, 'Failed create wiki') errors.add(:base, 'Failed create wiki')
false false
end end
def reference_issue_tracker?
default_issues_tracker? || jira_tracker_active?
end
def jira_tracker_active?
jira_tracker? && jira_service.active
end
end end
...@@ -40,10 +40,14 @@ class GitlabCiService < CiService ...@@ -40,10 +40,14 @@ class GitlabCiService < CiService
def execute(data) def execute(data)
return unless supported_events.include?(data[:object_kind]) return unless supported_events.include?(data[:object_kind])
ci_yaml_file = ci_yaml_file(data) sha = data[:checkout_sha]
if ci_yaml_file if sha.present?
data.merge!(ci_yaml_file: ci_yaml_file) file = ci_yaml_file(sha)
if file && file.data
data.merge!(ci_yaml_file: file.data)
end
end end
service_hook.execute(data) service_hook.execute(data)
...@@ -129,15 +133,15 @@ class GitlabCiService < CiService ...@@ -129,15 +133,15 @@ class GitlabCiService < CiService
private private
def ci_yaml_file(data) def ci_yaml_file(sha)
ref = data[:checkout_sha] repository.blob_at(sha, '.gitlab-ci.yml')
repo = project.repository
commit = repo.commit(ref)
blob = Gitlab::Git::Blob.find(repo, commit.id, ".gitlab-ci.yml")
blob && blob.data
end end
def fork_registration_path def fork_registration_path
project_url.sub(/projects\/\d*/, "#{API_PREFIX}/forks") project_url.sub(/projects\/\d*/, "#{API_PREFIX}/forks")
end end
def repository
project.repository
end
end end
...@@ -85,18 +85,13 @@ class IssueTrackerService < Service ...@@ -85,18 +85,13 @@ class IssueTrackerService < Service
result = false result = false
begin begin
url = URI.parse(self.project_url) response = HTTParty.head(self.project_url, verify: true)
if url.host && url.port if response
http = Net::HTTP.start(url.host, url.port, { open_timeout: 5, read_timeout: 5 }) message = "#{self.type} received response #{response.code} when attempting to connect to #{self.project_url}"
response = http.head("/") result = true
if response
message = "#{self.type} received response #{response.code} when attempting to connect to #{self.project_url}"
result = true
end
end end
rescue Timeout::Error, SocketError, Errno::ECONNRESET, Errno::ECONNREFUSED => error rescue HTTParty::Error, Timeout::Error, SocketError, Errno::ECONNRESET, Errno::ECONNREFUSED => error
message = "#{self.type} had an error when trying to connect to #{self.project_url}: #{error.message}" message = "#{self.type} had an error when trying to connect to #{self.project_url}: #{error.message}"
end end
Rails.logger.info(message) Rails.logger.info(message)
......
...@@ -50,12 +50,13 @@ ...@@ -50,12 +50,13 @@
# bitbucket_access_token :string(255) # bitbucket_access_token :string(255)
# bitbucket_access_token_secret :string(255) # bitbucket_access_token_secret :string(255)
# location :string(255) # location :string(255)
# public_email :string(255) default(""), not null
# encrypted_otp_secret :string(255) # encrypted_otp_secret :string(255)
# encrypted_otp_secret_iv :string(255) # encrypted_otp_secret_iv :string(255)
# encrypted_otp_secret_salt :string(255) # encrypted_otp_secret_salt :string(255)
# otp_required_for_login :boolean # otp_required_for_login :boolean
# otp_backup_codes :text # otp_backup_codes :text
# public_email :string(255) default(""), not null # dashboard :integer default(0)
# #
require 'carrierwave/orm/activerecord' require 'carrierwave/orm/activerecord'
...@@ -734,4 +735,8 @@ class User < ActiveRecord::Base ...@@ -734,4 +735,8 @@ class User < ActiveRecord::Base
def can_be_removed? def can_be_removed?
!solo_owned_groups.present? !solo_owned_groups.present?
end end
# User's Dashboard preference
# Note: When adding an option, it MUST go on the end of the array.
enum dashboard: [:projects, :stars]
end end
...@@ -92,18 +92,24 @@ class GitPushService ...@@ -92,18 +92,24 @@ class GitPushService
end end
end end
# Create cross-reference notes for any other references. Omit any issues that were referenced in an if project.reference_issue_tracker?
# issue-closing phrase, or have already been mentioned from this commit (probably from this commit create_cross_reference_notes(commit, issues_to_close)
# being pushed to a different branch). end
refs = commit.references(project, user) - issues_to_close end
refs.reject! { |r| commit.has_mentioned?(r) } end
if refs.present? def create_cross_reference_notes(commit, issues_to_close)
author ||= commit_user(commit) # Create cross-reference notes for any other references. Omit any issues that were referenced in an
# issue-closing phrase, or have already been mentioned from this commit (probably from this commit
# being pushed to a different branch).
refs = commit.references(project, user) - issues_to_close
refs.reject! { |r| commit.has_mentioned?(r) }
refs.each do |r| if refs.present?
Note.create_cross_reference_note(r, commit, author) author ||= commit_user(commit)
end
refs.each do |r|
Note.create_cross_reference_note(r, commit, author)
end end
end end
end end
......
module Issues module Issues
class CloseService < Issues::BaseService class CloseService < Issues::BaseService
def execute(issue, commit = nil) def execute(issue, commit = nil)
if issue.close if project.default_issues_tracker? && issue.close
event_service.close_issue(issue, current_user) event_service.close_issue(issue, current_user)
create_note(issue, commit) create_note(issue, commit)
notification_service.close_issue(issue, current_user) notification_service.close_issue(issue, current_user)
......
...@@ -216,13 +216,15 @@ class SystemNoteService ...@@ -216,13 +216,15 @@ class SystemNoteService
# Check if a cross-reference is disallowed # Check if a cross-reference is disallowed
# #
# This method prevents adding a "mentioned in !1" note on every single commit # This method prevents adding a "mentioned in !1" note on every single commit
# in a merge request. # in a merge request. Additionally, it prevents the creation of references to
# external issues (which would fail).
# #
# noteable - Noteable object being referenced # noteable - Noteable object being referenced
# mentioner - Mentionable object # mentioner - Mentionable object
# #
# Returns Boolean # Returns Boolean
def self.cross_reference_disallowed?(noteable, mentioner) def self.cross_reference_disallowed?(noteable, mentioner)
return true if noteable.is_a?(ExternalIssue) && !noteable.project.jira_tracker_active?
return false unless mentioner.is_a?(MergeRequest) return false unless mentioner.is_a?(MergeRequest)
return false unless noteable.is_a?(Commit) return false unless noteable.is_a?(Commit)
......
...@@ -88,6 +88,11 @@ ...@@ -88,6 +88,11 @@
= f.label :max_attachment_size, 'Maximum attachment size (MB)', class: 'control-label col-sm-2' = f.label :max_attachment_size, 'Maximum attachment size (MB)', class: 'control-label col-sm-2'
.col-sm-10 .col-sm-10
= f.number_field :max_attachment_size, class: 'form-control', min: 0 = f.number_field :max_attachment_size, class: 'form-control', min: 0
.form-group
= f.label :session_expire_delay, 'Session duration (minutes)', class: 'control-label col-sm-2'
.col-sm-10
= f.number_field :session_expire_delay, class: 'form-control'
%span.help-block#session_expire_delay_help_block GitLab restart is required to apply changes
.form-group .form-group
= f.label :restricted_signup_domains, 'Restricted domains for sign-ups', class: 'control-label col-sm-2' = f.label :restricted_signup_domains, 'Restricted domains for sign-ups', class: 'control-label col-sm-2'
.col-sm-10 .col-sm-10
......
...@@ -12,8 +12,7 @@ ...@@ -12,8 +12,7 @@
- if @group.new_record? - if @group.new_record?
.form-group .form-group
.col-sm-2 .col-sm-offset-2.col-sm-10
.col-sm-10
.alert.alert-info .alert.alert-info
= render 'shared/group_tips' = render 'shared/group_tips'
.form-actions .form-actions
......
...@@ -92,8 +92,7 @@ ...@@ -92,8 +92,7 @@
= namespace_select_tag :new_namespace_id, selected: params[:namespace_id], class: 'input-large' = namespace_select_tag :new_namespace_id, selected: params[:namespace_id], class: 'input-large'
.form-group .form-group
.col-sm-2 .col-sm-offset-2.col-sm-10
.col-sm-10
= f.submit 'Transfer', class: 'btn btn-primary' = f.submit 'Transfer', class: 'btn btn-primary'
.col-md-6 .col-md-6
......
= form_for application, url: doorkeeper_submit_path(application), html: {class: 'form-horizontal', role: 'form'} do |f| = form_for application, url: doorkeeper_submit_path(application), html: {class: 'form-horizontal', role: 'form'} do |f|
- if application.errors.any? - if application.errors.any?
.alert.alert-danger{"data-alert" => ""} .alert.alert-danger
%p Whoops! Check your form for possible errors %ul
= content_tag :div, class: "form-group#{' has-error' if application.errors[:name].present?}" do - application.errors.full_messages.each do |msg|
= f.label :name, class: 'col-sm-2 control-label' %li= msg
.form-group
= f.label :name, class: 'control-label'
.col-sm-10 .col-sm-10
= f.text_field :name, class: 'form-control' = f.text_field :name, class: 'form-control', required: true
= doorkeeper_errors_for application, :name
= content_tag :div, class: "form-group#{' has-error' if application.errors[:redirect_uri].present?}" do .form-group
= f.label :redirect_uri, class: 'col-sm-2 control-label' = f.label :redirect_uri, class: 'control-label'
.col-sm-10 .col-sm-10
= f.text_area :redirect_uri, class: 'form-control' = f.text_area :redirect_uri, class: 'form-control', required: true
= doorkeeper_errors_for application, :redirect_uri
%span.help-block %span.help-block
Use one line per URI Use one line per URI
- if Doorkeeper.configuration.native_redirect_uri - if Doorkeeper.configuration.native_redirect_uri
...@@ -19,6 +24,7 @@ ...@@ -19,6 +24,7 @@
Use Use
%code= Doorkeeper.configuration.native_redirect_uri %code= Doorkeeper.configuration.native_redirect_uri
for local tests for local tests
.form-actions .form-actions
= f.submit 'Submit', class: "btn btn-primary wide" = f.submit 'Submit', class: "btn btn-create"
= link_to "Cancel", applications_profile_path, class: "btn btn-default" = link_to "Cancel", applications_profile_path, class: "btn btn-cancel"
%h3.page-title New application - page_title "New Application"
%h3.page-title New Application
%hr
= render 'form', application: @application = render 'form', application: @application
\ No newline at end of file
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
%span You pushed to %span You pushed to
= link_to namespace_project_commits_path(event.project.namespace, event.project, event.ref_name) do = link_to namespace_project_commits_path(event.project.namespace, event.project, event.ref_name) do
%strong= event.ref_name %strong= event.ref_name
at %span at
%strong= link_to_project event.project %strong= link_to_project event.project
#{time_ago_with_tooltip(event.created_at)} #{time_ago_with_tooltip(event.created_at)}
......
...@@ -13,6 +13,3 @@ ...@@ -13,6 +13,3 @@
.public-projects .public-projects
%ul.bordered-list %ul.bordered-list
= render @trending_projects = render @trending_projects
.center.append-bottom-20
= link_to 'Show all projects', explore_projects_path, class: 'btn btn-primary'
...@@ -11,8 +11,7 @@ ...@@ -11,8 +11,7 @@
= render 'shared/group_form', f: f = render 'shared/group_form', f: f
.form-group .form-group
.col-sm-2 .col-sm-offset-2.col-sm-10
.col-sm-10
= image_tag group_icon(@group), alt: '', class: 'avatar group-avatar s160' = image_tag group_icon(@group), alt: '', class: 'avatar group-avatar s160'
%p.light %p.light
- if @group.avatar? - if @group.avatar?
......
...@@ -13,8 +13,7 @@ ...@@ -13,8 +13,7 @@
= render 'shared/choose_group_avatar_button', f: f = render 'shared/choose_group_avatar_button', f: f
.form-group .form-group
.col-sm-2 .col-sm-offset-2.col-sm-10
.col-sm-10
= render 'shared/group_tips' = render 'shared/group_tips'
.form-actions .form-actions
......
#modal-shortcuts.modal.hide{tabindex: -1} #modal-shortcuts.modal{tabindex: -1}
.modal-dialog .modal-dialog
.modal-content .modal-content
.modal-header .modal-header
......
!!! 5 !!! 5
%html{ lang: "en"} %html{ lang: "en"}
= render "layouts/head" = render "layouts/head"
%body{class: "#{app_theme}", :'data-page' => body_data_page} %body{class: "#{user_application_theme}", 'data-page' => body_data_page}
/ Ideally this would be inside the head, but turbolinks only evaluates page-specific JS in the body. -# Ideally this would be inside the head, but turbolinks only evaluates page-specific JS in the body.
= yield :scripts_body_top = yield :scripts_body_top
- if current_user - if current_user
= render "layouts/header/default", title: header_title = render "layouts/header/default", title: header_title
- else - else
......
!!! 5 !!! 5
%html{ lang: "en"} %html{ lang: "en"}
= render "layouts/head" = render "layouts/head"
%body.ui_mars.login-page.application %body.ui_charcoal.login-page.application
= render "layouts/header/empty" = render "layouts/header/empty"
= render "layouts/broadcast" = render "layouts/broadcast"
.container.navless-container .container.navless-container
......
!!! 5 !!! 5
%html{ lang: "en"} %html{ lang: "en"}
= render "layouts/head" = render "layouts/head"
%body{class: "#{app_theme} application"} %body{class: "#{user_application_theme} application"}
= render "layouts/header/empty" = render "layouts/header/empty"
.container.navless-container .container.navless-container
= render "layouts/flash" = render "layouts/flash"
......
...@@ -5,10 +5,7 @@ ...@@ -5,10 +5,7 @@
= brand_header_logo = brand_header_logo
%h3 GitLab %h3 GitLab
.header-content .header-content
%h1.title %button.navbar-toggle{type: 'button'}
= title
%button.navbar-toggle
%span.sr-only Toggle navigation %span.sr-only Toggle navigation
= icon('bars') = icon('bars')
...@@ -43,4 +40,6 @@ ...@@ -43,4 +40,6 @@
= link_to destroy_user_session_path, class: 'logout', method: :delete, title: 'Sign out', data: {toggle: 'tooltip', placement: 'bottom'} do = link_to destroy_user_session_path, class: 'logout', method: :delete, title: 'Sign out', data: {toggle: 'tooltip', placement: 'bottom'} do
= icon('sign-out') = icon('sign-out')
%h1.title= title
= render 'shared/outdated_browser' = render 'shared/outdated_browser'
...@@ -5,10 +5,10 @@ ...@@ -5,10 +5,10 @@
= brand_header_logo = brand_header_logo
%h3 GitLab %h3 GitLab
.header-content .header-content
%h1.title= title
- unless current_controller?('sessions') - unless current_controller?('sessions')
.pull-right .pull-right
= link_to "Sign in", new_session_path(:user, redirect_to_referer: 'yes'), class: 'btn btn-sign-in btn-success btn-sm' = link_to "Sign in", new_session_path(:user, redirect_to_referer: 'yes'), class: 'btn btn-sign-in btn-success btn-sm'
%h1.title= title
= render 'shared/outdated_browser' = render 'shared/outdated_browser'
%ul.nav.nav-sidebar %ul.nav.nav-sidebar
= nav_link(path: 'dashboard#show', html_options: {class: 'home'}) do = nav_link(path: ['dashboard#show', 'root#show'], html_options: {class: 'home'}) do
= link_to root_path, title: 'Home', class: 'shortcuts-activity', data: {placement: 'right'} do = link_to dashboard_path, title: 'Home', class: 'shortcuts-activity', data: {placement: 'right'} do
= icon('dashboard fw') = icon('dashboard fw')
%span %span
Your Projects Your Projects
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
= icon('gear fw') = icon('gear fw')
%span %span
Account Account
= nav_link(path: ['profiles#applications', 'applications#edit', 'applications#show', 'applications#new']) do = nav_link(path: ['profiles#applications', 'applications#edit', 'applications#show', 'applications#new', 'applications#create']) do
= link_to applications_profile_path, title: 'Applications', data: {placement: 'right'} do = link_to applications_profile_path, title: 'Applications', data: {placement: 'right'} do
= icon('cloud fw') = icon('cloud fw')
%span %span
...@@ -38,11 +38,12 @@ ...@@ -38,11 +38,12 @@
%span %span
SSH Keys SSH Keys
%span.count= current_user.keys.count %span.count= current_user.keys.count
= nav_link(path: 'profiles#design') do = nav_link(controller: :preferences) do
= link_to design_profile_path, title: 'Design', data: {placement: 'right'} do = link_to profile_preferences_path, title: 'Preferences', data: {placement: 'right'} do
-# TODO (rspeicher): Better icon?
= icon('image fw') = icon('image fw')
%span %span
Design Preferences
= nav_link(path: 'profiles#history') do = nav_link(path: 'profiles#history') do
= link_to history_profile_path, title: 'History', data: {placement: 'right'} do = link_to history_profile_path, title: 'History', data: {placement: 'right'} do
= icon('history fw') = icon('history fw')
......
- page_title "Account" - page_title "Account"
%h3.page-title %h3.page-title
= page_title = page_title
%p.light %p.light
Change your username and basic account settings. Change your username and basic account settings.
%hr %hr
- if current_user.ldap_user? - if current_user.ldap_user?
.alert.alert-info .alert.alert-info
Some options are unavailable for LDAP accounts Some options are unavailable for LDAP accounts
.account-page .account-page
.panel.panel-default.update-token .panel.panel-default.update-token
.panel-heading .panel-heading
Reset Private token Reset Private token
.panel-body .panel-body
= form_for @user, url: reset_private_token_profile_path, method: :put do |f| = form_for @user, url: reset_private_token_profile_path, method: :put do |f|
.data .data
%p %p
Your private token is used to access application resources without authentication. Your private token is used to access application resources without authentication.
%br %br
It can be used for atom feeds or the API. It can be used for atom feeds or the API.
%span.cred %span.cred
Keep it secret! Keep it secret!
%p.cgray %p.cgray
- if current_user.private_token - if current_user.private_token
= text_field_tag "token", current_user.private_token, class: "form-control" = text_field_tag "token", current_user.private_token, class: "form-control"
%div %div
= f.submit 'Reset private token', data: { confirm: "Are you sure?" }, class: "btn btn-default btn-build-token" = f.submit 'Reset private token', data: { confirm: "Are you sure?" }, class: "btn btn-default btn-build-token"
- else - else
%span You don`t have one yet. Click generate to fix it. %span You don`t have one yet. Click generate to fix it.
= f.submit 'Generate', class: "btn btn-default btn-build-token" = f.submit 'Generate', class: "btn btn-default btn-build-token"
.panel.panel-default .panel.panel-default
.panel-heading .panel-heading
Two-factor Authentication Two-factor Authentication
.panel-body .panel-body
- if current_user.otp_required_for_login - if current_user.otp_required_for_login
.pull-right .pull-right
= link_to 'Disable Two-factor Authentication', profile_two_factor_auth_path, method: :delete, class: 'btn btn-close btn-sm', = link_to 'Disable Two-factor Authentication', profile_two_factor_auth_path, method: :delete, class: 'btn btn-close btn-sm',
data: { confirm: 'Are you sure?' } data: { confirm: 'Are you sure?' }
%p.text-success %p.text-success
%strong %strong
Two-factor Authentication is enabled Two-factor Authentication is enabled
%p %p
If you lose your recovery codes you can If you lose your recovery codes you can
%strong %strong
= succeed ',' do = succeed ',' do
= link_to 'generate new ones', codes_profile_two_factor_auth_path, method: :post, data: { confirm: 'Are you sure?' } = link_to 'generate new ones', codes_profile_two_factor_auth_path, method: :post, data: { confirm: 'Are you sure?' }
invalidating all previous codes. invalidating all previous codes.
- else - else
%p %p
Increase your account's security by enabling two-factor authentication (2FA). Increase your account's security by enabling two-factor authentication (2FA).
%p %p
Each time you log in you’ll be required to provide your username and Each time you log in you’ll be required to provide your username and
password as usual, plus a randomly-generated code from your phone. password as usual, plus a randomly-generated code from your phone.
%div %div
= link_to 'Enable Two-factor Authentication', new_profile_two_factor_auth_path, class: 'btn btn-success' = link_to 'Enable Two-factor Authentication', new_profile_two_factor_auth_path, class: 'btn btn-success'
- if show_profile_social_tab? - if show_profile_social_tab?
.panel.panel-default .panel.panel-default
.panel-heading .panel-heading
Connected Accounts Connected Accounts
.panel-body .panel-body
.oauth-buttons.append-bottom-10 .oauth-buttons.append-bottom-10
%p Click on icon to activate signin with one of the following services %p Click on icon to activate signin with one of the following services
- enabled_social_providers.each do |provider| - enabled_social_providers.each do |provider|
.btn-group .btn-group
= link_to oauth_image_tag(provider), omniauth_authorize_path(User, provider), = link_to oauth_image_tag(provider), omniauth_authorize_path(User, provider),
method: :post, class: "btn btn-lg #{'active' if oauth_active?(provider)}" method: :post, class: "btn btn-lg #{'active' if oauth_active?(provider)}"
- if oauth_active?(provider) - if oauth_active?(provider)
= link_to unlink_profile_account_path(provider: provider), method: :delete, class: 'btn btn-lg' do = link_to unlink_profile_account_path(provider: provider), method: :delete, class: 'btn btn-lg' do
= icon('close') = icon('close')
- if show_profile_username_tab? - if show_profile_username_tab?
.panel.panel-warning.update-username .panel.panel-warning.update-username
.panel-heading .panel-heading
Change Username Change Username
.panel-body .panel-body
= form_for @user, url: update_username_profile_path, method: :put, remote: true do |f| = form_for @user, url: update_username_profile_path, method: :put, remote: true do |f|
%p %p
Changing your username will change path to all personal projects! Changing your username will change path to all personal projects!
%div %div
= f.text_field :username, required: true, class: 'form-control' = f.text_field :username, required: true, class: 'form-control'
&nbsp; &nbsp;
.loading-gif.hide .loading-gif.hide
%p %p
= icon('spinner spin') = icon('spinner spin')
Saving new username Saving new username
%p.light %p.light
= user_url(@user) = user_url(@user)
%div %div
= f.submit 'Save username', class: "btn btn-warning" = f.submit 'Save username', class: "btn btn-warning"
- if show_profile_remove_tab? - if show_profile_remove_tab?
.panel.panel-danger.remove-account .panel.panel-danger.remove-account
.panel-heading .panel-heading
Remove account Remove account
.panel-body .panel-body
- if @user.can_be_removed? - if @user.can_be_removed?
%p Deleting an account has the following effects: %p Deleting an account has the following effects:
%ul %ul
%li All user content like authored issues, snippets, comments will be removed %li All user content like authored issues, snippets, comments will be removed
- rp = current_user.personal_projects.count - rp = current_user.personal_projects.count
- unless rp.zero? - unless rp.zero?
%li #{pluralize rp, 'personal project'} will be removed and cannot be restored %li #{pluralize rp, 'personal project'} will be removed and cannot be restored
= link_to 'Delete account', user_registration_path, data: { confirm: "REMOVE #{current_user.name}? Are you sure?" }, method: :delete, class: "btn btn-remove" = link_to 'Delete account', user_registration_path, data: { confirm: "REMOVE #{current_user.name}? Are you sure?" }, method: :delete, class: "btn btn-remove"
- else - else
- if @user.solo_owned_groups.present? - if @user.solo_owned_groups.present?
%p %p
Your account is currently an owner in these groups: Your account is currently an owner in these groups:
%strong #{@user.solo_owned_groups.map(&:name).join(', ')} %strong #{@user.solo_owned_groups.map(&:name).join(', ')}
%p %p
You must transfer ownership or delete these groups before you can delete your account. You must transfer ownership or delete these groups before you can delete your account.
\ No newline at end of file
- page_title "Design"
%h3.page-title
= page_title
%p.light
Appearance settings will be saved to your profile and made available across all devices.
%hr
= form_for @user, url: profile_path, remote: true, method: :put do |f|
.panel.panel-default.application-theme
.panel-heading
Application theme
.panel-body
.themes_opts
= label_tag do
.prev.default
= f.radio_button :theme_id, 1
Graphite
= label_tag do
.prev.classic
= f.radio_button :theme_id, 2
Charcoal
= label_tag do
.prev.modern
= f.radio_button :theme_id, 3
Green
= label_tag do
.prev.gray
= f.radio_button :theme_id, 4
Gray
= label_tag do
.prev.violet
= f.radio_button :theme_id, 5
Violet
= label_tag do
.prev.blue
= f.radio_button :theme_id, 6
Blue
%br
.clearfix
.panel.panel-default.code-preview-theme
.panel-heading
Code preview theme
.panel-body
.code_highlight_opts
- color_schemes.each do |color_scheme_id, color_scheme|
= label_tag do
.prev
= image_tag "#{color_scheme}-scheme-preview.png"
= f.radio_button :color_scheme_id, color_scheme_id
= color_scheme.gsub(/[-_]+/, ' ').humanize
- page_title 'Preferences'
%h3.page-title
= page_title
%p.light
These settings allow you to customize the appearance and behavior of the site.
They are saved with your account and will persist to any device you use to
access the site.
%hr
= form_for @user, url: profile_preferences_path, remote: true, method: :put, html: {class: 'js-preferences-form form-horizontal'} do |f|
.panel.panel-default.application-theme
.panel-heading
Application theme
.panel-body
- Gitlab::Themes.each do |theme|
= label_tag do
.preview{class: theme.css_class}
= f.radio_button :theme_id, theme.id
= theme.name
.panel.panel-default.syntax-theme
.panel-heading
Syntax highlighting theme
.panel-body
- color_schemes.each do |color_scheme_id, color_scheme|
= label_tag do
.preview= image_tag "#{color_scheme}-scheme-preview.png"
= f.radio_button :color_scheme_id, color_scheme_id
= color_scheme.tr('-_', ' ').titleize
.panel.panel-default
.panel-heading
Behavior
.panel-body
.form-group
= f.label :dashboard, class: 'control-label' do
Default Dashboard
= link_to('(?)', help_page_path('profile', 'preferences') + '#default-dashboard', target: '_blank')
.col-sm-10
= f.select :dashboard, dashboard_choices, {}, class: 'form-control'
.panel-footer
= f.submit 'Save', class: 'btn btn-save'
// Remove body class for any previous theme, re-add current one
$('body').removeClass('<%= Gitlab::Themes.body_classes %>')
$('body').addClass('<%= user_application_theme %>')
// Re-enable the "Save" button
$('input[type=submit]').enable()
// Show the notice flash message
new Flash('<%= flash.discard(:notice) %>', 'notice')
...@@ -109,6 +109,5 @@ ...@@ -109,6 +109,5 @@
.row .row
.col-md-7 .col-md-7
.form-group .form-group
.col-sm-2 &nbsp; .col-sm-offset-2.col-sm-10
.col-sm-10
= f.submit 'Save changes', class: "btn btn-success" = f.submit 'Save changes', class: "btn btn-success"
// Remove body class for any previous theme, re-add current one
$('body').removeClass('<%= Gitlab::Theme.body_classes %>')
$('body').addClass('<%= app_theme %> <%= theme_type %>')
%div#bitbucket_import_modal.modal.hide %div#bitbucket_import_modal.modal
.modal-dialog .modal-dialog
.modal-content .modal-content
.modal-header .modal-header
......
%div#github_import_modal.modal.hide %div#github_import_modal.modal
.modal-dialog .modal-dialog
.modal-content .modal-content
.modal-header .modal-header
......
%div#gitlab_import_modal.modal.hide %div#gitlab_import_modal.modal
.modal-dialog .modal-dialog
.modal-content .modal-content
.modal-header .modal-header
......
- if issuable.errors.any? - if issuable.errors.any?
.row .row
.col-sm-10.col-sm-offset-2 .col-sm-offset-2.col-sm-10
.alert.alert-danger .alert.alert-danger
- issuable.errors.full_messages.each do |msg| - issuable.errors.full_messages.each do |msg|
%span= msg %span= msg
......
#modal-remove-blob.modal.hide #modal-remove-blob.modal
.modal-dialog .modal-dialog
.modal-content .modal-content
.modal-header .modal-header
...@@ -13,8 +13,7 @@ ...@@ -13,8 +13,7 @@
= render 'shared/commit_message_container', params: params, = render 'shared/commit_message_container', params: params,
placeholder: 'Removed this file because...' placeholder: 'Removed this file because...'
.form-group .form-group
.col-sm-2 .col-sm-offset-2.col-sm-10
.col-sm-10
= button_tag 'Remove file', class: 'btn btn-remove btn-remove-file' = button_tag 'Remove file', class: 'btn btn-remove btn-remove-file'
= link_to "Cancel", '#', class: "btn btn-cancel", "data-dismiss" => "modal" = link_to "Cancel", '#', class: "btn btn-cancel", "data-dismiss" => "modal"
......
- page_title "#{@commit.title} (#{@commit.short_id})", "Commits" - page_title "#{@commit.title} (#{@commit.short_id})", "Commits"
= render "commit_box" = render "commit_box"
= render "projects/diffs/diffs", diffs: @diffs, project: @project = render "projects/diffs/diffs", diffs: @diffs, project: @project
= render "projects/notes/notes_with_form" = render "projects/notes/notes_with_form", view: params[:view]
...@@ -18,6 +18,8 @@ ...@@ -18,6 +18,8 @@
- elsif type_left == 'old' || type_left.nil? - elsif type_left == 'old' || type_left.nil?
%td.old_line{id: line_code_left, class: "#{type_left}"} %td.old_line{id: line_code_left, class: "#{type_left}"}
= link_to raw(line_number_left), "##{line_code_left}", id: line_code_left = link_to raw(line_number_left), "##{line_code_left}", id: line_code_left
- if @comments_allowed && can?(current_user, :write_note, @project)
= link_to_new_diff_note(line_code_left, 'old')
%td.line_content{class: "parallel noteable_line #{type_left} #{line_code_left}", "line_code" => line_code_left }= raw line_content_left %td.line_content{class: "parallel noteable_line #{type_left} #{line_code_left}", "line_code" => line_code_left }= raw line_content_left
- if type_right == 'new' - if type_right == 'new'
...@@ -29,12 +31,14 @@ ...@@ -29,12 +31,14 @@
%td.new_line{id: new_line_code, class: "#{new_line_class}", data: { linenumber: line_number_right }} %td.new_line{id: new_line_code, class: "#{new_line_class}", data: { linenumber: line_number_right }}
= link_to raw(line_number_right), "##{new_line_code}", id: new_line_code = link_to raw(line_number_right), "##{new_line_code}", id: new_line_code
- if @comments_allowed && can?(current_user, :write_note, @project)
= link_to_new_diff_note(line_code_right, 'new')
%td.line_content.parallel{class: "noteable_line #{new_line_class} #{new_line_code}", "line_code" => new_line_code}= raw line_content_right %td.line_content.parallel{class: "noteable_line #{new_line_class} #{new_line_code}", "line_code" => new_line_code}= raw line_content_right
- if @reply_allowed - if @reply_allowed
- comments_left, comments_right = organize_comments(type_left, type_right, line_code_left, line_code_right) - comments_left, comments_right = organize_comments(type_left, type_right, line_code_left, line_code_right)
- if comments_left.present? || comments_right.present? - if comments_left.present? || comments_right.present?
= render "projects/notes/diff_notes_with_reply_parallel", notes1: comments_left, notes2: comments_right = render "projects/notes/diff_notes_with_reply_parallel", notes_left: comments_left, notes_right: comments_right
- if diff_file.diff.diff.blank? && diff_file.mode_changed? - if diff_file.diff.diff.blank? && diff_file.mode_changed?
.file-mode-changed .file-mode-changed
......
...@@ -82,8 +82,7 @@ ...@@ -82,8 +82,7 @@
%legend %legend
Project avatar: Project avatar:
.form-group .form-group
.col-sm-2 .col-sm-offset-2.col-sm-10
.col-sm-10
- if @project.avatar? - if @project.avatar?
= project_icon("#{@project.namespace.to_param}/#{@project.to_param}", alt: '', class: 'avatar project-avatar s160') = project_icon("#{@project.namespace.to_param}/#{@project.to_param}", alt: '', class: 'avatar project-avatar s160')
%p.light %p.light
......
= form_for [@project.namespace.becomes(Namespace), @project, @label], html: { class: 'form-horizontal label-form' } do |f| = form_for [@project.namespace.becomes(Namespace), @project, @label], html: { class: 'form-horizontal label-form' } do |f|
-if @label.errors.any? -if @label.errors.any?
.row .row
.col-sm-10.col-sm-offset-2 .col-sm-offset-2.col-sm-10
.alert.alert-danger .alert.alert-danger
- @label.errors.full_messages.each do |msg| - @label.errors.full_messages.each do |msg|
%span= msg %span= msg
......
%div#modal_merge_info.modal.hide %div#modal_merge_info.modal
.modal-dialog .modal-dialog
.modal-content .modal-content
.modal-header .modal-header
......
- note1 = notes1.present? ? notes1.first : nil - note1 = notes_left.present? ? notes_left.first : nil
- note2 = notes2.present? ? notes2.first : nil - note2 = notes_right.present? ? notes_right.first : nil
%tr.notes_holder %tr.notes_holder
- if note1 - if note1
%td.notes_line %td.notes_line.old
%span.btn.disabled %span.btn.disabled
%i.fa.fa-comment %i.fa.fa-comment
= notes1.count = notes_left.count
%td.notes_content.parallel %td.notes_content.parallel.old
%ul.notes{ rel: note1.discussion_id } %ul.notes{ rel: note1.discussion_id }
= render notes1 = render notes_left
.discussion-reply-holder .discussion-reply-holder
= link_to_reply_diff(note1) = link_to_reply_diff(note1, 'old')
- else - else
%td= "" %td.notes_line.old= ""
%td= "" %td.notes_content.parallel.old= ""
- if note2 - if note2
%td.notes_line %td.notes_line.new
%span.btn.disabled %span.btn.disabled
%i.fa.fa-comment %i.fa.fa-comment
= notes2.count = notes_right.count
%td.notes_content.parallel %td.notes_content.parallel.new
%ul.notes{ rel: note2.discussion_id } %ul.notes{ rel: note2.discussion_id }
= render notes2 = render notes_right
.discussion-reply-holder .discussion-reply-holder
= link_to_reply_diff(note2) = link_to_reply_diff(note2, 'new')
- else - else
%td= "" %td.notes_line.new= ""
%td= "" %td.notes_content.parallel.new= ""
= form_for [@project.namespace.becomes(Namespace), @project, @note], remote: true, html: { :'data-type' => 'json', multipart: true, id: nil, class: "new_note js-new-note-form common-note-form gfm-form" }, authenticity_token: true do |f| = form_for [@project.namespace.becomes(Namespace), @project, @note], remote: true, html: { :'data-type' => 'json', multipart: true, id: nil, class: "new_note js-new-note-form common-note-form gfm-form" }, authenticity_token: true do |f|
= hidden_field_tag :view, params[:view]
= hidden_field_tag :line_type
= note_target_fields(@note) = note_target_fields(@note)
= f.hidden_field :commit_id = f.hidden_field :commit_id
= f.hidden_field :line_code = f.hidden_field :line_code
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
.js-main-target-form .js-main-target-form
- if can? current_user, :write_note, @project - if can? current_user, :write_note, @project
= render "projects/notes/form" = render "projects/notes/form", view: params[:view]
:javascript :javascript
new Notes("#{namespace_project_notes_path(namespace_id: @project.namespace, target_id: @noteable.id, target_type: @noteable.class.name.underscore)}", #{@notes.map(&:id).to_json}, #{Time.now.to_i}) new Notes("#{namespace_project_notes_path(namespace_id: @project.namespace, target_id: @noteable.id, target_type: @noteable.class.name.underscore)}", #{@notes.map(&:id).to_json}, #{Time.now.to_i}, "#{params[:view]}")
...@@ -12,8 +12,7 @@ ...@@ -12,8 +12,7 @@
= f.select :format, options_for_select(ProjectWiki::MARKUPS, {selected: @page.format}), {}, class: "form-control" = f.select :format, options_for_select(ProjectWiki::MARKUPS, {selected: @page.format}), {}, class: "form-control"
.row .row
.col-sm-2 .col-sm-offset-2.col-sm-10
.col-sm-10
%p.cgray %p.cgray
To link to a (new) page you can just type To link to a (new) page you can just type
%code [Link Title](page-slug) %code [Link Title](page-slug)
......
%div#modal-new-wiki.modal.hide %div#modal-new-wiki.modal
.modal-dialog .modal-dialog
.modal-content .modal-content
.modal-header .modal-header
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
.input-group-addon.git-protocols .input-group-addon.git-protocols
.input-group-btn .input-group-btn
%button{ | %button{ |
type: 'button', |
class: "btn btn-sm #{ 'active' if default_clone_protocol == 'ssh' }#{ ' has_tooltip' if current_user && current_user.require_ssh_key? }", | class: "btn btn-sm #{ 'active' if default_clone_protocol == 'ssh' }#{ ' has_tooltip' if current_user && current_user.require_ssh_key? }", |
:"data-clone" => project.ssh_url_to_repo, | :"data-clone" => project.ssh_url_to_repo, |
:"data-title" => "Add an SSH key to your profile<br> to pull or push via SSH", :"data-title" => "Add an SSH key to your profile<br> to pull or push via SSH",
...@@ -11,6 +12,7 @@ ...@@ -11,6 +12,7 @@
SSH SSH
.input-group-btn .input-group-btn
%button{ | %button{ |
type: 'button', |
class: "btn btn-sm #{ 'active' if default_clone_protocol == 'http' }#{ ' has_tooltip' if current_user && current_user.require_password? }", | class: "btn btn-sm #{ 'active' if default_clone_protocol == 'http' }#{ ' has_tooltip' if current_user && current_user.require_password? }", |
:"data-clone" => project.http_url_to_repo, | :"data-clone" => project.http_url_to_repo, |
:"data-title" => "Set a password on your account<br> to pull or push via #{gitlab_config.protocol.upcase}", :"data-title" => "Set a password on your account<br> to pull or push via #{gitlab_config.protocol.upcase}",
......
#modal-confirm-danger.modal.hide{tabindex: -1} #modal-confirm-danger.modal{tabindex: -1}
.modal-dialog .modal-dialog
.modal-content .modal-content
.modal-header .modal-header
......
...@@ -3,8 +3,6 @@ ...@@ -3,8 +3,6 @@
- if avatar - if avatar
.dash-project-avatar .dash-project-avatar
= project_icon(project, alt: '', class: 'avatar project-avatar s40') = project_icon(project, alt: '', class: 'avatar project-avatar s40')
.dash-project-access-icon
= visibility_level_icon(project.visibility_level)
%span.str-truncated %span.str-truncated
%span.namespace-name %span.namespace-name
- if project.namespace - if project.namespace
......
...@@ -12,8 +12,8 @@ ...@@ -12,8 +12,8 @@
= render 'shared/visibility_level', f: f, visibility_level: visibility_level, can_change_visibility_level: true, form_model: @snippet = render 'shared/visibility_level', f: f, visibility_level: visibility_level, can_change_visibility_level: true, form_model: @snippet
.form-group .file-editor
.file-editor .form-group
= f.label :file_name, "File", class: 'control-label' = f.label :file_name, "File", class: 'control-label'
.col-sm-10 .col-sm-10
.file-holder.snippet .file-holder.snippet
......
...@@ -62,12 +62,13 @@ production: &base ...@@ -62,12 +62,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 ## Default theme ID
## BASIC = 1 ## 1 - Graphite
## MARS = 2 ## 2 - Charcoal
## MODERN = 3 ## 3 - Green
## GRAY = 4 ## 4 - Gray
## COLOR = 5 ## 5 - Violet
## 6 - Blue
# default_theme: 2 # default: 2 # default_theme: 2 # default: 2
## Automatic issue closing ## Automatic issue closing
......
...@@ -136,7 +136,7 @@ Settings['gitlab'] ||= Settingslogic.new({}) ...@@ -136,7 +136,7 @@ Settings['gitlab'] ||= Settingslogic.new({})
Settings.gitlab['default_projects_limit'] ||= 10 Settings.gitlab['default_projects_limit'] ||= 10
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::Theme::MARS if Settings.gitlab['default_theme'].nil? Settings.gitlab['default_theme'] = Gitlab::Themes::APPLICATION_DEFAULT if Settings.gitlab['default_theme'].nil?
Settings.gitlab['host'] ||= 'localhost' Settings.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?
...@@ -164,6 +164,7 @@ Settings.gitlab['issue_closing_pattern'] = '((?:[Cc]los(?:e[sd]?|ing)|[Ff]ix(?:e ...@@ -164,6 +164,7 @@ Settings.gitlab['issue_closing_pattern'] = '((?:[Cc]los(?:e[sd]?|ing)|[Ff]ix(?:e
Settings.gitlab['default_projects_features'] ||= {} Settings.gitlab['default_projects_features'] ||= {}
Settings.gitlab['webhook_timeout'] ||= 10 Settings.gitlab['webhook_timeout'] ||= 10
Settings.gitlab['max_attachment_size'] ||= 10 Settings.gitlab['max_attachment_size'] ||= 10
Settings.gitlab['session_expire_delay'] ||= 10080
Settings.gitlab.default_projects_features['issues'] = true if Settings.gitlab.default_projects_features['issues'].nil? Settings.gitlab.default_projects_features['issues'] = true if Settings.gitlab.default_projects_features['issues'].nil?
Settings.gitlab.default_projects_features['merge_requests'] = true if Settings.gitlab.default_projects_features['merge_requests'].nil? Settings.gitlab.default_projects_features['merge_requests'] = true if Settings.gitlab.default_projects_features['merge_requests'].nil?
Settings.gitlab.default_projects_features['wiki'] = true if Settings.gitlab.default_projects_features['wiki'].nil? Settings.gitlab.default_projects_features['wiki'] = true if Settings.gitlab.default_projects_features['wiki'].nil?
......
...@@ -20,3 +20,11 @@ OmniAuth.config.allowed_request_methods << :get if Gitlab.config.omniauth.auto_s ...@@ -20,3 +20,11 @@ OmniAuth.config.allowed_request_methods << :get if Gitlab.config.omniauth.auto_s
OmniAuth.config.before_request_phase do |env| OmniAuth.config.before_request_phase do |env|
OmniAuth::RequestForgeryProtection.new(env).call OmniAuth::RequestForgeryProtection.new(env).call
end end
if Gitlab.config.omniauth.enabled
Gitlab.config.omniauth.providers.each do |provider|
if provider['name'] == 'kerberos'
require 'omniauth-kerberos'
end
end
end
# Be sure to restart your server when you modify this file. # Be sure to restart your server when you modify this file.
require 'gitlab/current_settings'
include Gitlab::CurrentSettings
Settings.gitlab['session_expire_delay'] = current_application_settings.session_expire_delay
Gitlab::Application.config.session_store( Gitlab::Application.config.session_store(
:redis_store, # Using the cookie_store would enable session replay attacks. :redis_store, # Using the cookie_store would enable session replay attacks.
servers: Gitlab::Application.config.cache_store[1].merge(namespace: 'session:gitlab'), # re-use the Redis config from the Rails cache store servers: Gitlab::Application.config.cache_store[1].merge(namespace: 'session:gitlab'), # re-use the Redis config from the Rails cache store
key: '_gitlab_session', key: '_gitlab_session',
secure: Gitlab.config.gitlab.https, secure: Gitlab.config.gitlab.https,
httponly: true, httponly: true,
expire_after: 1.week, expire_after: Settings.gitlab['session_expire_delay'] * 60,
path: (Rails.application.config.relative_url_root.nil?) ? '/' : Rails.application.config.relative_url_root path: (Rails.application.config.relative_url_root.nil?) ? '/' : Rails.application.config.relative_url_root
) )
...@@ -223,7 +223,6 @@ Gitlab::Application.routes.draw do ...@@ -223,7 +223,6 @@ Gitlab::Application.routes.draw do
resource :profile, only: [:show, :update] do resource :profile, only: [:show, :update] do
member do member do
get :history get :history
get :design
get :applications get :applications
put :reset_private_token put :reset_private_token
...@@ -242,6 +241,7 @@ Gitlab::Application.routes.draw do ...@@ -242,6 +241,7 @@ Gitlab::Application.routes.draw do
put :reset put :reset
end end
end end
resource :preferences, only: [:show, :update]
resources :keys resources :keys
resources :emails, only: [:index, :create, :destroy] resources :emails, only: [:index, :create, :destroy]
resource :avatar, only: [:destroy] resource :avatar, only: [:destroy]
...@@ -330,7 +330,7 @@ Gitlab::Application.routes.draw do ...@@ -330,7 +330,7 @@ Gitlab::Application.routes.draw do
get '/users/auth/:provider/omniauth_error' => 'omniauth_callbacks#omniauth_error', as: :omniauth_error get '/users/auth/:provider/omniauth_error' => 'omniauth_callbacks#omniauth_error', as: :omniauth_error
end end
root to: "dashboard#show" root to: "root#show"
# #
# Project Area # Project Area
......
...@@ -12,7 +12,7 @@ admin = User.create( ...@@ -12,7 +12,7 @@ admin = User.create(
username: 'root', username: 'root',
password: password, password: password,
password_expires_at: expire_time, password_expires_at: expire_time,
theme_id: Gitlab::Theme::MARS theme_id: Gitlab::Themes::APPLICATION_DEFAULT
) )
......
class AddSessionExpireDelayForApplicationSettings < ActiveRecord::Migration
def change
add_column :application_settings, :session_expire_delay, :integer, default: 10080, null: false
end
end
\ No newline at end of file
class AddDashboardToUsers < ActiveRecord::Migration
def up
add_column :users, :dashboard, :integer, default: 0
end
def down
remove_column :users, :dashboard
end
end
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20150609125332) do ActiveRecord::Schema.define(version: 20150610065936) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
...@@ -40,13 +40,14 @@ ActiveRecord::Schema.define(version: 20150609125332) do ...@@ -40,13 +40,14 @@ ActiveRecord::Schema.define(version: 20150609125332) do
t.boolean "twitter_sharing_enabled", default: true t.boolean "twitter_sharing_enabled", default: true
t.text "help_text" t.text "help_text"
t.text "restricted_visibility_levels" t.text "restricted_visibility_levels"
t.integer "max_attachment_size", default: 10, null: false t.boolean "version_check_enabled", default: true
t.integer "max_attachment_size", default: 10, null: false
t.integer "default_project_visibility" t.integer "default_project_visibility"
t.integer "default_snippet_visibility" t.integer "default_snippet_visibility"
t.text "restricted_signup_domains" t.text "restricted_signup_domains"
t.boolean "version_check_enabled", default: true
t.boolean "user_oauth_applications", default: true t.boolean "user_oauth_applications", default: true
t.string "after_sign_out_path" t.string "after_sign_out_path"
t.integer "session_expire_delay", default: 10080, null: false
end end
create_table "approvals", force: true do |t| create_table "approvals", force: true do |t|
...@@ -579,12 +580,13 @@ ActiveRecord::Schema.define(version: 20150609125332) do ...@@ -579,12 +580,13 @@ ActiveRecord::Schema.define(version: 20150609125332) do
t.string "bitbucket_access_token" t.string "bitbucket_access_token"
t.string "bitbucket_access_token_secret" t.string "bitbucket_access_token_secret"
t.string "location" t.string "location"
t.string "public_email", default: "", null: false
t.string "encrypted_otp_secret" t.string "encrypted_otp_secret"
t.string "encrypted_otp_secret_iv" t.string "encrypted_otp_secret_iv"
t.string "encrypted_otp_secret_salt" t.string "encrypted_otp_secret_salt"
t.boolean "otp_required_for_login" t.boolean "otp_required_for_login"
t.text "otp_backup_codes" t.text "otp_backup_codes"
t.string "public_email", default: "", null: false
t.integer "dashboard", default: 0
end end
add_index "users", ["admin"], name: "index_users_on_admin", using: :btree add_index "users", ["admin"], name: "index_users_on_admin", using: :btree
......
...@@ -3,35 +3,36 @@ ...@@ -3,35 +3,36 @@
## User documentation ## User documentation
- [API](api/README.md) Automate GitLab via a simple and powerful API. - [API](api/README.md) Automate GitLab via a simple and powerful API.
- [GitLab as OAuth2 authentication service provider](integration/oauth_provider.md). It allows you to login to other applications from GitLab.
- [Importing to GitLab](workflow/importing/README.md). - [Importing to GitLab](workflow/importing/README.md).
- [Markdown](markdown/markdown.md) GitLab's advanced formatting system. - [Markdown](markdown/markdown.md) GitLab's advanced formatting system.
- [Permissions](permissions/permissions.md) Learn what each role in a project (guest/reporter/developer/master/owner) can do. - [Permissions](permissions/permissions.md) Learn what each role in a project (guest/reporter/developer/master/owner) can do.
- [Profile Settings](profile/README.md)
- [Project Services](project_services/project_services.md) Integrate a project with external services, such as CI and chat. - [Project Services](project_services/project_services.md) Integrate a project with external services, such as CI and chat.
- [Public access](public_access/public_access.md) Learn how you can allow public and internal access to projects. - [Public access](public_access/public_access.md) Learn how you can allow public and internal access to projects.
- [SSH](ssh/README.md) Setup your ssh keys and deploy keys for secure access to your projects. - [SSH](ssh/README.md) Setup your ssh keys and deploy keys for secure access to your projects.
- [Web hooks](web_hooks/web_hooks.md) Let GitLab notify you when new code has been pushed to your project. - [Web hooks](web_hooks/web_hooks.md) Let GitLab notify you when new code has been pushed to your project.
- [Workflow](workflow/README.md) Using GitLab functionality and importing projects from GitHub and SVN. - [Workflow](workflow/README.md) Using GitLab functionality and importing projects from GitHub and SVN.
- [GitLab as OAuth2 authentication service provider](integration/oauth_provider.md). It allows you to login to other applications from GitLab.
## Administrator documentation ## Administrator documentation
- [Install](install/README.md) Requirements, directory structures and installation from source.
- [Integration](integration/README.md) How to integrate with systems such as JIRA, Redmine, LDAP and Twitter.
- [Raketasks](raketasks/README.md) Backups, maintenance, automatic web hook setup and the importing of projects.
- [Custom git hooks](hooks/custom_hooks.md) Custom git hooks (on the filesystem) for when web hooks aren't enough.
- [System hooks](system_hooks/system_hooks.md) Notifications when users, projects and keys are changed.
- [Security](security/README.md) Learn what you can do to further secure your GitLab instance.
- [Update](update/README.md) Update guides to upgrade your installation.
- [Audit Events](administration/audit_events.md) Check how user access changed in projects and groups. - [Audit Events](administration/audit_events.md) Check how user access changed in projects and groups.
- [Changing the appearance of the login page](customization/branded_login_page.md) Make the login page branded for your GitLab instance. - [Changing the appearance of the login page](customization/branded_login_page.md) Make the login page branded for your GitLab instance.
- [Help message](customization/help_message.md) Set information about administrators of your GitLab instance. - [Custom git hooks](hooks/custom_hooks.md) Custom git hooks (on the filesystem) for when web hooks aren't enough.
- [Git Hooks](git_hooks/git_hooks.md) Advanced push rules for your project.
- [Email](tools/email.md) Email GitLab users from GitLab - [Email](tools/email.md) Email GitLab users from GitLab
- [Welcome message](customization/welcome_message.md) Add a custom welcome message to the sign-in page. - [Git Hooks](git_hooks/git_hooks.md) Advanced push rules for your project.
- [Help message](customization/help_message.md) Set information about administrators of your GitLab instance.
- [Install](install/README.md) Requirements, directory structures and installation from source.
- [Integration](integration/README.md) How to integrate with systems such as JIRA, Redmine, LDAP and Twitter.
- [Issue closing](customization/issue_closing.md) Customize how to close an issue from commit messages. - [Issue closing](customization/issue_closing.md) Customize how to close an issue from commit messages.
- [Libravatar](customization/libravatar.md) Use Libravatar for user avatars. - [Libravatar](customization/libravatar.md) Use Libravatar for user avatars.
- [Log system](logs/logs.md) Log system.
- [Operations](operations/README.md) Keeping GitLab up and running - [Operations](operations/README.md) Keeping GitLab up and running
- [Log system](logs/logs.md) Log system - [Raketasks](raketasks/README.md) Backups, maintenance, automatic web hook setup and the importing of projects.
- [Security](security/README.md) Learn what you can do to further secure your GitLab instance.
- [System hooks](system_hooks/system_hooks.md) Notifications when users, projects and keys are changed.
- [Update](update/README.md) Update guides to upgrade your installation.
- [Welcome message](customization/welcome_message.md) Add a custom welcome message to the sign-in page.
## Contributor documentation ## Contributor documentation
......
...@@ -4,6 +4,9 @@ Sometimes it is useful to import the database from a production environment ...@@ -4,6 +4,9 @@ Sometimes it is useful to import the database from a production environment
into a staging environment for testing. The procedure below assumes you have into a staging environment for testing. The procedure below assumes you have
SSH+sudo access to both the production environment and the staging VM. SSH+sudo access to both the production environment and the staging VM.
**Destroy your staging VM** when you are done with it. It is important to avoid
data leaks.
On the staging VM, add the following line to `/etc/gitlab/gitlab.rb` to speed up On the staging VM, add the following line to `/etc/gitlab/gitlab.rb` to speed up
large database imports. large database imports.
...@@ -12,6 +15,8 @@ large database imports. ...@@ -12,6 +15,8 @@ large database imports.
echo "postgresql['checkpoint_segments'] = 64" | sudo tee -a /etc/gitlab/gitlab.rb echo "postgresql['checkpoint_segments'] = 64" | sudo tee -a /etc/gitlab/gitlab.rb
sudo touch /etc/gitlab/skip-auto-migrations sudo touch /etc/gitlab/skip-auto-migrations
sudo gitlab-ctl reconfigure sudo gitlab-ctl reconfigure
sudo gitlab-ctl stop unicorn
sudo gitlab-ctl stop sidekiq
``` ```
Next, we let the production environment stream a compressed SQL dump to our Next, we let the production environment stream a compressed SQL dump to our
......
...@@ -177,3 +177,33 @@ File.open(full_path) do # Etc. ...@@ -177,3 +177,33 @@ File.open(full_path) do # Etc.
``` ```
A check like this could have avoided CVE-2013-4583. A check like this could have avoided CVE-2013-4583.
## Properly anchor regular expressions to the start and end of strings
When using regular expressions to validate user input that is passed as an argument to a shell command, make sure to use the `\A` and `\z` anchors that designate the start and end of the string, rather than `^` and `$`, or no anchors at all.
If you don't, an attacker could use this to execute commands with potentially harmful effect.
For example, when a project's `import_url` is validated like below, the user could trick GitLab into cloning from a Git repository on the local filesystem.
```ruby
validates :import_url, format: { with: URI.regexp(%w(ssh git http https)) }
# URI.regexp(%w(ssh git http https)) roughly evaluates to /(ssh|git|http|https):(something_that_looks_like_a_url)/
```
Suppose the user submits the following as their import URL:
```
file://git:/tmp/lol
```
Since there are no anchors in the used regular expression, the `git:/tmp/lol` in the value would match, and the validation would pass.
When importing, GitLab would execute the following command, passing the `import_url` as an argument:
```sh
git clone file://git:/tmp/lol
```
Git would simply ignore the `git:` part, interpret the path as `file:///tmp/lol` and import the repository into the new project, in turn potentially giving the attacker access to any repository in the system, whether private or not.
...@@ -88,8 +88,8 @@ Is the system packaged Git too old? Remove it and compile from source. ...@@ -88,8 +88,8 @@ Is the system packaged Git too old? Remove it and compile from source.
# Download and compile from source # Download and compile from source
cd /tmp cd /tmp
curl -L --progress https://www.kernel.org/pub/software/scm/git/git-2.1.2.tar.gz | tar xz curl -L --progress https://www.kernel.org/pub/software/scm/git/git-2.4.3.tar.gz | tar xz
cd git-2.1.2/ cd git-2.4.3/
./configure ./configure
make prefix=/usr/local all make prefix=/usr/local all
...@@ -195,9 +195,9 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da ...@@ -195,9 +195,9 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da
### Clone the Source ### Clone the Source
# Clone GitLab repository # Clone GitLab repository
sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 7-11-stable gitlab sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 7-12-stable gitlab
**Note:** You can change `7-11-stable-ee` to `master` if you want the *bleeding edge* version, but never install master on a production server! **Note:** You can change `7-12-stable-ee` to `master` if you want the *bleeding edge* version, but never install master on a production server!
### Configure It ### Configure It
......
# Profile Settings
- [Preferences](preferences.md)
- [Two-factor Authentication (2FA)](two_factor_authentication.md)
# Profile Preferences
Settings in the **Profile > Preferences** page allow the user to customize
various aspects of the site to their liking.
## Application theme
Changing this setting allows the user to customize the color scheme used for the
navigation bar on the left side of the screen.
The default is **Charcoal**.
## Syntax highlighting theme
Changing this setting allows the user to customize the theme used when viewing
syntax highlighted code on the site.
The default is **White**.
## Behavior
### Default Dashboard
For users who have access to a large number of projects but only keep up with a
select few, the amount of activity on the default Dashboard page can be
overwhelming.
Changing this setting allows the user to redefine what their default dashboard
will be. Setting it to **Starred Projects** will make that Dashboard view the
default when signing in or clicking the application logo in the upper left.
The default is **Your Projects**.
...@@ -4,7 +4,7 @@ GitLab provides a way to push update messages to an Irker server. When ...@@ -4,7 +4,7 @@ GitLab provides a way to push update messages to an Irker server. When
configured, pushes to a project will trigger the service to send data directly configured, pushes to a project will trigger the service to send data directly
to the Irker server. to the Irker server.
See the project homepage for further info: http://www.catb.org/esr/irker/ See the project homepage for further info: https://gitlab.com/esr/irker
## Needed setup ## Needed setup
......
...@@ -30,9 +30,6 @@ All steps from issue template are explained below ...@@ -30,9 +30,6 @@ All steps from issue template are explained below
``` ```
Xth: (7 working days before the 22nd) Xth: (7 working days before the 22nd)
- [ ] Update the CE changelog (#LINK)
- [ ] Update the EE changelog (#LINK)
- [ ] Update the CI changelog (#LINK)
- [ ] Triage the omnibus-gitlab milestone - [ ] Triage the omnibus-gitlab milestone
Xth: (6 working days before the 22nd) Xth: (6 working days before the 22nd)
...@@ -41,6 +38,7 @@ Xth: (6 working days before the 22nd) ...@@ -41,6 +38,7 @@ Xth: (6 working days before the 22nd)
- [ ] Determine QA person and notify this person - [ ] Determine QA person and notify this person
- [ ] Check the tasks in [how to rc1 guide](https://dev.gitlab.org/gitlab/gitlabhq/blob/master/doc/release/howto_rc1.md) and delegate tasks if necessary - [ ] Check the tasks in [how to rc1 guide](https://dev.gitlab.org/gitlab/gitlabhq/blob/master/doc/release/howto_rc1.md) and delegate tasks if necessary
- [ ] Create CE, EE, CI RC1 versions (#LINK) - [ ] Create CE, EE, CI RC1 versions (#LINK)
- [ ] Build RC1 packages (EE first) (#LINK)
Xth: (5 working days before the 22nd) Xth: (5 working days before the 22nd)
...@@ -142,7 +140,8 @@ Tweet about the RC release: ...@@ -142,7 +140,8 @@ Tweet about the RC release:
## Prepare the blog post ## Prepare the blog post
1. Start with a complete copy of the [release blog template](https://gitlab.com/gitlab-com/www-gitlab-com/blob/master/doc/release_blog_template.md) and fill it out. 1. The blog post template for this release should already exist and might have comments that were added during the month.
1. Fill out as much of the blog post template as you can.
1. Make sure the blog post contains information about the GitLab CI release. 1. Make sure the blog post contains information about the GitLab CI release.
1. Check the changelog of CE and EE for important changes. 1. Check the changelog of CE and EE for important changes.
1. Also check the CI changelog 1. Also check the CI changelog
...@@ -155,6 +154,7 @@ Tweet about the RC release: ...@@ -155,6 +154,7 @@ Tweet about the RC release:
1. Create a merge request on [GitLab.com](https://gitlab.com/gitlab-com/www-gitlab-com/tree/master) 1. Create a merge request on [GitLab.com](https://gitlab.com/gitlab-com/www-gitlab-com/tree/master)
1. Assign to one reviewer who will fix spelling issues by editing the branch (either with a git client or by using the online editor) 1. Assign to one reviewer who will fix spelling issues by editing the branch (either with a git client or by using the online editor)
1. Comment to the reviewer: '@person Please mention the whole team as soon as you are done (3 workdays before release at the latest)' 1. Comment to the reviewer: '@person Please mention the whole team as soon as you are done (3 workdays before release at the latest)'
1. Create a complete copy of the [release blog template](https://gitlab.com/gitlab-com/www-gitlab-com/blob/master/doc/release_blog_template.md) for the release after this.
## Create CE, EE, CI stable versions ## Create CE, EE, CI stable versions
...@@ -212,4 +212,4 @@ Consider creating a post on Hacker News. ...@@ -212,4 +212,4 @@ Consider creating a post on Hacker News.
## Create a WIP blogpost for the next release ## Create a WIP blogpost for the next release
Create a WIP blogpost using [release blog template](https://gitlab.com/gitlab-com/www-gitlab-com/blob/master/doc/release_blog_template.md). Create a WIP blogpost using [release blog template](https://gitlab.com/gitlab-com/www-gitlab-com/blob/master/doc/release_blog_template.md).
\ No newline at end of file
# From 6.x or 7.x to 7.11 # From 6.x or 7.x to 7.12
*Make sure you view this [upgrade guide from the `master` branch](../../../master/doc/update/6.x-or-7.x-to-7.11.md) for the most up to date instructions.* *Make sure you view this [upgrade guide from the `master` branch](../../../master/doc/update/6.x-or-7.x-to-7.12.md) for the most up to date instructions.*
This allows you to upgrade any version of GitLab from 6.0 and up (including 7.0 and up) to 7.11. This allows you to upgrade any version of GitLab from 6.0 and up (including 7.0 and up) to 7.12.
## Global issue numbers ## Global issue numbers
...@@ -71,7 +71,7 @@ sudo -u git -H git checkout -- db/schema.rb # local changes will be restored aut ...@@ -71,7 +71,7 @@ sudo -u git -H git checkout -- db/schema.rb # local changes will be restored aut
For GitLab Community Edition: For GitLab Community Edition:
```bash ```bash
sudo -u git -H git checkout 7-11-stable sudo -u git -H git checkout 7-12-stable
``` ```
OR OR
...@@ -79,7 +79,7 @@ OR ...@@ -79,7 +79,7 @@ OR
For GitLab Enterprise Edition: For GitLab Enterprise Edition:
```bash ```bash
sudo -u git -H git checkout 7-11-stable-ee sudo -u git -H git checkout 7-12-stable-ee
``` ```
## 4. Install additional packages ## 4. Install additional packages
...@@ -164,11 +164,11 @@ sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab ...@@ -164,11 +164,11 @@ sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab
TIP: to see what changed in `gitlab.yml.example` in this release use next command: TIP: to see what changed in `gitlab.yml.example` in this release use next command:
``` ```
git diff 6-0-stable:config/gitlab.yml.example 7-11-stable-ee:config/gitlab.yml.example git diff 6-0-stable:config/gitlab.yml.example 7-12-stable-ee:config/gitlab.yml.example
``` ```
* Make `/home/git/gitlab/config/gitlab.yml` the same as https://gitlab.com/gitlab-org/gitlab-ee/blob/7-11-stable-ee/config/gitlab.yml.example but with your settings. * Make `/home/git/gitlab/config/gitlab.yml` the same as https://gitlab.com/gitlab-org/gitlab-ee/blob/7-12-stable-ee/config/gitlab.yml.example but with your settings.
* Make `/home/git/gitlab/config/unicorn.rb` the same as https://gitlab.com/gitlab-org/gitlab-ee/blob/7-11-stable-ee/config/unicorn.rb.example but with your settings. * Make `/home/git/gitlab/config/unicorn.rb` the same as https://gitlab.com/gitlab-org/gitlab-ee/blob/7-12-stable-ee/config/unicorn.rb.example but with your settings.
* Make `/home/git/gitlab-shell/config.yml` the same as https://gitlab.com/gitlab-org/gitlab-shell/blob/v2.6.0/config.yml.example but with your settings. * Make `/home/git/gitlab-shell/config.yml` the same as https://gitlab.com/gitlab-org/gitlab-shell/blob/v2.6.0/config.yml.example but with your settings.
* Copy rack attack middleware config * Copy rack attack middleware config
...@@ -184,10 +184,15 @@ sudo cp lib/support/logrotate/gitlab /etc/logrotate.d/gitlab ...@@ -184,10 +184,15 @@ sudo cp lib/support/logrotate/gitlab /etc/logrotate.d/gitlab
### Change Nginx settings ### Change Nginx settings
* HTTP setups: Make `/etc/nginx/sites-available/gitlab` the same as https://gitlab.com/gitlab-org/gitlab-ee/blob/7-11-stable-ee/lib/support/nginx/gitlab but with your settings. * HTTP setups: Make `/etc/nginx/sites-available/gitlab` the same as https://gitlab.com/gitlab-org/gitlab-ee/blob/7-12-stable-ee/lib/support/nginx/gitlab but with your settings.
* HTTPS setups: Make `/etc/nginx/sites-available/gitlab-ssl` the same as https://gitlab.com/gitlab-org/gitlab-ee/blob/7-11-stable-ee/lib/support/nginx/gitlab-ssl but with your settings. * HTTPS setups: Make `/etc/nginx/sites-available/gitlab-ssl` the same as https://gitlab.com/gitlab-org/gitlab-ee/blob/7-12-stable-ee/lib/support/nginx/gitlab-ssl but with your settings.
* A new `location /uploads/` section has been added that needs to have the same content as the existing `location @gitlab` section. * A new `location /uploads/` section has been added that needs to have the same content as the existing `location @gitlab` section.
### Check the version of /usr/local/bin/git
If you installed Git from source into /usr/local/bin/git then please [check
your version](7.11-to-7.12.md).
## 9. Start application ## 9. Start application
sudo service gitlab start sudo service gitlab start
......
# From 7.11 to 7.12
### 0. Double-check your Git version
**This notice applies only to /usr/local/bin/git**
If you compiled Git from source on your GitLab server then please double-check
that you are using a version that protects against CVE-2014-9390. For six
months after this vulnerability became known the GitLab installation guide
still contained instructions that would install the outdated, 'vulnerable' Git
version 2.1.2.
Run the following command to get your current Git version.
```
/usr/local/bin/git --version
```
If you see 'No such file or directory' then you did not install Git according
to the outdated instructions from the GitLab installation guide and you can go
to the next step 'Stop server' below.
If you see a version string then it should be v1.8.5.6, v1.9.5, v2.0.5, v2.1.4,
v2.2.1 or newer. You can use the [instructions in the GitLab source
installation
guide](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/install/installation.md#1-packages-dependencies)
to install a newer version of Git.
### 1. Stop server
sudo service gitlab stop
### 2. Backup
```bash
cd /home/git/gitlab
sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production
```
### 3. Get latest code
```bash
sudo -u git -H git fetch --all
sudo -u git -H git checkout -- db/schema.rb # local changes will be restored automatically
```
For GitLab Community Edition:
```bash
sudo -u git -H git checkout 7-12-stable
```
OR
For GitLab Enterprise Edition:
```bash
sudo -u git -H git checkout 7-12-stable-ee
```
### 4. Update gitlab-shell
```bash
cd /home/git/gitlab-shell
sudo -u git -H git fetch
sudo -u git -H git checkout v2.6.3
```
### 5. Install libs, migrations, etc.
```bash
cd /home/git/gitlab
# MySQL installations (note: the line below states '--without ... postgres')
sudo -u git -H bundle install --without development test postgres --deployment
# PostgreSQL installations (note: the line below states '--without ... mysql')
sudo -u git -H bundle install --without development test mysql --deployment
# Run database migrations
sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
# Clean up assets and cache
sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS_ENV=production
# Update init.d script
sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab
```
### 6. Update config files
#### New configuration options for `gitlab.yml`
There are new configuration options available for [`gitlab.yml`](config/gitlab.yml.example). View them with the command below and apply them to your current `gitlab.yml`.
```
git diff origin/7-11-stable:config/gitlab.yml.example origin/7-12-stable:config/gitlab.yml.example
``````
### 7. Start application
sudo service gitlab start
sudo service nginx restart
### 8. Check application status
Check if GitLab and its environment are configured correctly:
sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production
To make sure you didn't miss anything run a more thorough check with:
sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production
If all items are green, then congratulations, the upgrade is complete!
## Things went south? Revert to previous version (7.11)
### 1. Revert the code to the previous version
Follow the [upgrade guide from 7.10 to 7.11](7.10-to-7.11.md), except for the database migration
(The backup is already migrated to the previous version)
### 2. Restore from the backup:
```bash
cd /home/git/gitlab
sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production
```
If you have more than one backup *.tar file(s) please add `BACKUP=timestamp_of_backup` to the command above.
# Workflow # Workflow
- [Feature branch workflow](workflow.md)
- [Project forking workflow](forking_workflow.md)
- [Project Features](project_features.md)
- [Authorization for merge requests](authorization_for_merge_requests.md)
- [Approval for merge requests](approval_for_merge_requests.md) - [Approval for merge requests](approval_for_merge_requests.md)
- [Rebase before merge](rebase_before_merge.md) - [Authorization for merge requests](authorization_for_merge_requests.md)
- [Change your time zone](timezone.md)
- [Feature branch workflow](workflow.md)
- [GitLab Flow](gitlab_flow.md)
- [Groups](groups.md) - [Groups](groups.md)
- [Share projects with other groups](share_projects_with_other_groups.md) - [Importing to GitLab](doc/importing/README.md)
- [Keyboard shortcuts](shortcuts.md)
- [Labels](labels.md) - [Labels](labels.md)
- [GitLab Flow](gitlab_flow.md) - [Manage large binaries with git annex](git_annex.md)
- [Notifications](notifications.md) - [Notifications](notifications.md)
- [Importing to GitLab](doc/importing/README.md) - [Project Features](project_features.md)
- [Two-factor Authentication (2FA)](two_factor_authentication.md) - [Project forking workflow](forking_workflow.md)
- [Protected branches](protected_branches.md) - [Protected branches](protected_branches.md)
- [Rebase before merge](rebase_before_merge.md)
- [Sharing a project with a group](share_with_group.md) - [Sharing a project with a group](share_with_group.md)
- [Change your time zone](timezone.md) - [Share projects with other groups](share_projects_with_other_groups.md)
- [Keyboard shortcuts](shortcuts.md) - [Two-factor Authentication (2FA)](two_factor_authentication.md)
- [Web Editor](web_editor.md) - [Web Editor](web_editor.md)
- [Manage large binaries with git annex](git_annex.md)
- ["Work In Progress" Merge Requests](wip_merge_requests.md) - ["Work In Progress" Merge Requests](wip_merge_requests.md)
...@@ -4,3 +4,6 @@ ...@@ -4,3 +4,6 @@
2. [GitHub](import_projects_from_github.md) 2. [GitHub](import_projects_from_github.md)
3. [GitLab.com](import_projects_from_gitlab_com.md) 3. [GitLab.com](import_projects_from_gitlab_com.md)
4. [SVN](migrating_from_svn.md) 4. [SVN](migrating_from_svn.md)
### Note
* If you'd like to migrate from a self-hosted GitLab instance to GitLab.com, you can copy your repos by changing the remote and pushing to the new server; but issues and merge requests can't be imported.
\ No newline at end of file
...@@ -18,9 +18,9 @@ Feature: Profile Active Tab ...@@ -18,9 +18,9 @@ Feature: Profile Active Tab
Then the active main tab should be SSH Keys Then the active main tab should be SSH Keys
And no other main tabs should be active And no other main tabs should be active
Scenario: On Profile Design Scenario: On Profile Preferences
Given I visit profile design page Given I visit profile preferences page
Then the active main tab should be Design Then the active main tab should be Preferences
And no other main tabs should be active And no other main tabs should be active
Scenario: On Profile History Scenario: On Profile History
......
...@@ -84,16 +84,3 @@ Feature: Profile ...@@ -84,16 +84,3 @@ Feature: Profile
Then I visit profile applications page Then I visit profile applications page
And I click to remove application And I click to remove application
Then I see that application is removed Then I see that application is removed
@javascript
Scenario: I change my application theme
Given I visit profile design page
When I change my application theme
Then I should see the theme change immediately
And I should receive feedback that the changes were saved
@javascript
Scenario: I change my code preview theme
Given I visit profile design page
When I change my code preview theme
Then I should receive feedback that the changes were saved
...@@ -77,3 +77,17 @@ Feature: Project Commits Diff Comments ...@@ -77,3 +77,17 @@ Feature: Project Commits Diff Comments
And I submit the diff comment And I submit the diff comment
Then I should not see the diff comment form Then I should not see the diff comment form
And I should see a discussion reply button And I should see a discussion reply button
@javascript
Scenario: I can add a comment on a side-by-side commit diff (left side)
Given I open a diff comment form
And I click side-by-side diff button
When I leave a diff comment in a parallel view on the left side like "Old comment"
Then I should see a diff comment on the left side saying "Old comment"
@javascript
Scenario: I can add a comment on a side-by-side commit diff (right side)
Given I open a diff comment form
And I click side-by-side diff button
When I leave a diff comment in a parallel view on the right side like "New comment"
Then I should see a diff comment on the right side saying "New comment"
...@@ -15,8 +15,8 @@ class Spinach::Features::ProfileActiveTab < Spinach::FeatureSteps ...@@ -15,8 +15,8 @@ class Spinach::Features::ProfileActiveTab < Spinach::FeatureSteps
ensure_active_main_tab('SSH Keys') ensure_active_main_tab('SSH Keys')
end end
step 'the active main tab should be Design' do step 'the active main tab should be Preferences' do
ensure_active_main_tab('Design') ensure_active_main_tab('Preferences')
end end
step 'the active main tab should be History' do step 'the active main tab should be History' do
......
...@@ -114,27 +114,6 @@ class Spinach::Features::Profile < Spinach::FeatureSteps ...@@ -114,27 +114,6 @@ class Spinach::Features::Profile < Spinach::FeatureSteps
expect(page).to have_content "#{current_user.name} closed issue" expect(page).to have_content "#{current_user.name} closed issue"
end end
step "I change my application theme" do
page.within '.application-theme' do
choose "Violet"
end
end
step "I change my code preview theme" do
page.within '.code-preview-theme' do
choose "Solarized dark"
end
end
step "I should see the theme change immediately" do
expect(page).to have_selector('body.ui_color')
expect(page).not_to have_selector('body.ui_basic')
end
step "I should receive feedback that the changes were saved" do
expect(page).to have_content("saved")
end
step 'my password is expired' do step 'my password is expired' do
current_user.update_attributes(password_expires_at: Time.now - 1.hour) current_user.update_attributes(password_expires_at: Time.now - 1.hour)
end end
...@@ -197,7 +176,7 @@ class Spinach::Features::Profile < Spinach::FeatureSteps ...@@ -197,7 +176,7 @@ class Spinach::Features::Profile < Spinach::FeatureSteps
end end
step 'I should see application form' do step 'I should see application form' do
expect(page).to have_content "New application" expect(page).to have_content "New Application"
end end
step 'I fill application form out and submit' do step 'I fill application form out and submit' do
......
...@@ -2,6 +2,7 @@ class Spinach::Features::ProjectCommits < Spinach::FeatureSteps ...@@ -2,6 +2,7 @@ class Spinach::Features::ProjectCommits < Spinach::FeatureSteps
include SharedAuthentication include SharedAuthentication
include SharedProject include SharedProject
include SharedPaths include SharedPaths
include SharedDiffNote
include RepoHelpers include RepoHelpers
step 'I see project commits' do step 'I see project commits' do
...@@ -88,14 +89,6 @@ class Spinach::Features::ProjectCommits < Spinach::FeatureSteps ...@@ -88,14 +89,6 @@ class Spinach::Features::ProjectCommits < Spinach::FeatureSteps
expect(links[1]['href']).to match %r{blob/#{sample_image_commit.new_blob_id}} expect(links[1]['href']).to match %r{blob/#{sample_image_commit.new_blob_id}}
end end
step 'I click side-by-side diff button' do
click_link "Side-by-side"
end
step 'I see side-by-side diff button' do
expect(page).to have_content "Side-by-side"
end
step 'I see inline diff button' do step 'I see inline diff button' do
expect(page).to have_content "Inline" expect(page).to have_content "Inline"
end end
......
...@@ -28,6 +28,22 @@ module SharedDiffNote ...@@ -28,6 +28,22 @@ module SharedDiffNote
end end
end end
step 'I leave a diff comment in a parallel view on the left side like "Old comment"' do
click_parallel_diff_line(sample_commit.line_code, 'old')
page.within("#{diff_file_selector} form[rel$='#{sample_commit.line_code}']") do
fill_in "note[note]", with: "Old comment"
find(".js-comment-button").trigger("click")
end
end
step 'I leave a diff comment in a parallel view on the right side like "New comment"' do
click_parallel_diff_line(sample_commit.line_code, 'new')
page.within("#{diff_file_selector} form[rel$='#{sample_commit.line_code}']") do
fill_in "note[note]", with: "New comment"
find(".js-comment-button").trigger("click")
end
end
step 'I preview a diff comment text like "Should fix it :smile:"' do step 'I preview a diff comment text like "Should fix it :smile:"' do
click_diff_line(sample_commit.line_code) click_diff_line(sample_commit.line_code)
page.within("#{diff_file_selector} form[rel$='#{sample_commit.line_code}']") do page.within("#{diff_file_selector} form[rel$='#{sample_commit.line_code}']") do
...@@ -102,6 +118,18 @@ module SharedDiffNote ...@@ -102,6 +118,18 @@ module SharedDiffNote
end end
end end
step 'I should see a diff comment on the left side saying "Old comment"' do
page.within("#{diff_file_selector} .notes_content.parallel.old") do
expect(page).to have_content("Old comment")
end
end
step 'I should see a diff comment on the right side saying "New comment"' do
page.within("#{diff_file_selector} .notes_content.parallel.new") do
expect(page).to have_content("New comment")
end
end
step 'I should see a discussion reply button' do step 'I should see a discussion reply button' do
page.within(diff_file_selector) do page.within(diff_file_selector) do
expect(page).to have_button('Reply') expect(page).to have_button('Reply')
...@@ -157,6 +185,14 @@ module SharedDiffNote ...@@ -157,6 +185,14 @@ module SharedDiffNote
end end
end end
step 'I click side-by-side diff button' do
click_link "Side-by-side"
end
step 'I see side-by-side diff button' do
expect(page).to have_content "Side-by-side"
end
def diff_file_selector def diff_file_selector
".diff-file:nth-of-type(1)" ".diff-file:nth-of-type(1)"
end end
...@@ -164,4 +200,8 @@ module SharedDiffNote ...@@ -164,4 +200,8 @@ module SharedDiffNote
def click_diff_line(code) def click_diff_line(code)
find("button[data-line-code='#{code}']").click find("button[data-line-code='#{code}']").click
end end
def click_parallel_diff_line(code, line_type)
find("button[data-line-code='#{code}'][data-line-type='#{line_type}']").trigger('click')
end
end end
...@@ -135,8 +135,8 @@ module SharedPaths ...@@ -135,8 +135,8 @@ module SharedPaths
visit profile_keys_path visit profile_keys_path
end end
step 'I visit profile design page' do step 'I visit profile preferences page' do
visit design_profile_path visit profile_preferences_path
end end
step 'I visit profile history page' do step 'I visit profile history page' do
......
...@@ -21,7 +21,8 @@ module Gitlab ...@@ -21,7 +21,8 @@ module Gitlab
gravatar_enabled: Settings.gravatar['enabled'], gravatar_enabled: Settings.gravatar['enabled'],
sign_in_text: Settings.extra['sign_in_text'], sign_in_text: Settings.extra['sign_in_text'],
restricted_visibility_levels: Settings.gitlab['restricted_visibility_levels'], restricted_visibility_levels: Settings.gitlab['restricted_visibility_levels'],
max_attachment_size: Settings.gitlab['max_attachment_size'] max_attachment_size: Settings.gitlab['max_attachment_size'],
session_expire_delay: Settings.gitlab['session_expire_delay']
) )
end end
end end
......
...@@ -27,7 +27,7 @@ module Gitlab ...@@ -27,7 +27,7 @@ module Gitlab
# Get latest 20 commits ASC # Get latest 20 commits ASC
commits_limited = commits.last(20) commits_limited = commits.last(20)
# For performance purposes maximum 20 latest commits # For performance purposes maximum 20 latest commits
# will be passed as post receive hook data. # will be passed as post receive hook data.
commit_attrs = commits_limited.map(&:hook_attrs) commit_attrs = commits_limited.map(&:hook_attrs)
...@@ -70,8 +70,11 @@ module Gitlab ...@@ -70,8 +70,11 @@ module Gitlab
end end
def checkout_sha(repository, newrev, ref) def checkout_sha(repository, newrev, ref)
# Checkout sha is nil when we remove branch or tag
return if Gitlab::Git.blank_ref?(newrev)
# Find sha for tag, except when it was deleted. # Find sha for tag, except when it was deleted.
if Gitlab::Git.tag_ref?(ref) && !Gitlab::Git.blank_ref?(newrev) if Gitlab::Git.tag_ref?(ref)
tag_name = Gitlab::Git.ref_name(ref) tag_name = Gitlab::Git.ref_name(ref)
tag = repository.find_tag(tag_name) tag = repository.find_tag(tag_name)
......
module Gitlab
class Theme
BASIC = 1 unless const_defined?(:BASIC)
MARS = 2 unless const_defined?(:MARS)
MODERN = 3 unless const_defined?(:MODERN)
GRAY = 4 unless const_defined?(:GRAY)
COLOR = 5 unless const_defined?(:COLOR)
BLUE = 6 unless const_defined?(:BLUE)
def self.classes
@classes ||= {
BASIC => 'ui_basic',
MARS => 'ui_mars',
MODERN => 'ui_modern',
GRAY => 'ui_gray',
COLOR => 'ui_color',
BLUE => 'ui_blue'
}
end
def self.css_class_by_id(id)
id ||= Gitlab.config.gitlab.default_theme
classes[id]
end
def self.types
@types ||= {
BASIC => 'light_theme',
MARS => 'dark_theme',
MODERN => 'dark_theme',
GRAY => 'dark_theme',
COLOR => 'dark_theme',
BLUE => 'light_theme'
}
end
def self.type_css_class_by_id(id)
id ||= Gitlab.config.gitlab.default_theme
types[id]
end
# 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 self.body_classes
(classes.values + types.values).uniq.join(' ')
end
end
end
module Gitlab
# Module containing GitLab's application theme definitions and helper methods
# for accessing them.
module Themes
# Theme ID used when no `default_theme` configuration setting is provided.
APPLICATION_DEFAULT = 2
# Struct class representing a single Theme
Theme = Struct.new(:id, :name, :css_class)
# All available Themes
THEMES = [
Theme.new(1, 'Graphite', 'ui_graphite'),
Theme.new(2, 'Charcoal', 'ui_charcoal'),
Theme.new(3, 'Green', 'ui_green'),
Theme.new(4, 'Gray', 'ui_gray'),
Theme.new(5, 'Violet', 'ui_violet'),
Theme.new(6, 'Blue', 'ui_blue')
].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 self.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 self.by_id(id)
THEMES.detect { |t| t.id == id } || default
end
# Get the default Theme
#
# Returns a Theme
def self.default
by_id(default_id)
end
# Iterate through each Theme
#
# Yields the Theme object
def self.each(&block)
THEMES.each(&block)
end
private
def self.default_id
id = Gitlab.config.gitlab.default_theme.to_i
# Prevent an invalid configuration setting from causing an infinite loop
if id < THEMES.first.id || id > THEMES.last.id
APPLICATION_DEFAULT
else
id
end
end
end
end
...@@ -73,7 +73,7 @@ server { ...@@ -73,7 +73,7 @@ server {
ssl_certificate_key /etc/nginx/ssl/gitlab.key; ssl_certificate_key /etc/nginx/ssl/gitlab.key;
# GitLab needs backwards compatible ciphers to retain compatibility with Java IDEs # GitLab needs backwards compatible ciphers to retain compatibility with Java IDEs
ssl_ciphers "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4"; ssl_ciphers "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4";
ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on; ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m; ssl_session_cache shared:SSL:10m;
......
...@@ -15,9 +15,9 @@ describe AutocompleteController do ...@@ -15,9 +15,9 @@ describe AutocompleteController do
let(:body) { JSON.parse(response.body) } let(:body) { JSON.parse(response.body) }
it { body.should be_kind_of(Array) } it { expect(body).to be_kind_of(Array) }
it { body.size.should eq(1) } it { expect(body.size).to eq(1) }
it { body.first["username"].should == user.username } it { expect(body.first["username"]).to eq user.username }
end end
context 'group members' do context 'group members' do
...@@ -32,9 +32,9 @@ describe AutocompleteController do ...@@ -32,9 +32,9 @@ describe AutocompleteController do
let(:body) { JSON.parse(response.body) } let(:body) { JSON.parse(response.body) }
it { body.should be_kind_of(Array) } it { expect(body).to be_kind_of(Array) }
it { body.size.should eq(1) } it { expect(body.size).to eq(1) }
it { body.first["username"].should == user.username } it { expect(body.first["username"]).to eq user.username }
end end
context 'all users' do context 'all users' do
...@@ -45,7 +45,7 @@ describe AutocompleteController do ...@@ -45,7 +45,7 @@ describe AutocompleteController do
let(:body) { JSON.parse(response.body) } let(:body) { JSON.parse(response.body) }
it { body.should be_kind_of(Array) } it { expect(body).to be_kind_of(Array) }
it { body.size.should eq(User.count) } it { expect(body.size).to eq(User.count) }
end end
end end
require 'spec_helper'
describe Profiles::PreferencesController do
let(:user) { create(:user) }
before do
sign_in(user)
allow(subject).to receive(:current_user).and_return(user)
end
describe 'GET show' do
it 'renders' do
get :show
expect(response).to render_template :show
end
it 'assigns user' do
get :show
expect(assigns[:user]).to eq user
end
end
describe 'PATCH update' do
def go(params: {}, format: :js)
params.reverse_merge!(
color_scheme_id: '1',
dashboard: 'stars',
theme_id: '1'
)
patch :update, user: params, format: format
end
context 'on successful update' do
it 'sets the flash' do
go
expect(flash[:notice]).to eq 'Preferences saved.'
end
it "changes the user's preferences" do
prefs = {
color_scheme_id: '1',
dashboard: 'stars',
theme_id: '2'
}.with_indifferent_access
expect(user).to receive(:update_attributes).with(prefs)
go params: prefs
end
end
context 'on failed update' do
it 'sets the flash' do
expect(user).to receive(:update_attributes).and_return(false)
go
expect(flash[:alert]).to eq('Failed to save preferences.')
end
end
context 'on invalid dashboard setting' do
it 'sets the flash' do
prefs = {dashboard: 'invalid'}
go params: prefs
expect(flash[:alert]).to match(/\AFailed to save preferences \(.+\)\.\z/)
end
end
context 'as js' do
it 'renders' do
go
expect(response).to render_template :update
end
end
context 'as html' do
it 'redirects' do
go format: :html
expect(response).to redirect_to(profile_preferences_path)
end
end
end
end
require 'spec_helper'
describe RootController do
describe 'GET show' do
context 'with a user' do
let(:user) { create(:user) }
before do
sign_in(user)
allow(subject).to receive(:current_user).and_return(user)
end
context 'who has customized their dashboard setting' do
before do
user.update_attribute(:dashboard, 'stars')
end
it 'redirects to their specified dashboard' do
get :show
expect(response).to redirect_to starred_dashboard_projects_path
end
end
context 'who uses the default dashboard setting' do
it 'renders the default dashboard' do
get :show
expect(response).to render_template 'dashboard/show'
end
end
end
end
end
...@@ -12,7 +12,7 @@ describe "Admin::Hooks", feature: true do ...@@ -12,7 +12,7 @@ describe "Admin::Hooks", feature: true do
describe "GET /admin/hooks" do describe "GET /admin/hooks" do
it "should be ok" do it "should be ok" do
visit admin_root_path visit admin_root_path
within ".sidebar-wrapper" do page.within ".sidebar-wrapper" do
click_on "Hooks" click_on "Hooks"
end end
expect(current_path).to eq(admin_hooks_path) expect(current_path).to eq(admin_hooks_path)
......
...@@ -311,10 +311,10 @@ describe 'Issues', feature: true do ...@@ -311,10 +311,10 @@ describe 'Issues', feature: true do
end end
def first_issue def first_issue
all('ul.issues-list li').first.text page.all('ul.issues-list li').first.text
end end
def last_issue def last_issue
all('ul.issues-list li').last.text page.all('ul.issues-list li').last.text
end end
end end
...@@ -22,20 +22,20 @@ describe 'Comments' do ...@@ -22,20 +22,20 @@ describe 'Comments' do
is_expected.to have_css('.js-main-target-form', visible: true, count: 1) is_expected.to have_css('.js-main-target-form', visible: true, count: 1)
expect(find('.js-main-target-form input[type=submit]').value). expect(find('.js-main-target-form input[type=submit]').value).
to eq('Add Comment') to eq('Add Comment')
within('.js-main-target-form') do page.within('.js-main-target-form') do
expect(page).not_to have_link('Cancel') expect(page).not_to have_link('Cancel')
end end
end end
describe 'with text' do describe 'with text' do
before do before do
within('.js-main-target-form') do page.within('.js-main-target-form') do
fill_in 'note[note]', with: 'This is awesome' fill_in 'note[note]', with: 'This is awesome'
end end
end end
it 'should have enable submit button and preview button' do it 'should have enable submit button and preview button' do
within('.js-main-target-form') do page.within('.js-main-target-form') do
expect(page).not_to have_css('.js-comment-button[disabled]') expect(page).not_to have_css('.js-comment-button[disabled]')
expect(page).to have_css('.js-md-preview-button', visible: true) expect(page).to have_css('.js-md-preview-button', visible: true)
end end
...@@ -45,7 +45,7 @@ describe 'Comments' do ...@@ -45,7 +45,7 @@ describe 'Comments' do
describe 'when posting a note' do describe 'when posting a note' do
before do before do
within('.js-main-target-form') do page.within('.js-main-target-form') do
fill_in 'note[note]', with: 'This is awsome!' fill_in 'note[note]', with: 'This is awsome!'
find('.js-md-preview-button').click find('.js-md-preview-button').click
click_button 'Add Comment' click_button 'Add Comment'
...@@ -54,11 +54,11 @@ describe 'Comments' do ...@@ -54,11 +54,11 @@ describe 'Comments' do
it 'should be added and form reset' do it 'should be added and form reset' do
is_expected.to have_content('This is awsome!') is_expected.to have_content('This is awsome!')
within('.js-main-target-form') do page.within('.js-main-target-form') do
expect(page).to have_no_field('note[note]', with: 'This is awesome!') expect(page).to have_no_field('note[note]', with: 'This is awesome!')
expect(page).to have_css('.js-md-preview', visible: :hidden) expect(page).to have_css('.js-md-preview', visible: :hidden)
end end
within('.js-main-target-form') do page.within('.js-main-target-form') do
is_expected.to have_css('.js-note-text', visible: true) is_expected.to have_css('.js-note-text', visible: true)
end end
end end
...@@ -66,7 +66,7 @@ describe 'Comments' do ...@@ -66,7 +66,7 @@ describe 'Comments' do
describe 'when editing a note', js: true do describe 'when editing a note', js: true do
it 'should contain the hidden edit form' do it 'should contain the hidden edit form' do
within("#note_#{note.id}") do page.within("#note_#{note.id}") do
is_expected.to have_css('.note-edit-form', visible: false) is_expected.to have_css('.note-edit-form', visible: false)
end end
end end
...@@ -78,7 +78,7 @@ describe 'Comments' do ...@@ -78,7 +78,7 @@ describe 'Comments' do
end end
it 'should show the note edit form and hide the note body' do it 'should show the note edit form and hide the note body' do
within("#note_#{note.id}") do page.within("#note_#{note.id}") do
expect(find('.current-note-edit-form', visible: true)).to be_visible expect(find('.current-note-edit-form', visible: true)).to be_visible
expect(find('.note-edit-form', visible: true)).to be_visible expect(find('.note-edit-form', visible: true)).to be_visible
expect(find(:css, '.note-body > .note-text', visible: false)).not_to be_visible expect(find(:css, '.note-body > .note-text', visible: false)).not_to be_visible
...@@ -90,17 +90,17 @@ describe 'Comments' do ...@@ -90,17 +90,17 @@ describe 'Comments' do
#within(".current-note-edit-form") do #within(".current-note-edit-form") do
#fill_in "note[note]", with: "Some new content" #fill_in "note[note]", with: "Some new content"
#find(".btn-cancel").click #find(".btn-cancel").click
#find(".js-note-text", visible: false).text.should == note.note #expect(find(".js-note-text", visible: false).text).to eq note.note
#end #end
#end #end
it 'appends the edited at time to the note' do it 'appends the edited at time to the note' do
within('.current-note-edit-form') do page.within('.current-note-edit-form') do
fill_in 'note[note]', with: 'Some new content' fill_in 'note[note]', with: 'Some new content'
find('.btn-save').click find('.btn-save').click
end end
within("#note_#{note.id}") do page.within("#note_#{note.id}") do
is_expected.to have_css('.note_edited_ago') is_expected.to have_css('.note_edited_ago')
expect(find('.note_edited_ago').text). expect(find('.note_edited_ago').text).
to match(/less than a minute ago/) to match(/less than a minute ago/)
...@@ -115,7 +115,7 @@ describe 'Comments' do ...@@ -115,7 +115,7 @@ describe 'Comments' do
end end
it 'shows the delete link' do it 'shows the delete link' do
within('.note-attachment') do page.within('.note-attachment') do
is_expected.to have_css('.js-note-attachment-delete') is_expected.to have_css('.js-note-attachment-delete')
end end
end end
...@@ -150,7 +150,7 @@ describe 'Comments' do ...@@ -150,7 +150,7 @@ describe 'Comments' do
it { is_expected.to have_css('.js-temp-notes-holder') } it { is_expected.to have_css('.js-temp-notes-holder') }
it 'has .new_note css class' do it 'has .new_note css class' do
within('.js-temp-notes-holder') do page.within('.js-temp-notes-holder') do
expect(subject).to have_css('.new_note') expect(subject).to have_css('.new_note')
end end
end end
...@@ -166,7 +166,7 @@ describe 'Comments' do ...@@ -166,7 +166,7 @@ describe 'Comments' do
end end
it 'should be removed when canceled' do it 'should be removed when canceled' do
within(".diff-file form[rel$='#{line_code}']") do page.within(".diff-file form[rel$='#{line_code}']") do
find('.js-close-discussion-note-form').trigger('click') find('.js-close-discussion-note-form').trigger('click')
end end
...@@ -186,11 +186,11 @@ describe 'Comments' do ...@@ -186,11 +186,11 @@ describe 'Comments' do
describe 'previewing them separately' do describe 'previewing them separately' do
before do before do
# add two separate texts and trigger previews on both # add two separate texts and trigger previews on both
within("tr[id='#{line_code}'] + .js-temp-notes-holder") do page.within("tr[id='#{line_code}'] + .js-temp-notes-holder") do
fill_in 'note[note]', with: 'One comment on line 7' fill_in 'note[note]', with: 'One comment on line 7'
find('.js-md-preview-button').click find('.js-md-preview-button').click
end end
within("tr[id='#{line_code_2}'] + .js-temp-notes-holder") do page.within("tr[id='#{line_code_2}'] + .js-temp-notes-holder") do
fill_in 'note[note]', with: 'Another comment on line 10' fill_in 'note[note]', with: 'Another comment on line 10'
find('.js-md-preview-button').click find('.js-md-preview-button').click
end end
...@@ -199,7 +199,7 @@ describe 'Comments' do ...@@ -199,7 +199,7 @@ describe 'Comments' do
describe 'posting a note' do describe 'posting a note' do
before do before do
within("tr[id='#{line_code_2}'] + .js-temp-notes-holder") do page.within("tr[id='#{line_code_2}'] + .js-temp-notes-holder") do
fill_in 'note[note]', with: 'Another comment on line 10' fill_in 'note[note]', with: 'Another comment on line 10'
click_button('Add Comment') click_button('Add Comment')
end end
......
require 'spec_helper'
describe 'Profile > Preferences' do
let(:user) { create(:user) }
before do
login_as(user)
visit profile_preferences_path
end
describe 'User changes their application theme', js: true do
let(:default) { Gitlab::Themes.default }
let(:theme) { Gitlab::Themes.by_id(5) }
it 'creates a flash message' do
choose "user_theme_id_#{theme.id}"
expect_preferences_saved_message
end
it 'updates their preference' do
choose "user_theme_id_#{theme.id}"
allowing_for_delay do
visit page.current_path
expect(page).to have_checked_field("user_theme_id_#{theme.id}")
end
end
it 'reflects the changes immediately' do
expect(page).to have_selector("body.#{default.css_class}")
choose "user_theme_id_#{theme.id}"
expect(page).not_to have_selector("body.#{default.css_class}")
expect(page).to have_selector("body.#{theme.css_class}")
end
end
describe 'User changes their syntax highlighting theme', js: true do
it 'creates a flash message' do
choose 'user_color_scheme_id_5'
expect_preferences_saved_message
end
it 'updates their preference' do
choose 'user_color_scheme_id_5'
allowing_for_delay do
visit page.current_path
expect(page).to have_checked_field('user_color_scheme_id_5')
end
end
end
describe 'User changes their default dashboard' do
it 'creates a flash message' do
select 'Starred Projects', from: 'user_dashboard'
click_button 'Save'
expect_preferences_saved_message
end
it 'updates their preference' do
select 'Starred Projects', from: 'user_dashboard'
click_button 'Save'
click_link 'Dashboard'
expect(page.current_path).to eq starred_dashboard_projects_path
click_link 'Your Projects'
expect(page.current_path).to eq dashboard_path
end
end
def expect_preferences_saved_message
within('.flash-container') do
expect(page).to have_content('Preferences saved.')
end
end
end
...@@ -7,7 +7,7 @@ describe "Search", feature: true do ...@@ -7,7 +7,7 @@ describe "Search", feature: true do
@project.team << [@user, :reporter] @project.team << [@user, :reporter]
visit search_path visit search_path
within '.search-holder' do page.within '.search-holder' do
fill_in "search", with: @project.name[0..3] fill_in "search", with: @project.name[0..3]
click_button "Search" click_button "Search"
end end
......
...@@ -36,8 +36,8 @@ describe "Profile access", feature: true do ...@@ -36,8 +36,8 @@ describe "Profile access", feature: true do
it { is_expected.to be_denied_for :visitor } it { is_expected.to be_denied_for :visitor }
end end
describe "GET /profile/design" do describe "GET /profile/preferences" do
subject { design_profile_path } subject { profile_preferences_path }
it { is_expected.to be_allowed_for @u1 } it { is_expected.to be_allowed_for @u1 }
it { is_expected.to be_allowed_for :admin } it { is_expected.to be_allowed_for :admin }
......
...@@ -185,27 +185,6 @@ describe ApplicationHelper do ...@@ -185,27 +185,6 @@ describe ApplicationHelper do
end end
end end
describe 'user_color_scheme_class' do
context 'with current_user is nil' do
it 'should return a string' do
allow(self).to receive(:current_user).and_return(nil)
expect(user_color_scheme_class).to be_kind_of(String)
end
end
context 'with a current_user' do
(1..5).each do |color_scheme_id|
context "with color_scheme_id == #{color_scheme_id}" do
it 'should return a string' do
current_user = double(:color_scheme_id => color_scheme_id)
allow(self).to receive(:current_user).and_return(current_user)
expect(user_color_scheme_class).to be_kind_of(String)
end
end
end
end
end
describe 'simple_sanitize' do describe 'simple_sanitize' do
let(:a_tag) { '<a href="#">Foo</a>' } let(:a_tag) { '<a href="#">Foo</a>' }
......
require 'spec_helper'
describe PreferencesHelper do
describe 'user_application_theme' do
context 'with a user' do
it "returns user's theme's css_class" do
user = double('user', theme_id: 3)
allow(self).to receive(:current_user).and_return(user)
expect(user_application_theme).to eq 'ui_green'
end
it 'returns the default when id is invalid' do
user = double('user', theme_id: Gitlab::Themes::THEMES.size + 5)
allow(Gitlab.config.gitlab).to receive(:default_theme).and_return(2)
allow(self).to receive(:current_user).and_return(user)
expect(user_application_theme).to eq 'ui_charcoal'
end
end
context 'without a user' do
before do
allow(self).to receive(:current_user).and_return(nil)
end
it 'returns the default theme' do
expect(user_application_theme).to eq Gitlab::Themes.default.css_class
end
end
end
describe 'dashboard_choices' do
it 'raises an exception when defined choices may be missing' do
expect(User).to receive(:dashboards).and_return(foo: 'foo')
expect { dashboard_choices }.to raise_error(RuntimeError)
end
it 'raises an exception when defined choices may be using the wrong key' do
expect(User).to receive(:dashboards).and_return(foo: 'foo', bar: 'bar')
expect { dashboard_choices }.to raise_error(KeyError)
end
it 'provides better option descriptions' do
expect(dashboard_choices).to match_array [
['Your Projects (default)', 'projects'],
['Starred Projects', 'stars']
]
end
end
describe 'user_color_scheme_class' do
context 'with current_user is nil' do
it 'should return a string' do
allow(self).to receive(:current_user).and_return(nil)
expect(user_color_scheme_class).to be_kind_of(String)
end
end
context 'with a current_user' do
(1..5).each do |color_scheme_id|
context "with color_scheme_id == #{color_scheme_id}" do
it 'should return a string' do
current_user = double(:color_scheme_id => color_scheme_id)
allow(self).to receive(:current_user).and_return(current_user)
expect(user_color_scheme_class).to be_kind_of(String)
end
end
end
end
end
end
...@@ -23,7 +23,7 @@ describe Gitlab::LDAP::Access do ...@@ -23,7 +23,7 @@ describe Gitlab::LDAP::Access do
it "should block user in GitLab" do it "should block user in GitLab" do
access.allowed? access.allowed?
user.should be_blocked expect(user).to be_blocked
end end
end end
...@@ -44,7 +44,7 @@ describe Gitlab::LDAP::Access do ...@@ -44,7 +44,7 @@ describe Gitlab::LDAP::Access do
it "does not unblock user in GitLab" do it "does not unblock user in GitLab" do
access.allowed? access.allowed?
user.should be_blocked expect(user).to be_blocked
end end
end end
...@@ -56,7 +56,7 @@ describe Gitlab::LDAP::Access do ...@@ -56,7 +56,7 @@ describe Gitlab::LDAP::Access do
it "should unblock user in GitLab" do it "should unblock user in GitLab" do
access.allowed? access.allowed?
user.should_not be_blocked expect(user).not_to be_blocked
end end
end end
end end
......
...@@ -36,6 +36,7 @@ describe 'Gitlab::NoteDataBuilder' do ...@@ -36,6 +36,7 @@ describe 'Gitlab::NoteDataBuilder' do
let(:note) { create(:note_on_issue, noteable_id: issue.id) } let(:note) { create(:note_on_issue, noteable_id: issue.id) }
it 'returns the note and issue-specific data' do it 'returns the note and issue-specific data' do
data[:issue]["updated_at"] = fixed_time
expect(data).to have_key(:issue) expect(data).to have_key(:issue)
expect(data[:issue]).to eq(issue.hook_attrs) expect(data[:issue]).to eq(issue.hook_attrs)
end end
...@@ -46,6 +47,7 @@ describe 'Gitlab::NoteDataBuilder' do ...@@ -46,6 +47,7 @@ describe 'Gitlab::NoteDataBuilder' do
let(:note) { create(:note_on_merge_request, noteable_id: merge_request.id) } let(:note) { create(:note_on_merge_request, noteable_id: merge_request.id) }
it 'returns the note and merge request data' do it 'returns the note and merge request data' do
data[:merge_request]["updated_at"] = fixed_time
expect(data).to have_key(:merge_request) expect(data).to have_key(:merge_request)
expect(data[:merge_request]).to eq(merge_request.hook_attrs) expect(data[:merge_request]).to eq(merge_request.hook_attrs)
end end
...@@ -56,6 +58,7 @@ describe 'Gitlab::NoteDataBuilder' do ...@@ -56,6 +58,7 @@ describe 'Gitlab::NoteDataBuilder' do
let(:note) { create(:note_on_merge_request_diff, noteable_id: merge_request.id) } let(:note) { create(:note_on_merge_request_diff, noteable_id: merge_request.id) }
it 'returns the note and merge request diff data' do it 'returns the note and merge request diff data' do
data[:merge_request]["updated_at"] = fixed_time
expect(data).to have_key(:merge_request) expect(data).to have_key(:merge_request)
expect(data[:merge_request]).to eq(merge_request.hook_attrs) expect(data[:merge_request]).to eq(merge_request.hook_attrs)
end end
...@@ -66,6 +69,7 @@ describe 'Gitlab::NoteDataBuilder' do ...@@ -66,6 +69,7 @@ describe 'Gitlab::NoteDataBuilder' do
let!(:note) { create(:note_on_project_snippet, noteable_id: snippet.id) } let!(:note) { create(:note_on_project_snippet, noteable_id: snippet.id) }
it 'returns the note and project snippet data' do it 'returns the note and project snippet data' do
data[:snippet]["updated_at"] = fixed_time
expect(data).to have_key(:snippet) expect(data).to have_key(:snippet)
expect(data[:snippet]).to eq(snippet.hook_attrs) expect(data[:snippet]).to eq(snippet.hook_attrs)
end end
......
...@@ -80,31 +80,31 @@ describe Gitlab::OAuth::AuthHash do ...@@ -80,31 +80,31 @@ describe Gitlab::OAuth::AuthHash do
context 'auth_hash constructed with ASCII-8BIT encoding' do context 'auth_hash constructed with ASCII-8BIT encoding' do
it 'forces utf8 encoding on uid' do it 'forces utf8 encoding on uid' do
auth_hash.uid.encoding.should eql Encoding::UTF_8 expect(auth_hash.uid.encoding).to eql Encoding::UTF_8
end end
it 'forces utf8 encoding on provider' do it 'forces utf8 encoding on provider' do
auth_hash.provider.encoding.should eql Encoding::UTF_8 expect(auth_hash.provider.encoding).to eql Encoding::UTF_8
end end
it 'forces utf8 encoding on name' do it 'forces utf8 encoding on name' do
auth_hash.name.encoding.should eql Encoding::UTF_8 expect(auth_hash.name.encoding).to eql Encoding::UTF_8
end end
it 'forces utf8 encoding on full_name' do it 'forces utf8 encoding on full_name' do
auth_hash.full_name.encoding.should eql Encoding::UTF_8 expect(auth_hash.full_name.encoding).to eql Encoding::UTF_8
end end
it 'forces utf8 encoding on username' do it 'forces utf8 encoding on username' do
auth_hash.username.encoding.should eql Encoding::UTF_8 expect(auth_hash.username.encoding).to eql Encoding::UTF_8
end end
it 'forces utf8 encoding on email' do it 'forces utf8 encoding on email' do
auth_hash.email.encoding.should eql Encoding::UTF_8 expect(auth_hash.email.encoding).to eql Encoding::UTF_8
end end
it 'forces utf8 encoding on password' do it 'forces utf8 encoding on password' do
auth_hash.password.encoding.should eql Encoding::UTF_8 expect(auth_hash.password.encoding).to eql Encoding::UTF_8
end end
end end
end end
require 'spec_helper'
describe Gitlab::Themes 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_graphite')
expect(css).to include(' ui_charcoal ')
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 'Graphite'
expect(described_class.by_id(6).name).to eq 'Blue'
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
# TODO (rspeicher): RSpec 3.x
# expect(described_class.each).to yield_with_arg(described_class::Theme)
end
end
end
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
# twitter_sharing_enabled :boolean default(TRUE) # twitter_sharing_enabled :boolean default(TRUE)
# restricted_visibility_levels :text # restricted_visibility_levels :text
# max_attachment_size :integer default(10), not null # max_attachment_size :integer default(10), not null
# session_expire_delay :integer default(10080), not null
# default_project_visibility :integer # default_project_visibility :integer
# default_snippet_visibility :integer # default_snippet_visibility :integer
# restricted_signup_domains :text # restricted_signup_domains :text
......
...@@ -52,7 +52,7 @@ describe ExternalWikiService do ...@@ -52,7 +52,7 @@ describe ExternalWikiService do
it 'should replace the wiki url' do it 'should replace the wiki url' do
wiki_path = get_project_wiki_path(project) wiki_path = get_project_wiki_path(project)
wiki_path.should match('https://gitlab.com') expect(wiki_path).to match('https://gitlab.com')
end end
end end
end end
......
...@@ -22,7 +22,7 @@ require 'spec_helper' ...@@ -22,7 +22,7 @@ require 'spec_helper'
describe Note do describe Note do
describe 'associations' do describe 'associations' do
it { is_expected.to belong_to(:project) } it { is_expected.to belong_to(:project) }
it { is_expected.to belong_to(:noteable) } it { is_expected.to belong_to(:noteable).touch(true) }
it { is_expected.to belong_to(:author).class_name('User') } it { is_expected.to belong_to(:author).class_name('User') }
end end
......
...@@ -58,7 +58,7 @@ describe GitlabCiService do ...@@ -58,7 +58,7 @@ describe GitlabCiService do
service_hook = double service_hook = double
service_hook.should_receive(:execute) service_hook.should_receive(:execute)
@service.should_receive(:service_hook).and_return(service_hook) @service.should_receive(:service_hook).and_return(service_hook)
@service.should_receive(:ci_yaml_file).with(push_sample_data) @service.should_receive(:ci_yaml_file).with(push_sample_data[:checkout_sha])
@service.execute(push_sample_data) @service.execute(push_sample_data)
end end
......
...@@ -43,7 +43,7 @@ describe IrkerService do ...@@ -43,7 +43,7 @@ describe IrkerService do
let(:_recipients) { 'a b c d' } let(:_recipients) { 'a b c d' }
it 'should add an error if there is too many recipients' do it 'should add an error if there is too many recipients' do
subject.send :check_recipients_count subject.send :check_recipients_count
subject.errors.should_not be_blank expect(subject.errors).not_to be_blank
end end
end end
...@@ -51,7 +51,7 @@ describe IrkerService do ...@@ -51,7 +51,7 @@ describe IrkerService do
let(:_recipients) { 'a b c' } let(:_recipients) { 'a b c' }
it 'should not add an error if there is 3 recipients' do it 'should not add an error if there is 3 recipients' do
subject.send :check_recipients_count subject.send :check_recipients_count
subject.errors.should be_blank expect(subject.errors).to be_blank
end end
end end
end end
...@@ -96,11 +96,11 @@ describe IrkerService do ...@@ -96,11 +96,11 @@ describe IrkerService do
conn = @irker_server.accept conn = @irker_server.accept
conn.readlines.each do |line| conn.readlines.each do |line|
msg = JSON.load(line.chomp("\n")) msg = JSON.load(line.chomp("\n"))
msg.keys.should match_array(['to', 'privmsg']) expect(msg.keys).to match_array(['to', 'privmsg'])
if msg['to'].is_a?(String) if msg['to'].is_a?(String)
msg['to'].should == 'irc://chat.freenode.net/#commits' expect(msg['to']).to eq 'irc://chat.freenode.net/#commits'
else else
msg['to'].should match_array(['irc://chat.freenode.net/#commits']) expect(msg['to']).to match_array(['irc://chat.freenode.net/#commits'])
end end
end end
conn.close conn.close
......
...@@ -50,12 +50,13 @@ ...@@ -50,12 +50,13 @@
# bitbucket_access_token :string(255) # bitbucket_access_token :string(255)
# bitbucket_access_token_secret :string(255) # bitbucket_access_token_secret :string(255)
# location :string(255) # location :string(255)
# public_email :string(255) default(""), not null
# encrypted_otp_secret :string(255) # encrypted_otp_secret :string(255)
# encrypted_otp_secret_iv :string(255) # encrypted_otp_secret_iv :string(255)
# encrypted_otp_secret_salt :string(255) # encrypted_otp_secret_salt :string(255)
# otp_required_for_login :boolean # otp_required_for_login :boolean
# otp_backup_codes :text # otp_backup_codes :text
# public_email :string(255) default(""), not null # dashboard :integer default(0)
# #
require 'spec_helper' require 'spec_helper'
...@@ -342,12 +343,12 @@ describe User do ...@@ -342,12 +343,12 @@ describe User do
end end
describe 'with default overrides' do describe 'with default overrides' do
let(:user) { User.new(projects_limit: 123, can_create_group: false, can_create_team: true, theme_id: Gitlab::Theme::BASIC) } let(:user) { User.new(projects_limit: 123, can_create_group: false, can_create_team: true, theme_id: 1) }
it "should apply defaults to user" do it "should apply 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(Gitlab::Theme::BASIC) expect(user.theme_id).to eq(1)
end end
end end
end end
......
...@@ -196,10 +196,10 @@ describe API::API, api: true do ...@@ -196,10 +196,10 @@ describe API::API, api: true do
it 'should return a project issue by iid' do it 'should return a project issue by iid' do
get api("/projects/#{project.id}/issues?iid=#{issue.iid}", user) get api("/projects/#{project.id}/issues?iid=#{issue.iid}", user)
response.status.should == 200 expect(response.status).to eq 200
json_response.first['title'].should == issue.title expect(json_response.first['title']).to eq issue.title
json_response.first['id'].should == issue.id expect(json_response.first['id']).to eq issue.id
json_response.first['iid'].should == issue.iid expect(json_response.first['iid']).to eq issue.iid
end end
it "should return 404 if issue id not found" do it "should return 404 if issue id not found" do
......
...@@ -118,9 +118,9 @@ describe API::API, api: true do ...@@ -118,9 +118,9 @@ describe API::API, api: true do
it 'should return merge_request by iid' do it 'should return merge_request by iid' do
url = "/projects/#{project.id}/merge_requests?iid=#{merge_request.iid}" url = "/projects/#{project.id}/merge_requests?iid=#{merge_request.iid}"
get api(url, user) get api(url, user)
response.status.should == 200 expect(response.status).to eq 200
json_response.first['title'].should == merge_request.title expect(json_response.first['title']).to eq merge_request.title
json_response.first['id'].should == merge_request.id expect(json_response.first['id']).to eq merge_request.id
end end
it "should return a 404 error if merge_request_id not found" do it "should return a 404 error if merge_request_id not found" do
......
...@@ -32,9 +32,9 @@ describe API::API, api: true do ...@@ -32,9 +32,9 @@ describe API::API, api: true do
it 'should return a project milestone by iid' do it 'should return a project milestone by iid' do
get api("/projects/#{project.id}/milestones?iid=#{milestone.iid}", user) get api("/projects/#{project.id}/milestones?iid=#{milestone.iid}", user)
response.status.should == 200 expect(response.status).to eq 200
json_response.first['title'].should == milestone.title expect(json_response.first['title']).to eq milestone.title
json_response.first['id'].should == milestone.id expect(json_response.first['id']).to eq milestone.id
end end
it 'should return 401 error if user not authenticated' do it 'should return 401 error if user not authenticated' do
......
...@@ -60,9 +60,9 @@ describe API::API, api: true do ...@@ -60,9 +60,9 @@ describe API::API, api: true do
it 'should include the project labels as the tag_list' do it 'should include the project labels as the tag_list' do
get api('/projects', user) get api('/projects', user)
response.status.should == 200 expect(response.status).to eq 200
json_response.should be_an Array expect(json_response).to be_an Array
json_response.first.keys.should include('tag_list') expect(json_response.first.keys).to include('tag_list')
end end
context 'and using search' do context 'and using search' do
......
...@@ -102,7 +102,6 @@ end ...@@ -102,7 +102,6 @@ end
# profile_token GET /profile/token(.:format) profile#token # profile_token GET /profile/token(.:format) profile#token
# profile_reset_private_token PUT /profile/reset_private_token(.:format) profile#reset_private_token # profile_reset_private_token PUT /profile/reset_private_token(.:format) profile#reset_private_token
# profile GET /profile(.:format) profile#show # profile GET /profile(.:format) profile#show
# profile_design GET /profile/design(.:format) profile#design
# profile_update PUT /profile/update(.:format) profile#update # profile_update PUT /profile/update(.:format) profile#update
describe ProfilesController, "routing" do describe ProfilesController, "routing" do
it "to #account" do it "to #account" do
...@@ -120,9 +119,19 @@ describe ProfilesController, "routing" do ...@@ -120,9 +119,19 @@ describe ProfilesController, "routing" do
it "to #show" do it "to #show" do
expect(get("/profile")).to route_to('profiles#show') expect(get("/profile")).to route_to('profiles#show')
end end
end
it "to #design" do # profile_preferences GET /profile/preferences(.:format) profiles/preferences#show
expect(get("/profile/design")).to route_to('profiles#design') # PATCH /profile/preferences(.:format) profiles/preferences#update
# PUT /profile/preferences(.:format) profiles/preferences#update
describe Profiles::PreferencesController, 'routing' do
it 'to #show' do
expect(get('/profile/preferences')).to route_to('profiles/preferences#show')
end
it 'to #update' do
expect(put('/profile/preferences')).to route_to('profiles/preferences#update')
expect(patch('/profile/preferences')).to route_to('profiles/preferences#update')
end end
end end
...@@ -195,11 +204,9 @@ end ...@@ -195,11 +204,9 @@ end
# dashboard GET /dashboard(.:format) dashboard#show # dashboard GET /dashboard(.:format) dashboard#show
# dashboard_issues GET /dashboard/issues(.:format) dashboard#issues # dashboard_issues GET /dashboard/issues(.:format) dashboard#issues
# dashboard_merge_requests GET /dashboard/merge_requests(.:format) dashboard#merge_requests # dashboard_merge_requests GET /dashboard/merge_requests(.:format) dashboard#merge_requests
# root / dashboard#show
describe DashboardController, "routing" do describe DashboardController, "routing" do
it "to #index" do it "to #index" do
expect(get("/dashboard")).to route_to('dashboard#show') expect(get("/dashboard")).to route_to('dashboard#show')
expect(get("/")).to route_to('dashboard#show')
end end
it "to #issues" do it "to #issues" do
...@@ -211,6 +218,14 @@ describe DashboardController, "routing" do ...@@ -211,6 +218,14 @@ describe DashboardController, "routing" do
end end
end end
# root / root#show
describe RootController, 'routing' do
it 'to #show' do
expect(get('/')).to route_to('root#show')
end
end
# new_user_session GET /users/sign_in(.:format) devise/sessions#new # new_user_session GET /users/sign_in(.:format) devise/sessions#new
# user_session POST /users/sign_in(.:format) devise/sessions#create # user_session POST /users/sign_in(.:format) devise/sessions#create
# destroy_user_session DELETE /users/sign_out(.:format) devise/sessions#destroy # destroy_user_session DELETE /users/sign_out(.:format) devise/sessions#destroy
......
...@@ -12,8 +12,8 @@ describe DestroyGroupService do ...@@ -12,8 +12,8 @@ describe DestroyGroupService do
destroy_group(group, user) destroy_group(group, user)
end end
it { Group.all.should_not include(group) } it { expect(Group.all).not_to include(group) }
it { Project.all.should_not include(project) } it { expect(Project.all).not_to include(project) }
end end
context 'file system' do context 'file system' do
...@@ -23,8 +23,8 @@ describe DestroyGroupService do ...@@ -23,8 +23,8 @@ describe DestroyGroupService do
Sidekiq::Testing.inline! { destroy_group(group, user) } Sidekiq::Testing.inline! { destroy_group(group, user) }
end end
it { gitlab_shell.exists?(group.path).should be_falsey } it { expect(gitlab_shell.exists?(group.path)).to be_falsey }
it { gitlab_shell.exists?(remove_path).should be_falsey } it { expect(gitlab_shell.exists?(remove_path)).to be_falsey }
end end
context 'Sidekiq fake' do context 'Sidekiq fake' do
...@@ -33,8 +33,8 @@ describe DestroyGroupService do ...@@ -33,8 +33,8 @@ describe DestroyGroupService do
Sidekiq::Testing.fake! { destroy_group(group, user) } Sidekiq::Testing.fake! { destroy_group(group, user) }
end end
it { gitlab_shell.exists?(group.path).should be_falsey } it { expect(gitlab_shell.exists?(group.path)).to be_falsey }
it { gitlab_shell.exists?(remove_path).should be_truthy } it { expect(gitlab_shell.exists?(remove_path)).to be_truthy }
end end
end end
......
...@@ -291,6 +291,15 @@ describe GitPushService do ...@@ -291,6 +291,15 @@ describe GitPushService do
).once ).once
end end
end end
it "doesn't close issues when external issue tracker is in use" do
allow(project).to receive(:default_issues_tracker?).and_return(false)
# The push still shouldn't create cross-reference notes.
expect {
service.execute(project, user, @oldrev, @newrev, 'refs/heads/hurf')
}.not_to change { Note.where(project_id: project.id, system: true).count }
end
end end
describe "empty project" do describe "empty project" do
......
...@@ -31,5 +31,15 @@ describe Issues::CloseService do ...@@ -31,5 +31,15 @@ describe Issues::CloseService do
expect(note.note).to include "Status changed to closed" expect(note.note).to include "Status changed to closed"
end end
end end
context "external issue tracker" do
before do
allow(project).to receive(:default_issues_tracker?).and_return(false)
@issue = Issues::CloseService.new(project, user, {}).execute(issue)
end
it { expect(@issue).to be_valid }
it { expect(@issue).to be_opened }
end
end end
end end
...@@ -12,9 +12,9 @@ describe Projects::DestroyService do ...@@ -12,9 +12,9 @@ describe Projects::DestroyService do
Sidekiq::Testing.inline! { destroy_project(project, user, {}) } Sidekiq::Testing.inline! { destroy_project(project, user, {}) }
end end
it { Project.all.should_not include(project) } it { expect(Project.all).not_to include(project) }
it { Dir.exists?(path).should be_falsey } it { expect(Dir.exists?(path)).to be_falsey }
it { Dir.exists?(remove_path).should be_falsey } it { expect(Dir.exists?(remove_path)).to be_falsey }
end end
context 'Sidekiq fake' do context 'Sidekiq fake' do
...@@ -23,9 +23,9 @@ describe Projects::DestroyService do ...@@ -23,9 +23,9 @@ describe Projects::DestroyService do
Sidekiq::Testing.fake! { destroy_project(project, user, {}) } Sidekiq::Testing.fake! { destroy_project(project, user, {}) }
end end
it { Project.all.should_not include(project) } it { expect(Project.all).not_to include(project) }
it { Dir.exists?(path).should be_falsey } it { expect(Dir.exists?(path)).to be_falsey }
it { Dir.exists?(remove_path).should be_truthy } it { expect(Dir.exists?(remove_path)).to be_truthy }
end end
def destroy_project(project, user, params) def destroy_project(project, user, params)
......
...@@ -20,8 +20,7 @@ describe Projects::TransferService do ...@@ -20,8 +20,7 @@ describe Projects::TransferService do
@result = transfer_project(project, user, new_namespace_id: nil) @result = transfer_project(project, user, new_namespace_id: nil)
end end
it { expect(@result).not_to be_nil } # { result.should be_false } passes on nil it { expect(@result).to eq false }
it { expect(@result).to be_falsey }
it { expect(project.namespace).to eq(user.namespace) } it { expect(project.namespace).to eq(user.namespace) }
end end
...@@ -30,8 +29,7 @@ describe Projects::TransferService do ...@@ -30,8 +29,7 @@ describe Projects::TransferService do
@result = transfer_project(project, user, new_namespace_id: group.id) @result = transfer_project(project, user, new_namespace_id: group.id)
end end
it { expect(@result).not_to be_nil } # { result.should be_false } passes on nil it { expect(@result).to eq false }
it { expect(@result).to be_falsey }
it { expect(project.namespace).to eq(user.namespace) } it { expect(project.namespace).to eq(user.namespace) }
end end
......
...@@ -338,6 +338,15 @@ describe SystemNoteService do ...@@ -338,6 +338,15 @@ describe SystemNoteService do
to be_falsey to be_falsey
end end
end end
context 'when notable is an ExternalIssue' do
let(:noteable) { ExternalIssue.new('EXT-1234', project) }
it 'is truthy' do
mentioner = noteable.dup
expect(described_class.cross_reference_disallowed?(noteable, mentioner)).
to be_truthy
end
end
end end
describe '.cross_reference_exists?' do describe '.cross_reference_exists?' do
......
...@@ -19,3 +19,36 @@ unless ENV['CI'] || ENV['CI_SERVER'] ...@@ -19,3 +19,36 @@ unless ENV['CI'] || ENV['CI_SERVER']
# Keep only the screenshots generated from the last failing test suite # Keep only the screenshots generated from the last failing test suite
Capybara::Screenshot.prune_strategy = :keep_last_run Capybara::Screenshot.prune_strategy = :keep_last_run
end end
module CapybaraHelpers
# Execute a block a certain number of times before considering it a failure
#
# The given block is called, and if it raises a `Capybara::ExpectationNotMet`
# error, we wait `interval` seconds and then try again, until `retries` is
# met.
#
# This allows for better handling of timing-sensitive expectations in a
# sketchy CI environment, for example.
#
# interval - Delay between retries in seconds (default: 0.5)
# retries - Number of times to execute before failing (default: 5)
def allowing_for_delay(interval: 0.5, retries: 5)
tries = 0
begin
yield
rescue Capybara::ExpectationNotMet => ex
if tries <= retries
tries += 1
sleep interval
retry
else
raise ex
end
end
end
end
RSpec.configure do |config|
config.include CapybaraHelpers, type: :feature
end
module LoginHelpers module LoginHelpers
# Internal: Create and log in as a user of the specified role # Internal: Log in as a specific user or a new user of a specific role
# #
# role - User role (e.g., :admin, :user) # user_or_role - User object, or a role to create (e.g., :admin, :user)
def login_as(role) #
@user = create(role) # Examples:
#
# # Create a user automatically
# login_as(:user)
#
# # Create an admin automatically
# login_as(:admin)
#
# # Provide an existing User record
# user = create(:user)
# login_as(user)
def login_as(user_or_role)
if user_or_role.kind_of?(User)
@user = user_or_role
else
@user = create(user_or_role)
end
login_with(@user) login_with(@user)
end end
......
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