diff --git a/.codeclimate.yml b/.codeclimate.yml index 6948720528..c96db5f580 100644 --- a/.codeclimate.yml +++ b/.codeclimate.yml @@ -3,5 +3,6 @@ languages: PHP: true exclude_paths: - "lib/strip_attributes/test/*" +- "db/migrate/" - "spec/" - "vendor/" diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000000..50a25ce19e --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,75 @@ +version: 2 +updates: + - package-ecosystem: bundler + directory: "/" + schedule: + interval: weekly + open-pull-requests-limit: 99 + rebase-strategy: "disabled" + ignore: + # requires Ruby >= 2.4.0 + - dependency-name: annotate + versions: + - ">= 3.1.1" + - dependency-name: capybara + versions: + - ">= 3.16.0" + - dependency-name: fast_gettext + versions: + - ">= 2.0.3" + - dependency-name: icalendar + versions: + - ">= 2.6.0" + - dependency-name: launchy + versions: + - ">= 2.5.0" + - dependency-name: maxmind-db + versions: + - ">= 1.1.0" + - dependency-name: pry-byebug + versions: + - ">= 2.8.0" + - dependency-name: rubocop + versions: + - ">= 0.82.0" + - dependency-name: sass-rails + versions: + - ">= 5.0.8" + + # pry-byebug requires Ruby >= 2.4.0 + - dependency-name: pry + versions: + - ">= 0.13.0" + + # requires Ruby >= 2.4 + - dependency-name: rubyzip + versions: + - ">= 2.0.0" + + # requires Ruby ~> 2.4 + - dependency-name: holidays + versions: + - ">= 8.0.0" + + # requires Ruby >= 2.4.6 + - dependency-name: globalize + versions: + - ">= 5.3.0" + + # requires Ruby >= 2.5.0 + - dependency-name: capybara + versions: + - ">= 3.33.0" + - dependency-name: gettext + versions: + - ">= 3.3.0" + + # factory_bot requires Ruby >= 2.5.0 + - dependency-name: factory_bot_rails + versions: + - ">= 6.0.0" + + # sprockets requires Ruby >= 2.5.0 + - dependency-name: sass-rails + versions: + - ">= 6.0.0" diff --git a/.github/workflows/rubocop.yml b/.github/workflows/rubocop.yml new file mode 100644 index 0000000000..477d2de349 --- /dev/null +++ b/.github/workflows/rubocop.yml @@ -0,0 +1,16 @@ +name: RuboCop + +on: [pull_request] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + + - name: Run RuboCop linter + uses: reviewdog/action-rubocop@v1 + with: + github_token: ${{ secrets.github_token }} + rubocop_version: 0.81.0 + rubocop_extensions: rubocop-performance:1.5.2 rubocop-rails:2.5.2 diff --git a/.gitignore b/.gitignore index 85617ce4f4..a5ba158236 100644 --- a/.gitignore +++ b/.gitignore @@ -1,43 +1,43 @@ !/lib/themes/.keep !/log/.keep -.bundle/ -.ruby-version -.sass-cache/ -.vagrant.yml -.vagrant/ -cache/ -config/*.deployed -config/aliases -config/config.tmp -config/crontab -config/database.yml -config/deploy.yml* -config/general*.yml -config/httpd.conf -config/logrotate -config/memcached.yml -config/newrelic.yml -config/rails_env.rb -config/user_spam_scorer.yml -coverage/ -db/schema.rb -db/structure.sql -files/ -lib/themes/* -locale/model_attributes.rb -log/* -old-cache/ -public/*theme/ -public/alavetelitheme -public/asktheeu-theme -public/assets/ -public/down.current.html -public/down.html -public/download/ -public/foi-live-creation.png -public/foi-user-use.png -public/google*.html -public/wordpress -tmp/ -vendor/bundle/ -vendor/data/ +/.bundle/ +/.ruby-version +/.sass-cache/ +/.vagrant.yml +/.vagrant/ +/cache/ +/config/*.deployed +/config/aliases +/config/config.tmp +/config/crontab +/config/database.yml +/config/deploy.yml* +/config/general*.yml +/config/httpd.conf +/config/logrotate +/config/memcached.yml +/config/newrelic.yml +/config/rails_env.rb +/config/user_spam_scorer.yml +/coverage/ +/db/schema.rb +/db/structure.sql +/files/ +/lib/themes/* +/locale/model_attributes.rb +/log/* +/old-cache/ +/public/*theme/ +/public/alavetelitheme +/public/asktheeu-theme +/public/assets/ +/public/down.current.html +/public/down.html +/public/download/ +/public/foi-live-creation.png +/public/foi-user-use.png +/public/google*.html +/public/wordpress +/tmp/ +/vendor/bundle/ +/vendor/data/ diff --git a/.hound.yml b/.hound.yml deleted file mode 100644 index 5eb837bdb9..0000000000 --- a/.hound.yml +++ /dev/null @@ -1,11 +0,0 @@ -eslint: - enabled: false - -rubocop: - version: 0.60.0 - -ruby: - config_file: .ruby-style.yml - -scss: - enabled: false diff --git a/.ruby-style.yml b/.ruby-style.yml index 1bd65ee6cd..479f7aa0a8 100644 --- a/.ruby-style.yml +++ b/.ruby-style.yml @@ -1,1509 +1,1464 @@ +--- +require: + - rubocop-performance + - rubocop-rails + AllCops: TargetRubyVersion: 2.3 RubyInterpreters: - ruby - rake Include: - - '**/*.rb' - - '**/*.gemfile' - - '**/*.gemspec' - - '**/*.rake' - - '**/*.ru' - - '**/*.spec' - - '**/Gemfile' - - '**/Rakefile' - - '**/Vagrantfile' + - "**/*.rb" + - "**/*.gemfile" + - "**/*.gemspec" + - "**/*.rake" + - "**/*.ru" + - "**/*.spec" + - "**/Gemfile" + - "**/Rakefile" + - "**/Vagrantfile" Exclude: - - 'commonlib/**/*' - - 'db/schema.rb' - - 'node_modules/**/*' - - 'vendor/**/*' - - '.git/**/*' + - commonlib/**/* + - db/schema.rb + - node_modules/**/* + - vendor/**/* + - ".git/**/*" DisplayCopNames: false StyleGuideCopsOnly: false DisabledByDefault: true +#################### Bundler #################### + +Bundler/DuplicatedGem: + Enabled: false + +Bundler/GemComment: + Enabled: false + +Bundler/InsecureProtocolSource: + Enabled: false + +Bundler/OrderedGems: + Enabled: false + +#################### Gemspec #################### + +Gemspec/DuplicatedAssignment: + Enabled: false + +Gemspec/OrderedDependencies: + Enabled: false + +Gemspec/RequiredRubyVersion: + Enabled: false + +Gemspec/RubyVersionGlobalsUsage: + Enabled: false + +#################### Layout #################### + Layout/AccessModifierIndentation: - Description: Check indentation of private/protected visibility modifiers. - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#indent-public-private-protected' Enabled: true -Layout/AlignArray: - Description: >- - Align the elements of an array literal if they span more than - one line. - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#align-multiline-arrays' - Enabled: true +Layout/ArgumentAlignment: + Enabled: false -Layout/AlignHash: - Description: >- - Align the elements of a hash literal if they span more than - one line. +Layout/ArrayAlignment: Enabled: true -Layout/AlignParameters: - Description: >- - Align the parameters of a method call if they span more - than one line. - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-double-indent' +Layout/AssignmentIndentation: Enabled: true +Layout/BlockAlignment: + Enabled: false + Layout/BlockEndNewline: - Description: 'Put end statement of multiline block on its own line.' Enabled: true Layout/CaseIndentation: - Description: 'Indentation of when in a case/when/[else/]end.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#indent-when-to-case' Enabled: true +Layout/ClassStructure: + Enabled: false + +Layout/ClosingHeredocIndentation: + Enabled: false + Layout/ClosingParenthesisIndentation: - Description: 'Checks the indentation of hanging closing parentheses.' Enabled: true Layout/CommentIndentation: - Description: 'Indentation of comments.' + Enabled: true + +Layout/ConditionPosition: + Enabled: true + +Layout/DefEndAlignment: Enabled: true Layout/DotPosition: - Description: 'Checks the position of the dot in multi-line method calls.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#consistent-multi-line-chains' Enabled: true - EnforcedStyle: 'trailing' + EnforcedStyle: trailing Layout/ElseAlignment: - Description: 'Align elses and elsifs correctly.' Enabled: true +Layout/EmptyComment: + Enabled: false + +Layout/EmptyLineAfterGuardClause: + Enabled: false + +Layout/EmptyLineAfterMagicComment: + Enabled: false + Layout/EmptyLineBetweenDefs: - Description: 'Use empty lines between defs.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#empty-lines-between-methods' Enabled: true Layout/EmptyLines: - Description: "Don't use several empty lines in a row." Enabled: true Layout/EmptyLinesAroundAccessModifier: - Description: "Keep blank lines around access modifiers." Enabled: true +Layout/EmptyLinesAroundArguments: + Enabled: false + +Layout/EmptyLinesAroundBeginBody: + Enabled: false + Layout/EmptyLinesAroundBlockBody: - Description: "Keeps track of empty lines around block bodies." Enabled: true - EnforcedStyle: no_empty_lines Exclude: - - 'spec/**/*' + - spec/**/* Layout/EmptyLinesAroundClassBody: - Description: "Keeps track of empty lines around class bodies." Enabled: true - EnforcedStyle: no_empty_lines + +Layout/EmptyLinesAroundExceptionHandlingKeywords: + Enabled: false + +Layout/EmptyLinesAroundMethodBody: + Enabled: true Layout/EmptyLinesAroundModuleBody: - Description: "Keeps track of empty lines around module bodies." Enabled: true - EnforcedStyle: no_empty_lines -Layout/EmptyLinesAroundMethodBody: - Description: "Keeps track of empty lines around method bodies." +Layout/EndAlignment: Enabled: true Layout/EndOfLine: - Description: 'Use Unix-style line endings.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#crlf' Enabled: true Layout/ExtraSpacing: - Description: 'Do not use unnecessary spacing.' Enabled: true -Layout/InitialIndentation: - Description: >- - Checks the indentation of the first non-blank non-comment line in a file. +Layout/FirstArgumentIndentation: + Enabled: true + +Layout/FirstArrayElementIndentation: + Enabled: true + +Layout/FirstArrayElementLineBreak: + Enabled: false + +Layout/FirstHashElementIndentation: Enabled: true +Layout/FirstHashElementLineBreak: + Enabled: false + +Layout/FirstMethodArgumentLineBreak: + Enabled: false + +Layout/FirstMethodParameterLineBreak: + Enabled: false + Layout/FirstParameterIndentation: - Description: 'Checks the indentation of the first parameter in a method call.' + Enabled: false + +Layout/HashAlignment: Enabled: true +Layout/HeredocArgumentClosingParenthesis: + Enabled: false + +Layout/HeredocIndentation: + Enabled: false + Layout/IndentationConsistency: - Description: 'Keep indentation straight.' Enabled: true Exclude: - Gemfile Layout/IndentationWidth: - Description: 'Use 2 spaces for indentation.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-indentation' Enabled: true -Layout/IndentArray: - Description: >- - Checks the indentation of the first element in an array - literal. +Layout/InitialIndentation: Enabled: true -Layout/IndentAssignment: - Description: >- - Checks the indentation of the first line of the - right-hand-side of a multi-line assignment. +Layout/LeadingCommentSpace: Enabled: true -Layout/IndentHash: - Description: 'Checks the indentation of the first key in a hash literal.' - Enabled: true +Layout/LeadingEmptyLines: + Enabled: false -Layout/LeadingCommentSpace: - Description: 'Comments should start with a space.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#hash-space' +Layout/LineLength: Enabled: true + IgnoreCopDirectives: false + IgnoredPatterns: + - "^\\s*it\\s+.*do$" + - "^\\s*context\\s+.*do$" + - "^\\s*describe\\s+.*do$" + - "^\\s*class\\s+[A-Z].*<.*" Layout/MultilineArrayBraceLayout: - Description: >- - Checks that the closing brace in an array literal is - either on the same line as the last array element, or - a new line. Enabled: true +Layout/MultilineArrayLineBreaks: + Enabled: false + +Layout/MultilineAssignmentLayout: + Enabled: false + Layout/MultilineBlockLayout: - Description: 'Ensures newlines after multiline block do statements.' Enabled: true Layout/MultilineHashBraceLayout: - Description: >- - Checks that the closing brace in a hash literal is - either on the same line as the last hash element, or - a new line. Enabled: true - EnforcedStyle: symmetrical + +Layout/MultilineHashKeyLineBreaks: + Enabled: false + +Layout/MultilineMethodArgumentLineBreaks: + Enabled: false Layout/MultilineMethodCallBraceLayout: - Description: >- - Checks that the closing brace in a method call is - either on the same line as the last method argument, or - a new line. Enabled: true Layout/MultilineMethodCallIndentation: - Description: >- - Checks indentation of method calls with the dot operator - that span more than one line. Enabled: false Layout/MultilineMethodDefinitionBraceLayout: - Description: >- - Checks that the closing brace in a method definition is - either on the same line as the last method parameter, or - a new line. Enabled: true Layout/MultilineOperationIndentation: - Description: >- - Checks indentation of binary operations that span more than - one line. Enabled: true -Layout/RescueEnsureAlignment: - Description: 'Align rescues and ensures correctly.' +Layout/ParameterAlignment: Enabled: true -Layout/SpaceBeforeFirstArg: - Description: >- - Checks that exactly one space is used between a method name - and the first argument for method calls without parentheses. +Layout/RescueEnsureAlignment: Enabled: true Layout/SpaceAfterColon: - Description: 'Use spaces after colons.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-operators' Enabled: true Layout/SpaceAfterComma: - Description: 'Use spaces after commas.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-operators' Enabled: true Layout/SpaceAfterMethodName: - Description: >- - Do not put a space between a method name and the opening - parenthesis in a method definition. - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#parens-no-spaces' Enabled: true Layout/SpaceAfterNot: - Description: Tracks redundant space after the ! operator. - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-space-bang' Enabled: true Layout/SpaceAfterSemicolon: - Description: 'Use spaces after semicolons.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-operators' Enabled: true -Layout/SpaceBeforeBlockBraces: - Description: >- - Checks that the left block brace has or doesn't have space - before it. +Layout/SpaceAroundBlockParameters: Enabled: true -Layout/SpaceBeforeComma: - Description: 'No spaces before commas.' +Layout/SpaceAroundEqualsInParameterDefault: Enabled: true -Layout/SpaceBeforeComment: - Description: >- - Checks for missing space between code and a comment on the - same line. +Layout/SpaceAroundKeyword: Enabled: true -Layout/SpaceBeforeSemicolon: - Description: 'No spaces before semicolons.' +Layout/SpaceAroundOperators: Enabled: true -Layout/SpaceInsideBlockBraces: - Description: >- - Checks that block braces have or don't have surrounding space. - For blocks taking parameters, checks that the left brace has - or doesn't have trailing space. +Layout/SpaceBeforeBlockBraces: Enabled: true -Layout/SpaceAroundBlockParameters: - Description: 'Checks the spacing inside and after block parameters pipes.' +Layout/SpaceBeforeComma: Enabled: true -Layout/SpaceAroundEqualsInParameterDefault: - Description: >- - Checks that the equals signs in parameter default assignments - have or don't have surrounding space depending on - configuration. - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-around-equals' +Layout/SpaceBeforeComment: Enabled: true -Layout/SpaceAroundKeyword: - Description: 'Use a space around keywords if appropriate.' +Layout/SpaceBeforeFirstArg: Enabled: true -Layout/SpaceAroundOperators: - Description: 'Use a single space around operators.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-operators' +Layout/SpaceBeforeSemicolon: Enabled: true -Layout/SpaceInsideArrayPercentLiteral: - Description: 'No unnecessary additional spaces between elements in %i/%w literals.' - Enabled: true +Layout/SpaceInLambdaLiteral: + Enabled: false -Layout/SpaceInsidePercentLiteralDelimiters: - Description: 'No unnecessary spaces inside delimiters of %i/%w/%x literals.' +Layout/SpaceInsideArrayLiteralBrackets: Enabled: true -Layout/SpaceInsideArrayLiteralBrackets: - Description: 'Checks the spacing inside array literal brackets.' +Layout/SpaceInsideArrayPercentLiteral: Enabled: true -Layout/SpaceInsideReferenceBrackets: - Description: 'Checks the spacing inside referential brackets.' +Layout/SpaceInsideBlockBraces: Enabled: true Layout/SpaceInsideHashLiteralBraces: - Description: "Use spaces inside hash literal braces - or don't." - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-operators' Enabled: true - EnforcedStyle: space - EnforcedStyleForEmptyBraces: no_space Layout/SpaceInsideParens: - Description: 'No spaces after ( or before ).' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-spaces-braces' + Enabled: true + +Layout/SpaceInsidePercentLiteralDelimiters: Enabled: true Layout/SpaceInsideRangeLiteral: - Description: 'No spaces inside range literals.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-space-inside-range-literals' + Enabled: true + +Layout/SpaceInsideReferenceBrackets: Enabled: true Layout/SpaceInsideStringInterpolation: - Description: 'Checks for padding/surrounding spaces inside string interpolation.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#string-interpolation' Enabled: false Layout/Tab: - Description: 'No hard tabs.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-indentation' Enabled: true -Layout/TrailingBlankLines: - Description: 'Checks trailing blank lines and final newline.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#newline-eof' +Layout/TrailingEmptyLines: Enabled: true Layout/TrailingWhitespace: - Description: 'Avoid trailing whitespace.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-trailing-whitespace' Enabled: true -Layout/BlockAlignment: - Description: 'Align block ends correctly.' +#################### Lint #################### + +Lint/AmbiguousBlockAssociation: Enabled: false -Layout/ConditionPosition: - Description: >- - Checks for condition placed in a confusing position relative to - the keyword. - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#same-line-condition' +Lint/AmbiguousOperator: Enabled: true -Layout/DefEndAlignment: - Description: 'Align ends corresponding to defs correctly.' +Lint/AmbiguousRegexpLiteral: Enabled: true -Layout/EndAlignment: - Description: 'Align ends correctly.' +Lint/AssignmentInCondition: Enabled: true -Style/Alias: - Description: 'Use alias instead of alias_method.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#alias-method' - Enabled: true +Lint/BigDecimalNew: + Enabled: false -Style/AndOr: - Description: 'Use &&/|| instead of and/or.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-and-or-or' - Enabled: true +Lint/BooleanSymbol: + Enabled: false -Style/ArrayJoin: - Description: 'Use Array#join instead of Array#*.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#array-join' +Lint/CircularArgumentReference: Enabled: true -Style/AsciiComments: - Description: 'Use only ascii symbols in comments.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#english-comments' - Enabled: false - -Style/Attr: - Description: 'Checks for uses of Module#attr.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#attr' +Lint/Debugger: Enabled: true -Style/BeginBlock: - Description: 'Avoid the use of BEGIN blocks.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-BEGIN-blocks' +Lint/DeprecatedClassMethods: Enabled: true -Style/BarePercentLiterals: - Description: 'Checks if usage of %() or %Q() matches configuration.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#percent-q-shorthand' +Lint/DisjunctiveAssignmentInConstructor: Enabled: false -Style/BlockComments: - Description: 'Do not use block comments.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-block-comments' - Enabled: true - -Style/BlockDelimiters: - Description: >- - Avoid using {...} for multi-line blocks (multiline chaining is - always ugly). - Prefer {...} over do...end for single-line blocks. - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#single-line-blocks' +Lint/DuplicateCaseCondition: Enabled: false - Exclude: - - 'spec/**/*' -Style/BracesAroundHashParameters: - Description: 'Enforce braces style around hash parameters.' +Lint/DuplicateHashKey: Enabled: true - EnforcedStyle: no_braces -Style/CaseEquality: - Description: 'Avoid explicit use of the case equality operator(===).' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-case-equality' +Lint/DuplicateMethods: Enabled: true -Style/CharacterLiteral: - Description: 'Checks for uses of character literals.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-character-literals' +Lint/EachWithObjectArgument: Enabled: true -Style/ClassAndModuleChildren: - Description: 'Checks style of children classes and modules.' - Enabled: false - -Style/ClassCheck: - Description: 'Enforces consistent use of `Object#is_a?` or `Object#kind_of?`.' +Lint/ElseLayout: Enabled: true -Style/ClassMethods: - Description: 'Use self when defining module/class methods.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#def-self-class-methods' +Lint/EmptyEnsure: Enabled: true -Style/ClassVars: - Description: 'Avoid the use of class variables.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-class-vars' - Enabled: true +Lint/EmptyExpression: + Enabled: false -Style/ColonMethodCall: - Description: 'Do not use :: for method call.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#double-colons' +Lint/EmptyInterpolation: Enabled: true -Style/CommandLiteral: - Description: 'Use `` or %x around command literals.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#percent-x' - Enabled: true +Lint/EmptyWhen: + Enabled: false -Style/CommentAnnotation: - Description: >- - Checks formatting of special comments - (TODO, FIXME, OPTIMIZE, HACK, REVIEW). - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#annotate-keywords' +Lint/EnsureReturn: Enabled: true -Style/ConditionalAssignment: - Description: >- - Use the return value of `if` and `case` statements for - assignment to a variable and variable comparison instead - of assigning that variable inside of each branch. +Lint/ErbNewArguments: Enabled: false -Style/DefWithParentheses: - Description: 'Use def with parentheses when there are arguments.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#method-parens' +Lint/FlipFlop: Enabled: true -Style/PreferredHashMethods: - Description: 'Checks use of `has_key?` and `has_value?` Hash methods.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#hash-key' +Lint/FloatOutOfRange: Enabled: true -Style/Documentation: - Description: 'Document classes and non-namespace modules.' +Lint/FormatParameterMismatch: Enabled: true - Exclude: - - 'db/migrate/*' - - 'spec/**/*' - - 'test/**/*' -Style/DoubleNegation: - Description: 'Checks for uses of double negation (!!).' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-bang-bang' +Lint/HeredocMethodCallPosition: + Enabled: false + +Lint/ImplicitStringConcatenation: Enabled: true -Style/EachForSimpleLoop: - Description: >- - Use `Integer#times` for a simple loop which iterates a fixed - number of times. +Lint/IneffectiveAccessModifier: Enabled: true -Style/EachWithObject: - Description: 'Prefer `each_with_object` over `inject` or `reduce`.' +Lint/InheritException: + Enabled: true + +Lint/InterpolationCheck: Enabled: false -Style/EmptyElse: - Description: 'Avoid empty else-clauses.' +Lint/LiteralAsCondition: Enabled: true -Style/EmptyCaseCondition: - Description: 'Avoid empty condition in case statements.' +Lint/LiteralInInterpolation: Enabled: true -Style/EmptyLiteral: - Description: 'Prefer literals to Array.new/Hash.new/String.new.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#literal-array-hash' +Lint/Loop: Enabled: true -Style/Encoding: - Description: 'Use UTF-8 as the source file encoding.' - StyleGuide: https://github.com/bbatsov/ruby-style-guide#utf-8 +Lint/MissingCopEnableDirective: Enabled: false -Style/EndBlock: - Description: 'Avoid the use of END blocks.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-END-blocks' - Enabled: true +Lint/MultipleComparison: + Enabled: false -Style/EvenOdd: - Description: 'Favor the use of Fixnum#even? && Fixnum#odd?' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#predicate-methods' +Lint/NestedMethodDefinition: Enabled: true -Style/FrozenStringLiteralComment: - Description: >- - Add the frozen_string_literal comment to the top of files - to help transition from Ruby 2.3.0 to Ruby 3.0. +Lint/NestedPercentLiteral: Enabled: false -Lint/FlipFlop: - Description: 'Checks for flip flops' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-flip-flops' - Enabled: true - -Style/For: - Description: 'Checks use of for or each in multiline loops.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-for-loops' +Lint/NextWithoutAccumulator: Enabled: true -Style/FormatString: - Description: 'Enforce the use of Kernel#sprintf, Kernel#format or String#%.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#sprintf' - Enabled: true +Lint/NonDeterministicRequireOrder: + Enabled: false -Style/GlobalVars: - Description: 'Do not introduce global variables.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#instance-vars' - Reference: 'http://www.zenspider.com/Languages/Ruby/QuickRef.html' +Lint/NonLocalExitFromIterator: Enabled: true -Style/GuardClause: - Description: 'Check for conditionals that can be replaced with guard clauses' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-nested-conditionals' - Enabled: true +Lint/NumberConversion: + Enabled: false -Style/HashSyntax: - Description: >- - Prefer Ruby 1.9 hash syntax { a: 1, b: 2 } over 1.8 syntax - { :a => 1, :b => 2 }. - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#hash-literals' - Enabled: true - EnforcedStyle: ruby19_no_mixed_keys - Exclude: - - config/routes.rb +Lint/OrderedMagicComments: + Enabled: false -Style/IfInsideElse: - Description: 'Finds if nodes inside else, which can be converted to elsif.' +Lint/ParenthesesAsGroupedExpression: Enabled: true -Style/IfUnlessModifier: - Description: >- - Favor modifier if/unless usage when you have a - single-line body. - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#if-as-a-modifier' +Lint/PercentStringArray: Enabled: true -Style/IfUnlessModifierOfIfUnless: - Description: >- - Avoid modifier if/unless usage on conditionals. +Lint/PercentSymbolArray: Enabled: true -Style/IfWithSemicolon: - Description: 'Do not use if x; .... Use the ternary operator instead.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-semicolon-ifs' +Lint/RandOne: Enabled: true -Style/IdenticalConditionalBranches: - Description: >- - Checks that conditional statements do not have an identical - line at the end of each branch, which can validly be moved - out of the conditional. +Lint/RedundantCopDisableDirective: Enabled: true -Style/InfiniteLoop: - Description: 'Use Kernel#loop for infinite loops.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#infinite-loop' - Enabled: true +Lint/RedundantCopEnableDirective: + Enabled: false -Style/Lambda: - Description: 'Use the new lambda literal syntax for single-line blocks.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#lambda-multi-line' - Enabled: true +Lint/RedundantRequireStatement: + Enabled: false -Style/LambdaCall: - Description: 'Use lambda.call(...) instead of lambda.(...).' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#proc-call' +Lint/RedundantSplatExpansion: Enabled: true -Style/LineEndConcatenation: - Description: >- - Use \ instead of + or << to concatenate two string literals at - line end. +Lint/RedundantStringCoercion: Enabled: true -Style/MethodCallWithoutArgsParentheses: - Description: 'Do not use parentheses for method calls with no arguments.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-args-no-parens' - Enabled: true +Lint/RedundantWithIndex: + Enabled: false -Style/MethodDefParentheses: - Description: >- - Checks if the method definitions have or don't have - parentheses. - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#method-parens' - Enabled: true +Lint/RedundantWithObject: + Enabled: false -Style/ModuleFunction: - Description: 'Checks for usage of `extend self` in modules.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#module-function' +Lint/RegexpAsCondition: Enabled: false -Style/MultilineBlockChain: - Description: 'Avoid multi-line chains of blocks.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#single-line-blocks' +Lint/RequireParentheses: Enabled: true -Style/MultilineIfThen: - Description: 'Do not use then for multi-line if/unless.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-then' +Lint/RescueException: Enabled: true -Style/MultilineTernaryOperator: - Description: >- - Avoid multi-line ?: (the ternary operator); - use if/unless instead. - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-multiline-ternary' - Enabled: true +Lint/RescueType: + Enabled: false -Style/MutableConstant: - Description: 'Do not assign mutable objects to constants.' +Lint/ReturnInVoidContext: Enabled: false -Style/NegatedIf: - Description: >- - Favor unless over if for negative conditions - (or control flow or). - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#unless-for-negatives' - Enabled: true +Lint/SafeNavigationChain: + Enabled: false -Style/NegatedWhile: - Description: 'Favor until over while for negative conditions.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#until-for-negatives' +Lint/SafeNavigationConsistency: + Enabled: false + +Lint/SafeNavigationWithEmpty: + Enabled: false + +Lint/ScriptPermission: + Enabled: false + +Lint/SendWithMixinArgument: + Enabled: false + +Lint/ShadowedArgument: + Enabled: false + +Lint/ShadowedException: Enabled: true -Style/NestedModifier: - Description: 'Avoid using nested modifiers.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-nested-modifiers' +Lint/ShadowingOuterLocalVariable: Enabled: true -Style/NestedParenthesizedCalls: - Description: >- - Parenthesize method calls which are nested inside the - argument list of another parenthesized method call. +Lint/StructNewOverride: + Enabled: false + +Lint/SuppressedException: Enabled: true -Style/NestedTernaryOperator: - Description: 'Use one expression per branch in a ternary operator.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-nested-ternary' +Lint/Syntax: Enabled: true -Style/Next: - Description: 'Use `next` to skip iteration instead of a condition at the end.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-nested-conditionals' +Lint/ToJSON: Enabled: false -Style/NilComparison: - Description: 'Prefer x.nil? to x == nil.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#predicate-methods' +Lint/UnderscorePrefixedVariableName: Enabled: true -Style/NonNilCheck: - Description: 'Checks for redundant nil checks.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-non-nil-checks' +Lint/UnifiedInteger: + Enabled: false + +Lint/UnreachableCode: Enabled: true -Style/Not: - Description: 'Use ! instead of not.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#bang-not-not' +Lint/UnusedBlockArgument: Enabled: true -Style/NumericLiterals: - Description: >- - Add underscores to large numeric literals to improve their - readability. - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#underscores-in-numerics' +Lint/UnusedMethodArgument: Enabled: true -Style/NumericLiteralPrefix: - Description: 'Use smallcase prefixes for numeric literals.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#numeric-literal-prefixes' +Lint/UriEscapeUnescape: + Enabled: false + +Lint/UriRegexp: + Enabled: false + +Lint/UselessAccessModifier: Enabled: true -Style/OneLineConditional: - Description: >- - Favor the ternary operator(?:) over - if/then/else/end constructs. - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#ternary-operator' +Lint/UselessAssignment: Enabled: false -Style/OptionalArguments: - Description: >- - Checks for optional arguments that do not appear at the end - of the argument list - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#optional-arguments' +Lint/UselessComparison: + Enabled: false + +Lint/UselessElseWithoutRescue: + Enabled: false + +Lint/UselessSetterCall: + Enabled: false + +Lint/Void: + Enabled: false + +#################### Metrics #################### + +Metrics/AbcSize: + Enabled: false + +Metrics/BlockLength: + Enabled: false + +Metrics/BlockNesting: + Enabled: false + +Metrics/ClassLength: + Enabled: false + +Metrics/CyclomaticComplexity: + Enabled: false + +Metrics/MethodLength: + Enabled: false + +Metrics/ModuleLength: + Enabled: false + +Metrics/ParameterLists: + Enabled: false + +Metrics/PerceivedComplexity: + Enabled: false + +#################### Migration #################### + +Migration/DepartmentName: + Enabled: false + +#################### Naming #################### + +Naming/AccessorMethodName: Enabled: true -Style/ParallelAssignment: - Description: >- - Check for simple usages of parallel assignment. - It will only warn when the number of variables - matches on both sides of the assignment. - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#parallel-assignment' +Naming/AsciiIdentifiers: Enabled: true -Style/ParenthesesAroundCondition: - Description: >- - Don't use parentheses around the condition of an - if/unless/while. - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-parens-if' +Naming/BinaryOperatorParameterName: + Enabled: false + +Naming/BlockParameterName: + Enabled: false + +Naming/ClassAndModuleCamelCase: Enabled: true -Style/PercentLiteralDelimiters: - Description: 'Use `%`-literal delimiters consistently' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#percent-literal-braces' +Naming/ConstantName: + Enabled: true + +Naming/FileName: + Enabled: true + +Naming/HeredocDelimiterCase: Enabled: false -Style/PercentQLiterals: - Description: 'Checks if uses of %Q/%q match the configured preference.' +Naming/HeredocDelimiterNaming: Enabled: false -Style/PerlBackrefs: - Description: 'Avoid Perl-style regex back references.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-perl-regexp-last-matchers' +Naming/MemoizedInstanceVariableName: Enabled: false -Style/Proc: - Description: 'Use proc instead of Proc.new.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#proc' +Naming/MethodName: Enabled: true -Style/RaiseArgs: - Description: 'Checks the arguments passed to raise/fail.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#exception-class-messages' +Naming/MethodParameterName: + Enabled: false + +Naming/PredicateName: Enabled: true -Style/RedundantBegin: - Description: "Don't use begin blocks when they are not needed." - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#begin-implicit' +Naming/RescuedExceptionsVariableName: + Enabled: false + +Naming/VariableName: Enabled: true -Style/RedundantException: - Description: "Checks for an obsolete RuntimeException argument in raise/fail." - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-explicit-runtimeerror' +Naming/VariableNumber: + Enabled: false + +#################### Performance #################### + +Performance/Caller: + Enabled: false + +Performance/CaseWhenSplat: + Enabled: false + +Performance/Casecmp: + Enabled: false + +Performance/ChainArrayAllocation: + Enabled: false + +Performance/CompareWithBlock: + Enabled: false + +Performance/Count: + Enabled: false + +Performance/Detect: + Enabled: false + +Performance/DoubleStartEndWith: + Enabled: false + +Performance/EndWith: + Enabled: false + +Performance/FixedSize: + Enabled: false + +Performance/FlatMap: + Enabled: false + +Performance/InefficientHashSearch: + Enabled: false + +Performance/OpenStruct: + Enabled: false + +Performance/RangeInclude: + Enabled: false + +Performance/RedundantBlockCall: + Enabled: false + +Performance/RedundantMatch: + Enabled: false + +Performance/RedundantMerge: Enabled: true -Style/RedundantFreeze: - Description: "Checks usages of Object#freeze on immutable objects." +Performance/RegexpMatch: + Enabled: false + +Performance/ReverseEach: + Enabled: false + +Performance/Size: + Enabled: false + +Performance/StartWith: + Enabled: false + +Performance/StringReplacement: + Enabled: false + +Performance/TimesMap: + Enabled: false + +Performance/UnfreezeString: + Enabled: false + +Performance/UriDefaultParser: + Enabled: false + +#################### Rails #################### + +Rails/ActionFilter: + Enabled: false + +Rails/ActiveRecordAliases: + Enabled: false + +Rails/ActiveRecordOverride: + Enabled: false + +Rails/ActiveSupportAliases: + Enabled: false + +Rails/ApplicationController: + Enabled: false + +Rails/ApplicationJob: + Enabled: false + +Rails/ApplicationMailer: + Enabled: false + +Rails/ApplicationRecord: + Enabled: false + +Rails/AssertNot: + Enabled: false + +Rails/BelongsTo: + Enabled: false + +Rails/Blank: + Enabled: false + +Rails/BulkChangeTable: + Enabled: false + +Rails/CreateTableWithTimestamps: + Enabled: false + +Rails/Date: + Enabled: false + +Rails/Delegate: + Enabled: false + +Rails/DelegateAllowBlank: + Enabled: false + +Rails/DynamicFindBy: + Enabled: false + +Rails/EnumHash: + Enabled: false + +Rails/EnumUniqueness: + Enabled: false + +Rails/EnvironmentComparison: + Enabled: false + +Rails/Exit: + Enabled: false + +Rails/FilePath: + Enabled: false + +Rails/FindBy: + Enabled: false + +Rails/FindEach: + Enabled: false + +Rails/HasAndBelongsToMany: + Enabled: false + +Rails/HasManyOrHasOneDependent: + Enabled: false + +Rails/HelperInstanceVariable: + Enabled: false + +Rails/HttpPositionalArguments: + Enabled: false + +Rails/HttpStatus: + Enabled: false + +Rails/IgnoredSkipActionFilterOption: + Enabled: false + +Rails/InverseOf: + Enabled: false + +Rails/LexicallyScopedActionFilter: + Enabled: false + +Rails/LinkToBlank: + Enabled: false + +Rails/NotNullColumn: + Enabled: false + +Rails/Output: + Enabled: false + +Rails/OutputSafety: + Enabled: false + +Rails/PluralizationGrammar: + Enabled: false + +Rails/Presence: + Enabled: false + +Rails/Present: + Enabled: false + +Rails/RakeEnvironment: + Enabled: false + +Rails/ReadWriteAttribute: + Enabled: false + +Rails/RedundantAllowNil: + Enabled: false + +Rails/RedundantReceiverInWithOptions: + Enabled: false + +Rails/ReflectionClassName: + Enabled: false + +Rails/RefuteMethods: + Enabled: false + +Rails/RelativeDateConstant: + Enabled: false + +Rails/RequestReferer: + Enabled: false + +Rails/ReversibleMigration: + Enabled: false + +Rails/SafeNavigation: + Enabled: false + +Rails/SafeNavigationWithBlank: + Enabled: false + +Rails/SaveBang: + Enabled: false + +Rails/ScopeArgs: + Enabled: false + +Rails/SkipsModelValidations: + Enabled: false + +Rails/TimeZone: + Enabled: false + +Rails/UniqBeforePluck: + Enabled: false + +Rails/UnknownEnv: + Enabled: false + +Rails/Validation: + Enabled: false + +#################### Security #################### + +Security/Eval: + Enabled: false + +Security/JSONLoad: + Enabled: false + +Security/MarshalLoad: + Enabled: false + +Security/Open: + Enabled: false + +Security/YAMLLoad: + Enabled: false + +#################### Style #################### + +Style/AccessModifierDeclarations: + Enabled: false + +Style/Alias: Enabled: true -Style/RedundantParentheses: - Description: "Checks for parentheses that seem not to serve any purpose." +Style/AndOr: Enabled: true -Style/RedundantReturn: - Description: "Don't use return where it's not required." - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-explicit-return' +Style/ArrayJoin: Enabled: true -Style/RedundantSelf: - Description: "Don't use self where it's not needed." - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-self-unless-required' +Style/AsciiComments: + Enabled: false + +Style/Attr: Enabled: true -Style/RegexpLiteral: - Description: 'Use / or %r around regular expressions.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#percent-r' +Style/AutoResourceCleanup: Enabled: false -Style/RescueModifier: - Description: 'Avoid using rescue in its modifier form.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-rescue-modifiers' +Style/BarePercentLiterals: + Enabled: false + +Style/BeginBlock: Enabled: true -Style/SelfAssignment: - Description: >- - Checks for places where self-assignment shorthand should have - been used. - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#self-assignment' +Style/BlockComments: Enabled: true -Style/Semicolon: - Description: "Don't use semicolons to terminate expressions." - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-semicolon' +Style/BlockDelimiters: + Enabled: false + Exclude: + - spec/**/* + +Style/CaseEquality: Enabled: true -Style/SignalException: - Description: 'Checks for proper usage of fail and raise.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#prefer-raise-over-fail' +Style/CharacterLiteral: Enabled: true -Style/SingleLineBlockParams: - Description: 'Enforces the names of some block params.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#reduce-blocks' +Style/ClassAndModuleChildren: Enabled: false -Style/SingleLineMethods: - Description: 'Avoid single-line methods.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-single-line-methods' +Style/ClassCheck: Enabled: true -Style/SpecialGlobalVars: - Description: 'Avoid Perl-style global variables.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-cryptic-perlisms' +Style/ClassMethods: Enabled: true -Style/StabbyLambdaParentheses: - Description: 'Check for the usage of parentheses around stabby lambda arguments.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#stabby-lambda-with-args' +Style/ClassVars: Enabled: true -Style/StringLiterals: - Description: 'Checks if uses of quotes match the configured preference.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#consistent-string-literals' +Style/CollectionMethods: Enabled: false -Style/StringLiteralsInInterpolation: - Description: >- - Checks if uses of quotes inside expressions in interpolated - strings match the configured preference. +Style/ColonMethodCall: + Enabled: true + +Style/ColonMethodDefinition: Enabled: false -Style/StructInheritance: - Description: 'Checks for inheritance from Struct.new.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-extend-struct-new' +Style/CommandLiteral: Enabled: true -Style/SymbolLiteral: - Description: 'Use plain symbols instead of string symbols when possible.' +Style/CommentAnnotation: Enabled: true -Style/SymbolProc: - Description: 'Use symbols as procs instead of blocks when possible.' +Style/CommentedKeyword: + Enabled: false + +Style/ConditionalAssignment: + Enabled: false + +Style/ConstantVisibility: + Enabled: false + +Style/Copyright: + Enabled: false + +Style/DateTime: + Enabled: false + +Style/DefWithParentheses: Enabled: true -Style/TrailingCommaInArguments: - Description: 'Checks for trailing comma in argument lists.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-trailing-params-comma' +Style/Dir: + Enabled: false + +Style/Documentation: Enabled: true + Exclude: + - db/migrate/* + - spec/**/* + - test/**/* -Style/TrailingCommaInArrayLiteral: - Description: 'Checks for trailing comma in array literals.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-trailing-array-commas' +Style/DocumentationMethod: + Enabled: false + +Style/DoubleCopDisableDirective: + Enabled: false + +Style/DoubleNegation: Enabled: true -Style/TrailingCommaInHashLiteral: - Description: 'Checks for trailing comma in hash literals.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-trailing-array-commas' +Style/EachForSimpleLoop: Enabled: true - EnforcedStyleForMultiline: no_comma -Style/TrivialAccessors: - Description: 'Prefer attr_* methods to trivial readers/writers.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#attr_family' +Style/EachWithObject: + Enabled: false + +Style/EmptyBlockParameter: + Enabled: false + +Style/EmptyCaseCondition: Enabled: true -Style/UnlessElse: - Description: >- - Do not use unless with else. Rewrite these with the positive - case first. - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-else-with-unless' +Style/EmptyElse: Enabled: true -Style/UnneededCapitalW: - Description: 'Checks for %W when interpolation is not needed.' +Style/EmptyLambdaParameter: + Enabled: false + +Style/EmptyLiteral: Enabled: true -Style/UnneededInterpolation: - Description: 'Checks for strings that are just an interpolated expression.' +Style/EmptyMethod: + Enabled: false + +Style/Encoding: + Enabled: false + +Style/EndBlock: Enabled: true -Style/UnneededPercentQ: - Description: 'Checks for %q/%Q when single quotes or double quotes would do.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#percent-q' +Style/EvalWithLocation: Enabled: false -Style/TrailingUnderscoreVariable: - Description: >- - Checks for the usage of unneeded trailing underscores at the - end of parallel variable assignment. - AllowNamedUnderscoreVariables: true +Style/EvenOdd: Enabled: true -Style/VariableInterpolation: - Description: >- - Don't interpolate global, instance and class variables - directly in strings. - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#curlies-interpolate' +Style/ExpandPathArguments: + Enabled: false + +Style/FloatDivision: + Enabled: false + +Style/For: Enabled: true -Style/WhenThen: - Description: 'Use when x then ... for one-line cases.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#one-line-cases' +Style/FormatString: Enabled: true -Style/WhileUntilDo: - Description: 'Checks for redundant do after while or until.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-multiline-while-do' +Style/FormatStringToken: + Enabled: false + +Style/FrozenStringLiteralComment: + Enabled: false + +Style/GlobalVars: Enabled: true -Style/WhileUntilModifier: - Description: >- - Favor modifier while/until usage when you have a - single-line body. - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#while-as-a-modifier' +Style/GuardClause: Enabled: true -Style/WordArray: - Description: 'Use %w or %W for arrays of words.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#percent-w' +Style/HashEachMethods: + Enabled: false + +Style/HashSyntax: Enabled: true + EnforcedStyle: ruby19_no_mixed_keys + Exclude: + - config/routes.rb -Style/ZeroLengthPredicate: - Description: 'Use #empty? when testing for objects of length 0.' +Style/HashTransformKeys: + Enabled: false + +Style/HashTransformValues: + Enabled: false + +Style/IdenticalConditionalBranches: + Enabled: true + +Style/IfInsideElse: + Enabled: true + +Style/IfUnlessModifier: + Enabled: true + +Style/IfUnlessModifierOfIfUnless: + Enabled: true + +Style/IfWithSemicolon: Enabled: true -#################### Naming ################################# +Style/ImplicitRuntimeError: + Enabled: false -Naming/AccessorMethodName: - Description: Check the naming of accessor methods for get_/set_. - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#accessor_mutator_method_names' +Style/InfiniteLoop: Enabled: true -Naming/AsciiIdentifiers: - Description: 'Use only ascii symbols in identifiers.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#english-identifiers' - Enabled: true +Style/InlineComment: + Enabled: false -Naming/ClassAndModuleCamelCase: - Description: 'Use CamelCase for classes and modules.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#camelcase-classes' - Enabled: true +Style/InverseMethods: + Enabled: false -Naming/ConstantName: - Description: 'Constants should use SCREAMING_SNAKE_CASE.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#screaming-snake-case' +Style/IpAddresses: + Enabled: false + +Style/Lambda: Enabled: true -Naming/FileName: - Description: 'Use snake_case for source file names.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#snake-case-files' +Style/LambdaCall: Enabled: true -Naming/MethodName: - Description: 'Use the configured style when naming methods.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#snake-case-symbols-methods-vars' +Style/LineEndConcatenation: Enabled: true -Naming/BinaryOperatorParameterName: - Description: 'When defining binary operators, name the argument other.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#other-arg' +Style/MethodCallWithArgsParentheses: Enabled: false -Naming/PredicateName: - Description: 'Check the names of predicate methods.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#bool-methods-qmark' +Style/MethodCallWithoutArgsParentheses: Enabled: true -Naming/VariableName: - Description: 'Use the configured style when naming variables.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#snake-case-symbols-methods-vars' +Style/MethodCalledOnDoEndBlock: + Enabled: false + +Style/MethodDefParentheses: Enabled: true -#################### Metrics ################################ +Style/MethodMissingSuper: + Enabled: false -Metrics/AbcSize: - Description: >- - A calculated magnitude based on number of assignments, - branches, and conditions. - Reference: 'http://c2.com/cgi/wiki?AbcMetric' +Style/MinMax: Enabled: false -Metrics/BlockNesting: - Description: 'Avoid excessive block nesting' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#three-is-the-number-thou-shalt-count' +Style/MissingElse: Enabled: false -Metrics/ClassLength: - Description: 'Avoid classes longer than 100 lines of code.' +Style/MissingRespondToMissing: Enabled: false -Metrics/ModuleLength: - Description: 'Avoid modules longer than 100 lines of code.' +Style/MixinGrouping: Enabled: false -Metrics/CyclomaticComplexity: - Description: >- - A complexity metric that is strongly correlated to the number - of test cases needed to validate a method. - Enabled: false - -Metrics/LineLength: - Description: 'Limit lines to 80 characters.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#80-character-limits' - Enabled: true - Max: 80 - # To make it possible to copy or click on URIs in the code, we allow lines - # containing a URI to be longer than Max. - AllowHeredoc: true - AllowURI: true - URISchemes: - - http - - https - # The IgnoreCopDirectives option causes the LineLength rule to ignore cop - # directives like '# rubocop: enable ...' when calculating a line's length. - IgnoreCopDirectives: false - # The IgnoredPatterns option is a list of !ruby/regexp and/or string - # elements. Strings will be converted to Regexp objects. A line that matches - # any regular expression listed in this option will be ignored by LineLength. - IgnoredPatterns: - - '^\s*it\s+.*do$' - - '^\s*context\s+.*do$' - - '^\s*describe\s+.*do$' - - '^\s*class\s+[A-Z].*<.*' +Style/MixinUsage: + Enabled: false -Metrics/MethodLength: - Description: 'Avoid methods longer than 10 lines of code.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#short-methods' +Style/ModuleFunction: Enabled: false -Metrics/ParameterLists: - Description: 'Avoid parameter lists longer than three or four parameters.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#too-many-params' +Style/MultilineBlockChain: + Enabled: true + +Style/MultilineIfModifier: Enabled: false -Metrics/PerceivedComplexity: - Description: >- - A complexity metric geared towards measuring complexity for a - human reader. +Style/MultilineIfThen: + Enabled: true + +Style/MultilineMemoization: Enabled: false -#################### Lint ################################ -### Warnings +Style/MultilineMethodSignature: + Enabled: false -Lint/AmbiguousOperator: - Description: >- - Checks for ambiguous operators in the first argument of a - method invocation without parentheses. - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#parens-as-args' +Style/MultilineTernaryOperator: Enabled: true -Lint/AmbiguousRegexpLiteral: - Description: >- - Checks for ambiguous regexp literals in the first argument of - a method invocation without parentheses. - Enabled: true +Style/MultilineWhenThen: + Enabled: false -Lint/AssignmentInCondition: - Description: "Don't use assignment in conditions." - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#safe-assignment-in-condition' - Enabled: true +Style/MultipleComparison: + Enabled: false -Lint/CircularArgumentReference: - Description: "Default values in optional keyword arguments and optional ordinal arguments should not refer back to the name of the argument." - Enabled: true +Style/MutableConstant: + Enabled: false -Lint/Debugger: - Description: 'Check for debugger calls.' +Style/NegatedIf: Enabled: true -Lint/DeprecatedClassMethods: - Description: 'Check for deprecated class method calls.' - Enabled: true +Style/NegatedUnless: + Enabled: false -Lint/DuplicateMethods: - Description: 'Check for duplicate method definitions.' +Style/NegatedWhile: Enabled: true -Lint/DuplicatedKey: - Description: 'Check for duplicate keys in hash literals.' +Style/NestedModifier: Enabled: true -Lint/EachWithObjectArgument: - Description: 'Check for immutable argument given to each_with_object.' +Style/NestedParenthesizedCalls: Enabled: true -Lint/ElseLayout: - Description: 'Check for odd code arrangement in an else block.' +Style/NestedTernaryOperator: Enabled: true -Lint/EmptyEnsure: - Description: 'Checks for empty ensure block.' - Enabled: true +Style/Next: + Enabled: false -Lint/EmptyInterpolation: - Description: 'Checks for empty string interpolation.' +Style/NilComparison: Enabled: true -Lint/EndInMethod: - Description: 'END blocks should not be placed inside method definitions.' +Style/NonNilCheck: Enabled: true -Lint/EnsureReturn: - Description: 'Do not use return in an ensure block.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-return-ensure' +Style/Not: Enabled: true -Lint/FloatOutOfRange: - Description: >- - Catches floating-point literals too large or small for Ruby to - represent. +Style/NumericLiteralPrefix: Enabled: true -Lint/FormatParameterMismatch: - Description: 'The number of parameters to format/sprint must match the fields.' +Style/NumericLiterals: Enabled: true -Lint/HandleExceptions: - Description: "Don't suppress exception." - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#dont-hide-exceptions' - Enabled: true +Style/NumericPredicate: + Enabled: false -Lint/ImplicitStringConcatenation: - Description: >- - Checks for adjacent string literals on the same line, which - could better be represented as a single string literal. - Enabled: true +Style/OneLineConditional: + Enabled: false -Lint/IneffectiveAccessModifier: - Description: >- - Checks for attempts to use `private` or `protected` to set - the visibility of a class method, which does not work. - Enabled: true +Style/OptionHash: + Enabled: false -Lint/InheritException: - Description: 'Avoid inheriting from the `Exception` class.' +Style/OptionalArguments: Enabled: true -Lint/LiteralAsCondition: - Description: 'Checks of literals used in conditions.' - Enabled: true +Style/OrAssignment: + Enabled: false -Lint/LiteralInInterpolation: - Description: 'Checks for literals used in interpolation.' +Style/ParallelAssignment: Enabled: true -Lint/Loop: - Description: >- - Use Kernel#loop with break rather than begin/end/until or - begin/end/while for post-loop tests. - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#loop-with-break' +Style/ParenthesesAroundCondition: Enabled: true -Lint/NestedMethodDefinition: - Description: 'Do not use nested method definitions.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-nested-methods' - Enabled: true +Style/PercentLiteralDelimiters: + Enabled: false -Lint/NextWithoutAccumulator: - Description: >- - Do not omit the accumulator when calling `next` - in a `reduce`/`inject` block. - Enabled: true +Style/PercentQLiterals: + Enabled: false -Lint/NonLocalExitFromIterator: - Description: 'Do not use return in iterator to cause non-local exit.' - Enabled: true +Style/PerlBackrefs: + Enabled: false -Lint/ParenthesesAsGroupedExpression: - Description: >- - Checks for method calls with a space before the opening - parenthesis. - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#parens-no-spaces' +Style/PreferredHashMethods: Enabled: true -Lint/PercentStringArray: - Description: >- - Checks for unwanted commas and quotes in %w/%W literals. +Style/Proc: Enabled: true -Lint/PercentSymbolArray: - Description: >- - Checks for unwanted commas and colons in %i/%I literals. +Style/RaiseArgs: Enabled: true -Lint/RandOne: - Description: >- - Checks for `rand(1)` calls. Such calls always return `0` - and most likely a mistake. - Enabled: true +Style/RandomWithOffset: + Enabled: false -Lint/RequireParentheses: - Description: >- - Use parentheses in the method call to avoid confusion - about precedence. +Style/RedundantBegin: Enabled: true -Lint/RescueException: - Description: 'Avoid rescuing the Exception class.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-blind-rescues' +Style/RedundantCapitalW: Enabled: true -Lint/ShadowedException: - Description: >- - Avoid rescuing a higher level exception - before a lower level exception. +Style/RedundantCondition: Enabled: false -Lint/ShadowingOuterLocalVariable: - Description: >- - Do not use the same name as outer local variable - for block arguments or block local variables. +Style/RedundantConditional: Enabled: false -Lint/StringConversionInInterpolation: - Description: 'Checks for Object#to_s usage in string interpolation.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-to-s' - Enabled: false +Style/RedundantException: + Enabled: true -Lint/UnderscorePrefixedVariableName: - Description: 'Do not use prefix `_` for a variable that is used.' - Enabled: false +Style/RedundantFreeze: + Enabled: true -Lint/UnneededCopDisableDirective: - Description: >- - Checks for rubocop:disable comments that can be removed. - Note: this cop is not disabled when disabling all cops. - It must be explicitly disabled. - Enabled: false +Style/RedundantInterpolation: + Enabled: true -Lint/UnusedBlockArgument: - Description: 'Checks for unused block arguments.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#underscore-unused-vars' - Enabled: false +Style/RedundantParentheses: + Enabled: true -Lint/UnusedMethodArgument: - Description: 'Checks for unused method arguments.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#underscore-unused-vars' +Style/RedundantPercentQ: Enabled: false -Lint/UnreachableCode: - Description: 'Unreachable code.' - Enabled: false +Style/RedundantReturn: + Enabled: true -Lint/UselessAccessModifier: - Description: 'Checks for useless access modifiers.' +Style/RedundantSelf: + Enabled: true + +Style/RedundantSort: Enabled: false -Lint/UnneededSplatExpansion: - Description: 'Checks for useless array splats.' +Style/RedundantSortBy: Enabled: false -Lint/UselessAssignment: - Description: 'Checks for useless assignment to a local variable.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#underscore-unused-vars' +Style/RegexpLiteral: Enabled: false -Lint/UselessComparison: - Description: 'Checks for comparison of something with itself.' +Style/RescueModifier: + Enabled: true + +Style/RescueStandardError: Enabled: false -Lint/UselessElseWithoutRescue: - Description: 'Checks for useless `else` in `begin..end` without `rescue`.' +Style/ReturnNil: Enabled: false -Lint/UselessSetterCall: - Description: 'Checks for useless setter call to a local variable.' +Style/SafeNavigation: Enabled: false -Lint/Void: - Description: 'Possible use of operator/literal/variable in void context.' +Style/Sample: Enabled: false -##################### Performance ############################# +Style/SelfAssignment: + Enabled: true -Performance/Casecmp: - Description: >- - Use `casecmp` rather than `downcase ==`, `upcase ==`, `== downcase`, or `== upcase`.. - Reference: 'https://github.com/JuanitoFatas/fast-ruby#stringcasecmp-vs-stringdowncase---code' - Enabled: false +Style/Semicolon: + Enabled: true -Performance/CaseWhenSplat: - Description: >- - Place `when` conditions that use splat at the end - of the list of `when` branches. +Style/Send: Enabled: false -Performance/Count: - Description: >- - Use `count` instead of `select...size`, `reject...size`, - `select...count`, `reject...count`, `select...length`, - and `reject...length`. - # This cop has known compatibility issues with `ActiveRecord` and other - # frameworks. ActiveRecord's `count` ignores the block that is passed to it. - # For more information, see the documentation in the cop itself. - # If you understand the known risk, you can disable `SafeMode`. - SafeMode: true - Enabled: false +Style/SignalException: + Enabled: true + EnforcedStyle: only_raise -Performance/Detect: - Description: >- - Use `detect` instead of `select.first`, `find_all.first`, - `select.last`, and `find_all.last`. - Reference: 'https://github.com/JuanitoFatas/fast-ruby#enumerabledetect-vs-enumerableselectfirst-code' - # This cop has known compatibility issues with `ActiveRecord` and other - # frameworks. `ActiveRecord` does not implement a `detect` method and `find` - # has its own meaning. Correcting `ActiveRecord` methods with this cop - # should be considered unsafe. - SafeMode: true +Style/SingleLineBlockParams: Enabled: false -Performance/DoubleStartEndWith: - Description: >- - Use `str.{start,end}_with?(x, ..., y, ...)` - instead of `str.{start,end}_with?(x, ...) || str.{start,end}_with?(y, ...)`. - Enabled: false +Style/SingleLineMethods: + Enabled: true -Performance/EndWith: - Description: 'Use `end_with?` instead of a regex match anchored to the end of a string.' - Reference: 'https://github.com/JuanitoFatas/fast-ruby#stringmatch-vs-stringstart_withstringend_with-code-start-code-end' - Enabled: false +Style/SpecialGlobalVars: + Enabled: true -Performance/FixedSize: - Description: 'Do not compute the size of statically sized objects except in constants' +Style/StabbyLambdaParentheses: + Enabled: true + +Style/StderrPuts: Enabled: false -Performance/FlatMap: - Description: >- - Use `Enumerable#flat_map` - instead of `Enumerable#map...Array#flatten(1)` - or `Enumberable#collect..Array#flatten(1)` - Reference: 'https://github.com/JuanitoFatas/fast-ruby#enumerablemaparrayflatten-vs-enumerableflat_map-code' +Style/StringHashKeys: Enabled: false - EnabledForFlattenWithoutParams: false - # If enabled, this cop will warn about usages of - # `flatten` being called without any parameters. - # This can be dangerous since `flat_map` will only flatten 1 level, and - # `flatten` without any parameters can flatten multiple levels. -Performance/LstripRstrip: - Description: 'Use `strip` instead of `lstrip.rstrip`.' +Style/StringLiterals: Enabled: false -Performance/RangeInclude: - Description: 'Use `Range#cover?` instead of `Range#include?`.' - Reference: 'https://github.com/JuanitoFatas/fast-ruby#cover-vs-include-code' +Style/StringLiteralsInInterpolation: Enabled: false -Performance/RedundantBlockCall: - Description: 'Use `yield` instead of `block.call`.' - Reference: 'https://github.com/JuanitoFatas/fast-ruby#proccall-vs-yield-code' +Style/StringMethods: Enabled: false -Performance/RedundantMatch: - Description: >- - Use `=~` instead of `String#match` or `Regexp#match` in a context where the - returned `MatchData` is not needed. +Style/Strip: Enabled: false -Performance/RedundantMerge: - Description: 'Use Hash#[]=, rather than Hash#merge! with a single key-value pair.' - Reference: 'https://github.com/JuanitoFatas/fast-ruby#hashmerge-vs-hash-code' +Style/StructInheritance: Enabled: true -Performance/RedundantSortBy: - Description: 'Use `sort` instead of `sort_by { |x| x }`.' - Enabled: false - -Performance/ReverseEach: - Description: 'Use `reverse_each` instead of `reverse.each`.' - Reference: 'https://github.com/JuanitoFatas/fast-ruby#enumerablereverseeach-vs-enumerablereverse_each-code' +Style/SymbolArray: Enabled: false -Performance/Sample: - Description: >- - Use `sample` instead of `shuffle.first`, - `shuffle.last`, and `shuffle[Fixnum]`. - Reference: 'https://github.com/JuanitoFatas/fast-ruby#arrayshufflefirst-vs-arraysample-code' - Enabled: false +Style/SymbolLiteral: + Enabled: true -Performance/Size: - Description: >- - Use `size` instead of `count` for counting - the number of elements in `Array` and `Hash`. - Reference: 'https://github.com/JuanitoFatas/fast-ruby#arraycount-vs-arraysize-code' - Enabled: false +Style/SymbolProc: + Enabled: true -Performance/StartWith: - Description: 'Use `start_with?` instead of a regex match anchored to the beginning of a string.' - Reference: 'https://github.com/JuanitoFatas/fast-ruby#stringmatch-vs-stringstart_withstringend_with-code-start-code-end' +Style/TernaryParentheses: Enabled: false -Performance/StringReplacement: - Description: >- - Use `tr` instead of `gsub` when you are replacing the same - number of characters. Use `delete` instead of `gsub` when - you are deleting characters. - Reference: 'https://github.com/JuanitoFatas/fast-ruby#stringgsub-vs-stringtr-code' +Style/TrailingBodyOnClass: Enabled: false -Performance/TimesMap: - Description: 'Checks for .times.map calls.' +Style/TrailingBodyOnMethodDefinition: Enabled: false -##################### Rails ################################## - -Rails/ActionFilter: - Description: 'Enforces consistent use of action filter methods.' +Style/TrailingBodyOnModule: Enabled: false -Rails/Date: - Description: >- - Checks the correct usage of date aware methods, - such as Date.today, Date.current etc. - Enabled: false +Style/TrailingCommaInArguments: + Enabled: true -Rails/Delegate: - Description: 'Prefer delegate method for delegations.' - Enabled: false +Style/TrailingCommaInArrayLiteral: + Enabled: true -Rails/Exit: - Description: >- - Favor `fail`, `break`, `return`, etc. over `exit` in - application or library code outside of Rake files to avoid - exits during unit testing or running in production. +Style/TrailingCommaInBlockArgs: Enabled: false -Rails/FindBy: - Description: 'Prefer find_by over where.first.' - Enabled: false +Style/TrailingCommaInHashLiteral: + Enabled: true -Rails/FindEach: - Description: 'Prefer all.find_each over all.find.' +Style/TrailingMethodEndStatement: Enabled: false -Rails/HasAndBelongsToMany: - Description: 'Prefer has_many :through to has_and_belongs_to_many.' - Enabled: false +Style/TrailingUnderscoreVariable: + Enabled: true -Rails/Output: - Description: 'Checks for calls to puts, print, etc.' - Enabled: false +Style/TrivialAccessors: + Enabled: true -Rails/OutputSafety: - Description: 'The use of `html_safe` or `raw` may be a security risk.' - Enabled: false +Style/UnlessElse: + Enabled: true -Rails/PluralizationGrammar: - Description: 'Checks for incorrect grammar when using methods like `3.day.ago`.' +Style/UnpackFirst: Enabled: false -Rails/ReadWriteAttribute: - Description: >- - Checks for read_attribute(:attr) and - write_attribute(:attr, val). - Enabled: false +Style/VariableInterpolation: + Enabled: true -Rails/RequestReferer: - Description: 'Use consistent syntax for request.referer.' - Enabled: false +Style/WhenThen: + Enabled: true -Rails/ScopeArgs: - Description: 'Checks the arguments of ActiveRecord scopes.' - Enabled: false +Style/WhileUntilDo: + Enabled: true -Rails/TimeZone: - Description: 'Checks the correct usage of time zone aware methods.' - StyleGuide: 'https://github.com/bbatsov/rails-style-guide#time' - Reference: 'http://danilenko.org/2012/7/6/rails_timezones' - Enabled: false +Style/WhileUntilModifier: + Enabled: true -Rails/UniqBeforePluck: - Description: 'Prefer the use of uniq or distinct before pluck.' - Enabled: false +Style/WordArray: + Enabled: true -Rails/Validation: - Description: 'Use validates :attribute, hash of validations.' +Style/YodaCondition: Enabled: false -Security/Eval: - Description: 'The use of eval represents a serious security risk.' - Enabled: false +Style/ZeroLengthPredicate: + Enabled: true diff --git a/.travis.yml b/.travis.yml index 8703901255..393a41987a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,18 +6,25 @@ branches: - master - develop rvm: - - 2.3.3 # Debian Stretch - 2.3 # Latest official 2.3.x - 2.4 # Latest official 2.4.x - 2.5 # Latest official 2.5.x - 2.6 # Latest official 2.6.x - - ruby-head + - 2.7 # Latest official 2.7.x gemfile: - Gemfile - - Gemfile.rails_next matrix: fast_finish: true + include: + - if: type != pull_request + rvm: 2.3.3 # Debian Stretch + - if: type != pull_request + rvm: ruby-head + - if: head_branch =~ /^rails/ + rvm: 2.6 + gemfile: Gemfile.rails_next allow_failures: + - rvm: 2.7 - rvm: ruby-head - gemfile: Gemfile.rails_next services: @@ -41,7 +48,6 @@ before_install: - psql -c "create database foi_test template template_utf8;" -U postgres - cp config/database.yml-test config/database.yml - cp config/general.yml-example config/general.yml - - cp config/newrelic.yml-example config/newrelic.yml - sudo apt-get update - export DEBIAN_FRONTEND=noninteractive - sudo apt-get -y install exim4-daemon-light diff --git a/.vagrant.yml.example b/.vagrant.yml.example index 66a4eee8f5..368b96250a 100644 --- a/.vagrant.yml.example +++ b/.vagrant.yml.example @@ -1,5 +1,7 @@ fqdn: alaveteli.10.10.10.30.nip.io ip: 10.10.10.30 +# Only use this on networks you trust +public_network: false memory: 1536 themes_dir: ../alaveteli-themes os: stretch64 diff --git a/Gemfile b/Gemfile index 8b873c5e4f..e7825690b3 100644 --- a/Gemfile +++ b/Gemfile @@ -84,68 +84,68 @@ def rails_upgrade? %w[1 true].include?(ENV['RAILS_UPGRADE']) end -gem 'rails', rails_upgrade? ? '5.2.3' : '5.1.7' +gem 'rails', rails_upgrade? ? '~> 6.0.3' : '~> 5.2.4' -gem 'pg', '~> 0.20.0' +gem 'pg', '~> 1.2.3' # New gem releases aren't being done. master is newer and supports Rails > 3.0 gem 'acts_as_versioned', :git => 'https://github.com/technoweenie/acts_as_versioned.git', :ref => '63b1fc8529d028' -gem 'active_model_otp', :git => 'https://github.com/heapsource/active_model_otp.git', :ref => '55d93a3979' +gem 'active_model_otp' gem 'bcrypt', '~> 3.1.13' -gem 'cancancan', '~> 1.17.0', '< 2.0.0' +gem 'cancancan', '~> 3.1.0' gem 'charlock_holmes', '~> 0.7.7' gem 'dalli', '~> 2.7.0' gem 'dynamic_form', '~> 1.1.0' -gem 'exception_notification', ['~> 4.1.0', '< 4.1.2'] +gem 'exception_notification', '~> 4.4.3' gem 'fancybox-rails', '~> 0.3.0' gem 'gnuplot', '~> 2.6.0' gem 'htmlentities', '~> 4.3.0' -gem 'icalendar', '~> 2.4.0' -gem 'jquery-rails', '~> 4.3.3' +gem 'icalendar', '~> 2.5.3' +gem 'jquery-rails', '~> 4.4.0' gem 'jquery-ui-rails', '~> 6.0.0' -gem 'json', ['~> 1.8.0', '< 2.0.0'] -gem 'holidays', '~> 4.7.0', '< 5.0.0' +gem 'json', '~> 2.3.1' +gem 'holidays', '~> 7.1.0' gem 'iso_country_codes', '~> 0.7.8' -gem 'mail', '~> 2.6.6' +gem 'mail', rails_upgrade? ? '~> 2.7.1' : '~> 2.6.6' gem 'maxmind-db', '~> 1.0.0' -gem 'mahoro', '~> 0.4' -gem 'newrelic_rpm' -gem 'nokogiri', '~> 1.10.7' +gem 'mahoro', '~> 0.5' +gem 'nokogiri', '~> 1.10.10' gem 'open4', '~> 1.3.0' -gem 'rack', '~> 2.0.8' +gem 'rack', '~> 2.2.3' gem 'rack-ssl', '~> 1.4.0' -gem 'rack-utf8_sanitizer', '~> 1.3.0' -gem 'recaptcha', '~> 4.9.0', '< 4.10.0', :require => 'recaptcha/rails' -gem 'rmagick', '~> 2.16.0' -gem 'rolify', '~> 5.2.0' +gem 'rack-utf8_sanitizer', '~> 1.7.0' +gem 'recaptcha', '~> 5.5.0', require: 'recaptcha/rails' +gem 'mini_magick', '~> 4.10.0' +gem 'rolify', '~> 5.3.0' gem 'ruby-msg', '~> 1.5.0', :git => 'https://github.com/mysociety/ruby-msg.git', :branch => 'ascii-encoding' gem 'rubyzip', '~> 1.3.0', '< 2.0.0' -gem 'secure_headers', '~> 3.6.0' +gem 'secure_headers', '~> 6.3.1' gem 'statistics2', '~> 0.54' -gem 'strip_attributes', :git => 'https://github.com/mysociety/strip_attributes.git', :ref => 'c1c14da' -gem 'stripe', '~> 3.29.0' +gem 'strip_attributes', :git => 'https://github.com/mysociety/strip_attributes.git', :branch => 'globalize3-rails5.2' +gem 'stripe', '~> 5.22.0' gem 'syslog_protocol', '~> 0.9.0' -gem 'thin', '~> 1.5.0', '< 1.6.0' +gem 'thin', '~> 1.7.2' gem 'vpim', '~> 13.11.11' -gem 'will_paginate', '~> 3.1.8' -gem 'xapian-full-alaveteli', '~> 1.2.21.1' +gem 'will_paginate', '~> 3.3.0' +gem 'xapian-full-alaveteli', '~> 1.4.11.1' gem 'xml-simple', '~> 1.1.0', :require => 'xmlsimple' +gem 'zip_tricks', '~> 5.3.1' # Gems only used by the research export task -gem 'gender_detector', '~> 1.0.0' +gem 'gender_detector', '~> 2.0.0' # Gems related to internationalisation -gem 'i18n', ['~> 0.9.0', '< 0.9.3'] -gem 'rails-i18n', '~> 5.1.0' -gem 'gettext_i18n_rails', '~> 0.10.1' - gem 'fast_gettext', '< 1.2.0' -gem 'gettext', '~> 2.3.0' -gem 'globalize', '~> 5.2.0' -gem 'locale', '~> 2.0.0', '< 2.1.0' +gem 'i18n', '~> 1.8.3' +gem 'rails-i18n', rails_upgrade? ? '~> 6.0.0' : '~> 5.1.0' +gem 'gettext_i18n_rails', '~> 1.8.1' + gem 'fast_gettext', '< 2.0.3' +gem 'gettext', '< 3.3.0' +gem 'globalize', rails_upgrade? ? '~> 5.3.0' : '~> 5.2.0' +gem 'locale', '~> 2.1.3' gem 'routing-filter', '~> 0.6.2' gem 'unicode', '~> 0.4.4' gem 'unidecoder', '~> 1.1.0' -gem 'money', '~> 6.13.6' +gem 'money', '~> 6.13.8' # mime-types 3.0.0 requires Ruby 2.0.0, and _something_ is trying to update it gem 'mime-types', '< 3.0.0', require: false @@ -153,42 +153,40 @@ gem 'mime-types', '< 3.0.0', require: false # Assets gem 'bootstrap-sass', '~> 2.3.2.2' gem 'sass-rails', '~> 5.0.7' -gem 'compass-rails', '~> 3.1.0' -gem 'coffee-rails', '~> 4.2.0' -gem 'uglifier', '~> 3.2.0' +gem 'uglifier', '~> 4.2.0' gem 'therubyracer', '~> 0.12.0' # Feature flags gem 'alaveteli_features', :path => 'gems/alaveteli_features' group :test do - gem 'webmock', '~> 3.5.1' - gem 'coveralls', '~> 0.8.0', :require => false - gem 'capybara', '~> 3.5.0' - gem 'delorean', '~> 2.1.0' - gem 'stripe-ruby-mock', git: 'https://github.com/gbp/stripe-ruby-mock', - branch: 'develop' + gem 'webmock', '~> 3.8.3' + gem 'coveralls', '~> 0.8.23', require: false + gem 'capybara', '~> 3.15.1' + gem 'stripe-ruby-mock', git: 'https://github.com/stripe-ruby-mock/stripe-ruby-mock', + ref: '2c925fd' gem('rails-controller-testing') end group :test, :development do - gem 'bullet', '~> 5.7.6' - gem 'factory_bot_rails', '~> 4.10.0' + gem 'bullet', '~> 6.1.0' + gem 'factory_bot_rails', '~> 5.2.0' gem 'oink', '~> 0.10.1' gem 'rspec-activemodel-mocks', '~> 1.1.0' - gem 'rspec-rails', '~> 3.7.2' + gem 'rspec-rails', '~> 4.0.1' gem 'pry', '~> 0.12.2' gem 'pry-byebug', '~> 3.7.0' - gem 'public_suffix', '~> 2.0.0', '< 3.0.0' end group :development do - gem 'annotate', '~> 2.7.0' + gem 'annotate', '< 3.1.1' gem 'capistrano', '~> 2.15.0', '< 3.0.0' gem 'net-ssh', ['~> 2.9.0', '< 3.0.0'] gem 'net-ssh-gateway', ['>= 1.1.0', '< 2.0.0'] - gem 'launchy', '~> 2.4.0' - gem 'listen', '~> 3.0.5' + gem 'launchy', '< 2.5.0' + gem 'listen', '>= 3.0.5', '< 3.3' gem 'web-console', '>= 3.3.0' - gem 'rubocop', '~> 0.63.1' + gem 'rubocop', '~> 0.81.0', require: false + gem 'rubocop-performance', '~> 1.5.2', require: false + gem 'rubocop-rails', require: false end diff --git a/Gemfile.lock b/Gemfile.lock index ca0bbc727b..678c2aae1d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,22 +1,3 @@ -GIT - remote: https://github.com/gbp/stripe-ruby-mock - revision: a9bc69707925dc7e7d091d02939791bd87e7ba98 - branch: develop - specs: - stripe-ruby-mock (2.5.6) - dante (>= 0.2.0) - multi_json (~> 1.0) - stripe (>= 2.0.3) - -GIT - remote: https://github.com/heapsource/active_model_otp.git - revision: 55d93a3979907d8382c83c9b0f3e94e3186167fc - ref: 55d93a3979 - specs: - active_model_otp (1.2.0) - activemodel - rotp - GIT remote: https://github.com/mysociety/ruby-msg.git revision: fae72e547299ab1f8b23239a79f5a1d353426b40 @@ -28,11 +9,21 @@ GIT GIT remote: https://github.com/mysociety/strip_attributes.git - revision: c1c14da7f33eda4cf5affaba01e6994155f7b7d4 - ref: c1c14da + revision: 62a5e1ee26501ad4c111b855cd73a5653091300b + branch: globalize3-rails5.2 + specs: + strip_attributes (1.11.0) + activemodel (>= 3.0, < 7.0) + +GIT + remote: https://github.com/stripe-ruby-mock/stripe-ruby-mock + revision: 2c925fd8cb568e3d0cfebffbe1babb490793f150 + ref: 2c925fd specs: - strip_attributes (1.8.0) - activemodel (>= 3.0, < 6.0) + stripe-ruby-mock (3.0.1) + dante (>= 0.2.0) + multi_json (~> 1.0) + stripe (> 5, < 6) GIT remote: https://github.com/technoweenie/acts_as_versioned.git @@ -49,150 +40,129 @@ PATH flipper (~> 0.10) flipper-active_record (~> 0.10) mime-types (< 3.0.0) - rails (~> 5.1.7) + rails (~> 5.2.4) GEM remote: https://rubygems.org/ specs: - actioncable (5.1.7) - actionpack (= 5.1.7) + actioncable (5.2.4.3) + actionpack (= 5.2.4.3) nio4r (~> 2.0) - websocket-driver (~> 0.6.1) - actionmailer (5.1.7) - actionpack (= 5.1.7) - actionview (= 5.1.7) - activejob (= 5.1.7) + websocket-driver (>= 0.6.1) + actionmailer (5.2.4.3) + actionpack (= 5.2.4.3) + actionview (= 5.2.4.3) + activejob (= 5.2.4.3) mail (~> 2.5, >= 2.5.4) rails-dom-testing (~> 2.0) - actionpack (5.1.7) - actionview (= 5.1.7) - activesupport (= 5.1.7) - rack (~> 2.0) + actionpack (5.2.4.3) + actionview (= 5.2.4.3) + activesupport (= 5.2.4.3) + rack (~> 2.0, >= 2.0.8) rack-test (>= 0.6.3) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.0.2) - actionview (5.1.7) - activesupport (= 5.1.7) + actionview (5.2.4.3) + activesupport (= 5.2.4.3) builder (~> 3.1) erubi (~> 1.4) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.0.3) - activejob (5.1.7) - activesupport (= 5.1.7) + active_model_otp (2.0.1) + activemodel + rotp (~> 5.0.0) + activejob (5.2.4.3) + activesupport (= 5.2.4.3) globalid (>= 0.3.6) - activemodel (5.1.7) - activesupport (= 5.1.7) - activerecord (5.1.7) - activemodel (= 5.1.7) - activesupport (= 5.1.7) - arel (~> 8.0) - activesupport (5.1.7) + activemodel (5.2.4.3) + activesupport (= 5.2.4.3) + activerecord (5.2.4.3) + activemodel (= 5.2.4.3) + activesupport (= 5.2.4.3) + arel (>= 9.0) + activestorage (5.2.4.3) + actionpack (= 5.2.4.3) + activerecord (= 5.2.4.3) + marcel (~> 0.3.1) + activesupport (5.2.4.3) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 0.7, < 2) minitest (~> 5.1) tzinfo (~> 1.1) - addressable (2.6.0) - public_suffix (>= 2.0.2, < 4.0) - annotate (2.7.2) - activerecord (>= 3.2, < 6.0) - rake (>= 10.4, < 13.0) - arel (8.0.0) - ast (2.4.0) + addressable (2.7.0) + public_suffix (>= 2.0.2, < 5.0) + annotate (3.1.0) + activerecord (>= 3.2, < 7.0) + rake (>= 10.4, < 14.0) + arel (9.0.0) + ast (2.4.1) bcrypt (3.1.13) bindex (0.7.0) bootstrap-sass (2.3.2.2) sass (~> 3.2) - builder (3.2.3) - bullet (5.7.6) + builder (3.2.4) + bullet (6.1.0) activesupport (>= 3.0.0) - uniform_notifier (~> 1.11.0) + uniform_notifier (~> 1.11) byebug (11.0.1) - cancancan (1.17.0) + cancancan (3.1.0) capistrano (2.15.9) highline net-scp (>= 1.0.0) net-sftp (>= 2.0.0) net-ssh (>= 2.0.14) net-ssh-gateway (>= 1.1.0) - capybara (3.5.1) + capybara (3.15.1) addressable mini_mime (>= 0.1.3) nokogiri (~> 1.8) rack (>= 1.6.0) rack-test (>= 0.6.3) - xpath (~> 3.1) + regexp_parser (~> 1.2) + xpath (~> 3.2) charlock_holmes (0.7.7) - chronic (0.10.2) - chunky_png (1.3.10) coderay (1.1.2) - coffee-rails (4.2.2) - coffee-script (>= 2.2.0) - railties (>= 4.0.0) - coffee-script (2.4.1) - coffee-script-source - execjs - coffee-script-source (1.12.2) - compass (1.0.3) - chunky_png (~> 1.2) - compass-core (~> 1.0.2) - compass-import-once (~> 1.0.5) - rb-fsevent (>= 0.9.3) - rb-inotify (>= 0.9) - sass (>= 3.3.13, < 3.5) - compass-core (1.0.3) - multi_json (~> 1.0) - sass (>= 3.3.0, < 3.5) - compass-import-once (1.0.5) - sass (>= 3.2, < 3.5) - compass-rails (3.1.0) - compass (~> 1.0.0) - sass-rails (< 5.1) - sprockets (< 4.0) - concurrent-ruby (1.1.5) - coveralls (0.8.22) + concurrent-ruby (1.1.6) + coveralls (0.8.23) json (>= 1.8, < 3) simplecov (~> 0.16.1) term-ansicolor (~> 1.3) - thor (~> 0.19.4) + thor (>= 0.19.4, < 2.0) tins (~> 1.6) crack (0.4.3) safe_yaml (~> 1.0.0) - crass (1.0.5) - daemons (1.2.4) - dalli (2.7.8) + crass (1.0.6) + daemons (1.3.1) + dalli (2.7.10) dante (0.2.0) - delorean (2.1.0) - chronic - diff-lcs (1.3) - docile (1.3.1) + diff-lcs (1.4.2) + docile (1.3.2) dynamic_form (1.1.4) erubi (1.9.0) eventmachine (1.2.7) - exception_notification (4.1.1) - actionmailer (>= 3.0.4) - activesupport (>= 3.0.4) + exception_notification (4.4.3) + actionmailer (>= 4.0, < 7) + activesupport (>= 4.0, < 7) execjs (2.7.0) - factory_bot (4.10.0) - activesupport (>= 3.0.0) - factory_bot_rails (4.10.0) - factory_bot (~> 4.10.0) - railties (>= 3.0.0) + factory_bot (5.2.0) + activesupport (>= 4.2.0) + factory_bot_rails (5.2.0) + factory_bot (~> 5.2.0) + railties (>= 4.2.0) fancybox-rails (0.3.1) railties (>= 3.1.0) - faraday (0.15.4) - multipart-post (>= 1.2, < 3) - fast_gettext (1.1.2) - ffi (1.9.25) - flipper (0.10.2) - flipper-active_record (0.10.2) - activerecord (>= 3.2, < 6) - flipper (~> 0.10.2) - gender_detector (1.0.0) - gettext (2.3.9) - locale - text - gettext_i18n_rails (0.10.1) - fast_gettext (>= 0.4.8) + fast_gettext (2.0.2) + ffi (1.13.1) + flipper (0.17.2) + flipper-active_record (0.17.2) + activerecord (>= 4.2, < 7) + flipper (~> 0.17.2) + gender_detector (2.0.0) + gettext (3.2.9) + locale (>= 2.0.5) + text (>= 1.3.0) + gettext_i18n_rails (1.8.1) + fast_gettext (>= 0.9.0) globalid (0.4.2) activesupport (>= 4.2.0) globalize (5.2.0) @@ -200,46 +170,51 @@ GEM activerecord (>= 4.2, < 5.3) request_store (~> 1.0) gnuplot (2.6.2) - hashdiff (0.3.8) + hashdiff (1.0.1) highline (2.0.0) hodel_3000_compliant_logger (0.1.1) - holidays (4.7.0) + holidays (7.1.0) htmlentities (4.3.4) - i18n (0.9.1) + i18n (1.8.3) concurrent-ruby (~> 1.0) - icalendar (2.4.1) + icalendar (2.5.3) + ice_cube (~> 0.16) + ice_cube (0.16.3) iso_country_codes (0.7.8) - jaro_winkler (1.5.2) - jquery-rails (4.3.3) + jaro_winkler (1.5.4) + jquery-rails (4.4.0) rails-dom-testing (>= 1, < 3) railties (>= 4.2.0) thor (>= 0.14, < 2.0) jquery-ui-rails (6.0.1) railties (>= 3.2.16) - json (1.8.6) + json (2.3.1) launchy (2.4.3) addressable (~> 2.3) libv8 (3.16.14.19) - listen (3.0.8) - rb-fsevent (~> 0.9, >= 0.9.4) - rb-inotify (~> 0.9, >= 0.9.7) - locale (2.0.9) - loofah (2.3.1) + listen (3.2.1) + rb-fsevent (~> 0.10, >= 0.10.3) + rb-inotify (~> 0.9, >= 0.9.10) + locale (2.1.3) + loofah (2.6.0) crass (~> 1.0.2) nokogiri (>= 1.5.9) - mahoro (0.4) + mahoro (0.5) mail (2.6.6) mime-types (>= 1.16, < 4) + marcel (0.3.3) + mimemagic (~> 0.3.2) maxmind-db (1.0.0) method_source (0.9.2) mime-types (2.99.3) - mini_mime (1.0.1) + mimemagic (0.3.5) + mini_magick (4.10.1) + mini_mime (1.0.2) mini_portile2 (2.4.0) - minitest (5.13.0) - money (6.13.6) + minitest (5.14.1) + money (6.13.8) i18n (>= 0.6.4, <= 2) multi_json (1.13.1) - multipart-post (2.1.1) net-scp (1.2.1) net-ssh (>= 2.6.5) net-sftp (2.1.2) @@ -247,49 +222,48 @@ GEM net-ssh (2.9.4) net-ssh-gateway (1.3.0) net-ssh (>= 2.6.5) - newrelic_rpm (6.8.0.360) nio4r (2.5.2) - nokogiri (1.10.7) + nokogiri (1.10.10) mini_portile2 (~> 2.4.0) oink (0.10.1) activerecord hodel_3000_compliant_logger open4 (1.3.4) - parallel (1.13.0) - parser (2.6.0.0) - ast (~> 2.4.0) - pg (0.20.0) - powerpack (0.1.2) + parallel (1.19.2) + parser (2.7.1.4) + ast (~> 2.4.1) + pg (1.2.3) pry (0.12.2) coderay (~> 1.1.0) method_source (~> 0.9.0) pry-byebug (3.7.0) byebug (~> 11.0) pry (~> 0.10) - public_suffix (2.0.5) - rack (2.0.8) + public_suffix (4.0.5) + rack (2.2.3) rack-ssl (1.4.1) rack rack-test (1.1.0) rack (>= 1.0, < 3) - rack-utf8_sanitizer (1.3.2) + rack-utf8_sanitizer (1.7.0) rack (>= 1.0, < 3.0) - rails (5.1.7) - actioncable (= 5.1.7) - actionmailer (= 5.1.7) - actionpack (= 5.1.7) - actionview (= 5.1.7) - activejob (= 5.1.7) - activemodel (= 5.1.7) - activerecord (= 5.1.7) - activesupport (= 5.1.7) + rails (5.2.4.3) + actioncable (= 5.2.4.3) + actionmailer (= 5.2.4.3) + actionpack (= 5.2.4.3) + actionview (= 5.2.4.3) + activejob (= 5.2.4.3) + activemodel (= 5.2.4.3) + activerecord (= 5.2.4.3) + activestorage (= 5.2.4.3) + activesupport (= 5.2.4.3) bundler (>= 1.3.0) - railties (= 5.1.7) + railties (= 5.2.4.3) sprockets-rails (>= 2.0.0) - rails-controller-testing (1.0.2) - actionpack (~> 5.x, >= 5.0.1) - actionview (~> 5.x, >= 5.0.1) - activesupport (~> 5.x) + rails-controller-testing (1.0.5) + actionpack (>= 5.0.1.rc1) + actionview (>= 5.0.1.rc1) + activesupport (>= 5.0.1.rc1) rails-dom-testing (2.0.3) activesupport (>= 4.2.0) nokogiri (>= 1.6) @@ -298,61 +272,69 @@ GEM rails-i18n (5.1.3) i18n (>= 0.7, < 2) railties (>= 5.0, < 6) - railties (5.1.7) - actionpack (= 5.1.7) - activesupport (= 5.1.7) + railties (5.2.4.3) + actionpack (= 5.2.4.3) + activesupport (= 5.2.4.3) method_source rake (>= 0.8.7) - thor (>= 0.18.1, < 2.0) + thor (>= 0.19.0, < 2.0) rainbow (3.0.0) - rake (12.3.3) - rb-fsevent (0.10.3) - rb-inotify (0.9.10) - ffi (>= 0.5.0, < 2) - recaptcha (4.9.0) + rake (13.0.1) + rb-fsevent (0.10.4) + rb-inotify (0.10.1) + ffi (~> 1.0) + recaptcha (5.5.0) json ref (2.0.0) + regexp_parser (1.7.1) request_store (1.4.1) rack (>= 1.4) - rmagick (2.16.0) - rolify (5.2.0) - rotp (3.3.0) - routing-filter (0.6.2) - actionpack (>= 4.2, < 6) - activesupport (>= 4.2, < 6) + rexml (3.2.4) + rolify (5.3.0) + rotp (5.0.0) + addressable (~> 2.5) + routing-filter (0.6.3) + actionpack (>= 4.2) + activesupport (>= 4.2) rspec-activemodel-mocks (1.1.0) activemodel (>= 3.0) activesupport (>= 3.0) rspec-mocks (>= 2.99, < 4.0) - rspec-core (3.7.1) - rspec-support (~> 3.7.0) - rspec-expectations (3.7.0) + rspec-core (3.9.2) + rspec-support (~> 3.9.3) + rspec-expectations (3.9.2) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.7.0) - rspec-mocks (3.7.0) + rspec-support (~> 3.9.0) + rspec-mocks (3.9.1) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.7.0) - rspec-rails (3.7.2) - actionpack (>= 3.0) - activesupport (>= 3.0) - railties (>= 3.0) - rspec-core (~> 3.7.0) - rspec-expectations (~> 3.7.0) - rspec-mocks (~> 3.7.0) - rspec-support (~> 3.7.0) - rspec-support (3.7.1) - rubocop (0.63.1) + rspec-support (~> 3.9.0) + rspec-rails (4.0.1) + actionpack (>= 4.2) + activesupport (>= 4.2) + railties (>= 4.2) + rspec-core (~> 3.9) + rspec-expectations (~> 3.9) + rspec-mocks (~> 3.9) + rspec-support (~> 3.9) + rspec-support (3.9.3) + rubocop (0.81.0) jaro_winkler (~> 1.5.1) parallel (~> 1.10) - parser (>= 2.5, != 2.5.1.1) - powerpack (~> 0.1) + parser (>= 2.7.0.1) rainbow (>= 2.2.2, < 4.0) + rexml ruby-progressbar (~> 1.7) - unicode-display_width (~> 1.4.0) + unicode-display_width (>= 1.4.0, < 2.0) + rubocop-performance (1.5.2) + rubocop (>= 0.71.0) + rubocop-rails (2.5.2) + activesupport + rack (>= 1.1) + rubocop (>= 0.72.0) ruby-ole (1.2.12.1) - ruby-progressbar (1.10.0) + ruby-progressbar (1.10.1) rubyzip (1.3.0) - safe_yaml (1.0.4) + safe_yaml (1.0.5) sass (3.4.25) sass-rails (5.0.7) railties (>= 4.0.0, < 6) @@ -360,8 +342,7 @@ GEM sprockets (>= 2.8, < 4.0) sprockets-rails (>= 2.0, < 4.0) tilt (>= 1.1, < 3) - secure_headers (3.6.5) - useragent + secure_headers (6.3.1) simplecov (0.16.1) docile (~> 1.1) json (>= 1.8, < 3) @@ -375,137 +356,136 @@ GEM activesupport (>= 4.0) sprockets (>= 3.0.0) statistics2 (0.54) - stripe (3.29.0) - faraday (~> 0.10) + stripe (5.22.0) + sync (0.5.0) syslog_protocol (0.9.2) - term-ansicolor (1.6.0) + term-ansicolor (1.7.1) tins (~> 1.0) text (1.3.1) therubyracer (0.12.3) libv8 (~> 3.16.14.15) ref - thin (1.5.1) - daemons (>= 1.0.9) - eventmachine (>= 0.12.6) - rack (>= 1.0.0) - thor (0.19.4) + thin (1.7.2) + daemons (~> 1.0, >= 1.0.9) + eventmachine (~> 1.0, >= 1.0.4) + rack (>= 1, < 3) + thor (1.0.1) thread_safe (0.3.6) tilt (2.0.8) - tins (1.16.3) - tzinfo (1.2.5) + tins (1.25.0) + sync + tzinfo (1.2.7) thread_safe (~> 0.1) - uglifier (3.2.0) + uglifier (4.2.0) execjs (>= 0.3.0, < 3) unicode (0.4.4.4) - unicode-display_width (1.4.1) + unicode-display_width (1.7.0) unidecoder (1.1.2) - uniform_notifier (1.11.0) - useragent (0.16.10) + uniform_notifier (1.13.0) vpim (13.11.11) web-console (3.7.0) actionview (>= 5.0) activemodel (>= 5.0) bindex (>= 0.4.0) railties (>= 5.0) - webmock (3.5.1) + webmock (3.8.3) addressable (>= 2.3.6) crack (>= 0.3.2) - hashdiff - websocket-driver (0.6.5) + hashdiff (>= 0.4.0, < 2.0.0) + websocket-driver (0.7.2) websocket-extensions (>= 0.1.0) - websocket-extensions (0.1.4) - will_paginate (3.1.8) - xapian-full-alaveteli (1.2.21.1) + websocket-extensions (0.1.5) + will_paginate (3.3.0) + xapian-full-alaveteli (1.4.11.1) xml-simple (1.1.5) - xpath (3.1.0) + xpath (3.2.0) nokogiri (~> 1.8) + zip_tricks (5.3.1) PLATFORMS ruby DEPENDENCIES - active_model_otp! + active_model_otp acts_as_versioned! alaveteli_features! - annotate (~> 2.7.0) + annotate (< 3.1.1) bcrypt (~> 3.1.13) bootstrap-sass (~> 2.3.2.2) - bullet (~> 5.7.6) - cancancan (~> 1.17.0, < 2.0.0) + bullet (~> 6.1.0) + cancancan (~> 3.1.0) capistrano (~> 2.15.0, < 3.0.0) - capybara (~> 3.5.0) + capybara (~> 3.15.1) charlock_holmes (~> 0.7.7) - coffee-rails (~> 4.2.0) - compass-rails (~> 3.1.0) - coveralls (~> 0.8.0) + coveralls (~> 0.8.23) dalli (~> 2.7.0) - delorean (~> 2.1.0) dynamic_form (~> 1.1.0) - exception_notification (~> 4.1.0, < 4.1.2) - factory_bot_rails (~> 4.10.0) + exception_notification (~> 4.4.3) + factory_bot_rails (~> 5.2.0) fancybox-rails (~> 0.3.0) - fast_gettext (< 1.2.0) - gender_detector (~> 1.0.0) - gettext (~> 2.3.0) - gettext_i18n_rails (~> 0.10.1) + fast_gettext (< 2.0.3) + gender_detector (~> 2.0.0) + gettext (< 3.3.0) + gettext_i18n_rails (~> 1.8.1) globalize (~> 5.2.0) gnuplot (~> 2.6.0) - holidays (~> 4.7.0, < 5.0.0) + holidays (~> 7.1.0) htmlentities (~> 4.3.0) - i18n (~> 0.9.0, < 0.9.3) - icalendar (~> 2.4.0) + i18n (~> 1.8.3) + icalendar (~> 2.5.3) iso_country_codes (~> 0.7.8) - jquery-rails (~> 4.3.3) + jquery-rails (~> 4.4.0) jquery-ui-rails (~> 6.0.0) - json (~> 1.8.0, < 2.0.0) - launchy (~> 2.4.0) - listen (~> 3.0.5) - locale (~> 2.0.0, < 2.1.0) - mahoro (~> 0.4) + json (~> 2.3.1) + launchy (< 2.5.0) + listen (>= 3.0.5, < 3.3) + locale (~> 2.1.3) + mahoro (~> 0.5) mail (~> 2.6.6) maxmind-db (~> 1.0.0) mime-types (< 3.0.0) - money (~> 6.13.6) + mini_magick (~> 4.10.0) + money (~> 6.13.8) net-ssh (~> 2.9.0, < 3.0.0) net-ssh-gateway (>= 1.1.0, < 2.0.0) - newrelic_rpm - nokogiri (~> 1.10.7) + nokogiri (~> 1.10.10) oink (~> 0.10.1) open4 (~> 1.3.0) - pg (~> 0.20.0) + pg (~> 1.2.3) pry (~> 0.12.2) pry-byebug (~> 3.7.0) - public_suffix (~> 2.0.0, < 3.0.0) - rack (~> 2.0.8) + rack (~> 2.2.3) rack-ssl (~> 1.4.0) - rack-utf8_sanitizer (~> 1.3.0) - rails (= 5.1.7) + rack-utf8_sanitizer (~> 1.7.0) + rails (~> 5.2.4) rails-controller-testing rails-i18n (~> 5.1.0) - recaptcha (~> 4.9.0, < 4.10.0) - rmagick (~> 2.16.0) - rolify (~> 5.2.0) + recaptcha (~> 5.5.0) + rolify (~> 5.3.0) routing-filter (~> 0.6.2) rspec-activemodel-mocks (~> 1.1.0) - rspec-rails (~> 3.7.2) - rubocop (~> 0.63.1) + rspec-rails (~> 4.0.1) + rubocop (~> 0.81.0) + rubocop-performance (~> 1.5.2) + rubocop-rails ruby-msg (~> 1.5.0)! rubyzip (~> 1.3.0, < 2.0.0) sass-rails (~> 5.0.7) - secure_headers (~> 3.6.0) + secure_headers (~> 6.3.1) statistics2 (~> 0.54) strip_attributes! - stripe (~> 3.29.0) + stripe (~> 5.22.0) stripe-ruby-mock! syslog_protocol (~> 0.9.0) therubyracer (~> 0.12.0) - thin (~> 1.5.0, < 1.6.0) - uglifier (~> 3.2.0) + thin (~> 1.7.2) + uglifier (~> 4.2.0) unicode (~> 0.4.4) unidecoder (~> 1.1.0) vpim (~> 13.11.11) web-console (>= 3.3.0) - webmock (~> 3.5.1) - will_paginate (~> 3.1.8) - xapian-full-alaveteli (~> 1.2.21.1) + webmock (~> 3.8.3) + will_paginate (~> 3.3.0) + xapian-full-alaveteli (~> 1.4.11.1) xml-simple (~> 1.1.0) + zip_tricks (~> 5.3.1) diff --git a/Gemfile.rails_next.lock b/Gemfile.rails_next.lock index 0ad0663c5a..6864ae8b59 100644 --- a/Gemfile.rails_next.lock +++ b/Gemfile.rails_next.lock @@ -1,22 +1,3 @@ -GIT - remote: https://github.com/gbp/stripe-ruby-mock - revision: a9bc69707925dc7e7d091d02939791bd87e7ba98 - branch: develop - specs: - stripe-ruby-mock (2.5.6) - dante (>= 0.2.0) - multi_json (~> 1.0) - stripe (>= 2.0.3) - -GIT - remote: https://github.com/heapsource/active_model_otp.git - revision: 55d93a3979907d8382c83c9b0f3e94e3186167fc - ref: 55d93a3979 - specs: - active_model_otp (1.2.0) - activemodel - rotp - GIT remote: https://github.com/mysociety/ruby-msg.git revision: fae72e547299ab1f8b23239a79f5a1d353426b40 @@ -28,11 +9,21 @@ GIT GIT remote: https://github.com/mysociety/strip_attributes.git - revision: c1c14da7f33eda4cf5affaba01e6994155f7b7d4 - ref: c1c14da + revision: 62a5e1ee26501ad4c111b855cd73a5653091300b + branch: globalize3-rails5.2 + specs: + strip_attributes (1.11.0) + activemodel (>= 3.0, < 7.0) + +GIT + remote: https://github.com/stripe-ruby-mock/stripe-ruby-mock + revision: 2c925fd8cb568e3d0cfebffbe1babb490793f150 + ref: 2c925fd specs: - strip_attributes (1.8.0) - activemodel (>= 3.0, < 6.0) + stripe-ruby-mock (3.0.1) + dante (>= 0.2.0) + multi_json (~> 1.0) + stripe (> 5, < 6) GIT remote: https://github.com/technoweenie/acts_as_versioned.git @@ -49,204 +40,194 @@ PATH flipper (~> 0.10) flipper-active_record (~> 0.10) mime-types (< 3.0.0) - rails (~> 5.2.3) + rails (~> 6.0.3) GEM remote: https://rubygems.org/ specs: - actioncable (5.2.3) - actionpack (= 5.2.3) + actioncable (6.0.3.2) + actionpack (= 6.0.3.2) nio4r (~> 2.0) websocket-driver (>= 0.6.1) - actionmailer (5.2.3) - actionpack (= 5.2.3) - actionview (= 5.2.3) - activejob (= 5.2.3) + actionmailbox (6.0.3.2) + actionpack (= 6.0.3.2) + activejob (= 6.0.3.2) + activerecord (= 6.0.3.2) + activestorage (= 6.0.3.2) + activesupport (= 6.0.3.2) + mail (>= 2.7.1) + actionmailer (6.0.3.2) + actionpack (= 6.0.3.2) + actionview (= 6.0.3.2) + activejob (= 6.0.3.2) mail (~> 2.5, >= 2.5.4) rails-dom-testing (~> 2.0) - actionpack (5.2.3) - actionview (= 5.2.3) - activesupport (= 5.2.3) - rack (~> 2.0) + actionpack (6.0.3.2) + actionview (= 6.0.3.2) + activesupport (= 6.0.3.2) + rack (~> 2.0, >= 2.0.8) rack-test (>= 0.6.3) rails-dom-testing (~> 2.0) - rails-html-sanitizer (~> 1.0, >= 1.0.2) - actionview (5.2.3) - activesupport (= 5.2.3) + rails-html-sanitizer (~> 1.0, >= 1.2.0) + actiontext (6.0.3.2) + actionpack (= 6.0.3.2) + activerecord (= 6.0.3.2) + activestorage (= 6.0.3.2) + activesupport (= 6.0.3.2) + nokogiri (>= 1.8.5) + actionview (6.0.3.2) + activesupport (= 6.0.3.2) builder (~> 3.1) erubi (~> 1.4) rails-dom-testing (~> 2.0) - rails-html-sanitizer (~> 1.0, >= 1.0.3) - activejob (5.2.3) - activesupport (= 5.2.3) + rails-html-sanitizer (~> 1.1, >= 1.2.0) + active_model_otp (2.0.1) + activemodel + rotp (~> 5.0.0) + activejob (6.0.3.2) + activesupport (= 6.0.3.2) globalid (>= 0.3.6) - activemodel (5.2.3) - activesupport (= 5.2.3) - activerecord (5.2.3) - activemodel (= 5.2.3) - activesupport (= 5.2.3) - arel (>= 9.0) - activestorage (5.2.3) - actionpack (= 5.2.3) - activerecord (= 5.2.3) + activemodel (6.0.3.2) + activesupport (= 6.0.3.2) + activerecord (6.0.3.2) + activemodel (= 6.0.3.2) + activesupport (= 6.0.3.2) + activestorage (6.0.3.2) + actionpack (= 6.0.3.2) + activejob (= 6.0.3.2) + activerecord (= 6.0.3.2) marcel (~> 0.3.1) - activesupport (5.2.3) + activesupport (6.0.3.2) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 0.7, < 2) minitest (~> 5.1) tzinfo (~> 1.1) - addressable (2.6.0) - public_suffix (>= 2.0.2, < 4.0) - annotate (2.7.4) - activerecord (>= 3.2, < 6.0) - rake (>= 10.4, < 13.0) - arel (9.0.0) - ast (2.4.0) + zeitwerk (~> 2.2, >= 2.2.2) + addressable (2.7.0) + public_suffix (>= 2.0.2, < 5.0) + annotate (3.1.0) + activerecord (>= 3.2, < 7.0) + rake (>= 10.4, < 14.0) + ast (2.4.1) bcrypt (3.1.13) bindex (0.7.0) bootstrap-sass (2.3.2.2) sass (~> 3.2) - builder (3.2.3) - bullet (5.7.6) + builder (3.2.4) + bullet (6.1.0) activesupport (>= 3.0.0) - uniform_notifier (~> 1.11.0) + uniform_notifier (~> 1.11) byebug (11.0.1) - cancancan (1.17.0) + cancancan (3.1.0) capistrano (2.15.9) highline net-scp (>= 1.0.0) net-sftp (>= 2.0.0) net-ssh (>= 2.0.14) net-ssh-gateway (>= 1.1.0) - capybara (3.5.1) + capybara (3.15.1) addressable mini_mime (>= 0.1.3) nokogiri (~> 1.8) rack (>= 1.6.0) rack-test (>= 0.6.3) - xpath (~> 3.1) + regexp_parser (~> 1.2) + xpath (~> 3.2) charlock_holmes (0.7.7) - chronic (0.10.2) - chunky_png (1.3.10) coderay (1.1.2) - coffee-rails (4.2.2) - coffee-script (>= 2.2.0) - railties (>= 4.0.0) - coffee-script (2.4.1) - coffee-script-source - execjs - coffee-script-source (1.12.2) - compass (1.0.3) - chunky_png (~> 1.2) - compass-core (~> 1.0.2) - compass-import-once (~> 1.0.5) - rb-fsevent (>= 0.9.3) - rb-inotify (>= 0.9) - sass (>= 3.3.13, < 3.5) - compass-core (1.0.3) - multi_json (~> 1.0) - sass (>= 3.3.0, < 3.5) - compass-import-once (1.0.5) - sass (>= 3.2, < 3.5) - compass-rails (3.1.0) - compass (~> 1.0.0) - sass-rails (< 5.1) - sprockets (< 4.0) - concurrent-ruby (1.1.5) - coveralls (0.8.22) + concurrent-ruby (1.1.6) + coveralls (0.8.23) json (>= 1.8, < 3) simplecov (~> 0.16.1) term-ansicolor (~> 1.3) - thor (~> 0.19.4) + thor (>= 0.19.4, < 2.0) tins (~> 1.6) crack (0.4.3) safe_yaml (~> 1.0.0) - crass (1.0.5) - daemons (1.2.6) - dalli (2.7.8) + crass (1.0.6) + daemons (1.3.1) + dalli (2.7.10) dante (0.2.0) - delorean (2.1.0) - chronic - diff-lcs (1.3) - docile (1.3.1) + diff-lcs (1.4.2) + docile (1.3.2) dynamic_form (1.1.4) erubi (1.9.0) eventmachine (1.2.7) - exception_notification (4.1.1) - actionmailer (>= 3.0.4) - activesupport (>= 3.0.4) + exception_notification (4.4.3) + actionmailer (>= 4.0, < 7) + activesupport (>= 4.0, < 7) execjs (2.7.0) - factory_bot (4.10.0) - activesupport (>= 3.0.0) - factory_bot_rails (4.10.0) - factory_bot (~> 4.10.0) - railties (>= 3.0.0) + factory_bot (5.2.0) + activesupport (>= 4.2.0) + factory_bot_rails (5.2.0) + factory_bot (~> 5.2.0) + railties (>= 4.2.0) fancybox-rails (0.3.1) railties (>= 3.1.0) - faraday (0.15.4) - multipart-post (>= 1.2, < 3) - fast_gettext (1.1.2) - ffi (1.9.25) - flipper (0.10.2) - flipper-active_record (0.10.2) - activerecord (>= 3.2, < 6) - flipper (~> 0.10.2) - gender_detector (1.0.0) - gettext (2.3.9) - locale - text - gettext_i18n_rails (0.10.1) - fast_gettext (>= 0.4.8) + fast_gettext (2.0.2) + ffi (1.13.0) + flipper (0.17.2) + flipper-active_record (0.17.2) + activerecord (>= 4.2, < 7) + flipper (~> 0.17.2) + gender_detector (2.0.0) + gettext (3.2.9) + locale (>= 2.0.5) + text (>= 1.3.0) + gettext_i18n_rails (1.8.1) + fast_gettext (>= 0.9.0) globalid (0.4.2) activesupport (>= 4.2.0) - globalize (5.2.0) - activemodel (>= 4.2, < 5.3) - activerecord (>= 4.2, < 5.3) + globalize (5.3.0) + activemodel (>= 4.2, < 6.1) + activerecord (>= 4.2, < 6.1) request_store (~> 1.0) gnuplot (2.6.2) - hashdiff (0.3.8) + hashdiff (1.0.1) highline (2.0.0) hodel_3000_compliant_logger (0.1.1) - holidays (4.7.0) + holidays (7.1.0) htmlentities (4.3.4) - i18n (0.9.1) + i18n (1.8.3) concurrent-ruby (~> 1.0) - icalendar (2.4.1) + icalendar (2.5.3) + ice_cube (~> 0.16) + ice_cube (0.16.3) iso_country_codes (0.7.8) - jaro_winkler (1.5.2) - jquery-rails (4.3.3) + jaro_winkler (1.5.4) + jquery-rails (4.4.0) rails-dom-testing (>= 1, < 3) railties (>= 4.2.0) thor (>= 0.14, < 2.0) jquery-ui-rails (6.0.1) railties (>= 3.2.16) - json (1.8.6) + json (2.3.1) launchy (2.4.3) addressable (~> 2.3) libv8 (3.16.14.19) - listen (3.0.8) - rb-fsevent (~> 0.9, >= 0.9.4) - rb-inotify (~> 0.9, >= 0.9.7) - locale (2.0.9) - loofah (2.3.1) + listen (3.2.1) + rb-fsevent (~> 0.10, >= 0.10.3) + rb-inotify (~> 0.9, >= 0.9.10) + locale (2.1.3) + loofah (2.6.0) crass (~> 1.0.2) nokogiri (>= 1.5.9) - mahoro (0.4) - mail (2.6.6) - mime-types (>= 1.16, < 4) + mahoro (0.5) + mail (2.7.1) + mini_mime (>= 0.1.1) marcel (0.3.3) mimemagic (~> 0.3.2) maxmind-db (1.0.0) method_source (0.9.2) mime-types (2.99.3) - mimemagic (0.3.3) - mini_mime (1.0.1) + mimemagic (0.3.5) + mini_magick (4.10.1) + mini_mime (1.0.2) mini_portile2 (2.4.0) - minitest (5.13.0) - money (6.13.6) + minitest (5.14.1) + money (6.13.8) i18n (>= 0.6.4, <= 2) multi_json (1.13.1) - multipart-post (2.1.1) net-scp (1.2.1) net-ssh (>= 2.6.5) net-sftp (2.1.2) @@ -254,122 +235,129 @@ GEM net-ssh (2.9.4) net-ssh-gateway (1.3.0) net-ssh (>= 2.6.5) - newrelic_rpm (6.8.0.360) nio4r (2.5.2) - nokogiri (1.10.7) + nokogiri (1.10.10) mini_portile2 (~> 2.4.0) oink (0.10.1) activerecord hodel_3000_compliant_logger open4 (1.3.4) - parallel (1.13.0) - parser (2.6.0.0) - ast (~> 2.4.0) - pg (0.20.0) - powerpack (0.1.2) + parallel (1.19.2) + parser (2.7.1.4) + ast (~> 2.4.1) + pg (1.2.3) pry (0.12.2) coderay (~> 1.1.0) method_source (~> 0.9.0) pry-byebug (3.7.0) byebug (~> 11.0) pry (~> 0.10) - public_suffix (2.0.5) - rack (2.0.8) + public_suffix (4.0.5) + rack (2.2.3) rack-ssl (1.4.1) rack rack-test (1.1.0) rack (>= 1.0, < 3) - rack-utf8_sanitizer (1.3.2) + rack-utf8_sanitizer (1.7.0) rack (>= 1.0, < 3.0) - rails (5.2.3) - actioncable (= 5.2.3) - actionmailer (= 5.2.3) - actionpack (= 5.2.3) - actionview (= 5.2.3) - activejob (= 5.2.3) - activemodel (= 5.2.3) - activerecord (= 5.2.3) - activestorage (= 5.2.3) - activesupport (= 5.2.3) + rails (6.0.3.2) + actioncable (= 6.0.3.2) + actionmailbox (= 6.0.3.2) + actionmailer (= 6.0.3.2) + actionpack (= 6.0.3.2) + actiontext (= 6.0.3.2) + actionview (= 6.0.3.2) + activejob (= 6.0.3.2) + activemodel (= 6.0.3.2) + activerecord (= 6.0.3.2) + activestorage (= 6.0.3.2) + activesupport (= 6.0.3.2) bundler (>= 1.3.0) - railties (= 5.2.3) + railties (= 6.0.3.2) sprockets-rails (>= 2.0.0) - rails-controller-testing (1.0.2) - actionpack (~> 5.x, >= 5.0.1) - actionview (~> 5.x, >= 5.0.1) - activesupport (~> 5.x) + rails-controller-testing (1.0.5) + actionpack (>= 5.0.1.rc1) + actionview (>= 5.0.1.rc1) + activesupport (>= 5.0.1.rc1) rails-dom-testing (2.0.3) activesupport (>= 4.2.0) nokogiri (>= 1.6) rails-html-sanitizer (1.3.0) loofah (~> 2.3) - rails-i18n (5.1.3) + rails-i18n (6.0.0) i18n (>= 0.7, < 2) - railties (>= 5.0, < 6) - railties (5.2.3) - actionpack (= 5.2.3) - activesupport (= 5.2.3) + railties (>= 6.0.0, < 7) + railties (6.0.3.2) + actionpack (= 6.0.3.2) + activesupport (= 6.0.3.2) method_source rake (>= 0.8.7) - thor (>= 0.19.0, < 2.0) + thor (>= 0.20.3, < 2.0) rainbow (3.0.0) - rake (12.3.3) - rb-fsevent (0.10.3) - rb-inotify (0.9.10) - ffi (>= 0.5.0, < 2) - recaptcha (4.9.0) + rake (13.0.1) + rb-fsevent (0.10.4) + rb-inotify (0.10.1) + ffi (~> 1.0) + recaptcha (5.5.0) json ref (2.0.0) - request_store (1.4.1) + regexp_parser (1.7.1) + request_store (1.5.0) rack (>= 1.4) - rmagick (2.16.0) - rolify (5.2.0) - rotp (3.3.1) - routing-filter (0.6.2) - actionpack (>= 4.2, < 6) - activesupport (>= 4.2, < 6) + rexml (3.2.4) + rolify (5.3.0) + rotp (5.0.0) + addressable (~> 2.5) + routing-filter (0.6.3) + actionpack (>= 4.2) + activesupport (>= 4.2) rspec-activemodel-mocks (1.1.0) activemodel (>= 3.0) activesupport (>= 3.0) rspec-mocks (>= 2.99, < 4.0) - rspec-core (3.7.1) - rspec-support (~> 3.7.0) - rspec-expectations (3.7.0) + rspec-core (3.9.2) + rspec-support (~> 3.9.3) + rspec-expectations (3.9.2) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.7.0) - rspec-mocks (3.7.0) + rspec-support (~> 3.9.0) + rspec-mocks (3.9.1) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.7.0) - rspec-rails (3.7.2) - actionpack (>= 3.0) - activesupport (>= 3.0) - railties (>= 3.0) - rspec-core (~> 3.7.0) - rspec-expectations (~> 3.7.0) - rspec-mocks (~> 3.7.0) - rspec-support (~> 3.7.0) - rspec-support (3.7.1) - rubocop (0.63.1) + rspec-support (~> 3.9.0) + rspec-rails (4.0.1) + actionpack (>= 4.2) + activesupport (>= 4.2) + railties (>= 4.2) + rspec-core (~> 3.9) + rspec-expectations (~> 3.9) + rspec-mocks (~> 3.9) + rspec-support (~> 3.9) + rspec-support (3.9.3) + rubocop (0.81.0) jaro_winkler (~> 1.5.1) parallel (~> 1.10) - parser (>= 2.5, != 2.5.1.1) - powerpack (~> 0.1) + parser (>= 2.7.0.1) rainbow (>= 2.2.2, < 4.0) + rexml ruby-progressbar (~> 1.7) - unicode-display_width (~> 1.4.0) + unicode-display_width (>= 1.4.0, < 2.0) + rubocop-performance (1.5.2) + rubocop (>= 0.71.0) + rubocop-rails (2.5.2) + activesupport + rack (>= 1.1) + rubocop (>= 0.72.0) ruby-ole (1.2.12.1) - ruby-progressbar (1.10.0) + ruby-progressbar (1.10.1) rubyzip (1.3.0) - safe_yaml (1.0.4) + safe_yaml (1.0.5) sass (3.4.25) - sass-rails (5.0.7) - railties (>= 4.0.0, < 6) + sass-rails (5.0.8) + railties (>= 5.2.0) sass (~> 3.1) sprockets (>= 2.8, < 4.0) sprockets-rails (>= 2.0, < 4.0) tilt (>= 1.1, < 3) - secure_headers (3.6.7) - useragent + secure_headers (6.3.1) simplecov (0.16.1) docile (~> 1.1) json (>= 1.8, < 3) @@ -383,137 +371,137 @@ GEM activesupport (>= 4.0) sprockets (>= 3.0.0) statistics2 (0.54) - stripe (3.29.0) - faraday (~> 0.10) + stripe (5.22.0) + sync (0.5.0) syslog_protocol (0.9.2) - term-ansicolor (1.6.0) + term-ansicolor (1.7.1) tins (~> 1.0) text (1.3.1) therubyracer (0.12.3) libv8 (~> 3.16.14.15) ref - thin (1.5.1) - daemons (>= 1.0.9) - eventmachine (>= 0.12.6) - rack (>= 1.0.0) - thor (0.19.4) + thin (1.7.2) + daemons (~> 1.0, >= 1.0.9) + eventmachine (~> 1.0, >= 1.0.4) + rack (>= 1, < 3) + thor (1.0.1) thread_safe (0.3.6) - tilt (2.0.8) - tins (1.16.3) - tzinfo (1.2.5) + tilt (2.0.10) + tins (1.25.0) + sync + tzinfo (1.2.7) thread_safe (~> 0.1) - uglifier (3.2.0) + uglifier (4.2.0) execjs (>= 0.3.0, < 3) unicode (0.4.4.4) - unicode-display_width (1.4.1) + unicode-display_width (1.7.0) unidecoder (1.1.2) - uniform_notifier (1.11.0) - useragent (0.16.10) + uniform_notifier (1.13.0) vpim (13.11.11) web-console (3.7.0) actionview (>= 5.0) activemodel (>= 5.0) bindex (>= 0.4.0) railties (>= 5.0) - webmock (3.5.1) + webmock (3.8.3) addressable (>= 2.3.6) crack (>= 0.3.2) - hashdiff - websocket-driver (0.7.1) + hashdiff (>= 0.4.0, < 2.0.0) + websocket-driver (0.7.2) websocket-extensions (>= 0.1.0) - websocket-extensions (0.1.4) - will_paginate (3.1.8) - xapian-full-alaveteli (1.2.21.1) + websocket-extensions (0.1.5) + will_paginate (3.3.0) + xapian-full-alaveteli (1.4.11.1) xml-simple (1.1.5) - xpath (3.1.0) + xpath (3.2.0) nokogiri (~> 1.8) + zeitwerk (2.3.0) + zip_tricks (5.3.1) PLATFORMS ruby DEPENDENCIES - active_model_otp! + active_model_otp acts_as_versioned! alaveteli_features! - annotate (~> 2.7.0) + annotate (< 3.1.1) bcrypt (~> 3.1.13) bootstrap-sass (~> 2.3.2.2) - bullet (~> 5.7.6) - cancancan (~> 1.17.0, < 2.0.0) + bullet (~> 6.1.0) + cancancan (~> 3.1.0) capistrano (~> 2.15.0, < 3.0.0) - capybara (~> 3.5.0) + capybara (~> 3.15.1) charlock_holmes (~> 0.7.7) - coffee-rails (~> 4.2.0) - compass-rails (~> 3.1.0) - coveralls (~> 0.8.0) + coveralls (~> 0.8.23) dalli (~> 2.7.0) - delorean (~> 2.1.0) dynamic_form (~> 1.1.0) - exception_notification (~> 4.1.0, < 4.1.2) - factory_bot_rails (~> 4.10.0) + exception_notification (~> 4.4.3) + factory_bot_rails (~> 5.2.0) fancybox-rails (~> 0.3.0) - fast_gettext (< 1.2.0) - gender_detector (~> 1.0.0) - gettext (~> 2.3.0) - gettext_i18n_rails (~> 0.10.1) - globalize (~> 5.2.0) + fast_gettext (< 2.0.3) + gender_detector (~> 2.0.0) + gettext (< 3.3.0) + gettext_i18n_rails (~> 1.8.1) + globalize (~> 5.3.0) gnuplot (~> 2.6.0) - holidays (~> 4.7.0, < 5.0.0) + holidays (~> 7.1.0) htmlentities (~> 4.3.0) - i18n (~> 0.9.0, < 0.9.3) - icalendar (~> 2.4.0) + i18n (~> 1.8.3) + icalendar (~> 2.5.3) iso_country_codes (~> 0.7.8) - jquery-rails (~> 4.3.3) + jquery-rails (~> 4.4.0) jquery-ui-rails (~> 6.0.0) - json (~> 1.8.0, < 2.0.0) - launchy (~> 2.4.0) - listen (~> 3.0.5) - locale (~> 2.0.0, < 2.1.0) - mahoro (~> 0.4) - mail (~> 2.6.6) + json (~> 2.3.1) + launchy (< 2.5.0) + listen (>= 3.0.5, < 3.3) + locale (~> 2.1.3) + mahoro (~> 0.5) + mail (~> 2.7.1) maxmind-db (~> 1.0.0) mime-types (< 3.0.0) - money (~> 6.13.6) + mini_magick (~> 4.10.0) + money (~> 6.13.8) net-ssh (~> 2.9.0, < 3.0.0) net-ssh-gateway (>= 1.1.0, < 2.0.0) - newrelic_rpm - nokogiri (~> 1.10.7) + nokogiri (~> 1.10.10) oink (~> 0.10.1) open4 (~> 1.3.0) - pg (~> 0.20.0) + pg (~> 1.2.3) pry (~> 0.12.2) pry-byebug (~> 3.7.0) - public_suffix (~> 2.0.0, < 3.0.0) - rack (~> 2.0.8) + rack (~> 2.2.3) rack-ssl (~> 1.4.0) - rack-utf8_sanitizer (~> 1.3.0) - rails (= 5.2.3) + rack-utf8_sanitizer (~> 1.7.0) + rails (~> 6.0.3) rails-controller-testing - rails-i18n (~> 5.1.0) - recaptcha (~> 4.9.0, < 4.10.0) - rmagick (~> 2.16.0) - rolify (~> 5.2.0) + rails-i18n (~> 6.0.0) + recaptcha (~> 5.5.0) + rolify (~> 5.3.0) routing-filter (~> 0.6.2) rspec-activemodel-mocks (~> 1.1.0) - rspec-rails (~> 3.7.2) - rubocop (~> 0.63.1) + rspec-rails (~> 4.0.1) + rubocop (~> 0.81.0) + rubocop-performance (~> 1.5.2) + rubocop-rails ruby-msg (~> 1.5.0)! rubyzip (~> 1.3.0, < 2.0.0) sass-rails (~> 5.0.7) - secure_headers (~> 3.6.0) + secure_headers (~> 6.3.1) statistics2 (~> 0.54) strip_attributes! - stripe (~> 3.29.0) + stripe (~> 5.22.0) stripe-ruby-mock! syslog_protocol (~> 0.9.0) therubyracer (~> 0.12.0) - thin (~> 1.5.0, < 1.6.0) - uglifier (~> 3.2.0) + thin (~> 1.7.2) + uglifier (~> 4.2.0) unicode (~> 0.4.4) unidecoder (~> 1.1.0) vpim (~> 13.11.11) web-console (>= 3.3.0) - webmock (~> 3.5.1) - will_paginate (~> 3.1.8) - xapian-full-alaveteli (~> 1.2.21.1) + webmock (~> 3.8.3) + will_paginate (~> 3.3.0) + xapian-full-alaveteli (~> 1.4.11.1) xml-simple (~> 1.1.0) + zip_tricks (~> 5.3.1) diff --git a/Vagrantfile b/Vagrantfile index e7be99de7e..6e06af60a2 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -84,6 +84,7 @@ end DEFAULTS = { 'fqdn' => 'alaveteli.10.10.10.30.nip.io', 'ip' => '10.10.10.30', + 'public_network' => false, 'memory' => 1536, 'themes_dir' => '../alaveteli-themes', 'os' => 'stretch64', @@ -142,6 +143,11 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| config.vm.define SETTINGS['name'] config.vm.box_url = os[:box_url] config.vm.hostname = "alaveteli-#{ SETTINGS['os'] }" + + if SETTINGS['public_network'] + config.vm.network :public_network + end + config.vm.network :private_network, ip: SETTINGS['ip'] config.vm.synced_folder '.', '/vagrant', disabled: true diff --git a/app/assets/javascripts/admin/admin.coffee b/app/assets/javascripts/admin/admin.coffee deleted file mode 100644 index 8892c3124d..0000000000 --- a/app/assets/javascripts/admin/admin.coffee +++ /dev/null @@ -1,94 +0,0 @@ -jQuery -> - $('.locales a:first').tab('show') - $('.accordion-body').on('hidden', -> - $(@).prev().find('i').first().removeClass().addClass('icon-chevron-right') - ) - $('.accordion-body').on('shown', -> - $(@).prev().find('i').first().removeClass().addClass('icon-chevron-down')) - $('.toggle-hidden').on('click', -> - $(@).parents('td').find('div:hidden').show() - false) - - $('#request_hidden_user_explanation_reasons').on('click', 'input', -> - $('#request_hidden_user_subject, #request_hidden_user_explanation, #request_hide_button').show() - info_request_id = $('#hide_request_form').attr('data-info-request-id') - message = $(this).attr('data-message') - $('#request_hidden_user_explanation_field').val("[loading default text...]") - $.ajax "/hidden_user_explanation?message=" + message + "&info_request_id=" + info_request_id, - type: "GET" - dataType: "text" - error: (data, textStatus, jqXHR) -> - $('#request_hidden_user_explanation_field').val("Error: #{textStatus}") - success: (data, textStatus, jqXHR) -> - $('#request_hidden_user_explanation_field').val(data) - ) - - $('#incoming_messages').on('change', 'input[class=delete-checkbox]', -> - selected = if $('#ids').val() isnt "" - $('#ids').val().split(',') - else - [] - if this.checked - selected.push this.value - $('#ids').val(selected.join(',')) - $('input[value="Delete selected messages"]').attr("disabled", false) - else - selected = selected.filter (e) => e isnt this.value - $('#ids').val(selected.join(',')) - if $('#ids').val() == "" - $('input[value="Delete selected messages"]').attr("disabled", true) - ) - $('#info_request_described_state').on('change', -> - submit_button = $(this).closest('form').find(':submit') - if (this.value is 'vexatious' or - this.value is 'not_foi') and - ($('#info_request_prominence').val() is 'normal' or - $('#info_request_prominence').val() is 'backpage') - $('#info_request_prominence'). - attr('title', - 'The request will not be hidden unless you change the prominence.') - $('#info_request_prominence').tooltip('show') - submit_button. - attr('title', - 'Warning! You are about to save this request without hiding it!') - submit_button.tooltip() - submit_button. - data('confirm', - 'You have set this request to "' + this.value + '" but not' + - ' hidden it using prominence. Are you sure you want to continue?') - else - $('#info_request_prominence').removeAttr('title') - $('#info_request_prominence').tooltip('destroy') - submit_button.removeData('confirm') - submit_button.removeAttr('title') - submit_button.tooltip('destroy') - ) - $('#info_request_prominence').on('change', -> - submit_button = $(this).closest('form').find(':submit') - if (this.value is 'normal' or this.value is 'backpage') and - ($('#info_request_described_state').val() is 'not_foi' or - $('#info_request_described_state').val() is 'vexatious') - $(this). - attr('title', - 'The request will not be hidden unless you change the prominence.') - $(this).tooltip('show') - submit_button. - attr('title', - 'Warning! You are about to save this request without hiding it!') - submit_button.tooltip() - submit_button. - data('confirm', - 'You have set this request to "' + this.value + '" but not' + - ' hidden it using prominence. Are you sure you want to continue?') - else - $(this).removeAttr('title') - $(this).tooltip('destroy') - submit_button.removeAttr('title') - submit_button.tooltip('destroy') - submit_button.removeData('confirm') - ) - $('[data-dismiss]').on 'click', -> - console.log 'click' - parent = $(this).parents(".#{$(this).data('dismiss')}") - parent.hide('slow') - diff --git a/app/assets/javascripts/admin/admin.js b/app/assets/javascripts/admin/admin.js new file mode 100644 index 0000000000..b5fc50ad3a --- /dev/null +++ b/app/assets/javascripts/admin/admin.js @@ -0,0 +1,92 @@ +(function() { + jQuery(function() { + $('.locales a:first').tab('show'); + $('.accordion-body').on('hidden', function() { + return $(this).prev().find('i').first().removeClass().addClass('icon-chevron-right'); + }); + $('.accordion-body').on('shown', function() { + return $(this).prev().find('i').first().removeClass().addClass('icon-chevron-down'); + }); + $('.toggle-hidden').on('click', function() { + $(this).parents('td').find('div:hidden').show(); + return false; + }); + $('#request_hidden_user_explanation_reasons').on('click', 'input', function() { + var info_request_id, message; + $('#request_hidden_user_subject, #request_hidden_user_explanation, #request_hide_button').show(); + info_request_id = $('#hide_request_form').attr('data-info-request-id'); + message = $(this).attr('data-message'); + $('#request_hidden_user_explanation_field').val("[loading default text...]"); + return $.ajax("/hidden_user_explanation?message=" + message + "&info_request_id=" + info_request_id, { + type: "GET", + dataType: "text", + error: function(data, textStatus, jqXHR) { + return $('#request_hidden_user_explanation_field').val("Error: " + textStatus); + }, + success: function(data, textStatus, jqXHR) { + return $('#request_hidden_user_explanation_field').val(data); + } + }); + }); + $('#incoming_messages').on('change', 'input[class=delete-checkbox]', function() { + var selected; + selected = $('#ids').val() !== "" ? $('#ids').val().split(',') : []; + if (this.checked) { + selected.push(this.value); + $('#ids').val(selected.join(',')); + return $('input[value="Delete selected messages"]').attr("disabled", false); + } else { + selected = selected.filter((function(_this) { + return function(e) { + return e !== _this.value; + }; + })(this)); + $('#ids').val(selected.join(',')); + if ($('#ids').val() === "") { + return $('input[value="Delete selected messages"]').attr("disabled", true); + } + } + }); + $('#info_request_described_state').on('change', function() { + var submit_button; + submit_button = $(this).closest('form').find(':submit'); + if ((this.value === 'vexatious' || this.value === 'not_foi') && ($('#info_request_prominence').val() === 'normal' || $('#info_request_prominence').val() === 'backpage')) { + $('#info_request_prominence').attr('title', 'The request will not be hidden unless you change the prominence.'); + $('#info_request_prominence').tooltip('show'); + submit_button.attr('title', 'Warning! You are about to save this request without hiding it!'); + submit_button.tooltip(); + return submit_button.data('confirm', 'You have set this request to "' + this.value + '" but not' + ' hidden it using prominence. Are you sure you want to continue?'); + } else { + $('#info_request_prominence').removeAttr('title'); + $('#info_request_prominence').tooltip('destroy'); + submit_button.removeData('confirm'); + submit_button.removeAttr('title'); + return submit_button.tooltip('destroy'); + } + }); + $('#info_request_prominence').on('change', function() { + var submit_button; + submit_button = $(this).closest('form').find(':submit'); + if ((this.value === 'normal' || this.value === 'backpage') && ($('#info_request_described_state').val() === 'not_foi' || $('#info_request_described_state').val() === 'vexatious')) { + $(this).attr('title', 'The request will not be hidden unless you change the prominence.'); + $(this).tooltip('show'); + submit_button.attr('title', 'Warning! You are about to save this request without hiding it!'); + submit_button.tooltip(); + return submit_button.data('confirm', 'You have set this request to "' + this.value + '" but not' + ' hidden it using prominence. Are you sure you want to continue?'); + } else { + $(this).removeAttr('title'); + $(this).tooltip('destroy'); + submit_button.removeAttr('title'); + submit_button.tooltip('destroy'); + return submit_button.removeData('confirm'); + } + }); + return $('[data-dismiss]').on('click', function() { + var parent; + console.log('click'); + parent = $(this).parents("." + ($(this).data('dismiss'))); + return parent.hide('slow'); + }); + }); + +}).call(this); diff --git a/app/assets/stylesheets/admin.scss b/app/assets/stylesheets/admin.scss index 81badb9178..890bd0f20c 100644 --- a/app/assets/stylesheets/admin.scss +++ b/app/assets/stylesheets/admin.scss @@ -44,7 +44,6 @@ body.admin { font-size: 14px; line-height: 20px; - @import "compass/css3"; @import "bootstrap"; #main { diff --git a/app/assets/stylesheets/responsive/_global_style.scss b/app/assets/stylesheets/responsive/_global_style.scss index f40855ad3e..5fe83bb5fd 100644 --- a/app/assets/stylesheets/responsive/_global_style.scss +++ b/app/assets/stylesheets/responsive/_global_style.scss @@ -459,3 +459,8 @@ button, } } } + +.button--full-width { + width: 100%; + text-align: center; +} diff --git a/app/assets/stylesheets/responsive/alaveteli_pro/_classify_layout.scss b/app/assets/stylesheets/responsive/alaveteli_pro/_classify_layout.scss new file mode 100644 index 0000000000..0cd46461cc --- /dev/null +++ b/app/assets/stylesheets/responsive/alaveteli_pro/_classify_layout.scss @@ -0,0 +1,52 @@ +.classify-left-column { + @include grid-column(12); + @include respond-min($main_menu-mobile_menu_cutoff) { + @include grid-column(9); + } +} + +.classify-right-column { + @include grid-column(12); + @include respond-min($main_menu-mobile_menu_cutoff) { + @include grid-column(3); + &.sidebar--sticky { + max-height: none;; + } + } +} + +.classify-request-controls { + margin-top: 2em; + + .input-label-aligned + .input-label-aligned { + margin-top: 1em; + } + + h3 { + font-weight: normal; + font-size: 1em; + } + + input[name="commit"] { + @extend .button--full-width; + } + + hr { + border-top-color: #d3cec5; + margin-top: 1em; + } +} + +.input-label-aligned { + display: flex; + align-items: flex-start; + + input[type="checkbox"], + input[type="radio"] { + margin-top: 3px; + } + + label { + flex: 1; + } +} diff --git a/app/assets/stylesheets/responsive/alaveteli_pro/_classify_style.scss b/app/assets/stylesheets/responsive/alaveteli_pro/_classify_style.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/app/assets/stylesheets/responsive/alaveteli_pro/_extract_layout.scss b/app/assets/stylesheets/responsive/alaveteli_pro/_extract_layout.scss new file mode 100644 index 0000000000..b15e42257a --- /dev/null +++ b/app/assets/stylesheets/responsive/alaveteli_pro/_extract_layout.scss @@ -0,0 +1,56 @@ +.extract-left-column { + @include grid-column(12); + @include respond-min($main_menu-mobile_menu_cutoff) { + @include grid-column(9); + } +} + +.extract-right-column { + @include grid-column(12); + @include respond-min($main_menu-mobile_menu_cutoff) { + @include grid-column(3); + + &.sidebar--sticky { + max-height: none; + } + } +} + +.extract-request-controls { + margin-top: 2em; + + input[type="text"], + input[type="date"], + input[type="email"], + input[type="url"], + input[type="time"], + input[type="number"], + textarea { + width: 100%; + } + + .button { + margin-bottom: 1em; + } +} + +.dataset-key { + margin-bottom: 0.8em; +} + +.boolean-checkbox { + display: flex; + align-items: flex-start; + + input[type="checkbox"] { + margin-top: 3px; + } + + label { + flex: 1; + } +} + +.form__skip-button { + margin-top: 1em; +} diff --git a/app/assets/stylesheets/responsive/alaveteli_pro/_extract_style.scss b/app/assets/stylesheets/responsive/alaveteli_pro/_extract_style.scss new file mode 100644 index 0000000000..089c72ff8d --- /dev/null +++ b/app/assets/stylesheets/responsive/alaveteli_pro/_extract_style.scss @@ -0,0 +1,15 @@ +.extract-answer-form { + hr { + margin-bottom: 1em; + } +} + +.extract-answer-form__question { + font-weight: bold; +} + +.extract-answer-form { + input[name="commit"] { + @extend .button--full-width; + } +} diff --git a/app/assets/stylesheets/responsive/alaveteli_pro/_projects_layout.scss b/app/assets/stylesheets/responsive/alaveteli_pro/_projects_layout.scss new file mode 100644 index 0000000000..b4973ad711 --- /dev/null +++ b/app/assets/stylesheets/responsive/alaveteli_pro/_projects_layout.scss @@ -0,0 +1,128 @@ +.projects-nav { + @include grid-column($columns:12); + display: flex; + flex-flow: row wrap; + justify-content: space-between; + margin: 2em 0 -1em; +} + +.projects-nav__menu { + @include respond-min( $main_menu-mobile_menu_cutoff ){ + position: relative; + top: -3px; + } +} + +.projects-nav__title { + h1 { + font-size: 1rem; + margin-top: 0; + } +} + +.project-body { + @include grid-column($columns:12); + @include respond-min( $main_menu-mobile_menu_cutoff ){ + @include grid-column($columns:8); + @include ie8 { + padding-right: 0.9375em; + } + @include lte-ie7 { + width: 36.813em; + } + } +} + +.project-aside { + @include grid-column($columns:12); + @include respond-min( $main_menu-mobile_menu_cutoff ){ + @include grid-column($columns:4); + @include ie8 { + padding-left: 0.9375em; + } + @include lte-ie7 { + width: 17.438em; + } + } +} + +.project-meta { + font-size: 0.875em; + color: #666; +} + +.project-aside { + h2 { + font-size: 1.2em; + } +} + +.project-download-data, +.project-access-status { + margin-top: 2em; +} + +.project-owner__photo { + max-width: 125px; + max-height: 250px; + margin-left: 1em; + float: right; + position: relative; + top: -2em; +} + +.project-owner__name { + font-size: 1em; +} + +.project-tasks { + h2 { + margin-bottom: 0; + } +} + +.project-task { + margin-top: 2em; + padding-bottom: 1em; +} + +.project-task-meta-information { + display: flex; + flex-flow: row wrap; + justify-content: space-between; + :first-child.project-meta { + margin-right: 0.5em; + } +} + +.project-workload { + margin: 1em 0; +} + +.project-workload-title { + font-size: 1em; +} + +.project-workload-diagram { + display: flex; + flex-flow: row no-wrap; +} + +.project-workload-indicator { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + + vertical-align: middle; + width: 100%; + height: 0.55em; // ~10px + margin-top: 0.425em; // make it sit in the centre, vertically + margin-right: 0.5em; + border: 0; + +} + +.project-workload-value { + width: 4em; + text-align: right; +} diff --git a/app/assets/stylesheets/responsive/alaveteli_pro/_projects_style.scss b/app/assets/stylesheets/responsive/alaveteli_pro/_projects_style.scss new file mode 100644 index 0000000000..e7f02ed372 --- /dev/null +++ b/app/assets/stylesheets/responsive/alaveteli_pro/_projects_style.scss @@ -0,0 +1,15 @@ +.project-workload-indicator { + + -webkit-progress-bar { + background-color: #f4f4f4; + } + + -moz-progress-bar, + -webkit-progress-value { + background-color: #008CBA; + } + + //ie10 + + color: #008CBA; +} diff --git a/app/assets/stylesheets/responsive/all.scss b/app/assets/stylesheets/responsive/all.scss index 7794d3aac2..ef92093330 100644 --- a/app/assets/stylesheets/responsive/all.scss +++ b/app/assets/stylesheets/responsive/all.scss @@ -104,6 +104,15 @@ @import "responsive/alaveteli_pro/_stripe"; +@import "responsive/alaveteli_pro/_projects_layout"; +@import "responsive/alaveteli_pro/_projects_style"; + +@import "responsive/alaveteli_pro/_classify_layout"; +@import "responsive/alaveteli_pro/_classify_style"; + +@import "responsive/alaveteli_pro/_extract_layout"; +@import "responsive/alaveteli_pro/_extract_style"; + @import "responsive/_ms_edge"; @import "responsive/custom"; diff --git a/app/controllers/admin_announcements_controller.rb b/app/controllers/admin_announcements_controller.rb index 3a4c84d2b7..3a4b3bf44d 100644 --- a/app/controllers/admin_announcements_controller.rb +++ b/app/controllers/admin_announcements_controller.rb @@ -32,7 +32,7 @@ def edit end def update - if @announcement.update_attributes(announcement_params) + if @announcement.update(announcement_params) notice = 'Announcement successfully updated.' redirect_to admin_announcements_path, notice: notice else diff --git a/app/controllers/admin_censor_rule_controller.rb b/app/controllers/admin_censor_rule_controller.rb index 1c6fbb8273..b193608159 100644 --- a/app/controllers/admin_censor_rule_controller.rb +++ b/app/controllers/admin_censor_rule_controller.rb @@ -31,7 +31,7 @@ def edit end def update - if @censor_rule.update_attributes(censor_rule_params) + if @censor_rule.update(censor_rule_params) flash[:notice] = 'Censor rule was successfully updated.' expire_requests_and_redirect else diff --git a/app/controllers/admin_comment_controller.rb b/app/controllers/admin_comment_controller.rb index d71cfbe593..3b6e4934c0 100644 --- a/app/controllers/admin_comment_controller.rb +++ b/app/controllers/admin_comment_controller.rb @@ -41,7 +41,7 @@ def update old_visible = @comment.visible old_attention = @comment.attention_requested - if @comment.update_attributes(comment_params) + if @comment.update(comment_params) update_type = if comment_hidden?(old_visible, old_body) "hide_comment" else diff --git a/app/controllers/admin_holidays_controller.rb b/app/controllers/admin_holidays_controller.rb index 513a72af1b..0ad6477193 100644 --- a/app/controllers/admin_holidays_controller.rb +++ b/app/controllers/admin_holidays_controller.rb @@ -35,7 +35,7 @@ def edit end def update - if @holiday.update_attributes(holiday_params) + if @holiday.update(holiday_params) flash[:notice] = 'Holiday successfully updated.' redirect_to admin_holidays_path else diff --git a/app/controllers/admin_incoming_message_controller.rb b/app/controllers/admin_incoming_message_controller.rb index 1d60f260f1..58e1ac4bae 100644 --- a/app/controllers/admin_incoming_message_controller.rb +++ b/app/controllers/admin_incoming_message_controller.rb @@ -9,7 +9,7 @@ def edit def update old_prominence = @incoming_message.prominence old_prominence_reason = @incoming_message.prominence_reason - if @incoming_message.update_attributes(incoming_message_params) + if @incoming_message.update(incoming_message_params) @incoming_message.info_request.log_event('edit_incoming', :incoming_message_id => @incoming_message.id, :editor => admin_current_user, diff --git a/app/controllers/admin_outgoing_message_controller.rb b/app/controllers/admin_outgoing_message_controller.rb index 548c91e505..33642a6447 100644 --- a/app/controllers/admin_outgoing_message_controller.rb +++ b/app/controllers/admin_outgoing_message_controller.rb @@ -27,7 +27,7 @@ def update old_body = @outgoing_message.raw_body old_prominence = @outgoing_message.prominence old_prominence_reason = @outgoing_message.prominence_reason - if @outgoing_message.update_attributes(outgoing_message_params) + if @outgoing_message.update(outgoing_message_params) @outgoing_message. info_request. log_event("edit_outgoing", diff --git a/app/controllers/admin_public_body_categories_controller.rb b/app/controllers/admin_public_body_categories_controller.rb index 95b1566b5b..cf3f2baee6 100644 --- a/app/controllers/admin_public_body_categories_controller.rb +++ b/app/controllers/admin_public_body_categories_controller.rb @@ -75,7 +75,7 @@ def update end end - if @public_body_category.update_attributes(public_body_category_params) + if @public_body_category.update(public_body_category_params) flash[:notice] = 'Category was successfully updated.' redirect_to edit_admin_category_path(@public_body_category) else diff --git a/app/controllers/admin_public_body_controller.rb b/app/controllers/admin_public_body_controller.rb index 9f9fd51e45..94b286cc89 100644 --- a/app/controllers/admin_public_body_controller.rb +++ b/app/controllers/admin_public_body_controller.rb @@ -98,7 +98,7 @@ def update end AlaveteliLocalization.with_locale(AlaveteliLocalization.default_locale) do params[:public_body][:last_edit_editor] = admin_current_user - if @public_body.update_attributes(public_body_params) + if @public_body.update(public_body_params) if @change_request @change_request.close! @change_request.send_response(params[:subject], params[:response]) diff --git a/app/controllers/admin_public_body_headings_controller.rb b/app/controllers/admin_public_body_headings_controller.rb index 88a4734c3e..00c9c5d3f4 100644 --- a/app/controllers/admin_public_body_headings_controller.rb +++ b/app/controllers/admin_public_body_headings_controller.rb @@ -29,7 +29,7 @@ def edit def update AlaveteliLocalization.with_locale(AlaveteliLocalization.default_locale) do - if @public_body_heading.update_attributes(public_body_heading_params) + if @public_body_heading.update(public_body_heading_params) flash[:notice] = 'Heading was successfully updated.' redirect_to edit_admin_heading_path(@public_body_heading) else diff --git a/app/controllers/admin_request_controller.rb b/app/controllers/admin_request_controller.rb index 196cff6316..82ef95302c 100644 --- a/app/controllers/admin_request_controller.rb +++ b/app/controllers/admin_request_controller.rb @@ -51,7 +51,7 @@ def update old_comments_allowed = @info_request.comments_allowed - if @info_request.update_attributes(info_request_params) + if @info_request.update(info_request_params) @info_request.log_event("edit", { :editor => admin_current_user, :old_title => old_title, :title => @info_request.title, diff --git a/app/controllers/admin_user_controller.rb b/app/controllers/admin_user_controller.rb index 0a2af39666..6963bca076 100644 --- a/app/controllers/admin_user_controller.rb +++ b/app/controllers/admin_user_controller.rb @@ -70,7 +70,7 @@ def edit end def update - if @admin_user.update_attributes(user_params) + if @admin_user.update(user_params) if @admin_user == @user && !@admin_user.is_admin? flash[:notice] = 'User successfully updated - ' \ 'you are no longer an admin.' diff --git a/app/controllers/alaveteli_pro/batch_downloads_controller.rb b/app/controllers/alaveteli_pro/batch_downloads_controller.rb index c15c13d96f..1a96dee219 100644 --- a/app/controllers/alaveteli_pro/batch_downloads_controller.rb +++ b/app/controllers/alaveteli_pro/batch_downloads_controller.rb @@ -3,11 +3,14 @@ # downloads. # class AlaveteliPro::BatchDownloadsController < AlaveteliPro::BaseController + include ActionController::Live + def show authorize! :download, info_request_batch respond_to do |format| format.html { head :bad_request } + format.zip { download_zip } format.csv do metrics = InfoRequestBatchMetrics.new(info_request_batch) send_data metrics.to_csv, filename: metrics.name, type: 'text/csv' @@ -21,4 +24,21 @@ def info_request_batch @info_request_batch ||= current_user.info_request_batches. find(params[:info_request_batch_id]) end + + def download_zip + zip = InfoRequestBatchZip.new(info_request_batch, ability: current_ability) + send_file_headers!( + type: 'application/zip', + disposition: 'attachment', + filename: zip.name + ) + response.headers['Last-Modified'] = Time.zone.now.httpdate.to_s + response.headers['X-Accel-Buffering'] = 'no' + + zip.stream do |chunk| + response.stream.write(chunk) + end + ensure + response.stream.close + end end diff --git a/app/controllers/alaveteli_pro/batch_request_authority_searches_controller.rb b/app/controllers/alaveteli_pro/batch_request_authority_searches_controller.rb index 85b6ae2707..55328e5bf6 100644 --- a/app/controllers/alaveteli_pro/batch_request_authority_searches_controller.rb +++ b/app/controllers/alaveteli_pro/batch_request_authority_searches_controller.rb @@ -4,8 +4,6 @@ class AlaveteliPro::BatchRequestAuthoritySearchesController < AlaveteliPro::Base MAX_RESULTS = 500 - before_action :check_user_has_batch_access - def index @draft_batch_request = find_or_initialise_draft @body_ids_added = @draft_batch_request.public_body_ids @@ -72,13 +70,6 @@ def check_page_limit!(page, per_page) end end - def check_user_has_batch_access - unless feature_enabled? :pro_batch_access, current_user - redirect_to new_alaveteli_pro_info_request_path - end - return true - end - def find_or_initialise_draft current_user.draft_info_request_batches.find_by(id: params[:draft_id]) || current_user.draft_info_request_batches.new diff --git a/app/controllers/alaveteli_pro/classifications_controller.rb b/app/controllers/alaveteli_pro/classifications_controller.rb new file mode 100644 index 0000000000..59fb2a253b --- /dev/null +++ b/app/controllers/alaveteli_pro/classifications_controller.rb @@ -0,0 +1,25 @@ +## +# Controller responsible for handling Alaveteli Pro InfoRequest classification +# +class AlaveteliPro::ClassificationsController < AlaveteliPro::BaseController + include Classifiable + + def create + set_described_state + + flash[:notice] = _('Your request has been updated!') + redirect_to show_alaveteli_pro_request_path( + url_title: @info_request.url_title + ) + end + + private + + def find_info_request + @info_request = InfoRequest.find_by!(url_title: params[:url_title]) + end + + def authorise_info_request + authorize! :update_request_state, @info_request + end +end diff --git a/app/controllers/alaveteli_pro/draft_info_request_batches_controller.rb b/app/controllers/alaveteli_pro/draft_info_request_batches_controller.rb index 7fbc12a651..3eba3dffac 100644 --- a/app/controllers/alaveteli_pro/draft_info_request_batches_controller.rb +++ b/app/controllers/alaveteli_pro/draft_info_request_batches_controller.rb @@ -9,7 +9,7 @@ def create def update @draft = current_user.draft_info_request_batches.find(params[:id]) - @draft.update_attributes(draft_params_multiple_bodies) + @draft.update(draft_params_multiple_bodies) if params[:preview] redirect_to preview_new_alaveteli_pro_info_request_batch_path(draft_id: @draft.id) else diff --git a/app/controllers/alaveteli_pro/draft_info_requests_controller.rb b/app/controllers/alaveteli_pro/draft_info_requests_controller.rb index fbfa870615..e5271fdcf9 100644 --- a/app/controllers/alaveteli_pro/draft_info_requests_controller.rb +++ b/app/controllers/alaveteli_pro/draft_info_requests_controller.rb @@ -13,7 +13,7 @@ def create def update @draft = current_user.draft_info_requests.find(params[:id]) - @draft.update_attributes(draft_params) + @draft.update(draft_params) redirect_after_create_or_update end diff --git a/app/controllers/alaveteli_pro/info_requests_controller.rb b/app/controllers/alaveteli_pro/info_requests_controller.rb index 0a6c0d081e..efa7497982 100644 --- a/app/controllers/alaveteli_pro/info_requests_controller.rb +++ b/app/controllers/alaveteli_pro/info_requests_controller.rb @@ -14,7 +14,7 @@ class AlaveteliPro::InfoRequestsController < AlaveteliPro::BaseController def index @request_filter = AlaveteliPro::RequestFilter.new if params[:alaveteli_pro_request_filter] - @request_filter.update_attributes(request_filter_params) + @request_filter.update(request_filter_params) end request_summaries = @request_filter.results(current_user) @page = params[:page] || 1 @@ -55,16 +55,6 @@ def create end end - def update - @info_request = InfoRequest.find(params[:id]) - authorize! :update_request_state, @info_request - new_status = info_request_params[:described_state] - @info_request.set_described_state(new_status, current_user) - flash[:notice] = _("Your request has been updated!") - redirect_to show_alaveteli_pro_request_path( - url_title: @info_request.url_title) - end - private def show_errors @@ -135,10 +125,6 @@ def request_filter_params permit(:filter, :order, :search) end - def info_request_params - params.require(:info_request).permit(:described_state) - end - def check_public_body_is_requestable if @info_request.public_body unless @info_request.public_body.is_requestable? diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 1e4f0213a0..1571a29f4e 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -28,6 +28,7 @@ class RouteNotFound < StandardError # Standard headers, footers and navigation for whole site layout "default" + include FastGettext::Translation # make functions like _, n_, N_ etc available) include AlaveteliPro::PostRedirectHandler @@ -190,6 +191,10 @@ def render_exception(exception) end def render_hidden(template='request/hidden', opts = {}) + # An embargoed is totally hidden - no indication that anything exists there + # to see + raise ActiveRecord::RecordNotFound if @info_request && @info_request.embargo + response_code = opts.delete(:response_code) { 403 } # forbidden options = { :template => template, :status => response_code }.merge(opts) @@ -200,45 +205,6 @@ def render_hidden(template='request/hidden', opts = {}) false end - # Used to work out where to cache fragments. We add an extra path to the - # URL using the first three digits of the info request id, because we can't - # have more than 32,000 entries in one directory on an ext3 filesystem. - def foi_fragment_cache_part_path(param) - param = param.permit(:incoming_message_id, :part, :file_name, :id, - :only_path, :locale, :skip_cache) - path = url_for(param) - id = param['id'] || param[:id] - first_three_digits = id.to_s[0..2] - path = path.sub("/request/", "/request/" + first_three_digits + "/") - return path - end - - def foi_fragment_cache_path(param) - path = File.join(Rails.root, 'cache', 'views', foi_fragment_cache_part_path(param)) - max_file_length = 255 - 35 # we subtract 35 because tempfile - # adds on a variable number of - # characters - return File.join(File.split(path).map { |x| x[0...max_file_length] }) - end - - def foi_fragment_cache_exists?(key_path) - return File.exists?(key_path) - end - - def foi_fragment_cache_read(key_path) - logger.info "Reading from fragment cache #{key_path}" - return File.read(key_path) - end - - def foi_fragment_cache_write(key_path, content) - FileUtils.mkdir_p(File.dirname(key_path)) - logger.info "Writing to fragment cache #{key_path}" - File.atomic_write(key_path) do |f| - f.write(content) - end - FileUtils.chmod 0644, key_path - end - # A helper method to set @in_pro_area, for controller actions which are # used in both a pro and non-pro context and depend on the :pro parameter # to know which one they're displaying. @@ -261,24 +227,26 @@ def form_authenticity_token(*args) end # Check the user is logged in - def authenticated?(reason_params) - unless session[:user_id] - post_redirect = PostRedirect.new(:uri => request.fullpath, :post_params => params, - :reason_params => reason_params) - post_redirect.save! - # Make sure this redirect does not get cached - it only applies to this user. - # HTTP 1.1 - headers['Cache-Control'] = 'no-cache, no-store, max-age=0, must-revalidate' - # HTTP 1.0 - headers['Pragma'] = 'no-cache' - # Proxies - headers['Expires'] = '0' - # 'modal' controls whether the sign-in form will be displayed in the typical full-blown - # page or on its own, useful for pop-ups - redirect_to signin_url(:token => post_redirect.token, :modal => params[:modal]) - return false - end - return true + def authenticated?(reason_params, post_redirect: nil) + return true if session[:user_id] + + post_redirect ||= PostRedirect.new(uri: request.fullpath, + post_params: params, + reason_params: reason_params) + post_redirect.save! + + # Make sure this redirect does not get cached - it only applies to this user + # HTTP 1.1 + headers['Cache-Control'] = 'no-cache, no-store, max-age=0, must-revalidate' + # HTTP 1.0 + headers['Pragma'] = 'no-cache' + # Proxies + headers['Expires'] = '0' + + # 'modal' controls whether the sign-in form will be displayed in the typical + # full-blown page or on its own, useful for pop-ups + redirect_to signin_url(token: post_redirect.token, modal: params[:modal]) + return false end def authenticated_as_user?(user, reason_params) diff --git a/app/controllers/attachments_controller.rb b/app/controllers/attachments_controller.rb new file mode 100644 index 0000000000..dcaa5acce2 --- /dev/null +++ b/app/controllers/attachments_controller.rb @@ -0,0 +1,187 @@ +## +# Controller to serve FoiAttachment records in both raw and as HTML. +# +class AttachmentsController < ApplicationController + include FragmentCachable + include InfoRequestHelper + + before_action :find_info_request, :find_incoming_message, :find_attachment + before_action :find_project + around_action :cache_attachments + + before_action :authenticate_attachment + before_action :authenticate_attachment_as_html, only: :show_as_html + + def show + # Prevent spam to magic request address. Note that the binary + # substitution method used depends on the content type + body = @incoming_message.apply_masks( + @attachment.default_body, + @attachment.content_type + ) + + if content_type == 'text/html' + body = + Loofah.scrub_document(body, :prune). + to_html(encoding: 'UTF-8'). + try(:html_safe) + end + + render body: body, content_type: content_type + end + + def show_as_html + # images made during conversion (e.g. images in PDF files) are put in the + # cache directory, so the same cache code in cache_attachments above will + # display them. + image_dir = File.dirname(cache_key_path) + FileUtils.mkdir_p(image_dir) + + html = @attachment.body_as_html( + image_dir, + attachment_url: Rack::Utils.escape(attachment_url(@attachment)), + content_for: { + head_suffix: render_to_string( + partial: 'request/view_html_stylesheet', + formats: [:html] + ), + body_prefix: render_to_string( + partial: 'request/view_html_prefix' + ) + } + ) + + html = @incoming_message.apply_masks(html, response.content_type) + + render html: html.html_safe + end + + private + + def find_info_request + @info_request = InfoRequest.find(params[:id]) + end + + def find_incoming_message + @incoming_message = @info_request.incoming_messages.find( + params[:incoming_message_id] + ) + end + + def find_attachment + @attachment = ( + @incoming_message.parse_raw_email! + + IncomingMessage.get_attachment_by_url_part_number_and_filename!( + @incoming_message.get_attachments_for_display, + part_number, + original_filename + ) + ) + end + + def find_project + return unless current_user && params[:project_id] + + @project = current_user.projects.find_by(id: params[:project_id]) + end + + def authenticate_attachment + # Test for hidden + if cannot?(:read, @info_request) + request.format = :html + return render_hidden + end + if cannot?(:read, @incoming_message) + request.format = :html + return render_hidden('request/hidden_correspondence') + end + + return if @attachment + + # If we can't find the right attachment, redirect to the incoming message: + redirect_to incoming_message_url(@incoming_message), status: 303 + end + + def authenticate_attachment_as_html + # The conversion process can generate files in the cache directory that can + # be served up directly by the webserver according to httpd.conf, so don't + # allow it unless that's OK. + return if message_is_public? + + raise ActiveRecord::RecordNotFound, 'Attachment HTML not found.' + end + + # special caching code so mime types are handled right + def cache_attachments + if !params[:skip_cache].nil? + yield + else + if foi_fragment_cache_exists?(cache_key_path) + logger.info("Reading cache for #{cache_key_path}") + + if File.directory?(cache_key_path) + render plain: 'Directory listing not allowed', status: 403 + else + render body: foi_fragment_cache_read(cache_key_path), + content_type: content_type + end + return + end + + yield + + if params[:skip_cache].nil? && response.status == 200 + # write it to the filesystem ourselves, so is just a plain file. (The + # various fragment cache functions using Ruby Marshall to write the file + # which adds a header, so isn't compatible with images that have been + # extracted elsewhere from PDFs) + if message_is_public? + logger.info("Writing cache for #{cache_key_path}") + foi_fragment_cache_write(cache_key_path, response.body) + end + end + end + end + + def part_number + params[:part].to_i + end + + def original_filename + filename = params[:file_name] + if action_name == 'show_as_html' + filename.gsub(/\.html$/, '') + else + filename + end + end + + def content_type + # we don't use @attachment.content_type here, as we want same mime type + # when cached in cache_attachments above + AlaveteliFileTypes.filename_to_mimetype(params[:file_name]) || + 'application/octet-stream' + end + + def message_is_public? + # Is this a completely public request that we can cache attachments for + # to be served up without authentication? + @incoming_message.info_request.prominence(decorate: true).is_public? && + @incoming_message.is_public? + end + + def cache_key_path + foi_fragment_cache_path( + id: @info_request.id, + incoming_message_id: @incoming_message.id, + part: part_number, + file_name: original_filename, + locale: false + ) + end + + def current_ability + @current_ability ||= Ability.new(current_user, project: @project) + end +end diff --git a/app/controllers/classifications_controller.rb b/app/controllers/classifications_controller.rb new file mode 100644 index 0000000000..f3bb413ca5 --- /dev/null +++ b/app/controllers/classifications_controller.rb @@ -0,0 +1,118 @@ +## +# Controller responsible for handling base InfoRequest classification +# +class ClassificationsController < ApplicationController + include Classifiable + + prepend_before_action :check_read_only, only: :create + + rescue_from CanCan::AccessDenied do + authenticated_as_user?( + @info_request.user, + web: _('To classify the response to this FOI request'), + email: _('Then you can classify the FOI response you have got from ' \ + '{{authority_name}}.', + authority_name: @info_request.public_body.name), + email_subject: _('Classify an FOI response from {{authority_name}}', + authority_name: @info_request.public_body.name) + ) + end + + rescue_from ActionController::ParameterMissing do + flash[:error] = _('Please choose whether or not you got some of the ' \ + 'information that you wanted.') + redirect_to_info_request + end + + def create + set_last_request(@info_request) + + if params[:last_info_request_event_id].to_i != @info_request. + last_event_id_needing_description + flash[:error] = _('The request has been updated since you originally ' \ + 'loaded this page. Please check for any new incoming ' \ + 'messages below, and try again.') + redirect_to_info_request + return + end + + event = set_described_state + + # If you're not the *actual* requester. e.g. you are playing the + # classification game, or you're doing this just because you are an + # admin user (not because you also own the request). + unless @info_request.is_actual_owning_user?(current_user) + # Create a classification event for league tables + RequestClassification.create!( + user_id: current_user.id, + info_request_event_id: event.id + ) + + # Don't give advice on what to do next, as it isn't their request + if session[:request_game] + flash[:notice] = { partial: 'request_game/thank_you.html.erb', + locals: { + info_request_title: @info_request.title, + url: request_path(@info_request) + } } + redirect_to categorise_play_url + else + flash[:notice] = _('Thank you for updating this request!') + redirect_to_info_request + end + return + end + + # Display advice for requester on what to do next, as appropriate + calculated_status = @info_request.calculate_status + partial_path = 'request/describe_notices' + if template_exists?(calculated_status, [partial_path], true) + flash[:notice] = + { + partial: "#{partial_path}/#{calculated_status}", + locals: { + info_request_id: @info_request.id, + annotations_enabled: feature_enabled?(:annotations) + } + } + end + + case calculated_status + when 'waiting_response', 'waiting_response_overdue', 'not_held', + 'successful', 'internal_review', 'error_message', 'requires_admin' + redirect_to_info_request + when 'waiting_response_very_overdue', 'rejected', 'partially_successful' + redirect_to unhappy_url(@info_request) + when 'waiting_clarification', 'user_withdrawn' + redirect_to respond_to_last_url(@info_request) + when 'gone_postal' + redirect_to respond_to_last_url(@info_request) + '?gone_postal=1' + else + return theme_describe_state(@info_request) if @@custom_states_loaded + + raise "unknown calculate_status #{@info_request.calculate_status}" + end + end + + private + + def find_info_request + @info_request = InfoRequest.not_embargoed.find_by!( + url_title: params[:url_title] + ) + end + + def authorise_info_request + # If this is an external request, go to the request page - we don't allow + # state change from the front end interface. + if @info_request.is_external? + redirect_to_info_request + else + authorize! :update_request_state, @info_request + end + end + + def redirect_to_info_request + redirect_to request_path(@info_request) + end +end diff --git a/app/controllers/concerns/classifiable.rb b/app/controllers/concerns/classifiable.rb new file mode 100644 index 0000000000..e4f11f39e7 --- /dev/null +++ b/app/controllers/concerns/classifiable.rb @@ -0,0 +1,89 @@ +## +# This module contains shared methods for InfoRequest classification +# +module Classifiable + extend ActiveSupport::Concern + + included do + before_action :find_info_request, :authorise_info_request + before_action :ensure_message, if: :message_required_for_state?, + only: :create + + # rubocop:disable Style/ClassVars, Lint/SuppressedException + @@custom_states_loaded = false + begin + require 'customstates' + include RequestControllerCustomStates + @@custom_states_loaded = true + rescue LoadError, NameError + end + # rubocop:enable Style/ClassVars, Lint/SuppressedException + end + + def message + @described_state = params[:described_state] + @last_info_request_event_id = @info_request. + last_event_id_needing_description + @title = case @described_state + when 'error_message' + _("I've received an error message") + when 'requires_admin' + _('This request requires administrator attention') + else + raise 'Unsupported state' + end + + render 'classifications/message' + end + + private + + def find_info_request + raise NotImplementedError + end + + def authorise_info_request + raise NotImplementedError + end + + def classification_params + params.require(:classification).permit(:described_state, :message) + end + + def ensure_message + return if classification_params[:message] || !message_required_for_state? + + redirect_to url_for( + action: :message, + url_title: @info_request.url_title, + described_state: classification_params[:described_state] + ) + end + + def message_required_for_state? + %w[error_message requires_admin].include?( + classification_params[:described_state] + ) + end + + def set_described_state + described_state = classification_params[:described_state] + message = classification_params[:message] + + log_params = { + user_id: current_user.id, + old_described_state: @info_request.described_state, + described_state: described_state + } + + log_params[:message] = message if message + + # InfoRequest#set_described_state requires this event to be created first + event = @info_request.log_event('status_update', log_params) + + # Make the state change + @info_request.set_described_state(described_state, current_user, message) + + event + end +end diff --git a/app/controllers/concerns/fragment_cachable.rb b/app/controllers/concerns/fragment_cachable.rb new file mode 100644 index 0000000000..742c8ec53d --- /dev/null +++ b/app/controllers/concerns/fragment_cachable.rb @@ -0,0 +1,42 @@ +module FragmentCachable + extend ActiveSupport::Concern + + private + + # Used to work out where to cache fragments. We add an extra path to the + # URL using the first three digits of the info request id, because we can't + # have more than 32,000 entries in one directory on an ext3 filesystem. + def foi_fragment_cache_part_path(param) + path = url_for(param.merge(only_path: true)) + id = param['id'] || param[:id] + first_three_digits = id.to_s[0..2] + path = path.sub("/request/", "/request/" + first_three_digits + "/") + return path + end + + def foi_fragment_cache_path(param) + path = File.join(Rails.root, 'cache', 'views', foi_fragment_cache_part_path(param)) + max_file_length = 255 - 35 # we subtract 35 because tempfile + # adds on a variable number of + # characters + return File.join(File.split(path).map { |x| x[0...max_file_length] }) + end + + def foi_fragment_cache_exists?(key_path) + return File.exist?(key_path) + end + + def foi_fragment_cache_read(key_path) + logger.info "Reading from fragment cache #{key_path}" + return File.read(key_path) + end + + def foi_fragment_cache_write(key_path, content) + FileUtils.mkdir_p(File.dirname(key_path)) + logger.info "Writing to fragment cache #{key_path}" + File.atomic_write(key_path) do |f| + f.write(content) + end + FileUtils.chmod 0644, key_path + end +end diff --git a/app/controllers/projects/base_controller.rb b/app/controllers/projects/base_controller.rb new file mode 100644 index 0000000000..0e7ffadd60 --- /dev/null +++ b/app/controllers/projects/base_controller.rb @@ -0,0 +1,24 @@ +# View and manage Projects +class Projects::BaseController < ApplicationController + before_action :check_feature_enabled + before_action :set_in_pro_area + before_action :find_project + + private + + def find_project + @project = Project.find(params[:project_id]) + end + + def check_feature_enabled + raise ActiveRecord::RecordNotFound unless feature_enabled?(:projects) + end + + def set_in_pro_area + @in_pro_area = true + end + + def current_ability + @current_ability ||= Ability.new(current_user, project: @project) + end +end diff --git a/app/controllers/projects/classifications_controller.rb b/app/controllers/projects/classifications_controller.rb new file mode 100644 index 0000000000..3dc3112ea4 --- /dev/null +++ b/app/controllers/projects/classifications_controller.rb @@ -0,0 +1,39 @@ +## +# Controller responsible for handling project InfoRequest classification +# +# Requires `url_title` to be passed in as a param +# +class Projects::ClassificationsController < Projects::BaseController + include Classifiable + + def create + @project.submissions.create(submission_params) + + flash[:notice] = _('Thank you for updating this request!') + redirect_to project_classify_path(@project) + end + + private + + def find_info_request + @info_request = @project.info_requests.classifiable.find_by!( + url_title: url_title + ) + end + + def authorise_info_request + authorize! :update_request_state, @info_request + end + + def url_title + params.require(:url_title) + end + + def submission_params + { + user: current_user, + info_request: @info_request, + resource: set_described_state + } + end +end diff --git a/app/controllers/projects/classifies_controller.rb b/app/controllers/projects/classifies_controller.rb new file mode 100644 index 0000000000..fd57472730 --- /dev/null +++ b/app/controllers/projects/classifies_controller.rb @@ -0,0 +1,57 @@ +require_dependency 'project/queue' + +# Classify a request in a Project +class Projects::ClassifiesController < Projects::BaseController + before_action :authenticate + + def show + authorize! :read, @project + + @queue = Project::Queue.classifiable(@project, session) + @info_request = @queue.next + + unless @info_request + if @project.info_requests.classifiable.any? + msg = _('Nice work! How about having another try at the requests you ' \ + 'skipped?') + @queue.reset + else + msg = _('There are no requests to classify right now. Great job!') + end + + redirect_to @project, notice: msg + return + end + + @state_transitions = @info_request.state.transitions( + is_pro_user: false, + is_owning_user: false, + in_internal_review: @info_request.described_state == 'internal_review', + user_asked_to_update_status: false + ) + end + + # Skip a request + def update + authorize! :read, @project + + info_request = + @project.info_requests.find_by!(url_title: params.require(:url_title)) + + queue = Project::Queue.classifiable(@project, session) + queue.skip(info_request) + + redirect_to project_classify_path(@project), notice: _('Skipped!') + end + + private + + def authenticate + authenticated?( + web: _('To join this project'), + email: _('Then you can join this project'), + email_subject: _('Confirm your account on {{site_name}}', + site_name: site_name) + ) + end +end diff --git a/app/controllers/projects/contributors_controller.rb b/app/controllers/projects/contributors_controller.rb new file mode 100644 index 0000000000..5de68e8994 --- /dev/null +++ b/app/controllers/projects/contributors_controller.rb @@ -0,0 +1,30 @@ +# Manage a Project's contributors +class Projects::ContributorsController < Projects::BaseController + def destroy + return unless authenticate! + + @contributor = @project.contributors.find(params[:id]) + authorize! :remove_contributor, @contributor + + @project.contributors.destroy(current_user) + + redirect_to root_path, notice: _('You have left the project.') + end + + private + + def authenticate! + post_redirect = PostRedirect.new( + uri: project_path(@project), + post_params: params, + reason_params: { + web: _('To leave this project'), + email: _('Then you can leave this project'), + email_subject: _('Confirm your account on {{site_name}}', + site_name: site_name) + } + ) + + authenticated?(nil, post_redirect: post_redirect) + end +end diff --git a/app/controllers/projects/downloads_controller.rb b/app/controllers/projects/downloads_controller.rb new file mode 100644 index 0000000000..061f004f7c --- /dev/null +++ b/app/controllers/projects/downloads_controller.rb @@ -0,0 +1,16 @@ +## +# Controller which manages Project data downloads. +# +class Projects::DownloadsController < Projects::BaseController + def show + authorize! :download, @project + + respond_to do |format| + format.html { head :bad_request } + format.csv do + export = Project::Export.new(@project) + send_data export.to_csv, filename: export.name, type: 'text/csv' + end + end + end +end diff --git a/app/controllers/projects/extracts_controller.rb b/app/controllers/projects/extracts_controller.rb new file mode 100644 index 0000000000..60bac4d2d0 --- /dev/null +++ b/app/controllers/projects/extracts_controller.rb @@ -0,0 +1,85 @@ +require_dependency 'project/queue' + +# Extract data from a Project +class Projects::ExtractsController < Projects::BaseController + before_action :authenticate, :find_info_request + + def show + authorize! :read, @project + + unless @info_request + if @project.info_requests.extractable.any? + msg = _('Nice work! How about having another try at the requests you ' \ + 'skipped?') + @queue.reset + else + msg = _('There are no requests to extract right now. Great job!') + end + + redirect_to @project, notice: msg + return + end + + @value_set = Dataset::ValueSet.new + end + + # Skip a request + def update + authorize! :read, @project + + queue = Project::Queue.extractable(@project, session) + queue.skip(@info_request) + + redirect_to project_extract_path(@project), notice: _('Skipped!') + end + + def create + authorize! :read, @project + + @value_set = Dataset::ValueSet.new(extract_params) + submission = @project.submissions.new(submission_params) + + if submission.save + redirect_to project_extract_path + else + flash.now[:error] = _("Extraction couldn't be saved.") + render :show + end + end + + private + + def authenticate + authenticated?( + web: _('To join this project'), + email: _('Then you can join this project'), + email_subject: _('Confirm your account on {{site_name}}', + site_name: site_name) + ) + end + + def find_info_request + if params[:url_title] + @info_request = @project.info_requests.extractable.find_by!( + url_title: params[:url_title] + ) + else + @queue = Project::Queue.extractable(@project, session) + @info_request = @queue.next + end + end + + def extract_params + params.require(:extract).permit( + :dataset_key_set_id, values_attributes: [:dataset_key_id, :value] + ) + end + + def submission_params + { + user: current_user, + info_request: @info_request, + resource: @value_set + } + end +end diff --git a/app/controllers/projects/invites_controller.rb b/app/controllers/projects/invites_controller.rb new file mode 100644 index 0000000000..01138b20af --- /dev/null +++ b/app/controllers/projects/invites_controller.rb @@ -0,0 +1,30 @@ +# Invite contributors to a Project +class Projects::InvitesController < Projects::BaseController + before_action :authenticate + + def create + if @project.member?(current_user) + flash[:notice] = _('You are already a member of this project') + else + @project.contributors << current_user + flash[:notice] = _('Welcome to the project!') + end + + redirect_to @project + end + + private + + def find_project + @project = Project.find_by!(invite_token: params[:token]) + end + + def authenticate + authenticated?( + web: _('To join this project'), + email: _('Then you can join this project'), + email_subject: _('Confirm your account on {{site_name}}', + site_name: site_name) + ) + end +end diff --git a/app/controllers/projects/projects_controller.rb b/app/controllers/projects/projects_controller.rb new file mode 100644 index 0000000000..b2aab27716 --- /dev/null +++ b/app/controllers/projects/projects_controller.rb @@ -0,0 +1,23 @@ +# View and manage Projects +class Projects::ProjectsController < Projects::BaseController + before_action :authenticate + + def show + authorize! :read, @project + end + + private + + def find_project + @project = Project.find(params[:id]) + end + + def authenticate + authenticated?( + web: _('To join this project'), + email: _('Then you can join this project'), + email_subject: _('Confirm your account on {{site_name}}', + site_name: site_name) + ) + end +end diff --git a/app/controllers/request_controller.rb b/app/controllers/request_controller.rb index b25b2bea87..78a3490378 100644 --- a/app/controllers/request_controller.rb +++ b/app/controllers/request_controller.rb @@ -9,7 +9,7 @@ require 'open-uri' class RequestController < ApplicationController - before_action :check_read_only, :only => [ :new, :describe_state, :upload_response ] + before_action :check_read_only, only: [:new, :upload_response] before_action :check_batch_requests_and_user_allowed, :only => [ :select_authorities, :new_batch ] before_action :set_render_recaptcha, :only => [ :new ] before_action :redirect_numeric_id_to_url_title, :only => [:show] @@ -22,14 +22,6 @@ class RequestController < ApplicationController MAX_RESULTS = 500 PER_PAGE = 25 - @@custom_states_loaded = false - begin - require 'customstates' - include RequestControllerCustomStates - @@custom_states_loaded = true - rescue LoadError, NameError - end - def select_authority # Check whether we force the user to sign in right at the start, or we allow her # to start filling the request anonymously @@ -440,145 +432,6 @@ def new redirect_to show_request_path(:url_title => @info_request.url_title) end - # Submitted to the describing state of messages form - def describe_state - info_request = InfoRequest.not_embargoed.find(params[:id].to_i) - set_last_request(info_request) - - # If this is an external request, go to the request page - we don't allow - # state change from the front end interface. - if info_request.is_external? - redirect_to request_url(info_request) - return - end - - # Check authenticated, and parameters set. - unless can?(:update_request_state, info_request) - authenticated_as_user?( - info_request.user, - :web => _("To classify the response to this FOI request"), - :email => _("Then you can classify the FOI response you have got " \ - "from {{authority_name}}.", - :authority_name => info_request.public_body.name), - :email_subject => _("Classify an FOI response from {{authority_name}}", - :authority_name => info_request.public_body.name)) - # do nothing - as "authenticated?" has done the redirect to signin page for us - return - end - - if !params[:incoming_message] - flash[:error] = _("Please choose whether or not you got some of the information that you wanted.") - redirect_to request_url(info_request) - return - end - - if params[:last_info_request_event_id].to_i != info_request.last_event_id_needing_description - flash[:error] = _("The request has been updated since you originally loaded this page. " \ - "Please check for any new incoming messages below, and try again.") - redirect_to request_url(info_request) - return - end - - described_state = params[:incoming_message][:described_state] - message = params[:incoming_message][:message] - - log_params = { - user_id: authenticated_user.id, - old_described_state: info_request.described_state, - described_state: described_state - } - - # For requires_admin and error_message states we ask for an extra message to - # send to the administrators. - # If this message hasn't been included then ask for it. If it has, log it. - if ["error_message", "requires_admin"].include?(described_state) - if message.nil? - redirect_to describe_state_message_url( - url_title: info_request.url_title, - described_state: described_state) - return - else - log_params[:message] = message - end - end - - # Make the state change - event = info_request.log_event("status_update", log_params) - info_request. - set_described_state(described_state, authenticated_user, message) - - # If you're not the *actual* requester. e.g. you are playing the - # classification game, or you're doing this just because you are an - # admin user (not because you also own the request). - if !info_request.is_actual_owning_user?(authenticated_user) - # Create a classification event for league tables - RequestClassification.create!(:user_id => authenticated_user.id, - :info_request_event_id => event.id) - - # Don't give advice on what to do next, as it isn't their request - if session[:request_game] - flash[:notice] = { :partial => "request_game/thank_you.html.erb", - :locals => { - :info_request_title => info_request.title, - :url => request_path(info_request) - } - } - redirect_to categorise_play_url - else - flash[:notice] = _('Thank you for updating this request!') - redirect_to request_url(info_request) - end - return - end - - # Display advice for requester on what to do next, as appropriate - calculated_status = info_request.calculate_status - partial_path = 'request/describe_notices' - if template_exists?(calculated_status, [partial_path], true) - flash[:notice] = - { - :partial => "#{partial_path}/#{calculated_status}", - :locals => { - :info_request_id => info_request.id, - :annotations_enabled => feature_enabled?(:annotations), - } - } - end - - case calculated_status - when 'waiting_response', 'waiting_response_overdue', 'not_held', 'successful', - 'internal_review', 'error_message', 'requires_admin' - redirect_to request_url(info_request) - when 'waiting_response_very_overdue', 'rejected', 'partially_successful' - redirect_to unhappy_url(info_request) - when 'waiting_clarification', 'user_withdrawn' - redirect_to respond_to_last_url(info_request) - when 'gone_postal' - redirect_to respond_to_last_url(info_request) + "?gone_postal=1" - else - if @@custom_states_loaded - return self.theme_describe_state(info_request) - else - raise "unknown calculate_status #{info_request.calculate_status}" - end - end - end - - # Collect a message to include with the change of state - def describe_state_message - @info_request = InfoRequest.not_embargoed.find_by_url_title!(params[:url_title]) - @described_state = params[:described_state] - @last_info_request_event_id = @info_request.last_event_id_needing_description - @title = case @described_state - when "error_message" - _("I've received an error message") - when "requires_admin" - _("This request requires administrator attention") - else - raise "Unsupported state" - end - end - # Used for links from polymorphic URLs e.g. in Atom feeds - just redirect to # proper URL for the message the event refers to def show_request_event @@ -596,170 +449,6 @@ def show_request_event end end - before_action :authenticate_attachment, :only => [ :get_attachment, :get_attachment_as_html ] - def authenticate_attachment - # Test for hidden - incoming_message = IncomingMessage.find(params[:incoming_message_id]) - raise ActiveRecord::RecordNotFound.new("Message not found") if incoming_message.nil? - if cannot?(:read, incoming_message.info_request) - @info_request = incoming_message.info_request # used by view - request.format = :html - return render_hidden - end - if cannot?(:read, incoming_message) - @incoming_message = incoming_message # used by view - request.format = :html - return render_hidden('request/hidden_correspondence') - end - # Is this a completely public request that we can cache attachments for - # to be served up without authentication? - if incoming_message.info_request.prominence(:decorate => true).is_public? && - incoming_message.is_public? - @files_can_be_cached = true - end - end - - # special caching code so mime types are handled right - around_action :cache_attachments, :only => [ :get_attachment, :get_attachment_as_html ] - def cache_attachments - if !params[:skip_cache].nil? - yield - else - key = params.merge(:only_path => true) - key_path = foi_fragment_cache_path(key) - if foi_fragment_cache_exists?(key_path) - logger.info("Reading cache for #{key_path}") - - if File.directory?(key_path) - render :plain => "Directory listing not allowed", :status => 403 - else - content_type = - AlaveteliFileTypes.filename_to_mimetype(params[:file_name]) || - 'application/octet-stream' - - render :body => foi_fragment_cache_read(key_path), - :content_type => content_type - end - return - end - - yield - - if params[:skip_cache].nil? && response.status == 200 - # write it to the fileystem ourselves, so is just a plain file. (The - # various fragment cache functions using Ruby Marshall to write the file - # which adds a header, so isnt compatible with images that have been - # extracted elsewhere from PDFs) - if @files_can_be_cached == true - logger.info("Writing cache for #{key_path}") - foi_fragment_cache_write(key_path, response.body) - end - end - end - end - - def get_attachment - get_attachment_internal(false) - return unless @attachment - - - # we don't use @attachment.content_type here, as we want same mime type when cached in cache_attachments above - content_type = - AlaveteliFileTypes.filename_to_mimetype(params[:file_name]) || - 'application/octet-stream' - - # Prevent spam to magic request address. Note that the binary - # subsitution method used depends on the content type - body = @incoming_message. - apply_masks(@attachment.default_body, @attachment.content_type) - - if content_type == 'text/html' - body = - Loofah.scrub_document(body, :prune). - to_html(encoding: 'UTF-8'). - try(:html_safe) - end - - render :body => body, :content_type => content_type - end - - def get_attachment_as_html - # The conversion process can generate files in the cache directory that can be served up - # directly by the webserver according to httpd.conf, so don't allow it unless that's OK. - if @files_can_be_cached != true - raise ActiveRecord::RecordNotFound.new("Attachment HTML not found.") - end - get_attachment_internal(true) - return unless @attachment - - # images made during conversion (e.g. images in PDF files) are put in the cache directory, so - # the same cache code in cache_attachments above will display them. - key = params.merge(:only_path => true) - key_path = foi_fragment_cache_path(key) - image_dir = File.dirname(key_path) - FileUtils.mkdir_p(image_dir) - - html = @attachment.body_as_html( - image_dir, - attachment_url: Rack::Utils.escape(@attachment_url), - content_for: { - head_suffix: render_to_string( - partial: 'request/view_html_stylesheet', - formats: [:html]), - body_prefix: render_to_string( - partial: 'request/view_html_prefix') - }) - - html = @incoming_message.apply_masks(html, response.content_type) - - render :html => html.html_safe - end - - # Internal function - def get_attachment_internal(html_conversion) - @incoming_message = IncomingMessage.find(params[:incoming_message_id]) - @requested_request = InfoRequest.find(params[:id]) - @incoming_message.parse_raw_email! - @info_request = @incoming_message.info_request - if @incoming_message.info_request_id != params[:id].to_i - # Note that params[:id] might not be an integer, though - # if we’ve got this far then it must begin with an integer - # and that integer must be the id number of an actual request. - message = "Incoming message %d does not belong to request '%s'" % [@incoming_message.info_request_id, params[:id]] - raise ActiveRecord::RecordNotFound.new(message) - end - @part_number = params[:part].to_i - @filename = params[:file_name] - if html_conversion - @original_filename = @filename.gsub(/\.html$/, "") - else - @original_filename = @filename - end - - # check permissions - raise "internal error, pre-auth filter should have caught this" if cannot?(:read, @info_request) - @attachment = IncomingMessage.get_attachment_by_url_part_number_and_filename(@incoming_message.get_attachments_for_display, @part_number, @original_filename) - # If we can't find the right attachment, redirect to the incoming message: - unless @attachment - return redirect_to incoming_message_url(@incoming_message), :status => 303 - end - - # check filename in URL matches that in database (use a censor rule if you want to change a filename) - if @attachment.display_filename != @original_filename && @attachment.old_display_filename != @original_filename - msg = 'please use same filename as original file has, display: ' - msg += "'#{ @attachment.display_filename }' " - msg += 'old_display: ' - msg += "'#{ @attachment.old_display_filename }' " - msg += 'original: ' - msg += "'#{ @original_filename }'" - raise ActiveRecord::RecordNotFound.new(msg) - end - - @attachment_url = get_attachment_url(:id => @incoming_message.info_request_id, - :incoming_message_id => @incoming_message.id, :part => @part_number, - :file_name => @original_filename ) - end - # FOI officers can upload a response def upload_response @locale = AlaveteliLocalization.locale @@ -860,7 +549,7 @@ def download_entire_request return render_hidden end cache_file_path = @info_request.make_zip_cache_path(@user) - if !File.exists?(cache_file_path) + if !File.exist?(cache_file_path) FileUtils.mkdir_p(File.dirname(cache_file_path)) make_request_zip(@info_request, cache_file_path) File.chmod(0644, cache_file_path) @@ -872,16 +561,6 @@ def download_entire_request private - def render_hidden(template='request/hidden', opts = {}) - # An embargoed is totally hidden - no indication that anything exists there - # to see - if @info_request && @info_request.embargo - raise ActiveRecord::RecordNotFound - else - return super(template, opts) - end - end - def info_request_params(batch = false) if batch unless params[:info_request].nil? || params[:info_request].empty? @@ -903,7 +582,6 @@ def can_update_status(info_request) def assign_variables_for_show_template(info_request) @info_request = info_request - @info_request_events = info_request.info_request_events @status = info_request.calculate_status @old_unclassified = info_request.is_old_unclassified? && !authenticated_user.nil? @@ -921,7 +599,7 @@ def assign_variables_for_show_template(info_request) @show_profile_photo = !!( !@info_request.is_external? && - @info_request.user.profile_photo && + @info_request.user.show_profile_photo? && !@render_to_file ) @@ -997,7 +675,7 @@ def make_request_summary_file(info_request) convert_command = AlaveteliConfiguration::html_to_pdf_command @render_to_file = true assign_variables_for_show_template(info_request) - if !convert_command.blank? && File.exists?(convert_command) + if !convert_command.blank? && File.exist?(convert_command) html_output = render_to_string(:template => 'request/show') tmp_input = Tempfile.new(['foihtml2pdf-input', '.html']) tmp_input.write(html_output) diff --git a/app/controllers/request_game_controller.rb b/app/controllers/request_game_controller.rb index 03381d7887..f81adf8eb6 100644 --- a/app/controllers/request_game_controller.rb +++ b/app/controllers/request_game_controller.rb @@ -26,7 +26,7 @@ def play where_old_unclassified. limit(3). is_searchable. - order('random()') + order(Arel.sql('random()')) if @missing == 0 flash.now[:notice] = { diff --git a/app/helpers/alaveteli_pro/info_requests_helper.rb b/app/helpers/alaveteli_pro/info_requests_helper.rb index 75071f0ad5..4abc105219 100644 --- a/app/helpers/alaveteli_pro/info_requests_helper.rb +++ b/app/helpers/alaveteli_pro/info_requests_helper.rb @@ -15,8 +15,8 @@ def publish_at_options options.unshift([_('Publish immediately'), '']) end - def embargo_extension_options(embargo) - options = embargo_options_from_date(embargo.publish_at) + def embargo_extension_options(embargo = nil) + options = embargo_options_from_date(embargo&.publish_at || Date.today) options.unshift([_('Choose a duration'), '']) end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index bb17f5b163..c4e97fef35 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -113,7 +113,7 @@ def theme_asset_exists?(asset_path) view_paths.paths.first.to_s. gsub("/app/views", "/app/assets/#{asset_path}") end - File.exists?(file_path) + File.exist?(file_path) end # Note that if the admin interface is proxied via another server, we can't diff --git a/app/helpers/classifications_helper.rb b/app/helpers/classifications_helper.rb new file mode 100644 index 0000000000..45fc8fa8bf --- /dev/null +++ b/app/helpers/classifications_helper.rb @@ -0,0 +1,13 @@ +# -*- encoding : utf-8 -*- +# Helpers for classifications +module ClassificationsHelper + def classification_radio_button(state, id_suffix: nil) + id = "#{ state }#{ id_suffix }" + radio_button 'classification', 'described_state', state, id: id + end + + def classification_label(state, text, id_suffix: nil) + id = "#{ state }#{ id_suffix }" + label_tag(id, text) + end +end diff --git a/app/helpers/download_helper.rb b/app/helpers/download_helper.rb new file mode 100644 index 0000000000..e96e007e4b --- /dev/null +++ b/app/helpers/download_helper.rb @@ -0,0 +1,14 @@ +## +# Helper methods relating to file downloads +# +module DownloadHelper + def generate_download_filename(resource:, id:, title:, type: nil, ext:) + url_title = MySociety::Format.simplify_url_part( + title, resource, 32 + ) + url_title += "-#{type}" if type + timestamp = Time.zone.now.to_formatted_s(:filename) + + "#{resource}-#{id}-#{url_title}-#{timestamp}.#{ext}" + end +end diff --git a/app/helpers/info_request_helper.rb b/app/helpers/info_request_helper.rb index 2c7dfadff7..bf4e705d82 100644 --- a/app/helpers/info_request_helper.rb +++ b/app/helpers/info_request_helper.rb @@ -18,6 +18,20 @@ def status_text(info_request, opts = {}) end end + def js_correspondence_navigation + css_class = 'js-request-navigation request-navigation' + + data_attrs = { + next_text: _('Next message'), + prev_text: _('Previous message'), + status_text: _('Message {{current_message}} of {{total_messages}}', + current_message: '[[x]]', + total_messages: '[[y]]') + } + + tag.div class: css_class, data: data_attrs + end + private def status_text_awaiting_description(info_request, opts = {}) @@ -261,15 +275,19 @@ def attachment_link(incoming_message, attachment) link_to image_tag(image, :class => "attachment__image", :alt => "Attachment"), - attachment_path(incoming_message, attachment) + attachment_path(attachment) end - def attachment_path(incoming_message, attachment, options = {}) - attach_params = attachment_params(incoming_message, attachment, options) + def attachment_path(attachment, options = {}) + attachment_url(attachment, options.merge(only_path: true)) + end + + def attachment_url(attachment, options = {}) + attach_params = attachment_params(attachment, options) if options[:html] - get_attachment_as_html_path(attach_params) + get_attachment_as_html_url(attach_params) else - get_attachment_path(attach_params) + get_attachment_url(attach_params) end end @@ -281,12 +299,13 @@ def details_help_link(public_body) private - def attachment_params(incoming_message, attachment, options = {}) + def attachment_params(attachment, options = {}) attach_params = { - :id => incoming_message.info_request_id, - :incoming_message_id => incoming_message.id, - :part => attachment.url_part_number, - :file_name => attachment.display_filename + id: attachment.incoming_message.info_request_id, + incoming_message_id: attachment.incoming_message_id, + part: attachment.url_part_number, + file_name: attachment.display_filename, + only_path: options.fetch(:only_path, false) } if options[:html] attach_params[:file_name] = "#{attachment.display_filename}.html" @@ -297,6 +316,8 @@ def attachment_params(incoming_message, attachment, options = {}) attach_params[:cookie_passthrough] = 1 end + attach_params[:project_id] = @project.id if @project + attach_params end diff --git a/app/models/ability.rb b/app/models/ability.rb index e996c84a7e..7bd73ca967 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -3,7 +3,9 @@ class Ability include CanCan::Ability include AlaveteliFeatures::Helpers - def initialize(user) + attr_reader :user, :project + + def initialize(user, project: nil) # Define abilities for the passed in user here. For example: # # user ||= User.new # guest user (not logged in) @@ -31,21 +33,22 @@ def initialize(user) # See the wiki for details: # https://github.com/CanCanCommunity/cancancan/wiki/Defining-Abilities + @user = user + @project = project + # Updating request status can :update_request_state, InfoRequest do |request| - self.class.can_update_request_state?(user, request) + can_update_request_state?(request) end # Viewing messages with prominence can :read, [IncomingMessage, OutgoingMessage] do |msg| - self.class.can_view_with_prominence?(msg.prominence, - msg.info_request, - user) + can_view_with_prominence?(msg.prominence, msg.info_request) end # Viewing requests with prominence can :read, InfoRequest do |request| - self.class.can_view_with_prominence?(request.prominence, request, user) + can_view_with_prominence?(request.prominence, request) end # Viewing batch requests @@ -68,7 +71,7 @@ def initialize(user) user && (user.is_pro_admin? || (user == batch_request.user && user.is_pro?)) else - user && self.class.requester_or_admin?(user, batch_request) + user && requester_or_admin?(batch_request) end end @@ -77,7 +80,7 @@ def initialize(user) if batch_request.embargo_duration user && (user == batch_request.user || user.is_pro_admin?) else - user && self.class.requester_or_admin?(user, batch_request) + user && requester_or_admin?(batch_request) end end @@ -150,19 +153,34 @@ def initialize(user) end end + if feature_enabled? :projects + can :read, Project do |target_project| + user && (user.is_pro_admin? || target_project.member?(user)) + end + + can :remove_contributor, User do |contributor| + user && project.contributor?(contributor) && + (project.owner?(user) || user == contributor) + end + + can :download, Project do |target_project| + user && (user.is_pro_admin? || target_project.owner?(user)) + end + end end private - def self.can_update_request_state?(user, request) - (user && request.is_old_unclassified?) || request.is_owning_user?(user) + def can_update_request_state?(request) + (user && request.is_old_unclassified?) || request.is_owning_user?(user) || + (project && project.info_request?(request) && project.member?(user)) end - def self.requester_or_admin?(user, request) + def requester_or_admin?(request) user == request.user || user.is_admin? end - def self.can_view_with_prominence?(prominence, info_request, user) + def can_view_with_prominence?(prominence, info_request) if info_request.embargo case prominence when 'hidden' @@ -170,7 +188,9 @@ def self.can_view_with_prominence?(prominence, info_request, user) when 'requester_only' info_request.is_actual_owning_user?(user) || User.view_hidden_and_embargoed?(user) else - info_request.is_actual_owning_user?(user) || User.view_embargoed?(user) + info_request.is_actual_owning_user?(user) || + User.view_embargoed?(user) || + project&.member?(user) end else case prominence diff --git a/app/models/alaveteli_pro/request_filter.rb b/app/models/alaveteli_pro/request_filter.rb index ad95806684..419f8d8a4a 100644 --- a/app/models/alaveteli_pro/request_filter.rb +++ b/app/models/alaveteli_pro/request_filter.rb @@ -7,7 +7,7 @@ class RequestFilter attr_accessor :filter, :order, :search - def update_attributes(attributes = {}) + def update(attributes = {}) self.attributes = attributes end diff --git a/app/models/alaveteli_pro/request_summary.rb b/app/models/alaveteli_pro/request_summary.rb index bbb791fa38..beda8e5e72 100644 --- a/app/models/alaveteli_pro/request_summary.rb +++ b/app/models/alaveteli_pro/request_summary.rb @@ -66,7 +66,7 @@ def self.create_or_update_from(request) end def update_from(request) - update_attributes(self.class.attributes_from_request(request)) + update(self.class.attributes_from_request(request)) end private diff --git a/app/models/censor_rule.rb b/app/models/censor_rule.rb index 6ed0a71b66..15218171de 100644 --- a/app/models/censor_rule.rb +++ b/app/models/censor_rule.rb @@ -51,7 +51,10 @@ def apply_to_text(text_to_censor) def apply_to_binary(binary_to_censor) return nil if binary_to_censor.nil? - binary_to_censor.gsub(to_replace('ASCII-8BIT')) { |match| match.gsub(single_char_regexp, 'x') } + + binary_to_censor.gsub(to_replace(binary_to_censor.encoding)) do |match| + match.gsub(single_char_regexp) { |m| 'x' * m.bytesize } + end end def is_global? @@ -72,6 +75,20 @@ def expire_requests end end + def censorable_requests + if info_request + # Prefer a chainable query instead of wrapping in Array for similar API + # between CensorRule types + InfoRequest.where(id: info_request_id) + elsif user + user.info_requests + elsif public_body + public_body.info_requests + else + InfoRequest.unscoped + end + end + private def single_char_regexp diff --git a/app/models/dataset.rb b/app/models/dataset.rb new file mode 100644 index 0000000000..5beb2f662d --- /dev/null +++ b/app/models/dataset.rb @@ -0,0 +1,8 @@ +## +# Module namespace for Dataset models +# +module Dataset + def self.table_name_prefix + 'dataset_' + end +end diff --git a/app/models/dataset/key.rb b/app/models/dataset/key.rb new file mode 100644 index 0000000000..64ebed4038 --- /dev/null +++ b/app/models/dataset/key.rb @@ -0,0 +1,38 @@ +# == Schema Information +# Schema version: 20200501183111 +# +# Table name: dataset_keys +# +# id :integer not null, primary key +# dataset_key_set_id :integer +# title :string +# format :string +# order :integer +# created_at :datetime not null +# updated_at :datetime not null +# + +## +# A dataset key representing an value which should be extractable from a +# resource +# +class Dataset::Key < ApplicationRecord + belongs_to :key_set, foreign_key: 'dataset_key_set_id' + has_many :values, foreign_key: 'dataset_key_id', inverse_of: :key + + default_scope -> { order(:order) } + + FORMATS = { + text: /\A.*\z/m, + numeric: /\A[0-9,%\+\-\s]*\z/, + boolean: /\A(0|1)\z/ + }.freeze + + validates :title, :format, :order, presence: true + validates :format, inclusion: { in: FORMATS.keys.map(&:to_s) } + validates :order, uniqueness: { scope: :dataset_key_set_id } + + def format_regexp + FORMATS[format.to_sym] + end +end diff --git a/app/models/dataset/key_set.rb b/app/models/dataset/key_set.rb new file mode 100644 index 0000000000..b04c34227f --- /dev/null +++ b/app/models/dataset/key_set.rb @@ -0,0 +1,29 @@ +# == Schema Information +# Schema version: 20200501183111 +# +# Table name: dataset_key_sets +# +# id :integer not null, primary key +# resource_type :string +# resource_id :integer +# created_at :datetime not null +# updated_at :datetime not null +# + +## +# A dataset collection of keys +# +class Dataset::KeySet < ApplicationRecord + belongs_to :resource, polymorphic: true + has_many :keys, foreign_key: 'dataset_key_set_id', inverse_of: :key_set + has_many :value_sets, foreign_key: 'dataset_key_set_id', inverse_of: :key_set + + RESOURCE_TYPES = %w[ + Project + InfoRequest + InfoRequestBatch + ].freeze + + validates :resource, presence: true + validates :resource_type, inclusion: { in: RESOURCE_TYPES } +end diff --git a/app/models/dataset/value.rb b/app/models/dataset/value.rb new file mode 100644 index 0000000000..823f92b8be --- /dev/null +++ b/app/models/dataset/value.rb @@ -0,0 +1,26 @@ +# == Schema Information +# Schema version: 20200501183111 +# +# Table name: dataset_values +# +# id :integer not null, primary key +# dataset_value_set_id :integer +# dataset_key_id :integer +# value :string +# notes :string +# created_at :datetime not null +# updated_at :datetime not null +# + +## +# A dataset value representing an extracted value for a given key +# +class Dataset::Value < ApplicationRecord + belongs_to :value_set, foreign_key: 'dataset_value_set_id' + belongs_to :key, foreign_key: 'dataset_key_id' + + validates :value_set, :key, presence: true + validates :value, + format: { with: -> (value) { value.key&.format_regexp } }, + allow_blank: true +end diff --git a/app/models/dataset/value_set.rb b/app/models/dataset/value_set.rb new file mode 100644 index 0000000000..0244fc6f31 --- /dev/null +++ b/app/models/dataset/value_set.rb @@ -0,0 +1,41 @@ +# == Schema Information +# Schema version: 20200501183111 +# +# Table name: dataset_value_sets +# +# id :integer not null, primary key +# resource_type :string +# resource_id :integer +# dataset_key_set_id :integer +# created_at :datetime not null +# updated_at :datetime not null +# + +## +# A dataset collection of values +# +class Dataset::ValueSet < ApplicationRecord + belongs_to :resource, polymorphic: true + belongs_to :key_set, foreign_key: 'dataset_key_set_id' + has_many :values, foreign_key: 'dataset_value_set_id', inverse_of: :value_set + + accepts_nested_attributes_for :values + + RESOURCE_TYPES = %w[ + InfoRequest + IncomingMessage + FoiAttachment + ].freeze + + validates :key_set, :values, presence: true + validates :resource_type, inclusion: { in: RESOURCE_TYPES }, if: :resource + validates_associated :values + validate :check_at_least_one_value_is_present + + private + + def check_at_least_one_value_is_present + return unless values.map(&:value).all?(&:blank?) + errors.add :values, :emtpy + end +end diff --git a/app/models/foi_attachment.rb b/app/models/foi_attachment.rb index 048f8a0005..11de50c308 100644 --- a/app/models/foi_attachment.rb +++ b/app/models/foi_attachment.rb @@ -36,6 +36,8 @@ class FoiAttachment < ApplicationRecord before_validation :ensure_filename!, :only => [:filename] before_destroy :delete_cached_file! + scope :binary, -> { where.not(content_type: AlaveteliTextMasker::TextMask) } + BODY_MAX_TRIES = 3 BODY_MAX_DELAY = 5 @@ -58,7 +60,7 @@ def delete_cached_file! def body=(d) self.hexdigest = Digest::MD5.hexdigest(d) - if !File.exists?(self.directory) + if !File.exist?(self.directory) FileUtils.mkdir_p self.directory end File.open(self.filepath, "wb") { |file| diff --git a/app/models/holiday_import.rb b/app/models/holiday_import.rb index b963e26aa1..4687a9b342 100644 --- a/app/models/holiday_import.rb +++ b/app/models/holiday_import.rb @@ -96,7 +96,7 @@ def populate_from_suggestions holidays << Holiday.new(:description => holiday_info_hash[:name], :day => holiday_info_hash[:date]) end - rescue Holidays::UnknownRegionError + rescue Holidays::InvalidRegion [] end end diff --git a/app/models/incoming_message.rb b/app/models/incoming_message.rb index ab2c6beeda..d4856f85b6 100644 --- a/app/models/incoming_message.rb +++ b/app/models/incoming_message.rb @@ -260,6 +260,31 @@ def self.get_attachment_by_url_part_number_and_filename(attachments, found_url_p end end + def self.get_attachment_by_url_part_number_and_filename!( + attachments, found_url_part_number, display_filename + ) + attachment = get_attachment_by_url_part_number_and_filename( + attachments, found_url_part_number, display_filename + ) + + return unless attachment + + # check filename in URL matches that in database (use a censor rule if you + # want to change a filename) + if attachment.display_filename != display_filename && + attachment.old_display_filename != display_filename + msg = 'please use same filename as original file has, display: ' + msg += "'#{ attachment.display_filename }' " + msg += 'old_display: ' + msg += "'#{ attachment.old_display_filename }' " + msg += 'original: ' + msg += "'#{ display_filename }'" + raise ActiveRecord::RecordNotFound, msg + end + + attachment + end + def apply_masks(text, content_type) mask_options = { :censor_rules => info_request.applicable_censor_rules, :masks => info_request.masks } @@ -510,9 +535,9 @@ def _uudecode_and_save_attachments(text) end hexdigest = Digest::MD5.hexdigest(content) attachment = foi_attachments.find_or_create_by(:hexdigest => hexdigest) - attachment.update_attributes(:filename => filename, - :content_type => content_type, - :body => content) + attachment.update(:filename => filename, + :content_type => content_type, + :body => content) attachment.save! attachments << attachment end @@ -537,7 +562,7 @@ def extract_attachments! attachments = [] attachment_attributes.each do |attrs| attachment = self.foi_attachments.find_or_create_by(:hexdigest => attrs[:hexdigest]) - attachment.update_attributes(attrs) + attachment.update(attrs) attachment.save! attachments << attachment end diff --git a/app/models/info_request.rb b/app/models/info_request.rb index c660ae9351..c3494e7246 100644 --- a/app/models/info_request.rb +++ b/app/models/info_request.rb @@ -125,7 +125,7 @@ class InfoRequest < ApplicationRecord :inverse_of => :info_request, :dependent => :destroy has_many :mail_server_logs, - -> { order('mail_server_log_done_id, "order"') }, + -> { order(:mail_server_log_done_id, :order) }, :inverse_of => :info_request, :dependent => :destroy has_one :embargo, @@ -133,6 +133,16 @@ class InfoRequest < ApplicationRecord :class_name => 'AlaveteliPro::Embargo', :dependent => :destroy + has_many :foi_attachments, through: :incoming_messages + + has_many :project_submissions, class_name: 'Project::Submission' + has_many :classification_project_submissions, + -> { classification }, + class_name: 'Project::Submission' + has_many :extraction_project_submissions, + -> { extraction }, + class_name: 'Project::Submission' + attr_accessor :is_batch_request_template attr_reader :followup_bad_reason @@ -156,6 +166,8 @@ class InfoRequest < ApplicationRecord scope :overdue, State::OverdueQuery.new scope :very_overdue, State::VeryOverdueQuery.new + scope :for_project, Project::InfoRequestQuery.new + class << self alias_method :in_progress, :awaiting_response end @@ -1616,7 +1628,7 @@ def move_to_public_body(destination_public_body, opts = {}) }) end - return_val = if update_attributes(attrs) + return_val = if update(attrs) log_event('move_request', :editor => editor, :public_body_url_name => public_body.url_name, @@ -1641,7 +1653,7 @@ def move_to_user(destination_user, opts = {}) old_user = user editor = opts.fetch(:editor) - return_val = if update_attributes(:user => destination_user) + return_val = if update(:user => destination_user) log_event('move_request', :editor => editor, :user_url_name => user.url_name, diff --git a/app/models/info_request_batch.rb b/app/models/info_request_batch.rb index a97389a614..eadabde4c7 100644 --- a/app/models/info_request_batch.rb +++ b/app/models/info_request_batch.rb @@ -280,4 +280,9 @@ def should_summarise? def log_event(*args) info_requests.map { |request| request.log_event(*args) } end + + def is_owning_user?(user) + return false unless user + user.id == user_id || user.owns_every_request? + end end diff --git a/app/models/mail_server_log.rb b/app/models/mail_server_log.rb index f0a159af49..dc124c88e2 100644 --- a/app/models/mail_server_log.rb +++ b/app/models/mail_server_log.rb @@ -271,11 +271,12 @@ def set_delivery_status(force = false) end def force_delivery_status(new_status) - # write the value without checking the old (invalid) value, avoiding - # the unintended ArgumentError caused by reading the old value - raw_write_attribute(:delivery_status, new_status) + # write the value without checking the old (invalid) value, avoiding the + # unintended ArgumentError caused by reading the old value + write_attribute_without_type_cast(:delivery_status, new_status) + # record the new value in `changes` so that save will have something - # to do as raw_write_attribute just updates the value + # to do as write_attribute_without_type_cast just updates the value delivery_status_will_change! end diff --git a/app/models/outgoing_message/template/internal_review.rb b/app/models/outgoing_message/template/internal_review.rb index 4656fc89db..dd3684e3f2 100644 --- a/app/models/outgoing_message/template/internal_review.rb +++ b/app/models/outgoing_message/template/internal_review.rb @@ -27,7 +27,7 @@ def letter(replacements = {}) "'{{info_request_title}}'.", replacements) msg += "\n\n\n\n" - msg += " [ #{ self.class.details_placeholder } ] " + msg += " [ #{ self.class.details_placeholder } ]" unless replacements[:embargo] msg += "\n\n\n\n" diff --git a/app/models/profile_photo.rb b/app/models/profile_photo.rb index 464ca00065..ebddd15a8f 100644 --- a/app/models/profile_photo.rb +++ b/app/models/profile_photo.rb @@ -41,24 +41,31 @@ def convert_image # convert to PNG if it isn't, and to right size altered = false - if image.format != 'PNG' - self.image.format = 'PNG' + if image.type != 'PNG' + self.image.format('PNG') altered = true end # draft images are before the user has cropped them - if !draft && (image.columns != WIDTH || image.rows != HEIGHT) + if !draft && (image.width != WIDTH || image.height != HEIGHT) # do any exact cropping (taken from Jcrop interface) if w && h - image.crop!(x.to_i, y.to_i, w.to_i, h.to_i) + image.crop("#{ w }x#{ h }+#{ x }+#{ y }") end # do any further cropping - image.resize_to_fill!(WIDTH, HEIGHT) + # resize_to_fill! + image.combine_options do |c| + c.thumbnail("#{ WIDTH }x#{ HEIGHT }^") + c.gravity 'center' + c.extent("#{ WIDTH }x#{ HEIGHT }") + end + altered = true end - if draft && (image.columns > MAX_DRAFT || image.rows > MAX_DRAFT) - image.resize_to_fit!(MAX_DRAFT, MAX_DRAFT) + if draft && (image.width > MAX_DRAFT || image.height > MAX_DRAFT) + # resize_to_fit! + image.resize("#{ MAX_DRAFT }x#{ MAX_DRAFT }") altered = true end @@ -80,14 +87,14 @@ def data_and_draft_checks return end - if image.format != 'PNG' + if image.type != 'PNG' errors.add(:data, _("Failed to convert image to a PNG")) end - if !draft && (image.columns != WIDTH || image.rows != HEIGHT) + if !draft && (image.width != WIDTH || image.height != HEIGHT) errors.add(:data, _("Failed to convert image to the correct size: at {{cols}}x{{rows}}, need {{width}}x{{height}}", - :cols => image.columns, - :rows => image.rows, + :cols => image.width, + :rows => image.height, :width => WIDTH, :height => HEIGHT)) end @@ -108,18 +115,16 @@ def convert_data_to_image return end - image_list = Magick::ImageList.new - begin - image_list.from_blob(data) - rescue Magick::ImageMagickError + converted = MiniMagick::Image.read(data) + rescue MiniMagick::Invalid self.image = nil return end # TODO: perhaps take largest image or somesuch if there were multiple # in the file? - self.image = image_list[0] + self.image = converted convert_image end end diff --git a/app/models/project.rb b/app/models/project.rb new file mode 100644 index 0000000000..3f782c6d5b --- /dev/null +++ b/app/models/project.rb @@ -0,0 +1,81 @@ +# == Schema Information +# Schema version: 20200520073810 +# +# Table name: projects +# +# id :integer not null, primary key +# title :string +# briefing :text +# created_at :datetime not null +# updated_at :datetime not null +# invite_token :string +# + +## +# A model to represent a FOI project which many contributors work on multiple +# info requests. +# +class Project < ApplicationRecord + has_many :memberships, class_name: 'Project::Membership' + has_one :owner_membership, + -> { where(role: Role.project_owner_role) }, + class_name: 'Project::Membership' + has_many :contributor_memberships, + -> { where(role: Role.project_contributor_role) }, + class_name: 'Project::Membership' + + has_many :members, through: :memberships, source: :user + has_one :owner, through: :owner_membership, source: :user + has_many :contributors, through: :contributor_memberships, source: :user + + has_many :resources, class_name: 'Project::Resource' + has_many :requests, + through: :resources, + source: :resource, + source_type: 'InfoRequest' + has_many :batches, + through: :resources, + source: :resource, + source_type: 'InfoRequestBatch' + + has_many :info_requests, + ->(project) { unscope(:where).for_project(project) }, + extend: Project::InfoRequestExtension + + has_one :key_set, class_name: 'Dataset::KeySet', as: :resource + + has_many :submissions, class_name: 'Project::Submission' + + validates :title, :owner, presence: true + + def info_request?(info_request) + info_requests.include?(info_request) + end + + def owner?(user) + user == owner + end + + def member?(user) + members.include?(user) + end + + def contributor?(user) + contributors.include?(user) + end + + def classification_progress + total = info_requests.count + return 0 if total.zero? + + ((info_requests.classified.count / total.to_f) * 100).floor + end + + def extraction_progress + extracted_count = info_requests.extracted.count + total = extracted_count + info_requests.extractable.count + return 0 if total.zero? + + ((extracted_count / total.to_f) * 100).floor + end +end diff --git a/app/models/project/export.rb b/app/models/project/export.rb new file mode 100644 index 0000000000..dcbfc50107 --- /dev/null +++ b/app/models/project/export.rb @@ -0,0 +1,38 @@ +## +# Export a project's info request classifications and data extractions to CSV. +# +class Project::Export + require_dependency 'project/export/info_request' + + include DownloadHelper + + attr_reader :project + protected :project + + def initialize(project) + @project = project + end + + def data + @data ||= project.info_requests.extracted.map do |info_request| + Project::Export::InfoRequest.new(project, info_request).data + end + end + + def name + generate_download_filename( + resource: 'project', + id: project.id, + title: project.title, + ext: 'csv' + ) + end + + def to_csv + CSV.generate do |csv| + header = data.first + csv << header.keys.map(&:to_s) if header + data.each { |row| csv << row.values } + end + end +end diff --git a/app/models/project/export/info_request.rb b/app/models/project/export/info_request.rb new file mode 100644 index 0000000000..d5563b74d3 --- /dev/null +++ b/app/models/project/export/info_request.rb @@ -0,0 +1,63 @@ +## +# Export key classification and extracted data for an info request within a +# given project. +# +class Project::Export::InfoRequest < SimpleDelegator + include Rails.application.routes.url_helpers + include LinkToHelper + default_url_options[:host] = AlaveteliConfiguration.domain + + attr_reader :project + protected :project + + def initialize(project, info_request) + @project = project + super(info_request) + end + + def data + { + request_url: request_url(self), + request_title: title, + public_body_name: public_body.name, + request_owner: user&.name, + latest_status_contributor: status_contributor, + status: described_state, + dataset_contributor: dataset_contributor + }.merge(extracted_values_as_hash) + end + + private + + def submissions + project.submissions.where(info_request: id) + end + + def status_submission + submissions.classification.last + end + + def extraction_submission + submissions.extraction.last + end + + def status_contributor + return project.owner.name unless status_submission + status_submission.user.name + end + + def dataset_contributor + return unless extraction_submission + extraction_submission.user.name + end + + def extracted_values + return unless extraction_submission + extraction_submission.resource.values + end + + def extracted_values_as_hash + return {} unless extracted_values + extracted_values.joins(:key).pluck('dataset_keys.title', :value).to_h + end +end diff --git a/app/models/project/info_request_extension.rb b/app/models/project/info_request_extension.rb new file mode 100644 index 0000000000..42dba46a63 --- /dev/null +++ b/app/models/project/info_request_extension.rb @@ -0,0 +1,28 @@ +class Project + ## + # An ActiveRecord association extensions with extra InfoRequest scopes for + # projects + # + module InfoRequestExtension + EXTRACTABLE_STATES = %w(successful partially_successful) + + def classifiable + where(awaiting_description: true) + end + + def classified + where(awaiting_description: false) + end + + def extractable + where(described_state: EXTRACTABLE_STATES). + left_joins(:extraction_project_submissions). + where(project_submissions: { id: nil }). + classified + end + + def extracted + joins(:extraction_project_submissions).distinct + end + end +end diff --git a/app/models/project/info_request_query.rb b/app/models/project/info_request_query.rb new file mode 100644 index 0000000000..de5a748eb8 --- /dev/null +++ b/app/models/project/info_request_query.rb @@ -0,0 +1,26 @@ +class Project + ## + # A association query between Project and InfoRequest. This is complex than a + # standard association as InfoRequest can belong to Project directly or via an + # InfoRequestBatch + # + class InfoRequestQuery + def initialize(relation = InfoRequest) + @relation = relation + end + + def call(project) + table = Project::Resource.table_name + + @relation.joins(<<~JOIN).where(table => { project_id: project }) + INNER JOIN #{table} ON ( + #{table}.resource_id = info_requests.id AND + #{table}.resource_type = 'InfoRequest' + ) OR ( + #{table}.resource_id = info_requests.info_request_batch_id AND + #{table}.resource_type = 'InfoRequestBatch' + ) + JOIN + end + end +end diff --git a/app/models/project/membership.rb b/app/models/project/membership.rb new file mode 100644 index 0000000000..2b9cbc50c5 --- /dev/null +++ b/app/models/project/membership.rb @@ -0,0 +1,24 @@ +# == Schema Information +# Schema version: 20200520073810 +# +# Table name: project_memberships +# +# id :integer not null, primary key +# project_id :integer +# user_id :integer +# role_id :integer +# created_at :datetime not null +# updated_at :datetime not null +# + +## +# A model to represent user membership to a project. Able to assign different +# roles for owners and contributors. +# +class Project::Membership < ApplicationRecord + belongs_to :project + belongs_to :user + belongs_to :role + + validates :project, :user, :role, presence: true +end diff --git a/app/models/project/queue.rb b/app/models/project/queue.rb new file mode 100644 index 0000000000..adb5c169c5 --- /dev/null +++ b/app/models/project/queue.rb @@ -0,0 +1,69 @@ +require_dependency 'project/queue/session_backend' + +# Gives the logged in User a new InfoRequest to contribute to. +class Project::Queue + extend Forwardable + + def self.classifiable(project, session) + backend = SessionBackend.primed(session, project, :classifiable) + new(project.info_requests.classifiable, backend) + end + + def self.extractable(project, session) + backend = SessionBackend.primed(session, project, :extractable) + new(project.info_requests.extractable, backend) + end + + def_delegators :backend, :skip, :reset + + def initialize(info_requests, backend) + @info_requests = info_requests + @backend = backend + end + + def next + find_and_remember_next + end + + def include?(info_request) + info_requests.include?(info_request) + end + + def ==(other) + info_requests == other.info_requests && backend == other.backend + end + + protected + + attr_reader :info_requests, :backend + + private + + def find_and_remember_next + find_next { |info_request| remember_current(info_request) } + end + + def find_next + request = find_current || sample + yield(request) if block_given? + request + end + + def find_current + id = backend.current + unskipped_requests.find_by(id: id) if id + end + + def sample + unskipped_requests.sample + end + + def remember_current(info_request) + return backend.clear_current unless info_request + backend.current = info_request + end + + def unskipped_requests + info_requests.where.not(id: backend.skipped) + end +end diff --git a/app/models/project/queue/session_backend.rb b/app/models/project/queue/session_backend.rb new file mode 100644 index 0000000000..9d64042baa --- /dev/null +++ b/app/models/project/queue/session_backend.rb @@ -0,0 +1,71 @@ +require_dependency 'project/queue' + +class Project::Queue + # Stores state for a Project::Queue in the session + class SessionBackend + def self.primed(session, project, queue_name) + project_id = project.to_param + queue_name = queue_name.to_s + + session['projects'] ||= {} + session['projects'][project_id] ||= {} + session['projects'][project_id][queue_name] ||= {} + session['projects'][project_id][queue_name]['current'] ||= nil + session['projects'][project_id][queue_name]['skipped'] ||= [] + + new(session, project_id: project_id, queue_name: queue_name) + end + + def initialize(session, project_id:, queue_name:) + @session = session + @project_id = project_id + @queue_name = queue_name + end + + def current=(id) + queue['current'] = id.to_param + end + + def current + queue['current'] + end + + def clear_current + queue['current'] = nil + end + + def skip(id) + skipped << id.to_param + end + + def skipped + queue['skipped'] + end + + def clear_skipped + skipped.clear + end + + def reset + clear_current + clear_skipped + true + end + + def ==(other) + session == other.session && + project_id == other.project_id && + queue_name == other.queue_name + end + + protected + + attr_reader :session, :project_id, :queue_name + + private + + def queue + session['projects'][project_id][queue_name] + end + end +end diff --git a/app/models/project/resource.rb b/app/models/project/resource.rb new file mode 100644 index 0000000000..1db2310841 --- /dev/null +++ b/app/models/project/resource.rb @@ -0,0 +1,23 @@ +# == Schema Information +# Schema version: 20200520073810 +# +# Table name: project_resources +# +# id :integer not null, primary key +# project_id :integer +# resource_type :string +# resource_id :integer +# created_at :datetime not null +# updated_at :datetime not null +# + +## +# A model to represent a project resource. These will be either info requests or +# info request batches. +# +class Project::Resource < ApplicationRecord + belongs_to :project + belongs_to :resource, polymorphic: true + + validates :project, :resource, presence: true +end diff --git a/app/models/project/submission.rb b/app/models/project/submission.rb new file mode 100644 index 0000000000..a2cddac92c --- /dev/null +++ b/app/models/project/submission.rb @@ -0,0 +1,37 @@ +# == Schema Information +# Schema version: 20200520073810 +# +# Table name: project_submissions +# +# id :integer not null, primary key +# project_id :integer +# user_id :integer +# resource_type :string +# resource_id :integer +# created_at :datetime not null +# updated_at :datetime not null +# info_request_id :integer +# + +## +# A submission to a Project by one of its members. Can either be an Info Request +# status update (Classification) or an extraction of data (Dataset::ValueSet). +# +class Project::Submission < ApplicationRecord + belongs_to :project + belongs_to :user + belongs_to :info_request + belongs_to :resource, polymorphic: true + + scope :classification, -> { where(resource_type: 'InfoRequestEvent') } + scope :extraction, -> { where(resource_type: 'Dataset::ValueSet') } + + RESOURCE_TYPES = %w[ + InfoRequestEvent + Dataset::ValueSet + ].freeze + + validates :project, :user, :info_request, :resource, presence: true + validates :resource_type, inclusion: { in: RESOURCE_TYPES } + validates_associated :resource +end diff --git a/app/models/public_body.rb b/app/models/public_body.rb index cd1988ea2c..319f3cc8f0 100644 --- a/app/models/public_body.rb +++ b/app/models/public_body.rb @@ -266,7 +266,8 @@ def self.find_by_url_name_with_historic(name) # public bodies in it old = PublicBody::Version. where(:url_name => name). - pluck('DISTINCT public_body_id') + distinct. + pluck(:public_body_id) # Maybe return the first one, so we show something relevant, # rather than throwing an error? @@ -874,7 +875,7 @@ def self.with_query(query, tag) if DatabaseCollation.supports?(underscore_locale) where(where_condition, where_parameters). joins(:translations). - order(%Q(public_body_translations.name COLLATE "#{underscore_locale}")) + order(Arel.sql(%Q(public_body_translations.name COLLATE "#{underscore_locale}"))) else where(where_condition, where_parameters). joins(:translations). diff --git a/app/models/public_body_heading.rb b/app/models/public_body_heading.rb index 97eafd1630..3faa8e1997 100644 --- a/app/models/public_body_heading.rb +++ b/app/models/public_body_heading.rb @@ -11,7 +11,7 @@ class PublicBodyHeading < ApplicationRecord has_many :public_body_category_links, - :inverse_of => :public_body_category, + :inverse_of => :public_body_heading, :dependent => :destroy has_many :public_body_categories, -> { order('public_body_category_links.category_display_order') }, diff --git a/app/models/raw_email.rb b/app/models/raw_email.rb index 1208a47f95..094719135a 100644 --- a/app/models/raw_email.rb +++ b/app/models/raw_email.rb @@ -95,7 +95,7 @@ def mail! end def data=(d) - FileUtils.mkdir_p(directory) unless File.exists?(directory) + FileUtils.mkdir_p(directory) unless File.exist?(directory) File.atomic_write(filepath) do |file| file.binmode file.write(d) @@ -119,7 +119,7 @@ def data_as_text end def destroy_file_representation! - File.delete(filepath) if File.exists?(filepath) + File.delete(filepath) if File.exist?(filepath) end def from_name diff --git a/app/models/role.rb b/app/models/role.rb index ee2561b4d6..7d1c95663e 100644 --- a/app/models/role.rb +++ b/app/models/role.rb @@ -15,33 +15,33 @@ class Role < ApplicationRecord extend AlaveteliFeatures::Helpers has_and_belongs_to_many :users, - :join_table => :users_roles, - :inverse_of => :roles + join_table: :users_roles, + inverse_of: :roles belongs_to :resource, - :polymorphic => true + polymorphic: true validates :resource_type, - :inclusion => { :in => Rolify.resource_types }, - :allow_nil => true + inclusion: { in: Rolify.resource_types }, + allow_nil: true scopify - - ROLES = ['admin'].freeze - PRO_ROLES = ['pro', 'pro_admin'].freeze + ROLES = %w[admin].freeze + PRO_ROLES = %w[pro pro_admin].freeze + PROJECT_ROLES = %w[project_owner project_contributor].freeze def self.allowed_roles - if feature_enabled? :alaveteli_pro - ROLES + PRO_ROLES - else - ROLES + [].tap do |allowed| + allowed.push(*ROLES) + allowed.push(*PRO_ROLES) if feature_enabled? :alaveteli_pro + allowed.push(*PROJECT_ROLES) if feature_enabled? :projects end end validates :name, - :inclusion => { :in => lambda { |role| Role.allowed_roles } }, - :uniqueness => { :scope => :resource_type } + inclusion: { in: -> (_name) { Role.allowed_roles } }, + uniqueness: { scope: :resource_type } def self.admin_role Role.find_by(name: 'admin') @@ -51,6 +51,24 @@ def self.pro_role Role.find_by(name: 'pro') end + def self.project_owner_role + Role.find_by(name: 'project_owner') + end + + def self.project_contributor_role + Role.find_by(name: 'project_contributor') + end + + # Public: Returns an array of symbols of the names of the roles + # which can be granted or revoked + # + # Returns an Array + def self.grantable_roles + allowed_roles.flat_map do |role| + grants_and_revokes(role.to_sym) + end.compact.uniq + end + # Public: Returns an array of symbols of the names of the roles # this role can grant and revoke # @@ -59,8 +77,8 @@ def self.pro_role # Returns an Array def self.grants_and_revokes(role) grants_and_revokes = { - :admin => [:admin], - :pro_admin => [:pro, :admin, :pro_admin] + admin: [:admin], + pro_admin: [:pro, :admin, :pro_admin] } grants_and_revokes[role] || [] end @@ -72,7 +90,6 @@ def self.grants_and_revokes(role) # # Returns an Array def self.requires(role) - { :pro_admin => [:admin] }[role] || [] + { pro_admin: [:admin] }[role] || [] end - end diff --git a/app/models/spam_address.rb b/app/models/spam_address.rb index 80b189818f..e0613c68fe 100644 --- a/app/models/spam_address.rb +++ b/app/models/spam_address.rb @@ -10,11 +10,23 @@ # class SpamAddress < ApplicationRecord - validates_presence_of :email, :message => 'Please enter the email address to mark as spam' - validates_uniqueness_of :email, :message => 'This address is already marked as spam' + validates_presence_of :email, + message: 'Enter the email address to mark as spam' + + validates_uniqueness_of :email, + message: 'This address is already marked as spam' + + strip_attributes + + before_save :downcase_email def self.spam?(email_address) - exists?(:email => email_address) + exists?(email: Array(email_address).compact.map(&:downcase)) end + private + + def downcase_email + email.downcase! + end end diff --git a/app/models/user.rb b/app/models/user.rb index 73350e7b9e..df4e989531 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -124,6 +124,8 @@ class User < ApplicationRecord has_many :announcement_dismissals, :inverse_of => :user, :dependent => :destroy + has_many :memberships, class_name: 'Project::Membership' + has_many :projects, through: :memberships scope :active, -> { not_banned.not_closed } scope :banned, -> { where.not(ban_text: "") } @@ -363,12 +365,6 @@ def locale (super || AlaveteliLocalization.locale).to_s end - def get_locale - warn %q([DEPRECATION] User#get_locale will be removed in 0.38. - It has been replaced by User#locale).squish - locale - end - def name _name = read_attribute(:name) if suspended? @@ -569,6 +565,10 @@ def set_profile_photo(new_profile_photo) end end + def show_profile_photo? + active? && profile_photo + end + def about_me_already_exists? return false if about_me.blank? self.class.where(:about_me => about_me).where.not(id: id).any? diff --git a/app/services/alaveteli_pro/access.rb b/app/services/alaveteli_pro/access.rb index 5bab598c3e..fc16e6d708 100644 --- a/app/services/alaveteli_pro/access.rb +++ b/app/services/alaveteli_pro/access.rb @@ -28,6 +28,5 @@ def grant end enable_actor(:notifications, user) - enable_actor(:pro_batch_access, user) end end diff --git a/app/services/info_request_batch_metrics.rb b/app/services/info_request_batch_metrics.rb index 5aa5a0bf81..5915bb1d6e 100644 --- a/app/services/info_request_batch_metrics.rb +++ b/app/services/info_request_batch_metrics.rb @@ -2,9 +2,13 @@ # This service returns basic metric information for a given info request batch. # class InfoRequestBatchMetrics + include DownloadHelper + include Rails.application.routes.url_helpers default_url_options[:host] = AlaveteliConfiguration.domain + attr_reader :info_request_batch + def initialize(info_request_batch) @info_request_batch = info_request_batch end @@ -26,13 +30,13 @@ def metrics end def name - id = @info_request_batch.id - url_title = MySociety::Format.simplify_url_part( - @info_request_batch.title, 'batch', 32 + generate_download_filename( + resource: 'batch', + id: info_request_batch.id, + title: info_request_batch.title, + type: 'dashboard', + ext: 'csv' ) - timestamp = Time.zone.now.to_formatted_s(:iso8601) - - "batch-#{id}-#{url_title}-dashboard-#{timestamp}.csv" end def to_csv diff --git a/app/services/info_request_batch_zip.rb b/app/services/info_request_batch_zip.rb new file mode 100644 index 0000000000..a9b740f674 --- /dev/null +++ b/app/services/info_request_batch_zip.rb @@ -0,0 +1,122 @@ +## +# This service streams a ZIP file with all incoming/ outgoing messages and +# attachments for each request for a given info request batch +# +class InfoRequestBatchZip + include DownloadHelper + include Enumerable + + ZippableFile = Struct.new(:path, :body) + + attr_reader :info_request_batch, :ability + + delegate :cannot?, to: :ability + + def initialize(info_request_batch, ability:) + @info_request_batch = info_request_batch + @ability = ability + end + + def files + to_a + end + + def name + generate_download_filename( + resource: 'batch', + id: info_request_batch.id, + title: info_request_batch.title, + type: 'export', + ext: 'zip' + ) + end + + def stream(&chunks) + block_writer = ZipTricks::BlockWrite.new(&chunks) + + ZipTricks::Streamer.open( + block_writer, + auto_rename_duplicate_filenames: true + ) do |zip| + each do |file| + zip.write_deflated_file(file.path) { |writer| writer << file.body } + end + end + end + + private + + def each(&_block) + to_enum(:each) unless block_given? + + yield prepare_dashboard_metrics + + info_request_events.each do |event| + if event.outgoing? + message = prepare_outgoing_message(event.outgoing_message) + yield message if message + + elsif event.response? + message = prepare_incoming_message(event.incoming_message) + yield message if message + + event.incoming_message.get_attachments_for_display.each do |attachment| + attachment = prepare_foi_attachment(attachment) + yield attachment if attachment + end + end + end + end + + def info_request_events + InfoRequestEvent.where(info_request: info_request_batch.info_requests). + includes(:info_request, info_request: [:public_body]). + includes(:outgoing_message). + includes(:incoming_message, incoming_message: [:foi_attachments]) + end + + def prepare_dashboard_metrics + metrics = InfoRequestBatchMetrics.new(info_request_batch) + ZippableFile.new(metrics.name, metrics.to_csv) + end + + def prepare_outgoing_message(message) + return if cannot?(:read, message) + + sent_at = message.last_sent_at.to_formatted_s(:filename) + name = "outgoing_#{message.id}.txt" + path = [base_path(message.info_request), sent_at, name].join('/') + + ZippableFile.new(path, message.body) + end + + def prepare_incoming_message(message) + return if cannot?(:read, message) + + sent_at = message.sent_at.to_formatted_s(:filename) + name = "incoming_#{message.id}.txt" + path = [base_path(message.info_request), sent_at, name].join('/') + + ZippableFile.new(path, message.get_main_body_text_unfolded) + end + + def prepare_foi_attachment(attachment) + message = attachment.incoming_message + return if cannot?(:read, message) + + sent_at = message.sent_at.to_formatted_s(:filename) + + path = [ + base_path(message.info_request), + sent_at, + "attachments-#{message.id}", + attachment.filename + ].join('/') + + ZippableFile.new(path, attachment.body) + end + + def base_path(info_request) + [info_request.public_body.name, info_request.url_title].join(' - ') + end +end diff --git a/app/views/admin_user/_form.html.erb b/app/views/admin_user/_form.html.erb index 6df0c35395..523dc9e774 100644 --- a/app/views/admin_user/_form.html.erb +++ b/app/views/admin_user/_form.html.erb @@ -26,7 +26,7 @@
Roles
- <% Role.where(:name => Role.allowed_roles).order(:name).each do |role| %> + <% Role.where(name: Role.grantable_roles).order(:name).each do |role| %>

