Merge remote-tracking branch 'origin/master' into zj/gitlab-ce-zj-auto-devops-table
This commit is contained in:
commit
12ddc28f84
|
@ -40,6 +40,7 @@ stages:
|
||||||
- test
|
- test
|
||||||
- post-test
|
- post-test
|
||||||
- pages
|
- pages
|
||||||
|
- post-cleanup
|
||||||
|
|
||||||
# Predefined scopes
|
# Predefined scopes
|
||||||
.dedicated-runner: &dedicated-runner
|
.dedicated-runner: &dedicated-runner
|
||||||
|
@ -153,8 +154,7 @@ stages:
|
||||||
- master@gitlab/gitlabhq
|
- master@gitlab/gitlabhq
|
||||||
- master@gitlab/gitlab-ee
|
- master@gitlab/gitlab-ee
|
||||||
|
|
||||||
# Trigger a package build on omnibus-gitlab repository
|
# Trigger a package build in omnibus-gitlab repository
|
||||||
|
|
||||||
build-package:
|
build-package:
|
||||||
image: ruby:2.3-alpine
|
image: ruby:2.3-alpine
|
||||||
before_script: []
|
before_script: []
|
||||||
|
@ -166,11 +166,47 @@ build-package:
|
||||||
cache: {}
|
cache: {}
|
||||||
when: manual
|
when: manual
|
||||||
script:
|
script:
|
||||||
- scripts/trigger-build
|
- scripts/trigger-build-omnibus
|
||||||
only:
|
only:
|
||||||
- //@gitlab-org/gitlab-ce
|
- //@gitlab-org/gitlab-ce
|
||||||
- //@gitlab-org/gitlab-ee
|
- //@gitlab-org/gitlab-ee
|
||||||
|
|
||||||
|
# Review docs base
|
||||||
|
.review-docs: &review-docs
|
||||||
|
image: ruby:2.4-alpine
|
||||||
|
before_script: []
|
||||||
|
services: []
|
||||||
|
variables:
|
||||||
|
SETUP_DB: "false"
|
||||||
|
USE_BUNDLE_INSTALL: "false"
|
||||||
|
cache: {}
|
||||||
|
when: manual
|
||||||
|
only:
|
||||||
|
- branches
|
||||||
|
|
||||||
|
# Trigger a docs build in gitlab-docs
|
||||||
|
# Useful to preview the docs changes live
|
||||||
|
review-docs-deploy:
|
||||||
|
<<: *review-docs
|
||||||
|
stage: build
|
||||||
|
environment:
|
||||||
|
name: review-docs/$CI_COMMIT_REF_NAME
|
||||||
|
on_stop: review-docs-cleanup
|
||||||
|
script:
|
||||||
|
- gem install gitlab --no-doc
|
||||||
|
- scripts/trigger-build-docs deploy
|
||||||
|
|
||||||
|
# Cleanup remote environment of gitlab-docs
|
||||||
|
review-docs-cleanup:
|
||||||
|
<<: *review-docs
|
||||||
|
stage: post-cleanup
|
||||||
|
environment:
|
||||||
|
name: review-docs/$CI_COMMIT_REF_NAME
|
||||||
|
action: stop
|
||||||
|
script:
|
||||||
|
- gem install gitlab --no-doc
|
||||||
|
- scripts/trigger-build-docs cleanup
|
||||||
|
|
||||||
# Retrieve knapsack and rspec_flaky reports
|
# Retrieve knapsack and rspec_flaky reports
|
||||||
retrieve-tests-metadata:
|
retrieve-tests-metadata:
|
||||||
<<: *tests-metadata-state
|
<<: *tests-metadata-state
|
||||||
|
|
|
@ -5,7 +5,7 @@ By submitting code as an individual you agree to the
|
||||||
By submitting code as an entity you agree to the
|
By submitting code as an entity you agree to the
|
||||||
[corporate contributor license agreement](doc/legal/corporate_contributor_license_agreement.md).
|
[corporate contributor license agreement](doc/legal/corporate_contributor_license_agreement.md).
|
||||||
|
|
||||||
_This notice should stay as the first item in the CONTRIBUTING.MD file._
|
_This notice should stay as the first item in the CONTRIBUTING.md file._
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ _This notice should stay as the first item in the CONTRIBUTING.MD file._
|
||||||
- [Workflow labels](#workflow-labels)
|
- [Workflow labels](#workflow-labels)
|
||||||
- [Type labels (~"feature proposal", ~bug, ~customer, etc.)](#type-labels-feature-proposal-bug-customer-etc)
|
- [Type labels (~"feature proposal", ~bug, ~customer, etc.)](#type-labels-feature-proposal-bug-customer-etc)
|
||||||
- [Subject labels (~wiki, ~"container registry", ~ldap, ~api, etc.)](#subject-labels-wiki-container-registry-ldap-api-etc)
|
- [Subject labels (~wiki, ~"container registry", ~ldap, ~api, etc.)](#subject-labels-wiki-container-registry-ldap-api-etc)
|
||||||
- [Team labels (~CI, ~Discussion, ~Edge, ~Platform, etc.)](#team-labels-ci-discussion-edge-platform-etc)
|
- [Team labels (~"CI/CD", ~Discussion, ~Edge, ~Platform, etc.)](#team-labels-ci-discussion-edge-platform-etc)
|
||||||
- [Priority labels (~Deliverable and ~Stretch)](#priority-labels-deliverable-and-stretch)
|
- [Priority labels (~Deliverable and ~Stretch)](#priority-labels-deliverable-and-stretch)
|
||||||
- [Label for community contributors (~"Accepting Merge Requests")](#label-for-community-contributors-accepting-merge-requests)
|
- [Label for community contributors (~"Accepting Merge Requests")](#label-for-community-contributors-accepting-merge-requests)
|
||||||
- [Implement design & UI elements](#implement-design--ui-elements)
|
- [Implement design & UI elements](#implement-design--ui-elements)
|
||||||
|
@ -115,7 +115,7 @@ Most issues will have labels for at least one of the following:
|
||||||
|
|
||||||
- Type: ~"feature proposal", ~bug, ~customer, etc.
|
- Type: ~"feature proposal", ~bug, ~customer, etc.
|
||||||
- Subject: ~wiki, ~"container registry", ~ldap, ~api, ~frontend, etc.
|
- Subject: ~wiki, ~"container registry", ~ldap, ~api, ~frontend, etc.
|
||||||
- Team: ~CI, ~Discussion, ~Edge, ~Platform, etc.
|
- Team: ~"CI/CD", ~Discussion, ~Edge, ~Platform, etc.
|
||||||
- Priority: ~Deliverable, ~Stretch
|
- Priority: ~Deliverable, ~Stretch
|
||||||
|
|
||||||
All labels, their meaning and priority are defined on the
|
All labels, their meaning and priority are defined on the
|
||||||
|
@ -157,13 +157,13 @@ Examples of subject labels are ~wiki, ~"container registry", ~ldap, ~api,
|
||||||
|
|
||||||
Subject labels are always all-lowercase.
|
Subject labels are always all-lowercase.
|
||||||
|
|
||||||
### Team labels (~CI, ~Discussion, ~Edge, ~Platform, etc.)
|
### Team labels (~"CI/CD", ~Discussion, ~Edge, ~Platform, etc.)
|
||||||
|
|
||||||
Team labels specify what team is responsible for this issue.
|
Team labels specify what team is responsible for this issue.
|
||||||
Assigning a team label makes sure issues get the attention of the appropriate
|
Assigning a team label makes sure issues get the attention of the appropriate
|
||||||
people.
|
people.
|
||||||
|
|
||||||
The current team labels are ~Build, ~CI, ~Discussion, ~Documentation, ~Edge,
|
The current team labels are ~Build, ~"CI/CD", ~Discussion, ~Documentation, ~Edge,
|
||||||
~Geo, ~Gitaly, ~Platform, ~Prometheus, ~Release, and ~"UX".
|
~Geo, ~Gitaly, ~Platform, ~Prometheus, ~Release, and ~"UX".
|
||||||
|
|
||||||
The descriptions on the [labels page][labels-page] explain what falls under the
|
The descriptions on the [labels page][labels-page] explain what falls under the
|
||||||
|
@ -217,11 +217,11 @@ After adding the ~"Accepting Merge Requests" label, we try to estimate the
|
||||||
[weight](#issue-weight) of the issue. We use issue weight to let contributors
|
[weight](#issue-weight) of the issue. We use issue weight to let contributors
|
||||||
know how difficult the issue is. Additionally:
|
know how difficult the issue is. Additionally:
|
||||||
|
|
||||||
- We advertise [~"Accepting Merge Requests" issues with weight < 5][up-for-grabs]
|
- We advertise ["Accepting Merge Requests" issues with weight < 5][up-for-grabs]
|
||||||
as suitable for people that have never contributed to GitLab before on the
|
as suitable for people that have never contributed to GitLab before on the
|
||||||
[Up For Grabs campaign](http://up-for-grabs.net)
|
[Up For Grabs campaign](http://up-for-grabs.net)
|
||||||
- We encourage people that have never contributed to any open source project to
|
- We encourage people that have never contributed to any open source project to
|
||||||
look for [~"Accepting Merge Requests" issues with a weight of 1][firt-timers]
|
look for ["Accepting Merge Requests" issues with a weight of 1][firt-timers]
|
||||||
|
|
||||||
[up-for-grabs]: https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name=Accepting+Merge+Requests&scope=all&sort=weight_asc&state=opened
|
[up-for-grabs]: https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name=Accepting+Merge+Requests&scope=all&sort=weight_asc&state=opened
|
||||||
[firt-timers]: https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name%5B%5D=Accepting+Merge+Requests&scope=all&sort=upvotes_desc&state=opened&weight=1
|
[firt-timers]: https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name%5B%5D=Accepting+Merge+Requests&scope=all&sort=upvotes_desc&state=opened&weight=1
|
||||||
|
|
|
@ -6,7 +6,8 @@ const Api = {
|
||||||
namespacesPath: '/api/:version/namespaces.json',
|
namespacesPath: '/api/:version/namespaces.json',
|
||||||
groupProjectsPath: '/api/:version/groups/:id/projects.json',
|
groupProjectsPath: '/api/:version/groups/:id/projects.json',
|
||||||
projectsPath: '/api/:version/projects.json',
|
projectsPath: '/api/:version/projects.json',
|
||||||
labelsPath: '/:namespace_path/:project_path/labels',
|
projectLabelsPath: '/:namespace_path/:project_path/labels',
|
||||||
|
groupLabelsPath: '/groups/:namespace_path/labels',
|
||||||
licensePath: '/api/:version/templates/licenses/:key',
|
licensePath: '/api/:version/templates/licenses/:key',
|
||||||
gitignorePath: '/api/:version/templates/gitignores/:key',
|
gitignorePath: '/api/:version/templates/gitignores/:key',
|
||||||
gitlabCiYmlPath: '/api/:version/templates/gitlab_ci_ymls/:key',
|
gitlabCiYmlPath: '/api/:version/templates/gitlab_ci_ymls/:key',
|
||||||
|
@ -74,9 +75,16 @@ const Api = {
|
||||||
},
|
},
|
||||||
|
|
||||||
newLabel(namespacePath, projectPath, data, callback) {
|
newLabel(namespacePath, projectPath, data, callback) {
|
||||||
const url = Api.buildUrl(Api.labelsPath)
|
let url;
|
||||||
|
|
||||||
|
if (projectPath) {
|
||||||
|
url = Api.buildUrl(Api.projectLabelsPath)
|
||||||
.replace(':namespace_path', namespacePath)
|
.replace(':namespace_path', namespacePath)
|
||||||
.replace(':project_path', projectPath);
|
.replace(':project_path', projectPath);
|
||||||
|
} else {
|
||||||
|
url = Api.buildUrl(Api.groupLabelsPath).replace(':namespace_path', namespacePath);
|
||||||
|
}
|
||||||
|
|
||||||
return $.ajax({
|
return $.ajax({
|
||||||
url,
|
url,
|
||||||
type: 'POST',
|
type: 'POST',
|
||||||
|
|
|
@ -53,7 +53,8 @@ $(() => {
|
||||||
data: {
|
data: {
|
||||||
state: Store.state,
|
state: Store.state,
|
||||||
loading: true,
|
loading: true,
|
||||||
endpoint: $boardApp.dataset.endpoint,
|
boardsEndpoint: $boardApp.dataset.boardsEndpoint,
|
||||||
|
listsEndpoint: $boardApp.dataset.listsEndpoint,
|
||||||
boardId: $boardApp.dataset.boardId,
|
boardId: $boardApp.dataset.boardId,
|
||||||
disabled: $boardApp.dataset.disabled === 'true',
|
disabled: $boardApp.dataset.disabled === 'true',
|
||||||
issueLinkBase: $boardApp.dataset.issueLinkBase,
|
issueLinkBase: $boardApp.dataset.issueLinkBase,
|
||||||
|
@ -68,7 +69,13 @@ $(() => {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
created () {
|
created () {
|
||||||
gl.boardService = new BoardService(this.endpoint, this.bulkUpdatePath, this.boardId);
|
gl.boardService = new BoardService({
|
||||||
|
boardsEndpoint: this.boardsEndpoint,
|
||||||
|
listsEndpoint: this.listsEndpoint,
|
||||||
|
bulkUpdatePath: this.bulkUpdatePath,
|
||||||
|
boardId: this.boardId,
|
||||||
|
});
|
||||||
|
Store.rootPath = this.boardsEndpoint;
|
||||||
|
|
||||||
this.filterManager = new FilteredSearchBoards(Store.filter, true);
|
this.filterManager = new FilteredSearchBoards(Store.filter, true);
|
||||||
this.filterManager.setup();
|
this.filterManager.setup();
|
||||||
|
@ -112,19 +119,21 @@ $(() => {
|
||||||
gl.IssueBoardsSearch = new Vue({
|
gl.IssueBoardsSearch = new Vue({
|
||||||
el: document.getElementById('js-add-list'),
|
el: document.getElementById('js-add-list'),
|
||||||
data: {
|
data: {
|
||||||
filters: Store.state.filters
|
filters: Store.state.filters,
|
||||||
},
|
},
|
||||||
mounted () {
|
mounted () {
|
||||||
gl.issueBoards.newListDropdownInit();
|
gl.issueBoards.newListDropdownInit();
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
gl.IssueBoardsModalAddBtn = new Vue({
|
gl.IssueBoardsModalAddBtn = new Vue({
|
||||||
mixins: [gl.issueBoards.ModalMixins],
|
mixins: [gl.issueBoards.ModalMixins],
|
||||||
el: document.getElementById('js-add-issues-btn'),
|
el: document.getElementById('js-add-issues-btn'),
|
||||||
data: {
|
data() {
|
||||||
|
return {
|
||||||
modal: ModalStore.store,
|
modal: ModalStore.store,
|
||||||
store: Store.state,
|
store: Store.state,
|
||||||
|
};
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
disabled() {
|
disabled() {
|
||||||
|
@ -133,6 +142,9 @@ $(() => {
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
disabled() {
|
disabled() {
|
||||||
|
if (!this.store) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
return !this.store.lists.filter(list => !list.preset).length;
|
return !this.store.lists.filter(list => !list.preset).length;
|
||||||
},
|
},
|
||||||
tooltipTitle() {
|
tooltipTitle() {
|
||||||
|
@ -145,7 +157,7 @@ $(() => {
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
updateTooltip() {
|
updateTooltip() {
|
||||||
const $tooltip = $(this.$el);
|
const $tooltip = $(this.$refs.addIssuesButton);
|
||||||
|
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
if (this.disabled) {
|
if (this.disabled) {
|
||||||
|
@ -165,16 +177,19 @@ $(() => {
|
||||||
this.updateTooltip();
|
this.updateTooltip();
|
||||||
},
|
},
|
||||||
template: `
|
template: `
|
||||||
|
<div class="board-extra-actions">
|
||||||
<button
|
<button
|
||||||
class="btn btn-create pull-right prepend-left-10"
|
class="btn btn-create prepend-left-10"
|
||||||
type="button"
|
type="button"
|
||||||
data-placement="bottom"
|
data-placement="bottom"
|
||||||
|
ref="addIssuesButton"
|
||||||
:class="{ 'disabled': disabled }"
|
:class="{ 'disabled': disabled }"
|
||||||
:title="tooltipTitle"
|
:title="tooltipTitle"
|
||||||
:aria-disabled="disabled"
|
:aria-disabled="disabled"
|
||||||
@click="openModal">
|
@click="openModal">
|
||||||
Add issues
|
Add issues
|
||||||
</button>
|
</button>
|
||||||
|
</div>
|
||||||
`,
|
`,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -77,7 +77,7 @@ export default {
|
||||||
this.showIssueForm = !this.showIssueForm;
|
this.showIssueForm = !this.showIssueForm;
|
||||||
},
|
},
|
||||||
onScroll() {
|
onScroll() {
|
||||||
if ((this.scrollTop() > this.scrollHeight() - this.scrollOffset) && !this.list.loadingMore) {
|
if (!this.loadingMore && (this.scrollTop() > this.scrollHeight() - this.scrollOffset)) {
|
||||||
this.loadNextPage();
|
this.loadNextPage();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -165,11 +165,9 @@ export default {
|
||||||
v-if="loading">
|
v-if="loading">
|
||||||
<loading-icon />
|
<loading-icon />
|
||||||
</div>
|
</div>
|
||||||
<transition name="slide-down">
|
|
||||||
<board-new-issue
|
<board-new-issue
|
||||||
:list="list"
|
:list="list"
|
||||||
v-if="list.type !== 'closed' && showIssueForm"/>
|
v-if="list.type !== 'closed' && showIssueForm"/>
|
||||||
</transition>
|
|
||||||
<ul
|
<ul
|
||||||
class="board-list"
|
class="board-list"
|
||||||
v-show="!loading"
|
v-show="!loading"
|
||||||
|
|
|
@ -6,7 +6,10 @@ const Store = gl.issueBoards.BoardsStore;
|
||||||
export default {
|
export default {
|
||||||
name: 'BoardNewIssue',
|
name: 'BoardNewIssue',
|
||||||
props: {
|
props: {
|
||||||
list: Object,
|
list: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -64,10 +64,13 @@ gl.issueBoards.IssueCardInner = Vue.extend({
|
||||||
return this.issue.assignees.length > this.numberOverLimit;
|
return this.issue.assignees.length > this.numberOverLimit;
|
||||||
},
|
},
|
||||||
cardUrl() {
|
cardUrl() {
|
||||||
return `${this.issueLinkBase}/${this.issue.id}`;
|
return `${this.issueLinkBase}/${this.issue.iid}`;
|
||||||
},
|
},
|
||||||
issueId() {
|
issueId() {
|
||||||
return `#${this.issue.id}`;
|
if (this.issue.iid) {
|
||||||
|
return `#${this.issue.iid}`;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
},
|
},
|
||||||
showLabelFooter() {
|
showLabelFooter() {
|
||||||
return this.issue.labels.find(l => this.showLabel(l)) !== undefined;
|
return this.issue.labels.find(l => this.showLabel(l)) !== undefined;
|
||||||
|
@ -143,7 +146,7 @@ gl.issueBoards.IssueCardInner = Vue.extend({
|
||||||
:title="issue.title">{{ issue.title }}</a>
|
:title="issue.title">{{ issue.title }}</a>
|
||||||
<span
|
<span
|
||||||
class="card-number"
|
class="card-number"
|
||||||
v-if="issue.id"
|
v-if="issueId"
|
||||||
>
|
>
|
||||||
{{ issueId }}
|
{{ issueId }}
|
||||||
</span>
|
</span>
|
||||||
|
|
|
@ -29,7 +29,7 @@ gl.issueBoards.ModalFooter = Vue.extend({
|
||||||
const firstListIndex = 1;
|
const firstListIndex = 1;
|
||||||
const list = this.modal.selectedList || this.state.lists[firstListIndex];
|
const list = this.modal.selectedList || this.state.lists[firstListIndex];
|
||||||
const selectedIssues = ModalStore.getSelectedIssues();
|
const selectedIssues = ModalStore.getSelectedIssues();
|
||||||
const issueIds = selectedIssues.map(issue => issue.globalId);
|
const issueIds = selectedIssues.map(issue => issue.id);
|
||||||
|
|
||||||
// Post the data to the backend
|
// Post the data to the backend
|
||||||
gl.boardService.bulkUpdate(issueIds, {
|
gl.boardService.bulkUpdate(issueIds, {
|
||||||
|
|
|
@ -27,7 +27,7 @@ gl.issueBoards.newListDropdownInit = () => {
|
||||||
|
|
||||||
$this.glDropdown({
|
$this.glDropdown({
|
||||||
data(term, callback) {
|
data(term, callback) {
|
||||||
$.get($this.attr('data-labels'))
|
$.get($this.attr('data-list-labels-path'))
|
||||||
.then((resp) => {
|
.then((resp) => {
|
||||||
callback(resp);
|
callback(resp);
|
||||||
});
|
});
|
||||||
|
|
|
@ -18,17 +18,33 @@ gl.issueBoards.RemoveIssueBtn = Vue.extend({
|
||||||
type: Object,
|
type: Object,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
|
issueUpdate: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
updateUrl() {
|
||||||
|
return this.issueUpdate;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
removeIssue() {
|
removeIssue() {
|
||||||
const issue = this.issue;
|
const issue = this.issue;
|
||||||
const lists = issue.getLists();
|
const lists = issue.getLists();
|
||||||
const labelIds = lists.map(list => list.label.id);
|
const listLabelIds = lists.map(list => list.label.id);
|
||||||
|
let labelIds = this.issue.labels
|
||||||
// Post the remove data
|
.map(label => label.id)
|
||||||
gl.boardService.bulkUpdate([issue.globalId], {
|
.filter(id => !listLabelIds.includes(id));
|
||||||
remove_label_ids: labelIds,
|
if (labelIds.length === 0) {
|
||||||
}).catch(() => {
|
labelIds = [''];
|
||||||
|
}
|
||||||
|
const data = {
|
||||||
|
issue: {
|
||||||
|
label_ids: labelIds,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
Vue.http.patch(this.updateUrl, data).catch(() => {
|
||||||
new Flash('Failed to remove issue from board, please try again.', 'alert');
|
new Flash('Failed to remove issue from board, please try again.', 'alert');
|
||||||
|
|
||||||
lists.forEach((list) => {
|
lists.forEach((list) => {
|
||||||
|
|
|
@ -7,8 +7,8 @@ import Vue from 'vue';
|
||||||
|
|
||||||
class ListIssue {
|
class ListIssue {
|
||||||
constructor (obj, defaultAvatar) {
|
constructor (obj, defaultAvatar) {
|
||||||
this.globalId = obj.id;
|
this.id = obj.id;
|
||||||
this.id = obj.iid;
|
this.iid = obj.iid;
|
||||||
this.title = obj.title;
|
this.title = obj.title;
|
||||||
this.confidential = obj.confidential;
|
this.confidential = obj.confidential;
|
||||||
this.dueDate = obj.due_date;
|
this.dueDate = obj.due_date;
|
||||||
|
|
|
@ -4,6 +4,7 @@ class ListLabel {
|
||||||
constructor (obj) {
|
constructor (obj) {
|
||||||
this.id = obj.id;
|
this.id = obj.id;
|
||||||
this.title = obj.title;
|
this.title = obj.title;
|
||||||
|
this.type = obj.type;
|
||||||
this.color = obj.color;
|
this.color = obj.color;
|
||||||
this.textColor = obj.text_color;
|
this.textColor = obj.text_color;
|
||||||
this.description = obj.description;
|
this.description = obj.description;
|
||||||
|
|
|
@ -110,11 +110,13 @@ class List {
|
||||||
return gl.boardService.newIssue(this.id, issue)
|
return gl.boardService.newIssue(this.id, issue)
|
||||||
.then(resp => resp.json())
|
.then(resp => resp.json())
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
issue.id = data.iid;
|
issue.id = data.id;
|
||||||
|
issue.iid = data.iid;
|
||||||
|
issue.project = data.project;
|
||||||
|
|
||||||
if (this.issuesSize > 1) {
|
if (this.issuesSize > 1) {
|
||||||
const moveBeforeIid = this.issues[1].id;
|
const moveBeforeId = this.issues[1].id;
|
||||||
gl.boardService.moveIssue(issue.id, null, null, null, moveBeforeIid);
|
gl.boardService.moveIssue(issue.id, null, null, null, moveBeforeId);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -126,19 +128,19 @@ class List {
|
||||||
}
|
}
|
||||||
|
|
||||||
addIssue (issue, listFrom, newIndex) {
|
addIssue (issue, listFrom, newIndex) {
|
||||||
let moveBeforeIid = null;
|
let moveBeforeId = null;
|
||||||
let moveAfterIid = null;
|
let moveAfterId = null;
|
||||||
|
|
||||||
if (!this.findIssue(issue.id)) {
|
if (!this.findIssue(issue.id)) {
|
||||||
if (newIndex !== undefined) {
|
if (newIndex !== undefined) {
|
||||||
this.issues.splice(newIndex, 0, issue);
|
this.issues.splice(newIndex, 0, issue);
|
||||||
|
|
||||||
if (this.issues[newIndex - 1]) {
|
if (this.issues[newIndex - 1]) {
|
||||||
moveBeforeIid = this.issues[newIndex - 1].id;
|
moveBeforeId = this.issues[newIndex - 1].id;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.issues[newIndex + 1]) {
|
if (this.issues[newIndex + 1]) {
|
||||||
moveAfterIid = this.issues[newIndex + 1].id;
|
moveAfterId = this.issues[newIndex + 1].id;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.issues.push(issue);
|
this.issues.push(issue);
|
||||||
|
@ -151,30 +153,30 @@ class List {
|
||||||
if (listFrom) {
|
if (listFrom) {
|
||||||
this.issuesSize += 1;
|
this.issuesSize += 1;
|
||||||
|
|
||||||
this.updateIssueLabel(issue, listFrom, moveBeforeIid, moveAfterIid);
|
this.updateIssueLabel(issue, listFrom, moveBeforeId, moveAfterId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
moveIssue (issue, oldIndex, newIndex, moveBeforeIid, moveAfterIid) {
|
moveIssue (issue, oldIndex, newIndex, moveBeforeId, moveAfterId) {
|
||||||
this.issues.splice(oldIndex, 1);
|
this.issues.splice(oldIndex, 1);
|
||||||
this.issues.splice(newIndex, 0, issue);
|
this.issues.splice(newIndex, 0, issue);
|
||||||
|
|
||||||
gl.boardService.moveIssue(issue.id, null, null, moveBeforeIid, moveAfterIid)
|
gl.boardService.moveIssue(issue.id, null, null, moveBeforeId, moveAfterId)
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
// TODO: handle request error
|
// TODO: handle request error
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
updateIssueLabel(issue, listFrom, moveBeforeIid, moveAfterIid) {
|
updateIssueLabel(issue, listFrom, moveBeforeId, moveAfterId) {
|
||||||
gl.boardService.moveIssue(issue.id, listFrom.id, this.id, moveBeforeIid, moveAfterIid)
|
gl.boardService.moveIssue(issue.id, listFrom.id, this.id, moveBeforeId, moveAfterId)
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
// TODO: handle request error
|
// TODO: handle request error
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
findIssue (id) {
|
findIssue (id) {
|
||||||
return this.issues.filter(issue => issue.id === id)[0];
|
return this.issues.find(issue => issue.id === id);
|
||||||
}
|
}
|
||||||
|
|
||||||
removeIssue (removeIssue) {
|
removeIssue (removeIssue) {
|
||||||
|
|
|
@ -3,21 +3,21 @@
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
|
|
||||||
class BoardService {
|
class BoardService {
|
||||||
constructor (root, bulkUpdatePath, boardId) {
|
constructor ({ boardsEndpoint, listsEndpoint, bulkUpdatePath, boardId }) {
|
||||||
this.boards = Vue.resource(`${root}{/id}.json`, {}, {
|
this.boards = Vue.resource(`${boardsEndpoint}{/id}.json`, {}, {
|
||||||
issues: {
|
issues: {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
url: `${root}/${boardId}/issues.json`
|
url: `${gon.relative_url_root}/boards/${boardId}/issues.json`,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.lists = Vue.resource(`${root}/${boardId}/lists{/id}`, {}, {
|
this.lists = Vue.resource(`${listsEndpoint}{/id}`, {}, {
|
||||||
generate: {
|
generate: {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
url: `${root}/${boardId}/lists/generate.json`
|
url: `${listsEndpoint}/generate.json`
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.issue = Vue.resource(`${root}/${boardId}/issues{/id}`, {});
|
this.issue = Vue.resource(`${gon.relative_url_root}/boards/${boardId}/issues{/id}`, {});
|
||||||
this.issues = Vue.resource(`${root}/${boardId}/lists{/id}/issues`, {}, {
|
this.issues = Vue.resource(`${listsEndpoint}{/id}/issues`, {}, {
|
||||||
bulkUpdate: {
|
bulkUpdate: {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
url: bulkUpdatePath,
|
url: bulkUpdatePath,
|
||||||
|
@ -60,12 +60,12 @@ class BoardService {
|
||||||
return this.issues.get(data);
|
return this.issues.get(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
moveIssue (id, from_list_id = null, to_list_id = null, move_before_iid = null, move_after_iid = null) {
|
moveIssue (id, from_list_id = null, to_list_id = null, move_before_id = null, move_after_id = null) {
|
||||||
return this.issue.update({ id }, {
|
return this.issue.update({ id }, {
|
||||||
from_list_id,
|
from_list_id,
|
||||||
to_list_id,
|
to_list_id,
|
||||||
move_before_iid,
|
move_before_id,
|
||||||
move_after_iid,
|
move_after_id,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,8 +21,10 @@ let headerHeight = 50;
|
||||||
|
|
||||||
export const getHeaderHeight = () => headerHeight;
|
export const getHeaderHeight = () => headerHeight;
|
||||||
|
|
||||||
|
export const isSidebarCollapsed = () => sidebar && sidebar.classList.contains('sidebar-icons-only');
|
||||||
|
|
||||||
export const canShowActiveSubItems = (el) => {
|
export const canShowActiveSubItems = (el) => {
|
||||||
if (el.classList.contains('active') && (sidebar && !sidebar.classList.contains('sidebar-icons-only'))) {
|
if (el.classList.contains('active') && !isSidebarCollapsed()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,12 +102,13 @@ export const moveSubItemsToPosition = (el, subItems) => {
|
||||||
|
|
||||||
export const showSubLevelItems = (el) => {
|
export const showSubLevelItems = (el) => {
|
||||||
const subItems = el.querySelector('.sidebar-sub-level-items');
|
const subItems = el.querySelector('.sidebar-sub-level-items');
|
||||||
|
const isIconOnly = subItems && subItems.classList.contains('is-fly-out-only');
|
||||||
|
|
||||||
if (!canShowSubItems() || !canShowActiveSubItems(el)) return;
|
if (!canShowSubItems() || !canShowActiveSubItems(el)) return;
|
||||||
|
|
||||||
el.classList.add(IS_OVER_CLASS);
|
el.classList.add(IS_OVER_CLASS);
|
||||||
|
|
||||||
if (!subItems) return;
|
if (!subItems || (!isSidebarCollapsed() && isIconOnly)) return;
|
||||||
|
|
||||||
subItems.style.display = 'block';
|
subItems.style.display = 'block';
|
||||||
el.classList.add(IS_SHOWING_FLY_OUT_CLASS);
|
el.classList.add(IS_SHOWING_FLY_OUT_CLASS);
|
||||||
|
|
|
@ -73,7 +73,7 @@ class Issue {
|
||||||
$(document).trigger('issuable:change', isClosed);
|
$(document).trigger('issuable:change', isClosed);
|
||||||
this.toggleCloseReopenButton(isClosed);
|
this.toggleCloseReopenButton(isClosed);
|
||||||
|
|
||||||
let numProjectIssues = Number(projectIssuesCounter.text().replace(/[^\d]/, ''));
|
let numProjectIssues = Number(projectIssuesCounter.first().text().trim().replace(/[^\d]/, ''));
|
||||||
numProjectIssues = isClosed ? numProjectIssues - 1 : numProjectIssues + 1;
|
numProjectIssues = isClosed ? numProjectIssues - 1 : numProjectIssues + 1;
|
||||||
projectIssuesCounter.text(gl.text.addDelimiter(numProjectIssues));
|
projectIssuesCounter.text(gl.text.addDelimiter(numProjectIssues));
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
@import "framework/flash";
|
@import "framework/flash";
|
||||||
@import "framework/forms";
|
@import "framework/forms";
|
||||||
@import "framework/gfm";
|
@import "framework/gfm";
|
||||||
|
@import "framework/gitlab-theme";
|
||||||
@import "framework/header";
|
@import "framework/header";
|
||||||
@import "framework/highlight";
|
@import "framework/highlight";
|
||||||
@import "framework/issue_box";
|
@import "framework/issue_box";
|
||||||
|
|
|
@ -412,11 +412,12 @@ table {
|
||||||
|
|
||||||
.gl-accessibility {
|
.gl-accessibility {
|
||||||
&:focus {
|
&:focus {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
top: 1px;
|
top: 1px;
|
||||||
left: 1px;
|
left: 1px;
|
||||||
width: auto;
|
width: auto;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
line-height: 50px;
|
|
||||||
padding: 0 10px;
|
padding: 0 10px;
|
||||||
clip: auto;
|
clip: auto;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
|
|
@ -183,7 +183,7 @@
|
||||||
width: auto;
|
width: auto;
|
||||||
top: 100%;
|
top: 100%;
|
||||||
left: 0;
|
left: 0;
|
||||||
z-index: 200;
|
z-index: 300;
|
||||||
min-width: 240px;
|
min-width: 240px;
|
||||||
max-width: 500px;
|
max-width: 500px;
|
||||||
margin-top: 2px;
|
margin-top: 2px;
|
||||||
|
@ -837,17 +837,30 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (max-width: $screen-xs-max) {
|
||||||
|
.navbar-gitlab {
|
||||||
|
li.header-projects,
|
||||||
|
li.header-more,
|
||||||
|
li.header-new,
|
||||||
|
li.header-user {
|
||||||
|
position: static;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
header.navbar-gitlab .dropdown {
|
||||||
|
.dropdown-menu,
|
||||||
|
.dropdown-menu-nav {
|
||||||
|
width: 100%;
|
||||||
|
min-width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@include new-style-dropdown('.breadcrumbs-list .dropdown ');
|
@include new-style-dropdown('.breadcrumbs-list .dropdown ');
|
||||||
@include new-style-dropdown('.js-namespace-select + ');
|
@include new-style-dropdown('.js-namespace-select + ');
|
||||||
|
|
||||||
header.navbar-gitlab-new .header-content .dropdown-menu.projects-dropdown-menu {
|
header.navbar-gitlab-new .header-content .dropdown-menu.projects-dropdown-menu {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|
||||||
@media (max-width: $screen-xs-max) {
|
|
||||||
display: table;
|
|
||||||
left: -50px;
|
|
||||||
min-width: 300px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.projects-dropdown-container {
|
.projects-dropdown-container {
|
||||||
|
|
|
@ -0,0 +1,265 @@
|
||||||
|
/**
|
||||||
|
* Styles the GitLab application with a specific color theme
|
||||||
|
*/
|
||||||
|
|
||||||
|
@mixin gitlab-theme($color-100, $color-200, $color-500, $color-700, $color-800, $color-900, $color-alternate) {
|
||||||
|
// Header
|
||||||
|
|
||||||
|
header.navbar-gitlab-new {
|
||||||
|
background: linear-gradient(to right, $color-900, $color-800);
|
||||||
|
|
||||||
|
.navbar-collapse {
|
||||||
|
color: $color-200;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container-fluid {
|
||||||
|
.navbar-toggle {
|
||||||
|
border-left: 1px solid lighten($color-700, 10%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar-sub-nav,
|
||||||
|
.navbar-nav {
|
||||||
|
> li {
|
||||||
|
> a:hover,
|
||||||
|
> a:focus {
|
||||||
|
background-color: rgba($color-200, .2);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active > a,
|
||||||
|
&.dropdown.open > a {
|
||||||
|
color: $color-900;
|
||||||
|
background-color: $color-alternate;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
fill: currentColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.line-separator {
|
||||||
|
border-left: 1px solid rgba($color-200, .2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar-sub-nav {
|
||||||
|
color: $color-200;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav {
|
||||||
|
> li {
|
||||||
|
color: $color-200;
|
||||||
|
|
||||||
|
> a {
|
||||||
|
svg {
|
||||||
|
fill: $color-200;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.header-user-dropdown-toggle {
|
||||||
|
.header-user-avatar {
|
||||||
|
border-color: $color-200;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:focus {
|
||||||
|
@media (min-width: $screen-sm-min) {
|
||||||
|
background-color: rgba($color-200, .2);
|
||||||
|
}
|
||||||
|
|
||||||
|
svg {
|
||||||
|
fill: currentColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active > a,
|
||||||
|
&.dropdown.open > a {
|
||||||
|
color: $color-900;
|
||||||
|
background-color: $color-alternate;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
svg {
|
||||||
|
fill: $color-900;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.impersonated-user,
|
||||||
|
.impersonated-user:hover {
|
||||||
|
svg {
|
||||||
|
fill: $color-900;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
> a {
|
||||||
|
&:hover,
|
||||||
|
&:focus {
|
||||||
|
background-color: rgba($color-200, .2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.search {
|
||||||
|
form {
|
||||||
|
background-color: rgba($color-200, .2);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: rgba($color-200, .3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.location-badge {
|
||||||
|
color: $color-100;
|
||||||
|
background-color: rgba($color-200, .1);
|
||||||
|
border-right: 1px solid $color-800;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-input::placeholder {
|
||||||
|
color: rgba($color-200, .8);
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-input-wrap {
|
||||||
|
.search-icon,
|
||||||
|
.clear-icon {
|
||||||
|
color: rgba($color-200, .8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.search-active {
|
||||||
|
form {
|
||||||
|
background-color: $white-light;
|
||||||
|
}
|
||||||
|
|
||||||
|
.location-badge {
|
||||||
|
color: $gl-text-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-input-wrap {
|
||||||
|
.search-icon {
|
||||||
|
color: rgba($color-200, .8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-sign-in {
|
||||||
|
background-color: $color-100;
|
||||||
|
color: $color-900;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Sidebar
|
||||||
|
.nav-sidebar li.active {
|
||||||
|
box-shadow: inset 4px 0 0 $color-700;
|
||||||
|
|
||||||
|
> a {
|
||||||
|
color: $color-900;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg {
|
||||||
|
fill: $color-900;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
body {
|
||||||
|
&.ui_indigo {
|
||||||
|
@include gitlab-theme($indigo-100, $indigo-200, $indigo-500, $indigo-700, $indigo-800, $indigo-900, $white-light);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.ui_dark {
|
||||||
|
@include gitlab-theme($theme-gray-100, $theme-gray-200, $theme-gray-500, $theme-gray-700, $theme-gray-800, $theme-gray-900, $white-light);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.ui_blue {
|
||||||
|
@include gitlab-theme($theme-blue-100, $theme-blue-200, $theme-blue-500, $theme-blue-700, $theme-blue-800, $theme-blue-900, $white-light);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.ui_green {
|
||||||
|
@include gitlab-theme($theme-green-100, $theme-green-200, $theme-green-500, $theme-green-700, $theme-green-800, $theme-green-900, $white-light);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.ui_light {
|
||||||
|
@include gitlab-theme($theme-gray-900, $theme-gray-700, $theme-gray-800, $theme-gray-700, $theme-gray-700, $theme-gray-100, $theme-gray-700);
|
||||||
|
|
||||||
|
header.navbar-gitlab-new {
|
||||||
|
background: $theme-gray-100;
|
||||||
|
box-shadow: 0 2px 0 0 $border-color;
|
||||||
|
|
||||||
|
.logo-text svg {
|
||||||
|
fill: $theme-gray-900;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar-sub-nav,
|
||||||
|
.navbar-nav {
|
||||||
|
> li {
|
||||||
|
> a:hover,
|
||||||
|
> a:focus {
|
||||||
|
color: $theme-gray-900;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active > a {
|
||||||
|
color: $white-light;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: $white-light;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.container-fluid {
|
||||||
|
.navbar-toggle,
|
||||||
|
.navbar-toggle:hover {
|
||||||
|
color: $theme-gray-700;
|
||||||
|
border-left: 1px solid $theme-gray-200;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.search {
|
||||||
|
form {
|
||||||
|
background-color: $white-light;
|
||||||
|
box-shadow: inset 0 0 0 1px $border-color;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: $white-light;
|
||||||
|
box-shadow: inset 0 0 0 1px $blue-100;
|
||||||
|
|
||||||
|
.location-badge {
|
||||||
|
box-shadow: inset 0 0 0 1px $blue-100;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-input-wrap {
|
||||||
|
.search-icon {
|
||||||
|
color: $theme-gray-200;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.location-badge {
|
||||||
|
color: $theme-gray-700;
|
||||||
|
box-shadow: inset 0 0 0 1px $border-color;
|
||||||
|
background-color: $nav-badge-bg;
|
||||||
|
border-right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-sidebar li.active {
|
||||||
|
> a {
|
||||||
|
color: $theme-gray-900;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg {
|
||||||
|
fill: $theme-gray-900;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -111,7 +111,6 @@ header {
|
||||||
svg {
|
svg {
|
||||||
height: 16px;
|
height: 16px;
|
||||||
width: 23px;
|
width: 23px;
|
||||||
fill: currentColor;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -74,6 +74,8 @@ $red-700: #a62d19;
|
||||||
$red-800: #8b2615;
|
$red-800: #8b2615;
|
||||||
$red-900: #711e11;
|
$red-900: #711e11;
|
||||||
|
|
||||||
|
// GitLab themes
|
||||||
|
|
||||||
$indigo-50: #f7f7ff;
|
$indigo-50: #f7f7ff;
|
||||||
$indigo-100: #ebebfa;
|
$indigo-100: #ebebfa;
|
||||||
$indigo-200: #d1d1f0;
|
$indigo-200: #d1d1f0;
|
||||||
|
@ -86,6 +88,43 @@ $indigo-800: #393982;
|
||||||
$indigo-900: #292961;
|
$indigo-900: #292961;
|
||||||
$indigo-950: #1a1a40;
|
$indigo-950: #1a1a40;
|
||||||
|
|
||||||
|
$theme-gray-50: #fafafa;
|
||||||
|
$theme-gray-100: #f2f2f2;
|
||||||
|
$theme-gray-200: #dfdfdf;
|
||||||
|
$theme-gray-300: #cccccc;
|
||||||
|
$theme-gray-400: #bababa;
|
||||||
|
$theme-gray-500: #a7a7a7;
|
||||||
|
$theme-gray-600: #949494;
|
||||||
|
$theme-gray-700: #707070;
|
||||||
|
$theme-gray-800: #4f4f4f;
|
||||||
|
$theme-gray-900: #2e2e2e;
|
||||||
|
$theme-gray-950: #1f1f1f;
|
||||||
|
|
||||||
|
$theme-blue-50: #f4f8fc;
|
||||||
|
$theme-blue-100: #e6edf5;
|
||||||
|
$theme-blue-200: #c8d7e6;
|
||||||
|
$theme-blue-300: #97b3cf;
|
||||||
|
$theme-blue-400: #648cb4;
|
||||||
|
$theme-blue-500: #4a79a8;
|
||||||
|
$theme-blue-600: #3e6fa0;
|
||||||
|
$theme-blue-700: #305c88;
|
||||||
|
$theme-blue-800: #25496e;
|
||||||
|
$theme-blue-900: #1a3652;
|
||||||
|
$theme-blue-950: #0f2235;
|
||||||
|
|
||||||
|
$theme-green-50: #f2faf6;
|
||||||
|
$theme-green-100: #e4f3ea;
|
||||||
|
$theme-green-200: #c0dfcd;
|
||||||
|
$theme-green-300: #8ac2a1;
|
||||||
|
$theme-green-400: #52a274;
|
||||||
|
$theme-green-500: #35935c;
|
||||||
|
$theme-green-600: #288a50;
|
||||||
|
$theme-green-700: #1c7441;
|
||||||
|
$theme-green-800: #145d33;
|
||||||
|
$theme-green-900: #0d4524;
|
||||||
|
$theme-green-950: #072d16;
|
||||||
|
|
||||||
|
|
||||||
$black: #000;
|
$black: #000;
|
||||||
$black-transparent: rgba(0, 0, 0, 0.3);
|
$black-transparent: rgba(0, 0, 0, 0.3);
|
||||||
$almost-black: #242424;
|
$almost-black: #242424;
|
||||||
|
|
|
@ -9,10 +9,20 @@
|
||||||
|
|
||||||
header.navbar-gitlab-new {
|
header.navbar-gitlab-new {
|
||||||
color: $white-light;
|
color: $white-light;
|
||||||
background: linear-gradient(to right, $indigo-900, $indigo-800);
|
|
||||||
border-bottom: 0;
|
border-bottom: 0;
|
||||||
min-height: $new-navbar-height;
|
min-height: $new-navbar-height;
|
||||||
|
|
||||||
|
.logo-text {
|
||||||
|
line-height: initial;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
width: 55px;
|
||||||
|
height: 14px;
|
||||||
|
margin: 0;
|
||||||
|
fill: $white-light;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.header-content {
|
.header-content {
|
||||||
display: -webkit-flex;
|
display: -webkit-flex;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -38,10 +48,10 @@ header.navbar-gitlab-new {
|
||||||
|
|
||||||
img {
|
img {
|
||||||
height: 28px;
|
height: 28px;
|
||||||
margin-right: 10px;
|
margin-right: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
> a {
|
a {
|
||||||
display: -webkit-flex;
|
display: -webkit-flex;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
@ -54,22 +64,6 @@ header.navbar-gitlab-new {
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.logo-text {
|
|
||||||
line-height: initial;
|
|
||||||
|
|
||||||
svg {
|
|
||||||
width: 55px;
|
|
||||||
height: 14px;
|
|
||||||
margin: 0;
|
|
||||||
fill: $white-light;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover,
|
|
||||||
&:focus {
|
|
||||||
background-color: rgba($indigo-200, .2);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,7 +100,6 @@ header.navbar-gitlab-new {
|
||||||
|
|
||||||
.navbar-collapse {
|
.navbar-collapse {
|
||||||
padding-left: 0;
|
padding-left: 0;
|
||||||
color: $indigo-200;
|
|
||||||
box-shadow: 0;
|
box-shadow: 0;
|
||||||
|
|
||||||
@media (max-width: $screen-xs-max) {
|
@media (max-width: $screen-xs-max) {
|
||||||
|
@ -132,7 +125,6 @@ header.navbar-gitlab-new {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
color: currentColor;
|
color: currentColor;
|
||||||
border-left: 1px solid lighten($indigo-700, 10%);
|
|
||||||
|
|
||||||
&:hover,
|
&:hover,
|
||||||
&:focus,
|
&:focus,
|
||||||
|
@ -167,42 +159,27 @@ header.navbar-gitlab-new {
|
||||||
will-change: color;
|
will-change: color;
|
||||||
margin: 4px 2px;
|
margin: 4px 2px;
|
||||||
padding: 6px 8px;
|
padding: 6px 8px;
|
||||||
color: $indigo-200;
|
|
||||||
height: 32px;
|
height: 32px;
|
||||||
|
|
||||||
@media (max-width: $screen-xs-max) {
|
@media (max-width: $screen-xs-max) {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
svg {
|
|
||||||
fill: $indigo-200;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.header-user-dropdown-toggle {
|
&.header-user-dropdown-toggle {
|
||||||
margin-left: 2px;
|
margin-left: 2px;
|
||||||
|
|
||||||
.header-user-avatar {
|
.header-user-avatar {
|
||||||
border-color: $indigo-200;
|
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.header-new-dropdown-toggle {
|
&:hover,
|
||||||
margin-right: 0;
|
&:focus {
|
||||||
}
|
|
||||||
|
|
||||||
> a:hover,
|
|
||||||
> a:focus {
|
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
outline: 0;
|
outline: 0;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
color: $white-light;
|
color: $white-light;
|
||||||
|
|
||||||
@media (min-width: $screen-sm-min) {
|
|
||||||
background-color: rgba($indigo-200, .2);
|
|
||||||
}
|
|
||||||
|
|
||||||
svg {
|
svg {
|
||||||
fill: currentColor;
|
fill: currentColor;
|
||||||
}
|
}
|
||||||
|
@ -213,6 +190,11 @@ header.navbar-gitlab-new {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-new-dropdown-toggle {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.impersonated-user,
|
.impersonated-user,
|
||||||
.impersonated-user:hover {
|
.impersonated-user:hover {
|
||||||
|
@ -220,10 +202,6 @@ header.navbar-gitlab-new {
|
||||||
background-color: $white-light;
|
background-color: $white-light;
|
||||||
border-top-right-radius: 0;
|
border-top-right-radius: 0;
|
||||||
border-bottom-right-radius: 0;
|
border-bottom-right-radius: 0;
|
||||||
|
|
||||||
svg {
|
|
||||||
fill: $indigo-900;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.impersonation-btn,
|
.impersonation-btn,
|
||||||
|
@ -241,8 +219,6 @@ header.navbar-gitlab-new {
|
||||||
|
|
||||||
&.active > a,
|
&.active > a,
|
||||||
&.dropdown.open > a {
|
&.dropdown.open > a {
|
||||||
color: $indigo-900;
|
|
||||||
background-color: $white-light;
|
|
||||||
|
|
||||||
svg {
|
svg {
|
||||||
fill: currentColor;
|
fill: currentColor;
|
||||||
|
@ -256,7 +232,6 @@ header.navbar-gitlab-new {
|
||||||
display: -webkit-flex;
|
display: -webkit-flex;
|
||||||
display: flex;
|
display: flex;
|
||||||
margin: 0 0 0 6px;
|
margin: 0 0 0 6px;
|
||||||
color: $indigo-200;
|
|
||||||
|
|
||||||
.dropdown-chevron {
|
.dropdown-chevron {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
@ -274,17 +249,6 @@ header.navbar-gitlab-new {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
outline: 0;
|
outline: 0;
|
||||||
color: $white-light;
|
color: $white-light;
|
||||||
background-color: rgba($indigo-200, .2);
|
|
||||||
|
|
||||||
svg {
|
|
||||||
fill: currentColor;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.active > a,
|
|
||||||
&.dropdown.open > a {
|
|
||||||
color: $indigo-900;
|
|
||||||
background-color: $white-light;
|
|
||||||
|
|
||||||
svg {
|
svg {
|
||||||
fill: currentColor;
|
fill: currentColor;
|
||||||
|
@ -309,7 +273,6 @@ header.navbar-gitlab-new {
|
||||||
}
|
}
|
||||||
|
|
||||||
&.line-separator {
|
&.line-separator {
|
||||||
border-left: 1px solid rgba($indigo-200, .2);
|
|
||||||
margin: 8px;
|
margin: 8px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -339,17 +302,14 @@ header.navbar-gitlab-new {
|
||||||
height: 32px;
|
height: 32px;
|
||||||
border: 0;
|
border: 0;
|
||||||
border-radius: $border-radius-default;
|
border-radius: $border-radius-default;
|
||||||
background-color: rgba($indigo-200, .2);
|
|
||||||
transition: border-color ease-in-out 0.15s, background-color ease-in-out 0.15s;
|
transition: border-color ease-in-out 0.15s, background-color ease-in-out 0.15s;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: rgba($indigo-200, .3);
|
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.search-active form {
|
&.search-active form {
|
||||||
background-color: $white-light;
|
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
|
|
||||||
.search-input {
|
.search-input {
|
||||||
|
@ -377,43 +337,26 @@ header.navbar-gitlab-new {
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-input::placeholder {
|
.search-input::placeholder {
|
||||||
color: rgba($indigo-200, .8);
|
|
||||||
transition: color ease-in-out 0.15s;
|
transition: color ease-in-out 0.15s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.location-badge {
|
.location-badge {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
color: $indigo-100;
|
|
||||||
background-color: rgba($indigo-200, .1);
|
|
||||||
will-change: color;
|
|
||||||
margin: -4px 4px -4px -4px;
|
margin: -4px 4px -4px -4px;
|
||||||
line-height: 25px;
|
line-height: 25px;
|
||||||
padding: 4px 8px;
|
padding: 4px 8px;
|
||||||
border-radius: 2px 0 0 2px;
|
border-radius: 2px 0 0 2px;
|
||||||
border-right: 1px solid $indigo-800;
|
|
||||||
height: 32px;
|
height: 32px;
|
||||||
transition: border-color ease-in-out 0.15s;
|
transition: border-color ease-in-out 0.15s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-input-wrap {
|
|
||||||
.search-icon,
|
|
||||||
.clear-icon {
|
|
||||||
color: rgba($indigo-200, .8);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.search-active {
|
&.search-active {
|
||||||
.location-badge {
|
.location-badge {
|
||||||
color: $gl-text-color;
|
|
||||||
background-color: $nav-badge-bg;
|
background-color: $nav-badge-bg;
|
||||||
border-color: $border-color;
|
border-color: $border-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-input-wrap {
|
.search-input-wrap {
|
||||||
.search-icon {
|
|
||||||
color: rgba($indigo-200, .8);
|
|
||||||
}
|
|
||||||
|
|
||||||
.clear-icon {
|
.clear-icon {
|
||||||
color: $white-light;
|
color: $white-light;
|
||||||
}
|
}
|
||||||
|
@ -517,8 +460,6 @@ header.navbar-gitlab-new {
|
||||||
|
|
||||||
.btn-sign-in {
|
.btn-sign-in {
|
||||||
margin-top: 3px;
|
margin-top: 3px;
|
||||||
background-color: $indigo-100;
|
|
||||||
color: $indigo-900;
|
|
||||||
font-weight: $gl-font-weight-bold;
|
font-weight: $gl-font-weight-bold;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
|
|
|
@ -106,11 +106,8 @@ $new-sidebar-collapsed-width: 50px;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.badge,
|
.badge:not(.fly-out-badge),
|
||||||
.sidebar-context-title {
|
.sidebar-context-title,
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-item-name {
|
.nav-item-name {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
@ -118,6 +115,10 @@ $new-sidebar-collapsed-width: 50px;
|
||||||
.sidebar-top-level-items > li > a {
|
.sidebar-top-level-items > li > a {
|
||||||
min-height: 44px;
|
min-height: 44px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.fly-out-top-item {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.nav-sidebar-expanded {
|
&.nav-sidebar-expanded {
|
||||||
|
@ -154,16 +155,9 @@ $new-sidebar-collapsed-width: 50px;
|
||||||
}
|
}
|
||||||
|
|
||||||
li.active {
|
li.active {
|
||||||
box-shadow: inset 4px 0 0 $active-border;
|
|
||||||
|
|
||||||
> a {
|
> a {
|
||||||
color: $active-color;
|
|
||||||
font-weight: $gl-font-weight-bold;
|
font-weight: $gl-font-weight-bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
svg {
|
|
||||||
fill: $active-color;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: $screen-xs-max) {
|
@media (max-width: $screen-xs-max) {
|
||||||
|
@ -179,6 +173,10 @@ $new-sidebar-collapsed-width: 50px;
|
||||||
width: 16px;
|
width: 16px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.fly-out-top-item {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-sidebar-inner-scroll {
|
.nav-sidebar-inner-scroll {
|
||||||
|
@ -249,7 +247,7 @@ $new-sidebar-collapsed-width: 50px;
|
||||||
left: $new-sidebar-width;
|
left: $new-sidebar-width;
|
||||||
min-width: 150px;
|
min-width: 150px;
|
||||||
margin-top: -1px;
|
margin-top: -1px;
|
||||||
padding: 8px 1px;
|
padding: 4px 1px;
|
||||||
background-color: $white-light;
|
background-color: $white-light;
|
||||||
box-shadow: 2px 1px 3px $dropdown-shadow-color;
|
box-shadow: 2px 1px 3px $dropdown-shadow-color;
|
||||||
border: 1px solid $gray-darker;
|
border: 1px solid $gray-darker;
|
||||||
|
@ -270,6 +268,13 @@ $new-sidebar-collapsed-width: 50px;
|
||||||
margin-top: 1px;
|
margin-top: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.divider {
|
||||||
|
height: 1px;
|
||||||
|
margin: 4px -1px;
|
||||||
|
padding: 0;
|
||||||
|
background-color: $dropdown-divider-color;
|
||||||
|
}
|
||||||
|
|
||||||
> .active {
|
> .active {
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
|
|
||||||
|
@ -309,7 +314,7 @@ $new-sidebar-collapsed-width: 50px;
|
||||||
font-weight: $gl-font-weight-bold;
|
font-weight: $gl-font-weight-bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-sub-level-items {
|
.sidebar-sub-level-items:not(.is-fly-out-only) {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -407,6 +412,19 @@ $new-sidebar-collapsed-width: 50px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.fly-out-top-item {
|
||||||
|
> a {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fly-out-badge {
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.fly-out-top-item-name {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
// Mobile nav
|
// Mobile nav
|
||||||
|
|
||||||
|
|
|
@ -117,13 +117,12 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.board-title {
|
.board-title {
|
||||||
position: initial;
|
|
||||||
padding: 0;
|
padding: 0;
|
||||||
border-bottom: 0;
|
border-bottom: 0;
|
||||||
|
|
||||||
> span {
|
> span {
|
||||||
display: block;
|
display: block;
|
||||||
transform: rotate(90deg) translate(25px, 0);
|
transform: rotate(90deg) translate(35px, 10px);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,11 +150,18 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.board-header {
|
.board-header {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&.has-border::before {
|
||||||
|
border-top: 3px solid;
|
||||||
|
border-color: inherit;
|
||||||
border-top-left-radius: $border-radius-default;
|
border-top-left-radius: $border-radius-default;
|
||||||
border-top-right-radius: $border-radius-default;
|
border-top-right-radius: $border-radius-default;
|
||||||
|
content: '';
|
||||||
&.has-border {
|
position: absolute;
|
||||||
border-top: 3px solid;
|
width: calc(100% + 2px);
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
margin-top: -1px;
|
margin-top: -1px;
|
||||||
margin-right: -1px;
|
margin-right: -1px;
|
||||||
margin-left: -1px;
|
margin-left: -1px;
|
||||||
|
@ -176,12 +182,16 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.board-title {
|
.board-title {
|
||||||
position: relative;
|
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: $gl-padding;
|
padding: 12px $gl-padding;
|
||||||
padding-bottom: ($gl-padding + 3px);
|
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
border-bottom: 1px solid $border-color;
|
border-bottom: 1px solid $border-color;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.board-title-text {
|
||||||
|
margin-right: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.board-delete {
|
.board-delete {
|
||||||
|
@ -221,43 +231,10 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.slide-down-enter {
|
|
||||||
transform: translateY(-100%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.slide-down-enter-active {
|
|
||||||
transition: transform $fade-in-duration;
|
|
||||||
|
|
||||||
+ .board-list {
|
|
||||||
transform: translateY(-136px);
|
|
||||||
transition: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.slide-down-enter-to {
|
|
||||||
+ .board-list {
|
|
||||||
transform: translateY(0);
|
|
||||||
transition: transform $fade-in-duration ease;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.slide-down-leave {
|
|
||||||
transform: translateY(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
.slide-down-leave-active {
|
|
||||||
transition: all $fade-in-duration;
|
|
||||||
transform: translateY(-136px);
|
|
||||||
|
|
||||||
+ .board-list {
|
|
||||||
transition: transform $fade-in-duration ease;
|
|
||||||
transform: translateY(-136px);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.board-list-component {
|
.board-list-component {
|
||||||
height: calc(100% - 49px);
|
height: calc(100% - 49px);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.board-list {
|
.board-list {
|
||||||
|
@ -429,7 +406,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.board-new-issue-form {
|
.board-new-issue-form {
|
||||||
z-index: 1;
|
z-index: 4;
|
||||||
margin: 5px;
|
margin: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,67 @@
|
||||||
|
@mixin application-theme-preview($color-1, $color-2, $color-3, $color-4) {
|
||||||
|
.one {
|
||||||
|
background-color: $color-1;
|
||||||
|
border-top-left-radius: $border-radius-default;
|
||||||
|
}
|
||||||
|
|
||||||
|
.two {
|
||||||
|
background-color: $color-2;
|
||||||
|
border-top-right-radius: $border-radius-default;
|
||||||
|
}
|
||||||
|
|
||||||
|
.three {
|
||||||
|
background-color: $color-3;
|
||||||
|
border-bottom-left-radius: $border-radius-default;
|
||||||
|
}
|
||||||
|
|
||||||
|
.four {
|
||||||
|
background-color: $color-4;
|
||||||
|
border-bottom-right-radius: $border-radius-default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.application-theme {
|
||||||
|
label {
|
||||||
|
margin-right: 20px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview {
|
||||||
|
font-size: 0;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
|
||||||
|
&.indigo {
|
||||||
|
@include application-theme-preview($indigo-900, $indigo-700, $indigo-800, $indigo-500);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.dark {
|
||||||
|
@include application-theme-preview($theme-gray-900, $theme-gray-700, $theme-gray-800, $theme-gray-600);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.light {
|
||||||
|
@include application-theme-preview($theme-gray-600, $theme-gray-200, $theme-gray-400, $theme-gray-100);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.blue {
|
||||||
|
@include application-theme-preview($theme-blue-900, $theme-blue-700, $theme-blue-800, $theme-blue-500);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.green {
|
||||||
|
@include application-theme-preview($theme-green-900, $theme-green-700, $theme-green-800, $theme-green-500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-row {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quadrant {
|
||||||
|
display: inline-block;
|
||||||
|
height: 50px;
|
||||||
|
width: 80px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.syntax-theme {
|
.syntax-theme {
|
||||||
label {
|
label {
|
||||||
margin-right: 20px;
|
margin-right: 20px;
|
||||||
|
|
|
@ -166,7 +166,7 @@ input[type="checkbox"]:hover {
|
||||||
.dropdown-menu {
|
.dropdown-menu {
|
||||||
transition-duration: 100ms, 75ms;
|
transition-duration: 100ms, 75ms;
|
||||||
transition-delay: 75ms, 100ms;
|
transition-delay: 75ms, 100ms;
|
||||||
transform: translateY(13px);
|
transform: translateY(7px);
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -211,6 +211,7 @@ class Admin::UsersController < Admin::ApplicationController
|
||||||
:provider,
|
:provider,
|
||||||
:remember_me,
|
:remember_me,
|
||||||
:skype,
|
:skype,
|
||||||
|
:theme_id,
|
||||||
:twitter,
|
:twitter,
|
||||||
:username,
|
:username,
|
||||||
:website_url
|
:website_url
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
module Boards
|
||||||
|
class ApplicationController < ::ApplicationController
|
||||||
|
respond_to :json
|
||||||
|
|
||||||
|
rescue_from ActiveRecord::RecordNotFound, with: :record_not_found
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def board
|
||||||
|
@board ||= Board.find(params[:board_id])
|
||||||
|
end
|
||||||
|
|
||||||
|
def board_parent
|
||||||
|
@board_parent ||= board.parent
|
||||||
|
end
|
||||||
|
|
||||||
|
def record_not_found(exception)
|
||||||
|
render json: { error: exception.message }, status: :not_found
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,90 @@
|
||||||
|
module Boards
|
||||||
|
class IssuesController < Boards::ApplicationController
|
||||||
|
include BoardsResponses
|
||||||
|
|
||||||
|
before_action :authorize_read_issue, only: [:index]
|
||||||
|
before_action :authorize_create_issue, only: [:create]
|
||||||
|
before_action :authorize_update_issue, only: [:update]
|
||||||
|
skip_before_action :authenticate_user!, only: [:index]
|
||||||
|
|
||||||
|
def index
|
||||||
|
issues = Boards::Issues::ListService.new(board_parent, current_user, filter_params).execute
|
||||||
|
issues = issues.page(params[:page]).per(params[:per] || 20)
|
||||||
|
make_sure_position_is_set(issues)
|
||||||
|
|
||||||
|
render json: {
|
||||||
|
issues: serialize_as_json(issues.preload(:project)),
|
||||||
|
size: issues.total_count
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def create
|
||||||
|
service = Boards::Issues::CreateService.new(board_parent, project, current_user, issue_params)
|
||||||
|
issue = service.execute
|
||||||
|
|
||||||
|
if issue.valid?
|
||||||
|
render json: serialize_as_json(issue)
|
||||||
|
else
|
||||||
|
render json: issue.errors, status: :unprocessable_entity
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def update
|
||||||
|
service = Boards::Issues::MoveService.new(board_parent, current_user, move_params)
|
||||||
|
|
||||||
|
if service.execute(issue)
|
||||||
|
head :ok
|
||||||
|
else
|
||||||
|
head :unprocessable_entity
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def make_sure_position_is_set(issues)
|
||||||
|
issues.each do |issue|
|
||||||
|
issue.move_to_end && issue.save unless issue.relative_position
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def issue
|
||||||
|
@issue ||= issues_finder.execute.find(params[:id])
|
||||||
|
end
|
||||||
|
|
||||||
|
def filter_params
|
||||||
|
params.merge(board_id: params[:board_id], id: params[:list_id])
|
||||||
|
.reject { |_, value| value.nil? }
|
||||||
|
end
|
||||||
|
|
||||||
|
def issues_finder
|
||||||
|
IssuesFinder.new(current_user, project_id: board_parent.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
def project
|
||||||
|
board_parent
|
||||||
|
end
|
||||||
|
|
||||||
|
def move_params
|
||||||
|
params.permit(:board_id, :id, :from_list_id, :to_list_id, :move_before_id, :move_after_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
def issue_params
|
||||||
|
params.require(:issue)
|
||||||
|
.permit(:title, :milestone_id, :project_id)
|
||||||
|
.merge(board_id: params[:board_id], list_id: params[:list_id], request: request)
|
||||||
|
end
|
||||||
|
|
||||||
|
def serialize_as_json(resource)
|
||||||
|
resource.as_json(
|
||||||
|
labels: true,
|
||||||
|
only: [:id, :iid, :project_id, :title, :confidential, :due_date, :relative_position],
|
||||||
|
include: {
|
||||||
|
project: { only: [:id, :path] },
|
||||||
|
assignees: { only: [:id, :name, :username], methods: [:avatar_url] },
|
||||||
|
milestone: { only: [:id, :title] }
|
||||||
|
},
|
||||||
|
user: current_user
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,75 @@
|
||||||
|
module Boards
|
||||||
|
class ListsController < Boards::ApplicationController
|
||||||
|
include BoardsResponses
|
||||||
|
|
||||||
|
before_action :authorize_admin_list, only: [:create, :update, :destroy, :generate]
|
||||||
|
before_action :authorize_read_list, only: [:index]
|
||||||
|
skip_before_action :authenticate_user!, only: [:index]
|
||||||
|
|
||||||
|
def index
|
||||||
|
lists = Boards::Lists::ListService.new(board.parent, current_user).execute(board)
|
||||||
|
|
||||||
|
render json: serialize_as_json(lists)
|
||||||
|
end
|
||||||
|
|
||||||
|
def create
|
||||||
|
list = Boards::Lists::CreateService.new(board.parent, current_user, list_params).execute(board)
|
||||||
|
|
||||||
|
if list.valid?
|
||||||
|
render json: serialize_as_json(list)
|
||||||
|
else
|
||||||
|
render json: list.errors, status: :unprocessable_entity
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def update
|
||||||
|
list = board.lists.movable.find(params[:id])
|
||||||
|
service = Boards::Lists::MoveService.new(board_parent, current_user, move_params)
|
||||||
|
|
||||||
|
if service.execute(list)
|
||||||
|
head :ok
|
||||||
|
else
|
||||||
|
head :unprocessable_entity
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def destroy
|
||||||
|
list = board.lists.destroyable.find(params[:id])
|
||||||
|
service = Boards::Lists::DestroyService.new(board_parent, current_user)
|
||||||
|
|
||||||
|
if service.execute(list)
|
||||||
|
head :ok
|
||||||
|
else
|
||||||
|
head :unprocessable_entity
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def generate
|
||||||
|
service = Boards::Lists::GenerateService.new(board_parent, current_user)
|
||||||
|
|
||||||
|
if service.execute(board)
|
||||||
|
render json: serialize_as_json(board.lists.movable)
|
||||||
|
else
|
||||||
|
head :unprocessable_entity
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def list_params
|
||||||
|
params.require(:list).permit(:label_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
def move_params
|
||||||
|
params.require(:list).permit(:position)
|
||||||
|
end
|
||||||
|
|
||||||
|
def serialize_as_json(resource)
|
||||||
|
resource.as_json(
|
||||||
|
only: [:id, :list_type, :position],
|
||||||
|
methods: [:title],
|
||||||
|
label: true
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,42 @@
|
||||||
|
module BoardsResponses
|
||||||
|
def authorize_read_list
|
||||||
|
authorize_action_for!(board.parent, :read_list)
|
||||||
|
end
|
||||||
|
|
||||||
|
def authorize_read_issue
|
||||||
|
authorize_action_for!(board.parent, :read_issue)
|
||||||
|
end
|
||||||
|
|
||||||
|
def authorize_update_issue
|
||||||
|
authorize_action_for!(issue, :admin_issue)
|
||||||
|
end
|
||||||
|
|
||||||
|
def authorize_create_issue
|
||||||
|
authorize_action_for!(project, :admin_issue)
|
||||||
|
end
|
||||||
|
|
||||||
|
def authorize_admin_list
|
||||||
|
authorize_action_for!(board.parent, :admin_list)
|
||||||
|
end
|
||||||
|
|
||||||
|
def authorize_action_for!(resource, ability)
|
||||||
|
return render_403 unless can?(current_user, ability, resource)
|
||||||
|
end
|
||||||
|
|
||||||
|
def respond_with_boards
|
||||||
|
respond_with(@boards)
|
||||||
|
end
|
||||||
|
|
||||||
|
def respond_with_board
|
||||||
|
respond_with(@board)
|
||||||
|
end
|
||||||
|
|
||||||
|
def respond_with(resource)
|
||||||
|
respond_to do |format|
|
||||||
|
format.html
|
||||||
|
format.json do
|
||||||
|
render json: serialize_as_json(resource)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -35,7 +35,8 @@ class Profiles::PreferencesController < Profiles::ApplicationController
|
||||||
:color_scheme_id,
|
:color_scheme_id,
|
||||||
:layout,
|
:layout,
|
||||||
:dashboard,
|
:dashboard,
|
||||||
:project_view
|
:project_view,
|
||||||
|
:theme_id
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,15 +0,0 @@
|
||||||
module Projects
|
|
||||||
module Boards
|
|
||||||
class ApplicationController < Projects::ApplicationController
|
|
||||||
respond_to :json
|
|
||||||
|
|
||||||
rescue_from ActiveRecord::RecordNotFound, with: :record_not_found
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def record_not_found(exception)
|
|
||||||
render json: { error: exception.message }, status: :not_found
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,94 +0,0 @@
|
||||||
module Projects
|
|
||||||
module Boards
|
|
||||||
class IssuesController < Boards::ApplicationController
|
|
||||||
before_action :authorize_read_issue!, only: [:index]
|
|
||||||
before_action :authorize_create_issue!, only: [:create]
|
|
||||||
before_action :authorize_update_issue!, only: [:update]
|
|
||||||
|
|
||||||
def index
|
|
||||||
issues = ::Boards::Issues::ListService.new(project, current_user, filter_params).execute
|
|
||||||
issues = issues.page(params[:page]).per(params[:per] || 20)
|
|
||||||
make_sure_position_is_set(issues)
|
|
||||||
|
|
||||||
render json: {
|
|
||||||
issues: serialize_as_json(issues),
|
|
||||||
size: issues.total_count
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
def create
|
|
||||||
service = ::Boards::Issues::CreateService.new(project, current_user, issue_params)
|
|
||||||
issue = service.execute
|
|
||||||
|
|
||||||
if issue.valid?
|
|
||||||
render json: serialize_as_json(issue)
|
|
||||||
else
|
|
||||||
render json: issue.errors, status: :unprocessable_entity
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def update
|
|
||||||
service = ::Boards::Issues::MoveService.new(project, current_user, move_params)
|
|
||||||
|
|
||||||
if service.execute(issue)
|
|
||||||
head :ok
|
|
||||||
else
|
|
||||||
head :unprocessable_entity
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def make_sure_position_is_set(issues)
|
|
||||||
issues.each do |issue|
|
|
||||||
issue.move_to_end && issue.save unless issue.relative_position
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def issue
|
|
||||||
@issue ||=
|
|
||||||
IssuesFinder.new(current_user, project_id: project.id)
|
|
||||||
.execute
|
|
||||||
.where(iid: params[:id])
|
|
||||||
.first!
|
|
||||||
end
|
|
||||||
|
|
||||||
def authorize_read_issue!
|
|
||||||
return render_403 unless can?(current_user, :read_issue, project)
|
|
||||||
end
|
|
||||||
|
|
||||||
def authorize_create_issue!
|
|
||||||
return render_403 unless can?(current_user, :admin_issue, project)
|
|
||||||
end
|
|
||||||
|
|
||||||
def authorize_update_issue!
|
|
||||||
return render_403 unless can?(current_user, :update_issue, issue)
|
|
||||||
end
|
|
||||||
|
|
||||||
def filter_params
|
|
||||||
params.merge(board_id: params[:board_id], id: params[:list_id])
|
|
||||||
.reject { |_, value| value.nil? }
|
|
||||||
end
|
|
||||||
|
|
||||||
def move_params
|
|
||||||
params.permit(:board_id, :id, :from_list_id, :to_list_id, :move_before_iid, :move_after_iid)
|
|
||||||
end
|
|
||||||
|
|
||||||
def issue_params
|
|
||||||
params.require(:issue).permit(:title).merge(board_id: params[:board_id], list_id: params[:list_id], request: request)
|
|
||||||
end
|
|
||||||
|
|
||||||
def serialize_as_json(resource)
|
|
||||||
resource.as_json(
|
|
||||||
labels: true,
|
|
||||||
only: [:id, :iid, :title, :confidential, :due_date, :relative_position],
|
|
||||||
include: {
|
|
||||||
assignees: { only: [:id, :name, :username], methods: [:avatar_url] },
|
|
||||||
milestone: { only: [:id, :title] }
|
|
||||||
},
|
|
||||||
user: current_user
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,86 +0,0 @@
|
||||||
module Projects
|
|
||||||
module Boards
|
|
||||||
class ListsController < Boards::ApplicationController
|
|
||||||
before_action :authorize_admin_list!, only: [:create, :update, :destroy, :generate]
|
|
||||||
before_action :authorize_read_list!, only: [:index]
|
|
||||||
|
|
||||||
def index
|
|
||||||
lists = ::Boards::Lists::ListService.new(project, current_user).execute(board)
|
|
||||||
|
|
||||||
render json: serialize_as_json(lists)
|
|
||||||
end
|
|
||||||
|
|
||||||
def create
|
|
||||||
list = ::Boards::Lists::CreateService.new(project, current_user, list_params).execute(board)
|
|
||||||
|
|
||||||
if list.valid?
|
|
||||||
render json: serialize_as_json(list)
|
|
||||||
else
|
|
||||||
render json: list.errors, status: :unprocessable_entity
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def update
|
|
||||||
list = board.lists.movable.find(params[:id])
|
|
||||||
service = ::Boards::Lists::MoveService.new(project, current_user, move_params)
|
|
||||||
|
|
||||||
if service.execute(list)
|
|
||||||
head :ok
|
|
||||||
else
|
|
||||||
head :unprocessable_entity
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def destroy
|
|
||||||
list = board.lists.destroyable.find(params[:id])
|
|
||||||
service = ::Boards::Lists::DestroyService.new(project, current_user)
|
|
||||||
|
|
||||||
if service.execute(list)
|
|
||||||
head :ok
|
|
||||||
else
|
|
||||||
head :unprocessable_entity
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def generate
|
|
||||||
service = ::Boards::Lists::GenerateService.new(project, current_user)
|
|
||||||
|
|
||||||
if service.execute(board)
|
|
||||||
render json: serialize_as_json(board.lists.movable)
|
|
||||||
else
|
|
||||||
head :unprocessable_entity
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def authorize_admin_list!
|
|
||||||
return render_403 unless can?(current_user, :admin_list, project)
|
|
||||||
end
|
|
||||||
|
|
||||||
def authorize_read_list!
|
|
||||||
return render_403 unless can?(current_user, :read_list, project)
|
|
||||||
end
|
|
||||||
|
|
||||||
def board
|
|
||||||
@board ||= project.boards.find(params[:board_id])
|
|
||||||
end
|
|
||||||
|
|
||||||
def list_params
|
|
||||||
params.require(:list).permit(:label_id)
|
|
||||||
end
|
|
||||||
|
|
||||||
def move_params
|
|
||||||
params.require(:list).permit(:position)
|
|
||||||
end
|
|
||||||
|
|
||||||
def serialize_as_json(resource)
|
|
||||||
resource.as_json(
|
|
||||||
only: [:id, :list_type, :position],
|
|
||||||
methods: [:title],
|
|
||||||
label: true
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,32 +1,31 @@
|
||||||
class Projects::BoardsController < Projects::ApplicationController
|
class Projects::BoardsController < Projects::ApplicationController
|
||||||
|
include BoardsResponses
|
||||||
include IssuableCollections
|
include IssuableCollections
|
||||||
|
|
||||||
before_action :authorize_read_board!, only: [:index, :show]
|
before_action :authorize_read_board!, only: [:index, :show]
|
||||||
|
before_action :assign_endpoint_vars
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@boards = ::Boards::ListService.new(project, current_user).execute
|
@boards = Boards::ListService.new(project, current_user).execute
|
||||||
|
|
||||||
respond_to do |format|
|
respond_with_boards
|
||||||
format.html
|
|
||||||
format.json do
|
|
||||||
render json: serialize_as_json(@boards)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def show
|
def show
|
||||||
@board = project.boards.find(params[:id])
|
@board = project.boards.find(params[:id])
|
||||||
|
|
||||||
respond_to do |format|
|
respond_with_board
|
||||||
format.html
|
|
||||||
format.json do
|
|
||||||
render json: serialize_as_json(@board)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def assign_endpoint_vars
|
||||||
|
@boards_endpoint = project_boards_url(project)
|
||||||
|
@bulk_issues_path = bulk_update_project_issues_path(project)
|
||||||
|
@namespace_path = project.namespace.full_path
|
||||||
|
@labels_endpoint = project_labels_path(project)
|
||||||
|
end
|
||||||
|
|
||||||
def authorize_read_board!
|
def authorize_read_board!
|
||||||
return access_denied! unless can?(current_user, :read_board, project)
|
return access_denied! unless can?(current_user, :read_board, project)
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,15 +1,80 @@
|
||||||
module BoardsHelper
|
module BoardsHelper
|
||||||
def board_data
|
def board
|
||||||
board = @board || @boards.first
|
@board ||= @board || @boards.first
|
||||||
|
end
|
||||||
|
|
||||||
|
def board_data
|
||||||
{
|
{
|
||||||
endpoint: project_boards_path(@project),
|
boards_endpoint: @boards_endpoint,
|
||||||
|
lists_endpoint: board_lists_url(board),
|
||||||
board_id: board.id,
|
board_id: board.id,
|
||||||
disabled: "#{!can?(current_user, :admin_list, @project)}",
|
disabled: "#{!can?(current_user, :admin_list, current_board_parent)}",
|
||||||
issue_link_base: project_issues_path(@project),
|
issue_link_base: build_issue_link_base,
|
||||||
root_path: root_path,
|
root_path: root_path,
|
||||||
bulk_update_path: bulk_update_project_issues_path(@project),
|
bulk_update_path: @bulk_issues_path,
|
||||||
default_avatar: image_path(default_avatar)
|
default_avatar: image_path(default_avatar)
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def build_issue_link_base
|
||||||
|
project_issues_path(@project)
|
||||||
|
end
|
||||||
|
|
||||||
|
def current_board_json
|
||||||
|
board = @board || @boards.first
|
||||||
|
|
||||||
|
board.to_json(
|
||||||
|
only: [:id, :name, :milestone_id],
|
||||||
|
include: {
|
||||||
|
milestone: { only: [:title] }
|
||||||
|
}
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def board_base_url
|
||||||
|
project_boards_path(@project)
|
||||||
|
end
|
||||||
|
|
||||||
|
def multiple_boards_available?
|
||||||
|
current_board_parent.multiple_issue_boards_available?(current_user)
|
||||||
|
end
|
||||||
|
|
||||||
|
def current_board_path(board)
|
||||||
|
@current_board_path ||= project_board_path(current_board_parent, board)
|
||||||
|
end
|
||||||
|
|
||||||
|
def current_board_parent
|
||||||
|
@current_board_parent ||= @project
|
||||||
|
end
|
||||||
|
|
||||||
|
def can_admin_issue?
|
||||||
|
can?(current_user, :admin_issue, current_board_parent)
|
||||||
|
end
|
||||||
|
|
||||||
|
def board_list_data
|
||||||
|
{
|
||||||
|
toggle: "dropdown",
|
||||||
|
list_labels_path: labels_filter_path(true),
|
||||||
|
labels: labels_filter_path(true),
|
||||||
|
labels_endpoint: @labels_endpoint,
|
||||||
|
namespace_path: @namespace_path,
|
||||||
|
project_path: @project&.try(:path)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def board_sidebar_user_data
|
||||||
|
dropdown_options = issue_assignees_dropdown_options
|
||||||
|
|
||||||
|
{
|
||||||
|
toggle: 'dropdown',
|
||||||
|
field_name: 'issue[assignee_ids][]',
|
||||||
|
first_user: current_user&.username,
|
||||||
|
current_user: 'true',
|
||||||
|
project_id: @project&.try(:id),
|
||||||
|
null_user: 'true',
|
||||||
|
multi_select: 'true',
|
||||||
|
'dropdown-header': dropdown_options[:data][:'dropdown-header'],
|
||||||
|
'max-select': dropdown_options[:data][:'max-select']
|
||||||
|
}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -347,6 +347,14 @@ module IssuablesHelper
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def labels_path
|
||||||
|
if @project
|
||||||
|
project_labels_path(@project)
|
||||||
|
elsif @group
|
||||||
|
group_labels_path(@group)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def issuable_sidebar_options(issuable, can_edit_issuable)
|
def issuable_sidebar_options(issuable, can_edit_issuable)
|
||||||
{
|
{
|
||||||
endpoint: "#{issuable_json_path(issuable)}?basic=true",
|
endpoint: "#{issuable_json_path(issuable)}?basic=true",
|
||||||
|
|
|
@ -121,13 +121,14 @@ module LabelsHelper
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def labels_filter_path
|
def labels_filter_path(only_group_labels = false)
|
||||||
return group_labels_path(@group, :json) if @group
|
|
||||||
|
|
||||||
project = @target_project || @project
|
project = @target_project || @project
|
||||||
|
|
||||||
if project
|
if project
|
||||||
project_labels_path(project, :json)
|
project_labels_path(project, :json)
|
||||||
|
elsif @group
|
||||||
|
options = { only_group_labels: only_group_labels } if only_group_labels
|
||||||
|
group_labels_path(@group, :json, options)
|
||||||
else
|
else
|
||||||
dashboard_labels_path(:json)
|
dashboard_labels_path(:json)
|
||||||
end
|
end
|
||||||
|
|
|
@ -40,6 +40,10 @@ module PreferencesHelper
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def user_application_theme
|
||||||
|
@user_application_theme ||= Gitlab::Themes.for_user(current_user).css_class
|
||||||
|
end
|
||||||
|
|
||||||
def user_color_scheme
|
def user_color_scheme
|
||||||
Gitlab::ColorSchemes.for_user(current_user).css_class
|
Gitlab::ColorSchemes.for_user(current_user).css_class
|
||||||
end
|
end
|
||||||
|
|
|
@ -134,7 +134,8 @@ module SearchHelper
|
||||||
end
|
end
|
||||||
|
|
||||||
def search_filter_input_options(type)
|
def search_filter_input_options(type)
|
||||||
opts = {
|
opts =
|
||||||
|
{
|
||||||
id: "filtered-search-#{type}",
|
id: "filtered-search-#{type}",
|
||||||
placeholder: 'Search or filter results...',
|
placeholder: 'Search or filter results...',
|
||||||
data: {
|
data: {
|
||||||
|
@ -147,6 +148,7 @@ module SearchHelper
|
||||||
opts[:data]['base-endpoint'] = project_path(@project)
|
opts[:data]['base-endpoint'] = project_path(@project)
|
||||||
else
|
else
|
||||||
# Group context
|
# Group context
|
||||||
|
opts[:data]['group-id'] = @group.id
|
||||||
opts[:data]['base-endpoint'] = group_canonical_path(@group)
|
opts[:data]['base-endpoint'] = group_canonical_path(@group)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,19 @@ class Board < ActiveRecord::Base
|
||||||
|
|
||||||
has_many :lists, -> { order(:list_type, :position) }, dependent: :delete_all # rubocop:disable Cop/ActiveRecordDependent
|
has_many :lists, -> { order(:list_type, :position) }, dependent: :delete_all # rubocop:disable Cop/ActiveRecordDependent
|
||||||
|
|
||||||
validates :project, presence: true
|
validates :project, presence: true, if: :project_needed?
|
||||||
|
|
||||||
|
def project_needed?
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
|
def parent
|
||||||
|
project
|
||||||
|
end
|
||||||
|
|
||||||
|
def group_board?
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
def backlog_list
|
def backlog_list
|
||||||
lists.merge(List.backlog).take
|
lists.merge(List.backlog).take
|
||||||
|
|
|
@ -10,8 +10,12 @@ module RelativePositioning
|
||||||
after_save :save_positionable_neighbours
|
after_save :save_positionable_neighbours
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def project_ids
|
||||||
|
[project.id]
|
||||||
|
end
|
||||||
|
|
||||||
def max_relative_position
|
def max_relative_position
|
||||||
self.class.in_projects(project.id).maximum(:relative_position)
|
self.class.in_projects(project_ids).maximum(:relative_position)
|
||||||
end
|
end
|
||||||
|
|
||||||
def prev_relative_position
|
def prev_relative_position
|
||||||
|
@ -19,7 +23,7 @@ module RelativePositioning
|
||||||
|
|
||||||
if self.relative_position
|
if self.relative_position
|
||||||
prev_pos = self.class
|
prev_pos = self.class
|
||||||
.in_projects(project.id)
|
.in_projects(project_ids)
|
||||||
.where('relative_position < ?', self.relative_position)
|
.where('relative_position < ?', self.relative_position)
|
||||||
.maximum(:relative_position)
|
.maximum(:relative_position)
|
||||||
end
|
end
|
||||||
|
@ -32,7 +36,7 @@ module RelativePositioning
|
||||||
|
|
||||||
if self.relative_position
|
if self.relative_position
|
||||||
next_pos = self.class
|
next_pos = self.class
|
||||||
.in_projects(project.id)
|
.in_projects(project_ids)
|
||||||
.where('relative_position > ?', self.relative_position)
|
.where('relative_position > ?', self.relative_position)
|
||||||
.minimum(:relative_position)
|
.minimum(:relative_position)
|
||||||
end
|
end
|
||||||
|
@ -59,7 +63,7 @@ module RelativePositioning
|
||||||
pos_after = before.next_relative_position
|
pos_after = before.next_relative_position
|
||||||
|
|
||||||
if before.shift_after?
|
if before.shift_after?
|
||||||
issue_to_move = self.class.in_projects(project.id).find_by!(relative_position: pos_after)
|
issue_to_move = self.class.in_projects(project_ids).find_by!(relative_position: pos_after)
|
||||||
issue_to_move.move_after
|
issue_to_move.move_after
|
||||||
@positionable_neighbours = [issue_to_move]
|
@positionable_neighbours = [issue_to_move]
|
||||||
|
|
||||||
|
@ -74,7 +78,7 @@ module RelativePositioning
|
||||||
pos_before = after.prev_relative_position
|
pos_before = after.prev_relative_position
|
||||||
|
|
||||||
if after.shift_before?
|
if after.shift_before?
|
||||||
issue_to_move = self.class.in_projects(project.id).find_by!(relative_position: pos_before)
|
issue_to_move = self.class.in_projects(project_ids).find_by!(relative_position: pos_before)
|
||||||
issue_to_move.move_before
|
issue_to_move.move_before
|
||||||
@positionable_neighbours = [issue_to_move]
|
@positionable_neighbours = [issue_to_move]
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
class Event < ActiveRecord::Base
|
class Event < ActiveRecord::Base
|
||||||
include Sortable
|
include Sortable
|
||||||
|
include IgnorableColumn
|
||||||
default_scope { reorder(nil).where.not(author_id: nil) }
|
default_scope { reorder(nil).where.not(author_id: nil) }
|
||||||
|
|
||||||
CREATED = 1
|
CREATED = 1
|
||||||
|
@ -50,13 +51,9 @@ class Event < ActiveRecord::Base
|
||||||
belongs_to :target, polymorphic: true # rubocop:disable Cop/PolymorphicAssociations
|
belongs_to :target, polymorphic: true # rubocop:disable Cop/PolymorphicAssociations
|
||||||
has_one :push_event_payload, foreign_key: :event_id
|
has_one :push_event_payload, foreign_key: :event_id
|
||||||
|
|
||||||
# For Hash only
|
|
||||||
serialize :data # rubocop:disable Cop/ActiveRecordSerialize
|
|
||||||
|
|
||||||
# Callbacks
|
# Callbacks
|
||||||
after_create :reset_project_activity
|
after_create :reset_project_activity
|
||||||
after_create :set_last_repository_updated_at, if: :push?
|
after_create :set_last_repository_updated_at, if: :push?
|
||||||
after_create :replicate_event_for_push_events_migration
|
|
||||||
|
|
||||||
# Scopes
|
# Scopes
|
||||||
scope :recent, -> { reorder(id: :desc) }
|
scope :recent, -> { reorder(id: :desc) }
|
||||||
|
@ -82,6 +79,10 @@ class Event < ActiveRecord::Base
|
||||||
|
|
||||||
self.inheritance_column = 'action'
|
self.inheritance_column = 'action'
|
||||||
|
|
||||||
|
# "data" will be removed in 10.0 but it may be possible that JOINs happen that
|
||||||
|
# include this column, hence we're ignoring it as well.
|
||||||
|
ignore_column :data
|
||||||
|
|
||||||
class << self
|
class << self
|
||||||
def model_name
|
def model_name
|
||||||
ActiveModel::Name.new(self, nil, 'event')
|
ActiveModel::Name.new(self, nil, 'event')
|
||||||
|
@ -159,7 +160,7 @@ class Event < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
|
|
||||||
def push?
|
def push?
|
||||||
action == PUSHED && valid_push?
|
false
|
||||||
end
|
end
|
||||||
|
|
||||||
def merged?
|
def merged?
|
||||||
|
@ -272,87 +273,6 @@ class Event < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def valid_push?
|
|
||||||
data[:ref] && ref_name.present?
|
|
||||||
rescue
|
|
||||||
false
|
|
||||||
end
|
|
||||||
|
|
||||||
def tag?
|
|
||||||
Gitlab::Git.tag_ref?(data[:ref])
|
|
||||||
end
|
|
||||||
|
|
||||||
def branch?
|
|
||||||
Gitlab::Git.branch_ref?(data[:ref])
|
|
||||||
end
|
|
||||||
|
|
||||||
def new_ref?
|
|
||||||
Gitlab::Git.blank_ref?(commit_from)
|
|
||||||
end
|
|
||||||
|
|
||||||
def rm_ref?
|
|
||||||
Gitlab::Git.blank_ref?(commit_to)
|
|
||||||
end
|
|
||||||
|
|
||||||
def md_ref?
|
|
||||||
!(rm_ref? || new_ref?)
|
|
||||||
end
|
|
||||||
|
|
||||||
def commit_from
|
|
||||||
data[:before]
|
|
||||||
end
|
|
||||||
|
|
||||||
def commit_to
|
|
||||||
data[:after]
|
|
||||||
end
|
|
||||||
|
|
||||||
def ref_name
|
|
||||||
if tag?
|
|
||||||
tag_name
|
|
||||||
else
|
|
||||||
branch_name
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def branch_name
|
|
||||||
@branch_name ||= Gitlab::Git.ref_name(data[:ref])
|
|
||||||
end
|
|
||||||
|
|
||||||
def tag_name
|
|
||||||
@tag_name ||= Gitlab::Git.ref_name(data[:ref])
|
|
||||||
end
|
|
||||||
|
|
||||||
# Max 20 commits from push DESC
|
|
||||||
def commits
|
|
||||||
@commits ||= (data[:commits] || []).reverse
|
|
||||||
end
|
|
||||||
|
|
||||||
def commit_title
|
|
||||||
commit = commits.last
|
|
||||||
|
|
||||||
commit[:message] if commit
|
|
||||||
end
|
|
||||||
|
|
||||||
def commit_id
|
|
||||||
commit_to || commit_from
|
|
||||||
end
|
|
||||||
|
|
||||||
def commits_count
|
|
||||||
data[:total_commits_count] || commits.count || 0
|
|
||||||
end
|
|
||||||
|
|
||||||
def ref_type
|
|
||||||
tag? ? "tag" : "branch"
|
|
||||||
end
|
|
||||||
|
|
||||||
def push_with_commits?
|
|
||||||
!commits.empty? && commit_from && commit_to
|
|
||||||
end
|
|
||||||
|
|
||||||
def last_push_to_non_root?
|
|
||||||
branch? && project.default_branch != branch_name
|
|
||||||
end
|
|
||||||
|
|
||||||
def target_iid
|
def target_iid
|
||||||
target.respond_to?(:iid) ? target.iid : target_id
|
target.respond_to?(:iid) ? target.iid : target_id
|
||||||
end
|
end
|
||||||
|
@ -432,16 +352,6 @@ class Event < ActiveRecord::Base
|
||||||
user ? author_id == user.id : false
|
user ? author_id == user.id : false
|
||||||
end
|
end
|
||||||
|
|
||||||
# We're manually replicating data into the new table since database triggers
|
|
||||||
# are not dumped to db/schema.rb. This could mean that a new installation
|
|
||||||
# would not have the triggers in place, thus losing events data in GitLab
|
|
||||||
# 10.0.
|
|
||||||
def replicate_event_for_push_events_migration
|
|
||||||
new_attributes = attributes.with_indifferent_access.except(:title, :data)
|
|
||||||
|
|
||||||
EventForMigration.create!(new_attributes)
|
|
||||||
end
|
|
||||||
|
|
||||||
def to_partial_path
|
def to_partial_path
|
||||||
# We are intentionally using `Event` rather than `self.class` so that
|
# We are intentionally using `Event` rather than `self.class` so that
|
||||||
# subclasses also use the `Event` implementation.
|
# subclasses also use the `Event` implementation.
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
# This model is used to replicate events between the old "events" table and the
|
|
||||||
# new "events_for_migration" table that will replace "events" in GitLab 10.0.
|
|
||||||
class EventForMigration < ActiveRecord::Base
|
|
||||||
self.table_name = 'events_for_migration'
|
|
||||||
end
|
|
|
@ -34,7 +34,8 @@ class Label < ActiveRecord::Base
|
||||||
|
|
||||||
scope :templates, -> { where(template: true) }
|
scope :templates, -> { where(template: true) }
|
||||||
scope :with_title, ->(title) { where(title: title) }
|
scope :with_title, ->(title) { where(title: title) }
|
||||||
scope :on_project_boards, ->(project_id) { joins(lists: :board).merge(List.movable).where(boards: { project_id: project_id }) }
|
scope :with_lists_and_board, -> { joins(lists: :board).merge(List.movable) }
|
||||||
|
scope :on_project_boards, ->(project_id) { with_lists_and_board.where(boards: { project_id: project_id }) }
|
||||||
|
|
||||||
def self.prioritized(project)
|
def self.prioritized(project)
|
||||||
joins(:priorities)
|
joins(:priorities)
|
||||||
|
@ -172,6 +173,7 @@ class Label < ActiveRecord::Base
|
||||||
|
|
||||||
def as_json(options = {})
|
def as_json(options = {})
|
||||||
super(options).tap do |json|
|
super(options).tap do |json|
|
||||||
|
json[:type] = self.try(:type)
|
||||||
json[:priority] = priority(options[:project]) if options.key?(:project)
|
json[:priority] = priority(options[:project]) if options.key?(:project)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1507,6 +1507,14 @@ class Project < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def multiple_issue_boards_available?(user)
|
||||||
|
feature_available?(:multiple_issue_boards, user)
|
||||||
|
end
|
||||||
|
|
||||||
|
def issue_board_milestone_available?(user = nil)
|
||||||
|
feature_available?(:issue_board_milestone, user)
|
||||||
|
end
|
||||||
|
|
||||||
def full_path_was
|
def full_path_was
|
||||||
File.join(namespace.full_path, previous_changes['path'].first)
|
File.join(namespace.full_path, previous_changes['path'].first)
|
||||||
end
|
end
|
||||||
|
|
|
@ -15,15 +15,21 @@ class PushEvent < Event
|
||||||
# should ensure the ID points to a valid project.
|
# should ensure the ID points to a valid project.
|
||||||
validates :project_id, presence: true
|
validates :project_id, presence: true
|
||||||
|
|
||||||
# The "data" field must not be set for push events since it's not used and a
|
|
||||||
# waste of space.
|
|
||||||
validates :data, absence: true
|
|
||||||
|
|
||||||
# These fields are also not used for push events, thus storing them would be a
|
# These fields are also not used for push events, thus storing them would be a
|
||||||
# waste.
|
# waste.
|
||||||
validates :target_id, absence: true
|
validates :target_id, absence: true
|
||||||
validates :target_type, absence: true
|
validates :target_type, absence: true
|
||||||
|
|
||||||
|
delegate :branch?, to: :push_event_payload
|
||||||
|
delegate :tag?, to: :push_event_payload
|
||||||
|
delegate :commit_from, to: :push_event_payload
|
||||||
|
delegate :commit_to, to: :push_event_payload
|
||||||
|
delegate :ref_type, to: :push_event_payload
|
||||||
|
delegate :commit_title, to: :push_event_payload
|
||||||
|
|
||||||
|
delegate :commit_count, to: :push_event_payload
|
||||||
|
alias_method :commits_count, :commit_count
|
||||||
|
|
||||||
def self.sti_name
|
def self.sti_name
|
||||||
PUSHED
|
PUSHED
|
||||||
end
|
end
|
||||||
|
@ -36,86 +42,35 @@ class PushEvent < Event
|
||||||
!!(commit_from && commit_to)
|
!!(commit_from && commit_to)
|
||||||
end
|
end
|
||||||
|
|
||||||
def tag?
|
|
||||||
return super unless push_event_payload
|
|
||||||
|
|
||||||
push_event_payload.tag?
|
|
||||||
end
|
|
||||||
|
|
||||||
def branch?
|
|
||||||
return super unless push_event_payload
|
|
||||||
|
|
||||||
push_event_payload.branch?
|
|
||||||
end
|
|
||||||
|
|
||||||
def valid_push?
|
def valid_push?
|
||||||
return super unless push_event_payload
|
|
||||||
|
|
||||||
push_event_payload.ref.present?
|
push_event_payload.ref.present?
|
||||||
end
|
end
|
||||||
|
|
||||||
def new_ref?
|
def new_ref?
|
||||||
return super unless push_event_payload
|
|
||||||
|
|
||||||
push_event_payload.created?
|
push_event_payload.created?
|
||||||
end
|
end
|
||||||
|
|
||||||
def rm_ref?
|
def rm_ref?
|
||||||
return super unless push_event_payload
|
|
||||||
|
|
||||||
push_event_payload.removed?
|
push_event_payload.removed?
|
||||||
end
|
end
|
||||||
|
|
||||||
def commit_from
|
def md_ref?
|
||||||
return super unless push_event_payload
|
!(rm_ref? || new_ref?)
|
||||||
|
|
||||||
push_event_payload.commit_from
|
|
||||||
end
|
|
||||||
|
|
||||||
def commit_to
|
|
||||||
return super unless push_event_payload
|
|
||||||
|
|
||||||
push_event_payload.commit_to
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def ref_name
|
def ref_name
|
||||||
return super unless push_event_payload
|
|
||||||
|
|
||||||
push_event_payload.ref
|
push_event_payload.ref
|
||||||
end
|
end
|
||||||
|
|
||||||
def ref_type
|
alias_method :branch_name, :ref_name
|
||||||
return super unless push_event_payload
|
alias_method :tag_name, :ref_name
|
||||||
|
|
||||||
push_event_payload.ref_type
|
|
||||||
end
|
|
||||||
|
|
||||||
def branch_name
|
|
||||||
return super unless push_event_payload
|
|
||||||
|
|
||||||
ref_name
|
|
||||||
end
|
|
||||||
|
|
||||||
def tag_name
|
|
||||||
return super unless push_event_payload
|
|
||||||
|
|
||||||
ref_name
|
|
||||||
end
|
|
||||||
|
|
||||||
def commit_title
|
|
||||||
return super unless push_event_payload
|
|
||||||
|
|
||||||
push_event_payload.commit_title
|
|
||||||
end
|
|
||||||
|
|
||||||
def commit_id
|
def commit_id
|
||||||
commit_to || commit_from
|
commit_to || commit_from
|
||||||
end
|
end
|
||||||
|
|
||||||
def commits_count
|
def last_push_to_non_root?
|
||||||
return super unless push_event_payload
|
branch? && project.default_branch != branch_name
|
||||||
|
|
||||||
push_event_payload.commit_count
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def validate_push_action
|
def validate_push_action
|
||||||
|
|
|
@ -35,6 +35,7 @@ class User < ActiveRecord::Base
|
||||||
default_value_for :project_view, :files
|
default_value_for :project_view, :files
|
||||||
default_value_for :notified_of_own_activity, false
|
default_value_for :notified_of_own_activity, false
|
||||||
default_value_for :preferred_language, I18n.default_locale
|
default_value_for :preferred_language, I18n.default_locale
|
||||||
|
default_value_for :theme_id, gitlab_config.default_theme
|
||||||
|
|
||||||
attr_encrypted :otp_secret,
|
attr_encrypted :otp_secret,
|
||||||
key: Gitlab::Application.secrets.otp_key_base,
|
key: Gitlab::Application.secrets.otp_key_base,
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
module Boards
|
||||||
|
class BaseService < ::BaseService
|
||||||
|
# Parent can either a group or a project
|
||||||
|
attr_accessor :parent, :current_user, :params
|
||||||
|
|
||||||
|
def initialize(parent, user, params = {})
|
||||||
|
@parent, @current_user, @params = parent, user, params.dup
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,5 +1,5 @@
|
||||||
module Boards
|
module Boards
|
||||||
class CreateService < BaseService
|
class CreateService < Boards::BaseService
|
||||||
def execute
|
def execute
|
||||||
create_board! if can_create_board?
|
create_board! if can_create_board?
|
||||||
end
|
end
|
||||||
|
@ -7,11 +7,11 @@ module Boards
|
||||||
private
|
private
|
||||||
|
|
||||||
def can_create_board?
|
def can_create_board?
|
||||||
project.boards.size == 0
|
parent.boards.size == 0
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_board!
|
def create_board!
|
||||||
board = project.boards.create(params)
|
board = parent.boards.create(params)
|
||||||
|
|
||||||
if board.persisted?
|
if board.persisted?
|
||||||
board.lists.create(list_type: :backlog)
|
board.lists.create(list_type: :backlog)
|
||||||
|
|
|
@ -1,6 +1,14 @@
|
||||||
module Boards
|
module Boards
|
||||||
module Issues
|
module Issues
|
||||||
class CreateService < BaseService
|
class CreateService < Boards::BaseService
|
||||||
|
attr_accessor :project
|
||||||
|
|
||||||
|
def initialize(parent, project, user, params = {})
|
||||||
|
@project = project
|
||||||
|
|
||||||
|
super(parent, user, params)
|
||||||
|
end
|
||||||
|
|
||||||
def execute
|
def execute
|
||||||
create_issue(params.merge(label_ids: [list.label_id]))
|
create_issue(params.merge(label_ids: [list.label_id]))
|
||||||
end
|
end
|
||||||
|
@ -8,7 +16,7 @@ module Boards
|
||||||
private
|
private
|
||||||
|
|
||||||
def board
|
def board
|
||||||
@board ||= project.boards.find(params.delete(:board_id))
|
@board ||= parent.boards.find(params.delete(:board_id))
|
||||||
end
|
end
|
||||||
|
|
||||||
def list
|
def list
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
module Boards
|
module Boards
|
||||||
module Issues
|
module Issues
|
||||||
class ListService < BaseService
|
class ListService < Boards::BaseService
|
||||||
def execute
|
def execute
|
||||||
issues = IssuesFinder.new(current_user, filter_params).execute
|
issues = IssuesFinder.new(current_user, filter_params).execute
|
||||||
issues = without_board_labels(issues) unless movable_list? || closed_list?
|
issues = without_board_labels(issues) unless movable_list? || closed_list?
|
||||||
|
@ -11,7 +11,7 @@ module Boards
|
||||||
private
|
private
|
||||||
|
|
||||||
def board
|
def board
|
||||||
@board ||= project.boards.find(params[:board_id])
|
@board ||= parent.boards.find(params[:board_id])
|
||||||
end
|
end
|
||||||
|
|
||||||
def list
|
def list
|
||||||
|
@ -33,14 +33,14 @@ module Boards
|
||||||
end
|
end
|
||||||
|
|
||||||
def filter_params
|
def filter_params
|
||||||
set_project
|
set_parent
|
||||||
set_state
|
set_state
|
||||||
|
|
||||||
params
|
params
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_project
|
def set_parent
|
||||||
params[:project_id] = project.id
|
params[:project_id] = parent.id
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_state
|
def set_state
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
module Boards
|
module Boards
|
||||||
module Issues
|
module Issues
|
||||||
class MoveService < BaseService
|
class MoveService < Boards::BaseService
|
||||||
def execute(issue)
|
def execute(issue)
|
||||||
return false unless can?(current_user, :update_issue, issue)
|
return false unless can?(current_user, :update_issue, issue)
|
||||||
return false if issue_params.empty?
|
return false if issue_params.empty?
|
||||||
|
|
||||||
update_service.execute(issue)
|
update(issue)
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def board
|
def board
|
||||||
@board ||= project.boards.find(params[:board_id])
|
@board ||= parent.boards.find(params[:board_id])
|
||||||
end
|
end
|
||||||
|
|
||||||
def move_between_lists?
|
def move_between_lists?
|
||||||
|
@ -27,8 +27,8 @@ module Boards
|
||||||
@moving_to_list ||= board.lists.find_by(id: params[:to_list_id])
|
@moving_to_list ||= board.lists.find_by(id: params[:to_list_id])
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_service
|
def update(issue)
|
||||||
::Issues::UpdateService.new(project, current_user, issue_params)
|
::Issues::UpdateService.new(issue.project, current_user, issue_params).execute(issue)
|
||||||
end
|
end
|
||||||
|
|
||||||
def issue_params
|
def issue_params
|
||||||
|
@ -42,7 +42,7 @@ module Boards
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
attrs[:move_between_iids] = move_between_iids if move_between_iids
|
attrs[:move_between_ids] = move_between_ids if move_between_ids
|
||||||
|
|
||||||
attrs
|
attrs
|
||||||
end
|
end
|
||||||
|
@ -61,16 +61,16 @@ module Boards
|
||||||
if moving_to_list.movable?
|
if moving_to_list.movable?
|
||||||
moving_from_list.label_id
|
moving_from_list.label_id
|
||||||
else
|
else
|
||||||
Label.on_project_boards(project.id).pluck(:label_id)
|
Label.on_project_boards(parent.id).pluck(:label_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
Array(label_ids).compact
|
Array(label_ids).compact
|
||||||
end
|
end
|
||||||
|
|
||||||
def move_between_iids
|
def move_between_ids
|
||||||
return unless params[:move_after_iid] || params[:move_before_iid]
|
return unless params[:move_after_id] || params[:move_before_id]
|
||||||
|
|
||||||
[params[:move_after_iid], params[:move_before_iid]]
|
[params[:move_after_id], params[:move_before_id]]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
module Boards
|
module Boards
|
||||||
class ListService < BaseService
|
class ListService < Boards::BaseService
|
||||||
def execute
|
def execute
|
||||||
create_board! if project.boards.empty?
|
create_board! if parent.boards.empty?
|
||||||
project.boards
|
parent.boards
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def create_board!
|
def create_board!
|
||||||
Boards::CreateService.new(project, current_user).execute
|
Boards::CreateService.new(parent, current_user).execute
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,19 +1,18 @@
|
||||||
module Boards
|
module Boards
|
||||||
module Lists
|
module Lists
|
||||||
class CreateService < BaseService
|
class CreateService < Boards::BaseService
|
||||||
def execute(board)
|
def execute(board)
|
||||||
List.transaction do
|
List.transaction do
|
||||||
label = available_labels.find(params[:label_id])
|
label = available_labels_for(board).find(params[:label_id])
|
||||||
position = next_position(board)
|
position = next_position(board)
|
||||||
|
|
||||||
create_list(board, label, position)
|
create_list(board, label, position)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def available_labels
|
def available_labels_for(board)
|
||||||
LabelsFinder.new(current_user, project_id: project.id).execute
|
LabelsFinder.new(current_user, project_id: parent.id).execute
|
||||||
end
|
end
|
||||||
|
|
||||||
def next_position(board)
|
def next_position(board)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
module Boards
|
module Boards
|
||||||
module Lists
|
module Lists
|
||||||
class DestroyService < BaseService
|
class DestroyService < Boards::BaseService
|
||||||
def execute(list)
|
def execute(list)
|
||||||
return false unless list.destroyable?
|
return false unless list.destroyable?
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
module Boards
|
module Boards
|
||||||
module Lists
|
module Lists
|
||||||
class GenerateService < BaseService
|
class GenerateService < Boards::BaseService
|
||||||
def execute(board)
|
def execute(board)
|
||||||
return false unless board.lists.movable.empty?
|
return false unless board.lists.movable.empty?
|
||||||
|
|
||||||
|
@ -15,11 +15,11 @@ module Boards
|
||||||
|
|
||||||
def create_list(board, params)
|
def create_list(board, params)
|
||||||
label = find_or_create_label(params)
|
label = find_or_create_label(params)
|
||||||
Lists::CreateService.new(project, current_user, label_id: label.id).execute(board)
|
Lists::CreateService.new(parent, current_user, label_id: label.id).execute(board)
|
||||||
end
|
end
|
||||||
|
|
||||||
def find_or_create_label(params)
|
def find_or_create_label(params)
|
||||||
::Labels::FindOrCreateService.new(current_user, project, params).execute
|
::Labels::FindOrCreateService.new(current_user, parent, params).execute
|
||||||
end
|
end
|
||||||
|
|
||||||
def label_params
|
def label_params
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
module Boards
|
module Boards
|
||||||
module Lists
|
module Lists
|
||||||
class ListService < BaseService
|
class ListService < Boards::BaseService
|
||||||
def execute(board)
|
def execute(board)
|
||||||
board.lists.create(list_type: :backlog) unless board.lists.backlog.exists?
|
board.lists.create(list_type: :backlog) unless board.lists.backlog.exists?
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
module Boards
|
module Boards
|
||||||
module Lists
|
module Lists
|
||||||
class MoveService < BaseService
|
class MoveService < Boards::BaseService
|
||||||
def execute(list)
|
def execute(list)
|
||||||
@board = list.board
|
@board = list.board
|
||||||
@old_position = list.position
|
@old_position = list.position
|
||||||
|
|
|
@ -3,7 +3,7 @@ module Issues
|
||||||
include SpamCheckService
|
include SpamCheckService
|
||||||
|
|
||||||
def execute(issue)
|
def execute(issue)
|
||||||
handle_move_between_iids(issue)
|
handle_move_between_ids(issue)
|
||||||
filter_spam_check_params
|
filter_spam_check_params
|
||||||
change_issue_duplicate(issue)
|
change_issue_duplicate(issue)
|
||||||
move_issue_to_new_project(issue) || update(issue)
|
move_issue_to_new_project(issue) || update(issue)
|
||||||
|
@ -54,13 +54,13 @@ module Issues
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def handle_move_between_iids(issue)
|
def handle_move_between_ids(issue)
|
||||||
return unless params[:move_between_iids]
|
return unless params[:move_between_ids]
|
||||||
|
|
||||||
after_iid, before_iid = params.delete(:move_between_iids)
|
after_id, before_id = params.delete(:move_between_ids)
|
||||||
|
|
||||||
issue_before = get_issue_if_allowed(issue.project, before_iid) if before_iid
|
issue_before = get_issue_if_allowed(issue.project, before_id) if before_id
|
||||||
issue_after = get_issue_if_allowed(issue.project, after_iid) if after_iid
|
issue_after = get_issue_if_allowed(issue.project, after_id) if after_id
|
||||||
|
|
||||||
issue.move_between(issue_before, issue_after)
|
issue.move_between(issue_before, issue_after)
|
||||||
end
|
end
|
||||||
|
@ -87,8 +87,8 @@ module Issues
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def get_issue_if_allowed(project, iid)
|
def get_issue_if_allowed(project, id)
|
||||||
issue = project.issues.find_by(iid: iid)
|
issue = project.issues.find(id)
|
||||||
issue if can?(current_user, :update_issue, issue)
|
issue if can?(current_user, :update_issue, issue)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
!!! 5
|
!!! 5
|
||||||
%html{ lang: I18n.locale, class: page_class }
|
%html{ lang: I18n.locale, class: page_class }
|
||||||
= render "layouts/head"
|
= render "layouts/head"
|
||||||
%body{ class: @body_class, data: { page: body_data_page, project: "#{@project.path if @project}", group: "#{@group.path if @group}", find_file: find_file_path } }
|
%body{ class: "#{user_application_theme} #{@body_class}", data: { page: body_data_page, project: "#{@project.path if @project}", group: "#{@group.path if @group}", find_file: find_file_path } }
|
||||||
= render "layouts/init_auto_complete" if @gfm_form
|
= render "layouts/init_auto_complete" if @gfm_form
|
||||||
= render 'peek/bar'
|
= render 'peek/bar'
|
||||||
= render "layouts/header/default"
|
= render "layouts/header/default"
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
%ul.list-unstyled.navbar-sub-nav
|
%ul.list-unstyled.navbar-sub-nav
|
||||||
= nav_link(path: ['root#index', 'projects#trending', 'projects#starred', 'dashboard/projects#index'], html_options: { id: 'nav-projects-dropdown', class: "home dropdown" }) do
|
= nav_link(path: ['root#index', 'projects#trending', 'projects#starred', 'dashboard/projects#index'], html_options: { id: 'nav-projects-dropdown', class: "home dropdown header-projects" }) do
|
||||||
%a{ href: "#", data: { toggle: "dropdown" } }
|
%a{ href: "#", data: { toggle: "dropdown" } }
|
||||||
Projects
|
Projects
|
||||||
= custom_icon('caret_down')
|
= custom_icon('caret_down')
|
||||||
|
@ -22,7 +22,7 @@
|
||||||
= link_to dashboard_snippets_path, class: 'dashboard-shortcuts-snippets', title: 'Snippets' do
|
= link_to dashboard_snippets_path, class: 'dashboard-shortcuts-snippets', title: 'Snippets' do
|
||||||
Snippets
|
Snippets
|
||||||
|
|
||||||
%li.dropdown.hidden-lg
|
%li.header-more.dropdown.hidden-lg
|
||||||
%a{ href: "#", data: { toggle: "dropdown" } }
|
%a{ href: "#", data: { toggle: "dropdown" } }
|
||||||
More
|
More
|
||||||
= custom_icon('caret_down')
|
= custom_icon('caret_down')
|
||||||
|
|
|
@ -14,6 +14,11 @@
|
||||||
Overview
|
Overview
|
||||||
|
|
||||||
%ul.sidebar-sub-level-items
|
%ul.sidebar-sub-level-items
|
||||||
|
= nav_link(controller: %w(dashboard admin projects users groups jobs runners cohorts), html_options: { class: "fly-out-top-item" } ) do
|
||||||
|
= link_to admin_root_path do
|
||||||
|
%strong.fly-out-top-item-name
|
||||||
|
#{ _('Overview') }
|
||||||
|
%li.divider.fly-out-top-item
|
||||||
= nav_link(controller: :dashboard, html_options: {class: 'home'}) do
|
= nav_link(controller: :dashboard, html_options: {class: 'home'}) do
|
||||||
= link_to admin_root_path, title: 'Overview' do
|
= link_to admin_root_path, title: 'Overview' do
|
||||||
%span
|
%span
|
||||||
|
@ -55,6 +60,11 @@
|
||||||
Monitoring
|
Monitoring
|
||||||
|
|
||||||
%ul.sidebar-sub-level-items
|
%ul.sidebar-sub-level-items
|
||||||
|
= nav_link(controller: %w(conversational_development_index system_info background_jobs logs health_check requests_profiles), html_options: { class: "fly-out-top-item" } ) do
|
||||||
|
= link_to admin_conversational_development_index_path do
|
||||||
|
%strong.fly-out-top-item-name
|
||||||
|
#{ _('Monitoring') }
|
||||||
|
%li.divider.fly-out-top-item
|
||||||
= nav_link(controller: :system_info) do
|
= nav_link(controller: :system_info) do
|
||||||
= link_to admin_system_info_path, title: 'System Info' do
|
= link_to admin_system_info_path, title: 'System Info' do
|
||||||
%span
|
%span
|
||||||
|
@ -82,6 +92,11 @@
|
||||||
= custom_icon('messages')
|
= custom_icon('messages')
|
||||||
%span.nav-item-name
|
%span.nav-item-name
|
||||||
Messages
|
Messages
|
||||||
|
%ul.sidebar-sub-level-items.is-fly-out-only
|
||||||
|
= nav_link(controller: :broadcast_messages, html_options: { class: "fly-out-top-item" } ) do
|
||||||
|
= link_to admin_broadcast_messages_path do
|
||||||
|
%strong.fly-out-top-item-name
|
||||||
|
#{ _('Messages') }
|
||||||
|
|
||||||
= nav_link(controller: [:hooks, :hook_logs]) do
|
= nav_link(controller: [:hooks, :hook_logs]) do
|
||||||
= sidebar_link admin_hooks_path, title: _('Hooks') do
|
= sidebar_link admin_hooks_path, title: _('Hooks') do
|
||||||
|
@ -89,6 +104,11 @@
|
||||||
= custom_icon('system_hooks')
|
= custom_icon('system_hooks')
|
||||||
%span.nav-item-name
|
%span.nav-item-name
|
||||||
System Hooks
|
System Hooks
|
||||||
|
%ul.sidebar-sub-level-items.is-fly-out-only
|
||||||
|
= nav_link(controller: [:hooks, :hook_logs], html_options: { class: "fly-out-top-item" } ) do
|
||||||
|
= link_to admin_hooks_path do
|
||||||
|
%strong.fly-out-top-item-name
|
||||||
|
#{ _('System Hooks') }
|
||||||
|
|
||||||
= nav_link(controller: :applications) do
|
= nav_link(controller: :applications) do
|
||||||
= sidebar_link admin_applications_path, title: _('Applications') do
|
= sidebar_link admin_applications_path, title: _('Applications') do
|
||||||
|
@ -96,6 +116,11 @@
|
||||||
= custom_icon('applications')
|
= custom_icon('applications')
|
||||||
%span.nav-item-name
|
%span.nav-item-name
|
||||||
Applications
|
Applications
|
||||||
|
%ul.sidebar-sub-level-items.is-fly-out-only
|
||||||
|
= nav_link(controller: :applications, html_options: { class: "fly-out-top-item" } ) do
|
||||||
|
= link_to admin_applications_path do
|
||||||
|
%strong.fly-out-top-item-name
|
||||||
|
#{ _('Applications') }
|
||||||
|
|
||||||
= nav_link(controller: :abuse_reports) do
|
= nav_link(controller: :abuse_reports) do
|
||||||
= sidebar_link admin_abuse_reports_path, title: _("Abuse Reports") do
|
= sidebar_link admin_abuse_reports_path, title: _("Abuse Reports") do
|
||||||
|
@ -104,6 +129,12 @@
|
||||||
%span.nav-item-name
|
%span.nav-item-name
|
||||||
Abuse Reports
|
Abuse Reports
|
||||||
%span.badge.count= number_with_delimiter(AbuseReport.count(:all))
|
%span.badge.count= number_with_delimiter(AbuseReport.count(:all))
|
||||||
|
%ul.sidebar-sub-level-items.is-fly-out-only
|
||||||
|
= nav_link(controller: :abuse_reports, html_options: { class: "fly-out-top-item" } ) do
|
||||||
|
= link_to admin_broadcast_messages_path do
|
||||||
|
%strong.fly-out-top-item-name
|
||||||
|
#{ _('Abuse Reports') }
|
||||||
|
%span.badge.count.merge_counter.js-merge-counter.fly-out-badge= number_with_delimiter(AbuseReport.count(:all))
|
||||||
|
|
||||||
- if akismet_enabled?
|
- if akismet_enabled?
|
||||||
= nav_link(controller: :spam_logs) do
|
= nav_link(controller: :spam_logs) do
|
||||||
|
@ -112,6 +143,11 @@
|
||||||
= custom_icon('spam_logs')
|
= custom_icon('spam_logs')
|
||||||
%span.nav-item-name
|
%span.nav-item-name
|
||||||
Spam Logs
|
Spam Logs
|
||||||
|
%ul.sidebar-sub-level-items.is-fly-out-only
|
||||||
|
= nav_link(controller: :spam_logs, html_options: { class: "fly-out-top-item" } ) do
|
||||||
|
= link_to admin_spam_logs_path do
|
||||||
|
%strong.fly-out-top-item-name
|
||||||
|
#{ _('Spam Logs') }
|
||||||
|
|
||||||
= nav_link(controller: :deploy_keys) do
|
= nav_link(controller: :deploy_keys) do
|
||||||
= sidebar_link admin_deploy_keys_path, title: _('Deploy Keys') do
|
= sidebar_link admin_deploy_keys_path, title: _('Deploy Keys') do
|
||||||
|
@ -119,6 +155,11 @@
|
||||||
= custom_icon('key')
|
= custom_icon('key')
|
||||||
%span.nav-item-name
|
%span.nav-item-name
|
||||||
Deploy Keys
|
Deploy Keys
|
||||||
|
%ul.sidebar-sub-level-items.is-fly-out-only
|
||||||
|
= nav_link(controller: :deploy_keys, html_options: { class: "fly-out-top-item" } ) do
|
||||||
|
= link_to admin_deploy_keys_path do
|
||||||
|
%strong.fly-out-top-item-name
|
||||||
|
#{ _('Deploy Keys') }
|
||||||
|
|
||||||
= nav_link(controller: :services) do
|
= nav_link(controller: :services) do
|
||||||
= sidebar_link admin_application_settings_services_path, title: _('Service Templates') do
|
= sidebar_link admin_application_settings_services_path, title: _('Service Templates') do
|
||||||
|
@ -126,6 +167,11 @@
|
||||||
= custom_icon('service_templates')
|
= custom_icon('service_templates')
|
||||||
%span.nav-item-name
|
%span.nav-item-name
|
||||||
Service Templates
|
Service Templates
|
||||||
|
%ul.sidebar-sub-level-items.is-fly-out-only
|
||||||
|
= nav_link(controller: :services, html_options: { class: "fly-out-top-item" } ) do
|
||||||
|
= link_to admin_application_settings_services_path do
|
||||||
|
%strong.fly-out-top-item-name
|
||||||
|
#{ _('Service Templates') }
|
||||||
|
|
||||||
= nav_link(controller: :labels) do
|
= nav_link(controller: :labels) do
|
||||||
= sidebar_link admin_labels_path, title: _('Labels') do
|
= sidebar_link admin_labels_path, title: _('Labels') do
|
||||||
|
@ -133,6 +179,11 @@
|
||||||
= custom_icon('labels')
|
= custom_icon('labels')
|
||||||
%span.nav-item-name
|
%span.nav-item-name
|
||||||
Labels
|
Labels
|
||||||
|
%ul.sidebar-sub-level-items.is-fly-out-only
|
||||||
|
= nav_link(controller: :labels, html_options: { class: "fly-out-top-item" } ) do
|
||||||
|
= link_to admin_labels_path do
|
||||||
|
%strong.fly-out-top-item-name
|
||||||
|
#{ _('Labels') }
|
||||||
|
|
||||||
= nav_link(controller: :appearances) do
|
= nav_link(controller: :appearances) do
|
||||||
= sidebar_link admin_appearances_path, title: _('Appearances') do
|
= sidebar_link admin_appearances_path, title: _('Appearances') do
|
||||||
|
@ -140,6 +191,11 @@
|
||||||
= custom_icon('appearance')
|
= custom_icon('appearance')
|
||||||
%span.nav-item-name
|
%span.nav-item-name
|
||||||
Appearance
|
Appearance
|
||||||
|
%ul.sidebar-sub-level-items.is-fly-out-only
|
||||||
|
= nav_link(controller: :appearances, html_options: { class: "fly-out-top-item" } ) do
|
||||||
|
= link_to admin_appearances_path do
|
||||||
|
%strong.fly-out-top-item-name
|
||||||
|
#{ _('Appearance') }
|
||||||
|
|
||||||
= nav_link(controller: :application_settings) do
|
= nav_link(controller: :application_settings) do
|
||||||
= sidebar_link admin_application_settings_path, title: _('Settings') do
|
= sidebar_link admin_application_settings_path, title: _('Settings') do
|
||||||
|
@ -147,5 +203,10 @@
|
||||||
= custom_icon('settings')
|
= custom_icon('settings')
|
||||||
%span.nav-item-name
|
%span.nav-item-name
|
||||||
Settings
|
Settings
|
||||||
|
%ul.sidebar-sub-level-items.is-fly-out-only
|
||||||
|
= nav_link(controller: :application_settings, html_options: { class: "fly-out-top-item" } ) do
|
||||||
|
= link_to admin_application_settings_path do
|
||||||
|
%strong.fly-out-top-item-name
|
||||||
|
#{ _('Settings') }
|
||||||
|
|
||||||
= render 'shared/sidebar_toggle_button'
|
= render 'shared/sidebar_toggle_button'
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
- issues = IssuesFinder.new(current_user, group_id: @group.id, state: 'opened').execute
|
||||||
|
- merge_requests = MergeRequestsFinder.new(current_user, group_id: @group.id, state: 'opened', non_archived: true).execute
|
||||||
|
|
||||||
.nav-sidebar{ class: ("sidebar-icons-only" if collapsed_sidebar?) }
|
.nav-sidebar{ class: ("sidebar-icons-only" if collapsed_sidebar?) }
|
||||||
.nav-sidebar-inner-scroll
|
.nav-sidebar-inner-scroll
|
||||||
.context-header
|
.context-header
|
||||||
|
@ -15,6 +18,11 @@
|
||||||
Overview
|
Overview
|
||||||
|
|
||||||
%ul.sidebar-sub-level-items
|
%ul.sidebar-sub-level-items
|
||||||
|
= nav_link(path: ['groups#show', 'groups#activity', 'groups#subgroups'], html_options: { class: "fly-out-top-item" } ) do
|
||||||
|
= link_to group_path(@group) do
|
||||||
|
%strong.fly-out-top-item-name
|
||||||
|
#{ _('Overview') }
|
||||||
|
%li.divider.fly-out-top-item
|
||||||
= nav_link(path: ['groups#show', 'groups#subgroups'], html_options: { class: 'home' }) do
|
= nav_link(path: ['groups#show', 'groups#subgroups'], html_options: { class: 'home' }) do
|
||||||
= link_to group_path(@group), title: 'Group details' do
|
= link_to group_path(@group), title: 'Group details' do
|
||||||
%span
|
%span
|
||||||
|
@ -30,11 +38,16 @@
|
||||||
.nav-icon-container
|
.nav-icon-container
|
||||||
= custom_icon('issues')
|
= custom_icon('issues')
|
||||||
%span.nav-item-name
|
%span.nav-item-name
|
||||||
- issues = IssuesFinder.new(current_user, group_id: @group.id, state: 'opened').execute
|
|
||||||
Issues
|
Issues
|
||||||
%span.badge.count= number_with_delimiter(issues.count)
|
%span.badge.count= number_with_delimiter(issues.count)
|
||||||
|
|
||||||
%ul.sidebar-sub-level-items
|
%ul.sidebar-sub-level-items
|
||||||
|
= nav_link(path: ['groups#issues', 'labels#index', 'milestones#index'], html_options: { class: "fly-out-top-item" } ) do
|
||||||
|
= link_to issues_group_path(@group) do
|
||||||
|
%strong.fly-out-top-item-name
|
||||||
|
#{ _('Issues') }
|
||||||
|
%span.badge.count.issue_counter.fly-out-badge= number_with_delimiter(issues.count)
|
||||||
|
%li.divider.fly-out-top-item
|
||||||
= nav_link(path: 'groups#issues', html_options: { class: 'home' }) do
|
= nav_link(path: 'groups#issues', html_options: { class: 'home' }) do
|
||||||
= link_to issues_group_path(@group), title: 'List' do
|
= link_to issues_group_path(@group), title: 'List' do
|
||||||
%span
|
%span
|
||||||
|
@ -55,15 +68,25 @@
|
||||||
.nav-icon-container
|
.nav-icon-container
|
||||||
= custom_icon('mr_bold')
|
= custom_icon('mr_bold')
|
||||||
%span.nav-item-name
|
%span.nav-item-name
|
||||||
- merge_requests = MergeRequestsFinder.new(current_user, group_id: @group.id, state: 'opened', non_archived: true).execute
|
|
||||||
Merge Requests
|
Merge Requests
|
||||||
%span.badge.count= number_with_delimiter(merge_requests.count)
|
%span.badge.count= number_with_delimiter(merge_requests.count)
|
||||||
|
%ul.sidebar-sub-level-items.is-fly-out-only
|
||||||
|
= nav_link(path: 'groups#merge_requests', html_options: { class: "fly-out-top-item" } ) do
|
||||||
|
= link_to merge_requests_group_path(@group) do
|
||||||
|
%strong.fly-out-top-item-name
|
||||||
|
#{ _('Merge Requests') }
|
||||||
|
%span.badge.count.merge_counter.js-merge-counter.fly-out-badge= number_with_delimiter(merge_requests.count)
|
||||||
= nav_link(path: 'group_members#index') do
|
= nav_link(path: 'group_members#index') do
|
||||||
= sidebar_link group_group_members_path(@group), title: _('Members') do
|
= sidebar_link group_group_members_path(@group), title: _('Members') do
|
||||||
.nav-icon-container
|
.nav-icon-container
|
||||||
= custom_icon('members')
|
= custom_icon('members')
|
||||||
%span.nav-item-name
|
%span.nav-item-name
|
||||||
Members
|
Members
|
||||||
|
%ul.sidebar-sub-level-items.is-fly-out-only
|
||||||
|
= nav_link(path: 'group_members#index', html_options: { class: "fly-out-top-item" } ) do
|
||||||
|
= link_to merge_requests_group_path(@group) do
|
||||||
|
%strong.fly-out-top-item-name
|
||||||
|
#{ _('Members') }
|
||||||
- if current_user && can?(current_user, :admin_group, @group)
|
- if current_user && can?(current_user, :admin_group, @group)
|
||||||
= nav_link(path: %w[groups#projects groups#edit ci_cd#show]) do
|
= nav_link(path: %w[groups#projects groups#edit ci_cd#show]) do
|
||||||
= sidebar_link edit_group_path(@group), title: _('Settings') do
|
= sidebar_link edit_group_path(@group), title: _('Settings') do
|
||||||
|
@ -72,6 +95,11 @@
|
||||||
%span.nav-item-name
|
%span.nav-item-name
|
||||||
Settings
|
Settings
|
||||||
%ul.sidebar-sub-level-items
|
%ul.sidebar-sub-level-items
|
||||||
|
= nav_link(path: %w[groups#projects groups#edit ci_cd#show], html_options: { class: "fly-out-top-item" } ) do
|
||||||
|
= link_to edit_group_path(@group) do
|
||||||
|
%strong.fly-out-top-item-name
|
||||||
|
#{ _('Settings') }
|
||||||
|
%li.divider.fly-out-top-item
|
||||||
= nav_link(path: 'groups#edit') do
|
= nav_link(path: 'groups#edit') do
|
||||||
= link_to edit_group_path(@group), title: 'General' do
|
= link_to edit_group_path(@group), title: 'General' do
|
||||||
%span
|
%span
|
||||||
|
|
|
@ -12,12 +12,22 @@
|
||||||
= custom_icon('profile')
|
= custom_icon('profile')
|
||||||
%span.nav-item-name
|
%span.nav-item-name
|
||||||
Profile
|
Profile
|
||||||
|
%ul.sidebar-sub-level-items.is-fly-out-only
|
||||||
|
= nav_link(path: 'profiles#show', html_options: { class: "fly-out-top-item" } ) do
|
||||||
|
= link_to profile_path do
|
||||||
|
%strong.fly-out-top-item-name
|
||||||
|
#{ _('Profile') }
|
||||||
= nav_link(controller: [:accounts, :two_factor_auths]) do
|
= nav_link(controller: [:accounts, :two_factor_auths]) do
|
||||||
= sidebar_link profile_account_path, title: _('Account') do
|
= sidebar_link profile_account_path, title: _('Account') do
|
||||||
.nav-icon-container
|
.nav-icon-container
|
||||||
= custom_icon('account')
|
= custom_icon('account')
|
||||||
%span.nav-item-name
|
%span.nav-item-name
|
||||||
Account
|
Account
|
||||||
|
%ul.sidebar-sub-level-items.is-fly-out-only
|
||||||
|
= nav_link(controller: [:accounts, :two_factor_auths], html_options: { class: "fly-out-top-item" } ) do
|
||||||
|
= link_to profile_account_path do
|
||||||
|
%strong.fly-out-top-item-name
|
||||||
|
#{ _('Account') }
|
||||||
- if current_application_settings.user_oauth_applications?
|
- if current_application_settings.user_oauth_applications?
|
||||||
= nav_link(controller: 'oauth/applications') do
|
= nav_link(controller: 'oauth/applications') do
|
||||||
= sidebar_link applications_profile_path, title: _('Applications') do
|
= sidebar_link applications_profile_path, title: _('Applications') do
|
||||||
|
@ -25,24 +35,44 @@
|
||||||
= custom_icon('applications')
|
= custom_icon('applications')
|
||||||
%span.nav-item-name
|
%span.nav-item-name
|
||||||
Applications
|
Applications
|
||||||
|
%ul.sidebar-sub-level-items.is-fly-out-only
|
||||||
|
= nav_link(controller: 'oauth/applications', html_options: { class: "fly-out-top-item" } ) do
|
||||||
|
= link_to applications_profile_path do
|
||||||
|
%strong.fly-out-top-item-name
|
||||||
|
#{ _('Applications') }
|
||||||
= nav_link(controller: :chat_names) do
|
= nav_link(controller: :chat_names) do
|
||||||
= sidebar_link profile_chat_names_path, title: _('Chat') do
|
= sidebar_link profile_chat_names_path, title: _('Chat') do
|
||||||
.nav-icon-container
|
.nav-icon-container
|
||||||
= custom_icon('chat')
|
= custom_icon('chat')
|
||||||
%span.nav-item-name
|
%span.nav-item-name
|
||||||
Chat
|
Chat
|
||||||
|
%ul.sidebar-sub-level-items.is-fly-out-only
|
||||||
|
= nav_link(controller: :chat_names, html_options: { class: "fly-out-top-item" } ) do
|
||||||
|
= link_to profile_chat_names_path do
|
||||||
|
%strong.fly-out-top-item-name
|
||||||
|
#{ _('Chat') }
|
||||||
= nav_link(controller: :personal_access_tokens) do
|
= nav_link(controller: :personal_access_tokens) do
|
||||||
= sidebar_link profile_personal_access_tokens_path, title: _('Access Tokens') do
|
= sidebar_link profile_personal_access_tokens_path, title: _('Access Tokens') do
|
||||||
.nav-icon-container
|
.nav-icon-container
|
||||||
= custom_icon('access_tokens')
|
= custom_icon('access_tokens')
|
||||||
%span.nav-item-name
|
%span.nav-item-name
|
||||||
Access Tokens
|
Access Tokens
|
||||||
|
%ul.sidebar-sub-level-items.is-fly-out-only
|
||||||
|
= nav_link(controller: :personal_access_tokens, html_options: { class: "fly-out-top-item" } ) do
|
||||||
|
= link_to profile_personal_access_tokens_path do
|
||||||
|
%strong.fly-out-top-item-name
|
||||||
|
#{ _('Access Tokens') }
|
||||||
= nav_link(controller: :emails) do
|
= nav_link(controller: :emails) do
|
||||||
= sidebar_link profile_emails_path, title: _('Emails') do
|
= sidebar_link profile_emails_path, title: _('Emails') do
|
||||||
.nav-icon-container
|
.nav-icon-container
|
||||||
= custom_icon('emails')
|
= custom_icon('emails')
|
||||||
%span.nav-item-name
|
%span.nav-item-name
|
||||||
Emails
|
Emails
|
||||||
|
%ul.sidebar-sub-level-items.is-fly-out-only
|
||||||
|
= nav_link(controller: :emails, html_options: { class: "fly-out-top-item" } ) do
|
||||||
|
= link_to profile_emails_path do
|
||||||
|
%strong.fly-out-top-item-name
|
||||||
|
#{ _('Emails') }
|
||||||
- unless current_user.ldap_user?
|
- unless current_user.ldap_user?
|
||||||
= nav_link(controller: :passwords) do
|
= nav_link(controller: :passwords) do
|
||||||
= sidebar_link edit_profile_password_path, title: _('Password') do
|
= sidebar_link edit_profile_password_path, title: _('Password') do
|
||||||
|
@ -50,36 +80,65 @@
|
||||||
= custom_icon('lock')
|
= custom_icon('lock')
|
||||||
%span.nav-item-name
|
%span.nav-item-name
|
||||||
Password
|
Password
|
||||||
|
%ul.sidebar-sub-level-items.is-fly-out-only
|
||||||
|
= nav_link(controller: :passwords, html_options: { class: "fly-out-top-item" } ) do
|
||||||
|
= link_to edit_profile_password_path do
|
||||||
|
%strong.fly-out-top-item-name
|
||||||
|
#{ _('Password') }
|
||||||
= nav_link(controller: :notifications) do
|
= nav_link(controller: :notifications) do
|
||||||
= sidebar_link profile_notifications_path, title: _('Notifications') do
|
= sidebar_link profile_notifications_path, title: _('Notifications') do
|
||||||
.nav-icon-container
|
.nav-icon-container
|
||||||
= custom_icon('notifications')
|
= custom_icon('notifications')
|
||||||
%span.nav-item-name
|
%span.nav-item-name
|
||||||
Notifications
|
Notifications
|
||||||
|
%ul.sidebar-sub-level-items.is-fly-out-only
|
||||||
|
= nav_link(controller: :notifications, html_options: { class: "fly-out-top-item" } ) do
|
||||||
|
= link_to profile_notifications_path do
|
||||||
|
%strong.fly-out-top-item-name
|
||||||
|
#{ _('Notifications') }
|
||||||
= nav_link(controller: :keys) do
|
= nav_link(controller: :keys) do
|
||||||
= sidebar_link profile_keys_path, title: _('SSH Keys') do
|
= sidebar_link profile_keys_path, title: _('SSH Keys') do
|
||||||
.nav-icon-container
|
.nav-icon-container
|
||||||
= custom_icon('key')
|
= custom_icon('key')
|
||||||
%span.nav-item-name
|
%span.nav-item-name
|
||||||
SSH Keys
|
SSH Keys
|
||||||
|
%ul.sidebar-sub-level-items.is-fly-out-only
|
||||||
|
= nav_link(controller: :keys, html_options: { class: "fly-out-top-item" } ) do
|
||||||
|
= link_to profile_keys_path do
|
||||||
|
%strong.fly-out-top-item-name
|
||||||
|
#{ _('SSH Keys') }
|
||||||
= nav_link(controller: :gpg_keys) do
|
= nav_link(controller: :gpg_keys) do
|
||||||
= sidebar_link profile_gpg_keys_path, title: _('GPG Keys') do
|
= sidebar_link profile_gpg_keys_path, title: _('GPG Keys') do
|
||||||
.nav-icon-container
|
.nav-icon-container
|
||||||
= custom_icon('key_2')
|
= custom_icon('key_2')
|
||||||
%span.nav-item-name
|
%span.nav-item-name
|
||||||
GPG Keys
|
GPG Keys
|
||||||
|
%ul.sidebar-sub-level-items.is-fly-out-only
|
||||||
|
= nav_link(controller: :gpg_keys, html_options: { class: "fly-out-top-item" } ) do
|
||||||
|
= link_to profile_gpg_keys_path do
|
||||||
|
%strong.fly-out-top-item-name
|
||||||
|
#{ _('GPG Keys') }
|
||||||
= nav_link(controller: :preferences) do
|
= nav_link(controller: :preferences) do
|
||||||
= sidebar_link profile_preferences_path, title: _('Preferences') do
|
= sidebar_link profile_preferences_path, title: _('Preferences') do
|
||||||
.nav-icon-container
|
.nav-icon-container
|
||||||
= custom_icon('preferences')
|
= custom_icon('preferences')
|
||||||
%span.nav-item-name
|
%span.nav-item-name
|
||||||
Preferences
|
Preferences
|
||||||
|
%ul.sidebar-sub-level-items.is-fly-out-only
|
||||||
|
= nav_link(controller: :preferences, html_options: { class: "fly-out-top-item" } ) do
|
||||||
|
= link_to profile_preferences_path do
|
||||||
|
%strong.fly-out-top-item-name
|
||||||
|
#{ _('Preferences') }
|
||||||
= nav_link(path: 'profiles#audit_log') do
|
= nav_link(path: 'profiles#audit_log') do
|
||||||
= sidebar_link audit_log_profile_path, title: _('Authentication log') do
|
= sidebar_link audit_log_profile_path, title: _('Authentication log') do
|
||||||
.nav-icon-container
|
.nav-icon-container
|
||||||
= custom_icon('authentication_log')
|
= custom_icon('authentication_log')
|
||||||
%span.nav-item-name
|
%span.nav-item-name
|
||||||
Authentication log
|
Authentication log
|
||||||
|
%ul.sidebar-sub-level-items.is-fly-out-only
|
||||||
|
= nav_link(path: 'profiles#audit_log', html_options: { class: "fly-out-top-item" } ) do
|
||||||
|
= link_to audit_log_profile_path do
|
||||||
|
%strong.fly-out-top-item-name
|
||||||
|
#{ _('Authentication Log') }
|
||||||
|
|
||||||
= render 'shared/sidebar_toggle_button'
|
= render 'shared/sidebar_toggle_button'
|
||||||
|
|
|
@ -16,6 +16,11 @@
|
||||||
Overview
|
Overview
|
||||||
|
|
||||||
%ul.sidebar-sub-level-items
|
%ul.sidebar-sub-level-items
|
||||||
|
= nav_link(path: 'projects#show', html_options: { class: "fly-out-top-item" } ) do
|
||||||
|
= link_to project_path(@project) do
|
||||||
|
%strong.fly-out-top-item-name
|
||||||
|
#{ _('Overview') }
|
||||||
|
%li.divider.fly-out-top-item
|
||||||
= nav_link(path: 'projects#show') do
|
= nav_link(path: 'projects#show') do
|
||||||
= link_to project_path(@project), title: _('Project details'), class: 'shortcuts-project' do
|
= link_to project_path(@project), title: _('Project details'), class: 'shortcuts-project' do
|
||||||
%span= _('Details')
|
%span= _('Details')
|
||||||
|
@ -38,6 +43,11 @@
|
||||||
Repository
|
Repository
|
||||||
|
|
||||||
%ul.sidebar-sub-level-items
|
%ul.sidebar-sub-level-items
|
||||||
|
= nav_link(controller: %w(tree blob blame edit_tree new_tree find_file commit commits compare projects/repositories tags branches releases graphs network), html_options: { class: "fly-out-top-item" } ) do
|
||||||
|
= link_to project_tree_path(@project) do
|
||||||
|
%strong.fly-out-top-item-name
|
||||||
|
#{ _('Repository') }
|
||||||
|
%li.divider.fly-out-top-item
|
||||||
= nav_link(controller: %w(tree blob blame edit_tree new_tree find_file)) do
|
= nav_link(controller: %w(tree blob blame edit_tree new_tree find_file)) do
|
||||||
= link_to project_tree_path(@project) do
|
= link_to project_tree_path(@project) do
|
||||||
#{ _('Files') }
|
#{ _('Files') }
|
||||||
|
@ -90,6 +100,14 @@
|
||||||
= number_with_delimiter(@project.open_issues_count)
|
= number_with_delimiter(@project.open_issues_count)
|
||||||
|
|
||||||
%ul.sidebar-sub-level-items
|
%ul.sidebar-sub-level-items
|
||||||
|
= nav_link(controller: :issues, html_options: { class: "fly-out-top-item" } ) do
|
||||||
|
= link_to project_issues_path(@project) do
|
||||||
|
%strong.fly-out-top-item-name
|
||||||
|
#{ _('Issues') }
|
||||||
|
- if @project.issues_enabled?
|
||||||
|
%span.badge.count.issue_counter.fly-out-badge
|
||||||
|
= number_with_delimiter(@project.open_issues_count)
|
||||||
|
%li.divider.fly-out-top-item
|
||||||
= nav_link(controller: :issues) do
|
= nav_link(controller: :issues) do
|
||||||
= link_to project_issues_path(@project), title: 'Issues' do
|
= link_to project_issues_path(@project), title: 'Issues' do
|
||||||
%span
|
%span
|
||||||
|
@ -133,6 +151,13 @@
|
||||||
Merge Requests
|
Merge Requests
|
||||||
%span.badge.count.merge_counter.js-merge-counter
|
%span.badge.count.merge_counter.js-merge-counter
|
||||||
= number_with_delimiter(@project.open_merge_requests_count)
|
= number_with_delimiter(@project.open_merge_requests_count)
|
||||||
|
%ul.sidebar-sub-level-items.is-fly-out-only
|
||||||
|
= nav_link(controller: :merge_requests, html_options: { class: "fly-out-top-item" } ) do
|
||||||
|
= link_to project_merge_requests_path(@project) do
|
||||||
|
%strong.fly-out-top-item-name
|
||||||
|
#{ _('Merge Requests') }
|
||||||
|
%span.badge.count.merge_counter.js-merge-counter.fly-out-badge
|
||||||
|
= number_with_delimiter(@project.open_merge_requests_count)
|
||||||
|
|
||||||
- if project_nav_tab? :pipelines
|
- if project_nav_tab? :pipelines
|
||||||
= nav_link(controller: [:pipelines, :builds, :jobs, :pipeline_schedules, :environments, :artifacts]) do
|
= nav_link(controller: [:pipelines, :builds, :jobs, :pipeline_schedules, :environments, :artifacts]) do
|
||||||
|
@ -143,6 +168,11 @@
|
||||||
CI / CD
|
CI / CD
|
||||||
|
|
||||||
%ul.sidebar-sub-level-items
|
%ul.sidebar-sub-level-items
|
||||||
|
= nav_link(controller: [:pipelines, :builds, :jobs, :pipeline_schedules, :environments, :artifacts], html_options: { class: "fly-out-top-item" } ) do
|
||||||
|
= link_to project_pipelines_path(@project) do
|
||||||
|
%strong.fly-out-top-item-name
|
||||||
|
#{ _('CI / CD') }
|
||||||
|
%li.divider.fly-out-top-item
|
||||||
- if project_nav_tab? :pipelines
|
- if project_nav_tab? :pipelines
|
||||||
= nav_link(path: ['pipelines#index', 'pipelines#show']) do
|
= nav_link(path: ['pipelines#index', 'pipelines#show']) do
|
||||||
= link_to project_pipelines_path(@project), title: 'Pipelines', class: 'shortcuts-pipelines' do
|
= link_to project_pipelines_path(@project), title: 'Pipelines', class: 'shortcuts-pipelines' do
|
||||||
|
@ -180,6 +210,11 @@
|
||||||
= custom_icon('wiki')
|
= custom_icon('wiki')
|
||||||
%span.nav-item-name
|
%span.nav-item-name
|
||||||
Wiki
|
Wiki
|
||||||
|
%ul.sidebar-sub-level-items.is-fly-out-only
|
||||||
|
= nav_link(controller: :wikis, html_options: { class: "fly-out-top-item" } ) do
|
||||||
|
= link_to get_project_wiki_path(@project) do
|
||||||
|
%strong.fly-out-top-item-name
|
||||||
|
#{ _('Wiki') }
|
||||||
|
|
||||||
- if project_nav_tab? :snippets
|
- if project_nav_tab? :snippets
|
||||||
= nav_link(controller: :snippets) do
|
= nav_link(controller: :snippets) do
|
||||||
|
@ -188,6 +223,11 @@
|
||||||
= custom_icon('snippets')
|
= custom_icon('snippets')
|
||||||
%span.nav-item-name
|
%span.nav-item-name
|
||||||
Snippets
|
Snippets
|
||||||
|
%ul.sidebar-sub-level-items.is-fly-out-only
|
||||||
|
= nav_link(controller: :snippets, html_options: { class: "fly-out-top-item" } ) do
|
||||||
|
= link_to project_snippets_path(@project) do
|
||||||
|
%strong.fly-out-top-item-name
|
||||||
|
#{ _('Snippets') }
|
||||||
|
|
||||||
- if project_nav_tab? :settings
|
- if project_nav_tab? :settings
|
||||||
= nav_link(path: %w[projects#edit project_members#index integrations#show services#edit repository#show ci_cd#show pages#show]) do
|
= nav_link(path: %w[projects#edit project_members#index integrations#show services#edit repository#show ci_cd#show pages#show]) do
|
||||||
|
@ -200,6 +240,11 @@
|
||||||
%ul.sidebar-sub-level-items
|
%ul.sidebar-sub-level-items
|
||||||
- can_edit = can?(current_user, :admin_project, @project)
|
- can_edit = can?(current_user, :admin_project, @project)
|
||||||
- if can_edit
|
- if can_edit
|
||||||
|
= nav_link(path: %w[projects#edit project_members#index integrations#show services#edit repository#show ci_cd#show pages#show], html_options: { class: "fly-out-top-item" } ) do
|
||||||
|
= link_to edit_project_path(@project) do
|
||||||
|
%strong.fly-out-top-item-name
|
||||||
|
#{ _('Settings') }
|
||||||
|
%li.divider.fly-out-top-item
|
||||||
= nav_link(path: %w[projects#edit]) do
|
= nav_link(path: %w[projects#edit]) do
|
||||||
= link_to edit_project_path(@project), title: 'General' do
|
= link_to edit_project_path(@project), title: 'General' do
|
||||||
%span
|
%span
|
||||||
|
|
|
@ -3,6 +3,26 @@
|
||||||
= render 'profiles/head'
|
= render 'profiles/head'
|
||||||
|
|
||||||
= form_for @user, url: profile_preferences_path, remote: true, method: :put, html: { class: 'row prepend-top-default js-preferences-form' } do |f|
|
= form_for @user, url: profile_preferences_path, remote: true, method: :put, html: { class: 'row prepend-top-default js-preferences-form' } do |f|
|
||||||
|
.col-lg-4.application-theme
|
||||||
|
%h4.prepend-top-0
|
||||||
|
GitLab navigation theme
|
||||||
|
%p Customize the appearance of the application header and navigation sidebar.
|
||||||
|
.col-lg-8.application-theme
|
||||||
|
- Gitlab::Themes.each do |theme|
|
||||||
|
= label_tag do
|
||||||
|
.preview{ class: theme.name.downcase }
|
||||||
|
.preview-row
|
||||||
|
.quadrant.one
|
||||||
|
.quadrant.two
|
||||||
|
.preview-row
|
||||||
|
.quadrant.three
|
||||||
|
.quadrant.four
|
||||||
|
= f.radio_button :theme_id, theme.id
|
||||||
|
= theme.name
|
||||||
|
|
||||||
|
.col-sm-12
|
||||||
|
%hr
|
||||||
|
|
||||||
.col-lg-4.profile-settings-sidebar
|
.col-lg-4.profile-settings-sidebar
|
||||||
%h4.prepend-top-0
|
%h4.prepend-top-0
|
||||||
Syntax highlighting theme
|
Syntax highlighting theme
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// Remove body class for any previous theme, re-add current one
|
||||||
|
$('body').removeClass('<%= Gitlab::Themes.body_classes %>')
|
||||||
|
$('body').addClass('<%= user_application_theme %>')
|
||||||
|
|
||||||
// Toggle container-fluid class
|
// Toggle container-fluid class
|
||||||
if ('<%= current_user.layout %>' === 'fluid') {
|
if ('<%= current_user.layout %>' === 'fluid') {
|
||||||
$('.content-wrapper .container-fluid').removeClass('container-limited')
|
$('.content-wrapper .container-fluid').removeClass('container-limited')
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
= render "show"
|
= render "shared/boards/show", board: @boards.first
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
= render "show"
|
= render "shared/boards/show", board: @board
|
||||||
|
|
|
@ -28,8 +28,8 @@
|
||||||
= link_to icon('question-circle'), help_page_path("gitlab-basics/create-project"), target: '_blank', aria: { label: "What’s included in a template?" }, title: "What’s included in a template?", class: 'has-tooltip', data: { placement: 'top'}
|
= link_to icon('question-circle'), help_page_path("gitlab-basics/create-project"), target: '_blank', aria: { label: "What’s included in a template?" }, title: "What’s included in a template?", class: 'has-tooltip', data: { placement: 'top'}
|
||||||
%div
|
%div
|
||||||
= render 'project_templates', f: f
|
= render 'project_templates', f: f
|
||||||
.second-column
|
|
||||||
- if import_sources_enabled?
|
- if import_sources_enabled?
|
||||||
|
.second-column
|
||||||
.project-import
|
.project-import
|
||||||
.form-group.clearfix
|
.form-group.clearfix
|
||||||
= f.label :visibility_level, class: 'label-light' do #the label here seems wrong
|
= f.label :visibility_level, class: 'label-light' do #the label here seems wrong
|
||||||
|
|
|
@ -27,9 +27,10 @@
|
||||||
= link_to project_tags_path(@project) do
|
= link_to project_tags_path(@project) do
|
||||||
#{n_('Tag', 'Tags', @repository.tag_count)} (#{number_with_delimiter(@repository.tag_count)})
|
#{n_('Tag', 'Tags', @repository.tag_count)} (#{number_with_delimiter(@repository.tag_count)})
|
||||||
|
|
||||||
- if default_project_view != 'readme' && @repository.readme
|
- if @repository.readme
|
||||||
%li
|
%li
|
||||||
= link_to _('Readme'), readme_path(@project)
|
= link_to _('Readme'),
|
||||||
|
default_project_view != 'readme' ? readme_path(@project) : '#readme'
|
||||||
|
|
||||||
- if @repository.changelog
|
- if @repository.changelog
|
||||||
%li
|
%li
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
- if readme.rich_viewer
|
- if readme.rich_viewer
|
||||||
%article.file-holder.readme-holder{ class: ("limited-width-container" unless fluid_layout) }
|
%article.file-holder.readme-holder{ id: 'readme', class: ("limited-width-container" unless fluid_layout) }
|
||||||
.js-file-title.file-title
|
.js-file-title.file-title
|
||||||
= blob_icon readme.mode, readme.name
|
= blob_icon readme.mode, readme.name
|
||||||
= link_to project_blob_path(@project, tree_join(@ref, readme.path)) do
|
= link_to project_blob_path(@project, tree_join(@ref, readme.path)) do
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
= webpack_bundle_tag 'filtered_search'
|
= webpack_bundle_tag 'filtered_search'
|
||||||
= webpack_bundle_tag 'boards'
|
= webpack_bundle_tag 'boards'
|
||||||
|
|
||||||
%script#js-board-template{ type: "text/x-template" }= render "projects/boards/components/board"
|
%script#js-board-template{ type: "text/x-template" }= render "shared/boards/components/board"
|
||||||
%script#js-board-modal-filter{ type: "text/x-template" }= render "shared/issuable/search_bar", type: :boards_modal
|
%script#js-board-modal-filter{ type: "text/x-template" }= render "shared/issuable/search_bar", type: :boards_modal
|
||||||
|
|
||||||
= render "projects/issues/head"
|
= render "projects/issues/head"
|
||||||
|
@ -30,7 +30,7 @@
|
||||||
":root-path" => "rootPath",
|
":root-path" => "rootPath",
|
||||||
":board-id" => "boardId",
|
":board-id" => "boardId",
|
||||||
":key" => "_uid" }
|
":key" => "_uid" }
|
||||||
= render "projects/boards/components/sidebar"
|
= render "shared/boards/components/sidebar"
|
||||||
%board-add-issues-modal{ "blank-state-image" => render('shared/empty_states/icons/issues.svg'),
|
%board-add-issues-modal{ "blank-state-image" => render('shared/empty_states/icons/issues.svg'),
|
||||||
"new-issue-path" => new_project_issue_path(@project),
|
"new-issue-path" => new_project_issue_path(@project),
|
||||||
"milestone-path" => milestones_filter_dropdown_path,
|
"milestone-path" => milestones_filter_dropdown_path,
|
|
@ -7,20 +7,26 @@
|
||||||
":class": "{ \"fa-caret-down\": list.isExpanded, \"fa-caret-right\": !list.isExpanded && list.position === -1, \"fa-caret-left\": !list.isExpanded && list.position !== -1 }",
|
":class": "{ \"fa-caret-down\": list.isExpanded, \"fa-caret-right\": !list.isExpanded && list.position === -1, \"fa-caret-left\": !list.isExpanded && list.position !== -1 }",
|
||||||
"aria-hidden": "true" }
|
"aria-hidden": "true" }
|
||||||
|
|
||||||
%span.has-tooltip{ "v-if": "list.type !== \"label\"",
|
%span.board-title-text.has-tooltip{ "v-if": "list.type !== \"label\"",
|
||||||
":title" => '(list.label ? list.label.description : "")' }
|
":title" => '(list.label ? list.label.description : "")' }
|
||||||
{{ list.title }}
|
{{ list.title }}
|
||||||
|
|
||||||
%span.has-tooltip{ "v-if": "list.type === \"label\"",
|
%span.has-tooltip{ "v-if": "list.type === \"label\"",
|
||||||
":title" => '(list.label ? list.label.description : "")',
|
":title" => '(list.label ? list.label.description : "")',
|
||||||
data: { container: "body", placement: "bottom" },
|
data: { container: "body", placement: "bottom" },
|
||||||
class: "label color-label title",
|
class: "label color-label title board-title-text",
|
||||||
":style" => "{ backgroundColor: (list.label && list.label.color ? list.label.color : null), color: (list.label && list.label.color ? list.label.text_color : \"#2e2e2e\") }" }
|
":style" => "{ backgroundColor: (list.label && list.label.color ? list.label.color : null), color: (list.label && list.label.color ? list.label.text_color : \"#2e2e2e\") }" }
|
||||||
{{ list.title }}
|
{{ list.title }}
|
||||||
.issue-count-badge.pull-right.clearfix{ "v-if" => 'list.type !== "blank"' }
|
- if can?(current_user, :admin_list, current_board_parent)
|
||||||
|
%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")
|
||||||
|
.issue-count-badge.clearfix{ "v-if" => 'list.type !== "blank"' }
|
||||||
%span.issue-count-badge-count.pull-left{ ":class" => '{ "has-btn": list.type !== "closed" && !disabled }' }
|
%span.issue-count-badge-count.pull-left{ ":class" => '{ "has-btn": list.type !== "closed" && !disabled }' }
|
||||||
{{ list.issuesSize }}
|
{{ list.issuesSize }}
|
||||||
- if can?(current_user, :admin_issue, @project)
|
- if can?(current_user, :admin_list, current_board_parent)
|
||||||
%button.issue-count-badge-add-button.btn.btn-small.btn-default.has-tooltip.js-no-trigger-collapse{ type: "button",
|
%button.issue-count-badge-add-button.btn.btn-small.btn-default.has-tooltip.js-no-trigger-collapse{ type: "button",
|
||||||
"@click" => "showNewIssueForm",
|
"@click" => "showNewIssueForm",
|
||||||
"v-if" => 'list.type !== "closed"',
|
"v-if" => 'list.type !== "closed"',
|
||||||
|
@ -28,12 +34,7 @@
|
||||||
"title" => "New issue",
|
"title" => "New issue",
|
||||||
data: { placement: "top", container: "body" } }
|
data: { placement: "top", container: "body" } }
|
||||||
= icon("plus", class: "js-no-trigger-collapse")
|
= icon("plus", class: "js-no-trigger-collapse")
|
||||||
- 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{ "v-if" => 'list.type !== "blank"',
|
%board-list{ "v-if" => 'list.type !== "blank"',
|
||||||
":list" => "list",
|
":list" => "list",
|
||||||
":issues" => "list.issues",
|
":issues" => "list.issues",
|
||||||
|
@ -42,5 +43,5 @@
|
||||||
":issue-link-base" => "issueLinkBase",
|
":issue-link-base" => "issueLinkBase",
|
||||||
":root-path" => "rootPath",
|
":root-path" => "rootPath",
|
||||||
"ref" => "board-list" }
|
"ref" => "board-list" }
|
||||||
- if can?(current_user, :admin_list, @project)
|
- if can?(current_user, :admin_list, current_board_parent)
|
||||||
%board-blank-state{ "v-if" => 'list.id == "blank"' }
|
%board-blank-state{ "v-if" => 'list.id == "blank"' }
|
|
@ -10,18 +10,19 @@
|
||||||
%br/
|
%br/
|
||||||
%span
|
%span
|
||||||
= precede "#" do
|
= precede "#" do
|
||||||
{{ issue.id }}
|
{{ issue.iid }}
|
||||||
%a.gutter-toggle.pull-right{ role: "button",
|
%a.gutter-toggle.pull-right{ role: "button",
|
||||||
href: "#",
|
href: "#",
|
||||||
"@click.prevent" => "closeSidebar",
|
"@click.prevent" => "closeSidebar",
|
||||||
"aria-label" => "Toggle sidebar" }
|
"aria-label" => "Toggle sidebar" }
|
||||||
= custom_icon("icon_close", size: 15)
|
= custom_icon("icon_close", size: 15)
|
||||||
.js-issuable-update
|
.js-issuable-update
|
||||||
= render "projects/boards/components/sidebar/assignee"
|
= render "shared/boards/components/sidebar/assignee"
|
||||||
= render "projects/boards/components/sidebar/milestone"
|
= render "shared/boards/components/sidebar/milestone"
|
||||||
= render "projects/boards/components/sidebar/due_date"
|
= render "shared/boards/components/sidebar/due_date"
|
||||||
= render "projects/boards/components/sidebar/labels"
|
= render "shared/boards/components/sidebar/labels"
|
||||||
= render "projects/boards/components/sidebar/notifications"
|
= render "shared/boards/components/sidebar/notifications"
|
||||||
%remove-btn{ ":issue" => "issue",
|
%remove-btn{ ":issue" => "issue",
|
||||||
|
":issue-update" => "'#{build_issue_link_base}/' + issue.iid + '.json'",
|
||||||
":list" => "list",
|
":list" => "list",
|
||||||
"v-if" => "canRemove" }
|
"v-if" => "canRemove" }
|
|
@ -2,13 +2,13 @@
|
||||||
%template{ "v-if" => "issue.assignees" }
|
%template{ "v-if" => "issue.assignees" }
|
||||||
%assignee-title{ ":number-of-assignees" => "issue.assignees.length",
|
%assignee-title{ ":number-of-assignees" => "issue.assignees.length",
|
||||||
":loading" => "loadingAssignees",
|
":loading" => "loadingAssignees",
|
||||||
":editable" => can?(current_user, :admin_issue, @project) }
|
":editable" => can_admin_issue? }
|
||||||
%assignees.value{ "root-path" => "#{root_url}",
|
%assignees.value{ "root-path" => "#{root_url}",
|
||||||
":users" => "issue.assignees",
|
":users" => "issue.assignees",
|
||||||
":editable" => can?(current_user, :admin_issue, @project),
|
":editable" => can_admin_issue?,
|
||||||
"@assign-self" => "assignSelf" }
|
"@assign-self" => "assignSelf" }
|
||||||
|
|
||||||
- if can?(current_user, :admin_issue, @project)
|
- if can_admin_issue?
|
||||||
.selectbox.hide-collapsed
|
.selectbox.hide-collapsed
|
||||||
%input.js-vue{ type: "hidden",
|
%input.js-vue{ type: "hidden",
|
||||||
name: "issue[assignee_ids][]",
|
name: "issue[assignee_ids][]",
|
||||||
|
@ -20,9 +20,9 @@
|
||||||
":data-username" => "assignee.username" }
|
":data-username" => "assignee.username" }
|
||||||
.dropdown
|
.dropdown
|
||||||
- dropdown_options = issue_assignees_dropdown_options
|
- dropdown_options = issue_assignees_dropdown_options
|
||||||
%button.dropdown-menu-toggle.js-user-search.js-author-search.js-multiselect.js-save-user-data.js-issue-board-sidebar{ type: 'button', ref: 'assigneeDropdown', data: { toggle: 'dropdown', field_name: 'issue[assignee_ids][]', first_user: current_user&.username, current_user: 'true', project_id: @project.id, null_user: 'true', multi_select: 'true', 'dropdown-header': dropdown_options[:data][:'dropdown-header'], 'max-select': dropdown_options[:data][:'max-select'] },
|
%button.dropdown-menu-toggle.js-user-search.js-author-search.js-multiselect.js-save-user-data.js-issue-board-sidebar{ type: 'button', ref: 'assigneeDropdown', data: board_sidebar_user_data,
|
||||||
":data-issuable-id" => "issue.id",
|
":data-issuable-id" => "issue.iid",
|
||||||
":data-issue-update" => "'#{project_issues_path(@project)}/' + issue.id + '.json'" }
|
":data-issue-update" => "'#{build_issue_link_base}/' + issue.iid + '.json'" }
|
||||||
= dropdown_options[:title]
|
= dropdown_options[:title]
|
||||||
= icon("chevron-down")
|
= icon("chevron-down")
|
||||||
.dropdown-menu.dropdown-select.dropdown-menu-user.dropdown-menu-selectable.dropdown-menu-author
|
.dropdown-menu.dropdown-select.dropdown-menu-user.dropdown-menu-selectable.dropdown-menu-author
|
|
@ -1,7 +1,7 @@
|
||||||
.block.due_date
|
.block.due_date
|
||||||
.title
|
.title
|
||||||
Due date
|
Due date
|
||||||
- if can?(current_user, :admin_issue, @project)
|
- if can_admin_issue?
|
||||||
= icon("spinner spin", class: "block-loading")
|
= icon("spinner spin", class: "block-loading")
|
||||||
= link_to "Edit", "#", class: "js-sidebar-dropdown-toggle edit-link pull-right"
|
= link_to "Edit", "#", class: "js-sidebar-dropdown-toggle edit-link pull-right"
|
||||||
.value
|
.value
|
||||||
|
@ -10,12 +10,12 @@
|
||||||
No due date
|
No due date
|
||||||
%span.bold{ "v-if" => "issue.dueDate" }
|
%span.bold{ "v-if" => "issue.dueDate" }
|
||||||
{{ issue.dueDate | due-date }}
|
{{ issue.dueDate | due-date }}
|
||||||
- if can?(current_user, :admin_issue, @project)
|
- if can_admin_issue?
|
||||||
%span.no-value.js-remove-due-date-holder{ "v-if" => "issue.dueDate" }
|
%span.no-value.js-remove-due-date-holder{ "v-if" => "issue.dueDate" }
|
||||||
\-
|
\-
|
||||||
%a.js-remove-due-date{ href: "#", role: "button" }
|
%a.js-remove-due-date{ href: "#", role: "button" }
|
||||||
remove due date
|
remove due date
|
||||||
- if can?(current_user, :admin_issue, @project)
|
- if can_admin_issue?
|
||||||
.selectbox
|
.selectbox
|
||||||
%input{ type: "hidden",
|
%input{ type: "hidden",
|
||||||
name: "issue[due_date]",
|
name: "issue[due_date]",
|
||||||
|
@ -23,7 +23,7 @@
|
||||||
.dropdown
|
.dropdown
|
||||||
%button.dropdown-menu-toggle.js-due-date-select.js-issue-boards-due-date{ type: 'button',
|
%button.dropdown-menu-toggle.js-due-date-select.js-issue-boards-due-date{ type: 'button',
|
||||||
data: { toggle: 'dropdown', field_name: "issue[due_date]", ability_name: "issue" },
|
data: { toggle: 'dropdown', field_name: "issue[due_date]", ability_name: "issue" },
|
||||||
":data-issue-update" => "'#{project_issues_path(@project)}/' + issue.id + '.json'" }
|
":data-issue-update" => "'#{build_issue_link_base}/' + issue.iid + '.json'" }
|
||||||
%span.dropdown-toggle-text Due date
|
%span.dropdown-toggle-text Due date
|
||||||
= icon('chevron-down')
|
= icon('chevron-down')
|
||||||
.dropdown-menu.dropdown-menu-due-date
|
.dropdown-menu.dropdown-menu-due-date
|
|
@ -1,7 +1,7 @@
|
||||||
.block.labels
|
.block.labels
|
||||||
.title
|
.title
|
||||||
Labels
|
Labels
|
||||||
- if can?(current_user, :admin_issue, @project)
|
- if can_admin_issue?
|
||||||
= icon("spinner spin", class: "block-loading")
|
= icon("spinner spin", class: "block-loading")
|
||||||
= link_to "Edit", "#", class: "js-sidebar-dropdown-toggle edit-link pull-right"
|
= link_to "Edit", "#", class: "js-sidebar-dropdown-toggle edit-link pull-right"
|
||||||
.value.issuable-show-labels
|
.value.issuable-show-labels
|
||||||
|
@ -11,7 +11,7 @@
|
||||||
"v-for" => "label in issue.labels" }
|
"v-for" => "label in issue.labels" }
|
||||||
%span.label.color-label.has-tooltip{ ":style" => "{ backgroundColor: label.color, color: label.textColor }" }
|
%span.label.color-label.has-tooltip{ ":style" => "{ backgroundColor: label.color, color: label.textColor }" }
|
||||||
{{ label.title }}
|
{{ label.title }}
|
||||||
- if can?(current_user, :admin_issue, @project)
|
- if can_admin_issue?
|
||||||
.selectbox
|
.selectbox
|
||||||
%input{ type: "hidden",
|
%input{ type: "hidden",
|
||||||
name: "issue[label_names][]",
|
name: "issue[label_names][]",
|
||||||
|
@ -19,12 +19,19 @@
|
||||||
":value" => "label.id" }
|
":value" => "label.id" }
|
||||||
.dropdown
|
.dropdown
|
||||||
%button.dropdown-menu-toggle.js-label-select.js-multiselect.js-issue-board-sidebar{ type: "button",
|
%button.dropdown-menu-toggle.js-label-select.js-multiselect.js-issue-board-sidebar{ type: "button",
|
||||||
data: { toggle: "dropdown", field_name: "issue[label_names][]", show_no: "true", show_any: "true", project_id: @project.id, labels: project_labels_path(@project, :json), namespace_path: @project.try(:namespace).try(:full_path), project_path: @project.try(:path) },
|
data: { toggle: "dropdown",
|
||||||
":data-issue-update" => "'#{project_issues_path(@project)}/' + issue.id + '.json'" }
|
field_name: "issue[label_names][]",
|
||||||
|
show_no: "true",
|
||||||
|
show_any: "true",
|
||||||
|
project_id: @project&.try(:id),
|
||||||
|
labels: labels_filter_path(false),
|
||||||
|
namespace_path: @project.try(:namespace).try(:full_path),
|
||||||
|
project_path: @project.try(:path) },
|
||||||
|
":data-issue-update" => "'#{build_issue_link_base}/' + issue.iid + '.json'" }
|
||||||
%span.dropdown-toggle-text
|
%span.dropdown-toggle-text
|
||||||
Label
|
Label
|
||||||
= icon('chevron-down')
|
= icon('chevron-down')
|
||||||
.dropdown-menu.dropdown-select.dropdown-menu-paging.dropdown-menu-labels.dropdown-menu-selectable
|
.dropdown-menu.dropdown-select.dropdown-menu-paging.dropdown-menu-labels.dropdown-menu-selectable
|
||||||
= render partial: "shared/issuable/label_page_default"
|
= render partial: "shared/issuable/label_page_default"
|
||||||
- if can? current_user, :admin_label, @project and @project
|
- if can?(current_user, :admin_label, current_board_parent)
|
||||||
= render partial: "shared/issuable/label_page_create"
|
= render partial: "shared/issuable/label_page_create"
|
|
@ -1,7 +1,7 @@
|
||||||
.block.milestone
|
.block.milestone
|
||||||
.title
|
.title
|
||||||
Milestone
|
Milestone
|
||||||
- if can?(current_user, :admin_issue, @project)
|
- if can_admin_issue?
|
||||||
= icon("spinner spin", class: "block-loading")
|
= icon("spinner spin", class: "block-loading")
|
||||||
= link_to "Edit", "#", class: "js-sidebar-dropdown-toggle edit-link pull-right"
|
= link_to "Edit", "#", class: "js-sidebar-dropdown-toggle edit-link pull-right"
|
||||||
.value
|
.value
|
||||||
|
@ -9,17 +9,17 @@
|
||||||
None
|
None
|
||||||
%span.bold.has-tooltip{ "v-if" => "issue.milestone" }
|
%span.bold.has-tooltip{ "v-if" => "issue.milestone" }
|
||||||
{{ issue.milestone.title }}
|
{{ issue.milestone.title }}
|
||||||
- if can?(current_user, :admin_issue, @project)
|
- if can_admin_issue?
|
||||||
.selectbox
|
.selectbox
|
||||||
%input{ type: "hidden",
|
%input{ type: "hidden",
|
||||||
":value" => "issue.milestone.id",
|
":value" => "issue.milestone.id",
|
||||||
name: "issue[milestone_id]",
|
name: "issue[milestone_id]",
|
||||||
"v-if" => "issue.milestone" }
|
"v-if" => "issue.milestone" }
|
||||||
.dropdown
|
.dropdown
|
||||||
%button.dropdown-menu-toggle.js-milestone-select.js-issue-board-sidebar{ type: "button", data: { toggle: "dropdown", show_no: "true", field_name: "issue[milestone_id]", project_id: @project.id, milestones: project_milestones_path(@project, :json), ability_name: "issue", use_id: "true", default_no: "true" },
|
%button.dropdown-menu-toggle.js-milestone-select.js-issue-board-sidebar{ type: "button", data: { toggle: "dropdown", show_no: "true", field_name: "issue[milestone_id]", milestones: milestones_filter_path(format: :json), ability_name: "issue", use_id: "true", default_no: "true" },
|
||||||
":data-selected" => "milestoneTitle",
|
":data-selected" => "milestoneTitle",
|
||||||
":data-issuable-id" => "issue.id",
|
":data-issuable-id" => "issue.iid",
|
||||||
":data-issue-update" => "'#{project_issues_path(@project)}/' + issue.id + '.json'" }
|
":data-issue-update" => "'#{build_issue_link_base}/' + issue.iid + '.json'" }
|
||||||
Milestone
|
Milestone
|
||||||
= icon("chevron-down")
|
= icon("chevron-down")
|
||||||
.dropdown-menu.dropdown-select.dropdown-menu-selectable
|
.dropdown-menu.dropdown-select.dropdown-menu-selectable
|
|
@ -1,5 +1,5 @@
|
||||||
- if current_user
|
- if current_user
|
||||||
.block.light.subscription{ ":data-url" => "'#{project_issues_path(@project)}/' + issue.id + '/toggle_subscription'" }
|
.block.light.subscription{ ":data-url" => "'#{build_issue_link_base}/' + issue.iid + '/toggle_subscription'" }
|
||||||
%span.issuable-header-text.hide-collapsed.pull-left
|
%span.issuable-header-text.hide-collapsed.pull-left
|
||||||
Notifications
|
Notifications
|
||||||
%button.btn.btn-default.pull-right.js-subscribe-button.issuable-subscribe-button.hide-collapsed{ type: "button" }
|
%button.btn.btn-default.pull-right.js-subscribe-button.issuable-subscribe-button.hide-collapsed{ type: "button" }
|
|
@ -0,0 +1 @@
|
||||||
|
= render "show"
|
|
@ -0,0 +1 @@
|
||||||
|
= render "show"
|
|
@ -8,20 +8,19 @@
|
||||||
- if show_boards_content
|
- if show_boards_content
|
||||||
.issue-board-dropdown-content
|
.issue-board-dropdown-content
|
||||||
%p
|
%p
|
||||||
Create lists from the labels you use in your project. Issues with that
|
Create lists from labels. Issues with that label appear in that list.
|
||||||
label will automatically be added to the list.
|
|
||||||
= dropdown_filter(filter_placeholder)
|
= dropdown_filter(filter_placeholder)
|
||||||
= dropdown_content
|
= dropdown_content
|
||||||
- if @project && show_footer
|
- if current_board_parent && show_footer
|
||||||
= dropdown_footer do
|
= dropdown_footer do
|
||||||
%ul.dropdown-footer-list
|
%ul.dropdown-footer-list
|
||||||
- if can?(current_user, :admin_label, @project)
|
- if can?(current_user, :admin_label, current_board_parent)
|
||||||
%li
|
%li
|
||||||
%a.dropdown-toggle-page{ href: "#" }
|
%a.dropdown-toggle-page{ href: "#" }
|
||||||
Create new label
|
Create new label
|
||||||
%li
|
%li
|
||||||
= link_to project_labels_path(@project), :"data-is-link" => true do
|
= link_to labels_path, :"data-is-link" => true do
|
||||||
- if show_create && @project && can?(current_user, :admin_label, @project)
|
- if show_create && can?(current_user, :admin_label, current_board_parent)
|
||||||
Manage labels
|
Manage labels
|
||||||
- else
|
- else
|
||||||
View labels
|
View labels
|
||||||
|
|
|
@ -104,13 +104,13 @@
|
||||||
= icon('times')
|
= icon('times')
|
||||||
.filter-dropdown-container
|
.filter-dropdown-container
|
||||||
- if type == :boards
|
- if type == :boards
|
||||||
- if can?(current_user, :admin_list, @project)
|
- if can?(current_user, :admin_list, board.parent)
|
||||||
.dropdown.prepend-left-10#js-add-list
|
.dropdown.prepend-left-10#js-add-list
|
||||||
%button.btn.btn-create.btn-inverted.js-new-board-list{ type: "button", data: { toggle: "dropdown", labels: labels_filter_path, namespace_path: @project.try(:namespace).try(:full_path), project_path: @project.try(:path) } }
|
%button.btn.btn-create.btn-inverted.js-new-board-list{ type: "button", data: board_list_data }
|
||||||
Add list
|
Add list
|
||||||
.dropdown-menu.dropdown-menu-paging.dropdown-menu-align-right.dropdown-menu-issues-board-new.dropdown-menu-selectable
|
.dropdown-menu.dropdown-menu-paging.dropdown-menu-align-right.dropdown-menu-issues-board-new.dropdown-menu-selectable
|
||||||
= render partial: "shared/issuable/label_page_default", locals: { show_footer: true, show_create: true, show_boards_content: true, title: "Add list" }
|
= render partial: "shared/issuable/label_page_default", locals: { show_footer: true, show_create: true, show_boards_content: true, title: "Add list" }
|
||||||
- if can?(current_user, :admin_label, @project)
|
- if can?(current_user, :admin_label, board.parent)
|
||||||
= render partial: "shared/issuable/label_page_create"
|
= render partial: "shared/issuable/label_page_create"
|
||||||
= dropdown_loading
|
= dropdown_loading
|
||||||
#js-add-issues-btn.prepend-left-10
|
#js-add-issues-btn.prepend-left-10
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Add div id to the readme in the project overview
|
||||||
|
merge_request: 13735
|
||||||
|
author: Riccardo Padovani @rpadovani
|
||||||
|
type: added
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Add option in preferences to change navigation theme color
|
||||||
|
merge_request:
|
||||||
|
author:
|
||||||
|
type: added
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Finish migration to the new events setup
|
||||||
|
merge_request:
|
||||||
|
author:
|
||||||
|
type: changed
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Add documentation for PlantUML in reStructuredText
|
||||||
|
merge_request: 13900
|
||||||
|
author: Markus Koller
|
||||||
|
type: other
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Fix stray OR in New Project page
|
||||||
|
merge_request: 14096
|
||||||
|
author: Robin Bobbitt
|
||||||
|
type: fixed
|
|
@ -76,6 +76,13 @@ production: &base
|
||||||
|
|
||||||
# default_can_create_group: false # default: true
|
# default_can_create_group: false # default: true
|
||||||
# username_changing_enabled: false # default: true - User can change her username/namespace
|
# username_changing_enabled: false # default: true - User can change her username/namespace
|
||||||
|
## Default theme ID
|
||||||
|
## 1 - Indigo
|
||||||
|
## 2 - Dark
|
||||||
|
## 3 - Light
|
||||||
|
## 4 - Blue
|
||||||
|
## 5 - Green
|
||||||
|
# default_theme: 1 # default: 1
|
||||||
|
|
||||||
## Automatic issue closing
|
## Automatic issue closing
|
||||||
# If a commit message matches this regular expression, all issues referenced from the matched text will be closed.
|
# If a commit message matches this regular expression, all issues referenced from the matched text will be closed.
|
||||||
|
|
|
@ -232,6 +232,7 @@ Settings['gitlab'] ||= Settingslogic.new({})
|
||||||
Settings.gitlab['default_projects_limit'] ||= 100000
|
Settings.gitlab['default_projects_limit'] ||= 100000
|
||||||
Settings.gitlab['default_branch_protection'] ||= 2
|
Settings.gitlab['default_branch_protection'] ||= 2
|
||||||
Settings.gitlab['default_can_create_group'] = true if Settings.gitlab['default_can_create_group'].nil?
|
Settings.gitlab['default_can_create_group'] = true if Settings.gitlab['default_can_create_group'].nil?
|
||||||
|
Settings.gitlab['default_theme'] = Gitlab::Themes::APPLICATION_DEFAULT if Settings.gitlab['default_theme'].nil?
|
||||||
Settings.gitlab['host'] ||= ENV['GITLAB_HOST'] || 'localhost'
|
Settings.gitlab['host'] ||= ENV['GITLAB_HOST'] || 'localhost'
|
||||||
Settings.gitlab['ssh_host'] ||= Settings.gitlab.host
|
Settings.gitlab['ssh_host'] ||= Settings.gitlab.host
|
||||||
Settings.gitlab['https'] = false if Settings.gitlab['https'].nil?
|
Settings.gitlab['https'] = false if Settings.gitlab['https'].nil?
|
||||||
|
|
|
@ -74,6 +74,19 @@ Rails.application.routes.draw do
|
||||||
# Notification settings
|
# Notification settings
|
||||||
resources :notification_settings, only: [:create, :update]
|
resources :notification_settings, only: [:create, :update]
|
||||||
|
|
||||||
|
# Boards resources shared between group and projects
|
||||||
|
resources :boards do
|
||||||
|
resources :lists, module: :boards, only: [:index, :create, :update, :destroy] do
|
||||||
|
collection do
|
||||||
|
post :generate
|
||||||
|
end
|
||||||
|
|
||||||
|
resources :issues, only: [:index, :create, :update]
|
||||||
|
end
|
||||||
|
|
||||||
|
resources :issues, module: :boards, only: [:index, :update]
|
||||||
|
end
|
||||||
|
|
||||||
draw :import
|
draw :import
|
||||||
draw :uploads
|
draw :uploads
|
||||||
draw :explore
|
draw :explore
|
||||||
|
|
|
@ -343,19 +343,7 @@ constraints(ProjectUrlConstrainer.new) do
|
||||||
|
|
||||||
get 'noteable/:target_type/:target_id/notes' => 'notes#index', as: 'noteable_notes'
|
get 'noteable/:target_type/:target_id/notes' => 'notes#index', as: 'noteable_notes'
|
||||||
|
|
||||||
resources :boards, only: [:index, :show] do
|
resources :boards, only: [:index, :show, :create, :update, :destroy]
|
||||||
scope module: :boards do
|
|
||||||
resources :issues, only: [:index, :update]
|
|
||||||
|
|
||||||
resources :lists, only: [:index, :create, :update, :destroy] do
|
|
||||||
collection do
|
|
||||||
post :generate
|
|
||||||
end
|
|
||||||
|
|
||||||
resources :issues, only: [:index, :create]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
resources :todos, only: [:create]
|
resources :todos, only: [:create]
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
|
||||||
|
# for more information on how to write migrations for GitLab.
|
||||||
|
|
||||||
|
class AddThemeIdToUsers < ActiveRecord::Migration
|
||||||
|
DOWNTIME = false
|
||||||
|
|
||||||
|
def change
|
||||||
|
add_column :users, :theme_id, :integer, limit: 2
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,18 @@
|
||||||
|
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
|
||||||
|
# for more information on how to write migrations for GitLab.
|
||||||
|
|
||||||
|
class StealRemainingEventMigrationJobs < ActiveRecord::Migration
|
||||||
|
include Gitlab::Database::MigrationHelpers
|
||||||
|
|
||||||
|
# Set this constant to true if this migration requires downtime.
|
||||||
|
DOWNTIME = false
|
||||||
|
|
||||||
|
disable_ddl_transaction!
|
||||||
|
|
||||||
|
def up
|
||||||
|
Gitlab::BackgroundMigration.steal('MigrateEventsToPushEventPayloads')
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,23 @@
|
||||||
|
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
|
||||||
|
# for more information on how to write migrations for GitLab.
|
||||||
|
|
||||||
|
class SwapEventMigrationTables < ActiveRecord::Migration
|
||||||
|
include Gitlab::Database::MigrationHelpers
|
||||||
|
|
||||||
|
# Set this constant to true if this migration requires downtime.
|
||||||
|
DOWNTIME = false
|
||||||
|
|
||||||
|
def up
|
||||||
|
rename_tables
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
rename_tables
|
||||||
|
end
|
||||||
|
|
||||||
|
def rename_tables
|
||||||
|
rename_table :events, :events_old
|
||||||
|
rename_table :events_for_migration, :events
|
||||||
|
rename_table :events_old, :events_for_migration
|
||||||
|
end
|
||||||
|
end
|
|
@ -7,6 +7,5 @@ class LimitsToMysql < ActiveRecord::Migration
|
||||||
change_column :merge_request_diffs, :st_diffs, :text, limit: 2147483647
|
change_column :merge_request_diffs, :st_diffs, :text, limit: 2147483647
|
||||||
change_column :snippets, :content, :text, limit: 2147483647
|
change_column :snippets, :content, :text, limit: 2147483647
|
||||||
change_column :notes, :st_diff, :text, limit: 2147483647
|
change_column :notes, :st_diff, :text, limit: 2147483647
|
||||||
change_column :events, :data, :text, limit: 2147483647
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue