Merge branch 'master' into zj-create-mattermost-team

This commit is contained in:
Z.J. van de Weg 2017-03-02 10:22:56 +01:00
commit ddfd09c09c
641 changed files with 9722 additions and 5572 deletions

View File

@ -23,6 +23,7 @@ AllCops:
- 'tmp/**/*'
- 'bin/**/*'
- 'generator_templates/**/*'
- 'builds/**/*'
# Gems in consecutive lines should be alphabetically sorted
Bundler/OrderedGems:

View File

@ -2,6 +2,25 @@
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)
- Disable unused tags count cache for Projects, Builds and Runners.
- Spam check and reCAPTCHA improvements.
- Allow searching issues for strings containing colons.
- Disabled tooltip on add issues button in usse boards.
- Fixed commit search UI.
- Fix MR changes tab size count when there are over 100 files in the diff.
- Disable invalid service templates.
- Use default branch as target_branch when parameter is missing.
- Upgrade GitLab Pages to v0.3.2.
- Add performance query regression fix for !9088 affecting #27267.
- Chat slash commands show labels correctly.
## 8.17.0 (2017-02-22)
- API: Fix file downloading. !0 (8267)
@ -182,6 +201,12 @@ entry.
- Remove deprecated GitlabCiService.
- Requeue pending deletion projects.
## 8.16.7 (2017-02-27)
- No changes.
- No changes.
- Fix MR changes tab size count when there are over 100 files in the diff.
## 8.16.6 (2017-02-17)
- API: Fix file downloading. !0 (8267)

View File

@ -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

View File

@ -20,7 +20,7 @@ gem 'rugged', '~> 0.24.0'
# Authentication libraries
gem 'devise', '~> 4.2'
gem 'doorkeeper', '~> 4.2.0'
gem 'omniauth', '~> 1.3.2'
gem 'omniauth', '~> 1.4.2'
gem 'omniauth-auth0', '~> 1.4.1'
gem 'omniauth-azure-oauth2', '~> 0.0.6'
gem 'omniauth-cas3', '~> 1.1.2'
@ -68,7 +68,7 @@ gem 'gollum-rugged_adapter', '~> 0.4.2', require: false
gem 'github-linguist', '~> 4.7.0', require: 'linguist'
# API
gem 'grape', '~> 0.18.0'
gem 'grape', '~> 0.19.0'
gem 'grape-entity', '~> 0.6.0'
gem 'rack-cors', '~> 0.4.0', require: 'rack/cors'
@ -329,8 +329,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 +350,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'

View File

@ -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)
@ -304,7 +308,7 @@ GEM
multi_json (~> 1.11)
os (~> 0.9)
signet (~> 0.7)
grape (0.18.0)
grape (0.19.1)
activesupport
builder
hashie (>= 2.1.0)
@ -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)
@ -328,7 +335,7 @@ GEM
temple (~> 0.7.6)
thor
tilt
hashie (3.4.4)
hashie (3.5.5)
health_check (2.2.1)
rails (>= 4.0)
hipchat (1.5.2)
@ -353,8 +360,8 @@ GEM
json (~> 1.8)
multi_xml (>= 0.5.2)
httpclient (2.8.2)
i18n (0.8.0)
ice_nine (0.11.1)
i18n (0.8.1)
ice_nine (0.11.2)
influxdb (0.2.3)
cause
json
@ -417,7 +424,7 @@ GEM
minitest (5.7.0)
mousetrap-rails (1.4.6)
multi_json (1.12.1)
multi_xml (0.5.5)
multi_xml (0.6.0)
multipart-post (2.0.0)
mustermann (0.4.0)
tool (~> 0.2)
@ -427,7 +434,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)
@ -441,7 +447,7 @@ GEM
octokit (4.6.2)
sawyer (~> 0.8.0, >= 0.5.3)
oj (2.17.4)
omniauth (1.3.2)
omniauth (1.4.2)
hashie (>= 1.2, < 4)
rack (>= 1.0, < 3)
omniauth-auth0 (1.4.1)
@ -758,7 +764,7 @@ GEM
eventmachine (~> 1.0, >= 1.0.4)
rack (>= 1, < 3)
thor (0.19.4)
thread_safe (0.3.5)
thread_safe (0.3.6)
tilt (2.0.6)
timecop (0.8.1)
timfel-krb5-auth (0.8.3)
@ -878,6 +884,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)
@ -886,7 +893,7 @@ DEPENDENCIES
gollum-rugged_adapter (~> 0.4.2)
gon (~> 6.1.0)
google-api-client (~> 0.8.6)
grape (~> 0.18.0)
grape (~> 0.19.0)
grape-entity (~> 0.6.0)
haml_lint (~> 0.21.0)
hamlit (~> 2.6.1)
@ -915,12 +922,11 @@ 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)
oj (~> 2.17.4)
omniauth (~> 1.3.2)
omniauth (~> 1.4.2)
omniauth-auth0 (~> 1.4.1)
omniauth-authentiq (~> 0.3.0)
omniauth-azure-oauth2 (~> 0.0.6)
@ -1011,4 +1017,4 @@ DEPENDENCIES
wikicloth (= 0.8.1)
BUNDLED WITH
1.14.3
1.14.4

View File

@ -6,13 +6,7 @@
/* global AwardsHandler */
/* global Aside */
function requireAll(context) { return context.keys().map(context); }
window.$ = window.jQuery = require('jquery');
require('jquery-ui/ui/autocomplete');
require('jquery-ui/ui/draggable');
require('jquery-ui/ui/effect-highlight');
require('jquery-ui/ui/sortable');
require('jquery-ujs');
require('vendor/jquery.endless-scroll');
require('vendor/jquery.highlight');
@ -46,15 +40,176 @@ require('./shortcuts_dashboard_navigation');
require('./shortcuts_issuable');
require('./shortcuts_network');
require('vendor/jquery.nicescroll');
requireAll(require.context('./behaviors', false, /^\.\/.*\.(js|es6)$/));
requireAll(require.context('./blob', false, /^\.\/.*\.(js|es6)$/));
requireAll(require.context('./templates', false, /^\.\/.*\.(js|es6)$/));
requireAll(require.context('./commit', false, /^\.\/.*\.(js|es6)$/));
requireAll(require.context('./extensions', false, /^\.\/.*\.(js|es6)$/));
requireAll(require.context('./lib/utils', false, /^\.\/.*\.(js|es6)$/));
requireAll(require.context('./u2f', false, /^\.\/.*\.(js|es6)$/));
requireAll(require.context('./droplab', false, /^\.\/.*\.(js|es6)$/));
requireAll(require.context('.', false, /^\.\/(?!application\.js).*\.(js|es6)$/));
// behaviors
require('./behaviors/autosize');
require('./behaviors/details_behavior');
require('./behaviors/quick_submit');
require('./behaviors/requires_input');
require('./behaviors/toggler_behavior');
// blob
require('./blob/blob_ci_yaml');
require('./blob/blob_dockerfile_selector');
require('./blob/blob_dockerfile_selectors');
require('./blob/blob_file_dropzone');
require('./blob/blob_gitignore_selector');
require('./blob/blob_gitignore_selectors');
require('./blob/blob_license_selector');
require('./blob/blob_license_selectors');
require('./blob/template_selector');
// templates
require('./templates/issuable_template_selector');
require('./templates/issuable_template_selectors');
// commit
require('./commit/file.js');
require('./commit/image_file.js');
// extensions
require('./extensions/array');
require('./extensions/custom_event');
require('./extensions/element');
require('./extensions/jquery');
require('./extensions/object');
// lib/utils
require('./lib/utils/animate');
require('./lib/utils/bootstrap_linked_tabs');
require('./lib/utils/common_utils');
require('./lib/utils/datetime_utility');
require('./lib/utils/notify');
require('./lib/utils/pretty_time');
require('./lib/utils/text_utility');
require('./lib/utils/type_utility');
require('./lib/utils/url_utility');
// u2f
require('./u2f/authenticate');
require('./u2f/error');
require('./u2f/register');
require('./u2f/util');
// droplab
require('./droplab/droplab');
require('./droplab/droplab_ajax');
require('./droplab/droplab_ajax_filter');
require('./droplab/droplab_filter');
// everything else
require('./abuse_reports');
require('./activities');
require('./admin');
require('./ajax_loading_spinner');
require('./api');
require('./aside');
require('./autosave');
require('./awards_handler');
require('./breakpoints');
require('./broadcast_message');
require('./build');
require('./build_artifacts');
require('./build_variables');
require('./ci_lint_editor');
require('./commit');
require('./commits');
require('./compare');
require('./compare_autocomplete');
require('./confirm_danger_modal');
require('./copy_as_gfm');
require('./copy_to_clipboard');
require('./create_label');
require('./diff');
require('./dispatcher');
require('./dropzone_input');
require('./due_date_select');
require('./files_comment_button');
require('./flash');
require('./gfm_auto_complete');
require('./gl_dropdown');
require('./gl_field_error');
require('./gl_field_errors');
require('./gl_form');
require('./group_avatar');
require('./group_label_subscription');
require('./groups_select');
require('./header');
require('./importer_status');
require('./issuable');
require('./issuable_context');
require('./issuable_form');
require('./issue');
require('./issue_status_select');
require('./issues_bulk_assignment');
require('./label_manager');
require('./labels');
require('./labels_select');
require('./layout_nav');
require('./line_highlighter');
require('./logo');
require('./member_expiration_date');
require('./members');
require('./merge_request');
require('./merge_request_tabs');
require('./merge_request_widget');
require('./merged_buttons');
require('./milestone');
require('./milestone_select');
require('./mini_pipeline_graph_dropdown');
require('./namespace_select');
require('./new_branch_form');
require('./new_commit_form');
require('./notes');
require('./notifications_dropdown');
require('./notifications_form');
require('./pager');
require('./pipelines');
require('./preview_markdown');
require('./project');
require('./project_avatar');
require('./project_find_file');
require('./project_fork');
require('./project_import');
require('./project_label_subscription');
require('./project_new');
require('./project_select');
require('./project_show');
require('./project_variables');
require('./projects_list');
require('./render_gfm');
require('./render_math');
require('./right_sidebar');
require('./search');
require('./search_autocomplete');
require('./shortcuts');
require('./shortcuts_blob');
require('./shortcuts_dashboard_navigation');
require('./shortcuts_find_file');
require('./shortcuts_issuable');
require('./shortcuts_navigation');
require('./shortcuts_network');
require('./signin_tabs_memoizer');
require('./single_file_diff');
require('./smart_interval');
require('./snippets_list');
require('./star');
require('./subbable_resource');
require('./subscription');
require('./subscription_select');
require('./syntax_highlight');
require('./task_list');
require('./todos');
require('./tree');
require('./user');
require('./user_tabs');
require('./username_validator');
require('./users_select');
require('./version_check_image');
require('./visibility_select');
require('./wikis');
require('./zen_mode');
require('vendor/fuzzaldrin-plus');
require('es6-promise').polyfill();

View File

@ -0,0 +1,69 @@
/* global Vue */
require('./issue_card_inner');
const Store = gl.issueBoards.BoardsStore;
export default {
name: 'BoardsIssueCard',
template: `
<li class="card"
:class="{ 'user-can-drag': !disabled && issue.id, 'is-disabled': disabled || !issue.id, 'is-active': issueDetailVisible }"
:index="index"
:data-issue-id="issue.id"
@mousedown="mouseDown"
@mousemove="mouseMove"
@mouseup="showIssue($event)">
<issue-card-inner
:list="list"
:issue="issue"
:issue-link-base="issueLinkBase"
:root-path="rootPath" />
</li>
`,
components: {
'issue-card-inner': gl.issueBoards.IssueCardInner,
},
props: {
list: Object,
issue: Object,
issueLinkBase: String,
disabled: Boolean,
index: Number,
rootPath: String,
},
data() {
return {
showDetail: false,
detailIssue: Store.detail,
};
},
computed: {
issueDetailVisible() {
return this.detailIssue.issue && this.detailIssue.issue.id === this.issue.id;
},
},
methods: {
mouseDown() {
this.showDetail = true;
},
mouseMove() {
this.showDetail = false;
},
showIssue(e) {
const targetTagName = e.target.tagName.toLowerCase();
if (targetTagName === 'a' || targetTagName === 'button') return;
if (this.showDetail) {
this.showDetail = false;
if (Store.detail.issue && Store.detail.issue.id === this.issue.id) {
Store.detail.issue = {};
} else {
Store.detail.issue = this.issue;
Store.detail.list = this.list;
}
}
},
},
};

View File

@ -1,61 +0,0 @@
/* eslint-disable comma-dangle, space-before-function-paren, dot-notation */
/* global Vue */
require('./issue_card_inner');
(() => {
const Store = gl.issueBoards.BoardsStore;
window.gl = window.gl || {};
window.gl.issueBoards = window.gl.issueBoards || {};
gl.issueBoards.BoardCard = Vue.extend({
template: '#js-board-list-card',
components: {
'issue-card-inner': gl.issueBoards.IssueCardInner,
},
props: {
list: Object,
issue: Object,
issueLinkBase: String,
disabled: Boolean,
index: Number,
rootPath: String,
},
data () {
return {
showDetail: false,
detailIssue: Store.detail
};
},
computed: {
issueDetailVisible () {
return this.detailIssue.issue && this.detailIssue.issue.id === this.issue.id;
}
},
methods: {
mouseDown () {
this.showDetail = true;
},
mouseMove() {
this.showDetail = false;
},
showIssue (e) {
const targetTagName = e.target.tagName.toLowerCase();
if (targetTagName === 'a' || targetTagName === 'button') return;
if (this.showDetail) {
this.showDetail = false;
if (Store.detail.issue && Store.detail.issue.id === this.issue.id) {
Store.detail.issue = {};
} else {
Store.detail.issue = this.issue;
Store.detail.list = this.list;
}
}
}
}
});
})();

View File

@ -2,8 +2,8 @@
/* global Vue */
/* global Sortable */
require('./board_card');
require('./board_new_issue');
import boardNewIssue from './board_new_issue';
import boardCard from './board_card';
(() => {
const Store = gl.issueBoards.BoardsStore;
@ -14,8 +14,8 @@ require('./board_new_issue');
gl.issueBoards.BoardList = Vue.extend({
template: '#js-board-list-template',
components: {
'board-card': gl.issueBoards.BoardCard,
'board-new-issue': gl.issueBoards.BoardNewIssue
boardCard,
boardNewIssue,
},
props: {
disabled: Boolean,
@ -81,6 +81,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({
@ -115,6 +121,9 @@ require('./board_new_issue');
this.loadNextPage();
}
};
}
},
beforeDestroy() {
gl.IssueBoardsApp.$off(`hide-issue-form-${this.list.id}`, this.toggleForm);
},
});
})();

View File

@ -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>
`,
};

View File

@ -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();
},
});
})();

View File

@ -123,14 +123,18 @@ class List {
if (listFrom) {
this.issuesSize += 1;
gl.boardService.moveIssue(issue.id, listFrom.id, this.id)
.then(() => {
listFrom.getIssues(false);
});
this.updateIssueLabel(issue, listFrom);
}
}
}
updateIssueLabel(issue, listFrom) {
gl.boardService.moveIssue(issue.id, listFrom.id, this.id)
.then(() => {
listFrom.getIssues(false);
});
}
findIssue (id) {
return this.issues.filter(issue => issue.id === id)[0];
}

View File

@ -92,9 +92,12 @@
const issueLists = issue.getLists();
const listLabels = issueLists.map(listIssue => listIssue.label);
// Add to new lists issues if it doesn't already exist
if (!issueTo) {
// Add to new lists issues if it doesn't already exist
listTo.addIssue(issue, listFrom, newIndex);
} else {
listTo.updateIssueLabel(issue, listFrom);
issueTo.removeLabel(listFrom.label);
}
if (listTo.type === 'done') {

View File

@ -7,7 +7,7 @@
var DOWN_BUILD_TRACE = '#down-build-trace';
this.Build = (function() {
Build.interval = null;
Build.timeout = null;
Build.state = null;
@ -31,7 +31,7 @@
this.$scrollBottomBtn = $('#scroll-bottom');
this.$buildRefreshAnimation = $('.js-build-refresh');
clearInterval(Build.interval);
clearTimeout(Build.timeout);
// Init breakpoint checker
this.bp = Breakpoints.get();
@ -52,17 +52,7 @@
this.getInitialBuildTrace();
this.initScrollButtonAffix();
}
if (this.buildStatus === "running" || this.buildStatus === "pending") {
Build.interval = setInterval((function(_this) {
// Check for new build output if user still watching build page
// Only valid for runnig build when output changes during time
return function() {
if (_this.location() === _this.pageUrl) {
return _this.getBuildTrace();
}
};
})(this), 4000);
}
this.invokeBuildTrace();
}
Build.prototype.initSidebar = function() {
@ -75,6 +65,22 @@
return window.location.href.split("#")[0];
};
Build.prototype.invokeBuildTrace = function() {
var continueRefreshStatuses = ['running', 'pending'];
// Continue to update build trace when build is running or pending
if (continueRefreshStatuses.indexOf(this.buildStatus) !== -1) {
// Check for new build output if user still watching build page
// Only valid for runnig build when output changes during time
Build.timeout = setTimeout((function(_this) {
return function() {
if (_this.location() === _this.pageUrl) {
return _this.getBuildTrace();
}
};
})(this), 4000);
}
};
Build.prototype.getInitialBuildTrace = function() {
var removeRefreshStatuses = ['success', 'failed', 'canceled', 'skipped'];
@ -86,7 +92,7 @@
if (window.location.hash === DOWN_BUILD_TRACE) {
$("html,body").scrollTop(this.$buildTrace.height());
}
if (removeRefreshStatuses.indexOf(buildData.status) >= 0) {
if (removeRefreshStatuses.indexOf(buildData.status) !== -1) {
this.$buildRefreshAnimation.remove();
return this.initScrollMonitor();
}
@ -105,6 +111,7 @@
if (log.state) {
_this.state = log.state;
}
_this.invokeBuildTrace();
if (log.status === "running") {
if (log.append) {
$('.js-build-output').append(log.html);

View File

@ -52,6 +52,30 @@
return this.views[viewMode].call(this);
};
ImageFile.prototype.initDraggable = function($el, padding, callback) {
var dragging = false;
var $body = $('body');
var $offsetEl = $el.parent();
$el.off('mousedown').on('mousedown', function() {
dragging = true;
$body.css('user-select', 'none');
});
$body.off('mouseup').off('mousemove').on('mouseup', function() {
dragging = false;
$body.css('user-select', '');
})
.on('mousemove', function(e) {
var left;
if (!dragging) return;
left = e.pageX - ($offsetEl.offset().left + padding);
callback(e, left);
});
};
prepareFrames = function(view) {
var maxHeight, maxWidth;
maxWidth = 0;
@ -96,26 +120,30 @@
maxHeight = 0;
return $('.swipe.view', this.file).each((function(_this) {
return function(index, view) {
var ref;
var $swipeWrap, $swipeBar, $swipeFrame, wrapPadding, ref;
ref = prepareFrames(view), maxWidth = ref[0], maxHeight = ref[1];
$('.swipe-frame', view).css({
$swipeFrame = $('.swipe-frame', view);
$swipeWrap = $('.swipe-wrap', view);
$swipeBar = $('.swipe-bar', view);
$swipeFrame.css({
width: maxWidth + 16,
height: maxHeight + 28
});
$('.swipe-wrap', view).css({
$swipeWrap.css({
width: maxWidth + 1,
height: maxHeight + 2
});
return $('.swipe-bar', view).css({
$swipeBar.css({
left: 0
}).draggable({
axis: 'x',
containment: 'parent',
drag: function(event) {
return $('.swipe-wrap', view).width((maxWidth + 1) - $(this).position().left);
},
stop: function(event) {
return $('.swipe-wrap', view).width((maxWidth + 1) - $(this).position().left);
});
wrapPadding = parseInt($swipeWrap.css('right').replace('px', ''), 10);
_this.initDraggable($swipeBar, wrapPadding, function(e, left) {
if (left > 0 && left < $swipeFrame.width() - (wrapPadding * 2)) {
$swipeWrap.width((maxWidth + 1) - left);
$swipeBar.css('left', left);
}
});
};
@ -128,9 +156,14 @@
dragTrackWidth = $('.drag-track', this.file).width() - $('.dragger', this.file).width();
return $('.onion-skin.view', this.file).each((function(_this) {
return function(index, view) {
var ref;
var $frame, $track, $dragger, $frameAdded, framePadding, ref, dragging = false;
ref = prepareFrames(view), maxWidth = ref[0], maxHeight = ref[1];
$('.onion-skin-frame', view).css({
$frame = $('.onion-skin-frame', view);
$frameAdded = $('.frame.added', view);
$track = $('.drag-track', view);
$dragger = $('.dragger', $track);
$frame.css({
width: maxWidth + 16,
height: maxHeight + 28
});
@ -138,16 +171,18 @@
width: maxWidth + 1,
height: maxHeight + 2
});
return $('.dragger', view).css({
$dragger.css({
left: dragTrackWidth
}).draggable({
axis: 'x',
containment: 'parent',
drag: function(event) {
return $('.frame.added', view).css('opacity', $(this).position().left / dragTrackWidth);
},
stop: function(event) {
return $('.frame.added', view).css('opacity', $(this).position().left / dragTrackWidth);
});
framePadding = parseInt($frameAdded.css('right').replace('px', ''), 10);
_this.initDraggable($dragger, framePadding, function(e, left) {
var opacity = left / dragTrackWidth;
if (opacity >= 0 && opacity <= 1) {
$dragger.css('left', left);
$frameAdded.css('opacity', opacity);
}
});
};

View File

@ -69,7 +69,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(() => {

View File

@ -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;

View File

@ -36,6 +36,7 @@
/* global Shortcuts */
const ShortcutsBlob = require('./shortcuts_blob');
const UserCallout = require('./user_callout');
(function() {
var Dispatcher;
@ -280,6 +281,9 @@ const ShortcutsBlob = require('./shortcuts_blob');
case 'ci:lints:show':
new gl.CILintEditor();
break;
case 'users:show':
new UserCallout();
break;
}
switch (path.first()) {
case 'sessions':
@ -316,6 +320,7 @@ const ShortcutsBlob = require('./shortcuts_blob');
case 'dashboard':
case 'root':
shortcut_handler = new ShortcutsDashboardNavigation();
new UserCallout();
break;
case 'profiles':
new NotificationsForm();

View File

@ -145,7 +145,7 @@ module.exports = Vue.component('environment-component', {
</div>
</div>
<div class="environments-container">
<div class="content-list environments-container">
<div class="environments-list-loading text-center" v-if="isLoading">
<i class="fa fa-spinner fa-spin"></i>
</div>
@ -181,12 +181,12 @@ module.exports = Vue.component('environment-component', {
:terminal-icon-svg="terminalIconSvg"
:commit-icon-svg="commitIconSvg">
</environment-table>
<table-pagination v-if="state.paginationInformation && state.paginationInformation.totalPages > 1"
:change="changePage"
:pageInfo="state.paginationInformation">
</table-pagination>
</div>
<table-pagination v-if="state.paginationInformation && state.paginationInformation.totalPages > 1"
:change="changePage"
:pageInfo="state.paginationInformation">
</table-pagination>
</div>
</div>
`,

View File

