Commit ae7b2ef6 authored by Douwe Maan's avatar Douwe Maan

Merge branch 'master' into issue_12658

# Conflicts:
#	app/models/issue.rb
#	app/views/projects/_home_panel.html.haml
#	app/views/shared/projects/_project.html.haml
#	db/schema.rb
#	spec/models/project_spec.rb
parents 8d544645 0305dd98
AllCops:
TargetRubyVersion: 2.1
# Cop names are not displayed in offense messages by default. Change behavior
# by overriding DisplayCopNames, or by giving the -D/--display-cop-names
# option.
DisplayCopNames: true
# Style guide URLs are not displayed in offense messages by default. Change
# behavior by overriding DisplayStyleGuide, or by giving the
# -S/--display-style-guide option.
DisplayStyleGuide: false
# Exclude some GitLab files
Exclude:
- 'vendor/**/*'
- 'db/**/*'
- 'tmp/**/*'
- 'bin/**/*'
- 'lib/backup/**/*'
- 'lib/ci/backup/**/*'
- 'lib/tasks/**/*'
- 'lib/ci/migrate/**/*'
- 'lib/email_validator.rb'
- 'lib/gitlab/upgrader.rb'
- 'lib/gitlab/seeder.rb'
##################### Style ##################################
# Check indentation of private/protected visibility modifiers.
Style/AccessModifierIndentation:
Description: Check indentation of private/protected visibility modifiers.
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#indent-public-private-protected'
Enabled: true
# Check the naming of accessor methods for get_/set_.
Style/AccessorMethodName:
Description: Check the naming of accessor methods for get_/set_.
Enabled: false
# Use alias_method instead of alias.
Style/Alias:
Description: 'Use alias_method instead of alias.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#alias-method'
EnforcedStyle: prefer_alias_method
Enabled: true
# Align the elements of an array literal if they span more than one line.
Style/AlignArray:
Description: >-
Align the elements of an array literal if they span more than
one line.
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#align-multiline-arrays'
Enabled: true
# Align the elements of a hash literal if they span more than one line.
Style/AlignHash:
Description: >-
Align the elements of a hash literal if they span more than
one line.
Enabled: true
# Align the parameters of a method call if they span more than one line.
Style/AlignParameters:
Description: >-
Align the parameters of a method call if they span more
than one line.
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-double-indent'
Enabled: false
# Use &&/|| instead of and/or.
Style/AndOr:
Description: 'Use &&/|| instead of and/or.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-and-or-or'
Enabled: false
# Use `Array#join` instead of `Array#*`.
Style/ArrayJoin:
Description: 'Use Array#join instead of Array#*.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#array-join'
Enabled: false
# Use only ascii symbols in comments.
Style/AsciiComments:
Description: 'Use only ascii symbols in comments.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#english-comments'
Enabled: true
# Use only ascii symbols in identifiers.
Style/AsciiIdentifiers:
Description: 'Use only ascii symbols in identifiers.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#english-identifiers'
Enabled: true
# Checks for uses of Module#attr.
Style/Attr:
Description: 'Checks for uses of Module#attr.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#attr'
Enabled: false
# Avoid the use of BEGIN blocks.
Style/BeginBlock:
Description: 'Avoid the use of BEGIN blocks.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-BEGIN-blocks'
Enabled: true
# Checks if usage of %() or %Q() matches configuration.
Style/BarePercentLiterals:
Description: 'Checks if usage of %() or %Q() matches configuration.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#percent-q-shorthand'
Enabled: false
# Do not use block comments.
Style/BlockComments:
Description: 'Do not use block comments.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-block-comments'
Enabled: false
# Put end statement of multiline block on its own line.
Style/BlockEndNewline:
Description: 'Put end statement of multiline block on its own line.'
Enabled: true
# Avoid using {...} for multi-line blocks (multiline chaining is # always
# ugly). Prefer {...} over do...end for single-line blocks.
Style/BlockDelimiters:
Description: >-
Avoid using {...} for multi-line blocks (multiline chaining is
always ugly).
Prefer {...} over do...end for single-line blocks.
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#single-line-blocks'
Enabled: true
# Enforce braces style around hash parameters.
Style/BracesAroundHashParameters:
Description: 'Enforce braces style around hash parameters.'
Enabled: false
# Avoid explicit use of the case equality operator(===).
Style/CaseEquality:
Description: 'Avoid explicit use of the case equality operator(===).'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-case-equality'
Enabled: false
# Indentation of when in a case/when/[else/]end.
Style/CaseIndentation:
Description: 'Indentation of when in a case/when/[else/]end.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#indent-when-to-case'
Enabled: true
# Checks for uses of character literals.
Style/CharacterLiteral:
Description: 'Checks for uses of character literals.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-character-literals'
Enabled: true
# Use CamelCase for classes and modules.'
Style/ClassAndModuleCamelCase:
Description: 'Use CamelCase for classes and modules.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#camelcase-classes'
Enabled: true
# Checks style of children classes and modules.
Style/ClassAndModuleChildren:
Description: 'Checks style of children classes and modules.'
Enabled: false
# Enforces consistent use of `Object#is_a?` or `Object#kind_of?`.
Style/ClassCheck:
Description: 'Enforces consistent use of `Object#is_a?` or `Object#kind_of?`.'
Enabled: false
# Use self when defining module/class methods.
Style/ClassMethods:
Description: 'Use self when defining module/class methods.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#def-self-singletons'
Enabled: false
# Avoid the use of class variables.
Style/ClassVars:
Description: 'Avoid the use of class variables.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-class-vars'
Enabled: true
# Do not use :: for method call.
Style/ColonMethodCall:
Description: 'Do not use :: for method call.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#double-colons'
Enabled: false
# Checks formatting of special comments (TODO, FIXME, OPTIMIZE, HACK, REVIEW).
Style/CommentAnnotation:
Description: >-
Checks formatting of special comments
(TODO, FIXME, OPTIMIZE, HACK, REVIEW).
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#annotate-keywords'
Enabled: false
# Indentation of comments.
Style/CommentIndentation:
Description: 'Indentation of comments.'
Enabled: true
# Use the return value of `if` and `case` statements for assignment to a
# variable and variable comparison instead of assigning that variable
# inside of each branch.
Style/ConditionalAssignment:
Enabled: false
# Constants should use SCREAMING_SNAKE_CASE.
Style/ConstantName:
Description: 'Constants should use SCREAMING_SNAKE_CASE.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#screaming-snake-case'
Enabled: true
# Use def with parentheses when there are arguments.
Style/DefWithParentheses:
Description: 'Use def with parentheses when there are arguments.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#method-parens'
Enabled: false
# Checks for use of deprecated Hash methods.
Style/DeprecatedHashMethods:
Description: 'Checks for use of deprecated Hash methods.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#hash-key'
Enabled: false
# Document classes and non-namespace modules.
Style/Documentation:
Description: 'Document classes and non-namespace modules.'
Enabled: false
# Checks the position of the dot in multi-line method calls.
Style/DotPosition:
Description: 'Checks the position of the dot in multi-line method calls.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#consistent-multi-line-chains'
Enabled: false
# Checks for uses of double negation (!!).
Style/DoubleNegation:
Description: 'Checks for uses of double negation (!!).'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-bang-bang'
Enabled: false
# Prefer `each_with_object` over `inject` or `reduce`.
Style/EachWithObject:
Description: 'Prefer `each_with_object` over `inject` or `reduce`.'
Enabled: false
# Align elses and elsifs correctly.
Style/ElseAlignment:
Description: 'Align elses and elsifs correctly.'
Enabled: true
# Avoid empty else-clauses.
Style/EmptyElse:
Description: 'Avoid empty else-clauses.'
Enabled: false
# Use empty lines between defs.
Style/EmptyLineBetweenDefs:
Description: 'Use empty lines between defs.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#empty-lines-between-methods'
Enabled: false
# Don't use several empty lines in a row.
Style/EmptyLines:
Description: "Don't use several empty lines in a row."
Enabled: false
# Keep blank lines around access modifiers.
Style/EmptyLinesAroundAccessModifier:
Description: "Keep blank lines around access modifiers."
Enabled: false
# Keeps track of empty lines around block bodies.
Style/EmptyLinesAroundBlockBody:
Description: "Keeps track of empty lines around block bodies."
Enabled: false
# Keeps track of empty lines around class bodies.
Style/EmptyLinesAroundClassBody:
Description: "Keeps track of empty lines around class bodies."
Enabled: false
# Keeps track of empty lines around module bodies.
Style/EmptyLinesAroundModuleBody:
Description: "Keeps track of empty lines around module bodies."
Enabled: false
# Keeps track of empty lines around method bodies.
Style/EmptyLinesAroundMethodBody:
Description: "Keeps track of empty lines around method bodies."
Enabled: false
# Prefer literals to Array.new/Hash.new/String.new.
Style/EmptyLiteral:
Description: 'Prefer literals to Array.new/Hash.new/String.new.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#literal-array-hash'
Enabled: false
# Avoid the use of END blocks.
Style/EndBlock:
Description: 'Avoid the use of END blocks.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-END-blocks'
Enabled: false
# Use Unix-style line endings.
Style/EndOfLine:
Description: 'Use Unix-style line endings.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#crlf'
Enabled: false
# Favor the use of Fixnum#even? && Fixnum#odd?
Style/EvenOdd:
Description: 'Favor the use of Fixnum#even? && Fixnum#odd?'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#predicate-methods'
Enabled: false
# Do not use unnecessary spacing.
Style/ExtraSpacing:
Description: 'Do not use unnecessary spacing.'
Enabled: false
# Use snake_case for source file names.
Style/FileName:
Description: 'Use snake_case for source file names.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#snake-case-files'
Enabled: false
# Checks for flip flops.
Style/FlipFlop:
Description: 'Checks for flip flops'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-flip-flops'
Enabled: false
# Checks use of for or each in multiline loops.
Style/For:
Description: 'Checks use of for or each in multiline loops.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-for-loops'
Enabled: false
# Enforce the use of Kernel#sprintf, Kernel#format or String#%.
Style/FormatString:
Description: 'Enforce the use of Kernel#sprintf, Kernel#format or String#%.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#sprintf'
Enabled: false
# Do not introduce global variables.
Style/GlobalVars:
Description: 'Do not introduce global variables.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#instance-vars'
Enabled: false
# Check for conditionals that can be replaced with guard clauses.
Style/GuardClause:
Description: 'Check for conditionals that can be replaced with guard clauses'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-nested-conditionals'
Enabled: false
# Prefer Ruby 1.9 hash syntax `{ a: 1, b: 2 }`
# over 1.8 syntax `{ :a => 1, :b => 2 }`.
Style/HashSyntax:
Description: >-
Prefer Ruby 1.9 hash syntax { a: 1, b: 2 } over 1.8 syntax
{ :a => 1, :b => 2 }.
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#hash-literals'
Enabled: true
# Finds if nodes inside else, which can be converted to elsif.
Style/IfInsideElse:
Enabled: false
# Favor modifier if/unless usage when you have a single-line body.
Style/IfUnlessModifier:
Description: >-
Favor modifier if/unless usage when you have a
single-line body.
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#if-as-a-modifier'
Enabled: false
# Do not use if x; .... Use the ternary operator instead.
Style/IfWithSemicolon:
Description: 'Do not use if x; .... Use the ternary operator instead.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-semicolon-ifs'
Enabled: false
# Checks that conditional statements do not have an identical line at the
# end of each branch, which can validly be moved out of the conditional.
Style/IdenticalConditionalBranches:
Enabled: false
# Checks the indentation of the first line of the right-hand-side of a
# multi-line assignment.
Style/IndentAssignment:
Enabled: false
# Keep indentation straight.
Style/IndentationConsistency:
Description: 'Keep indentation straight.'
Enabled: true
# Use 2 spaces for indentation.
Style/IndentationWidth:
Description: 'Use 2 spaces for indentation.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-indentation'
Enabled: true
# Checks the indentation of the first element in an array literal.
Style/IndentArray:
Description: >-
Checks the indentation of the first element in an array
literal.
Enabled: false
# Checks the indentation of the first key in a hash literal.
Style/IndentHash:
Description: 'Checks the indentation of the first key in a hash literal.'
Enabled: false
# Use Kernel#loop for infinite loops.
Style/InfiniteLoop:
Description: 'Use Kernel#loop for infinite loops.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#infinite-loop'
Enabled: false
# Use the new lambda literal syntax for single-line blocks.
Style/Lambda:
Description: 'Use the new lambda literal syntax for single-line blocks.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#lambda-multi-line'
Enabled: false
# Use lambda.call(...) instead of lambda.(...).
Style/LambdaCall:
Description: 'Use lambda.call(...) instead of lambda.(...).'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#proc-call'
Enabled: false
# Comments should start with a space.
Style/LeadingCommentSpace:
Description: 'Comments should start with a space.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#hash-space'
Enabled: false
# Use \ instead of + or << to concatenate two string literals at line end.
Style/LineEndConcatenation:
Description: >-
Use \ instead of + or << to concatenate two string literals at
line end.
Enabled: false
# Do not use parentheses for method calls with no arguments.
Style/MethodCallParentheses:
Description: 'Do not use parentheses for method calls with no arguments.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-args-no-parens'
Enabled: false
# Checks if the method definitions have or don't have parentheses.
Style/MethodDefParentheses:
Description: >-
Checks if the method definitions have or don't have
parentheses.
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#method-parens'
Enabled: false
# Use the configured style when naming methods.
Style/MethodName:
Description: 'Use the configured style when naming methods.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#snake-case-symbols-methods-vars'
Enabled: false
# Checks for usage of `extend self` in modules.
Style/ModuleFunction:
Description: 'Checks for usage of `extend self` in modules.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#module-function'
Enabled: false
# Avoid multi-line chains of blocks.
Style/MultilineBlockChain:
Description: 'Avoid multi-line chains of blocks.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#single-line-blocks'
Enabled: false
# Ensures newlines after multiline block do statements.
Style/MultilineBlockLayout:
Description: 'Ensures newlines after multiline block do statements.'
Enabled: true
# Do not use then for multi-line if/unless.
Style/MultilineIfThen:
Description: 'Do not use then for multi-line if/unless.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-then'
Enabled: false
# Checks indentation of method calls with the dot operator that span more than
# one line.
Style/MultilineMethodCallIndentation:
Enabled: false
# Checks indentation of binary operations that span more than one line.
Style/MultilineOperationIndentation:
Description: >-
Checks indentation of binary operations that span more than
one line.
Enabled: false
# Avoid multi-line `? :` (the ternary operator), use if/unless instead.
Style/MultilineTernaryOperator:
Description: >-
Avoid multi-line ?: (the ternary operator);
use if/unless instead.
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-multiline-ternary'
Enabled: false
# Do not assign mutable objects to constants.
Style/MutableConstant:
Enabled: false
# Favor unless over if for negative conditions (or control flow or).
Style/NegatedIf:
Description: >-
Favor unless over if for negative conditions
(or control flow or).
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#unless-for-negatives'
Enabled: false
# Favor until over while for negative conditions.
Style/NegatedWhile:
Description: 'Favor until over while for negative conditions.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#until-for-negatives'
Enabled: false
# Avoid using nested modifiers.
Style/NestedModifier:
Enabled: false
# Parenthesize method calls which are nested inside the argument list of
# another parenthesized method call.
Style/NestedParenthesizedCalls:
Enabled: false
# Use one expression per branch in a ternary operator.
Style/NestedTernaryOperator:
Description: 'Use one expression per branch in a ternary operator.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-nested-ternary'
Enabled: true
# Use `next` to skip iteration instead of a condition at the end.
Style/Next:
Description: 'Use `next` to skip iteration instead of a condition at the end.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-nested-conditionals'
Enabled: false
# Prefer x.nil? to x == nil.
Style/NilComparison:
Description: 'Prefer x.nil? to x == nil.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#predicate-methods'
Enabled: true
# Checks for redundant nil checks.
Style/NonNilCheck:
Description: 'Checks for redundant nil checks.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-non-nil-checks'
Enabled: true
# Use ! instead of not.
Style/Not:
Description: 'Use ! instead of not.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#bang-not-not'
Enabled: true
# Add underscores to large numeric literals to improve their readability.
Style/NumericLiterals:
Description: >-
Add underscores to large numeric literals to improve their
readability.
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#underscores-in-numerics'
Enabled: false
# Favor the ternary operator(?:) over if/then/else/end constructs.
Style/OneLineConditional:
Description: >-
Favor the ternary operator(?:) over
if/then/else/end constructs.
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#ternary-operator'
Enabled: true
# When defining binary operators, name the argument other.
Style/OpMethod:
Description: 'When defining binary operators, name the argument other.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#other-arg'
Enabled: false
# Check for simple usages of parallel assignment. It will only warn when
# the number of variables matches on both sides of the assignment.
Style/ParallelAssignment:
Description: >-
Check for simple usages of parallel assignment.
It will only warn when the number of variables
matches on both sides of the assignment.
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#parallel-assignment'
Enabled: false
# Don't use parentheses around the condition of an if/unless/while.
Style/ParenthesesAroundCondition:
Description: >-
Don't use parentheses around the condition of an
if/unless/while.
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-parens-if'
Enabled: true
# Use `%`-literal delimiters consistently.
Style/PercentLiteralDelimiters:
Description: 'Use `%`-literal delimiters consistently'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#percent-literal-braces'
Enabled: false
# Checks if uses of %Q/%q match the configured preference.
Style/PercentQLiterals:
Description: 'Checks if uses of %Q/%q match the configured preference.'
Enabled: false
# Avoid Perl-style regex back references.
Style/PerlBackrefs:
Description: 'Avoid Perl-style regex back references.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-perl-regexp-last-matchers'
Enabled: false
# Check the names of predicate methods.
Style/PredicateName:
Description: 'Check the names of predicate methods.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#bool-methods-qmark'
Enabled: false
# Use proc instead of Proc.new.
Style/Proc:
Description: 'Use proc instead of Proc.new.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#proc'
Enabled: false
# Checks the arguments passed to raise/fail.
Style/RaiseArgs:
Description: 'Checks the arguments passed to raise/fail.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#exception-class-messages'
Enabled: false
# Don't use begin blocks when they are not needed.
Style/RedundantBegin:
Description: "Don't use begin blocks when they are not needed."
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#begin-implicit'
Enabled: false
# Checks for an obsolete RuntimeException argument in raise/fail.
Style/RedundantException:
Description: "Checks for an obsolete RuntimeException argument in raise/fail."
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-explicit-runtimeerror'
Enabled: false
# Checks usages of Object#freeze on immutable objects.
Style/RedundantFreeze:
Enabled: false
# TODO: Enable RedundantParentheses Cop.
# Checks for parentheses that seem not to serve any purpose.
Style/RedundantParentheses:
Enabled: false
# Don't use return where it's not required.
Style/RedundantReturn:
Description: "Don't use return where it's not required."
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-explicit-return'
Enabled: true
# Don't use self where it's not needed.
Style/RedundantSelf:
Description: "Don't use self where it's not needed."
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-self-unless-required'
Enabled: false
# Use %r for regular expressions matching more than `MaxSlashes` '/'
# characters. Use %r only for regular expressions matching more
# than `MaxSlashes` '/' character.
Style/RegexpLiteral:
Description: >-
Use %r for regular expressions matching more than
`MaxSlashes` '/' characters.
Use %r only for regular expressions matching more than
`MaxSlashes` '/' character.
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#percent-r'
Enabled: false
# Avoid using rescue in its modifier form.
Style/RescueModifier:
Description: 'Avoid using rescue in its modifier form.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-rescue-modifiers'
Enabled: false
# Checks for places where self-assignment shorthand should have been used.
Style/SelfAssignment:
Description: >-
Checks for places where self-assignment shorthand should have
been used.
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#self-assignment'
Enabled: false
# Don't use semicolons to terminate expressions.
Style/Semicolon:
Description: "Don't use semicolons to terminate expressions."
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-semicolon'
Enabled: false
# Checks for proper usage of fail and raise.
Style/SignalException:
Description: 'Checks for proper usage of fail and raise.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#fail-method'
Enabled: false
# Enforces the names of some block params.
Style/SingleLineBlockParams:
Description: 'Enforces the names of some block params.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#reduce-blocks'
Enabled: false
# Avoid single-line methods.
Style/SingleLineMethods:
Description: 'Avoid single-line methods.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-single-line-methods'
Enabled: false
Style/SingleSpaceBeforeFirstArg:
Description: >-
Checks that exactly one space is used between a method name
and the first argument for method calls without parentheses.
Enabled: false
# Use spaces after colons.
Style/SpaceAfterColon:
Description: 'Use spaces after colons.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-operators'
Enabled: false
# Use spaces after commas.
Style/SpaceAfterComma:
Description: 'Use spaces after commas.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-operators'
Enabled: false
Style/SpaceAfterControlKeyword:
Description: 'Use spaces after if/elsif/unless/while/until/case/when.'
Enabled: false
# Do not put a space between a method name and the opening parenthesis in a
# method definition.
Style/SpaceAfterMethodName:
Description: >-
Do not put a space between a method name and the opening
parenthesis in a method definition.
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#parens-no-spaces'
Enabled: false
# Tracks redundant space after the ! operator.
Style/SpaceAfterNot:
Description: Tracks redundant space after the ! operator.
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-space-bang'
Enabled: false
# Use spaces after semicolons.
Style/SpaceAfterSemicolon:
Description: 'Use spaces after semicolons.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-operators'
Enabled: false
Style/SpaceBeforeBlockBraces:
Description: >-
Checks that the left block brace has or doesn't have space
before it.
# Checks that the equals signs in parameter default assignments have or don't
# have surrounding space depending on configuration.
Style/SpaceAroundEqualsInParameterDefault:
Enabled: false
Style/SpaceBeforeComma:
Description: 'No spaces before commas.'
# TODO: Enable SpaceAroundKeyword Cop.
# Use a space around keywords if appropriate.
Style/SpaceAroundKeyword:
Enabled: false
Style/SpaceBeforeComment:
Description: >-
Checks for missing space between code and a comment on the
same line.
# Use a single space around operators.
Style/SpaceAroundOperators:
Enabled: false
Style/SpaceBeforeSemicolon:
Description: 'No spaces before semicolons.'
# Checks that the left block brace has or doesn't have space before it.
Style/SpaceBeforeBlockBraces:
Enabled: false
Style/SpaceInsideBlockBraces:
Description: >-
Checks that block braces have or don't have surrounding space.
For blocks taking parameters, checks that the left brace has
or doesn't have trailing space.
# No spaces before commas.
Style/SpaceBeforeComma:
Enabled: false
Style/SpaceAroundEqualsInParameterDefault:
Description: >-
Checks that the equals signs in parameter default assignments
have or don't have surrounding space depending on
configuration.
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-around-equals'
# Checks for missing space between code and a comment on the same line.
Style/SpaceBeforeComment:
Enabled: false
Style/SpaceAroundOperators:
Description: 'Use spaces around operators.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-operators'
# Checks that exactly one space is used between a method name and the first
# argument for method calls without parentheses.
Style/SpaceBeforeFirstArg:
Enabled: false
# No spaces before semicolons.
Style/SpaceBeforeSemicolon:
Enabled: false
Style/SpaceBeforeModifierKeyword:
Description: 'Put a space before the modifier keyword.'
# Checks that block braces have or don't have surrounding space.
# For blocks taking parameters, checks that the left brace has or doesn't
# have trailing space.
Style/SpaceInsideBlockBraces:
Enabled: false
# No spaces after [ or before ].
Style/SpaceInsideBrackets:
Description: 'No spaces after [ or before ].'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-spaces-braces'
Enabled: false
# Use spaces inside hash literal braces - or don't.
Style/SpaceInsideHashLiteralBraces:
Description: "Use spaces inside hash literal braces - or don't."
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-operators'
Enabled: true
# No spaces after ( or before ).
Style/SpaceInsideParens:
Description: 'No spaces after ( or before ).'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-spaces-braces'
Enabled: false
# No spaces inside range literals.
Style/SpaceInsideRangeLiteral:
Description: 'No spaces inside range literals.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-space-inside-range-literals'
Enabled: false
# Checks for padding/surrounding spaces inside string interpolation.
Style/SpaceInsideStringInterpolation:
Enabled: false
# Avoid Perl-style global variables.
Style/SpecialGlobalVars:
Description: 'Avoid Perl-style global variables.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-cryptic-perlisms'
Enabled: false
# Check for the usage of parentheses around stabby lambda arguments.
Style/StabbyLambdaParentheses:
Enabled: false
# Checks if uses of quotes match the configured preference.
Style/StringLiterals:
Description: 'Checks if uses of quotes match the configured preference.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#consistent-string-literals'
Enabled: false
# Checks if uses of quotes inside expressions in interpolated strings match the
# configured preference.
Style/StringLiteralsInInterpolation:
Description: >-
Checks if uses of quotes inside expressions in interpolated
strings match the configured preference.
Enabled: false
# Checks if configured preferred methods are used over non-preferred.
Style/StringMethods:
Enabled: false
# Use %i or %I for arrays of symbols.
Style/SymbolArray:
Enabled: false
# Use symbols as procs instead of blocks when possible.
Style/SymbolProc:
Description: 'Use symbols as procs instead of blocks when possible.'
Enabled: false
# No hard tabs.
Style/Tab:
Description: 'No hard tabs.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-indentation'
Enabled: true
# Checks trailing blank lines and final newline.
Style/TrailingBlankLines:
Description: 'Checks trailing blank lines and final newline.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#newline-eof'
Enabled: true
Style/TrailingComma:
Description: 'Checks for trailing comma in parameter lists and literals.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-trailing-array-commas'
# Checks for trailing comma in array and hash literals.
Style/TrailingCommaInLiteral:
Enabled: false
# Checks for trailing comma in argument lists.
Style/TrailingCommaInArguments:
Enabled: false
# Avoid trailing whitespace.
Style/TrailingWhitespace:
Description: 'Avoid trailing whitespace.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-trailing-whitespace'
Enabled: false
# Checks for the usage of unneeded trailing underscores at the end of
# parallel variable assignment.
Style/TrailingUnderscoreVariable:
Description: >-
Checks for the usage of unneeded trailing underscores at the
end of parallel variable assignment.
AllowNamedUnderscoreVariables: true
Enabled: false
# Prefer attr_* methods to trivial readers/writers.
Style/TrivialAccessors:
Description: 'Prefer attr_* methods to trivial readers/writers.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#attr_family'
Enabled: false
# Do not use unless with else. Rewrite these with the positive case first.
Style/UnlessElse:
Description: >-
Do not use unless with else. Rewrite these with the positive
case first.
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-else-with-unless'
Enabled: false
# Checks for %W when interpolation is not needed.
Style/UnneededCapitalW:
Description: 'Checks for %W when interpolation is not needed.'
Enabled: false
# TODO: Enable UnneededInterpolation Cop.
# Checks for strings that are just an interpolated expression.
Style/UnneededInterpolation:
Enabled: false
# Checks for %q/%Q when single quotes or double quotes would do.
Style/UnneededPercentQ:
Description: 'Checks for %q/%Q when single quotes or double quotes would do.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#percent-q'
Enabled: false
# Don't interpolate global, instance and class variables directly in strings.
Style/VariableInterpolation:
Description: >-
Don't interpolate global, instance and class variables
directly in strings.
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#curlies-interpolate'
Enabled: false
# Use the configured style when naming variables.
Style/VariableName:
Description: 'Use the configured style when naming variables.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#snake-case-symbols-methods-vars'
Enabled: false
# Use when x then ... for one-line cases.
Style/WhenThen:
Description: 'Use when x then ... for one-line cases.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#one-line-cases'
Enabled: false
# Checks for redundant do after while or until.
Style/WhileUntilDo:
Description: 'Checks for redundant do after while or until.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-multiline-while-do'
Enabled: false
# Favor modifier while/until usage when you have a single-line body.
Style/WhileUntilModifier:
Description: >-
Favor modifier while/until usage when you have a
single-line body.
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#while-as-a-modifier'
Enabled: false
# Use %w or %W for arrays of words.
Style/WordArray:
Description: 'Use %w or %W for arrays of words.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#percent-w'
Enabled: false
# TODO: Enable ZeroLengthPredicate Cop.
# Use #empty? when testing for objects of length 0.
Style/ZeroLengthPredicate:
Enabled: false
#################### Metrics ################################
# A calculated magnitude based on number of assignments,
# branches, and conditions.
Metrics/AbcSize:
Description: >-
A calculated magnitude based on number of assignments,
branches, and conditions.
Enabled: true
Max: 70
Metrics/CyclomaticComplexity:
Description: >-
A complexity metric that is strongly correlated to the number
of test cases needed to validate a method.
Enabled: true
Max: 17
Metrics/PerceivedComplexity:
Description: >-
A complexity metric geared towards measuring complexity for a
human reader.
Enabled: true
Max: 17
Metrics/ParameterLists:
Description: 'Avoid parameter lists longer than three or four parameters.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#too-many-params'
Enabled: true
Max: 8
# Avoid excessive block nesting.
Metrics/BlockNesting:
Description: 'Avoid excessive block nesting'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#three-is-the-number-thou-shalt-count'
Enabled: true
Max: 4
# Avoid classes longer than 100 lines of code.
Metrics/ClassLength:
Description: 'Avoid classes longer than 100 lines of code.'
Enabled: false
# A complexity metric that is strongly correlated to the number
# of test cases needed to validate a method.
Metrics/CyclomaticComplexity:
Enabled: true
Max: 17
# Limit lines to 80 characters.
Metrics/LineLength:
Description: 'Limit lines to 80 characters.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#80-character-limits'
Enabled: false
# Avoid methods longer than 10 lines of code.
Metrics/MethodLength:
Description: 'Avoid methods longer than 10 lines of code.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#short-methods'
Enabled: false
# Avoid modules longer than 100 lines of code.
Metrics/ModuleLength:
Description: 'Avoid modules longer than 100 lines of code.'
Enabled: false
# Avoid parameter lists longer than three or four parameters.
Metrics/ParameterLists:
Enabled: true
Max: 8
# A complexity metric geared towards measuring complexity for a human reader.
Metrics/PerceivedComplexity:
Enabled: true
Max: 17
#################### Lint ################################
### Warnings
# Checks for ambiguous operators in the first argument of a method invocation
# without parentheses.
Lint/AmbiguousOperator:
Description: >-
Checks for ambiguous operators in the first argument of a
method invocation without parentheses.
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#parens-as-args'
Enabled: false
# Checks for ambiguous regexp literals in the first argument of a method
# invocation without parentheses.
Lint/AmbiguousRegexpLiteral:
Description: >-
Checks for ambiguous regexp literals in the first argument of
a method invocation without parenthesis.
Enabled: false
# Don't use assignment in conditions.
Lint/AssignmentInCondition:
Description: "Don't use assignment in conditions."
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#safe-assignment-in-condition'
Enabled: false
# Align block ends correctly.
Lint/BlockAlignment:
Description: 'Align block ends correctly.'
Enabled: false
# Default values in optional keyword arguments and optional ordinal arguments
# should not refer back to the name of the argument.
Lint/CircularArgumentReference:
Enabled: false
# Checks for condition placed in a confusing position relative to the keyword.
Lint/ConditionPosition:
Description: >-
Checks for condition placed in a confusing position relative to
the keyword.
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#same-line-condition'
Enabled: false
# Check for debugger calls.
Lint/Debugger:
Description: 'Check for debugger calls.'
Enabled: false
# Align ends corresponding to defs correctly.
Lint/DefEndAlignment:
Description: 'Align ends corresponding to defs correctly.'
Enabled: false
# Check for deprecated class method calls.
Lint/DeprecatedClassMethods:
Description: 'Check for deprecated class method calls.'
Enabled: false
# Check for duplicate method definitions.
Lint/DuplicateMethods:
Enabled: false
# Check for duplicate keys in hash literals.
Lint/DuplicatedKey:
Enabled: false
# Check for immutable argument given to each_with_object.
Lint/EachWithObjectArgument:
Enabled: false
# Check for odd code arrangement in an else block.
Lint/ElseLayout:
Description: 'Check for odd code arrangement in an else block.'
Enabled: false
# Checks for empty ensure block.
Lint/EmptyEnsure:
Description: 'Checks for empty ensure block.'
Enabled: false
# Checks for empty string interpolation.
Lint/EmptyInterpolation:
Description: 'Checks for empty string interpolation.'
Enabled: false
# Align ends correctly.
Lint/EndAlignment:
Description: 'Align ends correctly.'
Enabled: false
# END blocks should not be placed inside method definitions.
Lint/EndInMethod:
Description: 'END blocks should not be placed inside method definitions.'
Enabled: false
# Do not use return in an ensure block.
Lint/EnsureReturn:
Description: 'Do not use return in an ensure block.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-return-ensure'
Enabled: false
# The use of eval represents a serious security risk.
Lint/Eval:
Description: 'The use of eval represents a serious security risk.'
Enabled: false
# Catches floating-point literals too large or small for Ruby to represent.
Lint/FloatOutOfRange:
Enabled: false
# The number of parameters to format/sprint must match the fields.
Lint/FormatParameterMismatch:
Enabled: false
# Don't suppress exception.
Lint/HandleExceptions:
Description: "Don't suppress exception."
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#dont-hide-exceptions'
Enabled: false
# TODO: Enable ImplicitStringConcatenation Cop.
# Checks for adjacent string literals on the same line, which could better be
# represented as a single string literal.
Lint/ImplicitStringConcatenation:
Enabled: false
# TODO: Enable IneffectiveAccessModifier Cop.
# Checks for attempts to use `private` or `protected` to set the visibility
# of a class method, which does not work.
Lint/IneffectiveAccessModifier:
Enabled: false
# Checks for invalid character literals with a non-escaped whitespace
# character.
Lint/InvalidCharacterLiteral:
Description: >-
Checks for invalid character literals with a non-escaped
whitespace character.
Enabled: false
# Checks of literals used in conditions.
Lint/LiteralInCondition:
Description: 'Checks of literals used in conditions.'
Enabled: false
# Checks for literals used in interpolation.
Lint/LiteralInInterpolation:
Description: 'Checks for literals used in interpolation.'
Enabled: false
# Use Kernel#loop with break rather than begin/end/until or begin/end/while
# for post-loop tests.
Lint/Loop:
Description: >-
Use Kernel#loop with break rather than begin/end/until or
begin/end/while for post-loop tests.
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#loop-with-break'
Enabled: false
# Do not use nested method definitions.
Lint/NestedMethodDefinition:
Enabled: false
# Do not omit the accumulator when calling `next` in a `reduce`/`inject` block.
Lint/NextWithoutAccumulator:
Enabled: false
# Checks for method calls with a space before the opening parenthesis.
Lint/ParenthesesAsGroupedExpression:
Description: >-
Checks for method calls with a space before the opening
parenthesis.
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#parens-no-spaces'
Enabled: true
# Checks for `rand(1)` calls. Such calls always return `0` and most likely
# a mistake.
Lint/RandOne:
Enabled: false
# Use parentheses in the method call to avoid confusion about precedence.
Lint/RequireParentheses:
Description: >-
Use parentheses in the method call to avoid confusion
about precedence.
Enabled: false
# Avoid rescuing the Exception class.
Lint/RescueException:
Description: 'Avoid rescuing the Exception class.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-blind-rescues'
Enabled: true
# Do not use the same name as outer local variable for block arguments
# or block local variables.
Lint/ShadowingOuterLocalVariable:
Description: >-
Do not use the same name as outer local variable
for block arguments or block local variables.
Enabled: false
Lint/SpaceBeforeFirstArg:
Description: >-
Put a space between a method name and the first argument
in a method call without parentheses.
Enabled: false
# 'Checks for Object#to_s usage in string interpolation.
Lint/StringConversionInInterpolation:
Description: 'Checks for Object#to_s usage in string interpolation.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-to-s'
Enabled: false
# Do not use prefix `_` for a variable that is used.
Lint/UnderscorePrefixedVariableName:
Description: 'Do not use prefix `_` for a variable that is used.'
Enabled: true
# Checks for rubocop:disable comments that can be removed.
# Note: this cop is not disabled when disabling all cops.
# It must be explicitly disabled.
Lint/UnneededDisable:
Enabled: false
# Checks for unused block arguments.
Lint/UnusedBlockArgument:
Description: 'Checks for unused block arguments.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#underscore-unused-vars'
Enabled: false
# Checks for unused method arguments.
Lint/UnusedMethodArgument:
Description: 'Checks for unused method arguments.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#underscore-unused-vars'
Enabled: false
# Unreachable code.
Lint/UnreachableCode:
Description: 'Unreachable code.'
Enabled: false
# Checks for useless access modifiers.
Lint/UselessAccessModifier:
Description: 'Checks for useless access modifiers.'
Enabled: false
# Checks for useless assignment to a local variable.
Lint/UselessAssignment:
Description: 'Checks for useless assignment to a local variable.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#underscore-unused-vars'
Enabled: true
# Checks for comparison of something with itself.
Lint/UselessComparison:
Description: 'Checks for comparison of something with itself.'
Enabled: false
# Checks for useless `else` in `begin..end` without `rescue`.
Lint/UselessElseWithoutRescue:
Description: 'Checks for useless `else` in `begin..end` without `rescue`.'
Enabled: false
# Checks for useless setter call to a local variable.
Lint/UselessSetterCall:
Description: 'Checks for useless setter call to a local variable.'
Enabled: false
# Possible use of operator/literal/variable in void context.
Lint/Void:
Description: 'Possible use of operator/literal/variable in void context.'
Enabled: false
##################### Performance ############################
# TODO: Enable Casecmp Cop.
# Use `casecmp` rather than `downcase ==`.
Performance/Casecmp:
Enabled: false
# TODO: Enable DoubleStartEndWith Cop.
# Use `str.{start,end}_with?(x, ..., y, ...)` instead of
# `str.{start,end}_with?(x, ...) || str.{start,end}_with?(y, ...)`.
Performance/DoubleStartEndWith:
Enabled: false
# TODO: Enable EndWith Cop.
# Use `end_with?` instead of a regex match anchored to the end of a string.
Performance/EndWith:
Enabled: false
# TODO: Enable LstripRstrip Cop.
# Use `strip` instead of `lstrip.rstrip`.
Performance/LstripRstrip:
Enabled: false
# TODO: Enable RangeInclude Cop.
# Use `Range#cover?` instead of `Range#include?`.
Performance/RangeInclude:
Enabled: false
# TODO: Enable RedundantBlockCall Cop.
# Use `yield` instead of `block.call`.
Performance/RedundantBlockCall:
Enabled: false
# TODO: Enable RedundantMatch Cop.
# Use `=~` instead of `String#match` or `Regexp#match` in a context where the
# returned `MatchData` is not needed.
Performance/RedundantMatch:
Enabled: false
# TODO: Enable RedundantMerge Cop.
# Use `Hash#[]=`, rather than `Hash#merge!` with a single key-value pair.
Performance/RedundantMerge:
# Max number of key-value pairs to consider an offense
MaxKeyValuePairs: 2
Enabled: false
# TODO: Enable RedundantSortBy Cop.
# Use `sort` instead of `sort_by { |x| x }`.
Performance/RedundantSortBy:
Enabled: false
# TODO: Enable StartWith Cop.
# Use `start_with?` instead of a regex match anchored to the beginning of a
# string.
Performance/StartWith:
Enabled: false
# Use `tr` instead of `gsub` when you are replacing the same number of
# characters. Use `delete` instead of `gsub` when you are deleting
# characters.
Performance/StringReplacement:
Enabled: false
# TODO: Enable TimesMap Cop.
# Checks for `.times.map` calls.
Performance/TimesMap:
Enabled: false
##################### Rails ##################################
# Enables Rails cops.
Rails:
Enabled: true
# Enforces consistent use of action filter methods.
Rails/ActionFilter:
Description: 'Enforces consistent use of action filter methods.'
Enabled: true
EnforcedStyle: action
# Checks the correct usage of date aware methods, such as `Date.today`,
# `Date.current`, etc.
Rails/Date:
Description: >-
Checks the correct usage of date aware methods,
such as Date.today, Date.current etc.
Enabled: false
Rails/DefaultScope:
Description: 'Checks if the argument passed to default_scope is a block.'
# Prefer delegate method for delegations.
Rails/Delegate:
Enabled: false
Rails/Delegate:
Description: 'Prefer delegate method for delegations.'
# Prefer `find_by` over `where.first`.
Rails/FindBy:
Enabled: false
# Prefer `all.find_each` over `all.find`.
Rails/FindEach:
Enabled: false
# Prefer has_many :through to has_and_belongs_to_many.
Rails/HasAndBelongsToMany:
Description: 'Prefer has_many :through to has_and_belongs_to_many.'
Enabled: true
# Checks for calls to puts, print, etc.
Rails/Output:
Description: 'Checks for calls to puts, print, etc.'
Enabled: true
# Checks for incorrect grammar when using methods like `3.day.ago`.
Rails/PluralizationGrammar:
Enabled: false
# Checks for `read_attribute(:attr)` and `write_attribute(:attr, val)`.
Rails/ReadWriteAttribute:
Description: >-
Checks for read_attribute(:attr) and
write_attribute(:attr, val).
Enabled: false
# Checks the arguments of ActiveRecord scopes.
Rails/ScopeArgs:
Description: 'Checks the arguments of ActiveRecord scopes.'
Enabled: false
# Checks the correct usage of time zone aware methods.
# http://danilenko.org/2012/7/6/rails_timezones
Rails/TimeZone:
Description: 'Checks the correct usage of time zone aware methods.'
StyleGuide: 'https://github.com/bbatsov/rails-style-guide#time'
Reference: 'http://danilenko.org/2012/7/6/rails_timezones'
Enabled: false
# Use validates :attribute, hash of validations.
Rails/Validation:
Description: 'Use validates :attribute, hash of validations.'
Enabled: false
# Exclude some of GitLab files
#
#
AllCops:
RunRailsCops: true
Exclude:
- 'vendor/**/*'
- 'db/**/*'
- 'tmp/**/*'
- 'bin/**/*'
- 'lib/backup/**/*'
- 'lib/ci/backup/**/*'
- 'lib/tasks/**/*'
- 'lib/ci/migrate/**/*'
- 'lib/email_validator.rb'
- 'lib/gitlab/upgrader.rb'
- 'lib/gitlab/seeder.rb'
Please view this file on the master branch, on stable branches it's out of date.
v 8.6.0 (unreleased)
- Add ability to move issue to another project
- Prevent tokens in the import URL to be showed by the UI
- Fix bug where wrong commit ID was being used in a merge request diff to show old image (Stan Hu)
- Make HTTP(s) label consistent on clone bar (Stan Hu)
- Add confidential issues
- Bump gitlab_git to 9.0.3 (Stan Hu)
- Fix diff image view modes (2-up, swipe, onion skin) not working (Stan Hu)
- Support Golang subpackage fetching (Stan Hu)
- Bump Capybara gem to 2.6.2 (Stan Hu)
- New branch button appears on issues where applicable
......@@ -57,6 +61,7 @@ v 8.6.0 (unreleased)
- User deletion is now done in the background so the request can not time out
- Canceled builds are now ignored in compound build status if marked as `allowed to fail`
- Trigger a todo for mentions on commits page
- Let project owners and admins soft delete issues and merge requests
v 8.5.8
- Bump Git version requirement to 2.7.4
......
......@@ -222,6 +222,8 @@ gem 'net-ssh', '~> 3.0.1'
# Sentry integration
gem 'sentry-raven', '~> 0.15'
gem 'premailer-rails', '~> 1.9.0'
# Metrics
group :metrics do
gem 'allocations', '~> 1.0', require: false, platform: :mri
......@@ -285,7 +287,7 @@ group :development, :test do
gem 'spring-commands-spinach', '~> 1.0.0'
gem 'spring-commands-teaspoon', '~> 0.0.2'
gem 'rubocop', '~> 0.35.0', require: false
gem 'rubocop', '~> 0.38.0', require: false
gem 'scss_lint', '~> 0.47.0', require: false
gem 'coveralls', '~> 0.8.2', require: false
gem 'simplecov', '~> 0.10.0', require: false
......
......@@ -61,9 +61,7 @@ GEM
faraday_middleware-multi_json (~> 0.0)
oauth2 (~> 1.0)
asciidoctor (1.5.3)
ast (2.1.0)
astrolabe (1.3.1)
parser (~> 2.2)
ast (2.2.0)
attr_encrypted (1.3.4)
encryptor (>= 1.3.0)
attr_required (1.0.0)
......@@ -150,6 +148,8 @@ GEM
crack (0.4.3)
safe_yaml (~> 1.0.0)
creole (0.5.0)
css_parser (1.3.7)
addressable
d3_rails (3.5.11)
railties (>= 3.1.0)
daemons (1.2.3)
......@@ -423,6 +423,7 @@ GEM
haml (~> 4.0.0)
nokogiri (~> 1.6.0)
ruby_parser (~> 3.5)
htmlentities (4.3.4)
http-cookie (1.0.2)
domain_name (~> 0.5)
http_parser.rb (0.5.3)
......@@ -554,8 +555,8 @@ GEM
orm_adapter (0.5.0)
paranoia (2.1.4)
activerecord (~> 4.0)
parser (2.2.3.0)
ast (>= 1.1, < 3.0)
parser (2.3.0.6)
ast (~> 2.2)
pg (0.18.4)
poltergeist (1.9.0)
capybara (~> 2.1)
......@@ -564,6 +565,12 @@ GEM
websocket-driver (>= 0.2.0)
posix-spawn (0.3.11)
powerpack (0.1.1)
premailer (1.8.6)
css_parser (>= 1.3.6)
htmlentities (>= 4.0.0)
premailer-rails (1.9.0)
actionmailer (>= 3, < 5)
premailer (~> 1.7, >= 1.7.9)
pry (0.10.3)
coderay (~> 1.1.0)
method_source (~> 0.8.1)
......@@ -615,7 +622,7 @@ GEM
activesupport (= 4.2.5.2)
rake (>= 0.8.7)
thor (>= 0.18.1, < 2.0)
rainbow (2.0.0)
rainbow (2.1.0)
raindrops (0.15.0)
rake (10.5.0)
raphael-rails (2.1.2)
......@@ -687,13 +694,12 @@ GEM
rspec-retry (0.4.5)
rspec-core
rspec-support (3.3.0)
rubocop (0.35.1)
astrolabe (~> 1.3)
parser (>= 2.2.3.0, < 3.0)
rubocop (0.38.0)
parser (>= 2.3.0.6, < 3.0)
powerpack (~> 0.1)
rainbow (>= 1.99.1, < 3.0)
ruby-progressbar (~> 1.7)
tins (<= 1.6.0)
unicode-display_width (~> 1.0, >= 1.0.1)
ruby-fogbugz (0.2.1)
crack (~> 0.4)
ruby-progressbar (1.7.5)
......@@ -843,6 +849,7 @@ GEM
unf (0.1.4)
unf_ext
unf_ext (0.0.7.1)
unicode-display_width (1.0.2)
unicorn (4.9.0)
kgio (~> 2.6)
rack
......@@ -992,6 +999,7 @@ DEPENDENCIES
paranoia (~> 2.0)
pg (~> 0.18.2)
poltergeist (~> 1.9.0)
premailer-rails (~> 1.9.0)
pry-rails
quiet_assets (~> 1.0.2)
rack-attack (~> 4.3.1)
......@@ -1013,7 +1021,7 @@ DEPENDENCIES
rqrcode-rails3 (~> 0.1.7)
rspec-rails (~> 3.3.0)
rspec-retry
rubocop (~> 0.35.0)
rubocop (~> 0.38.0)
ruby-fogbugz (~> 0.2.1)
sanitize (~> 2.0)
sass-rails (~> 5.0.0)
......
......@@ -7,6 +7,7 @@
#= require jquery
#= require jquery-ui/autocomplete
#= require jquery-ui/datepicker
#= require jquery-ui/draggable
#= require jquery-ui/effect-highlight
#= require jquery-ui/sortable
#= require jquery_ujs
......@@ -138,7 +139,7 @@ $ ->
# Initialize tooltips
$('body').tooltip(
selector: '.has_tooltip, [data-toggle="tooltip"]'
selector: '.has-tooltip, [data-toggle="tooltip"]'
placement: (_, el) ->
$el = $(el)
$el.data('placement') || 'bottom'
......
......@@ -5,7 +5,6 @@ class @Aside
e.preventDefault()
btn = $(e.currentTarget)
icon = btn.find('i')
console.log('1')
if icon.hasClass('fa-angle-left')
btn.parent().find('section').hide()
......
......@@ -122,7 +122,7 @@ class @AwardsHandler
nodes = []
nodes.push(
"<button class='btn award-control js-emoji-btn has_tooltip active' title='me'>",
"<button class='btn award-control js-emoji-btn has-tooltip active' title='me'>",
"<div class='icon emoji-icon #{emojiCssClass}' data-emoji='#{emoji}'></div>",
"<span class='award-control-text js-counter'>1</span>",
"</button>"
......
......@@ -167,7 +167,11 @@ class GitLabDropdown
hidden: =>
if @options.filterable
@dropdown.find(".dropdown-input-field").blur().val("")
@dropdown
.find(".dropdown-input-field")
.blur()
.val("")
.trigger("keyup")
if @dropdown.find(".dropdown-toggle-page").length
$('.dropdown-menu', @dropdown).removeClass PAGE_TWO_CLASS
......
class @IssuableForm
issueMoveConfirmMsg: 'Are you sure you want to move this issue to another project?'
wipRegex: /^\s*(\[WIP\]\s*|WIP:\s*|WIP\s+)+\s*/i
constructor: (@form) ->
GitLab.GfmAutoComplete.setup()
new UsersSelect()
......@@ -7,12 +9,13 @@ class @IssuableForm
@titleField = @form.find("input[name*='[title]']")
@descriptionField = @form.find("textarea[name*='[description]']")
@issueMoveField = @form.find("#move_to_project_id")
return unless @titleField.length && @descriptionField.length
@initAutosave()
@form.on "submit", @resetAutosave
@form.on "submit", @handleSubmit
@form.on "click", ".btn-cancel", @resetAutosave
@initWip()
......@@ -30,6 +33,12 @@ class @IssuableForm
"description"
]
handleSubmit: =>
if (parseInt(@issueMoveField?.val()) ? 0) > 0
return false unless confirm(@issueMoveConfirmMsg)
@resetAutosave()
resetAutosave: =>
@titleField.data("autosave").reset()
@descriptionField.data("autosave").reset()
......
......@@ -3,6 +3,8 @@
# Handles persisting and restoring the current tab selection and lazily-loading
# content on the MergeRequests#show page.
#
#= require jquery.cookie
#
# ### Example Markup
#
# <ul class="nav-links merge-request-tabs">
......@@ -68,11 +70,15 @@ class @MergeRequestTabs
if action == 'commits'
@loadCommits($target.attr('href'))
@expandView()
else if action == 'diffs'
@loadDiff($target.attr('href'))
@shrinkView()
else if action == 'builds'
@loadBuilds($target.attr('href'))
@expandView()
else
@expandView()
@setCurrentAction(action)
......@@ -189,11 +195,24 @@ class @MergeRequestTabs
$('.container-fluid').removeClass('container-limited')
shrinkView: ->
$gutterIcon = $('.js-sidebar-toggle i')
$gutterIcon = $('.js-sidebar-toggle i:visible')
# Wait until listeners are set
setTimeout( ->
# Only when sidebar is collapsed
# Only when sidebar is expanded
if $gutterIcon.is('.fa-angle-double-right')
$gutterIcon.closest('a').trigger('click',[true])
$gutterIcon.closest('a').trigger('click', [true])
, 0)
# Expand the issuable sidebar unless the user explicitly collapsed it
expandView: ->
return if $.cookie('collapsed_gutter') == 'true'
$gutterIcon = $('.js-sidebar-toggle i:visible')
# Wait until listeners are set
setTimeout( ->
# Only when sidebar is collapsed
if $gutterIcon.is('.fa-angle-double-left')
$gutterIcon.closest('a').trigger('click', [true])
, 0)
......@@ -361,14 +361,12 @@ class @Notes
showEditForm: (e) ->
e.preventDefault()
note = $(this).closest(".note")
note.find(".note-body > .note-text").hide()
note.find(".note-header").hide()
note.addClass "is-editting"
form = note.find(".note-edit-form")
isNewForm = form.is(':not(.gfm-form)')
if isNewForm
form.addClass('gfm-form')
form.addClass('current-note-edit-form')
form.show()
# Show the attachment delete link
note.find(".js-note-attachment-delete").show()
......@@ -402,11 +400,9 @@ class @Notes
cancelEdit: (e) ->
e.preventDefault()
note = $(this).closest(".note")
note.find(".note-body > .note-text").show()
note.find(".note-header").show()
note.removeClass "is-editting"
note.find(".current-note-edit-form")
.removeClass("current-note-edit-form")
.hide()
###
Called in response to deleting a note of any kind.
......
......@@ -11,7 +11,6 @@ class @Project
$(@).toggleClass('active')
url = $("#project_clone").val()
console.log("url",url)
# Update the input field
$('#project_clone').val(url)
......
......@@ -95,4 +95,4 @@ window.ContributorsStatGraphUtil =
if date_range is null || date_range[0] <= new Date(date) <= date_range[1]
true
else
false
\ No newline at end of file
false
......@@ -16,7 +16,7 @@
}
&.group-avatar, &.project-avatar, &.avatar-tile {
@include border-radius(0px);
@include border-radius(0);
}
&.s16 { width: 16px; height: 16px; margin-right: 6px; }
......
......@@ -107,7 +107,7 @@
margin: 0;
font-size: 23px;
font-weight: normal;
margin: 16px 0 5px 0;
margin: 16px 0 5px;
color: #4c4e54;
font-size: 23px;
line-height: 1.1;
......
......@@ -75,7 +75,7 @@
width: 240px;
margin-top: 2px;
margin-bottom: 0;
padding: 10px 10px;
padding: 10px;
font-size: 14px;
font-weight: normal;
background-color: $dropdown-bg;
......
......@@ -117,4 +117,4 @@ body {
&.ui_violet {
@include gitlab-theme(#98c, $theme-violet, #436, #325);
}
}
\ No newline at end of file
}
......@@ -16,7 +16,7 @@ body {
}
.container .content {
margin: 0 0;
margin: 0;
}
.navless-container {
......
/**
* Generic mixins
*/
@mixin box-shadow($shadow) {
@mixin box-shadow($shadow) {
-webkit-box-shadow: $shadow;
-moz-box-shadow: $shadow;
-ms-box-shadow: $shadow;
......
......@@ -41,7 +41,7 @@
}
.select2-drop {
@include box-shadow(rgba(76, 86, 103, 0.247059) 0px 0px 1px 0px, rgba(31, 37, 50, 0.317647) 0px 2px 18px 0px);
@include box-shadow(rgba(76, 86, 103, 0.247059) 0 0 1px 0, rgba(31, 37, 50, 0.317647) 0 2px 18px 0);
@include border-radius ($border-radius-default);
border: none;
}
......
......@@ -39,8 +39,8 @@
h1 {
font-size: 1.3em;
font-weight: 600;
margin: 24px 0 12px 0;
padding: 0 0 10px 0;
margin: 24px 0 12px;
padding: 0 0 10px;
border-bottom: 1px solid #e7e9ed;
color: #313236;
}
......@@ -48,27 +48,27 @@
h2 {
font-size: 1.2em;
font-weight: 600;
margin: 24px 0 12px 0;
margin: 24px 0 12px;
color: #313236;
}
h3 {
margin: 24px 0 12px 0;
margin: 24px 0 12px;
font-size: 1.1em;
}
h4 {
margin: 24px 0 12px 0;
margin: 24px 0 12px;
font-size: 0.98em;
}
h5 {
margin: 24px 0 12px 0;
margin: 24px 0 12px;
font-size: 0.95em;
}
h6 {
margin: 24px 0 12px 0;
margin: 24px 0 12px;
font-size: 0.90em;
}
......@@ -76,7 +76,7 @@
color: #7f8fa4;
font-size: inherit;
padding: 8px 21px;
margin: 12px 0 12px;
margin: 12px 0;
border-left: 3px solid #e7e9ed;
}
......@@ -88,13 +88,13 @@
p {
color: #5c5d5e;
margin: 6px 0 0 0;
margin: 6px 0 0;
}
table {
@extend .table;
@extend .table-bordered;
margin: 12px 0 12px 0;
margin: 12px 0;
color: #5c5d5e;
th {
background: #f8fafc;
......@@ -102,7 +102,7 @@
}
pre {
margin: 12px 0 12px 0;
margin: 12px 0;
font-size: 13px;
line-height: 1.6em;
overflow-x: auto;
......@@ -191,7 +191,7 @@ body {
line-height: 1.3;
font-size: 1.25em;
font-weight: 600;
margin: 12px 7px 12px 7px;
margin: 12px 7px;
}
h1, h2, h3, h4, h5, h6 {
......
img {
max-width: 100%;
height: auto;
}
p.details {
font-style:italic;
color:#777
}
.footer p {
font-size:small;
color:#777
}
pre.commit-message {
white-space: pre-wrap;
}
.file-stats a {
text-decoration: none;
}
.file-stats .new-file {
color: #090;
}
.file-stats .deleted-file {
color: #B00;
}
......@@ -34,9 +34,9 @@
background: #fff
}
.visibility-levels {
.controls {
margin-bottom: 9px;
.visibility-levels {
.controls {
margin-bottom: 9px;
}
i {
......
......@@ -132,7 +132,7 @@
}
.image-info {
font-size: 12px;
margin: 5px 0 0 0;
margin: 5px 0 0;
color: grey;
}
......
......@@ -183,7 +183,7 @@
.block {
width: $sidebar_collapsed_width - 1px;
margin-left: -19px;
padding: 15px 0 0 0;
padding: 15px 0 0;
border-bottom: none;
overflow: hidden;
}
......@@ -273,12 +273,12 @@
}
.participants-list {
margin: -5px -5px;
margin: -5px;
}
.participants-author {
display: inline-block;
padding: 5px 5px;
padding: 5px;
.author_link {
display: block;
......
......@@ -45,7 +45,7 @@
.login-heading h3 {
font-weight: 300;
line-height: 1.5;
margin: 0 0 10px 0;
margin: 0 0 10px;
}
.login-footer {
......
......@@ -26,7 +26,7 @@
display: none;
}
.new_note, .edit_note {
.new_note, .note-edit-form {
.note-form-actions {
margin-top: $gl-padding;
}
......
......@@ -100,6 +100,18 @@ ul.notes {
display: block;
position: relative;
&.is-editting {
.note-header,
.note-text,
.edited-text {
display: none;
}
.note-edit-form {
display: block;
}
}
.note-body {
overflow: auto;
......
......@@ -54,7 +54,7 @@
}
.account-well {
padding: 10px 10px;
padding: 10px;
background-color: $help-well-bg;
border: 1px solid $help-well-border;
border-radius: $border-radius-base;
......
......@@ -37,7 +37,7 @@
.dropdown-menu {
left: auto;
width: auto;
right: 0px;
right: 0;
max-width: 240px;
}
}
......@@ -311,7 +311,7 @@ pre.light-well {
}
.git-empty {
margin: 0 7px 0 7px;
margin: 0 7px;
h5 {
color: #5c5d5e;
......
......@@ -16,7 +16,7 @@
#contributors {
.contributors-list {
margin: 0 0 10px 0;
margin: 0 0 10px;
list-style: none;
padding: 0;
}
......
......@@ -21,19 +21,19 @@
$l-white: #fff;
.term-bold {
font-weight: bold;
font-weight: bold;
}
.term-italic {
font-style: italic;
font-style: italic;
}
.term-conceal {
visibility: hidden;
visibility: hidden;
}
.term-underline {
text-decoration: underline;
text-decoration: underline;
}
.term-cross {
text-decoration: line-through;
text-decoration: line-through;
}
.term-fg-black {
......
......@@ -5,12 +5,12 @@ class Admin::GroupsController < Admin::ApplicationController
@groups = Group.all
@groups = @groups.sort(@sort = params[:sort])
@groups = @groups.search(params[:name]) if params[:name].present?
@groups = @groups.page(params[:page]).per(PER_PAGE)
@groups = @groups.page(params[:page])
end
def show
@members = @group.members.order("access_level DESC").page(params[:members_page]).per(PER_PAGE)
@projects = @group.projects.page(params[:projects_page]).per(PER_PAGE)
@members = @group.members.order("access_level DESC").page(params[:members_page])
@projects = @group.projects.page(params[:projects_page])
end
def new
......
......@@ -2,7 +2,7 @@ class Admin::LabelsController < Admin::ApplicationController
before_action :set_label, only: [:show, :edit, :update, :destroy]
def index
@labels = Label.templates.page(params[:page]).per(PER_PAGE)
@labels = Label.templates.page(params[:page])
end
def show
......
......@@ -11,15 +11,15 @@ class Admin::ProjectsController < Admin::ApplicationController
@projects = @projects.non_archived unless params[:with_archived].present?
@projects = @projects.search(params[:name]) if params[:name].present?
@projects = @projects.sort(@sort = params[:sort])
@projects = @projects.includes(:namespace).order("namespaces.path, projects.name ASC").page(params[:page]).per(PER_PAGE)
@projects = @projects.includes(:namespace).order("namespaces.path, projects.name ASC").page(params[:page])
end
def show
if @group
@group_members = @group.members.order("access_level DESC").page(params[:group_members_page]).per(PER_PAGE)
@group_members = @group.members.order("access_level DESC").page(params[:group_members_page])
end
@project_members = @project.project_members.page(params[:project_members_page]).per(PER_PAGE)
@project_members = @project.project_members.page(params[:project_members_page])
end
def transfer
......
......@@ -6,8 +6,6 @@ class ApplicationController < ActionController::Base
include GitlabRoutingHelper
include PageLayoutHelper
PER_PAGE = 20
before_action :authenticate_user_from_token!
before_action :authenticate_user!
before_action :validate_user_service_ticket!
......
......@@ -7,7 +7,7 @@ class AutocompleteController < ApplicationController
@users = @users.search(params[:search]) if params[:search].present?
@users = @users.active
@users = @users.reorder(:name)
@users = @users.page(params[:page]).per(PER_PAGE)
@users = @users.page(params[:page])
if params[:search].blank?
# Include current user if available to filter by "Me"
......
......@@ -6,7 +6,7 @@ module GlobalMilestones
@milestones = MilestonesFinder.new.execute(@projects, params)
@milestones = GlobalMilestone.build_collection(@milestones)
@milestones = @milestones.sort_by { |x| x.due_date.nil? ? epoch : x.due_date }
@milestones = Kaminari.paginate_array(@milestones).page(params[:page]).per(ApplicationController::PER_PAGE)
@milestones = Kaminari.paginate_array(@milestones).page(params[:page])
end
def milestone
......
module IssuableActions
extend ActiveSupport::Concern
included do
before_action :authorize_destroy_issuable!, only: :destroy
end
def destroy
issuable.destroy
name = issuable.class.name.titleize.downcase
flash[:notice] = "The #{name} was successfully deleted."
redirect_to polymorphic_path([@project.namespace.becomes(Namespace), @project, issuable.class])
end
private
def authorize_destroy_issuable!
unless current_user.can?(:"destroy_#{issuable.to_ability_name}", issuable)
return access_denied!
end
end
end
......@@ -3,7 +3,7 @@ module IssuesAction
def issues
@issues = get_issues_collection.non_archived
@issues = @issues.page(params[:page]).per(ApplicationController::PER_PAGE)
@issues = @issues.page(params[:page])
@issues = @issues.preload(:author, :project)
@label = @issuable_finder.labels.first
......
......@@ -3,7 +3,7 @@ module MergeRequestsAction
def merge_requests
@merge_requests = get_merge_requests_collection.non_archived
@merge_requests = @merge_requests.page(params[:page]).per(ApplicationController::PER_PAGE)
@merge_requests = @merge_requests.page(params[:page])
@merge_requests = @merge_requests.preload(:author, :target_project)
@label = @issuable_finder.labels.first
......
class Dashboard::GroupsController < Dashboard::ApplicationController
def index
@group_members = current_user.group_members.page(params[:page]).per(PER_PAGE)
@group_members = current_user.group_members.page(params[:page])
end
end
......@@ -8,7 +8,7 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController
@projects = filter_projects(@projects)
@projects = @projects.includes(:namespace)
@projects = @projects.sort(@sort = params[:sort])
@projects = @projects.page(params[:page]).per(PER_PAGE)
@projects = @projects.page(params[:page])
@last_push = current_user.recent_push
......@@ -32,7 +32,7 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController
@projects = filter_projects(@projects)
@projects = @projects.includes(:namespace, :forked_from_project, :tags)
@projects = @projects.sort(@sort = params[:sort])
@projects = @projects.page(params[:page]).per(PER_PAGE)
@projects = @projects.page(params[:page])
@last_push = current_user.recent_push
@groups = []
......
......@@ -6,6 +6,6 @@ class Dashboard::SnippetsController < Dashboard::ApplicationController
user: current_user,
scope: params[:scope]
)
@snippets = @snippets.page(params[:page]).per(PER_PAGE)
@snippets = @snippets.page(params[:page])
end
end
......@@ -2,7 +2,7 @@ class Dashboard::TodosController < Dashboard::ApplicationController
before_action :find_todos, only: [:index, :destroy, :destroy_all]
def index
@todos = @todos.page(params[:page]).per(PER_PAGE)
@todos = @todos.page(params[:page])
end
def destroy
......
......@@ -3,6 +3,6 @@ class Explore::GroupsController < Explore::ApplicationController
@groups = GroupsFinder.new.execute(current_user)
@groups = @groups.search(params[:search]) if params[:search].present?
@groups = @groups.sort(@sort = params[:sort])
@groups = @groups.page(params[:page]).per(PER_PAGE)
@groups = @groups.page(params[:page])
end
end
......@@ -8,7 +8,7 @@ class Explore::ProjectsController < Explore::ApplicationController
@projects = @projects.where(visibility_level: params[:visibility_level]) if params[:visibility_level].present?
@projects = filter_projects(@projects)
@projects = @projects.sort(@sort = params[:sort])
@projects = @projects.includes(:namespace).page(params[:page]).per(PER_PAGE)
@projects = @projects.includes(:namespace).page(params[:page])
respond_to do |format|
format.html
......@@ -23,7 +23,7 @@ class Explore::ProjectsController < Explore::ApplicationController
def trending
@projects = TrendingProjectsFinder.new.execute(current_user)
@projects = filter_projects(@projects)
@projects = @projects.page(params[:page]).per(PER_PAGE)
@projects = @projects.page(params[:page])
respond_to do |format|
format.html
......@@ -39,7 +39,7 @@ class Explore::ProjectsController < Explore::ApplicationController
@projects = ProjectsFinder.new.execute(current_user)
@projects = filter_projects(@projects)
@projects = @projects.reorder('star_count DESC')
@projects = @projects.page(params[:page]).per(PER_PAGE)
@projects = @projects.page(params[:page])
respond_to do |format|
format.html
......
class Explore::SnippetsController < Explore::ApplicationController
def index
@snippets = SnippetsFinder.new.execute(current_user, filter: :all)
@snippets = @snippets.page(params[:page]).per(PER_PAGE)
@snippets = @snippets.page(params[:page])
end
end
......@@ -42,7 +42,7 @@ class GroupsController < Groups::ApplicationController
@projects = @projects.includes(:namespace)
@projects = filter_projects(@projects)
@projects = @projects.sort(@sort = params[:sort])
@projects = @projects.page(params[:page]).per(PER_PAGE) if params[:filter_projects].blank?
@projects = @projects.page(params[:page]) if params[:filter_projects].blank?
@shared_projects = GroupProjectsFinder.new(group, only_shared: true).execute(current_user)
......
......@@ -34,8 +34,7 @@ class ProfilesController < Profiles::ApplicationController
def audit_log
@events = AuditEvent.where(entity_type: "User", entity_id: current_user.id).
order("created_at DESC").
page(params[:page]).
per(PER_PAGE)
page(params[:page])
end
def update_username
......
......@@ -8,7 +8,7 @@ class Projects::BranchesController < Projects::ApplicationController
def index
@sort = params[:sort] || 'name'
@branches = @repository.branches_sorted_by(@sort)
@branches = Kaminari.paginate_array(@branches).page(params[:page]).per(PER_PAGE)
@branches = Kaminari.paginate_array(@branches).page(params[:page])
@max_commits = @branches.reduce(0) do |memo, branch|
diverging_commit_counts = repository.diverging_commit_counts(branch)
......
......@@ -15,7 +15,7 @@ class Projects::ForksController < Projects::ApplicationController
@sort = params[:sort] || 'id_desc'
@forks = @forks.search(params[:filter_projects]) if params[:filter_projects].present?
@forks = @forks.order_by(@sort).page(params[:page]).per(PER_PAGE)
@forks = @forks.order_by(@sort).page(params[:page])
respond_to do |format|
format.html
......
class Projects::IssuesController < Projects::ApplicationController
include ToggleSubscriptionAction
include IssuableActions
before_action :module_enabled
before_action :issue, only: [:edit, :update, :show]
......@@ -33,7 +34,7 @@ class Projects::IssuesController < Projects::ApplicationController
end
end
@issues = @issues.page(params[:page]).per(PER_PAGE)
@issues = @issues.page(params[:page])
@label = @project.labels.find_by(title: params[:label_name])
respond_to do |format|
......@@ -90,6 +91,12 @@ class Projects::IssuesController < Projects::ApplicationController
def update
@issue = Issues::UpdateService.new(project, current_user, issue_params).execute(issue)
if params[:move_to_project_id].to_i > 0
new_project = Project.find(params[:move_to_project_id])
move_service = Issues::MoveService.new(project, current_user)
@issue = move_service.execute(@issue, new_project)
end
respond_to do |format|
format.js
format.html do
......@@ -127,6 +134,7 @@ class Projects::IssuesController < Projects::ApplicationController
end
end
alias_method :subscribable_resource, :issue
alias_method :issuable, :issue
def authorize_read_issue!
return render_404 unless can?(current_user, :read_issue, @issue)
......
......@@ -11,7 +11,7 @@ class Projects::LabelsController < Projects::ApplicationController
respond_to :js, :html
def index
@labels = @project.labels.page(params[:page]).per(PER_PAGE)
@labels = @project.labels.page(params[:page])
respond_to do |format|
format.html
......
class Projects::MergeRequestsController < Projects::ApplicationController
include ToggleSubscriptionAction
include DiffHelper
include IssuableActions
before_action :module_enabled
before_action :merge_request, only: [
......@@ -34,7 +35,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end
end
@merge_requests = @merge_requests.page(params[:page]).per(PER_PAGE)
@merge_requests = @merge_requests.page(params[:page])
@merge_requests = @merge_requests.preload(:target_project)
@label = @project.labels.find_by(title: params[:label_name])
......@@ -255,6 +256,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@merge_request ||= @project.merge_requests.find_by!(iid: params[:id])
end
alias_method :subscribable_resource, :merge_request
alias_method :issuable, :merge_request
def closes_issues
@closes_issues ||= @merge_request.closes_issues
......
......@@ -22,7 +22,7 @@ class Projects::MilestonesController < Projects::ApplicationController
respond_to do |format|
format.html do
@milestones = @milestones.page(params[:page]).per(PER_PAGE)
@milestones = @milestones.page(params[:page])
end
format.json do
render json: @milestones
......
......@@ -21,7 +21,7 @@ class Projects::SnippetsController < Projects::ApplicationController
filter: :by_project,
project: @project
})
@snippets = @snippets.page(params[:page]).per(PER_PAGE)
@snippets = @snippets.page(params[:page])
end
def new
......
......@@ -7,7 +7,7 @@ class Projects::TagsController < Projects::ApplicationController
def index
sorted = VersionSorter.rsort(@repository.tag_names)
@tags = Kaminari.paginate_array(sorted).page(params[:page]).per(PER_PAGE)
@tags = Kaminari.paginate_array(sorted).page(params[:page])
@releases = project.releases.where(tag: @tags)
end
......
......@@ -7,7 +7,7 @@ class Projects::WikisController < Projects::ApplicationController
before_action :load_project_wiki
def pages
@wiki_pages = Kaminari.paginate_array(@project_wiki.pages).page(params[:page]).per(PER_PAGE)
@wiki_pages = Kaminari.paginate_array(@project_wiki.pages).page(params[:page])
end
def show
......
......@@ -25,7 +25,7 @@ class SnippetsController < ApplicationController
filter: :by_user,
user: @user,
scope: params[:scope] }).
page(params[:page]).per(PER_PAGE)
page(params[:page])
render 'index'
else
......
......@@ -100,7 +100,7 @@ class UsersController < ApplicationController
def load_projects
@projects =
PersonalProjectsFinder.new(@user).execute(current_user)
.page(params[:page]).per(PER_PAGE)
.page(params[:page])
end
def load_contributed_projects
......
......@@ -27,7 +27,7 @@ module BlobHelper
link_opts)
if !on_top_of_branch?(project, ref)
button_tag "Edit", class: "btn btn-default disabled has_tooltip", title: "You can only edit files when you are on a branch", data: { container: 'body' }
button_tag "Edit", class: "btn btn-default disabled has-tooltip", title: "You can only edit files when you are on a branch", data: { container: 'body' }
elsif can_edit_blob?(blob, project, ref)
link_to "Edit", edit_path, class: 'btn'
elsif can?(current_user, :fork_project, project)
......@@ -50,9 +50,9 @@ module BlobHelper
return unless blob
if !on_top_of_branch?(project, ref)
button_tag label, class: "btn btn-#{btn_class} disabled has_tooltip", title: "You can only #{action} files when you are on a branch", data: { container: 'body' }
button_tag label, class: "btn btn-#{btn_class} disabled has-tooltip", title: "You can only #{action} files when you are on a branch", data: { container: 'body' }
elsif blob.lfs_pointer?
button_tag label, class: "btn btn-#{btn_class} disabled has_tooltip", title: "It is not possible to #{action} files that are stored in LFS using the web interface", data: { container: 'body' }
button_tag label, class: "btn btn-#{btn_class} disabled has-tooltip", title: "It is not possible to #{action} files that are stored in LFS using the web interface", data: { container: 'body' }
elsif can_edit_blob?(blob, project, ref)
button_tag label, class: "btn btn-#{btn_class}", 'data-target' => "#modal-#{modal_type}-blob", 'data-toggle' => 'modal'
elsif can?(current_user, :fork_project, project)
......
......@@ -23,36 +23,34 @@ module ButtonHelper
end
def http_clone_button(project)
klass = 'btn js-protocol-switch'
klass << ' active' if default_clone_protocol == 'http'
klass << ' has_tooltip' if current_user.try(:require_password?)
klass = 'http-selector'
klass << ' has-tooltip' if current_user.try(:require_password?)
protocol = gitlab_config.protocol.upcase
content_tag :button, protocol,
content_tag :a, protocol,
class: klass,
href: @project.http_url_to_repo,
data: {
clone: project.http_url_to_repo,
html: true,
placement: 'right',
container: 'body',
html: 'true',
title: "Set a password on your account<br>to pull or push via #{protocol}"
},
type: :button
}
end
def ssh_clone_button(project)
klass = 'btn js-protocol-switch'
klass << ' active' if default_clone_protocol == 'ssh'
klass << ' has_tooltip' if current_user.try(:require_ssh_key?)
klass = 'ssh-selector'
klass << ' has-tooltip' if current_user.try(:require_ssh_key?)
content_tag :button, 'SSH',
content_tag :a, 'SSH',
class: klass,
href: project.ssh_url_to_repo,
data: {
clone: project.ssh_url_to_repo,
html: true,
placement: 'right',
container: 'body',
html: 'true',
title: 'Add an SSH key to your profile<br>to pull or push via SSH.'
},
type: :button
}
end
end
......@@ -182,7 +182,7 @@ module CommitsHelper
end
options = {
class: "commit-#{options[:source]}-link has_tooltip",
class: "commit-#{options[:source]}-link has-tooltip",
data: { 'original-title'.to_sym => sanitize(source_email) }
}
......
......@@ -57,6 +57,19 @@ module IssuesHelper
options_from_collection_for_select(milestones, 'id', 'title', object.milestone_id)
end
def project_options(issuable, current_user, ability: :read_project)
projects = current_user.authorized_projects
projects = projects.select do |project|
current_user.can?(ability, project)
end
no_project = OpenStruct.new(id: 0, name_with_namespace: 'No project')
projects.unshift(no_project)
projects.delete(issuable.project)
options_from_collection_for_select(projects, :id, :name_with_namespace)
end
def status_box_class(item)
if item.respond_to?(:expired?) && item.expired?
'status-box-expired'
......
......@@ -56,7 +56,7 @@ module LabelsHelper
# Intentionally not using content_tag here so that this method can be called
# by LabelReferenceFilter
span = %(<span class="label color-label #{"has_tooltip" if tooltip}" ) +
span = %(<span class="label color-label #{"has-tooltip" if tooltip}" ) +
%(style="background-color: #{label_color}; color: #{text_color}" ) +
%(title="#{escape_once(label.description)}" data-container="body">) +
%(#{escape_once(label.name)}#{label_suffix}</span>)
......
......@@ -52,7 +52,7 @@ module ProjectsHelper
link_to(author_html, user_path(author), class: "author_link #{"#{opts[:mobile_classes]}" if opts[:mobile_classes]}").html_safe
else
title = opts[:title].sub(":name", sanitize(author.name))
link_to(author_html, user_path(author), class: "author_link has_tooltip", data: { 'original-title'.to_sym => title, container: 'body' } ).html_safe
link_to(author_html, user_path(author), class: "author_link has-tooltip", data: { 'original-title'.to_sym => title, container: 'body' } ).html_safe
end
end
......@@ -209,7 +209,7 @@ module ProjectsHelper
def default_clone_protocol
if !current_user || current_user.require_ssh_key?
"http"
gitlab_config.protocol
else
"ssh"
end
......
......@@ -36,6 +36,14 @@ module Emails
mail_answer_thread(@issue, issue_thread_options(updated_by_user_id, recipient_id))
end
def issue_moved_email(recipient, issue, new_issue, updated_by_user)
setup_issue_mail(issue.id, recipient.id)
@new_issue = new_issue
@new_project = new_issue.project
mail_answer_thread(issue, issue_thread_options(updated_by_user.id, recipient.id))
end
private
def setup_issue_mail(issue_id, recipient_id)
......
......@@ -234,7 +234,9 @@ class Ability
:rename_project,
:remove_project,
:archive_project,
:remove_fork_project
:remove_fork_project,
:destroy_merge_request,
:destroy_issue
]
end
......
......@@ -7,7 +7,10 @@ module InternalId
end
def set_iid
max_iid = project.send(self.class.name.tableize).maximum(:iid)
records = project.send(self.class.name.tableize)
records = records.with_deleted if self.paranoid?
max_iid = records.maximum(:iid)
self.iid = max_iid.to_i + 1
end
......
......@@ -58,6 +58,8 @@ module Issuable
attr_mentionable :description, cache: true
participant :author, :assignee, :notes_with_associations
strip_attributes :title
acts_as_paranoid
end
module ClassMethods
......@@ -209,4 +211,13 @@ module Issuable
Taskable.get_updated_tasks(old_content: previous_changes['description'].first,
new_content: description)
end
##
# Method that checks if issuable can be moved to another project.
#
# Should be overridden if issuable can be moved.
#
def can_move?(*)
false
end
end
......@@ -16,6 +16,7 @@
# state :string(255)
# iid :integer
# updated_by_id :integer
# moved_to_id :integer
#
require 'carrierwave/orm/activerecord'
......@@ -31,6 +32,8 @@ class Issue < ActiveRecord::Base
ActsAsTaggableOn.strict_case_match = true
belongs_to :project
belongs_to :moved_to, class_name: 'Issue'
validates :project, presence: true
scope :cared, ->(user) { where(assignee_id: user) }
......@@ -102,9 +105,9 @@ class Issue < ActiveRecord::Base
end
def related_branches
return [] if self.project.empty_repo?
self.project.repository.branch_names.select { |branch| branch.end_with?("-#{iid}") }
project.repository.branch_names.select do |branch|
branch.end_with?("-#{iid}")
end
end
# Reset issue events cache
......@@ -134,6 +137,18 @@ class Issue < ActiveRecord::Base
end.uniq.select { |mr| mr.open? && mr.closes_issue?(self) }
end
def moved?
!moved_to.nil?
end
def can_move?(user, to_project = nil)
if to_project
return false unless user.can?(:admin_issue, to_project)
end
!moved? && user.can?(:admin_issue, self.project)
end
def to_branch_name
"#{title.parameterize}-#{iid}"
end
......
......@@ -431,6 +431,7 @@ class Project < ActiveRecord::Base
def safe_import_url
result = URI.parse(self.import_url)
result.password = '*****' unless result.password.nil?
result.user = '*****' unless result.user.nil? || result.user == "git" #tokens or other data may be saved as user
result.to_s
rescue
self.import_url
......@@ -887,6 +888,7 @@ class Project < ActiveRecord::Base
# Forked import is handled asynchronously
unless forked?
if gitlab_shell.add_repository(path_with_namespace)
repository.after_create
true
else
errors.add(:base, 'Failed to create repository via gitlab-shell')
......
......@@ -123,23 +123,27 @@ class ProjectWiki
end
def repository
Repository.new(path_with_namespace, @project)
@repository ||= Repository.new(path_with_namespace, @project)
end
def default_branch
wiki.class.default_ref
end
private
def create_repo!
if init_repo(path_with_namespace)
Gollum::Wiki.new(path_to_repo)
wiki = Gollum::Wiki.new(path_to_repo)
else
raise CouldNotCreateWikiError
end
repository.after_create
wiki
end
private
def init_repo(path_with_namespace)
gitlab_shell.add_repository(path_with_namespace)
end
......
......@@ -42,12 +42,15 @@ class Repository
end
def exists?
return false unless raw_repository
return @exists unless @exists.nil?
raw_repository.rugged
true
rescue Gitlab::Git::Repository::NoRepository
false
@exists = cache.fetch(:exists?) do
begin
raw_repository && raw_repository.rugged ? true : false
rescue Gitlab::Git::Repository::NoRepository
false
end
end
end
def empty?
......@@ -320,12 +323,23 @@ class Repository
@avatar = nil
end
def expire_exists_cache
cache.expire(:exists?)
@exists = nil
end
# Runs code after a repository has been created.
def after_create
expire_exists_cache
end
# Runs code just before a repository is deleted.
def before_delete
expire_cache if exists?
expire_root_ref_cache
expire_emptiness_caches
expire_exists_cache
end
# Runs code just before the HEAD of a repository is changed.
......@@ -351,6 +365,7 @@ class Repository
# Runs code after a repository has been forked/imported.
def after_import
expire_emptiness_caches
expire_exists_cache
end
# Runs code after a new commit has been pushed.
......
......@@ -435,7 +435,7 @@ class User < ActiveRecord::Base
Group.where("namespaces.id IN (#{union.to_sql})")
end
# Returns the groups a user is authorized to access.
# Returns projects user is authorized to access.
def authorized_projects
Project.where("projects.id IN (#{projects_union.to_sql})")
end
......
......@@ -120,7 +120,7 @@ class GitPushService < BaseService
closed_issues = commit.closes_issues(current_user)
closed_issues.each do |issue|
if can?(current_user, :update_issue, issue)
Issues::CloseService.new(project, authors[commit], {}).execute(issue, commit)
Issues::CloseService.new(project, authors[commit], {}).execute(issue, commit: commit)
end
end
end
......
module Issues
class CloseService < Issues::BaseService
def execute(issue, commit = nil)
def execute(issue, commit: nil, notifications: true, system_note: true)
if project.jira_tracker? && project.jira_service.active
project.jira_service.execute(commit, issue)
todo_service.close_issue(issue, current_user)
......@@ -9,8 +9,8 @@ module Issues
if project.default_issues_tracker? && issue.close
event_service.close_issue(issue, current_user)
create_note(issue, commit)
notification_service.close_issue(issue, current_user)
create_note(issue, commit) if system_note
notification_service.close_issue(issue, current_user) if notifications
todo_service.close_issue(issue, current_user)
execute_hooks(issue, 'close')
end
......
......@@ -4,7 +4,7 @@ module Issues
filter_params
label_params = params[:label_ids]
issue = project.issues.new(params.except(:label_ids))
issue.author = current_user
issue.author = params[:author] || current_user
if issue.save
issue.update_attributes(label_ids: label_params)
......
module Issues
class MoveService < Issues::BaseService
class MoveError < StandardError; end
def execute(issue, new_project)
@old_issue = issue
@old_project = @project
@new_project = new_project
unless issue.can_move?(current_user, new_project)
raise MoveError, 'Cannot move issue due to insufficient permissions!'
end
if @project == new_project
raise MoveError, 'Cannot move issue to project it originates from!'
end
# Using transaction because of a high resources footprint
# on rewriting notes (unfolding references)
#
ActiveRecord::Base.transaction do
# New issue tasks
#
@new_issue = create_new_issue
rewrite_notes
add_note_moved_from
# Old issue tasks
#
add_note_moved_to
close_issue
mark_as_moved
end
notify_participants
@new_issue
end
private
def create_new_issue
new_params = { id: nil, iid: nil, label_ids: [], milestone: nil,
project: @new_project, author: @old_issue.author,
description: unfold_references(@old_issue.description) }
new_params = @old_issue.serializable_hash.merge(new_params)
CreateService.new(@new_project, @current_user, new_params).execute
end
def rewrite_notes
@old_issue.notes.find_each do |note|
new_note = note.dup
new_params = { project: @new_project, noteable: @new_issue,
note: unfold_references(new_note.note),
created_at: note.created_at }
new_note.update(new_params)
end
end
def close_issue
close_service = CloseService.new(@old_project, @current_user)
close_service.execute(@old_issue, notifications: false, system_note: false)
end
def add_note_moved_from
SystemNoteService.noteable_moved(@new_issue, @new_project,
@old_issue, @current_user,
direction: :from)
end
def add_note_moved_to
SystemNoteService.noteable_moved(@old_issue, @old_project,
@new_issue, @current_user,
direction: :to)
end
def unfold_references(content)
rewriter = Gitlab::Gfm::ReferenceRewriter.new(content, @old_project,
@current_user)
rewriter.rewrite(@new_project)
end
def notify_participants
notification_service.issue_moved(@old_issue, @new_issue, @current_user)
end
def mark_as_moved
@old_issue.update(moved_to: @new_issue)
end
end
end
......@@ -22,7 +22,7 @@ module MergeRequests
closed_issues = merge_request.closes_issues(current_user)
closed_issues.each do |issue|
if can?(current_user, :update_issue, issue)
Issues::CloseService.new(project, current_user, {}).execute(issue, merge_request)
Issues::CloseService.new(project, current_user, {}).execute(issue, commit: merge_request)
end
end
end
......
......@@ -236,6 +236,16 @@ class NotificationService
end
end
def issue_moved(issue, new_issue, current_user)
recipients = build_recipients(issue, issue.project, current_user)
recipients.map do |recipient|
email = mailer.issue_moved_email(recipient, issue, new_issue, current_user)
email.deliver_later
email
end
end
protected
# Get project users with WATCH notification level
......
......@@ -411,4 +411,26 @@ class SystemNoteService
body = "Marked the task **#{new_task.source}** as #{status_label}"
create_note(noteable: noteable, project: project, author: author, note: body)
end
# Called when noteable has been moved to another project
#
# direction - symbol, :to or :from
# noteable - Noteable object
# noteable_ref - Referenced noteable
# author - User performing the move
#
# Example Note text:
#
# "Moved to some_namespace/project_new#11"
#
# Returns the created Note object
def self.noteable_moved(noteable, project, noteable_ref, author, direction:)
unless [:to, :from].include?(direction)
raise ArgumentError, "Invalid direction `#{direction}`"
end
cross_reference = noteable_ref.to_reference(project)
body = "Moved #{direction} #{cross_reference}"
create_note(noteable: noteable, project: project, author: author, note: body)
end
end
- submit_btn_css ||= 'btn btn-link btn-remove btn-sm'
= form_tag admin_application_path(application) do
%input{:name => "_method", :type => "hidden", :value => "delete"}/
= submit_tag 'Destroy', onclick: "return confirm('Are you sure?')", class: submit_btn_css
\ No newline at end of file
= submit_tag 'Destroy', onclick: "return confirm('Are you sure?')", class: submit_btn_css
......@@ -6,4 +6,4 @@
%label{for: "remember_me"}
= check_box_tag :remember_me, '1', false, id: 'remember_me'
%span Remember me
= button_tag "Sign in", class: "btn-save btn"
\ No newline at end of file
= button_tag "Sign in", class: "btn-save btn"
......@@ -4,4 +4,4 @@
%hr
= render 'form', application: @application
\ No newline at end of file
= render 'form', application: @application
%h3.page-title An error has occurred
%main{:role => "main"}
%pre= @pre_auth.error_response.body[:error_description]
\ No newline at end of file
%pre= @pre_auth.error_response.body[:error_description]
%h3.page-title Authorization code:
%main{:role => "main"}
%code#authorization_code= params[:code]
\ No newline at end of file
%code#authorization_code= params[:code]
%ul.nav.nav-sidebar
= nav_link(path: ['root#index', 'projects#trending', 'projects#starred', 'dashboard/projects#index'], html_options: {class: 'home'}) do
= link_to dashboard_projects_path, title: 'Projects' do
= icon('home fw')
= icon('bookmark fw')
%span
Projects
= nav_link(controller: :todos) do
......
%ul.nav.nav-sidebar
= nav_link(path: ['dashboard#show', 'root#show', 'projects#trending', 'projects#starred', 'projects#index'], html_options: {class: 'home'}) do
= link_to explore_root_path, title: 'Projects' do
= icon('home fw')
= icon('bookmark fw')
%span
Projects
= nav_link(controller: :groups) do
......
%html{lang: "en"}
%head
%meta{content: "text/html; charset=utf-8", "http-equiv" => "Content-Type"}
%title
GitLab
:css
img {
max-width: 100%;
height: auto;
}
p.details {
font-style:italic;
color:#777
}
.footer p {
font-size:small;
color:#777
}
pre.commit-message {
white-space: pre-wrap;
}
.file-stats a {
text-decoration: none;
}
.file-stats .new-file {
color: #090;
}
.file-stats .deleted-file {
color: #B00;
}
%title
GitLab
= stylesheet_link_tag 'notify'
%body
%div.content
= yield
......
%p
Issue was moved to another project.
%p
New issue:
= link_to namespace_project_issue_url(@new_project.namespace, @new_project, @new_issue) do
= @new_issue.title
Issue was moved to another project.
New issue location:
<%= namespace_project_issue_url(@new_project.namespace, @new_project, @new_issue) %>
......@@ -8,7 +8,7 @@
= key.fingerprint
.pull-right
%span.key-created-at
created #{time_ago_with_tooltip(key.created_at)} ago
created #{time_ago_with_tooltip(key.created_at)}
= link_to path_to_key(key, is_admin), data: { confirm: 'Are you sure?'}, method: :delete, class: "btn btn-transparent prepend-left-10" do
%span.sr-only Remove
= icon('trash')
......@@ -11,7 +11,7 @@
- if branch.name == @repository.root_ref
%span.label.label-primary default
- elsif @repository.merged_to_root_ref? branch.name
%span.label.label-info.has_tooltip(title="Merged into #{@repository.root_ref}")
%span.label.label-info.has-tooltip(title="Merged into #{@repository.root_ref}")
merged
- if @project.protected_branch? branch.name
......@@ -30,7 +30,7 @@
Compare
- if can_remove_branch?(@project, branch.name)
= link_to namespace_project_branch_path(@project.namespace, @project, branch.name), class: 'btn btn-grouped btn-xs btn-remove remove-row has_tooltip', title: "Delete branch", method: :delete, data: { confirm: "Deleting the '#{branch.name}' branch cannot be undone. Are you sure?", container: 'body' }, remote: true do
= link_to namespace_project_branch_path(@project.namespace, @project, branch.name), class: 'btn btn-grouped btn-xs btn-remove remove-row has-tooltip', title: "Delete branch", method: :delete, data: { confirm: "Deleting the '#{branch.name}' branch cannot be undone. Are you sure?", container: 'body' }, remote: true do
= icon("trash-o")
- if branch.name != @repository.root_ref
......
- unless @project.empty_repo?
- if can? current_user, :download_code, @project
= link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: @ref, format: 'zip'), class: 'btn has_tooltip', data: {container: "body"}, rel: 'nofollow', title: "Download ZIP" do
= link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: @ref, format: 'zip'), class: 'btn has-tooltip', data: {container: "body"}, rel: 'nofollow', title: "Download ZIP" do
= icon('download')
- unless @project.empty_repo?
- if current_user && can?(current_user, :fork_project, @project)
- if current_user.already_forked?(@project) && current_user.manageable_namespaces.size < 2
= link_to namespace_project_path(current_user, current_user.fork_of(@project)), title: 'Go to your fork', class: 'btn has_tooltip' do
= link_to namespace_project_path(current_user, current_user.fork_of(@project)), title: 'Go to your fork', class: 'btn has-tooltip' do
= icon('code-fork fw')
Fork
%div.count-with-arrow
......@@ -9,7 +9,7 @@
%span.count
= @project.forks_count
- else
= link_to new_namespace_project_fork_path(@project.namespace, @project), title: "Fork project", class: 'btn has_tooltip' do
= link_to new_namespace_project_fork_path(@project.namespace, @project), title: "Fork project", class: 'btn has-tooltip' do
= icon('code-fork fw')
Fork
%div.count-with-arrow
......
......@@ -14,7 +14,7 @@
= notification_list_item(level, @membership)
- when GroupMember
.btn.disabled.notifications-btn.has_tooltip{title: "To change the notification level, you need to be a member of the project itself, not only its group."}
.btn.disabled.notifications-btn.has-tooltip{title: "To change the notification level, you need to be a member of the project itself, not only its group."}
= icon('bell')
= notification_label(@membership)
= icon('angle-down')
- if current_user
= link_to toggle_star_namespace_project_path(@project.namespace, @project), class: 'btn star-btn toggle-star has_tooltip', method: :post, remote: true, title: "Star project" do
= link_to toggle_star_namespace_project_path(@project.namespace, @project), class: 'btn star-btn toggle-star has-tooltip', method: :post, remote: true, title: "Star project" do
- if current_user.starred?(@project)
= icon('star fw')
%span.starred Unstar
......@@ -12,7 +12,7 @@
= @project.star_count
- else
= link_to new_user_session_path, class: 'btn has_tooltip star-btn', title: 'You must sign in to star a project' do
= link_to new_user_session_path, class: 'btn has-tooltip star-btn', title: 'You must sign in to star a project' do
= icon('star fw')
Star
%div.count-with-arrow
......
= form_tag namespace_project_compare_index_path(@project.namespace, @project), method: :post, class: 'form-inline js-requires-input' do
.clearfix
- if params[:to] && params[:from]
= link_to 'switch', {from: params[:to], to: params[:from]}, {class: 'commits-compare-switch has_tooltip', title: 'Switch base of comparison'}
= link_to 'switch', {from: params[:to], to: params[:from]}, {class: 'commits-compare-switch has-tooltip', title: 'Switch base of comparison'}
.form-group
.input-group.inline-input-group
%span.input-group-addon from
......
......@@ -28,7 +28,7 @@
.file-actions.hidden-xs
- if blob_text_viewable?(blob)
= link_to '#', class: 'js-toggle-diff-comments btn active has_tooltip', title: "Toggle comments for this file" do
= link_to '#', class: 'js-toggle-diff-comments btn active has-tooltip', title: "Toggle comments for this file" do
= icon('comments')
\
......
- type = line.type
%tr.line_holder{id: line_code, class: type}
- case type
- when 'match'
= render "projects/diffs/match_line", {line: line.text,
line_old: line.old_pos, line_new: line.new_pos, bottom: false, new_file: diff_file.new_file}
- when 'nonewline'
%td.old_line.diff-line-num
%td.new_line.diff-line-num
%td.line_content.match= line.text
- else
%td.old_line.diff-line-num{class: type}
- link_text = raw(type == "new" ? "&nbsp;" : line.old_pos)
- if defined?(plain) && plain
= link_text
- else
= link_to link_text, "##{line_code}", id: line_code
- if @comments_allowed && can?(current_user, :create_note, @project)
= link_to_new_diff_note(line_code)
%td.new_line.diff-line-num{class: type, data: {linenumber: line.new_pos}}
- link_text = raw(type == "old" ? "&nbsp;" : line.new_pos)
- if defined?(plain) && plain
= link_text
- else
= link_to link_text, "##{line_code}", id: line_code
%td.line_content{class: "noteable_line #{type} #{line_code}", data: { line_code: line_code }}= diff_line_content(line.text)
......@@ -8,26 +8,9 @@
- last_line = 0
- raw_diff_lines = diff_file.diff_lines.to_a
- diff_file.highlighted_diff_lines.each_with_index do |line, index|
- type = line.type
- last_line = line.new_pos
- line_code = generate_line_code(diff_file.file_path, line)
- line_old = line.old_pos
%tr.line_holder{ id: line_code, class: "#{type}" }
- if type == "match"
= render "projects/diffs/match_line", {line: line.text,
line_old: line_old, line_new: line.new_pos, bottom: false, new_file: diff_file.new_file}
- elsif type == 'nonewline'
%td.old_line.diff-line-num
%td.new_line.diff-line-num
%td.line_content.match= line.text
- else
%td.old_line.diff-line-num{class: type}
= link_to raw(type == "new" ? "&nbsp;" : line_old), "##{line_code}", id: line_code
- if @comments_allowed && can?(current_user, :create_note, @project)
= link_to_new_diff_note(line_code)
%td.new_line.diff-line-num{class: type, data: {linenumber: line.new_pos}}
= link_to raw(type == "old" ? "&nbsp;" : line.new_pos), "##{line_code}", id: line_code
%td.line_content{class: "noteable_line #{type} #{line_code}", data: { line_code: line_code }}= diff_line_content(line.text)
- last_line = line.new_pos
= render "projects/diffs/line", {line: line, diff_file: diff_file, line_code: line_code}
- if @reply_allowed
- comments = @line_notes.select { |n| n.line_code == line_code && n.active? }.sort_by(&:created_at)
......
......@@ -12,7 +12,7 @@
.col-md-2.col-sm-3
- if fork = namespace.find_fork_of(@project)
.fork-thumbnail
= link_to project_path(fork), title: "Visit project fork", class: 'has_tooltip' do
= link_to project_path(fork), title: "Visit project fork", class: 'has-tooltip' do
= image_tag namespace_icon(namespace, 100)
.caption
%strong
......@@ -22,7 +22,7 @@
- else
.fork-thumbnail
= link_to namespace_project_forks_path(@project.namespace, @project, namespace_key: namespace.id), title: "Fork here", method: "POST", class: 'has_tooltip' do
= link_to namespace_project_forks_path(@project.namespace, @project, namespace_key: namespace.id), title: "Fork here", method: "POST", class: 'has-tooltip' do
= image_tag namespace_icon(namespace, 100)
.caption
%strong
......
......@@ -45,7 +45,6 @@
- if can?(current_user, :update_issue, @issue)
= link_to 'Reopen issue', issue_path(@issue, issue: {state_event: :reopen}, status_only: true, format: 'json'), data: {no_turbolink: true}, class: "btn btn-nr btn-grouped btn-reopen #{issue_button_visibility(@issue, false)}", title: 'Reopen issue'
= link_to 'Close issue', issue_path(@issue, issue: {state_event: :close}, status_only: true, format: 'json'), data: {no_turbolink: true}, class: "btn btn-nr btn-grouped btn-close #{issue_button_visibility(@issue, true)}", title: 'Close issue'
= link_to edit_namespace_project_issue_path(@project.namespace, @project, @issue), class: 'btn btn-nr btn-grouped issuable-edit' do
= icon('pencil-square-o')
Edit
......
......@@ -17,7 +17,7 @@
- if merge_request.open? && merge_request.broken?
%li
= link_to merge_request_path(merge_request), class: "has_tooltip", title: "Cannot be merged automatically", data: { container: 'body' } do
= link_to merge_request_path(merge_request), class: "has-tooltip", title: "Cannot be merged automatically", data: { container: 'body' } do
= icon('exclamation-triangle')
- if merge_request.assignee
......
......@@ -29,7 +29,7 @@
- if @merge_request.open?
= link_to 'Close', merge_request_path(@merge_request, merge_request: { state_event: :close }), method: :put, class: 'btn btn-nr btn-grouped btn-close', title: 'Close merge request'
= link_to edit_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), class: 'btn btn-nr btn-grouped issuable-edit', id: 'edit_merge_request' do
%i.fa.fa-pencil-square-o
= icon('pencil-square-o')
Edit
- if @merge_request.closed?
= link_to 'Reopen', merge_request_path(@merge_request, merge_request: {state_event: :reopen }), method: :put, class: 'btn btn-nr btn-grouped btn-reopen reopen-mr-link', title: 'Reopen merge request'
......@@ -5,6 +5,6 @@
= render 'projects/zen', f: f, attr: :note, classes: 'note_text js-note-text js-task-list-field'
= render 'projects/notes/hints'
.note-form-actions
.note-form-actions.clearfix
= f.submit 'Save Comment', class: 'btn btn-nr btn-save btn-grouped js-comment-button'
= link_to 'Cancel', '#', class: 'btn btn-nr btn-cancel note-edit-cancel'
......@@ -15,11 +15,11 @@
= render 'projects/tags/download', ref: tag.name, project: @project
- if can?(current_user, :push_code, @project)
= link_to edit_namespace_project_tag_release_path(@project.namespace, @project, tag.name), class: 'btn-grouped btn has_tooltip', title: "Edit release notes" do
= link_to edit_namespace_project_tag_release_path(@project.namespace, @project, tag.name), class: 'btn-grouped btn has-tooltip', title: "Edit release notes" do
= icon("pencil")
- if can?(current_user, :admin_project, @project)
= link_to namespace_project_tag_path(@project.namespace, @project, tag.name), class: 'btn btn-grouped btn-xs btn-remove remove-row has_tooltip', title: "Delete tag", method: :delete, data: { confirm: "Deleting the '#{tag.name}' tag cannot be undone. Are you sure?", container: 'body' }, remote: true do
= link_to namespace_project_tag_path(@project.namespace, @project, tag.name), class: 'btn btn-grouped btn-xs btn-remove remove-row has-tooltip', title: "Delete tag", method: :delete, data: { confirm: "Deleting the '#{tag.name}' tag cannot be undone. Are you sure?", container: 'body' }, remote: true do
= icon("trash-o")
- if commit
......
......@@ -5,17 +5,17 @@
.gray-content-block
.pull-right
- if can?(current_user, :push_code, @project)
= link_to edit_namespace_project_tag_release_path(@project.namespace, @project, @tag.name), class: 'btn-grouped btn has_tooltip', title: 'Edit release notes' do
= link_to edit_namespace_project_tag_release_path(@project.namespace, @project, @tag.name), class: 'btn-grouped btn has-tooltip', title: 'Edit release notes' do
= icon("pencil")
= link_to namespace_project_tree_path(@project.namespace, @project, @tag.name), class: 'btn btn-grouped has_tooltip', title: 'Browse files' do
= link_to namespace_project_tree_path(@project.namespace, @project, @tag.name), class: 'btn btn-grouped has-tooltip', title: 'Browse files' do
= icon('files-o')
= link_to namespace_project_commits_path(@project.namespace, @project, @tag.name), class: 'btn btn-grouped has_tooltip', title: 'Browse commits' do
= link_to namespace_project_commits_path(@project.namespace, @project, @tag.name), class: 'btn btn-grouped has-tooltip', title: 'Browse commits' do
= icon('history')
- if can? current_user, :download_code, @project
= render 'projects/tags/download', ref: @tag.name, project: @project
- if can?(current_user, :admin_project, @project)
.pull-right
= link_to namespace_project_tag_path(@project.namespace, @project, @tag.name), class: 'btn btn-remove remove-row grouped has_tooltip', title: "Delete tag", method: :delete, data: { confirm: "Deleting the '#{@tag.name}' tag cannot be undone. Are you sure?" } do
= link_to namespace_project_tag_path(@project.namespace, @project, @tag.name), class: 'btn btn-remove remove-row grouped has-tooltip', title: "Delete tag", method: :delete, data: { confirm: "Deleting the '#{@tag.name}' tag cannot be undone. Are you sure?" } do
%i.fa.fa-trash-o
.title
%span.item-title= @tag.name
......
......@@ -15,7 +15,7 @@
- if current_user
%li
- if !on_top_of_branch?
%span.btn.btn-sm.add-to-tree.disabled.has_tooltip{title: "You can only add files when you are on a branch", data: { container: 'body' }}
%span.btn.btn-sm.add-to-tree.disabled.has-tooltip{title: "You can only add files when you are on a branch", data: { container: 'body' }}
= icon('plus')
- else
%span.dropdown
......
......@@ -6,4 +6,4 @@
- if milestone.description.present?
.description.term
= preserve do
= search_md_sanitize(markdown(milestone.description))
\ No newline at end of file
= search_md_sanitize(markdown(milestone.description))
......@@ -8,11 +8,9 @@
= icon('angle-down')
%ul.dropdown-menu.dropdown-menu-right.clone-options-dropdown
%li
%a#ssh-selector{href: @project.ssh_url_to_repo}
SSH
= ssh_clone_button(project)
%li
%a#http-selector{href: @project.http_url_to_repo}
HTTPS
= http_clone_button(project)
= text_field_tag :project_clone, default_url_to_repo(project), class: "js-select-on-focus form-control", readonly: true
.input-group-btn
......
......@@ -14,7 +14,7 @@
.stats
%span
= icon('home')
= icon('bookmark')
= number_with_delimiter(group.projects.count)
%span
......
......@@ -85,13 +85,26 @@
- if can? current_user, :admin_label, issuable.project
= link_to 'Create new label', new_namespace_project_label_path(issuable.project.namespace, issuable.project), target: :blank
- if issuable.can_move?(current_user)
%hr
.form-group
= label_tag :move_to_project_id, 'Move', class: 'control-label'
.col-sm-10
- projects = project_options(issuable, current_user, ability: :admin_issue)
= select_tag(:move_to_project_id, projects, include_blank: true,
class: 'select2', data: { placeholder: 'Select project' })
&nbsp;
%span{ data: { toggle: 'tooltip', placement: 'auto top' }, style: 'cursor: default',
title: 'Moving an issue will copy the discussion to a different project and close it here. All participants will be notified of the new location.' }
= icon('question-circle')
- if issuable.is_a?(MergeRequest)
%hr
- if @merge_request.new_record?
.form-group
= f.label :source_branch, class: 'control-label'
.col-sm-10
= f.select(:source_branch, [@merge_request.source_branch], { }, { class: 'source_branch select2 span2', disabled: true })
- if @merge_request.new_record?
.form-group
= f.label :source_branch, class: 'control-label'
.col-sm-10
= f.select(:source_branch, [@merge_request.source_branch], { }, { class: 'source_branch select2 span2', disabled: true })
.form-group
= f.label :target_branch, class: 'control-label'
.col-sm-10
......@@ -114,7 +127,11 @@
for this project.
- if issuable.new_record?
- cancel_project = issuable.source_project
= link_to 'Cancel', namespace_project_issues_path(@project.namespace, @project), class: 'btn btn-cancel'
- else
- cancel_project = issuable.project
= link_to 'Cancel', [cancel_project.namespace.becomes(Namespace), cancel_project, issuable], class: 'btn btn-cancel'
.pull-right
- if current_user.can?(:"destroy_#{issuable.to_ability_name}", @project)
= link_to polymorphic_path([@project.namespace.becomes(Namespace), @project, issuable]), method: :delete, class: 'btn btn-grouped' do
= icon('trash-o')
Delete
= link_to 'Cancel', namespace_project_issue_path(@project.namespace, @project, issuable), class: 'btn btn-grouped btn-cancel'
......@@ -23,5 +23,5 @@
- if assignee
= link_to polymorphic_path(base_url_args, { milestone_title: @milestone.title, assignee_id: issuable.assignee_id, state: 'all' }),
class: 'has_tooltip', data: { 'original-title' => "Assigned to #{sanitize(assignee.name)}", container: 'body' } do
class: 'has-tooltip', data: { 'original-title' => "Assigned to #{sanitize(assignee.name)}", container: 'body' } do
- image_tag(avatar_icon(issuable.assignee, 16), class: "avatar s16", alt: '')
.detail-page-header
.snippet-box.has_tooltip{class: visibility_level_color(@snippet.visibility_level), title: snippet_visibility_level_description(@snippet.visibility_level, @snippet), data: { container: 'body' }}
.snippet-box.has-tooltip{class: visibility_level_color(@snippet.visibility_level), title: snippet_visibility_level_description(@snippet.visibility_level, @snippet), data: { container: 'body' }}
= visibility_level_icon(@snippet.visibility_level, fw: false)
= visibility_level_label(@snippet.visibility_level)
%span.identifier
......
.awards.votes-block
- awards_sort(votable.notes.awards.grouped_awards).each do |emoji, notes|
%button.btn.award-control.js-emoji-btn.has_tooltip{class: (note_active_class(notes, current_user)), title: emoji_author_list(notes, current_user), data: {placement: "top"}}
%button.btn.award-control.js-emoji-btn.has-tooltip{class: (note_active_class(notes, current_user)), title: emoji_author_list(notes, current_user), data: {placement: "top"}}
= emoji_icon(emoji)
%span.award-control-text.js-counter
= notes.count
......
......@@ -20,14 +20,15 @@ class RepositoryForkWorker
return
end
project.repository.after_import
unless project.valid_repo?
logger.error("Project #{id} had an invalid repository after fork")
logger.error("Project #{project_id} had an invalid repository after fork")
project.update(import_error: "The forked repository is invalid.")
project.import_fail
return
end
project.repository.after_import
project.import_finish
end
end
......@@ -49,6 +49,7 @@ module Gitlab
config.assets.paths << Gemojione.index.images_path
config.assets.precompile << "*.png"
config.assets.precompile << "print.css"
config.assets.precompile << "notify.css"
# Version of your assets, change this if you want to expire all your assets
config.assets.version = '1.0'
......
# See https://github.com/fphilipe/premailer-rails#configuration
Premailer::Rails.config.merge!(
generate_text_part: false,
preserve_styles: true,
remove_comments: true,
remove_ids: true
)
......@@ -613,7 +613,7 @@ Rails.application.routes.draw do
end
end
resources :merge_requests, constraints: { id: /\d+/ }, except: [:destroy] do
resources :merge_requests, constraints: { id: /\d+/ } do
member do
get :commits
get :diffs
......@@ -684,7 +684,7 @@ Rails.application.routes.draw do
end
end
resources :issues, constraints: { id: /\d+/ }, except: [:destroy] do
resources :issues, constraints: { id: /\d+/ } do
member do
post :toggle_subscription
end
......
class AddDeleteAtToIssues < ActiveRecord::Migration
def change
add_column :issues, :deleted_at, :datetime
add_index :issues, :deleted_at
end
end
class AddDeleteAtToMergeRequests < ActiveRecord::Migration
def change
add_column :merge_requests, :deleted_at, :datetime
add_index :merge_requests, :deleted_at
end
end
class AddMovedToToIssue < ActiveRecord::Migration
def change
add_reference :issues, :moved_to, references: :issues
end
end
......@@ -416,7 +416,9 @@ ActiveRecord::Schema.define(version: 20160320204112) do
t.string "state"
t.integer "iid"
t.integer "updated_by_id"
t.boolean "confidential", default: false
t.integer "moved_to_id"
t.boolean "confidential", default: false
t.datetime "deleted_at"
end
add_index "issues", ["assignee_id"], name: "index_issues_on_assignee_id", using: :btree
......@@ -424,6 +426,7 @@ ActiveRecord::Schema.define(version: 20160320204112) do
add_index "issues", ["confidential"], name: "index_issues_on_confidential", using: :btree
add_index "issues", ["created_at", "id"], name: "index_issues_on_created_at_and_id", using: :btree
add_index "issues", ["created_at"], name: "index_issues_on_created_at", using: :btree
add_index "issues", ["deleted_at"], name: "index_issues_on_deleted_at", using: :btree
add_index "issues", ["description"], name: "index_issues_on_description_trigram", using: :gin, opclasses: {"description"=>"gin_trgm_ops"}
add_index "issues", ["milestone_id"], name: "index_issues_on_milestone_id", using: :btree
add_index "issues", ["project_id", "iid"], name: "index_issues_on_project_id_and_iid", unique: true, using: :btree
......@@ -546,12 +549,14 @@ ActiveRecord::Schema.define(version: 20160320204112) do
t.boolean "merge_when_build_succeeds", default: false, null: false
t.integer "merge_user_id"
t.string "merge_commit_sha"
t.datetime "deleted_at"
end
add_index "merge_requests", ["assignee_id"], name: "index_merge_requests_on_assignee_id", using: :btree
add_index "merge_requests", ["author_id"], name: "index_merge_requests_on_author_id", using: :btree
add_index "merge_requests", ["created_at", "id"], name: "index_merge_requests_on_created_at_and_id", using: :btree
add_index "merge_requests", ["created_at"], name: "index_merge_requests_on_created_at", using: :btree
add_index "merge_requests", ["deleted_at"], name: "index_merge_requests_on_deleted_at", using: :btree
add_index "merge_requests", ["description"], name: "index_merge_requests_on_description_trigram", using: :gin, opclasses: {"description"=>"gin_trgm_ops"}
add_index "merge_requests", ["milestone_id"], name: "index_merge_requests_on_milestone_id", using: :btree
add_index "merge_requests", ["source_branch"], name: "index_merge_requests_on_source_branch", using: :btree
......
......@@ -326,17 +326,25 @@ Example response:
}
```
## Delete existing issue (**Deprecated**)
## Delete an issue
This call is deprecated and returns a `405 Method Not Allowed` error if called.
An issue gets now closed and is done by calling
`PUT /projects/:id/issues/:issue_id` with the parameter `state_event` set to
`close`. See [edit issue](#edit-issue) for more details.
Only for admins and project owners. Soft deletes the issue in question.
If the operation is successful, a status code `200` is returned. In case you cannot
destroy this issue, or it is not present, code `404` is given.
```
DELETE /projects/:id/issues/:issue_id
```
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `id` | integer | yes | The ID of a project |
| `issue_id` | integer | yes | The ID of a project's issue |
```bash
curl -X DELETE -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/4/issues/85
```
## Comments on issues
Comments are done via the [notes](notes.md) resource.
......@@ -380,6 +380,25 @@ Parameters:
If the operation is successful, 200 and the updated merge request is returned.
If an error occurs, an error number and a message explaining the reason is returned.
## Delete a merge request
Only for admins and project owners. Soft deletes the merge request in question.
If the operation is successful, a status code `200` is returned. In case you cannot
destroy this merge request, or it is not present, code `404` is given.
```
DELETE /projects/:id/merge_requests/:merge_request_id
```
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `id` | integer | yes | The ID of a project |
| `merge_request_id` | integer | yes | The ID of a project's merge request |
```bash
curl -X DELETE -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/4/merge_request/85
```
## Accept MR
Merge changes submitted with MR using this API.
......
......@@ -348,7 +348,7 @@ GitLab Shell is an SSH access and repository management software developed speci
cd /home/git
sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-workhorse.git
cd gitlab-workhorse
sudo -u git -H git checkout 0.6.5
sudo -u git -H git checkout v0.7.1
sudo -u git -H make
### Initialize Database and Activate Advanced Features
......
......@@ -58,7 +58,7 @@ GitLab 8.1.
```bash
cd /home/git/gitlab-workhorse
sudo -u git -H git fetch --all
sudo -u git -H git checkout 0.6.5
sudo -u git -H git checkout v0.7.1
sudo -u git -H make
```
......
......@@ -160,6 +160,7 @@ Feature: Project Issues
Scenario: Issues on empty project
Given empty project "Empty Project"
And I have an ssh key
When I visit empty project page
And I see empty project details with ssh clone info
When I visit empty project's issues page
......
......@@ -27,7 +27,7 @@ class Spinach::Features::ProjectCreate < Spinach::FeatureSteps
step 'I click on HTTP' do
find('#clone-dropdown').click
find('#http-selector').click
find('.http-selector').click
end
step 'Remote url should update to http link' do
......@@ -36,7 +36,7 @@ class Spinach::Features::ProjectCreate < Spinach::FeatureSteps
step 'If I click on SSH' do
find('#clone-dropdown').click
find('#ssh-selector').click
find('.ssh-selector').click
end
step 'Remote url should update to ssh link' do
......
......@@ -5,6 +5,7 @@ class Spinach::Features::ProjectIssues < Spinach::FeatureSteps
include SharedNote
include SharedPaths
include SharedMarkdown
include SharedUser
step 'I should see "Release 0.4" in issues' do
expect(page).to have_content "Release 0.4"
......@@ -240,7 +241,7 @@ class Spinach::Features::ProjectIssues < Spinach::FeatureSteps
end
step 'empty project "Empty Project"' do
create :empty_project, name: 'Empty Project', namespace: @user.namespace
create :project_empty_repo, name: 'Empty Project', namespace: @user.namespace
end
When 'I visit empty project page' do
......
......@@ -118,9 +118,7 @@ module API
end
def authorize!(action, subject)
unless abilities.allowed?(current_user, action, subject)
forbidden!
end
forbidden! unless abilities.allowed?(current_user, action, subject)
end
def authorize_push_project
......
......@@ -191,7 +191,7 @@ module API
end
end
# Delete a project issue (deprecated)
# Delete a project issue
#
# Parameters:
# id (required) - The ID of a project
......@@ -199,7 +199,10 @@ module API
# Example Request:
# DELETE /projects/:id/issues/:issue_id
delete ":id/issues/:issue_id" do
not_allowed!
issue = user_project.issues.find_by(id: params[:issue_id])
authorize!(:destroy_issue, issue)
issue.destroy
end
end
end
......
......@@ -100,6 +100,18 @@ module API
end
end
# Delete a MR
#
# Parameters:
# id (required) - The ID of the project
# merge_request_id (required) - The MR id
delete ":id/merge_requests/:merge_request_id" do
merge_request = user_project.merge_requests.find_by(id: params[:merge_request_id])
authorize!(:destroy_merge_request, merge_request)
merge_request.destroy
end
# Routing "merge_request/:merge_request_id/..." is DEPRECATED and WILL BE REMOVED in version 9.0
# Use "merge_requests/:merge_request_id/..." instead.
#
......
......@@ -47,6 +47,7 @@ module Banzai
# Returns a String
def data_attribute(attributes = {})
attributes[:reference_filter] = self.class.name.demodulize
attributes.delete(:original) if context[:no_original_data]
attributes.map { |key, value| %Q(data-#{key.to_s.dasherize}="#{escape_once(value)}") }.join(" ")
end
......
module Gitlab
module Gfm
##
# Class that unfolds local references in text.
#
# The initializer takes text in Markdown and project this text is valid
# in context of.
#
# `unfold` method tries to find all local references and unfold each of
# those local references to cross reference format, assuming that the
# argument passed to this method is a project that references will be
# viewed from (see `Referable#to_reference method).
#
# Examples:
#
# 'Hello, this issue is related to #123 and
# other issues labeled with ~"label"', will be converted to:
#
# 'Hello, this issue is related to gitlab-org/gitlab-ce#123 and
# other issue labeled with gitlab-org/gitlab-ce~"label"'.
#
# It does respect markdown lexical rules, so text in code block will not be
# replaced, see another example:
#
# 'Merge request for issue #1234, see also link:
# http://gitlab.com/some/link/#1234, and code `puts #1234`' =>
#
# 'Merge request for issue gitlab-org/gitlab-ce#1234, se also link:
# http://gitlab.com/some/link/#1234, and code `puts #1234`'
#
class ReferenceRewriter
def initialize(text, source_project, current_user)
@text = text
@source_project = source_project
@current_user = current_user
@original_html = markdown(text)
end
def rewrite(target_project)
pattern = Gitlab::ReferenceExtractor.references_pattern
@text.gsub(pattern) do |reference|
unfold_reference(reference, Regexp.last_match, target_project)
end
end
private
def unfold_reference(reference, match, target_project)
before = @text[0...match.begin(0)]
after = @text[match.end(0)..-1]
referable = find_referable(reference)
return reference unless referable
cross_reference = referable.to_reference(target_project)
return reference if reference == cross_reference
new_text = before + cross_reference + after
substitution_valid?(new_text) ? cross_reference : reference
end
def find_referable(reference)
extractor = Gitlab::ReferenceExtractor.new(@source_project,
@current_user)
extractor.analyze(reference)
extractor.all.first
end
def substitution_valid?(substituted)
@original_html == markdown(substituted)
end
def markdown(text)
Banzai.render(text, project: @source_project, no_original_data: true)
end
end
end
end
module Gitlab
# Extract possible GFM references from an arbitrary String for further processing.
class ReferenceExtractor < Banzai::ReferenceExtractor
REFERABLES = %i(user issue label milestone merge_request snippet commit commit_range)
attr_accessor :project, :current_user, :author
def initialize(project, current_user = nil, author = nil)
......@@ -17,7 +18,7 @@ module Gitlab
super(text, context.merge(project: project))
end
%i(user label milestone merge_request snippet commit commit_range).each do |type|
REFERABLES.each do |type|
define_method("#{type}s") do
@references[type] ||= references(type, reference_context)
end
......@@ -31,6 +32,21 @@ module Gitlab
end
end
def all
REFERABLES.each { |referable| send(referable.to_s.pluralize) }
@references.values.flatten
end
def self.references_pattern
return @pattern if @pattern
patterns = REFERABLES.map do |ref|
ref.to_s.classify.constantize.try(:reference_pattern)
end
@pattern = Regexp.union(patterns.compact)
end
private
def reference_context
......
require('spec_helper')
describe Projects::IssuesController do
describe "GET #index" do
let(:project) { create(:project) }
let(:user) { create(:user) }
let(:issue) { create(:issue, project: project) }
let(:project) { create(:project_empty_repo) }
let(:user) { create(:user) }
let(:issue) { create(:issue, project: project) }
describe "GET #index" do
before do
sign_in(user)
project.team << [user, :developer]
......@@ -41,7 +41,7 @@ describe Projects::IssuesController do
end
describe 'Confidential Issues' do
let(:project) { create(:empty_project, :public) }
let(:project) { create(:project_empty_repo, :public) }
let(:assignee) { create(:assignee) }
let(:author) { create(:user) }
let(:non_member) { create(:user) }
......@@ -186,4 +186,29 @@ describe Projects::IssuesController do
end
end
end
describe "DELETE #destroy" do
context "when the user is a developer" do
before { sign_in(user) }
it "rejects a developer to destroy an issue" do
delete :destroy, namespace_id: project.namespace.path, project_id: project.path, id: issue.iid
expect(response.status).to eq(404)
end
end
context "when the user is owner" do
let(:owner) { create(:user) }
let(:namespace) { create(:namespace, owner: owner) }
let(:project) { create(:project, namespace: namespace) }
before { sign_in(owner) }
it "deletes the issue" do
delete :destroy, namespace_id: project.namespace.path, project_id: project.path, id: issue.iid
expect(response.status).to eq(302)
expect(controller).to set_flash[:notice].to(/The issue was successfully deleted\./).now
end
end
end
end
......@@ -157,6 +157,29 @@ describe Projects::MergeRequestsController do
end
end
describe "DELETE #destroy" do
it "denies access to users unless they're admin or project owner" do
delete :destroy, namespace_id: project.namespace.path, project_id: project.path, id: merge_request.iid
expect(response.status).to eq(404)
end
context "when the user is owner" do
let(:owner) { create(:user) }
let(:namespace) { create(:namespace, owner: owner) }
let(:project) { create(:project, namespace: namespace) }
before { sign_in owner }
it "deletes the merge request" do
delete :destroy, namespace_id: project.namespace.path, project_id: project.path, id: merge_request.iid
expect(response.status).to eq(302)
expect(controller).to set_flash[:notice].to(/The merge request was successfully deleted\./).now
end
end
end
describe 'GET diffs' do
def go(format: 'html')
get :diffs,
......
require 'rails_helper'
feature 'issue move to another project' do
let(:user) { create(:user) }
let(:old_project) { create(:project) }
let(:text) { 'Some issue description' }
let(:issue) do
create(:issue, description: text, project: old_project, author: user)
end
background { login_as(user) }
context 'user does not have permission to move issue' do
background do
old_project.team << [user, :guest]
edit_issue(issue)
end
scenario 'moving issue to another project not allowed' do
expect(page).to have_no_select('move_to_project_id')
end
end
context 'user has permission to move issue' do
let!(:mr) { create(:merge_request, source_project: old_project) }
let(:new_project) { create(:project) }
let(:text) { 'Text with !1' }
let(:cross_reference) { old_project.to_reference }
background do
old_project.team << [user, :reporter]
new_project.team << [user, :reporter]
edit_issue(issue)
end
scenario 'moving issue to another project' do
select(new_project.name_with_namespace, from: 'move_to_project_id')
click_button('Save changes')
expect(current_url).to include project_path(new_project)
page.within('.issue') do
expect(page).to have_content("Text with #{cross_reference}!1")
expect(page).to have_content("Moved from #{cross_reference}#1")
expect(page).to have_content(issue.title)
end
end
context 'projects user does not have permission to move issue to exist' do
let!(:private_project) { create(:project, :private) }
let(:another_project) { create(:project) }
background { another_project.team << [user, :guest] }
scenario 'browsing projects in projects select' do
options = [ '', 'No project', new_project.name_with_namespace ]
expect(page).to have_select('move_to_project_id', options: options)
end
end
context 'issue has been already moved' do
let(:new_issue) { create(:issue, project: new_project) }
let(:issue) do
create(:issue, project: old_project, author: user, moved_to: new_issue)
end
scenario 'user wants to move issue that has already been moved' do
expect(page).to have_no_select('move_to_project_id')
end
end
end
def edit_issue(issue)
visit issue_path(issue)
page.within('.issuable-header') { click_link 'Edit' }
end
def issue_path(issue)
namespace_project_issue_path(issue.project.namespace, issue.project, issue)
end
def project_path(project)
namespace_project_path(new_project.namespace, new_project)
end
end
......@@ -11,7 +11,7 @@ describe LabelsHelper do
end
it 'uses the instance variable' do
expect(link_to_label(label)).to match %r{<a href="/#{@project.to_reference}/issues\?label_name=#{label.name}"><span class="[\w\s\-]*has_tooltip".*</span></a>}
expect(link_to_label(label)).to match %r{<a href="/#{@project.to_reference}/issues\?label_name=#{label.name}"><span class="[\w\s\-]*has-tooltip".*</span></a>}
end
end
......@@ -41,8 +41,8 @@ describe LabelsHelper do
context 'with a tooltip argument' do
context 'set to false' do
it 'does not include the has_tooltip class' do
expect(link_to_label(label, tooltip: false)).not_to match %r{has_tooltip}
it 'does not include the has-tooltip class' do
expect(link_to_label(label, tooltip: false)).not_to match %r{has-tooltip}
end
end
end
......
......@@ -86,4 +86,23 @@ describe ProjectsHelper do
end
end
end
describe 'default_clone_protocol' do
describe 'using HTTP' do
it 'returns HTTP' do
expect(helper).to receive(:current_user).and_return(nil)
expect(helper.send(:default_clone_protocol)).to eq('http')
end
end
describe 'using HTTPS' do
it 'returns HTTPS' do
allow(Gitlab.config.gitlab).to receive(:protocol).and_return('https')
expect(helper).to receive(:current_user).and_return(nil)
expect(helper.send(:default_clone_protocol)).to eq('https')
end
end
end
end
......@@ -56,7 +56,7 @@ describe Banzai::Filter::LabelReferenceFilter, lib: true do
describe 'label span element' do
it 'includes default classes' do
doc = reference_filter("Label #{reference}")
expect(doc.css('a span').first.attr('class')).to eq 'label color-label has_tooltip'
expect(doc.css('a span').first.attr('class')).to eq 'label color-label has-tooltip'
end
it 'includes a style attribute' do
......
require 'spec_helper'
describe Gitlab::Gfm::ReferenceRewriter do
let(:text) { 'some text' }
let(:old_project) { create(:project) }
let(:new_project) { create(:project) }
let(:user) { create(:user) }
before { old_project.team << [user, :guest] }
describe '#rewrite' do
subject do
described_class.new(text, old_project, user).rewrite(new_project)
end
context 'multiple issues and merge requests referenced' do
let!(:issue_first) { create(:issue, project: old_project) }
let!(:issue_second) { create(:issue, project: old_project) }
let!(:merge_request) { create(:merge_request, source_project: old_project) }
context 'plain text description' do
let(:text) { 'Description that references #1, #2 and !1' }
it { is_expected.to include issue_first.to_reference(new_project) }
it { is_expected.to include issue_second.to_reference(new_project) }
it { is_expected.to include merge_request.to_reference(new_project) }
end
context 'description with ignored elements' do
let(:text) do
"Hi. This references #1, but not `#2`\n" +
'<pre>and not !1</pre>'
end
it { is_expected.to include issue_first.to_reference(new_project) }
it { is_expected.to_not include issue_second.to_reference(new_project) }
it { is_expected.to_not include merge_request.to_reference(new_project) }
end
context 'description ambigous elements' do
context 'url' do
let(:url) { 'http://gitlab.com/#1' }
let(:text) { "This references #1, but not #{url}" }
it { is_expected.to include url }
end
context 'code' do
let(:text) { "#1, but not `[#1]`" }
it { is_expected.to eq "#{issue_first.to_reference(new_project)}, but not `[#1]`" }
end
context 'code reverse' do
let(:text) { "not `#1`, but #1" }
it { is_expected.to eq "not `#1`, but #{issue_first.to_reference(new_project)}" }
end
context 'code in random order' do
let(:text) { "#1, `#1`, #1, `#1`" }
let(:ref) { issue_first.to_reference(new_project) }
it { is_expected.to eq "#{ref}, `#1`, #{ref}, `#1`" }
end
context 'description with labels' do
let!(:label) { create(:label, id: 123, name: 'test', project: old_project) }
let(:project_ref) { old_project.to_reference }
context 'label referenced by id' do
let(:text) { '#1 and ~123' }
it { is_expected.to eq %Q{#{project_ref}#1 and #{project_ref}~123} }
end
context 'label referenced by text' do
let(:text) { '#1 and ~"test"' }
it { is_expected.to eq %Q{#{project_ref}#1 and #{project_ref}~123} }
end
end
end
context 'reference contains milestone' do
let(:milestone) { create(:milestone) }
let(:text) { "milestone ref: #{milestone.to_reference}" }
it { is_expected.to eq text }
end
end
end
end
......@@ -124,4 +124,24 @@ describe Gitlab::ReferenceExtractor, lib: true do
expect(extracted).to match_array([issue])
end
end
describe '#all' do
let(:issue) { create(:issue, project: project) }
let(:label) { create(:label, project: project) }
let(:text) { "Ref. #{issue.to_reference} and #{label.to_reference}" }
before do
project.team << [project.creator, :developer]
subject.analyze(text)
end
it 'returns all referables' do
expect(subject.all).to match_array([issue, label])
end
end
describe '.references_pattern' do
subject { described_class.references_pattern }
it { is_expected.to be_kind_of Regexp }
end
end
......@@ -158,6 +158,33 @@ describe Notify do
is_expected.to have_body_text /#{namespace_project_issue_path project.namespace, project, issue}/
end
end
describe 'moved to another project' do
let(:new_issue) { create(:issue) }
subject { Notify.issue_moved_email(recipient, issue, new_issue, current_user) }
it_behaves_like 'an answer to an existing thread', 'issue'
it_behaves_like 'it should show Gmail Actions View Issue link'
it_behaves_like 'an unsubscribeable thread'
it 'contains description about action taken' do
is_expected.to have_body_text 'Issue was moved to another project'
end
it 'has the correct subject' do
is_expected.to have_subject /#{issue.title} \(##{issue.iid}\)/i
end
it 'contains link to new issue' do
new_issue_url = namespace_project_issue_path(new_issue.project.namespace,
new_issue.project, new_issue)
is_expected.to have_body_text new_issue_url
end
it 'contains a link to the original issue' do
is_expected.to have_body_text /#{namespace_project_issue_path project.namespace, project, issue}/
end
end
end
context 'for merge requests' do
......
......@@ -37,6 +37,11 @@ describe Issue, models: true do
subject { create(:issue) }
describe "act_as_paranoid" do
it { is_expected.to have_db_column(:deleted_at) }
it { is_expected.to have_db_index(:deleted_at) }
end
describe '#to_reference' do
it 'returns a String reference to the object' do
expect(subject.to_reference).to eq "##{subject.iid}"
......@@ -130,12 +135,62 @@ describe Issue, models: true do
end
end
describe '#can_move?' do
let(:user) { create(:user) }
let(:issue) { create(:issue) }
subject { issue.can_move?(user) }
context 'user is not a member of project issue belongs to' do
it { is_expected.to eq false}
end
context 'user is reporter in project issue belongs to' do
let(:project) { create(:project) }
let(:issue) { create(:issue, project: project) }
before { project.team << [user, :reporter] }
it { is_expected.to eq true }
context 'checking destination project also' do
subject { issue.can_move?(user, to_project) }
let(:to_project) { create(:project) }
context 'destination project allowed' do
before { to_project.team << [user, :reporter] }
it { is_expected.to eq true }
end
context 'destination project not allowed' do
before { to_project.team << [user, :guest] }
it { is_expected.to eq false }
end
end
end
end
describe '#moved?' do
let(:issue) { create(:issue) }
subject { issue.moved? }
context 'issue not moved' do
it { is_expected.to eq false }
end
context 'issue already moved' do
let(:moved_to_issue) { create(:issue) }
let(:issue) { create(:issue, moved_to: moved_to_issue) }
it { is_expected.to eq true }
end
end
describe '#related_branches' do
it "selects the right branches" do
allow(subject.project.repository).to receive(:branch_names).
and_return(["mpempe", "#{subject.iid}mepmep", subject.to_branch_name])
and_return(["mpempe", "#{subject.iid}mepmep", subject.to_branch_name])
expect(subject.related_branches).to eq [subject.to_branch_name]
expect(subject.related_branches).to eq([subject.to_branch_name])
end
end
......
......@@ -49,6 +49,11 @@ describe MergeRequest, models: true do
it { is_expected.to include_module(Taskable) }
end
describe "act_as_paranoid" do
it { is_expected.to have_db_column(:deleted_at) }
it { is_expected.to have_db_index(:deleted_at) }
end
describe 'validation' do
it { is_expected.to validate_presence_of(:target_branch) }
it { is_expected.to validate_presence_of(:source_branch) }
......
......@@ -732,4 +732,45 @@ describe Project, models: true do
it { expect(internal_project.visibility_level_allowed?(Gitlab::VisibilityLevel::PUBLIC)).to be_falsey }
end
end
describe '#create_repository' do
let(:project) { create(:project) }
let(:shell) { Gitlab::Shell.new }
before do
allow(project).to receive(:gitlab_shell).and_return(shell)
end
context 'using a regular repository' do
it 'creates the repository' do
expect(shell).to receive(:add_repository).
with(project.path_with_namespace).
and_return(true)
expect(project.repository).to receive(:after_create)
expect(project.create_repository).to eq(true)
end
it 'adds an error if the repository could not be created' do
expect(shell).to receive(:add_repository).
with(project.path_with_namespace).
and_return(false)
expect(project.repository).not_to receive(:after_create)
expect(project.create_repository).to eq(false)
expect(project.errors).not_to be_empty
end
end
context 'using a forked repository' do
it 'does nothing' do
expect(project).to receive(:forked?).and_return(true)
expect(shell).not_to receive(:add_repository)
project.create_repository
end
end
end
end
......@@ -244,6 +244,18 @@ describe ProjectWiki, models: true do
end
end
describe '#create_repo!' do
it 'creates a repository' do
expect(subject).to receive(:init_repo).
with(subject.path_with_namespace).
and_return(true)
expect(subject.repository).to receive(:after_create)
expect(subject.create_repo!).to be_an_instance_of(Gollum::Wiki)
end
end
private
def create_temp_repo(path)
......
......@@ -537,6 +537,12 @@ describe Repository, models: true do
repository.before_delete
end
it 'flushes the exists cache' do
expect(repository).to receive(:expire_exists_cache)
repository.before_delete
end
end
describe 'when a repository exists' do
......@@ -593,6 +599,12 @@ describe Repository, models: true do
repository.after_import
end
it 'flushes the exists cache' do
expect(repository).to receive(:expire_exists_cache)
repository.after_import
end
end
describe '#after_push_commit' do
......@@ -619,6 +631,14 @@ describe Repository, models: true do
end
end
describe '#after_create' do
it 'flushes the exists cache' do
expect(repository).to receive(:expire_exists_cache)
repository.after_create
end
end
describe "#main_language" do
it 'shows the main language of the project' do
expect(repository.main_language).to eq("Ruby")
......@@ -781,6 +801,16 @@ describe Repository, models: true do
end
end
describe '#expire_exists_cache' do
let(:cache) { repository.send(:cache) }
it 'expires the cache' do
expect(cache).to receive(:expire).with(:exists?)
repository.expire_exists_cache
end
end
describe '#build_cache' do
let(:cache) { repository.send(:cache) }
......
......@@ -2,12 +2,12 @@ require 'spec_helper'
describe API::API, api: true do
include ApiHelpers
let(:user) { create(:user) }
let(:non_member) { create(:user) }
let(:author) { create(:author) }
let(:assignee) { create(:assignee) }
let(:admin) { create(:admin) }
let!(:project) { create(:project, :public, namespace: user.namespace ) }
let(:user) { create(:user) }
let(:non_member) { create(:user) }
let(:author) { create(:author) }
let(:assignee) { create(:assignee) }
let(:admin) { create(:user, :admin) }
let!(:project) { create(:project, :public, namespace: user.namespace ) }
let!(:closed_issue) do
create :closed_issue,
author: user,
......@@ -469,9 +469,25 @@ describe API::API, api: true do
end
describe "DELETE /projects/:id/issues/:issue_id" do
it "should delete a project issue" do
delete api("/projects/#{project.id}/issues/#{issue.id}", user)
expect(response.status).to eq(405)
it "rejects a non member from deleting an issue" do
delete api("/projects/#{project.id}/issues/#{issue.id}", non_member)
expect(response.status).to be(403)
end
it "rejects a developer from deleting an issue" do
delete api("/projects/#{project.id}/issues/#{issue.id}", author)
expect(response.status).to be(403)
end
context "when the user is project owner" do
let(:owner) { create(:user) }
let(:project) { create(:project, namespace: owner.namespace) }
it "deletes the issue if an admin requests it" do
delete api("/projects/#{project.id}/issues/#{issue.id}", owner)
expect(response.status).to eq(200)
expect(json_response['state']).to eq 'opened'
end
end
end
end
......@@ -2,15 +2,17 @@ require "spec_helper"
describe API::API, api: true do
include ApiHelpers
let(:base_time) { Time.now }
let(:user) { create(:user) }
let!(:project) {create(:project, creator_id: user.id, namespace: user.namespace) }
let(:base_time) { Time.now }
let(:user) { create(:user) }
let(:admin) { create(:user, :admin) }
let(:non_member) { create(:user) }
let!(:project) { create(:project, creator_id: user.id, namespace: user.namespace) }
let!(:merge_request) { create(:merge_request, :simple, author: user, assignee: user, source_project: project, target_project: project, title: "Test", created_at: base_time) }
let!(:merge_request_closed) { create(:merge_request, state: "closed", author: user, assignee: user, source_project: project, target_project: project, title: "Closed test", created_at: base_time + 1.second) }
let!(:merge_request_merged) { create(:merge_request, state: "merged", author: user, assignee: user, source_project: project, target_project: project, title: "Merged test", created_at: base_time + 2.seconds) }
let!(:note) { create(:note_on_merge_request, author: user, project: project, noteable: merge_request, note: "a comment on a MR") }
let!(:note2) { create(:note_on_merge_request, author: user, project: project, noteable: merge_request, note: "another comment on a MR") }
let(:milestone) { create(:milestone, title: '1.0.0', project: project) }
let!(:note) { create(:note_on_merge_request, author: user, project: project, noteable: merge_request, note: "a comment on a MR") }
let!(:note2) { create(:note_on_merge_request, author: user, project: project, noteable: merge_request, note: "another comment on a MR") }
let(:milestone) { create(:milestone, title: '1.0.0', project: project) }
before do
project.team << [user, :reporters]
......@@ -315,6 +317,29 @@ describe API::API, api: true do
end
end
describe "DELETE /projects/:id/merge_requests/:merge_request_id" do
context "when the user is developer" do
let(:developer) { create(:user) }
before do
project.team << [developer, :developer]
end
it "denies the deletion of the merge request" do
delete api("/projects/#{project.id}/merge_requests/#{merge_request.id}", developer)
expect(response.status).to be(403)
end
end
context "when the user is project owner" do
it "destroys the merge request owners can destroy" do
delete api("/projects/#{project.id}/merge_requests/#{merge_request.id}", user)
expect(response.status).to eq(200)
end
end
end
describe "PUT /projects/:id/merge_requests/:merge_request_id to close MR" do
it "should return merge_request" do
put api("/projects/#{project.id}/merge_requests/#{merge_request.id}", user), state_event: "close"
......
require 'spec_helper'
describe Issues::MoveService, services: true do
let(:user) { create(:user) }
let(:author) { create(:user) }
let(:title) { 'Some issue' }
let(:description) { 'Some issue description' }
let(:old_project) { create(:project) }
let(:new_project) { create(:project) }
let(:old_issue) do
create(:issue, title: title, description: description,
project: old_project, author: author)
end
let(:move_service) do
described_class.new(old_project, user)
end
shared_context 'user can move issue' do
before do
old_project.team << [user, :reporter]
new_project.team << [user, :reporter]
end
end
describe '#execute' do
shared_context 'issue move executed' do
let!(:new_issue) { move_service.execute(old_issue, new_project) }
end
context 'issue movable' do
include_context 'user can move issue'
context 'generic issue' do
include_context 'issue move executed'
it 'creates a new issue in a new project' do
expect(new_issue.project).to eq new_project
end
it 'rewrites issue title' do
expect(new_issue.title).to eq title
end
it 'rewrites issue description' do
expect(new_issue.description).to eq description
end
it 'adds system note to old issue at the end' do
expect(old_issue.notes.last.note).to match /^Moved to/
end
it 'adds system note to new issue at the end' do
expect(new_issue.notes.last.note).to match /^Moved from/
end
it 'closes old issue' do
expect(old_issue.closed?).to be true
end
it 'persists new issue' do
expect(new_issue.persisted?).to be true
end
it 'persists all changes' do
expect(old_issue.changed?).to be false
expect(new_issue.changed?).to be false
end
it 'preserves author' do
expect(new_issue.author).to eq author
end
it 'removes data that is invalid in new context' do
expect(new_issue.milestone).to be_nil
expect(new_issue.labels).to be_empty
end
it 'creates a new internal id for issue' do
expect(new_issue.iid).to be 1
end
it 'marks issue as moved' do
expect(old_issue.moved?).to eq true
expect(old_issue.moved_to).to eq new_issue
end
end
context 'issue with notes' do
context 'notes without references' do
let(:notes_params) do
[{ system: false, note: 'Some comment 1' },
{ system: true, note: 'Some system note' },
{ system: false, note: 'Some comment 2' }]
end
let(:notes_contents) { notes_params.map { |n| n[:note] } }
before do
note_params = { noteable: old_issue, project: old_project, author: author }
notes_params.each do |note|
create(:note, note_params.merge(note))
end
end
include_context 'issue move executed'
let(:all_notes) { new_issue.notes.order('id ASC') }
let(:system_notes) { all_notes.system }
let(:user_notes) { all_notes.user }
it 'rewrites existing notes in valid order' do
expect(all_notes.pluck(:note).first(3)).to eq notes_contents
end
it 'adds a system note about move after rewritten notes' do
expect(system_notes.last.note).to match /^Moved from/
end
it 'preserves orignal author of comment' do
expect(user_notes.pluck(:author_id)).to all(eq(author.id))
end
it 'preserves time when note has been created at' do
expect(old_issue.notes.first.created_at)
.to eq new_issue.notes.first.created_at
end
end
context 'notes with references' do
before do
create(:merge_request, source_project: old_project)
create(:note, noteable: old_issue, project: old_project, author: author,
note: 'Note with reference to merge request !1')
end
include_context 'issue move executed'
let(:new_note) { new_issue.notes.first }
it 'rewrites references using a cross reference to old project' do
expect(new_note.note)
.to eq "Note with reference to merge request #{old_project.to_reference}!1"
end
end
end
describe 'rewritting references' do
include_context 'issue move executed'
context 'issue reference' do
let(:another_issue) { create(:issue, project: old_project) }
let(:description) { "Some description #{another_issue.to_reference}" }
it 'rewrites referenced issues creating cross project reference' do
expect(new_issue.description)
.to eq "Some description #{old_project.to_reference}#{another_issue.to_reference}"
end
end
end
context 'moving to same project' do
let(:new_project) { old_project }
it 'raises error' do
expect { move_service.execute(old_issue, new_project) }
.to raise_error(StandardError, /Cannot move issue/)
end
end
end
describe 'move permissions' do
let(:move) { move_service.execute(old_issue, new_project) }
context 'user is reporter in both projects' do
include_context 'user can move issue'
it { expect { move }.to_not raise_error }
end
context 'user is reporter only in new project' do
before { new_project.team << [user, :reporter] }
it { expect { move }.to raise_error(StandardError, /permissions/) }
end
context 'user is reporter only in old project' do
before { old_project.team << [user, :reporter] }
it { expect { move }.to raise_error(StandardError, /permissions/) }
end
context 'user is reporter in one project and guest in another' do
before do
new_project.team << [user, :guest]
old_project.team << [user, :reporter]
end
it { expect { move }.to raise_error(StandardError, /permissions/) }
end
context 'issue has already been moved' do
include_context 'user can move issue'
let(:moved_to_issue) { create(:issue) }
let(:old_issue) do
create(:issue, project: old_project, author: author,
moved_to: moved_to_issue)
end
it { expect { move }.to raise_error(StandardError, /permissions/) }
end
end
end
end
......@@ -453,6 +453,59 @@ describe SystemNoteService, services: true do
end
end
describe '.noteable_moved' do
let(:new_project) { create(:project) }
let(:new_noteable) { create(:issue, project: new_project) }
subject do
described_class.noteable_moved(noteable, project, new_noteable, author, direction: direction)
end
shared_examples 'cross project mentionable' do
include GitlabMarkdownHelper
it 'should contain cross reference to new noteable' do
expect(subject.note).to include cross_project_reference(new_project, new_noteable)
end
it 'should mention referenced noteable' do
expect(subject.note).to include new_noteable.to_reference
end
it 'should mention referenced project' do
expect(subject.note).to include new_project.to_reference
end
end
context 'moved to' do
let(:direction) { :to }
it_behaves_like 'cross project mentionable'
it 'should notify about noteable being moved to' do
expect(subject.note).to match /Moved to/
end
end
context 'moved from' do
let(:direction) { :from }
it_behaves_like 'cross project mentionable'
it 'should notify about noteable being moved from' do
expect(subject.note).to match /Moved from/
end
end
context 'invalid direction' do
let(:direction) { :invalid }
it 'should raise error' do
expect { subject }.to raise_error StandardError, /Invalid direction/
end
end
end
include JiraServiceHelper
describe 'JIRA integration' do
......
......@@ -3,12 +3,17 @@ require 'spec_helper'
describe RepositoryForkWorker do
let(:project) { create(:project) }
let(:fork_project) { create(:project, forked_from_project: project) }
let(:shell) { Gitlab::Shell.new }
subject { RepositoryForkWorker.new }
before do
allow(subject).to receive(:gitlab_shell).and_return(shell)
end
describe "#perform" do
it "creates a new repository from a fork" do
expect_any_instance_of(Gitlab::Shell).to receive(:fork_repository).with(
expect(shell).to receive(:fork_repository).with(
project.path_with_namespace,
fork_project.namespace.path
).and_return(true)
......@@ -19,20 +24,26 @@ describe RepositoryForkWorker do
fork_project.namespace.path)
end
it 'flushes the empty caches' do
expect_any_instance_of(Gitlab::Shell).to receive(:fork_repository).
it 'flushes various caches' do
expect(shell).to receive(:fork_repository).
with(project.path_with_namespace, fork_project.namespace.path).
and_return(true)
expect_any_instance_of(Repository).to receive(:expire_emptiness_caches).
and_call_original
expect_any_instance_of(Repository).to receive(:expire_exists_cache).
and_call_original
subject.perform(project.id, project.path_with_namespace,
fork_project.namespace.path)
end
it "handles bad fork" do
expect_any_instance_of(Gitlab::Shell).to receive(:fork_repository).and_return(false)
expect(shell).to receive(:fork_repository).and_return(false)
expect(subject.logger).to receive(:error)
subject.perform(
project.id,
project.path_with_namespace,
......
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