Commit d8317ed6 authored by GitLab Bot's avatar GitLab Bot

Merge remote-tracking branch 'upstream/master' into ce-to-ee-2018-01-11

# Conflicts:
#	app/assets/javascripts/boards/components/board_card.vue
#	app/assets/javascripts/clusters/components/application_row.vue
#	app/assets/javascripts/commit/pipelines/pipelines_table.vue
#	app/assets/javascripts/cycle_analytics/components/stage_code_component.vue
#	app/assets/javascripts/cycle_analytics/components/stage_component.vue
#	app/assets/javascripts/cycle_analytics/components/stage_plan_component.vue
#	app/assets/javascripts/cycle_analytics/components/stage_review_component.vue
#	app/assets/javascripts/cycle_analytics/components/stage_staging_component.vue
#	app/assets/javascripts/cycle_analytics/components/stage_test_component.vue
#	app/assets/javascripts/dispatcher.js
#	app/assets/javascripts/environments/components/environment_actions.vue
#	app/assets/javascripts/environments/components/environment_external_url.vue
#	app/assets/javascripts/environments/components/environment_item.vue
#	app/assets/javascripts/environments/components/environment_monitoring.vue
#	app/assets/javascripts/environments/components/environment_rollback.vue
#	app/assets/javascripts/environments/components/environment_stop.vue
#	app/assets/javascripts/environments/components/environment_terminal_button.vue
#	app/assets/javascripts/environments/components/environments_table.vue
#	app/assets/javascripts/environments/folder/environments_folder_view.vue
#	app/assets/javascripts/groups/components/app.vue
#	app/assets/javascripts/groups/components/item_actions.vue
#	app/assets/javascripts/groups/components/item_stats_value.vue
#	app/assets/javascripts/ide/components/ide_project_branches_tree.vue
#	app/assets/javascripts/ide/components/ide_side_bar.vue
#	app/assets/javascripts/ide/components/repo_editor.vue
#	app/assets/javascripts/ide/components/repo_preview.vue
#	app/assets/javascripts/ide/components/repo_tab.vue
#	app/assets/javascripts/issue_show/components/app.vue
#	app/assets/javascripts/monitoring/components/dashboard.vue
#	app/assets/javascripts/monitoring/components/graph.vue
#	app/assets/javascripts/monitoring/components/graph/flag.vue
#	app/assets/javascripts/notebook/cells/markdown.vue
#	app/assets/javascripts/notes/components/comment_form.vue
#	app/assets/javascripts/notes/components/note_attachment.vue
#	app/assets/javascripts/notes/components/note_form.vue
#	app/assets/javascripts/notes/components/noteable_note.vue
#	app/assets/javascripts/pipelines/components/async_button.vue
#	app/assets/javascripts/pipelines/components/graph/action_component.vue
#	app/assets/javascripts/pipelines/components/graph/graph_component.vue
#	app/assets/javascripts/pipelines/components/graph/stage_column_component.vue
#	app/assets/javascripts/pipelines/components/header_component.vue
#	app/assets/javascripts/pipelines/components/pipelines_table_row.vue
#	app/assets/javascripts/pipelines/components/stage.vue
#	app/assets/javascripts/projects/permissions/components/settings_panel.vue
#	app/assets/javascripts/registry/components/table_registry.vue
#	app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue
#	app/assets/javascripts/sidebar/components/lock/edit_form.vue
#	app/assets/javascripts/sidebar/components/lock/lock_issue_sidebar.vue
#	app/assets/javascripts/sidebar/components/subscriptions/sidebar_subscriptions.vue
#	app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue
#	app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue
#	app/assets/javascripts/vue_shared/components/ci_icon.vue
#	app/assets/javascripts/vue_shared/components/header_ci_component.vue
#	app/assets/javascripts/vue_shared/components/issue/issue_warning.vue
#	app/assets/javascripts/vue_shared/components/loading_button.vue
#	app/assets/javascripts/vue_shared/components/markdown/field.vue
#	app/assets/javascripts/vue_shared/components/markdown/toolbar.vue
#	app/assets/javascripts/vue_shared/components/modal.vue
#	app/assets/javascripts/vue_shared/components/recaptcha_modal.vue
#	app/assets/javascripts/vue_shared/components/table_pagination.vue
#	app/views/layouts/header/_default.html.haml
#	db/schema.rb
#	doc/development/fe_guide/style_guide_js.md
#	doc/topics/git/index.md
#	doc/user/project/clusters/index.md
#	features/support/env.rb
#	qa/qa/page/menu/admin.rb
#	qa/qa/scenario/test/sanity/selectors.rb
#	yarn.lock

