Commit d2d64393 authored by Kamil Trzcinski's avatar Kamil Trzcinski

Merge remote-tracking branch 'origin/master' into chat-name-authorize

parents d64183e1 b7aae9a6
CHANGELOG.md merge=union
*.js.es6 gitlab-language=javascript *.js.es6 gitlab-language=javascript
...@@ -61,9 +61,10 @@ update-knapsack: ...@@ -61,9 +61,10 @@ update-knapsack:
- scripts/merge-reports knapsack/spinach_report.json knapsack/spinach_node_*.json - scripts/merge-reports knapsack/spinach_report.json knapsack/spinach_node_*.json
- rm -f knapsack/*_node_*.json - rm -f knapsack/*_node_*.json
only: only:
- master - master@gitlab-org/gitlab-ce
- master@gitlab-org/gitlab-ee
# Execute all testing suites - master@gitlab/gitlabhq
- master@gitlab/gitlab-ee
.use-db: &use-db .use-db: &use-db
services: services:
...@@ -143,7 +144,10 @@ spinach 9 10: *spinach-knapsack ...@@ -143,7 +144,10 @@ spinach 9 10: *spinach-knapsack
image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.1-git-2.7-phantomjs-2.1" image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.1-git-2.7-phantomjs-2.1"
<<: *use-db <<: *use-db
only: only:
- master - master@gitlab-org/gitlab-ce
- master@gitlab-org/gitlab-ee
- master@gitlab/gitlabhq
- master@gitlab/gitlab-ee
cache: cache:
key: "ruby21" key: "ruby21"
paths: paths:
...@@ -286,7 +290,10 @@ bundler:audit: ...@@ -286,7 +290,10 @@ bundler:audit:
stage: test stage: test
<<: *ruby-static-analysis <<: *ruby-static-analysis
only: only:
- master - master@gitlab-org/gitlab-ce
- master@gitlab-org/gitlab-ee
- master@gitlab/gitlabhq
- master@gitlab/gitlab-ee
script: script:
- "bundle exec bundle-audit check --update --ignore OSVDB-115941" - "bundle exec bundle-audit check --update --ignore OSVDB-115941"
...@@ -297,6 +304,9 @@ migration paths: ...@@ -297,6 +304,9 @@ migration paths:
SETUP_DB: "false" SETUP_DB: "false"
only: only:
- master@gitlab-org/gitlab-ce - master@gitlab-org/gitlab-ce
- master@gitlab-org/gitlab-ee
- master@gitlab/gitlabhq
- master@gitlab/gitlab-ee
script: script:
- git checkout HEAD . - git checkout HEAD .
- git fetch --tags - git fetch --tags
......
...@@ -4,37 +4,6 @@ entry. ...@@ -4,37 +4,6 @@ entry.
## 8.14.0 (2016-11-22) ## 8.14.0 (2016-11-22)
- Use separate email-token for incoming email and revert back the inactive feature. !5914
- Replace jQuery.timeago with timeago.js. !6274 (ClemMakesApps)
- Add CI notifications. Who triggered a pipeline would receive an email after the pipeline is succeeded or failed. Users could also update notification settings accordingly. !6342
- Finer-grained Git gargage collection. !6588
- Introduce better credential and error checking to `rake gitlab:ldap:check`. !6601
- Process commits using a dedicated Sidekiq worker. !6802
- Fix showing pipeline status for a given commit from correct branch. !7034
- Add query param to filter users by external & blocked type. !7109 (Yatish Mehta)
- Issues atom feed url reflect filters on dashboard. !7114 (Lucas Deschamps)
- Add setting to only allow merge requests to be merged when all discussions are resolved. !7125 (Rodolfo Arruda)
- Remove an extra leading space from diff paste data. !7133 (Hiroyuki Sato)
- Fix 404 on network page when entering non-existent git revision. !7172 (Hiroyuki Sato)
- Rewrite git blame spinach feature tests to rspec feature tests. !7197 (Lisanne Fellinger)
- Only skip group when it's actually a group in the "Share with group" select. !7262
- Introduce round-robin project creation to spread load over multiple shards. !7266
- Ensure merge request's "remove branch" accessors return booleans. !7267
- Expose label IDs in API. !7275 (Rares Sfirlogea)
- Fix invalid filename validation on eslint. !7281
- API: Ability to retrieve version information. !7286 (Robert Schilling)
- Set default Sidekiq retries to 3. !7294
- Return 400 when creating a system hook fails. !7350 (Robert Schilling)
- Use the Gitlab Workhorse HTTP header in the admin dashboard. (Chris Wright)
- Add an index for project_id in project_import_data to improve performance.
- Fix broken link to observatory cli on Frontend Dev Guide. (Sam Rose)
- Faster search inside Project.
- Clicking "force remove source branch" label now toggles the checkbox again.
- Allow to test JIRA service settings without having a repository.
- Fix: Guest sees some repository details and gets 404.
- Bump omniauth-gitlab to 1.0.2 to fix incompatibility with omniauth-oauth2.
- Fix: Todos Filter Shows All Users.
- Fix broken commits search.
- Show correct environment log in admin/logs (@duk3luk3 !7191) - Show correct environment log in admin/logs (@duk3luk3 !7191)
- Fix Milestone dropdown not stay selected for `Upcoming` and `No Milestone` option !7117 - Fix Milestone dropdown not stay selected for `Upcoming` and `No Milestone` option !7117
- Diff collapse won't shift when collapsing. - Diff collapse won't shift when collapsing.
...@@ -51,6 +20,7 @@ entry. ...@@ -51,6 +20,7 @@ entry.
- Fail gracefully when creating merge request with non-existing branch (alexsanford) - Fail gracefully when creating merge request with non-existing branch (alexsanford)
- Fix mobile layout issues in admin user overview page !7087 - Fix mobile layout issues in admin user overview page !7087
- Fix HipChat notifications rendering (airatshigapov, eisnerd) - Fix HipChat notifications rendering (airatshigapov, eisnerd)
- Removed unneeded "Builds" and "Environments" link from project titles
- Remove 'Edit' button from wiki edit view !7143 (Hiroyuki Sato) - Remove 'Edit' button from wiki edit view !7143 (Hiroyuki Sato)
- Cleaned up global namespace JS !19661 (Jose Ivan Vargas) - Cleaned up global namespace JS !19661 (Jose Ivan Vargas)
- Refactor Jira service to use jira-ruby gem - Refactor Jira service to use jira-ruby gem
......
...@@ -137,6 +137,7 @@ gem 'acts-as-taggable-on', '~> 4.0' ...@@ -137,6 +137,7 @@ gem 'acts-as-taggable-on', '~> 4.0'
gem 'sidekiq', '~> 4.2' gem 'sidekiq', '~> 4.2'
gem 'sidekiq-cron', '~> 0.4.0' gem 'sidekiq-cron', '~> 0.4.0'
gem 'redis-namespace', '~> 1.5.2' gem 'redis-namespace', '~> 1.5.2'
gem 'sidekiq-limit_fetch', '~> 3.4'
# HTTP requests # HTTP requests
gem 'httparty', '~> 0.13.3' gem 'httparty', '~> 0.13.3'
......
...@@ -685,6 +685,8 @@ GEM ...@@ -685,6 +685,8 @@ GEM
redis-namespace (>= 1.5.2) redis-namespace (>= 1.5.2)
rufus-scheduler (>= 2.0.24) rufus-scheduler (>= 2.0.24)
sidekiq (>= 4.0.0) sidekiq (>= 4.0.0)
sidekiq-limit_fetch (3.4.0)
sidekiq (>= 4)
simplecov (0.12.0) simplecov (0.12.0)
docile (~> 1.1.0) docile (~> 1.1.0)
json (>= 1.8, < 3) json (>= 1.8, < 3)
...@@ -961,6 +963,7 @@ DEPENDENCIES ...@@ -961,6 +963,7 @@ DEPENDENCIES
shoulda-matchers (~> 2.8.0) shoulda-matchers (~> 2.8.0)
sidekiq (~> 4.2) sidekiq (~> 4.2)
sidekiq-cron (~> 0.4.0) sidekiq-cron (~> 0.4.0)
sidekiq-limit_fetch (~> 3.4)
simplecov (= 0.12.0) simplecov (= 0.12.0)
slack-notifier (~> 1.2.0) slack-notifier (~> 1.2.0)
spinach-rails (~> 0.2.1) spinach-rails (~> 0.2.1)
......
...@@ -82,6 +82,10 @@ GitLab is a Ruby on Rails application that runs on the following software: ...@@ -82,6 +82,10 @@ GitLab is a Ruby on Rails application that runs on the following software:
For more information please see the [architecture documentation](https://docs.gitlab.com/ce/development/architecture.html). For more information please see the [architecture documentation](https://docs.gitlab.com/ce/development/architecture.html).
## UX design
Please adhere to the [UX Guide](doc/development/ux_guide/readme.md) when creating designs and implementing code.
## Third-party applications ## Third-party applications
There are a lot of [third-party applications integrating with GitLab](https://about.gitlab.com/applications/). These include GUI Git clients, mobile applications and API wrappers for various languages. There are a lot of [third-party applications integrating with GitLab](https://about.gitlab.com/applications/). These include GUI Git clients, mobile applications and API wrappers for various languages.
......
...@@ -53,16 +53,34 @@ ...@@ -53,16 +53,34 @@
/*= require_directory ./u2f */ /*= require_directory ./u2f */
/*= require_directory . */ /*= require_directory . */
/*= require fuzzaldrin-plus */ /*= require fuzzaldrin-plus */
/*= require es6-promise.auto */
(function () { (function () {
document.addEventListener('page:fetch', gl.utils.cleanupBeforeFetch); document.addEventListener('page:fetch', gl.utils.cleanupBeforeFetch);
window.addEventListener('hashchange', gl.utils.shiftWindow); window.addEventListener('hashchange', gl.utils.shiftWindow);
// automatically adjust scroll position for hash urls taking the height of the navbar into account
// https://github.com/twitter/bootstrap/issues/1768
window.adjustScroll = function() {
var navbar = document.querySelector('.navbar-gitlab');
var subnav = document.querySelector('.layout-nav');
var fixedTabs = document.querySelector('.js-tabs-affix');
adjustment = 0;
if (navbar) adjustment -= navbar.offsetHeight;
if (subnav) adjustment -= subnav.offsetHeight;
if (fixedTabs) adjustment -= fixedTabs.offsetHeight;
return scrollBy(0, adjustment);
};
window.addEventListener("hashchange", adjustScroll);
window.onload = function () { window.onload = function () {
// Scroll the window to avoid the topnav bar // Scroll the window to avoid the topnav bar
// https://github.com/twitter/bootstrap/issues/1768 // https://github.com/twitter/bootstrap/issues/1768
if (location.hash) { if (location.hash) {
return setTimeout(gl.utils.shiftWindow, 100); return setTimeout(adjustScroll, 100);
} }
}; };
......
...@@ -22,6 +22,8 @@ $(() => { ...@@ -22,6 +22,8 @@ $(() => {
gl.IssueBoardsApp.$destroy(true); gl.IssueBoardsApp.$destroy(true);
} }
Store.create();
gl.IssueBoardsApp = new Vue({ gl.IssueBoardsApp = new Vue({
el: $boardApp, el: $boardApp,
components: { components: {
...@@ -37,16 +39,15 @@ $(() => { ...@@ -37,16 +39,15 @@ $(() => {
issueLinkBase: $boardApp.dataset.issueLinkBase, issueLinkBase: $boardApp.dataset.issueLinkBase,
detailIssue: Store.detail detailIssue: Store.detail
}, },
init: Store.create.bind(Store),
computed: { computed: {
detailIssueVisible () { detailIssueVisible () {
return Object.keys(this.detailIssue.issue).length; return Object.keys(this.detailIssue.issue).length;
} },
}, },
created () { created () {
gl.boardService = new BoardService(this.endpoint, this.boardId); gl.boardService = new BoardService(this.endpoint, this.boardId);
}, },
ready () { mounted () {
Store.disabled = this.disabled; Store.disabled = this.disabled;
gl.boardService.all() gl.boardService.all()
.then((resp) => { .then((resp) => {
...@@ -60,6 +61,8 @@ $(() => { ...@@ -60,6 +61,8 @@ $(() => {
} }
}); });
this.state.lists = _.sortBy(this.state.lists, 'position');
Store.addBlankState(); Store.addBlankState();
this.loading = false; this.loading = false;
}); });
...@@ -70,6 +73,9 @@ $(() => { ...@@ -70,6 +73,9 @@ $(() => {
el: '#js-boards-seach', el: '#js-boards-seach',
data: { data: {
filters: Store.state.filters filters: Store.state.filters
},
mounted () {
gl.issueBoards.newListDropdownInit();
} }
}); });
}); });
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
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',
components: { components: {
'board-list': gl.issueBoards.BoardList, 'board-list': gl.issueBoards.BoardList,
'board-delete': gl.issueBoards.BoardDelete, 'board-delete': gl.issueBoards.BoardDelete,
...@@ -24,7 +25,6 @@ ...@@ -24,7 +25,6 @@
return { return {
detailIssue: Store.detail, detailIssue: Store.detail,
filters: Store.state.filters, filters: Store.state.filters,
showIssueForm: false
}; };
}, },
watch: { watch: {
...@@ -58,10 +58,10 @@ ...@@ -58,10 +58,10 @@
}, },
methods: { methods: {
showNewIssueForm() { showNewIssueForm() {
this.showIssueForm = !this.showIssueForm; this.$refs['board-list'].showIssueForm = !this.$refs['board-list'].showIssueForm;
} }
}, },
ready () { mounted () {
const options = gl.issueBoards.getBoardSortableDefaultOptions({ const options = gl.issueBoards.getBoardSortableDefaultOptions({
disabled: this.disabled, disabled: this.disabled,
group: 'boards', group: 'boards',
...@@ -72,13 +72,9 @@ ...@@ -72,13 +72,9 @@
if (e.newIndex !== undefined && e.oldIndex !== e.newIndex) { if (e.newIndex !== undefined && e.oldIndex !== e.newIndex) {
const order = this.sortable.toArray(), const order = this.sortable.toArray(),
$board = this.$parent.$refs.board[e.oldIndex + 1], list = Store.findList('id', parseInt(e.item.dataset.id));
list = $board.list;
$board.$destroy(true);
this.$nextTick(() => { this.$nextTick(() => {
Store.state.lists.splice(e.newIndex, 0, list);
Store.moveList(list, order); Store.moveList(list, order);
}); });
} }
...@@ -87,8 +83,5 @@ ...@@ -87,8 +83,5 @@
this.sortable = Sortable.create(this.$el.parentNode, options); this.sortable = Sortable.create(this.$el.parentNode, options);
}, },
beforeDestroy () {
Store.state.lists.$remove(this.list);
}
}); });
})(); })();
...@@ -30,6 +30,8 @@ ...@@ -30,6 +30,8 @@
}); });
}); });
Store.state.lists = _.sortBy(Store.state.lists, 'position');
// Save the labels // Save the labels
gl.boardService.generateDefaultLists() gl.boardService.generateDefaultLists()
.then((resp) => { .then((resp) => {
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
window.gl.issueBoards = window.gl.issueBoards || {}; window.gl.issueBoards = window.gl.issueBoards || {};
gl.issueBoards.BoardCard = Vue.extend({ gl.issueBoards.BoardCard = Vue.extend({
template: '#js-board-list-card',
props: { props: {
list: Object, list: Object,
issue: Object, issue: Object,
...@@ -53,11 +54,6 @@ ...@@ -53,11 +54,6 @@
mouseDown () { mouseDown () {
this.showDetail = true; this.showDetail = true;
}, },
mouseMove () {
if (this.showDetail) {
this.showDetail = false;
}
},
showIssue (e) { showIssue (e) {
const targetTagName = e.target.tagName.toLowerCase(); const targetTagName = e.target.tagName.toLowerCase();
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
window.gl.issueBoards = window.gl.issueBoards || {}; window.gl.issueBoards = window.gl.issueBoards || {};
gl.issueBoards.BoardList = Vue.extend({ gl.issueBoards.BoardList = Vue.extend({
template: '#js-board-list-template',
components: { components: {
'board-card': gl.issueBoards.BoardCard, 'board-card': gl.issueBoards.BoardCard,
'board-new-issue': gl.issueBoards.BoardNewIssue 'board-new-issue': gl.issueBoards.BoardNewIssue
...@@ -19,20 +20,20 @@ ...@@ -19,20 +20,20 @@
issues: Array, issues: Array,
loading: Boolean, loading: Boolean,
issueLinkBase: String, issueLinkBase: String,
showIssueForm: Boolean
}, },
data () { data () {
return { return {
scrollOffset: 250, scrollOffset: 250,
filters: Store.state.filters, filters: Store.state.filters,
showCount: false showCount: false,
showIssueForm: false
}; };
}, },
watch: { watch: {
filters: { filters: {
handler () { handler () {
this.list.loadingMore = false; this.list.loadingMore = false;
this.$els.list.scrollTop = 0; this.$refs.list.scrollTop = 0;
}, },
deep: true deep: true
}, },
...@@ -51,15 +52,20 @@ ...@@ -51,15 +52,20 @@
}); });
} }
}, },
computed: {
orderedIssues () {
return _.sortBy(this.issues, 'priority');
},
},
methods: { methods: {
listHeight () { listHeight () {
return this.$els.list.getBoundingClientRect().height; return this.$refs.list.getBoundingClientRect().height;
}, },
scrollHeight () { scrollHeight () {
return this.$els.list.scrollHeight; return this.$refs.list.scrollHeight;
}, },
scrollTop () { scrollTop () {
return this.$els.list.scrollTop + this.listHeight(); return this.$refs.list.scrollTop + this.listHeight();
}, },
loadNextPage () { loadNextPage () {
const getIssues = this.list.nextPage(); const getIssues = this.list.nextPage();
...@@ -72,7 +78,7 @@ ...@@ -72,7 +78,7 @@
} }
}, },
}, },
ready () { mounted () {
const options = gl.issueBoards.getBoardSortableDefaultOptions({ const options = gl.issueBoards.getBoardSortableDefaultOptions({
group: 'issues', group: 'issues',
sort: false, sort: false,
...@@ -81,23 +87,27 @@ ...@@ -81,23 +87,27 @@
onStart: (e) => { onStart: (e) => {
const card = this.$refs.issue[e.oldIndex]; const card = this.$refs.issue[e.oldIndex];
card.showDetail = false;
Store.moving.issue = card.issue; Store.moving.issue = card.issue;
Store.moving.list = card.list; Store.moving.list = card.list;
gl.issueBoards.onStart(); gl.issueBoards.onStart();
}, },
onAdd: (e) => { onAdd: (e) => {
gl.issueBoards.BoardsStore.moveIssueToList(Store.moving.list, this.list, Store.moving.issue); // Add the element back to original list to allow Vue to handle DOM updates
e.from.appendChild(e.item);
this.$nextTick(() => {
// Update the issues once we know the element has been moved
gl.issueBoards.BoardsStore.moveIssueToList(Store.moving.list, this.list, Store.moving.issue);
});
}, },
onRemove: (e) => {
this.$refs.issue[e.oldIndex].$destroy(true);
}
}); });
this.sortable = Sortable.create(this.$els.list, options); this.sortable = Sortable.create(this.$refs.list, options);
// Scroll event on list to load more // Scroll event on list to load more
this.$els.list.onscroll = () => { this.$refs.list.onscroll = () => {
if ((this.scrollTop() > this.scrollHeight() - this.scrollOffset) && !this.list.loadingMore) { if ((this.scrollTop() > this.scrollHeight() - this.scrollOffset) && !this.list.loadingMore) {
this.loadNextPage(); this.loadNextPage();
} }
......
...@@ -7,7 +7,6 @@ ...@@ -7,7 +7,6 @@
gl.issueBoards.BoardNewIssue = Vue.extend({ gl.issueBoards.BoardNewIssue = Vue.extend({
props: { props: {
list: Object, list: Object,
showIssueForm: Boolean
}, },
data() { data() {
return { return {
...@@ -15,11 +14,6 @@ ...@@ -15,11 +14,6 @@
error: false error: false
}; };
}, },
watch: {
showIssueForm () {
this.$els.input.focus();
}
},
methods: { methods: {
submit(e) { submit(e) {
e.preventDefault(); e.preventDefault();
...@@ -37,28 +31,30 @@ ...@@ -37,28 +31,30 @@
this.list.newIssue(issue) this.list.newIssue(issue)
.then((data) => { .then((data) => {
// Need this because our jQuery very kindly disables buttons on ALL form submissions // Need this because our jQuery very kindly disables buttons on ALL form submissions
$(this.$els.submitButton).enable(); $(this.$refs.submitButton).enable();
Store.detail.issue = issue; Store.detail.issue = issue;
}) })
.catch(() => { .catch(() => {
// Need this because our jQuery very kindly disables buttons on ALL form submissions // Need this because our jQuery very kindly disables buttons on ALL form submissions
$(this.$els.submitButton).enable(); $(this.$refs.submitButton).enable();
// Remove the issue // Remove the issue
this.list.removeIssue(issue); this.list.removeIssue(issue);
// Show error message // Show error message
this.error = true; this.error = true;
this.showIssueForm = true;
}); });
this.cancel(); this.cancel();
}, },
cancel() { cancel() {
this.showIssueForm = false;
this.title = ''; this.title = '';
this.$parent.showIssueForm = false;
} }
} },
mounted() {
this.$refs.input.focus();
},
}); });
})(); })();
...@@ -41,7 +41,7 @@ ...@@ -41,7 +41,7 @@
this.detail.issue = {}; this.detail.issue = {};
} }
}, },
ready () { mounted () {
new IssuableContext(this.currentUser); new IssuableContext(this.currentUser);
new MilestoneSelect(); new MilestoneSelect();
new gl.DueDateSelectors(); new gl.DueDateSelectors();
......
/* eslint-disable */ /* eslint-disable */
$(() => { (() => {
window.gl = window.gl || {};
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) => {
...@@ -15,54 +18,58 @@ $(() => { ...@@ -15,54 +18,58 @@ $(() => {
}); });
}); });
$('.js-new-board-list').each(function () { gl.issueBoards.newListDropdownInit = () => {
const $this = $(this); $('.js-new-board-list').each(function () {
new gl.CreateLabelDropdown($this.closest('.dropdown').find('.dropdown-new-label'), $this.data('namespace-path'), $this.data('project-path')); const $this = $(this);
new gl.CreateLabelDropdown($this.closest('.dropdown').find('.dropdown-new-label'), $this.data('namespace-path'), $this.data('project-path'));
$this.glDropdown({ $this.glDropdown({
data(term, callback) { data(term, callback) {
$.get($this.attr('data-labels')) $.get($this.attr('data-labels'))
.then((resp) => { .then((resp) => {
callback(resp); callback(resp);
}); });
}, },
renderRow (label) { renderRow (label) {
const active = Store.findList('title', label.title), const active = Store.findList('title', label.title),
$li = $('<li />'), $li = $('<li />'),
$a = $('<a />', { $a = $('<a />', {
class: (active ? `is-active js-board-list-${active.id}` : ''), class: (active ? `is-active js-board-list-${active.id}` : ''),
text: label.title, text: label.title,
href: '#' href: '#'
}), }),
$labelColor = $('<span />', { $labelColor = $('<span />', {
class: 'dropdown-label-box', class: 'dropdown-label-box',
style: `background-color: ${label.color}` style: `background-color: ${label.color}`
}); });
return $li.append($a.prepend($labelColor)); return $li.append($a.prepend($labelColor));
}, },
search: { search: {
fields: ['title'] fields: ['title']
}, },
filterable: true, filterable: true,
selectable: true, selectable: true,
multiSelect: true, multiSelect: true,
clicked (label, $el, e) { clicked (label, $el, e) {
e.preventDefault(); e.preventDefault();
if (!Store.findList('title', label.title)) { if (!Store.findList('title', label.title)) {
Store.new({ Store.new({
title: label.title,
position: Store.state.lists.length - 2,
list_type: 'label',
label: {
id: label.id,
title: label.title, title: label.title,
color: label.color position: Store.state.lists.length - 2,
} list_type: 'label',
}); label: {
id: label.id,
title: label.title,
color: label.color
}
});
Store.state.lists = _.sortBy(Store.state.lists, 'position');
}
} }
} });
}); });
}); };
}); })();
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
fallbackOnBody: true, fallbackOnBody: true,
ghostClass: 'is-ghost', ghostClass: 'is-ghost',
filter: '.board-delete, .btn', filter: '.board-delete, .btn',
delay: gl.issueBoards.touchEnabled ? 100 : 50, delay: gl.issueBoards.touchEnabled ? 100 : 0,
scrollSensitivity: gl.issueBoards.touchEnabled ? 60 : 100, scrollSensitivity: gl.issueBoards.touchEnabled ? 60 : 100,
scrollSpeed: 20, scrollSpeed: 20,
onStart: gl.issueBoards.onStart, onStart: gl.issueBoards.onStart,
......
...@@ -42,7 +42,8 @@ class List { ...@@ -42,7 +42,8 @@ class List {
} }
destroy () { destroy () {
gl.issueBoards.BoardsStore.state.lists.$remove(this); const index = gl.issueBoards.BoardsStore.state.lists.indexOf(this);
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);
......
...@@ -39,6 +39,8 @@ ...@@ -39,6 +39,8 @@
// Remove any new issues from the backlog // Remove any new issues from the backlog
// as they will be visible in the new list // as they will be visible in the new list
list.issues.forEach(backlogList.removeIssue.bind(backlogList)); list.issues.forEach(backlogList.removeIssue.bind(backlogList));
this.state.lists = _.sortBy(this.state.lists, 'position');
}); });
this.removeBlankState(); this.removeBlankState();
}, },
...@@ -58,6 +60,8 @@ ...@@ -58,6 +60,8 @@
title: 'Welcome to your Issue Board!', title: 'Welcome to your Issue Board!',
position: 0 position: 0
}); });
this.state.lists = _.sortBy(this.state.lists, 'position');
}, },
removeBlankState () { removeBlankState () {
this.removeList('blank'); this.removeList('blank');
......
/* eslint-disable */ /* eslint-disable */
((w) => { (() => {
w.CommentAndResolveBtn = Vue.extend({ const CommentAndResolveBtn = Vue.extend({
props: { props: {
discussionId: String, discussionId: String,
textareaIsEmpty: Boolean },
data() {
return {
textareaIsEmpty: true
}
}, },
computed: { computed: {
discussion: function () { discussion: function () {
...@@ -35,7 +39,7 @@ ...@@ -35,7 +39,7 @@
} }
} }
}, },
ready: function () { mounted: function () {
const $textarea = $(`#new-discussion-note-form-${this.discussionId} .note-textarea`); const $textarea = $(`#new-discussion-note-form-${this.discussionId} .note-textarea`);
this.textareaIsEmpty = $textarea.val() === ''; this.textareaIsEmpty = $textarea.val() === '';
...@@ -47,4 +51,6 @@ ...@@ -47,4 +51,6 @@
$(`#new-discussion-note-form-${this.discussionId} .note-textarea`).off('input.comment-and-resolve-btn'); $(`#new-discussion-note-form-${this.discussionId} .note-textarea`).off('input.comment-and-resolve-btn');
} }
}); });
Vue.component('comment-and-resolve-btn', CommentAndResolveBtn);
})(window); })(window);
/* eslint-disable */ /* eslint-disable */
((w) => { (() => {
w.ResolveBtn = Vue.extend({ const ResolveBtn = Vue.extend({
props: { props: {
noteId: Number, noteId: Number,
discussionId: String, discussionId: String,
...@@ -54,7 +54,7 @@ ...@@ -54,7 +54,7 @@
}, },
methods: { methods: {
updateTooltip: function () { updateTooltip: function () {
$(this.$els.button) $(this.$refs.button)
.tooltip('hide') .tooltip('hide')
.tooltip('fixTitle'); .tooltip('fixTitle');
}, },
...@@ -89,8 +89,8 @@ ...@@ -89,8 +89,8 @@
}); });
} }
}, },
compiled: function () { mounted: function () {
$(this.$els.button).tooltip({ $(this.$refs.button).tooltip({
container: 'body' container: 'body'
}); });
}, },
...@@ -101,4 +101,6 @@ ...@@ -101,4 +101,6 @@
CommentsStore.create(this.discussionId, this.noteId, this.canResolve, this.resolved, this.resolvedBy); CommentsStore.create(this.discussionId, this.noteId, this.canResolve, this.resolved, this.resolvedBy);
} }
}); });
})(window);
Vue.component('resolve-btn', ResolveBtn);
})();
...@@ -13,6 +13,9 @@ ...@@ -13,6 +13,9 @@
computed: { computed: {
allResolved: function () { allResolved: function () {
return this.resolvedDiscussionCount === this.discussionCount; return this.resolvedDiscussionCount === this.discussionCount;
},
resolvedCountText() {
return this.discussionCount === 1 ? 'discussion' : 'discussions';
} }
} }
}); });
......
/* eslint-disable */ /* eslint-disable */
((w) => { (() => {
w.ResolveDiscussionBtn = Vue.extend({ const ResolveDiscussionBtn = Vue.extend({
props: { props: {
discussionId: String, discussionId: String,
mergeRequestId: Number, mergeRequestId: Number,
...@@ -54,4 +54,6 @@ ...@@ -54,4 +54,6 @@
CommentsStore.createDiscussion(this.discussionId, this.canResolve); CommentsStore.createDiscussion(this.discussionId, this.canResolve);
} }
}); });
})(window);
Vue.component('resolve-discussion-btn', ResolveDiscussionBtn);
})();
...@@ -8,24 +8,35 @@ ...@@ -8,24 +8,35 @@
//= require_directory ./components //= require_directory ./components
$(() => { $(() => {
window.DiffNotesApp = new Vue({ const COMPONENT_SELECTOR = 'resolve-btn, resolve-discussion-btn, jump-to-discussion, comment-and-resolve-btn';
el: '#diff-notes-app',
components: { window.gl = window.gl || {};
'resolve-btn': ResolveBtn, window.gl.diffNoteApps = {};
'resolve-discussion-btn': ResolveDiscussionBtn,
'comment-and-resolve-btn': CommentAndResolveBtn gl.diffNotesCompileComponents = () => {
}, const $components = $(COMPONENT_SELECTOR).filter(function () {
methods: { return $(this).closest('resolve-count').length !== 1;
compileComponents: function () { });
const $components = $('resolve-btn, resolve-discussion-btn, jump-to-discussion');
if ($components.length) { if ($components) {
$components.each(function () { $components.each(function () {
DiffNotesApp.$compile($(this).get(0)); const $this = $(this);
}); const noteId = $this.attr(':note-id');
const tmp = Vue.extend({
template: $this.get(0).outerHTML
});
const tmpApp = new tmp().$mount();
if (noteId) {
gl.diffNoteApps[`note_${noteId}`] = tmpApp;
} }
}
$this.replaceWith(tmpApp.$el);
});
} }
}); };
gl.diffNotesCompileComponents();
new Vue({ new Vue({
el: '#resolve-count-app', el: '#resolve-count-app',
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
}, },
// Team Members // Team Members
Members: { Members: {
template: '<li>${username} <small>${title}</small></li>' template: '<li>${avatarTag} ${username} <small>${title}</small></li>'
}, },
Labels: { Labels: {
template: '<li><span class="dropdown-label-box" style="background: ${color}"></span> ${title}</li>' template: '<li><span class="dropdown-label-box" style="background: ${color}"></span> ${title}</li>'
...@@ -51,6 +51,11 @@ ...@@ -51,6 +51,11 @@
if (!GitLab.GfmAutoComplete.dataLoaded) { if (!GitLab.GfmAutoComplete.dataLoaded) {
return this.at; return this.at;
} else { } else {
if (value.indexOf("unlabel") !== -1) {
GitLab.GfmAutoComplete.input.atwho('load', '~', GitLab.GfmAutoComplete.cachedData.unlabels);
} else {
GitLab.GfmAutoComplete.input.atwho('load', '~', GitLab.GfmAutoComplete.cachedData.labels);
}
return value; return value;
} }
} }
...@@ -118,7 +123,7 @@ ...@@ -118,7 +123,7 @@
beforeInsert: this.DefaultOptions.beforeInsert, beforeInsert: this.DefaultOptions.beforeInsert,
beforeSave: function(members) { beforeSave: function(members) {
return $.map(members, function(m) { return $.map(members, function(m) {
var title; let title = '';
if (m.username == null) { if (m.username == null) {
return m; return m;
} }
...@@ -126,8 +131,14 @@ ...@@ -126,8 +131,14 @@
if (m.count) { if (m.count) {
title += " (" + m.count + ")"; title += " (" + m.count + ")";
} }
const autoCompleteAvatar = m.avatar_url || m.username.charAt(0).toUpperCase();
const imgAvatar = `<img src="${m.avatar_url}" alt="${m.username}" class="avatar avatar-inline center s26"/>`;
const txtAvatar = `<div class="avatar center avatar-inline s26">${autoCompleteAvatar}</div>`;
return { return {
username: m.username, username: m.username,
avatarTag: autoCompleteAvatar.length === 1 ? txtAvatar : imgAvatar,
title: gl.utils.sanitize(title), title: gl.utils.sanitize(title),
search: gl.utils.sanitize(m.username + " " + m.name) search: gl.utils.sanitize(m.username + " " + m.name)
}; };
...@@ -352,3 +363,4 @@ ...@@ -352,3 +363,4 @@
}; };
}).call(this); }).call(this);
...@@ -36,7 +36,7 @@ ...@@ -36,7 +36,7 @@
this.loadEditor(); this.loadEditor();
} }
}, },
ready() { mounted() {
if (this.file.loadEditor) { if (this.file.loadEditor) {
this.loadEditor(); this.loadEditor();
} }
......
/* eslint-disable */
((global) => {
global.mergeConflicts = global.mergeConflicts || {};
global.mergeConflicts.parallelConflictLine = Vue.extend({
props: {
file: Object,
line: Object
},
mixins: [global.mergeConflicts.utils, global.mergeConflicts.actions],
template: '#parallel-conflict-line'
});
})(window.gl || (window.gl = {}));
...@@ -7,10 +7,22 @@ ...@@ -7,10 +7,22 @@
props: { props: {
file: Object file: Object
}, },
mixins: [global.mergeConflicts.utils], mixins: [global.mergeConflicts.utils, global.mergeConflicts.actions],
components: { template: `
'parallel-conflict-line': gl.mergeConflicts.parallelConflictLine <table>
} <tr class="line_holder parallel" v-for="section in file.parallelLines">
<template v-for="line in section">
<td class="diff-line-num header" :class="lineCssClass(line)" v-if="line.isHeader"></td>
<td class="line_content header" :class="lineCssClass(line)" v-if="line.isHeader">
<strong>{{line.richText}}</strong>
<button class="btn" @click="handleSelected(file, line.id, line.section)">{{line.buttonTitle}}</button>
</td>
<td class="diff-line-num old_line" :class="lineCssClass(line)" v-if="!line.isHeader">{{line.lineNumber}}</td>
<td class="line_content parallel" :class="lineCssClass(line)" v-if="!line.isHeader" v-html="line.richText"></td>
</template>
</tr>
</table>
`,
}); });
})(window.gl || (window.gl = {})); })(window.gl || (window.gl = {}));
...@@ -6,7 +6,6 @@ ...@@ -6,7 +6,6 @@
//= require ./mixins/line_conflict_actions //= require ./mixins/line_conflict_actions
//= require ./components/diff_file_editor //= require ./components/diff_file_editor
//= require ./components/inline_conflict_lines //= require ./components/inline_conflict_lines
//= require ./components/parallel_conflict_line
//= require ./components/parallel_conflict_lines //= require ./components/parallel_conflict_lines
$(() => { $(() => {
...@@ -49,7 +48,7 @@ $(() => { ...@@ -49,7 +48,7 @@ $(() => {
mergeConflictsStore.setLoadingState(false); mergeConflictsStore.setLoadingState(false);
this.$nextTick(() => { this.$nextTick(() => {
$(conflictsEl.querySelectorAll('.js-syntax-highlight')).syntaxHighlight(); $('.js-syntax-highlight').syntaxHighlight();
}); });
}); });
}, },
......
...@@ -130,7 +130,7 @@ ...@@ -130,7 +130,7 @@
MergeRequestTabs.prototype.scrollToElement = function(container) { MergeRequestTabs.prototype.scrollToElement = function(container) {
var $el, navBarHeight; var $el, navBarHeight;
if (window.location.hash) { if (window.location.hash) {
navBarHeight = $('.navbar-gitlab').outerHeight() + $('.layout-nav').outerHeight(); navBarHeight = $('.navbar-gitlab').outerHeight() + $('.layout-nav').outerHeight() + document.querySelector('.js-tabs-affix').offsetHeight;
$el = $(container + " " + window.location.hash + ":not(.match)"); $el = $(container + " " + window.location.hash + ":not(.match)");
if ($el.length) { if ($el.length) {
return $.scrollTo(container + " " + window.location.hash + ":not(.match)", { return $.scrollTo(container + " " + window.location.hash + ":not(.match)", {
...@@ -227,8 +227,8 @@ ...@@ -227,8 +227,8 @@
return function(data) { return function(data) {
$('#diffs').html(data.html); $('#diffs').html(data.html);
if (typeof DiffNotesApp !== 'undefined') { if (typeof gl.diffNotesCompileComponents !== 'undefined') {
DiffNotesApp.compileComponents(); gl.diffNotesCompileComponents();
} }
gl.utils.localTimeAgo($('.js-timeago', 'div#diffs')); gl.utils.localTimeAgo($('.js-timeago', 'div#diffs'));
......
...@@ -325,8 +325,8 @@ ...@@ -325,8 +325,8 @@
discussionContainer.append(note_html); discussionContainer.append(note_html);
} }
if (typeof DiffNotesApp !== 'undefined') { if (typeof gl.diffNotesCompileComponents !== 'undefined') {
DiffNotesApp.compileComponents(); gl.diffNotesCompileComponents();
} }
gl.utils.localTimeAgo($('.js-timeago', note_html), false); gl.utils.localTimeAgo($('.js-timeago', note_html), false);
...@@ -466,8 +466,8 @@ ...@@ -466,8 +466,8 @@
$note_li.replaceWith($html); $note_li.replaceWith($html);
if (typeof DiffNotesApp !== 'undefined') { if (typeof gl.diffNotesCompileComponents !== 'undefined') {
DiffNotesApp.compileComponents(); gl.diffNotesCompileComponents();
} }
}; };
...@@ -559,11 +559,9 @@ ...@@ -559,11 +559,9 @@
note = $(el); note = $(el);
notes = note.closest(".notes"); notes = note.closest(".notes");
if (typeof DiffNotesApp !== "undefined" && DiffNotesApp !== null) { if (typeof gl.diffNotesCompileComponents !== 'undefined') {
ref = DiffNotesApp.$refs[noteId]; if (gl.diffNoteApps[noteId]) {
gl.diffNoteApps[noteId].$destroy();
if (ref) {
ref.$destroy(true);
} }
} }
...@@ -643,11 +641,12 @@ ...@@ -643,11 +641,12 @@
form.find('.js-note-target-close').remove(); form.find('.js-note-target-close').remove();
this.setupNoteForm(form); this.setupNoteForm(form);
if (typeof DiffNotesApp !== 'undefined') { if (typeof gl.diffNotesCompileComponents !== 'undefined') {
var $commentBtn = form.find('comment-and-resolve-btn'); var $commentBtn = form.find('comment-and-resolve-btn');
$commentBtn $commentBtn
.attr(':discussion-id', "'" + dataHolder.data('discussionId') + "'"); .attr(':discussion-id', "'" + dataHolder.data('discussionId') + "'");
DiffNotesApp.$compile($commentBtn.get(0));
gl.diffNotesCompileComponents();
} }
form.find(".js-note-text").focus(); form.find(".js-note-text").focus();
......
...@@ -45,15 +45,15 @@ ...@@ -45,15 +45,15 @@
this.content.hide(); this.content.hide();
this.$toggleIcon.addClass('fa-caret-right').removeClass('fa-caret-down'); this.$toggleIcon.addClass('fa-caret-right').removeClass('fa-caret-down');
this.collapsedContent.show(); this.collapsedContent.show();
if (typeof DiffNotesApp !== 'undefined') { if (typeof gl.diffNotesCompileComponents !== 'undefined') {
DiffNotesApp.compileComponents(); gl.diffNotesCompileComponents();
} }
} else if (this.content) { } else if (this.content) {
this.collapsedContent.hide(); this.collapsedContent.hide();
this.content.show(); this.content.show();
this.$toggleIcon.addClass('fa-caret-down').removeClass('fa-caret-right'); this.$toggleIcon.addClass('fa-caret-down').removeClass('fa-caret-right');
if (typeof DiffNotesApp !== 'undefined') { if (typeof gl.diffNotesCompileComponents !== 'undefined') {
DiffNotesApp.compileComponents(); gl.diffNotesCompileComponents();
} }
} else { } else {
this.$toggleIcon.addClass('fa-caret-down').removeClass('fa-caret-right'); this.$toggleIcon.addClass('fa-caret-down').removeClass('fa-caret-right');
...@@ -76,8 +76,8 @@ ...@@ -76,8 +76,8 @@
} }
_this.collapsedContent.after(_this.content); _this.collapsedContent.after(_this.content);
if (typeof DiffNotesApp !== 'undefined') { if (typeof gl.diffNotesCompileComponents !== 'undefined') {
DiffNotesApp.compileComponents(); gl.diffNotesCompileComponents();
} }
if (cb) cb(); if (cb) cb();
......
...@@ -34,6 +34,7 @@ ...@@ -34,6 +34,7 @@
&.avatar-inline { &.avatar-inline {
float: none; float: none;
display: inline-block;
margin-left: 4px; margin-left: 4px;
margin-bottom: 2px; margin-bottom: 2px;
...@@ -41,6 +42,12 @@ ...@@ -41,6 +42,12 @@
&.s24 { margin-right: 4px; } &.s24 { margin-right: 4px; }
} }
&.center {
font-size: 14px;
line-height: 1.8em;
text-align: center;
}
&.avatar-tile { &.avatar-tile {
border-radius: 0; border-radius: 0;
border: none; border: none;
......
...@@ -141,6 +141,10 @@ ...@@ -141,6 +141,10 @@
&.btn-save { &.btn-save {
@include btn-outline($white-light, $green-normal, $green-normal, $green-light, $white-light, $green-light); @include btn-outline($white-light, $green-normal, $green-normal, $green-light, $white-light, $green-light);
} }
&.btn-remove {
@include btn-outline($white-light, $red-normal, $red-normal, $red-light, $white-light, $red-light);
}
} }
&.btn-gray { &.btn-gray {
......
...@@ -63,6 +63,11 @@ header { ...@@ -63,6 +63,11 @@ header {
&:focus, &:focus,
&:active { &:active {
background-color: $background-color; background-color: $background-color;
color: darken($gl-icon-color, 30%);
.todos-pending-count {
background: darken($todo-alert-blue, 10%);
}
} }
.fa-caret-down { .fa-caret-down {
...@@ -152,7 +157,7 @@ header { ...@@ -152,7 +157,7 @@ header {
padding-right: 20px; padding-right: 20px;
margin: 0; margin: 0;
font-size: 19px; font-size: 19px;
max-width: 400px; max-width: 385px;
display: inline-block; display: inline-block;
line-height: $header-height; line-height: $header-height;
font-weight: normal; font-weight: normal;
...@@ -191,6 +196,10 @@ header { ...@@ -191,6 +196,10 @@ header {
font-size: 10px; font-size: 10px;
text-align: center; text-align: center;
cursor: pointer; cursor: pointer;
&:hover {
color: darken($color: $gl-text-color, $amount: 30%);
}
} }
.project-item-select { .project-item-select {
...@@ -218,6 +227,14 @@ header { ...@@ -218,6 +227,14 @@ header {
} }
} }
.page-sidebar-pinned.right-sidebar-expanded {
@media (max-width: $screen-lg-min) {
.header-content .title {
width: 300px;
}
}
}
@media (max-width: $screen-xs-max) { @media (max-width: $screen-xs-max) {
header .container-fluid { header .container-fluid {
font-size: 18px; font-size: 18px;
......
...@@ -148,7 +148,19 @@ ...@@ -148,7 +148,19 @@
} }
} }
.atwho-view small.description { .atwho-view {
float: right; small.description {
padding: 3px 5px; float: right;
} padding: 3px 5px;
}
.avatar-inline {
margin-bottom: 0;
}
.cur {
.avatar {
border: 1px solid $white-light;
}
}
}
\ No newline at end of file
...@@ -71,7 +71,7 @@ ...@@ -71,7 +71,7 @@
display: none; display: none;
} }
.group-right-buttons { .group-buttons {
display: none; display: none;
} }
......
...@@ -252,7 +252,7 @@ $award-emoji-new-btn-icon-color: #dcdcdc; ...@@ -252,7 +252,7 @@ $award-emoji-new-btn-icon-color: #dcdcdc;
*/ */
$search-input-border-color: rgba(#4688f1, .8); $search-input-border-color: rgba(#4688f1, .8);
$search-input-focus-shadow-color: $dropdown-input-focus-shadow; $search-input-focus-shadow-color: $dropdown-input-focus-shadow;
$search-input-width: 244px; $search-input-width: 220px;
$location-badge-color: #aaa; $location-badge-color: #aaa;
$location-badge-bg: $gray-normal; $location-badge-bg: $gray-normal;
$location-badge-active-bg: #4f91f8; $location-badge-active-bg: #4f91f8;
......
...@@ -166,8 +166,12 @@ ...@@ -166,8 +166,12 @@
} }
} }
.board-list { .board-list-component {
height: calc(100% - 49px); height: calc(100% - 49px);
}
.board-list {
height: 100%;
margin-bottom: 0; margin-bottom: 0;
padding: 5px; padding: 5px;
list-style: none; list-style: none;
...@@ -175,7 +179,7 @@ ...@@ -175,7 +179,7 @@
overflow-x: hidden; overflow-x: hidden;
&.is-smaller { &.is-smaller {
height: calc(100% - 185px); height: calc(100% - 136px);
} }
} }
......
...@@ -62,6 +62,8 @@ ...@@ -62,6 +62,8 @@
.ci-status-link { .ci-status-link {
display: inline-block; display: inline-block;
position: relative;
top: 1px;
} }
.btn-clipboard, .btn-clipboard,
...@@ -82,7 +84,8 @@ ...@@ -82,7 +84,8 @@
font-weight: 600; font-weight: 600;
} }
.commit { .commit,
.generic_commit_status {
padding: 10px 0; padding: 10px 0;
position: relative; position: relative;
...@@ -100,7 +103,6 @@ ...@@ -100,7 +103,6 @@
vertical-align: baseline; vertical-align: baseline;
} }
.avatar { .avatar {
margin-left: -46px; margin-left: -46px;
} }
......
...@@ -10,7 +10,6 @@ ...@@ -10,7 +10,6 @@
} }
.group-row { .group-row {
.stats { .stats {
float: right; float: right;
line-height: $list-text-height; line-height: $list-text-height;
...@@ -23,36 +22,18 @@ ...@@ -23,36 +22,18 @@
} }
.ldap-group-links { .ldap-group-links {
.form-actions { .form-actions {
margin-bottom: $gl-padding; margin-bottom: $gl-padding;
} }
} }
.groups-cover-block { .group-buttons {
.notification-dropdown {
.container-fluid { display: inline-block;
position: relative;
}
.group-right-buttons {
position: absolute;
right: 16px;
.btn {
@include btn-gray;
padding: 3px 10px;
background-color: $background-color;
}
}
.group-avatar {
border: 0;
} }
} }
.groups-header { .groups-header {
@media (min-width: $screen-sm-min) { @media (min-width: $screen-sm-min) {
.nav-links { .nav-links {
width: 35%; width: 35%;
......
...@@ -267,20 +267,6 @@ ...@@ -267,20 +267,6 @@
} }
} }
.issuable-header-btn {
background: $gray-normal;
border: 1px solid $border-gray-normal;
&:hover {
background: $gray-dark;
border: 1px solid $border-gray-dark;
}
&.btn-primary {
@extend .btn-primary;
}
}
a { a {
&:hover { &:hover {
color: $md-link-color; color: $md-link-color;
......
...@@ -47,6 +47,7 @@ ...@@ -47,6 +47,7 @@
&.right { &.right {
float: right; float: right;
padding-right: 0;
a { a {
color: $gl-gray; color: $gl-gray;
......
...@@ -141,6 +141,22 @@ ul.notes { ...@@ -141,6 +141,22 @@ ul.notes {
} }
} }
.page-sidebar-pinned.right-sidebar-expanded {
@media (max-width: $screen-lg-min) {
.note-header {
.note-headline-light {
display: block;
}
.note-actions {
position: absolute;
right: 0;
top: 0;
}
}
}
}
// Diff code in discussion view // Diff code in discussion view
.discussion-body .diff-file { .discussion-body .diff-file {
.file-title { .file-title {
......
...@@ -109,10 +109,6 @@ ...@@ -109,10 +109,6 @@
float: none; float: none;
} }
.api {
color: $code-color;
}
.branch-commit { .branch-commit {
.branch-name { .branch-name {
......
...@@ -86,7 +86,8 @@ ...@@ -86,7 +86,8 @@
} }
} }
.project-home-panel { .project-home-panel,
.group-home-panel {
padding-top: 24px; padding-top: 24px;
padding-bottom: 24px; padding-bottom: 24px;
...@@ -94,7 +95,8 @@ ...@@ -94,7 +95,8 @@
border-bottom: 1px solid $border-color; border-bottom: 1px solid $border-color;
} }
.project-avatar { .project-avatar,
.group-avatar {
float: none; float: none;
margin: 0 auto; margin: 0 auto;
border: none; border: none;
...@@ -104,7 +106,8 @@ ...@@ -104,7 +106,8 @@
} }
} }
.project-title { .project-title,
.group-title {
margin-top: 10px; margin-top: 10px;
margin-bottom: 10px; margin-bottom: 10px;
font-size: 24px; font-size: 24px;
...@@ -118,10 +121,11 @@ ...@@ -118,10 +121,11 @@
} }
} }
.project-home-desc { .project-home-desc,
.group-home-desc {
margin-left: auto; margin-left: auto;
margin-right: auto; margin-right: auto;
margin-bottom: 15px; margin-bottom: 0;
max-width: 700px; max-width: 700px;
> p { > p {
...@@ -141,13 +145,18 @@ ...@@ -141,13 +145,18 @@
} }
} }
.project-repo-buttons { .project-repo-buttons,
font-size: 0; .group-buttons {
margin-top: 15px;
.btn { .btn {
@include btn-gray; @include btn-gray;
padding: 3px 10px; padding: 3px 10px;
&:last-child {
margin-left: 0;
}
.fa { .fa {
color: $layout-link-gray; color: $layout-link-gray;
} }
...@@ -168,7 +177,8 @@ ...@@ -168,7 +177,8 @@
} }
} }
.project-repo-btn-group, .download-button,
.dropdown-toggle,
.notification-dropdown, .notification-dropdown,
.project-dropdown { .project-dropdown {
margin-left: 10px; margin-left: 10px;
...@@ -474,9 +484,7 @@ a.deploy-project-label { ...@@ -474,9 +484,7 @@ a.deploy-project-label {
margin-right: $gl-padding; margin-right: $gl-padding;
} }
&.project-repo-buttons-right { &.right {
margin-top: 10px;
@media (min-width: $screen-md-min) { @media (min-width: $screen-md-min) {
float: right; float: right;
margin-top: 0; margin-top: 0;
......
...@@ -21,6 +21,11 @@ ...@@ -21,6 +21,11 @@
padding: 4px; padding: 4px;
width: $search-input-width; width: $search-input-width;
line-height: 24px; line-height: 24px;
&:hover {
border-color: lighten($dropdown-input-focus-border, 20%);
box-shadow: 0 0 4px lighten($search-input-focus-shadow-color, 20%);
}
} }
.location-text { .location-text {
...@@ -28,10 +33,9 @@ ...@@ -28,10 +33,9 @@
} }
.search-input { .search-input {
padding-right: 20px;
border: none; border: none;
font-size: 14px; font-size: 14px;
padding: 0; padding: 0 20px 0 0;
margin-left: 5px; margin-left: 5px;
line-height: 25px; line-height: 25px;
width: 98%; width: 98%;
......
...@@ -117,6 +117,8 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController ...@@ -117,6 +117,8 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
:send_user_confirmation_email, :send_user_confirmation_email,
:container_registry_token_expire_delay, :container_registry_token_expire_delay,
:enabled_git_access_protocol, :enabled_git_access_protocol,
:sidekiq_throttling_enabled,
:sidekiq_throttling_factor,
:housekeeping_enabled, :housekeeping_enabled,
:housekeeping_bitmaps_enabled, :housekeeping_bitmaps_enabled,
:housekeeping_incremental_repack_period, :housekeeping_incremental_repack_period,
...@@ -125,7 +127,8 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController ...@@ -125,7 +127,8 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
repository_storages: [], repository_storages: [],
restricted_visibility_levels: [], restricted_visibility_levels: [],
import_sources: [], import_sources: [],
disabled_oauth_sign_in_sources: [] disabled_oauth_sign_in_sources: [],
sidekiq_throttling_queues: []
) )
end end
end end
...@@ -3,7 +3,7 @@ module DiffForPath ...@@ -3,7 +3,7 @@ module DiffForPath
def render_diff_for_path(diffs) def render_diff_for_path(diffs)
diff_file = diffs.diff_files.find do |diff| diff_file = diffs.diff_files.find do |diff|
diff.old_path == params[:old_path] && diff.new_path == params[:new_path] diff.file_identifier == params[:file_identifier]
end end
return render_404 unless diff_file return render_404 unless diff_file
......
...@@ -58,7 +58,7 @@ class Groups::MilestonesController < Groups::ApplicationController ...@@ -58,7 +58,7 @@ class Groups::MilestonesController < Groups::ApplicationController
def render_new_with_error(empty_project_ids) def render_new_with_error(empty_project_ids)
@milestone = Milestone.new(milestone_params) @milestone = Milestone.new(milestone_params)
@milestone.errors.add(:project_id, "Please select at least one project.") if empty_project_ids @milestone.errors.add(:base, "Please select at least one project.") if empty_project_ids
render :new render :new
end end
......
...@@ -7,8 +7,8 @@ class HelpController < ApplicationController ...@@ -7,8 +7,8 @@ class HelpController < ApplicationController
@help_index = File.read(Rails.root.join('doc', 'README.md')) @help_index = File.read(Rails.root.join('doc', 'README.md'))
# Prefix Markdown links with `help/` unless they already have been # Prefix Markdown links with `help/` unless they already have been
# See http://rubular.com/r/nwwhzH6Z8X # See http://rubular.com/r/ie2MlpdUMq
@help_index.gsub!(/(\]\()(?!help\/)([^\)\(]+)(\))/, '\1help/\2\3') @help_index.gsub!(/(\]\()(\/?help\/)?([^\)\(]+\))/, '\1/help/\3')
end end
def show def show
......
...@@ -4,7 +4,7 @@ class Projects::BranchesController < Projects::ApplicationController ...@@ -4,7 +4,7 @@ class Projects::BranchesController < Projects::ApplicationController
# Authorize # Authorize
before_action :require_non_empty_project before_action :require_non_empty_project
before_action :authorize_download_code! before_action :authorize_download_code!
before_action :authorize_push_code!, only: [:new, :create, :destroy] before_action :authorize_push_code!, only: [:new, :create, :destroy, :destroy_all_merged]
def index def index
@sort = params[:sort].presence || sort_value_name @sort = params[:sort].presence || sort_value_name
...@@ -62,6 +62,13 @@ class Projects::BranchesController < Projects::ApplicationController ...@@ -62,6 +62,13 @@ class Projects::BranchesController < Projects::ApplicationController
end end
end end
def destroy_all_merged
DeleteMergedBranchesService.new(@project, current_user).async_execute
redirect_to namespace_project_branches_path(@project.namespace, @project),
notice: 'Merged branches are being deleted. This can take some time depending on the number of branches. Please refresh the page to see changes.'
end
private private
def ref def ref
......
...@@ -31,10 +31,6 @@ class Projects::LfsApiController < Projects::GitHttpClientController ...@@ -31,10 +31,6 @@ class Projects::LfsApiController < Projects::GitHttpClientController
private private
def objects
@objects ||= (params[:objects] || []).to_a
end
def existing_oids def existing_oids
@existing_oids ||= begin @existing_oids ||= begin
storage_project.lfs_objects.where(oid: objects.map { |o| o['oid'].to_s }).pluck(:oid) storage_project.lfs_objects.where(oid: objects.map { |o| o['oid'].to_s }).pluck(:oid)
......
...@@ -507,6 +507,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -507,6 +507,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@merge_request.close @merge_request.close
end end
labels
define_pipelines_vars define_pipelines_vars
end end
......
...@@ -18,7 +18,9 @@ class Projects::PipelinesController < Projects::ApplicationController ...@@ -18,7 +18,9 @@ class Projects::PipelinesController < Projects::ApplicationController
end end
def create def create
@pipeline = Ci::CreatePipelineService.new(project, current_user, create_params).execute(ignore_skip_ci: true, save_on_errors: false) @pipeline = Ci::CreatePipelineService
.new(project, current_user, create_params)
.execute(ignore_skip_ci: true, save_on_errors: false)
unless @pipeline.persisted? unless @pipeline.persisted?
render 'new' render 'new'
return return
......
...@@ -144,13 +144,15 @@ class ProjectsController < Projects::ApplicationController ...@@ -144,13 +144,15 @@ class ProjectsController < Projects::ApplicationController
autocomplete = ::Projects::AutocompleteService.new(@project, current_user) autocomplete = ::Projects::AutocompleteService.new(@project, current_user)
participants = ::Projects::ParticipantsService.new(@project, current_user).execute(noteable) participants = ::Projects::ParticipantsService.new(@project, current_user).execute(noteable)
unlabels = autocomplete.unlabels(noteable)
@suggestions = { @suggestions = {
emojis: Gitlab::AwardEmoji.urls, emojis: Gitlab::AwardEmoji.urls,
issues: autocomplete.issues, issues: autocomplete.issues,
milestones: autocomplete.milestones, milestones: autocomplete.milestones,
mergerequests: autocomplete.merge_requests, mergerequests: autocomplete.merge_requests,
labels: autocomplete.labels, labels: autocomplete.labels - unlabels,
unlabels: unlabels,
members: participants, members: participants,
commands: autocomplete.commands(noteable, params[:type]) commands: autocomplete.commands(noteable, params[:type])
} }
......
...@@ -100,4 +100,8 @@ module ApplicationSettingsHelper ...@@ -100,4 +100,8 @@ module ApplicationSettingsHelper
options_for_select(options, @application_setting.repository_storages) options_for_select(options, @application_setting.repository_storages)
end end
def sidekiq_queue_options_for_select
options_for_select(Sidekiq::Queue.all.map(&:name), @application_setting.sidekiq_throttling_queues)
end
end end
...@@ -3,7 +3,7 @@ module AuthHelper ...@@ -3,7 +3,7 @@ module AuthHelper
FORM_BASED_PROVIDERS = [/\Aldap/, 'crowd'].freeze FORM_BASED_PROVIDERS = [/\Aldap/, 'crowd'].freeze
def ldap_enabled? def ldap_enabled?
Gitlab.config.ldap.enabled Gitlab::LDAP::Config.enabled?
end end
def omniauth_enabled? def omniauth_enabled?
......
...@@ -82,6 +82,10 @@ module GitlabRoutingHelper ...@@ -82,6 +82,10 @@ module GitlabRoutingHelper
namespace_project_merge_request_path(entity.project.namespace, entity.project, entity, *args) namespace_project_merge_request_path(entity.project.namespace, entity.project, entity, *args)
end end
def pipeline_path(pipeline, *args)
namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline.id, *args)
end
def milestone_path(entity, *args) def milestone_path(entity, *args)
namespace_project_milestone_path(entity.project.namespace, entity.project, entity, *args) namespace_project_milestone_path(entity.project.namespace, entity.project, entity, *args)
end end
......
...@@ -30,11 +30,6 @@ module IssuablesHelper ...@@ -30,11 +30,6 @@ module IssuablesHelper
end end
end end
def can_add_template?(issuable)
names = issuable_templates(issuable)
names.empty? && can?(current_user, :push_code, @project) && !@project.private?
end
def template_dropdown_tag(issuable, &block) def template_dropdown_tag(issuable, &block)
title = selected_template(issuable) || "Choose a template" title = selected_template(issuable) || "Choose a template"
options = { options = {
...@@ -141,8 +136,19 @@ module IssuablesHelper ...@@ -141,8 +136,19 @@ module IssuablesHelper
html.html_safe html.html_safe
end end
def cached_assigned_issuables_count(assignee, issuable_type, state)
cache_key = hexdigest(['assigned_issuables_count', assignee.id, issuable_type, state].join('-'))
Rails.cache.fetch(cache_key, expires_in: 2.minutes) do
assigned_issuables_count(assignee, issuable_type, state)
end
end
private private
def assigned_issuables_count(assignee, issuable_type, state)
assignee.public_send("assigned_#{issuable_type}").public_send(state).count
end
def sidebar_gutter_collapsed? def sidebar_gutter_collapsed?
cookies[:collapsed_gutter] == 'true' cookies[:collapsed_gutter] == 'true'
end end
......
...@@ -30,6 +30,10 @@ module LfsHelper ...@@ -30,6 +30,10 @@ module LfsHelper
ci? || lfs_deploy_token? || user_can_download_code? || build_can_download_code? ci? || lfs_deploy_token? || user_can_download_code? || build_can_download_code?
end end
def objects
@objects ||= (params[:objects] || []).to_a
end
def user_can_download_code? def user_can_download_code?
has_authentication_ability?(:download_code) && can?(user, :download_code, project) has_authentication_ability?(:download_code) && can?(user, :download_code, project)
end end
......
...@@ -49,7 +49,7 @@ module ProjectsHelper ...@@ -49,7 +49,7 @@ module ProjectsHelper
end end
end end
def project_title(project, name = nil, url = nil) def project_title(project)
namespace_link = namespace_link =
if project.group if project.group
link_to(simple_sanitize(project.group.name), group_path(project.group)) link_to(simple_sanitize(project.group.name), group_path(project.group))
...@@ -66,10 +66,7 @@ module ProjectsHelper ...@@ -66,10 +66,7 @@ module ProjectsHelper
end end
end end
full_title = "#{namespace_link} / #{project_link}".html_safe "#{namespace_link} / #{project_link}".html_safe
full_title << ' &middot; '.html_safe << link_to(simple_sanitize(name), url) if name
full_title
end end
def remove_project_message(project) def remove_project_message(project)
......
module TriggersHelper module TriggersHelper
def builds_trigger_url(project_id) def builds_trigger_url(project_id, ref: nil)
"#{Settings.gitlab.url}/api/v3/projects/#{project_id}/trigger/builds" if ref.nil?
"#{Settings.gitlab.url}/api/v3/projects/#{project_id}/trigger/builds"
else
"#{Settings.gitlab.url}/api/v3/projects/#{project_id}/ref/#{ref}/trigger/builds"
end
end end
end end
...@@ -19,6 +19,7 @@ class ApplicationSetting < ActiveRecord::Base ...@@ -19,6 +19,7 @@ class ApplicationSetting < ActiveRecord::Base
serialize :domain_whitelist, Array serialize :domain_whitelist, Array
serialize :domain_blacklist, Array serialize :domain_blacklist, Array
serialize :repository_storages serialize :repository_storages
serialize :sidekiq_throttling_queues, Array
cache_markdown_field :sign_in_text cache_markdown_field :sign_in_text
cache_markdown_field :help_page_text cache_markdown_field :help_page_text
...@@ -85,6 +86,15 @@ class ApplicationSetting < ActiveRecord::Base ...@@ -85,6 +86,15 @@ class ApplicationSetting < ActiveRecord::Base
presence: { message: 'Domain blacklist cannot be empty if Blacklist is enabled.' }, presence: { message: 'Domain blacklist cannot be empty if Blacklist is enabled.' },
if: :domain_blacklist_enabled? if: :domain_blacklist_enabled?
validates :sidekiq_throttling_factor,
numericality: { greater_than: 0, less_than: 1 },
presence: { message: 'Throttling factor cannot be empty if Sidekiq Throttling is enabled.' },
if: :sidekiq_throttling_enabled?
validates :sidekiq_throttling_queues,
presence: { message: 'Queues to throttle cannot be empty if Sidekiq Throttling is enabled.' },
if: :sidekiq_throttling_enabled?
validates :housekeeping_incremental_repack_period, validates :housekeeping_incremental_repack_period,
presence: true, presence: true,
numericality: { only_integer: true, greater_than: 0 } numericality: { only_integer: true, greater_than: 0 }
...@@ -180,6 +190,7 @@ class ApplicationSetting < ActiveRecord::Base ...@@ -180,6 +190,7 @@ class ApplicationSetting < ActiveRecord::Base
container_registry_token_expire_delay: 5, container_registry_token_expire_delay: 5,
repository_storages: ['default'], repository_storages: ['default'],
user_default_external: false, user_default_external: false,
sidekiq_throttling_enabled: false,
housekeeping_enabled: true, housekeeping_enabled: true,
housekeeping_bitmaps_enabled: true, housekeeping_bitmaps_enabled: true,
housekeeping_incremental_repack_period: 10, housekeeping_incremental_repack_period: 10,
...@@ -192,6 +203,10 @@ class ApplicationSetting < ActiveRecord::Base ...@@ -192,6 +203,10 @@ class ApplicationSetting < ActiveRecord::Base
ActiveRecord::Base.connection.column_exists?(:application_settings, :home_page_url) ActiveRecord::Base.connection.column_exists?(:application_settings, :home_page_url)
end end
def sidekiq_throttling_column_exists?
ActiveRecord::Base.connection.column_exists?(:application_settings, :sidekiq_throttling_enabled)
end
def domain_whitelist_raw def domain_whitelist_raw
self.domain_whitelist.join("\n") unless self.domain_whitelist.nil? self.domain_whitelist.join("\n") unless self.domain_whitelist.nil?
end end
...@@ -245,6 +260,12 @@ class ApplicationSetting < ActiveRecord::Base ...@@ -245,6 +260,12 @@ class ApplicationSetting < ActiveRecord::Base
ensure_health_check_access_token! ensure_health_check_access_token!
end end
def sidekiq_throttling_enabled?
return false unless sidekiq_throttling_column_exists?
sidekiq_throttling_enabled
end
private private
def check_repository_storages def check_repository_storages
......
...@@ -266,7 +266,7 @@ class Issue < ActiveRecord::Base ...@@ -266,7 +266,7 @@ class Issue < ActiveRecord::Base
def as_json(options = {}) def as_json(options = {})
super(options).tap do |json| super(options).tap do |json|
json[:subscribed] = subscribed?(options[:user]) if options.has_key?(:user) json[:subscribed] = subscribed?(options[:user]) if options.has_key?(:user) && options[:user]
if options.has_key?(:labels) if options.has_key?(:labels)
json[:labels] = labels.as_json( json[:labels] = labels.as_json(
......
...@@ -7,6 +7,7 @@ class Note < ActiveRecord::Base ...@@ -7,6 +7,7 @@ class Note < ActiveRecord::Base
include Importable include Importable
include FasterCacheKeys include FasterCacheKeys
include CacheMarkdownField include CacheMarkdownField
include AfterCommitQueue
cache_markdown_field :note, pipeline: :note cache_markdown_field :note, pipeline: :note
......
...@@ -35,6 +35,7 @@ class Project < ActiveRecord::Base ...@@ -35,6 +35,7 @@ class Project < ActiveRecord::Base
default_value_for :builds_enabled, gitlab_config_features.builds default_value_for :builds_enabled, gitlab_config_features.builds
default_value_for :wiki_enabled, gitlab_config_features.wiki default_value_for :wiki_enabled, gitlab_config_features.wiki
default_value_for :snippets_enabled, gitlab_config_features.snippets default_value_for :snippets_enabled, gitlab_config_features.snippets
default_value_for :only_allow_merge_if_all_discussions_are_resolved, false
after_create :ensure_dir_exist after_create :ensure_dir_exist
after_create :create_project_feature, unless: :project_feature after_create :create_project_feature, unless: :project_feature
......
...@@ -49,20 +49,14 @@ class ProjectFeature < ActiveRecord::Base ...@@ -49,20 +49,14 @@ class ProjectFeature < ActiveRecord::Base
end end
def builds_enabled? def builds_enabled?
return true unless builds_access_level
builds_access_level > DISABLED builds_access_level > DISABLED
end end
def wiki_enabled? def wiki_enabled?
return true unless wiki_access_level
wiki_access_level > DISABLED wiki_access_level > DISABLED
end end
def merge_requests_enabled? def merge_requests_enabled?
return true unless merge_requests_access_level
merge_requests_access_level > DISABLED merge_requests_access_level > DISABLED
end end
......
...@@ -84,15 +84,17 @@ class Repository ...@@ -84,15 +84,17 @@ class Repository
def commit(ref = 'HEAD') def commit(ref = 'HEAD')
return nil unless exists? return nil unless exists?
commit = commit =
if ref.is_a?(Gitlab::Git::Commit) if ref.is_a?(Gitlab::Git::Commit)
ref ref
else else
Gitlab::Git::Commit.find(raw_repository, ref) Gitlab::Git::Commit.find(raw_repository, ref)
end end
commit = ::Commit.new(commit, @project) if commit commit = ::Commit.new(commit, @project) if commit
commit commit
rescue Rugged::OdbError rescue Rugged::OdbError, Rugged::TreeError
nil nil
end end
...@@ -232,6 +234,8 @@ class Repository ...@@ -232,6 +234,8 @@ class Repository
def ref_exists?(ref) def ref_exists?(ref)
rugged.references.exist?(ref) rugged.references.exist?(ref)
rescue Rugged::ReferenceError
false
end end
def update_ref!(name, newrev, oldrev) def update_ref!(name, newrev, oldrev)
...@@ -239,7 +243,7 @@ class Repository ...@@ -239,7 +243,7 @@ class Repository
# offer 'compare and swap' ref updates. Without compare-and-swap we can # offer 'compare and swap' ref updates. Without compare-and-swap we can
# (and have!) accidentally reset the ref to an earlier state, clobbering # (and have!) accidentally reset the ref to an earlier state, clobbering
# commits. See also https://github.com/libgit2/libgit2/issues/1534. # commits. See also https://github.com/libgit2/libgit2/issues/1534.
command = %w[git update-ref --stdin -z] command = %W(#{Gitlab.config.git.bin_path} update-ref --stdin -z)
_, status = Gitlab::Popen.popen(command, path_to_repo) do |stdin| _, status = Gitlab::Popen.popen(command, path_to_repo) do |stdin|
stdin.write("update #{name}\x00#{newrev}\x00#{oldrev}\x00") stdin.write("update #{name}\x00#{newrev}\x00#{oldrev}\x00")
end end
...@@ -270,11 +274,7 @@ class Repository ...@@ -270,11 +274,7 @@ class Repository
end end
def kept_around?(sha) def kept_around?(sha)
begin ref_exists?(keep_around_ref_name(sha))
ref_exists?(keep_around_ref_name(sha))
rescue Rugged::ReferenceError
false
end
end end
def tag_names def tag_names
...@@ -631,7 +631,7 @@ class Repository ...@@ -631,7 +631,7 @@ class Repository
@head_tree ||= Tree.new(self, head_commit.sha, nil) @head_tree ||= Tree.new(self, head_commit.sha, nil)
end end
def tree(sha = :head, path = nil) def tree(sha = :head, path = nil, recursive: false)
if sha == :head if sha == :head
if path.nil? if path.nil?
return head_tree return head_tree
...@@ -640,7 +640,7 @@ class Repository ...@@ -640,7 +640,7 @@ class Repository
end end
end end
Tree.new(self, sha, path) Tree.new(self, sha, path, recursive: recursive)
end end
def blob_at_branch(branch_name, path) def blob_at_branch(branch_name, path)
......
...@@ -3,15 +3,16 @@ class Tree ...@@ -3,15 +3,16 @@ class Tree
attr_accessor :repository, :sha, :path, :entries attr_accessor :repository, :sha, :path, :entries
def initialize(repository, sha, path = '/') def initialize(repository, sha, path = '/', recursive: false)
path = '/' if path.blank? path = '/' if path.blank?
@repository = repository @repository = repository
@sha = sha @sha = sha
@path = path @path = path
@recursive = recursive
git_repo = @repository.raw_repository git_repo = @repository.raw_repository
@entries = Gitlab::Git::Tree.where(git_repo, @sha, @path) @entries = get_entries(git_repo, @sha, @path, recursive: @recursive)
end end
def readme def readme
...@@ -58,4 +59,21 @@ class Tree ...@@ -58,4 +59,21 @@ class Tree
def sorted_entries def sorted_entries
trees + blobs + submodules trees + blobs + submodules
end end
private
def get_entries(git_repo, sha, path, recursive: false)
current_path_entries = Gitlab::Git::Tree.where(git_repo, sha, path)
ordered_entries = []
current_path_entries.each do |entry|
ordered_entries << entry
if recursive && entry.dir?
ordered_entries.concat(get_entries(git_repo, sha, entry.path, recursive: true))
end
end
ordered_entries
end
end end
require_relative 'base_service'
class DeleteMergedBranchesService < BaseService
def async_execute
DeleteMergedBranchesWorker.perform_async(project.id, current_user.id)
end
def execute
raise Gitlab::Access::AccessDeniedError unless can?(current_user, :push_code, project)
branches = project.repository.branch_names
branches = branches.select { |branch| project.repository.merged_to_root_ref?(branch) }
branches.each do |branch|
DeleteBranchService.new(project, current_user).execute(branch)
end
end
end
...@@ -26,9 +26,12 @@ module Notes ...@@ -26,9 +26,12 @@ module Notes
note.note = content note.note = content
end end
if !only_commands && note.save note.run_after_commit do
# Finish the harder work in the background # Finish the harder work in the background
NewNoteWorker.perform_in(2.seconds, note.id, params) NewNoteWorker.perform_async(note.id)
end
if !only_commands && note.save
todo_service.new_note(note, current_user) todo_service.new_note(note, current_user)
end end
......
...@@ -13,7 +13,14 @@ module Projects ...@@ -13,7 +13,14 @@ module Projects
end end
def labels def labels
LabelsFinder.new(current_user, project_id: project.id).execute.select([:title, :color]) LabelsFinder.new(current_user, project_id: project.id).execute.
pluck(:title, :color).map { |l| { title: l.first, color: l.second } }
end
def unlabels(noteable)
return [] unless noteable && noteable.respond_to?(:labels)
noteable.labels.pluck(:title, :color).map { |l| { title: l.first, color: l.second } }
end end
def commands(noteable, type) def commands(noteable, type)
......
module Projects module Projects
class ParticipantsService < BaseService class ParticipantsService < BaseService
attr_reader :noteable attr_reader :noteable
def execute(noteable) def execute(noteable)
@noteable = noteable @noteable = noteable
...@@ -15,7 +15,8 @@ module Projects ...@@ -15,7 +15,8 @@ module Projects
[{ [{
name: noteable.author.name, name: noteable.author.name,
username: noteable.author.username username: noteable.author.username,
avatar_url: noteable.author.avatar_url
}] }]
end end
...@@ -28,14 +29,14 @@ module Projects ...@@ -28,14 +29,14 @@ module Projects
def sorted(users) def sorted(users)
users.uniq.to_a.compact.sort_by(&:username).map do |user| users.uniq.to_a.compact.sort_by(&:username).map do |user|
{ username: user.username, name: user.name } { username: user.username, name: user.name, avatar_url: user.avatar_url }
end end
end end
def groups def groups
current_user.authorized_groups.sort_by(&:path).map do |group| current_user.authorized_groups.sort_by(&:path).map do |group|
count = group.users.count count = group.users.count
{ username: group.path, name: group.name, count: count } { username: group.path, name: group.name, count: count, avatar_url: group.avatar.url }
end end
end end
......
...@@ -283,6 +283,31 @@ ...@@ -283,6 +283,31 @@
The amount of points to store in a single UDP packet. More points The amount of points to store in a single UDP packet. More points
results in fewer but larger UDP packets being sent. results in fewer but larger UDP packets being sent.
%fieldset
%legend Background Jobs
%p
These settings require a restart to take effect.
.form-group
.col-sm-offset-2.col-sm-10
.checkbox
= f.label :sidekiq_throttling_enabled do
= f.check_box :sidekiq_throttling_enabled
Enable Sidekiq Job Throttling
.help-block
Limit the amount of resources slow running jobs are assigned.
.form-group
= f.label :sidekiq_throttling_queues, 'Sidekiq queues to throttle', class: 'control-label col-sm-2'
.col-sm-10
= f.select :sidekiq_throttling_queues, sidekiq_queue_options_for_select, { include_hidden: false }, multiple: true, class: 'select2 select-wide', data: { field: 'sidekiq_throttling_queues' }
.help-block
Choose which queues you wish to throttle.
.form-group
= f.label :sidekiq_throttling_factor, 'Throttling Factor', class: 'control-label col-sm-2'
.col-sm-10
= f.number_field :sidekiq_throttling_factor, class: 'form-control', min: '0.01', max: '0.99', step: '0.01'
.help-block
The factor by which the queues should be throttled. A value between 0.0 and 1.0, exclusive.
%fieldset %fieldset
%legend Spam and Anti-bot Protection %legend Spam and Anti-bot Protection
.form-group .form-group
......
...@@ -84,7 +84,7 @@ ...@@ -84,7 +84,7 @@
= render "shared/empty_states/todos_all_done.svg" = render "shared/empty_states/todos_all_done.svg"
- if todos_filter_empty? - if todos_filter_empty?
%h4.text-center %h4.text-center
Good job! Looks like you don't have any todos left. = Gitlab.config.gitlab.no_todos_messages.sample
%p.text-center %p.text-center
Are you looking for things to do? Take a look at Are you looking for things to do? Take a look at
= succeed "," do = succeed "," do
......
...@@ -8,3 +8,6 @@ ...@@ -8,3 +8,6 @@
- if signin_enabled? - if signin_enabled?
%li %li
= link_to 'Standard', '#ldap-standard', 'data-toggle' => 'tab' = link_to 'Standard', '#ldap-standard', 'data-toggle' => 'tab'
- if signin_enabled? && signup_enabled?
%li
= link_to 'Register', '#register-pane', 'data-toggle' => 'tab'
...@@ -34,7 +34,7 @@ ...@@ -34,7 +34,7 @@
= f.label :projects, "Projects", class: "control-label" = f.label :projects, "Projects", class: "control-label"
.col-sm-10 .col-sm-10
= f.collection_select :project_ids, @group.projects.non_archived, :id, :name, = f.collection_select :project_ids, @group.projects.non_archived, :id, :name,
{ selected: @group.projects.non_archived.pluck(:id) }, multiple: true, class: 'select2' { selected: @group.projects.non_archived.pluck(:id) }, required: true, multiple: true, class: 'select2'
.col-md-6 .col-md-6
.form-group .form-group
......
...@@ -4,25 +4,23 @@ ...@@ -4,25 +4,23 @@
- if current_user - if current_user
= auto_discovery_link_tag(:atom, group_url(@group, format: :atom, private_token: current_user.private_token), title: "#{@group.name} activity") = auto_discovery_link_tag(:atom, group_url(@group, format: :atom, private_token: current_user.private_token), title: "#{@group.name} activity")
.cover-block.groups-cover-block .group-home-panel.text-center
%div{ class: container_class } %div{ class: container_class }
.avatar-container.s70.group-avatar .avatar-container.s70.group-avatar
= image_tag group_icon(@group), class: "avatar s70 avatar-tile" = image_tag group_icon(@group), class: "avatar s70 avatar-tile"
.group-info %h1.group-title
.cover-title @#{@group.path}
%h1 %span.visibility-icon.has-tooltip{ data: { container: 'body' }, title: visibility_icon_description(@group) }
@#{@group.path} = visibility_level_icon(@group.visibility_level, fw: false)
%span.visibility-icon.has-tooltip{ data: { container: 'body' }, title: visibility_icon_description(@group) }
= visibility_level_icon(@group.visibility_level, fw: false)
.group-right-buttons.btn-group - if @group.description.present?
- if current_user .group-home-desc
.pull-left.append-right-10= render 'shared/members/access_request_buttons', source: @group = markdown_field(@group, :description)
= render 'shared/notifications/button', notification_setting: @notification_setting
- if @group.description.present? - if current_user
.cover-desc.description .group-buttons
= markdown_field(@group, :description) = render 'shared/members/access_request_buttons', source: @group
= render 'shared/notifications/button', notification_setting: @notification_setting
%div.groups-header{ class: container_class } %div.groups-header{ class: container_class }
.top-area .top-area
......
...@@ -26,12 +26,12 @@ ...@@ -26,12 +26,12 @@
= link_to assigned_issues_dashboard_path, title: 'Issues', class: 'dashboard-shortcuts-issues' do = link_to assigned_issues_dashboard_path, title: 'Issues', class: 'dashboard-shortcuts-issues' do
%span %span
Issues Issues
%span.count= number_with_delimiter(current_user.assigned_issues.opened.count) %span.count= number_with_delimiter(cached_assigned_issuables_count(current_user, :issues, :opened))
= nav_link(path: 'dashboard#merge_requests') do = nav_link(path: 'dashboard#merge_requests') do
= link_to assigned_mrs_dashboard_path, title: 'Merge Requests', class: 'dashboard-shortcuts-merge_requests' do = link_to assigned_mrs_dashboard_path, title: 'Merge Requests', class: 'dashboard-shortcuts-merge_requests' do
%span %span
Merge Requests Merge Requests
%span.count= number_with_delimiter(current_user.assigned_merge_requests.opened.count) %span.count= number_with_delimiter(cached_assigned_issuables_count(current_user, :merge_requests, :opened))
= nav_link(controller: 'dashboard/snippets') do = nav_link(controller: 'dashboard/snippets') do
= link_to dashboard_snippets_path, title: 'Snippets' do = link_to dashboard_snippets_path, title: 'Snippets' do
%span %span
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
- if can_admin_group - if can_admin_group
= nav_link(path: 'groups#projects') do = nav_link(path: 'groups#projects') do
= link_to 'Projects', projects_group_path(@group), title: 'Projects' = link_to 'Projects', projects_group_path(@group), title: 'Projects'
- if can_edit || can_leave - if (can_edit || can_leave) && can_admin_group
%li.divider %li.divider
- if can_edit - if can_edit
%li %li
......
- page_title 'Artifacts', "#{@build.name} (##{@build.id})", 'Builds' - page_title 'Artifacts', "#{@build.name} (##{@build.id})", 'Builds'
- header_title project_title(@project, "Builds", project_builds_path(@project))
.top-block.row-content-block.clearfix .top-block.row-content-block.clearfix
.pull-right .pull-right
......
- @no_container = true
- @content_class = "issue-boards-content"
- page_title "Boards"
- content_for :page_specific_javascripts do
= page_specific_javascript_tag('boards/boards_bundle.js')
= page_specific_javascript_tag('boards/test_utils/simulate_drag.js') if Rails.env.test?
%script#js-board-template{ type: "text/x-template" }= render "projects/boards/components/board"
%script#js-board-list-template{ type: "text/x-template" }= render "projects/boards/components/board_list"
%script#js-board-list-card{ type: "text/x-template" }= render "projects/boards/components/card"
= render "projects/issues/head"
= render 'shared/issuable/filter', type: :boards
#board-app.boards-app{ "v-cloak" => true, data: board_data }
.boards-list{ ":class" => "{ 'is-compact': detailIssueVisible }" }
.boards-app-loading.text-center{ "v-if" => "loading" }
= icon("spinner spin")
%board{ "v-cloak" => true,
"v-for" => "list in state.lists",
"ref" => "board",
":list" => "list",
":disabled" => "disabled",
":issue-link-base" => "issueLinkBase",
":key" => "_uid" }
= render "projects/boards/components/sidebar"
%board-blank-state{ "inline-template" => true, %board-blank-state{ "inline-template" => true,
"v-if" => "list.id == 'blank'" } "v-if" => 'list.id == "blank"' }
.board-blank-state .board-blank-state
%p %p
Add the following default lists to your Issue Board with one click: Add the following default lists to your Issue Board with one click:
......
%board{ "inline-template" => true, .board{ ":class" => '{ "is-draggable": !list.preset }',
"v-cloak" => true, ":data-id" => "list.id" }
"v-for" => "list in state.lists | orderBy 'position'", .board-inner
"v-ref:board" => true, %header.board-header{ ":class" => '{ "has-border": list.label }', ":style" => "{ borderTopColor: (list.label ? list.label.color : null) }" }
":list" => "list", %h3.board-title.js-board-handle{ ":class" => '{ "user-can-drag": (!disabled && !list.preset) }' }
":disabled" => "disabled", %span.has-tooltip{ ":title" => '(list.label ? list.label.description : "")',
":issue-link-base" => "issueLinkBase", data: { container: "body", placement: "bottom" } }
"track-by" => "_uid" } {{ list.title }}
.board{ ":class" => "{ 'is-draggable': !list.preset }", .board-issue-count-holder.pull-right.clearfix{ "v-if" => 'list.type !== "blank"' }
":data-id" => "list.id" } %span.board-issue-count.pull-left{ ":class" => '{ "has-btn": list.type !== "done" }' }
.board-inner {{ list.issuesSize }}
%header.board-header{ ":class" => "{ 'has-border': list.label }", ":style" => "{ borderTopColor: (list.label ? list.label.color : null) }" } - if can?(current_user, :admin_issue, @project)
%h3.board-title.js-board-handle{ ":class" => "{ 'user-can-drag': (!disabled && !list.preset) }" } %button.btn.btn-small.btn-default.pull-right.has-tooltip{ type: "button",
%span.has-tooltip{ ":title" => "(list.label ? list.label.description : '')", "@click" => "showNewIssueForm",
data: { container: "body", placement: "bottom" } } "v-if" => 'list.type !== "done"',
{{ list.title }} "aria-label" => "Add an issue",
.board-issue-count-holder.pull-right.clearfix{ "v-if" => "list.type !== 'blank'" } "title" => "Add an issue",
%span.board-issue-count.pull-left{ ":class" => "{ 'has-btn': list.type !== 'done' }" } data: { placement: "top", container: "body" } }
{{ list.issuesSize }} = icon("plus")
- if can?(current_user, :admin_issue, @project) - if can?(current_user, :admin_list, @project)
%button.btn.btn-small.btn-default.pull-right.has-tooltip{ type: "button", %board-delete{ "inline-template" => true,
"@click" => "showNewIssueForm",
"v-if" => "list.type !== 'done'",
"aria-label" => "Add an issue",
"title" => "Add an issue",
data: { placement: "top", container: "body" } }
= icon("plus")
- if can?(current_user, :admin_list, @project)
%board-delete{ "inline-template" => true,
":list" => "list",
"v-if" => "!list.preset && list.id" }
%button.board-delete.has-tooltip.pull-right{ type: "button", title: "Delete list", "aria-label" => "Delete list", data: { placement: "bottom" }, "@click.stop" => "deleteBoard" }
= icon("trash")
%board-list{ "inline-template" => true,
"v-if" => "list.type !== 'blank'",
":list" => "list",
":issues" => "list.issues",
":loading" => "list.loading",
":disabled" => "disabled",
":show-issue-form.sync" => "showIssueForm",
":issue-link-base" => "issueLinkBase" }
.board-list-loading.text-center{ "v-if" => "loading" }
= icon("spinner spin")
- if can? current_user, :create_issue, @project
%board-new-issue{ "inline-template" => true,
":list" => "list", ":list" => "list",
":show-issue-form.sync" => "showIssueForm", "v-if" => "!list.preset && list.id" }
"v-show" => "list.type !== 'done' && showIssueForm" } %button.board-delete.has-tooltip.pull-right{ type: "button", title: "Delete list", "aria-label" => "Delete list", data: { placement: "bottom" }, "@click.stop" => "deleteBoard" }
.card.board-new-issue-form = icon("trash")
%form{ "@submit" => "submit($event)" } %board-list{ "v-if" => 'list.type !== "blank"',
.flash-container{ "v-if" => "error" } ":list" => "list",
.flash-alert ":issues" => "list.issues",
An error occured. Please try again. ":loading" => "list.loading",
%label.label-light{ ":for" => "list.id + '-title'" } ":disabled" => "disabled",
Title ":issue-link-base" => "issueLinkBase",
%input.form-control{ type: "text", "ref" => "board-list" }
"v-model" => "title", - if can?(current_user, :admin_list, @project)
"v-el:input" => true, = render "projects/boards/components/blank_state"
":id" => "list.id + '-title'" }
.clearfix.prepend-top-10
%button.btn.btn-success.pull-left{ type: "submit",
":disabled" => "title === ''",
"v-el:submit-button" => true }
Submit issue
%button.btn.btn-default.pull-right{ type: "button",
"@click" => "cancel" }
Cancel
%ul.board-list{ "v-el:list" => true,
"v-show" => "!loading",
":data-board" => "list.id",
":class" => "{ 'is-smaller': showIssueForm }" }
= render "projects/boards/components/card"
%li.board-list-count.text-center{ "v-if" => "showCount" }
= icon("spinner spin", "v-show" => "list.loadingMore" )
%span{ "v-if" => "list.issues.length === list.issuesSize" }
Showing all issues
%span{ "v-else" => true }
Showing {{ list.issues.length }} of {{ list.issuesSize }} issues
- if can?(current_user, :admin_list, @project)
= render "projects/boards/components/blank_state"
.board-list-component
.board-list-loading.text-center{ "v-if" => "loading" }
= icon("spinner spin")
- if can? current_user, :create_issue, @project
%board-new-issue{ "inline-template" => true,
":list" => "list",
"v-if" => 'list.type !== "done" && showIssueForm' }
.card.board-new-issue-form
%form{ "@submit" => "submit($event)" }
.flash-container{ "v-if" => "error" }
.flash-alert
An error occured. Please try again.
%label.label-light{ ":for" => 'list.id + "-title"' }
Title
%input.form-control{ type: "text",
"v-model" => "title",
"ref" => "input",
":id" => 'list.id + "-title"' }
.clearfix.prepend-top-10
%button.btn.btn-success.pull-left{ type: "submit",
":disabled" => 'title === ""',
"ref" => "submit-button" }
Submit issue
%button.btn.btn-default.pull-right{ type: "button",
"@click" => "cancel" }
Cancel
%ul.board-list{ "ref" => "list",
"v-show" => "!loading",
":data-board" => "list.id",
":class" => '{ "is-smaller": showIssueForm }' }
%board-card{ "v-for" => "(issue, index) in orderedIssues",
"ref" => "issue",
":index" => "index",
":list" => "list",
":issue" => "issue",
":issue-link-base" => "issueLinkBase",
":disabled" => "disabled",
"key" => "id" }
%li.board-list-count.text-center{ "v-if" => "showCount" }
= icon("spinner spin", "v-show" => "list.loadingMore" )
%span{ "v-if" => "list.issues.length === list.issuesSize" }
Showing all issues
%span{ "v-else" => true }
Showing {{ list.issues.length }} of {{ list.issuesSize }} issues
%board-card{ "inline-template" => true, %li.card{ ":class" => '{ "user-can-drag": !disabled && issue.id, "is-disabled": disabled || !issue.id, "is-active": issueDetailVisible }',
"v-for" => "issue in issues | orderBy 'priority'", ":index" => "index",
"v-ref:issue" => true, "@mousedown" => "mouseDown",
":index" => "$index", "@mouseup" => "showIssue($event)" }
":list" => "list", %h4.card-title
":issue" => "issue", = icon("eye-slash", class: "confidential-icon", "v-if" => "issue.confidential")
":issue-link-base" => "issueLinkBase", %a{ ":href" => 'issueLinkBase + "/" + issue.id',
":disabled" => "disabled", ":title" => "issue.title" }
"track-by" => "id" } {{ issue.title }}
%li.card{ ":class" => "{ 'user-can-drag': !disabled && issue.id, 'is-disabled': disabled || !issue.id, 'is-active': issueDetailVisible }", .card-footer
":index" => "index", %span.card-number{ "v-if" => "issue.id" }
"@mousedown" => "mouseDown", = precede '#' do
"@mouseMove" => "mouseMove", {{ issue.id }}
"@mouseup" => "showIssue($event)" } %a.has-tooltip{ ":href" => "\"#{root_path}\" + issue.assignee.username",
%h4.card-title ":title" => '"Assigned to " + issue.assignee.name',
= icon("eye-slash", class: "confidential-icon", "v-if" => "issue.confidential") "v-if" => "issue.assignee",
%a{ ":href" => "issueLinkBase + '/' + issue.id", data: { container: 'body' } }
":title" => "issue.title" } %img.avatar.avatar-inline.s20{ ":src" => "issue.assignee.avatar", width: 20, height: 20 }
{{ issue.title }} %button.label.color-label.has-tooltip{ "v-for" => "label in issue.labels",
.card-footer type: "button",
%span.card-number{ "v-if" => "issue.id" } "v-if" => "(!list.label || label.id !== list.label.id)",
= precede '#' do "@click" => "filterByLabel(label, $event)",
{{ issue.id }} ":style" => "{ backgroundColor: label.color, color: label.textColor }",
%a.has-tooltip{ ":href" => "'#{root_path}' + issue.assignee.username", ":title" => "label.description",
":title" => "'Assigned to ' + issue.assignee.name", data: { container: 'body' } }
"v-if" => "issue.assignee", {{ label.title }}
data: { container: 'body' } }
%img.avatar.avatar-inline.s20{ ":src" => "issue.assignee.avatar", width: 20, height: 20 }
%button.label.color-label.has-tooltip{ "v-for" => "label in issue.labels",
type: "button",
"v-if" => "(!list.label || label.id !== list.label.id)",
"@click" => "filterByLabel(label, $event)",
":style" => "{ backgroundColor: label.color, color: label.textColor }",
":title" => "label.description",
data: { container: 'body' } }
{{ label.title }}
%board-sidebar{ "inline-template" => true, %board-sidebar{ "inline-template" => true,
":current-user" => "#{current_user.to_json(only: [:username, :id, :name], methods: [:avatar_url]) if current_user}" } ":current-user" => "#{current_user ? current_user.to_json(only: [:username, :id, :name], methods: [:avatar_url]) : {}}" }
%aside.right-sidebar.right-sidebar-expanded.issue-boards-sidebar{ "v-show" => "showSidebar" } %aside.right-sidebar.right-sidebar-expanded.issue-boards-sidebar{ "v-show" => "showSidebar" }
.issuable-sidebar .issuable-sidebar
.block.issuable-sidebar-header .block.issuable-sidebar-header
......
.block.assignee .block.assignee
.title.hide-collapsed .title.hide-collapsed
Assignee Assignee
= icon("spinner spin", class: "block-loading")
- if can?(current_user, :admin_issue, @project) - if can?(current_user, :admin_issue, @project)
= icon("spinner spin", class: "block-loading")
= link_to "Edit", "#", class: "edit-link pull-right" = link_to "Edit", "#", class: "edit-link pull-right"
.value.hide-collapsed .value.hide-collapsed
%span.assign-yourself.no-value{ "v-if" => "!issue.assignee" } %span.assign-yourself.no-value{ "v-if" => "!issue.assignee" }
......
.block.due_date .block.due_date
.title .title
Due date Due date
= icon("spinner spin", class: "block-loading")
- if can?(current_user, :admin_issue, @project) - if can?(current_user, :admin_issue, @project)
= icon("spinner spin", class: "block-loading")
= link_to "Edit", "#", class: "edit-link pull-right" = link_to "Edit", "#", class: "edit-link pull-right"
.value .value
.value-content .value-content
......
.block.labels .block.labels
.title .title
Labels Labels
= icon("spinner spin", class: "block-loading")
- if can?(current_user, :admin_issue, @project) - if can?(current_user, :admin_issue, @project)
= icon("spinner spin", class: "block-loading")
= link_to "Edit", "#", class: "edit-link pull-right" = link_to "Edit", "#", class: "edit-link pull-right"
.value.issuable-show-labels .value.issuable-show-labels
%span.no-value{ "v-if" => "issue.labels && issue.labels.length === 0" } %span.no-value{ "v-if" => "issue.labels && issue.labels.length === 0" }
......
.block.milestone .block.milestone
.title .title
Milestone Milestone
= icon("spinner spin", class: "block-loading")
- if can?(current_user, :admin_issue, @project) - if can?(current_user, :admin_issue, @project)
= icon("spinner spin", class: "block-loading")
= link_to "Edit", "#", class: "edit-link pull-right" = link_to "Edit", "#", class: "edit-link pull-right"
.value .value
%span.no-value{ "v-if" => "!issue.milestone" } %span.no-value{ "v-if" => "!issue.milestone" }
......
- @no_container = true = render "show"
- @content_class = "issue-boards-content"
- page_title "Boards"
- content_for :page_specific_javascripts do
= page_specific_javascript_tag('boards/boards_bundle.js')
= page_specific_javascript_tag('boards/test_utils/simulate_drag.js') if Rails.env.test?
= render "projects/issues/head"
= render 'shared/issuable/filter', type: :boards
#board-app.boards-app{ "v-cloak" => true, data: board_data }
.boards-list{ ":class" => "{ 'is-compact': detailIssueVisible }" }
.boards-app-loading.text-center{ "v-if" => "loading" }
= icon("spinner spin")
= render "projects/boards/components/board"
= render "projects/boards/components/sidebar"
- @no_container = true = render "show"
- @content_class = "issue-boards-content"
- page_title "Boards"
- content_for :page_specific_javascripts do
= page_specific_javascript_tag('boards/boards_bundle.js')
= page_specific_javascript_tag('boards/test_utils/simulate_drag.js') if Rails.env.test?
= render "projects/issues/head"
= render 'shared/issuable/filter', type: :boards
#board-app.boards-app{ "v-cloak" => true, data: board_data }
.boards-list{ ":class" => "{ 'is-compact': detailIssueVisible }" }
.boards-app-loading.text-center{ "v-if" => "loading" }
= icon("spinner spin")
= render "projects/boards/components/board"
= render "projects/boards/components/sidebar"
...@@ -26,6 +26,8 @@ ...@@ -26,6 +26,8 @@
= sort_title_oldest_updated = sort_title_oldest_updated
- if can? current_user, :push_code, @project - if can? current_user, :push_code, @project
= link_to namespace_project_merged_branches_path(@project.namespace, @project), class: 'btn btn-inverted btn-remove has-tooltip', title: "Delete all branches that are merged into '#{@project.repository.root_ref}'", method: :delete, data: { confirm: "Deleting the merged branches cannot be undone. Are you sure?", container: 'body' } do
Delete merged branches
= link_to new_namespace_project_branch_path(@project.namespace, @project), class: 'btn btn-create' do = link_to new_namespace_project_branch_path(@project.namespace, @project), class: 'btn btn-create' do
New branch New branch
......
...@@ -3,6 +3,9 @@ ...@@ -3,6 +3,9 @@
= ci_status_with_icon(@build.status) = ci_status_with_icon(@build.status)
Build Build
%strong ##{@build.id} %strong ##{@build.id}
in pipeline
= link_to pipeline_path(@build.pipeline) do
%strong ##{@build.pipeline.id}
for commit for commit
= link_to ci_status_path(@build.pipeline) do = link_to ci_status_path(@build.pipeline) do
%strong= @build.pipeline.short_sha %strong= @build.pipeline.short_sha
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
%tr %tr
%th Status %th Status
%th Build %th Build
%th Pipeline
- if admin - if admin
%th Project %th Project
%th Runner %th Runner
...@@ -19,6 +20,6 @@ ...@@ -19,6 +20,6 @@
%th Coverage %th Coverage
%th %th
= render partial: "projects/ci/builds/build", collection: builds, as: :build, locals: { commit_sha: true, ref: true, stage: true, allow_retry: true, coverage: admin || project.build_coverage_enabled?, admin: admin } = render partial: "projects/ci/builds/build", collection: builds, as: :build, locals: { commit_sha: true, ref: true, pipeline_link: true, stage: true, allow_retry: true, coverage: admin || project.build_coverage_enabled?, admin: admin }
= paginate builds, theme: 'gitlab' = paginate builds, theme: 'gitlab'
- @no_container = true - @no_container = true
- page_title "#{@build.name} (##{@build.id})", "Builds" - page_title "#{@build.name} (##{@build.id})", "Builds"
- header_title project_title(@project, "Builds", project_builds_path(@project)) - trace_with_state = @build.trace_with_state
= render "projects/pipelines/head", build_subnav: true = render "projects/pipelines/head", build_subnav: true
%div{ class: container_class } %div{ class: container_class }
......
- if !project.empty_repo? && can?(current_user, :download_code, project) - if !project.empty_repo? && can?(current_user, :download_code, project)
%span{class: 'hidden-xs hidden-sm download-button'} %span{class: 'download-button'}
.dropdown.inline .dropdown.inline
%button.btn{ 'data-toggle' => 'dropdown' } %button.btn{ 'data-toggle' => 'dropdown' }
= icon('download') = icon('download')
......
- if current_user - if current_user
.dropdown.inline.project-dropdown .dropdown.inline
%a.btn.dropdown-toggle{href: '#', "data-toggle" => "dropdown"} %a.btn.dropdown-toggle{href: '#', "data-toggle" => "dropdown"}
= icon('plus') = icon('plus')
= icon("caret-down") = icon("caret-down")
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
- ref = local_assigns.fetch(:ref, nil) - ref = local_assigns.fetch(:ref, nil)
- commit_sha = local_assigns.fetch(:commit_sha, nil) - commit_sha = local_assigns.fetch(:commit_sha, nil)
- retried = local_assigns.fetch(:retried, false) - retried = local_assigns.fetch(:retried, false)
- pipeline_link = local_assigns.fetch(:pipeline_link, false)
- stage = local_assigns.fetch(:stage, false) - stage = local_assigns.fetch(:stage, false)
- coverage = local_assigns.fetch(:coverage, false) - coverage = local_assigns.fetch(:coverage, false)
- allow_retry = local_assigns.fetch(:allow_retry, false) - allow_retry = local_assigns.fetch(:allow_retry, false)
...@@ -51,6 +52,16 @@ ...@@ -51,6 +52,16 @@
- if build.manual? - if build.manual?
%span.label.label-info manual %span.label.label-info manual
- if pipeline_link
%td
= link_to pipeline_path(build.pipeline) do
%span.pipeline-id ##{build.pipeline.id}
%span by
- if build.pipeline.user
= user_avatar(user: build.pipeline.user, size: 20)
- else
%span.monospace API
- if admin - if admin
%td %td
- if build.project - if build.project
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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