Skip to content
Snippets Groups Projects
Commit 04845fde authored by Stan Hu's avatar Stan Hu
Browse files

Merge remote-tracking branch 'origin/master' into dev/master

parents 27580720 e19f2531
No related branches found
No related tags found
No related merge requests found
Showing
with 472 additions and 104 deletions
Loading
Loading
@@ -6,6 +6,7 @@ app/controllers/projects/approvers_controller.rb
app/controllers/projects/protected_branches/merge_access_levels_controller.rb
app/controllers/projects/protected_branches/push_access_levels_controller.rb
app/controllers/projects/protected_tags/create_access_levels_controller.rb
app/helpers/system_note_helper.rb
app/policies/project_policy.rb
app/models/concerns/relative_positioning.rb
app/workers/stuck_merge_jobs_worker.rb
Loading
Loading
# Test Plan
<!-- This issue outlines testing activities related to a particular issue or epic.
[Here is an example test plan](https://gitlab.com/gitlab-org/gitlab-ce/issues/50353)
This and other comments should be removed as you write the plan -->
## Introduction
<!-- Briefly outline what is being tested
Mention the issue(s) this test plan is related to -->
## Scope
<!-- State any limits on aspects of the feature being tested
Outline the types of data to be included
Outline the types of tests to be performed (functional, security, performance,
database, automated, etc) -->
## ACC Matrix
<!-- Use the matrix below as a template to identify the Attributes, Components, and
Capabilities relevant to the scope of this test plan. Add or remove Attributes
and Components as required and list Capabilities in the next section
Attributes (columns) are adverbs or adjectives that describe (at a high level)
the qualities testing is meant to ensure Components have.
Components (rows) are nouns that define major parts of the product being tested.
Capabilities link Attributes and Components. They are what your product needs to
do to make sure a Component fulfills an Attribute
For more information see the [Google Testing Blog article about the 10 minute
test plan](https://testing.googleblog.com/2011/09/10-minute-test-plan.html) and
[this wiki page from an open-source tool that implements the ACC
model](https://code.google.com/archive/p/test-analytics/wikis/AccExplained.wiki). -->
| | Simple | Secure | Responsive | Obvious | Stable |
|------------|:------:|:------:|:----------:|:-------:|:------:|
| Admin | | | | | |
| Groups | | | | | |
| Project | | | | | |
| Repository | | | | | |
| Issues | | | | | |
| MRs | | | | | |
| CI/CD | | | | | |
| Ops | | | | | |
| Registry | | | | | |
| Wiki | | | | | |
| Snippets | | | | | |
| Settings | | | | | |
| Tracking | | | | | |
| API | | | | | |
## Capabilities
<!-- Use the ACC matrix above to help you identify Capabilities at each relevant
intersection of Components and Attributes.
Some features might be simple enough that they only involve one Component, while
more complex features could involve multiple or even all.
Example (from https://gitlab.com/gitlab-org/gitlab-ce/issues/50353):
* Respository is
* Simple
* It's easy to select the desired file template
* It doesn't require unnecessary actions to save the change
* It's easy to undo the change after selecting a template
* Responsive
* The list of templates can be restricted to allow a user to find a specific template among many
* Once a template is selected the file content updates quickly and smoothly
-->
## Test Plan
<!-- If the scope is small enough you may not need to write a list of tests to
perform. It might be enough to use the Capabilities to guide your testing.
If the feature is more complex, especially if it involves multiple Components,
briefly outline a set of tests here. When identifying tests to perform be sure
to consider risk. Note inherent/known levels of risk so that testing can focus
on high risk areas first.
New end-to-end and integration tests (Selenium and API) should be added to the
[Test Coverage sheet](https://docs.google.com/spreadsheets/d/1RlLfXGboJmNVIPP9jgFV5sXIACGfdcFq1tKd7xnlb74/)
Please note if automated tests already exist.
When adding new automated tests, please keep [testing levels](https://docs.gitlab.com/ce/development/testing_guide/testing_levels.html)
in mind.
-->
/label ~Quality
\ No newline at end of file
Loading
Loading
@@ -70,14 +70,15 @@ linters:
enabled: false
 
RuboCop:
enabled: false
enabled: true
# These cops are incredibly noisy when it comes to HAML templates, so we
# ignore them.
ignored_cops:
- Lint/BlockAlignment
- Lint/EndAlignment
- Layout/BlockAlignment
- Layout/EndAlignment
- Lint/Void
- Metrics/LineLength
- Naming/FileName
- Style/AlignParameters
- Style/BlockNesting
- Style/ElseAlignment
Loading
Loading
@@ -91,6 +92,52 @@ linters:
- Style/TrailingWhitespace
- Style/WhileUntilModifier
 
# These cops should eventually get enabled
- Cop/LineBreakAfterGuardClauses
- Cop/LineBreakAroundConditionalBlock
- Cop/ProjectPathHelper
- GitlabSecurity/PublicSend
- Layout/LeadingCommentSpace
- Layout/SpaceAfterColon
- Layout/SpaceAfterComma
- Layout/SpaceAroundOperators
- Layout/SpaceBeforeBlockBraces
- Layout/SpaceBeforeComma
- Layout/SpaceBeforeFirstArg
- Layout/SpaceInsideArrayLiteralBrackets
- Layout/SpaceInsideHashLiteralBraces
- Layout/SpaceInsideStringInterpolation
- Layout/TrailingBlankLines
- Lint/BooleanSymbol
- Lint/LiteralInInterpolation
- Lint/ParenthesesAsGroupedExpression
- Lint/RedundantWithIndex
- Lint/Syntax
- Lint/UselessAssignment
- Metrics/BlockNesting
- Naming/VariableName
- Performance/RedundantMatch
- Performance/StringReplacement
- Rails/Presence
- Rails/RequestReferer
- Style/AndOr
- Style/ColonMethodCall
- Style/ConditionalAssignment
- Style/HashSyntax
- Style/IdenticalConditionalBranches
- Style/NegatedIf
- Style/NestedTernaryOperator
- Style/Not
- Style/ParenthesesAroundCondition
- Style/RedundantParentheses
- Style/SelfAssignment
- Style/Semicolon
- Style/TernaryParentheses
- Style/TrailingCommaInHashLiteral
- Style/UnlessElse
- Style/WordArray
- Style/ZeroLengthPredicate
RubyComments:
enabled: true
 
Loading
Loading
Loading
Loading
@@ -314,7 +314,7 @@ This [documentation](doc/development/contributing/merge_request_workflow.md) has
 
## Definition of done
 
This [documentation](doc/development/contributing/merge_request_workflow.md)) has been moved.
This [documentation](doc/development/contributing/merge_request_workflow.md) has been moved.
 
 
## Style guides
Loading
Loading
Loading
Loading
@@ -126,7 +126,7 @@ GEM
numerizer (~> 0.1.1)
chunky_png (1.3.5)
citrus (3.0.2)
coderay (1.1.1)
coderay (1.1.2)
coercible (1.0.0)
descendants_tracker (~> 0.0.1)
commonmarker (0.17.8)
Loading
Loading
@@ -495,7 +495,7 @@ GEM
memoist (0.16.0)
memoizable (0.4.2)
thread_safe (~> 0.3, >= 0.3.1)
method_source (0.8.2)
method_source (0.9.0)
mime-types (3.1)
mime-types-data (~> 3.2015)
mime-types-data (3.2016.0521)
Loading
Loading
@@ -638,12 +638,11 @@ GEM
unparser
procto (0.0.3)
prometheus-client-mmap (0.9.4)
pry (0.10.4)
pry (0.11.3)
coderay (~> 1.1.0)
method_source (~> 0.8.1)
slop (~> 3.4)
pry-byebug (3.4.2)
byebug (~> 9.0)
method_source (~> 0.9.0)
pry-byebug (3.4.3)
byebug (>= 9.0, < 9.1)
pry (~> 0.10)
pry-rails (0.3.5)
pry (>= 0.9.10)
Loading
Loading
@@ -751,7 +750,7 @@ GEM
retriable (3.1.2)
rinku (2.0.0)
rotp (2.1.2)
rouge (3.2.0)
rouge (3.2.1)
rqrcode (0.7.0)
chunky_png
rqrcode-rails3 (0.1.7)
Loading
Loading
@@ -817,7 +816,7 @@ GEM
rubyzip (1.2.1)
rufus-scheduler (3.4.0)
et-orbi (~> 1.0)
rugged (0.27.2)
rugged (0.27.4)
safe_yaml (1.0.4)
sanitize (4.6.6)
crass (~> 1.0.2)
Loading
Loading
@@ -878,7 +877,6 @@ GEM
simplecov-html (~> 0.10.0)
simplecov-html (0.10.0)
slack-notifier (1.5.1)
slop (3.6.0)
spring (2.0.1)
activesupport (>= 4.2)
spring-commands-rspec (1.0.4)
Loading
Loading
Loading
Loading
@@ -146,13 +146,7 @@ export default {
staged: false,
prevPath: '',
moved: false,
lastCommit: Object.assign(state.entries[file.path].lastCommit, {
id: lastCommit.commit.id,
url: lastCommit.commit_path,
message: lastCommit.commit.message,
author: lastCommit.commit.author_name,
updatedAt: lastCommit.commit.authored_date,
}),
lastCommitSha: lastCommit.commit.id,
});
 
if (prevPath) {
Loading
Loading
<script>
import _ from 'underscore';
import CiIcon from '~/vue_shared/components/ci_icon.vue';
import { sprintf, __ } from '../../locale';
export default {
components: {
CiIcon,
},
props: {
deploymentStatus: {
type: Object,
required: true,
},
},
computed: {
environment() {
let environmentText;
switch (this.deploymentStatus.status) {
case 'latest':
environmentText = sprintf(
__('This job is the most recent deployment to %{link}.'),
{ link: this.environmentLink },
false,
);
break;
case 'out_of_date':
if (this.hasLastDeployment) {
environmentText = sprintf(
__(
'This job is an out-of-date deployment to %{environmentLink}. View the most recent deployment %{deploymentLink}.',
),
{
environmentLink: this.environmentLink,
deploymentLink: this.deploymentLink,
},
false,
);
} else {
environmentText = sprintf(
__('This job is an out-of-date deployment to %{environmentLink}.'),
{ environmentLink: this.environmentLink },
false,
);
}
break;
case 'failed':
environmentText = sprintf(
__('The deployment of this job to %{environmentLink} did not succeed.'),
{ environmentLink: this.environmentLink },
false,
);
break;
case 'creating':
if (this.hasLastDeployment) {
environmentText = sprintf(
__(
'This job is creating a deployment to %{environmentLink} and will overwrite the last %{deploymentLink}.',
),
{
environmentLink: this.environmentLink,
deploymentLink: this.deploymentLink,
},
false,
);
} else {
environmentText = sprintf(
__('This job is creating a deployment to %{environmentLink}.'),
{ environmentLink: this.environmentLink },
false,
);
}
break;
default:
break;
}
return environmentText;
},
environmentLink() {
return sprintf(
'%{startLink}%{name}%{endLink}',
{
startLink: `<a href="${this.deploymentStatus.environment.path}">`,
name: _.escape(this.deploymentStatus.environment.name),
endLink: '</a>',
},
false,
);
},
deploymentLink() {
return sprintf(
'%{startLink}%{name}%{endLink}',
{
startLink: `<a href="${this.lastDeployment.path}">`,
name: _.escape(this.lastDeployment.name),
endLink: '</a>',
},
false,
);
},
hasLastDeployment() {
return this.deploymentStatus.environment.last_deployment;
},
lastDeployment() {
return this.deploymentStatus.environment.last_deployment;
},
},
};
</script>
<template>
<div class="prepend-top-default js-environment-container">
<div class="environment-information">
<ci-icon :status="deploymentStatus.icon" />
<p v-html="environment"></p>
</div>
</div>
</template>
import initMilestonesShow from '~/pages/milestones/shared/init_milestones_show';
import initDeleteMilestoneModal from '~/pages/milestones/shared/delete_milestone_modal_init';
import Milestone from '~/milestone';
 
document.addEventListener('DOMContentLoaded', () => {
initMilestonesShow();
initDeleteMilestoneModal();
Milestone.initDeprecationMessage();
});
Loading
Loading
@@ -40,8 +40,8 @@
if (this.issueCount === 0 && this.mergeRequestCount === 0) {
return sprintf(
s__(`Milestones|
You’re about to permanently delete the milestone %{milestoneTitle} from this project.
%{milestoneTitle} is not currently used in any issues or merge requests.`),
You’re about to permanently delete the milestone %{milestoneTitle}.
This milestone is not currently used in any issues or merge requests.`),
{
milestoneTitle,
},
Loading
Loading
@@ -51,7 +51,7 @@ You’re about to permanently delete the milestone %{milestoneTitle} from this p
 
return sprintf(
s__(`Milestones|
You’re about to permanently delete the milestone %{milestoneTitle} from this project and remove it from %{issuesWithCount} and %{mergeRequestsWithCount}.
You’re about to permanently delete the milestone %{milestoneTitle} and remove it from %{issuesWithCount} and %{mergeRequestsWithCount}.
Once deleted, it cannot be undone or recovered.`),
{
milestoneTitle,
Loading
Loading
Loading
Loading
@@ -3,15 +3,22 @@ import createFlash from '~/flash';
import GfmAutoComplete from '~/gfm_auto_complete';
import EmojiMenu from './emoji_menu';
 
const defaultStatusEmoji = 'speech_balloon';
document.addEventListener('DOMContentLoaded', () => {
const toggleEmojiMenuButtonSelector = '.js-toggle-emoji-menu';
const toggleEmojiMenuButton = document.querySelector(toggleEmojiMenuButtonSelector);
const statusEmojiField = document.getElementById('js-status-emoji-field');
const statusMessageField = document.getElementById('js-status-message-field');
const findNoEmojiPlaceholder = () => document.getElementById('js-no-emoji-placeholder');
 
const toggleNoEmojiPlaceholder = (isVisible) => {
const placeholderElement = document.getElementById('js-no-emoji-placeholder');
placeholderElement.classList.toggle('hidden', !isVisible);
};
const findStatusEmoji = () => toggleEmojiMenuButton.querySelector('gl-emoji');
const removeStatusEmoji = () => {
const statusEmoji = toggleEmojiMenuButton.querySelector('gl-emoji');
const statusEmoji = findStatusEmoji();
if (statusEmoji) {
statusEmoji.remove();
}
Loading
Loading
@@ -19,7 +26,7 @@ document.addEventListener('DOMContentLoaded', () => {
 
const selectEmojiCallback = (emoji, emojiTag) => {
statusEmojiField.value = emoji;
findNoEmojiPlaceholder().classList.add('hidden');
toggleNoEmojiPlaceholder(false);
removeStatusEmoji();
toggleEmojiMenuButton.innerHTML += emojiTag;
};
Loading
Loading
@@ -29,7 +36,7 @@ document.addEventListener('DOMContentLoaded', () => {
statusEmojiField.value = '';
statusMessageField.value = '';
removeStatusEmoji();
findNoEmojiPlaceholder().classList.remove('hidden');
toggleNoEmojiPlaceholder(true);
});
 
const emojiAutocomplete = new GfmAutoComplete();
Loading
Loading
@@ -44,6 +51,23 @@ document.addEventListener('DOMContentLoaded', () => {
selectEmojiCallback,
);
emojiMenu.bindEvents();
const defaultEmojiTag = Emoji.glEmojiTag(defaultStatusEmoji);
statusMessageField.addEventListener('input', () => {
const hasStatusMessage = statusMessageField.value.trim() !== '';
const statusEmoji = findStatusEmoji();
if (hasStatusMessage && statusEmoji) {
return;
}
if (hasStatusMessage) {
toggleNoEmojiPlaceholder(false);
toggleEmojiMenuButton.innerHTML += defaultEmojiTag;
} else if (statusEmoji.dataset.name === defaultStatusEmoji) {
toggleNoEmojiPlaceholder(true);
removeStatusEmoji();
}
});
})
.catch(() => createFlash('Failed to load emoji list!'));
});
<script>
import Icon from '~/vue_shared/components/icon.vue';
import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate.vue';
import timeagoMixin from '../../vue_shared/mixins/timeago';
import tooltip from '../../vue_shared/directives/tooltip';
import LoadingButton from '../../vue_shared/components/loading_button.vue';
Loading
Loading
@@ -16,6 +17,7 @@ export default {
MemoryUsage,
StatusIcon,
Icon,
TooltipOnTruncate,
},
directives: {
tooltip,
Loading
Loading
@@ -88,14 +90,20 @@ export default {
<span>
Deployed to
</span>
<a
:href="deployment.url"
target="_blank"
rel="noopener noreferrer nofollow"
class="deploy-link js-deploy-meta"
<tooltip-on-truncate
:title="deployment.name"
truncate-target="child"
class="deploy-link label-truncate"
>
{{ deployment.name }}
</a>
<a
:href="deployment.url"
target="_blank"
rel="noopener noreferrer nofollow"
class="js-deploy-meta"
>
{{ deployment.name }}
</a>
</tooltip-on-truncate>
</template>
<span
v-tooltip
Loading
Loading
<script>
import tooltip from '~/vue_shared/directives/tooltip';
import { n__ } from '~/locale';
import _ from 'underscore';
import { n__, s__, sprintf } from '~/locale';
import { mergeUrlParams, webIDEUrl } from '~/lib/utils/url_utility';
import Icon from '~/vue_shared/components/icon.vue';
import clipboardButton from '~/vue_shared/components/clipboard_button.vue';
import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate.vue';
 
export default {
name: 'MRWidgetHeader',
directives: {
tooltip,
},
components: {
Icon,
clipboardButton,
TooltipOnTruncate,
},
props: {
mr: {
Loading
Loading
@@ -24,8 +23,12 @@ export default {
shouldShowCommitsBehindText() {
return this.mr.divergedCommitsCount > 0;
},
commitsText() {
return n__('%d commit behind', '%d commits behind', this.mr.divergedCommitsCount);
commitsBehindText() {
return sprintf(s__('mrWidget|The source branch is %{commitsBehindLinkStart}%{commitsBehind}%{commitsBehindLinkEnd} the target branch'), {
commitsBehindLinkStart: `<a href="${_.escape(this.mr.targetBranchPath)}">`,
commitsBehind: n__('%d commit behind', '%d commits behind', this.mr.divergedCommitsCount),
commitsBehindLinkEnd: '</a>',
}, false);
},
branchNameClipboardData() {
// This supports code in app/assets/javascripts/copy_to_clipboard.js that
Loading
Loading
@@ -36,12 +39,6 @@ export default {
gfm: `\`${this.mr.sourceBranch}\``,
});
},
isSourceBranchLong() {
return this.isBranchTitleLong(this.mr.sourceBranch);
},
isTargetBranchLong() {
return this.isBranchTitleLong(this.mr.targetBranch);
},
webIdePath() {
return mergeUrlParams({
target_project: this.mr.sourceProjectFullPath !== this.mr.targetProjectFullPath ?
Loading
Loading
@@ -49,11 +46,6 @@ export default {
}, webIDEUrl(`/${this.mr.sourceProjectFullPath}/merge_requests/${this.mr.iid}`));
},
},
methods: {
isBranchTitleLong(branchTitle) {
return branchTitle.length > 32;
},
},
};
</script>
<template>
Loading
Loading
@@ -65,30 +57,21 @@ export default {
<div class="normal">
<strong>
{{ s__("mrWidget|Request to merge") }}
<span
:class="{ 'label-truncated': isSourceBranchLong }"
:title="isSourceBranchLong ? mr.sourceBranch : ''"
:v-tooltip="isSourceBranchLong"
class="label-branch js-source-branch"
data-placement="bottom"
<tooltip-on-truncate
:title="mr.sourceBranch"
truncate-target="child"
class="label-branch label-truncate js-source-branch"
v-html="mr.sourceBranchLink"
>
</span>
<clipboard-button
/><clipboard-button
:text="branchNameClipboardData"
:title="__('Copy branch name to clipboard')"
css-class="btn-default btn-transparent btn-clipboard"
/>
{{ s__("mrWidget|into") }}
<span
:v-tooltip="isTargetBranchLong"
:class="{ 'label-truncatedtooltip': isTargetBranchLong }"
:title="isTargetBranchLong ? mr.targetBranch : ''"
class="label-branch"
data-placement="bottom"
<tooltip-on-truncate
:title="mr.targetBranch"
truncate-target="child"
class="label-branch label-truncate"
>
<a
:href="mr.targetBranchTreePath"
Loading
Loading
@@ -96,15 +79,13 @@ export default {
>
{{ mr.targetBranch }}
</a>
</span>
</tooltip-on-truncate>
</strong>
<div
v-if="shouldShowCommitsBehindText"
class="diverged-commits-count"
v-html="commitsBehindText"
>
<span class="monospace">{{ mr.sourceBranch }}</span>
is {{ commitsText }}
<span class="monospace">{{ mr.targetBranch }}</span>
</div>
</div>
 
Loading
Loading
Loading
Loading
@@ -3,6 +3,7 @@
import PipelineStage from '~/pipelines/components/stage.vue';
import CiIcon from '~/vue_shared/components/ci_icon.vue';
import Icon from '~/vue_shared/components/icon.vue';
import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate.vue';
 
export default {
name: 'MRWidgetPipeline',
Loading
Loading
@@ -10,6 +11,7 @@ export default {
PipelineStage,
CiIcon,
Icon,
TooltipOnTruncate,
},
props: {
pipeline: {
Loading
Loading
@@ -30,6 +32,10 @@ export default {
type: String,
required: false,
},
sourceBranch: {
type: String,
required: false,
},
},
computed: {
hasPipeline() {
Loading
Loading
@@ -107,11 +113,12 @@ export default {
>
{{ pipeline.commit.short_id }}</a>
on
<span
class="label-branch"
<tooltip-on-truncate
:title="sourceBranch"
truncate-target="child"
class="label-branch label-truncate"
v-html="sourceBranchLink"
>
</span>
/>
</template>
</div>
<div
Loading
Loading
Loading
Loading
@@ -254,6 +254,7 @@ export default {
:pipeline="mr.pipeline"
:ci-status="mr.ciStatus"
:has-ci="mr.hasCI"
:source-branch="mr.sourceBranch"
:source-branch-link="mr.sourceBranchLink"
/>
<deployment
Loading
Loading
Loading
Loading
@@ -71,7 +71,11 @@ export default {
},
methods: {
getPercent(count) {
return roundOffFloat((count / this.totalCount) * 100, 1);
const percent = roundOffFloat((count / this.totalCount) * 100, 1);
if (percent > 0 && percent < 1) {
return '< 1';
}
return percent;
},
barStyle(percent) {
return `width: ${percent}%;`;
Loading
Loading
<script>
import _ from 'underscore';
import tooltip from '../directives/tooltip';
export default {
directives: {
tooltip,
},
props: {
title: {
type: String,
required: false,
default: '',
},
placement: {
type: String,
required: false,
default: 'top',
},
truncateTarget: {
type: [String, Function],
required: false,
default: '',
},
},
data() {
return {
showTooltip: false,
};
},
mounted() {
const target = this.selectTarget();
if (target && target.scrollWidth > target.offsetWidth) {
this.showTooltip = true;
}
},
methods: {
selectTarget() {
if (_.isFunction(this.truncateTarget)) {
return this.truncateTarget(this.$el);
} else if (this.truncateTarget === 'child') {
return this.$el.childNodes[0];
}
return this.$el;
},
},
};
</script>
<template>
<span
v-tooltip
v-if="showTooltip"
:title="title"
:data-placement="placement"
class="js-show-tooltip"
>
<slot></slot>
</span>
<span
v-else
>
<slot></slot>
</span>
</template>
Loading
Loading
@@ -195,6 +195,7 @@
.ci-widget-content {
display: flex;
align-items: center;
flex: 1;
}
}
 
Loading
Loading
@@ -222,6 +223,7 @@
 
.normal {
flex: 1;
flex-basis: auto;
}
 
.capitalize {
Loading
Loading
@@ -235,22 +237,23 @@
font-weight: normal;
overflow: hidden;
word-break: break-all;
}
 
&.label-truncated {
position: relative;
display: inline-block;
width: 250px;
margin-bottom: -3px;
white-space: nowrap;
text-overflow: clip;
line-height: 14px;
&::after {
position: absolute;
content: '...';
right: 0;
font-family: $regular-font;
background-color: $gray-light;
.deploy-link,
.label-branch {
&.label-truncate {
// NOTE: This selector targets its children because some of the HTML comes from
// 'source_branch_link'. Once this external HTML is no longer used, we could
// simplify this.
> a,
> span {
display: inline-block;
max-width: 12.5em;
margin-bottom: -3px;
white-space: nowrap;
text-overflow: ellipsis;
line-height: 14px;
overflow: hidden;
}
}
}
Loading
Loading
@@ -582,7 +585,7 @@
 
@include media-breakpoint-down(md) {
flex-direction: column;
align-items: flex-start;
align-items: stretch;
 
.branch-actions {
margin-top: 16px;
Loading
Loading
@@ -593,13 +596,13 @@
.branch-actions {
align-self: center;
margin-left: $gl-padding;
white-space: nowrap;
}
}
}
 
.diverged-commits-count {
color: $gl-text-color-secondary;
font-size: 12px;
}
}
 
Loading
Loading
@@ -918,7 +921,7 @@
flex: 1;
flex-direction: row;
 
@include media-breakpoint-down(md) {
@include media-breakpoint-down(sm) {
flex-direction: column;
 
.stage-cell .stage-container {
Loading
Loading
Loading
Loading
@@ -2,8 +2,8 @@ class Groups::MilestonesController < Groups::ApplicationController
include MilestoneActions
 
before_action :group_projects
before_action :milestone, only: [:edit, :show, :update, :merge_requests, :participants, :labels]
before_action :authorize_admin_milestones!, only: [:edit, :new, :create, :update]
before_action :milestone, only: [:edit, :show, :update, :merge_requests, :participants, :labels, :destroy]
before_action :authorize_admin_milestones!, only: [:edit, :new, :create, :update, :destroy]
 
def index
respond_to do |format|
Loading
Loading
@@ -56,10 +56,21 @@ class Groups::MilestonesController < Groups::ApplicationController
redirect_to milestone_path
end
 
def destroy
return render_404 if @milestone.legacy_group_milestone?
Milestones::DestroyService.new(group, current_user).execute(@milestone)
respond_to do |format|
format.html { redirect_to group_milestones_path(group), status: :see_other }
format.js { head :ok }
end
end
private
 
def authorize_admin_milestones!
return render_404 unless can?(current_user, :admin_milestones, group)
return render_404 unless can?(current_user, :admin_milestone, group)
end
 
def milestone_params
Loading
Loading
Loading
Loading
@@ -5,6 +5,10 @@ module ImportHelper
false
end
 
def sanitize_project_name(name)
name.gsub(/[^\w\-]/, '-')
end
def import_project_target(owner, name)
namespace = current_user.can_create_group? ? owner : current_user.namespace_path
"#{namespace}/#{name}"
Loading
Loading
Loading
Loading
@@ -447,7 +447,7 @@ module ProjectsHelper
end
 
def project_permissions_panel_data(project)
data = {
{
currentSettings: project_permissions_settings(project),
canChangeVisibilityLevel: can_change_visibility_level?(project, current_user),
allowedVisibilityOptions: project_allowed_visibility_levels(project),
Loading
Loading
@@ -457,8 +457,10 @@ module ProjectsHelper
lfsAvailable: Gitlab.config.lfs.enabled,
lfsHelpPath: help_page_path('workflow/lfs/manage_large_binaries_with_git_lfs')
}
end
 
data.to_json.html_safe
def project_permissions_panel_data_json(project)
project_permissions_panel_data(project).to_json.html_safe
end
 
def project_allowed_visibility_levels(project)
Loading
Loading
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment