Merge branch 'master' into ide
This commit is contained in:
commit
2197ee02e9
|
|
@ -96,6 +96,7 @@ stages:
|
|||
- export KNAPSACK_GENERATE_REPORT=true
|
||||
- export CACHE_CLASSES=true
|
||||
- cp ${KNAPSACK_RSPEC_SUITE_REPORT_PATH} ${KNAPSACK_REPORT_PATH}
|
||||
- scripts/gitaly-test-spawn
|
||||
- knapsack rspec "--color --format documentation"
|
||||
artifacts:
|
||||
expire_in: 31d
|
||||
|
|
@ -221,6 +222,7 @@ setup-test-env:
|
|||
- bundle exec rake gettext:po_to_json
|
||||
- bundle exec rake gitlab:assets:compile
|
||||
- bundle exec ruby -Ispec -e 'require "spec_helper" ; TestEnv.init'
|
||||
- scripts/gitaly-test-build # Do not use 'bundle exec' here
|
||||
artifacts:
|
||||
expire_in: 7d
|
||||
paths:
|
||||
|
|
@ -486,6 +488,7 @@ karma:
|
|||
BABEL_ENV: "coverage"
|
||||
CHROME_LOG_FILE: "chrome_debug.log"
|
||||
script:
|
||||
- scripts/gitaly-test-spawn
|
||||
- bundle exec rake gettext:po_to_json
|
||||
- bundle exec rake karma
|
||||
coverage: '/^Statements *: (\d+\.\d+%)/'
|
||||
|
|
|
|||
457
.rubocop.yml
457
.rubocop.yml
|
|
@ -6,6 +6,7 @@ inherit_from: .rubocop_todo.yml
|
|||
|
||||
AllCops:
|
||||
TargetRubyVersion: 2.3
|
||||
TargetRailsVersion: 4.2
|
||||
# Cop names are not d§splayed in offense messages by default. Change behavior
|
||||
# by overriding DisplayCopNames, or by giving the -D/--display-cop-names
|
||||
# option.
|
||||
|
|
@ -29,12 +30,221 @@ AllCops:
|
|||
Bundler/OrderedGems:
|
||||
Enabled: false
|
||||
|
||||
# Style #######################################################################
|
||||
# Layout ######################################################################
|
||||
|
||||
# Check indentation of private/protected visibility modifiers.
|
||||
Style/AccessModifierIndentation:
|
||||
Layout/AccessModifierIndentation:
|
||||
Enabled: true
|
||||
|
||||
# Align the elements of an array literal if they span more than one line.
|
||||
Layout/AlignArray:
|
||||
Enabled: true
|
||||
|
||||
# Align the elements of a hash literal if they span more than one line.
|
||||
Layout/AlignHash:
|
||||
Enabled: true
|
||||
|
||||
# Here we check if the parameters on a multi-line method call or
|
||||
# definition are aligned.
|
||||
Layout/AlignParameters:
|
||||
Enabled: false
|
||||
|
||||
# Put end statement of multiline block on its own line.
|
||||
Layout/BlockEndNewline:
|
||||
Enabled: true
|
||||
|
||||
# Indentation of when in a case/when/[else/]end.
|
||||
Layout/CaseIndentation:
|
||||
Enabled: true
|
||||
|
||||
# Indentation of comments.
|
||||
Layout/CommentIndentation:
|
||||
Enabled: true
|
||||
|
||||
# Multi-line method chaining should be done with leading dots.
|
||||
Layout/DotPosition:
|
||||
Enabled: true
|
||||
EnforcedStyle: leading
|
||||
|
||||
# Align elses and elsifs correctly.
|
||||
Layout/ElseAlignment:
|
||||
Enabled: true
|
||||
|
||||
# Add an empty line after magic comments to separate them from code.
|
||||
Layout/EmptyLineAfterMagicComment:
|
||||
Enabled: false
|
||||
|
||||
# Use empty lines between defs.
|
||||
Layout/EmptyLineBetweenDefs:
|
||||
Enabled: true
|
||||
|
||||
# Don't use several empty lines in a row.
|
||||
Layout/EmptyLines:
|
||||
Enabled: true
|
||||
|
||||
# Keep blank lines around access modifiers.
|
||||
Layout/EmptyLinesAroundAccessModifier:
|
||||
Enabled: true
|
||||
|
||||
# Keeps track of empty lines around block bodies.
|
||||
Layout/EmptyLinesAroundBlockBody:
|
||||
Enabled: true
|
||||
|
||||
# Keeps track of empty lines around class bodies.
|
||||
Layout/EmptyLinesAroundClassBody:
|
||||
Enabled: true
|
||||
|
||||
# Keeps track of empty lines around exception handling keywords.
|
||||
Layout/EmptyLinesAroundExceptionHandlingKeywords:
|
||||
Enabled: false
|
||||
|
||||
# Keeps track of empty lines around method bodies.
|
||||
Layout/EmptyLinesAroundMethodBody:
|
||||
Enabled: true
|
||||
|
||||
# Keeps track of empty lines around module bodies.
|
||||
Layout/EmptyLinesAroundModuleBody:
|
||||
Enabled: true
|
||||
|
||||
# Use Unix-style line endings.
|
||||
Layout/EndOfLine:
|
||||
Enabled: true
|
||||
|
||||
# Checks for a line break before the first parameter in a multi-line method
|
||||
# parameter definition.
|
||||
Layout/FirstMethodParameterLineBreak:
|
||||
Enabled: true
|
||||
|
||||
# Keep indentation straight.
|
||||
Layout/IndentationConsistency:
|
||||
Enabled: true
|
||||
|
||||
# Use 2 spaces for indentation.
|
||||
Layout/IndentationWidth:
|
||||
Enabled: true
|
||||
|
||||
# Checks the indentation of the first line of the right-hand-side of a
|
||||
# multi-line assignment.
|
||||
Layout/IndentAssignment:
|
||||
Enabled: true
|
||||
|
||||
# This cops checks the indentation of the here document bodies.
|
||||
Layout/IndentHeredoc:
|
||||
Enabled: false
|
||||
|
||||
# Comments should start with a space.
|
||||
Layout/LeadingCommentSpace:
|
||||
Enabled: true
|
||||
|
||||
# Checks that the closing brace in an array literal is either on the same line
|
||||
# as the last array element, or a new line.
|
||||
Layout/MultilineArrayBraceLayout:
|
||||
Enabled: true
|
||||
EnforcedStyle: symmetrical
|
||||
|
||||
# Ensures newlines after multiline block do statements.
|
||||
Layout/MultilineBlockLayout:
|
||||
Enabled: true
|
||||
|
||||
# Checks that the closing brace in a hash literal is either on the same line as
|
||||
# the last hash element, or a new line.
|
||||
Layout/MultilineHashBraceLayout:
|
||||
Enabled: true
|
||||
EnforcedStyle: symmetrical
|
||||
|
||||
# Checks that the closing brace in a method call is either on the same line as
|
||||
# the last method argument, or a new line.
|
||||
Layout/MultilineMethodCallBraceLayout:
|
||||
Enabled: false
|
||||
EnforcedStyle: symmetrical
|
||||
|
||||
# Checks indentation of method calls with the dot operator that span more than
|
||||
# one line.
|
||||
Layout/MultilineMethodCallIndentation:
|
||||
Enabled: false
|
||||
|
||||
# Checks that the closing brace in a method definition is symmetrical with
|
||||
# respect to the opening brace and the method parameters.
|
||||
Layout/MultilineMethodDefinitionBraceLayout:
|
||||
Enabled: false
|
||||
|
||||
# Checks indentation of binary operations that span more than one line.
|
||||
Layout/MultilineOperationIndentation:
|
||||
Enabled: true
|
||||
EnforcedStyle: indented
|
||||
|
||||
# Use spaces after colons.
|
||||
Layout/SpaceAfterColon:
|
||||
Enabled: true
|
||||
|
||||
# Use spaces after commas.
|
||||
Layout/SpaceAfterComma:
|
||||
Enabled: true
|
||||
|
||||
# Do not put a space between a method name and the opening parenthesis in a
|
||||
# method definition.
|
||||
Layout/SpaceAfterMethodName:
|
||||
Enabled: true
|
||||
|
||||
# Tracks redundant space after the ! operator.
|
||||
Layout/SpaceAfterNot:
|
||||
Enabled: true
|
||||
|
||||
# Use spaces after semicolons.
|
||||
Layout/SpaceAfterSemicolon:
|
||||
Enabled: true
|
||||
|
||||
# Use space around equals in parameter default
|
||||
Layout/SpaceAroundEqualsInParameterDefault:
|
||||
Enabled: true
|
||||
|
||||
# Use a space around keywords if appropriate.
|
||||
Layout/SpaceAroundKeyword:
|
||||
Enabled: true
|
||||
|
||||
# Use a single space around operators.
|
||||
Layout/SpaceAroundOperators:
|
||||
Enabled: true
|
||||
|
||||
# No spaces before commas.
|
||||
Layout/SpaceBeforeComma:
|
||||
Enabled: true
|
||||
|
||||
# Checks for missing space between code and a comment on the same line.
|
||||
Layout/SpaceBeforeComment:
|
||||
Enabled: true
|
||||
|
||||
# No spaces before semicolons.
|
||||
Layout/SpaceBeforeSemicolon:
|
||||
Enabled: true
|
||||
|
||||
# Checks for spaces inside square brackets.
|
||||
Layout/SpaceInsideBrackets:
|
||||
Enabled: true
|
||||
|
||||
# Use spaces inside hash literal braces - or don't.
|
||||
Layout/SpaceInsideHashLiteralBraces:
|
||||
Enabled: true
|
||||
|
||||
# No spaces inside range literals.
|
||||
Layout/SpaceInsideRangeLiteral:
|
||||
Enabled: true
|
||||
|
||||
# Checks for padding/surrounding spaces inside string interpolation.
|
||||
Layout/SpaceInsideStringInterpolation:
|
||||
EnforcedStyle: no_space
|
||||
Enabled: true
|
||||
|
||||
# No hard tabs.
|
||||
Layout/Tab:
|
||||
Enabled: true
|
||||
|
||||
# Checks trailing blank lines and final newline.
|
||||
Layout/TrailingBlankLines:
|
||||
Enabled: true
|
||||
|
||||
# Style #######################################################################
|
||||
|
||||
# Check the naming of accessor methods for get_/set_.
|
||||
Style/AccessorMethodName:
|
||||
Enabled: false
|
||||
|
|
@ -44,19 +254,6 @@ Style/Alias:
|
|||
EnforcedStyle: prefer_alias_method
|
||||
Enabled: true
|
||||
|
||||
# Align the elements of an array literal if they span more than one line.
|
||||
Style/AlignArray:
|
||||
Enabled: true
|
||||
|
||||
# Align the elements of a hash literal if they span more than one line.
|
||||
Style/AlignHash:
|
||||
Enabled: true
|
||||
|
||||
# Here we check if the parameters on a multi-line method call or
|
||||
# definition are aligned.
|
||||
Style/AlignParameters:
|
||||
Enabled: false
|
||||
|
||||
# Whether `and` and `or` are banned only in conditionals (conditionals)
|
||||
# or completely (always).
|
||||
Style/AndOr:
|
||||
|
|
@ -91,10 +288,6 @@ Style/BlockComments:
|
|||
Style/BlockDelimiters:
|
||||
Enabled: true
|
||||
|
||||
# Put end statement of multiline block on its own line.
|
||||
Style/BlockEndNewline:
|
||||
Enabled: true
|
||||
|
||||
# This cop checks for braces around the last parameter in a method call
|
||||
# if the last parameter is a hash.
|
||||
Style/BracesAroundHashParameters:
|
||||
|
|
@ -104,10 +297,6 @@ Style/BracesAroundHashParameters:
|
|||
Style/CaseEquality:
|
||||
Enabled: false
|
||||
|
||||
# Indentation of when in a case/when/[else/]end.
|
||||
Style/CaseIndentation:
|
||||
Enabled: true
|
||||
|
||||
# Checks for uses of character literals.
|
||||
Style/CharacterLiteral:
|
||||
Enabled: true
|
||||
|
|
@ -142,10 +331,6 @@ Style/ColonMethodCall:
|
|||
Style/CommentAnnotation:
|
||||
Enabled: false
|
||||
|
||||
# Indentation of comments.
|
||||
Style/CommentIndentation:
|
||||
Enabled: true
|
||||
|
||||
# Check for `if` and `case` statements where each branch is used for
|
||||
# assignment to the same variable when using the return of the
|
||||
# condition can be used instead.
|
||||
|
|
@ -164,57 +349,16 @@ Style/DefWithParentheses:
|
|||
Style/Documentation:
|
||||
Enabled: false
|
||||
|
||||
# Multi-line method chaining should be done with leading dots.
|
||||
Style/DotPosition:
|
||||
Enabled: true
|
||||
EnforcedStyle: leading
|
||||
|
||||
# This cop checks for uses of double negation (!!) to convert something
|
||||
# to a boolean value. As this is both cryptic and usually redundant, it
|
||||
# should be avoided.
|
||||
Style/DoubleNegation:
|
||||
Enabled: false
|
||||
|
||||
# Align elses and elsifs correctly.
|
||||
Style/ElseAlignment:
|
||||
Enabled: true
|
||||
|
||||
# Use empty lines between defs.
|
||||
Style/EmptyLineBetweenDefs:
|
||||
Enabled: true
|
||||
|
||||
# Don't use several empty lines in a row.
|
||||
Style/EmptyLines:
|
||||
Enabled: true
|
||||
|
||||
# Keep blank lines around access modifiers.
|
||||
Style/EmptyLinesAroundAccessModifier:
|
||||
Enabled: true
|
||||
|
||||
# Keeps track of empty lines around block bodies.
|
||||
Style/EmptyLinesAroundBlockBody:
|
||||
Enabled: true
|
||||
|
||||
# Keeps track of empty lines around class bodies.
|
||||
Style/EmptyLinesAroundClassBody:
|
||||
Enabled: true
|
||||
|
||||
# Keeps track of empty lines around method bodies.
|
||||
Style/EmptyLinesAroundMethodBody:
|
||||
Enabled: true
|
||||
|
||||
# Keeps track of empty lines around module bodies.
|
||||
Style/EmptyLinesAroundModuleBody:
|
||||
Enabled: true
|
||||
|
||||
# Avoid the use of END blocks.
|
||||
Style/EndBlock:
|
||||
Enabled: true
|
||||
|
||||
# Use Unix-style line endings.
|
||||
Style/EndOfLine:
|
||||
Enabled: true
|
||||
|
||||
# Favor the use of Fixnum#even? && Fixnum#odd?
|
||||
Style/EvenOdd:
|
||||
Enabled: true
|
||||
|
|
@ -223,11 +367,6 @@ Style/EvenOdd:
|
|||
Style/FileName:
|
||||
Enabled: true
|
||||
|
||||
# Checks for a line break before the first parameter in a multi-line method
|
||||
# parameter definition.
|
||||
Style/FirstMethodParameterLineBreak:
|
||||
Enabled: true
|
||||
|
||||
# Checks for flip flops.
|
||||
Style/FlipFlop:
|
||||
Enabled: true
|
||||
|
|
@ -236,6 +375,10 @@ Style/FlipFlop:
|
|||
Style/For:
|
||||
Enabled: true
|
||||
|
||||
# Use a consistent style for format string tokens.
|
||||
Style/FormatStringToken:
|
||||
Enabled: false
|
||||
|
||||
# Checks if there is a magic comment to enforce string literals
|
||||
Style/FrozenStringLiteralComment:
|
||||
Enabled: false
|
||||
|
|
@ -261,31 +404,19 @@ Style/IdenticalConditionalBranches:
|
|||
Style/IfWithSemicolon:
|
||||
Enabled: true
|
||||
|
||||
# Checks the indentation of the first line of the right-hand-side of a
|
||||
# multi-line assignment.
|
||||
Style/IndentAssignment:
|
||||
Enabled: true
|
||||
|
||||
# Keep indentation straight.
|
||||
Style/IndentationConsistency:
|
||||
Enabled: true
|
||||
|
||||
# Use 2 spaces for indentation.
|
||||
Style/IndentationWidth:
|
||||
Enabled: true
|
||||
|
||||
# Use Kernel#loop for infinite loops.
|
||||
Style/InfiniteLoop:
|
||||
Enabled: true
|
||||
|
||||
# Use the inverse method instead of `!.method`
|
||||
# if an inverse method is defined.
|
||||
Style/InverseMethods:
|
||||
Enabled: false
|
||||
|
||||
# Use lambda.call(...) instead of lambda.(...).
|
||||
Style/LambdaCall:
|
||||
Enabled: true
|
||||
|
||||
# Comments should start with a space.
|
||||
Style/LeadingCommentSpace:
|
||||
Enabled: true
|
||||
|
||||
# Checks if the method definitions have or don't have parentheses.
|
||||
Style/MethodDefParentheses:
|
||||
Enabled: true
|
||||
|
|
@ -298,55 +429,23 @@ Style/MethodName:
|
|||
Style/ModuleFunction:
|
||||
Enabled: false
|
||||
|
||||
# Checks that the closing brace in an array literal is either on the same line
|
||||
# as the last array element, or a new line.
|
||||
Style/MultilineArrayBraceLayout:
|
||||
Enabled: true
|
||||
EnforcedStyle: symmetrical
|
||||
|
||||
# Avoid multi-line chains of blocks.
|
||||
Style/MultilineBlockChain:
|
||||
Enabled: true
|
||||
|
||||
# Ensures newlines after multiline block do statements.
|
||||
Style/MultilineBlockLayout:
|
||||
Enabled: true
|
||||
|
||||
# Checks that the closing brace in a hash literal is either on the same line as
|
||||
# the last hash element, or a new line.
|
||||
Style/MultilineHashBraceLayout:
|
||||
Enabled: true
|
||||
EnforcedStyle: symmetrical
|
||||
|
||||
# Do not use then for multi-line if/unless.
|
||||
Style/MultilineIfThen:
|
||||
Enabled: true
|
||||
|
||||
# Checks that the closing brace in a method call is either on the same line as
|
||||
# the last method argument, or a new line.
|
||||
Style/MultilineMethodCallBraceLayout:
|
||||
Enabled: false
|
||||
EnforcedStyle: symmetrical
|
||||
|
||||
# Checks indentation of method calls with the dot operator that span more than
|
||||
# one line.
|
||||
Style/MultilineMethodCallIndentation:
|
||||
Enabled: false
|
||||
|
||||
# Checks that the closing brace in a method definition is symmetrical with
|
||||
# respect to the opening brace and the method parameters.
|
||||
Style/MultilineMethodDefinitionBraceLayout:
|
||||
Enabled: false
|
||||
|
||||
# Checks indentation of binary operations that span more than one line.
|
||||
Style/MultilineOperationIndentation:
|
||||
Enabled: true
|
||||
EnforcedStyle: indented
|
||||
|
||||
# Avoid multi-line `? :` (the ternary operator), use if/unless instead.
|
||||
Style/MultilineTernaryOperator:
|
||||
Enabled: true
|
||||
|
||||
# Avoid comparing a variable with multiple items in a conditional,
|
||||
# use Array#include? instead.
|
||||
Style/MultipleComparison:
|
||||
Enabled: false
|
||||
|
||||
# This cop checks whether some constant value isn't a
|
||||
# mutable literal (e.g. array or hash).
|
||||
Style/MutableConstant:
|
||||
|
|
@ -421,68 +520,6 @@ Style/SignalException:
|
|||
EnforcedStyle: only_raise
|
||||
Enabled: true
|
||||
|
||||
# Use spaces after colons.
|
||||
Style/SpaceAfterColon:
|
||||
Enabled: true
|
||||
|
||||
# Use spaces after commas.
|
||||
Style/SpaceAfterComma:
|
||||
Enabled: true
|
||||
|
||||
# Do not put a space between a method name and the opening parenthesis in a
|
||||
# method definition.
|
||||
Style/SpaceAfterMethodName:
|
||||
Enabled: true
|
||||
|
||||
# Tracks redundant space after the ! operator.
|
||||
Style/SpaceAfterNot:
|
||||
Enabled: true
|
||||
|
||||
# Use spaces after semicolons.
|
||||
Style/SpaceAfterSemicolon:
|
||||
Enabled: true
|
||||
|
||||
# Use space around equals in parameter default
|
||||
Style/SpaceAroundEqualsInParameterDefault:
|
||||
Enabled: true
|
||||
|
||||
# Use a space around keywords if appropriate.
|
||||
Style/SpaceAroundKeyword:
|
||||
Enabled: true
|
||||
|
||||
# Use a single space around operators.
|
||||
Style/SpaceAroundOperators:
|
||||
Enabled: true
|
||||
|
||||
# No spaces before commas.
|
||||
Style/SpaceBeforeComma:
|
||||
Enabled: true
|
||||
|
||||
# Checks for missing space between code and a comment on the same line.
|
||||
Style/SpaceBeforeComment:
|
||||
Enabled: true
|
||||
|
||||
# No spaces before semicolons.
|
||||
Style/SpaceBeforeSemicolon:
|
||||
Enabled: true
|
||||
|
||||
# Checks for spaces inside square brackets.
|
||||
Style/SpaceInsideBrackets:
|
||||
Enabled: true
|
||||
|
||||
# Use spaces inside hash literal braces - or don't.
|
||||
Style/SpaceInsideHashLiteralBraces:
|
||||
Enabled: true
|
||||
|
||||
# No spaces inside range literals.
|
||||
Style/SpaceInsideRangeLiteral:
|
||||
Enabled: true
|
||||
|
||||
# Checks for padding/surrounding spaces inside string interpolation.
|
||||
Style/SpaceInsideStringInterpolation:
|
||||
EnforcedStyle: no_space
|
||||
Enabled: true
|
||||
|
||||
# Check for the usage of parentheses around stabby lambda arguments.
|
||||
Style/StabbyLambdaParentheses:
|
||||
EnforcedStyle: require_parentheses
|
||||
|
|
@ -498,13 +535,9 @@ Style/StringMethods:
|
|||
intern: to_sym
|
||||
Enabled: true
|
||||
|
||||
# No hard tabs.
|
||||
Style/Tab:
|
||||
Enabled: true
|
||||
|
||||
# Checks trailing blank lines and final newline.
|
||||
Style/TrailingBlankLines:
|
||||
Enabled: true
|
||||
# Use %i or %I for arrays of symbols.
|
||||
Style/SymbolArray:
|
||||
Enabled: false
|
||||
|
||||
# This cop checks for trailing comma in array and hash literals.
|
||||
Style/TrailingCommaInLiteral:
|
||||
|
|
@ -553,6 +586,10 @@ Style/WhileUntilModifier:
|
|||
Style/WordArray:
|
||||
Enabled: true
|
||||
|
||||
# Do not use literals as the first operand of a comparison.
|
||||
Style/YodaCondition:
|
||||
Enabled: false
|
||||
|
||||
# Use `proc` instead of `Proc.new`.
|
||||
Style/Proc:
|
||||
Enabled: true
|
||||
|
|
@ -608,6 +645,11 @@ Metrics/PerceivedComplexity:
|
|||
|
||||
# Lint ########################################################################
|
||||
|
||||
# Checks for ambiguous block association with method when param passed without
|
||||
# parentheses.
|
||||
Lint/AmbiguousBlockAssociation:
|
||||
Enabled: false
|
||||
|
||||
# Checks for ambiguous operators in the first argument of a method invocation
|
||||
# without parentheses.
|
||||
Lint/AmbiguousOperator:
|
||||
|
|
@ -809,6 +851,10 @@ Lint/Void:
|
|||
|
||||
# Performance #################################################################
|
||||
|
||||
# Use `caller(n..n)` instead of `caller`.
|
||||
Performance/Caller:
|
||||
Enabled: false
|
||||
|
||||
# Use `casecmp` rather than `downcase ==`.
|
||||
Performance/Casecmp:
|
||||
Enabled: true
|
||||
|
|
@ -883,14 +929,23 @@ Rails/ActionFilter:
|
|||
Enabled: true
|
||||
EnforcedStyle: action
|
||||
|
||||
# Check that models subclass ApplicationRecord.
|
||||
Rails/ApplicationRecord:
|
||||
Enabled: false
|
||||
|
||||
# Enforce using `blank?` and `present?`.
|
||||
Rails/Blank:
|
||||
Enabled: false
|
||||
|
||||
# Checks the correct usage of date aware methods, such as `Date.today`,
|
||||
# `Date.current`, etc.
|
||||
Rails/Date:
|
||||
Enabled: false
|
||||
|
||||
# Prefer delegate method for delegations.
|
||||
# Disabled per https://gitlab.com/gitlab-org/gitlab-ce/issues/35869
|
||||
Rails/Delegate:
|
||||
Enabled: true
|
||||
Enabled: false
|
||||
|
||||
# This cop checks dynamic `find_by_*` methods.
|
||||
Rails/DynamicFindBy:
|
||||
|
|
@ -939,10 +994,18 @@ Rails/OutputSafety:
|
|||
Rails/PluralizationGrammar:
|
||||
Enabled: true
|
||||
|
||||
# Enforce using `blank?` and `present?`.
|
||||
Rails/Present:
|
||||
Enabled: false
|
||||
|
||||
# Checks for `read_attribute(:attr)` and `write_attribute(:attr, val)`.
|
||||
Rails/ReadWriteAttribute:
|
||||
Enabled: false
|
||||
|
||||
# Do not assign relative date to constants.
|
||||
Rails/RelativeDateConstant:
|
||||
Enabled: false
|
||||
|
||||
# Checks the arguments of ActiveRecord scopes.
|
||||
Rails/ScopeArgs:
|
||||
Enabled: true
|
||||
|
|
|
|||
|
|
@ -1,26 +1,89 @@
|
|||
# This configuration was generated by
|
||||
# `rubocop --auto-gen-config --exclude-limit 0`
|
||||
# on 2017-04-07 20:17:35 -0400 using RuboCop version 0.47.1.
|
||||
# on 2017-07-10 01:48:30 +0900 using RuboCop version 0.49.1.
|
||||
# The point is for the user to remove these configuration records
|
||||
# one by one as the offenses are removed from the code base.
|
||||
# Note that changes in the inspected code, or installation of new
|
||||
# versions of RuboCop, may require this file to be generated again.
|
||||
|
||||
# Offense count: 233
|
||||
# Offense count: 181
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: AllowForAlignment, ForceEqualSignAlignment.
|
||||
Layout/ExtraSpacing:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 119
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle, SupportedStyles, IndentationWidth.
|
||||
# SupportedStyles: special_inside_parentheses, consistent, align_brackets
|
||||
Layout/IndentArray:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 208
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle, SupportedStyles, IndentationWidth.
|
||||
# SupportedStyles: special_inside_parentheses, consistent, align_braces
|
||||
Layout/IndentHash:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 174
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
||||
# SupportedStyles: space, no_space
|
||||
Layout/SpaceBeforeBlockBraces:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 8
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: AllowForAlignment.
|
||||
Layout/SpaceBeforeFirstArg:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 64
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
||||
# SupportedStyles: require_no_space, require_space
|
||||
Layout/SpaceInLambdaLiteral:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 256
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle, SupportedStyles, EnforcedStyleForEmptyBraces, SupportedStylesForEmptyBraces, SpaceBeforeBlockParameters.
|
||||
# SupportedStyles: space, no_space
|
||||
# SupportedStylesForEmptyBraces: space, no_space
|
||||
Layout/SpaceInsideBlockBraces:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 135
|
||||
# Cop supports --auto-correct.
|
||||
Layout/SpaceInsideParens:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 14
|
||||
# Cop supports --auto-correct.
|
||||
Layout/SpaceInsidePercentLiteralDelimiters:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 89
|
||||
# Cop supports --auto-correct.
|
||||
Layout/TrailingWhitespace:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 272
|
||||
RSpec/EmptyLineAfterFinalLet:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 167
|
||||
# Offense count: 181
|
||||
RSpec/EmptyLineAfterSubject:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 72
|
||||
# Offense count: 78
|
||||
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
||||
# SupportedStyles: implicit, each, example
|
||||
RSpec/HookArgument:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 11
|
||||
# Offense count: 9
|
||||
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
||||
# SupportedStyles: it_behaves_like, it_should_behave_like
|
||||
RSpec/ItBehavesLike:
|
||||
|
|
@ -30,19 +93,19 @@ RSpec/ItBehavesLike:
|
|||
RSpec/IteratedExpectation:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 3
|
||||
# Offense count: 2
|
||||
RSpec/OverwritingSetup:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 34
|
||||
# Offense count: 36
|
||||
RSpec/RepeatedExample:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 43
|
||||
# Offense count: 86
|
||||
RSpec/ScatteredLet:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 32
|
||||
# Offense count: 20
|
||||
RSpec/ScatteredSetup:
|
||||
Enabled: false
|
||||
|
||||
|
|
@ -50,7 +113,7 @@ RSpec/ScatteredSetup:
|
|||
RSpec/SharedContext:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 150
|
||||
# Offense count: 115
|
||||
Rails/FilePath:
|
||||
Enabled: false
|
||||
|
||||
|
|
@ -60,90 +123,71 @@ Rails/FilePath:
|
|||
Rails/ReversibleMigration:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 302
|
||||
# Offense count: 336
|
||||
# Configuration parameters: Blacklist.
|
||||
# Blacklist: decrement!, decrement_counter, increment!, increment_counter, toggle!, touch, update_all, update_attribute, update_column, update_columns, update_counters
|
||||
Rails/SkipsModelValidations:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 7
|
||||
# Offense count: 11
|
||||
# Cop supports --auto-correct.
|
||||
Security/YAMLLoad:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 59
|
||||
# Offense count: 58
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
||||
# SupportedStyles: percent_q, bare_percent
|
||||
Style/BarePercentLiterals:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 5
|
||||
# Offense count: 6
|
||||
# Cop supports --auto-correct.
|
||||
Style/EachWithObject:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 28
|
||||
# Offense count: 31
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
||||
# SupportedStyles: empty, nil, both
|
||||
Style/EmptyElse:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 4
|
||||
# Offense count: 9
|
||||
# Cop supports --auto-correct.
|
||||
Style/EmptyLiteral:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 59
|
||||
# Offense count: 78
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
||||
# SupportedStyles: compact, expanded
|
||||
Style/EmptyMethod:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 214
|
||||
# Offense count: 23
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: AllowForAlignment, ForceEqualSignAlignment.
|
||||
Style/ExtraSpacing:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 9
|
||||
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
||||
# SupportedStyles: format, sprintf, percent
|
||||
Style/FormatString:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 285
|
||||
# Offense count: 301
|
||||
# Configuration parameters: MinBodyLength.
|
||||
Style/GuardClause:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 16
|
||||
# Offense count: 18
|
||||
Style/IfInsideElse:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 186
|
||||
# Offense count: 182
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: MaxLineLength.
|
||||
Style/IfUnlessModifier:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 99
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle, SupportedStyles, IndentationWidth.
|
||||
# SupportedStyles: special_inside_parentheses, consistent, align_brackets
|
||||
Style/IndentArray:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 160
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle, SupportedStyles, IndentationWidth.
|
||||
# SupportedStyles: special_inside_parentheses, consistent, align_braces
|
||||
Style/IndentHash:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 50
|
||||
# Offense count: 52
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
||||
# SupportedStyles: line_count_dependent, lambda, literal
|
||||
|
|
@ -155,63 +199,63 @@ Style/Lambda:
|
|||
Style/LineEndConcatenation:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 34
|
||||
# Offense count: 40
|
||||
# Cop supports --auto-correct.
|
||||
Style/MethodCallWithoutArgsParentheses:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 10
|
||||
# Offense count: 13
|
||||
Style/MethodMissing:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 3
|
||||
# Offense count: 6
|
||||
# Cop supports --auto-correct.
|
||||
Style/MultilineIfModifier:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 24
|
||||
# Offense count: 26
|
||||
# Cop supports --auto-correct.
|
||||
Style/NestedParenthesizedCalls:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 18
|
||||
# Offense count: 20
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle, MinBodyLength, SupportedStyles.
|
||||
# SupportedStyles: skip_modifier_ifs, always
|
||||
Style/Next:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 37
|
||||
# Offense count: 45
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedOctalStyle, SupportedOctalStyles.
|
||||
# SupportedOctalStyles: zero_with_o, zero_only
|
||||
Style/NumericLiteralPrefix:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 88
|
||||
# Offense count: 98
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: AutoCorrect, EnforcedStyle, SupportedStyles.
|
||||
# SupportedStyles: predicate, comparison
|
||||
Style/NumericPredicate:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 36
|
||||
# Offense count: 42
|
||||
# Cop supports --auto-correct.
|
||||
Style/ParallelAssignment:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 570
|
||||
# Offense count: 800
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: PreferredDelimiters.
|
||||
Style/PercentLiteralDelimiters:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 14
|
||||
# Offense count: 15
|
||||
# Cop supports --auto-correct.
|
||||
Style/PerlBackrefs:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 83
|
||||
# Offense count: 105
|
||||
# Configuration parameters: NamePrefix, NamePrefixBlacklist, NameWhitelist.
|
||||
# NamePrefix: is_, has_, have_
|
||||
# NamePrefixBlacklist: is_, has_, have_
|
||||
|
|
@ -219,47 +263,47 @@ Style/PerlBackrefs:
|
|||
Style/PredicateName:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 65
|
||||
# Offense count: 58
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
||||
# SupportedStyles: compact, exploded
|
||||
Style/RaiseArgs:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 5
|
||||
# Offense count: 6
|
||||
# Cop supports --auto-correct.
|
||||
Style/RedundantBegin:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 32
|
||||
# Offense count: 37
|
||||
# Cop supports --auto-correct.
|
||||
Style/RedundantFreeze:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 15
|
||||
# Offense count: 14
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: AllowMultipleReturnValues.
|
||||
Style/RedundantReturn:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 382
|
||||
# Offense count: 406
|
||||
# Cop supports --auto-correct.
|
||||
Style/RedundantSelf:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 111
|
||||
# Offense count: 115
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle, SupportedStyles, AllowInnerSlashes.
|
||||
# SupportedStyles: slashes, percent_r, mixed
|
||||
Style/RegexpLiteral:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 24
|
||||
# Offense count: 29
|
||||
# Cop supports --auto-correct.
|
||||
Style/RescueModifier:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 7
|
||||
# Offense count: 8
|
||||
# Cop supports --auto-correct.
|
||||
Style/SelfAssignment:
|
||||
Enabled: false
|
||||
|
|
@ -270,101 +314,58 @@ Style/SelfAssignment:
|
|||
Style/SingleLineMethods:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 168
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
||||
# SupportedStyles: space, no_space
|
||||
Style/SpaceBeforeBlockBraces:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 8
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: AllowForAlignment.
|
||||
Style/SpaceBeforeFirstArg:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 46
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
||||
# SupportedStyles: require_no_space, require_space
|
||||
Style/SpaceInLambdaLiteral:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 229
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle, SupportedStyles, EnforcedStyleForEmptyBraces, SupportedStylesForEmptyBraces, SpaceBeforeBlockParameters.
|
||||
# SupportedStyles: space, no_space
|
||||
# SupportedStylesForEmptyBraces: space, no_space
|
||||
Style/SpaceInsideBlockBraces:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 116
|
||||
# Cop supports --auto-correct.
|
||||
Style/SpaceInsideParens:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 12
|
||||
# Cop supports --auto-correct.
|
||||
Style/SpaceInsidePercentLiteralDelimiters:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 57
|
||||
# Offense count: 64
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: SupportedStyles.
|
||||
# SupportedStyles: use_perl_names, use_english_names
|
||||
Style/SpecialGlobalVars:
|
||||
EnforcedStyle: use_perl_names
|
||||
|
||||
# Offense count: 42
|
||||
# Offense count: 44
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
||||
# SupportedStyles: single_quotes, double_quotes
|
||||
Style/StringLiteralsInInterpolation:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 64
|
||||
# Offense count: 84
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: IgnoredMethods.
|
||||
# IgnoredMethods: respond_to, define_method
|
||||
Style/SymbolProc:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 6
|
||||
# Offense count: 8
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle, SupportedStyles, AllowSafeAssignment.
|
||||
# SupportedStyles: require_parentheses, require_no_parentheses, require_parentheses_when_complex
|
||||
Style/TernaryParentheses:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 18
|
||||
# Offense count: 17
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: AllowNamedUnderscoreVariables.
|
||||
Style/TrailingUnderscoreVariable:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 78
|
||||
# Cop supports --auto-correct.
|
||||
Style/TrailingWhitespace:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 3
|
||||
# Offense count: 4
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: ExactNameMatch, AllowPredicates, AllowDSLWriters, IgnoreClassMethods, Whitelist.
|
||||
# Whitelist: to_ary, to_a, to_c, to_enum, to_h, to_hash, to_i, to_int, to_io, to_open, to_path, to_proc, to_r, to_regexp, to_str, to_s, to_sym
|
||||
Style/TrivialAccessors:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 6
|
||||
# Offense count: 5
|
||||
# Cop supports --auto-correct.
|
||||
Style/UnlessElse:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 24
|
||||
# Offense count: 28
|
||||
# Cop supports --auto-correct.
|
||||
Style/UnneededInterpolation:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 8
|
||||
# Offense count: 11
|
||||
# Cop supports --auto-correct.
|
||||
Style/ZeroLengthPredicate:
|
||||
Enabled: false
|
||||
|
|
|
|||
10
CHANGELOG.md
10
CHANGELOG.md
|
|
@ -2,6 +2,16 @@
|
|||
documentation](doc/development/changelog.md) for instructions on adding your own
|
||||
entry.
|
||||
|
||||
## 9.4.3 (2017-07-31)
|
||||
|
||||
- Fix Prometheus client PID reuse bug. !13130
|
||||
- Improve deploy environment chatops slash command. !13150
|
||||
- Fix asynchronous javascript paths when GitLab is installed under a relative URL. !13165
|
||||
- Fix LDAP authentication to Git repository or container registry.
|
||||
- Fixed new navigation breadcrumb title on help pages.
|
||||
- Ensure filesystem metrics test files are deleted.
|
||||
- Properly affixes nav bar in job view in microsoft edge.
|
||||
|
||||
## 9.4.2 (2017-07-28)
|
||||
|
||||
- Fix job merge request link to a forked source project. !12965
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
0.25.0
|
||||
0.26.0
|
||||
|
|
|
|||
4
Gemfile
4
Gemfile
|
|
@ -339,8 +339,8 @@ group :development, :test do
|
|||
gem 'spring-commands-rspec', '~> 1.0.4'
|
||||
gem 'spring-commands-spinach', '~> 1.1.0'
|
||||
|
||||
gem 'rubocop', '~> 0.47.1', require: false
|
||||
gem 'rubocop-rspec', '~> 1.15.0', require: false
|
||||
gem 'rubocop', '~> 0.49.1', require: false
|
||||
gem 'rubocop-rspec', '~> 1.15.1', require: false
|
||||
gem 'scss_lint', '~> 0.54.0', require: false
|
||||
gem 'haml_lint', '~> 0.21.0', require: false
|
||||
gem 'simplecov', '~> 0.14.0', require: false
|
||||
|
|
|
|||
12
Gemfile.lock
12
Gemfile.lock
|
|
@ -545,6 +545,7 @@ GEM
|
|||
rubypants (~> 0.2)
|
||||
orm_adapter (0.5.0)
|
||||
os (0.9.6)
|
||||
parallel (1.11.2)
|
||||
paranoia (2.3.1)
|
||||
activerecord (>= 4.0, < 5.2)
|
||||
parser (2.4.0.0)
|
||||
|
|
@ -730,13 +731,14 @@ GEM
|
|||
pg
|
||||
rails
|
||||
sqlite3
|
||||
rubocop (0.47.1)
|
||||
rubocop (0.49.1)
|
||||
parallel (~> 1.10)
|
||||
parser (>= 2.3.3.1, < 3.0)
|
||||
powerpack (~> 0.1)
|
||||
rainbow (>= 1.99.1, < 3.0)
|
||||
ruby-progressbar (~> 1.7)
|
||||
unicode-display_width (~> 1.0, >= 1.0.1)
|
||||
rubocop-rspec (1.15.0)
|
||||
rubocop-rspec (1.15.1)
|
||||
rubocop (>= 0.42.0)
|
||||
ruby-fogbugz (0.2.1)
|
||||
crack (~> 0.4)
|
||||
|
|
@ -868,7 +870,7 @@ GEM
|
|||
unf (0.1.4)
|
||||
unf_ext
|
||||
unf_ext (0.0.7.2)
|
||||
unicode-display_width (1.1.3)
|
||||
unicode-display_width (1.3.0)
|
||||
unicorn (5.1.0)
|
||||
kgio (~> 2.6)
|
||||
raindrops (~> 0.7)
|
||||
|
|
@ -1078,8 +1080,8 @@ DEPENDENCIES
|
|||
rspec-retry (~> 0.4.5)
|
||||
rspec-set (~> 0.1.3)
|
||||
rspec_profiling (~> 0.0.5)
|
||||
rubocop (~> 0.47.1)
|
||||
rubocop-rspec (~> 1.15.0)
|
||||
rubocop (~> 0.49.1)
|
||||
rubocop-rspec (~> 1.15.1)
|
||||
ruby-fogbugz (~> 0.2.1)
|
||||
ruby-prof (~> 0.16.2)
|
||||
ruby_parser (~> 3.8)
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import BlobFileDropzone from '../blob/blob_file_dropzone';
|
|||
$(() => {
|
||||
const editBlobForm = $('.js-edit-blob-form');
|
||||
const uploadBlobForm = $('.js-upload-blob-form');
|
||||
const deleteBlobForm = $('.js-delete-blob-form');
|
||||
|
||||
if (editBlobForm.length) {
|
||||
const urlRoot = editBlobForm.data('relative-url-root');
|
||||
|
|
@ -30,4 +31,8 @@ $(() => {
|
|||
'.btn-upload-file',
|
||||
);
|
||||
}
|
||||
|
||||
if (deleteBlobForm.length) {
|
||||
new NewCommitForm(deleteBlobForm);
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ window.Build = (function () {
|
|||
$(window)
|
||||
.off('scroll')
|
||||
.on('scroll', () => {
|
||||
const contentHeight = this.$buildTraceOutput.prop('scrollHeight');
|
||||
const contentHeight = this.$buildTraceOutput.height();
|
||||
if (contentHeight > this.windowSize) {
|
||||
// means the user did not scroll, the content was updated.
|
||||
this.windowSize = contentHeight;
|
||||
|
|
@ -105,16 +105,17 @@ window.Build = (function () {
|
|||
};
|
||||
|
||||
Build.prototype.canScroll = function () {
|
||||
return document.body.scrollHeight > window.innerHeight;
|
||||
return $(document).height() > $(window).height();
|
||||
};
|
||||
|
||||
Build.prototype.toggleScroll = function () {
|
||||
const currentPosition = document.body.scrollTop;
|
||||
const windowHeight = window.innerHeight;
|
||||
const currentPosition = $(document).scrollTop();
|
||||
const scrollHeight = $(document).height();
|
||||
|
||||
const windowHeight = $(window).height();
|
||||
if (this.canScroll()) {
|
||||
if (currentPosition > 0 &&
|
||||
(document.body.scrollHeight - currentPosition !== windowHeight)) {
|
||||
(scrollHeight - currentPosition !== windowHeight)) {
|
||||
// User is in the middle of the log
|
||||
|
||||
this.toggleDisableButton(this.$scrollTopBtn, false);
|
||||
|
|
@ -124,7 +125,7 @@ window.Build = (function () {
|
|||
|
||||
this.toggleDisableButton(this.$scrollTopBtn, true);
|
||||
this.toggleDisableButton(this.$scrollBottomBtn, false);
|
||||
} else if (document.body.scrollHeight - currentPosition === windowHeight) {
|
||||
} else if (scrollHeight - currentPosition === windowHeight) {
|
||||
// User is at the bottom of the build log.
|
||||
|
||||
this.toggleDisableButton(this.$scrollTopBtn, false);
|
||||
|
|
@ -137,7 +138,7 @@ window.Build = (function () {
|
|||
};
|
||||
|
||||
Build.prototype.scrollDown = function () {
|
||||
document.body.scrollTop = document.body.scrollHeight;
|
||||
$(document).scrollTop($(document).height());
|
||||
};
|
||||
|
||||
Build.prototype.scrollToBottom = function () {
|
||||
|
|
@ -147,7 +148,7 @@ window.Build = (function () {
|
|||
};
|
||||
|
||||
Build.prototype.scrollToTop = function () {
|
||||
document.body.scrollTop = 0;
|
||||
$(document).scrollTop(0);
|
||||
this.hasBeenScrolled = true;
|
||||
this.toggleScroll();
|
||||
};
|
||||
|
|
@ -178,7 +179,7 @@ window.Build = (function () {
|
|||
this.state = log.state;
|
||||
}
|
||||
|
||||
this.windowSize = this.$buildTraceOutput.prop('scrollHeight');
|
||||
this.windowSize = this.$buildTraceOutput.height();
|
||||
|
||||
if (log.append) {
|
||||
this.$buildTraceOutput.append(log.html);
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
/* global LabelsSelect */
|
||||
/* global MilestoneSelect */
|
||||
/* global Commit */
|
||||
/* global CommitsList */
|
||||
/* global NewBranchForm */
|
||||
/* global NotificationsForm */
|
||||
/* global NotificationsDropdown */
|
||||
|
|
@ -19,15 +20,20 @@
|
|||
/* global Search */
|
||||
/* global Admin */
|
||||
/* global NamespaceSelects */
|
||||
/* global NewCommitForm */
|
||||
/* global NewBranchForm */
|
||||
/* global Project */
|
||||
/* global ProjectAvatar */
|
||||
/* global MergeRequest */
|
||||
/* global Compare */
|
||||
/* global CompareAutocomplete */
|
||||
/* global ProjectFindFile */
|
||||
/* global ProjectNew */
|
||||
/* global ProjectShow */
|
||||
/* global ProjectImport */
|
||||
/* global Labels */
|
||||
/* global Shortcuts */
|
||||
/* global ShortcutsFindFile */
|
||||
/* global Sidebar */
|
||||
/* global ShortcutsWiki */
|
||||
|
||||
|
|
@ -138,6 +144,8 @@ import FeatureHelper from './helpers/feature_helper';
|
|||
.init();
|
||||
}
|
||||
|
||||
const filteredSearchEnabled = gl.FilteredSearchManager && document.querySelector('.filtered-search');
|
||||
|
||||
switch (page) {
|
||||
case 'profiles:preferences:show':
|
||||
initExperimentalFlags();
|
||||
|
|
@ -154,7 +162,7 @@ import FeatureHelper from './helpers/feature_helper';
|
|||
break;
|
||||
case 'projects:merge_requests:index':
|
||||
case 'projects:issues:index':
|
||||
if (gl.FilteredSearchManager && document.querySelector('.filtered-search')) {
|
||||
if (filteredSearchEnabled) {
|
||||
const filteredSearchManager = new gl.FilteredSearchManager(page === 'projects:issues:index' ? 'issues' : 'merge_requests');
|
||||
filteredSearchManager.setup();
|
||||
}
|
||||
|
|
@ -182,11 +190,17 @@ import FeatureHelper from './helpers/feature_helper';
|
|||
break;
|
||||
case 'dashboard:issues':
|
||||
case 'dashboard:merge_requests':
|
||||
case 'groups:issues':
|
||||
case 'groups:merge_requests':
|
||||
new ProjectSelect();
|
||||
initLegacyFilters();
|
||||
break;
|
||||
case 'groups:issues':
|
||||
if (filteredSearchEnabled) {
|
||||
const filteredSearchManager = new gl.FilteredSearchManager('issues');
|
||||
filteredSearchManager.setup();
|
||||
}
|
||||
new ProjectSelect();
|
||||
break;
|
||||
case 'dashboard:todos:index':
|
||||
new Todos();
|
||||
break;
|
||||
|
|
@ -200,7 +214,6 @@ import FeatureHelper from './helpers/feature_helper';
|
|||
break;
|
||||
case 'explore:groups:index':
|
||||
new GroupsList();
|
||||
|
||||
const landingElement = document.querySelector('.js-explore-groups-landing');
|
||||
if (!landingElement) break;
|
||||
const exploreGroupsLanding = new Landing(
|
||||
|
|
@ -223,6 +236,10 @@ import FeatureHelper from './helpers/feature_helper';
|
|||
case 'projects:compare:show':
|
||||
new gl.Diff();
|
||||
break;
|
||||
case 'projects:branches:new':
|
||||
case 'projects:branches:create':
|
||||
new NewBranchForm($('.js-create-branch-form'), JSON.parse(document.getElementById('availableRefs').innerHTML));
|
||||
break;
|
||||
case 'projects:branches:index':
|
||||
gl.AjaxLoadingSpinner.init();
|
||||
new DeleteModal();
|
||||
|
|
@ -310,19 +327,24 @@ import FeatureHelper from './helpers/feature_helper';
|
|||
container: '.js-commit-pipeline-graph',
|
||||
}).bindEvents();
|
||||
initNotes();
|
||||
$('.commit-info.branches').load(document.querySelector('.js-commit-box').dataset.commitPath);
|
||||
break;
|
||||
case 'projects:commit:pipelines':
|
||||
new MiniPipelineGraph({
|
||||
container: '.js-commit-pipeline-graph',
|
||||
}).bindEvents();
|
||||
break;
|
||||
case 'projects:commits:show':
|
||||
shortcut_handler = new ShortcutsNavigation();
|
||||
GpgBadges.fetch();
|
||||
$('.commit-info.branches').load(document.querySelector('.js-commit-box').dataset.commitPath);
|
||||
break;
|
||||
case 'projects:activity':
|
||||
new gl.Activities();
|
||||
shortcut_handler = new ShortcutsNavigation();
|
||||
break;
|
||||
case 'projects:commits:show':
|
||||
CommitsList.init(document.querySelector('.js-project-commits-show').dataset.commitsLimit);
|
||||
new gl.Activities();
|
||||
shortcut_handler = new ShortcutsNavigation();
|
||||
GpgBadges.fetch();
|
||||
break;
|
||||
case 'projects:show':
|
||||
shortcut_handler = new ShortcutsNavigation();
|
||||
new NotificationsForm();
|
||||
|
|
@ -335,6 +357,9 @@ import FeatureHelper from './helpers/feature_helper';
|
|||
case 'projects:edit':
|
||||
setupProjectEdit();
|
||||
break;
|
||||
case 'projects:imports:show':
|
||||
new ProjectImport();
|
||||
break;
|
||||
case 'projects:pipelines:new':
|
||||
new NewBranchForm($('.js-new-pipeline-form'));
|
||||
break;
|
||||
|
|
@ -394,11 +419,19 @@ import FeatureHelper from './helpers/feature_helper';
|
|||
|
||||
new TreeView();
|
||||
new BlobViewer();
|
||||
new NewCommitForm($('.js-create-dir-form'));
|
||||
$('#tree-slider').waitForImages(function() {
|
||||
gl.utils.ajaxGet(document.querySelector('.js-tree-content').dataset.logsPath);
|
||||
});
|
||||
break;
|
||||
case 'projects:find_file:show':
|
||||
const findElement = document.querySelector('.js-file-finder');
|
||||
const projectFindFile = new ProjectFindFile($(".file-finder-holder"), {
|
||||
url: findElement.dataset.fileFindUrl,
|
||||
treeUrl: findElement.dataset.findTreeUrl,
|
||||
blobUrlTemplate: findElement.dataset.blobUrlTemplate,
|
||||
});
|
||||
new ShortcutsFindFile(projectFindFile);
|
||||
shortcut_handler = true;
|
||||
break;
|
||||
case 'projects:blob:show':
|
||||
|
|
|
|||
|
|
@ -11,6 +11,16 @@ const Ajax = {
|
|||
|
||||
if (!self.destroyed) self.hook.list[config.method].call(self.hook.list, data);
|
||||
},
|
||||
preprocessing: function preprocessing(config, data) {
|
||||
let results = data;
|
||||
|
||||
if (config.preprocessing && !data.preprocessed) {
|
||||
results = config.preprocessing(data);
|
||||
AjaxCache.override(config.endpoint, results);
|
||||
}
|
||||
|
||||
return results;
|
||||
},
|
||||
init: function init(hook) {
|
||||
var self = this;
|
||||
self.destroyed = false;
|
||||
|
|
@ -31,7 +41,8 @@ const Ajax = {
|
|||
dynamicList.outerHTML = loadingTemplate.outerHTML;
|
||||
}
|
||||
|
||||
AjaxCache.retrieve(config.endpoint)
|
||||
return AjaxCache.retrieve(config.endpoint)
|
||||
.then(self.preprocessing.bind(null, config))
|
||||
.then((data) => self._loadData(data, config, self))
|
||||
.catch(config.onError);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import './filtered_search_dropdown';
|
|||
|
||||
class DropdownNonUser extends gl.FilteredSearchDropdown {
|
||||
constructor(options = {}) {
|
||||
const { input, endpoint, symbol } = options;
|
||||
const { input, endpoint, symbol, preprocessing } = options;
|
||||
super(options);
|
||||
this.symbol = symbol;
|
||||
this.config = {
|
||||
|
|
@ -14,6 +14,7 @@ class DropdownNonUser extends gl.FilteredSearchDropdown {
|
|||
endpoint,
|
||||
method: 'setData',
|
||||
loadingTemplate: this.loadingTemplate,
|
||||
preprocessing,
|
||||
onError() {
|
||||
/* eslint-disable no-new */
|
||||
new Flash('An error occured fetching the dropdown data.');
|
||||
|
|
|
|||
|
|
@ -50,6 +50,66 @@ class DropdownUtils {
|
|||
return updatedItem;
|
||||
}
|
||||
|
||||
static mergeDuplicateLabels(dataMap, newLabel) {
|
||||
const updatedMap = dataMap;
|
||||
const key = newLabel.title;
|
||||
|
||||
const hasKeyProperty = Object.prototype.hasOwnProperty.call(updatedMap, key);
|
||||
|
||||
if (!hasKeyProperty) {
|
||||
updatedMap[key] = newLabel;
|
||||
} else {
|
||||
const existing = updatedMap[key];
|
||||
|
||||
if (!existing.multipleColors) {
|
||||
existing.multipleColors = [existing.color];
|
||||
}
|
||||
|
||||
existing.multipleColors.push(newLabel.color);
|
||||
}
|
||||
|
||||
return updatedMap;
|
||||
}
|
||||
|
||||
static duplicateLabelColor(labelColors) {
|
||||
const colors = labelColors;
|
||||
const spacing = 100 / colors.length;
|
||||
|
||||
// Reduce the colors to 4
|
||||
colors.length = Math.min(colors.length, 4);
|
||||
|
||||
const color = colors.map((c, i) => {
|
||||
const percentFirst = Math.floor(spacing * i);
|
||||
const percentSecond = Math.floor(spacing * (i + 1));
|
||||
return `${c} ${percentFirst}%, ${c} ${percentSecond}%`;
|
||||
}).join(', ');
|
||||
|
||||
return `linear-gradient(${color})`;
|
||||
}
|
||||
|
||||
static duplicateLabelPreprocessing(data) {
|
||||
const results = [];
|
||||
const dataMap = {};
|
||||
|
||||
data.forEach(DropdownUtils.mergeDuplicateLabels.bind(null, dataMap));
|
||||
|
||||
Object.keys(dataMap)
|
||||
.forEach((key) => {
|
||||
const label = dataMap[key];
|
||||
|
||||
if (label.multipleColors) {
|
||||
label.color = DropdownUtils.duplicateLabelColor(label.multipleColors);
|
||||
label.text_color = '#000000';
|
||||
}
|
||||
|
||||
results.push(label);
|
||||
});
|
||||
|
||||
results.preprocessed = true;
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
static filterHint(config, item) {
|
||||
const { input, allowedKeys } = config;
|
||||
const updatedItem = item;
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@ class FilteredSearchDropdownManager {
|
|||
extraArguments: {
|
||||
endpoint: `${this.baseEndpoint}/labels.json`,
|
||||
symbol: '~',
|
||||
preprocessing: gl.DropdownUtils.duplicateLabelPreprocessing,
|
||||
},
|
||||
element: this.container.querySelector('#js-dropdown-label'),
|
||||
},
|
||||
|
|
|
|||
|
|
@ -20,13 +20,13 @@ class FilteredSearchManager {
|
|||
allowedKeys: this.filteredSearchTokenKeys.getKeys(),
|
||||
});
|
||||
this.searchHistoryDropdownElement = document.querySelector('.js-filtered-search-history-dropdown');
|
||||
const projectPath = this.searchHistoryDropdownElement ?
|
||||
this.searchHistoryDropdownElement.dataset.projectFullPath : 'project';
|
||||
const fullPath = this.searchHistoryDropdownElement ?
|
||||
this.searchHistoryDropdownElement.dataset.fullPath : 'project';
|
||||
let recentSearchesPagePrefix = 'issue-recent-searches';
|
||||
if (this.page === 'merge_requests') {
|
||||
recentSearchesPagePrefix = 'merge-request-recent-searches';
|
||||
}
|
||||
const recentSearchesKey = `${projectPath}-${recentSearchesPagePrefix}`;
|
||||
const recentSearchesKey = `${fullPath}-${recentSearchesPagePrefix}`;
|
||||
this.recentSearchesService = new RecentSearchesService(recentSearchesKey);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -58,29 +58,54 @@ class FilteredSearchVisualTokens {
|
|||
`;
|
||||
}
|
||||
|
||||
static setTokenStyle(tokenContainer, backgroundColor, textColor) {
|
||||
const token = tokenContainer;
|
||||
|
||||
// Labels with linear gradient should not override default background color
|
||||
if (backgroundColor.indexOf('linear-gradient') === -1) {
|
||||
token.style.backgroundColor = backgroundColor;
|
||||
}
|
||||
|
||||
token.style.color = textColor;
|
||||
|
||||
if (textColor === '#FFFFFF') {
|
||||
const removeToken = token.querySelector('.remove-token');
|
||||
removeToken.classList.add('inverted');
|
||||
}
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
static preprocessLabel(labelsEndpoint, labels) {
|
||||
let processed = labels;
|
||||
|
||||
if (!labels.preprocessed) {
|
||||
processed = gl.DropdownUtils.duplicateLabelPreprocessing(labels);
|
||||
AjaxCache.override(labelsEndpoint, processed);
|
||||
processed.preprocessed = true;
|
||||
}
|
||||
|
||||
return processed;
|
||||
}
|
||||
|
||||
static updateLabelTokenColor(tokenValueContainer, tokenValue) {
|
||||
const filteredSearchInput = FilteredSearchContainer.container.querySelector('.filtered-search');
|
||||
const baseEndpoint = filteredSearchInput.dataset.baseEndpoint;
|
||||
const labelsEndpoint = `${baseEndpoint}/labels.json`;
|
||||
|
||||
return AjaxCache.retrieve(labelsEndpoint)
|
||||
.then((labels) => {
|
||||
const matchingLabel = (labels || []).find(label => `~${gl.DropdownUtils.getEscapedText(label.title)}` === tokenValue);
|
||||
.then(FilteredSearchVisualTokens.preprocessLabel.bind(null, labelsEndpoint))
|
||||
.then((labels) => {
|
||||
const matchingLabel = (labels || []).find(label => `~${gl.DropdownUtils.getEscapedText(label.title)}` === tokenValue);
|
||||
|
||||
if (!matchingLabel) {
|
||||
return;
|
||||
}
|
||||
if (!matchingLabel) {
|
||||
return;
|
||||
}
|
||||
|
||||
const tokenValueStyle = tokenValueContainer.style;
|
||||
tokenValueStyle.backgroundColor = matchingLabel.color;
|
||||
tokenValueStyle.color = matchingLabel.text_color;
|
||||
|
||||
if (matchingLabel.text_color === '#FFFFFF') {
|
||||
const removeToken = tokenValueContainer.querySelector('.remove-token');
|
||||
removeToken.classList.add('inverted');
|
||||
}
|
||||
})
|
||||
.catch(() => new Flash('An error occurred while fetching label colors.'));
|
||||
FilteredSearchVisualTokens
|
||||
.setTokenStyle(tokenValueContainer, matchingLabel.color, matchingLabel.text_color);
|
||||
})
|
||||
.catch(() => new Flash('An error occurred while fetching label colors.'));
|
||||
}
|
||||
|
||||
static updateUserTokenAppearance(tokenValueContainer, tokenValueElement, tokenValue) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,51 @@
|
|||
/* global bp */
|
||||
import './breakpoints';
|
||||
|
||||
export const canShowSubItems = () => bp.getBreakpointSize() === 'md' || bp.getBreakpointSize() === 'lg';
|
||||
|
||||
export const calculateTop = (boundingRect, outerHeight) => {
|
||||
const windowHeight = window.innerHeight;
|
||||
const bottomOverflow = windowHeight - (boundingRect.top + outerHeight);
|
||||
|
||||
return bottomOverflow < 0 ? (boundingRect.top - outerHeight) + boundingRect.height :
|
||||
boundingRect.top;
|
||||
};
|
||||
|
||||
export const showSubLevelItems = (el) => {
|
||||
const subItems = el.querySelector('.sidebar-sub-level-items');
|
||||
|
||||
if (!subItems || !canShowSubItems()) return;
|
||||
|
||||
subItems.style.display = 'block';
|
||||
el.classList.add('is-over');
|
||||
|
||||
const boundingRect = el.getBoundingClientRect();
|
||||
const top = calculateTop(boundingRect, subItems.offsetHeight);
|
||||
const isAbove = top < boundingRect.top;
|
||||
|
||||
subItems.style.transform = `translate3d(0, ${Math.floor(top)}px, 0)`;
|
||||
|
||||
if (isAbove) {
|
||||
subItems.classList.add('is-above');
|
||||
}
|
||||
};
|
||||
|
||||
export const hideSubLevelItems = (el) => {
|
||||
const subItems = el.querySelector('.sidebar-sub-level-items');
|
||||
|
||||
if (!subItems || !canShowSubItems()) return;
|
||||
|
||||
el.classList.remove('is-over');
|
||||
subItems.style.display = 'none';
|
||||
subItems.classList.remove('is-above');
|
||||
};
|
||||
|
||||
export default () => {
|
||||
const items = [...document.querySelectorAll('.sidebar-top-level-items > li:not(.active)')]
|
||||
.filter(el => el.querySelector('.sidebar-sub-level-items'));
|
||||
|
||||
items.forEach((el) => {
|
||||
el.addEventListener('mouseenter', e => showSubLevelItems(e.currentTarget));
|
||||
el.addEventListener('mouseleave', e => hideSubLevelItems(e.currentTarget));
|
||||
});
|
||||
};
|
||||
|
|
@ -781,10 +781,10 @@ GitLabDropdown = (function() {
|
|||
|
||||
GitLabDropdown.prototype.focusTextInput = function(triggerFocus = false) {
|
||||
if (this.options.filterable) {
|
||||
$(':focus').blur();
|
||||
|
||||
this.dropdown.one('transitionend', () => {
|
||||
this.filterInput.focus();
|
||||
if (this.dropdown.is('.open')) {
|
||||
this.filterInput.focus();
|
||||
}
|
||||
});
|
||||
|
||||
if (triggerFocus) {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
import Chart from 'vendor/Chart';
|
||||
import ContributorsStatGraph from './stat_graph_contributors';
|
||||
|
||||
// export to global scope
|
||||
window.Chart = Chart;
|
||||
window.ContributorsStatGraph = ContributorsStatGraph;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,63 @@
|
|||
import Chart from 'vendor/Chart';
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const projectChartData = JSON.parse(document.getElementById('projectChartData').innerHTML);
|
||||
|
||||
const responsiveChart = (selector, data) => {
|
||||
const options = {
|
||||
scaleOverlay: true,
|
||||
responsive: true,
|
||||
pointHitDetectionRadius: 2,
|
||||
maintainAspectRatio: false,
|
||||
};
|
||||
// get selector by context
|
||||
const ctx = selector.get(0).getContext('2d');
|
||||
// pointing parent container to make chart.js inherit its width
|
||||
const container = $(selector).parent();
|
||||
const generateChart = () => {
|
||||
selector.attr('width', $(container).width());
|
||||
if (window.innerWidth < 768) {
|
||||
// Scale fonts if window width lower than 768px (iPad portrait)
|
||||
options.scaleFontSize = 8;
|
||||
}
|
||||
return new Chart(ctx).Bar(data, options);
|
||||
};
|
||||
// enabling auto-resizing
|
||||
$(window).resize(generateChart);
|
||||
return generateChart();
|
||||
};
|
||||
|
||||
const chartData = (keys, values) => {
|
||||
const data = {
|
||||
labels: keys,
|
||||
datasets: [{
|
||||
fillColor: 'rgba(220,220,220,0.5)',
|
||||
strokeColor: 'rgba(220,220,220,1)',
|
||||
barStrokeWidth: 1,
|
||||
barValueSpacing: 1,
|
||||
barDatasetSpacing: 1,
|
||||
data: values,
|
||||
}],
|
||||
};
|
||||
return data;
|
||||
};
|
||||
|
||||
const hourData = chartData(projectChartData.hour.keys, projectChartData.hour.values);
|
||||
responsiveChart($('#hour-chart'), hourData);
|
||||
|
||||
const dayData = chartData(projectChartData.weekDays.keys, projectChartData.weekDays.values);
|
||||
responsiveChart($('#weekday-chart'), dayData);
|
||||
|
||||
const monthData = chartData(projectChartData.month.keys, projectChartData.month.values);
|
||||
responsiveChart($('#month-chart'), monthData);
|
||||
|
||||
const data = projectChartData.languages;
|
||||
const ctx = $('#languages-chart').get(0).getContext('2d');
|
||||
const options = {
|
||||
scaleOverlay: true,
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
};
|
||||
|
||||
new Chart(ctx).Pie(data, options);
|
||||
});
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
import ContributorsStatGraph from './stat_graph_contributors';
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
$.ajax({
|
||||
type: 'GET',
|
||||
url: document.querySelector('.js-graphs-show').dataset.projectGraphPath,
|
||||
dataType: 'json',
|
||||
success(data) {
|
||||
const graph = new ContributorsStatGraph();
|
||||
graph.init(data);
|
||||
|
||||
$('#brush_change').change(() => {
|
||||
graph.change_date_header();
|
||||
graph.redraw_authors();
|
||||
});
|
||||
|
||||
$('.stat-graph').fadeIn();
|
||||
$('.loading-graph').hide();
|
||||
},
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
<script>
|
||||
export default {
|
||||
props: {
|
||||
entityId: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
entityName: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
/**
|
||||
* This method is based on app/helpers/application_helper.rb#project_identicon
|
||||
*/
|
||||
identiconStyles() {
|
||||
const allowedColors = [
|
||||
'#FFEBEE',
|
||||
'#F3E5F5',
|
||||
'#E8EAF6',
|
||||
'#E3F2FD',
|
||||
'#E0F2F1',
|
||||
'#FBE9E7',
|
||||
'#EEEEEE',
|
||||
];
|
||||
|
||||
const backgroundColor = allowedColors[this.entityId % 7];
|
||||
|
||||
return `background-color: ${backgroundColor}; color: #555;`;
|
||||
},
|
||||
identiconTitle() {
|
||||
return this.entityName.charAt(0).toUpperCase();
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="avatar s40 identicon"
|
||||
:style="identiconStyles">
|
||||
{{identiconTitle}}
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -1,7 +1,11 @@
|
|||
<script>
|
||||
import eventHub from '../event_hub';
|
||||
import groupIdenticon from './group_identicon.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
groupIdenticon,
|
||||
},
|
||||
props: {
|
||||
group: {
|
||||
type: Object,
|
||||
|
|
@ -92,6 +96,9 @@ export default {
|
|||
hasGroups() {
|
||||
return Object.keys(this.group.subGroups).length > 0;
|
||||
},
|
||||
hasAvatar() {
|
||||
return this.group.avatarUrl && this.group.avatarUrl.indexOf('/assets/no_group_avatar') === -1;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
@ -194,9 +201,15 @@ export default {
|
|||
<a
|
||||
:href="group.groupPath">
|
||||
<img
|
||||
v-if="hasAvatar"
|
||||
class="avatar s40"
|
||||
:src="group.avatarUrl"
|
||||
/>
|
||||
<group-identicon
|
||||
v-else
|
||||
:entity-id=group.id
|
||||
:entity-name="group.name"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
/* global ListLabel */
|
||||
|
||||
import IssuableBulkUpdateActions from './issuable_bulk_update_actions';
|
||||
import DropdownUtils from './filtered_search/dropdown_utils';
|
||||
|
||||
(function() {
|
||||
this.LabelsSelect = (function() {
|
||||
|
|
@ -218,18 +219,7 @@ import IssuableBulkUpdateActions from './issuable_bulk_update_actions';
|
|||
}
|
||||
}
|
||||
if (label.duplicate) {
|
||||
spacing = 100 / label.color.length;
|
||||
// Reduce the colors to 4
|
||||
label.color = label.color.filter(function(color, i) {
|
||||
return i < 4;
|
||||
});
|
||||
color = _.map(label.color, function(color, i) {
|
||||
var percentFirst, percentSecond;
|
||||
percentFirst = Math.floor(spacing * i);
|
||||
percentSecond = Math.floor(spacing * (i + 1));
|
||||
return color + " " + percentFirst + "%," + color + " " + percentSecond + "% ";
|
||||
}).join(',');
|
||||
color = "linear-gradient(" + color + ")";
|
||||
color = gl.DropdownUtils.duplicateLabelColor(label.color);
|
||||
}
|
||||
else {
|
||||
if (label.color != null) {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
import _ from 'underscore';
|
||||
import Cookies from 'js-cookie';
|
||||
import NewNavSidebar from './new_sidebar';
|
||||
import initFlyOutNav from './fly_out_nav';
|
||||
|
||||
(function() {
|
||||
var hideEndFade;
|
||||
|
|
@ -58,6 +59,8 @@ import NewNavSidebar from './new_sidebar';
|
|||
if (Cookies.get('new_nav') === 'true') {
|
||||
const newNavSidebar = new NewNavSidebar();
|
||||
newNavSidebar.bindEvents();
|
||||
|
||||
initFlyOutNav();
|
||||
}
|
||||
|
||||
$(window).on('scroll', _.throttle(applyScrollNavClass, 100));
|
||||
|
|
|
|||
|
|
@ -6,6 +6,10 @@ class AjaxCache extends Cache {
|
|||
this.pendingRequests = { };
|
||||
}
|
||||
|
||||
override(endpoint, data) {
|
||||
this.internalStorage[endpoint] = data;
|
||||
}
|
||||
|
||||
retrieve(endpoint, forceRetrieve) {
|
||||
if (this.hasData(endpoint) && !forceRetrieve) {
|
||||
return Promise.resolve(this.get(endpoint));
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/* eslint-disable no-useless-escape, max-len, quotes, no-var, no-underscore-dangle, func-names, space-before-function-paren, no-unused-vars, no-return-assign, object-shorthand, one-var, one-var-declaration-per-line, comma-dangle, consistent-return, class-methods-use-this, new-parens */
|
||||
|
||||
import 'vendor/cropper';
|
||||
import 'cropper';
|
||||
|
||||
((global) => {
|
||||
// Matches everything but the file name
|
||||
|
|
|
|||
|
|
@ -1,6 +1,40 @@
|
|||
document.addEventListener('DOMContentLoaded', () => {
|
||||
let hasUserDefinedProjectPath = false;
|
||||
|
||||
const deriveProjectPathFromUrl = ($projectImportUrl, $projectPath) => {
|
||||
if ($projectImportUrl.attr('disabled') || hasUserDefinedProjectPath) {
|
||||
return;
|
||||
}
|
||||
|
||||
let importUrl = $projectImportUrl.val().trim();
|
||||
if (importUrl.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
\/?: remove trailing slash
|
||||
(\.git\/?)?: remove trailing .git (with optional trailing slash)
|
||||
(\?.*)?: remove query string
|
||||
(#.*)?: remove fragment identifier
|
||||
*/
|
||||
importUrl = importUrl.replace(/\/?(\.git\/?)?(\?.*)?(#.*)?$/, '');
|
||||
|
||||
// extract everything after the last slash
|
||||
const pathMatch = /\/([^/]+)$/.exec(importUrl);
|
||||
if (pathMatch) {
|
||||
$projectPath.val(pathMatch[1]);
|
||||
}
|
||||
};
|
||||
|
||||
const bindEvents = () => {
|
||||
const $newProjectForm = $('#new_project');
|
||||
const importBtnTooltip = 'Please enter a valid project name.';
|
||||
const $importBtnWrapper = $('.import_gitlab_project');
|
||||
const $projectImportUrl = $('#project_import_url');
|
||||
const $projectPath = $('#project_path');
|
||||
|
||||
if ($newProjectForm.length !== 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
$('.how_to_import_link').on('click', (e) => {
|
||||
e.preventDefault();
|
||||
|
|
@ -13,19 +47,19 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||
|
||||
$('.btn_import_gitlab_project').on('click', () => {
|
||||
const importHref = $('a.btn_import_gitlab_project').attr('href');
|
||||
$('.btn_import_gitlab_project').attr('href', `${importHref}?namespace_id=${$('#project_namespace_id').val()}&path=${$('#project_path').val()}`);
|
||||
$('.btn_import_gitlab_project').attr('href', `${importHref}?namespace_id=${$('#project_namespace_id').val()}&path=${$projectPath.val()}`);
|
||||
});
|
||||
|
||||
$('.btn_import_gitlab_project').attr('disabled', !$('#project_path').val().trim().length);
|
||||
$('.btn_import_gitlab_project').attr('disabled', !$projectPath.val().trim().length);
|
||||
$importBtnWrapper.attr('title', importBtnTooltip);
|
||||
|
||||
$('#new_project').on('submit', () => {
|
||||
const $path = $('#project_path');
|
||||
$path.val($path.val().trim());
|
||||
$newProjectForm.on('submit', () => {
|
||||
$projectPath.val($projectPath.val().trim());
|
||||
});
|
||||
|
||||
$('#project_path').on('keyup', () => {
|
||||
if ($('#project_path').val().trim().length) {
|
||||
$projectPath.on('keyup', () => {
|
||||
hasUserDefinedProjectPath = $projectPath.val().trim().length > 0;
|
||||
if (hasUserDefinedProjectPath) {
|
||||
$('.btn_import_gitlab_project').attr('disabled', false);
|
||||
$importBtnWrapper.attr('title', '');
|
||||
$importBtnWrapper.removeClass('has-tooltip');
|
||||
|
|
@ -35,9 +69,17 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||
}
|
||||
});
|
||||
|
||||
$('#project_import_url').disable();
|
||||
$projectImportUrl.disable();
|
||||
$projectImportUrl.keyup(() => deriveProjectPathFromUrl($projectImportUrl, $projectPath));
|
||||
|
||||
$('.import_git').on('click', () => {
|
||||
const $projectImportUrl = $('#project_import_url');
|
||||
$projectImportUrl.attr('disabled', !$projectImportUrl.attr('disabled'));
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
document.addEventListener('DOMContentLoaded', bindEvents);
|
||||
|
||||
export default {
|
||||
bindEvents,
|
||||
deriveProjectPathFromUrl,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
/* global U2FRegister */
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const twoFactorNode = document.querySelector('.js-two-factor-auth');
|
||||
const skippable = twoFactorNode.dataset.twoFactorSkippable === 'true';
|
||||
if (skippable) {
|
||||
const button = `<a class="btn btn-xs btn-warning pull-right" data-method="patch" href="${twoFactorNode.dataset.two_factor_skip_url}">Configure it later</a>`;
|
||||
const flashAlert = document.querySelector('.flash-alert .container-fluid');
|
||||
if (flashAlert) flashAlert.insertAdjacentHTML('beforeend', button);
|
||||
}
|
||||
|
||||
const u2fRegister = new U2FRegister($('#js-register-u2f'), gon.u2f);
|
||||
u2fRegister.start();
|
||||
});
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
import Api from './api';
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
$('#js-project-dropdown').glDropdown({
|
||||
data: (term, callback) => {
|
||||
Api.projects(term, {
|
||||
order_by: 'last_activity_at',
|
||||
}, (data) => {
|
||||
callback(data);
|
||||
});
|
||||
},
|
||||
text: project => (project.name_with_namespace || project.name),
|
||||
selectable: true,
|
||||
fieldName: 'author_id',
|
||||
filterable: true,
|
||||
search: {
|
||||
fields: ['name_with_namespace'],
|
||||
},
|
||||
id: data => data.id,
|
||||
isSelected: data => (data.id === 2),
|
||||
});
|
||||
});
|
||||
|
|
@ -313,6 +313,25 @@ header {
|
|||
.impersonation i {
|
||||
color: $red-500;
|
||||
}
|
||||
|
||||
// TODO: fallback to global style
|
||||
.dropdown-menu,
|
||||
.dropdown-menu-nav {
|
||||
li {
|
||||
padding: 0 1px;
|
||||
|
||||
a {
|
||||
border-radius: 0;
|
||||
padding: 8px 16px;
|
||||
|
||||
&:hover,
|
||||
&:active,
|
||||
&:focus {
|
||||
background-color: $gray-darker;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.with-performance-bar header.navbar-gitlab {
|
||||
|
|
|
|||
|
|
@ -369,6 +369,10 @@ ul.indent-list {
|
|||
background-color: $row-hover;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.avatar-container > a {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -185,3 +185,28 @@
|
|||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: fallback to global style
|
||||
.atwho-view {
|
||||
.atwho-view-ul {
|
||||
padding: 8px 1px;
|
||||
|
||||
li {
|
||||
padding: 8px 16px;
|
||||
border: 0;
|
||||
|
||||
&.cur {
|
||||
background-color: $gray-darker;
|
||||
color: $gl-text-color;
|
||||
|
||||
small {
|
||||
color: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
strong {
|
||||
color: $gl-text-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -309,6 +309,25 @@ header.navbar-gitlab-new {
|
|||
outline: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: fallback to global style
|
||||
.dropdown-menu {
|
||||
li {
|
||||
padding: 0 1px;
|
||||
|
||||
a {
|
||||
border-radius: 0;
|
||||
padding: 8px 16px;
|
||||
|
||||
&.is-focused,
|
||||
&:hover,
|
||||
&:active,
|
||||
&:focus {
|
||||
background-color: $gray-darker;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.breadcrumbs-container {
|
||||
|
|
|
|||
|
|
@ -143,10 +143,19 @@ $new-sidebar-width: 220px;
|
|||
white-space: nowrap;
|
||||
|
||||
a {
|
||||
display: block;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 12px 16px;
|
||||
color: $inactive-color;
|
||||
}
|
||||
|
||||
svg {
|
||||
fill: $inactive-color;
|
||||
}
|
||||
}
|
||||
|
||||
.nav-item-name {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
li.active {
|
||||
|
|
@ -156,11 +165,25 @@ $new-sidebar-width: 220px;
|
|||
color: $active-color;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
svg {
|
||||
fill: $active-color;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: $screen-xs-max) {
|
||||
left: (-$new-sidebar-width);
|
||||
}
|
||||
|
||||
.nav-icon-container {
|
||||
display: flex;
|
||||
margin-right: 8px;
|
||||
|
||||
svg {
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.with-performance-bar .nav-sidebar {
|
||||
|
|
@ -173,7 +196,7 @@ $new-sidebar-width: 220px;
|
|||
|
||||
> li {
|
||||
a {
|
||||
padding: 8px 16px 8px 24px;
|
||||
padding: 8px 16px 8px 50px;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
|
|
@ -197,8 +220,83 @@ $new-sidebar-width: 220px;
|
|||
|
||||
.sidebar-top-level-items {
|
||||
> li {
|
||||
> a {
|
||||
@media (min-width: $screen-sm-min) {
|
||||
margin-right: 2px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: $gl-text-color;
|
||||
}
|
||||
}
|
||||
|
||||
&:not(.active) {
|
||||
> a {
|
||||
margin-left: 1px;
|
||||
margin-right: 3px;
|
||||
}
|
||||
|
||||
.sidebar-sub-level-items {
|
||||
@media (min-width: $screen-sm-min) {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 220px;
|
||||
width: 150px;
|
||||
margin-top: -1px;
|
||||
padding: 8px 1px;
|
||||
background-color: $white-light;
|
||||
box-shadow: 2px 1px 3px $dropdown-shadow-color;
|
||||
border: 1px solid $gray-darker;
|
||||
border-left: 0;
|
||||
border-radius: 0 3px 3px 0;
|
||||
|
||||
&::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: -30px;
|
||||
bottom: -30px;
|
||||
left: 0;
|
||||
right: -30px;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 44px;
|
||||
left: -30px;
|
||||
right: 35px;
|
||||
bottom: 0;
|
||||
height: 100%;
|
||||
max-height: 150px;
|
||||
z-index: -1;
|
||||
transform: skew(33deg);
|
||||
}
|
||||
|
||||
&.is-above {
|
||||
margin-top: 1px;
|
||||
|
||||
&::after {
|
||||
top: auto;
|
||||
bottom: 44px;
|
||||
transform: skew(-30deg);
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
padding: 8px 16px;
|
||||
color: $gl-text-color;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
background-color: $gray-darker;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.badge {
|
||||
float: right;
|
||||
background-color: $inactive-badge-background;
|
||||
color: $inactive-color;
|
||||
}
|
||||
|
|
@ -206,6 +304,10 @@ $new-sidebar-width: 220px;
|
|||
&.active {
|
||||
background: $active-background;
|
||||
|
||||
> a {
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
.badge {
|
||||
color: $active-color;
|
||||
font-weight: 600;
|
||||
|
|
@ -216,14 +318,10 @@ $new-sidebar-width: 220px;
|
|||
}
|
||||
}
|
||||
|
||||
> a:hover {
|
||||
background-color: $hover-background;
|
||||
color: $hover-color;
|
||||
|
||||
.badge {
|
||||
background-color: $indigo-500;
|
||||
color: $hover-color;
|
||||
}
|
||||
&:not(.active):hover > a,
|
||||
> a:hover,
|
||||
&.is-over > a {
|
||||
background-color: $white-light;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -311,9 +311,7 @@
|
|||
}
|
||||
|
||||
.dropdown-menu {
|
||||
right: $gl-padding;
|
||||
left: $gl-padding;
|
||||
width: auto;
|
||||
margin-top: -$gl-padding;
|
||||
}
|
||||
|
||||
svg {
|
||||
|
|
|
|||
|
|
@ -110,6 +110,10 @@
|
|||
|
||||
.js-ca-dropdown {
|
||||
top: $gl-padding-top;
|
||||
|
||||
.dropdown-menu-align-right {
|
||||
margin-top: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
.content-list {
|
||||
|
|
@ -442,6 +446,24 @@
|
|||
margin-bottom: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: fallback to global style
|
||||
.dropdown-menu {
|
||||
li {
|
||||
padding: 0 1px;
|
||||
|
||||
a {
|
||||
border-radius: 0;
|
||||
padding: 8px 16px;
|
||||
|
||||
&:hover,
|
||||
&:active,
|
||||
&:focus {
|
||||
background-color: $gray-darker;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.cycle-analytics-overview {
|
||||
|
|
|
|||
|
|
@ -202,6 +202,28 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: fallback to global style
|
||||
.dropdown-menu:not(.dropdown-menu-selectable) {
|
||||
li {
|
||||
padding: 0 1px;
|
||||
|
||||
&.dropdown-header {
|
||||
padding: 8px 16px;
|
||||
}
|
||||
|
||||
a {
|
||||
border-radius: 0;
|
||||
padding: 8px 16px;
|
||||
|
||||
&:hover,
|
||||
&:active,
|
||||
&:focus {
|
||||
background-color: $gray-darker;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.blob-commit-info {
|
||||
|
|
|
|||
|
|
@ -66,7 +66,8 @@ module Projects
|
|||
end
|
||||
|
||||
def filter_params
|
||||
params.merge(board_id: params[:board_id], id: params[:list_id]).compact
|
||||
params.merge(board_id: params[:board_id], id: params[:list_id])
|
||||
.reject { |_, value| value.nil? }
|
||||
end
|
||||
|
||||
def move_params
|
||||
|
|
|
|||
|
|
@ -43,23 +43,7 @@ class Projects::GraphsController < Projects::ApplicationController
|
|||
end
|
||||
|
||||
def get_languages
|
||||
@languages = Linguist::Repository.new(@repository.rugged, @repository.rugged.head.target_id).languages
|
||||
total = @languages.map(&:last).sum
|
||||
|
||||
@languages = @languages.map do |language|
|
||||
name, share = language
|
||||
color = Linguist::Language[name].color || "##{Digest::SHA256.hexdigest(name)[0...6]}"
|
||||
{
|
||||
value: (share.to_f * 100 / total).round(2),
|
||||
label: name,
|
||||
color: color,
|
||||
highlight: color
|
||||
}
|
||||
end
|
||||
|
||||
@languages.sort! do |x, y|
|
||||
y[:value] <=> x[:value]
|
||||
end
|
||||
@languages = @project.repository.languages
|
||||
end
|
||||
|
||||
def fetch_graph
|
||||
|
|
|
|||
|
|
@ -48,7 +48,11 @@ module GitlabRoutingHelper
|
|||
end
|
||||
|
||||
def milestone_path(entity, *args)
|
||||
project_milestone_path(entity.project, entity, *args)
|
||||
if entity.is_group_milestone?
|
||||
group_milestone_path(entity.group, entity, *args)
|
||||
elsif entity.is_project_milestone?
|
||||
project_milestone_path(entity.project, entity, *args)
|
||||
end
|
||||
end
|
||||
|
||||
def issue_url(entity, *args)
|
||||
|
|
@ -63,6 +67,14 @@ module GitlabRoutingHelper
|
|||
project_pipeline_url(pipeline.project, pipeline.id, *args)
|
||||
end
|
||||
|
||||
def milestone_url(entity, *args)
|
||||
if entity.is_group_milestone?
|
||||
group_milestone_url(entity.group, entity, *args)
|
||||
elsif entity.is_project_milestone?
|
||||
project_milestone_url(entity.project, entity, *args)
|
||||
end
|
||||
end
|
||||
|
||||
def pipeline_job_url(pipeline, build, *args)
|
||||
project_job_url(pipeline.project, build.id, *args)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ module MergeRequestsHelper
|
|||
|
||||
def merge_path_description(merge_request, separator)
|
||||
if merge_request.for_fork?
|
||||
"Project:Branches: #{@merge_request.source_project_path}:#{@merge_request.source_branch} #{separator} #{@merge_request.target_project.path_with_namespace}:#{@merge_request.target_branch}"
|
||||
"Project:Branches: #{@merge_request.source_project_path}:#{@merge_request.source_branch} #{separator} #{@merge_request.target_project.full_path}:#{@merge_request.target_branch}"
|
||||
else
|
||||
"Branches: #{@merge_request.source_branch} #{separator} #{@merge_request.target_branch}"
|
||||
end
|
||||
|
|
|
|||
|
|
@ -398,7 +398,7 @@ module ProjectsHelper
|
|||
if project
|
||||
import_path = "/Home/Stacks/import"
|
||||
|
||||
repo = project.path_with_namespace
|
||||
repo = project.full_path
|
||||
branch ||= project.default_branch
|
||||
sha ||= project.commit.short_id
|
||||
|
||||
|
|
@ -458,7 +458,7 @@ module ProjectsHelper
|
|||
|
||||
def readme_cache_key
|
||||
sha = @project.commit.try(:sha) || 'nil'
|
||||
[@project.path_with_namespace, sha, "readme"].join('-')
|
||||
[@project.full_path, sha, "readme"].join('-')
|
||||
end
|
||||
|
||||
def current_ref
|
||||
|
|
|
|||
|
|
@ -127,15 +127,23 @@ module SearchHelper
|
|||
end
|
||||
|
||||
def search_filter_input_options(type)
|
||||
{
|
||||
opts = {
|
||||
id: "filtered-search-#{type}",
|
||||
placeholder: 'Search or filter results...',
|
||||
data: {
|
||||
'project-id' => @project.id,
|
||||
'username-params' => @users.to_json(only: [:id, :username]),
|
||||
'base-endpoint' => project_path(@project)
|
||||
'username-params' => @users.to_json(only: [:id, :username])
|
||||
}
|
||||
}
|
||||
|
||||
if @project.present?
|
||||
opts[:data]['project-id'] = @project.id
|
||||
opts[:data]['base-endpoint'] = project_path(@project)
|
||||
else
|
||||
# Group context
|
||||
opts[:data]['base-endpoint'] = group_canonical_path(@group)
|
||||
end
|
||||
|
||||
opts
|
||||
end
|
||||
|
||||
# Sanitize a HTML field for search display. Most tags are stripped out and the
|
||||
|
|
|
|||
|
|
@ -165,7 +165,7 @@ class Notify < BaseMailer
|
|||
|
||||
headers['X-GitLab-Project'] = @project.name
|
||||
headers['X-GitLab-Project-Id'] = @project.id
|
||||
headers['X-GitLab-Project-Path'] = @project.path_with_namespace
|
||||
headers['X-GitLab-Project-Path'] = @project.full_path
|
||||
end
|
||||
|
||||
def add_unsubscription_headers_and_links
|
||||
|
|
|
|||
|
|
@ -317,7 +317,7 @@ module Ci
|
|||
return @config_processor if defined?(@config_processor)
|
||||
|
||||
@config_processor ||= begin
|
||||
Ci::GitlabCiYamlProcessor.new(ci_yaml_file, project.path_with_namespace)
|
||||
Ci::GitlabCiYamlProcessor.new(ci_yaml_file, project.full_path)
|
||||
rescue Ci::GitlabCiYamlProcessor::ValidationError, Psych::SyntaxError => e
|
||||
self.yaml_errors = e.message
|
||||
nil
|
||||
|
|
|
|||
|
|
@ -1,6 +1,12 @@
|
|||
module ProtectedBranchAccess
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
ALLOWED_ACCESS_LEVELS ||= [
|
||||
Gitlab::Access::MASTER,
|
||||
Gitlab::Access::DEVELOPER,
|
||||
Gitlab::Access::NO_ACCESS
|
||||
].freeze
|
||||
|
||||
included do
|
||||
include ProtectedRefAccess
|
||||
|
||||
|
|
@ -9,11 +15,7 @@ module ProtectedBranchAccess
|
|||
delegate :project, to: :protected_branch
|
||||
|
||||
validates :access_level, presence: true, inclusion: {
|
||||
in: [
|
||||
Gitlab::Access::MASTER,
|
||||
Gitlab::Access::DEVELOPER,
|
||||
Gitlab::Access::NO_ACCESS
|
||||
]
|
||||
in: ALLOWED_ACCESS_LEVELS
|
||||
}
|
||||
|
||||
def self.human_access_levels
|
||||
|
|
|
|||
|
|
@ -0,0 +1,102 @@
|
|||
module Storage
|
||||
module LegacyNamespace
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
def move_dir
|
||||
if any_project_has_container_registry_tags?
|
||||
raise Gitlab::UpdatePathError.new('Namespace cannot be moved, because at least one project has tags in container registry')
|
||||
end
|
||||
|
||||
# Move the namespace directory in all storage paths used by member projects
|
||||
repository_storage_paths.each do |repository_storage_path|
|
||||
# Ensure old directory exists before moving it
|
||||
gitlab_shell.add_namespace(repository_storage_path, full_path_was)
|
||||
|
||||
unless gitlab_shell.mv_namespace(repository_storage_path, full_path_was, full_path)
|
||||
Rails.logger.error "Exception moving path #{repository_storage_path} from #{full_path_was} to #{full_path}"
|
||||
|
||||
# if we cannot move namespace directory we should rollback
|
||||
# db changes in order to prevent out of sync between db and fs
|
||||
raise Gitlab::UpdatePathError.new('namespace directory cannot be moved')
|
||||
end
|
||||
end
|
||||
|
||||
Gitlab::UploadsTransfer.new.rename_namespace(full_path_was, full_path)
|
||||
Gitlab::PagesTransfer.new.rename_namespace(full_path_was, full_path)
|
||||
|
||||
remove_exports!
|
||||
|
||||
# If repositories moved successfully we need to
|
||||
# send update instructions to users.
|
||||
# However we cannot allow rollback since we moved namespace dir
|
||||
# So we basically we mute exceptions in next actions
|
||||
begin
|
||||
send_update_instructions
|
||||
true
|
||||
rescue
|
||||
# Returning false does not rollback after_* transaction but gives
|
||||
# us information about failing some of tasks
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
# Hooks
|
||||
|
||||
# Save the storage paths before the projects are destroyed to use them on after destroy
|
||||
def prepare_for_destroy
|
||||
old_repository_storage_paths
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def old_repository_storage_paths
|
||||
@old_repository_storage_paths ||= repository_storage_paths
|
||||
end
|
||||
|
||||
def repository_storage_paths
|
||||
# We need to get the storage paths for all the projects, even the ones that are
|
||||
# pending delete. Unscoping also get rids of the default order, which causes
|
||||
# problems with SELECT DISTINCT.
|
||||
Project.unscoped do
|
||||
all_projects.select('distinct(repository_storage)').to_a.map(&:repository_storage_path)
|
||||
end
|
||||
end
|
||||
|
||||
def rm_dir
|
||||
# Remove the namespace directory in all storages paths used by member projects
|
||||
old_repository_storage_paths.each do |repository_storage_path|
|
||||
# Move namespace directory into trash.
|
||||
# We will remove it later async
|
||||
new_path = "#{full_path}+#{id}+deleted"
|
||||
|
||||
if gitlab_shell.mv_namespace(repository_storage_path, full_path, new_path)
|
||||
Gitlab::AppLogger.info %Q(Namespace directory "#{full_path}" moved to "#{new_path}")
|
||||
|
||||
# Remove namespace directroy async with delay so
|
||||
# GitLab has time to remove all projects first
|
||||
run_after_commit do
|
||||
GitlabShellWorker.perform_in(5.minutes, :rm_namespace, repository_storage_path, new_path)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
remove_exports!
|
||||
end
|
||||
|
||||
def remove_exports!
|
||||
Gitlab::Popen.popen(%W(find #{export_path} -not -path #{export_path} -delete))
|
||||
end
|
||||
|
||||
def export_path
|
||||
File.join(Gitlab::ImportExport.storage_path, full_path_was)
|
||||
end
|
||||
|
||||
def full_path_was
|
||||
if parent
|
||||
parent.full_path + '/' + path_was
|
||||
else
|
||||
path_was
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
module Storage
|
||||
module LegacyProject
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
def disk_path
|
||||
full_path
|
||||
end
|
||||
|
||||
def ensure_storage_path_exist
|
||||
gitlab_shell.add_namespace(repository_storage_path, namespace.full_path)
|
||||
end
|
||||
|
||||
def rename_repo
|
||||
path_was = previous_changes['path'].first
|
||||
old_path_with_namespace = File.join(namespace.full_path, path_was)
|
||||
new_path_with_namespace = File.join(namespace.full_path, path)
|
||||
|
||||
Rails.logger.error "Attempting to rename #{old_path_with_namespace} -> #{new_path_with_namespace}"
|
||||
|
||||
if has_container_registry_tags?
|
||||
Rails.logger.error "Project #{old_path_with_namespace} cannot be renamed because container registry tags are present!"
|
||||
|
||||
# we currently doesn't support renaming repository if it contains images in container registry
|
||||
raise StandardError.new('Project cannot be renamed, because images are present in its container registry')
|
||||
end
|
||||
|
||||
expire_caches_before_rename(old_path_with_namespace)
|
||||
|
||||
if gitlab_shell.mv_repository(repository_storage_path, old_path_with_namespace, new_path_with_namespace)
|
||||
# If repository moved successfully we need to send update instructions to users.
|
||||
# However we cannot allow rollback since we moved repository
|
||||
# So we basically we mute exceptions in next actions
|
||||
begin
|
||||
gitlab_shell.mv_repository(repository_storage_path, "#{old_path_with_namespace}.wiki", "#{new_path_with_namespace}.wiki")
|
||||
send_move_instructions(old_path_with_namespace)
|
||||
expires_full_path_cache
|
||||
|
||||
@old_path_with_namespace = old_path_with_namespace
|
||||
|
||||
SystemHooksService.new.execute_hooks_for(self, :rename)
|
||||
|
||||
@repository = nil
|
||||
rescue => e
|
||||
Rails.logger.error "Exception renaming #{old_path_with_namespace} -> #{new_path_with_namespace}: #{e}"
|
||||
# Returning false does not rollback after_* transaction but gives
|
||||
# us information about failing some of tasks
|
||||
false
|
||||
end
|
||||
else
|
||||
Rails.logger.error "Repository could not be renamed: #{old_path_with_namespace} -> #{new_path_with_namespace}"
|
||||
|
||||
# if we cannot move namespace directory we should rollback
|
||||
# db changes in order to prevent out of sync between db and fs
|
||||
raise StandardError.new('repository cannot be renamed')
|
||||
end
|
||||
|
||||
Gitlab::AppLogger.info "Project was renamed: #{old_path_with_namespace} -> #{new_path_with_namespace}"
|
||||
|
||||
Gitlab::UploadsTransfer.new.rename_project(path_was, path, namespace.full_path)
|
||||
Gitlab::PagesTransfer.new.rename_project(path_was, path, namespace.full_path)
|
||||
end
|
||||
|
||||
def create_repository(force: false)
|
||||
# Forked import is handled asynchronously
|
||||
return if forked? && !force
|
||||
|
||||
if gitlab_shell.add_repository(repository_storage_path, path_with_namespace)
|
||||
repository.after_create
|
||||
true
|
||||
else
|
||||
errors.add(:base, 'Failed to create repository via gitlab-shell')
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
module Storage
|
||||
module LegacyProjectWiki
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
def disk_path
|
||||
project.disk_path + '.wiki'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
module Storage
|
||||
module LegacyRepository
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
delegate :disk_path, to: :project
|
||||
end
|
||||
end
|
||||
|
|
@ -630,7 +630,7 @@ class MergeRequest < ActiveRecord::Base
|
|||
|
||||
def target_project_path
|
||||
if target_project
|
||||
target_project.path_with_namespace
|
||||
target_project.full_path
|
||||
else
|
||||
"(removed)"
|
||||
end
|
||||
|
|
@ -638,7 +638,7 @@ class MergeRequest < ActiveRecord::Base
|
|||
|
||||
def source_project_path
|
||||
if source_project
|
||||
source_project.path_with_namespace
|
||||
source_project.full_path
|
||||
else
|
||||
"(removed)"
|
||||
end
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ class Namespace < ActiveRecord::Base
|
|||
include Gitlab::VisibilityLevel
|
||||
include Routable
|
||||
include AfterCommitQueue
|
||||
include Storage::LegacyNamespace
|
||||
|
||||
# Prevent users from creating unreasonably deep level of nesting.
|
||||
# The number 20 was taken based on maximum nesting level of
|
||||
|
|
@ -41,10 +42,11 @@ class Namespace < ActiveRecord::Base
|
|||
|
||||
delegate :name, to: :owner, allow_nil: true, prefix: true
|
||||
|
||||
after_update :move_dir, if: :path_changed?
|
||||
after_commit :refresh_access_of_projects_invited_groups, on: :update, if: -> { previous_changes.key?('share_with_group_lock') }
|
||||
|
||||
# Save the storage paths before the projects are destroyed to use them on after destroy
|
||||
# Legacy Storage specific hooks
|
||||
|
||||
after_update :move_dir, if: :path_changed?
|
||||
before_destroy(prepend: true) { prepare_for_destroy }
|
||||
after_destroy :rm_dir
|
||||
|
||||
|
|
@ -118,43 +120,6 @@ class Namespace < ActiveRecord::Base
|
|||
owner_name
|
||||
end
|
||||
|
||||
def move_dir
|
||||
if any_project_has_container_registry_tags?
|
||||
raise Gitlab::UpdatePathError.new('Namespace cannot be moved, because at least one project has tags in container registry')
|
||||
end
|
||||
|
||||
# Move the namespace directory in all storages paths used by member projects
|
||||
repository_storage_paths.each do |repository_storage_path|
|
||||
# Ensure old directory exists before moving it
|
||||
gitlab_shell.add_namespace(repository_storage_path, full_path_was)
|
||||
|
||||
unless gitlab_shell.mv_namespace(repository_storage_path, full_path_was, full_path)
|
||||
Rails.logger.error "Exception moving path #{repository_storage_path} from #{full_path_was} to #{full_path}"
|
||||
|
||||
# if we cannot move namespace directory we should rollback
|
||||
# db changes in order to prevent out of sync between db and fs
|
||||
raise Gitlab::UpdatePathError.new('namespace directory cannot be moved')
|
||||
end
|
||||
end
|
||||
|
||||
Gitlab::UploadsTransfer.new.rename_namespace(full_path_was, full_path)
|
||||
Gitlab::PagesTransfer.new.rename_namespace(full_path_was, full_path)
|
||||
|
||||
remove_exports!
|
||||
|
||||
# If repositories moved successfully we need to
|
||||
# send update instructions to users.
|
||||
# However we cannot allow rollback since we moved namespace dir
|
||||
# So we basically we mute exceptions in next actions
|
||||
begin
|
||||
send_update_instructions
|
||||
rescue
|
||||
# Returning false does not rollback after_* transaction but gives
|
||||
# us information about failing some of tasks
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def any_project_has_container_registry_tags?
|
||||
all_projects.any?(&:has_container_registry_tags?)
|
||||
end
|
||||
|
|
@ -206,14 +171,6 @@ class Namespace < ActiveRecord::Base
|
|||
parent_id_changed?
|
||||
end
|
||||
|
||||
def prepare_for_destroy
|
||||
old_repository_storage_paths
|
||||
end
|
||||
|
||||
def old_repository_storage_paths
|
||||
@old_repository_storage_paths ||= repository_storage_paths
|
||||
end
|
||||
|
||||
# Includes projects from this namespace and projects from all subgroups
|
||||
# that belongs to this namespace
|
||||
def all_projects
|
||||
|
|
@ -232,37 +189,6 @@ class Namespace < ActiveRecord::Base
|
|||
|
||||
private
|
||||
|
||||
def repository_storage_paths
|
||||
# We need to get the storage paths for all the projects, even the ones that are
|
||||
# pending delete. Unscoping also get rids of the default order, which causes
|
||||
# problems with SELECT DISTINCT.
|
||||
Project.unscoped do
|
||||
all_projects.select('distinct(repository_storage)').to_a.map(&:repository_storage_path)
|
||||
end
|
||||
end
|
||||
|
||||
def rm_dir
|
||||
# Remove the namespace directory in all storages paths used by member projects
|
||||
old_repository_storage_paths.each do |repository_storage_path|
|
||||
# Move namespace directory into trash.
|
||||
# We will remove it later async
|
||||
new_path = "#{full_path}+#{id}+deleted"
|
||||
|
||||
if gitlab_shell.mv_namespace(repository_storage_path, full_path, new_path)
|
||||
message = "Namespace directory \"#{full_path}\" moved to \"#{new_path}\""
|
||||
Gitlab::AppLogger.info message
|
||||
|
||||
# Remove namespace directroy async with delay so
|
||||
# GitLab has time to remove all projects first
|
||||
run_after_commit do
|
||||
GitlabShellWorker.perform_in(5.minutes, :rm_namespace, repository_storage_path, new_path)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
remove_exports!
|
||||
end
|
||||
|
||||
def refresh_access_of_projects_invited_groups
|
||||
Group
|
||||
.joins(project_group_links: :project)
|
||||
|
|
@ -270,22 +196,6 @@ class Namespace < ActiveRecord::Base
|
|||
.find_each(&:refresh_members_authorized_projects)
|
||||
end
|
||||
|
||||
def remove_exports!
|
||||
Gitlab::Popen.popen(%W(find #{export_path} -not -path #{export_path} -delete))
|
||||
end
|
||||
|
||||
def export_path
|
||||
File.join(Gitlab::ImportExport.storage_path, full_path_was)
|
||||
end
|
||||
|
||||
def full_path_was
|
||||
if parent
|
||||
parent.full_path + '/' + path_was
|
||||
else
|
||||
path_was
|
||||
end
|
||||
end
|
||||
|
||||
def nesting_level_allowed
|
||||
if ancestors.count > Group::NUMBER_OF_ANCESTORS_ALLOWED
|
||||
errors.add(:parent_id, "has too deep level of nesting")
|
||||
|
|
|
|||
|
|
@ -1,4 +1,8 @@
|
|||
class NotificationSetting < ActiveRecord::Base
|
||||
include IgnorableColumn
|
||||
|
||||
ignore_column :events
|
||||
|
||||
enum level: { global: 3, watch: 2, mention: 4, participating: 1, disabled: 0, custom: 5 }
|
||||
|
||||
default_value_for :level, NotificationSetting.levels[:global]
|
||||
|
|
@ -41,9 +45,6 @@ class NotificationSetting < ActiveRecord::Base
|
|||
:success_pipeline
|
||||
].freeze
|
||||
|
||||
store :events, coder: JSON
|
||||
before_save :convert_events
|
||||
|
||||
def self.find_or_create_for(source)
|
||||
setting = find_or_initialize_by(source: source)
|
||||
|
||||
|
|
@ -54,42 +55,17 @@ class NotificationSetting < ActiveRecord::Base
|
|||
setting
|
||||
end
|
||||
|
||||
# 1. Check if this event has a value stored in its database column.
|
||||
# 2. If it does, return that value.
|
||||
# 3. If it doesn't (the value is nil), return the value from the serialized
|
||||
# JSON hash in `events`.
|
||||
(EMAIL_EVENTS - [:failed_pipeline]).each do |event|
|
||||
define_method(event) do
|
||||
bool = super()
|
||||
|
||||
bool.nil? ? !!events[event] : bool
|
||||
end
|
||||
|
||||
alias_method :"#{event}?", event
|
||||
end
|
||||
|
||||
# Allow people to receive failed pipeline notifications if they already have
|
||||
# custom notifications enabled, as these are more like mentions than the other
|
||||
# custom settings.
|
||||
def failed_pipeline
|
||||
bool = super
|
||||
bool = events[:failed_pipeline] if bool.nil?
|
||||
|
||||
bool.nil? || bool
|
||||
end
|
||||
alias_method :failed_pipeline?, :failed_pipeline
|
||||
|
||||
def event_enabled?(event)
|
||||
respond_to?(event) && public_send(event)
|
||||
end
|
||||
|
||||
def convert_events
|
||||
return if events_before_type_cast.nil?
|
||||
|
||||
EMAIL_EVENTS.each do |event|
|
||||
write_attribute(event, public_send(event))
|
||||
end
|
||||
|
||||
write_attribute(:events, nil)
|
||||
respond_to?(event) && !!public_send(event)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ class Project < ActiveRecord::Base
|
|||
include ProjectFeaturesCompatibility
|
||||
include SelectForProjectAuthorization
|
||||
include Routable
|
||||
include Storage::LegacyProject
|
||||
|
||||
extend Gitlab::ConfigHelper
|
||||
|
||||
|
|
@ -43,9 +44,8 @@ class Project < ActiveRecord::Base
|
|||
default_value_for :snippets_enabled, gitlab_config_features.snippets
|
||||
default_value_for :only_allow_merge_if_all_discussions_are_resolved, false
|
||||
|
||||
after_create :ensure_dir_exist
|
||||
after_create :ensure_storage_path_exist
|
||||
after_create :create_project_feature, unless: :project_feature
|
||||
after_save :ensure_dir_exist, if: :namespace_id_changed?
|
||||
after_save :update_project_statistics, if: :namespace_id_changed?
|
||||
|
||||
# set last_activity_at to the same as created_at
|
||||
|
|
@ -67,6 +67,10 @@ class Project < ActiveRecord::Base
|
|||
|
||||
after_validation :check_pending_delete
|
||||
|
||||
# Legacy Storage specific hooks
|
||||
|
||||
after_save :ensure_storage_path_exist, if: :namespace_id_changed?
|
||||
|
||||
acts_as_taggable
|
||||
|
||||
attr_accessor :new_default_branch
|
||||
|
|
@ -375,7 +379,7 @@ class Project < ActiveRecord::Base
|
|||
begin
|
||||
Projects::HousekeepingService.new(project).execute
|
||||
rescue Projects::HousekeepingService::LeaseTaken => e
|
||||
Rails.logger.info("Could not perform housekeeping for project #{project.path_with_namespace} (#{project.id}): #{e}")
|
||||
Rails.logger.info("Could not perform housekeeping for project #{project.full_path} (#{project.id}): #{e}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -476,12 +480,12 @@ class Project < ActiveRecord::Base
|
|||
end
|
||||
|
||||
def repository
|
||||
@repository ||= Repository.new(path_with_namespace, self)
|
||||
@repository ||= Repository.new(full_path, self, disk_path: disk_path)
|
||||
end
|
||||
|
||||
def container_registry_url
|
||||
if Gitlab.config.registry.enabled
|
||||
"#{Gitlab.config.registry.host_port}/#{path_with_namespace.downcase}"
|
||||
"#{Gitlab.config.registry.host_port}/#{full_path.downcase}"
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -520,16 +524,16 @@ class Project < ActiveRecord::Base
|
|||
job_id =
|
||||
if forked?
|
||||
RepositoryForkWorker.perform_async(id, forked_from_project.repository_storage_path,
|
||||
forked_from_project.path_with_namespace,
|
||||
forked_from_project.full_path,
|
||||
self.namespace.full_path)
|
||||
else
|
||||
RepositoryImportWorker.perform_async(self.id)
|
||||
end
|
||||
|
||||
if job_id
|
||||
Rails.logger.info "Import job started for #{path_with_namespace} with job ID #{job_id}"
|
||||
Rails.logger.info "Import job started for #{full_path} with job ID #{job_id}"
|
||||
else
|
||||
Rails.logger.error "Import job failed to start for #{path_with_namespace}"
|
||||
Rails.logger.error "Import job failed to start for #{full_path}"
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -690,7 +694,7 @@ class Project < ActiveRecord::Base
|
|||
# `from` argument can be a Namespace or Project.
|
||||
def to_reference(from = nil, full: false)
|
||||
if full || cross_namespace_reference?(from)
|
||||
path_with_namespace
|
||||
full_path
|
||||
elsif cross_project_reference?(from)
|
||||
path
|
||||
end
|
||||
|
|
@ -714,7 +718,7 @@ class Project < ActiveRecord::Base
|
|||
author.ensure_incoming_email_token!
|
||||
|
||||
Gitlab::IncomingEmail.reply_address(
|
||||
"#{path_with_namespace}+#{author.incoming_email_token}")
|
||||
"#{full_path}+#{author.incoming_email_token}")
|
||||
end
|
||||
|
||||
def build_commit_note(commit)
|
||||
|
|
@ -941,7 +945,7 @@ class Project < ActiveRecord::Base
|
|||
end
|
||||
|
||||
def url_to_repo
|
||||
gitlab_shell.url_to_repo(path_with_namespace)
|
||||
gitlab_shell.url_to_repo(full_path)
|
||||
end
|
||||
|
||||
def repo_exists?
|
||||
|
|
@ -974,56 +978,6 @@ class Project < ActiveRecord::Base
|
|||
!group
|
||||
end
|
||||
|
||||
def rename_repo
|
||||
path_was = previous_changes['path'].first
|
||||
old_path_with_namespace = File.join(namespace.full_path, path_was)
|
||||
new_path_with_namespace = File.join(namespace.full_path, path)
|
||||
|
||||
Rails.logger.error "Attempting to rename #{old_path_with_namespace} -> #{new_path_with_namespace}"
|
||||
|
||||
if has_container_registry_tags?
|
||||
Rails.logger.error "Project #{old_path_with_namespace} cannot be renamed because container registry tags are present!"
|
||||
|
||||
# we currently doesn't support renaming repository if it contains images in container registry
|
||||
raise StandardError.new('Project cannot be renamed, because images are present in its container registry')
|
||||
end
|
||||
|
||||
expire_caches_before_rename(old_path_with_namespace)
|
||||
|
||||
if gitlab_shell.mv_repository(repository_storage_path, old_path_with_namespace, new_path_with_namespace)
|
||||
# If repository moved successfully we need to send update instructions to users.
|
||||
# However we cannot allow rollback since we moved repository
|
||||
# So we basically we mute exceptions in next actions
|
||||
begin
|
||||
gitlab_shell.mv_repository(repository_storage_path, "#{old_path_with_namespace}.wiki", "#{new_path_with_namespace}.wiki")
|
||||
send_move_instructions(old_path_with_namespace)
|
||||
expires_full_path_cache
|
||||
|
||||
@old_path_with_namespace = old_path_with_namespace
|
||||
|
||||
SystemHooksService.new.execute_hooks_for(self, :rename)
|
||||
|
||||
@repository = nil
|
||||
rescue => e
|
||||
Rails.logger.error "Exception renaming #{old_path_with_namespace} -> #{new_path_with_namespace}: #{e}"
|
||||
# Returning false does not rollback after_* transaction but gives
|
||||
# us information about failing some of tasks
|
||||
false
|
||||
end
|
||||
else
|
||||
Rails.logger.error "Repository could not be renamed: #{old_path_with_namespace} -> #{new_path_with_namespace}"
|
||||
|
||||
# if we cannot move namespace directory we should rollback
|
||||
# db changes in order to prevent out of sync between db and fs
|
||||
raise StandardError.new('repository cannot be renamed')
|
||||
end
|
||||
|
||||
Gitlab::AppLogger.info "Project was renamed: #{old_path_with_namespace} -> #{new_path_with_namespace}"
|
||||
|
||||
Gitlab::UploadsTransfer.new.rename_project(path_was, path, namespace.full_path)
|
||||
Gitlab::PagesTransfer.new.rename_project(path_was, path, namespace.full_path)
|
||||
end
|
||||
|
||||
# Expires various caches before a project is renamed.
|
||||
def expire_caches_before_rename(old_path)
|
||||
repo = Repository.new(old_path, self)
|
||||
|
|
@ -1048,7 +1002,7 @@ class Project < ActiveRecord::Base
|
|||
git_http_url: http_url_to_repo,
|
||||
namespace: namespace.name,
|
||||
visibility_level: visibility_level,
|
||||
path_with_namespace: path_with_namespace,
|
||||
path_with_namespace: full_path,
|
||||
default_branch: default_branch,
|
||||
ci_config_path: ci_config_path
|
||||
}
|
||||
|
|
@ -1109,19 +1063,6 @@ class Project < ActiveRecord::Base
|
|||
merge_requests.where(source_project_id: self.id)
|
||||
end
|
||||
|
||||
def create_repository(force: false)
|
||||
# Forked import is handled asynchronously
|
||||
return if forked? && !force
|
||||
|
||||
if gitlab_shell.add_repository(repository_storage_path, path_with_namespace)
|
||||
repository.after_create
|
||||
true
|
||||
else
|
||||
errors.add(:base, 'Failed to create repository via gitlab-shell')
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def ensure_repository
|
||||
create_repository(force: true) unless repository_exists?
|
||||
end
|
||||
|
|
@ -1257,7 +1198,7 @@ class Project < ActiveRecord::Base
|
|||
end
|
||||
|
||||
def pages_path
|
||||
File.join(Settings.pages.path, path_with_namespace)
|
||||
File.join(Settings.pages.path, disk_path)
|
||||
end
|
||||
|
||||
def public_pages_path
|
||||
|
|
@ -1265,9 +1206,21 @@ class Project < ActiveRecord::Base
|
|||
end
|
||||
|
||||
def remove_private_deploy_keys
|
||||
deploy_keys.where(public: false).delete_all
|
||||
exclude_keys_linked_to_other_projects = <<-SQL
|
||||
NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM deploy_keys_projects dkp2
|
||||
WHERE dkp2.deploy_key_id = deploy_keys_projects.deploy_key_id
|
||||
AND dkp2.project_id != deploy_keys_projects.project_id
|
||||
)
|
||||
SQL
|
||||
|
||||
deploy_keys.where(public: false)
|
||||
.where(exclude_keys_linked_to_other_projects)
|
||||
.delete_all
|
||||
end
|
||||
|
||||
# TODO: what to do here when not using Legacy Storage? Do we still need to rename and delay removal?
|
||||
def remove_pages
|
||||
::Projects::UpdatePagesConfigurationService.new(self).execute
|
||||
|
||||
|
|
@ -1315,7 +1268,7 @@ class Project < ActiveRecord::Base
|
|||
end
|
||||
|
||||
def export_path
|
||||
File.join(Gitlab::ImportExport.storage_path, path_with_namespace)
|
||||
File.join(Gitlab::ImportExport.storage_path, disk_path)
|
||||
end
|
||||
|
||||
def export_project_path
|
||||
|
|
@ -1327,16 +1280,12 @@ class Project < ActiveRecord::Base
|
|||
status.zero?
|
||||
end
|
||||
|
||||
def ensure_dir_exist
|
||||
gitlab_shell.add_namespace(repository_storage_path, namespace.full_path)
|
||||
end
|
||||
|
||||
def predefined_variables
|
||||
[
|
||||
{ key: 'CI_PROJECT_ID', value: id.to_s, public: true },
|
||||
{ key: 'CI_PROJECT_NAME', value: path, public: true },
|
||||
{ key: 'CI_PROJECT_PATH', value: path_with_namespace, public: true },
|
||||
{ key: 'CI_PROJECT_PATH_SLUG', value: path_with_namespace.parameterize, public: true },
|
||||
{ key: 'CI_PROJECT_PATH', value: full_path, public: true },
|
||||
{ key: 'CI_PROJECT_PATH_SLUG', value: full_path.parameterize, public: true },
|
||||
{ key: 'CI_PROJECT_NAMESPACE', value: namespace.full_path, public: true },
|
||||
{ key: 'CI_PROJECT_URL', value: web_url, public: true }
|
||||
]
|
||||
|
|
@ -1441,6 +1390,7 @@ class Project < ActiveRecord::Base
|
|||
|
||||
alias_method :name_with_namespace, :full_name
|
||||
alias_method :human_name, :full_name
|
||||
# @deprecated cannot remove yet because it has an index with its name in elasticsearch
|
||||
alias_method :path_with_namespace, :full_path
|
||||
|
||||
private
|
||||
|
|
@ -1495,7 +1445,7 @@ class Project < ActiveRecord::Base
|
|||
def pending_delete_twin
|
||||
return false unless path
|
||||
|
||||
Project.pending_delete.find_by_full_path(path_with_namespace)
|
||||
Project.pending_delete.find_by_full_path(full_path)
|
||||
end
|
||||
|
||||
##
|
||||
|
|
|
|||
|
|
@ -35,9 +35,9 @@ class FlowdockService < Service
|
|||
data[:after],
|
||||
token: token,
|
||||
repo: project.repository.path_to_repo,
|
||||
repo_url: "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}",
|
||||
commit_url: "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/commit/%s",
|
||||
diff_url: "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/compare/%s...%s"
|
||||
repo_url: "#{Gitlab.config.gitlab.url}/#{project.full_path}",
|
||||
commit_url: "#{Gitlab.config.gitlab.url}/#{project.full_path}/commit/%s",
|
||||
diff_url: "#{Gitlab.config.gitlab.url}/#{project.full_path}/compare/%s...%s"
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -140,7 +140,7 @@ class JiraService < IssueTrackerService
|
|||
url: resource_url(user_path(author))
|
||||
},
|
||||
project: {
|
||||
name: project.path_with_namespace,
|
||||
name: project.full_path,
|
||||
url: resource_url(namespace_project_path(project.namespace, project)) # rubocop:disable Cop/ProjectPathHelper
|
||||
},
|
||||
entity: {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
class ProjectWiki
|
||||
include Gitlab::ShellAdapter
|
||||
include Storage::LegacyProjectWiki
|
||||
|
||||
MARKUPS = {
|
||||
'Markdown' => :markdown,
|
||||
|
|
@ -26,16 +27,19 @@ class ProjectWiki
|
|||
@project.path + '.wiki'
|
||||
end
|
||||
|
||||
def path_with_namespace
|
||||
@project.path_with_namespace + ".wiki"
|
||||
def full_path
|
||||
@project.full_path + '.wiki'
|
||||
end
|
||||
|
||||
# @deprecated use full_path when you need it for an URL route or disk_path when you want to point to the filesystem
|
||||
alias_method :path_with_namespace, :full_path
|
||||
|
||||
def web_url
|
||||
Gitlab::Routing.url_helpers.project_wiki_url(@project, :home)
|
||||
end
|
||||
|
||||
def url_to_repo
|
||||
gitlab_shell.url_to_repo(path_with_namespace)
|
||||
gitlab_shell.url_to_repo(full_path)
|
||||
end
|
||||
|
||||
def ssh_url_to_repo
|
||||
|
|
@ -43,11 +47,11 @@ class ProjectWiki
|
|||
end
|
||||
|
||||
def http_url_to_repo
|
||||
"#{Gitlab.config.gitlab.url}/#{path_with_namespace}.git"
|
||||
"#{Gitlab.config.gitlab.url}/#{full_path}.git"
|
||||
end
|
||||
|
||||
def wiki_base_path
|
||||
[Gitlab.config.gitlab.relative_url_root, "/", @project.path_with_namespace, "/wikis"].join('')
|
||||
[Gitlab.config.gitlab.relative_url_root, '/', @project.full_path, '/wikis'].join('')
|
||||
end
|
||||
|
||||
# Returns the Gollum::Wiki object.
|
||||
|
|
@ -134,7 +138,7 @@ class ProjectWiki
|
|||
end
|
||||
|
||||
def repository
|
||||
@repository ||= Repository.new(path_with_namespace, @project)
|
||||
@repository ||= Repository.new(full_path, @project, disk_path: disk_path)
|
||||
end
|
||||
|
||||
def default_branch
|
||||
|
|
@ -142,7 +146,7 @@ class ProjectWiki
|
|||
end
|
||||
|
||||
def create_repo!
|
||||
if init_repo(path_with_namespace)
|
||||
if init_repo(disk_path)
|
||||
wiki = Gollum::Wiki.new(path_to_repo)
|
||||
else
|
||||
raise CouldNotCreateWikiError
|
||||
|
|
@ -162,15 +166,15 @@ class ProjectWiki
|
|||
web_url: web_url,
|
||||
git_ssh_url: ssh_url_to_repo,
|
||||
git_http_url: http_url_to_repo,
|
||||
path_with_namespace: path_with_namespace,
|
||||
path_with_namespace: full_path,
|
||||
default_branch: default_branch
|
||||
}
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def init_repo(path_with_namespace)
|
||||
gitlab_shell.add_repository(project.repository_storage_path, path_with_namespace)
|
||||
def init_repo(disk_path)
|
||||
gitlab_shell.add_repository(project.repository_storage_path, disk_path)
|
||||
end
|
||||
|
||||
def commit_details(action, message = nil, title = nil)
|
||||
|
|
@ -184,7 +188,7 @@ class ProjectWiki
|
|||
end
|
||||
|
||||
def path_to_repo
|
||||
@path_to_repo ||= File.join(project.repository_storage_path, "#{path_with_namespace}.git")
|
||||
@path_to_repo ||= File.join(project.repository_storage_path, "#{disk_path}.git")
|
||||
end
|
||||
|
||||
def update_project_activity
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ class Repository
|
|||
include Gitlab::ShellAdapter
|
||||
include RepositoryMirroring
|
||||
|
||||
attr_accessor :path_with_namespace, :project
|
||||
attr_accessor :full_path, :disk_path, :project
|
||||
|
||||
delegate :ref_name_for_sha, to: :raw_repository
|
||||
|
||||
|
|
@ -52,13 +52,14 @@ class Repository
|
|||
end
|
||||
end
|
||||
|
||||
def initialize(path_with_namespace, project)
|
||||
@path_with_namespace = path_with_namespace
|
||||
def initialize(full_path, project, disk_path: nil)
|
||||
@full_path = full_path
|
||||
@disk_path = disk_path || full_path
|
||||
@project = project
|
||||
end
|
||||
|
||||
def raw_repository
|
||||
return nil unless path_with_namespace
|
||||
return nil unless full_path
|
||||
|
||||
@raw_repository ||= initialize_raw_repository
|
||||
end
|
||||
|
|
@ -66,7 +67,7 @@ class Repository
|
|||
# Return absolute path to repository
|
||||
def path_to_repo
|
||||
@path_to_repo ||= File.expand_path(
|
||||
File.join(repository_storage_path, path_with_namespace + ".git")
|
||||
File.join(repository_storage_path, disk_path + '.git')
|
||||
)
|
||||
end
|
||||
|
||||
|
|
@ -469,7 +470,7 @@ class Repository
|
|||
|
||||
# Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/314
|
||||
def exists?
|
||||
return false unless path_with_namespace
|
||||
return false unless full_path
|
||||
|
||||
Gitlab::GitalyClient.migrate(:repository_exists) do |enabled|
|
||||
if enabled
|
||||
|
|
@ -943,7 +944,7 @@ class Repository
|
|||
if is_enabled
|
||||
raw_repository.is_ancestor?(ancestor_id, descendant_id)
|
||||
else
|
||||
merge_base_commit(ancestor_id, descendant_id) == ancestor_id
|
||||
rugged_is_ancestor?(ancestor_id, descendant_id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1005,7 +1006,7 @@ class Repository
|
|||
end
|
||||
|
||||
def fetch_remote(remote, forced: false, no_tags: false)
|
||||
gitlab_shell.fetch_remote(repository_storage_path, path_with_namespace, remote, forced: forced, no_tags: no_tags)
|
||||
gitlab_shell.fetch_remote(repository_storage_path, disk_path, remote, forced: forced, no_tags: no_tags)
|
||||
end
|
||||
|
||||
def fetch_ref(source_path, source_ref, target_ref)
|
||||
|
|
@ -1104,7 +1105,8 @@ class Repository
|
|||
end
|
||||
|
||||
def cache
|
||||
@cache ||= RepositoryCache.new(path_with_namespace, @project.id)
|
||||
# TODO: should we use UUIDs here? We could move repositories without clearing this cache
|
||||
@cache ||= RepositoryCache.new(full_path, @project.id)
|
||||
end
|
||||
|
||||
def tags_sorted_by_committed_date
|
||||
|
|
@ -1127,7 +1129,7 @@ class Repository
|
|||
end
|
||||
|
||||
def repository_event(event, tags = {})
|
||||
Gitlab::Metrics.add_event(event, { path: path_with_namespace }.merge(tags))
|
||||
Gitlab::Metrics.add_event(event, { path: full_path }.merge(tags))
|
||||
end
|
||||
|
||||
def create_commit(params = {})
|
||||
|
|
@ -1141,6 +1143,6 @@ class Repository
|
|||
end
|
||||
|
||||
def initialize_raw_repository
|
||||
Gitlab::Git::Repository.new(project.repository_storage, path_with_namespace + '.git')
|
||||
Gitlab::Git::Repository.new(project.repository_storage, disk_path + '.git')
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ class GlobalPolicy < BasePolicy
|
|||
prevent :log_in
|
||||
end
|
||||
|
||||
rule { admin | ~restricted_public_level }.policy do
|
||||
rule { ~(anonymous & restricted_public_level) }.policy do
|
||||
enable :read_users_list
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ class DeleteMergedBranchesService < BaseService
|
|||
# Prevent deletion of branches relevant to open merge requests
|
||||
branches -= merge_request_branch_names
|
||||
# Prevent deletion of protected branches
|
||||
branches -= project.protected_branches.pluck(:name)
|
||||
branches = branches.reject { |branch| project.protected_for?(branch) }
|
||||
|
||||
branches.each do |branch|
|
||||
DeleteBranchService.new(project, current_user).execute(branch)
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ class GitOperationService
|
|||
start_branch_name = nil if start_repository.empty_repo?
|
||||
|
||||
if start_branch_name && !start_repository.branch_exists?(start_branch_name)
|
||||
raise ArgumentError, "Cannot find branch #{start_branch_name} in #{start_repository.path_with_namespace}"
|
||||
raise ArgumentError, "Cannot find branch #{start_branch_name} in #{start_repository.full_path}"
|
||||
end
|
||||
|
||||
update_branch_with_hooks(branch_name) do
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ module Projects
|
|||
def async_execute
|
||||
project.update_attribute(:pending_delete, true)
|
||||
job_id = ProjectDestroyWorker.perform_async(project.id, current_user.id, params)
|
||||
Rails.logger.info("User #{current_user.id} scheduled destruction of project #{project.path_with_namespace} with job ID #{job_id}")
|
||||
Rails.logger.info("User #{current_user.id} scheduled destruction of project #{project.full_path} with job ID #{job_id}")
|
||||
end
|
||||
|
||||
def execute
|
||||
|
|
@ -40,7 +40,7 @@ module Projects
|
|||
private
|
||||
|
||||
def repo_path
|
||||
project.path_with_namespace
|
||||
project.disk_path
|
||||
end
|
||||
|
||||
def wiki_path
|
||||
|
|
@ -127,7 +127,7 @@ module Projects
|
|||
def flush_caches(project)
|
||||
project.repository.before_delete
|
||||
|
||||
Repository.new(wiki_path, project).before_delete
|
||||
Repository.new(wiki_path, project, disk_path: repo_path).before_delete
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ module Projects
|
|||
module ImportExport
|
||||
class ExportService < BaseService
|
||||
def execute(_options = {})
|
||||
@shared = Gitlab::ImportExport::Shared.new(relative_path: File.join(project.path_with_namespace, 'work'))
|
||||
@shared = Gitlab::ImportExport::Shared.new(relative_path: File.join(project.disk_path, 'work'))
|
||||
save_all
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ module Projects
|
|||
|
||||
success
|
||||
rescue => e
|
||||
error("Error importing repository #{project.import_url} into #{project.path_with_namespace} - #{e.message}")
|
||||
error("Error importing repository #{project.import_url} into #{project.full_path} - #{e.message}")
|
||||
end
|
||||
|
||||
private
|
||||
|
|
@ -51,7 +51,7 @@ module Projects
|
|||
end
|
||||
|
||||
def clone_repository
|
||||
gitlab_shell.import_repository(project.repository_storage_path, project.path_with_namespace, project.import_url)
|
||||
gitlab_shell.import_repository(project.repository_storage_path, project.disk_path, project.import_url)
|
||||
end
|
||||
|
||||
def fetch_repository
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ module Projects
|
|||
private
|
||||
|
||||
def transfer(project)
|
||||
@old_path = project.path_with_namespace
|
||||
@old_path = project.full_path
|
||||
@old_group = project.group
|
||||
@new_path = File.join(@new_namespace.try(:full_path) || '', project.path)
|
||||
@old_namespace = project.namespace
|
||||
|
|
@ -61,11 +61,13 @@ module Projects
|
|||
project.send_move_instructions(@old_path)
|
||||
|
||||
# Move main repository
|
||||
# TODO: check storage type and NOOP when not using Legacy
|
||||
unless move_repo_folder(@old_path, @new_path)
|
||||
raise TransferError.new('Cannot move project')
|
||||
end
|
||||
|
||||
# Move wiki repo also if present
|
||||
# TODO: check storage type and NOOP when not using Legacy
|
||||
move_repo_folder("#{@old_path}.wiki", "#{@new_path}.wiki")
|
||||
|
||||
# Move missing group labels to project
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ class SystemHooksService
|
|||
key: model.key,
|
||||
id: model.id
|
||||
)
|
||||
|
||||
|
||||
if model.user
|
||||
data[:username] = model.user.username
|
||||
end
|
||||
|
|
@ -56,7 +56,7 @@ class SystemHooksService
|
|||
when GroupMember
|
||||
data.merge!(group_member_data(model))
|
||||
end
|
||||
|
||||
|
||||
data
|
||||
end
|
||||
|
||||
|
|
@ -79,7 +79,7 @@ class SystemHooksService
|
|||
{
|
||||
name: model.name,
|
||||
path: model.path,
|
||||
path_with_namespace: model.path_with_namespace,
|
||||
path_with_namespace: model.full_path,
|
||||
project_id: model.id,
|
||||
owner_name: owner.name,
|
||||
owner_email: owner.respond_to?(:email) ? owner.email : "",
|
||||
|
|
@ -93,7 +93,7 @@ class SystemHooksService
|
|||
{
|
||||
project_name: project.name,
|
||||
project_path: project.path,
|
||||
project_path_with_namespace: project.path_with_namespace,
|
||||
project_path_with_namespace: project.full_path,
|
||||
project_id: project.id,
|
||||
user_username: model.user.username,
|
||||
user_name: model.user.name,
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@ class WebHookService
|
|||
request_headers: build_headers(hook_name),
|
||||
request_data: request_data,
|
||||
response_headers: format_response_headers(response),
|
||||
response_body: response.body,
|
||||
response_body: safe_response_body(response),
|
||||
response_status: response.code,
|
||||
internal_error_message: error_message
|
||||
)
|
||||
|
|
@ -124,4 +124,10 @@ class WebHookService
|
|||
def format_response_headers(response)
|
||||
response.headers.each_capitalized.to_h
|
||||
end
|
||||
|
||||
def safe_response_body(response)
|
||||
return '' unless response.body
|
||||
|
||||
response.body.encode('UTF-8', invalid: :replace, undef: :replace, replace: '')
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ class FileUploader < GitlabUploader
|
|||
#
|
||||
# Returns a String without a trailing slash
|
||||
def self.dynamic_path_segment(model)
|
||||
File.join(CarrierWave.root, base_dir, model.path_with_namespace)
|
||||
File.join(CarrierWave.root, base_dir, model.full_path)
|
||||
end
|
||||
|
||||
attr_accessor :model
|
||||
|
|
|
|||
|
|
@ -322,7 +322,7 @@
|
|||
\. This setting requires a
|
||||
= link_to 'restart', help_page_path('administration/restart_gitlab')
|
||||
to take effect.
|
||||
= link_to icon('question-circle'), help_page_path('administration/monitoring/performance/introduction')
|
||||
= link_to icon('question-circle'), help_page_path('administration/monitoring/prometheus/index')
|
||||
.form-group
|
||||
.col-sm-offset-2.col-sm-10
|
||||
.checkbox
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@
|
|||
%span.badge
|
||||
= storage_counter(project.statistics.storage_size)
|
||||
%span.pull-right.light
|
||||
%span.monospace= project.path_with_namespace + ".git"
|
||||
%span.monospace= project.full_path + '.git'
|
||||
.panel-footer
|
||||
= paginate @projects, param_name: 'projects_page', theme: 'gitlab'
|
||||
|
||||
|
|
@ -88,7 +88,7 @@
|
|||
%span.badge
|
||||
= storage_counter(project.statistics.storage_size)
|
||||
%span.pull-right.light
|
||||
%span.monospace= project.path_with_namespace + ".git"
|
||||
%span.monospace= project.full_path + '.git'
|
||||
|
||||
.col-md-6
|
||||
- if can?(current_user, :admin_group_member, @group)
|
||||
|
|
|
|||
|
|
@ -4,6 +4,10 @@
|
|||
= content_for :meta_tags do
|
||||
= auto_discovery_link_tag(:atom, params.merge(rss_url_options), title: "#{@group.name} issues")
|
||||
|
||||
- content_for :page_specific_javascripts do
|
||||
= webpack_bundle_tag 'common_vue'
|
||||
= webpack_bundle_tag 'filtered_search'
|
||||
|
||||
- if show_new_nav? && group_issues_exists
|
||||
- content_for :breadcrumbs_extra do
|
||||
= link_to params.merge(rss_url_options), class: 'btn btn-default append-right-10' do
|
||||
|
|
@ -20,7 +24,7 @@
|
|||
Subscribe
|
||||
= render 'shared/new_project_item_select', path: 'issues/new', label: "New issue"
|
||||
|
||||
= render 'shared/issuable/filter', type: :issues
|
||||
= render 'shared/issuable/search_bar', type: :issues
|
||||
|
||||
.row-content-block.second-block
|
||||
Only issues from the
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
- page_title "UI Development Kit", "Help"
|
||||
- lorem = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed fermentum nisi sapien, non consequat lectus aliquam ultrices. Suspendisse sodales est euismod nunc condimentum, a consectetur diam ornare."
|
||||
- content_for :page_specific_javascripts do
|
||||
= webpack_bundle_tag('ui_development_kit')
|
||||
|
||||
.gitlab-ui-dev-kit
|
||||
%h1 GitLab UI development kit
|
||||
|
|
@ -407,29 +409,6 @@
|
|||
.dropdown-content
|
||||
.dropdown-loading
|
||||
= icon('spinner spin')
|
||||
:javascript
|
||||
$('#js-project-dropdown').glDropdown({
|
||||
data: function (term, callback) {
|
||||
Api.projects(term, { order_by: 'last_activity_at' }, function (data) {
|
||||
callback(data);
|
||||
});
|
||||
},
|
||||
text: function (project) {
|
||||
return project.name_with_namespace || project.name;
|
||||
},
|
||||
selectable: true,
|
||||
fieldName: "author_id",
|
||||
filterable: true,
|
||||
search: {
|
||||
fields: ['name_with_namespace']
|
||||
},
|
||||
id: function (data) {
|
||||
return data.id;
|
||||
},
|
||||
isSelected: function (data) {
|
||||
return data.id === 2;
|
||||
}
|
||||
})
|
||||
|
||||
.example
|
||||
%div
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@
|
|||
%td
|
||||
= provider_project_link(provider, project.import_source)
|
||||
%td
|
||||
= link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project]
|
||||
= link_to project.full_path, [project.namespace.becomes(Namespace), project]
|
||||
%td.job-status
|
||||
- if project.import_status == 'finished'
|
||||
%span
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
job.attr("id", "project_#{@project.id}")
|
||||
target_field = job.find(".import-target")
|
||||
target_field.empty()
|
||||
target_field.append('#{link_to @project.path_with_namespace, project_path(@project)}')
|
||||
target_field.append('#{link_to @project.full_path, project_path(@project)}')
|
||||
$("table.import-jobs tbody").prepend(job)
|
||||
job.addClass("active").find(".import-actions").html("<i class='fa fa-spinner fa-spin'></i> started")
|
||||
- else
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@
|
|||
%td
|
||||
= link_to project.import_source, "https://bitbucket.org/#{project.import_source}", target: '_blank', rel: 'noopener noreferrer'
|
||||
%td
|
||||
= link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project]
|
||||
= link_to project.full_path, [project.namespace.becomes(Namespace), project]
|
||||
%td.job-status
|
||||
- if project.import_status == 'finished'
|
||||
%span
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@
|
|||
%td
|
||||
= project.import_source
|
||||
%td
|
||||
= link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project]
|
||||
= link_to project.full_path, [project.namespace.becomes(Namespace), project]
|
||||
%td.job-status
|
||||
- if project.import_status == 'finished'
|
||||
%span
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@
|
|||
%td
|
||||
= link_to project.import_source, "https://gitlab.com/#{project.import_source}", target: "_blank"
|
||||
%td
|
||||
= link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project]
|
||||
= link_to project.full_path, [project.namespace.becomes(Namespace), project]
|
||||
%td.job-status
|
||||
- if project.import_status == 'finished'
|
||||
%span
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@
|
|||
%td
|
||||
= link_to project.import_source, "https://code.google.com/p/#{project.import_source}", target: "_blank", rel: 'noopener noreferrer'
|
||||
%td
|
||||
= link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project]
|
||||
= link_to project.full_path, [project.namespace.becomes(Namespace), project]
|
||||
%td.job-status
|
||||
- if project.import_status == 'finished'
|
||||
%span
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
-# haml-lint:disable InlineJavaScript
|
||||
:javascript
|
||||
var _gaq = _gaq || [];
|
||||
_gaq.push(['_setAccount', '#{extra_config.google_analytics_id}']);
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
- noteable_type = @noteable.class if @noteable.present?
|
||||
|
||||
- if project
|
||||
-# haml-lint:disable InlineJavaScript
|
||||
:javascript
|
||||
gl.GfmAutoComplete = gl.GfmAutoComplete || {};
|
||||
gl.GfmAutoComplete.dataSources = {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<!-- Piwik -->
|
||||
-# haml-lint:disable InlineJavaScript
|
||||
:javascript
|
||||
var _paq = _paq || [];
|
||||
_paq.push(['trackPageView']);
|
||||
|
|
|
|||
|
|
@ -10,7 +10,9 @@
|
|||
%ul.sidebar-top-level-items
|
||||
= nav_link(controller: %w(dashboard admin projects users groups jobs runners cohorts), html_options: {class: 'home'}) do
|
||||
= link_to admin_root_path, title: 'Overview', class: 'shortcuts-tree' do
|
||||
%span
|
||||
.nav-icon-container
|
||||
= custom_icon('overview')
|
||||
%span.nav-item-name
|
||||
Overview
|
||||
|
||||
%ul.sidebar-sub-level-items
|
||||
|
|
@ -45,7 +47,9 @@
|
|||
|
||||
= nav_link(controller: %w(conversational_development_index system_info background_jobs logs health_check requests_profiles)) do
|
||||
= link_to admin_conversational_development_index_path, title: 'Monitoring' do
|
||||
%span
|
||||
.nav-icon-container
|
||||
= custom_icon('monitoring')
|
||||
%span.nav-item-name
|
||||
Monitoring
|
||||
|
||||
%ul.sidebar-sub-level-items
|
||||
|
|
@ -76,52 +80,72 @@
|
|||
|
||||
= nav_link(controller: :broadcast_messages) do
|
||||
= link_to admin_broadcast_messages_path, title: 'Messages' do
|
||||
%span
|
||||
.nav-icon-container
|
||||
= custom_icon('messages')
|
||||
%span.nav-item-name
|
||||
Messages
|
||||
= nav_link(controller: [:hooks, :hook_logs]) do
|
||||
= link_to admin_hooks_path, title: 'Hooks' do
|
||||
%span
|
||||
.nav-icon-container
|
||||
= custom_icon('system_hooks')
|
||||
%span.nav-item-name
|
||||
System Hooks
|
||||
|
||||
= nav_link(controller: :applications) do
|
||||
= link_to admin_applications_path, title: 'Applications' do
|
||||
%span
|
||||
.nav-icon-container
|
||||
= custom_icon('applications')
|
||||
%span.nav-item-name
|
||||
Applications
|
||||
|
||||
= nav_link(controller: :abuse_reports) do
|
||||
= link_to admin_abuse_reports_path, title: "Abuse Reports" do
|
||||
%span
|
||||
%span.badge.count= number_with_delimiter(AbuseReport.count(:all))
|
||||
.nav-icon-container
|
||||
= custom_icon('abuse_reports')
|
||||
%span.nav-item-name
|
||||
Abuse Reports
|
||||
%span.badge.count= number_with_delimiter(AbuseReport.count(:all))
|
||||
|
||||
- if akismet_enabled?
|
||||
= nav_link(controller: :spam_logs) do
|
||||
= link_to admin_spam_logs_path, title: "Spam Logs" do
|
||||
%span
|
||||
.nav-icon-container
|
||||
= custom_icon('mr_bold')
|
||||
%span.nav-item-name
|
||||
Spam Logs
|
||||
|
||||
= nav_link(controller: :deploy_keys) do
|
||||
= link_to admin_deploy_keys_path, title: 'Deploy Keys' do
|
||||
%span
|
||||
.nav-icon-container
|
||||
= custom_icon('key')
|
||||
%span.nav-item-name
|
||||
Deploy Keys
|
||||
|
||||
= nav_link(controller: :services) do
|
||||
= link_to admin_application_settings_services_path, title: 'Service Templates' do
|
||||
%span
|
||||
.nav-icon-container
|
||||
= custom_icon('service_templates')
|
||||
%span.nav-item-name
|
||||
Service Templates
|
||||
|
||||
= nav_link(controller: :labels) do
|
||||
= link_to admin_labels_path, title: 'Labels' do
|
||||
%span
|
||||
.nav-icon-container
|
||||
= custom_icon('labels')
|
||||
%span.nav-item-name
|
||||
Labels
|
||||
|
||||
= nav_link(controller: :appearances) do
|
||||
= link_to admin_appearances_path, title: 'Appearances' do
|
||||
%span
|
||||
.nav-icon-container
|
||||
= custom_icon('appearance')
|
||||
%span.nav-item-name
|
||||
Appearance
|
||||
|
||||
%li.divider
|
||||
= nav_link(controller: :application_settings) do
|
||||
= link_to admin_application_settings_path, title: 'Settings' do
|
||||
%span
|
||||
.nav-icon-container
|
||||
= custom_icon('settings')
|
||||
%span.nav-item-name
|
||||
Settings
|
||||
|
|
|
|||
|
|
@ -11,7 +11,9 @@
|
|||
%ul.sidebar-top-level-items
|
||||
= nav_link(path: ['groups#show', 'groups#activity', 'groups#subgroups'], html_options: { class: 'home' }) do
|
||||
= link_to group_path(@group), title: 'About group' do
|
||||
%span
|
||||
.nav-icon-container
|
||||
= custom_icon('project')
|
||||
%span.nav-item-name
|
||||
About
|
||||
|
||||
%ul.sidebar-sub-level-items
|
||||
|
|
@ -27,10 +29,12 @@
|
|||
|
||||
= nav_link(path: ['groups#issues', 'labels#index', 'milestones#index']) do
|
||||
= link_to issues_group_path(@group), title: 'Issues' do
|
||||
%span
|
||||
.nav-icon-container
|
||||
= custom_icon('issues')
|
||||
%span.nav-item-name
|
||||
- issues = IssuesFinder.new(current_user, group_id: @group.id, state: 'opened').execute
|
||||
%span.badge.count= number_with_delimiter(issues.count)
|
||||
Issues
|
||||
%span.badge.count= number_with_delimiter(issues.count)
|
||||
|
||||
%ul.sidebar-sub-level-items
|
||||
= nav_link(path: 'groups#issues', html_options: { class: 'home' }) do
|
||||
|
|
@ -50,18 +54,24 @@
|
|||
|
||||
= nav_link(path: 'groups#merge_requests') do
|
||||
= link_to merge_requests_group_path(@group), title: 'Merge Requests' do
|
||||
%span
|
||||
.nav-icon-container
|
||||
= custom_icon('mr_bold')
|
||||
%span.nav-item-name
|
||||
- merge_requests = MergeRequestsFinder.new(current_user, group_id: @group.id, state: 'opened', non_archived: true).execute
|
||||
%span.badge.count= number_with_delimiter(merge_requests.count)
|
||||
Merge Requests
|
||||
%span.badge.count= number_with_delimiter(merge_requests.count)
|
||||
= nav_link(path: 'group_members#index') do
|
||||
= link_to group_group_members_path(@group), title: 'Members' do
|
||||
%span
|
||||
.nav-icon-container
|
||||
= custom_icon('members')
|
||||
%span.nav-item-name
|
||||
Members
|
||||
- if current_user && can?(current_user, :admin_group, @group)
|
||||
= nav_link(path: %w[groups#projects groups#edit ci_cd#show]) do
|
||||
= link_to edit_group_path(@group), title: 'Settings' do
|
||||
%span
|
||||
.nav-icon-container
|
||||
= custom_icon('settings')
|
||||
%span.nav-item-name
|
||||
Settings
|
||||
%ul.sidebar-sub-level-items
|
||||
= nav_link(path: 'groups#edit') do
|
||||
|
|
|
|||
|
|
@ -10,52 +10,76 @@
|
|||
%ul.sidebar-top-level-items
|
||||
= nav_link(path: 'profiles#show', html_options: {class: 'home'}) do
|
||||
= link_to profile_path, title: 'Profile Settings' do
|
||||
%span
|
||||
.nav-icon-container
|
||||
= custom_icon('profile')
|
||||
%span.nav-item-name
|
||||
Profile
|
||||
= nav_link(controller: [:accounts, :two_factor_auths]) do
|
||||
= link_to profile_account_path, title: 'Account' do
|
||||
%span
|
||||
.nav-icon-container
|
||||
= custom_icon('account')
|
||||
%span.nav-item-name
|
||||
Account
|
||||
- if current_application_settings.user_oauth_applications?
|
||||
= nav_link(controller: 'oauth/applications') do
|
||||
= link_to applications_profile_path, title: 'Applications' do
|
||||
%span
|
||||
.nav-icon-container
|
||||
= custom_icon('applications')
|
||||
%span.nav-item-name
|
||||
Applications
|
||||
= nav_link(controller: :chat_names) do
|
||||
= link_to profile_chat_names_path, title: 'Chat' do
|
||||
%span
|
||||
.nav-icon-container
|
||||
= custom_icon('chat')
|
||||
%span.nav-item-name
|
||||
Chat
|
||||
= nav_link(controller: :personal_access_tokens) do
|
||||
= link_to profile_personal_access_tokens_path, title: 'Access Tokens' do
|
||||
%span
|
||||
.nav-icon-container
|
||||
= custom_icon('access_tokens')
|
||||
%span.nav-item-name
|
||||
Access Tokens
|
||||
= nav_link(controller: :emails) do
|
||||
= link_to profile_emails_path, title: 'Emails' do
|
||||
%span
|
||||
.nav-icon-container
|
||||
= custom_icon('emails')
|
||||
%span.nav-item-name
|
||||
Emails
|
||||
- unless current_user.ldap_user?
|
||||
= nav_link(controller: :passwords) do
|
||||
= link_to edit_profile_password_path, title: 'Password' do
|
||||
%span
|
||||
.nav-icon-container
|
||||
= custom_icon('lock')
|
||||
%span.nav-item-name
|
||||
Password
|
||||
= nav_link(controller: :notifications) do
|
||||
= link_to profile_notifications_path, title: 'Notifications' do
|
||||
%span
|
||||
.nav-icon-container
|
||||
= custom_icon('notifications')
|
||||
%span.nav-item-name
|
||||
Notifications
|
||||
|
||||
= nav_link(controller: :keys) do
|
||||
= link_to profile_keys_path, title: 'SSH Keys' do
|
||||
%span
|
||||
.nav-icon-container
|
||||
= custom_icon('key')
|
||||
%span.nav-item-name
|
||||
SSH Keys
|
||||
= nav_link(controller: :gpg_keys) do
|
||||
= link_to profile_gpg_keys_path, title: 'GPG Keys' do
|
||||
%span
|
||||
.nav-icon-container
|
||||
= custom_icon('key_2')
|
||||
%span.nav-item-name
|
||||
GPG Keys
|
||||
= nav_link(controller: :preferences) do
|
||||
= link_to profile_preferences_path, title: 'Preferences' do
|
||||
%span
|
||||
.nav-icon-container
|
||||
= custom_icon('preferences')
|
||||
%span.nav-item-name
|
||||
Preferences
|
||||
= nav_link(path: 'profiles#audit_log') do
|
||||
= link_to audit_log_profile_path, title: 'Authentication log' do
|
||||
%span
|
||||
.nav-icon-container
|
||||
= custom_icon('authentication_log')
|
||||
%span.nav-item-name
|
||||
Authentication log
|
||||
|
|
|
|||
|
|
@ -12,7 +12,9 @@
|
|||
%ul.sidebar-top-level-items
|
||||
= nav_link(path: ['projects#show', 'projects#activity', 'cycle_analytics#show'], html_options: { class: 'home' }) do
|
||||
= link_to project_path(@project), title: 'About project', class: 'shortcuts-project' do
|
||||
%span
|
||||
.nav-icon-container
|
||||
= custom_icon('project')
|
||||
%span.nav-item-name
|
||||
About
|
||||
|
||||
%ul.sidebar-sub-level-items
|
||||
|
|
@ -32,7 +34,9 @@
|
|||
- if project_nav_tab? :files
|
||||
= nav_link(controller: %w(tree blob blame edit_tree new_tree find_file commit commits compare projects/repositories tags branches releases graphs network)) do
|
||||
= link_to project_tree_path(@project), title: 'Repository', class: 'shortcuts-tree' do
|
||||
%span
|
||||
.nav-icon-container
|
||||
= custom_icon('doc_text')
|
||||
%span.nav-item-name
|
||||
Repository
|
||||
|
||||
%ul.sidebar-sub-level-items
|
||||
|
|
@ -71,58 +75,57 @@
|
|||
- if project_nav_tab? :container_registry
|
||||
= nav_link(controller: %w[projects/registry/repositories]) do
|
||||
= link_to project_container_registry_index_path(@project), title: 'Container Registry', class: 'shortcuts-container-registry' do
|
||||
%span
|
||||
.nav-icon-container
|
||||
= custom_icon('mr_bold')
|
||||
%span.nav-item-name
|
||||
Registry
|
||||
|
||||
- if project_nav_tab? :issues
|
||||
= nav_link(controller: @project.issues_enabled? ? [:issues, :labels, :milestones, :boards] : :issues) do
|
||||
= link_to project_issues_path(@project), title: 'Issues', class: 'shortcuts-issues' do
|
||||
%span
|
||||
- if @project.issues_enabled?
|
||||
%span.badge.count.issue_counter= number_with_delimiter(IssuesFinder.new(current_user, project_id: @project.id).execute.opened.count)
|
||||
.nav-icon-container
|
||||
= custom_icon('issues')
|
||||
%span.nav-item-name
|
||||
Issues
|
||||
- if @project.issues_enabled?
|
||||
%span.badge.count.issue_counter= number_with_delimiter(IssuesFinder.new(current_user, project_id: @project.id).execute.opened.count)
|
||||
|
||||
%ul.sidebar-sub-level-items
|
||||
- if project_nav_tab?(:issues) && !current_controller?(:merge_requests)
|
||||
= nav_link(controller: :issues) do
|
||||
= link_to project_issues_path(@project), title: 'Issues' do
|
||||
%span
|
||||
List
|
||||
= nav_link(controller: :issues) do
|
||||
= link_to project_issues_path(@project), title: 'Issues' do
|
||||
%span
|
||||
List
|
||||
|
||||
= nav_link(controller: :boards) do
|
||||
= link_to project_boards_path(@project), title: 'Board' do
|
||||
%span
|
||||
Board
|
||||
= nav_link(controller: :boards) do
|
||||
= link_to project_boards_path(@project), title: 'Board' do
|
||||
%span
|
||||
Board
|
||||
|
||||
- if project_nav_tab?(:merge_requests) && current_controller?(:merge_requests)
|
||||
= nav_link(controller: :merge_requests) do
|
||||
= link_to project_merge_requests_path(@project), title: 'Merge Requests' do
|
||||
%span
|
||||
Merge Requests
|
||||
= nav_link(controller: :labels) do
|
||||
= link_to project_labels_path(@project), title: 'Labels' do
|
||||
%span
|
||||
Labels
|
||||
|
||||
- if project_nav_tab? :labels
|
||||
= nav_link(controller: :labels) do
|
||||
= link_to project_labels_path(@project), title: 'Labels' do
|
||||
%span
|
||||
Labels
|
||||
|
||||
- if project_nav_tab? :milestones
|
||||
= nav_link(controller: :milestones) do
|
||||
= link_to project_milestones_path(@project), title: 'Milestones' do
|
||||
%span
|
||||
Milestones
|
||||
= nav_link(controller: :milestones) do
|
||||
= link_to project_milestones_path(@project), title: 'Milestones' do
|
||||
%span
|
||||
Milestones
|
||||
|
||||
- if project_nav_tab? :merge_requests
|
||||
= nav_link(controller: @project.issues_enabled? ? :merge_requests : [:merge_requests, :labels, :milestones]) do
|
||||
= link_to project_merge_requests_path(@project), title: 'Merge Requests', class: 'shortcuts-merge_requests' do
|
||||
%span
|
||||
%span.badge.count.merge_counter.js-merge-counter= number_with_delimiter(MergeRequestsFinder.new(current_user, project_id: @project.id).execute.opened.count)
|
||||
.nav-icon-container
|
||||
= custom_icon('mr_bold')
|
||||
%span.nav-item-name
|
||||
Merge Requests
|
||||
%span.badge.count.merge_counter.js-merge-counter= number_with_delimiter(MergeRequestsFinder.new(current_user, project_id: @project.id).execute.opened.count)
|
||||
|
||||
- if project_nav_tab? :pipelines
|
||||
= nav_link(controller: [:pipelines, :builds, :jobs, :pipeline_schedules, :environments, :artifacts]) do
|
||||
= link_to project_pipelines_path(@project), title: 'Pipelines', class: 'shortcuts-pipelines' do
|
||||
%span
|
||||
.nav-icon-container
|
||||
= custom_icon('pipeline')
|
||||
%span.nav-item-name
|
||||
Pipelines
|
||||
|
||||
%ul.sidebar-sub-level-items
|
||||
|
|
@ -159,25 +162,31 @@
|
|||
- if project_nav_tab? :wiki
|
||||
= nav_link(controller: :wikis) do
|
||||
= link_to get_project_wiki_path(@project), title: 'Wiki', class: 'shortcuts-wiki' do
|
||||
%span
|
||||
.nav-icon-container
|
||||
= custom_icon('wiki')
|
||||
%span.nav-item-name
|
||||
Wiki
|
||||
|
||||
- if project_nav_tab? :snippets
|
||||
= nav_link(controller: :snippets) do
|
||||
= link_to project_snippets_path(@project), title: 'Snippets', class: 'shortcuts-snippets' do
|
||||
%span
|
||||
.nav-icon-container
|
||||
= custom_icon('snippets')
|
||||
%span.nav-item-name
|
||||
Snippets
|
||||
|
||||
- if project_nav_tab? :settings
|
||||
= nav_link(path: %w[projects#edit project_members#index integrations#show services#edit repository#show ci_cd#show pages#show]) do
|
||||
= link_to edit_project_path(@project), title: 'Settings', class: 'shortcuts-tree' do
|
||||
%span
|
||||
.nav-icon-container
|
||||
= custom_icon('settings')
|
||||
%span.nav-item-name
|
||||
Settings
|
||||
|
||||
%ul.sidebar-sub-level-items
|
||||
- can_edit = can?(current_user, :admin_project, @project)
|
||||
- if can_edit
|
||||
= nav_link(controller: :projects) do
|
||||
= nav_link(path: %w[projects#edit]) do
|
||||
= link_to edit_project_path(@project), title: 'General' do
|
||||
%span
|
||||
General
|
||||
|
|
@ -207,9 +216,11 @@
|
|||
|
||||
- else
|
||||
= nav_link(path: %w[members#show]) do
|
||||
= link_to project_settings_members_path(@project), title: 'Settings', class: 'shortcuts-tree' do
|
||||
= link_to project_settings_members_path(@project), title: 'Members', class: 'shortcuts-tree' do
|
||||
.nav-icon-container
|
||||
= custom_icon('members')
|
||||
%span
|
||||
Settings
|
||||
Members
|
||||
|
||||
-# Shortcut to Project > Activity
|
||||
%li.hidden
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
- content_for :project_javascripts do
|
||||
- project = @target_project || @project
|
||||
- if current_user
|
||||
-# haml-lint:disable InlineJavaScript
|
||||
:javascript
|
||||
window.uploads_path = "#{project_uploads_path(project)}";
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
- content_for :page_specific_javascripts do
|
||||
- if @snippet && current_user
|
||||
-# haml-lint:disable InlineJavaScript
|
||||
:javascript
|
||||
window.uploads_path = "#{upload_path('personal_snippet', id: @snippet.id)}";
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
- page_title "Personal Access Tokens"
|
||||
- @content_class = "limit-container-width" unless fluid_layout
|
||||
|
||||
= render 'profiles/head'
|
||||
|
||||
.row.prepend-top-default
|
||||
|
|
@ -19,7 +20,7 @@
|
|||
%h5.prepend-top-0
|
||||
Your New Personal Access Token
|
||||
.form-group
|
||||
= text_field_tag 'created-personal-access-token', flash[:personal_access_token], readonly: true, class: "form-control", 'aria-describedby' => "created-personal-access-token-help-block"
|
||||
= text_field_tag 'created-personal-access-token', flash[:personal_access_token], readonly: true, class: "form-control js-select-on-focus", 'aria-describedby' => "created-personal-access-token-help-block"
|
||||
= clipboard_button(text: flash[:personal_access_token], title: "Copy personal access token to clipboard", placement: "left")
|
||||
%span#created-personal-access-token-help-block.help-block.text-danger Make sure you save it - you won't be able to access it again.
|
||||
|
||||
|
|
@ -28,8 +29,3 @@
|
|||
= render "shared/personal_access_tokens_form", path: profile_personal_access_tokens_path, impersonation: false, token: @personal_access_token, scopes: @scopes
|
||||
|
||||
= render "shared/personal_access_tokens_table", impersonation: false, active_tokens: @active_personal_access_tokens, inactive_tokens: @inactive_personal_access_tokens
|
||||
|
||||
:javascript
|
||||
$("#created-personal-access-token").click(function() {
|
||||
this.select();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -7,97 +7,92 @@
|
|||
|
||||
= render 'profiles/head'
|
||||
|
||||
- if inject_u2f_api?
|
||||
- content_for :page_specific_javascripts do
|
||||
- content_for :page_specific_javascripts do
|
||||
- if inject_u2f_api?
|
||||
= page_specific_javascript_bundle_tag('u2f')
|
||||
= page_specific_javascript_bundle_tag('two_factor_auth')
|
||||
|
||||
.row.prepend-top-default
|
||||
.col-lg-4
|
||||
%h4.prepend-top-0
|
||||
Register Two-Factor Authentication App
|
||||
%p
|
||||
Use an app on your mobile device to enable two-factor authentication (2FA).
|
||||
.col-lg-8
|
||||
- if current_user.two_factor_otp_enabled?
|
||||
= icon "check inverse", base: "circle", class: "text-success", text: "You've already enabled two-factor authentication using mobile authenticator applications. You can disable it from your account settings page."
|
||||
- else
|
||||
.js-two-factor-auth{ 'data-two-factor-skippable' => "#{two_factor_skippable?}", 'data-two_factor_skip_url' => skip_profile_two_factor_auth_path }
|
||||
.row.prepend-top-default
|
||||
.col-lg-4
|
||||
%h4.prepend-top-0
|
||||
Register Two-Factor Authentication App
|
||||
%p
|
||||
Download the Google Authenticator application from App Store or Google Play Store and scan this code.
|
||||
More information is available in the #{link_to('documentation', help_page_path('profile/two_factor_authentication'))}.
|
||||
.row.append-bottom-10
|
||||
.col-md-4
|
||||
= raw @qr_code
|
||||
.col-md-8
|
||||
.account-well
|
||||
%p.prepend-top-0.append-bottom-0
|
||||
Can't scan the code?
|
||||
%p.prepend-top-0.append-bottom-0
|
||||
To add the entry manually, provide the following details to the application on your phone.
|
||||
%p.prepend-top-0.append-bottom-0
|
||||
Account:
|
||||
= @account_string
|
||||
%p.prepend-top-0.append-bottom-0
|
||||
Key:
|
||||
= current_user.otp_secret.scan(/.{4}/).join(' ')
|
||||
%p.two-factor-new-manual-content
|
||||
Time based: Yes
|
||||
= form_tag profile_two_factor_auth_path, method: :post do |f|
|
||||
- if @error
|
||||
.alert.alert-danger
|
||||
= @error
|
||||
.form-group
|
||||
= label_tag :pin_code, nil, class: "label-light"
|
||||
= text_field_tag :pin_code, nil, class: "form-control", required: true
|
||||
.prepend-top-default
|
||||
= submit_tag 'Register with two-factor app', class: 'btn btn-success'
|
||||
Use an app on your mobile device to enable two-factor authentication (2FA).
|
||||
.col-lg-8
|
||||
- if current_user.two_factor_otp_enabled?
|
||||
= icon "check inverse", base: "circle", class: "text-success", text: "You've already enabled two-factor authentication using mobile authenticator applications. You can disable it from your account settings page."
|
||||
- else
|
||||
%p
|
||||
Download the Google Authenticator application from App Store or Google Play Store and scan this code.
|
||||
More information is available in the #{link_to('documentation', help_page_path('profile/two_factor_authentication'))}.
|
||||
.row.append-bottom-10
|
||||
.col-md-4
|
||||
= raw @qr_code
|
||||
.col-md-8
|
||||
.account-well
|
||||
%p.prepend-top-0.append-bottom-0
|
||||
Can't scan the code?
|
||||
%p.prepend-top-0.append-bottom-0
|
||||
To add the entry manually, provide the following details to the application on your phone.
|
||||
%p.prepend-top-0.append-bottom-0
|
||||
Account:
|
||||
= @account_string
|
||||
%p.prepend-top-0.append-bottom-0
|
||||
Key:
|
||||
= current_user.otp_secret.scan(/.{4}/).join(' ')
|
||||
%p.two-factor-new-manual-content
|
||||
Time based: Yes
|
||||
= form_tag profile_two_factor_auth_path, method: :post do |f|
|
||||
- if @error
|
||||
.alert.alert-danger
|
||||
= @error
|
||||
.form-group
|
||||
= label_tag :pin_code, nil, class: "label-light"
|
||||
= text_field_tag :pin_code, nil, class: "form-control", required: true
|
||||
.prepend-top-default
|
||||
= submit_tag 'Register with two-factor app', class: 'btn btn-success'
|
||||
|
||||
%hr
|
||||
%hr
|
||||
|
||||
.row.prepend-top-default
|
||||
.row.prepend-top-default
|
||||
.col-lg-4
|
||||
%h4.prepend-top-0
|
||||
Register Universal Two-Factor (U2F) Device
|
||||
%p
|
||||
Use a hardware device to add the second factor of authentication.
|
||||
%p
|
||||
As U2F devices are only supported by a few browsers, we require that you set up a
|
||||
two-factor authentication app before a U2F device. That way you'll always be able to
|
||||
log in - even when you're using an unsupported browser.
|
||||
.col-lg-8
|
||||
- if @u2f_registration.errors.present?
|
||||
= form_errors(@u2f_registration)
|
||||
= render "u2f/register"
|
||||
|
||||
.col-lg-4
|
||||
%h4.prepend-top-0
|
||||
Register Universal Two-Factor (U2F) Device
|
||||
%p
|
||||
Use a hardware device to add the second factor of authentication.
|
||||
%p
|
||||
As U2F devices are only supported by a few browsers, we require that you set up a
|
||||
two-factor authentication app before a U2F device. That way you'll always be able to
|
||||
log in - even when you're using an unsupported browser.
|
||||
.col-lg-8
|
||||
- if @u2f_registration.errors.present?
|
||||
= form_errors(@u2f_registration)
|
||||
= render "u2f/register"
|
||||
%hr
|
||||
|
||||
%hr
|
||||
%h5 U2F Devices (#{@u2f_registrations.length})
|
||||
|
||||
%h5 U2F Devices (#{@u2f_registrations.length})
|
||||
|
||||
- if @u2f_registrations.present?
|
||||
.table-responsive
|
||||
%table.table.table-bordered.u2f-registrations
|
||||
%colgroup
|
||||
%col{ width: "50%" }
|
||||
%col{ width: "30%" }
|
||||
%col{ width: "20%" }
|
||||
%thead
|
||||
%tr
|
||||
%th Name
|
||||
%th Registered On
|
||||
%th
|
||||
%tbody
|
||||
- @u2f_registrations.each do |registration|
|
||||
- if @u2f_registrations.present?
|
||||
.table-responsive
|
||||
%table.table.table-bordered.u2f-registrations
|
||||
%colgroup
|
||||
%col{ width: "50%" }
|
||||
%col{ width: "30%" }
|
||||
%col{ width: "20%" }
|
||||
%thead
|
||||
%tr
|
||||
%td= registration.name.presence || "<no name set>"
|
||||
%td= registration.created_at.to_date.to_s(:medium)
|
||||
%td= link_to "Delete", profile_u2f_registration_path(registration), method: :delete, class: "btn btn-danger pull-right", data: { confirm: "Are you sure you want to delete this device? This action cannot be undone." }
|
||||
%th Name
|
||||
%th Registered On
|
||||
%th
|
||||
%tbody
|
||||
- @u2f_registrations.each do |registration|
|
||||
%tr
|
||||
%td= registration.name.presence || "<no name set>"
|
||||
%td= registration.created_at.to_date.to_s(:medium)
|
||||
%td= link_to "Delete", profile_u2f_registration_path(registration), method: :delete, class: "btn btn-danger pull-right", data: { confirm: "Are you sure you want to delete this device? This action cannot be undone." }
|
||||
|
||||
- else
|
||||
.settings-message.text-center
|
||||
You don't have any U2F devices registered yet.
|
||||
|
||||
|
||||
- if two_factor_skippable?
|
||||
:javascript
|
||||
var button = "<a class='btn btn-xs btn-warning pull-right' data-method='patch' href='#{skip_profile_two_factor_auth_path}'>Configure it later</a>";
|
||||
$(".flash-alert").append(button);
|
||||
- else
|
||||
.settings-message.text-center
|
||||
You don't have any U2F devices registered yet.
|
||||
|
|
|
|||
|
|
@ -8,9 +8,3 @@
|
|||
|
||||
.content_list.project-activity{ :"data-href" => activity_project_path(@project) }
|
||||
= spinner
|
||||
|
||||
:javascript
|
||||
var activity = new gl.Activities();
|
||||
$(document).on('page:restore', function (event) {
|
||||
activity.reloadActivities()
|
||||
})
|
||||
|
|
|
|||
|
|
@ -20,6 +20,3 @@
|
|||
- unless can?(current_user, :push_code, @project)
|
||||
.inline.prepend-left-10
|
||||
= commit_in_fork_help
|
||||
|
||||
:javascript
|
||||
new NewCommitForm($('.js-create-dir-form'))
|
||||
|
|
|
|||
|
|
@ -13,6 +13,3 @@
|
|||
.col-sm-offset-2.col-sm-10
|
||||
= button_tag 'Delete file', class: 'btn btn-remove btn-remove-file'
|
||||
= link_to "Cancel", '#', class: "btn btn-cancel", "data-dismiss" => "modal"
|
||||
|
||||
:javascript
|
||||
new NewCommitForm($('.js-delete-blob-form'))
|
||||
|
|
|
|||
|
|
@ -28,8 +28,4 @@
|
|||
.form-actions
|
||||
= button_tag 'Create branch', class: 'btn btn-create', tabindex: 3
|
||||
= link_to 'Cancel', project_branches_path(@project), class: 'btn btn-cancel'
|
||||
|
||||
:javascript
|
||||
var availableRefs = #{@project.repository.ref_names.to_json};
|
||||
|
||||
new NewBranchForm($('.js-create-branch-form'), availableRefs)
|
||||
%script#availableRefs{ type: "application/json" }= @project.repository.ref_names.to_json.html_safe
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
.page-content-header
|
||||
.page-content-header.js-commit-box{ 'data-commit-path' => branches_project_commit_path(@project, @commit.id) }
|
||||
.header-main-content
|
||||
= render partial: 'signature', object: @commit.signature
|
||||
%strong
|
||||
|
|
@ -79,6 +79,3 @@
|
|||
= render 'shared/mini_pipeline_graph', pipeline: last_pipeline, klass: 'js-commit-pipeline-graph'
|
||||
in
|
||||
= time_interval_in_words last_pipeline.duration
|
||||
|
||||
:javascript
|
||||
$(".commit-info.branches").load("#{branches_project_commit_path(@project, @commit.id)}");
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
- notes = commit.notes
|
||||
- note_count = notes.user.count
|
||||
|
||||
- cache_key = [project.path_with_namespace, commit.id, current_application_settings, note_count, @path.presence, current_controller?(:commits)]
|
||||
- cache_key = [project.full_path, commit.id, current_application_settings, note_count, @path.presence, current_controller?(:commits)]
|
||||
- cache_key.push(commit.status(ref)) if commit.status(ref)
|
||||
|
||||
= cache(cache_key, expires_in: 1.day) do
|
||||
|
|
|
|||
|
|
@ -11,34 +11,32 @@
|
|||
= content_for :sub_nav do
|
||||
= render "head"
|
||||
|
||||
%div{ class: container_class }
|
||||
.tree-holder
|
||||
.nav-block
|
||||
.tree-ref-container
|
||||
.tree-ref-holder
|
||||
= render 'shared/ref_switcher', destination: 'commits'
|
||||
.js-project-commits-show{ 'data-commits-limit' => @limit }
|
||||
%div{ class: container_class }
|
||||
.tree-holder
|
||||
.nav-block
|
||||
.tree-ref-container
|
||||
.tree-ref-holder
|
||||
= render 'shared/ref_switcher', destination: 'commits'
|
||||
|
||||
%ul.breadcrumb.repo-breadcrumb
|
||||
= commits_breadcrumbs
|
||||
.tree-controls.hidden-xs.hidden-sm
|
||||
- if @merge_request.present?
|
||||
.control
|
||||
= link_to _("View open merge request"), project_merge_request_path(@project, @merge_request), class: 'btn'
|
||||
- elsif create_mr_button?(@repository.root_ref, @ref)
|
||||
.control
|
||||
= link_to _("Create merge request"), create_mr_path(@repository.root_ref, @ref), class: 'btn btn-success'
|
||||
|
||||
%ul.breadcrumb.repo-breadcrumb
|
||||
= commits_breadcrumbs
|
||||
.tree-controls.hidden-xs.hidden-sm
|
||||
- if @merge_request.present?
|
||||
.control
|
||||
= link_to _("View open merge request"), project_merge_request_path(@project, @merge_request), class: 'btn'
|
||||
- elsif create_mr_button?(@repository.root_ref, @ref)
|
||||
= form_tag(project_commits_path(@project, @id), method: :get, class: 'commits-search-form', data: { 'signatures-path' => namespace_project_signatures_path }) do
|
||||
= search_field_tag :search, params[:search], { placeholder: _('Filter by commit message'), id: 'commits-search', class: 'form-control search-text-input input-short', spellcheck: false }
|
||||
.control
|
||||
= link_to _("Create merge request"), create_mr_path(@repository.root_ref, @ref), class: 'btn btn-success'
|
||||
= link_to project_commits_path(@project, @ref, rss_url_options), title: _("Commits feed"), class: 'btn' do
|
||||
= icon("rss")
|
||||
|
||||
.control
|
||||
= form_tag(project_commits_path(@project, @id), method: :get, class: 'commits-search-form', data: { 'signatures-path' => namespace_project_signatures_path }) do
|
||||
= search_field_tag :search, params[:search], { placeholder: _('Filter by commit message'), id: 'commits-search', class: 'form-control search-text-input input-short', spellcheck: false }
|
||||
.control
|
||||
= link_to project_commits_path(@project, @ref, rss_url_options), title: _("Commits feed"), class: 'btn' do
|
||||
= icon("rss")
|
||||
|
||||
%div{ id: dom_id(@project) }
|
||||
%ol#commits-list.list-unstyled.content_list
|
||||
= render 'commits', project: @project, ref: @ref
|
||||
= spinner
|
||||
|
||||
:javascript
|
||||
CommitsList.init(#{@limit});
|
||||
%div{ id: dom_id(@project) }
|
||||
%ol#commits-list.list-unstyled.content_list
|
||||
= render 'commits', project: @project, ref: @ref
|
||||
= spinner
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
- page_title "Find File", @ref
|
||||
= render "projects/commits/head"
|
||||
|
||||
.file-finder-holder.tree-holder.clearfix
|
||||
.file-finder-holder.tree-holder.clearfix.js-file-finder{ 'data-file-find-url': "#{escape_javascript(project_files_path(@project, @ref, @options.merge(format: :json)))}", 'data-find-tree-url': escape_javascript(project_tree_path(@project, @ref)), 'data-blob-url-template': escape_javascript(project_blob_path(@project, @id || @commit.id)) }
|
||||
.nav-block
|
||||
.tree-ref-holder
|
||||
= render 'shared/ref_switcher', destination: 'find_file', path: @path
|
||||
|
|
@ -17,11 +17,3 @@
|
|||
%table.table.files-slider{ class: "table_#{@hex_path} tree-table table-striped" }
|
||||
%tbody
|
||||
= spinner nil, true
|
||||
|
||||
:javascript
|
||||
var projectFindFile = new ProjectFindFile($(".file-finder-holder"), {
|
||||
url: "#{escape_javascript(project_files_path(@project, @ref, @options.merge(format: :json)))}",
|
||||
treeUrl: "#{escape_javascript(project_tree_path(@project, @ref))}",
|
||||
blobUrlTemplate: "#{escape_javascript(project_blob_path(@project, @id || @commit.id))}"
|
||||
});
|
||||
new ShortcutsFindFile(projectFindFile);
|
||||
|
|
|
|||
|
|
@ -3,8 +3,9 @@
|
|||
- if show_new_nav?
|
||||
- add_to_breadcrumbs("Repository", project_tree_path(@project))
|
||||
- content_for :page_specific_javascripts do
|
||||
= page_specific_javascript_bundle_tag('common_d3')
|
||||
= page_specific_javascript_bundle_tag('graphs')
|
||||
= webpack_bundle_tag('common_d3')
|
||||
= webpack_bundle_tag('graphs')
|
||||
= webpack_bundle_tag('graphs_charts')
|
||||
= render "projects/commits/head"
|
||||
|
||||
.repo-charts{ class: container_class }
|
||||
|
|
@ -75,55 +76,10 @@
|
|||
Commits per day hour (UTC)
|
||||
%canvas#hour-chart
|
||||
|
||||
:javascript
|
||||
var responsiveChart = function (selector, data) {
|
||||
var options = { "scaleOverlay": true, responsive: true, pointHitDetectionRadius: 2, maintainAspectRatio: false };
|
||||
// get selector by context
|
||||
var ctx = selector.get(0).getContext("2d");
|
||||
// pointing parent container to make chart.js inherit its width
|
||||
var container = $(selector).parent();
|
||||
var generateChart = function() {
|
||||
selector.attr('width', $(container).width());
|
||||
if (window.innerWidth < 768) {
|
||||
// Scale fonts if window width lower than 768px (iPad portrait)
|
||||
options.scaleFontSize = 8
|
||||
}
|
||||
return new Chart(ctx).Bar(data, options);
|
||||
};
|
||||
// enabling auto-resizing
|
||||
$(window).resize(generateChart);
|
||||
return generateChart();
|
||||
};
|
||||
|
||||
var chartData = function (keys, values) {
|
||||
var data = {
|
||||
labels : keys,
|
||||
datasets : [{
|
||||
fillColor : "rgba(220,220,220,0.5)",
|
||||
strokeColor : "rgba(220,220,220,1)",
|
||||
barStrokeWidth: 1,
|
||||
barValueSpacing: 1,
|
||||
barDatasetSpacing: 1,
|
||||
data : values
|
||||
}]
|
||||
};
|
||||
return data;
|
||||
};
|
||||
|
||||
var hourData = chartData(#{@commits_per_time.keys.to_json}, #{@commits_per_time.values.to_json});
|
||||
responsiveChart($('#hour-chart'), hourData);
|
||||
|
||||
var dayData = chartData(#{@commits_per_week_days.keys.to_json}, #{@commits_per_week_days.values.to_json});
|
||||
responsiveChart($('#weekday-chart'), dayData);
|
||||
|
||||
var monthData = chartData(#{@commits_per_month.keys.to_json}, #{@commits_per_month.values.to_json});
|
||||
responsiveChart($('#month-chart'), monthData);
|
||||
|
||||
var data = #{@languages.to_json};
|
||||
var ctx = $("#languages-chart").get(0).getContext("2d");
|
||||
var options = {
|
||||
scaleOverlay: true,
|
||||
responsive: true,
|
||||
maintainAspectRatio: false
|
||||
}
|
||||
var myPieChart = new Chart(ctx).Pie(data, options);
|
||||
%script#projectChartData{ type: "application/json" }
|
||||
- projectChartData = {};
|
||||
- projectChartData['hour'] = { 'keys' => @commits_per_time.keys, 'values' => @commits_per_time.values }
|
||||
- projectChartData['weekDays'] = { 'keys' => @commits_per_week_days.keys, 'values' => @commits_per_week_days.values }
|
||||
- projectChartData['month'] = { 'keys' => @commits_per_month.keys, 'values' => @commits_per_month.values }
|
||||
- projectChartData['languages'] = @languages
|
||||
= projectChartData.to_json.html_safe
|
||||
|
|
|
|||
|
|
@ -1,15 +1,16 @@
|
|||
- @no_container = true
|
||||
- page_title "Contributors"
|
||||
- content_for :page_specific_javascripts do
|
||||
= page_specific_javascript_bundle_tag('common_d3')
|
||||
= page_specific_javascript_bundle_tag('graphs')
|
||||
= webpack_bundle_tag('common_d3')
|
||||
= webpack_bundle_tag('graphs')
|
||||
= webpack_bundle_tag('graphs_show')
|
||||
|
||||
- if show_new_nav?
|
||||
- add_to_breadcrumbs("Repository", project_tree_path(@project))
|
||||
|
||||
= render 'projects/commits/head'
|
||||
|
||||
%div{ class: container_class }
|
||||
.js-graphs-show{ class: container_class, 'data-project-graph-path': project_graph_path(@project, current_ref, format: :json) }
|
||||
.sub-header-block
|
||||
.tree-ref-holder
|
||||
= render 'shared/ref_switcher', destination: 'graphs'
|
||||
|
|
@ -33,24 +34,3 @@
|
|||
#contributors-master
|
||||
#contributors.clearfix
|
||||
%ol.contributors-list.clearfix
|
||||
|
||||
|
||||
|
||||
:javascript
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
url: "#{project_graph_path(@project, current_ref, format: :json)}",
|
||||
dataType: "json",
|
||||
success: function (data) {
|
||||
var graph = new ContributorsStatGraph();
|
||||
graph.init(data);
|
||||
|
||||
$("#brush_change").change(function(){
|
||||
graph.change_date_header();
|
||||
graph.redraw_authors();
|
||||
});
|
||||
|
||||
$(".stat-graph").fadeIn();
|
||||
$(".loading-graph").hide();
|
||||
}
|
||||
});
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue