Commit 8619042d authored by Luke "Jared" Bennett's avatar Luke "Jared" Bennett

Merge branch 'master' into droplab-templating-xss-fix

parents 606275da c3bb21ff
image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.3.3-git-2.7-phantomjs-2.1-node-7.1" image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.3.3-golang-1.8-git-2.7-phantomjs-2.1-node-7.1"
cache: cache:
key: "ruby-233" key: "ruby-233"
......
...@@ -25,14 +25,20 @@ logs, and code as it's very hard to read otherwise.) ...@@ -25,14 +25,20 @@ logs, and code as it's very hard to read otherwise.)
#### Results of GitLab environment info #### Results of GitLab environment info
<details>
(For installations with omnibus-gitlab package run and paste the output of: (For installations with omnibus-gitlab package run and paste the output of:
`sudo gitlab-rake gitlab:env:info`) `sudo gitlab-rake gitlab:env:info`)
(For installations from source run and paste the output of: (For installations from source run and paste the output of:
`sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production`) `sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production`)
</details>
#### Results of GitLab application Check #### Results of GitLab application Check
<details>
(For installations with omnibus-gitlab package run and paste the output of: (For installations with omnibus-gitlab package run and paste the output of:
`sudo gitlab-rake gitlab:check SANITIZE=true`) `sudo gitlab-rake gitlab:check SANITIZE=true`)
...@@ -41,6 +47,8 @@ logs, and code as it's very hard to read otherwise.) ...@@ -41,6 +47,8 @@ logs, and code as it's very hard to read otherwise.)
(we will only investigate if the tests are passing) (we will only investigate if the tests are passing)
</details>
### Possible fixes ### Possible fixes
(If you can, link to the line of code that might be responsible for the problem) (If you can, link to the line of code that might be responsible for the problem)
...@@ -7,13 +7,12 @@ import boardBlankState from './board_blank_state'; ...@@ -7,13 +7,12 @@ import boardBlankState from './board_blank_state';
require('./board_delete'); require('./board_delete');
require('./board_list'); require('./board_list');
(() => { const Store = gl.issueBoards.BoardsStore;
const Store = gl.issueBoards.BoardsStore;
window.gl = window.gl || {}; window.gl = window.gl || {};
window.gl.issueBoards = window.gl.issueBoards || {}; window.gl.issueBoards = window.gl.issueBoards || {};
gl.issueBoards.Board = Vue.extend({ gl.issueBoards.Board = Vue.extend({
template: '#js-board-template', template: '#js-board-template',
components: { components: {
boardList, boardList,
...@@ -102,5 +101,4 @@ require('./board_list'); ...@@ -102,5 +101,4 @@ require('./board_list');
this.sortable = Sortable.create(this.$el.parentNode, this.sortableOptions); this.sortable = Sortable.create(this.$el.parentNode, this.sortableOptions);
}, },
}); });
})();
...@@ -2,11 +2,10 @@ ...@@ -2,11 +2,10 @@
import Vue from 'vue'; import Vue from 'vue';
(() => { window.gl = window.gl || {};
window.gl = window.gl || {}; window.gl.issueBoards = window.gl.issueBoards || {};
window.gl.issueBoards = window.gl.issueBoards || {};
gl.issueBoards.BoardDelete = Vue.extend({ gl.issueBoards.BoardDelete = Vue.extend({
props: { props: {
list: Object list: Object
}, },
...@@ -19,5 +18,4 @@ import Vue from 'vue'; ...@@ -19,5 +18,4 @@ import Vue from 'vue';
} }
} }
} }
}); });
})();
...@@ -8,13 +8,12 @@ import Vue from 'vue'; ...@@ -8,13 +8,12 @@ import Vue from 'vue';
require('./sidebar/remove_issue'); require('./sidebar/remove_issue');
(() => { const Store = gl.issueBoards.BoardsStore;
const Store = gl.issueBoards.BoardsStore;
window.gl = window.gl || {}; window.gl = window.gl || {};
window.gl.issueBoards = window.gl.issueBoards || {}; window.gl.issueBoards = window.gl.issueBoards || {};
gl.issueBoards.BoardSidebar = Vue.extend({ gl.issueBoards.BoardSidebar = Vue.extend({
props: { props: {
currentUser: Object currentUser: Object
}, },
...@@ -69,5 +68,4 @@ require('./sidebar/remove_issue'); ...@@ -69,5 +68,4 @@ require('./sidebar/remove_issue');
components: { components: {
removeBtn: gl.issueBoards.RemoveIssueBtn, removeBtn: gl.issueBoards.RemoveIssueBtn,
}, },
}); });
})();
import Vue from 'vue'; import Vue from 'vue';
import eventHub from '../eventhub'; import eventHub from '../eventhub';
(() => { const Store = gl.issueBoards.BoardsStore;
const Store = gl.issueBoards.BoardsStore;
window.gl = window.gl || {}; window.gl = window.gl || {};
window.gl.issueBoards = window.gl.issueBoards || {}; window.gl.issueBoards = window.gl.issueBoards || {};
gl.issueBoards.IssueCardInner = Vue.extend({ gl.issueBoards.IssueCardInner = Vue.extend({
props: { props: {
issue: { issue: {
type: Object, type: Object,
...@@ -137,5 +136,4 @@ import eventHub from '../eventhub'; ...@@ -137,5 +136,4 @@ import eventHub from '../eventhub';
</div> </div>
</div> </div>
`, `,
}); });
})();
import Vue from 'vue'; import Vue from 'vue';
(() => { const ModalStore = gl.issueBoards.ModalStore;
const ModalStore = gl.issueBoards.ModalStore;
gl.issueBoards.ModalEmptyState = Vue.extend({ gl.issueBoards.ModalEmptyState = Vue.extend({
mixins: [gl.issueBoards.ModalMixins], mixins: [gl.issueBoards.ModalMixins],
data() { data() {
return ModalStore.store; return ModalStore.store;
...@@ -67,5 +66,4 @@ import Vue from 'vue'; ...@@ -67,5 +66,4 @@ import Vue from 'vue';
</div> </div>
</section> </section>
`, `,
}); });
})();
...@@ -5,10 +5,9 @@ import Vue from 'vue'; ...@@ -5,10 +5,9 @@ import Vue from 'vue';
require('./lists_dropdown'); require('./lists_dropdown');
(() => { const ModalStore = gl.issueBoards.ModalStore;
const ModalStore = gl.issueBoards.ModalStore;
gl.issueBoards.ModalFooter = Vue.extend({ gl.issueBoards.ModalFooter = Vue.extend({
mixins: [gl.issueBoards.ModalMixins], mixins: [gl.issueBoards.ModalMixins],
data() { data() {
return { return {
...@@ -80,5 +79,4 @@ require('./lists_dropdown'); ...@@ -80,5 +79,4 @@ require('./lists_dropdown');
</button> </button>
</footer> </footer>
`, `,
}); });
})();
...@@ -3,10 +3,9 @@ import modalFilters from './filters'; ...@@ -3,10 +3,9 @@ import modalFilters from './filters';
require('./tabs'); require('./tabs');
(() => { const ModalStore = gl.issueBoards.ModalStore;
const ModalStore = gl.issueBoards.ModalStore;
gl.issueBoards.ModalHeader = Vue.extend({ gl.issueBoards.ModalHeader = Vue.extend({
mixins: [gl.issueBoards.ModalMixins], mixins: [gl.issueBoards.ModalMixins],
props: { props: {
projectId: { projectId: {
...@@ -78,5 +77,4 @@ require('./tabs'); ...@@ -78,5 +77,4 @@ require('./tabs');
</div> </div>
</div> </div>
`, `,
}); });
})();
...@@ -8,10 +8,9 @@ require('./list'); ...@@ -8,10 +8,9 @@ require('./list');
require('./footer'); require('./footer');
require('./empty_state'); require('./empty_state');
(() => { const ModalStore = gl.issueBoards.ModalStore;
const ModalStore = gl.issueBoards.ModalStore;
gl.issueBoards.IssuesModal = Vue.extend({ gl.issueBoards.IssuesModal = Vue.extend({
props: { props: {
blankStateImage: { blankStateImage: {
type: String, type: String,
...@@ -163,5 +162,4 @@ require('./empty_state'); ...@@ -163,5 +162,4 @@ require('./empty_state');
</div> </div>
</div> </div>
`, `,
}); });
})();
...@@ -3,10 +3,9 @@ ...@@ -3,10 +3,9 @@
import Vue from 'vue'; import Vue from 'vue';
(() => { const ModalStore = gl.issueBoards.ModalStore;
const ModalStore = gl.issueBoards.ModalStore;
gl.issueBoards.ModalList = Vue.extend({ gl.issueBoards.ModalList = Vue.extend({
props: { props: {
issueLinkBase: { issueLinkBase: {
type: String, type: String,
...@@ -157,5 +156,4 @@ import Vue from 'vue'; ...@@ -157,5 +156,4 @@ import Vue from 'vue';
</div> </div>
</section> </section>
`, `,
}); });
})();
import Vue from 'vue'; import Vue from 'vue';
(() => { const ModalStore = gl.issueBoards.ModalStore;
const ModalStore = gl.issueBoards.ModalStore;
gl.issueBoards.ModalFooterListsDropdown = Vue.extend({ gl.issueBoards.ModalFooterListsDropdown = Vue.extend({
data() { data() {
return { return {
modal: ModalStore.store, modal: ModalStore.store,
...@@ -53,5 +52,4 @@ import Vue from 'vue'; ...@@ -53,5 +52,4 @@ import Vue from 'vue';
</div> </div>
</div> </div>
`, `,
}); });
})();
import Vue from 'vue'; import Vue from 'vue';
(() => { const ModalStore = gl.issueBoards.ModalStore;
const ModalStore = gl.issueBoards.ModalStore;
gl.issueBoards.ModalTabs = Vue.extend({ gl.issueBoards.ModalTabs = Vue.extend({
mixins: [gl.issueBoards.ModalMixins], mixins: [gl.issueBoards.ModalMixins],
data() { data() {
return ModalStore.store; return ModalStore.store;
...@@ -44,5 +43,4 @@ import Vue from 'vue'; ...@@ -44,5 +43,4 @@ import Vue from 'vue';
</ul> </ul>
</div> </div>
`, `,
}); });
})();
/* eslint-disable comma-dangle, func-names, no-new, space-before-function-paren, one-var */ /* eslint-disable comma-dangle, func-names, no-new, space-before-function-paren, one-var */
(() => { window.gl = window.gl || {};
window.gl = window.gl || {}; window.gl.issueBoards = window.gl.issueBoards || {};
window.gl.issueBoards = window.gl.issueBoards || {};
const Store = gl.issueBoards.BoardsStore; const Store = gl.issueBoards.BoardsStore;
$(document).off('created.label').on('created.label', (e, label) => { $(document).off('created.label').on('created.label', (e, label) => {
Store.new({ Store.new({
title: label.title, title: label.title,
position: Store.state.lists.length - 2, position: Store.state.lists.length - 2,
...@@ -17,9 +16,9 @@ ...@@ -17,9 +16,9 @@
color: label.color color: label.color
} }
}); });
}); });
gl.issueBoards.newListDropdownInit = () => { gl.issueBoards.newListDropdownInit = () => {
$('.js-new-board-list').each(function () { $('.js-new-board-list').each(function () {
const $this = $(this); const $this = $(this);
new gl.CreateLabelDropdown($this.closest('.dropdown').find('.dropdown-new-label'), $this.data('namespace-path'), $this.data('project-path')); new gl.CreateLabelDropdown($this.closest('.dropdown').find('.dropdown-new-label'), $this.data('namespace-path'), $this.data('project-path'));
...@@ -72,5 +71,4 @@ ...@@ -72,5 +71,4 @@
} }
}); });
}); });
}; };
})();
...@@ -3,13 +3,12 @@ ...@@ -3,13 +3,12 @@
import Vue from 'vue'; import Vue from 'vue';
(() => { const Store = gl.issueBoards.BoardsStore;
const Store = gl.issueBoards.BoardsStore;
window.gl = window.gl || {}; window.gl = window.gl || {};
window.gl.issueBoards = window.gl.issueBoards || {}; window.gl.issueBoards = window.gl.issueBoards || {};
gl.issueBoards.RemoveIssueBtn = Vue.extend({ gl.issueBoards.RemoveIssueBtn = Vue.extend({
props: { props: {
issue: { issue: {
type: Object, type: Object,
...@@ -57,5 +56,4 @@ import Vue from 'vue'; ...@@ -57,5 +56,4 @@ import Vue from 'vue';
</button> </button>
</div> </div>
`, `,
}); });
})();
(() => { const ModalStore = gl.issueBoards.ModalStore;
const ModalStore = gl.issueBoards.ModalStore;
gl.issueBoards.ModalMixins = { gl.issueBoards.ModalMixins = {
methods: { methods: {
toggleModal(toggle) { toggleModal(toggle) {
ModalStore.store.showAddIssuesModal = toggle; ModalStore.store.showAddIssuesModal = toggle;
...@@ -10,5 +9,4 @@ ...@@ -10,5 +9,4 @@
ModalStore.store.activeTab = tab; ModalStore.store.activeTab = tab;
}, },
}, },
}; };
})();
/* eslint-disable no-unused-vars, no-mixed-operators, comma-dangle */ /* eslint-disable no-unused-vars, no-mixed-operators, comma-dangle */
/* global DocumentTouch */ /* global DocumentTouch */
((w) => { window.gl = window.gl || {};
window.gl = window.gl || {}; window.gl.issueBoards = window.gl.issueBoards || {};
window.gl.issueBoards = window.gl.issueBoards || {};
gl.issueBoards.onStart = () => { gl.issueBoards.onStart = () => {
$('.has-tooltip').tooltip('hide') $('.has-tooltip').tooltip('hide')
.tooltip('disable'); .tooltip('disable');
document.body.classList.add('is-dragging'); document.body.classList.add('is-dragging');
}; };
gl.issueBoards.onEnd = () => { gl.issueBoards.onEnd = () => {
$('.has-tooltip').tooltip('enable'); $('.has-tooltip').tooltip('enable');
document.body.classList.remove('is-dragging'); document.body.classList.remove('is-dragging');
}; };
gl.issueBoards.touchEnabled = ('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch; gl.issueBoards.touchEnabled = ('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch;
gl.issueBoards.getBoardSortableDefaultOptions = (obj) => { gl.issueBoards.getBoardSortableDefaultOptions = (obj) => {
const defaultSortOptions = { const defaultSortOptions = {
animation: 200, animation: 200,
forceFallback: true, forceFallback: true,
...@@ -35,5 +34,4 @@ ...@@ -35,5 +34,4 @@
Object.keys(obj).forEach((key) => { defaultSortOptions[key] = obj[key]; }); Object.keys(obj).forEach((key) => { defaultSortOptions[key] = obj[key]; });
return defaultSortOptions; return defaultSortOptions;
}; };
})(window);
...@@ -3,11 +3,10 @@ ...@@ -3,11 +3,10 @@
import Cookies from 'js-cookie'; import Cookies from 'js-cookie';
(() => { window.gl = window.gl || {};
window.gl = window.gl || {}; window.gl.issueBoards = window.gl.issueBoards || {};
window.gl.issueBoards = window.gl.issueBoards || {};
gl.issueBoards.BoardsStore = { gl.issueBoards.BoardsStore = {
disabled: false, disabled: false,
filter: { filter: {
path: '', path: '',
...@@ -123,5 +122,4 @@ import Cookies from 'js-cookie'; ...@@ -123,5 +122,4 @@ import Cookies from 'js-cookie';
updateFiltersUrl () { updateFiltersUrl () {
history.pushState(null, null, `?${this.filter.path}`); history.pushState(null, null, `?${this.filter.path}`);
} }
}; };
})();
(() => { window.gl = window.gl || {};
window.gl = window.gl || {}; window.gl.issueBoards = window.gl.issueBoards || {};
window.gl.issueBoards = window.gl.issueBoards || {};
class ModalStore { class ModalStore {
constructor() { constructor() {
this.store = { this.store = {
columns: 3, columns: 3,
...@@ -94,7 +93,6 @@ ...@@ -94,7 +93,6 @@
return this.store.selectedIssues return this.store.selectedIssues
.filter(filteredIssue => filteredIssue.id === issue.id)[0]; .filter(filteredIssue => filteredIssue.id === issue.id)[0];
} }
} }
gl.issueBoards.ModalStore = new ModalStore(); gl.issueBoards.ModalStore = new ModalStore();
})();
...@@ -2,10 +2,10 @@ ...@@ -2,10 +2,10 @@
import Vue from 'vue'; import Vue from 'vue';
((global) => { const global = window.gl || (window.gl = {});
global.cycleAnalytics = global.cycleAnalytics || {}; global.cycleAnalytics = global.cycleAnalytics || {};
global.cycleAnalytics.StageCodeComponent = Vue.extend({ global.cycleAnalytics.StageCodeComponent = Vue.extend({
props: { props: {
items: Array, items: Array,
stage: Object, stage: Object,
...@@ -43,5 +43,4 @@ import Vue from 'vue'; ...@@ -43,5 +43,4 @@ import Vue from 'vue';
</ul> </ul>
</div> </div>
`, `,
}); });
})(window.gl || (window.gl = {}));
...@@ -2,10 +2,10 @@ ...@@ -2,10 +2,10 @@
import Vue from 'vue'; import Vue from 'vue';
((global) => { const global = window.gl || (window.gl = {});
global.cycleAnalytics = global.cycleAnalytics || {}; global.cycleAnalytics = global.cycleAnalytics || {};
global.cycleAnalytics.StageIssueComponent = Vue.extend({ global.cycleAnalytics.StageIssueComponent = Vue.extend({
props: { props: {
items: Array, items: Array,
stage: Object, stage: Object,
...@@ -45,5 +45,4 @@ import Vue from 'vue'; ...@@ -45,5 +45,4 @@ import Vue from 'vue';
</ul> </ul>
</div> </div>
`, `,
}); });
})(window.gl || (window.gl = {}));
...@@ -2,10 +2,10 @@ ...@@ -2,10 +2,10 @@
import Vue from 'vue'; import Vue from 'vue';
import iconCommit from '../svg/icon_commit.svg'; import iconCommit from '../svg/icon_commit.svg';
((global) => { const global = window.gl || (window.gl = {});
global.cycleAnalytics = global.cycleAnalytics || {}; global.cycleAnalytics = global.cycleAnalytics || {};
global.cycleAnalytics.StagePlanComponent = Vue.extend({ global.cycleAnalytics.StagePlanComponent = Vue.extend({
props: { props: {
items: Array, items: Array,
stage: Object, stage: Object,
...@@ -47,5 +47,4 @@ import iconCommit from '../svg/icon_commit.svg'; ...@@ -47,5 +47,4 @@ import iconCommit from '../svg/icon_commit.svg';
</ul> </ul>
</div> </div>
`, `,
}); });
})(window.gl || (window.gl = {}));
...@@ -2,10 +2,10 @@ ...@@ -2,10 +2,10 @@
import Vue from 'vue'; import Vue from 'vue';
((global) => { const global = window.gl || (window.gl = {});
global.cycleAnalytics = global.cycleAnalytics || {}; global.cycleAnalytics = global.cycleAnalytics || {};
global.cycleAnalytics.StageProductionComponent = Vue.extend({ global.cycleAnalytics.StageProductionComponent = Vue.extend({
props: { props: {
items: Array, items: Array,
stage: Object, stage: Object,
...@@ -45,5 +45,4 @@ import Vue from 'vue'; ...@@ -45,5 +45,4 @@ import Vue from 'vue';
</ul> </ul>
</div> </div>
`, `,
}); });
})(window.gl || (window.gl = {}));
...@@ -2,10 +2,10 @@ ...@@ -2,10 +2,10 @@
import Vue from 'vue'; import Vue from 'vue';
((global) => { const global = window.gl || (window.gl = {});
global.cycleAnalytics = global.cycleAnalytics || {}; global.cycleAnalytics = global.cycleAnalytics || {};
global.cycleAnalytics.StageReviewComponent = Vue.extend({ global.cycleAnalytics.StageReviewComponent = Vue.extend({
props: { props: {
items: Array, items: Array,
stage: Object, stage: Object,
...@@ -55,5 +55,4 @@ import Vue from 'vue'; ...@@ -55,5 +55,4 @@ import Vue from 'vue';
</ul> </ul>
</div> </div>
`, `,
}); });
})(window.gl || (window.gl = {}));
...@@ -2,10 +2,10 @@ ...@@ -2,10 +2,10 @@
import Vue from 'vue'; import Vue from 'vue';
import iconBranch from '../svg/icon_branch.svg'; import iconBranch from '../svg/icon_branch.svg';
((global) => { const global = window.gl || (window.gl = {});
global.cycleAnalytics = global.cycleAnalytics || {}; global.cycleAnalytics = global.cycleAnalytics || {};
global.cycleAnalytics.StageStagingComponent = Vue.extend({ global.cycleAnalytics.StageStagingComponent = Vue.extend({
props: { props: {
items: Array, items: Array,
stage: Object, stage: Object,
...@@ -45,5 +45,4 @@ import iconBranch from '../svg/icon_branch.svg'; ...@@ -45,5 +45,4 @@ import iconBranch from '../svg/icon_branch.svg';
</ul> </ul>
</div> </div>
`, `,
}); });
})(window.gl || (window.gl = {}));
...@@ -3,10 +3,10 @@ import Vue from 'vue'; ...@@ -3,10 +3,10 @@ import Vue from 'vue';
import iconBuildStatus from '../svg/icon_build_status.svg'; import iconBuildStatus from '../svg/icon_build_status.svg';
import iconBranch from '../svg/icon_branch.svg'; import iconBranch from '../svg/icon_branch.svg';
((global) => { const global = window.gl || (window.gl = {});
global.cycleAnalytics = global.cycleAnalytics || {}; global.cycleAnalytics = global.cycleAnalytics || {};
global.cycleAnalytics.StageTestComponent = Vue.extend({ global.cycleAnalytics.StageTestComponent = Vue.extend({
props: { props: {
items: Array, items: Array,
stage: Object, stage: Object,
...@@ -46,5 +46,4 @@ import iconBranch from '../svg/icon_branch.svg'; ...@@ -46,5 +46,4 @@ import iconBranch from '../svg/icon_branch.svg';
</ul> </ul>
</div> </div>
`, `,
}); });
})(window.gl || (window.gl = {}));
...@@ -2,10 +2,10 @@ ...@@ -2,10 +2,10 @@
import Vue from 'vue'; import Vue from 'vue';
((global) => { const global = window.gl || (window.gl = {});
global.cycleAnalytics = global.cycleAnalytics || {}; global.cycleAnalytics = global.cycleAnalytics || {};
global.cycleAnalytics.TotalTimeComponent = Vue.extend({ global.cycleAnalytics.TotalTimeComponent = Vue.extend({
props: { props: {
time: Object, time: Object,
}, },
...@@ -22,5 +22,4 @@ import Vue from 'vue'; ...@@ -22,5 +22,4 @@ import Vue from 'vue';
</template> </template>
</span> </span>
`, `,
}); });
})(window.gl || (window.gl = {}));
/* eslint-disable no-param-reassign */ /* eslint-disable no-param-reassign */
((global) => {
global.cycleAnalytics = global.cycleAnalytics || {};
class CycleAnalyticsService { const global = window.gl || (window.gl = {});
global.cycleAnalytics = global.cycleAnalytics || {};
class CycleAnalyticsService {
constructor(options) { constructor(options) {
this.requestPath = options.requestPath; this.requestPath = options.requestPath;
} }
...@@ -35,7 +36,6 @@ ...@@ -35,7 +36,6 @@
}, },
}); });
} }
} }
global.cycleAnalytics.CycleAnalyticsService = CycleAnalyticsService; global.cycleAnalytics.CycleAnalyticsService = CycleAnalyticsService;
})(window.gl || (window.gl = {}));
...@@ -3,10 +3,10 @@ ...@@ -3,10 +3,10 @@
require('../lib/utils/text_utility'); require('../lib/utils/text_utility');
const DEFAULT_EVENT_OBJECTS = require('./default_event_objects'); const DEFAULT_EVENT_OBJECTS = require('./default_event_objects');
((global) => { const global = window.gl || (window.gl = {});
global.cycleAnalytics = global.cycleAnalytics || {}; global.cycleAnalytics = global.cycleAnalytics || {};
const EMPTY_STAGE_TEXTS = { const EMPTY_STAGE_TEXTS = {
issue: 'The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage.', issue: 'The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage.',
plan: 'The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit.', plan: 'The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit.',
code: 'The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request.', code: 'The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request.',
...@@ -14,9 +14,9 @@ const DEFAULT_EVENT_OBJECTS = require('./default_event_objects'); ...@@ -14,9 +14,9 @@ const DEFAULT_EVENT_OBJECTS = require('./default_event_objects');
review: 'The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request.', review: 'The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request.',
staging: 'The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time.', staging: 'The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time.',
production: 'The production stage shows the total time it takes between creating an issue and deploying the code to production. The data will be automatically added once you have completed the full idea to production cycle.', production: 'The production stage shows the total time it takes between creating an issue and deploying the code to production. The data will be automatically added once you have completed the full idea to production cycle.',
}; };
global.cycleAnalytics.CycleAnalyticsStore = { global.cycleAnalytics.CycleAnalyticsStore = {
state: { state: {
summary: '', summary: '',
stats: '', stats: '',
...@@ -100,5 +100,4 @@ const DEFAULT_EVENT_OBJECTS = require('./default_event_objects'); ...@@ -100,5 +100,4 @@ const DEFAULT_EVENT_OBJECTS = require('./default_event_objects');
currentActiveStage() { currentActiveStage() {
return this.state.stages.find(stage => stage.active); return this.state.stages.find(stage => stage.active);
}, },
}; };
})(window.gl || (window.gl = {}));
...@@ -50,7 +50,7 @@ window.gl.GfmAutoComplete = { ...@@ -50,7 +50,7 @@ window.gl.GfmAutoComplete = {
template: '<li>${title}</li>' template: '<li>${title}</li>'
}, },
Loading: { Loading: {
template: '<li style="pointer-events: none;"><i class="fa fa-refresh fa-spin"></i> Loading...</li>' template: '<li style="pointer-events: none;"><i class="fa fa-spinner fa-spin"></i> Loading...</li>'
}, },
DefaultOptions: { DefaultOptions: {
sorter: function(query, items, searchKey) { sorter: function(query, items, searchKey) {
......
...@@ -20,57 +20,60 @@ class Issue { ...@@ -20,57 +20,60 @@ class Issue {
}); });
Issue.initIssueBtnEventListeners(); Issue.initIssueBtnEventListeners();
} }
Issue.$btnNewBranch = $('#new-branch');
Issue.initMergeRequests(); Issue.initMergeRequests();
Issue.initRelatedBranches(); Issue.initRelatedBranches();
Issue.initCanCreateBranch(); Issue.initCanCreateBranch();
} }
static initIssueBtnEventListeners() { static initIssueBtnEventListeners() {
var issueFailMessage; const issueFailMessage = 'Unable to update this issue at this time.';
issueFailMessage = 'Unable to update this issue at this time.';
return $('a.btn-close, a.btn-reopen').on('click', function(e) { const closeButtons = $('a.btn-close');
var $this, isClose, shouldSubmit, url; const isClosedBadge = $('div.status-box-closed');
const isOpenBadge = $('div.status-box-open');
const projectIssuesCounter = $('.issue_counter');
const reopenButtons = $('a.btn-reopen');
return closeButtons.add(reopenButtons).on('click', function(e) {
var $this, shouldSubmit, url;
e.preventDefault(); e.preventDefault();
e.stopImmediatePropagation(); e.stopImmediatePropagation();
$this = $(this); $this = $(this);
isClose = $this.hasClass('btn-close');
shouldSubmit = $this.hasClass('btn-comment'); shouldSubmit = $this.hasClass('btn-comment');
if (shouldSubmit) { if (shouldSubmit) {
Issue.submitNoteForm($this.closest('form')); Issue.submitNoteForm($this.closest('form'));
} }
$this.prop('disabled', true); $this.prop('disabled', true);
Issue.setNewBranchButtonState(true, null);
url = $this.attr('href'); url = $this.attr('href');
return $.ajax({ return $.ajax({
type: 'PUT', type: 'PUT',
url: url, url: url
error: function(jqXHR, textStatus, errorThrown) { }).fail(function(jqXHR, textStatus, errorThrown) {
var issueStatus; new Flash(issueFailMessage);
issueStatus = isClose ? 'close' : 'open'; Issue.initCanCreateBranch();
return new Flash(issueFailMessage, 'alert'); }).done(function(data, textStatus, jqXHR) {
},
success: function(data, textStatus, jqXHR) {
if ('id' in data) { if ('id' in data) {
$(document).trigger('issuable:change'); $(document).trigger('issuable:change');
let total = Number($('.issue_counter').text().replace(/[^\d]/, ''));
if (isClose) { const isClosed = $this.hasClass('btn-close');
$('a.btn-close').addClass('hidden'); closeButtons.toggleClass('hidden', isClosed);
$('a.btn-reopen').removeClass('hidden'); reopenButtons.toggleClass('hidden', !isClosed);
$('div.status-box-closed').removeClass('hidden'); isClosedBadge.toggleClass('hidden', !isClosed);
$('div.status-box-open').addClass('hidden'); isOpenBadge.toggleClass('hidden', isClosed);
total -= 1;
} else { let numProjectIssues = Number(projectIssuesCounter.text().replace(/[^\d]/, ''));
$('a.btn-reopen').addClass('hidden'); numProjectIssues = isClosed ? numProjectIssues - 1 : numProjectIssues + 1;
$('a.btn-close').removeClass('hidden'); projectIssuesCounter.text(gl.text.addDelimiter(numProjectIssues));
$('div.status-box-closed').addClass('hidden');
$('div.status-box-open').removeClass('hidden');
total += 1;
}
$('.issue_counter').text(gl.text.addDelimiter(total));
} else { } else {
new Flash(issueFailMessage, 'alert'); new Flash(issueFailMessage);
}
return $this.prop('disabled', false);
} }
$this.prop('disabled', false);
Issue.initCanCreateBranch();
}); });
}); });
} }
...@@ -86,9 +89,9 @@ class Issue { ...@@ -86,9 +89,9 @@ class Issue {
static initMergeRequests() { static initMergeRequests() {
var $container; var $container;
$container = $('#merge-requests'); $container = $('#merge-requests');
return $.getJSON($container.data('url')).error(function() { return $.getJSON($container.data('url')).fail(function() {
return new Flash('Failed to load referenced merge requests', 'alert'); return new Flash('Failed to load referenced merge requests');
}).success(function(data) { }).done(function(data) {
if ('html' in data) { if ('html' in data) {
return $container.html(data.html); return $container.html(data.html);
} }
...@@ -98,9 +101,9 @@ class Issue { ...@@ -98,9 +101,9 @@ class Issue {
static initRelatedBranches() { static initRelatedBranches() {
var $container; var $container;
$container = $('#related-branches'); $container = $('#related-branches');
return $.getJSON($container.data('url')).error(function() { return $.getJSON($container.data('url')).fail(function() {
return new Flash('Failed to load related branches', 'alert'); return new Flash('Failed to load related branches');
}).success(function(data) { }).done(function(data) {
if ('html' in data) { if ('html' in data) {
return $container.html(data.html); return $container.html(data.html);
} }
...@@ -108,24 +111,27 @@ class Issue { ...@@ -108,24 +111,27 @@ class Issue {
} }
static initCanCreateBranch() { static initCanCreateBranch() {
var $container;
$container = $('#new-branch');
// If the user doesn't have the required permissions the container isn't // If the user doesn't have the required permissions the container isn't
// rendered at all. // rendered at all.
if ($container.length === 0) { if (Issue.$btnNewBranch.length === 0) {
return; return;
} }
return $.getJSON($container.data('path')).error(function() { return $.getJSON(Issue.$btnNewBranch.data('path')).fail(function() {
$container.find('.unavailable').show(); Issue.setNewBranchButtonState(false, false);
return new Flash('Failed to check if a new branch can be created.', 'alert'); new Flash('Failed to check if a new branch can be created.');
}).success(function(data) { }).done(function(data) {
if (data.can_create_branch) { Issue.setNewBranchButtonState(false, data.can_create_branch);
$container.find('.available').show();
} else {
return $container.find('.unavailable').show();
}
}); });
} }
static setNewBranchButtonState(isPending, canCreate) {
if (Issue.$btnNewBranch.length === 0) {
return;
}
Issue.$btnNewBranch.find('.available').toggle(!isPending && canCreate);
Issue.$btnNewBranch.find('.unavailable').toggle(!isPending && !canCreate);
}
} }
export default Issue; export default Issue;
/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-param-reassign, no-cond-assign, quotes, one-var, one-var-declaration-per-line, operator-assignment, no-else-return, prefer-template, prefer-arrow-callback, no-empty, max-len, consistent-return, no-unused-vars, no-return-assign, max-len */ /* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-param-reassign, no-cond-assign, quotes, one-var, one-var-declaration-per-line, operator-assignment, no-else-return, prefer-template, prefer-arrow-callback, no-empty, max-len, consistent-return, no-unused-vars, no-return-assign, max-len, vars-on-top */
require('vendor/latinise'); require('vendor/latinise');
(function() { var base;
(function(w) { var w = window;
var base; if (w.gl == null) {
if (w.gl == null) {
w.gl = {}; w.gl = {};
} }
if ((base = w.gl).text == null) { if ((base = w.gl).text == null) {
base.text = {}; base.text = {};
} }
gl.text.addDelimiter = function(text) { gl.text.addDelimiter = function(text) {
return text ? text.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") : text; return text ? text.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") : text;
}; };
gl.text.highCountTrim = function(count) { gl.text.highCountTrim = function(count) {
return count > 99 ? '99+' : count; return count > 99 ? '99+' : count;
}; };
gl.text.randomString = function() { gl.text.randomString = function() {
return Math.random().toString(36).substring(7); return Math.random().toString(36).substring(7);
}; };
gl.text.replaceRange = function(s, start, end, substitute) { gl.text.replaceRange = function(s, start, end, substitute) {
return s.substring(0, start) + substitute + s.substring(end); return s.substring(0, start) + substitute + s.substring(end);
}; };
gl.text.getTextWidth = function(text, font) { gl.text.getTextWidth = function(text, font) {
/** /**
* Uses canvas.measureText to compute and return the width of the given text of given font in pixels. * Uses canvas.measureText to compute and return the width of the given text of given font in pixels.
* *
...@@ -37,19 +35,19 @@ require('vendor/latinise'); ...@@ -37,19 +35,19 @@ require('vendor/latinise');
var context = canvas.getContext('2d'); var context = canvas.getContext('2d');
context.font = font; context.font = font;
return context.measureText(text).width; return context.measureText(text).width;
}; };
gl.text.selectedText = function(text, textarea) { gl.text.selectedText = function(text, textarea) {
return text.substring(textarea.selectionStart, textarea.selectionEnd); return text.substring(textarea.selectionStart, textarea.selectionEnd);
}; };
gl.text.lineBefore = function(text, textarea) { gl.text.lineBefore = function(text, textarea) {
var split; var split;
split = text.substring(0, textarea.selectionStart).trim().split('\n'); split = text.substring(0, textarea.selectionStart).trim().split('\n');
return split[split.length - 1]; return split[split.length - 1];
}; };
gl.text.lineAfter = function(text, textarea) { gl.text.lineAfter = function(text, textarea) {
return text.substring(textarea.selectionEnd).trim().split('\n')[0]; return text.substring(textarea.selectionEnd).trim().split('\n')[0];
}; };
gl.text.blockTagText = function(text, textArea, blockTag, selected) { gl.text.blockTagText = function(text, textArea, blockTag, selected) {
var lineAfter, lineBefore; var lineAfter, lineBefore;
lineBefore = this.lineBefore(text, textArea); lineBefore = this.lineBefore(text, textArea);
lineAfter = this.lineAfter(text, textArea); lineAfter = this.lineAfter(text, textArea);
...@@ -63,8 +61,8 @@ require('vendor/latinise'); ...@@ -63,8 +61,8 @@ require('vendor/latinise');
} else { } else {
return blockTag + "\n" + selected + "\n" + blockTag; return blockTag + "\n" + selected + "\n" + blockTag;
} }
}; };
gl.text.insertText = function(textArea, text, tag, blockTag, selected, wrap) { gl.text.insertText = function(textArea, text, tag, blockTag, selected, wrap) {
var insertText, inserted, selectedSplit, startChar, removedLastNewLine, removedFirstNewLine, currentLineEmpty, lastNewLine; var insertText, inserted, selectedSplit, startChar, removedLastNewLine, removedFirstNewLine, currentLineEmpty, lastNewLine;
removedLastNewLine = false; removedLastNewLine = false;
removedFirstNewLine = false; removedFirstNewLine = false;
...@@ -132,8 +130,8 @@ require('vendor/latinise'); ...@@ -132,8 +130,8 @@ require('vendor/latinise');
} catch (error) {} } catch (error) {}
} }
return this.moveCursor(textArea, tag, wrap, removedLastNewLine); return this.moveCursor(textArea, tag, wrap, removedLastNewLine);
}; };
gl.text.moveCursor = function(textArea, tag, wrapped, removedLastNewLine) { gl.text.moveCursor = function(textArea, tag, wrapped, removedLastNewLine) {
var pos; var pos;
if (!textArea.setSelectionRange) { if (!textArea.setSelectionRange) {
return; return;
...@@ -151,8 +149,8 @@ require('vendor/latinise'); ...@@ -151,8 +149,8 @@ require('vendor/latinise');
return textArea.setSelectionRange(pos, pos); return textArea.setSelectionRange(pos, pos);
} }
}; };
gl.text.updateText = function(textArea, tag, blockTag, wrap) { gl.text.updateText = function(textArea, tag, blockTag, wrap) {
var $textArea, selected, text; var $textArea, selected, text;
$textArea = $(textArea); $textArea = $(textArea);
textArea = $textArea.get(0); textArea = $textArea.get(0);
...@@ -160,8 +158,8 @@ require('vendor/latinise'); ...@@ -160,8 +158,8 @@ require('vendor/latinise');
selected = this.selectedText(text, textArea); selected = this.selectedText(text, textArea);
$textArea.focus(); $textArea.focus();
return this.insertText(textArea, text, tag, blockTag, selected, wrap); return this.insertText(textArea, text, tag, blockTag, selected, wrap);
}; };
gl.text.init = function(form) { gl.text.init = function(form) {
var self; var self;
self = this; self = this;
return $('.js-md', form).off('click').on('click', function() { return $('.js-md', form).off('click').on('click', function() {
...@@ -169,24 +167,22 @@ require('vendor/latinise'); ...@@ -169,24 +167,22 @@ require('vendor/latinise');
$this = $(this); $this = $(this);
return self.updateText($this.closest('.md-area').find('textarea'), $this.data('md-tag'), $this.data('md-block'), !$this.data('md-prepend')); return self.updateText($this.closest('.md-area').find('textarea'), $this.data('md-tag'), $this.data('md-block'), !$this.data('md-prepend'));
}); });
}; };
gl.text.removeListeners = function(form) { gl.text.removeListeners = function(form) {
return $('.js-md', form).off(); return $('.js-md', form).off();
}; };
gl.text.humanize = function(string) { gl.text.humanize = function(string) {
return string.charAt(0).toUpperCase() + string.replace(/_/g, ' ').slice(1); return string.charAt(0).toUpperCase() + string.replace(/_/g, ' ').slice(1);
}; };
gl.text.pluralize = function(str, count) { gl.text.pluralize = function(str, count) {
return str + (count > 1 || count === 0 ? 's' : ''); return str + (count > 1 || count === 0 ? 's' : '');
}; };
gl.text.truncate = function(string, maxLength) { gl.text.truncate = function(string, maxLength) {
return string.substr(0, (maxLength - 3)) + '...'; return string.substr(0, (maxLength - 3)) + '...';
}; };
gl.text.dasherize = function(str) { gl.text.dasherize = function(str) {
return str.replace(/[_\s]+/g, '-'); return str.replace(/[_\s]+/g, '-');
}; };
gl.text.slugify = function(str) { gl.text.slugify = function(str) {
return str.trim().toLowerCase().latinise(); return str.trim().toLowerCase().latinise();
}; };
})(window);
}).call(window);
/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-param-reassign, no-cond-assign, one-var, one-var-declaration-per-line, no-void, guard-for-in, no-restricted-syntax, prefer-template, quotes, max-len */ /* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-param-reassign, no-cond-assign, one-var, one-var-declaration-per-line, no-void, guard-for-in, no-restricted-syntax, prefer-template, quotes, max-len */
(function() { var base;
(function(w) { var w = window;
var base; if (w.gl == null) {
if (w.gl == null) {
w.gl = {}; w.gl = {};
} }
if ((base = w.gl).utils == null) { if ((base = w.gl).utils == null) {
base.utils = {}; base.utils = {};
} }
// Returns an array containing the value(s) of the // Returns an array containing the value(s) of the
// of the key passed as an argument // of the key passed as an argument
w.gl.utils.getParameterValues = function(sParam) { w.gl.utils.getParameterValues = function(sParam) {
var i, sPageURL, sParameterName, sURLVariables, values; var i, sPageURL, sParameterName, sURLVariables, values;
sPageURL = decodeURIComponent(window.location.search.substring(1)); sPageURL = decodeURIComponent(window.location.search.substring(1));
sURLVariables = sPageURL.split('&'); sURLVariables = sPageURL.split('&');
...@@ -25,10 +24,10 @@ ...@@ -25,10 +24,10 @@
i += 1; i += 1;
} }
return values; return values;
}; };
// @param {Object} params - url keys and value to merge // @param {Object} params - url keys and value to merge
// @param {String} url // @param {String} url
w.gl.utils.mergeUrlParams = function(params, url) { w.gl.utils.mergeUrlParams = function(params, url) {
var lastChar, newUrl, paramName, paramValue, pattern; var lastChar, newUrl, paramName, paramValue, pattern;
newUrl = decodeURIComponent(url); newUrl = decodeURIComponent(url);
for (paramName in params) { for (paramName in params) {
...@@ -48,9 +47,9 @@ ...@@ -48,9 +47,9 @@
newUrl = newUrl.slice(0, -1); newUrl = newUrl.slice(0, -1);
} }
return newUrl; return newUrl;
}; };
// removes parameter query string from url. returns the modified url // removes parameter query string from url. returns the modified url
w.gl.utils.removeParamQueryString = function(url, param) { w.gl.utils.removeParamQueryString = function(url, param) {
var urlVariables, variables; var urlVariables, variables;
url = decodeURIComponent(url); url = decodeURIComponent(url);
urlVariables = url.split('&'); urlVariables = url.split('&');
...@@ -65,15 +64,15 @@ ...@@ -65,15 +64,15 @@
} }
return results; return results;
})()).join('&'); })()).join('&');
}; };
w.gl.utils.removeParams = (params) => { w.gl.utils.removeParams = (params) => {
const url = new URL(window.location.href); const url = new URL(window.location.href);
params.forEach((param) => { params.forEach((param) => {
url.search = w.gl.utils.removeParamQueryString(url.search, param); url.search = w.gl.utils.removeParamQueryString(url.search, param);
}); });
return url.href; return url.href;
}; };
w.gl.utils.getLocationHash = function(url) { w.gl.utils.getLocationHash = function(url) {
var hashIndex; var hashIndex;
if (typeof url === 'undefined') { if (typeof url === 'undefined') {
// Note: We can't use window.location.hash here because it's // Note: We can't use window.location.hash here because it's
...@@ -82,12 +81,10 @@ ...@@ -82,12 +81,10 @@
} }
hashIndex = url.indexOf('#'); hashIndex = url.indexOf('#');
return hashIndex === -1 ? null : url.substring(hashIndex + 1); return hashIndex === -1 ? null : url.substring(hashIndex + 1);
}; };
w.gl.utils.refreshCurrentPage = () => gl.utils.visitUrl(document.location.href); w.gl.utils.refreshCurrentPage = () => gl.utils.visitUrl(document.location.href);
w.gl.utils.visitUrl = (url) => { w.gl.utils.visitUrl = (url) => {
document.location.href = url; document.location.href = url;
}; };
})(window);
}).call(window);
/* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, max-len */ /* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, max-len */
(function() { import '~/lib/utils/url_utility';
var bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; };
(function() {
this.MergedButtons = (function() { this.MergedButtons = (function() {
function MergedButtons() { function MergedButtons() {
this.removeSourceBranch = bind(this.removeSourceBranch, this); this.removeSourceBranch = this.removeSourceBranch.bind(this);
this.removeBranchSuccess = this.removeBranchSuccess.bind(this);
this.removeBranchError = this.removeBranchError.bind(this);
this.$removeBranchWidget = $('.remove_source_branch_widget'); this.$removeBranchWidget = $('.remove_source_branch_widget');
this.$removeBranchProgress = $('.remove_source_branch_in_progress'); this.$removeBranchProgress = $('.remove_source_branch_in_progress');
this.$removeBranchFailed = $('.remove_source_branch_widget.failed'); this.$removeBranchFailed = $('.remove_source_branch_widget.failed');
...@@ -22,7 +24,7 @@ ...@@ -22,7 +24,7 @@
MergedButtons.prototype.initEventListeners = function() { MergedButtons.prototype.initEventListeners = function() {
$(document).on('click', '.remove_source_branch', this.removeSourceBranch); $(document).on('click', '.remove_source_branch', this.removeSourceBranch);
$(document).on('ajax:success', '.remove_source_branch', this.removeBranchSuccess); $(document).on('ajax:success', '.remove_source_branch', this.removeBranchSuccess);
return $(document).on('ajax:error', '.remove_source_branch', this.removeBranchError); $(document).on('ajax:error', '.remove_source_branch', this.removeBranchError);
}; };
MergedButtons.prototype.removeSourceBranch = function() { MergedButtons.prototype.removeSourceBranch = function() {
...@@ -31,7 +33,7 @@ ...@@ -31,7 +33,7 @@
}; };
MergedButtons.prototype.removeBranchSuccess = function() { MergedButtons.prototype.removeBranchSuccess = function() {
return location.reload(); gl.utils.refreshCurrentPage();
}; };
MergedButtons.prototype.removeBranchError = function() { MergedButtons.prototype.removeBranchError = function() {
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
direction: rtl; direction: rtl;
@media (min-width: $screen-sm-min) and (max-width: $screen-md-max) { @media (min-width: $screen-sm-min) and (max-width: $screen-md-max) {
overflow-x: scroll; overflow-x: auto;
} }
} }
......
...@@ -82,7 +82,7 @@ ...@@ -82,7 +82,7 @@
.input-token:last-child { .input-token:last-child {
flex: 1; flex: 1;
-webkit-flex: 1; -webkit-flex: 1;
max-width: initial; max-width: inherit;
} }
} }
...@@ -246,17 +246,17 @@ ...@@ -246,17 +246,17 @@
} }
} }
.filtered-search-history-dropdown-toggle-button { .filtered-search-history-dropdown-wrapper {
position: static;
display: flex; display: flex;
align-items: center; flex-direction: column;
}
.filtered-search-history-dropdown-toggle-button {
flex: 1;
width: auto; width: auto;
height: 100%; padding-right: 10px;
padding-top: 0;
padding-left: 0.75em;
padding-bottom: 0;
padding-right: 0.5em;
background-color: transparent;
border-radius: 0; border-radius: 0;
border-top: 0; border-top: 0;
border-left: 0; border-left: 0;
...@@ -264,6 +264,7 @@ ...@@ -264,6 +264,7 @@
border-right: 1px solid $border-color; border-right: 1px solid $border-color;
color: $gl-text-color-secondary; color: $gl-text-color-secondary;
line-height: 1;
transition: color 0.1s linear; transition: color 0.1s linear;
...@@ -275,24 +276,21 @@ ...@@ -275,24 +276,21 @@
} }
.dropdown-toggle-text { .dropdown-toggle-text {
display: inline-block;
color: inherit; color: inherit;
.fa { .fa {
vertical-align: middle;
color: inherit; color: inherit;
} }
} }
.fa { .fa {
position: initial; position: static;
} }
} }
.filtered-search-history-dropdown-wrapper {
position: initial;
flex-shrink: 0;
}
.filtered-search-history-dropdown { .filtered-search-history-dropdown {
width: 40%; width: 40%;
......
...@@ -158,6 +158,7 @@ ...@@ -158,6 +158,7 @@
li.task-list-item { li.task-list-item {
list-style-type: none; list-style-type: none;
position: relative; position: relative;
min-height: 22px;
padding-left: 28px; padding-left: 28px;
margin-left: 0 !important; margin-left: 0 !important;
......
...@@ -26,6 +26,7 @@ $gray-dark: darken($gray-light, $darken-dark-factor); ...@@ -26,6 +26,7 @@ $gray-dark: darken($gray-light, $darken-dark-factor);
$gray-darker: #eee; $gray-darker: #eee;
$gray-darkest: #c4c4c4; $gray-darkest: #c4c4c4;
$green-25: #f6fcf8;
$green-50: #e4f5eb; $green-50: #e4f5eb;
$green-100: #bae6cc; $green-100: #bae6cc;
$green-200: #8dd5aa; $green-200: #8dd5aa;
...@@ -37,6 +38,7 @@ $green-700: #12753a; ...@@ -37,6 +38,7 @@ $green-700: #12753a;
$green-800: #0e5a2d; $green-800: #0e5a2d;
$green-900: #0a4020; $green-900: #0a4020;
$blue-25: #f6fafd;
$blue-50: #e4eff9; $blue-50: #e4eff9;
$blue-100: #bcd7f1; $blue-100: #bcd7f1;
$blue-200: #8fbce8; $blue-200: #8fbce8;
...@@ -48,6 +50,7 @@ $blue-700: #17599c; ...@@ -48,6 +50,7 @@ $blue-700: #17599c;
$blue-800: #134a81; $blue-800: #134a81;
$blue-900: #0f3b66; $blue-900: #0f3b66;
$orange-25: #fffcf8;
$orange-50: #fff2e1; $orange-50: #fff2e1;
$orange-100: #fedfb3; $orange-100: #fedfb3;
$orange-200: #feca81; $orange-200: #feca81;
...@@ -59,6 +62,7 @@ $orange-700: #c26700; ...@@ -59,6 +62,7 @@ $orange-700: #c26700;
$orange-800: #a35100; $orange-800: #a35100;
$orange-900: #853b00; $orange-900: #853b00;
$red-25: #fef7f6;
$red-50: #fbe7e4; $red-50: #fbe7e4;
$red-100: #f4c4bc; $red-100: #f4c4bc;
$red-200: #ed9d90; $red-200: #ed9d90;
...@@ -147,7 +151,7 @@ $gl-sidebar-padding: 22px; ...@@ -147,7 +151,7 @@ $gl-sidebar-padding: 22px;
/* /*
* Misc * Misc
*/ */
$row-hover: lighten($blue-50, 2%); $row-hover: $blue-25;
$row-hover-border: $blue-100; $row-hover-border: $blue-100;
$progress-color: #c0392b; $progress-color: #c0392b;
$header-height: 50px; $header-height: 50px;
...@@ -223,18 +227,18 @@ $gl-btn-active-gradient: inset 0 2px 3px $gl-btn-active-background; ...@@ -223,18 +227,18 @@ $gl-btn-active-gradient: inset 0 2px 3px $gl-btn-active-background;
/* /*
* Commit Diff Colors * Commit Diff Colors
*/ */
$added: $green-300; $added: #63c363;
$deleted: $red-300; $deleted: #f77;
$line-added: $green-50; $line-added: #ecfdf0;
$line-added-dark: $green-100; $line-added-dark: #c7f0d2;
$line-removed: $red-50; $line-removed: #fbe9eb;
$line-removed-dark: $red-100; $line-removed-dark: #fac5cd;
$line-number-old: lighten($red-100, 5%); $line-number-old: #f9d7dc;
$line-number-new: lighten($green-100, 5%); $line-number-new: #ddfbe6;
$line-number-select: lighten($orange-100, 5%); $line-number-select: #fbf2da;
$line-target-blue: $blue-50; $line-target-blue: #f6faff;
$line-select-yellow: $orange-50; $line-select-yellow: #fcf8e7;
$line-select-yellow-dark: $orange-100; $line-select-yellow-dark: #f0e2bd;
$dark-diff-match-bg: rgba(255, 255, 255, 0.3); $dark-diff-match-bg: rgba(255, 255, 255, 0.3);
$dark-diff-match-color: rgba(255, 255, 255, 0.1); $dark-diff-match-color: rgba(255, 255, 255, 0.1);
$file-mode-changed: #777; $file-mode-changed: #777;
......
...@@ -39,7 +39,7 @@ ...@@ -39,7 +39,7 @@
overflow-y: hidden; overflow-y: hidden;
font-size: 12px; font-size: 12px;
.fa-refresh { .fa-spinner {
font-size: 24px; font-size: 24px;
margin-left: 20px; margin-left: 20px;
} }
...@@ -219,7 +219,7 @@ ...@@ -219,7 +219,7 @@
font-size: 12px; font-size: 12px;
position: relative; position: relative;
.fa-refresh { .fa-spinner {
font-size: 24px; font-size: 24px;
} }
...@@ -366,7 +366,7 @@ ...@@ -366,7 +366,7 @@
background-color: $row-hover; background-color: $row-hover;
} }
.fa-refresh { .fa-spinner {
font-size: 13px; font-size: 13px;
margin-left: 3px; margin-left: 3px;
} }
......
...@@ -10,10 +10,14 @@ ...@@ -10,10 +10,14 @@
position: relative; position: relative;
&.event-inline { &.event-inline {
.profile-icon { .system-note-image {
top: 20px; top: 20px;
} }
.user-avatar {
top: 14px;
}
.event-title, .event-title,
.event-item-timestamp { .event-item-timestamp {
line-height: 40px; line-height: 40px;
...@@ -24,7 +28,7 @@ ...@@ -24,7 +28,7 @@
color: $gl-text-color; color: $gl-text-color;
} }
.profile-icon { .system-note-image {
position: absolute; position: absolute;
left: 0; left: 0;
top: 14px; top: 14px;
...@@ -35,15 +39,18 @@ ...@@ -35,15 +39,18 @@
fill: $gl-text-color-secondary; fill: $gl-text-color-secondary;
} }
&.open-icon svg { &.opened-icon,
&.created-icon {
svg {
fill: $green-300; fill: $green-300;
} }
}
&.closed-icon svg { &.closed-icon svg {
fill: $red-300; fill: $red-300;
} }
&.fork-icon svg { &.accepted-icon svg {
fill: $blue-300; fill: $blue-300;
} }
} }
...@@ -128,8 +135,7 @@ ...@@ -128,8 +135,7 @@
li { li {
&.commit { &.commit {
background: transparent; background: transparent;
padding: 3px; padding: 0;
padding-left: 0;
border: none; border: none;
.commit-row-title { .commit-row-title {
...@@ -183,7 +189,7 @@ ...@@ -183,7 +189,7 @@
max-width: 100%; max-width: 100%;
} }
.profile-icon { .system-note-image {
display: none; display: none;
} }
......
...@@ -18,12 +18,12 @@ ul.notes { ...@@ -18,12 +18,12 @@ ul.notes {
float: left; float: left;
svg { svg {
width: 18px; width: 16px;
height: 18px; height: 16px;
fill: $gray-darkest; fill: $gray-darkest;
position: absolute; position: absolute;
left: 30px; left: 0;
top: 15px; top: 16px;
} }
} }
...@@ -144,6 +144,10 @@ ul.notes { ...@@ -144,6 +144,10 @@ ul.notes {
padding: 0; padding: 0;
clear: both; clear: both;
@media (min-width: $screen-sm-min) {
margin-left: 65px;
}
&.timeline-entry::after { &.timeline-entry::after {
clear: none; clear: none;
} }
...@@ -172,6 +176,10 @@ ul.notes { ...@@ -172,6 +176,10 @@ ul.notes {
.timeline-content { .timeline-content {
padding: 14px 10px; padding: 14px 10px;
@media (min-width: $screen-sm-min) {
margin-left: 20px;
}
} }
.note-header { .note-header {
...@@ -619,7 +627,6 @@ ul.notes { ...@@ -619,7 +627,6 @@ ul.notes {
} }
&:not(.is-disabled):hover, &:not(.is-disabled):hover,
&:not(.is-disabled):focus,
&.is-active { &.is-active {
color: $gl-text-green; color: $gl-text-green;
...@@ -633,6 +640,11 @@ ul.notes { ...@@ -633,6 +640,11 @@ ul.notes {
height: 15px; height: 15px;
width: 15px; width: 15px;
} }
.loading {
margin: 0;
height: auto;
}
} }
.discussion-next-btn { .discussion-next-btn {
......
...@@ -49,7 +49,7 @@ class Projects::HooksController < Projects::ApplicationController ...@@ -49,7 +49,7 @@ class Projects::HooksController < Projects::ApplicationController
def hook_params def hook_params
params.require(:hook).permit( params.require(:hook).permit(
:build_events, :job_events,
:pipeline_events, :pipeline_events,
:enable_ssl_verification, :enable_ssl_verification,
:issues_events, :issues_events,
......
...@@ -38,7 +38,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -38,7 +38,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@collection_type = "MergeRequest" @collection_type = "MergeRequest"
@merge_requests = merge_requests_collection @merge_requests = merge_requests_collection
@merge_requests = @merge_requests.page(params[:page]) @merge_requests = @merge_requests.page(params[:page])
@merge_requests = @merge_requests.includes(merge_request_diff: :merge_request) @merge_requests = @merge_requests.preload(merge_request_diff: :merge_request)
@issuable_meta_data = issuable_meta_data(@merge_requests, @collection_type) @issuable_meta_data = issuable_meta_data(@merge_requests, @collection_type)
if @merge_requests.out_of_range? && @merge_requests.total_pages != 0 if @merge_requests.out_of_range? && @merge_requests.total_pages != 0
......
...@@ -60,7 +60,7 @@ class RegistrationsController < Devise::RegistrationsController ...@@ -60,7 +60,7 @@ class RegistrationsController < Devise::RegistrationsController
end end
def resource def resource
@resource ||= Users::CreateService.new(current_user, sign_up_params).build @resource ||= Users::BuildService.new(current_user, sign_up_params).execute
end end
def devise_mapping def devise_mapping
......
module EventsHelper module EventsHelper
ICON_NAMES_BY_EVENT_TYPE = {
'pushed to' => 'icon_commit',
'pushed new' => 'icon_commit',
'created' => 'icon_status_open',
'opened' => 'icon_status_open',
'closed' => 'icon_status_closed',
'accepted' => 'icon_code_fork',
'commented on' => 'icon_comment_o',
'deleted' => 'icon_trash_o'
}.freeze
def link_to_author(event) def link_to_author(event)
author = event.author author = event.author
...@@ -183,4 +194,21 @@ module EventsHelper ...@@ -183,4 +194,21 @@ module EventsHelper
"event-inline" "event-inline"
end end
end end
def icon_for_event(note)
icon_name = ICON_NAMES_BY_EVENT_TYPE[note]
custom_icon(icon_name) if icon_name
end
def icon_for_profile_event(event)
if current_path?('users#show')
content_tag :div, class: "system-note-image #{event.action_name.parameterize}-icon" do
icon_for_event(event.action_name)
end
else
content_tag :div, class: 'system-note-image user-avatar' do
author_avatar(event, size: 32)
end
end
end
end end
...@@ -3,7 +3,8 @@ module JavascriptHelper ...@@ -3,7 +3,8 @@ module JavascriptHelper
javascript_include_tag asset_path(js) javascript_include_tag asset_path(js)
end end
def page_specific_javascript_bundle_tag(js) # deprecated; use webpack_bundle_tag directly instead
javascript_include_tag(*webpack_asset_paths(js)) def page_specific_javascript_bundle_tag(bundle)
webpack_bundle_tag(bundle)
end end
end end
require 'webpack/rails/manifest'
module WebpackHelper
def webpack_bundle_tag(bundle)
javascript_include_tag(*gitlab_webpack_asset_paths(bundle))
end
# override webpack-rails gem helper until changes can make it upstream
def gitlab_webpack_asset_paths(source, extension: nil)
return "" unless source.present?
paths = Webpack::Rails::Manifest.asset_paths(source)
if extension
paths = paths.select { |p| p.ends_with? ".#{extension}" }
end
# include full webpack-dev-server url for rspec tests running locally
if Rails.env.test? && Rails.configuration.webpack.dev_server.enabled
host = Rails.configuration.webpack.dev_server.host
port = Rails.configuration.webpack.dev_server.port
protocol = Rails.configuration.webpack.dev_server.https ? 'https' : 'http'
paths.map! do |p|
"#{protocol}://#{host}:#{port}#{p}"
end
end
paths
end
end
...@@ -326,14 +326,13 @@ class Commit ...@@ -326,14 +326,13 @@ class Commit
end end
def raw_diffs(*args) def raw_diffs(*args)
use_gitaly = Gitlab::GitalyClient.feature_enabled?(:commit_raw_diffs) # NOTE: This feature is intentionally disabled until
deltas_only = args.last.is_a?(Hash) && args.last[:deltas_only] # https://gitlab.com/gitlab-org/gitaly/issues/178 is resolved
# if Gitlab::GitalyClient.feature_enabled?(:commit_raw_diffs)
if use_gitaly && !deltas_only # Gitlab::GitalyClient::Commit.diff_from_parent(self, *args)
Gitlab::GitalyClient::Commit.diff_from_parent(self, *args) # else
else
raw.diffs(*args) raw.diffs(*args)
end # end
end end
def diffs(diff_options = nil) def diffs(diff_options = nil)
......
...@@ -2,9 +2,9 @@ ...@@ -2,9 +2,9 @@
module DiscussionOnDiff module DiscussionOnDiff
extend ActiveSupport::Concern extend ActiveSupport::Concern
included do
NUMBER_OF_TRUNCATED_DIFF_LINES = 16 NUMBER_OF_TRUNCATED_DIFF_LINES = 16
included do
delegate :line_code, delegate :line_code,
:original_line_code, :original_line_code,
:diff_file, :diff_file,
......
...@@ -20,7 +20,8 @@ class ContainerRepository < ActiveRecord::Base ...@@ -20,7 +20,8 @@ class ContainerRepository < ActiveRecord::Base
end end
def path def path
@path ||= [project.full_path, name].select(&:present?).join('/') @path ||= [project.full_path, name]
.select(&:present?).join('/').downcase
end end
def location def location
......
...@@ -21,6 +21,8 @@ class Label < ActiveRecord::Base ...@@ -21,6 +21,8 @@ class Label < ActiveRecord::Base
has_many :issues, through: :label_links, source: :target, source_type: 'Issue' has_many :issues, through: :label_links, source: :target, source_type: 'Issue'
has_many :merge_requests, through: :label_links, source: :target, source_type: 'MergeRequest' has_many :merge_requests, through: :label_links, source: :target, source_type: 'MergeRequest'
before_validation :strip_whitespace_from_title_and_color
validates :color, color: true, allow_blank: false validates :color, color: true, allow_blank: false
# Don't allow ',' for label titles # Don't allow ',' for label titles
...@@ -193,4 +195,8 @@ class Label < ActiveRecord::Base ...@@ -193,4 +195,8 @@ class Label < ActiveRecord::Base
def sanitize_title(value) def sanitize_title(value)
CGI.unescapeHTML(Sanitize.clean(value.to_s)) CGI.unescapeHTML(Sanitize.clean(value.to_s))
end end
def strip_whitespace_from_title_and_color
%w(color title).each { |attr| self[attr] = self[attr]&.strip }
end
end end
...@@ -96,6 +96,7 @@ class Note < ActiveRecord::Base ...@@ -96,6 +96,7 @@ class Note < ActiveRecord::Base
before_validation :set_discussion_id, on: :create before_validation :set_discussion_id, on: :create
after_save :keep_around_commit, unless: :for_personal_snippet? after_save :keep_around_commit, unless: :for_personal_snippet?
after_save :expire_etag_cache after_save :expire_etag_cache
after_destroy :expire_etag_cache
class << self class << self
def model_name def model_name
......
...@@ -963,13 +963,15 @@ class Repository ...@@ -963,13 +963,15 @@ class Repository
end end
def is_ancestor?(ancestor_id, descendant_id) def is_ancestor?(ancestor_id, descendant_id)
Gitlab::GitalyClient.migrate(:is_ancestor) do |is_enabled| # NOTE: This feature is intentionally disabled until
if is_enabled # https://gitlab.com/gitlab-org/gitlab-ce/issues/30586 is resolved
raw_repository.is_ancestor?(ancestor_id, descendant_id) # Gitlab::GitalyClient.migrate(:is_ancestor) do |is_enabled|
else # if is_enabled
# raw_repository.is_ancestor?(ancestor_id, descendant_id)
# else
merge_base_commit(ancestor_id, descendant_id) == ancestor_id merge_base_commit(ancestor_id, descendant_id) == ancestor_id
end # end
end # end
end end
def empty_repo? def empty_repo?
......
...@@ -28,6 +28,7 @@ class GroupPolicy < BasePolicy ...@@ -28,6 +28,7 @@ class GroupPolicy < BasePolicy
can! :admin_namespace can! :admin_namespace
can! :admin_group_member can! :admin_group_member
can! :change_visibility_level can! :change_visibility_level
can! :create_subgroup if @user.can_create_group
end end
if globally_viewable && @subject.request_access_enabled && !member if globally_viewable && @subject.request_access_enabled && !member
......
...@@ -58,6 +58,9 @@ module Projects ...@@ -58,6 +58,9 @@ module Projects
fail(error: @project.errors.full_messages.join(', ')) fail(error: @project.errors.full_messages.join(', '))
end end
@project @project
rescue ActiveRecord::RecordInvalid => e
message = "Unable to save #{e.record.type}: #{e.record.errors.full_messages.join(", ")} "
fail(error: message)
rescue => e rescue => e
fail(error: e.message) fail(error: e.message)
end end
......
module Users
# Service for building a new user.
class BuildService < BaseService
def initialize(current_user, params = {})
@current_user = current_user
@params = params.dup
end
def execute
raise Gitlab::Access::AccessDeniedError unless can_create_user?
user = User.new(build_user_params)
if current_user&.admin?
if params[:reset_password]
user.generate_reset_token
params[:force_random_password] = true
end
if params[:force_random_password]
random_password = Devise.friendly_token.first(Devise.password_length.min)
user.password = user.password_confirmation = random_password
end
end
identity_attrs = params.slice(:extern_uid, :provider)
if identity_attrs.any?
user.identities.build(identity_attrs)
end
user
end
private
def can_create_user?
(current_user.nil? && current_application_settings.signup_enabled?) || current_user&.admin?
end
# Allowed params for creating a user (admins only)
def admin_create_params
[
:access_level,
:admin,
:avatar,
:bio,
:can_create_group,
:color_scheme_id,
:email,
:external,
:force_random_password,
:hide_no_password,
:hide_no_ssh_key,
:key_id,
:linkedin,
:name,
:password,
:password_automatically_set,
:password_expires_at,
:projects_limit,
:remember_me,
:skip_confirmation,
:skype,
:theme_id,
:twitter,
:username,
:website_url
]
end
# Allowed params for user signup
def signup_params
[
:email,
:email_confirmation,
:password_automatically_set,
:name,
:password,
:username
]
end
def build_user_params
if current_user&.admin?
user_params = params.slice(*admin_create_params)
user_params[:created_by_id] = current_user&.id
if params[:reset_password]
user_params.merge!(force_random_password: true, password_expires_at: nil)
end
else
user_params = params.slice(*signup_params)
user_params[:skip_confirmation] = !current_application_settings.send_user_confirmation_email
end
user_params
end
end
end
...@@ -6,34 +6,10 @@ module Users ...@@ -6,34 +6,10 @@ module Users
@params = params.dup @params = params.dup
end end
def build
raise Gitlab::Access::AccessDeniedError unless can_create_user?
user = User.new(build_user_params)
if current_user&.admin?
if params[:reset_password]
@reset_token = user.generate_reset_token
params[:force_random_password] = true
end
if params[:force_random_password]
random_password = Devise.friendly_token.first(Devise.password_length.min)
user.password = user.password_confirmation = random_password
end
end
identity_attrs = params.slice(:extern_uid, :provider)
if identity_attrs.any?
user.identities.build(identity_attrs)
end
user
end
def execute def execute
user = build user = Users::BuildService.new(current_user, params).execute
@reset_token = user.generate_reset_token if user.recently_sent_password_reset?
if user.save if user.save
log_info("User \"#{user.name}\" (#{user.email}) was created") log_info("User \"#{user.name}\" (#{user.email}) was created")
...@@ -43,70 +19,5 @@ module Users ...@@ -43,70 +19,5 @@ module Users
user user
end end
private
def can_create_user?
(current_user.nil? && current_application_settings.signup_enabled?) || current_user&.admin?
end
# Allowed params for creating a user (admins only)
def admin_create_params
[
:access_level,
:admin,
:avatar,
:bio,
:can_create_group,
:color_scheme_id,
:email,
:external,
:force_random_password,
:password_automatically_set,
:hide_no_password,
:hide_no_ssh_key,
:key_id,
:linkedin,
:name,
:password,
:password_expires_at,
:projects_limit,
:remember_me,
:skip_confirmation,
:skype,
:theme_id,
:twitter,
:username,
:website_url
]
end
# Allowed params for user signup
def signup_params
[
:email,
:email_confirmation,
:password_automatically_set,
:name,
:password,
:username
]
end
def build_user_params
if current_user&.admin?
user_params = params.slice(*admin_create_params)
user_params[:created_by_id] = current_user&.id
if params[:reset_password]
user_params.merge!(force_random_password: true, password_expires_at: nil)
end
else
user_params = params.slice(*signup_params)
user_params[:skip_confirmation] = !current_application_settings.send_user_confirmation_email
end
user_params
end
end end
end end
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
= button_to reset_health_check_token_admin_application_settings_path, = button_to reset_health_check_token_admin_application_settings_path,
method: :put, class: 'btn btn-default', method: :put, class: 'btn btn-default',
data: { confirm: 'Are you sure you want to reset the health check token?' } do data: { confirm: 'Are you sure you want to reset the health check token?' } do
= icon('refresh') = icon('spinner')
Reset health check access token Reset health check access token
%p.light %p.light
Health information can be retrieved as plain text, JSON, or XML using: Health information can be retrieved as plain text, JSON, or XML using:
......
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
= button_to reset_runners_token_admin_application_settings_path, = button_to reset_runners_token_admin_application_settings_path,
method: :put, class: 'btn btn-default', method: :put, class: 'btn btn-default',
data: { confirm: 'Are you sure you want to reset registration token?' } do data: { confirm: 'Are you sure you want to reset registration token?' } do
= icon('refresh') = icon('spinner')
Reset runners registration token Reset runners registration token
.bs-callout .bs-callout
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
- @services.sort_by(&:title).each do |service| - @services.sort_by(&:title).each do |service|
%tr %tr
%td %td
= icon("copy", class: 'clgray') = boolean_to_icon service.activated?
%td %td
= link_to edit_admin_application_settings_service_path(service.id) do = link_to edit_admin_application_settings_service_path(service.id) do
%strong= service.title %strong= service.title
......
- if event.target = icon_for_profile_event(event)
- if event.action_name == "opened"
.profile-icon.open-icon
= custom_icon("icon_status_open")
- elsif event.action_name == "closed"
.profile-icon.closed-icon
= custom_icon("icon_status_closed")
- else
.profile-icon.fork-icon
= custom_icon("icon_code_fork")
.event-title .event-title
%span.author_name= link_to_author event %span.author_name= link_to_author event
......
.profile-icon.open-icon = icon_for_profile_event(event)
= custom_icon("icon_status_open")
.event-title .event-title
%span.author_name= link_to_author event %span.author_name= link_to_author event
......
.profile-icon = icon_for_profile_event(event)
= custom_icon("icon_comment_o")
.event-title .event-title
%span.author_name= link_to_author event %span.author_name= link_to_author event
......
- project = event.project - project = event.project
.profile-icon = icon_for_profile_event(event)
- if event.action_name == "deleted"
= custom_icon("trash_o")
- else
= custom_icon("icon_commit")
.event-title .event-title
%span.author_name= link_to_author event %span.author_name= link_to_author event
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
.nav-controls .nav-controls
= form_tag request.path, method: :get do |f| = form_tag request.path, method: :get do |f|
= search_field_tag :filter_groups, params[:filter_groups], placeholder: 'Filter by name', class: 'form-control', spellcheck: false = search_field_tag :filter_groups, params[:filter_groups], placeholder: 'Filter by name', class: 'form-control', spellcheck: false
- if can? current_user, :admin_group, @group - if can?(current_user, :create_subgroup, @group)
= link_to new_group_path(parent_id: @group.id), class: 'btn btn-new pull-right' do = link_to new_group_path(parent_id: @group.id), class: 'btn btn-new pull-right' do
New Subgroup New Subgroup
......
...@@ -252,7 +252,7 @@ ...@@ -252,7 +252,7 @@
= icon('chevron-down') = icon('chevron-down')
.dropdown-menu.dropdown-select.dropdown-menu-selectable .dropdown-menu.dropdown-select.dropdown-menu-selectable
.dropdown-title .dropdown-title
%span Dropdown Title %span Dropdown title
%button.dropdown-title-button.dropdown-menu-close{ aria: { label: "Close" } } %button.dropdown-title-button.dropdown-menu-close{ aria: { label: "Close" } }
= icon('times') = icon('times')
.dropdown-input .dropdown-input
...@@ -291,7 +291,7 @@ ...@@ -291,7 +291,7 @@
= icon('chevron-down') = icon('chevron-down')
.dropdown-menu.dropdown-select.dropdown-menu-selectable.is-loading .dropdown-menu.dropdown-select.dropdown-menu-selectable.is-loading
.dropdown-title .dropdown-title
%span Dropdown Title %span Dropdown title
%button.dropdown-title-button.dropdown-menu-close{ aria: { label: "Close" } } %button.dropdown-title-button.dropdown-menu-close{ aria: { label: "Close" } }
= icon('times') = icon('times')
.dropdown-input .dropdown-input
...@@ -335,7 +335,7 @@ ...@@ -335,7 +335,7 @@
= icon('chevron-down') = icon('chevron-down')
.dropdown-menu.dropdown-select.dropdown-menu-selectable.dropdown-menu-user .dropdown-menu.dropdown-select.dropdown-menu-selectable.dropdown-menu-user
.dropdown-title .dropdown-title
%span Dropdown Title %span Dropdown title
%button.dropdown-title-button.dropdown-menu-close{ aria: { label: "Close" } } %button.dropdown-title-button.dropdown-menu-close{ aria: { label: "Close" } }
= icon('times') = icon('times')
.dropdown-input .dropdown-input
...@@ -362,7 +362,7 @@ ...@@ -362,7 +362,7 @@
.dropdown-title .dropdown-title
%button.dropdown-title-button.dropdown-menu-back{ aria: { label: "Go back" } } %button.dropdown-title-button.dropdown-menu-back{ aria: { label: "Go back" } }
= icon('arrow-left') = icon('arrow-left')
%span Dropdown Title %span Dropdown title
%button.dropdown-title-button.dropdown-menu-close{ aria: { label: "Close" } } %button.dropdown-title-button.dropdown-menu-close{ aria: { label: "Close" } }
= icon('times') = icon('times')
.dropdown-input .dropdown-input
......
...@@ -28,9 +28,9 @@ ...@@ -28,9 +28,9 @@
= stylesheet_link_tag "application", media: "all" = stylesheet_link_tag "application", media: "all"
= stylesheet_link_tag "print", media: "print" = stylesheet_link_tag "print", media: "print"
= javascript_include_tag(*webpack_asset_paths("runtime")) = webpack_bundle_tag "runtime"
= javascript_include_tag(*webpack_asset_paths("common")) = webpack_bundle_tag "common"
= javascript_include_tag(*webpack_asset_paths("main")) = webpack_bundle_tag "main"
- if content_for?(:page_specific_javascripts) - if content_for?(:page_specific_javascripts)
= yield :page_specific_javascripts = yield :page_specific_javascripts
......
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
Registry Registry
- if project_nav_tab? :issues - if project_nav_tab? :issues
= nav_link(controller: [:issues, :labels, :milestones, :boards]) do = nav_link(controller: @project.default_issues_tracker? ? [:issues, :labels, :milestones, :boards] : :issues) do
= link_to namespace_project_issues_path(@project.namespace, @project), title: 'Issues', class: 'shortcuts-issues' do = link_to namespace_project_issues_path(@project.namespace, @project), title: 'Issues', class: 'shortcuts-issues' do
%span %span
Issues Issues
...@@ -31,7 +31,7 @@ ...@@ -31,7 +31,7 @@
%span.badge.count.issue_counter= number_with_delimiter(IssuesFinder.new(current_user, project_id: @project.id).execute.opened.count) %span.badge.count.issue_counter= number_with_delimiter(IssuesFinder.new(current_user, project_id: @project.id).execute.opened.count)
- if project_nav_tab? :merge_requests - if project_nav_tab? :merge_requests
= nav_link(controller: :merge_requests) do = nav_link(controller: @project.default_issues_tracker? ? :merge_requests : [:merge_requests, :labels, :milestones]) do
= link_to namespace_project_merge_requests_path(@project.namespace, @project), title: 'Merge Requests', class: 'shortcuts-merge_requests' do = link_to namespace_project_merge_requests_path(@project.namespace, @project), title: 'Merge Requests', class: 'shortcuts-merge_requests' do
%span %span
Merge Requests Merge Requests
......
...@@ -136,7 +136,7 @@ ...@@ -136,7 +136,7 @@
- else - else
= build.id = build.id
- if build.retried? - if build.retried?
%i.fa.fa-refresh.has-tooltip{ data: { container: 'body', placement: 'bottom' }, title: 'Job was retried' } %i.fa.fa-spinner.has-tooltip{ data: { container: 'body', placement: 'bottom' }, title: 'Job was retried' }
:javascript :javascript
new Sidebar(); new Sidebar();
...@@ -36,7 +36,7 @@ ...@@ -36,7 +36,7 @@
= icon('warning', class: 'text-warning has-tooltip', title: 'Job is stuck. Check runners.') = icon('warning', class: 'text-warning has-tooltip', title: 'Job is stuck. Check runners.')
- if retried - if retried
= icon('refresh', class: 'text-warning has-tooltip', title: 'Job was retried') = icon('spinner', class: 'text-warning has-tooltip', title: 'Job was retried')
.label-container .label-container
- if job.tags.any? - if job.tags.any?
......
- @no_container = true - @no_container = true
- page_title "Edit", @label.name, "Labels" - page_title "Edit", @label.name, "Labels"
= render "projects/issues/head" = render "shared/mr_head"
%div{ class: container_class } %div{ class: container_class }
%h3.page-title %h3.page-title
......
- @no_container = true - @no_container = true
- page_title "Labels" - page_title "Labels"
- hide_class = '' - hide_class = ''
= render "projects/issues/head" = render "shared/mr_head"
- if @labels.exists? || @prioritized_labels.exists? - if @labels.exists? || @prioritized_labels.exists?
%div{ class: container_class } %div{ class: container_class }
......
- @no_container = true - @no_container = true
- page_title "New Label" - page_title "New Label"
= render "projects/issues/head" = render "shared/mr_head"
%div{ class: container_class } %div{ class: container_class }
%h3.page-title %h3.page-title
......
= content_for :sub_nav do
.scrolling-tabs-container.sub-nav-scroll
= render 'shared/nav_scroll'
.nav-links.sub-nav.scrolling-tabs
%ul{ class: (container_class) }
= nav_link(controller: :merge_requests) do
= link_to namespace_project_merge_requests_path(@project.namespace, @project), title: 'Merge Requests' do
%span
List
- if project_nav_tab? :labels
= nav_link(controller: :labels) do
= link_to namespace_project_labels_path(@project.namespace, @project), title: 'Labels' do
%span
Labels
- if project_nav_tab? :milestones
= nav_link(controller: :milestones) do
= link_to namespace_project_milestones_path(@project.namespace, @project), title: 'Milestones' do
%span
Milestones
...@@ -2,6 +2,9 @@ ...@@ -2,6 +2,9 @@
- @bulk_edit = can?(current_user, :admin_merge_request, @project) - @bulk_edit = can?(current_user, :admin_merge_request, @project)
- page_title "Merge Requests" - page_title "Merge Requests"
- unless @project.default_issues_tracker?
= content_for :sub_nav do
= render "projects/merge_requests/head"
= render 'projects/last_push' = render 'projects/last_push'
- content_for :page_specific_javascripts do - content_for :page_specific_javascripts do
......
- @no_container = true - @no_container = true
- page_title "Edit", @milestone.title, "Milestones" - page_title "Edit", @milestone.title, "Milestones"
= render "projects/issues/head" = render "shared/mr_head"
%div{ class: container_class } %div{ class: container_class }
......
- @no_container = true - @no_container = true
- page_title 'Milestones' - page_title 'Milestones'
= render 'projects/issues/head' = render "shared/mr_head"
%div{ class: container_class } %div{ class: container_class }
.top-area .top-area
......
- @no_container = true - @no_container = true
- page_title "New Milestone" - page_title "New Milestone"
= render "projects/issues/head" = render "shared/mr_head"
%div{ class: container_class } %div{ class: container_class }
%h3.page-title %h3.page-title
......
- @no_container = true - @no_container = true
- page_title @milestone.title, "Milestones" - page_title @milestone.title, "Milestones"
- page_description @milestone.description - page_description @milestone.description
= render "projects/issues/head" = render "shared/mr_head"
%div{ class: container_class } %div{ class: container_class }
.detail-page-header.milestone-page-header .detail-page-header.milestone-page-header
......
...@@ -52,11 +52,10 @@ ...@@ -52,11 +52,10 @@
":aria-label" => "buttonText", ":aria-label" => "buttonText",
"@click" => "resolve", "@click" => "resolve",
":title" => "buttonText", ":title" => "buttonText",
"v-show" => "!loading",
":ref" => "'button'" } ":ref" => "'button'" }
= icon("spin spinner", "v-show" => "loading")
= render "shared/icons/icon_status_success.svg" = icon("spin spinner", "v-show" => "loading", class: 'loading')
%div{ 'v-show' => '!loading' }= render "shared/icons/icon_status_success.svg"
- if current_user - if current_user
- if note.emoji_awardable? - if note.emoji_awardable?
......
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
= render "projects/last_push" = render "projects/last_push"
= render "home_panel" = render "home_panel"
- if current_user && can?(current_user, :download_code, @project) - if can?(current_user, :download_code, @project)
%nav.project-stats{ class: container_class } %nav.project-stats{ class: container_class }
%ul.nav %ul.nav
%li %li
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
= f.label :import_url, class: 'control-label' do = f.label :import_url, class: 'control-label' do
%span Git repository URL %span Git repository URL
.col-sm-10 .col-sm-10
= f.text_field :import_url, autocomplete: 'off', class: 'form-control', placeholder: 'https://username:password@gitlab.company.com/group/project.git', disabled: true = f.text_field :import_url, autocomplete: 'off', class: 'form-control', placeholder: 'https://username:password@gitlab.company.com/group/project.git'
.well.prepend-top-20 .well.prepend-top-20
%ul %ul
......
- if @project.default_issues_tracker?
= render "projects/issues/head"
- else
= render "projects/merge_requests/head"
...@@ -16,6 +16,8 @@ ...@@ -16,6 +16,8 @@
Also, issues are searchable and filterable. Also, issues are searchable and filterable.
- if project_select_button - if project_select_button
= render 'shared/new_project_item_select', path: 'issues/new', label: 'New issue' = render 'shared/new_project_item_select', path: 'issues/new', label: 'New issue'
= link_to 'New issue', button_path, class: 'btn btn-new', title: 'New issue', id: 'new_issue_link'
- else - else
.text-center
%h4 There are no issues to show. %h4 There are no issues to show.
= link_to 'New issue', button_path, class: 'btn btn-new', title: 'New issue', id: 'new_issue_link' = link_to 'New issue', button_path, class: 'btn btn-new', title: 'New issue', id: 'new_issue_link'
<svg width="14" height="14" viewBox="0 0 14 14" xmlns="http://www.w3.org/2000/svg"><path d="M0 7c0-3.866 3.142-7 7-7 3.866 0 7 3.142 7 7 0 3.866-3.142 7-7 7-3.866 0-7-3.142-7-7z"/><path d="M1 7c0 3.309 2.69 6 6 6 3.309 0 6-2.69 6-6 0-3.309-2.69-6-6-6-3.309 0-6 2.69-6 6z" fill="#FFF"/><path d="M9.427 6.523a.932.932 0 0 0-.808.489v-.01c-.49-.01-1.059-.172-1.46-.489-.35-.278-.7-.772-.882-1.17a.964.964 0 0 0 .35-.744.943.943 0 0 0-.934-.959c-.518 0-.933.432-.933.964 0 .35.191.662.467.825v3.147a.97.97 0 0 0-.467.825c0 .532.415.959.933.959a.943.943 0 0 0 .934-.96.965.965 0 0 0-.467-.824V6.844c.313.336.672.61 1.073.81.402.202.948.303 1.386.308v-.01c.168.293.467.49.808.49a.943.943 0 0 0 .933-.96.943.943 0 0 0-.933-.96z"/></svg> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path d="m2 3c.552 0 1-.448 1-1 0-.552-.448-1-1-1-.552 0-1 .448-1 1 0 .552.448 1 1 1m.761.85c.154 2.556 1.987 4.692 4.45 5.255.328-.655 1.01-1.105 1.789-1.105 1.105 0 2 .895 2 2 0 1.105-.895 2-2 2-.89 0-1.645-.582-1.904-1.386-1.916-.376-3.548-1.5-4.596-3.044v4.493c.863.222 1.5 1.01 1.5 1.937 0 1.105-.895 2-2 2-1.105 0-2-.895-2-2 0-.74.402-1.387 1-1.732v-8.535c-.598-.346-1-.992-1-1.732 0-1.105.895-2 2-2 1.105 0 2 .895 2 2 0 .835-.512 1.551-1.239 1.85m6.239 7.15c.552 0 1-.448 1-1 0-.552-.448-1-1-1-.552 0-1 .448-1 1 0 .552.448 1 1 1m-7 4c.552 0 1-.448 1-1 0-.552-.448-1-1-1-.552 0-1 .448-1 1 0 .552.448 1 1 1" transform="translate(3)"/></svg>
...@@ -3,7 +3,6 @@ class BuildCoverageWorker ...@@ -3,7 +3,6 @@ class BuildCoverageWorker
include BuildQueue include BuildQueue
def perform(build_id) def perform(build_id)
Ci::Build.find_by(id: build_id) Ci::Build.find_by(id: build_id)&.update_coverage
.try(:update_coverage)
end end
end end
---
title: Fix filtered search input width for IE
merge_request:
author:
---
title: Update all instances of the old loading icon
merge_request: 10490
author: Andrew Torres
---
title: Add webpack_bundle_tag helper to improve non-localhost GDK configurations
merge_request: 10604
author:
---
title: Turns true value and false value database methods from instance to class methods
merge_request: 10583
author:
---
title: Implement Users::BuildService
merge_request: 30349
author: George Andrinopoulos
---
title: Fix issue's note cache expiration after delete
merge_request:
author: mhasbini
---
title: Show sub-nav under Merge Requests when issue tracker is non-default.
merge_request: 10658
author:
---
title: "[BB Importer] Save the error trace and the whole raw document to debug problems
easier"
merge_request:
author:
---
title: Fixed alignment of empty task list items
merge_request:
author:
---
title: Fix missing capitalisation on views
merge_request:
author:
---
title: Fix bad query for PostgreSQL showing merge requests list
merge_request: 10666
author:
---
title: Fix invalid encoding when showing some traces
merge_request: 10681
author:
---
title: Remove heading and trailing spaces from label's color and title
merge_request: 10603
author: blackst0ne
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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