Commit 272b8dca authored by Shinya Maeda's avatar Shinya Maeda

Merge branch 'master' into per-project-pipeline-iid

parents 0e22b50d 50fda506
...@@ -8,8 +8,6 @@ engines: ...@@ -8,8 +8,6 @@ engines:
languages: languages:
- ruby - ruby
- javascript - javascript
exclude_paths:
- "lib/api/v3/*"
ratings: ratings:
paths: paths:
- Gemfile.lock - Gemfile.lock
......
{
"env": {
"browser": true,
"es6": true
},
"extends": [
"airbnb-base",
"plugin:vue/recommended"
],
"globals": {
"__webpack_public_path__": true,
"gl": false,
"gon": false,
"localStorage": false
},
"parserOptions": {
"parser": "babel-eslint"
},
"plugins": [
"filenames",
"import",
"html",
"promise"
],
"settings": {
"html/html-extensions": [".html", ".html.raw"],
"import/resolver": {
"webpack": {
"config": "./config/webpack.config.js"
}
}
},
"rules": {
"filenames/match-regex": [2, "^[a-z0-9_]+$"],
"import/no-commonjs": "error",
"no-multiple-empty-lines": ["error", { "max": 1 }],
"promise/catch-or-return": "error",
"no-underscore-dangle": ["error", { "allow": ["__", "_links"] }],
"no-mixed-operators": 0,
"space-before-function-paren": 0,
"curly": 0,
"arrow-parens": 0,
"vue/html-self-closing": [
"error",
{
"html": {
"void": "always",
"normal": "never",
"component": "always"
},
"svg": "always",
"math": "always"
}
]
}
}
---
env:
browser: true
es6: true
extends:
- airbnb-base
- plugin:vue/recommended
globals:
__webpack_public_path__: true
gl: false
gon: false
localStorage: false
parserOptions:
parser: babel-eslint
plugins:
- filenames
- import
- html
- promise
settings:
html/html-extensions:
- ".html"
- ".html.raw"
import/resolver:
webpack:
config: "./config/webpack.config.js"
rules:
filenames/match-regex:
- error
- "^[a-z0-9_]+$"
import/no-commonjs: error
no-multiple-empty-lines:
- error
- max: 1
promise/catch-or-return: error
no-underscore-dangle:
- error
- allow:
- __
- _links
no-mixed-operators: off
vue/html-self-closing:
- error
- html:
void: always
normal: never
component: always
svg: always
math: always
## Conflicting rules with prettier:
space-before-function-paren: off
curly: off
arrow-parens: off
function-paren-newline: off
object-curly-newline: off
padded-blocks: off
# Disabled for now, to make the eslint 3 -> eslint 4 update smoother
## Indent rule. We are using the old for now: https://eslint.org/docs/user-guide/migrating-to-4.0.0#indent-rewrite
indent: off
indent-legacy:
- error
- 2
- SwitchCase: 1
VariableDeclarator: 1
outerIIFEBody: 1
FunctionDeclaration:
parameters: 1
body: 1
FunctionExpression:
parameters: 1
body: 1
## Destructuring: https://eslint.org/docs/rules/prefer-destructuring
prefer-destructuring: off
## no-restricted-globals: https://eslint.org/docs/rules/no-restricted-globals
no-restricted-globals: off
## no-multi-assign: https://eslint.org/docs/rules/no-multi-assign
no-multi-assign: off
...@@ -64,6 +64,7 @@ eslint-report.html ...@@ -64,6 +64,7 @@ eslint-report.html
/tags /tags
/tmp/* /tmp/*
/vendor/bundle/* /vendor/bundle/*
/vendor/gitaly-ruby
/builds* /builds*
/shared/* /shared/*
/.gitlab_workhorse_secret /.gitlab_workhorse_secret
......
...@@ -591,7 +591,7 @@ ee_compat_check: ...@@ -591,7 +591,7 @@ ee_compat_check:
except: except:
- master - master
- tags - tags
- /^[\d-]+-stable(-ee)?/ - /[\d-]+-stable(-ee)?/
- /^security-/ - /^security-/
- branches@gitlab-org/gitlab-ee - branches@gitlab-org/gitlab-ee
- branches@gitlab/gitlab-ee - branches@gitlab/gitlab-ee
......
...@@ -173,7 +173,6 @@ Lint/UriEscapeUnescape: ...@@ -173,7 +173,6 @@ Lint/UriEscapeUnescape:
- 'spec/requests/api/files_spec.rb' - 'spec/requests/api/files_spec.rb'
- 'spec/requests/api/internal_spec.rb' - 'spec/requests/api/internal_spec.rb'
- 'spec/requests/api/issues_spec.rb' - 'spec/requests/api/issues_spec.rb'
- 'spec/requests/api/v3/issues_spec.rb'
# Offense count: 1 # Offense count: 1
# Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns. # Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
...@@ -333,8 +332,6 @@ RSpec/ScatteredSetup: ...@@ -333,8 +332,6 @@ RSpec/ScatteredSetup:
- 'spec/lib/gitlab/bitbucket_import/importer_spec.rb' - 'spec/lib/gitlab/bitbucket_import/importer_spec.rb'
- 'spec/lib/gitlab/git/env_spec.rb' - 'spec/lib/gitlab/git/env_spec.rb'
- 'spec/requests/api/jobs_spec.rb' - 'spec/requests/api/jobs_spec.rb'
- 'spec/requests/api/v3/builds_spec.rb'
- 'spec/requests/api/v3/projects_spec.rb'
- 'spec/services/projects/create_service_spec.rb' - 'spec/services/projects/create_service_spec.rb'
# Offense count: 1 # Offense count: 1
...@@ -618,7 +615,6 @@ Style/OrAssignment: ...@@ -618,7 +615,6 @@ Style/OrAssignment:
Exclude: Exclude:
- 'app/models/concerns/token_authenticatable.rb' - 'app/models/concerns/token_authenticatable.rb'
- 'lib/api/commit_statuses.rb' - 'lib/api/commit_statuses.rb'
- 'lib/api/v3/members.rb'
- 'lib/gitlab/project_transfer.rb' - 'lib/gitlab/project_transfer.rb'
# Offense count: 50 # Offense count: 50
...@@ -781,7 +777,6 @@ Style/TernaryParentheses: ...@@ -781,7 +777,6 @@ Style/TernaryParentheses:
- 'app/finders/projects_finder.rb' - 'app/finders/projects_finder.rb'
- 'app/helpers/namespaces_helper.rb' - 'app/helpers/namespaces_helper.rb'
- 'features/support/capybara.rb' - 'features/support/capybara.rb'
- 'lib/api/v3/projects.rb'
- 'lib/gitlab/ci/build/artifacts/metadata/entry.rb' - 'lib/gitlab/ci/build/artifacts/metadata/entry.rb'
- 'spec/requests/api/pipeline_schedules_spec.rb' - 'spec/requests/api/pipeline_schedules_spec.rb'
- 'spec/support/capybara.rb' - 'spec/support/capybara.rb'
......
...@@ -2,6 +2,20 @@ ...@@ -2,6 +2,20 @@
documentation](doc/development/changelog.md) for instructions on adding your own documentation](doc/development/changelog.md) for instructions on adding your own
entry. entry.
## 10.8.3 (2018-05-30)
### Fixed (4 changes)
- Replace Gitlab::REVISION with Gitlab.revision and handle installations without a .git directory. !19125
- Fix encoding of branch names on compare and new merge request page. !19143
- Fix remote mirror database inconsistencies when upgrading from EE to CE. !19196
- Fix local storage not being cleared after creating a new issue.
### Performance (1 change)
- Memoize Gitlab::Database.version.
## 10.8.2 (2018-05-28) ## 10.8.2 (2018-05-28)
### Security (3 changes) ### Security (3 changes)
......
...@@ -28,7 +28,7 @@ gem 'mysql2', '~> 0.4.10', group: :mysql ...@@ -28,7 +28,7 @@ gem 'mysql2', '~> 0.4.10', group: :mysql
gem 'pg', '~> 0.18.2', group: :postgres gem 'pg', '~> 0.18.2', group: :postgres
gem 'rugged', '~> 0.27' gem 'rugged', '~> 0.27'
gem 'grape-route-helpers', '~> 2.1.0' gem 'grape-path-helpers', '~> 1.0'
gem 'faraday', '~> 0.12' gem 'faraday', '~> 0.12'
...@@ -144,6 +144,9 @@ gem 'truncato', '~> 0.7.9' ...@@ -144,6 +144,9 @@ gem 'truncato', '~> 0.7.9'
gem 'bootstrap_form', '~> 2.7.0' gem 'bootstrap_form', '~> 2.7.0'
gem 'nokogiri', '~> 1.8.2' gem 'nokogiri', '~> 1.8.2'
# Calendar rendering
gem 'icalendar'
# Diffs # Diffs
gem 'diffy', '~> 3.1.0' gem 'diffy', '~> 3.1.0'
...@@ -219,7 +222,7 @@ gem 'asana', '~> 0.6.0' ...@@ -219,7 +222,7 @@ gem 'asana', '~> 0.6.0'
gem 'ruby-fogbugz', '~> 0.2.1' gem 'ruby-fogbugz', '~> 0.2.1'
# Kubernetes integration # Kubernetes integration
gem 'kubeclient', '~> 3.0' gem 'kubeclient', '~> 3.1.0'
# Sanitize user input # Sanitize user input
gem 'sanitize', '~> 2.0' gem 'sanitize', '~> 2.0'
...@@ -320,7 +323,7 @@ group :development, :test do ...@@ -320,7 +323,7 @@ group :development, :test do
gem 'pry-byebug', '~> 3.4.1', platform: :mri gem 'pry-byebug', '~> 3.4.1', platform: :mri
gem 'pry-rails', '~> 0.3.4' gem 'pry-rails', '~> 0.3.4'
gem 'awesome_print', '~> 1.2.0', require: false gem 'awesome_print', require: false
gem 'fuubar', '~> 2.2.0' gem 'fuubar', '~> 2.2.0'
gem 'database_cleaner', '~> 1.5.0' gem 'database_cleaner', '~> 1.5.0'
......
...@@ -69,7 +69,7 @@ GEM ...@@ -69,7 +69,7 @@ GEM
attr_encrypted (3.1.0) attr_encrypted (3.1.0)
encryptor (~> 3.0.0) encryptor (~> 3.0.0)
attr_required (1.0.0) attr_required (1.0.0)
awesome_print (1.2.0) awesome_print (1.8.0)
axiom-types (0.1.1) axiom-types (0.1.1)
descendants_tracker (~> 0.0.4) descendants_tracker (~> 0.0.4)
ice_nine (~> 0.11.0) ice_nine (~> 0.11.0)
...@@ -168,7 +168,7 @@ GEM ...@@ -168,7 +168,7 @@ GEM
diff-lcs (1.3) diff-lcs (1.3)
diffy (3.1.0) diffy (3.1.0)
docile (1.1.5) docile (1.1.5)
domain_name (0.5.20170404) domain_name (0.5.20180417)
unf (>= 0.0.5, < 1.0.0) unf (>= 0.0.5, < 1.0.0)
doorkeeper (4.3.2) doorkeeper (4.3.2)
railties (>= 4.2) railties (>= 4.2)
...@@ -348,7 +348,7 @@ GEM ...@@ -348,7 +348,7 @@ GEM
signet (~> 0.7) signet (~> 0.7)
gpgme (2.0.13) gpgme (2.0.13)
mini_portile2 (~> 2.1) mini_portile2 (~> 2.1)
grape (1.0.2) grape (1.0.3)
activesupport activesupport
builder builder
mustermann-grape (~> 1.0.0) mustermann-grape (~> 1.0.0)
...@@ -358,10 +358,10 @@ GEM ...@@ -358,10 +358,10 @@ GEM
grape-entity (0.7.1) grape-entity (0.7.1)
activesupport (>= 4.0) activesupport (>= 4.0)
multi_json (>= 1.3.2) multi_json (>= 1.3.2)
grape-route-helpers (2.1.0) grape-path-helpers (1.0.1)
activesupport activesupport (~> 4)
grape (>= 0.16.0) grape (~> 1.0)
rake rake (~> 12)
grape_logging (1.7.0) grape_logging (1.7.0)
grape grape
grpc (1.11.0) grpc (1.11.0)
...@@ -410,6 +410,7 @@ GEM ...@@ -410,6 +410,7 @@ GEM
httpclient (2.8.3) httpclient (2.8.3)
i18n (0.9.5) i18n (0.9.5)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
icalendar (2.4.1)
ice_nine (0.11.2) ice_nine (0.11.2)
influxdb (0.2.3) influxdb (0.2.3)
cause cause
...@@ -446,9 +447,9 @@ GEM ...@@ -446,9 +447,9 @@ GEM
knapsack (1.16.0) knapsack (1.16.0)
rake rake
timecop (>= 0.1.0) timecop (>= 0.1.0)
kubeclient (3.0.0) kubeclient (3.1.0)
http (~> 2.2.2) http (~> 2.2.2)
recursive-open-struct (~> 1.0.4) recursive-open-struct (~> 1.0, >= 1.0.4)
rest-client (~> 2.0) rest-client (~> 2.0)
launchy (2.4.3) launchy (2.4.3)
addressable (~> 2.3) addressable (~> 2.3)
...@@ -698,7 +699,7 @@ GEM ...@@ -698,7 +699,7 @@ GEM
re2 (1.1.1) re2 (1.1.1)
recaptcha (3.0.0) recaptcha (3.0.0)
json json
recursive-open-struct (1.0.5) recursive-open-struct (1.1.0)
redcarpet (3.4.0) redcarpet (3.4.0)
redis (3.3.5) redis (3.3.5)
redis-actionpack (5.0.2) redis-actionpack (5.0.2)
...@@ -977,7 +978,7 @@ DEPENDENCIES ...@@ -977,7 +978,7 @@ DEPENDENCIES
asciidoctor-plantuml (= 0.0.8) asciidoctor-plantuml (= 0.0.8)
asset_sync (~> 2.4) asset_sync (~> 2.4)
attr_encrypted (~> 3.1.0) attr_encrypted (~> 3.1.0)
awesome_print (~> 1.2.0) awesome_print
babosa (~> 1.0.2) babosa (~> 1.0.2)
base32 (~> 0.3.0) base32 (~> 0.3.0)
batch-loader (~> 1.2.1) batch-loader (~> 1.2.1)
...@@ -1049,7 +1050,7 @@ DEPENDENCIES ...@@ -1049,7 +1050,7 @@ DEPENDENCIES
gpgme gpgme
grape (~> 1.0) grape (~> 1.0)
grape-entity (~> 0.7.1) grape-entity (~> 0.7.1)
grape-route-helpers (~> 2.1.0) grape-path-helpers (~> 1.0)
grape_logging (~> 1.7) grape_logging (~> 1.7)
grpc (~> 1.11.0) grpc (~> 1.11.0)
haml_lint (~> 0.26.0) haml_lint (~> 0.26.0)
...@@ -1060,6 +1061,7 @@ DEPENDENCIES ...@@ -1060,6 +1061,7 @@ DEPENDENCIES
html-pipeline (~> 2.7.1) html-pipeline (~> 2.7.1)
html2text html2text
httparty (~> 0.13.3) httparty (~> 0.13.3)
icalendar
influxdb (~> 0.2) influxdb (~> 0.2)
jira-ruby (~> 1.4) jira-ruby (~> 1.4)
jquery-atwho-rails (~> 1.3.2) jquery-atwho-rails (~> 1.3.2)
...@@ -1067,7 +1069,7 @@ DEPENDENCIES ...@@ -1067,7 +1069,7 @@ DEPENDENCIES
jwt (~> 1.5.6) jwt (~> 1.5.6)
kaminari (~> 1.0) kaminari (~> 1.0)
knapsack (~> 1.16) knapsack (~> 1.16)
kubeclient (~> 3.0) kubeclient (~> 3.1.0)
letter_opener_web (~> 1.3.0) letter_opener_web (~> 1.3.0)
license_finder (~> 3.1) license_finder (~> 3.1)
licensee (~> 8.9) licensee (~> 8.9)
......
...@@ -41,10 +41,10 @@ gl.issueBoards.ModalEmptyState = Vue.extend({ ...@@ -41,10 +41,10 @@ gl.issueBoards.ModalEmptyState = Vue.extend({
template: ` template: `
<section class="empty-state"> <section class="empty-state">
<div class="row"> <div class="row">
<div class="col-xs-12 col-sm-6 order-sm-last"> <div class="col-12 col-md-6 order-md-last">
<aside class="svg-content"><img :src="emptyStateSvg"/></aside> <aside class="svg-content"><img :src="emptyStateSvg"/></aside>
</div> </div>
<div class="col-xs-12 col-sm-6 order-sm-first"> <div class="col-12 col-md-6 order-md-first">
<div class="text-content"> <div class="text-content">
<h4>{{ contents.title }}</h4> <h4>{{ contents.title }}</h4>
<p v-html="contents.content"></p> <p v-html="contents.content"></p>
......
/* global ListIssue */
import Vue from 'vue'; import Vue from 'vue';
import bp from '../../../breakpoints'; import bp from '../../../breakpoints';
import ModalStore from '../../stores/modal_store'; import ModalStore from '../../stores/modal_store';
...@@ -56,8 +54,11 @@ gl.issueBoards.ModalList = Vue.extend({ ...@@ -56,8 +54,11 @@ gl.issueBoards.ModalList = Vue.extend({
scrollHandler() { scrollHandler() {
const currentPage = Math.floor(this.issues.length / this.perPage); const currentPage = Math.floor(this.issues.length / this.perPage);
if ((this.scrollTop() > this.scrollHeight() - 100) && !this.loadingNewPage if (
&& currentPage === this.page) { this.scrollTop() > this.scrollHeight() - 100 &&
!this.loadingNewPage &&
currentPage === this.page
) {
this.loadingNewPage = true; this.loadingNewPage = true;
this.page += 1; this.page += 1;
} }
......
<script> <script>
/* global ListIssue */ import $ from 'jquery';
import _ from 'underscore';
import eventHub from '../eventhub';
import loadingIcon from '../../vue_shared/components/loading_icon.vue';
import Api from '../../api';
import $ from 'jquery'; export default {
import _ from 'underscore'; name: 'BoardProjectSelect',
import eventHub from '../eventhub'; components: {
import loadingIcon from '../../vue_shared/components/loading_icon.vue'; loadingIcon,
import Api from '../../api'; },
props: {
export default { groupId: {
name: 'BoardProjectSelect', type: Number,
components: { required: true,
loadingIcon, default: 0,
},
props: {
groupId: {
type: Number,
required: true,
default: 0,
},
}, },
data() { },
return { data() {
loading: true, return {
selectedProject: {}, loading: true,
}; selectedProject: {},
};
},
computed: {
selectedProjectName() {
return this.selectedProject.name || 'Select a project';
}, },
computed: { },
selectedProjectName() { mounted() {
return this.selectedProject.name || 'Select a project'; $(this.$refs.projectsDropdown).glDropdown({
filterable: true,
filterRemote: true,
search: {
fields: ['name_with_namespace'],
}, },
}, clicked: ({ $el, e }) => {
mounted() { e.preventDefault();
$(this.$refs.projectsDropdown).glDropdown({ this.selectedProject = {
filterable: true, id: $el.data('project-id'),
filterRemote: true, name: $el.data('project-name'),
search: { };
fields: ['name_with_namespace'], eventHub.$emit('setSelectedProject', this.selectedProject);
}, },
clicked: ({ $el, e }) => { selectable: true,
e.preventDefault(); data: (term, callback) => {
this.selectedProject = { this.loading = true;
id: $el.data('project-id'), return Api.groupProjects(this.groupId, term, projects => {
name: $el.data('project-name'), this.loading = false;
}; callback(projects);
eventHub.$emit('setSelectedProject', this.selectedProject); });
}, },
selectable: true, renderRow(project) {
data: (term, callback) => { return `
this.loading = true;
return Api.groupProjects(this.groupId, term, (projects) => {
this.loading = false;
callback(projects);
});
},
renderRow(project) {
return `
<li> <li>
<a href='#' class='dropdown-menu-link' data-project-id="${project.id}" data-project-name="${project.name}"> <a href='#' class='dropdown-menu-link' data-project-id="${project.id}" data-project-name="${project.name}">
${_.escape(project.name)} ${_.escape(project.name)}
</a> </a>
</li> </li>
`; `;
}, },
text: project => project.name, text: project => project.name,
}); });
}, },
}; };
</script> </script>
<template> <template>
......
...@@ -126,7 +126,6 @@ export default { ...@@ -126,7 +126,6 @@ export default {
</div> </div>
<form <form
v-if="!isCompact" v-if="!isCompact"
class="form-horizontal"
@submit.prevent.stop="commitChanges" @submit.prevent.stop="commitChanges"
ref="formEl" ref="formEl"
> >
......
<script>
import Icon from '~/vue_shared/components/icon.vue';
export default {
components: {
Icon,
},
props: {
file: {
type: Object,
required: true,
},
},
computed: {
showButtons() {
return this.file.permalink;
},
},
};
</script>
<template>
<div
v-if="showButtons"
class="pull-right ide-btn-group"
>
<a
:href="file.permalink"
target="_blank"
:title="s__('IDE|Open in file view')"
rel="noopener noreferrer"
>
<span class="vertical-align-middle">Open in file view</span>
<icon
name="external-link"
css-classes="vertical-align-middle space-right"
:size="16"
/>
</a>
</div>
</template>
<script>
import { __ } from '~/locale';
import tooltip from '~/vue_shared/directives/tooltip';
import Icon from '~/vue_shared/components/icon.vue';
export default {
components: {
Icon,
},
directives: {
tooltip,
},
props: {
file: {
type: Object,
required: true,
},
},
computed: {
showButtons() {
return (
this.file.rawPath || this.file.blamePath || this.file.commitsPath || this.file.permalink
);
},
rawDownloadButtonLabel() {
return this.file.binary ? __('Download') : __('Raw');
},
},
};
</script>
<template>
<div
v-if="showButtons"
class="float-right ide-btn-group"
>
<a
v-tooltip
v-if="!file.binary"
:href="file.blamePath"
:title="__('Blame')"
class="btn btn-sm btn-transparent blame"
>
<icon
name="blame"
:size="16"
/>
</a>
<a
v-tooltip
:href="file.commitsPath"
:title="__('History')"
class="btn btn-sm btn-transparent history"
>
<icon
name="history"
:size="16"
/>
</a>
<a
v-tooltip
:href="file.permalink"
:title="__('Permalink')"
class="btn btn-sm btn-transparent permalink"
>
<icon
name="link"
:size="16"
/>
</a>
<a
v-tooltip
:href="file.rawPath"
target="_blank"
class="btn btn-sm btn-transparent prepend-left-10 raw"
rel="noopener noreferrer"
:title="rawDownloadButtonLabel">
<icon
name="download"
:size="16"
/>
</a>
</div>
</template>
...@@ -6,12 +6,12 @@ import ContentViewer from '~/vue_shared/components/content_viewer/content_viewer ...@@ -6,12 +6,12 @@ import ContentViewer from '~/vue_shared/components/content_viewer/content_viewer
import { activityBarViews, viewerTypes } from '../constants'; import { activityBarViews, viewerTypes } from '../constants';
import monacoLoader from '../monaco_loader'; import monacoLoader from '../monaco_loader';
import Editor from '../lib/editor'; import Editor from '../lib/editor';
import IdeFileButtons from './ide_file_buttons.vue'; import ExternalLink from './external_link.vue';
export default { export default {
components: { components: {
ContentViewer, ContentViewer,
IdeFileButtons, ExternalLink,
}, },
props: { props: {
file: { file: {
...@@ -224,7 +224,7 @@ export default { ...@@ -224,7 +224,7 @@ export default {
</a> </a>
</li> </li>
</ul> </ul>
<ide-file-buttons <external-link
:file="file" :file="file"
/> />
</div> </div>
......
/* global monaco */
import Disposable from './disposable'; import Disposable from './disposable';
import eventHub from '../../eventhub'; import eventHub from '../../eventhub';
......
...@@ -84,11 +84,11 @@ export const getFileData = ({ state, commit, dispatch }, { path, makeFileActive ...@@ -84,11 +84,11 @@ export const getFileData = ({ state, commit, dispatch }, { path, makeFileActive
}); });
}; };
export const setFileMrChange = ({ state, commit }, { file, mrChange }) => { export const setFileMrChange = ({ commit }, { file, mrChange }) => {
commit(types.SET_FILE_MERGE_REQUEST_CHANGE, { file, mrChange }); commit(types.SET_FILE_MERGE_REQUEST_CHANGE, { file, mrChange });
}; };
export const getRawFileData = ({ state, commit, dispatch }, { path, baseSha }) => { export const getRawFileData = ({ state, commit }, { path, baseSha }) => {
const file = state.entries[path]; const file = state.entries[path];
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
service service
...@@ -156,7 +156,7 @@ export const setEditorPosition = ({ getters, commit }, { editorRow, editorColumn ...@@ -156,7 +156,7 @@ export const setEditorPosition = ({ getters, commit }, { editorRow, editorColumn
} }
}; };
export const setFileViewMode = ({ state, commit }, { file, viewMode }) => { export const setFileViewMode = ({ commit }, { file, viewMode }) => {
commit(types.SET_FILE_VIEWMODE, { file, viewMode }); commit(types.SET_FILE_VIEWMODE, { file, viewMode });
}; };
......
...@@ -3,7 +3,7 @@ import service from '../../services'; ...@@ -3,7 +3,7 @@ import service from '../../services';
import * as types from '../mutation_types'; import * as types from '../mutation_types';
export const getMergeRequestData = ( export const getMergeRequestData = (
{ commit, state, dispatch }, { commit, state },
{ projectId, mergeRequestId, force = false } = {}, { projectId, mergeRequestId, force = false } = {},
) => ) =>
new Promise((resolve, reject) => { new Promise((resolve, reject) => {
...@@ -32,7 +32,7 @@ export const getMergeRequestData = ( ...@@ -32,7 +32,7 @@ export const getMergeRequestData = (
}); });
export const getMergeRequestChanges = ( export const getMergeRequestChanges = (
{ commit, state, dispatch }, { commit, state },
{ projectId, mergeRequestId, force = false } = {}, { projectId, mergeRequestId, force = false } = {},
) => ) =>
new Promise((resolve, reject) => { new Promise((resolve, reject) => {
...@@ -58,7 +58,7 @@ export const getMergeRequestChanges = ( ...@@ -58,7 +58,7 @@ export const getMergeRequestChanges = (
}); });
export const getMergeRequestVersions = ( export const getMergeRequestVersions = (
{ commit, state, dispatch }, { commit, state },
{ projectId, mergeRequestId, force = false } = {}, { projectId, mergeRequestId, force = false } = {},
) => ) =>
new Promise((resolve, reject) => { new Promise((resolve, reject) => {
......
...@@ -7,10 +7,7 @@ import Poll from '../../../lib/utils/poll'; ...@@ -7,10 +7,7 @@ import Poll from '../../../lib/utils/poll';
let eTagPoll; let eTagPoll;
export const getProjectData = ( export const getProjectData = ({ commit, state }, { namespace, projectId, force = false } = {}) =>
{ commit, state, dispatch },
{ namespace, projectId, force = false } = {},
) =>
new Promise((resolve, reject) => { new Promise((resolve, reject) => {
if (!state.projects[`${namespace}/${projectId}`] || force) { if (!state.projects[`${namespace}/${projectId}`] || force) {
commit(types.TOGGLE_LOADING, { entry: state }); commit(types.TOGGLE_LOADING, { entry: state });
...@@ -40,10 +37,7 @@ export const getProjectData = ( ...@@ -40,10 +37,7 @@ export const getProjectData = (
} }
}); });
export const getBranchData = ( export const getBranchData = ({ commit, state }, { projectId, branchId, force = false } = {}) =>
{ commit, state, dispatch },
{ projectId, branchId, force = false } = {},
) =>
new Promise((resolve, reject) => { new Promise((resolve, reject) => {
if ( if (
typeof state.projects[`${projectId}`] === 'undefined' || typeof state.projects[`${projectId}`] === 'undefined' ||
...@@ -78,7 +72,7 @@ export const getBranchData = ( ...@@ -78,7 +72,7 @@ export const getBranchData = (
} }
}); });
export const refreshLastCommitData = ({ commit, state, dispatch }, { projectId, branchId } = {}) => export const refreshLastCommitData = ({ commit }, { projectId, branchId } = {}) =>
service service
.getBranchData(projectId, branchId) .getBranchData(projectId, branchId)
.then(({ data }) => { .then(({ data }) => {
...@@ -92,7 +86,7 @@ export const refreshLastCommitData = ({ commit, state, dispatch }, { projectId, ...@@ -92,7 +86,7 @@ export const refreshLastCommitData = ({ commit, state, dispatch }, { projectId,
flash(__('Error loading last commit.'), 'alert', document, null, false, true); flash(__('Error loading last commit.'), 'alert', document, null, false, true);
}); });
export const pollSuccessCallBack = ({ commit, state, dispatch }, { data }) => { export const pollSuccessCallBack = ({ commit, state }, { data }) => {
if (data.pipelines && data.pipelines.length) { if (data.pipelines && data.pipelines.length) {
const lastCommitHash = const lastCommitHash =
state.projects[state.currentProjectId].branches[state.currentBranchId].commit.id; state.projects[state.currentProjectId].branches[state.currentBranchId].commit.id;
......
...@@ -5,7 +5,7 @@ import * as types from '../mutation_types'; ...@@ -5,7 +5,7 @@ import * as types from '../mutation_types';
import { findEntry } from '../utils'; import { findEntry } from '../utils';
import FilesDecoratorWorker from '../workers/files_decorator_worker'; import FilesDecoratorWorker from '../workers/files_decorator_worker';
export const toggleTreeOpen = ({ commit, dispatch }, path) => { export const toggleTreeOpen = ({ commit }, path) => {
commit(types.TOGGLE_TREE_OPEN, path); commit(types.TOGGLE_TREE_OPEN, path);
}; };
...@@ -23,7 +23,7 @@ export const handleTreeEntryAction = ({ commit, dispatch }, row) => { ...@@ -23,7 +23,7 @@ export const handleTreeEntryAction = ({ commit, dispatch }, row) => {
} }
}; };
export const getLastCommitData = ({ state, commit, dispatch, getters }, tree = state) => { export const getLastCommitData = ({ state, commit, dispatch }, tree = state) => {
if (!tree || tree.lastCommitPath === null || !tree.lastCommitPath) return; if (!tree || tree.lastCommitPath === null || !tree.lastCommitPath) return;
service service
...@@ -49,7 +49,7 @@ export const getLastCommitData = ({ state, commit, dispatch, getters }, tree = s ...@@ -49,7 +49,7 @@ export const getLastCommitData = ({ state, commit, dispatch, getters }, tree = s
.catch(() => flash('Error fetching log data.', 'alert', document, null, false, true)); .catch(() => flash('Error fetching log data.', 'alert', document, null, false, true));
}; };
export const getFiles = ({ state, commit, dispatch }, { projectId, branchId } = {}) => export const getFiles = ({ state, commit }, { projectId, branchId } = {}) =>
new Promise((resolve, reject) => { new Promise((resolve, reject) => {
if (!state.trees[`${projectId}/${branchId}`]) { if (!state.trees[`${projectId}/${branchId}`]) {
const selectedProject = state.projects[projectId]; const selectedProject = state.projects[projectId];
......
...@@ -31,9 +31,9 @@ export const setLastCommitMessage = ({ rootState, commit }, data) => { ...@@ -31,9 +31,9 @@ export const setLastCommitMessage = ({ rootState, commit }, data) => {
const currentProject = rootState.projects[rootState.currentProjectId]; const currentProject = rootState.projects[rootState.currentProjectId];
const commitStats = data.stats const commitStats = data.stats
? sprintf(__('with %{additions} additions, %{deletions} deletions.'), { ? sprintf(__('with %{additions} additions, %{deletions} deletions.'), {
additions: data.stats.additions, // eslint-disable-line indent additions: data.stats.additions, // eslint-disable-line indent-legacy
deletions: data.stats.deletions, // eslint-disable-line indent deletions: data.stats.deletions, // eslint-disable-line indent-legacy
}) // eslint-disable-line indent }) // eslint-disable-line indent-legacy
: ''; : '';
const commitMsg = sprintf( const commitMsg = sprintf(
__('Your changes have been committed. Commit %{commitId} %{commitStats}'), __('Your changes have been committed. Commit %{commitId} %{commitStats}'),
...@@ -74,10 +74,7 @@ export const checkCommitStatus = ({ rootState }) => ...@@ -74,10 +74,7 @@ export const checkCommitStatus = ({ rootState }) =>
), ),
); );
export const updateFilesAfterCommit = ( export const updateFilesAfterCommit = ({ commit, dispatch, rootState }, { data }) => {
{ commit, dispatch, state, rootState, rootGetters },
{ data },
) => {
const selectedProject = rootState.projects[rootState.currentProjectId]; const selectedProject = rootState.projects[rootState.currentProjectId];
const lastCommit = { const lastCommit = {
commit_path: `${selectedProject.web_url}/commit/${data.id}`, commit_path: `${selectedProject.web_url}/commit/${data.id}`,
......
...@@ -30,7 +30,7 @@ export default class IssuableForm { ...@@ -30,7 +30,7 @@ export default class IssuableForm {
} }
this.initAutosave(); this.initAutosave();
this.form.on('submit:success', this.handleSubmit); this.form.on('submit', this.handleSubmit);
this.form.on('click', '.btn-cancel', this.resetAutosave); this.form.on('click', '.btn-cancel', this.resetAutosave);
this.initWip(); this.initWip();
......
...@@ -84,7 +84,7 @@ export default class Job { ...@@ -84,7 +84,7 @@ export default class Job {
If the browser does not support position sticky, it returns the position as static. If the browser does not support position sticky, it returns the position as static.
If the browser does support sticky, then we allow the browser to handle it, if not If the browser does support sticky, then we allow the browser to handle it, if not
then we use a polyfill then we use a polyfill
**/ */
if (this.$topBar.css('position') !== 'static') return; if (this.$topBar.css('position') !== 'static') return;
StickyFill.add(this.$topBar); StickyFill.add(this.$topBar);
......
/* global Build */
import Visibility from 'visibilityjs'; import Visibility from 'visibilityjs';
import Flash from '../flash'; import Flash from '../flash';
import Poll from '../lib/utils/poll'; import Poll from '../lib/utils/poll';
...@@ -50,7 +48,8 @@ export default class JobMediator { ...@@ -50,7 +48,8 @@ export default class JobMediator {
} }
getJob() { getJob() {
return this.service.getJob() return this.service
.getJob()
.then(response => this.successCallback(response)) .then(response => this.successCallback(response))
.catch(() => this.errorCallback()); .catch(() => this.errorCallback());
} }
......
...@@ -9,7 +9,7 @@ delete window.translations; ...@@ -9,7 +9,7 @@ delete window.translations;
Translates `text` Translates `text`
@param text The text to be translated @param text The text to be translated
@returns {String} The translated text @returns {String} The translated text
**/ */
const gettext = locale.gettext.bind(locale); const gettext = locale.gettext.bind(locale);
/** /**
...@@ -21,7 +21,7 @@ const gettext = locale.gettext.bind(locale); ...@@ -21,7 +21,7 @@ const gettext = locale.gettext.bind(locale);
@param pluralText Plural text to translate (eg. '%d days') @param pluralText Plural text to translate (eg. '%d days')
@param count Number to decide which translation to use (eg. 2) @param count Number to decide which translation to use (eg. 2)
@returns {String} Translated text with the number replaced (eg. '2 days') @returns {String} Translated text with the number replaced (eg. '2 days')
**/ */
const ngettext = (text, pluralText, count) => { const ngettext = (text, pluralText, count) => {
const translated = locale.ngettext(text, pluralText, count).replace(/%d/g, count).split('|'); const translated = locale.ngettext(text, pluralText, count).replace(/%d/g, count).split('|');
...@@ -38,7 +38,7 @@ const ngettext = (text, pluralText, count) => { ...@@ -38,7 +38,7 @@ const ngettext = (text, pluralText, count) => {
(eg. 'Context') (eg. 'Context')
@param key Is the dynamic variable you want to be translated @param key Is the dynamic variable you want to be translated
@returns {String} Translated context based text @returns {String} Translated context based text
**/ */
const pgettext = (keyOrContext, key) => { const pgettext = (keyOrContext, key) => {
const normalizedKey = key ? `${keyOrContext}|${key}` : keyOrContext; const normalizedKey = key ? `${keyOrContext}|${key}` : keyOrContext;
const translated = gettext(normalizedKey).split('|'); const translated = gettext(normalizedKey).split('|');
......
...@@ -10,7 +10,7 @@ import _ from 'underscore'; ...@@ -10,7 +10,7 @@ import _ from 'underscore';
@see https://ruby-doc.org/core-2.3.3/Kernel.html#method-i-sprintf @see https://ruby-doc.org/core-2.3.3/Kernel.html#method-i-sprintf
@see https://gitlab.com/gitlab-org/gitlab-ce/issues/37992 @see https://gitlab.com/gitlab-org/gitlab-ce/issues/37992
**/ */
export default (input, parameters, escapeParameters = true) => { export default (input, parameters, escapeParameters = true) => {
let output = input; let output = input;
......
...@@ -427,7 +427,7 @@ export default class MergeRequestTabs { ...@@ -427,7 +427,7 @@ export default class MergeRequestTabs {
If the browser does not support position sticky, it returns the position as static. If the browser does not support position sticky, it returns the position as static.
If the browser does support sticky, then we allow the browser to handle it, if not If the browser does support sticky, then we allow the browser to handle it, if not
then we default back to Bootstraps affix then we default back to Bootstraps affix
**/ */
if ($tabs.css('position') !== 'static') return; if ($tabs.css('position') !== 'static') return;
const $diffTabs = $('#diff-notes-app'); const $diffTabs = $('#diff-notes-app');
......
...@@ -12,20 +12,13 @@ import { isInViewport, scrollToElement } from '../../lib/utils/common_utils'; ...@@ -12,20 +12,13 @@ import { isInViewport, scrollToElement } from '../../lib/utils/common_utils';
let eTagPoll; let eTagPoll;
export const setNotesData = ({ commit }, data) => export const setNotesData = ({ commit }, data) => commit(types.SET_NOTES_DATA, data);
commit(types.SET_NOTES_DATA, data); export const setNoteableData = ({ commit }, data) => commit(types.SET_NOTEABLE_DATA, data);
export const setNoteableData = ({ commit }, data) => export const setUserData = ({ commit }, data) => commit(types.SET_USER_DATA, data);
commit(types.SET_NOTEABLE_DATA, data); export const setLastFetchedAt = ({ commit }, data) => commit(types.SET_LAST_FETCHED_AT, data);
export const setUserData = ({ commit }, data) => export const setInitialNotes = ({ commit }, data) => commit(types.SET_INITIAL_NOTES, data);
commit(types.SET_USER_DATA, data); export const setTargetNoteHash = ({ commit }, data) => commit(types.SET_TARGET_NOTE_HASH, data);
export const setLastFetchedAt = ({ commit }, data) => export const toggleDiscussion = ({ commit }, data) => commit(types.TOGGLE_DISCUSSION, data);
commit(types.SET_LAST_FETCHED_AT, data);
export const setInitialNotes = ({ commit }, data) =>
commit(types.SET_INITIAL_NOTES, data);
export const setTargetNoteHash = ({ commit }, data) =>
commit(types.SET_TARGET_NOTE_HASH, data);
export const toggleDiscussion = ({ commit }, data) =>
commit(types.TOGGLE_DISCUSSION, data);
export const fetchNotes = ({ commit }, path) => export const fetchNotes = ({ commit }, path) =>
service service
...@@ -69,20 +62,14 @@ export const createNewNote = ({ commit }, { endpoint, data }) => ...@@ -69,20 +62,14 @@ export const createNewNote = ({ commit }, { endpoint, data }) =>
return res; return res;
}); });
export const removePlaceholderNotes = ({ commit }) => export const removePlaceholderNotes = ({ commit }) => commit(types.REMOVE_PLACEHOLDER_NOTES);
commit(types.REMOVE_PLACEHOLDER_NOTES);
export const toggleResolveNote = ( export const toggleResolveNote = ({ commit }, { endpoint, isResolved, discussion }) =>
{ commit },
{ endpoint, isResolved, discussion },
) =>
service service
.toggleResolveNote(endpoint, isResolved) .toggleResolveNote(endpoint, isResolved)
.then(res => res.json()) .then(res => res.json())
.then(res => { .then(res => {
const mutationType = discussion const mutationType = discussion ? types.UPDATE_DISCUSSION : types.UPDATE_NOTE;
? types.UPDATE_DISCUSSION
: types.UPDATE_NOTE;
commit(mutationType, res); commit(mutationType, res);
}); });
...@@ -114,7 +101,7 @@ export const reopenIssue = ({ commit, dispatch, state }) => { ...@@ -114,7 +101,7 @@ export const reopenIssue = ({ commit, dispatch, state }) => {
export const toggleStateButtonLoading = ({ commit }, value) => export const toggleStateButtonLoading = ({ commit }, value) =>
commit(types.TOGGLE_STATE_BUTTON_LOADING, value); commit(types.TOGGLE_STATE_BUTTON_LOADING, value);
export const emitStateChangedEvent = ({ commit, getters }, data) => { export const emitStateChangedEvent = ({ getters }, data) => {
const event = new CustomEvent('issuable_vue_app:change', { const event = new CustomEvent('issuable_vue_app:change', {
detail: { detail: {
data, data,
...@@ -179,10 +166,7 @@ export const saveNote = ({ commit, dispatch }, noteData) => { ...@@ -179,10 +166,7 @@ export const saveNote = ({ commit, dispatch }, noteData) => {
loadAwardsHandler() loadAwardsHandler()
.then(awardsHandler => { .then(awardsHandler => {
awardsHandler.addAwardToEmojiBar( awardsHandler.addAwardToEmojiBar(votesBlock, commandsChanges.emoji_award);
votesBlock,
commandsChanges.emoji_award,
);
awardsHandler.scrollToAwards(); awardsHandler.scrollToAwards();
}) })
.catch(() => { .catch(() => {
...@@ -194,10 +178,7 @@ export const saveNote = ({ commit, dispatch }, noteData) => { ...@@ -194,10 +178,7 @@ export const saveNote = ({ commit, dispatch }, noteData) => {
}); });
} }
if ( if (commandsChanges.spend_time != null || commandsChanges.time_estimate != null) {
commandsChanges.spend_time != null ||
commandsChanges.time_estimate != null
) {
sidebarTimeTrackingEventHub.$emit('timeTrackingUpdated', res); sidebarTimeTrackingEventHub.$emit('timeTrackingUpdated', res);
} }
} }
...@@ -218,14 +199,8 @@ const pollSuccessCallBack = (resp, commit, state, getters) => { ...@@ -218,14 +199,8 @@ const pollSuccessCallBack = (resp, commit, state, getters) => {
resp.notes.forEach(note => { resp.notes.forEach(note => {
if (notesById[note.id]) { if (notesById[note.id]) {
commit(types.UPDATE_NOTE, note); commit(types.UPDATE_NOTE, note);
} else if ( } else if (note.type === constants.DISCUSSION_NOTE || note.type === constants.DIFF_NOTE) {
note.type === constants.DISCUSSION_NOTE || const discussion = utils.findNoteObjectById(state.notes, note.discussion_id);
note.type === constants.DIFF_NOTE
) {
const discussion = utils.findNoteObjectById(
state.notes,
note.discussion_id,
);
if (discussion) { if (discussion) {
commit(types.ADD_NEW_REPLY_TO_DISCUSSION, note); commit(types.ADD_NEW_REPLY_TO_DISCUSSION, note);
...@@ -249,11 +224,8 @@ export const poll = ({ commit, state, getters }) => { ...@@ -249,11 +224,8 @@ export const poll = ({ commit, state, getters }) => {
method: 'poll', method: 'poll',
data: state, data: state,
successCallback: resp => successCallback: resp =>
resp resp.json().then(data => pollSuccessCallBack(data, commit, state, getters)),
.json() errorCallback: () => Flash('Something went wrong while fetching latest comments.'),
.then(data => pollSuccessCallBack(data, commit, state, getters)),
errorCallback: () =>
Flash('Something went wrong while fetching latest comments.'),
}); });
if (!Visibility.hidden()) { if (!Visibility.hidden()) {
...@@ -292,14 +264,11 @@ export const fetchData = ({ commit, state, getters }) => { ...@@ -292,14 +264,11 @@ export const fetchData = ({ commit, state, getters }) => {
.catch(() => Flash('Something went wrong while fetching latest comments.')); .catch(() => Flash('Something went wrong while fetching latest comments.'));
}; };
export const toggleAward = ( export const toggleAward = ({ commit, getters }, { awardName, noteId }) => {
{ commit, state, getters, dispatch },
{ awardName, noteId },
) => {
commit(types.TOGGLE_AWARD, { awardName, note: getters.notesById[noteId] }); commit(types.TOGGLE_AWARD, { awardName, note: getters.notesById[noteId] });
}; };
export const toggleAwardRequest = ({ commit, getters, dispatch }, data) => { export const toggleAwardRequest = ({ dispatch }, data) => {
const { endpoint, awardName } = data; const { endpoint, awardName } = data;
return service return service
......
...@@ -59,7 +59,7 @@ export default { ...@@ -59,7 +59,7 @@ export default {
ref="form" ref="form"
:action="deleteWikiUrl" :action="deleteWikiUrl"
method="post" method="post"
class="form-horizontal js-requires-input" class="js-requires-input"
> >
<input <input
ref="method" ref="method"
......
...@@ -5,7 +5,7 @@ import $ from 'jquery'; ...@@ -5,7 +5,7 @@ import $ from 'jquery';
* *
* Toggling this checkbox adds/removes a `remember_me` parameter to the * Toggling this checkbox adds/removes a `remember_me` parameter to the
* login buttons' href, which is passed on to the omniauth callback. * login buttons' href, which is passed on to the omniauth callback.
**/ */
export default class OAuthRememberMe { export default class OAuthRememberMe {
constructor(opts = {}) { constructor(opts = {}) {
......
...@@ -79,12 +79,13 @@ export default { ...@@ -79,12 +79,13 @@ export default {
}; };
</script> </script>
<template> <template>
<div class="ci-job-dropdown-container dropdown"> <div class="ci-job-dropdown-container dropdown dropright">
<button <button
v-tooltip v-tooltip
type="button" type="button"
data-toggle="dropdown" data-toggle="dropdown"
data-container="body" data-container="body"
data-boundary="viewport"
class="dropdown-menu-toggle build-content" class="dropdown-menu-toggle build-content"
:title="tooltipText" :title="tooltipText"
> >
......
...@@ -37,7 +37,7 @@ const IGNORE_URLS = [ ...@@ -37,7 +37,7 @@ const IGNORE_URLS = [
/extensions\//i, /extensions\//i,
/^chrome:\/\//i, /^chrome:\/\//i,
// Other plugins // Other plugins
/127\.0\.0\.1:4001\/isrunning/i, // Cacaoweb /127\.0\.0\.1:4001\/isrunning/i, // Cacaoweb
/webappstoolbarba\.texthelp\.com\//i, /webappstoolbarba\.texthelp\.com\//i,
/metrics\.itunes\.apple\.com\.edgesuite\.net\//i, /metrics\.itunes\.apple\.com\.edgesuite\.net\//i,
]; ];
......
...@@ -7,9 +7,10 @@ Vue.use(VueResource); ...@@ -7,9 +7,10 @@ Vue.use(VueResource);
export const fetchRepos = ({ commit, state }) => { export const fetchRepos = ({ commit, state }) => {
commit(types.TOGGLE_MAIN_LOADING); commit(types.TOGGLE_MAIN_LOADING);
return Vue.http.get(state.endpoint) return Vue.http
.get(state.endpoint)
.then(res => res.json()) .then(res => res.json())
.then((response) => { .then(response => {
commit(types.TOGGLE_MAIN_LOADING); commit(types.TOGGLE_MAIN_LOADING);
commit(types.SET_REPOS_LIST, response); commit(types.SET_REPOS_LIST, response);
}); });
...@@ -18,19 +19,20 @@ export const fetchRepos = ({ commit, state }) => { ...@@ -18,19 +19,20 @@ export const fetchRepos = ({ commit, state }) => {
export const fetchList = ({ commit }, { repo, page }) => { export const fetchList = ({ commit }, { repo, page }) => {
commit(types.TOGGLE_REGISTRY_LIST_LOADING, repo); commit(types.TOGGLE_REGISTRY_LIST_LOADING, repo);
return Vue.http.get(repo.tagsPath, { params: { page } }) return Vue.http.get(repo.tagsPath, { params: { page } }).then(response => {
.then((response) => { const headers = response.headers;
const headers = response.headers;
return response.json().then((resp) => { return response.json().then(resp => {
commit(types.TOGGLE_REGISTRY_LIST_LOADING, repo); commit(types.TOGGLE_REGISTRY_LIST_LOADING, repo);
commit(types.SET_REGISTRY_LIST, { repo, resp, headers }); commit(types.SET_REGISTRY_LIST, { repo, resp, headers });
});
}); });
});
}; };
// eslint-disable-next-line no-unused-vars
export const deleteRepo = ({ commit }, repo) => Vue.http.delete(repo.destroyPath); export const deleteRepo = ({ commit }, repo) => Vue.http.delete(repo.destroyPath);
// eslint-disable-next-line no-unused-vars
export const deleteRegistry = ({ commit }, image) => Vue.http.delete(image.destroyPath); export const deleteRegistry = ({ commit }, image) => Vue.http.delete(image.destroyPath);
export const setMainEndpoint = ({ commit }, data) => commit(types.SET_MAIN_ENDPOINT, data); export const setMainEndpoint = ({ commit }, data) => commit(types.SET_MAIN_ENDPOINT, data);
......
...@@ -168,8 +168,8 @@ ...@@ -168,8 +168,8 @@
<a <a
:href="mr.mergeCommitPath" :href="mr.mergeCommitPath"
class="commit-sha js-mr-merged-commit-sha" class="commit-sha js-mr-merged-commit-sha"
v-text="mr.shortMergeCommitSha"
> >
{{ mr.shortMergeCommitSha }}
</a> </a>
<clipboard-button <clipboard-button
:title="__('Copy commit SHA to clipboard')" :title="__('Copy commit SHA to clipboard')"
......
/*
The squash-before-merge button is EE only, but it's located right in the middle
of the readyToMerge state component template.
If we didn't declare this component in CE, we'd need to maintain a separate copy
of the readyToMergeState template in EE, which is pretty big and likely to change.
Instead, in CE, we declare the component, but it's hidden and is configured to do nothing.
In EE, the configuration extends this object to add a functioning squash-before-merge
button.
*/
<script> <script>
export default {}; import Icon from '~/vue_shared/components/icon.vue';
import eventHub from '~/vue_merge_request_widget/event_hub';
import tooltip from '~/vue_shared/directives/tooltip';
export default {
components: {
Icon,
},
directives: {
tooltip,
},
props: {
mr: {
type: Object,
required: true,
},
isMergeButtonDisabled: {
type: Boolean,
required: true,
},
},
data() {
return {
squashBeforeMerge: this.mr.squash,
};
},
methods: {
updateSquashModel() {
eventHub.$emit('MRWidgetUpdateSquash', this.squashBeforeMerge);
},
},
};
</script> </script>
<template>
<div class="accept-control inline">
<label class="merge-param-checkbox">
<input
type="checkbox"
name="squash"
class="qa-squash-checkbox"
:disabled="isMergeButtonDisabled"
v-model="squashBeforeMerge"
@change="updateSquashModel"
/>
{{ __('Squash commits') }}
</label>
<a
:href="mr.squashBeforeMergeHelpPath"
data-title="About this feature"
data-placement="bottom"
target="_blank"
rel="noopener noreferrer nofollow"
data-container="body"
v-tooltip
>
<icon
name="question-o"
/>
</a>
</div>
</template>
...@@ -6,11 +6,13 @@ import MergeRequest from '../../../merge_request'; ...@@ -6,11 +6,13 @@ import MergeRequest from '../../../merge_request';
import Flash from '../../../flash'; import Flash from '../../../flash';
import statusIcon from '../mr_widget_status_icon.vue'; import statusIcon from '../mr_widget_status_icon.vue';
import eventHub from '../../event_hub'; import eventHub from '../../event_hub';
import SquashBeforeMerge from './mr_widget_squash_before_merge.vue';
export default { export default {
name: 'ReadyToMerge', name: 'ReadyToMerge',
components: { components: {
statusIcon, statusIcon,
'squash-before-merge': SquashBeforeMerge,
}, },
props: { props: {
mr: { type: Object, required: true }, mr: { type: Object, required: true },
...@@ -101,6 +103,12 @@ export default { ...@@ -101,6 +103,12 @@ export default {
return enableSquashBeforeMerge && commitsCount > 1; return enableSquashBeforeMerge && commitsCount > 1;
}, },
}, },
created() {
eventHub.$on('MRWidgetUpdateSquash', this.handleUpdateSquash);
},
beforeDestroy() {
eventHub.$off('MRWidgetUpdateSquash', this.handleUpdateSquash);
},
methods: { methods: {
shouldShowMergeControls() { shouldShowMergeControls() {
return this.mr.isMergeAllowed || this.shouldShowMergeWhenPipelineSucceedsText; return this.mr.isMergeAllowed || this.shouldShowMergeWhenPipelineSucceedsText;
...@@ -128,13 +136,9 @@ export default { ...@@ -128,13 +136,9 @@ export default {
commit_message: this.commitMessage, commit_message: this.commitMessage,
merge_when_pipeline_succeeds: this.setToMergeWhenPipelineSucceeds, merge_when_pipeline_succeeds: this.setToMergeWhenPipelineSucceeds,
should_remove_source_branch: this.removeSourceBranch === true, should_remove_source_branch: this.removeSourceBranch === true,
squash: this.mr.squash,
}; };
// Only truthy in EE extension of this component
if (this.setAdditionalParams) {
this.setAdditionalParams(options);
}
this.isMakingRequest = true; this.isMakingRequest = true;
this.service.merge(options) this.service.merge(options)
.then(res => res.data) .then(res => res.data)
...@@ -154,6 +158,9 @@ export default { ...@@ -154,6 +158,9 @@ export default {
new Flash('Something went wrong. Please try again.'); // eslint-disable-line new Flash('Something went wrong. Please try again.'); // eslint-disable-line
}); });
}, },
handleUpdateSquash(val) {
this.mr.squash = val;
},
initiateMergePolling() { initiateMergePolling() {
simplePoll((continuePolling, stopPolling) => { simplePoll((continuePolling, stopPolling) => {
this.handleMergePolling(continuePolling, stopPolling); this.handleMergePolling(continuePolling, stopPolling);
......
...@@ -15,6 +15,11 @@ export default class MergeRequestStore { ...@@ -15,6 +15,11 @@ export default class MergeRequestStore {
const currentUser = data.current_user; const currentUser = data.current_user;
const pipelineStatus = data.pipeline ? data.pipeline.details.status : null; const pipelineStatus = data.pipeline ? data.pipeline.details.status : null;
this.squash = data.squash;
this.squashBeforeMergeHelpPath = this.squashBeforeMergeHelpPath ||
data.squash_before_merge_help_path;
this.enableSquashBeforeMerge = this.enableSquashBeforeMerge || true;
this.title = data.title; this.title = data.title;
this.targetBranch = data.target_branch; this.targetBranch = data.target_branch;
this.sourceBranch = data.source_branch; this.sourceBranch = data.source_branch;
......
...@@ -2,7 +2,9 @@ import $ from 'jquery'; ...@@ -2,7 +2,9 @@ import $ from 'jquery';
export default { export default {
bind(el) { bind(el) {
$(el).tooltip(); $(el).tooltip({
trigger: 'hover',
});
}, },
componentUpdated(el) { componentUpdated(el) {
......
...@@ -13,7 +13,7 @@ export default (Vue) => { ...@@ -13,7 +13,7 @@ export default (Vue) => {
@param text The text to be translated @param text The text to be translated
@returns {String} The translated text @returns {String} The translated text
**/ */
__, __,
/** /**
Translate the text with a number Translate the text with a number
...@@ -24,7 +24,7 @@ export default (Vue) => { ...@@ -24,7 +24,7 @@ export default (Vue) => {
@param pluralText Plural text to translate (eg. '%d days') @param pluralText Plural text to translate (eg. '%d days')
@param count Number to decide which translation to use (eg. 2) @param count Number to decide which translation to use (eg. 2)
@returns {String} Translated text with the number replaced (eg. '2 days') @returns {String} Translated text with the number replaced (eg. '2 days')
**/ */
n__, n__,
/** /**
Translate context based text Translate context based text
...@@ -36,7 +36,7 @@ export default (Vue) => { ...@@ -36,7 +36,7 @@ export default (Vue) => {
(eg. 'Context') (eg. 'Context')
@param key Is the dynamic variable you want to be translated @param key Is the dynamic variable you want to be translated
@returns {String} Translated context based text @returns {String} Translated context based text
**/ */
s__, s__,
sprintf, sprintf,
}, },
......
...@@ -87,7 +87,8 @@ table { ...@@ -87,7 +87,8 @@ table {
display: none; display: none;
} }
.dropdown-toggle::after { .dropdown-toggle::after,
.dropright .dropdown-menu-toggle::after {
// Remove bootstrap's dropdown caret // Remove bootstrap's dropdown caret
display: none; display: none;
} }
...@@ -148,8 +149,14 @@ table { ...@@ -148,8 +149,14 @@ table {
} }
} }
.nav-tabs .nav-link { .nav-tabs {
border: 0; .nav-link {
border: 0;
}
.nav-item {
margin-bottom: 0;
}
} }
pre code { pre code {
......
/*
* This is a minimal stylesheet, meant to be used for error pages.
*/
@import 'framework/variables';
@import '../../../node_modules/bootstrap/scss/functions';
@import '../../../node_modules/bootstrap/scss/variables';
@import '../../../node_modules/bootstrap/scss/mixins';
@import '../../../node_modules/bootstrap/scss/reboot';
@import '../../../node_modules/bootstrap/scss/buttons';
@import '../../../node_modules/bootstrap/scss/forms';
$body-color: #666;
$header-color: #456;
body {
color: $body-color;
text-align: center;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
margin: auto;
font-size: 14px;
}
h1 {
font-size: 56px;
line-height: 100px;
font-weight: 400;
color: $header-color;
}
h2 {
font-size: 24px;
color: $body-color;
line-height: 1.5em;
}
h3 {
color: $header-color;
font-size: 20px;
font-weight: 400;
line-height: 28px;
}
img {
max-width: 80vw;
display: block;
margin: 40px auto;
}
a {
text-decoration: none;
color: $blue-600;
}
.page-container {
margin: auto 20px;
}
.container {
margin: auto;
max-width: 600px;
border-bottom: 1px solid $border-color;
padding-bottom: 1em;
}
.action-container {
padding: 0.5em 0;
}
.form-inline-flex {
display: flex;
flex-wrap: wrap;
button {
display: block;
width: 100%;
}
.field {
display: block;
width: 100%;
margin-bottom: 1em;
}
@include media-breakpoint-up(sm) {
flex-wrap: nowrap;
button {
width: auto;
}
.field {
margin-bottom: 0;
margin-right: 0.5em;
}
}
}
.error-nav {
padding: 0;
text-align: center;
li {
display: block;
padding-bottom: 1em;
}
@include media-breakpoint-up(sm) {
li {
display: inline-block;
padding-bottom: 0;
&:not(:first-child)::before {
content: '\00B7';
display: inline-block;
padding: 0 1em;
}
}
}
}
...@@ -169,11 +169,14 @@ ...@@ -169,11 +169,14 @@
color: $color-800; color: $color-800;
} }
.nav-links li a.active { .nav-links li {
border-bottom: 2px solid $color-500; &.active a,
a.active {
border-bottom: 2px solid $color-500;
.badge.badge-pill { .badge.badge-pill {
font-weight: $gl-font-weight-bold; font-weight: $gl-font-weight-bold;
}
} }
} }
......
...@@ -19,14 +19,23 @@ ...@@ -19,14 +19,23 @@
width: auto; width: auto;
display: inline-block; display: inline-block;
overflow-x: auto; overflow-x: auto;
border-left: 0; border: 0;
border-right: 0; border-color: $md-area-border;
border-bottom: 0;
@supports(width: fit-content) { @supports(width: fit-content) {
display: block; display: block;
width: fit-content; width: fit-content;
} }
tr {
th {
border-bottom: solid 2px $md-area-border;
}
td {
border-color: $md-area-border;
}
}
} }
/* /*
......
...@@ -74,12 +74,6 @@ body.modal-open { ...@@ -74,12 +74,6 @@ body.modal-open {
} }
} }
@include media-breakpoint-up(lg) {
.modal-full {
width: 98%;
}
}
.modal { .modal {
background-color: $black-transparent; background-color: $black-transparent;
z-index: 2100; z-index: 2100;
......
...@@ -31,14 +31,15 @@ ...@@ -31,14 +31,15 @@
color: $black; color: $black;
} }
} }
}
&.active { &.active a,
color: $black; a.active {
font-weight: $gl-font-weight-bold; color: $black;
font-weight: $gl-font-weight-bold;
.badge.badge-pill { .badge.badge-pill {
color: $black; color: $black;
}
} }
} }
} }
......
...@@ -49,26 +49,11 @@ ...@@ -49,26 +49,11 @@
margin-top: 15px; margin-top: 15px;
} }
.snippet-embed-input {
height: 35px;
}
.embed-snippet { .embed-snippet {
padding-right: 0; padding-right: 0;
padding-top: $gl-padding; padding-top: $gl-padding;
.form-control {
cursor: auto;
width: 101%;
margin-left: -1px;
}
.embed-toggle-list li button { .embed-toggle-list li button {
padding: 8px 40px; padding: 8px 40px;
} }
.embed-toggle,
.snippet-clipboard-btn {
height: 35px;
}
} }
...@@ -373,6 +373,7 @@ $dropdown-chevron-size: 10px; ...@@ -373,6 +373,7 @@ $dropdown-chevron-size: 10px;
$dropdown-toggle-active-border-color: darken($border-color, 14%); $dropdown-toggle-active-border-color: darken($border-color, 14%);
$dropdown-item-hover-bg: $gray-darker; $dropdown-item-hover-bg: $gray-darker;
$dropdown-fade-mask-height: 32px; $dropdown-fade-mask-height: 32px;
$dropdown-member-form-control-width: 163px;
/* /*
* Filtered Search * Filtered Search
......
...@@ -36,13 +36,12 @@ ...@@ -36,13 +36,12 @@
} }
} }
.form-horizontal { .form-group {
margin-top: 20px; margin-bottom: 0;
@include media-breakpoint-up(sm) { @include media-breakpoint-down(sm) {
display: -webkit-flex; display: block;
display: flex; margin-left: 5px;
margin-top: 3px;
} }
} }
...@@ -62,10 +61,15 @@ ...@@ -62,10 +61,15 @@
} }
.member-form-control { .member-form-control {
@include media-breakpoint-down(xs) { @include media-breakpoint-down(sm) {
padding-bottom: 5px; width: $dropdown-member-form-control-width;
margin-left: 0; margin-left: 0;
padding-bottom: 5px;
}
@include media-breakpoint-down(xs) {
margin-right: 0; margin-right: 0;
width: auto;
} }
} }
...@@ -207,10 +211,6 @@ ...@@ -207,10 +211,6 @@
align-self: flex-start; align-self: flex-start;
} }
.form-horizontal ~ .btn {
margin-right: 0;
}
@include media-breakpoint-down(xs) { @include media-breakpoint-down(xs) {
display: block; display: block;
...@@ -220,6 +220,12 @@ ...@@ -220,6 +220,12 @@
display: block; display: block;
} }
.controls > .btn:last-child {
margin-left: 5px;
margin-right: 5px;
width: auto;
}
.form-control { .form-control {
width: 100%; width: 100%;
} }
...@@ -232,10 +238,6 @@ ...@@ -232,10 +238,6 @@
.member-controls { .member-controls {
margin-top: 5px; margin-top: 5px;
} }
.form-horizontal {
margin-top: 10px;
}
} }
} }
...@@ -259,10 +261,6 @@ ...@@ -259,10 +261,6 @@
margin-top: 0; margin-top: 0;
} }
.form-horizontal {
display: block;
}
.member-form-control { .member-form-control {
margin: 5px 0; margin: 5px 0;
} }
......
...@@ -146,14 +146,15 @@ class ApplicationController < ActionController::Base ...@@ -146,14 +146,15 @@ class ApplicationController < ActionController::Base
end end
def render_403 def render_403
head :forbidden respond_to do |format|
format.any { head :forbidden }
format.html { render "errors/access_denied", layout: "errors", status: 403 }
end
end end
def render_404 def render_404
respond_to do |format| respond_to do |format|
format.html do format.html { render "errors/not_found", layout: "errors", status: 404 }
render file: Rails.root.join("public", "404"), layout: false, status: "404"
end
# Prevent the Rails CSRF protector from thinking a missing .js file is a JavaScript file # Prevent the Rails CSRF protector from thinking a missing .js file is a JavaScript file
format.js { render json: '', status: :not_found, content_type: 'application/json' } format.js { render json: '', status: :not_found, content_type: 'application/json' }
format.any { head :not_found } format.any { head :not_found }
......
...@@ -17,10 +17,23 @@ module IssuesAction ...@@ -17,10 +17,23 @@ module IssuesAction
end end
# rubocop:enable Gitlab/ModuleWithInstanceVariables # rubocop:enable Gitlab/ModuleWithInstanceVariables
# rubocop:disable Gitlab/ModuleWithInstanceVariables
def issues_calendar
@issues = issuables_collection
.non_archived
.with_due_date
.limit(100)
respond_to do |format|
format.ics { response.headers['Content-Disposition'] = 'inline' }
end
end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
private private
def finder_type def finder_type
(super if defined?(super)) || (super if defined?(super)) ||
(IssuesFinder if action_name == 'issues') (IssuesFinder if %w(issues issues_calendar).include?(action_name))
end end
end end
module Groups
class SharedProjectsController < Groups::ApplicationController
respond_to :json
before_action :group
skip_cross_project_access_check :index
def index
shared_projects = GroupProjectsFinder.new(
group: group,
current_user: current_user,
params: finder_params,
options: { only_shared: true }
).execute
serializer = GroupChildSerializer.new(current_user: current_user)
.with_pagination(request, response)
render json: serializer.represent(shared_projects)
end
private
def finder_params
@finder_params ||= begin
# Make the `search` param consistent for the frontend,
# which will be using `filter`.
params[:search] ||= params[:filter] if params[:filter]
params.permit(:sort, :search)
end
end
end
end
...@@ -34,12 +34,12 @@ class ProfilesController < Profiles::ApplicationController ...@@ -34,12 +34,12 @@ class ProfilesController < Profiles::ApplicationController
redirect_to profile_personal_access_tokens_path redirect_to profile_personal_access_tokens_path
end end
def reset_rss_token def reset_feed_token
Users::UpdateService.new(current_user, user: @user).execute! do |user| Users::UpdateService.new(current_user, user: @user).execute! do |user|
user.reset_rss_token! user.reset_feed_token!
end end
flash[:notice] = "RSS token was successfully reset" flash[:notice] = 'Feed token was successfully reset'
redirect_to profile_personal_access_tokens_path redirect_to profile_personal_access_tokens_path
end end
......
...@@ -10,8 +10,8 @@ class Projects::IssuesController < Projects::ApplicationController ...@@ -10,8 +10,8 @@ class Projects::IssuesController < Projects::ApplicationController
before_action :whitelist_query_limiting, only: [:create, :create_merge_request, :move, :bulk_update] before_action :whitelist_query_limiting, only: [:create, :create_merge_request, :move, :bulk_update]
before_action :check_issues_available! before_action :check_issues_available!
before_action :issue, except: [:index, :new, :create, :bulk_update] before_action :issue, except: [:index, :calendar, :new, :create, :bulk_update]
before_action :set_issuables_index, only: [:index] before_action :set_issuables_index, only: [:index, :calendar]
# Allow write(create) issue # Allow write(create) issue
before_action :authorize_create_issue!, only: [:new, :create] before_action :authorize_create_issue!, only: [:new, :create]
...@@ -39,6 +39,17 @@ class Projects::IssuesController < Projects::ApplicationController ...@@ -39,6 +39,17 @@ class Projects::IssuesController < Projects::ApplicationController
end end
end end
def calendar
@issues = @issuables
.non_archived
.with_due_date
.limit(100)
respond_to do |format|
format.ics { response.headers['Content-Disposition'] = 'inline' }
end
end
def new def new
params[:issue] ||= ActionController::Parameters.new( params[:issue] ||= ActionController::Parameters.new(
assignee_ids: "" assignee_ids: ""
......
...@@ -24,6 +24,7 @@ class Projects::MergeRequests::ApplicationController < Projects::ApplicationCont ...@@ -24,6 +24,7 @@ class Projects::MergeRequests::ApplicationController < Projects::ApplicationCont
:source_branch, :source_branch,
:source_project_id, :source_project_id,
:state_event, :state_event,
:squash,
:target_branch, :target_branch,
:target_project_id, :target_project_id,
:task_num, :task_num,
......
...@@ -253,7 +253,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo ...@@ -253,7 +253,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
end end
def merge_params_attributes def merge_params_attributes
[:should_remove_source_branch, :commit_message] [:should_remove_source_branch, :commit_message, :squash]
end end
def merge_when_pipeline_succeeds_active? def merge_when_pipeline_succeeds_active?
...@@ -282,7 +282,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo ...@@ -282,7 +282,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
return :sha_mismatch if params[:sha] != @merge_request.diff_head_sha return :sha_mismatch if params[:sha] != @merge_request.diff_head_sha
@merge_request.update(merge_error: nil) @merge_request.update(merge_error: nil, squash: merge_params.fetch(:squash, false))
if params[:merge_when_pipeline_succeeds].present? if params[:merge_when_pipeline_succeeds].present?
return :failed unless @merge_request.actual_head_pipeline return :failed unless @merge_request.actual_head_pipeline
...@@ -296,14 +296,14 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo ...@@ -296,14 +296,14 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
elsif @merge_request.actual_head_pipeline.success? elsif @merge_request.actual_head_pipeline.success?
# This can be triggered when a user clicks the auto merge button while # This can be triggered when a user clicks the auto merge button while
# the tests finish at about the same time # the tests finish at about the same time
@merge_request.merge_async(current_user.id, params) @merge_request.merge_async(current_user.id, merge_params)
:success :success
else else
:failed :failed
end end
else else
@merge_request.merge_async(current_user.id, params) @merge_request.merge_async(current_user.id, merge_params)
:success :success
end end
......
...@@ -75,6 +75,8 @@ class IssuesFinder < IssuableFinder ...@@ -75,6 +75,8 @@ class IssuesFinder < IssuableFinder
items = items.due_between(Date.today.beginning_of_week, Date.today.end_of_week) items = items.due_between(Date.today.beginning_of_week, Date.today.end_of_week)
elsif filter_by_due_this_month? elsif filter_by_due_this_month?
items = items.due_between(Date.today.beginning_of_month, Date.today.end_of_month) items = items.due_between(Date.today.beginning_of_month, Date.today.end_of_month)
elsif filter_by_due_next_month_and_previous_two_weeks?
items = items.due_between(Date.today - 2.weeks, (Date.today + 1.month).end_of_month)
end end
end end
...@@ -97,6 +99,10 @@ class IssuesFinder < IssuableFinder ...@@ -97,6 +99,10 @@ class IssuesFinder < IssuableFinder
due_date? && params[:due_date] == Issue::DueThisMonth.name due_date? && params[:due_date] == Issue::DueThisMonth.name
end end
def filter_by_due_next_month_and_previous_two_weeks?
due_date? && params[:due_date] == Issue::DueNextMonthAndPreviousTwoWeeks.name
end
def due_date? def due_date?
params[:due_date].present? params[:due_date].present?
end end
......
module CalendarHelper
def calendar_url_options
{ format: :ics,
feed_token: current_user.try(:feed_token),
due_date: Issue::DueNextMonthAndPreviousTwoWeeks.name,
sort: 'closest_future_date' }
end
end
...@@ -97,8 +97,9 @@ module MergeRequestsHelper ...@@ -97,8 +97,9 @@ module MergeRequestsHelper
{ {
merge_when_pipeline_succeeds: true, merge_when_pipeline_succeeds: true,
should_remove_source_branch: true, should_remove_source_branch: true,
sha: merge_request.diff_head_sha sha: merge_request.diff_head_sha,
}.merge(merge_params_ee(merge_request)) squash: merge_request.squash
}
end end
def tab_link_for(merge_request, tab, options = {}, &block) def tab_link_for(merge_request, tab, options = {}, &block)
...@@ -149,8 +150,4 @@ module MergeRequestsHelper ...@@ -149,8 +150,4 @@ module MergeRequestsHelper
current_user.fork_of(project) current_user.fork_of(project)
end end
end end
def merge_params_ee(merge_request)
{}
end
end end
module RssHelper module RssHelper
def rss_url_options def rss_url_options
{ format: :atom, rss_token: current_user.try(:rss_token) } { format: :atom, feed_token: current_user.try(:feed_token) }
end end
end end
...@@ -360,17 +360,6 @@ class ApplicationSetting < ActiveRecord::Base ...@@ -360,17 +360,6 @@ class ApplicationSetting < ActiveRecord::Base
Array(read_attribute(:repository_storages)) Array(read_attribute(:repository_storages))
end end
# DEPRECATED
# repository_storage is still required in the API. Remove in 9.0
# Still used in API v3
def repository_storage
repository_storages.first
end
def repository_storage=(value)
self.repository_storages = [value]
end
def default_project_visibility=(level) def default_project_visibility=(level)
super(Gitlab::VisibilityLevel.level_value(level)) super(Gitlab::VisibilityLevel.level_value(level))
end end
......
...@@ -97,8 +97,6 @@ module Issuable ...@@ -97,8 +97,6 @@ module Issuable
strip_attributes :title strip_attributes :title
after_save :ensure_metrics, unless: :imported?
# We want to use optimistic lock for cases when only title or description are involved # We want to use optimistic lock for cases when only title or description are involved
# http://api.rubyonrails.org/classes/ActiveRecord/Locking/Optimistic.html # http://api.rubyonrails.org/classes/ActiveRecord/Locking/Optimistic.html
def locking_enabled? def locking_enabled?
......
# Makes api V3 compatible with old project features permissions methods # Makes api V4 compatible with old project features permissions methods
# #
# After migrating issues_enabled merge_requests_enabled builds_enabled snippets_enabled and wiki_enabled # After migrating issues_enabled merge_requests_enabled builds_enabled snippets_enabled and wiki_enabled
# fields to a new table "project_features", support for the old fields is still needed in the API. # fields to a new table "project_features", support for the old fields is still needed in the API.
......
...@@ -15,12 +15,13 @@ class Issue < ActiveRecord::Base ...@@ -15,12 +15,13 @@ class Issue < ActiveRecord::Base
ignore_column :assignee_id, :branch_name, :deleted_at ignore_column :assignee_id, :branch_name, :deleted_at
DueDateStruct = Struct.new(:title, :name).freeze DueDateStruct = Struct.new(:title, :name).freeze
NoDueDate = DueDateStruct.new('No Due Date', '0').freeze NoDueDate = DueDateStruct.new('No Due Date', '0').freeze
AnyDueDate = DueDateStruct.new('Any Due Date', '').freeze AnyDueDate = DueDateStruct.new('Any Due Date', '').freeze
Overdue = DueDateStruct.new('Overdue', 'overdue').freeze Overdue = DueDateStruct.new('Overdue', 'overdue').freeze
DueThisWeek = DueDateStruct.new('Due This Week', 'week').freeze DueThisWeek = DueDateStruct.new('Due This Week', 'week').freeze
DueThisMonth = DueDateStruct.new('Due This Month', 'month').freeze DueThisMonth = DueDateStruct.new('Due This Month', 'month').freeze
DueNextMonthAndPreviousTwoWeeks = DueDateStruct.new('Due Next Month And Previous Two Weeks', 'next_month_and_previous_two_weeks').freeze
belongs_to :project belongs_to :project
belongs_to :moved_to, class_name: 'Issue' belongs_to :moved_to, class_name: 'Issue'
...@@ -47,6 +48,7 @@ class Issue < ActiveRecord::Base ...@@ -47,6 +48,7 @@ class Issue < ActiveRecord::Base
scope :unassigned, -> { where('NOT EXISTS (SELECT TRUE FROM issue_assignees WHERE issue_id = issues.id)') } scope :unassigned, -> { where('NOT EXISTS (SELECT TRUE FROM issue_assignees WHERE issue_id = issues.id)') }
scope :assigned_to, ->(u) { where('EXISTS (SELECT TRUE FROM issue_assignees WHERE user_id = ? AND issue_id = issues.id)', u.id)} scope :assigned_to, ->(u) { where('EXISTS (SELECT TRUE FROM issue_assignees WHERE user_id = ? AND issue_id = issues.id)', u.id)}
scope :with_due_date, -> { where('due_date IS NOT NULL') }
scope :without_due_date, -> { where(due_date: nil) } scope :without_due_date, -> { where(due_date: nil) }
scope :due_before, ->(date) { where('issues.due_date < ?', date) } scope :due_before, ->(date) { where('issues.due_date < ?', date) }
scope :due_between, ->(from_date, to_date) { where('issues.due_date >= ?', from_date).where('issues.due_date <= ?', to_date) } scope :due_between, ->(from_date, to_date) { where('issues.due_date >= ?', from_date).where('issues.due_date <= ?', to_date) }
...@@ -54,12 +56,14 @@ class Issue < ActiveRecord::Base ...@@ -54,12 +56,14 @@ class Issue < ActiveRecord::Base
scope :order_due_date_asc, -> { reorder('issues.due_date IS NULL, issues.due_date ASC') } scope :order_due_date_asc, -> { reorder('issues.due_date IS NULL, issues.due_date ASC') }
scope :order_due_date_desc, -> { reorder('issues.due_date IS NULL, issues.due_date DESC') } scope :order_due_date_desc, -> { reorder('issues.due_date IS NULL, issues.due_date DESC') }
scope :order_closest_future_date, -> { reorder('CASE WHEN due_date >= CURRENT_DATE THEN 0 ELSE 1 END ASC, ABS(CURRENT_DATE - due_date) ASC') }
scope :preload_associations, -> { preload(:labels, project: :namespace) } scope :preload_associations, -> { preload(:labels, project: :namespace) }
scope :public_only, -> { where(confidential: false) } scope :public_only, -> { where(confidential: false) }
after_save :expire_etag_cache after_save :expire_etag_cache
after_save :ensure_metrics, unless: :imported?
attr_spammable :title, spam_title: true attr_spammable :title, spam_title: true
attr_spammable :description, spam_description: true attr_spammable :description, spam_description: true
...@@ -120,6 +124,7 @@ class Issue < ActiveRecord::Base ...@@ -120,6 +124,7 @@ class Issue < ActiveRecord::Base
def self.sort_by_attribute(method, excluded_labels: []) def self.sort_by_attribute(method, excluded_labels: [])
case method.to_s case method.to_s
when 'closest_future_date' then order_closest_future_date
when 'due_date' then order_due_date_asc when 'due_date' then order_due_date_asc
when 'due_date_asc' then order_due_date_asc when 'due_date_asc' then order_due_date_asc
when 'due_date_desc' then order_due_date_desc when 'due_date_desc' then order_due_date_desc
......
...@@ -59,6 +59,7 @@ class MergeRequest < ActiveRecord::Base ...@@ -59,6 +59,7 @@ class MergeRequest < ActiveRecord::Base
after_create :ensure_merge_request_diff, unless: :importing? after_create :ensure_merge_request_diff, unless: :importing?
after_update :clear_memoized_shas after_update :clear_memoized_shas
after_update :reload_diff_if_branch_changed after_update :reload_diff_if_branch_changed
after_save :ensure_metrics
# When this attribute is true some MR validation is ignored # When this attribute is true some MR validation is ignored
# It allows us to close or modify broken merge requests # It allows us to close or modify broken merge requests
...@@ -1141,4 +1142,11 @@ class MergeRequest < ActiveRecord::Base ...@@ -1141,4 +1142,11 @@ class MergeRequest < ActiveRecord::Base
maintainer_push_possible? && maintainer_push_possible? &&
Ability.allowed?(user, :push_code, source_project) Ability.allowed?(user, :push_code, source_project)
end end
def squash_in_progress?
# The source project can be deleted
return false unless source_project
source_project.repository.squash_in_progress?(id)
end
end end
...@@ -957,6 +957,22 @@ class Repository ...@@ -957,6 +957,22 @@ class Repository
remote_branch: merge_request.target_branch) remote_branch: merge_request.target_branch)
end end
def blob_data_at(sha, path)
blob = blob_at(sha, path)
return unless blob
blob.load_all_data!
blob.data
end
def squash(user, merge_request)
raw.squash(user, merge_request.id, branch: merge_request.target_branch,
start_sha: merge_request.diff_start_sha,
end_sha: merge_request.diff_head_sha,
author: merge_request.author,
message: merge_request.title)
end
private private
# TODO Generice finder, later split this on finders by Ref or Oid # TODO Generice finder, later split this on finders by Ref or Oid
...@@ -971,14 +987,6 @@ class Repository ...@@ -971,14 +987,6 @@ class Repository
::Commit.new(commit, @project) if commit ::Commit.new(commit, @project) if commit
end end
def blob_data_at(sha, path)
blob = blob_at(sha, path)
return unless blob
blob.load_all_data!
blob.data
end
def cache def cache
@cache ||= Gitlab::RepositoryCache.new(self) @cache ||= Gitlab::RepositoryCache.new(self)
end end
......
...@@ -206,10 +206,11 @@ class Service < ActiveRecord::Base ...@@ -206,10 +206,11 @@ class Service < ActiveRecord::Base
args.each do |arg| args.each do |arg|
class_eval %{ class_eval %{
def #{arg}? def #{arg}?
# '!!' is used because nil or empty string is converted to nil
if Gitlab.rails5? if Gitlab.rails5?
!ActiveModel::Type::Boolean::FALSE_VALUES.include?(#{arg}) !!ActiveRecord::Type::Boolean.new.cast(#{arg})
else else
ActiveRecord::ConnectionAdapters::Column::TRUE_VALUES.include?(#{arg}) !!ActiveRecord::Type::Boolean.new.type_cast_from_database(#{arg})
end end
end end
} }
......
...@@ -26,7 +26,7 @@ class User < ActiveRecord::Base ...@@ -26,7 +26,7 @@ class User < ActiveRecord::Base
ignore_column :authentication_token ignore_column :authentication_token
add_authentication_token_field :incoming_email_token add_authentication_token_field :incoming_email_token
add_authentication_token_field :rss_token add_authentication_token_field :feed_token
default_value_for :admin, false default_value_for :admin, false
default_value_for(:external) { Gitlab::CurrentSettings.user_default_external } default_value_for(:external) { Gitlab::CurrentSettings.user_default_external }
...@@ -1167,11 +1167,11 @@ class User < ActiveRecord::Base ...@@ -1167,11 +1167,11 @@ class User < ActiveRecord::Base
save save
end end
# each existing user needs to have an `rss_token`. # each existing user needs to have an `feed_token`.
# we do this on read since migrating all existing users is not a feasible # we do this on read since migrating all existing users is not a feasible
# solution. # solution.
def rss_token def feed_token
ensure_rss_token! ensure_feed_token!
end end
def sync_attribute?(attribute) def sync_attribute?(attribute)
......
class CommitStatusPresenter < Gitlab::View::Presenter::Delegated class CommitStatusPresenter < Gitlab::View::Presenter::Delegated
CALLOUT_FAILURE_MESSAGES = { CALLOUT_FAILURE_MESSAGES = {
unknown_failure: 'There is an unknown failure, please try again', unknown_failure: 'There is an unknown failure, please try again',
script_failure: 'There has been a script failure. Check the job log for more information',
api_failure: 'There has been an API failure, please try again', api_failure: 'There has been an API failure, please try again',
stuck_or_timeout_failure: 'There has been a timeout failure or the job got stuck. Check your timeout limits or try again', stuck_or_timeout_failure: 'There has been a timeout failure or the job got stuck. Check your timeout limits or try again',
runner_system_failure: 'There has been a runner system failure, please try again', runner_system_failure: 'There has been a runner system failure, please try again',
missing_dependency_failure: 'There has been a missing dependency failure, check the job log for more information' missing_dependency_failure: 'There has been a missing dependency failure'
}.freeze }.freeze
presents :build presents :build
......
...@@ -31,7 +31,7 @@ class GroupChildEntity < Grape::Entity ...@@ -31,7 +31,7 @@ class GroupChildEntity < Grape::Entity
end end
# Project only attributes # Project only attributes
expose :star_count, expose :star_count, :archived,
if: lambda { |_instance, _options| project? } if: lambda { |_instance, _options| project? }
# Group only attributes # Group only attributes
......
...@@ -26,7 +26,7 @@ class JobEntity < Grape::Entity ...@@ -26,7 +26,7 @@ class JobEntity < Grape::Entity
expose :created_at expose :created_at
expose :updated_at expose :updated_at
expose :detailed_status, as: :status, with: StatusEntity expose :detailed_status, as: :status, with: StatusEntity
expose :callout_message, if: -> (*) { failed? } expose :callout_message, if: -> (*) { failed? && !build.script_failure? }
expose :recoverable, if: -> (*) { failed? } expose :recoverable, if: -> (*) { failed? }
private private
......
...@@ -10,6 +10,7 @@ class MergeRequestWidgetEntity < IssuableEntity ...@@ -10,6 +10,7 @@ class MergeRequestWidgetEntity < IssuableEntity
expose :merge_when_pipeline_succeeds expose :merge_when_pipeline_succeeds
expose :source_branch expose :source_branch
expose :source_project_id expose :source_project_id
expose :squash
expose :target_branch expose :target_branch
expose :target_project_id expose :target_project_id
expose :allow_maintainer_to_push expose :allow_maintainer_to_push
......
...@@ -63,7 +63,7 @@ module Boards ...@@ -63,7 +63,7 @@ module Boards
def without_board_labels(issues) def without_board_labels(issues)
return issues unless board_label_ids.any? return issues unless board_label_ids.any?
issues.where.not(issues_label_links.limit(1).arel.exists) issues.where.not('EXISTS (?)', issues_label_links.limit(1))
end end
def issues_label_links def issues_label_links
...@@ -71,10 +71,8 @@ module Boards ...@@ -71,10 +71,8 @@ module Boards
end end
def with_list_label(issues) def with_list_label(issues)
issues.where( issues.where('EXISTS (?)', LabelLink.where("label_links.target_type = 'Issue' AND label_links.target_id = issues.id")
LabelLink.where("label_links.target_type = 'Issue' AND label_links.target_id = issues.id") .where("label_links.label_id = ?", list.label_id).limit(1))
.where("label_links.label_id = ?", list.label_id).limit(1).arel.exists
)
end end
end end
end end
......
...@@ -34,6 +34,19 @@ module MergeRequests ...@@ -34,6 +34,19 @@ module MergeRequests
handle_merge_error(log_message: e.message, save_message_on_model: true) handle_merge_error(log_message: e.message, save_message_on_model: true)
end end
def source
return merge_request.diff_head_sha unless merge_request.squash
squash_result = ::MergeRequests::SquashService.new(project, current_user, params).execute(merge_request)
case squash_result[:status]
when :success
squash_result[:squash_sha]
when :error
raise ::MergeRequests::MergeService::MergeError, squash_result[:message]
end
end
private private
def error_check! def error_check!
...@@ -116,9 +129,5 @@ module MergeRequests ...@@ -116,9 +129,5 @@ module MergeRequests
def merge_request_info def merge_request_info
merge_request.to_reference(full: true) merge_request.to_reference(full: true)
end end
def source
@source ||= @merge_request.diff_head_sha
end
end end
end end
module MergeRequests
class SquashService < MergeRequests::WorkingCopyBaseService
def execute(merge_request)
@merge_request = merge_request
@repository = target_project.repository
squash || error('Failed to squash. Should be done manually.')
end
def squash
if merge_request.commits_count < 2
return success(squash_sha: merge_request.diff_head_sha)
end
if merge_request.squash_in_progress?
return error('Squash task canceled: another squash is already in progress.')
end
squash_sha = repository.squash(current_user, merge_request)
success(squash_sha: squash_sha)
rescue => e
log_error("Failed to squash merge request #{merge_request.to_reference(full: true)}:")
log_error(e.message)
false
end
end
end
...@@ -11,6 +11,7 @@ module ObjectStorage ...@@ -11,6 +11,7 @@ module ObjectStorage
ObjectStorageUnavailable = Class.new(StandardError) ObjectStorageUnavailable = Class.new(StandardError)
DIRECT_UPLOAD_TIMEOUT = 4.hours DIRECT_UPLOAD_TIMEOUT = 4.hours
DIRECT_UPLOAD_EXPIRE_OFFSET = 15.minutes
TMP_UPLOAD_PATH = 'tmp/uploads'.freeze TMP_UPLOAD_PATH = 'tmp/uploads'.freeze
module Store module Store
...@@ -174,11 +175,12 @@ module ObjectStorage ...@@ -174,11 +175,12 @@ module ObjectStorage
id = [CarrierWave.generate_cache_id, SecureRandom.hex].join('-') id = [CarrierWave.generate_cache_id, SecureRandom.hex].join('-')
upload_path = File.join(TMP_UPLOAD_PATH, id) upload_path = File.join(TMP_UPLOAD_PATH, id)
connection = ::Fog::Storage.new(self.object_store_credentials) connection = ::Fog::Storage.new(self.object_store_credentials)
expire_at = Time.now + DIRECT_UPLOAD_TIMEOUT expire_at = Time.now + DIRECT_UPLOAD_TIMEOUT + DIRECT_UPLOAD_EXPIRE_OFFSET
options = { 'Content-Type' => 'application/octet-stream' } options = { 'Content-Type' => 'application/octet-stream' }
{ {
ID: id, ID: id,
Timeout: DIRECT_UPLOAD_TIMEOUT,
GetURL: connection.get_object_url(remote_store_path, upload_path, expire_at), GetURL: connection.get_object_url(remote_store_path, upload_path, expire_at),
DeleteURL: connection.delete_object_url(remote_store_path, upload_path, expire_at), DeleteURL: connection.delete_object_url(remote_store_path, upload_path, expire_at),
StoreURL: connection.put_object_url(remote_store_path, upload_path, expire_at, options) StoreURL: connection.put_object_url(remote_store_path, upload_path, expire_at, options)
......
...@@ -5,8 +5,8 @@ ...@@ -5,8 +5,8 @@
.form-group.row .form-group.row
.offset-sm-2.col-sm-10 .offset-sm-2.col-sm-10
.form-check .form-check
= f.label :gravatar_enabled do = f.check_box :gravatar_enabled, class: 'form-check-input'
= f.check_box :gravatar_enabled = f.label :gravatar_enabled, class: 'form-check-label' do
Gravatar enabled Gravatar enabled
.form-group.row .form-group.row
= f.label :default_projects_limit, class: 'col-form-label col-sm-2' = f.label :default_projects_limit, class: 'col-form-label col-sm-2'
...@@ -25,15 +25,15 @@ ...@@ -25,15 +25,15 @@
= f.label :user_oauth_applications, 'User OAuth applications', class: 'col-form-label col-sm-2' = f.label :user_oauth_applications, 'User OAuth applications', class: 'col-form-label col-sm-2'
.col-sm-10 .col-sm-10
.form-check .form-check
= f.label :user_oauth_applications do = f.check_box :user_oauth_applications, class: 'form-check-input'
= f.check_box :user_oauth_applications = f.label :user_oauth_applications, class: 'form-check-label' do
Allow users to register any application to use GitLab as an OAuth provider Allow users to register any application to use GitLab as an OAuth provider
.form-group.row .form-group.row
= f.label :user_default_external, 'New users set to external', class: 'col-form-label col-sm-2' = f.label :user_default_external, 'New users set to external', class: 'col-form-label col-sm-2'
.col-sm-10 .col-sm-10
.form-check .form-check
= f.label :user_default_external do = f.check_box :user_default_external, class: 'form-check-input'
= f.check_box :user_default_external = f.label :user_default_external, class: 'form-check-label' do
Newly registered users will by default be external Newly registered users will by default be external
= f.submit 'Save changes', class: 'btn btn-success' = f.submit 'Save changes', class: 'btn btn-success'
...@@ -9,8 +9,8 @@ ...@@ -9,8 +9,8 @@
.form-group.row .form-group.row
.offset-sm-2.col-sm-10 .offset-sm-2.col-sm-10
.form-check .form-check
= f.label :sidekiq_throttling_enabled do = f.check_box :sidekiq_throttling_enabled, class: 'form-check-input'
= f.check_box :sidekiq_throttling_enabled = f.label :sidekiq_throttling_enabled, class: 'form-check-label' do
Enable Sidekiq Job Throttling Enable Sidekiq Job Throttling
.form-text.text-muted .form-text.text-muted
Limit the amount of resources slow running jobs are assigned. Limit the amount of resources slow running jobs are assigned.
......
...@@ -5,8 +5,8 @@ ...@@ -5,8 +5,8 @@
.form-group.row .form-group.row
.offset-sm-2.col-sm-10 .offset-sm-2.col-sm-10
.form-check .form-check
= f.label :auto_devops_enabled do = f.check_box :auto_devops_enabled, class: 'form-check-input'
= f.check_box :auto_devops_enabled = f.label :auto_devops_enabled, class: 'form-check-label' do
Enabled Auto DevOps for projects by default Enabled Auto DevOps for projects by default
.form-text.text-muted .form-text.text-muted
It will automatically build, test, and deploy applications based on a predefined CI/CD configuration It will automatically build, test, and deploy applications based on a predefined CI/CD configuration
...@@ -20,8 +20,8 @@ ...@@ -20,8 +20,8 @@
.form-group.row .form-group.row
.offset-sm-2.col-sm-10 .offset-sm-2.col-sm-10
.form-check .form-check
= f.label :shared_runners_enabled do = f.check_box :shared_runners_enabled, class: 'form-check-input'
= f.check_box :shared_runners_enabled = f.label :shared_runners_enabled, class: 'form-check-label' do
Enable shared runners for new projects Enable shared runners for new projects
.form-group.row .form-group.row
= f.label :shared_runners_text, class: 'col-form-label col-sm-2' = f.label :shared_runners_text, class: 'col-form-label col-sm-2'
......
...@@ -5,8 +5,8 @@ ...@@ -5,8 +5,8 @@
.form-group.row .form-group.row
.offset-sm-2.col-sm-10 .offset-sm-2.col-sm-10
.form-check .form-check
= f.label :email_author_in_body do = f.check_box :email_author_in_body, class: 'form-check-input'
= f.check_box :email_author_in_body = f.label :email_author_in_body, class: 'form-check-label' do
Include author name in notification email body Include author name in notification email body
.form-text.text-muted .form-text.text-muted
Some email servers do not support overriding the email sender name. Some email servers do not support overriding the email sender name.
...@@ -15,8 +15,8 @@ ...@@ -15,8 +15,8 @@
.form-group.row .form-group.row
.offset-sm-2.col-sm-10 .offset-sm-2.col-sm-10
.form-check .form-check
= f.label :html_emails_enabled do = f.check_box :html_emails_enabled, class: 'form-check-input'
= f.check_box :html_emails_enabled = f.label :html_emails_enabled, class: 'form-check-label' do
Enable HTML emails Enable HTML emails
.form-text.text-muted .form-text.text-muted
By default GitLab sends emails in HTML and plain text formats so mail By default GitLab sends emails in HTML and plain text formats so mail
......
...@@ -10,8 +10,8 @@ ...@@ -10,8 +10,8 @@
.form-group.row .form-group.row
.offset-sm-2.col-sm-10 .offset-sm-2.col-sm-10
.form-check .form-check
= f.label :help_page_hide_commercial_content do = f.check_box :help_page_hide_commercial_content, class: 'form-check-input'
= f.check_box :help_page_hide_commercial_content = f.label :help_page_hide_commercial_content, class: 'form-check-label' do
Hide marketing-related entries from help Hide marketing-related entries from help
.form-group.row .form-group.row
= f.label :help_page_support_url, 'Support page URL', class: 'col-form-label col-sm-2' = f.label :help_page_support_url, 'Support page URL', class: 'col-form-label col-sm-2'
......
...@@ -11,8 +11,8 @@ ...@@ -11,8 +11,8 @@
.form-group.row .form-group.row
.offset-sm-2.col-sm-10 .offset-sm-2.col-sm-10
.form-check .form-check
= f.label :metrics_enabled do = f.check_box :metrics_enabled, class: 'form-check-input'
= f.check_box :metrics_enabled = f.label :metrics_enabled, class: 'form-check-label' do
Enable InfluxDB Metrics Enable InfluxDB Metrics
.form-group.row .form-group.row
= f.label :metrics_host, 'InfluxDB host', class: 'col-form-label col-sm-2' = f.label :metrics_host, 'InfluxDB host', class: 'col-form-label col-sm-2'
......
...@@ -5,8 +5,8 @@ ...@@ -5,8 +5,8 @@
.form-group.row .form-group.row
.offset-sm-2.col-sm-10 .offset-sm-2.col-sm-10
.form-check .form-check
= f.label :throttle_unauthenticated_enabled do = f.check_box :throttle_unauthenticated_enabled, class: 'form-check-input'
= f.check_box :throttle_unauthenticated_enabled = f.label :throttle_unauthenticated_enabled, class: 'form-check-label' do
Enable unauthenticated request rate limit Enable unauthenticated request rate limit
%span.form-text.text-muted %span.form-text.text-muted
Helps reduce request volume (e.g. from crawlers or abusive bots) Helps reduce request volume (e.g. from crawlers or abusive bots)
...@@ -21,8 +21,8 @@ ...@@ -21,8 +21,8 @@
.form-group.row .form-group.row
.offset-sm-2.col-sm-10 .offset-sm-2.col-sm-10
.form-check .form-check
= f.label :throttle_authenticated_api_enabled do = f.check_box :throttle_authenticated_api_enabled, class: 'form-check-input'
= f.check_box :throttle_authenticated_api_enabled = f.label :throttle_authenticated_api_enabled, class: 'form-check-label' do
Enable authenticated API request rate limit Enable authenticated API request rate limit
%span.form-text.text-muted %span.form-text.text-muted
Helps reduce request volume (e.g. from crawlers or abusive bots) Helps reduce request volume (e.g. from crawlers or abusive bots)
...@@ -37,8 +37,8 @@ ...@@ -37,8 +37,8 @@
.form-group.row .form-group.row
.offset-sm-2.col-sm-10 .offset-sm-2.col-sm-10
.form-check .form-check
= f.label :throttle_authenticated_web_enabled do = f.check_box :throttle_authenticated_web_enabled, class: 'form-check-input'
= f.check_box :throttle_authenticated_web_enabled = f.label :throttle_authenticated_web_enabled, class: 'form-check-label' do
Enable authenticated web request rate limit Enable authenticated web request rate limit
%span.form-text.text-muted %span.form-text.text-muted
Helps reduce request volume (e.g. from crawlers or abusive bots) Helps reduce request volume (e.g. from crawlers or abusive bots)
......
...@@ -5,8 +5,8 @@ ...@@ -5,8 +5,8 @@
.form-group.row .form-group.row
.offset-sm-2.col-sm-10 .offset-sm-2.col-sm-10
.form-check .form-check
= f.label :koding_enabled do = f.check_box :koding_enabled, class: 'form-check-input'
= f.check_box :koding_enabled = f.label :koding_enabled, class: 'form-check-label' do
Enable Koding Enable Koding
.form-text.text-muted .form-text.text-muted
Koding integration has been deprecated since GitLab 10.0. If you disable your Koding integration, you will not be able to enable it again. Koding integration has been deprecated since GitLab 10.0. If you disable your Koding integration, you will not be able to enable it again.
......
...@@ -5,8 +5,8 @@ ...@@ -5,8 +5,8 @@
.form-group.row .form-group.row
.offset-sm-2.col-sm-10 .offset-sm-2.col-sm-10
.form-check .form-check
= f.label :sentry_enabled do = f.check_box :sentry_enabled, class: 'form-check-input'
= f.check_box :sentry_enabled = f.label :sentry_enabled, class: 'form-check-label' do
Enable Sentry Enable Sentry
.form-text.text-muted .form-text.text-muted
%p This setting requires a restart to take effect. %p This setting requires a restart to take effect.
...@@ -21,8 +21,8 @@ ...@@ -21,8 +21,8 @@
.form-group.row .form-group.row
.offset-sm-2.col-sm-10 .offset-sm-2.col-sm-10
.form-check .form-check
= f.label :clientside_sentry_enabled do = f.check_box :clientside_sentry_enabled, class: 'form-check-input'
= f.check_box :clientside_sentry_enabled = f.label :clientside_sentry_enabled, class: 'form-check-label' do
Enable Clientside Sentry Enable Clientside Sentry
.form-text.text-muted .form-text.text-muted
Sentry can also be used for reporting and logging clientside exceptions. Sentry can also be used for reporting and logging clientside exceptions.
......
...@@ -5,8 +5,8 @@ ...@@ -5,8 +5,8 @@
.form-group.row .form-group.row
.offset-sm-2.col-sm-10 .offset-sm-2.col-sm-10
.form-check .form-check
= f.label :allow_local_requests_from_hooks_and_services do = f.check_box :allow_local_requests_from_hooks_and_services, class: 'form-check-input'
= f.check_box :allow_local_requests_from_hooks_and_services = f.label :allow_local_requests_from_hooks_and_services, class: 'form-check-label' do
Allow requests to the local network from hooks and services Allow requests to the local network from hooks and services
= f.submit 'Save changes', class: "btn btn-success" = f.submit 'Save changes', class: "btn btn-success"
...@@ -10,8 +10,8 @@ ...@@ -10,8 +10,8 @@
.form-group.row .form-group.row
.offset-sm-2.col-sm-10 .offset-sm-2.col-sm-10
.form-check .form-check
= f.label :pages_domain_verification_enabled do = f.check_box :pages_domain_verification_enabled, class: 'form-check-input'
= f.check_box :pages_domain_verification_enabled = f.label :pages_domain_verification_enabled, class: 'form-check-label' do
Require users to prove ownership of custom domains Require users to prove ownership of custom domains
.form-text.text-muted .form-text.text-muted
Domain verification is an essential security measure for public GitLab Domain verification is an essential security measure for public GitLab
......
...@@ -5,8 +5,8 @@ ...@@ -5,8 +5,8 @@
.form-group.row .form-group.row
.offset-sm-2.col-sm-10 .offset-sm-2.col-sm-10
.form-check .form-check
= f.label :authorized_keys_enabled do = f.check_box :authorized_keys_enabled, class: 'form-check-input'
= f.check_box :authorized_keys_enabled = f.label :authorized_keys_enabled, class: 'form-check-label' do
Write to "authorized_keys" file Write to "authorized_keys" file
.form-text.text-muted .form-text.text-muted
By default, we write to the "authorized_keys" file to support Git By default, we write to the "authorized_keys" file to support Git
......
...@@ -5,8 +5,8 @@ ...@@ -5,8 +5,8 @@
.form-group.row .form-group.row
.offset-sm-2.col-sm-10 .offset-sm-2.col-sm-10
.form-check .form-check
= f.label :performance_bar_enabled do = f.check_box :performance_bar_enabled, class: 'form-check-input'
= f.check_box :performance_bar_enabled = f.label :performance_bar_enabled, class: 'form-check-label' do
Enable the Performance Bar Enable the Performance Bar
.form-group.row .form-group.row
= f.label :performance_bar_allowed_group_path, 'Allowed group', class: 'col-form-label col-sm-2' = f.label :performance_bar_allowed_group_path, 'Allowed group', class: 'col-form-label col-sm-2'
......
...@@ -5,8 +5,8 @@ ...@@ -5,8 +5,8 @@
.form-group.row .form-group.row
.offset-sm-2.col-sm-10 .offset-sm-2.col-sm-10
.form-check .form-check
= f.label :plantuml_enabled do = f.check_box :plantuml_enabled, class: 'form-check-input'
= f.check_box :plantuml_enabled = f.label :plantuml_enabled, class: 'form-check-label' do
Enable PlantUML Enable PlantUML
.form-group.row .form-group.row
= f.label :plantuml_url, 'PlantUML URL', class: 'col-form-label col-sm-2' = f.label :plantuml_url, 'PlantUML URL', class: 'col-form-label col-sm-2'
......
...@@ -14,8 +14,8 @@ ...@@ -14,8 +14,8 @@
.form-group.row .form-group.row
.offset-sm-2.col-sm-10 .offset-sm-2.col-sm-10
.form-check .form-check
= f.label :prometheus_metrics_enabled do = f.check_box :prometheus_metrics_enabled, class: 'form-check-input'
= f.check_box :prometheus_metrics_enabled = f.label :prometheus_metrics_enabled, class: 'form-check-label' do
Enable Prometheus Metrics Enable Prometheus Metrics
- unless Gitlab::Metrics.metrics_folder_present? - unless Gitlab::Metrics.metrics_folder_present?
.form-text.text-muted .form-text.text-muted
......
...@@ -7,8 +7,8 @@ ...@@ -7,8 +7,8 @@
.form-group.row .form-group.row
.offset-sm-2.col-sm-10 .offset-sm-2.col-sm-10
.form-check .form-check
= f.label :repository_checks_enabled do = f.check_box :repository_checks_enabled, class: 'form-check-input'
= f.check_box :repository_checks_enabled = f.label :repository_checks_enabled, class: 'form-check-label' do
Enable Repository Checks Enable Repository Checks
.form-text.text-muted .form-text.text-muted
GitLab will periodically run GitLab will periodically run
...@@ -25,8 +25,8 @@ ...@@ -25,8 +25,8 @@
.form-group.row .form-group.row
.offset-sm-2.col-sm-10 .offset-sm-2.col-sm-10
.form-check .form-check
= f.label :housekeeping_enabled do = f.check_box :housekeeping_enabled, class: 'form-check-input'
= f.check_box :housekeeping_enabled = f.label :housekeeping_enabled, class: 'form-check-label' do
Enable automatic repository housekeeping (git repack, git gc) Enable automatic repository housekeeping (git repack, git gc)
.form-text.text-muted .form-text.text-muted
If you keep automatic housekeeping disabled for a long time Git If you keep automatic housekeeping disabled for a long time Git
...@@ -34,8 +34,8 @@ ...@@ -34,8 +34,8 @@
repositories will use more disk space. We recommend to always leave repositories will use more disk space. We recommend to always leave
this enabled. this enabled.
.form-check .form-check
= f.label :housekeeping_bitmaps_enabled do = f.check_box :housekeeping_bitmaps_enabled, class: 'form-check-input'
= f.check_box :housekeeping_bitmaps_enabled = f.label :housekeeping_bitmaps_enabled, class: 'form-check-label' do
Enable Git pack file bitmap creation Enable Git pack file bitmap creation
.form-text.text-muted .form-text.text-muted
Creating pack file bitmaps makes housekeeping take a little longer but Creating pack file bitmaps makes housekeeping take a little longer but
......
= form_for @application_setting, url: admin_application_settings_path, html: { class: 'form-horizontal fieldset-form' } do |f| = form_for @application_setting, url: admin_application_settings_path do |f|
= form_errors(@application_setting) = form_errors(@application_setting)
%fieldset %fieldset
.form-group .form-group.row
= f.label :mirror_available, 'Enable mirror configuration', class: 'control-label col-sm-2' = f.label :mirror_available, 'Enable mirror configuration', class: 'control-label col-sm-4'
.col-sm-10 .col-sm-8
.form-check .form-check
= f.label :mirror_available do = f.check_box :mirror_available, class: 'form-check-input'
= f.check_box :mirror_available = f.label :mirror_available, class: 'form-check-label' do
Allow mirrors to be setup for projects Allow mirrors to be setup for projects
%span.form-text.text-muted %span.form-text.text-muted
If disabled, only admins will be able to setup mirrors in projects. If disabled, only admins will be able to setup mirrors in projects.
......
...@@ -6,8 +6,8 @@ ...@@ -6,8 +6,8 @@
.form-group.row .form-group.row
.offset-sm-2.col-sm-10 .offset-sm-2.col-sm-10
.form-check .form-check
= f.label :hashed_storage_enabled do = f.check_box :hashed_storage_enabled, class: 'form-check-input'
= f.check_box :hashed_storage_enabled = f.label :hashed_storage_enabled, class: 'form-check-label' do
Create new projects using hashed storage paths Create new projects using hashed storage paths
.form-text.text-muted .form-text.text-muted
Enable immutable, hash-based paths and repository names to store repositories on disk. This prevents Enable immutable, hash-based paths and repository names to store repositories on disk. This prevents
......
...@@ -5,16 +5,16 @@ ...@@ -5,16 +5,16 @@
.form-group.row .form-group.row
.offset-sm-2.col-sm-10 .offset-sm-2.col-sm-10
.form-check .form-check
= f.label :password_authentication_enabled_for_web do = f.check_box :password_authentication_enabled_for_web, class: 'form-check-input'
= f.check_box :password_authentication_enabled_for_web = f.label :password_authentication_enabled_for_web, class: 'form-check-label' do
Password authentication enabled for web interface Password authentication enabled for web interface
.form-text.text-muted .form-text.text-muted
When disabled, an external authentication provider must be used. When disabled, an external authentication provider must be used.
.form-group.row .form-group.row
.offset-sm-2.col-sm-10 .offset-sm-2.col-sm-10
.form-check .form-check
= f.label :password_authentication_enabled_for_git do = f.check_box :password_authentication_enabled_for_git, class: 'form-check-input'
= f.check_box :password_authentication_enabled_for_git = f.label :password_authentication_enabled_for_git, class: 'form-check-label' do
Password authentication enabled for Git over HTTP(S) Password authentication enabled for Git over HTTP(S)
.form-text.text-muted .form-text.text-muted
When disabled, a Personal Access Token When disabled, a Personal Access Token
...@@ -33,8 +33,8 @@ ...@@ -33,8 +33,8 @@
= f.label :two_factor_authentication, 'Two-factor authentication', class: 'col-form-label col-sm-2' = f.label :two_factor_authentication, 'Two-factor authentication', class: 'col-form-label col-sm-2'
.col-sm-10 .col-sm-10
.form-check .form-check
= f.label :require_two_factor_authentication do = f.check_box :require_two_factor_authentication, class: 'form-check-input'
= f.check_box :require_two_factor_authentication = f.label :require_two_factor_authentication, class: 'form-check-label' do
Require all users to setup Two-factor authentication Require all users to setup Two-factor authentication
.form-group.row .form-group.row
= f.label :two_factor_authentication, 'Two-factor grace period (hours)', class: 'col-form-label col-sm-2' = f.label :two_factor_authentication, 'Two-factor grace period (hours)', class: 'col-form-label col-sm-2'
......
...@@ -5,14 +5,14 @@ ...@@ -5,14 +5,14 @@
.form-group.row .form-group.row
.offset-sm-2.col-sm-10 .offset-sm-2.col-sm-10
.form-check .form-check
= f.label :signup_enabled do = f.check_box :signup_enabled, class: 'form-check-input'
= f.check_box :signup_enabled = f.label :signup_enabled, class: 'form-check-label' do
Sign-up enabled Sign-up enabled
.form-group.row .form-group.row
.offset-sm-2.col-sm-10 .offset-sm-2.col-sm-10
.form-check .form-check
= f.label :send_user_confirmation_email do = f.check_box :send_user_confirmation_email, class: 'form-check-input'
= f.check_box :send_user_confirmation_email = f.label :send_user_confirmation_email, class: 'form-check-label' do
Send confirmation email on sign-up Send confirmation email on sign-up
.form-group.row .form-group.row
= f.label :domain_whitelist, 'Whitelisted domains for sign-ups', class: 'col-form-label col-sm-2' = f.label :domain_whitelist, 'Whitelisted domains for sign-ups', class: 'col-form-label col-sm-2'
...@@ -23,19 +23,19 @@ ...@@ -23,19 +23,19 @@
= f.label :domain_blacklist_enabled, 'Domain Blacklist', class: 'col-form-label col-sm-2' = f.label :domain_blacklist_enabled, 'Domain Blacklist', class: 'col-form-label col-sm-2'
.col-sm-10 .col-sm-10
.form-check .form-check
= f.label :domain_blacklist_enabled do = f.check_box :domain_blacklist_enabled, class: 'form-check-input'
= f.check_box :domain_blacklist_enabled = f.label :domain_blacklist_enabled, class: 'form-check-label' do
Enable domain blacklist for sign ups Enable domain blacklist for sign ups
.form-group.row .form-group.row
.offset-sm-2.col-sm-10 .offset-sm-2.col-sm-10
.form-check .form-check
= label_tag :blacklist_type_file do = radio_button_tag :blacklist_type, :file, class: 'form-check-input'
= radio_button_tag :blacklist_type, :file = label_tag :blacklist_type_file, class: 'form-check-label' do
.option-title .option-title
Upload blacklist file Upload blacklist file
.form-check .form-check
= label_tag :blacklist_type_raw do = radio_button_tag :blacklist_type, :raw, @application_setting.domain_blacklist.present? || @application_setting.domain_blacklist.blank?, class: 'form-check-input'
= radio_button_tag :blacklist_type, :raw, @application_setting.domain_blacklist.present? || @application_setting.domain_blacklist.blank? = label_tag :blacklist_type_raw, class: 'form-check-label' do
.option-title .option-title
Enter blacklist manually Enter blacklist manually
.form-group.row.blacklist-file .form-group.row.blacklist-file
......
...@@ -5,8 +5,8 @@ ...@@ -5,8 +5,8 @@
.form-group.row .form-group.row
.offset-sm-2.col-sm-10 .offset-sm-2.col-sm-10
.form-check .form-check
= f.label :recaptcha_enabled do = f.check_box :recaptcha_enabled, class: 'form-check-input'
= f.check_box :recaptcha_enabled = f.label :recaptcha_enabled, class: 'form-check-label' do
Enable reCAPTCHA Enable reCAPTCHA
%span.form-text.text-muted#recaptcha_help_block Helps prevent bots from creating accounts %span.form-text.text-muted#recaptcha_help_block Helps prevent bots from creating accounts
...@@ -26,8 +26,8 @@ ...@@ -26,8 +26,8 @@
.form-group.row .form-group.row
.offset-sm-2.col-sm-10 .offset-sm-2.col-sm-10
.form-check .form-check
= f.label :akismet_enabled do = f.check_box :akismet_enabled, class: 'form-check-input'
= f.check_box :akismet_enabled = f.label :akismet_enabled, class: 'form-check-label' do
Enable Akismet Enable Akismet
%span.form-text.text-muted#akismet_help_block Helps prevent bots from creating issues %span.form-text.text-muted#akismet_help_block Helps prevent bots from creating issues
...@@ -42,8 +42,8 @@ ...@@ -42,8 +42,8 @@
.form-group.row .form-group.row
.offset-sm-2.col-sm-10 .offset-sm-2.col-sm-10
.form-check .form-check
= f.label :unique_ips_limit_enabled do = f.check_box :unique_ips_limit_enabled, class: 'form-check-input'
= f.check_box :unique_ips_limit_enabled = f.label :unique_ips_limit_enabled, class: 'form-check-label' do
Limit sign in from multiple ips Limit sign in from multiple ips
%span.form-text.text-muted#unique_ip_help_block %span.form-text.text-muted#unique_ip_help_block
Helps prevent malicious users hide their activity Helps prevent malicious users hide their activity
......
= form_for @application_setting, url: admin_application_settings_path, html: { class: 'form-horizontal fieldset-form' } do |f| = form_for @application_setting, url: admin_application_settings_path do |f|
= form_errors(@application_setting) = form_errors(@application_setting)
%fieldset %fieldset
.form-group .form-group.row
.col-sm-12 .col-sm-12
.form-check .form-check
= f.label :enforce_terms do = f.check_box :enforce_terms, class: 'form-check-input'
= f.check_box :enforce_terms = f.label :enforce_terms, class: 'form-check-label' do
= _("Require all users to accept Terms of Service when they access GitLab.") = _("Require all users to accept Terms of Service when they access GitLab.")
.form-text.text-muted .form-text.text-muted
= _("When enabled, users cannot use GitLab until the terms have been accepted.") = _("When enabled, users cannot use GitLab until the terms have been accepted.")
.form-group .form-group.row
.col-sm-12 .col-sm-12
= f.label :terms do = f.label :terms do
= _("Terms of Service Agreement") = _("Terms of Service Agreement")
......
...@@ -5,8 +5,8 @@ ...@@ -5,8 +5,8 @@
.form-group.row .form-group.row
.offset-sm-2.col-sm-10 .offset-sm-2.col-sm-10
.form-check .form-check
= f.label :version_check_enabled do = f.check_box :version_check_enabled, class: 'form-check-input'
= f.check_box :version_check_enabled = f.label :version_check_enabled, class: 'form-check-label' do
Enable version check Enable version check
.form-text.text-muted .form-text.text-muted
GitLab will inform you if a new version is available. GitLab will inform you if a new version is available.
...@@ -16,8 +16,8 @@ ...@@ -16,8 +16,8 @@
.offset-sm-2.col-sm-10 .offset-sm-2.col-sm-10
- can_be_configured = @application_setting.usage_ping_can_be_configured? - can_be_configured = @application_setting.usage_ping_can_be_configured?
.form-check .form-check
= f.label :usage_ping_enabled do = f.check_box :usage_ping_enabled, disabled: !can_be_configured, class: 'form-check-input'
= f.check_box :usage_ping_enabled, disabled: !can_be_configured = f.label :usage_ping_enabled, class: 'form-check-label' do
Enable usage ping Enable usage ping
.form-text.text-muted .form-text.text-muted
- if can_be_configured - if can_be_configured
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment