Commit 83418ad8 authored by Lin Jen-Shin's avatar Lin Jen-Shin

Merge remote-tracking branch 'upstream/master' into 27762-add-default-artifacts-expiration

* upstream/master: (247 commits)
  Switched CONTRIBUTING.md style guide recommendation for method chaining
  Fix new offenses
  Stylistic tweaks
  Fix OAuth/SAML user blocking behavior
  Revert "Enable Style/DotPosition"
  Revert "Prefer leading style for Style/DotPosition"
  Revert "Enable Style/BarePercentLiterals"
  Manually correct autocorrect
  Move up delegate calls
  Exclude migrations from Style/MutableConstant
  ActiveSupport delegation is preferred over Forwardable
  Update haml_lint to work with newest rubocop
  Add explanations to cops
  Update rubocop and rubocop-rspec and regenerate .rubocop_todo.yml
  Update rubocop and rubocop-rspec and regenerate .rubocop_todo.yml
  Order cops alphabetically
  Don’t exclude some file in lib from rubocop
  Fix new offenses
  Enable Rails/Delegate
  Enable Style/WordArray
  ...
parents 91965cef c5b29ed6
......@@ -51,3 +51,4 @@ eslint-report.html
/builds/*
/shared/*
/.gitlab_workhorse_secret
/webpack-report/
......@@ -240,6 +240,25 @@ rake db:seed_fu:
paths:
- log/development.log
rake gitlab:assets:compile:
stage: test
<<: *dedicated-runner
dependencies: []
variables:
NODE_ENV: "production"
RAILS_ENV: "production"
SETUP_DB: "false"
USE_DB: "false"
SKIP_STORAGE_VALIDATION: "true"
WEBPACK_REPORT: "true"
script:
- bundle exec rake yarn:install gitlab:assets:compile
artifacts:
name: webpack-report
expire_in: 31d
paths:
- webpack-report/
rake karma:
cache:
paths:
......@@ -281,7 +300,7 @@ bundler:audit:
- master@gitlab/gitlabhq
- master@gitlab/gitlab-ee
script:
- "bundle exec bundle-audit check --update --ignore OSVDB-115941"
- "bundle exec bundle-audit check --update --ignore OSVDB-115941 CVE-2016-6316 CVE-2016-6317"
migration paths:
stage: test
......@@ -388,6 +407,7 @@ pages:
dependencies:
- coverage
- rake karma
- rake gitlab:assets:compile
- lint:javascript:report
script:
- mv public/ .public/
......@@ -395,6 +415,7 @@ pages:
- mv coverage/ public/coverage-ruby/ || true
- mv coverage-javascript/ public/coverage-javascript/ || true
- mv eslint-report.html public/ || true
- mv webpack-report/ public/webpack-report/ || true
artifacts:
paths:
- public
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -426,7 +426,7 @@ merge request:
1. [Ruby](https://github.com/bbatsov/ruby-style-guide).
Important sections include [Source Code Layout][rss-source] and
[Naming][rss-naming]. Use:
- multi-line method chaining style **Option B**: dot `.` on previous line
- multi-line method chaining style **Option A**: dot `.` on the second line
- string literal quoting style **Option A**: single quoted by default
1. [Rails](https://github.com/bbatsov/rails-style-guide)
1. [Newlines styleguide][newlines-styleguide]
......
source 'https://rubygems.org'
gem 'rails', '4.2.7.1'
gem 'rails', '4.2.8'
gem 'rails-deprecated_sanitizer', '~> 1.0.3'
# Responders respond_to and respond_with
......@@ -201,7 +201,7 @@ gem 'babosa', '~> 1.0.2'
gem 'loofah', '~> 2.0.3'
# Working with license
gem 'licensee', '~> 8.0.0'
gem 'licensee', '~> 8.7.0'
# Protect against bruteforcing
gem 'rack-attack', '~> 4.4.1'
......@@ -301,10 +301,10 @@ group :development, :test do
gem 'spring-commands-rspec', '~> 1.0.4'
gem 'spring-commands-spinach', '~> 1.1.0'
gem 'rubocop', '~> 0.46.0', require: false
gem 'rubocop-rspec', '~> 1.9.1', require: false
gem 'rubocop', '~> 0.47.1', require: false
gem 'rubocop-rspec', '~> 1.12.0', require: false
gem 'scss_lint', '~> 0.47.0', require: false
gem 'haml_lint', '~> 0.18.2', require: false
gem 'haml_lint', '~> 0.21.0', require: false
gem 'simplecov', '0.12.0', require: false
gem 'flay', '~> 2.6.1', require: false
gem 'bundler-audit', '~> 0.5.0', require: false
......@@ -333,7 +333,7 @@ gem 'newrelic_rpm', '~> 3.16'
gem 'octokit', '~> 4.6.2'
gem 'mail_room', '~> 0.9.0'
gem 'mail_room', '~> 0.9.1'
gem 'email_reply_trimmer', '~> 0.1'
gem 'html2text'
......
......@@ -3,40 +3,39 @@ GEM
specs:
RedCloth (4.3.2)
ace-rails-ap (4.1.0)
actionmailer (4.2.7.1)
actionpack (= 4.2.7.1)
actionview (= 4.2.7.1)
activejob (= 4.2.7.1)
actionmailer (4.2.8)
actionpack (= 4.2.8)
actionview (= 4.2.8)
activejob (= 4.2.8)
mail (~> 2.5, >= 2.5.4)
rails-dom-testing (~> 1.0, >= 1.0.5)
actionpack (4.2.7.1)
actionview (= 4.2.7.1)
activesupport (= 4.2.7.1)
actionpack (4.2.8)
actionview (= 4.2.8)
activesupport (= 4.2.8)
rack (~> 1.6)
rack-test (~> 0.6.2)
rails-dom-testing (~> 1.0, >= 1.0.5)
rails-html-sanitizer (~> 1.0, >= 1.0.2)
actionview (4.2.7.1)
activesupport (= 4.2.7.1)
actionview (4.2.8)
activesupport (= 4.2.8)
builder (~> 3.1)
erubis (~> 2.7.0)
rails-dom-testing (~> 1.0, >= 1.0.5)
rails-html-sanitizer (~> 1.0, >= 1.0.2)
activejob (4.2.7.1)
activesupport (= 4.2.7.1)
rails-html-sanitizer (~> 1.0, >= 1.0.3)
activejob (4.2.8)
activesupport (= 4.2.8)
globalid (>= 0.3.0)
activemodel (4.2.7.1)
activesupport (= 4.2.7.1)
activemodel (4.2.8)
activesupport (= 4.2.8)
builder (~> 3.1)
activerecord (4.2.7.1)
activemodel (= 4.2.7.1)
activesupport (= 4.2.7.1)
activerecord (4.2.8)
activemodel (= 4.2.8)
activesupport (= 4.2.8)
arel (~> 6.0)
activerecord_sane_schema_dumper (0.2)
rails (>= 4, < 5)
activesupport (4.2.7.1)
activesupport (4.2.8)
i18n (~> 0.7)
json (~> 1.7, >= 1.7.7)
minitest (~> 5.1)
thread_safe (~> 0.3, >= 0.3.4)
tzinfo (~> 1.1)
......@@ -47,7 +46,7 @@ GEM
activerecord (>= 3.0)
akismet (2.0.0)
allocations (1.0.5)
arel (6.0.3)
arel (6.0.4)
asana (0.4.0)
faraday (~> 0.9)
faraday_middleware (~> 0.9)
......@@ -86,7 +85,7 @@ GEM
sass (>= 3.3.4)
brakeman (3.4.1)
browser (2.2.0)
builder (3.2.2)
builder (3.2.3)
bullet (5.2.0)
activesupport (>= 3.0.0)
uniform_notifier (~> 1.10.0)
......@@ -127,7 +126,7 @@ GEM
execjs
coffee-script-source (1.10.0)
colorize (0.7.7)
concurrent-ruby (1.0.2)
concurrent-ruby (1.0.4)
connection_pool (2.2.1)
crack (0.4.3)
safe_yaml (~> 1.0.0)
......@@ -320,10 +319,10 @@ GEM
multi_json (>= 1.3.2)
haml (4.0.7)
tilt
haml_lint (0.18.2)
haml_lint (0.21.0)
haml (~> 4.0)
rake (>= 10, < 12)
rubocop (>= 0.36.0)
rake (>= 10, < 13)
rubocop (>= 0.47.0)
sysexits (~> 1.1)
hamlit (2.6.1)
temple (~> 0.7.6)
......@@ -354,7 +353,7 @@ GEM
json (~> 1.8)
multi_xml (>= 0.5.2)
httpclient (2.8.2)
i18n (0.7.0)
i18n (0.8.0)
ice_nine (0.11.1)
influxdb (0.2.3)
cause
......@@ -370,7 +369,7 @@ GEM
thor (>= 0.14, < 2.0)
jquery-ui-rails (5.0.5)
railties (>= 3.2.16)
json (1.8.3)
json (1.8.6)
json-schema (2.6.2)
addressable (~> 2.3.8)
jwt (1.5.6)
......@@ -399,8 +398,8 @@ GEM
rubyzip
thor
xml-simple
licensee (8.0.0)
rugged (>= 0.24b)
licensee (8.7.0)
rugged (~> 0.24)
little-plugger (1.1.4)
logging (2.1.0)
little-plugger (~> 1.1)
......@@ -409,7 +408,7 @@ GEM
nokogiri (>= 1.5.9)
mail (2.6.4)
mime-types (>= 1.16, < 4)
mail_room (0.9.0)
mail_room (0.9.1)
memoist (0.15.0)
method_source (0.8.2)
mime-types (2.99.3)
......@@ -429,9 +428,8 @@ GEM
net-ssh (3.0.1)
netrc (0.11.0)
newrelic_rpm (3.16.0.318)
nokogiri (1.6.8)
nokogiri (1.6.8.1)
mini_portile2 (~> 2.1.0)
pkg-config (~> 1.1.7)
numerizer (0.1.1)
oauth (0.5.1)
oauth2 (1.2.0)
......@@ -503,10 +501,9 @@ GEM
os (0.9.6)
paranoia (2.2.0)
activerecord (>= 4.0, < 5.1)
parser (2.3.1.4)
parser (2.4.0.0)
ast (~> 2.2)
pg (0.18.4)
pkg-config (1.1.7)
poltergeist (1.9.0)
capybara (~> 2.1)
cliver (~> 0.3.1)
......@@ -548,28 +545,28 @@ GEM
rack
rack-test (0.6.3)
rack (>= 1.0)
rails (4.2.7.1)
actionmailer (= 4.2.7.1)
actionpack (= 4.2.7.1)
actionview (= 4.2.7.1)
activejob (= 4.2.7.1)
activemodel (= 4.2.7.1)
activerecord (= 4.2.7.1)
activesupport (= 4.2.7.1)
rails (4.2.8)
actionmailer (= 4.2.8)
actionpack (= 4.2.8)
actionview (= 4.2.8)
activejob (= 4.2.8)
activemodel (= 4.2.8)
activerecord (= 4.2.8)
activesupport (= 4.2.8)
bundler (>= 1.3.0, < 2.0)
railties (= 4.2.7.1)
railties (= 4.2.8)
sprockets-rails
rails-deprecated_sanitizer (1.0.3)
activesupport (>= 4.2.0.alpha)
rails-dom-testing (1.0.7)
rails-dom-testing (1.0.8)
activesupport (>= 4.2.0.beta, < 5.0)
nokogiri (~> 1.6.0)
nokogiri (~> 1.6)
rails-deprecated_sanitizer (>= 1.0.1)
rails-html-sanitizer (1.0.3)
loofah (~> 2.0)
railties (4.2.7.1)
actionpack (= 4.2.7.1)
activesupport (= 4.2.7.1)
railties (4.2.8)
actionpack (= 4.2.8)
activesupport (= 4.2.8)
rake (>= 0.8.7)
thor (>= 0.18.1, < 2.0)
rainbow (2.1.0)
......@@ -645,13 +642,13 @@ GEM
pg
rails
sqlite3
rubocop (0.46.0)
parser (>= 2.3.1.1, < 3.0)
rubocop (0.47.1)
parser (>= 2.3.3.1, < 3.0)
powerpack (~> 0.1)
rainbow (>= 1.99.1, < 3.0)
ruby-progressbar (~> 1.7)
unicode-display_width (~> 1.0, >= 1.0.1)
rubocop-rspec (1.9.1)
rubocop-rspec (1.12.0)
rubocop (>= 0.42.0)
ruby-fogbugz (0.2.1)
crack (~> 0.4)
......@@ -733,10 +730,10 @@ GEM
spring (>= 0.9.1)
spring-commands-spinach (1.1.0)
spring (>= 0.9.1)
sprockets (3.7.0)
sprockets (3.7.1)
concurrent-ruby (~> 1.0)
rack (> 1, < 3)
sprockets-rails (3.1.1)
sprockets-rails (3.2.0)
actionpack (>= 4.0)
activesupport (>= 4.0)
sprockets (>= 3.0.0)
......@@ -760,9 +757,9 @@ GEM
daemons (~> 1.0, >= 1.0.9)
eventmachine (~> 1.0, >= 1.0.4)
rack (>= 1, < 3)
thor (0.19.1)
thor (0.19.4)
thread_safe (0.3.5)
tilt (2.0.5)
tilt (2.0.6)
timecop (0.8.1)
timfel-krb5-auth (0.8.3)
tool (0.2.3)
......@@ -779,7 +776,7 @@ GEM
unf (0.1.4)
unf_ext
unf_ext (0.0.7.2)
unicode-display_width (1.1.1)
unicode-display_width (1.1.3)
unicorn (5.1.0)
kgio (~> 2.6)
raindrops (~> 0.7)
......@@ -891,7 +888,7 @@ DEPENDENCIES
google-api-client (~> 0.8.6)
grape (~> 0.18.0)
grape-entity (~> 0.6.0)
haml_lint (~> 0.18.2)
haml_lint (~> 0.21.0)
hamlit (~> 2.6.1)
health_check (~> 2.2.0)
hipchat (~> 1.5.0)
......@@ -910,9 +907,9 @@ DEPENDENCIES
kubeclient (~> 2.2.0)
letter_opener_web (~> 1.3.0)
license_finder (~> 2.1.0)
licensee (~> 8.0.0)
licensee (~> 8.7.0)
loofah (~> 2.0.3)
mail_room (~> 0.9.0)
mail_room (~> 0.9.1)
method_source (~> 0.8)
minitest (~> 5.7.0)
mousetrap-rails (~> 1.4.6)
......@@ -949,7 +946,7 @@ DEPENDENCIES
rack-cors (~> 0.4.0)
rack-oauth2 (~> 1.2.1)
rack-proxy (~> 0.6.0)
rails (= 4.2.7.1)
rails (= 4.2.8)
rails-deprecated_sanitizer (~> 1.0.3)
rainbow (~> 2.1.0)
rblineprof (~> 0.3.6)
......@@ -966,8 +963,8 @@ DEPENDENCIES
rspec-rails (~> 3.5.0)
rspec-retry (~> 0.4.5)
rspec_profiling (~> 0.0.5)
rubocop (~> 0.46.0)
rubocop-rspec (~> 1.9.1)
rubocop (~> 0.47.1)
rubocop-rspec (~> 1.12.0)
ruby-fogbugz (~> 0.2.1)
ruby-prof (~> 0.16.2)
rugged (~> 0.24.0)
......
......@@ -29,7 +29,7 @@ We're hiring developers, support people, and production engineers all the time,
There are two editions of GitLab:
- GitLab Community Edition (CE) is available freely under the MIT Expat license.
- GitLab Enterprise Edition (EE) includes [extra features](https://about.gitlab.com/features/#compare) that are more useful for organizations with more than 100 users. To use EE and get official support please [become a subscriber](https://about.gitlab.com/pricing/).
- GitLab Enterprise Edition (EE) includes [extra features](https://about.gitlab.com/products/#compare-options) that are more useful for organizations with more than 100 users. To use EE and get official support please [become a subscriber](https://about.gitlab.com/products/).
## Website
......
8.17.0-pre
8.18.0-pre
<svg width="12" height="15" viewBox="0 0 12 15" xmlns="http://www.w3.org/2000/svg"><path d="M10.267 11.028V5.167c-.028-.728-.318-1.372-.878-1.923-.56-.55-1.194-.85-1.922-.877h-.934V.5l-2.8 2.8 2.8 2.8V4.233h.934a.976.976 0 0 1 .644.29.88.88 0 0 1 .289.644v5.861a1.86 1.86 0 0 0 .933 3.472 1.86 1.86 0 0 0 .934-3.472zM3.733 3.3a1.86 1.86 0 0 0-1.866-1.867 1.86 1.86 0 0 0-.934 3.472v6.123a1.86 1.86 0 0 0 .933 3.472 1.86 1.86 0 0 0 .934-3.472V4.905c.55-.317.933-.914.933-1.605z" fill-rule="nonzero"/></svg>
class AjaxLoadingSpinner {
static init() {
const $elements = $('.js-ajax-loading-spinner');
$elements.on('ajax:beforeSend', AjaxLoadingSpinner.ajaxBeforeSend);
$elements.on('ajax:complete', AjaxLoadingSpinner.ajaxComplete);
}
static ajaxBeforeSend(e) {
e.target.setAttribute('disabled', '');
const iconElement = e.target.querySelector('i');
// get first fa- icon
const originalIcon = iconElement.className.match(/(fa-)([^\s]+)/g).first();
iconElement.dataset.icon = originalIcon;
AjaxLoadingSpinner.toggleLoadingIcon(iconElement);
$(e.target).off('ajax:beforeSend', AjaxLoadingSpinner.ajaxBeforeSend);
}
static ajaxComplete(e) {
e.target.removeAttribute('disabled');
const iconElement = e.target.querySelector('i');
AjaxLoadingSpinner.toggleLoadingIcon(iconElement);
$(e.target).off('ajax:complete', AjaxLoadingSpinner.ajaxComplete);
}
static toggleLoadingIcon(iconElement) {
const classList = iconElement.classList;
classList.toggle(iconElement.dataset.icon);
classList.toggle('fa-spinner');
classList.toggle('fa-spin');
}
}
window.gl = window.gl || {};
gl.AjaxLoadingSpinner = AjaxLoadingSpinner;
/* eslint-disable one-var, quote-props, comma-dangle, space-before-function-paren, import/newline-after-import, no-multi-spaces, max-len */
/* eslint-disable one-var, quote-props, comma-dangle, space-before-function-paren */
/* global Vue */
/* global BoardService */
function requireAll(context) { return context.keys().map(context); }
window.Vue = require('vue');
window.Vue.use(require('vue-resource'));
requireAll(require.context('./models', true, /^\.\/.*\.(js|es6)$/));
requireAll(require.context('./stores', true, /^\.\/.*\.(js|es6)$/));
requireAll(require.context('./services', true, /^\.\/.*\.(js|es6)$/));
requireAll(require.context('./mixins', true, /^\.\/.*\.(js|es6)$/));
requireAll(require.context('./filters', true, /^\.\/.*\.(js|es6)$/));
require('./models/issue');
require('./models/label');
require('./models/list');
require('./models/milestone');
require('./models/user');
require('./stores/boards_store');
require('./stores/modal_store');
require('./services/board_service');
require('./mixins/modal_mixins');
require('./mixins/sortable_default_options');
require('./filters/due_date_filters');
require('./components/board');
require('./components/board_sidebar');
require('./components/new_list_dropdown');
......@@ -93,17 +97,53 @@ $(() => {
modal: ModalStore.store,
store: Store.state,
},
watch: {
disabled() {
this.updateTooltip();
},
},
computed: {
disabled() {
return !this.store.lists.filter(list => list.type !== 'blank' && list.type !== 'done').length;
},
tooltipTitle() {
if (this.disabled) {
return 'Please add a list to your board first';
}
return '';
},
},
methods: {
updateTooltip() {
const $tooltip = $(this.$el);
this.$nextTick(() => {
if (this.disabled) {
$tooltip.tooltip();
} else {
$tooltip.tooltip('destroy');
}
});
},
openModal() {
if (!this.disabled) {
this.toggleModal(true);
}
},
},
mounted() {
this.updateTooltip();
},
template: `
<button
class="btn btn-create pull-right prepend-left-10 has-tooltip"
class="btn btn-create pull-right prepend-left-10"
type="button"
:disabled="disabled"
@click="toggleModal(true)">
data-placement="bottom"
:class="{ 'disabled': disabled }"
:title="tooltipTitle"
:aria-disabled="disabled"
@click="openModal">
Add issues
</button>
`,
......
......@@ -107,9 +107,9 @@
if (typeof label.message === 'string') {
errors = label.message;
} else {
errors = label.message.map(function (value, key) {
return key + " " + value[0];
}).join("<br/>");
errors = Object.keys(label.message).map(key =>
`${gl.text.humanize(key)} ${label.message[key].join(', ')}`
).join("<br/>");
}
this.$newLabelError
......
......@@ -4,10 +4,20 @@
window.Vue = require('vue');
window.Cookies = require('js-cookie');
function requireAll(context) { return context.keys().map(context); }
requireAll(require.context('./svg', false, /^\.\/.*\.(js|es6)$/));
requireAll(require.context('.', true, /^\.\/(?!cycle_analytics_bundle).*\.(js|es6)$/));
require('./svg/icon_branch');
require('./svg/icon_build_status');
require('./svg/icon_commit');
require('./components/stage_code_component');
require('./components/stage_issue_component');
require('./components/stage_plan_component');
require('./components/stage_production_component');
require('./components/stage_review_component');
require('./components/stage_staging_component');
require('./components/stage_test_component');
require('./components/total_time_component');
require('./cycle_analytics_service');
require('./cycle_analytics_store');
require('./default_event_objects');
$(() => {
const OVERVIEW_DIALOG_COOKIE = 'cycle_analytics_help_dismissed';
......
/* eslint-disable func-names, comma-dangle, new-cap, no-new, import/newline-after-import, no-multi-spaces, max-len */
/* eslint-disable func-names, comma-dangle, new-cap, no-new, max-len */
/* global Vue */
/* global ResolveCount */
function requireAll(context) { return context.keys().map(context); }
const Vue = require('vue');
requireAll(require.context('./models', false, /^\.\/.*\.(js|es6)$/));
requireAll(require.context('./stores', false, /^\.\/.*\.(js|es6)$/));
requireAll(require.context('./services', false, /^\.\/.*\.(js|es6)$/));
requireAll(require.context('./mixins', false, /^\.\/.*\.(js|es6)$/));
requireAll(require.context('./components', false, /^\.\/.*\.(js|es6)$/));
require('./models/discussion');
require('./models/note');
require('./stores/comments');
require('./services/resolve');
require('./mixins/discussion');
require('./components/comment_resolve_btn');
require('./components/jump_to_discussion');
require('./components/resolve_btn');
require('./components/resolve_count');
require('./components/resolve_discussion_btn');
$(() => {
const projectPath = document.querySelector('.merge-request').dataset.projectPath;
......
......@@ -74,7 +74,7 @@ const ShortcutsBlob = require('./shortcuts_blob');
case 'projects:merge_requests:index':
case 'projects:issues:index':
if (gl.FilteredSearchManager) {
new gl.FilteredSearchManager();
new gl.FilteredSearchManager(page === 'projects:issues:index' ? 'issues' : 'merge_requests');
}
Issuable.init();
new gl.IssuableBulkActions({
......@@ -108,6 +108,9 @@ const ShortcutsBlob = require('./shortcuts_blob');
case 'projects:compare:show':
new gl.Diff();
break;
case 'projects:branches:index':
gl.AjaxLoadingSpinner.init();
break;
case 'projects:issues:new':
case 'projects:issues:edit':
shortcut_handler = new ShortcutsNavigation();
......
......@@ -126,13 +126,14 @@ require('./preview_markdown');
};
pasteText = function(text) {
var afterSelection, beforeSelection, caretEnd, caretStart, textEnd;
var formattedText = text + "\n\n";
caretStart = $(child)[0].selectionStart;
caretEnd = $(child)[0].selectionEnd;
textEnd = $(child).val().length;
beforeSelection = $(child).val().substring(0, caretStart);
afterSelection = $(child).val().substring(caretEnd, textEnd);
$(child).val(beforeSelection + text + afterSelection);
child.get(0).setSelectionRange(caretStart + text.length, caretEnd + text.length);
$(child).val(beforeSelection + formattedText + afterSelection);
child.get(0).setSelectionRange(caretStart + formattedText.length, caretEnd + formattedText.length);
return form_textarea.trigger("input");
};
getFilename = function(e) {
......
......@@ -15,29 +15,29 @@ module.exports = Vue.component('actions-component', {
},
template: `
<div class="inline">
<div class="dropdown">
<a class="dropdown-new btn btn-default" data-toggle="dropdown">
<div class="btn-group" role="group">
<button class="dropdown btn btn-default dropdown-new" data-toggle="dropdown">
<span>
<span class="js-dropdown-play-icon-container" v-html="playIconSvg"></span>
<i class="fa fa-caret-down"></i>
</a>
</span>
<ul class="dropdown-menu dropdown-menu-align-right">
<li v-for="action in actions">
<a :href="action.play_path"
data-method="post"
rel="nofollow"
class="js-manual-action-link">
<ul class="dropdown-menu dropdown-menu-align-right">
<li v-for="action in actions">
<a :href="action.play_path"
data-method="post"
rel="nofollow"
class="js-manual-action-link">
<span class="js-action-play-icon-container" v-html="playIconSvg"></span>
<span class="js-action-play-icon-container" v-html="playIconSvg"></span>
<span>
{{action.name}}
</span>
</a>
</li>
</ul>
</div>
</div>
<span>
{{action.name}}
</span>
</a>
</li>
</ul>
</button>
</div>
`,
});
......@@ -505,39 +505,26 @@ module.exports = Vue.component('environment-item', {
<td class="hidden-xs">
<div v-if="!model.isFolder">
<div v-if="hasManualActions && canCreateDeployment"
class="inline js-manual-actions-container">
<actions-component
<div class="btn-group" role="group">
<actions-component v-if="hasManualActions && canCreateDeployment"
:play-icon-svg="playIconSvg"
:actions="manualActions">
</actions-component>
</div>
<div v-if="externalURL && canReadEnvironment"
class="inline js-external-url-container">
<external-url-component
<external-url-component v-if="externalURL && canReadEnvironment"
:external-url="externalURL">
</external-url-component>
</div>
<div v-if="hasStopAction && canCreateDeployment"
class="inline js-stop-component-container">
<stop-component
<stop-component v-if="hasStopAction && canCreateDeployment"
:stop-url="model.stop_path">
</stop-component>
</div>
<div v-if="model && model.terminal_path"
class="inline js-terminal-button-container">
<terminal-button-component
<terminal-button-component v-if="model && model.terminal_path"
:terminal-icon-svg="terminalIconSvg"
:terminal-path="model.terminal_path">
</terminal-button-component>
</div>
<div v-if="canRetry && canCreateDeployment"
class="inline js-rollback-component-container">
<rollback-component
<rollback-component v-if="canRetry && canCreateDeployment"
:is-last-deployment="isLastDeployment"
:retry-url="retryUrl">
</rollback-component>
......
......@@ -37,23 +37,18 @@ require('./filtered_search_dropdown');
}
renderContent() {
const dropdownData = [{
icon: 'fa-pencil',
hint: 'author:',
tag: '&lt;@author&gt;',
}, {
icon: 'fa-user',
hint: 'assignee:',
tag: '&lt;@assignee&gt;',
}, {
icon: 'fa-clock-o',
hint: 'milestone:',
tag: '&lt;%milestone&gt;',
}, {
icon: 'fa-tag',
hint: 'label:',
tag: '&lt;~label&gt;',
}];
const dropdownData = [];
[].forEach.call(this.input.parentElement.querySelectorAll('.dropdown-menu'), (dropdownMenu) => {
const { icon, hint, tag } = dropdownMenu.dataset;
if (icon && hint && tag) {
dropdownData.push({
icon: `fa-${icon}`,
hint,
tag: `&lt;${tag}&gt;`,
});
}
});
this.droplab.changeHookList(this.hookId, this.dropdown, [droplabFilter], this.config);
this.droplab.setData(this.hookId, dropdownData);
......
function requireAll(context) { return context.keys().map(context); }
requireAll(require.context('./', true, /^\.\/(?!filtered_search_bundle).*\.(js|es6)$/));
require('./dropdown_hint');
require('./dropdown_non_user');
require('./dropdown_user');
require('./dropdown_utils');
require('./filtered_search_dropdown_manager');
require('./filtered_search_dropdown');
require('./filtered_search_manager');
require('./filtered_search_token_keys');
require('./filtered_search_tokenizer');
......@@ -52,8 +52,9 @@
}
renderContent(forceShowList = false) {
if (forceShowList && this.getCurrentHook().list.hidden) {
this.getCurrentHook().list.show();
const currentHook = this.getCurrentHook();
if (forceShowList && currentHook && currentHook.list.hidden) {
currentHook.list.show();
}
}
......@@ -92,18 +93,24 @@
}
hideDropdown() {
this.getCurrentHook().list.hide();
const currentHook = this.getCurrentHook();
if (currentHook) {
currentHook.list.hide();
}
}
resetFilters() {
const hook = this.getCurrentHook();
const data = hook.list.data;
const results = data.map((o) => {
const updated = o;
updated.droplab_hidden = false;
return updated;
});
hook.list.render(results);
if (hook) {
const data = hook.list.data;
const results = data.map((o) => {
const updated = o;
updated.droplab_hidden = false;
return updated;
});
hook.list.render(results);
}
}
}
......
......@@ -2,10 +2,12 @@
(() => {
class FilteredSearchDropdownManager {
constructor(baseEndpoint = '') {
constructor(baseEndpoint = '', page) {
this.baseEndpoint = baseEndpoint.replace(/\/$/, '');
this.tokenizer = gl.FilteredSearchTokenizer;
this.filteredSearchTokenKeys = gl.FilteredSearchTokenKeys;
this.filteredSearchInput = document.querySelector('.filtered-search');
this.page = page;
this.setupMapping();
......@@ -150,7 +152,7 @@
this.droplab = new DropLab();
}
const match = gl.FilteredSearchTokenKeys.searchByKey(dropdownName.toLowerCase());
const match = this.filteredSearchTokenKeys.searchByKey(dropdownName.toLowerCase());
const shouldOpenFilterDropdown = match && this.currentDropdown !== match.key
&& this.mapping[match.key];
const shouldOpenHintDropdown = !match && this.currentDropdown !== 'hint';
......
(() => {
class FilteredSearchManager {
constructor() {
constructor(page) {
this.filteredSearchInput = document.querySelector('.filtered-search');
this.clearSearchButton = document.querySelector('.clear-search');
this.filteredSearchTokenKeys = gl.FilteredSearchTokenKeys;
if (this.filteredSearchInput) {
this.tokenizer = gl.FilteredSearchTokenizer;
this.dropdownManager = new gl.FilteredSearchDropdownManager(this.filteredSearchInput.getAttribute('data-base-endpoint') || '');
this.dropdownManager = new gl.FilteredSearchDropdownManager(this.filteredSearchInput.getAttribute('data-base-endpoint') || '', page);
this.bindEvents();
this.loadSearchParamsFromURL();
......@@ -117,8 +118,8 @@
const keyParam = decodeURIComponent(split[0]);
const value = split[1];
// Check if it matches edge conditions listed in gl.FilteredSearchTokenKeys
const condition = gl.FilteredSearchTokenKeys.searchByConditionUrl(p);
// Check if it matches edge conditions listed in this.filteredSearchTokenKeys
const condition = this.filteredSearchTokenKeys.searchByConditionUrl(p);
if (condition) {
inputValues.push(`${condition.tokenKey}:${condition.value}`);
......@@ -126,7 +127,7 @@
// Sanitize value since URL converts spaces into +
// Replace before decode so that we know what was originally + versus the encoded +
const sanitizedValue = value ? decodeURIComponent(value.replace(/\+/g, ' ')) : value;
const match = gl.FilteredSearchTokenKeys.searchByKeyParam(keyParam);
const match = this.filteredSearchTokenKeys.searchByKeyParam(keyParam);
if (match) {
const indexOf = keyParam.indexOf('_');
......@@ -171,9 +172,9 @@
paths.push(`state=${currentState}`);
tokens.forEach((token) => {
const condition = gl.FilteredSearchTokenKeys
const condition = this.filteredSearchTokenKeys
.searchByConditionKeyValue(token.key, token.value.toLowerCase());
const { param } = gl.FilteredSearchTokenKeys.searchByKey(token.key) || {};
const { param } = this.filteredSearchTokenKeys.searchByKey(token.key) || {};
const keyParam = param ? `${token.key}_${param}` : token.key;
let tokenPath = '';
......
......@@ -47,9 +47,10 @@
}
// Only filter asynchronously only if option remote is set
if (this.options.remote) {
$inputContainer.parent().addClass('is-loading');
clearTimeout(timeout);
return timeout = setTimeout(function() {
$inputContainer.parent().addClass('is-loading');
return this.options.query(this.input.val(), function(data) {
$inputContainer.parent().removeClass('is-loading');
return this.options.callback(data);
......
// require everything else in this directory
function requireAll(context) { return context.keys().map(context); }
requireAll(require.context('.', false, /^\.\/(?!graphs_bundle).*\.(js|es6)$/));
require('./stat_graph_contributors_graph');
require('./stat_graph_contributors_util');
require('./stat_graph_contributors');
require('./stat_graph');
......@@ -2,7 +2,7 @@
(function() {
$(document).on('todo:toggle', function(e, count) {
var $todoPendingCount = $('.todos-pending-count');
$todoPendingCount.text(gl.text.addDelimiter(count));
$todoPendingCount.text(gl.text.highCountTrim(count));
$todoPendingCount.toggleClass('hidden', count === 0);
});
})();
......@@ -14,6 +14,9 @@ require('vendor/latinise');
gl.text.addDelimiter = function(text) {
return text ? text.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") : text;
};
gl.text.highCountTrim = function(count) {
return count > 99 ? '99+' : count;
};
gl.text.randomString = function() {
return Math.random().toString(36).substring(7);
};
......
......@@ -2,9 +2,8 @@
/* global Network */
/* global ShortcutsNetwork */
// require everything else in this directory
function requireAll(context) { return context.keys().map(context); }
requireAll(require.context('.', false, /^\.\/(?!network_bundle).*\.(js|es6)$/));
require('./branch_graph');
require('./network');
(function() {
$(function() {
......
......@@ -169,10 +169,10 @@
url: issuesPath + "/?author_username=" + userName
}, 'separator', {
text: 'Merge requests assigned to me',
url: mrPath + "/?assignee_id=" + userId
url: mrPath + "/?assignee_username=" + userName
}, {
text: "Merge requests I've created",
url: mrPath + "/?author_id=" + userId
url: mrPath + "/?author_username=" + userName
}
];
if (!name) {
......
(() => {
class VersionCheckImage {
static bindErrorEvent(imageElement) {
imageElement.off('error').on('error', () => imageElement.hide());
}
class VersionCheckImage {
static bindErrorEvent(imageElement) {
imageElement.off('error').on('error', () => imageElement.hide());
}
}
window.gl = window.gl || {};
gl.VersionCheckImage = VersionCheckImage;
})();
window.gl = window.gl || {};
gl.VersionCheckImage = VersionCheckImage;
module.exports = VersionCheckImage;
......@@ -23,7 +23,7 @@ const CommitPipelinesStoreWithTimeAgo = require('../commit/pipelines/pipelines_s
apiScope: 'all',
pageInfo: {},
pagenum: 1,
count: { all: 0, running_or_pending: 0 },
count: {},
pageRequest: false,
};
},
......
......@@ -148,11 +148,16 @@ header {
}
.header-logo {
display: inline-block;
margin: 0 8px 0 3px;
position: relative;
position: absolute;
left: 50%;
top: 7px;
transition-duration: .3s;
z-index: 999;
#logo {
position: relative;
left: -50%;
}
svg,
img {
......@@ -162,6 +167,15 @@ header {
&:hover {
cursor: pointer;
}
@media (max-width: $screen-xs-max) {
right: 20px;
left: auto;
#logo {
left: auto;
}
}
}
.title {
......@@ -169,7 +183,7 @@ header {
padding-right: 20px;
margin: 0;
font-size: 18px;
max-width: 450px;
max-width: 385px;
display: inline-block;
line-height: $header-height;
font-weight: normal;
......@@ -179,6 +193,10 @@ 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;
}
......
......@@ -96,16 +96,6 @@ ul.unstyled-list > li {
border-bottom: none;
}
ul.task-list {
li.task-list-item {
list-style-type: none;
}
ul:not(.task-list) {
padding-left: 1.3em;
}
}
// Generic content list
ul.content-list {
@include basic-list;
......
......@@ -76,6 +76,13 @@
#{$property}: $value;
}
/* http://phrappe.com/css/conditional-css-for-webkit-based-browsers/ */
@mixin on-webkit-only {
@media screen and (-webkit-min-device-pixel-ratio:0) {
@content;
}
}
@mixin keyframes($animation-name) {
@-webkit-keyframes #{$animation-name} {
@content;
......
@mixin fade($gradient-direction, $gradient-color) {
visibility: hidden;
opacity: 0;
z-index: 1;
z-index: 2;
position: absolute;
bottom: 12px;
width: 43px;
......@@ -18,7 +18,7 @@
.fa {
position: relative;
top: 6px;
top: 5px;
font-size: 18px;
}
}
......@@ -79,6 +79,7 @@
}
&.sub-nav {
text-align: center;
background-color: $gray-normal;
.container-fluid {
......@@ -286,6 +287,7 @@
background: $gray-light;
border-bottom: 1px solid $border-color;
transition: padding $sidebar-transition-duration;
text-align: center;
.container-fluid {
position: relative;
......@@ -351,7 +353,7 @@
right: -5px;
.fa {
right: -28px;
right: -7px;
}
}
......@@ -381,7 +383,7 @@
left: 0;
.fa {
left: -4px;
left: 10px;
}
}
}
......
......@@ -55,7 +55,7 @@
padding-right: 0;
@media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) {
&:not(.build-sidebar):not(.wiki-sidebar) {
.content-wrapper {
padding-right: $gutter_collapsed_width;
}
}
......
......@@ -134,7 +134,7 @@
ul,
ol {
padding: 0;
margin: 3px 0 3px 28px !important;
margin: 3px 0 !important;
}
ul:dir(rtl),
......@@ -144,6 +144,29 @@
li {
line-height: 1.6em;
margin-left: 25px;
padding-left: 3px;
/* Normalize the bullet position on webkit. */
@include on-webkit-only {
margin-left: 28px;
padding-left: 0;
}
}
ul.task-list {
li.task-list-item {
list-style-type: none;
position: relative;
padding-left: 28px;
margin-left: 0 !important;
input.task-list-item-checkbox {
position: absolute;
left: 8px;
top: 5px;
}
}
}
a[href*="/uploads/"],
......
......@@ -35,7 +35,6 @@
display: table-cell;
}
.environments-name,
.environments-commit,
.environments-actions {
width: 20%;
......@@ -45,6 +44,7 @@
width: 10%;
}
.environments-name,
.environments-deploy,
.environments-build {
width: 15%;
......@@ -62,6 +62,22 @@
}
}
.btn-group {
> a {
color: $gl-text-color-secondary;
}
svg path {
fill: $gl-text-color-secondary;
}
.dropdown {
outline: none;
}
}
.commit-title {
margin: 0;
}
......
......@@ -10,6 +10,11 @@
.issue-labels {
display: inline-block;
}
.icon-merge-request-unmerged {
height: 13px;
margin-bottom: 3px;
}
}
}
......
......@@ -652,23 +652,29 @@ pre.light-well {
}
}
.container-fluid.project-stats-container {
@media (max-width: $screen-xs-max) {
padding: 12px 0;
}
}
.project-last-commit {
background-color: $gray-light;
padding: 12px $gl-padding;
border: 1px solid $border-color;
@media (min-width: $screen-sm-min) {
margin-top: $gl-padding;
}
@media (min-width: $screen-sm-min) {
border-radius: $border-radius-base;
&.container-fluid {
padding-top: 12px;
padding-bottom: 12px;
background-color: $gray-light;
border: 1px solid $border-color;
border-right-width: 0;
border-left-width: 0;
@media (min-width: $screen-sm-min) {
border-right-width: 1px;
border-left-width: 1px;
}
}
&.container-limited {
@media (min-width: 1281px) {
border-radius: $border-radius-base;
}
}
.ci-status {
......
......@@ -6,6 +6,8 @@
.navbar-nav {
li {
.badge.todos-pending-count {
position: inherit;
top: -6px;
margin-top: -5px;
font-weight: normal;
background: $todo-alert-blue;
......@@ -43,6 +45,12 @@
}
}
.todo-avatar,
.todo-actions {
-webkit-flex: 0 0 auto;
flex: 0 0 auto;
}
.todo-actions {
display: -webkit-flex;
display: flex;
......@@ -55,8 +63,9 @@
}
.todo-item {
-webkit-flex: auto;
flex: auto;
-webkit-flex: 0 1 100%;
flex: 0 1 100%;
min-width: 0;
}
}
......@@ -74,8 +83,29 @@
.todo-item {
.todo-title {
@include str-truncated(calc(100% - 174px));
overflow: visible;
display: flex;
& > .title-item {
-webkit-flex: 0 0 auto;
flex: 0 0 auto;
margin: 0 2px;
&:first-child {
margin-left: 0;
}
&:last-child {
margin-right: 0;
}
}
.todo-label {
-webkit-flex: 0 1 auto;
flex: 0 1 auto;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
.status-box {
......@@ -154,10 +184,12 @@
.todo-item {
.todo-title {
white-space: normal;
overflow: visible;
max-width: 100%;
flex-flow: row wrap;
margin-bottom: 10px;
.todo-label {
white-space: normal;
}
}
.todo-body {
......
......@@ -3,7 +3,7 @@ class Admin::SystemInfoController < Admin::ApplicationController
'nobrowse',
'read-only',
'ro'
]
].freeze
EXCLUDED_MOUNT_TYPES = [
'autofs',
......@@ -27,7 +27,7 @@ class Admin::SystemInfoController < Admin::ApplicationController
'tmpfs',
'tracefs',
'vfat'
]
].freeze
def show
@cpus = Vmstat.cpu rescue nil
......
......@@ -74,7 +74,7 @@ class ApplicationController < ActionController::Base
def authenticate_user!(*args)
if redirect_to_home_page_url?
redirect_to current_application_settings.home_page_url and return
return redirect_to current_application_settings.home_page_url
end
super(*args)
......@@ -131,7 +131,7 @@ class ApplicationController < ActionController::Base
headers['X-UA-Compatible'] = 'IE=edge'
headers['X-Content-Type-Options'] = 'nosniff'
# Enabling HSTS for non-standard ports would send clients to the wrong port
if Gitlab.config.gitlab.https and Gitlab.config.gitlab.port == 443
if Gitlab.config.gitlab.https && Gitlab.config.gitlab.port == 443
headers['Strict-Transport-Security'] = 'max-age=31536000'
end
end
......@@ -152,7 +152,7 @@ class ApplicationController < ActionController::Base
def check_password_expiration
if current_user && current_user.password_expires_at && current_user.password_expires_at < Time.now && !current_user.ldap_user?
redirect_to new_profile_password_path and return
return redirect_to new_profile_password_path
end
end
......@@ -181,7 +181,7 @@ class ApplicationController < ActionController::Base
end
def gitlab_ldap_access(&block)
Gitlab::LDAP::Access.open { |access| block.call(access) }
Gitlab::LDAP::Access.open { |access| yield(access) }
end
# JSON for infinite scroll via Pager object
......@@ -218,7 +218,7 @@ class ApplicationController < ActionController::Base
def require_email
if current_user && current_user.temp_oauth_email? && session[:impersonator_id].nil?
redirect_to profile_path, notice: 'Please complete your profile with email address' and return
return redirect_to profile_path, notice: 'Please complete your profile with email address'
end
end
......
......@@ -101,13 +101,14 @@ module CreatesCommit
# TODO: We should really clean this up
def set_commit_variables
if can?(current_user, :push_code, @project)
# Edit file in this project
@mr_source_project = @project
else
# Merge request from fork to this project
@mr_source_project = current_user.fork_of(@project)
end
@mr_source_project =
if can?(current_user, :push_code, @project)
# Edit file in this project
@project
else
# Merge request from fork to this project
current_user.fork_of(@project)
end
# Merge request to this project
@mr_target_project = @project
......
......@@ -9,24 +9,32 @@ module IssuableCollections
private
def issuable_meta_data(issuable_collection)
def issuable_meta_data(issuable_collection, collection_type)
# map has to be used here since using pluck or select will
# throw an error when ordering issuables by priority which inserts
# a new order into the collection.
# We cannot use reorder to not mess up the paginated collection.
issuable_ids = issuable_collection.map(&:id)
issuable_note_count = Note.count_for_collection(issuable_ids, @collection_type)
issuable_ids = issuable_collection.map(&:id)
issuable_note_count = Note.count_for_collection(issuable_ids, @collection_type)
issuable_votes_count = AwardEmoji.votes_for_collection(issuable_ids, @collection_type)
issuable_merge_requests_count =
if collection_type == 'Issue'
MergeRequestsClosingIssues.count_for_collection(issuable_ids)
else
[]
end
issuable_ids.each_with_object({}) do |id, issuable_meta|
downvotes = issuable_votes_count.find { |votes| votes.awardable_id == id && votes.downvote? }
upvotes = issuable_votes_count.find { |votes| votes.awardable_id == id && votes.upvote? }
notes = issuable_note_count.find { |notes| notes.noteable_id == id }
upvotes = issuable_votes_count.find { |votes| votes.awardable_id == id && votes.upvote? }
notes = issuable_note_count.find { |notes| notes.noteable_id == id }
merge_requests = issuable_merge_requests_count.find { |mr| mr.first == id }
issuable_meta[id] = Issuable::IssuableMeta.new(
upvotes.try(:count).to_i,
downvotes.try(:count).to_i,
notes.try(:count).to_i
notes.try(:count).to_i,
merge_requests.try(:last).to_i
)
end
end
......
......@@ -10,7 +10,7 @@ module IssuesAction
.page(params[:page])
@collection_type = "Issue"
@issuable_meta_data = issuable_meta_data(@issues)
@issuable_meta_data = issuable_meta_data(@issues, @collection_type)
respond_to do |format|
format.html
......
......@@ -9,7 +9,7 @@ module MergeRequestsAction
.page(params[:page])
@collection_type = "MergeRequest"
@issuable_meta_data = issuable_meta_data(@merge_requests)
@issuable_meta_data = issuable_meta_data(@merge_requests, @collection_type)
end
private
......
......@@ -59,10 +59,10 @@ module ServiceParams
:user_key,
:username,
:webhook
]
].freeze
# Parameters to ignore if no value is specified
FILTER_BLANK_PARAMS = [:password]
FILTER_BLANK_PARAMS = [:password].freeze
def service_params
dynamic_params = @service.event_channel_names + @service.event_names
......
......@@ -17,13 +17,31 @@ module SpammableActions
private
def recaptcha_params
return {} unless params[:recaptcha_verification] && Gitlab::Recaptcha.load_configurations! && verify_recaptcha
def recaptcha_check_with_fallback(&fallback)
if spammable.valid?
redirect_to spammable
elsif render_recaptcha?
if params[:recaptcha_verification]
flash[:alert] = 'There was an error with the reCAPTCHA. Please solve the reCAPTCHA again.'
end
render :verify
else
yield
end
end
def spammable_params
default_params = { request: request }
recaptcha_check = params[:recaptcha_verification] &&
Gitlab::Recaptcha.load_configurations! &&
verify_recaptcha
return default_params unless recaptcha_check
{
recaptcha_verified: true,
spam_log_id: params[:spam_log_id]
}
{ recaptcha_verified: true,
spam_log_id: params[:spam_log_id] }.merge(default_params)
end
def spammable
......
class Dashboard::TodosController < Dashboard::ApplicationController
include ActionView::Helpers::NumberHelper
before_action :find_todos, only: [:index, :destroy_all]
def index
......@@ -35,6 +37,11 @@ class Dashboard::TodosController < Dashboard::ApplicationController
render json: todos_counts
end
# Used in TodosHelper also
def self.todos_count_format(count)
count >= 100 ? '99+' : count
end
private
def find_todos
......@@ -43,8 +50,8 @@ class Dashboard::TodosController < Dashboard::ApplicationController
def todos_counts
{
count: current_user.todos_pending_count,
done_count: current_user.todos_done_count
count: number_with_delimiter(current_user.todos_pending_count),
done_count: number_with_delimiter(current_user.todos_done_count)
}
end
end
......@@ -29,7 +29,7 @@ class Import::FogbugzController < Import::BaseController
unless user_map.is_a?(Hash) && user_map.all? { |k, v| !v[:name].blank? }
flash.now[:alert] = 'All users must have a name.'
render 'new_user_map' and return
return render 'new_user_map'
end
session[:fogbugz_user_map] = user_map
......
......@@ -44,13 +44,13 @@ class Import::GoogleCodeController < Import::BaseController
rescue
flash.now[:alert] = "The entered user map is not a valid JSON user map."
render "new_user_map" and return
return render "new_user_map"
end
unless user_map.is_a?(Hash) && user_map.all? { |k, v| k.is_a?(String) && v.is_a?(String) }
flash.now[:alert] = "The entered user map is not a valid JSON user map."
render "new_user_map" and return
return render "new_user_map"
end
# This is the default, so let's not save it into the database.
......
......@@ -42,9 +42,7 @@ class InvitesController < ApplicationController
@token = params[:id]
@member = Member.find_by_invite_token(@token)
unless @member
render_404 and return
end
return render_404 unless @member
@member
end
......
......@@ -5,7 +5,7 @@ class JwtController < ApplicationController
SERVICES = {
Auth::ContainerRegistryAuthenticationService::AUDIENCE => Auth::ContainerRegistryAuthenticationService,
}
}.freeze
def auth
service = SERVICES[params[:service]]
......@@ -39,7 +39,8 @@ class JwtController < ApplicationController
message: "HTTP Basic: Access denied\n" \
"You have 2FA enabled, please use a personal access token for Git over HTTP.\n" \
"You can generate one at #{profile_personal_access_tokens_url}" }
] }, status: 401
]
}, status: 401
end
def render_unauthorized
......@@ -47,7 +48,8 @@ class JwtController < ApplicationController
errors: [
{ code: 'UNAUTHORIZED',
message: 'HTTP Basic: Access denied' }
] }, status: 401
]
}, status: 401
end
def auth_params
......
......@@ -122,7 +122,7 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
else
error_message = @user.errors.full_messages.to_sentence
redirect_to omniauth_error_path(oauth['provider'], error: error_message) and return
return redirect_to omniauth_error_path(oauth['provider'], error: error_message)
end
end
......
......@@ -45,13 +45,13 @@ class Profiles::KeysController < Profiles::ApplicationController
if user.present?
render text: user.all_ssh_keys.join("\n"), content_type: "text/plain"
else
render_404 and return
return render_404
end
rescue => e
render text: e.message
end
else
render_404 and return
return render_404
end
end
......
......@@ -80,7 +80,7 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController
def build_qr_code
uri = current_user.otp_provisioning_uri(account_string, issuer: issuer_host)
RQRCode::render_qrcode(uri, :svg, level: :m, unit: 3)
RQRCode.render_qrcode(uri, :svg, level: :m, unit: 3)
end
def account_string
......
......@@ -95,7 +95,7 @@ class Projects::BlobController < Projects::ApplicationController
else
if tree = @repository.tree(@commit.id, @path)
if tree.entries.any?
redirect_to namespace_project_tree_path(@project.namespace, @project, File.join(@ref, @path)) and return
return redirect_to namespace_project_tree_path(@project.namespace, @project, File.join(@ref, @path))
end
end
......
class Projects::BranchesController < Projects::ApplicationController
include ActionView::Helpers::SanitizeHelper
include SortingHelper
# Authorize
before_action :require_non_empty_project
before_action :require_non_empty_project, except: :create
before_action :authorize_download_code!
before_action :authorize_push_code!, only: [:new, :create, :destroy, :destroy_all_merged]
......@@ -32,6 +33,8 @@ class Projects::BranchesController < Projects::ApplicationController
branch_name = sanitize(strip_tags(params[:branch_name]))
branch_name = Addressable::URI.unescape(branch_name)
redirect_to_autodeploy = project.empty_repo? && project.deployment_services.present?
result = CreateBranchService.new(project, current_user).
execute(branch_name, ref)
......@@ -42,8 +45,15 @@ class Projects::BranchesController < Projects::ApplicationController
if result[:status] == :success
@branch = result[:branch]
redirect_to namespace_project_tree_path(@project.namespace, @project,
@branch.name)
if redirect_to_autodeploy
redirect_to(
url_to_autodeploy_setup(project, branch_name),
notice: view_context.autodeploy_flash_notice(branch_name))
else
redirect_to namespace_project_tree_path(@project.namespace, @project,
@branch.name)
end
else
@error = result[:message]
render action: 'new'
......@@ -76,7 +86,19 @@ class Projects::BranchesController < Projects::ApplicationController
ref_escaped = sanitize(strip_tags(params[:ref]))
Addressable::URI.unescape(ref_escaped)
else
@project.default_branch
@project.default_branch || 'master'
end
end
def url_to_autodeploy_setup(project, branch_name)
namespace_project_new_blob_path(
project.namespace,
project,
branch_name,
file_name: '.gitlab-ci.yml',
commit_message: 'Set up auto deploy',
target_branch: branch_name,
context: 'autodeploy'
)
end
end
......@@ -76,11 +76,12 @@ class Projects::GitHttpClientController < Projects::ApplicationController
return @project if defined?(@project)
project_id, _ = project_id_with_suffix
if project_id.blank?
@project = nil
else
@project = Project.find_by_full_path("#{params[:namespace_id]}/#{project_id}")
end
@project =
if project_id.blank?
nil
else
Project.find_by_full_path("#{params[:namespace_id]}/#{project_id}")
end
end
# This method returns two values so that we can parse
......
......@@ -26,7 +26,7 @@ class Projects::IssuesController < Projects::ApplicationController
@collection_type = "Issue"
@issues = issues_collection
@issues = @issues.page(params[:page])
@issuable_meta_data = issuable_meta_data(@issues)
@issuable_meta_data = issuable_meta_data(@issues, @collection_type)
if @issues.out_of_range? && @issues.total_pages != 0
return redirect_to url_for(params.merge(page: @issues.total_pages))
......@@ -94,15 +94,15 @@ class Projects::IssuesController < Projects::ApplicationController
end
def create
extra_params = { request: request,
merge_request_for_resolving_discussions: merge_request_for_resolving_discussions }
extra_params.merge!(recaptcha_params)
create_params = issue_params
.merge(merge_request_for_resolving_discussions: merge_request_for_resolving_discussions)
.merge(spammable_params)
@issue = Issues::CreateService.new(project, current_user, issue_params.merge(extra_params)).execute
@issue = Issues::CreateService.new(project, current_user, create_params).execute
respond_to do |format|
format.html do
html_response_create
recaptcha_check_with_fallback { render :new }
end
format.js do
@link = @issue.attachment.url.to_js
......@@ -111,7 +111,9 @@ class Projects::IssuesController < Projects::ApplicationController
end
def update
@issue = Issues::UpdateService.new(project, current_user, issue_params).execute(issue)
update_params = issue_params.merge(spammable_params)
@issue = Issues::UpdateService.new(project, current_user, update_params).execute(issue)
if params[:move_to_project_id].to_i > 0
new_project = Project.find(params[:move_to_project_id])
......@@ -123,11 +125,7 @@ class Projects::IssuesController < Projects::ApplicationController
respond_to do |format|
format.html do
if @issue.valid?
redirect_to issue_path(@issue)
else
render :edit
end
recaptcha_check_with_fallback { render :edit }
end
format.json do
......@@ -179,20 +177,6 @@ class Projects::IssuesController < Projects::ApplicationController
protected
def html_response_create
if @issue.valid?
redirect_to issue_path(@issue)
elsif render_recaptcha?
if params[:recaptcha_verification]
flash[:alert] = 'There was an error with the reCAPTCHA. Please solve the reCAPTCHA again.'
end
render :verify
else
render :new
end
end
def issue
# The Sortable default scope causes performance issues when used with find_by
@noteable = @issue ||= @project.issues.where(iid: params[:id]).reorder(nil).take || redirect_old
......
......@@ -39,7 +39,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@collection_type = "MergeRequest"
@merge_requests = merge_requests_collection
@merge_requests = @merge_requests.page(params[:page])
@issuable_meta_data = issuable_meta_data(@merge_requests)
@issuable_meta_data = issuable_meta_data(@merge_requests, @collection_type)
if @merge_requests.out_of_range? && @merge_requests.total_pages != 0
return redirect_to url_for(params.merge(page: @merge_requests.total_pages))
......@@ -50,6 +50,17 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@labels = LabelsFinder.new(current_user, labels_params).execute
end
@users = []
if params[:assignee_id].present?
assignee = User.find_by_id(params[:assignee_id])
@users.push(assignee) if assignee
end
if params[:author_id].present?
author = User.find_by_id(params[:author_id])
@users.push(author) if author
end
respond_to do |format|
format.html
format.json do
......@@ -370,14 +381,15 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end
def merge_widget_refresh
if merge_request.merge_when_build_succeeds
@status = :merge_when_build_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
# in last case it does not have any special status. Possible error is handled inside widget js function
@status = :success
end
@status =
if merge_request.merge_when_build_succeeds
:merge_when_build_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
# in last case it does not have any special status. Possible error is handled inside widget js function
:success
end
render 'merge'
end
......
......@@ -13,9 +13,15 @@ class Projects::PipelinesController < Projects::ApplicationController
.page(params[:page])
.per(30)
@running_or_pending_count = PipelinesFinder
@running_count = PipelinesFinder
.new(project).execute(scope: 'running').count
@pending_count = PipelinesFinder
.new(project).execute(scope: 'pending').count
@finished_count = PipelinesFinder
.new(project).execute(scope: 'finished').count
@pipelines_count = PipelinesFinder
.new(project).execute.count
......@@ -29,7 +35,9 @@ class Projects::PipelinesController < Projects::ApplicationController
.represent(@pipelines),
count: {
all: @pipelines_count,
running_or_pending: @running_or_pending_count
running: @running_count,
pending: @pending_count,
finished: @finished_count,
}
}
end
......
......@@ -38,24 +38,19 @@ class Projects::SnippetsController < Projects::ApplicationController
end
def create
create_params = snippet_params.merge(request: request)
create_params = snippet_params.merge(spammable_params)
@snippet = CreateSnippetService.new(@project, current_user, create_params).execute
if @snippet.valid?
respond_with(@snippet,
location: namespace_project_snippet_path(@project.namespace,
@project, @snippet))
else
render :new
end
recaptcha_check_with_fallback { render :new }
end
def update
UpdateSnippetService.new(project, current_user, @snippet,
snippet_params).execute
respond_with(@snippet,
location: namespace_project_snippet_path(@project.namespace,
@project, @snippet))
update_params = snippet_params.merge(spammable_params)
UpdateSnippetService.new(project, current_user, @snippet, update_params).execute
recaptcha_check_with_fallback { render :edit }
end
def show
......
......@@ -15,10 +15,10 @@ class Projects::TreeController < Projects::ApplicationController
if tree.entries.empty?
if @repository.blob_at(@commit.id, @path)
redirect_to(
return redirect_to(
namespace_project_blob_path(@project.namespace, @project,
File.join(@ref, @path))
) and return
)
elsif @path.present?
return render_404
end
......
......@@ -15,11 +15,12 @@ class SessionsController < Devise::SessionsController
def new
set_minimum_password_length
if Gitlab.config.ldap.enabled
@ldap_servers = Gitlab::LDAP::Config.servers
else
@ldap_servers = []
end
@ldap_servers =
if Gitlab.config.ldap.enabled
Gitlab::LDAP::Config.servers
else
[]
end
super
end
......
......@@ -23,13 +23,14 @@ class SnippetsController < ApplicationController
if params[:username].present?
@user = User.find_by(username: params[:username])
render_404 and return unless @user
return render_404 unless @user
@snippets = SnippetsFinder.new.execute(current_user, {
filter: :by_user,
user: @user,
scope: params[:scope] }).
page(params[:page])
scope: params[:scope]
})
.page(params[:page])
render 'index'
else
......@@ -42,16 +43,19 @@ class SnippetsController < ApplicationController
end
def create
create_params = snippet_params.merge(request: request)
create_params = snippet_params.merge(spammable_params)
@snippet = CreateSnippetService.new(nil, current_user, create_params).execute
respond_with @snippet.becomes(Snippet)
recaptcha_check_with_fallback { render :new }
end
def update
UpdateSnippetService.new(nil, current_user, @snippet,
snippet_params).execute
respond_with @snippet.becomes(Snippet)
update_params = snippet_params.merge(spammable_params)
UpdateSnippetService.new(nil, current_user, @snippet, update_params).execute
recaptcha_check_with_fallback { render :edit }
end
def show
......
......@@ -19,7 +19,7 @@
# iids: integer[]
#
class IssuableFinder
NONE = '0'
NONE = '0'.freeze
attr_accessor :current_user, :params
......
......@@ -28,11 +28,12 @@ class NotesFinder
private
def init_collection
if @params[:target_id]
@notes = on_target(@params[:target_type], @params[:target_id])
else
@notes = notes_of_any_type
end
@notes =
if @params[:target_id]
on_target(@params[:target_type], @params[:target_id])
else
notes_of_any_type
end
end
def notes_of_any_type
......
......@@ -10,7 +10,11 @@ class PipelinesFinder
scoped_pipelines =
case scope
when 'running'
pipelines.running_or_pending
pipelines.running
when 'pending'
pipelines.pending
when 'finished'
pipelines.finished
when 'branches'
from_ids(ids_for_ref(branches))
when 'tags'
......
......@@ -13,7 +13,7 @@
#
class TodosFinder
NONE = '0'
NONE = '0'.freeze
attr_accessor :current_user, :params
......@@ -99,7 +99,7 @@ class TodosFinder
end
def type?
type.present? && ['Issue', 'MergeRequest'].include?(type)
type.present? && %w(Issue MergeRequest).include?(type)
end
def type
......
......@@ -69,11 +69,12 @@ module ApplicationHelper
end
def avatar_icon(user_or_email = nil, size = nil, scale = 2)
if user_or_email.is_a?(User)
user = user_or_email
else
user = User.find_by_any_email(user_or_email.try(:downcase))
end
user =
if user_or_email.is_a?(User)
user_or_email
else
User.find_by_any_email(user_or_email.try(:downcase))
end
if user
user.avatar_url(size) || default_avatar
......
module ApplicationSettingsHelper
def gravatar_enabled?
current_application_settings.gravatar_enabled?
end
def signup_enabled?
current_application_settings.signup_enabled?
end
def signin_enabled?
current_application_settings.signin_enabled?
end
delegate :gravatar_enabled?,
:signup_enabled?,
:signin_enabled?,
:akismet_enabled?,
:koding_enabled?,
to: :current_application_settings
def user_oauth_applications?
current_application_settings.user_oauth_applications
end
def askimet_enabled?
current_application_settings.akismet_enabled?
end
def koding_enabled?
current_application_settings.koding_enabled?
end
def allowed_protocols_present?
current_application_settings.enabled_git_access_protocol.present?
end
......
......@@ -153,16 +153,17 @@ module BlobHelper
# Because we are opionated we set the cache headers ourselves.
response.cache_control[:public] = @project.public?
if @ref && @commit && @ref == @commit.id
# This is a link to a commit by its commit SHA. That means that the blob
# is immutable. The only reason to invalidate the cache is if the commit
# was deleted or if the user lost access to the repository.
response.cache_control[:max_age] = Blob::CACHE_TIME_IMMUTABLE
else
# A branch or tag points at this blob. That means that the expected blob
# value may change over time.
response.cache_control[:max_age] = Blob::CACHE_TIME
end
response.cache_control[:max_age] =
if @ref && @commit && @ref == @commit.id
# This is a link to a commit by its commit SHA. That means that the blob
# is immutable. The only reason to invalidate the cache is if the commit
# was deleted or if the user lost access to the repository.
Blob::CACHE_TIME_IMMUTABLE
else
# A branch or tag points at this blob. That means that the expected blob
# value may change over time.
Blob::CACHE_TIME
end
response.etag = @blob.id
!stale
......
module EmailsHelper
include AppearancesHelper
# Google Actions
# https://developers.google.com/gmail/markup/reference/go-to-action
def email_action(url)
......@@ -22,7 +24,7 @@ module EmailsHelper
def action_title(url)
return unless url
["merge_requests", "issues", "commit"].each do |action|
%w(merge_requests issues commit).each do |action|
if url.split("/").include?(action)
return "View #{action.humanize.singularize}"
end
......@@ -49,4 +51,19 @@ module EmailsHelper
msg = "This link is valid for #{password_reset_token_valid_time}. "
msg << "After it expires, you can #{link_tag}."
end
def header_logo
if brand_item && brand_item.header_logo?
image_tag(
brand_item.header_logo,
style: 'height: 50px'
)
else
image_tag(
image_url('mailers/gitlab_header_logo.gif'),
size: "55x50",
alt: "GitLab"
)
end
end
end
......@@ -23,7 +23,7 @@ module IssuablesHelper
def issuable_json_path(issuable)
project = issuable.project
if issuable.kind_of?(MergeRequest)
if issuable.is_a?(MergeRequest)
namespace_project_merge_request_path(project.namespace, project, issuable.iid, :json)
else
namespace_project_issue_path(project.namespace, project, issuable.iid, :json)
......@@ -198,7 +198,7 @@ module IssuablesHelper
@counts[issuable_type][state]
end
IRRELEVANT_PARAMS_FOR_CACHE_KEY = %i[utf8 sort page]
IRRELEVANT_PARAMS_FOR_CACHE_KEY = %i[utf8 sort page].freeze
private_constant :IRRELEVANT_PARAMS_FOR_CACHE_KEY
def issuables_state_counter_cache_key(issuable_type, state)
......
......@@ -2,6 +2,7 @@ module JavascriptHelper
def page_specific_javascript_tag(js)
javascript_include_tag asset_path(js)
end
def page_specific_javascript_bundle_tag(js)
javascript_include_tag(*webpack_asset_paths(js))
end
......
module NamespacesHelper
def namespace_id_from(params)
params.dig(:project, :namespace_id) || params[:namespace_id]
end
def namespaces_options(selected = :current_user, display_path: false, extra_group: nil)
groups = current_user.owned_groups + current_user.masters_groups
......@@ -29,7 +33,7 @@ module NamespacesHelper
end
def namespace_icon(namespace, size = 40)
if namespace.kind_of?(Group)
if namespace.is_a?(Group)
group_icon(namespace)
else
avatar_icon(namespace.owner.email, size)
......
......@@ -150,6 +150,15 @@ module ProjectsHelper
).html_safe
end
def link_to_autodeploy_doc
link_to 'About auto deploy', help_page_path('ci/autodeploy/index'), target: '_blank'
end
def autodeploy_flash_notice(branch_name)
"Branch <strong>#{truncate(sanitize(branch_name))}</strong> was created. To set up auto deploy, \
choose a GitLab CI Yaml template and commit your changes. #{link_to_autodeploy_doc}".html_safe
end
private
def repo_children_classes(field)
......
......@@ -30,7 +30,7 @@ module SortingHelper
}
if current_controller?('admin/projects')
options.merge!(sort_value_largest_repo => sort_title_largest_repo)
options[sort_value_largest_repo] = sort_title_largest_repo
end
options
......
......@@ -37,8 +37,8 @@ module SubmoduleHelper
end
def self_url?(url, namespace, project)
return true if url == [ Gitlab.config.gitlab.url, '/', namespace, '/',
project, '.git' ].join('')
return true if url == [Gitlab.config.gitlab.url, '/', namespace, '/',
project, '.git'].join('')
url == gitlab_shell.url_to_repo([namespace, '/', project].join(''))
end
......@@ -48,8 +48,8 @@ module SubmoduleHelper
end
def standard_links(host, namespace, project, commit)
base = [ 'https://', host, '/', namespace, '/', project ].join('')
[base, [ base, '/tree/', commit ].join('')]
base = ['https://', host, '/', namespace, '/', project].join('')
[base, [base, '/tree/', commit].join('')]
end
def relative_self_links(url, commit)
......
......@@ -99,7 +99,7 @@ module TabHelper
return 'active'
end
if ['services', 'hooks', 'deploy_keys', 'protected_branches'].include? controller.controller_name
if %w(services hooks deploy_keys protected_branches).include? controller.controller_name
"active"
end
end
......
......@@ -3,6 +3,10 @@ module TodosHelper
@todos_pending_count ||= current_user.todos_pending_count
end
def todos_count_format(count)
count > 99 ? '99+' : count
end
def todos_done_count
@todos_done_count ||= current_user.todos_done_count
end
......@@ -146,6 +150,6 @@ module TodosHelper
private
def show_todo_state?(todo)
(todo.target.is_a?(MergeRequest) || todo.target.is_a?(Issue)) && ['closed', 'merged'].include?(todo.target.state)
(todo.target.is_a?(MergeRequest) || todo.target.is_a?(Issue)) && %w(closed merged).include?(todo.target.state)
end
end
......@@ -89,13 +89,9 @@ module VisibilityLevelHelper
current_application_settings.restricted_visibility_levels || []
end
def default_project_visibility
current_application_settings.default_project_visibility
end
def default_group_visibility
current_application_settings.default_group_visibility
end
delegate :default_project_visibility,
:default_group_visibility,
to: :current_application_settings
def skip_level?(form_model, level)
form_model.is_a?(Project) && !form_model.visibility_level_allowed?(level)
......
......@@ -22,8 +22,8 @@ module Emails
mail(bcc: recipients,
subject: pipeline_subject(status),
skip_premailer: true) do |format|
format.html { render layout: false }
format.text
format.html { render layout: 'mailer' }
format.text { render layout: 'mailer' }
end
end
......
class RepositoryCheckMailer < BaseMailer
def notify(failed_count)
if failed_count == 1
@message = "One project failed its last repository check"
else
@message = "#{failed_count} projects failed their last repository check"
end
@message =
if failed_count == 1
"One project failed its last repository check"
else
"#{failed_count} projects failed their last repository check"
end
mail(
to: User.admins.pluck(:email),
......
......@@ -5,7 +5,7 @@ class ApplicationSetting < ActiveRecord::Base
add_authentication_token_field :runners_registration_token
add_authentication_token_field :health_check_access_token
CACHE_KEY = 'application_setting.last'
CACHE_KEY = 'application_setting.last'.freeze
DOMAIN_LIST_SEPARATOR = %r{\s*[,;]\s* # comma or semicolon, optionally surrounded by whitespace
| # or
\s # any whitespace character
......
......@@ -22,8 +22,10 @@ module Ci
serialize :options
serialize :yaml_variables, Gitlab::Serializer::Ci::Variables
delegate :name, to: :project, prefix: true
validates :coverage, numericality: true, allow_blank: true
validates_presence_of :ref
validates :ref, presence: true
scope :unstarted, ->() { where(runner_id: nil) }
scope :ignore_failures, ->() { where(allow_failure: false) }
......@@ -233,10 +235,6 @@ module Ci
gl_project_id
end
def project_name
project.name
end
def repo_url
auth = "gitlab-ci-token:#{ensure_token!}@"
project.http_url_to_repo.sub(/^https?:\/\//) do |prefix|
......@@ -257,7 +255,7 @@ module Ci
return unless regex
matches = text.scan(Regexp.new(regex)).last
matches = matches.last if matches.kind_of?(Array)
matches = matches.last if matches.is_a?(Array)
coverage = matches.gsub(/\d+(\.\d+)?/).first
if coverage.present?
......
......@@ -14,9 +14,11 @@ module Ci
has_many :builds, foreign_key: :commit_id
has_many :trigger_requests, dependent: :destroy, foreign_key: :commit_id
validates_presence_of :sha, unless: :importing?
validates_presence_of :ref, unless: :importing?
validates_presence_of :status, unless: :importing?
delegate :id, to: :project, prefix: true
validates :sha, presence: { unless: :importing? }
validates :ref, presence: { unless: :importing? }
validates :status, presence: { unless: :importing? }
validate :valid_commit_sha, unless: :importing?
after_create :keep_around_commits, unless: :importing?
......@@ -93,8 +95,11 @@ module Ci
.select("max(#{quoted_table_name}.id)")
.group(:ref, :sha)
relation = ref ? where(ref: ref) : self
relation.where(id: max_id)
if ref
where(ref: ref, id: max_id.where(ref: ref))
else
where(id: max_id)
end
end
def self.latest_status(ref = nil)
......@@ -150,10 +155,6 @@ module Ci
builds.latest.with_artifacts_not_expired.includes(project: [:namespace])
end
def project_id
project.id
end
# For now the only user who participates is the user who triggered
def participants(_current_user = nil)
Array(user)
......
......@@ -4,8 +4,8 @@ module Ci
RUNNER_QUEUE_EXPIRY_TIME = 60.minutes
LAST_CONTACT_TIME = 1.hour.ago
AVAILABLE_SCOPES = %w[specific shared active paused online]
FORM_EDITABLE = %i[description tag_list active run_untagged locked]
AVAILABLE_SCOPES = %w[specific shared active paused online].freeze
FORM_EDITABLE = %i[description tag_list active run_untagged locked].freeze
has_many :builds
has_many :runner_projects, dependent: :destroy
......
......@@ -5,6 +5,6 @@ module Ci
belongs_to :runner
belongs_to :project, foreign_key: :gl_project_id
validates_uniqueness_of :runner_id, scope: :gl_project_id
validates :runner_id, uniqueness: { scope: :gl_project_id }
end
end
......@@ -7,8 +7,8 @@ module Ci
belongs_to :project, foreign_key: :gl_project_id
has_many :trigger_requests, dependent: :destroy
validates_presence_of :token
validates_uniqueness_of :token
validates :token, presence: true
validates :token, uniqueness: true
before_validation :set_default_values
......
......@@ -22,12 +22,12 @@ class Commit
DIFF_HARD_LIMIT_LINES = 50000
# The SHA can be between 7 and 40 hex characters.
COMMIT_SHA_PATTERN = '\h{7,40}'
COMMIT_SHA_PATTERN = '\h{7,40}'.freeze
class << self
def decorate(commits, project)
commits.map do |commit|
if commit.kind_of?(Commit)
if commit.is_a?(Commit)
commit
else
self.new(commit, project)
......@@ -105,7 +105,7 @@ class Commit
end
def diff_line_count
@diff_line_count ||= Commit::diff_line_count(raw_diffs)
@diff_line_count ||= Commit.diff_line_count(raw_diffs)
@diff_line_count
end
......@@ -122,11 +122,12 @@ class Commit
def full_title
return @full_title if @full_title
if safe_message.blank?
@full_title = no_commit_message
else
@full_title = safe_message.split("\n", 2).first
end
@full_title =
if safe_message.blank?
no_commit_message
else
safe_message.split("\n", 2).first
end
end
# Returns the commits description
......
......@@ -10,10 +10,11 @@ class CommitStatus < ActiveRecord::Base
belongs_to :user
delegate :commit, to: :pipeline
delegate :sha, :short_sha, to: :pipeline
validates :pipeline, presence: true, unless: :importing?
validates_presence_of :name
validates :name, presence: true
alias_attribute :author, :user
......@@ -102,8 +103,6 @@ class CommitStatus < ActiveRecord::Base
end
end
delegate :sha, :short_sha, to: :pipeline
def before_sha
pipeline.before_sha || Gitlab::Git::BLANK_SHA
end
......
......@@ -11,14 +11,15 @@ module CacheMarkdownField
# Knows about the relationship between markdown and html field names, and
# stores the rendering contexts for the latter
class FieldData
extend Forwardable
def initialize
@data = {}
end
def_delegators :@data, :[], :[]=
def_delegator :@data, :keys, :markdown_fields
delegate :[], :[]=, to: :@data
def markdown_fields
@data.keys
end
def html_field(markdown_field)
"#{markdown_field}_html"
......@@ -45,7 +46,7 @@ module CacheMarkdownField
Project
Release
Snippet
]
].freeze
def self.caching_classes
CACHING_CLASSES.map(&:constantize)
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment