summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG15
-rw-r--r--app/assets/javascripts/awards_handler.coffee48
-rw-r--r--app/assets/javascripts/blob/blob_file_dropzone.js.coffee2
-rw-r--r--app/assets/javascripts/merge_request_widget.js.coffee2
-rw-r--r--app/assets/javascripts/new_branch_form.js.coffee78
-rw-r--r--app/assets/javascripts/new_commit_form.js.coffee2
-rw-r--r--app/assets/javascripts/project.js.coffee5
-rw-r--r--app/assets/javascripts/star.js.coffee22
-rw-r--r--app/assets/stylesheets/pages/awards.scss4
-rw-r--r--app/assets/stylesheets/pages/emojis.scss2
-rw-r--r--app/assets/stylesheets/pages/projects.scss77
-rw-r--r--app/controllers/concerns/creates_commit.rb103
-rw-r--r--app/controllers/concerns/creates_merge_request_for_commit.rb28
-rw-r--r--app/controllers/projects/blob_controller.rb94
-rw-r--r--app/controllers/projects/forks_controller.rb28
-rw-r--r--app/controllers/projects/imports_controller.rb29
-rw-r--r--app/controllers/projects/tree_controller.rb38
-rw-r--r--app/controllers/projects_controller.rb2
-rw-r--r--app/helpers/blob_helper.rb102
-rw-r--r--app/helpers/tree_helper.rb41
-rw-r--r--app/helpers/visibility_level_helper.rb3
-rw-r--r--app/models/ability.rb16
-rw-r--r--app/models/global_milestone.rb2
-rw-r--r--app/models/identity.rb1
-rw-r--r--app/models/project.rb27
-rw-r--r--app/models/repository.rb59
-rw-r--r--app/services/base_service.rb5
-rw-r--r--app/services/create_branch_service.rb19
-rw-r--r--app/services/files/base_service.rb26
-rw-r--r--app/services/files/create_service.rb2
-rw-r--r--app/services/projects/update_service.rb28
-rw-r--r--app/views/explore/projects/index.html.haml2
-rw-r--r--app/views/explore/projects/starred.html.haml2
-rw-r--r--app/views/explore/projects/trending.html.haml2
-rw-r--r--app/views/projects/_commit_button.html.haml4
-rw-r--r--app/views/projects/_home_panel.html.haml5
-rw-r--r--app/views/projects/blob/_actions.html.haml15
-rw-r--r--app/views/projects/blob/_new_dir.html.haml4
-rw-r--r--app/views/projects/blob/_upload.html.haml5
-rw-r--r--app/views/projects/blob/edit.html.haml2
-rw-r--r--app/views/projects/blob/show.html.haml2
-rw-r--r--app/views/projects/branches/new.html.haml10
-rw-r--r--app/views/projects/buttons/_dropdown.html.haml20
-rw-r--r--app/views/projects/buttons/_fork.html.haml5
-rw-r--r--app/views/projects/buttons/_star.html.haml22
-rw-r--r--app/views/projects/diffs/_file.html.haml7
-rw-r--r--app/views/projects/forks/new.html.haml1
-rw-r--r--app/views/projects/merge_requests/_merge_request.html.haml2
-rw-r--r--app/views/projects/tree/_tree_content.html.haml2
-rw-r--r--app/views/projects/tree/_tree_header.html.haml89
-rw-r--r--app/views/shared/_clone_panel.html.haml29
-rw-r--r--app/views/shared/_new_commit_form.html.haml28
-rw-r--r--app/views/votes/_votes_block.html.haml5
-rw-r--r--features/project/commits/branches.feature1
-rw-r--r--features/project/create.feature1
-rw-r--r--features/project/issues/award_emoji.feature10
-rw-r--r--features/project/merge_requests/accept.feature8
-rw-r--r--features/project/source/browse_files.feature106
-rw-r--r--features/project/star.feature1
-rw-r--r--features/steps/project/commits/branches.rb3
-rw-r--r--features/steps/project/create.rb6
-rw-r--r--features/steps/project/issues/award_emoji.rb12
-rw-r--r--features/steps/project/merge_requests/acceptance.rb4
-rw-r--r--features/steps/project/source/browse_files.rb35
-rw-r--r--features/steps/project/star.rb2
-rw-r--r--lib/api/files.rb2
-rw-r--r--lib/gitlab/ldap/user.rb6
-rw-r--r--lib/gitlab/o_auth/user.rb2
-rw-r--r--lib/gitlab/visibility_level.rb9
-rw-r--r--spec/controllers/projects/tree_controller_spec.rb8
-rw-r--r--spec/javascripts/fixtures/new_branch.html.haml4
-rw-r--r--spec/javascripts/new_branch_spec.js.coffee160
-rw-r--r--spec/lib/gitlab/ldap/user_spec.rb15
-rw-r--r--spec/models/global_milestone_spec.rb10
-rw-r--r--spec/models/project_spec.rb24
-rw-r--r--spec/requests/api/branches_spec.rb2
-rw-r--r--spec/services/projects/update_service_spec.rb39
77 files changed, 1261 insertions, 382 deletions
diff --git a/CHANGELOG b/CHANGELOG
index 30cd589fddd..a5acf4a502b 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,16 +1,24 @@
Please view this file on the master branch, on stable branches it's out of date.
v 8.4.0 (unreleased)
- - Fix Error 500 when doing a search in dashboard before visiting any project (Stan Hu)
- Implement new UI for group page
+ - Implement search inside emoji picker
- Add project permissions to all project API endpoints (Stan Hu)
- Link to milestone in "Milestone changed" system note
+ - 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.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
v 8.3.0
- - Add CAS support (tduehr)
- 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)
- - Add link to merge request on build detail page.
- Add open_issues_count to project API (Stan Hu)
- Expand character set of usernames created by Omniauth (Corey Hinshaw)
- Add button to automatically merge a merge request when the build succeeds (Zeger-Jan van de Weg)
@@ -70,7 +78,6 @@ v 8.3.0
- Do not show build status unless builds are enabled and `.gitlab-ci.yml` is present
- Persist runners registration token in database
- Fix online editor should not remove newlines at the end of the file
- - Expose Git's version in the admin area
v 8.2.3
- Fix application settings cache not expiring after changes (Stan Hu)
diff --git a/app/assets/javascripts/awards_handler.coffee b/app/assets/javascripts/awards_handler.coffee
index b5432773713..04bf5cc7bb5 100644
--- a/app/assets/javascripts/awards_handler.coffee
+++ b/app/assets/javascripts/awards_handler.coffee
@@ -10,6 +10,9 @@ class @AwardsHandler
if $(".emoji-menu").is(":visible")
$(".emoji-menu").hide()
+ @renderFrequentlyUsedBlock()
+ @setupSearch()
+
addAward: (emoji) ->
emoji = @normilizeEmojiName(emoji)
@postEmoji emoji, =>
@@ -18,6 +21,8 @@ class @AwardsHandler
$(".emoji-menu").hide()
addAwardToEmojiBar: (emoji) ->
+ @addEmojiToFrequentlyUsedList(emoji)
+
emoji = @normilizeEmojiName(emoji)
if @exist(emoji)
if @isActive(emoji)
@@ -114,3 +119,46 @@ class @AwardsHandler
normilizeEmojiName: (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()
diff --git a/app/assets/javascripts/blob/blob_file_dropzone.js.coffee b/app/assets/javascripts/blob/blob_file_dropzone.js.coffee
index 195f8b11e5d..9df932817f6 100644
--- a/app/assets/javascripts/blob/blob_file_dropzone.js.coffee
+++ b/app/assets/javascripts/blob/blob_file_dropzone.js.coffee
@@ -35,7 +35,7 @@ class @BlobFileDropzone
return
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('commit_message', form.find('.js-commit-message').val())
return
diff --git a/app/assets/javascripts/merge_request_widget.js.coffee b/app/assets/javascripts/merge_request_widget.js.coffee
index c4b63966fe7..738ffc8343b 100644
--- a/app/assets/javascripts/merge_request_widget.js.coffee
+++ b/app/assets/javascripts/merge_request_widget.js.coffee
@@ -18,7 +18,7 @@ class @MergeRequestWidget
if data.state == "merged"
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
$('.mr-widget-body').html("<h4>" + data.merge_error + "</h4>")
else
diff --git a/app/assets/javascripts/new_branch_form.js.coffee b/app/assets/javascripts/new_branch_form.js.coffee
new file mode 100644
index 00000000000..4b350854f78
--- /dev/null
+++ b/app/assets/javascripts/new_branch_form.js.coffee
@@ -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)
diff --git a/app/assets/javascripts/new_commit_form.js.coffee b/app/assets/javascripts/new_commit_form.js.coffee
index 3c7b776155f..03f0f51acfa 100644
--- a/app/assets/javascripts/new_commit_form.js.coffee
+++ b/app/assets/javascripts/new_commit_form.js.coffee
@@ -1,6 +1,6 @@
class @NewCommitForm
constructor: (form) ->
- @newBranch = form.find('.js-new-branch')
+ @newBranch = form.find('.js-target-branch')
@originalBranch = form.find('.js-original-branch')
@createMergeRequest = form.find('.js-create-merge-request')
@createMergeRequestContainer = form.find('.js-create-merge-request-container')
diff --git a/app/assets/javascripts/project.js.coffee b/app/assets/javascripts/project.js.coffee
index 1f221945c06..d7a658f8faa 100644
--- a/app/assets/javascripts/project.js.coffee
+++ b/app/assets/javascripts/project.js.coffee
@@ -1,7 +1,7 @@
class @Project
constructor: ->
# Git protocol switcher
- $('.js-protocol-switch').click ->
+ $('ul.clone-options-dropdown a').click ->
return if $(@).hasClass('active')
@@ -10,7 +10,8 @@ class @Project
# Add the active class for the clicked button
$(@).toggleClass('active')
- url = $(@).data('clone')
+ url = $("#project_clone").val()
+ console.log("url",url)
# Update the input field
$('#project_clone').val(url)
diff --git a/app/assets/javascripts/star.js.coffee b/app/assets/javascripts/star.js.coffee
new file mode 100644
index 00000000000..d849b2e7950
--- /dev/null
+++ b/app/assets/javascripts/star.js.coffee
@@ -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 \ No newline at end of file
diff --git a/app/assets/stylesheets/pages/awards.scss b/app/assets/stylesheets/pages/awards.scss
index aa1a06b2fcc..87dd30f4111 100644
--- a/app/assets/stylesheets/pages/awards.scss
+++ b/app/assets/stylesheets/pages/awards.scss
@@ -101,6 +101,10 @@
overflow: auto;
}
+ input.emoji-search{
+ background: image-url("icon-search.png") 240px no-repeat;
+ }
+
li {
cursor: pointer;
width: 30px;
diff --git a/app/assets/stylesheets/pages/emojis.scss b/app/assets/stylesheets/pages/emojis.scss
index 819ec9a2f5f..89a94c5a780 100644
--- a/app/assets/stylesheets/pages/emojis.scss
+++ b/app/assets/stylesheets/pages/emojis.scss
@@ -4,7 +4,7 @@ The source: gemojione gem.
*/
.emoji-icon{
- background-image: url(emoji.png);
+ background-image: image-url("emoji.png");
background-repeat: no-repeat;
}
diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss
index 99006b9f5d1..cff3edb7ed2 100644
--- a/app/assets/stylesheets/pages/projects.scss
+++ b/app/assets/stylesheets/pages/projects.scss
@@ -91,21 +91,83 @@
}
}
- .input-group {
+ .git-clone-holder {
display: inline-table;
position: relative;
- top: 17px;
}
.project-repo-buttons {
margin-top: 12px;
margin-bottom: 0px;
+ .count-buttons {
+ display: block;
+ margin-bottom: 12px;
+ }
+
.btn {
@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 {
+ @include btn-gray;
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;
}
+ .clone-options {
+ display: table-cell;
+ a.btn {
+ width: 100%;
+ }
+ }
+
.form-control {
cursor: auto;
@extend .monospace;
@@ -337,6 +406,8 @@ 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;
diff --git a/app/controllers/concerns/creates_commit.rb b/app/controllers/concerns/creates_commit.rb
new file mode 100644
index 00000000000..62127a09081
--- /dev/null
+++ b/app/controllers/concerns/creates_commit.rb
@@ -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
diff --git a/app/controllers/concerns/creates_merge_request_for_commit.rb b/app/controllers/concerns/creates_merge_request_for_commit.rb
deleted file mode 100644
index c7527822158..00000000000
--- a/app/controllers/concerns/creates_merge_request_for_commit.rb
+++ /dev/null
@@ -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
diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb
index 62163682936..c56a3497bb2 100644
--- a/app/controllers/projects/blob_controller.rb
+++ b/app/controllers/projects/blob_controller.rb
@@ -1,7 +1,7 @@
# Controller for viewing a file's blame
class Projects::BlobController < Projects::ApplicationController
include ExtractsPath
- include CreatesMergeRequestForCommit
+ include CreatesCommit
include ActionView::Helpers::SanitizeHelper
# 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 :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 :commit, except: [:new, :create]
before_action :blob, except: [:new, :create]
before_action :from_merge_request, only: [:edit, :update]
before_action :require_branch_head, only: [:edit, :update]
before_action :editor_variables, except: [:show, :preview, :diff]
- before_action :after_edit_path, only: [:edit, :update]
def new
commit unless @repository.empty?
end
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_path: namespace_project_new_blob_path(@project.namespace, @project, @ref))
end
@@ -36,6 +36,14 @@ class Projects::BlobController < Projects::ApplicationController
end
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,
failure_view: :edit,
failure_path: namespace_project_blob_path(@project.namespace, @project, @id))
@@ -50,15 +58,10 @@ class Projects::BlobController < Projects::ApplicationController
end
def destroy
- result = Files::DeleteService.new(@project, current_user, @commit_params).execute
-
- if result[:status] == :success
- flash[:notice] = "Your changes have been successfully committed"
- redirect_to after_destroy_path
- else
- flash[:alert] = result[:message]
- render :show
- end
+ create_commit(Files::DeleteService, success_notice: "The file has been successfully deleted.",
+ success_path: namespace_project_tree_path(@project.namespace, @project, @target_branch),
+ failure_view: :show,
+ failure_path: namespace_project_blob_path(@project.namespace, @project, @id))
end
def diff
@@ -108,74 +111,13 @@ class Projects::BlobController < Projects::ApplicationController
render_404
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
# If blob edit was initiated from merge request page
@from_merge_request ||= MergeRequest.find_by(id: params[:from_merge_request_id])
end
- def sanitized_new_branch_name
- sanitize(strip_tags(params[:new_branch]))
- end
-
def editor_variables
- @current_branch = @ref
-
- @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
+ @target_branch = params[:target_branch]
@file_path =
if action_name.to_s == 'create'
@@ -194,8 +136,6 @@ class Projects::BlobController < Projects::ApplicationController
@commit_params = {
file_path: @file_path,
- current_branch: @current_branch,
- target_branch: @new_branch,
commit_message: params[:commit_message],
file_content: params[:content],
file_content_encoding: params[:encoding]
diff --git a/app/controllers/projects/forks_controller.rb b/app/controllers/projects/forks_controller.rb
index 8a785076bb7..750181f0c19 100644
--- a/app/controllers/projects/forks_controller.rb
+++ b/app/controllers/projects/forks_controller.rb
@@ -10,19 +10,35 @@ class Projects::ForksController < Projects::ApplicationController
def create
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.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
- redirect_to(
- namespace_project_path(@forked_project.namespace, @forked_project),
- notice: 'Project was successfully forked.'
- )
+ if continue_params
+ redirect_to continue_params[:to], notice: continue_params[:notice]
+ else
+ redirect_to namespace_project_path(@forked_project.namespace, @forked_project), notice: "The project was successfully forked."
+ end
end
else
render :error
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
diff --git a/app/controllers/projects/imports_controller.rb b/app/controllers/projects/imports_controller.rb
index fb8788f0818..8d8035ef5ff 100644
--- a/app/controllers/projects/imports_controller.rb
+++ b/app/controllers/projects/imports_controller.rb
@@ -1,7 +1,7 @@
class Projects::ImportsController < Projects::ApplicationController
# Authorize
before_action :authorize_admin_project!
- before_action :require_no_repo
+ before_action :require_no_repo, except: :show
before_action :redirect_if_progress, except: :show
def new
@@ -24,21 +24,36 @@ class Projects::ImportsController < Projects::ApplicationController
end
def show
- unless @project.import_in_progress?
- if @project.import_finished?
- redirect_to(project_path(@project)) and return
+ if @project.repository_exists? || @project.import_finished?
+ if continue_params
+ redirect_to continue_params[:to], notice: continue_params[:notice]
else
- redirect_to(new_namespace_project_import_path(@project.namespace,
- @project)) and return
+ redirect_to project_path(@project), notice: "The project was successfully forked."
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
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
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
diff --git a/app/controllers/projects/tree_controller.rb b/app/controllers/projects/tree_controller.rb
index 8f272ad1281..cb3ed0f6f9c 100644
--- a/app/controllers/projects/tree_controller.rb
+++ b/app/controllers/projects/tree_controller.rb
@@ -1,14 +1,14 @@
# Controller for viewing a repository's file structure
class Projects::TreeController < Projects::ApplicationController
include ExtractsPath
- include CreatesMergeRequestForCommit
+ include CreatesCommit
include ActionView::Helpers::SanitizeHelper
before_action :require_non_empty_project, except: [:new, :create]
before_action :assign_ref_vars
before_action :assign_dir_vars, only: [:create_dir]
before_action :authorize_download_code!
- before_action :authorize_push_code!, only: [:create_dir]
+ before_action :authorize_edit_tree!, only: [:create_dir]
def show
return render_404 unless @repository.commit(@ref)
@@ -34,44 +34,20 @@ class Projects::TreeController < Projects::ApplicationController
def create_dir
return render_404 unless @commit_params.values.all?
- begin
- result = Files::CreateDirService.new(@project, current_user, @commit_params).execute
- message = result[:message]
- 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
+ create_commit(Files::CreateDirService, success_notice: "The directory has been successfully created.",
+ success_path: namespace_project_tree_path(@project.namespace, @project, File.join(@target_branch, @dir_name)),
+ failure_path: namespace_project_tree_path(@project.namespace, @project, @ref))
end
private
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])
@commit_params = {
file_path: @dir_name,
- current_branch: @ref,
- target_branch: @new_branch,
commit_message: params[:commit_message],
}
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
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index bf5e25ff895..2dab04f2a7c 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -171,7 +171,7 @@ class ProjectsController < ApplicationController
@project.reload
render json: {
- html: view_to_html_string("projects/buttons/_star")
+ star_count: @project.star_count
}
end
diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb
index 68e5d5be600..d31d4cde08f 100644
--- a/app/helpers/blob_helper.rb
+++ b/app/helpers/blob_helper.rb
@@ -22,32 +22,90 @@ module BlobHelper
%w(credits changelog news copying copyright license authors)
end
- def edit_blob_link(project, ref, path, options = {})
- blob =
- begin
- project.repository.blob_at(ref, path)
- rescue
- nil
- end
-
- return unless blob && blob.text? && blob_editable?(blob)
-
- text = 'Edit'
- after = options[:after] || ''
+ def edit_blob_link(project = @project, ref = @ref, path = @path, options = {})
+ return unless current_user
+
+ blob = project.repository.blob_at(ref, path) rescue nil
+
+ return unless blob && blob_text_viewable?(blob)
+
from_mr = options[:from_merge_request_id]
link_opts = {}
link_opts[:from_merge_request_id] = from_mr if from_mr
- cls = 'btn btn-small'
- link_to(text,
- namespace_project_edit_blob_path(project.namespace, project,
- tree_join(ref, path),
- link_opts),
- class: cls
- ) + after.html_safe
+
+ edit_path = namespace_project_edit_blob_path(project.namespace, project,
+ tree_join(ref, path),
+ link_opts)
+
+ if !on_top_of_branch?
+ 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
+
+ def modify_file_link(project = @project, ref = @ref, path = @path, label:, action:, btn_class:, modal_type:)
+ 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 blob_editable?(blob, project = @project, ref = @ref)
- !blob.lfs_pointer? && allowed_tree_edit?(project, ref)
+ def can_edit_blob?(blob, project = @project, ref = @ref)
+ !blob.lfs_pointer? && can_edit_tree?(project, ref)
end
def leave_edit_message
@@ -70,7 +128,7 @@ module BlobHelper
icon("#{file_type_icon_class('file', mode, name)} fw")
end
- def blob_viewable?(blob)
+ def blob_text_viewable?(blob)
blob && blob.text? && !blob.lfs_pointer?
end
diff --git a/app/helpers/tree_helper.rb b/app/helpers/tree_helper.rb
index f448dd0ab61..2ad7c80dae0 100644
--- a/app/helpers/tree_helper.rb
+++ b/app/helpers/tree_helper.rb
@@ -50,24 +50,49 @@ module TreeHelper
project.repository.branch_names.include?(ref)
end
- def allowed_tree_edit?(project = nil, ref = nil)
+ def can_edit_tree?(project = nil, ref = nil)
project ||= @project
ref ||= @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
def tree_edit_branch(project = @project, ref = @ref)
- if allowed_tree_edit?(project, ref)
- if can_push_branch?(project, ref)
- ref
- else
- project.repository.next_patch_branch
- end
+ return unless can_edit_tree?(project, ref)
+
+ if can_push_branch?(project, ref)
+ ref
+ else
+ project = tree_edit_project(project)
+ project.repository.next_patch_branch
+ 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)
if @path.present?
part_path = ""
diff --git a/app/helpers/visibility_level_helper.rb b/app/helpers/visibility_level_helper.rb
index 2e69ce923a2..71d33b445c2 100644
--- a/app/helpers/visibility_level_helper.rb
+++ b/app/helpers/visibility_level_helper.rb
@@ -69,7 +69,6 @@ module VisibilityLevelHelper
def skip_level?(form_model, level)
form_model.is_a?(Project) &&
- form_model.forked? &&
- !Gitlab::VisibilityLevel.allowed_fork_levels(form_model.forked_from_project.visibility_level).include?(level)
+ !form_model.visibility_level_allowed?(level)
end
end
diff --git a/app/models/ability.rb b/app/models/ability.rb
index cd5ae0fb0fd..1b3ee757040 100644
--- a/app/models/ability.rb
+++ b/app/models/ability.rb
@@ -132,14 +132,14 @@ class Ability
end
def public_project_rules
- project_guest_rules + [
+ @public_project_rules ||= project_guest_rules + [
:download_code,
:fork_project
]
end
def project_guest_rules
- [
+ @project_guest_rules ||= [
:read_project,
:read_wiki,
:read_issue,
@@ -157,7 +157,7 @@ class Ability
end
def project_report_rules
- project_guest_rules + [
+ @project_report_rules ||= project_guest_rules + [
:create_commit_status,
:read_commit_statuses,
:download_code,
@@ -170,7 +170,7 @@ class Ability
end
def project_dev_rules
- project_report_rules + [
+ @project_dev_rules ||= project_report_rules + [
:admin_merge_request,
:create_merge_request,
:create_wiki,
@@ -181,7 +181,7 @@ class Ability
end
def project_archived_rules
- [
+ @project_archived_rules ||= [
:create_merge_request,
:push_code,
:push_code_to_protected_branches,
@@ -191,7 +191,7 @@ class Ability
end
def project_master_rules
- project_dev_rules + [
+ @project_master_rules ||= project_dev_rules + [
:push_code_to_protected_branches,
:update_project_snippet,
:update_merge_request,
@@ -206,7 +206,7 @@ class Ability
end
def project_admin_rules
- project_master_rules + [
+ @project_admin_rules ||= project_master_rules + [
:change_namespace,
:change_visibility_level,
:rename_project,
@@ -332,7 +332,7 @@ class Ability
end
if snippet.public? || snippet.internal?
- rules << :read_personal_snippet
+ rules << :read_personal_snippet
end
rules
diff --git a/app/models/global_milestone.rb b/app/models/global_milestone.rb
index 8bfc79d88f8..af1d7562ebe 100644
--- a/app/models/global_milestone.rb
+++ b/app/models/global_milestone.rb
@@ -16,7 +16,7 @@ class GlobalMilestone
end
def safe_title
- @title.to_slug.to_s
+ @title.to_slug.normalize.to_s
end
def expired?
diff --git a/app/models/identity.rb b/app/models/identity.rb
index ad60154be71..8bcdc194953 100644
--- a/app/models/identity.rb
+++ b/app/models/identity.rb
@@ -12,6 +12,7 @@
class Identity < ActiveRecord::Base
include Sortable
+ include CaseSensitivity
belongs_to :user
validates :provider, presence: true
diff --git a/app/models/project.rb b/app/models/project.rb
index b28a7ca429c..75f85310d5f 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -64,6 +64,19 @@ class Project < ActiveRecord::Base
update_column(:last_activity_at, self.created_at)
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
acts_as_taggable_on :tags
@@ -100,9 +113,12 @@ class Project < ActiveRecord::Base
has_one :gitlab_issue_tracker_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
has_many :merge_requests, dependent: :destroy, foreign_key: 'target_project_id'
# Merge requests from source project should be kept when source project was removed
@@ -768,7 +784,7 @@ class Project < ActiveRecord::Base
end
def forks_count
- ForkedProjectLink.where(forked_from_project_id: self.id).count
+ forks.count
end
def find_label(name)
@@ -862,4 +878,9 @@ class Project < ActiveRecord::Base
def open_issues_count
issues.opened.count
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
diff --git a/app/models/repository.rb b/app/models/repository.rb
index 2c25f4ce451..9f688e3b45b 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -592,47 +592,54 @@ class Repository
Gitlab::Popen.popen(args, path_to_repo)
end
- def commit_with_hooks(current_user, branch)
- oldrev = Gitlab::Git::BLANK_SHA
- ref = Gitlab::Git::BRANCH_REF_PREFIX + branch
- was_empty = empty?
-
- # Create temporary ref
+ def with_tmp_ref(oldrev = nil)
random_string = SecureRandom.hex
tmp_ref = "refs/tmp/#{random_string}/head"
- unless was_empty
- oldrev = find_branch(branch).target
+ if oldrev && !Gitlab::Git.blank_ref?(oldrev)
rugged.references.create(tmp_ref, oldrev)
end
# Make commit in tmp ref
- newrev = yield(tmp_ref)
+ yield(tmp_ref)
+ ensure
+ rugged.references.delete(tmp_ref) rescue nil
+ end
+
+ def commit_with_hooks(current_user, branch)
+ oldrev = Gitlab::Git::BLANK_SHA
+ ref = Gitlab::Git::BRANCH_REF_PREFIX + branch
+ was_empty = empty?
- unless newrev
- raise CommitError.new('Failed to create commit')
+ unless was_empty
+ oldrev = find_branch(branch).target
end
- GitHooksService.new.execute(current_user, path_to_repo, oldrev, newrev, ref) do
- if was_empty
- # Create branch
- rugged.references.create(ref, newrev)
- else
- # Update head
- current_head = find_branch(branch).target
+ with_tmp_ref(oldrev) do |tmp_ref|
+ # Make commit in tmp ref
+ newrev = yield(tmp_ref)
+
+ unless newrev
+ raise CommitError.new('Failed to create commit')
+ end
- # Make sure target branch was not changed during pre-receive hook
- if current_head == oldrev
- rugged.references.update(ref, newrev)
+ GitHooksService.new.execute(current_user, path_to_repo, oldrev, newrev, ref) do
+ if was_empty
+ # Create branch
+ rugged.references.create(ref, newrev)
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
- rescue GitHooksService::PreReceiveError
- # Remove tmp ref and return error to user
- rugged.references.delete(tmp_ref)
- raise
end
private
diff --git a/app/services/base_service.rb b/app/services/base_service.rb
index f00ec7408b6..b48ca67d4d2 100644
--- a/app/services/base_service.rb
+++ b/app/services/base_service.rb
@@ -39,10 +39,7 @@ class BaseService
def deny_visibility_level(model, denied_visibility_level = nil)
denied_visibility_level ||= model.visibility_level
- level_name = 'Unknown'
- Gitlab::VisibilityLevel.options.each do |name, level|
- level_name = name if level == denied_visibility_level
- end
+ level_name = Gitlab::VisibilityLevel.level_name(denied_visibility_level)
model.errors.add(
:visibility_level,
diff --git a/app/services/create_branch_service.rb b/app/services/create_branch_service.rb
index de18f3bc556..f139872c728 100644
--- a/app/services/create_branch_service.rb
+++ b/app/services/create_branch_service.rb
@@ -1,10 +1,10 @@
require_relative 'base_service'
class CreateBranchService < BaseService
- def execute(branch_name, ref)
+ def execute(branch_name, ref, source_project: @project)
valid_branch = Gitlab::GitRefValidator.validate(branch_name)
if valid_branch == false
- return error('Branch name invalid')
+ return error('Branch name is invalid')
end
repository = project.repository
@@ -13,7 +13,20 @@ class CreateBranchService < BaseService
return error('Branch already exists')
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
push_data = build_push_data(project, current_user, new_branch)
diff --git a/app/services/files/base_service.rb b/app/services/files/base_service.rb
index 9a67b160940..0326a8823e9 100644
--- a/app/services/files/base_service.rb
+++ b/app/services/files/base_service.rb
@@ -3,8 +3,10 @@ module Files
class ValidationError < StandardError; end
def execute
- @current_branch = params[:current_branch]
+ @source_project = params[:source_project] || @project
+ @source_branch = params[:source_branch]
@target_branch = params[:target_branch]
+
@commit_message = params[:commit_message]
@file_path = params[:file_path]
@file_content = if params[:file_content_encoding] == 'base64'
@@ -16,8 +18,8 @@ module Files
# Validate parameters
validate
- # Create new branch if it different from current_branch
- if @target_branch != @current_branch
+ # Create new branch if it different from source_branch
+ if different_branch?
create_target_branch
end
@@ -26,18 +28,14 @@ module Files
else
error("Something went wrong. Your changes were not committed")
end
- rescue Repository::CommitError, GitHooksService::PreReceiveError, ValidationError => ex
+ rescue Repository::CommitError, Gitlab::Git::Repository::InvalidBlobName, GitHooksService::PreReceiveError, ValidationError => ex
error(ex.message)
end
private
- def current_branch
- @current_branch ||= params[:current_branch]
- end
-
- def target_branch
- @target_branch ||= params[:target_branch]
+ def different_branch?
+ @source_branch != @target_branch || @source_project != @project
end
def raise_error(message)
@@ -52,11 +50,11 @@ module Files
end
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")
end
- if @current_branch != @target_branch
+ if different_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")
end
@@ -65,10 +63,10 @@ module Files
end
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
- 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
diff --git a/app/services/files/create_service.rb b/app/services/files/create_service.rb
index 2348920cc58..e4cde4a2fd8 100644
--- a/app/services/files/create_service.rb
+++ b/app/services/files/create_service.rb
@@ -26,7 +26,7 @@ module Files
unless project.empty_repo?
@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
raise_error("Your changes could not be committed because a file with the same name already exists")
diff --git a/app/services/projects/update_service.rb b/app/services/projects/update_service.rb
index 69bdd045ddf..895e089bea3 100644
--- a/app/services/projects/update_service.rb
+++ b/app/services/projects/update_service.rb
@@ -3,12 +3,16 @@ module Projects
def execute
# check that user is allowed to set specified visibility_level
new_visibility = params[:visibility_level]
- if new_visibility && new_visibility.to_i != project.visibility_level
- unless can?(current_user, :change_visibility_level, project) &&
- Gitlab::VisibilityLevel.allowed_for?(current_user, new_visibility)
- deny_visibility_level(project, new_visibility)
- return project
+ if new_visibility
+ if new_visibility.to_i != project.visibility_level
+ unless can?(current_user, :change_visibility_level, project) &&
+ Gitlab::VisibilityLevel.allowed_for?(current_user, new_visibility)
+ deny_visibility_level(project, new_visibility)
+ return project
+ end
end
+
+ return false unless visibility_level_allowed?(new_visibility)
end
new_branch = params[:default_branch]
@@ -23,5 +27,19 @@ module Projects
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
diff --git a/app/views/explore/projects/index.html.haml b/app/views/explore/projects/index.html.haml
index 76bdd68fd76..b9a958fbe7b 100644
--- a/app/views/explore/projects/index.html.haml
+++ b/app/views/explore/projects/index.html.haml
@@ -6,7 +6,7 @@
- else
= render 'explore/head'
-.gray-content-block.clearfix
+.gray-content-block.clearfix.second-block
= render 'filter'
= render 'projects', projects: @projects
= paginate @projects, theme: "gitlab"
diff --git a/app/views/explore/projects/starred.html.haml b/app/views/explore/projects/starred.html.haml
index e30c3633223..95d46e331f8 100644
--- a/app/views/explore/projects/starred.html.haml
+++ b/app/views/explore/projects/starred.html.haml
@@ -7,7 +7,7 @@
= render 'explore/head'
.explore-trending-block
- .gray-content-block
+ .gray-content-block.second-block
.pull-right
= render 'explore/projects/dropdown'
.oneline
diff --git a/app/views/explore/projects/trending.html.haml b/app/views/explore/projects/trending.html.haml
index 1412b19acde..fa0b718e48b 100644
--- a/app/views/explore/projects/trending.html.haml
+++ b/app/views/explore/projects/trending.html.haml
@@ -7,7 +7,7 @@
= render 'explore/head'
.explore-trending-block
- .gray-content-block
+ .gray-content-block.second-block
.pull-right
= render 'explore/projects/dropdown'
.oneline
diff --git a/app/views/projects/_commit_button.html.haml b/app/views/projects/_commit_button.html.haml
index 2fd3d9e1be4..640612ca433 100644
--- a/app/views/projects/_commit_button.html.haml
+++ b/app/views/projects/_commit_button.html.haml
@@ -2,3 +2,7 @@
= button_tag 'Commit Changes', class: 'btn commit-btn js-commit-button btn-create'
= link_to 'Cancel', cancel_path,
class: 'btn btn-cancel', data: {confirm: leave_edit_message}
+
+ - unless can?(current_user, :push_code, @project)
+ .inline.prepend-left-10
+ = commit_in_fork_help
diff --git a/app/views/projects/_home_panel.html.haml b/app/views/projects/_home_panel.html.haml
index c1669ac046b..e92115b9b98 100644
--- a/app/views/projects/_home_panel.html.haml
+++ b/app/views/projects/_home_panel.html.haml
@@ -27,7 +27,7 @@
= icon('rss')
.project-repo-buttons
- .split-one
+ .split-one.count-buttons
= render 'projects/buttons/star'
= render 'projects/buttons/fork'
@@ -38,3 +38,6 @@
= render 'projects/buttons/dropdown'
= render 'projects/buttons/notifications'
+
+:coffeescript
+ new Star() \ No newline at end of file
diff --git a/app/views/projects/blob/_actions.html.haml b/app/views/projects/blob/_actions.html.haml
index b1df8d19938..cdac50f7a8d 100644
--- a/app/views/projects/blob/_actions.html.haml
+++ b/app/views/projects/blob/_actions.html.haml
@@ -2,7 +2,7 @@
= link_to 'Raw', namespace_project_raw_path(@project.namespace, @project, @id),
class: 'btn btn-sm', target: '_blank'
-# 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)
= link_to 'Normal View', namespace_project_blob_path(@project.namespace, @project, @id),
class: 'btn btn-sm'
@@ -14,13 +14,8 @@
= link_to 'Permalink', namespace_project_blob_path(@project.namespace, @project,
tree_join(@commit.sha, @path)), class: 'btn btn-sm'
-- if blob_editable?(@blob)
+- if current_user
.btn-group{ role: "group" }
- = edit_blob_link(@project, @ref, @path)
- %button.btn.btn-default{ 'data-target' => '#modal-upload-blob', 'data-toggle' => 'modal' } Replace
- %button.btn.btn-remove{ 'data-target' => '#modal-remove-blob', 'data-toggle' => 'modal' } Delete
-- 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
+ = edit_blob_link
+ = replace_blob_link
+ = delete_blob_link
diff --git a/app/views/projects/blob/_new_dir.html.haml b/app/views/projects/blob/_new_dir.html.haml
index fc6c9f5fd09..084608bbba3 100644
--- a/app/views/projects/blob/_new_dir.html.haml
+++ b/app/views/projects/blob/_new_dir.html.haml
@@ -17,5 +17,9 @@
= submit_tag "Create directory", class: 'btn btn-create'
= 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
new NewCommitForm($('.js-create-dir-form'))
diff --git a/app/views/projects/blob/_upload.html.haml b/app/views/projects/blob/_upload.html.haml
index ecc90a30e78..676924dc6ca 100644
--- a/app/views/projects/blob/_upload.html.haml
+++ b/app/views/projects/blob/_upload.html.haml
@@ -20,6 +20,11 @@
= button_tag button_title, class: 'btn btn-small btn-create btn-upload-file', id: 'submit-all'
= 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
disableButtonIfEmptyField($('.js-upload-blob-form').find('.js-commit-message'), '.btn-upload-file');
new BlobFileDropzone($('.js-upload-blob-form'), '#{method}');
diff --git a/app/views/projects/blob/edit.html.haml b/app/views/projects/blob/edit.html.haml
index a47fe7ede80..09fa148b129 100644
--- a/app/views/projects/blob/edit.html.haml
+++ b/app/views/projects/blob/edit.html.haml
@@ -20,7 +20,7 @@
= hidden_field_tag 'last_commit', @last_commit
= hidden_field_tag 'content', '', id: "file-content"
= hidden_field_tag 'from_merge_request_id', params[:from_merge_request_id]
- = render 'projects/commit_button', ref: @ref, cancel_path: @after_edit_path
+ = render 'projects/commit_button', ref: @ref, cancel_path: namespace_project_blob_path(@project.namespace, @project, @id)
:javascript
blob = new EditBlob(gon.relative_url_root + "#{Gitlab::Application.config.assets.prefix}", "#{@blob.language.try(:ace_mode)}")
diff --git a/app/views/projects/blob/show.html.haml b/app/views/projects/blob/show.html.haml
index 3f8d11ed8c8..6988039b6c7 100644
--- a/app/views/projects/blob/show.html.haml
+++ b/app/views/projects/blob/show.html.haml
@@ -6,7 +6,7 @@
%div#tree-holder.tree-holder
= render 'blob', blob: @blob
-- if blob_editable?(@blob)
+- if can_edit_blob?(@blob)
= render 'projects/blob/remove'
- title = "Replace #{@blob.name}"
diff --git a/app/views/projects/branches/new.html.haml b/app/views/projects/branches/new.html.haml
index 31943a2407a..c659af6338c 100644
--- a/app/views/projects/branches/new.html.haml
+++ b/app/views/projects/branches/new.html.haml
@@ -9,11 +9,12 @@
New Branch
%hr
-= form_tag namespace_project_branches_path, method: :post, id: "new-branch-form", class: "form-horizontal js-requires-input" do
+= form_tag namespace_project_branches_path, method: :post, id: "new-branch-form", class: "form-horizontal js-create-branch-form js-requires-input" do
.form-group
= label_tag :branch_name, nil, class: 'control-label'
.col-sm-10
- = text_field_tag :branch_name, params[:branch_name], required: true, tabindex: 1, autofocus: true, class: 'form-control'
+ = text_field_tag :branch_name, params[:branch_name], required: true, tabindex: 1, autofocus: true, class: 'form-control js-branch-name'
+ .help-block.text-danger.js-branch-name-error
.form-group
= label_tag :ref, 'Create from', class: 'control-label'
.col-sm-10
@@ -26,7 +27,4 @@
:javascript
var availableRefs = #{@project.repository.ref_names.to_json};
- $("#ref").autocomplete({
- source: availableRefs,
- minLength: 1
- });
+ new NewBranchForm($('.js-create-branch-form'), availableRefs)
diff --git a/app/views/projects/buttons/_dropdown.html.haml b/app/views/projects/buttons/_dropdown.html.haml
index b277b765b6b..1f639fecc30 100644
--- a/app/views/projects/buttons/_dropdown.html.haml
+++ b/app/views/projects/buttons/_dropdown.html.haml
@@ -18,10 +18,11 @@
= link_to new_namespace_project_snippet_path(@project.namespace, @project) do
= icon('file-text-o fw')
New snippet
+
- if can?(current_user, :push_code, @project)
%li.divider
%li
- = link_to namespace_project_new_blob_path(@project.namespace, @project, @project.default_branch || 'master'), title: 'New file' do
+ = link_to namespace_project_new_blob_path(@project.namespace, @project, @project.default_branch || 'master') do
= icon('file fw')
New file
%li
@@ -32,3 +33,20 @@
= link_to new_namespace_project_tag_path(@project.namespace, @project) do
= icon('tags fw')
New tag
+ - elsif current_user && current_user.already_forked?(@project)
+ %li.divider
+ %li
+ = link_to namespace_project_new_blob_path(@project.namespace, @project, @project.default_branch || 'master') do
+ = icon('file fw')
+ New file
+ - elsif can?(current_user, :fork_project, @project)
+ %li.divider
+ %li
+ - continue_params = { to: namespace_project_new_blob_path(@project.namespace, @project, @project.default_branch || 'master'),
+ 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 fork_path, method: :post do
+ = icon('file fw')
+ New file
diff --git a/app/views/projects/buttons/_fork.html.haml b/app/views/projects/buttons/_fork.html.haml
index 2d3abf09051..133531887a2 100644
--- a/app/views/projects/buttons/_fork.html.haml
+++ b/app/views/projects/buttons/_fork.html.haml
@@ -4,10 +4,15 @@
= link_to namespace_project_path(current_user, current_user.fork_of(@project)), title: 'Go to your fork', class: 'btn has_tooltip' do
= icon('code-fork fw')
Fork
+ %div.count-with-arrow
+ %span.arrow
%span.count
= @project.forks_count
- else
= link_to new_namespace_project_fork_path(@project.namespace, @project), title: "Fork project", class: 'btn has_tooltip' do
= icon('code-fork fw')
+ Fork
+ %div.count-with-arrow
+ %span.arrow
%span.count
= @project.forks_count
diff --git a/app/views/projects/buttons/_star.html.haml b/app/views/projects/buttons/_star.html.haml
index 41a3ec6d90f..21ba426aaa1 100644
--- a/app/views/projects/buttons/_star.html.haml
+++ b/app/views/projects/buttons/_star.html.haml
@@ -1,19 +1,21 @@
- if current_user
= link_to toggle_star_namespace_project_path(@project.namespace, @project), class: 'btn star-btn toggle-star has_tooltip', method: :post, remote: true, title: "Star project" do
- = icon('star fw')
- %span.count
+ - if current_user.starred?(@project)
+ = icon('star fw')
+ %span.starred Unstar
+ - else
+ = icon('star-o fw')
+ %span Star
+ %div.count-with-arrow
+ %span.arrow
+ %span.count.star-count
= @project.star_count
- :javascript
- $('.project-home-panel .toggle-star').on('ajax:success', function (e, data, status, xhr) {
- $(this).replaceWith(data.html);
- })
- .on('ajax:error', function (e, xhr, status, error) {
- new Flash('Star toggle failed. Try again later.', 'alert');
- });
-
- else
= link_to new_user_session_path, class: 'btn has_tooltip star-btn', title: 'You must sign in to star a project' do
= icon('star fw')
+ Star
+ %div.count-with-arrow
+ %span.arrow
%span.count
= @project.star_count
diff --git a/app/views/projects/diffs/_file.html.haml b/app/views/projects/diffs/_file.html.haml
index 327e7d9245a..517f6aef7c5 100644
--- a/app/views/projects/diffs/_file.html.haml
+++ b/app/views/projects/diffs/_file.html.haml
@@ -24,7 +24,7 @@
= "#{diff_file.diff.a_mode} → #{diff_file.diff.b_mode}"
.diff-controls
- - if blob_viewable?(blob)
+ - if blob_text_viewable?(blob)
= link_to '#', class: 'js-toggle-diff-comments btn btn-sm active has_tooltip', title: "Toggle comments for this file" do
%i.fa.fa-comments
&nbsp;
@@ -32,14 +32,15 @@
- if editable_diff?(diff_file)
= edit_blob_link(@merge_request.source_project,
@merge_request.source_branch, diff_file.new_path,
- after: '&nbsp;', from_merge_request_id: @merge_request.id)
+ from_merge_request_id: @merge_request.id)
+ &nbsp;
= view_file_btn(diff_commit.id, diff_file, project)
.diff-content.diff-wrap-lines
-# Skipp all non non-supported blobs
- return unless blob.respond_to?('text?')
- - if blob_viewable?(blob)
+ - if blob_text_viewable?(blob)
- if diff_view == 'parallel'
= render "projects/diffs/parallel_view", diff_file: diff_file, project: project, blob: blob, index: i
- else
diff --git a/app/views/projects/forks/new.html.haml b/app/views/projects/forks/new.html.haml
index f0b0a11c04a..8a2c027a455 100644
--- a/app/views/projects/forks/new.html.haml
+++ b/app/views/projects/forks/new.html.haml
@@ -43,4 +43,3 @@
%i.fa.fa-spinner.fa-spin
Forking repository
%p Please wait a moment, this page will automatically refresh when ready.
-
diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml
index 105c731c7e1..a051729dc32 100644
--- a/app/views/projects/merge_requests/_merge_request.html.haml
+++ b/app/views/projects/merge_requests/_merge_request.html.haml
@@ -17,7 +17,7 @@
- if merge_request.open? && merge_request.broken?
%li
- = link_to merge_request_path(merge_request), class: "has_tooltip", title: "Cannot be merged automatically", data: {container: 'body'} do
+ = link_to merge_request_path(merge_request), class: "has_tooltip", title: "Cannot be merged automatically", data: { container: 'body' } do
= icon('exclamation-triangle')
- if merge_request.assignee
diff --git a/app/views/projects/tree/_tree_content.html.haml b/app/views/projects/tree/_tree_content.html.haml
index 1bc90edd8f0..1927883513a 100644
--- a/app/views/projects/tree/_tree_content.html.haml
+++ b/app/views/projects/tree/_tree_content.html.haml
@@ -29,7 +29,7 @@
- if tree.readme
= render "projects/tree/readme", readme: tree.readme
-- if allowed_tree_edit?
+- if can_edit_tree?
= render 'projects/blob/upload', title: 'Upload New File', placeholder: 'Upload new file', button_title: 'Upload file', form_path: namespace_project_create_blob_path(@project.namespace, @project, @id), method: :post
= render 'projects/blob/new_dir'
diff --git a/app/views/projects/tree/_tree_header.html.haml b/app/views/projects/tree/_tree_header.html.haml
index 89b072cea92..3343288ad2b 100644
--- a/app/views/projects/tree/_tree_header.html.haml
+++ b/app/views/projects/tree/_tree_header.html.haml
@@ -11,34 +11,65 @@
= link_to truncate(title, length: 40), namespace_project_tree_path(@project.namespace, @project, path)
- else
= link_to title, '#'
- - if allowed_tree_edit?
+
+ - if current_user
%li
- %span.dropdown
- %a.dropdown-toggle.btn.btn-sm.add-to-tree{href: '#', "data-toggle" => "dropdown"}
+ - if !on_top_of_branch?
+ %span.btn.btn-sm.add-to-tree.disabled.has_tooltip{title: "You can only add files when you are on a branch", data: { container: 'body' }}
= icon('plus')
- %ul.dropdown-menu
- %li
- = link_to namespace_project_new_blob_path(@project.namespace, @project, @id), title: 'Create file', id: 'new-file-link' do
- = icon('pencil fw')
- New file
- %li
- = link_to '#modal-upload-blob', { 'data-target' => '#modal-upload-blob', 'data-toggle' => 'modal'} do
- = icon('file fw')
- Upload file
- %li
- = link_to '#modal-create-new-dir', { 'data-target' => '#modal-create-new-dir', 'data-toggle' => 'modal'} do
- = icon('folder fw')
- New directory
- %li.divider
- %li
- = link_to new_namespace_project_branch_path(@project.namespace, @project) do
- = icon('code-fork fw')
- New branch
- %li
- = link_to new_namespace_project_tag_path(@project.namespace, @project) do
- = icon('tags fw')
- New tag
- - elsif !on_top_of_branch?
- %li
- %span.btn.btn-sm.add-to-tree.disabled.has_tooltip{title: "You can only add files when you are on a branch.", data: {container: 'body'}}
- = icon('plus')
+ - else
+ %span.dropdown
+ %a.dropdown-toggle.btn.btn-sm.add-to-tree{href: '#', "data-toggle" => "dropdown"}
+ = icon('plus')
+ %ul.dropdown-menu
+ - if can_edit_tree?
+ %li
+ = link_to namespace_project_new_blob_path(@project.namespace, @project, @id) do
+ = icon('pencil fw')
+ New file
+ %li
+ = link_to '#modal-upload-blob', { 'data-target' => '#modal-upload-blob', 'data-toggle' => 'modal'} do
+ = icon('file fw')
+ Upload file
+ %li
+ = link_to '#modal-create-new-dir', { 'data-target' => '#modal-create-new-dir', 'data-toggle' => 'modal'} do
+ = icon('folder fw')
+ New directory
+ - elsif can?(current_user, :fork_project, @project)
+ %li
+ - continue_params = { to: namespace_project_new_blob_path(@project.namespace, @project, @id),
+ 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 fork_path, method: :post do
+ = icon('pencil fw')
+ New file
+ %li
+ - continue_params = { to: request.fullpath,
+ notice: edit_in_new_fork_notice + " Try to upload a 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 fork_path, method: :post do
+ = icon('file fw')
+ Upload file
+ %li
+ - continue_params = { to: request.fullpath,
+ notice: edit_in_new_fork_notice + " Try to create a new directory 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 fork_path, method: :post do
+ = icon('folder fw')
+ New directory
+
+ %li.divider
+ %li
+ = link_to new_namespace_project_branch_path(@project.namespace, @project) do
+ = icon('code-fork fw')
+ New branch
+ %li
+ = link_to new_namespace_project_tag_path(@project.namespace, @project) do
+ = icon('tags fw')
+ New tag
diff --git a/app/views/shared/_clone_panel.html.haml b/app/views/shared/_clone_panel.html.haml
index edb5778f424..687a59c270f 100644
--- a/app/views/shared/_clone_panel.html.haml
+++ b/app/views/shared/_clone_panel.html.haml
@@ -1,10 +1,27 @@
- project = project || @project
-.git-clone-holder.input-group
- .input-group-addon.git-protocols
- .input-group-btn
- = ssh_clone_button(project)
- .input-group-btn
- = http_clone_button(project)
+
+.git-clone-holder
+ .btn-group.clone-options
+ %a#clone-dropdown.clone-dropdown-btn.btn{href: '#', 'data-toggle' => 'dropdown'}
+ %span
+ = default_clone_protocol.upcase
+ = icon('angle-down')
+ %ul.dropdown-menu.dropdown-menu-right.clone-options-dropdown
+ %li
+ %a#ssh-selector{href: @project.ssh_url_to_repo}
+ SSH
+ %li
+ %a#http-selector{href: @project.http_url_to_repo}
+ HTTPS
+
= text_field_tag :project_clone, default_url_to_repo(project), class: "js-select-on-focus form-control", readonly: true
.input-group-btn
= clipboard_button(clipboard_target: '#project_clone')
+
+:javascript
+ $('ul.clone-options-dropdown a').on('click',function(e){
+ e.preventDefault();
+ var $this = $(this);
+ $('a.clone-dropdown-btn span').text($this.text());
+ $('#project_clone').val($this.attr('href'));
+ });
diff --git a/app/views/shared/_new_commit_form.html.haml b/app/views/shared/_new_commit_form.html.haml
index 111219f2064..0c8ac48bb58 100644
--- a/app/views/shared/_new_commit_form.html.haml
+++ b/app/views/shared/_new_commit_form.html.haml
@@ -1,16 +1,22 @@
= render 'shared/commit_message_container', placeholder: placeholder
-- unless @project.empty_repo?
- .form-group.branch
- = label_tag 'new_branch', 'Target branch', class: 'control-label'
- .col-sm-10
- = text_field_tag 'new_branch', @new_branch || tree_edit_branch, required: true, class: "form-control js-new-branch"
+- if @project.empty_repo?
+ = hidden_field_tag 'target_branch', @ref
+- else
+ - if can?(current_user, :push_code, @project)
+ .form-group.branch
+ = label_tag 'target_branch', 'Target branch', class: 'control-label'
+ .col-sm-10
+ = text_field_tag 'target_branch', @target_branch || tree_edit_branch, required: true, class: "form-control js-target-branch"
- .js-create-merge-request-container
- .checkbox
- - nonce = SecureRandom.hex
- = label_tag "create_merge_request-#{nonce}" do
- = check_box_tag 'create_merge_request', 1, true, class: 'js-create-merge-request', id: "create_merge_request-#{nonce}"
- Start a <strong>new merge request</strong> with these changes
+ .js-create-merge-request-container
+ .checkbox
+ - nonce = SecureRandom.hex
+ = label_tag "create_merge_request-#{nonce}" do
+ = check_box_tag 'create_merge_request', 1, true, class: 'js-create-merge-request', id: "create_merge_request-#{nonce}"
+ Start a <strong>new merge request</strong> with these changes
+ - else
+ = hidden_field_tag 'target_branch', @target_branch || tree_edit_branch
+ = hidden_field_tag 'create_merge_request', 1
= hidden_field_tag 'original_branch', @ref, class: 'js-original-branch'
diff --git a/app/views/votes/_votes_block.html.haml b/app/views/votes/_votes_block.html.haml
index 829f1664fba..e16187bb42f 100644
--- a/app/views/votes/_votes_block.html.haml
+++ b/app/views/votes/_votes_block.html.haml
@@ -11,6 +11,7 @@
= icon('smile-o')
.emoji-menu
.emoji-menu-content
+ = text_field_tag :emoji_search, "", class: "emoji-search search-input form-control"
- AwardEmoji.emoji_by_category.each do |category, emojis|
%h5= AwardEmoji::CATEGORIES[category]
%ul
@@ -32,11 +33,11 @@
aliases
)
- $(".emoji-menu-content li").click (e)->
+ $(".awards").on "click", ".emoji-menu-content li", (e) ->
emoji = $(this).find(".emoji-icon").data("emoji")
awards_handler.addAward(emoji)
- $(".awards").on "click", ".award", (e)->
+ $(".awards").on "click", ".award", (e) ->
emoji = $(this).find(".icon").data("emoji")
awards_handler.addAward(emoji)
diff --git a/features/project/commits/branches.feature b/features/project/commits/branches.feature
index 5103ca12947..2c17d32154a 100644
--- a/features/project/commits/branches.feature
+++ b/features/project/commits/branches.feature
@@ -25,6 +25,7 @@ Feature: Project Commits Branches
And I click branch 'improve/awesome' delete link
Then I should not see branch 'improve/awesome'
+ @javascript
Scenario: I create a branch with invalid name
Given I visit project branches page
And I click new branch link
diff --git a/features/project/create.feature b/features/project/create.feature
index a86079143e5..27136798e36 100644
--- a/features/project/create.feature
+++ b/features/project/create.feature
@@ -1,3 +1,4 @@
+@project-create
Feature: Project Create
In order to get access to project sections
A user with ability to create a project
diff --git a/features/project/issues/award_emoji.feature b/features/project/issues/award_emoji.feature
index cbf2e7104ab..9a06fdc2ee6 100644
--- a/features/project/issues/award_emoji.feature
+++ b/features/project/issues/award_emoji.feature
@@ -19,6 +19,12 @@ Feature: Award Emoji
Then I can see the activity and food categories
@javascript
+ Scenario: I can search emoji
+ Given I click to emoji-picker
+ And I search "hand"
+ Then I see search result for "hand"
+
+ @javascript
Scenario: I add award emoji using regular comment
- Given I leave comment with a single emoji
- Then I have award added
+ Given I leave comment with a single emoji
+ Then I have award added
diff --git a/features/project/merge_requests/accept.feature b/features/project/merge_requests/accept.feature
index d5e4f2b0bd8..330ec8ae0fe 100644
--- a/features/project/merge_requests/accept.feature
+++ b/features/project/merge_requests/accept.feature
@@ -13,6 +13,14 @@ Feature: Project Merge Requests Acceptance
And I should not see the Remove Source Branch button
@javascript
+ Scenario: Accepting the Merge Request when URL has an anchor
+ Given I am on the Merge Request detail with note anchor page
+ When I click on "Remove source branch" option
+ And I click on Accept Merge Request
+ Then I should see merge request merged
+ And I should not see the Remove Source Branch button
+
+ @javascript
Scenario: Accepting the Merge Request without removing the source branch
Given I am on the Merge Request detail page
When I click on Accept Merge Request
diff --git a/features/project/source/browse_files.feature b/features/project/source/browse_files.feature
index 02159ee3776..a8c276b949e 100644
--- a/features/project/source/browse_files.feature
+++ b/features/project/source/browse_files.feature
@@ -24,6 +24,12 @@ Feature: Project Source Browse Files
Given I click on "New file" link in repo
Then I can see new file page
+ Scenario: I can create file when I don't have write access
+ Given I don't have write access
+ And I click on "New file" link in repo
+ Then I should see a notice about a new fork having been created
+ Then I can see new file page
+
@javascript
Scenario: I can create and commit file
Given I click on "New file" link in repo
@@ -35,6 +41,17 @@ Feature: Project Source Browse Files
And I should see its new content
@javascript
+ Scenario: I can create and commit file when I don't have write access
+ Given I don't have write access
+ And I click on "New file" link in repo
+ And I edit code
+ And I fill the new file name
+ And I fill the commit message
+ And I click on "Commit Changes"
+ Then I am redirected to the fork's new merge request page
+ And I can see the new commit message
+
+ @javascript
Scenario: I can create and commit file with new lines at the end of file
Given I click on "New file" link in repo
And I edit code with new lines at end of file
@@ -46,6 +63,17 @@ Feature: Project Source Browse Files
And I should see its content with new lines preserved at end of file
@javascript
+ Scenario: I can create and commit file and specify new branch
+ Given I click on "New file" link in repo
+ And I edit code
+ And I fill the new file name
+ And I fill the commit message
+ And I fill the new branch name
+ And I click on "Commit Changes"
+ Then I am redirected to the new merge request page
+ And I should see its new content
+
+ @javascript
Scenario: I can upload file and commit
Given I click on "Upload file" link in repo
And I upload a new text file
@@ -57,6 +85,19 @@ Feature: Project Source Browse Files
And I can see the new commit message
@javascript
+ Scenario: I can upload file and commit when I don't have write access
+ Given I don't have write access
+ And I click on "Upload file" link in repo
+ Then I should see a notice about a new fork having been created
+ When I click on "Upload file" link in repo
+ And I upload a new text file
+ And I fill the upload file commit message
+ And I click on "Upload file"
+ Then I can see the new text file
+ And I am redirected to the fork's new merge request page
+ And I can see the new commit message
+
+ @javascript
Scenario: I can replace file and commit
Given I click on ".gitignore" file in repo
And I see the ".gitignore"
@@ -68,15 +109,19 @@ Feature: Project Source Browse Files
And I can see the replacement commit message
@javascript
- Scenario: I can create and commit file and specify new branch
- Given I click on "New file" link in repo
- And I edit code
- And I fill the new file name
- And I fill the commit message
- And I fill the new branch name
- And I click on "Commit Changes"
- Then I am redirected to the new merge request page
- And I should see its new content
+ Scenario: I can replace file and commit when I don't have write access
+ Given I don't have write access
+ And I click on ".gitignore" file in repo
+ And I see the ".gitignore"
+ And I click on "Replace"
+ Then I should see a notice about a new fork having been created
+ When I click on "Replace"
+ And I replace it with a text file
+ And I fill the replace file commit message
+ And I click on "Replace file"
+ Then I can see the new text file
+ And I am redirected to the fork's new merge request page
+ And I can see the replacement commit message
@javascript
Scenario: I can create file in empty repo
@@ -117,6 +162,14 @@ Feature: Project Source Browse Files
And I click button "Edit"
Then I can edit code
+ @javascript
+ Scenario: I can edit file when I don't have write access
+ Given I don't have write access
+ And I click on ".gitignore" file in repo
+ And I click button "Edit"
+ Then I should see a notice about a new fork having been created
+ And I can edit code
+
Scenario: If the file is binary the edit link is hidden
Given I visit a binary file in the repo
Then I cannot see the edit button
@@ -132,6 +185,17 @@ Feature: Project Source Browse Files
And I should see its new content
@javascript
+ Scenario: I can edit and commit file when I don't have write access
+ Given I don't have write access
+ And I click on ".gitignore" file in repo
+ And I click button "Edit"
+ And I edit code
+ And I fill the commit message
+ And I click on "Commit Changes"
+ Then I am redirected to the fork's new merge request page
+ And I can see the new commit message
+
+ @javascript
Scenario: I can edit and commit file to new branch
Given I click on ".gitignore" file in repo
And I click button "Edit"
@@ -162,6 +226,17 @@ Feature: Project Source Browse Files
Then I am redirected to the new merge request page
@javascript
+ Scenario: I can create directory in repo when I don't have write access
+ Given I don't have write access
+ When I click on "New directory" link in repo
+ Then I should see a notice about a new fork having been created
+ When I click on "New directory" link in repo
+ And I fill the new directory name
+ And I fill the commit message
+ And I click on "Create directory"
+ Then I am redirected to the fork's new merge request page
+
+ @javascript
Scenario: I attempt to create an existing directory
When I click on "New directory" link in repo
And I fill an existing directory name
@@ -188,6 +263,19 @@ Feature: Project Source Browse Files
Then I am redirected to the files URL
And I don't see the ".gitignore"
+ @javascript
+ Scenario: I can delete file and commit when I don't have write access
+ Given I don't have write access
+ And I click on ".gitignore" file in repo
+ And I see the ".gitignore"
+ And I click on "Delete"
+ Then I should see a notice about a new fork having been created
+ When I click on "Delete"
+ And I fill the commit message
+ And I click on "Delete file"
+ Then I am redirected to the fork's new merge request page
+ And I can see the new commit message
+
Scenario: I can browse directory with Browse Dir
Given I click on files directory
And I click on History link
diff --git a/features/project/star.feature b/features/project/star.feature
index a45f9c470ea..618f44fe6dc 100644
--- a/features/project/star.feature
+++ b/features/project/star.feature
@@ -1,3 +1,4 @@
+@project-stars
Feature: Project Star
Scenario: New projects have 0 stars
Given public project "Community"
diff --git a/features/steps/project/commits/branches.rb b/features/steps/project/commits/branches.rb
index 338f5e8d3ee..0a42931147d 100644
--- a/features/steps/project/commits/branches.rb
+++ b/features/steps/project/commits/branches.rb
@@ -61,7 +61,8 @@ class Spinach::Features::ProjectCommitsBranches < Spinach::FeatureSteps
end
step 'I should see new an error that branch is invalid' do
- expect(page).to have_content 'Branch name invalid'
+ expect(page).to have_content 'Branch name is invalid'
+ expect(page).to have_content "can't contain spaces"
end
step 'I should see new an error that ref is invalid' do
diff --git a/features/steps/project/create.rb b/features/steps/project/create.rb
index f90218f3791..8a0e8fc2b6c 100644
--- a/features/steps/project/create.rb
+++ b/features/steps/project/create.rb
@@ -26,7 +26,8 @@ class Spinach::Features::ProjectCreate < Spinach::FeatureSteps
end
step 'I click on HTTP' do
- click_button 'HTTP'
+ find('#clone-dropdown').click
+ find('#http-selector').click
end
step 'Remote url should update to http link' do
@@ -34,7 +35,8 @@ class Spinach::Features::ProjectCreate < Spinach::FeatureSteps
end
step 'If I click on SSH' do
- click_button 'SSH'
+ find('#clone-dropdown').click
+ find('#ssh-selector').click
end
step 'Remote url should update to ssh link' do
diff --git a/features/steps/project/issues/award_emoji.rb b/features/steps/project/issues/award_emoji.rb
index c94d0ba7306..a7e15398819 100644
--- a/features/steps/project/issues/award_emoji.rb
+++ b/features/steps/project/issues/award_emoji.rb
@@ -52,4 +52,16 @@ class Spinach::Features::AwardEmoji < Spinach::FeatureSteps
click_button 'Add Comment'
end
end
+
+ step 'I search "hand"' do
+ page.within('.emoji-menu-content') do
+ fill_in 'emoji_search', with: 'hand'
+ end
+ end
+
+ step 'I see search result for "hand"' do
+ page.within '.emoji-menu-content' do
+ expect(page).to have_selector '[data-emoji="raised_hand"]'
+ end
+ end
end
diff --git a/features/steps/project/merge_requests/acceptance.rb b/features/steps/project/merge_requests/acceptance.rb
index 383c055c4ef..2685f5fd6b4 100644
--- a/features/steps/project/merge_requests/acceptance.rb
+++ b/features/steps/project/merge_requests/acceptance.rb
@@ -6,6 +6,10 @@ class Spinach::Features::ProjectMergeRequestsAcceptance < Spinach::FeatureSteps
visit merge_request_path(@merge_request)
end
+ step 'I am on the Merge Request detail with note anchor page' do
+ visit merge_request_path(@merge_request, anchor: 'note_123')
+ end
+
step 'I click on "Remove source branch" option' do
check('Remove source branch')
end
diff --git a/features/steps/project/source/browse_files.rb b/features/steps/project/source/browse_files.rb
index 0c6df18ce2e..d08935aa101 100644
--- a/features/steps/project/source/browse_files.rb
+++ b/features/steps/project/source/browse_files.rb
@@ -5,6 +5,12 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
include SharedPaths
include RepoHelpers
+ step "I don't have write access" do
+ @project = create(:project, name: "Other Project", path: "other-project")
+ @project.team << [@user, :reporter]
+ visit namespace_project_tree_path(@project.namespace, @project, root_ref)
+ end
+
step 'I should see files from repository' do
expect(page).to have_content "VERSION"
expect(page).to have_content ".gitignore"
@@ -75,7 +81,7 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
end
step 'I fill the new branch name' do
- fill_in :new_branch, with: 'new_branch_name', visible: true
+ fill_in :target_branch, with: 'new_branch_name', visible: true
end
step 'I fill the new file name with an illegal name' do
@@ -87,7 +93,7 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
end
step 'I fill the commit message' do
- fill_in :commit_message, with: 'Not yet a commit message.', visible: true
+ fill_in :commit_message, with: 'New commit message', visible: true
end
step 'I click link "Diff"' do
@@ -103,7 +109,7 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
end
step 'I click on "Delete"' do
- click_button 'Delete'
+ click_on 'Delete'
end
step 'I click on "Delete file"' do
@@ -111,7 +117,7 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
end
step 'I click on "Replace"' do
- click_button "Replace"
+ click_on "Replace"
end
step 'I click on "Replace file"' do
@@ -124,7 +130,7 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
step 'I click on "New file" link in repo' do
find('.add-to-tree').click
- click_link 'Create file'
+ click_link 'New file'
end
step 'I click on "Upload file" link in repo' do
@@ -155,7 +161,7 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
end
step 'I can see the new commit message' do
- expect(page).to have_content "New upload commit message"
+ expect(page).to have_content "New commit message"
end
step 'I upload a new text file' do
@@ -164,7 +170,7 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
step 'I fill the upload file commit message' do
page.within('#modal-upload-blob') do
- fill_in :commit_message, with: 'New upload commit message'
+ fill_in :commit_message, with: 'New commit message'
end
end
@@ -251,9 +257,14 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
expect(current_path).to eq(new_namespace_project_merge_request_path(@project.namespace, @project))
end
+ step "I am redirected to the fork's new merge request page" do
+ fork = @user.fork_of(@project)
+ expect(current_path).to eq(new_namespace_project_merge_request_path(fork.namespace, fork))
+ end
+
step 'I am redirected to the root directory' do
expect(current_path).to eq(
- namespace_project_tree_path(@project.namespace, @project, 'master/'))
+ namespace_project_tree_path(@project.namespace, @project, 'master'))
end
step "I don't see the permalink link" do
@@ -332,8 +343,12 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
expect(page).to have_content 'Permalink'
expect(page).not_to have_content 'Edit'
expect(page).not_to have_content 'Blame'
- expect(page).not_to have_content 'Delete'
- expect(page).not_to have_content 'Replace'
+ expect(page).to have_content 'Delete'
+ expect(page).to have_content 'Replace'
+ end
+
+ step 'I should see a notice about a new fork having been created' do
+ expect(page).to have_content "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
private
diff --git a/features/steps/project/star.rb b/features/steps/project/star.rb
index bd2e0619cdd..9f7c748a3b7 100644
--- a/features/steps/project/star.rb
+++ b/features/steps/project/star.rb
@@ -32,6 +32,6 @@ class Spinach::Features::ProjectStar < Spinach::FeatureSteps
protected
def has_n_stars(n)
- expect(page).to have_css(".star-btn .count", text: n, visible: true)
+ expect(page).to have_css(".star-count", text: n, visible: true)
end
end
diff --git a/lib/api/files.rb b/lib/api/files.rb
index a7a768f8895..8ad2c1883c7 100644
--- a/lib/api/files.rb
+++ b/lib/api/files.rb
@@ -7,7 +7,7 @@ module API
def commit_params(attrs)
{
file_path: attrs[:file_path],
- current_branch: attrs[:branch_name],
+ source_branch: attrs[:branch_name],
target_branch: attrs[:branch_name],
commit_message: attrs[:commit_message],
file_content: attrs[:content],
diff --git a/lib/gitlab/ldap/user.rb b/lib/gitlab/ldap/user.rb
index 4be99dd88c2..aef08c97d1d 100644
--- a/lib/gitlab/ldap/user.rb
+++ b/lib/gitlab/ldap/user.rb
@@ -14,7 +14,7 @@ module Gitlab
# LDAP distinguished name is case-insensitive
identity = ::Identity.
where(provider: provider).
- where('lower(extern_uid) = ?', uid.mb_chars.downcase.to_s).last
+ iwhere(extern_uid: uid).last
identity && identity.user
end
end
@@ -31,7 +31,7 @@ module Gitlab
def find_by_uid_and_provider
self.class.find_by_uid_and_provider(
- auth_hash.uid.downcase, auth_hash.provider)
+ auth_hash.uid, auth_hash.provider)
end
def find_by_email
@@ -47,7 +47,7 @@ module Gitlab
# find_or_initialize_by doesn't update `gl_user.identities`, and isn't autosaved.
identity = gl_user.identities.find { |identity| identity.provider == auth_hash.provider }
identity ||= gl_user.identities.build(provider: auth_hash.provider)
-
+
# For a new user set extern_uid to the LDAP DN
# For an existing user with matching email but changed DN, update the DN.
# For an existing user with no change in DN, this line changes nothing.
diff --git a/lib/gitlab/o_auth/user.rb b/lib/gitlab/o_auth/user.rb
index 17ce4d4b174..f1a362f5303 100644
--- a/lib/gitlab/o_auth/user.rb
+++ b/lib/gitlab/o_auth/user.rb
@@ -64,7 +64,7 @@ module Gitlab
# If a corresponding person exists with same uid in a LDAP server,
# set up a Gitlab user with dual LDAP and Omniauth identities.
- if user = Gitlab::LDAP::User.find_by_uid_and_provider(ldap_person.dn.downcase, ldap_person.provider)
+ if user = Gitlab::LDAP::User.find_by_uid_and_provider(ldap_person.dn, ldap_person.provider)
# Case when a LDAP user already exists in Gitlab. Add the Omniauth identity to existing account.
user.identities.build(extern_uid: auth_hash.uid, provider: auth_hash.provider)
else
diff --git a/lib/gitlab/visibility_level.rb b/lib/gitlab/visibility_level.rb
index 335dc44be19..3160a3c7582 100644
--- a/lib/gitlab/visibility_level.rb
+++ b/lib/gitlab/visibility_level.rb
@@ -51,6 +51,15 @@ module Gitlab
def allowed_fork_levels(origin_level)
[PRIVATE, INTERNAL, PUBLIC].select{ |level| level <= origin_level }
end
+
+ def level_name(level)
+ level_name = 'Unknown'
+ options.each do |name, lvl|
+ level_name = name if lvl == level.to_i
+ end
+
+ level_name
+ end
end
def private?
diff --git a/spec/controllers/projects/tree_controller_spec.rb b/spec/controllers/projects/tree_controller_spec.rb
index a474574c6e5..e74731c9ed8 100644
--- a/spec/controllers/projects/tree_controller_spec.rb
+++ b/spec/controllers/projects/tree_controller_spec.rb
@@ -98,7 +98,7 @@ describe Projects::TreeController do
project_id: project.to_param,
id: 'master',
dir_name: path,
- new_branch: target_branch,
+ target_branch: target_branch,
commit_message: 'Test commit message')
end
@@ -108,8 +108,8 @@ describe Projects::TreeController do
it 'redirects to the new directory' do
expect(subject).
- to redirect_to("/#{project.path_with_namespace}/blob/#{target_branch}/#{path}")
- expect(flash[:notice]).to eq('The directory has been successfully created')
+ to redirect_to("/#{project.path_with_namespace}/tree/#{target_branch}/#{path}")
+ expect(flash[:notice]).to eq('The directory has been successfully created.')
end
end
@@ -119,7 +119,7 @@ describe Projects::TreeController do
it 'does not allow overwriting of existing files' do
expect(subject).
- to redirect_to("/#{project.path_with_namespace}/blob/master")
+ to redirect_to("/#{project.path_with_namespace}/tree/master")
expect(flash[:alert]).to eq('Directory already exists as a file')
end
end
diff --git a/spec/javascripts/fixtures/new_branch.html.haml b/spec/javascripts/fixtures/new_branch.html.haml
new file mode 100644
index 00000000000..f06629e5ecc
--- /dev/null
+++ b/spec/javascripts/fixtures/new_branch.html.haml
@@ -0,0 +1,4 @@
+%form.js-create-branch-form
+ %input.js-branch-name
+ .js-branch-name-error
+ %input{id: "ref"}
diff --git a/spec/javascripts/new_branch_spec.js.coffee b/spec/javascripts/new_branch_spec.js.coffee
new file mode 100644
index 00000000000..f2ce85efcdc
--- /dev/null
+++ b/spec/javascripts/new_branch_spec.js.coffee
@@ -0,0 +1,160 @@
+#= require jquery-ui
+#= require new_branch_form
+
+describe 'Branch', ->
+ describe 'create a new branch', ->
+ fixture.preload('new_branch.html')
+
+ fillNameWith = (value) ->
+ $('.js-branch-name').val(value).trigger('blur')
+
+ expectToHaveError = (error) ->
+ expect($('.js-branch-name-error span').text()).toEqual(error)
+
+ beforeEach ->
+ fixture.load('new_branch.html')
+ $('form').on 'submit', (e) -> e.preventDefault()
+
+ @form = new NewBranchForm($('.js-create-branch-form'), [])
+
+ it "can't start with a dot", ->
+ fillNameWith '.foo'
+ expectToHaveError "can't start with '.'"
+
+ it "can't start with a slash", ->
+ fillNameWith '/foo'
+ expectToHaveError "can't start with '/'"
+
+ it "can't have two consecutive dots", ->
+ fillNameWith 'foo..bar'
+ expectToHaveError "can't contain '..'"
+
+ it "can't have spaces anywhere", ->
+ fillNameWith ' foo'
+ expectToHaveError "can't contain spaces"
+ fillNameWith 'foo bar'
+ expectToHaveError "can't contain spaces"
+ fillNameWith 'foo '
+ expectToHaveError "can't contain spaces"
+
+ it "can't have ~ anywhere", ->
+ fillNameWith '~foo'
+ expectToHaveError "can't contain '~'"
+ fillNameWith 'foo~bar'
+ expectToHaveError "can't contain '~'"
+ fillNameWith 'foo~'
+ expectToHaveError "can't contain '~'"
+
+ it "can't have tilde anwhere", ->
+ fillNameWith '~foo'
+ expectToHaveError "can't contain '~'"
+ fillNameWith 'foo~bar'
+ expectToHaveError "can't contain '~'"
+ fillNameWith 'foo~'
+ expectToHaveError "can't contain '~'"
+
+ it "can't have caret anywhere", ->
+ fillNameWith '^foo'
+ expectToHaveError "can't contain '^'"
+ fillNameWith 'foo^bar'
+ expectToHaveError "can't contain '^'"
+ fillNameWith 'foo^'
+ expectToHaveError "can't contain '^'"
+
+ it "can't have : anywhere", ->
+ fillNameWith ':foo'
+ expectToHaveError "can't contain ':'"
+ fillNameWith 'foo:bar'
+ expectToHaveError "can't contain ':'"
+ fillNameWith ':foo'
+ expectToHaveError "can't contain ':'"
+
+ it "can't have question mark anywhere", ->
+ fillNameWith '?foo'
+ expectToHaveError "can't contain '?'"
+ fillNameWith 'foo?bar'
+ expectToHaveError "can't contain '?'"
+ fillNameWith 'foo?'
+ expectToHaveError "can't contain '?'"
+
+ it "can't have asterisk anywhere", ->
+ fillNameWith '*foo'
+ expectToHaveError "can't contain '*'"
+ fillNameWith 'foo*bar'
+ expectToHaveError "can't contain '*'"
+ fillNameWith 'foo*'
+ expectToHaveError "can't contain '*'"
+
+ it "can't have open bracket anywhere", ->
+ fillNameWith '[foo'
+ expectToHaveError "can't contain '['"
+ fillNameWith 'foo[bar'
+ expectToHaveError "can't contain '['"
+ fillNameWith 'foo['
+ expectToHaveError "can't contain '['"
+
+ it "can't have a backslash anywhere", ->
+ fillNameWith '\\foo'
+ expectToHaveError "can't contain '\\'"
+ fillNameWith 'foo\\bar'
+ expectToHaveError "can't contain '\\'"
+ fillNameWith 'foo\\'
+ expectToHaveError "can't contain '\\'"
+
+ it "can't contain a sequence @{ anywhere", ->
+ fillNameWith '@{foo'
+ expectToHaveError "can't contain '@{'"
+ fillNameWith 'foo@{bar'
+ expectToHaveError "can't contain '@{'"
+ fillNameWith 'foo@{'
+ expectToHaveError "can't contain '@{'"
+
+ it "can't have consecutive slashes", ->
+ fillNameWith 'foo//bar'
+ expectToHaveError "can't contain consecutive slashes"
+
+ it "can't end with a slash", ->
+ fillNameWith 'foo/'
+ expectToHaveError "can't end in '/'"
+
+ it "can't end with a dot", ->
+ fillNameWith 'foo.'
+ expectToHaveError "can't end in '.'"
+
+ it "can't end with .lock", ->
+ fillNameWith 'foo.lock'
+ expectToHaveError "can't end in '.lock'"
+
+ it "can't be the single character @", ->
+ fillNameWith '@'
+ expectToHaveError "can't be '@'"
+
+ it "concatenates all error messages", ->
+ fillNameWith '/foo bar?~.'
+ expectToHaveError "can't start with '/', can't contain spaces, '?', '~', can't end in '.'"
+
+ it "doesn't duplicate error messages", ->
+ fillNameWith '?foo?bar?zoo?'
+ expectToHaveError "can't contain '?'"
+
+ it "removes the error message when is a valid name", ->
+ fillNameWith 'foo?bar'
+ expect($('.js-branch-name-error span').length).toEqual(1)
+ fillNameWith 'foobar'
+ expect($('.js-branch-name-error span').length).toEqual(0)
+
+ it "can have dashes anywhere", ->
+ fillNameWith '-foo-bar-zoo-'
+ expect($('.js-branch-name-error span').length).toEqual(0)
+
+ it "can have underscores anywhere", ->
+ fillNameWith '_foo_bar_zoo_'
+ expect($('.js-branch-name-error span').length).toEqual(0)
+
+ it "can have numbers anywhere", ->
+ fillNameWith '1foo2bar3zoo4'
+ expect($('.js-branch-name-error span').length).toEqual(0)
+
+ it "can be only letters", ->
+ fillNameWith 'foo'
+ expect($('.js-branch-name-error span').length).toEqual(0)
diff --git a/spec/lib/gitlab/ldap/user_spec.rb b/spec/lib/gitlab/ldap/user_spec.rb
index 3bba5e2efa2..1e755259dae 100644
--- a/spec/lib/gitlab/ldap/user_spec.rb
+++ b/spec/lib/gitlab/ldap/user_spec.rb
@@ -42,6 +42,21 @@ describe Gitlab::LDAP::User, lib: true do
end
end
+ describe '.find_by_uid_and_provider' do
+ it 'retrieves the correct user' do
+ special_info = {
+ name: 'John Åström',
+ email: 'john@example.com',
+ nickname: 'jastrom'
+ }
+ special_hash = OmniAuth::AuthHash.new(uid: 'CN=John Åström,CN=Users,DC=Example,DC=com', provider: 'ldapmain', info: special_info)
+ special_chars_user = described_class.new(special_hash)
+ user = special_chars_user.save
+
+ expect(described_class.find_by_uid_and_provider(special_hash.uid, special_hash.provider)).to eq user
+ end
+ end
+
describe :find_or_create do
it "finds the user if already existing" do
create(:omniauth_user, extern_uid: 'my-uid', provider: 'ldapmain')
diff --git a/spec/models/global_milestone_spec.rb b/spec/models/global_milestone_spec.rb
index ba03e6aabd0..197c99cd007 100644
--- a/spec/models/global_milestone_spec.rb
+++ b/spec/models/global_milestone_spec.rb
@@ -62,4 +62,14 @@ describe GlobalMilestone, models: true do
expect(@global_milestone.milestones.count).to eq(3)
end
end
+
+ describe :safe_title do
+ let(:milestone) { create(:milestone, title: "git / test", project: project1) }
+
+ it 'should strip out slashes and spaces' do
+ global_milestone = GlobalMilestone.new(milestone.title, [milestone])
+
+ expect(global_milestone.safe_title).to eq('git-test')
+ end
+ end
end
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index c4d3813e9c9..400bdf2d962 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -552,4 +552,28 @@ describe Project, models: true do
end
end
end
+
+ describe '#visibility_level_allowed?' do
+ let(:project) { create :project, visibility_level: Gitlab::VisibilityLevel::INTERNAL }
+
+ context 'when checking on non-forked project' do
+ it { expect(project.visibility_level_allowed?(Gitlab::VisibilityLevel::PRIVATE)).to be_truthy }
+ it { expect(project.visibility_level_allowed?(Gitlab::VisibilityLevel::INTERNAL)).to be_truthy }
+ it { expect(project.visibility_level_allowed?(Gitlab::VisibilityLevel::PUBLIC)).to be_truthy }
+ end
+
+ context 'when checking on forked project' do
+ let(:forked_project) { create :forked_project_with_submodules }
+
+ before do
+ forked_project.build_forked_project_link(forked_to_project_id: forked_project.id, forked_from_project_id: project.id)
+ forked_project.save
+ end
+
+ it { expect(forked_project.visibility_level_allowed?(Gitlab::VisibilityLevel::PRIVATE)).to be_truthy }
+ it { expect(forked_project.visibility_level_allowed?(Gitlab::VisibilityLevel::INTERNAL)).to be_truthy }
+ it { expect(forked_project.visibility_level_allowed?(Gitlab::VisibilityLevel::PUBLIC)).to be_falsey }
+ end
+
+ end
end
diff --git a/spec/requests/api/branches_spec.rb b/spec/requests/api/branches_spec.rb
index 5c1b58535cc..36461e84c3a 100644
--- a/spec/requests/api/branches_spec.rb
+++ b/spec/requests/api/branches_spec.rb
@@ -118,7 +118,7 @@ describe API::API, api: true do
branch_name: 'new design',
ref: branch_sha
expect(response.status).to eq(400)
- expect(json_response['message']).to eq('Branch name invalid')
+ expect(json_response['message']).to eq('Branch name is invalid')
end
it 'should return 400 if branch already exists' do
diff --git a/spec/services/projects/update_service_spec.rb b/spec/services/projects/update_service_spec.rb
index c36d4581989..3c06a890163 100644
--- a/spec/services/projects/update_service_spec.rb
+++ b/spec/services/projects/update_service_spec.rb
@@ -100,6 +100,45 @@ describe Projects::UpdateService, services: true do
end
end
+ describe :visibility_level do
+ let(:user) { create :user, admin: true }
+ let(:project) { create :project, visibility_level: Gitlab::VisibilityLevel::INTERNAL }
+ let(:forked_project) { create :forked_project_with_submodules, visibility_level: Gitlab::VisibilityLevel::INTERNAL }
+ let(:opts) { {} }
+
+ before do
+ forked_project.build_forked_project_link(forked_to_project_id: forked_project.id, forked_from_project_id: project.id)
+ forked_project.save
+
+ @created_internal = project.internal?
+ @fork_created_internal = forked_project.internal?
+ end
+
+ context 'should update forks visibility level when parent set to more restrictive' do
+ before do
+ opts.merge!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
+ update_project(project, user, opts).inspect
+ end
+
+ it { expect(@created_internal).to be_truthy }
+ it { expect(@fork_created_internal).to be_truthy }
+ it { expect(project.private?).to be_truthy }
+ it { expect(project.forks.first.private?).to be_truthy }
+ end
+
+ context 'should not update forks visibility level when parent set to less restrictive' do
+ before do
+ opts.merge!(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
+ update_project(project, user, opts).inspect
+ end
+
+ it { expect(@created_internal).to be_truthy }
+ it { expect(@fork_created_internal).to be_truthy }
+ it { expect(project.public?).to be_truthy }
+ it { expect(project.forks.first.internal?).to be_truthy }
+ end
+ end
+
def update_project(project, user, opts)
Projects::UpdateService.new(project, user, opts).execute
end