diff --git a/app/views/request/events/_comment.html.erb b/app/views/request/events/_comment.html.erb new file mode 100644 index 0000000000..4ee6e3973f --- /dev/null +++ b/app/views/request/events/_comment.html.erb @@ -0,0 +1,3 @@ +<%= render partial: 'comment/single_comment', + locals: { comment: info_request_event.comment, + show_correspondence_footer: show_correspondence_footer } %> diff --git a/app/views/request/events/_followup_resent.html.erb b/app/views/request/events/_followup_resent.html.erb new file mode 100644 index 0000000000..cff57e0471 --- /dev/null +++ b/app/views/request/events/_followup_resent.html.erb @@ -0,0 +1,4 @@ +<%= render partial: 'request/resent_outgoing_correspondence', + locals: { outgoing_message: info_request_event.outgoing_message, + info_request_event: info_request_event, + show_correspondence_footer: show_correspondence_footer } %> diff --git a/app/views/request/events/_followup_sent.html.erb b/app/views/request/events/_followup_sent.html.erb new file mode 100644 index 0000000000..8019a522bc --- /dev/null +++ b/app/views/request/events/_followup_sent.html.erb @@ -0,0 +1,4 @@ +<%= render partial: 'request/outgoing_correspondence', + locals: { outgoing_message: info_request_event.outgoing_message, + info_request_event: info_request_event, + show_correspondence_footer: show_correspondence_footer } %> diff --git a/app/views/request/events/_resent.html.erb b/app/views/request/events/_resent.html.erb new file mode 100644 index 0000000000..a6c97660ba --- /dev/null +++ b/app/views/request/events/_resent.html.erb @@ -0,0 +1,3 @@ +<%= render partial: 'request/resent_outgoing_correspondence', + locals: { outgoing_message: info_request_event.outgoing_message, + info_request_event: info_request_event } %> diff --git a/app/views/request/events/_response.html.erb b/app/views/request/events/_response.html.erb new file mode 100644 index 0000000000..a893fc2052 --- /dev/null +++ b/app/views/request/events/_response.html.erb @@ -0,0 +1,3 @@ +<%= render partial: 'request/incoming_correspondence', + locals: { incoming_message: info_request_event.incoming_message, + show_correspondence_footer: show_correspondence_footer } %> diff --git a/app/views/request/events/_send_error.html.erb b/app/views/request/events/_send_error.html.erb new file mode 100644 index 0000000000..8019a522bc --- /dev/null +++ b/app/views/request/events/_send_error.html.erb @@ -0,0 +1,4 @@ +<%= render partial: 'request/outgoing_correspondence', + locals: { outgoing_message: info_request_event.outgoing_message, + info_request_event: info_request_event, + show_correspondence_footer: show_correspondence_footer } %> diff --git a/app/views/request/events/_sent.html.erb b/app/views/request/events/_sent.html.erb new file mode 100644 index 0000000000..8019a522bc --- /dev/null +++ b/app/views/request/events/_sent.html.erb @@ -0,0 +1,4 @@ +<%= render partial: 'request/outgoing_correspondence', + locals: { outgoing_message: info_request_event.outgoing_message, + info_request_event: info_request_event, + show_correspondence_footer: show_correspondence_footer } %> diff --git a/app/views/request/show.html.erb b/app/views/request/show.html.erb index 50ca6808af..2a09a7ef02 100644 --- a/app/views/request/show.html.erb +++ b/app/views/request/show.html.erb @@ -1,79 +1,52 @@ -<% @title = _("{{title}} - a Freedom of Information request to {{public_body}}", - :title => h(@info_request.title), - :public_body => (@info_request.public_body.name)) %> +<% @title = _('{{title}} - a Freedom of Information request to {{public_body}}', + title: h(@info_request.title), + public_body: (@info_request.public_body.name)) %> -<%= render :partial => 'request_meta_tags' %> +<%= render partial: 'request/request_meta_tags' %> -<% if flash[:request_sent] %><%= render :partial => 'request_sent' %><% end %> +<% if flash[:request_sent] %> + <%= render partial: 'request_sent' %> +<% end %> -<%= render :partial => 'ga_events' %> +<%= render partial: 'request/ga_events' %> -<%= render :partial => 'hidden_request_messages' %> +<%= render partial: 'request/hidden_request_messages' %> <% if @show_top_describe_state_form %>

- <%= render :partial => 'describe_state', :locals => { :id_suffix => "1" } %> + <%= render partial: 'request/describe_state', locals: { id_suffix: '1' } %>
<% end %> -
-
- -

<%=h(@info_request.title)%>

- -
-
- <% if @show_profile_photo %> -
- -
- <% end %> -

- <%= render :partial => 'request_subtitle' %> -

- <% unless @render_to_file %> -
- <% if @in_pro_area %> - <%= render :partial => 'alaveteli_pro/info_requests/after_actions' %> - <% else %> - <%= render :partial => 'after_actions' %> - <%= render :partial => 'track/tracking_links_simple', - :locals => { - :track_thing => @track_thing, - :own_request => @info_request.user && \ - @info_request.user == @user, - :location => 'toolbar ' } %> - <% end %> -
- <% end %> -
-
-
-
- -

<%= status_text(@info_request, - :new_responses_count => @new_responses_count, - :is_owning_user => @is_owning_user, - :render_to_file => @render_to_file, - :old_unclassified => @old_unclassified, - :redirect_to => request.fullpath) %> -

-
-
-
-
+<%= render partial: 'request/request_header', + locals: { info_request: @info_request, + user: @user, + follower_count: @follower_count, + in_pro_area: @in_pro_area, + track_thing: @track_thing, + show_profile_photo: @show_profile_photo, + show_action_menu: !@render_to_file, + new_responses_count: @new_responses_count, + is_owning_user: @is_owning_user, + render_to_file: @render_to_file, + show_owner_update_status_action: @show_owner_update_status_action, + show_other_user_update_status_action: @show_other_user_update_status_action, + last_response: @last_response, + old_unclassified: @old_unclassified } %>
- - <% for info_request_event in @info_request_events %> + <% @info_request.info_request_events.each do |info_request_event| %> <% if info_request_event.visible %> - <%= render :partial => 'correspondence', :locals => { :info_request_event => info_request_event } %> + <%= render partial: 'request/correspondence', + locals: { info_request_event: info_request_event, + show_correspondence_footer: !@info_request.embargo } %> <% end %> <% end %> <% if @show_bottom_describe_state_form %>
- <%= render :partial => 'describe_state', :locals => { :id_suffix => "2" } %> + <%= render partial: 'request/describe_state', + locals: { id_suffix: '2' } %>
<% end %> @@ -81,18 +54,28 @@ <% end %> -
<%- if @sidebar %> diff --git a/app/views/request/show.text.erb b/app/views/request/show.text.erb index 70e882fdaa..a33e6217db 100644 --- a/app/views/request/show.text.erb +++ b/app/views/request/show.text.erb @@ -4,7 +4,7 @@ :request_title => @info_request.title, :full_url => "http://#{AlaveteliConfiguration::domain}#{show_request_path(:url_title=>@info_request.url_title)}") %>. -<% @info_request_events.each do |info_request_event| %> +<% @info_request.info_request_events.each do |info_request_event| %> <% if info_request_event.visible %> <% case info_request_event.event_type %> <% when 'response' %> diff --git a/app/views/request_mailer/requires_admin.text.erb b/app/views/request_mailer/requires_admin.text.erb index c1ec7404a9..00bad2f5c2 100644 --- a/app/views/request_mailer/requires_admin.text.erb +++ b/app/views/request_mailer/requires_admin.text.erb @@ -7,8 +7,9 @@ :user_name => raw(@reported_by.name), :law_used => raw(@info_request.law_used_human(:short))) %> -<%= _("Request '{{request_title}}':", - :request_title => raw(@info_request.title)) %> +<%= _("Request '{{request_title}}' to {{public_body_name}}:", + :request_title => raw(@info_request.title), + :public_body_name => raw(@info_request.public_body.name)) %> <%= @url %> <%= _('Administration URL:') %> diff --git a/app/views/statistics/_people_leaderboard.html.erb b/app/views/statistics/_people_leaderboard.html.erb index 3e85c7c9a0..fb8fff54b6 100644 --- a/app/views/statistics/_people_leaderboard.html.erb +++ b/app/views/statistics/_people_leaderboard.html.erb @@ -3,7 +3,7 @@ <% users_with_counts.each do |user, count| %>
  • <%= link_to user, class: "leaderboard-person-details" do %> - <% if user.profile_photo %> + <% if user.show_profile_photo? %> <%= image_tag get_profile_photo_url(url_name: user.url_name), alt: _("Profile image for {{user_name}}", user_name: user.name) %> <% else %> <%= user.name.first.upcase %> diff --git a/app/views/track/_tracking_links_simple.html.erb b/app/views/track/_tracking_links_simple.html.erb index 899659b28d..cee493590a 100644 --- a/app/views/track/_tracking_links_simple.html.erb +++ b/app/views/track/_tracking_links_simple.html.erb @@ -1,6 +1,6 @@ <% existing_track = local_assigns.fetch(:existing_track) do - TrackThing.find_existing(@user, track_thing) if @user + TrackThing.find_existing(user, track_thing) if user end %> <% if !own_request %> @@ -17,8 +17,8 @@
    <%= n_("{{count}} follower", "{{count}} followers", - @follower_count, - :count => content_tag(:span, @follower_count, :id => "follow_count")) %> + follower_count, + :count => content_tag(:span, follower_count, :id => "follow_count")) %>
  • <% end %> diff --git a/app/views/user/_user_listing_single.html.erb b/app/views/user/_user_listing_single.html.erb index 9d592056c0..b432b99576 100644 --- a/app/views/user/_user_listing_single.html.erb +++ b/app/views/user/_user_listing_single.html.erb @@ -3,7 +3,7 @@ <% end %>
    - <% if display_user.profile_photo %> + <% if display_user.show_profile_photo? %>