diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 94ea6c68a4..c06e767725 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -7,87 +7,6 @@ updates: 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: icalendar - versions: - - ">= 2.6.0" - - dependency-name: launchy - versions: - - ">= 2.5.0" - - dependency-name: listen - versions: - - ">= 3.5.1" - - dependency-name: maxmind-db - versions: - - ">= 1.1.0" - - dependency-name: pry-byebug - versions: - - ">= 3.8.0" - - dependency-name: recaptcha - versions: - - ">= 5.7.0" - - dependency-name: rubocop - versions: - - ">= 0.82.0" - - dependency-name: rubocop-performance - versions: - - ">= 1.6.0" - - dependency-name: sass-rails - versions: - - ">= 5.0.8" - - dependency-name: simplecov - versions: - - ">= 0.18.0" - - # pry-byebug requires Ruby >= 2.4.0 - - dependency-name: pry - versions: - - ">= 0.13.0" - - # simplecov requires Ruby >= 2.4.0 - - dependency-name: simplecov-lcov - versions: - - ">= 0.8.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: rolify - versions: - - ">= 6.0.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" - # capistrano 3 is a major API change that we're unlikely to support - dependency-name: capistrano versions: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bc0c50e5ec..84b17f151d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -49,7 +49,7 @@ jobs: run: | sudo apt-get -y update sudo apt-get -y install exim4-daemon-light - sudo apt-get -y install `cut -d " " -f 1 config/packages.ubuntu-bionic | egrep -v "(^#|wkhtml|bundler|^ruby|^rake)"` + sudo apt-get -y install `cut -d " " -f 1 config/packages.ubuntu-focal | egrep -v "(^#|wkhtml|bundler|^ruby|^rake)"` - name: Install Ruby ${{ matrix.ruby }} uses: ruby/setup-ruby@v1 diff --git a/.github/workflows/rubocop.yml b/.github/workflows/rubocop.yml index 39ef47305e..f255a60310 100644 --- a/.github/workflows/rubocop.yml +++ b/.github/workflows/rubocop.yml @@ -17,6 +17,6 @@ jobs: 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 + rubocop_version: gemfile + rubocop_extensions: rubocop-performance:gemfile rubocop-rails:gemfile level: warning diff --git a/.ruby-style.yml b/.ruby-style.yml index 479f7aa0a8..639c80ca83 100644 --- a/.ruby-style.yml +++ b/.ruby-style.yml @@ -4,7 +4,7 @@ require: - rubocop-rails AllCops: - TargetRubyVersion: 2.3 + TargetRubyVersion: 2.5 RubyInterpreters: - ruby - rake @@ -36,6 +36,12 @@ Bundler/DuplicatedGem: Bundler/GemComment: Enabled: false +Bundler/GemFilename: + Enabled: false + +Bundler/GemVersion: + Enabled: false + Bundler/InsecureProtocolSource: Enabled: false @@ -44,6 +50,9 @@ Bundler/OrderedGems: #################### Gemspec #################### +Gemspec/DateAssignment: + Enabled: false + Gemspec/DuplicatedAssignment: Enabled: false @@ -70,6 +79,9 @@ Layout/ArrayAlignment: Layout/AssignmentIndentation: Enabled: true +Layout/BeginEndAlignment: + Enabled: false + Layout/BlockAlignment: Enabled: false @@ -113,6 +125,9 @@ Layout/EmptyLineAfterGuardClause: Layout/EmptyLineAfterMagicComment: Enabled: false +Layout/EmptyLineAfterMultilineCondition: + Enabled: false + Layout/EmptyLineBetweenDefs: Enabled: true @@ -125,6 +140,9 @@ Layout/EmptyLinesAroundAccessModifier: Layout/EmptyLinesAroundArguments: Enabled: false +Layout/EmptyLinesAroundAttributeAccessor: + Enabled: false + Layout/EmptyLinesAroundBeginBody: Enabled: false @@ -192,6 +210,9 @@ Layout/IndentationConsistency: Exclude: - Gemfile +Layout/IndentationStyle: + Enabled: true + Layout/IndentationWidth: Enabled: true @@ -204,8 +225,12 @@ Layout/LeadingCommentSpace: Layout/LeadingEmptyLines: Enabled: false +Layout/LineEndStringConcatenationIndentation: + Enabled: false + Layout/LineLength: Enabled: true + Max: 80 IgnoreCopDirectives: false IgnoredPatterns: - "^\\s*it\\s+.*do$" @@ -249,9 +274,15 @@ Layout/MultilineOperationIndentation: Layout/ParameterAlignment: Enabled: true +Layout/RedundantLineBreak: + Enabled: false + Layout/RescueEnsureAlignment: Enabled: true +Layout/SingleLineBlockChain: + Enabled: false + Layout/SpaceAfterColon: Enabled: true @@ -276,12 +307,18 @@ Layout/SpaceAroundEqualsInParameterDefault: Layout/SpaceAroundKeyword: Enabled: true +Layout/SpaceAroundMethodCallOperator: + Enabled: false + Layout/SpaceAroundOperators: Enabled: true Layout/SpaceBeforeBlockBraces: Enabled: true +Layout/SpaceBeforeBrackets: + Enabled: false + Layout/SpaceBeforeComma: Enabled: true @@ -324,9 +361,6 @@ Layout/SpaceInsideReferenceBrackets: Layout/SpaceInsideStringInterpolation: Enabled: false -Layout/Tab: - Enabled: true - Layout/TrailingEmptyLines: Enabled: true @@ -335,12 +369,21 @@ Layout/TrailingWhitespace: #################### Lint #################### +Lint/AmbiguousAssignment: + Enabled: false + Lint/AmbiguousBlockAssociation: Enabled: false Lint/AmbiguousOperator: Enabled: true +Lint/AmbiguousOperatorPrecedence: + Enabled: false + +Lint/AmbiguousRange: + Enabled: false + Lint/AmbiguousRegexpLiteral: Enabled: true @@ -350,42 +393,87 @@ Lint/AssignmentInCondition: Lint/BigDecimalNew: Enabled: false +Lint/BinaryOperatorWithIdenticalOperands: + Enabled: false + Lint/BooleanSymbol: Enabled: false Lint/CircularArgumentReference: Enabled: true +Lint/ConstantDefinitionInBlock: + Enabled: false + +Lint/ConstantResolution: + Enabled: false + Lint/Debugger: Enabled: true Lint/DeprecatedClassMethods: Enabled: true +Lint/DeprecatedConstants: + Enabled: false + +Lint/DeprecatedOpenSSLConstant: + Enabled: false + Lint/DisjunctiveAssignmentInConstructor: Enabled: false +Lint/DuplicateBranch: + Enabled: false + Lint/DuplicateCaseCondition: Enabled: false +Lint/DuplicateElsifCondition: + Enabled: false + Lint/DuplicateHashKey: Enabled: true Lint/DuplicateMethods: Enabled: true +Lint/DuplicateRegexpCharacterClassElement: + Enabled: false + +Lint/DuplicateRequire: + Enabled: false + +Lint/DuplicateRescueException: + Enabled: false + Lint/EachWithObjectArgument: Enabled: true Lint/ElseLayout: Enabled: true +Lint/EmptyBlock: + Enabled: false + +Lint/EmptyClass: + Enabled: false + +Lint/EmptyConditionalBody: + Enabled: false + Lint/EmptyEnsure: Enabled: true Lint/EmptyExpression: Enabled: false +Lint/EmptyFile: + Enabled: false + +Lint/EmptyInPattern: + Enabled: false + Lint/EmptyInterpolation: Enabled: true @@ -401,18 +489,30 @@ Lint/ErbNewArguments: Lint/FlipFlop: Enabled: true +Lint/FloatComparison: + Enabled: false + Lint/FloatOutOfRange: Enabled: true Lint/FormatParameterMismatch: Enabled: true +Lint/HashCompareByIdentity: + Enabled: false + Lint/HeredocMethodCallPosition: Enabled: false +Lint/IdentityComparison: + Enabled: false + Lint/ImplicitStringConcatenation: Enabled: true +Lint/IncompatibleIoSelectWithFiberScheduler: + Enabled: false + Lint/IneffectiveAccessModifier: Enabled: true @@ -422,6 +522,9 @@ Lint/InheritException: Lint/InterpolationCheck: Enabled: false +Lint/LambdaWithoutLiteralBlock: + Enabled: false + Lint/LiteralAsCondition: Enabled: true @@ -434,6 +537,12 @@ Lint/Loop: Lint/MissingCopEnableDirective: Enabled: false +Lint/MissingSuper: + Enabled: false + +Lint/MixedRegexpCaptureTypes: + Enabled: false + Lint/MultipleComparison: Enabled: false @@ -446,6 +555,9 @@ Lint/NestedPercentLiteral: Lint/NextWithoutAccumulator: Enabled: true +Lint/NoReturnInBeginEndBlocks: + Enabled: false + Lint/NonDeterministicRequireOrder: Enabled: false @@ -455,9 +567,18 @@ Lint/NonLocalExitFromIterator: Lint/NumberConversion: Enabled: false +Lint/NumberedParameterAssignment: + Enabled: false + +Lint/OrAssignmentToConstant: + Enabled: false + Lint/OrderedMagicComments: Enabled: false +Lint/OutOfRangeRegexpRef: + Enabled: false + Lint/ParenthesesAsGroupedExpression: Enabled: true @@ -467,6 +588,9 @@ Lint/PercentStringArray: Lint/PercentSymbolArray: Enabled: true +Lint/RaiseException: + Enabled: false + Lint/RandOne: Enabled: true @@ -476,9 +600,15 @@ Lint/RedundantCopDisableDirective: Lint/RedundantCopEnableDirective: Enabled: false +Lint/RedundantDirGlobSort: + Enabled: false + Lint/RedundantRequireStatement: Enabled: false +Lint/RedundantSafeNavigation: + Enabled: false + Lint/RedundantSplatExpansion: Enabled: true @@ -497,6 +627,9 @@ Lint/RegexpAsCondition: Lint/RequireParentheses: Enabled: true +Lint/RequireRelativeSelfPath: + Enabled: false + Lint/RescueException: Enabled: true @@ -518,6 +651,9 @@ Lint/SafeNavigationWithEmpty: Lint/ScriptPermission: Enabled: false +Lint/SelfAssignment: + Enabled: false + Lint/SendWithMixinArgument: Enabled: false @@ -536,21 +672,45 @@ Lint/StructNewOverride: Lint/SuppressedException: Enabled: true +Lint/SymbolConversion: + Enabled: false + Lint/Syntax: Enabled: true +Lint/ToEnumArguments: + Enabled: false + Lint/ToJSON: Enabled: false +Lint/TopLevelReturnWithArgument: + Enabled: false + +Lint/TrailingCommaInAttributeDeclaration: + Enabled: false + +Lint/TripleQuotes: + Enabled: false + Lint/UnderscorePrefixedVariableName: Enabled: true +Lint/UnexpectedBlockArity: + Enabled: false + Lint/UnifiedInteger: Enabled: false +Lint/UnmodifiedReduceAccumulator: + Enabled: false + Lint/UnreachableCode: Enabled: true +Lint/UnreachableLoop: + Enabled: false + Lint/UnusedBlockArgument: Enabled: true @@ -569,15 +729,18 @@ Lint/UselessAccessModifier: Lint/UselessAssignment: Enabled: false -Lint/UselessComparison: +Lint/UselessElseWithoutRescue: Enabled: false -Lint/UselessElseWithoutRescue: +Lint/UselessMethodDefinition: Enabled: false Lint/UselessSetterCall: Enabled: false +Lint/UselessTimes: + Enabled: false + Lint/Void: Enabled: false @@ -644,6 +807,9 @@ Naming/HeredocDelimiterCase: Naming/HeredocDelimiterNaming: Enabled: false +Naming/InclusiveLanguage: + Enabled: false + Naming/MemoizedInstanceVariableName: Enabled: false @@ -667,6 +833,21 @@ Naming/VariableNumber: #################### Performance #################### +Performance/AncestorsInclude: + Enabled: false + +Performance/ArraySemiInfiniteRangeSlice: + Enabled: false + +Performance/BigDecimalWithNumericArgument: + Enabled: false + +Performance/BindCall: + Enabled: false + +Performance/BlockGivenWithExplicitBlock: + Enabled: false + Performance/Caller: Enabled: false @@ -679,12 +860,27 @@ Performance/Casecmp: Performance/ChainArrayAllocation: Enabled: false +Performance/CollectionLiteralInLoop: + Enabled: false + Performance/CompareWithBlock: Enabled: false +Performance/ConcurrentMonotonicTime: + Enabled: false + +Performance/ConstantRegexp: + Enabled: false + Performance/Count: Enabled: false +Performance/DeletePrefix: + Enabled: false + +Performance/DeleteSuffix: + Enabled: false + Performance/Detect: Enabled: false @@ -703,6 +899,15 @@ Performance/FlatMap: Performance/InefficientHashSearch: Enabled: false +Performance/IoReadlines: + Enabled: false + +Performance/MapCompact: + Enabled: false + +Performance/MethodObjectAsBlock: + Enabled: false + Performance/OpenStruct: Enabled: false @@ -712,27 +917,57 @@ Performance/RangeInclude: Performance/RedundantBlockCall: Enabled: false +Performance/RedundantEqualityComparisonBlock: + Enabled: false + Performance/RedundantMatch: Enabled: false Performance/RedundantMerge: Enabled: true +Performance/RedundantSortBlock: + Enabled: false + +Performance/RedundantSplitRegexpArgument: + Enabled: false + +Performance/RedundantStringChars: + Enabled: false + Performance/RegexpMatch: Enabled: false Performance/ReverseEach: Enabled: false +Performance/ReverseFirst: + Enabled: false + +Performance/SelectMap: + Enabled: false + Performance/Size: Enabled: false +Performance/SortReverse: + Enabled: false + +Performance/Squeeze: + Enabled: false + Performance/StartWith: Enabled: false +Performance/StringInclude: + Enabled: false + Performance/StringReplacement: Enabled: false +Performance/Sum: + Enabled: false + Performance/TimesMap: Enabled: false @@ -750,12 +985,21 @@ Rails/ActionFilter: Rails/ActiveRecordAliases: Enabled: false +Rails/ActiveRecordCallbacksOrder: + Enabled: false + Rails/ActiveRecordOverride: Enabled: false Rails/ActiveSupportAliases: Enabled: false +Rails/AddColumnIndex: + Enabled: false + +Rails/AfterCommitOverride: + Enabled: false + Rails/ApplicationController: Enabled: false @@ -768,9 +1012,15 @@ Rails/ApplicationMailer: Rails/ApplicationRecord: Enabled: false +Rails/ArelStar: + Enabled: false + Rails/AssertNot: Enabled: false +Rails/AttributeDefaultBlockValue: + Enabled: false + Rails/BelongsTo: Enabled: false @@ -780,12 +1030,18 @@ Rails/Blank: Rails/BulkChangeTable: Enabled: false +Rails/ContentTag: + Enabled: false + Rails/CreateTableWithTimestamps: Enabled: false Rails/Date: Enabled: false +Rails/DefaultScope: + Enabled: false + Rails/Delegate: Enabled: false @@ -795,6 +1051,9 @@ Rails/DelegateAllowBlank: Rails/DynamicFindBy: Enabled: false +Rails/EagerEvaluationLogMessage: + Enabled: false + Rails/EnumHash: Enabled: false @@ -804,15 +1063,24 @@ Rails/EnumUniqueness: Rails/EnvironmentComparison: Enabled: false +Rails/EnvironmentVariableAccess: + Enabled: false + Rails/Exit: Enabled: false +Rails/ExpandedDateRange: + Enabled: false + Rails/FilePath: Enabled: false Rails/FindBy: Enabled: false +Rails/FindById: + Enabled: false + Rails/FindEach: Enabled: false @@ -831,9 +1099,21 @@ Rails/HttpPositionalArguments: Rails/HttpStatus: Enabled: false +Rails/I18nLocaleAssignment: + Enabled: false + Rails/IgnoredSkipActionFilterOption: Enabled: false +Rails/IndexBy: + Enabled: false + +Rails/IndexWith: + Enabled: false + +Rails/Inquiry: + Enabled: false + Rails/InverseOf: Enabled: false @@ -843,15 +1123,39 @@ Rails/LexicallyScopedActionFilter: Rails/LinkToBlank: Enabled: false +Rails/MailerName: + Enabled: false + +Rails/MatchRoute: + Enabled: false + +Rails/NegateInclude: + Enabled: false + Rails/NotNullColumn: Enabled: false +Rails/OrderById: + Enabled: false + Rails/Output: Enabled: false Rails/OutputSafety: Enabled: false +Rails/Pick: + Enabled: false + +Rails/Pluck: + Enabled: false + +Rails/PluckId: + Enabled: false + +Rails/PluckInWhere: + Enabled: false + Rails/PluralizationGrammar: Enabled: false @@ -870,9 +1174,15 @@ Rails/ReadWriteAttribute: Rails/RedundantAllowNil: Enabled: false +Rails/RedundantForeignKey: + Enabled: false + Rails/RedundantReceiverInWithOptions: Enabled: false +Rails/RedundantTravelBack: + Enabled: false + Rails/ReflectionClassName: Enabled: false @@ -882,12 +1192,24 @@ Rails/RefuteMethods: Rails/RelativeDateConstant: Enabled: false +Rails/RenderInline: + Enabled: false + +Rails/RenderPlainText: + Enabled: false + Rails/RequestReferer: Enabled: false +Rails/RequireDependency: + Enabled: false + Rails/ReversibleMigration: Enabled: false +Rails/ReversibleMigrationMethodDefinition: + Enabled: false + Rails/SafeNavigation: Enabled: false @@ -900,26 +1222,53 @@ Rails/SaveBang: Rails/ScopeArgs: Enabled: false +Rails/ShortI18n: + Enabled: false + Rails/SkipsModelValidations: Enabled: false +Rails/SquishedSQLHeredocs: + Enabled: false + Rails/TimeZone: Enabled: false +Rails/TimeZoneAssignment: + Enabled: false + Rails/UniqBeforePluck: Enabled: false +Rails/UniqueValidationWithoutIndex: + Enabled: false + Rails/UnknownEnv: Enabled: false +Rails/UnusedIgnoredColumns: + Enabled: false + Rails/Validation: Enabled: false +Rails/WhereEquals: + Enabled: false + +Rails/WhereExists: + Enabled: false + +Rails/WhereNot: + Enabled: false + #################### Security #################### Security/Eval: Enabled: false +Security/IoMethods: + Enabled: false + Security/JSONLoad: Enabled: false @@ -937,12 +1286,21 @@ Security/YAMLLoad: Style/AccessModifierDeclarations: Enabled: false +Style/AccessorGrouping: + Enabled: false + Style/Alias: Enabled: true Style/AndOr: Enabled: true +Style/ArgumentsForwarding: + Enabled: false + +Style/ArrayCoercion: + Enabled: false + Style/ArrayJoin: Enabled: true @@ -961,6 +1319,9 @@ Style/BarePercentLiterals: Style/BeginBlock: Enabled: true +Style/BisectedAttrAccessor: + Enabled: false + Style/BlockComments: Enabled: true @@ -972,6 +1333,9 @@ Style/BlockDelimiters: Style/CaseEquality: Enabled: true +Style/CaseLikeIf: + Enabled: false + Style/CharacterLiteral: Enabled: true @@ -981,12 +1345,21 @@ Style/ClassAndModuleChildren: Style/ClassCheck: Enabled: true +Style/ClassEqualityComparison: + Enabled: false + Style/ClassMethods: Enabled: true +Style/ClassMethodsDefinitions: + Enabled: false + Style/ClassVars: Enabled: true +Style/CollectionCompact: + Enabled: false + Style/CollectionMethods: Enabled: false @@ -996,6 +1369,9 @@ Style/ColonMethodCall: Style/ColonMethodDefinition: Enabled: false +Style/CombinableLoops: + Enabled: false + Style/CommandLiteral: Enabled: true @@ -1023,6 +1399,12 @@ Style/DefWithParentheses: Style/Dir: Enabled: false +Style/DisableCopsWithinSourceCodeDirective: + Enabled: false + +Style/DocumentDynamicEvalDefinition: + Enabled: false + Style/Documentation: Enabled: true Exclude: @@ -1069,6 +1451,9 @@ Style/Encoding: Style/EndBlock: Enabled: true +Style/EndlessMethod: + Enabled: false + Style/EvalWithLocation: Enabled: false @@ -1078,6 +1463,12 @@ Style/EvenOdd: Style/ExpandPathArguments: Enabled: false +Style/ExplicitBlockArgument: + Enabled: false + +Style/ExponentialNotation: + Enabled: false + Style/FloatDivision: Enabled: false @@ -1093,15 +1484,30 @@ Style/FormatStringToken: Style/FrozenStringLiteralComment: Enabled: false +Style/GlobalStdStream: + Enabled: false + Style/GlobalVars: Enabled: true Style/GuardClause: Enabled: true +Style/HashAsLastArrayItem: + Enabled: false + +Style/HashConversion: + Enabled: false + Style/HashEachMethods: Enabled: false +Style/HashExcept: + Enabled: false + +Style/HashLikeCase: + Enabled: false + Style/HashSyntax: Enabled: true EnforcedStyle: ruby19_no_mixed_keys @@ -1126,12 +1532,18 @@ Style/IfUnlessModifier: Style/IfUnlessModifierOfIfUnless: Enabled: true +Style/IfWithBooleanLiteralBranches: + Enabled: false + Style/IfWithSemicolon: Enabled: true Style/ImplicitRuntimeError: Enabled: false +Style/InPatternThen: + Enabled: false + Style/InfiniteLoop: Enabled: true @@ -1144,8 +1556,12 @@ Style/InverseMethods: Style/IpAddresses: Enabled: false +Style/KeywordParametersOrder: + Enabled: false + Style/Lambda: Enabled: true + EnforcedStyle: literal Style/LambdaCall: Enabled: true @@ -1165,9 +1581,6 @@ Style/MethodCalledOnDoEndBlock: Style/MethodDefParentheses: Enabled: true -Style/MethodMissingSuper: - Enabled: false - Style/MinMax: Enabled: false @@ -1195,6 +1608,9 @@ Style/MultilineIfModifier: Style/MultilineIfThen: Enabled: true +Style/MultilineInPatternThen: + Enabled: false + Style/MultilineMemoization: Enabled: false @@ -1216,6 +1632,9 @@ Style/MutableConstant: Style/NegatedIf: Enabled: true +Style/NegatedIfElseCondition: + Enabled: false + Style/NegatedUnless: Enabled: false @@ -1237,12 +1656,21 @@ Style/Next: Style/NilComparison: Enabled: true +Style/NilLambda: + Enabled: false + Style/NonNilCheck: Enabled: true Style/Not: Enabled: true +Style/NumberedParameters: + Enabled: false + +Style/NumberedParametersLimit: + Enabled: false + Style/NumericLiteralPrefix: Enabled: true @@ -1261,6 +1689,9 @@ Style/OptionHash: Style/OptionalArguments: Enabled: true +Style/OptionalBooleanParameter: + Enabled: false + Style/OrAssignment: Enabled: false @@ -1285,12 +1716,21 @@ Style/PreferredHashMethods: Style/Proc: Enabled: true +Style/QuotedSymbols: + Enabled: false + Style/RaiseArgs: Enabled: true Style/RandomWithOffset: Enabled: false +Style/RedundantArgument: + Enabled: false + +Style/RedundantAssignment: + Enabled: false + Style/RedundantBegin: Enabled: true @@ -1306,6 +1746,12 @@ Style/RedundantConditional: Style/RedundantException: Enabled: true +Style/RedundantFetchBlock: + Enabled: false + +Style/RedundantFileExtensionInRequire: + Enabled: false + Style/RedundantFreeze: Enabled: true @@ -1318,12 +1764,24 @@ Style/RedundantParentheses: Style/RedundantPercentQ: Enabled: false +Style/RedundantRegexpCharacterClass: + Enabled: false + +Style/RedundantRegexpEscape: + Enabled: false + Style/RedundantReturn: Enabled: true Style/RedundantSelf: Enabled: true +Style/RedundantSelfAssignment: + Enabled: false + +Style/RedundantSelfAssignmentBranch: + Enabled: false + Style/RedundantSort: Enabled: false @@ -1348,6 +1806,9 @@ Style/SafeNavigation: Style/Sample: Enabled: false +Style/SelectByRegexp: + Enabled: false + Style/SelfAssignment: Enabled: true @@ -1361,21 +1822,39 @@ Style/SignalException: Enabled: true EnforcedStyle: only_raise +Style/SingleArgumentDig: + Enabled: false + Style/SingleLineBlockParams: Enabled: false Style/SingleLineMethods: Enabled: true +Style/SlicingWithRange: + Enabled: false + +Style/SoleNestedConditional: + Enabled: false + Style/SpecialGlobalVars: Enabled: true Style/StabbyLambdaParentheses: Enabled: true +Style/StaticClass: + Enabled: false + Style/StderrPuts: Enabled: false +Style/StringChars: + Enabled: false + +Style/StringConcatenation: + Enabled: false + Style/StringHashKeys: Enabled: false @@ -1394,6 +1873,9 @@ Style/Strip: Style/StructInheritance: Enabled: true +Style/SwapValues: + Enabled: false + Style/SymbolArray: Enabled: false @@ -1406,6 +1888,9 @@ Style/SymbolProc: Style/TernaryParentheses: Enabled: false +Style/TopLevelMethodDefinition: + Enabled: false + Style/TrailingBodyOnClass: Enabled: false @@ -1439,6 +1924,9 @@ Style/TrivialAccessors: Style/UnlessElse: Enabled: true +Style/UnlessLogicalOperators: + Enabled: false + Style/UnpackFirst: Enabled: false diff --git a/Gemfile b/Gemfile index 96165a2a39..a5f359a896 100644 --- a/Gemfile +++ b/Gemfile @@ -84,8 +84,7 @@ def rails_upgrade? %w[1 true].include?(ENV['RAILS_UPGRADE']) end -gem 'rails', rails_upgrade? ? '~> 6.0.3' : '~> 5.2.4' - gem 'nio4r', rails_upgrade? ? nil : '< 2.5.3' +gem 'rails', rails_upgrade? ? '~> 6.1.4' : '~> 6.0.3' gem 'pg', '~> 1.2.3' @@ -93,57 +92,55 @@ gem 'pg', '~> 1.2.3' gem 'acts_as_versioned', :git => 'https://github.com/technoweenie/acts_as_versioned.git', :ref => '63b1fc8529d028' gem 'active_model_otp' gem 'bcrypt', '~> 3.1.16' -gem 'cancancan', '~> 3.2.2' +gem 'cancancan', '~> 3.3.0' gem 'charlock_holmes', '~> 0.7.7' -gem 'dalli', '~> 2.7.11' -gem 'dynamic_form', '~> 1.1.0' +gem 'dalli', '~> 3.0.4' 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.5.3' +gem 'icalendar', '~> 2.7.1' gem 'jquery-rails', '~> 4.4.0' gem 'jquery-ui-rails', '~> 6.0.0' -gem 'json', '~> 2.5.1' -gem 'holidays', '~> 7.1.0' +gem 'json', '~> 2.6.1' +gem 'holidays', '~> 8.4.1' gem 'iso_country_codes', '~> 0.7.8' gem 'mail', '~> 2.7.1' gem 'maxmind-db', '~> 1.0.0' gem 'mahoro', '~> 0.5' -gem 'nokogiri', '~> 1.11.7' +gem 'nokogiri', '~> 1.12.5' gem 'open4', '~> 1.3.0' gem 'rack', '~> 2.2.3' -gem 'rack-ssl', '~> 1.4.0' gem 'rack-utf8_sanitizer', '~> 1.7.0' -gem 'recaptcha', '~> 5.6.0', require: 'recaptcha/rails' +gem 'recaptcha', '~> 5.8.1', require: 'recaptcha/rails' gem 'mini_magick', '~> 4.11.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', '~> 6.3.2' +gem 'rubyzip', '~> 2.3.2' +gem 'secure_headers', '~> 6.3.3' gem 'statistics2', '~> 0.54' gem 'strip_attributes', :git => 'https://github.com/mysociety/strip_attributes.git', :branch => 'globalize3-rails5.2' -gem 'stripe', '~> 5.34.0' +gem 'stripe', '~> 5.39.0' gem 'syslog_protocol', '~> 0.9.0' gem 'thin', '~> 1.8.1' gem 'vpim', '~> 13.11.11' -gem 'will_paginate', '~> 3.3.0' +gem 'will_paginate', '~> 3.3.1' gem 'xapian-full-alaveteli', '~> 1.4.18.1' -gem 'xml-simple', '~> 1.1.8', :require => 'xmlsimple' +gem 'xml-simple', '~> 1.1.9', :require => 'xmlsimple' gem 'zip_tricks', '~> 5.6.0' # Gems only used by the research export task gem 'gender_detector', '~> 2.0.0' # Gems related to internationalisation -gem 'i18n', '~> 1.8.10' -gem 'rails-i18n', rails_upgrade? ? '~> 6.0.0' : '~> 5.1.0' +gem 'i18n', '~> 1.8.11' +gem 'rails-i18n', '~> 6.0.0' gem 'gettext_i18n_rails', '~> 1.8.1' - gem 'fast_gettext', '~> 2.0.3' -gem 'gettext', '~> 3.3.8' -gem 'globalize', rails_upgrade? ? '~> 5.3.0' : '~> 5.2.0' + gem 'fast_gettext', '~> 2.1.0' +gem 'gettext', '~> 3.4.1' +gem 'globalize', rails_upgrade? ? '~> 6.0.0' : '~> 5.3.0' gem 'locale', '~> 2.1.3' -gem 'routing-filter', '~> 0.6.2' +gem 'routing-filter', rails_upgrade? ? '~> 0.7.0' : '~> 0.6.2' gem 'unicode', '~> 0.4.4' gem 'unidecoder', '~> 1.1.0' gem 'money', '~> 6.16.0' @@ -154,7 +151,7 @@ gem 'mime-types', '< 3.0.0', require: false # Assets gem 'bootstrap-sass', '~> 2.3.2.2' gem 'mini_racer', '~> 0.4.0' -gem 'sass-rails', rails_upgrade? ? '~> 5.0.8' : '~> 5.0.7' +gem 'sass-rails', '~> 5.0.8' gem 'uglifier', '~> 4.2.0' # Feature flags @@ -162,23 +159,23 @@ gem 'alaveteli_features', :path => 'gems/alaveteli_features' group :test do gem 'fivemat', '~> 1.3.7' - gem 'webmock', '~> 3.13.0' + gem 'webmock', '~> 3.14.0' gem 'simplecov', '~> 0.17.1' gem 'simplecov-lcov', '~> 0.7.0' - gem 'capybara', '~> 3.15.1' + gem 'capybara', '~> 3.35.3' 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', '~> 6.1.4' - gem 'factory_bot_rails', '~> 5.2.0' + gem 'bullet', '~> 6.1.5' + gem 'factory_bot_rails', '~> 6.2.0' gem 'oink', '~> 0.10.1' gem 'rspec-activemodel-mocks', '~> 1.1.0' - gem 'rspec-rails', '~> 5.0.1' - gem 'pry', '~> 0.12.2' - gem 'pry-byebug', '~> 3.7.0' + gem 'rspec-rails', '~> 5.0.2' + gem 'pry', '~> 0.13.0' + gem 'pry-byebug', '~> 3.9.0' end group :development do @@ -187,9 +184,9 @@ group :development do gem 'net-ssh', '~> 6.1.0' gem 'net-ssh-gateway', '>= 1.1.0', '< 3.0.0' gem 'launchy', '< 2.5.0' - gem 'listen', '>= 3.0.5', '< 3.5.1' + gem 'listen', '>= 3.0.5', '< 3.7.1' gem 'web-console', '>= 3.3.0' - gem 'rubocop', '~> 0.81.0', require: false - gem 'rubocop-performance', '~> 1.5.2', require: false + gem 'rubocop', '~> 1.22.3', require: false + gem 'rubocop-performance', require: false gem 'rubocop-rails', require: false end diff --git a/Gemfile.lock b/Gemfile.lock index 9a4676993b..4473e2e871 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -40,160 +40,169 @@ PATH flipper (~> 0.10) flipper-active_record (~> 0.10) mime-types (< 3.0.0) - rails (~> 5.2.4) + rails (~> 6.0.3) GEM remote: https://rubygems.org/ specs: - actioncable (5.2.6) - actionpack (= 5.2.6) + actioncable (6.0.4.1) + actionpack (= 6.0.4.1) nio4r (~> 2.0) websocket-driver (>= 0.6.1) - actionmailer (5.2.6) - actionpack (= 5.2.6) - actionview (= 5.2.6) - activejob (= 5.2.6) + actionmailbox (6.0.4.1) + actionpack (= 6.0.4.1) + activejob (= 6.0.4.1) + activerecord (= 6.0.4.1) + activestorage (= 6.0.4.1) + activesupport (= 6.0.4.1) + mail (>= 2.7.1) + actionmailer (6.0.4.1) + actionpack (= 6.0.4.1) + actionview (= 6.0.4.1) + activejob (= 6.0.4.1) mail (~> 2.5, >= 2.5.4) rails-dom-testing (~> 2.0) - actionpack (5.2.6) - actionview (= 5.2.6) - activesupport (= 5.2.6) + actionpack (6.0.4.1) + actionview (= 6.0.4.1) + activesupport (= 6.0.4.1) 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.6) - activesupport (= 5.2.6) + rails-html-sanitizer (~> 1.0, >= 1.2.0) + actiontext (6.0.4.1) + actionpack (= 6.0.4.1) + activerecord (= 6.0.4.1) + activestorage (= 6.0.4.1) + activesupport (= 6.0.4.1) + nokogiri (>= 1.8.5) + actionview (6.0.4.1) + activesupport (= 6.0.4.1) builder (~> 3.1) erubi (~> 1.4) rails-dom-testing (~> 2.0) - rails-html-sanitizer (~> 1.0, >= 1.0.3) - active_model_otp (2.2.0) + rails-html-sanitizer (~> 1.1, >= 1.2.0) + active_model_otp (2.3.1) activemodel rotp (~> 6.2.0) - activejob (5.2.6) - activesupport (= 5.2.6) + activejob (6.0.4.1) + activesupport (= 6.0.4.1) globalid (>= 0.3.6) - activemodel (5.2.6) - activesupport (= 5.2.6) - activerecord (5.2.6) - activemodel (= 5.2.6) - activesupport (= 5.2.6) - arel (>= 9.0) - activestorage (5.2.6) - actionpack (= 5.2.6) - activerecord (= 5.2.6) + activemodel (6.0.4.1) + activesupport (= 6.0.4.1) + activerecord (6.0.4.1) + activemodel (= 6.0.4.1) + activesupport (= 6.0.4.1) + activestorage (6.0.4.1) + actionpack (= 6.0.4.1) + activejob (= 6.0.4.1) + activerecord (= 6.0.4.1) marcel (~> 1.0.0) - activesupport (5.2.6) + activesupport (6.0.4.1) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 0.7, < 2) minitest (~> 5.1) tzinfo (~> 1.1) + zeitwerk (~> 2.2, >= 2.2.2) addressable (2.8.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) + ast (2.4.2) bcrypt (3.1.16) - bindex (0.7.0) + bindex (0.8.1) bootstrap-sass (2.3.2.2) sass (~> 3.2) builder (3.2.4) - bullet (6.1.4) + bullet (6.1.5) activesupport (>= 3.0.0) uniform_notifier (~> 1.11) - byebug (11.0.1) - cancancan (3.2.2) + byebug (11.1.3) + cancancan (3.3.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.15.1) + capybara (3.35.3) addressable mini_mime (>= 0.1.3) nokogiri (~> 1.8) rack (>= 1.6.0) rack-test (>= 0.6.3) - regexp_parser (~> 1.2) + regexp_parser (>= 1.5, < 3.0) xpath (~> 3.2) charlock_holmes (0.7.7) - coderay (1.1.2) - concurrent-ruby (1.1.8) + coderay (1.1.3) + concurrent-ruby (1.1.9) crack (0.4.5) rexml crass (1.0.6) - csv (3.2.0) daemons (1.4.0) - dalli (2.7.11) + dalli (3.0.4) dante (0.2.0) diff-lcs (1.4.4) docile (1.3.5) - dynamic_form (1.1.4) erubi (1.10.0) eventmachine (1.2.7) exception_notification (4.4.3) actionmailer (>= 4.0, < 7) activesupport (>= 4.0, < 7) execjs (2.7.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) + factory_bot (6.2.0) + activesupport (>= 5.0.0) + factory_bot_rails (6.2.0) + factory_bot (~> 6.2.0) + railties (>= 5.0.0) fancybox-rails (0.3.1) railties (>= 3.1.0) - fast_gettext (2.0.3) - ffi (1.15.0) + fast_gettext (2.1.0) + ffi (1.15.3) fivemat (1.3.7) - flipper (0.17.2) - flipper-active_record (0.17.2) + flipper (0.22.1) + flipper-active_record (0.22.1) activerecord (>= 4.2, < 7) - flipper (~> 0.17.2) + flipper (~> 0.22.1) gender_detector (2.0.0) - gettext (3.3.8) + gettext (3.4.1) locale (>= 2.0.5) - red-datasets 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) + globalid (0.5.2) + activesupport (>= 5.0) + globalize (5.3.1) + activemodel (>= 4.2, < 6.1) + activerecord (>= 4.2, < 6.1) request_store (~> 1.0) gnuplot (2.6.2) hashdiff (1.0.1) highline (2.0.0) hodel_3000_compliant_logger (0.1.1) - holidays (7.1.0) + holidays (8.4.1) htmlentities (4.3.4) - i18n (1.8.10) + i18n (1.8.11) concurrent-ruby (~> 1.0) - icalendar (2.5.3) + icalendar (2.7.1) ice_cube (~> 0.16) ice_cube (0.16.3) iso_country_codes (0.7.8) - 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 (2.5.1) + json (2.6.1) launchy (2.4.3) addressable (~> 2.3) libv8-node (15.14.0.1) - listen (3.5.0) + listen (3.7.0) rb-fsevent (~> 0.10, >= 0.10.3) rb-inotify (~> 0.9, >= 0.9.10) locale (2.1.3) - loofah (2.9.1) + loofah (2.12.0) crass (~> 1.0.2) nokogiri (>= 1.5.9) mahoro (0.5) @@ -201,11 +210,11 @@ GEM mini_mime (>= 0.1.1) marcel (1.0.1) maxmind-db (1.0.0) - method_source (0.9.2) + method_source (1.0.0) mime-types (2.99.3) mini_magick (4.11.0) - mini_mime (1.1.0) - mini_portile2 (2.5.3) + mini_mime (1.1.1) + mini_portile2 (2.6.1) mini_racer (0.4.0) libv8-node (~> 15.14.0.0) minitest (5.14.4) @@ -219,45 +228,45 @@ GEM net-ssh (6.1.0) net-ssh-gateway (2.0.0) net-ssh (>= 4.0.0) - nio4r (2.5.2) - nokogiri (1.11.7) - mini_portile2 (~> 2.5.0) + nio4r (2.5.8) + nokogiri (1.12.5) + mini_portile2 (~> 2.6.1) racc (~> 1.4) oink (0.10.1) activerecord hodel_3000_compliant_logger open4 (1.3.4) - parallel (1.19.2) - parser (2.7.1.4) + parallel (1.21.0) + parser (3.0.2.0) 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) + pry (0.13.1) + coderay (~> 1.1) + method_source (~> 1.0) + pry-byebug (3.9.0) byebug (~> 11.0) - pry (~> 0.10) + pry (~> 0.13.0) public_suffix (4.0.6) racc (1.5.2) rack (2.2.3) - rack-ssl (1.4.1) - rack rack-test (1.1.0) rack (>= 1.0, < 3) rack-utf8_sanitizer (1.7.0) rack (>= 1.0, < 3.0) - rails (5.2.6) - actioncable (= 5.2.6) - actionmailer (= 5.2.6) - actionpack (= 5.2.6) - actionview (= 5.2.6) - activejob (= 5.2.6) - activemodel (= 5.2.6) - activerecord (= 5.2.6) - activestorage (= 5.2.6) - activesupport (= 5.2.6) + rails (6.0.4.1) + actioncable (= 6.0.4.1) + actionmailbox (= 6.0.4.1) + actionmailer (= 6.0.4.1) + actionpack (= 6.0.4.1) + actiontext (= 6.0.4.1) + actionview (= 6.0.4.1) + activejob (= 6.0.4.1) + activemodel (= 6.0.4.1) + activerecord (= 6.0.4.1) + activestorage (= 6.0.4.1) + activesupport (= 6.0.4.1) bundler (>= 1.3.0) - railties (= 5.2.6) + railties (= 6.0.4.1) sprockets-rails (>= 2.0.0) rails-controller-testing (1.0.5) actionpack (>= 5.0.1.rc1) @@ -266,30 +275,26 @@ GEM rails-dom-testing (2.0.3) activesupport (>= 4.2.0) nokogiri (>= 1.6) - rails-html-sanitizer (1.3.0) + rails-html-sanitizer (1.4.2) loofah (~> 2.3) - rails-i18n (5.1.3) + rails-i18n (6.0.0) i18n (>= 0.7, < 2) - railties (>= 5.0, < 6) - railties (5.2.6) - actionpack (= 5.2.6) - activesupport (= 5.2.6) + railties (>= 6.0.0, < 7) + railties (6.0.4.1) + actionpack (= 6.0.4.1) + activesupport (= 6.0.4.1) method_source rake (>= 0.8.7) - thor (>= 0.19.0, < 2.0) + thor (>= 0.20.3, < 2.0) rainbow (3.0.0) - rake (13.0.3) - rb-fsevent (0.10.4) + rake (13.0.6) + rb-fsevent (0.11.0) rb-inotify (0.10.1) ffi (~> 1.0) - recaptcha (5.6.0) + recaptcha (5.8.1) json - red-datasets (0.1.4) - csv (>= 3.0.5) - rexml - rubyzip - regexp_parser (1.7.1) - request_store (1.4.1) + regexp_parser (2.1.1) + request_store (1.5.0) rack (>= 1.4) rexml (3.2.5) rolify (5.3.0) @@ -309,7 +314,7 @@ GEM rspec-mocks (3.10.2) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.10.0) - rspec-rails (5.0.1) + rspec-rails (5.0.2) actionpack (>= 5.2) activesupport (>= 5.2) railties (>= 5.2) @@ -318,31 +323,35 @@ GEM rspec-mocks (~> 3.10) rspec-support (~> 3.10) rspec-support (3.10.2) - rubocop (0.81.0) - jaro_winkler (~> 1.5.1) + rubocop (1.22.3) parallel (~> 1.10) - parser (>= 2.7.0.1) + parser (>= 3.0.0.0) rainbow (>= 2.2.2, < 4.0) + regexp_parser (>= 1.8, < 3.0) rexml + rubocop-ast (>= 1.12.0, < 2.0) ruby-progressbar (~> 1.7) - unicode-display_width (>= 1.4.0, < 2.0) - rubocop-performance (1.5.2) - rubocop (>= 0.71.0) - rubocop-rails (2.5.2) - activesupport + unicode-display_width (>= 1.4.0, < 3.0) + rubocop-ast (1.13.0) + parser (>= 3.0.1.1) + rubocop-performance (1.12.0) + rubocop (>= 1.7.0, < 2.0) + rubocop-ast (>= 0.4.0) + rubocop-rails (2.12.4) + activesupport (>= 4.2.0) rack (>= 1.1) - rubocop (>= 0.72.0) + rubocop (>= 1.7.0, < 2.0) ruby-ole (1.2.12.1) - ruby-progressbar (1.10.1) - rubyzip (1.3.0) + ruby-progressbar (1.11.0) + rubyzip (2.3.2) 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 (6.3.2) + secure_headers (6.3.3) simplecov (0.17.1) docile (~> 1.1) json (>= 1.8, < 3) @@ -357,7 +366,7 @@ GEM activesupport (>= 4.0) sprockets (>= 3.0.0) statistics2 (0.54) - stripe (5.34.0) + stripe (5.39.0) syslog_protocol (0.9.2) text (1.3.1) thin (1.8.1) @@ -366,33 +375,35 @@ GEM rack (>= 1, < 3) thor (1.1.0) thread_safe (0.3.6) - tilt (2.0.8) + tilt (2.0.10) tzinfo (1.2.9) thread_safe (~> 0.1) uglifier (4.2.0) execjs (>= 0.3.0, < 3) unicode (0.4.4.4) - unicode-display_width (1.7.0) + unicode-display_width (2.1.0) unidecoder (1.1.2) - uniform_notifier (1.14.1) + uniform_notifier (1.14.2) vpim (13.11.11) - web-console (3.7.0) - actionview (>= 5.0) - activemodel (>= 5.0) + web-console (4.1.0) + actionview (>= 6.0.0) + activemodel (>= 6.0.0) bindex (>= 0.4.0) - railties (>= 5.0) - webmock (3.13.0) - addressable (>= 2.3.6) + railties (>= 6.0.0) + webmock (3.14.0) + addressable (>= 2.8.0) crack (>= 0.3.2) hashdiff (>= 0.4.0, < 2.0.0) - websocket-driver (0.7.3) + websocket-driver (0.7.5) websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) - will_paginate (3.3.0) + will_paginate (3.3.1) xapian-full-alaveteli (1.4.18.1) - xml-simple (1.1.8) + xml-simple (1.1.9) + rexml xpath (3.2.0) nokogiri (~> 1.8) + zeitwerk (2.5.1) zip_tricks (5.6.0) PLATFORMS @@ -405,33 +416,32 @@ DEPENDENCIES annotate (< 3.1.1) bcrypt (~> 3.1.16) bootstrap-sass (~> 2.3.2.2) - bullet (~> 6.1.4) - cancancan (~> 3.2.2) + bullet (~> 6.1.5) + cancancan (~> 3.3.0) capistrano (~> 2.15.0, < 3.0.0) - capybara (~> 3.15.1) + capybara (~> 3.35.3) charlock_holmes (~> 0.7.7) - dalli (~> 2.7.11) - dynamic_form (~> 1.1.0) + dalli (~> 3.0.4) exception_notification (~> 4.4.3) - factory_bot_rails (~> 5.2.0) + factory_bot_rails (~> 6.2.0) fancybox-rails (~> 0.3.0) - fast_gettext (~> 2.0.3) + fast_gettext (~> 2.1.0) fivemat (~> 1.3.7) gender_detector (~> 2.0.0) - gettext (~> 3.3.8) + gettext (~> 3.4.1) gettext_i18n_rails (~> 1.8.1) - globalize (~> 5.2.0) + globalize (~> 5.3.0) gnuplot (~> 2.6.0) - holidays (~> 7.1.0) + holidays (~> 8.4.1) htmlentities (~> 4.3.0) - i18n (~> 1.8.10) - icalendar (~> 2.5.3) + i18n (~> 1.8.11) + icalendar (~> 2.7.1) iso_country_codes (~> 0.7.8) jquery-rails (~> 4.4.0) jquery-ui-rails (~> 6.0.0) - json (~> 2.5.1) + json (~> 2.6.1) launchy (< 2.5.0) - listen (>= 3.0.5, < 3.5.1) + listen (>= 3.0.5, < 3.7.1) locale (~> 2.1.3) mahoro (~> 0.5) mail (~> 2.7.1) @@ -442,36 +452,34 @@ DEPENDENCIES money (~> 6.16.0) net-ssh (~> 6.1.0) net-ssh-gateway (>= 1.1.0, < 3.0.0) - nio4r (< 2.5.3) - nokogiri (~> 1.11.7) + nokogiri (~> 1.12.5) oink (~> 0.10.1) open4 (~> 1.3.0) pg (~> 1.2.3) - pry (~> 0.12.2) - pry-byebug (~> 3.7.0) + pry (~> 0.13.0) + pry-byebug (~> 3.9.0) rack (~> 2.2.3) - rack-ssl (~> 1.4.0) rack-utf8_sanitizer (~> 1.7.0) - rails (~> 5.2.4) + rails (~> 6.0.3) rails-controller-testing - rails-i18n (~> 5.1.0) - recaptcha (~> 5.6.0) + rails-i18n (~> 6.0.0) + recaptcha (~> 5.8.1) rolify (~> 5.3.0) routing-filter (~> 0.6.2) rspec-activemodel-mocks (~> 1.1.0) - rspec-rails (~> 5.0.1) - rubocop (~> 0.81.0) - rubocop-performance (~> 1.5.2) + rspec-rails (~> 5.0.2) + rubocop (~> 1.22.3) + rubocop-performance rubocop-rails ruby-msg (~> 1.5.0)! - rubyzip (~> 1.3.0, < 2.0.0) - sass-rails (~> 5.0.7) - secure_headers (~> 6.3.2) + rubyzip (~> 2.3.2) + sass-rails (~> 5.0.8) + secure_headers (~> 6.3.3) simplecov (~> 0.17.1) simplecov-lcov (~> 0.7.0) statistics2 (~> 0.54) strip_attributes! - stripe (~> 5.34.0) + stripe (~> 5.39.0) stripe-ruby-mock! syslog_protocol (~> 0.9.0) thin (~> 1.8.1) @@ -480,8 +488,8 @@ DEPENDENCIES unidecoder (~> 1.1.0) vpim (~> 13.11.11) web-console (>= 3.3.0) - webmock (~> 3.13.0) - will_paginate (~> 3.3.0) + webmock (~> 3.14.0) + will_paginate (~> 3.3.1) xapian-full-alaveteli (~> 1.4.18.1) - xml-simple (~> 1.1.8) + xml-simple (~> 1.1.9) zip_tricks (~> 5.6.0) diff --git a/Gemfile.rails_next.lock b/Gemfile.rails_next.lock index 22e9350fae..75c402a0b6 100644 --- a/Gemfile.rails_next.lock +++ b/Gemfile.rails_next.lock @@ -40,173 +40,173 @@ PATH flipper (~> 0.10) flipper-active_record (~> 0.10) mime-types (< 3.0.0) - rails (~> 6.0.3) + rails (~> 6.1.4) GEM remote: https://rubygems.org/ specs: - actioncable (6.0.4) - actionpack (= 6.0.4) + actioncable (6.1.4.1) + actionpack (= 6.1.4.1) + activesupport (= 6.1.4.1) nio4r (~> 2.0) websocket-driver (>= 0.6.1) - actionmailbox (6.0.4) - actionpack (= 6.0.4) - activejob (= 6.0.4) - activerecord (= 6.0.4) - activestorage (= 6.0.4) - activesupport (= 6.0.4) + actionmailbox (6.1.4.1) + actionpack (= 6.1.4.1) + activejob (= 6.1.4.1) + activerecord (= 6.1.4.1) + activestorage (= 6.1.4.1) + activesupport (= 6.1.4.1) mail (>= 2.7.1) - actionmailer (6.0.4) - actionpack (= 6.0.4) - actionview (= 6.0.4) - activejob (= 6.0.4) + actionmailer (6.1.4.1) + actionpack (= 6.1.4.1) + actionview (= 6.1.4.1) + activejob (= 6.1.4.1) + activesupport (= 6.1.4.1) mail (~> 2.5, >= 2.5.4) rails-dom-testing (~> 2.0) - actionpack (6.0.4) - actionview (= 6.0.4) - activesupport (= 6.0.4) - rack (~> 2.0, >= 2.0.8) + actionpack (6.1.4.1) + actionview (= 6.1.4.1) + activesupport (= 6.1.4.1) + rack (~> 2.0, >= 2.0.9) rack-test (>= 0.6.3) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.2.0) - actiontext (6.0.4) - actionpack (= 6.0.4) - activerecord (= 6.0.4) - activestorage (= 6.0.4) - activesupport (= 6.0.4) + actiontext (6.1.4.1) + actionpack (= 6.1.4.1) + activerecord (= 6.1.4.1) + activestorage (= 6.1.4.1) + activesupport (= 6.1.4.1) nokogiri (>= 1.8.5) - actionview (6.0.4) - activesupport (= 6.0.4) + actionview (6.1.4.1) + activesupport (= 6.1.4.1) builder (~> 3.1) erubi (~> 1.4) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.1, >= 1.2.0) - active_model_otp (2.2.0) + active_model_otp (2.3.1) activemodel rotp (~> 6.2.0) - activejob (6.0.4) - activesupport (= 6.0.4) + activejob (6.1.4.1) + activesupport (= 6.1.4.1) globalid (>= 0.3.6) - activemodel (6.0.4) - activesupport (= 6.0.4) - activerecord (6.0.4) - activemodel (= 6.0.4) - activesupport (= 6.0.4) - activestorage (6.0.4) - actionpack (= 6.0.4) - activejob (= 6.0.4) - activerecord (= 6.0.4) + activemodel (6.1.4.1) + activesupport (= 6.1.4.1) + activerecord (6.1.4.1) + activemodel (= 6.1.4.1) + activesupport (= 6.1.4.1) + activestorage (6.1.4.1) + actionpack (= 6.1.4.1) + activejob (= 6.1.4.1) + activerecord (= 6.1.4.1) + activesupport (= 6.1.4.1) marcel (~> 1.0.0) - activesupport (6.0.4) + mini_mime (>= 1.1.0) + activesupport (6.1.4.1) concurrent-ruby (~> 1.0, >= 1.0.2) - i18n (>= 0.7, < 2) - minitest (~> 5.1) - tzinfo (~> 1.1) - zeitwerk (~> 2.2, >= 2.2.2) + i18n (>= 1.6, < 2) + minitest (>= 5.1) + tzinfo (~> 2.0) + zeitwerk (~> 2.3) addressable (2.8.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) + ast (2.4.2) bcrypt (3.1.16) - bindex (0.7.0) + bindex (0.8.1) bootstrap-sass (2.3.2.2) sass (~> 3.2) builder (3.2.4) - bullet (6.1.4) + bullet (6.1.5) activesupport (>= 3.0.0) uniform_notifier (~> 1.11) - byebug (11.0.1) - cancancan (3.2.2) + byebug (11.1.3) + cancancan (3.3.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.15.1) + capybara (3.35.3) addressable mini_mime (>= 0.1.3) nokogiri (~> 1.8) rack (>= 1.6.0) rack-test (>= 0.6.3) - regexp_parser (~> 1.2) + regexp_parser (>= 1.5, < 3.0) xpath (~> 3.2) charlock_holmes (0.7.7) - coderay (1.1.2) + coderay (1.1.3) concurrent-ruby (1.1.9) crack (0.4.5) rexml crass (1.0.6) - csv (3.2.0) daemons (1.4.0) - dalli (2.7.11) + dalli (3.0.4) dante (0.2.0) diff-lcs (1.4.4) docile (1.3.5) - dynamic_form (1.1.4) erubi (1.10.0) eventmachine (1.2.7) exception_notification (4.4.3) actionmailer (>= 4.0, < 7) activesupport (>= 4.0, < 7) execjs (2.7.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) + factory_bot (6.2.0) + activesupport (>= 5.0.0) + factory_bot_rails (6.2.0) + factory_bot (~> 6.2.0) + railties (>= 5.0.0) fancybox-rails (0.3.1) railties (>= 3.1.0) - fast_gettext (2.0.3) - ffi (1.15.0) + fast_gettext (2.1.0) + ffi (1.15.3) fivemat (1.3.7) - flipper (0.21.0) - flipper-active_record (0.21.0) - activerecord (>= 5.0, < 7) - flipper (~> 0.21.0) + flipper (0.22.1) + flipper-active_record (0.22.1) + activerecord (>= 4.2, < 7) + flipper (~> 0.22.1) gender_detector (2.0.0) - gettext (3.3.8) + gettext (3.4.1) locale (>= 2.0.5) - red-datasets 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.3.1) - activemodel (>= 4.2, < 6.1) - activerecord (>= 4.2, < 6.1) + globalid (0.5.2) + activesupport (>= 5.0) + globalize (6.0.1) + activemodel (>= 4.2, < 7.0) + activerecord (>= 4.2, < 7.0) request_store (~> 1.0) gnuplot (2.6.2) hashdiff (1.0.1) highline (2.0.0) hodel_3000_compliant_logger (0.1.1) - holidays (7.1.0) + holidays (8.4.1) htmlentities (4.3.4) - i18n (1.8.10) + i18n (1.8.11) concurrent-ruby (~> 1.0) - icalendar (2.5.3) + icalendar (2.7.1) ice_cube (~> 0.16) ice_cube (0.16.3) iso_country_codes (0.7.8) - 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 (2.5.1) + json (2.6.1) launchy (2.4.3) addressable (~> 2.3) libv8-node (15.14.0.1) - listen (3.5.0) + listen (3.7.0) rb-fsevent (~> 0.10, >= 0.10.3) rb-inotify (~> 0.9, >= 0.9.10) locale (2.1.3) - loofah (2.10.0) + loofah (2.12.0) crass (~> 1.0.2) nokogiri (>= 1.5.9) mahoro (0.5) @@ -214,11 +214,11 @@ GEM mini_mime (>= 0.1.1) marcel (1.0.1) maxmind-db (1.0.0) - method_source (0.9.2) + method_source (1.0.0) mime-types (2.99.3) mini_magick (4.11.0) - mini_mime (1.1.0) - mini_portile2 (2.5.3) + mini_mime (1.1.1) + mini_portile2 (2.6.1) mini_racer (0.4.0) libv8-node (~> 15.14.0.0) minitest (5.14.4) @@ -232,47 +232,45 @@ GEM net-ssh (6.1.0) net-ssh-gateway (2.0.0) net-ssh (>= 4.0.0) - nio4r (2.5.7) - nokogiri (1.11.7) - mini_portile2 (~> 2.5.0) + nio4r (2.5.8) + nokogiri (1.12.5) + mini_portile2 (~> 2.6.1) racc (~> 1.4) oink (0.10.1) activerecord hodel_3000_compliant_logger open4 (1.3.4) - parallel (1.19.2) - parser (2.7.1.4) + parallel (1.21.0) + parser (3.0.2.0) 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) + pry (0.13.1) + coderay (~> 1.1) + method_source (~> 1.0) + pry-byebug (3.9.0) byebug (~> 11.0) - pry (~> 0.10) + pry (~> 0.13.0) public_suffix (4.0.6) racc (1.5.2) rack (2.2.3) - rack-ssl (1.4.1) - rack rack-test (1.1.0) rack (>= 1.0, < 3) rack-utf8_sanitizer (1.7.0) rack (>= 1.0, < 3.0) - rails (6.0.4) - actioncable (= 6.0.4) - actionmailbox (= 6.0.4) - actionmailer (= 6.0.4) - actionpack (= 6.0.4) - actiontext (= 6.0.4) - actionview (= 6.0.4) - activejob (= 6.0.4) - activemodel (= 6.0.4) - activerecord (= 6.0.4) - activestorage (= 6.0.4) - activesupport (= 6.0.4) - bundler (>= 1.3.0) - railties (= 6.0.4) + rails (6.1.4.1) + actioncable (= 6.1.4.1) + actionmailbox (= 6.1.4.1) + actionmailer (= 6.1.4.1) + actionpack (= 6.1.4.1) + actiontext (= 6.1.4.1) + actionview (= 6.1.4.1) + activejob (= 6.1.4.1) + activemodel (= 6.1.4.1) + activerecord (= 6.1.4.1) + activestorage (= 6.1.4.1) + activesupport (= 6.1.4.1) + bundler (>= 1.15.0) + railties (= 6.1.4.1) sprockets-rails (>= 2.0.0) rails-controller-testing (1.0.5) actionpack (>= 5.0.1.rc1) @@ -281,37 +279,33 @@ GEM rails-dom-testing (2.0.3) activesupport (>= 4.2.0) nokogiri (>= 1.6) - rails-html-sanitizer (1.3.0) + rails-html-sanitizer (1.4.2) loofah (~> 2.3) rails-i18n (6.0.0) i18n (>= 0.7, < 2) railties (>= 6.0.0, < 7) - railties (6.0.4) - actionpack (= 6.0.4) - activesupport (= 6.0.4) + railties (6.1.4.1) + actionpack (= 6.1.4.1) + activesupport (= 6.1.4.1) method_source - rake (>= 0.8.7) - thor (>= 0.20.3, < 2.0) + rake (>= 0.13) + thor (~> 1.0) rainbow (3.0.0) - rake (13.0.3) - rb-fsevent (0.10.4) + rake (13.0.6) + rb-fsevent (0.11.0) rb-inotify (0.10.1) ffi (~> 1.0) - recaptcha (5.6.0) + recaptcha (5.8.1) json - red-datasets (0.1.4) - csv (>= 3.0.5) - rexml - rubyzip - regexp_parser (1.7.1) + regexp_parser (2.1.1) request_store (1.5.0) rack (>= 1.4) rexml (3.2.5) rolify (5.3.0) rotp (6.2.0) - routing-filter (0.6.3) - actionpack (>= 4.2) - activesupport (>= 4.2) + routing-filter (0.7.0) + actionpack (>= 6.1) + activesupport (>= 6.1) rspec-activemodel-mocks (1.1.0) activemodel (>= 3.0) activesupport (>= 3.0) @@ -324,7 +318,7 @@ GEM rspec-mocks (3.10.2) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.10.0) - rspec-rails (5.0.1) + rspec-rails (5.0.2) actionpack (>= 5.2) activesupport (>= 5.2) railties (>= 5.2) @@ -333,23 +327,27 @@ GEM rspec-mocks (~> 3.10) rspec-support (~> 3.10) rspec-support (3.10.2) - rubocop (0.81.0) - jaro_winkler (~> 1.5.1) + rubocop (1.22.3) parallel (~> 1.10) - parser (>= 2.7.0.1) + parser (>= 3.0.0.0) rainbow (>= 2.2.2, < 4.0) + regexp_parser (>= 1.8, < 3.0) rexml + rubocop-ast (>= 1.12.0, < 2.0) ruby-progressbar (~> 1.7) - unicode-display_width (>= 1.4.0, < 2.0) - rubocop-performance (1.5.2) - rubocop (>= 0.71.0) - rubocop-rails (2.5.2) - activesupport + unicode-display_width (>= 1.4.0, < 3.0) + rubocop-ast (1.13.0) + parser (>= 3.0.1.1) + rubocop-performance (1.12.0) + rubocop (>= 1.7.0, < 2.0) + rubocop-ast (>= 0.4.0) + rubocop-rails (2.12.4) + activesupport (>= 4.2.0) rack (>= 1.1) - rubocop (>= 0.72.0) + rubocop (>= 1.7.0, < 2.0) ruby-ole (1.2.12.1) - ruby-progressbar (1.10.1) - rubyzip (1.3.0) + ruby-progressbar (1.11.0) + rubyzip (2.3.2) sass (3.4.25) sass-rails (5.0.8) railties (>= 5.2.0) @@ -357,7 +355,7 @@ GEM sprockets (>= 2.8, < 4.0) sprockets-rails (>= 2.0, < 4.0) tilt (>= 1.1, < 3) - secure_headers (6.3.2) + secure_headers (6.3.3) simplecov (0.17.1) docile (~> 1.1) json (>= 1.8, < 3) @@ -372,7 +370,7 @@ GEM activesupport (>= 4.0) sprockets (>= 3.0.0) statistics2 (0.54) - stripe (5.34.0) + stripe (5.39.0) syslog_protocol (0.9.2) text (1.3.1) thin (1.8.1) @@ -380,35 +378,35 @@ GEM eventmachine (~> 1.0, >= 1.0.4) rack (>= 1, < 3) thor (1.1.0) - thread_safe (0.3.6) tilt (2.0.10) - tzinfo (1.2.9) - thread_safe (~> 0.1) + tzinfo (2.0.4) + concurrent-ruby (~> 1.0) uglifier (4.2.0) execjs (>= 0.3.0, < 3) unicode (0.4.4.4) - unicode-display_width (1.7.0) + unicode-display_width (2.1.0) unidecoder (1.1.2) - uniform_notifier (1.14.1) + uniform_notifier (1.14.2) vpim (13.11.11) - web-console (3.7.0) - actionview (>= 5.0) - activemodel (>= 5.0) + web-console (4.1.0) + actionview (>= 6.0.0) + activemodel (>= 6.0.0) bindex (>= 0.4.0) - railties (>= 5.0) - webmock (3.13.0) - addressable (>= 2.3.6) + railties (>= 6.0.0) + webmock (3.14.0) + addressable (>= 2.8.0) crack (>= 0.3.2) hashdiff (>= 0.4.0, < 2.0.0) websocket-driver (0.7.5) websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) - will_paginate (3.3.0) + will_paginate (3.3.1) xapian-full-alaveteli (1.4.18.1) - xml-simple (1.1.8) + xml-simple (1.1.9) + rexml xpath (3.2.0) nokogiri (~> 1.8) - zeitwerk (2.4.2) + zeitwerk (2.5.1) zip_tricks (5.6.0) PLATFORMS @@ -421,33 +419,32 @@ DEPENDENCIES annotate (< 3.1.1) bcrypt (~> 3.1.16) bootstrap-sass (~> 2.3.2.2) - bullet (~> 6.1.4) - cancancan (~> 3.2.2) + bullet (~> 6.1.5) + cancancan (~> 3.3.0) capistrano (~> 2.15.0, < 3.0.0) - capybara (~> 3.15.1) + capybara (~> 3.35.3) charlock_holmes (~> 0.7.7) - dalli (~> 2.7.11) - dynamic_form (~> 1.1.0) + dalli (~> 3.0.4) exception_notification (~> 4.4.3) - factory_bot_rails (~> 5.2.0) + factory_bot_rails (~> 6.2.0) fancybox-rails (~> 0.3.0) - fast_gettext (~> 2.0.3) + fast_gettext (~> 2.1.0) fivemat (~> 1.3.7) gender_detector (~> 2.0.0) - gettext (~> 3.3.8) + gettext (~> 3.4.1) gettext_i18n_rails (~> 1.8.1) - globalize (~> 5.3.0) + globalize (~> 6.0.0) gnuplot (~> 2.6.0) - holidays (~> 7.1.0) + holidays (~> 8.4.1) htmlentities (~> 4.3.0) - i18n (~> 1.8.10) - icalendar (~> 2.5.3) + i18n (~> 1.8.11) + icalendar (~> 2.7.1) iso_country_codes (~> 0.7.8) jquery-rails (~> 4.4.0) jquery-ui-rails (~> 6.0.0) - json (~> 2.5.1) + json (~> 2.6.1) launchy (< 2.5.0) - listen (>= 3.0.5, < 3.5.1) + listen (>= 3.0.5, < 3.7.1) locale (~> 2.1.3) mahoro (~> 0.5) mail (~> 2.7.1) @@ -458,36 +455,34 @@ DEPENDENCIES money (~> 6.16.0) net-ssh (~> 6.1.0) net-ssh-gateway (>= 1.1.0, < 3.0.0) - nio4r - nokogiri (~> 1.11.7) + nokogiri (~> 1.12.5) oink (~> 0.10.1) open4 (~> 1.3.0) pg (~> 1.2.3) - pry (~> 0.12.2) - pry-byebug (~> 3.7.0) + pry (~> 0.13.0) + pry-byebug (~> 3.9.0) rack (~> 2.2.3) - rack-ssl (~> 1.4.0) rack-utf8_sanitizer (~> 1.7.0) - rails (~> 6.0.3) + rails (~> 6.1.4) rails-controller-testing rails-i18n (~> 6.0.0) - recaptcha (~> 5.6.0) + recaptcha (~> 5.8.1) rolify (~> 5.3.0) - routing-filter (~> 0.6.2) + routing-filter (~> 0.7.0) rspec-activemodel-mocks (~> 1.1.0) - rspec-rails (~> 5.0.1) - rubocop (~> 0.81.0) - rubocop-performance (~> 1.5.2) + rspec-rails (~> 5.0.2) + rubocop (~> 1.22.3) + rubocop-performance rubocop-rails ruby-msg (~> 1.5.0)! - rubyzip (~> 1.3.0, < 2.0.0) + rubyzip (~> 2.3.2) sass-rails (~> 5.0.8) - secure_headers (~> 6.3.2) + secure_headers (~> 6.3.3) simplecov (~> 0.17.1) simplecov-lcov (~> 0.7.0) statistics2 (~> 0.54) strip_attributes! - stripe (~> 5.34.0) + stripe (~> 5.39.0) stripe-ruby-mock! syslog_protocol (~> 0.9.0) thin (~> 1.8.1) @@ -496,8 +491,8 @@ DEPENDENCIES unidecoder (~> 1.1.0) vpim (~> 13.11.11) web-console (>= 3.3.0) - webmock (~> 3.13.0) - will_paginate (~> 3.3.0) + webmock (~> 3.14.0) + will_paginate (~> 3.3.1) xapian-full-alaveteli (~> 1.4.18.1) - xml-simple (~> 1.1.8) + xml-simple (~> 1.1.9) zip_tricks (~> 5.6.0) diff --git a/README.md b/README.md index 2dc053dff0..ab3a9806a6 100644 --- a/README.md +++ b/README.md @@ -17,9 +17,9 @@ all work together on producing the best possible software, and help move towards a world where governments approach transparency as the norm, rather than the exception. -Please join our mailing list at -https://groups.google.com/group/alaveteli-dev and introduce yourself, or -drop a line to hello@alaveteli.org to let us know that you're using Alaveteli. +Please join our [developers mailing list](https://groups.google.com/group/alaveteli-dev) +and introduce yourself, or drop a line to hello@alaveteli.org to let us know +that you're using Alaveteli. There's lots of useful information and documentation (including a blog) on [the project website](http://alaveteli.org). There's background @@ -39,6 +39,7 @@ Every Alaveteli commit is tested by GitHub Actions on the [following Ruby platfo * ruby-2.5 * ruby-2.6 +* ruby-2.7 If you use a ruby version management tool (such as RVM or .rbenv) and want to use the default development version used by the Alaveteli team (currently 2.5.8), you can create a `.ruby-version` symlink with a target of `.ruby-version.example` to switch to that automatically in the project directory. diff --git a/Vagrantfile b/Vagrantfile index 25aca5a265..02b04fe5ec 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -128,6 +128,10 @@ SUPPORTED_OPERATING_SYSTEMS = { box: 'ubuntu/bionic64', box_url: 'https://app.vagrantup.com/ubuntu/boxes/bionic64' }, + 'focal64' => { + box: 'ubuntu/focal64', + box_url: 'https://app.vagrantup.com/ubuntu/boxes/focal64' + }, 'stretch64' => { box: 'debian/stretch64', box_url: 'https://app.vagrantup.com/debian/boxes/stretch64' @@ -135,6 +139,10 @@ SUPPORTED_OPERATING_SYSTEMS = { 'buster64' => { box: 'debian/buster64', box_url: 'https://app.vagrantup.com/debian/boxes/buster64' + }, + 'bullseye64' => { + box: 'debian/bullseye64', + box_url: 'https://app.vagrantup.com/debian/boxes/bullseye64' } } diff --git a/app/assets/images/alaveteli-pro/pro-phase-icons--action-needed.png b/app/assets/images/alaveteli-pro/pro-phase-icons--action-needed.png index 205b6d5910..c3944d6250 100644 Binary files a/app/assets/images/alaveteli-pro/pro-phase-icons--action-needed.png and b/app/assets/images/alaveteli-pro/pro-phase-icons--action-needed.png differ diff --git a/app/assets/images/alaveteli-pro/pro-phase-icons--action-needed@2.png b/app/assets/images/alaveteli-pro/pro-phase-icons--action-needed@2.png index 37e796d6e2..e3d6343234 100644 Binary files a/app/assets/images/alaveteli-pro/pro-phase-icons--action-needed@2.png and b/app/assets/images/alaveteli-pro/pro-phase-icons--action-needed@2.png differ diff --git a/app/assets/images/alaveteli-pro/pro-phase-icons--complete.png b/app/assets/images/alaveteli-pro/pro-phase-icons--complete.png index bc47251b56..2fc4af5d6e 100644 Binary files a/app/assets/images/alaveteli-pro/pro-phase-icons--complete.png and b/app/assets/images/alaveteli-pro/pro-phase-icons--complete.png differ diff --git a/app/assets/images/alaveteli-pro/pro-phase-icons--complete@2.png b/app/assets/images/alaveteli-pro/pro-phase-icons--complete@2.png index f7e4351436..d826e5f6f9 100644 Binary files a/app/assets/images/alaveteli-pro/pro-phase-icons--complete@2.png and b/app/assets/images/alaveteli-pro/pro-phase-icons--complete@2.png differ diff --git a/app/assets/images/alaveteli-pro/pro-phase-icons--in-progress.png b/app/assets/images/alaveteli-pro/pro-phase-icons--in-progress.png index 463fc93160..a1607420b3 100644 Binary files a/app/assets/images/alaveteli-pro/pro-phase-icons--in-progress.png and b/app/assets/images/alaveteli-pro/pro-phase-icons--in-progress.png differ diff --git a/app/assets/images/alaveteli-pro/pro-phase-icons--in-progress@2.png b/app/assets/images/alaveteli-pro/pro-phase-icons--in-progress@2.png index e182700bc0..bdbe769004 100644 Binary files a/app/assets/images/alaveteli-pro/pro-phase-icons--in-progress@2.png and b/app/assets/images/alaveteli-pro/pro-phase-icons--in-progress@2.png differ diff --git a/app/assets/images/alaveteli-pro/pro-phase-icons.png b/app/assets/images/alaveteli-pro/pro-phase-icons.png index c8ea3b9b58..31a3b9f30f 100644 Binary files a/app/assets/images/alaveteli-pro/pro-phase-icons.png and b/app/assets/images/alaveteli-pro/pro-phase-icons.png differ diff --git a/app/assets/images/alaveteli-pro/pro-phase-icons@2.png b/app/assets/images/alaveteli-pro/pro-phase-icons@2.png index 9e7a779ebe..683caa8f15 100644 Binary files a/app/assets/images/alaveteli-pro/pro-phase-icons@2.png and b/app/assets/images/alaveteli-pro/pro-phase-icons@2.png differ diff --git a/app/assets/javascripts/wizard.js b/app/assets/javascripts/wizard.js index 8a3cb1d58c..39e4e0e182 100644 --- a/app/assets/javascripts/wizard.js +++ b/app/assets/javascripts/wizard.js @@ -357,4 +357,25 @@ }); + // work out what hashes we're looking for + var IDs = []; + + $("#help_unhappy h2[ID]").each(function(){ + IDs.push(this.id); + }); + + function checkHashes(hash) { + if(IDs.includes(hash.slice(1))) { + $(hash + ' + details' ).attr('open', '').addClass('flash'); + } + } + + // show unrolled details tag if they've come to the page with a known location hash + checkHashes(window.location.hash); + + // if the URL hash changes highlight the new hash + $(window).on('hashchange', function(e){ + checkHashes(window.location.hash); + }); + })(window.jQuery); diff --git a/app/assets/stylesheets/responsive/_search_style.scss b/app/assets/stylesheets/responsive/_search_style.scss index 236dcf2371..b4ccc014be 100644 --- a/app/assets/stylesheets/responsive/_search_style.scss +++ b/app/assets/stylesheets/responsive/_search_style.scss @@ -135,3 +135,16 @@ input.use-datepicker[type=text] { box-shadow: inset 0 2px 5px 1px rgba(0, 0, 0, 0.1); } } + +.search_latest { + ul.search_latest__list { + list-style: none; + padding-left: 0; + } + + li.search_latest__item { + a:after { + content: " →" + } + } +} diff --git a/app/assets/stylesheets/responsive/_sidebar_style.scss b/app/assets/stylesheets/responsive/_sidebar_style.scss index b40d357a36..f92bee00f5 100644 --- a/app/assets/stylesheets/responsive/_sidebar_style.scss +++ b/app/assets/stylesheets/responsive/_sidebar_style.scss @@ -16,6 +16,10 @@ } } +.sidebar__note { + font-size: 0.9em; +} + // Blog page doesn't have the request page header, so extra // padding is required to stop its contents touching the nav. #general_blog .right_column { diff --git a/app/assets/stylesheets/responsive/_wizard_style.scss b/app/assets/stylesheets/responsive/_wizard_style.scss index 097d436a84..5a960a615d 100644 --- a/app/assets/stylesheets/responsive/_wizard_style.scss +++ b/app/assets/stylesheets/responsive/_wizard_style.scss @@ -51,3 +51,12 @@ margin: 0.5em 0 0 0; } } + +.flash { + animation: target-fade 3s 1; +} + +@keyframes target-fade { + 0% { background-color: #F3BD2A; } + 100% { background-color: transparent; } +} diff --git a/app/assets/stylesheets/responsive/alaveteli_pro/_dashboard_layout.scss b/app/assets/stylesheets/responsive/alaveteli_pro/_dashboard_layout.scss index 61f1ef91a3..ca8357f997 100644 --- a/app/assets/stylesheets/responsive/alaveteli_pro/_dashboard_layout.scss +++ b/app/assets/stylesheets/responsive/alaveteli_pro/_dashboard_layout.scss @@ -43,7 +43,7 @@ $dashboard-collapse: 50em; //where the dashboard layout needs to change .dashboard-folders, .dashboard-sub-folders { - margin: 0; + margin: 0 0 1em 0; padding: 0; } @@ -51,9 +51,10 @@ $dashboard-collapse: 50em; //where the dashboard layout needs to change .dashboard-folder { font-size: 0.875em; //14px } - .dashboard-folder__name { - padding-left: 0.5em; - } +} + +.dashboard-folder__name { + padding-left: 0.5em; } .dashboard-folder--selected { diff --git a/app/assets/stylesheets/responsive/alaveteli_pro/_dashboard_style.scss b/app/assets/stylesheets/responsive/alaveteli_pro/_dashboard_style.scss index 24df89f679..4a2f2f045b 100644 --- a/app/assets/stylesheets/responsive/alaveteli_pro/_dashboard_style.scss +++ b/app/assets/stylesheets/responsive/alaveteli_pro/_dashboard_style.scss @@ -44,7 +44,7 @@ .phase-icon { background-image: image-url('alaveteli-pro/pro-phase-icons.png'); background-position: 0 -256px; - background-size: 44px 486.5px; + background-size: 44px 539px; width: 22px; height: 22px; display: inline-block; @@ -109,6 +109,10 @@ background-position: 0 -452px; } +.phase-icon--draft { + background-position: 0 -519px; +} + .dashboard-folder { .phase-icon { position: relative; @@ -153,26 +157,34 @@ .dashboard__announcements { background-color: #F3F1EB; - padding: 3em 1.5em 1em 1.5em; + padding: 0.5em 1.5em 1em 1.5em; position: relative; - @media (min-width: 28em) { + @include respond-min( $dashboard-collapse ) { padding-left: 80px; - padding-top: 1.5em; + padding-top: 0.5em; } &:after { content: ''; position: absolute; - top: -14px; - left: 1em; - height: 50px; - width: 51px; + top: 15px; + right: 1em; + + height: 31px; + width: 30px; background-image: image-url('alaveteli-pro/announcement.png'); @media (-webkit-min-device-pixel-ratio: 1.5), (min-resolution: 144dpi) { background-image: image-url('alaveteli-pro/announcement@2.png'); } background-repeat: no-repeat; background-position: top left; - background-size: 50px 51px; + background-size: 30px 31px; + @include respond-min( $dashboard-collapse ){ + top: -14px; + left: 1em; + height: 50px; + width: 51px; + background-size: 50px 51px; + } } } diff --git a/app/controllers/admin_controller.rb b/app/controllers/admin_controller.rb index 396800a358..f0201bf022 100644 --- a/app/controllers/admin_controller.rb +++ b/app/controllers/admin_controller.rb @@ -44,11 +44,14 @@ def authenticate else if session[:using_admin].nil? || session[:admin_name].nil? if params[:emergency].nil? || AlaveteliConfiguration::disable_emergency_user - if authenticated?( - :web => _("To log into the administrative interface"), - :email => _("Then you can log into the administrative interface"), - :email_subject => _("Log into the admin interface"), - :user_name => "a superuser") + if !authenticated? + ask_to_login( + web: _('To log into the administrative interface'), + email: _('Then you can log into the administrative interface'), + email_subject: _('Log into the admin interface'), + user_name: 'a superuser' + ) + else if !@user.nil? && @user.is_admin? session[:using_admin] = 1 session[:admin_name] = @user.url_name diff --git a/app/controllers/admin_general_controller.rb b/app/controllers/admin_general_controller.rb index 880a396932..60cb83a4be 100644 --- a/app/controllers/admin_general_controller.rb +++ b/app/controllers/admin_general_controller.rb @@ -17,6 +17,10 @@ def index @attention_requests = InfoRequest. find_in_state('attention_requested'). not_embargoed + + @old_unclassified_count = + InfoRequest.where_old_unclassified.is_searchable.count + @old_unclassified = InfoRequest.where_old_unclassified. limit(20). is_searchable @@ -156,7 +160,7 @@ def stats def debug @admin_current_user = admin_current_user - @current_commit = alaveteli_git_commit + @current_commit = Statistics::General.new.to_h[:alaveteli_git_commit] @current_branch = `git branch | perl -ne 'print $1 if /^\\* (.*)/'` @current_version = ALAVETELI_VERSION repo = `git remote show origin -n | perl -ne 'print $1 if m{Fetch URL: .*github\\.com[:/](.*)\\.git}'` diff --git a/app/controllers/admin_raw_email_controller.rb b/app/controllers/admin_raw_email_controller.rb index 5b9ac5b812..7acfa98aef 100644 --- a/app/controllers/admin_raw_email_controller.rb +++ b/app/controllers/admin_raw_email_controller.rb @@ -5,6 +5,8 @@ # Email: hello@mysociety.org; WWW: http://www.mysociety.org/ class AdminRawEmailController < AdminController + skip_before_action :html_response + before_action :set_raw_email, only: [:show] def show diff --git a/app/controllers/admin_users_sessions_controller.rb b/app/controllers/admin_users_sessions_controller.rb index fa9be46008..0e5d4cf19f 100644 --- a/app/controllers/admin_users_sessions_controller.rb +++ b/app/controllers/admin_users_sessions_controller.rb @@ -18,6 +18,7 @@ def create @admin_user.confirm! session[:user_id] = @admin_user.id + session[:user_login_token] = @admin_user.login_token session[:user_circumstance] = 'login_as' redirect_to user_path(@admin_user) diff --git a/app/controllers/alaveteli_pro/account_request_controller.rb b/app/controllers/alaveteli_pro/account_request_controller.rb index a5ef4cbac0..3dcfa00967 100644 --- a/app/controllers/alaveteli_pro/account_request_controller.rb +++ b/app/controllers/alaveteli_pro/account_request_controller.rb @@ -44,18 +44,16 @@ def grant_pro_access flash[:new_pro_user] = true flash[:notice] = _('Welcome to {{pro_site_name}}!', - pro_site_name: AlaveteliConfiguration.pro_site_name) + pro_site_name: pro_site_name) redirect_to alaveteli_pro_dashboard_path end def authenticate - post_redirect_params = { + authenticated? || ask_to_login( web: _('To upgrade your account'), email: _('Then you can upgrade your account'), email_subject: _('To upgrade your account') - } - - authenticated?(post_redirect_params) + ) end end diff --git a/app/controllers/alaveteli_pro/base_controller.rb b/app/controllers/alaveteli_pro/base_controller.rb index 9ef7e6f3d8..65f47cb652 100644 --- a/app/controllers/alaveteli_pro/base_controller.rb +++ b/app/controllers/alaveteli_pro/base_controller.rb @@ -14,20 +14,22 @@ class AlaveteliPro::BaseController < ApplicationController def pro_user_authenticated?(reason_params = nil) if reason_params.nil? reason_params = { - web: _("To access {{pro_site_name}}", - pro_site_name: AlaveteliConfiguration.pro_site_name), - email: _("Then you can access {{pro_site_name}}", - pro_site_name: AlaveteliConfiguration.pro_site_name) + web: _('To access {{pro_site_name}}', + pro_site_name: pro_site_name), + email: _('Then you can access {{pro_site_name}}', + pro_site_name: pro_site_name) } end - if authenticated?(reason_params) + if !authenticated? + ask_to_login(reason_params) + else unless current_user.is_pro? redirect_to( frontpage_path, flash: { - notice: _("This page is only accessible to {{pro_site_name}}" \ - " users", - pro_site_name: AlaveteliConfiguration.pro_site_name) + notice: _('This page is only accessible to {{pro_site_name}} ' \ + 'users', + pro_site_name: pro_site_name) } ) end @@ -36,12 +38,6 @@ def pro_user_authenticated?(reason_params = nil) return false end - # A pro-specific version of authenticated? that sets the `pro: true` param - # so that compatible controllers will know to use the pro livery post redirect - def pro_authenticated?(reason_params) - authenticated?(reason_params.merge(pro: true)) - end - # An override of set_in_pro_area from ApplicationController, because we are # always in the pro area if we're using a descendant of this controller. # Note that this is called as a before_action in this class, so that diff --git a/app/controllers/alaveteli_pro/batch_downloads_controller.rb b/app/controllers/alaveteli_pro/batch_downloads_controller.rb index 1a96dee219..e42a8530dc 100644 --- a/app/controllers/alaveteli_pro/batch_downloads_controller.rb +++ b/app/controllers/alaveteli_pro/batch_downloads_controller.rb @@ -5,11 +5,12 @@ class AlaveteliPro::BatchDownloadsController < AlaveteliPro::BaseController include ActionController::Live + skip_before_action :html_response + 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) diff --git a/app/controllers/alaveteli_pro/embargoes_controller.rb b/app/controllers/alaveteli_pro/embargoes_controller.rb index 6c6405d24a..1855b50fe2 100644 --- a/app/controllers/alaveteli_pro/embargoes_controller.rb +++ b/app/controllers/alaveteli_pro/embargoes_controller.rb @@ -20,7 +20,7 @@ def create if @embargo.save flash[:notice] = _("Your request will now be private on " \ "{{site_name}} until {{expiry_date}}.", - site_name: AlaveteliConfiguration.site_name, + site_name: site_name, expiry_date: I18n.l( @embargo.publish_at, format: '%d %B %Y')) else diff --git a/app/controllers/alaveteli_pro/info_request_batches_controller.rb b/app/controllers/alaveteli_pro/info_request_batches_controller.rb index c46d803d74..2e2a340f1f 100644 --- a/app/controllers/alaveteli_pro/info_request_batches_controller.rb +++ b/app/controllers/alaveteli_pro/info_request_batches_controller.rb @@ -32,7 +32,7 @@ def create handle_rate_monitor_limit_hit(current_user.id) end - @info_request_batch.save + @info_request_batch.save! @draft_info_request_batch.destroy redirect_to show_alaveteli_pro_batch_request_path(id: @info_request_batch.id) else diff --git a/app/controllers/alaveteli_pro/info_requests_controller.rb b/app/controllers/alaveteli_pro/info_requests_controller.rb index 3e066c792c..3e0bece21f 100644 --- a/app/controllers/alaveteli_pro/info_requests_controller.rb +++ b/app/controllers/alaveteli_pro/info_requests_controller.rb @@ -43,8 +43,8 @@ def preview def create if all_models_valid? - @info_request.save # Saves @outgoing_message too - @embargo.save if @embargo.present? + @info_request.save! # Saves @outgoing_message too + @embargo.save! if @embargo.present? send_initial_message(@outgoing_message) destroy_draft redirect_to show_alaveteli_pro_request_path( @@ -70,20 +70,21 @@ def all_models_valid? end def set_draft - if params[:draft_id] - @draft_info_request = current_user.draft_info_requests.find( - params[:draft_id] - ) - end + return unless params[:draft_id] + @draft_info_request = + current_user.draft_info_requests.find(params[:draft_id]) end def set_public_body - if params[:public_body] - @public_body = PublicBody.find_by_url_name(params[:public_body]) - end + return unless params[:public_body] + @public_body = PublicBody.find_by_url_name(params[:public_body]) end def load_data_from_draft + unless @draft_info_request + return redirect_to new_alaveteli_pro_info_request_path + end + @info_request = InfoRequest.from_draft(@draft_info_request) @outgoing_message = @info_request.outgoing_messages.first @embargo = @info_request.embargo @@ -99,23 +100,22 @@ def create_initial_objects end def destroy_draft - if params[:draft_id] - current_user.draft_info_requests.destroy(params[:draft_id]) - end + return unless params[:draft_id] + current_user.draft_info_requests.destroy(params[:draft_id]) end def send_initial_message(outgoing_message) - if outgoing_message.sendable? - mail_message = OutgoingMailer.initial_request( - outgoing_message.info_request, - outgoing_message - ).deliver_now - - outgoing_message.record_email_delivery( - mail_message.to_addrs.join(', '), - mail_message.message_id - ) - end + return unless outgoing_message.sendable? + + mail_message = OutgoingMailer.initial_request( + outgoing_message.info_request, + outgoing_message + ).deliver_now + + outgoing_message.record_email_delivery( + mail_message.to_addrs.join(', '), + mail_message.message_id + ) end def request_filter_params @@ -125,12 +125,11 @@ def request_filter_params end def check_public_body_is_requestable - if @info_request.public_body - unless @info_request.public_body.is_requestable? - reason = @info_request.public_body.not_requestable_reason - view = "request/new_#{reason}.html.erb" - render view - end - end + return if @info_request.public_body.nil? || + @info_request.public_body.is_requestable? + + reason = @info_request.public_body.not_requestable_reason + view = "request/new_#{reason}.html.erb" + render view end end diff --git a/app/controllers/alaveteli_pro/payment_methods_controller.rb b/app/controllers/alaveteli_pro/payment_methods_controller.rb index c942fcb74f..0a53765565 100644 --- a/app/controllers/alaveteli_pro/payment_methods_controller.rb +++ b/app/controllers/alaveteli_pro/payment_methods_controller.rb @@ -34,11 +34,11 @@ def update private def authenticate - post_redirect_params = { - :web => _('To update your payment details'), - :email => _('Then you can update your payment details'), - :email_subject => _('To update your payment details') } - authenticated?(post_redirect_params) + authenticated? || ask_to_login( + web: _('To update your payment details'), + email: _('Then you can update your payment details'), + email_subject: _('To update your payment details') + ) end end diff --git a/app/controllers/alaveteli_pro/plans_controller.rb b/app/controllers/alaveteli_pro/plans_controller.rb index b7ff59133f..cc33c016c4 100644 --- a/app/controllers/alaveteli_pro/plans_controller.rb +++ b/app/controllers/alaveteli_pro/plans_controller.rb @@ -8,7 +8,7 @@ def index default_plan_name = add_stripe_namespace('pro') stripe_plan = Stripe::Plan.retrieve(default_plan_name) @plan = AlaveteliPro::WithTax.new(stripe_plan) - @pro_site_name = AlaveteliConfiguration.pro_site_name + @pro_site_name = pro_site_name end def show @@ -25,15 +25,15 @@ def plan_name end def authenticate - post_redirect_params = { + authenticated? || ask_to_login( + pro: true, web: _('To signup to {{site_name}}', - site_name: AlaveteliConfiguration.pro_site_name), + site_name: pro_site_name), email: _('Then you can activate your {{site_name}} account', - site_name: AlaveteliConfiguration.pro_site_name), + site_name: pro_site_name), email_subject: _('Confirm your account on {{site_name}}', - site_name: AlaveteliConfiguration.pro_site_name) } - - pro_authenticated?(post_redirect_params) + site_name: pro_site_name) + ) end def check_has_current_subscription diff --git a/app/controllers/alaveteli_pro/public_bodies_controller.rb b/app/controllers/alaveteli_pro/public_bodies_controller.rb index 70a87a7b18..417d74800b 100644 --- a/app/controllers/alaveteli_pro/public_bodies_controller.rb +++ b/app/controllers/alaveteli_pro/public_bodies_controller.rb @@ -1,6 +1,8 @@ class AlaveteliPro::PublicBodiesController < AlaveteliPro::BaseController include AlaveteliPro::PublicBodiesHelper + skip_before_action :html_response + def index query = params[:query] || "" xapian_results = typeahead_search(query, :model => PublicBody, diff --git a/app/controllers/alaveteli_pro/stripe_webhooks_controller.rb b/app/controllers/alaveteli_pro/stripe_webhooks_controller.rb index 947f576cc3..b10267da86 100644 --- a/app/controllers/alaveteli_pro/stripe_webhooks_controller.rb +++ b/app/controllers/alaveteli_pro/stripe_webhooks_controller.rb @@ -3,6 +3,8 @@ class AlaveteliPro::StripeWebhooksController < ApplicationController class MissingTypeStripeWebhookError < StandardError; end class UnknownPlanStripeWebhookError < StandardError; end + skip_before_action :html_response + rescue_from JSON::ParserError, MissingTypeStripeWebhookError do |exception| # Invalid payload, reject the webhook notify_exception(exception) @@ -59,7 +61,7 @@ def invoice_payment_succeeded plan_name = subscription.plan.name charge.description = - "#{ AlaveteliConfiguration.pro_site_name }: #{ plan_name }" + "#{ pro_site_name }: #{ plan_name }" charge.save end diff --git a/app/controllers/alaveteli_pro/subscriptions_controller.rb b/app/controllers/alaveteli_pro/subscriptions_controller.rb index 97daaa95d0..52317582cb 100644 --- a/app/controllers/alaveteli_pro/subscriptions_controller.rb +++ b/app/controllers/alaveteli_pro/subscriptions_controller.rb @@ -1,6 +1,7 @@ class AlaveteliPro::SubscriptionsController < AlaveteliPro::BaseController include AlaveteliPro::StripeNamespace + skip_before_action :html_response, only: [:create, :authorise] skip_before_action :pro_user_authenticated?, only: [:create, :authorise] before_action :authenticate, only: [:create, :authorise] before_action :prevent_duplicate_submission, only: [:create] @@ -160,7 +161,7 @@ def destroy flash[:notice] = _('You have successfully cancelled your subscription ' \ 'to {{pro_site_name}}', - pro_site_name: AlaveteliConfiguration.pro_site_name) + pro_site_name: pro_site_name) rescue Stripe::RateLimitError, Stripe::InvalidRequestError, @@ -181,11 +182,11 @@ def destroy private def authenticate - post_redirect_params = { - :web => _('To upgrade your account'), - :email => _('Then you can upgrade your account'), - :email_subject => _('To upgrade your account') } - authenticated?(post_redirect_params) + authenticated? || ask_to_login( + web: _('To upgrade your account'), + email: _('Then you can upgrade your account'), + email_subject: _('To upgrade your account') + ) end def check_has_current_subscription diff --git a/app/controllers/api_controller.rb b/app/controllers/api_controller.rb index 99d1cf8489..bec3b62370 100644 --- a/app/controllers/api_controller.rb +++ b/app/controllers/api_controller.rb @@ -1,4 +1,6 @@ class ApiController < ApplicationController + skip_before_action :html_response + before_action :check_api_key before_action :check_external_request, :only => [:add_correspondence, :update_state] diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index d5a997fe20..8d1c892c51 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -15,8 +15,8 @@ class RouteNotFound < StandardError before_action :set_gettext_locale before_action :collect_locales - protect_from_forgery :if => :user?, :with => :exception - skip_before_action :verify_authenticity_token, :unless => :user? + protect_from_forgery if: :authenticated?, with: :exception + skip_before_action :verify_authenticity_token, unless: :authenticated? # Deal with access denied errors from CanCan rescue_from CanCan::AccessDenied do |exception| @@ -33,6 +33,7 @@ class RouteNotFound < StandardError include AlaveteliPro::PostRedirectHandler # Note: a filter stops the chain if it redirects or renders something + before_action :html_response before_action :authentication_check before_action :check_in_post_redirect before_action :session_remember_me @@ -46,9 +47,9 @@ def set_vary_header helper_method :anonymous_cache, :short_cache, :medium_cache, :long_cache def anonymous_cache(time) - if session[:user_id].nil? - headers['Cache-Control'] = "max-age=#{time}, public" - end + return if authenticated? + + headers['Cache-Control'] = "max-age=#{time}, public" end def short_cache @@ -140,12 +141,21 @@ def validate_session_timestamp end def persist_session_timestamp - session[:ttl] = Time.zone.now if session[:user_id] && !session[:remember_me] + session[:ttl] = Time.zone.now if authenticated? && !session[:remember_me] + end + + def sign_in(user, remember_me: nil) + remember_me ||= session[:remember_me] + clear_session_credentials + session[:user_id] = user.id + session[:user_login_token] = user.login_token + session[:remember_me] = remember_me end # Logout form def clear_session_credentials session[:user_id] = nil + session[:user_login_token] = nil session[:user_circumstance] = nil session[:remember_me] = false session[:using_admin] = nil @@ -216,18 +226,46 @@ def set_in_pro_area private def user? - !session[:user_id].nil? + warn 'DEPRECATION: ApplicationController#user? will be removed in 0.41. ' \ + 'It has been replaced with authenticated?' + + authenticated? end # Override the Rails method to only set the CSRF form token if there is a # logged in user def form_authenticity_token(*args) - super if user? + super if authenticated? end # Check the user is logged in - def authenticated?(reason_params = {}) - return true if session[:user_id] + def authenticated?(as: nil, **reason_params) + unless reason_params.empty? + warn 'DEPRECATION: ApplicationController#authenticated?(reason_params) ' \ + 'will be removed in 0.41. It has been replaced with ' \ + 'ApplicationController#authenticated? || ' \ + 'ApplicationController#ask_to_login(**reason_params)' + return authenticated?(as: as) || ask_to_login(**reason_params) + end + + if as + authenticated_user == as + else + authenticated_user.present? + end + end + + def ask_to_login(as: nil, **reason_params) + if as + reason_params[:user_name] = as.name + reason_params[:user_url] = show_user_url(url_name: as.url_name) + + if authenticated? + # They are already logged in, but as the wrong user + @reason_params = reason_params + render(template: 'user/wrong_user') && return + end + end post_redirect = reason_params.delete(:post_redirect) post_redirect ||= PostRedirect.new(uri: request.fullpath, @@ -237,7 +275,7 @@ def authenticated?(reason_params = {}) # 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' + headers['Cache-Control'] = 'private, no-cache, no-store, max-age=0, must-revalidate' # HTTP 1.0 headers['Pragma'] = 'no-cache' # Proxies @@ -246,38 +284,26 @@ def authenticated?(reason_params = {}) # '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 + + false end - def authenticated_as_user?(user, reason_params = {}) - reason_params[:user_name] = user.name - reason_params[:user_url] = show_user_url(:url_name => user.url_name) - if session[:user_id] - if session[:user_id] == user.id - # They are logged in as the right user - return true - else - # They are already logged in, but as the wrong user - @reason_params = reason_params - render :template => 'user/wrong_user' - return - end - end - # They are not logged in at all - return authenticated?(reason_params) + def authenticated_as_user?(user, reason_params = nil) + warn 'DEPRECATION: ApplicationController#authenticated_as_user?(user, ' \ + 'reason_params) will be removed in 0.41. It has been replaced with ' \ + 'ApplicationController#authenticated?(as: user) || ' \ + 'ApplicationController#ask_to_login(as: user, **reason_params)' + + authenticated?(as: user) || ask_to_login(as: user, **reason_params) end # Return logged in user def authenticated_user - if session[:user_id].nil? - return nil - else - begin - return User.find(session[:user_id]) - rescue ActiveRecord::RecordNotFound - return nil - end - end + return unless session[:user_id] + + @user ||= User.find_by( + id: session[:user_id], login_token: session[:user_login_token] + ) end # For CanCanCan and other libs which need a Devise-like current_user method @@ -345,11 +371,13 @@ def check_in_post_redirect end end + def html_response + respond_to :html + end + # Default layout shows user in corner, so needs access to it def authentication_check - if session[:user_id] - @user = authenticated_user - end + @user ||= authenticated_user end # @@ -433,16 +461,17 @@ def typeahead_search(query, options) # Store last visited pages, for contact form; but only for logged in users, as otherwise this breaks caching def set_last_request(info_request) - if !session[:user_id].nil? - cookies["last_request_id"] = info_request.id - cookies["last_body_id"] = nil - end + return unless authenticated? + + cookies["last_request_id"] = info_request.id + cookies["last_body_id"] = nil end + def set_last_body(public_body) - if !session[:user_id].nil? - cookies["last_request_id"] = nil - cookies["last_body_id"] = public_body.id - end + return unless authenticated? + + cookies["last_request_id"] = nil + cookies["last_body_id"] = public_body.id end def country_from_ip @@ -458,10 +487,6 @@ def user_ip end end - def alaveteli_git_commit - `git log -1 --format="%H"`.strip - end - # URL Encode the path parameter for use in render_exception # # params - the params Hash diff --git a/app/controllers/attachments_controller.rb b/app/controllers/attachments_controller.rb index a6c276b2dc..c64174f82e 100644 --- a/app/controllers/attachments_controller.rb +++ b/app/controllers/attachments_controller.rb @@ -4,6 +4,9 @@ class AttachmentsController < ApplicationController include FragmentCachable include InfoRequestHelper + include PublicTokenable + + skip_before_action :html_response before_action :find_info_request, :find_incoming_message, :find_attachment before_action :find_project @@ -55,11 +58,7 @@ def show_as_html } ) - html = if rails_upgrade? - @incoming_message.apply_masks(html, response.media_type) - else - @incoming_message.apply_masks(html, response.content_type) - end + html = @incoming_message.apply_masks(html, response.media_type) render html: html.html_safe end @@ -67,7 +66,12 @@ def show_as_html private def find_info_request - @info_request = InfoRequest.find(params[:id]) + @info_request = + if public_token? + InfoRequest.find_by!(public_token: public_token) + else + InfoRequest.find(params[:id]) + end end def find_incoming_message @@ -195,7 +199,9 @@ def cache_key_path end def current_ability - @current_ability ||= Ability.new(current_user, project: @project) + @current_ability ||= Ability.new( + current_user, project: @project, public_token: public_token? + ) end def prominence diff --git a/app/controllers/citations_controller.rb b/app/controllers/citations_controller.rb index 331fac36f6..0034ebca64 100644 --- a/app/controllers/citations_controller.rb +++ b/app/controllers/citations_controller.rb @@ -26,7 +26,7 @@ def create private def authenticate - authenticated?( + authenticated? || ask_to_login( web: _('To add a citation'), email: _('Then you can add citations'), email_subject: _('Confirm your account on {{site_name}}', diff --git a/app/controllers/classifications_controller.rb b/app/controllers/classifications_controller.rb index a6267d336a..a0c81a6e87 100644 --- a/app/controllers/classifications_controller.rb +++ b/app/controllers/classifications_controller.rb @@ -7,8 +7,8 @@ class ClassificationsController < ApplicationController prepend_before_action :check_read_only, only: :create rescue_from CanCan::AccessDenied do - authenticated_as_user?( - @info_request.user, + authenticated?(as: @info_request.user) || ask_to_login( + as: @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}}.', diff --git a/app/controllers/comment_controller.rb b/app/controllers/comment_controller.rb index c5ed8ebce6..d913f679b4 100644 --- a/app/controllers/comment_controller.rb +++ b/app/controllers/comment_controller.rb @@ -38,12 +38,16 @@ def new return end - if authenticated?( - :web => _("To post your annotation"), - :email => _("Then your annotation to {{info_request_title}} will be posted.",:info_request_title=>@info_request.title), - :email_subject => _("Confirm your annotation to {{info_request_title}}",:info_request_title=>@info_request.title) + if !authenticated? + ask_to_login( + web: _('To post your annotation'), + email: _('Then your annotation to {{info_request_title}} will be ' \ + 'posted.', + info_request_title: @info_request.title), + email_subject: _('Confirm your annotation to {{info_request_title}}', + info_request_title: @info_request.title) ) - + else if spam_comment?(params[:comment][:body], @user) handle_spam_comment(@user) && return end @@ -73,8 +77,6 @@ def new # we don't use comment_url here, as then you don't see the flash at top of page redirect_to request_url(@info_request) - else - # do nothing - as "authenticated?" has done the redirect to signin page for us end end @@ -112,10 +114,10 @@ def reject_unless_comments_allowed # Banned from adding comments? def reject_if_user_banned - if authenticated_user && !authenticated_user.can_make_comments? - @details = authenticated_user.can_fail_html - render :template => 'user/banned' - end + return unless authenticated? && !authenticated_user.can_make_comments? + + @details = authenticated_user.can_fail_html + render template: 'user/banned' end # An override of ApplicationController#set_in_pro_area to set the flag diff --git a/app/controllers/concerns/public_tokenable.rb b/app/controllers/concerns/public_tokenable.rb new file mode 100644 index 0000000000..32f2795bae --- /dev/null +++ b/app/controllers/concerns/public_tokenable.rb @@ -0,0 +1,30 @@ +## +# Module with helper methods for controllers which allows them to respond +# differently when a public token param exists. +# +# This this is done primarily by redefining the current ability method. +# +module PublicTokenable + extend ActiveSupport::Concern + + included do + before_action :set_no_crawl_headers + helper_method :public_token + end + + private + + def set_no_crawl_headers + headers['X-Robots-Tag'] = 'noindex' if public_token + end + + def public_token + params[:public_token] + end + + def current_ability + @current_ability ||= Ability.new( + current_user, public_token: public_token.present? + ) + end +end diff --git a/app/controllers/followups_controller.rb b/app/controllers/followups_controller.rb index 0cabb1b786..24e974cbcd 100644 --- a/app/controllers/followups_controller.rb +++ b/app/controllers/followups_controller.rb @@ -88,15 +88,16 @@ def check_user_credentials # We want to make sure they're the right user first, before they start # writing a message and wasting their time if they are not the requester. params = get_login_params(@incoming_message, @info_request) - return if !authenticated_as_user?(@info_request.user, params) - if authenticated_user and !authenticated_user.can_make_followup? + unless authenticated?(as: @info_request.user) + ask_to_login(as: @info_request.user, **params) + return + end + if authenticated? && !authenticated_user.can_make_followup? @details = authenticated_user.can_fail_html render :template => 'user/banned' return end - if authenticated_user && cannot?(:read, @info_request) - return render_hidden - end + render_hidden if authenticated? && cannot?(:read, @info_request) end def get_login_params(is_incoming, info_request) diff --git a/app/controllers/general_controller.rb b/app/controllers/general_controller.rb index de5ffa1ebc..dfa30f67ea 100644 --- a/app/controllers/general_controller.rb +++ b/app/controllers/general_controller.rb @@ -9,6 +9,8 @@ class GeneralController < ApplicationController MAX_RESULTS = 500 + skip_before_action :html_response, only: :version + before_action :redirect_pros_to_dashboard, only: :frontpage # New, improved front page! @@ -23,8 +25,6 @@ def frontpage @feed_autodetect = [ { :url => do_track_url(@track_thing, 'feed'), :title => _('Successful requests'), :has_json => true } ] - - respond_to :html end # Display blog entries @@ -36,8 +36,6 @@ def blog medium_cache get_blog_content - - respond_to :html end def get_blog_content @@ -88,15 +86,6 @@ def search # TODO: Why is this so complicated with arrays and stuff? Look at the route # in config/routes.rb for comments. - # 404 if the request is a format we don't support (e.g:.json) - # 200 if the request is an invalid format (e.g: .invalid). This allows - # invalid search terms to render the search results page with a "no results - # found" message. - if !request.format.nil? && !request.format.html? - respond_to { |format| format.any { head :not_found } } - return - end - combined = params[:combined].split("/") @sortby = nil @bodies = @requests = @users = true @@ -218,24 +207,7 @@ def not_found def version respond_to do |format| - format.json { - render json: { - alaveteli_git_commit: alaveteli_git_commit, - alaveteli_version: ALAVETELI_VERSION, - ruby_version: RUBY_VERSION, - visible_public_body_count: PublicBody.visible.count, - visible_request_count: InfoRequest.is_searchable.count, - private_request_count: InfoRequest.embargoed.count, - confirmed_user_count: User.active.where(email_confirmed: true).count, - visible_comment_count: Comment.visible.count, - track_thing_count: TrackThing.count, - widget_vote_count: WidgetVote.count, - public_body_change_request_count: PublicBodyChangeRequest.count, - request_classification_count: RequestClassification.count, - visible_followup_message_count: OutgoingMessage. - where(prominence: 'normal', message_type: 'followup').count - } - } + format.json { render json: Statistics::General.new } end end diff --git a/app/controllers/health_checks_controller.rb b/app/controllers/health_checks_controller.rb index 473a1aaccd..87a39ea59d 100644 --- a/app/controllers/health_checks_controller.rb +++ b/app/controllers/health_checks_controller.rb @@ -3,14 +3,11 @@ class HealthChecksController < ApplicationController def index @health_checks = HealthChecks.all - respond_to do |format| - if HealthChecks.ok? - format.html { render :action => :index, :layout => false } - else - format.html { render :action => :index, :layout => false , :status => 500 } - end + if HealthChecks.ok? + render :action => :index, :layout => false + else + render :action => :index, :layout => false , :status => 500 end - end end diff --git a/app/controllers/one_time_passwords_controller.rb b/app/controllers/one_time_passwords_controller.rb index ede67dc48a..1993ad3650 100644 --- a/app/controllers/one_time_passwords_controller.rb +++ b/app/controllers/one_time_passwords_controller.rb @@ -52,11 +52,10 @@ def check_two_factor_config end def authenticate - post_redirect_params = { - :web => _('To view your two factor authentication details'), - :email => _('To view your two factor authentication details'), - :email_subject => _('To view your two factor authentication details') } - - authenticated?(post_redirect_params) + authenticated? || ask_to_login( + web: _('To view your two factor authentication details'), + email: _('To view your two factor authentication details'), + email_subject: _('To view your two factor authentication details') + ) end end diff --git a/app/controllers/outgoing_messages/delivery_statuses_controller.rb b/app/controllers/outgoing_messages/delivery_statuses_controller.rb index 93248b024d..acad9bf475 100644 --- a/app/controllers/outgoing_messages/delivery_statuses_controller.rb +++ b/app/controllers/outgoing_messages/delivery_statuses_controller.rb @@ -14,8 +14,6 @@ def show log.line(:redact => !@user.is_admin?) end end - - respond_to :html end protected diff --git a/app/controllers/password_changes_controller.rb b/app/controllers/password_changes_controller.rb index b01b98b043..1e3f1b3e6a 100644 --- a/app/controllers/password_changes_controller.rb +++ b/app/controllers/password_changes_controller.rb @@ -85,7 +85,7 @@ def update end if @password_change_user.save - session[:user_id] ||= @password_change_user.id + sign_in(@password_change_user) if @pretoken_redirect if otp_enabled?(@password_change_user) diff --git a/app/controllers/projects/classifies_controller.rb b/app/controllers/projects/classifies_controller.rb index fd57472730..0dbd4e2552 100644 --- a/app/controllers/projects/classifies_controller.rb +++ b/app/controllers/projects/classifies_controller.rb @@ -47,7 +47,7 @@ def update private def authenticate - authenticated?( + authenticated? || ask_to_login( web: _('To join this project'), email: _('Then you can join this project'), email_subject: _('Confirm your account on {{site_name}}', diff --git a/app/controllers/projects/contributors_controller.rb b/app/controllers/projects/contributors_controller.rb index a140acff6d..1679fb6587 100644 --- a/app/controllers/projects/contributors_controller.rb +++ b/app/controllers/projects/contributors_controller.rb @@ -25,6 +25,6 @@ def authenticate! } ) - authenticated?(post_redirect: post_redirect) + authenticated? || ask_to_login(post_redirect: post_redirect) end end diff --git a/app/controllers/projects/downloads_controller.rb b/app/controllers/projects/downloads_controller.rb index 061f004f7c..f2cde94183 100644 --- a/app/controllers/projects/downloads_controller.rb +++ b/app/controllers/projects/downloads_controller.rb @@ -2,11 +2,12 @@ # Controller which manages Project data downloads. # class Projects::DownloadsController < Projects::BaseController + skip_before_action :html_response + 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' diff --git a/app/controllers/projects/extracts_controller.rb b/app/controllers/projects/extracts_controller.rb index 60bac4d2d0..3b3bd24d1e 100644 --- a/app/controllers/projects/extracts_controller.rb +++ b/app/controllers/projects/extracts_controller.rb @@ -50,7 +50,7 @@ def create private def authenticate - authenticated?( + authenticated? || ask_to_login( web: _('To join this project'), email: _('Then you can join this project'), email_subject: _('Confirm your account on {{site_name}}', diff --git a/app/controllers/projects/invites_controller.rb b/app/controllers/projects/invites_controller.rb index 01138b20af..080dc6a281 100644 --- a/app/controllers/projects/invites_controller.rb +++ b/app/controllers/projects/invites_controller.rb @@ -20,7 +20,7 @@ def find_project end def authenticate - authenticated?( + authenticated? || ask_to_login( web: _('To join this project'), email: _('Then you can join this project'), email_subject: _('Confirm your account on {{site_name}}', diff --git a/app/controllers/projects/projects_controller.rb b/app/controllers/projects/projects_controller.rb index b2aab27716..883e094016 100644 --- a/app/controllers/projects/projects_controller.rb +++ b/app/controllers/projects/projects_controller.rb @@ -13,7 +13,7 @@ def find_project end def authenticate - authenticated?( + authenticated? || ask_to_login( web: _('To join this project'), email: _('Then you can join this project'), email_subject: _('Confirm your account on {{site_name}}', diff --git a/app/controllers/public_body_controller.rb b/app/controllers/public_body_controller.rb index acfc88f3c3..f3c1271d71 100644 --- a/app/controllers/public_body_controller.rb +++ b/app/controllers/public_body_controller.rb @@ -7,6 +7,7 @@ require 'tempfile' class PublicBodyController < ApplicationController + skip_before_action :html_response, only: [:show, :list_all_csv] MAX_RESULTS = 500 # TODO: tidy this up with better error messages, and a more standard infrastructure for the redirect to canonical URL @@ -162,9 +163,7 @@ def list end end - respond_to do |format| - format.html { render :template => 'public_body/list' } - end + render :template => 'public_body/list' end end diff --git a/app/controllers/public_tokens_controller.rb b/app/controllers/public_tokens_controller.rb new file mode 100644 index 0000000000..2734b83a23 --- /dev/null +++ b/app/controllers/public_tokens_controller.rb @@ -0,0 +1,88 @@ +## +# Controller responsible for creating, destroying public tokens and rendering +# any InfoRequest by its public token +# +class PublicTokensController < ApplicationController + include PublicTokenable + + before_action :find_info_request, only: %i[create destroy] + before_action :can_share_info_request, only: %i[create destroy] + + before_action :find_info_request_by_public_token, only: :show + before_action :can_view_info_request, only: :show + before_action :assign_variables_for_show_template, only: :show + + def show + render template: 'request/show' + end + + def create + @info_request.enable_public_token! + + public_url = public_token_url(@info_request.public_token, locale: false) + anchor = helpers.link_to(public_url, public_url, target: '_blank') + + flash.notice = { + inline: _('This request is now publicly accessible via {{public_url}}', + public_url: anchor) + } + + redirect_to show_request_path(@info_request.url_title) + end + + def destroy + @info_request.disable_public_token! + + flash.notice = _('The publicly accessible link for this request has now ' \ + 'been disabled') + + redirect_to show_request_path(@info_request.url_title) + end + + private + + def find_info_request + @info_request = InfoRequest.find_by!(url_title: params[:url_title]) + end + + def can_share_info_request + authorize! :share, @info_request + end + + def find_info_request_by_public_token + @info_request = InfoRequest.find_by!(public_token: public_token) + end + + def can_view_info_request + if guest.can?(:read, @info_request) + redirect_to show_request_path(@info_request.url_title) + elsif cannot?(:read, @info_request) + render_hidden + end + end + + def guest + @guest ||= Ability.new(nil) + end + + # rubocop:disable all + def assign_variables_for_show_template + # taken from RequestController#assign_variables_for_show_template + + @show_profile_photo = !!( + !@info_request.is_external? && + @info_request.user.show_profile_photo? && + !@render_to_file + ) + + @old_unclassified = + @info_request.is_old_unclassified? && !authenticated_user.nil? + @is_owning_user = @info_request.is_owning_user?(authenticated_user) + @new_responses_count = + @info_request. + events_needing_description. + select { |event| event.event_type == 'response' }. + size + end + # rubocop:enable all +end diff --git a/app/controllers/refusal_advice_controller.rb b/app/controllers/refusal_advice_controller.rb index 3651e422d3..fcd74bec8c 100644 --- a/app/controllers/refusal_advice_controller.rb +++ b/app/controllers/refusal_advice_controller.rb @@ -20,7 +20,8 @@ def create private def authenticate - authenticated_as_user?(info_request.user) if info_request + return unless info_request + authenticated?(as: info_request.user) || ask_to_login(as: info_request.user) end def log_event @@ -37,15 +38,11 @@ def internal_redirect_to case action.target[:internal] when 'followup' - redirect_to new_request_followup_path( - request_id: info_request.id, anchor: 'followup' - ) - + redirect_to new_request_followup_path(request_id: info_request.id) when 'internal_review' redirect_to new_request_followup_path( - request_id: info_request.id, internal_review: '1', anchor: 'followup' + request_id: info_request.id, internal_review: '1' ) - when 'new_request' redirect_to new_request_to_body_path( url_name: info_request.public_body.url_name diff --git a/app/controllers/reports_controller.rb b/app/controllers/reports_controller.rb index 16645c0218..166639a82d 100644 --- a/app/controllers/reports_controller.rb +++ b/app/controllers/reports_controller.rb @@ -12,7 +12,7 @@ def create render "new" return end - if !authenticated_user + if !authenticated? flash[:notice] = _("You need to be logged in to report a request for administrator attention") elsif @info_request.attention_requested flash[:notice] = _("This request has already been reported for administrator attention") @@ -35,12 +35,15 @@ def new _("Report request: {{title}}", :title => @info_request.title) end - if authenticated?( - :web => _("To report this request"), - :email => _("Then you can report the request '{{title}}'", :title => @info_request.title), - :email_subject => _("Report an offensive or unsuitable request"), - :comment_id => params[:comment_id]) - end + return if authenticated? + + ask_to_login( + web: _('To report this request'), + email: _("Then you can report the request '{{title}}'", + title: @info_request.title), + email_subject: _('Report an offensive or unsuitable request'), + comment_id: params[:comment_id] + ) end private diff --git a/app/controllers/request_controller.rb b/app/controllers/request_controller.rb index 2113113c0d..51832cfeed 100644 --- a/app/controllers/request_controller.rb +++ b/app/controllers/request_controller.rb @@ -7,6 +7,8 @@ require 'zip' class RequestController < ApplicationController + skip_before_action :html_response, only: [:show, :select_authorities] + 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 ] @@ -27,12 +29,13 @@ class RequestController < ApplicationController 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 - if AlaveteliConfiguration::force_registration_on_new_request && !authenticated?( - :web => _("To send and publish your FOI request"), - :email => _("Then you'll be allowed to send FOI requests."), - :email_subject => _("Confirm your email address") + if AlaveteliConfiguration.force_registration_on_new_request && + !authenticated? + ask_to_login( + web: _('To send and publish your FOI request'), + email: _("Then you'll be allowed to send FOI requests."), + email_subject: _('Confirm your email address') ) - # do nothing - as "authenticated?" has done the redirect to signin page for us return end if !params[:query].nil? @@ -95,17 +98,20 @@ def show assign_variables_for_show_template(@info_request) # Only owners (and people who own everything) can update status - if @update_status - return if !@is_owning_user && !authenticated_as_user?( - @info_request.user, - :web => _("To update the status of this FOI request"), - :email => _("Then you can update the status of your request to " \ - "{{authority_name}}.", - :authority_name => @info_request.public_body.name), - :email_subject => _("Update the status of your request to " \ - "{{authority_name}}", - :authority_name => @info_request.public_body.name) + if @update_status && !@is_owning_user && !authenticated?( + as: @info_request.user + ) + ask_to_login( + as: @info_request.user, + web: _('To update the status of this FOI request'), + email: _('Then you can update the status of your request to ' \ + '{{authority_name}}.', + authority_name: @info_request.public_body.name), + email_subject: _('Update the status of your request to ' \ + '{{authority_name}}', + authority_name: @info_request.public_body.name) ) + return end # What state transitions can the request go into @@ -166,12 +172,6 @@ def similar end def list - # respond with a 404 without a database lookup if request was not for html - if request.format && !request.format.html? - respond_to { |format| format.any { head :not_found } } - return - end - medium_cache @view = params[:view] @page = get_search_page_from_params if !@page # used in cache case, as perform_search sets @page as side effect @@ -236,9 +236,9 @@ def new_batch params[:outgoing_message][:body], params[:public_body_ids]) - @info_request = InfoRequest.create_from_attributes(info_request_params(@batch), - outgoing_message_params, - authenticated_user) + @info_request = InfoRequest.build_from_attributes(info_request_params(@batch), + outgoing_message_params, + authenticated_user) @outgoing_message = @info_request.outgoing_messages.first @info_request.is_batch_request_template = true if !@existing_batch.nil? || !@info_request.valid? @@ -286,7 +286,7 @@ def new # Banned from making new requests? user_exceeded_limit = false - if !authenticated_user.nil? && !authenticated_user.can_file_requests? + if authenticated? && !authenticated_user.can_file_requests? # If the reason the user cannot make new requests is that they are # rate-limited, it’s possible they composed a request before they # logged in and we want to include the text of the request so they @@ -326,8 +326,8 @@ def new @existing_request = InfoRequest.find_existing(params[:info_request][:title], params[:info_request][:public_body_id], params[:outgoing_message][:body]) # Create both FOI request and the first request message - @info_request = InfoRequest.create_from_attributes(info_request_params, - outgoing_message_params) + @info_request = InfoRequest.build_from_attributes(info_request_params, + outgoing_message_params) @outgoing_message = @info_request.outgoing_messages.first # Maybe we lost the address while they're writing it @@ -357,12 +357,15 @@ def new return end - if !authenticated?( - :web => _("To send and publish your FOI request").to_str, - :email => _("Then your FOI request to {{public_body_name}} will be sent and published.",:public_body_name=>@info_request.public_body.name), - :email_subject => _("Confirm your FOI request to {{public_body_name}}",:public_body_name=>@info_request.public_body.name) + unless authenticated? + ask_to_login( + web: _('To send and publish your FOI request').to_str, + email: _('Then your FOI request to {{public_body_name}} will be sent ' \ + 'and published.', + public_body_name: @info_request.public_body.name), + email_subject: _('Confirm your FOI request to {{public_body_name}}', + public_body_name: @info_request.public_body.name) ) - # do nothing - as "authenticated?" has done the redirect to signin page for us return end @@ -452,16 +455,17 @@ def upload_response @info_request = InfoRequest.not_embargoed.find_by_url_title!(params[:url_title]) @reason_params = { - :web => _("To upload a response, you must be logged in using an " \ - "email address from {{authority_name}}", - :authority_name => CGI.escapeHTML(@info_request.public_body.name)), - :email => _("Then you can upload an FOI response. "), - :email_subject => _("Confirm your account on {{site_name}}", - :site_name => site_name) + web: _('To upload a response, you must be logged in using an ' \ + 'email address from {{authority_name}}', + authority_name: CGI.escapeHTML(@info_request.public_body.name)), + email: _('Then you can upload an FOI response. '), + email_subject: _('Confirm your account on {{site_name}}', + site_name: site_name) } - if !authenticated?(@reason_params) - return + unless authenticated? + ask_to_login(**@reason_params) + return false end if !@info_request.public_body.is_foi_officer?(@user) @@ -532,13 +536,17 @@ def download_entire_request if @info_request.embargo && cannot?(:read, @info_request) render_hidden end - if authenticated?( - :web => _("To download the zip file"), - :email => _("Then you can download a zip file of {{info_request_title}}.", - :info_request_title=>@info_request.title), - :email_subject => _("Log in to download a zip file of {{info_request_title}}", - :info_request_title=>@info_request.title) + if !authenticated? + ask_to_login( + web: _('To download the zip file'), + email: _('Then you can download a zip file of ' \ + '{{info_request_title}}.', + info_request_title: @info_request.title), + email_subject: _('Log in to download a zip file of ' \ + '{{info_request_title}}', + info_request_title: @info_request.title) ) + else # Test for whole request being hidden or requester-only if cannot?(:read, @info_request) return render_hidden @@ -579,7 +587,7 @@ def assign_variables_for_show_template(info_request) @info_request = info_request @status = info_request.calculate_status @old_unclassified = - info_request.is_old_unclassified? && !authenticated_user.nil? + info_request.is_old_unclassified? && authenticated? @is_owning_user = info_request.is_owning_user?(authenticated_user) @last_info_request_event_id = info_request.last_event_id_needing_description @new_responses_count = @@ -618,9 +626,11 @@ def assign_variables_for_show_template(info_request) @old_unclassified && !@render_to_file ) + @show_action_menu = !@render_to_file + @similar_requests, @similar_more = @info_request.similar_requests - @citations = @info_request.citations.limit(3) + @citations = @info_request.citations.newest(3) end def assign_state_transition_variables @@ -647,7 +657,7 @@ def state_transitions_empty?(transitions) end def make_request_zip(info_request, file_path) - Zip::File.open(file_path, Zip::File::CREATE) do |zipfile| + Zip::File.open(file_path, create: true) do |zipfile| file_info = make_request_summary_file(info_request) zipfile.get_output_stream(file_info[:filename]) { |f| f.write(file_info[:data]) } message_index = 0 @@ -703,12 +713,13 @@ def check_batch_requests_and_user_allowed if !AlaveteliConfiguration::allow_batch_requests raise RouteNotFound.new("Page not enabled") end - if !authenticated?( - :web => _("To make a batch request"), - :email => _("Then you can make a batch request"), - :email_subject => _("Make a batch request"), - :user_name => "a user who has been authorised to make batch requests") - # do nothing - as "authenticated?" has done the redirect to signin page for us + unless authenticated? + ask_to_login( + web: _('To make a batch request'), + email: _('Then you can make a batch request'), + email_subject: _('Make a batch request'), + user_name: 'a user who has been authorised to make batch requests' + ) return end if !@user.can_make_batch_requests? diff --git a/app/controllers/request_game_controller.rb b/app/controllers/request_game_controller.rb index 9562c4199a..e309018c7c 100644 --- a/app/controllers/request_game_controller.rb +++ b/app/controllers/request_game_controller.rb @@ -45,12 +45,12 @@ def play def show url_title = params[:url_title] - if !authenticated?( - :web => _("To play the request categorisation game"), - :email => _("Then you can play the request categorisation game."), - :email_subject => _("Play the request categorisation game") + unless authenticated? + ask_to_login( + web: _('To play the request categorisation game'), + email: _('Then you can play the request categorisation game.'), + email_subject: _('Play the request categorisation game') ) - # do nothing - as "authenticated?" has done the redirect to signin page for us return end redirect_to show_request_url(:url_title => url_title) diff --git a/app/controllers/services_controller.rb b/app/controllers/services_controller.rb index ab1d18d7c3..40f45b8eda 100644 --- a/app/controllers/services_controller.rb +++ b/app/controllers/services_controller.rb @@ -2,6 +2,8 @@ class ServicesController < ApplicationController + skip_before_action :html_response + def other_country_message flash.keep diff --git a/app/controllers/statistics_controller.rb b/app/controllers/statistics_controller.rb index 07f1790416..d4ca107398 100644 --- a/app/controllers/statistics_controller.rb +++ b/app/controllers/statistics_controller.rb @@ -1,11 +1,13 @@ class StatisticsController < ApplicationController + skip_before_action :html_response + def index unless AlaveteliConfiguration::public_body_statistics_page raise ActiveRecord::RecordNotFound.new("Page not enabled") end @public_bodies = Statistics.public_bodies - @users = Statistics.users + @leaderboard = Statistics.leaderboard @request_hides_by_week = Statistics.by_week_to_today_with_noughts( InfoRequestEvent.count_of_hides_by_week, InfoRequest.is_public.order(:created_at).first&.created_at @@ -16,7 +18,7 @@ def index format.json do render json: { public_bodies: @public_bodies, - users: Statistics.user_json_for_api(@users), + users: Statistics.user_json_for_api(@leaderboard), requests: { hides_by_week: @request_hides_by_week } diff --git a/app/controllers/track_controller.rb b/app/controllers/track_controller.rb index 95f5264c12..9ea449ee9a 100644 --- a/app/controllers/track_controller.rb +++ b/app/controllers/track_controller.rb @@ -6,6 +6,8 @@ # Email: hello@mysociety.org; WWW: http://www.mysociety.org/ class TrackController < ApplicationController + skip_before_action :html_response + before_action :medium_cache # Track all updates to a particular request @@ -131,7 +133,8 @@ def track_set end end - if not authenticated?(@track_thing.params) + unless authenticated? + ask_to_login(**@track_thing.params) return false end @@ -147,7 +150,11 @@ def track_set return true else # this will most likely be tripped by a single error - probably track_query length - flash[:error] = @track_thing.errors.map { |_, msg| msg }.join(", ") + if rails_upgrade? + flash[:error] = @track_thing.errors.map { |e| e.message }.join(", ") + else + flash[:error] = @track_thing.errors.map { |_, msg| msg }.join(", ") + end return false end end @@ -196,19 +203,20 @@ def atom_feed_internal def update track_thing = TrackThing.find(params[:track_id].to_i) - if not authenticated_as_user?(track_thing.tracking_user, - :web => _("To cancel this alert"), - :email => _("Then you can cancel the alert."), - :email_subject => _("Cancel a {{site_name}} alert",:site_name=>site_name) - ) - # do nothing - as "authenticated?" has done the redirect to signin page for us + unless authenticated?(as: track_thing.tracking_user) + ask_to_login( + as: track_thing.tracking_user, + web: _('To cancel this alert'), + email: _('Then you can cancel the alert.'), + email_subject: _('Cancel a {{site_name}} alert', site_name: site_name) + ) return end new_medium = params[:track_medium] if new_medium == 'delete' track_thing.destroy - flash[:notice] = view_context.unsubscribe_notice(track_thing) + flash[:notice] = { inline: view_context.unsubscribe_notice(track_thing) } redirect_to SafeRedirect.new(params[:r]).path else msg = @@ -226,12 +234,14 @@ def update def delete_all_type user_id = User.find(params[:user].to_i) - if not authenticated_as_user?(user_id, - :web => _("To cancel these alerts"), - :email => _("Then you can cancel the alerts."), - :email_subject => _("Cancel some {{site_name}} alerts",:site_name=>site_name) - ) - # do nothing - as "authenticated?" has done the redirect to signin page for us + unless authenticated?(as: user_id) + ask_to_login( + as: user_id, + web: _('To cancel these alerts'), + email: _('Then you can cancel the alerts.'), + email_subject: _('Cancel some {{site_name}} alerts', + site_name: site_name) + ) return end diff --git a/app/controllers/user_controller.rb b/app/controllers/user_controller.rb index b7d225d989..22d7f2d1af 100644 --- a/app/controllers/user_controller.rb +++ b/app/controllers/user_controller.rb @@ -9,6 +9,10 @@ class UserController < ApplicationController include UserSpamCheck + skip_before_action :html_response, only: [ + :show, :wall, :get_draft_profile_photo, :get_profile_photo + ] + layout :select_layout before_action :normalize_url_name, :only => :show before_action :work_out_post_redirect, :only => [ :signup ] @@ -135,7 +139,11 @@ def signup if user_alreadyexists # attempt to remove the 'already in use message' from the errors hash # so it doesn't get accidentally shown to the end user - @user_signup.errors[:email].delete_if { |message| message == _("This email is already in use") } + if rails_upgrade? + @user_signup.errors.delete(:email, :taken) + else + @user_signup.errors[:email].delete_if { |message| message == _("This email is already in use") } + end end if error || !@user_signup.errors.empty? # Show the form @@ -179,11 +187,14 @@ def ip_rate_limiter # Change your email def signchangeemail # "authenticated?" has done the redirect to signin page for us - return unless authenticated?( - :web => _("To change your email address used on {{site_name}}",:site_name=>site_name), - :email => _("Then you can change your email address used on {{site_name}}",:site_name=>site_name), - :email_subject => _("Change your email address used on {{site_name}}",:site_name=>site_name) - ) + return unless authenticated? || ask_to_login( + web: _('To change your email address used on {{site_name}}', + site_name: site_name), + email: _('Then you can change your email address used on {{site_name}}', + site_name: site_name), + email_subject: _('Change your email address used on {{site_name}}', + site_name: site_name) + ) unless params[:submitted_signchangeemail_do] render :action => 'signchangeemail' @@ -221,12 +232,13 @@ def signchangeemail if (not session[:user_circumstance]) or (session[:user_circumstance] != "change_email") # don't store the password in the db params[:signchangeemail].delete(:password) - post_redirect = PostRedirect.new(:uri => signchangeemail_url, - :post_params => params, - :circumstance => "change_email" # special login that lets you change your email - ) - post_redirect.user = @user - post_redirect.save! + + post_redirect = PostRedirect.create!( + uri: signchangeemail_url, + post_params: params, + user: @user, + circumstance: 'change_email' + ) url = confirm_url(:email_token => post_redirect.email_token) UserMailer. @@ -245,6 +257,8 @@ def signchangeemail @user.email = @signchangeemail.new_email @user.save! + sign_in(@user) + # Now clear the circumstance session[:user_circumstance] = nil flash[:notice] = _("You have now changed your email address used on {{site_name}}",:site_name=>site_name) @@ -260,7 +274,7 @@ def river def set_profile_photo # check they are logged in (the upload photo option is anyway only available when logged in) - if authenticated_user.nil? + unless authenticated? flash[:error] = _("You need to be logged in to change your profile photo.") redirect_to frontpage_url return @@ -287,7 +301,7 @@ def set_profile_photo render :template => 'user/set_draft_profile_photo' return end - @draft_profile_photo.save + @draft_profile_photo.save! if params[:automatically_crop] # no javascript, crop automatically @@ -325,7 +339,7 @@ def set_profile_photo def clear_profile_photo # check they are logged in (the upload photo option is anyway only available when logged in) - if authenticated_user.nil? + unless authenticated? flash[:error] = _("You need to be logged in to clear your profile photo.") redirect_to frontpage_url return @@ -360,7 +374,7 @@ def get_profile_photo # Change about me text on your profile page def set_receive_email_alerts - if authenticated_user.nil? + unless authenticated? flash[:error] = _("You need to be logged in to edit your profile.") redirect_to frontpage_url return diff --git a/app/controllers/user_profile/about_me_controller.rb b/app/controllers/user_profile/about_me_controller.rb index e9e5a52aec..d132e656aa 100644 --- a/app/controllers/user_profile/about_me_controller.rb +++ b/app/controllers/user_profile/about_me_controller.rb @@ -43,11 +43,11 @@ def update private def check_user_logged_in - if authenticated_user.nil? - flash[:error] = _("You need to be logged in to change the text about you on your profile.") - redirect_to frontpage_url - return - end + return if authenticated? + + flash[:error] = _('You need to be logged in to change the text about you ' \ + 'on your profile.') + redirect_to frontpage_url end def set_title diff --git a/app/controllers/users/confirmations_controller.rb b/app/controllers/users/confirmations_controller.rb index 6d5771b96d..850080334a 100644 --- a/app/controllers/users/confirmations_controller.rb +++ b/app/controllers/users/confirmations_controller.rb @@ -3,7 +3,7 @@ class Users::ConfirmationsController < UserController def confirm post_redirect = PostRedirect.find_by_email_token(params[:email_token]) - if post_redirect.nil? + if post_redirect.nil? || !post_redirect.email_token_valid? render :template => 'user/bad_token' return end @@ -30,7 +30,7 @@ def confirm @user = confirm_user!(post_redirect.user) end - session[:user_id] = @user.id + sign_in(@user) end session[:user_circumstance] = post_redirect.circumstance diff --git a/app/controllers/users/messages_controller.rb b/app/controllers/users/messages_controller.rb index d86da4ee09..2990766c17 100644 --- a/app/controllers/users/messages_controller.rb +++ b/app/controllers/users/messages_controller.rb @@ -30,11 +30,10 @@ def set_recipient def check_can_send_messages # Banned from messaging users? - if authenticated_user && !authenticated_user.can_contact_other_users? - @details = authenticated_user.can_fail_html - render template: 'user/banned' - return - end + return unless authenticated? && !authenticated_user.can_contact_other_users? + + @details = authenticated_user.can_fail_html + render template: 'user/banned' end def check_logged_in @@ -43,7 +42,7 @@ def check_logged_in # between the two users) # # "authenticated?" has done the redirect to signin page for us - return unless authenticated?( + return unless authenticated? || ask_to_login( web: _('To send a message to {{user_name}}', user_name: CGI.escapeHTML(@recipient_user.name)), email: _('Then you can send a message to {{user_name}}.', diff --git a/app/controllers/users/sessions_controller.rb b/app/controllers/users/sessions_controller.rb index 91a4bbca04..de26072db2 100644 --- a/app/controllers/users/sessions_controller.rb +++ b/app/controllers/users/sessions_controller.rb @@ -44,10 +44,7 @@ def create end && return end - session[:user_id] = @user_signin.id - session[:ttl] = nil - session[:user_circumstance] = nil - session[:remember_me] = params[:remember_me] ? true : false + sign_in(@user_signin, remember_me: params[:remember_me].present?) if is_modal_dialog render :template => 'users/sessions/show' diff --git a/app/helpers/admin/translated_record_form.rb b/app/helpers/admin/translated_record_form.rb index 0c4e21321d..6c45e1be13 100644 --- a/app/helpers/admin/translated_record_form.rb +++ b/app/helpers/admin/translated_record_form.rb @@ -73,8 +73,14 @@ def locale_fields(t, locale) end def default_locale_error_messages - default_locale_errors = - object.errors.reject { |attr, _| attr.to_s.starts_with?('translation') } + if rails_upgrade? + default_locale_errors = object.errors.reject do |error| + error.attribute.starts_with?('translation') + end + else + default_locale_errors = + object.errors.reject { |attr, _| attr.to_s.starts_with?('translation') } + end @template.concat(errors_for(default_locale, default_locale_errors)) end @@ -92,12 +98,22 @@ def errors_for(locale, errors) @template.concat(locale_name(locale)) ul = @template.tag.ul do - errors.each do |attr, message| - content = @template.tag.li do - "#{ attr } #{ message }".html_safe + if rails_upgrade? + errors.each do |error| + content = @template.tag.li do + "#{ error.attribute } #{ error.message }".html_safe + end + + @template.concat(content) end + else + errors.each do |attr, message| + content = @template.tag.li do + "#{ attr } #{ message }".html_safe + end - @template.concat(content) + @template.concat(content) + end end end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index f3819bfdc9..87b78f91b9 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -51,8 +51,14 @@ def foi_error_messages_for(*params) error_messages = "".html_safe objects.each do |object| - object.errors.each do |attr, message| - error_messages << content_tag(:li, h(message)) + if rails_upgrade? + object.errors.each do |error| + error_messages << content_tag(:li, h(error.message)) + end + else + object.errors.each do |attr, message| + error_messages << content_tag(:li, h(message)) + end end end @@ -66,17 +72,21 @@ def locale_name(locale) def admin_value(v) if v.nil? nil - elsif v.instance_of?(Time) + elsif v.is_a?(Time) admin_date(v) else h(v) end end - def admin_date(date) + def admin_date(date, ago: true, ago_only: false) ago_text = _('{{length_of_time}} ago', :length_of_time => time_ago_in_words(date)) + text = ago_text if ago_only + exact_date = I18n.l(date, :format => "%e %B %Y %H:%M:%S") - return "#{exact_date} (#{ago_text})" + text ||= "#{exact_date} (#{ago_text})" if ago + + time_tag(date, text || exact_date, title: date) end def read_asset_file(asset_name) @@ -215,7 +225,7 @@ def show_pro_upsell?(user) def pro_upsell_text pro_link = link_to(account_request_index_path) do - AlaveteliConfiguration.pro_site_name + pro_site_name end pro_site_name_link = content_tag(:strong, pro_link) diff --git a/app/helpers/info_request_helper.rb b/app/helpers/info_request_helper.rb index e9b07ebfee..fdbb98df11 100644 --- a/app/helpers/info_request_helper.rb +++ b/app/helpers/info_request_helper.rb @@ -114,9 +114,9 @@ def status_text_waiting_response_very_overdue(info_request, opts = {}) str += ' ' str += _('You can complain by') str += ' ' - str += link_to _("requesting an internal review"), + str += link_to _('requesting an internal review'), new_request_followup_path(:request_id => info_request.id) + - "?internal_review=1#followup" + '?internal_review=1' str += '.' end @@ -155,7 +155,7 @@ def status_text_waiting_clarification(info_request, opts = {}) str += _('Please') str += ' ' str += link_to _("send a follow up message"), - respond_to_last_path(info_request, :anchor => 'followup') + respond_to_last_path(info_request) str += '.' else str += _('The request is waiting for clarification.') @@ -283,8 +283,13 @@ def attachment_path(attachment, options = {}) def attachment_url(attachment, options = {}) attach_params = attachment_params(attachment, options) - if options[:html] + + if options[:html] && public_token? + share_attachment_as_html_url(attach_params) + elsif options[:html] get_attachment_as_html_url(attach_params) + elsif public_token? + share_attachment_url(attach_params) else get_attachment_url(attach_params) end @@ -300,12 +305,16 @@ def details_help_link(public_body) def attachment_params(attachment, options = {}) attach_params = { - 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 public_token? + attach_params[:public_token] = public_token + else + attach_params[:id] = attachment.incoming_message.info_request_id + end if options[:html] attach_params[:file_name] = "#{attachment.display_filename}.html" else @@ -320,4 +329,7 @@ def attachment_params(attachment, options = {}) attach_params end + def public_token? + defined?(public_token) && public_token.present? + end end diff --git a/app/helpers/link_to_helper.rb b/app/helpers/link_to_helper.rb index 157c45f9d6..b7185d8172 100755 --- a/app/helpers/link_to_helper.rb +++ b/app/helpers/link_to_helper.rb @@ -295,8 +295,10 @@ def current_path_as_json # Private: Generate a request_url linking to the new correspondence def message_url(message, options = {}) message_type = message.class.to_s.gsub('Message', '').downcase + anchor = "#{ message_type }-#{ message.id }" - default_options = { :anchor => "#{ message_type }-#{ message.id }" } + return "##{anchor}" if options[:anchor_only] + default_options = { anchor: anchor } if options.delete(:cachebust) default_options.merge!(:nocache => "#{ message_type }-#{ message.id }") diff --git a/app/helpers/unsubscribe_helper.rb b/app/helpers/unsubscribe_helper.rb new file mode 100644 index 0000000000..5a4f83e22b --- /dev/null +++ b/app/helpers/unsubscribe_helper.rb @@ -0,0 +1,17 @@ +## +# Adds unsubscribe helper methods to mailers +# +# Requires @user to be defined within the mailer action +# +module UnsubscribeHelper + def unsubscribe_url + signin_url( + r: user_url(@user, anchor: 'email_subscriptions', only_path: true) + ) + end + + def disable_email_alerts_url + token = CGI.escape(User::EmailAlerts.token(@user)) + users_disable_email_alerts_url(token: token) + end +end diff --git a/app/mailers/alaveteli_pro/account_mailer.rb b/app/mailers/alaveteli_pro/account_mailer.rb index 609a317441..af4ea2e789 100644 --- a/app/mailers/alaveteli_pro/account_mailer.rb +++ b/app/mailers/alaveteli_pro/account_mailer.rb @@ -13,7 +13,7 @@ def account_request(account_request) mail(:from => blackhole_email, :to => pro_contact_from_name_and_email, :subject => _("{{pro_site_name}} account request", - pro_site_name: AlaveteliConfiguration.pro_site_name) + pro_site_name: pro_site_name) ) end end diff --git a/app/mailers/alaveteli_pro/embargo_mailer.rb b/app/mailers/alaveteli_pro/embargo_mailer.rb index 68f7974565..8d8bc2b53e 100644 --- a/app/mailers/alaveteli_pro/embargo_mailer.rb +++ b/app/mailers/alaveteli_pro/embargo_mailer.rb @@ -70,8 +70,9 @@ def expiring_alert(user, info_requests) "{{count}} request will be made public on {{site_name}} this week", "{{count}} requests will be made public on {{site_name}} this week", info_requests.count, - :site_name => AlaveteliConfiguration.site_name.html_safe, - :count => info_requests.count) + site_name: site_name.html_safe, + count: info_requests.count + ) auto_generated_headers mail_user(@user, subject) end @@ -83,8 +84,9 @@ def expired_alert(user, info_requests) "{{count}} request has been made public on {{site_name}}", "{{count}} requests have been made public on {{site_name}}", info_requests.count, - :site_name => AlaveteliConfiguration.site_name.html_safe, - :count => info_requests.count) + site_name: site_name.html_safe, + count: info_requests.count + ) auto_generated_headers mail_user(@user, subject) end diff --git a/app/mailers/alaveteli_pro/metrics_mailer.rb b/app/mailers/alaveteli_pro/metrics_mailer.rb index 594f254dbb..18fc666ec2 100644 --- a/app/mailers/alaveteli_pro/metrics_mailer.rb +++ b/app/mailers/alaveteli_pro/metrics_mailer.rb @@ -17,7 +17,7 @@ def weekly_report(report) set_auto_generated_headers subject = _("{{pro_site_name}} Weekly Metrics", - pro_site_name: AlaveteliConfiguration.pro_site_name.html_safe) + pro_site_name: pro_site_name.html_safe) mail(from: pro_contact_from_name_and_email, to: pro_contact_from_name_and_email, diff --git a/app/mailers/alaveteli_pro/subscription_mailer.rb b/app/mailers/alaveteli_pro/subscription_mailer.rb index 7b57aab65f..b77269a6db 100644 --- a/app/mailers/alaveteli_pro/subscription_mailer.rb +++ b/app/mailers/alaveteli_pro/subscription_mailer.rb @@ -4,10 +4,10 @@ def payment_failed(user) auto_generated_headers(user) subject = _('Action Required: Payment failed on {{pro_site_name}}', - pro_site_name: AlaveteliConfiguration.pro_site_name) + pro_site_name: pro_site_name) @user_name = user.name - @pro_site_name = AlaveteliConfiguration.pro_site_name.html_safe + @pro_site_name = pro_site_name.html_safe @subscriptions_url = subscriptions_url mail_user(user, subject) end diff --git a/app/mailers/alaveteli_pro/webhook_mailer.rb b/app/mailers/alaveteli_pro/webhook_mailer.rb index 8906c8ef26..60bf437a5d 100644 --- a/app/mailers/alaveteli_pro/webhook_mailer.rb +++ b/app/mailers/alaveteli_pro/webhook_mailer.rb @@ -20,7 +20,7 @@ def digest(webhooks) subject = _( "{{pro_site_name}} webhook daily digest", - pro_site_name: AlaveteliConfiguration.pro_site_name.html_safe + pro_site_name: pro_site_name.html_safe ) mail_pro_team(subject) end diff --git a/app/mailers/application_mailer.rb b/app/mailers/application_mailer.rb index 34060ea19e..e5e80adeb2 100644 --- a/app/mailers/application_mailer.rb +++ b/app/mailers/application_mailer.rb @@ -12,6 +12,14 @@ class ApplicationMailer < ActionMailer::Base include MailerHelper include AlaveteliFeatures::Helpers + # URL generating functions are needed by all controllers (for redirects), + # views (for links) and mailers (for use in emails), so include them into + # all of all. + include LinkToHelper + + # Site-wide access to configuration settings + include ConfigHelper + # This really should be the default - otherwise you lose any information # about the errors, and have to do error checking on return codes. self.raise_delivery_errors = true @@ -72,13 +80,4 @@ def set_reply_to_headers(user = nil, opts = {}) def set_footer_template @footer_template = nil end - - # URL generating functions are needed by all controllers (for redirects), - # views (for links) and mailers (for use in emails), so include them into - # all of all. - include LinkToHelper - - # Site-wide access to configuration settings - include ConfigHelper - end diff --git a/app/mailers/notification_mailer.rb b/app/mailers/notification_mailer.rb index 5ba533ae5f..be16e13bc7 100644 --- a/app/mailers/notification_mailer.rb +++ b/app/mailers/notification_mailer.rb @@ -88,7 +88,7 @@ def daily_summary(user, notifications) mail_user( user, _("Your daily request summary from {{pro_site_name}}", - pro_site_name: AlaveteliConfiguration.pro_site_name) + pro_site_name: pro_site_name) ) end @@ -121,8 +121,9 @@ def embargo_expiring_notification(notification) subject = _( "Your FOI request - {{request_title}} will be made public on " \ "{{site_name}} this week", - :request_title => @info_request.title.html_safe, - :site_name => AlaveteliConfiguration.site_name.html_safe) + request_title: @info_request.title.html_safe, + site_name: site_name.html_safe + ) mail_user(@info_request.user, subject, @@ -138,8 +139,9 @@ def embargo_expired_notification(notification) subject = _( "Your FOI request - {{request_title}} has been made public on " \ "{{site_name}}", - :request_title => @info_request.title.html_safe, - :site_name => AlaveteliConfiguration.site_name.html_safe) + request_title: @info_request.title.html_safe, + site_name: site_name.html_safe + ) mail_user(@info_request.user, subject, @@ -148,8 +150,7 @@ def embargo_expired_notification(notification) def overdue_notification(notification) @info_request = notification.info_request_event.info_request - @url = - signin_url(r: respond_to_last_path(@info_request, anchor: 'followup')) + @url = signin_url(r: respond_to_last_path(@info_request)) set_reply_to_headers(@info_request.user) set_auto_generated_headers @@ -164,8 +165,7 @@ def overdue_notification(notification) def very_overdue_notification(notification) @info_request = notification.info_request_event.info_request - @url = - signin_url(r: respond_to_last_path(@info_request, anchor: 'followup')) + @url = signin_url(r: respond_to_last_path(@info_request)) set_reply_to_headers(@info_request.user) set_auto_generated_headers diff --git a/app/mailers/request_mailer.rb b/app/mailers/request_mailer.rb index c64cc60593..73d52d3540 100644 --- a/app/mailers/request_mailer.rb +++ b/app/mailers/request_mailer.rb @@ -101,7 +101,7 @@ def new_response(info_request, incoming_message) # Tell the requester that the public body is late in replying def overdue_alert(info_request, user) - @url = respond_to_last_url(info_request, anchor: 'followup') + @url = respond_to_last_url(info_request) @info_request = info_request set_reply_to_headers(user) @@ -116,7 +116,7 @@ def overdue_alert(info_request, user) # Tell the requester that the public body is very late in replying def very_overdue_alert(info_request, user) - @url = respond_to_last_url(info_request, anchor: 'followup') + @url = respond_to_last_url(info_request) @info_request = info_request set_reply_to_headers(user) diff --git a/app/mailers/survey_mailer.rb b/app/mailers/survey_mailer.rb index 963214c388..5f4ba23541 100644 --- a/app/mailers/survey_mailer.rb +++ b/app/mailers/survey_mailer.rb @@ -4,6 +4,7 @@ # class SurveyMailer < ApplicationMailer include AlaveteliFeatures::Helpers + helper UnsubscribeHelper before_action :set_footer_template @@ -11,6 +12,7 @@ def survey_alert(info_request) return unless Survey.enabled? @info_request = info_request + @user = info_request.user @url = Survey.new(info_request.public_body).url headers( @@ -21,7 +23,7 @@ def survey_alert(info_request) ) mail( - to: info_request.user.name_and_email, + to: @user.name_and_email, from: contact_from_name_and_email, subject: _('A survey about your recent Freedom of Information request') ) @@ -53,6 +55,6 @@ def self.alert_survey private def set_footer_template - @footer_template = 'default' + @footer_template = 'default_with_unsubscribe' end end diff --git a/app/mailers/track_mailer.rb b/app/mailers/track_mailer.rb index e8190b7726..af29c144b2 100644 --- a/app/mailers/track_mailer.rb +++ b/app/mailers/track_mailer.rb @@ -5,6 +5,7 @@ # Email: hello@mysociety.org; WWW: http://www.mysociety.org/ class TrackMailer < ApplicationMailer + helper UnsubscribeHelper before_action :set_footer_template, :only => :event_digest @@ -17,14 +18,6 @@ def contact_from_name_and_email def event_digest(user, email_about_things) @user, @email_about_things = user, email_about_things - @unsubscribe_url = - signin_url(r: user_url(user, - anchor: 'email_subscriptions', - only_path: true)) - - token = CGI.escape(User::EmailAlerts.token(user)) - @disable_email_alerts_url = users_disable_email_alerts_url(token: token) - headers( # http://tools.ietf.org/html/rfc3834 'Auto-Submitted' => 'auto-generated', @@ -55,7 +48,7 @@ def self.alert_tracks now = Time.zone.now one_week_ago = now - 7.days User.where(["last_daily_track_email < ?", now - 1.day ]).find_each do |user| - next if !user.should_be_emailed? || !user.receive_email_alerts + next unless user.should_be_emailed? email_about_things = [] track_things = TrackThing.where(:tracking_user_id => user.id, @@ -149,7 +142,7 @@ def self.alert_tracks_loop private def set_footer_template - @footer_template = 'default' + @footer_template = 'default_with_unsubscribe' end end diff --git a/app/models/ability.rb b/app/models/ability.rb index 143d871133..e322334f86 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -2,9 +2,9 @@ class Ability include CanCan::Ability include AlaveteliFeatures::Helpers - attr_reader :user, :project + attr_reader :user, :project, :public_token - def initialize(user, project: nil) + def initialize(user, project: nil, public_token: false) # Define abilities for the passed in user here. For example: # # user ||= User.new # guest user (not logged in) @@ -34,6 +34,7 @@ def initialize(user, project: nil) @user = user @project = project + @public_token = public_token # Updating request status can :update_request_state, InfoRequest do |request| @@ -57,7 +58,7 @@ def initialize(user, project: nil) # Viewing batch requests can :read, InfoRequestBatch do |batch_request| if batch_request.embargo_duration - user && (user == batch_request.user || User.view_embargoed?(user)) + user && (user == batch_request.user || user&.view_embargoed?) else true end @@ -128,6 +129,11 @@ def initialize(user, project: nil) user && (user.is_admin? || user.is_pro? || info_request.user == user) end + can :share, InfoRequest do |info_request| + info_request.embargo && + (user&.is_pro_admin? || info_request.is_actual_owning_user?(user)) + end + can :admin, Comment do |comment| if comment.info_request.embargo user && user.is_pro_admin? @@ -187,20 +193,22 @@ def can_view_with_prominence?(prominence, info_request) if info_request.embargo case prominence when 'hidden' - User.view_hidden_and_embargoed?(user) + user&.view_hidden_and_embargoed? when 'requester_only' - info_request.is_actual_owning_user?(user) || User.view_hidden_and_embargoed?(user) + info_request.is_actual_owning_user?(user) || + user&.view_hidden_and_embargoed? else info_request.is_actual_owning_user?(user) || - User.view_embargoed?(user) || - project&.member?(user) + user&.view_embargoed? || + project&.member?(user) || + public_token end else case prominence when 'hidden' - User.view_hidden?(user) + user&.view_hidden? when 'requester_only' - info_request.is_actual_owning_user?(user) || User.view_hidden?(user) + info_request.is_actual_owning_user?(user) || user&.view_hidden? else true end diff --git a/app/models/alaveteli_pro/draft_info_request_batch.rb b/app/models/alaveteli_pro/draft_info_request_batch.rb index 7c01121978..2943fc7819 100644 --- a/app/models/alaveteli_pro/draft_info_request_batch.rb +++ b/app/models/alaveteli_pro/draft_info_request_batch.rb @@ -29,6 +29,8 @@ class AlaveteliPro::DraftInfoRequestBatch < ApplicationRecord after_initialize :set_default_body + strip_attributes only: %i[embargo_duration] + def set_default_body if body.blank? template = OutgoingMessage::Template::BatchRequest.new diff --git a/app/models/alaveteli_pro/embargo.rb b/app/models/alaveteli_pro/embargo.rb index af22575055..1f1dd715b0 100644 --- a/app/models/alaveteli_pro/embargo.rb +++ b/app/models/alaveteli_pro/embargo.rb @@ -71,7 +71,7 @@ def extend(extension) self.extension = extension self.publish_at += duration_as_duration(extension.extension_duration) self.expiring_notification_at = calculate_expiring_notification_at - save + save! end def expiring_soon? diff --git a/app/models/application_record.rb b/app/models/application_record.rb index 10a4cba84d..d0e78acfd2 100644 --- a/app/models/application_record.rb +++ b/app/models/application_record.rb @@ -1,3 +1,5 @@ class ApplicationRecord < ActiveRecord::Base + include ConfigHelper + self.abstract_class = true end diff --git a/app/models/censor_rule.rb b/app/models/censor_rule.rb index ccaddba968..c653c88b54 100644 --- a/app/models/censor_rule.rb +++ b/app/models/censor_rule.rb @@ -92,11 +92,7 @@ def censorable_requests private def single_char_regexp - if String.method_defined?(:encode) - Regexp.new('.'.force_encoding('ASCII-8BIT')) - else - Regexp.new('.', nil, 'N') - end + Regexp.new('.'.force_encoding('ASCII-8BIT')) end def require_valid_regexp @@ -112,7 +108,7 @@ def to_replace(encoding) end def encoded_text(encoding) - String.method_defined?(:encode) ? text.dup.force_encoding(encoding) : text + text.dup.force_encoding(encoding) end def make_regexp(encoding) diff --git a/app/models/citation.rb b/app/models/citation.rb index e02df7819d..8758f522fb 100644 --- a/app/models/citation.rb +++ b/app/models/citation.rb @@ -32,6 +32,10 @@ class Citation < ApplicationRecord validates :type, inclusion: { in: %w(news_story academic_paper other), message: _('Please select a type') } + scope :newest, ->(limit = 1) do + order(created_at: :desc).limit(limit) + end + scope :for_request, ->(info_request) do where(citable: info_request). or(where(citable: info_request.info_request_batch)) diff --git a/app/models/concerns/message_prominence.rb b/app/models/concerns/message_prominence.rb index 6c71f655c9..5681f3ffa1 100644 --- a/app/models/concerns/message_prominence.rb +++ b/app/models/concerns/message_prominence.rb @@ -1,9 +1,8 @@ module MessageProminence - extend ActiveSupport::Concern included do - validates_inclusion_of :prominence, :in => self.prominence_states + validates_inclusion_of :prominence, in: self.prominence_states end def indexed_by_search? @@ -11,13 +10,12 @@ def indexed_by_search? end def is_public? - self.prominence == 'normal' + prominence == 'normal' end module ClassMethods def prominence_states - ['normal', 'hidden','requester_only'] + %w(normal requester_only hidden) end end - end diff --git a/app/models/foi_attachment.rb b/app/models/foi_attachment.rb index 30ed933897..3f62af9509 100644 --- a/app/models/foi_attachment.rb +++ b/app/models/foi_attachment.rb @@ -67,10 +67,7 @@ def body=(d) file.write d } update_display_size! - @cached_body = d - if String.method_defined?(:encode) - @cached_body = @cached_body.force_encoding("ASCII-8BIT") - end + @cached_body = d.force_encoding("ASCII-8BIT") end # raw body, encoded as binary diff --git a/app/models/holiday_import.rb b/app/models/holiday_import.rb index 261855bee2..3e2d739f66 100644 --- a/app/models/holiday_import.rb +++ b/app/models/holiday_import.rb @@ -45,6 +45,10 @@ def save holidays.all?(&:save) end + def save! + holidays.all?(&:save!) + end + def holidays_attributes=(incoming_data) incoming_data.each { |offset, incoming| self.holidays << Holiday.new(incoming) } end diff --git a/app/models/incoming_message.rb b/app/models/incoming_message.rb index 9a5656ad20..a2d79c606e 100644 --- a/app/models/incoming_message.rb +++ b/app/models/incoming_message.rb @@ -34,7 +34,6 @@ require 'rexml/document' require 'zip' -require 'iconv' unless String.method_defined?(:encode) class IncomingMessage < ApplicationRecord include AdminColumn @@ -97,16 +96,16 @@ def parse_raw_email!(force = nil) if (!force.nil? || self.last_parsed.nil?) ActiveRecord::Base.transaction do self.extract_attachments! - write_attribute(:sent_at, raw_email.date || self.created_at) - write_attribute(:subject, raw_email.subject) - write_attribute(:mail_from, raw_email.from_name) + self.sent_at = raw_email.date || created_at + self.subject = raw_email.subject + self.mail_from = raw_email.from_name if from_email self.mail_from_domain = PublicBody.extract_domain_from_email(from_email) else self.mail_from_domain = "" end - write_attribute(:valid_to_reply_to, raw_email.valid_to_reply_to?) + self.valid_to_reply_to = raw_email.valid_to_reply_to? self.last_parsed = Time.zone.now self.foi_attachments.reload self.save! @@ -465,7 +464,7 @@ def _convert_part_body_to_text(part) # Add an annotation if the text had to be scrubbed if part && part.body_as_text.scrubbed? text += _("\n\n[ {{site_name}} note: The above text was badly encoded, and has had strange characters removed. ]", - :site_name => AlaveteliConfiguration.site_name) + site_name: site_name) end # Fix DOS style linefeeds to Unix style ones (or other later regexps won't work) text = text.gsub(/\r\n/, "\n") diff --git a/app/models/info_request.rb b/app/models/info_request.rb index 63e0afc26f..ffdc7cc6b6 100644 --- a/app/models/info_request.rb +++ b/app/models/info_request.rb @@ -32,6 +32,7 @@ # use_notifications :boolean # last_event_time :datetime # incoming_messages_count :integer default("0") +# public_token :string # require 'digest/sha1' @@ -45,6 +46,7 @@ class InfoRequest < ApplicationRecord include Rails.application.routes.url_helpers include AlaveteliPro::RequestSummaries include AlaveteliFeatures::Helpers + include InfoRequest::PublicToken include InfoRequest::Sluggable include InfoRequest::TitleValidation @@ -180,12 +182,13 @@ class << self ] after_initialize :set_defaults + before_create :set_use_notifications + before_validation :compute_idhash + before_validation :set_law_used, on: :create after_save :update_counter_cache - after_destroy :update_counter_cache - after_update :reindex_some_request_events + after_update :reindex_request_events, if: :reindexable_attribute_changed? before_destroy :expire - before_validation :compute_idhash - before_create :set_use_notifications + after_destroy :update_counter_cache # Return info request corresponding to an incoming email address, or nil if # none found. Checks the hash to ensure the email came from the public body - @@ -341,7 +344,7 @@ def self._extract_id_hash_from_email(incoming_email) # TODO: this *should* also check outgoing message joined to is an initial # request (rather than follow up) def self.find_existing(title, public_body_id, body) - conditions = { title: title, public_body_id: public_body_id } + conditions = { title: title&.strip, public_body_id: public_body_id } InfoRequest. includes(:outgoing_messages). @@ -426,7 +429,7 @@ def self.magic_email_for_id(prefix_part, id) magic_email end - def self.create_from_attributes(info_request_atts, outgoing_message_atts, user=nil) + def self.build_from_attributes(info_request_atts, outgoing_message_atts, user=nil) info_request = new(info_request_atts) default_message_params = { :status => 'ready', @@ -770,16 +773,6 @@ def user_json_for_api rescue LoadError, NameError end - # If the URL name has changed, then all request: queries will break unless - # we update index for every event. Also reindex if prominence changes. - def reindex_some_request_events - return unless saved_change_to_attribute?(:url_title) || - saved_change_to_attribute?(:prominence) || - saved_change_to_attribute?(:user_id) - - reindex_request_events - end - def reindex_request_events info_request_events.find_each do |event| event.xapian_mark_needs_index @@ -824,7 +817,7 @@ def update_last_public_response_at else self.last_public_response_at = nil end - save + save! end # Remove spaces from ends (for when used in emails etc.) @@ -875,12 +868,6 @@ def legislation public_body&.legislation || Legislation.default end - def law_used_human(key = :full) - warn %q([DEPRECATION] InfoRequest#law_used_human will be replaced with - InfoRequest#legislation as of 0.40).squish - legislation.to_s(key) - end - def find_existing_outgoing_message(body) outgoing_messages.with_body(body).first end @@ -1423,7 +1410,7 @@ def zip_cache_file_suffix(user) "" # If the user can view hidden things, they can view anything, so no need # to go any further - elsif User.view_hidden?(user) + elsif user&.view_hidden? "_hidden" # If the user can't view hidden things, but owns the request, they can # see more than the public, so they get requester_only @@ -1511,12 +1498,11 @@ def apply_masks(text, content_type) # Masks we apply to text associated with this request convert email addresses # we know about into textual descriptions of them def masks - masks = [{ :to_replace => incoming_email, - :replacement => _('[FOI #{{request}} email]', - :request => id.to_s) }, - { :to_replace => AlaveteliConfiguration::contact_email, - :replacement => _("[{{site_name}} contact email]", - :site_name => AlaveteliConfiguration::site_name)} ] + masks = [{ to_replace: incoming_email, + replacement: _('[FOI #{{request}} email]', request: id.to_s) }, + { to_replace: AlaveteliConfiguration.contact_email, + replacement: _("[{{site_name}} contact email]", + site_name: site_name) }] if public_body.is_followupable? masks << { :to_replace => public_body.request_email, :replacement => _("[{{public_body}} request email]", @@ -1871,8 +1857,11 @@ def set_defaults # this should only happen on Model.exists? call. It can be safely ignored. # See http://www.tatvartha.com/2011/03/activerecordmissingattributeerror-missing-attribute-a-bug-or-a-features/ end + end - self.law_used ||= legislation.key if new_record? + def set_law_used + return if law_used_changed? + self.law_used = public_body.legislation.key if public_body end def set_use_notifications @@ -1888,4 +1877,12 @@ def must_be_valid_state errors.add(:described_state, "is not a valid state") end end + + # If the URL name has changed, then all request: queries will break unless + # we update index for every event. Also reindex if prominence changes. + def reindexable_attribute_changed? + %i[url_title prominence user_id].any? do |attr| + saved_change_to_attribute?(attr) + end + end end diff --git a/app/models/info_request/public_token.rb b/app/models/info_request/public_token.rb new file mode 100644 index 0000000000..d730263732 --- /dev/null +++ b/app/models/info_request/public_token.rb @@ -0,0 +1,17 @@ +## +# Methods for enabling and disabling InfoRequest public tokens +# +module InfoRequest::PublicToken + extend ActiveSupport::Concern + + def enable_public_token! + token = Digest::UUID.uuid_v4 + update(public_token: token) + log_event('public_token', token: token, shared: true) + end + + def disable_public_token! + update(public_token: nil) + log_event('public_token', token: nil, shared: false) + end +end diff --git a/app/models/info_request/sluggable.rb b/app/models/info_request/sluggable.rb index d54d82872c..1a6fcf387b 100644 --- a/app/models/info_request/sluggable.rb +++ b/app/models/info_request/sluggable.rb @@ -39,7 +39,7 @@ def update_url_title suffix = suffix_number(url_title) unique_url_title = suffix ? "#{url_title}_#{suffix}" : url_title - write_attribute(:url_title, unique_url_title) + self.url_title = unique_url_title end def suffix_number(url_title) diff --git a/app/models/info_request_batch.rb b/app/models/info_request_batch.rb index 14fe914568..022986b321 100644 --- a/app/models/info_request_batch.rb +++ b/app/models/info_request_batch.rb @@ -39,6 +39,8 @@ class InfoRequestBatch < ApplicationRecord validates_presence_of :user validates_presence_of :body + strip_attributes only: %i[embargo_duration] + def self.send_batches where(sent_at: nil).find_each do |info_request_batch| AlaveteliLocalization.with_locale(info_request_batch.user.locale) do @@ -100,9 +102,9 @@ def create_batch! # Create a FOI request for a public body def create_request!(public_body) filled_body = OutgoingMessage.fill_in_salutation(body, public_body) - info_request = InfoRequest.create_from_attributes({ title: title }, - { body: filled_body }, - user) + info_request = InfoRequest.build_from_attributes({ title: title }, + { body: filled_body }, + user) info_request.public_body = public_body info_request.info_request_batch = self @@ -141,7 +143,7 @@ def sent? def example_request public_body = self.public_bodies.first body = OutgoingMessage.fill_in_salutation(self.body, public_body) - info_request = InfoRequest.create_from_attributes( + info_request = InfoRequest.build_from_attributes( { :title => self.title, :public_body => public_body }, { :body => body }, self.user diff --git a/app/models/info_request_event.rb b/app/models/info_request_event.rb index 5d4a1bfbb5..7b94d96219 100644 --- a/app/models/info_request_event.rb +++ b/app/models/info_request_event.rb @@ -55,7 +55,8 @@ class InfoRequestEvent < ApplicationRecord 'expire_embargo', # an embargo on the request expires 'set_embargo', # an embargo is added or extended 'send_error', # an error during sending - 'refusal_advice' # the results of completing the refusal advice wizard + 'refusal_advice', # the results of completing the refusal advice wizard + 'public_token' # has the shareable public token been generated or not ].freeze belongs_to :info_request, diff --git a/app/models/mail_server_log.rb b/app/models/mail_server_log.rb index f45a976a06..580b7b6915 100644 --- a/app/models/mail_server_log.rb +++ b/app/models/mail_server_log.rb @@ -251,7 +251,7 @@ def delivery_status # attempting to rescue # https://apidock.com/rails/v4.2.7/ActiveRecord/AttributeMethods/Dirty/write_attribute set_delivery_status(true) - save + save! DeliveryStatusSerializer.load(read_attribute(:delivery_status)) end end @@ -265,7 +265,7 @@ def set_delivery_status(force = false) if force force_delivery_status(decorated.delivery_status) else - write_attribute(:delivery_status, decorated.delivery_status) + self.delivery_status = decorated.delivery_status end end end @@ -273,10 +273,10 @@ def set_delivery_status(force = false) 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 - write_attribute_without_type_cast(:delivery_status, new_status) + update_columns(delivery_status: new_status) - # record the new value in `changes` so that save will have something - # to do as write_attribute_without_type_cast just updates the value + # record the new value in `changes` so that save will have something to do + # as update_columns just updates the value delivery_status_will_change! end diff --git a/app/models/outgoing_message.rb b/app/models/outgoing_message.rb index f27f6e9bd6..8c007d7e8b 100644 --- a/app/models/outgoing_message.rb +++ b/app/models/outgoing_message.rb @@ -69,10 +69,8 @@ class OutgoingMessage < ApplicationRecord self.default_url_options[:host] = AlaveteliConfiguration.domain - # https links in emails if forcing SSL - if AlaveteliConfiguration::force_ssl - self.default_url_options[:protocol] = "https" - end + scope :followup, -> { where(message_type: 'followup') } + scope :is_searchable, -> { where(prominence: 'normal') } def self.expected_send_errors [ EOFError, diff --git a/app/models/outgoing_message/snippet.rb b/app/models/outgoing_message/snippet.rb index 987894c15a..82a26492bf 100644 --- a/app/models/outgoing_message/snippet.rb +++ b/app/models/outgoing_message/snippet.rb @@ -1,12 +1,12 @@ # == Schema Information -# Schema version: 20210114161442 +# Schema version: 20210928115500 # # Table name: outgoing_message_snippets # # id :bigint not null, primary key # created_at :datetime not null # updated_at :datetime not null -# outgoing_message_snippet_id :integer not null +# outgoing_message_snippet_id :bigint not null # name :string # body :text # diff --git a/app/models/post_redirect.rb b/app/models/post_redirect.rb index c99b1229a2..ac7e8fe863 100644 --- a/app/models/post_redirect.rb +++ b/app/models/post_redirect.rb @@ -26,6 +26,7 @@ # Copyright (c) 2007 UK Citizens Online Democracy. All rights reserved. # Email: hello@mysociety.org; WWW: http://www.mysociety.org/ +require 'digest' require 'openssl' # for random bytes function class PostRedirect < ApplicationRecord @@ -40,6 +41,17 @@ class PostRedirect < ApplicationRecord after_initialize :generate_token after_initialize :generate_email_token + def self.verifier + Rails.application.message_verifier(to_s) + end + + def self.generate_verifiable_token(user:, circumstance:) + verifier.generate( + { user_id: user.id, login_token: user.login_token }, + purpose: circumstance + ) + end + # Makes a random token, suitable for using in URLs e.g confirmation # messages. def self.generate_random_token @@ -80,6 +92,13 @@ def local_part_uri $1 end + def email_token_valid? + return true unless PostRedirect.verifier.valid_message?(email_token) + + data = PostRedirect.verifier.verify(email_token, purpose: circumstance) + user.id == data[:user_id] && user.login_token == data[:login_token] + end + private # The token is used to return you to what you are doing after the login @@ -91,6 +110,12 @@ def generate_token # There is a separate token to use in the URL if we send a confirmation # email. def generate_email_token - self.email_token = PostRedirect.generate_random_token unless email_token + if !user || circumstance == 'normal' + self.email_token ||= PostRedirect.generate_random_token + end + + self.email_token ||= PostRedirect.generate_verifiable_token( + user: user, circumstance: circumstance + ) end end diff --git a/app/models/pro_account.rb b/app/models/pro_account.rb index 78dcfaa738..aabe7974de 100644 --- a/app/models/pro_account.rb +++ b/app/models/pro_account.rb @@ -21,6 +21,8 @@ class ProAccount < ApplicationRecord validates :user, presence: true + strip_attributes only: %i[default_embargo_duration] + def subscription? subscriptions.current.any? end diff --git a/app/models/profile_photo.rb b/app/models/profile_photo.rb index 084f2c5735..c8ef69525e 100644 --- a/app/models/profile_photo.rb +++ b/app/models/profile_photo.rb @@ -70,7 +70,7 @@ def convert_image end if altered - write_attribute(:data, image.to_blob) + self.data = image.to_blob end end diff --git a/app/models/public_body.rb b/app/models/public_body.rb index c19fb29e70..04363b4503 100644 --- a/app/models/public_body.rb +++ b/app/models/public_body.rb @@ -129,8 +129,8 @@ class ImportCSVDryRun < StandardError; end ], :eager_load => [:translations] - strip_attributes :allow_empty => false, :except => [:request_email] - strip_attributes :allow_empty => true, :only => [:request_email] + strip_attributes allow_empty: false, except: %i[request_email] + strip_attributes allow_empty: true, only: %i[request_email] translates :name, :short_name, :request_email, :url_name, :notes, :first_letter, :publication_scheme @@ -143,7 +143,8 @@ class ImportCSVDryRun < StandardError; end # Cannot be grouped at top as it depends on the `translates` macro class Translation include PublicBodyDerivedFields - strip_attributes :allow_empty => true + strip_attributes allow_empty: false, except: %i[request_email] + strip_attributes allow_empty: true, only: %i[request_email] end self.non_versioned_columns << 'created_at' << 'updated_at' << 'first_letter' << 'api_key' @@ -582,8 +583,14 @@ def import_values_from_csv_row(row, line, name, options) begin save! rescue ActiveRecord::RecordInvalid - errors.full_messages.each do |msg| - options[:errors].push "error: line #{ line }: #{ msg } for authority '#{ name }'" + if rails_upgrade? + errors.each do |error| + options[:errors].push "error: line #{ line }: #{ error.full_message } for authority '#{ name }'" + end + else + errors.full_messages.each do |msg| + options[:errors].push "error: line #{ line }: #{ msg } for authority '#{ name }'" + end end next end @@ -709,7 +716,7 @@ def json_for_api end def expire_requests - info_requests.each { |request| request.expire } + info_requests.find_each(&:expire) end def self.where_clause_for_stats(minimum_requests, total_column) @@ -917,17 +924,10 @@ def questions private - # if the URL name has changed, then all requested_from: queries - # will break unless we update index for every event for every - # request linked to it + # If the url_name has changed, then all requested_from: queries will break + # unless we update index for every event for every request linked to it. def reindex_requested_from - return unless saved_change_to_attribute?(:url_name) - - info_requests.find_each do |info_request| - info_request.info_request_events.find_each do |info_request_event| - info_request_event.xapian_mark_needs_index - end - end + expire_requests if saved_change_to_attribute?(:url_name) end # Read an attribute value (without using locale fallbacks if the diff --git a/app/models/public_body_change_request.rb b/app/models/public_body_change_request.rb index e0ed9b2957..378f0c056a 100644 --- a/app/models/public_body_change_request.rb +++ b/app/models/public_body_change_request.rb @@ -82,6 +82,10 @@ def get_public_body_name public_body ? public_body.name : public_body_name end + def current_public_body_email + public_body&.request_email + end + def send_message mail = if add_body_request? diff --git a/app/models/raw_email.rb b/app/models/raw_email.rb index e5649e2eff..b329aa9012 100644 --- a/app/models/raw_email.rb +++ b/app/models/raw_email.rb @@ -107,15 +107,9 @@ def data end def data_as_text - text = data - if text.respond_to?(:encoding) - text = text.encode("UTF-8", :invalid => :replace, + data.encode("UTF-8", :invalid => :replace, :undef => :replace, :replace => "") - else - text = Iconv.conv('UTF-8//IGNORE', 'UTF-8', text) - end - text end def destroy_file_representation! diff --git a/app/models/statistics.rb b/app/models/statistics.rb index 6f252ac8b4..415ccb70ce 100644 --- a/app/models/statistics.rb +++ b/app/models/statistics.rb @@ -1,141 +1,131 @@ -class Statistics - class << self - def public_bodies - per_graph = 10 - minimum_requests = AlaveteliConfiguration::minimum_requests_for_statistics - # Make sure minimum_requests is > 0 to avoid division-by-zero - minimum_requests = [minimum_requests, 1].max - total_column = 'info_requests_count' +# Collection of classes and methods to generate various statistics +# Many of the top-level methods ought to be extracted to classes. +module Statistics + def self.public_bodies + per_graph = 10 + minimum_requests = AlaveteliConfiguration.minimum_requests_for_statistics + # Make sure minimum_requests is > 0 to avoid division-by-zero + minimum_requests = [minimum_requests, 1].max + total_column = 'info_requests_count' - graph_list = [] + graph_list = [] - [ - [ - total_column, - [{:title => _('Public bodies with the most requests'), - :y_axis => _('Number of requests'), - :highest => true}] - ], - [ - 'info_requests_successful_count', - [ - {:title => _('Public bodies with the most successful requests'), - :y_axis => _('Percentage of total requests'), - :highest => true}, - {:title => _('Public bodies with the fewest successful requests'), - :y_axis => _('Percentage of total requests'), - :highest => false} - ] - ], - [ - 'info_requests_overdue_count', - [{:title => _('Public bodies with most overdue requests'), - :y_axis => _('Percentage of requests that are overdue'), - :highest => true}] - ], - [ - 'info_requests_not_held_count', - [{:title => _('Public bodies that most frequently replied with "Not Held"'), - :y_axis => _('Percentage of total requests'), - :highest => true}] - ] - ].each do |column, graphs_properties| - graphs_properties.each do |graph_properties| - percentages = (column != total_column) - highest = graph_properties[:highest] + [ + [total_column, + [{ title: _('Public bodies with the most requests'), + y_axis: _('Number of requests'), + highest: true }]], + ['info_requests_successful_count', + [{ title: _('Public bodies with the most successful requests'), + y_axis: _('Percentage of total requests'), + highest: true }, + { title: _('Public bodies with the fewest successful requests'), + y_axis: _('Percentage of total requests'), + highest: false }]], + ['info_requests_overdue_count', + [{ title: _('Public bodies with most overdue requests'), + y_axis: _('Percentage of requests that are overdue'), + highest: true }]], + ['info_requests_not_held_count', + [{ title: _('Public bodies that most frequently replied with ' \ + '"Not Held"'), + y_axis: _('Percentage of total requests'), + highest: true }]] + ].each do |column, graphs_properties| + graphs_properties.each do |graph_properties| + percentages = (column != total_column) + highest = graph_properties[:highest] - data = nil + data = nil + data = if percentages - data = PublicBody.get_request_percentages(column, - per_graph, - highest, - minimum_requests) + PublicBody.get_request_percentages(column, + per_graph, + highest, + minimum_requests) else - data = PublicBody.get_request_totals(per_graph, - highest, - minimum_requests) + PublicBody.get_request_totals(per_graph, + highest, + minimum_requests) end - if data - graph_list.push self.simplify_stats_for_graphs(data, - column, - percentages, - graph_properties) - end + if data + graph_list.push simplify_stats_for_graphs(data, + column, + percentages, + graph_properties) end end - - graph_list end - # This is a helper method to take data returned by the above method and - # converting them to simpler data structure that can be rendered by a - # Javascript graph library. - def simplify_stats_for_graphs(data, - column, - percentages, - graph_properties) - # Copy the data, only taking known-to-be-safe keys: - result = Hash.new { |h, k| h[k] = [] } - result.update Hash[data.select do |key, value| - ['y_values', - 'y_max', - 'totals', - 'cis_below', - 'cis_above'].include? key - end] + graph_list + end - # Extract data about the public bodies for the x-axis, - # tooltips, and so on: - data['public_bodies'].each_with_index do |pb, i| - result['x_values'] << i - result['x_ticks'] << [i, pb.short_or_long_name.truncate(30)] - result['tooltips'] << "#{pb.name} (#{result['totals'][i]})" - result['public_bodies'] << { - 'name' => pb.name, - # FIXME: This seems nasty, can we simplify it? - 'url' => Rails.application.routes.url_helpers.show_public_body_path(url_name: pb.url_name) - } - end + # This is a helper method to take data returned by the above method and + # converting them to simpler data structure that can be rendered by a + # Javascript graph library. + def self.simplify_stats_for_graphs(data, column, percentages, graph_properties) + # Copy the data, only taking known-to-be-safe keys: + result = Hash.new { |h, k| h[k] = [] } - # Set graph metadata properties, like the title, axis labels, etc. - graph_id = "#{column}-" - graph_id += graph_properties[:highest] ? 'highest' : 'lowest' - result.update({ - 'id' => graph_id, - 'x_axis' => _('Public Bodies'), - 'y_axis' => graph_properties[:y_axis], - 'errorbars' => percentages, - 'title' => graph_properties[:title] - }) - end + result.update Hash[data.select do |key, _value| + %w[y_values + y_max + totals + cis_below + cis_above].include?(key) + end] - def users - { - all_time_requesters: User.all_time_requesters, - last_28_day_requesters: User.last_28_day_requesters, - all_time_commenters: User.all_time_commenters, - last_28_day_commenters: User.last_28_day_commenters + # Extract data about the public bodies for the x-axis, + # tooltips, and so on: + data['public_bodies'].each_with_index do |pb, i| + result['x_values'] << i + result['x_ticks'] << [i, pb.short_or_long_name.truncate(30)] + result['tooltips'] << "#{pb.name} (#{result['totals'][i]})" + result['public_bodies'] << { + 'name' => pb.name, + # FIXME: This seems nasty, can we simplify it? + 'url' => Rails.application.routes.url_helpers. + show_public_body_path(url_name: pb.url_name) } end - def user_json_for_api(user_statistics) - user_statistics.each do |k,v| - user_statistics[k] = v.map { |u,c| { user: u.json_for_api, count: c } } - end + # Set graph metadata properties, like the title, axis labels, etc. + graph_id = "#{column}-" + graph_id += graph_properties[:highest] ? 'highest' : 'lowest' + result.update({ 'id' => graph_id, + 'x_axis' => _('Public Bodies'), + 'y_axis' => graph_properties[:y_axis], + 'errorbars' => percentages, + 'title' => graph_properties[:title] }) + end + + def self.leaderboard + leaderboard = Leaderboard.new + { all_time_requesters: leaderboard.all_time_requesters, + last_28_day_requesters: leaderboard.last_28_day_requesters, + all_time_commenters: leaderboard.all_time_commenters, + last_28_day_commenters: leaderboard.last_28_day_commenters } + end + + def self.user_json_for_api(user_statistics) + user_statistics.each do |k, v| + user_statistics[k] = v.map { |u, c| { user: u.json_for_api, count: c } } end + end - def by_week_to_today_with_noughts(counts_by_week, start_date) - earliest_week = start_date.to_date.at_beginning_of_week - latest_week = Date.current.at_beginning_of_week + def self.by_week_to_today_with_noughts(counts_by_week, start_date) + earliest_week = start_date.to_date.at_beginning_of_week + latest_week = Date.current.at_beginning_of_week - counts_by_week.map! { |date, count| [date.to_s, count] } + counts_by_week.map! { |date, count| [date.to_s, count] } - (earliest_week..latest_week).step(7) do |date| - counts_by_week << [date.to_s, 0] unless counts_by_week.any? { |c| c.first == date.to_s } + (earliest_week..latest_week).step(7) do |date| + unless counts_by_week.any? { |c| c.first == date.to_s } + counts_by_week << [date.to_s, 0] end - - counts_by_week.sort end + + counts_by_week.sort end end diff --git a/app/models/statistics/general.rb b/app/models/statistics/general.rb new file mode 100644 index 0000000000..b009430f3a --- /dev/null +++ b/app/models/statistics/general.rb @@ -0,0 +1,34 @@ +module Statistics + # High-level site statistics and version information + class General + def to_h + { + alaveteli_git_commit: alaveteli_git_commit, + alaveteli_version: ALAVETELI_VERSION, + ruby_version: RUBY_VERSION, + visible_public_body_count: PublicBody.visible.count, + visible_request_count: InfoRequest.is_searchable.count, + private_request_count: InfoRequest.embargoed.count, + confirmed_user_count: User.active.where(email_confirmed: true).count, + visible_comment_count: Comment.visible.count, + track_thing_count: TrackThing.count, + widget_vote_count: WidgetVote.count, + public_body_change_request_count: PublicBodyChangeRequest.count, + request_classification_count: RequestClassification.count, + visible_followup_message_count: OutgoingMessage. + followup.is_searchable.count, + citation_count: Citation.count + } + end + + def to_json(*) + to_h.to_json + end + + protected + + def alaveteli_git_commit + `git log -1 --format="%H"`.strip + end + end +end diff --git a/app/models/statistics/leaderboard.rb b/app/models/statistics/leaderboard.rb new file mode 100644 index 0000000000..2600626898 --- /dev/null +++ b/app/models/statistics/leaderboard.rb @@ -0,0 +1,54 @@ +module Statistics + # Top 10 User records who've performed specific site actions + class Leaderboard + def all_time_requesters + InfoRequest.is_public. + joins(:user). + group(:user). + order('count_info_requests_all DESC'). + limit(10). + count + end + + def last_28_day_requesters + # TODO: Refactor as it's basically the same as all_time_requesters + InfoRequest.is_public. + where('info_requests.created_at >= ?', 28.days.ago). + joins(:user). + group(:user). + order('count_info_requests_all DESC'). + limit(10). + count + end + + def all_time_commenters + commenters = Comment.visible. + joins(:user). + group('comments.user_id'). + order('count_all DESC'). + limit(10). + count + # TODO: Have user objects automatically instantiated like the InfoRequest + # queries above + result = {} + commenters.each { |user_id, count| result[User.find(user_id)] = count } + result + end + + def last_28_day_commenters + # TODO: Refactor as it's basically the same as all_time_commenters + commenters = Comment.visible. + where('comments.created_at >= ?', 28.days.ago). + joins(:user). + group('comments.user_id'). + order('count_all DESC'). + limit(10). + count + # TODO: Have user objects automatically instantiated like the InfoRequest + # queries above + result = {} + commenters.each { |user_id, count| result[User.find(user_id)] = count } + result + end + end +end diff --git a/app/models/track_thing.rb b/app/models/track_thing.rb index 7c75691f81..eae54f40b0 100644 --- a/app/models/track_thing.rb +++ b/app/models/track_thing.rb @@ -238,7 +238,7 @@ def public_body_updates_params # Authentication web: _("To follow requests made using {{site_name}} to the public " \ "authority '{{public_body_name}}'", - site_name: AlaveteliConfiguration.site_name.html_safe, + site_name: site_name.html_safe, public_body_name: public_body.name.html_safe), email: _("Then you will be notified whenever someone requests " \ "something or gets a response from '{{public_body_name}}'.", diff --git a/app/models/user.rb b/app/models/user.rb index 6661e331c2..ae711e177f 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,5 +1,5 @@ # == Schema Information -# Schema version: 20210114161442 +# Schema version: 20210921094059 # # Table name: users # @@ -34,19 +34,21 @@ # daily_summary_hour :integer # daily_summary_minute :integer # closed_at :datetime +# login_token :string # class User < ApplicationRecord include AlaveteliFeatures::Helpers include AlaveteliPro::PhaseCounts include User::Authentication + include User::LoginToken + include User::OneTimePassword include User::Survey rolify before_add: :setup_pro_account strip_attributes :allow_empty => true attr_accessor :no_xapian_reindex - attr_accessor :entered_otp_code has_many :info_requests, -> { order('info_requests.created_at desc') }, @@ -146,8 +148,6 @@ class User < ApplicationRecord :message => _("This email is already in use") } validate :email_and_name_are_valid - validate :verify_otp_code, - :if => Proc.new { |u| u.otp_enabled? && u.require_otp? } after_initialize :set_defaults after_update :reindex_referencing_models, :update_pro_account @@ -159,7 +159,6 @@ class User < ApplicationRecord :terms => [ [ :variety, 'V', "variety" ] ], :if => :indexed_by_search? - has_one_time_password :counter_based => true def self.pro with_role :pro @@ -223,20 +222,28 @@ def self.internal_admin_user end def self.owns_every_request?(user) - !user.nil? && user.owns_every_request? + warn %q([DEPRECATION] User#owns_every_request? will be removed in 0.41. + It has been replaced by User#owns_every_request?).squish + user&.owns_every_request? end - # Can the user see every request, response, and outgoing message, even hidden ones? def self.view_hidden?(user) - !user.nil? && user.is_admin? + warn %q([DEPRECATION] User.view_hidden? will be removed in 0.41. + It has been replaced by User#view_hidden?).squish + user&.view_hidden? end def self.view_embargoed?(user) - !user.nil? && user.is_pro_admin? + warn %q([DEPRECATION] User.view_embargoed? will be removed in 0.41. + It has been replaced by User#view_embargoed?).squish + user&.view_embargoed? end def self.view_hidden_and_embargoed?(user) - view_hidden?(user) && view_embargoed?(user) + warn %q([DEPRECATION] User.view_hidden_and_embargoed? will be removed in + 0.41. It has been replaced by User#view_hidden_and_embargoed?). + squish + user&.view_hidden_and_embargoed? end # Should the user be kept logged into their own account @@ -281,52 +288,16 @@ def self.find_similar_named_users(user) user.name, true, user.id).order(:created_at) end - def self.all_time_requesters - InfoRequest.is_public. - joins(:user). - group(:user). - order("count_info_requests_all DESC"). - limit(10). - count - end - - def self.last_28_day_requesters - # TODO: Refactor as it's basically the same as all_time_requesters - InfoRequest.is_public. - where("info_requests.created_at >= ?", 28.days.ago). - joins(:user). - group(:user). - order("count_info_requests_all DESC"). - limit(10). - count - end - - def self.all_time_commenters - commenters = Comment.visible. - joins(:user). - group("comments.user_id"). - order("count_all DESC"). - limit(10). - count - # TODO: Have user objects automatically instantiated like the InfoRequest queries above - result = {} - commenters.each { |user_id,count| result[User.find(user_id)] = count } - result - end - - def self.last_28_day_commenters - # TODO: Refactor as it's basically the same as all_time_commenters - commenters = Comment.visible. - where("comments.created_at >= ?", 28.days.ago). - joins(:user). - group("comments.user_id"). - order("count_all DESC"). - limit(10). - count - # TODO: Have user objects automatically instantiated like the InfoRequest queries above - result = {} - commenters.each { |user_id,count| result[User.find(user_id)] = count } - result + def view_hidden? + is_admin? + end + + def view_embargoed? + is_pro_admin? + end + + def view_hidden_and_embargoed? + view_hidden? && view_embargoed? end def transactions(*associations) @@ -349,17 +320,21 @@ def reindex_referencing_models return if no_xapian_reindex == true return unless saved_change_to_attribute?(:url_name) + expire_comments + expire_requests + end + + def expire_requests + info_requests.find_each(&:expire) + end + + def expire_comments comments.find_each do |comment| + # TODO: Extract to Comment#expire comment.info_request_events.find_each do |info_request_event| info_request_event.xapian_mark_needs_index end end - - info_requests.find_each do |info_request| - info_request.info_request_events.find_each do |info_request_event| - info_request_event.xapian_mark_needs_index - end - end end def locale @@ -390,32 +365,7 @@ def update_url_name unique_url_name = url_name + "_" + suffix_num.to_s suffix_num = suffix_num + 1 end - write_attribute(:url_name, unique_url_name) - end - - def otp_enabled? - (otp_secret_key && otp_counter && otp_enabled) ? true : false - end - - def enable_otp - otp_regenerate_secret - otp_regenerate_counter - self.otp_enabled = true - end - - def disable_otp - self.otp_enabled = false - self.require_otp = false - true - end - - def require_otp? - @require_otp = false if @require_otp.nil? - @require_otp - end - - def require_otp=(value) - @require_otp = value ? true : false + self.url_name = unique_url_name end # For use in to/from in email messages @@ -507,10 +457,6 @@ def exceeded_limit? recent_requests >= AlaveteliConfiguration.max_requests_per_user_per_day end - def expire_requests - info_requests.each { |request| request.expire } - end - def next_request_permitted_at return nil if no_limit @@ -562,7 +508,7 @@ def set_profile_photo(new_profile_photo) ActiveRecord::Base.transaction do profile_photo.destroy unless profile_photo.nil? self.profile_photo = new_profile_photo - save + save! end end @@ -605,7 +551,7 @@ def record_bounce(message) def confirm(save_record = false) self.email_confirmed = true - save if save_record + save! if save_record end def confirm! @@ -614,7 +560,7 @@ def confirm! end def should_be_emailed? - email_confirmed && email_bounced_at.nil? && active? + active? && email_confirmed? && receive_email_alerts? && !email_bounced_at end def indexed_by_search? @@ -709,17 +655,6 @@ def email_and_name_are_valid end end - def verify_otp_code - if entered_otp_code.nil? || !authenticate_otp(entered_otp_code) - msg = _('Invalid one time password') - errors.add(:otp_code, msg) - return false - end - - self.otp_counter += 1 - self.entered_otp_code = nil - end - def setup_pro_account(role) return unless role == Role.pro_role pro_account || build_pro_account if feature_enabled?(:pro_pricing) diff --git a/app/models/user/authentication.rb b/app/models/user/authentication.rb index c97d4e9f74..b58b543ecf 100644 --- a/app/models/user/authentication.rb +++ b/app/models/user/authentication.rb @@ -82,6 +82,6 @@ def attempt_password_upgrade(password) self.password = password self.salt = nil - save(validate: false) + save!(validate: false) end end diff --git a/app/models/user/login_token.rb b/app/models/user/login_token.rb new file mode 100644 index 0000000000..d6824dd15a --- /dev/null +++ b/app/models/user/login_token.rb @@ -0,0 +1,21 @@ +# Sets the current users login token based on their current password and email +module User::LoginToken + extend ActiveSupport::Concern + + included do + before_save :set_login_token + end + + private + + def set_login_token + set_login_token! if email_changed? || hashed_password_changed? + end + + def set_login_token! + self.login_token = Digest::UUID.uuid_v5("User;#{id}", { + email: email, + hashed_password: hashed_password + }.to_s) + end +end diff --git a/app/models/user/one_time_password.rb b/app/models/user/one_time_password.rb new file mode 100644 index 0000000000..2b716429e1 --- /dev/null +++ b/app/models/user/one_time_password.rb @@ -0,0 +1,54 @@ +# Optional two factor authentication +module User::OneTimePassword + extend ActiveSupport::Concern + + included do + has_one_time_password counter_based: true + + attr_accessor :entered_otp_code + + validate :verify_otp_code, if: :otp_enabled_and_required? + end + + def otp_enabled? + (otp_secret_key && otp_counter && otp_enabled) ? true : false + end + + def enable_otp + otp_regenerate_secret + otp_regenerate_counter + self.otp_enabled = true + end + + def disable_otp + self.otp_enabled = false + self.require_otp = false + true + end + + def require_otp? + @require_otp = false if @require_otp.nil? + @require_otp + end + + def require_otp=(value) + @require_otp = value ? true : false + end + + private + + def otp_enabled_and_required? + otp_enabled? && require_otp? + end + + def verify_otp_code + if entered_otp_code.nil? || !authenticate_otp(entered_otp_code) + msg = _('Invalid one time password') + errors.add(:otp_code, msg) + return false + end + + self.otp_counter += 1 + self.entered_otp_code = nil + end +end diff --git a/app/models/user/survey.rb b/app/models/user/survey.rb index 8c7c47803a..50c3c81b1a 100644 --- a/app/models/user/survey.rb +++ b/app/models/user/survey.rb @@ -7,6 +7,6 @@ def survey_recently_sent? end def can_send_survey? - active? && !survey_recently_sent? + should_be_emailed? && !survey_recently_sent? end end diff --git a/app/views/admin/outgoing_messages/snippets/index.html.erb b/app/views/admin/outgoing_messages/snippets/index.html.erb index b83a6da5af..a647e004db 100644 --- a/app/views/admin/outgoing_messages/snippets/index.html.erb +++ b/app/views/admin/outgoing_messages/snippets/index.html.erb @@ -37,12 +37,7 @@ - <% if type == 'datetime' %> - <%= value.to_s(:db) %> - (<%= time_ago_in_words(value) %> ago) - <% else %> - <%= h value %> - <% end %> + <%= admin_value(value) %> <% end %> diff --git a/app/views/admin_announcements/_form.html.erb b/app/views/admin_announcements/_form.html.erb index 08f7434f60..32c512d82f 100644 --- a/app/views/admin_announcements/_form.html.erb +++ b/app/views/admin_announcements/_form.html.erb @@ -1,4 +1,4 @@ -<%= f.error_messages %> +<%= foi_error_messages_for :announcement %> diff --git a/app/views/admin_censor_rule/_form.html.erb b/app/views/admin_censor_rule/_form.html.erb index d3343629ae..1b81c920cb 100644 --- a/app/views/admin_censor_rule/_form.html.erb +++ b/app/views/admin_censor_rule/_form.html.erb @@ -1,4 +1,4 @@ -<%= error_messages_for 'censor_rule' %> +<%= foi_error_messages_for :censor_rule %>
Applies to diff --git a/app/views/admin_comment/edit.html.erb b/app/views/admin_comment/edit.html.erb index 46e43d1e4e..9cb1e4f3df 100644 --- a/app/views/admin_comment/edit.html.erb +++ b/app/views/admin_comment/edit.html.erb @@ -1,6 +1,6 @@

Edit annotation

-<%= error_messages_for 'comment' %> +<%= foi_error_messages_for 'comment' %> <%= form_tag admin_comment_path(@comment), :method => 'put' do %> @@ -19,7 +19,7 @@ ]) %>

-

<%= submit_tag 'Save', :accesskey => 's' %>

+

<%= submit_tag 'Save', :accesskey => 's', :class => 'btn btn-success' %>

<% end %>

@@ -70,13 +70,8 @@ :locals => { :params => info_request_event.params_diff } %> - <% elsif value.nil? %> - nil - <% elsif %w(text string).include?(type) %> - <%=h value.humanize %> - <% elsif type == 'datetime' %> - <%= admin_date value %> - <%=h value %> + <% else %> + <%= admin_value(value) %> <% end %> diff --git a/app/views/admin_comment/index.html.erb b/app/views/admin_comment/index.html.erb index cda7db40b8..5ed06aa746 100644 --- a/app/views/admin_comment/index.html.erb +++ b/app/views/admin_comment/index.html.erb @@ -18,7 +18,7 @@ edit_admin_comment_path(comment) %>

@@ -29,12 +29,7 @@ <%= h name %> - <% if type == 'datetime' %> - <%= value.to_s(:db) %> - (<%= time_ago_in_words(value) %> ago) - <% else %> - <%= h value %> - <% end %> + <%= admin_value(value) %> <% end %> diff --git a/app/views/admin_general/_change_request_summary.html.erb b/app/views/admin_general/_change_request_summary.html.erb index d264342ea8..d7fca5995b 100644 --- a/app/views/admin_general/_change_request_summary.html.erb +++ b/app/views/admin_general/_change_request_summary.html.erb @@ -52,8 +52,7 @@ Requested on - <%= I18n.l(@change_request.created_at, :format => "%e %B %Y %H:%M:%S") %> - (<%= "#{time_ago_in_words(@change_request.created_at)} ago" %>) + <%= admin_date(@change_request.created_at) %> diff --git a/app/views/admin_general/index.html.erb b/app/views/admin_general/index.html.erb index a199dfc6d4..17e055efc0 100644 --- a/app/views/admin_general/index.html.erb +++ b/app/views/admin_general/index.html.erb @@ -55,6 +55,7 @@ locals: { id: 'unclassified', parent: 'public-request-things-to-do', + count: @old_unclassified_count, items: @old_unclassified, label: to_do_list_label } %> @@ -74,8 +75,8 @@ parent: 'authority-things-to-do', items: @blank_contacts, count: @blank_contact_count, - label: 'Find missing FOI email for these - public authorities (try phoning!)' } %> + label: 'Find missing FOI email for these public + authorities' } %> <% if @new_body_requests.size > 0 %>
diff --git a/app/views/admin_general/stats.html.erb b/app/views/admin_general/stats.html.erb index 9996dfabf3..ee6feec9fb 100644 --- a/app/views/admin_general/stats.html.erb +++ b/app/views/admin_general/stats.html.erb @@ -10,7 +10,7 @@

Statistics

Chart of requests (excluding backpaged)

- <% if File.exists?(File.expand_path("public/foi-live-creation.png", Rails.root)) %> + <% if File.exist?(File.expand_path("public/foi-live-creation.png", Rails.root)) %> Chart of requests <% else %> No requests yet (the raw data may include the Holding Pen request) @@ -37,7 +37,7 @@

Chart of users

- <% if File.exists?(File.expand_path("public/foi-user-use.png", Rails.root)) %> + <% if File.exist?(File.expand_path("public/foi-user-use.png", Rails.root)) %> Chart of users <% else %> No user data yet diff --git a/app/views/admin_holiday_imports/new.html.erb b/app/views/admin_holiday_imports/new.html.erb index d62b7b15c0..5b372e72cd 100644 --- a/app/views/admin_holiday_imports/new.html.erb +++ b/app/views/admin_holiday_imports/new.html.erb @@ -3,7 +3,8 @@ <%= form_for( @holiday_import, :as => 'holiday_import', :url => '', :method => 'get', :html => { :class => 'form-horizontal form-inline' }) do |f| %> <% if @holiday_import.holidays.empty? %> - <%= error_messages_for 'holiday_import', :header_message => 'There was a problem with these import settings' %> + <% if @holiday_import.errors.any? %>

There was a problem with these import settings

<% end %> + <%= foi_error_messages_for :holiday_import %> <% end %> Import settings
@@ -58,7 +59,7 @@ <% end %> <% else %> <%= form_for( @holiday_import, :as => 'holiday_import', :url => admin_holiday_imports_path, :html => { :class => 'form-inline' } ) do |f| -%> - <%= error_messages_for 'holiday_import' %> + <%= foi_error_messages_for :holiday_import %> <% if @holiday_import.source == 'suggestions' %> Suggested holidays for <%= @holiday_import.suggestions_country_name %> (<%= @holiday_import.period %>) diff --git a/app/views/admin_holidays/_form.html.erb b/app/views/admin_holidays/_form.html.erb index 01fcc0f5b0..8d9138c80c 100644 --- a/app/views/admin_holidays/_form.html.erb +++ b/app/views/admin_holidays/_form.html.erb @@ -1,4 +1,4 @@ -<%= error_messages_for 'holiday' %> +<%= foi_error_messages_for :holiday %>
<% if holiday.new_record? %> @@ -16,7 +16,7 @@ <%= f.submit "Remove", :class => 'btn remove-holiday' %> <% else %> <%= link_to("Cancel", admin_holidays_path, :class => 'btn') %> - <%= f.submit "Save", :class => 'btn btn-warning' %> + <%= f.submit "Save", :class => 'btn btn-success' %> <% end %>
diff --git a/app/views/admin_incoming_message/bulk_destroy.html.erb b/app/views/admin_incoming_message/bulk_destroy.html.erb index 8c63e81f78..cf757f4c3a 100644 --- a/app/views/admin_incoming_message/bulk_destroy.html.erb +++ b/app/views/admin_incoming_message/bulk_destroy.html.erb @@ -2,7 +2,7 @@ <% @incoming_messages.each do |incoming_message| %> <%= h(incoming_message.mail_from) %> - at <%=admin_value(incoming_message.sent_at)%> + at <%= admin_date(incoming_message.sent_at) %>
<% if !incoming_message.cached_main_body_text_folded.nil? %> <%= truncate( diff --git a/app/views/admin_outgoing_message/edit.html.erb b/app/views/admin_outgoing_message/edit.html.erb index 6c386b6d96..05d18fea37 100644 --- a/app/views/admin_outgoing_message/edit.html.erb +++ b/app/views/admin_outgoing_message/edit.html.erb @@ -1,6 +1,6 @@

Edit outgoing message

-<%= error_messages_for 'outgoing_message' %> +<%= foi_error_messages_for :outgoing_message %> <%= form_tag admin_outgoing_message_path(@outgoing_message), :method => 'put' do %>
@@ -35,7 +35,7 @@ only the edited version will be shown on the public page if you do that.

- <%= submit_tag 'Save', :accesskey => 's', :class => 'btn btn-primary' %> + <%= submit_tag 'Save', :accesskey => 's', :class => 'btn btn-success' %> <%= link_to resend_admin_outgoing_message_path(@outgoing_message), :class => 'btn btn-warning', :method => :post, diff --git a/app/views/admin_public_body/_form.html.erb b/app/views/admin_public_body/_form.html.erb index 696e4dda7c..3e440840a2 100644 --- a/app/views/admin_public_body/_form.html.erb +++ b/app/views/admin_public_body/_form.html.erb @@ -1,8 +1,16 @@ <% if @public_body.errors.any? %>
    - <% @public_body.errors.each do |attr, message| %> - <% unless attr.to_s.starts_with?('translation') %> -
  • <%= message %>
  • + <% if rails_upgrade? %> + <% @public_body.errors.each do |error| %> + <% unless error.attribute.to_s.starts_with?('translation') %> +
  • <%= error.message %>
  • + <% end %> + <% end %> + <% else %> + <% @public_body.errors.each do |attr, message| %> + <% unless attr.to_s.starts_with?('translation') %> +
  • <%= message %>
  • + <% end %> <% end %> <% end %>
@@ -12,8 +20,14 @@ <% if translation.errors.any? %> <%= locale_name(translation.locale.to_s) || translation.locale.to_s %>
    - <% translation.errors.each do |attr, message| %> -
  • <%= message %>
  • + <% if rails_upgrade? %> + <% translation.errors.each do |error| %> +
  • <%= error.message %>
  • + <% end %> + <% else %> + <% translation.errors.each do |attr, message| %> +
  • <%= message %>
  • + <% end %> <% end %>
<% end %> diff --git a/app/views/admin_public_body/_locale_fields.html.erb b/app/views/admin_public_body/_locale_fields.html.erb index 6bfd452496..821b18c44e 100644 --- a/app/views/admin_public_body/_locale_fields.html.erb +++ b/app/views/admin_public_body/_locale_fields.html.erb @@ -48,14 +48,14 @@
- <%= t.label :notes, 'Public notes', :class => 'control-label' %> + <%= t.label :notes, 'Public notes', class: 'control-label' %> +
- <%= t.text_area :notes, :rows => 3, :class => "span6" %> + <%= t.text_area :notes, rows: 10, class: 'span5' %> +

HTML, for users to consider when making FOI requests to the authority

-
- diff --git a/app/views/admin_public_body/_one_list.html.erb b/app/views/admin_public_body/_one_list.html.erb index 772d0d57fa..d700356651 100644 --- a/app/views/admin_public_body/_one_list.html.erb +++ b/app/views/admin_public_body/_one_list.html.erb @@ -19,12 +19,7 @@ <%=name%> - <% if type == 'datetime' %> - <%= value.to_s(:db) %> - (<%= time_ago_in_words(value) %> ago) - <% else %> - <%= h value %>  - <% end %> + <%= admin_value(value) %>
<% end %> diff --git a/app/views/admin_public_body/import_csv.html.erb b/app/views/admin_public_body/import_csv.html.erb index c90efcbec8..57ab0b78e0 100644 --- a/app/views/admin_public_body/import_csv.html.erb +++ b/app/views/admin_public_body/import_csv.html.erb @@ -50,10 +50,12 @@

CSV file format: The first row should be a list - of fields, starting with #. The fields name and - request_email are required; additionally, translated values are - supported by adding the locale name to the field name, - e.g. name.es, name.de
+ of field headers, starting with #. The fields + name and request_email are required, but the + values for request_email may be left blank to leave the + existing request_email unchanged. Translated values are + supported by adding the locale name to the field name, e.g. + name.es, name.de
Example:

diff --git a/app/views/admin_public_body/new.html.erb b/app/views/admin_public_body/new.html.erb index 8935e7ee22..f5dfbc3fb7 100644 --- a/app/views/admin_public_body/new.html.erb +++ b/app/views/admin_public_body/new.html.erb @@ -9,7 +9,7 @@
- <%= f.submit "Create", :class => "btn btn-primary" %> + <%= f.submit "Create", :class => "btn btn-success" %>
<% end %>
diff --git a/app/views/admin_public_body_categories/_form.html.erb b/app/views/admin_public_body_categories/_form.html.erb index 2d289f3a7b..681ce06e89 100644 --- a/app/views/admin_public_body_categories/_form.html.erb +++ b/app/views/admin_public_body_categories/_form.html.erb @@ -1,8 +1,16 @@ <% if @public_body_category.errors.any? %>
    - <% @public_body_category.errors.each do |attr, message| %> - <% unless attr.to_s.starts_with?('translation') %> -
  • <%= message %>
  • + <% if rails_upgrade? %> + <% @public_body_category.errors.each do |error| %> + <% unless error.attribute.to_s.starts_with?('translation') %> +
  • <%= error.message %>
  • + <% end %> + <% end %> + <% else %> + <% @public_body_category.errors.each do |attr, message| %> + <% unless attr.to_s.starts_with?('translation') %> +
  • <%= message %>
  • + <% end %> <% end %> <% end %>
@@ -12,8 +20,14 @@ <% if translation.errors.any? %> <%= locale_name(translation.locale.to_s) || translation.locale.to_s %>
    - <% translation.errors.each do |attr, message| %> -
  • <%= message %>
  • + <% if rails_upgrade? %> + <% translation.errors.each do |error| %> +
  • <%= error.message %>
  • + <% end %> + <% else %> + <% translation.errors.each do |attr, message| %> +
  • <%= message %>
  • + <% end %> <% end %>
<% end %> diff --git a/app/views/admin_public_body_categories/_heading_list.html.erb b/app/views/admin_public_body_categories/_heading_list.html.erb index 629ee4084b..48b712d9a6 100644 --- a/app/views/admin_public_body_categories/_heading_list.html.erb +++ b/app/views/admin_public_body_categories/_heading_list.html.erb @@ -20,7 +20,7 @@
- <%= link_to "Save", '#', :class => "btn btn-primary disabled save-order", "data-heading-id" => heading.id, "data-list-id" => "#heading_#{heading.id}_category_list", 'data-endpoint' => reorder_categories_admin_heading_path(heading) %>

Drag and drop to change the order of categories.

+ <%= link_to "Save", '#', :class => "btn btn-success disabled save-order", "data-heading-id" => heading.id, "data-list-id" => "#heading_#{heading.id}_category_list", 'data-endpoint' => reorder_categories_admin_heading_path(heading) %>

Drag and drop to change the order of categories.

diff --git a/app/views/admin_public_body_categories/index.html.erb b/app/views/admin_public_body_categories/index.html.erb index 4eaae3edd2..1d4afb1393 100644 --- a/app/views/admin_public_body_categories/index.html.erb +++ b/app/views/admin_public_body_categories/index.html.erb @@ -23,6 +23,6 @@ <% end %> <% end %>
- <%= link_to "Save", '#', :class => "btn btn-primary disabled save-order", "data-list-id" => '#category_list', 'data-endpoint' => reorder_admin_headings_path %>

Drag and drop to change the order of category headings.

+ <%= link_to "Save", '#', :class => "btn btn-success disabled save-order", "data-list-id" => '#category_list', 'data-endpoint' => reorder_admin_headings_path %>

Drag and drop to change the order of category headings.

diff --git a/app/views/admin_public_body_headings/_form.html.erb b/app/views/admin_public_body_headings/_form.html.erb index 9a508b9148..9a314dce82 100644 --- a/app/views/admin_public_body_headings/_form.html.erb +++ b/app/views/admin_public_body_headings/_form.html.erb @@ -1,8 +1,16 @@ <% if @public_body_heading.errors.any? %>
    - <% @public_body_heading.errors.each do |attr, message| %> - <% unless attr.to_s.starts_with?('translation') %> -
  • <%= message %>
  • + <% if rails_upgrade? %> + <% @public_body_heading.errors.each do |error| %> + <% unless error.attribute.to_s.starts_with?('translation') %> +
  • <%= error.message %>
  • + <% end %> + <% end %> + <% else %> + <% @public_body_heading.errors.each do |attr, message| %> + <% unless attr.to_s.starts_with?('translation') %> +
  • <%= message %>
  • + <% end %> <% end %> <% end %>
@@ -12,8 +20,14 @@ <% if translation.errors.any? %> <%= locale_name(translation.locale.to_s) || translation.locale.to_s %>
    - <% translation.errors.each do |attr, message| %> -
  • <%= message %>
  • + <% if rails_upgrade? %> + <% translation.errors.each do |error| %> +
  • <%= error.message %>
  • + <% end %> + <% else %> + <% translation.errors.each do |attr, message| %> +
  • <%= message %>
  • + <% end %> <% end %>
<% end %> diff --git a/app/views/admin_public_body_headings/new.html.erb b/app/views/admin_public_body_headings/new.html.erb index 36aef029a3..292b33bdb5 100644 --- a/app/views/admin_public_body_headings/new.html.erb +++ b/app/views/admin_public_body_headings/new.html.erb @@ -8,7 +8,7 @@ <%= render :partial => 'form', :locals => { :f => f } %>
- <%= f.submit "Create", :class => "btn btn-primary" %> + <%= f.submit "Create", :class => "btn btn-success" %> <%= link_to 'List all', admin_categories_path, :class => "btn" %>
<% end %> diff --git a/app/views/admin_request/_some_annotations.html.erb b/app/views/admin_request/_some_annotations.html.erb index 387a97f1be..233e2fd552 100644 --- a/app/views/admin_request/_some_annotations.html.erb +++ b/app/views/admin_request/_some_annotations.html.erb @@ -12,7 +12,7 @@ #<%= comment.id %> -- <%= h(comment.user.name) %> - <%= admin_value(comment.created_at) %> + <%= admin_date(comment.created_at) %> <% end %> <%= comment_visibility(comment) %> diff --git a/app/views/admin_request/_some_requests.html.erb b/app/views/admin_request/_some_requests.html.erb index 805a47c180..6fff72cfbe 100644 --- a/app/views/admin_request/_some_requests.html.erb +++ b/app/views/admin_request/_some_requests.html.erb @@ -11,7 +11,7 @@ <% end %>
@@ -21,12 +21,7 @@ <%= h name %> - <% if type == 'datetime' && value %> - <%= value.to_s(:db) %> - (<%= time_ago_in_words(value) %> ago) - <% else %> - <%=h value %> - <% end %> + <%= admin_value(value) %>
<% end %> diff --git a/app/views/admin_request/edit.html.erb b/app/views/admin_request/edit.html.erb index 353d9323d7..c3f62972b1 100644 --- a/app/views/admin_request/edit.html.erb +++ b/app/views/admin_request/edit.html.erb @@ -1,6 +1,6 @@

Edit basic details of request

-<%= error_messages_for 'info_request' %> +<%= foi_error_messages_for :info_request %> <%= form_tag admin_request_path(@info_request), :method => :put do %> @@ -82,7 +82,7 @@ <%= text_field 'info_request', 'tag_string', :size => 60 %>

<%= submit_tag 'Save changes', :accesskey => 's', - :class => 'btn btn-primary', + :class => 'btn btn-success', :data => { toggle: 'tooltip' } %>

diff --git a/app/views/admin_request/show.html.erb b/app/views/admin_request/show.html.erb index 3fe809992f..662de899f2 100644 --- a/app/views/admin_request/show.html.erb +++ b/app/views/admin_request/show.html.erb @@ -57,15 +57,12 @@ <%= name %>: - <% if type == 'datetime' && value %> - <%= value.to_s(:db) %> - (<%= time_ago_in_words(value) %> ago) - <% elsif column_name == 'prominence' %> + <% if column_name == 'prominence' %> <%= h highlight_prominence(value) %> <% elsif column_name == 'allow_new_responses_from' %> <%= h highlight_allow_new_responses_from(value) %> <% else %> - <%= h value %> + <%= admin_value(value)%> <% end %> <% if column_name == 'described_state' %> @@ -248,14 +245,8 @@ <% if column_name == 'params_yaml' %> <%= render :partial => 'params', :locals => { :params => info_request_event.params_diff } %> - <% elsif value.nil? %> - nil - <% elsif %w(text string).include?(type) %> - <%=h value.humanize %> - <% elsif type == 'datetime' %> - <%= admin_date value %> <% else %> - <%=h value %> + <%= admin_value(value) %> <% end %> @@ -340,7 +331,7 @@ <%=incoming_message.id%> -- <%= h(incoming_message.mail_from) %> - at <%=admin_value(incoming_message.sent_at) %> + at <%= admin_date(incoming_message.sent_at) %> <% end %>
<% if !incoming_message.cached_main_body_text_folded.nil? %> diff --git a/app/views/admin_spam_addresses/index.html.erb b/app/views/admin_spam_addresses/index.html.erb index f445e35aad..5365d9f89f 100644 --- a/app/views/admin_spam_addresses/index.html.erb +++ b/app/views/admin_spam_addresses/index.html.erb @@ -17,9 +17,9 @@
<%= form_for(@spam_address, :url => admin_spam_addresses_path, :html => { :class => 'form-inline' }) do |f| -%> - <%= error_messages_for @spam_address %> + <%= foi_error_messages_for :spam_address %> <%= f.text_field :email, :class => 'input-xxlarge', :placeholder => 'Enter email' %> - <%= f.submit 'Add Spam Address', :class => 'btn btn-warning' %> + <%= f.submit 'Add Spam Address', :class => 'btn btn-success' %> <% end -%>
diff --git a/app/views/admin_track/_some_tracks.html.erb b/app/views/admin_track/_some_tracks.html.erb index fdef4f332b..8282fea0d9 100644 --- a/app/views/admin_track/_some_tracks.html.erb +++ b/app/views/admin_track/_some_tracks.html.erb @@ -47,13 +47,10 @@ <%=column.name.humanize%> - <% if column.type.to_s == 'datetime' %> - <%= track_thing.send(column.name).to_s(:db) %> - (<%= time_ago_in_words(track_thing.send(column.name)) %> ago) - <% elsif column.name == 'track_medium' and track_thing.track_medium == 'feed' %> + <% if column.name == 'track_medium' and track_thing.track_medium == 'feed' %> <%= link_to track_thing.track_medium, atom_feed_path(:track_id => track_thing.id) %> <% else %> - <%= h track_thing.send(column.name)%> + <%= admin_value(track_thing.send(column.name)) %> <% end %> diff --git a/app/views/admin_user/_form.html.erb b/app/views/admin_user/_form.html.erb index 523dc9e774..dcb8f0de78 100644 --- a/app/views/admin_user/_form.html.erb +++ b/app/views/admin_user/_form.html.erb @@ -1,4 +1,4 @@ -<%= error_messages_for 'admin_user' %> +<%= foi_error_messages_for :admin_user %> @@ -7,7 +7,8 @@
<%= text_field 'admin_user', 'name', :class => "span3" %>
- will change URL name and break URLs; unlike authorities, there is no history; you will need to rebuild the search index afterwards + will change URL name and break URLs; unlike authorities, there is no + history
diff --git a/app/views/admin_user/_user_table.html.erb b/app/views/admin_user/_user_table.html.erb index f55bb6ba8d..6664a07152 100644 --- a/app/views/admin_user/_user_table.html.erb +++ b/app/views/admin_user/_user_table.html.erb @@ -11,7 +11,7 @@ <%= link_to("(#{ h(user.email) })", "mailto:#{ h(user.email) }") %> - updated <%= I18n.l(user.updated_at, :format => "%e %B %Y %H:%M:%S") %> + updated <%= admin_date(user.updated_at, ago: false) %>
@@ -28,12 +28,7 @@ <%= h name %> - <% if type == 'datetime' %> - <%= value.to_s(:db) %> - (<%= time_ago_in_words(value) %> ago) - <% else %> - <%= h value %> - <% end %> + <%= admin_value(value) %> <% end %> diff --git a/app/views/admin_user/edit.html.erb b/app/views/admin_user/edit.html.erb index f44bf5ec38..e5fac39830 100644 --- a/app/views/admin_user/edit.html.erb +++ b/app/views/admin_user/edit.html.erb @@ -6,7 +6,7 @@ <%= form_tag admin_user_path(@admin_user), :method => 'put', :class => "span12 form form-horizontal" do %> <%= render :partial => 'form' %>
- <%= submit_tag 'Save', :accesskey => 's', :class => "btn btn-primary" %> + <%= submit_tag 'Save', :accesskey => 's', :class => "btn btn-success" %>
<% end %>
@@ -33,12 +33,13 @@ By design, this is extremely difficult to reverse.

-

+

Anonymising the account:

+

Closing the account: +

+

+

1 If the user has any requests, this will create a Censor Rule that applies to the User. The Censor Rule will take the User’s diff --git a/app/views/alaveteli_pro/comment/_note.html.erb b/app/views/alaveteli_pro/comment/_note.html.erb index 840cfaee59..5a14ae47de 100644 --- a/app/views/alaveteli_pro/comment/_note.html.erb +++ b/app/views/alaveteli_pro/comment/_note.html.erb @@ -1 +1,3 @@ - <%= _('Note: When this request is made public on {{site_name}}, your name and annotation will appear in search engines.', :site_name => AlaveteliConfiguration.site_name) %> +<%= _('Note: When this request is made public on {{site_name}}, your name ' \ + 'and annotation will appear in search engines.', + site_name: site_name) %> diff --git a/app/views/alaveteli_pro/comment/_suggestions.html.erb b/app/views/alaveteli_pro/comment/_suggestions.html.erb index e438791ec1..43f5b97f12 100644 --- a/app/views/alaveteli_pro/comment/_suggestions.html.erb +++ b/app/views/alaveteli_pro/comment/_suggestions.html.erb @@ -27,15 +27,15 @@ <% end %> <% if [ 'requires_admin' ].include?(@info_request.described_state) %> -

  • <%= _("Your thoughts on what the {{site_name}} " \ - "administrators should do about the request.", - :site_name=>site_name) %>
  • +
  • <%= _('Your thoughts on what the {{site_name}} ' \ + 'administrators should do about the request.', + site_name: site_name) %>
  • <% end %> <% content_for :public_warning do %> <%= _('When your request is made public on {{site_name}}, any ' \ 'annotations you add will also be public. However, they are ' \ 'not sent to {{public_body_name}}.', - :site_name => AlaveteliConfiguration.site_name, - :public_body_name => h(@info_request.public_body.name)) %> + site_name: site_name, + public_body_name: h(@info_request.public_body.name)) %> <% end %> diff --git a/app/views/alaveteli_pro/dashboard/_projects.html.erb b/app/views/alaveteli_pro/dashboard/_projects.html.erb index e4bf09959f..889e5706a7 100644 --- a/app/views/alaveteli_pro/dashboard/_projects.html.erb +++ b/app/views/alaveteli_pro/dashboard/_projects.html.erb @@ -38,6 +38,7 @@ <% else %> <% end %> + <%= _('Drafts') %> <%= @user.phase_counts['draft'] %> diff --git a/app/views/alaveteli_pro/followups/_embargoed_form_title.html.erb b/app/views/alaveteli_pro/followups/_embargoed_form_title.html.erb index 9fa2cf1fa6..c70e7e9201 100644 --- a/app/views/alaveteli_pro/followups/_embargoed_form_title.html.erb +++ b/app/views/alaveteli_pro/followups/_embargoed_form_title.html.erb @@ -14,6 +14,6 @@ <%= _("Note: When this request is made public on {{site_name}}, this " \ "message will also be made public.", - :site_name => AlaveteliConfiguration.site_name) %> + site_name: site_name) %>

    diff --git a/app/views/alaveteli_pro/info_request_batches/_embargo_info.html.erb b/app/views/alaveteli_pro/info_request_batches/_embargo_info.html.erb index ac4bf85f07..7c82d97bb2 100644 --- a/app/views/alaveteli_pro/info_request_batches/_embargo_info.html.erb +++ b/app/views/alaveteli_pro/info_request_batches/_embargo_info.html.erb @@ -25,7 +25,7 @@ <% if tense == :future %>

    <%= _("Unless you choose a privacy option, requests in this batch " \ "will be public on {{site_name}} immediately.", - site_name: AlaveteliConfiguration.site_name) %>

    + site_name: site_name) %>

    <% else %>

    <%= _("Requests in this batch are public") %>

    <% end %> diff --git a/app/views/alaveteli_pro/info_request_batches/_form.html.erb b/app/views/alaveteli_pro/info_request_batches/_form.html.erb index 0275afe078..2fcf032596 100644 --- a/app/views/alaveteli_pro/info_request_batches/_form.html.erb +++ b/app/views/alaveteli_pro/info_request_batches/_form.html.erb @@ -50,7 +50,7 @@ <%= _("All requests within this batch will remain private for the " \ "same duration. When an individual request is made public all " \ "other requests in this batch will also be published on " \ - "{{site_name}}.", site_name: AlaveteliConfiguration.site_name) %> + "{{site_name}}.", site_name: site_name) %>

    diff --git a/app/views/alaveteli_pro/info_requests/_embargo_info.html.erb b/app/views/alaveteli_pro/info_requests/_embargo_info.html.erb index 0a4fbd2946..dfeec8e75f 100644 --- a/app/views/alaveteli_pro/info_requests/_embargo_info.html.erb +++ b/app/views/alaveteli_pro/info_requests/_embargo_info.html.erb @@ -25,7 +25,7 @@ <% if tense == :future %>

    <%= _("Unless you choose a privacy option, your request will be " \ "public on {{site_name}} immediately.", - site_name: AlaveteliConfiguration.site_name) %>

    + site_name: site_name) %>

    <% else %>

    <%= _("This request is public") %>

    <% end %> diff --git a/app/views/alaveteli_pro/info_requests/_embargo_note.html.erb b/app/views/alaveteli_pro/info_requests/_embargo_note.html.erb index 6f2326d8da..a2b9867b54 100644 --- a/app/views/alaveteli_pro/info_requests/_embargo_note.html.erb +++ b/app/views/alaveteli_pro/info_requests/_embargo_note.html.erb @@ -7,8 +7,8 @@ "delivery to the authority). They will not reveal the " \ "contents of your request or any response you get to anyone else. " \ "The authority may still publish it in a disclosure log as usual.", - site_name: AlaveteliConfiguration.site_name, - pro_site_name: AlaveteliConfiguration.pro_site_name) %> + site_name: site_name, + pro_site_name: pro_site_name) %>

    <%= _("We will remind you by email and on the site when the request " \ diff --git a/app/views/alaveteli_pro/info_requests/_sidebar.html.erb b/app/views/alaveteli_pro/info_requests/_sidebar.html.erb index 41ddebc0fd..c637afcf98 100644 --- a/app/views/alaveteli_pro/info_requests/_sidebar.html.erb +++ b/app/views/alaveteli_pro/info_requests/_sidebar.html.erb @@ -18,6 +18,9 @@ <%= render partial: 'request/citations', locals: { citations: @citations, info_request: @info_request } %> + <%= render partial: 'request/share_by_link', + locals: { info_request: @info_request } %> + <% unless state_transitions_empty?(@state_transitions) %>

    diff --git a/app/views/alaveteli_pro/pages/marketing_roles/campaigners/how_it_works/_embargoes.html.erb b/app/views/alaveteli_pro/pages/marketing_roles/campaigners/how_it_works/_embargoes.html.erb index 9a60205fe9..0480253785 100644 --- a/app/views/alaveteli_pro/pages/marketing_roles/campaigners/how_it_works/_embargoes.html.erb +++ b/app/views/alaveteli_pro/pages/marketing_roles/campaigners/how_it_works/_embargoes.html.erb @@ -5,7 +5,7 @@ <%= _('Pro gives you all of the ease of {{site_name}}, without the worry ' \ 'of releasing all your work before you’re ready for the world to ' \ 'see it.', - site_name: AlaveteliConfiguration.site_name) %> + site_name: site_name) %>

    diff --git a/app/views/alaveteli_pro/pages/marketing_roles/generic/features/_dashboard.html.erb b/app/views/alaveteli_pro/pages/marketing_roles/generic/features/_dashboard.html.erb index 994620ea61..6c1de7ca8e 100644 --- a/app/views/alaveteli_pro/pages/marketing_roles/generic/features/_dashboard.html.erb +++ b/app/views/alaveteli_pro/pages/marketing_roles/generic/features/_dashboard.html.erb @@ -7,6 +7,6 @@

    <%= _('{{pro_site_name}} moves request handling out of your email and ' \ 'onto a dedicated dashboard, keeping everything organised.', - pro_site_name: AlaveteliConfiguration.pro_site_name) %> + pro_site_name: pro_site_name) %>

    diff --git a/app/views/alaveteli_pro/pages/marketing_roles/generic/features/_time.html.erb b/app/views/alaveteli_pro/pages/marketing_roles/generic/features/_time.html.erb index cb8ce40522..05dd011319 100644 --- a/app/views/alaveteli_pro/pages/marketing_roles/generic/features/_time.html.erb +++ b/app/views/alaveteli_pro/pages/marketing_roles/generic/features/_time.html.erb @@ -7,6 +7,6 @@

    <%= _('{{pro_site_name}} does all the heavy lifting, leaving you with ' \ 'more time to focus on your investigation.', - pro_site_name: AlaveteliConfiguration.pro_site_name) %> + pro_site_name: pro_site_name) %>

    diff --git a/app/views/alaveteli_pro/pages/marketing_roles/journalists/_marketing_hero.html.erb b/app/views/alaveteli_pro/pages/marketing_roles/journalists/_marketing_hero.html.erb index 0eb2decf6d..685ec4fbb7 100644 --- a/app/views/alaveteli_pro/pages/marketing_roles/journalists/_marketing_hero.html.erb +++ b/app/views/alaveteli_pro/pages/marketing_roles/journalists/_marketing_hero.html.erb @@ -10,7 +10,7 @@

    <%= _('{{pro_site_name}} is here to help', - pro_site_name: AlaveteliConfiguration.pro_site_name) %> + pro_site_name: pro_site_name) %>

    diff --git a/app/views/alaveteli_pro/pages/marketing_roles/journalists/features/_time.html.erb b/app/views/alaveteli_pro/pages/marketing_roles/journalists/features/_time.html.erb index 4f666d1999..0544beb31b 100644 --- a/app/views/alaveteli_pro/pages/marketing_roles/journalists/features/_time.html.erb +++ b/app/views/alaveteli_pro/pages/marketing_roles/journalists/features/_time.html.erb @@ -7,6 +7,6 @@

    <%= _('{{pro_site_name}} does all the heavy lifting, leaving you with ' \ 'more time to focus on your story.', - pro_site_name: AlaveteliConfiguration.pro_site_name) %> + pro_site_name: pro_site_name) %>

    diff --git a/app/views/alaveteli_pro/pages/marketing_roles/journalists/how_it_works/_embargoes.html.erb b/app/views/alaveteli_pro/pages/marketing_roles/journalists/how_it_works/_embargoes.html.erb index 81ee2b5607..d6f7b2096e 100644 --- a/app/views/alaveteli_pro/pages/marketing_roles/journalists/how_it_works/_embargoes.html.erb +++ b/app/views/alaveteli_pro/pages/marketing_roles/journalists/how_it_works/_embargoes.html.erb @@ -4,7 +4,7 @@

    <%= _('Pro gives you all of the ease of {{site_name}}, without the worry ' \ 'of others snatching your scoop before it’s ready.', - site_name: AlaveteliConfiguration.site_name) %> + site_name: site_name) %>

    diff --git a/app/views/alaveteli_pro/pages/marketing_roles/researchers/_marketing_hero.html.erb b/app/views/alaveteli_pro/pages/marketing_roles/researchers/_marketing_hero.html.erb index 7058fa5da1..5bc1b8b9c9 100644 --- a/app/views/alaveteli_pro/pages/marketing_roles/researchers/_marketing_hero.html.erb +++ b/app/views/alaveteli_pro/pages/marketing_roles/researchers/_marketing_hero.html.erb @@ -10,7 +10,7 @@

    <%= _('{{pro_site_name}} does the hard work for you', - pro_site_name: AlaveteliConfiguration.pro_site_name) %> + pro_site_name: pro_site_name) %>

    diff --git a/app/views/alaveteli_pro/pages/marketing_roles/researchers/features/_time.html.erb b/app/views/alaveteli_pro/pages/marketing_roles/researchers/features/_time.html.erb index 4161e0b444..97ba14eeb9 100644 --- a/app/views/alaveteli_pro/pages/marketing_roles/researchers/features/_time.html.erb +++ b/app/views/alaveteli_pro/pages/marketing_roles/researchers/features/_time.html.erb @@ -7,6 +7,6 @@

    <%= _('{{pro_site_name}} does all the heavy lifting, leaving you with ' \ 'more time to focus on your research.', - pro_site_name: AlaveteliConfiguration.pro_site_name) %> + pro_site_name: pro_site_name) %>

    diff --git a/app/views/alaveteli_pro/pages/marketing_roles/researchers/how_it_works/_embargoes.html.erb b/app/views/alaveteli_pro/pages/marketing_roles/researchers/how_it_works/_embargoes.html.erb index a0abce18db..b64767f320 100644 --- a/app/views/alaveteli_pro/pages/marketing_roles/researchers/how_it_works/_embargoes.html.erb +++ b/app/views/alaveteli_pro/pages/marketing_roles/researchers/how_it_works/_embargoes.html.erb @@ -5,7 +5,7 @@ <%= _('Pro gives you all of the ease of {{site_name}}, without the worry ' \ 'of releasing all your work before you’re ready for the world to ' \ 'see it.', - site_name: AlaveteliConfiguration.site_name) %> + site_name: site_name) %>

    diff --git a/app/views/alaveteli_pro/subscriptions/_cancel_subscription.html.erb b/app/views/alaveteli_pro/subscriptions/_cancel_subscription.html.erb index 8b887cbdc6..9dcf283dcf 100644 --- a/app/views/alaveteli_pro/subscriptions/_cancel_subscription.html.erb +++ b/app/views/alaveteli_pro/subscriptions/_cancel_subscription.html.erb @@ -5,7 +5,7 @@

    <%= _('Your subscription has been cancelled, your access to ' \ '{{pro_site_name}} will end on {{date}}.', - pro_site_name: AlaveteliConfiguration.pro_site_name, + pro_site_name: pro_site_name, date: simple_date(Time.zone.at(subscription.current_period_end), format: :text)) %>