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": [
["latest", { "es2015": { "modules": false } }],
"stage-2"
],
"presets": [["latest", { "es2015": { "modules": false } }], "stage-2"],
"env": {
"coverage": {
"plugins": [
["istanbul", {
"exclude": [
"spec/javascripts/**/*",
"app/assets/javascripts/locale/**/app.js"
]
}],
["transform-define", {
"process.env.BABEL_ENV": "coverage"
}]
[
"istanbul",
{
"exclude": ["spec/javascripts/**/*", "app/assets/javascripts/locale/**/app.js"]
}
],
[
"transform-define",
{
"process.env.BABEL_ENV": "coverage"
}
]
]
}
}
......
/app/assets/javascripts/locale/**/app.js
/config/
/builds/
/coverage/
/coverage-javascript/
/node_modules/
/public/
/scripts/
/tmp/
/vendor/
karma.config.js
webpack.config.js
svg.config.js
/app/assets/javascripts/locale/**/app.js
......@@ -544,7 +544,7 @@ migration:path-mysql:
.db-rollback: &db-rollback
<<: *dedicated-no-docs-pull-cache-job
script:
- bundle exec rake db:rollback STEP=119
- bundle exec rake db:migrate VERSION=20170523121229
- bundle exec rake db:migrate
db:rollback-pg:
......
/app/assets/javascripts/locale/**/app.js
/node_modules/
/public/
/vendor/
/tmp/
{
"printWidth": 100,
"singleQuote": true,
"trailingComma": "all"
"trailingComma": "es5",
"overrides": [
{
"files": ["**/app/**/*", "**/spec/**/*"],
"options": {
"trailingComma": "all"
}
}
]
}
......@@ -118,6 +118,9 @@ Gitlab/ModuleWithInstanceVariables:
- spec/support/**/*.rb
- features/steps/**/*.rb
Gitlab/HTTParty:
Enabled: true
GitlabSecurity/PublicSend:
Enabled: true
Exclude:
......
This diff is collapsed.
......@@ -38,7 +38,7 @@ gem 'devise', '~> 4.2'
gem 'doorkeeper', '~> 4.3'
gem 'doorkeeper-openid_connect', '~> 1.3'
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-cas3', '~> 1.1.4'
gem 'omniauth-facebook', '~> 4.0.0'
......@@ -231,7 +231,7 @@ gem 'sanitize', '~> 2.0'
gem 'babosa', '~> 1.0.2'
# Sanitizes SVG input
gem 'loofah', '~> 2.0.3'
gem 'loofah', '~> 2.2'
# Working with license
gem 'licensee', '~> 8.9'
......
......@@ -143,6 +143,7 @@ GEM
connection_pool (2.2.1)
crack (0.4.3)
safe_yaml (~> 1.0.0)
crass (1.0.3)
creole (0.5.0)
css_parser (1.5.0)
addressable
......@@ -485,7 +486,8 @@ GEM
actionpack (>= 4, < 5.2)
activesupport (>= 4, < 5.2)
railties (>= 4, < 5.2)
loofah (2.0.3)
loofah (2.2.2)
crass (~> 1.0.2)
nokogiri (>= 1.5.9)
mail (2.7.0)
mini_mime (>= 0.1.1)
......@@ -527,8 +529,8 @@ GEM
omniauth (1.8.1)
hashie (>= 3.4.6, < 3.6.0)
rack (>= 1.6.2, < 3)
omniauth-auth0 (1.4.1)
omniauth-oauth2 (~> 1.1)
omniauth-auth0 (2.0.0)
omniauth-oauth2 (~> 1.4)
omniauth-authentiq (0.3.1)
omniauth-oauth2 (~> 1.3, >= 1.3.1)
omniauth-azure-oauth2 (0.0.9)
......@@ -679,8 +681,8 @@ GEM
activesupport (>= 4.2.0, < 5.0)
nokogiri (~> 1.6)
rails-deprecated_sanitizer (>= 1.0.1)
rails-html-sanitizer (1.0.3)
loofah (~> 2.0)
rails-html-sanitizer (1.0.4)
loofah (~> 2.2, >= 2.2.2)
rails-i18n (4.0.9)
i18n (~> 0.7)
railties (~> 4.0)
......@@ -1093,7 +1095,7 @@ DEPENDENCIES
license_finder (~> 3.1)
licensee (~> 8.9)
lograge (~> 0.5)
loofah (~> 2.0.3)
loofah (~> 2.2)
mail_room (~> 0.9.1)
method_source (~> 0.8)
minitest (~> 5.7.0)
......@@ -1105,7 +1107,7 @@ DEPENDENCIES
oauth2 (~> 1.4)
octokit (~> 4.8)
omniauth (~> 1.8)
omniauth-auth0 (~> 1.4.1)
omniauth-auth0 (~> 2.0.0)
omniauth-authentiq (~> 0.3.1)
omniauth-azure-oauth2 (~> 0.0.9)
omniauth-cas3 (~> 1.1.4)
......
10.6.0-pre
10.7.0-pre
import $ from 'jquery';
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';
const Store = gl.issueBoards.BoardsStore;
......@@ -45,7 +45,7 @@ gl.issueBoards.IssueCardInner = Vue.extend({
};
},
components: {
userAvatarLink,
UserAvatarLink,
},
computed: {
numberOverLimit() {
......
......@@ -4,7 +4,7 @@
import $ from 'jquery';
import { s__ } from '~/locale';
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 { mergeUrlParams } from '~/lib/utils/url_utility';
......@@ -15,7 +15,7 @@ import groupsComponent from './groups.vue';
export default {
components: {
loadingIcon,
modal,
DeprecatedModal,
groupsComponent,
},
props: {
......@@ -52,8 +52,9 @@ export default {
},
},
created() {
this.searchEmptyMessage = this.hideProjects ?
COMMON_STR.GROUP_SEARCH_EMPTY : COMMON_STR.GROUP_PROJECT_SEARCH_EMPTY;
this.searchEmptyMessage = this.hideProjects
? COMMON_STR.GROUP_SEARCH_EMPTY
: COMMON_STR.GROUP_PROJECT_SEARCH_EMPTY;
eventHub.$on('fetchPage', this.fetchPage);
eventHub.$on('toggleChildren', this.toggleChildren);
......@@ -72,22 +73,30 @@ export default {
eventHub.$off('updateGroups', this.updateGroups);
},
methods: {
fetchGroups({ parentId, page, filterGroupsBy, sortBy, archived, updatePagination }) {
return this.service.getGroups(parentId, page, filterGroupsBy, sortBy, archived)
.then((res) => {
if (updatePagination) {
this.updatePagination(res.headers);
}
fetchGroups({
parentId,
page,
filterGroupsBy,
sortBy,
archived,
updatePagination,
}) {
return this.service
.getGroups(parentId, page, filterGroupsBy, sortBy, archived)
.then(res => {
if (updatePagination) {
this.updatePagination(res.headers);
}
return res;
})
.then(res => res.json())
.catch(() => {
this.isLoading = false;
$.scrollTo(0);
return res;
})
.then(res => res.json())
.catch(() => {
this.isLoading = false;
$.scrollTo(0);
Flash(COMMON_STR.FAILURE);
});
Flash(COMMON_STR.FAILURE);
});
},
fetchAllGroups() {
const page = getParameterByName('page') || null;
......@@ -103,7 +112,7 @@ export default {
sortBy,
archived,
updatePagination: true,
}).then((res) => {
}).then(res => {
this.isLoading = false;
this.updateGroups(res, Boolean(filterGroupsBy));
});
......@@ -118,14 +127,18 @@ export default {
sortBy,
archived,
updatePagination: true,
}).then((res) => {
}).then(res => {
this.isLoading = false;
$.scrollTo(0);
const currentPath = mergeUrlParams({ page }, window.location.href);
window.history.replaceState({
page: currentPath,
}, document.title, currentPath);
window.history.replaceState(
{
page: currentPath,
},
document.title,
currentPath,
);
this.updateGroups(res);
});
......@@ -138,11 +151,13 @@ export default {
// eslint-disable-next-line promise/catch-or-return
this.fetchGroups({
parentId: parentGroup.id,
}).then((res) => {
this.store.setGroupChildren(parentGroup, res);
}).catch(() => {
parentGroup.isChildrenLoading = false;
});
})
.then(res => {
this.store.setGroupChildren(parentGroup, res);
})
.catch(() => {
parentGroup.isChildrenLoading = false;
});
} else {
parentGroup.isOpen = true;
}
......@@ -154,7 +169,11 @@ export default {
this.targetGroup = group;
this.targetParentGroup = parentGroup;
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() {
this.showModal = false;
......@@ -162,14 +181,15 @@ export default {
leaveGroup() {
this.showModal = false;
this.targetGroup.isBeingRemoved = true;
this.service.leaveGroup(this.targetGroup.leavePath)
this.service
.leaveGroup(this.targetGroup.leavePath)
.then(res => res.json())
.then((res) => {
.then(res => {
$.scrollTo(0);
this.store.removeGroup(this.targetGroup, this.targetParentGroup);
Flash(res.notice, 'notice');
})
.catch((err) => {
.catch(err => {
let message = COMMON_STR.FAILURE;
if (err.status === 403) {
message = COMMON_STR.LEAVE_FORBIDDEN;
......@@ -208,8 +228,8 @@ export default {
:search-empty-message="searchEmptyMessage"
:page-info="pageInfo"
/>
<modal
v-if="showModal"
<deprecated-modal
v-show="showModal"
kind="warning"
:primary-button-label="__('Leave')"
:title="__('Are you sure?')"
......
<script>
import { __ } from '~/locale';
import modal from '~/vue_shared/components/modal.vue';
import { __ } from '~/locale';
import DeprecatedModal from '~/vue_shared/components/deprecated_modal.vue';
export default {
components: {
modal,
export default {
components: {
DeprecatedModal,
},
props: {
branchId: {
type: String,
required: true,
},
props: {
branchId: {
type: String,
required: true,
},
type: {
type: String,
required: true,
},
path: {
type: String,
required: true,
},
type: {
type: String,
required: true,
},
data() {
return {
entryName: this.path !== '' ? `${this.path}/` : '',
};
path: {
type: String,
required: true,
},
computed: {
modalTitle() {
if (this.type === 'tree') {
return __('Create new directory');
}
},
data() {
return {
entryName: this.path !== '' ? `${this.path}/` : '',
};
},
computed: {
modalTitle() {
if (this.type === 'tree') {
return __('Create new directory');
}
return __('Create new file');
},
buttonLabel() {
if (this.type === 'tree') {
return __('Create directory');
}
return __('Create file');
},
formLabelName() {
if (this.type === 'tree') {
return __('Directory name');
}
return __('Create new file');
},
buttonLabel() {
if (this.type === 'tree') {
return __('Create directory');
}
return __('File name');
},
return __('Create file');
},
mounted() {
this.$refs.fieldName.focus();
formLabelName() {
if (this.type === 'tree') {
return __('Directory name');
}
return __('File name');
},
methods: {
createEntryInStore() {
this.$emit('create', {
branchId: this.branchId,
name: this.entryName,
type: this.type,
});
},
mounted() {
this.$refs.fieldName.focus();
},
methods: {
createEntryInStore() {
this.$emit('create', {
branchId: this.branchId,
name: this.entryName,
type: this.type,
});
this.hideModal();
},
hideModal() {
this.$emit('hide');
},
this.hideModal();
},
hideModal() {
this.$emit('hide');
},
};
},
};
</script>
<template>
<modal
<deprecated-modal
:title="modalTitle"
:primary-button-label="buttonLabel"
kind="success"
......@@ -95,5 +95,5 @@
</div>
</fieldset>
</form>
</modal>
</deprecated-modal>
</template>
......@@ -2,7 +2,7 @@
import { mapState, mapActions, mapGetters } from 'vuex';
import tooltip from '~/vue_shared/directives/tooltip';
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 commitFilesList from './commit_sidebar/list.vue';
import * as consts from '../stores/modules/commit/constants';
......@@ -10,7 +10,7 @@ import Actions from './commit_sidebar/actions.vue';
export default {
components: {
modal,
DeprecatedModal,
icon,
commitFilesList,
Actions,
......@@ -37,23 +37,20 @@ export default {
'lastCommitMsg',
'changedFiles',
]),
...mapState('commit', [
'commitMessage',
'submitCommitLoading',
]),
...mapState('commit', ['commitMessage', 'submitCommitLoading']),
...mapGetters('commit', [
'commitButtonDisabled',
'discardDraftButtonDisabled',
'branchName',
]),
statusSvg() {
return this.lastCommitMsg ? this.committedStateSvgPath : this.noChangesStateSvgPath;
return this.lastCommitMsg
? this.committedStateSvgPath
: this.noChangesStateSvgPath;
},
},
methods: {
...mapActions([
'setPanelCollapsedStatus',
]),
...mapActions(['setPanelCollapsedStatus']),
...mapActions('commit', [
'updateCommitMessage',
'discardDraft',
......@@ -67,8 +64,9 @@ export default {
});
},
forceCreateNewBranch() {
return this.updateCommitAction(consts.COMMIT_TO_NEW_BRANCH)
.then(() => this.commitChanges());
return this.updateCommitAction(consts.COMMIT_TO_NEW_BRANCH).then(() =>
this.commitChanges(),
);
},
},
};
......@@ -81,7 +79,7 @@ export default {
'multi-file-commit-empty-state-container': !changedFiles.length
}"
>
<modal
<deprecated-modal
id="ide-create-branch-modal"
:primary-button-label="__('Create new branch')"
kind="success"
......@@ -92,7 +90,7 @@ export default {
{{ __(`This branch has changed since you started editing.
Would you like to create a new branch?`) }}
</template>
</modal>
</deprecated-modal>
<commit-files-list
title="Staged"
:file-list="changedFiles"
......
......@@ -1727,6 +1727,7 @@ export default class Notes {
// Get Form metadata
const $submitBtn = $(e.target);
$submitBtn.prop('disabled', true);
let $form = $submitBtn.parents('form');
const $closeBtn = $form.find('.js-note-target-close');
const isDiscussionNote =
......@@ -1761,7 +1762,6 @@ export default class Notes {
// If comment is to resolve discussion, disable submit buttons while
// comment posting is finished.
if (isDiscussionResolve) {
$submitBtn.disable();
$form.find('.js-comment-submit-button').disable();
}
......@@ -1816,6 +1816,7 @@ export default class Notes {
.then(res => {
const note = res.data;
$submitBtn.prop('disabled', false);
// Submission successful! remove placeholder
$notesContainer.find(`#${noteUniqueId}`).remove();
......@@ -1899,7 +1900,7 @@ export default class Notes {
.catch(() => {
// Submission failed, remove placeholder note and show Flash error message
$notesContainer.find(`#${noteUniqueId}`).remove();
$submitBtn.prop('disabled', false);
const blurEvent = new CustomEvent('blur.imageDiff', {
detail: e,
});
......
<script>
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';
export default {
components: {
modal,
DeprecatedModal,
},
props: {
deleteProjectUrl: {
......@@ -79,7 +79,7 @@
</script>
<template>
<modal
<deprecated-modal
id="delete-project-modal"
:title="title"
:text="text"
......@@ -121,5 +121,5 @@
/>
</form>
</template>
</modal>
</deprecated-modal>
</template>
<script>
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';
export default {
components: {
modal,
DeprecatedModal,
},
props: {
deleteUserUrl: {
......@@ -113,7 +113,7 @@
</script>
<template>
<modal
<deprecated-modal
id="delete-user-modal"
:title="title"
:text="text"
......@@ -170,5 +170,5 @@
{{ secondaryButtonLabel }}
</button>
</template>
</modal>
</deprecated-modal>
</template>
......@@ -2,14 +2,14 @@
import axios from '~/lib/utils/axios_utils';
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 { redirectTo } from '~/lib/utils/url_utility';
import eventHub from '../event_hub';
export default {
components: {
modal,
DeprecatedModal,
},
props: {
issueCount: {
......@@ -92,7 +92,7 @@ Once deleted, it cannot be undone or recovered.`),
</script>
<template>
<modal
<deprecated-modal
id="delete-milestone-modal"
:title="title"
:text="text"
......@@ -106,5 +106,5 @@ Once deleted, it cannot be undone or recovered.`),
<p v-html="props.text"></p>
</template>
</modal>
</deprecated-modal>
</template>
......@@ -27,12 +27,21 @@ export default {
required: true,
},
},
computed: {
metricDetails() {
return this.currentRequest.details[this.metric];
},
detailsList() {
return this.metricDetails[this.details];
},
},
};
</script>
<template>
<div
:id="`peek-view-${metric}`"
class="view"
v-if="currentRequest.details"
>
<button
:data-target="`#modal-peek-${metric}-details`"
......@@ -40,34 +49,40 @@ export default {
type="button"
data-toggle="modal"
>
<span
v-if="currentRequest.details"
class="bold"
>
{{ currentRequest.details[metric].duration }}
/
{{ currentRequest.details[metric].calls }}
</span>
{{ metricDetails.duration }}
/
{{ metricDetails.calls }}
</button>
<gl-modal
v-if="currentRequest.details"
:id="`modal-peek-${metric}-details`"
:header-title-text="header"
class="performance-bar-modal"
>
<table class="table">
<tr
v-for="(item, index) in currentRequest.details[metric][details]"
:key="index"
>
<td><strong>{{ item.duration }}ms</strong></td>
<td
v-for="key in keys"
:key="key"
<table
class="table"
>
<template v-if="detailsList.length">
<tr
v-for="(item, index) in detailsList"
:key="index"
>
{{ item[key] }}
</td>
</tr>
<td><strong>{{ item.duration }}ms</strong></td>
<td
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>
<div slot="footer">
......
......@@ -113,27 +113,21 @@ export default {
id="js-peek"
: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
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
v-if="initialRequest && currentRequest.details"
/>
......@@ -186,6 +180,12 @@ export default {
gc
</span>
</div>
<request-selector
v-if="currentRequest"
:current-request="currentRequest"
:requests="requests"
@change-current-request="changeCurrentRequest"
/>
</div>
</div>
</template>
......@@ -37,7 +37,7 @@ export default {
<template>
<div
id="peek-request-selector"
class="append-right-5 pull-right"
class="pull-right"
>
<select v-model="currentRequestId">
<option
......
......@@ -5,6 +5,8 @@ export default {
.getElementById('peek-view-performance-bar')
.cloneNode(true);
upstreamPerformanceBar.classList.remove('hidden');
this.$refs.wrapper.appendChild(upstreamPerformanceBar);
},
};
......
......@@ -4,9 +4,9 @@ import Vue from 'vue';
import performanceBarApp from './components/performance_bar_app.vue';
import PerformanceBarStore from './stores/performance_bar_store';
export default () =>
export default ({ container }) =>
new Vue({
el: '#js-peek',
el: container,
components: {
performanceBarApp,
},
......
import Vue from 'vue';
import _ from 'underscore';
import axios from '../../lib/utils/axios_utils';
let vueResourceInterceptor;
export default class PerformanceBarService {
static fetchRequestDetails(peekUrl, requestId) {
return axios.get(peekUrl, { params: { request_id: requestId } });
}
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 => {
const requestId = response.headers['x-request-id'];
const requestUrl = response.config.url;
......@@ -20,5 +37,9 @@ export default class PerformanceBarService {
static removeInterceptor(interceptor) {
axios.interceptors.response.eject(interceptor);
Vue.http.interceptors = _.without(
Vue.http.interceptors,
vueResourceInterceptor,
);
}
}
<script>
import modal from '~/vue_shared/components/modal.vue';
import DeprecatedModal from '~/vue_shared/components/deprecated_modal.vue';
import { s__, sprintf } from '~/locale';
import pipelinesTableRowComponent from './pipelines_table_row.vue';
import eventHub from '../event_hub';
......@@ -12,7 +12,7 @@
export default {
components: {
pipelinesTableRowComponent,
modal,
DeprecatedModal,
},
props: {
pipelines: {
......@@ -120,7 +120,7 @@
:auto-devops-help-path="autoDevopsHelpPath"
:view-type="viewType"
/>
<modal
<deprecated-modal
id="confirmation-modal"
:title="modalTitle"
:text="modalText"
......@@ -134,6 +134,6 @@
>
<p v-html="props.text"></p>
</template>
</modal>
</deprecated-modal>
</div>
</template>
<script>
import modal from '~/vue_shared/components/modal.vue';
import DeprecatedModal from '~/vue_shared/components/deprecated_modal.vue';
import { __, s__, sprintf } from '~/locale';
import csrf from '~/lib/utils/csrf';
export default {
components: {
modal,
DeprecatedModal,
},
props: {
actionUrl: {
......@@ -76,7 +76,7 @@ Once you confirm %{deleteAccount}, it cannot be undone or recovered.`),
</script>
<template>
<modal
<deprecated-modal
id="delete-account-modal"
:title="s__('Profiles|Delete your account?')"
:text="text"
......@@ -131,5 +131,5 @@ Once you confirm %{deleteAccount}, it cannot be undone or recovered.`),
</form>
</template>
</modal>
</deprecated-modal>
</template>
<script>
import { sprintf, s__ } from '~/locale';
import statusCodes from '../../lib/utils/http_status';
import { bytesToMiB } from '../../lib/utils/number_utils';
import { backOff } from '../../lib/utils/common_utils';
......@@ -45,17 +46,28 @@ export default {
shouldShowMetricsUnavailable() {
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 memoryFrom = Number(this.memoryFrom);
let memoryUsageMsg = '';
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) {
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() {
......@@ -130,24 +142,22 @@ export default {
<i
class="fa fa-spinner fa-spin usage-info-load-spinner"
aria-hidden="true">
</i>Loading deployment statistics
</i>{{ s__('mrWidget|Loading deployment statistics') }}
</p>
<p
v-if="shouldShowMemoryGraph"
class="usage-info js-usage-info">
<a
:href="metricsMonitoringUrl"
>Memory</a> usage <b>{{ memoryChangeType }}</b> from {{ memoryFrom }}MB to {{ memoryTo }}MB
{{ memoryChangeMessage }}
</p>
<p
v-if="shouldShowLoadFailure"
class="usage-info js-usage-info usage-info-failed">
Failed to load deployment statistics
{{ s__('mrWidget|Failed to load deployment statistics') }}
</p>
<p
v-if="shouldShowMetricsUnavailable"
class="usage-info js-usage-info usage-info-unavailable">
Deployment statistics are not available currently
{{ s__('mrWidget|Deployment statistics are not available currently') }}
</p>
<memory-graph
v-if="shouldShowMemoryGraph"
......
<script>
/* eslint-disable vue/require-default-prop */
export default {
name: 'Modal',
name: 'DeprecatedModal', // use GlModal instead
props: {
id: {
......
<script>
import modal from './modal.vue';
import DeprecatedModal from './deprecated_modal.vue';
export default {
name: 'RecaptchaModal',
components: {
modal,
DeprecatedModal,
},
props: {
......@@ -65,7 +65,7 @@
</script>
<template>
<modal
<deprecated-modal
kind="warning"
class="recaptcha-modal js-recaptcha-modal"
:hide-footer="true"
......@@ -82,5 +82,5 @@
>
</div>
</div>
</modal>
</deprecated-modal>
</template>
......@@ -446,6 +446,10 @@ img.emoji {
opacity: .5;
}
.break-word {
word-wrap: break-word;
}
/** COMMON CLASSES **/
.prepend-top-0 { margin-top: 0; }
.prepend-top-5 { margin-top: 5px; }
......
......@@ -622,7 +622,7 @@
}
.dropdown-content {
max-height: $dropdown-max-height;
max-height: 252px;
overflow-y: auto;
}
......@@ -699,6 +699,31 @@
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-content {
max-height: 230px;
......
......@@ -152,3 +152,4 @@
.sidebar-collapsed-icon .sidebar-collapsed-value {
font-size: 12px;
}
......@@ -31,8 +31,12 @@
.dropdown-menu-issues-board-new {
width: 320px;
.open & {
max-height: 400px;
}
.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 {
padding: 12px 12px 0 0;
float: right;
padding: 0 6px;
.graph-side {
position: relative;
......@@ -53,3 +64,9 @@
background-color: $divergence-graph-separator-bg;
}
}
.divergence-graph,
.branch-item .controls {
flex: 0 0 auto;
white-space: nowrap;
}
......@@ -162,17 +162,14 @@
* Last push widget
*/
.event-last-push {
overflow: auto;
width: 100%;
display: flex;
align-items: center;
.event-last-push-text {
@include str-truncated(100%);
padding: 4px 0;
font-size: 13px;
float: left;
margin-right: -150px;
padding-right: 150px;
line-height: 20px;
margin-right: $gl-padding;
}
}
......
......@@ -26,9 +26,15 @@
}
}
.dropdown-menu-labels {
.dropdown-content {
max-height: 135px;
}
}
.dropdown-new-label {
.dropdown-content {
max-height: 260px;
max-height: 136px;
}
}
......
......@@ -9,7 +9,6 @@
.new_project,
.edit-project,
.import-project {
.help-block {
margin-bottom: 10px;
}
......@@ -18,18 +17,25 @@
border-radius: $border-radius-base;
}
.input-group > div {
.input-group {
display: flex;
&:last-child {
padding-right: 0;
.select2-container {
display: unset;
max-width: unset;
width: unset !important;
flex-grow: 1;
}
> div {
&:last-child {
padding-right: 0;
}
}
}
@media (max-width: $screen-xs-max) {
.input-group > div {
margin-bottom: 14px;
&:last-child {
margin-bottom: 0;
}
......@@ -41,17 +47,24 @@
}
.input-group-addon {
overflow: hidden;
text-overflow: ellipsis;
line-height: unset;
width: unset;
max-width: 50%;
text-align: left;
&.static-namespace {
height: 35px;
border-radius: 3px;
border: 1px solid $border-color;
max-width: 100%;
flex-grow: 1;
}
+ .select2 a,
+ .btn-default {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
border-radius: 0 $border-radius-base $border-radius-base 0;
}
}
}
......@@ -290,7 +303,7 @@
font-size: 13px;
font-weight: $gl-font-weight-bold;
line-height: 13px;
letter-spacing: .4px;
letter-spacing: 0.4px;
padding: 6px 14px;
text-align: center;
vertical-align: middle;
......@@ -443,7 +456,7 @@ a.deploy-project-label {
text-decoration: none;
&.disabled {
opacity: .3;
opacity: 0.3;
cursor: not-allowed;
}
}
......@@ -600,26 +613,26 @@ a.deploy-project-label {
}
.first-column {
@media(min-width: $screen-xs-min) {
@media (min-width: $screen-xs-min) {
max-width: 50%;
padding-right: 30px;
}
@media(max-width: $screen-xs-max) {
@media (max-width: $screen-xs-max) {
max-width: 100%;
width: 100%;
}
}
.second-column {
@media(min-width: $screen-xs-min) {
@media (min-width: $screen-xs-min) {
width: 50%;
flex: 1;
padding-left: 30px;
position: relative;
}
@media(max-width: $screen-xs-max) {
@media (max-width: $screen-xs-max) {
max-width: 100%;
width: 100%;
padding-left: 0;
......@@ -632,7 +645,7 @@ a.deploy-project-label {
}
&::before {
content: "OR";
content: 'OR';
position: absolute;
left: -10px;
top: 50%;
......@@ -656,7 +669,7 @@ a.deploy-project-label {
}
&::after {
content: "";
content: '';
position: absolute;
background-color: $border-color;
bottom: 0;
......@@ -921,10 +934,7 @@ pre.light-well {
border-right: solid 1px transparent;
}
}
}
.protected-tags-list,
.protected-branches-list {
.dropdown-menu-toggle {
width: 100%;
max-width: 300px;
......
......@@ -112,7 +112,7 @@ input[type="checkbox"]:hover {
}
.dropdown-content {
max-height: 350px;
max-height: 302px;
}
}
......
......@@ -15,6 +15,10 @@
line-height: $performance-bar-height;
color: $perf-bar-text;
select {
width: 200px;
}
&.disabled {
display: none;
}
......@@ -43,12 +47,6 @@
}
}
.wrapper {
width: 80%;
height: $performance-bar-height;
margin: 0 auto;
}
// UI Elements
.bucket {
background: $perf-bar-bucket-bg;
......@@ -108,8 +106,14 @@
}
}
.performance-bar-modal .modal-footer {
display: none;
.performance-bar-modal {
.modal-footer {
display: none;
}
.modal-dialog {
width: 860px;
}
}
}
......
......@@ -3,23 +3,9 @@
# Automatically sets the layout and ensures an administrator is logged in
class Admin::ApplicationController < ApplicationController
before_action :authenticate_admin!
before_action :display_read_only_information
layout 'admin'
def authenticate_admin!
render_404 unless current_user.admin?
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
......@@ -18,6 +18,18 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
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
def failure_message
exception = env["omniauth.error"]
......@@ -95,6 +107,14 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
handle_omniauth
end
def auth0
if oauth['uid'].blank?
fail_auth0_login
else
handle_omniauth
end
end
private
def handle_omniauth
......@@ -170,6 +190,12 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
redirect_to new_user_session_path
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
label = Gitlab::Auth::OAuth::Provider.label_for(oauth['provider'])
flash[:alert] = "Signing in using #{label} has been disabled"
......
......@@ -21,4 +21,26 @@ class Projects::PagesController < Projects::ApplicationController
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
......@@ -10,10 +10,7 @@ class Projects::PipelinesSettingsController < Projects::ApplicationController
if service.execute
flash[:notice] = "Pipelines settings for '#{@project.name}' were successfully updated."
if service.run_auto_devops_pipeline?
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
run_autodevops_pipeline(service)
redirect_to project_settings_ci_cd_path(@project)
else
......@@ -24,6 +21,18 @@ class Projects::PipelinesSettingsController < Projects::ApplicationController
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
params.require(:project).permit(
:runners_token, :builds_enabled, :build_allow_git_fetch,
......
......@@ -323,4 +323,11 @@ module ApplicationHelper
def locale_path
asset_path("locale/#{Gitlab::I18n.locale}/app.js")
end
# Overridden in EE
def read_only_message
return unless Gitlab::Database.read_only?
_('You are on a read-only GitLab instance.')
end
end
......@@ -96,7 +96,7 @@ module ApplicationSettingsHelper
def repository_storages_options_for_select(selected)
options = Gitlab.config.repositories.storages.map do |name, storage|
["#{name} - #{storage['path']}", name]
["#{name} - #{storage['gitaly_address']}", name]
end
options_for_select(options, selected)
......@@ -245,7 +245,8 @@ module ApplicationSettingsHelper
:usage_ping_enabled,
:user_default_external,
:user_oauth_applications,
:version_check_enabled
:version_check_enabled,
:allow_local_requests_from_hooks_and_services
]
end
end
......@@ -531,4 +531,22 @@ module ProjectsHelper
def can_show_last_commit_in_list?(project)
can?(current_user, :read_cross_project) && project.commit
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
......@@ -330,7 +330,8 @@ class ApplicationSetting < ActiveRecord::Base
usage_ping_enabled: Settings.gitlab['usage_ping_enabled'],
gitaly_timeout_fast: 10,
gitaly_timeout_medium: 30,
gitaly_timeout_default: 55
gitaly_timeout_default: 55,
allow_local_requests_from_hooks_and_services: false
}
end
......
......@@ -14,7 +14,7 @@ module Ci
has_many :stages
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 :variables, class_name: 'Ci::PipelineVariable'
......
......@@ -21,7 +21,7 @@ module Avatarable
def avatar_type
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
......
......@@ -189,12 +189,6 @@ class Group < Namespace
owners.include?(user) && owners.size == 1
end
def avatar_type
unless self.avatar.image?
self.errors.add :avatar, "only images allowed"
end
end
def post_create_hook
Gitlab::AppLogger.info("Group \"#{name}\" was created")
......
......@@ -6,8 +6,10 @@ class PagesDomain < ActiveRecord::Base
validates :domain, hostname: { allow_numeric_hostname: true }
validates :domain, uniqueness: { case_sensitive: false }
validates :certificate, certificate: true, allow_nil: true, allow_blank: true
validates :key, certificate_key: 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 :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
validate :validate_pages_domain
......@@ -46,6 +48,10 @@ class PagesDomain < ActiveRecord::Base
!Gitlab::CurrentSettings.pages_domain_verification_enabled? || enabled_until.present?
end
def https?
certificate.present?
end
def to_param
domain
end
......
......@@ -38,6 +38,9 @@ class Project < ActiveRecord::Base
attachments: 2
}.freeze
# Valids ports to import from
VALID_IMPORT_PORTS = [22, 80, 443].freeze
cache_markdown_field :description, pipeline: :description
delegate :feature_available?, :builds_enabled?, :wiki_enabled?,
......@@ -264,6 +267,7 @@ class Project < ActiveRecord::Base
validate :visibility_level_allowed_by_group
validate :visibility_level_allowed_as_fork
validate :check_wiki_path_conflict
validate :validate_pages_https_only, if: -> { changes.has_key?(:pages_https_only) }
validates :repository_storage,
presence: true,
inclusion: { in: ->(_object) { Gitlab.config.repositories.storages.keys } }
......@@ -500,7 +504,7 @@ class Project < ActiveRecord::Base
end
def repository_storage_path
Gitlab.config.repositories.storages[repository_storage].try(:[], 'path')
Gitlab.config.repositories.storages[repository_storage]&.legacy_disk_path
end
def team
......@@ -734,6 +738,26 @@ class Project < ActiveRecord::Base
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
if persisted? && errors.include?(:path)
path_was
......
class AssemblaService < Service
include HTTParty
prop_accessor :token, :subdomain
validates :token, presence: true, if: :activated?
......@@ -31,6 +29,6 @@ class AssemblaService < Service
return unless supported_events.include?(data[:object_kind])
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
......@@ -117,14 +117,14 @@ class BambooService < CiService
url = build_url(path)
if username.blank? && password.blank?
HTTParty.get(url, verify: false)
Gitlab::HTTP.get(url, verify: false)
else
url << '&os_authType=basic'
HTTParty.get(url, verify: false,
basic_auth: {
username: username,
password: password
})
Gitlab::HTTP.get(url, verify: false,
basic_auth: {
username: username,
password: password
})
end
end
end
......@@ -71,7 +71,7 @@ class BuildkiteService < CiService
end
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 =
if response.code == 200 && response['status']
......
class CampfireService < Service
include HTTParty
prop_accessor :token, :subdomain, :room
validates :token, presence: true, if: :activated?
......@@ -31,7 +29,6 @@ class CampfireService < Service
def execute(data)
return unless supported_events.include?(data[:object_kind])
self.class.base_uri base_uri
message = build_message(data)
speak(self.room, message, auth)
end
......@@ -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
end
# Returns a list of rooms, or [].
# https://github.com/basecamp/campfire-api/blob/master/sections/rooms.md#get-rooms
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"] : []
end
......
......@@ -49,7 +49,7 @@ class DroneCiService < CiService
end
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 =
if response.code == 200 && response['status']
......
class ExternalWikiService < Service
include HTTParty
prop_accessor :external_wiki_url
validates :external_wiki_url, presence: true, url: true, if: :activated?
......@@ -24,7 +22,7 @@ class ExternalWikiService < Service
end
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
nil
end
......
......@@ -77,13 +77,13 @@ class IssueTrackerService < Service
result = false
begin
response = HTTParty.head(self.project_url, verify: true)
response = Gitlab::HTTP.head(self.project_url, verify: true)
if response
message = "#{self.type} received response #{response.code} when attempting to connect to #{self.project_url}"
result = true
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}"
end
Rails.logger.info(message)
......
......@@ -52,7 +52,7 @@ class MockCiService < CiService
#
#
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)
rescue Errno::ECONNREFUSED
:error
......
class PackagistService < Service
include HTTParty
prop_accessor :username, :token, :server
validates :username, presence: true, if: :activated?
......
class PivotaltrackerService < Service
include HTTParty
API_ENDPOINT = 'https://www.pivotaltracker.com/services/v5/source_commits'.freeze
prop_accessor :token, :restrict_to_branch
......@@ -52,7 +50,7 @@ class PivotaltrackerService < Service
'message' => commit[:message]
}
}
PivotaltrackerService.post(
Gitlab::HTTP.post(
API_ENDPOINT,
body: message.to_json,
headers: {
......
class PushoverService < Service
include HTTParty
base_uri 'https://api.pushover.net/1'
BASE_URI = 'https://api.pushover.net/1'.freeze
prop_accessor :api_key, :user_key, :device, :priority, :sound
validates :api_key, :user_key, :priority, presence: true, if: :activated?
......@@ -99,6 +98,6 @@ class PushoverService < Service
pushover_data[:sound] = sound
end
PushoverService.post('/messages.json', body: pushover_data)
Gitlab::HTTP.post('/messages.json', base_uri: BASE_URI, body: pushover_data)
end
end
......@@ -83,7 +83,7 @@ class TeamcityService < CiService
branch = Gitlab::Git.ref_name(data[:ref])
HTTParty.post(
Gitlab::HTTP.post(
build_url('httpAuth/app/rest/buildQueue'),
body: "<build branchName=\"#{branch}\">"\
"<buildType id=\"#{build_type}\"/>"\
......@@ -134,10 +134,10 @@ class TeamcityService < CiService
end
def get_path(path)
HTTParty.get(build_url(path), verify: false,
basic_auth: {
username: username,
password: password
})
Gitlab::HTTP.get(build_url(path), verify: false,
basic_auth: {
username: username,
password: password
})
end
end
......@@ -65,7 +65,7 @@ module Ci
project.pipelines
.where(ref: pipeline.ref)
.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
end
......
......@@ -28,7 +28,7 @@ module Projects
def add_repository_to_project
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
# We should skip the repository for a GitHub import or GitLab project import,
......
......@@ -18,7 +18,8 @@ module Projects
def pages_config
{
domains: pages_domains_config
domains: pages_domains_config,
https_only: project.pages_https_only?
}
end
......@@ -27,7 +28,8 @@ module Projects
{
domain: domain.domain,
certificate: domain.certificate,
key: domain.key
key: domain.key,
https_only: project.pages_https_only? && domain.https?
}
end
end
......
......@@ -24,6 +24,8 @@ module Projects
system_hook_service.execute_hooks_for(project, :update)
end
update_pages_config if changing_pages_https_only?
success
else
model_errors = project.errors.full_messages.to_sentence
......@@ -67,5 +69,13 @@ module Projects
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')
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
......@@ -14,16 +14,17 @@ class SubmitUsagePingService
def execute
return false unless Gitlab::CurrentSettings.usage_ping_enabled?
response = HTTParty.post(
response = Gitlab::HTTP.post(
URL,
body: Gitlab::UsageData.to_json(force_refresh: true),
allow_local_requests: true,
headers: { 'Content-type' => 'application/json' }
)
store_metrics(response)
true
rescue HTTParty::Error => e
rescue Gitlab::HTTP::Error => e
Rails.logger.info "Unable to contact GitLab, Inc.: #{e}"
false
......
......@@ -3,23 +3,20 @@ class WebHookService
attr_reader :body, :headers, :code
def initialize
@headers = HTTParty::Response::Headers.new({})
@headers = Gitlab::HTTP::Response::Headers.new({})
@body = ''
@code = 'internal error'
end
end
include HTTParty
# HTTParty timeout
default_timeout Gitlab.config.gitlab.webhook_timeout
attr_accessor :hook, :data, :hook_name
attr_accessor :hook, :data, :hook_name, :request_options
def initialize(hook, data, hook_name)
@hook = hook
@data = data
@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
def execute
......@@ -73,11 +70,12 @@ class WebHookService
end
def make_request(url, basic_auth = false)
self.class.post(url,
Gitlab::HTTP.post(url,
body: data.to_json,
headers: build_headers(hook_name),
verify: hook.enable_ssl_verification,
basic_auth: basic_auth)
basic_auth: basic_auth,
**request_options)
end
def make_request_with_auth
......
......@@ -16,8 +16,6 @@ class CertificateValidator < ActiveModel::EachValidator
private
def valid_certificate_pem?(value)
return false unless value
OpenSSL::X509::Certificate.new(value).present?
rescue OpenSSL::X509::CertificateError
false
......
......@@ -4,7 +4,7 @@
# protect against Server-side Request Forgery (SSRF).
class ImportableUrlValidator < ActiveModel::EachValidator
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")
end
end
......
......@@ -860,5 +860,14 @@
.col-sm-10
= 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
= f.submit 'Save', class: 'btn btn-save'
......@@ -13,13 +13,13 @@
.form-group
.input-group
- if current_user.can_select_namespace?
.input-group-addon
.input-group-addon.has-tooltip{ title: 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
- else
.input-group-addon.static-namespace
#{root_url}#{current_user.username}/
.input-group-addon.static-namespace.has-tooltip{ title: user_url(current_user.username) + '/' }
#{user_url(current_user.username)}/
= hidden_field_tag :namespace_id, value: current_user.namespace_id
.form-group.col-xs-12.col-sm-6.project-path
= label_tag :path, 'Project name', class: 'label-light'
......
......@@ -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;" }
%img{ alt: "GitLab", height: "33", src: image_url('mailers/gitlab_footer_logo.gif'), style: "display:block;margin:0 auto 1em;", width: "90" }/
%div
%a{ href: profile_notifications_url, style: "color:#3777b0;text-decoration:none;" } Manage all notifications
&middot;
%a{ href: help_url, style: "color:#3777b0;text-decoration:none;" } Help
%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
- manage_notifications_link = link_to(_("Manage all notifications"), profile_notifications_url, style: "color:#3777b0;text-decoration:none;")
- help_link = link_to(_("Help"), help_url, style: "color:#3777b0;text-decoration:none;")
= _("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 }
= yield :additional_footer
......@@ -6,6 +6,7 @@
.mobile-overlay
.alert-wrapper
= render "layouts/broadcast"
= render 'layouts/header/read_only_banner'
= yield :flash_message
- unless @hide_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 @@
profile_url: url_for(params.merge(lineprofiler: 'true')) },
class: Peek.env }
#peek-view-performance-bar
#peek-view-performance-bar.hidden
= render_server_response_time
%span#serverstats
%ul.performance-bar
......@@ -13,6 +13,6 @@
#{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
#{ _('Create merge request') }
......@@ -9,12 +9,12 @@
Project path
.input-group
- if current_user.can_select_namespace?
.input-group-addon
.input-group-addon.has-tooltip{ title: 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}
- else
.input-group-addon.static-namespace
.input-group-addon.static-namespace.has-tooltip{ title: user_url(current_user.username) + '/' }
#{user_url(current_user.username)}/
= f.hidden_field :namespace_id, value: current_user.namespace_id
.form-group.project-path.col-sm-6
......
......@@ -5,81 +5,82 @@
- number_commits_behind = diverging_commit_counts[:behind]
- number_commits_ahead = diverging_commit_counts[:ahead]
- merge_project = can?(current_user, :create_merge_request, @project) ? @project : (current_user && current_user.fork_of(@project))
%li{ class: "js-branch-#{branch.name}" }
%div
= link_to project_tree_path(@project, branch.name), class: 'item-title str-truncated ref-name' do
= sprite_icon('fork', size: 12)
= branch.name
&nbsp;
- if branch.name == @repository.root_ref
%span.label.label-primary default
- elsif merged
%span.label.label-info.has-tooltip{ title: s_('Branches|Merged into %{default_branch}') % { default_branch: @repository.root_ref } }
= s_('Branches|merged')
%li{ class: "branch-item js-branch-#{branch.name}" }
.branch-info
.branch-title
= link_to project_tree_path(@project, branch.name), class: 'item-title str-truncated-100 ref-name' do
= sprite_icon('fork', size: 12)
= branch.name
&nbsp;
- if branch.name == @repository.root_ref
%span.label.label-primary default
- elsif 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)
%span.label.label-success
= 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 protected_branch?(@project, branch)
%span.label.label-success
= s_('Branches|protected')
- if branch.name != @repository.root_ref
= link_to project_compare_index_path(@project, from: @repository.root_ref, to: branch.name),
class: "btn btn-default #{'prepend-left-10' unless merge_project}",
method: :post,
title: s_('Branches|Compare') do
= s_('Branches|Compare')
.block-truncated
- if commit
= render 'projects/branches/commit', commit: commit, project: @project
- else
= s_('Branches|Cant find HEAD commit for this branch')
= 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)
- if branch.name == @project.repository.root_ref
%button{ class: "btn btn-remove remove-row js-ajax-loading-spinner has-tooltip disabled",
disabled: true,
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")
.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
.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)
= link_to project_compare_index_path(@project, from: @repository.root_ref, to: branch.name),
class: "btn btn-default #{'prepend-left-10' unless merge_project}",
method: :post,
title: s_('Branches|Compare') do
= s_('Branches|Compare')
= render 'projects/buttons/download', project: @project, ref: branch.name, pipeline: @refs_pipelines[branch.name]
- if commit
= render 'projects/branches/commit', commit: commit, project: @project
- else
%p
= s_('Branches|Cant find HEAD commit for this branch')
- if can?(current_user, :push_code, @project)
- if branch.name == @project.repository.root_ref
%button{ class: "btn btn-remove remove-row js-ajax-loading-spinner has-tooltip disabled",
disabled: true,
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 @@
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.
- if Gitlab.config.pages.external_https
= render 'https_only'
%hr.clearfix
= 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