Commit ef8b1dbf authored by Douwe Maan's avatar Douwe Maan

Merge branch 'master' into milestone-ref

parents 32543f3b e3befaed
Please view this file on the master branch, on stable branches it's out of date. Please view this file on the master branch, on stable branches it's out of date.
v 8.4.0 (unreleased) 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 new UI for group page
- Implement search inside emoji picker
- Add project permissions to all project API endpoints (Stan Hu) - Add project permissions to all project API endpoints (Stan Hu)
- Link to milestone in "Milestone changed" system note - 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 v 8.3.0
- Add CAS support (tduehr)
- Bump rack-attack to 4.3.1 for security fix (Stan Hu) - Bump rack-attack to 4.3.1 for security fix (Stan Hu)
- API support for starred projects for authorized user (Zeger-Jan van de Weg) - API support for starred projects for authorized user (Zeger-Jan van de Weg)
- Add link to merge request on build detail page.
- Add open_issues_count to project API (Stan Hu) - Add open_issues_count to project API (Stan Hu)
- Expand character set of usernames created by Omniauth (Corey Hinshaw) - 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) - 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 ...@@ -70,7 +78,6 @@ v 8.3.0
- Do not show build status unless builds are enabled and `.gitlab-ci.yml` is present - Do not show build status unless builds are enabled and `.gitlab-ci.yml` is present
- Persist runners registration token in database - Persist runners registration token in database
- Fix online editor should not remove newlines at the end of the file - 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 v 8.2.3
- Fix application settings cache not expiring after changes (Stan Hu) - Fix application settings cache not expiring after changes (Stan Hu)
......
...@@ -10,6 +10,9 @@ class @AwardsHandler ...@@ -10,6 +10,9 @@ class @AwardsHandler
if $(".emoji-menu").is(":visible") if $(".emoji-menu").is(":visible")
$(".emoji-menu").hide() $(".emoji-menu").hide()
@renderFrequentlyUsedBlock()
@setupSearch()
addAward: (emoji) -> addAward: (emoji) ->
emoji = @normilizeEmojiName(emoji) emoji = @normilizeEmojiName(emoji)
@postEmoji emoji, => @postEmoji emoji, =>
...@@ -18,6 +21,8 @@ class @AwardsHandler ...@@ -18,6 +21,8 @@ class @AwardsHandler
$(".emoji-menu").hide() $(".emoji-menu").hide()
addAwardToEmojiBar: (emoji) -> addAwardToEmojiBar: (emoji) ->
@addEmojiToFrequentlyUsedList(emoji)
emoji = @normilizeEmojiName(emoji) emoji = @normilizeEmojiName(emoji)
if @exist(emoji) if @exist(emoji)
if @isActive(emoji) if @isActive(emoji)
...@@ -114,3 +119,46 @@ class @AwardsHandler ...@@ -114,3 +119,46 @@ class @AwardsHandler
normilizeEmojiName: (emoji) -> normilizeEmojiName: (emoji) ->
@aliases[emoji] || emoji @aliases[emoji] || emoji
addEmojiToFrequentlyUsedList: (emoji) ->
frequently_used_emojis = @getFrequentlyUsedEmojis()
frequently_used_emojis.push(emoji)
$.cookie('frequently_used_emojis', frequently_used_emojis.join(","), { expires: 365 })
getFrequentlyUsedEmojis: ->
frequently_used_emojis = ($.cookie('frequently_used_emojis') || "").split(",")
frequently_used_emojis = ["thumbsup", "thumbsdown"].concat(frequently_used_emojis)
_.compact(_.uniq(frequently_used_emojis))
renderFrequentlyUsedBlock: ->
frequently_used_emojis = @getFrequentlyUsedEmojis()
ul = $("<ul>")
for emoji in frequently_used_emojis
do (emoji) ->
$(".emoji-menu-content [data-emoji='#{emoji}']").closest("li").clone().appendTo(ul)
$("input.emoji-search").after(ul).after($("<h5>").text("Frequently used"))
setupSearch: ->
$("input.emoji-search").keyup (ev) =>
term = $(ev.target).val()
# Clean previous search results
$("ul.emoji-search,h5.emoji-search").remove()
if term
# Generate a search result block
h5 = $("<h5>").text("Search results").addClass("emoji-search")
found_emojis = @searchEmojis(term).show()
ul = $("<ul>").addClass("emoji-search").append(found_emojis)
$(".emoji-menu-content ul, .emoji-menu-content h5").hide()
$(".emoji-menu-content").append(h5).append(ul)
else
$(".emoji-menu-content").children().show()
searchEmojis: (term)->
$(".emoji-menu-content [data-emoji*='#{term}']").closest("li").clone()
...@@ -35,7 +35,7 @@ class @BlobFileDropzone ...@@ -35,7 +35,7 @@ class @BlobFileDropzone
return return
this.on 'sending', (file, xhr, formData) -> this.on 'sending', (file, xhr, formData) ->
formData.append('new_branch', form.find('.js-new-branch').val()) formData.append('target_branch', form.find('.js-target-branch').val())
formData.append('create_merge_request', form.find('.js-create-merge-request').val()) formData.append('create_merge_request', form.find('.js-create-merge-request').val())
formData.append('commit_message', form.find('.js-commit-message').val()) formData.append('commit_message', form.find('.js-commit-message').val())
return return
......
...@@ -18,7 +18,7 @@ class @MergeRequestWidget ...@@ -18,7 +18,7 @@ class @MergeRequestWidget
if data.state == "merged" if data.state == "merged"
urlSuffix = if deleteSourceBranch then '?delete_source=true' else '' urlSuffix = if deleteSourceBranch then '?delete_source=true' else ''
window.location.href = window.location.href + urlSuffix window.location.href = window.location.pathname + urlSuffix
else if data.merge_error else if data.merge_error
$('.mr-widget-body').html("<h4>" + data.merge_error + "</h4>") $('.mr-widget-body').html("<h4>" + data.merge_error + "</h4>")
else else
......
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)
class @NewCommitForm class @NewCommitForm
constructor: (form) -> constructor: (form) ->
@newBranch = form.find('.js-new-branch') @newBranch = form.find('.js-target-branch')
@originalBranch = form.find('.js-original-branch') @originalBranch = form.find('.js-original-branch')
@createMergeRequest = form.find('.js-create-merge-request') @createMergeRequest = form.find('.js-create-merge-request')
@createMergeRequestContainer = form.find('.js-create-merge-request-container') @createMergeRequestContainer = form.find('.js-create-merge-request-container')
......
class @Project class @Project
constructor: -> constructor: ->
# Git protocol switcher # Git protocol switcher
$('.js-protocol-switch').click -> $('ul.clone-options-dropdown a').click ->
return if $(@).hasClass('active') return if $(@).hasClass('active')
...@@ -10,7 +10,8 @@ class @Project ...@@ -10,7 +10,8 @@ class @Project
# Add the active class for the clicked button # Add the active class for the clicked button
$(@).toggleClass('active') $(@).toggleClass('active')
url = $(@).data('clone') url = $("#project_clone").val()
console.log("url",url)
# Update the input field # Update the input field
$('#project_clone').val(url) $('#project_clone').val(url)
......
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
...@@ -101,6 +101,10 @@ ...@@ -101,6 +101,10 @@
overflow: auto; overflow: auto;
} }
input.emoji-search{
background: image-url("icon-search.png") 240px no-repeat;
}
li { li {
cursor: pointer; cursor: pointer;
width: 30px; width: 30px;
......
...@@ -4,7 +4,7 @@ The source: gemojione gem. ...@@ -4,7 +4,7 @@ The source: gemojione gem.
*/ */
.emoji-icon{ .emoji-icon{
background-image: url(emoji.png); background-image: image-url("emoji.png");
background-repeat: no-repeat; background-repeat: no-repeat;
} }
......
...@@ -91,21 +91,83 @@ ...@@ -91,21 +91,83 @@
} }
} }
.input-group { .git-clone-holder {
display: inline-table; display: inline-table;
position: relative; position: relative;
top: 17px;
} }
.project-repo-buttons { .project-repo-buttons {
margin-top: 12px; margin-top: 12px;
margin-bottom: 0px; margin-bottom: 0px;
.count-buttons {
display: block;
margin-bottom: 12px;
}
.btn { .btn {
@include btn-gray; @include btn-gray;
text-transform: none;
}
.count-with-arrow {
display: inline-block;
position: relative;
margin-left: 4px;
.arrow {
&:before {
content: '';
display: inline-block;
position: absolute;
width: 0;
height: 0;
border-color: transparent;
border-style: solid;
top: 50%;
left: 0;
margin-top: -6px;
border-width: 7px 5px 7px 0;
border-right-color: #dce0e5;
}
&:after {
content: '';
position: absolute;
width: 0;
height: 0;
border-color: transparent;
border-style: solid;
top: 50%;
left: 1px;
margin-top: -9px;
border-width: 10px 7px 10px 0;
border-right-color: #FFF;
}
}
.count { .count {
@include btn-gray;
display: inline-block; display: inline-block;
background: white;
border-radius: 2px;
border-width: 1px;
border-style: solid;
font-size: 13px;
font-weight: 600;
line-height: 20px;
padding: 11px 16px;
letter-spacing: .4px;
padding: 10px;
text-align: center;
vertical-align: middle;
touch-action: manipulation;
cursor: pointer;
background-image: none;
white-space: nowrap;
margin: 0 11px 0px 4px;
&:hover {
background: #FFF;
}
} }
} }
} }
...@@ -125,6 +187,13 @@ ...@@ -125,6 +187,13 @@
margin-right: 45px; margin-right: 45px;
} }
.clone-options {
display: table-cell;
a.btn {
width: 100%;
}
}
.form-control { .form-control {
cursor: auto; cursor: auto;
@extend .monospace; @extend .monospace;
...@@ -337,6 +406,8 @@ ul.nav.nav-projects-tabs { ...@@ -337,6 +406,8 @@ ul.nav.nav-projects-tabs {
.top-area { .top-area {
border-bottom: 1px solid #EEE; border-bottom: 1px solid #EEE;
margin: 0 -16px;
padding: 0 $gl-padding;
ul.left-top-menu { ul.left-top-menu {
display: inline-block; display: inline-block;
......
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
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
# Controller for viewing a file's blame # Controller for viewing a file's blame
class Projects::BlobController < Projects::ApplicationController class Projects::BlobController < Projects::ApplicationController
include ExtractsPath include ExtractsPath
include CreatesMergeRequestForCommit include CreatesCommit
include ActionView::Helpers::SanitizeHelper include ActionView::Helpers::SanitizeHelper
# Raised when given an invalid file path # Raised when given an invalid file path
...@@ -9,21 +9,21 @@ class Projects::BlobController < Projects::ApplicationController ...@@ -9,21 +9,21 @@ class Projects::BlobController < Projects::ApplicationController
before_action :require_non_empty_project, except: [:new, :create] before_action :require_non_empty_project, except: [:new, :create]
before_action :authorize_download_code! before_action :authorize_download_code!
before_action :authorize_push_code!, only: [:destroy, :create] before_action :authorize_edit_tree!, only: [:new, :create, :edit, :update, :destroy]
before_action :assign_blob_vars before_action :assign_blob_vars
before_action :commit, except: [:new, :create] before_action :commit, except: [:new, :create]
before_action :blob, except: [:new, :create] before_action :blob, except: [:new, :create]
before_action :from_merge_request, only: [:edit, :update] before_action :from_merge_request, only: [:edit, :update]
before_action :require_branch_head, only: [:edit, :update] before_action :require_branch_head, only: [:edit, :update]
before_action :editor_variables, except: [:show, :preview, :diff] before_action :editor_variables, except: [:show, :preview, :diff]
before_action :after_edit_path, only: [:edit, :update]
def new def new
commit unless @repository.empty? commit unless @repository.empty?
end end
def create def create
create_commit(Files::CreateService, success_path: after_create_path, create_commit(Files::CreateService, success_notice: "The file has been successfully created.",
success_path: namespace_project_blob_path(@project.namespace, @project, File.join(@target_branch, @file_path)),
failure_view: :new, failure_view: :new,
failure_path: namespace_project_new_blob_path(@project.namespace, @project, @ref)) failure_path: namespace_project_new_blob_path(@project.namespace, @project, @ref))
end end
...@@ -36,6 +36,14 @@ class Projects::BlobController < Projects::ApplicationController ...@@ -36,6 +36,14 @@ class Projects::BlobController < Projects::ApplicationController
end end
def update def update
after_edit_path =
if from_merge_request && @target_branch == @ref
diffs_namespace_project_merge_request_path(from_merge_request.target_project.namespace, from_merge_request.target_project, from_merge_request) +
"#file-path-#{hexdigest(@path)}"
else
namespace_project_blob_path(@project.namespace, @project, File.join(@target_branch, @path))
end
create_commit(Files::UpdateService, success_path: after_edit_path, create_commit(Files::UpdateService, success_path: after_edit_path,
failure_view: :edit, failure_view: :edit,
failure_path: namespace_project_blob_path(@project.namespace, @project, @id)) failure_path: namespace_project_blob_path(@project.namespace, @project, @id))
...@@ -50,15 +58,10 @@ class Projects::BlobController < Projects::ApplicationController ...@@ -50,15 +58,10 @@ class Projects::BlobController < Projects::ApplicationController
end end
def destroy def destroy
result = Files::DeleteService.new(@project, current_user, @commit_params).execute create_commit(Files::DeleteService, success_notice: "The file has been successfully deleted.",
success_path: namespace_project_tree_path(@project.namespace, @project, @target_branch),
if result[:status] == :success failure_view: :show,
flash[:notice] = "Your changes have been successfully committed" failure_path: namespace_project_blob_path(@project.namespace, @project, @id))
redirect_to after_destroy_path
else
flash[:alert] = result[:message]
render :show
end
end end
def diff def diff
...@@ -108,74 +111,13 @@ class Projects::BlobController < Projects::ApplicationController ...@@ -108,74 +111,13 @@ class Projects::BlobController < Projects::ApplicationController
render_404 render_404
end end
def create_commit(service, success_path:, failure_view:, failure_path:)
result = service.new(@project, current_user, @commit_params).execute
if result[:status] == :success
flash[:notice] = "Your changes have been successfully committed"
respond_to do |format|
format.html { redirect_to success_path }
format.json { render json: { message: "success", filePath: success_path } }
end
else
flash[:alert] = result[:message]
respond_to do |format|
format.html { render failure_view }
format.json { render json: { message: "failed", filePath: failure_path } }
end
end
end
def after_create_path
@after_create_path ||=
if create_merge_request?
new_merge_request_path
else
namespace_project_blob_path(@project.namespace, @project, File.join(@new_branch, @file_path))
end
end
def after_edit_path
@after_edit_path ||=
if create_merge_request?
new_merge_request_path
elsif from_merge_request && @new_branch == @ref
diffs_namespace_project_merge_request_path(from_merge_request.target_project.namespace, from_merge_request.target_project, from_merge_request) +
"#file-path-#{hexdigest(@path)}"
else
namespace_project_blob_path(@project.namespace, @project, File.join(@new_branch, @path))
end
end
def after_destroy_path
@after_destroy_path ||=
if create_merge_request?
new_merge_request_path
else
namespace_project_tree_path(@project.namespace, @project, @new_branch)
end
end
def from_merge_request def from_merge_request
# If blob edit was initiated from merge request page # If blob edit was initiated from merge request page
@from_merge_request ||= MergeRequest.find_by(id: params[:from_merge_request_id]) @from_merge_request ||= MergeRequest.find_by(id: params[:from_merge_request_id])
end end
def sanitized_new_branch_name
sanitize(strip_tags(params[:new_branch]))
end
def editor_variables def editor_variables
@current_branch = @ref @target_branch = params[:target_branch]
@new_branch =
if params[:new_branch].present?
sanitized_new_branch_name
elsif ::Gitlab::GitAccess.new(current_user, @project).can_push_to_branch?(@ref)
@ref
else
@repository.next_patch_branch
end
@file_path = @file_path =
if action_name.to_s == 'create' if action_name.to_s == 'create'
...@@ -194,8 +136,6 @@ class Projects::BlobController < Projects::ApplicationController ...@@ -194,8 +136,6 @@ class Projects::BlobController < Projects::ApplicationController
@commit_params = { @commit_params = {
file_path: @file_path, file_path: @file_path,
current_branch: @current_branch,
target_branch: @new_branch,
commit_message: params[:commit_message], commit_message: params[:commit_message],
file_content: params[:content], file_content: params[:content],
file_content_encoding: params[:encoding] file_content_encoding: params[:encoding]
......
...@@ -10,19 +10,35 @@ class Projects::ForksController < Projects::ApplicationController ...@@ -10,19 +10,35 @@ class Projects::ForksController < Projects::ApplicationController
def create def create
namespace = Namespace.find(params[:namespace_key]) namespace = Namespace.find(params[:namespace_key])
@forked_project = ::Projects::ForkService.new(project, current_user, namespace: namespace).execute
@forked_project = namespace.projects.find_by(path: project.path)
@forked_project = nil unless @forked_project && @forked_project.forked_from_project == project
@forked_project ||= ::Projects::ForkService.new(project, current_user, namespace: namespace).execute
if @forked_project.saved? && @forked_project.forked? if @forked_project.saved? && @forked_project.forked?
if @forked_project.import_in_progress? if @forked_project.import_in_progress?
redirect_to namespace_project_import_path(@forked_project.namespace, @forked_project) redirect_to namespace_project_import_path(@forked_project.namespace, @forked_project, continue: continue_params)
else else
redirect_to( if continue_params
namespace_project_path(@forked_project.namespace, @forked_project), redirect_to continue_params[:to], notice: continue_params[:notice]
notice: 'Project was successfully forked.' else
) redirect_to namespace_project_path(@forked_project.namespace, @forked_project), notice: "The project was successfully forked."
end
end end
else else
render :error render :error
end end
end end
private
def continue_params
continue_params = params[:continue]
if continue_params
continue_params.permit(:to, :notice, :notice_now)
else
nil
end
end
end end
class Projects::ImportsController < Projects::ApplicationController class Projects::ImportsController < Projects::ApplicationController
# Authorize # Authorize
before_action :authorize_admin_project! before_action :authorize_admin_project!
before_action :require_no_repo before_action :require_no_repo, except: :show
before_action :redirect_if_progress, except: :show before_action :redirect_if_progress, except: :show
def new def new
...@@ -24,21 +24,36 @@ class Projects::ImportsController < Projects::ApplicationController ...@@ -24,21 +24,36 @@ class Projects::ImportsController < Projects::ApplicationController
end end
def show def show
unless @project.import_in_progress? if @project.repository_exists? || @project.import_finished?
if @project.import_finished? if continue_params
redirect_to(project_path(@project)) and return redirect_to continue_params[:to], notice: continue_params[:notice]
else else
redirect_to(new_namespace_project_import_path(@project.namespace, redirect_to project_path(@project), notice: "The project was successfully forked."
@project)) and return
end end
elsif @project.import_failed?
redirect_to new_namespace_project_import_path(@project.namespace, @project)
else
if continue_params && continue_params[:notice_now]
flash.now[:notice] = continue_params[:notice_now]
end
# Render
end end
end end
private private
def continue_params
continue_params = params[:continue]
if continue_params
continue_params.permit(:to, :notice, :notice_now)
else
nil
end
end
def require_no_repo def require_no_repo
if @project.repository_exists? && !@project.import_in_progress? if @project.repository_exists? && !@project.import_in_progress?
redirect_to(namespace_project_path(@project.namespace, @project)) and return redirect_to(namespace_project_path(@project.namespace, @project))
end end
end end
......
# Controller for viewing a repository's file structure # Controller for viewing a repository's file structure
class Projects::TreeController < Projects::ApplicationController class Projects::TreeController < Projects::ApplicationController
include ExtractsPath include ExtractsPath
include CreatesMergeRequestForCommit include CreatesCommit
include ActionView::Helpers::SanitizeHelper include ActionView::Helpers::SanitizeHelper
before_action :require_non_empty_project, except: [:new, :create] before_action :require_non_empty_project, except: [:new, :create]
before_action :assign_ref_vars before_action :assign_ref_vars
before_action :assign_dir_vars, only: [:create_dir] before_action :assign_dir_vars, only: [:create_dir]
before_action :authorize_download_code! before_action :authorize_download_code!
before_action :authorize_push_code!, only: [:create_dir] before_action :authorize_edit_tree!, only: [:create_dir]
def show def show
return render_404 unless @repository.commit(@ref) return render_404 unless @repository.commit(@ref)
...@@ -34,44 +34,20 @@ class Projects::TreeController < Projects::ApplicationController ...@@ -34,44 +34,20 @@ class Projects::TreeController < Projects::ApplicationController
def create_dir def create_dir
return render_404 unless @commit_params.values.all? return render_404 unless @commit_params.values.all?
begin create_commit(Files::CreateDirService, success_notice: "The directory has been successfully created.",
result = Files::CreateDirService.new(@project, current_user, @commit_params).execute success_path: namespace_project_tree_path(@project.namespace, @project, File.join(@target_branch, @dir_name)),
message = result[:message] failure_path: namespace_project_tree_path(@project.namespace, @project, @ref))
rescue => e
message = e.to_s
end
if result && result[:status] == :success
flash[:notice] = "The directory has been successfully created"
respond_to do |format|
format.html { redirect_to after_create_dir_path }
end
else
flash[:alert] = message
respond_to do |format|
format.html { redirect_to namespace_project_blob_path(@project.namespace, @project, @new_branch) }
end
end
end end
private private
def assign_dir_vars def assign_dir_vars
@new_branch = params[:new_branch].present? ? sanitize(strip_tags(params[:new_branch])) : @ref @target_branch = params[:target_branch]
@dir_name = File.join(@path, params[:dir_name]) @dir_name = File.join(@path, params[:dir_name])
@commit_params = { @commit_params = {
file_path: @dir_name, file_path: @dir_name,
current_branch: @ref,
target_branch: @new_branch,
commit_message: params[:commit_message], commit_message: params[:commit_message],
} }
end end
def after_create_dir_path
if create_merge_request?
new_merge_request_path
else
namespace_project_blob_path(@project.namespace, @project, File.join(@new_branch, @dir_name))
end
end
end end
...@@ -171,7 +171,7 @@ class ProjectsController < ApplicationController ...@@ -171,7 +171,7 @@ class ProjectsController < ApplicationController
@project.reload @project.reload
render json: { render json: {
html: view_to_html_string("projects/buttons/_star") star_count: @project.star_count
} }
end end
......
...@@ -22,32 +22,90 @@ module BlobHelper ...@@ -22,32 +22,90 @@ module BlobHelper
%w(credits changelog news copying copyright license authors) %w(credits changelog news copying copyright license authors)
end end
def edit_blob_link(project, ref, path, options = {}) def edit_blob_link(project = @project, ref = @ref, path = @path, options = {})
blob = return unless current_user
begin
project.repository.blob_at(ref, path) blob = project.repository.blob_at(ref, path) rescue nil
rescue
nil return unless blob && blob_text_viewable?(blob)
end
return unless blob && blob.text? && blob_editable?(blob)
text = 'Edit'
after = options[:after] || ''
from_mr = options[:from_merge_request_id] from_mr = options[:from_merge_request_id]
link_opts = {} link_opts = {}
link_opts[:from_merge_request_id] = from_mr if from_mr link_opts[:from_merge_request_id] = from_mr if from_mr
cls = 'btn btn-small'
link_to(text, edit_path = namespace_project_edit_blob_path(project.namespace, project,
namespace_project_edit_blob_path(project.namespace, project, tree_join(ref, path),
tree_join(ref, path), link_opts)
link_opts),
class: cls if !on_top_of_branch?
) + after.html_safe button_tag "Edit", class: "btn btn-default disabled has_tooltip", title: "You can only edit files when you are on a branch", data: { container: 'body' }
elsif can_edit_blob?(blob)
link_to "Edit", edit_path, class: 'btn btn-small'
elsif can?(current_user, :fork_project, project)
continue_params = {
to: edit_path,
notice: edit_in_new_fork_notice,
notice_now: edit_in_new_fork_notice_now
}
fork_path = namespace_project_fork_path(project.namespace, project, namespace_key: current_user.namespace.id,
continue: continue_params)
link_to "Edit", fork_path, class: 'btn btn-small', method: :post
end
end
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 end
def blob_editable?(blob, project = @project, ref = @ref) def can_edit_blob?(blob, project = @project, ref = @ref)
!blob.lfs_pointer? && allowed_tree_edit?(project, ref) !blob.lfs_pointer? && can_edit_tree?(project, ref)
end end
def leave_edit_message def leave_edit_message
...@@ -70,7 +128,7 @@ module BlobHelper ...@@ -70,7 +128,7 @@ module BlobHelper
icon("#{file_type_icon_class('file', mode, name)} fw") icon("#{file_type_icon_class('file', mode, name)} fw")
end end
def blob_viewable?(blob) def blob_text_viewable?(blob)
blob && blob.text? && !blob.lfs_pointer? blob && blob.text? && !blob.lfs_pointer?
end end
......
...@@ -50,24 +50,49 @@ module TreeHelper ...@@ -50,24 +50,49 @@ module TreeHelper
project.repository.branch_names.include?(ref) project.repository.branch_names.include?(ref)
end end
def allowed_tree_edit?(project = nil, ref = nil) def can_edit_tree?(project = nil, ref = nil)
project ||= @project project ||= @project
ref ||= @ref ref ||= @ref
return false unless on_top_of_branch?(project, ref) return false unless on_top_of_branch?(project, ref)
can?(current_user, :push_code, project) can?(current_user, :push_code, project) ||
(current_user && current_user.already_forked?(project))
end end
def tree_edit_branch(project = @project, ref = @ref) def tree_edit_branch(project = @project, ref = @ref)
if allowed_tree_edit?(project, ref) return unless can_edit_tree?(project, ref)
if can_push_branch?(project, ref)
ref if can_push_branch?(project, ref)
else ref
project.repository.next_patch_branch else
end project = tree_edit_project(project)
project.repository.next_patch_branch
end
end
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
end end
def edit_in_new_fork_notice_now
"You're not allowed to make changes to this project directly." +
" A fork of this project is being created that you can make changes in, so you can submit a merge request."
end
def edit_in_new_fork_notice
"You're not allowed to make changes to this project directly." +
" A fork of this project has been created that you can make changes in, so you can submit a merge request."
end
def commit_in_fork_help
"A new branch will be created in your fork and a new merge request will be started."
end
def tree_breadcrumbs(tree, max_links = 2) def tree_breadcrumbs(tree, max_links = 2)
if @path.present? if @path.present?
part_path = "" part_path = ""
......
...@@ -69,7 +69,6 @@ module VisibilityLevelHelper ...@@ -69,7 +69,6 @@ module VisibilityLevelHelper
def skip_level?(form_model, level) def skip_level?(form_model, level)
form_model.is_a?(Project) && form_model.is_a?(Project) &&
form_model.forked? && !form_model.visibility_level_allowed?(level)
!Gitlab::VisibilityLevel.allowed_fork_levels(form_model.forked_from_project.visibility_level).include?(level)
end end
end end
...@@ -132,14 +132,14 @@ class Ability ...@@ -132,14 +132,14 @@ class Ability
end end
def public_project_rules def public_project_rules
project_guest_rules + [ @public_project_rules ||= project_guest_rules + [
:download_code, :download_code,
:fork_project :fork_project
] ]
end end
def project_guest_rules def project_guest_rules
[ @project_guest_rules ||= [
:read_project, :read_project,
:read_wiki, :read_wiki,
:read_issue, :read_issue,
...@@ -157,7 +157,7 @@ class Ability ...@@ -157,7 +157,7 @@ class Ability
end end
def project_report_rules def project_report_rules
project_guest_rules + [ @project_report_rules ||= project_guest_rules + [
:create_commit_status, :create_commit_status,
:read_commit_statuses, :read_commit_statuses,
:download_code, :download_code,
...@@ -170,7 +170,7 @@ class Ability ...@@ -170,7 +170,7 @@ class Ability
end end
def project_dev_rules def project_dev_rules
project_report_rules + [ @project_dev_rules ||= project_report_rules + [
:admin_merge_request, :admin_merge_request,
:create_merge_request, :create_merge_request,
:create_wiki, :create_wiki,
...@@ -181,7 +181,7 @@ class Ability ...@@ -181,7 +181,7 @@ class Ability
end end
def project_archived_rules def project_archived_rules
[ @project_archived_rules ||= [
:create_merge_request, :create_merge_request,
:push_code, :push_code,
:push_code_to_protected_branches, :push_code_to_protected_branches,
...@@ -191,7 +191,7 @@ class Ability ...@@ -191,7 +191,7 @@ class Ability
end end
def project_master_rules def project_master_rules
project_dev_rules + [ @project_master_rules ||= project_dev_rules + [
:push_code_to_protected_branches, :push_code_to_protected_branches,
:update_project_snippet, :update_project_snippet,
:update_merge_request, :update_merge_request,
...@@ -206,7 +206,7 @@ class Ability ...@@ -206,7 +206,7 @@ class Ability
end end
def project_admin_rules def project_admin_rules
project_master_rules + [ @project_admin_rules ||= project_master_rules + [
:change_namespace, :change_namespace,
:change_visibility_level, :change_visibility_level,
:rename_project, :rename_project,
...@@ -332,7 +332,7 @@ class Ability ...@@ -332,7 +332,7 @@ class Ability
end end
if snippet.public? || snippet.internal? if snippet.public? || snippet.internal?
rules << :read_personal_snippet rules << :read_personal_snippet
end end
rules rules
......
...@@ -16,7 +16,7 @@ class GlobalMilestone ...@@ -16,7 +16,7 @@ class GlobalMilestone
end end
def safe_title def safe_title
@title.to_slug.to_s @title.to_slug.normalize.to_s
end end
def expired? def expired?
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
class Identity < ActiveRecord::Base class Identity < ActiveRecord::Base
include Sortable include Sortable
include CaseSensitivity
belongs_to :user belongs_to :user
validates :provider, presence: true validates :provider, presence: true
......
...@@ -64,6 +64,19 @@ class Project < ActiveRecord::Base ...@@ -64,6 +64,19 @@ class Project < ActiveRecord::Base
update_column(:last_activity_at, self.created_at) update_column(:last_activity_at, self.created_at)
end end
# update visibility_levet of forks
after_update :update_forks_visibility_level
def update_forks_visibility_level
return unless visibility_level < visibility_level_was
forks.each do |forked_project|
if forked_project.visibility_level > visibility_level
forked_project.visibility_level = visibility_level
forked_project.save!
end
end
end
ActsAsTaggableOn.strict_case_match = true ActsAsTaggableOn.strict_case_match = true
acts_as_taggable_on :tags acts_as_taggable_on :tags
...@@ -100,9 +113,12 @@ class Project < ActiveRecord::Base ...@@ -100,9 +113,12 @@ class Project < ActiveRecord::Base
has_one :gitlab_issue_tracker_service, dependent: :destroy has_one :gitlab_issue_tracker_service, dependent: :destroy
has_one :external_wiki_service, dependent: :destroy has_one :external_wiki_service, dependent: :destroy
has_one :forked_project_link, dependent: :destroy, foreign_key: "forked_to_project_id" has_one :forked_project_link, dependent: :destroy, foreign_key: "forked_to_project_id"
has_one :forked_from_project, through: :forked_project_link
has_many :forked_project_links, foreign_key: "forked_from_project_id"
has_many :forks, through: :forked_project_links, source: :forked_to_project
has_one :forked_from_project, through: :forked_project_link
# Merge Requests for target project should be removed with it # Merge Requests for target project should be removed with it
has_many :merge_requests, dependent: :destroy, foreign_key: 'target_project_id' has_many :merge_requests, dependent: :destroy, foreign_key: 'target_project_id'
# Merge requests from source project should be kept when source project was removed # Merge requests from source project should be kept when source project was removed
...@@ -768,7 +784,7 @@ class Project < ActiveRecord::Base ...@@ -768,7 +784,7 @@ class Project < ActiveRecord::Base
end end
def forks_count def forks_count
ForkedProjectLink.where(forked_from_project_id: self.id).count forks.count
end end
def find_label(name) def find_label(name)
...@@ -862,4 +878,9 @@ class Project < ActiveRecord::Base ...@@ -862,4 +878,9 @@ class Project < ActiveRecord::Base
def open_issues_count def open_issues_count
issues.opened.count issues.opened.count
end end
def visibility_level_allowed?(level)
return true unless forked?
Gitlab::VisibilityLevel.allowed_fork_levels(forked_from_project.visibility_level).include?(level.to_i)
end
end end
...@@ -592,47 +592,54 @@ class Repository ...@@ -592,47 +592,54 @@ class Repository
Gitlab::Popen.popen(args, path_to_repo) Gitlab::Popen.popen(args, path_to_repo)
end end
def commit_with_hooks(current_user, branch) def with_tmp_ref(oldrev = nil)
oldrev = Gitlab::Git::BLANK_SHA
ref = Gitlab::Git::BRANCH_REF_PREFIX + branch
was_empty = empty?
# Create temporary ref
random_string = SecureRandom.hex random_string = SecureRandom.hex
tmp_ref = "refs/tmp/#{random_string}/head" tmp_ref = "refs/tmp/#{random_string}/head"
unless was_empty if oldrev && !Gitlab::Git.blank_ref?(oldrev)
oldrev = find_branch(branch).target
rugged.references.create(tmp_ref, oldrev) rugged.references.create(tmp_ref, oldrev)
end end
# Make commit in tmp ref # 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 unless was_empty
raise CommitError.new('Failed to create commit') oldrev = find_branch(branch).target
end end
GitHooksService.new.execute(current_user, path_to_repo, oldrev, newrev, ref) do with_tmp_ref(oldrev) do |tmp_ref|
if was_empty # Make commit in tmp ref
# Create branch newrev = yield(tmp_ref)
rugged.references.create(ref, newrev)
else unless newrev
# Update head raise CommitError.new('Failed to create commit')
current_head = find_branch(branch).target end
# Make sure target branch was not changed during pre-receive hook GitHooksService.new.execute(current_user, path_to_repo, oldrev, newrev, ref) do
if current_head == oldrev if was_empty
rugged.references.update(ref, newrev) # Create branch
rugged.references.create(ref, newrev)
else else
raise CommitError.new('Commit was rejected because branch received new push') # Update head
current_head = find_branch(branch).target
# Make sure target branch was not changed during pre-receive hook
if current_head == oldrev
rugged.references.update(ref, newrev)
else
raise CommitError.new('Commit was rejected because branch received new push')
end
end end
end end
end end
rescue GitHooksService::PreReceiveError
# Remove tmp ref and return error to user
rugged.references.delete(tmp_ref)
raise
end end
private private
......
...@@ -39,10 +39,7 @@ class BaseService ...@@ -39,10 +39,7 @@ class BaseService
def deny_visibility_level(model, denied_visibility_level = nil) def deny_visibility_level(model, denied_visibility_level = nil)
denied_visibility_level ||= model.visibility_level denied_visibility_level ||= model.visibility_level
level_name = 'Unknown' level_name = Gitlab::VisibilityLevel.level_name(denied_visibility_level)
Gitlab::VisibilityLevel.options.each do |name, level|
level_name = name if level == denied_visibility_level
end
model.errors.add( model.errors.add(
:visibility_level, :visibility_level,
......
require_relative 'base_service' require_relative 'base_service'
class CreateBranchService < BaseService class CreateBranchService < BaseService
def execute(branch_name, ref) def execute(branch_name, ref, source_project: @project)
valid_branch = Gitlab::GitRefValidator.validate(branch_name) valid_branch = Gitlab::GitRefValidator.validate(branch_name)
if valid_branch == false if valid_branch == false
return error('Branch name invalid') return error('Branch name is invalid')
end end
repository = project.repository repository = project.repository
...@@ -13,7 +13,20 @@ class CreateBranchService < BaseService ...@@ -13,7 +13,20 @@ class CreateBranchService < BaseService
return error('Branch already exists') return error('Branch already exists')
end end
new_branch = repository.add_branch(current_user, branch_name, ref) new_branch = nil
if source_project != @project
repository.with_tmp_ref do |tmp_ref|
repository.fetch_ref(
source_project.repository.path_to_repo,
"refs/heads/#{ref}",
tmp_ref
)
new_branch = repository.add_branch(current_user, branch_name, tmp_ref)
end
else
new_branch = repository.add_branch(current_user, branch_name, ref)
end
if new_branch if new_branch
push_data = build_push_data(project, current_user, new_branch) push_data = build_push_data(project, current_user, new_branch)
......
...@@ -3,8 +3,10 @@ module Files ...@@ -3,8 +3,10 @@ module Files
class ValidationError < StandardError; end class ValidationError < StandardError; end
def execute def execute
@current_branch = params[:current_branch] @source_project = params[:source_project] || @project
@source_branch = params[:source_branch]
@target_branch = params[:target_branch] @target_branch = params[:target_branch]
@commit_message = params[:commit_message] @commit_message = params[:commit_message]
@file_path = params[:file_path] @file_path = params[:file_path]
@file_content = if params[:file_content_encoding] == 'base64' @file_content = if params[:file_content_encoding] == 'base64'
...@@ -16,8 +18,8 @@ module Files ...@@ -16,8 +18,8 @@ module Files
# Validate parameters # Validate parameters
validate validate
# Create new branch if it different from current_branch # Create new branch if it different from source_branch
if @target_branch != @current_branch if different_branch?
create_target_branch create_target_branch
end end
...@@ -26,18 +28,14 @@ module Files ...@@ -26,18 +28,14 @@ module Files
else else
error("Something went wrong. Your changes were not committed") error("Something went wrong. Your changes were not committed")
end end
rescue Repository::CommitError, GitHooksService::PreReceiveError, ValidationError => ex rescue Repository::CommitError, Gitlab::Git::Repository::InvalidBlobName, GitHooksService::PreReceiveError, ValidationError => ex
error(ex.message) error(ex.message)
end end
private private
def current_branch def different_branch?
@current_branch ||= params[:current_branch] @source_branch != @target_branch || @source_project != @project
end
def target_branch
@target_branch ||= params[:target_branch]
end end
def raise_error(message) def raise_error(message)
...@@ -52,11 +50,11 @@ module Files ...@@ -52,11 +50,11 @@ module Files
end end
unless project.empty_repo? unless project.empty_repo?
unless repository.branch_names.include?(@current_branch) unless @source_project.repository.branch_names.include?(@source_branch)
raise_error("You can only create or edit files when you are on a branch") raise_error("You can only create or edit files when you are on a branch")
end end
if @current_branch != @target_branch if different_branch?
if repository.branch_names.include?(@target_branch) if repository.branch_names.include?(@target_branch)
raise_error("Branch with such name already exists. You need to switch to this branch in order to make changes") raise_error("Branch with such name already exists. You need to switch to this branch in order to make changes")
end end
...@@ -65,10 +63,10 @@ module Files ...@@ -65,10 +63,10 @@ module Files
end end
def create_target_branch def create_target_branch
result = CreateBranchService.new(project, current_user).execute(@target_branch, @current_branch) result = CreateBranchService.new(project, current_user).execute(@target_branch, @source_branch, source_project: @source_project)
unless result[:status] == :success unless result[:status] == :success
raise_error("Something went wrong when we tried to create #{@target_branch} for you") raise_error("Something went wrong when we tried to create #{@target_branch} for you: #{result[:message]}")
end end
end end
end end
......
...@@ -26,7 +26,7 @@ module Files ...@@ -26,7 +26,7 @@ module Files
unless project.empty_repo? unless project.empty_repo?
@file_path.slice!(0) if @file_path.start_with?('/') @file_path.slice!(0) if @file_path.start_with?('/')
blob = repository.blob_at_branch(@current_branch, @file_path) blob = repository.blob_at_branch(@source_branch, @file_path)
if blob if blob
raise_error("Your changes could not be committed because a file with the same name already exists") raise_error("Your changes could not be committed because a file with the same name already exists")
......
...@@ -3,12 +3,16 @@ module Projects ...@@ -3,12 +3,16 @@ module Projects
def execute def execute
# check that user is allowed to set specified visibility_level # check that user is allowed to set specified visibility_level
new_visibility = params[:visibility_level] new_visibility = params[:visibility_level]
if new_visibility && new_visibility.to_i != project.visibility_level if new_visibility
unless can?(current_user, :change_visibility_level, project) && if new_visibility.to_i != project.visibility_level
Gitlab::VisibilityLevel.allowed_for?(current_user, new_visibility) unless can?(current_user, :change_visibility_level, project) &&
deny_visibility_level(project, new_visibility) Gitlab::VisibilityLevel.allowed_for?(current_user, new_visibility)
return project deny_visibility_level(project, new_visibility)
return project
end
end end
return false unless visibility_level_allowed?(new_visibility)
end end
new_branch = params[:default_branch] new_branch = params[:default_branch]
...@@ -23,5 +27,19 @@ module Projects ...@@ -23,5 +27,19 @@ module Projects
end end
end end
end end
private
def visibility_level_allowed?(level)
return true if project.visibility_level_allowed?(level)
level_name = Gitlab::VisibilityLevel.level_name(level)
project.errors.add(
:visibility_level,
"#{level_name} could not be set as visibility level of this project - parent project settings are more restrictive"
)
false
end
end end
end end
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
- else - else
= render 'explore/head' = render 'explore/head'
.gray-content-block.clearfix .gray-content-block.clearfix.second-block
= render 'filter' = render 'filter'
= render 'projects', projects: @projects = render 'projects', projects: @projects
= paginate @projects, theme: "gitlab" = paginate @projects, theme: "gitlab"
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
= render 'explore/head' = render 'explore/head'
.explore-trending-block .explore-trending-block
.gray-content-block .gray-content-block.second-block
.pull-right .pull-right
= render 'explore/projects/dropdown' = render 'explore/projects/dropdown'
.oneline .oneline
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
= render 'explore/head' = render 'explore/head'
.explore-trending-block .explore-trending-block
.gray-content-block .gray-content-block.second-block
.pull-right .pull-right
= render 'explore/projects/dropdown' = render 'explore/projects/dropdown'
.oneline .oneline
......
...@@ -2,3 +2,7 @@ ...@@ -2,3 +2,7 @@
= button_tag 'Commit Changes', class: 'btn commit-btn js-commit-button btn-create' = button_tag 'Commit Changes', class: 'btn commit-btn js-commit-button btn-create'
= link_to 'Cancel', cancel_path, = link_to 'Cancel', cancel_path,
class: 'btn btn-cancel', data: {confirm: leave_edit_message} class: 'btn btn-cancel', data: {confirm: leave_edit_message}
- unless can?(current_user, :push_code, @project)
.inline.prepend-left-10
= commit_in_fork_help
...@@ -27,7 +27,7 @@ ...@@ -27,7 +27,7 @@
= icon('rss') = icon('rss')
.project-repo-buttons .project-repo-buttons
.split-one .split-one.count-buttons
= render 'projects/buttons/star' = render 'projects/buttons/star'
= render 'projects/buttons/fork' = render 'projects/buttons/fork'
...@@ -38,3 +38,6 @@ ...@@ -38,3 +38,6 @@
= render 'projects/buttons/dropdown' = render 'projects/buttons/dropdown'
= render 'projects/buttons/notifications' = render 'projects/buttons/notifications'
:coffeescript
new Star()
\ No newline at end of file
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
= link_to 'Raw', namespace_project_raw_path(@project.namespace, @project, @id), = link_to 'Raw', namespace_project_raw_path(@project.namespace, @project, @id),
class: 'btn btn-sm', target: '_blank' class: 'btn btn-sm', target: '_blank'
-# only show normal/blame view links for text files -# only show normal/blame view links for text files
- if blob_viewable?(@blob) - if blob_text_viewable?(@blob)
- if current_page? namespace_project_blame_path(@project.namespace, @project, @id) - if current_page? namespace_project_blame_path(@project.namespace, @project, @id)
= link_to 'Normal View', namespace_project_blob_path(@project.namespace, @project, @id), = link_to 'Normal View', namespace_project_blob_path(@project.namespace, @project, @id),
class: 'btn btn-sm' class: 'btn btn-sm'
...@@ -14,13 +14,8 @@ ...@@ -14,13 +14,8 @@
= link_to 'Permalink', namespace_project_blob_path(@project.namespace, @project, = link_to 'Permalink', namespace_project_blob_path(@project.namespace, @project,
tree_join(@commit.sha, @path)), class: 'btn btn-sm' tree_join(@commit.sha, @path)), class: 'btn btn-sm'
- if blob_editable?(@blob) - if current_user
.btn-group{ role: "group" } .btn-group{ role: "group" }
= edit_blob_link(@project, @ref, @path) = edit_blob_link
%button.btn.btn-default{ 'data-target' => '#modal-upload-blob', 'data-toggle' => 'modal' } Replace = replace_blob_link
%button.btn.btn-remove{ 'data-target' => '#modal-remove-blob', 'data-toggle' => 'modal' } Delete = delete_blob_link
- elsif !on_top_of_branch?
.btn-group{ role: "group" }
%button.btn.btn-default.disabled.has_tooltip{title: "You can only edit files when you are on a branch.", data: {container: 'body'}} Edit
%button.btn.btn-default.disabled.has_tooltip{title: "You can only replace files when you are on a branch.", data: {container: 'body'}} Replace
%button.btn.btn-remove.disabled.has_tooltip{title: "You can only delete files when you are on a branch.", data: {container: 'body'}} Delete
...@@ -17,5 +17,9 @@ ...@@ -17,5 +17,9 @@
= submit_tag "Create directory", class: 'btn btn-create' = submit_tag "Create directory", class: 'btn btn-create'
= link_to "Cancel", '#', class: "btn btn-cancel", "data-dismiss" => "modal" = link_to "Cancel", '#', class: "btn btn-cancel", "data-dismiss" => "modal"
- unless can?(current_user, :push_code, @project)
.inline.prepend-left-10
= commit_in_fork_help
:javascript :javascript
new NewCommitForm($('.js-create-dir-form')) new NewCommitForm($('.js-create-dir-form'))
...@@ -20,6 +20,11 @@ ...@@ -20,6 +20,11 @@
= button_tag button_title, class: 'btn btn-small btn-create btn-upload-file', id: 'submit-all' = 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" = link_to "Cancel", '#', class: "btn btn-cancel", "data-dismiss" => "modal"
- unless can?(current_user, :push_code, @project)
.inline.prepend-left-10
= commit_in_fork_help
:javascript :javascript
disableButtonIfEmptyField($('.js-upload-blob-form').find('.js-commit-message'), '.btn-upload-file'); disableButtonIfEmptyField($('.js-upload-blob-form').find('.js-commit-message'), '.btn-upload-file');
new BlobFileDropzone($('.js-upload-blob-form'), '#{method}'); new BlobFileDropzone($('.js-upload-blob-form'), '#{method}');
......
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
= hidden_field_tag 'last_commit', @last_commit = hidden_field_tag 'last_commit', @last_commit
= hidden_field_tag 'content', '', id: "file-content" = hidden_field_tag 'content', '', id: "file-content"
= hidden_field_tag 'from_merge_request_id', params[:from_merge_request_id] = 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 :javascript
blob = new EditBlob(gon.relative_url_root + "#{Gitlab::Application.config.assets.prefix}", "#{@blob.language.try(:ace_mode)}") blob = new EditBlob(gon.relative_url_root + "#{Gitlab::Application.config.assets.prefix}", "#{@blob.language.try(:ace_mode)}")
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
%div#tree-holder.tree-holder %div#tree-holder.tree-holder
= render 'blob', blob: @blob = render 'blob', blob: @blob
- if blob_editable?(@blob) - if can_edit_blob?(@blob)
= render 'projects/blob/remove' = render 'projects/blob/remove'
- title = "Replace #{@blob.name}" - title = "Replace #{@blob.name}"
......
...@@ -9,11 +9,12 @@ ...@@ -9,11 +9,12 @@
New Branch New Branch
%hr %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 .form-group
= label_tag :branch_name, nil, class: 'control-label' = label_tag :branch_name, nil, class: 'control-label'
.col-sm-10 .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 .form-group
= label_tag :ref, 'Create from', class: 'control-label' = label_tag :ref, 'Create from', class: 'control-label'
.col-sm-10 .col-sm-10
...@@ -26,7 +27,4 @@ ...@@ -26,7 +27,4 @@
:javascript :javascript
var availableRefs = #{@project.repository.ref_names.to_json}; var availableRefs = #{@project.repository.ref_names.to_json};
$("#ref").autocomplete({ new NewBranchForm($('.js-create-branch-form'), availableRefs)
source: availableRefs,
minLength: 1
});
...@@ -18,10 +18,11 @@ ...@@ -18,10 +18,11 @@
= link_to new_namespace_project_snippet_path(@project.namespace, @project) do = link_to new_namespace_project_snippet_path(@project.namespace, @project) do
= icon('file-text-o fw') = icon('file-text-o fw')
New snippet New snippet
- if can?(current_user, :push_code, @project) - if can?(current_user, :push_code, @project)
%li.divider %li.divider
%li %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') = icon('file fw')
New file New file
%li %li
...@@ -32,3 +33,20 @@ ...@@ -32,3 +33,20 @@
= link_to new_namespace_project_tag_path(@project.namespace, @project) do = link_to new_namespace_project_tag_path(@project.namespace, @project) do
= icon('tags fw') = icon('tags fw')
New tag 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
...@@ -4,10 +4,15 @@ ...@@ -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 = 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') = icon('code-fork fw')
Fork Fork
%div.count-with-arrow
%span.arrow
%span.count %span.count
= @project.forks_count = @project.forks_count
- else - else
= link_to new_namespace_project_fork_path(@project.namespace, @project), title: "Fork project", class: 'btn has_tooltip' do = link_to new_namespace_project_fork_path(@project.namespace, @project), title: "Fork project", class: 'btn has_tooltip' do
= icon('code-fork fw') = icon('code-fork fw')
Fork
%div.count-with-arrow
%span.arrow
%span.count %span.count
= @project.forks_count = @project.forks_count
- if current_user - 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 = 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') - if current_user.starred?(@project)
%span.count = 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 = @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 - else
= link_to new_user_session_path, class: 'btn has_tooltip star-btn', title: 'You must sign in to star a project' do = 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') = icon('star fw')
Star
%div.count-with-arrow
%span.arrow
%span.count %span.count
= @project.star_count = @project.star_count
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
= "#{diff_file.diff.a_mode}#{diff_file.diff.b_mode}" = "#{diff_file.diff.a_mode}#{diff_file.diff.b_mode}"
.diff-controls .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 = link_to '#', class: 'js-toggle-diff-comments btn btn-sm active has_tooltip', title: "Toggle comments for this file" do
%i.fa.fa-comments %i.fa.fa-comments
&nbsp; &nbsp;
...@@ -32,14 +32,15 @@ ...@@ -32,14 +32,15 @@
- if editable_diff?(diff_file) - if editable_diff?(diff_file)
= edit_blob_link(@merge_request.source_project, = edit_blob_link(@merge_request.source_project,
@merge_request.source_branch, diff_file.new_path, @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) = view_file_btn(diff_commit.id, diff_file, project)
.diff-content.diff-wrap-lines .diff-content.diff-wrap-lines
-# Skipp all non non-supported blobs -# Skipp all non non-supported blobs
- return unless blob.respond_to?('text?') - return unless blob.respond_to?('text?')
- if blob_viewable?(blob) - if blob_text_viewable?(blob)
- if diff_view == 'parallel' - if diff_view == 'parallel'
= render "projects/diffs/parallel_view", diff_file: diff_file, project: project, blob: blob, index: i = render "projects/diffs/parallel_view", diff_file: diff_file, project: project, blob: blob, index: i
- else - else
......
...@@ -43,4 +43,3 @@ ...@@ -43,4 +43,3 @@
%i.fa.fa-spinner.fa-spin %i.fa.fa-spinner.fa-spin
Forking repository Forking repository
%p Please wait a moment, this page will automatically refresh when ready. %p Please wait a moment, this page will automatically refresh when ready.
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
- if merge_request.open? && merge_request.broken? - if merge_request.open? && merge_request.broken?
%li %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') = icon('exclamation-triangle')
- if merge_request.assignee - if merge_request.assignee
......
...@@ -29,7 +29,7 @@ ...@@ -29,7 +29,7 @@
- if tree.readme - if tree.readme
= render "projects/tree/readme", readme: 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/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' = render 'projects/blob/new_dir'
......
...@@ -11,34 +11,65 @@ ...@@ -11,34 +11,65 @@
= link_to truncate(title, length: 40), namespace_project_tree_path(@project.namespace, @project, path) = link_to truncate(title, length: 40), namespace_project_tree_path(@project.namespace, @project, path)
- else - else
= link_to title, '#' = link_to title, '#'
- if allowed_tree_edit?
- if current_user
%li %li
%span.dropdown - if !on_top_of_branch?
%a.dropdown-toggle.btn.btn-sm.add-to-tree{href: '#', "data-toggle" => "dropdown"} %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') = icon('plus')
%ul.dropdown-menu - else
%li %span.dropdown
= link_to namespace_project_new_blob_path(@project.namespace, @project, @id), title: 'Create file', id: 'new-file-link' do %a.dropdown-toggle.btn.btn-sm.add-to-tree{href: '#', "data-toggle" => "dropdown"}
= icon('pencil fw') = icon('plus')
New file %ul.dropdown-menu
%li - if can_edit_tree?
= link_to '#modal-upload-blob', { 'data-target' => '#modal-upload-blob', 'data-toggle' => 'modal'} do %li
= icon('file fw') = link_to namespace_project_new_blob_path(@project.namespace, @project, @id) do
Upload file = icon('pencil fw')
%li New file
= link_to '#modal-create-new-dir', { 'data-target' => '#modal-create-new-dir', 'data-toggle' => 'modal'} do %li
= icon('folder fw') = link_to '#modal-upload-blob', { 'data-target' => '#modal-upload-blob', 'data-toggle' => 'modal'} do
New directory = icon('file fw')
%li.divider Upload file
%li %li
= link_to new_namespace_project_branch_path(@project.namespace, @project) do = link_to '#modal-create-new-dir', { 'data-target' => '#modal-create-new-dir', 'data-toggle' => 'modal'} do
= icon('code-fork fw') = icon('folder fw')
New branch New directory
%li - elsif can?(current_user, :fork_project, @project)
= link_to new_namespace_project_tag_path(@project.namespace, @project) do %li
= icon('tags fw') - continue_params = { to: namespace_project_new_blob_path(@project.namespace, @project, @id),
New tag notice: edit_in_new_fork_notice,
- elsif !on_top_of_branch? notice_now: edit_in_new_fork_notice_now }
%li - fork_path = namespace_project_fork_path(@project.namespace, @project, namespace_key: current_user.namespace.id,
%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'}} continue: continue_params)
= icon('plus') = 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
- project = project || @project - project = project || @project
.git-clone-holder.input-group
.input-group-addon.git-protocols .git-clone-holder
.input-group-btn .btn-group.clone-options
= ssh_clone_button(project) %a#clone-dropdown.clone-dropdown-btn.btn{href: '#', 'data-toggle' => 'dropdown'}
.input-group-btn %span
= http_clone_button(project) = 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 = text_field_tag :project_clone, default_url_to_repo(project), class: "js-select-on-focus form-control", readonly: true
.input-group-btn .input-group-btn
= clipboard_button(clipboard_target: '#project_clone') = 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'));
});
= render 'shared/commit_message_container', placeholder: placeholder = render 'shared/commit_message_container', placeholder: placeholder
- unless @project.empty_repo? - if @project.empty_repo?
.form-group.branch = hidden_field_tag 'target_branch', @ref
= label_tag 'new_branch', 'Target branch', class: 'control-label' - else
.col-sm-10 - if can?(current_user, :push_code, @project)
= text_field_tag 'new_branch', @new_branch || tree_edit_branch, required: true, class: "form-control js-new-branch" .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 .js-create-merge-request-container
.checkbox .checkbox
- nonce = SecureRandom.hex - nonce = SecureRandom.hex
= label_tag "create_merge_request-#{nonce}" do = 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}" = 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 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' = hidden_field_tag 'original_branch', @ref, class: 'js-original-branch'
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
= icon('smile-o') = icon('smile-o')
.emoji-menu .emoji-menu
.emoji-menu-content .emoji-menu-content
= text_field_tag :emoji_search, "", class: "emoji-search search-input form-control"
- AwardEmoji.emoji_by_category.each do |category, emojis| - AwardEmoji.emoji_by_category.each do |category, emojis|
%h5= AwardEmoji::CATEGORIES[category] %h5= AwardEmoji::CATEGORIES[category]
%ul %ul
...@@ -32,11 +33,11 @@ ...@@ -32,11 +33,11 @@
aliases aliases
) )
$(".emoji-menu-content li").click (e)-> $(".awards").on "click", ".emoji-menu-content li", (e) ->
emoji = $(this).find(".emoji-icon").data("emoji") emoji = $(this).find(".emoji-icon").data("emoji")
awards_handler.addAward(emoji) awards_handler.addAward(emoji)
$(".awards").on "click", ".award", (e)-> $(".awards").on "click", ".award", (e) ->
emoji = $(this).find(".icon").data("emoji") emoji = $(this).find(".icon").data("emoji")
awards_handler.addAward(emoji) awards_handler.addAward(emoji)
......
...@@ -25,6 +25,7 @@ Feature: Project Commits Branches ...@@ -25,6 +25,7 @@ Feature: Project Commits Branches
And I click branch 'improve/awesome' delete link And I click branch 'improve/awesome' delete link
Then I should not see branch 'improve/awesome' Then I should not see branch 'improve/awesome'
@javascript
Scenario: I create a branch with invalid name Scenario: I create a branch with invalid name
Given I visit project branches page Given I visit project branches page
And I click new branch link And I click new branch link
......
@project-create
Feature: Project Create Feature: Project Create
In order to get access to project sections In order to get access to project sections
A user with ability to create a project A user with ability to create a project
......
...@@ -18,7 +18,13 @@ Feature: Award Emoji ...@@ -18,7 +18,13 @@ Feature: Award Emoji
Given I click to emoji-picker Given I click to emoji-picker
Then I can see the activity and food categories 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 @javascript
Scenario: I add award emoji using regular comment Scenario: I add award emoji using regular comment
Given I leave comment with a single emoji Given I leave comment with a single emoji
Then I have award added Then I have award added
...@@ -12,6 +12,14 @@ Feature: Project Merge Requests Acceptance ...@@ -12,6 +12,14 @@ Feature: Project Merge Requests Acceptance
Then I should see merge request merged Then I should see merge request merged
And I should not see the Remove Source Branch button 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 @javascript
Scenario: Accepting the Merge Request without removing the source branch Scenario: Accepting the Merge Request without removing the source branch
Given I am on the Merge Request detail page Given I am on the Merge Request detail page
......
...@@ -24,6 +24,12 @@ Feature: Project Source Browse Files ...@@ -24,6 +24,12 @@ Feature: Project Source Browse Files
Given I click on "New file" link in repo Given I click on "New file" link in repo
Then I can see new file page 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 @javascript
Scenario: I can create and commit file Scenario: I can create and commit file
Given I click on "New file" link in repo Given I click on "New file" link in repo
...@@ -34,6 +40,17 @@ Feature: Project Source Browse Files ...@@ -34,6 +40,17 @@ Feature: Project Source Browse Files
Then I am redirected to the new file Then I am redirected to the new file
And I should see its new content 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 @javascript
Scenario: I can create and commit file with new lines at the end of file Scenario: I can create and commit file with new lines at the end of file
Given I click on "New file" link in repo Given I click on "New file" link in repo
...@@ -45,6 +62,17 @@ Feature: Project Source Browse Files ...@@ -45,6 +62,17 @@ Feature: Project Source Browse Files
And I click button "Edit" And I click button "Edit"
And I should see its content with new lines preserved at end of file 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 @javascript
Scenario: I can upload file and commit Scenario: I can upload file and commit
Given I click on "Upload file" link in repo Given I click on "Upload file" link in repo
...@@ -56,6 +84,19 @@ Feature: Project Source Browse Files ...@@ -56,6 +84,19 @@ Feature: Project Source Browse Files
And I am redirected to the new merge request page And I am redirected to the new merge request page
And I can see the new commit message 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 @javascript
Scenario: I can replace file and commit Scenario: I can replace file and commit
Given I click on ".gitignore" file in repo Given I click on ".gitignore" file in repo
...@@ -68,15 +109,19 @@ Feature: Project Source Browse Files ...@@ -68,15 +109,19 @@ Feature: Project Source Browse Files
And I can see the replacement commit message And I can see the replacement commit message
@javascript @javascript
Scenario: I can create and commit file and specify new branch Scenario: I can replace file and commit when I don't have write access
Given I click on "New file" link in repo Given I don't have write access
And I edit code And I click on ".gitignore" file in repo
And I fill the new file name And I see the ".gitignore"
And I fill the commit message And I click on "Replace"
And I fill the new branch name Then I should see a notice about a new fork having been created
And I click on "Commit Changes" When I click on "Replace"
Then I am redirected to the new merge request page And I replace it with a text file
And I should see its new content 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 @javascript
Scenario: I can create file in empty repo Scenario: I can create file in empty repo
...@@ -117,6 +162,14 @@ Feature: Project Source Browse Files ...@@ -117,6 +162,14 @@ Feature: Project Source Browse Files
And I click button "Edit" And I click button "Edit"
Then I can edit code 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 Scenario: If the file is binary the edit link is hidden
Given I visit a binary file in the repo Given I visit a binary file in the repo
Then I cannot see the edit button Then I cannot see the edit button
...@@ -131,6 +184,17 @@ Feature: Project Source Browse Files ...@@ -131,6 +184,17 @@ Feature: Project Source Browse Files
Then I am redirected to the ".gitignore" Then I am redirected to the ".gitignore"
And I should see its new content 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 @javascript
Scenario: I can edit and commit file to new branch Scenario: I can edit and commit file to new branch
Given I click on ".gitignore" file in repo Given I click on ".gitignore" file in repo
...@@ -161,6 +225,17 @@ Feature: Project Source Browse Files ...@@ -161,6 +225,17 @@ Feature: Project Source Browse Files
And I click on "Create directory" And I click on "Create directory"
Then I am redirected to the new merge request page 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 @javascript
Scenario: I attempt to create an existing directory Scenario: I attempt to create an existing directory
When I click on "New directory" link in repo When I click on "New directory" link in repo
...@@ -188,6 +263,19 @@ Feature: Project Source Browse Files ...@@ -188,6 +263,19 @@ Feature: Project Source Browse Files
Then I am redirected to the files URL Then I am redirected to the files URL
And I don't see the ".gitignore" 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 Scenario: I can browse directory with Browse Dir
Given I click on files directory Given I click on files directory
And I click on History link And I click on History link
......
@project-stars
Feature: Project Star Feature: Project Star
Scenario: New projects have 0 stars Scenario: New projects have 0 stars
Given public project "Community" Given public project "Community"
......
...@@ -61,7 +61,8 @@ class Spinach::Features::ProjectCommitsBranches < Spinach::FeatureSteps ...@@ -61,7 +61,8 @@ class Spinach::Features::ProjectCommitsBranches < Spinach::FeatureSteps
end end
step 'I should see new an error that branch is invalid' do 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 end
step 'I should see new an error that ref is invalid' do step 'I should see new an error that ref is invalid' do
......
...@@ -26,7 +26,8 @@ class Spinach::Features::ProjectCreate < Spinach::FeatureSteps ...@@ -26,7 +26,8 @@ class Spinach::Features::ProjectCreate < Spinach::FeatureSteps
end end
step 'I click on HTTP' do step 'I click on HTTP' do
click_button 'HTTP' find('#clone-dropdown').click
find('#http-selector').click
end end
step 'Remote url should update to http link' do step 'Remote url should update to http link' do
...@@ -34,7 +35,8 @@ class Spinach::Features::ProjectCreate < Spinach::FeatureSteps ...@@ -34,7 +35,8 @@ class Spinach::Features::ProjectCreate < Spinach::FeatureSteps
end end
step 'If I click on SSH' do step 'If I click on SSH' do
click_button 'SSH' find('#clone-dropdown').click
find('#ssh-selector').click
end end
step 'Remote url should update to ssh link' do step 'Remote url should update to ssh link' do
......
...@@ -52,4 +52,16 @@ class Spinach::Features::AwardEmoji < Spinach::FeatureSteps ...@@ -52,4 +52,16 @@ class Spinach::Features::AwardEmoji < Spinach::FeatureSteps
click_button 'Add Comment' click_button 'Add Comment'
end end
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 end
...@@ -6,6 +6,10 @@ class Spinach::Features::ProjectMergeRequestsAcceptance < Spinach::FeatureSteps ...@@ -6,6 +6,10 @@ class Spinach::Features::ProjectMergeRequestsAcceptance < Spinach::FeatureSteps
visit merge_request_path(@merge_request) visit merge_request_path(@merge_request)
end 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 step 'I click on "Remove source branch" option' do
check('Remove source branch') check('Remove source branch')
end end
......
...@@ -5,6 +5,12 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps ...@@ -5,6 +5,12 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
include SharedPaths include SharedPaths
include RepoHelpers 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 step 'I should see files from repository' do
expect(page).to have_content "VERSION" expect(page).to have_content "VERSION"
expect(page).to have_content ".gitignore" expect(page).to have_content ".gitignore"
...@@ -75,7 +81,7 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps ...@@ -75,7 +81,7 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
end end
step 'I fill the new branch name' do 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 end
step 'I fill the new file name with an illegal name' do step 'I fill the new file name with an illegal name' do
...@@ -87,7 +93,7 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps ...@@ -87,7 +93,7 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
end end
step 'I fill the commit message' do 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 end
step 'I click link "Diff"' do step 'I click link "Diff"' do
...@@ -103,7 +109,7 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps ...@@ -103,7 +109,7 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
end end
step 'I click on "Delete"' do step 'I click on "Delete"' do
click_button 'Delete' click_on 'Delete'
end end
step 'I click on "Delete file"' do step 'I click on "Delete file"' do
...@@ -111,7 +117,7 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps ...@@ -111,7 +117,7 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
end end
step 'I click on "Replace"' do step 'I click on "Replace"' do
click_button "Replace" click_on "Replace"
end end
step 'I click on "Replace file"' do step 'I click on "Replace file"' do
...@@ -124,7 +130,7 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps ...@@ -124,7 +130,7 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
step 'I click on "New file" link in repo' do step 'I click on "New file" link in repo' do
find('.add-to-tree').click find('.add-to-tree').click
click_link 'Create file' click_link 'New file'
end end
step 'I click on "Upload file" link in repo' do step 'I click on "Upload file" link in repo' do
...@@ -155,7 +161,7 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps ...@@ -155,7 +161,7 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
end end
step 'I can see the new commit message' do 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 end
step 'I upload a new text file' do step 'I upload a new text file' do
...@@ -164,7 +170,7 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps ...@@ -164,7 +170,7 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
step 'I fill the upload file commit message' do step 'I fill the upload file commit message' do
page.within('#modal-upload-blob') 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
end end
...@@ -251,9 +257,14 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps ...@@ -251,9 +257,14 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
expect(current_path).to eq(new_namespace_project_merge_request_path(@project.namespace, @project)) expect(current_path).to eq(new_namespace_project_merge_request_path(@project.namespace, @project))
end 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 step 'I am redirected to the root directory' do
expect(current_path).to eq( expect(current_path).to eq(
namespace_project_tree_path(@project.namespace, @project, 'master/')) namespace_project_tree_path(@project.namespace, @project, 'master'))
end end
step "I don't see the permalink link" do step "I don't see the permalink link" do
...@@ -332,8 +343,12 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps ...@@ -332,8 +343,12 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
expect(page).to have_content 'Permalink' expect(page).to have_content 'Permalink'
expect(page).not_to have_content 'Edit' expect(page).not_to have_content 'Edit'
expect(page).not_to have_content 'Blame' expect(page).not_to have_content 'Blame'
expect(page).not_to have_content 'Delete' expect(page).to have_content 'Delete'
expect(page).not_to have_content 'Replace' 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 end
private private
......
...@@ -32,6 +32,6 @@ class Spinach::Features::ProjectStar < Spinach::FeatureSteps ...@@ -32,6 +32,6 @@ class Spinach::Features::ProjectStar < Spinach::FeatureSteps
protected protected
def has_n_stars(n) 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
end end
...@@ -7,7 +7,7 @@ module API ...@@ -7,7 +7,7 @@ module API
def commit_params(attrs) def commit_params(attrs)
{ {
file_path: attrs[:file_path], file_path: attrs[:file_path],
current_branch: attrs[:branch_name], source_branch: attrs[:branch_name],
target_branch: attrs[:branch_name], target_branch: attrs[:branch_name],
commit_message: attrs[:commit_message], commit_message: attrs[:commit_message],
file_content: attrs[:content], file_content: attrs[:content],
......
...@@ -14,7 +14,7 @@ module Gitlab ...@@ -14,7 +14,7 @@ module Gitlab
# LDAP distinguished name is case-insensitive # LDAP distinguished name is case-insensitive
identity = ::Identity. identity = ::Identity.
where(provider: provider). where(provider: provider).
where('lower(extern_uid) = ?', uid.mb_chars.downcase.to_s).last iwhere(extern_uid: uid).last
identity && identity.user identity && identity.user
end end
end end
...@@ -31,7 +31,7 @@ module Gitlab ...@@ -31,7 +31,7 @@ module Gitlab
def find_by_uid_and_provider def find_by_uid_and_provider
self.class.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 end
def find_by_email def find_by_email
...@@ -47,7 +47,7 @@ module Gitlab ...@@ -47,7 +47,7 @@ module Gitlab
# find_or_initialize_by doesn't update `gl_user.identities`, and isn't autosaved. # 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.find { |identity| identity.provider == auth_hash.provider }
identity ||= gl_user.identities.build(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 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 matching email but changed DN, update the DN.
# For an existing user with no change in DN, this line changes nothing. # For an existing user with no change in DN, this line changes nothing.
......
...@@ -64,7 +64,7 @@ module Gitlab ...@@ -64,7 +64,7 @@ module Gitlab
# If a corresponding person exists with same uid in a LDAP server, # If a corresponding person exists with same uid in a LDAP server,
# set up a Gitlab user with dual LDAP and Omniauth identities. # 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. # 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) user.identities.build(extern_uid: auth_hash.uid, provider: auth_hash.provider)
else else
......
...@@ -51,6 +51,15 @@ module Gitlab ...@@ -51,6 +51,15 @@ module Gitlab
def allowed_fork_levels(origin_level) def allowed_fork_levels(origin_level)
[PRIVATE, INTERNAL, PUBLIC].select{ |level| level <= origin_level } [PRIVATE, INTERNAL, PUBLIC].select{ |level| level <= origin_level }
end 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 end
def private? def private?
......
...@@ -98,7 +98,7 @@ describe Projects::TreeController do ...@@ -98,7 +98,7 @@ describe Projects::TreeController do
project_id: project.to_param, project_id: project.to_param,
id: 'master', id: 'master',
dir_name: path, dir_name: path,
new_branch: target_branch, target_branch: target_branch,
commit_message: 'Test commit message') commit_message: 'Test commit message')
end end
...@@ -108,8 +108,8 @@ describe Projects::TreeController do ...@@ -108,8 +108,8 @@ describe Projects::TreeController do
it 'redirects to the new directory' do it 'redirects to the new directory' do
expect(subject). expect(subject).
to redirect_to("/#{project.path_with_namespace}/blob/#{target_branch}/#{path}") to redirect_to("/#{project.path_with_namespace}/tree/#{target_branch}/#{path}")
expect(flash[:notice]).to eq('The directory has been successfully created') expect(flash[:notice]).to eq('The directory has been successfully created.')
end end
end end
...@@ -119,7 +119,7 @@ describe Projects::TreeController do ...@@ -119,7 +119,7 @@ describe Projects::TreeController do
it 'does not allow overwriting of existing files' do it 'does not allow overwriting of existing files' do
expect(subject). 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') expect(flash[:alert]).to eq('Directory already exists as a file')
end end
end end
......
%form.js-create-branch-form
%input.js-branch-name
.js-branch-name-error
%input{id: "ref"}
#= 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)
...@@ -42,6 +42,21 @@ describe Gitlab::LDAP::User, lib: true do ...@@ -42,6 +42,21 @@ describe Gitlab::LDAP::User, lib: true do
end end
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 describe :find_or_create do
it "finds the user if already existing" do it "finds the user if already existing" do
create(:omniauth_user, extern_uid: 'my-uid', provider: 'ldapmain') create(:omniauth_user, extern_uid: 'my-uid', provider: 'ldapmain')
......
...@@ -62,4 +62,14 @@ describe GlobalMilestone, models: true do ...@@ -62,4 +62,14 @@ describe GlobalMilestone, models: true do
expect(@global_milestone.milestones.count).to eq(3) expect(@global_milestone.milestones.count).to eq(3)
end end
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 end
...@@ -552,4 +552,28 @@ describe Project, models: true do ...@@ -552,4 +552,28 @@ describe Project, models: true do
end end
end 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 end
...@@ -118,7 +118,7 @@ describe API::API, api: true do ...@@ -118,7 +118,7 @@ describe API::API, api: true do
branch_name: 'new design', branch_name: 'new design',
ref: branch_sha ref: branch_sha
expect(response.status).to eq(400) 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 end
it 'should return 400 if branch already exists' do it 'should return 400 if branch already exists' do
......
...@@ -100,6 +100,45 @@ describe Projects::UpdateService, services: true do ...@@ -100,6 +100,45 @@ describe Projects::UpdateService, services: true do
end end
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) def update_project(project, user, opts)
Projects::UpdateService.new(project, user, opts).execute Projects::UpdateService.new(project, user, opts).execute
end end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment