Merge branch 'master' into sh-headless-chrome-support
This commit is contained in:
commit
3e75b7fa81
|
@ -474,7 +474,6 @@ db:rollback-mysql:
|
|||
variables:
|
||||
SIZE: "1"
|
||||
SETUP_DB: "false"
|
||||
RAILS_ENV: "development"
|
||||
script:
|
||||
- git clone https://gitlab.com/gitlab-org/gitlab-test.git
|
||||
/home/git/repositories/gitlab-org/gitlab-test.git
|
||||
|
|
18
.rubocop.yml
18
.rubocop.yml
|
@ -251,6 +251,10 @@ Layout/Tab:
|
|||
Layout/TrailingBlankLines:
|
||||
Enabled: true
|
||||
|
||||
# Avoid trailing whitespace.
|
||||
Layout/TrailingWhitespace:
|
||||
Enabled: true
|
||||
|
||||
# Style #######################################################################
|
||||
|
||||
# Check the naming of accessor methods for get_/set_.
|
||||
|
@ -1174,29 +1178,33 @@ RSpec/VerifiedDoubles:
|
|||
GitlabSecurity/DeepMunge:
|
||||
Enabled: true
|
||||
Exclude:
|
||||
- 'spec/**/*'
|
||||
- 'lib/**/*.rake'
|
||||
- 'spec/**/*'
|
||||
|
||||
GitlabSecurity/PublicSend:
|
||||
Enabled: true
|
||||
Exclude:
|
||||
- 'spec/**/*'
|
||||
- 'config/**/*'
|
||||
- 'db/**/*'
|
||||
- 'features/**/*'
|
||||
- 'lib/**/*.rake'
|
||||
- 'qa/**/*'
|
||||
- 'spec/**/*'
|
||||
|
||||
GitlabSecurity/RedirectToParamsUpdate:
|
||||
Enabled: true
|
||||
Exclude:
|
||||
- 'spec/**/*'
|
||||
- 'lib/**/*.rake'
|
||||
- 'spec/**/*'
|
||||
|
||||
GitlabSecurity/SqlInjection:
|
||||
Enabled: true
|
||||
Exclude:
|
||||
- 'spec/**/*'
|
||||
- 'lib/**/*.rake'
|
||||
- 'spec/**/*'
|
||||
|
||||
GitlabSecurity/SystemCommandInjection:
|
||||
Enabled: true
|
||||
Exclude:
|
||||
- 'spec/**/*'
|
||||
- 'lib/**/*.rake'
|
||||
- 'spec/**/*'
|
||||
|
|
|
@ -57,11 +57,6 @@ Layout/SpaceInsideParens:
|
|||
Layout/SpaceInsidePercentLiteralDelimiters:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 89
|
||||
# Cop supports --auto-correct.
|
||||
Layout/TrailingWhitespace:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 272
|
||||
RSpec/EmptyLineAfterFinalLet:
|
||||
Enabled: false
|
||||
|
|
2
Gemfile
2
Gemfile
|
@ -232,7 +232,7 @@ gem 'ace-rails-ap', '~> 4.1.0'
|
|||
gem 'mousetrap-rails', '~> 1.4.6'
|
||||
|
||||
# Detect and convert string character encoding
|
||||
gem 'charlock_holmes', '~> 0.7.3'
|
||||
gem 'charlock_holmes', '~> 0.7.5'
|
||||
|
||||
# Faster JSON
|
||||
gem 'oj', '~> 2.17.4'
|
||||
|
|
|
@ -117,7 +117,7 @@ GEM
|
|||
activesupport (>= 4.0.0)
|
||||
mime-types (>= 1.16)
|
||||
cause (0.1)
|
||||
charlock_holmes (0.7.3)
|
||||
charlock_holmes (0.7.5)
|
||||
childprocess (0.7.0)
|
||||
ffi (~> 1.0, >= 1.0.11)
|
||||
chronic (0.10.2)
|
||||
|
@ -983,7 +983,7 @@ DEPENDENCIES
|
|||
capybara (~> 2.15)
|
||||
capybara-screenshot (~> 1.0.0)
|
||||
carrierwave (~> 1.1)
|
||||
charlock_holmes (~> 0.7.3)
|
||||
charlock_holmes (~> 0.7.5)
|
||||
chronic (~> 0.10.2)
|
||||
chronic_duration (~> 0.10.6)
|
||||
concurrent-ruby (~> 1.0.5)
|
||||
|
|
16
PROCESS.md
16
PROCESS.md
|
@ -141,18 +141,22 @@ the stable branch are:
|
|||
* Fixes for security issues
|
||||
* New or updated translations (as long as they do not touch application code)
|
||||
|
||||
Any merge requests cherry-picked into the stable branch for a previous release
|
||||
will also be picked into the latest stable branch. These fixes will be shipped
|
||||
in the next RC for that release if it is before the 22nd. If the fixes are are
|
||||
completed on or after the 22nd, they will be shipped in a patch for that
|
||||
release.
|
||||
|
||||
During the feature freeze all merge requests that are meant to go into the upcoming
|
||||
release should have the correct milestone assigned _and_ have the label
|
||||
~"Pick into Stable" set, so that release managers can find and pick them.
|
||||
Merge requests without a milestone and this label will
|
||||
not be merged into any stable branches.
|
||||
|
||||
Fixes marked like this will be shipped in the next RC for that release. Once
|
||||
the final RC has been prepared ready for release on the 22nd, further fixes
|
||||
marked ~"Pick into Stable" will go into a patch for that release.
|
||||
|
||||
If a merge request is to be picked into more than one release it will also need
|
||||
the ~"Pick into Backports" label set to remind the release manager to change
|
||||
the milestone after cherry-picking. As before, it should still have the
|
||||
~"Pick into Stable" label and the milestone of the highest release it will be
|
||||
picked into.
|
||||
|
||||
### Asking for an exception
|
||||
|
||||
If you think a merge request should go into an RC or patch even though it does not meet these requirements,
|
||||
|
|
|
@ -17,7 +17,7 @@ window.CommitsList = (function() {
|
|||
}
|
||||
});
|
||||
|
||||
Pager.init(limit, false, false, this.processCommits);
|
||||
Pager.init(parseInt(limit, 10), false, false, this.processCommits);
|
||||
|
||||
this.content = $("#commits-list");
|
||||
this.searchField = $("#commits-search");
|
||||
|
|
|
@ -42,6 +42,10 @@ $(() => {
|
|||
$components.each(function () {
|
||||
const $this = $(this);
|
||||
const noteId = $this.attr(':note-id');
|
||||
const discussionId = $this.attr(':discussion-id');
|
||||
|
||||
if ($this.is('comment-and-resolve-btn') && !discussionId) return;
|
||||
|
||||
const tmp = Vue.extend({
|
||||
template: $this.get(0).outerHTML
|
||||
});
|
||||
|
|
|
@ -644,7 +644,7 @@ import initChangesDropdown from './init_changes_dropdown';
|
|||
return Dispatcher;
|
||||
})();
|
||||
|
||||
$(function() {
|
||||
$(window).on('load', function() {
|
||||
new Dispatcher();
|
||||
});
|
||||
}).call(window);
|
||||
|
|
|
@ -15,6 +15,10 @@ export const setOpenMenu = (menu = null) => { currentOpenMenu = menu; };
|
|||
|
||||
export const slope = (a, b) => (b.y - a.y) / (b.x - a.x);
|
||||
|
||||
let headerHeight = 50;
|
||||
|
||||
export const getHeaderHeight = () => headerHeight;
|
||||
|
||||
export const canShowActiveSubItems = (el) => {
|
||||
const isHiddenByMedia = bp.getBreakpointSize() === 'sm' || bp.getBreakpointSize() === 'md';
|
||||
|
||||
|
@ -74,7 +78,7 @@ export const moveSubItemsToPosition = (el, subItems) => {
|
|||
const isAbove = top < boundingRect.top;
|
||||
|
||||
subItems.classList.add('fly-out-list');
|
||||
subItems.style.transform = `translate3d(0, ${Math.floor(top)}px, 0)`; // eslint-disable-line no-param-reassign
|
||||
subItems.style.transform = `translate3d(0, ${Math.floor(top) - headerHeight}px, 0)`; // eslint-disable-line no-param-reassign
|
||||
|
||||
const subItemsRect = subItems.getBoundingClientRect();
|
||||
|
||||
|
@ -153,6 +157,8 @@ export default () => {
|
|||
}, getHideSubItemsInterval());
|
||||
});
|
||||
|
||||
headerHeight = document.querySelector('.nav-sidebar').offsetTop;
|
||||
|
||||
items.forEach((el) => {
|
||||
const subItems = el.querySelector('.sidebar-sub-level-items');
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
export const isSticky = (el, scrollY, stickyTop) => {
|
||||
const top = el.offsetTop - scrollY;
|
||||
|
||||
if (top === stickyTop) {
|
||||
if (top <= stickyTop) {
|
||||
el.classList.add('is-stuck');
|
||||
} else {
|
||||
el.classList.remove('is-stuck');
|
||||
|
|
|
@ -132,8 +132,9 @@ import './project_select';
|
|||
import './project_show';
|
||||
import './project_variables';
|
||||
import './projects_list';
|
||||
import './render_gfm';
|
||||
import './syntax_highlight';
|
||||
import './render_math';
|
||||
import './render_gfm';
|
||||
import './right_sidebar';
|
||||
import './search';
|
||||
import './search_autocomplete';
|
||||
|
@ -141,7 +142,6 @@ import './smart_interval';
|
|||
import './star';
|
||||
import './subscription';
|
||||
import './subscription_select';
|
||||
import './syntax_highlight';
|
||||
|
||||
import './dispatcher';
|
||||
|
||||
|
|
|
@ -43,10 +43,12 @@ export default class NewNavSidebar {
|
|||
}
|
||||
|
||||
toggleCollapsedSidebar(collapsed) {
|
||||
this.$sidebar.toggleClass('sidebar-icons-only', collapsed);
|
||||
const breakpoint = bp.getBreakpointSize();
|
||||
|
||||
if (this.$sidebar.length) {
|
||||
this.$sidebar.toggleClass('sidebar-icons-only', collapsed);
|
||||
this.$page.toggleClass('page-with-new-sidebar', !collapsed);
|
||||
this.$page.toggleClass('page-with-icon-sidebar', collapsed);
|
||||
this.$page.toggleClass('page-with-icon-sidebar', breakpoint === 'sm' ? true : collapsed);
|
||||
}
|
||||
NewNavSidebar.setCollapsedCookie(collapsed);
|
||||
}
|
||||
|
|
|
@ -11,7 +11,5 @@
|
|||
return this;
|
||||
};
|
||||
|
||||
$(document).on('ready load', function() {
|
||||
return $('body').renderGFM();
|
||||
});
|
||||
$(() => $('body').renderGFM());
|
||||
}).call(window);
|
||||
|
|
|
@ -43,15 +43,18 @@ export default {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<div class="repository-view tree-content-holder">
|
||||
<repo-sidebar/><div v-if="isMini"
|
||||
class="panel-right"
|
||||
:class="{'edit-mode': editMode}">
|
||||
<repo-tabs/>
|
||||
<component
|
||||
:is="currentBlobView"
|
||||
class="blob-viewer-container"/>
|
||||
<repo-file-buttons/>
|
||||
<div class="repository-view">
|
||||
<div class="tree-content-holder" :class="{'tree-content-holder-mini' : isMini}">
|
||||
<repo-sidebar/>
|
||||
<div v-if="isMini"
|
||||
class="panel-right"
|
||||
:class="{'edit-mode': editMode}">
|
||||
<repo-tabs/>
|
||||
<component
|
||||
:is="currentBlobView"
|
||||
class="blob-viewer-container"/>
|
||||
<repo-file-buttons/>
|
||||
</div>
|
||||
</div>
|
||||
<repo-commit-section/>
|
||||
<popup-dialog
|
||||
|
|
|
@ -71,7 +71,7 @@ export default {
|
|||
/>
|
||||
<div v-if="!isConfidential" class="no-value confidential-value">
|
||||
<i class="fa fa-eye is-not-confidential"></i>
|
||||
This issue is not confidential
|
||||
Not confidential
|
||||
</div>
|
||||
<div v-else class="value confidential-value hide-collapsed">
|
||||
<i aria-hidden="true" data-hidden="true" class="fa fa-eye-slash is-confidential"></i>
|
||||
|
|
|
@ -204,6 +204,16 @@
|
|||
}
|
||||
}
|
||||
|
||||
div.avatar {
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
.center {
|
||||
line-height: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
strong {
|
||||
color: $gl-text-color;
|
||||
}
|
||||
|
|
|
@ -161,6 +161,8 @@
|
|||
}
|
||||
|
||||
.nav-controls {
|
||||
@include new-style-dropdown;
|
||||
|
||||
display: inline-block;
|
||||
float: right;
|
||||
text-align: right;
|
||||
|
|
|
@ -97,9 +97,9 @@ $new-sidebar-collapsed-width: 50px;
|
|||
top: $header-height;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
overflow: auto;
|
||||
background-color: $gray-normal;
|
||||
box-shadow: inset -2px 0 0 $border-color;
|
||||
transform: translate3d(0, 0, 0);
|
||||
|
||||
&.sidebar-icons-only {
|
||||
width: $new-sidebar-collapsed-width;
|
||||
|
@ -176,6 +176,12 @@ $new-sidebar-collapsed-width: 50px;
|
|||
}
|
||||
}
|
||||
|
||||
.nav-sidebar-inner-scroll {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.with-performance-bar .nav-sidebar {
|
||||
top: $header-height + $performance-bar-height;
|
||||
}
|
||||
|
|
|
@ -81,6 +81,7 @@
|
|||
border: 1px solid $white-normal;
|
||||
padding: 5px;
|
||||
max-height: calc(100vh - 100px);
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.emoji-block {
|
||||
|
|
|
@ -47,14 +47,26 @@
|
|||
margin: 20px;
|
||||
}
|
||||
|
||||
.repository-view.tree-content-holder {
|
||||
.repository-view {
|
||||
border: 1px solid $border-color;
|
||||
border-radius: $border-radius-default;
|
||||
color: $almost-black;
|
||||
|
||||
.tree-content-holder {
|
||||
display: flex;
|
||||
max-height: 100vh;
|
||||
min-height: 300px;
|
||||
}
|
||||
|
||||
.tree-content-holder-mini {
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.panel-right {
|
||||
display: inline-block;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 80%;
|
||||
height: 100%;
|
||||
|
||||
.monaco-editor.vs {
|
||||
.line-numbers {
|
||||
|
@ -85,16 +97,17 @@
|
|||
}
|
||||
|
||||
.blob-viewer-container {
|
||||
height: calc(100vh - 62px);
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
#tabs {
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
padding-left: 0;
|
||||
margin-bottom: 0;
|
||||
display: flex;
|
||||
white-space: nowrap;
|
||||
width: 100%;
|
||||
overflow-y: hidden;
|
||||
overflow-x: auto;
|
||||
|
||||
|
@ -222,14 +235,12 @@
|
|||
}
|
||||
|
||||
#sidebar {
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
|
||||
&.sidebar-mini {
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
width: 20%;
|
||||
border-right: 1px solid $white-normal;
|
||||
min-height: 475px;
|
||||
height: calc(100vh + 20px);
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ module IssuableActions
|
|||
def destroy
|
||||
issuable.destroy
|
||||
destroy_method = "destroy_#{issuable.class.name.underscore}".to_sym
|
||||
TodoService.new.public_send(destroy_method, issuable, current_user)
|
||||
TodoService.new.public_send(destroy_method, issuable, current_user) # rubocop:disable GitlabSecurity/PublicSend
|
||||
|
||||
name = issuable.human_class_name
|
||||
flash[:notice] = "The #{name} was successfully deleted."
|
||||
|
|
|
@ -34,7 +34,7 @@ class Groups::ApplicationController < ApplicationController
|
|||
|
||||
def build_canonical_path(group)
|
||||
params[:group_id] = group.to_param
|
||||
|
||||
|
||||
url_for(params)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -64,7 +64,7 @@ class Import::GithubController < Import::BaseController
|
|||
end
|
||||
|
||||
def import_enabled?
|
||||
__send__("#{provider}_import_enabled?")
|
||||
__send__("#{provider}_import_enabled?") # rubocop:disable GitlabSecurity/PublicSend
|
||||
end
|
||||
|
||||
def new_import_url
|
||||
|
|
|
@ -142,13 +142,13 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
|
|||
def oauth
|
||||
@oauth ||= request.env['omniauth.auth']
|
||||
end
|
||||
|
||||
|
||||
def fail_login
|
||||
error_message = @user.errors.full_messages.to_sentence
|
||||
|
||||
return redirect_to omniauth_error_path(oauth['provider'], error: error_message)
|
||||
end
|
||||
|
||||
|
||||
def fail_ldap_login
|
||||
flash[:alert] = 'Access denied for your LDAP account.'
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ module Projects
|
|||
module CycleAnalytics
|
||||
class EventsController < Projects::ApplicationController
|
||||
include CycleAnalyticsParams
|
||||
|
||||
|
||||
before_action :authorize_read_cycle_analytics!
|
||||
before_action :authorize_read_build!, only: [:test, :staging]
|
||||
before_action :authorize_read_issue!, only: [:issue, :production]
|
||||
|
@ -11,33 +11,33 @@ module Projects
|
|||
def issue
|
||||
render_events(cycle_analytics[:issue].events)
|
||||
end
|
||||
|
||||
|
||||
def plan
|
||||
render_events(cycle_analytics[:plan].events)
|
||||
end
|
||||
|
||||
|
||||
def code
|
||||
render_events(cycle_analytics[:code].events)
|
||||
end
|
||||
|
||||
|
||||
def test
|
||||
options(events_params)[:branch] = events_params[:branch_name]
|
||||
|
||||
|
||||
render_events(cycle_analytics[:test].events)
|
||||
end
|
||||
|
||||
|
||||
def review
|
||||
render_events(cycle_analytics[:review].events)
|
||||
end
|
||||
|
||||
|
||||
def staging
|
||||
render_events(cycle_analytics[:staging].events)
|
||||
end
|
||||
|
||||
|
||||
def production
|
||||
render_events(cycle_analytics[:production].events)
|
||||
end
|
||||
|
||||
|
||||
private
|
||||
|
||||
def render_events(events)
|
||||
|
@ -46,14 +46,14 @@ module Projects
|
|||
format.json { render json: { events: events } }
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def cycle_analytics
|
||||
@cycle_analytics ||= ::CycleAnalytics.new(project, options(events_params))
|
||||
end
|
||||
|
||||
|
||||
def events_params
|
||||
return {} unless params[:events].present?
|
||||
|
||||
|
||||
params[:events].permit(:start_date, :branch_name)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -218,8 +218,8 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
|
|||
if can?(current_user, :read_environment, environment) && environment.has_metrics?
|
||||
metrics_project_environment_deployment_path(environment.project, environment, deployment)
|
||||
end
|
||||
|
||||
metrics_monitoring_url =
|
||||
|
||||
metrics_monitoring_url =
|
||||
if can?(current_user, :read_environment, environment)
|
||||
environment_metrics_path(environment)
|
||||
end
|
||||
|
|
|
@ -89,7 +89,7 @@ class UploadsController < ApplicationController
|
|||
|
||||
@uploader.retrieve_from_store!(params[:filename])
|
||||
else
|
||||
@uploader = @model.send(upload_mount)
|
||||
@uploader = @model.public_send(upload_mount) # rubocop:disable GitlabSecurity/PublicSend
|
||||
|
||||
redirect_to @uploader.url unless @uploader.file_storage?
|
||||
end
|
||||
|
|
|
@ -128,10 +128,10 @@ module CommitsHelper
|
|||
# avatar: true will prepend the avatar image
|
||||
# size: size of the avatar image in px
|
||||
def commit_person_link(commit, options = {})
|
||||
user = commit.send(options[:source])
|
||||
user = commit.public_send(options[:source]) # rubocop:disable GitlabSecurity/PublicSend
|
||||
|
||||
source_name = clean(commit.send "#{options[:source]}_name".to_sym)
|
||||
source_email = clean(commit.send "#{options[:source]}_email".to_sym)
|
||||
source_name = clean(commit.public_send(:"#{options[:source]}_name")) # rubocop:disable GitlabSecurity/PublicSend
|
||||
source_email = clean(commit.public_send(:"#{options[:source]}_email")) # rubocop:disable GitlabSecurity/PublicSend
|
||||
|
||||
person_name = user.try(:name) || source_name
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ module ImportHelper
|
|||
end
|
||||
|
||||
def provider_project_link(provider, path_with_namespace)
|
||||
url = __send__("#{provider}_project_url", path_with_namespace)
|
||||
url = __send__("#{provider}_project_url", path_with_namespace) # rubocop:disable GitlabSecurity/PublicSend
|
||||
|
||||
link_to path_with_namespace, url, target: '_blank', rel: 'noopener noreferrer'
|
||||
end
|
||||
|
|
|
@ -174,7 +174,14 @@ module IssuablesHelper
|
|||
end
|
||||
|
||||
def assigned_issuables_count(issuable_type)
|
||||
current_user.public_send("assigned_open_#{issuable_type}_count")
|
||||
case issuable_type
|
||||
when :issues
|
||||
current_user.assigned_open_issues_count
|
||||
when :merge_requests
|
||||
current_user.assigned_open_merge_requests_count
|
||||
else
|
||||
raise ArgumentError, "invalid issuable `#{issuable_type}`"
|
||||
end
|
||||
end
|
||||
|
||||
def issuable_filter_params
|
||||
|
@ -298,10 +305,6 @@ module IssuablesHelper
|
|||
cookies[:collapsed_gutter] == 'true'
|
||||
end
|
||||
|
||||
def base_issuable_scope(issuable)
|
||||
issuable.project.send(issuable.class.table_name).send(issuable_state_scope(issuable))
|
||||
end
|
||||
|
||||
def issuable_state_scope(issuable)
|
||||
if issuable.respond_to?(:merged?) && issuable.merged?
|
||||
:merged
|
||||
|
|
|
@ -32,7 +32,18 @@ module MilestonesHelper
|
|||
end
|
||||
|
||||
def milestone_issues_by_label_count(milestone, label, state:)
|
||||
milestone.issues.with_label(label.title).send(state).size
|
||||
issues = milestone.issues.with_label(label.title)
|
||||
issues =
|
||||
case state
|
||||
when :opened
|
||||
issues.opened
|
||||
when :closed
|
||||
issues.closed
|
||||
else
|
||||
raise ArgumentError, "invalid milestone state `#{state}`"
|
||||
end
|
||||
|
||||
issues.size
|
||||
end
|
||||
|
||||
# Returns count of milestones for different states
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
module PipelineSchedulesHelper
|
||||
def timezone_data
|
||||
ActiveSupport::TimeZone.all.map do |timezone|
|
||||
{
|
||||
name: timezone.name,
|
||||
offset: timezone.utc_offset,
|
||||
identifier: timezone.tzinfo.identifier
|
||||
ActiveSupport::TimeZone.all.map do |timezone|
|
||||
{
|
||||
name: timezone.name,
|
||||
offset: timezone.utc_offset,
|
||||
identifier: timezone.tzinfo.identifier
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -149,15 +149,16 @@ module ProjectsHelper
|
|||
# Don't show option "everyone with access" if project is private
|
||||
options = project_feature_options
|
||||
|
||||
level = @project.project_feature.public_send(field) # rubocop:disable GitlabSecurity/PublicSend
|
||||
|
||||
if @project.private?
|
||||
level = @project.project_feature.send(field)
|
||||
disabled_option = ProjectFeature::ENABLED
|
||||
highest_available_option = ProjectFeature::PRIVATE if level == disabled_option
|
||||
end
|
||||
|
||||
options = options_for_select(
|
||||
options.invert,
|
||||
selected: highest_available_option || @project.project_feature.public_send(field),
|
||||
selected: highest_available_option || level,
|
||||
disabled: disabled_option
|
||||
)
|
||||
|
||||
|
@ -488,7 +489,7 @@ module ProjectsHelper
|
|||
end
|
||||
|
||||
def filename_path(project, filename)
|
||||
if project && blob = project.repository.send(filename)
|
||||
if project && blob = project.repository.public_send(filename) # rubocop:disable GitlabSecurity/PublicSend
|
||||
project_blob_path(
|
||||
project,
|
||||
tree_join(project.default_branch, blob.name)
|
||||
|
|
|
@ -2,7 +2,7 @@ module VersionCheckHelper
|
|||
def version_status_badge
|
||||
if Rails.env.production? && current_application_settings.version_check_enabled
|
||||
image_url = VersionCheck.new.url
|
||||
image_tag image_url, class: 'js-version-status-badge', lazy: false
|
||||
image_tag image_url, class: 'js-version-status-badge'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,7 +2,7 @@ module BlobViewer
|
|||
class Notebook < Base
|
||||
include Rich
|
||||
include ClientSide
|
||||
|
||||
|
||||
self.partial_name = 'notebook'
|
||||
self.extensions = %w(ipynb)
|
||||
self.binary = false
|
||||
|
|
|
@ -194,10 +194,7 @@ module Ci
|
|||
# * Maximum length is 63 bytes
|
||||
# * First/Last Character is not a hyphen
|
||||
def ref_slug
|
||||
ref.to_s
|
||||
.downcase
|
||||
.gsub(/[^a-z0-9]/, '-')[0..62]
|
||||
.gsub(/(\A-+|-+\z)/, '')
|
||||
Gitlab::Utils.slugify(ref.to_s)
|
||||
end
|
||||
|
||||
# Variables whose value does not depend on environment
|
||||
|
|
|
@ -200,7 +200,7 @@ class Commit
|
|||
end
|
||||
|
||||
def method_missing(m, *args, &block)
|
||||
@raw.send(m, *args, &block)
|
||||
@raw.__send__(m, *args, &block) # rubocop:disable GitlabSecurity/PublicSend
|
||||
end
|
||||
|
||||
def respond_to_missing?(method, include_private = false)
|
||||
|
@ -383,6 +383,6 @@ class Commit
|
|||
end
|
||||
|
||||
def gpg_commit
|
||||
@gpg_commit ||= Gitlab::Gpg::Commit.new(self)
|
||||
@gpg_commit ||= Gitlab::Gpg::Commit.for_commit(self)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -78,7 +78,7 @@ module CacheMarkdownField
|
|||
def cached_html_up_to_date?(markdown_field)
|
||||
html_field = cached_markdown_fields.html_field(markdown_field)
|
||||
|
||||
cached = cached_html_for(markdown_field).present? && __send__(markdown_field).present?
|
||||
cached = cached_html_for(markdown_field).present? && __send__(markdown_field).present? # rubocop:disable GitlabSecurity/PublicSend
|
||||
return false unless cached
|
||||
|
||||
markdown_changed = attribute_changed?(markdown_field) || false
|
||||
|
@ -93,14 +93,14 @@ module CacheMarkdownField
|
|||
end
|
||||
|
||||
def attribute_invalidated?(attr)
|
||||
__send__("#{attr}_invalidated?")
|
||||
__send__("#{attr}_invalidated?") # rubocop:disable GitlabSecurity/PublicSend
|
||||
end
|
||||
|
||||
def cached_html_for(markdown_field)
|
||||
raise ArgumentError.new("Unknown field: #{field}") unless
|
||||
cached_markdown_fields.markdown_fields.include?(markdown_field)
|
||||
|
||||
__send__(cached_markdown_fields.html_field(markdown_field))
|
||||
__send__(cached_markdown_fields.html_field(markdown_field)) # rubocop:disable GitlabSecurity/PublicSend
|
||||
end
|
||||
|
||||
included do
|
||||
|
|
|
@ -9,7 +9,7 @@ module InternalId
|
|||
def set_iid
|
||||
if iid.blank?
|
||||
parent = project || group
|
||||
records = parent.send(self.class.name.tableize)
|
||||
records = parent.public_send(self.class.name.tableize) # rubocop:disable GitlabSecurity/PublicSend
|
||||
records = records.with_deleted if self.paranoid?
|
||||
max_iid = records.maximum(:iid)
|
||||
|
||||
|
|
|
@ -56,7 +56,7 @@ module Mentionable
|
|||
end
|
||||
|
||||
self.class.mentionable_attrs.each do |attr, options|
|
||||
text = __send__(attr)
|
||||
text = __send__(attr) # rubocop:disable GitlabSecurity/PublicSend
|
||||
options = options.merge(
|
||||
cache_key: [self, attr],
|
||||
author: author,
|
||||
|
@ -100,7 +100,7 @@ module Mentionable
|
|||
end
|
||||
|
||||
self.class.mentionable_attrs.any? do |attr, _|
|
||||
__send__(attr) =~ reference_pattern
|
||||
__send__(attr) =~ reference_pattern # rubocop:disable GitlabSecurity/PublicSend
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -82,7 +82,7 @@ module Participable
|
|||
if attr.respond_to?(:call)
|
||||
source.instance_exec(current_user, ext, &attr)
|
||||
else
|
||||
process << source.__send__(attr)
|
||||
process << source.__send__(attr) # rubocop:disable GitlabSecurity/PublicSend
|
||||
end
|
||||
end
|
||||
when Enumerable, ActiveRecord::Relation
|
||||
|
|
|
@ -32,6 +32,6 @@ module ProjectFeaturesCompatibility
|
|||
build_project_feature unless project_feature
|
||||
|
||||
access_level = Gitlab::Utils.to_boolean(value) ? ProjectFeature::ENABLED : ProjectFeature::DISABLED
|
||||
project_feature.send(:write_attribute, field, access_level)
|
||||
project_feature.__send__(:write_attribute, field, access_level) # rubocop:disable GitlabSecurity/PublicSend
|
||||
end
|
||||
end
|
||||
|
|
|
@ -12,7 +12,7 @@ class DeployKeysProject < ActiveRecord::Base
|
|||
|
||||
def destroy_orphaned_deploy_key
|
||||
return unless self.deploy_key.destroyed_when_orphaned? && self.deploy_key.orphaned?
|
||||
|
||||
|
||||
self.deploy_key.destroy
|
||||
end
|
||||
end
|
||||
|
|
|
@ -18,4 +18,8 @@ class GpgSignature < ActiveRecord::Base
|
|||
def commit
|
||||
project.commit(commit_sha)
|
||||
end
|
||||
|
||||
def gpg_commit
|
||||
Gitlab::Gpg::Commit.new(project, commit_sha)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -12,7 +12,7 @@ module Network
|
|||
end
|
||||
|
||||
def method_missing(m, *args, &block)
|
||||
@commit.send(m, *args, &block)
|
||||
@commit.__send__(m, *args, &block) # rubocop:disable GitlabSecurity/PublicSend
|
||||
end
|
||||
|
||||
def space
|
||||
|
|
|
@ -920,14 +920,14 @@ class Project < ActiveRecord::Base
|
|||
end
|
||||
|
||||
def execute_hooks(data, hooks_scope = :push_hooks)
|
||||
hooks.send(hooks_scope).each do |hook|
|
||||
hooks.public_send(hooks_scope).each do |hook| # rubocop:disable GitlabSecurity/PublicSend
|
||||
hook.async_execute(data, hooks_scope.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
def execute_services(data, hooks_scope = :push_hooks)
|
||||
# Call only service hooks that are active for this scope
|
||||
services.send(hooks_scope).each do |service|
|
||||
services.public_send(hooks_scope).each do |service| # rubocop:disable GitlabSecurity/PublicSend
|
||||
service.async_execute(data)
|
||||
end
|
||||
end
|
||||
|
@ -1282,12 +1282,16 @@ class Project < ActiveRecord::Base
|
|||
status.zero?
|
||||
end
|
||||
|
||||
def full_path_slug
|
||||
Gitlab::Utils.slugify(full_path.to_s)
|
||||
end
|
||||
|
||||
def predefined_variables
|
||||
[
|
||||
{ key: 'CI_PROJECT_ID', value: id.to_s, public: true },
|
||||
{ key: 'CI_PROJECT_NAME', value: path, public: true },
|
||||
{ key: 'CI_PROJECT_PATH', value: full_path, public: true },
|
||||
{ key: 'CI_PROJECT_PATH_SLUG', value: full_path.parameterize, public: true },
|
||||
{ key: 'CI_PROJECT_PATH_SLUG', value: full_path_slug, public: true },
|
||||
{ key: 'CI_PROJECT_NAMESPACE', value: namespace.full_path, public: true },
|
||||
{ key: 'CI_PROJECT_URL', value: web_url, public: true }
|
||||
]
|
||||
|
|
|
@ -115,7 +115,7 @@ class ChatNotificationService < Service
|
|||
|
||||
def get_channel_field(event)
|
||||
field_name = event_channel_name(event)
|
||||
self.public_send(field_name)
|
||||
self.public_send(field_name) # rubocop:disable GitlabSecurity/PublicSend
|
||||
end
|
||||
|
||||
def build_event_channels
|
||||
|
|
|
@ -53,7 +53,7 @@ class HipchatService < Service
|
|||
return unless supported_events.include?(data[:object_kind])
|
||||
message = create_message(data)
|
||||
return unless message.present?
|
||||
gate[room].send('GitLab', message, message_options(data))
|
||||
gate[room].send('GitLab', message, message_options(data)) # rubocop:disable GitlabSecurity/PublicSend
|
||||
end
|
||||
|
||||
def test(data)
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
class ProtectableDropdown
|
||||
REF_TYPES = %i[branches tags].freeze
|
||||
|
||||
def initialize(project, ref_type)
|
||||
raise ArgumentError, "invalid ref type `#{ref_type}`" unless ref_type.in?(REF_TYPES)
|
||||
|
||||
@project = project
|
||||
@ref_type = ref_type
|
||||
end
|
||||
|
@ -16,7 +20,7 @@ class ProtectableDropdown
|
|||
private
|
||||
|
||||
def refs
|
||||
@project.repository.public_send(@ref_type)
|
||||
@project.repository.public_send(@ref_type) # rubocop:disable GitlabSecurity/PublicSend
|
||||
end
|
||||
|
||||
def ref_names
|
||||
|
@ -24,7 +28,7 @@ class ProtectableDropdown
|
|||
end
|
||||
|
||||
def protections
|
||||
@project.public_send("protected_#{@ref_type}")
|
||||
@project.public_send("protected_#{@ref_type}") # rubocop:disable GitlabSecurity/PublicSend
|
||||
end
|
||||
|
||||
def non_wildcard_protected_ref_names
|
||||
|
|
|
@ -14,7 +14,7 @@ class RedirectRoute < ActiveRecord::Base
|
|||
else
|
||||
'redirect_routes.path = ? OR redirect_routes.path LIKE ?'
|
||||
end
|
||||
|
||||
|
||||
where(wheres, path, "#{sanitize_sql_like(path)}/%")
|
||||
end
|
||||
end
|
||||
|
|
|
@ -48,7 +48,9 @@ class Repository
|
|||
alias_method(original, name)
|
||||
|
||||
define_method(name) do
|
||||
cache_method_output(name, fallback: fallback, memoize_only: memoize_only) { __send__(original) }
|
||||
cache_method_output(name, fallback: fallback, memoize_only: memoize_only) do
|
||||
__send__(original) # rubocop:disable GitlabSecurity/PublicSend
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -443,9 +445,9 @@ class Repository
|
|||
def method_missing(m, *args, &block)
|
||||
if m == :lookup && !block_given?
|
||||
lookup_cache[m] ||= {}
|
||||
lookup_cache[m][args.join(":")] ||= raw_repository.send(m, *args, &block)
|
||||
lookup_cache[m][args.join(":")] ||= raw_repository.__send__(m, *args, &block) # rubocop:disable GitlabSecurity/PublicSend
|
||||
else
|
||||
raw_repository.send(m, *args, &block)
|
||||
raw_repository.__send__(m, *args, &block) # rubocop:disable GitlabSecurity/PublicSend
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -776,7 +778,7 @@ class Repository
|
|||
end
|
||||
|
||||
actions.each do |options|
|
||||
index.public_send(options.delete(:action), options)
|
||||
index.public_send(options.delete(:action), options) # rubocop:disable GitlabSecurity/PublicSend
|
||||
end
|
||||
|
||||
options = {
|
||||
|
|
|
@ -1070,7 +1070,7 @@ class User < ActiveRecord::Base
|
|||
# Added according to https://github.com/plataformatec/devise/blob/7df57d5081f9884849ca15e4fde179ef164a575f/README.md#activejob-integration
|
||||
def send_devise_notification(notification, *args)
|
||||
return true unless can?(:receive_notifications)
|
||||
devise_mailer.send(notification, self, *args).deliver_later
|
||||
devise_mailer.__send__(notification, self, *args).deliver_later # rubocop:disable GitlabSecurity/PublicSend
|
||||
end
|
||||
|
||||
# This works around a bug in Devise 4.2.0 that erroneously causes a user to
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
class ProjectEntity < Grape::Entity
|
||||
include RequestAwareEntity
|
||||
|
||||
|
||||
expose :id
|
||||
expose :name
|
||||
|
||||
|
|
|
@ -58,7 +58,7 @@ class AkismetService
|
|||
}
|
||||
|
||||
begin
|
||||
akismet_client.public_send(type, options[:ip_address], options[:user_agent], params)
|
||||
akismet_client.public_send(type, options[:ip_address], options[:user_agent], params) # rubocop:disable GitlabSecurity/PublicSend
|
||||
true
|
||||
rescue => e
|
||||
Rails.logger.error("Unable to connect to Akismet: #{e}, skipping!")
|
||||
|
|
|
@ -23,7 +23,7 @@ module Ci
|
|||
end
|
||||
|
||||
attributes = CLONE_ACCESSORS.map do |attribute|
|
||||
[attribute, build.send(attribute)]
|
||||
[attribute, build.public_send(attribute)] # rubocop:disable GitlabSecurity/PublicSend
|
||||
end
|
||||
|
||||
attributes.push([:user, current_user])
|
||||
|
|
|
@ -11,6 +11,7 @@ module Commits
|
|||
def commit_change(action)
|
||||
raise NotImplementedError unless repository.respond_to?(action)
|
||||
|
||||
# rubocop:disable GitlabSecurity/PublicSend
|
||||
repository.public_send(
|
||||
action,
|
||||
current_user,
|
||||
|
|
|
@ -90,8 +90,19 @@ class GitPushService < BaseService
|
|||
end
|
||||
|
||||
def update_signatures
|
||||
@push_commits.each do |commit|
|
||||
CreateGpgSignatureWorker.perform_async(commit.sha, @project.id)
|
||||
commit_shas = @push_commits.last(PROCESS_COMMIT_LIMIT).map(&:sha)
|
||||
|
||||
return if commit_shas.empty?
|
||||
|
||||
shas_with_cached_signatures = GpgSignature.where(commit_sha: commit_shas).pluck(:commit_sha)
|
||||
commit_shas -= shas_with_cached_signatures
|
||||
|
||||
return if commit_shas.empty?
|
||||
|
||||
commit_shas = Gitlab::Git::Commit.shas_with_signatures(project.repository, commit_shas)
|
||||
|
||||
commit_shas.each do |sha|
|
||||
CreateGpgSignatureWorker.perform_async(sha, project.id)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -338,7 +338,7 @@ class IssuableBaseService < BaseService
|
|||
|
||||
def invalidate_cache_counts(issuable, users: [], skip_project_cache: false)
|
||||
users.each do |user|
|
||||
user.public_send("invalidate_#{issuable.model_name.singular}_cache_counts")
|
||||
user.public_send("invalidate_#{issuable.model_name.singular}_cache_counts") # rubocop:disable GitlabSecurity/PublicSend
|
||||
end
|
||||
|
||||
unless skip_project_cache
|
||||
|
|
|
@ -31,7 +31,7 @@ module Members
|
|||
source.members.find_by(condition) ||
|
||||
source.requesters.find_by!(condition)
|
||||
else
|
||||
source.public_send(scope).find_by!(condition)
|
||||
source.public_send(scope).find_by!(condition) # rubocop:disable GitlabSecurity/PublicSend
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -25,7 +25,6 @@ module MergeRequests
|
|||
end
|
||||
|
||||
def after_create(issuable)
|
||||
event_service.open_mr(issuable, current_user)
|
||||
todo_service.new_merge_request(issuable, current_user)
|
||||
issuable.cache_merge_request_closes_issues!(current_user)
|
||||
update_merge_requests_head_pipeline(issuable)
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# rubocop:disable GitlabSecurity/PublicSend
|
||||
|
||||
# NotificationService class
|
||||
#
|
||||
# Used for notifying users with emails about different events
|
||||
|
|
|
@ -4,7 +4,7 @@ class SystemHooksService
|
|||
end
|
||||
|
||||
def execute_hooks(data, hooks_scope = :all)
|
||||
SystemHook.public_send(hooks_scope).find_each do |hook|
|
||||
SystemHook.public_send(hooks_scope).find_each do |hook| # rubocop:disable GitlabSecurity/PublicSend
|
||||
hook.async_execute(data, 'system_hooks')
|
||||
end
|
||||
end
|
||||
|
|
|
@ -18,7 +18,7 @@ module TestHooks
|
|||
end
|
||||
|
||||
error_message = catch(:validation_error) do
|
||||
sample_data = self.__send__(trigger_data_method)
|
||||
sample_data = self.__send__(trigger_data_method) # rubocop:disable GitlabSecurity/PublicSend
|
||||
|
||||
return hook.execute(sample_data, trigger)
|
||||
end
|
||||
|
|
|
@ -189,7 +189,7 @@
|
|||
= icon('chevron-down')
|
||||
%ul.dropdown-menu
|
||||
%li
|
||||
%a Sort by date
|
||||
= link_to 'Sort by date', '#'
|
||||
|
||||
= link_to 'New issue', '#', class: 'btn btn-new btn-inverted'
|
||||
|
||||
|
|
|
@ -1,150 +1,151 @@
|
|||
.nav-sidebar{ class: ("sidebar-icons-only" if collapsed_sidebar?) }
|
||||
.context-header
|
||||
= link_to admin_root_path, title: 'Admin Overview' do
|
||||
.avatar-container.s40.settings-avatar
|
||||
= icon('wrench')
|
||||
.project-title Admin Area
|
||||
%ul.sidebar-top-level-items
|
||||
= nav_link(controller: %w(dashboard admin projects users groups jobs runners cohorts), html_options: {class: 'home'}) do
|
||||
= link_to admin_root_path, title: 'Overview', class: 'shortcuts-tree' do
|
||||
.nav-icon-container
|
||||
= custom_icon('overview')
|
||||
%span.nav-item-name
|
||||
Overview
|
||||
|
||||
%ul.sidebar-sub-level-items
|
||||
= nav_link(controller: :dashboard, html_options: {class: 'home'}) do
|
||||
= link_to admin_root_path, title: 'Overview' do
|
||||
%span
|
||||
Dashboard
|
||||
= nav_link(controller: [:admin, :projects]) do
|
||||
= link_to admin_projects_path, title: 'Projects' do
|
||||
%span
|
||||
Projects
|
||||
= nav_link(controller: :users) do
|
||||
= link_to admin_users_path, title: 'Users' do
|
||||
%span
|
||||
Users
|
||||
= nav_link(controller: :groups) do
|
||||
= link_to admin_groups_path, title: 'Groups' do
|
||||
%span
|
||||
Groups
|
||||
= nav_link path: 'jobs#index' do
|
||||
= link_to admin_jobs_path, title: 'Jobs' do
|
||||
%span
|
||||
Jobs
|
||||
= nav_link path: ['runners#index', 'runners#show'] do
|
||||
= link_to admin_runners_path, title: 'Runners' do
|
||||
%span
|
||||
Runners
|
||||
= nav_link path: 'cohorts#index' do
|
||||
= link_to admin_cohorts_path, title: 'Cohorts' do
|
||||
%span
|
||||
Cohorts
|
||||
|
||||
= nav_link(controller: %w(conversational_development_index system_info background_jobs logs health_check requests_profiles)) do
|
||||
= link_to admin_conversational_development_index_path, title: 'Monitoring' do
|
||||
.nav-icon-container
|
||||
= custom_icon('monitoring')
|
||||
%span.nav-item-name
|
||||
Monitoring
|
||||
|
||||
%ul.sidebar-sub-level-items
|
||||
= nav_link(controller: :conversational_development_index) do
|
||||
= link_to admin_conversational_development_index_path, title: 'ConvDev Index' do
|
||||
%span
|
||||
ConvDev Index
|
||||
= nav_link(controller: :system_info) do
|
||||
= link_to admin_system_info_path, title: 'System Info' do
|
||||
%span
|
||||
System Info
|
||||
= nav_link(controller: :background_jobs) do
|
||||
= link_to admin_background_jobs_path, title: 'Background Jobs' do
|
||||
%span
|
||||
Background Jobs
|
||||
= nav_link(controller: :logs) do
|
||||
= link_to admin_logs_path, title: 'Logs' do
|
||||
%span
|
||||
Logs
|
||||
= nav_link(controller: :health_check) do
|
||||
= link_to admin_health_check_path, title: 'Health Check' do
|
||||
%span
|
||||
Health Check
|
||||
= nav_link(controller: :requests_profiles) do
|
||||
= link_to admin_requests_profiles_path, title: 'Requests Profiles' do
|
||||
%span
|
||||
Requests Profiles
|
||||
|
||||
= nav_link(controller: :broadcast_messages) do
|
||||
= link_to admin_broadcast_messages_path, title: 'Messages' do
|
||||
.nav-icon-container
|
||||
= custom_icon('messages')
|
||||
%span.nav-item-name
|
||||
Messages
|
||||
= nav_link(controller: [:hooks, :hook_logs]) do
|
||||
= link_to admin_hooks_path, title: 'Hooks' do
|
||||
.nav-icon-container
|
||||
= custom_icon('system_hooks')
|
||||
%span.nav-item-name
|
||||
System Hooks
|
||||
|
||||
= nav_link(controller: :applications) do
|
||||
= link_to admin_applications_path, title: 'Applications' do
|
||||
.nav-icon-container
|
||||
= custom_icon('applications')
|
||||
%span.nav-item-name
|
||||
Applications
|
||||
|
||||
= nav_link(controller: :abuse_reports) do
|
||||
= link_to admin_abuse_reports_path, title: "Abuse Reports" do
|
||||
.nav-icon-container
|
||||
= custom_icon('abuse_reports')
|
||||
%span.nav-item-name
|
||||
Abuse Reports
|
||||
%span.badge.count= number_with_delimiter(AbuseReport.count(:all))
|
||||
|
||||
- if akismet_enabled?
|
||||
= nav_link(controller: :spam_logs) do
|
||||
= link_to admin_spam_logs_path, title: "Spam Logs" do
|
||||
.nav-sidebar-inner-scroll
|
||||
.context-header
|
||||
= link_to admin_root_path, title: 'Admin Overview' do
|
||||
.avatar-container.s40.settings-avatar
|
||||
= icon('wrench')
|
||||
.project-title Admin Area
|
||||
%ul.sidebar-top-level-items
|
||||
= nav_link(controller: %w(dashboard admin projects users groups jobs runners cohorts), html_options: {class: 'home'}) do
|
||||
= link_to admin_root_path, title: 'Overview', class: 'shortcuts-tree' do
|
||||
.nav-icon-container
|
||||
= custom_icon('spam_logs')
|
||||
= custom_icon('overview')
|
||||
%span.nav-item-name
|
||||
Spam Logs
|
||||
Overview
|
||||
|
||||
= nav_link(controller: :deploy_keys) do
|
||||
= link_to admin_deploy_keys_path, title: 'Deploy Keys' do
|
||||
.nav-icon-container
|
||||
= custom_icon('key')
|
||||
%span.nav-item-name
|
||||
Deploy Keys
|
||||
%ul.sidebar-sub-level-items
|
||||
= nav_link(controller: :dashboard, html_options: {class: 'home'}) do
|
||||
= link_to admin_root_path, title: 'Overview' do
|
||||
%span
|
||||
Dashboard
|
||||
= nav_link(controller: [:admin, :projects]) do
|
||||
= link_to admin_projects_path, title: 'Projects' do
|
||||
%span
|
||||
Projects
|
||||
= nav_link(controller: :users) do
|
||||
= link_to admin_users_path, title: 'Users' do
|
||||
%span
|
||||
Users
|
||||
= nav_link(controller: :groups) do
|
||||
= link_to admin_groups_path, title: 'Groups' do
|
||||
%span
|
||||
Groups
|
||||
= nav_link path: 'jobs#index' do
|
||||
= link_to admin_jobs_path, title: 'Jobs' do
|
||||
%span
|
||||
Jobs
|
||||
= nav_link path: ['runners#index', 'runners#show'] do
|
||||
= link_to admin_runners_path, title: 'Runners' do
|
||||
%span
|
||||
Runners
|
||||
= nav_link path: 'cohorts#index' do
|
||||
= link_to admin_cohorts_path, title: 'Cohorts' do
|
||||
%span
|
||||
Cohorts
|
||||
|
||||
= nav_link(controller: :services) do
|
||||
= link_to admin_application_settings_services_path, title: 'Service Templates' do
|
||||
.nav-icon-container
|
||||
= custom_icon('service_templates')
|
||||
%span.nav-item-name
|
||||
Service Templates
|
||||
= nav_link(controller: %w(conversational_development_index system_info background_jobs logs health_check requests_profiles)) do
|
||||
= link_to admin_conversational_development_index_path, title: 'Monitoring' do
|
||||
.nav-icon-container
|
||||
= custom_icon('monitoring')
|
||||
%span.nav-item-name
|
||||
Monitoring
|
||||
|
||||
= nav_link(controller: :labels) do
|
||||
= link_to admin_labels_path, title: 'Labels' do
|
||||
.nav-icon-container
|
||||
= custom_icon('labels')
|
||||
%span.nav-item-name
|
||||
Labels
|
||||
%ul.sidebar-sub-level-items
|
||||
= nav_link(controller: :conversational_development_index) do
|
||||
= link_to admin_conversational_development_index_path, title: 'ConvDev Index' do
|
||||
%span
|
||||
ConvDev Index
|
||||
= nav_link(controller: :system_info) do
|
||||
= link_to admin_system_info_path, title: 'System Info' do
|
||||
%span
|
||||
System Info
|
||||
= nav_link(controller: :background_jobs) do
|
||||
= link_to admin_background_jobs_path, title: 'Background Jobs' do
|
||||
%span
|
||||
Background Jobs
|
||||
= nav_link(controller: :logs) do
|
||||
= link_to admin_logs_path, title: 'Logs' do
|
||||
%span
|
||||
Logs
|
||||
= nav_link(controller: :health_check) do
|
||||
= link_to admin_health_check_path, title: 'Health Check' do
|
||||
%span
|
||||
Health Check
|
||||
= nav_link(controller: :requests_profiles) do
|
||||
= link_to admin_requests_profiles_path, title: 'Requests Profiles' do
|
||||
%span
|
||||
Requests Profiles
|
||||
|
||||
= nav_link(controller: :appearances) do
|
||||
= link_to admin_appearances_path, title: 'Appearances' do
|
||||
.nav-icon-container
|
||||
= custom_icon('appearance')
|
||||
%span.nav-item-name
|
||||
Appearance
|
||||
= nav_link(controller: :broadcast_messages) do
|
||||
= link_to admin_broadcast_messages_path, title: 'Messages' do
|
||||
.nav-icon-container
|
||||
= custom_icon('messages')
|
||||
%span.nav-item-name
|
||||
Messages
|
||||
= nav_link(controller: [:hooks, :hook_logs]) do
|
||||
= link_to admin_hooks_path, title: 'Hooks' do
|
||||
.nav-icon-container
|
||||
= custom_icon('system_hooks')
|
||||
%span.nav-item-name
|
||||
System Hooks
|
||||
|
||||
%li.divider
|
||||
= nav_link(controller: :application_settings) do
|
||||
= link_to admin_application_settings_path, title: 'Settings' do
|
||||
.nav-icon-container
|
||||
= custom_icon('settings')
|
||||
%span.nav-item-name
|
||||
Settings
|
||||
= nav_link(controller: :applications) do
|
||||
= link_to admin_applications_path, title: 'Applications' do
|
||||
.nav-icon-container
|
||||
= custom_icon('applications')
|
||||
%span.nav-item-name
|
||||
Applications
|
||||
|
||||
= render 'shared/sidebar_toggle_button'
|
||||
= nav_link(controller: :abuse_reports) do
|
||||
= link_to admin_abuse_reports_path, title: "Abuse Reports" do
|
||||
.nav-icon-container
|
||||
= custom_icon('abuse_reports')
|
||||
%span.nav-item-name
|
||||
Abuse Reports
|
||||
%span.badge.count= number_with_delimiter(AbuseReport.count(:all))
|
||||
|
||||
- if akismet_enabled?
|
||||
= nav_link(controller: :spam_logs) do
|
||||
= link_to admin_spam_logs_path, title: "Spam Logs" do
|
||||
.nav-icon-container
|
||||
= custom_icon('spam_logs')
|
||||
%span.nav-item-name
|
||||
Spam Logs
|
||||
|
||||
= nav_link(controller: :deploy_keys) do
|
||||
= link_to admin_deploy_keys_path, title: 'Deploy Keys' do
|
||||
.nav-icon-container
|
||||
= custom_icon('key')
|
||||
%span.nav-item-name
|
||||
Deploy Keys
|
||||
|
||||
= nav_link(controller: :services) do
|
||||
= link_to admin_application_settings_services_path, title: 'Service Templates' do
|
||||
.nav-icon-container
|
||||
= custom_icon('service_templates')
|
||||
%span.nav-item-name
|
||||
Service Templates
|
||||
|
||||
= nav_link(controller: :labels) do
|
||||
= link_to admin_labels_path, title: 'Labels' do
|
||||
.nav-icon-container
|
||||
= custom_icon('labels')
|
||||
%span.nav-item-name
|
||||
Labels
|
||||
|
||||
= nav_link(controller: :appearances) do
|
||||
= link_to admin_appearances_path, title: 'Appearances' do
|
||||
.nav-icon-container
|
||||
= custom_icon('appearance')
|
||||
%span.nav-item-name
|
||||
Appearance
|
||||
|
||||
%li.divider
|
||||
= nav_link(controller: :application_settings) do
|
||||
= link_to admin_application_settings_path, title: 'Settings' do
|
||||
.nav-icon-container
|
||||
= custom_icon('settings')
|
||||
%span.nav-item-name
|
||||
Settings
|
||||
|
||||
= render 'shared/sidebar_toggle_button'
|
||||
|
|
|
@ -1,89 +1,90 @@
|
|||
.nav-sidebar{ class: ("sidebar-icons-only" if collapsed_sidebar?) }
|
||||
.context-header
|
||||
= link_to group_path(@group), title: @group.name do
|
||||
.avatar-container.s40.group-avatar
|
||||
= image_tag group_icon(@group), class: "avatar s40 avatar-tile"
|
||||
.group-title
|
||||
= @group.name
|
||||
%ul.sidebar-top-level-items
|
||||
= nav_link(path: ['groups#show', 'groups#activity', 'groups#subgroups'], html_options: { class: 'home' }) do
|
||||
= link_to group_path(@group), title: 'Group overview' do
|
||||
.nav-icon-container
|
||||
= custom_icon('project')
|
||||
%span.nav-item-name
|
||||
Overview
|
||||
|
||||
%ul.sidebar-sub-level-items
|
||||
= nav_link(path: ['groups#show', 'groups#subgroups'], html_options: { class: 'home' }) do
|
||||
= link_to group_path(@group), title: 'Group details' do
|
||||
%span
|
||||
Details
|
||||
|
||||
= nav_link(path: 'groups#activity') do
|
||||
= link_to activity_group_path(@group), title: 'Activity' do
|
||||
%span
|
||||
Activity
|
||||
|
||||
= nav_link(path: ['groups#issues', 'labels#index', 'milestones#index']) do
|
||||
= link_to issues_group_path(@group), title: 'Issues' do
|
||||
.nav-icon-container
|
||||
= custom_icon('issues')
|
||||
%span.nav-item-name
|
||||
- issues = IssuesFinder.new(current_user, group_id: @group.id, state: 'opened').execute
|
||||
Issues
|
||||
%span.badge.count= number_with_delimiter(issues.count)
|
||||
|
||||
%ul.sidebar-sub-level-items
|
||||
= nav_link(path: 'groups#issues', html_options: { class: 'home' }) do
|
||||
= link_to issues_group_path(@group), title: 'List' do
|
||||
%span
|
||||
List
|
||||
|
||||
= nav_link(path: 'labels#index') do
|
||||
= link_to group_labels_path(@group), title: 'Labels' do
|
||||
%span
|
||||
Labels
|
||||
|
||||
= nav_link(path: 'milestones#index') do
|
||||
= link_to group_milestones_path(@group), title: 'Milestones' do
|
||||
%span
|
||||
Milestones
|
||||
|
||||
= nav_link(path: 'groups#merge_requests') do
|
||||
= link_to merge_requests_group_path(@group), title: 'Merge Requests' do
|
||||
.nav-icon-container
|
||||
= custom_icon('mr_bold')
|
||||
%span.nav-item-name
|
||||
- merge_requests = MergeRequestsFinder.new(current_user, group_id: @group.id, state: 'opened', non_archived: true).execute
|
||||
Merge Requests
|
||||
%span.badge.count= number_with_delimiter(merge_requests.count)
|
||||
= nav_link(path: 'group_members#index') do
|
||||
= link_to group_group_members_path(@group), title: 'Members' do
|
||||
.nav-icon-container
|
||||
= custom_icon('members')
|
||||
%span.nav-item-name
|
||||
Members
|
||||
- if current_user && can?(current_user, :admin_group, @group)
|
||||
= nav_link(path: %w[groups#projects groups#edit ci_cd#show]) do
|
||||
= link_to edit_group_path(@group), title: 'Settings' do
|
||||
.nav-sidebar-inner-scroll
|
||||
.context-header
|
||||
= link_to group_path(@group), title: @group.name do
|
||||
.avatar-container.s40.group-avatar
|
||||
= image_tag group_icon(@group), class: "avatar s40 avatar-tile"
|
||||
.group-title
|
||||
= @group.name
|
||||
%ul.sidebar-top-level-items
|
||||
= nav_link(path: ['groups#show', 'groups#activity', 'groups#subgroups'], html_options: { class: 'home' }) do
|
||||
= link_to group_path(@group), title: 'Group overview' do
|
||||
.nav-icon-container
|
||||
= custom_icon('settings')
|
||||
= custom_icon('project')
|
||||
%span.nav-item-name
|
||||
Settings
|
||||
Overview
|
||||
|
||||
%ul.sidebar-sub-level-items
|
||||
= nav_link(path: 'groups#edit') do
|
||||
= link_to edit_group_path(@group), title: 'General' do
|
||||
= nav_link(path: ['groups#show', 'groups#subgroups'], html_options: { class: 'home' }) do
|
||||
= link_to group_path(@group), title: 'Group details' do
|
||||
%span
|
||||
General
|
||||
Details
|
||||
|
||||
= nav_link(path: 'groups#projects') do
|
||||
= link_to projects_group_path(@group), title: 'Projects' do
|
||||
= nav_link(path: 'groups#activity') do
|
||||
= link_to activity_group_path(@group), title: 'Activity' do
|
||||
%span
|
||||
Projects
|
||||
Activity
|
||||
|
||||
= nav_link(controller: :ci_cd) do
|
||||
= link_to group_settings_ci_cd_path(@group), title: 'CI / CD' do
|
||||
= nav_link(path: ['groups#issues', 'labels#index', 'milestones#index']) do
|
||||
= link_to issues_group_path(@group), title: 'Issues' do
|
||||
.nav-icon-container
|
||||
= custom_icon('issues')
|
||||
%span.nav-item-name
|
||||
- issues = IssuesFinder.new(current_user, group_id: @group.id, state: 'opened').execute
|
||||
Issues
|
||||
%span.badge.count= number_with_delimiter(issues.count)
|
||||
|
||||
%ul.sidebar-sub-level-items
|
||||
= nav_link(path: 'groups#issues', html_options: { class: 'home' }) do
|
||||
= link_to issues_group_path(@group), title: 'List' do
|
||||
%span
|
||||
CI / CD
|
||||
List
|
||||
|
||||
= render 'shared/sidebar_toggle_button'
|
||||
= nav_link(path: 'labels#index') do
|
||||
= link_to group_labels_path(@group), title: 'Labels' do
|
||||
%span
|
||||
Labels
|
||||
|
||||
= nav_link(path: 'milestones#index') do
|
||||
= link_to group_milestones_path(@group), title: 'Milestones' do
|
||||
%span
|
||||
Milestones
|
||||
|
||||
= nav_link(path: 'groups#merge_requests') do
|
||||
= link_to merge_requests_group_path(@group), title: 'Merge Requests' do
|
||||
.nav-icon-container
|
||||
= custom_icon('mr_bold')
|
||||
%span.nav-item-name
|
||||
- merge_requests = MergeRequestsFinder.new(current_user, group_id: @group.id, state: 'opened', non_archived: true).execute
|
||||
Merge Requests
|
||||
%span.badge.count= number_with_delimiter(merge_requests.count)
|
||||
= nav_link(path: 'group_members#index') do
|
||||
= link_to group_group_members_path(@group), title: 'Members' do
|
||||
.nav-icon-container
|
||||
= custom_icon('members')
|
||||
%span.nav-item-name
|
||||
Members
|
||||
- if current_user && can?(current_user, :admin_group, @group)
|
||||
= nav_link(path: %w[groups#projects groups#edit ci_cd#show]) do
|
||||
= link_to edit_group_path(@group), title: 'Settings' do
|
||||
.nav-icon-container
|
||||
= custom_icon('settings')
|
||||
%span.nav-item-name
|
||||
Settings
|
||||
%ul.sidebar-sub-level-items
|
||||
= nav_link(path: 'groups#edit') do
|
||||
= link_to edit_group_path(@group), title: 'General' do
|
||||
%span
|
||||
General
|
||||
|
||||
= nav_link(path: 'groups#projects') do
|
||||
= link_to projects_group_path(@group), title: 'Projects' do
|
||||
%span
|
||||
Projects
|
||||
|
||||
= nav_link(controller: :ci_cd) do
|
||||
= link_to group_settings_ci_cd_path(@group), title: 'CI / CD' do
|
||||
%span
|
||||
CI / CD
|
||||
|
||||
= render 'shared/sidebar_toggle_button'
|
||||
|
|
|
@ -1,84 +1,85 @@
|
|||
.nav-sidebar{ class: ("sidebar-icons-only" if collapsed_sidebar?) }
|
||||
.context-header
|
||||
= link_to profile_path, title: 'Profile Settings' do
|
||||
.avatar-container.s40.settings-avatar
|
||||
= icon('user')
|
||||
.project-title User Settings
|
||||
%ul.sidebar-top-level-items
|
||||
= nav_link(path: 'profiles#show', html_options: {class: 'home'}) do
|
||||
.nav-sidebar-inner-scroll
|
||||
.context-header
|
||||
= link_to profile_path, title: 'Profile Settings' do
|
||||
.nav-icon-container
|
||||
= custom_icon('profile')
|
||||
%span.nav-item-name
|
||||
Profile
|
||||
= nav_link(controller: [:accounts, :two_factor_auths]) do
|
||||
= link_to profile_account_path, title: 'Account' do
|
||||
.nav-icon-container
|
||||
= custom_icon('account')
|
||||
%span.nav-item-name
|
||||
Account
|
||||
- if current_application_settings.user_oauth_applications?
|
||||
= nav_link(controller: 'oauth/applications') do
|
||||
= link_to applications_profile_path, title: 'Applications' do
|
||||
.avatar-container.s40.settings-avatar
|
||||
= icon('user')
|
||||
.project-title User Settings
|
||||
%ul.sidebar-top-level-items
|
||||
= nav_link(path: 'profiles#show', html_options: {class: 'home'}) do
|
||||
= link_to profile_path, title: 'Profile Settings' do
|
||||
.nav-icon-container
|
||||
= custom_icon('applications')
|
||||
= custom_icon('profile')
|
||||
%span.nav-item-name
|
||||
Applications
|
||||
= nav_link(controller: :chat_names) do
|
||||
= link_to profile_chat_names_path, title: 'Chat' do
|
||||
.nav-icon-container
|
||||
= custom_icon('chat')
|
||||
%span.nav-item-name
|
||||
Chat
|
||||
= nav_link(controller: :personal_access_tokens) do
|
||||
= link_to profile_personal_access_tokens_path, title: 'Access Tokens' do
|
||||
.nav-icon-container
|
||||
= custom_icon('access_tokens')
|
||||
%span.nav-item-name
|
||||
Access Tokens
|
||||
= nav_link(controller: :emails) do
|
||||
= link_to profile_emails_path, title: 'Emails' do
|
||||
.nav-icon-container
|
||||
= custom_icon('emails')
|
||||
%span.nav-item-name
|
||||
Emails
|
||||
- unless current_user.ldap_user?
|
||||
= nav_link(controller: :passwords) do
|
||||
= link_to edit_profile_password_path, title: 'Password' do
|
||||
Profile
|
||||
= nav_link(controller: [:accounts, :two_factor_auths]) do
|
||||
= link_to profile_account_path, title: 'Account' do
|
||||
.nav-icon-container
|
||||
= custom_icon('lock')
|
||||
= custom_icon('account')
|
||||
%span.nav-item-name
|
||||
Password
|
||||
= nav_link(controller: :notifications) do
|
||||
= link_to profile_notifications_path, title: 'Notifications' do
|
||||
.nav-icon-container
|
||||
= custom_icon('notifications')
|
||||
%span.nav-item-name
|
||||
Notifications
|
||||
Account
|
||||
- if current_application_settings.user_oauth_applications?
|
||||
= nav_link(controller: 'oauth/applications') do
|
||||
= link_to applications_profile_path, title: 'Applications' do
|
||||
.nav-icon-container
|
||||
= custom_icon('applications')
|
||||
%span.nav-item-name
|
||||
Applications
|
||||
= nav_link(controller: :chat_names) do
|
||||
= link_to profile_chat_names_path, title: 'Chat' do
|
||||
.nav-icon-container
|
||||
= custom_icon('chat')
|
||||
%span.nav-item-name
|
||||
Chat
|
||||
= nav_link(controller: :personal_access_tokens) do
|
||||
= link_to profile_personal_access_tokens_path, title: 'Access Tokens' do
|
||||
.nav-icon-container
|
||||
= custom_icon('access_tokens')
|
||||
%span.nav-item-name
|
||||
Access Tokens
|
||||
= nav_link(controller: :emails) do
|
||||
= link_to profile_emails_path, title: 'Emails' do
|
||||
.nav-icon-container
|
||||
= custom_icon('emails')
|
||||
%span.nav-item-name
|
||||
Emails
|
||||
- unless current_user.ldap_user?
|
||||
= nav_link(controller: :passwords) do
|
||||
= link_to edit_profile_password_path, title: 'Password' do
|
||||
.nav-icon-container
|
||||
= custom_icon('lock')
|
||||
%span.nav-item-name
|
||||
Password
|
||||
= nav_link(controller: :notifications) do
|
||||
= link_to profile_notifications_path, title: 'Notifications' do
|
||||
.nav-icon-container
|
||||
= custom_icon('notifications')
|
||||
%span.nav-item-name
|
||||
Notifications
|
||||
|
||||
= nav_link(controller: :keys) do
|
||||
= link_to profile_keys_path, title: 'SSH Keys' do
|
||||
.nav-icon-container
|
||||
= custom_icon('key')
|
||||
%span.nav-item-name
|
||||
SSH Keys
|
||||
= nav_link(controller: :gpg_keys) do
|
||||
= link_to profile_gpg_keys_path, title: 'GPG Keys' do
|
||||
.nav-icon-container
|
||||
= custom_icon('key_2')
|
||||
%span.nav-item-name
|
||||
GPG Keys
|
||||
= nav_link(controller: :preferences) do
|
||||
= link_to profile_preferences_path, title: 'Preferences' do
|
||||
.nav-icon-container
|
||||
= custom_icon('preferences')
|
||||
%span.nav-item-name
|
||||
Preferences
|
||||
= nav_link(path: 'profiles#audit_log') do
|
||||
= link_to audit_log_profile_path, title: 'Authentication log' do
|
||||
.nav-icon-container
|
||||
= custom_icon('authentication_log')
|
||||
%span.nav-item-name
|
||||
Authentication log
|
||||
= nav_link(controller: :keys) do
|
||||
= link_to profile_keys_path, title: 'SSH Keys' do
|
||||
.nav-icon-container
|
||||
= custom_icon('key')
|
||||
%span.nav-item-name
|
||||
SSH Keys
|
||||
= nav_link(controller: :gpg_keys) do
|
||||
= link_to profile_gpg_keys_path, title: 'GPG Keys' do
|
||||
.nav-icon-container
|
||||
= custom_icon('key_2')
|
||||
%span.nav-item-name
|
||||
GPG Keys
|
||||
= nav_link(controller: :preferences) do
|
||||
= link_to profile_preferences_path, title: 'Preferences' do
|
||||
.nav-icon-container
|
||||
= custom_icon('preferences')
|
||||
%span.nav-item-name
|
||||
Preferences
|
||||
= nav_link(path: 'profiles#audit_log') do
|
||||
= link_to audit_log_profile_path, title: 'Authentication log' do
|
||||
.nav-icon-container
|
||||
= custom_icon('authentication_log')
|
||||
%span.nav-item-name
|
||||
Authentication log
|
||||
|
||||
= render 'shared/sidebar_toggle_button'
|
||||
= render 'shared/sidebar_toggle_button'
|
||||
|
|
|
@ -1,261 +1,262 @@
|
|||
.nav-sidebar{ class: ("sidebar-icons-only" if collapsed_sidebar?) }
|
||||
- can_edit = can?(current_user, :admin_project, @project)
|
||||
.context-header
|
||||
= link_to project_path(@project), title: @project.name do
|
||||
.avatar-container.s40.project-avatar
|
||||
= project_icon(@project, alt: @project.name, class: 'avatar s40 avatar-tile')
|
||||
.project-title
|
||||
= @project.name
|
||||
%ul.sidebar-top-level-items
|
||||
= nav_link(path: ['projects#show', 'projects#activity', 'cycle_analytics#show'], html_options: { class: 'home' }) do
|
||||
= link_to project_path(@project), title: 'Project overview', class: 'shortcuts-project' do
|
||||
.nav-icon-container
|
||||
= custom_icon('project')
|
||||
%span.nav-item-name
|
||||
Overview
|
||||
|
||||
%ul.sidebar-sub-level-items
|
||||
= nav_link(path: 'projects#show') do
|
||||
= link_to project_path(@project), title: _('Project details'), class: 'shortcuts-project' do
|
||||
%span= _('Details')
|
||||
|
||||
= nav_link(path: 'projects#activity') do
|
||||
= link_to activity_project_path(@project), title: _('Activity'), class: 'shortcuts-project-activity' do
|
||||
%span= _('Activity')
|
||||
|
||||
- if can?(current_user, :read_cycle_analytics, @project)
|
||||
= nav_link(path: 'cycle_analytics#show') do
|
||||
= link_to project_cycle_analytics_path(@project), title: _('Cycle Analytics'), class: 'shortcuts-project-cycle-analytics' do
|
||||
%span= _('Cycle Analytics')
|
||||
|
||||
- if project_nav_tab? :files
|
||||
= nav_link(controller: %w(tree blob blame edit_tree new_tree find_file commit commits compare projects/repositories tags branches releases graphs network)) do
|
||||
= link_to project_tree_path(@project), title: 'Repository', class: 'shortcuts-tree' do
|
||||
.nav-sidebar-inner-scroll
|
||||
- can_edit = can?(current_user, :admin_project, @project)
|
||||
.context-header
|
||||
= link_to project_path(@project), title: @project.name do
|
||||
.avatar-container.s40.project-avatar
|
||||
= project_icon(@project, alt: @project.name, class: 'avatar s40 avatar-tile')
|
||||
.project-title
|
||||
= @project.name
|
||||
%ul.sidebar-top-level-items
|
||||
= nav_link(path: ['projects#show', 'projects#activity', 'cycle_analytics#show'], html_options: { class: 'home' }) do
|
||||
= link_to project_path(@project), title: 'Project overview', class: 'shortcuts-project' do
|
||||
.nav-icon-container
|
||||
= custom_icon('doc_text')
|
||||
= custom_icon('project')
|
||||
%span.nav-item-name
|
||||
Repository
|
||||
Overview
|
||||
|
||||
%ul.sidebar-sub-level-items
|
||||
= nav_link(controller: %w(tree blob blame edit_tree new_tree find_file)) do
|
||||
= link_to project_tree_path(@project) do
|
||||
#{ _('Files') }
|
||||
= nav_link(path: 'projects#show') do
|
||||
= link_to project_path(@project), title: _('Project details'), class: 'shortcuts-project' do
|
||||
%span= _('Details')
|
||||
|
||||
= nav_link(controller: [:commit, :commits]) do
|
||||
= link_to project_commits_path(@project, current_ref) do
|
||||
#{ _('Commits') }
|
||||
= nav_link(path: 'projects#activity') do
|
||||
= link_to activity_project_path(@project), title: _('Activity'), class: 'shortcuts-project-activity' do
|
||||
%span= _('Activity')
|
||||
|
||||
= nav_link(html_options: {class: branches_tab_class}) do
|
||||
= link_to project_branches_path(@project) do
|
||||
#{ _('Branches') }
|
||||
- if can?(current_user, :read_cycle_analytics, @project)
|
||||
= nav_link(path: 'cycle_analytics#show') do
|
||||
= link_to project_cycle_analytics_path(@project), title: _('Cycle Analytics'), class: 'shortcuts-project-cycle-analytics' do
|
||||
%span= _('Cycle Analytics')
|
||||
|
||||
= nav_link(controller: [:tags, :releases]) do
|
||||
= link_to project_tags_path(@project) do
|
||||
#{ _('Tags') }
|
||||
- if project_nav_tab? :files
|
||||
= nav_link(controller: %w(tree blob blame edit_tree new_tree find_file commit commits compare projects/repositories tags branches releases graphs network)) do
|
||||
= link_to project_tree_path(@project), title: 'Repository', class: 'shortcuts-tree' do
|
||||
.nav-icon-container
|
||||
= custom_icon('doc_text')
|
||||
%span.nav-item-name
|
||||
Repository
|
||||
|
||||
= nav_link(path: 'graphs#show') do
|
||||
= link_to project_graph_path(@project, current_ref) do
|
||||
#{ _('Contributors') }
|
||||
%ul.sidebar-sub-level-items
|
||||
= nav_link(controller: %w(tree blob blame edit_tree new_tree find_file)) do
|
||||
= link_to project_tree_path(@project) do
|
||||
#{ _('Files') }
|
||||
|
||||
= nav_link(controller: %w(network)) do
|
||||
= link_to project_network_path(@project, current_ref) do
|
||||
#{ s_('ProjectNetworkGraph|Graph') }
|
||||
= nav_link(controller: [:commit, :commits]) do
|
||||
= link_to project_commits_path(@project, current_ref) do
|
||||
#{ _('Commits') }
|
||||
|
||||
= nav_link(controller: :compare) do
|
||||
= link_to project_compare_index_path(@project, from: @repository.root_ref, to: current_ref) do
|
||||
#{ _('Compare') }
|
||||
= nav_link(html_options: {class: branches_tab_class}) do
|
||||
= link_to project_branches_path(@project) do
|
||||
#{ _('Branches') }
|
||||
|
||||
= nav_link(path: 'graphs#charts') do
|
||||
= link_to charts_project_graph_path(@project, current_ref) do
|
||||
#{ _('Charts') }
|
||||
= nav_link(controller: [:tags, :releases]) do
|
||||
= link_to project_tags_path(@project) do
|
||||
#{ _('Tags') }
|
||||
|
||||
- if project_nav_tab? :container_registry
|
||||
= nav_link(controller: %w[projects/registry/repositories]) do
|
||||
= link_to project_container_registry_index_path(@project), title: 'Container Registry', class: 'shortcuts-container-registry' do
|
||||
.nav-icon-container
|
||||
= custom_icon('container_registry')
|
||||
%span.nav-item-name
|
||||
Registry
|
||||
= nav_link(path: 'graphs#show') do
|
||||
= link_to project_graph_path(@project, current_ref) do
|
||||
#{ _('Contributors') }
|
||||
|
||||
- if project_nav_tab? :issues
|
||||
= nav_link(controller: @project.issues_enabled? ? [:issues, :labels, :milestones, :boards] : :issues) do
|
||||
= link_to project_issues_path(@project), title: 'Issues', class: 'shortcuts-issues' do
|
||||
.nav-icon-container
|
||||
= custom_icon('issues')
|
||||
%span.nav-item-name
|
||||
Issues
|
||||
- if @project.issues_enabled?
|
||||
%span.badge.count.issue_counter= number_with_delimiter(IssuesFinder.new(current_user, project_id: @project.id).execute.opened.count)
|
||||
= nav_link(controller: %w(network)) do
|
||||
= link_to project_network_path(@project, current_ref) do
|
||||
#{ s_('ProjectNetworkGraph|Graph') }
|
||||
|
||||
%ul.sidebar-sub-level-items
|
||||
= nav_link(controller: :issues) do
|
||||
= link_to project_issues_path(@project), title: 'Issues' do
|
||||
%span
|
||||
List
|
||||
= nav_link(controller: :compare) do
|
||||
= link_to project_compare_index_path(@project, from: @repository.root_ref, to: current_ref) do
|
||||
#{ _('Compare') }
|
||||
|
||||
= nav_link(controller: :boards) do
|
||||
= link_to project_boards_path(@project), title: 'Board' do
|
||||
%span
|
||||
Board
|
||||
= nav_link(path: 'graphs#charts') do
|
||||
= link_to charts_project_graph_path(@project, current_ref) do
|
||||
#{ _('Charts') }
|
||||
|
||||
= nav_link(controller: :labels) do
|
||||
= link_to project_labels_path(@project), title: 'Labels' do
|
||||
%span
|
||||
Labels
|
||||
- if project_nav_tab? :container_registry
|
||||
= nav_link(controller: %w[projects/registry/repositories]) do
|
||||
= link_to project_container_registry_index_path(@project), title: 'Container Registry', class: 'shortcuts-container-registry' do
|
||||
.nav-icon-container
|
||||
= custom_icon('container_registry')
|
||||
%span.nav-item-name
|
||||
Registry
|
||||
|
||||
= nav_link(controller: :milestones) do
|
||||
= link_to project_milestones_path(@project), title: 'Milestones' do
|
||||
%span
|
||||
Milestones
|
||||
- if project_nav_tab? :issues
|
||||
= nav_link(controller: @project.issues_enabled? ? [:issues, :labels, :milestones, :boards] : :issues) do
|
||||
= link_to project_issues_path(@project), title: 'Issues', class: 'shortcuts-issues' do
|
||||
.nav-icon-container
|
||||
= custom_icon('issues')
|
||||
%span.nav-item-name
|
||||
Issues
|
||||
- if @project.issues_enabled?
|
||||
%span.badge.count.issue_counter= number_with_delimiter(IssuesFinder.new(current_user, project_id: @project.id).execute.opened.count)
|
||||
|
||||
- if project_nav_tab? :merge_requests
|
||||
= nav_link(controller: @project.issues_enabled? ? :merge_requests : [:merge_requests, :labels, :milestones]) do
|
||||
= link_to project_merge_requests_path(@project), title: 'Merge Requests', class: 'shortcuts-merge_requests' do
|
||||
.nav-icon-container
|
||||
= custom_icon('mr_bold')
|
||||
%span.nav-item-name
|
||||
Merge Requests
|
||||
%span.badge.count.merge_counter.js-merge-counter= number_with_delimiter(MergeRequestsFinder.new(current_user, project_id: @project.id).execute.opened.count)
|
||||
|
||||
- if project_nav_tab? :pipelines
|
||||
= nav_link(controller: [:pipelines, :builds, :jobs, :pipeline_schedules, :environments, :artifacts]) do
|
||||
= link_to project_pipelines_path(@project), title: 'CI / CD', class: 'shortcuts-pipelines' do
|
||||
.nav-icon-container
|
||||
= custom_icon('pipeline')
|
||||
%span.nav-item-name
|
||||
CI / CD
|
||||
|
||||
%ul.sidebar-sub-level-items
|
||||
- if project_nav_tab? :pipelines
|
||||
= nav_link(path: ['pipelines#index', 'pipelines#show']) do
|
||||
= link_to project_pipelines_path(@project), title: 'Pipelines', class: 'shortcuts-pipelines' do
|
||||
%ul.sidebar-sub-level-items
|
||||
= nav_link(controller: :issues) do
|
||||
= link_to project_issues_path(@project), title: 'Issues' do
|
||||
%span
|
||||
Pipelines
|
||||
List
|
||||
|
||||
- if project_nav_tab? :builds
|
||||
= nav_link(controller: [:jobs, :artifacts]) do
|
||||
= link_to project_jobs_path(@project), title: 'Jobs', class: 'shortcuts-builds' do
|
||||
= nav_link(controller: :boards) do
|
||||
= link_to project_boards_path(@project), title: 'Board' do
|
||||
%span
|
||||
Jobs
|
||||
Board
|
||||
|
||||
- if project_nav_tab? :pipelines
|
||||
= nav_link(controller: :pipeline_schedules) do
|
||||
= link_to pipeline_schedules_path(@project), title: 'Schedules', class: 'shortcuts-builds' do
|
||||
= nav_link(controller: :labels) do
|
||||
= link_to project_labels_path(@project), title: 'Labels' do
|
||||
%span
|
||||
Schedules
|
||||
Labels
|
||||
|
||||
- if project_nav_tab? :environments
|
||||
= nav_link(controller: :environments) do
|
||||
= link_to project_environments_path(@project), title: 'Environments', class: 'shortcuts-environments' do
|
||||
= nav_link(controller: :milestones) do
|
||||
= link_to project_milestones_path(@project), title: 'Milestones' do
|
||||
%span
|
||||
Environments
|
||||
Milestones
|
||||
|
||||
- if @project.feature_available?(:builds, current_user) && !@project.empty_repo?
|
||||
= nav_link(path: 'pipelines#charts') do
|
||||
= link_to charts_project_pipelines_path(@project), title: 'Charts', class: 'shortcuts-pipelines-charts' do
|
||||
%span
|
||||
Charts
|
||||
- if project_nav_tab? :merge_requests
|
||||
= nav_link(controller: @project.issues_enabled? ? :merge_requests : [:merge_requests, :labels, :milestones]) do
|
||||
= link_to project_merge_requests_path(@project), title: 'Merge Requests', class: 'shortcuts-merge_requests' do
|
||||
.nav-icon-container
|
||||
= custom_icon('mr_bold')
|
||||
%span.nav-item-name
|
||||
Merge Requests
|
||||
%span.badge.count.merge_counter.js-merge-counter= number_with_delimiter(MergeRequestsFinder.new(current_user, project_id: @project.id).execute.opened.count)
|
||||
|
||||
- if project_nav_tab? :wiki
|
||||
= nav_link(controller: :wikis) do
|
||||
= link_to get_project_wiki_path(@project), title: 'Wiki', class: 'shortcuts-wiki' do
|
||||
.nav-icon-container
|
||||
= custom_icon('wiki')
|
||||
%span.nav-item-name
|
||||
Wiki
|
||||
- if project_nav_tab? :pipelines
|
||||
= nav_link(controller: [:pipelines, :builds, :jobs, :pipeline_schedules, :environments, :artifacts]) do
|
||||
= link_to project_pipelines_path(@project), title: 'CI / CD', class: 'shortcuts-pipelines' do
|
||||
.nav-icon-container
|
||||
= custom_icon('pipeline')
|
||||
%span.nav-item-name
|
||||
CI / CD
|
||||
|
||||
- if project_nav_tab? :snippets
|
||||
= nav_link(controller: :snippets) do
|
||||
= link_to project_snippets_path(@project), title: 'Snippets', class: 'shortcuts-snippets' do
|
||||
.nav-icon-container
|
||||
= custom_icon('snippets')
|
||||
%span.nav-item-name
|
||||
Snippets
|
||||
|
||||
- if project_nav_tab? :settings
|
||||
= nav_link(path: %w[projects#edit project_members#index integrations#show services#edit repository#show ci_cd#show pages#show]) do
|
||||
= link_to edit_project_path(@project), title: 'Settings', class: 'shortcuts-tree' do
|
||||
.nav-icon-container
|
||||
= custom_icon('settings')
|
||||
%span.nav-item-name
|
||||
Settings
|
||||
|
||||
%ul.sidebar-sub-level-items
|
||||
- can_edit = can?(current_user, :admin_project, @project)
|
||||
- if can_edit
|
||||
= nav_link(path: %w[projects#edit]) do
|
||||
= link_to edit_project_path(@project), title: 'General' do
|
||||
%span
|
||||
General
|
||||
= nav_link(controller: :project_members) do
|
||||
= link_to project_project_members_path(@project), title: 'Members' do
|
||||
%span
|
||||
Members
|
||||
- if can_edit
|
||||
= nav_link(controller: [:integrations, :services, :hooks, :hook_logs]) do
|
||||
= link_to project_settings_integrations_path(@project), title: 'Integrations' do
|
||||
%span
|
||||
Integrations
|
||||
= nav_link(controller: :repository) do
|
||||
= link_to project_settings_repository_path(@project), title: 'Repository' do
|
||||
%span
|
||||
Repository
|
||||
- if @project.feature_available?(:builds, current_user)
|
||||
= nav_link(controller: :ci_cd) do
|
||||
= link_to project_settings_ci_cd_path(@project), title: 'CI / CD' do
|
||||
%ul.sidebar-sub-level-items
|
||||
- if project_nav_tab? :pipelines
|
||||
= nav_link(path: ['pipelines#index', 'pipelines#show']) do
|
||||
= link_to project_pipelines_path(@project), title: 'Pipelines', class: 'shortcuts-pipelines' do
|
||||
%span
|
||||
CI / CD
|
||||
- if Gitlab.config.pages.enabled
|
||||
= nav_link(controller: :pages) do
|
||||
= link_to project_pages_path(@project), title: 'Pages' do
|
||||
Pipelines
|
||||
|
||||
- if project_nav_tab? :builds
|
||||
= nav_link(controller: [:jobs, :artifacts]) do
|
||||
= link_to project_jobs_path(@project), title: 'Jobs', class: 'shortcuts-builds' do
|
||||
%span
|
||||
Pages
|
||||
Jobs
|
||||
|
||||
- else
|
||||
= nav_link(path: %w[members#show]) do
|
||||
= link_to project_settings_members_path(@project), title: 'Members', class: 'shortcuts-tree' do
|
||||
.nav-icon-container
|
||||
= custom_icon('members')
|
||||
%span.nav-item-name
|
||||
Members
|
||||
- if project_nav_tab? :pipelines
|
||||
= nav_link(controller: :pipeline_schedules) do
|
||||
= link_to pipeline_schedules_path(@project), title: 'Schedules', class: 'shortcuts-builds' do
|
||||
%span
|
||||
Schedules
|
||||
|
||||
= render 'shared/sidebar_toggle_button'
|
||||
- if project_nav_tab? :environments
|
||||
= nav_link(controller: :environments) do
|
||||
= link_to project_environments_path(@project), title: 'Environments', class: 'shortcuts-environments' do
|
||||
%span
|
||||
Environments
|
||||
|
||||
-# Shortcut to Project > Activity
|
||||
%li.hidden
|
||||
= link_to activity_project_path(@project), title: 'Activity', class: 'shortcuts-project-activity' do
|
||||
%span
|
||||
Activity
|
||||
- if @project.feature_available?(:builds, current_user) && !@project.empty_repo?
|
||||
= nav_link(path: 'pipelines#charts') do
|
||||
= link_to charts_project_pipelines_path(@project), title: 'Charts', class: 'shortcuts-pipelines-charts' do
|
||||
%span
|
||||
Charts
|
||||
|
||||
-# Shortcut to Repository > Graph (formerly, Network)
|
||||
- if project_nav_tab? :network
|
||||
- if project_nav_tab? :wiki
|
||||
= nav_link(controller: :wikis) do
|
||||
= link_to get_project_wiki_path(@project), title: 'Wiki', class: 'shortcuts-wiki' do
|
||||
.nav-icon-container
|
||||
= custom_icon('wiki')
|
||||
%span.nav-item-name
|
||||
Wiki
|
||||
|
||||
- if project_nav_tab? :snippets
|
||||
= nav_link(controller: :snippets) do
|
||||
= link_to project_snippets_path(@project), title: 'Snippets', class: 'shortcuts-snippets' do
|
||||
.nav-icon-container
|
||||
= custom_icon('snippets')
|
||||
%span.nav-item-name
|
||||
Snippets
|
||||
|
||||
- if project_nav_tab? :settings
|
||||
= nav_link(path: %w[projects#edit project_members#index integrations#show services#edit repository#show ci_cd#show pages#show]) do
|
||||
= link_to edit_project_path(@project), title: 'Settings', class: 'shortcuts-tree' do
|
||||
.nav-icon-container
|
||||
= custom_icon('settings')
|
||||
%span.nav-item-name
|
||||
Settings
|
||||
|
||||
%ul.sidebar-sub-level-items
|
||||
- can_edit = can?(current_user, :admin_project, @project)
|
||||
- if can_edit
|
||||
= nav_link(path: %w[projects#edit]) do
|
||||
= link_to edit_project_path(@project), title: 'General' do
|
||||
%span
|
||||
General
|
||||
= nav_link(controller: :project_members) do
|
||||
= link_to project_project_members_path(@project), title: 'Members' do
|
||||
%span
|
||||
Members
|
||||
- if can_edit
|
||||
= nav_link(controller: [:integrations, :services, :hooks, :hook_logs]) do
|
||||
= link_to project_settings_integrations_path(@project), title: 'Integrations' do
|
||||
%span
|
||||
Integrations
|
||||
= nav_link(controller: :repository) do
|
||||
= link_to project_settings_repository_path(@project), title: 'Repository' do
|
||||
%span
|
||||
Repository
|
||||
- if @project.feature_available?(:builds, current_user)
|
||||
= nav_link(controller: :ci_cd) do
|
||||
= link_to project_settings_ci_cd_path(@project), title: 'CI / CD' do
|
||||
%span
|
||||
CI / CD
|
||||
- if Gitlab.config.pages.enabled
|
||||
= nav_link(controller: :pages) do
|
||||
= link_to project_pages_path(@project), title: 'Pages' do
|
||||
%span
|
||||
Pages
|
||||
|
||||
- else
|
||||
= nav_link(path: %w[members#show]) do
|
||||
= link_to project_settings_members_path(@project), title: 'Members', class: 'shortcuts-tree' do
|
||||
.nav-icon-container
|
||||
= custom_icon('members')
|
||||
%span.nav-item-name
|
||||
Members
|
||||
|
||||
= render 'shared/sidebar_toggle_button'
|
||||
|
||||
-# Shortcut to Project > Activity
|
||||
%li.hidden
|
||||
= link_to project_network_path(@project, current_ref), title: 'Network', class: 'shortcuts-network' do
|
||||
Graph
|
||||
= link_to activity_project_path(@project), title: 'Activity', class: 'shortcuts-project-activity' do
|
||||
%span
|
||||
Activity
|
||||
|
||||
-# Shortcut to Repository > Charts (formerly, top-nav item "Graphs")
|
||||
- unless @project.empty_repo?
|
||||
-# Shortcut to Repository > Graph (formerly, Network)
|
||||
- if project_nav_tab? :network
|
||||
%li.hidden
|
||||
= link_to project_network_path(@project, current_ref), title: 'Network', class: 'shortcuts-network' do
|
||||
Graph
|
||||
|
||||
-# Shortcut to Repository > Charts (formerly, top-nav item "Graphs")
|
||||
- unless @project.empty_repo?
|
||||
%li.hidden
|
||||
= link_to charts_project_graph_path(@project, current_ref), title: 'Charts', class: 'shortcuts-repository-charts' do
|
||||
Charts
|
||||
|
||||
-# Shortcut to Issues > New Issue
|
||||
%li.hidden
|
||||
= link_to charts_project_graph_path(@project, current_ref), title: 'Charts', class: 'shortcuts-repository-charts' do
|
||||
Charts
|
||||
= link_to new_project_issue_path(@project), class: 'shortcuts-new-issue' do
|
||||
Create a new issue
|
||||
|
||||
-# Shortcut to Issues > New Issue
|
||||
%li.hidden
|
||||
= link_to new_project_issue_path(@project), class: 'shortcuts-new-issue' do
|
||||
Create a new issue
|
||||
-# Shortcut to Pipelines > Jobs
|
||||
- if project_nav_tab? :builds
|
||||
%li.hidden
|
||||
= link_to project_jobs_path(@project), title: 'Jobs', class: 'shortcuts-builds' do
|
||||
Jobs
|
||||
|
||||
-# Shortcut to Pipelines > Jobs
|
||||
- if project_nav_tab? :builds
|
||||
-# Shortcut to commits page
|
||||
- if project_nav_tab? :commits
|
||||
%li.hidden
|
||||
= link_to project_commits_path(@project), title: 'Commits', class: 'shortcuts-commits' do
|
||||
Commits
|
||||
|
||||
-# Shortcut to issue boards
|
||||
%li.hidden
|
||||
= link_to project_jobs_path(@project), title: 'Jobs', class: 'shortcuts-builds' do
|
||||
Jobs
|
||||
|
||||
-# Shortcut to commits page
|
||||
- if project_nav_tab? :commits
|
||||
%li.hidden
|
||||
= link_to project_commits_path(@project), title: 'Commits', class: 'shortcuts-commits' do
|
||||
Commits
|
||||
|
||||
-# Shortcut to issue boards
|
||||
%li.hidden
|
||||
= link_to 'Issue Boards', project_boards_path(@project), title: 'Issue Boards', class: 'shortcuts-issue-boards'
|
||||
= link_to 'Issue Boards', project_boards_path(@project), title: 'Issue Boards', class: 'shortcuts-issue-boards'
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
= link_to 'Close merge request', merge_request_path(@merge_request, merge_request: { state_event: :close }), method: :put, class: "btn btn-nr btn-comment btn-close close-mr-link js-note-target-close", title: "Close merge request", data: { original_text: "Close merge request", alternative_text: "Comment & close merge request"}
|
||||
- if @merge_request.reopenable?
|
||||
= link_to 'Reopen merge request', merge_request_path(@merge_request, merge_request: { state_event: :reopen }), method: :put, class: "btn btn-nr btn-comment btn-reopen reopen-mr-link js-note-target-close js-note-target-reopen", title: "Reopen merge request", data: { original_text: "Reopen merge request", alternative_text: "Comment & reopen merge request"}
|
||||
%comment-and-resolve-btn{ "inline-template" => true, ":discussion-id" => "" }
|
||||
%button.btn.btn-nr.btn-default.append-right-10.js-comment-resolve-button{ "v-if" => "showButton", type: "submit", data: { project_path: "#{project_path(@merge_request.project)}" } }
|
||||
{{ buttonText }}
|
||||
%comment-and-resolve-btn{ "inline-template" => true }
|
||||
%button.btn.btn-nr.btn-default.append-right-10.js-comment-resolve-button{ "v-if" => "showButton", type: "submit", data: { project_path: "#{project_path(@merge_request.project)}" } }
|
||||
{{ buttonText }}
|
||||
|
||||
#notes= render "shared/notes/notes_with_form", :autocomplete => true
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
.col-sm-6.milestone-actions
|
||||
- if can?(current_user, :admin_milestones, @group)
|
||||
- if milestone.is_group_milestone?
|
||||
= link_to edit_group_milestone_path(@group, milestone.id), class: "btn btn-xs btn-grouped" do
|
||||
= link_to edit_group_milestone_path(@group, milestone), class: "btn btn-xs btn-grouped" do
|
||||
Edit
|
||||
\
|
||||
- if milestone.closed?
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
.pull-right
|
||||
- if can?(current_user, :admin_milestones, group)
|
||||
- if milestone.is_group_milestone?
|
||||
= link_to edit_group_milestone_path(group, milestone.iid), class: "btn btn btn-grouped" do
|
||||
= link_to edit_group_milestone_path(group, milestone), class: "btn btn btn-grouped" do
|
||||
Edit
|
||||
- if milestone.active?
|
||||
= link_to 'Close Milestone', group_milestone_route(milestone, {state_event: :close }), method: :put, class: "btn btn-grouped btn-close"
|
||||
|
|
|
@ -4,13 +4,9 @@ class CreateGpgSignatureWorker
|
|||
|
||||
def perform(commit_sha, project_id)
|
||||
project = Project.find_by(id: project_id)
|
||||
|
||||
return unless project
|
||||
|
||||
commit = project.commit(commit_sha)
|
||||
|
||||
return unless commit
|
||||
|
||||
commit.signature
|
||||
# This calculates and caches the signature in the database
|
||||
Gitlab::Gpg::Commit.new(project, commit_sha).signature
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,6 +4,6 @@ class GitlabShellWorker
|
|||
include DedicatedSidekiqQueue
|
||||
|
||||
def perform(action, *arg)
|
||||
gitlab_shell.send(action, *arg)
|
||||
gitlab_shell.__send__(action, *arg) # rubocop:disable GitlabSecurity/PublicSend
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
title: Added tests for commits API unauthenticated user and public/private project
|
||||
merge_request: 13287
|
||||
author: Jacopo Beschi @jacopo-beschi
|
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
title: Fix CI_PROJECT_PATH_SLUG slugify
|
||||
merge_request: 13350
|
||||
author: Ivan Chernov
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix commit list not loading the correct page when scrolling
|
||||
merge_request:
|
||||
author:
|
||||
type: fixed
|
|
@ -649,6 +649,9 @@ test:
|
|||
default:
|
||||
path: tmp/tests/repositories/
|
||||
gitaly_address: unix:tmp/tests/gitaly/gitaly.socket
|
||||
failure_count_threshold: 999999
|
||||
failure_wait_time: 0
|
||||
storage_timeout: 30
|
||||
broken:
|
||||
path: tmp/tests/non-existent-repositories
|
||||
gitaly_address: unix:tmp/tests/gitaly/gitaly.socket
|
||||
|
|
|
@ -5,5 +5,5 @@ ActsAsTaggableOn.strict_case_match = true
|
|||
ActsAsTaggableOn.tags_counter = false
|
||||
|
||||
# validate that counter cache is disabled
|
||||
raise "Counter cache is not disabled" if
|
||||
raise "Counter cache is not disabled" if
|
||||
ActsAsTaggableOn::Tagging.reflections["tag"].options[:counter_cache]
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# rubocop:disable GitlabSecurity/PublicSend
|
||||
|
||||
require_dependency Rails.root.join('lib/gitlab') # Load Gitlab as soon as possible
|
||||
|
||||
class Settings < Settingslogic
|
||||
|
|
|
@ -37,12 +37,12 @@ def validate_storages_config
|
|||
storage_validation_error("#{name} is not a valid storage, because it has no `path` key. Refer to gitlab.yml.example for an updated example")
|
||||
end
|
||||
|
||||
%w(failure_count_threshold failure_wait_time failure_reset_time storage_timeout).each do |setting|
|
||||
%w(failure_count_threshold failure_reset_time storage_timeout).each do |setting|
|
||||
# Falling back to the defaults is fine!
|
||||
next if repository_storage[setting].nil?
|
||||
|
||||
unless repository_storage[setting].to_f > 0
|
||||
storage_validation_error("#{setting}, for storage `#{name}` needs to be greater than 0")
|
||||
storage_validation_error("`#{setting}` for storage `#{name}` needs to be greater than 0")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
module ActiveRecord
|
||||
class PredicateBuilder
|
||||
class ArrayHandler
|
||||
module TypeCasting
|
||||
def call(attribute, value)
|
||||
# This is necessary because by default ActiveRecord does not respect
|
||||
# custom type definitions (like our `ShaAttribute`) when providing an
|
||||
# array in `where`, like in `where(commit_sha: [sha1, sha2, sha3])`.
|
||||
model = attribute.relation&.engine
|
||||
type = model.user_provided_columns[attribute.name] if model
|
||||
value = value.map { |value| type.type_cast_for_database(value) } if type
|
||||
|
||||
super(attribute, value)
|
||||
end
|
||||
end
|
||||
|
||||
prepend TypeCasting
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,15 +1,15 @@
|
|||
app = Rails.application
|
||||
|
||||
if app.config.serve_static_files
|
||||
# The `ActionDispatch::Static` middleware intercepts requests for static files
|
||||
# by checking if they exist in the `/public` directory.
|
||||
# The `ActionDispatch::Static` middleware intercepts requests for static files
|
||||
# by checking if they exist in the `/public` directory.
|
||||
# We're replacing it with our `Gitlab::Middleware::Static` that does the same,
|
||||
# except ignoring `/uploads`, letting those go through to the GitLab Rails app.
|
||||
|
||||
app.config.middleware.swap(
|
||||
ActionDispatch::Static,
|
||||
Gitlab::Middleware::Static,
|
||||
app.paths["public"].first,
|
||||
ActionDispatch::Static,
|
||||
Gitlab::Middleware::Static,
|
||||
app.paths["public"].first,
|
||||
app.config.static_cache_control
|
||||
)
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
# as the ActionDispatch::Request object. This is necessary for libraries
|
||||
# like rack_attack where they don't use ActionDispatch, and we want them
|
||||
# to block/throttle requests on private networks.
|
||||
# Rack Attack specific issue: https://github.com/kickstarter/rack-attack/issues/145
|
||||
# Rack Attack specific issue: https://github.com/kickstarter/rack-attack/issues/145
|
||||
module Rack
|
||||
class Request
|
||||
def trusted_proxy?(ip)
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
resource :repository, only: [:create] do
|
||||
member do
|
||||
get ':ref/archive', constraints: { format: Gitlab::PathRegex.archive_formats_regex, ref: /.+/ }, action: 'archive', as: 'archive'
|
||||
|
||||
|
||||
# deprecated since GitLab 9.5
|
||||
get 'archive', constraints: { format: Gitlab::PathRegex.archive_formats_regex }, as: 'archive_alternative'
|
||||
end
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
class DefaultRequestAccessProjects < ActiveRecord::Migration
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
DOWNTIME = false
|
||||
|
||||
|
||||
def up
|
||||
change_column_default :projects, :request_access_enabled, false
|
||||
end
|
||||
|
|
|
@ -21,7 +21,7 @@ class UpdateRetriedForCiBuild < ActiveRecord::Migration
|
|||
private
|
||||
|
||||
def up_mysql
|
||||
# This is a trick to overcome MySQL limitation:
|
||||
# This is a trick to overcome MySQL limitation:
|
||||
# Mysql2::Error: Table 'ci_builds' is specified twice, both as a target for 'UPDATE' and as a separate source for data
|
||||
# However, this leads to create a temporary table from `max(ci_builds.id)` which is slow and do full database update
|
||||
execute <<-SQL.strip_heredoc
|
||||
|
|
|
@ -7,7 +7,7 @@ class MigrateOldArtifacts < ActiveRecord::Migration
|
|||
|
||||
# This uses special heuristic to find potential candidates for data migration
|
||||
# Read more about this here: https://gitlab.com/gitlab-org/gitlab-ce/issues/32036#note_30422345
|
||||
|
||||
|
||||
def up
|
||||
builds_with_artifacts.find_each do |build|
|
||||
build.migrate_artifacts!
|
||||
|
@ -51,14 +51,14 @@ class MigrateOldArtifacts < ActiveRecord::Migration
|
|||
private
|
||||
|
||||
def source_artifacts_path
|
||||
@source_artifacts_path ||=
|
||||
@source_artifacts_path ||=
|
||||
File.join(Gitlab.config.artifacts.path,
|
||||
created_at.utc.strftime('%Y_%m'),
|
||||
ci_id.to_s, id.to_s)
|
||||
end
|
||||
|
||||
def target_artifacts_path
|
||||
@target_artifacts_path ||=
|
||||
@target_artifacts_path ||=
|
||||
File.join(Gitlab.config.artifacts.path,
|
||||
created_at.utc.strftime('%Y_%m'),
|
||||
project_id.to_s, id.to_s)
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
|
||||
# for more information on how to write migrations for GitLab.
|
||||
|
||||
class RemoveDuplicateMrEvents < ActiveRecord::Migration
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
DOWNTIME = false
|
||||
|
||||
class Event < ActiveRecord::Base
|
||||
self.table_name = 'events'
|
||||
end
|
||||
|
||||
def up
|
||||
base_condition = "action = 1 AND target_type = 'MergeRequest' AND created_at > '2017-08-13'"
|
||||
Event.select('target_id, count(*)')
|
||||
.where(base_condition)
|
||||
.group('target_id').having('count(*) > 1').each do |event|
|
||||
duplicates = Event.where("#{base_condition} AND target_id = #{event.target_id}").pluck(:id)
|
||||
duplicates.shift
|
||||
|
||||
Event.where(id: duplicates).delete_all
|
||||
end
|
||||
end
|
||||
|
||||
def down
|
||||
end
|
||||
end
|
|
@ -11,7 +11,7 @@
|
|||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(version: 20170809161910) do
|
||||
ActiveRecord::Schema.define(version: 20170815060945) do
|
||||
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "plpgsql"
|
||||
|
|
|
@ -46,6 +46,7 @@
|
|||
|
||||
## Databases
|
||||
|
||||
- [Merge Request Checklist](database_merge_request_checklist.md)
|
||||
- [What requires downtime?](what_requires_downtime.md)
|
||||
- [Adding database indexes](adding_database_indexes.md)
|
||||
- [Post Deployment Migrations](post_deployment_migrations.md)
|
||||
|
@ -56,6 +57,9 @@
|
|||
- [Background Migrations](background_migrations.md)
|
||||
- [Storing SHA1 Hashes As Binary](sha1_as_binary.md)
|
||||
- [Iterating Tables In Batches](iterating_tables_in_batches.md)
|
||||
- [Ordering Table Columns](ordering_table_columns.md)
|
||||
- [Verifying Database Capabilities](verifying_database_capabilities.md)
|
||||
- [Hash Indexes](hash_indexes.md)
|
||||
|
||||
## i18n
|
||||
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
# Merge Request Checklist
|
||||
|
||||
When creating a merge request that performs database related changes (schema
|
||||
changes, adjusting queries to optimise performance, etc) you should use the
|
||||
merge request template called "Database Changes". This template contains a
|
||||
checklist of steps to follow to make sure the changes are up to snuff.
|
||||
|
||||
To use the checklist, create a new merge request and click on the "Choose a
|
||||
template" dropdown, then click "Database Changes".
|
||||
|
||||
An example of this checklist can be found at
|
||||
https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/12463.
|
||||
|
||||
The source code of the checklist can be found in at
|
||||
https://gitlab.com/gitlab-org/gitlab-ce/blob/master/.gitlab/merge_request_templates/Database%20Changes.md
|
|
@ -0,0 +1,20 @@
|
|||
# Hash Indexes
|
||||
|
||||
Both PostgreSQL and MySQL support hash indexes besides the regular btree
|
||||
indexes. Hash indexes however are to be avoided at all costs. While they may
|
||||
_sometimes_ provide better performance the cost of rehashing can be very high.
|
||||
More importantly: at least until PostgreSQL 10.0 hash indexes are not
|
||||
WAL-logged, meaning they are not replicated to any replicas. From the PostgreSQL
|
||||
documentation:
|
||||
|
||||
> Hash index operations are not presently WAL-logged, so hash indexes might need
|
||||
> to be rebuilt with REINDEX after a database crash if there were unwritten
|
||||
> changes. Also, changes to hash indexes are not replicated over streaming or
|
||||
> file-based replication after the initial base backup, so they give wrong
|
||||
> answers to queries that subsequently use them. For these reasons, hash index
|
||||
> use is presently discouraged.
|
||||
|
||||
RuboCop is configured to register an offence when it detects the use of a hash
|
||||
index.
|
||||
|
||||
Instead of using hash indexes you should use regular btree indexes.
|
|
@ -0,0 +1,127 @@
|
|||
# Ordering Table Columns
|
||||
|
||||
Similar to C structures the space of a table is influenced by the order of
|
||||
columns. This is because the size of columns is aligned depending on the type of
|
||||
the column. Take the following column order for example:
|
||||
|
||||
* id (integer, 4 bytes)
|
||||
* name (text, variable)
|
||||
* user_id (integer, 4 bytes)
|
||||
|
||||
Integers are aligned to the word size. This means that on a 64 bit platform the
|
||||
actual size of each column would be: 8 bytes, variable, 8 bytes. This means that
|
||||
each row will require at least 16 bytes for the two integers, and a variable
|
||||
amount for the text field. If a table has a few rows this is not an issue, but
|
||||
once you start storing millions of rows you can save space by using a different
|
||||
order. For the above example a more ideal column order would be the following:
|
||||
|
||||
* id (integer, 4 bytes)
|
||||
* user_id (integer, 4 bytes)
|
||||
* name (text, variable)
|
||||
|
||||
In this setup the `id` and `user_id` columns can be packed together, which means
|
||||
we only need 8 bytes to store _both_ of them. This in turn each row will require
|
||||
8 bytes less of space.
|
||||
|
||||
For GitLab we require that columns of new tables are ordered based to use the
|
||||
least amount of space. An easy way of doing this is to order them based on the
|
||||
type size in descending order with variable sizes (string and text columns for
|
||||
example) at the end.
|
||||
|
||||
## Type Sizes
|
||||
|
||||
While the PostgreSQL docuemntation
|
||||
(https://www.postgresql.org/docs/current/static/datatype.html) contains plenty
|
||||
of information we will list the sizes of common types here so it's easier to
|
||||
look them up. Here "word" refers to the word size, which is 4 bytes for a 32
|
||||
bits platform and 8 bytes for a 64 bits platform.
|
||||
|
||||
| Type | Size | Aligned To |
|
||||
|:-----------------|:-------------------------------------|:-----------|
|
||||
| smallint | 2 bytes | 1 word |
|
||||
| integer | 4 bytes | 1 word |
|
||||
| bigint | 8 bytes | 8 bytes |
|
||||
| real | 4 bytes | 1 word |
|
||||
| double precision | 8 bytes | 8 bytes |
|
||||
| boolean | 1 byte | not needed |
|
||||
| text / string | variable, 1 byte plus the data | 1 word |
|
||||
| bytea | variable, 1 or 4 bytes plus the data | 1 word |
|
||||
| timestamp | 8 bytes | 8 bytes |
|
||||
| timestamptz | 8 bytes | 8 bytes |
|
||||
| date | 4 bytes | 1 word |
|
||||
|
||||
A "variable" size means the actual size depends on the value being stored. If
|
||||
PostgreSQL determines this can be embedded directly into a row it may do so, but
|
||||
for very large values it will store the data externally and store a pointer (of
|
||||
1 word in size) in the column. Because of this variable sized columns should
|
||||
always be at the end of a table.
|
||||
|
||||
## Real Example
|
||||
|
||||
Let's use the "events" table as an example, which currently has the following
|
||||
layout:
|
||||
|
||||
| Column | Type | Size |
|
||||
|:------------|:----------------------------|:---------|
|
||||
| id | integer | 4 bytes |
|
||||
| target_type | character varying | variable |
|
||||
| target_id | integer | 4 bytes |
|
||||
| title | character varying | variable |
|
||||
| data | text | variable |
|
||||
| project_id | integer | 4 bytes |
|
||||
| created_at | timestamp without time zone | 8 bytes |
|
||||
| updated_at | timestamp without time zone | 8 bytes |
|
||||
| action | integer | 4 bytes |
|
||||
| author_id | integer | 4 bytes |
|
||||
|
||||
After adding padding to align the columns this would translate to columns being
|
||||
divided into fixed size chunks as follows:
|
||||
|
||||
| Chunk Size | Columns |
|
||||
|:-----------|:------------------|
|
||||
| 8 bytes | id |
|
||||
| variable | target_type |
|
||||
| 8 bytes | target_id |
|
||||
| variable | title |
|
||||
| variable | data |
|
||||
| 8 bytes | project_id |
|
||||
| 8 bytes | created_at |
|
||||
| 8 bytes | updated_at |
|
||||
| 8 bytes | action, author_id |
|
||||
|
||||
This means that excluding the variable sized data we need at least 48 bytes per
|
||||
row.
|
||||
|
||||
We can optimise this by using the following column order instead:
|
||||
|
||||
| Column | Type | Size |
|
||||
|:------------|:----------------------------|:---------|
|
||||
| created_at | timestamp without time zone | 8 bytes |
|
||||
| updated_at | timestamp without time zone | 8 bytes |
|
||||
| id | integer | 4 bytes |
|
||||
| target_id | integer | 4 bytes |
|
||||
| project_id | integer | 4 bytes |
|
||||
| action | integer | 4 bytes |
|
||||
| author_id | integer | 4 bytes |
|
||||
| target_type | character varying | variable |
|
||||
| title | character varying | variable |
|
||||
| data | text | variable |
|
||||
|
||||
This would produce the following chunks:
|
||||
|
||||
| Chunk Size | Columns |
|
||||
|:-----------|:-------------------|
|
||||
| 8 bytes | created_at |
|
||||
| 8 bytes | updated_at |
|
||||
| 8 bytes | id, target_id |
|
||||
| 8 bytes | project_id, action |
|
||||
| 8 bytes | author_id |
|
||||
| variable | target_type |
|
||||
| variable | title |
|
||||
| variable | data |
|
||||
|
||||
Here we only need 40 bytes per row excluding the variable sized data. 8 bytes
|
||||
being saved may not sound like much, but for tables as large as the "events"
|
||||
table it does begin to matter. For example, when storing 80 000 000 rows this
|
||||
translates to a space saving of at least 610 MB: all by just changing the order
|
||||
of a few columns.
|
|
@ -1,7 +1,8 @@
|
|||
# Serializing Data
|
||||
|
||||
**Summary:** don't store serialized data in the database, use separate columns
|
||||
and/or tables instead.
|
||||
and/or tables instead. This includes storing of comma separated values as a
|
||||
string.
|
||||
|
||||
Rails makes it possible to store serialized data in JSON, YAML or other formats.
|
||||
Such a field can be defined as follows:
|
||||
|
|
|
@ -216,4 +216,30 @@ exact same results. This also means there's no need to add an index on
|
|||
`created_at` to ensure consistent performance as `id` is already indexed by
|
||||
default.
|
||||
|
||||
## Use WHERE EXISTS instead of WHERE IN
|
||||
|
||||
While `WHERE IN` and `WHERE EXISTS` can be used to produce the same data it is
|
||||
recommended to use `WHERE EXISTS` whenever possible. While in many cases
|
||||
PostgreSQL can optimise `WHERE IN` quite well there are also many cases where
|
||||
`WHERE EXISTS` will perform (much) better.
|
||||
|
||||
In Rails you have to use this by creating SQL fragments:
|
||||
|
||||
```ruby
|
||||
Project.where('EXISTS (?)', User.select(1).where('projects.creator_id = users.id AND users.foo = X'))
|
||||
```
|
||||
|
||||
This would then produce a query along the lines of the following:
|
||||
|
||||
```sql
|
||||
SELECT *
|
||||
FROM projects
|
||||
WHERE EXISTS (
|
||||
SELECT 1
|
||||
FROM users
|
||||
WHERE projects.creator_id = users.id
|
||||
AND users.foo = X
|
||||
)
|
||||
```
|
||||
|
||||
[gin-index]: http://www.postgresql.org/docs/current/static/gin.html
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
# Verifying Database Capabilities
|
||||
|
||||
Sometimes certain bits of code may only work on a certain database and/or
|
||||
version. While we try to avoid such code as much as possible sometimes it is
|
||||
necessary to add database (version) specific behaviour.
|
||||
|
||||
To facilitate this we have the following methods that you can use:
|
||||
|
||||
* `Gitlab::Database.postgresql?`: returns `true` if PostgreSQL is being used
|
||||
* `Gitlab::Database.mysql?`: returns `true` if MySQL is being used
|
||||
* `Gitlab::Database.version`: returns the PostgreSQL version number as a string
|
||||
in the format `X.Y.Z`. This method does not work for MySQL
|
||||
|
||||
This allows you to write code such as:
|
||||
|
||||
```ruby
|
||||
if Gitlab::Database.postgresql?
|
||||
if Gitlab::Database.version.to_f >= 9.6
|
||||
run_really_fast_query
|
||||
else
|
||||
run_fast_query
|
||||
end
|
||||
else
|
||||
run_query
|
||||
end
|
||||
```
|
|
@ -1,5 +1,9 @@
|
|||
# How to create a project in GitLab
|
||||
|
||||
>**Notes:**
|
||||
- For a list of words that are not allowed to be used as project names see the
|
||||
[reserved names][reserved].
|
||||
|
||||
1. In your dashboard, click the green **New project** button or use the plus
|
||||
icon in the upper right corner of the navigation bar.
|
||||
|
||||
|
@ -26,3 +30,4 @@
|
|||
1. Click **Create project**.
|
||||
|
||||
[import it]: ../workflow/importing/README.md
|
||||
[reserved]: ../user/reserved_names.md
|
||||
|
|
|
@ -80,7 +80,7 @@ Make sure you have the right version of Git installed
|
|||
# Install Git
|
||||
sudo apt-get install -y git-core
|
||||
|
||||
# Make sure Git is version 2.8.4 or higher
|
||||
# Make sure Git is version 2.13.0 or higher
|
||||
git --version
|
||||
|
||||
Is the system packaged Git too old? Remove it and compile from source.
|
||||
|
|
|
@ -57,6 +57,10 @@ By doing so:
|
|||
|
||||
## Create a new group
|
||||
|
||||
> **Notes:**
|
||||
- For a list of words that are not allowed to be used as group names see the
|
||||
[reserved names][reserved].
|
||||
|
||||
You can create a group in GitLab from:
|
||||
|
||||
1. The Groups page: expand the left menu, click **Groups**, and click the green button **New group**:
|
||||
|
@ -213,4 +217,5 @@ for the group (GitLab admins only, available in [GitLab Enterprise Edition Start
|
|||
- **Pipelines quota**: keep track of the [pipeline quota](../admin_area/settings/continuous_integration.md) for the group
|
||||
|
||||
[permissions]: ../permissions.md#permissions
|
||||
[ee]: https://about.gitlab.com/products/
|
||||
[ee]: https://about.gitlab.com/products/
|
||||
[reserved]: ../reserved_names.md
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue