...
 
Commits (253)
......@@ -105,7 +105,7 @@
variables:
SETUP_DB: "false"
script:
- git fetch https://gitlab.com/gitlab-org/gitlab-ce.git v9.3.0
- git fetch https://gitlab.com/gitlab-org/gitlab-ce.git v11.5.0
- git checkout -f FETCH_HEAD
- sed -i "s/gem 'oj', '~> 2.17.4'//" Gemfile
- bundle update google-protobuf grpc
......@@ -113,6 +113,7 @@
- date
- cp config/gitlab.yml.example config/gitlab.yml
- bundle exec rake db:drop db:create db:schema:load db:seed_fu
- bundle exec rake add_limits_mysql
- date
- git checkout -f $CI_COMMIT_SHA
- bundle install $BUNDLE_INSTALL_FLAGS
......
This diff is collapsed.
......@@ -201,9 +201,6 @@ gem 'redis-rails', '~> 5.0.2'
gem 'redis', '~> 3.2'
gem 'connection_pool', '~> 2.0'
# Discord integration
gem 'discordrb-webhooks-blackst0ne', '~> 3.3', require: false
# HipChat integration
gem 'hipchat', '~> 1.5.0'
......@@ -333,7 +330,7 @@ group :development, :test do
gem 'database_cleaner', '~> 1.7.0'
gem 'factory_bot_rails', '~> 4.8.2'
gem 'rspec-rails', '~> 3.7.0'
gem 'rspec-retry', '~> 0.4.5'
gem 'rspec-retry', '~> 0.6.1'
gem 'rspec_profiling', '~> 0.0.5'
gem 'rspec-set', '~> 0.1.3'
gem 'rspec-parameterized', require: false
......@@ -407,7 +404,6 @@ gem 'health_check', '~> 2.6.0'
# System information
gem 'vmstat', '~> 2.3.0'
gem 'sys-filesystem', '~> 1.1.6'
gem 'sys-proctable', '~> 1.2'
# SSH host key support
gem 'net-ssh', '~> 5.0'
......@@ -420,7 +416,7 @@ group :ed25519 do
end
# Gitaly GRPC client
gem 'gitaly-proto', '~> 1.27.0', require: 'gitaly'
gem 'gitaly-proto', '~> 1.27.2', require: 'gitaly'
gem 'grpc', '~> 1.19.0'
......
......@@ -170,8 +170,6 @@ GEM
rotp (~> 2.0)
diff-lcs (1.3)
diffy (3.1.0)
discordrb-webhooks-blackst0ne (3.3.0)
rest-client (~> 2.0)
docile (1.1.5)
domain_name (0.5.20180417)
unf (>= 0.0.5, < 1.0.0)
......@@ -283,7 +281,7 @@ GEM
gettext_i18n_rails (>= 0.7.1)
po_to_json (>= 1.0.0)
rails (>= 3.2.0)
gitaly-proto (1.27.0)
gitaly-proto (1.27.2)
grpc (~> 1.0)
github-markup (1.7.0)
gitlab-default_value_for (3.1.1)
......@@ -780,8 +778,8 @@ GEM
rspec-expectations (~> 3.7.0)
rspec-mocks (~> 3.7.0)
rspec-support (~> 3.7.0)
rspec-retry (0.4.5)
rspec-core
rspec-retry (0.6.1)
rspec-core (> 3.3)
rspec-set (0.1.3)
rspec-support (3.7.1)
rspec_junit_formatter (0.4.1)
......@@ -904,8 +902,6 @@ GEM
httpclient (>= 2.4)
sys-filesystem (1.1.6)
ffi
sys-proctable (1.2.1)
ffi
sysexits (1.2.0)
temple (0.8.0)
test-prof (0.2.5)
......@@ -1037,7 +1033,6 @@ DEPENDENCIES
devise (~> 4.4)
devise-two-factor (~> 3.0.0)
diffy (~> 3.1.0)
discordrb-webhooks-blackst0ne (~> 3.3)
doorkeeper (~> 4.3)
doorkeeper-openid_connect (~> 1.5)
ed25519 (~> 1.2)
......@@ -1067,7 +1062,7 @@ DEPENDENCIES
gettext (~> 3.2.2)
gettext_i18n_rails (~> 1.8.0)
gettext_i18n_rails_js (~> 1.3)
gitaly-proto (~> 1.27.0)
gitaly-proto (~> 1.27.2)
github-markup (~> 1.7.0)
gitlab-default_value_for (~> 3.1.1)
gitlab-labkit (~> 0.2.0)
......@@ -1177,7 +1172,7 @@ DEPENDENCIES
rqrcode-rails3 (~> 0.1.7)
rspec-parameterized
rspec-rails (~> 3.7.0)
rspec-retry (~> 0.4.5)
rspec-retry (~> 0.6.1)
rspec-set (~> 0.1.3)
rspec_junit_formatter
rspec_profiling (~> 0.0.5)
......@@ -1211,7 +1206,6 @@ DEPENDENCIES
stackprof (~> 0.2.10)
state_machines-activerecord (~> 0.5.1)
sys-filesystem (~> 1.1.6)
sys-proctable (~> 1.2)
test-prof (~> 0.2.5)
thin (~> 1.7.0)
timecop (~> 0.8.0)
......
11.11.0-pre
11.11.8
......@@ -561,6 +561,11 @@ GitLabDropdown = (function() {
!$target.data('isLink')
) {
e.stopPropagation();
// This prevents automatic scrolling to the top
if ($target.closest('a').length) {
return false;
}
}
return true;
......
......@@ -56,7 +56,13 @@ export default {
return Api.branchSingle(projectId, currentBranchId);
},
commit(projectId, payload) {
return Api.commitMultiple(projectId, payload);
// Currently the `commit` endpoint does not support `start_sha` so we
// have to make the request in the FE. This is not ideal and will be
// resolved soon. https://gitlab.com/gitlab-org/gitlab-ce/issues/59023
const { branch, start_sha: ref } = payload;
const branchPromise = ref ? Api.createBranch(projectId, { ref, branch }) : Promise.resolve();
return branchPromise.then(() => Api.commitMultiple(projectId, payload));
},
getFiles(projectUrl, branchId) {
const url = `${projectUrl}/files/${branchId}`;
......
......@@ -123,6 +123,7 @@ export const commitChanges = ({ commit, state, getters, dispatch, rootState, roo
getters,
state,
rootState,
rootGetters,
});
return service.commit(rootState.currentProjectId, payload);
......
......@@ -135,7 +135,14 @@ export const getCommitFiles = stagedFiles =>
});
}, []);
export const createCommitPayload = ({ branch, getters, newBranch, state, rootState }) => ({
export const createCommitPayload = ({
branch,
getters,
newBranch,
state,
rootState,
rootGetters,
}) => ({
branch,
commit_message: state.commitMessage || getters.preBuiltCommitMessage,
actions: getCommitFiles(rootState.stagedFiles).map(f => ({
......@@ -146,7 +153,7 @@ export const createCommitPayload = ({ branch, getters, newBranch, state, rootSta
encoding: f.base64 ? 'base64' : 'text',
last_commit_id: newBranch || f.deleted || f.prevPath ? undefined : f.lastCommitSha,
})),
start_branch: newBranch ? rootState.currentBranchId : undefined,
start_sha: newBranch ? rootGetters.lastCommit.short_id : undefined,
});
export const createNewMergeRequestUrl = (projectUrl, source, target) =>
......
......@@ -102,13 +102,10 @@ export default {
</p>
<div class="text-center">
<a v-if="currentState.buttonPath" :href="currentState.buttonPath" class="btn btn-success">
{{ currentState.buttonText }}
</a>
<a
v-if="currentState.secondaryButtonPath"
:href="currentState.secondaryButtonPath"
class="btn"
class="btn btn-success"
>
{{ currentState.secondaryButtonText }}
</a>
......
......@@ -47,6 +47,7 @@
display: flex;
align-items: flex-start;
width: 100%;
padding-bottom: $gl-padding;
@include media-breakpoint-down(xs) {
display: block;
......
......@@ -280,3 +280,7 @@ label {
max-width: $input-lg-width;
width: 100%;
}
.input-group-text {
max-height: $input-height;
}
......@@ -7,6 +7,7 @@ $secondary: $gray-light;
$input-disabled-bg: $gray-light;
$input-border-color: $gray-200;
$input-color: $gl-text-color;
$input-font-size: $gl-font-size;
$font-family-sans-serif: $regular-font;
$font-family-monospace: $monospace-font;
$btn-line-height: 20px;
......
# frozen_string_literal: true
module ImportUrlParams
def import_url_params
return {} unless params.dig(:project, :import_url).present?
{ import_url: import_params_to_full_url(params[:project]) }
end
def import_params_to_full_url(params)
Gitlab::UrlSanitizer.new(
params[:import_url],
credentials: {
user: params[:import_url_user],
password: params[:import_url_password]
}
).full_url
end
end
......@@ -41,7 +41,7 @@ module IssuableCollections
return if pagination_disabled?
@issuables = @issuables.page(params[:page])
@issuable_meta_data = issuable_meta_data(@issuables, collection_type)
@issuable_meta_data = issuable_meta_data(@issuables, collection_type, current_user)
@total_pages = issuable_page_count
end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
......
......@@ -11,7 +11,7 @@ module IssuableCollectionsAction
.non_archived
.page(params[:page])
@issuable_meta_data = issuable_meta_data(@issues, collection_type)
@issuable_meta_data = issuable_meta_data(@issues, collection_type, current_user)
respond_to do |format|
format.html
......@@ -22,7 +22,7 @@ module IssuableCollectionsAction
def merge_requests
@merge_requests = issuables_collection.page(params[:page])
@issuable_meta_data = issuable_meta_data(@merge_requests, collection_type)
@issuable_meta_data = issuable_meta_data(@merge_requests, collection_type, current_user)
end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
......
......@@ -26,16 +26,22 @@ module MilestoneActions
end
end
# rubocop:disable Gitlab/ModuleWithInstanceVariables
def labels
respond_to do |format|
format.html { redirect_to milestone_redirect_path }
format.json do
milestone_labels = @milestone.issue_labels_visible_by_user(current_user)
render json: tabs_json("shared/milestones/_labels_tab", {
labels: @milestone.labels.map { |label| label.present(issuable_subject: @milestone.parent) } # rubocop:disable Gitlab/ModuleWithInstanceVariables
labels: milestone_labels.map do |label|
label.present(issuable_subject: @milestone.parent)
end
})
end
end
end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
private
......
......@@ -203,17 +203,17 @@ module NotesActions
# These params are also sent by the client but we need to set these based on
# target_type and target_id because we're checking permissions based on that
create_params[:noteable_type] = params[:target_type].classify
create_params[:noteable_type] = noteable.class.name
case params[:target_type]
when 'commit'
create_params[:commit_id] = params[:target_id]
when 'merge_request'
create_params[:noteable_id] = params[:target_id]
case noteable
when Commit
create_params[:commit_id] = noteable.id
when MergeRequest
create_params[:noteable_id] = noteable.id
# Notes on MergeRequest can have an extra `commit_id` context
create_params[:commit_id] = params.dig(:note, :commit_id)
else
create_params[:noteable_id] = params[:target_id]
create_params[:noteable_id] = noteable.id
end
end
end
......
......@@ -24,6 +24,9 @@ class GroupsController < Groups::ApplicationController
before_action :user_actions, only: [:show]
before_filter :validate_name, only: [:create, :update, :transfer]
skip_cross_project_access_check :index, :new, :create, :edit, :update,
:destroy, :projects
# When loading show as an atom feed, we render events that could leak cross
......@@ -220,6 +223,19 @@ class GroupsController < Groups::ApplicationController
end
end
def validate_name
group = Group.new(group_params)
unless !group.path || group.path.empty? || group.path.length > 4 || current_user.admin?
flash.now[:alert] = 'Path must have at least a length of 5.'
if action_name == 'update'
render action: 'edit'
else
@group = group
render action: 'new'
end
end
end
def build_canonical_path(group)
return group_path(group) if action_name == 'show' # root group path
......
......@@ -11,7 +11,7 @@ class Import::FogbugzController < Import::BaseController
def callback
begin
res = Gitlab::FogbugzImport::Client.new(import_params.symbolize_keys)
res = Gitlab::FogbugzImport::Client.new(import_params.to_h.symbolize_keys)
rescue
# If the URI is invalid various errors can occur
return redirect_to new_import_fogbugz_path, alert: _('Could not connect to FogBugz, check your URL')
......@@ -26,7 +26,7 @@ class Import::FogbugzController < Import::BaseController
end
def create_user_map
user_map = params[:users]
user_map = user_map_params.to_h[:users]
unless user_map.is_a?(Hash) && user_map.all? { |k, v| !v[:name].blank? }
flash.now[:alert] = _('All users must have a name.')
......@@ -99,6 +99,10 @@ class Import::FogbugzController < Import::BaseController
params.permit(:uri, :email, :password)
end
def user_map_params
params.permit(users: %w(name email gitlab_user))
end
def verify_fogbugz_import_enabled
render_404 unless fogbugz_import_enabled?
end
......
......@@ -5,6 +5,7 @@ class PasswordsController < Devise::PasswordsController
before_action :resource_from_email, only: [:create]
before_action :check_password_authentication_available, only: [:create]
before_action :prevent_cas_reset, only: [:create]
before_action :throttle_reset, only: [:create]
# rubocop: disable CodeReuse/ActiveRecord
......@@ -55,6 +56,13 @@ class PasswordsController < Devise::PasswordsController
alert: _("Password authentication is unavailable.")
end
def prevent_cas_reset
return unless resource && resource.cas_user?
redirect_to after_sending_reset_password_instructions_path_for(resource_name),
alert: "Cannot reset password for CAS user."
end
def throttle_reset
return unless resource && resource.recently_sent_password_reset?
......
......@@ -8,20 +8,6 @@ class Profiles::AccountsController < Profiles::ApplicationController
end
# rubocop: disable CodeReuse/ActiveRecord
def unlink
provider = params[:provider]
identity = current_user.identities.find_by(provider: provider)
return render_404 unless identity
if unlink_provider_allowed?(provider)
identity.destroy
else
flash[:alert] = "You are not allowed to unlink your primary login account"
end
redirect_to profile_account_path
end
# rubocop: enable CodeReuse/ActiveRecord
private
......
......@@ -13,6 +13,11 @@ class Projects::ApplicationController < ApplicationController
helper_method :repository, :can_collaborate_with_project?, :user_access
rescue_from Gitlab::Template::Finders::RepoTemplateFinder::FileNotFoundError do |exception|
log_exception(exception)
render_404
end
private
def project
......
......@@ -3,7 +3,8 @@
class Projects::BadgesController < Projects::ApplicationController
layout 'project_settings'
before_action :authorize_admin_project!, only: [:index]
before_action :no_cache_headers, except: [:index]
before_action :no_cache_headers, only: [:pipeline, :coverage]
before_action :authorize_read_build!, only: [:pipeline, :coverage]
def pipeline
pipeline_status = Gitlab::Badge::Pipeline::Status
......
......@@ -2,6 +2,7 @@
class Projects::ImportsController < Projects::ApplicationController
include ContinueParams
include ImportUrlParams
# Authorize
before_action :authorize_admin_project!
......@@ -67,10 +68,12 @@ class Projects::ImportsController < Projects::ApplicationController
end
def import_params_attributes
[:import_url]
[]
end
def import_params
params.require(:project).permit(import_params_attributes)
params.require(:project)
.permit(import_params_attributes)
.merge(import_url_params)
end
end
......@@ -41,7 +41,7 @@ class Projects::MergeRequests::ApplicationController < Projects::ApplicationCont
def set_pipeline_variables
@pipelines =
if can?(current_user, :read_pipeline, @project)
if can?(current_user, :read_pipeline, @merge_request.source_project)
@merge_request.all_pipelines
else
Ci::Pipeline.none
......
......@@ -82,7 +82,8 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
end
def pipelines
@pipelines = @merge_request.all_pipelines.page(params[:page]).per(30)
set_pipeline_variables
@pipelines = @pipelines.page(params[:page]).per(30)
Gitlab::PollingInterval.set_header(response, interval: 10_000)
......
# frozen_string_literal: true
class Projects::TemplatesController < Projects::ApplicationController
before_action :authenticate_user!, :get_template_class
before_action :authenticate_user!
before_action :authorize_can_read_issuable!
before_action :get_template_class
def show
template = @template_type.find(params[:key], project)
......@@ -13,9 +15,20 @@ class Projects::TemplatesController < Projects::ApplicationController
private
# User must have:
# - `read_merge_request` to see merge request templates, or
# - `read_issue` to see issue templates
#
# Note params[:template_type] has a route constraint to limit it to
# `merge_request` or `issue`
def authorize_can_read_issuable!
action = [:read_, params[:template_type]].join
authorize_action!(action)
end
def get_template_class
template_types = { issue: Gitlab::Template::IssueTemplate, merge_request: Gitlab::Template::MergeRequestTemplate }.with_indifferent_access
@template_type = template_types[params[:template_type]]
render json: [], status: :not_found unless @template_type
end
end
......@@ -4,7 +4,7 @@ class Projects::TriggersController < Projects::ApplicationController
before_action :authorize_admin_build!
before_action :authorize_manage_trigger!, except: [:index, :create]
before_action :authorize_admin_trigger!, only: [:edit, :update]
before_action :trigger, only: [:take_ownership, :edit, :update, :destroy]
before_action :trigger, only: [:edit, :update, :destroy]
layout 'project_settings'
......@@ -24,16 +24,6 @@ class Projects::TriggersController < Projects::ApplicationController
redirect_to project_settings_ci_cd_path(@project, anchor: 'js-pipeline-triggers')
end
def take_ownership
if trigger.update(owner: current_user)
flash[:notice] = _('Trigger was re-assigned.')
else
flash[:alert] = _('You could not take ownership of trigger.')
end
redirect_to project_settings_ci_cd_path(@project, anchor: 'js-pipeline-triggers')
end
def edit
end
......
......@@ -7,6 +7,7 @@ class ProjectsController < Projects::ApplicationController
include PreviewMarkdown
include SendFileUpload
include RecordUserLastActivity
include ImportUrlParams
prepend_before_action(only: [:show]) { authenticate_sessionless_user!(:rss) }
......@@ -297,7 +298,7 @@ class ProjectsController < Projects::ApplicationController
elsif @project.feature_available?(:issues, current_user)
@issues = issuables_collection.page(params[:page])
@collection_type = 'Issue'
@issuable_meta_data = issuable_meta_data(@issues, @collection_type)
@issuable_meta_data = issuable_meta_data(@issues, @collection_type, current_user)
end
render :show
......@@ -333,6 +334,7 @@ class ProjectsController < Projects::ApplicationController
def project_params(attributes: [])
params.require(:project)
.permit(project_params_attributes + attributes)
.merge(import_url_params)
end
def project_params_attributes
......
......@@ -18,6 +18,7 @@ class SessionsController < Devise::SessionsController
prepend_before_action :store_redirect_uri, only: [:new]
prepend_before_action :ldap_servers, only: [:new, :create]
prepend_before_action :require_no_authentication_without_flash, only: [:new, :create]
prepend_before_action :ensure_password_authentication_enabled!, if: :password_based_login?, only: [:create]
before_action :auto_sign_in_with_provider, only: [:new]
before_action :load_recaptcha
......@@ -138,6 +139,14 @@ class SessionsController < Devise::SessionsController
end
# rubocop: enable CodeReuse/ActiveRecord
def ensure_password_authentication_enabled!
render_403 unless Gitlab::CurrentSettings.password_authentication_enabled_for_web?
end
def password_based_login?
user_params[:login].present? || user_params[:password].present?
end
def user_params
params.require(:user).permit(:login, :password, :remember_me, :otp_attempt, :device_response)
end
......
......@@ -5,8 +5,8 @@ class Snippets::NotesController < ApplicationController
include ToggleAwardEmoji
skip_before_action :authenticate_user!, only: [:index]
before_action :snippet
before_action :authorize_read_snippet!, only: [:show, :index, :create]
before_action :authorize_read_snippet!, only: [:show, :index]
before_action :authorize_create_note!, only: [:create]
private
......@@ -33,4 +33,8 @@ class Snippets::NotesController < ApplicationController
def authorize_read_snippet!
return render_404 unless can?(current_user, :read_personal_snippet, snippet)
end
def authorize_create_note!
access_denied! unless can?(current_user, :create_note, noteable)
end
end
......@@ -137,7 +137,7 @@ class SnippetsController < ApplicationController
def move_temporary_files
params[:files].each do |file|
FileMover.new(file, @snippet).execute
FileMover.new(file, from_model: current_user, to_model: @snippet).execute
end
end
end
......@@ -41,7 +41,11 @@ class UploadsController < ApplicationController
when Note
can?(current_user, :read_project, model.project)
when User
true
# We validate the current user has enough (writing)
# access to itself when a secret is given.
# For instance, user avatars are readable by anyone,
# while temporary, user snippet uploads are not.
!secret? || can?(current_user, :update_user, model)
when Appearance
true
else
......@@ -56,9 +60,13 @@ class UploadsController < ApplicationController
def authorize_create_access!
return unless model
# for now we support only personal snippets comments. Only personal_snippet
# is allowed as a model to #create through routing.
authorized = can?(current_user, :create_note, model)
authorized =
case model
when User
can?(current_user, :update_user, model)
else
can?(current_user, :create_note, model)
end
render_unauthorized unless authorized
end
......@@ -75,6 +83,10 @@ class UploadsController < ApplicationController
User === model || Appearance === model
end
def secret?
params[:secret].present?
end
def upload_model_class
MODEL_CLASSES[params[:model]] || raise(UnknownUploadModelError)
end
......
......@@ -4,6 +4,8 @@ module Types
class LabelType < BaseObject
graphql_name 'Label'
authorize :read_label
field :description, GraphQL::STRING_TYPE, null: true
field :title, GraphQL::STRING_TYPE, null: false
field :color, GraphQL::STRING_TYPE, null: false
......
......@@ -4,6 +4,8 @@ module Types
class MetadataType < ::Types::BaseObject
graphql_name 'Metadata'
authorize :read_instance_metadata
field :version, GraphQL::STRING_TYPE, null: false
field :revision, GraphQL::STRING_TYPE, null: false
end
......
......@@ -4,6 +4,8 @@ module Types
class NamespaceType < BaseObject
graphql_name 'Namespace'
authorize :read_namespace
field :id, GraphQL::ID_TYPE, null: false
field :name, GraphQL::STRING_TYPE, null: false
......
......@@ -66,7 +66,7 @@ module Types
field :only_allow_merge_if_all_discussions_are_resolved, GraphQL::BOOLEAN_TYPE, null: true
field :printing_merge_request_link_enabled, GraphQL::BOOLEAN_TYPE, null: true
field :namespace, Types::NamespaceType, null: false
field :namespace, Types::NamespaceType, null: true
field :group, Types::GroupType, null: true
field :merge_requests,
......
......@@ -17,10 +17,7 @@ module Types
field :metadata, Types::MetadataType,
null: true,
resolver: Resolvers::MetadataResolver,
description: 'Metadata about GitLab' do |*args|
authorize :read_instance_metadata
end
description: 'Metadata about GitLab'
field :echo, GraphQL::STRING_TYPE, null: false, function: Functions::Echo.new
end
......
......@@ -160,6 +160,7 @@ module ApplicationSettingsHelper
:akismet_api_key,
:akismet_enabled,
:allow_local_requests_from_hooks_and_services,
:dns_rebinding_protection_enabled,
:archive_builds_in_human_readable,
:authorized_keys_enabled,
:auto_devops_enabled,
......
......@@ -280,7 +280,7 @@ module IssuablesHelper
initialTaskStatus: issuable.task_status
}
data[:hasClosingMergeRequest] = issuable.merge_requests_count != 0 if issuable.is_a?(Issue)
data[:hasClosingMergeRequest] = issuable.merge_requests_count(current_user) != 0 if issuable.is_a?(Issue)
if parent.is_a?(Group)
data[:groupPath] = parent.path
......
......@@ -5,7 +5,7 @@ module LabelsHelper
include ActionView::Helpers::TagHelper
def show_label_issuables_link?(label, issuables_type, current_user: nil, project: nil)
return true if label.is_a?(GroupLabel)
return true unless label.project_label?
return true unless project
project.feature_available?(issuables_type, current_user)
......@@ -159,13 +159,6 @@ module LabelsHelper
label.subscribed?(current_user, project) ? 'Unsubscribe' : 'Subscribe'
end
def label_deletion_confirm_text(label)
case label
when GroupLabel then _('Remove this label? This will affect all projects within the group. Are you sure?')
when ProjectLabel then _('Remove this label? Are you sure?')
end
end
def create_label_title(subject)
case subject
when Group
......@@ -200,7 +193,7 @@ module LabelsHelper
end
def label_status_tooltip(label, status)
type = label.is_a?(ProjectLabel) ? 'project' : 'group'
type = label.project_label? ? 'project' : 'group'
level = status.unsubscribed? ? type : status.sub('-level', '')
action = status.unsubscribed? ? 'Subscribe' : 'Unsubscribe'
......
......@@ -100,4 +100,8 @@ module NotificationsHelper
css_class: "icon notifications-icon js-notifications-icon"
)
end
def show_unsubscribe_title?(noteable)
can?(current_user, "read_#{noteable.to_ability_name}".to_sym, noteable)
end
end
......@@ -367,7 +367,7 @@ module ProjectsHelper
snippets: :read_project_snippet,
settings: :admin_project,
builds: :read_build,
clusters: :read_cluster,
#clusters: :read_cluster,
serverless: :read_cluster,
error_tracking: :read_sentry_issue,
labels: :read_label,
......@@ -429,11 +429,7 @@ module ProjectsHelper
end
def default_clone_protocol
if allowed_protocols_present?
enabled_protocol
else
extra_default_clone_protocol
end
extra_default_clone_protocol
end
def extra_default_clone_protocol
......
# frozen_string_literal: true
module SnippetsHelper
def snippets_upload_path(snippet, user)
return unless user
if snippet&.persisted?
upload_path('personal_snippet', id: snippet.id)
else
upload_path('user', id: user.id)
end
end
def reliable_snippet_path(snippet, opts = nil)
if snippet.project_id?
project_snippet_path(snippet.project, snippet, opts)
......
# frozen_string_literal: true
class DeviseMailer < Devise::Mailer
default from: "#{Gitlab.config.gitlab.email_display_name} <#{Gitlab.config.gitlab.email_from}>"
default reply_to: Gitlab.config.gitlab.email_reply_to
default from: proc { default_sender_address.format }
default reply_to: proc { default_reply_to_address.format }
layout 'mailer/devise'
......@@ -16,4 +16,16 @@ class DeviseMailer < Devise::Mailer
subject.join(' | ')
end
def default_sender_address
address = Mail::Address.new(Gitlab.config.gitlab.email_from)
address.display_name = Gitlab.config.gitlab.email_display_name
address
end
def default_reply_to_address
address = Mail::Address.new(Gitlab.config.gitlab.email_reply_to)
address.display_name = Gitlab.config.gitlab.email_display_name
address
end
end
......@@ -21,6 +21,7 @@ module ApplicationSettingImplementation
after_sign_up_text: nil,
akismet_enabled: false,
allow_local_requests_from_hooks_and_services: false,
dns_rebinding_protection_enabled: true,
authorized_keys_enabled: true, # TODO default to false if the instance is configured to use AuthorizedKeysCommand
container_registry_token_expire_delay: 5,
default_artifacts_expire_in: '30 days',
......
......@@ -352,7 +352,7 @@ module Ci
end
def retryable?
!archived? && (success? || failed?)
!archived? && (success? || failed? || canceled?)
end
def retries_count
......
......@@ -29,7 +29,11 @@ module Issuable
# This object is used to gather issuable meta data for displaying