Commit 9c7d87ef authored by Achilleas Pipinellis's avatar Achilleas Pipinellis

Merge branch 'master' into 'docs/hide-gcp-install'

# Conflicts:
#   doc/install/README.md
parents 1e0df21a 180ec711
...@@ -145,12 +145,12 @@ gem 'acts-as-taggable-on', '~> 4.0' ...@@ -145,12 +145,12 @@ gem 'acts-as-taggable-on', '~> 4.0'
# Background jobs # Background jobs
gem 'sidekiq', '~> 5.0' gem 'sidekiq', '~> 5.0'
gem 'sidekiq-cron', '~> 0.4.4' gem 'sidekiq-cron', '~> 0.6.0'
gem 'redis-namespace', '~> 1.5.2' gem 'redis-namespace', '~> 1.5.2'
gem 'sidekiq-limit_fetch', '~> 3.4' gem 'sidekiq-limit_fetch', '~> 3.4'
# Cron Parser # Cron Parser
gem 'rufus-scheduler', '~> 3.1.10' gem 'rufus-scheduler', '~> 3.4'
# HTTP requests # HTTP requests
gem 'httparty', '~> 0.13.3' gem 'httparty', '~> 0.13.3'
......
...@@ -181,6 +181,8 @@ GEM ...@@ -181,6 +181,8 @@ GEM
equalizer (0.0.11) equalizer (0.0.11)
erubis (2.7.0) erubis (2.7.0)
escape_utils (1.1.1) escape_utils (1.1.1)
et-orbi (1.0.3)
tzinfo
eventmachine (1.0.8) eventmachine (1.0.8)
excon (0.55.0) excon (0.55.0)
execjs (2.6.0) execjs (2.6.0)
...@@ -697,7 +699,8 @@ GEM ...@@ -697,7 +699,8 @@ GEM
rubyntlm (0.5.2) rubyntlm (0.5.2)
rubypants (0.2.0) rubypants (0.2.0)
rubyzip (1.2.1) rubyzip (1.2.1)
rufus-scheduler (3.1.10) rufus-scheduler (3.4.0)
et-orbi (~> 1.0)
rugged (0.25.1.1) rugged (0.25.1.1)
safe_yaml (1.0.4) safe_yaml (1.0.4)
sanitize (2.1.0) sanitize (2.1.0)
...@@ -734,9 +737,8 @@ GEM ...@@ -734,9 +737,8 @@ GEM
connection_pool (~> 2.2, >= 2.2.0) connection_pool (~> 2.2, >= 2.2.0)
rack-protection (>= 1.5.0) rack-protection (>= 1.5.0)
redis (~> 3.3, >= 3.3.3) redis (~> 3.3, >= 3.3.3)
sidekiq-cron (0.4.4) sidekiq-cron (0.6.0)
redis-namespace (>= 1.5.2) rufus-scheduler (>= 3.3.0)
rufus-scheduler (>= 2.0.24)
sidekiq (>= 4.2.1) sidekiq (>= 4.2.1)
sidekiq-limit_fetch (3.4.0) sidekiq-limit_fetch (3.4.0)
sidekiq (>= 4) sidekiq (>= 4)
...@@ -1013,7 +1015,7 @@ DEPENDENCIES ...@@ -1013,7 +1015,7 @@ DEPENDENCIES
ruby-fogbugz (~> 0.2.1) ruby-fogbugz (~> 0.2.1)
ruby-prof (~> 0.16.2) ruby-prof (~> 0.16.2)
ruby_parser (~> 3.8.4) ruby_parser (~> 3.8.4)
rufus-scheduler (~> 3.1.10) rufus-scheduler (~> 3.4)
rugged (~> 0.25.1.1) rugged (~> 0.25.1.1)
sanitize (~> 2.0) sanitize (~> 2.0)
sass-rails (~> 5.0.6) sass-rails (~> 5.0.6)
...@@ -1025,7 +1027,7 @@ DEPENDENCIES ...@@ -1025,7 +1027,7 @@ DEPENDENCIES
sham_rack (~> 1.3.6) sham_rack (~> 1.3.6)
shoulda-matchers (~> 2.8.0) shoulda-matchers (~> 2.8.0)
sidekiq (~> 5.0) sidekiq (~> 5.0)
sidekiq-cron (~> 0.4.4) sidekiq-cron (~> 0.6.0)
sidekiq-limit_fetch (~> 3.4) sidekiq-limit_fetch (~> 3.4)
simplecov (~> 0.14.0) simplecov (~> 0.14.0)
slack-notifier (~> 1.5.1) slack-notifier (~> 1.5.1)
......
...@@ -8,7 +8,10 @@ export default class BlobViewer { ...@@ -8,7 +8,10 @@ export default class BlobViewer {
this.richViewer = document.querySelector('.blob-viewer[data-type="rich"]'); this.richViewer = document.querySelector('.blob-viewer[data-type="rich"]');
this.$fileHolder = $('.file-holder'); this.$fileHolder = $('.file-holder');
let initialViewerName = document.querySelector('.blob-viewer:not(.hidden)').getAttribute('data-type'); const initialViewer = document.querySelector('.blob-viewer:not(.hidden)');
if (!initialViewer) return;
let initialViewerName = initialViewer.getAttribute('data-type');
this.initBindings(); this.initBindings();
......
...@@ -35,7 +35,10 @@ gl.issueBoards.Board = Vue.extend({ ...@@ -35,7 +35,10 @@ gl.issueBoards.Board = Vue.extend({
filter: { filter: {
handler() { handler() {
this.list.page = 1; this.list.page = 1;
this.list.getIssues(true); this.list.getIssues(true)
.catch(() => {
// TODO: handle request error
});
}, },
deep: true, deep: true,
}, },
......
...@@ -70,7 +70,10 @@ export default { ...@@ -70,7 +70,10 @@ export default {
list.id = listObj.id; list.id = listObj.id;
list.label.id = listObj.label.id; list.label.id = listObj.label.id;
list.getIssues(); list.getIssues()
.catch(() => {
// TODO: handle request error
});
}); });
}) })
.catch(() => { .catch(() => {
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
import boardNewIssue from './board_new_issue'; import boardNewIssue from './board_new_issue';
import boardCard from './board_card'; import boardCard from './board_card';
import eventHub from '../eventhub'; import eventHub from '../eventhub';
import loadingIcon from '../../vue_shared/components/loading_icon.vue';
const Store = gl.issueBoards.BoardsStore; const Store = gl.issueBoards.BoardsStore;
...@@ -44,6 +45,7 @@ export default { ...@@ -44,6 +45,7 @@ export default {
components: { components: {
boardCard, boardCard,
boardNewIssue, boardNewIssue,
loadingIcon,
}, },
methods: { methods: {
listHeight() { listHeight() {
...@@ -90,7 +92,10 @@ export default { ...@@ -90,7 +92,10 @@ export default {
if (this.scrollHeight() <= this.listHeight() && if (this.scrollHeight() <= this.listHeight() &&
this.list.issuesSize > this.list.issues.length) { this.list.issuesSize > this.list.issues.length) {
this.list.page += 1; this.list.page += 1;
this.list.getIssues(false); this.list.getIssues(false)
.catch(() => {
// TODO: handle request error
});
} }
if (this.scrollHeight() > Math.ceil(this.listHeight())) { if (this.scrollHeight() > Math.ceil(this.listHeight())) {
...@@ -153,10 +158,7 @@ export default { ...@@ -153,10 +158,7 @@ export default {
class="board-list-loading text-center" class="board-list-loading text-center"
aria-label="Loading issues" aria-label="Loading issues"
v-if="loading"> v-if="loading">
<i <loading-icon />
class="fa fa-spinner fa-spin"
aria-hidden="true">
</i>
</div> </div>
<board-new-issue <board-new-issue
:list="list" :list="list"
...@@ -181,12 +183,12 @@ export default { ...@@ -181,12 +183,12 @@ export default {
class="board-list-count text-center" class="board-list-count text-center"
v-if="showCount" v-if="showCount"
data-id="-1"> data-id="-1">
<i
class="fa fa-spinner fa-spin" <loading-icon
aria-label="Loading more issues" v-show="list.loadingMore"
aria-hidden="true" label="Loading more issues"
v-show="list.loadingMore"> />
</i>
<span v-if="list.issues.length === list.issuesSize"> <span v-if="list.issues.length === list.issuesSize">
Showing all issues Showing all issues
</span> </span>
......
...@@ -36,6 +36,9 @@ gl.issueBoards.BoardSidebar = Vue.extend({ ...@@ -36,6 +36,9 @@ gl.issueBoards.BoardSidebar = Vue.extend({
}, },
assigneeId() { assigneeId() {
return this.issue.assignee ? this.issue.assignee.id : 0; return this.issue.assignee ? this.issue.assignee.id : 0;
},
milestoneTitle() {
return this.issue.milestone ? this.issue.milestone.title : 'No Milestone';
} }
}, },
watch: { watch: {
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
import Vue from 'vue'; import Vue from 'vue';
import queryData from '../../utils/query_data'; import queryData from '../../utils/query_data';
import loadingIcon from '../../../vue_shared/components/loading_icon.vue';
require('./header'); require('./header');
require('./list'); require('./list');
...@@ -108,6 +109,8 @@ gl.issueBoards.IssuesModal = Vue.extend({ ...@@ -108,6 +109,8 @@ gl.issueBoards.IssuesModal = Vue.extend({
if (!this.issuesCount) { if (!this.issuesCount) {
this.issuesCount = data.size; this.issuesCount = data.size;
} }
}).catch(() => {
// TODO: handle request error
}); });
}, },
}, },
...@@ -135,6 +138,7 @@ gl.issueBoards.IssuesModal = Vue.extend({ ...@@ -135,6 +138,7 @@ gl.issueBoards.IssuesModal = Vue.extend({
'modal-list': gl.issueBoards.ModalList, 'modal-list': gl.issueBoards.ModalList,
'modal-footer': gl.issueBoards.ModalFooter, 'modal-footer': gl.issueBoards.ModalFooter,
'empty-state': gl.issueBoards.ModalEmptyState, 'empty-state': gl.issueBoards.ModalEmptyState,
loadingIcon,
}, },
template: ` template: `
<div <div
...@@ -159,7 +163,7 @@ gl.issueBoards.IssuesModal = Vue.extend({ ...@@ -159,7 +163,7 @@ gl.issueBoards.IssuesModal = Vue.extend({
class="add-issues-list text-center" class="add-issues-list text-center"
v-if="loading || filterLoading"> v-if="loading || filterLoading">
<div class="add-issues-list-loading"> <div class="add-issues-list-loading">
<i class="fa fa-spinner fa-spin"></i> <loading-icon />
</div> </div>
</section> </section>
<modal-footer></modal-footer> <modal-footer></modal-footer>
......
...@@ -25,7 +25,9 @@ class List { ...@@ -25,7 +25,9 @@ class List {
} }
if (this.type !== 'blank' && this.id) { if (this.type !== 'blank' && this.id) {
this.getIssues(); this.getIssues().catch(() => {
// TODO: handle request error
});
} }
} }
...@@ -52,11 +54,17 @@ class List { ...@@ -52,11 +54,17 @@ class List {
gl.issueBoards.BoardsStore.state.lists.splice(index, 1); gl.issueBoards.BoardsStore.state.lists.splice(index, 1);
gl.issueBoards.BoardsStore.updateNewListDropdown(this.id); gl.issueBoards.BoardsStore.updateNewListDropdown(this.id);
gl.boardService.destroyList(this.id); gl.boardService.destroyList(this.id)
.catch(() => {
// TODO: handle request error
});
} }
update () { update () {
gl.boardService.updateList(this.id, this.position); gl.boardService.updateList(this.id, this.position)
.catch(() => {
// TODO: handle request error
});
} }
nextPage () { nextPage () {
...@@ -146,11 +154,17 @@ class List { ...@@ -146,11 +154,17 @@ class List {
this.issues.splice(oldIndex, 1); this.issues.splice(oldIndex, 1);
this.issues.splice(newIndex, 0, issue); this.issues.splice(newIndex, 0, issue);
gl.boardService.moveIssue(issue.id, null, null, moveBeforeIid, moveAfterIid); gl.boardService.moveIssue(issue.id, null, null, moveBeforeIid, moveAfterIid)
.catch(() => {
// TODO: handle request error
});
} }
updateIssueLabel(issue, listFrom, moveBeforeIid, moveAfterIid) { updateIssueLabel(issue, listFrom, moveBeforeIid, moveAfterIid) {
gl.boardService.moveIssue(issue.id, listFrom.id, this.id, moveBeforeIid, moveAfterIid); gl.boardService.moveIssue(issue.id, listFrom.id, this.id, moveBeforeIid, moveAfterIid)
.catch(() => {
// TODO: handle request error
});
} }
findIssue (id) { findIssue (id) {
......
...@@ -6,6 +6,7 @@ import PipelineStore from '../../pipelines/stores/pipelines_store'; ...@@ -6,6 +6,7 @@ import PipelineStore from '../../pipelines/stores/pipelines_store';
import eventHub from '../../pipelines/event_hub'; import eventHub from '../../pipelines/event_hub';
import EmptyState from '../../pipelines/components/empty_state.vue'; import EmptyState from '../../pipelines/components/empty_state.vue';
import ErrorState from '../../pipelines/components/error_state.vue'; import ErrorState from '../../pipelines/components/error_state.vue';
import loadingIcon from '../../vue_shared/components/loading_icon.vue';
import '../../lib/utils/common_utils'; import '../../lib/utils/common_utils';
import '../../vue_shared/vue_resource_interceptor'; import '../../vue_shared/vue_resource_interceptor';
import Poll from '../../lib/utils/poll'; import Poll from '../../lib/utils/poll';
...@@ -17,8 +18,6 @@ import Poll from '../../lib/utils/poll'; ...@@ -17,8 +18,6 @@ import Poll from '../../lib/utils/poll';
* We need a store to store the received environemnts. * We need a store to store the received environemnts.
* We need a service to communicate with the server. * We need a service to communicate with the server.
* *
* Necessary SVG in the table are provided as props. This should be refactored
* as soon as we have Webpack and can load them directly into JS files.
*/ */
export default Vue.component('pipelines-table', { export default Vue.component('pipelines-table', {
...@@ -27,6 +26,7 @@ export default Vue.component('pipelines-table', { ...@@ -27,6 +26,7 @@ export default Vue.component('pipelines-table', {
'pipelines-table-component': PipelinesTableComponent, 'pipelines-table-component': PipelinesTableComponent,
'error-state': ErrorState, 'error-state': ErrorState,
'empty-state': EmptyState, 'empty-state': EmptyState,
loadingIcon,
}, },
/** /**
...@@ -151,13 +151,12 @@ export default Vue.component('pipelines-table', { ...@@ -151,13 +151,12 @@ export default Vue.component('pipelines-table', {
template: ` template: `
<div class="content-list pipelines"> <div class="content-list pipelines">
<div
class="realtime-loading" <loading-icon
v-if="isLoading"> label="Loading pipelines"
<i size="3"
class="fa fa-spinner fa-spin" v-if="isLoading"
aria-hidden="true" /> />
</div>
<empty-state <empty-state
v-if="shouldRenderEmptyState" v-if="shouldRenderEmptyState"
......
<script> <script>
import eventHub from '../eventhub'; import eventHub from '../eventhub';
import loadingIcon from '../../vue_shared/components/loading_icon.vue';
export default { export default {
data() { data() {
...@@ -22,6 +23,11 @@ ...@@ -22,6 +23,11 @@
default: 'btn-default', default: 'btn-default',
}, },
}, },
components: {
loadingIcon,
},
methods: { methods: {
doAction() { doAction() {
this.isLoading = true; this.isLoading = true;
...@@ -44,11 +50,6 @@ ...@@ -44,11 +50,6 @@
:disabled="isLoading" :disabled="isLoading"
@click="doAction"> @click="doAction">
{{ text }} {{ text }}
<i <loading-icon v-if="isLoading" />
v-if="isLoading"
class="fa fa-spinner fa-spin"
aria-hidden="true"
aria-label="Loading">
</i>
</button> </button>
</template> </template>
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
import DeployKeysService from '../service'; import DeployKeysService from '../service';
import DeployKeysStore from '../store'; import DeployKeysStore from '../store';
import keysPanel from './keys_panel.vue'; import keysPanel from './keys_panel.vue';
import loadingIcon from '../../vue_shared/components/loading_icon.vue';
export default { export default {
data() { data() {
...@@ -28,6 +29,7 @@ ...@@ -28,6 +29,7 @@
}, },
components: { components: {
keysPanel, keysPanel,
loadingIcon,
}, },
methods: { methods: {
fetchKeys() { fetchKeys() {
...@@ -74,15 +76,11 @@ ...@@ -74,15 +76,11 @@
<template> <template>
<div class="col-lg-9 col-lg-offset-3 append-bottom-default deploy-keys"> <div class="col-lg-9 col-lg-offset-3 append-bottom-default deploy-keys">
<div <loading-icon
class="text-center" v-if="isLoading && !hasKeys"
v-if="isLoading && !hasKeys"> size="2"
<i label="Loading deploy keys"
class="fa fa-spinner fa-spin fa-2x" />
aria-hidden="true"
aria-label="Loading deploy keys">
</i>
</div>
<div v-else-if="hasKeys"> <div v-else-if="hasKeys">
<keys-panel <keys-panel
title="Enabled deploy keys for this project" title="Enabled deploy keys for this project"
......
...@@ -246,6 +246,7 @@ const ShortcutsBlob = require('./shortcuts_blob'); ...@@ -246,6 +246,7 @@ const ShortcutsBlob = require('./shortcuts_blob');
new NotificationsForm(); new NotificationsForm();
if ($('#tree-slider').length) { if ($('#tree-slider').length) {
new TreeView(); new TreeView();
new BlobViewer();
} }
break; break;
case 'projects:pipelines:builds': case 'projects:pipelines:builds':
...@@ -300,6 +301,7 @@ const ShortcutsBlob = require('./shortcuts_blob'); ...@@ -300,6 +301,7 @@ const ShortcutsBlob = require('./shortcuts_blob');
case 'projects:tree:show': case 'projects:tree:show':
shortcut_handler = new ShortcutsNavigation(); shortcut_handler = new ShortcutsNavigation();
new TreeView(); new TreeView();
new BlobViewer();
gl.TargetBranchDropDown.bootstrap(); gl.TargetBranchDropDown.bootstrap();
break; break;
case 'projects:find_file:show': case 'projects:find_file:show':
......
<script> <script>
/* global Flash */ /* global Flash */
import EnvironmentsService from '../services/environments_service'; import EnvironmentsService from '../services/environments_service';
import EnvironmentTable from './environments_table.vue'; import environmentTable from './environments_table.vue';
import EnvironmentsStore from '../stores/environments_store'; import EnvironmentsStore from '../stores/environments_store';
import TablePaginationComponent from '../../vue_shared/components/table_pagination'; import loadingIcon from '../../vue_shared/components/loading_icon.vue';
import tablePagination from '../../vue_shared/components/table_pagination.vue';
import '../../lib/utils/common_utils'; import '../../lib/utils/common_utils';
import eventHub from '../event_hub'; import eventHub from '../event_hub';
export default { export default {
components: { components: {
'environment-table': EnvironmentTable, environmentTable,
'table-pagination': TablePaginationComponent, tablePagination,
loadingIcon,
}, },
data() { data() {
...@@ -186,14 +188,11 @@ export default { ...@@ -186,14 +188,11 @@ export default {
</div> </div>
<div class="content-list environments-container"> <div class="content-list environments-container">
<div <loading-icon
class="environments-list-loading text-center" label="Loading environments"
v-if="isLoading"> size="3"
v-if="isLoading"
<i />
class="fa fa-spinner fa-spin"
aria-hidden="true" />
</div>
<div <div
class="blank-state blank-state-no-icon" class="blank-state blank-state-no-icon"
......
<script> <script>
import playIconSvg from 'icons/_icon_play.svg'; import playIconSvg from 'icons/_icon_play.svg';
import eventHub from '../event_hub'; import eventHub from '../event_hub';
import loadingIcon from '../../vue_shared/components/loading_icon.vue';
export default { export default {
props: { props: {
...@@ -11,6 +12,10 @@ export default { ...@@ -11,6 +12,10 @@ export default {
}, },
}, },
components: {
loadingIcon,
},
data() { data() {
return { return {
playIconSvg, playIconSvg,
...@@ -61,10 +66,7 @@ export default { ...@@ -61,10 +66,7 @@ export default {
<i <i
class="fa fa-caret-down" class="fa fa-caret-down"
aria-hidden="true"/> aria-hidden="true"/>
<i <loading-icon v-if="isLoading" />
v-if="isLoading"
class="fa fa-spinner fa-spin"
aria-hidden="true"/>
</span> </span>
</button> </button>
......
<script> <script>
import Timeago from 'timeago.js'; import Timeago from 'timeago.js';
import _ from 'underscore';
import '../../lib/utils/text_utility'; import '../../lib/utils/text_utility';
import ActionsComponent from './environment_actions.vue'; import ActionsComponent from './environment_actions.vue';
import ExternalUrlComponent from './environment_external_url.vue'; import ExternalUrlComponent from './environment_external_url.vue';
...@@ -59,7 +60,7 @@ export default { ...@@ -59,7 +60,7 @@ export default {
hasLastDeploymentKey() { hasLastDeploymentKey() {
if (this.model && if (this.model &&
this.model.last_deployment && this.model.last_deployment &&
!this.$options.isObjectEmpty(this.model.last_deployment)) { !_.isEmpty(this.model.last_deployment)) {
return true; return true;
} }
return false; return false;
...@@ -310,8 +311,8 @@ export default { ...@@ -310,8 +311,8 @@ export default {
*/ */
deploymentHasUser() { deploymentHasUser() {
return this.model && return this.model &&
!this.$options.isObjectEmpty(this.model.last_deployment) && !_.isEmpty(this.model.last_deployment) &&
!this.$options.isObjectEmpty(this.model.last_deployment.user); !_.isEmpty(this.model.last_deployment.user);
}, },
/** /**
...@@ -322,8 +323,8 @@ export default { ...@@ -322,8 +323,8 @@ export default {
*/ */
deploymentUser() { deploymentUser() {
if (this.model && if (this.model &&
!this.$options.isObjectEmpty(this.model.last_deployment) && !_.isEmpty(this.model.last_deployment) &&
!this.$options.isObjectEmpty(this.model.last_deployment.user)) { !_.isEmpty(this.model.last_deployment.user)) {
return this.model.last_deployment.user; return this.model.last_deployment.user;
} }
return {}; return {};
...@@ -338,8 +339,8 @@ export default { ...@@ -338,8 +339,8 @@ export default {
*/ */
shouldRenderBuildName() { shouldRenderBuildName() {
return !this.model.isFolder && return !this.model.isFolder &&
!this.$options.isObjectEmpty(this.model.last_deployment) && !_.isEmpty(this.model.last_deployment) &&
!this.$options.isObjectEmpty(this.model.last_deployment.deployable); !_.isEmpty(this.model.last_deployment.deployable);
}, },
/** /**
...@@ -380,7 +381,7 @@ export default { ...@@ -380,7 +381,7 @@ export default {
*/ */
shouldRenderDeploymentID() { shouldRenderDeploymentID() {
return !this.model.isFolder && return !this.model.isFolder &&
!this.$options.isObjectEmpty(this.model.last_deployment) && !_.isEmpty(this.model.last_deployment) &&
this.model.last_deployment.iid !== undefined; this.model.last_deployment.iid !== undefined;
}, },
...@@ -410,21 +411,6 @@ export default { ...@@ -410,21 +411,6 @@ export default {
}, },
}, },
/**
* Helper to verify if certain given object are empty.
* Should be replaced by lodash _.isEmpty - https://lodash.com/docs/4.17.2#isEmpty
* @param {Object} object
* @returns {Bollean}
*/
isObjectEmpty(object) {
for (const key in object) { // eslint-disable-line
if (hasOwnProperty.call(object, key)) {
return false;
}
}
return true;
},
methods: { methods: {
onClickFolder() { onClickFolder() {
eventHub.$emit('toggleFolder', this.model, this.folderUrl); eventHub.$emit('toggleFolder', this.model, this.folderUrl);
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
* Makes a post request when the button is clicked. * Makes a post request when the button is clicked.
*/ */
import eventHub from '../event_hub'; import eventHub from '../event_hub';
import loadingIcon from '../../vue_shared/components/loading_icon.vue';
export default { export default {
props: { props: {
...@@ -20,6 +21,10 @@ export default { ...@@ -20,6 +21,10 @@ export default {
}, },
}, },
components: {
loadingIcon,
},
data() { data() {
return { return {
isLoading: false, isLoading: false,
...@@ -49,9 +54,6 @@ export default { ...@@ -49,9 +54,6 @@ export default {
Rollback Rollback
</span> </span>
<i <loading-icon v-if="isLoading" />
v-if="isLoading"
class="fa fa-spinner fa-spin"
aria-hidden="true" />
</button> </button>
</template> </template>
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
* Used in environments table. * Used in environments table.
*/ */
import eventHub from '../event_hub'; import eventHub from '../event_hub';
import loadingIcon from '../../vue_shared/components/loading_icon.vue';
export default { export default {
props: { props: {
...@@ -19,6 +20,10 @@ export default { ...@@ -19,6 +20,10 @@ export default {
}; };
}, },
components: {
loadingIcon,
},
computed: { computed: {
title() { title() {
return 'Stop'; return 'Stop';
...@@ -51,9 +56,6 @@ export default { ...@@ -51,9 +56,6 @@ export default {
<i <i
class="fa fa-stop stop-env-icon" class="fa fa-stop stop-env-icon"
aria-hidden="true" /> aria-hidden="true" />
<i <loading-icon v-if="isLoading" />
v-if="isLoading"
class="fa fa-spinner fa-spin"
aria-hidden="true" />
</button> </button>
</template> </template>
...@@ -3,10 +3,12 @@ ...@@ -3,10 +3,12 @@
* Render environments table. * Render environments table.
*/ */
import EnvironmentTableRowComponent from './environment_item.vue'; import EnvironmentTableRowComponent from './environment_item.vue';
import loadingIcon from '../../vue_shared/components/loading_icon.vue';
export default { export default {
components: { components: {
'environment-item': EnvironmentTableRowComponent, 'environment-item': EnvironmentTableRowComponent,
loadingIcon,
}, },
props: { props: {
...@@ -77,10 +79,8 @@ export default { ...@@ -77,10 +79,8 @@ export default {
<template v-if="model.isFolder && model.isOpen && model.children && model.children.length > 0"> <template v-if="model.isFolder && model.isOpen && model.children && model.children.length > 0">
<tr v-if="isLoadingFolderContent"> <tr v-if="isLoadingFolderContent">
<td colspan="6" class="text-center"> <td colspan="6">
<i <loading-icon size="2" />
class="fa fa-spin fa-spinner fa-2x"
aria-hidden="true" />
</td> </td>
</tr> </tr>
......
<script> <script>
/* global Flash */ /* global Flash */
import EnvironmentsService from '../services/environments_service'; import EnvironmentsService from '../services/environments_service';
import EnvironmentTable from '../components/environments_table.vue'; import environmentTable from '../components/environments_table.vue';
import EnvironmentsStore from '../stores/environments_store'; import EnvironmentsStore from '../stores/environments_store';
import TablePaginationComponent from '../../vue_shared/components/table_pagination'; import loadingIcon from '../../vue_shared/components/loading_icon.vue';
import tablePagination from '../../vue_shared/components/table_pagination.vue';
import '../../lib/utils/common_utils'; import '../../lib/utils/common_utils';
import '../../vue_shared/vue_resource_interceptor'; import '../../vue_shared/vue_resource_interceptor';
export default { export default {
components: { components: {
'environment-table': EnvironmentTable, environmentTable,
'table-pagination': TablePaginationComponent, tablePagination,
loadingIcon,
}, },
data() { data() {
...@@ -153,13 +155,12 @@ export default { ...@@ -153,13 +155,12 @@ export default {
</div> </div>
<div class="environments-container"> <div class="environments-container">
<div
class="environments-list-loading text-center" <loading-icon
v-if="isLoading"> label="Loading environments"
<i v-if="isLoading"
class="fa fa-spinner fa-spin" size="3"
aria-hidden="true"/> />
</div>
<div <div
class="table-holder" class="table-holder"
......
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
/* global notes */ /* global notes */
let $commentButtonTemplate; let $commentButtonTemplate;
var bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; };
window.FilesCommentButton = (function() { window.FilesCommentButton = (function() {
var COMMENT_BUTTON_CLASS, EMPTY_CELL_CLASS, LINE_COLUMN_CLASSES, LINE_CONTENT_CLASS, LINE_HOLDER_CLASS, LINE_NUMBER_CLASS, OLD_LINE_CLASS, TEXT_FILE_SELECTOR, UNFOLDABLE_LINE_CLASS; var COMMENT_BUTTON_CLASS, EMPTY_CELL_CLASS, LINE_COLUMN_CLASSES, LINE_CONTENT_CLASS, LINE_HOLDER_CLASS, LINE_NUMBER_CLASS, OLD_LINE_CLASS, TEXT_FILE_SELECTOR, UNFOLDABLE_LINE_CLASS;
...@@ -27,8 +26,8 @@ window.FilesCommentButton = (function() { ...@@ -27,8 +26,8 @@ window.FilesCommentButton = (function() {
TEXT_FILE_SELECTOR = '.text-file'; TEXT_FILE_SELECTOR = '.text-file';
function FilesCommentButton(filesContainerElement) { function FilesCommentButton(filesContainerElement) {
this.render = bind(this.render, this); this.render = this.render.bind(this);
this.hideButton = bind(this.hideButton, this); this.hideButton = this.hideButton.bind(this);
this.isParallelView = notes.isParallelView(); this.isParallelView = notes.isParallelView();
filesContainerElement.on('mouseover', LINE_COLUMN_CLASSES, this.render) filesContainerElement.on('mouseover', LINE_COLUMN_CLASSES, this.render)
.on('mouseleave', LINE_COLUMN_CLASSES, this.hideButton); .on('mouseleave', LINE_COLUMN_CLASSES, this.hideButton);
......
/* eslint-disable func-names, space-before-function-paren, no-var, one-var, one-var-declaration-per-line, prefer-rest-params, max-len, vars-on-top, wrap-iife, no-unused-vars, quotes, no-shadow, no-cond-assign, prefer-arrow-callback, no-return-assign, no-else-return, camelcase, comma-dangle, no-lonely-if, guard-for-in, no-restricted-syntax, consistent-return, prefer-template, no-param-reassign, no-loop-func, no-mixed-operators */ /* eslint-disable func-names, space-before-function-paren, no-var, one-var, one-var-declaration-per-line, prefer-rest-params, max-len, vars-on-top, wrap-iife, no-unused-vars, quotes, no-shadow, no-cond-assign, prefer-arrow-callback, no-return-assign, no-else-return, camelcase, comma-dangle, no-lonely-if, guard-for-in, no-restricted-syntax, consistent-return, prefer-template, no-param-reassign, no-loop-func, no-mixed-operators */
/* global fuzzaldrinPlus */ /* global fuzzaldrinPlus */
import { isObject } from './lib/utils/type_utility';
var GitLabDropdown, GitLabDropdownFilter, GitLabDropdownRemote, var GitLabDropdown, GitLabDropdownFilter, GitLabDropdownRemote;
bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; },
indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i += 1) { if (i in this && this[i] === item) return i; } return -1; };
GitLabDropdownFilter = (function() { GitLabDropdownFilter = (function() {
var ARROW_KEY_CODES, BLUR_KEYCODES, HAS_VALUE_CLASS; var ARROW_KEY_CODES, BLUR_KEYCODES, HAS_VALUE_CLASS;
...@@ -95,7 +94,7 @@ GitLabDropdownFilter = (function() { ...@@ -95,7 +94,7 @@ GitLabDropdownFilter = (function() {
// { prop: 'def' } // { prop: 'def' }
// ] // ]
// } // }
if (gl.utils.isObject(data)) { if (isObject(data)) {
results = {}; results = {};
for (key in data) { for (key in data) {
group = data[key]; group = data[key];
...@@ -213,10 +212,10 @@ GitLabDropdown = (function() { ...@@ -213,10 +212,10 @@ GitLabDropdown = (function() {
var searchFields, selector, self; var searchFields, selector, self;
this.el = el1; this.el = el1;
this.options = options; this.options = options;
this.updateLabel = bind(this.updateLabel, this); this.updateLabel = this.updateLabel.bind(this);
this.hidden = bind(this.hidden, this); this.hidden = this.hidden.bind(this);
this.opened = bind(this.opened, this); this.opened = this.opened.bind(this);
this.shouldPropagate = bind(this.shouldPropagate, this); this.shouldPropagate = this.shouldPropagate.bind(this);
self = this; self = this;
selector = $(this.el).data("target"); selector = $(this.el).data("target");
this.dropdown = selector != null ? $(selector) : $(this.el).parent(); this.dropdown = selector != null ? $(selector) : $(this.el).parent();
...@@ -398,7 +397,7 @@ GitLabDropdown = (function() { ...@@ -398,7 +397,7 @@ GitLabDropdown = (function() {
html = [this.noResults()]; html = [this.noResults()];
} else { } else {
// Handle array groups // Handle array groups
if (gl.utils.isObject(data)) { if (isObject(data)) {
html = []; html = [];
for (name in data) { for (name in data) {
groupData = data[name]; groupData = data[name];
...@@ -610,7 +609,12 @@ GitLabDropdown = (function() { ...@@ -610,7 +609,12 @@ GitLabDropdown = (function() {
var link = document.createElement('a'); var link = document.createElement('a');
link.href = url; link.href = url;
link.innerHTML = text;
if (this.highlight) {
link.innerHTML = text;
} else {
link.textContent = text;
}
if (selected) { if (selected) {
link.className = 'is-active'; link.className = 'is-active';
...@@ -627,8 +631,8 @@ GitLabDropdown = (function() { ...@@ -627,8 +631,8 @@ GitLabDropdown = (function() {
}; };
GitLabDropdown.prototype.highlightTextMatches = function(text, term) { GitLabDropdown.prototype.highlightTextMatches = function(text, term) {
var occurrences; const occurrences = fuzzaldrinPlus.match(text, term);
occurrences = fuzzaldrinPlus.match(text, term); const indexOf = [].indexOf;
return text.split('').map(function(character, i) { return text.split('').map(function(character, i) {
if (indexOf.call(occurrences, i) !== -1) { if (indexOf.call(occurrences, i) !== -1) {
return "<b>" + character + "</b>"; return "<b>" + character + "</b>";
......
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
import d3 from 'd3'; import d3 from 'd3';
const bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; };
const extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; const extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
const hasProp = {}.hasOwnProperty; const hasProp = {}.hasOwnProperty;
...@@ -95,7 +94,7 @@ export const ContributorsMasterGraph = (function(superClass) { ...@@ -95,7 +94,7 @@ export const ContributorsMasterGraph = (function(superClass) {
function ContributorsMasterGraph(data1) { function ContributorsMasterGraph(data1) {
this.data = data1; this.data = data1;
this.update_content = bind(this.update_content, this); this.update_content = this.update_content.bind(this);
this.width = $('.content').width() - 70; this.width = $('.content').width() - 70;
this.height = 200; this.height = 200;
this.x = null; this.x = null;
......
...@@ -7,8 +7,6 @@ ...@@ -7,8 +7,6 @@
/* global Pikaday */ /* global Pikaday */
(function() { (function() {
var bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; };
this.IssuableForm = (function() { this.IssuableForm = (function() {
IssuableForm.prototype.issueMoveConfirmMsg = 'Are you sure you want to move this issue to another project?'; IssuableForm.prototype.issueMoveConfirmMsg = 'Are you sure you want to move this issue to another project?';
...@@ -17,10 +15,10 @@ ...@@ -17,10 +15,10 @@
function IssuableForm(form) { function IssuableForm(form) {
var $issuableDueDate, calendar; var $issuableDueDate, calendar;
this.form = form; this.form = form;
this.toggleWip = bind(this.toggleWip, this); this.toggleWip = this.toggleWip.bind(this);
this.renderWipExplanation = bind(this.renderWipExplanation, this); this.renderWipExplanation = this.renderWipExplanation.bind(this);
this.resetAutosave = bind(this.resetAutosave, this); this.resetAutosave = this.resetAutosave.bind(this);
this.handleSubmit = bind(this.handleSubmit, this); this.handleSubmit = this.handleSubmit.bind(this);
gl.GfmAutoComplete.setup(); gl.GfmAutoComplete.setup();
new UsersSelect(); new UsersSelect();
new ZenMode(); new ZenMode();
......
/* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, vars-on-top, no-unused-vars, max-len */ /* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, vars-on-top, no-unused-vars, max-len */
(function() { (function() {
var bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; };
this.Labels = (function() { this.Labels = (function() {
function Labels() { function Labels() {
this.setSuggestedColor = bind(this.setSuggestedColor, this); this.setSuggestedColor = this.setSuggestedColor.bind(this);
this.updateColorPreview = bind(this.updateColorPreview, this); this.updateColorPreview = this.updateColorPreview.bind(this);
var form; var form;
form = $('.label-form'); form = $('.label-form');
this.cleanBinding(); this.cleanBinding();
......
/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-param-reassign, no-cond-assign, no-return-assign, max-len */ // eslint-disable-next-line import/prefer-default-export
(function() { export const isObject = obj => obj && obj.constructor === Object;
(function(w) {
var base;
if (w.gl == null) {
w.gl = {};
}
if ((base = w.gl).utils == null) {
base.utils = {};
}
return w.gl.utils.isObject = function(obj) {
return (obj != null) && (obj.constructor === Object);
};
})(window);
}).call(window);
...@@ -31,8 +31,6 @@ require('vendor/jquery.scrollTo'); ...@@ -31,8 +31,6 @@ require('vendor/jquery.scrollTo');
// </div> // </div>
// //
(function() { (function() {
var bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; };
this.LineHighlighter = (function() { this.LineHighlighter = (function() {
// CSS class applied to highlighted lines // CSS class applied to highlighted lines
LineHighlighter.prototype.highlightClass = 'hll'; LineHighlighter.prototype.highlightClass = 'hll';
...@@ -47,9 +45,9 @@ require('vendor/jquery.scrollTo'); ...@@ -47,9 +45,9 @@ require('vendor/jquery.scrollTo');
// hash - String URL hash for dependency injection in tests // hash - String URL hash for dependency injection in tests
hash = location.hash; hash = location.hash;
} }
this.setHash = bind(this.setHash, this); this.setHash = this.setHash.bind(this);
this.highlightLine = bind(this.highlightLine, this); this.highlightLine = this.highlightLine.bind(this);
this.clickHandler = bind(this.clickHandler, this); this.clickHandler = this.clickHandler.bind(this);
this.highlightHash = this.highlightHash.bind(this); this.highlightHash = this.highlightHash.bind(this);
this._hash = hash; this._hash = hash;
this.bindEvents(); this.bindEvents();
......
This diff is collapsed.
...@@ -59,7 +59,6 @@ import './lib/utils/datetime_utility'; ...@@ -59,7 +59,6 @@ import './lib/utils/datetime_utility';
import './lib/utils/notify'; import './lib/utils/notify';
import './lib/utils/pretty_time'; import './lib/utils/pretty_time';
import './lib/utils/text_utility'; import './lib/utils/text_utility';
import './lib/utils/type_utility';
import './lib/utils/url_utility'; import './lib/utils/url_utility';
// u2f // u2f
......
...@@ -6,8 +6,6 @@ require('./task_list'); ...@@ -6,8 +6,6 @@ require('./task_list');
require('./merge_request_tabs'); require('./merge_request_tabs');
(function() { (function() {
var bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; };
this.MergeRequest = (function() { this.MergeRequest = (function() {
function MergeRequest(opts) { function MergeRequest(opts) {
// Initialize MergeRequest behavior // Initialize MergeRequest behavior
...@@ -16,7 +14,7 @@ require('./merge_request_tabs'); ...@@ -16,7 +14,7 @@ require('./merge_request_tabs');
// action - String, current controller action // action - String, current controller action
// //
this.opts = opts != null ? opts : {}; this.opts = opts != null ? opts : {};
this.submitNoteForm = bind(this.submitNoteForm, this); this.submitNoteForm = this.submitNoteForm.bind(this);
this.$el = $('.merge-request'); this.$el = $('.merge-request');
this.$('.show-all-commits').on('click', (function(_this) { this.$('.show-all-commits').on('click', (function(_this) {
return function() { return function() {
......
...@@ -7,8 +7,6 @@ import './smart_interval'; ...@@ -7,8 +7,6 @@ import './smart_interval';
import MiniPipelineGraph from './mini_pipeline_graph_dropdown'; import MiniPipelineGraph from './mini_pipeline_graph_dropdown';
((global) => { ((global) => {
var indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i += 1) { if (i in this && this[i] === item) return i; } return -1; };
const DEPLOYMENT_TEMPLATE = `<div class="mr-widget-heading" id="<%- id %>"> const DEPLOYMENT_TEMPLATE = `<div class="mr-widget-heading" id="<%- id %>">
<div class="ci_widget ci-success"> <div class="ci_widget ci-success">
<%= ci_success_icon %> <%= ci_success_icon %>
...@@ -258,7 +256,7 @@ import MiniPipelineGraph from './mini_pipeline_graph_dropdown'; ...@@ -258,7 +256,7 @@ import MiniPipelineGraph from './mini_pipeline_graph_dropdown';
let stateClass = 'btn-danger'; let stateClass = 'btn-danger';
if (!hasCi) { if (!hasCi) {
stateClass = 'btn-create'; stateClass = 'btn-create';
} else if (indexOf.call(allowed_states, state) !== -1) { } else if (allowed_states.indexOf(state) !== -1) {
switch (state) { switch (state) {
case "failed": case "failed":
case "canceled": case "canceled":
......
...@@ -18,12 +18,11 @@ ...@@ -18,12 +18,11 @@
} }
$els.each(function(i, dropdown) { $els.each(function(i, dropdown) {
var $block, $dropdown, $loading, $selectbox, $sidebarCollapsedValue, $value, abilityName, collapsedSidebarLabelTemplate, defaultLabel, issuableId, issueUpdateURL, milestoneLinkNoneTemplate, milestoneLinkTemplate, milestonesUrl, projectId, selectedMilestone, showAny, showNo, showUpcoming, showStarted, useId, showMenuAbove; var $block, $dropdown, $loading, $selectbox, $sidebarCollapsedValue, $value, abilityName, collapsedSidebarLabelTemplate, defaultLabel, defaultNo, issuableId, issueUpdateURL, milestoneLinkNoneTemplate, milestoneLinkTemplate, milestonesUrl, projectId, selectedMilestone, selectedMilestoneDefault, showAny, showNo, showUpcoming, showStarted, useId, showMenuAbove;
$dropdown = $(dropdown); $dropdown = $(dropdown);
projectId = $dropdown.data('project-id'); projectId = $dropdown.data('project-id');
milestonesUrl = $dropdown.data('milestones'); milestonesUrl = $dropdown.data('milestones');
issueUpdateURL = $dropdown.data('issueUpdate'); issueUpdateURL = $dropdown.data('issueUpdate');
selectedMilestone = $dropdown.data('selected');
showNo = $dropdown.data('show-no'); showNo = $dropdown.data('show-no');
showAny = $dropdown.data('show-any'); showAny = $dropdown.data('show-any');
showMenuAbove = $dropdown.data('showMenuAbove'); showMenuAbove = $dropdown.data('showMenuAbove');
...@@ -31,6 +30,7 @@ ...@@ -31,6 +30,7 @@
showStarted = $dropdown.data('show-started'); showStarted = $dropdown.data('show-started');
useId = $dropdown.data('use-id'); useId = $dropdown.data('use-id');
defaultLabel = $dropdown.data('default-label'); defaultLabel = $dropdown.data('default-label');
defaultNo = $dropdown.data('default-no');
issuableId = $dropdown.data('issuable-id'); issuableId = $dropdown.data('issuable-id');
abilityName = $dropdown.data('ability-name'); abilityName = $dropdown.data('ability-name');
$selectbox = $dropdown.closest('.selectbox'); $selectbox = $dropdown.closest('.selectbox');
...@@ -38,6 +38,9 @@ ...@@ -38,6 +38,9 @@
$sidebarCollapsedValue = $block.find('.sidebar-collapsed-icon'); $sidebarCollapsedValue = $block.find('.sidebar-collapsed-icon');
$value = $block.find('.value'); $value = $block.find('.value');
$loading = $block.find('.block-loading').fadeOut(); $loading = $block.find('.block-loading').fadeOut();
selectedMilestoneDefault = (showAny ? '' : null);
selectedMilestoneDefault = (showNo && defaultNo ? 'No Milestone' : selectedMilestoneDefault);
selectedMilestone = $dropdown.data('selected') || selectedMilestoneDefault;
if (issueUpdateURL) { if (issueUpdateURL) {
milestoneLinkTemplate = _.template('<a href="/<%- full_path %>/milestones/<%- iid %>" class="bold has-tooltip" data-container="body" title="<%- remaining %>"><%- title %></a>'); milestoneLinkTemplate = _.template('<a href="/<%- full_path %>/milestones/<%- iid %>" class="bold has-tooltip" data-container="body" title="<%- remaining %>"><%- title %></a>');
milestoneLinkNoneTemplate = '<span class="no-value">None</span>'; milestoneLinkNoneTemplate = '<span class="no-value">None</span>';
...@@ -86,8 +89,18 @@ ...@@ -86,8 +89,18 @@
if (showMenuAbove) { if (showMenuAbove) {
$dropdown.data('glDropdown').positionMenuAbove(); $dropdown.data('glDropdown').positionMenuAbove();
} }
$(`[data-milestone-id="${selectedMilestone}"] > a`).addClass('is-active');
}); });
}, },
renderRow: function(milestone) {
return `
<li data-milestone-id="${milestone.name}">
<a href='#' class='dropdown-menu-milestone-link'>
${_.escape(milestone.title)}
</a>
</li>
`;
},
filterable: true, filterable: true,
search: { search: {
fields: ['title'] fields: ['title']
...@@ -120,15 +133,24 @@ ...@@ -120,15 +133,24 @@
// display:block overrides the hide-collapse rule // display:block overrides the hide-collapse rule
return $value.css('display', ''); return $value.css('display', '');
}, },
opened: function(e) {
const $el = $(e.currentTarget);
if ($dropdown.hasClass('js-issue-board-sidebar')) {
selectedMilestone = $dropdown[0].dataset.selected || selectedMilestoneDefault;
}
$('a.is-active', $el).removeClass('is-active');
$(`[data-milestone-id="${selectedMilestone}"] > a`, $el).addClass('is-active');
},
vue: $dropdown.hasClass('js-issue-board-sidebar'), vue: $dropdown.hasClass('js-issue-board-sidebar'),
clicked: function(options) { clicked: function(options) {
const { $el, e } = options; const { $el, e } = options;
let selected = options.selectedObj; let selected = options.selectedObj;
var data, isIssueIndex, isMRIndex, isSelecting, page, boardsStore;
var data, isIssueIndex, isMRIndex, page, boardsStore;
page = $('body').data('page'); page = $('body').data('page');
isIssueIndex = page === 'projects:issues:index'; isIssueIndex = page === 'projects:issues:index';
isMRIndex = (page === page && page === 'projects:merge_requests:index'); isMRIndex = (page === page && page === 'projects:merge_requests:index');
isSelecting = (selected.name !== selectedMilestone);
selectedMilestone = isSelecting ? selected.name : selectedMilestoneDefault;
if ($dropdown.hasClass('js-filter-bulk-update') || $dropdown.hasClass('js-issuable-form-dropdown')) { if ($dropdown.hasClass('js-filter-bulk-update') || $dropdown.hasClass('js-issuable-form-dropdown')) {
e.preventDefault(); e.preventDefault();
return; return;
...@@ -142,16 +164,11 @@ ...@@ -142,16 +164,11 @@
boardsStore[$dropdown.data('field-name')] = selected.name; boardsStore[$dropdown.data('field-name')] = selected.name;
e.preventDefault(); e.preventDefault();
} else if ($dropdown.hasClass('js-filter-submit') && (isIssueIndex || isMRIndex)) { } else if ($dropdown.hasClass('js-filter-submit') && (isIssueIndex || isMRIndex)) {
if (selected.name != null) {
selectedMilestone = selected.name;
} else {
selectedMilestone = '';
}
return Issuable.filterResults($dropdown.closest('form')); return Issuable.filterResults($dropdown.closest('form'));
} else if ($dropdown.hasClass('js-filter-submit')) { } else if ($dropdown.hasClass('js-filter-submit')) {
return $dropdown.closest('form').submit(); return $dropdown.closest('form').submit();
} else if ($dropdown.hasClass('js-issue-board-sidebar')) { } else if ($dropdown.hasClass('js-issue-board-sidebar')) {
if (selected.id !== -1) { if (selected.id !== -1 && isSelecting) {
gl.issueBoards.boardStoreIssueSet('milestone', new ListMilestone({ gl.issueBoards.boardStoreIssueSet('milestone', new ListMilestone({
id: selected.id, id: selected.id,
title: selected.name title: selected.name
......
...@@ -2,11 +2,9 @@ ...@@ -2,11 +2,9 @@
/* global Api */ /* global Api */
(function() { (function() {
var bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; };
window.NamespaceSelect = (function() { window.NamespaceSelect = (function() {
function NamespaceSelect(opts) { function NamespaceSelect(opts) {
this.onSelectItem = bind(this.onSelectItem, this); this.onSelectItem = this.onSelectItem.bind(this);
var fieldName, showAny; var fieldName, showAny;
this.dropdown = opts.dropdown; this.dropdown = opts.dropdown;
showAny = true; showAny = true;
......
/* eslint-disable func-names, space-before-function-paren, no-var, one-var, prefer-rest-params, max-len, vars-on-top, wrap-iife, consistent-return, comma-dangle, one-var-declaration-per-line, quotes, no-return-assign, prefer-arrow-callback, prefer-template, no-shadow, no-else-return, max-len, object-shorthand */ /* eslint-disable func-names, space-before-function-paren, no-var, one-var, prefer-rest-params, max-len, vars-on-top, wrap-iife, consistent-return, comma-dangle, one-var-declaration-per-line, quotes, no-return-assign, prefer-arrow-callback, prefer-template, no-shadow, no-else-return, max-len, object-shorthand */
(function() { (function() {
var bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; },
indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i += 1) { if (i in this && this[i] === item) return i; } return -1; };
this.NewBranchForm = (function() { this.NewBranchForm = (function() {
function NewBranchForm(form, availableRefs) { function NewBranchForm(form, availableRefs) {
this.validate = bind(this.validate, this); this.validate = this.validate.bind(this);
this.branchNameError = form.find('.js-branch-name-error'); this.branchNameError = form.find('.js-branch-name-error');
this.name = form.find('.js-branch-name'); this.name = form.find('.js-branch-name');
this.ref = form.find('#ref'); this.ref = form.find('#ref');
...@@ -95,6 +92,8 @@ ...@@ -95,6 +92,8 @@
NewBranchForm.prototype.validate = function() { NewBranchForm.prototype.validate = function() {
var errorMessage, errors, formatter, unique, validator; var errorMessage, errors, formatter, unique, validator;
const indexOf = [].indexOf;
this.branchNameError.empty(); this.branchNameError.empty();
unique = function(values, value) { unique = function(values, value) {
if (indexOf.call(values, value) === -1) { if (indexOf.call(values, value) === -1) {
......
/* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, no-return-assign, max-len */ /* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, no-return-assign, max-len */
(function() { (function() {
var bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; };
this.NewCommitForm = (function() { this.NewCommitForm = (function() {
function NewCommitForm(form, targetBranchName = 'target_branch') { function NewCommitForm(form, targetBranchName = 'target_branch') {
this.form = form; this.form = form;
this.targetBranchName = targetBranchName; this.targetBranchName = targetBranchName;
this.renderDestination = bind(this.renderDestination, this); this.renderDestination = this.renderDestination.bind(this);
this.targetBranchDropdown = form.find('button.js-target-branch'); this.targetBranchDropdown = form.find('button.js-target-branch');
this.originalBranch = form.find('.js-original-branch'); this.originalBranch = form.find('.js-original-branch');
this.createMergeRequest = form.find('.js-create-merge-request'); this.createMergeRequest = form.find('.js-create-merge-request');
......
...@@ -22,8 +22,6 @@ const normalizeNewlines = function(str) { ...@@ -22,8 +22,6 @@ const normalizeNewlines = function(str) {
}; };
(function() { (function() {
var bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; };
this.Notes = (function() { this.Notes = (function() {
const MAX_VISIBLE_COMMIT_LIST_COUNT = 3; const MAX_VISIBLE_COMMIT_LIST_COUNT = 3;
const REGEX_SLASH_COMMANDS = /\/\w+/g; const REGEX_SLASH_COMMANDS = /\/\w+/g;
...@@ -31,24 +29,24 @@ const normalizeNewlines = function(str) { ...@@ -31,24 +29,24 @@ const normalizeNewlines = function(str) {
Notes.interval = null; Notes.interval = null;
function Notes(notes_url, note_ids, last_fetched_at, view) { function Notes(notes_url, note_ids, last_fetched_at, view) {
this.updateTargetButtons = bind(this.updateTargetButtons, this); this.updateTargetButtons = this.updateTargetButtons.bind(this);
this.updateComment = bind(this.updateComment, this); this.updateComment = this.updateComment.bind(this);
this.visibilityChange = bind(this.visibilityChange, this); this.visibilityChange = this.visibilityChange.bind(this);
this.cancelDiscussionForm = bind(this.cancelDiscussionForm, this); this.cancelDiscussionForm = this.cancelDiscussionForm.bind(this);
this.addDiffNote = bind(this.addDiffNote, this); this.addDiffNote = this.addDiffNote.bind(this);
this.setupDiscussionNoteForm = bind(this.setupDiscussionNoteForm, this); this.setupDiscussionNoteForm = this.setupDiscussionNoteForm.bind(this);
this.replyToDiscussionNote = bind(this.replyToDiscussionNote, this); this.replyToDiscussionNote = this.replyToDiscussionNote.bind(this);
this.removeNote = bind(this.removeNote, this); this.removeNote = this.removeNote.bind(this);
this.cancelEdit = bind(this.cancelEdit, this); this.cancelEdit = this.cancelEdit.bind(this);
this.updateNote = bind(this.updateNote, this); this.updateNote = this.updateNote.bind(this);
this.addDiscussionNote = bind(this.addDiscussionNote, this); this.addDiscussionNote = this.addDiscussionNote.bind(this);
this.addNoteError = bind(this.addNoteError, this); this.addNoteError = this.addNoteError.bind(this);
this.addNote = bind(this.addNote, this); this.addNote = this.addNote.bind(this);
this.resetMainTargetForm = bind(this.resetMainTargetForm, this); this.resetMainTargetForm = this.resetMainTargetForm.bind(this);
this.refresh = bind(this.refresh, this); this.refresh = this.refresh.bind(this);
this.keydownNoteText = bind(this.keydownNoteText, this); this.keydownNoteText = this.keydownNoteText.bind(this);
this.toggleCommitList = bind(this.toggleCommitList, this); this.toggleCommitList = this.toggleCommitList.bind(this);
this.postComment = bind(this.postComment, this); this.postComment = this.postComment.bind(this);
this.notes_url = notes_url; this.notes_url = notes_url;
this.note_ids = note_ids; this.note_ids = note_ids;
......
/* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, one-var, one-var-declaration-per-line, newline-per-chained-call, comma-dangle, consistent-return, prefer-arrow-callback, max-len */ /* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, one-var, one-var-declaration-per-line, newline-per-chained-call, comma-dangle, consistent-return, prefer-arrow-callback, max-len */
(function() { (function() {
var bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; };
this.NotificationsForm = (function() { this.NotificationsForm = (function() {
function NotificationsForm() { function NotificationsForm() {
this.toggleCheckbox = bind(this.toggleCheckbox, this); this.toggleCheckbox = this.toggleCheckbox.bind(this);
this.removeEventListeners(); this.removeEventListeners();
this.initEventListeners(); this.initEventListeners();
} }
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
/* global Flash */ /* global Flash */
import '~/flash'; import '~/flash';
import eventHub from '../event_hub'; import eventHub from '../event_hub';
import loadingIcon from '../../vue_shared/components/loading_icon.vue';
export default { export default {
props: { props: {
...@@ -37,6 +38,10 @@ export default { ...@@ -37,6 +38,10 @@ export default {
}, },
}, },
components: {
loadingIcon,
},
data() { data() {
return { return {
isLoading: false, isLoading: false,
...@@ -94,9 +99,6 @@ export default { ...@@ -94,9 +99,6 @@ export default {
<i <i
:class="iconClass" :class="iconClass"
aria-hidden="true" /> aria-hidden="true" />
<i <loading-icon v-if="isLoading" />
class="fa fa-spinner fa-spin"
aria-hidden="true"
v-if="isLoading" />
</button> </button>
</template> </template>
...@@ -5,11 +5,13 @@ ...@@ -5,11 +5,13 @@
import PipelineService from '../../services/pipeline_service'; import PipelineService from '../../services/pipeline_service';
import PipelineStore from '../../stores/pipeline_store'; import PipelineStore from '../../stores/pipeline_store';
import stageColumnComponent from './stage_column_component.vue'; import stageColumnComponent from './stage_column_component.vue';
import loadingIcon from '../../../vue_shared/components/loading_icon.vue';
import '../../../flash'; import '../../../flash';
export default { export default {
components: { components: {
stageColumnComponent, stageColumnComponent,
loadingIcon,
}, },
data() { data() {
...@@ -64,6 +66,24 @@ ...@@ -64,6 +66,24 @@
capitalizeStageName(name) { capitalizeStageName(name) {
return name.charAt(0).toUpperCase() + name.slice(1); return name.charAt(0).toUpperCase() + name.slice(1);
}, },
isFirstColumn(index) {
return index === 0;
},
stageConnectorClass(index, stage) {
let className;
// If it's the first stage column and only has one job
if (index === 0 && stage.groups.length === 1) {
className = 'no-margin';
} else if (index > 0) {
// If it is not the first column
className = 'left-margin';
}
return className;
},
}, },
}; };
</script> </script>
...@@ -71,21 +91,22 @@ ...@@ -71,21 +91,22 @@
<div class="build-content middle-block js-pipeline-graph"> <div class="build-content middle-block js-pipeline-graph">
<div class="pipeline-visualization pipeline-graph"> <div class="pipeline-visualization pipeline-graph">
<div class="text-center"> <div class="text-center">
<i <loading-icon
v-if="isLoading" v-if="isLoading"
class="loading-icon fa fa-spin fa-spinner fa-3x" size="3"
aria-label="Loading" />
aria-hidden="true" />
</div> </div>
<ul <ul
v-if="!isLoading" v-if="!isLoading"
class="stage-column-list"> class="stage-column-list">
<stage-column-component <stage-column-component
v-for="stage in state.graph" v-for="(stage, index) in state.graph"
:title="capitalizeStageName(stage.name)" :title="capitalizeStageName(stage.name)"
:jobs="stage.groups" :jobs="stage.groups"
:key="stage.name"/> :key="stage.name"
:stage-connector-class="stageConnectorClass(index, stage)"
:is-first-column="isFirstColumn(index)"/>
</ul> </ul>
</div> </div>
</div> </div>
......
...@@ -13,6 +13,18 @@ export default { ...@@ -13,6 +13,18 @@ export default {
type: Array, type: Array,
required: true, required: true,
}, },
isFirstColumn: {
type: Boolean,
required: false,
default: false,
},
stageConnectorClass: {
type: String,
required: false,
default: '',
},
}, },
components: { components: {
...@@ -28,20 +40,27 @@ export default { ...@@ -28,20 +40,27 @@ export default {
jobId(job) { jobId(job) {
return `ci-badge-${job.name}`; return `ci-badge-${job.name}`;
}, },
buildConnnectorClass(index) {
return index === 0 && !this.isFirstColumn ? 'left-connector' : '';
},
}, },
}; };
</script> </script>
<template> <template>
<li class="stage-column"> <li
class="stage-column"
:class="stageConnectorClass">
<div class="stage-name"> <div class="stage-name">
{{title}} {{title}}
</div> </div>
<div class="builds-container"> <div class="builds-container">
<ul> <ul>
<li <li
v-for="job in jobs" v-for="(job, index) in jobs"
:key="job.id" :key="job.id"
class="build" class="build"
:class="buildConnnectorClass(index)"
:id="jobId(job)"> :id="jobId(job)">
<div class="curve"></div> <div class="curve"></div>
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
import '~/flash'; import '~/flash';
import playIconSvg from 'icons/_icon_play.svg'; import playIconSvg from 'icons/_icon_play.svg';
import eventHub from '../event_hub'; import eventHub from '../event_hub';
import loadingIconComponent from '../../vue_shared/components/loading_icon.vue';
export default { export default {
props: { props: {
...@@ -17,6 +18,10 @@ export default { ...@@ -17,6 +18,10 @@ export default {
}, },
}, },
components: {
loadingIconComponent,
},
data() { data() {
return { return {
playIconSvg, playIconSvg,
...@@ -65,10 +70,7 @@ export default { ...@@ -65,10 +70,7 @@ export default {
<i <i
class="fa fa-caret-down" class="fa fa-caret-down"
aria-hidden="true" /> aria-hidden="true" />
<i <loading-icon v-if="isLoading" />
v-if="isLoading"
class="fa fa-spinner fa-spin"
aria-hidden="true" />
</button> </button>
<ul class="dropdown-menu dropdown-menu-align-right"> <ul class="dropdown-menu dropdown-menu-align-right">
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
/* global Flash */ /* global Flash */
import { borderlessStatusIconEntityMap } from '../../vue_shared/ci_status_icons'; import { borderlessStatusIconEntityMap } from '../../vue_shared/ci_status_icons';
import loadingIcon from '../../vue_shared/components/loading_icon.vue';
export default { export default {
props: { props: {
...@@ -38,6 +39,10 @@ export default { ...@@ -38,6 +39,10 @@ export default {
}; };
}, },
components: {
loadingIcon,
},
updated() { updated() {
if (this.dropdownContent.length > 0) { if (this.dropdownContent.length > 0) {
this.stopDropdownClickPropagation(); this.stopDropdownClickPropagation();
...@@ -153,15 +158,7 @@ export default { ...@@ -153,15 +158,7 @@ export default {
:class="dropdownClass" :class="dropdownClass"
class="js-builds-dropdown-list scrollable-menu"> class="js-builds-dropdown-list scrollable-menu">
<div <loading-icon v-if="isLoading"/>
class="text-center"
v-if="isLoading">
<i
class="fa fa-spin fa-spinner"
aria-hidden="true"
aria-label="Loading">
</i>
</div>
<ul <ul
v-else v-else
......
...@@ -2,11 +2,12 @@ import Visibility from 'visibilityjs'; ...@@ -2,11 +2,12 @@ import Visibility from 'visibilityjs';
import PipelinesService from './services/pipelines_service'; import PipelinesService from './services/pipelines_service';
import eventHub from './event_hub'; import eventHub from './event_hub';
import PipelinesTableComponent from '../vue_shared/components/pipelines_table'; import PipelinesTableComponent from '../vue_shared/components/pipelines_table';
import TablePaginationComponent from '../vue_shared/components/table_pagination'; import tablePagination from '../vue_shared/components/table_pagination.vue';
import EmptyState from './components/empty_state.vue'; import EmptyState from './components/empty_state.vue';
import ErrorState from './components/error_state.vue'; import ErrorState from './components/error_state.vue';
import NavigationTabs from './components/navigation_tabs'; import NavigationTabs from './components/navigation_tabs';
import NavigationControls from './components/nav_controls'; import NavigationControls from './components/nav_controls';
import loadingIcon from '../vue_shared/components/loading_icon.vue';
import Poll from '../lib/utils/poll'; import Poll from '../lib/utils/poll';
export default { export default {
...@@ -18,12 +19,13 @@ export default { ...@@ -18,12 +19,13 @@ export default {
}, },
components: { components: {
'gl-pagination': TablePaginationComponent, tablePagination,
'pipelines-table-component': PipelinesTableComponent, 'pipelines-table-component': PipelinesTableComponent,
'empty-state': EmptyState, 'empty-state': EmptyState,
'error-state': ErrorState, 'error-state': ErrorState,
'navigation-tabs': NavigationTabs, 'navigation-tabs': NavigationTabs,
'navigation-controls': NavigationControls, 'navigation-controls': NavigationControls,
loadingIcon,
}, },
data() { data() {
...@@ -244,13 +246,11 @@ export default { ...@@ -244,13 +246,11 @@ export default {
<div class="content-list pipelines"> <div class="content-list pipelines">
<div <loading-icon
class="realtime-loading" label="Loading Pipelines"
v-if="isLoading"> size="3"
<i v-if="isLoading"
class="fa fa-spinner fa-spin" />
aria-hidden="true" />
</div>
<empty-state <empty-state
v-if="shouldRenderEmptyState" v-if="shouldRenderEmptyState"
...@@ -275,12 +275,13 @@ export default { ...@@ -275,12 +275,13 @@ export default {
/> />
</div> </div>
<gl-pagination <table-pagination
v-if="shouldRenderPagination" v-if="shouldRenderPagination"
:pagenum="pagenum" :pagenum="pagenum"
:change="change" :change="change"
:count="state.count.all" :count="state.count.all"
:pageInfo="state.pageInfo"/> :pageInfo="state.pageInfo"
/>
</div> </div>
</div> </div>
`, `,
......
...@@ -2,18 +2,16 @@ ...@@ -2,18 +2,16 @@
/* global fuzzaldrinPlus */ /* global fuzzaldrinPlus */
(function() { (function() {
var bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; };
this.ProjectFindFile = (function() { this.ProjectFindFile = (function() {
var highlighter; var highlighter;
function ProjectFindFile(element1, options) { function ProjectFindFile(element1, options) {
this.element = element1; this.element = element1;
this.options = options; this.options = options;
this.goToBlob = bind(this.goToBlob, this); this.goToBlob = this.goToBlob.bind(this);
this.goToTree = bind(this.goToTree, this); this.goToTree = this.goToTree.bind(this);
this.selectRowDown = bind(this.selectRowDown, this); this.selectRowDown = this.selectRowDown.bind(this);
this.selectRowUp = bind(this.selectRowUp, this); this.selectRowUp = this.selectRowUp.bind(this);
this.filePaths = {}; this.filePaths = {};
this.inputElement = this.element.find(".file-finder-input"); this.inputElement = this.element.find(".file-finder-input");
// init event // init event
......
/* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, no-unused-vars, one-var, no-underscore-dangle, prefer-template, no-else-return, prefer-arrow-callback, max-len */ /* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, no-unused-vars, one-var, no-underscore-dangle, prefer-template, no-else-return, prefer-arrow-callback, max-len */
(function() { (function() {
var bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; };
this.ProjectNew = (function() { this.ProjectNew = (function() {
function ProjectNew() { function ProjectNew() {
this.toggleSettings = bind(this.toggleSettings, this); this.toggleSettings = this.toggleSettings.bind(this);
this.$selects = $('.features select'); this.$selects = $('.features select');
this.$repoSelects = this.$selects.filter('.js-repo-select'); this.$repoSelects = this.$selects.filter('.js-repo-select');
......
...@@ -3,11 +3,9 @@ ...@@ -3,11 +3,9 @@
import Cookies from 'js-cookie'; import Cookies from 'js-cookie';
(function() { (function() {
var bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; };
this.Sidebar = (function() { this.Sidebar = (function() {
function Sidebar(currentUser) { function Sidebar(currentUser) {
this.toggleTodo = bind(this.toggleTodo, this); this.toggleTodo = this.toggleTodo.bind(this);
this.sidebar = $('aside'); this.sidebar = $('aside');
this.removeListeners(); this.removeListeners();
this.addEventListeners(); this.addEventListeners();
......
/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, one-var, one-var-declaration-per-line, object-shorthand, prefer-arrow-callback, comma-dangle, prefer-template, quotes, no-else-return, max-len */ /* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, one-var, one-var-declaration-per-line, object-shorthand, prefer-arrow-callback, comma-dangle, prefer-template, quotes, no-else-return, max-len */
/* global Flash */
/* global Api */ /* global Api */
(function() { (function() {
...@@ -7,6 +8,7 @@ ...@@ -7,6 +8,7 @@
var $groupDropdown, $projectDropdown; var $groupDropdown, $projectDropdown;
$groupDropdown = $('.js-search-group-dropdown'); $groupDropdown = $('.js-search-group-dropdown');
$projectDropdown = $('.js-search-project-dropdown'); $projectDropdown = $('.js-search-project-dropdown');
this.groupId = $groupDropdown.data('group-id');
this.eventListeners(); this.eventListeners();
$groupDropdown.glDropdown({ $groupDropdown.glDropdown({
selectable: true, selectable: true,
...@@ -46,14 +48,18 @@ ...@@ -46,14 +48,18 @@
search: { search: {
fields: ['name'] fields: ['name']
}, },
data: function(term, callback) { data: (term, callback) => {
return Api.projects(term, { order_by: 'id' }, function(data) { this.getProjectsData(term)
data.unshift({ .then((data) => {
name_with_namespace: 'Any' data.unshift({
}); name_with_namespace: 'Any'
data.splice(1, 0, 'divider'); });
return callback(data); data.splice(1, 0, 'divider');
});
return data;
})
.then(data => callback(data))
.catch(() => new Flash('Error fetching projects'));
}, },
id: function(obj) { id: function(obj) {
return obj.id; return obj.id;
...@@ -95,6 +101,18 @@ ...@@ -95,6 +101,18 @@
return $('.js-search-input').val('').trigger('keyup').focus(); return $('.js-search-input').val('').trigger('keyup').focus();
}; };
Search.prototype.getProjectsData = function(term) {
return new Promise((resolve) => {
if (this.groupId) {
Api.groupProjects(this.groupId, term, resolve);
} else {
Api.projects(term, {
order_by: 'id',
}, resolve);
}
});
};
return Search; return Search;
})(); })();
}).call(window); }).call(window);
...@@ -4,11 +4,9 @@ ...@@ -4,11 +4,9 @@
import findAndFollowLink from './shortcuts_dashboard_navigation'; import findAndFollowLink from './shortcuts_dashboard_navigation';
(function() { (function() {
var bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; };
this.Shortcuts = (function() { this.Shortcuts = (function() {
function Shortcuts(skipResetBindings) { function Shortcuts(skipResetBindings) {
this.onToggleHelp = bind(this.onToggleHelp, this); this.onToggleHelp = this.onToggleHelp.bind(this);
this.enabledHelp = []; this.enabledHelp = [];
if (!skipResetBindings) { if (!skipResetBindings) {
Mousetrap.reset(); Mousetrap.reset();
......
/* eslint-disable func-names, prefer-arrow-callback, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, one-var, one-var-declaration-per-line, consistent-return, no-param-reassign, max-len */ /* eslint-disable func-names, prefer-arrow-callback, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, one-var, one-var-declaration-per-line, consistent-return, no-param-reassign, max-len */
(function() { (function() {
var bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; };
window.SingleFileDiff = (function() { window.SingleFileDiff = (function() {
var COLLAPSED_HTML, ERROR_HTML, LOADING_HTML, WRAPPER; var COLLAPSED_HTML, ERROR_HTML, LOADING_HTML, WRAPPER;
...@@ -16,7 +14,7 @@ ...@@ -16,7 +14,7 @@
function SingleFileDiff(file) { function SingleFileDiff(file) {
this.file = file; this.file = file;
this.toggleDiff = bind(this.toggleDiff, this); this.toggleDiff = this.toggleDiff.bind(this);
this.content = $('.diff-content', this.file); this.content = $('.diff-content', this.file);
this.$toggleIcon = $('.diff-toggle-caret', this.file); this.$toggleIcon = $('.diff-toggle-caret', this.file);
this.diffForPath = this.content.find('[data-diff-for-path]').data('diff-for-path'); this.diffForPath = this.content.find('[data-diff-for-path]').data('diff-for-path');
......
...@@ -10,18 +10,16 @@ ...@@ -10,18 +10,16 @@
(function() { (function() {
const global = window.gl || (window.gl = {}); const global = window.gl || (window.gl = {});
var bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; };
global.U2FAuthenticate = (function() { global.U2FAuthenticate = (function() {
function U2FAuthenticate(container, form, u2fParams, fallbackButton, fallbackUI) { function U2FAuthenticate(container, form, u2fParams, fallbackButton, fallbackUI) {
this.container = container; this.container = container;
this.renderNotSupported = bind(this.renderNotSupported, this); this.renderNotSupported = this.renderNotSupported.bind(this);
this.renderAuthenticated = bind(this.renderAuthenticated, this); this.renderAuthenticated = this.renderAuthenticated.bind(this);
this.renderError = bind(this.renderError, this); this.renderError = this.renderError.bind(this);
this.renderInProgress = bind(this.renderInProgress, this); this.renderInProgress = this.renderInProgress.bind(this);
this.renderTemplate = bind(this.renderTemplate, this); this.renderTemplate = this.renderTemplate.bind(this);
this.authenticate = bind(this.authenticate, this); this.authenticate = this.authenticate.bind(this);
this.start = bind(this.start, this); this.start = this.start.bind(this);
this.appId = u2fParams.app_id; this.appId = u2fParams.app_id;
this.challenge = u2fParams.challenge; this.challenge = u2fParams.challenge;
this.form = form; this.form = form;
......
...@@ -2,12 +2,10 @@ ...@@ -2,12 +2,10 @@
/* global u2f */ /* global u2f */
(function() { (function() {
var bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; };
this.U2FError = (function() { this.U2FError = (function() {
function U2FError(errorCode, u2fFlowType) { function U2FError(errorCode, u2fFlowType) {
this.errorCode = errorCode; this.errorCode = errorCode;
this.message = bind(this.message, this); this.message = this.message.bind(this);
this.httpsDisabled = window.location.protocol !== 'https:'; this.httpsDisabled = window.location.protocol !== 'https:';
this.u2fFlowType = u2fFlowType; this.u2fFlowType = u2fFlowType;
} }
......
...@@ -8,19 +8,17 @@ ...@@ -8,19 +8,17 @@
// State Flow #1: setup -> in_progress -> registered -> POST to server // State Flow #1: setup -> in_progress -> registered -> POST to server
// State Flow #2: setup -> in_progress -> error -> setup // State Flow #2: setup -> in_progress -> error -> setup
(function() { (function() {
var bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; };
this.U2FRegister = (function() { this.U2FRegister = (function() {
function U2FRegister(container, u2fParams) { function U2FRegister(container, u2fParams) {
this.container = container; this.container = container;
this.renderNotSupported = bind(this.renderNotSupported, this); this.renderNotSupported = this.renderNotSupported.bind(this);
this.renderRegistered = bind(this.renderRegistered, this); this.renderRegistered = this.renderRegistered.bind(this);
this.renderError = bind(this.renderError, this); this.renderError = this.renderError.bind(this);
this.renderInProgress = bind(this.renderInProgress, this); this.renderInProgress = this.renderInProgress.bind(this);
this.renderSetup = bind(this.renderSetup, this); this.renderSetup = this.renderSetup.bind(this);
this.renderTemplate = bind(this.renderTemplate, this); this.renderTemplate = this.renderTemplate.bind(this);
this.register = bind(this.register, this); this.register = this.register.bind(this);
this.start = bind(this.start, this); this.start = this.start.bind(this);
this.appId = u2fParams.app_id; this.appId = u2fParams.app_id;
this.registerRequests = u2fParams.register_requests; this.registerRequests = u2fParams.register_requests;
this.signRequests = u2fParams.sign_requests; this.signRequests = u2fParams.sign_requests;
......
...@@ -3,12 +3,10 @@ ...@@ -3,12 +3,10 @@
import d3 from 'd3'; import d3 from 'd3';
(function() { (function() {
var bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; };
this.Calendar = (function() { this.Calendar = (function() {
function Calendar(timestamps, calendar_activities_path) { function Calendar(timestamps, calendar_activities_path) {
this.calendar_activities_path = calendar_activities_path; this.calendar_activities_path = calendar_activities_path;
this.clickDay = bind(this.clickDay, this); this.clickDay = this.clickDay.bind(this);
this.currentSelectedDate = ''; this.currentSelectedDate = '';
this.daySpace = 1; this.daySpace = 1;
this.daySize = 15; this.daySize = 15;
......
...@@ -6,14 +6,13 @@ ...@@ -6,14 +6,13 @@
window.emitSidebarEvent = window.emitSidebarEvent || $.noop; window.emitSidebarEvent = window.emitSidebarEvent || $.noop;
(function() { (function() {
var bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; }, const slice = [].slice;
slice = [].slice;
this.UsersSelect = (function() { this.UsersSelect = (function() {
function UsersSelect(currentUser, els) { function UsersSelect(currentUser, els) {
var $els; var $els;
this.users = bind(this.users, this); this.users = this.users.bind(this);
this.user = bind(this.user, this); this.user = this.user.bind(this);
this.usersPath = "/autocomplete/users.json"; this.usersPath = "/autocomplete/users.json";
this.userPath = "/autocomplete/users/:id.json"; this.userPath = "/autocomplete/users/:id.json";
if (currentUser != null) { if (currentUser != null) {
......
...@@ -108,8 +108,6 @@ export default { ...@@ -108,8 +108,6 @@ export default {
</div> </div>
<mr-widget-memory-usage <mr-widget-memory-usage
v-if="deployment.metrics_url" v-if="deployment.metrics_url"
:mr="mr"
:service="service"
:metricsUrl="deployment.metrics_url" :metricsUrl="deployment.metrics_url"
/> />
</div> </div>
......
...@@ -5,8 +5,6 @@ import MRWidgetService from '../services/mr_widget_service'; ...@@ -5,8 +5,6 @@ import MRWidgetService from '../services/mr_widget_service';
export default { export default {
name: 'MemoryUsage', name: 'MemoryUsage',
props: { props: {
mr: { type: Object, required: true },
service: { type: Object, required: true },
metricsUrl: { type: String, required: true }, metricsUrl: { type: String, required: true },
}, },
data() { data() {
...@@ -14,6 +12,7 @@ export default { ...@@ -14,6 +12,7 @@ export default {
// memoryFrom: 0, // memoryFrom: 0,
// memoryTo: 0, // memoryTo: 0,
memoryMetrics: [], memoryMetrics: [],
deploymentTime: 0,
hasMetrics: false, hasMetrics: false,
loadFailed: false, loadFailed: false,
loadingMetrics: true, loadingMetrics: true,
...@@ -23,8 +22,22 @@ export default { ...@@ -23,8 +22,22 @@ export default {
components: { components: {
'mr-memory-graph': MemoryGraph, 'mr-memory-graph': MemoryGraph,
}, },
computed: {
shouldShowLoading() {
return this.loadingMetrics && !this.hasMetrics && !this.loadFailed;
},
shouldShowMemoryGraph() {
return !this.loadingMetrics && this.hasMetrics && !this.loadFailed;
},
shouldShowLoadFailure() {
return !this.loadingMetrics && !this.hasMetrics && this.loadFailed;
},
shouldShowMetricsUnavailable() {
return !this.loadingMetrics && !this.hasMetrics && !this.loadFailed;
},
},
methods: { methods: {
computeGraphData(metrics) { computeGraphData(metrics, deploymentTime) {
this.loadingMetrics = false; this.loadingMetrics = false;
const { memory_values } = metrics; const { memory_values } = metrics;
// if (memory_previous.length > 0) { // if (memory_previous.length > 0) {
...@@ -38,70 +51,73 @@ export default { ...@@ -38,70 +51,73 @@ export default {
if (memory_values.length > 0) { if (memory_values.length > 0) {
this.hasMetrics = true; this.hasMetrics = true;
this.memoryMetrics = memory_values[0].values; this.memoryMetrics = memory_values[0].values;
this.deploymentTime = deploymentTime;
} }
}, },
}, loadMetrics() {
mounted() { gl.utils.backOff((next, stop) => {
this.$props.loadingMetrics = true; MRWidgetService.fetchMetrics(this.metricsUrl)
gl.utils.backOff((next, stop) => { .then((res) => {
MRWidgetService.fetchMetrics(this.$props.metricsUrl) if (res.status === statusCodes.NO_CONTENT) {
.then((res) => { this.backOffRequestCounter = this.backOffRequestCounter += 1;
if (res.status === statusCodes.NO_CONTENT) { /* eslint-disable no-unused-expressions */
this.backOffRequestCounter = this.backOffRequestCounter += 1; this.backOffRequestCounter < 3 ? next() : stop(res);
if (this.backOffRequestCounter < 3) {
next();
} else { } else {
stop(res); stop(res);
} }
} else { })
stop(res); .catch(stop);
})
.then((res) => {
if (res.status === statusCodes.NO_CONTENT) {
return res;
} }
})
.catch(stop);
})
.then((res) => {
if (res.status === statusCodes.NO_CONTENT) {
return res;
}
return res.json(); return res.json();
}) })
.then((res) => { .then((res) => {
this.computeGraphData(res.metrics); this.computeGraphData(res.metrics, res.deployment_time);
return res; return res;
}) })
.catch(() => { .catch(() => {
this.$props.loadFailed = true; this.loadFailed = true;
}); this.loadingMetrics = false;
});
},
},
mounted() {
this.loadingMetrics = true;
this.loadMetrics();
}, },
template: ` template: `
<div class="mr-info-list mr-memory-usage"> <div class="mr-info-list clearfix mr-memory-usage js-mr-memory-usage">
<div class="legend"></div> <div class="legend"></div>
<p <p
v-if="loadingMetrics" v-if="shouldShowLoading"
class="usage-info usage-info-loading"> class="usage-info js-usage-info usage-info-loading">
<i <i
class="fa fa-spinner fa-spin usage-info-load-spinner" class="fa fa-spinner fa-spin usage-info-load-spinner"
aria-hidden="true" />Loading deployment statistics. aria-hidden="true" />Loading deployment statistics.
</p> </p>
<p <p
v-if="!hasMetrics && !loadingMetrics" v-if="shouldShowMemoryGraph"
class="usage-info usage-info-loading"> class="usage-info js-usage-info">
Deployment statistics are not available currently.
</p>
<p
v-if="hasMetrics"
class="usage-info">
Deployment memory usage: Deployment memory usage:
</p> </p>
<p <p
v-if="loadFailed" v-if="shouldShowLoadFailure"
class="usage-info"> class="usage-info js-usage-info usage-info-failed">
Failed to load deployment statistics. Failed to load deployment statistics.
</p> </p>
<p
v-if="shouldShowMetricsUnavailable"
class="usage-info js-usage-info usage-info-unavailable">
Deployment statistics are not available currently.
</p>
<mr-memory-graph <mr-memory-graph
v-if="hasMetrics" v-if="shouldShowMemoryGraph"
:metrics="memoryMetrics" :metrics="memoryMetrics"
:deploymentTime="deploymentTime"
height="25" height="25"
width="100" /> width="100" />
</div> </div>
......
import cancelSVG from 'icons/_icon_action_cancel.svg'; import cancelSVG from 'icons/_icon_action_cancel.svg';
import retrySVG from 'icons/_icon_action_retry.svg'; import retrySVG from 'icons/_icon_action_retry.svg';
import playSVG from 'icons/_icon_action_play.svg'; import playSVG from 'icons/_icon_action_play.svg';
import stopSVG from 'icons/_icon_action_stop.svg';
export default function getActionIcon(action) { export default function getActionIcon(action) {
let icon; let icon;
...@@ -14,6 +15,9 @@ export default function getActionIcon(action) { ...@@ -14,6 +15,9 @@ export default function getActionIcon(action) {
case 'icon_action_play': case 'icon_action_play':
icon = playSVG; icon = playSVG;
break; break;
case 'icon_action_stop':
icon = stopSVG;
break;
default: default:
icon = ''; icon = '';
} }
......
<script>
export default {
props: {
label: {
type: String,
required: false,
default: 'Loading',
},
size: {
type: String,
required: false,
default: '1',
},
},
computed: {
cssClass() {
return `fa-${this.size}x`;
},
},
};
</script>
<template>
<div class="text-center">
<i
class="fa fa-spin fa-spinner"
:class="cssClass"
aria-hidden="true"
:aria-label="label">
</i>
</div>
</template>
...@@ -2,6 +2,7 @@ export default { ...@@ -2,6 +2,7 @@ export default {
name: 'MemoryGraph', name: 'MemoryGraph',
props: { props: {
metrics: { type: Array, required: true }, metrics: { type: Array, required: true },
deploymentTime: { type: Number, required: true },
width: { type: String, required: true }, width: { type: String, required: true },
height: { type: String, required: true }, height: { type: String, required: true },
}, },
...@@ -9,27 +10,105 @@ export default { ...@@ -9,27 +10,105 @@ export default {
return { return {
pathD: '', pathD: '',
pathViewBox: '', pathViewBox: '',
// dotX: '', dotX: '',
// dotY: '', dotY: '',
}; };
}, },
computed: {
getFormattedMedian() {
const deployedSince = gl.utils.getTimeago().format(this.deploymentTime * 1000);
return `Deployed ${deployedSince}`;
},
},
methods: {
/**
* Returns metric value index in metrics array
* with timestamp closest to matching median
*/
getMedianMetricIndex(median, metrics) {
let matchIndex = 0;
let timestampDiff = 0;
let smallestDiff = 0;
const metricTimestamps = metrics.map(v => v[0]);
// Find metric timestamp which is closest to deploymentTime
timestampDiff = Math.abs(metricTimestamps[0] - median);
metricTimestamps.forEach((timestamp, index) => {
if (index === 0) { // Skip first element
return;
}
smallestDiff = Math.abs(timestamp - median);
if (smallestDiff < timestampDiff) {
matchIndex = index;
timestampDiff = smallestDiff;
}
});
return matchIndex;
},
/**
* Get Graph Plotting values to render Line and Dot
*/
getGraphPlotValues(median, metrics) {
const renderData = metrics.map(v => v[1]);
const medianMetricIndex = this.getMedianMetricIndex(median, metrics);
let cx = 0;
let cy = 0;
// Find Maximum and Minimum values from `renderData` array
const maxMemory = Math.max.apply(null, renderData);
const minMemory = Math.min.apply(null, renderData);
// Find difference between extreme ends
const diff = maxMemory - minMemory;
const lineWidth = renderData.length;
// Iterate over metrics values and perform following
// 1. Find x & y co-ords for deploymentTime's memory value
// 2. Return line path against maxMemory
const linePath = renderData.map((y, x) => {
if (medianMetricIndex === x) {
cx = x;
cy = maxMemory - y;
}
return `${x} ${maxMemory - y}`;
});
return {
pathD: linePath,
pathViewBox: {
lineWidth,
diff,
},
dotX: cx,
dotY: cy,
};
},
/**
* Render Graph based on provided median and metrics values
*/
renderGraph(median, metrics) {
const { pathD, pathViewBox, dotX, dotY } = this.getGraphPlotValues(median, metrics);
// Set props and update graph on UI.
this.pathD = `M ${pathD}`;
this.pathViewBox = `0 0 ${pathViewBox.lineWidth} ${pathViewBox.diff}`;
this.dotX = dotX;
this.dotY = dotY;
},
},
mounted() { mounted() {
const renderData = this.$props.metrics.map(v => v[1]); this.renderGraph(this.deploymentTime, this.metrics);
const maxMemory = Math.max.apply(null, renderData);
const minMemory = Math.min.apply(null, renderData);
const diff = maxMemory - minMemory;
// const cx = 0;
// const cy = 0;
const lineWidth = renderData.length;
const linePath = renderData.map((y, x) => `${x} ${maxMemory - y}`);
this.pathD = `M ${linePath}`;
this.pathViewBox = `0 0 ${lineWidth} ${diff}`;
}, },
template: ` template: `
<div class="memory-graph-container"> <div class="memory-graph-container">
<svg :width="width" :height="height" xmlns="http://www.w3.org/2000/svg"> <svg class="has-tooltip" :title="getFormattedMedian" :width="width" :height="height" xmlns="http://www.w3.org/2000/svg">
<path :d="pathD" :viewBox="pathViewBox" /> <path :d="pathD" :viewBox="pathViewBox" />
<!--<circle r="0.8" :cx="dotX" :cy="dotY" tranform="translate(0 -1)" /> --> <circle r="1.5" :cx="dotX" :cy="dotY" tranform="translate(0 -1)" />
</svg> </svg>
</div> </div>
`, `,
......
<script>
const PAGINATION_UI_BUTTON_LIMIT = 4; const PAGINATION_UI_BUTTON_LIMIT = 4;
const UI_LIMIT = 6; const UI_LIMIT = 6;
const SPREAD = '...'; const SPREAD = '...';
...@@ -114,22 +115,23 @@ export default { ...@@ -114,22 +115,23 @@ export default {
return items; return items;
}, },
}, },
template: `
<div class="gl-pagination">
<ul class="pagination clearfix">
<li v-for='item in getItems'
:class='{
page: item.page,
prev: item.prev,
next: item.next,
separator: item.separator,
active: item.active,
disabled: item.disabled
}'
>
<a @click="changePage($event)">{{item.title}}</a>
</li>
</ul>
</div>
`,
}; };
</script>
<template>
<div class="gl-pagination">
<ul class="pagination clearfix">
<li
v-for="item in getItems"
:class="{
page: item.page,
prev: item.prev,
next: item.next,
separator: item.separator,
active: item.active,
disabled: item.disabled
}">
<a @click="changePage($event)">{{item.title}}</a>
</li>
</ul>
</div>
</template>
...@@ -312,7 +312,7 @@ ...@@ -312,7 +312,7 @@
} }
.empty-state { .empty-state {
margin: 100px 0 0; margin: 5% auto 0;
.text-content { .text-content {
max-width: 460px; max-width: 460px;
...@@ -335,27 +335,12 @@ ...@@ -335,27 +335,12 @@
} }
.btn { .btn {
margin: $btn-side-margin $btn-side-margin 0 0; margin: $btn-side-margin 5px;
}
@media(max-width: $screen-xs-max) {
margin-top: 50px;
text-align: center;
.btn { @media(max-width: $screen-xs-max) {
width: 100%; width: 100%;
} }
} }
@media(min-width: $screen-xs-max) {
&.merge-requests .text-content {
margin-top: 40px;
}
&.labels .text-content {
margin-top: 70px;
}
}
} }
.flex-container-block { .flex-container-block {
......
...@@ -4,13 +4,14 @@ ...@@ -4,13 +4,14 @@
*/ */
.file-holder { .file-holder {
border: 1px solid $border-color; border: 1px solid $border-color;
border-radius: $border-radius-default;
&.file-holder-no-border { &.file-holder-no-border {
border: 0; border: 0;
} }
&.readme-holder { &.readme-holder {
margin: $gl-padding-top 0; margin: $gl-padding 0;
} }
table { table {
...@@ -25,7 +26,7 @@ ...@@ -25,7 +26,7 @@
text-align: left; text-align: left;
padding: 10px $gl-padding; padding: 10px $gl-padding;
word-wrap: break-word; word-wrap: break-word;
border-radius: 3px 3px 0 0; border-radius: $border-radius-default $border-radius-default 0 0;
&.file-title-clear { &.file-title-clear {
padding-left: 0; padding-left: 0;
...@@ -94,9 +95,16 @@ ...@@ -94,9 +95,16 @@
tr { tr {
border-bottom: 1px solid $blame-border; border-bottom: 1px solid $blame-border;
&:last-child {
border-bottom: none;
}
} }
td { td {
border-top: none;
border-bottom: none;
&:first-child { &:first-child {
border-left: none; border-left: none;
} }
...@@ -107,7 +115,7 @@ ...@@ -107,7 +115,7 @@
} }
td.blame-commit { td.blame-commit {
padding: 0 10px; padding: 5px 10px;
min-width: 400px; min-width: 400px;
background: $gray-light; background: $gray-light;
} }
...@@ -246,7 +254,7 @@ span.idiff { ...@@ -246,7 +254,7 @@ span.idiff {
border-bottom: 1px solid $border-color; border-bottom: 1px solid $border-color;
padding: 5px $gl-padding; padding: 5px $gl-padding;
margin: 0; margin: 0;
border-radius: 3px 3px 0 0; border-radius: $border-radius-default $border-radius-default 0 0;
.file-header-content { .file-header-content {
white-space: nowrap; white-space: nowrap;
......
.memory-graph-container { .memory-graph-container {
svg { svg {
background: $white-light; background: $white-light;
cursor: pointer;
&:hover {
box-shadow: 0 0 4px $gray-darkest inset;
}
} }
path { path {
fill: none; fill: none;
stroke: $blue-500; stroke: $blue-500;
stroke-width: 1px; stroke-width: 2px;
} }
circle { circle {
stroke: $blue-700; stroke: $blue-700;
fill: $blue-700; fill: $blue-700;
stroke-width: 4px;
} }
} }
...@@ -163,7 +163,7 @@ $fixed-layout-width: 1280px; ...@@ -163,7 +163,7 @@ $fixed-layout-width: 1280px;
$limited-layout-width: 990px; $limited-layout-width: 990px;
$gl-avatar-size: 40px; $gl-avatar-size: 40px;
$error-exclamation-point: $red-500; $error-exclamation-point: $red-500;
$border-radius-default: 2px; $border-radius-default: 3px;
$settings-icon-size: 18px; $settings-icon-size: 18px;
$provider-btn-not-active-color: $blue-500; $provider-btn-not-active-color: $blue-500;
$link-underline-blue: $blue-500; $link-underline-blue: $blue-500;
......
...@@ -163,7 +163,6 @@ ...@@ -163,7 +163,6 @@
.avatar-cell { .avatar-cell {
width: 46px; width: 46px;
padding-left: 10px;
img { img {
margin-right: 0; margin-right: 0;
...@@ -175,7 +174,6 @@ ...@@ -175,7 +174,6 @@
justify-content: space-between; justify-content: space-between;
align-items: flex-start; align-items: flex-start;
flex-grow: 1; flex-grow: 1;
padding-left: 10px;
.merge-request-branches & { .merge-request-branches & {
flex-direction: column; flex-direction: column;
......
// Common // Common
.diff-file { .diff-file {
border: 1px solid $border-color;
margin-bottom: $gl-padding; margin-bottom: $gl-padding;
border-radius: 3px;
.commit-short-id { .commit-short-id {
font-family: $regular_font; font-family: $regular_font;
font-weight: 400; font-weight: 400;
} }
.diff-header {
position: relative;
background: $gray-light;
border-bottom: 1px solid $border-color;
padding: 10px 16px;
color: $gl-text-color;
z-index: 10;
border-radius: 3px 3px 0 0;
.diff-title {
font-family: $monospace_font;
word-break: break-all;
display: block;
.file-mode {
color: $file-mode-changed;
}
}
.commit-short-id {
font-family: $monospace_font;
font-size: smaller;
}
}
.file-title, .file-title,
.file-title-flex-parent { .file-title-flex-parent {
cursor: pointer; cursor: pointer;
......
...@@ -5,11 +5,6 @@ ...@@ -5,11 +5,6 @@
} }
} }
.environments-list-loading {
width: 100%;
font-size: 34px;
}
.environments-folder-name { .environments-folder-name {
font-weight: normal; font-weight: normal;
padding-top: 20px; padding-top: 20px;
......
...@@ -51,6 +51,7 @@ ul.related-merge-requests > li { ...@@ -51,6 +51,7 @@ ul.related-merge-requests > li {
display: -ms-flexbox; display: -ms-flexbox;
display: -webkit-flex; display: -webkit-flex;
display: flex; display: flex;
align-items: center;
.merge-request-id { .merge-request-id {
flex-shrink: 0; flex-shrink: 0;
...@@ -59,6 +60,14 @@ ul.related-merge-requests > li { ...@@ -59,6 +60,14 @@ ul.related-merge-requests > li {
.merge-request-info { .merge-request-info {
margin-left: 5px; margin-left: 5px;
} }
.row_title {
vertical-align: bottom;
}
gl-emoji {
font-size: 1em;
}
} }
.merge-requests-title, .merge-requests-title,
...@@ -114,7 +123,6 @@ ul.related-merge-requests > li { ...@@ -114,7 +123,6 @@ ul.related-merge-requests > li {
.related-merge-requests { .related-merge-requests {
.ci-status-link { .ci-status-link {
display: block; display: block;
margin-top: 3px;
margin-right: 5px; margin-right: 5px;
} }
......
...@@ -131,12 +131,6 @@ ...@@ -131,12 +131,6 @@
line-height: 16px; line-height: 16px;
} }
@media (min-width: $screen-sm-min) {
.stage-cell {
padding: 0 4px;
}
}
@media (max-width: $screen-xs-max) { @media (max-width: $screen-xs-max) {
order: 1; order: 1;
margin-top: $gl-padding-top; margin-top: $gl-padding-top;
...@@ -182,8 +176,7 @@ ...@@ -182,8 +176,7 @@
} }
&.mr-memory-usage { &.mr-memory-usage {
margin-top: 10px; margin: 5px 0 10px 25px;
margin-bottom: 10px;
} }
} }
...@@ -511,7 +504,12 @@ ...@@ -511,7 +504,12 @@
.mr-info-list.mr-memory-usage { .mr-info-list.mr-memory-usage {
.legend { .legend {
height: 75%; height: 65%;
top: 0;
@media (max-width: $screen-xs-max) {
height: 20px;
}
} }
p { p {
...@@ -731,13 +729,15 @@ ...@@ -731,13 +729,15 @@
} }
.mr-memory-usage { .mr-memory-usage {
p.usage-info-loading { p.usage-info-loading,
margin-bottom: 6px; p.usage-info-unavailable,
p.usage-info-failed {
margin-bottom: 5px;
}
.usage-info-load-spinner { p.usage-info-loading .usage-info-load-spinner {
margin-right: 10px; margin-right: 10px;
font-size: 16px; font-size: 16px;
}
} }
@media (max-width: $screen-md-min) { @media (max-width: $screen-md-min) {
......
...@@ -284,10 +284,6 @@ ul.notes { ...@@ -284,10 +284,6 @@ ul.notes {
} }
} }
.diff-header > span {
margin-right: 10px;
}
.line_content { .line_content {
white-space: pre-wrap; white-space: pre-wrap;
} }
......
.pipelines { .pipelines {
.realtime-loading {
font-size: 40px;
text-align: center;
margin: 0 auto;
}
.stage { .stage {
max-width: 90px; max-width: 90px;
width: 90px; width: 90px;
...@@ -14,10 +8,6 @@ ...@@ -14,10 +8,6 @@
white-space: nowrap; white-space: nowrap;
} }
.empty-state {
margin: 5% auto 0;
}
.table-holder { .table-holder {
width: 100%; width: 100%;
...@@ -257,7 +247,7 @@ ...@@ -257,7 +247,7 @@
.stage-cell { .stage-cell {
font-size: 0; font-size: 0;
padding: 10px 4px; padding: 0 4px;
> .stage-container > div > button > span > svg, > .stage-container > div > button > span > svg,
> .stage-container > button > svg { > .stage-container > button > svg {
...@@ -384,9 +374,9 @@ ...@@ -384,9 +374,9 @@
content: ''; content: '';
position: absolute; position: absolute;
top: 48%; top: 48%;
left: -48px; left: -44px;
border-top: 2px solid $border-color; border-top: 2px solid $border-color;
width: 48px; width: 44px;
height: 1px; height: 1px;
} }
} }
...@@ -486,7 +476,7 @@ ...@@ -486,7 +476,7 @@
color: $gl-text-color-secondary; color: $gl-text-color-secondary;
// Action Icons in big pipeline-graph nodes // Action Icons in big pipeline-graph nodes
> div > .ci-action-icon-container .ci-action-icon-wrapper { .ci-action-icon-container .ci-action-icon-wrapper {
height: 30px; height: 30px;
width: 30px; width: 30px;
background: $white-light; background: $white-light;
...@@ -511,7 +501,7 @@ ...@@ -511,7 +501,7 @@
} }
} }
> div > .ci-action-icon-container { .ci-action-icon-container {
position: absolute; position: absolute;
right: 5px; right: 5px;
top: 5px; top: 5px;
...@@ -541,7 +531,7 @@ ...@@ -541,7 +531,7 @@
} }
} }
> div > .build-content { .build-content {
display: inline-block; display: inline-block;
padding: 8px 10px 9px; padding: 8px 10px 9px;
width: 100%; width: 100%;
......
...@@ -138,11 +138,12 @@ ...@@ -138,11 +138,12 @@
.blob-commit-info { .blob-commit-info {
list-style: none; list-style: none;
background: $gray-light;
padding: 16px 16px 16px 6px;
border: 1px solid $border-color;
border-bottom: none;
margin: 0; margin: 0;
padding: 0;
}
.blob-content-holder {
margin-top: $gl-padding;
} }
.blob-upload-dropzone-previews { .blob-upload-dropzone-previews {
......
...@@ -47,7 +47,7 @@ module IssuableCollections ...@@ -47,7 +47,7 @@ module IssuableCollections
end end
def merge_requests_collection def merge_requests_collection
merge_requests_finder.execute.preload(:source_project, :target_project, :author, :assignee, :labels, :milestone, :merge_request_diff, target_project: :namespace) merge_requests_finder.execute.preload(:source_project, :target_project, :author, :assignee, :labels, :milestone, :merge_request_diff, :head_pipeline, target_project: :namespace)
end end
def issues_finder def issues_finder
......
class Dashboard::SnippetsController < Dashboard::ApplicationController class Dashboard::SnippetsController < Dashboard::ApplicationController
def index def index
@snippets = SnippetsFinder.new.execute( @snippets = SnippetsFinder.new(
current_user, current_user,
filter: :by_user, author: current_user,
user: current_user,
scope: params[:scope] scope: params[:scope]
) ).execute
@snippets = @snippets.page(params[:page]) @snippets = @snippets.page(params[:page])
end end
end end
class Explore::GroupsController < Explore::ApplicationController class Explore::GroupsController < Explore::ApplicationController
def index def index
@groups = GroupsFinder.new.execute(current_user) @groups = GroupsFinder.new(current_user).execute
@groups = @groups.search(params[:filter_groups]) if params[:filter_groups].present? @groups = @groups.search(params[:filter_groups]) if params[:filter_groups].present?
@groups = @groups.sort(@sort = params[:sort]) @groups = @groups.sort(@sort = params[:sort])
@groups = @groups.page(params[:page]) @groups = @groups.page(params[:page])
......
class Explore::SnippetsController < Explore::ApplicationController class Explore::SnippetsController < Explore::ApplicationController
def index def index
@snippets = SnippetsFinder.new.execute(current_user, filter: :all) @snippets = SnippetsFinder.new(current_user).execute
@snippets = @snippets.page(params[:page]) @snippets = @snippets.page(params[:page])
end end
end end
...@@ -64,7 +64,7 @@ class GroupsController < Groups::ApplicationController ...@@ -64,7 +64,7 @@ class GroupsController < Groups::ApplicationController
end end
def subgroups def subgroups
@nested_groups = group.children @nested_groups = GroupsFinder.new(current_user, parent: group).execute
@nested_groups = @nested_groups.search(params[:filter_groups]) if params[:filter_groups].present? @nested_groups = @nested_groups.search(params[:filter_groups]) if params[:filter_groups].present?
end end
......
...@@ -227,7 +227,7 @@ class Projects::IssuesController < Projects::ApplicationController ...@@ -227,7 +227,7 @@ class Projects::IssuesController < Projects::ApplicationController
def issue def issue
# The Sortable default scope causes performance issues when used with find_by # The Sortable default scope causes performance issues when used with find_by
@noteable = @issue ||= @project.issues.where(iid: params[:id]).reorder(nil).take || redirect_old @noteable = @issue ||= @project.issues.where(iid: params[:id]).reorder(nil).take!
end end
alias_method :subscribable_resource, :issue alias_method :subscribable_resource, :issue
alias_method :issuable, :issue alias_method :issuable, :issue
...@@ -266,21 +266,6 @@ class Projects::IssuesController < Projects::ApplicationController ...@@ -266,21 +266,6 @@ class Projects::IssuesController < Projects::ApplicationController
end end
end end
# Since iids are implemented only in 6.1
# user may navigate to issue page using old global ids.
#
# To prevent 404 errors we provide a redirect to correct iids until 7.0 release
#
def redirect_old
issue = @project.issues.find_by(id: params[:id])
if issue
redirect_to issue_path(issue)
else
raise ActiveRecord::RecordNotFound.new
end
end
def issue_params def issue_params
params.require(:issue).permit( params.require(:issue).permit(
:title, :assignee_id, :position, :description, :confidential, :title, :assignee_id, :position, :description, :confidential,
......
...@@ -410,10 +410,10 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -410,10 +410,10 @@ class Projects::MergeRequestsController < Projects::ApplicationController
metrics_url = metrics_url =
if can?(current_user, :read_environment, environment) && environment.has_metrics? if can?(current_user, :read_environment, environment) && environment.has_metrics?
metrics_namespace_project_environment_path(environment.project.namespace, metrics_namespace_project_environment_deployment_path(environment.project.namespace,
environment.project, environment.project,
environment, environment,
deployment) deployment)
end end
{ {
......
...@@ -23,12 +23,11 @@ class Projects::SnippetsController < Projects::ApplicationController ...@@ -23,12 +23,11 @@ class Projects::SnippetsController < Projects::ApplicationController
respond_to :html respond_to :html
def index def index
@snippets = SnippetsFinder.new.execute( @snippets = SnippetsFinder.new(
current_user, current_user,
filter: :by_project,
project: @project, project: @project,
scope: params[:scope] scope: params[:scope]
) ).execute
@snippets = @snippets.page(params[:page]) @snippets = @snippets.page(params[:page])
if @snippets.out_of_range? && @snippets.total_pages != 0 if @snippets.out_of_range? && @snippets.total_pages != 0
redirect_to namespace_project_snippets_path(page: @snippets.total_pages) redirect_to namespace_project_snippets_path(page: @snippets.total_pages)
......
...@@ -27,12 +27,8 @@ class SnippetsController < ApplicationController ...@@ -27,12 +27,8 @@ class SnippetsController < ApplicationController
return render_404 unless @user return render_404 unless @user
@snippets = SnippetsFinder.new.execute(current_user, { @snippets = SnippetsFinder.new(current_user, author: @user, scope: params[:scope])
filter: :by_user, .execute.page(params[:page])
user: @user,
scope: params[:scope]
})
.page(params[:page])
render 'index' render 'index'
else else
...@@ -103,20 +99,20 @@ class SnippetsController < ApplicationController ...@@ -103,20 +99,20 @@ class SnippetsController < ApplicationController
protected protected
def snippet def snippet
@snippet ||= if current_user @snippet ||= PersonalSnippet.find_by(id: params[:id])
PersonalSnippet.where("author_id = ? OR visibility_level IN (?)",
current_user.id,
[Snippet::PUBLIC, Snippet::INTERNAL]).
find(params[:id])
else
PersonalSnippet.find(params[:id])
end
end end
alias_method :awardable, :snippet alias_method :awardable, :snippet
alias_method :spammable, :snippet alias_method :spammable, :snippet
def authorize_read_snippet! def authorize_read_snippet!
authenticate_user! unless can?(current_user, :read_personal_snippet, @snippet) return if can?(current_user, :read_personal_snippet, @snippet)
if current_user
render_404
else
authenticate_user!
end
end end
def authorize_update_snippet! def authorize_update_snippet!
......
...@@ -128,12 +128,11 @@ class UsersController < ApplicationController ...@@ -128,12 +128,11 @@ class UsersController < ApplicationController
end end
def load_snippets def load_snippets
@snippets = SnippetsFinder.new.execute( @snippets = SnippetsFinder.new(
current_user, current_user,
filter: :by_user, author: user,
user: user,
scope: params[:scope] scope: params[:scope]
).page(params[:page]) ).execute.page(params[:page])
end end
def projects_for_current_user def projects_for_current_user
......
class GroupsFinder < UnionFinder class GroupsFinder < UnionFinder
def execute(current_user = nil) def initialize(current_user = nil, params = {})
segments = all_groups(current_user) @current_user = current_user
@params = params
end
find_union(segments, Group).with_route.order_id_desc def execute
groups = find_union(all_groups, Group).with_route.order_id_desc
by_parent(groups)
end end
private private
def all_groups(current_user) attr_reader :current_user, :params
def all_groups
groups = [] groups = []
groups << current_user.authorized_groups if current_user groups << current_user.authorized_groups if current_user
...@@ -15,4 +21,10 @@ class GroupsFinder < UnionFinder ...@@ -15,4 +21,10 @@ class GroupsFinder < UnionFinder
groups groups
end end
def by_parent(groups)
return groups unless params[:parent]
groups.where(parent: params[:parent])
end
end end
...@@ -67,7 +67,7 @@ class NotesFinder ...@@ -67,7 +67,7 @@ class NotesFinder
when "merge_request" when "merge_request"
MergeRequestsFinder.new(@current_user, project_id: @project.id).execute MergeRequestsFinder.new(@current_user, project_id: @project.id).execute
when "snippet", "project_snippet" when "snippet", "project_snippet"
SnippetsFinder.new.execute(@current_user, filter: :by_project, project: @project) SnippetsFinder.new(@current_user, project: @project).execute
when "personal_snippet" when "personal_snippet"
PersonalSnippet.all PersonalSnippet.all
else else
......
class SnippetsFinder class SnippetsFinder < UnionFinder
def execute(current_user, params = {}) attr_accessor :current_user, :params
filter = params[:filter]
user = params.fetch(:user, current_user) def initialize(current_user, params = {})
@current_user = current_user
case filter @params = params
when :all then end
snippets(current_user).fresh
when :public then def execute
Snippet.are_public.fresh items = init_collection
when :by_user then items = by_project(items)
by_user(current_user, user, params[:scope]) items = by_author(items)
when :by_project items = by_visibility(items)
by_project(current_user, params[:project], params[:scope])
end items.fresh
end end
private private
def snippets(current_user) def init_collection
if current_user items = Snippet.all
Snippet.public_and_internal
else accessible(items)
# Not authenticated
#
# Return only:
# public snippets
Snippet.are_public
end
end end
def by_user(current_user, user, scope) def accessible(items)
snippets = user.snippets.fresh segments = []
segments << items.public_to_user(current_user)
segments << authorized_to_user(items) if current_user
if current_user find_union(segments, Snippet)
include_private = user == current_user
by_scope(snippets, scope, include_private)
else
snippets.are_public
end
end end
def by_project(current_user, project, scope) def authorized_to_user(items)
snippets = project.snippets.fresh items.where(
'author_id = :author_id
OR project_id IN (:project_ids)',
author_id: current_user.id,
project_ids: current_user.authorized_projects.select(:id))
end
if current_user def by_visibility(items)
include_private = project.team.member?(current_user) || current_user.admin? visibility = params[:visibility] || visibility_from_scope
by_scope(snippets, scope, include_private)
else return items unless visibility
snippets.are_public
end items.where(visibility_level: visibility)
end
def by_author(items)
return items unless params[:author]
items.where(author_id: params[:author].id)
end
def by_project(items)
return items unless params[:project]
items.where(project_id: params[:project].id)
end end
def by_scope(snippets, scope = nil, include_private = false) def visibility_from_scope
case scope.to_s case params[:scope].to_s
when 'are_private' when 'are_private'
include_private ? snippets.are_private : Snippet.none Snippet::PRIVATE
when 'are_internal' when 'are_internal'
snippets.are_internal Snippet::INTERNAL
when 'are_public' when 'are_public'
snippets.are_public Snippet::PUBLIC
else else
include_private ? snippets : snippets.public_and_internal nil
end end
end end
end end
...@@ -77,7 +77,7 @@ module ApplicationHelper ...@@ -77,7 +77,7 @@ module ApplicationHelper
end end
if user if user
user.avatar_url(size) || default_avatar user.avatar_url(size: size) || default_avatar
else else
gravatar_icon(user_or_email, size, scale) gravatar_icon(user_or_email, size, scale)
end end
......
...@@ -18,7 +18,7 @@ module BlobHelper ...@@ -18,7 +18,7 @@ module BlobHelper
blob = options.delete(:blob) blob = options.delete(:blob)
blob ||= project.repository.blob_at(ref, path) rescue nil blob ||= project.repository.blob_at(ref, path) rescue nil
return unless blob return unless blob && blob.readable_text?
common_classes = "btn js-edit-blob #{options[:extra_class]}" common_classes = "btn js-edit-blob #{options[:extra_class]}"
......
...@@ -100,17 +100,15 @@ module CommitsHelper ...@@ -100,17 +100,15 @@ module CommitsHelper
end end
def link_to_browse_code(project, commit) def link_to_browse_code(project, commit)
return unless current_controller?(:projects, :commits)
if @path.blank? if @path.blank?
return link_to( return link_to(
"Browse Files", "Browse Files",
namespace_project_tree_path(project.namespace, project, commit), namespace_project_tree_path(project.namespace, project, commit),
class: "btn btn-default" class: "btn btn-default"
) )
end elsif @repo.blob_at(commit.id, @path)
return unless current_controller?(:projects, :commits)
if @repo.blob_at(commit.id, @path)
return link_to( return link_to(
"Browse File", "Browse File",
namespace_project_blob_path(project.namespace, project, namespace_project_blob_path(project.namespace, project,
......
...@@ -116,13 +116,13 @@ module MarkupHelper ...@@ -116,13 +116,13 @@ module MarkupHelper
if gitlab_markdown?(file_name) if gitlab_markdown?(file_name)
markdown_unsafe(text, context) markdown_unsafe(text, context)
elsif asciidoc?(file_name) elsif asciidoc?(file_name)
asciidoc_unsafe(text) asciidoc_unsafe(text, context)
elsif plain?(file_name) elsif plain?(file_name)
content_tag :pre, class: 'plain-readme' do content_tag :pre, class: 'plain-readme' do
text text
end end
else else
other_markup_unsafe(file_name, text) other_markup_unsafe(file_name, text, context)
end end
rescue RuntimeError rescue RuntimeError
simple_format(text) simple_format(text)
...@@ -217,12 +217,12 @@ module MarkupHelper ...@@ -217,12 +217,12 @@ module MarkupHelper
Banzai.render(text, context) Banzai.render(text, context)
end end
def asciidoc_unsafe(text) def asciidoc_unsafe(text, context = {})
Gitlab::Asciidoc.render(text) Gitlab::Asciidoc.render(text, context)
end end
def other_markup_unsafe(file_name, text) def other_markup_unsafe(file_name, text, context = {})
Gitlab::OtherMarkup.render(file_name, text) Gitlab::OtherMarkup.render(file_name, text, context)
end end
def prepare_for_rendering(html, context = {}) def prepare_for_rendering(html, context = {})
......
module SubmoduleHelper module SubmoduleHelper
include Gitlab::ShellAdapter include Gitlab::ShellAdapter
VALID_SUBMODULE_PROTOCOLS = %w[http https git ssh].freeze
# links to files listing for submodule if submodule is a project on this server # links to files listing for submodule if submodule is a project on this server
def submodule_links(submodule_item, ref = nil, repository = @repository) def submodule_links(submodule_item, ref = nil, repository = @repository)
url = repository.submodule_url_for(ref, submodule_item.path) url = repository.submodule_url_for(ref, submodule_item.path)
return url, nil unless url =~ /([^\/:]+)\/([^\/]+(?:\.git)?)\Z/ if url =~ /([^\/:]+)\/([^\/]+(?:\.git)?)\Z/
namespace, project = $1, $2
namespace = $1 project.sub!(/\.git\z/, '')
project = $2
project.chomp!('.git')
if self_url?(url, namespace, project) if self_url?(url, namespace, project)
return namespace_project_path(namespace, project), [namespace_project_path(namespace, project),
namespace_project_tree_path(namespace, project, namespace_project_tree_path(namespace, project, submodule_item.id)]
submodule_item.id) elsif relative_self_url?(url)
elsif relative_self_url?(url) relative_self_links(url, submodule_item.id)
relative_self_links(url, submodule_item.id) elsif github_dot_com_url?(url)
elsif github_dot_com_url?(url) standard_links('github.com', namespace, project, submodule_item.id)
standard_links('github.com', namespace, project, submodule_item.id) elsif gitlab_dot_com_url?(url)
elsif gitlab_dot_com_url?(url) standard_links('gitlab.com', namespace, project, submodule_item.id)
standard_links('gitlab.com', namespace, project, submodule_item.id) else
[sanitize_submodule_url(url), nil]
end
else else
return url, nil [sanitize_submodule_url(url), nil]
end end
end end
...@@ -73,4 +75,16 @@ module SubmoduleHelper ...@@ -73,4 +75,16 @@ module SubmoduleHelper
namespace_project_tree_path(namespace, base, commit) namespace_project_tree_path(namespace, base, commit)
] ]
end end
def sanitize_submodule_url(url)
uri = URI.parse(url)
if uri.scheme.in?(VALID_SUBMODULE_PROTOCOLS)
uri.to_s
else
nil
end
rescue URI::InvalidURIError
nil
end
end end
...@@ -81,7 +81,7 @@ module TreeHelper ...@@ -81,7 +81,7 @@ module TreeHelper
part_path = "" part_path = ""
parts = @path.split('/') parts = @path.split('/')
yield('..', nil) if parts.count > max_links yield('..', File.join(*parts.first(parts.count - 2))) if parts.count > max_links
parts.each do |part| parts.each do |part|
part_path = File.join(part_path, part) unless part_path.empty? part_path = File.join(part_path, part) unless part_path.empty?
......
...@@ -18,6 +18,10 @@ module Ci ...@@ -18,6 +18,10 @@ module Ci
has_many :builds, foreign_key: :commit_id has_many :builds, foreign_key: :commit_id
has_many :trigger_requests, dependent: :destroy, foreign_key: :commit_id has_many :trigger_requests, dependent: :destroy, foreign_key: :commit_id
# Merge requests for which the current pipeline is running against
# the merge request's latest commit.
has_many :merge_requests, foreign_key: "head_pipeline_id"
has_many :pending_builds, -> { pending }, foreign_key: :commit_id, class_name: 'Ci::Build' has_many :pending_builds, -> { pending }, foreign_key: :commit_id, class_name: 'Ci::Build'
has_many :retryable_builds, -> { latest.failed_or_canceled }, foreign_key: :commit_id, class_name: 'Ci::Build' has_many :retryable_builds, -> { latest.failed_or_canceled }, foreign_key: :commit_id, class_name: 'Ci::Build'
has_many :cancelable_statuses, -> { cancelable }, foreign_key: :commit_id, class_name: 'CommitStatus' has_many :cancelable_statuses, -> { cancelable }, foreign_key: :commit_id, class_name: 'CommitStatus'
...@@ -381,14 +385,6 @@ module Ci ...@@ -381,14 +385,6 @@ module Ci
project.execute_services(data, :pipeline_hooks) project.execute_services(data, :pipeline_hooks)
end end
# Merge requests for which the current pipeline is running against
# the merge request's latest commit.
def merge_requests
@merge_requests ||= project.merge_requests
.where(source_branch: self.ref)
.select { |merge_request| merge_request.head_pipeline.try(:id) == self.id }
end
# All the merge requests for which the current pipeline runs/ran against # All the merge requests for which the current pipeline runs/ran against
def all_merge_requests def all_merge_requests
@all_merge_requests ||= project.merge_requests.where(source_branch: ref) @all_merge_requests ||= project.merge_requests.where(source_branch: ref)
......
...@@ -336,6 +336,8 @@ class Commit ...@@ -336,6 +336,8 @@ class Commit
end end
end end
delegate :deltas, to: :raw, prefix: :raw
def diffs(diff_options = nil) def diffs(diff_options = nil)
Gitlab::Diff::FileCollection::Commit.new(self, diff_options: diff_options) Gitlab::Diff::FileCollection::Commit.new(self, diff_options: diff_options)
end end
...@@ -373,7 +375,7 @@ class Commit ...@@ -373,7 +375,7 @@ class Commit
def repo_changes def repo_changes
changes = { added: [], modified: [], removed: [] } changes = { added: [], modified: [], removed: [] }
raw_diffs(deltas_only: true).each do |diff| raw_deltas.each do |diff|
if diff.deleted_file if diff.deleted_file
changes[:removed] << diff.old_path changes[:removed] << diff.old_path
elsif diff.renamed_file || diff.new_file elsif diff.renamed_file || diff.new_file
......
module Avatarable
extend ActiveSupport::Concern
def avatar_path(only_path: true)
return unless self[:avatar].present?
# If only_path is true then use the relative path of avatar.
# Otherwise use full path (including host).
asset_host = ActionController::Base.asset_host
gitlab_host = only_path ? gitlab_config.relative_url_root : gitlab_config.url
# If asset_host is set then it is expected that assets are handled by a standalone host.
# That means we do not want to get GitLab's relative_url_root option anymore.
host = asset_host.present? ? asset_host : gitlab_host
[host, avatar.url].join
end
end
...@@ -4,6 +4,7 @@ class Group < Namespace ...@@ -4,6 +4,7 @@ class Group < Namespace
include Gitlab::ConfigHelper include Gitlab::ConfigHelper
include Gitlab::VisibilityLevel include Gitlab::VisibilityLevel
include AccessRequestable include AccessRequestable
include Avatarable
include Referable include Referable
include SelectForProjectAuthorization include SelectForProjectAuthorization
...@@ -111,10 +112,10 @@ class Group < Namespace ...@@ -111,10 +112,10 @@ class Group < Namespace
allowed_by_projects allowed_by_projects
end end
def avatar_url(size = nil) def avatar_url(**args)
if self[:avatar].present? # We use avatar_path instead of overriding avatar_url because of carrierwave.
[gitlab_config.url, avatar.url].join # See https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/11001/diffs#note_28659864
end avatar_path(args)
end end
def lfs_enabled? def lfs_enabled?
......
...@@ -13,6 +13,8 @@ class MergeRequest < ActiveRecord::Base ...@@ -13,6 +13,8 @@ class MergeRequest < ActiveRecord::Base
has_one :merge_request_diff, has_one :merge_request_diff,
-> { order('merge_request_diffs.id DESC') } -> { order('merge_request_diffs.id DESC') }
belongs_to :head_pipeline, foreign_key: "head_pipeline_id", class_name: "Ci::Pipeline"
has_many :events, as: :target, dependent: :destroy has_many :events, as: :target, dependent: :destroy
has_many :merge_requests_closing_issues, class_name: 'MergeRequestsClosingIssues', dependent: :delete_all has_many :merge_requests_closing_issues, class_name: 'MergeRequestsClosingIssues', dependent: :delete_all
...@@ -829,12 +831,6 @@ class MergeRequest < ActiveRecord::Base ...@@ -829,12 +831,6 @@ class MergeRequest < ActiveRecord::Base
diverged_commits_count > 0 diverged_commits_count > 0
end end
def head_pipeline
return unless diff_head_sha && source_project
@head_pipeline ||= source_project.pipeline_for(source_branch, diff_head_sha)
end
def all_pipelines def all_pipelines
return Ci::Pipeline.none unless source_project return Ci::Pipeline.none unless source_project
......
...@@ -6,6 +6,7 @@ class Project < ActiveRecord::Base ...@@ -6,6 +6,7 @@ class Project < ActiveRecord::Base
include Gitlab::VisibilityLevel include Gitlab::VisibilityLevel
include Gitlab::CurrentSettings include Gitlab::CurrentSettings
include AccessRequestable include AccessRequestable
include Avatarable
include CacheMarkdownField include CacheMarkdownField
include Referable include Referable
include Sortable include Sortable
...@@ -798,12 +799,10 @@ class Project < ActiveRecord::Base ...@@ -798,12 +799,10 @@ class Project < ActiveRecord::Base
repository.avatar repository.avatar
end end
def avatar_url def avatar_url(**args)
if self[:avatar].present? # We use avatar_path instead of overriding avatar_url because of carrierwave.
[gitlab_config.url, avatar.url].join # See https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/11001/diffs#note_28659864
elsif avatar_in_git avatar_path(args) || (Gitlab::Routing.url_helpers.namespace_project_avatar_url(namespace, self) if avatar_in_git)
Gitlab::Routing.url_helpers.namespace_project_avatar_url(namespace, self)
end
end end
# For compatibility with old code # For compatibility with old code
......
class ReadmeBlob < SimpleDelegator
attr_reader :repository
def initialize(blob, repository)
@repository = repository
super(blob)
end
def rendered_markup
repository.rendered_readme
end
end
...@@ -518,7 +518,7 @@ class Repository ...@@ -518,7 +518,7 @@ class Repository
def readme def readme
if head = tree(:head) if head = tree(:head)
head.readme ReadmeBlob.new(head.readme, self)
end end
end end
......
...@@ -152,18 +152,5 @@ class Snippet < ActiveRecord::Base ...@@ -152,18 +152,5 @@ class Snippet < ActiveRecord::Base
where(table[:content].matches(pattern)) where(table[:content].matches(pattern))
end end
def accessible_to(user)
return are_public unless user.present?
return all if user.admin?
where(
'visibility_level IN (:visibility_levels)
OR author_id = :author_id
OR project_id IN (:project_ids)',
visibility_levels: [Snippet::PUBLIC, Snippet::INTERNAL],
author_id: user.id,
project_ids: user.authorized_projects.select(:id))
end
end end
end end
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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