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: Style/AccessModifierIndentation:
Description: Check indentation of private/protected visibility modifiers.
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#indent-public-private-protected'
Enabled: true Enabled: true
# Check the naming of accessor methods for get_/set_.
Style/AccessorMethodName: Style/AccessorMethodName:
Description: Check the naming of accessor methods for get_/set_.
Enabled: false Enabled: false
# Use alias_method instead of alias.
Style/Alias: Style/Alias:
Description: 'Use alias_method instead of alias.' EnforcedStyle: prefer_alias_method
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#alias-method'
Enabled: true Enabled: true
# Align the elements of an array literal if they span more than one line.
Style/AlignArray: 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 Enabled: true
# Align the elements of a hash literal if they span more than one line.
Style/AlignHash: Style/AlignHash:
Description: >-
Align the elements of a hash literal if they span more than
one line.
Enabled: true Enabled: true
# Align the parameters of a method call if they span more than one line.
Style/AlignParameters: 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 Enabled: false
# Use &&/|| instead of and/or.
Style/AndOr: Style/AndOr:
Description: 'Use &&/|| instead of and/or.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-and-or-or'
Enabled: false Enabled: false
# Use `Array#join` instead of `Array#*`.
Style/ArrayJoin: Style/ArrayJoin:
Description: 'Use Array#join instead of Array#*.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#array-join'
Enabled: false Enabled: false
# Use only ascii symbols in comments.
Style/AsciiComments: Style/AsciiComments:
Description: 'Use only ascii symbols in comments.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#english-comments'
Enabled: true Enabled: true
# Use only ascii symbols in identifiers.
Style/AsciiIdentifiers: Style/AsciiIdentifiers:
Description: 'Use only ascii symbols in identifiers.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#english-identifiers'
Enabled: true Enabled: true
# Checks for uses of Module#attr.
Style/Attr: Style/Attr:
Description: 'Checks for uses of Module#attr.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#attr'
Enabled: false Enabled: false
# Avoid the use of BEGIN blocks.
Style/BeginBlock: Style/BeginBlock:
Description: 'Avoid the use of BEGIN blocks.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-BEGIN-blocks'
Enabled: true Enabled: true
# Checks if usage of %() or %Q() matches configuration.
Style/BarePercentLiterals: Style/BarePercentLiterals:
Description: 'Checks if usage of %() or %Q() matches configuration.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#percent-q-shorthand'
Enabled: false Enabled: false
# Do not use block comments.
Style/BlockComments: Style/BlockComments:
Description: 'Do not use block comments.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-block-comments'
Enabled: false Enabled: false
# Put end statement of multiline block on its own line.
Style/BlockEndNewline: Style/BlockEndNewline:
Description: 'Put end statement of multiline block on its own line.'
Enabled: true Enabled: true
# Avoid using {...} for multi-line blocks (multiline chaining is # always
# ugly). Prefer {...} over do...end for single-line blocks.
Style/BlockDelimiters: 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 Enabled: true
# Enforce braces style around hash parameters.
Style/BracesAroundHashParameters: Style/BracesAroundHashParameters:
Description: 'Enforce braces style around hash parameters.'
Enabled: false Enabled: false
# Avoid explicit use of the case equality operator(===).
Style/CaseEquality: Style/CaseEquality:
Description: 'Avoid explicit use of the case equality operator(===).'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-case-equality'
Enabled: false Enabled: false
# Indentation of when in a case/when/[else/]end.
Style/CaseIndentation: 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 Enabled: true
# Checks for uses of character literals.
Style/CharacterLiteral: Style/CharacterLiteral:
Description: 'Checks for uses of character literals.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-character-literals'
Enabled: true Enabled: true
# Use CamelCase for classes and modules.'
Style/ClassAndModuleCamelCase: Style/ClassAndModuleCamelCase:
Description: 'Use CamelCase for classes and modules.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#camelcase-classes'
Enabled: true Enabled: true
# Checks style of children classes and modules.
Style/ClassAndModuleChildren: Style/ClassAndModuleChildren:
Description: 'Checks style of children classes and modules.'
Enabled: false Enabled: false
# Enforces consistent use of `Object#is_a?` or `Object#kind_of?`.
Style/ClassCheck: Style/ClassCheck:
Description: 'Enforces consistent use of `Object#is_a?` or `Object#kind_of?`.'
Enabled: false Enabled: false
# Use self when defining module/class methods.
Style/ClassMethods: Style/ClassMethods:
Description: 'Use self when defining module/class methods.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#def-self-singletons'
Enabled: false Enabled: false
# Avoid the use of class variables.
Style/ClassVars: Style/ClassVars:
Description: 'Avoid the use of class variables.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-class-vars'
Enabled: true Enabled: true
# Do not use :: for method call.
Style/ColonMethodCall: Style/ColonMethodCall:
Description: 'Do not use :: for method call.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#double-colons'
Enabled: false Enabled: false
# Checks formatting of special comments (TODO, FIXME, OPTIMIZE, HACK, REVIEW).
Style/CommentAnnotation: 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 Enabled: false
# Indentation of comments.
Style/CommentIndentation: Style/CommentIndentation:
Description: 'Indentation of comments.'
Enabled: true 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: Style/ConstantName:
Description: 'Constants should use SCREAMING_SNAKE_CASE.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#screaming-snake-case'
Enabled: true Enabled: true
# Use def with parentheses when there are arguments.
Style/DefWithParentheses: Style/DefWithParentheses:
Description: 'Use def with parentheses when there are arguments.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#method-parens'
Enabled: false Enabled: false
# Checks for use of deprecated Hash methods.
Style/DeprecatedHashMethods: Style/DeprecatedHashMethods:
Description: 'Checks for use of deprecated Hash methods.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#hash-key'
Enabled: false Enabled: false
# Document classes and non-namespace modules.
Style/Documentation: Style/Documentation:
Description: 'Document classes and non-namespace modules.'
Enabled: false Enabled: false
# Checks the position of the dot in multi-line method calls.
Style/DotPosition: 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 Enabled: false
# Checks for uses of double negation (!!).
Style/DoubleNegation: Style/DoubleNegation:
Description: 'Checks for uses of double negation (!!).'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-bang-bang'
Enabled: false Enabled: false
# Prefer `each_with_object` over `inject` or `reduce`.
Style/EachWithObject: Style/EachWithObject:
Description: 'Prefer `each_with_object` over `inject` or `reduce`.'
Enabled: false Enabled: false
# Align elses and elsifs correctly.
Style/ElseAlignment: Style/ElseAlignment:
Description: 'Align elses and elsifs correctly.'
Enabled: true Enabled: true
# Avoid empty else-clauses.
Style/EmptyElse: Style/EmptyElse:
Description: 'Avoid empty else-clauses.'
Enabled: false Enabled: false
# Use empty lines between defs.
Style/EmptyLineBetweenDefs: Style/EmptyLineBetweenDefs:
Description: 'Use empty lines between defs.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#empty-lines-between-methods'
Enabled: false Enabled: false
# Don't use several empty lines in a row.
Style/EmptyLines: Style/EmptyLines:
Description: "Don't use several empty lines in a row."
Enabled: false Enabled: false
# Keep blank lines around access modifiers.
Style/EmptyLinesAroundAccessModifier: Style/EmptyLinesAroundAccessModifier:
Description: "Keep blank lines around access modifiers."
Enabled: false Enabled: false
# Keeps track of empty lines around block bodies.
Style/EmptyLinesAroundBlockBody: Style/EmptyLinesAroundBlockBody:
Description: "Keeps track of empty lines around block bodies."
Enabled: false Enabled: false
# Keeps track of empty lines around class bodies.
Style/EmptyLinesAroundClassBody: Style/EmptyLinesAroundClassBody:
Description: "Keeps track of empty lines around class bodies."
Enabled: false Enabled: false
# Keeps track of empty lines around module bodies.
Style/EmptyLinesAroundModuleBody: Style/EmptyLinesAroundModuleBody:
Description: "Keeps track of empty lines around module bodies."
Enabled: false Enabled: false
# Keeps track of empty lines around method bodies.
Style/EmptyLinesAroundMethodBody: Style/EmptyLinesAroundMethodBody:
Description: "Keeps track of empty lines around method bodies."
Enabled: false Enabled: false
# Prefer literals to Array.new/Hash.new/String.new.
Style/EmptyLiteral: 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 Enabled: false
# Avoid the use of END blocks.
Style/EndBlock: Style/EndBlock:
Description: 'Avoid the use of END blocks.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-END-blocks'
Enabled: false Enabled: false
# Use Unix-style line endings.
Style/EndOfLine: Style/EndOfLine:
Description: 'Use Unix-style line endings.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#crlf'
Enabled: false Enabled: false
# Favor the use of Fixnum#even? && Fixnum#odd?
Style/EvenOdd: Style/EvenOdd:
Description: 'Favor the use of Fixnum#even? && Fixnum#odd?'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#predicate-methods'
Enabled: false Enabled: false
# Do not use unnecessary spacing.
Style/ExtraSpacing: Style/ExtraSpacing:
Description: 'Do not use unnecessary spacing.'
Enabled: false Enabled: false
# Use snake_case for source file names.
Style/FileName: Style/FileName:
Description: 'Use snake_case for source file names.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#snake-case-files'
Enabled: false Enabled: false
# Checks for flip flops.
Style/FlipFlop: Style/FlipFlop:
Description: 'Checks for flip flops'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-flip-flops'
Enabled: false Enabled: false
# Checks use of for or each in multiline loops.
Style/For: 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 Enabled: false
# Enforce the use of Kernel#sprintf, Kernel#format or String#%.
Style/FormatString: Style/FormatString:
Description: 'Enforce the use of Kernel#sprintf, Kernel#format or String#%.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#sprintf'
Enabled: false Enabled: false
# Do not introduce global variables.
Style/GlobalVars: Style/GlobalVars:
Description: 'Do not introduce global variables.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#instance-vars'
Enabled: false Enabled: false
# Check for conditionals that can be replaced with guard clauses.
Style/GuardClause: 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 Enabled: false
# Prefer Ruby 1.9 hash syntax `{ a: 1, b: 2 }`
# over 1.8 syntax `{ :a => 1, :b => 2 }`.
Style/HashSyntax: 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 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: 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 Enabled: false
# Do not use if x; .... Use the ternary operator instead.
Style/IfWithSemicolon: 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 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: Style/IndentationConsistency:
Description: 'Keep indentation straight.'
Enabled: true Enabled: true
# Use 2 spaces for indentation.
Style/IndentationWidth: Style/IndentationWidth:
Description: 'Use 2 spaces for indentation.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-indentation'
Enabled: true Enabled: true
# Checks the indentation of the first element in an array literal.
Style/IndentArray: Style/IndentArray:
Description: >-
Checks the indentation of the first element in an array
literal.
Enabled: false Enabled: false
# Checks the indentation of the first key in a hash literal.
Style/IndentHash: Style/IndentHash:
Description: 'Checks the indentation of the first key in a hash literal.'
Enabled: false Enabled: false
# Use Kernel#loop for infinite loops.
Style/InfiniteLoop: Style/InfiniteLoop:
Description: 'Use Kernel#loop for infinite loops.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#infinite-loop'
Enabled: false Enabled: false
# Use the new lambda literal syntax for single-line blocks.
Style/Lambda: 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 Enabled: false
# Use lambda.call(...) instead of lambda.(...).
Style/LambdaCall: Style/LambdaCall:
Description: 'Use lambda.call(...) instead of lambda.(...).'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#proc-call'
Enabled: false Enabled: false
# Comments should start with a space.
Style/LeadingCommentSpace: Style/LeadingCommentSpace:
Description: 'Comments should start with a space.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#hash-space'
Enabled: false Enabled: false
# Use \ instead of + or << to concatenate two string literals at line end.
Style/LineEndConcatenation: Style/LineEndConcatenation:
Description: >-
Use \ instead of + or << to concatenate two string literals at
line end.
Enabled: false Enabled: false
# Do not use parentheses for method calls with no arguments.
Style/MethodCallParentheses: 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 Enabled: false
# Checks if the method definitions have or don't have parentheses.
Style/MethodDefParentheses: 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 Enabled: false
# Use the configured style when naming methods.
Style/MethodName: 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 Enabled: false
# Checks for usage of `extend self` in modules.
Style/ModuleFunction: Style/ModuleFunction:
Description: 'Checks for usage of `extend self` in modules.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#module-function'
Enabled: false Enabled: false
# Avoid multi-line chains of blocks.
Style/MultilineBlockChain: Style/MultilineBlockChain:
Description: 'Avoid multi-line chains of blocks.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#single-line-blocks'
Enabled: false Enabled: false
# Ensures newlines after multiline block do statements.
Style/MultilineBlockLayout: Style/MultilineBlockLayout:
Description: 'Ensures newlines after multiline block do statements.'
Enabled: true Enabled: true
# Do not use then for multi-line if/unless.
Style/MultilineIfThen: Style/MultilineIfThen:
Description: 'Do not use then for multi-line if/unless.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-then'
Enabled: false 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: Style/MultilineOperationIndentation:
Description: >-
Checks indentation of binary operations that span more than
one line.
Enabled: false Enabled: false
# Avoid multi-line `? :` (the ternary operator), use if/unless instead.
Style/MultilineTernaryOperator: 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 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: 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 Enabled: false
# Favor until over while for negative conditions.
Style/NegatedWhile: Style/NegatedWhile:
Description: 'Favor until over while for negative conditions.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#until-for-negatives'
Enabled: false 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: 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 Enabled: true
# Use `next` to skip iteration instead of a condition at the end.
Style/Next: 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 Enabled: false
# Prefer x.nil? to x == nil.
Style/NilComparison: Style/NilComparison:
Description: 'Prefer x.nil? to x == nil.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#predicate-methods'
Enabled: true Enabled: true
# Checks for redundant nil checks.
Style/NonNilCheck: Style/NonNilCheck:
Description: 'Checks for redundant nil checks.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-non-nil-checks'
Enabled: true Enabled: true
# Use ! instead of not.
Style/Not: Style/Not:
Description: 'Use ! instead of not.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#bang-not-not'
Enabled: true Enabled: true
# Add underscores to large numeric literals to improve their readability.
Style/NumericLiterals: 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 Enabled: false
# Favor the ternary operator(?:) over if/then/else/end constructs.
Style/OneLineConditional: 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 Enabled: true
# When defining binary operators, name the argument other.
Style/OpMethod: Style/OpMethod:
Description: 'When defining binary operators, name the argument other.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#other-arg'
Enabled: false 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: 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 Enabled: false
# Don't use parentheses around the condition of an if/unless/while.
Style/ParenthesesAroundCondition: 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 Enabled: true
# Use `%`-literal delimiters consistently.
Style/PercentLiteralDelimiters: Style/PercentLiteralDelimiters:
Description: 'Use `%`-literal delimiters consistently'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#percent-literal-braces'
Enabled: false Enabled: false
# Checks if uses of %Q/%q match the configured preference.
Style/PercentQLiterals: Style/PercentQLiterals:
Description: 'Checks if uses of %Q/%q match the configured preference.'
Enabled: false Enabled: false
# Avoid Perl-style regex back references.
Style/PerlBackrefs: Style/PerlBackrefs:
Description: 'Avoid Perl-style regex back references.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-perl-regexp-last-matchers'
Enabled: false Enabled: false
# Check the names of predicate methods.
Style/PredicateName: Style/PredicateName:
Description: 'Check the names of predicate methods.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#bool-methods-qmark'
Enabled: false Enabled: false
# Use proc instead of Proc.new.
Style/Proc: Style/Proc:
Description: 'Use proc instead of Proc.new.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#proc'
Enabled: false Enabled: false
# Checks the arguments passed to raise/fail.
Style/RaiseArgs: Style/RaiseArgs:
Description: 'Checks the arguments passed to raise/fail.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#exception-class-messages'
Enabled: false Enabled: false
# Don't use begin blocks when they are not needed.
Style/RedundantBegin: 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 Enabled: false
# Checks for an obsolete RuntimeException argument in raise/fail.
Style/RedundantException: 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 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: 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 Enabled: true
# Don't use self where it's not needed.
Style/RedundantSelf: 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 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: 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 Enabled: false
# Avoid using rescue in its modifier form.
Style/RescueModifier: Style/RescueModifier:
Description: 'Avoid using rescue in its modifier form.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-rescue-modifiers'
Enabled: false Enabled: false
# Checks for places where self-assignment shorthand should have been used.
Style/SelfAssignment: 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 Enabled: false
# Don't use semicolons to terminate expressions.
Style/Semicolon: Style/Semicolon:
Description: "Don't use semicolons to terminate expressions."
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-semicolon'
Enabled: false Enabled: false
# Checks for proper usage of fail and raise.
Style/SignalException: Style/SignalException:
Description: 'Checks for proper usage of fail and raise.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#fail-method'
Enabled: false Enabled: false
# Enforces the names of some block params.
Style/SingleLineBlockParams: Style/SingleLineBlockParams:
Description: 'Enforces the names of some block params.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#reduce-blocks'
Enabled: false Enabled: false
# Avoid single-line methods.
Style/SingleLineMethods: 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 Enabled: false
# Use spaces after colons.
Style/SpaceAfterColon: Style/SpaceAfterColon:
Description: 'Use spaces after colons.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-operators'
Enabled: false Enabled: false
# Use spaces after commas.
Style/SpaceAfterComma: 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 Enabled: false
# Do not put a space between a method name and the opening parenthesis in a
# method definition.
Style/SpaceAfterMethodName: 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 Enabled: false
# Tracks redundant space after the ! operator.
Style/SpaceAfterNot: Style/SpaceAfterNot:
Description: Tracks redundant space after the ! operator.
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-space-bang'
Enabled: false Enabled: false
# Use spaces after semicolons.
Style/SpaceAfterSemicolon: Style/SpaceAfterSemicolon:
Description: 'Use spaces after semicolons.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-operators'
Enabled: false Enabled: false
Style/SpaceBeforeBlockBraces: # Checks that the equals signs in parameter default assignments have or don't
Description: >- # have surrounding space depending on configuration.
Checks that the left block brace has or doesn't have space Style/SpaceAroundEqualsInParameterDefault:
before it.
Enabled: false Enabled: false
Style/SpaceBeforeComma: # TODO: Enable SpaceAroundKeyword Cop.
Description: 'No spaces before commas.' # Use a space around keywords if appropriate.
Style/SpaceAroundKeyword:
Enabled: false Enabled: false
Style/SpaceBeforeComment: # Use a single space around operators.
Description: >- Style/SpaceAroundOperators:
Checks for missing space between code and a comment on the
same line.
Enabled: false Enabled: false
Style/SpaceBeforeSemicolon: # Checks that the left block brace has or doesn't have space before it.
Description: 'No spaces before semicolons.' Style/SpaceBeforeBlockBraces:
Enabled: false Enabled: false
Style/SpaceInsideBlockBraces: # No spaces before commas.
Description: >- Style/SpaceBeforeComma:
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.
Enabled: false Enabled: false
Style/SpaceAroundEqualsInParameterDefault: # Checks for missing space between code and a comment on the same line.
Description: >- Style/SpaceBeforeComment:
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'
Enabled: false Enabled: false
Style/SpaceAroundOperators: # Checks that exactly one space is used between a method name and the first
Description: 'Use spaces around operators.' # argument for method calls without parentheses.
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-operators' Style/SpaceBeforeFirstArg:
Enabled: false
# No spaces before semicolons.
Style/SpaceBeforeSemicolon:
Enabled: false Enabled: false
Style/SpaceBeforeModifierKeyword: # Checks that block braces have or don't have surrounding space.
Description: 'Put a space before the modifier keyword.' # For blocks taking parameters, checks that the left brace has or doesn't
# have trailing space.
Style/SpaceInsideBlockBraces:
Enabled: false Enabled: false
# No spaces after [ or before ].
Style/SpaceInsideBrackets: Style/SpaceInsideBrackets:
Description: 'No spaces after [ or before ].'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-spaces-braces'
Enabled: false Enabled: false
# Use spaces inside hash literal braces - or don't.
Style/SpaceInsideHashLiteralBraces: Style/SpaceInsideHashLiteralBraces:
Description: "Use spaces inside hash literal braces - or don't."
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-operators'
Enabled: true Enabled: true
# No spaces after ( or before ).
Style/SpaceInsideParens: Style/SpaceInsideParens:
Description: 'No spaces after ( or before ).'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-spaces-braces'
Enabled: false Enabled: false
# No spaces inside range literals.
Style/SpaceInsideRangeLiteral: Style/SpaceInsideRangeLiteral:
Description: 'No spaces inside range literals.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-space-inside-range-literals'
Enabled: false Enabled: false
# Checks for padding/surrounding spaces inside string interpolation.
Style/SpaceInsideStringInterpolation:
Enabled: false
# Avoid Perl-style global variables.
Style/SpecialGlobalVars: Style/SpecialGlobalVars:
Description: 'Avoid Perl-style global variables.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-cryptic-perlisms'
Enabled: false 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: 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 Enabled: false
# Checks if uses of quotes inside expressions in interpolated strings match the
# configured preference.
Style/StringLiteralsInInterpolation: Style/StringLiteralsInInterpolation:
Description: >-
Checks if uses of quotes inside expressions in interpolated
strings match the configured preference.
Enabled: false 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: Style/SymbolProc:
Description: 'Use symbols as procs instead of blocks when possible.'
Enabled: false Enabled: false
# No hard tabs.
Style/Tab: Style/Tab:
Description: 'No hard tabs.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-indentation'
Enabled: true Enabled: true
# Checks trailing blank lines and final newline.
Style/TrailingBlankLines: Style/TrailingBlankLines:
Description: 'Checks trailing blank lines and final newline.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#newline-eof'
Enabled: true Enabled: true
Style/TrailingComma: # Checks for trailing comma in array and hash literals.
Description: 'Checks for trailing comma in parameter lists and literals.' Style/TrailingCommaInLiteral:
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-trailing-array-commas'
Enabled: false Enabled: false
# Checks for trailing comma in argument lists.
Style/TrailingCommaInArguments:
Enabled: false
# Avoid trailing whitespace.
Style/TrailingWhitespace: Style/TrailingWhitespace:
Description: 'Avoid trailing whitespace.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-trailing-whitespace'
Enabled: false Enabled: false
# Checks for the usage of unneeded trailing underscores at the end of
# parallel variable assignment.
Style/TrailingUnderscoreVariable: Style/TrailingUnderscoreVariable:
Description: >-
Checks for the usage of unneeded trailing underscores at the
end of parallel variable assignment.
AllowNamedUnderscoreVariables: true
Enabled: false Enabled: false
# Prefer attr_* methods to trivial readers/writers.
Style/TrivialAccessors: Style/TrivialAccessors:
Description: 'Prefer attr_* methods to trivial readers/writers.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#attr_family'
Enabled: false Enabled: false
# Do not use unless with else. Rewrite these with the positive case first.
Style/UnlessElse: 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 Enabled: false
# Checks for %W when interpolation is not needed.
Style/UnneededCapitalW: Style/UnneededCapitalW:
Description: 'Checks for %W when interpolation is not needed.'
Enabled: false 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: 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 Enabled: false
# Don't interpolate global, instance and class variables directly in strings.
Style/VariableInterpolation: 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 Enabled: false
# Use the configured style when naming variables.
Style/VariableName: 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 Enabled: false
# Use when x then ... for one-line cases.
Style/WhenThen: Style/WhenThen:
Description: 'Use when x then ... for one-line cases.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#one-line-cases'
Enabled: false Enabled: false
# Checks for redundant do after while or until.
Style/WhileUntilDo: 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 Enabled: false
# Favor modifier while/until usage when you have a single-line body.
Style/WhileUntilModifier: 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 Enabled: false
# Use %w or %W for arrays of words.
Style/WordArray: Style/WordArray:
Description: 'Use %w or %W for arrays of words.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#percent-w'
Enabled: false Enabled: false
# TODO: Enable ZeroLengthPredicate Cop.
# Use #empty? when testing for objects of length 0.
Style/ZeroLengthPredicate:
Enabled: false
#################### Metrics ################################ #################### Metrics ################################
# A calculated magnitude based on number of assignments,
# branches, and conditions.
Metrics/AbcSize: Metrics/AbcSize:
Description: >-
A calculated magnitude based on number of assignments,
branches, and conditions.
Enabled: true Enabled: true
Max: 70 Max: 70
Metrics/CyclomaticComplexity: # Avoid excessive block nesting.
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
Metrics/BlockNesting: Metrics/BlockNesting:
Description: 'Avoid excessive block nesting'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#three-is-the-number-thou-shalt-count'
Enabled: true Enabled: true
Max: 4 Max: 4
# Avoid classes longer than 100 lines of code.
Metrics/ClassLength: Metrics/ClassLength:
Description: 'Avoid classes longer than 100 lines of code.'
Enabled: false 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: Metrics/LineLength:
Description: 'Limit lines to 80 characters.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#80-character-limits'
Enabled: false Enabled: false
# Avoid methods longer than 10 lines of code.
Metrics/MethodLength: Metrics/MethodLength:
Description: 'Avoid methods longer than 10 lines of code.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#short-methods'
Enabled: false Enabled: false
# Avoid modules longer than 100 lines of code.
Metrics/ModuleLength: Metrics/ModuleLength:
Description: 'Avoid modules longer than 100 lines of code.'
Enabled: false 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 ################################ #################### Lint ################################
### Warnings
# Checks for ambiguous operators in the first argument of a method invocation
# without parentheses.
Lint/AmbiguousOperator: 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 Enabled: false
# Checks for ambiguous regexp literals in the first argument of a method
# invocation without parentheses.
Lint/AmbiguousRegexpLiteral: Lint/AmbiguousRegexpLiteral:
Description: >-
Checks for ambiguous regexp literals in the first argument of
a method invocation without parenthesis.
Enabled: false Enabled: false
# Don't use assignment in conditions.
Lint/AssignmentInCondition: Lint/AssignmentInCondition:
Description: "Don't use assignment in conditions."
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#safe-assignment-in-condition'
Enabled: false Enabled: false
# Align block ends correctly.
Lint/BlockAlignment: Lint/BlockAlignment:
Description: 'Align block ends correctly.'
Enabled: false 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: 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 Enabled: false
# Check for debugger calls.
Lint/Debugger: Lint/Debugger:
Description: 'Check for debugger calls.'
Enabled: false Enabled: false
# Align ends corresponding to defs correctly.
Lint/DefEndAlignment: Lint/DefEndAlignment:
Description: 'Align ends corresponding to defs correctly.'
Enabled: false Enabled: false
# Check for deprecated class method calls.
Lint/DeprecatedClassMethods: Lint/DeprecatedClassMethods:
Description: 'Check for deprecated class method calls.'
Enabled: false 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: Lint/ElseLayout:
Description: 'Check for odd code arrangement in an else block.'
Enabled: false Enabled: false
# Checks for empty ensure block.
Lint/EmptyEnsure: Lint/EmptyEnsure:
Description: 'Checks for empty ensure block.'
Enabled: false Enabled: false
# Checks for empty string interpolation.
Lint/EmptyInterpolation: Lint/EmptyInterpolation:
Description: 'Checks for empty string interpolation.'
Enabled: false Enabled: false
# Align ends correctly.
Lint/EndAlignment: Lint/EndAlignment:
Description: 'Align ends correctly.'
Enabled: false Enabled: false
# END blocks should not be placed inside method definitions.
Lint/EndInMethod: Lint/EndInMethod:
Description: 'END blocks should not be placed inside method definitions.'
Enabled: false Enabled: false
# Do not use return in an ensure block.
Lint/EnsureReturn: Lint/EnsureReturn:
Description: 'Do not use return in an ensure block.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-return-ensure'
Enabled: false Enabled: false
# The use of eval represents a serious security risk.
Lint/Eval: Lint/Eval:
Description: 'The use of eval represents a serious security risk.'
Enabled: false 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: Lint/HandleExceptions:
Description: "Don't suppress exception."
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#dont-hide-exceptions'
Enabled: false 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: Lint/InvalidCharacterLiteral:
Description: >-
Checks for invalid character literals with a non-escaped
whitespace character.
Enabled: false Enabled: false
# Checks of literals used in conditions.
Lint/LiteralInCondition: Lint/LiteralInCondition:
Description: 'Checks of literals used in conditions.'
Enabled: false Enabled: false
# Checks for literals used in interpolation.
Lint/LiteralInInterpolation: Lint/LiteralInInterpolation:
Description: 'Checks for literals used in interpolation.'
Enabled: false Enabled: false
# Use Kernel#loop with break rather than begin/end/until or begin/end/while
# for post-loop tests.
Lint/Loop: 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 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: 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 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: Lint/RequireParentheses:
Description: >-
Use parentheses in the method call to avoid confusion
about precedence.
Enabled: false Enabled: false
# Avoid rescuing the Exception class.
Lint/RescueException: Lint/RescueException:
Description: 'Avoid rescuing the Exception class.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-blind-rescues'
Enabled: true Enabled: true
# Do not use the same name as outer local variable for block arguments
# or block local variables.
Lint/ShadowingOuterLocalVariable: 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 Enabled: false
# 'Checks for Object#to_s usage in string interpolation.
Lint/StringConversionInInterpolation: 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 Enabled: false
# Do not use prefix `_` for a variable that is used.
Lint/UnderscorePrefixedVariableName: Lint/UnderscorePrefixedVariableName:
Description: 'Do not use prefix `_` for a variable that is used.'
Enabled: true 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: Lint/UnusedBlockArgument:
Description: 'Checks for unused block arguments.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#underscore-unused-vars'
Enabled: false Enabled: false
# Checks for unused method arguments.
Lint/UnusedMethodArgument: Lint/UnusedMethodArgument:
Description: 'Checks for unused method arguments.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#underscore-unused-vars'
Enabled: false Enabled: false
# Unreachable code.
Lint/UnreachableCode: Lint/UnreachableCode:
Description: 'Unreachable code.'
Enabled: false Enabled: false
# Checks for useless access modifiers.
Lint/UselessAccessModifier: Lint/UselessAccessModifier:
Description: 'Checks for useless access modifiers.'
Enabled: false Enabled: false
# Checks for useless assignment to a local variable.
Lint/UselessAssignment: Lint/UselessAssignment:
Description: 'Checks for useless assignment to a local variable.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#underscore-unused-vars'
Enabled: true Enabled: true
# Checks for comparison of something with itself.
Lint/UselessComparison: Lint/UselessComparison:
Description: 'Checks for comparison of something with itself.'
Enabled: false Enabled: false
# Checks for useless `else` in `begin..end` without `rescue`.
Lint/UselessElseWithoutRescue: Lint/UselessElseWithoutRescue:
Description: 'Checks for useless `else` in `begin..end` without `rescue`.'
Enabled: false Enabled: false
# Checks for useless setter call to a local variable.
Lint/UselessSetterCall: Lint/UselessSetterCall:
Description: 'Checks for useless setter call to a local variable.'
Enabled: false Enabled: false
# Possible use of operator/literal/variable in void context.
Lint/Void: Lint/Void:
Description: 'Possible use of operator/literal/variable in void context.'
Enabled: false 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 ################################## ##################### Rails ##################################
# Enables Rails cops.
Rails:
Enabled: true
# Enforces consistent use of action filter methods.
Rails/ActionFilter: Rails/ActionFilter:
Description: 'Enforces consistent use of action filter methods.'
Enabled: true Enabled: true
EnforcedStyle: action
# Checks the correct usage of date aware methods, such as `Date.today`,
# `Date.current`, etc.
Rails/Date: Rails/Date:
Description: >-
Checks the correct usage of date aware methods,
such as Date.today, Date.current etc.
Enabled: false Enabled: false
Rails/DefaultScope: # Prefer delegate method for delegations.
Description: 'Checks if the argument passed to default_scope is a block.' Rails/Delegate:
Enabled: false Enabled: false
Rails/Delegate: # Prefer `find_by` over `where.first`.
Description: 'Prefer delegate method for delegations.' Rails/FindBy:
Enabled: false
# Prefer `all.find_each` over `all.find`.
Rails/FindEach:
Enabled: false Enabled: false
# Prefer has_many :through to has_and_belongs_to_many.
Rails/HasAndBelongsToMany: Rails/HasAndBelongsToMany:
Description: 'Prefer has_many :through to has_and_belongs_to_many.'
Enabled: true Enabled: true
# Checks for calls to puts, print, etc.
Rails/Output: Rails/Output:
Description: 'Checks for calls to puts, print, etc.'
Enabled: true 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: Rails/ReadWriteAttribute:
Description: >-
Checks for read_attribute(:attr) and
write_attribute(:attr, val).
Enabled: false Enabled: false
# Checks the arguments of ActiveRecord scopes.
Rails/ScopeArgs: Rails/ScopeArgs:
Description: 'Checks the arguments of ActiveRecord scopes.'
Enabled: false Enabled: false
# Checks the correct usage of time zone aware methods.
# http://danilenko.org/2012/7/6/rails_timezones
Rails/TimeZone: 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 Enabled: false
# Use validates :attribute, hash of validations.
Rails/Validation: Rails/Validation:
Description: 'Use validates :attribute, hash of validations.'
Enabled: false 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. Please view this file on the master branch, on stable branches it's out of date.
v 8.6.0 (unreleased) 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) - 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 - Add confidential issues
- Bump gitlab_git to 9.0.3 (Stan Hu) - 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) - Support Golang subpackage fetching (Stan Hu)
- Bump Capybara gem to 2.6.2 (Stan Hu) - Bump Capybara gem to 2.6.2 (Stan Hu)
- New branch button appears on issues where applicable - New branch button appears on issues where applicable
...@@ -57,6 +61,7 @@ v 8.6.0 (unreleased) ...@@ -57,6 +61,7 @@ v 8.6.0 (unreleased)
- User deletion is now done in the background so the request can not time out - 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` - Canceled builds are now ignored in compound build status if marked as `allowed to fail`
- Trigger a todo for mentions on commits page - Trigger a todo for mentions on commits page
- Let project owners and admins soft delete issues and merge requests
v 8.5.8 v 8.5.8
- Bump Git version requirement to 2.7.4 - Bump Git version requirement to 2.7.4
......
...@@ -222,6 +222,8 @@ gem 'net-ssh', '~> 3.0.1' ...@@ -222,6 +222,8 @@ gem 'net-ssh', '~> 3.0.1'
# Sentry integration # Sentry integration
gem 'sentry-raven', '~> 0.15' gem 'sentry-raven', '~> 0.15'
gem 'premailer-rails', '~> 1.9.0'
# Metrics # Metrics
group :metrics do group :metrics do
gem 'allocations', '~> 1.0', require: false, platform: :mri gem 'allocations', '~> 1.0', require: false, platform: :mri
...@@ -285,7 +287,7 @@ group :development, :test do ...@@ -285,7 +287,7 @@ group :development, :test do
gem 'spring-commands-spinach', '~> 1.0.0' gem 'spring-commands-spinach', '~> 1.0.0'
gem 'spring-commands-teaspoon', '~> 0.0.2' 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 'scss_lint', '~> 0.47.0', require: false
gem 'coveralls', '~> 0.8.2', require: false gem 'coveralls', '~> 0.8.2', require: false
gem 'simplecov', '~> 0.10.0', require: false gem 'simplecov', '~> 0.10.0', require: false
......
...@@ -61,9 +61,7 @@ GEM ...@@ -61,9 +61,7 @@ GEM
faraday_middleware-multi_json (~> 0.0) faraday_middleware-multi_json (~> 0.0)
oauth2 (~> 1.0) oauth2 (~> 1.0)
asciidoctor (1.5.3) asciidoctor (1.5.3)
ast (2.1.0) ast (2.2.0)
astrolabe (1.3.1)
parser (~> 2.2)
attr_encrypted (1.3.4) attr_encrypted (1.3.4)
encryptor (>= 1.3.0) encryptor (>= 1.3.0)
attr_required (1.0.0) attr_required (1.0.0)
...@@ -150,6 +148,8 @@ GEM ...@@ -150,6 +148,8 @@ GEM
crack (0.4.3) crack (0.4.3)
safe_yaml (~> 1.0.0) safe_yaml (~> 1.0.0)
creole (0.5.0) creole (0.5.0)
css_parser (1.3.7)
addressable
d3_rails (3.5.11) d3_rails (3.5.11)
railties (>= 3.1.0) railties (>= 3.1.0)
daemons (1.2.3) daemons (1.2.3)
...@@ -423,6 +423,7 @@ GEM ...@@ -423,6 +423,7 @@ GEM
haml (~> 4.0.0) haml (~> 4.0.0)
nokogiri (~> 1.6.0) nokogiri (~> 1.6.0)
ruby_parser (~> 3.5) ruby_parser (~> 3.5)
htmlentities (4.3.4)
http-cookie (1.0.2) http-cookie (1.0.2)
domain_name (~> 0.5) domain_name (~> 0.5)
http_parser.rb (0.5.3) http_parser.rb (0.5.3)
...@@ -554,8 +555,8 @@ GEM ...@@ -554,8 +555,8 @@ GEM
orm_adapter (0.5.0) orm_adapter (0.5.0)
paranoia (2.1.4) paranoia (2.1.4)
activerecord (~> 4.0) activerecord (~> 4.0)
parser (2.2.3.0) parser (2.3.0.6)
ast (>= 1.1, < 3.0) ast (~> 2.2)
pg (0.18.4) pg (0.18.4)
poltergeist (1.9.0) poltergeist (1.9.0)
capybara (~> 2.1) capybara (~> 2.1)
...@@ -564,6 +565,12 @@ GEM ...@@ -564,6 +565,12 @@ GEM
websocket-driver (>= 0.2.0) websocket-driver (>= 0.2.0)
posix-spawn (0.3.11) posix-spawn (0.3.11)
powerpack (0.1.1) 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) pry (0.10.3)
coderay (~> 1.1.0) coderay (~> 1.1.0)
method_source (~> 0.8.1) method_source (~> 0.8.1)
...@@ -615,7 +622,7 @@ GEM ...@@ -615,7 +622,7 @@ GEM
activesupport (= 4.2.5.2) activesupport (= 4.2.5.2)
rake (>= 0.8.7) rake (>= 0.8.7)
thor (>= 0.18.1, < 2.0) thor (>= 0.18.1, < 2.0)
rainbow (2.0.0) rainbow (2.1.0)
raindrops (0.15.0) raindrops (0.15.0)
rake (10.5.0) rake (10.5.0)
raphael-rails (2.1.2) raphael-rails (2.1.2)
...@@ -687,13 +694,12 @@ GEM ...@@ -687,13 +694,12 @@ GEM
rspec-retry (0.4.5) rspec-retry (0.4.5)
rspec-core rspec-core
rspec-support (3.3.0) rspec-support (3.3.0)
rubocop (0.35.1) rubocop (0.38.0)
astrolabe (~> 1.3) parser (>= 2.3.0.6, < 3.0)
parser (>= 2.2.3.0, < 3.0)
powerpack (~> 0.1) powerpack (~> 0.1)
rainbow (>= 1.99.1, < 3.0) rainbow (>= 1.99.1, < 3.0)
ruby-progressbar (~> 1.7) ruby-progressbar (~> 1.7)
tins (<= 1.6.0) unicode-display_width (~> 1.0, >= 1.0.1)
ruby-fogbugz (0.2.1) ruby-fogbugz (0.2.1)
crack (~> 0.4) crack (~> 0.4)
ruby-progressbar (1.7.5) ruby-progressbar (1.7.5)
...@@ -843,6 +849,7 @@ GEM ...@@ -843,6 +849,7 @@ GEM
unf (0.1.4) unf (0.1.4)
unf_ext unf_ext
unf_ext (0.0.7.1) unf_ext (0.0.7.1)
unicode-display_width (1.0.2)
unicorn (4.9.0) unicorn (4.9.0)
kgio (~> 2.6) kgio (~> 2.6)
rack rack
...@@ -992,6 +999,7 @@ DEPENDENCIES ...@@ -992,6 +999,7 @@ DEPENDENCIES
paranoia (~> 2.0) paranoia (~> 2.0)
pg (~> 0.18.2) pg (~> 0.18.2)
poltergeist (~> 1.9.0) poltergeist (~> 1.9.0)
premailer-rails (~> 1.9.0)
pry-rails pry-rails
quiet_assets (~> 1.0.2) quiet_assets (~> 1.0.2)
rack-attack (~> 4.3.1) rack-attack (~> 4.3.1)
...@@ -1013,7 +1021,7 @@ DEPENDENCIES ...@@ -1013,7 +1021,7 @@ DEPENDENCIES
rqrcode-rails3 (~> 0.1.7) rqrcode-rails3 (~> 0.1.7)
rspec-rails (~> 3.3.0) rspec-rails (~> 3.3.0)
rspec-retry rspec-retry
rubocop (~> 0.35.0) rubocop (~> 0.38.0)
ruby-fogbugz (~> 0.2.1) ruby-fogbugz (~> 0.2.1)
sanitize (~> 2.0) sanitize (~> 2.0)
sass-rails (~> 5.0.0) sass-rails (~> 5.0.0)
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#= require jquery #= require jquery
#= require jquery-ui/autocomplete #= require jquery-ui/autocomplete
#= require jquery-ui/datepicker #= require jquery-ui/datepicker
#= require jquery-ui/draggable
#= require jquery-ui/effect-highlight #= require jquery-ui/effect-highlight
#= require jquery-ui/sortable #= require jquery-ui/sortable
#= require jquery_ujs #= require jquery_ujs
...@@ -138,7 +139,7 @@ $ -> ...@@ -138,7 +139,7 @@ $ ->
# Initialize tooltips # Initialize tooltips
$('body').tooltip( $('body').tooltip(
selector: '.has_tooltip, [data-toggle="tooltip"]' selector: '.has-tooltip, [data-toggle="tooltip"]'
placement: (_, el) -> placement: (_, el) ->
$el = $(el) $el = $(el)
$el.data('placement') || 'bottom' $el.data('placement') || 'bottom'
......
...@@ -5,7 +5,6 @@ class @Aside ...@@ -5,7 +5,6 @@ class @Aside
e.preventDefault() e.preventDefault()
btn = $(e.currentTarget) btn = $(e.currentTarget)
icon = btn.find('i') icon = btn.find('i')
console.log('1')
if icon.hasClass('fa-angle-left') if icon.hasClass('fa-angle-left')
btn.parent().find('section').hide() btn.parent().find('section').hide()
......
...@@ -122,7 +122,7 @@ class @AwardsHandler ...@@ -122,7 +122,7 @@ class @AwardsHandler
nodes = [] nodes = []
nodes.push( 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>", "<div class='icon emoji-icon #{emojiCssClass}' data-emoji='#{emoji}'></div>",
"<span class='award-control-text js-counter'>1</span>", "<span class='award-control-text js-counter'>1</span>",
"</button>" "</button>"
......
...@@ -167,7 +167,11 @@ class GitLabDropdown ...@@ -167,7 +167,11 @@ class GitLabDropdown
hidden: => hidden: =>
if @options.filterable 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 if @dropdown.find(".dropdown-toggle-page").length
$('.dropdown-menu', @dropdown).removeClass PAGE_TWO_CLASS $('.dropdown-menu', @dropdown).removeClass PAGE_TWO_CLASS
......
class @IssuableForm 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 wipRegex: /^\s*(\[WIP\]\s*|WIP:\s*|WIP\s+)+\s*/i
constructor: (@form) -> constructor: (@form) ->
GitLab.GfmAutoComplete.setup() GitLab.GfmAutoComplete.setup()
new UsersSelect() new UsersSelect()
...@@ -7,12 +9,13 @@ class @IssuableForm ...@@ -7,12 +9,13 @@ class @IssuableForm
@titleField = @form.find("input[name*='[title]']") @titleField = @form.find("input[name*='[title]']")
@descriptionField = @form.find("textarea[name*='[description]']") @descriptionField = @form.find("textarea[name*='[description]']")
@issueMoveField = @form.find("#move_to_project_id")
return unless @titleField.length && @descriptionField.length return unless @titleField.length && @descriptionField.length
@initAutosave() @initAutosave()
@form.on "submit", @resetAutosave @form.on "submit", @handleSubmit
@form.on "click", ".btn-cancel", @resetAutosave @form.on "click", ".btn-cancel", @resetAutosave
@initWip() @initWip()
...@@ -30,6 +33,12 @@ class @IssuableForm ...@@ -30,6 +33,12 @@ class @IssuableForm
"description" "description"
] ]
handleSubmit: =>
if (parseInt(@issueMoveField?.val()) ? 0) > 0
return false unless confirm(@issueMoveConfirmMsg)
@resetAutosave()
resetAutosave: => resetAutosave: =>
@titleField.data("autosave").reset() @titleField.data("autosave").reset()
@descriptionField.data("autosave").reset() @descriptionField.data("autosave").reset()
......
...@@ -3,6 +3,8 @@ ...@@ -3,6 +3,8 @@
# Handles persisting and restoring the current tab selection and lazily-loading # Handles persisting and restoring the current tab selection and lazily-loading
# content on the MergeRequests#show page. # content on the MergeRequests#show page.
# #
#= require jquery.cookie
#
# ### Example Markup # ### Example Markup
# #
# <ul class="nav-links merge-request-tabs"> # <ul class="nav-links merge-request-tabs">
...@@ -68,11 +70,15 @@ class @MergeRequestTabs ...@@ -68,11 +70,15 @@ class @MergeRequestTabs
if action == 'commits' if action == 'commits'
@loadCommits($target.attr('href')) @loadCommits($target.attr('href'))
@expandView()
else if action == 'diffs' else if action == 'diffs'
@loadDiff($target.attr('href')) @loadDiff($target.attr('href'))
@shrinkView() @shrinkView()
else if action == 'builds' else if action == 'builds'
@loadBuilds($target.attr('href')) @loadBuilds($target.attr('href'))
@expandView()
else
@expandView()
@setCurrentAction(action) @setCurrentAction(action)
...@@ -189,11 +195,24 @@ class @MergeRequestTabs ...@@ -189,11 +195,24 @@ class @MergeRequestTabs
$('.container-fluid').removeClass('container-limited') $('.container-fluid').removeClass('container-limited')
shrinkView: -> shrinkView: ->
$gutterIcon = $('.js-sidebar-toggle i') $gutterIcon = $('.js-sidebar-toggle i:visible')
# Wait until listeners are set # Wait until listeners are set
setTimeout( -> setTimeout( ->
# Only when sidebar is collapsed # Only when sidebar is expanded
if $gutterIcon.is('.fa-angle-double-right') 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) , 0)
...@@ -361,14 +361,12 @@ class @Notes ...@@ -361,14 +361,12 @@ class @Notes
showEditForm: (e) -> showEditForm: (e) ->
e.preventDefault() e.preventDefault()
note = $(this).closest(".note") note = $(this).closest(".note")
note.find(".note-body > .note-text").hide() note.addClass "is-editting"
note.find(".note-header").hide()
form = note.find(".note-edit-form") form = note.find(".note-edit-form")
isNewForm = form.is(':not(.gfm-form)') isNewForm = form.is(':not(.gfm-form)')
if isNewForm if isNewForm
form.addClass('gfm-form') form.addClass('gfm-form')
form.addClass('current-note-edit-form') form.addClass('current-note-edit-form')
form.show()
# Show the attachment delete link # Show the attachment delete link
note.find(".js-note-attachment-delete").show() note.find(".js-note-attachment-delete").show()
...@@ -402,11 +400,9 @@ class @Notes ...@@ -402,11 +400,9 @@ class @Notes
cancelEdit: (e) -> cancelEdit: (e) ->
e.preventDefault() e.preventDefault()
note = $(this).closest(".note") note = $(this).closest(".note")
note.find(".note-body > .note-text").show() note.removeClass "is-editting"
note.find(".note-header").show()
note.find(".current-note-edit-form") note.find(".current-note-edit-form")
.removeClass("current-note-edit-form") .removeClass("current-note-edit-form")
.hide()
### ###
Called in response to deleting a note of any kind. Called in response to deleting a note of any kind.
......
...@@ -11,7 +11,6 @@ class @Project ...@@ -11,7 +11,6 @@ class @Project
$(@).toggleClass('active') $(@).toggleClass('active')
url = $("#project_clone").val() url = $("#project_clone").val()
console.log("url",url)
# Update the input field # Update the input field
$('#project_clone').val(url) $('#project_clone').val(url)
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
} }
&.group-avatar, &.project-avatar, &.avatar-tile { &.group-avatar, &.project-avatar, &.avatar-tile {
@include border-radius(0px); @include border-radius(0);
} }
&.s16 { width: 16px; height: 16px; margin-right: 6px; } &.s16 { width: 16px; height: 16px; margin-right: 6px; }
......
...@@ -107,7 +107,7 @@ ...@@ -107,7 +107,7 @@
margin: 0; margin: 0;
font-size: 23px; font-size: 23px;
font-weight: normal; font-weight: normal;
margin: 16px 0 5px 0; margin: 16px 0 5px;
color: #4c4e54; color: #4c4e54;
font-size: 23px; font-size: 23px;
line-height: 1.1; line-height: 1.1;
......
...@@ -75,7 +75,7 @@ ...@@ -75,7 +75,7 @@
width: 240px; width: 240px;
margin-top: 2px; margin-top: 2px;
margin-bottom: 0; margin-bottom: 0;
padding: 10px 10px; padding: 10px;
font-size: 14px; font-size: 14px;
font-weight: normal; font-weight: normal;
background-color: $dropdown-bg; background-color: $dropdown-bg;
......
...@@ -16,7 +16,7 @@ body { ...@@ -16,7 +16,7 @@ body {
} }
.container .content { .container .content {
margin: 0 0; margin: 0;
} }
.navless-container { .navless-container {
......
/** /**
* Generic mixins * Generic mixins
*/ */
@mixin box-shadow($shadow) { @mixin box-shadow($shadow) {
-webkit-box-shadow: $shadow; -webkit-box-shadow: $shadow;
-moz-box-shadow: $shadow; -moz-box-shadow: $shadow;
-ms-box-shadow: $shadow; -ms-box-shadow: $shadow;
......
...@@ -41,7 +41,7 @@ ...@@ -41,7 +41,7 @@
} }
.select2-drop { .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); @include border-radius ($border-radius-default);
border: none; border: none;
} }
......
...@@ -39,8 +39,8 @@ ...@@ -39,8 +39,8 @@
h1 { h1 {
font-size: 1.3em; font-size: 1.3em;
font-weight: 600; font-weight: 600;
margin: 24px 0 12px 0; margin: 24px 0 12px;
padding: 0 0 10px 0; padding: 0 0 10px;
border-bottom: 1px solid #e7e9ed; border-bottom: 1px solid #e7e9ed;
color: #313236; color: #313236;
} }
...@@ -48,27 +48,27 @@ ...@@ -48,27 +48,27 @@
h2 { h2 {
font-size: 1.2em; font-size: 1.2em;
font-weight: 600; font-weight: 600;
margin: 24px 0 12px 0; margin: 24px 0 12px;
color: #313236; color: #313236;
} }
h3 { h3 {
margin: 24px 0 12px 0; margin: 24px 0 12px;
font-size: 1.1em; font-size: 1.1em;
} }
h4 { h4 {
margin: 24px 0 12px 0; margin: 24px 0 12px;
font-size: 0.98em; font-size: 0.98em;
} }
h5 { h5 {
margin: 24px 0 12px 0; margin: 24px 0 12px;
font-size: 0.95em; font-size: 0.95em;
} }
h6 { h6 {
margin: 24px 0 12px 0; margin: 24px 0 12px;
font-size: 0.90em; font-size: 0.90em;
} }
...@@ -76,7 +76,7 @@ ...@@ -76,7 +76,7 @@
color: #7f8fa4; color: #7f8fa4;
font-size: inherit; font-size: inherit;
padding: 8px 21px; padding: 8px 21px;
margin: 12px 0 12px; margin: 12px 0;
border-left: 3px solid #e7e9ed; border-left: 3px solid #e7e9ed;
} }
...@@ -88,13 +88,13 @@ ...@@ -88,13 +88,13 @@
p { p {
color: #5c5d5e; color: #5c5d5e;
margin: 6px 0 0 0; margin: 6px 0 0;
} }
table { table {
@extend .table; @extend .table;
@extend .table-bordered; @extend .table-bordered;
margin: 12px 0 12px 0; margin: 12px 0;
color: #5c5d5e; color: #5c5d5e;
th { th {
background: #f8fafc; background: #f8fafc;
...@@ -102,7 +102,7 @@ ...@@ -102,7 +102,7 @@
} }
pre { pre {
margin: 12px 0 12px 0; margin: 12px 0;
font-size: 13px; font-size: 13px;
line-height: 1.6em; line-height: 1.6em;
overflow-x: auto; overflow-x: auto;
...@@ -191,7 +191,7 @@ body { ...@@ -191,7 +191,7 @@ body {
line-height: 1.3; line-height: 1.3;
font-size: 1.25em; font-size: 1.25em;
font-weight: 600; font-weight: 600;
margin: 12px 7px 12px 7px; margin: 12px 7px;
} }
h1, h2, h3, h4, h5, h6 { 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;
}
...@@ -132,7 +132,7 @@ ...@@ -132,7 +132,7 @@
} }
.image-info { .image-info {
font-size: 12px; font-size: 12px;
margin: 5px 0 0 0; margin: 5px 0 0;
color: grey; color: grey;
} }
......
...@@ -183,7 +183,7 @@ ...@@ -183,7 +183,7 @@
.block { .block {
width: $sidebar_collapsed_width - 1px; width: $sidebar_collapsed_width - 1px;
margin-left: -19px; margin-left: -19px;
padding: 15px 0 0 0; padding: 15px 0 0;
border-bottom: none; border-bottom: none;
overflow: hidden; overflow: hidden;
} }
...@@ -273,12 +273,12 @@ ...@@ -273,12 +273,12 @@
} }
.participants-list { .participants-list {
margin: -5px -5px; margin: -5px;
} }
.participants-author { .participants-author {
display: inline-block; display: inline-block;
padding: 5px 5px; padding: 5px;
.author_link { .author_link {
display: block; display: block;
......
...@@ -45,7 +45,7 @@ ...@@ -45,7 +45,7 @@
.login-heading h3 { .login-heading h3 {
font-weight: 300; font-weight: 300;
line-height: 1.5; line-height: 1.5;
margin: 0 0 10px 0; margin: 0 0 10px;
} }
.login-footer { .login-footer {
......
...@@ -26,7 +26,7 @@ ...@@ -26,7 +26,7 @@
display: none; display: none;
} }
.new_note, .edit_note { .new_note, .note-edit-form {
.note-form-actions { .note-form-actions {
margin-top: $gl-padding; margin-top: $gl-padding;
} }
......
...@@ -100,6 +100,18 @@ ul.notes { ...@@ -100,6 +100,18 @@ ul.notes {
display: block; display: block;
position: relative; position: relative;
&.is-editting {
.note-header,
.note-text,
.edited-text {
display: none;
}
.note-edit-form {
display: block;
}
}
.note-body { .note-body {
overflow: auto; overflow: auto;
......
...@@ -54,7 +54,7 @@ ...@@ -54,7 +54,7 @@
} }
.account-well { .account-well {
padding: 10px 10px; padding: 10px;
background-color: $help-well-bg; background-color: $help-well-bg;
border: 1px solid $help-well-border; border: 1px solid $help-well-border;
border-radius: $border-radius-base; border-radius: $border-radius-base;
......
...@@ -37,7 +37,7 @@ ...@@ -37,7 +37,7 @@
.dropdown-menu { .dropdown-menu {
left: auto; left: auto;
width: auto; width: auto;
right: 0px; right: 0;
max-width: 240px; max-width: 240px;
} }
} }
...@@ -311,7 +311,7 @@ pre.light-well { ...@@ -311,7 +311,7 @@ pre.light-well {
} }
.git-empty { .git-empty {
margin: 0 7px 0 7px; margin: 0 7px;
h5 { h5 {
color: #5c5d5e; color: #5c5d5e;
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
#contributors { #contributors {
.contributors-list { .contributors-list {
margin: 0 0 10px 0; margin: 0 0 10px;
list-style: none; list-style: none;
padding: 0; padding: 0;
} }
......
...@@ -5,12 +5,12 @@ class Admin::GroupsController < Admin::ApplicationController ...@@ -5,12 +5,12 @@ class Admin::GroupsController < Admin::ApplicationController
@groups = Group.all @groups = Group.all
@groups = @groups.sort(@sort = params[:sort]) @groups = @groups.sort(@sort = params[:sort])
@groups = @groups.search(params[:name]) if params[:name].present? @groups = @groups.search(params[:name]) if params[:name].present?
@groups = @groups.page(params[:page]).per(PER_PAGE) @groups = @groups.page(params[:page])
end end
def show def show
@members = @group.members.order("access_level DESC").page(params[:members_page]).per(PER_PAGE) @members = @group.members.order("access_level DESC").page(params[:members_page])
@projects = @group.projects.page(params[:projects_page]).per(PER_PAGE) @projects = @group.projects.page(params[:projects_page])
end end
def new def new
......
...@@ -2,7 +2,7 @@ class Admin::LabelsController < Admin::ApplicationController ...@@ -2,7 +2,7 @@ class Admin::LabelsController < Admin::ApplicationController
before_action :set_label, only: [:show, :edit, :update, :destroy] before_action :set_label, only: [:show, :edit, :update, :destroy]
def index def index
@labels = Label.templates.page(params[:page]).per(PER_PAGE) @labels = Label.templates.page(params[:page])
end end
def show def show
......
...@@ -11,15 +11,15 @@ class Admin::ProjectsController < Admin::ApplicationController ...@@ -11,15 +11,15 @@ class Admin::ProjectsController < Admin::ApplicationController
@projects = @projects.non_archived unless params[:with_archived].present? @projects = @projects.non_archived unless params[:with_archived].present?
@projects = @projects.search(params[:name]) if params[:name].present? @projects = @projects.search(params[:name]) if params[:name].present?
@projects = @projects.sort(@sort = params[:sort]) @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 end
def show def show
if @group 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 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 end
def transfer def transfer
......
...@@ -6,8 +6,6 @@ class ApplicationController < ActionController::Base ...@@ -6,8 +6,6 @@ class ApplicationController < ActionController::Base
include GitlabRoutingHelper include GitlabRoutingHelper
include PageLayoutHelper include PageLayoutHelper
PER_PAGE = 20
before_action :authenticate_user_from_token! before_action :authenticate_user_from_token!
before_action :authenticate_user! before_action :authenticate_user!
before_action :validate_user_service_ticket! before_action :validate_user_service_ticket!
......
...@@ -7,7 +7,7 @@ class AutocompleteController < ApplicationController ...@@ -7,7 +7,7 @@ class AutocompleteController < ApplicationController
@users = @users.search(params[:search]) if params[:search].present? @users = @users.search(params[:search]) if params[:search].present?
@users = @users.active @users = @users.active
@users = @users.reorder(:name) @users = @users.reorder(:name)
@users = @users.page(params[:page]).per(PER_PAGE) @users = @users.page(params[:page])
if params[:search].blank? if params[:search].blank?
# Include current user if available to filter by "Me" # Include current user if available to filter by "Me"
......
...@@ -6,7 +6,7 @@ module GlobalMilestones ...@@ -6,7 +6,7 @@ module GlobalMilestones
@milestones = MilestonesFinder.new.execute(@projects, params) @milestones = MilestonesFinder.new.execute(@projects, params)
@milestones = GlobalMilestone.build_collection(@milestones) @milestones = GlobalMilestone.build_collection(@milestones)
@milestones = @milestones.sort_by { |x| x.due_date.nil? ? epoch : x.due_date } @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 end
def milestone 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 ...@@ -3,7 +3,7 @@ module IssuesAction
def issues def issues
@issues = get_issues_collection.non_archived @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) @issues = @issues.preload(:author, :project)
@label = @issuable_finder.labels.first @label = @issuable_finder.labels.first
......
...@@ -3,7 +3,7 @@ module MergeRequestsAction ...@@ -3,7 +3,7 @@ module MergeRequestsAction
def merge_requests def merge_requests
@merge_requests = get_merge_requests_collection.non_archived @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) @merge_requests = @merge_requests.preload(:author, :target_project)
@label = @issuable_finder.labels.first @label = @issuable_finder.labels.first
......
class Dashboard::GroupsController < Dashboard::ApplicationController class Dashboard::GroupsController < Dashboard::ApplicationController
def index 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
end end
...@@ -8,7 +8,7 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController ...@@ -8,7 +8,7 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController
@projects = filter_projects(@projects) @projects = filter_projects(@projects)
@projects = @projects.includes(:namespace) @projects = @projects.includes(:namespace)
@projects = @projects.sort(@sort = params[:sort]) @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 @last_push = current_user.recent_push
...@@ -32,7 +32,7 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController ...@@ -32,7 +32,7 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController
@projects = filter_projects(@projects) @projects = filter_projects(@projects)
@projects = @projects.includes(:namespace, :forked_from_project, :tags) @projects = @projects.includes(:namespace, :forked_from_project, :tags)
@projects = @projects.sort(@sort = params[:sort]) @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 @last_push = current_user.recent_push
@groups = [] @groups = []
......
...@@ -6,6 +6,6 @@ class Dashboard::SnippetsController < Dashboard::ApplicationController ...@@ -6,6 +6,6 @@ class Dashboard::SnippetsController < Dashboard::ApplicationController
user: current_user, user: current_user,
scope: params[:scope] scope: params[:scope]
) )
@snippets = @snippets.page(params[:page]).per(PER_PAGE) @snippets = @snippets.page(params[:page])
end end
end end
...@@ -2,7 +2,7 @@ class Dashboard::TodosController < Dashboard::ApplicationController ...@@ -2,7 +2,7 @@ class Dashboard::TodosController < Dashboard::ApplicationController
before_action :find_todos, only: [:index, :destroy, :destroy_all] before_action :find_todos, only: [:index, :destroy, :destroy_all]
def index def index
@todos = @todos.page(params[:page]).per(PER_PAGE) @todos = @todos.page(params[:page])
end end
def destroy def destroy
......
...@@ -3,6 +3,6 @@ class Explore::GroupsController < Explore::ApplicationController ...@@ -3,6 +3,6 @@ class Explore::GroupsController < Explore::ApplicationController
@groups = GroupsFinder.new.execute(current_user) @groups = GroupsFinder.new.execute(current_user)
@groups = @groups.search(params[:search]) if params[:search].present? @groups = @groups.search(params[:search]) if params[:search].present?
@groups = @groups.sort(@sort = params[:sort]) @groups = @groups.sort(@sort = params[:sort])
@groups = @groups.page(params[:page]).per(PER_PAGE) @groups = @groups.page(params[:page])
end end
end end
...@@ -8,7 +8,7 @@ class Explore::ProjectsController < Explore::ApplicationController ...@@ -8,7 +8,7 @@ class Explore::ProjectsController < Explore::ApplicationController
@projects = @projects.where(visibility_level: params[:visibility_level]) if params[:visibility_level].present? @projects = @projects.where(visibility_level: params[:visibility_level]) if params[:visibility_level].present?
@projects = filter_projects(@projects) @projects = filter_projects(@projects)
@projects = @projects.sort(@sort = params[:sort]) @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| respond_to do |format|
format.html format.html
...@@ -23,7 +23,7 @@ class Explore::ProjectsController < Explore::ApplicationController ...@@ -23,7 +23,7 @@ class Explore::ProjectsController < Explore::ApplicationController
def trending def trending
@projects = TrendingProjectsFinder.new.execute(current_user) @projects = TrendingProjectsFinder.new.execute(current_user)
@projects = filter_projects(@projects) @projects = filter_projects(@projects)
@projects = @projects.page(params[:page]).per(PER_PAGE) @projects = @projects.page(params[:page])
respond_to do |format| respond_to do |format|
format.html format.html
...@@ -39,7 +39,7 @@ class Explore::ProjectsController < Explore::ApplicationController ...@@ -39,7 +39,7 @@ class Explore::ProjectsController < Explore::ApplicationController
@projects = ProjectsFinder.new.execute(current_user) @projects = ProjectsFinder.new.execute(current_user)
@projects = filter_projects(@projects) @projects = filter_projects(@projects)
@projects = @projects.reorder('star_count DESC') @projects = @projects.reorder('star_count DESC')
@projects = @projects.page(params[:page]).per(PER_PAGE) @projects = @projects.page(params[:page])
respond_to do |format| respond_to do |format|
format.html format.html
......
class Explore::SnippetsController < Explore::ApplicationController class Explore::SnippetsController < Explore::ApplicationController
def index def index
@snippets = SnippetsFinder.new.execute(current_user, filter: :all) @snippets = SnippetsFinder.new.execute(current_user, filter: :all)
@snippets = @snippets.page(params[:page]).per(PER_PAGE) @snippets = @snippets.page(params[:page])
end end
end end
...@@ -42,7 +42,7 @@ class GroupsController < Groups::ApplicationController ...@@ -42,7 +42,7 @@ class GroupsController < Groups::ApplicationController
@projects = @projects.includes(:namespace) @projects = @projects.includes(:namespace)
@projects = filter_projects(@projects) @projects = filter_projects(@projects)
@projects = @projects.sort(@sort = params[:sort]) @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) @shared_projects = GroupProjectsFinder.new(group, only_shared: true).execute(current_user)
......
...@@ -34,8 +34,7 @@ class ProfilesController < Profiles::ApplicationController ...@@ -34,8 +34,7 @@ class ProfilesController < Profiles::ApplicationController
def audit_log def audit_log
@events = AuditEvent.where(entity_type: "User", entity_id: current_user.id). @events = AuditEvent.where(entity_type: "User", entity_id: current_user.id).
order("created_at DESC"). order("created_at DESC").
page(params[:page]). page(params[:page])
per(PER_PAGE)
end end
def update_username def update_username
......
...@@ -8,7 +8,7 @@ class Projects::BranchesController < Projects::ApplicationController ...@@ -8,7 +8,7 @@ class Projects::BranchesController < Projects::ApplicationController
def index def index
@sort = params[:sort] || 'name' @sort = params[:sort] || 'name'
@branches = @repository.branches_sorted_by(@sort) @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| @max_commits = @branches.reduce(0) do |memo, branch|
diverging_commit_counts = repository.diverging_commit_counts(branch) diverging_commit_counts = repository.diverging_commit_counts(branch)
......
...@@ -15,7 +15,7 @@ class Projects::ForksController < Projects::ApplicationController ...@@ -15,7 +15,7 @@ class Projects::ForksController < Projects::ApplicationController
@sort = params[:sort] || 'id_desc' @sort = params[:sort] || 'id_desc'
@forks = @forks.search(params[:filter_projects]) if params[:filter_projects].present? @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| respond_to do |format|
format.html format.html
......
class Projects::IssuesController < Projects::ApplicationController class Projects::IssuesController < Projects::ApplicationController
include ToggleSubscriptionAction include ToggleSubscriptionAction
include IssuableActions
before_action :module_enabled before_action :module_enabled
before_action :issue, only: [:edit, :update, :show] before_action :issue, only: [:edit, :update, :show]
...@@ -33,7 +34,7 @@ class Projects::IssuesController < Projects::ApplicationController ...@@ -33,7 +34,7 @@ class Projects::IssuesController < Projects::ApplicationController
end end
end end
@issues = @issues.page(params[:page]).per(PER_PAGE) @issues = @issues.page(params[:page])
@label = @project.labels.find_by(title: params[:label_name]) @label = @project.labels.find_by(title: params[:label_name])
respond_to do |format| respond_to do |format|
...@@ -90,6 +91,12 @@ class Projects::IssuesController < Projects::ApplicationController ...@@ -90,6 +91,12 @@ class Projects::IssuesController < Projects::ApplicationController
def update def update
@issue = Issues::UpdateService.new(project, current_user, issue_params).execute(issue) @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| respond_to do |format|
format.js format.js
format.html do format.html do
...@@ -127,6 +134,7 @@ class Projects::IssuesController < Projects::ApplicationController ...@@ -127,6 +134,7 @@ class Projects::IssuesController < Projects::ApplicationController
end end
end end
alias_method :subscribable_resource, :issue alias_method :subscribable_resource, :issue
alias_method :issuable, :issue
def authorize_read_issue! def authorize_read_issue!
return render_404 unless can?(current_user, :read_issue, @issue) return render_404 unless can?(current_user, :read_issue, @issue)
......
...@@ -11,7 +11,7 @@ class Projects::LabelsController < Projects::ApplicationController ...@@ -11,7 +11,7 @@ class Projects::LabelsController < Projects::ApplicationController
respond_to :js, :html respond_to :js, :html
def index def index
@labels = @project.labels.page(params[:page]).per(PER_PAGE) @labels = @project.labels.page(params[:page])
respond_to do |format| respond_to do |format|
format.html format.html
......
class Projects::MergeRequestsController < Projects::ApplicationController class Projects::MergeRequestsController < Projects::ApplicationController
include ToggleSubscriptionAction include ToggleSubscriptionAction
include DiffHelper include DiffHelper
include IssuableActions
before_action :module_enabled before_action :module_enabled
before_action :merge_request, only: [ before_action :merge_request, only: [
...@@ -34,7 +35,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -34,7 +35,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end end
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) @merge_requests = @merge_requests.preload(:target_project)
@label = @project.labels.find_by(title: params[:label_name]) @label = @project.labels.find_by(title: params[:label_name])
...@@ -255,6 +256,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -255,6 +256,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@merge_request ||= @project.merge_requests.find_by!(iid: params[:id]) @merge_request ||= @project.merge_requests.find_by!(iid: params[:id])
end end
alias_method :subscribable_resource, :merge_request alias_method :subscribable_resource, :merge_request
alias_method :issuable, :merge_request
def closes_issues def closes_issues
@closes_issues ||= @merge_request.closes_issues @closes_issues ||= @merge_request.closes_issues
......
...@@ -22,7 +22,7 @@ class Projects::MilestonesController < Projects::ApplicationController ...@@ -22,7 +22,7 @@ class Projects::MilestonesController < Projects::ApplicationController
respond_to do |format| respond_to do |format|
format.html do format.html do
@milestones = @milestones.page(params[:page]).per(PER_PAGE) @milestones = @milestones.page(params[:page])
end end
format.json do format.json do
render json: @milestones render json: @milestones
......
...@@ -21,7 +21,7 @@ class Projects::SnippetsController < Projects::ApplicationController ...@@ -21,7 +21,7 @@ class Projects::SnippetsController < Projects::ApplicationController
filter: :by_project, filter: :by_project,
project: @project project: @project
}) })
@snippets = @snippets.page(params[:page]).per(PER_PAGE) @snippets = @snippets.page(params[:page])
end end
def new def new
......
...@@ -7,7 +7,7 @@ class Projects::TagsController < Projects::ApplicationController ...@@ -7,7 +7,7 @@ class Projects::TagsController < Projects::ApplicationController
def index def index
sorted = VersionSorter.rsort(@repository.tag_names) 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) @releases = project.releases.where(tag: @tags)
end end
......
...@@ -7,7 +7,7 @@ class Projects::WikisController < Projects::ApplicationController ...@@ -7,7 +7,7 @@ class Projects::WikisController < Projects::ApplicationController
before_action :load_project_wiki before_action :load_project_wiki
def pages 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 end
def show def show
......
...@@ -25,7 +25,7 @@ class SnippetsController < ApplicationController ...@@ -25,7 +25,7 @@ class SnippetsController < ApplicationController
filter: :by_user, filter: :by_user,
user: @user, user: @user,
scope: params[:scope] }). scope: params[:scope] }).
page(params[:page]).per(PER_PAGE) page(params[:page])
render 'index' render 'index'
else else
......
...@@ -100,7 +100,7 @@ class UsersController < ApplicationController ...@@ -100,7 +100,7 @@ class UsersController < ApplicationController
def load_projects def load_projects
@projects = @projects =
PersonalProjectsFinder.new(@user).execute(current_user) PersonalProjectsFinder.new(@user).execute(current_user)
.page(params[:page]).per(PER_PAGE) .page(params[:page])
end end
def load_contributed_projects def load_contributed_projects
......
...@@ -27,7 +27,7 @@ module BlobHelper ...@@ -27,7 +27,7 @@ module BlobHelper
link_opts) link_opts)
if !on_top_of_branch?(project, ref) 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) elsif can_edit_blob?(blob, project, ref)
link_to "Edit", edit_path, class: 'btn' link_to "Edit", edit_path, class: 'btn'
elsif can?(current_user, :fork_project, project) elsif can?(current_user, :fork_project, project)
...@@ -50,9 +50,9 @@ module BlobHelper ...@@ -50,9 +50,9 @@ module BlobHelper
return unless blob return unless blob
if !on_top_of_branch?(project, ref) 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? 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) elsif can_edit_blob?(blob, project, ref)
button_tag label, class: "btn btn-#{btn_class}", 'data-target' => "#modal-#{modal_type}-blob", 'data-toggle' => 'modal' button_tag label, class: "btn btn-#{btn_class}", 'data-target' => "#modal-#{modal_type}-blob", 'data-toggle' => 'modal'
elsif can?(current_user, :fork_project, project) elsif can?(current_user, :fork_project, project)
......
...@@ -23,36 +23,34 @@ module ButtonHelper ...@@ -23,36 +23,34 @@ module ButtonHelper
end end
def http_clone_button(project) def http_clone_button(project)
klass = 'btn js-protocol-switch' klass = 'http-selector'
klass << ' active' if default_clone_protocol == 'http' klass << ' has-tooltip' if current_user.try(:require_password?)
klass << ' has_tooltip' if current_user.try(:require_password?)
protocol = gitlab_config.protocol.upcase protocol = gitlab_config.protocol.upcase
content_tag :button, protocol, content_tag :a, protocol,
class: klass, class: klass,
href: @project.http_url_to_repo,
data: { data: {
clone: project.http_url_to_repo, html: true,
placement: 'right',
container: 'body', container: 'body',
html: 'true',
title: "Set a password on your account<br>to pull or push via #{protocol}" title: "Set a password on your account<br>to pull or push via #{protocol}"
}, }
type: :button
end end
def ssh_clone_button(project) def ssh_clone_button(project)
klass = 'btn js-protocol-switch' klass = 'ssh-selector'
klass << ' active' if default_clone_protocol == 'ssh' klass << ' has-tooltip' if current_user.try(:require_ssh_key?)
klass << ' has_tooltip' if current_user.try(:require_ssh_key?)
content_tag :button, 'SSH', content_tag :a, 'SSH',
class: klass, class: klass,
href: project.ssh_url_to_repo,
data: { data: {
clone: project.ssh_url_to_repo, html: true,
placement: 'right',
container: 'body', container: 'body',
html: 'true',
title: 'Add an SSH key to your profile<br>to pull or push via SSH.' title: 'Add an SSH key to your profile<br>to pull or push via SSH.'
}, }
type: :button
end end
end end
...@@ -182,7 +182,7 @@ module CommitsHelper ...@@ -182,7 +182,7 @@ module CommitsHelper
end end
options = { options = {
class: "commit-#{options[:source]}-link has_tooltip", class: "commit-#{options[:source]}-link has-tooltip",
data: { 'original-title'.to_sym => sanitize(source_email) } data: { 'original-title'.to_sym => sanitize(source_email) }
} }
......
...@@ -57,6 +57,19 @@ module IssuesHelper ...@@ -57,6 +57,19 @@ module IssuesHelper
options_from_collection_for_select(milestones, 'id', 'title', object.milestone_id) options_from_collection_for_select(milestones, 'id', 'title', object.milestone_id)
end 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) def status_box_class(item)
if item.respond_to?(:expired?) && item.expired? if item.respond_to?(:expired?) && item.expired?
'status-box-expired' 'status-box-expired'
......
...@@ -56,7 +56,7 @@ module LabelsHelper ...@@ -56,7 +56,7 @@ module LabelsHelper
# Intentionally not using content_tag here so that this method can be called # Intentionally not using content_tag here so that this method can be called
# by LabelReferenceFilter # 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}" ) + %(style="background-color: #{label_color}; color: #{text_color}" ) +
%(title="#{escape_once(label.description)}" data-container="body">) + %(title="#{escape_once(label.description)}" data-container="body">) +
%(#{escape_once(label.name)}#{label_suffix}</span>) %(#{escape_once(label.name)}#{label_suffix}</span>)
......
...@@ -52,7 +52,7 @@ module ProjectsHelper ...@@ -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 link_to(author_html, user_path(author), class: "author_link #{"#{opts[:mobile_classes]}" if opts[:mobile_classes]}").html_safe
else else
title = opts[:title].sub(":name", sanitize(author.name)) 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
end end
...@@ -209,7 +209,7 @@ module ProjectsHelper ...@@ -209,7 +209,7 @@ module ProjectsHelper
def default_clone_protocol def default_clone_protocol
if !current_user || current_user.require_ssh_key? if !current_user || current_user.require_ssh_key?
"http" gitlab_config.protocol
else else
"ssh" "ssh"
end end
......
...@@ -36,6 +36,14 @@ module Emails ...@@ -36,6 +36,14 @@ module Emails
mail_answer_thread(@issue, issue_thread_options(updated_by_user_id, recipient_id)) mail_answer_thread(@issue, issue_thread_options(updated_by_user_id, recipient_id))
end 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 private
def setup_issue_mail(issue_id, recipient_id) def setup_issue_mail(issue_id, recipient_id)
......
...@@ -234,7 +234,9 @@ class Ability ...@@ -234,7 +234,9 @@ class Ability
:rename_project, :rename_project,
:remove_project, :remove_project,
:archive_project, :archive_project,
:remove_fork_project :remove_fork_project,
:destroy_merge_request,
:destroy_issue
] ]
end end
......
...@@ -7,7 +7,10 @@ module InternalId ...@@ -7,7 +7,10 @@ module InternalId
end end
def set_iid 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 self.iid = max_iid.to_i + 1
end end
......
...@@ -58,6 +58,8 @@ module Issuable ...@@ -58,6 +58,8 @@ module Issuable
attr_mentionable :description, cache: true attr_mentionable :description, cache: true
participant :author, :assignee, :notes_with_associations participant :author, :assignee, :notes_with_associations
strip_attributes :title strip_attributes :title
acts_as_paranoid
end end
module ClassMethods module ClassMethods
...@@ -209,4 +211,13 @@ module Issuable ...@@ -209,4 +211,13 @@ module Issuable
Taskable.get_updated_tasks(old_content: previous_changes['description'].first, Taskable.get_updated_tasks(old_content: previous_changes['description'].first,
new_content: description) new_content: description)
end 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 end
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
# state :string(255) # state :string(255)
# iid :integer # iid :integer
# updated_by_id :integer # updated_by_id :integer
# moved_to_id :integer
# #
require 'carrierwave/orm/activerecord' require 'carrierwave/orm/activerecord'
...@@ -31,6 +32,8 @@ class Issue < ActiveRecord::Base ...@@ -31,6 +32,8 @@ class Issue < ActiveRecord::Base
ActsAsTaggableOn.strict_case_match = true ActsAsTaggableOn.strict_case_match = true
belongs_to :project belongs_to :project
belongs_to :moved_to, class_name: 'Issue'
validates :project, presence: true validates :project, presence: true
scope :cared, ->(user) { where(assignee_id: user) } scope :cared, ->(user) { where(assignee_id: user) }
...@@ -102,9 +105,9 @@ class Issue < ActiveRecord::Base ...@@ -102,9 +105,9 @@ class Issue < ActiveRecord::Base
end end
def related_branches def related_branches
return [] if self.project.empty_repo? project.repository.branch_names.select do |branch|
branch.end_with?("-#{iid}")
self.project.repository.branch_names.select { |branch| branch.end_with?("-#{iid}") } end
end end
# Reset issue events cache # Reset issue events cache
...@@ -134,6 +137,18 @@ class Issue < ActiveRecord::Base ...@@ -134,6 +137,18 @@ class Issue < ActiveRecord::Base
end.uniq.select { |mr| mr.open? && mr.closes_issue?(self) } end.uniq.select { |mr| mr.open? && mr.closes_issue?(self) }
end 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 def to_branch_name
"#{title.parameterize}-#{iid}" "#{title.parameterize}-#{iid}"
end end
......
...@@ -431,6 +431,7 @@ class Project < ActiveRecord::Base ...@@ -431,6 +431,7 @@ class Project < ActiveRecord::Base
def safe_import_url def safe_import_url
result = URI.parse(self.import_url) result = URI.parse(self.import_url)
result.password = '*****' unless result.password.nil? 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 result.to_s
rescue rescue
self.import_url self.import_url
...@@ -887,6 +888,7 @@ class Project < ActiveRecord::Base ...@@ -887,6 +888,7 @@ class Project < ActiveRecord::Base
# Forked import is handled asynchronously # Forked import is handled asynchronously
unless forked? unless forked?
if gitlab_shell.add_repository(path_with_namespace) if gitlab_shell.add_repository(path_with_namespace)
repository.after_create
true true
else else
errors.add(:base, 'Failed to create repository via gitlab-shell') errors.add(:base, 'Failed to create repository via gitlab-shell')
......
...@@ -123,23 +123,27 @@ class ProjectWiki ...@@ -123,23 +123,27 @@ class ProjectWiki
end end
def repository def repository
Repository.new(path_with_namespace, @project) @repository ||= Repository.new(path_with_namespace, @project)
end end
def default_branch def default_branch
wiki.class.default_ref wiki.class.default_ref
end end
private
def create_repo! def create_repo!
if init_repo(path_with_namespace) if init_repo(path_with_namespace)
Gollum::Wiki.new(path_to_repo) wiki = Gollum::Wiki.new(path_to_repo)
else else
raise CouldNotCreateWikiError raise CouldNotCreateWikiError
end end
repository.after_create
wiki
end end
private
def init_repo(path_with_namespace) def init_repo(path_with_namespace)
gitlab_shell.add_repository(path_with_namespace) gitlab_shell.add_repository(path_with_namespace)
end end
......
...@@ -42,13 +42,16 @@ class Repository ...@@ -42,13 +42,16 @@ class Repository
end end
def exists? def exists?
return false unless raw_repository return @exists unless @exists.nil?
raw_repository.rugged @exists = cache.fetch(:exists?) do
true begin
raw_repository && raw_repository.rugged ? true : false
rescue Gitlab::Git::Repository::NoRepository rescue Gitlab::Git::Repository::NoRepository
false false
end end
end
end
def empty? def empty?
return @empty unless @empty.nil? return @empty unless @empty.nil?
...@@ -320,12 +323,23 @@ class Repository ...@@ -320,12 +323,23 @@ class Repository
@avatar = nil @avatar = nil
end 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. # Runs code just before a repository is deleted.
def before_delete def before_delete
expire_cache if exists? expire_cache if exists?
expire_root_ref_cache expire_root_ref_cache
expire_emptiness_caches expire_emptiness_caches
expire_exists_cache
end end
# Runs code just before the HEAD of a repository is changed. # Runs code just before the HEAD of a repository is changed.
...@@ -351,6 +365,7 @@ class Repository ...@@ -351,6 +365,7 @@ class Repository
# Runs code after a repository has been forked/imported. # Runs code after a repository has been forked/imported.
def after_import def after_import
expire_emptiness_caches expire_emptiness_caches
expire_exists_cache
end end
# Runs code after a new commit has been pushed. # Runs code after a new commit has been pushed.
......
...@@ -435,7 +435,7 @@ class User < ActiveRecord::Base ...@@ -435,7 +435,7 @@ class User < ActiveRecord::Base
Group.where("namespaces.id IN (#{union.to_sql})") Group.where("namespaces.id IN (#{union.to_sql})")
end end
# Returns the groups a user is authorized to access. # Returns projects user is authorized to access.
def authorized_projects def authorized_projects
Project.where("projects.id IN (#{projects_union.to_sql})") Project.where("projects.id IN (#{projects_union.to_sql})")
end end
......
...@@ -120,7 +120,7 @@ class GitPushService < BaseService ...@@ -120,7 +120,7 @@ class GitPushService < BaseService
closed_issues = commit.closes_issues(current_user) closed_issues = commit.closes_issues(current_user)
closed_issues.each do |issue| closed_issues.each do |issue|
if can?(current_user, :update_issue, 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 end
end end
......
module Issues module Issues
class CloseService < Issues::BaseService 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 if project.jira_tracker? && project.jira_service.active
project.jira_service.execute(commit, issue) project.jira_service.execute(commit, issue)
todo_service.close_issue(issue, current_user) todo_service.close_issue(issue, current_user)
...@@ -9,8 +9,8 @@ module Issues ...@@ -9,8 +9,8 @@ module Issues
if project.default_issues_tracker? && issue.close if project.default_issues_tracker? && issue.close
event_service.close_issue(issue, current_user) event_service.close_issue(issue, current_user)
create_note(issue, commit) create_note(issue, commit) if system_note
notification_service.close_issue(issue, current_user) notification_service.close_issue(issue, current_user) if notifications
todo_service.close_issue(issue, current_user) todo_service.close_issue(issue, current_user)
execute_hooks(issue, 'close') execute_hooks(issue, 'close')
end end
......
...@@ -4,7 +4,7 @@ module Issues ...@@ -4,7 +4,7 @@ module Issues
filter_params filter_params
label_params = params[:label_ids] label_params = params[:label_ids]
issue = project.issues.new(params.except(:label_ids)) issue = project.issues.new(params.except(:label_ids))
issue.author = current_user issue.author = params[:author] || current_user
if issue.save if issue.save
issue.update_attributes(label_ids: label_params) 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 ...@@ -22,7 +22,7 @@ module MergeRequests
closed_issues = merge_request.closes_issues(current_user) closed_issues = merge_request.closes_issues(current_user)
closed_issues.each do |issue| closed_issues.each do |issue|
if can?(current_user, :update_issue, 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 end
end end
......
...@@ -236,6 +236,16 @@ class NotificationService ...@@ -236,6 +236,16 @@ class NotificationService
end end
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 protected
# Get project users with WATCH notification level # Get project users with WATCH notification level
......
...@@ -411,4 +411,26 @@ class SystemNoteService ...@@ -411,4 +411,26 @@ class SystemNoteService
body = "Marked the task **#{new_task.source}** as #{status_label}" body = "Marked the task **#{new_task.source}** as #{status_label}"
create_note(noteable: noteable, project: project, author: author, note: body) create_note(noteable: noteable, project: project, author: author, note: body)
end 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 end
%ul.nav.nav-sidebar %ul.nav.nav-sidebar
= nav_link(path: ['root#index', 'projects#trending', 'projects#starred', 'dashboard/projects#index'], html_options: {class: 'home'}) do = 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 = link_to dashboard_projects_path, title: 'Projects' do
= icon('home fw') = icon('bookmark fw')
%span %span
Projects Projects
= nav_link(controller: :todos) do = nav_link(controller: :todos) do
......
%ul.nav.nav-sidebar %ul.nav.nav-sidebar
= nav_link(path: ['dashboard#show', 'root#show', 'projects#trending', 'projects#starred', 'projects#index'], html_options: {class: 'home'}) do = 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 = link_to explore_root_path, title: 'Projects' do
= icon('home fw') = icon('bookmark fw')
%span %span
Projects Projects
= nav_link(controller: :groups) do = nav_link(controller: :groups) do
......
...@@ -3,31 +3,7 @@ ...@@ -3,31 +3,7 @@
%meta{content: "text/html; charset=utf-8", "http-equiv" => "Content-Type"} %meta{content: "text/html; charset=utf-8", "http-equiv" => "Content-Type"}
%title %title
GitLab GitLab
:css = stylesheet_link_tag 'notify'
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;
}
%body %body
%div.content %div.content
= yield = 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 @@ ...@@ -8,7 +8,7 @@
= key.fingerprint = key.fingerprint
.pull-right .pull-right
%span.key-created-at %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 = 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 %span.sr-only Remove
= icon('trash') = icon('trash')
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
- if branch.name == @repository.root_ref - if branch.name == @repository.root_ref
%span.label.label-primary default %span.label.label-primary default
- elsif @repository.merged_to_root_ref? branch.name - 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 merged
- if @project.protected_branch? branch.name - if @project.protected_branch? branch.name
...@@ -30,7 +30,7 @@ ...@@ -30,7 +30,7 @@
Compare Compare
- if can_remove_branch?(@project, branch.name) - 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") = icon("trash-o")
- if branch.name != @repository.root_ref - if branch.name != @repository.root_ref
......
- unless @project.empty_repo? - unless @project.empty_repo?
- if can? current_user, :download_code, @project - 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') = icon('download')
- unless @project.empty_repo? - unless @project.empty_repo?
- if current_user && can?(current_user, :fork_project, @project) - if current_user && can?(current_user, :fork_project, @project)
- if current_user.already_forked?(@project) && current_user.manageable_namespaces.size < 2 - 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') = icon('code-fork fw')
Fork Fork
%div.count-with-arrow %div.count-with-arrow
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
%span.count %span.count
= @project.forks_count = @project.forks_count
- else - 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') = icon('code-fork fw')
Fork Fork
%div.count-with-arrow %div.count-with-arrow
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
= notification_list_item(level, @membership) = notification_list_item(level, @membership)
- when GroupMember - 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') = icon('bell')
= notification_label(@membership) = notification_label(@membership)
= icon('angle-down') = icon('angle-down')
- if current_user - 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) - if current_user.starred?(@project)
= icon('star fw') = icon('star fw')
%span.starred Unstar %span.starred Unstar
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
= @project.star_count = @project.star_count
- else - 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') = icon('star fw')
Star Star
%div.count-with-arrow %div.count-with-arrow
......
= form_tag namespace_project_compare_index_path(@project.namespace, @project), method: :post, class: 'form-inline js-requires-input' do = form_tag namespace_project_compare_index_path(@project.namespace, @project), method: :post, class: 'form-inline js-requires-input' do
.clearfix .clearfix
- if params[:to] && params[:from] - 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 .form-group
.input-group.inline-input-group .input-group.inline-input-group
%span.input-group-addon from %span.input-group-addon from
......
...@@ -28,7 +28,7 @@ ...@@ -28,7 +28,7 @@
.file-actions.hidden-xs .file-actions.hidden-xs
- if blob_text_viewable?(blob) - 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') = 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 @@ ...@@ -8,26 +8,9 @@
- last_line = 0 - last_line = 0
- raw_diff_lines = diff_file.diff_lines.to_a - raw_diff_lines = diff_file.diff_lines.to_a
- diff_file.highlighted_diff_lines.each_with_index do |line, index| - 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_code = generate_line_code(diff_file.file_path, line)
- line_old = line.old_pos - last_line = line.new_pos
%tr.line_holder{ id: line_code, class: "#{type}" } = render "projects/diffs/line", {line: line, diff_file: diff_file, line_code: line_code}
- 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)
- if @reply_allowed - if @reply_allowed
- comments = @line_notes.select { |n| n.line_code == line_code && n.active? }.sort_by(&:created_at) - comments = @line_notes.select { |n| n.line_code == line_code && n.active? }.sort_by(&:created_at)
......
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
.col-md-2.col-sm-3 .col-md-2.col-sm-3
- if fork = namespace.find_fork_of(@project) - if fork = namespace.find_fork_of(@project)
.fork-thumbnail .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) = image_tag namespace_icon(namespace, 100)
.caption .caption
%strong %strong
...@@ -22,7 +22,7 @@ ...@@ -22,7 +22,7 @@
- else - else
.fork-thumbnail .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) = image_tag namespace_icon(namespace, 100)
.caption .caption
%strong %strong
......
...@@ -45,7 +45,6 @@ ...@@ -45,7 +45,6 @@
- if can?(current_user, :update_issue, @issue) - 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 '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 '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 = link_to edit_namespace_project_issue_path(@project.namespace, @project, @issue), class: 'btn btn-nr btn-grouped issuable-edit' do
= icon('pencil-square-o') = icon('pencil-square-o')
Edit Edit
......
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
- if merge_request.open? && merge_request.broken? - if merge_request.open? && merge_request.broken?
%li %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') = icon('exclamation-triangle')
- if merge_request.assignee - if merge_request.assignee
......
...@@ -29,7 +29,7 @@ ...@@ -29,7 +29,7 @@
- if @merge_request.open? - 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 '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 = 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 Edit
- if @merge_request.closed? - 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' = 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 @@ ...@@ -5,6 +5,6 @@
= render 'projects/zen', f: f, attr: :note, classes: 'note_text js-note-text js-task-list-field' = render 'projects/zen', f: f, attr: :note, classes: 'note_text js-note-text js-task-list-field'
= render 'projects/notes/hints' = 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' = 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' = link_to 'Cancel', '#', class: 'btn btn-nr btn-cancel note-edit-cancel'
...@@ -15,11 +15,11 @@ ...@@ -15,11 +15,11 @@
= render 'projects/tags/download', ref: tag.name, project: @project = render 'projects/tags/download', ref: tag.name, project: @project
- if can?(current_user, :push_code, @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") = icon("pencil")
- if can?(current_user, :admin_project, @project) - 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") = icon("trash-o")
- if commit - if commit
......
...@@ -5,17 +5,17 @@ ...@@ -5,17 +5,17 @@
.gray-content-block .gray-content-block
.pull-right .pull-right
- if can?(current_user, :push_code, @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") = 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') = 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') = icon('history')
- if can? current_user, :download_code, @project - if can? current_user, :download_code, @project
= render 'projects/tags/download', ref: @tag.name, project: @project = render 'projects/tags/download', ref: @tag.name, project: @project
- if can?(current_user, :admin_project, @project) - if can?(current_user, :admin_project, @project)
.pull-right .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 %i.fa.fa-trash-o
.title .title
%span.item-title= @tag.name %span.item-title= @tag.name
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
- if current_user - if current_user
%li %li
- if !on_top_of_branch? - 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') = icon('plus')
- else - else
%span.dropdown %span.dropdown
......
...@@ -8,11 +8,9 @@ ...@@ -8,11 +8,9 @@
= icon('angle-down') = icon('angle-down')
%ul.dropdown-menu.dropdown-menu-right.clone-options-dropdown %ul.dropdown-menu.dropdown-menu-right.clone-options-dropdown
%li %li
%a#ssh-selector{href: @project.ssh_url_to_repo} = ssh_clone_button(project)
SSH
%li %li
%a#http-selector{href: @project.http_url_to_repo} = http_clone_button(project)
HTTPS
= text_field_tag :project_clone, default_url_to_repo(project), class: "js-select-on-focus form-control", readonly: true = text_field_tag :project_clone, default_url_to_repo(project), class: "js-select-on-focus form-control", readonly: true
.input-group-btn .input-group-btn
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
.stats .stats
%span %span
= icon('home') = icon('bookmark')
= number_with_delimiter(group.projects.count) = number_with_delimiter(group.projects.count)
%span %span
......
...@@ -85,6 +85,19 @@ ...@@ -85,6 +85,19 @@
- if can? current_user, :admin_label, issuable.project - 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 = 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) - if issuable.is_a?(MergeRequest)
%hr %hr
- if @merge_request.new_record? - if @merge_request.new_record?
...@@ -114,7 +127,11 @@ ...@@ -114,7 +127,11 @@
for this project. for this project.
- if issuable.new_record? - if issuable.new_record?
- cancel_project = issuable.source_project = link_to 'Cancel', namespace_project_issues_path(@project.namespace, @project), class: 'btn btn-cancel'
- else - else
- cancel_project = issuable.project .pull-right
= link_to 'Cancel', [cancel_project.namespace.becomes(Namespace), cancel_project, issuable], class: 'btn btn-cancel' - 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 @@ ...@@ -23,5 +23,5 @@
- if assignee - if assignee
= link_to polymorphic_path(base_url_args, { milestone_title: @milestone.title, assignee_id: issuable.assignee_id, state: 'all' }), = 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: '') - image_tag(avatar_icon(issuable.assignee, 16), class: "avatar s16", alt: '')
.detail-page-header .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_icon(@snippet.visibility_level, fw: false)
= visibility_level_label(@snippet.visibility_level) = visibility_level_label(@snippet.visibility_level)
%span.identifier %span.identifier
......
.awards.votes-block .awards.votes-block
- awards_sort(votable.notes.awards.grouped_awards).each do |emoji, notes| - 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) = emoji_icon(emoji)
%span.award-control-text.js-counter %span.award-control-text.js-counter
= notes.count = notes.count
......
...@@ -20,14 +20,15 @@ class RepositoryForkWorker ...@@ -20,14 +20,15 @@ class RepositoryForkWorker
return return
end end
project.repository.after_import
unless project.valid_repo? 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.update(import_error: "The forked repository is invalid.")
project.import_fail project.import_fail
return return
end end
project.repository.after_import
project.import_finish project.import_finish
end end
end end
...@@ -49,6 +49,7 @@ module Gitlab ...@@ -49,6 +49,7 @@ module Gitlab
config.assets.paths << Gemojione.index.images_path config.assets.paths << Gemojione.index.images_path
config.assets.precompile << "*.png" config.assets.precompile << "*.png"
config.assets.precompile << "print.css" config.assets.precompile << "print.css"
config.assets.precompile << "notify.css"
# Version of your assets, change this if you want to expire all your assets # Version of your assets, change this if you want to expire all your assets
config.assets.version = '1.0' 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 ...@@ -613,7 +613,7 @@ Rails.application.routes.draw do
end end
end end
resources :merge_requests, constraints: { id: /\d+/ }, except: [:destroy] do resources :merge_requests, constraints: { id: /\d+/ } do
member do member do
get :commits get :commits
get :diffs get :diffs
...@@ -684,7 +684,7 @@ Rails.application.routes.draw do ...@@ -684,7 +684,7 @@ Rails.application.routes.draw do
end end
end end
resources :issues, constraints: { id: /\d+/ }, except: [:destroy] do resources :issues, constraints: { id: /\d+/ } do
member do member do
post :toggle_subscription post :toggle_subscription
end 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 ...@@ -416,7 +416,9 @@ ActiveRecord::Schema.define(version: 20160320204112) do
t.string "state" t.string "state"
t.integer "iid" t.integer "iid"
t.integer "updated_by_id" t.integer "updated_by_id"
t.integer "moved_to_id"
t.boolean "confidential", default: false t.boolean "confidential", default: false
t.datetime "deleted_at"
end end
add_index "issues", ["assignee_id"], name: "index_issues_on_assignee_id", using: :btree add_index "issues", ["assignee_id"], name: "index_issues_on_assignee_id", using: :btree
...@@ -424,6 +426,7 @@ ActiveRecord::Schema.define(version: 20160320204112) do ...@@ -424,6 +426,7 @@ ActiveRecord::Schema.define(version: 20160320204112) do
add_index "issues", ["confidential"], name: "index_issues_on_confidential", using: :btree 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", "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", ["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", ["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", ["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 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 ...@@ -546,12 +549,14 @@ ActiveRecord::Schema.define(version: 20160320204112) do
t.boolean "merge_when_build_succeeds", default: false, null: false t.boolean "merge_when_build_succeeds", default: false, null: false
t.integer "merge_user_id" t.integer "merge_user_id"
t.string "merge_commit_sha" t.string "merge_commit_sha"
t.datetime "deleted_at"
end end
add_index "merge_requests", ["assignee_id"], name: "index_merge_requests_on_assignee_id", using: :btree 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", ["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", "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", ["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", ["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", ["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 add_index "merge_requests", ["source_branch"], name: "index_merge_requests_on_source_branch", using: :btree
......
...@@ -326,17 +326,25 @@ Example response: ...@@ -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. Only for admins and project owners. Soft deletes the issue in question.
An issue gets now closed and is done by calling If the operation is successful, a status code `200` is returned. In case you cannot
`PUT /projects/:id/issues/:issue_id` with the parameter `state_event` set to destroy this issue, or it is not present, code `404` is given.
`close`. See [edit issue](#edit-issue) for more details.
``` ```
DELETE /projects/:id/issues/:issue_id 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 on issues
Comments are done via the [notes](notes.md) resource. Comments are done via the [notes](notes.md) resource.
...@@ -380,6 +380,25 @@ Parameters: ...@@ -380,6 +380,25 @@ Parameters:
If the operation is successful, 200 and the updated merge request is returned. 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. 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 ## Accept MR
Merge changes submitted with MR using this API. Merge changes submitted with MR using this API.
......
...@@ -348,7 +348,7 @@ GitLab Shell is an SSH access and repository management software developed speci ...@@ -348,7 +348,7 @@ GitLab Shell is an SSH access and repository management software developed speci
cd /home/git cd /home/git
sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-workhorse.git sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-workhorse.git
cd gitlab-workhorse 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 sudo -u git -H make
### Initialize Database and Activate Advanced Features ### Initialize Database and Activate Advanced Features
......
...@@ -58,7 +58,7 @@ GitLab 8.1. ...@@ -58,7 +58,7 @@ GitLab 8.1.
```bash ```bash
cd /home/git/gitlab-workhorse cd /home/git/gitlab-workhorse
sudo -u git -H git fetch --all 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 sudo -u git -H make
``` ```
......
...@@ -160,6 +160,7 @@ Feature: Project Issues ...@@ -160,6 +160,7 @@ Feature: Project Issues
Scenario: Issues on empty project Scenario: Issues on empty project
Given empty project "Empty Project" Given empty project "Empty Project"
And I have an ssh key
When I visit empty project page When I visit empty project page
And I see empty project details with ssh clone info And I see empty project details with ssh clone info
When I visit empty project's issues page When I visit empty project's issues page
......
...@@ -27,7 +27,7 @@ class Spinach::Features::ProjectCreate < Spinach::FeatureSteps ...@@ -27,7 +27,7 @@ class Spinach::Features::ProjectCreate < Spinach::FeatureSteps
step 'I click on HTTP' do step 'I click on HTTP' do
find('#clone-dropdown').click find('#clone-dropdown').click
find('#http-selector').click find('.http-selector').click
end end
step 'Remote url should update to http link' do step 'Remote url should update to http link' do
...@@ -36,7 +36,7 @@ class Spinach::Features::ProjectCreate < Spinach::FeatureSteps ...@@ -36,7 +36,7 @@ class Spinach::Features::ProjectCreate < Spinach::FeatureSteps
step 'If I click on SSH' do step 'If I click on SSH' do
find('#clone-dropdown').click find('#clone-dropdown').click
find('#ssh-selector').click find('.ssh-selector').click
end end
step 'Remote url should update to ssh link' do step 'Remote url should update to ssh link' do
......
...@@ -5,6 +5,7 @@ class Spinach::Features::ProjectIssues < Spinach::FeatureSteps ...@@ -5,6 +5,7 @@ class Spinach::Features::ProjectIssues < Spinach::FeatureSteps
include SharedNote include SharedNote
include SharedPaths include SharedPaths
include SharedMarkdown include SharedMarkdown
include SharedUser
step 'I should see "Release 0.4" in issues' do step 'I should see "Release 0.4" in issues' do
expect(page).to have_content "Release 0.4" expect(page).to have_content "Release 0.4"
...@@ -240,7 +241,7 @@ class Spinach::Features::ProjectIssues < Spinach::FeatureSteps ...@@ -240,7 +241,7 @@ class Spinach::Features::ProjectIssues < Spinach::FeatureSteps
end end
step 'empty project "Empty Project"' do 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 end
When 'I visit empty project page' do When 'I visit empty project page' do
......
...@@ -118,9 +118,7 @@ module API ...@@ -118,9 +118,7 @@ module API
end end
def authorize!(action, subject) def authorize!(action, subject)
unless abilities.allowed?(current_user, action, subject) forbidden! unless abilities.allowed?(current_user, action, subject)
forbidden!
end
end end
def authorize_push_project def authorize_push_project
......
...@@ -191,7 +191,7 @@ module API ...@@ -191,7 +191,7 @@ module API
end end
end end
# Delete a project issue (deprecated) # Delete a project issue
# #
# Parameters: # Parameters:
# id (required) - The ID of a project # id (required) - The ID of a project
...@@ -199,7 +199,10 @@ module API ...@@ -199,7 +199,10 @@ module API
# Example Request: # Example Request:
# DELETE /projects/:id/issues/:issue_id # DELETE /projects/:id/issues/:issue_id
delete ":id/issues/:issue_id" do 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 end
end end
......
...@@ -100,6 +100,18 @@ module API ...@@ -100,6 +100,18 @@ module API
end end
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 # Routing "merge_request/:merge_request_id/..." is DEPRECATED and WILL BE REMOVED in version 9.0
# Use "merge_requests/:merge_request_id/..." instead. # Use "merge_requests/:merge_request_id/..." instead.
# #
......
...@@ -47,6 +47,7 @@ module Banzai ...@@ -47,6 +47,7 @@ module Banzai
# Returns a String # Returns a String
def data_attribute(attributes = {}) def data_attribute(attributes = {})
attributes[:reference_filter] = self.class.name.demodulize 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(" ") attributes.map { |key, value| %Q(data-#{key.to_s.dasherize}="#{escape_once(value)}") }.join(" ")
end 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 module Gitlab
# Extract possible GFM references from an arbitrary String for further processing. # Extract possible GFM references from an arbitrary String for further processing.
class ReferenceExtractor < Banzai::ReferenceExtractor class ReferenceExtractor < Banzai::ReferenceExtractor
REFERABLES = %i(user issue label milestone merge_request snippet commit commit_range)
attr_accessor :project, :current_user, :author attr_accessor :project, :current_user, :author
def initialize(project, current_user = nil, author = nil) def initialize(project, current_user = nil, author = nil)
...@@ -17,7 +18,7 @@ module Gitlab ...@@ -17,7 +18,7 @@ module Gitlab
super(text, context.merge(project: project)) super(text, context.merge(project: project))
end end
%i(user label milestone merge_request snippet commit commit_range).each do |type| REFERABLES.each do |type|
define_method("#{type}s") do define_method("#{type}s") do
@references[type] ||= references(type, reference_context) @references[type] ||= references(type, reference_context)
end end
...@@ -31,6 +32,21 @@ module Gitlab ...@@ -31,6 +32,21 @@ module Gitlab
end end
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 private
def reference_context def reference_context
......
require('spec_helper') require('spec_helper')
describe Projects::IssuesController do describe Projects::IssuesController do
describe "GET #index" do let(:project) { create(:project_empty_repo) }
let(:project) { create(:project) }
let(:user) { create(:user) } let(:user) { create(:user) }
let(:issue) { create(:issue, project: project) } let(:issue) { create(:issue, project: project) }
describe "GET #index" do
before do before do
sign_in(user) sign_in(user)
project.team << [user, :developer] project.team << [user, :developer]
...@@ -41,7 +41,7 @@ describe Projects::IssuesController do ...@@ -41,7 +41,7 @@ describe Projects::IssuesController do
end end
describe 'Confidential Issues' do describe 'Confidential Issues' do
let(:project) { create(:empty_project, :public) } let(:project) { create(:project_empty_repo, :public) }
let(:assignee) { create(:assignee) } let(:assignee) { create(:assignee) }
let(:author) { create(:user) } let(:author) { create(:user) }
let(:non_member) { create(:user) } let(:non_member) { create(:user) }
...@@ -186,4 +186,29 @@ describe Projects::IssuesController do ...@@ -186,4 +186,29 @@ describe Projects::IssuesController do
end end
end 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 end
...@@ -157,6 +157,29 @@ describe Projects::MergeRequestsController do ...@@ -157,6 +157,29 @@ describe Projects::MergeRequestsController do
end end
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 describe 'GET diffs' do
def go(format: 'html') def go(format: 'html')
get :diffs, 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 ...@@ -11,7 +11,7 @@ describe LabelsHelper do
end end
it 'uses the instance variable' do 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
end end
...@@ -41,8 +41,8 @@ describe LabelsHelper do ...@@ -41,8 +41,8 @@ describe LabelsHelper do
context 'with a tooltip argument' do context 'with a tooltip argument' do
context 'set to false' do context 'set to false' do
it 'does not include the has_tooltip class' do it 'does not include the has-tooltip class' do
expect(link_to_label(label, tooltip: false)).not_to match %r{has_tooltip} expect(link_to_label(label, tooltip: false)).not_to match %r{has-tooltip}
end end
end end
end end
......
...@@ -86,4 +86,23 @@ describe ProjectsHelper do ...@@ -86,4 +86,23 @@ describe ProjectsHelper do
end end
end 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 end
...@@ -56,7 +56,7 @@ describe Banzai::Filter::LabelReferenceFilter, lib: true do ...@@ -56,7 +56,7 @@ describe Banzai::Filter::LabelReferenceFilter, lib: true do
describe 'label span element' do describe 'label span element' do
it 'includes default classes' do it 'includes default classes' do
doc = reference_filter("Label #{reference}") 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 end
it 'includes a style attribute' do 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 ...@@ -124,4 +124,24 @@ describe Gitlab::ReferenceExtractor, lib: true do
expect(extracted).to match_array([issue]) expect(extracted).to match_array([issue])
end end
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 end
...@@ -158,6 +158,33 @@ describe Notify do ...@@ -158,6 +158,33 @@ describe Notify do
is_expected.to have_body_text /#{namespace_project_issue_path project.namespace, project, issue}/ is_expected.to have_body_text /#{namespace_project_issue_path project.namespace, project, issue}/
end end
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 end
context 'for merge requests' do context 'for merge requests' do
......
...@@ -37,6 +37,11 @@ describe Issue, models: true do ...@@ -37,6 +37,11 @@ describe Issue, models: true do
subject { create(:issue) } 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 describe '#to_reference' do
it 'returns a String reference to the object' do it 'returns a String reference to the object' do
expect(subject.to_reference).to eq "##{subject.iid}" expect(subject.to_reference).to eq "##{subject.iid}"
...@@ -130,12 +135,62 @@ describe Issue, models: true do ...@@ -130,12 +135,62 @@ describe Issue, models: true do
end end
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 describe '#related_branches' do
it "selects the right branches" do it "selects the right branches" do
allow(subject.project.repository).to receive(:branch_names). 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
end end
......
...@@ -49,6 +49,11 @@ describe MergeRequest, models: true do ...@@ -49,6 +49,11 @@ describe MergeRequest, models: true do
it { is_expected.to include_module(Taskable) } it { is_expected.to include_module(Taskable) }
end 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 describe 'validation' do
it { is_expected.to validate_presence_of(:target_branch) } it { is_expected.to validate_presence_of(:target_branch) }
it { is_expected.to validate_presence_of(:source_branch) } it { is_expected.to validate_presence_of(:source_branch) }
......
...@@ -732,4 +732,45 @@ describe Project, models: true do ...@@ -732,4 +732,45 @@ describe Project, models: true do
it { expect(internal_project.visibility_level_allowed?(Gitlab::VisibilityLevel::PUBLIC)).to be_falsey } it { expect(internal_project.visibility_level_allowed?(Gitlab::VisibilityLevel::PUBLIC)).to be_falsey }
end end
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 end
...@@ -244,6 +244,18 @@ describe ProjectWiki, models: true do ...@@ -244,6 +244,18 @@ describe ProjectWiki, models: true do
end end
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 private
def create_temp_repo(path) def create_temp_repo(path)
......
...@@ -537,6 +537,12 @@ describe Repository, models: true do ...@@ -537,6 +537,12 @@ describe Repository, models: true do
repository.before_delete repository.before_delete
end end
it 'flushes the exists cache' do
expect(repository).to receive(:expire_exists_cache)
repository.before_delete
end
end end
describe 'when a repository exists' do describe 'when a repository exists' do
...@@ -593,6 +599,12 @@ describe Repository, models: true do ...@@ -593,6 +599,12 @@ describe Repository, models: true do
repository.after_import repository.after_import
end end
it 'flushes the exists cache' do
expect(repository).to receive(:expire_exists_cache)
repository.after_import
end
end end
describe '#after_push_commit' do describe '#after_push_commit' do
...@@ -619,6 +631,14 @@ describe Repository, models: true do ...@@ -619,6 +631,14 @@ describe Repository, models: true do
end end
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 describe "#main_language" do
it 'shows the main language of the project' do it 'shows the main language of the project' do
expect(repository.main_language).to eq("Ruby") expect(repository.main_language).to eq("Ruby")
...@@ -781,6 +801,16 @@ describe Repository, models: true do ...@@ -781,6 +801,16 @@ describe Repository, models: true do
end end
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 describe '#build_cache' do
let(:cache) { repository.send(:cache) } let(:cache) { repository.send(:cache) }
......
...@@ -6,7 +6,7 @@ describe API::API, api: true do ...@@ -6,7 +6,7 @@ describe API::API, api: true do
let(:non_member) { create(:user) } let(:non_member) { create(:user) }
let(:author) { create(:author) } let(:author) { create(:author) }
let(:assignee) { create(:assignee) } let(:assignee) { create(:assignee) }
let(:admin) { create(:admin) } let(:admin) { create(:user, :admin) }
let!(:project) { create(:project, :public, namespace: user.namespace ) } let!(:project) { create(:project, :public, namespace: user.namespace ) }
let!(:closed_issue) do let!(:closed_issue) do
create :closed_issue, create :closed_issue,
...@@ -469,9 +469,25 @@ describe API::API, api: true do ...@@ -469,9 +469,25 @@ describe API::API, api: true do
end end
describe "DELETE /projects/:id/issues/:issue_id" do describe "DELETE /projects/:id/issues/:issue_id" do
it "should delete a project issue" do it "rejects a non member from deleting an issue" do
delete api("/projects/#{project.id}/issues/#{issue.id}", user) delete api("/projects/#{project.id}/issues/#{issue.id}", non_member)
expect(response.status).to eq(405) 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 end
end end
...@@ -4,7 +4,9 @@ describe API::API, api: true do ...@@ -4,7 +4,9 @@ describe API::API, api: true do
include ApiHelpers include ApiHelpers
let(:base_time) { Time.now } let(:base_time) { Time.now }
let(:user) { create(:user) } let(:user) { create(:user) }
let!(:project) {create(:project, creator_id: user.id, namespace: user.namespace) } 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) { 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_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!(: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) }
...@@ -315,6 +317,29 @@ describe API::API, api: true do ...@@ -315,6 +317,29 @@ describe API::API, api: true do
end end
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 describe "PUT /projects/:id/merge_requests/:merge_request_id to close MR" do
it "should return merge_request" do it "should return merge_request" do
put api("/projects/#{project.id}/merge_requests/#{merge_request.id}", user), state_event: "close" 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 ...@@ -453,6 +453,59 @@ describe SystemNoteService, services: true do
end end
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 include JiraServiceHelper
describe 'JIRA integration' do describe 'JIRA integration' do
......
...@@ -3,12 +3,17 @@ require 'spec_helper' ...@@ -3,12 +3,17 @@ require 'spec_helper'
describe RepositoryForkWorker do describe RepositoryForkWorker do
let(:project) { create(:project) } let(:project) { create(:project) }
let(:fork_project) { create(:project, forked_from_project: project) } let(:fork_project) { create(:project, forked_from_project: project) }
let(:shell) { Gitlab::Shell.new }
subject { RepositoryForkWorker.new } subject { RepositoryForkWorker.new }
before do
allow(subject).to receive(:gitlab_shell).and_return(shell)
end
describe "#perform" do describe "#perform" do
it "creates a new repository from a fork" 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, project.path_with_namespace,
fork_project.namespace.path fork_project.namespace.path
).and_return(true) ).and_return(true)
...@@ -19,20 +24,26 @@ describe RepositoryForkWorker do ...@@ -19,20 +24,26 @@ describe RepositoryForkWorker do
fork_project.namespace.path) fork_project.namespace.path)
end end
it 'flushes the empty caches' do it 'flushes various caches' do
expect_any_instance_of(Gitlab::Shell).to receive(:fork_repository). expect(shell).to receive(:fork_repository).
with(project.path_with_namespace, fork_project.namespace.path). with(project.path_with_namespace, fork_project.namespace.path).
and_return(true) and_return(true)
expect_any_instance_of(Repository).to receive(:expire_emptiness_caches). expect_any_instance_of(Repository).to receive(:expire_emptiness_caches).
and_call_original and_call_original
expect_any_instance_of(Repository).to receive(:expire_exists_cache).
and_call_original
subject.perform(project.id, project.path_with_namespace, subject.perform(project.id, project.path_with_namespace,
fork_project.namespace.path) fork_project.namespace.path)
end end
it "handles bad fork" do 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( subject.perform(
project.id, project.id,
project.path_with_namespace, 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