Merge branch 'master' into adding_crime_security
This commit is contained in:
commit
05f8c585f7
26
CHANGELOG
26
CHANGELOG
|
@ -1,6 +1,26 @@
|
||||||
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 8.3.0 (unreleased)
|
v 8.4.0 (unreleased)
|
||||||
|
- Implement new UI for group page
|
||||||
|
- Implement search inside emoji picker
|
||||||
|
- Add API support for looking up a user by username (Stan Hu)
|
||||||
|
- Add project permissions to all project API endpoints (Stan Hu)
|
||||||
|
- Expose Git's version in the admin area
|
||||||
|
- Add "Frequently used" category to emoji picker
|
||||||
|
- Add CAS support (tduehr)
|
||||||
|
- Add link to merge request on build detail page.
|
||||||
|
|
||||||
|
v 8.3.2 (unreleased)
|
||||||
|
- Enable "Add key" button when user fills in a proper key
|
||||||
|
|
||||||
|
v 8.3.1
|
||||||
|
- Fix Error 500 when global milestones have slashes (Stan Hu)
|
||||||
|
- Fix Error 500 when doing a search in dashboard before visiting any project (Stan Hu)
|
||||||
|
- Fix LDAP identity and user retrieval when special characters are used
|
||||||
|
- Move Sidekiq-cron configuration to gitlab.yml
|
||||||
|
- Enable forcing Two-Factor authentication sitewide, with optional grace period
|
||||||
|
|
||||||
|
v 8.3.0
|
||||||
- Bump rack-attack to 4.3.1 for security fix (Stan Hu)
|
- Bump rack-attack to 4.3.1 for security fix (Stan Hu)
|
||||||
- API support for starred projects for authorized user (Zeger-Jan van de Weg)
|
- API support for starred projects for authorized user (Zeger-Jan van de Weg)
|
||||||
- Add open_issues_count to project API (Stan Hu)
|
- Add open_issues_count to project API (Stan Hu)
|
||||||
|
@ -8,6 +28,8 @@ v 8.3.0 (unreleased)
|
||||||
- Add button to automatically merge a merge request when the build succeeds (Zeger-Jan van de Weg)
|
- Add button to automatically merge a merge request when the build succeeds (Zeger-Jan van de Weg)
|
||||||
- Provide better diagnostic message upon project creation errors (Stan Hu)
|
- Provide better diagnostic message upon project creation errors (Stan Hu)
|
||||||
- Bump devise to 3.5.3 to fix reset token expiring after account creation (Stan Hu)
|
- Bump devise to 3.5.3 to fix reset token expiring after account creation (Stan Hu)
|
||||||
|
- Remove api credentials from link to build_page
|
||||||
|
- Deprecate GitLabCiService making it to always be inactive
|
||||||
- Bump gollum-lib to 4.1.0 (Stan Hu)
|
- Bump gollum-lib to 4.1.0 (Stan Hu)
|
||||||
- Fix broken group avatar upload under "New group" (Stan Hu)
|
- Fix broken group avatar upload under "New group" (Stan Hu)
|
||||||
- Update project repositorize size and commit count during import:repos task (Stan Hu)
|
- Update project repositorize size and commit count during import:repos task (Stan Hu)
|
||||||
|
@ -19,8 +41,10 @@ v 8.3.0 (unreleased)
|
||||||
- Fix 500 error when update group member permission
|
- Fix 500 error when update group member permission
|
||||||
- Trim leading and trailing whitespace of milestone and issueable titles (Jose Corcuera)
|
- Trim leading and trailing whitespace of milestone and issueable titles (Jose Corcuera)
|
||||||
- Recognize issue/MR/snippet/commit links as references
|
- Recognize issue/MR/snippet/commit links as references
|
||||||
|
- Backport JIRA features from EE to CE
|
||||||
- Add ignore whitespace change option to commit view
|
- Add ignore whitespace change option to commit view
|
||||||
- Fire update hook from GitLab
|
- Fire update hook from GitLab
|
||||||
|
- Allow account unlock via email
|
||||||
- Style warning about mentioning many people in a comment
|
- Style warning about mentioning many people in a comment
|
||||||
- Fix: sort milestones by due date once again (Greg Smethells)
|
- Fix: sort milestones by due date once again (Greg Smethells)
|
||||||
- Migrate all CI::Services and CI::WebHooks to Services and WebHooks
|
- Migrate all CI::Services and CI::WebHooks to Services and WebHooks
|
||||||
|
|
|
@ -358,7 +358,7 @@ available at [http://contributor-covenant.org/version/1/1/0/](http://contributor
|
||||||
[core team]: https://about.gitlab.com/core-team/
|
[core team]: https://about.gitlab.com/core-team/
|
||||||
[getting help page]: https://about.gitlab.com/getting-help/
|
[getting help page]: https://about.gitlab.com/getting-help/
|
||||||
[Codetriage]: http://www.codetriage.com/gitlabhq/gitlabhq
|
[Codetriage]: http://www.codetriage.com/gitlabhq/gitlabhq
|
||||||
[up-for-grabs]: https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name=up+for+grabs
|
[up-for-grabs]: https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name=up-for-grabs
|
||||||
[medium-up-for-grabs]: https://medium.com/@kentcdodds/first-timers-only-78281ea47455
|
[medium-up-for-grabs]: https://medium.com/@kentcdodds/first-timers-only-78281ea47455
|
||||||
[ce-tracker]: https://gitlab.com/gitlab-org/gitlab-ce/issues
|
[ce-tracker]: https://gitlab.com/gitlab-org/gitlab-ce/issues
|
||||||
[ee-tracker]: https://gitlab.com/gitlab-org/gitlab-ee/issues
|
[ee-tracker]: https://gitlab.com/gitlab-org/gitlab-ee/issues
|
||||||
|
|
12
Gemfile
12
Gemfile
|
@ -23,6 +23,7 @@ gem 'devise-async', '~> 0.9.0'
|
||||||
gem 'doorkeeper', '~> 2.2.0'
|
gem 'doorkeeper', '~> 2.2.0'
|
||||||
gem 'omniauth', '~> 1.2.2'
|
gem 'omniauth', '~> 1.2.2'
|
||||||
gem 'omniauth-bitbucket', '~> 0.0.2'
|
gem 'omniauth-bitbucket', '~> 0.0.2'
|
||||||
|
gem 'omniauth-cas3', '~> 1.1.2'
|
||||||
gem 'omniauth-facebook', '~> 3.0.0'
|
gem 'omniauth-facebook', '~> 3.0.0'
|
||||||
gem 'omniauth-github', '~> 1.1.1'
|
gem 'omniauth-github', '~> 1.1.1'
|
||||||
gem 'omniauth-gitlab', '~> 1.0.0'
|
gem 'omniauth-gitlab', '~> 1.0.0'
|
||||||
|
@ -101,6 +102,9 @@ gem 'wikicloth', '0.8.1'
|
||||||
gem 'asciidoctor', '~> 1.5.2'
|
gem 'asciidoctor', '~> 1.5.2'
|
||||||
gem 'rouge', '~> 1.10.1'
|
gem 'rouge', '~> 1.10.1'
|
||||||
|
|
||||||
|
# See https://groups.google.com/forum/#!topic/ruby-security-ann/aSbgDiwb24s
|
||||||
|
gem 'nokogiri', '1.6.7.1'
|
||||||
|
|
||||||
# Diffs
|
# Diffs
|
||||||
gem 'diffy', '~> 3.0.3'
|
gem 'diffy', '~> 3.0.3'
|
||||||
|
|
||||||
|
@ -168,7 +172,7 @@ gem 'd3_rails', '~> 3.5.5'
|
||||||
gem "cal-heatmap-rails", "~> 0.0.1"
|
gem "cal-heatmap-rails", "~> 0.0.1"
|
||||||
|
|
||||||
# underscore-rails
|
# underscore-rails
|
||||||
gem "underscore-rails", "~> 1.4.4"
|
gem "underscore-rails", "~> 1.8.0"
|
||||||
|
|
||||||
# Sanitize user input
|
# Sanitize user input
|
||||||
gem "sanitize", '~> 2.0'
|
gem "sanitize", '~> 2.0'
|
||||||
|
@ -186,7 +190,7 @@ gem 'mousetrap-rails', '~> 1.4.6'
|
||||||
# Detect and convert string character encoding
|
# Detect and convert string character encoding
|
||||||
gem 'charlock_holmes', '~> 0.7.3'
|
gem 'charlock_holmes', '~> 0.7.3'
|
||||||
|
|
||||||
gem "sass-rails", '~> 4.0.5'
|
gem "sass-rails", '~> 5.0.0'
|
||||||
gem "coffee-rails", '~> 4.1.0'
|
gem "coffee-rails", '~> 4.1.0'
|
||||||
gem "uglifier", '~> 2.7.2'
|
gem "uglifier", '~> 2.7.2'
|
||||||
gem 'turbolinks', '~> 2.5.0'
|
gem 'turbolinks', '~> 2.5.0'
|
||||||
|
@ -198,9 +202,9 @@ gem 'font-awesome-rails', '~> 4.2'
|
||||||
gem 'gitlab_emoji', '~> 0.2.0'
|
gem 'gitlab_emoji', '~> 0.2.0'
|
||||||
gem 'gon', '~> 6.0.1'
|
gem 'gon', '~> 6.0.1'
|
||||||
gem 'jquery-atwho-rails', '~> 1.3.2'
|
gem 'jquery-atwho-rails', '~> 1.3.2'
|
||||||
gem 'jquery-rails', '~> 3.1.3'
|
gem 'jquery-rails', '~> 4.0.0'
|
||||||
gem 'jquery-scrollto-rails', '~> 1.4.3'
|
gem 'jquery-scrollto-rails', '~> 1.4.3'
|
||||||
gem 'jquery-ui-rails', '~> 4.2.1'
|
gem 'jquery-ui-rails', '~> 5.0.0'
|
||||||
gem 'nprogress-rails', '~> 0.1.6.7'
|
gem 'nprogress-rails', '~> 0.1.6.7'
|
||||||
gem 'raphael-rails', '~> 2.1.2'
|
gem 'raphael-rails', '~> 2.1.2'
|
||||||
gem 'request_store', '~> 1.2.0'
|
gem 'request_store', '~> 1.2.0'
|
||||||
|
|
36
Gemfile.lock
36
Gemfile.lock
|
@ -372,15 +372,16 @@ GEM
|
||||||
inflecto (0.0.2)
|
inflecto (0.0.2)
|
||||||
ipaddress (0.8.0)
|
ipaddress (0.8.0)
|
||||||
jquery-atwho-rails (1.3.2)
|
jquery-atwho-rails (1.3.2)
|
||||||
jquery-rails (3.1.4)
|
jquery-rails (4.0.5)
|
||||||
railties (>= 3.0, < 5.0)
|
rails-dom-testing (~> 1.0)
|
||||||
|
railties (>= 4.2.0)
|
||||||
thor (>= 0.14, < 2.0)
|
thor (>= 0.14, < 2.0)
|
||||||
jquery-scrollto-rails (1.4.3)
|
jquery-scrollto-rails (1.4.3)
|
||||||
railties (> 3.1, < 5.0)
|
railties (> 3.1, < 5.0)
|
||||||
jquery-turbolinks (2.1.0)
|
jquery-turbolinks (2.1.0)
|
||||||
railties (>= 3.1.0)
|
railties (>= 3.1.0)
|
||||||
turbolinks
|
turbolinks
|
||||||
jquery-ui-rails (4.2.1)
|
jquery-ui-rails (5.0.5)
|
||||||
railties (>= 3.2.16)
|
railties (>= 3.2.16)
|
||||||
json (1.8.3)
|
json (1.8.3)
|
||||||
jwt (1.5.2)
|
jwt (1.5.2)
|
||||||
|
@ -420,7 +421,7 @@ GEM
|
||||||
grape
|
grape
|
||||||
newrelic_rpm
|
newrelic_rpm
|
||||||
newrelic_rpm (3.9.4.245)
|
newrelic_rpm (3.9.4.245)
|
||||||
nokogiri (1.6.7)
|
nokogiri (1.6.7.1)
|
||||||
mini_portile2 (~> 2.0.0.rc2)
|
mini_portile2 (~> 2.0.0.rc2)
|
||||||
nprogress-rails (0.1.6.7)
|
nprogress-rails (0.1.6.7)
|
||||||
oauth (0.4.7)
|
oauth (0.4.7)
|
||||||
|
@ -439,6 +440,10 @@ GEM
|
||||||
multi_json (~> 1.7)
|
multi_json (~> 1.7)
|
||||||
omniauth (~> 1.1)
|
omniauth (~> 1.1)
|
||||||
omniauth-oauth (~> 1.0)
|
omniauth-oauth (~> 1.0)
|
||||||
|
omniauth-cas3 (1.1.3)
|
||||||
|
addressable (~> 2.3)
|
||||||
|
nokogiri (~> 1.6.6)
|
||||||
|
omniauth (~> 1.2)
|
||||||
omniauth-facebook (3.0.0)
|
omniauth-facebook (3.0.0)
|
||||||
omniauth-oauth2 (~> 1.2)
|
omniauth-oauth2 (~> 1.2)
|
||||||
omniauth-github (1.1.2)
|
omniauth-github (1.1.2)
|
||||||
|
@ -643,12 +648,13 @@ GEM
|
||||||
safe_yaml (1.0.4)
|
safe_yaml (1.0.4)
|
||||||
sanitize (2.1.0)
|
sanitize (2.1.0)
|
||||||
nokogiri (>= 1.4.4)
|
nokogiri (>= 1.4.4)
|
||||||
sass (3.2.19)
|
sass (3.4.20)
|
||||||
sass-rails (4.0.5)
|
sass-rails (5.0.4)
|
||||||
railties (>= 4.0.0, < 5.0)
|
railties (>= 4.0.0, < 5.0)
|
||||||
sass (~> 3.2.2)
|
sass (~> 3.1)
|
||||||
sprockets (~> 2.8, < 3.0)
|
sprockets (>= 2.8, < 4.0)
|
||||||
sprockets-rails (~> 2.0)
|
sprockets-rails (>= 2.0, < 4.0)
|
||||||
|
tilt (>= 1.1, < 3)
|
||||||
sawyer (0.6.0)
|
sawyer (0.6.0)
|
||||||
addressable (~> 2.3.5)
|
addressable (~> 2.3.5)
|
||||||
faraday (~> 0.8, < 0.10)
|
faraday (~> 0.8, < 0.10)
|
||||||
|
@ -763,7 +769,7 @@ GEM
|
||||||
uglifier (2.7.2)
|
uglifier (2.7.2)
|
||||||
execjs (>= 0.3.0)
|
execjs (>= 0.3.0)
|
||||||
json (>= 1.8.0)
|
json (>= 1.8.0)
|
||||||
underscore-rails (1.4.4)
|
underscore-rails (1.8.3)
|
||||||
unf (0.1.4)
|
unf (0.1.4)
|
||||||
unf_ext
|
unf_ext
|
||||||
unf_ext (0.0.7.1)
|
unf_ext (0.0.7.1)
|
||||||
|
@ -874,10 +880,10 @@ DEPENDENCIES
|
||||||
html-pipeline (~> 1.11.0)
|
html-pipeline (~> 1.11.0)
|
||||||
httparty (~> 0.13.3)
|
httparty (~> 0.13.3)
|
||||||
jquery-atwho-rails (~> 1.3.2)
|
jquery-atwho-rails (~> 1.3.2)
|
||||||
jquery-rails (~> 3.1.3)
|
jquery-rails (~> 4.0.0)
|
||||||
jquery-scrollto-rails (~> 1.4.3)
|
jquery-scrollto-rails (~> 1.4.3)
|
||||||
jquery-turbolinks (~> 2.1.0)
|
jquery-turbolinks (~> 2.1.0)
|
||||||
jquery-ui-rails (~> 4.2.1)
|
jquery-ui-rails (~> 5.0.0)
|
||||||
kaminari (~> 0.16.3)
|
kaminari (~> 0.16.3)
|
||||||
letter_opener (~> 1.1.2)
|
letter_opener (~> 1.1.2)
|
||||||
mail_room (~> 0.6.1)
|
mail_room (~> 0.6.1)
|
||||||
|
@ -888,11 +894,13 @@ DEPENDENCIES
|
||||||
net-ssh (~> 3.0.1)
|
net-ssh (~> 3.0.1)
|
||||||
newrelic-grape
|
newrelic-grape
|
||||||
newrelic_rpm (~> 3.9.4.245)
|
newrelic_rpm (~> 3.9.4.245)
|
||||||
|
nokogiri (= 1.6.7.1)
|
||||||
nprogress-rails (~> 0.1.6.7)
|
nprogress-rails (~> 0.1.6.7)
|
||||||
oauth2 (~> 1.0.0)
|
oauth2 (~> 1.0.0)
|
||||||
octokit (~> 3.7.0)
|
octokit (~> 3.7.0)
|
||||||
omniauth (~> 1.2.2)
|
omniauth (~> 1.2.2)
|
||||||
omniauth-bitbucket (~> 0.0.2)
|
omniauth-bitbucket (~> 0.0.2)
|
||||||
|
omniauth-cas3 (~> 1.1.2)
|
||||||
omniauth-facebook (~> 3.0.0)
|
omniauth-facebook (~> 3.0.0)
|
||||||
omniauth-github (~> 1.1.1)
|
omniauth-github (~> 1.1.1)
|
||||||
omniauth-gitlab (~> 1.0.0)
|
omniauth-gitlab (~> 1.0.0)
|
||||||
|
@ -928,7 +936,7 @@ DEPENDENCIES
|
||||||
rubocop (~> 0.35.0)
|
rubocop (~> 0.35.0)
|
||||||
ruby-fogbugz (~> 0.2.1)
|
ruby-fogbugz (~> 0.2.1)
|
||||||
sanitize (~> 2.0)
|
sanitize (~> 2.0)
|
||||||
sass-rails (~> 4.0.5)
|
sass-rails (~> 5.0.0)
|
||||||
sdoc (~> 0.3.20)
|
sdoc (~> 0.3.20)
|
||||||
seed-fu (~> 2.3.5)
|
seed-fu (~> 2.3.5)
|
||||||
select2-rails (~> 3.5.9)
|
select2-rails (~> 3.5.9)
|
||||||
|
@ -957,7 +965,7 @@ DEPENDENCIES
|
||||||
tinder (~> 1.10.0)
|
tinder (~> 1.10.0)
|
||||||
turbolinks (~> 2.5.0)
|
turbolinks (~> 2.5.0)
|
||||||
uglifier (~> 2.7.2)
|
uglifier (~> 2.7.2)
|
||||||
underscore-rails (~> 1.4.4)
|
underscore-rails (~> 1.8.0)
|
||||||
unf (~> 0.1.4)
|
unf (~> 0.1.4)
|
||||||
unicorn (~> 4.8.2)
|
unicorn (~> 4.8.2)
|
||||||
unicorn-worker-killer (~> 0.4.2)
|
unicorn-worker-killer (~> 0.4.2)
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 813 KiB |
|
@ -5,7 +5,7 @@
|
||||||
# the compiled file.
|
# the compiled file.
|
||||||
#
|
#
|
||||||
#= require jquery
|
#= require jquery
|
||||||
#= require jquery.ui.all
|
#= require jquery-ui
|
||||||
#= require jquery_ujs
|
#= require jquery_ujs
|
||||||
#= require jquery.cookie
|
#= require jquery.cookie
|
||||||
#= require jquery.endless-scroll
|
#= require jquery.endless-scroll
|
||||||
|
|
|
@ -1,12 +1,28 @@
|
||||||
class @AwardsHandler
|
class @AwardsHandler
|
||||||
constructor: (@post_emoji_url, @noteable_type, @noteable_id, @aliases) ->
|
constructor: (@post_emoji_url, @noteable_type, @noteable_id, @aliases) ->
|
||||||
|
$(".add-award").click (event)->
|
||||||
|
event.stopPropagation()
|
||||||
|
event.preventDefault()
|
||||||
|
$(".emoji-menu").show()
|
||||||
|
|
||||||
|
$("html").click ->
|
||||||
|
if !$(event.target).closest(".emoji-menu").length
|
||||||
|
if $(".emoji-menu").is(":visible")
|
||||||
|
$(".emoji-menu").hide()
|
||||||
|
|
||||||
|
@renderFrequentlyUsedBlock()
|
||||||
|
@setupSearch()
|
||||||
|
|
||||||
addAward: (emoji) ->
|
addAward: (emoji) ->
|
||||||
emoji = @normilizeEmojiName(emoji)
|
emoji = @normilizeEmojiName(emoji)
|
||||||
@postEmoji emoji, =>
|
@postEmoji emoji, =>
|
||||||
@addAwardToEmojiBar(emoji)
|
@addAwardToEmojiBar(emoji)
|
||||||
|
|
||||||
|
$(".emoji-menu").hide()
|
||||||
|
|
||||||
addAwardToEmojiBar: (emoji, custom_path = '') ->
|
addAwardToEmojiBar: (emoji) ->
|
||||||
|
@addEmojiToFrequentlyUsedList(emoji)
|
||||||
|
|
||||||
emoji = @normilizeEmojiName(emoji)
|
emoji = @normilizeEmojiName(emoji)
|
||||||
if @exist(emoji)
|
if @exist(emoji)
|
||||||
if @isActive(emoji)
|
if @isActive(emoji)
|
||||||
|
@ -17,7 +33,7 @@ class @AwardsHandler
|
||||||
counter.parent().addClass("active")
|
counter.parent().addClass("active")
|
||||||
@addMeToAuthorList(emoji)
|
@addMeToAuthorList(emoji)
|
||||||
else
|
else
|
||||||
@createEmoji(emoji, custom_path)
|
@createEmoji(emoji)
|
||||||
|
|
||||||
exist: (emoji) ->
|
exist: (emoji) ->
|
||||||
@findEmojiIcon(emoji).length > 0
|
@findEmojiIcon(emoji).length > 0
|
||||||
|
@ -54,35 +70,39 @@ class @AwardsHandler
|
||||||
resetTooltip: (award) ->
|
resetTooltip: (award) ->
|
||||||
award.tooltip("destroy")
|
award.tooltip("destroy")
|
||||||
|
|
||||||
# "destroy" call is asynchronous, this is why we need to set timeout.
|
# "destroy" call is asynchronous and there is no appropriate callback on it, this is why we need to set timeout.
|
||||||
setTimeout (->
|
setTimeout (->
|
||||||
award.tooltip()
|
award.tooltip()
|
||||||
), 200
|
), 200
|
||||||
|
|
||||||
|
|
||||||
createEmoji: (emoji, custom_path) ->
|
createEmoji: (emoji) ->
|
||||||
|
emojiCssClass = @resolveNameToCssClass(emoji)
|
||||||
|
|
||||||
nodes = []
|
nodes = []
|
||||||
nodes.push("<div class='award active' title='me'>")
|
nodes.push("<div class='award active' title='me'>")
|
||||||
nodes.push("<div class='icon' data-emoji='" + emoji + "'>")
|
nodes.push("<div class='icon emoji-icon #{emojiCssClass}' data-emoji='#{emoji}'></div>")
|
||||||
nodes.push(@getImage(emoji, custom_path))
|
nodes.push("<div class='counter'>1</div>")
|
||||||
nodes.push("</div>")
|
nodes.push("</div>")
|
||||||
nodes.push("<div class='counter'>1")
|
|
||||||
nodes.push("</div></div>")
|
|
||||||
|
|
||||||
$(".awards-controls").before(nodes.join("\n"))
|
emoji_node = $(nodes.join("\n")).insertBefore(".awards-controls").find(".emoji-icon").data("emoji", emoji)
|
||||||
|
|
||||||
$(".award").tooltip()
|
$(".award").tooltip()
|
||||||
|
|
||||||
getImage: (emoji, custom_path) ->
|
resolveNameToCssClass: (emoji) ->
|
||||||
if custom_path
|
emoji_icon = $(".emoji-menu-content [data-emoji='#{emoji}']")
|
||||||
$("<img>").attr({src: custom_path, width: 20, height: 20}).wrap("<div>").parent().html()
|
|
||||||
else
|
|
||||||
$("li[data-emoji='" + emoji + "']").html()
|
|
||||||
|
|
||||||
|
if emoji_icon.length > 0
|
||||||
|
unicodeName = emoji_icon.data("unicode-name")
|
||||||
|
else
|
||||||
|
# Find by alias
|
||||||
|
unicodeName = $(".emoji-menu-content [data-aliases*=':#{emoji}:']").data("unicode-name")
|
||||||
|
|
||||||
|
"emoji-#{unicodeName}"
|
||||||
|
|
||||||
postEmoji: (emoji, callback) ->
|
postEmoji: (emoji, callback) ->
|
||||||
$.post @post_emoji_url, { note: {
|
$.post @post_emoji_url, { note: {
|
||||||
note: ":" + emoji + ":"
|
note: ":#{emoji}:"
|
||||||
noteable_type: @noteable_type
|
noteable_type: @noteable_type
|
||||||
noteable_id: @noteable_id
|
noteable_id: @noteable_id
|
||||||
}},(data) ->
|
}},(data) ->
|
||||||
|
@ -90,7 +110,7 @@ class @AwardsHandler
|
||||||
callback.call()
|
callback.call()
|
||||||
|
|
||||||
findEmojiIcon: (emoji) ->
|
findEmojiIcon: (emoji) ->
|
||||||
$(".icon[data-emoji='" + emoji + "']")
|
$(".award [data-emoji='#{emoji}']")
|
||||||
|
|
||||||
scrollToAwards: ->
|
scrollToAwards: ->
|
||||||
$('body, html').animate({
|
$('body, html').animate({
|
||||||
|
@ -99,3 +119,46 @@ class @AwardsHandler
|
||||||
|
|
||||||
normilizeEmojiName: (emoji) ->
|
normilizeEmojiName: (emoji) ->
|
||||||
@aliases[emoji] || emoji
|
@aliases[emoji] || emoji
|
||||||
|
|
||||||
|
addEmojiToFrequentlyUsedList: (emoji) ->
|
||||||
|
frequently_used_emojis = @getFrequentlyUsedEmojis()
|
||||||
|
frequently_used_emojis.push(emoji)
|
||||||
|
$.cookie('frequently_used_emojis', frequently_used_emojis.join(","), { expires: 365 })
|
||||||
|
|
||||||
|
getFrequentlyUsedEmojis: ->
|
||||||
|
frequently_used_emojis = ($.cookie('frequently_used_emojis') || "").split(",")
|
||||||
|
|
||||||
|
frequently_used_emojis = ["thumbsup", "thumbsdown"].concat(frequently_used_emojis)
|
||||||
|
|
||||||
|
_.compact(_.uniq(frequently_used_emojis))
|
||||||
|
|
||||||
|
renderFrequentlyUsedBlock: ->
|
||||||
|
frequently_used_emojis = @getFrequentlyUsedEmojis()
|
||||||
|
|
||||||
|
ul = $("<ul>")
|
||||||
|
|
||||||
|
for emoji in frequently_used_emojis
|
||||||
|
do (emoji) ->
|
||||||
|
$(".emoji-menu-content [data-emoji='#{emoji}']").closest("li").clone().appendTo(ul)
|
||||||
|
|
||||||
|
$("input.emoji-search").after(ul).after($("<h5>").text("Frequently used"))
|
||||||
|
|
||||||
|
setupSearch: ->
|
||||||
|
$("input.emoji-search").keyup (ev) =>
|
||||||
|
term = $(ev.target).val()
|
||||||
|
|
||||||
|
# Clean previous search results
|
||||||
|
$("ul.emoji-search,h5.emoji-search").remove()
|
||||||
|
|
||||||
|
if term
|
||||||
|
# Generate a search result block
|
||||||
|
h5 = $("<h5>").text("Search results").addClass("emoji-search")
|
||||||
|
found_emojis = @searchEmojis(term).show()
|
||||||
|
ul = $("<ul>").addClass("emoji-search").append(found_emojis)
|
||||||
|
$(".emoji-menu-content ul, .emoji-menu-content h5").hide()
|
||||||
|
$(".emoji-menu-content").append(h5).append(ul)
|
||||||
|
else
|
||||||
|
$(".emoji-menu-content").children().show()
|
||||||
|
|
||||||
|
searchEmojis: (term)->
|
||||||
|
$(".emoji-menu-content [data-emoji*='#{term}']").closest("li").clone()
|
||||||
|
|
|
@ -35,7 +35,7 @@ class @BlobFileDropzone
|
||||||
return
|
return
|
||||||
|
|
||||||
this.on 'sending', (file, xhr, formData) ->
|
this.on 'sending', (file, xhr, formData) ->
|
||||||
formData.append('new_branch', form.find('.js-new-branch').val())
|
formData.append('target_branch', form.find('.js-target-branch').val())
|
||||||
formData.append('create_merge_request', form.find('.js-create-merge-request').val())
|
formData.append('create_merge_request', form.find('.js-create-merge-request').val())
|
||||||
formData.append('commit_message', form.find('.js-commit-message').val())
|
formData.append('commit_message', form.find('.js-commit-message').val())
|
||||||
return
|
return
|
||||||
|
|
|
@ -18,7 +18,7 @@ class @MergeRequestWidget
|
||||||
if data.state == "merged"
|
if data.state == "merged"
|
||||||
urlSuffix = if deleteSourceBranch then '?delete_source=true' else ''
|
urlSuffix = if deleteSourceBranch then '?delete_source=true' else ''
|
||||||
|
|
||||||
window.location.href = window.location.href + urlSuffix
|
window.location.href = window.location.pathname + urlSuffix
|
||||||
else if data.merge_error
|
else if data.merge_error
|
||||||
$('.mr-widget-body').html("<h4>" + data.merge_error + "</h4>")
|
$('.mr-widget-body').html("<h4>" + data.merge_error + "</h4>")
|
||||||
else
|
else
|
||||||
|
|
|
@ -0,0 +1,78 @@
|
||||||
|
class @NewBranchForm
|
||||||
|
constructor: (form, availableRefs) ->
|
||||||
|
@branchNameError = form.find('.js-branch-name-error')
|
||||||
|
@name = form.find('.js-branch-name')
|
||||||
|
@ref = form.find('#ref')
|
||||||
|
|
||||||
|
@setupAvailableRefs(availableRefs)
|
||||||
|
@setupRestrictions()
|
||||||
|
@addBinding()
|
||||||
|
@init()
|
||||||
|
|
||||||
|
addBinding: ->
|
||||||
|
@name.on 'blur', @validate
|
||||||
|
|
||||||
|
init: ->
|
||||||
|
@name.trigger 'blur' if @name.val().length > 0
|
||||||
|
|
||||||
|
setupAvailableRefs: (availableRefs) ->
|
||||||
|
@ref.autocomplete
|
||||||
|
source: availableRefs,
|
||||||
|
minLength: 1
|
||||||
|
|
||||||
|
setupRestrictions: ->
|
||||||
|
startsWith = {
|
||||||
|
pattern: /^(\/|\.)/g,
|
||||||
|
prefix: "can't start with",
|
||||||
|
conjunction: "or"
|
||||||
|
}
|
||||||
|
|
||||||
|
endsWith = {
|
||||||
|
pattern: /(\/|\.|\.lock)$/g,
|
||||||
|
prefix: "can't end in",
|
||||||
|
conjunction: "or"
|
||||||
|
}
|
||||||
|
|
||||||
|
invalid = {
|
||||||
|
pattern: /(\s|~|\^|:|\?|\*|\[|\\|\.\.|@\{|\/{2,}){1}/g
|
||||||
|
prefix: "can't contain",
|
||||||
|
conjunction: ", "
|
||||||
|
}
|
||||||
|
|
||||||
|
single = {
|
||||||
|
pattern: /^@+$/g
|
||||||
|
prefix: "can't be",
|
||||||
|
conjunction: "or"
|
||||||
|
}
|
||||||
|
|
||||||
|
@restrictions = [startsWith, invalid, endsWith, single]
|
||||||
|
|
||||||
|
validate: =>
|
||||||
|
@branchNameError.empty()
|
||||||
|
|
||||||
|
unique = (values, value) ->
|
||||||
|
values.push(value) unless value in values
|
||||||
|
values
|
||||||
|
|
||||||
|
formatter = (values, restriction) ->
|
||||||
|
formatted = values.map (value) ->
|
||||||
|
switch
|
||||||
|
when /\s/.test value then 'spaces'
|
||||||
|
when /\/{2,}/g.test value then 'consecutive slashes'
|
||||||
|
else "'#{value}'"
|
||||||
|
|
||||||
|
"#{restriction.prefix} #{formatted.join(restriction.conjunction)}"
|
||||||
|
|
||||||
|
validator = (errors, restriction) =>
|
||||||
|
matched = @name.val().match(restriction.pattern)
|
||||||
|
|
||||||
|
if matched
|
||||||
|
errors.concat formatter(matched.reduce(unique, []), restriction)
|
||||||
|
else
|
||||||
|
errors
|
||||||
|
|
||||||
|
errors = @restrictions.reduce validator, []
|
||||||
|
|
||||||
|
if errors.length > 0
|
||||||
|
errorMessage = $("<span/>").text(errors.join(', '))
|
||||||
|
@branchNameError.append(errorMessage)
|
|
@ -1,6 +1,6 @@
|
||||||
class @NewCommitForm
|
class @NewCommitForm
|
||||||
constructor: (form) ->
|
constructor: (form) ->
|
||||||
@newBranch = form.find('.js-new-branch')
|
@newBranch = form.find('.js-target-branch')
|
||||||
@originalBranch = form.find('.js-original-branch')
|
@originalBranch = form.find('.js-original-branch')
|
||||||
@createMergeRequest = form.find('.js-create-merge-request')
|
@createMergeRequest = form.find('.js-create-merge-request')
|
||||||
@createMergeRequestContainer = form.find('.js-create-merge-request-container')
|
@createMergeRequestContainer = form.find('.js-create-merge-request-container')
|
||||||
|
|
|
@ -127,7 +127,7 @@ class @Notes
|
||||||
@initTaskList()
|
@initTaskList()
|
||||||
|
|
||||||
if note.award
|
if note.award
|
||||||
awards_handler.addAwardToEmojiBar(note.note, note.emoji_path)
|
awards_handler.addAwardToEmojiBar(note.note)
|
||||||
awards_handler.scrollToAwards()
|
awards_handler.scrollToAwards()
|
||||||
|
|
||||||
###
|
###
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
class @Project
|
class @Project
|
||||||
constructor: ->
|
constructor: ->
|
||||||
# Git protocol switcher
|
# Git protocol switcher
|
||||||
$('.js-protocol-switch').click ->
|
$('ul.clone-options-dropdown a').click ->
|
||||||
return if $(@).hasClass('active')
|
return if $(@).hasClass('active')
|
||||||
|
|
||||||
|
|
||||||
|
@ -10,7 +10,8 @@ class @Project
|
||||||
# Add the active class for the clicked button
|
# Add the active class for the clicked button
|
||||||
$(@).toggleClass('active')
|
$(@).toggleClass('active')
|
||||||
|
|
||||||
url = $(@).data('clone')
|
url = $("#project_clone").val()
|
||||||
|
console.log("url",url)
|
||||||
|
|
||||||
# Update the input field
|
# Update the input field
|
||||||
$('#project_clone').val(url)
|
$('#project_clone').val(url)
|
||||||
|
|
|
@ -8,17 +8,17 @@ class @ProjectsList
|
||||||
|
|
||||||
$(".projects-list-filter").keyup ->
|
$(".projects-list-filter").keyup ->
|
||||||
terms = $(this).val()
|
terms = $(this).val()
|
||||||
uiBox = $(this).closest('.projects-list-holder')
|
uiBox = $('div.projects-list-holder')
|
||||||
if terms == "" || terms == undefined
|
if terms == "" || terms == undefined
|
||||||
uiBox.find(".projects-list li").show()
|
uiBox.find("ul.projects-list li").show()
|
||||||
else
|
else
|
||||||
uiBox.find(".projects-list li").each (index) ->
|
uiBox.find("ul.projects-list li").each (index) ->
|
||||||
name = $(this).find(".filter-title").text()
|
name = $(this).find("span.filter-title").text()
|
||||||
|
|
||||||
if name.toLowerCase().search(terms.toLowerCase()) == -1
|
if name.toLowerCase().search(terms.toLowerCase()) == -1
|
||||||
$(this).hide()
|
$(this).hide()
|
||||||
else
|
else
|
||||||
$(this).show()
|
$(this).show()
|
||||||
uiBox.find(".projects-list li.bottom").hide()
|
uiBox.find("ul.projects-list li.bottom").hide()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
class @Star
|
||||||
|
constructor: ->
|
||||||
|
$('.project-home-panel .toggle-star').on('ajax:success', (e, data, status, xhr) ->
|
||||||
|
$this = $(this)
|
||||||
|
$starSpan = $this.find('span')
|
||||||
|
$starIcon = $this.find('i')
|
||||||
|
|
||||||
|
toggleStar = (isStarred) ->
|
||||||
|
$this.parent().find('span.count').text data.star_count
|
||||||
|
if isStarred
|
||||||
|
$starSpan.removeClass('starred').text 'Star'
|
||||||
|
$starIcon.removeClass('fa-star').addClass 'fa-star-o'
|
||||||
|
else
|
||||||
|
$starSpan.addClass('starred').text 'Unstar'
|
||||||
|
$starIcon.removeClass('fa-star-o').addClass 'fa-star'
|
||||||
|
return
|
||||||
|
|
||||||
|
toggleStar $starSpan.hasClass('starred')
|
||||||
|
return
|
||||||
|
).on 'ajax:error', (e, xhr, status, error) ->
|
||||||
|
new Flash('Star toggle failed. Try again later.', 'alert')
|
||||||
|
return
|
|
@ -2,8 +2,8 @@
|
||||||
* This is a manifest file that'll automatically include all the stylesheets available in this directory
|
* This is a manifest file that'll automatically include all the stylesheets available in this directory
|
||||||
* and any sub-directories. You're free to add application-wide styles to this file and they'll appear at
|
* and any sub-directories. You're free to add application-wide styles to this file and they'll appear at
|
||||||
* the top of the compiled file, but it's generally better to create a new file per style scope.
|
* the top of the compiled file, but it's generally better to create a new file per style scope.
|
||||||
*= require jquery.ui.datepicker
|
*= require jquery-ui/datepicker
|
||||||
*= require jquery.ui.autocomplete
|
*= require jquery-ui/autocomplete
|
||||||
*= require jquery.atwho
|
*= require jquery.atwho
|
||||||
*= require select2
|
*= require select2
|
||||||
*= require_self
|
*= require_self
|
||||||
|
@ -48,4 +48,4 @@
|
||||||
/*
|
/*
|
||||||
* Styles for JS behaviors.
|
* Styles for JS behaviors.
|
||||||
*/
|
*/
|
||||||
@import "behaviors.scss";
|
@import "behaviors.scss";
|
||||||
|
|
|
@ -76,7 +76,7 @@
|
||||||
|
|
||||||
.cover-block {
|
.cover-block {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
background: #f7f8fa;
|
background: $background-color;
|
||||||
margin: -$gl-padding;
|
margin: -$gl-padding;
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
padding: 44px $gl-padding;
|
padding: 44px $gl-padding;
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
@mixin btn-default {
|
@mixin btn-default {
|
||||||
@include border-radius(2px);
|
@include border-radius(3px);
|
||||||
border-width: 1px;
|
border-width: 1px;
|
||||||
border-style: solid;
|
border-style: solid;
|
||||||
text-transform: uppercase;
|
font-size: 15px;
|
||||||
font-size: 13px;
|
font-weight: 500;
|
||||||
font-weight: 600;
|
|
||||||
line-height: 18px;
|
line-height: 18px;
|
||||||
padding: 11px $gl-padding;
|
padding: 11px $gl-padding;
|
||||||
letter-spacing: .4px;
|
letter-spacing: .4px;
|
||||||
|
@ -18,7 +17,7 @@
|
||||||
|
|
||||||
@mixin btn-middle {
|
@mixin btn-middle {
|
||||||
@include btn-default;
|
@include btn-default;
|
||||||
@include border-radius(2px);
|
@include border-radius(3px);
|
||||||
padding: 11px 24px;
|
padding: 11px 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,6 +50,10 @@
|
||||||
@include btn-color($blue-light, $border-blue-light, $blue-normal, $border-blue-normal, $blue-dark, $border-blue-dark, #FFFFFF);
|
@include btn-color($blue-light, $border-blue-light, $blue-normal, $border-blue-normal, $blue-dark, $border-blue-dark, #FFFFFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@mixin btn-blue-medium {
|
||||||
|
@include btn-color($blue-medium-light, $border-blue-light, $blue-medium, $border-blue-normal, $blue-medium-dark, $border-blue-dark, #FFFFFF);
|
||||||
|
}
|
||||||
|
|
||||||
@mixin btn-orange {
|
@mixin btn-orange {
|
||||||
@include btn-color($orange-light, $border-orange-light, $orange-normal, $border-orange-normal, $orange-dark, $border-orange-dark, #FFFFFF);
|
@include btn-color($orange-light, $border-orange-light, $orange-normal, $border-orange-normal, $orange-dark, $border-orange-dark, #FFFFFF);
|
||||||
}
|
}
|
||||||
|
@ -60,7 +63,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
@mixin btn-gray {
|
@mixin btn-gray {
|
||||||
@include btn-color($gray-light, $border-gray-light, $gray-normal, $border-gray-normal, $gray-dark, $border-gray-dark, #313236);
|
@include btn-color($gray-light, $border-gray-light, $gray-normal, $border-gray-light, $gray-dark, $border-gray-dark, #313236);
|
||||||
}
|
}
|
||||||
|
|
||||||
@mixin btn-white {
|
@mixin btn-white {
|
||||||
|
@ -75,6 +78,10 @@
|
||||||
padding: 5px 10px;
|
padding: 5px 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.btn-nr {
|
||||||
|
padding: 7px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
&.btn-xs {
|
&.btn-xs {
|
||||||
padding: 1px 5px;
|
padding: 1px 5px;
|
||||||
}
|
}
|
||||||
|
@ -91,11 +98,15 @@
|
||||||
@include btn-gray;
|
@include btn-gray;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.btn-primary,
|
&.btn-primary {
|
||||||
|
@include btn-blue-medium;
|
||||||
|
}
|
||||||
|
|
||||||
&.btn-info {
|
&.btn-info {
|
||||||
@include btn-blue;
|
@include btn-blue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.btn-close,
|
||||||
&.btn-warning {
|
&.btn-warning {
|
||||||
@include btn-orange;
|
@include btn-orange;
|
||||||
}
|
}
|
||||||
|
@ -110,20 +121,8 @@
|
||||||
float: right;
|
float: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.btn-close {
|
|
||||||
color: $gl-danger;
|
|
||||||
border-color: $gl-danger;
|
|
||||||
&:hover {
|
|
||||||
color: #B94A48;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.btn-reopen {
|
&.btn-reopen {
|
||||||
color: $gl-success;
|
/* should be same as parent class for now */
|
||||||
border-color: $gl-success;
|
|
||||||
&:hover {
|
|
||||||
color: #468847;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&.btn-grouped {
|
&.btn-grouped {
|
||||||
|
|
|
@ -374,7 +374,7 @@ table {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.center-top-menu {
|
.center-top-menu, .left-top-menu {
|
||||||
@include nav-menu;
|
@include nav-menu;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin-top: 5px;
|
margin-top: 5px;
|
||||||
|
@ -408,6 +408,11 @@ table {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.left-top-menu {
|
||||||
|
text-align: left;
|
||||||
|
border-bottom: 1px solid #EEE;
|
||||||
|
}
|
||||||
|
|
||||||
.center-middle-menu {
|
.center-middle-menu {
|
||||||
@include nav-menu;
|
@include nav-menu;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.status-box {
|
.status-box {
|
||||||
@include border-radius(2px);
|
@include border-radius(3px);
|
||||||
|
|
||||||
display: block;
|
display: block;
|
||||||
float: left;
|
float: left;
|
||||||
|
@ -25,7 +25,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
&.status-box-open {
|
&.status-box-open {
|
||||||
background-color: #019875;
|
background-color: $green-light;
|
||||||
color: #FFF;
|
color: #FFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ html {
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
background-color: #EAEBEC !important;
|
background-color: #F3F3F3 !important;
|
||||||
|
|
||||||
&.navless {
|
&.navless {
|
||||||
background-color: white !important;
|
background-color: white !important;
|
||||||
|
|
|
@ -123,7 +123,6 @@
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
list-style: none;
|
list-style: none;
|
||||||
margin-top: 5px;
|
|
||||||
height: 56px;
|
height: 56px;
|
||||||
|
|
||||||
li {
|
li {
|
||||||
|
@ -131,9 +130,9 @@
|
||||||
|
|
||||||
a {
|
a {
|
||||||
padding: 14px;
|
padding: 14px;
|
||||||
font-size: 17px;
|
font-size: 15px;
|
||||||
line-height: 28px;
|
line-height: 28px;
|
||||||
color: #7f8fa4;
|
color: #959494;
|
||||||
border-bottom: 2px solid transparent;
|
border-bottom: 2px solid transparent;
|
||||||
|
|
||||||
&:hover, &:active, &:focus {
|
&:hover, &:active, &:focus {
|
||||||
|
@ -143,8 +142,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
&.active a {
|
&.active a {
|
||||||
color: #4c4e54;
|
color: #616060;
|
||||||
border-bottom: 2px solid #1cacfc;
|
border-bottom: 2px solid #4688f1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.badge {
|
.badge {
|
||||||
|
|
|
@ -81,7 +81,7 @@
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.center-top-menu {
|
.center-top-menu, .left-top-menu {
|
||||||
li a {
|
li a {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
padding: 19px 10px;
|
padding: 19px 10px;
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
$hover: #FFFAF1;
|
$hover: #faf9f9;
|
||||||
$gl-text-color: #54565B;
|
$gl-text-color: #54565B;
|
||||||
$gl-text-green: #4A2;
|
$gl-text-green: #4A2;
|
||||||
$gl-text-red: #D12F19;
|
$gl-text-red: #D12F19;
|
||||||
$gl-text-orange: #D90;
|
$gl-text-orange: #D90;
|
||||||
$gl-header-color: #4c4e54;
|
$gl-header-color: #323232;
|
||||||
$gl-link-color: #333c48;
|
$gl-link-color: #333c48;
|
||||||
$md-text-color: #444;
|
$md-text-color: #444;
|
||||||
$md-link-color: #3084bb;
|
$md-link-color: #3084bb;
|
||||||
|
@ -15,13 +15,14 @@ $sidebar_width: 230px;
|
||||||
$avatar_radius: 50%;
|
$avatar_radius: 50%;
|
||||||
$code_font_size: 13px;
|
$code_font_size: 13px;
|
||||||
$code_line_height: 1.5;
|
$code_line_height: 1.5;
|
||||||
$border-color: #dce0e6;
|
$border-color: #efeff1;
|
||||||
$table-border-color: #eef0f2;
|
$table-border-color: #eef0f2;
|
||||||
$background-color: #F7F8FA;
|
$background-color: #faf9f9;
|
||||||
$header-height: 58px;
|
$header-height: 58px;
|
||||||
$fixed-layout-width: 1280px;
|
$fixed-layout-width: 1280px;
|
||||||
$gl-gray: #7f8fa4;
|
$gl-gray: #5a5a5a;
|
||||||
$gl-padding: 16px;
|
$gl-padding: 16px;
|
||||||
|
$gl-padding-top:10px;
|
||||||
$gl-avatar-size: 46px;
|
$gl-avatar-size: 46px;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -29,12 +30,12 @@ $gl-avatar-size: 46px;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
$white-light: #FFFFFF;
|
$white-light: #FFFFFF;
|
||||||
$white-normal: #DCE0E5;
|
$white-normal: #ededed;
|
||||||
$white-dark: #E4E7ED;
|
$white-dark: #ededed;
|
||||||
|
|
||||||
$gray-light: #F0F2F5;
|
$gray-light: #f7f7f7;
|
||||||
$gray-normal: #DCE0E5;
|
$gray-normal: #ededed;
|
||||||
$gray-dark: #E4E7ED;
|
$gray-dark: #ededed;
|
||||||
|
|
||||||
$green-light: #31AF64;
|
$green-light: #31AF64;
|
||||||
$green-normal: #2FAA60;
|
$green-normal: #2FAA60;
|
||||||
|
@ -44,6 +45,10 @@ $blue-light: #2EA8E5;
|
||||||
$blue-normal: #2D9FD8;
|
$blue-normal: #2D9FD8;
|
||||||
$blue-dark: #2897CE;
|
$blue-dark: #2897CE;
|
||||||
|
|
||||||
|
$blue-medium-light: #3498CB;
|
||||||
|
$blue-medium: #2F8EBF;
|
||||||
|
$blue-medium-dark: #2D86B4;
|
||||||
|
|
||||||
$orange-light: #FC6443;
|
$orange-light: #FC6443;
|
||||||
$orange-normal: #E75E40;
|
$orange-normal: #E75E40;
|
||||||
$orange-dark: #CE5237;
|
$orange-dark: #CE5237;
|
||||||
|
@ -52,11 +57,11 @@ $red-light: #F43263;
|
||||||
$red-normal: #E52C5A;
|
$red-normal: #E52C5A;
|
||||||
$red-dark: #D22852;
|
$red-dark: #D22852;
|
||||||
|
|
||||||
$border-white-light: #E3E7EC;
|
$border-white-light: #F1F2F4;
|
||||||
$border-white-normal: #D6DAE2;
|
$border-white-normal: #D6DAE2;
|
||||||
$border-white-dark: #C6CACF;
|
$border-white-dark: #C6CACF;
|
||||||
|
|
||||||
$border-gray-light: #DCE0E5;
|
$border-gray-light: #d1d1d1;
|
||||||
$border-gray-normal: #D6DAE2;
|
$border-gray-normal: #D6DAE2;
|
||||||
$border-gray-dark: #C6CACF;
|
$border-gray-dark: #C6CACF;
|
||||||
|
|
||||||
|
@ -76,6 +81,8 @@ $border-red-light: #E52C5A;
|
||||||
$border-red-normal: #D22852;
|
$border-red-normal: #D22852;
|
||||||
$border-red-dark: #CA264F;
|
$border-red-dark: #CA264F;
|
||||||
|
|
||||||
|
/* header */
|
||||||
|
$light-grey-header: #faf9f9;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* State colors:
|
* State colors:
|
||||||
|
|
|
@ -2,6 +2,12 @@
|
||||||
@include clearfix;
|
@include clearfix;
|
||||||
line-height: 34px;
|
line-height: 34px;
|
||||||
|
|
||||||
|
.emoji-icon {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
margin: 7px 0 0 5px;
|
||||||
|
}
|
||||||
|
|
||||||
.award {
|
.award {
|
||||||
@include border-radius(5px);
|
@include border-radius(5px);
|
||||||
|
|
||||||
|
@ -40,6 +46,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.awards-controls {
|
.awards-controls {
|
||||||
|
position: relative;
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
float: left;
|
float: left;
|
||||||
|
|
||||||
|
@ -55,32 +62,64 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.awards-menu {
|
.emoji-menu{
|
||||||
padding: $gl-padding;
|
position: absolute;
|
||||||
min-width: 214px;
|
top: 100%;
|
||||||
|
left: 0;
|
||||||
|
z-index: 1000;
|
||||||
|
display: none;
|
||||||
|
float: left;
|
||||||
|
min-width: 160px;
|
||||||
|
padding: 5px 0;
|
||||||
|
margin: 2px 0 0;
|
||||||
|
font-size: 14px;
|
||||||
|
text-align: left;
|
||||||
|
list-style: none;
|
||||||
|
background-color: #fff;
|
||||||
|
-webkit-background-clip: padding-box;
|
||||||
|
background-clip: padding-box;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border: 1px solid rgba(0,0,0,.15);
|
||||||
|
border-radius: 4px;
|
||||||
|
-webkit-box-shadow: 0 6px 12px rgba(0,0,0,.175);
|
||||||
|
box-shadow: 0 6px 12px rgba(0,0,0,.175);
|
||||||
|
|
||||||
> li {
|
.emoji-menu-content {
|
||||||
cursor: pointer;
|
padding: $gl-padding;
|
||||||
width: 30px;
|
width: 300px;
|
||||||
height: 30px;
|
height: 300px;
|
||||||
text-align: center;
|
overflow-y: scroll;
|
||||||
@include border-radius(5px);
|
|
||||||
|
|
||||||
img {
|
h5 {
|
||||||
margin-bottom: 2px;
|
clear: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
ul {
|
||||||
background-color: #ccc;
|
list-style-type: none;
|
||||||
|
margin-left: -20px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
input.emoji-search{
|
||||||
|
background: image-url("icon-search.png") 240px no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
cursor: pointer;
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
text-align: center;
|
||||||
|
float: left;
|
||||||
|
margin: 3px;
|
||||||
|
list-decorate: none;
|
||||||
|
@include border-radius(5px);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #ccc;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.awards-menu{
|
|
||||||
li {
|
|
||||||
float: left;
|
|
||||||
margin: 3px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
border-bottom: 1px solid $border-color;
|
border-bottom: 1px solid $border-color;
|
||||||
color: #5c5d5e;
|
color: #5c5d5e;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
line-height: 42px;
|
line-height: 34px;
|
||||||
|
|
||||||
.author {
|
.author {
|
||||||
color: #5c5d5e;
|
color: #5c5d5e;
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -75,16 +75,15 @@
|
||||||
|
|
||||||
.common-note-form {
|
.common-note-form {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
background: #F7F8FA;
|
background: #fff;
|
||||||
padding: $gl-padding;
|
padding: $gl-padding;
|
||||||
margin-left: -$gl-padding;
|
margin-left: -$gl-padding;
|
||||||
margin-right: -$gl-padding;
|
margin-right: -$gl-padding;
|
||||||
border-top: 1px solid $border-color;
|
|
||||||
margin-bottom: -$gl-padding;
|
margin-bottom: -$gl-padding;
|
||||||
}
|
}
|
||||||
|
|
||||||
.note-form-actions {
|
.note-form-actions {
|
||||||
background: #F9F9F9;
|
background: #fff;
|
||||||
|
|
||||||
.note-form-option {
|
.note-form-option {
|
||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
|
|
|
@ -128,7 +128,7 @@ ul.notes {
|
||||||
}
|
}
|
||||||
|
|
||||||
&:last-child {
|
&:last-child {
|
||||||
border-bottom: none;
|
border-bottom: 1px solid $border-color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -91,21 +91,83 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.input-group {
|
.git-clone-holder {
|
||||||
display: inline-table;
|
display: inline-table;
|
||||||
position: relative;
|
position: relative;
|
||||||
top: 17px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.project-repo-buttons {
|
.project-repo-buttons {
|
||||||
margin-top: 12px;
|
margin-top: 12px;
|
||||||
margin-bottom: 0px;
|
margin-bottom: 0px;
|
||||||
|
|
||||||
|
.count-buttons {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
.btn {
|
.btn {
|
||||||
@include btn-gray;
|
@include btn-gray;
|
||||||
|
text-transform: none;
|
||||||
|
}
|
||||||
|
.count-with-arrow {
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
margin-left: 4px;
|
||||||
|
|
||||||
|
.arrow {
|
||||||
|
&:before {
|
||||||
|
content: '';
|
||||||
|
display: inline-block;
|
||||||
|
position: absolute;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
border-color: transparent;
|
||||||
|
border-style: solid;
|
||||||
|
top: 50%;
|
||||||
|
left: 0;
|
||||||
|
margin-top: -6px;
|
||||||
|
border-width: 7px 5px 7px 0;
|
||||||
|
border-right-color: #dce0e5;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
border-color: transparent;
|
||||||
|
border-style: solid;
|
||||||
|
top: 50%;
|
||||||
|
left: 1px;
|
||||||
|
margin-top: -9px;
|
||||||
|
border-width: 10px 7px 10px 0;
|
||||||
|
border-right-color: #FFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
.count {
|
.count {
|
||||||
|
@include btn-gray;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
background: white;
|
||||||
|
border-radius: 2px;
|
||||||
|
border-width: 1px;
|
||||||
|
border-style: solid;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 20px;
|
||||||
|
padding: 11px 16px;
|
||||||
|
letter-spacing: .4px;
|
||||||
|
padding: 10px;
|
||||||
|
text-align: center;
|
||||||
|
vertical-align: middle;
|
||||||
|
touch-action: manipulation;
|
||||||
|
cursor: pointer;
|
||||||
|
background-image: none;
|
||||||
|
white-space: nowrap;
|
||||||
|
margin: 0 11px 0px 4px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #FFF;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -125,6 +187,13 @@
|
||||||
margin-right: 45px;
|
margin-right: 45px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.clone-options {
|
||||||
|
display: table-cell;
|
||||||
|
a.btn {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.form-control {
|
.form-control {
|
||||||
cursor: auto;
|
cursor: auto;
|
||||||
@extend .monospace;
|
@extend .monospace;
|
||||||
|
@ -335,6 +404,38 @@ ul.nav.nav-projects-tabs {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.top-area {
|
||||||
|
border-bottom: 1px solid #EEE;
|
||||||
|
margin: 0 -16px;
|
||||||
|
padding: 0 $gl-padding;
|
||||||
|
|
||||||
|
ul.left-top-menu {
|
||||||
|
display: inline-block;
|
||||||
|
width: 50%;
|
||||||
|
margin-bottom: 0px;
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.projects-search-form {
|
||||||
|
width: 50%;
|
||||||
|
display: inline-block;
|
||||||
|
float: right;
|
||||||
|
padding-top: 7px;
|
||||||
|
text-align: right;
|
||||||
|
|
||||||
|
.btn-green {
|
||||||
|
margin-top: -2px;
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: $screen-xs-max) {
|
||||||
|
.projects-search-form {
|
||||||
|
padding-top: 15px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.fork-namespaces {
|
.fork-namespaces {
|
||||||
.fork-thumbnail {
|
.fork-thumbnail {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
@ -412,11 +513,18 @@ pre.light-well {
|
||||||
|
|
||||||
.projects-search-form {
|
.projects-search-form {
|
||||||
margin: -$gl-padding;
|
margin: -$gl-padding;
|
||||||
background-color: #f8fafc;
|
|
||||||
padding: $gl-padding;
|
padding: $gl-padding;
|
||||||
margin-bottom: 0px;
|
margin-bottom: 0px;
|
||||||
border-top: 1px solid #e7e9ed;
|
|
||||||
border-bottom: 1px solid #e7e9ed;
|
input {
|
||||||
|
display: inline-block;
|
||||||
|
width: calc(100% - 151px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
display: inline-block;
|
||||||
|
width: 135px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.git-empty {
|
.git-empty {
|
||||||
|
|
|
@ -49,6 +49,8 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
|
||||||
:default_branch_protection,
|
:default_branch_protection,
|
||||||
:signup_enabled,
|
:signup_enabled,
|
||||||
:signin_enabled,
|
:signin_enabled,
|
||||||
|
:require_two_factor_authentication,
|
||||||
|
:two_factor_grace_period,
|
||||||
:gravatar_enabled,
|
:gravatar_enabled,
|
||||||
:twitter_sharing_enabled,
|
:twitter_sharing_enabled,
|
||||||
:sign_in_text,
|
:sign_in_text,
|
||||||
|
|
|
@ -1,6 +1,21 @@
|
||||||
class Admin::IdentitiesController < Admin::ApplicationController
|
class Admin::IdentitiesController < Admin::ApplicationController
|
||||||
before_action :user
|
before_action :user
|
||||||
before_action :identity, except: :index
|
before_action :identity, except: [:index, :new, :create]
|
||||||
|
|
||||||
|
def new
|
||||||
|
@identity = Identity.new
|
||||||
|
end
|
||||||
|
|
||||||
|
def create
|
||||||
|
@identity = Identity.new(identity_params)
|
||||||
|
@identity.user_id = user.id
|
||||||
|
|
||||||
|
if @identity.save
|
||||||
|
redirect_to admin_user_identities_path(@user), notice: 'User identity was successfully created.'
|
||||||
|
else
|
||||||
|
render :new
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@identities = @user.identities
|
@identities = @user.identities
|
||||||
|
|
|
@ -10,8 +10,10 @@ class ApplicationController < ActionController::Base
|
||||||
|
|
||||||
before_action :authenticate_user_from_token!
|
before_action :authenticate_user_from_token!
|
||||||
before_action :authenticate_user!
|
before_action :authenticate_user!
|
||||||
|
before_action :validate_user_service_ticket!
|
||||||
before_action :reject_blocked!
|
before_action :reject_blocked!
|
||||||
before_action :check_password_expiration
|
before_action :check_password_expiration
|
||||||
|
before_action :check_2fa_requirement
|
||||||
before_action :ldap_security_check
|
before_action :ldap_security_check
|
||||||
before_action :default_headers
|
before_action :default_headers
|
||||||
before_action :add_gon_variables
|
before_action :add_gon_variables
|
||||||
|
@ -202,12 +204,32 @@ class ApplicationController < ActionController::Base
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def validate_user_service_ticket!
|
||||||
|
return unless signed_in? && session[:service_tickets]
|
||||||
|
|
||||||
|
valid = session[:service_tickets].all? do |provider, ticket|
|
||||||
|
Gitlab::OAuth::Session.valid?(provider, ticket)
|
||||||
|
end
|
||||||
|
|
||||||
|
unless valid
|
||||||
|
session[:service_tickets] = nil
|
||||||
|
sign_out current_user
|
||||||
|
redirect_to new_user_session_path
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def check_password_expiration
|
def check_password_expiration
|
||||||
if current_user && current_user.password_expires_at && current_user.password_expires_at < Time.now && !current_user.ldap_user?
|
if current_user && current_user.password_expires_at && current_user.password_expires_at < Time.now && !current_user.ldap_user?
|
||||||
redirect_to new_profile_password_path and return
|
redirect_to new_profile_password_path and return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def check_2fa_requirement
|
||||||
|
if two_factor_authentication_required? && current_user && !current_user.two_factor_enabled && !skip_two_factor?
|
||||||
|
redirect_to new_profile_two_factor_auth_path
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def ldap_security_check
|
def ldap_security_check
|
||||||
if current_user && current_user.requires_ldap_check?
|
if current_user && current_user.requires_ldap_check?
|
||||||
unless Gitlab::LDAP::Access.allowed?(current_user)
|
unless Gitlab::LDAP::Access.allowed?(current_user)
|
||||||
|
@ -342,6 +364,23 @@ class ApplicationController < ActionController::Base
|
||||||
current_application_settings.import_sources.include?('git')
|
current_application_settings.import_sources.include?('git')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def two_factor_authentication_required?
|
||||||
|
current_application_settings.require_two_factor_authentication
|
||||||
|
end
|
||||||
|
|
||||||
|
def two_factor_grace_period
|
||||||
|
current_application_settings.two_factor_grace_period
|
||||||
|
end
|
||||||
|
|
||||||
|
def two_factor_grace_period_expired?
|
||||||
|
date = current_user.otp_grace_period_started_at
|
||||||
|
date && (date + two_factor_grace_period.hours) < Time.current
|
||||||
|
end
|
||||||
|
|
||||||
|
def skip_two_factor?
|
||||||
|
session[:skip_tfa] && session[:skip_tfa] > Time.current
|
||||||
|
end
|
||||||
|
|
||||||
def redirect_to_home_page_url?
|
def redirect_to_home_page_url?
|
||||||
# If user is not signed-in and tries to access root_path - redirect him to landing page
|
# If user is not signed-in and tries to access root_path - redirect him to landing page
|
||||||
# Don't redirect to the default URL to prevent endless redirections
|
# Don't redirect to the default URL to prevent endless redirections
|
||||||
|
|
|
@ -19,8 +19,10 @@ module Ci
|
||||||
@error = e.message
|
@error = e.message
|
||||||
@status = false
|
@status = false
|
||||||
rescue
|
rescue
|
||||||
@error = "Undefined error"
|
@error = 'Undefined error'
|
||||||
@status = false
|
@status = false
|
||||||
|
ensure
|
||||||
|
render :show
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,103 @@
|
||||||
|
module CreatesCommit
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
def create_commit(service, success_path:, failure_path:, failure_view: nil, success_notice: nil)
|
||||||
|
set_commit_variables
|
||||||
|
|
||||||
|
commit_params = @commit_params.merge(
|
||||||
|
source_project: @project,
|
||||||
|
source_branch: @ref,
|
||||||
|
target_branch: @target_branch
|
||||||
|
)
|
||||||
|
|
||||||
|
result = service.new(@tree_edit_project, current_user, commit_params).execute
|
||||||
|
|
||||||
|
if result[:status] == :success
|
||||||
|
flash[:notice] = success_notice || "Your changes have been successfully committed."
|
||||||
|
|
||||||
|
if create_merge_request?
|
||||||
|
success_path = new_merge_request_path
|
||||||
|
target = different_project? ? "project" : "branch"
|
||||||
|
flash[:notice] << " You can now submit a merge request to get this change into the original #{target}."
|
||||||
|
end
|
||||||
|
|
||||||
|
respond_to do |format|
|
||||||
|
format.html { redirect_to success_path }
|
||||||
|
format.json { render json: { message: "success", filePath: success_path } }
|
||||||
|
end
|
||||||
|
else
|
||||||
|
flash[:alert] = result[:message]
|
||||||
|
respond_to do |format|
|
||||||
|
format.html do
|
||||||
|
if failure_view
|
||||||
|
render failure_view
|
||||||
|
else
|
||||||
|
redirect_to failure_path
|
||||||
|
end
|
||||||
|
end
|
||||||
|
format.json { render json: { message: "failed", filePath: failure_path } }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def authorize_edit_tree!
|
||||||
|
return if can?(current_user, :push_code, project)
|
||||||
|
return if current_user && current_user.already_forked?(project)
|
||||||
|
|
||||||
|
access_denied!
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def new_merge_request_path
|
||||||
|
new_namespace_project_merge_request_path(
|
||||||
|
@mr_source_project.namespace,
|
||||||
|
@mr_source_project,
|
||||||
|
merge_request: {
|
||||||
|
source_project_id: @mr_source_project.id,
|
||||||
|
target_project_id: @mr_target_project.id,
|
||||||
|
source_branch: @mr_source_branch,
|
||||||
|
target_branch: @mr_target_branch
|
||||||
|
}
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def different_project?
|
||||||
|
@mr_source_project != @mr_target_project
|
||||||
|
end
|
||||||
|
|
||||||
|
def different_branch?
|
||||||
|
@mr_source_branch != @mr_target_branch || different_project?
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_merge_request?
|
||||||
|
params[:create_merge_request].present? && different_branch?
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_commit_variables
|
||||||
|
@mr_source_branch = @target_branch
|
||||||
|
|
||||||
|
if can?(current_user, :push_code, @project)
|
||||||
|
# Edit file in this project
|
||||||
|
@tree_edit_project = @project
|
||||||
|
@mr_source_project = @project
|
||||||
|
|
||||||
|
if @project.forked?
|
||||||
|
# Merge request from this project to fork origin
|
||||||
|
@mr_target_project = @project.forked_from_project
|
||||||
|
@mr_target_branch = @mr_target_project.repository.root_ref
|
||||||
|
else
|
||||||
|
# Merge request to this project
|
||||||
|
@mr_target_project = @project
|
||||||
|
@mr_target_branch = @ref
|
||||||
|
end
|
||||||
|
else
|
||||||
|
# Edit file in fork
|
||||||
|
@tree_edit_project = current_user.fork_of(@project)
|
||||||
|
# Merge request from fork to this project
|
||||||
|
@mr_source_project = @tree_edit_project
|
||||||
|
@mr_target_project = @project
|
||||||
|
@mr_target_branch = @mr_target_project.repository.root_ref
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,28 +0,0 @@
|
||||||
module CreatesMergeRequestForCommit
|
|
||||||
extend ActiveSupport::Concern
|
|
||||||
|
|
||||||
def new_merge_request_path
|
|
||||||
if @project.forked?
|
|
||||||
target_project = @project.forked_from_project || @project
|
|
||||||
target_branch = target_project.repository.root_ref
|
|
||||||
else
|
|
||||||
target_project = @project
|
|
||||||
target_branch = @ref
|
|
||||||
end
|
|
||||||
|
|
||||||
new_namespace_project_merge_request_path(
|
|
||||||
@project.namespace,
|
|
||||||
@project,
|
|
||||||
merge_request: {
|
|
||||||
source_project_id: @project.id,
|
|
||||||
target_project_id: target_project.id,
|
|
||||||
source_branch: @new_branch,
|
|
||||||
target_branch: target_branch
|
|
||||||
}
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def create_merge_request?
|
|
||||||
params[:create_merge_request] && @new_branch != @ref
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,6 +1,6 @@
|
||||||
class OmniauthCallbacksController < Devise::OmniauthCallbacksController
|
class OmniauthCallbacksController < Devise::OmniauthCallbacksController
|
||||||
|
|
||||||
protect_from_forgery except: [:kerberos, :saml]
|
protect_from_forgery except: [:kerberos, :saml, :cas3]
|
||||||
|
|
||||||
Gitlab.config.omniauth.providers.each do |provider|
|
Gitlab.config.omniauth.providers.each do |provider|
|
||||||
define_method provider['name'] do
|
define_method provider['name'] do
|
||||||
|
@ -42,6 +42,14 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
|
||||||
render 'errors/omniauth_error', layout: "errors", status: 422
|
render 'errors/omniauth_error', layout: "errors", status: 422
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def cas3
|
||||||
|
ticket = params['ticket']
|
||||||
|
if ticket
|
||||||
|
handle_service_ticket oauth['provider'], ticket
|
||||||
|
end
|
||||||
|
handle_omniauth
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def handle_omniauth
|
def handle_omniauth
|
||||||
|
@ -84,6 +92,12 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
|
||||||
redirect_to new_user_session_path
|
redirect_to new_user_session_path
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def handle_service_ticket provider, ticket
|
||||||
|
Gitlab::OAuth::Session.create provider, ticket
|
||||||
|
session[:service_tickets] ||= {}
|
||||||
|
session[:service_tickets][provider] = ticket
|
||||||
|
end
|
||||||
|
|
||||||
def oauth
|
def oauth
|
||||||
@oauth ||= request.env['omniauth.auth']
|
@oauth ||= request.env['omniauth.auth']
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,8 +1,22 @@
|
||||||
class Profiles::TwoFactorAuthsController < Profiles::ApplicationController
|
class Profiles::TwoFactorAuthsController < Profiles::ApplicationController
|
||||||
|
skip_before_action :check_2fa_requirement
|
||||||
|
|
||||||
def new
|
def new
|
||||||
unless current_user.otp_secret
|
unless current_user.otp_secret
|
||||||
current_user.otp_secret = User.generate_otp_secret(32)
|
current_user.otp_secret = User.generate_otp_secret(32)
|
||||||
current_user.save!
|
end
|
||||||
|
|
||||||
|
unless current_user.otp_grace_period_started_at && two_factor_grace_period
|
||||||
|
current_user.otp_grace_period_started_at = Time.current
|
||||||
|
end
|
||||||
|
|
||||||
|
current_user.save! if current_user.changed?
|
||||||
|
|
||||||
|
if two_factor_grace_period_expired?
|
||||||
|
flash.now[:alert] = 'You must configure Two-Factor Authentication in your account.'
|
||||||
|
else
|
||||||
|
grace_period_deadline = current_user.otp_grace_period_started_at + two_factor_grace_period.hours
|
||||||
|
flash.now[:alert] = "You must configure Two-Factor Authentication in your account until #{l(grace_period_deadline)}."
|
||||||
end
|
end
|
||||||
|
|
||||||
@qr_code = build_qr_code
|
@qr_code = build_qr_code
|
||||||
|
@ -34,6 +48,15 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController
|
||||||
redirect_to profile_account_path
|
redirect_to profile_account_path
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def skip
|
||||||
|
if two_factor_grace_period_expired?
|
||||||
|
redirect_to new_profile_two_factor_auth_path, alert: 'Cannot skip two factor authentication setup'
|
||||||
|
else
|
||||||
|
session[:skip_tfa] = current_user.otp_grace_period_started_at + two_factor_grace_period.hours
|
||||||
|
redirect_to root_path
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def build_qr_code
|
def build_qr_code
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# Controller for viewing a file's blame
|
# Controller for viewing a file's blame
|
||||||
class Projects::BlobController < Projects::ApplicationController
|
class Projects::BlobController < Projects::ApplicationController
|
||||||
include ExtractsPath
|
include ExtractsPath
|
||||||
include CreatesMergeRequestForCommit
|
include CreatesCommit
|
||||||
include ActionView::Helpers::SanitizeHelper
|
include ActionView::Helpers::SanitizeHelper
|
||||||
|
|
||||||
# Raised when given an invalid file path
|
# Raised when given an invalid file path
|
||||||
|
@ -9,21 +9,21 @@ class Projects::BlobController < Projects::ApplicationController
|
||||||
|
|
||||||
before_action :require_non_empty_project, except: [:new, :create]
|
before_action :require_non_empty_project, except: [:new, :create]
|
||||||
before_action :authorize_download_code!
|
before_action :authorize_download_code!
|
||||||
before_action :authorize_push_code!, only: [:destroy, :create]
|
before_action :authorize_edit_tree!, only: [:new, :create, :edit, :update, :destroy]
|
||||||
before_action :assign_blob_vars
|
before_action :assign_blob_vars
|
||||||
before_action :commit, except: [:new, :create]
|
before_action :commit, except: [:new, :create]
|
||||||
before_action :blob, except: [:new, :create]
|
before_action :blob, except: [:new, :create]
|
||||||
before_action :from_merge_request, only: [:edit, :update]
|
before_action :from_merge_request, only: [:edit, :update]
|
||||||
before_action :require_branch_head, only: [:edit, :update]
|
before_action :require_branch_head, only: [:edit, :update]
|
||||||
before_action :editor_variables, except: [:show, :preview, :diff]
|
before_action :editor_variables, except: [:show, :preview, :diff]
|
||||||
before_action :after_edit_path, only: [:edit, :update]
|
|
||||||
|
|
||||||
def new
|
def new
|
||||||
commit unless @repository.empty?
|
commit unless @repository.empty?
|
||||||
end
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
create_commit(Files::CreateService, success_path: after_create_path,
|
create_commit(Files::CreateService, success_notice: "The file has been successfully created.",
|
||||||
|
success_path: namespace_project_blob_path(@project.namespace, @project, File.join(@target_branch, @file_path)),
|
||||||
failure_view: :new,
|
failure_view: :new,
|
||||||
failure_path: namespace_project_new_blob_path(@project.namespace, @project, @ref))
|
failure_path: namespace_project_new_blob_path(@project.namespace, @project, @ref))
|
||||||
end
|
end
|
||||||
|
@ -36,6 +36,14 @@ class Projects::BlobController < Projects::ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def update
|
def update
|
||||||
|
after_edit_path =
|
||||||
|
if from_merge_request && @target_branch == @ref
|
||||||
|
diffs_namespace_project_merge_request_path(from_merge_request.target_project.namespace, from_merge_request.target_project, from_merge_request) +
|
||||||
|
"#file-path-#{hexdigest(@path)}"
|
||||||
|
else
|
||||||
|
namespace_project_blob_path(@project.namespace, @project, File.join(@target_branch, @path))
|
||||||
|
end
|
||||||
|
|
||||||
create_commit(Files::UpdateService, success_path: after_edit_path,
|
create_commit(Files::UpdateService, success_path: after_edit_path,
|
||||||
failure_view: :edit,
|
failure_view: :edit,
|
||||||
failure_path: namespace_project_blob_path(@project.namespace, @project, @id))
|
failure_path: namespace_project_blob_path(@project.namespace, @project, @id))
|
||||||
|
@ -50,15 +58,10 @@ class Projects::BlobController < Projects::ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def destroy
|
def destroy
|
||||||
result = Files::DeleteService.new(@project, current_user, @commit_params).execute
|
create_commit(Files::DeleteService, success_notice: "The file has been successfully deleted.",
|
||||||
|
success_path: namespace_project_tree_path(@project.namespace, @project, @target_branch),
|
||||||
if result[:status] == :success
|
failure_view: :show,
|
||||||
flash[:notice] = "Your changes have been successfully committed"
|
failure_path: namespace_project_blob_path(@project.namespace, @project, @id))
|
||||||
redirect_to after_destroy_path
|
|
||||||
else
|
|
||||||
flash[:alert] = result[:message]
|
|
||||||
render :show
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def diff
|
def diff
|
||||||
|
@ -108,74 +111,13 @@ class Projects::BlobController < Projects::ApplicationController
|
||||||
render_404
|
render_404
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_commit(service, success_path:, failure_view:, failure_path:)
|
|
||||||
result = service.new(@project, current_user, @commit_params).execute
|
|
||||||
|
|
||||||
if result[:status] == :success
|
|
||||||
flash[:notice] = "Your changes have been successfully committed"
|
|
||||||
respond_to do |format|
|
|
||||||
format.html { redirect_to success_path }
|
|
||||||
format.json { render json: { message: "success", filePath: success_path } }
|
|
||||||
end
|
|
||||||
else
|
|
||||||
flash[:alert] = result[:message]
|
|
||||||
respond_to do |format|
|
|
||||||
format.html { render failure_view }
|
|
||||||
format.json { render json: { message: "failed", filePath: failure_path } }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def after_create_path
|
|
||||||
@after_create_path ||=
|
|
||||||
if create_merge_request?
|
|
||||||
new_merge_request_path
|
|
||||||
else
|
|
||||||
namespace_project_blob_path(@project.namespace, @project, File.join(@new_branch, @file_path))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def after_edit_path
|
|
||||||
@after_edit_path ||=
|
|
||||||
if create_merge_request?
|
|
||||||
new_merge_request_path
|
|
||||||
elsif from_merge_request && @new_branch == @ref
|
|
||||||
diffs_namespace_project_merge_request_path(from_merge_request.target_project.namespace, from_merge_request.target_project, from_merge_request) +
|
|
||||||
"#file-path-#{hexdigest(@path)}"
|
|
||||||
else
|
|
||||||
namespace_project_blob_path(@project.namespace, @project, File.join(@new_branch, @path))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def after_destroy_path
|
|
||||||
@after_destroy_path ||=
|
|
||||||
if create_merge_request?
|
|
||||||
new_merge_request_path
|
|
||||||
else
|
|
||||||
namespace_project_tree_path(@project.namespace, @project, @new_branch)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def from_merge_request
|
def from_merge_request
|
||||||
# If blob edit was initiated from merge request page
|
# If blob edit was initiated from merge request page
|
||||||
@from_merge_request ||= MergeRequest.find_by(id: params[:from_merge_request_id])
|
@from_merge_request ||= MergeRequest.find_by(id: params[:from_merge_request_id])
|
||||||
end
|
end
|
||||||
|
|
||||||
def sanitized_new_branch_name
|
|
||||||
sanitize(strip_tags(params[:new_branch]))
|
|
||||||
end
|
|
||||||
|
|
||||||
def editor_variables
|
def editor_variables
|
||||||
@current_branch = @ref
|
@target_branch = params[:target_branch]
|
||||||
|
|
||||||
@new_branch =
|
|
||||||
if params[:new_branch].present?
|
|
||||||
sanitized_new_branch_name
|
|
||||||
elsif ::Gitlab::GitAccess.new(current_user, @project).can_push_to_branch?(@ref)
|
|
||||||
@ref
|
|
||||||
else
|
|
||||||
@repository.next_patch_branch
|
|
||||||
end
|
|
||||||
|
|
||||||
@file_path =
|
@file_path =
|
||||||
if action_name.to_s == 'create'
|
if action_name.to_s == 'create'
|
||||||
|
@ -194,8 +136,6 @@ class Projects::BlobController < Projects::ApplicationController
|
||||||
|
|
||||||
@commit_params = {
|
@commit_params = {
|
||||||
file_path: @file_path,
|
file_path: @file_path,
|
||||||
current_branch: @current_branch,
|
|
||||||
target_branch: @new_branch,
|
|
||||||
commit_message: params[:commit_message],
|
commit_message: params[:commit_message],
|
||||||
file_content: params[:content],
|
file_content: params[:content],
|
||||||
file_content_encoding: params[:encoding]
|
file_content_encoding: params[:encoding]
|
||||||
|
|
|
@ -10,19 +10,35 @@ class Projects::ForksController < Projects::ApplicationController
|
||||||
|
|
||||||
def create
|
def create
|
||||||
namespace = Namespace.find(params[:namespace_key])
|
namespace = Namespace.find(params[:namespace_key])
|
||||||
@forked_project = ::Projects::ForkService.new(project, current_user, namespace: namespace).execute
|
|
||||||
|
@forked_project = namespace.projects.find_by(path: project.path)
|
||||||
|
@forked_project = nil unless @forked_project && @forked_project.forked_from_project == project
|
||||||
|
|
||||||
|
@forked_project ||= ::Projects::ForkService.new(project, current_user, namespace: namespace).execute
|
||||||
|
|
||||||
if @forked_project.saved? && @forked_project.forked?
|
if @forked_project.saved? && @forked_project.forked?
|
||||||
if @forked_project.import_in_progress?
|
if @forked_project.import_in_progress?
|
||||||
redirect_to namespace_project_import_path(@forked_project.namespace, @forked_project)
|
redirect_to namespace_project_import_path(@forked_project.namespace, @forked_project, continue: continue_params)
|
||||||
else
|
else
|
||||||
redirect_to(
|
if continue_params
|
||||||
namespace_project_path(@forked_project.namespace, @forked_project),
|
redirect_to continue_params[:to], notice: continue_params[:notice]
|
||||||
notice: 'Project was successfully forked.'
|
else
|
||||||
)
|
redirect_to namespace_project_path(@forked_project.namespace, @forked_project), notice: "The project was successfully forked."
|
||||||
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
render :error
|
render :error
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def continue_params
|
||||||
|
continue_params = params[:continue]
|
||||||
|
if continue_params
|
||||||
|
continue_params.permit(:to, :notice, :notice_now)
|
||||||
|
else
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
class Projects::ImportsController < Projects::ApplicationController
|
class Projects::ImportsController < Projects::ApplicationController
|
||||||
# Authorize
|
# Authorize
|
||||||
before_action :authorize_admin_project!
|
before_action :authorize_admin_project!
|
||||||
before_action :require_no_repo
|
before_action :require_no_repo, except: :show
|
||||||
before_action :redirect_if_progress, except: :show
|
before_action :redirect_if_progress, except: :show
|
||||||
|
|
||||||
def new
|
def new
|
||||||
|
@ -24,21 +24,36 @@ class Projects::ImportsController < Projects::ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def show
|
def show
|
||||||
unless @project.import_in_progress?
|
if @project.repository_exists? || @project.import_finished?
|
||||||
if @project.import_finished?
|
if continue_params
|
||||||
redirect_to(project_path(@project)) and return
|
redirect_to continue_params[:to], notice: continue_params[:notice]
|
||||||
else
|
else
|
||||||
redirect_to(new_namespace_project_import_path(@project.namespace,
|
redirect_to project_path(@project), notice: "The project was successfully forked."
|
||||||
@project)) and return
|
|
||||||
end
|
end
|
||||||
|
elsif @project.import_failed?
|
||||||
|
redirect_to new_namespace_project_import_path(@project.namespace, @project)
|
||||||
|
else
|
||||||
|
if continue_params && continue_params[:notice_now]
|
||||||
|
flash.now[:notice] = continue_params[:notice_now]
|
||||||
|
end
|
||||||
|
# Render
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def continue_params
|
||||||
|
continue_params = params[:continue]
|
||||||
|
if continue_params
|
||||||
|
continue_params.permit(:to, :notice, :notice_now)
|
||||||
|
else
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def require_no_repo
|
def require_no_repo
|
||||||
if @project.repository_exists? && !@project.import_in_progress?
|
if @project.repository_exists? && !@project.import_in_progress?
|
||||||
redirect_to(namespace_project_path(@project.namespace, @project)) and return
|
redirect_to(namespace_project_path(@project.namespace, @project))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -139,7 +139,6 @@ class Projects::NotesController < Projects::ApplicationController
|
||||||
discussion_id: note.discussion_id,
|
discussion_id: note.discussion_id,
|
||||||
html: note_to_html(note),
|
html: note_to_html(note),
|
||||||
award: note.is_award,
|
award: note.is_award,
|
||||||
emoji_path: note.is_award ? view_context.image_url(::AwardEmoji.path_to_emoji_image(note.note)) : "",
|
|
||||||
note: note.note,
|
note: note.note,
|
||||||
discussion_html: note_to_discussion_html(note),
|
discussion_html: note_to_discussion_html(note),
|
||||||
discussion_with_diff_html: note_to_discussion_with_diff_html(note)
|
discussion_with_diff_html: note_to_discussion_with_diff_html(note)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
class Projects::ServicesController < Projects::ApplicationController
|
class Projects::ServicesController < Projects::ApplicationController
|
||||||
ALLOWED_PARAMS = [:title, :token, :type, :active, :api_key, :api_version, :subdomain,
|
ALLOWED_PARAMS = [:title, :token, :type, :active, :api_key, :api_url, :api_version, :subdomain,
|
||||||
:room, :recipients, :project_url, :webhook,
|
:room, :recipients, :project_url, :webhook,
|
||||||
:user_key, :device, :priority, :sound, :bamboo_url, :username, :password,
|
:user_key, :device, :priority, :sound, :bamboo_url, :username, :password,
|
||||||
:build_key, :server, :teamcity_url, :drone_url, :build_type,
|
:build_key, :server, :teamcity_url, :drone_url, :build_type,
|
||||||
|
@ -10,7 +10,8 @@ class Projects::ServicesController < Projects::ApplicationController
|
||||||
:notify_only_broken_builds, :add_pusher,
|
:notify_only_broken_builds, :add_pusher,
|
||||||
:send_from_committer_email, :disable_diffs, :external_wiki_url,
|
:send_from_committer_email, :disable_diffs, :external_wiki_url,
|
||||||
:notify, :color,
|
:notify, :color,
|
||||||
:server_host, :server_port, :default_irc_uri, :enable_ssl_verification]
|
:server_host, :server_port, :default_irc_uri, :enable_ssl_verification,
|
||||||
|
:jira_issue_transition_id]
|
||||||
|
|
||||||
# Parameters to ignore if no value is specified
|
# Parameters to ignore if no value is specified
|
||||||
FILTER_BLANK_PARAMS = [:password]
|
FILTER_BLANK_PARAMS = [:password]
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
# Controller for viewing a repository's file structure
|
# Controller for viewing a repository's file structure
|
||||||
class Projects::TreeController < Projects::ApplicationController
|
class Projects::TreeController < Projects::ApplicationController
|
||||||
include ExtractsPath
|
include ExtractsPath
|
||||||
include CreatesMergeRequestForCommit
|
include CreatesCommit
|
||||||
include ActionView::Helpers::SanitizeHelper
|
include ActionView::Helpers::SanitizeHelper
|
||||||
|
|
||||||
before_action :require_non_empty_project, except: [:new, :create]
|
before_action :require_non_empty_project, except: [:new, :create]
|
||||||
before_action :assign_ref_vars
|
before_action :assign_ref_vars
|
||||||
before_action :assign_dir_vars, only: [:create_dir]
|
before_action :assign_dir_vars, only: [:create_dir]
|
||||||
before_action :authorize_download_code!
|
before_action :authorize_download_code!
|
||||||
before_action :authorize_push_code!, only: [:create_dir]
|
before_action :authorize_edit_tree!, only: [:create_dir]
|
||||||
|
|
||||||
def show
|
def show
|
||||||
return render_404 unless @repository.commit(@ref)
|
return render_404 unless @repository.commit(@ref)
|
||||||
|
@ -34,44 +34,20 @@ class Projects::TreeController < Projects::ApplicationController
|
||||||
def create_dir
|
def create_dir
|
||||||
return render_404 unless @commit_params.values.all?
|
return render_404 unless @commit_params.values.all?
|
||||||
|
|
||||||
begin
|
create_commit(Files::CreateDirService, success_notice: "The directory has been successfully created.",
|
||||||
result = Files::CreateDirService.new(@project, current_user, @commit_params).execute
|
success_path: namespace_project_tree_path(@project.namespace, @project, File.join(@target_branch, @dir_name)),
|
||||||
message = result[:message]
|
failure_path: namespace_project_tree_path(@project.namespace, @project, @ref))
|
||||||
rescue => e
|
|
||||||
message = e.to_s
|
|
||||||
end
|
|
||||||
|
|
||||||
if result && result[:status] == :success
|
|
||||||
flash[:notice] = "The directory has been successfully created"
|
|
||||||
respond_to do |format|
|
|
||||||
format.html { redirect_to after_create_dir_path }
|
|
||||||
end
|
|
||||||
else
|
|
||||||
flash[:alert] = message
|
|
||||||
respond_to do |format|
|
|
||||||
format.html { redirect_to namespace_project_blob_path(@project.namespace, @project, @new_branch) }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def assign_dir_vars
|
def assign_dir_vars
|
||||||
@new_branch = params[:new_branch].present? ? sanitize(strip_tags(params[:new_branch])) : @ref
|
@target_branch = params[:target_branch]
|
||||||
|
|
||||||
@dir_name = File.join(@path, params[:dir_name])
|
@dir_name = File.join(@path, params[:dir_name])
|
||||||
@commit_params = {
|
@commit_params = {
|
||||||
file_path: @dir_name,
|
file_path: @dir_name,
|
||||||
current_branch: @ref,
|
|
||||||
target_branch: @new_branch,
|
|
||||||
commit_message: params[:commit_message],
|
commit_message: params[:commit_message],
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
def after_create_dir_path
|
|
||||||
if create_merge_request?
|
|
||||||
new_merge_request_path
|
|
||||||
else
|
|
||||||
namespace_project_blob_path(@project.namespace, @project, File.join(@new_branch, @dir_name))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -171,7 +171,7 @@ class ProjectsController < ApplicationController
|
||||||
@project.reload
|
@project.reload
|
||||||
|
|
||||||
render json: {
|
render json: {
|
||||||
html: view_to_html_string("projects/buttons/_star")
|
star_count: @project.star_count
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -50,5 +50,17 @@ module AuthHelper
|
||||||
current_user.identities.exists?(provider: provider.to_s)
|
current_user.identities.exists?(provider: provider.to_s)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def two_factor_skippable?
|
||||||
|
current_application_settings.require_two_factor_authentication &&
|
||||||
|
!current_user.two_factor_enabled &&
|
||||||
|
current_application_settings.two_factor_grace_period &&
|
||||||
|
!two_factor_grace_period_expired?
|
||||||
|
end
|
||||||
|
|
||||||
|
def two_factor_grace_period_expired?
|
||||||
|
current_user.otp_grace_period_started_at &&
|
||||||
|
(current_user.otp_grace_period_started_at + current_application_settings.two_factor_grace_period.hours) < Time.current
|
||||||
|
end
|
||||||
|
|
||||||
extend self
|
extend self
|
||||||
end
|
end
|
||||||
|
|
|
@ -22,32 +22,90 @@ module BlobHelper
|
||||||
%w(credits changelog news copying copyright license authors)
|
%w(credits changelog news copying copyright license authors)
|
||||||
end
|
end
|
||||||
|
|
||||||
def edit_blob_link(project, ref, path, options = {})
|
def edit_blob_link(project = @project, ref = @ref, path = @path, options = {})
|
||||||
blob =
|
return unless current_user
|
||||||
begin
|
|
||||||
project.repository.blob_at(ref, path)
|
|
||||||
rescue
|
|
||||||
nil
|
|
||||||
end
|
|
||||||
|
|
||||||
return unless blob && blob.text? && blob_editable?(blob)
|
blob = project.repository.blob_at(ref, path) rescue nil
|
||||||
|
|
||||||
|
return unless blob && blob_text_viewable?(blob)
|
||||||
|
|
||||||
text = 'Edit'
|
|
||||||
after = options[:after] || ''
|
|
||||||
from_mr = options[:from_merge_request_id]
|
from_mr = options[:from_merge_request_id]
|
||||||
link_opts = {}
|
link_opts = {}
|
||||||
link_opts[:from_merge_request_id] = from_mr if from_mr
|
link_opts[:from_merge_request_id] = from_mr if from_mr
|
||||||
cls = 'btn btn-small'
|
|
||||||
link_to(text,
|
edit_path = namespace_project_edit_blob_path(project.namespace, project,
|
||||||
namespace_project_edit_blob_path(project.namespace, project,
|
tree_join(ref, path),
|
||||||
tree_join(ref, path),
|
link_opts)
|
||||||
link_opts),
|
|
||||||
class: cls
|
if !on_top_of_branch?
|
||||||
) + after.html_safe
|
button_tag "Edit", class: "btn btn-default disabled has_tooltip", title: "You can only edit files when you are on a branch", data: { container: 'body' }
|
||||||
|
elsif can_edit_blob?(blob)
|
||||||
|
link_to "Edit", edit_path, class: 'btn btn-small'
|
||||||
|
elsif can?(current_user, :fork_project, project)
|
||||||
|
continue_params = {
|
||||||
|
to: edit_path,
|
||||||
|
notice: edit_in_new_fork_notice,
|
||||||
|
notice_now: edit_in_new_fork_notice_now
|
||||||
|
}
|
||||||
|
fork_path = namespace_project_fork_path(project.namespace, project, namespace_key: current_user.namespace.id,
|
||||||
|
continue: continue_params)
|
||||||
|
|
||||||
|
link_to "Edit", fork_path, class: 'btn btn-small', method: :post
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def blob_editable?(blob, project = @project, ref = @ref)
|
def modify_file_link(project = @project, ref = @ref, path = @path, label:, action:, btn_class:, modal_type:)
|
||||||
!blob.lfs_pointer? && allowed_tree_edit?(project, ref)
|
return unless current_user
|
||||||
|
|
||||||
|
blob = project.repository.blob_at(ref, path) rescue nil
|
||||||
|
|
||||||
|
return unless blob
|
||||||
|
|
||||||
|
if !on_top_of_branch?
|
||||||
|
button_tag label, class: "btn btn-#{btn_class} disabled has_tooltip", title: "You can only #{action} files when you are on a branch", data: { container: 'body' }
|
||||||
|
elsif blob.lfs_pointer?
|
||||||
|
button_tag label, class: "btn btn-#{btn_class} disabled has_tooltip", title: "It is not possible to #{action} files that are stored in LFS using the web interface", data: { container: 'body' }
|
||||||
|
elsif can_edit_blob?(blob)
|
||||||
|
button_tag label, class: "btn btn-#{btn_class}", 'data-target' => "#modal-#{modal_type}-blob", 'data-toggle' => 'modal'
|
||||||
|
elsif can?(current_user, :fork_project, project)
|
||||||
|
continue_params = {
|
||||||
|
to: request.fullpath,
|
||||||
|
notice: edit_in_new_fork_notice + " Try to #{action} this file again.",
|
||||||
|
notice_now: edit_in_new_fork_notice_now
|
||||||
|
}
|
||||||
|
fork_path = namespace_project_fork_path(project.namespace, project, namespace_key: current_user.namespace.id,
|
||||||
|
continue: continue_params)
|
||||||
|
|
||||||
|
link_to label, fork_path, class: "btn btn-#{btn_class}", method: :post
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def replace_blob_link(project = @project, ref = @ref, path = @path)
|
||||||
|
modify_file_link(
|
||||||
|
project,
|
||||||
|
ref,
|
||||||
|
path,
|
||||||
|
label: "Replace",
|
||||||
|
action: "replace",
|
||||||
|
btn_class: "default",
|
||||||
|
modal_type: "upload"
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def delete_blob_link(project = @project, ref = @ref, path = @path)
|
||||||
|
modify_file_link(
|
||||||
|
project,
|
||||||
|
ref,
|
||||||
|
path,
|
||||||
|
label: "Delete",
|
||||||
|
action: "delete",
|
||||||
|
btn_class: "remove",
|
||||||
|
modal_type: "remove"
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def can_edit_blob?(blob, project = @project, ref = @ref)
|
||||||
|
!blob.lfs_pointer? && can_edit_tree?(project, ref)
|
||||||
end
|
end
|
||||||
|
|
||||||
def leave_edit_message
|
def leave_edit_message
|
||||||
|
@ -70,7 +128,7 @@ module BlobHelper
|
||||||
icon("#{file_type_icon_class('file', mode, name)} fw")
|
icon("#{file_type_icon_class('file', mode, name)} fw")
|
||||||
end
|
end
|
||||||
|
|
||||||
def blob_viewable?(blob)
|
def blob_text_viewable?(blob)
|
||||||
blob && blob.text? && !blob.lfs_pointer?
|
blob && blob.text? && !blob.lfs_pointer?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -94,11 +94,14 @@ module IssuesHelper
|
||||||
end.sort.to_sentence(last_word_connector: ', or ')
|
end.sort.to_sentence(last_word_connector: ', or ')
|
||||||
end
|
end
|
||||||
|
|
||||||
def url_to_emoji(name)
|
def emoji_icon(name, unicode = nil, aliases = [])
|
||||||
emoji_path = ::AwardEmoji.path_to_emoji_image(name)
|
unicode ||= Emoji.emoji_filename(name)
|
||||||
url_to_image(emoji_path)
|
|
||||||
rescue StandardError
|
content_tag :div, "",
|
||||||
""
|
class: "icon emoji-icon emoji-#{unicode}",
|
||||||
|
"data-emoji" => name,
|
||||||
|
"data-aliases" => aliases.join(" "),
|
||||||
|
"data-unicode-name" => unicode
|
||||||
end
|
end
|
||||||
|
|
||||||
def emoji_author_list(notes, current_user)
|
def emoji_author_list(notes, current_user)
|
||||||
|
@ -109,10 +112,6 @@ module IssuesHelper
|
||||||
list.join(", ")
|
list.join(", ")
|
||||||
end
|
end
|
||||||
|
|
||||||
def emoji_list
|
|
||||||
::AwardEmoji::EMOJI_LIST
|
|
||||||
end
|
|
||||||
|
|
||||||
def note_active_class(notes, current_user)
|
def note_active_class(notes, current_user)
|
||||||
if current_user && notes.pluck(:author_id).include?(current_user.id)
|
if current_user && notes.pluck(:author_id).include?(current_user.id)
|
||||||
"active"
|
"active"
|
||||||
|
|
|
@ -27,7 +27,16 @@ module MergeRequestsHelper
|
||||||
end
|
end
|
||||||
|
|
||||||
def ci_build_details_path(merge_request)
|
def ci_build_details_path(merge_request)
|
||||||
merge_request.source_project.ci_service.build_page(merge_request.last_commit.sha, merge_request.source_branch)
|
build_url = merge_request.source_project.ci_service.build_page(merge_request.last_commit.sha, merge_request.source_branch)
|
||||||
|
return nil unless build_url
|
||||||
|
|
||||||
|
parsed_url = URI.parse(build_url)
|
||||||
|
|
||||||
|
unless parsed_url.userinfo.blank?
|
||||||
|
parsed_url.userinfo = ''
|
||||||
|
end
|
||||||
|
|
||||||
|
parsed_url.to_s
|
||||||
end
|
end
|
||||||
|
|
||||||
def merge_path_description(merge_request, separator)
|
def merge_path_description(merge_request, separator)
|
||||||
|
|
|
@ -105,6 +105,14 @@ module ProjectsHelper
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def user_max_access_in_project(user_id, project)
|
||||||
|
level = project.team.max_member_access(user_id)
|
||||||
|
|
||||||
|
if level
|
||||||
|
Gitlab::Access.options_with_owner.key(level)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def get_project_nav_tabs(project, current_user)
|
def get_project_nav_tabs(project, current_user)
|
||||||
|
@ -277,14 +285,6 @@ module ProjectsHelper
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def user_max_access_in_project(user, project)
|
|
||||||
level = project.team.max_member_access(user)
|
|
||||||
|
|
||||||
if level
|
|
||||||
Gitlab::Access.options_with_owner.key(level)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def leave_project_message(project)
|
def leave_project_message(project)
|
||||||
"Are you sure you want to leave \"#{project.name}\" project?"
|
"Are you sure you want to leave \"#{project.name}\" project?"
|
||||||
end
|
end
|
||||||
|
|
|
@ -50,24 +50,49 @@ module TreeHelper
|
||||||
project.repository.branch_names.include?(ref)
|
project.repository.branch_names.include?(ref)
|
||||||
end
|
end
|
||||||
|
|
||||||
def allowed_tree_edit?(project = nil, ref = nil)
|
def can_edit_tree?(project = nil, ref = nil)
|
||||||
project ||= @project
|
project ||= @project
|
||||||
ref ||= @ref
|
ref ||= @ref
|
||||||
|
|
||||||
return false unless on_top_of_branch?(project, ref)
|
return false unless on_top_of_branch?(project, ref)
|
||||||
|
|
||||||
can?(current_user, :push_code, project)
|
can?(current_user, :push_code, project) ||
|
||||||
|
(current_user && current_user.already_forked?(project))
|
||||||
end
|
end
|
||||||
|
|
||||||
def tree_edit_branch(project = @project, ref = @ref)
|
def tree_edit_branch(project = @project, ref = @ref)
|
||||||
if allowed_tree_edit?(project, ref)
|
return unless can_edit_tree?(project, ref)
|
||||||
if can_push_branch?(project, ref)
|
|
||||||
ref
|
if can_push_branch?(project, ref)
|
||||||
else
|
ref
|
||||||
project.repository.next_patch_branch
|
else
|
||||||
end
|
project = tree_edit_project(project)
|
||||||
|
project.repository.next_patch_branch
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def tree_edit_project(project = @project)
|
||||||
|
if can?(current_user, :push_code, project)
|
||||||
|
project
|
||||||
|
elsif current_user && current_user.already_forked?(project)
|
||||||
|
current_user.fork_of(project)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def edit_in_new_fork_notice_now
|
||||||
|
"You're not allowed to make changes to this project directly." +
|
||||||
|
" A fork of this project is being created that you can make changes in, so you can submit a merge request."
|
||||||
|
end
|
||||||
|
|
||||||
|
def edit_in_new_fork_notice
|
||||||
|
"You're not allowed to make changes to this project directly." +
|
||||||
|
" A fork of this project has been created that you can make changes in, so you can submit a merge request."
|
||||||
|
end
|
||||||
|
|
||||||
|
def commit_in_fork_help
|
||||||
|
"A new branch will be created in your fork and a new merge request will be started."
|
||||||
|
end
|
||||||
|
|
||||||
def tree_breadcrumbs(tree, max_links = 2)
|
def tree_breadcrumbs(tree, max_links = 2)
|
||||||
if @path.present?
|
if @path.present?
|
||||||
part_path = ""
|
part_path = ""
|
||||||
|
|
|
@ -69,7 +69,6 @@ module VisibilityLevelHelper
|
||||||
|
|
||||||
def skip_level?(form_model, level)
|
def skip_level?(form_model, level)
|
||||||
form_model.is_a?(Project) &&
|
form_model.is_a?(Project) &&
|
||||||
form_model.forked? &&
|
!form_model.visibility_level_allowed?(level)
|
||||||
!Gitlab::VisibilityLevel.allowed_fork_levels(form_model.forked_from_project.visibility_level).include?(level)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -132,14 +132,14 @@ class Ability
|
||||||
end
|
end
|
||||||
|
|
||||||
def public_project_rules
|
def public_project_rules
|
||||||
project_guest_rules + [
|
@public_project_rules ||= project_guest_rules + [
|
||||||
:download_code,
|
:download_code,
|
||||||
:fork_project
|
:fork_project
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
def project_guest_rules
|
def project_guest_rules
|
||||||
[
|
@project_guest_rules ||= [
|
||||||
:read_project,
|
:read_project,
|
||||||
:read_wiki,
|
:read_wiki,
|
||||||
:read_issue,
|
:read_issue,
|
||||||
|
@ -157,7 +157,7 @@ class Ability
|
||||||
end
|
end
|
||||||
|
|
||||||
def project_report_rules
|
def project_report_rules
|
||||||
project_guest_rules + [
|
@project_report_rules ||= project_guest_rules + [
|
||||||
:create_commit_status,
|
:create_commit_status,
|
||||||
:read_commit_statuses,
|
:read_commit_statuses,
|
||||||
:download_code,
|
:download_code,
|
||||||
|
@ -170,7 +170,7 @@ class Ability
|
||||||
end
|
end
|
||||||
|
|
||||||
def project_dev_rules
|
def project_dev_rules
|
||||||
project_report_rules + [
|
@project_dev_rules ||= project_report_rules + [
|
||||||
:admin_merge_request,
|
:admin_merge_request,
|
||||||
:create_merge_request,
|
:create_merge_request,
|
||||||
:create_wiki,
|
:create_wiki,
|
||||||
|
@ -181,7 +181,7 @@ class Ability
|
||||||
end
|
end
|
||||||
|
|
||||||
def project_archived_rules
|
def project_archived_rules
|
||||||
[
|
@project_archived_rules ||= [
|
||||||
:create_merge_request,
|
:create_merge_request,
|
||||||
:push_code,
|
:push_code,
|
||||||
:push_code_to_protected_branches,
|
:push_code_to_protected_branches,
|
||||||
|
@ -191,7 +191,7 @@ class Ability
|
||||||
end
|
end
|
||||||
|
|
||||||
def project_master_rules
|
def project_master_rules
|
||||||
project_dev_rules + [
|
@project_master_rules ||= project_dev_rules + [
|
||||||
:push_code_to_protected_branches,
|
:push_code_to_protected_branches,
|
||||||
:update_project_snippet,
|
:update_project_snippet,
|
||||||
:update_merge_request,
|
:update_merge_request,
|
||||||
|
@ -206,7 +206,7 @@ class Ability
|
||||||
end
|
end
|
||||||
|
|
||||||
def project_admin_rules
|
def project_admin_rules
|
||||||
project_master_rules + [
|
@project_admin_rules ||= project_master_rules + [
|
||||||
:change_namespace,
|
:change_namespace,
|
||||||
:change_visibility_level,
|
:change_visibility_level,
|
||||||
:rename_project,
|
:rename_project,
|
||||||
|
@ -332,7 +332,7 @@ class Ability
|
||||||
end
|
end
|
||||||
|
|
||||||
if snippet.public? || snippet.internal?
|
if snippet.public? || snippet.internal?
|
||||||
rules << :read_personal_snippet
|
rules << :read_personal_snippet
|
||||||
end
|
end
|
||||||
|
|
||||||
rules
|
rules
|
||||||
|
|
|
@ -2,32 +2,34 @@
|
||||||
#
|
#
|
||||||
# Table name: application_settings
|
# Table name: application_settings
|
||||||
#
|
#
|
||||||
# id :integer not null, primary key
|
# id :integer not null, primary key
|
||||||
# default_projects_limit :integer
|
# default_projects_limit :integer
|
||||||
# signup_enabled :boolean
|
# signup_enabled :boolean
|
||||||
# signin_enabled :boolean
|
# signin_enabled :boolean
|
||||||
# gravatar_enabled :boolean
|
# gravatar_enabled :boolean
|
||||||
# sign_in_text :text
|
# sign_in_text :text
|
||||||
# created_at :datetime
|
# created_at :datetime
|
||||||
# updated_at :datetime
|
# updated_at :datetime
|
||||||
# home_page_url :string(255)
|
# home_page_url :string(255)
|
||||||
# default_branch_protection :integer default(2)
|
# default_branch_protection :integer default(2)
|
||||||
# twitter_sharing_enabled :boolean default(TRUE)
|
# twitter_sharing_enabled :boolean default(TRUE)
|
||||||
# restricted_visibility_levels :text
|
# restricted_visibility_levels :text
|
||||||
# version_check_enabled :boolean default(TRUE)
|
# version_check_enabled :boolean default(TRUE)
|
||||||
# max_attachment_size :integer default(10), not null
|
# max_attachment_size :integer default(10), 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
|
||||||
# user_oauth_applications :boolean default(TRUE)
|
# user_oauth_applications :boolean default(TRUE)
|
||||||
# after_sign_out_path :string(255)
|
# after_sign_out_path :string(255)
|
||||||
# session_expire_delay :integer default(10080), not null
|
# session_expire_delay :integer default(10080), not null
|
||||||
# import_sources :text
|
# import_sources :text
|
||||||
# help_page_text :text
|
# help_page_text :text
|
||||||
# admin_notification_email :string(255)
|
# admin_notification_email :string(255)
|
||||||
# shared_runners_enabled :boolean default(TRUE), not null
|
# shared_runners_enabled :boolean default(TRUE), not null
|
||||||
# max_artifacts_size :integer default(100), not null
|
# max_artifacts_size :integer default(100), not null
|
||||||
# runners_registration_token :string(255)
|
# runners_registration_token :string(255)
|
||||||
|
# require_two_factor_authentication :boolean default(TRUE)
|
||||||
|
# two_factor_grace_period :integer default(48)
|
||||||
#
|
#
|
||||||
|
|
||||||
class ApplicationSetting < ActiveRecord::Base
|
class ApplicationSetting < ActiveRecord::Base
|
||||||
|
@ -58,6 +60,9 @@ class ApplicationSetting < ActiveRecord::Base
|
||||||
allow_blank: true,
|
allow_blank: true,
|
||||||
email: true
|
email: true
|
||||||
|
|
||||||
|
validates :two_factor_grace_period,
|
||||||
|
numericality: { greater_than_or_equal_to: 0 }
|
||||||
|
|
||||||
validates_each :restricted_visibility_levels do |record, attr, value|
|
validates_each :restricted_visibility_levels do |record, attr, value|
|
||||||
unless value.nil?
|
unless value.nil?
|
||||||
value.each do |level|
|
value.each do |level|
|
||||||
|
@ -112,6 +117,8 @@ class ApplicationSetting < ActiveRecord::Base
|
||||||
import_sources: ['github','bitbucket','gitlab','gitorious','google_code','fogbugz','git'],
|
import_sources: ['github','bitbucket','gitlab','gitorious','google_code','fogbugz','git'],
|
||||||
shared_runners_enabled: Settings.gitlab_ci['shared_runners_enabled'],
|
shared_runners_enabled: Settings.gitlab_ci['shared_runners_enabled'],
|
||||||
max_artifacts_size: Settings.artifacts['max_size'],
|
max_artifacts_size: Settings.artifacts['max_size'],
|
||||||
|
require_two_factor_authentication: false,
|
||||||
|
two_factor_grace_period: 48
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -134,4 +141,8 @@ class ApplicationSetting < ActiveRecord::Base
|
||||||
/x)
|
/x)
|
||||||
self.restricted_signup_domains.reject! { |d| d.empty? }
|
self.restricted_signup_domains.reject! { |d| d.empty? }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def runners_registration_token
|
||||||
|
ensure_runners_registration_token!
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -135,6 +135,16 @@ module Ci
|
||||||
predefined_variables + yaml_variables + project_variables + trigger_variables
|
predefined_variables + yaml_variables + project_variables + trigger_variables
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def merge_request
|
||||||
|
merge_requests = MergeRequest.includes(:merge_request_diff)
|
||||||
|
.where(source_branch: ref, source_project_id: commit.gl_project_id)
|
||||||
|
.reorder(iid: :asc)
|
||||||
|
|
||||||
|
merge_requests.find do |merge_request|
|
||||||
|
merge_request.commits.any? { |ci| ci.id == commit.sha }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def project
|
def project
|
||||||
commit.project
|
commit.project
|
||||||
end
|
end
|
||||||
|
@ -170,7 +180,8 @@ module Ci
|
||||||
|
|
||||||
def extract_coverage(text, regex)
|
def extract_coverage(text, regex)
|
||||||
begin
|
begin
|
||||||
matches = text.gsub(Regexp.new(regex)).to_a.last
|
matches = text.scan(Regexp.new(regex)).last
|
||||||
|
matches = matches.last if matches.kind_of?(Array)
|
||||||
coverage = matches.gsub(/\d+(\.\d+)?/).first
|
coverage = matches.gsub(/\d+(\.\d+)?/).first
|
||||||
|
|
||||||
if coverage.present?
|
if coverage.present?
|
||||||
|
|
|
@ -37,7 +37,7 @@ module Participable
|
||||||
|
|
||||||
# Be aware that this method makes a lot of sql queries.
|
# Be aware that this method makes a lot of sql queries.
|
||||||
# Save result into variable if you are going to reuse it inside same request
|
# Save result into variable if you are going to reuse it inside same request
|
||||||
def participants(current_user = self.author, load_lazy_references: true)
|
def participants(current_user = self.author)
|
||||||
participants =
|
participants =
|
||||||
Gitlab::ReferenceExtractor.lazily do
|
Gitlab::ReferenceExtractor.lazily do
|
||||||
self.class.participant_attrs.flat_map do |attr|
|
self.class.participant_attrs.flat_map do |attr|
|
||||||
|
|
|
@ -18,15 +18,16 @@ module TokenAuthenticatable
|
||||||
|
|
||||||
define_method("ensure_#{token_field}") do
|
define_method("ensure_#{token_field}") do
|
||||||
current_token = read_attribute(token_field)
|
current_token = read_attribute(token_field)
|
||||||
if current_token.blank?
|
current_token.blank? ? write_new_token(token_field) : current_token
|
||||||
write_attribute(token_field, generate_token_for(token_field))
|
end
|
||||||
else
|
|
||||||
current_token
|
define_method("ensure_#{token_field}!") do
|
||||||
end
|
send("reset_#{token_field}!") if read_attribute(token_field).blank?
|
||||||
|
read_attribute(token_field)
|
||||||
end
|
end
|
||||||
|
|
||||||
define_method("reset_#{token_field}!") do
|
define_method("reset_#{token_field}!") do
|
||||||
write_attribute(token_field, generate_token_for(token_field))
|
write_new_token(token_field)
|
||||||
save!
|
save!
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -34,7 +35,12 @@ module TokenAuthenticatable
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def generate_token_for(token_field)
|
def write_new_token(token_field)
|
||||||
|
new_token = generate_token(token_field)
|
||||||
|
write_attribute(token_field, new_token)
|
||||||
|
end
|
||||||
|
|
||||||
|
def generate_token(token_field)
|
||||||
loop do
|
loop do
|
||||||
token = Devise.friendly_token
|
token = Devise.friendly_token
|
||||||
break token unless self.class.unscoped.find_by(token_field => token)
|
break token unless self.class.unscoped.find_by(token_field => token)
|
||||||
|
|
|
@ -16,7 +16,7 @@ class GlobalMilestone
|
||||||
end
|
end
|
||||||
|
|
||||||
def safe_title
|
def safe_title
|
||||||
@title.to_slug.to_s
|
@title.to_slug.normalize.to_s
|
||||||
end
|
end
|
||||||
|
|
||||||
def expired?
|
def expired?
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
|
|
||||||
class Identity < ActiveRecord::Base
|
class Identity < ActiveRecord::Base
|
||||||
include Sortable
|
include Sortable
|
||||||
|
include CaseSensitivity
|
||||||
belongs_to :user
|
belongs_to :user
|
||||||
|
|
||||||
validates :provider, presence: true
|
validates :provider, presence: true
|
||||||
|
|
|
@ -86,7 +86,7 @@ class Issue < ActiveRecord::Base
|
||||||
def referenced_merge_requests
|
def referenced_merge_requests
|
||||||
Gitlab::ReferenceExtractor.lazily do
|
Gitlab::ReferenceExtractor.lazily do
|
||||||
[self, *notes].flat_map do |note|
|
[self, *notes].flat_map do |note|
|
||||||
note.all_references(load_lazy_references: false).merge_requests
|
note.all_references.merge_requests
|
||||||
end
|
end
|
||||||
end.sort_by(&:iid)
|
end.sort_by(&:iid)
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
class JiraIssue < ExternalIssue
|
||||||
|
end
|
|
@ -335,7 +335,7 @@ class MergeRequest < ActiveRecord::Base
|
||||||
issues = commits.flat_map { |c| c.closes_issues(current_user) }
|
issues = commits.flat_map { |c| c.closes_issues(current_user) }
|
||||||
issues.push(*Gitlab::ClosingIssueExtractor.new(project, current_user).
|
issues.push(*Gitlab::ClosingIssueExtractor.new(project, current_user).
|
||||||
closed_by_message(description))
|
closed_by_message(description))
|
||||||
issues.uniq
|
issues.uniq(&:id)
|
||||||
else
|
else
|
||||||
[]
|
[]
|
||||||
end
|
end
|
||||||
|
|
|
@ -64,6 +64,19 @@ class Project < ActiveRecord::Base
|
||||||
update_column(:last_activity_at, self.created_at)
|
update_column(:last_activity_at, self.created_at)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# update visibility_levet of forks
|
||||||
|
after_update :update_forks_visibility_level
|
||||||
|
def update_forks_visibility_level
|
||||||
|
return unless visibility_level < visibility_level_was
|
||||||
|
|
||||||
|
forks.each do |forked_project|
|
||||||
|
if forked_project.visibility_level > visibility_level
|
||||||
|
forked_project.visibility_level = visibility_level
|
||||||
|
forked_project.save!
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
ActsAsTaggableOn.strict_case_match = true
|
ActsAsTaggableOn.strict_case_match = true
|
||||||
acts_as_taggable_on :tags
|
acts_as_taggable_on :tags
|
||||||
|
|
||||||
|
@ -100,9 +113,12 @@ class Project < ActiveRecord::Base
|
||||||
has_one :gitlab_issue_tracker_service, dependent: :destroy
|
has_one :gitlab_issue_tracker_service, dependent: :destroy
|
||||||
has_one :external_wiki_service, dependent: :destroy
|
has_one :external_wiki_service, dependent: :destroy
|
||||||
|
|
||||||
has_one :forked_project_link, dependent: :destroy, foreign_key: "forked_to_project_id"
|
has_one :forked_project_link, dependent: :destroy, foreign_key: "forked_to_project_id"
|
||||||
|
has_one :forked_from_project, through: :forked_project_link
|
||||||
|
|
||||||
|
has_many :forked_project_links, foreign_key: "forked_from_project_id"
|
||||||
|
has_many :forks, through: :forked_project_links, source: :forked_to_project
|
||||||
|
|
||||||
has_one :forked_from_project, through: :forked_project_link
|
|
||||||
# Merge Requests for target project should be removed with it
|
# Merge Requests for target project should be removed with it
|
||||||
has_many :merge_requests, dependent: :destroy, foreign_key: 'target_project_id'
|
has_many :merge_requests, dependent: :destroy, foreign_key: 'target_project_id'
|
||||||
# Merge requests from source project should be kept when source project was removed
|
# Merge requests from source project should be kept when source project was removed
|
||||||
|
@ -499,6 +515,10 @@ class Project < ActiveRecord::Base
|
||||||
@ci_service ||= ci_services.find(&:activated?)
|
@ci_service ||= ci_services.find(&:activated?)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def jira_tracker?
|
||||||
|
issues_tracker.to_param == 'jira'
|
||||||
|
end
|
||||||
|
|
||||||
def avatar_type
|
def avatar_type
|
||||||
unless self.avatar.image?
|
unless self.avatar.image?
|
||||||
self.errors.add :avatar, 'only images allowed'
|
self.errors.add :avatar, 'only images allowed'
|
||||||
|
@ -764,7 +784,7 @@ class Project < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
|
|
||||||
def forks_count
|
def forks_count
|
||||||
ForkedProjectLink.where(forked_from_project_id: self.id).count
|
forks.count
|
||||||
end
|
end
|
||||||
|
|
||||||
def find_label(name)
|
def find_label(name)
|
||||||
|
@ -799,6 +819,10 @@ class Project < ActiveRecord::Base
|
||||||
false
|
false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def jira_tracker_active?
|
||||||
|
jira_tracker? && jira_service.active
|
||||||
|
end
|
||||||
|
|
||||||
def ci_commit(sha)
|
def ci_commit(sha)
|
||||||
ci_commits.find_by(sha: sha)
|
ci_commits.find_by(sha: sha)
|
||||||
end
|
end
|
||||||
|
@ -854,4 +878,9 @@ class Project < ActiveRecord::Base
|
||||||
def open_issues_count
|
def open_issues_count
|
||||||
issues.opened.count
|
issues.opened.count
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def visibility_level_allowed?(level)
|
||||||
|
return true unless forked?
|
||||||
|
Gitlab::VisibilityLevel.allowed_fork_levels(forked_from_project.visibility_level).include?(level.to_i)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -18,6 +18,11 @@
|
||||||
# note_events :boolean default(TRUE), not null
|
# note_events :boolean default(TRUE), not null
|
||||||
#
|
#
|
||||||
|
|
||||||
|
# TODO(ayufan): The GitLabCiService is deprecated and the type should be removed when the database entries are removed
|
||||||
class GitlabCiService < CiService
|
class GitlabCiService < CiService
|
||||||
# this is no longer used
|
# We override the active accessor to always make GitLabCiService disabled
|
||||||
|
# Otherwise the GitLabCiService can be picked, but should never be since it's deprecated
|
||||||
|
def active
|
||||||
|
false
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -19,9 +19,24 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
class JiraService < IssueTrackerService
|
class JiraService < IssueTrackerService
|
||||||
|
include HTTParty
|
||||||
include Gitlab::Application.routes.url_helpers
|
include Gitlab::Application.routes.url_helpers
|
||||||
|
|
||||||
prop_accessor :title, :description, :project_url, :issues_url, :new_issue_url
|
DEFAULT_API_VERSION = 2
|
||||||
|
|
||||||
|
prop_accessor :username, :password, :api_url, :jira_issue_transition_id,
|
||||||
|
:title, :description, :project_url, :issues_url, :new_issue_url
|
||||||
|
|
||||||
|
before_validation :set_api_url, :set_jira_issue_transition_id
|
||||||
|
|
||||||
|
before_update :reset_password
|
||||||
|
|
||||||
|
def reset_password
|
||||||
|
# don't reset the password if a new one is provided
|
||||||
|
if api_url_changed? && !password_touched?
|
||||||
|
self.password = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def help
|
def help
|
||||||
line1 = 'Setting `project_url`, `issues_url` and `new_issue_url` will '\
|
line1 = 'Setting `project_url`, `issues_url` and `new_issue_url` will '\
|
||||||
|
@ -54,4 +69,228 @@ class JiraService < IssueTrackerService
|
||||||
def to_param
|
def to_param
|
||||||
'jira'
|
'jira'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def fields
|
||||||
|
super.push(
|
||||||
|
{ type: 'text', name: 'api_url', placeholder: 'https://jira.example.com/rest/api/2' },
|
||||||
|
{ type: 'text', name: 'username', placeholder: '' },
|
||||||
|
{ type: 'password', name: 'password', placeholder: '' },
|
||||||
|
{ type: 'text', name: 'jira_issue_transition_id', placeholder: '2' }
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def execute(push, issue = nil)
|
||||||
|
if issue.nil?
|
||||||
|
# No specific issue, that means
|
||||||
|
# we just want to test settings
|
||||||
|
test_settings
|
||||||
|
else
|
||||||
|
close_issue(push, issue)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_cross_reference_note(mentioned, noteable, author)
|
||||||
|
issue_name = mentioned.id
|
||||||
|
project = self.project
|
||||||
|
noteable_name = noteable.class.name.underscore.downcase
|
||||||
|
noteable_id = if noteable.is_a?(Commit)
|
||||||
|
noteable.id
|
||||||
|
else
|
||||||
|
noteable.iid
|
||||||
|
end
|
||||||
|
|
||||||
|
entity_url = build_entity_url(noteable_name.to_sym, noteable_id)
|
||||||
|
|
||||||
|
data = {
|
||||||
|
user: {
|
||||||
|
name: author.name,
|
||||||
|
url: resource_url(user_path(author)),
|
||||||
|
},
|
||||||
|
project: {
|
||||||
|
name: project.path_with_namespace,
|
||||||
|
url: resource_url(namespace_project_path(project.namespace, project))
|
||||||
|
},
|
||||||
|
entity: {
|
||||||
|
name: noteable_name.humanize.downcase,
|
||||||
|
url: entity_url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
add_comment(data, issue_name)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_settings
|
||||||
|
result = JiraService.get(
|
||||||
|
jira_api_test_url,
|
||||||
|
headers: {
|
||||||
|
'Content-Type' => 'application/json',
|
||||||
|
'Authorization' => "Basic #{auth}"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
case result.code
|
||||||
|
when 201, 200
|
||||||
|
Rails.logger.info("#{self.class.name} SUCCESS #{result.code}: Successfully connected to #{api_url}.")
|
||||||
|
true
|
||||||
|
else
|
||||||
|
Rails.logger.info("#{self.class.name} ERROR #{result.code}: #{result.parsed_response}")
|
||||||
|
false
|
||||||
|
end
|
||||||
|
rescue Errno::ECONNREFUSED => e
|
||||||
|
Rails.logger.info "#{self.class.name} ERROR: #{e.message}. API URL: #{api_url}."
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def build_api_url_from_project_url
|
||||||
|
server = URI(project_url)
|
||||||
|
default_ports = [["http",80],["https",443]].include?([server.scheme,server.port])
|
||||||
|
server_url = "#{server.scheme}://#{server.host}"
|
||||||
|
server_url.concat(":#{server.port}") unless default_ports
|
||||||
|
"#{server_url}/rest/api/#{DEFAULT_API_VERSION}"
|
||||||
|
rescue
|
||||||
|
"" # looks like project URL was not valid
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_api_url
|
||||||
|
self.api_url = build_api_url_from_project_url if self.api_url.blank?
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_jira_issue_transition_id
|
||||||
|
self.jira_issue_transition_id ||= "2"
|
||||||
|
end
|
||||||
|
|
||||||
|
def close_issue(entity, issue)
|
||||||
|
commit_id = if entity.is_a?(Commit)
|
||||||
|
entity.id
|
||||||
|
elsif entity.is_a?(MergeRequest)
|
||||||
|
entity.last_commit.id
|
||||||
|
end
|
||||||
|
commit_url = build_entity_url(:commit, commit_id)
|
||||||
|
|
||||||
|
# Depending on the JIRA project's workflow, a comment during transition
|
||||||
|
# may or may not be allowed. Split the operation in to two calls so the
|
||||||
|
# comment always works.
|
||||||
|
transition_issue(issue)
|
||||||
|
add_issue_solved_comment(issue, commit_id, commit_url)
|
||||||
|
end
|
||||||
|
|
||||||
|
def transition_issue(issue)
|
||||||
|
message = {
|
||||||
|
transition: {
|
||||||
|
id: jira_issue_transition_id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
send_message(close_issue_url(issue.iid), message.to_json)
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_issue_solved_comment(issue, commit_id, commit_url)
|
||||||
|
comment = {
|
||||||
|
body: "Issue solved with [#{commit_id}|#{commit_url}]."
|
||||||
|
}
|
||||||
|
|
||||||
|
send_message(comment_url(issue.iid), comment.to_json)
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_comment(data, issue_name)
|
||||||
|
url = comment_url(issue_name)
|
||||||
|
user_name = data[:user][:name]
|
||||||
|
user_url = data[:user][:url]
|
||||||
|
entity_name = data[:entity][:name]
|
||||||
|
entity_url = data[:entity][:url]
|
||||||
|
project_name = data[:project][:name]
|
||||||
|
|
||||||
|
message = {
|
||||||
|
body: "[#{user_name}|#{user_url}] mentioned this issue in [a #{entity_name} of #{project_name}|#{entity_url}]."
|
||||||
|
}
|
||||||
|
|
||||||
|
unless existing_comment?(issue_name, message[:body])
|
||||||
|
send_message(url, message.to_json)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
def auth
|
||||||
|
require 'base64'
|
||||||
|
Base64.urlsafe_encode64("#{self.username}:#{self.password}")
|
||||||
|
end
|
||||||
|
|
||||||
|
def send_message(url, message)
|
||||||
|
result = JiraService.post(
|
||||||
|
url,
|
||||||
|
body: message,
|
||||||
|
headers: {
|
||||||
|
'Content-Type' => 'application/json',
|
||||||
|
'Authorization' => "Basic #{auth}"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
message = case result.code
|
||||||
|
when 201, 200, 204
|
||||||
|
"#{self.class.name} SUCCESS #{result.code}: Successfully posted to #{url}."
|
||||||
|
when 401
|
||||||
|
"#{self.class.name} ERROR 401: Unauthorized. Check the #{self.username} credentials and JIRA access permissions and try again."
|
||||||
|
else
|
||||||
|
"#{self.class.name} ERROR #{result.code}: #{result.parsed_response}"
|
||||||
|
end
|
||||||
|
|
||||||
|
Rails.logger.info(message)
|
||||||
|
message
|
||||||
|
rescue URI::InvalidURIError, Errno::ECONNREFUSED => e
|
||||||
|
Rails.logger.info "#{self.class.name} ERROR: #{e.message}. Hostname: #{url}."
|
||||||
|
end
|
||||||
|
|
||||||
|
def existing_comment?(issue_name, new_comment)
|
||||||
|
result = JiraService.get(
|
||||||
|
comment_url(issue_name),
|
||||||
|
headers: {
|
||||||
|
'Content-Type' => 'application/json',
|
||||||
|
'Authorization' => "Basic #{auth}"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
case result.code
|
||||||
|
when 201, 200
|
||||||
|
existing_comments = JSON.parse(result.body)['comments']
|
||||||
|
|
||||||
|
if existing_comments.present?
|
||||||
|
return existing_comments.map { |comment| comment['body'].include?(new_comment) }.any?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
false
|
||||||
|
rescue JSON::ParserError
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
def resource_url(resource)
|
||||||
|
"#{Settings.gitlab['url'].chomp("/")}#{resource}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def build_entity_url(entity_name, entity_id)
|
||||||
|
resource_url(
|
||||||
|
polymorphic_url(
|
||||||
|
[
|
||||||
|
self.project.namespace.becomes(Namespace),
|
||||||
|
self.project,
|
||||||
|
entity_name
|
||||||
|
],
|
||||||
|
id: entity_id,
|
||||||
|
routing_type: :path
|
||||||
|
)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def close_issue_url(issue_name)
|
||||||
|
"#{self.api_url}/issue/#{issue_name}/transitions"
|
||||||
|
end
|
||||||
|
|
||||||
|
def comment_url(issue_name)
|
||||||
|
"#{self.api_url}/issue/#{issue_name}/comment"
|
||||||
|
end
|
||||||
|
|
||||||
|
def jira_api_test_url
|
||||||
|
"#{self.api_url}/myself"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -592,47 +592,54 @@ class Repository
|
||||||
Gitlab::Popen.popen(args, path_to_repo)
|
Gitlab::Popen.popen(args, path_to_repo)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def with_tmp_ref(oldrev = nil)
|
||||||
|
random_string = SecureRandom.hex
|
||||||
|
tmp_ref = "refs/tmp/#{random_string}/head"
|
||||||
|
|
||||||
|
if oldrev && !Gitlab::Git.blank_ref?(oldrev)
|
||||||
|
rugged.references.create(tmp_ref, oldrev)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Make commit in tmp ref
|
||||||
|
yield(tmp_ref)
|
||||||
|
ensure
|
||||||
|
rugged.references.delete(tmp_ref) rescue nil
|
||||||
|
end
|
||||||
|
|
||||||
def commit_with_hooks(current_user, branch)
|
def commit_with_hooks(current_user, branch)
|
||||||
oldrev = Gitlab::Git::BLANK_SHA
|
oldrev = Gitlab::Git::BLANK_SHA
|
||||||
ref = Gitlab::Git::BRANCH_REF_PREFIX + branch
|
ref = Gitlab::Git::BRANCH_REF_PREFIX + branch
|
||||||
was_empty = empty?
|
was_empty = empty?
|
||||||
|
|
||||||
# Create temporary ref
|
|
||||||
random_string = SecureRandom.hex
|
|
||||||
tmp_ref = "refs/tmp/#{random_string}/head"
|
|
||||||
|
|
||||||
unless was_empty
|
unless was_empty
|
||||||
oldrev = find_branch(branch).target
|
oldrev = find_branch(branch).target
|
||||||
rugged.references.create(tmp_ref, oldrev)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Make commit in tmp ref
|
with_tmp_ref(oldrev) do |tmp_ref|
|
||||||
newrev = yield(tmp_ref)
|
# Make commit in tmp ref
|
||||||
|
newrev = yield(tmp_ref)
|
||||||
|
|
||||||
unless newrev
|
unless newrev
|
||||||
raise CommitError.new('Failed to create commit')
|
raise CommitError.new('Failed to create commit')
|
||||||
end
|
end
|
||||||
|
|
||||||
GitHooksService.new.execute(current_user, path_to_repo, oldrev, newrev, ref) do
|
GitHooksService.new.execute(current_user, path_to_repo, oldrev, newrev, ref) do
|
||||||
if was_empty
|
if was_empty
|
||||||
# Create branch
|
# Create branch
|
||||||
rugged.references.create(ref, newrev)
|
rugged.references.create(ref, newrev)
|
||||||
else
|
|
||||||
# Update head
|
|
||||||
current_head = find_branch(branch).target
|
|
||||||
|
|
||||||
# Make sure target branch was not changed during pre-receive hook
|
|
||||||
if current_head == oldrev
|
|
||||||
rugged.references.update(ref, newrev)
|
|
||||||
else
|
else
|
||||||
raise CommitError.new('Commit was rejected because branch received new push')
|
# Update head
|
||||||
|
current_head = find_branch(branch).target
|
||||||
|
|
||||||
|
# Make sure target branch was not changed during pre-receive hook
|
||||||
|
if current_head == oldrev
|
||||||
|
rugged.references.update(ref, newrev)
|
||||||
|
else
|
||||||
|
raise CommitError.new('Commit was rejected because branch received new push')
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
rescue GitHooksService::PreReceiveError
|
|
||||||
# Remove tmp ref and return error to user
|
|
||||||
rugged.references.delete(tmp_ref)
|
|
||||||
raise
|
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
# bio :string(255)
|
# bio :string(255)
|
||||||
# failed_attempts :integer default(0)
|
# failed_attempts :integer default(0)
|
||||||
# locked_at :datetime
|
# locked_at :datetime
|
||||||
|
# unlock_token :string(255)
|
||||||
# username :string(255)
|
# username :string(255)
|
||||||
# can_create_group :boolean default(TRUE), not null
|
# can_create_group :boolean default(TRUE), not null
|
||||||
# can_create_team :boolean default(TRUE), not null
|
# can_create_team :boolean default(TRUE), not null
|
||||||
|
|
|
@ -39,10 +39,7 @@ class BaseService
|
||||||
def deny_visibility_level(model, denied_visibility_level = nil)
|
def deny_visibility_level(model, denied_visibility_level = nil)
|
||||||
denied_visibility_level ||= model.visibility_level
|
denied_visibility_level ||= model.visibility_level
|
||||||
|
|
||||||
level_name = 'Unknown'
|
level_name = Gitlab::VisibilityLevel.level_name(denied_visibility_level)
|
||||||
Gitlab::VisibilityLevel.options.each do |name, level|
|
|
||||||
level_name = name if level == denied_visibility_level
|
|
||||||
end
|
|
||||||
|
|
||||||
model.errors.add(
|
model.errors.add(
|
||||||
:visibility_level,
|
:visibility_level,
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
require_relative 'base_service'
|
require_relative 'base_service'
|
||||||
|
|
||||||
class CreateBranchService < BaseService
|
class CreateBranchService < BaseService
|
||||||
def execute(branch_name, ref)
|
def execute(branch_name, ref, source_project: @project)
|
||||||
valid_branch = Gitlab::GitRefValidator.validate(branch_name)
|
valid_branch = Gitlab::GitRefValidator.validate(branch_name)
|
||||||
if valid_branch == false
|
if valid_branch == false
|
||||||
return error('Branch name invalid')
|
return error('Branch name is invalid')
|
||||||
end
|
end
|
||||||
|
|
||||||
repository = project.repository
|
repository = project.repository
|
||||||
|
@ -13,7 +13,20 @@ class CreateBranchService < BaseService
|
||||||
return error('Branch already exists')
|
return error('Branch already exists')
|
||||||
end
|
end
|
||||||
|
|
||||||
new_branch = repository.add_branch(current_user, branch_name, ref)
|
new_branch = nil
|
||||||
|
if source_project != @project
|
||||||
|
repository.with_tmp_ref do |tmp_ref|
|
||||||
|
repository.fetch_ref(
|
||||||
|
source_project.repository.path_to_repo,
|
||||||
|
"refs/heads/#{ref}",
|
||||||
|
tmp_ref
|
||||||
|
)
|
||||||
|
|
||||||
|
new_branch = repository.add_branch(current_user, branch_name, tmp_ref)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
new_branch = repository.add_branch(current_user, branch_name, ref)
|
||||||
|
end
|
||||||
|
|
||||||
if new_branch
|
if new_branch
|
||||||
push_data = build_push_data(project, current_user, new_branch)
|
push_data = build_push_data(project, current_user, new_branch)
|
||||||
|
|
|
@ -3,8 +3,10 @@ module Files
|
||||||
class ValidationError < StandardError; end
|
class ValidationError < StandardError; end
|
||||||
|
|
||||||
def execute
|
def execute
|
||||||
@current_branch = params[:current_branch]
|
@source_project = params[:source_project] || @project
|
||||||
|
@source_branch = params[:source_branch]
|
||||||
@target_branch = params[:target_branch]
|
@target_branch = params[:target_branch]
|
||||||
|
|
||||||
@commit_message = params[:commit_message]
|
@commit_message = params[:commit_message]
|
||||||
@file_path = params[:file_path]
|
@file_path = params[:file_path]
|
||||||
@file_content = if params[:file_content_encoding] == 'base64'
|
@file_content = if params[:file_content_encoding] == 'base64'
|
||||||
|
@ -16,8 +18,8 @@ module Files
|
||||||
# Validate parameters
|
# Validate parameters
|
||||||
validate
|
validate
|
||||||
|
|
||||||
# Create new branch if it different from current_branch
|
# Create new branch if it different from source_branch
|
||||||
if @target_branch != @current_branch
|
if different_branch?
|
||||||
create_target_branch
|
create_target_branch
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -26,18 +28,14 @@ module Files
|
||||||
else
|
else
|
||||||
error("Something went wrong. Your changes were not committed")
|
error("Something went wrong. Your changes were not committed")
|
||||||
end
|
end
|
||||||
rescue Repository::CommitError, GitHooksService::PreReceiveError, ValidationError => ex
|
rescue Repository::CommitError, Gitlab::Git::Repository::InvalidBlobName, GitHooksService::PreReceiveError, ValidationError => ex
|
||||||
error(ex.message)
|
error(ex.message)
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def current_branch
|
def different_branch?
|
||||||
@current_branch ||= params[:current_branch]
|
@source_branch != @target_branch || @source_project != @project
|
||||||
end
|
|
||||||
|
|
||||||
def target_branch
|
|
||||||
@target_branch ||= params[:target_branch]
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def raise_error(message)
|
def raise_error(message)
|
||||||
|
@ -52,11 +50,11 @@ module Files
|
||||||
end
|
end
|
||||||
|
|
||||||
unless project.empty_repo?
|
unless project.empty_repo?
|
||||||
unless repository.branch_names.include?(@current_branch)
|
unless @source_project.repository.branch_names.include?(@source_branch)
|
||||||
raise_error("You can only create or edit files when you are on a branch")
|
raise_error("You can only create or edit files when you are on a branch")
|
||||||
end
|
end
|
||||||
|
|
||||||
if @current_branch != @target_branch
|
if different_branch?
|
||||||
if repository.branch_names.include?(@target_branch)
|
if repository.branch_names.include?(@target_branch)
|
||||||
raise_error("Branch with such name already exists. You need to switch to this branch in order to make changes")
|
raise_error("Branch with such name already exists. You need to switch to this branch in order to make changes")
|
||||||
end
|
end
|
||||||
|
@ -65,10 +63,10 @@ module Files
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_target_branch
|
def create_target_branch
|
||||||
result = CreateBranchService.new(project, current_user).execute(@target_branch, @current_branch)
|
result = CreateBranchService.new(project, current_user).execute(@target_branch, @source_branch, source_project: @source_project)
|
||||||
|
|
||||||
unless result[:status] == :success
|
unless result[:status] == :success
|
||||||
raise_error("Something went wrong when we tried to create #{@target_branch} for you")
|
raise_error("Something went wrong when we tried to create #{@target_branch} for you: #{result[:message]}")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -26,7 +26,7 @@ module Files
|
||||||
unless project.empty_repo?
|
unless project.empty_repo?
|
||||||
@file_path.slice!(0) if @file_path.start_with?('/')
|
@file_path.slice!(0) if @file_path.start_with?('/')
|
||||||
|
|
||||||
blob = repository.blob_at_branch(@current_branch, @file_path)
|
blob = repository.blob_at_branch(@source_branch, @file_path)
|
||||||
|
|
||||||
if blob
|
if blob
|
||||||
raise_error("Your changes could not be committed because a file with the same name already exists")
|
raise_error("Your changes could not be committed because a file with the same name already exists")
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
module Issues
|
module Issues
|
||||||
class CloseService < Issues::BaseService
|
class CloseService < Issues::BaseService
|
||||||
def execute(issue, commit = nil)
|
def execute(issue, commit = nil)
|
||||||
|
if project.jira_tracker? && project.jira_service.active
|
||||||
|
project.jira_service.execute(commit, issue)
|
||||||
|
return issue
|
||||||
|
end
|
||||||
|
|
||||||
if project.default_issues_tracker? && 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)
|
||||||
|
|
|
@ -3,12 +3,16 @@ module Projects
|
||||||
def execute
|
def execute
|
||||||
# check that user is allowed to set specified visibility_level
|
# check that user is allowed to set specified visibility_level
|
||||||
new_visibility = params[:visibility_level]
|
new_visibility = params[:visibility_level]
|
||||||
if new_visibility && new_visibility.to_i != project.visibility_level
|
if new_visibility
|
||||||
unless can?(current_user, :change_visibility_level, project) &&
|
if new_visibility.to_i != project.visibility_level
|
||||||
Gitlab::VisibilityLevel.allowed_for?(current_user, new_visibility)
|
unless can?(current_user, :change_visibility_level, project) &&
|
||||||
deny_visibility_level(project, new_visibility)
|
Gitlab::VisibilityLevel.allowed_for?(current_user, new_visibility)
|
||||||
return project
|
deny_visibility_level(project, new_visibility)
|
||||||
|
return project
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
return false unless visibility_level_allowed?(new_visibility)
|
||||||
end
|
end
|
||||||
|
|
||||||
new_branch = params[:default_branch]
|
new_branch = params[:default_branch]
|
||||||
|
@ -23,5 +27,19 @@ module Projects
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def visibility_level_allowed?(level)
|
||||||
|
return true if project.visibility_level_allowed?(level)
|
||||||
|
|
||||||
|
level_name = Gitlab::VisibilityLevel.level_name(level)
|
||||||
|
project.errors.add(
|
||||||
|
:visibility_level,
|
||||||
|
"#{level_name} could not be set as visibility level of this project - parent project settings are more restrictive"
|
||||||
|
)
|
||||||
|
|
||||||
|
false
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -241,9 +241,14 @@ class SystemNoteService
|
||||||
note_options.merge!(noteable: noteable)
|
note_options.merge!(noteable: noteable)
|
||||||
end
|
end
|
||||||
|
|
||||||
create_note(note_options)
|
if noteable.is_a?(ExternalIssue)
|
||||||
|
noteable.project.issues_tracker.create_cross_reference_note(noteable, mentioner, author)
|
||||||
|
else
|
||||||
|
create_note(note_options)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def self.cross_reference?(note_text)
|
def self.cross_reference?(note_text)
|
||||||
note_text.start_with?(cross_reference_note_prefix)
|
note_text.start_with?(cross_reference_note_prefix)
|
||||||
end
|
end
|
||||||
|
@ -259,7 +264,7 @@ class SystemNoteService
|
||||||
#
|
#
|
||||||
# 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)
|
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)
|
||||||
|
|
||||||
|
|
|
@ -104,6 +104,18 @@
|
||||||
= f.label :signin_enabled do
|
= f.label :signin_enabled do
|
||||||
= f.check_box :signin_enabled
|
= f.check_box :signin_enabled
|
||||||
Sign-in enabled
|
Sign-in enabled
|
||||||
|
.form-group
|
||||||
|
= f.label :two_factor_authentication, 'Two-Factor authentication', class: 'control-label col-sm-2'
|
||||||
|
.col-sm-10
|
||||||
|
.checkbox
|
||||||
|
= f.label :require_two_factor_authentication do
|
||||||
|
= f.check_box :require_two_factor_authentication
|
||||||
|
Require all users to setup Two-Factor authentication
|
||||||
|
.form-group
|
||||||
|
= f.label :two_factor_authentication, 'Two-Factor grace period (hours)', class: 'control-label col-sm-2'
|
||||||
|
.col-sm-10
|
||||||
|
= f.number_field :two_factor_grace_period, min: 0, class: 'form-control', placeholder: '0'
|
||||||
|
.help-block Amount of time (in hours) that users are allowed to skip forced configuration of two-factor authentication
|
||||||
.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
|
||||||
|
|
|
@ -79,6 +79,10 @@
|
||||||
GitLab API
|
GitLab API
|
||||||
%span.pull-right
|
%span.pull-right
|
||||||
= API::API::version
|
= API::API::version
|
||||||
|
%p
|
||||||
|
Git
|
||||||
|
%span.pull-right
|
||||||
|
= Gitlab::Git.version
|
||||||
%p
|
%p
|
||||||
Ruby
|
Ruby
|
||||||
%span.pull-right
|
%span.pull-right
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
- page_title "Identities", @user.name, "Users"
|
- page_title "Identities", @user.name, "Users"
|
||||||
= render 'admin/users/head'
|
= render 'admin/users/head'
|
||||||
|
|
||||||
|
= link_to 'New Identity', new_admin_user_identity_path, class: 'pull-right btn btn-new'
|
||||||
- if @identities.present?
|
- if @identities.present?
|
||||||
.table-holder
|
.table-holder
|
||||||
%table.table
|
%table.table
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
- page_title "New Identity"
|
||||||
|
%h3.page-title New identity
|
||||||
|
%hr
|
||||||
|
= render 'form'
|
|
@ -3,7 +3,7 @@
|
||||||
To register a new runner you should enter the following registration token.
|
To register a new runner you should enter the following registration token.
|
||||||
With this token the runner will request a unique runner token and use that for future communication.
|
With this token the runner will request a unique runner token and use that for future communication.
|
||||||
Registration token is
|
Registration token is
|
||||||
%code{ id: 'runners-token' } #{current_application_settings.ensure_runners_registration_token}
|
%code{ id: 'runners-token' } #{current_application_settings.runners_registration_token}
|
||||||
|
|
||||||
.bs-callout.clearfix
|
.bs-callout.clearfix
|
||||||
.pull-left
|
.pull-left
|
||||||
|
|
|
@ -41,5 +41,3 @@
|
||||||
%i.fa.fa-remove.incorrect-syntax
|
%i.fa.fa-remove.incorrect-syntax
|
||||||
%b Error:
|
%b Error:
|
||||||
= @error
|
= @error
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
:plain
|
|
||||||
$(".results").html("#{escape_javascript(render "create")}")
|
|
|
@ -1,27 +1,17 @@
|
||||||
%h2 Check your .gitlab-ci.yml
|
%h2 Check your .gitlab-ci.yml
|
||||||
%hr
|
%hr
|
||||||
|
|
||||||
= form_tag ci_lint_path, method: :post, remote: true do
|
.row
|
||||||
.control-group
|
= form_tag ci_lint_path, method: :post do
|
||||||
= label_tag :content, "Content of .gitlab-ci.yml", class: 'control-label'
|
.form-group
|
||||||
.controls
|
= label_tag :content, 'Content of .gitlab-ci.yml', class: 'control-label text-nowrap'
|
||||||
= text_area_tag :content, nil, class: 'form-control span1', rows: 7, require: true
|
.col-sm-12
|
||||||
|
= text_area_tag :content, nil, class: 'form-control span1', rows: 7, require: true
|
||||||
|
.col-sm-12
|
||||||
|
.pull-left.prepend-top-10
|
||||||
|
= submit_tag 'Validate', class: 'btn btn-success submit-yml'
|
||||||
|
|
||||||
.control-group.clearfix
|
.row.prepend-top-20
|
||||||
.controls.pull-left.prepend-top-10
|
.col-sm-12
|
||||||
= submit_tag "Validate", class: 'btn btn-success submit-yml'
|
.results
|
||||||
|
= render partial: 'create' if defined?(@status)
|
||||||
|
|
||||||
%p.text-center.loading
|
|
||||||
%i.fa.fa-refresh.fa-spin
|
|
||||||
|
|
||||||
.results.prepend-top-20
|
|
||||||
|
|
||||||
:javascript
|
|
||||||
$(".loading").hide();
|
|
||||||
$('form').bind('ajax:beforeSend', function() {
|
|
||||||
$(".loading").show();
|
|
||||||
});
|
|
||||||
$('form').bind('ajax:complete', function() {
|
|
||||||
$(".loading").hide();
|
|
||||||
});
|
|
||||||
|
|
|
@ -1,13 +1,20 @@
|
||||||
= content_for :flash_message do
|
= content_for :flash_message do
|
||||||
= render 'shared/project_limit'
|
= render 'shared/project_limit'
|
||||||
|
.top-area
|
||||||
|
%ul.left-top-menu
|
||||||
|
= nav_link(page: [dashboard_projects_path, root_path]) do
|
||||||
|
= link_to dashboard_projects_path, title: 'Home', class: 'shortcuts-activity', data: {placement: 'right'} do
|
||||||
|
Your Projects
|
||||||
|
= nav_link(page: starred_dashboard_projects_path) do
|
||||||
|
= link_to starred_dashboard_projects_path, title: 'Starred Projects', data: {placement: 'right'} do
|
||||||
|
Starred Projects
|
||||||
|
= nav_link(page: [explore_root_path, trending_explore_projects_path, starred_explore_projects_path, explore_projects_path], html_options: { class: 'hidden-xs' }) do
|
||||||
|
= link_to explore_root_path, title: 'Explore', data: {placement: 'right'} do
|
||||||
|
Explore Projects
|
||||||
|
|
||||||
%ul.center-top-menu
|
.projects-search-form
|
||||||
= nav_link(page: [dashboard_projects_path, root_path]) do
|
= search_field_tag :filter_projects, nil, placeholder: 'Filter by name...', class: 'projects-list-filter form-control hidden-xs', spellcheck: false
|
||||||
= link_to dashboard_projects_path, title: 'Home', class: 'shortcuts-activity', data: {placement: 'right'} do
|
- if current_user.can_create_project?
|
||||||
Your Projects
|
= link_to new_project_path, class: 'btn btn-green' do
|
||||||
= nav_link(page: starred_dashboard_projects_path) do
|
%i.fa.fa-plus
|
||||||
= link_to starred_dashboard_projects_path, title: 'Starred Projects', data: {placement: 'right'} do
|
New Project
|
||||||
Starred Projects
|
|
||||||
= nav_link(page: [explore_root_path, trending_explore_projects_path, starred_explore_projects_path, explore_projects_path], html_options: { class: 'hidden-xs' }) do
|
|
||||||
= link_to explore_root_path, title: 'Explore', data: {placement: 'right'} do
|
|
||||||
Explore Projects
|
|
||||||
|
|
|
@ -1,11 +1,3 @@
|
||||||
.projects-list-holder
|
.projects-list-holder
|
||||||
.projects-search-form
|
|
||||||
.input-group
|
|
||||||
= search_field_tag :filter_projects, nil, placeholder: 'Filter by name', class: 'projects-list-filter form-control', spellcheck: false
|
|
||||||
- if current_user.can_create_project?
|
|
||||||
%span.input-group-btn
|
|
||||||
= link_to new_project_path, class: 'btn btn-green' do
|
|
||||||
%i.fa.fa-plus
|
|
||||||
New Project
|
|
||||||
|
|
||||||
= render 'shared/projects/list', projects: @projects, ci: true
|
= render 'shared/projects/list', projects: @projects, ci: true
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
<p>Hello <%= @resource.email %>!</p>
|
|
||||||
|
|
||||||
<p>Your account has been locked due to an excessive amount of unsuccessful sign in attempts.</p>
|
|
||||||
|
|
||||||
<p>Click the link below to unlock your account:</p>
|
|
||||||
|
|
||||||
<p><%= link_to 'Unlock your account', unlock_url(@resource, unlock_token: @token) %></p>
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
%p
|
||||||
|
Hello #{@resource.name}!
|
||||||
|
|
||||||
|
%p
|
||||||
|
Your GitLab account has been locked due to an excessive amount of unsuccessful
|
||||||
|
sign in attempts. Your account will automatically unlock in
|
||||||
|
= time_ago_in_words(Devise.unlock_in.from_now)
|
||||||
|
or you may click the link below to unlock now.
|
||||||
|
|
||||||
|
%p= link_to 'Unlock your account', unlock_url(@resource, unlock_token: @token)
|
|
@ -1,12 +0,0 @@
|
||||||
<h2>Resend unlock instructions</h2>
|
|
||||||
|
|
||||||
<%= form_for(resource, as: resource_name, url: unlock_path(resource_name), html: { method: :post }) do |f| %>
|
|
||||||
<%= devise_error_messages! %>
|
|
||||||
|
|
||||||
<div><%= f.label :email %><br />
|
|
||||||
<%= f.email_field :email %></div>
|
|
||||||
|
|
||||||
<div><%= f.submit "Resend unlock instructions" %></div>
|
|
||||||
<% end %>
|
|
||||||
|
|
||||||
<%= render partial: "devise/shared/links" %>
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
.login-box
|
||||||
|
.login-heading
|
||||||
|
%h3 Resend unlock email
|
||||||
|
.login-body
|
||||||
|
= form_for(resource, as: resource_name, url: unlock_path(resource_name), html: { method: :post }) do |f|
|
||||||
|
.devise-errors
|
||||||
|
= devise_error_messages!
|
||||||
|
.clearfix.append-bottom-20
|
||||||
|
= f.email_field :email, class: 'form-control', placeholder: 'Email', autofocus: 'autofocus', autocapitalize: 'off', autocorrect: 'off'
|
||||||
|
.clearfix
|
||||||
|
= f.submit 'Resend unlock instructions', class: 'btn btn-success'
|
||||||
|
|
||||||
|
.clearfix.prepend-top-20
|
||||||
|
= render 'devise/shared/sign_in_link'
|
|
@ -6,7 +6,7 @@
|
||||||
- else
|
- else
|
||||||
= render 'explore/head'
|
= render 'explore/head'
|
||||||
|
|
||||||
.gray-content-block.clearfix
|
.gray-content-block.clearfix.second-block
|
||||||
= render 'filter'
|
= render 'filter'
|
||||||
= render 'projects', projects: @projects
|
= render 'projects', projects: @projects
|
||||||
= paginate @projects, theme: "gitlab"
|
= paginate @projects, theme: "gitlab"
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
= render 'explore/head'
|
= render 'explore/head'
|
||||||
|
|
||||||
.explore-trending-block
|
.explore-trending-block
|
||||||
.gray-content-block
|
.gray-content-block.second-block
|
||||||
.pull-right
|
.pull-right
|
||||||
= render 'explore/projects/dropdown'
|
= render 'explore/projects/dropdown'
|
||||||
.oneline
|
.oneline
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
= render 'explore/head'
|
= render 'explore/head'
|
||||||
|
|
||||||
.explore-trending-block
|
.explore-trending-block
|
||||||
.gray-content-block
|
.gray-content-block.second-block
|
||||||
.pull-right
|
.pull-right
|
||||||
= render 'explore/projects/dropdown'
|
= render 'explore/projects/dropdown'
|
||||||
.oneline
|
.oneline
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
.panel.panel-default.projects-list-holder
|
.projects-list-holder
|
||||||
.panel-heading.clearfix
|
.projects-search-form
|
||||||
.input-group
|
.input-group
|
||||||
= search_field_tag :filter_projects, nil, placeholder: 'Filter by name', class: 'projects-list-filter form-control', spellcheck: false
|
= search_field_tag :filter_projects, nil, placeholder: 'Filter by name', class: 'projects-list-filter form-control', spellcheck: false
|
||||||
- if can? current_user, :create_projects, @group
|
- if can? current_user, :create_projects, @group
|
||||||
|
|
|
@ -5,37 +5,47 @@
|
||||||
- if current_user
|
- if current_user
|
||||||
= auto_discovery_link_tag(:atom, group_url(@group, format: :atom, private_token: current_user.private_token), title: "#{@group.name} activity")
|
= auto_discovery_link_tag(:atom, group_url(@group, format: :atom, private_token: current_user.private_token), title: "#{@group.name} activity")
|
||||||
|
|
||||||
.dashboard
|
.cover-block
|
||||||
.header-with-avatar.clearfix
|
.avatar-holder
|
||||||
= image_tag group_icon(@group), class: "avatar group-avatar s90"
|
= link_to group_icon(@group), target: '_blank' do
|
||||||
%h3
|
= image_tag group_icon(@group), class: "avatar group-avatar s90"
|
||||||
= @group.name
|
.cover-title
|
||||||
.username
|
= @group.name
|
||||||
@#{@group.path}
|
|
||||||
- if @group.description.present?
|
|
||||||
.description
|
|
||||||
= markdown(@group.description, pipeline: :description)
|
|
||||||
%hr
|
|
||||||
|
|
||||||
= render 'shared/show_aside'
|
.cover-desc.username
|
||||||
|
@#{@group.path}
|
||||||
|
|
||||||
- if can?(current_user, :read_group, @group)
|
- if @group.description.present?
|
||||||
.row
|
.cover-desc.description
|
||||||
%section.activities.col-md-7
|
= markdown(@group.description, pipeline: :description)
|
||||||
.hidden-xs
|
|
||||||
- if current_user
|
|
||||||
= render "events/event_last_push", event: @last_push
|
|
||||||
.pull-right
|
|
||||||
= link_to group_path(@group, { format: :atom, private_token: current_user.private_token }), title: "Feed", class: 'btn rss-btn' do
|
|
||||||
%i.fa.fa-rss
|
|
||||||
|
|
||||||
= render 'shared/event_filter'
|
- if can?(current_user, :read_group, @group)
|
||||||
%hr
|
%ul.center-top-menu.no-top
|
||||||
|
%li.active
|
||||||
|
= link_to "#activity", 'data-toggle' => 'tab' do
|
||||||
|
Activity
|
||||||
|
- if @projects.present?
|
||||||
|
%li
|
||||||
|
= link_to "#projects", 'data-toggle' => 'tab' do
|
||||||
|
Projects
|
||||||
|
|
||||||
.content_list
|
.tab-content
|
||||||
= spinner
|
.tab-pane.active#activity
|
||||||
%aside.side.col-md-5
|
.gray-content-block.activity-filter-block
|
||||||
= render "projects", projects: @projects
|
- if current_user
|
||||||
- else
|
= render "events/event_last_push", event: @last_push
|
||||||
%p
|
.pull-right
|
||||||
This group does not have public projects
|
= link_to group_path(@group, { format: :atom, private_token: current_user.private_token }), title: "Feed", class: 'btn rss-btn' do
|
||||||
|
%i.fa.fa-rss
|
||||||
|
|
||||||
|
= render 'shared/event_filter'
|
||||||
|
|
||||||
|
.content_list
|
||||||
|
= spinner
|
||||||
|
|
||||||
|
.tab-pane#projects
|
||||||
|
= render "projects", projects: @projects
|
||||||
|
|
||||||
|
- else
|
||||||
|
%p
|
||||||
|
This group does not have public projects
|
||||||
|
|
|
@ -12,6 +12,6 @@
|
||||||
comment = val.match(/^\S+ \S+ (.+)\n?$/);
|
comment = val.match(/^\S+ \S+ (.+)\n?$/);
|
||||||
|
|
||||||
if( comment && comment.length > 1 && title.val() == '' ){
|
if( comment && comment.length > 1 && title.val() == '' ){
|
||||||
$('#key_title').val( comment[1] );
|
$('#key_title').val( comment[1] ).change();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -38,3 +38,4 @@
|
||||||
= text_field_tag :pin_code, nil, class: "form-control", required: true, autofocus: true
|
= text_field_tag :pin_code, nil, class: "form-control", required: true, autofocus: true
|
||||||
.form-actions
|
.form-actions
|
||||||
= submit_tag 'Submit', class: 'btn btn-success'
|
= submit_tag 'Submit', class: 'btn btn-success'
|
||||||
|
= link_to 'Configure it later', skip_profile_two_factor_auth_path, :method => :patch, class: 'btn btn-cancel' if two_factor_skippable?
|
||||||
|
|
|
@ -2,3 +2,7 @@
|
||||||
= button_tag 'Commit Changes', class: 'btn commit-btn js-commit-button btn-create'
|
= button_tag 'Commit Changes', class: 'btn commit-btn js-commit-button btn-create'
|
||||||
= link_to 'Cancel', cancel_path,
|
= link_to 'Cancel', cancel_path,
|
||||||
class: 'btn btn-cancel', data: {confirm: leave_edit_message}
|
class: 'btn btn-cancel', data: {confirm: leave_edit_message}
|
||||||
|
|
||||||
|
- unless can?(current_user, :push_code, @project)
|
||||||
|
.inline.prepend-left-10
|
||||||
|
= commit_in_fork_help
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
= icon('rss')
|
= icon('rss')
|
||||||
|
|
||||||
.project-repo-buttons
|
.project-repo-buttons
|
||||||
.split-one
|
.split-one.count-buttons
|
||||||
= render 'projects/buttons/star'
|
= render 'projects/buttons/star'
|
||||||
= render 'projects/buttons/fork'
|
= render 'projects/buttons/fork'
|
||||||
|
|
||||||
|
@ -38,3 +38,6 @@
|
||||||
= render 'projects/buttons/dropdown'
|
= render 'projects/buttons/dropdown'
|
||||||
|
|
||||||
= render 'projects/buttons/notifications'
|
= render 'projects/buttons/notifications'
|
||||||
|
|
||||||
|
:coffeescript
|
||||||
|
new Star()
|
|
@ -2,7 +2,7 @@
|
||||||
= link_to 'Raw', namespace_project_raw_path(@project.namespace, @project, @id),
|
= link_to 'Raw', namespace_project_raw_path(@project.namespace, @project, @id),
|
||||||
class: 'btn btn-sm', target: '_blank'
|
class: 'btn btn-sm', target: '_blank'
|
||||||
-# only show normal/blame view links for text files
|
-# only show normal/blame view links for text files
|
||||||
- if blob_viewable?(@blob)
|
- if blob_text_viewable?(@blob)
|
||||||
- if current_page? namespace_project_blame_path(@project.namespace, @project, @id)
|
- if current_page? namespace_project_blame_path(@project.namespace, @project, @id)
|
||||||
= link_to 'Normal View', namespace_project_blob_path(@project.namespace, @project, @id),
|
= link_to 'Normal View', namespace_project_blob_path(@project.namespace, @project, @id),
|
||||||
class: 'btn btn-sm'
|
class: 'btn btn-sm'
|
||||||
|
@ -14,13 +14,8 @@
|
||||||
= link_to 'Permalink', namespace_project_blob_path(@project.namespace, @project,
|
= link_to 'Permalink', namespace_project_blob_path(@project.namespace, @project,
|
||||||
tree_join(@commit.sha, @path)), class: 'btn btn-sm'
|
tree_join(@commit.sha, @path)), class: 'btn btn-sm'
|
||||||
|
|
||||||
- if blob_editable?(@blob)
|
- if current_user
|
||||||
.btn-group{ role: "group" }
|
.btn-group{ role: "group" }
|
||||||
= edit_blob_link(@project, @ref, @path)
|
= edit_blob_link
|
||||||
%button.btn.btn-default{ 'data-target' => '#modal-upload-blob', 'data-toggle' => 'modal' } Replace
|
= replace_blob_link
|
||||||
%button.btn.btn-remove{ 'data-target' => '#modal-remove-blob', 'data-toggle' => 'modal' } Delete
|
= delete_blob_link
|
||||||
- elsif !on_top_of_branch?
|
|
||||||
.btn-group{ role: "group" }
|
|
||||||
%button.btn.btn-default.disabled.has_tooltip{title: "You can only edit files when you are on a branch.", data: {container: 'body'}} Edit
|
|
||||||
%button.btn.btn-default.disabled.has_tooltip{title: "You can only replace files when you are on a branch.", data: {container: 'body'}} Replace
|
|
||||||
%button.btn.btn-remove.disabled.has_tooltip{title: "You can only delete files when you are on a branch.", data: {container: 'body'}} Delete
|
|
||||||
|
|
|
@ -17,5 +17,9 @@
|
||||||
= submit_tag "Create directory", class: 'btn btn-create'
|
= submit_tag "Create directory", class: 'btn btn-create'
|
||||||
= link_to "Cancel", '#', class: "btn btn-cancel", "data-dismiss" => "modal"
|
= link_to "Cancel", '#', class: "btn btn-cancel", "data-dismiss" => "modal"
|
||||||
|
|
||||||
|
- unless can?(current_user, :push_code, @project)
|
||||||
|
.inline.prepend-left-10
|
||||||
|
= commit_in_fork_help
|
||||||
|
|
||||||
:javascript
|
:javascript
|
||||||
new NewCommitForm($('.js-create-dir-form'))
|
new NewCommitForm($('.js-create-dir-form'))
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue