Commit 54a35141 authored by Luke Bennett's avatar Luke Bennett

Merge remote-tracking branch 'origin/master' into deprecation-warning-for-dynamic-milestones

parents daf2a79b 9af61b3f
{ {
"presets": [ "presets": [["latest", { "es2015": { "modules": false } }], "stage-2"],
["latest", { "es2015": { "modules": false } }],
"stage-2"
],
"env": { "env": {
"coverage": { "coverage": {
"plugins": [ "plugins": [
["istanbul", { [
"exclude": [ "istanbul",
"spec/javascripts/**/*", {
"app/assets/javascripts/locale/**/app.js" "exclude": ["spec/javascripts/**/*", "app/assets/javascripts/locale/**/app.js"]
] }
}], ],
["transform-define", { [
"process.env.BABEL_ENV": "coverage" "transform-define",
}] {
"process.env.BABEL_ENV": "coverage"
}
]
] ]
} }
} }
......
/app/assets/javascripts/locale/**/app.js
/config/
/builds/ /builds/
/coverage/ /coverage/
/coverage-javascript/ /coverage-javascript/
/node_modules/ /node_modules/
/public/ /public/
/scripts/
/tmp/ /tmp/
/vendor/ /vendor/
karma.config.js karma.config.js
webpack.config.js webpack.config.js
svg.config.js
/app/assets/javascripts/locale/**/app.js
...@@ -544,7 +544,7 @@ migration:path-mysql: ...@@ -544,7 +544,7 @@ migration:path-mysql:
.db-rollback: &db-rollback .db-rollback: &db-rollback
<<: *dedicated-no-docs-pull-cache-job <<: *dedicated-no-docs-pull-cache-job
script: script:
- bundle exec rake db:rollback STEP=119 - bundle exec rake db:migrate VERSION=20170523121229
- bundle exec rake db:migrate - bundle exec rake db:migrate
db:rollback-pg: db:rollback-pg:
......
/app/assets/javascripts/locale/**/app.js
/node_modules/
/public/
/vendor/
/tmp/
{ {
"printWidth": 100,
"singleQuote": true, "singleQuote": true,
"trailingComma": "all" "trailingComma": "es5",
"overrides": [
{
"files": ["**/app/**/*", "**/spec/**/*"],
"options": {
"trailingComma": "all"
}
}
]
} }
...@@ -118,6 +118,9 @@ Gitlab/ModuleWithInstanceVariables: ...@@ -118,6 +118,9 @@ Gitlab/ModuleWithInstanceVariables:
- spec/support/**/*.rb - spec/support/**/*.rb
- features/steps/**/*.rb - features/steps/**/*.rb
Gitlab/HTTParty:
Enabled: true
GitlabSecurity/PublicSend: GitlabSecurity/PublicSend:
Enabled: true Enabled: true
Exclude: Exclude:
......
This diff is collapsed.
...@@ -38,7 +38,7 @@ gem 'devise', '~> 4.2' ...@@ -38,7 +38,7 @@ gem 'devise', '~> 4.2'
gem 'doorkeeper', '~> 4.3' gem 'doorkeeper', '~> 4.3'
gem 'doorkeeper-openid_connect', '~> 1.3' gem 'doorkeeper-openid_connect', '~> 1.3'
gem 'omniauth', '~> 1.8' gem 'omniauth', '~> 1.8'
gem 'omniauth-auth0', '~> 1.4.1' gem 'omniauth-auth0', '~> 2.0.0'
gem 'omniauth-azure-oauth2', '~> 0.0.9' gem 'omniauth-azure-oauth2', '~> 0.0.9'
gem 'omniauth-cas3', '~> 1.1.4' gem 'omniauth-cas3', '~> 1.1.4'
gem 'omniauth-facebook', '~> 4.0.0' gem 'omniauth-facebook', '~> 4.0.0'
...@@ -231,7 +231,7 @@ gem 'sanitize', '~> 2.0' ...@@ -231,7 +231,7 @@ gem 'sanitize', '~> 2.0'
gem 'babosa', '~> 1.0.2' gem 'babosa', '~> 1.0.2'
# Sanitizes SVG input # Sanitizes SVG input
gem 'loofah', '~> 2.0.3' gem 'loofah', '~> 2.2'
# Working with license # Working with license
gem 'licensee', '~> 8.9' gem 'licensee', '~> 8.9'
......
...@@ -143,6 +143,7 @@ GEM ...@@ -143,6 +143,7 @@ GEM
connection_pool (2.2.1) connection_pool (2.2.1)
crack (0.4.3) crack (0.4.3)
safe_yaml (~> 1.0.0) safe_yaml (~> 1.0.0)
crass (1.0.3)
creole (0.5.0) creole (0.5.0)
css_parser (1.5.0) css_parser (1.5.0)
addressable addressable
...@@ -485,7 +486,8 @@ GEM ...@@ -485,7 +486,8 @@ GEM
actionpack (>= 4, < 5.2) actionpack (>= 4, < 5.2)
activesupport (>= 4, < 5.2) activesupport (>= 4, < 5.2)
railties (>= 4, < 5.2) railties (>= 4, < 5.2)
loofah (2.0.3) loofah (2.2.2)
crass (~> 1.0.2)
nokogiri (>= 1.5.9) nokogiri (>= 1.5.9)
mail (2.7.0) mail (2.7.0)
mini_mime (>= 0.1.1) mini_mime (>= 0.1.1)
...@@ -527,8 +529,8 @@ GEM ...@@ -527,8 +529,8 @@ GEM
omniauth (1.8.1) omniauth (1.8.1)
hashie (>= 3.4.6, < 3.6.0) hashie (>= 3.4.6, < 3.6.0)
rack (>= 1.6.2, < 3) rack (>= 1.6.2, < 3)
omniauth-auth0 (1.4.1) omniauth-auth0 (2.0.0)
omniauth-oauth2 (~> 1.1) omniauth-oauth2 (~> 1.4)
omniauth-authentiq (0.3.1) omniauth-authentiq (0.3.1)
omniauth-oauth2 (~> 1.3, >= 1.3.1) omniauth-oauth2 (~> 1.3, >= 1.3.1)
omniauth-azure-oauth2 (0.0.9) omniauth-azure-oauth2 (0.0.9)
...@@ -679,8 +681,8 @@ GEM ...@@ -679,8 +681,8 @@ GEM
activesupport (>= 4.2.0, < 5.0) activesupport (>= 4.2.0, < 5.0)
nokogiri (~> 1.6) nokogiri (~> 1.6)
rails-deprecated_sanitizer (>= 1.0.1) rails-deprecated_sanitizer (>= 1.0.1)
rails-html-sanitizer (1.0.3) rails-html-sanitizer (1.0.4)
loofah (~> 2.0) loofah (~> 2.2, >= 2.2.2)
rails-i18n (4.0.9) rails-i18n (4.0.9)
i18n (~> 0.7) i18n (~> 0.7)
railties (~> 4.0) railties (~> 4.0)
...@@ -1093,7 +1095,7 @@ DEPENDENCIES ...@@ -1093,7 +1095,7 @@ DEPENDENCIES
license_finder (~> 3.1) license_finder (~> 3.1)
licensee (~> 8.9) licensee (~> 8.9)
lograge (~> 0.5) lograge (~> 0.5)
loofah (~> 2.0.3) loofah (~> 2.2)
mail_room (~> 0.9.1) mail_room (~> 0.9.1)
method_source (~> 0.8) method_source (~> 0.8)
minitest (~> 5.7.0) minitest (~> 5.7.0)
...@@ -1105,7 +1107,7 @@ DEPENDENCIES ...@@ -1105,7 +1107,7 @@ DEPENDENCIES
oauth2 (~> 1.4) oauth2 (~> 1.4)
octokit (~> 4.8) octokit (~> 4.8)
omniauth (~> 1.8) omniauth (~> 1.8)
omniauth-auth0 (~> 1.4.1) omniauth-auth0 (~> 2.0.0)
omniauth-authentiq (~> 0.3.1) omniauth-authentiq (~> 0.3.1)
omniauth-azure-oauth2 (~> 0.0.9) omniauth-azure-oauth2 (~> 0.0.9)
omniauth-cas3 (~> 1.1.4) omniauth-cas3 (~> 1.1.4)
......
10.6.0-pre 10.7.0-pre
import $ from 'jquery'; import $ from 'jquery';
import Vue from 'vue'; import Vue from 'vue';
import userAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue'; import UserAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue';
import eventHub from '../eventhub'; import eventHub from '../eventhub';
const Store = gl.issueBoards.BoardsStore; const Store = gl.issueBoards.BoardsStore;
...@@ -45,7 +45,7 @@ gl.issueBoards.IssueCardInner = Vue.extend({ ...@@ -45,7 +45,7 @@ gl.issueBoards.IssueCardInner = Vue.extend({
}; };
}, },
components: { components: {
userAvatarLink, UserAvatarLink,
}, },
computed: { computed: {
numberOverLimit() { numberOverLimit() {
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
import $ from 'jquery'; import $ from 'jquery';
import { s__ } from '~/locale'; import { s__ } from '~/locale';
import loadingIcon from '~/vue_shared/components/loading_icon.vue'; import loadingIcon from '~/vue_shared/components/loading_icon.vue';
import modal from '~/vue_shared/components/modal.vue'; import DeprecatedModal from '~/vue_shared/components/deprecated_modal.vue';
import { getParameterByName } from '~/lib/utils/common_utils'; import { getParameterByName } from '~/lib/utils/common_utils';
import { mergeUrlParams } from '~/lib/utils/url_utility'; import { mergeUrlParams } from '~/lib/utils/url_utility';
...@@ -15,7 +15,7 @@ import groupsComponent from './groups.vue'; ...@@ -15,7 +15,7 @@ import groupsComponent from './groups.vue';
export default { export default {
components: { components: {
loadingIcon, loadingIcon,
modal, DeprecatedModal,
groupsComponent, groupsComponent,
}, },
props: { props: {
...@@ -52,8 +52,9 @@ export default { ...@@ -52,8 +52,9 @@ export default {
}, },
}, },
created() { created() {
this.searchEmptyMessage = this.hideProjects ? this.searchEmptyMessage = this.hideProjects
COMMON_STR.GROUP_SEARCH_EMPTY : COMMON_STR.GROUP_PROJECT_SEARCH_EMPTY; ? COMMON_STR.GROUP_SEARCH_EMPTY
: COMMON_STR.GROUP_PROJECT_SEARCH_EMPTY;
eventHub.$on('fetchPage', this.fetchPage); eventHub.$on('fetchPage', this.fetchPage);
eventHub.$on('toggleChildren', this.toggleChildren); eventHub.$on('toggleChildren', this.toggleChildren);
...@@ -72,22 +73,30 @@ export default { ...@@ -72,22 +73,30 @@ export default {
eventHub.$off('updateGroups', this.updateGroups); eventHub.$off('updateGroups', this.updateGroups);
}, },
methods: { methods: {
fetchGroups({ parentId, page, filterGroupsBy, sortBy, archived, updatePagination }) { fetchGroups({
return this.service.getGroups(parentId, page, filterGroupsBy, sortBy, archived) parentId,
.then((res) => { page,
if (updatePagination) { filterGroupsBy,
this.updatePagination(res.headers); sortBy,
} archived,
updatePagination,
}) {
return this.service
.getGroups(parentId, page, filterGroupsBy, sortBy, archived)
.then(res => {
if (updatePagination) {
this.updatePagination(res.headers);
}
return res; return res;
}) })
.then(res => res.json()) .then(res => res.json())
.catch(() => { .catch(() => {
this.isLoading = false; this.isLoading = false;
$.scrollTo(0); $.scrollTo(0);
Flash(COMMON_STR.FAILURE); Flash(COMMON_STR.FAILURE);
}); });
}, },
fetchAllGroups() { fetchAllGroups() {
const page = getParameterByName('page') || null; const page = getParameterByName('page') || null;
...@@ -103,7 +112,7 @@ export default { ...@@ -103,7 +112,7 @@ export default {
sortBy, sortBy,
archived, archived,
updatePagination: true, updatePagination: true,
}).then((res) => { }).then(res => {
this.isLoading = false; this.isLoading = false;
this.updateGroups(res, Boolean(filterGroupsBy)); this.updateGroups(res, Boolean(filterGroupsBy));
}); });
...@@ -118,14 +127,18 @@ export default { ...@@ -118,14 +127,18 @@ export default {
sortBy, sortBy,
archived, archived,
updatePagination: true, updatePagination: true,
}).then((res) => { }).then(res => {
this.isLoading = false; this.isLoading = false;
$.scrollTo(0); $.scrollTo(0);
const currentPath = mergeUrlParams({ page }, window.location.href); const currentPath = mergeUrlParams({ page }, window.location.href);
window.history.replaceState({ window.history.replaceState(
page: currentPath, {
}, document.title, currentPath); page: currentPath,
},
document.title,
currentPath,
);
this.updateGroups(res); this.updateGroups(res);
}); });
...@@ -138,11 +151,13 @@ export default { ...@@ -138,11 +151,13 @@ export default {
// eslint-disable-next-line promise/catch-or-return // eslint-disable-next-line promise/catch-or-return
this.fetchGroups({ this.fetchGroups({
parentId: parentGroup.id, parentId: parentGroup.id,
}).then((res) => { })
this.store.setGroupChildren(parentGroup, res); .then(res => {
}).catch(() => { this.store.setGroupChildren(parentGroup, res);
parentGroup.isChildrenLoading = false; })
}); .catch(() => {
parentGroup.isChildrenLoading = false;
});
} else { } else {
parentGroup.isOpen = true; parentGroup.isOpen = true;
} }
...@@ -154,7 +169,11 @@ export default { ...@@ -154,7 +169,11 @@ export default {
this.targetGroup = group; this.targetGroup = group;
this.targetParentGroup = parentGroup; this.targetParentGroup = parentGroup;
this.showModal = true; this.showModal = true;
this.groupLeaveConfirmationMessage = s__(`GroupsTree|Are you sure you want to leave the "${group.fullName}" group?`); this.groupLeaveConfirmationMessage = s__(
`GroupsTree|Are you sure you want to leave the "${
group.fullName
}" group?`,
);
}, },
hideLeaveGroupModal() { hideLeaveGroupModal() {
this.showModal = false; this.showModal = false;
...@@ -162,14 +181,15 @@ export default { ...@@ -162,14 +181,15 @@ export default {
leaveGroup() { leaveGroup() {
this.showModal = false; this.showModal = false;
this.targetGroup.isBeingRemoved = true; this.targetGroup.isBeingRemoved = true;
this.service.leaveGroup(this.targetGroup.leavePath) this.service
.leaveGroup(this.targetGroup.leavePath)
.then(res => res.json()) .then(res => res.json())
.then((res) => { .then(res => {
$.scrollTo(0); $.scrollTo(0);
this.store.removeGroup(this.targetGroup, this.targetParentGroup); this.store.removeGroup(this.targetGroup, this.targetParentGroup);
Flash(res.notice, 'notice'); Flash(res.notice, 'notice');
}) })
.catch((err) => { .catch(err => {
let message = COMMON_STR.FAILURE; let message = COMMON_STR.FAILURE;
if (err.status === 403) { if (err.status === 403) {
message = COMMON_STR.LEAVE_FORBIDDEN; message = COMMON_STR.LEAVE_FORBIDDEN;
...@@ -208,8 +228,8 @@ export default { ...@@ -208,8 +228,8 @@ export default {
:search-empty-message="searchEmptyMessage" :search-empty-message="searchEmptyMessage"
:page-info="pageInfo" :page-info="pageInfo"
/> />
<modal <deprecated-modal
v-if="showModal" v-show="showModal"
kind="warning" kind="warning"
:primary-button-label="__('Leave')" :primary-button-label="__('Leave')"
:title="__('Are you sure?')" :title="__('Are you sure?')"
......
<script> <script>
import { __ } from '~/locale'; import { __ } from '~/locale';
import modal from '~/vue_shared/components/modal.vue'; import DeprecatedModal from '~/vue_shared/components/deprecated_modal.vue';
export default { export default {
components: { components: {
modal, DeprecatedModal,
},
props: {
branchId: {
type: String,
required: true,
}, },
props: { type: {
branchId: { type: String,
type: String, required: true,
required: true,
},
type: {
type: String,
required: true,
},
path: {
type: String,
required: true,
},
}, },
data() { path: {
return { type: String,
entryName: this.path !== '' ? `${this.path}/` : '', required: true,
};
}, },
computed: { },
modalTitle() { data() {
if (this.type === 'tree') { return {
return __('Create new directory'); entryName: this.path !== '' ? `${this.path}/` : '',
} };
},
computed: {
modalTitle() {
if (this.type === 'tree') {
return __('Create new directory');
}
return __('Create new file'); return __('Create new file');
}, },
buttonLabel() { buttonLabel() {
if (this.type === 'tree') { if (this.type === 'tree') {
return __('Create directory'); return __('Create directory');
} }
return __('Create file');
},
formLabelName() {
if (this.type === 'tree') {
return __('Directory name');
}
return __('File name'); return __('Create file');
},
}, },
mounted() { formLabelName() {
this.$refs.fieldName.focus(); if (this.type === 'tree') {
return __('Directory name');
}
return __('File name');
}, },
methods: { },
createEntryInStore() { mounted() {
this.$emit('create', { this.$refs.fieldName.focus();
branchId: this.branchId, },
name: this.entryName, methods: {
type: this.type, createEntryInStore() {
}); this.$emit('create', {
branchId: this.branchId,
name: this.entryName,
type: this.type,
});
this.hideModal(); this.hideModal();
}, },
hideModal() { hideModal() {
this.$emit('hide'); this.$emit('hide');
},
}, },
}; },
};
</script> </script>
<template> <template>
<modal <deprecated-modal
:title="modalTitle" :title="modalTitle"
:primary-button-label="buttonLabel" :primary-button-label="buttonLabel"
kind="success" kind="success"
...@@ -95,5 +95,5 @@ ...@@ -95,5 +95,5 @@
</div> </div>
</fieldset> </fieldset>
</form> </form>
</modal> </deprecated-modal>
</template> </template>
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
import { mapState, mapActions, mapGetters } from 'vuex'; import { mapState, mapActions, mapGetters } from 'vuex';
import tooltip from '~/vue_shared/directives/tooltip'; import tooltip from '~/vue_shared/directives/tooltip';
import icon from '~/vue_shared/components/icon.vue'; import icon from '~/vue_shared/components/icon.vue';
import modal from '~/vue_shared/components/modal.vue'; import DeprecatedModal from '~/vue_shared/components/deprecated_modal.vue';
import LoadingButton from '~/vue_shared/components/loading_button.vue'; import LoadingButton from '~/vue_shared/components/loading_button.vue';
import commitFilesList from './commit_sidebar/list.vue'; import commitFilesList from './commit_sidebar/list.vue';
import * as consts from '../stores/modules/commit/constants'; import * as consts from '../stores/modules/commit/constants';
...@@ -10,7 +10,7 @@ import Actions from './commit_sidebar/actions.vue'; ...@@ -10,7 +10,7 @@ import Actions from './commit_sidebar/actions.vue';
export default { export default {
components: { components: {
modal, DeprecatedModal,
icon, icon,
commitFilesList, commitFilesList,
Actions, Actions,
...@@ -37,23 +37,20 @@ export default { ...@@ -37,23 +37,20 @@ export default {
'lastCommitMsg', 'lastCommitMsg',
'changedFiles', 'changedFiles',
]), ]),
...mapState('commit', [ ...mapState('commit', ['commitMessage', 'submitCommitLoading']),
'commitMessage',
'submitCommitLoading',
]),
...mapGetters('commit', [ ...mapGetters('commit', [
'commitButtonDisabled', 'commitButtonDisabled',
'discardDraftButtonDisabled', 'discardDraftButtonDisabled',
'branchName', 'branchName',
]), ]),
statusSvg() { statusSvg() {
return this.lastCommitMsg ? this.committedStateSvgPath : this.noChangesStateSvgPath; return this.lastCommitMsg
? this.committedStateSvgPath
: this.noChangesStateSvgPath;
}, },
}, },
methods: { methods: {
...mapActions([ ...mapActions(['setPanelCollapsedStatus']),
'setPanelCollapsedStatus',
]),
...mapActions('commit', [ ...mapActions('commit', [
'updateCommitMessage', 'updateCommitMessage',
'discardDraft', 'discardDraft',
...@@ -67,8 +64,9 @@ export default { ...@@ -67,8 +64,9 @@ export default {
}); });
}, },
forceCreateNewBranch() { forceCreateNewBranch() {
return this.updateCommitAction(consts.COMMIT_TO_NEW_BRANCH) return this.updateCommitAction(consts.COMMIT_TO_NEW_BRANCH).then(() =>
.then(() => this.commitChanges()); this.commitChanges(),
);
}, },
}, },
}; };
...@@ -81,7 +79,7 @@ export default { ...@@ -81,7 +79,7 @@ export default {
'multi-file-commit-empty-state-container': !changedFiles.length 'multi-file-commit-empty-state-container': !changedFiles.length
}" }"
> >
<modal <deprecated-modal
id="ide-create-branch-modal" id="ide-create-branch-modal"
:primary-button-label="__('Create new branch')" :primary-button-label="__('Create new branch')"
kind="success" kind="success"
...@@ -92,7 +90,7 @@ export default { ...@@ -92,7 +90,7 @@ export default {
{{ __(`This branch has changed since you started editing. {{ __(`This branch has changed since you started editing.
Would you like to create a new branch?`) }} Would you like to create a new branch?`) }}
</template> </template>
</modal> </deprecated-modal>
<commit-files-list <commit-files-list
title="Staged" title="Staged"
:file-list="changedFiles" :file-list="changedFiles"
......
...@@ -1727,6 +1727,7 @@ export default class Notes { ...@@ -1727,6 +1727,7 @@ export default class Notes {
// Get Form metadata // Get Form metadata
const $submitBtn = $(e.target); const $submitBtn = $(e.target);
$submitBtn.prop('disabled', true);
let $form = $submitBtn.parents('form'); let $form = $submitBtn.parents('form');
const $closeBtn = $form.find('.js-note-target-close'); const $closeBtn = $form.find('.js-note-target-close');
const isDiscussionNote = const isDiscussionNote =
...@@ -1761,7 +1762,6 @@ export default class Notes { ...@@ -1761,7 +1762,6 @@ export default class Notes {
// If comment is to resolve discussion, disable submit buttons while // If comment is to resolve discussion, disable submit buttons while
// comment posting is finished. // comment posting is finished.
if (isDiscussionResolve) { if (isDiscussionResolve) {
$submitBtn.disable();
$form.find('.js-comment-submit-button').disable(); $form.find('.js-comment-submit-button').disable();
} }
...@@ -1816,6 +1816,7 @@ export default class Notes { ...@@ -1816,6 +1816,7 @@ export default class Notes {
.then(res => { .then(res => {
const note = res.data; const note = res.data;
$submitBtn.prop('disabled', false);
// Submission successful! remove placeholder // Submission successful! remove placeholder
$notesContainer.find(`#${noteUniqueId}`).remove(); $notesContainer.find(`#${noteUniqueId}`).remove();
...@@ -1899,7 +1900,7 @@ export default class Notes { ...@@ -1899,7 +1900,7 @@ export default class Notes {
.catch(() => { .catch(() => {
// Submission failed, remove placeholder note and show Flash error message // Submission failed, remove placeholder note and show Flash error message
$notesContainer.find(`#${noteUniqueId}`).remove(); $notesContainer.find(`#${noteUniqueId}`).remove();
$submitBtn.prop('disabled', false);
const blurEvent = new CustomEvent('blur.imageDiff', { const blurEvent = new CustomEvent('blur.imageDiff', {
detail: e, detail: e,
}); });
......
<script> <script>
import _ from 'underscore'; import _ from 'underscore';
import modal from '~/vue_shared/components/modal.vue'; import DeprecatedModal from '~/vue_shared/components/deprecated_modal.vue';
import { s__, sprintf } from '~/locale'; import { s__, sprintf } from '~/locale';
export default { export default {
components: { components: {
modal, DeprecatedModal,
}, },
props: { props: {
deleteProjectUrl: { deleteProjectUrl: {
...@@ -79,7 +79,7 @@ ...@@ -79,7 +79,7 @@
</script> </script>
<template> <template>
<modal <deprecated-modal
id="delete-project-modal" id="delete-project-modal"
:title="title" :title="title"
:text="text" :text="text"
...@@ -121,5 +121,5 @@ ...@@ -121,5 +121,5 @@
/> />
</form> </form>
</template> </template>
</modal> </deprecated-modal>
</template> </template>
<script> <script>
import _ from 'underscore'; import _ from 'underscore';
import modal from '~/vue_shared/components/modal.vue'; import DeprecatedModal from '~/vue_shared/components/deprecated_modal.vue';
import { s__, sprintf } from '~/locale'; import { s__, sprintf } from '~/locale';
export default { export default {
components: { components: {
modal, DeprecatedModal,
}, },
props: { props: {
deleteUserUrl: { deleteUserUrl: {
...@@ -113,7 +113,7 @@ ...@@ -113,7 +113,7 @@
</script> </script>
<template> <template>
<modal <deprecated-modal
id="delete-user-modal" id="delete-user-modal"
:title="title" :title="title"
:text="text" :text="text"
...@@ -170,5 +170,5 @@ ...@@ -170,5 +170,5 @@
{{ secondaryButtonLabel }} {{ secondaryButtonLabel }}
</button> </button>
</template> </template>
</modal> </deprecated-modal>
</template> </template>
...@@ -2,14 +2,14 @@ ...@@ -2,14 +2,14 @@
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
import Flash from '~/flash'; import Flash from '~/flash';
import modal from '~/vue_shared/components/modal.vue'; import DeprecatedModal from '~/vue_shared/components/deprecated_modal.vue';
import { n__, s__, sprintf } from '~/locale'; import { n__, s__, sprintf } from '~/locale';
import { redirectTo } from '~/lib/utils/url_utility'; import { redirectTo } from '~/lib/utils/url_utility';
import eventHub from '../event_hub'; import eventHub from '../event_hub';
export default { export default {
components: { components: {
modal, DeprecatedModal,
}, },
props: { props: {
issueCount: { issueCount: {
...@@ -92,7 +92,7 @@ Once deleted, it cannot be undone or recovered.`), ...@@ -92,7 +92,7 @@ Once deleted, it cannot be undone or recovered.`),
</script> </script>
<template> <template>
<modal <deprecated-modal
id="delete-milestone-modal" id="delete-milestone-modal"
:title="title" :title="title"
:text="text" :text="text"
...@@ -106,5 +106,5 @@ Once deleted, it cannot be undone or recovered.`), ...@@ -106,5 +106,5 @@ Once deleted, it cannot be undone or recovered.`),
<p v-html="props.text"></p> <p v-html="props.text"></p>
</template> </template>
</modal> </deprecated-modal>
</template> </template>
...@@ -27,12 +27,21 @@ export default { ...@@ -27,12 +27,21 @@ export default {
required: true, required: true,
}, },
}, },
computed: {
metricDetails() {
return this.currentRequest.details[this.metric];
},
detailsList() {
return this.metricDetails[this.details];
},
},
}; };
</script> </script>
<template> <template>
<div <div
:id="`peek-view-${metric}`" :id="`peek-view-${metric}`"
class="view" class="view"
v-if="currentRequest.details"
> >
<button <button
:data-target="`#modal-peek-${metric}-details`" :data-target="`#modal-peek-${metric}-details`"
...@@ -40,34 +49,40 @@ export default { ...@@ -40,34 +49,40 @@ export default {
type="button" type="button"
data-toggle="modal" data-toggle="modal"
> >
<span {{ metricDetails.duration }}
v-if="currentRequest.details" /
class="bold" {{ metricDetails.calls }}
>
{{ currentRequest.details[metric].duration }}
/
{{ currentRequest.details[metric].calls }}
</span>
</button> </button>
<gl-modal <gl-modal
v-if="currentRequest.details"
:id="`modal-peek-${metric}-details`" :id="`modal-peek-${metric}-details`"
:header-title-text="header" :header-title-text="header"
class="performance-bar-modal" class="performance-bar-modal"
> >
<table class="table"> <table
<tr class="table"
v-for="(item, index) in currentRequest.details[metric][details]" >
:key="index" <template v-if="detailsList.length">
> <tr
<td><strong>{{ item.duration }}ms</strong></td> v-for="(item, index) in detailsList"
<td :key="index"
v-for="key in keys"
:key="key"
> >
{{ item[key] }} <td><strong>{{ item.duration }}ms</strong></td>
</td> <td
</tr> v-for="key in keys"
:key="key"
class="break-word"
>
{{ item[key] }}
</td>
</tr>
</template>
<template v-else>
<tr>
<td>
No {{ header.toLowerCase() }} for this request.
</td>
</tr>
</template>
</table> </table>
<div slot="footer"> <div slot="footer">
......
...@@ -113,27 +113,21 @@ export default { ...@@ -113,27 +113,21 @@ export default {
id="js-peek" id="js-peek"
:class="env" :class="env"
> >
<request-selector
v-if="currentRequest"
:current-request="currentRequest"
:requests="requests"
@change-current-request="changeCurrentRequest"
/>
<div
id="peek-view-host"
class="view prepend-left-5"
>
<span
v-if="currentRequest && currentRequest.details"
class="current-host"
>
{{ currentRequest.details.host.hostname }}
</span>
</div>
<div <div
v-if="currentRequest" v-if="currentRequest"
class="wrapper" class="container-fluid container-limited"
> >
<div
id="peek-view-host"
class="view"
>
<span
v-if="currentRequest.details"
class="current-host"
>
{{ currentRequest.details.host.hostname }}
</span>
</div>
<upstream-performance-bar <upstream-performance-bar
v-if="initialRequest && currentRequest.details" v-if="initialRequest && currentRequest.details"
/> />
...@@ -186,6 +180,12 @@ export default { ...@@ -186,6 +180,12 @@ export default {
gc gc
</span> </span>
</div> </div>
<request-selector
v-if="currentRequest"
:current-request="currentRequest"
:requests="requests"
@change-current-request="changeCurrentRequest"
/>
</div> </div>
</div> </div>
</template> </template>
...@@ -37,7 +37,7 @@ export default { ...@@ -37,7 +37,7 @@ export default {
<template> <template>
<div <div
id="peek-request-selector" id="peek-request-selector"
class="append-right-5 pull-right" class="pull-right"
> >
<select v-model="currentRequestId"> <select v-model="currentRequestId">
<option <option
......
...@@ -5,6 +5,8 @@ export default { ...@@ -5,6 +5,8 @@ export default {
.getElementById('peek-view-performance-bar') .getElementById('peek-view-performance-bar')
.cloneNode(true); .cloneNode(true);
upstreamPerformanceBar.classList.remove('hidden');
this.$refs.wrapper.appendChild(upstreamPerformanceBar); this.$refs.wrapper.appendChild(upstreamPerformanceBar);
}, },
}; };
......
...@@ -4,9 +4,9 @@ import Vue from 'vue'; ...@@ -4,9 +4,9 @@ import Vue from 'vue';
import performanceBarApp from './components/performance_bar_app.vue'; import performanceBarApp from './components/performance_bar_app.vue';
import PerformanceBarStore from './stores/performance_bar_store'; import PerformanceBarStore from './stores/performance_bar_store';
export default () => export default ({ container }) =>
new Vue({ new Vue({
el: '#js-peek', el: container,
components: { components: {
performanceBarApp, performanceBarApp,
}, },
......
import Vue from 'vue';
import _ from 'underscore';
import axios from '../../lib/utils/axios_utils'; import axios from '../../lib/utils/axios_utils';
let vueResourceInterceptor;
export default class PerformanceBarService { export default class PerformanceBarService {
static fetchRequestDetails(peekUrl, requestId) { static fetchRequestDetails(peekUrl, requestId) {
return axios.get(peekUrl, { params: { request_id: requestId } }); return axios.get(peekUrl, { params: { request_id: requestId } });
} }
static registerInterceptor(peekUrl, callback) { static registerInterceptor(peekUrl, callback) {
vueResourceInterceptor = (request, next) => {
next(response => {
const requestId = response.headers['x-request-id'];
const requestUrl = response.url;
if (requestUrl !== peekUrl && requestId) {
callback(requestId, requestUrl);
}
});
};
Vue.http.interceptors.push(vueResourceInterceptor);
return axios.interceptors.response.use(response => { return axios.interceptors.response.use(response => {
const requestId = response.headers['x-request-id']; const requestId = response.headers['x-request-id'];
const requestUrl = response.config.url; const requestUrl = response.config.url;
...@@ -20,5 +37,9 @@ export default class PerformanceBarService { ...@@ -20,5 +37,9 @@ export default class PerformanceBarService {
static removeInterceptor(interceptor) { static removeInterceptor(interceptor) {
axios.interceptors.response.eject(interceptor); axios.interceptors.response.eject(interceptor);
Vue.http.interceptors = _.without(
Vue.http.interceptors,
vueResourceInterceptor,
);
} }
} }
<script> <script>
import modal from '~/vue_shared/components/modal.vue'; import DeprecatedModal from '~/vue_shared/components/deprecated_modal.vue';
import { s__, sprintf } from '~/locale'; import { s__, sprintf } from '~/locale';
import pipelinesTableRowComponent from './pipelines_table_row.vue'; import pipelinesTableRowComponent from './pipelines_table_row.vue';
import eventHub from '../event_hub'; import eventHub from '../event_hub';
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
export default { export default {
components: { components: {
pipelinesTableRowComponent, pipelinesTableRowComponent,
modal, DeprecatedModal,
}, },
props: { props: {
pipelines: { pipelines: {
...@@ -120,7 +120,7 @@ ...@@ -120,7 +120,7 @@
:auto-devops-help-path="autoDevopsHelpPath" :auto-devops-help-path="autoDevopsHelpPath"
:view-type="viewType" :view-type="viewType"
/> />
<modal <deprecated-modal
id="confirmation-modal" id="confirmation-modal"
:title="modalTitle" :title="modalTitle"
:text="modalText" :text="modalText"
...@@ -134,6 +134,6 @@ ...@@ -134,6 +134,6 @@
> >
<p v-html="props.text"></p> <p v-html="props.text"></p>
</template> </template>
</modal> </deprecated-modal>
</div> </div>
</template> </template>
<script> <script>
import modal from '~/vue_shared/components/modal.vue'; import DeprecatedModal from '~/vue_shared/components/deprecated_modal.vue';
import { __, s__, sprintf } from '~/locale'; import { __, s__, sprintf } from '~/locale';
import csrf from '~/lib/utils/csrf'; import csrf from '~/lib/utils/csrf';
export default { export default {
components: { components: {
modal, DeprecatedModal,
}, },
props: { props: {
actionUrl: { actionUrl: {
...@@ -76,7 +76,7 @@ Once you confirm %{deleteAccount}, it cannot be undone or recovered.`), ...@@ -76,7 +76,7 @@ Once you confirm %{deleteAccount}, it cannot be undone or recovered.`),
</script> </script>
<template> <template>
<modal <deprecated-modal
id="delete-account-modal" id="delete-account-modal"
:title="s__('Profiles|Delete your account?')" :title="s__('Profiles|Delete your account?')"
:text="text" :text="text"
...@@ -131,5 +131,5 @@ Once you confirm %{deleteAccount}, it cannot be undone or recovered.`), ...@@ -131,5 +131,5 @@ Once you confirm %{deleteAccount}, it cannot be undone or recovered.`),
</form> </form>
</template> </template>
</modal> </deprecated-modal>
</template> </template>
<script> <script>
import { sprintf, s__ } from '~/locale';
import statusCodes from '../../lib/utils/http_status'; import statusCodes from '../../lib/utils/http_status';
import { bytesToMiB } from '../../lib/utils/number_utils'; import { bytesToMiB } from '../../lib/utils/number_utils';
import { backOff } from '../../lib/utils/common_utils'; import { backOff } from '../../lib/utils/common_utils';
...@@ -45,17 +46,28 @@ export default { ...@@ -45,17 +46,28 @@ export default {
shouldShowMetricsUnavailable() { shouldShowMetricsUnavailable() {
return !this.loadingMetrics && !this.hasMetrics && !this.loadFailed; return !this.loadingMetrics && !this.hasMetrics && !this.loadFailed;
}, },
memoryChangeType() { memoryChangeMessage() {
const messageProps = {
memoryFrom: this.memoryFrom,
memoryTo: this.memoryTo,
metricsLinkStart: `<a href="${this.metricsMonitoringUrl}">`,
metricsLinkEnd: '</a>',
emphasisStart: '<b>',
emphasisEnd: '</b>',
};
const memoryTo = Number(this.memoryTo); const memoryTo = Number(this.memoryTo);
const memoryFrom = Number(this.memoryFrom); const memoryFrom = Number(this.memoryFrom);
let memoryUsageMsg = '';
if (memoryTo > memoryFrom) { if (memoryTo > memoryFrom) {
return 'increased'; memoryUsageMsg = sprintf(s__('mrWidget|%{metricsLinkStart} Memory %{metricsLinkEnd} usage %{emphasisStart} increased %{emphasisEnd} from %{memoryFrom}MB to %{memoryTo}MB'), messageProps, false);
} else if (memoryTo < memoryFrom) { } else if (memoryTo < memoryFrom) {
return 'decreased'; memoryUsageMsg = sprintf(s__('mrWidget|%{metricsLinkStart} Memory %{metricsLinkEnd} usage %{emphasisStart} decreased %{emphasisEnd} from %{memoryFrom}MB to %{memoryTo}MB'), messageProps, false);
} else {
memoryUsageMsg = sprintf(s__('mrWidget|%{metricsLinkStart} Memory %{metricsLinkEnd} usage is %{emphasisStart} unchanged %{emphasisEnd} at %{memoryFrom}MB'), messageProps, false);
} }
return 'unchanged'; return memoryUsageMsg;
}, },
}, },
mounted() { mounted() {
...@@ -130,24 +142,22 @@ export default { ...@@ -130,24 +142,22 @@ export default {
<i <i
class="fa fa-spinner fa-spin usage-info-load-spinner" class="fa fa-spinner fa-spin usage-info-load-spinner"
aria-hidden="true"> aria-hidden="true">
</i>Loading deployment statistics </i>{{ s__('mrWidget|Loading deployment statistics') }}
</p> </p>
<p <p
v-if="shouldShowMemoryGraph" v-if="shouldShowMemoryGraph"
class="usage-info js-usage-info"> class="usage-info js-usage-info">
<a {{ memoryChangeMessage }}
:href="metricsMonitoringUrl"
>Memory</a> usage <b>{{ memoryChangeType }}</b> from {{ memoryFrom }}MB to {{ memoryTo }}MB
</p> </p>
<p <p
v-if="shouldShowLoadFailure" v-if="shouldShowLoadFailure"
class="usage-info js-usage-info usage-info-failed"> class="usage-info js-usage-info usage-info-failed">
Failed to load deployment statistics {{ s__('mrWidget|Failed to load deployment statistics') }}
</p> </p>
<p <p
v-if="shouldShowMetricsUnavailable" v-if="shouldShowMetricsUnavailable"
class="usage-info js-usage-info usage-info-unavailable"> class="usage-info js-usage-info usage-info-unavailable">
Deployment statistics are not available currently {{ s__('mrWidget|Deployment statistics are not available currently') }}
</p> </p>
<memory-graph <memory-graph
v-if="shouldShowMemoryGraph" v-if="shouldShowMemoryGraph"
......
<script> <script>
/* eslint-disable vue/require-default-prop */ /* eslint-disable vue/require-default-prop */
export default { export default {
name: 'Modal', name: 'DeprecatedModal', // use GlModal instead
props: { props: {
id: { id: {
......
<script> <script>
import modal from './modal.vue'; import DeprecatedModal from './deprecated_modal.vue';
export default { export default {
name: 'RecaptchaModal', name: 'RecaptchaModal',
components: { components: {
modal, DeprecatedModal,
}, },
props: { props: {
...@@ -65,7 +65,7 @@ ...@@ -65,7 +65,7 @@
</script> </script>
<template> <template>
<modal <deprecated-modal
kind="warning" kind="warning"
class="recaptcha-modal js-recaptcha-modal" class="recaptcha-modal js-recaptcha-modal"
:hide-footer="true" :hide-footer="true"
...@@ -82,5 +82,5 @@ ...@@ -82,5 +82,5 @@
> >
</div> </div>
</div> </div>
</modal> </deprecated-modal>
</template> </template>
...@@ -446,6 +446,10 @@ img.emoji { ...@@ -446,6 +446,10 @@ img.emoji {
opacity: .5; opacity: .5;
} }
.break-word {
word-wrap: break-word;
}
/** COMMON CLASSES **/ /** COMMON CLASSES **/
.prepend-top-0 { margin-top: 0; } .prepend-top-0 { margin-top: 0; }
.prepend-top-5 { margin-top: 5px; } .prepend-top-5 { margin-top: 5px; }
......
...@@ -622,7 +622,7 @@ ...@@ -622,7 +622,7 @@
} }
.dropdown-content { .dropdown-content {
max-height: $dropdown-max-height; max-height: 252px;
overflow-y: auto; overflow-y: auto;
} }
...@@ -699,6 +699,31 @@ ...@@ -699,6 +699,31 @@
border-radius: $border-radius-base; border-radius: $border-radius-base;
} }
.git-revision-dropdown {
.dropdown-content {
max-height: 215px;
}
}
.sidebar-move-issue-dropdown {
.dropdown-content {
max-height: 160px;
}
}
.dropdown-menu-author {
.dropdown-content {
max-height: 215px;
}
}
.dropdown-menu-labels {
.dropdown-content {
max-height: 128px;
}
}
.dropdown-menu-due-date { .dropdown-menu-due-date {
.dropdown-content { .dropdown-content {
max-height: 230px; max-height: 230px;
......
...@@ -152,3 +152,4 @@ ...@@ -152,3 +152,4 @@
.sidebar-collapsed-icon .sidebar-collapsed-value { .sidebar-collapsed-icon .sidebar-collapsed-value {
font-size: 12px; font-size: 12px;
} }
...@@ -31,8 +31,12 @@ ...@@ -31,8 +31,12 @@
.dropdown-menu-issues-board-new { .dropdown-menu-issues-board-new {
width: 320px; width: 320px;
.open & {
max-height: 400px;
}
.dropdown-content { .dropdown-content {
max-height: 150px; max-height: 162px;
} }
} }
......
.content-list > .branch-item,
.branch-title {
display: flex;
align-items: center;
}
.branch-info {
flex: auto;
min-width: 0;
overflow: hidden;
}
.divergence-graph { .divergence-graph {
padding: 12px 12px 0 0; padding: 0 6px;
float: right;
.graph-side { .graph-side {
position: relative; position: relative;
...@@ -53,3 +64,9 @@ ...@@ -53,3 +64,9 @@
background-color: $divergence-graph-separator-bg; background-color: $divergence-graph-separator-bg;
} }
} }
.divergence-graph,
.branch-item .controls {
flex: 0 0 auto;
white-space: nowrap;
}
...@@ -162,17 +162,14 @@ ...@@ -162,17 +162,14 @@
* Last push widget * Last push widget
*/ */
.event-last-push { .event-last-push {
overflow: auto;
width: 100%; width: 100%;
display: flex;
align-items: center;
.event-last-push-text { .event-last-push-text {
@include str-truncated(100%); @include str-truncated(100%);
padding: 4px 0;
font-size: 13px; font-size: 13px;
float: left; margin-right: $gl-padding;
margin-right: -150px;
padding-right: 150px;
line-height: 20px;
} }
} }
......
...@@ -26,9 +26,15 @@ ...@@ -26,9 +26,15 @@
} }
} }
.dropdown-menu-labels {
.dropdown-content {
max-height: 135px;
}
}
.dropdown-new-label { .dropdown-new-label {
.dropdown-content { .dropdown-content {
max-height: 260px; max-height: 136px;
} }
} }
......
...@@ -9,7 +9,6 @@ ...@@ -9,7 +9,6 @@
.new_project, .new_project,
.edit-project, .edit-project,
.import-project { .import-project {
.help-block { .help-block {
margin-bottom: 10px; margin-bottom: 10px;
} }
...@@ -18,18 +17,25 @@ ...@@ -18,18 +17,25 @@
border-radius: $border-radius-base; border-radius: $border-radius-base;
} }
.input-group > div { .input-group {
display: flex;
&:last-child { .select2-container {
padding-right: 0; display: unset;
max-width: unset;
width: unset !important;
flex-grow: 1;
}
> div {
&:last-child {
padding-right: 0;
}
} }
} }
@media (max-width: $screen-xs-max) { @media (max-width: $screen-xs-max) {
.input-group > div { .input-group > div {
margin-bottom: 14px;
&:last-child { &:last-child {
margin-bottom: 0; margin-bottom: 0;
} }
...@@ -41,17 +47,24 @@ ...@@ -41,17 +47,24 @@
} }
.input-group-addon { .input-group-addon {
overflow: hidden;
text-overflow: ellipsis;
line-height: unset;
width: unset;
max-width: 50%;
text-align: left;
&.static-namespace { &.static-namespace {
height: 35px; height: 35px;
border-radius: 3px; border-radius: 3px;
border: 1px solid $border-color; border: 1px solid $border-color;
max-width: 100%;
flex-grow: 1;
} }
+ .select2 a, + .select2 a,
+ .btn-default { + .btn-default {
border-top-left-radius: 0; border-radius: 0 $border-radius-base $border-radius-base 0;
border-bottom-left-radius: 0;
} }
} }
} }
...@@ -290,7 +303,7 @@ ...@@ -290,7 +303,7 @@
font-size: 13px; font-size: 13px;
font-weight: $gl-font-weight-bold; font-weight: $gl-font-weight-bold;
line-height: 13px; line-height: 13px;
letter-spacing: .4px; letter-spacing: 0.4px;
padding: 6px 14px; padding: 6px 14px;
text-align: center; text-align: center;
vertical-align: middle; vertical-align: middle;
...@@ -443,7 +456,7 @@ a.deploy-project-label { ...@@ -443,7 +456,7 @@ a.deploy-project-label {
text-decoration: none; text-decoration: none;
&.disabled { &.disabled {
opacity: .3; opacity: 0.3;
cursor: not-allowed; cursor: not-allowed;
} }
} }
...@@ -600,26 +613,26 @@ a.deploy-project-label { ...@@ -600,26 +613,26 @@ a.deploy-project-label {
} }
.first-column { .first-column {
@media(min-width: $screen-xs-min) { @media (min-width: $screen-xs-min) {
max-width: 50%; max-width: 50%;
padding-right: 30px; padding-right: 30px;
} }
@media(max-width: $screen-xs-max) { @media (max-width: $screen-xs-max) {
max-width: 100%; max-width: 100%;
width: 100%; width: 100%;
} }
} }
.second-column { .second-column {
@media(min-width: $screen-xs-min) { @media (min-width: $screen-xs-min) {
width: 50%; width: 50%;
flex: 1; flex: 1;
padding-left: 30px; padding-left: 30px;
position: relative; position: relative;
} }
@media(max-width: $screen-xs-max) { @media (max-width: $screen-xs-max) {
max-width: 100%; max-width: 100%;
width: 100%; width: 100%;
padding-left: 0; padding-left: 0;
...@@ -632,7 +645,7 @@ a.deploy-project-label { ...@@ -632,7 +645,7 @@ a.deploy-project-label {
} }
&::before { &::before {
content: "OR"; content: 'OR';
position: absolute; position: absolute;
left: -10px; left: -10px;
top: 50%; top: 50%;
...@@ -656,7 +669,7 @@ a.deploy-project-label { ...@@ -656,7 +669,7 @@ a.deploy-project-label {
} }
&::after { &::after {
content: ""; content: '';
position: absolute; position: absolute;
background-color: $border-color; background-color: $border-color;
bottom: 0; bottom: 0;
...@@ -921,10 +934,7 @@ pre.light-well { ...@@ -921,10 +934,7 @@ pre.light-well {
border-right: solid 1px transparent; border-right: solid 1px transparent;
} }
} }
}
.protected-tags-list,
.protected-branches-list {
.dropdown-menu-toggle { .dropdown-menu-toggle {
width: 100%; width: 100%;
max-width: 300px; max-width: 300px;
......
...@@ -112,7 +112,7 @@ input[type="checkbox"]:hover { ...@@ -112,7 +112,7 @@ input[type="checkbox"]:hover {
} }
.dropdown-content { .dropdown-content {
max-height: 350px; max-height: 302px;
} }
} }
......
...@@ -15,6 +15,10 @@ ...@@ -15,6 +15,10 @@
line-height: $performance-bar-height; line-height: $performance-bar-height;
color: $perf-bar-text; color: $perf-bar-text;
select {
width: 200px;
}
&.disabled { &.disabled {
display: none; display: none;
} }
...@@ -43,12 +47,6 @@ ...@@ -43,12 +47,6 @@
} }
} }
.wrapper {
width: 80%;
height: $performance-bar-height;
margin: 0 auto;
}
// UI Elements // UI Elements
.bucket { .bucket {
background: $perf-bar-bucket-bg; background: $perf-bar-bucket-bg;
...@@ -108,8 +106,14 @@ ...@@ -108,8 +106,14 @@
} }
} }
.performance-bar-modal .modal-footer { .performance-bar-modal {
display: none; .modal-footer {
display: none;
}
.modal-dialog {
width: 860px;
}
} }
} }
......
...@@ -3,23 +3,9 @@ ...@@ -3,23 +3,9 @@
# Automatically sets the layout and ensures an administrator is logged in # Automatically sets the layout and ensures an administrator is logged in
class Admin::ApplicationController < ApplicationController class Admin::ApplicationController < ApplicationController
before_action :authenticate_admin! before_action :authenticate_admin!
before_action :display_read_only_information
layout 'admin' layout 'admin'
def authenticate_admin! def authenticate_admin!
render_404 unless current_user.admin? render_404 unless current_user.admin?
end end
def display_read_only_information
return unless Gitlab::Database.read_only?
flash.now[:notice] = read_only_message
end
private
# Overridden in EE
def read_only_message
_('You are on a read-only GitLab instance.')
end
end end
...@@ -18,6 +18,18 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController ...@@ -18,6 +18,18 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
end end
end end
# Extend the standard implementation to also increment
# the number of failed sign in attempts
def failure
if params[:username].present? && AuthHelper.form_based_provider?(failed_strategy.name)
user = User.by_login(params[:username])
user&.increment_failed_attempts!
end
super
end
# Extend the standard message generation to accept our custom exception # Extend the standard message generation to accept our custom exception
def failure_message def failure_message
exception = env["omniauth.error"] exception = env["omniauth.error"]
...@@ -95,6 +107,14 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController ...@@ -95,6 +107,14 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
handle_omniauth handle_omniauth
end end
def auth0
if oauth['uid'].blank?
fail_auth0_login
else
handle_omniauth
end
end
private private
def handle_omniauth def handle_omniauth
...@@ -170,6 +190,12 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController ...@@ -170,6 +190,12 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
redirect_to new_user_session_path redirect_to new_user_session_path
end end
def fail_auth0_login
flash[:alert] = 'Wrong extern UID provided. Make sure Auth0 is configured correctly.'
redirect_to new_user_session_path
end
def handle_disabled_provider def handle_disabled_provider
label = Gitlab::Auth::OAuth::Provider.label_for(oauth['provider']) label = Gitlab::Auth::OAuth::Provider.label_for(oauth['provider'])
flash[:alert] = "Signing in using #{label} has been disabled" flash[:alert] = "Signing in using #{label} has been disabled"
......
...@@ -21,4 +21,26 @@ class Projects::PagesController < Projects::ApplicationController ...@@ -21,4 +21,26 @@ class Projects::PagesController < Projects::ApplicationController
end end
end end
end end
def update
result = Projects::UpdateService.new(@project, current_user, project_params).execute
respond_to do |format|
format.html do
if result[:status] == :success
flash[:notice] = 'Your changes have been saved'
else
flash[:alert] = 'Something went wrong on our end'
end
redirect_to project_pages_path(@project)
end
end
end
private
def project_params
params.require(:project).permit(:pages_https_only)
end
end end
...@@ -10,10 +10,7 @@ class Projects::PipelinesSettingsController < Projects::ApplicationController ...@@ -10,10 +10,7 @@ class Projects::PipelinesSettingsController < Projects::ApplicationController
if service.execute if service.execute
flash[:notice] = "Pipelines settings for '#{@project.name}' were successfully updated." flash[:notice] = "Pipelines settings for '#{@project.name}' were successfully updated."
if service.run_auto_devops_pipeline? run_autodevops_pipeline(service)
CreatePipelineWorker.perform_async(project.id, current_user.id, project.default_branch, :web, ignore_skip_ci: true, save_on_errors: false)
flash[:success] = "A new Auto DevOps pipeline has been created, go to <a href=\"#{project_pipelines_path(@project)}\">Pipelines page</a> for details".html_safe
end
redirect_to project_settings_ci_cd_path(@project) redirect_to project_settings_ci_cd_path(@project)
else else
...@@ -24,6 +21,18 @@ class Projects::PipelinesSettingsController < Projects::ApplicationController ...@@ -24,6 +21,18 @@ class Projects::PipelinesSettingsController < Projects::ApplicationController
private private
def run_autodevops_pipeline(service)
return unless service.run_auto_devops_pipeline?
if @project.empty_repo?
flash[:warning] = "This repository is currently empty. A new Auto DevOps pipeline will be created after a new file has been pushed to a branch."
return
end
CreatePipelineWorker.perform_async(project.id, current_user.id, project.default_branch, :web, ignore_skip_ci: true, save_on_errors: false)
flash[:success] = "A new Auto DevOps pipeline has been created, go to <a href=\"#{project_pipelines_path(@project)}\">Pipelines page</a> for details".html_safe
end
def update_params def update_params
params.require(:project).permit( params.require(:project).permit(
:runners_token, :builds_enabled, :build_allow_git_fetch, :runners_token, :builds_enabled, :build_allow_git_fetch,
......
...@@ -323,4 +323,11 @@ module ApplicationHelper ...@@ -323,4 +323,11 @@ module ApplicationHelper
def locale_path def locale_path
asset_path("locale/#{Gitlab::I18n.locale}/app.js") asset_path("locale/#{Gitlab::I18n.locale}/app.js")
end end
# Overridden in EE
def read_only_message
return unless Gitlab::Database.read_only?
_('You are on a read-only GitLab instance.')
end
end end
...@@ -96,7 +96,7 @@ module ApplicationSettingsHelper ...@@ -96,7 +96,7 @@ module ApplicationSettingsHelper
def repository_storages_options_for_select(selected) def repository_storages_options_for_select(selected)
options = Gitlab.config.repositories.storages.map do |name, storage| options = Gitlab.config.repositories.storages.map do |name, storage|
["#{name} - #{storage['path']}", name] ["#{name} - #{storage['gitaly_address']}", name]
end end
options_for_select(options, selected) options_for_select(options, selected)
...@@ -245,7 +245,8 @@ module ApplicationSettingsHelper ...@@ -245,7 +245,8 @@ module ApplicationSettingsHelper
:usage_ping_enabled, :usage_ping_enabled,
:user_default_external, :user_default_external,
:user_oauth_applications, :user_oauth_applications,
:version_check_enabled :version_check_enabled,
:allow_local_requests_from_hooks_and_services
] ]
end end
end end
...@@ -531,4 +531,22 @@ module ProjectsHelper ...@@ -531,4 +531,22 @@ module ProjectsHelper
def can_show_last_commit_in_list?(project) def can_show_last_commit_in_list?(project)
can?(current_user, :read_cross_project) && project.commit can?(current_user, :read_cross_project) && project.commit
end end
def pages_https_only_disabled?
!@project.pages_domains.all?(&:https?)
end
def pages_https_only_title
return unless pages_https_only_disabled?
"You must enable HTTPS for all your domains first"
end
def pages_https_only_label_class
if pages_https_only_disabled?
"list-label disabled"
else
"list-label"
end
end
end end
...@@ -330,7 +330,8 @@ class ApplicationSetting < ActiveRecord::Base ...@@ -330,7 +330,8 @@ class ApplicationSetting < ActiveRecord::Base
usage_ping_enabled: Settings.gitlab['usage_ping_enabled'], usage_ping_enabled: Settings.gitlab['usage_ping_enabled'],
gitaly_timeout_fast: 10, gitaly_timeout_fast: 10,
gitaly_timeout_medium: 30, gitaly_timeout_medium: 30,
gitaly_timeout_default: 55 gitaly_timeout_default: 55,
allow_local_requests_from_hooks_and_services: false
} }
end end
......
...@@ -14,7 +14,7 @@ module Ci ...@@ -14,7 +14,7 @@ module Ci
has_many :stages has_many :stages
has_many :statuses, class_name: 'CommitStatus', foreign_key: :commit_id, inverse_of: :pipeline has_many :statuses, class_name: 'CommitStatus', foreign_key: :commit_id, inverse_of: :pipeline
has_many :builds, foreign_key: :commit_id has_many :builds, foreign_key: :commit_id, inverse_of: :pipeline
has_many :trigger_requests, dependent: :destroy, foreign_key: :commit_id # rubocop:disable Cop/ActiveRecordDependent has_many :trigger_requests, dependent: :destroy, foreign_key: :commit_id # rubocop:disable Cop/ActiveRecordDependent
has_many :variables, class_name: 'Ci::PipelineVariable' has_many :variables, class_name: 'Ci::PipelineVariable'
......
...@@ -21,7 +21,7 @@ module Avatarable ...@@ -21,7 +21,7 @@ module Avatarable
def avatar_type def avatar_type
unless self.avatar.image? unless self.avatar.image?
self.errors.add :avatar, "only images allowed" errors.add :avatar, "file format is not supported. Please try one of the following supported formats: #{AvatarUploader::IMAGE_EXT.join(', ')}"
end end
end end
......
...@@ -189,12 +189,6 @@ class Group < Namespace ...@@ -189,12 +189,6 @@ class Group < Namespace
owners.include?(user) && owners.size == 1 owners.include?(user) && owners.size == 1
end end
def avatar_type
unless self.avatar.image?
self.errors.add :avatar, "only images allowed"
end
end
def post_create_hook def post_create_hook
Gitlab::AppLogger.info("Group \"#{name}\" was created") Gitlab::AppLogger.info("Group \"#{name}\" was created")
......
...@@ -6,8 +6,10 @@ class PagesDomain < ActiveRecord::Base ...@@ -6,8 +6,10 @@ class PagesDomain < ActiveRecord::Base
validates :domain, hostname: { allow_numeric_hostname: true } validates :domain, hostname: { allow_numeric_hostname: true }
validates :domain, uniqueness: { case_sensitive: false } validates :domain, uniqueness: { case_sensitive: false }
validates :certificate, certificate: true, allow_nil: true, allow_blank: true validates :certificate, presence: { message: 'must be present if HTTPS-only is enabled' }, if: ->(domain) { domain.project&.pages_https_only? }
validates :key, certificate_key: true, allow_nil: true, allow_blank: true validates :certificate, certificate: true, if: ->(domain) { domain.certificate.present? }
validates :key, presence: { message: 'must be present if HTTPS-only is enabled' }, if: ->(domain) { domain.project&.pages_https_only? }
validates :key, certificate_key: true, if: ->(domain) { domain.key.present? }
validates :verification_code, presence: true, allow_blank: false validates :verification_code, presence: true, allow_blank: false
validate :validate_pages_domain validate :validate_pages_domain
...@@ -46,6 +48,10 @@ class PagesDomain < ActiveRecord::Base ...@@ -46,6 +48,10 @@ class PagesDomain < ActiveRecord::Base
!Gitlab::CurrentSettings.pages_domain_verification_enabled? || enabled_until.present? !Gitlab::CurrentSettings.pages_domain_verification_enabled? || enabled_until.present?
end end
def https?
certificate.present?
end
def to_param def to_param
domain domain
end end
......
...@@ -38,6 +38,9 @@ class Project < ActiveRecord::Base ...@@ -38,6 +38,9 @@ class Project < ActiveRecord::Base
attachments: 2 attachments: 2
}.freeze }.freeze
# Valids ports to import from
VALID_IMPORT_PORTS = [22, 80, 443].freeze
cache_markdown_field :description, pipeline: :description cache_markdown_field :description, pipeline: :description
delegate :feature_available?, :builds_enabled?, :wiki_enabled?, delegate :feature_available?, :builds_enabled?, :wiki_enabled?,
...@@ -264,6 +267,7 @@ class Project < ActiveRecord::Base ...@@ -264,6 +267,7 @@ class Project < ActiveRecord::Base
validate :visibility_level_allowed_by_group validate :visibility_level_allowed_by_group
validate :visibility_level_allowed_as_fork validate :visibility_level_allowed_as_fork
validate :check_wiki_path_conflict validate :check_wiki_path_conflict
validate :validate_pages_https_only, if: -> { changes.has_key?(:pages_https_only) }
validates :repository_storage, validates :repository_storage,
presence: true, presence: true,
inclusion: { in: ->(_object) { Gitlab.config.repositories.storages.keys } } inclusion: { in: ->(_object) { Gitlab.config.repositories.storages.keys } }
...@@ -500,7 +504,7 @@ class Project < ActiveRecord::Base ...@@ -500,7 +504,7 @@ class Project < ActiveRecord::Base
end end
def repository_storage_path def repository_storage_path
Gitlab.config.repositories.storages[repository_storage].try(:[], 'path') Gitlab.config.repositories.storages[repository_storage]&.legacy_disk_path
end end
def team def team
...@@ -734,6 +738,26 @@ class Project < ActiveRecord::Base ...@@ -734,6 +738,26 @@ class Project < ActiveRecord::Base
end end
end end
def pages_https_only
return false unless Gitlab.config.pages.external_https
super
end
def pages_https_only?
return false unless Gitlab.config.pages.external_https
super
end
def validate_pages_https_only
return unless pages_https_only?
unless pages_domains.all?(&:https?)
errors.add(:pages_https_only, "cannot be enabled unless all domains have TLS certificates")
end
end
def to_param def to_param
if persisted? && errors.include?(:path) if persisted? && errors.include?(:path)
path_was path_was
......
class AssemblaService < Service class AssemblaService < Service
include HTTParty
prop_accessor :token, :subdomain prop_accessor :token, :subdomain
validates :token, presence: true, if: :activated? validates :token, presence: true, if: :activated?
...@@ -31,6 +29,6 @@ class AssemblaService < Service ...@@ -31,6 +29,6 @@ class AssemblaService < Service
return unless supported_events.include?(data[:object_kind]) return unless supported_events.include?(data[:object_kind])
url = "https://atlas.assembla.com/spaces/#{subdomain}/github_tool?secret_key=#{token}" url = "https://atlas.assembla.com/spaces/#{subdomain}/github_tool?secret_key=#{token}"
AssemblaService.post(url, body: { payload: data }.to_json, headers: { 'Content-Type' => 'application/json' }) Gitlab::HTTP.post(url, body: { payload: data }.to_json, headers: { 'Content-Type' => 'application/json' })
end end
end end
...@@ -117,14 +117,14 @@ class BambooService < CiService ...@@ -117,14 +117,14 @@ class BambooService < CiService
url = build_url(path) url = build_url(path)
if username.blank? && password.blank? if username.blank? && password.blank?
HTTParty.get(url, verify: false) Gitlab::HTTP.get(url, verify: false)
else else
url << '&os_authType=basic' url << '&os_authType=basic'
HTTParty.get(url, verify: false, Gitlab::HTTP.get(url, verify: false,
basic_auth: { basic_auth: {
username: username, username: username,
password: password password: password
}) })
end end
end end
end end
...@@ -71,7 +71,7 @@ class BuildkiteService < CiService ...@@ -71,7 +71,7 @@ class BuildkiteService < CiService
end end
def calculate_reactive_cache(sha, ref) def calculate_reactive_cache(sha, ref)
response = HTTParty.get(commit_status_path(sha), verify: false) response = Gitlab::HTTP.get(commit_status_path(sha), verify: false)
status = status =
if response.code == 200 && response['status'] if response.code == 200 && response['status']
......
class CampfireService < Service class CampfireService < Service
include HTTParty
prop_accessor :token, :subdomain, :room prop_accessor :token, :subdomain, :room
validates :token, presence: true, if: :activated? validates :token, presence: true, if: :activated?
...@@ -31,7 +29,6 @@ class CampfireService < Service ...@@ -31,7 +29,6 @@ class CampfireService < Service
def execute(data) def execute(data)
return unless supported_events.include?(data[:object_kind]) return unless supported_events.include?(data[:object_kind])
self.class.base_uri base_uri
message = build_message(data) message = build_message(data)
speak(self.room, message, auth) speak(self.room, message, auth)
end end
...@@ -69,14 +66,14 @@ class CampfireService < Service ...@@ -69,14 +66,14 @@ class CampfireService < Service
} }
} }
} }
res = self.class.post(path, auth.merge(body)) res = Gitlab::HTTP.post(path, base_uri: base_uri, **auth.merge(body))
res.code == 201 ? res : nil res.code == 201 ? res : nil
end end
# Returns a list of rooms, or []. # Returns a list of rooms, or [].
# https://github.com/basecamp/campfire-api/blob/master/sections/rooms.md#get-rooms # https://github.com/basecamp/campfire-api/blob/master/sections/rooms.md#get-rooms
def rooms(auth) def rooms(auth)
res = self.class.get("/rooms.json", auth) res = Gitlab::HTTP.get("/rooms.json", base_uri: base_uri, **auth)
res.code == 200 ? res["rooms"] : [] res.code == 200 ? res["rooms"] : []
end end
......
...@@ -49,7 +49,7 @@ class DroneCiService < CiService ...@@ -49,7 +49,7 @@ class DroneCiService < CiService
end end
def calculate_reactive_cache(sha, ref) def calculate_reactive_cache(sha, ref)
response = HTTParty.get(commit_status_path(sha, ref), verify: enable_ssl_verification) response = Gitlab::HTTP.get(commit_status_path(sha, ref), verify: enable_ssl_verification)
status = status =
if response.code == 200 && response['status'] if response.code == 200 && response['status']
......
class ExternalWikiService < Service class ExternalWikiService < Service
include HTTParty
prop_accessor :external_wiki_url prop_accessor :external_wiki_url
validates :external_wiki_url, presence: true, url: true, if: :activated? validates :external_wiki_url, presence: true, url: true, if: :activated?
...@@ -24,7 +22,7 @@ class ExternalWikiService < Service ...@@ -24,7 +22,7 @@ class ExternalWikiService < Service
end end
def execute(_data) def execute(_data)
@response = HTTParty.get(properties['external_wiki_url'], verify: true) rescue nil @response = Gitlab::HTTP.get(properties['external_wiki_url'], verify: true) rescue nil
if @response != 200 if @response != 200
nil nil
end end
......
...@@ -77,13 +77,13 @@ class IssueTrackerService < Service ...@@ -77,13 +77,13 @@ class IssueTrackerService < Service
result = false result = false
begin begin
response = HTTParty.head(self.project_url, verify: true) response = Gitlab::HTTP.head(self.project_url, verify: true)
if response if response
message = "#{self.type} received response #{response.code} when attempting to connect to #{self.project_url}" message = "#{self.type} received response #{response.code} when attempting to connect to #{self.project_url}"
result = true result = true
end end
rescue HTTParty::Error, Timeout::Error, SocketError, Errno::ECONNRESET, Errno::ECONNREFUSED, OpenSSL::SSL::SSLError => error rescue Gitlab::HTTP::Error, Timeout::Error, SocketError, Errno::ECONNRESET, Errno::ECONNREFUSED, OpenSSL::SSL::SSLError => error
message = "#{self.type} had an error when trying to connect to #{self.project_url}: #{error.message}" message = "#{self.type} had an error when trying to connect to #{self.project_url}: #{error.message}"
end end
Rails.logger.info(message) Rails.logger.info(message)
......
...@@ -52,7 +52,7 @@ class MockCiService < CiService ...@@ -52,7 +52,7 @@ class MockCiService < CiService
# #
# #
def commit_status(sha, ref) def commit_status(sha, ref)
response = HTTParty.get(commit_status_path(sha), verify: false) response = Gitlab::HTTP.get(commit_status_path(sha), verify: false)
read_commit_status(response) read_commit_status(response)
rescue Errno::ECONNREFUSED rescue Errno::ECONNREFUSED
:error :error
......
class PackagistService < Service class PackagistService < Service
include HTTParty
prop_accessor :username, :token, :server prop_accessor :username, :token, :server
validates :username, presence: true, if: :activated? validates :username, presence: true, if: :activated?
......
class PivotaltrackerService < Service class PivotaltrackerService < Service
include HTTParty
API_ENDPOINT = 'https://www.pivotaltracker.com/services/v5/source_commits'.freeze API_ENDPOINT = 'https://www.pivotaltracker.com/services/v5/source_commits'.freeze
prop_accessor :token, :restrict_to_branch prop_accessor :token, :restrict_to_branch
...@@ -52,7 +50,7 @@ class PivotaltrackerService < Service ...@@ -52,7 +50,7 @@ class PivotaltrackerService < Service
'message' => commit[:message] 'message' => commit[:message]
} }
} }
PivotaltrackerService.post( Gitlab::HTTP.post(
API_ENDPOINT, API_ENDPOINT,
body: message.to_json, body: message.to_json,
headers: { headers: {
......
class PushoverService < Service class PushoverService < Service
include HTTParty BASE_URI = 'https://api.pushover.net/1'.freeze
base_uri 'https://api.pushover.net/1'
prop_accessor :api_key, :user_key, :device, :priority, :sound prop_accessor :api_key, :user_key, :device, :priority, :sound
validates :api_key, :user_key, :priority, presence: true, if: :activated? validates :api_key, :user_key, :priority, presence: true, if: :activated?
...@@ -99,6 +98,6 @@ class PushoverService < Service ...@@ -99,6 +98,6 @@ class PushoverService < Service
pushover_data[:sound] = sound pushover_data[:sound] = sound
end end
PushoverService.post('/messages.json', body: pushover_data) Gitlab::HTTP.post('/messages.json', base_uri: BASE_URI, body: pushover_data)
end end
end end
...@@ -83,7 +83,7 @@ class TeamcityService < CiService ...@@ -83,7 +83,7 @@ class TeamcityService < CiService
branch = Gitlab::Git.ref_name(data[:ref]) branch = Gitlab::Git.ref_name(data[:ref])
HTTParty.post( Gitlab::HTTP.post(
build_url('httpAuth/app/rest/buildQueue'), build_url('httpAuth/app/rest/buildQueue'),
body: "<build branchName=\"#{branch}\">"\ body: "<build branchName=\"#{branch}\">"\
"<buildType id=\"#{build_type}\"/>"\ "<buildType id=\"#{build_type}\"/>"\
...@@ -134,10 +134,10 @@ class TeamcityService < CiService ...@@ -134,10 +134,10 @@ class TeamcityService < CiService
end end
def get_path(path) def get_path(path)
HTTParty.get(build_url(path), verify: false, Gitlab::HTTP.get(build_url(path), verify: false,
basic_auth: { basic_auth: {
username: username, username: username,
password: password password: password
}) })
end end
end end
...@@ -65,7 +65,7 @@ module Ci ...@@ -65,7 +65,7 @@ module Ci
project.pipelines project.pipelines
.where(ref: pipeline.ref) .where(ref: pipeline.ref)
.where.not(id: pipeline.id) .where.not(id: pipeline.id)
.where.not(sha: project.repository.sha_from_ref(pipeline.ref)) .where.not(sha: project.commit(pipeline.ref).try(:id))
.created_or_pending .created_or_pending
end end
......
...@@ -28,7 +28,7 @@ module Projects ...@@ -28,7 +28,7 @@ module Projects
def add_repository_to_project def add_repository_to_project
if project.external_import? && !unknown_url? if project.external_import? && !unknown_url?
raise Error, 'Blocked import URL.' if Gitlab::UrlBlocker.blocked_url?(project.import_url) raise Error, 'Blocked import URL.' if Gitlab::UrlBlocker.blocked_url?(project.import_url, valid_ports: Project::VALID_IMPORT_PORTS)
end end
# We should skip the repository for a GitHub import or GitLab project import, # We should skip the repository for a GitHub import or GitLab project import,
......
...@@ -18,7 +18,8 @@ module Projects ...@@ -18,7 +18,8 @@ module Projects
def pages_config def pages_config
{ {
domains: pages_domains_config domains: pages_domains_config,
https_only: project.pages_https_only?
} }
end end
...@@ -27,7 +28,8 @@ module Projects ...@@ -27,7 +28,8 @@ module Projects
{ {
domain: domain.domain, domain: domain.domain,
certificate: domain.certificate, certificate: domain.certificate,
key: domain.key key: domain.key,
https_only: project.pages_https_only? && domain.https?
} }
end end
end end
......
...@@ -24,6 +24,8 @@ module Projects ...@@ -24,6 +24,8 @@ module Projects
system_hook_service.execute_hooks_for(project, :update) system_hook_service.execute_hooks_for(project, :update)
end end
update_pages_config if changing_pages_https_only?
success success
else else
model_errors = project.errors.full_messages.to_sentence model_errors = project.errors.full_messages.to_sentence
...@@ -67,5 +69,13 @@ module Projects ...@@ -67,5 +69,13 @@ module Projects
log_error("Could not create wiki for #{project.full_name}") log_error("Could not create wiki for #{project.full_name}")
Gitlab::Metrics.counter(:wiki_can_not_be_created_total, 'Counts the times we failed to create a wiki') Gitlab::Metrics.counter(:wiki_can_not_be_created_total, 'Counts the times we failed to create a wiki')
end end
def update_pages_config
Projects::UpdatePagesConfigurationService.new(project).execute
end
def changing_pages_https_only?
project.previous_changes.include?(:pages_https_only)
end
end end
end end
...@@ -14,16 +14,17 @@ class SubmitUsagePingService ...@@ -14,16 +14,17 @@ class SubmitUsagePingService
def execute def execute
return false unless Gitlab::CurrentSettings.usage_ping_enabled? return false unless Gitlab::CurrentSettings.usage_ping_enabled?
response = HTTParty.post( response = Gitlab::HTTP.post(
URL, URL,
body: Gitlab::UsageData.to_json(force_refresh: true), body: Gitlab::UsageData.to_json(force_refresh: true),
allow_local_requests: true,
headers: { 'Content-type' => 'application/json' } headers: { 'Content-type' => 'application/json' }
) )
store_metrics(response) store_metrics(response)
true true
rescue HTTParty::Error => e rescue Gitlab::HTTP::Error => e
Rails.logger.info "Unable to contact GitLab, Inc.: #{e}" Rails.logger.info "Unable to contact GitLab, Inc.: #{e}"
false false
......
...@@ -3,23 +3,20 @@ class WebHookService ...@@ -3,23 +3,20 @@ class WebHookService
attr_reader :body, :headers, :code attr_reader :body, :headers, :code
def initialize def initialize
@headers = HTTParty::Response::Headers.new({}) @headers = Gitlab::HTTP::Response::Headers.new({})
@body = '' @body = ''
@code = 'internal error' @code = 'internal error'
end end
end end
include HTTParty attr_accessor :hook, :data, :hook_name, :request_options
# HTTParty timeout
default_timeout Gitlab.config.gitlab.webhook_timeout
attr_accessor :hook, :data, :hook_name
def initialize(hook, data, hook_name) def initialize(hook, data, hook_name)
@hook = hook @hook = hook
@data = data @data = data
@hook_name = hook_name.to_s @hook_name = hook_name.to_s
@request_options = { timeout: Gitlab.config.gitlab.webhook_timeout }
@request_options.merge!(allow_local_requests: true) if @hook.is_a?(SystemHook)
end end
def execute def execute
...@@ -73,11 +70,12 @@ class WebHookService ...@@ -73,11 +70,12 @@ class WebHookService
end end
def make_request(url, basic_auth = false) def make_request(url, basic_auth = false)
self.class.post(url, Gitlab::HTTP.post(url,
body: data.to_json, body: data.to_json,
headers: build_headers(hook_name), headers: build_headers(hook_name),
verify: hook.enable_ssl_verification, verify: hook.enable_ssl_verification,
basic_auth: basic_auth) basic_auth: basic_auth,
**request_options)
end end
def make_request_with_auth def make_request_with_auth
......
...@@ -16,8 +16,6 @@ class CertificateValidator < ActiveModel::EachValidator ...@@ -16,8 +16,6 @@ class CertificateValidator < ActiveModel::EachValidator
private private
def valid_certificate_pem?(value) def valid_certificate_pem?(value)
return false unless value
OpenSSL::X509::Certificate.new(value).present? OpenSSL::X509::Certificate.new(value).present?
rescue OpenSSL::X509::CertificateError rescue OpenSSL::X509::CertificateError
false false
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
# protect against Server-side Request Forgery (SSRF). # protect against Server-side Request Forgery (SSRF).
class ImportableUrlValidator < ActiveModel::EachValidator class ImportableUrlValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value) def validate_each(record, attribute, value)
if Gitlab::UrlBlocker.blocked_url?(value) if Gitlab::UrlBlocker.blocked_url?(value, valid_ports: Project::VALID_IMPORT_PORTS)
record.errors.add(attribute, "imports are not allowed from that URL") record.errors.add(attribute, "imports are not allowed from that URL")
end end
end end
......
...@@ -860,5 +860,14 @@ ...@@ -860,5 +860,14 @@
.col-sm-10 .col-sm-10
= f.number_field :throttle_authenticated_web_period_in_seconds, class: 'form-control' = f.number_field :throttle_authenticated_web_period_in_seconds, class: 'form-control'
%fieldset
%legend Outbound requests
.form-group
.col-sm-offset-2.col-sm-10
.checkbox
= f.label :allow_local_requests_from_hooks_and_services do
= f.check_box :allow_local_requests_from_hooks_and_services
Allow requests to the local network from hooks and services
.form-actions .form-actions
= f.submit 'Save', class: 'btn btn-save' = f.submit 'Save', class: 'btn btn-save'
...@@ -13,13 +13,13 @@ ...@@ -13,13 +13,13 @@
.form-group .form-group
.input-group .input-group
- if current_user.can_select_namespace? - if current_user.can_select_namespace?
.input-group-addon .input-group-addon.has-tooltip{ title: root_url }
= root_url = root_url
= select_tag :namespace_id, namespaces_options(namespace_id_from(params) || :current_user, display_path: true, extra_group: namespace_id_from(params)), class: 'select2 js-select-namespace', tabindex: 1 = select_tag :namespace_id, namespaces_options(namespace_id_from(params) || :current_user, display_path: true, extra_group: namespace_id_from(params)), class: 'select2 js-select-namespace', tabindex: 1
- else - else
.input-group-addon.static-namespace .input-group-addon.static-namespace.has-tooltip{ title: user_url(current_user.username) + '/' }
#{root_url}#{current_user.username}/ #{user_url(current_user.username)}/
= hidden_field_tag :namespace_id, value: current_user.namespace_id = hidden_field_tag :namespace_id, value: current_user.namespace_id
.form-group.col-xs-12.col-sm-6.project-path .form-group.col-xs-12.col-sm-6.project-path
= label_tag :path, 'Project name', class: 'label-light' = label_tag :path, 'Project name', class: 'label-light'
......
...@@ -67,12 +67,8 @@ ...@@ -67,12 +67,8 @@
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:25px 0;font-size:13px;line-height:1.6;color:#5c5c5c;" } %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:25px 0;font-size:13px;line-height:1.6;color:#5c5c5c;" }
%img{ alt: "GitLab", height: "33", src: image_url('mailers/gitlab_footer_logo.gif'), style: "display:block;margin:0 auto 1em;", width: "90" }/ %img{ alt: "GitLab", height: "33", src: image_url('mailers/gitlab_footer_logo.gif'), style: "display:block;margin:0 auto 1em;", width: "90" }/
%div %div
%a{ href: profile_notifications_url, style: "color:#3777b0;text-decoration:none;" } Manage all notifications - manage_notifications_link = link_to(_("Manage all notifications"), profile_notifications_url, style: "color:#3777b0;text-decoration:none;")
&middot; - help_link = link_to(_("Help"), help_url, style: "color:#3777b0;text-decoration:none;")
%a{ href: help_url, style: "color:#3777b0;text-decoration:none;" } Help = _("You're receiving this email because of your account on %{host}. %{manage_notifications_link} &middot; %{help_link}").html_safe % { host: Gitlab.config.gitlab.host, manage_notifications_link: manage_notifications_link, help_link: help_link }
%div
You're receiving this email because of your account on
= succeed "." do
%a{ href: root_url, style: "color:#3777b0;text-decoration:none;" }= Gitlab.config.gitlab.host
= yield :additional_footer = yield :additional_footer
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
.mobile-overlay .mobile-overlay
.alert-wrapper .alert-wrapper
= render "layouts/broadcast" = render "layouts/broadcast"
= render 'layouts/header/read_only_banner'
= yield :flash_message = yield :flash_message
- unless @hide_breadcrumbs - unless @hide_breadcrumbs
= render "layouts/nav/breadcrumbs" = render "layouts/nav/breadcrumbs"
......
- message = read_only_message
- if message
.flash-container.flash-container-page
.flash-notice
%div{ class: (container_class) }
%span
= message
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
profile_url: url_for(params.merge(lineprofiler: 'true')) }, profile_url: url_for(params.merge(lineprofiler: 'true')) },
class: Peek.env } class: Peek.env }
#peek-view-performance-bar #peek-view-performance-bar.hidden
= render_server_response_time = render_server_response_time
%span#serverstats %span#serverstats
%ul.performance-bar %ul.performance-bar
...@@ -13,6 +13,6 @@ ...@@ -13,6 +13,6 @@
#{time_ago_with_tooltip(event.created_at)} #{time_ago_with_tooltip(event.created_at)}
.pull-right .flex-right
= link_to new_mr_path_from_push_event(event), title: _("New merge request"), class: "btn btn-info btn-sm qa-create-merge-request" do = link_to new_mr_path_from_push_event(event), title: _("New merge request"), class: "btn btn-info btn-sm qa-create-merge-request" do
#{ _('Create merge request') } #{ _('Create merge request') }
...@@ -9,12 +9,12 @@ ...@@ -9,12 +9,12 @@
Project path Project path
.input-group .input-group
- if current_user.can_select_namespace? - if current_user.can_select_namespace?
.input-group-addon .input-group-addon.has-tooltip{ title: root_url }
= root_url = root_url
= f.select :namespace_id, namespaces_options(namespace_id_from(params) || :current_user, display_path: true, extra_group: namespace_id_from(params)), {}, { class: 'select2 js-select-namespace qa-project-namespace-select', tabindex: 1} = f.select :namespace_id, namespaces_options(namespace_id_from(params) || :current_user, display_path: true, extra_group: namespace_id_from(params)), {}, { class: 'select2 js-select-namespace qa-project-namespace-select', tabindex: 1}
- else - else
.input-group-addon.static-namespace .input-group-addon.static-namespace.has-tooltip{ title: user_url(current_user.username) + '/' }
#{user_url(current_user.username)}/ #{user_url(current_user.username)}/
= f.hidden_field :namespace_id, value: current_user.namespace_id = f.hidden_field :namespace_id, value: current_user.namespace_id
.form-group.project-path.col-sm-6 .form-group.project-path.col-sm-6
......
...@@ -5,81 +5,82 @@ ...@@ -5,81 +5,82 @@
- number_commits_behind = diverging_commit_counts[:behind] - number_commits_behind = diverging_commit_counts[:behind]
- number_commits_ahead = diverging_commit_counts[:ahead] - number_commits_ahead = diverging_commit_counts[:ahead]
- merge_project = can?(current_user, :create_merge_request, @project) ? @project : (current_user && current_user.fork_of(@project)) - merge_project = can?(current_user, :create_merge_request, @project) ? @project : (current_user && current_user.fork_of(@project))
%li{ class: "js-branch-#{branch.name}" } %li{ class: "branch-item js-branch-#{branch.name}" }
%div .branch-info
= link_to project_tree_path(@project, branch.name), class: 'item-title str-truncated ref-name' do .branch-title
= sprite_icon('fork', size: 12) = link_to project_tree_path(@project, branch.name), class: 'item-title str-truncated-100 ref-name' do
= branch.name = sprite_icon('fork', size: 12)
&nbsp; = branch.name
- if branch.name == @repository.root_ref &nbsp;
%span.label.label-primary default - if branch.name == @repository.root_ref
- elsif merged %span.label.label-primary default
%span.label.label-info.has-tooltip{ title: s_('Branches|Merged into %{default_branch}') % { default_branch: @repository.root_ref } } - elsif merged
= s_('Branches|merged') %span.label.label-info.has-tooltip{ title: s_('Branches|Merged into %{default_branch}') % { default_branch: @repository.root_ref } }
= s_('Branches|merged')
- if protected_branch?(@project, branch) - if protected_branch?(@project, branch)
%span.label.label-success %span.label.label-success
= s_('Branches|protected') = s_('Branches|protected')
.controls.hidden-xs<
- if merge_project && create_mr_button?(@repository.root_ref, branch.name)
= link_to create_mr_path(@repository.root_ref, branch.name), class: 'btn btn-default' do
= _('Merge request')
- if branch.name != @repository.root_ref .block-truncated
= link_to project_compare_index_path(@project, from: @repository.root_ref, to: branch.name), - if commit
class: "btn btn-default #{'prepend-left-10' unless merge_project}", = render 'projects/branches/commit', commit: commit, project: @project
method: :post, - else
title: s_('Branches|Compare') do = s_('Branches|Cant find HEAD commit for this branch')
= s_('Branches|Compare')
= render 'projects/buttons/download', project: @project, ref: branch.name, pipeline: @refs_pipelines[branch.name] - if branch.name != @repository.root_ref
.divergence-graph{ title: s_('%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead') % { number_commits_behind: diverging_count_label(number_commits_behind),
default_branch: @repository.root_ref,
number_commits_ahead: diverging_count_label(number_commits_ahead) } }
.graph-side
.bar.bar-behind{ style: "width: #{number_commits_behind * bar_graph_width_factor}%" }
%span.count.count-behind= diverging_count_label(number_commits_behind)
.graph-separator
.graph-side
.bar.bar-ahead{ style: "width: #{number_commits_ahead * bar_graph_width_factor}%" }
%span.count.count-ahead= diverging_count_label(number_commits_ahead)
- if can?(current_user, :push_code, @project) .controls.hidden-xs<
- if branch.name == @project.repository.root_ref - if merge_project && create_mr_button?(@repository.root_ref, branch.name)
%button{ class: "btn btn-remove remove-row js-ajax-loading-spinner has-tooltip disabled", = link_to create_mr_path(@repository.root_ref, branch.name), class: 'btn btn-default' do
disabled: true, = _('Merge request')
title: s_('Branches|The default branch cannot be deleted') }
= icon("trash-o")
- elsif protected_branch?(@project, branch)
- if can?(current_user, :delete_protected_branch, @project)
%button{ class: "btn btn-remove remove-row js-ajax-loading-spinner has-tooltip",
title: s_('Branches|Delete protected branch'),
data: { toggle: "modal",
target: "#modal-delete-branch",
delete_path: project_branch_path(@project, branch.name),
branch_name: branch.name,
is_merged: ("true" if merged) } }
= icon("trash-o")
- else
%button{ class: "btn btn-remove remove-row js-ajax-loading-spinner has-tooltip disabled",
disabled: true,
title: s_('Branches|Only a project master or owner can delete a protected branch') }
= icon("trash-o")
- else
= link_to project_branch_path(@project, branch.name),
class: "btn btn-remove remove-row js-ajax-loading-spinner has-tooltip",
title: s_('Branches|Delete branch'),
method: :delete,
data: { confirm: s_("Branches|Deleting the '%{branch_name}' branch cannot be undone. Are you sure?") % { branch_name: branch.name } },
remote: true,
'aria-label' => s_('Branches|Delete branch') do
= icon("trash-o")
- if branch.name != @repository.root_ref - if branch.name != @repository.root_ref
.divergence-graph{ title: s_('%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead') % { number_commits_behind: diverging_count_label(number_commits_behind), = link_to project_compare_index_path(@project, from: @repository.root_ref, to: branch.name),
default_branch: @repository.root_ref, class: "btn btn-default #{'prepend-left-10' unless merge_project}",
number_commits_ahead: diverging_count_label(number_commits_ahead) } } method: :post,
.graph-side title: s_('Branches|Compare') do
.bar.bar-behind{ style: "width: #{number_commits_behind * bar_graph_width_factor}%" } = s_('Branches|Compare')
%span.count.count-behind= diverging_count_label(number_commits_behind)
.graph-separator
.graph-side
.bar.bar-ahead{ style: "width: #{number_commits_ahead * bar_graph_width_factor}%" }
%span.count.count-ahead= diverging_count_label(number_commits_ahead)
= render 'projects/buttons/download', project: @project, ref: branch.name, pipeline: @refs_pipelines[branch.name]
- if commit - if can?(current_user, :push_code, @project)
= render 'projects/branches/commit', commit: commit, project: @project - if branch.name == @project.repository.root_ref
- else %button{ class: "btn btn-remove remove-row js-ajax-loading-spinner has-tooltip disabled",
%p disabled: true,
= s_('Branches|Cant find HEAD commit for this branch') title: s_('Branches|The default branch cannot be deleted') }
= icon("trash-o")
- elsif protected_branch?(@project, branch)
- if can?(current_user, :delete_protected_branch, @project)
%button{ class: "btn btn-remove remove-row js-ajax-loading-spinner has-tooltip",
title: s_('Branches|Delete protected branch'),
data: { toggle: "modal",
target: "#modal-delete-branch",
delete_path: project_branch_path(@project, branch.name),
branch_name: branch.name,
is_merged: ("true" if merged) } }
= icon("trash-o")
- else
%button{ class: "btn btn-remove remove-row js-ajax-loading-spinner has-tooltip disabled",
disabled: true,
title: s_('Branches|Only a project master or owner can delete a protected branch') }
= icon("trash-o")
- else
= link_to project_branch_path(@project, branch.name),
class: "btn btn-remove remove-row js-ajax-loading-spinner has-tooltip",
title: s_('Branches|Delete branch'),
method: :delete,
data: { confirm: s_("Branches|Deleting the '%{branch_name}' branch cannot be undone. Are you sure?") % { branch_name: branch.name } },
remote: true,
'aria-label' => s_('Branches|Delete branch') do
= icon("trash-o")
= form_for @project, url: namespace_project_pages_path(@project.namespace.becomes(Namespace), @project), html: { class: 'inline', title: pages_https_only_title } do |f|
= f.check_box :pages_https_only, class: 'pull-left', disabled: pages_https_only_disabled?
.prepend-left-20
= f.label :pages_https_only, class: pages_https_only_label_class do
%strong Force domains with SSL certificates to use HTTPS
- unless pages_https_only_disabled?
.prepend-top-10
= f.submit 'Save', class: 'btn btn-success'
...@@ -13,6 +13,9 @@ ...@@ -13,6 +13,9 @@
Combined with the power of GitLab CI and the help of GitLab Runner Combined with the power of GitLab CI and the help of GitLab Runner
you can deploy static pages for your individual projects, your user or your group. you can deploy static pages for your individual projects, your user or your group.
- if Gitlab.config.pages.external_https
= render 'https_only'
%hr.clearfix %hr.clearfix
= render 'access' = render 'access'
......
---
title: Add missing pagination on the commit diff endpoint
merge_request: 17203
author: Maxime Roussin-Bélanger
type: fixed
---
title: Moved o_auth/saml/ldap modules under gitlab/auth
merge_request: 17359
author: Horatiu Eugen Vlad
---
title: Update issue closing pattern to allow variations in punctuation
merge_request: 17198
author: Vicky Chijwani
type: changed
---
title: Clear the Labels dropdown search filter after a selection is made
merge_request: 17393
author: Andrew Torres
type: changed
---
title: Update to github-linguist 5.3.x
merge_request: 17241
author: Ken Ding
type: other
---
title: Group MRs on issue page by project and namespace.
merge_request: 8494
author: Jeff Stubler
---
title: Add project export API
merge_request: 15860
author: Travis Miller
type: added
---
title: Add email button to new issue by email
merge_request: 10942
author: Islam Wazery
---
title: Fix duplicate system notes when merging a merge request.
merge_request: 17035
author:
type: fixed
---
title: Allow installation of GitLab Runner with a single click
merge_request: 17134
author:
type: added
---
title: Fix Slack/Mattermost notifications not respecting `notify_only_default_branch` setting for pushes
merge_request: 17345
author:
type: fixed
---
title: remove avater underline
merge_request: 17219
author: Ken Ding
type: fixed
---
title: Fix Teleporting Emoji
merge_request: 16963
author: Jared Deckard <jared.deckard@gmail.com>
type: fixed
---
title: update toml-rb to 1.0.0
merge_request: 17259
author: Ken Ding
type: other
---
title: Display a link to external issue tracker when enabled
merge_request:
author:
type: changed
---
title: Handle empty state in Pipelines page
merge_request:
author:
type: fixed
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