Merge branch 'master' of gitlab.com:gitlab-org/gitlab-ce into orderable-issues
This commit is contained in:
commit
5bf2ab73ba
|
@ -300,7 +300,7 @@ bundler:audit:
|
|||
- master@gitlab/gitlabhq
|
||||
- master@gitlab/gitlab-ee
|
||||
script:
|
||||
- "bundle exec bundle-audit check --update --ignore OSVDB-115941 CVE-2016-6316 CVE-2016-6317"
|
||||
- "bundle exec bundle-audit check --update"
|
||||
|
||||
migration paths:
|
||||
stage: test
|
||||
|
|
|
@ -2,6 +2,10 @@
|
|||
documentation](doc/development/changelog.md) for instructions on adding your own
|
||||
entry.
|
||||
|
||||
## 8.17.2 (2017-03-01)
|
||||
|
||||
- Expire all webpack assets after 8.17.1 included a badly compiled asset. !9602
|
||||
|
||||
## 8.17.1 (2017-02-28)
|
||||
|
||||
- Replace setInterval with setTimeout to prevent highly frequent requests. !9271 (Takuya Noguchi)
|
||||
|
|
|
@ -1,32 +1,48 @@
|
|||
## Contributor license agreement
|
||||
|
||||
By submitting code as an individual you agree to the
|
||||
[individual contributor license agreement](doc/legal/individual_contributor_license_agreement.md).
|
||||
By submitting code as an entity you agree to the
|
||||
[corporate contributor license agreement](doc/legal/corporate_contributor_license_agreement.md).
|
||||
|
||||
_This notice should stay as the first item in the CONTRIBUTING.MD file._
|
||||
|
||||
---
|
||||
|
||||
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
|
||||
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
|
||||
**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*
|
||||
|
||||
- [Contributor license agreement](#contributor-license-agreement)
|
||||
- [Contribute to GitLab](#contribute-to-gitlab)
|
||||
- [Contributor license agreement](#contributor-license-agreement)
|
||||
- [Security vulnerability disclosure](#security-vulnerability-disclosure)
|
||||
- [Closing policy for issues and merge requests](#closing-policy-for-issues-and-merge-requests)
|
||||
- [Helping others](#helping-others)
|
||||
- [I want to contribute!](#i-want-to-contribute)
|
||||
- [Implement design & UI elements](#implement-design-ui-elements)
|
||||
- [Issue tracker](#issue-tracker)
|
||||
- [Feature proposals](#feature-proposals)
|
||||
- [Issue tracker guidelines](#issue-tracker-guidelines)
|
||||
- [Issue weight](#issue-weight)
|
||||
- [Regression issues](#regression-issues)
|
||||
- [Technical debt](#technical-debt)
|
||||
- [Stewardship](#stewardship)
|
||||
- [Merge requests](#merge-requests)
|
||||
- [Merge request guidelines](#merge-request-guidelines)
|
||||
- [Contribution acceptance criteria](#contribution-acceptance-criteria)
|
||||
- [Changes for Stable Releases](#changes-for-stable-releases)
|
||||
- [Definition of done](#definition-of-done)
|
||||
- [Style guides](#style-guides)
|
||||
- [Code of conduct](#code-of-conduct)
|
||||
- [Security vulnerability disclosure](#security-vulnerability-disclosure)
|
||||
- [Closing policy for issues and merge requests](#closing-policy-for-issues-and-merge-requests)
|
||||
- [Helping others](#helping-others)
|
||||
- [I want to contribute!](#i-want-to-contribute)
|
||||
- [Implement design & UI elements](#implement-design-ui-elements)
|
||||
- [Release retrospective and kickoff](#release-retrospective-and-kickoff)
|
||||
- [Retrospective](#retrospective)
|
||||
- [Kickoff](#kickoff)
|
||||
- [Issue tracker](#issue-tracker)
|
||||
- [Feature proposals](#feature-proposals)
|
||||
- [Issue tracker guidelines](#issue-tracker-guidelines)
|
||||
- [Issue weight](#issue-weight)
|
||||
- [Regression issues](#regression-issues)
|
||||
- [Technical debt](#technical-debt)
|
||||
- [Stewardship](#stewardship)
|
||||
- [Merge requests](#merge-requests)
|
||||
- [Merge request guidelines](#merge-request-guidelines)
|
||||
- [Contribution acceptance criteria](#contribution-acceptance-criteria)
|
||||
- [Changes for Stable Releases](#changes-for-stable-releases)
|
||||
- [Definition of done](#definition-of-done)
|
||||
- [Style guides](#style-guides)
|
||||
- [Code of conduct](#code-of-conduct)
|
||||
|
||||
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
||||
|
||||
# Contribute to GitLab
|
||||
---
|
||||
|
||||
## Contribute to GitLab
|
||||
|
||||
Thank you for your interest in contributing to GitLab. This guide details how
|
||||
to contribute to GitLab in a way that is efficient for everyone.
|
||||
|
@ -41,13 +57,6 @@ operates please see [the GitLab contributing process](PROCESS.md).
|
|||
|
||||
- [GitLab Inc engineers should refer to the engineering workflow document](https://about.gitlab.com/handbook/engineering/workflow/)
|
||||
|
||||
## Contributor license agreement
|
||||
|
||||
By submitting code as an individual you agree to the
|
||||
[individual contributor license agreement](doc/legal/individual_contributor_license_agreement.md).
|
||||
By submitting code as an entity you agree to the
|
||||
[corporate contributor license agreement](doc/legal/corporate_contributor_license_agreement.md).
|
||||
|
||||
## Security vulnerability disclosure
|
||||
|
||||
Please report suspected security vulnerabilities in private to
|
||||
|
|
6
Gemfile
6
Gemfile
|
@ -236,7 +236,6 @@ gem 'gemojione', '~> 3.0'
|
|||
gem 'gon', '~> 6.1.0'
|
||||
gem 'jquery-atwho-rails', '~> 1.3.2'
|
||||
gem 'jquery-rails', '~> 4.1.0'
|
||||
gem 'jquery-ui-rails', '~> 5.0.0'
|
||||
gem 'request_store', '~> 1.3'
|
||||
gem 'select2-rails', '~> 3.5.9'
|
||||
gem 'virtus', '~> 1.0.1'
|
||||
|
@ -329,8 +328,6 @@ group :test do
|
|||
gem 'timecop', '~> 0.8.0'
|
||||
end
|
||||
|
||||
gem 'newrelic_rpm', '~> 3.16'
|
||||
|
||||
gem 'octokit', '~> 4.6.2'
|
||||
|
||||
gem 'mail_room', '~> 0.9.1'
|
||||
|
@ -352,3 +349,6 @@ gem 'health_check', '~> 2.2.0'
|
|||
# System information
|
||||
gem 'vmstat', '~> 2.3.0'
|
||||
gem 'sys-filesystem', '~> 1.1.6'
|
||||
|
||||
# Gitaly GRPC client
|
||||
gem 'gitaly', '~> 0.2.1'
|
||||
|
|
17
Gemfile.lock
17
Gemfile.lock
|
@ -245,6 +245,9 @@ GEM
|
|||
json
|
||||
get_process_mem (0.2.0)
|
||||
gherkin-ruby (0.3.2)
|
||||
gitaly (0.2.1)
|
||||
google-protobuf (~> 3.1)
|
||||
grpc (~> 1.0)
|
||||
github-linguist (4.7.6)
|
||||
charlock_holmes (~> 0.7.3)
|
||||
escape_utils (~> 1.1.0)
|
||||
|
@ -296,6 +299,7 @@ GEM
|
|||
multi_json (~> 1.10)
|
||||
retriable (~> 1.4)
|
||||
signet (~> 0.6)
|
||||
google-protobuf (3.2.0)
|
||||
googleauth (0.5.1)
|
||||
faraday (~> 0.9)
|
||||
jwt (~> 1.4)
|
||||
|
@ -317,6 +321,9 @@ GEM
|
|||
grape-entity (0.6.0)
|
||||
activesupport
|
||||
multi_json (>= 1.3.2)
|
||||
grpc (1.1.2)
|
||||
google-protobuf (~> 3.1)
|
||||
googleauth (~> 0.5.1)
|
||||
haml (4.0.7)
|
||||
tilt
|
||||
haml_lint (0.21.0)
|
||||
|
@ -367,8 +374,6 @@ GEM
|
|||
rails-dom-testing (>= 1, < 3)
|
||||
railties (>= 4.2.0)
|
||||
thor (>= 0.14, < 2.0)
|
||||
jquery-ui-rails (5.0.5)
|
||||
railties (>= 3.2.16)
|
||||
json (1.8.6)
|
||||
json-schema (2.6.2)
|
||||
addressable (~> 2.3.8)
|
||||
|
@ -427,7 +432,6 @@ GEM
|
|||
net-ldap (0.12.1)
|
||||
net-ssh (3.0.1)
|
||||
netrc (0.11.0)
|
||||
newrelic_rpm (3.16.0.318)
|
||||
nokogiri (1.6.8.1)
|
||||
mini_portile2 (~> 2.1.0)
|
||||
numerizer (0.1.1)
|
||||
|
@ -660,7 +664,7 @@ GEM
|
|||
sexp_processor (~> 4.1)
|
||||
rubyntlm (0.5.2)
|
||||
rubypants (0.2.0)
|
||||
rubyzip (1.2.0)
|
||||
rubyzip (1.2.1)
|
||||
rufus-scheduler (3.1.10)
|
||||
rugged (0.24.0)
|
||||
safe_yaml (1.0.4)
|
||||
|
@ -878,6 +882,7 @@ DEPENDENCIES
|
|||
fuubar (~> 2.0.0)
|
||||
gemnasium-gitlab-service (~> 0.2)
|
||||
gemojione (~> 3.0)
|
||||
gitaly (~> 0.2.1)
|
||||
github-linguist (~> 4.7.0)
|
||||
gitlab-flowdock-git-hook (~> 1.0.1)
|
||||
gitlab-markup (~> 1.5.1)
|
||||
|
@ -899,7 +904,6 @@ DEPENDENCIES
|
|||
jira-ruby (~> 1.1.2)
|
||||
jquery-atwho-rails (~> 1.3.2)
|
||||
jquery-rails (~> 4.1.0)
|
||||
jquery-ui-rails (~> 5.0.0)
|
||||
json-schema (~> 2.6.2)
|
||||
jwt (~> 1.5.6)
|
||||
kaminari (~> 0.17.0)
|
||||
|
@ -915,7 +919,6 @@ DEPENDENCIES
|
|||
mousetrap-rails (~> 1.4.6)
|
||||
mysql2 (~> 0.3.16)
|
||||
net-ssh (~> 3.0.1)
|
||||
newrelic_rpm (~> 3.16)
|
||||
nokogiri (~> 1.6.7, >= 1.6.7.2)
|
||||
oauth2 (~> 1.2.0)
|
||||
octokit (~> 4.6.2)
|
||||
|
@ -1011,4 +1014,4 @@ DEPENDENCIES
|
|||
wikicloth (= 0.8.1)
|
||||
|
||||
BUNDLED WITH
|
||||
1.14.4
|
||||
1.14.5
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
window.$ = window.jQuery = require('jquery');
|
||||
require('jquery-ujs');
|
||||
require('vendor/jquery.endless-scroll');
|
||||
require('vendor/jquery.highlight');
|
||||
require('vendor/jquery.waitforimages');
|
||||
require('vendor/jquery.caret');
|
||||
require('vendor/jquery.atwho');
|
||||
|
@ -18,15 +17,11 @@ window.Cookies = require('js-cookie');
|
|||
require('./autosave');
|
||||
require('bootstrap/js/affix');
|
||||
require('bootstrap/js/alert');
|
||||
require('bootstrap/js/button');
|
||||
require('bootstrap/js/collapse');
|
||||
require('bootstrap/js/dropdown');
|
||||
require('bootstrap/js/modal');
|
||||
require('bootstrap/js/scrollspy');
|
||||
require('bootstrap/js/tab');
|
||||
require('bootstrap/js/transition');
|
||||
require('bootstrap/js/tooltip');
|
||||
require('bootstrap/js/popover');
|
||||
require('select2/select2.js');
|
||||
window.Pikaday = require('pikaday');
|
||||
window._ = require('underscore');
|
||||
|
@ -236,6 +231,10 @@ require('es6-promise').polyfill();
|
|||
var bootstrapBreakpoint = bp.getBreakpointSize();
|
||||
var fitSidebarForSize;
|
||||
|
||||
$(document).on('scroll', function() {
|
||||
$('.has-tooltip').tooltip('hide');
|
||||
});
|
||||
|
||||
// Set the default path for all cookies to GitLab's root directory
|
||||
Cookies.defaults.path = gon.relative_url_root || '/';
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ require('./issue_card_inner');
|
|||
|
||||
const Store = gl.issueBoards.BoardsStore;
|
||||
|
||||
module.exports = {
|
||||
export default {
|
||||
name: 'BoardsIssueCard',
|
||||
template: `
|
||||
<li class="card"
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
/* global Vue */
|
||||
/* global Sortable */
|
||||
|
||||
const boardCard = require('./board_card');
|
||||
require('./board_new_issue');
|
||||
import boardNewIssue from './board_new_issue';
|
||||
import boardCard from './board_card';
|
||||
|
||||
(() => {
|
||||
const Store = gl.issueBoards.BoardsStore;
|
||||
|
@ -15,7 +15,7 @@ require('./board_new_issue');
|
|||
template: '#js-board-list-template',
|
||||
components: {
|
||||
boardCard,
|
||||
'board-new-issue': gl.issueBoards.BoardNewIssue
|
||||
boardNewIssue,
|
||||
},
|
||||
props: {
|
||||
disabled: Boolean,
|
||||
|
@ -76,6 +76,12 @@ require('./board_new_issue');
|
|||
});
|
||||
}
|
||||
},
|
||||
toggleForm() {
|
||||
this.showIssueForm = !this.showIssueForm;
|
||||
},
|
||||
},
|
||||
created() {
|
||||
gl.IssueBoardsApp.$on(`hide-issue-form-${this.list.id}`, this.toggleForm);
|
||||
},
|
||||
mounted () {
|
||||
const options = gl.issueBoards.getBoardSortableDefaultOptions({
|
||||
|
@ -113,6 +119,9 @@ require('./board_new_issue');
|
|||
this.loadNextPage();
|
||||
}
|
||||
};
|
||||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
gl.IssueBoardsApp.$off(`hide-issue-form-${this.list.id}`, this.toggleForm);
|
||||
},
|
||||
});
|
||||
})();
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
/* global ListIssue */
|
||||
const Store = gl.issueBoards.BoardsStore;
|
||||
|
||||
export default {
|
||||
name: 'BoardNewIssue',
|
||||
props: {
|
||||
list: Object,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
title: '',
|
||||
error: false,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
submit(e) {
|
||||
e.preventDefault();
|
||||
if (this.title.trim() === '') return;
|
||||
|
||||
this.error = false;
|
||||
|
||||
const labels = this.list.label ? [this.list.label] : [];
|
||||
const issue = new ListIssue({
|
||||
title: this.title,
|
||||
labels,
|
||||
subscribed: true,
|
||||
});
|
||||
|
||||
this.list.newIssue(issue)
|
||||
.then(() => {
|
||||
// Need this because our jQuery very kindly disables buttons on ALL form submissions
|
||||
$(this.$refs.submitButton).enable();
|
||||
|
||||
Store.detail.issue = issue;
|
||||
Store.detail.list = this.list;
|
||||
})
|
||||
.catch(() => {
|
||||
// Need this because our jQuery very kindly disables buttons on ALL form submissions
|
||||
$(this.$refs.submitButton).enable();
|
||||
|
||||
// Remove the issue
|
||||
this.list.removeIssue(issue);
|
||||
|
||||
// Show error message
|
||||
this.error = true;
|
||||
});
|
||||
|
||||
this.cancel();
|
||||
},
|
||||
cancel() {
|
||||
this.title = '';
|
||||
gl.IssueBoardsApp.$emit(`hide-issue-form-${this.list.id}`);
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.$refs.input.focus();
|
||||
},
|
||||
template: `
|
||||
<div class="card board-new-issue-form">
|
||||
<form @submit="submit($event)">
|
||||
<div class="flash-container"
|
||||
v-if="error">
|
||||
<div class="flash-alert">
|
||||
An error occured. Please try again.
|
||||
</div>
|
||||
</div>
|
||||
<label class="label-light"
|
||||
:for="list.id + '-title'">
|
||||
Title
|
||||
</label>
|
||||
<input class="form-control"
|
||||
type="text"
|
||||
v-model="title"
|
||||
ref="input"
|
||||
:id="list.id + '-title'" />
|
||||
<div class="clearfix prepend-top-10">
|
||||
<button class="btn btn-success pull-left"
|
||||
type="submit"
|
||||
:disabled="title === ''"
|
||||
ref="submit-button">
|
||||
Submit issue
|
||||
</button>
|
||||
<button class="btn btn-default pull-right"
|
||||
type="button"
|
||||
@click="cancel">
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
`,
|
||||
};
|
|
@ -1,64 +0,0 @@
|
|||
/* eslint-disable comma-dangle, no-unused-vars */
|
||||
/* global Vue */
|
||||
/* global ListIssue */
|
||||
|
||||
(() => {
|
||||
const Store = gl.issueBoards.BoardsStore;
|
||||
|
||||
window.gl = window.gl || {};
|
||||
|
||||
gl.issueBoards.BoardNewIssue = Vue.extend({
|
||||
props: {
|
||||
list: Object,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
title: '',
|
||||
error: false
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
submit(e) {
|
||||
e.preventDefault();
|
||||
if (this.title.trim() === '') return;
|
||||
|
||||
this.error = false;
|
||||
|
||||
const labels = this.list.label ? [this.list.label] : [];
|
||||
const issue = new ListIssue({
|
||||
title: this.title,
|
||||
labels,
|
||||
subscribed: true
|
||||
});
|
||||
|
||||
this.list.newIssue(issue)
|
||||
.then((data) => {
|
||||
// Need this because our jQuery very kindly disables buttons on ALL form submissions
|
||||
$(this.$refs.submitButton).enable();
|
||||
|
||||
Store.detail.issue = issue;
|
||||
Store.detail.list = this.list;
|
||||
})
|
||||
.catch(() => {
|
||||
// Need this because our jQuery very kindly disables buttons on ALL form submissions
|
||||
$(this.$refs.submitButton).enable();
|
||||
|
||||
// Remove the issue
|
||||
this.list.removeIssue(issue);
|
||||
|
||||
// Show error message
|
||||
this.error = true;
|
||||
});
|
||||
|
||||
this.cancel();
|
||||
},
|
||||
cancel() {
|
||||
this.title = '';
|
||||
this.$parent.showIssueForm = false;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$refs.input.focus();
|
||||
},
|
||||
});
|
||||
})();
|
|
@ -39,15 +39,10 @@ const PipelineStore = require('./pipelines_store');
|
|||
*/
|
||||
data() {
|
||||
const pipelinesTableData = document.querySelector('#commit-pipeline-table-view').dataset;
|
||||
const svgsData = document.querySelector('.pipeline-svgs').dataset;
|
||||
const store = new PipelineStore();
|
||||
|
||||
// Transform svgs DOMStringMap to a plain Object.
|
||||
const svgsObject = gl.utils.DOMStringMapToObject(svgsData);
|
||||
|
||||
return {
|
||||
endpoint: pipelinesTableData.endpoint,
|
||||
svgs: svgsObject,
|
||||
store,
|
||||
state: store.state,
|
||||
isLoading: false,
|
||||
|
@ -69,7 +64,9 @@ const PipelineStore = require('./pipelines_store');
|
|||
return pipelinesService.all()
|
||||
.then(response => response.json())
|
||||
.then((json) => {
|
||||
this.store.storePipelines(json);
|
||||
// depending of the endpoint the response can either bring a `pipelines` key or not.
|
||||
const pipelines = json.pipelines || json;
|
||||
this.store.storePipelines(pipelines);
|
||||
this.isLoading = false;
|
||||
})
|
||||
.catch(() => {
|
||||
|
@ -99,10 +96,7 @@ const PipelineStore = require('./pipelines_store');
|
|||
|
||||
<div class="table-holder pipelines"
|
||||
v-if="!isLoading && state.pipelines.length > 0">
|
||||
<pipelines-table-component
|
||||
:pipelines="state.pipelines"
|
||||
:svgs="svgs">
|
||||
</pipelines-table-component>
|
||||
<pipelines-table-component :pipelines="state.pipelines"/>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
|
|
|
@ -25,6 +25,9 @@ require('./lib/utils/common_utils');
|
|||
},
|
||||
},
|
||||
ReferenceFilter: {
|
||||
'.tooltip'(el, text) {
|
||||
return '';
|
||||
},
|
||||
'a.gfm:not([data-link=true])'(el, text) {
|
||||
return el.dataset.original || text;
|
||||
},
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/* eslint-disable no-param-reassign */
|
||||
/* global Vue */
|
||||
import Vue from 'vue';
|
||||
import iconCommit from '../svg/icon_commit.svg';
|
||||
|
||||
((global) => {
|
||||
global.cycleAnalytics = global.cycleAnalytics || {};
|
||||
|
@ -9,6 +10,11 @@
|
|||
items: Array,
|
||||
stage: Object,
|
||||
},
|
||||
|
||||
data() {
|
||||
return { iconCommit };
|
||||
},
|
||||
|
||||
template: `
|
||||
<div>
|
||||
<div class="events-description">
|
||||
|
@ -31,7 +37,7 @@
|
|||
</h5>
|
||||
<span>
|
||||
First
|
||||
<span class="commit-icon">${global.cycleAnalytics.svgs.iconCommit}</span>
|
||||
<span class="commit-icon">${iconCommit}</span>
|
||||
<a :href="commit.commitUrl" class="commit-hash-link monospace">{{ commit.shortSha }}</a>
|
||||
pushed by
|
||||
<a :href="commit.author.webUrl" class="commit-author-link">
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/* eslint-disable no-param-reassign */
|
||||
/* global Vue */
|
||||
import Vue from 'vue';
|
||||
import iconBranch from '../svg/icon_branch.svg';
|
||||
|
||||
((global) => {
|
||||
global.cycleAnalytics = global.cycleAnalytics || {};
|
||||
|
@ -9,6 +10,9 @@
|
|||
items: Array,
|
||||
stage: Object,
|
||||
},
|
||||
data() {
|
||||
return { iconBranch };
|
||||
},
|
||||
template: `
|
||||
<div>
|
||||
<div class="events-description">
|
||||
|
@ -22,7 +26,7 @@
|
|||
<a :href="build.url" class="pipeline-id">#{{ build.id }}</a>
|
||||
<i class="fa fa-code-fork"></i>
|
||||
<a :href="build.branch.url" class="branch-name monospace">{{ build.branch.name }}</a>
|
||||
<span class="icon-branch">${global.cycleAnalytics.svgs.iconBranch}</span>
|
||||
<span class="icon-branch">${iconBranch}</span>
|
||||
<a :href="build.commitUrl" class="short-sha monospace">{{ build.shortSha }}</a>
|
||||
</h5>
|
||||
<span>
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
/* eslint-disable no-param-reassign */
|
||||
/* global Vue */
|
||||
import Vue from 'vue';
|
||||
import iconBuildStatus from '../svg/icon_build_status.svg';
|
||||
import iconBranch from '../svg/icon_branch.svg';
|
||||
|
||||
((global) => {
|
||||
global.cycleAnalytics = global.cycleAnalytics || {};
|
||||
|
@ -9,6 +11,9 @@
|
|||
items: Array,
|
||||
stage: Object,
|
||||
},
|
||||
data() {
|
||||
return { iconBuildStatus, iconBranch };
|
||||
},
|
||||
template: `
|
||||
<div>
|
||||
<div class="events-description">
|
||||
|
@ -18,13 +23,13 @@
|
|||
<li v-for="build in items" class="stage-event-item item-build-component">
|
||||
<div class="item-details">
|
||||
<h5 class="item-title">
|
||||
<span class="icon-build-status">${global.cycleAnalytics.svgs.iconBuildStatus}</span>
|
||||
<span class="icon-build-status">${iconBuildStatus}</span>
|
||||
<a :href="build.url" class="item-build-name">{{ build.name }}</a>
|
||||
·
|
||||
<a :href="build.url" class="pipeline-id">#{{ build.id }}</a>
|
||||
<i class="fa fa-code-fork"></i>
|
||||
<a :href="build.branch.url" class="branch-name monospace">{{ build.branch.name }}</a>
|
||||
<span class="icon-branch">${global.cycleAnalytics.svgs.iconBranch}</span>
|
||||
<span class="icon-branch">${iconBranch}</span>
|
||||
<a :href="build.commitUrl" class="short-sha monospace">{{ build.shortSha }}</a>
|
||||
</h5>
|
||||
<span>
|
||||
|
|
|
@ -4,9 +4,6 @@
|
|||
|
||||
window.Vue = require('vue');
|
||||
window.Cookies = require('js-cookie');
|
||||
require('./svg/icon_branch');
|
||||
require('./svg/icon_build_status');
|
||||
require('./svg/icon_commit');
|
||||
require('./components/stage_code_component');
|
||||
require('./components/stage_issue_component');
|
||||
require('./components/stage_plan_component');
|
||||
|
|
|
@ -75,8 +75,11 @@ const DEFAULT_EVENT_OBJECTS = require('./default_event_objects');
|
|||
const eventItem = Object.assign({}, DEFAULT_EVENT_OBJECTS[stage.slug], item);
|
||||
|
||||
eventItem.totalTime = eventItem.total_time;
|
||||
eventItem.author.webUrl = eventItem.author.web_url;
|
||||
eventItem.author.avatarUrl = eventItem.author.avatar_url;
|
||||
|
||||
if (eventItem.author) {
|
||||
eventItem.author.webUrl = eventItem.author.web_url;
|
||||
eventItem.author.avatarUrl = eventItem.author.avatar_url;
|
||||
}
|
||||
|
||||
if (eventItem.created_at) eventItem.createdAt = eventItem.created_at;
|
||||
if (eventItem.short_sha) eventItem.shortSha = eventItem.short_sha;
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
/* eslint-disable no-param-reassign */
|
||||
((global) => {
|
||||
global.cycleAnalytics = global.cycleAnalytics || {};
|
||||
global.cycleAnalytics.svgs = global.cycleAnalytics.svgs || {};
|
||||
|
||||
global.cycleAnalytics.svgs.iconBranch = '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14"><path fill="#8C8C8C" fill-rule="evenodd" d="M9.678 6.722C9.353 5.167 8.053 4 6.5 4S3.647 5.167 3.322 6.722h-2.6c-.397 0-.722.35-.722.778 0 .428.325.778.722.778h2.6C3.647 9.833 4.947 11 6.5 11s2.853-1.167 3.178-2.722h2.6c.397 0 .722-.35.722-.778 0-.428-.325-.778-.722-.778h-2.6zM4.694 7.5c0-1.09.795-1.944 1.806-1.944 1.01 0 1.806.855 1.806 1.944 0 1.09-.795 1.944-1.806 1.944-1.01 0-1.806-.855-1.806-1.944z"/></svg>';
|
||||
})(window.gl || (window.gl = {}));
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14"><path fill="#8C8C8C" fill-rule="evenodd" d="M9.678 6.722C9.353 5.167 8.053 4 6.5 4S3.647 5.167 3.322 6.722h-2.6c-.397 0-.722.35-.722.778 0 .428.325.778.722.778h2.6C3.647 9.833 4.947 11 6.5 11s2.853-1.167 3.178-2.722h2.6c.397 0 .722-.35.722-.778 0-.428-.325-.778-.722-.778h-2.6zM4.694 7.5c0-1.09.795-1.944 1.806-1.944 1.01 0 1.806.855 1.806 1.944 0 1.09-.795 1.944-1.806 1.944-1.01 0-1.806-.855-1.806-1.944z"/></svg>
|
After Width: | Height: | Size: 479 B |
|
@ -1,7 +0,0 @@
|
|||
/* eslint-disable no-param-reassign */
|
||||
((global) => {
|
||||
global.cycleAnalytics = global.cycleAnalytics || {};
|
||||
global.cycleAnalytics.svgs = global.cycleAnalytics.svgs || {};
|
||||
|
||||
global.cycleAnalytics.svgs.iconBuildStatus = '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14"><g fill="#31AF64" fill-rule="evenodd"><path d="M12.5 7c0-3.038-2.462-5.5-5.5-5.5S1.5 3.962 1.5 7s2.462 5.5 5.5 5.5 5.5-2.462 5.5-5.5zM0 7c0-3.866 3.134-7 7-7s7 3.134 7 7-3.134 7-7 7-7-3.134-7-7z"/><path d="M6.28 7.697L5.045 6.464c-.117-.117-.305-.117-.42-.002l-.614.614c-.11.113-.11.303.007.42l1.91 1.91c.19.19.51.197.703.004l.264-.265L9.997 6.04c.108-.107.107-.293-.01-.408l-.612-.614c-.114-.113-.298-.12-.41-.01L6.28 7.7z"/></g></svg>';
|
||||
})(window.gl || (window.gl = {}));
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14"><g fill="#31AF64" fill-rule="evenodd"><path d="M12.5 7c0-3.038-2.462-5.5-5.5-5.5S1.5 3.962 1.5 7s2.462 5.5 5.5 5.5 5.5-2.462 5.5-5.5zM0 7c0-3.866 3.134-7 7-7s7 3.134 7 7-3.134 7-7 7-7-3.134-7-7z"/><path d="M6.28 7.697L5.045 6.464c-.117-.117-.305-.117-.42-.002l-.614.614c-.11.113-.11.303.007.42l1.91 1.91c.19.19.51.197.703.004l.264-.265L9.997 6.04c.108-.107.107-.293-.01-.408l-.612-.614c-.114-.113-.298-.12-.41-.01L6.28 7.7z"/></g></svg>
|
After Width: | Height: | Size: 500 B |
|
@ -1,7 +0,0 @@
|
|||
/* eslint-disable no-param-reassign */
|
||||
((global) => {
|
||||
global.cycleAnalytics = global.cycleAnalytics || {};
|
||||
global.cycleAnalytics.svgs = global.cycleAnalytics.svgs || {};
|
||||
|
||||
global.cycleAnalytics.svgs.iconCommit = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 40"><path fill="#8F8F8F" fill-rule="evenodd" d="M28.777 18c-.91-4.008-4.494-7-8.777-7-4.283 0-7.868 2.992-8.777 7H4.01C2.9 18 2 18.895 2 20c0 1.112.9 2 2.01 2h7.213c.91 4.008 4.494 7 8.777 7 4.283 0 7.868-2.992 8.777-7h7.214C37.1 22 38 21.105 38 20c0-1.112-.9-2-2.01-2h-7.213zM20 25c2.76 0 5-2.24 5-5s-2.24-5-5-5-5 2.24-5 5 2.24 5 5 5z"/></svg>';
|
||||
})(window.gl || (window.gl = {}));
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 40"><path fill="#8F8F8F" fill-rule="evenodd" d="M28.777 18c-.91-4.008-4.494-7-8.777-7-4.283 0-7.868 2.992-8.777 7H4.01C2.9 18 2 18.895 2 20c0 1.112.9 2 2.01 2h7.213c.91 4.008 4.494 7 8.777 7 4.283 0 7.868-2.992 8.777-7h7.214C37.1 22 38 21.105 38 20c0-1.112-.9-2-2.01-2h-7.213zM20 25c2.76 0 5-2.24 5-5s-2.24-5-5-5-5 2.24-5 5 2.24 5 5 5z"/></svg>
|
After Width: | Height: | Size: 401 B |
|
@ -25,6 +25,10 @@ require('./lib/utils/url_utility');
|
|||
isBound = true;
|
||||
}
|
||||
|
||||
if (gl.utils.getLocationHash()) {
|
||||
this.highlightSelectedLine();
|
||||
}
|
||||
|
||||
this.openAnchoredDiff();
|
||||
}
|
||||
|
||||
|
@ -78,7 +82,7 @@ require('./lib/utils/url_utility');
|
|||
if (nothingHereBlock.length) {
|
||||
const clickTarget = $('.js-file-title, .click-to-expand', diffFile);
|
||||
diffFile.data('singleFileDiff').toggleDiff(clickTarget, () => {
|
||||
this.highlighSelectedLine();
|
||||
this.highlightSelectedLine();
|
||||
if (cb) cb();
|
||||
});
|
||||
} else if (cb) {
|
||||
|
@ -94,7 +98,7 @@ require('./lib/utils/url_utility');
|
|||
} else {
|
||||
window.location.hash = hash;
|
||||
}
|
||||
this.highlighSelectedLine();
|
||||
this.highlightSelectedLine();
|
||||
}
|
||||
|
||||
diffViewType() {
|
||||
|
@ -108,7 +112,7 @@ require('./lib/utils/url_utility');
|
|||
return line.find('.diff-line-num').map((i, elm) => parseInt($(elm).data('linenumber'), 10));
|
||||
}
|
||||
|
||||
highlighSelectedLine() {
|
||||
highlightSelectedLine() {
|
||||
const hash = gl.utils.getLocationHash();
|
||||
const $diffFiles = $('.diff-file');
|
||||
$diffFiles.find('.hll').removeClass('hll');
|
||||
|
|
|
@ -35,6 +35,8 @@
|
|||
/* global Labels */
|
||||
/* global Shortcuts */
|
||||
|
||||
import GroupsList from './groups_list';
|
||||
|
||||
const ShortcutsBlob = require('./shortcuts_blob');
|
||||
const UserCallout = require('./user_callout');
|
||||
|
||||
|
@ -96,6 +98,10 @@ const UserCallout = require('./user_callout');
|
|||
case 'dashboard:todos:index':
|
||||
new gl.Todos();
|
||||
break;
|
||||
case 'dashboard:groups:index':
|
||||
case 'explore:groups:index':
|
||||
new GroupsList();
|
||||
break;
|
||||
case 'projects:milestones:new':
|
||||
case 'projects:milestones:edit':
|
||||
case 'projects:milestones:update':
|
||||
|
|
|
@ -35,9 +35,6 @@ module.exports = Vue.component('environment-component', {
|
|||
projectStoppedEnvironmentsPath: environmentsData.projectStoppedEnvironmentsPath,
|
||||
newEnvironmentPath: environmentsData.newEnvironmentPath,
|
||||
helpPagePath: environmentsData.helpPagePath,
|
||||
commitIconSvg: environmentsData.commitIconSvg,
|
||||
playIconSvg: environmentsData.playIconSvg,
|
||||
terminalIconSvg: environmentsData.terminalIconSvg,
|
||||
|
||||
// Pagination Properties,
|
||||
paginationInformation: {},
|
||||
|
@ -78,7 +75,7 @@ module.exports = Vue.component('environment-component', {
|
|||
|
||||
this.isLoading = true;
|
||||
|
||||
return service.all()
|
||||
return service.get()
|
||||
.then(resp => ({
|
||||
headers: resp.headers,
|
||||
body: resp.json(),
|
||||
|
@ -176,11 +173,7 @@ module.exports = Vue.component('environment-component', {
|
|||
<environment-table
|
||||
:environments="state.environments"
|
||||
:can-create-deployment="canCreateDeploymentParsed"
|
||||
:can-read-environment="canReadEnvironmentParsed"
|
||||
:play-icon-svg="playIconSvg"
|
||||
:terminal-icon-svg="terminalIconSvg"
|
||||
:commit-icon-svg="commitIconSvg">
|
||||
</environment-table>
|
||||
:can-read-environment="canReadEnvironmentParsed"/>
|
||||
</div>
|
||||
|
||||
<table-pagination v-if="state.paginationInformation && state.paginationInformation.totalPages > 1"
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
const Vue = require('vue');
|
||||
const playIconSvg = require('icons/_icon_play.svg');
|
||||
|
||||
module.exports = Vue.component('actions-component', {
|
||||
props: {
|
||||
|
@ -7,11 +8,10 @@ module.exports = Vue.component('actions-component', {
|
|||
required: false,
|
||||
default: () => [],
|
||||
},
|
||||
},
|
||||
|
||||
playIconSvg: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
data() {
|
||||
return { playIconSvg };
|
||||
},
|
||||
|
||||
template: `
|
||||
|
@ -28,9 +28,7 @@ module.exports = Vue.component('actions-component', {
|
|||
data-method="post"
|
||||
rel="nofollow"
|
||||
class="js-manual-action-link">
|
||||
|
||||
<span class="js-action-play-icon-container" v-html="playIconSvg"></span>
|
||||
|
||||
${playIconSvg}
|
||||
<span>
|
||||
{{action.name}}
|
||||
</span>
|
||||
|
|
|
@ -46,21 +46,6 @@ module.exports = Vue.component('environment-item', {
|
|||
required: false,
|
||||
default: false,
|
||||
},
|
||||
|
||||
commitIconSvg: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
|
||||
playIconSvg: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
|
||||
terminalIconSvg: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
},
|
||||
|
||||
computed: {
|
||||
|
@ -487,9 +472,7 @@ module.exports = Vue.component('environment-item', {
|
|||
:commit-url="commitUrl"
|
||||
:short-sha="commitShortSha"
|
||||
:title="commitTitle"
|
||||
:author="commitAuthor"
|
||||
:commit-icon-svg="commitIconSvg">
|
||||
</commit-component>
|
||||
:author="commitAuthor"/>
|
||||
</div>
|
||||
<p v-if="!model.isFolder && !hasLastDeploymentKey" class="commit-title">
|
||||
No deployments yet
|
||||
|
@ -506,27 +489,20 @@ module.exports = Vue.component('environment-item', {
|
|||
<td class="environments-actions">
|
||||
<div v-if="!model.isFolder" class="btn-group pull-right" role="group">
|
||||
<actions-component v-if="hasManualActions && canCreateDeployment"
|
||||
:play-icon-svg="playIconSvg"
|
||||
:actions="manualActions">
|
||||
</actions-component>
|
||||
:actions="manualActions"/>
|
||||
|
||||
<external-url-component v-if="externalURL && canReadEnvironment"
|
||||
:external-url="externalURL">
|
||||
</external-url-component>
|
||||
:external-url="externalURL"/>
|
||||
|
||||
<stop-component v-if="hasStopAction && canCreateDeployment"
|
||||
:stop-url="model.stop_path">
|
||||
</stop-component>
|
||||
:stop-url="model.stop_path"/>
|
||||
|
||||
<terminal-button-component v-if="model && model.terminal_path"
|
||||
:terminal-icon-svg="terminalIconSvg"
|
||||
:terminal-path="model.terminal_path">
|
||||
</terminal-button-component>
|
||||
:terminal-path="model.terminal_path"/>
|
||||
|
||||
<rollback-component v-if="canRetry && canCreateDeployment"
|
||||
:is-last-deployment="isLastDeployment"
|
||||
:retry-url="retryUrl">
|
||||
</rollback-component>
|
||||
:retry-url="retryUrl"/>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
* Used in environments table.
|
||||
*/
|
||||
const Vue = require('vue');
|
||||
const terminalIconSvg = require('icons/_icon_terminal.svg');
|
||||
|
||||
module.exports = Vue.component('terminal-button-component', {
|
||||
props: {
|
||||
|
@ -10,16 +11,16 @@ module.exports = Vue.component('terminal-button-component', {
|
|||
type: String,
|
||||
default: '',
|
||||
},
|
||||
terminalIconSvg: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return { terminalIconSvg };
|
||||
},
|
||||
|
||||
template: `
|
||||
<a class="btn terminal-button"
|
||||
:href="terminalPath">
|
||||
<span class="js-terminal-icon-container" v-html="terminalIconSvg"></span>
|
||||
${terminalIconSvg}
|
||||
</a>
|
||||
`,
|
||||
});
|
||||
|
|
|
@ -28,21 +28,6 @@ module.exports = Vue.component('environment-table-component', {
|
|||
required: false,
|
||||
default: false,
|
||||
},
|
||||
|
||||
commitIconSvg: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
|
||||
playIconSvg: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
|
||||
terminalIconSvg: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
},
|
||||
|
||||
template: `
|
||||
|
@ -63,10 +48,7 @@ module.exports = Vue.component('environment-table-component', {
|
|||
<tr is="environment-item"
|
||||
:model="model"
|
||||
:can-create-deployment="canCreateDeployment"
|
||||
:can-read-environment="canReadEnvironment"
|
||||
:play-icon-svg="playIconSvg"
|
||||
:terminal-icon-svg="terminalIconSvg"
|
||||
:commit-icon-svg="commitIconSvg"></tr>
|
||||
:can-read-environment="canReadEnvironment"></tr>
|
||||
</template>
|
||||
</tbody>
|
||||
</table>
|
||||
|
|
|
@ -92,7 +92,7 @@ module.exports = Vue.component('environment-folder-view', {
|
|||
|
||||
this.isLoading = true;
|
||||
|
||||
return service.all()
|
||||
return service.get()
|
||||
.then(resp => ({
|
||||
headers: resp.headers,
|
||||
body: resp.json(),
|
||||
|
|
|
@ -5,7 +5,7 @@ class EnvironmentsService {
|
|||
this.environments = Vue.resource(endpoint);
|
||||
}
|
||||
|
||||
all() {
|
||||
get() {
|
||||
return this.environments.get();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
/**
|
||||
* Based on project list search.
|
||||
* Makes search request for groups when user types a value in the search input.
|
||||
* Updates the html content of the page with the received one.
|
||||
*/
|
||||
export default class GroupsList {
|
||||
constructor() {
|
||||
this.groupsListFilterElement = document.querySelector('.js-groups-list-filter');
|
||||
this.groupsListHolderElement = document.querySelector('.js-groups-list-holder');
|
||||
|
||||
this.initSearch();
|
||||
}
|
||||
|
||||
initSearch() {
|
||||
this.debounceFilter = _.debounce(this.filterResults.bind(this), 500);
|
||||
|
||||
this.groupsListFilterElement.removeEventListener('input', this.debounceFilter);
|
||||
this.groupsListFilterElement.addEventListener('input', this.debounceFilter);
|
||||
}
|
||||
|
||||
filterResults() {
|
||||
const form = document.querySelector('form#group-filter-form');
|
||||
const groupFilterUrl = `${form.getAttribute('action')}?${$(form).serialize()}`;
|
||||
|
||||
$(this.groupsListHolderElement).fadeTo(250, 0.5);
|
||||
|
||||
return $.ajax({
|
||||
url: form.getAttribute('action'),
|
||||
data: $(form).serialize(),
|
||||
type: 'GET',
|
||||
dataType: 'json',
|
||||
context: this,
|
||||
complete() {
|
||||
$(this.groupsListHolderElement).fadeTo(250, 1);
|
||||
},
|
||||
success(data) {
|
||||
this.groupsListHolderElement.innerHTML = data.html;
|
||||
|
||||
// Change url so if user reload a page - search results are saved
|
||||
return window.history.replaceState({
|
||||
page: groupFilterUrl,
|
||||
|
||||
}, document.title, groupFilterUrl);
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,4 +1,6 @@
|
|||
/* global Vue */
|
||||
import stopwatchSvg from 'icons/_icon_stopwatch.svg';
|
||||
|
||||
require('../../../lib/utils/pretty_time');
|
||||
|
||||
(() => {
|
||||
|
@ -11,7 +13,6 @@ require('../../../lib/utils/pretty_time');
|
|||
'showNoTimeTrackingState',
|
||||
'timeSpentHumanReadable',
|
||||
'timeEstimateHumanReadable',
|
||||
'stopwatchSvg',
|
||||
],
|
||||
methods: {
|
||||
abbreviateTime(timeStr) {
|
||||
|
@ -20,7 +21,7 @@ require('../../../lib/utils/pretty_time');
|
|||
},
|
||||
template: `
|
||||
<div class='sidebar-collapsed-icon'>
|
||||
<div v-html='stopwatchSvg'></div>
|
||||
${stopwatchSvg}
|
||||
<div class='time-tracking-collapsed-summary'>
|
||||
<div class='compare' v-if='showComparisonState'>
|
||||
<span>{{ abbreviateTime(timeSpentHumanReadable) }} / {{ abbreviateTime(timeEstimateHumanReadable) }}</span>
|
||||
|
|
|
@ -15,7 +15,6 @@ require('./comparison_pane');
|
|||
'time_spent',
|
||||
'human_time_estimate',
|
||||
'human_time_spent',
|
||||
'stopwatchSvg',
|
||||
'docsUrl',
|
||||
],
|
||||
data() {
|
||||
|
@ -71,20 +70,19 @@ require('./comparison_pane');
|
|||
:show-spent-only-state='showSpentOnlyState'
|
||||
:show-estimate-only-state='showEstimateOnlyState'
|
||||
:time-spent-human-readable='timeSpentHumanReadable'
|
||||
:time-estimate-human-readable='timeEstimateHumanReadable'
|
||||
:stopwatch-svg='stopwatchSvg'>
|
||||
:time-estimate-human-readable='timeEstimateHumanReadable'>
|
||||
</time-tracking-collapsed-state>
|
||||
<div class='title hide-collapsed'>
|
||||
Time tracking
|
||||
<div class='help-button pull-right'
|
||||
v-if='!showHelpState'
|
||||
@click='toggleHelpState(true)'>
|
||||
<i class='fa fa-question-circle'></i>
|
||||
<i class='fa fa-question-circle' aria-hidden='true'></i>
|
||||
</div>
|
||||
<div class='close-help-button pull-right'
|
||||
v-if='showHelpState'
|
||||
@click='toggleHelpState(false)'>
|
||||
<i class='fa fa-close'></i>
|
||||
<i class='fa fa-close' aria-hidden='true'></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class='time-tracking-content hide-collapsed'>
|
||||
|
|
|
@ -39,8 +39,9 @@ require('../../subbable_resource');
|
|||
listenForSlashCommands() {
|
||||
$(document).on('ajax:success', '.gfm-form', (e, data) => {
|
||||
const subscribedCommands = ['spend_time', 'time_estimate'];
|
||||
const changedCommands = data.commands_changes;
|
||||
|
||||
const changedCommands = data.commands_changes
|
||||
? Object.keys(data.commands_changes)
|
||||
: [];
|
||||
if (changedCommands && _.intersection(subscribedCommands, changedCommands).length) {
|
||||
this.fetchIssuable();
|
||||
}
|
||||
|
|
|
@ -246,17 +246,6 @@
|
|||
previousPage: parseInt(paginationInformation['X-PREV-PAGE'], 10),
|
||||
});
|
||||
|
||||
/**
|
||||
* Transforms a DOMStringMap into a plain object.
|
||||
*
|
||||
* @param {DOMStringMap} DOMStringMapObject
|
||||
* @returns {Object}
|
||||
*/
|
||||
w.gl.utils.DOMStringMapToObject = DOMStringMapObject => Object.keys(DOMStringMapObject).reduce((acc, element) => {
|
||||
acc[element] = DOMStringMapObject[element];
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
/**
|
||||
* Updates the search parameter of a URL given the parameter and values provided.
|
||||
*
|
||||
|
|
|
@ -65,9 +65,10 @@ require('vendor/latinise');
|
|||
}
|
||||
};
|
||||
gl.text.insertText = function(textArea, text, tag, blockTag, selected, wrap) {
|
||||
var insertText, inserted, selectedSplit, startChar, removedLastNewLine, removedFirstNewLine;
|
||||
var insertText, inserted, selectedSplit, startChar, removedLastNewLine, removedFirstNewLine, currentLineEmpty, lastNewLine;
|
||||
removedLastNewLine = false;
|
||||
removedFirstNewLine = false;
|
||||
currentLineEmpty = false;
|
||||
|
||||
// Remove the first newline
|
||||
if (selected.indexOf('\n') === 0) {
|
||||
|
@ -82,7 +83,17 @@ require('vendor/latinise');
|
|||
}
|
||||
|
||||
selectedSplit = selected.split('\n');
|
||||
startChar = !wrap && textArea.selectionStart > 0 ? '\n' : '';
|
||||
|
||||
if (!wrap) {
|
||||
lastNewLine = textArea.value.substr(0, textArea.selectionStart).lastIndexOf('\n');
|
||||
|
||||
// Check whether the current line is empty or consists only of spaces(=handle as empty)
|
||||
if (/^\s*$/.test(textArea.value.substring(lastNewLine, textArea.selectionStart))) {
|
||||
currentLineEmpty = true;
|
||||
}
|
||||
}
|
||||
|
||||
startChar = !wrap && !currentLineEmpty && textArea.selectionStart > 0 ? '\n' : '';
|
||||
|
||||
if (selectedSplit.length > 1 && (!wrap || (blockTag != null))) {
|
||||
if (blockTag != null) {
|
||||
|
@ -142,9 +153,8 @@ require('vendor/latinise');
|
|||
}
|
||||
};
|
||||
gl.text.updateText = function(textArea, tag, blockTag, wrap) {
|
||||
var $textArea, oldVal, selected, text;
|
||||
var $textArea, selected, text;
|
||||
$textArea = $(textArea);
|
||||
oldVal = $textArea.val();
|
||||
textArea = $textArea.get(0);
|
||||
text = $textArea.val();
|
||||
selected = this.selectedText(text, textArea);
|
||||
|
|
|
@ -129,8 +129,9 @@ require('./smart_interval');
|
|||
};
|
||||
|
||||
MergeRequestWidget.prototype.getMergeStatus = function() {
|
||||
return $.get(this.opts.merge_check_url, function(data) {
|
||||
return $.get(this.opts.merge_check_url, (data) => {
|
||||
var $html = $(data);
|
||||
this.updateMergeButton(this.status, this.hasCi, $html);
|
||||
$('.mr-widget-body').replaceWith($html.find('.mr-widget-body'));
|
||||
$('.mr-widget-footer').replaceWith($html.find('.mr-widget-footer'));
|
||||
});
|
||||
|
@ -154,9 +155,9 @@ require('./smart_interval');
|
|||
return $.getJSON(this.opts.ci_status_url, (function(_this) {
|
||||
return function(data) {
|
||||
var message, status, title;
|
||||
if (!data.status) {
|
||||
return;
|
||||
}
|
||||
_this.status = data.status;
|
||||
_this.hasCi = data.has_ci;
|
||||
_this.updateMergeButton(_this.status, _this.hasCi);
|
||||
if (data.environments && data.environments.length) _this.renderEnvironments(data.environments);
|
||||
if (data.status !== _this.opts.ci_status ||
|
||||
data.sha !== _this.opts.ci_sha ||
|
||||
|
@ -232,36 +233,45 @@ require('./smart_interval');
|
|||
return;
|
||||
}
|
||||
$('.ci_widget').hide();
|
||||
allowed_states = ["failed", "canceled", "running", "pending", "success", "success_with_warnings", "skipped", "not_found"];
|
||||
if (indexOf.call(allowed_states, state) !== -1) {
|
||||
$('.ci_widget.ci-' + state).show();
|
||||
$('.ci_widget.ci-' + state).show();
|
||||
|
||||
this.initMiniPipelineGraph();
|
||||
};
|
||||
|
||||
MergeRequestWidget.prototype.showCICoverage = function(coverage) {
|
||||
var text = `Coverage ${coverage}%`;
|
||||
return $('.ci_widget:visible .ci-coverage').text(text);
|
||||
};
|
||||
|
||||
MergeRequestWidget.prototype.updateMergeButton = function(state, hasCi, $html) {
|
||||
const allowed_states = ["failed", "canceled", "running", "pending", "success", "success_with_warnings", "skipped", "not_found"];
|
||||
let stateClass = 'btn-danger';
|
||||
if (!hasCi) {
|
||||
stateClass = 'btn-create';
|
||||
} else if (indexOf.call(allowed_states, state) !== -1) {
|
||||
switch (state) {
|
||||
case "failed":
|
||||
case "canceled":
|
||||
case "not_found":
|
||||
this.setMergeButtonClass('btn-danger');
|
||||
stateClass = 'btn-danger';
|
||||
break;
|
||||
case "running":
|
||||
this.setMergeButtonClass('btn-info');
|
||||
stateClass = 'btn-info';
|
||||
break;
|
||||
case "success":
|
||||
case "success_with_warnings":
|
||||
this.setMergeButtonClass('btn-create');
|
||||
stateClass = 'btn-create';
|
||||
}
|
||||
} else {
|
||||
$('.ci_widget.ci-error').show();
|
||||
this.setMergeButtonClass('btn-danger');
|
||||
stateClass = 'btn-danger';
|
||||
}
|
||||
|
||||
this.setMergeButtonClass(stateClass, $html);
|
||||
};
|
||||
|
||||
MergeRequestWidget.prototype.showCICoverage = function(coverage) {
|
||||
var text;
|
||||
text = 'Coverage ' + coverage + '%';
|
||||
return $('.ci_widget:visible .ci-coverage').text(text);
|
||||
};
|
||||
|
||||
MergeRequestWidget.prototype.setMergeButtonClass = function(css_class) {
|
||||
return $('.js-merge-button,.accept-action .dropdown-toggle').removeClass('btn-danger btn-info btn-create').addClass(css_class);
|
||||
MergeRequestWidget.prototype.setMergeButtonClass = function(css_class, $html = $('.mr-state-widget')) {
|
||||
return $html.find('.js-merge-button').removeClass('btn-danger btn-info btn-create').addClass(css_class);
|
||||
};
|
||||
|
||||
MergeRequestWidget.prototype.updatePipelineUrls = function(id) {
|
||||
|
|
|
@ -15,15 +15,15 @@
|
|||
});
|
||||
|
||||
$(document)
|
||||
.off('click', '.accept_merge_request')
|
||||
.on('click', '.accept_merge_request', () => {
|
||||
$('.js-merge-button').html('<i class="fa fa-spinner fa-spin"></i> Merge in progress');
|
||||
.off('click', '.accept-merge-request')
|
||||
.on('click', '.accept-merge-request', () => {
|
||||
$('.js-merge-button, .js-merge-when-pipeline-succeeds-button').html('<i class="fa fa-spinner fa-spin"></i> Merge in progress');
|
||||
});
|
||||
|
||||
$(document)
|
||||
.off('click', '.merge_when_build_succeeds')
|
||||
.on('click', '.merge_when_build_succeeds', () => {
|
||||
$('#merge_when_build_succeeds').val('1');
|
||||
.off('click', '.merge-when-pipeline-succeeds')
|
||||
.on('click', '.merge-when-pipeline-succeeds', () => {
|
||||
$('#merge_when_pipeline_succeeds').val('1');
|
||||
});
|
||||
|
||||
$(document)
|
||||
|
|
|
@ -246,12 +246,21 @@ require('./task_list');
|
|||
};
|
||||
|
||||
Notes.prototype.handleCreateChanges = function(note) {
|
||||
var votesBlock;
|
||||
if (typeof note === 'undefined') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (note.commands_changes && note.commands_changes.indexOf('merge') !== -1) {
|
||||
$.get(mrRefreshWidgetUrl);
|
||||
if (note.commands_changes) {
|
||||
if ('merge' in note.commands_changes) {
|
||||
$.get(mrRefreshWidgetUrl);
|
||||
}
|
||||
|
||||
if ('emoji_award' in note.commands_changes) {
|
||||
votesBlock = $('.js-awards-block').eq(0);
|
||||
gl.awardsHandler.addAwardToEmojiBar(votesBlock, note.commands_changes.emoji_award);
|
||||
return gl.awardsHandler.scrollToAwards();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -262,26 +271,16 @@ require('./task_list');
|
|||
*/
|
||||
|
||||
Notes.prototype.renderNote = function(note) {
|
||||
var $notesList, votesBlock;
|
||||
var $notesList;
|
||||
if (!note.valid) {
|
||||
if (note.award) {
|
||||
new Flash('You have already awarded this emoji!', 'alert', this.parentTimeline);
|
||||
}
|
||||
else {
|
||||
if (note.errors.commands_only) {
|
||||
new Flash(note.errors.commands_only, 'notice', this.parentTimeline);
|
||||
this.refresh();
|
||||
}
|
||||
if (note.errors.commands_only) {
|
||||
new Flash(note.errors.commands_only, 'notice', this.parentTimeline);
|
||||
this.refresh();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (note.award) {
|
||||
votesBlock = $('.js-awards-block').eq(0);
|
||||
gl.awardsHandler.addAwardToEmojiBar(votesBlock, note.name);
|
||||
return gl.awardsHandler.scrollToAwards();
|
||||
// render note if it not present in loaded list
|
||||
// or skip if rendered
|
||||
} else if (this.isNewNote(note)) {
|
||||
|
||||
if (this.isNewNote(note)) {
|
||||
this.note_ids.push(note.id);
|
||||
$notesList = $('ul.main-notes-list');
|
||||
$notesList.append(note.html).syntaxHighlight();
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
this.onPickImageClick = this.onPickImageClick.bind(this);
|
||||
this.fileInput = $(input);
|
||||
this.modalCropImg = _.isString(this.modalCropImg) ? $(this.modalCropImg) : this.modalCropImg;
|
||||
this.fileInput.attr('name', `${this.fileInput.attr('name')}-trigger`).attr('id', `this.fileInput.attr('id')-trigger`);
|
||||
this.fileInput.attr('name', `${this.fileInput.attr('name')}-trigger`).attr('id', `${this.fileInput.attr('id')}-trigger`);
|
||||
this.exportWidth = exportWidth;
|
||||
this.exportHeight = exportHeight;
|
||||
this.cropBoxWidth = cropBoxWidth;
|
||||
|
|
|
@ -16,9 +16,6 @@ require('./shortcuts');
|
|||
Mousetrap.bind('g p', function() {
|
||||
return ShortcutsNavigation.findAndFollowLink('.shortcuts-project');
|
||||
});
|
||||
Mousetrap.bind('g e', function() {
|
||||
return ShortcutsNavigation.findAndFollowLink('.shortcuts-project-activity');
|
||||
});
|
||||
Mousetrap.bind('g f', function() {
|
||||
return ShortcutsNavigation.findAndFollowLink('.shortcuts-tree');
|
||||
});
|
||||
|
@ -31,9 +28,6 @@ require('./shortcuts');
|
|||
Mousetrap.bind('g n', function() {
|
||||
return ShortcutsNavigation.findAndFollowLink('.shortcuts-network');
|
||||
});
|
||||
Mousetrap.bind('g g', function() {
|
||||
return ShortcutsNavigation.findAndFollowLink('.shortcuts-graphs');
|
||||
});
|
||||
Mousetrap.bind('g i', function() {
|
||||
return ShortcutsNavigation.findAndFollowLink('.shortcuts-issues');
|
||||
});
|
||||
|
|
|
@ -43,6 +43,8 @@ class UserCallout {
|
|||
this.userCalloutBody.append($template);
|
||||
$template.find(closeButton).on('click', e => this.dismissCallout(e));
|
||||
$template.find(userCalloutBtn).on('click', e => this.dismissCallout(e));
|
||||
} else {
|
||||
this.userCalloutBody.remove();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -50,7 +52,7 @@ class UserCallout {
|
|||
Cookies.set(USER_CALLOUT_COOKIE, 'true');
|
||||
const $currentTarget = $(e.currentTarget);
|
||||
if ($currentTarget.hasClass('close-user-callout')) {
|
||||
this.userCalloutBody.empty();
|
||||
this.userCalloutBody.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,15 +11,10 @@ $(() => new Vue({
|
|||
|
||||
data() {
|
||||
const project = document.querySelector('.pipelines');
|
||||
const svgs = document.querySelector('.pipeline-svgs').dataset;
|
||||
|
||||
// Transform svgs DOMStringMap to a plain Object.
|
||||
const svgsObject = gl.utils.DOMStringMapToObject(svgs);
|
||||
|
||||
return {
|
||||
scope: project.dataset.url,
|
||||
store: new gl.PipelineStore(),
|
||||
svgs: svgsObject,
|
||||
};
|
||||
},
|
||||
components: {
|
||||
|
@ -27,10 +22,8 @@ $(() => new Vue({
|
|||
},
|
||||
template: `
|
||||
<vue-pipelines
|
||||
:scope='scope'
|
||||
:store='store'
|
||||
:svgs='svgs'
|
||||
>
|
||||
:scope="scope"
|
||||
:store="store">
|
||||
</vue-pipelines>
|
||||
`,
|
||||
}));
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
/* global Vue, Flash, gl */
|
||||
/* eslint-disable no-param-reassign, no-alert */
|
||||
/* eslint-disable no-param-reassign, no-alert */
|
||||
const playIconSvg = require('icons/_icon_play.svg');
|
||||
|
||||
((gl) => {
|
||||
gl.VuePipelineActions = Vue.extend({
|
||||
props: ['pipeline', 'svgs'],
|
||||
props: ['pipeline'],
|
||||
computed: {
|
||||
actions() {
|
||||
return this.pipeline.details.manual_actions.length > 0;
|
||||
|
@ -31,6 +32,11 @@
|
|||
}
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return { playIconSvg };
|
||||
},
|
||||
|
||||
template: `
|
||||
<td class="pipeline-actions">
|
||||
<div class="pull-right">
|
||||
|
@ -42,7 +48,7 @@
|
|||
title="Manual job"
|
||||
data-placement="top"
|
||||
aria-label="Manual job">
|
||||
<span v-html="svgs.iconPlay" aria-hidden="true"></span>
|
||||
<span v-html="playIconSvg" aria-hidden="true"></span>
|
||||
<i class="fa fa-caret-down" aria-hidden="true"></i>
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu-align-right">
|
||||
|
@ -50,8 +56,8 @@
|
|||
<a
|
||||
rel="nofollow"
|
||||
data-method="post"
|
||||
:href="action.path">
|
||||
<span v-html="svgs.iconPlay" aria-hidden="true"></span>
|
||||
:href="action.path" >
|
||||
<span v-html="playIconSvg" aria-hidden="true"></span>
|
||||
<span>{{action.name}}</span>
|
||||
</a>
|
||||
</li>
|
||||
|
|
|
@ -27,7 +27,7 @@ const CommitPipelinesStoreWithTimeAgo = require('../commit/pipelines/pipelines_s
|
|||
pageRequest: false,
|
||||
};
|
||||
},
|
||||
props: ['scope', 'store', 'svgs'],
|
||||
props: ['scope', 'store'],
|
||||
created() {
|
||||
const pagenum = gl.utils.getParameterByName('page');
|
||||
const scope = gl.utils.getParameterByName('scope');
|
||||
|
@ -45,18 +45,15 @@ const CommitPipelinesStoreWithTimeAgo = require('../commit/pipelines/pipelines_s
|
|||
|
||||
methods: {
|
||||
/**
|
||||
* Changes the URL according to the pagination component.
|
||||
* Will change the page number and update the URL.
|
||||
*
|
||||
* If no scope is provided, 'all' is assumed.
|
||||
*
|
||||
* Pagination component sends "null" when no scope is provided.
|
||||
*
|
||||
* @param {Number} pagenum
|
||||
* @param {String} apiScope = 'all'
|
||||
* @param {Number} pageNumber desired page to go to.
|
||||
*/
|
||||
change(pagenum, apiScope) {
|
||||
if (!apiScope) apiScope = 'all';
|
||||
gl.utils.visitUrl(`?scope=${apiScope}&page=${pagenum}`);
|
||||
change(pageNumber) {
|
||||
const param = gl.utils.setParamInURL('page', pageNumber);
|
||||
|
||||
gl.utils.visitUrl(param);
|
||||
return param;
|
||||
},
|
||||
},
|
||||
template: `
|
||||
|
@ -73,10 +70,7 @@ const CommitPipelinesStoreWithTimeAgo = require('../commit/pipelines/pipelines_s
|
|||
</div>
|
||||
|
||||
<div class="table-holder" v-if='!pageRequest && pipelines.length'>
|
||||
<pipelines-table-component
|
||||
:pipelines='pipelines'
|
||||
:svgs='svgs'>
|
||||
</pipelines-table-component>
|
||||
<pipelines-table-component :pipelines='pipelines'/>
|
||||
</div>
|
||||
|
||||
<gl-pagination
|
||||
|
|
|
@ -1,27 +1,42 @@
|
|||
/* global Vue, Flash, gl */
|
||||
/* eslint-disable no-param-reassign */
|
||||
import canceledSvg from 'icons/_icon_status_canceled_borderless.svg';
|
||||
import createdSvg from 'icons/_icon_status_created_borderless.svg';
|
||||
import failedSvg from 'icons/_icon_status_failed_borderless.svg';
|
||||
import manualSvg from 'icons/_icon_status_manual_borderless.svg';
|
||||
import pendingSvg from 'icons/_icon_status_pending_borderless.svg';
|
||||
import runningSvg from 'icons/_icon_status_running_borderless.svg';
|
||||
import skippedSvg from 'icons/_icon_status_skipped_borderless.svg';
|
||||
import successSvg from 'icons/_icon_status_success_borderless.svg';
|
||||
import warningSvg from 'icons/_icon_status_warning_borderless.svg';
|
||||
|
||||
((gl) => {
|
||||
gl.VueStage = Vue.extend({
|
||||
data() {
|
||||
const svgsDictionary = {
|
||||
icon_status_canceled: canceledSvg,
|
||||
icon_status_created: createdSvg,
|
||||
icon_status_failed: failedSvg,
|
||||
icon_status_manual: manualSvg,
|
||||
icon_status_pending: pendingSvg,
|
||||
icon_status_running: runningSvg,
|
||||
icon_status_skipped: skippedSvg,
|
||||
icon_status_success: successSvg,
|
||||
icon_status_warning: warningSvg,
|
||||
};
|
||||
|
||||
return {
|
||||
builds: '',
|
||||
spinner: '<span class="fa fa-spinner fa-spin"></span>',
|
||||
svg: svgsDictionary[this.stage.status.icon],
|
||||
};
|
||||
},
|
||||
|
||||
props: {
|
||||
stage: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
svgs: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
match: {
|
||||
type: Function,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
|
||||
updated() {
|
||||
|
@ -73,11 +88,6 @@
|
|||
tooltip() {
|
||||
return `has-tooltip ci-status-icon ci-status-icon-${this.stage.status.group}`;
|
||||
},
|
||||
svg() {
|
||||
const { icon } = this.stage.status;
|
||||
const stageIcon = icon.replace(/icon/i, 'stage_icon');
|
||||
return this.svgs[this.match(stageIcon)];
|
||||
},
|
||||
triggerButtonClass() {
|
||||
return `mini-pipeline-graph-dropdown-toggle has-tooltip js-builds-dropdown-button ci-status-icon-${this.stage.status.group}`;
|
||||
},
|
||||
|
@ -91,8 +101,7 @@
|
|||
data-placement="top"
|
||||
data-toggle="dropdown"
|
||||
type="button"
|
||||
:aria-label="stage.title"
|
||||
>
|
||||
:aria-label="stage.title">
|
||||
<span v-html="svg" aria-hidden="true"></span>
|
||||
<i class="fa fa-caret-down" aria-hidden="true"></i>
|
||||
</button>
|
||||
|
@ -101,8 +110,7 @@
|
|||
<div
|
||||
:class="dropdownClass"
|
||||
class="js-builds-dropdown-list scrollable-menu"
|
||||
v-html="buildsOrSpinner"
|
||||
>
|
||||
v-html="buildsOrSpinner">
|
||||
</div>
|
||||
</ul>
|
||||
</div>
|
||||
|
|
|
@ -1,32 +1,62 @@
|
|||
/* global Vue, gl */
|
||||
/* eslint-disable no-param-reassign */
|
||||
|
||||
import canceledSvg from 'icons/_icon_status_canceled.svg';
|
||||
import createdSvg from 'icons/_icon_status_created.svg';
|
||||
import failedSvg from 'icons/_icon_status_failed.svg';
|
||||
import manualSvg from 'icons/_icon_status_manual.svg';
|
||||
import pendingSvg from 'icons/_icon_status_pending.svg';
|
||||
import runningSvg from 'icons/_icon_status_running.svg';
|
||||
import skippedSvg from 'icons/_icon_status_skipped.svg';
|
||||
import successSvg from 'icons/_icon_status_success.svg';
|
||||
import warningSvg from 'icons/_icon_status_warning.svg';
|
||||
|
||||
((gl) => {
|
||||
gl.VueStatusScope = Vue.extend({
|
||||
props: [
|
||||
'pipeline', 'svgs', 'match',
|
||||
'pipeline',
|
||||
],
|
||||
|
||||
data() {
|
||||
const svgsDictionary = {
|
||||
icon_status_canceled: canceledSvg,
|
||||
icon_status_created: createdSvg,
|
||||
icon_status_failed: failedSvg,
|
||||
icon_status_manual: manualSvg,
|
||||
icon_status_pending: pendingSvg,
|
||||
icon_status_running: runningSvg,
|
||||
icon_status_skipped: skippedSvg,
|
||||
icon_status_success: successSvg,
|
||||
icon_status_warning: warningSvg,
|
||||
};
|
||||
|
||||
return {
|
||||
svg: svgsDictionary[this.pipeline.details.status.icon],
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
cssClasses() {
|
||||
const cssObject = { 'ci-status': true };
|
||||
cssObject[`ci-${this.pipeline.details.status.group}`] = true;
|
||||
return cssObject;
|
||||
},
|
||||
svg() {
|
||||
return this.svgs[this.match(this.pipeline.details.status.icon)];
|
||||
},
|
||||
|
||||
detailsPath() {
|
||||
const { status } = this.pipeline.details;
|
||||
return status.has_details ? status.details_path : false;
|
||||
},
|
||||
|
||||
content() {
|
||||
return `${this.svg} ${this.pipeline.details.status.text}`;
|
||||
},
|
||||
},
|
||||
template: `
|
||||
<td class="commit-link">
|
||||
<a
|
||||
:class='cssClasses'
|
||||
:href='detailsPath'
|
||||
v-html='svg + pipeline.details.status.text'
|
||||
>
|
||||
:class="cssClasses"
|
||||
:href="detailsPath"
|
||||
v-html="content">
|
||||
</a>
|
||||
</td>
|
||||
`,
|
||||
|
|
|
@ -4,14 +4,17 @@
|
|||
window.Vue = require('vue');
|
||||
require('../lib/utils/datetime_utility');
|
||||
|
||||
const iconTimerSvg = require('../../../views/shared/icons/_icon_timer.svg');
|
||||
|
||||
((gl) => {
|
||||
gl.VueTimeAgo = Vue.extend({
|
||||
data() {
|
||||
return {
|
||||
currentTime: new Date(),
|
||||
iconTimerSvg,
|
||||
};
|
||||
},
|
||||
props: ['pipeline', 'svgs'],
|
||||
props: ['pipeline'],
|
||||
computed: {
|
||||
timeAgo() {
|
||||
return gl.utils.getTimeago();
|
||||
|
@ -56,7 +59,7 @@ require('../lib/utils/datetime_utility');
|
|||
template: `
|
||||
<td class="pipelines-time-ago">
|
||||
<p class="duration" v-if='duration'>
|
||||
<span v-html='svgs.iconTimer'></span>
|
||||
<span v-html="iconTimerSvg"></span>
|
||||
{{duration}}
|
||||
</p>
|
||||
<p class="finished-at" v-if='timeStopped'>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/* global Vue */
|
||||
window.Vue = require('vue');
|
||||
const commitIconSvg = require('icons/_icon_commit.svg');
|
||||
|
||||
(() => {
|
||||
window.gl = window.gl || {};
|
||||
|
@ -69,11 +70,6 @@ window.Vue = require('vue');
|
|||
required: false,
|
||||
default: () => ({}),
|
||||
},
|
||||
|
||||
commitIconSvg: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
},
|
||||
|
||||
computed: {
|
||||
|
@ -116,6 +112,10 @@ window.Vue = require('vue');
|
|||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return { commitIconSvg };
|
||||
},
|
||||
|
||||
template: `
|
||||
<div class="branch-commit">
|
||||
|
||||
|
|
|
@ -21,14 +21,6 @@ require('./pipelines_table_row');
|
|||
default: () => ([]),
|
||||
},
|
||||
|
||||
/**
|
||||
* TODO: Remove this when we have webpack.
|
||||
*/
|
||||
svgs: {
|
||||
type: Object,
|
||||
required: true,
|
||||
default: () => ({}),
|
||||
},
|
||||
},
|
||||
|
||||
components: {
|
||||
|
@ -51,8 +43,7 @@ require('./pipelines_table_row');
|
|||
<template v-for="model in pipelines"
|
||||
v-bind:model="model">
|
||||
<tr is="pipelines-table-row-component"
|
||||
:pipeline="model"
|
||||
:svgs="svgs"></tr>
|
||||
:pipeline="model"></tr>
|
||||
</template>
|
||||
</tbody>
|
||||
</table>
|
||||
|
|
|
@ -25,14 +25,6 @@ require('./commit');
|
|||
default: () => ({}),
|
||||
},
|
||||
|
||||
/**
|
||||
* TODO: Remove this when we have webpack;
|
||||
*/
|
||||
svgs: {
|
||||
type: Object,
|
||||
required: true,
|
||||
default: () => ({}),
|
||||
},
|
||||
},
|
||||
|
||||
components: {
|
||||
|
@ -174,30 +166,9 @@ require('./commit');
|
|||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
/**
|
||||
* FIXME: This should not be in this component but in the components that
|
||||
* need this function.
|
||||
*
|
||||
* Used to render SVGs in the following components:
|
||||
* - status-scope
|
||||
* - dropdown-stage
|
||||
*
|
||||
* @param {String} string
|
||||
* @return {String}
|
||||
*/
|
||||
match(string) {
|
||||
return string.replace(/_([a-z])/g, (m, w) => w.toUpperCase());
|
||||
},
|
||||
},
|
||||
|
||||
template: `
|
||||
<tr class="commit">
|
||||
<status-scope
|
||||
:pipeline="pipeline"
|
||||
:svgs="svgs"
|
||||
:match="match">
|
||||
</status-scope>
|
||||
<status-scope :pipeline="pipeline"/>
|
||||
|
||||
<pipeline-url :pipeline="pipeline"></pipeline-url>
|
||||
|
||||
|
@ -208,26 +179,20 @@ require('./commit');
|
|||
:commit-url="commitUrl"
|
||||
:short-sha="commitShortSha"
|
||||
:title="commitTitle"
|
||||
:author="commitAuthor"
|
||||
:commit-icon-svg="svgs.commitIconSvg">
|
||||
</commit-component>
|
||||
:author="commitAuthor"/>
|
||||
</td>
|
||||
|
||||
<td class="stage-cell">
|
||||
<div class="stage-container dropdown js-mini-pipeline-graph"
|
||||
v-if="pipeline.details.stages.length > 0"
|
||||
v-for="stage in pipeline.details.stages">
|
||||
<dropdown-stage
|
||||
:stage="stage"
|
||||
:svgs="svgs"
|
||||
:match="match">
|
||||
</dropdown-stage>
|
||||
<dropdown-stage :stage="stage"/>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<time-ago :pipeline="pipeline" :svgs="svgs"></time-ago>
|
||||
<time-ago :pipeline="pipeline"/>
|
||||
|
||||
<pipeline-actions :pipeline="pipeline" :svgs="svgs"></pipeline-actions>
|
||||
<pipeline-actions :pipeline="pipeline" />
|
||||
</tr>
|
||||
`,
|
||||
});
|
||||
|
|
|
@ -19,12 +19,11 @@ window.Vue = require('vue');
|
|||
|
||||
/**
|
||||
This function will take the information given by the pagination component
|
||||
And make a new Turbolinks call
|
||||
|
||||
Here is an example `change` method:
|
||||
|
||||
change(pagenum, apiScope) {
|
||||
gl.utils.visitUrl(`?scope=${apiScope}&p=${pagenum}`);
|
||||
change(pagenum) {
|
||||
gl.utils.visitUrl(`?page=${pagenum}`);
|
||||
},
|
||||
*/
|
||||
|
||||
|
@ -57,8 +56,6 @@ window.Vue = require('vue');
|
|||
},
|
||||
methods: {
|
||||
changePage(e) {
|
||||
const apiScope = gl.utils.getParameterByName('scope');
|
||||
|
||||
const text = e.target.innerText;
|
||||
const { totalPages, nextPage, previousPage } = this.pageInfo;
|
||||
|
||||
|
@ -66,19 +63,19 @@ window.Vue = require('vue');
|
|||
case SPREAD:
|
||||
break;
|
||||
case LAST:
|
||||
this.change(totalPages, apiScope);
|
||||
this.change(totalPages);
|
||||
break;
|
||||
case NEXT:
|
||||
this.change(nextPage, apiScope);
|
||||
this.change(nextPage);
|
||||
break;
|
||||
case PREV:
|
||||
this.change(previousPage, apiScope);
|
||||
this.change(previousPage);
|
||||
break;
|
||||
case FIRST:
|
||||
this.change(1, apiScope);
|
||||
this.change(1);
|
||||
break;
|
||||
default:
|
||||
this.change(+text, apiScope);
|
||||
this.change(+text);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
.calender-block {
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
border-top: 0;
|
||||
direction: rtl;
|
||||
|
||||
@media (min-width: $screen-sm-min) and (max-width: $screen-md-max) {
|
||||
|
|
|
@ -107,11 +107,12 @@
|
|||
|
||||
&.fa-spinner {
|
||||
font-size: 16px;
|
||||
margin-top: -8px;
|
||||
margin-top: -3px;
|
||||
}
|
||||
}
|
||||
|
||||
.fa-chevron-down {
|
||||
.fa-chevron-down,
|
||||
.fa-spinner {
|
||||
position: absolute;
|
||||
top: 11px;
|
||||
right: 8px;
|
||||
|
@ -192,6 +193,10 @@
|
|||
&.is-focused {
|
||||
background-color: $dropdown-link-hover-bg;
|
||||
text-decoration: none;
|
||||
|
||||
.badge {
|
||||
background-color: darken($row-hover, 5%);
|
||||
}
|
||||
}
|
||||
|
||||
&.dropdown-menu-empty-link {
|
||||
|
@ -228,6 +233,12 @@
|
|||
padding: 5px 8px;
|
||||
color: $gl-text-color-secondary;
|
||||
}
|
||||
|
||||
.badge {
|
||||
position: absolute;
|
||||
right: 8px;
|
||||
top: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-menu-drop-up {
|
||||
|
|
|
@ -271,6 +271,7 @@ span.idiff {
|
|||
font-size: 13px;
|
||||
line-height: 28px;
|
||||
display: inline-block;
|
||||
float: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -149,14 +149,14 @@ header {
|
|||
|
||||
.header-logo {
|
||||
display: inline-block;
|
||||
margin: 0 8px 0 3px;
|
||||
margin: 0 7px 0 2px;
|
||||
position: relative;
|
||||
top: 7px;
|
||||
top: 10px;
|
||||
transition-duration: .3s;
|
||||
|
||||
svg,
|
||||
img {
|
||||
height: 36px;
|
||||
height: 28px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
|
|
|
@ -73,10 +73,6 @@
|
|||
right: $gutter_collapsed_width;
|
||||
}
|
||||
}
|
||||
|
||||
&.with-overlay {
|
||||
padding-right: $gutter_collapsed_width;
|
||||
}
|
||||
}
|
||||
|
||||
.right-sidebar {
|
||||
|
|
|
@ -21,6 +21,7 @@ $dark-highlight-color: $black;
|
|||
$dark-pre-hll-bg: #373b41;
|
||||
$dark-hll-bg: #373b41;
|
||||
$dark-over-bg: #9f9ab5;
|
||||
$dark-expanded-bg: #3e3e3e;
|
||||
$dark-c: #969896;
|
||||
$dark-err: #c66;
|
||||
$dark-k: #b294bb;
|
||||
|
@ -155,6 +156,22 @@ $dark-il: #de935f;
|
|||
.line_content.match {
|
||||
@include dark-diff-match-line;
|
||||
}
|
||||
|
||||
&:not(.diff-expanded) + .diff-expanded,
|
||||
&.diff-expanded + .line_holder:not(.diff-expanded) {
|
||||
> .diff-line-num,
|
||||
> .line_content {
|
||||
border-top: 1px solid $black;
|
||||
}
|
||||
}
|
||||
|
||||
&.diff-expanded {
|
||||
> .diff-line-num,
|
||||
> .line_content {
|
||||
background: $dark-expanded-bg;
|
||||
border-color: $dark-expanded-bg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// highlight line via anchor
|
||||
|
|
|
@ -14,6 +14,7 @@ $monokai-line-empty-border: darken($monokai-line-empty-bg, 15%);
|
|||
$monokai-diff-border: #808080;
|
||||
$monokai-highlight-bg: #ffe792;
|
||||
$monokai-over-bg: #9f9ab5;
|
||||
$monokai-expanded-bg: #3e3e3e;
|
||||
|
||||
$monokai-new-bg: rgba(166, 226, 46, 0.1);
|
||||
$monokai-new-idiff: rgba(166, 226, 46, 0.15);
|
||||
|
@ -155,6 +156,22 @@ $monokai-gi: #a6e22e;
|
|||
.line_content.match {
|
||||
@include dark-diff-match-line;
|
||||
}
|
||||
|
||||
&:not(.diff-expanded) + .diff-expanded,
|
||||
&.diff-expanded + .line_holder:not(.diff-expanded) {
|
||||
> .diff-line-num,
|
||||
> .line_content {
|
||||
border-top: 1px solid $black;
|
||||
}
|
||||
}
|
||||
|
||||
&.diff-expanded {
|
||||
> .diff-line-num,
|
||||
> .line_content {
|
||||
background: $monokai-expanded-bg;
|
||||
border-color: $monokai-expanded-bg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// highlight line via anchor
|
||||
|
|
|
@ -18,6 +18,7 @@ $solarized-dark-line-color-old: #7a6c71;
|
|||
$solarized-dark-highlight: #094554;
|
||||
$solarized-dark-hll-bg: #174652;
|
||||
$solarized-dark-over-bg: #9f9ab5;
|
||||
$solarized-dark-expanded-bg: #010d10;
|
||||
$solarized-dark-c: #586e75;
|
||||
$solarized-dark-err: #93a1a1;
|
||||
$solarized-dark-g: #93a1a1;
|
||||
|
@ -159,6 +160,22 @@ $solarized-dark-il: #2aa198;
|
|||
.line_content.match {
|
||||
@include dark-diff-match-line;
|
||||
}
|
||||
|
||||
&:not(.diff-expanded) + .diff-expanded,
|
||||
&.diff-expanded + .line_holder:not(.diff-expanded) {
|
||||
> .diff-line-num,
|
||||
> .line_content {
|
||||
border-top: 1px solid $black;
|
||||
}
|
||||
}
|
||||
|
||||
&.diff-expanded {
|
||||
> .diff-line-num,
|
||||
> .line_content {
|
||||
background: $solarized-dark-expanded-bg;
|
||||
border-color: $solarized-dark-expanded-bg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// highlight line via anchor
|
||||
|
|
|
@ -19,6 +19,8 @@ $solarized-light-line-color-old: #ad9186;
|
|||
$solarized-light-highlight: #eee8d5;
|
||||
$solarized-light-hll-bg: #ddd8c5;
|
||||
$solarized-light-over-bg: #ded7fc;
|
||||
$solarized-light-expanded-border: #d2cdbd;
|
||||
$solarized-light-expanded-bg: #ece6d4;
|
||||
$solarized-light-c: #93a1a1;
|
||||
$solarized-light-err: #586e75;
|
||||
$solarized-light-g: #586e75;
|
||||
|
@ -166,6 +168,22 @@ $solarized-light-il: #2aa198;
|
|||
.line_content.match {
|
||||
@include matchLine;
|
||||
}
|
||||
|
||||
&:not(.diff-expanded) + .diff-expanded,
|
||||
&.diff-expanded + .line_holder:not(.diff-expanded) {
|
||||
> .diff-line-num,
|
||||
> .line_content {
|
||||
border-top: 1px solid $solarized-light-expanded-border;
|
||||
}
|
||||
}
|
||||
|
||||
&.diff-expanded {
|
||||
> .diff-line-num,
|
||||
> .line_content {
|
||||
background: $solarized-light-expanded-bg;
|
||||
border-color: $solarized-light-expanded-bg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// highlight line via anchor
|
||||
|
|
|
@ -8,6 +8,8 @@ $white-highlight: #fafe3d;
|
|||
$white-pre-hll-bg: #f8eec7;
|
||||
$white-hll-bg: #f8f8f8;
|
||||
$white-over-bg: #ded7fc;
|
||||
$white-expanded-border: #e0e0e0;
|
||||
$white-expanded-bg: #f7f7f7;
|
||||
$white-c: #998;
|
||||
$white-err: #a61717;
|
||||
$white-err-bg: #e3d2d2;
|
||||
|
@ -140,6 +142,22 @@ $white-gc-bg: #eaf2f5;
|
|||
}
|
||||
}
|
||||
|
||||
&:not(.diff-expanded) + .diff-expanded,
|
||||
&.diff-expanded + .line_holder:not(.diff-expanded) {
|
||||
> .diff-line-num,
|
||||
> .line_content {
|
||||
border-top: 1px solid $white-expanded-border;
|
||||
}
|
||||
}
|
||||
|
||||
&.diff-expanded {
|
||||
> .diff-line-num,
|
||||
> .line_content {
|
||||
background: $white-expanded-bg;
|
||||
border-color: $white-expanded-bg;
|
||||
}
|
||||
}
|
||||
|
||||
.line_content {
|
||||
&.old {
|
||||
background-color: $line-removed;
|
||||
|
|
|
@ -133,8 +133,13 @@
|
|||
width: 35px;
|
||||
font-weight: normal;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
&[disabled] {
|
||||
cursor: default;
|
||||
|
||||
&:hover,
|
||||
&:active {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -155,7 +155,7 @@
|
|||
|
||||
@media (max-width: $screen-xs-max) {
|
||||
.event-item {
|
||||
padding-left: $gl-padding;
|
||||
padding-left: 0;
|
||||
|
||||
.event-title {
|
||||
white-space: normal;
|
||||
|
@ -169,8 +169,7 @@
|
|||
|
||||
.event-body {
|
||||
margin: 0;
|
||||
border-left: 2px solid $events-body-border;
|
||||
padding-left: 10px;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.event-item-timestamp {
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
background-color: $gl-success;
|
||||
}
|
||||
|
||||
.accept_merge_request {
|
||||
.accept-merge-request {
|
||||
&.ci-pending,
|
||||
&.ci-running {
|
||||
@include btn-blue;
|
||||
|
@ -42,6 +42,12 @@
|
|||
@include btn-red;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-toggle {
|
||||
.fa {
|
||||
color: inherit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.accept-control {
|
||||
|
|
|
@ -279,7 +279,7 @@ table.u2f-registrations {
|
|||
}
|
||||
|
||||
.user-callout {
|
||||
margin: 24px auto 0;
|
||||
margin: 0 auto;
|
||||
|
||||
.bordered-box {
|
||||
border: 1px solid $border-color;
|
||||
|
@ -287,6 +287,7 @@ table.u2f-registrations {
|
|||
}
|
||||
|
||||
.landing {
|
||||
margin-top: $gl-padding;
|
||||
margin-bottom: $gl-padding;
|
||||
|
||||
.close {
|
||||
|
|
|
@ -178,3 +178,29 @@
|
|||
margin-left: $btn-side-margin;
|
||||
}
|
||||
}
|
||||
|
||||
.repo-charts {
|
||||
.sub-header {
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.sub-header-block.border-top {
|
||||
margin-top: 20px;
|
||||
padding: 0;
|
||||
border-top: 1px solid $white-dark;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.commit-stats li {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.tree-ref-header {
|
||||
margin-bottom: 20px;
|
||||
|
||||
h4 {
|
||||
margin: 0;
|
||||
line-height: 36px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -72,14 +72,6 @@ class ApplicationController < ActionController::Base
|
|||
end
|
||||
end
|
||||
|
||||
def authenticate_user!(*args)
|
||||
if redirect_to_home_page_url?
|
||||
return redirect_to current_application_settings.home_page_url
|
||||
end
|
||||
|
||||
super(*args)
|
||||
end
|
||||
|
||||
def log_exception(exception)
|
||||
application_trace = ActionDispatch::ExceptionWrapper.new(env, exception).application_trace
|
||||
application_trace.map!{ |t| " #{t}\n" }
|
||||
|
@ -130,10 +122,6 @@ class ApplicationController < ActionController::Base
|
|||
headers['X-XSS-Protection'] = '1; mode=block'
|
||||
headers['X-UA-Compatible'] = 'IE=edge'
|
||||
headers['X-Content-Type-Options'] = 'nosniff'
|
||||
# Enabling HSTS for non-standard ports would send clients to the wrong port
|
||||
if Gitlab.config.gitlab.https && Gitlab.config.gitlab.port == 443
|
||||
headers['Strict-Transport-Security'] = 'max-age=31536000'
|
||||
end
|
||||
end
|
||||
|
||||
def validate_user_service_ticket!
|
||||
|
@ -287,19 +275,6 @@ class ApplicationController < ActionController::Base
|
|||
session[:skip_tfa] && session[:skip_tfa] > Time.current
|
||||
end
|
||||
|
||||
def redirect_to_home_page_url?
|
||||
# If user is not signed-in and tries to access root_path - redirect him to landing page
|
||||
# Don't redirect to the default URL to prevent endless redirections
|
||||
return false unless current_application_settings.home_page_url.present?
|
||||
|
||||
home_page_url = current_application_settings.home_page_url.chomp('/')
|
||||
root_urls = [Gitlab.config.gitlab['url'].chomp('/'), root_url.chomp('/')]
|
||||
|
||||
return false if root_urls.include?(home_page_url)
|
||||
|
||||
current_user.nil? && root_path == request.path
|
||||
end
|
||||
|
||||
# U2F (universal 2nd factor) devices need a unique identifier for the application
|
||||
# to perform authentication.
|
||||
# https://developers.yubico.com/U2F/App_ID.html
|
||||
|
|
|
@ -1,47 +0,0 @@
|
|||
module Ci
|
||||
class ProjectsController < ::ApplicationController
|
||||
before_action :project
|
||||
before_action :no_cache, only: [:badge]
|
||||
before_action :authorize_read_project!, except: [:badge, :index]
|
||||
skip_before_action :authenticate_user!, only: [:badge]
|
||||
protect_from_forgery
|
||||
|
||||
def index
|
||||
redirect_to root_path
|
||||
end
|
||||
|
||||
def show
|
||||
# Temporary compatibility with CI badges pointing to CI project page
|
||||
redirect_to namespace_project_path(project.namespace, project)
|
||||
end
|
||||
|
||||
# Project status badge
|
||||
# Image with build status for sha or ref
|
||||
#
|
||||
# This action in DEPRECATED, this is here only for backwards compatibility
|
||||
# with projects migrated from GitLab CI.
|
||||
#
|
||||
def badge
|
||||
return render_404 unless @project
|
||||
|
||||
image = Ci::ImageForBuildService.new.execute(@project, params)
|
||||
send_file image.path, filename: image.name, disposition: 'inline', type: "image/svg+xml"
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def project
|
||||
@project ||= Project.find_by(ci_id: params[:id].to_i)
|
||||
end
|
||||
|
||||
def no_cache
|
||||
response.headers["Cache-Control"] = "no-cache, no-store, max-age=0, must-revalidate"
|
||||
response.headers["Pragma"] = "no-cache"
|
||||
response.headers["Expires"] = "Fri, 01 Jan 1990 00:00:00 GMT"
|
||||
end
|
||||
|
||||
def authorize_read_project!
|
||||
return access_denied! unless can?(current_user, :read_project, project)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -4,10 +4,9 @@ module CreatesCommit
|
|||
def create_commit(service, success_path:, failure_path:, failure_view: nil, success_notice: nil)
|
||||
set_commit_variables
|
||||
|
||||
start_branch = @mr_target_branch unless initial_commit?
|
||||
commit_params = @commit_params.merge(
|
||||
start_project: @mr_target_project,
|
||||
start_branch: start_branch,
|
||||
start_branch: @mr_target_branch,
|
||||
target_branch: @mr_source_branch
|
||||
)
|
||||
|
||||
|
@ -17,12 +16,16 @@ module CreatesCommit
|
|||
if result[:status] == :success
|
||||
update_flash_notice(success_notice)
|
||||
|
||||
success_path = final_success_path(success_path)
|
||||
|
||||
respond_to do |format|
|
||||
format.html { redirect_to final_success_path(success_path) }
|
||||
format.json { render json: { message: "success", filePath: final_success_path(success_path) } }
|
||||
format.html { redirect_to success_path }
|
||||
format.json { render json: { message: "success", filePath: success_path } }
|
||||
end
|
||||
else
|
||||
flash[:alert] = result[:message]
|
||||
failure_path = failure_path.call if failure_path.respond_to?(:call)
|
||||
|
||||
respond_to do |format|
|
||||
format.html do
|
||||
if failure_view
|
||||
|
@ -58,9 +61,13 @@ module CreatesCommit
|
|||
end
|
||||
|
||||
def final_success_path(success_path)
|
||||
return success_path unless create_merge_request?
|
||||
if create_merge_request?
|
||||
merge_request_exists? ? existing_merge_request_path : new_merge_request_path
|
||||
else
|
||||
success_path = success_path.call if success_path.respond_to?(:call)
|
||||
|
||||
merge_request_exists? ? existing_merge_request_path : new_merge_request_path
|
||||
success_path
|
||||
end
|
||||
end
|
||||
|
||||
def new_merge_request_path
|
||||
|
@ -92,47 +99,26 @@ module CreatesCommit
|
|||
end
|
||||
|
||||
def create_merge_request?
|
||||
# XXX: Even if the field is set, if we're checking the same branch
|
||||
# Even if the field is set, if we're checking the same branch
|
||||
# as the target branch in the same project,
|
||||
# we don't want to create a merge request.
|
||||
params[:create_merge_request].present? &&
|
||||
(different_project? || @ref != @target_branch)
|
||||
(different_project? || @mr_target_branch != @mr_source_branch)
|
||||
end
|
||||
|
||||
# TODO: We should really clean this up
|
||||
def set_commit_variables
|
||||
@mr_source_project =
|
||||
if can?(current_user, :push_code, @project)
|
||||
# Edit file in this project
|
||||
@project
|
||||
else
|
||||
# Merge request from fork to this project
|
||||
current_user.fork_of(@project)
|
||||
end
|
||||
if can?(current_user, :push_code, @project)
|
||||
@mr_source_project = @project
|
||||
@target_branch ||= @ref
|
||||
else
|
||||
@mr_source_project = current_user.fork_of(@project)
|
||||
@target_branch ||= @mr_source_project.repository.next_branch('patch')
|
||||
end
|
||||
|
||||
# Merge request to this project
|
||||
@mr_target_project = @project
|
||||
@mr_target_branch = @ref || @target_branch
|
||||
@mr_target_branch ||= @ref || @target_branch
|
||||
|
||||
@mr_source_branch = guess_mr_source_branch
|
||||
end
|
||||
|
||||
def initial_commit?
|
||||
@mr_target_branch.nil? ||
|
||||
!@mr_target_project.repository.branch_exists?(@mr_target_branch)
|
||||
end
|
||||
|
||||
def guess_mr_source_branch
|
||||
# XXX: Happens when viewing a commit without a branch. In this case,
|
||||
# @target_branch would be the default branch for @mr_source_project,
|
||||
# however we want a generated new branch here. Thus we can't use
|
||||
# @target_branch, but should pass nil to indicate that we want a new
|
||||
# branch instead of @target_branch.
|
||||
return if
|
||||
create_merge_request? &&
|
||||
# XXX: Don't understand why rubocop prefers this indention
|
||||
@mr_source_project.repository.branch_exists?(@target_branch)
|
||||
|
||||
@target_branch
|
||||
@mr_source_branch = @target_branch
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,5 +1,17 @@
|
|||
class Dashboard::GroupsController < Dashboard::ApplicationController
|
||||
def index
|
||||
@group_members = current_user.group_members.includes(source: :route).page(params[:page])
|
||||
@group_members = current_user.group_members.includes(source: :route).joins(:group)
|
||||
@group_members = @group_members.merge(Group.search(params[:filter_groups])) if params[:filter_groups].present?
|
||||
@group_members = @group_members.merge(Group.sort(@sort = params[:sort]))
|
||||
@group_members = @group_members.page(params[:page])
|
||||
|
||||
respond_to do |format|
|
||||
format.html
|
||||
format.json do
|
||||
render json: {
|
||||
html: view_to_html_string("dashboard/groups/_groups", locals: { group_members: @group_members })
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,8 +1,17 @@
|
|||
class Explore::GroupsController < Explore::ApplicationController
|
||||
def index
|
||||
@groups = GroupsFinder.new.execute(current_user)
|
||||
@groups = @groups.search(params[:search]) if params[:search].present?
|
||||
@groups = @groups.search(params[:filter_groups]) if params[:filter_groups].present?
|
||||
@groups = @groups.sort(@sort = params[:sort])
|
||||
@groups = @groups.page(params[:page])
|
||||
|
||||
respond_to do |format|
|
||||
format.html
|
||||
format.json do
|
||||
render json: {
|
||||
html: view_to_html_string("explore/groups/_groups", locals: { groups: @groups })
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,7 +5,7 @@ class Projects::BlobController < Projects::ApplicationController
|
|||
include ActionView::Helpers::SanitizeHelper
|
||||
|
||||
# Raised when given an invalid file path
|
||||
class InvalidPathError < StandardError; end
|
||||
InvalidPathError = Class.new(StandardError)
|
||||
|
||||
before_action :require_non_empty_project, except: [:new, :create]
|
||||
before_action :authorize_download_code!
|
||||
|
@ -24,7 +24,7 @@ class Projects::BlobController < Projects::ApplicationController
|
|||
|
||||
def create
|
||||
create_commit(Files::CreateService, success_notice: "The file has been successfully created.",
|
||||
success_path: namespace_project_blob_path(@project.namespace, @project, File.join(@target_branch, @file_path)),
|
||||
success_path: -> { namespace_project_blob_path(@project.namespace, @project, File.join(@target_branch, @file_path)) },
|
||||
failure_view: :new,
|
||||
failure_path: namespace_project_new_blob_path(@project.namespace, @project, @ref))
|
||||
end
|
||||
|
@ -40,7 +40,7 @@ class Projects::BlobController < Projects::ApplicationController
|
|||
|
||||
def update
|
||||
@path = params[:file_path] if params[:file_path].present?
|
||||
create_commit(Files::UpdateService, success_path: after_edit_path,
|
||||
create_commit(Files::UpdateService, success_path: -> { after_edit_path },
|
||||
failure_view: :edit,
|
||||
failure_path: namespace_project_blob_path(@project.namespace, @project, @id))
|
||||
|
||||
|
@ -62,7 +62,7 @@ class Projects::BlobController < Projects::ApplicationController
|
|||
|
||||
def destroy
|
||||
create_commit(Files::DestroyService, success_notice: "The file has been successfully deleted.",
|
||||
success_path: namespace_project_tree_path(@project.namespace, @project, @target_branch),
|
||||
success_path: -> { namespace_project_tree_path(@project.namespace, @project, @target_branch) },
|
||||
failure_view: :show,
|
||||
failure_path: namespace_project_blob_path(@project.namespace, @project, @id))
|
||||
end
|
||||
|
|
|
@ -51,23 +51,35 @@ class Projects::CommitController < Projects::ApplicationController
|
|||
def revert
|
||||
assign_change_commit_vars
|
||||
|
||||
return render_404 if @target_branch.blank?
|
||||
return render_404 if @start_branch.blank?
|
||||
|
||||
@target_branch = create_new_branch? ? @commit.revert_branch_name : @start_branch
|
||||
|
||||
@mr_target_branch = @start_branch
|
||||
|
||||
create_commit(Commits::RevertService, success_notice: "The #{@commit.change_type_title(current_user)} has been successfully reverted.",
|
||||
success_path: successful_change_path, failure_path: failed_change_path)
|
||||
success_path: -> { successful_change_path }, failure_path: failed_change_path)
|
||||
end
|
||||
|
||||
def cherry_pick
|
||||
assign_change_commit_vars
|
||||
|
||||
return render_404 if @target_branch.blank?
|
||||
return render_404 if @start_branch.blank?
|
||||
|
||||
@target_branch = create_new_branch? ? @commit.cherry_pick_branch_name : @start_branch
|
||||
|
||||
@mr_target_branch = @start_branch
|
||||
|
||||
create_commit(Commits::CherryPickService, success_notice: "The #{@commit.change_type_title(current_user)} has been successfully cherry-picked.",
|
||||
success_path: successful_change_path, failure_path: failed_change_path)
|
||||
success_path: -> { successful_change_path }, failure_path: failed_change_path)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def create_new_branch?
|
||||
params[:create_merge_request].present? || !can?(current_user, :push_code, @project)
|
||||
end
|
||||
|
||||
def successful_change_path
|
||||
referenced_merge_request_url || namespace_project_commits_url(@project.namespace, @project, @target_branch)
|
||||
end
|
||||
|
@ -78,7 +90,7 @@ class Projects::CommitController < Projects::ApplicationController
|
|||
|
||||
def referenced_merge_request_url
|
||||
if merge_request = @commit.merged_merge_request(current_user)
|
||||
namespace_project_merge_request_url(@project.namespace, @project, merge_request)
|
||||
namespace_project_merge_request_url(merge_request.target_project.namespace, merge_request.target_project, merge_request)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -94,7 +106,7 @@ class Projects::CommitController < Projects::ApplicationController
|
|||
|
||||
@diffs = commit.diffs(opts)
|
||||
@notes_count = commit.notes.count
|
||||
|
||||
|
||||
@environment = EnvironmentsFinder.new(@project, current_user, commit: @commit).execute.last
|
||||
end
|
||||
|
||||
|
@ -118,11 +130,7 @@ class Projects::CommitController < Projects::ApplicationController
|
|||
end
|
||||
|
||||
def assign_change_commit_vars
|
||||
@commit = project.commit(params[:id])
|
||||
@target_branch = params[:target_branch]
|
||||
@commit_params = {
|
||||
commit: @commit,
|
||||
create_merge_request: params[:create_merge_request].present? || different_project?
|
||||
}
|
||||
@start_branch = params[:start_branch]
|
||||
@commit_params = { commit: @commit }
|
||||
end
|
||||
end
|
||||
|
|
|
@ -17,6 +17,25 @@ class Projects::GraphsController < Projects::ApplicationController
|
|||
end
|
||||
|
||||
def commits
|
||||
redirect_to action: 'charts'
|
||||
end
|
||||
|
||||
def languages
|
||||
redirect_to action: 'charts'
|
||||
end
|
||||
|
||||
def charts
|
||||
get_commits
|
||||
get_languages
|
||||
end
|
||||
|
||||
def ci
|
||||
redirect_to charts_namespace_project_pipelines_path(@project.namespace, @project)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def get_commits
|
||||
@commits = @project.repository.commits(@ref, limit: 2000, skip_merges: true)
|
||||
@commits_graph = Gitlab::Graphs::Commits.new(@commits)
|
||||
@commits_per_week_days = @commits_graph.commits_per_week_days
|
||||
|
@ -24,15 +43,7 @@ class Projects::GraphsController < Projects::ApplicationController
|
|||
@commits_per_month = @commits_graph.commits_per_month
|
||||
end
|
||||
|
||||
def ci
|
||||
@charts = {}
|
||||
@charts[:week] = Ci::Charts::WeekChart.new(project)
|
||||
@charts[:month] = Ci::Charts::MonthChart.new(project)
|
||||
@charts[:year] = Ci::Charts::YearChart.new(project)
|
||||
@charts[:build_times] = Ci::Charts::BuildTime.new(project)
|
||||
end
|
||||
|
||||
def languages
|
||||
def get_languages
|
||||
@languages = Linguist::Repository.new(@repository.rugged, @repository.rugged.head.target_id).languages
|
||||
total = @languages.map(&:last).sum
|
||||
|
||||
|
@ -52,8 +63,6 @@ class Projects::GraphsController < Projects::ApplicationController
|
|||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def fetch_graph
|
||||
@commits = @project.repository.commits(@ref, limit: 6000, skip_merges: true)
|
||||
@log = []
|
||||
|
|
|
@ -10,11 +10,11 @@ class Projects::MergeRequestsController < Projects::ApplicationController
|
|||
before_action :module_enabled
|
||||
before_action :merge_request, only: [
|
||||
:edit, :update, :show, :diffs, :commits, :conflicts, :conflict_for_path, :pipelines, :merge, :merge_check,
|
||||
:ci_status, :ci_environments_status, :toggle_subscription, :cancel_merge_when_build_succeeds, :remove_wip, :resolve_conflicts, :assign_related_issues
|
||||
:ci_status, :ci_environments_status, :toggle_subscription, :cancel_merge_when_pipeline_succeeds, :remove_wip, :resolve_conflicts, :assign_related_issues
|
||||
]
|
||||
before_action :validates_merge_request, only: [:show, :diffs, :commits, :pipelines]
|
||||
before_action :define_show_vars, only: [:show, :diffs, :commits, :conflicts, :conflict_for_path, :builds, :pipelines]
|
||||
before_action :define_widget_vars, only: [:merge, :cancel_merge_when_build_succeeds, :merge_check]
|
||||
before_action :define_widget_vars, only: [:merge, :cancel_merge_when_pipeline_succeeds, :merge_check]
|
||||
before_action :define_commit_vars, only: [:diffs]
|
||||
before_action :define_diff_comment_vars, only: [:diffs]
|
||||
before_action :ensure_ref_fetched, only: [:show, :diffs, :commits, :builds, :conflicts, :conflict_for_path, :pipelines]
|
||||
|
@ -245,9 +245,11 @@ class Projects::MergeRequestsController < Projects::ApplicationController
|
|||
format.json do
|
||||
define_pipelines_vars
|
||||
|
||||
render json: PipelineSerializer
|
||||
render json: {
|
||||
pipelines: PipelineSerializer
|
||||
.new(project: @project, user: @current_user)
|
||||
.represent(@pipelines)
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -322,12 +324,13 @@ class Projects::MergeRequestsController < Projects::ApplicationController
|
|||
|
||||
def merge_check
|
||||
@merge_request.check_if_can_be_merged
|
||||
@pipelines = @merge_request.all_pipelines
|
||||
|
||||
render partial: "projects/merge_requests/widget/show.html.haml", layout: false
|
||||
end
|
||||
|
||||
def cancel_merge_when_build_succeeds
|
||||
unless @merge_request.can_cancel_merge_when_build_succeeds?(current_user)
|
||||
def cancel_merge_when_pipeline_succeeds
|
||||
unless @merge_request.can_cancel_merge_when_pipeline_succeeds?(current_user)
|
||||
return access_denied!
|
||||
end
|
||||
|
||||
|
@ -339,9 +342,9 @@ class Projects::MergeRequestsController < Projects::ApplicationController
|
|||
def merge
|
||||
return access_denied! unless @merge_request.can_be_merged_by?(current_user)
|
||||
|
||||
# Disable the CI check if merge_when_build_succeeds is enabled since we have
|
||||
# Disable the CI check if merge_when_pipeline_succeeds is enabled since we have
|
||||
# to wait until CI completes to know
|
||||
unless @merge_request.mergeable?(skip_ci_check: merge_when_build_succeeds_active?)
|
||||
unless @merge_request.mergeable?(skip_ci_check: merge_when_pipeline_succeeds_active?)
|
||||
@status = :failed
|
||||
return
|
||||
end
|
||||
|
@ -353,7 +356,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
|
|||
|
||||
@merge_request.update(merge_error: nil)
|
||||
|
||||
if params[:merge_when_build_succeeds].present?
|
||||
if params[:merge_when_pipeline_succeeds].present?
|
||||
unless @merge_request.head_pipeline
|
||||
@status = :failed
|
||||
return
|
||||
|
@ -364,7 +367,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
|
|||
.new(@project, current_user, merge_params)
|
||||
.execute(@merge_request)
|
||||
|
||||
@status = :merge_when_build_succeeds
|
||||
@status = :merge_when_pipeline_succeeds
|
||||
elsif @merge_request.head_pipeline.success?
|
||||
# This can be triggered when a user clicks the auto merge button while
|
||||
# the tests finish at about the same time
|
||||
|
@ -381,8 +384,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController
|
|||
|
||||
def merge_widget_refresh
|
||||
@status =
|
||||
if merge_request.merge_when_build_succeeds
|
||||
:merge_when_build_succeeds
|
||||
if merge_request.merge_when_pipeline_succeeds
|
||||
:merge_when_pipeline_succeeds
|
||||
else
|
||||
# Only MRs that can be merged end in this action
|
||||
# MR can be already picked up for merge / merged already or can be waiting for worker to be picked up
|
||||
|
@ -444,6 +447,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
|
|||
|
||||
def ci_status
|
||||
pipeline = @merge_request.head_pipeline
|
||||
@pipelines = @merge_request.all_pipelines
|
||||
|
||||
if pipeline
|
||||
status = pipeline.status
|
||||
|
@ -462,7 +466,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController
|
|||
sha: (merge_request.diff_head_commit.short_id if merge_request.diff_head_sha),
|
||||
status: status,
|
||||
coverage: coverage,
|
||||
pipeline: pipeline.try(:id)
|
||||
pipeline: pipeline.try(:id),
|
||||
has_ci: @merge_request.has_ci?
|
||||
}
|
||||
|
||||
render json: response
|
||||
|
@ -672,8 +677,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController
|
|||
@merge_request.ensure_ref_fetched
|
||||
end
|
||||
|
||||
def merge_when_build_succeeds_active?
|
||||
params[:merge_when_build_succeeds].present? &&
|
||||
def merge_when_pipeline_succeeds_active?
|
||||
params[:merge_when_pipeline_succeeds].present? &&
|
||||
@merge_request.head_pipeline && @merge_request.head_pipeline.active?
|
||||
end
|
||||
|
||||
|
|
|
@ -148,17 +148,10 @@ class Projects::NotesController < Projects::ApplicationController
|
|||
|
||||
def note_json(note)
|
||||
attrs = {
|
||||
award: false,
|
||||
id: note.id
|
||||
}
|
||||
|
||||
if note.is_a?(AwardEmoji)
|
||||
attrs.merge!(
|
||||
valid: note.valid?,
|
||||
award: true,
|
||||
name: note.name
|
||||
)
|
||||
elsif note.persisted?
|
||||
if note.persisted?
|
||||
Banzai::NoteRenderer.render([note], @project, current_user)
|
||||
|
||||
attrs.merge!(
|
||||
|
@ -198,7 +191,7 @@ class Projects::NotesController < Projects::ApplicationController
|
|||
)
|
||||
end
|
||||
|
||||
attrs[:commands_changes] = note.commands_changes unless attrs[:award]
|
||||
attrs[:commands_changes] = note.commands_changes
|
||||
attrs
|
||||
end
|
||||
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
class Projects::PipelinesController < Projects::ApplicationController
|
||||
before_action :pipeline, except: [:index, :new, :create]
|
||||
before_action :pipeline, except: [:index, :new, :create, :charts]
|
||||
before_action :commit, only: [:show, :builds]
|
||||
before_action :authorize_read_pipeline!
|
||||
before_action :authorize_create_pipeline!, only: [:new, :create]
|
||||
before_action :authorize_update_pipeline!, only: [:retry, :cancel]
|
||||
before_action :builds_enabled, only: :charts
|
||||
|
||||
def index
|
||||
@scope = params[:scope]
|
||||
|
@ -92,6 +93,14 @@ class Projects::PipelinesController < Projects::ApplicationController
|
|||
redirect_back_or_default default: namespace_project_pipelines_path(project.namespace, project)
|
||||
end
|
||||
|
||||
def charts
|
||||
@charts = {}
|
||||
@charts[:week] = Ci::Charts::WeekChart.new(project)
|
||||
@charts[:month] = Ci::Charts::MonthChart.new(project)
|
||||
@charts[:year] = Ci::Charts::YearChart.new(project)
|
||||
@charts[:build_times] = Ci::Charts::BuildTime.new(project)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def create_params
|
||||
|
|
|
@ -314,7 +314,7 @@ class ProjectsController < Projects::ApplicationController
|
|||
:name,
|
||||
:namespace_id,
|
||||
:only_allow_merge_if_all_discussions_are_resolved,
|
||||
:only_allow_merge_if_build_succeeds,
|
||||
:only_allow_merge_if_pipeline_succeeds,
|
||||
:path,
|
||||
:public_builds,
|
||||
:request_access_enabled,
|
||||
|
|
|
@ -8,7 +8,9 @@
|
|||
# `DashboardController#show`, which is the default.
|
||||
class RootController < Dashboard::ProjectsController
|
||||
skip_before_action :authenticate_user!, only: [:index]
|
||||
before_action :redirect_to_custom_dashboard, only: [:index]
|
||||
|
||||
before_action :redirect_unlogged_user, if: -> { current_user.nil? }
|
||||
before_action :redirect_logged_user, if: -> { current_user.present? }
|
||||
|
||||
def index
|
||||
super
|
||||
|
@ -16,23 +18,38 @@ class RootController < Dashboard::ProjectsController
|
|||
|
||||
private
|
||||
|
||||
def redirect_to_custom_dashboard
|
||||
return redirect_to new_user_session_path unless current_user
|
||||
def redirect_unlogged_user
|
||||
if redirect_to_home_page_url?
|
||||
redirect_to(current_application_settings.home_page_url)
|
||||
else
|
||||
redirect_to(new_user_session_path)
|
||||
end
|
||||
end
|
||||
|
||||
def redirect_logged_user
|
||||
case current_user.dashboard
|
||||
when 'stars'
|
||||
flash.keep
|
||||
redirect_to starred_dashboard_projects_path
|
||||
redirect_to(starred_dashboard_projects_path)
|
||||
when 'project_activity'
|
||||
redirect_to activity_dashboard_path
|
||||
redirect_to(activity_dashboard_path)
|
||||
when 'starred_project_activity'
|
||||
redirect_to activity_dashboard_path(filter: 'starred')
|
||||
redirect_to(activity_dashboard_path(filter: 'starred'))
|
||||
when 'groups'
|
||||
redirect_to dashboard_groups_path
|
||||
redirect_to(dashboard_groups_path)
|
||||
when 'todos'
|
||||
redirect_to dashboard_todos_path
|
||||
else
|
||||
return
|
||||
redirect_to(dashboard_todos_path)
|
||||
end
|
||||
end
|
||||
|
||||
def redirect_to_home_page_url?
|
||||
# If user is not signed-in and tries to access root_path - redirect him to landing page
|
||||
# Don't redirect to the default URL to prevent endless redirections
|
||||
return false unless current_application_settings.home_page_url.present?
|
||||
|
||||
home_page_url = current_application_settings.home_page_url.chomp('/')
|
||||
root_urls = [Gitlab.config.gitlab['url'].chomp('/'), root_url.chomp('/')]
|
||||
|
||||
root_urls.exclude?(home_page_url)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -19,7 +19,7 @@ module ButtonHelper
|
|||
title = data[:title] || 'Copy to clipboard'
|
||||
data = { toggle: 'tooltip', placement: 'bottom', container: 'body' }.merge(data)
|
||||
content_tag :button,
|
||||
icon('clipboard'),
|
||||
icon('clipboard', 'aria-hidden': 'true'),
|
||||
class: "btn #{css_class}",
|
||||
data: data,
|
||||
type: :button,
|
||||
|
|
|
@ -9,12 +9,20 @@ module ExploreHelper
|
|||
}
|
||||
|
||||
options = exist_opts.merge(options)
|
||||
path = request.path
|
||||
path << "?#{options.to_param}"
|
||||
path
|
||||
request_path_with_options(options)
|
||||
end
|
||||
|
||||
def filter_groups_path(options = {})
|
||||
request_path_with_options(options)
|
||||
end
|
||||
|
||||
def explore_controller?
|
||||
controller.class.name.split("::").first == "Explore"
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def request_path_with_options(options = {})
|
||||
request.path + "?#{options.to_param}"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
module IssuablesHelper
|
||||
def sidebar_gutter_toggle_icon
|
||||
sidebar_gutter_collapsed? ? icon('angle-double-left') : icon('angle-double-right')
|
||||
sidebar_gutter_collapsed? ? icon('angle-double-left', { 'aria-hidden': 'true' }) : icon('angle-double-right', { 'aria-hidden': 'true' })
|
||||
end
|
||||
|
||||
def sidebar_gutter_collapsed_class
|
||||
|
|
|
@ -146,7 +146,7 @@ module MergeRequestsHelper
|
|||
|
||||
def merge_params(merge_request)
|
||||
{
|
||||
merge_when_build_succeeds: true,
|
||||
merge_when_pipeline_succeeds: true,
|
||||
should_remove_source_branch: true,
|
||||
sha: merge_request.diff_head_sha
|
||||
}.merge(merge_params_ee(merge_request))
|
||||
|
|
|
@ -97,7 +97,7 @@ module MilestonesHelper
|
|||
|
||||
def milestone_date_range(milestone)
|
||||
if milestone.start_date && milestone.due_date
|
||||
"#{milestone.start_date.to_s(:medium)} - #{milestone.due_date.to_s(:medium)}"
|
||||
"#{milestone.start_date.to_s(:medium)}–#{milestone.due_date.to_s(:medium)}"
|
||||
elsif milestone.due_date
|
||||
if milestone.due_date.past?
|
||||
"expired on #{milestone.due_date.to_s(:medium)}"
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
module RssHelper
|
||||
def rss_url_options
|
||||
{ format: :atom, private_token: current_user.try(:private_token) }
|
||||
end
|
||||
end
|
|
@ -179,6 +179,7 @@ class ApplicationSetting < ActiveRecord::Base
|
|||
default_project_visibility: Settings.gitlab.default_projects_features['visibility_level'],
|
||||
default_projects_limit: Settings.gitlab['default_projects_limit'],
|
||||
default_snippet_visibility: Settings.gitlab.default_projects_features['visibility_level'],
|
||||
default_group_visibility: Settings.gitlab.default_projects_features['visibility_level'],
|
||||
disabled_oauth_sign_in_sources: [],
|
||||
domain_whitelist: Settings.gitlab['domain_whitelist'],
|
||||
gravatar_enabled: Settings.gravatar['enabled'],
|
||||
|
@ -277,6 +278,22 @@ class ApplicationSetting < ActiveRecord::Base
|
|||
self.repository_storages = [value]
|
||||
end
|
||||
|
||||
def default_project_visibility=(level)
|
||||
super(Gitlab::VisibilityLevel.level_value(level))
|
||||
end
|
||||
|
||||
def default_snippet_visibility=(level)
|
||||
super(Gitlab::VisibilityLevel.level_value(level))
|
||||
end
|
||||
|
||||
def default_group_visibility=(level)
|
||||
super(Gitlab::VisibilityLevel.level_value(level))
|
||||
end
|
||||
|
||||
def restricted_visibility_levels=(levels)
|
||||
super(levels.map { |level| Gitlab::VisibilityLevel.level_value(level) })
|
||||
end
|
||||
|
||||
# Choose one of the available repository storage options. Currently all have
|
||||
# equal weighting.
|
||||
def pick_repository_storage
|
||||
|
|
|
@ -55,15 +55,6 @@ module Ci
|
|||
pending.unstarted.order('created_at ASC').first
|
||||
end
|
||||
|
||||
def create_from(build)
|
||||
new_build = build.dup
|
||||
new_build.status = 'pending'
|
||||
new_build.runner_id = nil
|
||||
new_build.trigger_request_id = nil
|
||||
new_build.token = nil
|
||||
new_build.save
|
||||
end
|
||||
|
||||
def retry(build, current_user)
|
||||
Ci::RetryBuildService
|
||||
.new(build.project, current_user)
|
||||
|
|
|
@ -93,7 +93,7 @@ class Group < Namespace
|
|||
end
|
||||
|
||||
def visibility_level_field
|
||||
visibility_level
|
||||
:visibility_level
|
||||
end
|
||||
|
||||
def visibility_level_allowed_by_projects
|
||||
|
|
|
@ -97,7 +97,7 @@ class MergeRequest < ActiveRecord::Base
|
|||
validates :source_branch, presence: true
|
||||
validates :target_project, presence: true
|
||||
validates :target_branch, presence: true
|
||||
validates :merge_user, presence: true, if: :merge_when_build_succeeds?, unless: :importing?
|
||||
validates :merge_user, presence: true, if: :merge_when_pipeline_succeeds?, unless: :importing?
|
||||
validate :validate_branches, unless: [:allow_broken, :importing?, :closed_without_fork?]
|
||||
validate :validate_fork, unless: :closed_without_fork?
|
||||
|
||||
|
@ -436,7 +436,7 @@ class MergeRequest < ActiveRecord::Base
|
|||
true
|
||||
end
|
||||
|
||||
def can_cancel_merge_when_build_succeeds?(current_user)
|
||||
def can_cancel_merge_when_pipeline_succeeds?(current_user)
|
||||
can_be_merged_by?(current_user) || self.author == current_user
|
||||
end
|
||||
|
||||
|
@ -644,10 +644,10 @@ class MergeRequest < ActiveRecord::Base
|
|||
message.join("\n\n")
|
||||
end
|
||||
|
||||
def reset_merge_when_build_succeeds
|
||||
return unless merge_when_build_succeeds?
|
||||
def reset_merge_when_pipeline_succeeds
|
||||
return unless merge_when_pipeline_succeeds?
|
||||
|
||||
self.merge_when_build_succeeds = false
|
||||
self.merge_when_pipeline_succeeds = false
|
||||
self.merge_user = nil
|
||||
if merge_params
|
||||
merge_params.delete('should_remove_source_branch')
|
||||
|
@ -684,7 +684,10 @@ class MergeRequest < ActiveRecord::Base
|
|||
end
|
||||
|
||||
def has_ci?
|
||||
source_project.try(:ci_service) && commits.any?
|
||||
has_ci_integration = source_project.try(:ci_service)
|
||||
uses_gitlab_ci = all_pipelines.any?
|
||||
|
||||
(has_ci_integration || uses_gitlab_ci) && commits.any?
|
||||
end
|
||||
|
||||
def branch_missing?
|
||||
|
@ -706,7 +709,7 @@ class MergeRequest < ActiveRecord::Base
|
|||
end
|
||||
|
||||
def mergeable_ci_state?
|
||||
return true unless project.only_allow_merge_if_build_succeeds?
|
||||
return true unless project.only_allow_merge_if_pipeline_succeeds?
|
||||
|
||||
!head_pipeline || head_pipeline.success? || head_pipeline.skipped?
|
||||
end
|
||||
|
|
|
@ -231,10 +231,6 @@ class Note < ActiveRecord::Base
|
|||
note =~ /\A#{Banzai::Filter::EmojiFilter.emoji_pattern}\s?\Z/
|
||||
end
|
||||
|
||||
def award_emoji_name
|
||||
note.match(Banzai::Filter::EmojiFilter.emoji_pattern)[1]
|
||||
end
|
||||
|
||||
def to_ability_name
|
||||
for_personal_snippet? ? 'personal_snippet' : noteable_type.underscore
|
||||
end
|
||||
|
|
|
@ -19,7 +19,7 @@ class Project < ActiveRecord::Base
|
|||
|
||||
extend Gitlab::ConfigHelper
|
||||
|
||||
class BoardLimitExceeded < StandardError; end
|
||||
BoardLimitExceeded = Class.new(StandardError)
|
||||
|
||||
NUMBER_OF_PERMITTED_BOARDS = 1
|
||||
UNKNOWN_IMPORT_URL = 'http://unknown.git'.freeze
|
||||
|
@ -113,6 +113,7 @@ class Project < ActiveRecord::Base
|
|||
has_one :gitlab_issue_tracker_service, dependent: :destroy, inverse_of: :project
|
||||
has_one :external_wiki_service, dependent: :destroy
|
||||
has_one :kubernetes_service, dependent: :destroy, inverse_of: :project
|
||||
has_one :mock_ci_service, dependent: :destroy
|
||||
|
||||
has_one :forked_project_link, dependent: :destroy, foreign_key: "forked_to_project_id"
|
||||
has_one :forked_from_project, through: :forked_project_link
|
||||
|
@ -334,7 +335,7 @@ class Project < ActiveRecord::Base
|
|||
end
|
||||
|
||||
def search_by_visibility(level)
|
||||
where(visibility_level: Gitlab::VisibilityLevel.const_get(level.upcase))
|
||||
where(visibility_level: Gitlab::VisibilityLevel.string_options[level])
|
||||
end
|
||||
|
||||
def search_by_title(query)
|
||||
|
@ -1003,7 +1004,7 @@ class Project < ActiveRecord::Base
|
|||
end
|
||||
|
||||
def visibility_level_field
|
||||
visibility_level
|
||||
:visibility_level
|
||||
end
|
||||
|
||||
def archive!
|
||||
|
|
|
@ -7,7 +7,7 @@ class ProjectWiki
|
|||
'AsciiDoc' => :asciidoc
|
||||
}.freeze unless defined?(MARKUPS)
|
||||
|
||||
class CouldNotCreateWikiError < StandardError; end
|
||||
CouldNotCreateWikiError = Class.new(StandardError)
|
||||
|
||||
# Returns a string describing what went wrong after
|
||||
# an operation fails.
|
||||
|
|
|
@ -6,6 +6,7 @@ class Repository
|
|||
attr_accessor :path_with_namespace, :project
|
||||
|
||||
CommitError = Class.new(StandardError)
|
||||
CreateTreeError = Class.new(StandardError)
|
||||
|
||||
# Methods that cache data from the Git repository.
|
||||
#
|
||||
|
@ -862,17 +863,18 @@ class Repository
|
|||
end
|
||||
|
||||
def revert(
|
||||
user, commit, branch_name, revert_tree_id = nil,
|
||||
user, commit, branch_name,
|
||||
start_branch_name: nil, start_project: project)
|
||||
revert_tree_id ||= check_revert_content(commit, branch_name)
|
||||
|
||||
return false unless revert_tree_id
|
||||
|
||||
GitOperationService.new(user, self).with_branch(
|
||||
branch_name,
|
||||
start_branch_name: start_branch_name,
|
||||
start_project: start_project) do |start_commit|
|
||||
|
||||
revert_tree_id = check_revert_content(commit, start_commit.sha)
|
||||
unless revert_tree_id
|
||||
raise Repository::CreateTreeError.new('Failed to revert commit')
|
||||
end
|
||||
|
||||
committer = user_to_committer(user)
|
||||
|
||||
Rugged::Commit.create(rugged,
|
||||
|
@ -885,17 +887,18 @@ class Repository
|
|||
end
|
||||
|
||||
def cherry_pick(
|
||||
user, commit, branch_name, cherry_pick_tree_id = nil,
|
||||
user, commit, branch_name,
|
||||
start_branch_name: nil, start_project: project)
|
||||
cherry_pick_tree_id ||= check_cherry_pick_content(commit, branch_name)
|
||||
|
||||
return false unless cherry_pick_tree_id
|
||||
|
||||
GitOperationService.new(user, self).with_branch(
|
||||
branch_name,
|
||||
start_branch_name: start_branch_name,
|
||||
start_project: start_project) do |start_commit|
|
||||
|
||||
cherry_pick_tree_id = check_cherry_pick_content(commit, start_commit.sha)
|
||||
unless cherry_pick_tree_id
|
||||
raise Repository::CreateTreeError.new('Failed to cherry-pick commit')
|
||||
end
|
||||
|
||||
committer = user_to_committer(user)
|
||||
|
||||
Rugged::Commit.create(rugged,
|
||||
|
@ -919,9 +922,8 @@ class Repository
|
|||
end
|
||||
end
|
||||
|
||||
def check_revert_content(target_commit, branch_name)
|
||||
source_sha = commit(branch_name).sha
|
||||
args = [target_commit.sha, source_sha]
|
||||
def check_revert_content(target_commit, source_sha)
|
||||
args = [target_commit.sha, source_sha]
|
||||
args << { mainline: 1 } if target_commit.merge_commit?
|
||||
|
||||
revert_index = rugged.revert_commit(*args)
|
||||
|
@ -933,9 +935,8 @@ class Repository
|
|||
tree_id
|
||||
end
|
||||
|
||||
def check_cherry_pick_content(target_commit, branch_name)
|
||||
source_sha = commit(branch_name).sha
|
||||
args = [target_commit.sha, source_sha]
|
||||
def check_cherry_pick_content(target_commit, source_sha)
|
||||
args = [target_commit.sha, source_sha]
|
||||
args << 1 if target_commit.merge_commit?
|
||||
|
||||
cherry_pick_index = rugged.cherrypick_commit(*args)
|
||||
|
@ -995,6 +996,8 @@ class Repository
|
|||
end
|
||||
|
||||
def with_repo_branch_commit(start_repository, start_branch_name)
|
||||
return yield(nil) if start_repository.empty_repo?
|
||||
|
||||
branch_name_or_sha =
|
||||
if start_repository == self
|
||||
start_branch_name
|
||||
|
|
|
@ -120,7 +120,7 @@ class Snippet < ActiveRecord::Base
|
|||
end
|
||||
|
||||
def visibility_level_field
|
||||
visibility_level
|
||||
:visibility_level
|
||||
end
|
||||
|
||||
def no_highlighting?
|
||||
|
|
|
@ -346,7 +346,11 @@ class User < ActiveRecord::Base
|
|||
# Return (create if necessary) the ghost user. The ghost user
|
||||
# owns records previously belonging to deleted users.
|
||||
def ghost
|
||||
User.find_by_ghost(true) || create_ghost_user
|
||||
unique_internal(where(ghost: true), 'ghost', 'ghost%s@example.com') do |u|
|
||||
u.bio = 'This is a "Ghost User", created to hold all issues authored by users that have since been deleted. This user cannot be removed.'
|
||||
u.state = :blocked
|
||||
u.name = 'Ghost User'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -1017,10 +1021,14 @@ class User < ActiveRecord::Base
|
|||
end
|
||||
end
|
||||
|
||||
def self.create_ghost_user
|
||||
# Since we only want a single ghost user in an instance, we use an
|
||||
def self.unique_internal(scope, username, email_pattern, &b)
|
||||
scope.first || create_unique_internal(scope, username, email_pattern, &b)
|
||||
end
|
||||
|
||||
def self.create_unique_internal(scope, username, email_pattern, &creation_block)
|
||||
# Since we only want a single one of these in an instance, we use an
|
||||
# exclusive lease to ensure than this block is never run concurrently.
|
||||
lease_key = "ghost_user_creation"
|
||||
lease_key = "user:unique_internal:#{username}"
|
||||
lease = Gitlab::ExclusiveLease.new(lease_key, timeout: 1.minute.to_i)
|
||||
|
||||
until uuid = lease.try_obtain
|
||||
|
@ -1029,25 +1037,25 @@ class User < ActiveRecord::Base
|
|||
sleep(1)
|
||||
end
|
||||
|
||||
# Recheck if a ghost user is already present. One might have been
|
||||
# Recheck if the user is already present. One might have been
|
||||
# added between the time we last checked (first line of this method)
|
||||
# and the time we acquired the lock.
|
||||
ghost_user = User.find_by_ghost(true)
|
||||
return ghost_user if ghost_user.present?
|
||||
existing_user = uncached { scope.first }
|
||||
return existing_user if existing_user.present?
|
||||
|
||||
uniquify = Uniquify.new
|
||||
|
||||
username = uniquify.string("ghost") { |s| User.find_by_username(s) }
|
||||
username = uniquify.string(username) { |s| User.find_by_username(s) }
|
||||
|
||||
email = uniquify.string(-> (n) { "ghost#{n}@example.com" }) do |s|
|
||||
email = uniquify.string(-> (n) { Kernel.sprintf(email_pattern, n) }) do |s|
|
||||
User.find_by_email(s)
|
||||
end
|
||||
|
||||
bio = 'This is a "Ghost User", created to hold all issues authored by users that have since been deleted. This user cannot be removed.'
|
||||
|
||||
User.create(
|
||||
username: username, password: Devise.friendly_token, bio: bio,
|
||||
email: email, name: "Ghost User", state: :blocked, ghost: true
|
||||
scope.create(
|
||||
username: username,
|
||||
password: Devise.friendly_token,
|
||||
email: email,
|
||||
&creation_block
|
||||
)
|
||||
ensure
|
||||
Gitlab::ExclusiveLease.cancel(lease_key, uuid)
|
||||
|
|
|
@ -33,8 +33,6 @@ class GroupPolicy < BasePolicy
|
|||
if globally_viewable && @subject.request_access_enabled && !member
|
||||
can! :request_access
|
||||
end
|
||||
|
||||
additional_rules!(master)
|
||||
end
|
||||
|
||||
def can_read_group?
|
||||
|
@ -45,8 +43,4 @@ class GroupPolicy < BasePolicy
|
|||
|
||||
GroupProjectsFinder.new(@subject).execute(@user).any?
|
||||
end
|
||||
|
||||
def additional_rules!(master)
|
||||
# This is meant to be overriden in EE
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6,7 +6,7 @@ class MergeRequestEntity < IssuableEntity
|
|||
expose :merge_params
|
||||
expose :merge_status
|
||||
expose :merge_user_id
|
||||
expose :merge_when_build_succeeds
|
||||
expose :merge_when_pipeline_succeeds
|
||||
expose :source_branch
|
||||
expose :source_project_id
|
||||
expose :target_branch
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue