Commit 3ca1f6d0 authored by Nick Thomas's avatar Nick Thomas

Merge remote-tracking branch 'upstream/master' into ce-to-ee-2017-06-07

parents 4471ca45 dbc7bbc3
...@@ -3,6 +3,11 @@ ...@@ -3,6 +3,11 @@
import timeago from 'timeago.js'; import timeago from 'timeago.js';
import dateFormat from 'vendor/date.format'; import dateFormat from 'vendor/date.format';
import {
lang,
s__,
} from '../../locale';
window.timeago = timeago; window.timeago = timeago;
window.dateFormat = dateFormat; window.dateFormat = dateFormat;
...@@ -48,26 +53,45 @@ window.dateFormat = dateFormat; ...@@ -48,26 +53,45 @@ window.dateFormat = dateFormat;
var locale; var locale;
if (!timeagoInstance) { if (!timeagoInstance) {
const localeRemaining = function(number, index) {
return [
[s__('Timeago|less than a minute ago'), s__('Timeago|a while')],
[s__('Timeago|less than a minute ago'), s__('Timeago|%s seconds remaining')],
[s__('Timeago|about a minute ago'), s__('Timeago|1 minute remaining')],
[s__('Timeago|%s minutes ago'), s__('Timeago|%s minutes remaining')],
[s__('Timeago|about an hour ago'), s__('Timeago|1 hour remaining')],
[s__('Timeago|about %s hours ago'), s__('Timeago|%s hours remaining')],
[s__('Timeago|a day ago'), s__('Timeago|1 day remaining')],
[s__('Timeago|%s days ago'), s__('Timeago|%s days remaining')],
[s__('Timeago|a week ago'), s__('Timeago|1 week remaining')],
[s__('Timeago|%s weeks ago'), s__('Timeago|%s weeks remaining')],
[s__('Timeago|a month ago'), s__('Timeago|1 month remaining')],
[s__('Timeago|%s months ago'), s__('Timeago|%s months remaining')],
[s__('Timeago|a year ago'), s__('Timeago|1 year remaining')],
[s__('Timeago|%s years ago'), s__('Timeago|%s years remaining')]
][index];
};
locale = function(number, index) { locale = function(number, index) {
return [ return [
['less than a minute ago', 'a while'], [s__('Timeago|less than a minute ago'), s__('Timeago|a while')],
['less than a minute ago', 'in %s seconds'], [s__('Timeago|less than a minute ago'), s__('Timeago|in %s seconds')],
['about a minute ago', 'in 1 minute'], [s__('Timeago|about a minute ago'), s__('Timeago|in 1 minute')],
['%s minutes ago', 'in %s minutes'], [s__('Timeago|%s minutes ago'), s__('Timeago|in %s minutes')],
['about an hour ago', 'in 1 hour'], [s__('Timeago|about an hour ago'), s__('Timeago|in 1 hour')],
['about %s hours ago', 'in %s hours'], [s__('Timeago|about %s hours ago'), s__('Timeago|in %s hours')],
['a day ago', 'in 1 day'], [s__('Timeago|a day ago'), s__('Timeago|in 1 day')],
['%s days ago', 'in %s days'], [s__('Timeago|%s days ago'), s__('Timeago|in %s days')],
['a week ago', 'in 1 week'], [s__('Timeago|a week ago'), s__('Timeago|in 1 week')],
['%s weeks ago', 'in %s weeks'], [s__('Timeago|%s weeks ago'), s__('Timeago|in %s weeks')],
['a month ago', 'in 1 month'], [s__('Timeago|a month ago'), s__('Timeago|in 1 month')],
['%s months ago', 'in %s months'], [s__('Timeago|%s months ago'), s__('Timeago|in %s months')],
['a year ago', 'in 1 year'], [s__('Timeago|a year ago'), s__('Timeago|in 1 year')],
['%s years ago', 'in %s years'] [s__('Timeago|%s years ago'), s__('Timeago|in %s years')]
][index]; ][index];
}; };
timeago.register('gl_en', locale); timeago.register(lang, locale);
timeago.register(`${lang}-remaining`, localeRemaining);
timeagoInstance = timeago(); timeagoInstance = timeago();
} }
...@@ -79,13 +103,11 @@ window.dateFormat = dateFormat; ...@@ -79,13 +103,11 @@ window.dateFormat = dateFormat;
if (!time) { if (!time) {
return ''; return '';
} }
suffix || (suffix = 'remaining'); if (new Date(time) < new Date()) {
expiredLabel || (expiredLabel = 'Past due'); expiredLabel || (expiredLabel = s__('Timeago|Past due'));
timefor = gl.utils.getTimeago().format(time).replace('in', '');
if (timefor.indexOf('ago') > -1) {
timefor = expiredLabel; timefor = expiredLabel;
} else { } else {
timefor = timefor.trim() + ' ' + suffix; timefor = gl.utils.getTimeago().format(time, `${lang}-remaining`).trim();
} }
return timefor; return timefor;
}; };
...@@ -102,7 +124,7 @@ window.dateFormat = dateFormat; ...@@ -102,7 +124,7 @@ window.dateFormat = dateFormat;
}; };
w.gl.utils.updateTimeagoText = function(el) { w.gl.utils.updateTimeagoText = function(el) {
const formattedDate = gl.utils.getTimeago().format(el.getAttribute('datetime'), 'gl_en'); const formattedDate = gl.utils.getTimeago().format(el.getAttribute('datetime'), lang);
if (el.textContent !== formattedDate) { if (el.textContent !== formattedDate) {
el.textContent = formattedDate; el.textContent = formattedDate;
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
...@@ -9,7 +9,7 @@ class ProfilesController < Profiles::ApplicationController ...@@ -9,7 +9,7 @@ class ProfilesController < Profiles::ApplicationController
end end
def update def update
user_params.except!(:email) if @user.ldap_user? user_params.except!(:email) if @user.external_email?
respond_to do |format| respond_to do |format|
if @user.update_attributes(user_params) if @user.update_attributes(user_params)
...@@ -76,7 +76,7 @@ class ProfilesController < Profiles::ApplicationController ...@@ -76,7 +76,7 @@ class ProfilesController < Profiles::ApplicationController
end end
def user_params def user_params
params.require(:user).permit( @user_params ||= params.require(:user).permit(
:avatar, :avatar,
:bio, :bio,
:email, :email,
......
module Projects
class IssueLinksController < ApplicationController
before_action :authorize_admin_issue_link!, only: [:create, :destroy]
def index
render json: issues
end
def create
create_params = params.slice(:issue_references)
result = IssueLinks::CreateService.new(issue, current_user, create_params).execute
render json: { message: result[:message], issues: issues }, status: result[:http_status]
end
def destroy
issue_link = IssueLink.find(params[:id])
return render_403 unless can?(current_user, :admin_issue_link, issue_link.target.project)
IssueLinks::DestroyService.new(issue_link, current_user).execute
render json: { issues: issues }
end
private
def issues
IssueLinks::ListService.new(issue, current_user).execute
end
def authorize_admin_issue_link!
render_403 unless can?(current_user, :admin_issue_link, @project)
end
def issue
@issue ||=
IssuesFinder.new(current_user, project_id: @project.id)
.execute
.find_by!(iid: params[:issue_id])
end
end
end
...@@ -34,7 +34,7 @@ class ProjectsController < Projects::ApplicationController ...@@ -34,7 +34,7 @@ class ProjectsController < Projects::ApplicationController
redirect_to( redirect_to(
project_path(@project), project_path(@project),
notice: "Project '#{@project.name}' was successfully created." notice: _("Project '%{project_name}' was successfully created.") % { project_name: @project.name }
) )
else else
render 'new' render 'new'
...@@ -49,7 +49,7 @@ class ProjectsController < Projects::ApplicationController ...@@ -49,7 +49,7 @@ class ProjectsController < Projects::ApplicationController
respond_to do |format| respond_to do |format|
if result[:status] == :success if result[:status] == :success
flash[:notice] = "Project '#{@project.name}' was successfully updated." flash[:notice] = _("Project '%{project_name}' was successfully updated.") % { project_name: @project.name }
format.html do format.html do
redirect_to(edit_project_path(@project)) redirect_to(edit_project_path(@project))
end end
...@@ -76,7 +76,7 @@ class ProjectsController < Projects::ApplicationController ...@@ -76,7 +76,7 @@ class ProjectsController < Projects::ApplicationController
return access_denied! unless can?(current_user, :remove_fork_project, @project) return access_denied! unless can?(current_user, :remove_fork_project, @project)
if ::Projects::UnlinkForkService.new(@project, current_user).execute if ::Projects::UnlinkForkService.new(@project, current_user).execute
flash[:notice] = 'The fork relationship has been removed.' flash[:notice] = _('The fork relationship has been removed.')
end end
end end
...@@ -98,7 +98,7 @@ class ProjectsController < Projects::ApplicationController ...@@ -98,7 +98,7 @@ class ProjectsController < Projects::ApplicationController
end end
if @project.pending_delete? if @project.pending_delete?
flash[:alert] = "Project #{@project.name} queued for deletion." flash[:alert] = _("Project '%{project_name}' queued for deletion.") % { project_name: @project.name }
end end
respond_to do |format| respond_to do |format|
...@@ -118,7 +118,7 @@ class ProjectsController < Projects::ApplicationController ...@@ -118,7 +118,7 @@ class ProjectsController < Projects::ApplicationController
return access_denied! unless can?(current_user, :remove_project, @project) return access_denied! unless can?(current_user, :remove_project, @project)
::Projects::DestroyService.new(@project, current_user, {}).async_execute ::Projects::DestroyService.new(@project, current_user, {}).async_execute
flash[:alert] = "Project '#{@project.name_with_namespace}' will be deleted." flash[:alert] = _("Project '%{project_name}' will be deleted.") % { project_name: @project.name_with_namespace }
redirect_to dashboard_projects_path, status: 302 redirect_to dashboard_projects_path, status: 302
rescue Projects::DestroyService::DestroyError => ex rescue Projects::DestroyService::DestroyError => ex
...@@ -157,7 +157,7 @@ class ProjectsController < Projects::ApplicationController ...@@ -157,7 +157,7 @@ class ProjectsController < Projects::ApplicationController
redirect_to( redirect_to(
project_path(@project), project_path(@project),
notice: "Housekeeping successfully started" notice: _("Housekeeping successfully started")
) )
rescue ::Projects::HousekeepingService::LeaseTaken => ex rescue ::Projects::HousekeepingService::LeaseTaken => ex
redirect_to( redirect_to(
...@@ -171,7 +171,7 @@ class ProjectsController < Projects::ApplicationController ...@@ -171,7 +171,7 @@ class ProjectsController < Projects::ApplicationController
redirect_to( redirect_to(
edit_project_path(@project), edit_project_path(@project),
notice: "Project export started. A download link will be sent by email." notice: _("Project export started. A download link will be sent by email.")
) )
end end
...@@ -183,16 +183,16 @@ class ProjectsController < Projects::ApplicationController ...@@ -183,16 +183,16 @@ class ProjectsController < Projects::ApplicationController
else else
redirect_to( redirect_to(
edit_project_path(@project), edit_project_path(@project),
alert: "Project export link has expired. Please generate a new export from your project settings." alert: _("Project export link has expired. Please generate a new export from your project settings.")
) )
end end
end end
def remove_export def remove_export
if @project.remove_exports if @project.remove_exports
flash[:notice] = "Project export has been deleted." flash[:notice] = _("Project export has been deleted.")
else else
flash[:alert] = "Project export could not be deleted." flash[:alert] = _("Project export could not be deleted.")
end end
redirect_to(edit_project_path(@project)) redirect_to(edit_project_path(@project))
end end
...@@ -203,7 +203,7 @@ class ProjectsController < Projects::ApplicationController ...@@ -203,7 +203,7 @@ class ProjectsController < Projects::ApplicationController
else else
redirect_to( redirect_to(
edit_project_path(@project), edit_project_path(@project),
alert: "Project export could not be deleted." alert: _("Project export could not be deleted.")
) )
end end
end end
...@@ -221,13 +221,13 @@ class ProjectsController < Projects::ApplicationController ...@@ -221,13 +221,13 @@ class ProjectsController < Projects::ApplicationController
branches = BranchesFinder.new(@repository, params).execute.map(&:name) branches = BranchesFinder.new(@repository, params).execute.map(&:name)
options = { options = {
'Branches' => branches.take(100) s_('RefSwitcher|Branches') => branches.take(100)
} }
unless @repository.tag_count.zero? unless @repository.tag_count.zero?
tags = TagsFinder.new(@repository, params).execute.map(&:name) tags = TagsFinder.new(@repository, params).execute.map(&:name)
options['Tags'] = tags.take(100) options[s_('RefSwitcher|Tags')] = tags.take(100)
end end
# If reference is commit id - we should add it to branch/tag selectbox # If reference is commit id - we should add it to branch/tag selectbox
......
...@@ -16,7 +16,6 @@ ...@@ -16,7 +16,6 @@
# label_name: string # label_name: string
# sort: string # sort: string
# non_archived: boolean # non_archived: boolean
# feature_availability_check: boolean (default: true)
# iids: integer[] # iids: integer[]
# #
class IssuableFinder class IssuableFinder
...@@ -26,15 +25,11 @@ class IssuableFinder ...@@ -26,15 +25,11 @@ class IssuableFinder
ARRAY_PARAMS = { label_name: [], iids: [] }.freeze ARRAY_PARAMS = { label_name: [], iids: [] }.freeze
VALID_PARAMS = (SCALAR_PARAMS + [ARRAY_PARAMS]).freeze VALID_PARAMS = (SCALAR_PARAMS + [ARRAY_PARAMS]).freeze
DEFAULT_PARAMS = {
feature_availability_check: true
}.freeze
attr_accessor :current_user, :params attr_accessor :current_user, :params
def initialize(current_user, params = {}) def initialize(current_user, params = {})
@current_user = current_user @current_user = current_user
@params = DEFAULT_PARAMS.merge(params).with_indifferent_access @params = params
end end
def execute def execute
...@@ -131,20 +126,7 @@ class IssuableFinder ...@@ -131,20 +126,7 @@ class IssuableFinder
ProjectsFinder.new(current_user: current_user, project_ids_relation: item_project_ids(items)).execute ProjectsFinder.new(current_user: current_user, project_ids_relation: item_project_ids(items)).execute
end end
# Querying through feature availability for an user is expensive @projects = projects.with_feature_available_for_user(klass, current_user).reorder(nil)
# (i.e. https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/1719#note_31406525),
# and there are cases which a project level access check should be enough.
# In any case, `feature_availability_check` param should be kept with `true`
# by default.
#
projects =
if params[:feature_availability_check]
projects.with_feature_available_for_user(klass, current_user)
else
projects
end
@projects = projects.reorder(nil)
end end
def search def search
......
...@@ -61,7 +61,7 @@ module ButtonHelper ...@@ -61,7 +61,7 @@ module ButtonHelper
html: true, html: true,
placement: placement, placement: placement,
container: 'body', container: 'body',
title: "Set a password on your account<br>to pull or push via #{protocol}", title: _("Set a password on your account to pull or push via %{protocol}") % { protocol: protocol },
primary_url: (geo_primary_http_url_to_repo(project) if Gitlab::Geo.secondary?) primary_url: (geo_primary_http_url_to_repo(project) if Gitlab::Geo.secondary?)
} }
end end
...@@ -77,7 +77,7 @@ module ButtonHelper ...@@ -77,7 +77,7 @@ module ButtonHelper
html: true, html: true,
placement: placement, placement: placement,
container: 'body', container: 'body',
title: 'Add an SSH key to your profile<br>to pull or push via SSH.' title: _('Add an SSH key to your profile to pull or push via SSH.')
} }
end end
......
...@@ -16,16 +16,18 @@ module CiStatusHelper ...@@ -16,16 +16,18 @@ module CiStatusHelper
return status.label return status.label
end end
case status label = case status
when 'success' when 'success'
'passed' 'passed'
when 'success_with_warnings' when 'success_with_warnings'
'passed with warnings' 'passed with warnings'
when 'manual' when 'manual'
'waiting for manual action' 'waiting for manual action'
else else
status status
end end
translation = "CiStatusLabel|#{label}"
s_(translation)
end end
def ci_text_for_status(status) def ci_text_for_status(status)
...@@ -35,13 +37,22 @@ module CiStatusHelper ...@@ -35,13 +37,22 @@ module CiStatusHelper
case status case status
when 'success' when 'success'
'passed' s_('CiStatusText|passed')
when 'success_with_warnings' when 'success_with_warnings'
'passed' s_('CiStatusText|passed')
when 'manual' when 'manual'
'blocked' s_('CiStatusText|blocked')
else else
status # All states are already being translated inside the detailed statuses:
# :running => Gitlab::Ci::Status::Running
# :skipped => Gitlab::Ci::Status::Skipped
# :failed => Gitlab::Ci::Status::Failed
# :success => Gitlab::Ci::Status::Success
# :canceled => Gitlab::Ci::Status::Canceled
# The following states are customized above:
# :manual => Gitlab::Ci::Status::Manual
status_translation = "CiStatusText|#{status}"
s_(status_translation)
end end
end end
......
...@@ -21,30 +21,36 @@ module NotificationsHelper ...@@ -21,30 +21,36 @@ module NotificationsHelper
end end
def notification_title(level) def notification_title(level)
# Can be anything in `NotificationSetting.level:
case level.to_sym case level.to_sym
when :participating when :participating
'Participate' s_('NotificationLevel|Participate')
when :mention when :mention
'On mention' s_('NotificationLevel|On mention')
else else
level.to_s.titlecase N_('NotificationLevel|Global')
N_('NotificationLevel|Watch')
N_('NotificationLevel|Disabled')
N_('NotificationLevel|Custom')
level = "NotificationLevel|#{level.to_s.humanize}"
s_(level)
end end
end end
def notification_description(level) def notification_description(level)
case level.to_sym case level.to_sym
when :participating when :participating
'You will only receive notifications for threads you have participated in' _('You will only receive notifications for threads you have participated in')
when :mention when :mention
'You will receive notifications only for comments in which you were @mentioned' _('You will receive notifications only for comments in which you were @mentioned')
when :watch when :watch
'You will receive notifications for any activity' _('You will receive notifications for any activity')
when :disabled when :disabled
'You will not get any notifications via email' _('You will not get any notifications via email')
when :global when :global
'Use your global notification setting' _('Use your global notification setting')
when :custom when :custom
'You will only receive notifications for the events you choose' _('You will only receive notifications for the events you choose')
end end
end end
...@@ -76,11 +82,22 @@ module NotificationsHelper ...@@ -76,11 +82,22 @@ module NotificationsHelper
end end
def notification_event_name(event) def notification_event_name(event)
# All values from NotificationSetting::EMAIL_EVENTS
case event case event
when :success_pipeline when :success_pipeline
'Successful pipeline' s_('NotificationEvent|Successful pipeline')
else else
event.to_s.humanize N_('NotificationEvent|New note')
N_('NotificationEvent|New issue')
N_('NotificationEvent|Reopen issue')
N_('NotificationEvent|Close issue')
N_('NotificationEvent|Reassign issue')
N_('NotificationEvent|New merge request')
N_('NotificationEvent|Close merge request')
N_('NotificationEvent|Reassign merge request')
N_('NotificationEvent|Merge merge request')
N_('NotificationEvent|Failed pipeline')
s_(event.to_s.humanize)
end end
end end
end end
module ProfilesHelper
def email_provider_label
return unless current_user.external_email?
current_user.email_provider.present? ? Gitlab::OAuth::Provider.label_for(current_user.email_provider) : "LDAP"
end
end
...@@ -70,15 +70,18 @@ module ProjectsHelper ...@@ -70,15 +70,18 @@ module ProjectsHelper
end end
def remove_project_message(project) def remove_project_message(project)
"You are going to remove #{project.name_with_namespace}.\n Removed project CANNOT be restored!\n Are you ABSOLUTELY sure?" _("You are going to remove %{project_name_with_namespace}.\nRemoved project CANNOT be restored!\nAre you ABSOLUTELY sure?") %
{ project_name_with_namespace: project.name_with_namespace }
end end
def transfer_project_message(project) def transfer_project_message(project)
"You are going to transfer #{project.name_with_namespace} to another owner. Are you ABSOLUTELY sure?" _("You are going to transfer %{project_name_with_namespace} to another owner. Are you ABSOLUTELY sure?") %
{ project_name_with_namespace: project.name_with_namespace }
end end
def remove_fork_project_message(project) def remove_fork_project_message(project)
"You are going to remove the fork relationship to source project #{@project.forked_from_project.name_with_namespace}. Are you ABSOLUTELY sure?" _("You are going to remove the fork relationship to source project %{forked_from_project}. Are you ABSOLUTELY sure?") %
{ forked_from_project: @project.forked_from_project.name_with_namespace }
end end
def project_nav_tabs def project_nav_tabs
...@@ -159,12 +162,13 @@ module ProjectsHelper ...@@ -159,12 +162,13 @@ module ProjectsHelper
end end
def link_to_autodeploy_doc def link_to_autodeploy_doc
link_to 'About auto deploy', help_page_path('ci/autodeploy/index'), target: '_blank' link_to _('About auto deploy'), help_page_path('ci/autodeploy/index'), target: '_blank'
end end
def autodeploy_flash_notice(branch_name) def autodeploy_flash_notice(branch_name)
"Branch <strong>#{truncate(sanitize(branch_name))}</strong> was created. To set up auto deploy, \ translation = _("Branch <strong>%{branch_name}</strong> was created. To set up auto deploy, choose a GitLab CI Yaml template and commit your changes. %{link_to_autodeploy_doc}") %
choose a GitLab CI Yaml template and commit your changes. #{link_to_autodeploy_doc}".html_safe { branch_name: truncate(sanitize(branch_name)), link_to_autodeploy_doc: link_to_autodeploy_doc }
translation.html_safe
end end
def project_list_cache_key(project) def project_list_cache_key(project)
...@@ -250,11 +254,11 @@ module ProjectsHelper ...@@ -250,11 +254,11 @@ module ProjectsHelper
def project_lfs_status(project) def project_lfs_status(project)
if project.lfs_enabled? if project.lfs_enabled?
content_tag(:span, class: 'lfs-enabled') do content_tag(:span, class: 'lfs-enabled') do
'Enabled' s_('LFSStatus|Enabled')
end end
else else
content_tag(:span, class: 'lfs-disabled') do content_tag(:span, class: 'lfs-disabled') do
'Disabled' s_('LFSStatus|Disabled')
end end
end end
end end
...@@ -273,7 +277,7 @@ module ProjectsHelper ...@@ -273,7 +277,7 @@ module ProjectsHelper
if current_user if current_user
current_user.name current_user.name
else else
"Your name" _("Your name")
end end
end end
...@@ -319,17 +323,18 @@ module ProjectsHelper ...@@ -319,17 +323,18 @@ module ProjectsHelper
if project.last_activity_at if project.last_activity_at
time_ago_with_tooltip(project.last_activity_at, placement: 'bottom', html_class: 'last_activity_time_ago') time_ago_with_tooltip(project.last_activity_at, placement: 'bottom', html_class: 'last_activity_time_ago')
else else
"Never" s_("ProjectLastActivity|Never")
end end
end end
def add_special_file_path(project, file_name:, commit_message: nil, branch_name: nil, context: nil) def add_special_file_path(project, file_name:, commit_message: nil, branch_name: nil, context: nil)
commit_message ||= s_("CommitMessage|Add %{file_name}") % { file_name: file_name.downcase }
namespace_project_new_blob_path( namespace_project_new_blob_path(
project.namespace, project.namespace,
project, project,
project.default_branch || 'master', project.default_branch || 'master',
file_name: file_name, file_name: file_name,
commit_message: commit_message || "Add #{file_name.downcase}", commit_message: commit_message,
branch_name: branch_name, branch_name: branch_name,
context: context context: context
) )
...@@ -480,9 +485,9 @@ module ProjectsHelper ...@@ -480,9 +485,9 @@ module ProjectsHelper
def project_feature_options def project_feature_options
{ {
'Disabled' => ProjectFeature::DISABLED, s_('ProjectFeature|Disabled') => ProjectFeature::DISABLED,
'Only team members' => ProjectFeature::PRIVATE, s_('ProjectFeature|Only team members') => ProjectFeature::PRIVATE,
'Everyone with access' => ProjectFeature::ENABLED s_('ProjectFeature|Everyone with access') => ProjectFeature::ENABLED
} }
end end
......
...@@ -29,11 +29,11 @@ module VisibilityLevelHelper ...@@ -29,11 +29,11 @@ module VisibilityLevelHelper
def project_visibility_level_description(level) def project_visibility_level_description(level)
case level case level
when Gitlab::VisibilityLevel::PRIVATE when Gitlab::VisibilityLevel::PRIVATE
"Project access must be granted explicitly to each user." _("Project access must be granted explicitly to each user.")
when Gitlab::VisibilityLevel::INTERNAL when Gitlab::VisibilityLevel::INTERNAL
"The project can be accessed by any logged in user." _("The project can be accessed by any logged in user.")
when Gitlab::VisibilityLevel::PUBLIC when Gitlab::VisibilityLevel::PUBLIC
"The project can be accessed without any authentication." _("The project can be accessed without any authentication.")
end end
end end
...@@ -81,7 +81,9 @@ module VisibilityLevelHelper ...@@ -81,7 +81,9 @@ module VisibilityLevelHelper
end end
def visibility_level_label(level) def visibility_level_label(level)
Project.visibility_levels.key(level) # The visibility level can be:
# 'VisibilityLevel|Private', 'VisibilityLevel|Internal', 'VisibilityLevel|Public'
s_(Project.visibility_levels.key(level))
end end
def restricted_visibility_levels(show_all = false) def restricted_visibility_levels(show_all = false)
......
class IssueLink < ActiveRecord::Base
belongs_to :source, class_name: 'Issue'
belongs_to :target, class_name: 'Issue'
validates :source, presence: true
validates :target, presence: true
validates :source, uniqueness: { scope: :target_id, message: 'is already related' }
validate :check_self_relation
private
def check_self_relation
return unless source && target
if source == target
errors.add(:source, 'cannot be related to itself')
end
end
end
...@@ -7,14 +7,12 @@ class License < ActiveRecord::Base ...@@ -7,14 +7,12 @@ class License < ActiveRecord::Base
AUDITOR_USER_FEATURE = 'GitLab_Auditor_User'.freeze AUDITOR_USER_FEATURE = 'GitLab_Auditor_User'.freeze
SERVICE_DESK_FEATURE = 'GitLab_ServiceDesk'.freeze SERVICE_DESK_FEATURE = 'GitLab_ServiceDesk'.freeze
OBJECT_STORAGE_FEATURE = 'GitLab_ObjectStorage'.freeze OBJECT_STORAGE_FEATURE = 'GitLab_ObjectStorage'.freeze
RELATED_ISSUES_FEATURE = 'RelatedIssues'.freeze
FEATURE_CODES = { FEATURE_CODES = {
geo: GEO_FEATURE, geo: GEO_FEATURE,
auditor_user: AUDITOR_USER_FEATURE, auditor_user: AUDITOR_USER_FEATURE,
service_desk: SERVICE_DESK_FEATURE, service_desk: SERVICE_DESK_FEATURE,
object_storage: OBJECT_STORAGE_FEATURE, object_storage: OBJECT_STORAGE_FEATURE,
related_issues: RELATED_ISSUES_FEATURE,
# Features that make sense to Namespace: # Features that make sense to Namespace:
deploy_board: DEPLOY_BOARD_FEATURE, deploy_board: DEPLOY_BOARD_FEATURE,
file_lock: FILE_LOCK_FEATURE file_lock: FILE_LOCK_FEATURE
...@@ -26,7 +24,7 @@ class License < ActiveRecord::Base ...@@ -26,7 +24,7 @@ class License < ActiveRecord::Base
EARLY_ADOPTER_PLAN = 'early_adopter'.freeze EARLY_ADOPTER_PLAN = 'early_adopter'.freeze
EES_FEATURES = [ EES_FEATURES = [
{ RELATED_ISSUES_FEATURE => 1 } # ..
].freeze ].freeze
EEP_FEATURES = [ EEP_FEATURES = [
......
...@@ -3,7 +3,7 @@ class SystemNoteMetadata < ActiveRecord::Base ...@@ -3,7 +3,7 @@ class SystemNoteMetadata < ActiveRecord::Base
commit description merge confidential visible label assignee cross_reference commit description merge confidential visible label assignee cross_reference
title time_tracking branch milestone discussion task moved opened closed merged title time_tracking branch milestone discussion task moved opened closed merged
outdated outdated
approved unapproved relate unrelate approved unapproved
].freeze ].freeze
validates :note, presence: true validates :note, presence: true
......
...@@ -22,11 +22,6 @@ module EE ...@@ -22,11 +22,6 @@ module EE
cannot! :create_note cannot! :create_note
cannot! :read_project cannot! :read_project
end end
unless project.feature_available?(:related_issues)
cannot! :read_issue_link
cannot! :admin_issue_link
end
end end
end end
end end
...@@ -55,9 +55,6 @@ class ProjectPolicy < BasePolicy ...@@ -55,9 +55,6 @@ class ProjectPolicy < BasePolicy
can! :read_pipeline_schedule can! :read_pipeline_schedule
can! :read_build can! :read_build
end end
# EE-only
can! :read_issue_link
end end
def reporter_access! def reporter_access!
...@@ -82,9 +79,6 @@ class ProjectPolicy < BasePolicy ...@@ -82,9 +79,6 @@ class ProjectPolicy < BasePolicy
if project.feature_available?(:deploy_board) || Rails.env.development? if project.feature_available?(:deploy_board) || Rails.env.development?
can! :read_deploy_board can! :read_deploy_board
end end
# EE-only
can! :admin_issue_link
end end
# Permissions given when an user is team member of a project # Permissions given when an user is team member of a project
...@@ -327,8 +321,5 @@ class ProjectPolicy < BasePolicy ...@@ -327,8 +321,5 @@ class ProjectPolicy < BasePolicy
# NOTE: may be overridden by IssuePolicy # NOTE: may be overridden by IssuePolicy
can! :read_issue can! :read_issue
# EE-only
can! :read_issue_link
end end
end end
module IssueLinks
class CreateService < BaseService
def initialize(issue, user, params)
@issue, @current_user, @params = issue, user, params.dup
end
def execute
if referenced_issues.blank?
return error('No Issue found for given reference', 401)
end
create_issue_links
success
end
private
def create_issue_links
referenced_issues.each do |referenced_issue|
create_notes(referenced_issue) if relate_issues(referenced_issue)
end
end
def relate_issues(referenced_issue)
IssueLink.create(source: @issue, target: referenced_issue)
end
def create_notes(referenced_issue)
SystemNoteService.relate_issue(@issue, referenced_issue, current_user)
SystemNoteService.relate_issue(referenced_issue, @issue, current_user)
end
def referenced_issues
@referenced_issues ||= begin
issue_references = params[:issue_references]
text = issue_references.join(' ')
extractor = Gitlab::ReferenceExtractor.new(@issue.project, @current_user)
extractor.analyze(text)
extractor.issues.select do |issue|
can?(current_user, :admin_issue_link, issue)
end
end
end
end
end
module IssueLinks
class DestroyService < BaseService
def initialize(issue_link, user)
@issue_link = issue_link
@current_user = user
@issue = issue_link.source
@referenced_issue = issue_link.target
end
def execute
remove_relation
create_notes
success(message: 'Relation was removed')
end
private
def remove_relation
@issue_link.destroy!
end
def create_notes
SystemNoteService.unrelate_issue(@issue, @referenced_issue, current_user)
SystemNoteService.unrelate_issue(@referenced_issue, @issue, current_user)
end
end
end
module IssueLinks
class ListService
include Gitlab::Routing
def initialize(issue, user)
@issue, @current_user, @project = issue, user, issue.project
end
def execute
issues.map do |referenced_issue|
{
id: referenced_issue.id,
iid: referenced_issue.iid,
title: referenced_issue.title,
state: referenced_issue.state,
project_path: referenced_issue.project.path,
namespace_full_path: referenced_issue.project.namespace.full_path,
path: namespace_project_issue_path(referenced_issue.project.namespace, referenced_issue.project, referenced_issue.iid),
destroy_relation_path: destroy_relation_path(referenced_issue)
}
end
end
private
def issues
related_issues = Issue
.select(['issues.*', 'issue_links.id AS issue_links_id'])
.joins("INNER JOIN issue_links ON
(issue_links.source_id = issues.id AND issue_links.target_id = #{@issue.id})
OR
(issue_links.target_id = issues.id AND issue_links.source_id = #{@issue.id})")
.preload(project: :namespace)
.reorder('issue_links_id')
Ability.issues_readable_by_user(related_issues, @current_user)
end
def destroy_relation_path(issue)
# Make sure the user can admin both the current issue AND the
# referenced issue projects in order to return the removal link.
if can_destroy_issue_link_on_current_project? && can_destroy_issue_link?(issue.project)
namespace_project_issue_link_path(@project.namespace,
@issue.project,
@issue.iid,
issue.issue_links_id)
end
end
def can_destroy_issue_link_on_current_project?
return @can_destroy_on_current_project if defined?(@can_destroy_on_current_project)
@can_destroy_on_current_project = can_destroy_issue_link?(@project)
end
def can_destroy_issue_link?(project)
Ability.allowed?(@current_user, :admin_issue_link, project)
end
end
end
...@@ -552,38 +552,6 @@ module SystemNoteService ...@@ -552,38 +552,6 @@ module SystemNoteService
create_note(NoteSummary.new(noteable, project, author, body, action: 'moved')) create_note(NoteSummary.new(noteable, project, author, body, action: 'moved'))
end end
#
# noteable - Noteable object
# noteable_ref - Referenced noteable object
# user - User performing reference
#
# Example Note text:
#
# "marked this issue as related to gitlab-ce#9001"
#
# Returns the created Note object
def relate_issue(noteable, noteable_ref, user)
body = "marked this issue as related to #{noteable_ref.to_reference(noteable.project)}"
create_note(NoteSummary.new(noteable, noteable.project, user, body, action: 'relate'))
end
#
# noteable - Noteable object
# noteable_ref - Referenced noteable object
# user - User performing reference
#
# Example Note text:
#
# "removed the relation with gitlab-ce#9001"
#
# Returns the created Note object
def unrelate_issue(noteable, noteable_ref, user)
body = "removed the relation with #{noteable_ref.to_reference(noteable.project)}"
create_note(NoteSummary.new(noteable, noteable.project, user, body, action: 'unrelate'))
end
# Called when the merge request is approved by user # Called when the merge request is approved by user
# #
# noteable - Noteable object # noteable - Noteable object
......
...@@ -33,6 +33,7 @@ ...@@ -33,6 +33,7 @@
= webpack_bundle_tag "runtime" = webpack_bundle_tag "runtime"
= webpack_bundle_tag "common" = webpack_bundle_tag "common"
= webpack_bundle_tag "locale"
= webpack_bundle_tag "main" = webpack_bundle_tag "main"
= webpack_bundle_tag "raven" if current_application_settings.clientside_sentry_enabled = webpack_bundle_tag "raven" if current_application_settings.clientside_sentry_enabled
= webpack_bundle_tag "test" if Rails.env.test? = webpack_bundle_tag "test" if Rails.env.test?
......
...@@ -49,10 +49,10 @@ ...@@ -49,10 +49,10 @@
.form-group .form-group
= f.label :email, class: "label-light" = f.label :email, class: "label-light"
- if @user.ldap_user? && @user.ldap_email? - if @user.external_email?
= f.text_field :email, class: "form-control", required: true, readonly: true = f.text_field :email, class: "form-control", required: true, readonly: true
%span.help-block.light %span.help-block.light
Your email address was automatically set based on the LDAP server. Your email address was automatically set based on your #{email_provider_label} account.
- else - else
- if @user.temp_oauth_email? - if @user.temp_oauth_email?
= f.text_field :email, class: "form-control", required: true, value: nil = f.text_field :email, class: "form-control", required: true, value: nil
......
= link_to namespace_project_find_file_path(@project.namespace, @project, @ref), class: 'btn btn-grouped shortcuts-find-file', rel: 'nofollow' do = link_to namespace_project_find_file_path(@project.namespace, @project, @ref), class: 'btn btn-grouped shortcuts-find-file', rel: 'nofollow' do
= icon('search') = icon('search')
%span Find file %span= _('Find file')
...@@ -4,17 +4,14 @@ ...@@ -4,17 +4,14 @@
.nav-links.sub-nav.scrolling-tabs .nav-links.sub-nav.scrolling-tabs
%ul{ class: container_class } %ul{ class: container_class }
= nav_link(path: 'projects#show') do = nav_link(path: 'projects#show') do
= link_to project_path(@project), title: 'Project home', class: 'shortcuts-project' do = link_to project_path(@project), title: _('Project home'), class: 'shortcuts-project' do
%span %span= _('Home')
Home
= nav_link(path: 'projects#activity') do = nav_link(path: 'projects#activity') do
= link_to activity_project_path(@project), title: 'Activity', class: 'shortcuts-project-activity' do = link_to activity_project_path(@project), title: _('Activity'), class: 'shortcuts-project-activity' do
%span %span= _('Activity')
Activity
- if can?(current_user, :read_cycle_analytics, @project) - if can?(current_user, :read_cycle_analytics, @project)
= nav_link(path: 'cycle_analytics#show') do = nav_link(path: 'cycle_analytics#show') do
= link_to project_cycle_analytics_path(@project), title: 'Cycle Analytics', class: 'shortcuts-project-cycle-analytics' do = link_to project_cycle_analytics_path(@project), title: _('Cycle Analytics'), class: 'shortcuts-project-cycle-analytics' do
%span %span= _('Cycle Analytics')
Cycle Analytics
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
- if forked_from_project = @project.forked_from_project - if forked_from_project = @project.forked_from_project
%p %p
Forked from #{ s_('ForkedFromProjectPath|Forked from') }
= link_to project_path(forked_from_project) do = link_to project_path(forked_from_project) do
= forked_from_project.namespace.try(:name) = forked_from_project.namespace.try(:name)
- if @project.mirror? - if @project.mirror?
......
...@@ -15,4 +15,4 @@ ...@@ -15,4 +15,4 @@
.pull-right .pull-right
= link_to new_mr_path_from_push_event(event), title: "New merge request", class: "btn btn-info btn-sm" do = link_to new_mr_path_from_push_event(event), title: "New merge request", class: "btn btn-info btn-sm" do
Create merge request #{ _('Create merge request') }
...@@ -3,18 +3,18 @@ ...@@ -3,18 +3,18 @@
.modal-content .modal-content
.modal-header .modal-header
%a.close{ href: "#", "data-dismiss" => "modal" } × %a.close{ href: "#", "data-dismiss" => "modal" } ×
%h3.page-title Create New Directory %h3.page-title= _('Create New Directory')
.modal-body .modal-body
= form_tag namespace_project_create_dir_path(@project.namespace, @project, @id), method: :post, remote: false, class: 'form-horizontal js-create-dir-form js-quick-submit js-requires-input' do = form_tag namespace_project_create_dir_path(@project.namespace, @project, @id), method: :post, remote: false, class: 'form-horizontal js-create-dir-form js-quick-submit js-requires-input' do
.form-group .form-group
= label_tag :dir_name, 'Directory name', class: 'control-label' = label_tag :dir_name, _('Directory name'), class: 'control-label'
.col-sm-10 .col-sm-10
= text_field_tag :dir_name, params[:dir_name], required: true, class: 'form-control' = text_field_tag :dir_name, params[:dir_name], required: true, class: 'form-control'
= render 'shared/new_commit_form', placeholder: "Add new directory" = render 'shared/new_commit_form', placeholder: _("Add new directory")
.form-actions .form-actions
= submit_tag "Create directory", class: 'btn btn-create' = submit_tag _("Create directory"), class: 'btn btn-create'
= link_to "Cancel", '#', class: "btn btn-cancel", "data-dismiss" => "modal" = link_to "Cancel", '#', class: "btn btn-cancel", "data-dismiss" => "modal"
- unless can?(current_user, :push_code, @project) - unless can?(current_user, :push_code, @project)
......
...@@ -2,29 +2,29 @@ ...@@ -2,29 +2,29 @@
- if !project.empty_repo? && can?(current_user, :download_code, project) - if !project.empty_repo? && can?(current_user, :download_code, project)
.project-action-button.dropdown.inline> .project-action-button.dropdown.inline>
%button.btn.has-tooltip{ title: 'Download', 'data-toggle' => 'dropdown', 'aria-label' => 'Download' } %button.btn.has-tooltip{ title: 'Download', 'data-toggle' => 'dropdown', 'aria-label' => s_('DownloadSource|Download') }
= icon('download') = icon('download')
= icon("caret-down") = icon("caret-down")
%span.sr-only %span.sr-only= _('Select Archive Format')
Select Archive Format
%ul.dropdown-menu.dropdown-menu-align-right{ role: 'menu' } %ul.dropdown-menu.dropdown-menu-align-right{ role: 'menu' }
%li.dropdown-header Source code %li.dropdown-header
#{ _('Source code') }
%li %li
= link_to archive_namespace_project_repository_path(project.namespace, project, ref: ref, format: 'zip'), rel: 'nofollow', download: '' do = link_to archive_namespace_project_repository_path(project.namespace, project, ref: ref, format: 'zip'), rel: 'nofollow', download: '' do
%i.fa.fa-download %i.fa.fa-download
%span Download zip %span= _('Download zip')
%li %li
= link_to archive_namespace_project_repository_path(project.namespace, project, ref: ref, format: 'tar.gz'), rel: 'nofollow', download: '' do = link_to archive_namespace_project_repository_path(project.namespace, project, ref: ref, format: 'tar.gz'), rel: 'nofollow', download: '' do
%i.fa.fa-download %i.fa.fa-download
%span Download tar.gz %span= _('Download tar.gz')
%li %li
= link_to archive_namespace_project_repository_path(project.namespace, project, ref: ref, format: 'tar.bz2'), rel: 'nofollow', download: '' do = link_to archive_namespace_project_repository_path(project.namespace, project, ref: ref, format: 'tar.bz2'), rel: 'nofollow', download: '' do
%i.fa.fa-download %i.fa.fa-download
%span Download tar.bz2 %span= _('Download tar.bz2')
%li %li
= link_to archive_namespace_project_repository_path(project.namespace, project, ref: ref, format: 'tar'), rel: 'nofollow', download: '' do = link_to archive_namespace_project_repository_path(project.namespace, project, ref: ref, format: 'tar'), rel: 'nofollow', download: '' do
%i.fa.fa-download %i.fa.fa-download
%span Download tar %span= _('Download tar')
- if pipeline - if pipeline
- artifacts = pipeline.builds.latest.with_artifacts - artifacts = pipeline.builds.latest.with_artifacts
...@@ -39,4 +39,5 @@ ...@@ -39,4 +39,5 @@
%li %li
= link_to latest_succeeded_namespace_project_artifacts_path(project.namespace, project, "#{ref}/download", job: job.name), rel: 'nofollow', download: '' do = link_to latest_succeeded_namespace_project_artifacts_path(project.namespace, project, "#{ref}/download", job: job.name), rel: 'nofollow', download: '' do
%i.fa.fa-download %i.fa.fa-download
%span Download '#{job.name}' %span
#{ s_('DownloadArtifacts|Download') } '#{job.name}'
...@@ -12,19 +12,19 @@ ...@@ -12,19 +12,19 @@
%li %li
= link_to new_namespace_project_issue_path(@project.namespace, @project) do = link_to new_namespace_project_issue_path(@project.namespace, @project) do
= icon('exclamation-circle fw') = icon('exclamation-circle fw')
New issue #{ _('New issue') }
- if merge_project - if merge_project
%li %li
= link_to new_namespace_project_merge_request_path(merge_project.namespace, merge_project) do = link_to new_namespace_project_merge_request_path(merge_project.namespace, merge_project) do
= icon('tasks fw') = icon('tasks fw')
New merge request #{ _('New merge request') }
- if can_create_snippet - if can_create_snippet
%li %li
= link_to new_namespace_project_snippet_path(@project.namespace, @project) do = link_to new_namespace_project_snippet_path(@project.namespace, @project) do
= icon('file-text-o fw') = icon('file-text-o fw')
New snippet #{ _('New snippet') }
- if can_create_issue || merge_project || can_create_snippet - if can_create_issue || merge_project || can_create_snippet
%li.divider %li.divider
...@@ -33,20 +33,20 @@ ...@@ -33,20 +33,20 @@
%li %li
= link_to namespace_project_new_blob_path(@project.namespace, @project, @project.default_branch || 'master') do = link_to namespace_project_new_blob_path(@project.namespace, @project, @project.default_branch || 'master') do
= icon('file fw') = icon('file fw')
New file #{ _('New file') }
%li %li
= link_to new_namespace_project_branch_path(@project.namespace, @project) do = link_to new_namespace_project_branch_path(@project.namespace, @project) do
= icon('code-fork fw') = icon('code-fork fw')
New branch #{ _('New branch') }
%li %li
= link_to new_namespace_project_tag_path(@project.namespace, @project) do = link_to new_namespace_project_tag_path(@project.namespace, @project) do
= icon('tags fw') = icon('tags fw')
New tag #{ _('New tag') }
- elsif current_user && current_user.already_forked?(@project) - elsif current_user && current_user.already_forked?(@project)
%li %li
= link_to namespace_project_new_blob_path(@project.namespace, @project, @project.default_branch || 'master') do = link_to namespace_project_new_blob_path(@project.namespace, @project, @project.default_branch || 'master') do
= icon('file fw') = icon('file fw')
New file #{ _('New file') }
- elsif can?(current_user, :fork_project, @project) - elsif can?(current_user, :fork_project, @project)
%li %li
- continue_params = { to: namespace_project_new_blob_path(@project.namespace, @project, @project.default_branch || 'master'), - continue_params = { to: namespace_project_new_blob_path(@project.namespace, @project, @project.default_branch || 'master'),
...@@ -56,4 +56,4 @@ ...@@ -56,4 +56,4 @@
continue: continue_params) continue: continue_params)
= link_to fork_path, method: :post do = link_to fork_path, method: :post do
= icon('file fw') = icon('file fw')
New file #{ _('New file') }
- unless @project.empty_repo? - unless @project.empty_repo?
- if current_user && can?(current_user, :fork_project, @project) - if current_user && can?(current_user, :fork_project, @project)
- if current_user.already_forked?(@project) && current_user.manageable_namespaces.size < 2 - if current_user.already_forked?(@project) && current_user.manageable_namespaces.size < 2
= link_to namespace_project_path(current_user, current_user.fork_of(@project)), title: 'Go to your fork', class: 'btn has-tooltip' do = link_to namespace_project_path(current_user, current_user.fork_of(@project)), title: _('Go to your fork'), class: 'btn has-tooltip' do
= custom_icon('icon_fork') = custom_icon('icon_fork')
%span Fork %span= s_('GoToYourFork|Fork')
- else - else
= link_to new_namespace_project_fork_path(@project.namespace, @project), class: 'btn' do = link_to new_namespace_project_fork_path(@project.namespace, @project), class: 'btn' do
= custom_icon('icon_fork') = custom_icon('icon_fork')
%span Fork %span= s_('CreateNewFork|Fork')
.count-with-arrow .count-with-arrow
%span.arrow %span.arrow
= link_to namespace_project_forks_path(@project.namespace, @project), title: 'Forks', class: 'count' do = link_to namespace_project_forks_path(@project.namespace, @project), title: n_('Forks', @project.forks_count), class: 'count' do
= @project.forks_count = @project.forks_count
- if koding_enabled? && current_user && @repository.koding_yml && can_push_branch?(@project, @project.default_branch) - if koding_enabled? && current_user && @repository.koding_yml && can_push_branch?(@project, @project.default_branch)
= link_to koding_project_url(@project), class: 'btn project-action-button inline', target: '_blank', rel: 'noopener noreferrer' do = link_to koding_project_url(@project), class: 'btn project-action-button inline', target: '_blank', rel: 'noopener noreferrer' do
Run in IDE (Koding) _('Run in IDE (Koding)')
...@@ -2,19 +2,19 @@ ...@@ -2,19 +2,19 @@
= link_to toggle_star_namespace_project_path(@project.namespace, @project), { class: 'btn star-btn toggle-star', method: :post, remote: true } do = link_to toggle_star_namespace_project_path(@project.namespace, @project), { class: 'btn star-btn toggle-star', method: :post, remote: true } do
- if current_user.starred?(@project) - if current_user.starred?(@project)
= icon('star') = icon('star')
%span.starred Unstar %span.starred= _('Unstar')
- else - else
= icon('star-o') = icon('star-o')
%span Star %span= s_('StarProject|Star')
.count-with-arrow .count-with-arrow
%span.arrow %span.arrow
%span.count.star-count %span.count.star-count
= @project.star_count = @project.star_count
- else - else
= link_to new_user_session_path, class: 'btn has-tooltip star-btn', title: 'You must sign in to star a project' do = link_to new_user_session_path, class: 'btn has-tooltip star-btn', title: _('You must sign in to star a project') do
= icon('star') = icon('star')
Star #{ s_('StarProject|Star') }
.count-with-arrow .count-with-arrow
%span.arrow %span.arrow
%span.count %span.count
......
...@@ -31,12 +31,12 @@ ...@@ -31,12 +31,12 @@
= preserve(markdown(commit.description, pipeline: :single_line, author: commit.author)) = preserve(markdown(commit.description, pipeline: :single_line, author: commit.author))
.commiter .commiter
= commit_author_link(commit, avatar: false, size: 24) = commit_author_link(commit, avatar: false, size: 24)
committed #{ _('committed') }
#{time_ago_with_tooltip(commit.committed_date)} #{time_ago_with_tooltip(commit.committed_date)}
.commit-actions.flex-row.hidden-xs .commit-actions.flex-row.hidden-xs
- if commit.status(ref) - if commit.status(ref)
= render_commit_status(commit, ref: ref) = render_commit_status(commit, ref: ref)
= link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit-sha btn btn-transparent" = link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit-sha btn btn-transparent"
= clipboard_button(text: commit.id, title: "Copy commit SHA to clipboard") = clipboard_button(text: commit.id, title: _("Copy commit SHA to clipboard"))
= link_to_browse_code(project, commit) = link_to_browse_code(project, commit)
...@@ -5,35 +5,35 @@ ...@@ -5,35 +5,35 @@
%ul{ class: (container_class) } %ul{ class: (container_class) }
= 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
Files #{ _('Files') }
= nav_link(controller: [:commit, :commits]) do = nav_link(controller: [:commit, :commits]) do
= link_to namespace_project_commits_path(@project.namespace, @project, current_ref) do = link_to namespace_project_commits_path(@project.namespace, @project, current_ref) do
Commits #{ _('Commits') }
= nav_link(html_options: {class: branches_tab_class}) do = nav_link(html_options: {class: branches_tab_class}) do
= link_to namespace_project_branches_path(@project.namespace, @project) do = link_to namespace_project_branches_path(@project.namespace, @project) do
Branches #{ _('Branches') }
= 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') }
= nav_link(path: 'graphs#show') do = nav_link(path: 'graphs#show') do
= link_to namespace_project_graph_path(@project.namespace, @project, current_ref) do = link_to namespace_project_graph_path(@project.namespace, @project, current_ref) do
Contributors #{ _('Contributors') }
= nav_link(controller: %w(network)) do = nav_link(controller: %w(network)) do
= link_to namespace_project_network_path(@project.namespace, @project, current_ref) do = link_to namespace_project_network_path(@project.namespace, @project, current_ref) do
Graph #{ s_('ProjectNetworkGraph|Graph') }
= nav_link(controller: :compare) do = nav_link(controller: :compare) do
= link_to namespace_project_compare_index_path(@project.namespace, @project, from: @repository.root_ref, to: current_ref) do = link_to namespace_project_compare_index_path(@project.namespace, @project, from: @repository.root_ref, to: current_ref) do
Compare #{ _('Compare') }
= nav_link(path: 'graphs#charts') do = nav_link(path: 'graphs#charts') do
= link_to charts_namespace_project_graph_path(@project.namespace, @project, current_ref) do = link_to charts_namespace_project_graph_path(@project.namespace, @project, current_ref) do
Charts #{ _('Charts') }
- if @project.feature_available?(:file_lock) - if @project.feature_available?(:file_lock)
= nav_link(controller: [:path_locks]) do = nav_link(controller: [:path_locks]) do
......
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
- page_title "Cycle Analytics" - page_title "Cycle Analytics"
- content_for :page_specific_javascripts do - content_for :page_specific_javascripts do
= page_specific_javascript_bundle_tag('common_vue') = page_specific_javascript_bundle_tag('common_vue')
= page_specific_javascript_bundle_tag('locale')
= page_specific_javascript_bundle_tag('cycle_analytics') = page_specific_javascript_bundle_tag('cycle_analytics')
= render "projects/head" = render "projects/head"
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
= link_to namespace_project_tree_path(@project.namespace, @project, @ref) do = link_to namespace_project_tree_path(@project.namespace, @project, @ref) do
= @project.path = @project.path
%li.file-finder %li.file-finder
%input#file_find.form-control.file-finder-input{ type: "text", placeholder: 'Find by path', autocomplete: 'off' } %input#file_find.form-control.file-finder-input{ type: "text", placeholder: _('Find by path'), autocomplete: 'off' }
.tree-content-holder .tree-content-holder
.table-holder .table-holder
......
%h2 %h2
%i.fa.fa-warning %i.fa.fa-warning
No repository #{ _('No repository') }
%p.slead %p.slead
The repository for this project does not exist. #{ _('The repository for this project does not exist.') }
%br %br
This means you can not push code until you create an empty repository or import existing one. #{ _('This means you can not push code until you create an empty repository or import existing one.') }
%hr %hr
.no-repo-actions .no-repo-actions
= link_to namespace_project_repository_path(@project.namespace, @project), method: :post, class: 'btn btn-primary' do = link_to namespace_project_repository_path(@project.namespace, @project), method: :post, class: 'btn btn-primary' do
Create empty bare repository #{ _('Create empty bare repository') }
%strong.prepend-left-10.append-right-10 or %strong.prepend-left-10.append-right-10 or
= link_to new_namespace_project_import_path(@project.namespace, @project), class: 'btn' do = link_to new_namespace_project_import_path(@project.namespace, @project), class: 'btn' do
Import repository #{ _('Import repository') }
- if can? current_user, :remove_project, @project - if can? current_user, :remove_project, @project
.prepend-top-20 .prepend-top-20
= link_to 'Remove project', project_path(@project), data: { confirm: remove_project_message(@project)}, method: :delete, class: "btn btn-remove pull-right" = link_to _('Remove project'), project_path(@project), data: { confirm: remove_project_message(@project)}, method: :delete, class: "btn btn-remove pull-right"
...@@ -20,24 +20,24 @@ ...@@ -20,24 +20,24 @@
%ul.nav %ul.nav
%li %li
= link_to project_files_path(@project) do = link_to project_files_path(@project) do
Files (#{storage_counter(@project.statistics.total_repository_size)}) #{_('Files')} (#{storage_counter(@project.statistics.total_repository_size)})
%li %li
= link_to namespace_project_commits_path(@project.namespace, @project, current_ref) do = link_to namespace_project_commits_path(@project.namespace, @project, current_ref) do
#{'Commit'.pluralize(@project.statistics.commit_count)} (#{number_with_delimiter(@project.statistics.commit_count)}) #{n_('Commit', 'Commits', @project.statistics.commit_count)} (#{number_with_delimiter(@project.statistics.commit_count)})
%li %l
= link_to namespace_project_branches_path(@project.namespace, @project) do = link_to namespace_project_branches_path(@project.namespace, @project) do
#{'Branch'.pluralize(@repository.branch_count)} (#{number_with_delimiter(@repository.branch_count)}) #{n_('Branch', 'Branches', @repository.branch_count)} (#{number_with_delimiter(@repository.branch_count)})
%li %li
= link_to namespace_project_tags_path(@project.namespace, @project) do = link_to namespace_project_tags_path(@project.namespace, @project) do
#{'Tag'.pluralize(@repository.tag_count)} (#{number_with_delimiter(@repository.tag_count)}) #{n_('Tag', 'Tags', @repository.tag_count)} (#{number_with_delimiter(@repository.tag_count)})
- if default_project_view != 'readme' && @repository.readme - if default_project_view != 'readme' && @repository.readme
%li %li
= link_to 'Readme', readme_path(@project) = link_to _('Readme'), readme_path(@project)
- if @repository.changelog - if @repository.changelog
%li %li
= link_to 'Changelog', changelog_path(@project) = link_to _('Changelog'), changelog_path(@project)
- if @repository.license_blob - if @repository.license_blob
%li %li
...@@ -45,43 +45,43 @@ ...@@ -45,43 +45,43 @@
- if @repository.contribution_guide - if @repository.contribution_guide
%li %li
= link_to 'Contribution guide', contribution_guide_path(@project) = link_to _('Contribution guide'), contribution_guide_path(@project)
- if @repository.gitlab_ci_yml - if @repository.gitlab_ci_yml
%li %li
= link_to 'CI configuration', ci_configuration_path(@project) = link_to _('CI configuration'), ci_configuration_path(@project)
- if current_user && can_push_branch?(@project, @project.default_branch) - if current_user && can_push_branch?(@project, @project.default_branch)
- unless @repository.changelog - unless @repository.changelog
%li.missing %li.missing
= link_to add_special_file_path(@project, file_name: 'CHANGELOG') do = link_to add_special_file_path(@project, file_name: 'CHANGELOG') do
Add Changelog #{ _('Add Changelog') }
- unless @repository.license_blob - unless @repository.license_blob
%li.missing %li.missing
= link_to add_special_file_path(@project, file_name: 'LICENSE') do = link_to add_special_file_path(@project, file_name: 'LICENSE') do
Add License #{ _('Add License') }
- unless @repository.contribution_guide - unless @repository.contribution_guide
%li.missing %li.missing
= link_to add_special_file_path(@project, file_name: 'CONTRIBUTING.md', commit_message: 'Add contribution guide') do = link_to add_special_file_path(@project, file_name: 'CONTRIBUTING.md', commit_message: 'Add contribution guide') do
Add Contribution guide #{ _('Add Contribution guide') }
- 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 koding_enabled? && @repository.koding_yml.blank? - if koding_enabled? && @repository.koding_yml.blank?
%li.missing %li.missing
= link_to 'Set up Koding', add_koding_stack_path(@project) = link_to _('Set up Koding'), add_koding_stack_path(@project)
- if @repository.gitlab_ci_yml.blank? && @project.deployment_service.present? - if @repository.gitlab_ci_yml.blank? && @project.deployment_service.present?
%li.missing %li.missing
= link_to add_special_file_path(@project, file_name: '.gitlab-ci.yml', commit_message: 'Set up auto deploy', branch_name: 'auto-deploy', context: 'autodeploy') do = link_to add_special_file_path(@project, file_name: '.gitlab-ci.yml', commit_message: 'Set up auto deploy', branch_name: 'auto-deploy', context: 'autodeploy') do
Set up auto deploy #{ _('Set up auto deploy') }
%div{ class: container_class } %div{ class: container_class }
- if @project.archived? - if @project.archived?
.text-warning.center.prepend-top-20 .text-warning.center.prepend-top-20
%p %p
= icon("exclamation-triangle fw") = icon("exclamation-triangle fw")
Archived project! Repository is read-only #{ _('Archived project! Repository is read-only') }
- view_path = default_project_view - view_path = default_project_view
......
...@@ -3,10 +3,10 @@ ...@@ -3,10 +3,10 @@
%table.table#tree-slider{ class: "table_#{@hex_path} tree-table" } %table.table#tree-slider{ class: "table_#{@hex_path} tree-table" }
%thead %thead
%tr %tr
%th Name %th= s_('ProjectFileTree|Name')
%th.hidden-xs %th.hidden-xs
.pull-left Last commit .pull-left= _('Last commit')
%th.text-right Last Update %th.text-right= _('Last Update')
- if @path.present? - if @path.present?
%tr.tree-item %tr.tree-item
%td.tree-item-file-name %td.tree-item-file-name
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
= render "projects/tree/readme", readme: tree.readme = render "projects/tree/readme", readme: tree.readme
- if can_edit_tree? - if can_edit_tree?
= render 'projects/blob/upload', title: 'Upload New File', placeholder: 'Upload new file', button_title: 'Upload file', form_path: namespace_project_create_blob_path(@project.namespace, @project, @id), method: :post = render 'projects/blob/upload', title: _('Upload New File'), placeholder: _('Upload New File'), button_title: _('Upload file'), form_path: namespace_project_create_blob_path(@project.namespace, @project, @id), method: :post
= render 'projects/blob/new_dir' = render 'projects/blob/new_dir'
- if @project.feature_available?(:file_lock) - if @project.feature_available?(:file_lock)
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
= render 'projects/find_file_link' = render 'projects/find_file_link'
= link_to 'History', namespace_project_commits_path(@project.namespace, @project, @id), class: 'btn btn-grouped' = link_to s_('Commits|History'), namespace_project_commits_path(@project.namespace, @project, @id), class: 'btn btn-grouped'
= render 'projects/buttons/download', project: @project, ref: @ref = render 'projects/buttons/download', project: @project, ref: @ref
...@@ -22,7 +22,7 @@ ...@@ -22,7 +22,7 @@
- if current_user - if current_user
%li %li
- if !on_top_of_branch? - if !on_top_of_branch?
%span.btn.add-to-tree.disabled.has-tooltip{ title: "You can only add files when you are on a branch", data: { container: 'body' } } %span.btn.add-to-tree.disabled.has-tooltip{ title: _("You can only add files when you are on a branch"), data: { container: 'body' } }
= icon('plus') = icon('plus')
- else - else
%span.dropdown %span.dropdown
...@@ -33,15 +33,15 @@ ...@@ -33,15 +33,15 @@
%li %li
= link_to namespace_project_new_blob_path(@project.namespace, @project, @id) do = link_to namespace_project_new_blob_path(@project.namespace, @project, @id) do
= icon('pencil fw') = icon('pencil fw')
New file #{ _('New file') }
%li %li
= link_to '#modal-upload-blob', { 'data-target' => '#modal-upload-blob', 'data-toggle' => 'modal' } do = link_to '#modal-upload-blob', { 'data-target' => '#modal-upload-blob', 'data-toggle' => 'modal' } do
= icon('file fw') = icon('file fw')
Upload file #{ _('Upload file') }
%li %li
= link_to '#modal-create-new-dir', { 'data-target' => '#modal-create-new-dir', 'data-toggle' => 'modal' } do = link_to '#modal-create-new-dir', { 'data-target' => '#modal-create-new-dir', 'data-toggle' => 'modal' } do
= icon('folder fw') = icon('folder fw')
New directory #{ _('New directory') }
- elsif can?(current_user, :fork_project, @project) - elsif can?(current_user, :fork_project, @project)
%li %li
- continue_params = { to: namespace_project_new_blob_path(@project.namespace, @project, @id), - continue_params = { to: namespace_project_new_blob_path(@project.namespace, @project, @id),
...@@ -51,7 +51,7 @@ ...@@ -51,7 +51,7 @@
continue: continue_params) continue: continue_params)
= link_to fork_path, method: :post do = link_to fork_path, method: :post do
= icon('pencil fw') = icon('pencil fw')
New file #{ _('New file') }
%li %li
- continue_params = { to: request.fullpath, - continue_params = { to: request.fullpath,
notice: edit_in_new_fork_notice + " Try to upload a file again.", notice: edit_in_new_fork_notice + " Try to upload a file again.",
...@@ -60,7 +60,7 @@ ...@@ -60,7 +60,7 @@
continue: continue_params) continue: continue_params)
= link_to fork_path, method: :post do = link_to fork_path, method: :post do
= icon('file fw') = icon('file fw')
Upload file #{ _('Upload file') }
%li %li
- continue_params = { to: request.fullpath, - continue_params = { to: request.fullpath,
notice: edit_in_new_fork_notice + " Try to create a new directory again.", notice: edit_in_new_fork_notice + " Try to create a new directory again.",
...@@ -69,14 +69,14 @@ ...@@ -69,14 +69,14 @@
continue: continue_params) continue: continue_params)
= link_to fork_path, method: :post do = link_to fork_path, method: :post do
= icon('folder fw') = icon('folder fw')
New directory #{ _('New directory') }
%li.divider %li.divider
%li %li
= link_to new_namespace_project_branch_path(@project.namespace, @project) do = link_to new_namespace_project_branch_path(@project.namespace, @project) do
= icon('code-fork fw') = icon('code-fork fw')
New branch #{ _('New branch') }
%li %li
= link_to new_namespace_project_tag_path(@project.namespace, @project) do = link_to new_namespace_project_tag_path(@project.namespace, @project) do
= icon('tags fw') = icon('tags fw')
New tag #{ _('New tag') }
- @no_container = true - @no_container = true
- page_title @path.presence || "Files", @ref - page_title @path.presence || _("Files"), @ref
= content_for :meta_tags do = content_for :meta_tags do
= auto_discovery_link_tag(:atom, namespace_project_commits_url(@project.namespace, @project, @ref, rss_url_options), title: "#{@project.name}:#{@ref} commits") = auto_discovery_link_tag(:atom, namespace_project_commits_url(@project.namespace, @project, @ref, rss_url_options), title: "#{@project.name}:#{@ref} commits")
= render "projects/commits/head" = render "projects/commits/head"
......
...@@ -22,7 +22,7 @@ ...@@ -22,7 +22,7 @@
= text_field_tag :project_clone, default_url_to_repo(project), class: "js-select-on-focus form-control", readonly: true, aria: { label: 'Project clone URL' } = text_field_tag :project_clone, default_url_to_repo(project), class: "js-select-on-focus form-control", readonly: true, aria: { label: 'Project clone URL' }
.input-group-btn .input-group-btn
= clipboard_button(target: '#project_clone', title: "Copy URL to clipboard") = clipboard_button(target: '#project_clone', title: _("Copy URL to clipboard"))
= geo_button(modal_target: '#modal-geo-info') if Gitlab::Geo.secondary? = geo_button(modal_target: '#modal-geo-info') if Gitlab::Geo.secondary?
......
- if cookies[:hide_no_password_message].blank? && !current_user.hide_no_password && current_user.require_password? - if cookies[:hide_no_password_message].blank? && !current_user.hide_no_password && current_user.require_password?
.no-password-message.alert.alert-warning .no-password-message.alert.alert-warning
You won't be able to pull or push project code via #{gitlab_config.protocol.upcase} until you #{link_to 'set a password', edit_profile_password_path} on your account - set_password_link = link_to s_('SetPasswordToCloneLink|set a password'), edit_profile_password_path
- translation_params = { protocol: gitlab_config.protocol.upcase, set_password_link: set_password_link }
- set_password_message = _("You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account") % translation_params
.alert-link-group .alert-link-group
= link_to "Don't show again", profile_path(user: {hide_no_password: true}), method: :put = link_to _("Don't show again"), profile_path(user: {hide_no_password: true}), method: :put
| |
= link_to 'Remind later', '#', class: 'hide-no-password-message' = link_to _('Remind later'), '#', class: 'hide-no-password-message'
- if cookies[:hide_no_ssh_message].blank? && !current_user.hide_no_ssh_key && current_user.require_ssh_key? - if cookies[:hide_no_ssh_message].blank? && !current_user.hide_no_ssh_key && current_user.require_ssh_key?
.no-ssh-key-message.alert.alert-warning .no-ssh-key-message.alert.alert-warning
You won't be able to pull or push project code via SSH until you #{link_to 'add an SSH key', profile_keys_path, class: 'alert-link'} to your profile - add_ssh_key_link = link_to s_('MissingSSHKeyWarningLink|add an SSH key'), profile_keys_path, class: 'alert-link'
- ssh_message = _("You won't be able to pull or push project code via SSH until you %{add_ssh_key_link} to your profile") % { add_ssh_key_link: add_ssh_key_link }
#{ ssh_message.html_safe }
.alert-link-group .alert-link-group
= link_to "Don't show again", profile_path(user: {hide_no_ssh_key: true}), method: :put, class: 'alert-link' = link_to _("Don't show again"), profile_path(user: {hide_no_ssh_key: true}), method: :put, class: 'alert-link'
| |
= link_to 'Remind later', '#', class: 'hide-no-ssh-message alert-link' = link_to _('Remind later'), '#', class: 'hide-no-ssh-message alert-link'
...@@ -6,9 +6,9 @@ ...@@ -6,9 +6,9 @@
- @options && @options.each do |key, value| - @options && @options.each do |key, value|
= hidden_field_tag key, value, id: nil = hidden_field_tag key, value, id: nil
.dropdown .dropdown
= dropdown_toggle dropdown_toggle_text, { toggle: "dropdown", selected: dropdown_toggle_text, ref: @ref, refs_url: refs_namespace_project_path(@project.namespace, @project), field_name: 'ref', submit_form_on_click: true }, { toggle_class: "js-project-refs-dropdown git-revision-dropdown-toggle" } = dropdown_toggle dropdown_toggle_text, { toggle: "dropdown", selected: dropdown_toggle_text, ref: @ref, refs_url: refs_namespace_project_path(@project.namespace, @project), field_name: 'ref', submit_form_on_click: true }, { toggle_class: "js-project-refs-dropdown" }
.dropdown-menu.dropdown-menu-selectable.git-revision-dropdown{ class: ("dropdown-menu-align-right" if local_assigns[:align_right]) } .dropdown-menu.dropdown-menu-selectable.git-revision-dropdown{ class: ("dropdown-menu-align-right" if local_assigns[:align_right]) }
= dropdown_title "Switch branch/tag" = dropdown_title _("Switch branch/tag")
= dropdown_filter "Search branches and tags" = dropdown_filter _("Search branches and tags")
= dropdown_content = dropdown_content
= dropdown_loading = dropdown_loading
...@@ -2,16 +2,17 @@ ...@@ -2,16 +2,17 @@
.project-action-button.inline .project-action-button.inline
- if can?(current_user, :"destroy_#{model_name}_member", source.members.find_by(user_id: current_user.id)) - if can?(current_user, :"destroy_#{model_name}_member", source.members.find_by(user_id: current_user.id))
= link_to "Leave #{model_name}", polymorphic_path([:leave, source, :members]), - link_text = source.is_a?(Group) ? _('Leave group') : _('Leave project')
= link_to link_text, polymorphic_path([:leave, source, :members]),
method: :delete, method: :delete,
data: { confirm: leave_confirmation_message(source) }, data: { confirm: leave_confirmation_message(source) },
class: 'btn' class: 'btn'
- elsif requester = source.requesters.find_by(user_id: current_user.id) - elsif requester = source.requesters.find_by(user_id: current_user.id)
= link_to 'Withdraw Access Request', polymorphic_path([:leave, source, :members]), = link_to _('Withdraw Access Request'), polymorphic_path([:leave, source, :members]),
method: :delete, method: :delete,
data: { confirm: remove_member_message(requester) }, data: { confirm: remove_member_message(requester) },
class: 'btn' class: 'btn'
- elsif source.request_access_enabled && can?(current_user, :request_access, source) - elsif source.request_access_enabled && can?(current_user, :request_access, source)
= link_to 'Request Access', polymorphic_path([:request_access, source, :members]), = link_to _('Request Access'), polymorphic_path([:request_access, source, :members]),
method: :post, method: :post,
class: 'btn' class: 'btn'
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
%button.close{ type: "button", "aria-label": "close", data: { dismiss: "modal" } } %button.close{ type: "button", "aria-label": "close", data: { dismiss: "modal" } }
%span{ "aria-hidden": "true" } } × %span{ "aria-hidden": "true" } } ×
%h4#custom-notifications-title.modal-title %h4#custom-notifications-title.modal-title
Custom notification events #{ _('Custom notification events') }
.modal-body .modal-body
.container-fluid .container-fluid
...@@ -13,12 +13,11 @@ ...@@ -13,12 +13,11 @@
= hidden_setting_source_input(notification_setting) = hidden_setting_source_input(notification_setting)
.row .row
.col-lg-4 .col-lg-4
%h4.prepend-top-0 %h4.prepend-top-0= _('Notification events')
Notification events
%p %p
Custom notification levels are the same as participating levels. With custom notification levels you will also receive notifications for select events. To find out more, check out - notification_link = link_to _('notification emails'), help_page_path('workflow/notifications'), target: '_blank'
= succeed "." do - paragraph = _('Custom notification levels are the same as participating levels. With custom notification levels you will also receive notifications for select events. To find out more, check out %{notification_link}.') % { notification_link: notification_link.html_safe }
%a{ href: help_page_path('workflow/notifications'), target: "_blank" } notification emails #{ paragraph.html_safe }
.col-lg-8 .col-lg-8
- NotificationSetting::EMAIL_EVENTS.each_with_index do |event, index| - NotificationSetting::EMAIL_EVENTS.each_with_index do |event, index|
- field_id = "#{notifications_menu_identifier("modal", notification_setting)}_notification_setting[#{event}]" - field_id = "#{notifications_menu_identifier("modal", notification_setting)}_notification_setting[#{event}]"
......
---
title: Allows manually adding bi-directional relationships between issues in the issue page (EES feature)
merge_request:
author:
---
title: Translate backend for Project & Repository pages
merge_request: 11183
author:
---
title: Sync email address from specified omniauth provider
merge_request: 11268
author: Robin Bobbitt
...@@ -432,6 +432,10 @@ production: &base ...@@ -432,6 +432,10 @@ production: &base
# showing GitLab's sign-in page (default: show the GitLab sign-in page) # showing GitLab's sign-in page (default: show the GitLab sign-in page)
# auto_sign_in_with_provider: saml # auto_sign_in_with_provider: saml
# Sync user's email address from the specified Omniauth provider every time the user logs
# in (default: nil). And consequently make this field read-only.
# sync_email_from_provider: cas3
# CAUTION! # CAUTION!
# This allows users to login without having a user account first. Define the allowed providers # This allows users to login without having a user account first. Define the allowed providers
# using an array, e.g. ["saml", "twitter"], or as true/false to allow all providers or none. # using an array, e.g. ["saml", "twitter"], or as true/false to allow all providers or none.
......
...@@ -190,6 +190,7 @@ Settings.omniauth['external_providers'] = [] if Settings.omniauth['external_prov ...@@ -190,6 +190,7 @@ Settings.omniauth['external_providers'] = [] if Settings.omniauth['external_prov
Settings.omniauth['block_auto_created_users'] = true if Settings.omniauth['block_auto_created_users'].nil? Settings.omniauth['block_auto_created_users'] = true if Settings.omniauth['block_auto_created_users'].nil?
Settings.omniauth['auto_link_ldap_user'] = false if Settings.omniauth['auto_link_ldap_user'].nil? Settings.omniauth['auto_link_ldap_user'] = false if Settings.omniauth['auto_link_ldap_user'].nil?
Settings.omniauth['auto_link_saml_user'] = false if Settings.omniauth['auto_link_saml_user'].nil? Settings.omniauth['auto_link_saml_user'] = false if Settings.omniauth['auto_link_saml_user'].nil?
Settings.omniauth['sync_email_from_provider'] ||= nil
Settings.omniauth['providers'] ||= [] Settings.omniauth['providers'] ||= []
Settings.omniauth['cas3'] ||= Settingslogic.new({}) Settings.omniauth['cas3'] ||= Settingslogic.new({})
......
...@@ -3,11 +3,6 @@ ...@@ -3,11 +3,6 @@
en: en:
hello: "Hello world" hello: "Hello world"
activerecord:
attributes:
issue_link:
source: Source issue
target: Target issue
errors: errors:
messages: messages:
label_already_exists_at_group_level: "already exists at group level for %{group}. Please choose another one." label_already_exists_at_group_level: "already exists at group level for %{group}. Please choose another one."
......
...@@ -311,8 +311,6 @@ constraints(ProjectUrlConstrainer.new) do ...@@ -311,8 +311,6 @@ constraints(ProjectUrlConstrainer.new) do
post :bulk_update post :bulk_update
post :export_csv post :export_csv
end end
resources :issue_links, only: [:index, :create, :destroy], as: 'links', path: 'links'
end end
resources :project_members, except: [:show, :new, :edit], constraints: { id: /[a-zA-Z.\/0-9_\-#%+]+/ }, concerns: :access_requestable do resources :project_members, except: [:show, :new, :edit], constraints: { id: /[a-zA-Z.\/0-9_\-#%+]+/ }, concerns: :access_requestable do
......
...@@ -188,15 +188,7 @@ var config = { ...@@ -188,15 +188,7 @@ var config = {
// create cacheable common library bundles // create cacheable common library bundles
new webpack.optimize.CommonsChunkPlugin({ new webpack.optimize.CommonsChunkPlugin({
names: ['main', 'common', 'runtime'], names: ['main', 'locale', 'common', 'runtime'],
}),
// locale common library
new webpack.optimize.CommonsChunkPlugin({
name: 'locale',
chunks: [
'cycle_analytics',
],
}), }),
], ],
......
class CreateIssueLinksTable < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
create_table :issue_links do |t|
t.integer :source_id, null: false, index: true
t.integer :target_id, null: false, index: true
t.timestamps null: true
end
add_index :issue_links, [:source_id, :target_id], unique: true
add_concurrent_foreign_key :issue_links, :issues, column: :source_id
add_concurrent_foreign_key :issue_links, :issues, column: :target_id
end
def down
drop_table :issue_links
end
end
class RenameUsersLdapEmailToExternalEmail < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
rename_column_concurrently :users, :ldap_email, :external_email
end
def down
cleanup_concurrent_column_rename :users, :external_email, :ldap_email
end
end
class AddEmailProviderToUsers < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def change
add_column :users, :email_provider, :string
end
end
class CleanupUsersLdapEmailRename < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
cleanup_concurrent_column_rename :users, :ldap_email, :external_email
end
def down
rename_column_concurrently :users, :external_email, :ldap_email
end
end
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20170602003304) do ActiveRecord::Schema.define(version: 20170603200744) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
...@@ -655,17 +655,6 @@ ActiveRecord::Schema.define(version: 20170602003304) do ...@@ -655,17 +655,6 @@ ActiveRecord::Schema.define(version: 20170602003304) do
add_index "issue_assignees", ["issue_id", "user_id"], name: "index_issue_assignees_on_issue_id_and_user_id", unique: true, using: :btree add_index "issue_assignees", ["issue_id", "user_id"], name: "index_issue_assignees_on_issue_id_and_user_id", unique: true, using: :btree
add_index "issue_assignees", ["user_id"], name: "index_issue_assignees_on_user_id", using: :btree add_index "issue_assignees", ["user_id"], name: "index_issue_assignees_on_user_id", using: :btree
create_table "issue_links", force: :cascade do |t|
t.integer "source_id", null: false
t.integer "target_id", null: false
t.datetime "created_at"
t.datetime "updated_at"
end
add_index "issue_links", ["source_id", "target_id"], name: "index_issue_links_on_source_id_and_target_id", unique: true, using: :btree
add_index "issue_links", ["source_id"], name: "index_issue_links_on_source_id", using: :btree
add_index "issue_links", ["target_id"], name: "index_issue_links_on_target_id", using: :btree
create_table "issue_metrics", force: :cascade do |t| create_table "issue_metrics", force: :cascade do |t|
t.integer "issue_id", null: false t.integer "issue_id", null: false
t.datetime "first_mentioned_in_commit_at" t.datetime "first_mentioned_in_commit_at"
...@@ -1682,7 +1671,6 @@ ActiveRecord::Schema.define(version: 20170602003304) do ...@@ -1682,7 +1671,6 @@ ActiveRecord::Schema.define(version: 20170602003304) do
t.text "note" t.text "note"
t.string "unlock_token" t.string "unlock_token"
t.datetime "otp_grace_period_started_at" t.datetime "otp_grace_period_started_at"
t.boolean "ldap_email", default: false, null: false
t.boolean "external", default: false t.boolean "external", default: false
t.string "incoming_email_token" t.string "incoming_email_token"
t.string "organization" t.string "organization"
...@@ -1695,6 +1683,8 @@ ActiveRecord::Schema.define(version: 20170602003304) do ...@@ -1695,6 +1683,8 @@ ActiveRecord::Schema.define(version: 20170602003304) do
t.boolean "support_bot" t.boolean "support_bot"
t.string "preferred_language" t.string "preferred_language"
t.string "rss_token" t.string "rss_token"
t.boolean "external_email", default: false, null: false
t.string "email_provider"
end end
add_index "users", ["admin"], name: "index_users_on_admin", using: :btree add_index "users", ["admin"], name: "index_users_on_admin", using: :btree
...@@ -1790,8 +1780,6 @@ ActiveRecord::Schema.define(version: 20170602003304) do ...@@ -1790,8 +1780,6 @@ ActiveRecord::Schema.define(version: 20170602003304) do
add_foreign_key "geo_repository_updated_events", "projects", on_delete: :cascade add_foreign_key "geo_repository_updated_events", "projects", on_delete: :cascade
add_foreign_key "issue_assignees", "issues", name: "fk_b7d881734a", on_delete: :cascade add_foreign_key "issue_assignees", "issues", name: "fk_b7d881734a", on_delete: :cascade
add_foreign_key "issue_assignees", "users", name: "fk_5e0c8d9154", on_delete: :cascade add_foreign_key "issue_assignees", "users", name: "fk_5e0c8d9154", on_delete: :cascade
add_foreign_key "issue_links", "issues", column: "source_id", name: "fk_c900194ff2", on_delete: :cascade
add_foreign_key "issue_links", "issues", column: "target_id", name: "fk_e71bb44f1f", on_delete: :cascade
add_foreign_key "issue_metrics", "issues", on_delete: :cascade add_foreign_key "issue_metrics", "issues", on_delete: :cascade
add_foreign_key "label_priorities", "labels", on_delete: :cascade add_foreign_key "label_priorities", "labels", on_delete: :cascade
add_foreign_key "label_priorities", "projects", on_delete: :cascade add_foreign_key "label_priorities", "projects", on_delete: :cascade
......
...@@ -14,7 +14,7 @@ Constants for project visibility levels are next: ...@@ -14,7 +14,7 @@ Constants for project visibility levels are next:
The project can be cloned by any logged in user. The project can be cloned by any logged in user.
* `public`: * `public`:
The project can be cloned without any authentication. The project can be accessed without any authentication.
## List projects ## List projects
......
This diff is collapsed.
This diff is collapsed.
doc/ci/triggers/img/triggers_page.png

108 KB | W: | H:

doc/ci/triggers/img/triggers_page.png

20.4 KB | W: | H:

doc/ci/triggers/img/triggers_page.png
doc/ci/triggers/img/triggers_page.png
doc/ci/triggers/img/triggers_page.png
doc/ci/triggers/img/triggers_page.png
  • 2-up
  • Swipe
  • Onion skin
...@@ -54,7 +54,7 @@ future GitLab releases.** ...@@ -54,7 +54,7 @@ future GitLab releases.**
| **CI_RUNNER_ID** | 8.10 | 0.5 | The unique id of runner being used | | **CI_RUNNER_ID** | 8.10 | 0.5 | The unique id of runner being used |
| **CI_RUNNER_TAGS** | 8.10 | 0.5 | The defined runner tags | | **CI_RUNNER_TAGS** | 8.10 | 0.5 | The defined runner tags |
| **CI_PIPELINE_ID** | 8.10 | 0.5 | The unique id of the current pipeline that GitLab CI uses internally | | **CI_PIPELINE_ID** | 8.10 | 0.5 | The unique id of the current pipeline that GitLab CI uses internally |
| **CI_PIPELINE_SOURCE** | 9.3 | all | The variable indicates how the pipeline was triggered, possible options are: push, web, trigger, schedule, api, pipeline | | **CI_PIPELINE_SOURCE** | 9.3 | all | ([EEP]) The variable indicates how the pipeline was triggered, possible options are: push, web, trigger, schedule, api, pipeline |
| **CI_PIPELINE_TRIGGERED** | all | all | The flag to indicate that job was [triggered] | | **CI_PIPELINE_TRIGGERED** | all | all | The flag to indicate that job was [triggered] |
| **CI_PROJECT_DIR** | all | all | The full path where the repository is cloned and where the job is run | | **CI_PROJECT_DIR** | all | all | The full path where the repository is cloned and where the job is run |
| **CI_PROJECT_ID** | all | all | The unique id of the current project that GitLab CI uses internally | | **CI_PROJECT_ID** | all | all | The unique id of the current project that GitLab CI uses internally |
...@@ -432,3 +432,4 @@ export CI_REGISTRY_PASSWORD="longalfanumstring" ...@@ -432,3 +432,4 @@ export CI_REGISTRY_PASSWORD="longalfanumstring"
[protected branches]: ../../user/project/protected_branches.md [protected branches]: ../../user/project/protected_branches.md
[protected tags]: ../../user/project/protected_tags.md [protected tags]: ../../user/project/protected_tags.md
[shellexecutors]: https://docs.gitlab.com/runner/executors/ [shellexecutors]: https://docs.gitlab.com/runner/executors/
[eep]: https://about.gitlab.com/gitlab-ee/ "Available only in GitLab Enterprise Edition Premium"
...@@ -112,8 +112,33 @@ all matching branches: ...@@ -112,8 +112,33 @@ all matching branches:
![Protected branch matches](img/protected_branches_matches.png) ![Protected branch matches](img/protected_branches_matches.png)
## Deleting a protected branch
> [Introduced][ce-21393] in GitLab 9.3.
From time to time, it may be required to delete or clean up branches that are
protected.
User with [Master permissions][perm] and up can manually delete protected
branches via GitLab's web interface:
1. Visit **Repository > Branches**
1. Click on the delete icon next to the branch you wish to delete
1. In order to prevent accidental deletion, an additional confirmation is
required
![Delete protected branches](img/protected_branches_delete.png)
Deleting a protected branch is only allowed via the web interface, not via Git.
This means that you can't accidentally delete a protected branch from your
command line or a Git client application.
## Changelog ## Changelog
**9.2**
- Allow deletion of protected branches via the web interface [gitlab-org/gitlab-ce#21393][ce-21393]
**8.11** **8.11**
- Allow creating protected branches that can't be pushed to [gitlab-org/gitlab-ce!5081][ce-5081] - Allow creating protected branches that can't be pushed to [gitlab-org/gitlab-ce!5081][ce-5081]
...@@ -128,4 +153,6 @@ all matching branches: ...@@ -128,4 +153,6 @@ all matching branches:
[ce-4665]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/4665 "Allow specifying protected branches using wildcards" [ce-4665]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/4665 "Allow specifying protected branches using wildcards"
[ce-4892]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/4892 "Allow developers to merge into a protected branch without having push access" [ce-4892]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/4892 "Allow developers to merge into a protected branch without having push access"
[ce-5081]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5081 "Allow creating protected branches that can't be pushed to" [ce-5081]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5081 "Allow creating protected branches that can't be pushed to"
[ce-21393]: https://gitlab.com/gitlab-org/gitlab-ce/issues/21393
[ee-restrict]: http://docs.gitlab.com/ee/user/project/protected_branches.html#restricting-push-and-merge-access-to-certain-users [ee-restrict]: http://docs.gitlab.com/ee/user/project/protected_branches.html#restricting-push-and-merge-access-to-certain-users
[perm]: ../permissions.md
...@@ -3,11 +3,11 @@ module Gitlab ...@@ -3,11 +3,11 @@ module Gitlab
module Status module Status
class Canceled < Status::Core class Canceled < Status::Core
def text def text
'canceled' s_('CiStatusText|canceled')
end end
def label def label
'canceled' s_('CiStatusLabel|canceled')
end end
def icon def icon
......
...@@ -3,11 +3,11 @@ module Gitlab ...@@ -3,11 +3,11 @@ module Gitlab
module Status module Status
class Created < Status::Core class Created < Status::Core
def text def text
'created' s_('CiStatusText|created')
end end
def label def label
'created' s_('CiStatusLabel|created')
end end
def icon def icon
......
...@@ -3,11 +3,11 @@ module Gitlab ...@@ -3,11 +3,11 @@ module Gitlab
module Status module Status
class Failed < Status::Core class Failed < Status::Core
def text def text
'failed' s_('CiStatusText|failed')
end end
def label def label
'failed' s_('CiStatusLabel|failed')
end end
def icon def icon
......
...@@ -3,11 +3,11 @@ module Gitlab ...@@ -3,11 +3,11 @@ module Gitlab
module Status module Status
class Manual < Status::Core class Manual < Status::Core
def text def text
'manual' s_('CiStatusText|manual')
end end
def label def label
'manual action' s_('CiStatusLabel|manual action')
end end
def icon def icon
......
...@@ -3,11 +3,11 @@ module Gitlab ...@@ -3,11 +3,11 @@ module Gitlab
module Status module Status
class Pending < Status::Core class Pending < Status::Core
def text def text
'pending' s_('CiStatusText|pending')
end end
def label def label
'pending' s_('CiStatusLabel|pending')
end end
def icon def icon
......
...@@ -4,11 +4,11 @@ module Gitlab ...@@ -4,11 +4,11 @@ module Gitlab
module Pipeline module Pipeline
class Blocked < Status::Extended class Blocked < Status::Extended
def text def text
'blocked' s_('CiStatusText|blocked')
end end
def label def label
'waiting for manual action' s_('CiStatusLabel|waiting for manual action')
end end
def self.matches?(pipeline, user) def self.matches?(pipeline, user)
......
...@@ -3,11 +3,11 @@ module Gitlab ...@@ -3,11 +3,11 @@ module Gitlab
module Status module Status
class Running < Status::Core class Running < Status::Core
def text def text
'running' s_('CiStatus|running')
end end
def label def label
'running' s_('CiStatus|running')
end end
def icon def icon
......
...@@ -3,11 +3,11 @@ module Gitlab ...@@ -3,11 +3,11 @@ module Gitlab
module Status module Status
class Skipped < Status::Core class Skipped < Status::Core
def text def text
'skipped' s_('CiStatusText|skipped')
end end
def label def label
'skipped' s_('CiStatusLabel|skipped')
end end
def icon def icon
......
...@@ -3,11 +3,11 @@ module Gitlab ...@@ -3,11 +3,11 @@ module Gitlab
module Status module Status
class Success < Status::Core class Success < Status::Core
def text def text
'passed' s_('CiStatusText|passed')
end end
def label def label
'passed' s_('CiStatusLabel|passed')
end end
def icon def icon
......
...@@ -7,11 +7,11 @@ module Gitlab ...@@ -7,11 +7,11 @@ module Gitlab
# #
class SuccessWarning < Status::Extended class SuccessWarning < Status::Extended
def text def text
'passed' s_('CiStatusText|passed')
end end
def label def label
'passed with warnings' s_('CiStatusLabel|passed with warnings')
end end
def icon def icon
......
...@@ -70,7 +70,7 @@ module Gitlab ...@@ -70,7 +70,7 @@ module Gitlab
found_user = Gitlab::LDAP::Person.find_by_dn(ldap_identity.extern_uid, adapter) found_user = Gitlab::LDAP::Person.find_by_dn(ldap_identity.extern_uid, adapter)
return found_user if found_user return found_user if found_user
if user.ldap_email? if user.external_email? && [nil, provider].include?(user.email_provider)
Gitlab::LDAP::Person.find_by_email(user.email, adapter) Gitlab::LDAP::Person.find_by_email(user.email, adapter)
end end
end end
......
...@@ -41,11 +41,6 @@ module Gitlab ...@@ -41,11 +41,6 @@ module Gitlab
def update_user_attributes def update_user_attributes
if persisted? if persisted?
if auth_hash.has_email?
gl_user.skip_reconfirmation!
gl_user.email = auth_hash.email
end
# find_or_initialize_by doesn't update `gl_user.identities`, and isn't autosaved. # find_or_initialize_by doesn't update `gl_user.identities`, and isn't autosaved.
identity = gl_user.identities.find { |identity| identity.provider == auth_hash.provider } identity = gl_user.identities.find { |identity| identity.provider == auth_hash.provider }
identity ||= gl_user.identities.build(provider: auth_hash.provider) identity ||= gl_user.identities.build(provider: auth_hash.provider)
...@@ -55,10 +50,6 @@ module Gitlab ...@@ -55,10 +50,6 @@ module Gitlab
# For an existing identity with no change in DN, this line changes nothing. # For an existing identity with no change in DN, this line changes nothing.
identity.extern_uid = auth_hash.uid identity.extern_uid = auth_hash.uid
end end
gl_user.ldap_email = auth_hash.has_email?
gl_user
end end
def changed? def changed?
...@@ -69,6 +60,10 @@ module Gitlab ...@@ -69,6 +60,10 @@ module Gitlab
ldap_config.block_auto_created_users ldap_config.block_auto_created_users
end end
def sync_email_from_provider?
true
end
def allowed? def allowed?
Gitlab::LDAP::Access.allowed?(gl_user) Gitlab::LDAP::Access.allowed?(gl_user)
end end
......
...@@ -12,6 +12,7 @@ module Gitlab ...@@ -12,6 +12,7 @@ module Gitlab
def initialize(auth_hash) def initialize(auth_hash)
self.auth_hash = auth_hash self.auth_hash = auth_hash
update_email
end end
def persisted? def persisted?
...@@ -174,6 +175,22 @@ module Gitlab ...@@ -174,6 +175,22 @@ module Gitlab
} }
end end
def sync_email_from_provider?
auth_hash.provider.to_s == Gitlab.config.omniauth.sync_email_from_provider.to_s
end
def update_email
if auth_hash.has_email? && sync_email_from_provider?
if persisted?
gl_user.skip_reconfirmation!
gl_user.email = auth_hash.email
end
gl_user.external_email = true
gl_user.email_provider = auth_hash.provider
end
end
def log def log
Gitlab::AppLogger Gitlab::AppLogger
end end
......
...@@ -41,9 +41,9 @@ module Gitlab ...@@ -41,9 +41,9 @@ module Gitlab
def options def options
{ {
'Private' => PRIVATE, N_('VisibilityLevel|Private') => PRIVATE,
'Internal' => INTERNAL, N_('VisibilityLevel|Internal') => INTERNAL,
'Public' => PUBLIC N_('VisibilityLevel|Public') => PUBLIC
} }
end end
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
require('spec_helper')
describe ProfilesController do
describe "PUT update" do
it "allows an email update from a user without an external email address" do
user = create(:user)
sign_in(user)
put :update,
user: { email: "john@gmail.com", name: "John" }
user.reload
expect(response.status).to eq(302)
expect(user.unconfirmed_email).to eq('john@gmail.com')
end
it "ignores an email update from a user with an external email address" do
ldap_user = create(:omniauth_user, external_email: true)
sign_in(ldap_user)
put :update,
user: { email: "john@gmail.com", name: "John" }
ldap_user.reload
expect(response.status).to eq(302)
expect(ldap_user.unconfirmed_email).not_to eq('john@gmail.com')
end
end
end
...@@ -284,18 +284,13 @@ describe Projects::MergeRequestsController do ...@@ -284,18 +284,13 @@ describe Projects::MergeRequestsController do
context 'number of queries' do context 'number of queries' do
it 'verifies number of queries' do it 'verifies number of queries' do
RequestStore.begin!
# pre-create objects # pre-create objects
merge_request merge_request
recorded = ActiveRecord::QueryRecorder.new { go(format: :json) } recorded = ActiveRecord::QueryRecorder.new { go(format: :json) }
expect(recorded.count).to be_within(1).of(31) expect(recorded.count).to be_within(10).of(100)
expect(recorded.cached_count).to eq(0) expect(recorded.cached_count).to eq(0)
RequestStore.end!
RequestStore.clear!
end end
end end
end end
......
FactoryGirl.define do
factory :issue_link do
source factory: :issue
target factory: :issue
end
end
...@@ -56,11 +56,10 @@ describe 'Admin::AuditLogs', feature: true, js: true do ...@@ -56,11 +56,10 @@ describe 'Admin::AuditLogs', feature: true, js: true do
end end
describe 'project events' do describe 'project events' do
let(:project) { create(:empty_project) }
let(:project_member) { create(:project_member, user: user) } let(:project_member) { create(:project_member, user: user) }
before do before do
AuditEventService.new(user, project, { action: :destroy }). AuditEventService.new(user, project_member.project, { action: :destroy }).
for_member(project_member).security_event for_member(project_member).security_event
visit admin_audit_logs_path visit admin_audit_logs_path
......
...@@ -17,10 +17,10 @@ feature "New project", feature: true do ...@@ -17,10 +17,10 @@ feature "New project", feature: true do
expect(find_field("project_visibility_level_#{level}")).to be_checked expect(find_field("project_visibility_level_#{level}")).to be_checked
end end
it 'saves visibility level on validation error' do it "saves visibility level #{level} on validation error" do
visit new_project_path visit new_project_path
choose(key) choose(s_(key))
click_button('Create project') click_button('Create project')
expect(find_field("project_visibility_level_#{level}")).to be_checked expect(find_field("project_visibility_level_#{level}")).to be_checked
......
...@@ -288,18 +288,6 @@ describe IssuesFinder do ...@@ -288,18 +288,6 @@ describe IssuesFinder do
expect(issues.count).to eq 0 expect(issues.count).to eq 0
end end
it 'returns disabled issues if feature_availability_check param set to false' do
[project1, project2].each do |project|
project.project_feature.update!(issues_access_level: ProjectFeature::DISABLED)
end
issues = described_class
.new(search_user, params.reverse_merge(scope: scope, state: 'opened', feature_availability_check: false))
.execute
expect(issues.count).to eq 3
end
end end
end end
......
...@@ -12,5 +12,11 @@ describe NotificationsHelper do ...@@ -12,5 +12,11 @@ describe NotificationsHelper do
describe 'notification_title' do describe 'notification_title' do
it { expect(notification_title(:watch)).to match('Watch') } it { expect(notification_title(:watch)).to match('Watch') }
it { expect(notification_title(:mention)).to match('On mention') } it { expect(notification_title(:mention)).to match('On mention') }
it { expect(notification_title(:global)).to match('Global') }
end
describe '#notification_event_name' do
it { expect(notification_event_name(:success_pipeline)).to match('Successful pipeline') }
it { expect(notification_event_name(:failed_pipeline)).to match('Failed pipeline') }
end end
end end
require 'rails_helper'
describe ProfilesHelper do
describe '#email_provider_label' do
it "returns nil for users without external email" do
user = create(:user)
allow(helper).to receive(:current_user).and_return(user)
expect(helper.email_provider_label).to be_nil
end
it "returns omniauth provider label for users with external email" do
stub_cas_omniauth_provider
cas_user = create(:omniauth_user, provider: 'cas3', external_email: true, email_provider: 'cas3')
allow(helper).to receive(:current_user).and_return(cas_user)
expect(helper.email_provider_label).to eq('CAS')
end
it "returns 'LDAP' for users with external email but no email provider" do
ldap_user = create(:omniauth_user, external_email: true)
allow(helper).to receive(:current_user).and_return(ldap_user)
expect(helper.email_provider_label).to eq('LDAP')
end
end
def stub_cas_omniauth_provider
provider = OpenStruct.new(
'name' => 'cas3',
'label' => 'CAS'
)
stub_omniauth_setting(providers: [provider])
end
end
...@@ -58,7 +58,7 @@ describe('Build', () => { ...@@ -58,7 +58,7 @@ describe('Build', () => {
it('displays the remove date correctly', () => { it('displays the remove date correctly', () => {
const removeDateElement = document.querySelector('.js-artifacts-remove'); const removeDateElement = document.querySelector('.js-artifacts-remove');
expect(removeDateElement.innerText.trim()).toBe('1 year'); expect(removeDateElement.innerText.trim()).toBe('1 year remaining');
}); });
}); });
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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