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) ...@@ -13,9 +13,11 @@ v 8.9.0 (unreleased)
- Fix endless redirections when accessing user OAuth applications when they are disabled - Fix endless redirections when accessing user OAuth applications when they are disabled
- Allow enabling wiki page events from Webhook management UI - Allow enabling wiki page events from Webhook management UI
- Bump rouge to 1.11.0 - 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 issue with arrow keys not working in search autocomplete dropdown
- Fix an issue where note polling stopped working if a window was in the - Fix an issue where note polling stopped working if a window was in the
background during a refresh. background during a refresh.
- Pre-processing Markdown now only happens when needed
- Make EmailsOnPushWorker use Sidekiq mailers queue - Make EmailsOnPushWorker use Sidekiq mailers queue
- Redesign all Devise emails. !4297 - Redesign all Devise emails. !4297
- Don't show 'Leave Project' to group members - Don't show 'Leave Project' to group members
...@@ -148,6 +150,7 @@ v 8.9.0 (unreleased) ...@@ -148,6 +150,7 @@ v 8.9.0 (unreleased)
- Ensure Todos counters doesn't count Todos for projects pending delete - Ensure Todos counters doesn't count Todos for projects pending delete
- Add left/right arrows horizontal navigation - Add left/right arrows horizontal navigation
- Add tooltip to pin/unpin navbar - Add tooltip to pin/unpin navbar
- Add new sub nav style to Wiki and Graphs sub navigation
v 8.8.5 v 8.8.5
- Import GitHub repositories respecting the API rate limit !4166 - Import GitHub repositories respecting the API rate limit !4166
......
...@@ -48,7 +48,7 @@ gem 'attr_encrypted', '~> 3.0.0' ...@@ -48,7 +48,7 @@ gem 'attr_encrypted', '~> 3.0.0'
gem 'u2f', '~> 0.2.1' gem 'u2f', '~> 0.2.1'
# Browser detection # Browser detection
gem "browser", '~> 2.0.3' gem "browser", '~> 2.2'
# Extracting information from a git repository # Extracting information from a git repository
# Provide access to Gitlab::Git library # Provide access to Gitlab::Git library
...@@ -330,7 +330,7 @@ gem "newrelic_rpm", '~> 3.14' ...@@ -330,7 +330,7 @@ gem "newrelic_rpm", '~> 3.14'
gem 'octokit', '~> 4.3.0' gem 'octokit', '~> 4.3.0'
gem "mail_room", "~> 0.7" gem "mail_room", "~> 0.8"
gem 'email_reply_parser', '~> 0.5.8' gem 'email_reply_parser', '~> 0.5.8'
......
...@@ -98,7 +98,7 @@ GEM ...@@ -98,7 +98,7 @@ GEM
autoprefixer-rails (>= 5.2.1) autoprefixer-rails (>= 5.2.1)
sass (>= 3.3.4) sass (>= 3.3.4)
brakeman (3.3.2) brakeman (3.3.2)
browser (2.0.3) browser (2.2.0)
builder (3.2.2) builder (3.2.2)
bullet (5.0.0) bullet (5.0.0)
activesupport (>= 3.0.0) activesupport (>= 3.0.0)
...@@ -398,7 +398,7 @@ GEM ...@@ -398,7 +398,7 @@ GEM
systemu (~> 2.6.2) systemu (~> 2.6.2)
mail (2.6.4) mail (2.6.4)
mime-types (>= 1.16, < 4) mime-types (>= 1.16, < 4)
mail_room (0.7.0) mail_room (0.8.0)
method_source (0.8.2) method_source (0.8.2)
mime-types (2.99.2) mime-types (2.99.2)
mimemagic (0.3.0) mimemagic (0.3.0)
...@@ -833,7 +833,7 @@ DEPENDENCIES ...@@ -833,7 +833,7 @@ DEPENDENCIES
binding_of_caller (~> 0.7.2) binding_of_caller (~> 0.7.2)
bootstrap-sass (~> 3.3.0) bootstrap-sass (~> 3.3.0)
brakeman (~> 3.3.0) brakeman (~> 3.3.0)
browser (~> 2.0.3) browser (~> 2.2)
bullet bullet
bundler-audit bundler-audit
byebug byebug
...@@ -899,7 +899,7 @@ DEPENDENCIES ...@@ -899,7 +899,7 @@ DEPENDENCIES
license_finder license_finder
licensee (~> 8.0.0) licensee (~> 8.0.0)
loofah (~> 2.0.3) loofah (~> 2.0.3)
mail_room (~> 0.7) mail_room (~> 0.8)
method_source (~> 0.8) method_source (~> 0.8)
minitest (~> 5.7.0) minitest (~> 5.7.0)
mousetrap-rails (~> 1.4.6) mousetrap-rails (~> 1.4.6)
......
...@@ -121,6 +121,11 @@ window.onload = -> ...@@ -121,6 +121,11 @@ window.onload = ->
setTimeout shiftWindow, 100 setTimeout shiftWindow, 100
$ -> $ ->
$document = $(document)
$window = $(window)
$body = $('body')
gl.utils.preventDisabledButtons() gl.utils.preventDisabledButtons()
bootstrapBreakpoint = bp.getBreakpointSize() bootstrapBreakpoint = bp.getBreakpointSize()
...@@ -152,7 +157,7 @@ $ -> ...@@ -152,7 +157,7 @@ $ ->
), 1 ), 1
# Initialize tooltips # Initialize tooltips
$('body').tooltip( $body.tooltip(
selector: '.has-tooltip, [data-toggle="tooltip"]' selector: '.has-tooltip, [data-toggle="tooltip"]'
placement: (_, el) -> placement: (_, el) ->
$el = $(el) $el = $(el)
...@@ -171,7 +176,7 @@ $ -> ...@@ -171,7 +176,7 @@ $ ->
flash.show() flash.show()
# Disable form buttons while a form is submitting # 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"]', @) buttons = $('[type="submit"]', @)
switch e.type switch e.type
...@@ -184,7 +189,7 @@ $ -> ...@@ -184,7 +189,7 @@ $ ->
$('.account-box').hover -> $(@).toggleClass('hover') $('.account-box').hover -> $(@).toggleClass('hover')
# Commit show suppressed diff # 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 = $(@).parent()
$container.next('table').show() $container.next('table').show()
$container.remove() $container.remove()
...@@ -197,13 +202,13 @@ $ -> ...@@ -197,13 +202,13 @@ $ ->
$('.navbar-toggle i').toggleClass("fa-angle-right fa-angle-left") $('.navbar-toggle i').toggleClass("fa-angle-right fa-angle-left")
# Show/hide comments on diff # Show/hide comments on diff
$("body").on "click", ".js-toggle-diff-comments", (e) -> $body.on "click", ".js-toggle-diff-comments", (e) ->
$(@).toggleClass('active') $(@).toggleClass('active')
$(@).closest(".diff-file").find(".notes_holder").toggle() $(@).closest(".diff-file").find(".notes_holder").toggle()
e.preventDefault() e.preventDefault()
$(document).off "click", '.js-confirm-danger' $document.off "click", '.js-confirm-danger'
$(document).on "click", '.js-confirm-danger', (e) -> $document.on "click", '.js-confirm-danger', (e) ->
e.preventDefault() e.preventDefault()
btn = $(e.target) btn = $(e.target)
text = btn.data("confirm-danger-message") text = btn.data("confirm-danger-message")
...@@ -211,7 +216,7 @@ $ -> ...@@ -211,7 +216,7 @@ $ ->
new ConfirmDangerModal(form, text) new ConfirmDangerModal(form, text)
$(document).on 'click', 'button', -> $document.on 'click', 'button', ->
$(this).blur() $(this).blur()
$('input[type="search"]').each -> $('input[type="search"]').each ->
...@@ -219,7 +224,7 @@ $ -> ...@@ -219,7 +224,7 @@ $ ->
$this.attr 'value', $this.val() $this.attr 'value', $this.val()
return return
$(document) $document
.off 'keyup', 'input[type="search"]' .off 'keyup', 'input[type="search"]'
.on 'keyup', 'input[type="search"]' , (e) -> .on 'keyup', 'input[type="search"]' , (e) ->
$this = $(this) $this = $(this)
...@@ -227,7 +232,7 @@ $ -> ...@@ -227,7 +232,7 @@ $ ->
$sidebarGutterToggle = $('.js-sidebar-toggle') $sidebarGutterToggle = $('.js-sidebar-toggle')
$(document) $document
.off 'breakpoint:change' .off 'breakpoint:change'
.on 'breakpoint:change', (e, breakpoint) -> .on 'breakpoint:change', (e, breakpoint) ->
if breakpoint is 'sm' or breakpoint is 'xs' if breakpoint is 'sm' or breakpoint is 'xs'
...@@ -239,14 +244,14 @@ $ -> ...@@ -239,14 +244,14 @@ $ ->
oldBootstrapBreakpoint = bootstrapBreakpoint oldBootstrapBreakpoint = bootstrapBreakpoint
bootstrapBreakpoint = bp.getBreakpointSize() bootstrapBreakpoint = bp.getBreakpointSize()
if bootstrapBreakpoint != oldBootstrapBreakpoint if bootstrapBreakpoint != oldBootstrapBreakpoint
$(document).trigger('breakpoint:change', [bootstrapBreakpoint]) $document.trigger('breakpoint:change', [bootstrapBreakpoint])
checkInitialSidebarSize = -> checkInitialSidebarSize = ->
bootstrapBreakpoint = bp.getBreakpointSize() bootstrapBreakpoint = bp.getBreakpointSize()
if bootstrapBreakpoint is "xs" or "sm" if bootstrapBreakpoint is "xs" or "sm"
$(document).trigger('breakpoint:change', [bootstrapBreakpoint]) $document.trigger('breakpoint:change', [bootstrapBreakpoint])
$(window) $window
.off "resize.app" .off "resize.app"
.on "resize.app", (e) -> .on "resize.app", (e) ->
fitSidebarForSize() fitSidebarForSize()
...@@ -256,14 +261,14 @@ $ -> ...@@ -256,14 +261,14 @@ $ ->
new Aside() new Aside()
# Sidenav pinning # 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: '/' }) $.cookie('pin_nav', 'false', { path: '/' })
$('.page-with-sidebar') $('.page-with-sidebar')
.toggleClass('page-sidebar-collapsed page-sidebar-expanded') .toggleClass('page-sidebar-collapsed page-sidebar-expanded')
.removeClass('page-sidebar-pinned') .removeClass('page-sidebar-pinned')
$('.navbar-fixed-top').removeClass('header-pinned-nav') $('.navbar-fixed-top').removeClass('header-pinned-nav')
$(document) $document
.off 'click', '.js-nav-pin' .off 'click', '.js-nav-pin'
.on 'click', '.js-nav-pin', (e) -> .on 'click', '.js-nav-pin', (e) ->
e.preventDefault() e.preventDefault()
......
...@@ -121,7 +121,11 @@ class @ContributorsMasterGraph extends ContributorsGraph ...@@ -121,7 +121,11 @@ class @ContributorsMasterGraph extends ContributorsGraph
class @ContributorsAuthorGraph extends ContributorsGraph class @ContributorsAuthorGraph extends ContributorsGraph
constructor: (@data) -> 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 @height = 200
@x = null @x = null
@y = null @y = null
......
...@@ -97,6 +97,22 @@ ...@@ -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 { .cover-block {
text-align: center; text-align: center;
background: $background-color; background: $background-color;
......
...@@ -461,10 +461,12 @@ ...@@ -461,10 +461,12 @@
} }
} }
.ui-state-active, .ui-datepicker-calendar {
.ui-state-hover { .ui-state-hover,
color: $md-link-color; .ui-state-active {
background-color: $calendar-hover-bg; color: #fff;
border: 0;
}
} }
.ui-datepicker-prev, .ui-datepicker-prev,
......
...@@ -111,10 +111,6 @@ ...@@ -111,10 +111,6 @@
width: 50%; width: 50%;
line-height: 28px; line-height: 28px;
&.wiki-page {
padding: 16px 10px 11px;
}
/* Small devices (phones, tablets, 768px and lower) */ /* Small devices (phones, tablets, 768px and lower) */
@media (max-width: $screen-sm-min) { @media (max-width: $screen-sm-min) {
width: 100%; width: 100%;
......
...@@ -91,7 +91,6 @@ ...@@ -91,7 +91,6 @@
text-decoration: none; text-decoration: none;
font-weight: normal; font-weight: normal;
outline: none; outline: none;
white-space: nowrap;
&:hover, &:hover,
&:active, &:active,
......
...@@ -50,11 +50,10 @@ ...@@ -50,11 +50,10 @@
.label-row { .label-row {
.label-name { .label-name {
display: block; display: inline-block;
margin-bottom: 10px; margin-bottom: 10px;
@media (min-width: $screen-sm-min) { @media (min-width: $screen-sm-min) {
display: inline-block;
width: 200px; width: 200px;
margin-bottom: 0; margin-bottom: 0;
} }
...@@ -63,6 +62,7 @@ ...@@ -63,6 +62,7 @@
.label-description { .label-description {
display: block; display: block;
margin-bottom: 10px; margin-bottom: 10px;
margin-left: 50px;
@media (min-width: $screen-sm-min) { @media (min-width: $screen-sm-min) {
display: inline-block; display: inline-block;
......
...@@ -14,24 +14,38 @@ ...@@ -14,24 +14,38 @@
font-size: 10px; font-size: 10px;
} }
#contributors-master {
@include make-md-column(12);
svg {
width: 100%;
}
}
#contributors { #contributors {
.contributors-list { .contributors-list {
margin: 0 0 10px; margin: 0 0 10px;
list-style: none; list-style: none;
padding: 0; padding: 0;
svg {
width: 100%;
}
} }
.person { .person {
&:nth-child(even) { @include make-md-column(6);
float: right;
}
float: left;
margin-top: 10px; margin-top: 10px;
@media (max-width: $screen-sm-min) {
width: 100%;
}
} }
.person .spark { .person .spark {
display: block; display: block;
background: #f3f3f3; background: #f3f3f3;
width: 100%;
} }
.person .area-contributor { .person .area-contributor {
......
...@@ -5,6 +5,7 @@ class Admin::AppearancesController < Admin::ApplicationController ...@@ -5,6 +5,7 @@ class Admin::AppearancesController < Admin::ApplicationController
end end
def preview def preview
render 'preview', layout: 'devise'
end end
def create def create
......
...@@ -36,6 +36,10 @@ class ApplicationController < ActionController::Base ...@@ -36,6 +36,10 @@ class ApplicationController < ActionController::Base
render_404 render_404
end end
rescue_from Gitlab::Access::AccessDeniedError do |exception|
render_403
end
def redirect_back_or_default(default: root_path, options: {}) def redirect_back_or_default(default: root_path, options: {})
redirect_to request.referer.present? ? :back : default, options redirect_to request.referer.present? ? :back : default, options
end end
......
...@@ -21,29 +21,18 @@ module MembershipActions ...@@ -21,29 +21,18 @@ module MembershipActions
def leave def leave
@member = membershipable.members.find_by(user_id: current_user) @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) source_type = @member.real_source_type.humanize(capitalize: false)
notice =
if can?(current_user, action_member_permission(:destroy, @member), @member) if @member.request?
notice = "Your access request to the #{source_type} has been withdrawn."
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
else else
render_403 "You left the \"#{@member.source.human_name}\" #{source_type}."
end end
end redirect_path = @member.request? ? @member.source : [:dashboard, @member.real_source_type.tableize]
redirect_to redirect_path, notice: notice
end end
protected protected
...@@ -51,8 +40,4 @@ module MembershipActions ...@@ -51,8 +40,4 @@ module MembershipActions
def membershipable def membershipable
raise NotImplementedError raise NotImplementedError
end end
def cannot_leave?
raise NotImplementedError
end
end end
...@@ -36,9 +36,7 @@ class Groups::GroupMembersController < Groups::ApplicationController ...@@ -36,9 +36,7 @@ class Groups::GroupMembersController < Groups::ApplicationController
def destroy def destroy
@group_member = @group.group_members.find(params[:id]) @group_member = @group.group_members.find(params[:id])
return render_403 unless can?(current_user, :destroy_group_member, @group_member) Members::DestroyService.new(@group_member, current_user).execute
@group_member.destroy
respond_to do |format| respond_to do |format|
format.html { redirect_to group_group_members_path(@group), notice: 'User was successfully removed from group.' } 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 ...@@ -68,8 +66,4 @@ class Groups::GroupMembersController < Groups::ApplicationController
# MembershipActions concern # MembershipActions concern
alias_method :membershipable, :group alias_method :membershipable, :group
def cannot_leave?
@group.last_owner?(current_user)
end
end end
...@@ -50,9 +50,7 @@ class Projects::ProjectMembersController < Projects::ApplicationController ...@@ -50,9 +50,7 @@ class Projects::ProjectMembersController < Projects::ApplicationController
def destroy def destroy
@project_member = @project.project_members.find(params[:id]) @project_member = @project.project_members.find(params[:id])
return render_403 unless can?(current_user, :destroy_project_member, @project_member) Members::DestroyService.new(@project_member, current_user).execute
@project_member.destroy
respond_to do |format| respond_to do |format|
format.html do format.html do
...@@ -98,8 +96,4 @@ class Projects::ProjectMembersController < Projects::ApplicationController ...@@ -98,8 +96,4 @@ class Projects::ProjectMembersController < Projects::ApplicationController
# MembershipActions concern # MembershipActions concern
alias_method :membershipable, :project alias_method :membershipable, :project
def cannot_leave?
current_user == @project.owner
end
end end
...@@ -50,8 +50,6 @@ module GitlabMarkdownHelper ...@@ -50,8 +50,6 @@ module GitlabMarkdownHelper
context[:project] ||= @project context[:project] ||= @project
text = Banzai.pre_process(text, context)
html = Banzai.render(text, context) html = Banzai.render(text, context)
context.merge!( context.merge!(
......
...@@ -48,7 +48,6 @@ class Member < ActiveRecord::Base ...@@ -48,7 +48,6 @@ class Member < ActiveRecord::Base
after_create :post_create_hook, unless: [:pending?, :importing?] after_create :post_create_hook, unless: [:pending?, :importing?]
after_update :post_update_hook, unless: [:pending?, :importing?] after_update :post_update_hook, unless: [:pending?, :importing?]
after_destroy :post_destroy_hook, unless: :pending? after_destroy :post_destroy_hook, unless: :pending?
after_destroy :post_decline_request, if: :request?
delegate :name, :username, :email, to: :user, prefix: true delegate :name, :username, :email, to: :user, prefix: true
...@@ -188,7 +187,7 @@ class Member < ActiveRecord::Base ...@@ -188,7 +187,7 @@ class Member < ActiveRecord::Base
end end
def send_request def send_request
# override in subclass notification_service.new_access_request(self)
end end
def post_create_hook def post_create_hook
...@@ -215,10 +214,6 @@ class Member < ActiveRecord::Base ...@@ -215,10 +214,6 @@ class Member < ActiveRecord::Base
post_create_hook post_create_hook
end end
def post_decline_request
# override in subclass
end
def system_hook_service def system_hook_service
SystemHooksService.new SystemHooksService.new
end end
......
...@@ -33,12 +33,6 @@ class GroupMember < Member ...@@ -33,12 +33,6 @@ class GroupMember < Member
super super
end end
def send_request
notification_service.new_group_access_request(self)
super
end
def post_create_hook def post_create_hook
notification_service.new_group_member(self) notification_service.new_group_member(self)
...@@ -64,10 +58,4 @@ class GroupMember < Member ...@@ -64,10 +58,4 @@ class GroupMember < Member
super super
end end
def post_decline_request
notification_service.decline_group_access_request(self)
super
end
end end
...@@ -111,12 +111,6 @@ class ProjectMember < Member ...@@ -111,12 +111,6 @@ class ProjectMember < Member
super super
end end
def send_request
notification_service.new_project_access_request(self)
super
end
def post_create_hook def post_create_hook
unless owner? unless owner?
event_service.join_project(self.project, self.user) event_service.join_project(self.project, self.user)
...@@ -152,12 +146,6 @@ class ProjectMember < Member ...@@ -152,12 +146,6 @@ class ProjectMember < Member
super super
end end
def post_decline_request
notification_service.decline_project_access_request(self)
super
end
def event_service def event_service
EventCreateService.new EventCreateService.new
end 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 ...@@ -83,7 +83,7 @@ module MergeRequests
closes_issue = "Closes ##{iid}" closes_issue = "Closes ##{iid}"
if merge_request.description.present? if merge_request.description.present?
merge_request.description << closes_issue.prepend("\n") merge_request.description += closes_issue.prepend("\n")
else else
merge_request.description = closes_issue merge_request.description = closes_issue
end end
......
...@@ -181,15 +181,16 @@ class NotificationService ...@@ -181,15 +181,16 @@ class NotificationService
end end
end end
# Project access request # Members
def new_project_access_request(project_member) def new_access_request(member)
mailer.member_access_requested_email(project_member.real_source_type, project_member.id).deliver_later mailer.member_access_requested_email(member.real_source_type, member.id).deliver_later
end end
def decline_project_access_request(project_member) def decline_access_request(member)
mailer.member_access_denied_email(project_member.real_source_type, project_member.project.id, project_member.user.id).deliver_later mailer.member_access_denied_email(member.real_source_type, member.source_id, member.user_id).deliver_later
end end
# Project invite
def invite_project_member(project_member, token) def invite_project_member(project_member, token)
mailer.member_invited_email(project_member.real_source_type, project_member.id, token).deliver_later mailer.member_invited_email(project_member.real_source_type, project_member.id, token).deliver_later
end end
...@@ -216,15 +217,7 @@ class NotificationService ...@@ -216,15 +217,7 @@ class NotificationService
mailer.member_access_granted_email(project_member.real_source_type, project_member.id).deliver_later mailer.member_access_granted_email(project_member.real_source_type, project_member.id).deliver_later
end end
# Group access request # Group invite
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
def invite_group_member(group_member, token) def invite_group_member(group_member, token)
mailer.member_invited_email(group_member.real_source_type, group_member.id, token).deliver_later mailer.member_invited_email(group_member.real_source_type, group_member.id, token).deliver_later
end end
......
...@@ -46,7 +46,7 @@ ...@@ -46,7 +46,7 @@
Maximum file size is 1MB. Pages are optimized for a 72x72 px header logo Maximum file size is 1MB. Pages are optimized for a 72x72 px header logo
.form-actions .form-actions
= f.submit 'Save', class: 'btn btn-save' = f.submit 'Save', class: 'btn btn-save append-right-10'
- if @appearance.persisted? - if @appearance.persisted?
= link_to 'Preview last save', preview_admin_appearances_path, class: 'btn', target: '_blank' = link_to 'Preview last save', preview_admin_appearances_path, class: 'btn', target: '_blank'
......
- page_title "Preview | Appearance" - page_title "Preview | Appearance"
%h3.page-title .login-box
Appearance settings - Preview .login-heading
%hr %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 @@ ...@@ -88,28 +88,17 @@
= select_tag :access_level, options_for_select(GroupMember.access_level_roles), class: "project-access-select select2" = select_tag :access_level, options_for_select(GroupMember.access_level_roles), class: "project-access-select select2"
%hr %hr
= button_tag 'Add users to group', class: "btn btn-create" = button_tag 'Add users to group', class: "btn btn-create"
= render 'shared/members/requests', membership_source: @group, members: @members.request
.panel.panel-default .panel.panel-default
.panel-heading .panel-heading
%h3.panel-title %strong= @group.name
Members group members
%span.badge %span.badge= @group.members.non_request.size
#{@group.group_members.count} .pull-right
%ul.well-list.group-users-list = link_to icon('pencil-square-o', text: 'Manage Access'), polymorphic_url([@group, :members]), class: "btn btn-xs"
- @members.each do |member| %ul.well-list.group-users-list.content-list
- user = member.user = render partial: 'shared/members/member', collection: @members.non_request, as: :member, locals: { show_controls: false }
%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
.panel-footer .panel-footer
= paginate @members, param_name: 'members_page', theme: 'gitlab' = paginate @members.non_request, param_name: 'members_page', theme: 'gitlab'
...@@ -135,44 +135,27 @@ ...@@ -135,44 +135,27 @@
- if @group - if @group
.panel.panel-default .panel.panel-default
.panel-heading .panel-heading
%strong #{@group.name} %strong= @group.name
group members (#{@group.group_members.count}) group members
%span.badge= @group_members.non_request.size
.pull-right .pull-right
= link_to admin_group_path(@group), class: 'btn btn-xs' do = link_to admin_group_path(@group), class: 'btn btn-xs' do
%i.fa.fa-pencil-square-o = icon('pencil-square-o', text: 'Manage Access')
%ul.well-list %ul.well-list.content-list
- @group_members.each do |member| = render partial: 'shared/members/member', collection: @group_members.non_request, as: :member, locals: { show_controls: false }
= render 'shared/members/member', member: member, show_controls: false
.panel-footer .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.panel-default
.panel-heading .panel-heading
Project members %strong= @project.name
%small project members
(#{@project.users.count}) %span.badge= @project.users.size
.pull-right .pull-right
= link_to namespace_project_project_members_path(@project.namespace, @project), class: "btn btn-xs" do = link_to icon('pencil-square-o', text: 'Manage Access'), polymorphic_url([@project, :members]), class: "btn btn-xs"
%i.fa.fa-pencil-square-o %ul.well-list.project_members.content-list
Manage Access = render partial: 'shared/members/member', collection: @project_members.non_request, as: :member, locals: { show_controls: false }
%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
.panel-footer .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 @@ ...@@ -17,8 +17,7 @@
.panel-heading .panel-heading
%strong #{@group.name} %strong #{@group.name}
group members group members
%small %span.badge= @members.non_request.size
(#{@members.total_count})
.controls .controls
= form_tag group_group_members_path(@group), method: :get, class: 'form-inline member-search-form' do = form_tag group_group_members_path(@group), method: :get, class: 'form-inline member-search-form' do
.form-group .form-group
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
= render 'layouts/nav/admin_settings' = render 'layouts/nav/admin_settings'
%ul.nav-links.scrolling-tabs %ul.nav-links.scrolling-tabs
.fade-left %li.fade-left
= icon('arrow-left') = icon('arrow-left')
= nav_link(controller: %w(dashboard admin projects users groups builds runners), html_options: {class: 'home'}) do = 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 = link_to admin_root_path, title: 'Overview', class: 'shortcuts-tree' do
...@@ -37,5 +37,5 @@ ...@@ -37,5 +37,5 @@
= link_to admin_spam_logs_path, title: "Spam Logs" do = link_to admin_spam_logs_path, title: "Spam Logs" do
%span %span
Spam Logs Spam Logs
.fade-right %li.fade-right
= icon('arrow-right') = icon('arrow-right')
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
= render 'layouts/nav/group_settings' = render 'layouts/nav/group_settings'
%ul.nav-links.scrolling-tabs %ul.nav-links.scrolling-tabs
.fade-left %li.fade-left
= icon('arrow-left') = icon('arrow-left')
= nav_link(path: 'groups#show', html_options: {class: 'home'}) do = nav_link(path: 'groups#show', html_options: {class: 'home'}) do
= link_to group_path(@group), title: 'Home' do = link_to group_path(@group), title: 'Home' do
...@@ -32,5 +32,5 @@ ...@@ -32,5 +32,5 @@
= link_to group_group_members_path(@group), title: 'Members' do = link_to group_group_members_path(@group), title: 'Members' do
%span %span
Members Members
.fade-right %li.fade-right
= icon('arrow-right') = icon('arrow-right')
- if current_user - if current_user
- if access = @group.users.find_by(id: current_user.id) - can_edit = can?(current_user, :admin_group, @group)
.controls - member = @group.members.non_request.find_by(user_id: current_user.id)
.dropdown.group-settings-dropdown - can_leave = member && can?(current_user, :destroy_group_member, member)
%a.dropdown-new.btn.btn-default#group-settings-button{href: '#', 'data-toggle' => 'dropdown'}
= icon('cog') .controls
= icon('caret-down') .dropdown.group-settings-dropdown
%ul.dropdown-menu.dropdown-menu-align-right %a.dropdown-new.btn.btn-default#group-settings-button{href: '#', 'data-toggle' => 'dropdown'}
- if can?(current_user, :admin_group, @group) = icon('cog')
= nav_link(path: 'groups#projects') do = icon('caret-down')
= link_to projects_group_path(@group), title: 'Projects' do %ul.dropdown-menu.dropdown-menu-align-right
Projects = nav_link(path: 'groups#projects') do
%li.divider = link_to 'Projects', projects_group_path(@group), title: 'Projects'
%li %li.divider
= link_to edit_group_path(@group) do - if can_edit
Edit Group %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 %ul.nav-links.scrolling-tabs
.fade-left %li.fade-left
= icon('arrow-left') = icon('arrow-left')
= nav_link(path: 'profiles#show', html_options: {class: 'home'}) do = nav_link(path: 'profiles#show', html_options: {class: 'home'}) do
= link_to profile_path, title: 'Profile Settings' do = link_to profile_path, title: 'Profile Settings' do
...@@ -44,5 +44,5 @@ ...@@ -44,5 +44,5 @@
= link_to audit_log_profile_path, title: 'Audit Log' do = link_to audit_log_profile_path, title: 'Audit Log' do
%span %span
Audit Log Audit Log
.fade-right %li.fade-right
= icon('arrow-right') = icon('arrow-right')
...@@ -5,19 +5,20 @@ ...@@ -5,19 +5,20 @@
= icon('cog') = icon('cog')
= icon('caret-down') = icon('caret-down')
%ul.dropdown-menu.dropdown-menu-align-right %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) - 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 %li.divider
- if can_edit - if can_edit
%li %li
= link_to edit_project_path(@project) do = link_to edit_project_path(@project) do
Edit Project Edit Project
- if is_project_member - if can_leave
%li %li
= link_to polymorphic_path([:leave, @project, :members]), = link_to polymorphic_path([:leave, @project, :members]),
data: { confirm: leave_confirmation_message(@project) }, method: :delete, title: 'Leave project' do data: { confirm: leave_confirmation_message(@project) }, method: :delete, title: 'Leave project' do
...@@ -25,7 +26,7 @@ ...@@ -25,7 +26,7 @@
%div{ class: nav_control_class } %div{ class: nav_control_class }
%ul.nav-links.scrolling-tabs %ul.nav-links.scrolling-tabs
.fade-left %li.fade-left
= icon('arrow-left') = icon('arrow-left')
= nav_link(path: 'projects#show', html_options: {class: 'home'}) do = nav_link(path: 'projects#show', html_options: {class: 'home'}) do
= link_to project_path(@project), title: 'Project', class: 'shortcuts-project' do = link_to project_path(@project), title: 'Project', class: 'shortcuts-project' do
...@@ -39,9 +40,9 @@ ...@@ -39,9 +40,9 @@
- if project_nav_tab? :files - 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 = 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 %span
Code Repository
- if project_nav_tab? :pipelines - if project_nav_tab? :pipelines
= nav_link(controller: [:pipelines, :builds, :environments]) do = nav_link(controller: [:pipelines, :builds, :environments]) do
...@@ -110,5 +111,5 @@ ...@@ -110,5 +111,5 @@
%li.hidden %li.hidden
= link_to project_commits_path(@project), title: 'Commits', class: 'shortcuts-commits' do = link_to project_commits_path(@project), title: 'Commits', class: 'shortcuts-commits' do
Commits Commits
.fade-right %li.fade-right
= icon('arrow-right') = icon('arrow-right')
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
= link_to namespace_project_project_members_path(@project.namespace, @project), title: 'Members', class: 'team-tab tab' do = link_to namespace_project_project_members_path(@project.namespace, @project), title: 'Members', class: 'team-tab tab' do
%span %span
Members Members
- if access && can_edit - if can_edit
- if @project.allowed_to_share_with_group? - if @project.allowed_to_share_with_group?
= nav_link(controller: :group_links) do = nav_link(controller: :group_links) do
= link_to namespace_project_group_links_path(@project.namespace, @project), title: "Groups" do = link_to namespace_project_group_links_path(@project.namespace, @project), title: "Groups" do
......
...@@ -6,4 +6,4 @@ ...@@ -6,4 +6,4 @@
%ul %ul
- @errors.each do |error| - @errors.each do |error|
%li %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 .scrolling-tabs-container
.nav-links.sub-nav.scrolling-tabs .nav-links.sub-nav.scrolling-tabs
%ul{ class: (container_class) } %ul{ class: (container_class) }
.fade-left %li.fade-left
= icon('arrow-left') = icon('arrow-left')
= nav_link(controller: %w(tree blob blame edit_tree new_tree find_file)) do = nav_link(controller: %w(tree blob blame edit_tree new_tree find_file)) do
= link_to project_files_path(@project) do = link_to project_files_path(@project) do
...@@ -26,5 +26,5 @@ ...@@ -26,5 +26,5 @@
= nav_link(controller: [:tags, :releases]) do = nav_link(controller: [:tags, :releases]) do
= link_to namespace_project_tags_path(@project.namespace, @project) do = link_to namespace_project_tags_path(@project.namespace, @project) do
Tags Tags
.fade-right %li.fade-right
= icon('arrow-right') = icon('arrow-right')
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
= render "projects/commits/head" = render "projects/commits/head"
%div{ class: (container_class) } %div{ class: (container_class) }
.row-content-block.second-block.content-component-block .sub-header-block
Compare branches, tags or commit ranges. Compare branches, tags or commit ranges.
%br %br
Fill input field with commit id like Fill input field with commit id like
......
- @no_container = true
- page_title "#{params[:from]}...#{params[:to]}" - page_title "#{params[:from]}...#{params[:to]}"
= render "projects/commits/head" = render "projects/commits/head"
%div{ class: (container_class) }
.sub-header-block.no-bottom-space
= render "form"
.row-content-block - if @commits.present?
= render "form"
- if @commits.present?
.prepend-top-default
= render "projects/commits/commit_list" = render "projects/commits/commit_list"
= render "projects/diffs/diffs", diffs: @diffs, project: @project, diff_refs: @diff_refs = render "projects/diffs/diffs", diffs: @diffs, project: @project, diff_refs: @diff_refs
- else - else
.light-well.prepend-top-default .light-well
.center .center
%h4 %h4
There isn't anything to compare. There isn't anything to compare.
%p.slead %p.slead
- if params[:to] == params[:from] - if params[:to] == params[:from]
%span.label-branch #{params[:from]} %span.label-branch #{params[:from]}
and and
%span.label-branch #{params[:to]} %span.label-branch #{params[:to]}
are the same. are the same.
- else - else
You'll need to use different branch names to get a valid comparison. You'll need to use different branch names to get a valid comparison.
...@@ -40,9 +40,3 @@ ...@@ -40,9 +40,3 @@
= render 'projects', projects: @forks = 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") .nav-links.sub-nav
%ul.nav-links %ul{ class: (container_class) }
= nav_link(action: :show) do
= link_to 'Contributors', namespace_project_graph_path - page_specific_javascripts asset_path("graphs/application.js")
= nav_link(action: :commits) do = nav_link(action: :show) do
= link_to 'Commits', commits_namespace_project_graph_path = link_to 'Contributors', namespace_project_graph_path
= nav_link(action: :languages) do = nav_link(action: :commits) do
= link_to 'Languages', languages_namespace_project_graph_path = link_to 'Commits', commits_namespace_project_graph_path
- if @project.builds_enabled? = nav_link(action: :languages) do
= nav_link(action: :ci) do = link_to 'Languages', languages_namespace_project_graph_path
= link_to ci_namespace_project_graph_path do - if @project.builds_enabled?
Continuous Integration = nav_link(action: :ci) do
= link_to ci_namespace_project_graph_path do
Continuous Integration
- @no_container = true
- page_title "Continuous Integration", "Graphs" - page_title "Continuous Integration", "Graphs"
= render 'head' = render 'head'
.row-content-block.append-bottom-default
.oneline
A collection of graphs for Continuous Integration
#charts.ci-charts %div{ class: (container_class) }
.row .sub-header-block
.col-md-6 .oneline
= render 'projects/graphs/ci/overall' A collection of graphs for Continuous Integration
.col-md-6
= render 'projects/graphs/ci/build_times'
%hr #charts.ci-charts
= render 'projects/graphs/ci/builds' .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" - page_title "Commits", "Graphs"
= render 'head' = render 'head'
.row-content-block.append-bottom-default %div{ class: (container_class) }
.tree-ref-holder .sub-header-block
= render 'shared/ref_switcher', destination: 'graphs_commits' .tree-ref-holder
%ul.breadcrumb.repo-breadcrumb = render 'shared/ref_switcher', destination: 'graphs_commits'
= commits_breadcrumbs %ul.breadcrumb.repo-breadcrumb
= commits_breadcrumbs
%p.lead %p.lead
Commit statistics for Commit statistics for
%strong #{@ref} %strong #{@ref}
#{@commits_graph.start_date.strftime('%b %d')} - #{@commits_graph.end_date.strftime('%b %d')} #{@commits_graph.start_date.strftime('%b %d')} - #{@commits_graph.end_date.strftime('%b %d')}
.row .row
.col-md-6 .col-md-6
%ul %ul
%li %li
%p.lead %p.lead
%strong #{@commits_graph.commits.size} %strong #{@commits_graph.commits.size}
commits during commits during
%strong #{@commits_graph.duration} %strong #{@commits_graph.duration}
days days
%li %li
%p.lead %p.lead
Average Average
%strong #{@commits_graph.commit_per_day} %strong #{@commits_graph.commit_per_day}
commits per day commits per day
%li %li
%p.lead %p.lead
Contributed by Contributed by
%strong #{@commits_graph.authors} %strong #{@commits_graph.authors}
authors authors
.col-md-6 .col-md-6
%div %div
%p.slead %p.slead
Commits per day of month Commits per day of month
%canvas#month-chart %canvas#month-chart
.row .row
.col-md-6 .col-md-6
%div %div
%p.slead %p.slead
Commits per day hour (UTC) Commits per day hour (UTC)
%canvas#hour-chart %canvas#hour-chart
.col-md-6 .col-md-6
%div %div
%p.slead %p.slead
Commits per weekday Commits per weekday
%canvas#weekday-chart %canvas#weekday-chart
:javascript :javascript
var responsiveChart = function (selector, data) { var responsiveChart = function (selector, data) {
......
- @no_container = true
- page_title "Languages", "Graphs" - page_title "Languages", "Graphs"
= render 'head' = render 'head'
.row-content-block.append-bottom-default %div{ class: (container_class) }
.oneline .sub-header-block
Programming languages used in this repository .oneline
Programming languages used in this repository
.row .row
.col-md-8 .col-md-8
%canvas#languages-chart{ height: 400 } %canvas#languages-chart{ height: 400 }
.col-md-4 .col-md-4
%ul.bordered-list %ul.bordered-list
- @languages.each do |language| - @languages.each do |language|
%li %li
%span{ style: "color: #{language[:color]}" } %span{ style: "color: #{language[:color]}" }
= icon('circle') = icon('circle')
&nbsp; &nbsp;
= language[:label] = language[:label]
.pull-right .pull-right
= language[:value] = language[:value]
\% \%
:javascript :javascript
var data = #{@languages.to_json}; var data = #{@languages.to_json};
......
- @no_container = true
- page_title "Contributors", "Graphs" - page_title "Contributors", "Graphs"
= render 'head' = render 'head'
.row-content-block.append-bottom-default %div{ class: (container_class) }
.tree-ref-holder .sub-header-block
= render 'shared/ref_switcher', destination: 'graphs' .tree-ref-holder
%ul.breadcrumb.repo-breadcrumb = render 'shared/ref_switcher', destination: 'graphs'
= commits_breadcrumbs %ul.breadcrumb.repo-breadcrumb
= commits_breadcrumbs
.loading-graph
.center .loading-graph
%h3.page-title .center
%i.fa.fa-spinner.fa-spin %h3.page-title
Building repository graph. %i.fa.fa-spinner.fa-spin
%p.slead Please wait a moment, this page will automatically refresh when ready. Building repository graph.
%p.slead Please wait a moment, this page will automatically refresh when ready.
.stat-graph.hide
.header.clearfix .stat-graph.hide
%h3#date_header.page-title .header.clearfix
%p.light %h3#date_header.page-title
Commits to #{@ref}, excluding merge commits. Limited to 6,000 commits. %p.light
%input#brush_change{:type => "hidden"} Commits to #{@ref}, excluding merge commits. Limited to 6,000 commits.
.graphs %input#brush_change{:type => "hidden"}
#contributors-master .graphs.row
#contributors.clearfix #contributors-master
%ol.contributors-list.clearfix #contributors.clearfix
%ol.contributors-list.clearfix
......
...@@ -15,5 +15,5 @@ ...@@ -15,5 +15,5 @@
= check_box_tag :filter_ref, 1, @options[:filter_ref] = check_box_tag :filter_ref, 1, @options[:filter_ref]
%span Begin with the selected commit %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 = spinner nil, true
...@@ -2,8 +2,7 @@ ...@@ -2,8 +2,7 @@
.panel-heading .panel-heading
%strong #{@group.name} %strong #{@group.name}
group members group members
%small %span.badge= members.size
(#{members.count})
- if can?(current_user, :admin_group_member, @group) - if can?(current_user, :admin_group_member, @group)
.controls .controls
= link_to 'Manage group members', = link_to 'Manage group members',
......
- @project_group_links.each do |group_links| - @project_group_links.each do |group_links|
- shared_group = group_links.group - 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.panel-default
.panel-heading .panel-heading
Shared with Shared with
...@@ -15,7 +16,7 @@ ...@@ -15,7 +16,7 @@
Edit group members Edit group members
%ul.content-list %ul.content-list
= render partial: 'shared/members/member', = 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, as: :member,
locals: { show_controls: false, show_roles: false } locals: { show_controls: false, show_roles: false }
- if shared_group_users_count > 20 - if shared_group_users_count > 20
......
...@@ -2,8 +2,7 @@ ...@@ -2,8 +2,7 @@
.panel-heading .panel-heading
%strong #{@project.name} %strong #{@project.name}
project members project members
%small %span.badge= members.size
(#{members.count})
.controls .controls
= form_tag namespace_project_project_members_path(@project.namespace, @project), method: :get, class: 'form-inline member-search-form' do = form_tag namespace_project_project_members_path(@project.namespace, @project), method: :get, class: 'form-inline member-search-form' do
.form-group .form-group
......
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
= render 'team', members: @project_members.non_request = render 'team', members: @project_members.non_request
- if @group - 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? - if @project_group_links.any? && @project.allowed_to_share_with_group?
= render "shared_group_members" = render "shared_group_members"
...@@ -60,7 +60,7 @@ ...@@ -60,7 +60,7 @@
- unless @repository.gitlab_ci_yml - unless @repository.gitlab_ci_yml
%li.missing %li.missing
= link_to add_special_file_path(@project, file_name: '.gitlab-ci.yml') do = link_to add_special_file_path(@project, file_name: '.gitlab-ci.yml') do
Set up CI Set Up CI
- if @repository.commit - if @repository.commit
.content-block.second-block.white .content-block.second-block.white
......
- if (@page && @page.persisted?) - 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 = link_to namespace_project_wiki_history_path(@project.namespace, @project, @page), class: "btn" do
Page History Page History
- if can?(current_user, :create_wiki, @project) - if can?(current_user, :create_wiki, @project)
......
.top-area .nav-links.sub-nav
%ul.nav-links %ul{ class: (container_class) }
= nav_link(html_options: {class: params[:id] == 'home' ? 'active' : '' }) do = nav_link(html_options: {class: params[:id] == 'home' ? 'active' : '' }) do
= link_to 'Home', namespace_project_wiki_path(@project.namespace, @project, :home) = link_to 'Home', namespace_project_wiki_path(@project.namespace, @project, :home)
...@@ -10,9 +10,4 @@ ...@@ -10,9 +10,4 @@
= link_to namespace_project_wikis_git_access_path(@project.namespace, @project) do = link_to namespace_project_wikis_git_access_path(@project.namespace, @project) do
Git Access Git Access
.nav-controls = render 'projects/wikis/new'
- 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'
%div#modal-new-wiki.modal - @no_container = true
.modal-dialog
.modal-content %div{ class: (container_class) }
.modal-header %div#modal-new-wiki.modal
%a.close{href: "#", "data-dismiss" => "modal"} × .modal-dialog
%h3.page-title New Wiki Page .modal-content
.modal-body .modal-header
%form.new-wiki-page %a.close{href: "#", "data-dismiss" => "modal"} ×
.form-group %h3.page-title New Wiki Page
= label_tag :new_wiki_path do .modal-body
%span Page slug %form.new-wiki-page
= 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-group
.form-actions = label_tag :new_wiki_path do
= button_tag 'Create Page', class: 'build-new-wiki btn btn-create' %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" - page_title "Edit", @page.title.capitalize, "Wiki"
= render 'nav' = render 'nav'
.top-area %div{ class: (container_class) }
.nav-text.wiki-page .top-area
%strong .nav-text
- if @page.persisted? %strong
= link_to @page.title.capitalize, namespace_project_wiki_path(@project.namespace, @project, @page) - if @page.persisted?
- else = link_to @page.title.capitalize, namespace_project_wiki_path(@project.namespace, @project, @page)
= @page.title.capitalize - else
%span.light = @page.title.capitalize
&middot; %span.light
Edit Page &middot;
Edit Page
.nav-controls .nav-controls
= render 'main_links' - 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" - page_title "Git Access", "Wiki"
= render 'nav' = render 'nav'
.row-content-block %div{ class: (container_class) }
%span.oneline .sub-header-block
Git access for %span.oneline
%strong= @project_wiki.path_with_namespace Git access for
%strong= @project_wiki.path_with_namespace
.pull-right .pull-right
= render "shared/clone_panel", project: @project_wiki = render "shared/clone_panel", project: @project_wiki
.git-empty.prepend-top-default .prepend-top-default
%fieldset %fieldset
%legend Install Gollum: %legend Install Gollum:
%pre.dark %pre.dark
:preserve :preserve
gem install gollum gem install gollum
%legend Clone Your Wiki: %legend Clone Your Wiki:
%pre.dark %pre.dark
:preserve :preserve
git clone #{ content_tag(:span, default_url_to_repo(@project_wiki), class: 'clone')} git clone #{ content_tag(:span, default_url_to_repo(@project_wiki), class: 'clone')}
cd #{h @project_wiki.path} cd #{h @project_wiki.path}
%legend Start Gollum And Edit Locally: %legend Start Gollum And Edit Locally:
%pre.dark %pre.dark
:preserve :preserve
gollum gollum
== Sinatra/1.3.5 has taken the stage on 4567 for development with backup from Thin == Sinatra/1.3.5 has taken the stage on 4567 for development with backup from Thin
>> Thin web server (v1.5.0 codename Knife) >> Thin web server (v1.5.0 codename Knife)
>> Maximum connections set to 1024 >> Maximum connections set to 1024
>> Listening on 0.0.0.0:4567, CTRL+C to stop >> Listening on 0.0.0.0:4567, CTRL+C to stop
- page_title "History", @page.title.capitalize, "Wiki" - page_title "History", @page.title.capitalize, "Wiki"
= render 'nav' = 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 .table-holder
.nav-text %table.table
%strong %thead
= 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
%tr %tr
%td %th Page version
= link_to project_wiki_path_with_version(@project, @page, %th Author
commit.id, index == 0) do %th Commit Message
= truncate_sha(commit.id) %th Last updated
%td %th Format
= commit.author.name %tbody
%td - @page.versions.each_with_index do |version, index|
= commit.message - commit = version
%td %tr
#{time_ago_with_tooltip(version.authored_date)} %td
%td = link_to project_wiki_path_with_version(@project, @page,
%strong commit.id, index == 0) do
= @page.page.wiki.page(@page.page.name, commit.id).try(:format) = 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" - page_title "Pages", "Wiki"
= render 'nav' = render 'nav'
%ul.content-list %div{ class: (container_class) }
- @wiki_pages.each do |wiki_page| %ul.content-list
%li - @wiki_pages.each do |wiki_page|
= link_to wiki_page.title, namespace_project_wiki_path(@project.namespace, @project, wiki_page) %li
%small (#{wiki_page.format}) = link_to wiki_page.title, namespace_project_wiki_path(@project.namespace, @project, wiki_page)
.pull-right %small (#{wiki_page.format})
%small Last edited #{time_ago_with_tooltip(wiki_page.commit.authored_date)} .pull-right
= paginate @wiki_pages, theme: 'gitlab' %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" - page_title @page.title.capitalize, "Wiki"
= render 'nav' = render 'nav'
.top-area %div{ class: (container_class) }
.nav-text .top-area
%strong= @page.title.capitalize .nav-text
%strong= @page.title.capitalize
%span.wiki-last-edit-by %span.wiki-last-edit-by
&middot; &middot;
last edited by #{@page.commit.author.name} #{time_ago_with_tooltip(@page.commit.authored_date)} last edited by #{@page.commit.author.name} #{time_ago_with_tooltip(@page.commit.authored_date)}
.nav-controls .nav-controls
= render 'main_links' = render 'main_links'
- if @page.historical? - if @page.historical?
.warning_message .warning_message
This is an old version of this page. 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)}. 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-holder.prepend-top-default.append-bottom-default
.wiki .wiki
= preserve do = preserve do
= render_wiki_content(@page) = render_wiki_content(@page)
%ul.nav-links.event-filter.scrolling-tabs %ul.nav-links.event-filter.scrolling-tabs
.fade-left %li.fade-left
= icon('arrow-left') = icon('arrow-left')
= event_filter_link EventFilter.push, 'Push events' = event_filter_link EventFilter.push, 'Push events'
= event_filter_link EventFilter.merged, 'Merge events' = event_filter_link EventFilter.merged, 'Merge events'
= event_filter_link EventFilter.comments, 'Comments' = event_filter_link EventFilter.comments, 'Comments'
= event_filter_link EventFilter.team, 'Team' = event_filter_link EventFilter.team, 'Team'
.fade-right %li.fade-right
= icon('arrow-right') = icon('arrow-right')
...@@ -3,6 +3,6 @@ ...@@ -3,6 +3,6 @@
.panel-heading .panel-heading
%strong= membership_source.name %strong= membership_source.name
access requests access requests
%small= "(#{members.size})" %span.badge= members.size
%ul.content-list %ul.content-list
= render partial: 'shared/members/member', collection: members, as: :member = render partial: 'shared/members/member', collection: members, as: :member
...@@ -16,6 +16,12 @@ ...@@ -16,6 +16,12 @@
= render "shared/projects/project", project: project, skip_namespace: skip_namespace, = 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, 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 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 = paginate(projects, remote: remote, theme: "gitlab") if projects.respond_to? :total_pages
- else - else
.nothing-here-block No projects found .nothing-here-block No projects found
......
...@@ -215,7 +215,7 @@ Settings.gitlab.default_projects_features['container_registry'] = true if Settin ...@@ -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.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['repository_downloads_path'] = File.join(Settings.shared['path'], 'cache/archive') if Settings.gitlab['repository_downloads_path'].nil?
Settings.gitlab['restricted_signup_domains'] ||= [] 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'] ||= [] Settings.gitlab['trusted_proxies'] ||= []
......
...@@ -3,18 +3,18 @@ ...@@ -3,18 +3,18 @@
## User documentation ## User documentation
- [API](api/README.md) Automate GitLab via a simple and powerful API. - [API](api/README.md) Automate GitLab via a simple and powerful API.
- [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. - [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. - [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 to GitLab](workflow/importing/README.md).
- [Importing and exporting projects between instances](user/project/settings/import_export.md). - [Importing and exporting projects between instances](user/project/settings/import_export.md).
- [Markdown](markdown/markdown.md) GitLab's advanced formatting system. - [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. - [Permissions](permissions/permissions.md) Learn what each role in a project (external/guest/reporter/developer/master/owner) can do.
- [Profile Settings](profile/README.md) - [Profile Settings](profile/README.md)
- [Project Services](project_services/project_services.md) Integrate a project with external services, such as CI and chat. - [Project Services](project_services/project_services.md) Integrate a project with external services, such as CI and chat.
- [Public access](public_access/public_access.md) Learn how you can allow public and internal access to projects. - [Public access](public_access/public_access.md) Learn how you can allow public and internal access to projects.
- [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. - [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. - [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. - [Workflow](workflow/README.md) Using GitLab functionality and importing projects from GitHub and SVN.
...@@ -25,15 +25,15 @@ ...@@ -25,15 +25,15 @@
external authentication with LDAP, SAML, CAS and additional Omniauth providers. 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. - [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. - [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. - [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. - [Issue closing](customization/issue_closing.md) Customize how to close an issue from commit messages.
- [Libravatar](customization/libravatar.md) Use Libravatar for user avatars. - [Libravatar](customization/libravatar.md) Use Libravatar for user avatars.
- [Log system](administration/logs.md) Log system. - [Log system](administration/logs.md) Log system.
- [Environment Variables](administration/environment_variables.md) to configure GitLab. - [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. - [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. - [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. - [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. - [Update](update/README.md) Update guides to upgrade your installation.
...@@ -42,11 +42,11 @@ ...@@ -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. - [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) - [Git LFS configuration](workflow/lfs/lfs_administration.md)
- [Housekeeping](administration/housekeeping.md) Keep your Git repository tidy and fast. - [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 - [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 - [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 - [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 - [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 - [Container Registry](administration/container_registry.md) Configure Docker Registry with GitLab.
## Contributor documentation ## Contributor documentation
......
...@@ -22,6 +22,7 @@ You can read more about Docker Registry at https://docs.docker.com/registry/intr ...@@ -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 per project](#disable-container-registry-per-project)
- [Disable Container Registry for new projects site-wide](#disable-container-registry-for-new-projects-site-wide) - [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 path](#container-registry-storage-path)
- [Container Registry storage driver](#container-registry-storage-driver)
- [Storage limitations](#storage-limitations) - [Storage limitations](#storage-limitations)
- [Changelog](#changelog) - [Changelog](#changelog)
...@@ -306,8 +307,12 @@ the Container Registry by themselves, follow the steps below. ...@@ -306,8 +307,12 @@ the Container Registry by themselves, follow the steps below.
## Container Registry storage path ## Container Registry storage path
To change the storage path where Docker images will be stored, follow the >**Note:**
steps below. 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: This path is accessible to:
...@@ -349,6 +354,72 @@ The default location where images are stored in source installations, is ...@@ -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. 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 ## Storage limitations
Currently, there is no storage limitation, which means a user can upload an Currently, there is no storage limitation, which means a user can upload an
......
...@@ -32,6 +32,7 @@ following locations: ...@@ -32,6 +32,7 @@ following locations:
- [Services](services.md) - [Services](services.md)
- [Session](session.md) - [Session](session.md)
- [Settings](settings.md) - [Settings](settings.md)
- [Sidekiq metrics](sidekiq_metrics.md)
- [System Hooks](system_hooks.md) - [System Hooks](system_hooks.md)
- [Tags](tags.md) - [Tags](tags.md)
- [Users](users.md) - [Users](users.md)
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
- [Get started with GitLab CI](quick_start/README.md) - [Get started with GitLab CI](quick_start/README.md)
- [CI examples for various languages](examples/README.md) - [CI examples for various languages](examples/README.md)
- [Learn how to enable or disable GitLab CI](enable_or_disable_ci.md) - [Learn how to enable or disable GitLab CI](enable_or_disable_ci.md)
- [Pipelines and builds](pipelines.md)
- [Environments and deployments](environments.md) - [Environments and deployments](environments.md)
- [Learn how `.gitlab-ci.yml` works](yaml/README.md) - [Learn how `.gitlab-ci.yml` works](yaml/README.md)
- [Configure a Runner, the application that runs your builds](runners/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. ...@@ -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 Only deploys that happen after your `.gitlab-ci.yml` is properly configured will
show up in the environments and deployments lists. show up in the environments and deployments lists.
[Pipelines]: quick_start/README.md [Pipelines]: pipelines.md
[jobs]: yaml/README.md#jobs [jobs]: yaml/README.md#jobs
[environments]: #environments [environments]: #environments
[deployments]: #deployments [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 @@ ...@@ -4,41 +4,41 @@
is fully integrated into GitLab itself and is [enabled] by default on all is fully integrated into GitLab itself and is [enabled] by default on all
projects. projects.
The TL;DR version of how GitLab CI works is the following.
---
GitLab offers a [continuous integration][ci] service. If you GitLab offers a [continuous integration][ci] service. If you
[add a `.gitlab-ci.yml` file][yaml] to the root directory of your repository, [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 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 The `.gitlab-ci.yml` file tells the GitLab runner what to do. By default it runs
runs three [stages]: `build`, `test`, and `deploy`. 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 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 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. 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. 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: 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. Add `.gitlab-ci.yml` to the root directory of your repository
1. Configure a Runner 1. Configure a Runner
From there on, on every push to your Git repository, the build will be From there on, on every push to your Git repository, the Runner will
automagically started by the Runner and will appear under the project's automagically start the pipeline and the pipeline will appear under the
`/builds` page. project's `/pipelines` page.
--- ---
This guide assumes that you: This guide assumes that you:
- have a working GitLab instance of version 8.0 or higher or are using - 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 - 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. 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` ...@@ -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, file and start builds on _Runners_ according to the contents of the file,
for that commit. for that commit.
Because `.gitlab-ci.yml` is in the repository, it is version controlled, Because `.gitlab-ci.yml` is in the repository and is version controlled, old
old versions still build successfully, forks can easily make use of CI, versions still build successfully, forks can easily make use of CI, branches can
branches can have separate builds and you have a single source of truth for CI. 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` You can read more about the reasons why we are using `.gitlab-ci.yml` [in our
[in our blog about it][blog-ci]. blog about it][blog-ci].
**Note:** `.gitlab-ci.yml` is a [YAML](https://en.wikipedia.org/wiki/YAML) file **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 so you have to pay extra attention to indentation. Always use spaces, not tabs.
tabs.
### Creating a simple `.gitlab-ci.yml` file ### 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 ...@@ -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 Lint tool under the page `/ci/lint` of your GitLab instance. You can also find
the link under **Settings > CI settings** in your project. 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). [the documentation on .gitlab-ci.yml](../yaml/README.md).
### Push `.gitlab-ci.yml` to GitLab ### Push `.gitlab-ci.yml` to GitLab
...@@ -122,7 +121,8 @@ git commit -m "Add .gitlab-ci.yml" ...@@ -122,7 +121,8 @@ git commit -m "Add .gitlab-ci.yml"
git push origin master 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 You can also go to the **Commits** page and notice the little clock icon next
to the commit SHA. to the commit SHA.
...@@ -138,15 +138,14 @@ Notice that there are two jobs pending which are named after what we wrote in ...@@ -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 `.gitlab-ci.yml`. The red triangle indicates that there is no Runner configured
yet for these builds. 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 ## Configuring a Runner
In GitLab, Runners run the builds that you define in `.gitlab-ci.yml`. In GitLab, Runners run the builds that you define in `.gitlab-ci.yml`. A Runner
A Runner can be a virtual machine, a VPS, a bare-metal machine, a docker can be a virtual machine, a VPS, a bare-metal machine, a docker container or
container or even a cluster of containers. GitLab and the Runners communicate even a cluster of containers. GitLab and the Runners communicate through an API,
through an API, so the only needed requirement is that the machine on which the so the only requirement is that the Runner's machine has Internet access.
Runner is configured to have Internet access.
A Runner can be specific to a certain project or serve multiple projects in 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_. 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 ...@@ -188,12 +187,16 @@ To enable **Shared Runners** you have to go to your project's
[Read more on Shared Runners](../runners/README.md). [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 After configuring the Runner successfully, you should see the status of your
last commit change from _pending_ to either _running_, _success_ or _failed_. 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) ![Commit status](img/builds_status.png)
...@@ -238,3 +241,4 @@ CI with various languages. ...@@ -238,3 +241,4 @@ CI with various languages.
[runner]: ../runners/README.md [runner]: ../runners/README.md
[enabled]: ../enable_or_disable_ci.md [enabled]: ../enable_or_disable_ci.md
[stages]: ../yaml/README.md#stages [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. ...@@ -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 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 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 ```yaml
job1: job1:
...@@ -165,7 +165,7 @@ stages: ...@@ -165,7 +165,7 @@ stages:
There are also two edge cases worth mentioning: 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. `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. 2. If a job doesn't specify a `stage`, the job is assigned the `test` stage.
......
...@@ -26,13 +26,6 @@ Feature: Admin Groups ...@@ -26,13 +26,6 @@ Feature: Admin Groups
When I visit group page When I visit group page
Then I should see project shared with group 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 @javascript
Scenario: Invite user to a group by e-mail Scenario: Invite user to a group by e-mail
When I visit admin group page When I visit admin group page
......
...@@ -5,53 +5,9 @@ Feature: Dashboard Group ...@@ -5,53 +5,9 @@ Feature: Dashboard Group
And "John Doe" is owner of group "Owned" And "John Doe" is owner of group "Owned"
And "John Doe" is guest of group "Guest" 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 Scenario: Create a group from dasboard
And I visit dashboard groups page And I visit dashboard groups page
And I click new group link And I click new group link
And submit form with new group "Samurai" info And submit form with new group "Samurai" info
Then I should be redirected to group "Samurai" page Then I should be redirected to group "Samurai" page
And I should see newly created group "Samurai" And I should see newly created group "Samurai"
...@@ -10,9 +10,9 @@ Feature: Project Active Tab ...@@ -10,9 +10,9 @@ Feature: Project Active Tab
Then the active main tab should be Home Then the active main tab should be Home
And no other main tabs should be active And no other main tabs should be active
Scenario: On Project Code Scenario: On Project Repository
Given I visit my project's files page 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 And no other main tabs should be active
Scenario: On Project Issues Scenario: On Project Issues
...@@ -59,46 +59,46 @@ Feature: Project Active Tab ...@@ -59,46 +59,46 @@ Feature: Project Active Tab
And no other sub navs should be active And no other sub navs should be active
And the active main tab should be Settings 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 Given I visit my project's files page
Then the active sub tab should be Files Then the active sub tab should be Files
And no other sub tabs should be active 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 Given I visit my project's commits page
Then the active sub tab should be Commits Then the active sub tab should be Commits
And no other sub tabs should be active 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 Given I visit my project's network page
Then the active sub tab should be Network Then the active sub tab should be Network
And no other sub tabs should be active 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 Given I visit my project's commits page
And I click the "Compare" tab And I click the "Compare" tab
Then the active sub tab should be Compare Then the active sub tab should be Compare
And no other sub tabs should be active 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 Given I visit my project's commits page
And I click the "Branches" tab And I click the "Branches" tab
Then the active sub tab should be Branches Then the active sub tab should be Branches
And no other sub tabs should be active 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 Given I visit my project's commits page
And I click the "Tags" tab And I click the "Tags" tab
Then the active sub tab should be Tags Then the active sub tab should be Tags
And no other sub tabs should be active 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 Scenario: On Project Issues/Browse
Given I visit my project's issues page Given I visit my project's issues page
......
...@@ -8,21 +8,21 @@ Feature: Project Shortcuts ...@@ -8,21 +8,21 @@ Feature: Project Shortcuts
@javascript @javascript
Scenario: Navigate to files tab Scenario: Navigate to files tab
Given I press "g" and "f" 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 Then the active sub tab should be Files
@javascript @javascript
Scenario: Navigate to commits tab Scenario: Navigate to commits tab
Given I visit my project's files page Given I visit my project's files page
Given I press "g" and "c" 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 Then the active sub tab should be Commits
@javascript @javascript
Scenario: Navigate to network tab Scenario: Navigate to network tab
Given I press "g" and "n" Given I press "g" and "n"
Then the active sub tab should be Network 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 @javascript
Scenario: Navigate to graphs tab Scenario: Navigate to graphs tab
......
...@@ -62,7 +62,7 @@ class Spinach::Features::AdminGroups < Spinach::FeatureSteps ...@@ -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 step 'I should see "johndoe@gitlab.com" in team list in every project as "Reporter"' do
page.within ".group-users-list" 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" expect(page).to have_content "Reporter"
end end
end end
...@@ -92,12 +92,6 @@ class Spinach::Features::AdminGroups < Spinach::FeatureSteps ...@@ -92,12 +92,6 @@ class Spinach::Features::AdminGroups < Spinach::FeatureSteps
current_group.add_reporter(user_john) current_group.add_reporter(user_john)
end 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 step 'I should not see "John Doe" in team list' do
page.within ".group-users-list" do page.within ".group-users-list" do
expect(page).not_to have_content "John Doe" expect(page).not_to have_content "John Doe"
......
...@@ -4,44 +4,6 @@ class Spinach::Features::DashboardGroup < Spinach::FeatureSteps ...@@ -4,44 +4,6 @@ class Spinach::Features::DashboardGroup < Spinach::FeatureSteps
include SharedPaths include SharedPaths
include SharedUser 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 step 'I click new group link' do
click_link "New Group" click_link "New Group"
end end
...@@ -60,8 +22,4 @@ class Spinach::Features::DashboardGroup < Spinach::FeatureSteps ...@@ -60,8 +22,4 @@ class Spinach::Features::DashboardGroup < Spinach::FeatureSteps
expect(page).to have_content "Samurai" expect(page).to have_content "Samurai"
expect(page).to have_content "Tokugawa Shogunate" expect(page).to have_content "Tokugawa Shogunate"
end 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 end
...@@ -13,12 +13,12 @@ class Spinach::Features::ProjectFindFile < Spinach::FeatureSteps ...@@ -13,12 +13,12 @@ class Spinach::Features::ProjectFindFile < Spinach::FeatureSteps
end end
step 'I should see "find file" page' do 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) expect(page).to have_selector('.file-finder-holder', count: 1)
end end
step 'I fill in Find by path with "git"' do 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) expect(page).to have_selector('.file-finder-holder', count: 1)
end end
......
...@@ -8,8 +8,8 @@ module SharedProjectTab ...@@ -8,8 +8,8 @@ module SharedProjectTab
ensure_active_main_tab('Project') ensure_active_main_tab('Project')
end end
step 'the active main tab should be Code' do step 'the active main tab should be Repository' do
ensure_active_main_tab('Code') ensure_active_main_tab('Repository')
end end
step 'the active main tab should be Graphs' do step 'the active main tab should be Graphs' do
......
...@@ -7,10 +7,6 @@ module Banzai ...@@ -7,10 +7,6 @@ module Banzai
Renderer.render_result(text, context) Renderer.render_result(text, context)
end end
def self.pre_process(text, context)
Renderer.pre_process(text, context)
end
def self.post_process(html, context) def self.post_process(html, context)
Renderer.post_process(html, context) Renderer.post_process(html, context)
end end
......
...@@ -30,13 +30,9 @@ module Banzai ...@@ -30,13 +30,9 @@ module Banzai
end end
def self.render_result(text, context = {}) def self.render_result(text, context = {})
Pipeline[context[:pipeline]].call(text, context) text = Pipeline[:pre_process].to_html(text, context) if text
end
def self.pre_process(text, context) Pipeline[context[:pipeline]].call(text, context)
pipeline = Pipeline[:pre_process]
pipeline.to_html(text, context)
end end
# Perform post-processing on an HTML String # Perform post-processing on an HTML String
......
...@@ -5,6 +5,8 @@ ...@@ -5,6 +5,8 @@
# #
module Gitlab module Gitlab
module Access module Access
class AccessDeniedError < StandardError; end
GUEST = 10 GUEST = 10
REPORTER = 20 REPORTER = 20
DEVELOPER = 30 DEVELOPER = 30
......
...@@ -118,9 +118,7 @@ describe Groups::GroupMembersController do ...@@ -118,9 +118,7 @@ describe Groups::GroupMembersController do
it 'cannot removes himself from the group' do it 'cannot removes himself from the group' do
delete :leave, group_id: group delete :leave, group_id: group
expect(response).to redirect_to(group_path(group)) expect(response.status).to eq(403)
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
end end
end end
...@@ -134,7 +132,7 @@ describe Groups::GroupMembersController do ...@@ -134,7 +132,7 @@ describe Groups::GroupMembersController do
delete :leave, group_id: group delete :leave, group_id: group
expect(response).to set_flash.to 'Your access request to the group has been withdrawn.' 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.members.request).to be_empty
expect(group.users).not_to include user expect(group.users).not_to include user
end end
......
...@@ -171,11 +171,7 @@ describe Projects::ProjectMembersController do ...@@ -171,11 +171,7 @@ describe Projects::ProjectMembersController do
delete :leave, namespace_id: project.namespace, delete :leave, namespace_id: project.namespace,
project_id: project project_id: project
expect(response).to redirect_to( expect(response.status).to eq(403)
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
end end
end end
...@@ -190,7 +186,7 @@ describe Projects::ProjectMembersController do ...@@ -190,7 +186,7 @@ describe Projects::ProjectMembersController do
project_id: project project_id: project
expect(response).to set_flash.to 'Your access request to the project has been withdrawn.' 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.members.request).to be_empty
expect(project.users).not_to include user expect(project.users).not_to include user
end 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 ...@@ -42,7 +42,7 @@ feature 'Groups > Members > Owner manages access requests', feature: true do
def expect_visible_access_request(group, user) def expect_visible_access_request(group, user)
expect(group.members.request.exists?(user_id: user)).to be_truthy 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 expect(page).to have_content user.name
end end
end end
...@@ -21,6 +21,7 @@ feature 'Groups > Members > User requests access', feature: true do ...@@ -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 'Your request for access has been queued for review.'
expect(page).to have_content 'Withdraw Access Request' expect(page).to have_content 'Withdraw Access Request'
expect(page).not_to have_content 'Leave Group'
end end
scenario 'user is not listed in the group members page' do 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 ...@@ -41,7 +41,7 @@ feature 'Projects > Members > Master manages access requests', feature: true do
def expect_visible_access_request(project, user) def expect_visible_access_request(project, user)
expect(project.members.request.exists?(user_id: user)).to be_truthy 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 expect(page).to have_content user.name
end end
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 ...@@ -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 'Your request for access has been queued for review.'
expect(page).to have_content 'Withdraw Access Request' expect(page).to have_content 'Withdraw Access Request'
expect(page).not_to have_content 'Leave Project'
end end
scenario 'user is not listed in the project members page' do scenario 'user is not listed in the project members page' do
......
...@@ -70,22 +70,6 @@ feature 'Project', feature: true do ...@@ -70,22 +70,6 @@ feature 'Project', feature: true do
end end
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 describe 'project title' do
include WaitForAjax include WaitForAjax
......
...@@ -134,18 +134,6 @@ describe Member, models: true do ...@@ -134,18 +134,6 @@ describe Member, models: true do
it { is_expected.to respond_to(:user_email) } it { is_expected.to respond_to(:user_email) }
end 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 describe ".add_user" do
let!(:user) { create(:user) } let!(:user) { create(:user) }
let(:project) { create(:project) } let(:project) { create(:project) }
......
...@@ -61,16 +61,6 @@ describe GroupMember, models: true do ...@@ -61,16 +61,6 @@ describe GroupMember, models: true do
end end
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 describe '#real_source_type' do
subject { create(:group_member).real_source_type } subject { create(:group_member).real_source_type }
......
...@@ -152,15 +152,5 @@ describe ProjectMember, models: true do ...@@ -152,15 +152,5 @@ describe ProjectMember, models: true do
member.__send__(:after_accept_request) member.__send__(:after_accept_request)
end end
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
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