@ -503,32 +503,30 @@ module.exports = Vue.component('environment-item', {
</span>
</td>
<td class="hidden-xs">
<div v-if="!model.isFolder">
<div class="btn-group" role="group">
<actions-component v-if="hasManualActions && canCreateDeployment"
:play-icon-svg="playIconSvg"
:actions="manualActions">
</actions-component>
<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>
<external-url-component v-if="externalURL && canReadEnvironment"
:external-url="externalURL">
</external-url-component>
<external-url-component v-if="externalURL && canReadEnvironment"
:external-url="externalURL">
</external-url-component>
<stop-component v-if="hasStopAction && canCreateDeployment"
:stop-url="model.stop_path">
</stop-component>
<stop-component v-if="hasStopAction && canCreateDeployment"
:stop-url="model.stop_path">
</stop-component>
<terminal-button-component v-if="model && model.terminal_path"
:terminal-icon-svg="terminalIconSvg"
:terminal-path="model.terminal_path">
</terminal-button-component>
<terminal-button-component v-if="model && model.terminal_path"
:terminal-icon-svg="terminalIconSvg"
:terminal-path="model.terminal_path">
</terminal-button-component>
<rollback-component v-if="canRetry && canCreateDeployment"
:is-last-deployment="isLastDeployment"
:retry-url="retryUrl">
</rollback-component>
</div>
<rollback-component v-if="canRetry && canCreateDeployment"
:is-last-deployment="isLastDeployment"
:retry-url="retryUrl">
</rollback-component>
</div>
</td>
</tr>

View File

@ -46,7 +46,7 @@ module.exports = Vue.component('environment-table-component', {
},
template: `
<table class="table ci-table environments">
<table class="table ci-table">
<thead>
<tr>
<th class="environments-name">Environment</th>
@ -54,7 +54,7 @@ module.exports = Vue.component('environment-table-component', {
<th class="environments-build">Job</th>
<th class="environments-commit">Commit</th>
<th class="environments-date">Updated</th>
<th class="hidden-xs environments-actions"></th>
<th class="environments-actions"></th>
</tr>
</thead>
<tbody>

View File

@ -1,16 +1,16 @@
/* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, max-len, one-var, one-var-declaration-per-line, quotes, prefer-template, newline-per-chained-call, comma-dangle, new-cap, no-else-return, consistent-return */
/* global FilesCommentButton */
/* global notes */
(function() {
let $commentButtonTemplate;
var bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; };
this.FilesCommentButton = (function() {
var COMMENT_BUTTON_CLASS, COMMENT_BUTTON_TEMPLATE, DEBOUNCE_TIMEOUT_DURATION, EMPTY_CELL_CLASS, LINE_COLUMN_CLASSES, LINE_CONTENT_CLASS, LINE_HOLDER_CLASS, LINE_NUMBER_CLASS, OLD_LINE_CLASS, TEXT_FILE_SELECTOR, UNFOLDABLE_LINE_CLASS;
var COMMENT_BUTTON_CLASS, EMPTY_CELL_CLASS, LINE_COLUMN_CLASSES, LINE_CONTENT_CLASS, LINE_HOLDER_CLASS, LINE_NUMBER_CLASS, OLD_LINE_CLASS, TEXT_FILE_SELECTOR, UNFOLDABLE_LINE_CLASS;
COMMENT_BUTTON_CLASS = '.add-diff-note';
COMMENT_BUTTON_TEMPLATE = _.template('<button name="button" type="submit" class="btn <%- COMMENT_BUTTON_CLASS %> js-add-diff-note-button" title="Add a comment to this line"><i class="fa fa-comment-o"></i></button>');
LINE_HOLDER_CLASS = '.line_holder';
LINE_NUMBER_CLASS = 'diff-line-num';
@ -27,26 +27,29 @@
TEXT_FILE_SELECTOR = '.text-file';
DEBOUNCE_TIMEOUT_DURATION = 100;
function FilesCommentButton(filesContainerElement) {
var debounce;
this.filesContainerElement = filesContainerElement;
this.destroy = bind(this.destroy, this);
this.render = bind(this.render, this);
this.VIEW_TYPE = $('input#view[type=hidden]').val();
debounce = _.debounce(this.render, DEBOUNCE_TIMEOUT_DURATION);
$(this.filesContainerElement).off('mouseover', LINE_COLUMN_CLASSES).off('mouseleave', LINE_COLUMN_CLASSES).on('mouseover', LINE_COLUMN_CLASSES, debounce).on('mouseleave', LINE_COLUMN_CLASSES, this.destroy);
this.hideButton = bind(this.hideButton, this);
this.isParallelView = notes.isParallelView();
filesContainerElement.on('mouseover', LINE_COLUMN_CLASSES, this.render)
.on('mouseleave', LINE_COLUMN_CLASSES, this.hideButton);
}
FilesCommentButton.prototype.render = function(e) {
var $currentTarget, buttonParentElement, lineContentElement, textFileElement;
var $currentTarget, buttonParentElement, lineContentElement, textFileElement, $button;
$currentTarget = $(e.currentTarget);
buttonParentElement = this.getButtonParent($currentTarget);
if (!this.validateButtonParent(buttonParentElement)) return;
lineContentElement = this.getLineContent($currentTarget);
if (!this.validateLineContent(lineContentElement)) return;
buttonParentElement = this.getButtonParent($currentTarget);
if (!this.validateButtonParent(buttonParentElement) || !this.validateLineContent(lineContentElement)) return;
$button = $(COMMENT_BUTTON_CLASS, buttonParentElement);
buttonParentElement.addClass('is-over')
.nextUntil(`.${LINE_CONTENT_CLASS}`).addClass('is-over');
if ($button.length) {
return;
}
textFileElement = this.getTextFileElement($currentTarget);
buttonParentElement.append(this.buildButton({
@ -61,19 +64,16 @@
}));
};
FilesCommentButton.prototype.destroy = function(e) {
if (this.isMovingToSameType(e)) {
return;
}
$(COMMENT_BUTTON_CLASS, this.getButtonParent($(e.currentTarget))).remove();
FilesCommentButton.prototype.hideButton = function(e) {
var $currentTarget = $(e.currentTarget);
var buttonParentElement = this.getButtonParent($currentTarget);
buttonParentElement.removeClass('is-over')
.nextUntil(`.${LINE_CONTENT_CLASS}`).removeClass('is-over');
};
FilesCommentButton.prototype.buildButton = function(buttonAttributes) {
var initializedButtonTemplate;
initializedButtonTemplate = COMMENT_BUTTON_TEMPLATE({
COMMENT_BUTTON_CLASS: COMMENT_BUTTON_CLASS.substr(1)
});
return $(initializedButtonTemplate).attr({
return $commentButtonTemplate.clone().attr({
'data-noteable-type': buttonAttributes.noteableType,
'data-noteable-id': buttonAttributes.noteableID,
'data-commit-id': buttonAttributes.commitID,
@ -86,14 +86,14 @@
};
FilesCommentButton.prototype.getTextFileElement = function(hoveredElement) {
return $(hoveredElement.closest(TEXT_FILE_SELECTOR));
return hoveredElement.closest(TEXT_FILE_SELECTOR);
};
FilesCommentButton.prototype.getLineContent = function(hoveredElement) {
if (hoveredElement.hasClass(LINE_CONTENT_CLASS)) {
return hoveredElement;
}
if (this.VIEW_TYPE === 'inline') {
if (!this.isParallelView) {
return $(hoveredElement).closest(LINE_HOLDER_CLASS).find("." + LINE_CONTENT_CLASS);
} else {
return $(hoveredElement).next("." + LINE_CONTENT_CLASS);
@ -101,7 +101,7 @@
};
FilesCommentButton.prototype.getButtonParent = function(hoveredElement) {
if (this.VIEW_TYPE === 'inline') {
if (!this.isParallelView) {
if (hoveredElement.hasClass(OLD_LINE_CLASS)) {
return hoveredElement;
}
@ -114,17 +114,8 @@
}
};
FilesCommentButton.prototype.isMovingToSameType = function(e) {
var newButtonParent;
newButtonParent = this.getButtonParent($(e.toElement));
if (!newButtonParent) {
return false;
}
return newButtonParent.is(this.getButtonParent($(e.currentTarget)));
};
FilesCommentButton.prototype.validateButtonParent = function(buttonParentElement) {
return !buttonParentElement.hasClass(EMPTY_CELL_CLASS) && !buttonParentElement.hasClass(UNFOLDABLE_LINE_CLASS) && $(COMMENT_BUTTON_CLASS, buttonParentElement).length === 0;
return !buttonParentElement.hasClass(EMPTY_CELL_CLASS) && !buttonParentElement.hasClass(UNFOLDABLE_LINE_CLASS);
};
FilesCommentButton.prototype.validateLineContent = function(lineContentElement) {
@ -135,6 +126,8 @@
})();
$.fn.filesCommentButton = function() {
$commentButtonTemplate = $('<button name="button" type="submit" class="add-diff-note js-add-diff-note-button" title="Add a comment to this line"><i class="fa fa-comment-o"></i></button>');
if (!(this && (this.parent().data('can-create-note') != null))) {
return;
}

View File

@ -48,7 +48,11 @@
}
setOffset(offset = 0) {
this.dropdown.style.left = `${offset}px`;
if (window.innerWidth > 480) {
this.dropdown.style.left = `${offset}px`;
} else {
this.dropdown.style.left = '0px';
}
}
renderContent(forceShowList = false) {

View File

@ -63,7 +63,7 @@
}
GitLabDropdownFilter.prototype.shouldBlur = function(keyCode) {
return BLUR_KEYCODES.indexOf(keyCode) >= 0;
return BLUR_KEYCODES.indexOf(keyCode) !== -1;
};
GitLabDropdownFilter.prototype.filter = function(search_text) {
@ -605,7 +605,7 @@
var occurrences;
occurrences = fuzzaldrinPlus.match(text, term);
return text.split('').map(function(character, i) {
if (indexOf.call(occurrences, i) >= 0) {
if (indexOf.call(occurrences, i) !== -1) {
return "<b>" + character + "</b>";
} else {
return character;
@ -748,7 +748,7 @@
return function(e) {
var $listItems, PREV_INDEX, currentKeyCode;
currentKeyCode = e.which;
if (ARROW_KEY_CODES.indexOf(currentKeyCode) >= 0) {
if (ARROW_KEY_CODES.indexOf(currentKeyCode) !== -1) {
e.preventDefault();
e.stopImmediatePropagation();
PREV_INDEX = currentIndex;

View File

@ -1,4 +1,4 @@
require('./stat_graph_contributors_graph');
require('./stat_graph_contributors_util');
require('./stat_graph_contributors');
require('./stat_graph');
import ContributorsStatGraph from './stat_graph_contributors';
// export to global scope
window.ContributorsStatGraph = ContributorsStatGraph;

View File

@ -1,18 +0,0 @@
/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-return-assign, max-len */
(function() {
this.StatGraph = (function() {
function StatGraph() {}
StatGraph.log = {};
StatGraph.get_log = function() {
return this.log;
};
StatGraph.set_log = function(data) {
return this.log = data;
};
return StatGraph;
})();
}).call(window);

View File

@ -1,116 +1,111 @@
/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, one-var, camelcase, one-var-declaration-per-line, quotes, no-param-reassign, quote-props, comma-dangle, prefer-template, max-len, no-return-assign */
/* global ContributorsGraph */
/* global ContributorsAuthorGraph */
/* global ContributorsMasterGraph */
/* global ContributorsStatGraphUtil */
/* global d3 */
/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, one-var, camelcase, one-var-declaration-per-line, quotes, no-param-reassign, quote-props, comma-dangle, prefer-template, max-len, no-return-assign, no-shadow */
window.d3 = require('d3');
import d3 from 'd3';
import { ContributorsGraph, ContributorsAuthorGraph, ContributorsMasterGraph } from './stat_graph_contributors_graph';
import ContributorsStatGraphUtil from './stat_graph_contributors_util';
(function() {
this.ContributorsStatGraph = (function() {
function ContributorsStatGraph() {}
export default (function() {
function ContributorsStatGraph() {}
ContributorsStatGraph.prototype.init = function(log) {
var author_commits, total_commits;
this.parsed_log = ContributorsStatGraphUtil.parse_log(log);
this.set_current_field("commits");
total_commits = ContributorsStatGraphUtil.get_total_data(this.parsed_log, this.field);
author_commits = ContributorsStatGraphUtil.get_author_data(this.parsed_log, this.field);
this.add_master_graph(total_commits);
this.add_authors_graph(author_commits);
return this.change_date_header();
};
ContributorsStatGraph.prototype.init = function(log) {
var author_commits, total_commits;
this.parsed_log = ContributorsStatGraphUtil.parse_log(log);
this.set_current_field("commits");
total_commits = ContributorsStatGraphUtil.get_total_data(this.parsed_log, this.field);
author_commits = ContributorsStatGraphUtil.get_author_data(this.parsed_log, this.field);
this.add_master_graph(total_commits);
this.add_authors_graph(author_commits);
return this.change_date_header();
};
ContributorsStatGraph.prototype.add_master_graph = function(total_data) {
this.master_graph = new ContributorsMasterGraph(total_data);
return this.master_graph.draw();
};
ContributorsStatGraph.prototype.add_master_graph = function(total_data) {
this.master_graph = new ContributorsMasterGraph(total_data);
return this.master_graph.draw();
};
ContributorsStatGraph.prototype.add_authors_graph = function(author_data) {
var limited_author_data;
this.authors = [];
limited_author_data = author_data.slice(0, 100);
return _.each(limited_author_data, (function(_this) {
return function(d) {
var author_graph, author_header;
author_header = _this.create_author_header(d);
$(".contributors-list").append(author_header);
_this.authors[d.author_name] = author_graph = new ContributorsAuthorGraph(d.dates);
return author_graph.draw();
};
})(this));
};
ContributorsStatGraph.prototype.add_authors_graph = function(author_data) {
var limited_author_data;
this.authors = [];
limited_author_data = author_data.slice(0, 100);
return _.each(limited_author_data, (function(_this) {
return function(d) {
var author_graph, author_header;
author_header = _this.create_author_header(d);
$(".contributors-list").append(author_header);
_this.authors[d.author_name] = author_graph = new ContributorsAuthorGraph(d.dates);
return author_graph.draw();
};
})(this));
};
ContributorsStatGraph.prototype.format_author_commit_info = function(author) {
var commits;
commits = $('<span/>', {
"class": 'graph-author-commits-count'
});
commits.text(author.commits + " commits");
return $('<span/>').append(commits);
};
ContributorsStatGraph.prototype.format_author_commit_info = function(author) {
var commits;
commits = $('<span/>', {
"class": 'graph-author-commits-count'
});
commits.text(author.commits + " commits");
return $('<span/>').append(commits);
};
ContributorsStatGraph.prototype.create_author_header = function(author) {
var author_commit_info, author_commit_info_span, author_email, author_name, list_item;
list_item = $('<li/>', {
"class": 'person',
style: 'display: block;'
});
author_name = $('<h4>' + author.author_name + '</h4>');
author_email = $('<p class="graph-author-email">' + author.author_email + '</p>');
author_commit_info_span = $('<span/>', {
"class": 'commits'
});
author_commit_info = this.format_author_commit_info(author);
author_commit_info_span.html(author_commit_info);
list_item.append(author_name);
list_item.append(author_email);
list_item.append(author_commit_info_span);
return list_item;
};
ContributorsStatGraph.prototype.create_author_header = function(author) {
var author_commit_info, author_commit_info_span, author_email, author_name, list_item;
list_item = $('<li/>', {
"class": 'person',
style: 'display: block;'
});
author_name = $('<h4>' + author.author_name + '</h4>');
author_email = $('<p class="graph-author-email">' + author.author_email + '</p>');
author_commit_info_span = $('<span/>', {
"class": 'commits'
});
author_commit_info = this.format_author_commit_info(author);
author_commit_info_span.html(author_commit_info);
list_item.append(author_name);
list_item.append(author_email);
list_item.append(author_commit_info_span);
return list_item;
};
ContributorsStatGraph.prototype.redraw_master = function() {
var total_data;
total_data = ContributorsStatGraphUtil.get_total_data(this.parsed_log, this.field);
this.master_graph.set_data(total_data);
return this.master_graph.redraw();
};
ContributorsStatGraph.prototype.redraw_master = function() {
var total_data;
total_data = ContributorsStatGraphUtil.get_total_data(this.parsed_log, this.field);
this.master_graph.set_data(total_data);
return this.master_graph.redraw();
};
ContributorsStatGraph.prototype.redraw_authors = function() {
var author_commits, x_domain;
$("ol").html("");
x_domain = ContributorsGraph.prototype.x_domain;
author_commits = ContributorsStatGraphUtil.get_author_data(this.parsed_log, this.field, x_domain);
return _.each(author_commits, (function(_this) {
return function(d) {
_this.redraw_author_commit_info(d);
$(_this.authors[d.author_name].list_item).appendTo("ol");
_this.authors[d.author_name].set_data(d.dates);
return _this.authors[d.author_name].redraw();
};
})(this));
};
ContributorsStatGraph.prototype.redraw_authors = function() {
var author_commits, x_domain;
$("ol").html("");
x_domain = ContributorsGraph.prototype.x_domain;
author_commits = ContributorsStatGraphUtil.get_author_data(this.parsed_log, this.field, x_domain);
return _.each(author_commits, (function(_this) {
return function(d) {
_this.redraw_author_commit_info(d);
$(_this.authors[d.author_name].list_item).appendTo("ol");
_this.authors[d.author_name].set_data(d.dates);
return _this.authors[d.author_name].redraw();
};
})(this));
};
ContributorsStatGraph.prototype.set_current_field = function(field) {
return this.field = field;
};
ContributorsStatGraph.prototype.set_current_field = function(field) {
return this.field = field;
};
ContributorsStatGraph.prototype.change_date_header = function() {
var print, print_date_format, x_domain;
x_domain = ContributorsGraph.prototype.x_domain;
print_date_format = d3.time.format("%B %e %Y");
print = print_date_format(x_domain[0]) + " - " + print_date_format(x_domain[1]);
return $("#date_header").text(print);
};
ContributorsStatGraph.prototype.change_date_header = function() {
var print, print_date_format, x_domain;
x_domain = ContributorsGraph.prototype.x_domain;
print_date_format = d3.time.format("%B %e %Y");
print = print_date_format(x_domain[0]) + " - " + print_date_format(x_domain[1]);
return $("#date_header").text(print);
};
ContributorsStatGraph.prototype.redraw_author_commit_info = function(author) {
var author_commit_info, author_list_item;
author_list_item = $(this.authors[author.author_name].list_item);
author_commit_info = this.format_author_commit_info(author);
return author_list_item.find("span").html(author_commit_info);
};
ContributorsStatGraph.prototype.redraw_author_commit_info = function(author) {
var author_commit_info, author_list_item;
author_list_item = $(this.authors[author.author_name].list_item);
author_commit_info = this.format_author_commit_info(author);
return author_list_item.find("span").html(author_commit_info);
};
return ContributorsStatGraph;
})();
}).call(window);
return ContributorsStatGraph;
})();

View File

@ -1,276 +1,272 @@
/* eslint-disable func-names, space-before-function-paren, one-var, no-var, prefer-rest-params, max-len, no-restricted-syntax, vars-on-top, no-use-before-define, no-param-reassign, new-cap, no-underscore-dangle, wrap-iife, comma-dangle, no-return-assign, prefer-arrow-callback, quotes, prefer-template, newline-per-chained-call, no-else-return */
/* global d3 */
/* global ContributorsGraph */
/* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, max-len, no-restricted-syntax, vars-on-top, no-use-before-define, no-param-reassign, new-cap, no-underscore-dangle, wrap-iife, comma-dangle, no-return-assign, prefer-arrow-callback, quotes, prefer-template, newline-per-chained-call, no-else-return, no-shadow */
window.d3 = require('d3');
import d3 from 'd3';
(function() {
var bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; },
extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
hasProp = {}.hasOwnProperty;
const bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; };
const extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
const hasProp = {}.hasOwnProperty;
this.ContributorsGraph = (function() {
function ContributorsGraph() {}
export const ContributorsGraph = (function() {
function ContributorsGraph() {}
ContributorsGraph.prototype.MARGIN = {
top: 20,
right: 20,
bottom: 30,
left: 50
};
ContributorsGraph.prototype.MARGIN = {
top: 20,
right: 20,
bottom: 30,
left: 50
};
ContributorsGraph.prototype.x_domain = null;
ContributorsGraph.prototype.x_domain = null;
ContributorsGraph.prototype.y_domain = null;
ContributorsGraph.prototype.y_domain = null;
ContributorsGraph.prototype.dates = [];
ContributorsGraph.prototype.dates = [];
ContributorsGraph.set_x_domain = function(data) {
return ContributorsGraph.prototype.x_domain = data;
};
ContributorsGraph.set_x_domain = function(data) {
return ContributorsGraph.prototype.x_domain = data;
};
ContributorsGraph.set_y_domain = function(data) {
return ContributorsGraph.prototype.y_domain = [
0, d3.max(data, function(d) {
return d.commits = d.commits || d.additions || d.deletions;
})
];
};
ContributorsGraph.set_y_domain = function(data) {
return ContributorsGraph.prototype.y_domain = [
0, d3.max(data, function(d) {
return d.commits = d.commits || d.additions || d.deletions;
})
];
};
ContributorsGraph.init_x_domain = function(data) {
return ContributorsGraph.prototype.x_domain = d3.extent(data, function(d) {
return d.date;
});
};
ContributorsGraph.init_x_domain = function(data) {
return ContributorsGraph.prototype.x_domain = d3.extent(data, function(d) {
return d.date;
});
};
ContributorsGraph.init_y_domain = function(data) {
return ContributorsGraph.prototype.y_domain = [
0, d3.max(data, function(d) {
return d.commits = d.commits || d.additions || d.deletions;
})
];
};
ContributorsGraph.init_y_domain = function(data) {
return ContributorsGraph.prototype.y_domain = [
0, d3.max(data, function(d) {
return d.commits = d.commits || d.additions || d.deletions;
})
];
};
ContributorsGraph.init_domain = function(data) {
ContributorsGraph.init_x_domain(data);
return ContributorsGraph.init_y_domain(data);
};
ContributorsGraph.init_domain = function(data) {
ContributorsGraph.init_x_domain(data);
return ContributorsGraph.init_y_domain(data);
};
ContributorsGraph.set_dates = function(data) {
return ContributorsGraph.prototype.dates = data;
};
ContributorsGraph.set_dates = function(data) {
return ContributorsGraph.prototype.dates = data;
};
ContributorsGraph.prototype.set_x_domain = function() {
return this.x.domain(this.x_domain);
};
ContributorsGraph.prototype.set_x_domain = function() {
return this.x.domain(this.x_domain);
};
ContributorsGraph.prototype.set_y_domain = function() {
return this.y.domain(this.y_domain);
};
ContributorsGraph.prototype.set_y_domain = function() {
return this.y.domain(this.y_domain);
};
ContributorsGraph.prototype.set_domain = function() {
this.set_x_domain();
return this.set_y_domain();
};
ContributorsGraph.prototype.set_domain = function() {
this.set_x_domain();
return this.set_y_domain();
};
ContributorsGraph.prototype.create_scale = function(width, height) {
this.x = d3.time.scale().range([0, width]).clamp(true);
return this.y = d3.scale.linear().range([height, 0]).nice();
};
ContributorsGraph.prototype.create_scale = function(width, height) {
this.x = d3.time.scale().range([0, width]).clamp(true);
return this.y = d3.scale.linear().range([height, 0]).nice();
};
ContributorsGraph.prototype.draw_x_axis = function() {
return this.svg.append("g").attr("class", "x axis").attr("transform", "translate(0, " + this.height + ")").call(this.x_axis);
};
ContributorsGraph.prototype.draw_x_axis = function() {
return this.svg.append("g").attr("class", "x axis").attr("transform", "translate(0, " + this.height + ")").call(this.x_axis);
};
ContributorsGraph.prototype.draw_y_axis = function() {
return this.svg.append("g").attr("class", "y axis").call(this.y_axis);
};
ContributorsGraph.prototype.draw_y_axis = function() {
return this.svg.append("g").attr("class", "y axis").call(this.y_axis);
};
ContributorsGraph.prototype.set_data = function(data) {
return this.data = data;
};
ContributorsGraph.prototype.set_data = function(data) {
return this.data = data;
};
return ContributorsGraph;
})();
return ContributorsGraph;
})();
this.ContributorsMasterGraph = (function(superClass) {
extend(ContributorsMasterGraph, superClass);
export const ContributorsMasterGraph = (function(superClass) {
extend(ContributorsMasterGraph, superClass);
function ContributorsMasterGraph(data1) {
this.data = data1;
this.update_content = bind(this.update_content, this);
this.width = $('.content').width() - 70;
this.height = 200;
this.x = null;
this.y = null;
this.x_axis = null;
this.y_axis = null;
this.area = null;
this.svg = null;
this.brush = null;
this.x_max_domain = null;
function ContributorsMasterGraph(data1) {
this.data = data1;
this.update_content = bind(this.update_content, this);
this.width = $('.content').width() - 70;
this.height = 200;
this.x = null;
this.y = null;
this.x_axis = null;
this.y_axis = null;
this.area = null;
this.svg = null;
this.brush = null;
this.x_max_domain = null;
}
ContributorsMasterGraph.prototype.process_dates = function(data) {
var dates;
dates = this.get_dates(data);
this.parse_dates(data);
return ContributorsGraph.set_dates(dates);
};
ContributorsMasterGraph.prototype.get_dates = function(data) {
return _.pluck(data, 'date');
};
ContributorsMasterGraph.prototype.parse_dates = function(data) {
var parseDate;
parseDate = d3.time.format("%Y-%m-%d").parse;
return data.forEach(function(d) {
return d.date = parseDate(d.date);
});
};
ContributorsMasterGraph.prototype.create_scale = function() {
return ContributorsMasterGraph.__super__.create_scale.call(this, this.width, this.height);
};
ContributorsMasterGraph.prototype.create_axes = function() {
this.x_axis = d3.svg.axis().scale(this.x).orient("bottom");
return this.y_axis = d3.svg.axis().scale(this.y).orient("left").ticks(5);
};
ContributorsMasterGraph.prototype.create_svg = function() {
return this.svg = d3.select("#contributors-master").append("svg").attr("width", this.width + this.MARGIN.left + this.MARGIN.right).attr("height", this.height + this.MARGIN.top + this.MARGIN.bottom).attr("class", "tint-box").append("g").attr("transform", "translate(" + this.MARGIN.left + "," + this.MARGIN.top + ")");
};
ContributorsMasterGraph.prototype.create_area = function(x, y) {
return this.area = d3.svg.area().x(function(d) {
return x(d.date);
}).y0(this.height).y1(function(d) {
d.commits = d.commits || d.additions || d.deletions;
return y(d.commits);
}).interpolate("basis");
};
ContributorsMasterGraph.prototype.create_brush = function() {
return this.brush = d3.svg.brush().x(this.x).on("brushend", this.update_content);
};
ContributorsMasterGraph.prototype.draw_path = function(data) {
return this.svg.append("path").datum(data).attr("class", "area").attr("d", this.area);
};
ContributorsMasterGraph.prototype.add_brush = function() {
return this.svg.append("g").attr("class", "selection").call(this.brush).selectAll("rect").attr("height", this.height);
};
ContributorsMasterGraph.prototype.update_content = function() {
ContributorsGraph.set_x_domain(this.brush.empty() ? this.x_max_domain : this.brush.extent());
return $("#brush_change").trigger('change');
};
ContributorsMasterGraph.prototype.draw = function() {
this.process_dates(this.data);
this.create_scale();
this.create_axes();
ContributorsGraph.init_domain(this.data);
this.x_max_domain = this.x_domain;
this.set_domain();
this.create_area(this.x, this.y);
this.create_svg();
this.create_brush();
this.draw_path(this.data);
this.draw_x_axis();
this.draw_y_axis();
return this.add_brush();
};
ContributorsMasterGraph.prototype.redraw = function() {
this.process_dates(this.data);
ContributorsGraph.set_y_domain(this.data);
this.set_y_domain();
this.svg.select("path").datum(this.data);
this.svg.select("path").attr("d", this.area);
return this.svg.select(".y.axis").call(this.y_axis);
};
return ContributorsMasterGraph;
})(ContributorsGraph);
export const ContributorsAuthorGraph = (function(superClass) {
extend(ContributorsAuthorGraph, superClass);
function ContributorsAuthorGraph(data1) {
this.data = data1;
// Don't split graph size in half for mobile devices.
if ($(window).width() < 768) {
this.width = $('.content').width() - 80;
} else {
this.width = ($('.content').width() / 2) - 100;
}
this.height = 200;
this.x = null;
this.y = null;
this.x_axis = null;
this.y_axis = null;
this.area = null;
this.svg = null;
this.list_item = null;
}
ContributorsMasterGraph.prototype.process_dates = function(data) {
var dates;
dates = this.get_dates(data);
this.parse_dates(data);
return ContributorsGraph.set_dates(dates);
};
ContributorsAuthorGraph.prototype.create_scale = function() {
return ContributorsAuthorGraph.__super__.create_scale.call(this, this.width, this.height);
};
ContributorsMasterGraph.prototype.get_dates = function(data) {
return _.pluck(data, 'date');
};
ContributorsAuthorGraph.prototype.create_axes = function() {
this.x_axis = d3.svg.axis().scale(this.x).orient("bottom").ticks(8);
return this.y_axis = d3.svg.axis().scale(this.y).orient("left").ticks(5);
};
ContributorsMasterGraph.prototype.parse_dates = function(data) {
ContributorsAuthorGraph.prototype.create_area = function(x, y) {
return this.area = d3.svg.area().x(function(d) {
var parseDate;
parseDate = d3.time.format("%Y-%m-%d").parse;
return data.forEach(function(d) {
return d.date = parseDate(d.date);
});
};
return x(parseDate(d));
}).y0(this.height).y1((function(_this) {
return function(d) {
if (_this.data[d] != null) {
return y(_this.data[d]);
} else {
return y(0);
}
};
})(this)).interpolate("basis");
};
ContributorsMasterGraph.prototype.create_scale = function() {
return ContributorsMasterGraph.__super__.create_scale.call(this, this.width, this.height);
};
ContributorsAuthorGraph.prototype.create_svg = function() {
this.list_item = d3.selectAll(".person")[0].pop();
return this.svg = d3.select(this.list_item).append("svg").attr("width", this.width + this.MARGIN.left + this.MARGIN.right).attr("height", this.height + this.MARGIN.top + this.MARGIN.bottom).attr("class", "spark").append("g").attr("transform", "translate(" + this.MARGIN.left + "," + this.MARGIN.top + ")");
};
ContributorsMasterGraph.prototype.create_axes = function() {
this.x_axis = d3.svg.axis().scale(this.x).orient("bottom");
return this.y_axis = d3.svg.axis().scale(this.y).orient("left").ticks(5);
};
ContributorsAuthorGraph.prototype.draw_path = function(data) {
return this.svg.append("path").datum(data).attr("class", "area-contributor").attr("d", this.area);
};
ContributorsMasterGraph.prototype.create_svg = function() {
return this.svg = d3.select("#contributors-master").append("svg").attr("width", this.width + this.MARGIN.left + this.MARGIN.right).attr("height", this.height + this.MARGIN.top + this.MARGIN.bottom).attr("class", "tint-box").append("g").attr("transform", "translate(" + this.MARGIN.left + "," + this.MARGIN.top + ")");
};
ContributorsAuthorGraph.prototype.draw = function() {
this.create_scale();
this.create_axes();
this.set_domain();
this.create_area(this.x, this.y);
this.create_svg();
this.draw_path(this.dates);
this.draw_x_axis();
return this.draw_y_axis();
};
ContributorsMasterGraph.prototype.create_area = function(x, y) {
return this.area = d3.svg.area().x(function(d) {
return x(d.date);
}).y0(this.height).y1(function(d) {
d.commits = d.commits || d.additions || d.deletions;
return y(d.commits);
}).interpolate("basis");
};
ContributorsAuthorGraph.prototype.redraw = function() {
this.set_domain();
this.svg.select("path").datum(this.dates);
this.svg.select("path").attr("d", this.area);
this.svg.select(".x.axis").call(this.x_axis);
return this.svg.select(".y.axis").call(this.y_axis);
};
ContributorsMasterGraph.prototype.create_brush = function() {
return this.brush = d3.svg.brush().x(this.x).on("brushend", this.update_content);
};
ContributorsMasterGraph.prototype.draw_path = function(data) {
return this.svg.append("path").datum(data).attr("class", "area").attr("d", this.area);
};
ContributorsMasterGraph.prototype.add_brush = function() {
return this.svg.append("g").attr("class", "selection").call(this.brush).selectAll("rect").attr("height", this.height);
};
ContributorsMasterGraph.prototype.update_content = function() {
ContributorsGraph.set_x_domain(this.brush.empty() ? this.x_max_domain : this.brush.extent());
return $("#brush_change").trigger('change');
};
ContributorsMasterGraph.prototype.draw = function() {
this.process_dates(this.data);
this.create_scale();
this.create_axes();
ContributorsGraph.init_domain(this.data);
this.x_max_domain = this.x_domain;
this.set_domain();
this.create_area(this.x, this.y);
this.create_svg();
this.create_brush();
this.draw_path(this.data);
this.draw_x_axis();
this.draw_y_axis();
return this.add_brush();
};
ContributorsMasterGraph.prototype.redraw = function() {
this.process_dates(this.data);
ContributorsGraph.set_y_domain(this.data);
this.set_y_domain();
this.svg.select("path").datum(this.data);
this.svg.select("path").attr("d", this.area);
return this.svg.select(".y.axis").call(this.y_axis);
};
return ContributorsMasterGraph;
})(ContributorsGraph);
this.ContributorsAuthorGraph = (function(superClass) {
extend(ContributorsAuthorGraph, superClass);
function ContributorsAuthorGraph(data1) {
this.data = data1;
// Don't split graph size in half for mobile devices.
if ($(window).width() < 768) {
this.width = $('.content').width() - 80;
} else {
this.width = ($('.content').width() / 2) - 100;
}
this.height = 200;
this.x = null;
this.y = null;
this.x_axis = null;
this.y_axis = null;
this.area = null;
this.svg = null;
this.list_item = null;
}
ContributorsAuthorGraph.prototype.create_scale = function() {
return ContributorsAuthorGraph.__super__.create_scale.call(this, this.width, this.height);
};
ContributorsAuthorGraph.prototype.create_axes = function() {
this.x_axis = d3.svg.axis().scale(this.x).orient("bottom").ticks(8);
return this.y_axis = d3.svg.axis().scale(this.y).orient("left").ticks(5);
};
ContributorsAuthorGraph.prototype.create_area = function(x, y) {
return this.area = d3.svg.area().x(function(d) {
var parseDate;
parseDate = d3.time.format("%Y-%m-%d").parse;
return x(parseDate(d));
}).y0(this.height).y1((function(_this) {
return function(d) {
if (_this.data[d] != null) {
return y(_this.data[d]);
} else {
return y(0);
}
};
})(this)).interpolate("basis");
};
ContributorsAuthorGraph.prototype.create_svg = function() {
this.list_item = d3.selectAll(".person")[0].pop();
return this.svg = d3.select(this.list_item).append("svg").attr("width", this.width + this.MARGIN.left + this.MARGIN.right).attr("height", this.height + this.MARGIN.top + this.MARGIN.bottom).attr("class", "spark").append("g").attr("transform", "translate(" + this.MARGIN.left + "," + this.MARGIN.top + ")");
};
ContributorsAuthorGraph.prototype.draw_path = function(data) {
return this.svg.append("path").datum(data).attr("class", "area-contributor").attr("d", this.area);
};
ContributorsAuthorGraph.prototype.draw = function() {
this.create_scale();
this.create_axes();
this.set_domain();
this.create_area(this.x, this.y);
this.create_svg();
this.draw_path(this.dates);
this.draw_x_axis();
return this.draw_y_axis();
};
ContributorsAuthorGraph.prototype.redraw = function() {
this.set_domain();
this.svg.select("path").datum(this.dates);
this.svg.select("path").attr("d", this.area);
this.svg.select(".x.axis").call(this.x_axis);
return this.svg.select(".y.axis").call(this.y_axis);
};
return ContributorsAuthorGraph;
})(ContributorsGraph);
}).call(window);
return ContributorsAuthorGraph;
})(ContributorsGraph);

View File

@ -1,138 +1,137 @@
/* eslint-disable func-names, space-before-function-paren, object-shorthand, no-var, one-var, camelcase, one-var-declaration-per-line, comma-dangle, no-param-reassign, no-return-assign, quotes, prefer-arrow-callback, wrap-iife, consistent-return, no-unused-vars, max-len, no-cond-assign, no-else-return, max-len */
(function() {
window.ContributorsStatGraphUtil = {
parse_log: function(log) {
var by_author, by_email, data, entry, i, len, total, normalized_email;
total = {};
by_author = {};
by_email = {};
for (i = 0, len = log.length; i < len; i += 1) {
entry = log[i];
if (total[entry.date] == null) {
this.add_date(entry.date, total);
}
normalized_email = entry.author_email.toLowerCase();
data = by_author[entry.author_name] || by_email[normalized_email];
if (data == null) {
data = this.add_author(entry, by_author, by_email);
}
if (!data[entry.date]) {
this.add_date(entry.date, data);
}
this.store_data(entry, total[entry.date], data[entry.date]);
export default {
parse_log: function(log) {
var by_author, by_email, data, entry, i, len, total, normalized_email;
total = {};
by_author = {};
by_email = {};
for (i = 0, len = log.length; i < len; i += 1) {
entry = log[i];
if (total[entry.date] == null) {
this.add_date(entry.date, total);
}
total = _.toArray(total);
by_author = _.toArray(by_author);
return {
total: total,
by_author: by_author
};
},
add_date: function(date, collection) {
collection[date] = {};
return collection[date].date = date;
},
add_author: function(author, by_author, by_email) {
var data, normalized_email;
data = {};
data.author_name = author.author_name;
data.author_email = author.author_email;
normalized_email = author.author_email.toLowerCase();
by_author[author.author_name] = data;
by_email[normalized_email] = data;
return data;
},
store_data: function(entry, total, by_author) {
this.store_commits(total, by_author);
this.store_additions(entry, total, by_author);
return this.store_deletions(entry, total, by_author);
},
store_commits: function(total, by_author) {
this.add(total, "commits", 1);
return this.add(by_author, "commits", 1);
},
add: function(collection, field, value) {
if (collection[field] == null) {
collection[field] = 0;
normalized_email = entry.author_email.toLowerCase();
data = by_author[entry.author_name] || by_email[normalized_email];
if (data == null) {
data = this.add_author(entry, by_author, by_email);
}
return collection[field] += value;
},
store_additions: function(entry, total, by_author) {
if (entry.additions == null) {
entry.additions = 0;
}
this.add(total, "additions", entry.additions);
return this.add(by_author, "additions", entry.additions);
},
store_deletions: function(entry, total, by_author) {
if (entry.deletions == null) {
entry.deletions = 0;
}
this.add(total, "deletions", entry.deletions);
return this.add(by_author, "deletions", entry.deletions);
},
get_total_data: function(parsed_log, field) {
var log, total_data;
log = parsed_log.total;
total_data = this.pick_field(log, field);
return _.sortBy(total_data, function(d) {
return d.date;
});
},
pick_field: function(log, field) {
var total_data;
total_data = [];
_.each(log, function(d) {
return total_data.push(_.pick(d, [field, 'date']));
});
return total_data;
},
get_author_data: function(parsed_log, field, date_range) {
var author_data, log;
if (date_range == null) {
date_range = null;
}
log = parsed_log.by_author;
author_data = [];
_.each(log, (function(_this) {
return function(log_entry) {
var parsed_log_entry;
parsed_log_entry = _this.parse_log_entry(log_entry, field, date_range);
if (!_.isEmpty(parsed_log_entry.dates)) {
return author_data.push(parsed_log_entry);
}
};
})(this));
return _.sortBy(author_data, function(d) {
return d[field];
}).reverse();
},
parse_log_entry: function(log_entry, field, date_range) {
var parsed_entry;
parsed_entry = {};
parsed_entry.author_name = log_entry.author_name;
parsed_entry.author_email = log_entry.author_email;
parsed_entry.dates = {};
parsed_entry.commits = parsed_entry.additions = parsed_entry.deletions = 0;
_.each(_.omit(log_entry, 'author_name', 'author_email'), (function(_this) {
return function(value, key) {
if (_this.in_range(value.date, date_range)) {
parsed_entry.dates[value.date] = value[field];
parsed_entry.commits += value.commits;
parsed_entry.additions += value.additions;
return parsed_entry.deletions += value.deletions;
}
};
})(this));
return parsed_entry;
},
in_range: function(date, date_range) {
var ref;
if (date_range === null || (date_range[0] <= (ref = new Date(date)) && ref <= date_range[1])) {
return true;
} else {
return false;
if (!data[entry.date]) {
this.add_date(entry.date, data);
}
this.store_data(entry, total[entry.date], data[entry.date]);
}
};
}).call(window);
total = _.toArray(total);
by_author = _.toArray(by_author);
return {
total: total,
by_author: by_author
};
},
add_date: function(date, collection) {
collection[date] = {};
return collection[date].date = date;
},
add_author: function(author, by_author, by_email) {
var data, normalized_email;
data = {};
data.author_name = author.author_name;
data.author_email = author.author_email;
normalized_email = author.author_email.toLowerCase();
by_author[author.author_name] = data;
by_email[normalized_email] = data;
return data;
},
store_data: function(entry, total, by_author) {
this.store_commits(total, by_author);
this.store_additions(entry, total, by_author);
return this.store_deletions(entry, total, by_author);
},
store_commits: function(total, by_author) {
this.add(total, "commits", 1);
return this.add(by_author, "commits", 1);
},
add: function(collection, field, value) {
if (collection[field] == null) {
collection[field] = 0;
}
return collection[field] += value;
},
store_additions: function(entry, total, by_author) {
if (entry.additions == null) {
entry.additions = 0;
}
this.add(total, "additions", entry.additions);
return this.add(by_author, "additions", entry.additions);
},
store_deletions: function(entry, total, by_author) {
if (entry.deletions == null) {
entry.deletions = 0;
}
this.add(total, "deletions", entry.deletions);
return this.add(by_author, "deletions", entry.deletions);
},
get_total_data: function(parsed_log, field) {
var log, total_data;
log = parsed_log.total;
total_data = this.pick_field(log, field);
return _.sortBy(total_data, function(d) {
return d.date;
});
},
pick_field: function(log, field) {
var total_data;
total_data = [];
_.each(log, function(d) {
return total_data.push(_.pick(d, [field, 'date']));
});
return total_data;
},
get_author_data: function(parsed_log, field, date_range) {
var author_data, log;
if (date_range == null) {
date_range = null;
}
log = parsed_log.by_author;
author_data = [];
_.each(log, (function(_this) {
return function(log_entry) {
var parsed_log_entry;
parsed_log_entry = _this.parse_log_entry(log_entry, field, date_range);
if (!_.isEmpty(parsed_log_entry.dates)) {
return author_data.push(parsed_log_entry);
}
};
})(this));
return _.sortBy(author_data, function(d) {
return d[field];
}).reverse();
},
parse_log_entry: function(log_entry, field, date_range) {
var parsed_entry;
parsed_entry = {};
parsed_entry.author_name = log_entry.author_name;
parsed_entry.author_email = log_entry.author_email;
parsed_entry.dates = {};
parsed_entry.commits = parsed_entry.additions = parsed_entry.deletions = 0;
_.each(_.omit(log_entry, 'author_name', 'author_email'), (function(_this) {
return function(value, key) {
if (_this.in_range(value.date, date_range)) {
parsed_entry.dates[value.date] = value[field];
parsed_entry.commits += value.commits;
parsed_entry.additions += value.additions;
return parsed_entry.deletions += value.deletions;
}
};
})(this));
return parsed_entry;
},
in_range: function(date, date_range) {
var ref;
if (date_range === null || (date_range[0] <= (ref = new Date(date)) && ref <= date_range[1])) {
return true;
} else {
return false;
}
}
};

View File

@ -116,7 +116,7 @@
formData = $.param(formData);
formAction = form.attr('action');
issuesUrl = formAction;
issuesUrl += "" + (formAction.indexOf('?') < 0 ? '?' : '&');
issuesUrl += "" + (formAction.indexOf('?') === -1 ? '?' : '&');
issuesUrl += formData;
return gl.utils.visitUrl(issuesUrl);
};

View File

@ -79,12 +79,12 @@ require('./comparison_pane');
<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'>

View File

@ -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();
}

View File

@ -296,5 +296,58 @@
* @returns {Boolean}
*/
w.gl.utils.convertPermissionToBoolean = permission => permission === 'true';
/**
* Back Off exponential algorithm
* backOff :: (Function<next, stop>, Number) -> Promise<Any, Error>
*
* @param {Function<next, stop>} fn function to be called
* @param {Number} timeout
* @return {Promise<Any, Error>}
* @example
* ```
* backOff(function (next, stop) {
* // Let's perform this function repeatedly for 60s or for the timeout provided.
*
* ourFunction()
* .then(function (result) {
* // continue if result is not what we need
* next();
*
* // when result is what we need let's stop with the repetions and jump out of the cycle
* stop(result);
* })
* .catch(function (error) {
* // if there is an error, we need to stop this with an error.
* stop(error);
* })
* }, 60000)
* .then(function (result) {})
* .catch(function (error) {
* // deal with errors passed to stop()
* })
* ```
*/
w.gl.utils.backOff = (fn, timeout = 60000) => {
const maxInterval = 32000;
let nextInterval = 2000;
const startTime = Date.now();
return new Promise((resolve, reject) => {
const stop = arg => ((arg instanceof Error) ? reject(arg) : resolve(arg));
const next = () => {
if (Date.now() - startTime < timeout) {
setTimeout(fn.bind(null, next, stop), nextInterval);
nextInterval = Math.min(nextInterval + nextInterval, maxInterval);
} else {
reject(new Error('BACKOFF_TIMEOUT'));
}
};
fn(next, stop);
});
};
})(window);
}).call(window);

View File

@ -0,0 +1,10 @@
/**
* exports HTTP status codes
*/
const statusCodes = {
NO_CONTENT: 204,
OK: 200,
};
module.exports = statusCodes;

View File

@ -83,7 +83,7 @@ require('./smart_interval');
return function() {
var page;
page = $('body').data('page').split(':').last();
if (allowedPages.indexOf(page) < 0) {
if (allowedPages.indexOf(page) === -1) {
return _this.clearEventListeners();
}
};
@ -233,7 +233,7 @@ require('./smart_interval');
}
$('.ci_widget').hide();
allowed_states = ["failed", "canceled", "running", "pending", "success", "success_with_warnings", "skipped", "not_found"];
if (indexOf.call(allowed_states, state) >= 0) {
if (indexOf.call(allowed_states, state) !== -1) {
$('.ci_widget.ci-' + state).show();
switch (state) {
case "failed":

View File

@ -21,9 +21,9 @@
});
$(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)

View File

@ -78,7 +78,6 @@
} else {
$(element).find('.assignee-icon').empty();
}
return $(element).effect('highlight');
};
function Milestone() {

View File

@ -39,7 +39,7 @@
$value = $block.find('.value');
$loading = $block.find('.block-loading').fadeOut();
if (issueUpdateURL) {
milestoneLinkTemplate = _.template('<a href="/<%- namespace %>/<%- path %>/milestones/<%- iid %>" class="bold has-tooltip" data-container="body" title="<%- remaining %>"><%- title %></a>');
milestoneLinkTemplate = _.template('<a href="/<%- full_path %>/milestones/<%- iid %>" class="bold has-tooltip" data-container="body" title="<%- remaining %>"><%- title %></a>');
milestoneLinkNoneTemplate = '<span class="no-value">None</span>';
collapsedSidebarLabelTemplate = _.template('<span class="has-tooltip" data-container="body" title="<%- remaining %>" data-placement="left"> <%- title %> </span>');
}
@ -181,8 +181,7 @@
$selectbox.hide();
$value.css('display', '');
if (data.milestone != null) {
data.milestone.namespace = _this.currentProject.namespace;
data.milestone.path = _this.currentProject.path;
data.milestone.full_path = _this.currentProject.full_path;
data.milestone.remaining = gl.utils.timeFor(data.milestone.due_date);
$value.html(milestoneLinkTemplate(data.milestone));
return $sidebarCollapsedValue.find('span').html(collapsedSidebarLabelTemplate(data.milestone));

View File

@ -1,4 +1,4 @@
/* eslint-disable func-names, space-before-function-paren, no-var, one-var, prefer-rest-params, max-len, vars-on-top, wrap-iife, consistent-return, comma-dangle, one-var-declaration-per-line, quotes, no-return-assign, prefer-arrow-callback, prefer-template, no-shadow, no-else-return, max-len */
/* eslint-disable func-names, space-before-function-paren, no-var, one-var, prefer-rest-params, max-len, vars-on-top, wrap-iife, consistent-return, comma-dangle, one-var-declaration-per-line, quotes, no-return-assign, prefer-arrow-callback, prefer-template, no-shadow, no-else-return, max-len, object-shorthand */
(function() {
var bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; },
indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i += 1) { if (i in this && this[i] === item) return i; } return -1; };
@ -20,15 +20,35 @@
};
NewBranchForm.prototype.init = function() {
if (this.name.val().length > 0) {
if (this.name.length && this.name.val().length > 0) {
return this.name.trigger('blur');
}
};
NewBranchForm.prototype.setupAvailableRefs = function(availableRefs) {
return this.ref.autocomplete({
source: availableRefs,
minLength: 1
var $branchSelect = $('.js-branch-select');
$branchSelect.glDropdown({
data: availableRefs,
filterable: true,
filterByText: true,
remote: false,
fieldName: $branchSelect.data('field-name'),
selectable: true,
isSelectable: function(branch, $el) {
return !$el.hasClass('is-active');
},
text: function(branch) {
return branch;
},
id: function(branch) {
return branch;
},
toggleLabel: function(branch) {
if (branch) {
return branch;
}
}
});
};
@ -61,7 +81,7 @@
var errorMessage, errors, formatter, unique, validator;
this.branchNameError.empty();
unique = function(values, value) {
if (indexOf.call(values, value) < 0) {
if (indexOf.call(values, value) === -1) {
values.push(value);
}
return values;

View File

@ -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();

View File

@ -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;

View File

@ -84,13 +84,14 @@
}
$(function() {
$(document).on('focusout.ssh_key', '#key_key', function() {
$(document).on('input.ssh_key', '#key_key', function() {
const $title = $('#key_title');
const comment = $(this).val().match(/^\S+ \S+ (.+)\n?$/);
if (comment && comment.length > 1 && $title.val() === '') {
// Extract the SSH Key title from its comment
if (comment && comment.length > 1) {
return $title.val(comment[1]).change();
}
// Extract the SSH Key title from its comment
});
if (global.utils.getPagePath() === 'profiles') {
return new Profile();

View File

@ -1,3 +1,2 @@
// require everything else in this directory
function requireAll(context) { return context.keys().map(context); }
requireAll(require.context('.', false, /^\.\/(?!profile_bundle).*\.(js|es6)$/));
require('./gl_crop');
require('./profile');

View File

@ -116,7 +116,7 @@
if ($('input[name="ref"]').length) {
var $form = $dropdown.closest('form');
var action = $form.attr('action');
var divider = action.indexOf('?') < 0 ? '?' : '&';
var divider = action.indexOf('?') === -1 ? '?' : '&';
gl.utils.visitUrl(action + '' + divider + '' + $form.serialize());
}
}

View File

@ -36,6 +36,9 @@
// Do not update if one dropdown has not selected any option
if (!($allowedToMergeInput.length && $allowedToPushInput.length)) return;
this.$allowedToMergeDropdown.disable();
this.$allowedToPushDropdown.disable();
$.ajax({
type: 'POST',
url: this.$wrap.data('url'),
@ -53,13 +56,13 @@
}]
}
},
success: () => {
this.$wrap.effect('highlight');
},
error() {
$.scrollTo(0);
new Flash('Failed to update branch!');
}
}).always(() => {
this.$allowedToMergeDropdown.enable();
this.$allowedToPushDropdown.enable();
});
}
};

View File

@ -1,3 +1,5 @@
// require everything else in this directory
function requireAll(context) { return context.keys().map(context); }
requireAll(require.context('.', false, /^\.\/(?!protected_branches_bundle).*\.(js|es6)$/));
require('./protected_branch_access_dropdown');
require('./protected_branch_create');
require('./protected_branch_dropdown');
require('./protected_branch_edit');
require('./protected_branch_edit_list');

View File

@ -1,10 +1,6 @@
/* eslint-disable func-names, space-before-function-paren, prefer-arrow-callback, no-var, quotes, max-len */
/* global ace */
// require everything else in this directory
function requireAll(context) { return context.keys().map(context); }
requireAll(require.context('.', false, /^\.\/(?!snippet_bundle).*\.(js|es6)$/));
(function() {
$(function() {
var editor = ace.edit("editor");

View File

@ -1,3 +1,4 @@
/* global Flash */
require('vendor/task_list');
class TaskList {
@ -6,6 +7,16 @@ class TaskList {
this.dataType = options.dataType;
this.fieldName = options.fieldName;
this.onSuccess = options.onSuccess || (() => {});
this.onError = function showFlash(response) {
let errorMessages = '';
if (response.responseJSON) {
errorMessages = response.responseJSON.errors.join(' ');
}
return new Flash(errorMessages || 'Update failed', 'alert');
};
this.init();
}
@ -32,6 +43,7 @@ class TaskList {
url: $target.data('update-url') || $('form.js-issuable-update').attr('action'),
data: patchData,
success: this.onSuccess,
error: this.onError,
});
}
}

View File

@ -0,0 +1,58 @@
/* global Cookies */
const userCalloutElementName = '.user-callout';
const closeButton = '.close-user-callout';
const userCalloutBtn = '.user-callout-btn';
const userCalloutSvgAttrName = 'callout-svg';
const USER_CALLOUT_COOKIE = 'user_callout_dismissed';
const USER_CALLOUT_TEMPLATE = `
<div class="bordered-box landing content-block">
<button class="btn btn-default close close-user-callout" type="button">
<i class="fa fa-times dismiss-icon"></i>
</button>
<div class="row">
<div class="col-sm-3 col-xs-12 svg-container">
</div>
<div class="col-sm-8 col-xs-12 inner-content">
<h4>
Customize your experience
</h4>
<p>
Change syntax themes, default project pages, and more in preferences.
</p>
<a class="btn user-callout-btn" href="/profile/preferences">Check it out</a>
</div>
</div>
</div>`;
class UserCallout {
constructor() {
this.isCalloutDismissed = Cookies.get(USER_CALLOUT_COOKIE);
this.userCalloutBody = $(userCalloutElementName);
this.userCalloutSvg = $(userCalloutElementName).attr(userCalloutSvgAttrName);
$(userCalloutElementName).removeAttr(userCalloutSvgAttrName);
this.init();
}
init() {
const $template = $(USER_CALLOUT_TEMPLATE);
if (!this.isCalloutDismissed || this.isCalloutDismissed === 'false') {
$template.find('.svg-container').append(this.userCalloutSvg);
this.userCalloutBody.append($template);
$template.find(closeButton).on('click', e => this.dismissCallout(e));
$template.find(userCalloutBtn).on('click', e => this.dismissCallout(e));
}
}
dismissCallout(e) {
Cookies.set(USER_CALLOUT_COOKIE, 'true');
const $currentTarget = $(e.currentTarget);
if ($currentTarget.hasClass('close-user-callout')) {
this.userCalloutBody.empty();
}
}
}
module.exports = UserCallout;

View File

@ -1,3 +1 @@
// require everything else in this directory
function requireAll(context) { return context.keys().map(context); }
requireAll(require.context('.', false, /^\.\/(?!users_bundle).*\.(js|es6)$/));
require('./calendar');

View File

@ -1,5 +1,5 @@
/* global Vue, Flash, gl */
/* eslint-disable no-param-reassign */
/* eslint-disable no-param-reassign, no-alert */
((gl) => {
gl.VuePipelineActions = Vue.extend({
@ -16,21 +16,33 @@
download(name) {
return `Download ${name} artifacts`;
},
/**
* Shows a dialog when the user clicks in the cancel button.
* We need to prevent the default behavior and stop propagation because the
* link relies on UJS.
*
* @param {Event} event
*/
confirmAction(event) {
if (!confirm('Are you sure you want to cancel this pipeline?')) {
event.preventDefault();
event.stopPropagation();
}
},
},
template: `
<td class="pipeline-actions hidden-xs">
<div class="controls pull-right">
<div class="btn-group inline">
<div class="btn-group">
<td class="pipeline-actions">
<div class="pull-right">
<div class="btn-group">
<div class="btn-group" v-if="actions">
<button
v-if='actions'
class="dropdown-toggle btn btn-default has-tooltip js-pipeline-dropdown-manual-actions"
data-toggle="dropdown"
title="Manual job"
data-placement="top"
aria-label="Manual job"
>
<span v-html='svgs.iconPlay' aria-hidden="true"></span>
aria-label="Manual job">
<span v-html="svgs.iconPlay" aria-hidden="true"></span>
<i class="fa fa-caret-down" aria-hidden="true"></i>
</button>
<ul class="dropdown-menu dropdown-menu-align-right">
@ -38,23 +50,21 @@
<a
rel="nofollow"
data-method="post"
:href='action.path'
>
<span v-html='svgs.iconPlay' aria-hidden="true"></span>
:href="action.path">
<span v-html="svgs.iconPlay" aria-hidden="true"></span>
<span>{{action.name}}</span>
</a>
</li>
</ul>
</div>
<div class="btn-group">
<div class="btn-group" v-if="artifacts">
<button
v-if='artifacts'
class="dropdown-toggle btn btn-default build-artifacts has-tooltip js-pipeline-dropdown-download"
title="Artifacts"
data-placement="top"
data-toggle="dropdown"
aria-label="Artifacts"
>
aria-label="Artifacts">
<i class="fa fa-download" aria-hidden="true"></i>
<i class="fa fa-caret-down" aria-hidden="true"></i>
</button>
@ -62,41 +72,39 @@
<li v-for='artifact in pipeline.details.artifacts'>
<a
rel="nofollow"
download
:href='artifact.path'
>
:href="artifact.path">
<i class="fa fa-download" aria-hidden="true"></i>
<span>{{download(artifact.name)}}</span>
</a>
</li>
</ul>
</div>
</div>
<div class="cancel-retry-btns inline">
<a
v-if='pipeline.flags.retryable'
class="btn has-tooltip"
title="Retry"
rel="nofollow"
data-method="post"
data-placement="top"
data-toggle="dropdown"
:href='pipeline.retry_path'
aria-label="Retry">
<i class="fa fa-repeat" aria-hidden="true"></i>
</a>
<a
v-if='pipeline.flags.cancelable'
class="btn btn-remove has-tooltip"
title="Cancel"
rel="nofollow"
data-method="post"
data-placement="top"
data-toggle="dropdown"
:href='pipeline.cancel_path'
aria-label="Cancel">
<i class="fa fa-remove" aria-hidden="true"></i>
</a>
<div class="btn-group" v-if="pipeline.flags.retryable">
<a
class="btn btn-default btn-retry has-tooltip"
title="Retry"
rel="nofollow"
data-method="post"
data-placement="top"
data-toggle="dropdown"
:href='pipeline.retry_path'
aria-label="Retry">
<i class="fa fa-repeat" aria-hidden="true"></i>
</a>
</div>
<div class="btn-group" v-if="pipeline.flags.cancelable">
<a
class="btn btn-remove has-tooltip"
title="Cancel"
rel="nofollow"
data-method="post"
data-placement="top"
data-toggle="dropdown"
:href='pipeline.cancel_path'
aria-label="Cancel">
<i class="fa fa-remove" aria-hidden="true"></i>
</a>
</div>
</div>
</div>
</td>

View File

@ -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: `

View File

@ -23,6 +23,13 @@
required: true,
},
},
updated() {
if (this.builds) {
this.stopDropdownClickPropagation();
}
},
methods: {
fetchBuilds(e) {
const areaExpanded = e.currentTarget.attributes['aria-expanded'];
@ -37,17 +44,19 @@
return flash;
});
},
keepGraph(e) {
const { target } = e;
if (target.className.indexOf('js-ci-action-icon') >= 0) return null;
if (
target.parentElement &&
(target.parentElement.className.indexOf('js-ci-action-icon') >= 0)
) return null;
return e.stopPropagation();
/**
* When the user right clicks or cmd/ctrl + click in the job name
* the dropdown should not be closed and the link should open in another tab,
* so we stop propagation of the click event inside the dropdown.
*
* Since this component is rendered multiple times per page we need to guarantee we only
* target the click event of this component.
*/
stopDropdownClickPropagation() {
$(this.$el.querySelectorAll('.js-builds-dropdown-list a.mini-pipeline-graph-dropdown-item')).on('click', (e) => {
e.stopPropagation();
});
},
},
computed: {
@ -76,13 +85,13 @@
template: `
<div>
<button
@click='fetchBuilds($event)'
@click="fetchBuilds($event)"
:class="triggerButtonClass"
:title='stage.title'
:title="stage.title"
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>
@ -90,7 +99,6 @@
<ul class="dropdown-menu mini-pipeline-graph-dropdown-menu js-builds-dropdown-container">
<div class="arrow-up" aria-hidden="true"></div>
<div
@click='keepGraph($event)'
:class="dropdownClass"
class="js-builds-dropdown-list scrollable-menu"
v-html="buildsOrSpinner"

View File

@ -54,7 +54,7 @@ require('../lib/utils/datetime_utility');
},
},
template: `
<td>
<td class="pipelines-time-ago">
<p class="duration" v-if='duration'>
<span v-html='svgs.iconTimer'></span>
{{duration}}
@ -65,8 +65,7 @@ require('../lib/utils/datetime_utility');
data-toggle="tooltip"
data-placement="top"
data-container="body"
:data-original-title='localTimeFinished'
>
:data-original-title='localTimeFinished'>
{{timeStopped.words}}
</time>
</p>

View File

@ -44,7 +44,7 @@ require('./pipelines_table_row');
<th class="js-pipeline-commit pipeline-commit">Commit</th>
<th class="js-pipeline-stages pipeline-stages">Stages</th>
<th class="js-pipeline-date pipeline-date"></th>
<th class="js-pipeline-actions pipeline-actions hidden-xs"></th>
<th class="js-pipeline-actions pipeline-actions"></th>
</tr>
</thead>
<tbody>

View File

@ -23,8 +23,8 @@ window.Vue = require('vue');
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 +57,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 +64,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;
}
},

View File

@ -2,7 +2,6 @@
* This is a manifest file that'll automatically include all the stylesheets available in this directory
* and any sub-directories. You're free to add application-wide styles to this file and they'll appear at
* the top of the compiled file, but it's generally better to create a new file per style scope.
*= require jquery-ui/autocomplete
*= require jquery.atwho
*= require select2
*= require_self

View File

@ -229,7 +229,7 @@
.controls {
float: right;
margin-top: 8px;
padding-bottom: 7px;
padding-bottom: 8px;
border-bottom: 1px solid $border-color;
}
}

View File

@ -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) {

View File

@ -271,6 +271,7 @@ span.idiff {
font-size: 13px;
line-height: 28px;
display: inline-block;
float: none;
}
}
}

View File

@ -26,6 +26,11 @@
.filtered-search-container {
display: -webkit-flex;
display: flex;
@media (max-width: $screen-xs-min) {
-webkit-flex-direction: column;
flex-direction: column;
}
}
.filtered-search-input-container {
@ -34,6 +39,20 @@
position: relative;
width: 100%;
@media (max-width: $screen-xs-min) {
-webkit-flex: 1 1 100%;
flex: 1 1 100%;
margin-bottom: 10px;
.dropdown-menu {
width: auto;
left: 0;
right: 0;
max-width: none;
min-width: 100%;
}
}
.form-control {
padding-left: 25px;
padding-right: 25px;
@ -79,6 +98,31 @@
overflow: auto;
}
@media (max-width: $screen-xs-min) {
.issues-details-filters {
padding: 0 0 10px;
background-color: $white-light;
border-top: 0;
}
.filter-dropdown-container {
.dropdown-toggle,
.dropdown {
width: 100%;
}
.dropdown {
margin-left: 0;
}
.fa-chevron-down {
position: absolute;
right: 10px;
top: 10px;
}
}
}
%filter-dropdown-item-btn-hover {
background-color: $dropdown-hover-color;
color: $white-light;
@ -148,4 +192,4 @@
.filter-dropdown-loading {
padding: 8px 16px;
}
}

View File

@ -148,16 +148,11 @@ header {
}
.header-logo {
position: absolute;
left: 50%;
display: inline-block;
margin: 0 8px 0 3px;
position: relative;
top: 7px;
transition-duration: .3s;
z-index: 999;
#logo {
position: relative;
left: -50%;
}
svg,
img {
@ -167,15 +162,6 @@ header {
&:hover {
cursor: pointer;
}
@media (max-width: $screen-xs-max) {
right: 20px;
left: auto;
#logo {
left: auto;
}
}
}
.title {
@ -183,7 +169,6 @@ header {
padding-right: 20px;
margin: 0;
font-size: 18px;
max-width: 385px;
display: inline-block;
line-height: $header-height;
font-weight: normal;
@ -193,14 +178,18 @@ header {
vertical-align: top;
white-space: nowrap;
@media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) {
max-width: 300px;
}
@media (max-width: $screen-xs-max) {
max-width: 190px;
}
@media (min-width: $screen-sm-min) and (max-width: $screen-md-max) {
max-width: 428px;
}
@media (min-width: $screen-lg-min) {
max-width: 685px;
}
a {
color: $gl-text-color;

View File

@ -2,17 +2,6 @@
font-family: $regular_font;
font-size: $font-size-base;
&.ui-autocomplete {
border-color: $jq-ui-border;
padding: 0;
margin-top: 2px;
z-index: 1001;
.ui-menu-item a {
padding: 4px 10px;
}
}
.ui-state-default {
border: 1px solid $white-light;
background: $white-light;

View File

@ -20,6 +20,8 @@ $dark-highlight-bg: #ffe792;
$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;
@ -139,9 +141,37 @@ $dark-il: #de935f;
}
}
.diff-line-num {
&.is-over,
&.hll:not(.empty-cell).is-over {
background-color: $dark-over-bg;
border-color: darken($dark-over-bg, 5%);
a {
color: darken($dark-over-bg, 15%);
}
}
}
.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

View File

@ -13,6 +13,8 @@ $monokai-line-empty-bg: #49483e;
$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);
@ -139,9 +141,37 @@ $monokai-gi: #a6e22e;
}
}
.diff-line-num {
&.is-over,
&.hll:not(.empty-cell).is-over {
background-color: $monokai-over-bg;
border-color: darken($monokai-over-bg, 5%);
a {
color: darken($monokai-over-bg, 15%);
}
}
}
.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

View File

@ -17,6 +17,8 @@ $solarized-dark-line-color-new: #5a766c;
$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;
@ -143,9 +145,37 @@ $solarized-dark-il: #2aa198;
}
}
.diff-line-num {
&.is-over,
&.hll:not(.empty-cell).is-over {
background-color: $solarized-dark-over-bg;
border-color: darken($solarized-dark-over-bg, 5%);
a {
color: darken($solarized-dark-over-bg, 15%);
}
}
}
.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

View File

@ -18,6 +18,9 @@ $solarized-light-line-color-new: #a1a080;
$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;
@ -150,9 +153,37 @@ $solarized-light-il: #2aa198;
}
}
.diff-line-num {
&.is-over,
&.hll:not(.empty-cell).is-over {
background-color: $solarized-light-over-bg;
border-color: darken($solarized-light-over-bg, 5%);
a {
color: darken($solarized-light-over-bg, 15%);
}
}
}
.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

View File

@ -7,6 +7,9 @@ $white-code-color: $gl-text-color;
$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;
@ -123,12 +126,38 @@ $white-gc-bg: #eaf2f5;
}
}
&.is-over,
&.hll:not(.empty-cell).is-over {
background-color: $white-over-bg;
border-color: darken($white-over-bg, 5%);
a {
color: darken($white-over-bg, 15%);
}
}
&.hll:not(.empty-cell) {
background-color: $line-number-select;
border-color: $line-select-yellow-dark;
}
}
&: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;

View File

@ -89,6 +89,10 @@
.diff-line-num {
width: 50px;
a {
transition: none;
}
}
.line_holder td {
@ -109,10 +113,6 @@
td.line_content.parallel {
width: 46%;
}
.add-diff-note {
margin-left: -65px;
}
}
.old_line,
@ -133,8 +133,13 @@
width: 35px;
font-weight: normal;
&:hover {
text-decoration: underline;
&[disabled] {
cursor: default;
&:hover,
&:active {
text-decoration: none;
}
}
}
}

View File

@ -15,112 +15,97 @@
padding-top: 20px;
}
@media (max-width: $screen-xs-max) {
.environments-container {
.environments-container {
.table-holder {
width: 100%;
overflow: auto;
}
}
.environments {
table-layout: fixed;
.environments-commit,
.environments-actions,
.environments-deploy,
.environments-build,
.environments-date {
position: static;
float: none;
display: table-cell;
}
.environments-commit,
.environments-actions {
width: 20%;
}
.environments-date {
width: 10%;
}
.environments-name,
.environments-deploy,
.environments-build {
width: 15%;
}
.environment-name,
.environments-build-cell,
.deployment-column {
word-break: break-all;
}
.deployment-column {
.avatar {
float: none;
.table.ci-table {
.environments-actions {
min-width: 200px;
}
}
.btn-group {
.environments-commit,
.environments-actions {
width: 20%;
}
> a {
.environments-date {
width: 10%;
}
.environments-name,
.environments-deploy,
.environments-build {
width: 15%;
}
.deployment-column {
> span {
word-break: break-all;
}
.avatar {
float: none;
}
}
.btn-group {
> a {
color: $gl-text-color-secondary;
}
svg path {
fill: $gl-text-color-secondary;
}
.dropdown {
outline: none;
}
}
.commit-title {
margin: 0;
}
.avatar-image-container {
text-decoration: none;
}
.icon-play {
height: 13px;
width: 12px;
}
.external-url,
.dropdown-new {
color: $gl-text-color-secondary;
}
svg path {
fill: $gl-text-color-secondary;
.dropdown-menu {
.fa {
margin-right: 6px;
color: $gl-text-color-secondary;
}
}
.dropdown {
outline: none;
.build-link,
.branch-name {
color: $gl-text-color;
}
}
.commit-title {
margin: 0;
}
.avatar-image-container {
text-decoration: none;
}
.icon-play {
height: 13px;
width: 12px;
}
.external-url,
.dropdown-new {
color: $gl-text-color-secondary;
}
.dropdown-menu {
.fa {
margin-right: 6px;
.stop-env-link,
.external-url {
color: $gl-text-color-secondary;
.stop-env-icon {
font-size: 14px;
}
}
}
.build-link,
.branch-name {
color: $gl-text-color;
}
.stop-env-link,
.external-url {
color: $gl-text-color-secondary;
.stop-env-icon {
font-size: 14px;
}
}
.deployment {
.build-column {
.deployment .build-column {
.build-link {
color: $gl-text-color;
}
@ -129,34 +114,32 @@
float: none;
}
}
}
.folder-icon {
margin-right: 3px;
color: $gl-text-color-secondary;
display: inline-block;
.fa:nth-child(1) {
.folder-icon {
margin-right: 3px;
color: $gl-text-color-secondary;
display: inline-block;
.fa:nth-child(1) {
margin-right: 3px;
}
}
}
.folder-name {
cursor: pointer;
color: $gl-text-color-secondary;
display: inline-block;
}
}
.folder-name {
cursor: pointer;
color: $gl-text-color-secondary;
display: inline-block;
}
.table.ci-table.environments {
.icon-container {
width: 20px;
text-align: center;
}
.icon-container {
width: 20px;
text-align: center;
}
.branch-commit {
.commit-id {
margin-right: 0;
.branch-commit {
.commit-id {
margin-right: 0;
}
}
}
}

View File

@ -452,36 +452,37 @@ ul.notes {
* Line note button on the side of diffs
*/
.diff-file tr.line_holder {
@mixin show-add-diff-note {
display: inline-block;
.add-diff-note {
display: none;
margin-top: -2px;
border-radius: 50%;
background: $white-light;
padding: 1px 5px;
font-size: 12px;
color: $gl-link-color;
margin-left: -55px;
position: absolute;
z-index: 10;
width: 23px;
height: 23px;
border: 1px solid $border-color;
transition: transform .1s ease-in-out;
&:hover {
background: $gl-info;
color: $white-light;
transform: scale(1.15);
}
.add-diff-note {
margin-top: -8px;
border-radius: 40px;
background: $white-light;
padding: 4px;
font-size: 16px;
color: $gl-link-color;
margin-left: -56px;
position: absolute;
z-index: 10;
width: 32px;
// "hide" it by default
display: none;
&:hover {
background: $gl-info;
color: $white-light;
@include show-add-diff-note;
}
&:active {
outline: 0;
}
}
// "show" the icon also if we just hover somewhere over the line
&:hover > td {
.diff-file {
.is-over {
.add-diff-note {
@include show-add-diff-note;
display: inline-block;
}
}
}

View File

@ -13,21 +13,16 @@
white-space: nowrap;
}
.table-holder {
width: 100%;
overflow: auto;
}
.commit-title {
margin: 0;
}
.controls {
white-space: nowrap;
}
.btn {
margin: 4px;
}
.table.ci-table {
min-width: 1200px;
table-layout: fixed;
.label {
margin-bottom: 3px;
@ -37,16 +32,72 @@
color: $black;
}
.pipeline-date,
.pipeline-status {
width: 10%;
.stage-cell {
min-width: 130px; // Guarantees we show at least 4 stages in line
width: 20%;
}
.pipelines-time-ago {
text-align: right;
}
.pipeline-info,
.pipeline-commit,
.pipeline-stages,
.pipeline-actions {
width: 20%;
padding-right: 0;
min-width: 170px; //Guarantees buttons don't break in several lines.
.btn-default {
color: $gl-text-color-secondary;
}
.btn.btn-retry:hover,
.btn.btn-retry:focus {
border-color: $gray-darkest;
background-color: $white-normal;
}
svg path {
fill: $gl-text-color-secondary;
}
.dropdown-menu {
max-height: 250px;
overflow-y: auto;
}
.dropdown-toggle,
.dropdown-menu {
color: $gl-text-color-secondary;
.fa {
color: $gl-text-color-secondary;
font-size: 14px;
}
svg,
.fa {
margin-right: 0;
}
}
.btn-group {
&.open {
.btn-default {
background-color: $white-normal;
border-color: $border-white-normal;
}
}
.btn {
.icon-play {
height: 13px;
width: 12px;
}
}
}
.tooltip {
white-space: nowrap;
}
}
}
}
@ -54,6 +105,7 @@
@media (max-width: $screen-md-max) {
.content-list {
&.pipelines,
&.environments-container,
&.builds-content-list {
width: 100%;
overflow: auto;
@ -61,27 +113,10 @@
}
}
.content-list.pipelines .table-holder {
min-height: 300px;
}
.pipeline-holder {
width: 100%;
overflow: auto;
}
.table.ci-table {
min-width: 900px;
&.pipeline {
min-width: 650px;
}
&.builds-page {
tr {
height: 71px;
}
&.builds-page tr {
height: 71px;
}
tr {
@ -94,12 +129,16 @@
padding: 10px 8px;
}
td.environments-actions {
padding-right: 0;
}
td.stage-cell {
padding: 10px 0;
}
.commit-link {
padding: 9px 8px 10px;
padding: 9px 8px 10px 2px;
}
}
@ -206,72 +245,8 @@
}
}
.pipeline-actions {
min-width: 140px;
.btn {
margin: 0;
color: $gl-text-color-secondary;
}
.cancel-retry-btns {
vertical-align: middle;
.btn:not(:first-child) {
margin-left: 8px;
}
}
.dropdown-menu {
max-height: 250px;
overflow-y: auto;
}
.dropdown-toggle,
.dropdown-menu {
color: $gl-text-color-secondary;
.fa {
color: $gl-text-color-secondary;
font-size: 14px;
}
svg,
.fa {
margin-right: 0;
}
}
.btn-remove {
color: $white-light;
}
.btn-group {
&.open {
.btn-default {
background-color: $white-normal;
border-color: $border-white-normal;
}
}
.btn {
.icon-play {
height: 13px;
width: 12px;
}
}
}
.tooltip {
white-space: nowrap;
}
}
.build-link {
a {
color: $gl-text-color;
}
.build-link a {
color: $gl-text-color;
}
.btn-group.open .dropdown-toggle {
@ -335,31 +310,8 @@
}
.tab-pane {
&.pipelines {
.ci-table {
min-width: 900px;
}
.content-list.pipelines {
overflow: auto;
}
.stage {
max-width: 100px;
width: 100px;
}
.pipeline-actions {
min-width: initial;
}
}
&.builds {
.ci-table {
tr {
height: 71px;
}
}
&.builds .ci-table tr {
height: 71px;
}
}

View File

@ -277,3 +277,41 @@ table.u2f-registrations {
padding-left: 18px;
}
}
.user-callout {
margin: 24px auto 0;
.bordered-box {
border: 1px solid $border-color;
border-radius: $border-radius-default;
}
.landing {
margin-bottom: $gl-padding;
.close {
margin-right: 20px;
}
.dismiss-icon {
float: right;
cursor: pointer;
color: $cycle-analytics-dismiss-icon-color;
}
.svg-container {
text-align: center;
svg {
width: 136px;
height: 136px;
}
}
}
@media(max-width: $screen-xs-max) {
.inner-content {
padding-left: 30px;
}
}
}

View File

@ -638,14 +638,6 @@ pre.light-well {
margin: 0;
}
.activity-filter-block {
.controls {
padding-bottom: 7px;
margin-top: 8px;
border-bottom: 1px solid $border-color;
}
}
.commits-search-form {
.input-short {
min-width: 200px;

View File

@ -83,6 +83,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
:akismet_api_key,
:akismet_enabled,
:container_registry_token_expire_delay,
:default_artifacts_expire_in,
:default_branch_protection,
:default_group_visibility,
:default_project_visibility,

View File

@ -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" }
@ -287,19 +279,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

View File

@ -26,6 +26,23 @@ module IssuableActions
private
def render_conflict_response
respond_to do |format|
format.html do
@conflict = true
render :edit
end
format.json do
render json: {
errors: [
"Someone edited this #{issuable.human_class_name} at the same time you did. Please refresh your browser and make sure your changes will not unintentionally remove theirs."
]
}, status: 409
end
end
end
def labels
@labels ||= LabelsFinder.new(current_user, project_id: @project.id).execute
end

View File

@ -33,6 +33,7 @@ module ServiceParams
:issues_url,
:jira_issue_transition_id,
:merge_requests_events,
:mock_service_url,
:namespace,
:new_issue_url,
:notify,

View File

@ -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!

View File

@ -134,8 +134,7 @@ class Projects::IssuesController < Projects::ApplicationController
end
rescue ActiveRecord::StaleObjectError
@conflict = true
render :edit
render_conflict_response
end
def referenced_merge_requests

View File

@ -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
@ -296,22 +298,21 @@ class Projects::MergeRequestsController < Projects::ApplicationController
def update
@merge_request = MergeRequests::UpdateService.new(project, current_user, merge_request_params).execute(@merge_request)
if @merge_request.valid?
respond_to do |format|
format.html do
redirect_to([@merge_request.target_project.namespace.becomes(Namespace),
@merge_request.target_project, @merge_request])
end
format.json do
render json: @merge_request.to_json(include: { milestone: {}, assignee: { methods: :avatar_url }, labels: { methods: :text_color } }, methods: [:task_status, :task_status_short])
respond_to do |format|
format.html do
if @merge_request.valid?
redirect_to([@merge_request.target_project.namespace.becomes(Namespace), @merge_request.target_project, @merge_request])
else
render :edit
end
end
else
render "edit"
format.json do
render json: @merge_request.to_json(include: { milestone: {}, assignee: { methods: :avatar_url }, labels: { methods: :text_color } }, methods: [:task_status, :task_status_short])
end
end
rescue ActiveRecord::StaleObjectError
@conflict = true
render :edit
render_conflict_response
end
def remove_wip
@ -327,8 +328,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController
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
@ -340,9 +341,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
@ -354,7 +355,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
@ -365,7 +366,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
@ -382,8 +383,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
@ -673,8 +674,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

View File

@ -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

View File

@ -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,

View File

@ -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

View File

@ -15,4 +15,11 @@ module BuildsHelper
log_state: @build.trace_with_state[:state].to_s
}
end
def build_failed_issue_options
{
title: "Build Failed ##{@build.id}",
description: namespace_project_build_url(@project.namespace, @project, @build)
}
end
end

View File

@ -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,
@ -34,7 +34,7 @@ module ButtonHelper
content_tag (append_link ? :a : :span), protocol,
class: klass,
href: (project.http_url_to_repo if append_link),
href: (project.http_url_to_repo(current_user) if append_link),
data: {
html: true,
placement: placement,

View File

@ -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
@ -52,7 +52,7 @@ module IssuablesHelper
field_name: 'issuable_template',
selected: selected_template(issuable),
project_path: ref_project.path,
namespace_path: ref_project.namespace.path
namespace_path: ref_project.namespace.full_path
}
}

View File

@ -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))

View File

@ -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)}"

View File

@ -241,7 +241,7 @@ module ProjectsHelper
when 'ssh'
project.ssh_url_to_repo
else
project.http_url_to_repo
project.http_url_to_repo(current_user)
end
end

View File

@ -76,6 +76,12 @@ class ApplicationSetting < ActiveRecord::Base
presence: true,
numericality: { only_integer: true, greater_than: 0 }
validates :max_artifacts_size,
presence: true,
numericality: { only_integer: true, greater_than: 0 }
validates :default_artifacts_expire_in, presence: true, duration: true
validates :container_registry_token_expire_delay,
presence: true,
numericality: { only_integer: true, greater_than: 0 }
@ -168,6 +174,7 @@ class ApplicationSetting < ActiveRecord::Base
after_sign_up_text: nil,
akismet_enabled: false,
container_registry_token_expire_delay: 5,
default_artifacts_expire_in: '30 days',
default_branch_protection: Settings.gitlab['default_branch_protection'],
default_project_visibility: Settings.gitlab.default_projects_features['visibility_level'],
default_projects_limit: Settings.gitlab['default_projects_limit'],
@ -201,9 +208,9 @@ class ApplicationSetting < ActiveRecord::Base
sign_in_text: nil,
signin_enabled: Settings.gitlab['signin_enabled'],
signup_enabled: Settings.gitlab['signup_enabled'],
terminal_max_session_time: 0,
two_factor_grace_period: 48,
user_default_external: false,
terminal_max_session_time: 0
user_default_external: false
}
end
@ -215,6 +222,14 @@ class ApplicationSetting < ActiveRecord::Base
create(defaults)
end
def self.human_attribute_name(attr, _options = {})
if attr == :default_artifacts_expire_in
'Default artifacts expiration'
else
super
end
end
def home_page_url_column_exist
ActiveRecord::Base.connection.column_exists?(:application_settings, :home_page_url)
end

View File

@ -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)
@ -484,7 +475,7 @@ module Ci
def artifacts_expire_in=(value)
self.artifacts_expire_at =
if value
Time.now + ChronicDuration.parse(value)
ChronicDuration.parse(value)&.seconds&.from_now
end
end

View File

@ -0,0 +1,30 @@
class Uniquify
# Return a version of the given 'base' string that is unique
# by appending a counter to it. Uniqueness is determined by
# repeated calls to the passed block.
#
# If `base` is a function/proc, we expect that calling it with a
# candidate counter returns a string to test/return.
def string(base)
@base = base
@counter = nil
increment_counter! while yield(base_string)
base_string
end
private
def base_string
if @base.respond_to?(:call)
@base.call(@counter)
else
"#{@base}#{@counter}"
end
end
def increment_counter!
@counter ||= 0
@counter += 1
end
end

View File

@ -36,7 +36,7 @@ class Event < ActiveRecord::Base
scope :code_push, -> { where(action: PUSHED) }
scope :in_projects, ->(projects) do
where(project_id: projects).recent
where(project_id: projects.pluck(:id)).recent
end
scope :with_associations, -> { includes(:author, :project, project: :namespace).preload(:target) }

View File

@ -91,17 +91,13 @@ class MergeRequest < ActiveRecord::Base
around_transition do |merge_request, transition, block|
Gitlab::Timeless.timeless(merge_request, &block)
end
after_transition unchecked: :cannot_be_merged do |merge_request, transition|
TodoService.new.merge_request_became_unmergeable(merge_request)
end
end
validates :source_project, presence: true, unless: [:allow_broken, :importing?, :closed_without_fork?]
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?
@ -440,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
@ -648,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')
@ -710,7 +706,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

View File

@ -99,14 +99,8 @@ class Namespace < ActiveRecord::Base
# Work around that by setting their username to "blank", followed by a counter.
path = "blank" if path.blank?
counter = 0
base = path
while Namespace.find_by_path_or_name(path)
counter += 1
path = "#{base}#{counter}"
end
path
uniquify = Uniquify.new
uniquify.string(path) { |s| Namespace.find_by_path_or_name(s) }
end
end

View File

@ -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

View File

@ -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
@ -359,7 +359,7 @@ class Project < ActiveRecord::Base
end
def reference_pattern
name_pattern = Gitlab::Regex::NAMESPACE_REGEX_STR
name_pattern = Gitlab::Regex::FULL_NAMESPACE_REGEX_STR
%r{
((?<namespace>#{name_pattern})\/)?
@ -847,10 +847,6 @@ class Project < ActiveRecord::Base
gitlab_shell.url_to_repo(path_with_namespace)
end
def namespace_dir
namespace.try(:path) || ''
end
def repo_exists?
@repo_exists ||= repository.exists?
rescue
@ -873,8 +869,14 @@ class Project < ActiveRecord::Base
url_to_repo
end
def http_url_to_repo
"#{web_url}.git"
def http_url_to_repo(user = nil)
url = web_url
if user
url.sub!(%r{\Ahttps?://}) { |protocol| "#{protocol}#{user.username}@" }
end
"#{url}.git"
end
# Check if current branch name is marked as protected in the system
@ -899,8 +901,8 @@ class Project < ActiveRecord::Base
def rename_repo
path_was = previous_changes['path'].first
old_path_with_namespace = File.join(namespace_dir, path_was)
new_path_with_namespace = File.join(namespace_dir, path)
old_path_with_namespace = File.join(namespace.full_path, path_was)
new_path_with_namespace = File.join(namespace.full_path, path)
Rails.logger.error "Attempting to rename #{old_path_with_namespace} -> #{new_path_with_namespace}"

View File

@ -33,8 +33,15 @@ class ProjectGroupLink < ActiveRecord::Base
private
def different_group
if self.group && self.project && self.project.group == self.group
errors.add(:base, "Project cannot be shared with the project it is in.")
return unless self.group && self.project
project_group = self.project.group
return unless project_group
group_ids = project_group.ancestors.map(&:id).push(project_group.id)
if group_ids.include?(self.group.id)
errors.add(:base, "Project cannot be shared with the group it is in or one of its ancestors.")
end
end

View File

@ -15,10 +15,10 @@ class MattermostService < ChatNotificationService
'This service sends notifications about projects events to Mattermost channels.<br />
To set up this service:
<ol>
<li><a href="https://docs.mattermost.com/developer/webhooks-incoming.html#enabling-incoming-webhooks">Enable incoming webhooks</a> in your Mattermost installation. </li>
<li><a href="https://docs.mattermost.com/developer/webhooks-incoming.html#creating-integrations-using-incoming-webhooks">Add an incoming webhook</a> in your Mattermost team. The default channel can be overridden for each event. </li>
<li>Paste the webhook <strong>URL</strong> into the field bellow. </li>
<li>Select events below to enable notifications. The channel and username are optional. </li>
<li><a href="https://docs.mattermost.com/developer/webhooks-incoming.html#enabling-incoming-webhooks">Enable incoming webhooks</a> in your Mattermost installation.</li>
<li><a href="https://docs.mattermost.com/developer/webhooks-incoming.html#creating-integrations-using-incoming-webhooks">Add an incoming webhook</a> in your Mattermost team. The default channel can be overridden for each event.</li>
<li>Paste the webhook <strong>URL</strong> into the field below.</li>
<li>Select events below to enable notifications. The <strong>Channel handle</strong> and <strong>Username</strong> fields are optional.</li>
</ol>'
end
@ -28,14 +28,14 @@ class MattermostService < ChatNotificationService
def default_fields
[
{ type: 'text', name: 'webhook', placeholder: 'http://mattermost_host/hooks/...' },
{ type: 'text', name: 'username', placeholder: 'username' },
{ type: 'text', name: 'webhook', placeholder: 'e.g. http://mattermost_host/hooks/…' },
{ type: 'text', name: 'username', placeholder: 'e.g. GitLab' },
{ type: 'checkbox', name: 'notify_only_broken_builds' },
{ type: 'checkbox', name: 'notify_only_broken_pipelines' },
]
end
def default_channel_placeholder
"town-square"
"Channel handle (e.g. town-square)"
end
end

Some files were not shown because too many files have changed in this diff Show More