Commit 193d7079 authored by Achilleas Pipinellis's avatar Achilleas Pipinellis

Merge branch 'master' into feature/import-export-docs

parents 51b51de7 df8dd6de
......@@ -13,9 +13,11 @@ v 8.9.0 (unreleased)
- Fix endless redirections when accessing user OAuth applications when they are disabled
- Allow enabling wiki page events from Webhook management UI
- Bump rouge to 1.11.0
- Fix MR-auto-close text added to description
- Fix issue with arrow keys not working in search autocomplete dropdown
- Fix an issue where note polling stopped working if a window was in the
background during a refresh.
- Pre-processing Markdown now only happens when needed
- Make EmailsOnPushWorker use Sidekiq mailers queue
- Redesign all Devise emails. !4297
- Don't show 'Leave Project' to group members
......@@ -148,6 +150,7 @@ v 8.9.0 (unreleased)
- Ensure Todos counters doesn't count Todos for projects pending delete
- Add left/right arrows horizontal navigation
- Add tooltip to pin/unpin navbar
- Add new sub nav style to Wiki and Graphs sub navigation
v 8.8.5
- Import GitHub repositories respecting the API rate limit !4166
......
......@@ -48,7 +48,7 @@ gem 'attr_encrypted', '~> 3.0.0'
gem 'u2f', '~> 0.2.1'
# Browser detection
gem "browser", '~> 2.0.3'
gem "browser", '~> 2.2'
# Extracting information from a git repository
# Provide access to Gitlab::Git library
......@@ -330,7 +330,7 @@ gem "newrelic_rpm", '~> 3.14'
gem 'octokit', '~> 4.3.0'
gem "mail_room", "~> 0.7"
gem "mail_room", "~> 0.8"
gem 'email_reply_parser', '~> 0.5.8'
......
......@@ -98,7 +98,7 @@ GEM
autoprefixer-rails (>= 5.2.1)
sass (>= 3.3.4)
brakeman (3.3.2)
browser (2.0.3)
browser (2.2.0)
builder (3.2.2)
bullet (5.0.0)
activesupport (>= 3.0.0)
......@@ -398,7 +398,7 @@ GEM
systemu (~> 2.6.2)
mail (2.6.4)
mime-types (>= 1.16, < 4)
mail_room (0.7.0)
mail_room (0.8.0)
method_source (0.8.2)
mime-types (2.99.2)
mimemagic (0.3.0)
......@@ -833,7 +833,7 @@ DEPENDENCIES
binding_of_caller (~> 0.7.2)
bootstrap-sass (~> 3.3.0)
brakeman (~> 3.3.0)
browser (~> 2.0.3)
browser (~> 2.2)
bullet
bundler-audit
byebug
......@@ -899,7 +899,7 @@ DEPENDENCIES
license_finder
licensee (~> 8.0.0)
loofah (~> 2.0.3)
mail_room (~> 0.7)
mail_room (~> 0.8)
method_source (~> 0.8)
minitest (~> 5.7.0)
mousetrap-rails (~> 1.4.6)
......
......@@ -121,6 +121,11 @@ window.onload = ->
setTimeout shiftWindow, 100
$ ->
$document = $(document)
$window = $(window)
$body = $('body')
gl.utils.preventDisabledButtons()
bootstrapBreakpoint = bp.getBreakpointSize()
......@@ -152,7 +157,7 @@ $ ->
), 1
# Initialize tooltips
$('body').tooltip(
$body.tooltip(
selector: '.has-tooltip, [data-toggle="tooltip"]'
placement: (_, el) ->
$el = $(el)
......@@ -171,7 +176,7 @@ $ ->
flash.show()
# Disable form buttons while a form is submitting
$('body').on 'ajax:complete, ajax:beforeSend, submit', 'form', (e) ->
$body.on 'ajax:complete, ajax:beforeSend, submit', 'form', (e) ->
buttons = $('[type="submit"]', @)
switch e.type
......@@ -184,7 +189,7 @@ $ ->
$('.account-box').hover -> $(@).toggleClass('hover')
# Commit show suppressed diff
$(document).on 'click', '.diff-content .js-show-suppressed-diff', ->
$document.on 'click', '.diff-content .js-show-suppressed-diff', ->
$container = $(@).parent()
$container.next('table').show()
$container.remove()
......@@ -197,13 +202,13 @@ $ ->
$('.navbar-toggle i').toggleClass("fa-angle-right fa-angle-left")
# Show/hide comments on diff
$("body").on "click", ".js-toggle-diff-comments", (e) ->
$body.on "click", ".js-toggle-diff-comments", (e) ->
$(@).toggleClass('active')
$(@).closest(".diff-file").find(".notes_holder").toggle()
e.preventDefault()
$(document).off "click", '.js-confirm-danger'
$(document).on "click", '.js-confirm-danger', (e) ->
$document.off "click", '.js-confirm-danger'
$document.on "click", '.js-confirm-danger', (e) ->
e.preventDefault()
btn = $(e.target)
text = btn.data("confirm-danger-message")
......@@ -211,7 +216,7 @@ $ ->
new ConfirmDangerModal(form, text)
$(document).on 'click', 'button', ->
$document.on 'click', 'button', ->
$(this).blur()
$('input[type="search"]').each ->
......@@ -219,7 +224,7 @@ $ ->
$this.attr 'value', $this.val()
return
$(document)
$document
.off 'keyup', 'input[type="search"]'
.on 'keyup', 'input[type="search"]' , (e) ->
$this = $(this)
......@@ -227,7 +232,7 @@ $ ->
$sidebarGutterToggle = $('.js-sidebar-toggle')
$(document)
$document
.off 'breakpoint:change'
.on 'breakpoint:change', (e, breakpoint) ->
if breakpoint is 'sm' or breakpoint is 'xs'
......@@ -239,14 +244,14 @@ $ ->
oldBootstrapBreakpoint = bootstrapBreakpoint
bootstrapBreakpoint = bp.getBreakpointSize()
if bootstrapBreakpoint != oldBootstrapBreakpoint
$(document).trigger('breakpoint:change', [bootstrapBreakpoint])
$document.trigger('breakpoint:change', [bootstrapBreakpoint])
checkInitialSidebarSize = ->
bootstrapBreakpoint = bp.getBreakpointSize()
if bootstrapBreakpoint is "xs" or "sm"
$(document).trigger('breakpoint:change', [bootstrapBreakpoint])
$document.trigger('breakpoint:change', [bootstrapBreakpoint])
$(window)
$window
.off "resize.app"
.on "resize.app", (e) ->
fitSidebarForSize()
......@@ -256,14 +261,14 @@ $ ->
new Aside()
# Sidenav pinning
if $(window).width() < 1440 and $.cookie('pin_nav') is 'true'
if $window.width() < 1440 and $.cookie('pin_nav') is 'true'
$.cookie('pin_nav', 'false', { path: '/' })
$('.page-with-sidebar')
.toggleClass('page-sidebar-collapsed page-sidebar-expanded')
.removeClass('page-sidebar-pinned')
$('.navbar-fixed-top').removeClass('header-pinned-nav')
$(document)
$document
.off 'click', '.js-nav-pin'
.on 'click', '.js-nav-pin', (e) ->
e.preventDefault()
......
......@@ -121,7 +121,11 @@ class @ContributorsMasterGraph extends ContributorsGraph
class @ContributorsAuthorGraph extends ContributorsGraph
constructor: (@data) ->
@width = $('.content').width()/2 - 100
# Don't split graph size in half for mobile devices.
if $(window).width() < 768
@width = $('.content').width() - 80
else
@width = ($('.content').width() / 2) - 100
@height = 200
@x = null
@y = null
......
......@@ -97,6 +97,22 @@
}
}
.sub-header-block {
background-color: $white-light;
border-bottom: 1px solid $white-dark;
padding: 11px 0;
margin-bottom: 11px;
.oneline {
line-height: 35px;
}
&.no-bottom-space {
border-bottom: 0;
margin-bottom: 0;
}
}
.cover-block {
text-align: center;
background: $background-color;
......
......@@ -461,10 +461,12 @@
}
}
.ui-state-active,
.ui-state-hover {
color: $md-link-color;
background-color: $calendar-hover-bg;
.ui-datepicker-calendar {
.ui-state-hover,
.ui-state-active {
color: #fff;
border: 0;
}
}
.ui-datepicker-prev,
......
......@@ -111,10 +111,6 @@
width: 50%;
line-height: 28px;
&.wiki-page {
padding: 16px 10px 11px;
}
/* Small devices (phones, tablets, 768px and lower) */
@media (max-width: $screen-sm-min) {
width: 100%;
......
......@@ -91,7 +91,6 @@
text-decoration: none;
font-weight: normal;
outline: none;
white-space: nowrap;
&:hover,
&:active,
......
......@@ -50,11 +50,10 @@
.label-row {
.label-name {
display: block;
display: inline-block;
margin-bottom: 10px;
@media (min-width: $screen-sm-min) {
display: inline-block;
width: 200px;
margin-bottom: 0;
}
......@@ -63,6 +62,7 @@
.label-description {
display: block;
margin-bottom: 10px;
margin-left: 50px;
@media (min-width: $screen-sm-min) {
display: inline-block;
......
......@@ -14,24 +14,38 @@
font-size: 10px;
}
#contributors-master {
@include make-md-column(12);
svg {
width: 100%;
}
}
#contributors {
.contributors-list {
margin: 0 0 10px;
list-style: none;
padding: 0;
svg {
width: 100%;
}
}
.person {
&:nth-child(even) {
float: right;
}
float: left;
@include make-md-column(6);
margin-top: 10px;
@media (max-width: $screen-sm-min) {
width: 100%;
}
}
.person .spark {
display: block;
background: #f3f3f3;
width: 100%;
}
.person .area-contributor {
......
......@@ -5,6 +5,7 @@ class Admin::AppearancesController < Admin::ApplicationController
end
def preview
render 'preview', layout: 'devise'
end
def create
......
......@@ -36,6 +36,10 @@ class ApplicationController < ActionController::Base
render_404
end
rescue_from Gitlab::Access::AccessDeniedError do |exception|
render_403
end
def redirect_back_or_default(default: root_path, options: {})
redirect_to request.referer.present? ? :back : default, options
end
......
......@@ -21,29 +21,18 @@ module MembershipActions
def leave
@member = membershipable.members.find_by(user_id: current_user)
return render_403 unless @member
Members::DestroyService.new(@member, current_user).execute
source_type = @member.real_source_type.humanize(capitalize: false)
if can?(current_user, action_member_permission(:destroy, @member), @member)
notice =
if @member.request?
"Your access request to the #{source_type} has been withdrawn."
else
"You left the \"#{@member.source.human_name}\" #{source_type}."
end
@member.destroy
redirect_to [:dashboard, @member.real_source_type.tableize], notice: notice
else
if cannot_leave?
alert = "You can not leave the \"#{@member.source.human_name}\" #{source_type}."
alert << " Transfer or delete the #{source_type}."
redirect_to polymorphic_url(membershipable), alert: alert
notice =
if @member.request?
"Your access request to the #{source_type} has been withdrawn."
else
render_403
"You left the \"#{@member.source.human_name}\" #{source_type}."
end
end
redirect_path = @member.request? ? @member.source : [:dashboard, @member.real_source_type.tableize]
redirect_to redirect_path, notice: notice
end
protected
......@@ -51,8 +40,4 @@ module MembershipActions
def membershipable
raise NotImplementedError
end
def cannot_leave?
raise NotImplementedError
end
end
......@@ -36,9 +36,7 @@ class Groups::GroupMembersController < Groups::ApplicationController
def destroy
@group_member = @group.group_members.find(params[:id])
return render_403 unless can?(current_user, :destroy_group_member, @group_member)
@group_member.destroy
Members::DestroyService.new(@group_member, current_user).execute
respond_to do |format|
format.html { redirect_to group_group_members_path(@group), notice: 'User was successfully removed from group.' }
......@@ -68,8 +66,4 @@ class Groups::GroupMembersController < Groups::ApplicationController
# MembershipActions concern
alias_method :membershipable, :group
def cannot_leave?
@group.last_owner?(current_user)
end
end
......@@ -50,9 +50,7 @@ class Projects::ProjectMembersController < Projects::ApplicationController
def destroy
@project_member = @project.project_members.find(params[:id])
return render_403 unless can?(current_user, :destroy_project_member, @project_member)
@project_member.destroy
Members::DestroyService.new(@project_member, current_user).execute
respond_to do |format|
format.html do
......@@ -98,8 +96,4 @@ class Projects::ProjectMembersController < Projects::ApplicationController
# MembershipActions concern
alias_method :membershipable, :project
def cannot_leave?
current_user == @project.owner
end
end
......@@ -50,8 +50,6 @@ module GitlabMarkdownHelper
context[:project] ||= @project
text = Banzai.pre_process(text, context)
html = Banzai.render(text, context)
context.merge!(
......
......@@ -48,7 +48,6 @@ class Member < ActiveRecord::Base
after_create :post_create_hook, unless: [:pending?, :importing?]
after_update :post_update_hook, unless: [:pending?, :importing?]
after_destroy :post_destroy_hook, unless: :pending?
after_destroy :post_decline_request, if: :request?
delegate :name, :username, :email, to: :user, prefix: true
......@@ -188,7 +187,7 @@ class Member < ActiveRecord::Base
end
def send_request
# override in subclass
notification_service.new_access_request(self)
end
def post_create_hook
......@@ -215,10 +214,6 @@ class Member < ActiveRecord::Base
post_create_hook
end
def post_decline_request
# override in subclass
end
def system_hook_service
SystemHooksService.new
end
......
......@@ -33,12 +33,6 @@ class GroupMember < Member
super
end
def send_request
notification_service.new_group_access_request(self)
super
end
def post_create_hook
notification_service.new_group_member(self)
......@@ -64,10 +58,4 @@ class GroupMember < Member
super
end
def post_decline_request
notification_service.decline_group_access_request(self)
super
end
end
......@@ -111,12 +111,6 @@ class ProjectMember < Member
super
end
def send_request
notification_service.new_project_access_request(self)
super
end
def post_create_hook
unless owner?
event_service.join_project(self.project, self.user)
......@@ -152,12 +146,6 @@ class ProjectMember < Member
super
end
def post_decline_request
notification_service.decline_project_access_request(self)
super
end
def event_service
EventCreateService.new
end
......
module Members
class DestroyService < BaseService
attr_accessor :member, :current_user
def initialize(member, user)
@member, @current_user = member, user
end
def execute
unless member && can?(current_user, "destroy_#{member.type.underscore}".to_sym, member)
raise Gitlab::Access::AccessDeniedError
end
member.destroy
if member.request? && member.user != current_user
notification_service.decline_access_request(member)
end
end
end
end
......@@ -83,7 +83,7 @@ module MergeRequests
closes_issue = "Closes ##{iid}"
if merge_request.description.present?
merge_request.description << closes_issue.prepend("\n")
merge_request.description += closes_issue.prepend("\n")
else
merge_request.description = closes_issue
end
......
......@@ -181,15 +181,16 @@ class NotificationService
end
end
# Project access request
def new_project_access_request(project_member)
mailer.member_access_requested_email(project_member.real_source_type, project_member.id).deliver_later
# Members
def new_access_request(member)
mailer.member_access_requested_email(member.real_source_type, member.id).deliver_later
end
def decline_project_access_request(project_member)
mailer.member_access_denied_email(project_member.real_source_type, project_member.project.id, project_member.user.id).deliver_later
def decline_access_request(member)
mailer.member_access_denied_email(member.real_source_type, member.source_id, member.user_id).deliver_later
end
# Project invite
def invite_project_member(project_member, token)
mailer.member_invited_email(project_member.real_source_type, project_member.id, token).deliver_later
end
......@@ -216,15 +217,7 @@ class NotificationService
mailer.member_access_granted_email(project_member.real_source_type, project_member.id).deliver_later
end
# Group access request
def new_group_access_request(group_member)
mailer.member_access_requested_email(group_member.real_source_type, group_member.id).deliver_later
end
def decline_group_access_request(group_member)
mailer.member_access_denied_email(group_member.real_source_type, group_member.group.id, group_member.user.id).deliver_later
end
# Group invite
def invite_group_member(group_member, token)
mailer.member_invited_email(group_member.real_source_type, group_member.id, token).deliver_later
end
......
......@@ -46,7 +46,7 @@
Maximum file size is 1MB. Pages are optimized for a 72x72 px header logo
.form-actions
= f.submit 'Save', class: 'btn btn-save'
= f.submit 'Save', class: 'btn btn-save append-right-10'
- if @appearance.persisted?
= link_to 'Preview last save', preview_admin_appearances_path, class: 'btn', target: '_blank'
......
- page_title "Preview | Appearance"
%h3.page-title
Appearance settings - Preview
%hr
.login-box
.login-heading
%h3 Existing user? Sign in
%form
= text_field_tag :login, nil, class: "form-control top", placeholder: "Username or Email"
= password_field_tag :password, nil, class: "form-control bottom", placeholder: "Password"
= button_tag "Sign in", class: "btn-create btn"
.ui-box
.title
Sign-in page
%div
.login-page
.container
.content
.login-title
%h1= brand_title
%hr
.container
.content
.row
.col-sm-7
.brand-image
= brand_image
.brand_text
= brand_text
.col-sm-4
.login-box
%h3.page-title Sign in
= text_field_tag :login, nil, class: "form-control top", placeholder: "Username or Email"
= password_field_tag :password, nil, class: "form-control bottom", placeholder: "Password"
= button_tag "Sign in", class: "btn-create btn"
......@@ -88,28 +88,17 @@
= select_tag :access_level, options_for_select(GroupMember.access_level_roles), class: "project-access-select select2"
%hr
= button_tag 'Add users to group', class: "btn btn-create"
= render 'shared/members/requests', membership_source: @group, members: @members.request
.panel.panel-default
.panel-heading
%h3.panel-title
Members
%span.badge
#{@group.group_members.count}
%ul.well-list.group-users-list
- @members.each do |member|
- user = member.user
%li{class: dom_class(member), id: (dom_id(user) if user)}
.list-item-name
- if user
%strong
= link_to user.name, admin_user_path(user)
- else
%strong
= member.invite_email
(invited)
%span.pull-right.light
= member.human_access
- if can?(current_user, :destroy_group_member, member)
= link_to group_group_member_path(@group, member), data: { confirm: remove_member_message(member) }, method: :delete, remote: true, class: "btn-xs btn btn-remove", title: 'Remove user from group' do
%i.fa.fa-minus.fa-inverse
%strong= @group.name
group members
%span.badge= @group.members.non_request.size
.pull-right
= link_to icon('pencil-square-o', text: 'Manage Access'), polymorphic_url([@group, :members]), class: "btn btn-xs"
%ul.well-list.group-users-list.content-list
= render partial: 'shared/members/member', collection: @members.non_request, as: :member, locals: { show_controls: false }
.panel-footer
= paginate @members, param_name: 'members_page', theme: 'gitlab'
= paginate @members.non_request, param_name: 'members_page', theme: 'gitlab'
......@@ -135,44 +135,27 @@
- if @group
.panel.panel-default
.panel-heading
%strong #{@group.name}
group members (#{@group.group_members.count})
%strong= @group.name
group members
%span.badge= @group_members.non_request.size
.pull-right
= link_to admin_group_path(@group), class: 'btn btn-xs' do
%i.fa.fa-pencil-square-o
%ul.well-list
- @group_members.each do |member|
= render 'shared/members/member', member: member, show_controls: false
= icon('pencil-square-o', text: 'Manage Access')
%ul.well-list.content-list
= render partial: 'shared/members/member', collection: @group_members.non_request, as: :member, locals: { show_controls: false }
.panel-footer
= paginate @group_members, param_name: 'group_members_page', theme: 'gitlab'
= paginate @group_members.non_request, param_name: 'group_members_page', theme: 'gitlab'
= render 'shared/members/requests', membership_source: @project, members: @project_members.request
.panel.panel-default
.panel-heading
Project members
%small
(#{@project.users.count})
%strong= @project.name
project members
%span.badge= @project.users.size
.pull-right
= link_to namespace_project_project_members_path(@project.namespace, @project), class: "btn btn-xs" do
%i.fa.fa-pencil-square-o
Manage Access
%ul.well-list.project_members
- @project_members.each do |project_member|
- user = project_member.user
%li.project_member
.list-item-name
- if user
%strong
= link_to user.name, admin_user_path(user)
- else
%strong
= project_member.invite_email
(invited)
.pull-right
- if project_member.owner?
%span.light Owner
- else
%span.light= project_member.human_access
= link_to namespace_project_project_member_path(@project.namespace, @project, project_member), data: { confirm: remove_member_message(project_member)}, method: :delete, remote: true, class: "btn btn-sm btn-remove" do
%i.fa.fa-times
= link_to icon('pencil-square-o', text: 'Manage Access'), polymorphic_url([@project, :members]), class: "btn btn-xs"
%ul.well-list.project_members.content-list
= render partial: 'shared/members/member', collection: @project_members.non_request, as: :member, locals: { show_controls: false }
.panel-footer
= paginate @project_members, param_name: 'project_members_page', theme: 'gitlab'
= paginate @project_members.non_request, param_name: 'project_members_page', theme: 'gitlab'
......@@ -17,8 +17,7 @@
.panel-heading
%strong #{@group.name}
group members
%small
(#{@members.total_count})
%span.badge= @members.non_request.size
.controls
= form_tag group_group_members_path(@group), method: :get, class: 'form-inline member-search-form' do
.form-group
......
......@@ -2,7 +2,7 @@
= render 'layouts/nav/admin_settings'
%ul.nav-links.scrolling-tabs
.fade-left
%li.fade-left
= icon('arrow-left')
= nav_link(controller: %w(dashboard admin projects users groups builds runners), html_options: {class: 'home'}) do
= link_to admin_root_path, title: 'Overview', class: 'shortcuts-tree' do
......@@ -37,5 +37,5 @@
= link_to admin_spam_logs_path, title: "Spam Logs" do
%span
Spam Logs
.fade-right
%li.fade-right
= icon('arrow-right')
......@@ -2,7 +2,7 @@
= render 'layouts/nav/group_settings'
%ul.nav-links.scrolling-tabs
.fade-left
%li.fade-left
= icon('arrow-left')
= nav_link(path: 'groups#show', html_options: {class: 'home'}) do
= link_to group_path(@group), title: 'Home' do
......@@ -32,5 +32,5 @@
= link_to group_group_members_path(@group), title: 'Members' do
%span
Members
.fade-right
%li.fade-right
= icon('arrow-right')
- if current_user
- if access = @group.users.find_by(id: current_user.id)
.controls
.dropdown.group-settings-dropdown
%a.dropdown-new.btn.btn-default#group-settings-button{href: '#', 'data-toggle' => 'dropdown'}
= icon('cog')
= icon('caret-down')
%ul.dropdown-menu.dropdown-menu-align-right
- if can?(current_user, :admin_group, @group)
= nav_link(path: 'groups#projects') do
= link_to projects_group_path(@group), title: 'Projects' do
Projects
%li.divider
%li
= link_to edit_group_path(@group) do
Edit Group
- can_edit = can?(current_user, :admin_group, @group)
- member = @group.members.non_request.find_by(user_id: current_user.id)
- can_leave = member && can?(current_user, :destroy_group_member, member)
.controls
.dropdown.group-settings-dropdown
%a.dropdown-new.btn.btn-default#group-settings-button{href: '#', 'data-toggle' => 'dropdown'}
= icon('cog')
= icon('caret-down')
%ul.dropdown-menu.dropdown-menu-align-right
= nav_link(path: 'groups#projects') do
= link_to 'Projects', projects_group_path(@group), title: 'Projects'
%li.divider
- if can_edit
%li
= link_to 'Edit Group', edit_group_path(@group)
- if can_leave
%li
= link_to polymorphic_path([:leave, @group, :members]),
data: { confirm: leave_confirmation_message(@group) }, method: :delete, title: 'Leave group' do
Leave Group
%ul.nav-links.scrolling-tabs
.fade-left
%li.fade-left
= icon('arrow-left')
= nav_link(path: 'profiles#show', html_options: {class: 'home'}) do
= link_to profile_path, title: 'Profile Settings' do
......@@ -44,5 +44,5 @@
= link_to audit_log_profile_path, title: 'Audit Log' do
%span
Audit Log
.fade-right
%li.fade-right
= icon('arrow-right')
......@@ -5,19 +5,20 @@
= icon('cog')
= icon('caret-down')
%ul.dropdown-menu.dropdown-menu-align-right
- is_project_member = @project.users.exists?(current_user.id)
- access = @project.team.max_member_access(current_user.id)
- can_edit = can?(current_user, :admin_project, @project)
-# We don't use @project.team.find_member because it searches for group members too...
- member = @project.members.non_request.find_by(user_id: current_user.id)
- can_leave = member && can?(current_user, :destroy_project_member, member)
= render 'layouts/nav/project_settings', access: access, can_edit: can_edit
= render 'layouts/nav/project_settings', can_edit: can_edit
- if can_edit || is_project_member
- if can_edit || can_leave
%li.divider
- if can_edit
%li
= link_to edit_project_path(@project) do
Edit Project
- if is_project_member
- if can_leave
%li
= link_to polymorphic_path([:leave, @project, :members]),
data: { confirm: leave_confirmation_message(@project) }, method: :delete, title: 'Leave project' do
......@@ -25,7 +26,7 @@
%div{ class: nav_control_class }
%ul.nav-links.scrolling-tabs
.fade-left
%li.fade-left
= icon('arrow-left')
= nav_link(path: 'projects#show', html_options: {class: 'home'}) do
= link_to project_path(@project), title: 'Project', class: 'shortcuts-project' do
......@@ -39,9 +40,9 @@
- if project_nav_tab? :files
= nav_link(controller: %w(tree blob blame edit_tree new_tree find_file commit commits compare repositories tags branches releases network)) do
= link_to project_files_path(@project), title: 'Code', class: 'shortcuts-tree' do
= link_to project_files_path(@project), title: 'Repository', class: 'shortcuts-tree' do
%span
Code
Repository
- if project_nav_tab? :pipelines
= nav_link(controller: [:pipelines, :builds, :environments]) do
......@@ -110,5 +111,5 @@
%li.hidden
= link_to project_commits_path(@project), title: 'Commits', class: 'shortcuts-commits' do
Commits
.fade-right
%li.fade-right
= icon('arrow-right')
......@@ -3,7 +3,7 @@
= link_to namespace_project_project_members_path(@project.namespace, @project), title: 'Members', class: 'team-tab tab' do
%span
Members
- if access && can_edit
- if can_edit
- if @project.allowed_to_share_with_group?
= nav_link(controller: :group_links) do
= link_to namespace_project_group_links_path(@project.namespace, @project), title: "Groups" do
......
......@@ -6,4 +6,4 @@
%ul
- @errors.each do |error|
%li
error
#{error}
Project <%= @project.name %> couldn't be exported.
The errors we encountered were:
- @errors.each do |error|
<%= error %>
\ No newline at end of file
= "Project #{@project.name} couldn't be exported."
= "The errors we encountered were:"
- @errors.each do |error|
#{error}
\ No newline at end of file
.scrolling-tabs-container
.nav-links.sub-nav.scrolling-tabs
%ul{ class: (container_class) }
.fade-left
%li.fade-left
= icon('arrow-left')
= nav_link(controller: %w(tree blob blame edit_tree new_tree find_file)) do
= link_to project_files_path(@project) do
......@@ -26,5 +26,5 @@
= nav_link(controller: [:tags, :releases]) do
= link_to namespace_project_tags_path(@project.namespace, @project) do
Tags
.fade-right
%li.fade-right
= icon('arrow-right')
......@@ -3,7 +3,7 @@
= render "projects/commits/head"
%div{ class: (container_class) }
.row-content-block.second-block.content-component-block
.sub-header-block
Compare branches, tags or commit ranges.
%br
Fill input field with commit id like
......
- @no_container = true
- page_title "#{params[:from]}...#{params[:to]}"
= render "projects/commits/head"
%div{ class: (container_class) }
.sub-header-block.no-bottom-space
= render "form"
.row-content-block
= render "form"
- if @commits.present?
.prepend-top-default
- if @commits.present?
= render "projects/commits/commit_list"
= render "projects/diffs/diffs", diffs: @diffs, project: @project, diff_refs: @diff_refs
- else
.light-well.prepend-top-default
.center
%h4
There isn't anything to compare.
%p.slead
- if params[:to] == params[:from]
%span.label-branch #{params[:from]}
and
%span.label-branch #{params[:to]}
are the same.
- else
You'll need to use different branch names to get a valid comparison.
- else
.light-well
.center
%h4
There isn't anything to compare.
%p.slead
- if params[:to] == params[:from]
%span.label-branch #{params[:from]}
and
%span.label-branch #{params[:to]}
are the same.
- else
You'll need to use different branch names to get a valid comparison.
......@@ -40,9 +40,3 @@
= render 'projects', projects: @forks
- if @private_forks_count > 0
.private-forks-notice
= icon('lock fw', base: 'circle', class: 'fa-lg private-fork-icon')
%strong= pluralize(@private_forks_count, 'private fork')
%span you have no access to.
- page_specific_javascripts asset_path("graphs/application.js")
%ul.nav-links
= nav_link(action: :show) do
= link_to 'Contributors', namespace_project_graph_path
= nav_link(action: :commits) do
= link_to 'Commits', commits_namespace_project_graph_path
= nav_link(action: :languages) do
= link_to 'Languages', languages_namespace_project_graph_path
- if @project.builds_enabled?
= nav_link(action: :ci) do
= link_to ci_namespace_project_graph_path do
Continuous Integration
.nav-links.sub-nav
%ul{ class: (container_class) }
- page_specific_javascripts asset_path("graphs/application.js")
= nav_link(action: :show) do
= link_to 'Contributors', namespace_project_graph_path
= nav_link(action: :commits) do
= link_to 'Commits', commits_namespace_project_graph_path
= nav_link(action: :languages) do
= link_to 'Languages', languages_namespace_project_graph_path
- if @project.builds_enabled?
= nav_link(action: :ci) do
= link_to ci_namespace_project_graph_path do
Continuous Integration
- @no_container = true
- page_title "Continuous Integration", "Graphs"
= render 'head'
.row-content-block.append-bottom-default
.oneline
A collection of graphs for Continuous Integration
#charts.ci-charts
.row
.col-md-6
= render 'projects/graphs/ci/overall'
.col-md-6
= render 'projects/graphs/ci/build_times'
%div{ class: (container_class) }
.sub-header-block
.oneline
A collection of graphs for Continuous Integration
%hr
= render 'projects/graphs/ci/builds'
#charts.ci-charts
.row
.col-md-6
= render 'projects/graphs/ci/overall'
.col-md-6
= render 'projects/graphs/ci/build_times'
%hr
= render 'projects/graphs/ci/builds'
- @no_container = true
- page_title "Commits", "Graphs"
= render 'head'
.row-content-block.append-bottom-default
.tree-ref-holder
= render 'shared/ref_switcher', destination: 'graphs_commits'
%ul.breadcrumb.repo-breadcrumb
= commits_breadcrumbs
%div{ class: (container_class) }
.sub-header-block
.tree-ref-holder
= render 'shared/ref_switcher', destination: 'graphs_commits'
%ul.breadcrumb.repo-breadcrumb
= commits_breadcrumbs
%p.lead
Commit statistics for
%strong #{@ref}
#{@commits_graph.start_date.strftime('%b %d')} - #{@commits_graph.end_date.strftime('%b %d')}
%p.lead
Commit statistics for
%strong #{@ref}
#{@commits_graph.start_date.strftime('%b %d')} - #{@commits_graph.end_date.strftime('%b %d')}
.row
.col-md-6
%ul
%li
%p.lead
%strong #{@commits_graph.commits.size}
commits during
%strong #{@commits_graph.duration}
days
%li
%p.lead
Average
%strong #{@commits_graph.commit_per_day}
commits per day
%li
%p.lead
Contributed by
%strong #{@commits_graph.authors}
authors
.col-md-6
%div
%p.slead
Commits per day of month
%canvas#month-chart
.row
.col-md-6
%div
%p.slead
Commits per day hour (UTC)
%canvas#hour-chart
.col-md-6
%div
%p.slead
Commits per weekday
%canvas#weekday-chart
.row
.col-md-6
%ul
%li
%p.lead
%strong #{@commits_graph.commits.size}
commits during
%strong #{@commits_graph.duration}
days
%li
%p.lead
Average
%strong #{@commits_graph.commit_per_day}
commits per day
%li
%p.lead
Contributed by
%strong #{@commits_graph.authors}
authors
.col-md-6
%div
%p.slead
Commits per day of month
%canvas#month-chart
.row
.col-md-6
%div
%p.slead
Commits per day hour (UTC)
%canvas#hour-chart
.col-md-6
%div
%p.slead
Commits per weekday
%canvas#weekday-chart
:javascript
var responsiveChart = function (selector, data) {
......
- @no_container = true
- page_title "Languages", "Graphs"
= render 'head'
.row-content-block.append-bottom-default
.oneline
Programming languages used in this repository
%div{ class: (container_class) }
.sub-header-block
.oneline
Programming languages used in this repository
.row
.col-md-8
%canvas#languages-chart{ height: 400 }
.col-md-4
%ul.bordered-list
- @languages.each do |language|
%li
%span{ style: "color: #{language[:color]}" }
= icon('circle')
&nbsp;
= language[:label]
.pull-right
= language[:value]
\%
.row
.col-md-8
%canvas#languages-chart{ height: 400 }
.col-md-4
%ul.bordered-list
- @languages.each do |language|
%li
%span{ style: "color: #{language[:color]}" }
= icon('circle')
&nbsp;
= language[:label]
.pull-right
= language[:value]
\%
:javascript
var data = #{@languages.to_json};
......
- @no_container = true
- page_title "Contributors", "Graphs"
= render 'head'
.row-content-block.append-bottom-default
.tree-ref-holder
= render 'shared/ref_switcher', destination: 'graphs'
%ul.breadcrumb.repo-breadcrumb
= commits_breadcrumbs
.loading-graph
.center
%h3.page-title
%i.fa.fa-spinner.fa-spin
Building repository graph.
%p.slead Please wait a moment, this page will automatically refresh when ready.
.stat-graph.hide
.header.clearfix
%h3#date_header.page-title
%p.light
Commits to #{@ref}, excluding merge commits. Limited to 6,000 commits.
%input#brush_change{:type => "hidden"}
.graphs
#contributors-master
#contributors.clearfix
%ol.contributors-list.clearfix
%div{ class: (container_class) }
.sub-header-block
.tree-ref-holder
= render 'shared/ref_switcher', destination: 'graphs'
%ul.breadcrumb.repo-breadcrumb
= commits_breadcrumbs
.loading-graph
.center
%h3.page-title
%i.fa.fa-spinner.fa-spin
Building repository graph.
%p.slead Please wait a moment, this page will automatically refresh when ready.
.stat-graph.hide
.header.clearfix
%h3#date_header.page-title
%p.light
Commits to #{@ref}, excluding merge commits. Limited to 6,000 commits.
%input#brush_change{:type => "hidden"}
.graphs.row
#contributors-master
#contributors.clearfix
%ol.contributors-list.clearfix
......
......@@ -15,5 +15,5 @@
= check_box_tag :filter_ref, 1, @options[:filter_ref]
%span Begin with the selected commit
.network-graph{ data: { url: '#{escape_javascript(@url)}', commit_url: '#{escape_javascript(@commit_url)}', ref: '#{escape_javascript(@ref)}', commit_id: '#{escape_javascript(@commit.id)}' } }
.network-graph{ data: { url: @url, commit_url: @commit_url, ref: @ref, commit_id: @commit.id } }
= spinner nil, true
......@@ -2,8 +2,7 @@
.panel-heading
%strong #{@group.name}
group members
%small
(#{members.count})
%span.badge= members.size
- if can?(current_user, :admin_group_member, @group)
.controls
= link_to 'Manage group members',
......
- @project_group_links.each do |group_links|
- shared_group = group_links.group
- shared_group_users_count = group_links.group.group_members.count
- shared_group_members = shared_group.members.non_request
- shared_group_users_count = shared_group_members.size
.panel.panel-default
.panel-heading
Shared with
......@@ -15,7 +16,7 @@
Edit group members
%ul.content-list
= render partial: 'shared/members/member',
collection: shared_group.group_members.order(access_level: :desc).limit(20),
collection: shared_group_members.order(access_level: :desc).limit(20),
as: :member,
locals: { show_controls: false, show_roles: false }
- if shared_group_users_count > 20
......
......@@ -2,8 +2,7 @@
.panel-heading
%strong #{@project.name}
project members
%small
(#{members.count})
%span.badge= members.size
.controls
= form_tag namespace_project_project_members_path(@project.namespace, @project), method: :get, class: 'form-inline member-search-form' do
.form-group
......
......@@ -18,7 +18,7 @@
= render 'team', members: @project_members.non_request
- if @group
= render "group_members", members: @group_members
= render "group_members", members: @group_members.non_request
- if @project_group_links.any? && @project.allowed_to_share_with_group?
= render "shared_group_members"
......@@ -60,7 +60,7 @@
- unless @repository.gitlab_ci_yml
%li.missing
= link_to add_special_file_path(@project, file_name: '.gitlab-ci.yml') do
Set up CI
Set Up CI
- if @repository.commit
.content-block.second-block.white
......
- if (@page && @page.persisted?)
- if can?(current_user, :create_wiki, @project)
= link_to '#modal-new-wiki', class: "add-new-wiki btn btn-new", "data-toggle" => "modal" do
New Page
= link_to namespace_project_wiki_history_path(@project.namespace, @project, @page), class: "btn" do
Page History
- if can?(current_user, :create_wiki, @project)
......
.top-area
%ul.nav-links
.nav-links.sub-nav
%ul{ class: (container_class) }
= nav_link(html_options: {class: params[:id] == 'home' ? 'active' : '' }) do
= link_to 'Home', namespace_project_wiki_path(@project.namespace, @project, :home)
......@@ -10,9 +10,4 @@
= link_to namespace_project_wikis_git_access_path(@project.namespace, @project) do
Git Access
.nav-controls
- if can?(current_user, :create_wiki, @project)
= link_to '#modal-new-wiki', class: "add-new-wiki btn btn-new", "data-toggle" => "modal" do
New Page
= render 'projects/wikis/new'
= render 'projects/wikis/new'
%div#modal-new-wiki.modal
.modal-dialog
.modal-content
.modal-header
%a.close{href: "#", "data-dismiss" => "modal"} ×
%h3.page-title New Wiki Page
.modal-body
%form.new-wiki-page
.form-group
= label_tag :new_wiki_path do
%span Page slug
= text_field_tag :new_wiki_path, nil, placeholder: 'how-to-setup', class: 'form-control', required: true, :'data-wikis-path' => namespace_project_wikis_path(@project.namespace, @project), autofocus: true
.form-actions
= button_tag 'Create Page', class: 'build-new-wiki btn btn-create'
- @no_container = true
%div{ class: (container_class) }
%div#modal-new-wiki.modal
.modal-dialog
.modal-content
.modal-header
%a.close{href: "#", "data-dismiss" => "modal"} ×
%h3.page-title New Wiki Page
.modal-body
%form.new-wiki-page
.form-group
= label_tag :new_wiki_path do
%span Page slug
= text_field_tag :new_wiki_path, nil, placeholder: 'how-to-setup', class: 'form-control', required: true, :'data-wikis-path' => namespace_project_wikis_path(@project.namespace, @project), autofocus: true
.form-actions
= button_tag 'Create Page', class: 'build-new-wiki btn btn-create'
- @no_container = true
- page_title "Edit", @page.title.capitalize, "Wiki"
= render 'nav'
.top-area
.nav-text.wiki-page
%strong
- if @page.persisted?
= link_to @page.title.capitalize, namespace_project_wiki_path(@project.namespace, @project, @page)
- else
= @page.title.capitalize
%span.light
&middot;
Edit Page
%div{ class: (container_class) }
.top-area
.nav-text
%strong
- if @page.persisted?
= link_to @page.title.capitalize, namespace_project_wiki_path(@project.namespace, @project, @page)
- else
= @page.title.capitalize
%span.light
&middot;
Edit Page
.nav-controls
= render 'main_links'
.nav-controls
- if can?(current_user, :create_wiki, @project)
= link_to '#modal-new-wiki', class: "add-new-wiki btn btn-new", "data-toggle" => "modal" do
New Page
= render 'main_links'
= render 'form'
= render 'form'
- @no_container = true
- page_title "Git Access", "Wiki"
= render 'nav'
.row-content-block
%span.oneline
Git access for
%strong= @project_wiki.path_with_namespace
%div{ class: (container_class) }
.sub-header-block
%span.oneline
Git access for
%strong= @project_wiki.path_with_namespace
.pull-right
= render "shared/clone_panel", project: @project_wiki
.pull-right
= render "shared/clone_panel", project: @project_wiki
.git-empty.prepend-top-default
%fieldset
%legend Install Gollum:
%pre.dark
:preserve
gem install gollum
.prepend-top-default
%fieldset
%legend Install Gollum:
%pre.dark
:preserve
gem install gollum
%legend Clone Your Wiki:
%pre.dark
:preserve
git clone #{ content_tag(:span, default_url_to_repo(@project_wiki), class: 'clone')}
cd #{h @project_wiki.path}
%legend Clone Your Wiki:
%pre.dark
:preserve
git clone #{ content_tag(:span, default_url_to_repo(@project_wiki), class: 'clone')}
cd #{h @project_wiki.path}
%legend Start Gollum And Edit Locally:
%pre.dark
:preserve
gollum
== Sinatra/1.3.5 has taken the stage on 4567 for development with backup from Thin
>> Thin web server (v1.5.0 codename Knife)
>> Maximum connections set to 1024
>> Listening on 0.0.0.0:4567, CTRL+C to stop
%legend Start Gollum And Edit Locally:
%pre.dark
:preserve
gollum
== Sinatra/1.3.5 has taken the stage on 4567 for development with backup from Thin
>> Thin web server (v1.5.0 codename Knife)
>> Maximum connections set to 1024
>> Listening on 0.0.0.0:4567, CTRL+C to stop
- page_title "History", @page.title.capitalize, "Wiki"
= render 'nav'
%div{ class: (container_class) }
.top-area
.nav-text
%strong
= link_to @page.title.capitalize, namespace_project_wiki_path(@project.namespace, @project, @page)
%span.light
&middot;
History
.top-area
.nav-text
%strong
= link_to @page.title.capitalize, namespace_project_wiki_path(@project.namespace, @project, @page)
%span.light
&middot;
History
.table-holder
%table.table
%thead
%tr
%th Page version
%th Author
%th Commit Message
%th Last updated
%th Format
%tbody
- @page.versions.each_with_index do |version, index|
- commit = version
.table-holder
%table.table
%thead
%tr
%td
= link_to project_wiki_path_with_version(@project, @page,
commit.id, index == 0) do
= truncate_sha(commit.id)
%td
= commit.author.name
%td
= commit.message
%td
#{time_ago_with_tooltip(version.authored_date)}
%td
%strong
= @page.page.wiki.page(@page.page.name, commit.id).try(:format)
%th Page version
%th Author
%th Commit Message
%th Last updated
%th Format
%tbody
- @page.versions.each_with_index do |version, index|
- commit = version
%tr
%td
= link_to project_wiki_path_with_version(@project, @page,
commit.id, index == 0) do
= truncate_sha(commit.id)
%td
= commit.author.name
%td
= commit.message
%td
#{time_ago_with_tooltip(version.authored_date)}
%td
%strong
= @page.page.wiki.page(@page.page.name, commit.id).try(:format)
- @no_container = true
- page_title "Pages", "Wiki"
= render 'nav'
%ul.content-list
- @wiki_pages.each do |wiki_page|
%li
= link_to wiki_page.title, namespace_project_wiki_path(@project.namespace, @project, wiki_page)
%small (#{wiki_page.format})
.pull-right
%small Last edited #{time_ago_with_tooltip(wiki_page.commit.authored_date)}
= paginate @wiki_pages, theme: 'gitlab'
%div{ class: (container_class) }
%ul.content-list
- @wiki_pages.each do |wiki_page|
%li
= link_to wiki_page.title, namespace_project_wiki_path(@project.namespace, @project, wiki_page)
%small (#{wiki_page.format})
.pull-right
%small Last edited #{time_ago_with_tooltip(wiki_page.commit.authored_date)}
= paginate @wiki_pages, theme: 'gitlab'
- @no_container = true
- page_title @page.title.capitalize, "Wiki"
= render 'nav'
.top-area
.nav-text
%strong= @page.title.capitalize
%div{ class: (container_class) }
.top-area
.nav-text
%strong= @page.title.capitalize
%span.wiki-last-edit-by
&middot;
last edited by #{@page.commit.author.name} #{time_ago_with_tooltip(@page.commit.authored_date)}
%span.wiki-last-edit-by
&middot;
last edited by #{@page.commit.author.name} #{time_ago_with_tooltip(@page.commit.authored_date)}
.nav-controls
= render 'main_links'
.nav-controls
= render 'main_links'
- if @page.historical?
.warning_message
This is an old version of this page.
You can view the #{link_to "most recent version", namespace_project_wiki_path(@project.namespace, @project, @page)} or browse the #{link_to "history", namespace_project_wiki_history_path(@project.namespace, @project, @page)}.
- if @page.historical?
.warning_message
This is an old version of this page.
You can view the #{link_to "most recent version", namespace_project_wiki_path(@project.namespace, @project, @page)} or browse the #{link_to "history", namespace_project_wiki_history_path(@project.namespace, @project, @page)}.
.wiki-holder.prepend-top-default.append-bottom-default
.wiki
= preserve do
= render_wiki_content(@page)
.wiki-holder.prepend-top-default.append-bottom-default
.wiki
= preserve do
= render_wiki_content(@page)
%ul.nav-links.event-filter.scrolling-tabs
.fade-left
%li.fade-left
= icon('arrow-left')
= event_filter_link EventFilter.push, 'Push events'
= event_filter_link EventFilter.merged, 'Merge events'
= event_filter_link EventFilter.comments, 'Comments'
= event_filter_link EventFilter.team, 'Team'
.fade-right
%li.fade-right
= icon('arrow-right')
......@@ -3,6 +3,6 @@
.panel-heading
%strong= membership_source.name
access requests
%small= "(#{members.size})"
%span.badge= members.size
%ul.content-list
= render partial: 'shared/members/member', collection: members, as: :member
......@@ -16,6 +16,12 @@
= render "shared/projects/project", project: project, skip_namespace: skip_namespace,
avatar: avatar, stars: stars, css_class: css_class, ci: ci, use_creator_avatar: use_creator_avatar,
forks: forks, show_last_commit_as_description: show_last_commit_as_description
- if @private_forks_count && @private_forks_count > 0
%li.project-row.private-forks-notice
= icon('lock fw', base: 'circle', class: 'fa-lg private-fork-icon')
%strong= pluralize(@private_forks_count, 'private fork')
%span you have no access to.
= paginate(projects, remote: remote, theme: "gitlab") if projects.respond_to? :total_pages
- else
.nothing-here-block No projects found
......
......@@ -215,7 +215,7 @@ Settings.gitlab.default_projects_features['container_registry'] = true if Settin
Settings.gitlab.default_projects_features['visibility_level'] = Settings.send(:verify_constant, Gitlab::VisibilityLevel, Settings.gitlab.default_projects_features['visibility_level'], Gitlab::VisibilityLevel::PRIVATE)
Settings.gitlab['repository_downloads_path'] = File.join(Settings.shared['path'], 'cache/archive') if Settings.gitlab['repository_downloads_path'].nil?
Settings.gitlab['restricted_signup_domains'] ||= []
Settings.gitlab['import_sources'] ||= ['github','bitbucket','gitlab','gitorious','google_code','fogbugz','git']
Settings.gitlab['import_sources'] ||= %w[github bitbucket gitlab gitorious google_code fogbugz git gitlab_project]
Settings.gitlab['trusted_proxies'] ||= []
......
......@@ -3,18 +3,18 @@
## User documentation
- [API](api/README.md) Automate GitLab via a simple and powerful API.
- [CI](ci/README.md) GitLab Continuous Integration (CI) getting started, `.gitlab-ci.yml` options, and examples.
- [CI/CD](ci/README.md) GitLab Continuous Integration (CI) and Continuous Delivery (CD) getting started, `.gitlab-ci.yml` options, and examples.
- [GitLab as OAuth2 authentication service provider](integration/oauth_provider.md). It allows you to login to other applications from GitLab.
- [Container Registry](container_registry/README.md) Learn how to use GitLab Container Registry.
- [GitLab Basics](gitlab-basics/README.md) Find step by step how to start working on your commandline and on GitLab.
- [Importing to GitLab](workflow/importing/README.md).
- [Importing and exporting projects between instances](user/project/settings/import_export.md).
- [Markdown](markdown/markdown.md) GitLab's advanced formatting system.
- [Migrating from SVN](workflow/importing/migrating_from_svn.md) Convert a SVN repository to Git and GitLab
- [Migrating from SVN](workflow/importing/migrating_from_svn.md) Convert a SVN repository to Git and GitLab.
- [Permissions](permissions/permissions.md) Learn what each role in a project (external/guest/reporter/developer/master/owner) can do.
- [Profile Settings](profile/README.md)
- [Project Services](project_services/project_services.md) Integrate a project with external services, such as CI and chat.
- [Public access](public_access/public_access.md) Learn how you can allow public and internal access to projects.
- [Container Registry](container_registry/README.md) Learn how to use GitLab Container Registry.
- [SSH](ssh/README.md) Setup your ssh keys and deploy keys for secure access to your projects.
- [Webhooks](web_hooks/web_hooks.md) Let GitLab notify you when new code has been pushed to your project.
- [Workflow](workflow/README.md) Using GitLab functionality and importing projects from GitHub and SVN.
......@@ -25,15 +25,15 @@
external authentication with LDAP, SAML, CAS and additional Omniauth providers.
- [Custom git hooks](hooks/custom_hooks.md) Custom git hooks (on the filesystem) for when webhooks aren't enough.
- [Install](install/README.md) Requirements, directory structures and installation from source.
- [Restart GitLab](administration/restart_gitlab.md) Learn how to restart GitLab and its components
- [Restart GitLab](administration/restart_gitlab.md) Learn how to restart GitLab and its components.
- [Integration](integration/README.md) How to integrate with systems such as JIRA, Redmine, Twitter.
- [Issue closing](customization/issue_closing.md) Customize how to close an issue from commit messages.
- [Libravatar](customization/libravatar.md) Use Libravatar for user avatars.
- [Log system](administration/logs.md) Log system.
- [Environment Variables](administration/environment_variables.md) to configure GitLab.
- [Operations](operations/README.md) Keeping GitLab up and running
- [Operations](operations/README.md) Keeping GitLab up and running.
- [Raketasks](raketasks/README.md) Backups, maintenance, automatic webhook setup and the importing of projects.
- [Repository checks](administration/repository_checks.md) Periodic Git repository checks
- [Repository checks](administration/repository_checks.md) Periodic Git repository checks.
- [Security](security/README.md) Learn what you can do to further secure your GitLab instance.
- [System hooks](system_hooks/system_hooks.md) Notifications when users, projects and keys are changed.
- [Update](update/README.md) Update guides to upgrade your installation.
......@@ -42,11 +42,11 @@
- [Migrate GitLab CI to CE/EE](migrate_ci_to_ce/README.md) Follow this guide to migrate your existing GitLab CI data to GitLab CE/EE.
- [Git LFS configuration](workflow/lfs/lfs_administration.md)
- [Housekeeping](administration/housekeeping.md) Keep your Git repository tidy and fast.
- [GitLab Performance Monitoring](monitoring/performance/introduction.md) Configure GitLab and InfluxDB for measuring performance metrics
- [Monitoring uptime](monitoring/health_check.md) Check the server status using the health check endpoint
- [Sidekiq Troubleshooting](administration/troubleshooting/sidekiq.md) Debug when Sidekiq appears hung and is not processing jobs
- [High Availability](administration/high_availability/README.md) Configure multiple servers for scaling or high availability
- [Container Registry](administration/container_registry.md) Configure Docker Registry with GitLab
- [GitLab Performance Monitoring](monitoring/performance/introduction.md) Configure GitLab and InfluxDB for measuring performance metrics.
- [Monitoring uptime](monitoring/health_check.md) Check the server status using the health check endpoint.
- [Sidekiq Troubleshooting](administration/troubleshooting/sidekiq.md) Debug when Sidekiq appears hung and is not processing jobs.
- [High Availability](administration/high_availability/README.md) Configure multiple servers for scaling or high availability.
- [Container Registry](administration/container_registry.md) Configure Docker Registry with GitLab.
## Contributor documentation
......
......@@ -22,6 +22,7 @@ You can read more about Docker Registry at https://docs.docker.com/registry/intr
- [Disable Container Registry per project](#disable-container-registry-per-project)
- [Disable Container Registry for new projects site-wide](#disable-container-registry-for-new-projects-site-wide)
- [Container Registry storage path](#container-registry-storage-path)
- [Container Registry storage driver](#container-registry-storage-driver)
- [Storage limitations](#storage-limitations)
- [Changelog](#changelog)
......@@ -306,8 +307,12 @@ the Container Registry by themselves, follow the steps below.
## Container Registry storage path
To change the storage path where Docker images will be stored, follow the
steps below.
>**Note:**
For configuring storage in the cloud instead of the filesystem, see the
[storage driver configuration](#container-registry-storage-driver).
If you want to store your images on the filesystem, you can change the storage
path for the Container Registry, follow the steps below.
This path is accessible to:
......@@ -349,6 +354,72 @@ The default location where images are stored in source installations, is
1. Save the file and [restart GitLab][] for the changes to take effect.
## Container Registry storage driver
You can configure the Container Registry to use a different storage backend by
configuring a different storage driver. By default the GitLab Container Registry
is configured to use the filesystem driver, which makes use of [storage path](#container-registry-storage-path)
configuration.
The different supported drivers are:
| Driver | Description |
|------------|-------------------------------------|
| filesystem | Uses a path on the local filesystem |
| azure | Microsoft Azure Blob Storage |
| gcs | Google Cloud Storage |
| s3 | Amazon Simple Storage Service |
| swift | OpenStack Swift Object Storage |
| oss | Aliyun OSS |
Read more about the individual driver's config options in the
[Docker Registry docs][storage-config].
> **Warning** GitLab will not backup Docker images that are not stored on the
filesystem. Remember to enable backups with your object storage provider if
desired.
---
**Omnibus GitLab installations**
To configure the storage driver in Omnibus:
1. Edit `/etc/gitlab/gitlab.rb`:
```ruby
registry['storage'] = {
's3' => {
'accesskey' => 's3-access-key',
'secretkey' => 's3-secret-key-for-access-key',
'bucket' => 'your-s3-bucket'
}
}
```
1. Save the file and [reconfigure GitLab][] for the changes to take effect.
---
**Installations from source**
Configuring the storage driver is done in your registry config YML file created
when you [deployed your docker registry][registry-deploy].
Example:
```
storage:
s3:
accesskey: 'AKIAKIAKI'
secretkey: 'secret123'
bucket: 'gitlab-registry-bucket-AKIAKIAKI'
cache:
blobdescriptor: inmemory
delete:
enabled: true
```
## Storage limitations
Currently, there is no storage limitation, which means a user can upload an
......
......@@ -32,6 +32,7 @@ following locations:
- [Services](services.md)
- [Session](session.md)
- [Settings](settings.md)
- [Sidekiq metrics](sidekiq_metrics.md)
- [System Hooks](system_hooks.md)
- [Tags](tags.md)
- [Users](users.md)
......
......@@ -5,6 +5,7 @@
- [Get started with GitLab CI](quick_start/README.md)
- [CI examples for various languages](examples/README.md)
- [Learn how to enable or disable GitLab CI](enable_or_disable_ci.md)
- [Pipelines and builds](pipelines.md)
- [Environments and deployments](environments.md)
- [Learn how `.gitlab-ci.yml` works](yaml/README.md)
- [Configure a Runner, the application that runs your builds](runners/README.md)
......
......@@ -52,7 +52,7 @@ Clicking on an environment will show the history of deployments.
Only deploys that happen after your `.gitlab-ci.yml` is properly configured will
show up in the environments and deployments lists.
[Pipelines]: quick_start/README.md
[Pipelines]: pipelines.md
[jobs]: yaml/README.md#jobs
[environments]: #environments
[deployments]: #deployments
# Introduction to pipelines and builds
>**Note:**
Introduced in GitLab 8.8.
## Pipelines
A pipeline is a group of [builds] that get executed in [stages] (batches). All
of the builds in a stage are executed in parallel (if there are enough
concurrent [runners]), and if they all succeed, the pipeline moves on to the
next stage. If one of the builds fails, the next stage is not (usually)
executed.
## Builds
Builds are individual runs of [jobs]. Not to be confused with a `build` job or
`build` stage.
## Defining pipelines
Pipelines are defined in `.gitlab-ci.yml` by specifying [jobs] that run in
[stages].
See full [documentation](yaml/README.md#jobs).
## Seeing pipeline status
You can find the current and historical pipeline runs under **Pipelines** for your
project.
## Seeing build status
Clicking on a pipeline will show the builds that were run for that pipeline.
[builds]: #builds
[jobs]: yaml/README.md#jobs
[stages]: yaml/README.md#stages
[runners]: runners/README.md
......@@ -4,41 +4,41 @@
is fully integrated into GitLab itself and is [enabled] by default on all
projects.
The TL;DR version of how GitLab CI works is the following.
---
GitLab offers a [continuous integration][ci] service. If you
[add a `.gitlab-ci.yml` file][yaml] to the root directory of your repository,
and configure your GitLab project to use a [Runner], then each merge request or
push triggers a build.
push triggers your CI [pipeline].
The `.gitlab-ci.yml` file tells the GitLab runner what to do. By default it
runs three [stages]: `build`, `test`, and `deploy`.
The `.gitlab-ci.yml` file tells the GitLab runner what to do. By default it runs
a pipeline with three [stages]: `build`, `test`, and `deploy`. You don't need to
use all three stages; stages with no jobs are simply ignored.
If everything runs OK (no non-zero return values), you'll get a nice green
checkmark associated with the pushed commit or merge request. This makes it
easy to see whether a merge request will cause any of the tests to fail before
easy to see whether a merge request caused any of the tests to fail before
you even look at the code.
Most projects only use GitLab's CI service to run the test suite so that
Most projects use GitLab's CI service to run the test suite so that
developers get immediate feedback if they broke something.
There's a growing trend to use continuous delivery and continuous deployment to
automatically deploy tested code to staging and production environments.
So in brief, the steps needed to have a working CI can be summed up to:
1. Add `.gitlab-ci.yml` to the root directory of your repository
1. Configure a Runner
From there on, on every push to your Git repository, the build will be
automagically started by the Runner and will appear under the project's
`/builds` page.
From there on, on every push to your Git repository, the Runner will
automagically start the pipeline and the pipeline will appear under the
project's `/pipelines` page.
---
This guide assumes that you:
- have a working GitLab instance of version 8.0 or higher or are using
[GitLab.com](https://gitlab.com/users/sign_in)
[GitLab.com](https://gitlab.com)
- have a project in GitLab that you would like to use CI for
Let's break it down to pieces and work on solving the GitLab CI puzzle.
......@@ -57,15 +57,14 @@ On any push to your repository, GitLab will look for the `.gitlab-ci.yml`
file and start builds on _Runners_ according to the contents of the file,
for that commit.
Because `.gitlab-ci.yml` is in the repository, it is version controlled,
old versions still build successfully, forks can easily make use of CI,
branches can have separate builds and you have a single source of truth for CI.
You can read more about the reasons why we are using `.gitlab-ci.yml`
[in our blog about it][blog-ci].
Because `.gitlab-ci.yml` is in the repository and is version controlled, old
versions still build successfully, forks can easily make use of CI, branches can
have different pipelines and jobs, and you have a single source of truth for CI.
You can read more about the reasons why we are using `.gitlab-ci.yml` [in our
blog about it][blog-ci].
**Note:** `.gitlab-ci.yml` is a [YAML](https://en.wikipedia.org/wiki/YAML) file
so you have to pay extra attention to the indentation. Always use spaces, not
tabs.
so you have to pay extra attention to indentation. Always use spaces, not tabs.
### Creating a simple `.gitlab-ci.yml` file
......@@ -108,7 +107,7 @@ If you want to check whether your `.gitlab-ci.yml` file is valid, there is a
Lint tool under the page `/ci/lint` of your GitLab instance. You can also find
the link under **Settings > CI settings** in your project.
For more information and a complete `.gitlab-ci.yml` syntax, please check
For more information and a complete `.gitlab-ci.yml` syntax, please read
[the documentation on .gitlab-ci.yml](../yaml/README.md).
### Push `.gitlab-ci.yml` to GitLab
......@@ -122,7 +121,8 @@ git commit -m "Add .gitlab-ci.yml"
git push origin master
```
Now if you go to the **Builds** page you will see that the builds are pending.
Now if you go to the **Pipelines** page you will see that the pipeline is
pending.
You can also go to the **Commits** page and notice the little clock icon next
to the commit SHA.
......@@ -138,15 +138,14 @@ Notice that there are two jobs pending which are named after what we wrote in
`.gitlab-ci.yml`. The red triangle indicates that there is no Runner configured
yet for these builds.
The next step is to configure a Runner so that it picks the pending jobs.
The next step is to configure a Runner so that it picks the pending builds.
## Configuring a Runner
In GitLab, Runners run the builds that you define in `.gitlab-ci.yml`.
A Runner can be a virtual machine, a VPS, a bare-metal machine, a docker
container or even a cluster of containers. GitLab and the Runners communicate
through an API, so the only needed requirement is that the machine on which the
Runner is configured to have Internet access.
In GitLab, Runners run the builds that you define in `.gitlab-ci.yml`. A Runner
can be a virtual machine, a VPS, a bare-metal machine, a docker container or
even a cluster of containers. GitLab and the Runners communicate through an API,
so the only requirement is that the Runner's machine has Internet access.
A Runner can be specific to a certain project or serve multiple projects in
GitLab. If it serves all projects it's called a _Shared Runner_.
......@@ -188,12 +187,16 @@ To enable **Shared Runners** you have to go to your project's
[Read more on Shared Runners](../runners/README.md).
## Seeing the status of your build
## Seeing the status of your pipeline and builds
After configuring the Runner successfully, you should see the status of your
last commit change from _pending_ to either _running_, _success_ or _failed_.
You can view all builds, by going to the **Builds** page in your project.
You can view all pipelines by going to the **Pipelines** page in your project.
![Commit status](img/pipelines_status.png)
Or you can view all builds, by going to the **Pipelines > Builds** page.
![Commit status](img/builds_status.png)
......@@ -238,3 +241,4 @@ CI with various languages.
[runner]: ../runners/README.md
[enabled]: ../enable_or_disable_ci.md
[stages]: ../yaml/README.md#stages
[pipeline]: ../pipelines.md
......@@ -54,7 +54,7 @@ of your repository and contains definitions of how your project should be built.
The YAML file defines a set of jobs with constraints stating when they should
be run. The jobs are defined as top-level elements with a name and always have
to contain the `script` clause:
to contain at least the `script` clause:
```yaml
job1:
......@@ -165,7 +165,7 @@ stages:
There are also two edge cases worth mentioning:
1. If no `stages` is defined in `.gitlab-ci.yml`, then by default the `build`,
1. If no `stages` are defined in `.gitlab-ci.yml`, then by default the `build`,
`test` and `deploy` are allowed to be used as job's stage by default.
2. If a job doesn't specify a `stage`, the job is assigned the `test` stage.
......
......@@ -26,13 +26,6 @@ Feature: Admin Groups
When I visit group page
Then I should see project shared with group
@javascript
Scenario: Remove user from group
Given we have user "John Doe" in group
When I visit admin group page
And I remove user "John Doe" from group
Then I should not see "John Doe" in team list
@javascript
Scenario: Invite user to a group by e-mail
When I visit admin group page
......
......@@ -5,53 +5,9 @@ Feature: Dashboard Group
And "John Doe" is owner of group "Owned"
And "John Doe" is guest of group "Guest"
# Leave groups
@javascript
Scenario: Owner should be able to leave from group if he is not the last owner
Given "Mary Jane" is owner of group "Owned"
When I visit dashboard groups page
Then I should see group "Owned" in group list
Then I should see group "Guest" in group list
When I click on the "Leave" button for group "Owned"
And I visit dashboard groups page
Then I should not see group "Owned" in group list
Then I should see group "Guest" in group list
@javascript
Scenario: Owner should not be able to leave from group if he is the last owner
Given "Mary Jane" is guest of group "Owned"
When I visit dashboard groups page
Then I should see group "Owned" in group list
Then I should see group "Guest" in group list
When I click on the "Leave" button for group "Owned"
Then I should see the "Can not leave message"
@javascript
Scenario: Guest should be able to leave from group
Given "Mary Jane" is guest of group "Guest"
When I visit dashboard groups page
Then I should see group "Owned" in group list
Then I should see group "Guest" in group list
When I click on the "Leave" button for group "Guest"
When I visit dashboard groups page
Then I should see group "Owned" in group list
Then I should not see group "Guest" in group list
@javascript
Scenario: Guest should be able to leave from group even if he is the only user in the group
When I visit dashboard groups page
Then I should see group "Owned" in group list
Then I should see group "Guest" in group list
When I click on the "Leave" button for group "Guest"
When I visit dashboard groups page
Then I should see group "Owned" in group list
Then I should not see group "Guest" in group list
Scenario: Create a group from dasboard
And I visit dashboard groups page
And I click new group link
And submit form with new group "Samurai" info
Then I should be redirected to group "Samurai" page
And I should see newly created group "Samurai"
......@@ -10,9 +10,9 @@ Feature: Project Active Tab
Then the active main tab should be Home
And no other main tabs should be active
Scenario: On Project Code
Scenario: On Project Repository
Given I visit my project's files page
Then the active main tab should be Code
Then the active main tab should be Repository
And no other main tabs should be active
Scenario: On Project Issues
......@@ -59,46 +59,46 @@ Feature: Project Active Tab
And no other sub navs should be active
And the active main tab should be Settings
# Sub Tabs: Code
# Sub Tabs: Repository
Scenario: On Project Code/Files
Scenario: On Project Repository/Files
Given I visit my project's files page
Then the active sub tab should be Files
And no other sub tabs should be active
And the active main tab should be Code
And the active main tab should be Repository
Scenario: On Project Code/Commits
Scenario: On Project Repository/Commits
Given I visit my project's commits page
Then the active sub tab should be Commits
And no other sub tabs should be active
And the active main tab should be Code
And the active main tab should be Repository
Scenario: On Project Code/Network
Scenario: On Project Repository/Network
Given I visit my project's network page
Then the active sub tab should be Network
And no other sub tabs should be active
And the active main tab should be Code
And the active main tab should be Repository
Scenario: On Project Code/Compare
Scenario: On Project Repository/Compare
Given I visit my project's commits page
And I click the "Compare" tab
Then the active sub tab should be Compare
And no other sub tabs should be active
And the active main tab should be Code
And the active main tab should be Repository
Scenario: On Project Code/Branches
Scenario: On Project Repository/Branches
Given I visit my project's commits page
And I click the "Branches" tab
Then the active sub tab should be Branches
And no other sub tabs should be active
And the active main tab should be Code
And the active main tab should be Repository
Scenario: On Project Code/Tags
Scenario: On Project Repository/Tags
Given I visit my project's commits page
And I click the "Tags" tab
Then the active sub tab should be Tags
And no other sub tabs should be active
And the active main tab should be Code
And the active main tab should be Repository
Scenario: On Project Issues/Browse
Given I visit my project's issues page
......
......@@ -8,21 +8,21 @@ Feature: Project Shortcuts
@javascript
Scenario: Navigate to files tab
Given I press "g" and "f"
Then the active main tab should be Code
Then the active main tab should be Repository
Then the active sub tab should be Files
@javascript
Scenario: Navigate to commits tab
Given I visit my project's files page
Given I press "g" and "c"
Then the active main tab should be Code
Then the active main tab should be Repository
Then the active sub tab should be Commits
@javascript
Scenario: Navigate to network tab
Given I press "g" and "n"
Then the active sub tab should be Network
And the active main tab should be Code
And the active main tab should be Repository
@javascript
Scenario: Navigate to graphs tab
......
......@@ -62,7 +62,7 @@ class Spinach::Features::AdminGroups < Spinach::FeatureSteps
step 'I should see "johndoe@gitlab.com" in team list in every project as "Reporter"' do
page.within ".group-users-list" do
expect(page).to have_content "johndoe@gitlab.com (invited)"
expect(page).to have_content "johndoe@gitlab.com – Invited by"
expect(page).to have_content "Reporter"
end
end
......@@ -92,12 +92,6 @@ class Spinach::Features::AdminGroups < Spinach::FeatureSteps
current_group.add_reporter(user_john)
end
step 'I remove user "John Doe" from group' do
page.within "#user_#{user_john.id}" do
click_link 'Remove user from group'
end
end
step 'I should not see "John Doe" in team list' do
page.within ".group-users-list" do
expect(page).not_to have_content "John Doe"
......
......@@ -4,44 +4,6 @@ class Spinach::Features::DashboardGroup < Spinach::FeatureSteps
include SharedPaths
include SharedUser
# Leave
step 'I click on the "Leave" button for group "Owned"' do
find(:css, 'li', text: "Owner").find(:css, 'i.fa.fa-sign-out').click
# poltergeist always confirms popups.
end
step 'I click on the "Leave" button for group "Guest"' do
find(:css, 'li', text: "Guest").find(:css, 'i.fa.fa-sign-out').click
# poltergeist always confirms popups.
end
step 'I should not see the "Leave" button for group "Owned"' do
expect(find(:css, 'li', text: "Owner")).not_to have_selector(:css, 'i.fa.fa-sign-out')
# poltergeist always confirms popups.
end
step 'I should not see the "Leave" button for groupr "Guest"' do
expect(find(:css, 'li', text: "Guest")).not_to have_selector(:css, 'i.fa.fa-sign-out')
# poltergeist always confirms popups.
end
step 'I should see group "Owned" in group list' do
expect(page).to have_content("Owned")
end
step 'I should not see group "Owned" in group list' do
expect(page).not_to have_content("Owned")
end
step 'I should see group "Guest" in group list' do
expect(page).to have_content("Guest")
end
step 'I should not see group "Guest" in group list' do
expect(page).not_to have_content("Guest")
end
step 'I click new group link' do
click_link "New Group"
end
......@@ -60,8 +22,4 @@ class Spinach::Features::DashboardGroup < Spinach::FeatureSteps
expect(page).to have_content "Samurai"
expect(page).to have_content "Tokugawa Shogunate"
end
step 'I should see the "Can not leave message"' do
expect(page).to have_content "You can not leave the \"Owned\" group."
end
end
......@@ -13,12 +13,12 @@ class Spinach::Features::ProjectFindFile < Spinach::FeatureSteps
end
step 'I should see "find file" page' do
ensure_active_main_tab('Code')
ensure_active_main_tab('Repository')
expect(page).to have_selector('.file-finder-holder', count: 1)
end
step 'I fill in Find by path with "git"' do
ensure_active_main_tab('Code')
ensure_active_main_tab('Repository')
expect(page).to have_selector('.file-finder-holder', count: 1)
end
......
......@@ -8,8 +8,8 @@ module SharedProjectTab
ensure_active_main_tab('Project')
end
step 'the active main tab should be Code' do
ensure_active_main_tab('Code')
step 'the active main tab should be Repository' do
ensure_active_main_tab('Repository')
end
step 'the active main tab should be Graphs' do
......
......@@ -7,10 +7,6 @@ module Banzai
Renderer.render_result(text, context)
end
def self.pre_process(text, context)
Renderer.pre_process(text, context)
end
def self.post_process(html, context)
Renderer.post_process(html, context)
end
......
......@@ -30,13 +30,9 @@ module Banzai
end
def self.render_result(text, context = {})
Pipeline[context[:pipeline]].call(text, context)
end
text = Pipeline[:pre_process].to_html(text, context) if text
def self.pre_process(text, context)
pipeline = Pipeline[:pre_process]
pipeline.to_html(text, context)
Pipeline[context[:pipeline]].call(text, context)
end
# Perform post-processing on an HTML String
......
......@@ -5,6 +5,8 @@
#
module Gitlab
module Access
class AccessDeniedError < StandardError; end
GUEST = 10
REPORTER = 20
DEVELOPER = 30
......
......@@ -118,9 +118,7 @@ describe Groups::GroupMembersController do
it 'cannot removes himself from the group' do
delete :leave, group_id: group
expect(response).to redirect_to(group_path(group))
expect(response).to set_flash[:alert].to "You can not leave the \"#{group.name}\" group. Transfer or delete the group."
expect(group.users).to include user
expect(response.status).to eq(403)
end
end
......@@ -134,7 +132,7 @@ describe Groups::GroupMembersController do
delete :leave, group_id: group
expect(response).to set_flash.to 'Your access request to the group has been withdrawn.'
expect(response).to redirect_to(dashboard_groups_path)
expect(response).to redirect_to(group_path(group))
expect(group.members.request).to be_empty
expect(group.users).not_to include user
end
......
......@@ -171,11 +171,7 @@ describe Projects::ProjectMembersController do
delete :leave, namespace_id: project.namespace,
project_id: project
expect(response).to redirect_to(
namespace_project_path(project.namespace, project)
)
expect(response).to set_flash[:alert].to "You can not leave the \"#{project.human_name}\" project. Transfer or delete the project."
expect(project.users).to include user
expect(response.status).to eq(403)
end
end
......@@ -190,7 +186,7 @@ describe Projects::ProjectMembersController do
project_id: project
expect(response).to set_flash.to 'Your access request to the project has been withdrawn.'
expect(response).to redirect_to(dashboard_projects_path)
expect(response).to redirect_to(namespace_project_path(project.namespace, project))
expect(project.members.request).to be_empty
expect(project.users).not_to include user
end
......
require 'spec_helper'
feature 'Groups > Members > Last owner cannot leave group', feature: true do
let(:owner) { create(:user) }
let(:group) { create(:group) }
background do
group.add_owner(owner)
login_as(owner)
visit group_path(group)
end
scenario 'user does not see a "Leave Group" link' do
expect(page).not_to have_content 'Leave Group'
end
end
require 'spec_helper'
feature 'Groups > Members > Member leaves group', feature: true do
let(:user) { create(:user) }
let(:owner) { create(:user) }
let(:group) { create(:group, :public) }
background do
group.add_owner(owner)
group.add_developer(user)
login_as(user)
visit group_path(group)
end
scenario 'user leaves group' do
click_link 'Leave Group'
expect(current_path).to eq(dashboard_groups_path)
expect(group.users.exists?(user.id)).to be_falsey
end
end
......@@ -42,7 +42,7 @@ feature 'Groups > Members > Owner manages access requests', feature: true do
def expect_visible_access_request(group, user)
expect(group.members.request.exists?(user_id: user)).to be_truthy
expect(page).to have_content "#{group.name} access requests (1)"
expect(page).to have_content "#{group.name} access requests 1"
expect(page).to have_content user.name
end
end
......@@ -21,6 +21,7 @@ feature 'Groups > Members > User requests access', feature: true do
expect(page).to have_content 'Your request for access has been queued for review.'
expect(page).to have_content 'Withdraw Access Request'
expect(page).not_to have_content 'Leave Group'
end
scenario 'user is not listed in the group members page' do
......
......@@ -41,7 +41,7 @@ feature 'Projects > Members > Master manages access requests', feature: true do
def expect_visible_access_request(project, user)
expect(project.members.request.exists?(user_id: user)).to be_truthy
expect(page).to have_content "#{project.name} access requests (1)"
expect(page).to have_content "#{project.name} access requests 1"
expect(page).to have_content user.name
end
end
require 'spec_helper'
feature 'Projects > Members > Member leaves project', feature: true do
let(:user) { create(:user) }
let(:project) { create(:project) }
background do
project.team << [user, :developer]
login_as(user)
visit namespace_project_path(project.namespace, project)
end
scenario 'user leaves project' do
click_link 'Leave Project'
expect(current_path).to eq(dashboard_projects_path)
expect(project.users.exists?(user.id)).to be_falsey
end
end
require 'spec_helper'
feature 'Projects > Members > Owner cannot leave project', feature: true do
let(:owner) { create(:user) }
let(:project) { create(:project) }
background do
project.team << [owner, :owner]
login_as(owner)
visit namespace_project_path(project.namespace, project)
end
scenario 'user does not see a "Leave Project" link' do
expect(page).not_to have_content 'Leave Project'
end
end
......@@ -21,6 +21,7 @@ feature 'Projects > Members > User requests access', feature: true do
expect(page).to have_content 'Your request for access has been queued for review.'
expect(page).to have_content 'Withdraw Access Request'
expect(page).not_to have_content 'Leave Project'
end
scenario 'user is not listed in the project members page' do
......
......@@ -70,22 +70,6 @@ feature 'Project', feature: true do
end
end
describe 'leave project link' do
let(:user) { create(:user) }
let(:project) { create(:project, namespace: user.namespace) }
before do
login_with(user)
project.team.add_user(user, Gitlab::Access::MASTER)
visit namespace_project_path(project.namespace, project)
end
it 'click project-settings and find leave project' do
find('#project-settings-button').click
expect(page).to have_link('Leave Project')
end
end
describe 'project title' do
include WaitForAjax
......
......@@ -134,18 +134,6 @@ describe Member, models: true do
it { is_expected.to respond_to(:user_email) }
end
describe 'Callbacks' do
describe 'after_destroy :post_decline_request, if: :request?' do
let(:member) { create(:project_member, requested_at: Time.now.utc) }
it 'calls #post_decline_request' do
expect(member).to receive(:post_decline_request)
member.destroy
end
end
end
describe ".add_user" do
let!(:user) { create(:user) }
let(:project) { create(:project) }
......
......@@ -61,16 +61,6 @@ describe GroupMember, models: true do
end
end
describe '#post_decline_request' do
it 'calls NotificationService.decline_group_access_request' do
member = create(:group_member, user: build_stubbed(:user), requested_at: Time.now)
expect_any_instance_of(NotificationService).to receive(:decline_group_access_request)
member.__send__(:post_decline_request)
end
end
describe '#real_source_type' do
subject { create(:group_member).real_source_type }
......
......@@ -152,15 +152,5 @@ describe ProjectMember, models: true do
member.__send__(:after_accept_request)
end
end
describe '#post_decline_request' do
it 'calls NotificationService.decline_project_access_request' do
member = create(:project_member, user: build_stubbed(:user), requested_at: Time.now)
expect_any_instance_of(NotificationService).to receive(:decline_project_access_request)
member.__send__(:post_decline_request)
end
end
end
end
require 'spec_helper'
describe Members::DestroyService, services: true do
let(:user) { create(:user) }
let(:project) { create(:project) }
let!(:member) { create(:project_member, source: project) }
context 'when member is nil' do
before do
project.team << [user, :developer]
end
it 'does not destroy the member' do
expect { destroy_member(nil, user) }.to raise_error(Gitlab::Access::AccessDeniedError)
end
end
context 'when current user cannot destroy the given member' do
before do
project.team << [user, :developer]
end
it 'does not destroy the member' do
expect { destroy_member(member, user) }.to raise_error(Gitlab::Access::AccessDeniedError)
end
end
context 'when current user can destroy the given member' do
before do
project.team << [user, :master]
end
it 'destroys the member' do
destroy_member(member, user)
expect(member).to be_destroyed
end
context 'when the given member is a requester' do
before do
member.update_column(:requested_at, Time.now)
end
it 'calls Member#after_decline_request' do
expect_any_instance_of(NotificationService).to receive(:decline_access_request).with(member)
destroy_member(member, user)
end
context 'when current user is the member' do
it 'does not call Member#after_decline_request' do
expect_any_instance_of(NotificationService).not_to receive(:decline_access_request).with(member)
destroy_member(member, member.user)
end
end
context 'when current user is the member and ' do
it 'does not call Member#after_decline_request' do
expect_any_instance_of(NotificationService).not_to receive(:decline_access_request).with(member)
destroy_member(member, member.user)
end
end
end
end
def destroy_member(member, user)
Members::DestroyService.new(member, user).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