[ci skip]
parents 633591fb 4fbe2a00
......@@ -8,7 +8,8 @@
"plugins": [
["istanbul", {
"exclude": [
"spec/javascripts/**/*"
"spec/javascripts/**/*",
"app/assets/javascripts/locale/**/app.js"
]
}],
["transform-define", {
......
......@@ -629,6 +629,7 @@ codequality:
paths: [codeclimate.json]
sast:
<<: *except-docs
image: registry.gitlab.com/gitlab-org/gl-sast:latest
before_script: []
script:
......
app/assets/images/multi-editor-on.png

5.34 KB | W: | H:

app/assets/images/multi-editor-on.png

3.88 KB | W: | H:

app/assets/images/multi-editor-on.png
app/assets/images/multi-editor-on.png
app/assets/images/multi-editor-on.png
app/assets/images/multi-editor-on.png
  • 2-up
  • Swipe
  • Onion skin
......@@ -35,9 +35,12 @@ export default {
type: String,
default: '',
},
<<<<<<< HEAD
groupId: {
type: Number,
},
=======
>>>>>>> upstream/master
},
data() {
return {
......
......@@ -67,8 +67,13 @@
// Avoid the potential for the real-time data to say APPLICATION_INSTALLABLE but
// we already made a request to install and are just waiting for the real-time
// to sync up.
<<<<<<< HEAD
return (this.status !== APPLICATION_INSTALLABLE &&
this.status !== APPLICATION_ERROR) ||
=======
return (this.status !== APPLICATION_INSTALLABLE
&& this.status !== APPLICATION_ERROR) ||
>>>>>>> upstream/master
this.requestStatus === REQUEST_LOADING ||
this.requestStatus === REQUEST_SUCCESS;
},
......
......@@ -7,6 +7,10 @@
mixins: [
pipelinesMixin,
],
<<<<<<< HEAD
=======
>>>>>>> upstream/master
props: {
endpoint: {
type: String,
......
......@@ -43,8 +43,12 @@
</h5>
<a
:href="mergeRequest.url"
<<<<<<< HEAD
class="issue-link"
>
=======
class="issue-link">
>>>>>>> upstream/master
!{{ mergeRequest.iid }}
</a>
&middot;
......@@ -52,8 +56,12 @@
{{ s__('OpenedNDaysAgo|Opened') }}
<a
:href="mergeRequest.url"
<<<<<<< HEAD
class="issue-date"
>
=======
class="issue-date">
>>>>>>> upstream/master
{{ mergeRequest.createdAt }}
</a>
</span>
......@@ -61,8 +69,12 @@
{{ s__('ByAuthor|by') }}
<a
:href="mergeRequest.author.webUrl"
<<<<<<< HEAD
class="issue-author-link"
>
=======
class="issue-author-link">
>>>>>>> upstream/master
{{ mergeRequest.author.name }}
</a>
</span>
......
......@@ -47,18 +47,26 @@
<a
:href="issue.url"
class="issue-link"
<<<<<<< HEAD
>
#{{ issue.iid }}
</a>
=======
>#{{ issue.iid }}</a>
>>>>>>> upstream/master
&middot;
<span>
{{ s__('OpenedNDaysAgo|Opened') }}
<a
:href="issue.url"
class="issue-date"
<<<<<<< HEAD
>
{{ issue.createdAt }}
</a>
=======
>{{ issue.createdAt }}</a>
>>>>>>> upstream/master
</span>
<span>
{{ s__('ByAuthor|by') }}
......
......@@ -57,9 +57,13 @@
<a
:href="commit.commitUrl"
class="commit-hash-link commit-sha"
<<<<<<< HEAD
>
{{ commit.shortSha }}
</a>
=======
>{{ commit.shortSha }}</a>
>>>>>>> upstream/master
{{ s__('FirstPushedBy|pushed by') }}
<a
:href="commit.author.webUrl"
......
......@@ -46,9 +46,13 @@
<a
:href="mergeRequest.url"
class="issue-link"
<<<<<<< HEAD
>
!{{ mergeRequest.iid }}
</a>
=======
>!{{ mergeRequest.iid }}</a>
>>>>>>> upstream/master
&middot;
<span>
{{ s__('OpenedNDaysAgo|Opened') }}
......
......@@ -63,7 +63,12 @@
</a>
<span
class="icon-branch"
<<<<<<< HEAD
v-html="iconBranch">
=======
v-html="iconBranch"
>
>>>>>>> upstream/master
</span>
<a
:href="build.commitUrl"
......
......@@ -80,16 +80,24 @@
</span>
<a
:href="build.commitUrl"
<<<<<<< HEAD
class="commit-sha"
>
=======
class="commit-sha">
>>>>>>> upstream/master
{{ build.shortSha }}
</a>
</h5>
<span>
<a
:href="build.url"
<<<<<<< HEAD
class="issue-date"
>
=======
class="issue-date">
>>>>>>> upstream/master
{{ build.date }}
</a>
</span>
......
......@@ -580,6 +580,7 @@ import initLDAPGroupsSelect from 'ee/ldap_groups_select'; // eslint-disable-line
import('./pages/search/show')
.then(callDefault)
.catch(fail);
<<<<<<< HEAD
new UserCallout();
break;
case 'projects:mirrors:show':
......@@ -591,6 +592,8 @@ import initLDAPGroupsSelect from 'ee/ldap_groups_select'; // eslint-disable-line
break;
case 'admin:audit_logs:index':
import(/* webpackChunkName: "ee_audit_logs" */ 'ee/pages/admin/audit_logs').then(m => m.default()).catch(fail);
=======
>>>>>>> upstream/master
break;
case 'projects:settings:repository:show':
new UsersSelect();
......@@ -700,11 +703,15 @@ import initLDAPGroupsSelect from 'ee/ldap_groups_select'; // eslint-disable-line
.then(callDefault)
.catch(fail);
break;
<<<<<<< HEAD
case 'edit':
import(/* webpackChunkName: "ee_admin_groups_edit" */ 'ee/pages/admin/groups/edit').then(m => m.default()).catch(fail);
break;
}
=======
}
>>>>>>> upstream/master
break;
case 'projects':
import('./pages/admin/projects')
......@@ -728,11 +735,14 @@ import initLDAPGroupsSelect from 'ee/ldap_groups_select'; // eslint-disable-line
import('./pages/admin/abuse_reports')
.then(callDefault)
.catch(fail);
<<<<<<< HEAD
break;
case 'geo_nodes':
import(/* webpackChunkName: 'geo_node_form' */ './geo/geo_node_form')
.then(geoNodeForm => geoNodeForm.default($('.js-geo-node-form')))
.catch(() => {});
=======
>>>>>>> upstream/master
break;
}
break;
......
......@@ -12,7 +12,10 @@
components: {
loadingIcon,
},
<<<<<<< HEAD
=======
>>>>>>> upstream/master
props: {
actions: {
type: Array,
......@@ -69,7 +72,12 @@
<span v-html="playIconSvg"></span>
<i
class="fa fa-caret-down"
<<<<<<< HEAD
aria-hidden="true">
=======
aria-hidden="true"
>
>>>>>>> upstream/master
</i>
<loading-icon v-if="isLoading" />
</span>
......@@ -78,8 +86,12 @@
<ul class="dropdown-menu dropdown-menu-align-right">
<li
v-for="(action, i) in actions"
<<<<<<< HEAD
:key="i"
>
=======
:key="i">
>>>>>>> upstream/master
<button
type="button"
class="js-manual-action-link no-btn btn"
......
......@@ -3,6 +3,7 @@
import { s__ } from '../../locale';
/**
<<<<<<< HEAD
* Renders the external url link in environments table.
*/
export default {
......@@ -16,6 +17,20 @@
required: true,
},
},
=======
* Renders the external url link in environments table.
*/
export default {
directives: {
tooltip,
},
props: {
externalUrl: {
type: String,
required: true,
},
},
>>>>>>> upstream/master
computed: {
title() {
......
<script>
/**
<<<<<<< HEAD
* Renders the Monitoring (Metrics) link in environments table.
*/
=======
* Renders the Monitoring (Metrics) link in environments table.
*/
>>>>>>> upstream/master
import tooltip from '../../vue_shared/directives/tooltip';
export default {
directives: {
tooltip,
<<<<<<< HEAD
},
props: {
monitoringUrl: {
......@@ -14,6 +20,16 @@
required: true,
},
},
=======
},
props: {
monitoringUrl: {
type: String,
required: true,
},
},
>>>>>>> upstream/master
computed: {
title() {
......
<script>
/**
<<<<<<< HEAD
* Renders Rollback or Re deploy button in environments table depending
* of the provided property `isLastDeployment`.
*
* Makes a post request when the button is clicked.
*/
=======
* Renders Rollback or Re deploy button in environments table depending
* of the provided property `isLastDeployment`.
*
* Makes a post request when the button is clicked.
*/
>>>>>>> upstream/master
import eventHub from '../event_hub';
import loadingIcon from '../../vue_shared/components/loading_icon.vue';
......@@ -12,11 +20,24 @@
components: {
loadingIcon,
},
<<<<<<< HEAD
=======
>>>>>>> upstream/master
props: {
retryUrl: {
type: String,
default: '',
},
<<<<<<< HEAD
isLastDeployment: {
type: Boolean,
default: true,
},
},
=======
isLastDeployment: {
type: Boolean,
......@@ -24,6 +45,7 @@
},
},
>>>>>>> upstream/master
data() {
return {
isLoading: false,
......
<script>
/**
<<<<<<< HEAD
* Renders the stop "button" that allows stop an environment.
* Used in environments table.
*/
=======
* Renders the stop "button" that allows stop an environment.
* Used in environments table.
*/
>>>>>>> upstream/master
import eventHub from '../event_hub';
import loadingIcon from '../../vue_shared/components/loading_icon.vue';
import tooltip from '../../vue_shared/directives/tooltip';
......@@ -10,10 +16,18 @@
export default {
components: {
loadingIcon,
<<<<<<< HEAD
},
directives: {
tooltip,
},
=======
},
directives: {
tooltip,
},
>>>>>>> upstream/master
props: {
stopUrl: {
......
<script>
/**
<<<<<<< HEAD
* Renders a terminal button to open a web terminal.
* Used in environments table.
*/
=======
* Renders a terminal button to open a web terminal.
* Used in environments table.
*/
>>>>>>> upstream/master
import terminalIconSvg from 'icons/_icon_terminal.svg';
import tooltip from '../../vue_shared/directives/tooltip';
export default {
directives: {
tooltip,
<<<<<<< HEAD
},
props: {
terminalPath: {
......@@ -22,6 +29,23 @@
terminalIconSvg,
};
},
=======
},
props: {
terminalPath: {
type: String,
required: false,
default: '',
},
},
data() {
return {
terminalIconSvg,
};
},
>>>>>>> upstream/master
computed: {
title() {
......
......@@ -32,7 +32,6 @@ export default {
default: false,
},
},
methods: {
folderUrl(model) {
return `${window.location.pathname}/folders/${model.folderName}`;
......@@ -88,8 +87,12 @@ export default {
</div>
<template
v-for="(model, i) in environments"
<<<<<<< HEAD
:model="model"
>
=======
:model="model">
>>>>>>> upstream/master
<div
is="environment-item"
:model="model"
......@@ -97,6 +100,7 @@ export default {
:can-read-environment="canReadEnvironment"
:key="i"
/>
<<<<<<< HEAD
<div
v-if="model.hasDeployBoard && model.isDeployBoardVisible"
......@@ -111,14 +115,20 @@ export default {
/>
</div>
</div>
=======
>>>>>>> upstream/master
<template
v-if="shouldRenderFolderContent(model)"
>
<div
v-if="model.isLoadingFolderContent"
<<<<<<< HEAD
:key="i"
>
=======
:key="i">
>>>>>>> upstream/master
<loading-icon size="2" />
</div>
......
......@@ -29,7 +29,10 @@
required: true,
},
},
<<<<<<< HEAD
=======
>>>>>>> upstream/master
methods: {
successCallback(resp) {
this.saveData(resp);
......
<script>
/* global Flash */
import { s__ } from '~/locale';
import loadingIcon from '~/vue_shared/components/loading_icon.vue';
import modal from '~/vue_shared/components/modal.vue';
import { getParameterByName } from '~/lib/utils/common_utils';
import { mergeUrlParams } from '~/lib/utils/url_utility';
import eventHub from '../event_hub';
import { getParameterByName } from '../../lib/utils/common_utils';
import loadingIcon from '../../vue_shared/components/loading_icon.vue';
import { COMMON_STR } from '../constants';
import { mergeUrlParams } from '../../lib/utils/url_utility';
import groupsComponent from './groups.vue';
export default {
components: {
loadingIcon,
modal,
groupsComponent,
},
props: {
......@@ -32,6 +36,10 @@ export default {
isLoading: true,
isSearchEmpty: false,
searchEmptyMessage: '',
showModal: false,
groupLeaveConfirmationMessage: '',
targetGroup: null,
targetParentGroup: null,
};
},
computed: {
......@@ -48,7 +56,11 @@ export default {
eventHub.$on('fetchPage', this.fetchPage);
eventHub.$on('toggleChildren', this.toggleChildren);
<<<<<<< HEAD
eventHub.$on('leaveGroup', this.leaveGroup);
=======
eventHub.$on('showLeaveGroupModal', this.showLeaveGroupModal);
>>>>>>> upstream/master
eventHub.$on('updatePagination', this.updatePagination);
eventHub.$on('updateGroups', this.updateGroups);
},
......@@ -58,7 +70,11 @@ export default {
beforeDestroy() {
eventHub.$off('fetchPage', this.fetchPage);
eventHub.$off('toggleChildren', this.toggleChildren);
<<<<<<< HEAD
eventHub.$off('leaveGroup', this.leaveGroup);
=======
eventHub.$off('showLeaveGroupModal', this.showLeaveGroupModal);
>>>>>>> upstream/master
eventHub.$off('updatePagination', this.updatePagination);
eventHub.$off('updateGroups', this.updateGroups);
},
......@@ -141,14 +157,23 @@ export default {
parentGroup.isOpen = false;
}
},
leaveGroup(group, parentGroup) {
const targetGroup = group;
targetGroup.isBeingRemoved = true;
this.service.leaveGroup(targetGroup.leavePath)
showLeaveGroupModal(group, parentGroup) {
this.targetGroup = group;
this.targetParentGroup = parentGroup;
this.showModal = true;
this.groupLeaveConfirmationMessage = s__(`GroupsTree|Are you sure you want to leave the "${group.fullName}" group?`);
},
hideLeaveGroupModal() {
this.showModal = false;
},
leaveGroup() {
this.showModal = false;
this.targetGroup.isBeingRemoved = true;
this.service.leaveGroup(this.targetGroup.leavePath)
.then(res => res.json())
.then((res) => {
$.scrollTo(0);
this.store.removeGroup(targetGroup, parentGroup);
this.store.removeGroup(this.targetGroup, this.targetParentGroup);
Flash(res.notice, 'notice');
})
.catch((err) => {
......@@ -157,7 +182,7 @@ export default {
message = COMMON_STR.LEAVE_FORBIDDEN;
}
Flash(message);
targetGroup.isBeingRemoved = false;
this.targetGroup.isBeingRemoved = false;
});
},
updatePagination(headers) {
......@@ -190,5 +215,14 @@ export default {
:search-empty-message="searchEmptyMessage"
:page-info="pageInfo"
/>
<modal
v-show="showModal"
:primary-button-label="__('Leave')"
kind="warning"
:title="__('Are you sure?')"
:text="groupLeaveConfirmationMessage"
@cancel="hideLeaveGroupModal"
@submit="leaveGroup"
/>
</div>
</template>
<script>
<<<<<<< HEAD
import { s__ } from '~/locale';
import tooltip from '~/vue_shared/directives/tooltip';
import icon from '~/vue_shared/components/icon.vue';
......@@ -10,10 +11,30 @@
components: {
icon,
modal,
=======
import tooltip from '~/vue_shared/directives/tooltip';
import icon from '~/vue_shared/components/icon.vue';
import eventHub from '../event_hub';
import { COMMON_STR } from '../constants';
export default {
components: {
icon,
},
directives: {
tooltip,
},
props: {
parentGroup: {
type: Object,
required: false,
default: () => ({}),
>>>>>>> upstream/master
},
directives: {
tooltip,
},
<<<<<<< HEAD
props: {
parentGroup: {
type: Object,
......@@ -24,12 +45,19 @@
type: Object,
required: true,
},
=======
},
computed: {
leaveBtnTitle() {
return COMMON_STR.LEAVE_BTN_TITLE;
>>>>>>> upstream/master
},
data() {
return {
modalStatus: false,
};
},
<<<<<<< HEAD
computed: {
leaveBtnTitle() {
return COMMON_STR.LEAVE_BTN_TITLE;
......@@ -51,6 +79,15 @@
},
},
};
=======
},
methods: {
onLeaveGroup() {
eventHub.$emit('showLeaveGroupModal', this.group, this.parentGroup);
},
},
};
>>>>>>> upstream/master
</script>
<template>
......@@ -78,14 +115,5 @@
class="leave-group btn no-expand">
<icon name="leave"/>
</a>
<modal
v-show="modalStatus"
:primary-button-label="__('Leave')"
kind="warning"
:title="__('Are you sure?')"
:text="__('Are you sure you want to leave this group?')"
:body="leaveConfirmationMessage"
@submit="leaveGroup"
/>
</div>
</template>
......@@ -30,11 +30,19 @@
default: 'bottom',
},
/**
<<<<<<< HEAD
* value could either be number or string
* as `memberCount` is always passed as string
* while `subgroupCount` & `projectCount`
* are always number
*/
=======
* value could either be number or string
* as `memberCount` is always passed as string
* while `subgroupCount` & `projectCount`
* are always number
*/
>>>>>>> upstream/master
value: {
type: [Number, String],
required: false,
......
......@@ -41,7 +41,11 @@ export default {
</div>
</div>
<div>
<<<<<<< HEAD
<repo-tree :tree-id="branch.treeId"/>
=======
<repo-tree :tree-id="branch.treeId" />
>>>>>>> upstream/master
</div>
</div>
</template>
......@@ -73,7 +73,12 @@
<div
class="multi-file-loading-container"
v-for="n in 3"
<<<<<<< HEAD
:key="n">
=======
:key="n"
>
>>>>>>> upstream/master
<skeleton-loading-container />
</div>
</template>
......
......@@ -17,7 +17,11 @@ export default {
'panelResizing',
]),
shouldHideEditor() {
<<<<<<< HEAD
return this.activeFile && this.activeFile.binary && !this.activeFile.raw;
=======
return this.activeFile.binary && !this.activeFile.raw;
>>>>>>> upstream/master
},
},
watch: {
......
......@@ -61,11 +61,18 @@
v-else
class="vertical-center render-error">
<p class="text-center">
<<<<<<< HEAD
The source could not be displayed because a rendering error occurred. You can
<a
:href="activeFile.rawPath"
download
>download</a> it instead.
=======
The source could not be displayed because a rendering error occurred.
You can <a
:href="activeFile.rawPath"
download>download</a> it instead.
>>>>>>> upstream/master
</p>
</div>
</div>
......
<script>
import { mapActions } from 'vuex';
<<<<<<< HEAD
import fileStatusIcon from './repo_file_status_icon.vue';
=======
>>>>>>> upstream/master
import fileIcon from '../../vue_shared/components/file_icon.vue';
export default {
components: {
<<<<<<< HEAD
fileStatusIcon,
=======
>>>>>>> upstream/master
fileIcon,
},
props: {
......
......@@ -267,6 +267,10 @@
:project-path="projectPath"
:project-namespace="projectNamespace"
:show-delete-button="showDeleteButton"
<<<<<<< HEAD
=======
:can-attach-file="canAttachFile"
>>>>>>> upstream/master
:enable-autocomplete="enableAutocomplete"
/>
......
......@@ -55,6 +55,7 @@
eventHub.$off('toggleAspectRatio', this.toggleAspectRatio);
eventHub.$off('hoverChanged', this.hoverChanged);
window.removeEventListener('resize', this.resizeThrottled, false);
<<<<<<< HEAD
},
mounted() {
......@@ -65,8 +66,19 @@
this.getGraphsData();
window.addEventListener('resize', this.resizeThrottled, false);
}
=======
>>>>>>> upstream/master
},
mounted() {
this.resizeThrottled = _.throttle(this.resize, 600);
if (!this.hasMetrics) {
this.state = 'gettingStarted';
} else {
this.getGraphsData();
window.addEventListener('resize', this.resizeThrottled, false);
}
},
methods: {
getGraphsData() {
this.state = 'loading';
......
......@@ -259,7 +259,12 @@
<svg
class="graph-data"
:viewBox="innerViewBox"
<<<<<<< HEAD
ref="graphData">
=======
ref="graphData"
>
>>>>>>> upstream/master
<graph-path
v-for="(path, index) in timeSeries"
:key="index"
......
<script>
import { dateFormat, timeFormat } from '../../utils/date_time_formatters';
import { formatRelevantDigits } from '../../../lib/utils/number_utils';
import Icon from '../../../vue_shared/components/icon.vue';
import icon from '../../../vue_shared/components/icon.vue';
export default {
components: {
<<<<<<< HEAD
Icon,
=======
icon,
>>>>>>> upstream/master
},
props: {
currentXCoordinate: {
......
......@@ -93,8 +93,12 @@
<prompt />
<div
class="markdown"
<<<<<<< HEAD
v-html="markdown"
>
=======
v-html="markdown">
>>>>>>> upstream/master
</div>
</div>
</template>
......
......@@ -307,8 +307,12 @@ js-gfm-input js-autosize markdown-area js-vue-textarea"
<div class="note-form-actions">
<div
class="pull-left btn-group
<<<<<<< HEAD
append-right-10 comment-type-dropdown js-comment-type-dropdown droplab-dropdown"
>
=======
append-right-10 comment-type-dropdown js-comment-type-dropdown droplab-dropdown">
>>>>>>> upstream/master
<button
@click.prevent="handleSave()"
:disabled="isSubmitButtonDisabled"
......
......@@ -30,8 +30,12 @@
rel="noopener noreferrer">
<i
class="fa fa-paperclip"
<<<<<<< HEAD
aria-hidden="true"
>
=======
aria-hidden="true">
>>>>>>> upstream/master
</i>
{{ attachment.filename }}
</a>
......
......@@ -116,8 +116,12 @@
<template>
<div
ref="editNoteForm"
<<<<<<< HEAD
class="note-edit-form current-note-edit-form"
>
=======
class="note-edit-form current-note-edit-form">
>>>>>>> upstream/master
<div
v-if="conflictWhileEditing"
class="js-conflict-edit-warning alert alert-danger">
......
......@@ -51,6 +51,10 @@
return `note_${this.note.id}`;
},
},
<<<<<<< HEAD
=======
>>>>>>> upstream/master
created() {
eventHub.$on('enterEditMode', ({ noteId }) => {
if (noteId === this.note.id) {
......@@ -59,6 +63,10 @@
}
});
},
<<<<<<< HEAD
=======
>>>>>>> upstream/master
methods: {
...mapActions([
'deleteNote',
......
<script>
<<<<<<< HEAD
/* eslint-disable no-alert, vue/require-default-prop */
=======
/* eslint-disable no-alert */
>>>>>>> upstream/master
import eventHub from '../event_hub';
import loadingIcon from '../../vue_shared/components/loading_icon.vue';
......@@ -9,6 +13,7 @@
directives: {
tooltip,
},
<<<<<<< HEAD
components: {
loadingIcon,
......@@ -54,6 +59,52 @@
if (this.confirmActionMessage && confirm(this.confirmActionMessage)) {
this.makeRequest();
} else if (!this.confirmActionMessage) {
=======
components: {
loadingIcon,
},
props: {
endpoint: {
type: String,
required: true,
},
title: {
type: String,
required: true,
},
icon: {
type: String,
required: true,
},
cssClass: {
type: String,
required: true,
},
confirmActionMessage: {
type: String,
required: false,
default: '',
},
},
data() {
return {
isLoading: false,
};
},
computed: {
iconClass() {
return `fa fa-${this.icon}`;
},
buttonClass() {
return `btn ${this.cssClass}`;
},
},
methods: {
onClick() {
if (this.confirmActionMessage !== '' && confirm(this.confirmActionMessage)) {
this.makeRequest();
} else if (this.confirmActionMessage === '') {
>>>>>>> upstream/master
this.makeRequest();
}
},
......
......@@ -14,6 +14,10 @@
directives: {
tooltip,
},
<<<<<<< HEAD
=======
>>>>>>> upstream/master
props: {
tooltipText: {
type: String,
......
......@@ -63,7 +63,8 @@
* target the click event of this component.
*/
stopDropdownClickPropagation() {
$(this.$el.querySelectorAll('.js-grouped-pipeline-dropdown a.mini-pipeline-graph-dropdown-item'))
$(this.$el
.querySelectorAll('.js-grouped-pipeline-dropdown a.mini-pipeline-graph-dropdown-item'))
.on('click', (e) => {
e.stopPropagation();
});
......
<script>
import loadingIcon from '~/vue_shared/components/loading_icon.vue';
<<<<<<< HEAD
import linkedPipelinesColumn from './linked_pipelines_column.vue';
=======
>>>>>>> upstream/master
import stageColumnComponent from './stage_column_component.vue';
export default {
components: {
<<<<<<< HEAD
linkedPipelinesColumn,
stageColumnComponent,
loadingIcon,
},
=======
stageColumnComponent,
loadingIcon,
},
>>>>>>> upstream/master
props: {
isLoading: {
type: Boolean,
......@@ -100,7 +110,10 @@
:key="stage.name"
:stage-connector-class="stageConnectorClass(index, stage)"
:is-first-column="isFirstColumn(index)"
<<<<<<< HEAD
:has-triggered-by="hasTriggeredBy"
=======
>>>>>>> upstream/master
/>
</ul>
......
......@@ -13,11 +13,25 @@
required: true,
},
<<<<<<< HEAD
jobs: {
type: Array,
required: true,
},
=======
props: {
title: {
type: String,
required: true,
},
jobs: {
type: Array,
required: true,
},
>>>>>>> upstream/master
isFirstColumn: {
type: Boolean,
required: false,
......@@ -29,20 +43,33 @@
required: false,
default: '',
},
<<<<<<< HEAD
hasTriggeredBy: {
type: Boolean,
required: true,
},
=======
>>>>>>> upstream/master
},
methods: {
firstJob(list) {
return list[0];
},
<<<<<<< HEAD
jobId(job) {
return `ci-badge-${job.name}`;
=======
jobId(job) {
return `ci-badge-${job.name}`;
},
buildConnnectorClass(index) {
return index === 0 && !this.isFirstColumn ? 'left-connector' : '';
>>>>>>> upstream/master
},
},
};
</script>
......@@ -59,10 +86,16 @@
v-for="(job, index) in jobs"
:key="job.id"
class="build"
<<<<<<< HEAD
:class="{
'left-connector': index === 0 && (!isFirstColumn || hasTriggeredBy)
}"
:id="jobId(job)">
=======
:class="buildConnnectorClass(index)"
:id="jobId(job)"
>
>>>>>>> upstream/master
<div class="curve"></div>
......
......@@ -19,11 +19,15 @@
required: true,
},
},
<<<<<<< HEAD
=======
>>>>>>> upstream/master
data() {
return {
actions: this.getActions(),
};
<<<<<<< HEAD
},
computed: {
......@@ -63,6 +67,47 @@
});
}
=======
},
computed: {
status() {
return this.pipeline.details && this.pipeline.details.status;
},
shouldRenderContent() {
return !this.isLoading && Object.keys(this.pipeline).length;
},
},
watch: {
pipeline() {
this.actions = this.getActions();
},
},
methods: {
postAction(action) {
const index = this.actions.indexOf(action);
this.$set(this.actions[index], 'isLoading', true);
eventHub.$emit('headerPostAction', action);
},
getActions() {
const actions = [];
if (this.pipeline.retry_path) {
actions.push({
label: 'Retry',
path: this.pipeline.retry_path,
cssClass: 'js-retry-button btn btn-inverted-secondary',
type: 'button',
isLoading: false,
});
}
>>>>>>> upstream/master
if (this.pipeline.cancel_path) {
actions.push({
label: 'Cancel running',
......
......@@ -250,7 +250,8 @@
<div
class="blank-state-row"
v-if="shouldRenderNoPipelinesMessage">
v-if="shouldRenderNoPipelinesMessage"
>
<div class="blank-state-center">
<h2 class="blank-state-title js-blank-state-title">No pipelines to show.</h2>
</div>
......
......@@ -223,8 +223,12 @@
<div class="table-section section-10 commit-link">
<div
class="table-mobile-header"
<<<<<<< HEAD
role="rowheader"
>
=======
role="rowheader">
>>>>>>> upstream/master
Status
</div>
<div class="table-mobile-content">
......
......@@ -50,7 +50,13 @@
computed: {
dropdownClass() {
<<<<<<< HEAD
return this.dropdownContent.length > 0 ? 'js-builds-dropdown-container' : 'js-builds-dropdown-loading';
=======
return this.dropdownContent.length > 0 ?
'js-builds-dropdown-container' :
'js-builds-dropdown-loading';
>>>>>>> upstream/master
},
triggerButtonClass() {
......@@ -162,7 +168,8 @@
<ul
class="dropdown-menu mini-pipeline-graph-dropdown-menu js-builds-dropdown-container"
aria-labelledby="stageDropdown">
aria-labelledby="stageDropdown"
>
<li
:class="dropdownClass"
......
<script>
<<<<<<< HEAD
/* eslint-disable vue/require-default-prop */
=======
>>>>>>> upstream/master
import projectFeatureSetting from './project_feature_setting.vue';
import projectFeatureToggle from '../../../vue_shared/components/toggle_button.vue';
import projectSettingRow from './project_setting_row.vue';
......@@ -12,6 +15,7 @@
projectFeatureToggle,
projectSettingRow,
},
<<<<<<< HEAD
props: {
currentSettings: {
type: Object,
......@@ -51,6 +55,51 @@
},
},
=======
props: {
currentSettings: {
type: Object,
required: true,
},
canChangeVisibilityLevel: {
type: Boolean,
required: false,
default: false,
},
allowedVisibilityOptions: {
type: Array,
required: false,
default: () => [0, 10, 20],
},
lfsAvailable: {
type: Boolean,
required: false,
default: false,
},
registryAvailable: {
type: Boolean,
required: false,
default: false,
},
visibilityHelpPath: {
type: String,
required: false,
default: '',
},
lfsHelpPath: {
type: String,
required: false,
default: '',
},
registryHelpPath: {
type: String,
required: false,
default: '',
},
},
>>>>>>> upstream/master
data() {
const defaults = {
visibilityOptions,
......@@ -118,6 +167,32 @@
}
},
<<<<<<< HEAD
=======
watch: {
visibilityLevel(value, oldValue) {
if (value === visibilityOptions.PRIVATE) {
// when private, features are restricted to "only team members"
this.issuesAccessLevel = Math.min(10, this.issuesAccessLevel);
this.repositoryAccessLevel = Math.min(10, this.repositoryAccessLevel);
this.mergeRequestsAccessLevel = Math.min(10, this.mergeRequestsAccessLevel);
this.buildsAccessLevel = Math.min(10, this.buildsAccessLevel);
this.wikiAccessLevel = Math.min(10, this.wikiAccessLevel);
this.snippetsAccessLevel = Math.min(10, this.snippetsAccessLevel);
this.highlightChanges();
} else if (oldValue === visibilityOptions.PRIVATE) {
// if changing away from private, make enabled features more permissive
if (this.issuesAccessLevel > 0) this.issuesAccessLevel = 20;
if (this.repositoryAccessLevel > 0) this.repositoryAccessLevel = 20;
if (this.mergeRequestsAccessLevel > 0) this.mergeRequestsAccessLevel = 20;
if (this.buildsAccessLevel > 0) this.buildsAccessLevel = 20;
if (this.wikiAccessLevel > 0) this.wikiAccessLevel = 20;
if (this.snippetsAccessLevel > 0) this.snippetsAccessLevel = 20;
this.highlightChanges();
}
},
>>>>>>> upstream/master
repositoryAccessLevel(value, oldValue) {
if (value < oldValue) {
// sub-features cannot have more premissive access level
......@@ -140,6 +215,7 @@
if (value === 0) toggleHiddenClassBySelector('.issues-feature', true);
else if (oldValue === 0) toggleHiddenClassBySelector('.issues-feature', false);
},
<<<<<<< HEAD
mergeRequestsAccessLevel(value, oldValue) {
if (value === 0) toggleHiddenClassBySelector('.merge-requests-feature', true);
......@@ -164,7 +240,33 @@
},
},
};
=======
mergeRequestsAccessLevel(value, oldValue) {
if (value === 0) toggleHiddenClassBySelector('.merge-requests-feature', true);
else if (oldValue === 0) toggleHiddenClassBySelector('.merge-requests-feature', false);
},
buildsAccessLevel(value, oldValue) {
if (value === 0) toggleHiddenClassBySelector('.builds-feature', true);
else if (oldValue === 0) toggleHiddenClassBySelector('.builds-feature', false);
},
},
>>>>>>> upstream/master
methods: {
highlightChanges() {
this.highlightChangesClass = true;
this.$nextTick(() => {
this.highlightChangesClass = false;
});
},
visibilityAllowed(option) {
return this.allowedVisibilityOptions.includes(option);
},
},
};
</script>
<template>
......
......@@ -96,7 +96,12 @@
<span
v-tooltip
:title="item.revision"
<<<<<<< HEAD
data-placement="bottom">
=======
data-placement="bottom"
>
>>>>>>> upstream/master
{{ item.shortRevision }}
</span>
</td>
......@@ -121,10 +126,19 @@
:aria-label="s__('ContainerRegistry|Remove tag')"
data-container="body"
v-tooltip
<<<<<<< HEAD
@click="handleDeleteRegistry(item)">
<i
class="fa fa-trash"
aria-hidden="true">
=======
@click="handleDeleteRegistry(item)"
>
<i
class="fa fa-trash"
aria-hidden="true"
>
>>>>>>> upstream/master
</i>
</button>
</td>
......
......@@ -39,7 +39,14 @@
updateConfidentialAttribute(confidential) {
this.service.update('issue', { confidential })
.then(() => location.reload())
<<<<<<< HEAD
.catch(() => new Flash('Something went wrong trying to change the confidentiality of this issue'));
=======
.catch(() => {
Flash(`Something went wrong trying to
change the confidentiality of this issue`);
});
>>>>>>> upstream/master
},
},
};
......
......@@ -33,8 +33,12 @@
<div class="dropdown-menu sidebar-item-warning-message">
<p
class="text"
<<<<<<< HEAD
v-if="isLocked"
>
=======
v-if="isLocked">
>>>>>>> upstream/master
Unlock this {{ issuableDisplayName }}?
<strong>Everyone</strong>
will be able to comment.
......@@ -42,8 +46,12 @@
<p
class="text"
<<<<<<< HEAD
v-else
>
=======
v-else>
>>>>>>> upstream/master
Lock this {{ issuableDisplayName }}?
Only
<strong>project members</strong>
......
<script>
<<<<<<< HEAD
import Flash from '~/flash';
=======
import Flash from '../../../flash';
>>>>>>> upstream/master
import editForm from './edit_form.vue';
import issuableMixin from '../../../vue_shared/mixins/issuable';
import Icon from '../../../vue_shared/components/icon.vue';
......@@ -12,6 +16,10 @@
mixins: [
issuableMixin,
],
<<<<<<< HEAD
=======
>>>>>>> upstream/master
props: {
isLocked: {
required: true,
......@@ -36,6 +44,23 @@
return this.isLocked ? 'lock' : 'lock-open';
},
<<<<<<< HEAD
isLockDialogOpen() {
return this.mediator.store.isLockDialogOpen;
},
},
methods: {
toggleForm() {
this.mediator.store.isLockDialogOpen = !this.mediator.store.isLockDialogOpen;
},
=======
computed: {
lockIcon() {
return this.isLocked ? 'lock' : 'lock-open';
},
isLockDialogOpen() {
return this.mediator.store.isLockDialogOpen;
},
......@@ -46,12 +71,18 @@
this.mediator.store.isLockDialogOpen = !this.mediator.store.isLockDialogOpen;
},
>>>>>>> upstream/master
updateLockedAttribute(locked) {
this.mediator.service.update(this.issuableType, {
discussion_locked: locked,
})
.then(() => location.reload())
<<<<<<< HEAD
.catch(() => Flash(this.__(`Something went wrong trying to change the locked state of this ${this.issuableDisplayName}`)));
=======
.catch(() => Flash(this.__(`Something went wrong trying to
change the locked state of this ${this.issuableDisplayName}`)));
>>>>>>> upstream/master
},
},
};
......
......@@ -101,7 +101,8 @@
<div
v-for="participant in visibleParticipants"
:key="participant.id"
class="participants-author js-participants-author">
class="participants-author js-participants-author"
>
<a
class="author_link"
:href="participant.web_url"
......
......@@ -20,15 +20,20 @@ export default {
store: new Store(),
};
},
<<<<<<< HEAD
created() {
eventHub.$on('toggleSubscription', this.onToggleSubscription);
},
=======
created() {
eventHub.$on('toggleSubscription', this.onToggleSubscription);
},
>>>>>>> upstream/master
beforeDestroy() {
eventHub.$off('toggleSubscription', this.onToggleSubscription);
},
methods: {
onToggleSubscription() {
this.mediator.toggleSubscription()
......
......@@ -11,7 +11,10 @@
pipelineStage,
ciIcon,
icon,
<<<<<<< HEAD
linkedPipelinesMiniList,
=======
>>>>>>> upstream/master
},
props: {
pipeline: {
......@@ -98,6 +101,7 @@
{{ pipeline.commit.short_id }}</a>.
<span class="mr-widget-pipeline-graph">
<<<<<<< HEAD
<span class="stage-cell">
<linked-pipelines-mini-list
v-if="triggeredBy.length"
......@@ -120,13 +124,25 @@
v-if="triggered.length"
:triggered="triggered"
/>
=======
<span
class="stage-cell"
v-if="hasStages"
>
<div
v-for="(stage, i) in pipeline.details.stages"
:key="i"
class="stage-container dropdown js-mini-pipeline-graph"
>
<pipeline-stage :stage="stage" />
</div>
>>>>>>> upstream/master
</span>
</span>
<template v-if="pipeline.coverage">
Coverage {{ pipeline.coverage }}%
</template>
</div>
</template>
</div>
......
......@@ -107,8 +107,12 @@
<template v-if="!mr.rebaseInProgress && mr.canPushToSourceBranch && !isMakingRequest">
<div
class="accept-merge-holder clearfix
<<<<<<< HEAD
js-toggle-container accept-action media space-children"
>
=======
js-toggle-container accept-action media space-children">
>>>>>>> upstream/master
<button
type="button"
class="btn btn-sm btn-reopen btn-success"
......
......@@ -27,7 +27,10 @@
components: {
icon,
},
<<<<<<< HEAD
=======
>>>>>>> upstream/master
props: {
status: {
type: Object,
......
......@@ -6,12 +6,21 @@
import userAvatarImage from './user_avatar/user_avatar_image.vue';
/**
<<<<<<< HEAD
* Renders header component for job and pipeline page based on UI mockups
*
* Used in:
* - job show page
* - pipeline show page
*/
=======
* Renders header component for job and pipeline page based on UI mockups
*
* Used in:
* - job show page
* - pipeline show page
*/
>>>>>>> upstream/master
export default {
components: {
ciIconBadge,
......@@ -127,7 +136,12 @@
v-if="action.type === 'link'"
:href="action.path"
:class="action.cssClass"
<<<<<<< HEAD
:key="i">
=======
:key="i"
>
>>>>>>> upstream/master
{{ action.label }}
</a>
......
......@@ -5,7 +5,10 @@
components: {
icon,
},
<<<<<<< HEAD
=======
>>>>>>> upstream/master
props: {
isLocked: {
type: Boolean,
......
<script>
/* eslint-disable vue/require-default-prop */
<<<<<<< HEAD
/* This is a re-usable vue component for rendering a button
that will probably be sending off ajax requests and need
to show the loading status by setting the `loading` option.
......@@ -49,6 +50,58 @@
this.$emit('click', e);
},
},
=======
/* This is a re-usable vue component for rendering a button
that will probably be sending off ajax requests and need
to show the loading status by setting the `loading` option.
This can also be used for initial page load when you don't
know the action of the button yet by setting
`loading: true, label: undefined`.
Sample configuration:
<loading-button
:loading="true"
:label="Hello"
@click="..."
/>
*/
import loadingIcon from './loading_icon.vue';
export default {
components: {
loadingIcon,
},
props: {
loading: {
type: Boolean,
required: false,
default: false,
},
disabled: {
type: Boolean,
required: false,
default: false,
},
label: {
type: String,
required: false,
},
containerClass: {
type: String,
required: false,
default: 'btn btn-align-content',
},
},
methods: {
onClick(e) {
this.$emit('click', e);
},
},
>>>>>>> upstream/master
};
</script>
......
......@@ -30,6 +30,14 @@
type: String,
required: false,
default: '',
<<<<<<< HEAD
=======
},
canAttachFile: {
type: Boolean,
required: false,
default: true,
>>>>>>> upstream/master
},
enableAutocomplete: {
type: Boolean,
......@@ -121,7 +129,8 @@
/>
<div
class="md-write-holder"
v-show="!previewMarkdown">
v-show="!previewMarkdown"
>
<div class="zen-backdrop">
<slot name="textarea"></slot>
<a
......@@ -137,6 +146,10 @@
<markdown-toolbar
:markdown-docs-path="markdownDocsPath"
:quick-actions-docs-path="quickActionsDocsPath"
<<<<<<< HEAD
=======
:can-attach-file="canAttachFile"
>>>>>>> upstream/master
/>
</div>
</div>
......
......@@ -71,13 +71,15 @@
class="js-preview-link"
href="#md-preview-holder"
tabindex="-1"
@click.prevent="previewMarkdownTab($event)">
@click.prevent="previewMarkdownTab($event)"
>
Preview
</a>
</li>
<li
class="md-header-toolbar"
:class="{ active: !previewMarkdown }">
:class="{ active: !previewMarkdown }"
>
<toolbar-button
tag="**"
button-title="Add bold text"
......@@ -125,7 +127,8 @@
data-container="body"
tabindex="-1"
title="Go full screen"
type="button">
type="button"
>
<icon
name="screen-full"
/>
......
......@@ -16,7 +16,15 @@
return this.quickActionsDocsPath !== '';
},
},
<<<<<<< HEAD
=======
computed: {
hasQuickActionsDocsPath() {
return this.quickActionsDocsPath !== '';
},
},
>>>>>>> upstream/master
};
</script>
......@@ -27,7 +35,8 @@
<a
:href="markdownDocsPath"
target="_blank"
tabindex="-1">
tabindex="-1"
>
Markdown is supported
</a>
</template>
......
<script>
<<<<<<< HEAD
/* eslint-disable vue/require-default-prop */
export default {
......@@ -67,6 +68,75 @@
},
},
=======
/* eslint-disable vue/require-default-prop */
export default {
name: 'Modal',
props: {
id: {
type: String,
required: false,
},
title: {
type: String,
required: false,
},
text: {
type: String,
required: false,
},
hideFooter: {
type: Boolean,
required: false,
default: false,
},
kind: {
type: String,
required: false,
default: 'primary',
},
modalDialogClass: {
type: String,
required: false,
default: '',
},
closeKind: {
type: String,
required: false,
default: 'default',
},
closeButtonLabel: {
type: String,
required: false,
default: 'Cancel',
},
primaryButtonLabel: {
type: String,
required: false,
default: '',
},
submitDisabled: {
type: Boolean,
required: false,
default: false,
},
},
computed: {
btnKindClass() {
return {
[`btn-${this.kind}`]: true,
};
},
btnCancelKindClass() {
return {
[`btn-${this.closeKind}`]: true,
};
},
},
>>>>>>> upstream/master
methods: {
emitCancel(event) {
this.$emit('cancel', event);
......
<script>
import modal from './modal.vue';
import modal from './modal.vue';
<<<<<<< HEAD
export default {
name: 'RecaptchaModal',
components: {
modal,
},
=======
export default {
name: 'RecaptchaModal',
>>>>>>> upstream/master
props: {
html: {
type: String,
required: false,
default: '',
components: {
modal,
},
},
data() {
return {
script: {},
scriptSrc: 'https://www.google.com/recaptcha/api.js',
};
},
props: {
html: {
type: String,
required: false,
default: '',
},
},
<<<<<<< HEAD
watch: {
html() {
this.appendRecaptchaScript();
......@@ -32,36 +35,62 @@ export default {
mounted() {
window.recaptchaDialogCallback = this.submit.bind(this);
},
=======
data() {
return {
script: {},
scriptSrc: 'https://www.google.com/recaptcha/api.js',
};
},
>>>>>>> upstream/master
methods: {
appendRecaptchaScript() {
this.removeRecaptchaScript();
watch: {
html() {
this.appendRecaptchaScript();
},
},
const script = document.createElement('script');
script.src = this.scriptSrc;
script.classList.add('js-recaptcha-script');
script.async = true;
script.defer = true;
mounted() {
window.recaptchaDialogCallback = this.submit.bind(this);
},
this.script = script;
methods: {
appendRecaptchaScript() {
this.removeRecaptchaScript();
document.body.appendChild(script);
},
const script = document.createElement('script');
script.src = this.scriptSrc;
script.classList.add('js-recaptcha-script');
script.async = true;
script.defer = true;
removeRecaptchaScript() {
if (this.script instanceof Element) this.script.remove();
},
this.script = script;
close() {
this.removeRecaptchaScript();
this.$emit('close');
},
document.body.appendChild(script);
},
<<<<<<< HEAD
submit() {
this.$el.querySelector('form').submit();
},
},
};
=======
removeRecaptchaScript() {
if (this.script instanceof Element) this.script.remove();
},
close() {
this.removeRecaptchaScript();
this.$emit('close');
},
submit() {
this.$el.querySelector('form').submit();
},
},
};
>>>>>>> upstream/master
</script>
<template>
......
......@@ -13,12 +13,15 @@
props: {
/**
This function will take the information given by the pagination component
<<<<<<< HEAD
Here is an example `change` method:
change(pagenum) {
gl.utils.visitUrl(`?page=${pagenum}`);
},
=======
>>>>>>> upstream/master
*/
change: {
type: Function,
......
......@@ -18,14 +18,9 @@
margin: $gl-padding 0;
&.limited-width-container .file-content {
max-width: $limited-layout-width-sm;
max-width: $limited-layout-width;
margin-left: auto;
margin-right: auto;
@media (min-width: $screen-md-min) {
padding-top: 64px;
padding-bottom: 64px;
}
}
}
......@@ -128,7 +123,7 @@
}
&.wiki {
padding: 30px $gl-padding;
padding: $gl-padding;
}
&.blob-no-preview {
......
......@@ -16,12 +16,6 @@
display: inline-block;
}
@media (min-width: $screen-md-min) {
.blob-viewer[data-type="rich"] {
margin: 20px;
}
}
.ide-view {
display: flex;
height: calc(100vh - #{$header-height});
......
class Projects::Clusters::GcpController < Projects::ApplicationController
before_action :authorize_read_cluster!
before_action :authorize_google_api, except: [:login]
before_action :authorize_google_project_billing, only: [:new]
before_action :authorize_google_project_billing, only: [:new, :create]
before_action :authorize_create_cluster!, only: [:new, :create]
before_action :verify_billing, only: [:create]
def login
begin
......@@ -23,24 +24,34 @@ class Projects::Clusters::GcpController < Projects::ApplicationController
end
def create
@cluster = ::Clusters::CreateService
.new(project, current_user, create_params)
.execute(token_in_session)
if @cluster.persisted?
redirect_to project_cluster_path(project, @cluster)
else
render :new
end
end
private
def verify_billing
case google_project_billing_status
when 'true'
@cluster = ::Clusters::CreateService
.new(project, current_user, create_params)
.execute(token_in_session)
return redirect_to project_cluster_path(project, @cluster) if @cluster.persisted?
return
when 'false'
flash[:error] = _('Please enable billing for one of your projects to be able to create a cluster.')
flash[:alert] = _('Please <a href=%{link_to_billing} target="_blank" rel="noopener noreferrer">enable billing for one of your projects to be able to create a cluster</a>, then try again.').html_safe % { link_to_billing: "https://console.cloud.google.com/freetrial?utm_campaign=2018_cpanel&utm_source=gitlab&utm_medium=referral" }
else
flash[:error] = _('We could not verify that one of your projects on GCP has billing enabled. Please try again.')
flash[:alert] = _('We could not verify that one of your projects on GCP has billing enabled. Please try again.')
end
@cluster = ::Clusters::Cluster.new(create_params)
render :new
end
private
def create_params
params.require(:cluster).permit(
:enabled,
......
......@@ -46,7 +46,7 @@ module BlobHelper
end
def ide_edit_text
"#{_('Multi Edit')} <span class='label label-primary'>#{_('Beta')}</span>".html_safe
"#{_('Web IDE')}"
end
def ide_blob_link(project = @project, ref = @ref, path = @path, options = {})
......
......@@ -49,6 +49,7 @@ class MergeRequestDiff < ActiveRecord::Base
ensure_commit_shas
save_commits
save_diffs
save
keep_around_commits
end
......@@ -56,7 +57,6 @@ class MergeRequestDiff < ActiveRecord::Base
self.start_commit_sha ||= merge_request.target_branch_sha
self.head_commit_sha ||= merge_request.source_branch_sha
self.base_commit_sha ||= find_base_sha
save
end
# Override head_commit_sha to keep compatibility with merge request diff
......@@ -195,7 +195,7 @@ class MergeRequestDiff < ActiveRecord::Base
end
def commits_count
merge_request_diff_commits.size
super || merge_request_diff_commits.size
end
private
......@@ -264,13 +264,16 @@ class MergeRequestDiff < ActiveRecord::Base
new_attributes[:state] = :overflow if diff_collection.overflow?
end
update(new_attributes)
assign_attributes(new_attributes)
end
def save_commits
MergeRequestDiffCommit.create_bulk(self.id, compare.commits.reverse)
merge_request_diff_commits.reload
# merge_request_diff_commits.reload is preferred way to reload associated
# objects but it returns cached result for some reason in this case
commits = merge_request_diff_commits(true)
self.commits_count = commits.size
end
def repository
......
......@@ -1160,7 +1160,7 @@ class Project < ActiveRecord::Base
def change_head(branch)
if repository.branch_exists?(branch)
repository.before_change_head
repository.write_ref('HEAD', "refs/heads/#{branch}")
repository.raw_repository.write_ref('HEAD', "refs/heads/#{branch}", shell: false)
repository.copy_gitattributes(branch)
repository.after_change_head
reload_default_branch
......
......@@ -267,7 +267,7 @@ class Repository
# This will still fail if the file is corrupted (e.g. 0 bytes)
begin
write_ref(keep_around_ref_name(sha), sha)
raw_repository.write_ref(keep_around_ref_name(sha), sha, shell: false)
rescue Rugged::ReferenceError => ex
Rails.logger.error "Unable to create #{REF_KEEP_AROUND} reference for repository #{path}: #{ex}"
rescue Rugged::OSError => ex
......@@ -281,10 +281,6 @@ class Repository
ref_exists?(keep_around_ref_name(sha))
end
def write_ref(ref_path, sha)
rugged.references.create(ref_path, sha, force: true)
end
def diverging_commit_counts(branch)
root_ref_hash = raw_repository.commit(root_ref).id
cache.fetch(:"diverging_commit_counts_#{branch.name}") do
......
......@@ -2,7 +2,10 @@ class CheckGcpProjectBillingService
def execute(token)
client = GoogleApi::CloudPlatform::Client.new(token, nil)
client.projects_list.select do |project|
client.projects_get_billing_info(project.name).billingEnabled
begin
client.projects_get_billing_info(project.project_id).billing_enabled
rescue
end
end
end
end
......@@ -24,7 +24,7 @@ module Issues
@new_issue = create_new_issue
rewrite_notes
rewrite_award_emoji
rewrite_issue_award_emoji
add_note_moved_from
# Old issue tasks
......@@ -76,7 +76,7 @@ module Issues
end
def rewrite_notes
@old_issue.notes.find_each do |note|
@old_issue.notes_with_associations.find_each do |note|
new_note = note.dup
new_params = { project: @new_project, noteable: @new_issue,
note: rewrite_content(new_note.note),
......@@ -84,13 +84,19 @@ module Issues
updated_at: note.updated_at }
new_note.update(new_params)
rewrite_award_emoji(note, new_note)
end
end
def rewrite_award_emoji
@old_issue.award_emoji.each do |award|
def rewrite_issue_award_emoji
rewrite_award_emoji(@old_issue, @new_issue)
end
def rewrite_award_emoji(old_awardable, new_awardable)
old_awardable.award_emoji.each do |award|
new_award = award.dup
new_award.awardable = @new_issue
new_award.awardable = new_awardable
new_award.save
end
end
......
......@@ -156,13 +156,9 @@ module MergeRequests
end
def assign_title_from_issue
return unless issue
return unless issue && issue.is_a?(Issue)
merge_request.title =
case issue
when Issue then "Resolve \"#{issue.title}\""
when ExternalIssue then "Resolve #{issue.title}"
end
merge_request.title = "Resolve \"#{issue.title}\""
end
def issue_iid
......
module MergeRequests
class RebaseService < MergeRequests::WorkingCopyBaseService
REBASE_ERROR = 'Rebase failed. Please rebase locally'.freeze
def execute(merge_request)
@merge_request = merge_request
if rebase
success
else
error('Failed to rebase. Should be done manually')
error(REBASE_ERROR)
end
end
......@@ -22,8 +24,8 @@ module MergeRequests
true
rescue => e
log_error('Failed to rebase branch:')
log_error(e.message, save_message_on_model: true)
log_error(REBASE_ERROR, save_message_on_model: true)
log_error(e.message)
false
end
end
......
<<<<<<< HEAD
%header.navbar.navbar-gitlab.navbar-gitlab-new.qa-navbar
=======
%header.navbar.navbar-gitlab.qa-navbar
>>>>>>> upstream/master
%a.sr-only.gl-accessibility{ href: "#content-body", tabindex: "1" } Skip to content
.container-fluid
.header-content
......@@ -56,8 +60,6 @@
= link_to "Profile", current_user, class: 'profile-link', data: { user: current_user.username }
%li
= link_to "Settings", profile_path
%li
= link_to "Turn on multi edit", profile_preferences_path
- if current_user
%li
= link_to "Help", help_path
......
......@@ -5,8 +5,8 @@
= form_for @user, url: profile_preferences_path, remote: true, method: :put, html: { class: 'row prepend-top-default js-preferences-form' } do |f|
.col-lg-4
%h4.prepend-top-0
GitLab multi file editor
%p Unlock an additional editing experience which makes it possible to edit and commit multiple files
Web IDE (Beta)
%p Enable the new web IDE on this device to make it possible to open and edit multiple files with a single commit
.col-lg-8.multi-file-editor-options
= label_tag do
.preview.append-bottom-10= image_tag "multi-editor-off.png"
......
......@@ -30,12 +30,13 @@
%li
= link_to project_new_blob_path(@project, @project.default_branch || 'master') do
#{ _('New file') }
%li
= link_to new_project_branch_path(@project) do
#{ _('New branch') }
%li
= link_to new_project_tag_path(@project) do
#{ _('New tag') }
- unless @project.empty_repo?
%li
= link_to new_project_branch_path(@project) do
#{ _('New branch') }
%li
= link_to new_project_tag_path(@project) do
#{ _('New tag') }
- elsif current_user && current_user.already_forked?(@project)
%li
= link_to project_new_blob_path(@project, @project.default_branch || 'master') do
......
......@@ -4,11 +4,11 @@
= s_('ClusterIntegration|Please make sure that your Google account meets the following requirements:')
%ul
%li
- link_to_kubernetes_engine = link_to(s_('ClusterIntegration|access to Google Kubernetes Engine'), 'https://console.cloud.google.com', target: '_blank', rel: 'noopener noreferrer')
- link_to_kubernetes_engine = link_to(s_('ClusterIntegration|access to Google Kubernetes Engine'), 'https://console.cloud.google.com/freetrial?utm_campaign=2018_cpanel&utm_source=gitlab&utm_medium=referral', target: '_blank', rel: 'noopener noreferrer')
= s_('ClusterIntegration|Your account must have %{link_to_kubernetes_engine}').html_safe % { link_to_kubernetes_engine: link_to_kubernetes_engine }
%li
- link_to_requirements = link_to(s_('ClusterIntegration|meets the requirements'), 'https://cloud.google.com/kubernetes-engine/docs/quickstart', target: '_blank', rel: 'noopener noreferrer')
- link_to_requirements = link_to(s_('ClusterIntegration|meets the requirements'), 'https://cloud.google.com/kubernetes-engine/docs/quickstart?utm_campaign=2018_cpanel&utm_source=gitlab&utm_medium=referral', target: '_blank', rel: 'noopener noreferrer')
= s_('ClusterIntegration|Make sure your account %{link_to_requirements} to create clusters').html_safe % { link_to_requirements: link_to_requirements }
%li
- link_to_container_project = link_to(s_('ClusterIntegration|Google Kubernetes Engine project'), 'https://console.cloud.google.com/home/dashboard', target: '_blank', rel: 'noopener noreferrer')
- link_to_container_project = link_to(s_('ClusterIntegration|Google Kubernetes Engine project'), 'https://console.cloud.google.com/home/dashboard?utm_campaign=2018_cpanel&utm_source=gitlab&utm_medium=referral', target: '_blank', rel: 'noopener noreferrer')
= s_('ClusterIntegration|This account must have permissions to create a cluster in the %{link_to_container_project} specified below').html_safe % { link_to_container_project: link_to_container_project }
- @content_class = "limit-container-width" unless fluid_layout
- add_to_breadcrumbs "Clusters", project_clusters_path(@project)
- breadcrumb_title @cluster.id
- breadcrumb_title @cluster.name
- page_title _("Cluster")
- expanded = Rails.env.test?
......
......@@ -35,3 +35,6 @@
- if diff_file.mode_changed?
%small
#{diff_file.a_mode}#{diff_file.b_mode}
- if diff_file.stored_externally? && diff_file.external_storage == :lfs
%span.label.label-lfs.append-right-5 LFS
......@@ -4,7 +4,7 @@ class CheckGcpProjectBillingWorker
include ApplicationWorker
include ClusterQueue
LEASE_TIMEOUT = 15.seconds.to_i
LEASE_TIMEOUT = 3.seconds.to_i
SESSION_KEY_TIMEOUT = 5.minutes
BILLING_TIMEOUT = 1.hour
......@@ -23,13 +23,13 @@ class CheckGcpProjectBillingWorker
end
def self.redis_shared_state_key_for(token)
"gitlab:gcp:#{token.hash}:billing_enabled"
"gitlab:gcp:#{Digest::SHA1.hexdigest(token)}:billing_enabled"
end
def perform(token_key)
return unless token_key
token = self.get_session_token(token_key)
token = self.class.get_session_token(token_key)
return unless token
return unless try_obtain_lease_for(token)
......
---
title: Default merge request title is set correctly again when external issue tracker is activated
merge_request: 16356
author: Ben305
type: fixed
---
title: Store number of commits in merge_request_diffs table.
merge_request:
author:
type: performance
---
title: Add `pipelines` endpoint to merge requests API
merge_request: 15454
author: Tony Rom <thetonyrom@gmail.com>
type: added
---
title: Hide new branch and tag links for projects with an empty repo
merge_request:
author:
type: fixed
---
title: Display user friendly error message if rebase fails.
merge_request:
author:
type: fixed
---
title: Make project README containers wider on fixed layout
merge_request: 16181
author: Takuya Noguchi
type: fixed
---
title: Make modal dialog common for Groups tree app
merge_request: 16311
author:
type: fixed
---
title: Make rich blob viewer wider for PC
merge_request: 16262
author: Takuya Noguchi
type: fixed
---
title: Fix web ide user preferences copy and buttons
merge_request: 41789
author:
type: other
---
title: Add rake task to check integrity of uploaded files
merge_request:
author:
type: added
---
title: Fix bug where award emojis would be lost when moving issues between projects
merge_request:
author:
type: fixed
class AddCommitsCountToMergeRequestDiff < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
MIGRATION = 'AddMergeRequestDiffCommitsCount'.freeze
BATCH_SIZE = 5000
DELAY_INTERVAL = 5.minutes.to_i
class MergeRequestDiff < ActiveRecord::Base
self.table_name = 'merge_request_diffs'
include ::EachBatch
end
disable_ddl_transaction!
def up
add_column :merge_request_diffs, :commits_count, :integer
say 'Populating the MergeRequestDiff `commits_count`'
queue_background_migration_jobs_by_range_at_intervals(MergeRequestDiff, MIGRATION, DELAY_INTERVAL, batch_size: BATCH_SIZE)
end
def down
remove_column :merge_request_diffs, :commits_count
end
end
......@@ -11,7 +11,11 @@
#
# It's strongly recommended that you check this file into your version control system.
<<<<<<< HEAD
ActiveRecord::Schema.define(version: 20180105233807) do
=======
ActiveRecord::Schema.define(version: 20180105212544) do
>>>>>>> upstream/master
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
......@@ -1401,6 +1405,7 @@ ActiveRecord::Schema.define(version: 20180105233807) do
t.string "real_size"
t.string "head_commit_sha"
t.string "start_commit_sha"
t.integer "commits_count"
end
add_index "merge_request_diffs", ["merge_request_id", "id"], name: "index_merge_request_diffs_on_merge_request_id_and_id", using: :btree
......
......@@ -66,3 +66,15 @@ On the sign in page there should now be a Crowd tab in the sign in form.
[reconfigure]: ../restart_gitlab.md#omnibus-gitlab-reconfigure
[restart]: ../restart_gitlab.md#installations-from-source
## Troubleshooting
If you see an error message like the one below when you sign in after Crowd authentication is configured, you may want to consult the Crowd administrator for the Crowd log file to know the exact cause:
```
could not authorize you from Crowd because invalid credentials
```
Please make sure the Crowd users who need to login to GitLab are authorized to [the application](#configure-a-new-crowd-application) in the step of **Authorisation**. This could be verified by try "Authentication test" for Crowd as of 2.11.
![Example Crowd application authorisation configuration](img/crowd_application_authorisation.png)
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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