diff --git a/.rubocop.yml b/.rubocop.yml new file mode 100644 index 0000000..a893304 --- /dev/null +++ b/.rubocop.yml @@ -0,0 +1,103 @@ +inherit_from: .rubocop_todo.yml + +AllCops: + TargetRubyVersion: 2.3 + TargetRailsVersion: 5.2 + + Exclude: + - '**/vendor/**/*' + - '**/tmp/**/*' + - '**/bin/**/*' + - '**/plugins/**/*' + - '**/extra/**/*' + - '**/lib/generators/**/templates/*' + - '**/lib/tasks/**/*' + - '**/files/**/*' + - 'db/schema.rb' + +# Enable extensions + +require: + - rubocop-performance + - rubocop-rails + +# Rules for Redmine + +Bundler/OrderedGems: + Enabled: false + +Layout/EmptyLineBetweenDefs: + AllowAdjacentOneLineDefs: true + +Layout/SpaceBeforeBlockBraces: + # "space" is used more than "no_space". + # But "no_space" is more natural in one liner. + # str = path.to_s.split(%r{[/\\]}).select{|p| !p.blank?}.join("/") + Enabled: false + +# You can see results by "rubocop --only Layout/SpaceInsideBlockBraces" +Layout/SpaceInsideBlockBraces: + EnforcedStyle: no_space + SpaceBeforeBlockParameters: false + +# You can see results by "rubocop --only Layout/SpaceInsideHashLiteralBraces" +Layout/SpaceInsideHashLiteralBraces: + EnforcedStyle: no_space + +Layout/TrailingWhitespace: + AllowInHeredoc: true + +Lint/HandleExceptions: + AllowComments: true + +Metrics: + Enabled: false + +Naming/AccessorMethodName: + Enabled: false + +Naming/BinaryOperatorParameterName: + Enabled: false + +Naming/PredicateName: + Enabled: false + +Rails/BulkChangeTable: + Exclude: + - 'db/migrate/20120714122200_add_workflows_rule_fields.rb' + - 'db/migrate/20131214094309_remove_custom_fields_min_max_length_default_values.rb' + +Rails/HelperInstanceVariable: + Enabled: false + +# Configuration parameters: AllowedChars. +Style/AsciiComments: + Exclude: + # Copyright credit has non ascii character. + # We can not change nor remove it. + - 'app/models/repository/git.rb' + +Style/FormatStringToken: + Enabled: false + +Style/FrozenStringLiteralComment: + Enabled: true + EnforcedStyle: always + Exclude: + - 'db/**/*.rb' + - 'Gemfile' + - 'Rakefile' + - 'config.ru' + - 'config/additional_environment.rb' + +Style/HashSyntax: + Enabled: true + EnforcedStyle: no_mixed_keys + +Style/IdenticalConditionalBranches: + Exclude: + - 'config/initializers/10-patches.rb' + - 'lib/redmine/wiki_formatting/textile/redcloth3.rb' + +Style/TrailingCommaInArrayLiteral: + Enabled: false diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml new file mode 100644 index 0000000..39fd8a0 --- /dev/null +++ b/.rubocop_todo.yml @@ -0,0 +1,1637 @@ +# This configuration was generated by +# `rubocop --auto-gen-config --exclude-limit 20 --no-offense-counts --no-auto-gen-timestamp` +# using RuboCop version 0.76.0. +# The point is for the user to remove these configuration records +# one by one as the offenses are removed from the code base. +# Note that changes in the inspected code, or installation of new +# versions of RuboCop, may require this file to be generated again. + +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, IndentationWidth. +# SupportedStyles: with_first_argument, with_fixed_indentation +Layout/AlignArguments: + Exclude: + - 'config/application.rb' + - 'lib/redmine/scm/adapters/bazaar_adapter.rb' + - 'lib/redmine/scm/adapters/subversion_adapter.rb' + - 'test/functional/journals_controller_test.rb' + - 'test/functional/my_controller_test.rb' + - 'test/functional/roles_controller_test.rb' + - 'test/integration/api_test/attachments_test.rb' + - 'test/integration/api_test/groups_test.rb' + - 'test/integration/api_test/issue_relations_test.rb' + - 'test/integration/api_test/memberships_test.rb' + - 'test/integration/api_test/projects_test.rb' + - 'test/integration/api_test/wiki_pages_test.rb' + - 'test/test_helper.rb' + - 'test/unit/changeset_test.rb' + - 'test/unit/lib/redmine/field_format/version_field_format_test.rb' + - 'test/unit/lib/redmine/wiki_formatting/html_parser_test.rb' + - 'test/unit/lib/redmine/wiki_formatting/markdown_formatter_test.rb' + - 'test/unit/lib/redmine/wiki_formatting/markdown_html_parser_test.rb' + - 'test/unit/lib/redmine/wiki_formatting/textile_formatter_test.rb' + - 'test/unit/lib/redmine/wiki_formatting/textile_html_parser_test.rb' + +# Cop supports --auto-correct. +# Configuration parameters: AllowMultipleStyles, EnforcedHashRocketStyle, EnforcedColonStyle, EnforcedLastArgumentHashStyle. +# SupportedHashRocketStyles: key, separator, table +# SupportedColonStyles: key, separator, table +# SupportedLastArgumentHashStyles: always_inspect, always_ignore, ignore_implicit, ignore_explicit +Layout/AlignHash: + Enabled: false + +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, IndentOneStep, IndentationWidth. +# SupportedStyles: case, end +Layout/CaseIndentation: + Exclude: + - 'config/initializers/10-patches.rb' + +# Cop supports --auto-correct. +Layout/ClosingHeredocIndentation: + Exclude: + - 'test/unit/lib/redmine/wiki_formatting/textile_formatter_test.rb' + - 'test/unit/lib/redmine/wiki_formatting_test.rb' + +# Cop supports --auto-correct. +Layout/ClosingParenthesisIndentation: + Enabled: false + +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle. +# SupportedStyles: leading, trailing +Layout/DotPosition: + Enabled: false + +# Cop supports --auto-correct. +Layout/EmptyLineAfterGuardClause: + Enabled: false + +# Cop supports --auto-correct. +Layout/EmptyLines: + Exclude: + - 'config/routes.rb' + - 'db/migrate/001_setup.rb' + - 'lib/redmine/sudo_mode.rb' + - 'test/helpers/application_helper_test.rb' + +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle. +# SupportedStyles: around, only_before +Layout/EmptyLinesAroundAccessModifier: + Exclude: + - 'config/initializers/10-patches.rb' + +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle. +# SupportedStyles: empty_lines, empty_lines_except_namespace, empty_lines_special, no_empty_lines, beginning_only, ending_only +Layout/EmptyLinesAroundClassBody: + Enabled: false + +# Cop supports --auto-correct. +Layout/EmptyLinesAroundExceptionHandlingKeywords: + Exclude: + - 'app/controllers/activities_controller.rb' + - 'app/controllers/imports_controller.rb' + - 'app/controllers/wiki_controller.rb' + - 'test/functional/settings_controller_test.rb' + +# Cop supports --auto-correct. +Layout/EmptyLinesAroundMethodBody: + Exclude: + - 'app/models/query.rb' + - 'db/migrate/001_setup.rb' + - 'db/migrate/20111201201315_add_unique_index_to_issue_relations.rb' + - 'test/functional/journals_controller_test.rb' + - 'test/functional/users_controller_test.rb' + - 'test/integration/account_test.rb' + - 'test/unit/attachment_test.rb' + - 'test/unit/lib/redmine/menu_manager/mapper_test.rb' + - 'test/unit/lib/redmine/menu_manager/menu_helper_test.rb' + - 'test/unit/lib/redmine/unified_diff_test.rb' + +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle. +# SupportedStyles: empty_lines, empty_lines_except_namespace, empty_lines_special, no_empty_lines +Layout/EmptyLinesAroundModuleBody: + Enabled: false + +# Cop supports --auto-correct. +# Configuration parameters: AllowForAlignment, AllowBeforeTrailingComments, ForceEqualSignAlignment. +Layout/ExtraSpacing: + Enabled: false + +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, IndentationWidth. +# SupportedStyles: consistent, consistent_relative_to_receiver, special_for_inner_method_call, special_for_inner_method_call_in_parentheses +Layout/IndentFirstArgument: + Enabled: false + +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, IndentationWidth. +# SupportedStyles: special_inside_parentheses, consistent, align_brackets +Layout/IndentFirstArrayElement: + Exclude: + - 'app/models/setting.rb' + - 'lib/redmine/nested_set/issue_nested_set.rb' + - 'lib/redmine/nested_set/project_nested_set.rb' + - 'lib/redmine/wiki_formatting/textile/redcloth3.rb' + - 'test/functional/auth_sources_controller_test.rb' + - 'test/functional/repositories_mercurial_controller_test.rb' + - 'test/functional/settings_controller_test.rb' + - 'test/helpers/activities_helper_test.rb' + - 'test/helpers/issues_helper_test.rb' + - 'test/unit/issue_test.rb' + - 'test/unit/lib/redmine/scm/adapters/git_adapter_test.rb' + - 'test/unit/lib/redmine/scm/adapters/mercurial_adapter_test.rb' + - 'test/unit/project_test.rb' + - 'test/unit/repository_git_test.rb' + - 'test/unit/version_test.rb' + +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, IndentationWidth. +# SupportedStyles: special_inside_parentheses, consistent, align_braces +Layout/IndentFirstHashElement: + Enabled: false + +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle. +# SupportedStyles: squiggly, active_support, powerpack, unindent +Layout/IndentHeredoc: + Exclude: + - 'test/unit/lib/redmine/wiki_formatting/textile_formatter_test.rb' + - 'test/unit/lib/redmine/wiki_formatting_test.rb' + +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle. +# SupportedStyles: normal, indented_internal_methods +Layout/IndentationConsistency: + Exclude: + - 'test/mocks/open_id_authentication_mock.rb' + +# Cop supports --auto-correct. +# Configuration parameters: Width, IgnoredPatterns. +Layout/IndentationWidth: + Enabled: false + +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle. +# SupportedStyles: symmetrical, new_line, same_line +Layout/MultilineArrayBraceLayout: + Exclude: + - 'app/helpers/settings_helper.rb' + - 'app/helpers/users_helper.rb' + +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle. +# SupportedStyles: symmetrical, new_line, same_line +Layout/MultilineHashBraceLayout: + Enabled: false + +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle. +# SupportedStyles: symmetrical, new_line, same_line +Layout/MultilineMethodCallBraceLayout: + Enabled: false + +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, IndentationWidth. +# SupportedStyles: aligned, indented, indented_relative_to_receiver +Layout/MultilineMethodCallIndentation: + Enabled: false + +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, IndentationWidth. +# SupportedStyles: aligned, indented +Layout/MultilineOperationIndentation: + Enabled: false + +# Cop supports --auto-correct. +Layout/RescueEnsureAlignment: + Exclude: + - 'db/migrate/085_add_role_tracker_old_status_index_to_workflows.rb' + +# Cop supports --auto-correct. +Layout/SpaceAfterComma: + Enabled: false + +# Cop supports --auto-correct. +Layout/SpaceAfterNot: + Exclude: + - 'app/controllers/application_controller.rb' + - 'app/models/repository/cvs.rb' + - 'app/models/repository/git.rb' + - 'lib/redmine/codeset_util.rb' + - 'test/functional/account_controller_test.rb' + - 'test/functional/documents_controller_test.rb' + - 'test/functional/issues_controller_test.rb' + - 'test/functional/repositories_git_controller_test.rb' + - 'test/functional/timelog_controller_test.rb' + - 'test/functional/workflows_controller_test.rb' + - 'test/helpers/application_helper_test.rb' + - 'test/unit/attachment_test.rb' + - 'test/unit/lib/redmine/export/pdf_test.rb' + - 'test/unit/project_copy_test.rb' + - 'test/unit/project_test.rb' + - 'test/unit/query_test.rb' + - 'test/unit/role_test.rb' + - 'test/unit/user_test.rb' + +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle. +# SupportedStyles: space, no_space +Layout/SpaceAroundEqualsInParameterDefault: + Enabled: false + +# Cop supports --auto-correct. +# Configuration parameters: AllowForAlignment. +Layout/SpaceAroundOperators: + Enabled: false + +# Cop supports --auto-correct. +Layout/SpaceBeforeComma: + Exclude: + - 'test/mocks/open_id_authentication_mock.rb' + +# Cop supports --auto-correct. +# Configuration parameters: AllowForAlignment. +Layout/SpaceBeforeFirstArg: + Exclude: + - 'test/helpers/issues_helper_test.rb' + - 'test/helpers/search_helper_test.rb' + - 'test/unit/lib/redmine/menu_manager_test.rb' + +# Cop supports --auto-correct. +Layout/SpaceBeforeSemicolon: + Exclude: + - 'app/models/user_preference.rb' + - 'config/initializers/10-patches.rb' + +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle. +# SupportedStyles: require_no_space, require_space +Layout/SpaceInLambdaLiteral: + Exclude: + - 'app/helpers/application_helper.rb' + +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBrackets. +# SupportedStyles: space, no_space, compact +# SupportedStylesForEmptyBrackets: space, no_space +Layout/SpaceInsideArrayLiteralBrackets: + Exclude: + - 'app/controllers/projects_controller.rb' + - 'app/helpers/timelog_helper.rb' + - 'app/models/query.rb' + - 'lib/diff.rb' + - 'lib/redmine/i18n.rb' + - 'test/mocks/open_id_authentication_mock.rb' + - 'test/unit/mailer_localisation_test.rb' + - 'test/unit/mailer_test.rb' + - 'test/unit/search_test.rb' + +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBraces, SpaceBeforeBlockParameters. +# SupportedStyles: space, no_space +# SupportedStylesForEmptyBraces: space, no_space +Layout/SpaceInsideBlockBraces: + Enabled: false + +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBraces. +# SupportedStyles: space, no_space, compact +# SupportedStylesForEmptyBraces: space, no_space +Layout/SpaceInsideHashLiteralBraces: + Enabled: false + +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle. +# SupportedStyles: space, no_space +Layout/SpaceInsideParens: + Exclude: + - 'app/controllers/application_controller.rb' + - 'app/helpers/issues_helper.rb' + - 'app/models/auth_source_ldap.rb' + - 'app/models/repository/cvs.rb' + - 'lib/redmine/scm/adapters/abstract_adapter.rb' + - 'lib/redmine/scm/adapters/cvs_adapter.rb' + - 'lib/redmine/scm/adapters/git_adapter.rb' + - 'lib/redmine/sudo_mode.rb' + - 'lib/redmine/wiki_formatting/textile/formatter.rb' + - 'lib/redmine/wiki_formatting/textile/redcloth3.rb' + - 'test/helpers/application_helper_test.rb' + - 'test/unit/changeset_test.rb' + - 'test/unit/issue_subtasking_test.rb' + - 'test/unit/lib/redmine/export/pdf_test.rb' + - 'test/unit/lib/redmine/wiki_formatting/textile_formatter_test.rb' + - 'test/unit/query_test.rb' + - 'test/unit/repository_test.rb' + - 'test/unit/user_test.rb' + +# Cop supports --auto-correct. +Layout/SpaceInsidePercentLiteralDelimiters: + Exclude: + - 'app/models/project.rb' + +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle. +# SupportedStyles: space, no_space +Layout/SpaceInsideStringInterpolation: + Exclude: + - 'app/helpers/application_helper.rb' + - 'app/helpers/workflows_helper.rb' + - 'app/models/issue_query.rb' + - 'app/models/mail_handler.rb' + - 'lib/redmine/core_ext/string/inflections.rb' + - 'test/integration/admin_test.rb' + +Lint/AmbiguousBlockAssociation: + Enabled: false + +Lint/AmbiguousOperator: + Exclude: + - 'app/controllers/application_controller.rb' + - 'app/helpers/application_helper.rb' + - 'test/unit/custom_field_test.rb' + - 'test/unit/member_test.rb' + +Lint/AmbiguousRegexpLiteral: + Enabled: false + +# Configuration parameters: AllowSafeAssignment. +Lint/AssignmentInCondition: + Enabled: false + +# Cop supports --auto-correct. +Lint/DeprecatedClassMethods: + Exclude: + - 'Gemfile' + - 'app/models/import.rb' + - 'config/application.rb' + - 'config/boot.rb' + - 'config/routes.rb' + - 'lib/redmine/scm/adapters/abstract_adapter.rb' + - 'lib/redmine/thumbnail.rb' + - 'lib/redmine/utils.rb' + - 'test/functional/issues_controller_test.rb' + - 'test/unit/attachment_test.rb' + - 'test/unit/issue_import_test.rb' + +Lint/EmptyWhen: + Exclude: + - 'app/controllers/issues_controller.rb' + - 'app/controllers/wiki_controller.rb' + +# Configuration parameters: AllowComments. +Lint/HandleExceptions: + Exclude: + - 'app/controllers/activities_controller.rb' + - 'app/models/import.rb' + - 'db/migrate/085_add_role_tracker_old_status_index_to_workflows.rb' + - 'lib/redmine/scm/adapters/cvs_adapter.rb' + - 'lib/redmine/scm/adapters/subversion_adapter.rb' + +Lint/IneffectiveAccessModifier: + Exclude: + - 'app/models/auth_source_ldap.rb' + +Lint/InterpolationCheck: + Exclude: + - 'app/models/user.rb' + +# Cop supports --auto-correct. +Lint/LiteralInInterpolation: + Exclude: + - 'test/unit/mail_handler_test.rb' + +Lint/Loop: + Exclude: + - 'lib/redmine/helpers/gantt.rb' + +Lint/ParenthesesAsGroupedExpression: + Exclude: + - 'lib/redmine/field_format.rb' + - 'test/functional/account_controller_test.rb' + - 'test/functional/email_addresses_controller_test.rb' + - 'test/functional/my_controller_test.rb' + - 'test/functional/settings_controller_test.rb' + - 'test/functional/users_controller_test.rb' + - 'test/helpers/application_helper_test.rb' + - 'test/unit/attachment_test.rb' + - 'test/unit/lib/redmine/export/pdf_test.rb' + +# Cop supports --auto-correct. +Lint/SendWithMixinArgument: + Exclude: + - 'lib/redmine/acts/positioned.rb' + - 'test/object_helpers.rb' + +Lint/ShadowingOuterLocalVariable: + Enabled: false + +# Cop supports --auto-correct. +Lint/StringConversionInInterpolation: + Exclude: + - 'lib/redmine/export/pdf/issues_pdf_helper.rb' + - 'test/functional/repositories_filesystem_controller_test.rb' + - 'test/functional/repositories_git_controller_test.rb' + - 'test/functional/repositories_mercurial_controller_test.rb' + +# Cop supports --auto-correct. +Lint/UnifiedInteger: + Exclude: + - 'test/unit/query_test.rb' + +# Cop supports --auto-correct. +# Configuration parameters: IgnoreEmptyBlocks, AllowUnusedKeywordArguments. +Lint/UnusedBlockArgument: + Enabled: false + +# Cop supports --auto-correct. +# Configuration parameters: AllowUnusedKeywordArguments, IgnoreEmptyMethods. +Lint/UnusedMethodArgument: + Enabled: false + +Lint/UriEscapeUnescape: + Exclude: + - 'lib/redmine/field_format.rb' + - 'lib/redmine/scm/adapters/subversion_adapter.rb' + - 'test/functional/wiki_controller_test.rb' + +Lint/UselessAssignment: + Enabled: false + +# Configuration parameters: CheckForMethodsWithNoSideEffects. +Lint/Void: + Exclude: + - 'app/models/query.rb' + - 'app/models/time_entry.rb' + - 'app/models/wiki_content_version.rb' + - 'lib/redmine/unified_diff.rb' + +Naming/ConstantName: + Exclude: + - 'app/models/document_category.rb' + - 'app/models/issue_priority.rb' + - 'app/models/time_entry_activity.rb' + - 'lib/redmine/helpers/gantt.rb' + +# Configuration parameters: ExpectMatchingDefinition, Regex, IgnoreExecutableScripts, AllowedAcronyms. +# AllowedAcronyms: CLI, DSL, ACL, API, ASCII, CPU, CSS, DNS, EOF, GUID, HTML, HTTP, HTTPS, ID, IP, JSON, LHS, QPS, RAM, RHS, RPC, SLA, SMTP, SQL, SSH, TCP, TLS, TTL, UDP, UI, UID, UUID, URI, URL, UTF8, VM, XML, XMPP, XSRF, XSS +Naming/FileName: + Exclude: + - 'config/initializers/00-core_plugins.rb' + - 'config/initializers/10-patches.rb' + - 'config/initializers/20-mime_types.rb' + - 'config/initializers/30-redmine.rb' + +# Configuration parameters: EnforcedStyleForLeadingUnderscores. +# SupportedStylesForLeadingUnderscores: disallowed, required, optional +Naming/MemoizedInstanceVariableName: + Exclude: + - 'app/controllers/email_addresses_controller.rb' + - 'app/controllers/users_controller.rb' + - 'app/controllers/workflows_controller.rb' + - 'app/models/issue.rb' + - 'app/models/project.rb' + - 'app/models/query.rb' + - 'app/models/role.rb' + - 'lib/redmine/field_format.rb' + - 'lib/redmine/helpers/calendar.rb' + - 'lib/redmine/search.rb' + +# Configuration parameters: EnforcedStyle, IgnoredPatterns. +# SupportedStyles: snake_case, camelCase +Naming/MethodName: + Exclude: + - 'lib/redmine/export/pdf.rb' + - 'lib/redmine/scm/adapters/cvs_adapter.rb' + - 'lib/redmine/wiki_formatting/textile/redcloth3.rb' + - 'test/helpers/application_helper_test.rb' + +# Cop supports --auto-correct. +# Configuration parameters: PreferredName. +Naming/RescuedExceptionsVariableName: + Exclude: + - 'app/models/user.rb' + - 'lib/redmine/scm/adapters/abstract_adapter.rb' + - 'lib/redmine/scm/adapters/filesystem_adapter.rb' + +# Configuration parameters: MinNameLength, AllowNamesEndingInNumbers, AllowedNames, ForbiddenNames. +# AllowedNames: io, id, to, by, on, in, at, ip, db, os +Naming/UncommunicativeMethodParamName: + Enabled: false + +# Configuration parameters: EnforcedStyle. +# SupportedStyles: snake_case, camelCase +Naming/VariableName: + Exclude: + - 'lib/redmine/scm/adapters/cvs_adapter.rb' + - 'lib/redmine/wiki_formatting/textile/redcloth3.rb' + +# Configuration parameters: EnforcedStyle. +# SupportedStyles: snake_case, normalcase, non_integer +Naming/VariableNumber: + Exclude: + - 'test/functional/versions_controller_test.rb' + - 'test/helpers/application_helper_test.rb' + - 'test/unit/lib/redmine/export/pdf_test.rb' + - 'test/unit/lib/redmine/scm/adapters/git_adapter_test.rb' + - 'test/unit/lib/redmine/scm/adapters/mercurial_adapter_test.rb' + - 'test/unit/project_test.rb' + +Performance/FixedSize: + Exclude: + - 'test/integration/api_test/issues_test.rb' + - 'test/integration/attachments_test.rb' + +# Cop supports --auto-correct. +Performance/RedundantMatch: + Exclude: + - 'app/models/issue_relation.rb' + - 'lib/redmine/wiki_formatting/textile/formatter.rb' + - 'lib/redmine/wiki_formatting/textile/redcloth3.rb' + +# Cop supports --auto-correct. +# Configuration parameters: MaxKeyValuePairs. +Performance/RedundantMerge: + Exclude: + - 'app/controllers/issues_controller.rb' + - 'app/helpers/application_helper.rb' + - 'app/helpers/avatars_helper.rb' + - 'app/helpers/custom_fields_helper.rb' + - 'app/helpers/workflows_helper.rb' + - 'app/models/auth_source_ldap.rb' + - 'app/models/principal.rb' + - 'lib/redmine/access_control.rb' + - 'test/functional/imports_controller_test.rb' + - 'test/unit/issue_import_test.rb' + - 'test/unit/time_entry_import_test.rb' + +# Cop supports --auto-correct. +Performance/StringReplacement: + Exclude: + - 'app/helpers/application_helper.rb' + - 'app/helpers/imports_helper.rb' + - 'app/helpers/settings_helper.rb' + - 'lib/redmine/core_ext/string/conversions.rb' + - 'lib/redmine/wiki_formatting/textile/redcloth3.rb' + - 'test/functional/repositories_cvs_controller_test.rb' + - 'test/functional/repositories_git_controller_test.rb' + - 'test/helpers/application_helper_test.rb' + - 'test/integration/repositories_git_test.rb' + - 'test/unit/lib/redmine/scm/adapters/cvs_adapter_test.rb' + - 'test/unit/lib/redmine/scm/adapters/mercurial_adapter_test.rb' + - 'test/unit/repository_cvs_test.rb' + - 'test/unit/repository_git_test.rb' + +# Configuration parameters: Include. +# Include: app/models/**/*.rb +Rails/ActiveRecordOverride: + Exclude: + - 'app/models/email_address.rb' + - 'app/models/issue.rb' + - 'app/models/journal.rb' + - 'app/models/member.rb' + +# Cop supports --auto-correct. +Rails/ApplicationRecord: + Enabled: false + +# Cop supports --auto-correct. +# Configuration parameters: Include. +# Include: **/test/**/* +Rails/AssertNot: + Enabled: false + +# Cop supports --auto-correct. +# Configuration parameters: NilOrEmpty, NotPresent, UnlessPresent. +Rails/Blank: + Exclude: + - 'app/controllers/application_controller.rb' + - 'app/controllers/context_menus_controller.rb' + - 'app/controllers/repositories_controller.rb' + - 'app/controllers/watchers_controller.rb' + - 'app/helpers/queries_helper.rb' + - 'app/helpers/repositories_helper.rb' + - 'app/models/mailer.rb' + - 'app/models/query.rb' + - 'app/models/repository/git.rb' + - 'app/models/role.rb' + - 'lib/redmine/field_format.rb' + - 'lib/redmine/wiki_formatting/macros.rb' + +# Configuration parameters: Include. +# Include: db/migrate/*.rb +Rails/CreateTableWithTimestamps: + Enabled: false + +# Configuration parameters: EnforcedStyle. +# SupportedStyles: strict, flexible +Rails/Date: + Enabled: false + +# Cop supports --auto-correct. +# Configuration parameters: EnforceForPrefixed. +Rails/Delegate: + Exclude: + - 'app/models/changeset.rb' + - 'app/models/custom_field.rb' + - 'app/models/custom_field_enumeration.rb' + - 'app/models/custom_field_value.rb' + - 'app/models/custom_value.rb' + - 'app/models/group.rb' + - 'app/models/message.rb' + - 'app/models/repository.rb' + - 'app/models/repository/git.rb' + - 'app/models/time_entry_activity.rb' + - 'app/models/wiki_content.rb' + - 'app/models/wiki_content_version.rb' + - 'lib/redmine/menu_manager.rb' + +# Cop supports --auto-correct. +# Configuration parameters: Whitelist. +# Whitelist: find_by_sql +Rails/DynamicFindBy: + Enabled: false + +# Configuration parameters: Include. +# Include: app/**/*.rb, config/**/*.rb, lib/**/*.rb +Rails/Exit: + Exclude: + - 'lib/**/*.rake' + - 'config/environment.rb' + - 'config/initializers/10-patches.rb' + - 'config/routes.rb' + +# Configuration parameters: EnforcedStyle. +# SupportedStyles: slashes, arguments +Rails/FilePath: + Enabled: false + +# Cop supports --auto-correct. +# Configuration parameters: Include. +# Include: app/models/**/*.rb +Rails/FindBy: + Exclude: + - 'app/models/attachment.rb' + - 'app/models/enumeration.rb' + - 'app/models/issue_import.rb' + - 'app/models/repository.rb' + - 'app/models/repository/cvs.rb' + - 'app/models/repository/git.rb' + - 'app/models/repository/mercurial.rb' + - 'app/models/time_entry_query.rb' + - 'app/models/user.rb' + - 'app/models/wiki.rb' + - 'app/models/wiki_content_version.rb' + +# Cop supports --auto-correct. +# Configuration parameters: Include. +# Include: app/models/**/*.rb +Rails/FindEach: + Exclude: + - 'app/models/auth_source.rb' + - 'app/models/group.rb' + - 'app/models/issue.rb' + - 'app/models/issue_status.rb' + - 'app/models/project.rb' + - 'app/models/query.rb' + - 'app/models/repository.rb' + - 'app/models/setting.rb' + - 'app/models/watcher.rb' + - 'app/models/wiki_page.rb' + +# Configuration parameters: Include. +# Include: app/models/**/*.rb +Rails/HasAndBelongsToMany: + Exclude: + - 'app/models/changeset.rb' + - 'app/models/custom_field.rb' + - 'app/models/group.rb' + - 'app/models/issue.rb' + - 'app/models/issue_custom_field.rb' + - 'app/models/project.rb' + - 'app/models/query.rb' + - 'app/models/role.rb' + - 'app/models/tracker.rb' + - 'app/models/user.rb' + +# Configuration parameters: Include. +# Include: app/models/**/*.rb +Rails/HasManyOrHasOneDependent: + Exclude: + - 'app/models/auth_source.rb' + - 'app/models/document_category.rb' + - 'app/models/issue_priority.rb' + - 'app/models/issue_status.rb' + - 'app/models/project.rb' + - 'app/models/time_entry_activity.rb' + - 'app/models/tracker.rb' + - 'app/models/wiki.rb' + +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle. +# SupportedStyles: numeric, symbolic +Rails/HttpStatus: + Exclude: + - 'app/controllers/mail_handler_controller.rb' + - 'app/controllers/sys_controller.rb' + - 'app/controllers/watchers_controller.rb' + +# Configuration parameters: Include. +# Include: app/models/**/*.rb +Rails/InverseOf: + Exclude: + - 'app/models/board.rb' + - 'app/models/custom_field.rb' + - 'app/models/document_category.rb' + - 'app/models/issue.rb' + - 'app/models/issue_category.rb' + - 'app/models/issue_priority.rb' + - 'app/models/issue_status.rb' + - 'app/models/journal.rb' + - 'app/models/member.rb' + - 'app/models/news.rb' + - 'app/models/principal.rb' + - 'app/models/project.rb' + - 'app/models/repository.rb' + - 'app/models/repository/mercurial.rb' + - 'app/models/time_entry_activity.rb' + - 'app/models/user.rb' + - 'app/models/version.rb' + - 'app/models/wiki.rb' + - 'app/models/wiki_page.rb' + +# Configuration parameters: Include. +# Include: app/controllers/**/*.rb +Rails/LexicallyScopedActionFilter: + Exclude: + - 'app/controllers/projects_controller.rb' + +# Cop supports --auto-correct. +Rails/LinkToBlank: + Exclude: + - 'app/helpers/avatars_helper.rb' + +# Configuration parameters: Include. +# Include: app/**/*.rb, config/**/*.rb, db/**/*.rb, lib/**/*.rb +Rails/Output: + Exclude: + - 'app/models/mail_handler.rb' + - 'config/routes.rb' + - 'lib/redmine/unified_diff.rb' + +Rails/OutputSafety: + Enabled: false + +# Cop supports --auto-correct. +Rails/PluralizationGrammar: + Exclude: + - 'lib/redmine/helpers/time_report.rb' + - 'test/functional/gantts_controller_test.rb' + - 'test/unit/mailer_test.rb' + - 'test/unit/query_test.rb' + +# Cop supports --auto-correct. +Rails/Presence: + Exclude: + - 'app/controllers/activities_controller.rb' + - 'app/helpers/application_helper.rb' + - 'app/models/repository.rb' + - 'lib/redmine/codeset_util.rb' + - 'lib/redmine/i18n.rb' + - 'lib/redmine/scm/adapters/abstract_adapter.rb' + - 'lib/redmine/scm/adapters/filesystem_adapter.rb' + - 'lib/redmine/scm/adapters/git_adapter.rb' + - 'lib/redmine/scm/adapters/mercurial_adapter.rb' + - 'lib/redmine/sort_criteria.rb' + +# Cop supports --auto-correct. +# Configuration parameters: NotNilAndNotEmpty, NotBlank, UnlessBlank. +Rails/Present: + Enabled: false + +# Cop supports --auto-correct. +# Configuration parameters: Include. +# Include: app/models/**/*.rb +Rails/ReadWriteAttribute: + Exclude: + - 'app/models/attachment.rb' + - 'app/models/auth_source_ldap.rb' + - 'app/models/changeset.rb' + - 'app/models/custom_field.rb' + - 'app/models/email_address.rb' + - 'app/models/issue.rb' + - 'app/models/journal_detail.rb' + - 'app/models/message.rb' + - 'app/models/query.rb' + - 'app/models/repository.rb' + - 'app/models/repository/git.rb' + - 'app/models/role.rb' + - 'app/models/setting.rb' + - 'app/models/time_entry.rb' + - 'app/models/user.rb' + - 'app/models/user_preference.rb' + - 'app/models/wiki_page.rb' + +Rails/ReflectionClassName: + Exclude: + - 'lib/redmine/nested_set/issue_nested_set.rb' + - 'lib/redmine/nested_set/project_nested_set.rb' + +# Cop supports --auto-correct. +# Configuration parameters: Include. +# Include: **/test/**/* +Rails/RefuteMethods: + Exclude: + - 'test/functional/projects_controller_test.rb' + - 'test/functional/wiki_controller_test.rb' + - 'test/integration/account_test.rb' + - 'test/integration/api_test/my_test.rb' + - 'test/unit/attachment_test.rb' + - 'test/unit/custom_field_test.rb' + - 'test/unit/lib/redmine/project_jump_box_test.rb' + - 'test/unit/mail_handler_test.rb' + +# Configuration parameters: Blacklist, Whitelist. +# Blacklist: decrement!, decrement_counter, increment!, increment_counter, toggle!, touch, update_all, update_attribute, update_column, update_columns, update_counters +Rails/SkipsModelValidations: + Enabled: false + +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle. +# SupportedStyles: strict, flexible +Rails/TimeZone: + Enabled: false + +# Cop supports --auto-correct. +# Configuration parameters: Include. +# Include: app/models/**/*.rb +Rails/Validation: + Enabled: false + +Security/Eval: + Exclude: + - 'app/helpers/attachments_helper.rb' + - 'app/models/user.rb' + - 'config/initializers/00-core_plugins.rb' + +Security/Open: + Exclude: + - 'app/models/version.rb' + +# Cop supports --auto-correct. +Security/YAMLLoad: + Exclude: + - 'Gemfile' + - 'app/models/setting.rb' + - 'lib/redmine/configuration.rb' + +# Configuration parameters: EnforcedStyle. +# SupportedStyles: inline, group +Style/AccessModifierDeclarations: + Enabled: false + +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle. +# SupportedStyles: prefer_alias, prefer_alias_method +Style/Alias: + Exclude: + - 'app/controllers/repositories_controller.rb' + - 'app/models/custom_field_enumeration.rb' + - 'app/models/enumeration.rb' + - 'app/models/issue.rb' + - 'app/models/issue_category.rb' + - 'app/models/issue_query.rb' + - 'app/models/issue_status.rb' + - 'app/models/member.rb' + - 'app/models/project.rb' + - 'app/models/user.rb' + - 'app/models/version.rb' + - 'config/initializers/10-patches.rb' + - 'lib/redmine/export/pdf.rb' + - 'lib/redmine/menu_manager.rb' + - 'lib/redmine/plugin.rb' + - 'lib/redmine/wiki_formatting/markdown/formatter.rb' + - 'lib/redmine/wiki_formatting/textile/formatter.rb' + +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle. +# SupportedStyles: always, conditionals +Style/AndOr: + Exclude: + - 'app/controllers/account_controller.rb' + - 'app/controllers/application_controller.rb' + - 'app/controllers/calendars_controller.rb' + - 'app/helpers/repositories_helper.rb' + - 'app/models/query.rb' + - 'db/migrate/022_serialize_possibles_values.rb' + - 'lib/redmine/export/pdf.rb' + - 'lib/redmine/field_format.rb' + - 'lib/redmine/helpers/gantt.rb' + - 'lib/redmine/scm/adapters/abstract_adapter.rb' + - 'lib/redmine/scm/adapters/bazaar_adapter.rb' + - 'lib/redmine/scm/adapters/filesystem_adapter.rb' + - 'lib/redmine/scm/adapters/git_adapter.rb' + - 'lib/redmine/scm/adapters/subversion_adapter.rb' + - 'lib/redmine/wiki_formatting.rb' + - 'lib/redmine/wiki_formatting/textile/redcloth3.rb' + +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle. +# SupportedStyles: percent_q, bare_percent +Style/BarePercentLiterals: + Exclude: + - 'test/integration/api_test/api_test.rb' + +# Cop supports --auto-correct. +Style/BlockComments: + Exclude: + - 'lib/diff.rb' + +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, ProceduralMethods, FunctionalMethods, IgnoredMethods, AllowBracesOnProceduralOneLiners. +# SupportedStyles: line_count_based, semantic, braces_for_chaining, always_braces +# ProceduralMethods: benchmark, bm, bmbm, create, each_with_object, measure, new, realtime, tap, with_object +# FunctionalMethods: let, let!, subject, watch +# IgnoredMethods: lambda, proc, it +Style/BlockDelimiters: + Enabled: false + +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle. +# SupportedStyles: braces, no_braces, context_dependent +Style/BracesAroundHashParameters: + Enabled: false + +# Cop supports --auto-correct. +# Configuration parameters: AutoCorrect, EnforcedStyle. +# SupportedStyles: nested, compact +Style/ClassAndModuleChildren: + Enabled: false + +# Cop supports --auto-correct. +Style/ClassMethods: + Exclude: + - 'lib/diff.rb' + +Style/ClassVars: + Exclude: + - 'app/models/attachment.rb' + - 'config/initializers/10-patches.rb' + - 'lib/redmine/activity.rb' + - 'lib/redmine/export/pdf.rb' + - 'lib/redmine/hook.rb' + - 'lib/redmine/i18n.rb' + - 'lib/redmine/menu_manager.rb' + - 'lib/redmine/my_page.rb' + - 'lib/redmine/scm/adapters/bazaar_adapter.rb' + - 'lib/redmine/scm/adapters/cvs_adapter.rb' + - 'lib/redmine/scm/adapters/git_adapter.rb' + - 'lib/redmine/scm/adapters/mercurial_adapter.rb' + - 'lib/redmine/scm/adapters/subversion_adapter.rb' + - 'lib/redmine/search.rb' + - 'lib/redmine/themes.rb' + - 'lib/redmine/wiki_formatting.rb' + - 'lib/redmine/wiki_formatting/macros.rb' + - 'lib/redmine/wiki_formatting/markdown/formatter.rb' + - 'test/helpers/activities_helper_test.rb' + +# Cop supports --auto-correct. +Style/ColonMethodCall: + Exclude: + - 'Gemfile' + - 'app/controllers/admin_controller.rb' + - 'app/models/setting.rb' + - 'lib/redmine/configuration.rb' + - 'lib/redmine/export/pdf.rb' + - 'lib/redmine/wiki_formatting.rb' + - 'lib/redmine/wiki_formatting/textile/redcloth3.rb' + - 'test/test_helper.rb' + - 'test/unit/default_data_test.rb' + - 'test/unit/lib/redmine/export/pdf_test.rb' + +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, SingleLineConditionsOnly, IncludeTernaryExpressions. +# SupportedStyles: assign_to_condition, assign_inside_condition +Style/ConditionalAssignment: + Enabled: false + +Style/Documentation: + Enabled: false + +Style/DoubleNegation: + Exclude: + - 'app/controllers/application_controller.rb' + - 'app/helpers/application_helper.rb' + - 'app/models/attachment.rb' + - 'app/models/issue_import.rb' + - 'app/models/mailer.rb' + - 'app/models/query.rb' + - 'app/models/user.rb' + - 'lib/redmine/sudo_mode.rb' + +# Cop supports --auto-correct. +Style/EachWithObject: + Exclude: + - 'app/controllers/repositories_controller.rb' + - 'app/helpers/queries_helper.rb' + - 'app/models/issue.rb' + - 'app/models/issue_import.rb' + - 'app/models/journal.rb' + - 'app/models/mail_handler.rb' + - 'app/models/project.rb' + - 'app/models/query.rb' + - 'app/models/repository.rb' + - 'app/models/setting.rb' + - 'app/models/time_entry_import.rb' + - 'app/models/workflow_permission.rb' + - 'config/initializers/10-patches.rb' + - 'lib/redmine/acts/positioned.rb' + - 'lib/redmine/configuration.rb' + - 'lib/redmine/i18n.rb' + - 'lib/redmine/mime_type.rb' + - 'lib/redmine/my_page.rb' + - 'lib/redmine/sort_criteria.rb' + - 'test/test_helper.rb' + +# Cop supports --auto-correct. +Style/EmptyCaseCondition: + Exclude: + - 'app/models/auth_source_ldap.rb' + - 'app/models/issue_priority.rb' + +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle. +# SupportedStyles: empty, nil, both +Style/EmptyElse: + Exclude: + - 'app/helpers/avatars_helper.rb' + - 'app/models/import.rb' + - 'app/models/mail_handler.rb' + - 'app/models/query.rb' + +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle. +# SupportedStyles: compact, expanded +Style/EmptyMethod: + Enabled: false + +Style/EvalWithLocation: + Exclude: + - 'app/helpers/attachments_helper.rb' + +# Cop supports --auto-correct. +Style/ExpandPathArguments: + Enabled: false + +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle. +# SupportedStyles: each, for +Style/For: + Exclude: + - 'lib/redmine/export/pdf/issues_pdf_helper.rb' + - 'lib/redmine/export/pdf/wiki_pdf_helper.rb' + +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle. +# SupportedStyles: format, sprintf, percent +Style/FormatString: + Exclude: + - 'app/helpers/application_helper.rb' + - 'app/helpers/queries_helper.rb' + - 'app/models/query.rb' + - 'config/initializers/10-patches.rb' + - 'lib/generators/redmine_plugin_model/redmine_plugin_model_generator.rb' + - 'lib/redmine/export/csv.rb' + - 'lib/redmine/export/pdf/issues_pdf_helper.rb' + - 'lib/redmine/helpers/gantt.rb' + - 'lib/redmine/i18n.rb' + - 'lib/redmine/info.rb' + +# Configuration parameters: AllowedVariables. +Style/GlobalVars: + Exclude: + - 'test/test_helper.rb' + +# Configuration parameters: MinBodyLength. +Style/GuardClause: + Enabled: false + +# Configuration parameters: AllowIfModifier. +Style/IfInsideElse: + Exclude: + - 'app/controllers/account_controller.rb' + - 'app/controllers/activities_controller.rb' + - 'app/controllers/application_controller.rb' + - 'app/helpers/application_helper.rb' + - 'app/helpers/issues_helper.rb' + - 'app/models/custom_field.rb' + - 'app/models/issue.rb' + - 'app/models/project.rb' + - 'app/models/query.rb' + - 'app/models/version.rb' + - 'config/initializers/10-patches.rb' + - 'lib/redmine/field_format.rb' + - 'lib/redmine/helpers/gantt.rb' + - 'lib/redmine/menu_manager.rb' + - 'lib/redmine/pop3.rb' + - 'lib/redmine/scm/adapters/bazaar_adapter.rb' + - 'lib/redmine/views/builders/structure.rb' + +# Cop supports --auto-correct. +Style/IfUnlessModifier: + Enabled: false + +Style/IfUnlessModifierOfIfUnless: + Exclude: + - 'app/models/attachment.rb' + +# Cop supports --auto-correct. +# Configuration parameters: InverseMethods, InverseBlocks. +Style/InverseMethods: + Exclude: + - 'app/controllers/roles_controller.rb' + - 'app/helpers/application_helper.rb' + - 'app/helpers/issues_helper.rb' + - 'app/helpers/repositories_helper.rb' + - 'app/models/document.rb' + - 'app/models/issue.rb' + - 'app/models/member.rb' + - 'app/models/message.rb' + - 'app/models/project.rb' + - 'app/models/tracker.rb' + - 'app/models/version.rb' + - 'app/models/wiki_content.rb' + - 'db/migrate/096_add_commit_access_permission.rb' + - 'lib/redmine/default_data/loader.rb' + - 'lib/redmine/helpers/diff.rb' + - 'lib/redmine/scm/adapters/abstract_adapter.rb' + - 'lib/redmine/scm/adapters/git_adapter.rb' + - 'lib/redmine/wiki_formatting/textile/redcloth3.rb' + - 'test/unit/issue_test.rb' + - 'test/unit/query_test.rb' + +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle. +# SupportedStyles: line_count_dependent, lambda, literal +Style/Lambda: + Enabled: false + +# Cop supports --auto-correct. +Style/LineEndConcatenation: + Enabled: false + +# Cop supports --auto-correct. +# Configuration parameters: IgnoredMethods. +Style/MethodCallWithoutArgsParentheses: + Exclude: + - 'lib/redmine/export/pdf.rb' + - 'lib/redmine/helpers/gantt.rb' + - 'lib/redmine/scm/adapters/cvs_adapter.rb' + - 'lib/redmine/scm/adapters/filesystem_adapter.rb' + +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle. +# SupportedStyles: require_parentheses, require_no_parentheses, require_no_parentheses_except_multiline +Style/MethodDefParentheses: + Exclude: + - 'lib/redmine/menu_manager.rb' + +Style/MethodMissingSuper: + Exclude: + - 'lib/redmine/views/builders/structure.rb' + +Style/MissingRespondToMissing: + Exclude: + - 'lib/redmine/views/builders/structure.rb' + +Style/MixinUsage: + Exclude: + - 'test/test_helper.rb' + +# Cop supports --auto-correct. +Style/MultilineIfModifier: + Exclude: + - 'app/helpers/application_helper.rb' + - 'app/helpers/attachments_helper.rb' + - 'app/helpers/custom_fields_helper.rb' + - 'app/helpers/projects_helper.rb' + - 'app/helpers/reports_helper.rb' + - 'app/helpers/repositories_helper.rb' + - 'app/helpers/search_helper.rb' + - 'app/models/issue_query.rb' + - 'app/models/query.rb' + - 'app/models/repository/bazaar.rb' + - 'app/models/repository/subversion.rb' + - 'app/models/time_entry_query.rb' + - 'app/views/common/feed.atom.builder' + - 'lib/redmine/access_keys.rb' + - 'lib/redmine/helpers/gantt.rb' + - 'lib/redmine/scm/adapters/git_adapter.rb' + - 'lib/redmine/scm/adapters/subversion_adapter.rb' + - 'lib/redmine/wiki_formatting.rb' + - 'lib/redmine/wiki_formatting/textile/redcloth3.rb' + - 'test/mocks/open_id_authentication_mock.rb' + +Style/MultilineTernaryOperator: + Exclude: + - 'app/controllers/workflows_controller.rb' + - 'app/helpers/settings_helper.rb' + - 'app/models/issue_query.rb' + - 'lib/redmine/ciphering.rb' + - 'lib/redmine/scm/adapters/filesystem_adapter.rb' + - 'lib/redmine/scm/adapters/git_adapter.rb' + +Style/MultipleComparison: + Exclude: + - 'app/helpers/application_helper.rb' + - 'app/helpers/workflows_helper.rb' + - 'app/models/import.rb' + - 'app/models/version.rb' + - 'app/models/wiki_page.rb' + - 'app/models/workflow_transition.rb' + +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle. +# SupportedStyles: literals, strict +Style/MutableConstant: + Enabled: false + +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle. +# SupportedStyles: both, prefix, postfix +Style/NegatedIf: + Exclude: + - 'app/controllers/application_controller.rb' + - 'app/controllers/messages_controller.rb' + - 'app/helpers/repositories_helper.rb' + - 'app/models/attachment.rb' + - 'app/models/auth_source_ldap.rb' + - 'app/models/custom_field.rb' + - 'app/models/project.rb' + - 'app/models/repository/cvs.rb' + - 'lib/diff.rb' + - 'lib/redmine/codeset_util.rb' + - 'lib/redmine/thumbnail.rb' + +# Cop supports --auto-correct. +Style/NegatedWhile: + Exclude: + - 'app/helpers/application_helper.rb' + +# Cop supports --auto-correct. +# Configuration parameters: Whitelist. +# Whitelist: be, be_a, be_an, be_between, be_falsey, be_kind_of, be_instance_of, be_truthy, be_within, eq, eql, end_with, include, match, raise_error, respond_to, start_with +Style/NestedParenthesizedCalls: + Exclude: + - 'lib/redmine/views/labelled_form_builder.rb' + - 'test/unit/mail_handler_test.rb' + +Style/NestedTernaryOperator: + Exclude: + - 'app/helpers/queries_helper.rb' + - 'app/models/journal.rb' + +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, MinBodyLength. +# SupportedStyles: skip_modifier_ifs, always +Style/Next: + Enabled: false + +# Cop supports --auto-correct. +Style/Not: + Exclude: + - 'db/migrate/096_add_commit_access_permission.rb' + - 'lib/redmine/scm/adapters/filesystem_adapter.rb' + - 'lib/redmine/wiki_formatting/textile/redcloth3.rb' + +# Cop supports --auto-correct. +# Configuration parameters: EnforcedOctalStyle. +# SupportedOctalStyles: zero_with_o, zero_only +Style/NumericLiteralPrefix: + Exclude: + - 'test/unit/user_test.rb' + +# Cop supports --auto-correct. +# Configuration parameters: Strict. +Style/NumericLiterals: + MinDigits: 9 + +# Cop supports --auto-correct. +# Configuration parameters: AutoCorrect, EnforcedStyle, IgnoredMethods. +# SupportedStyles: predicate, comparison +Style/NumericPredicate: + Enabled: false + +# Cop supports --auto-correct. +Style/OrAssignment: + Exclude: + - 'app/helpers/sort_helper.rb' + - 'app/models/repository/cvs.rb' + - 'app/models/user.rb' + - 'lib/redmine/helpers/diff.rb' + +# Cop supports --auto-correct. +Style/ParallelAssignment: + Enabled: false + +# Cop supports --auto-correct. +# Configuration parameters: AllowSafeAssignment, AllowInMultilineConditions. +Style/ParenthesesAroundCondition: + Exclude: + - 'app/models/repository/subversion.rb' + - 'lib/redmine/scm/adapters/cvs_adapter.rb' + - 'lib/redmine/scm/adapters/git_adapter.rb' + +# Cop supports --auto-correct. +# Configuration parameters: PreferredDelimiters. +Style/PercentLiteralDelimiters: + Enabled: false + +# Cop supports --auto-correct. +Style/PerlBackrefs: + Enabled: false + +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle. +# SupportedStyles: short, verbose +Style/PreferredHashMethods: + Enabled: false + +# Cop supports --auto-correct. +Style/Proc: + Enabled: false + +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle. +# SupportedStyles: compact, exploded +Style/RaiseArgs: + Exclude: + - 'app/models/auth_source_ldap.rb' + - 'app/models/issue_query.rb' + - 'app/models/mailer.rb' + - 'app/models/query.rb' + - 'app/models/tracker.rb' + - 'app/models/user.rb' + - 'app/models/workflow_rule.rb' + - 'lib/redmine/access_control.rb' + - 'lib/redmine/default_data/loader.rb' + - 'lib/redmine/plugin.rb' + - 'lib/redmine/scm/adapters/abstract_adapter.rb' + - 'lib/redmine/scm/adapters/filesystem_adapter.rb' + - 'lib/redmine/sort_criteria.rb' + +# Cop supports --auto-correct. +Style/RedundantCondition: + Exclude: + - 'app/controllers/messages_controller.rb' + - 'app/controllers/previews_controller.rb' + - 'app/models/issue.rb' + +# Cop supports --auto-correct. +Style/RedundantConditional: + Exclude: + - 'app/controllers/workflows_controller.rb' + - 'app/helpers/application_helper.rb' + - 'app/models/enumeration.rb' + - 'lib/redmine/field_format.rb' + +# Cop supports --auto-correct. +Style/RedundantInterpolation: + Exclude: + - 'app/helpers/my_helper.rb' + - 'app/helpers/queries_helper.rb' + - 'app/models/query.rb' + - 'app/views/common/feed.atom.builder' + - 'app/views/journals/index.builder' + - 'lib/redmine/helpers/time_report.rb' + - 'lib/redmine/scm/adapters/subversion_adapter.rb' + - 'lib/redmine/wiki_formatting.rb' + - 'test/functional/versions_controller_test.rb' + - 'test/functional/watchers_controller_test.rb' + +# Cop supports --auto-correct. +Style/RedundantParentheses: + Exclude: + - 'app/controllers/search_controller.rb' + - 'app/helpers/users_helper.rb' + - 'app/models/enumeration.rb' + - 'app/models/principal.rb' + - 'app/models/query.rb' + - 'app/models/repository/cvs.rb' + - 'app/models/version.rb' + - 'lib/redmine/field_format.rb' + - 'lib/redmine/helpers/gantt.rb' + - 'lib/redmine/platform.rb' + - 'lib/redmine/scm/adapters/cvs_adapter.rb' + - 'lib/redmine/scm/adapters/filesystem_adapter.rb' + - 'test/helpers/application_helper_test.rb' + - 'test/unit/attachment_test.rb' + - 'test/unit/lib/redmine/export/pdf_test.rb' + - 'test/unit/project_test.rb' + - 'test/unit/user_test.rb' + - 'test/unit/version_test.rb' + +# Cop supports --auto-correct. +# Configuration parameters: AllowMultipleReturnValues. +Style/RedundantReturn: + Enabled: false + +# Cop supports --auto-correct. +Style/RedundantSelf: + Enabled: false + +# Cop supports --auto-correct. +Style/RedundantSort: + Exclude: + - 'app/models/import.rb' + - 'lib/redmine/scm/adapters/abstract_adapter.rb' + - 'test/functional/issues_controller_test.rb' + - 'test/unit/member_test.rb' + +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, AllowInnerSlashes. +# SupportedStyles: slashes, percent_r, mixed +Style/RegexpLiteral: + Enabled: false + +# Cop supports --auto-correct. +Style/RescueModifier: + Exclude: + - 'app/controllers/application_controller.rb' + - 'app/controllers/attachments_controller.rb' + - 'app/controllers/imports_controller.rb' + - 'app/controllers/watchers_controller.rb' + - 'app/helpers/avatars_helper.rb' + - 'app/models/custom_field.rb' + - 'app/models/import.rb' + - 'app/models/query.rb' + - 'app/models/repository.rb' + - 'lib/redmine/export/csv.rb' + - 'lib/redmine/field_format.rb' + - 'lib/redmine/search.rb' + - 'lib/redmine/wiki_formatting.rb' + - 'test/unit/lib/redmine/wiki_formatting/markdown_formatter_test.rb' + +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle. +# SupportedStyles: implicit, explicit +Style/RescueStandardError: + Enabled: false + +# Cop supports --auto-correct. +# Configuration parameters: ConvertCodeThatCanStartToReturnNil, Whitelist. +# Whitelist: present?, blank?, presence, try, try! +Style/SafeNavigation: + Enabled: false + +# Cop supports --auto-correct. +Style/SelfAssignment: + Exclude: + - 'app/controllers/repositories_controller.rb' + - 'app/helpers/application_helper.rb' + - 'app/helpers/reports_helper.rb' + - 'app/models/auth_source_ldap.rb' + - 'app/models/project.rb' + - 'app/models/query.rb' + - 'app/models/repository/cvs.rb' + - 'lib/redmine/helpers/calendar.rb' + - 'lib/redmine/helpers/gantt.rb' + - 'lib/redmine/helpers/time_report.rb' + +# Cop supports --auto-correct. +# Configuration parameters: AllowAsExpressionSeparator. +Style/Semicolon: + Exclude: + - 'app/controllers/account_controller.rb' + - 'app/controllers/context_menus_controller.rb' + - 'app/controllers/journals_controller.rb' + - 'app/controllers/messages_controller.rb' + - 'app/controllers/repositories_controller.rb' + - 'app/controllers/search_controller.rb' + - 'app/models/issue.rb' + - 'app/models/issue_query.rb' + - 'app/models/journal.rb' + - 'app/models/mail_handler.rb' + - 'app/models/project.rb' + - 'app/models/query.rb' + - 'app/models/tracker.rb' + - 'lib/redmine/configuration.rb' + - 'lib/redmine/i18n.rb' + - 'lib/redmine/scm/adapters/git_adapter.rb' + - 'lib/redmine/sort_criteria.rb' + - 'lib/redmine/wiki_formatting/textile/redcloth3.rb' + +# Cop supports --auto-correct. +# Configuration parameters: AllowIfMethodIsEmpty. +Style/SingleLineMethods: + Enabled: false + +# Cop supports --auto-correct. +# Configuration parameters: . +# SupportedStyles: use_perl_names, use_english_names +Style/SpecialGlobalVars: + EnforcedStyle: use_perl_names + +# Cop supports --auto-correct. +Style/StderrPuts: + Exclude: + - 'config/environment.rb' + - 'config/initializers/10-patches.rb' + +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, ConsistentQuotesInMultiline. +# SupportedStyles: single_quotes, double_quotes +Style/StringLiterals: + Enabled: false + +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle. +# SupportedStyles: single_quotes, double_quotes +Style/StringLiteralsInInterpolation: + Exclude: + - 'app/models/issue_query.rb' + - 'config/environment.rb' + - 'lib/redmine/scm/adapters/cvs_adapter.rb' + - 'lib/redmine/wiki_formatting.rb' + - 'lib/redmine/wiki_formatting/textile/redcloth3.rb' + - 'test/unit/member_test.rb' + +Style/StructInheritance: + Exclude: + - 'lib/redmine/notifiable.rb' + - 'lib/redmine/sudo_mode.rb' + +# Cop supports --auto-correct. +# Configuration parameters: MinSize. +# SupportedStyles: percent, brackets +Style/SymbolArray: + EnforcedStyle: brackets + +# Cop supports --auto-correct. +# Configuration parameters: IgnoredMethods. +# IgnoredMethods: respond_to, define_method +Style/SymbolProc: + Enabled: false + +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, AllowSafeAssignment. +# SupportedStyles: require_parentheses, require_no_parentheses, require_parentheses_when_complex +Style/TernaryParentheses: + Enabled: false + +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyleForMultiline. +# SupportedStylesForMultiline: comma, consistent_comma, no_comma +Style/TrailingCommaInHashLiteral: + Enabled: false + +# Cop supports --auto-correct. +# Configuration parameters: ExactNameMatch, AllowPredicates, AllowDSLWriters, IgnoreClassMethods, Whitelist. +# Whitelist: to_ary, to_a, to_c, to_enum, to_h, to_hash, to_i, to_int, to_io, to_open, to_path, to_proc, to_r, to_regexp, to_str, to_s, to_sym +Style/TrivialAccessors: + Exclude: + - 'app/models/issue.rb' + - 'app/models/journal.rb' + - 'app/models/query.rb' + - 'lib/redmine/access_control.rb' + - 'lib/redmine/scm/adapters/abstract_adapter.rb' + - 'lib/redmine/scm/adapters/cvs_adapter.rb' + - 'lib/redmine/scm/adapters/filesystem_adapter.rb' + - 'lib/redmine/scm/adapters/git_adapter.rb' + - 'lib/redmine/scm/adapters/mercurial_adapter.rb' + +# Cop supports --auto-correct. +Style/UnlessElse: + Exclude: + - 'app/controllers/auth_sources_controller.rb' + - 'app/controllers/trackers_controller.rb' + - 'lib/redmine/scm/adapters/abstract_adapter.rb' + - 'lib/redmine/unified_diff.rb' + +# Cop supports --auto-correct. +Style/VariableInterpolation: + Exclude: + - 'lib/redmine/configuration.rb' + +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, MinSize, WordRegex. +# SupportedStyles: percent, brackets +Style/WordArray: + Enabled: false + +# Cop supports --auto-correct. +Style/ZeroLengthPredicate: + Exclude: + - 'app/models/import.rb' + - 'app/models/mail_handler.rb' + - 'app/models/user.rb' + - 'lib/redmine/field_format.rb' + - 'lib/redmine/unified_diff.rb' + - 'lib/redmine/wiki_formatting/macros.rb' + - 'test/functional/custom_fields_controller_test.rb' + - 'test/object_helpers.rb' + - 'test/unit/activity_test.rb' + - 'test/unit/custom_field_test.rb' diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7894bca..94e13c5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,8 +1,10 @@ +Your contributions to Redmine are welcome! -**Do not send a pull request to this GitHub repository**. +Please **open an issue on the [official website]** instead of sending pull requests. -For more detail, please see [official website] wiki [Contribute]. +Since the development of Redmine is not conducted on GitHub but on the [official website] and core developers are not monitoring the GitHub repo, pull requests might not get reviewed. -[official website]: http://www.redmine.org -[Contribute]: http://www.redmine.org/projects/redmine/wiki/Contribute +For more detail about how to contribute, please see the wiki page [Contribute] on the [official website]. +[official website]: https://www.redmine.org/ +[Contribute]: https://www.redmine.org/projects/redmine/wiki/Contribute diff --git a/Gemfile b/Gemfile index 68b1b43..f9a98ff 100644 --- a/Gemfile +++ b/Gemfile @@ -1,41 +1,28 @@ source 'https://rubygems.org' -gem "bundler", ">= 1.5.0", "< 2.0.0" +ruby '>= 2.3.0', '< 2.7.0' if Bundler::VERSION >= '1.12.0' +gem "bundler", ">= 1.5.0" -gem "rails", "4.2.11.1" -gem "addressable", "2.4.0" if RUBY_VERSION < "2.0" -if RUBY_VERSION < "2.1" - gem "public_suffix", (RUBY_VERSION < "2.0" ? "~> 1.4" : "~> 2.0.5") -end -gem "jquery-rails", "~> 3.1.4" -gem "coderay", "~> 1.1.1" -gem "request_store", "1.0.5" -gem "mime-types", (RUBY_VERSION >= "2.0" ? "~> 3.0" : "~> 2.99") -gem "protected_attributes" +gem 'rails', '5.2.4.2' +gem 'sprockets', '~> 3.7.2' if RUBY_VERSION < '2.5' +gem "rouge", "~> 3.12.0" +gem "request_store", "~> 1.4.1" +gem "mini_mime", "~> 1.0.1" gem "actionpack-xml_parser" -gem "roadie-rails", "~> 1.1.1" -gem "roadie", "~> 3.2.1" +gem "roadie-rails", (RUBY_VERSION < "2.5" ? "~> 1.3.0" : "~> 2.1.0") gem "mimemagic" -gem "mail", "~> 2.6.4" - -gem "nokogiri", (RUBY_VERSION >= "2.1" ? "~> 1.8.1" : "~> 1.6.8") -gem "i18n", "~> 0.7.0" -gem "ffi", "1.9.14", :platforms => :mingw if RUBY_VERSION < "2.0" -gem "xpath", "< 3.2.0" if RUBY_VERSION < "2.3" - -# Request at least rails-html-sanitizer 1.0.3 because of security advisories -gem "rails-html-sanitizer", ">= 1.0.3" - -# TODO: Remove the following line when #32223 is fixed -gem "sprockets", "~> 3.7.2" +gem "mail", "~> 2.7.1" +gem "csv", "~> 3.1.1" +gem "nokogiri", "~> 1.10.0" +gem "i18n", "~> 1.6.0" +gem "rbpdf", "~> 1.20.0" # Windows does not include zoneinfo files, so bundle the tzinfo-data gem gem 'tzinfo-data', platforms: [:mingw, :x64_mingw, :mswin] -gem "rbpdf", "~> 1.19.6" # Optional gem for LDAP authentication group :ldap do - gem "net-ldap", "~> 0.12.0" + gem "net-ldap", "~> 0.16.0" end # Optional gem for OpenID authentication @@ -44,16 +31,14 @@ group :openid do gem "rack-openid" end -platforms :mri, :mingw, :x64_mingw do - # Optional gem for exporting the gantt to a PNG file, not supported with jruby - group :rmagick do - gem "rmagick", "~> 2.16.0" - end +# Optional gem for exporting the gantt to a PNG file +group :minimagick do + gem "mini_magick", "~> 4.9.5" +end - # Optional Markdown support, not for JRuby - group :markdown do - gem "redcarpet", "~> 3.4.0" - end +# Optional Markdown support, not for JRuby +group :markdown do + gem "redcarpet", "~> 3.5.0" end # Include database gems for the adapters found in the database @@ -68,15 +53,14 @@ if File.exist?(database_file) adapters.each do |adapter| case adapter when 'mysql2' - gem "mysql2", "~> 0.4.6", :platforms => [:mri, :mingw, :x64_mingw] + gem "mysql2", "~> 0.5.0", :platforms => [:mri, :mingw, :x64_mingw] when /postgresql/ - gem "pg", "~> 0.18.1", :platforms => [:mri, :mingw, :x64_mingw] + gem "pg", "~> 1.1.4", :platforms => [:mri, :mingw, :x64_mingw] when /sqlite3/ - gem "sqlite3", (RUBY_VERSION < "2.0" && RUBY_PLATFORM =~ /mingw/ ? "1.3.12" : "~>1.3.12"), - :platforms => [:mri, :mingw, :x64_mingw] + gem "sqlite3", "~> 1.4.0", :platforms => [:mri, :mingw, :x64_mingw] when /sqlserver/ - gem "tiny_tds", (RUBY_VERSION >= "2.0" ? "~> 1.0.5" : "~> 0.7.0"), :platforms => [:mri, :mingw, :x64_mingw] - gem "activerecord-sqlserver-adapter", :platforms => [:mri, :mingw, :x64_mingw] + gem "tiny_tds", "~> 2.1.2", :platforms => [:mri, :mingw, :x64_mingw] + gem "activerecord-sqlserver-adapter", "~> 5.2.1", :platforms => [:mri, :mingw, :x64_mingw] else warn("Unknown database adapter `#{adapter}` found in config/database.yml, use Gemfile.local to load your own database gems") end @@ -89,20 +73,22 @@ else end group :development do - gem "rdoc", "~> 4.3" gem "yard" end group :test do - gem "minitest" gem "rails-dom-testing" gem 'mocha', '>= 1.4.0' - gem "simplecov", "~> 0.9.1", :require => false - # TODO: remove this after upgrading to Rails 5 - gem "test_after_commit", "~> 0.4.2" - # For running UI tests - gem "capybara", '~> 2.13' - gem "selenium-webdriver", "~> 2.53.4" + gem "simplecov", "~> 0.17.0", :require => false + gem "ffi", platforms: [:mingw, :x64_mingw, :mswin] + # For running system tests + gem 'puma', '~> 3.7' + gem "capybara", (RUBY_VERSION < "2.4" ? "~> 3.15.1" : "~> 3.25.0") + gem "selenium-webdriver" + # RuboCop + gem 'rubocop', '~> 0.76.0' + gem 'rubocop-performance', '~> 1.5.0' + gem 'rubocop-rails', '~> 2.3.0' end local_gemfile = File.join(File.dirname(__FILE__), "Gemfile.local") diff --git a/app/controllers/account_controller.rb b/app/controllers/account_controller.rb index 5070295..ff8631e 100644 --- a/app/controllers/account_controller.rb +++ b/app/controllers/account_controller.rb @@ -1,5 +1,7 @@ +# frozen_string_literal: true + # Redmine - project management software -# Copyright (C) 2006-2017 Jean-Philippe Lang +# Copyright (C) 2006-2019 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -62,9 +64,15 @@ class AccountController < ApplicationController (redirect_to(home_url); return) unless Setting.lost_password? if prt = (params[:token] || session[:password_recovery_token]) @token = Token.find_token("recovery", prt.to_s) - if @token.nil? || @token.expired? + if @token.nil? redirect_to home_url return + elsif @token.expired? + # remove expired token from session and let user try again + session[:password_recovery_token] = nil + flash[:error] = l(:error_token_expired) + redirect_to lost_password_url + return end # redirect to remove the token query parameter from the URL and add it to the session @@ -87,7 +95,7 @@ class AccountController < ApplicationController @user.must_change_passwd = false if @user.save @token.destroy - Mailer.password_updated(@user, { remote_ip: request.remote_ip }) + Mailer.deliver_password_updated(@user, User.current) flash[:notice] = l(:notice_account_password_updated) redirect_to signin_path return @@ -119,7 +127,7 @@ class AccountController < ApplicationController if token.save # Don't use the param to send the email recipent = user.mails.detect {|e| email.casecmp(e) == 0} || user.mail - Mailer.lost_password(token, recipent).deliver + Mailer.deliver_lost_password(user, token, recipent) flash[:notice] = l(:notice_account_lost_email_sent) redirect_to signin_path return @@ -313,7 +321,7 @@ class AccountController < ApplicationController def register_by_email_activation(user, &block) token = Token.new(:user => user, :action => "register") if user.save and token.save - Mailer.register(token).deliver + Mailer.deliver_register(user, token) flash[:notice] = l(:notice_account_register_done, :email => ERB::Util.h(user.mail)) redirect_to signin_path else @@ -343,7 +351,7 @@ class AccountController < ApplicationController def register_manually_by_administrator(user, &block) if user.save # Sends an email to the administrators - Mailer.account_activation_request(user).deliver + Mailer.deliver_account_activation_request(user) account_pending(user) else yield if block_given? diff --git a/app/controllers/activities_controller.rb b/app/controllers/activities_controller.rb index f82f011..d809245 100644 --- a/app/controllers/activities_controller.rb +++ b/app/controllers/activities_controller.rb @@ -1,5 +1,7 @@ +# frozen_string_literal: true + # Redmine - project management software -# Copyright (C) 2006-2017 Jean-Philippe Lang +# Copyright (C) 2006-2019 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -17,7 +19,7 @@ class ActivitiesController < ApplicationController menu_item :activity - before_action :find_optional_project + before_action :find_optional_project_by_id, :authorize_global accept_rss_auth :index def index @@ -76,15 +78,4 @@ class ActivitiesController < ApplicationController rescue ActiveRecord::RecordNotFound render_404 end - - private - - # TODO: refactor, duplicated in projects_controller - def find_optional_project - return true unless params[:id] - @project = Project.find(params[:id]) - authorize - rescue ActiveRecord::RecordNotFound - render_404 - end end diff --git a/app/controllers/admin_controller.rb b/app/controllers/admin_controller.rb index dfc73c5..ac55133 100644 --- a/app/controllers/admin_controller.rb +++ b/app/controllers/admin_controller.rb @@ -1,5 +1,7 @@ +# frozen_string_literal: true + # Redmine - project management software -# Copyright (C) 2006-2017 Jean-Philippe Lang +# Copyright (C) 2006-2019 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -52,7 +54,7 @@ class AdminController < ApplicationController begin Redmine::DefaultData::Loader::load(params[:lang]) flash[:notice] = l(:notice_default_data_loaded) - rescue Exception => e + rescue => e flash[:error] = l(:error_can_t_load_default_data, ERB::Util.h(e.message)) end end @@ -60,16 +62,12 @@ class AdminController < ApplicationController end def test_email - raise_delivery_errors = ActionMailer::Base.raise_delivery_errors - # Force ActionMailer to raise delivery errors so we can catch it - ActionMailer::Base.raise_delivery_errors = true begin - @test = Mailer.test_email(User.current).deliver + Mailer.deliver_test_email(User.current) flash[:notice] = l(:notice_email_sent, ERB::Util.h(User.current.mail)) - rescue Exception => e + rescue => e flash[:error] = l(:notice_email_error, ERB::Util.h(Redmine::CodesetUtil.replace_invalid_utf8(e.message.dup))) end - ActionMailer::Base.raise_delivery_errors = raise_delivery_errors redirect_to settings_path(:tab => 'notifications') end @@ -78,8 +76,9 @@ class AdminController < ApplicationController [:text_default_administrator_account_changed, User.default_admin_account_changed?], [:text_file_repository_writable, File.writable?(Attachment.storage_path)], ["#{l :text_plugin_assets_writable} (./public/plugin_assets)", File.writable?(Redmine::Plugin.public_directory)], - [:text_rmagick_available, Object.const_defined?(:Magick)], - [:text_convert_available, Redmine::Thumbnail.convert_available?] + [:text_minimagick_available, Object.const_defined?(:MiniMagick)], + [:text_convert_available, Redmine::Thumbnail.convert_available?], + [:text_gs_available, Redmine::Thumbnail.gs_available?] ] end end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 1d42901..f095ac7 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,5 +1,7 @@ +# frozen_string_literal: true + # Redmine - project management software -# Copyright (C) 2006-2017 Jean-Philippe Lang +# Copyright (C) 2006-2019 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -18,14 +20,17 @@ require 'uri' require 'cgi' -class Unauthorized < Exception; end +class Unauthorized < StandardError; end class ApplicationController < ActionController::Base include Redmine::I18n include Redmine::Pagination include Redmine::Hook::Helper include RoutesHelper + include AvatarsHelper + helper :routes + helper :avatars class_attribute :accept_api_auth_actions class_attribute :accept_rss_auth_actions @@ -52,6 +57,7 @@ class ApplicationController < ActionController::Base end before_action :session_expiration, :user_setup, :check_if_login_required, :set_localization, :check_password_change + after_action :record_project_usage rescue_from ::Unauthorized, :with => :deny_access rescue_from ::ActionView::MissingTemplate, :with => :missing_template @@ -112,7 +118,7 @@ class ApplicationController < ActionController::Base if (key = api_key_from_request) # Use API key user = User.find_by_api_key(key) - elsif request.authorization.to_s =~ /\ABasic /i + elsif /\ABasic /i.match?(request.authorization.to_s) # HTTP Basic, either username/password or API key/random authenticate_with_http_basic do |username, password| user = User.try_to_login(username, password) || User.find_by_api_key(username) @@ -229,9 +235,14 @@ class ApplicationController < ActionController::Base format.any(:atom, :pdf, :csv) { redirect_to signin_path(:back_url => url) } - format.xml { head :unauthorized, 'WWW-Authenticate' => 'Basic realm="Redmine API"' } + format.api { + if Setting.rest_api_enabled? && accept_api_auth? + head(:unauthorized, 'WWW-Authenticate' => 'Basic realm="Redmine API"') + else + head(:forbidden) + end + } format.js { head :unauthorized, 'WWW-Authenticate' => 'Basic realm="Redmine API"' } - format.json { head :unauthorized, 'WWW-Authenticate' => 'Basic realm="Redmine API"' } format.any { head :unauthorized } end return false @@ -259,7 +270,11 @@ class ApplicationController < ActionController::Base true else if @project && @project.archived? + @archived_project = @project render_403 :message => :notice_not_authorized_archived_project + elsif @project && !@project.allows_to?(:controller => ctrl, :action => action) + # Project module is disabled + render_403 else deny_access end @@ -272,27 +287,31 @@ class ApplicationController < ActionController::Base end # Find project of id params[:id] - def find_project - @project = Project.find(params[:id]) + def find_project(project_id=params[:id]) + @project = Project.find(project_id) rescue ActiveRecord::RecordNotFound render_404 end # Find project of id params[:project_id] def find_project_by_project_id - @project = Project.find(params[:project_id]) - rescue ActiveRecord::RecordNotFound - render_404 + find_project(params[:project_id]) + end + + # Find project of id params[:id] if present + def find_optional_project_by_id + if params[:id].present? + find_project(params[:id]) + end end # Find a project based on params[:project_id] - # TODO: some subclasses override this, see about merging their logic + # and authorize the user for the requested action def find_optional_project - @project = Project.find(params[:project_id]) unless params[:project_id].blank? - allowed = User.current.allowed_to?({:controller => params[:controller], :action => params[:action]}, @project, :global => true) - allowed ? true : deny_access - rescue ActiveRecord::RecordNotFound - render_404 + if params[:project_id].present? + find_project(params[:project_id]) + end + authorize_global end # Finds and sets @project based on @object.project @@ -385,18 +404,30 @@ class ApplicationController < ActionController::Base end end + def record_project_usage + if @project && @project.id && User.current.logged? && User.current.allowed_to?(:view_project, @project) + Redmine::ProjectJumpBox.new(User.current).project_used(@project) + end + true + end + def back_url url = params[:back_url] if url.nil? && referer = request.env['HTTP_REFERER'] url = CGI.unescape(referer.to_s) + # URLs that contains the utf8=[checkmark] parameter added by Rails are + # parsed as invalid by URI.parse so the redirect to the back URL would + # not be accepted (ApplicationController#validate_back_url would return + # false) + url.gsub!(/(\?|&)utf8=\u2713&?/, '\1') end url end + helper_method :back_url def redirect_back_or_default(default, options={}) - back_url = params[:back_url].to_s - if back_url.present? && valid_url = validate_back_url(back_url) - redirect_to(valid_url) + if back_url = validate_back_url(params[:back_url].to_s) + redirect_to(back_url) return elsif options[:referer] redirect_to_referer_or default @@ -409,6 +440,8 @@ class ApplicationController < ActionController::Base # Returns a validated URL string if back_url is a valid url for redirection, # otherwise false def validate_back_url(back_url) + return false if back_url.blank? + if CGI.unescape(back_url).include?('..') return false end @@ -431,11 +464,11 @@ class ApplicationController < ActionController::Base path = uri.to_s # Ensure that the remaining URL starts with a slash, followed by a # non-slash character or the end - if path !~ %r{\A/([^/]|\z)} + if !%r{\A/([^/]|\z)}.match?(path) return false end - if path.match(%r{/(login|account/register|account/lost_password)}) + if %r{/(login|account/register|account/lost_password)}.match?(path) return false end @@ -446,11 +479,13 @@ class ApplicationController < ActionController::Base return path end private :validate_back_url + helper_method :validate_back_url def valid_back_url?(back_url) !!validate_back_url(back_url) end private :valid_back_url? + helper_method :valid_back_url? # Redirects to the request referer if present, redirects to args or call block otherwise. def redirect_to_referer_or(*args, &block) @@ -460,7 +495,7 @@ class ApplicationController < ActionController::Base if args.any? redirect_to *args elsif block_given? - block.call + yield else raise "#redirect_to_referer_or takes arguments or a block" end @@ -495,8 +530,8 @@ class ApplicationController < ActionController::Base end # Handler for ActionView::MissingTemplate exception - def missing_template - logger.warn "Missing template, responding with 404" + def missing_template(exception) + logger.warn "Missing template, responding with 404: #{exception}" @project = nil render_404 end @@ -616,7 +651,7 @@ class ApplicationController < ActionController::Base # Returns a string that can be used as filename value in Content-Disposition header def filename_for_content_disposition(name) - request.env['HTTP_USER_AGENT'] =~ %r{(MSIE|Trident|Edge)} ? ERB::Util.url_encode(name) : name + %r{(MSIE|Trident|Edge)}.match?(request.env['HTTP_USER_AGENT']) ? ERB::Util.url_encode(name) : name end def api_request? @@ -649,9 +684,9 @@ class ApplicationController < ActionController::Base render_error "An error occurred while executing the query and has been logged. Please report this error to your Redmine administrator." end - # Renders a 200 response for successful updates or deletions via the API + # Renders a 204 response for successful updates or deletions via the API def render_api_ok - render_api_head :ok + render_api_head :no_content end # Renders a head API response diff --git a/app/controllers/attachments_controller.rb b/app/controllers/attachments_controller.rb index 64cb05e..4a88079 100644 --- a/app/controllers/attachments_controller.rb +++ b/app/controllers/attachments_controller.rb @@ -1,5 +1,7 @@ +# frozen_string_literal: true + # Redmine - project management software -# Copyright (C) 2006-2017 Jean-Philippe Lang +# Copyright (C) 2006-2019 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -32,6 +34,14 @@ class AttachmentsController < ApplicationController def show respond_to do |format| format.html { + if @attachment.container.respond_to?(:attachments) + @attachments = @attachment.container.attachments.to_a + if index = @attachments.index(@attachment) + @paginator = Redmine::Pagination::Paginator.new( + @attachments.size, 1, index+1 + ) + end + end if @attachment.is_diff? @diff = File.read(@attachment.diskfile, :mode => "rb") @diff_type = params[:type] || User.current.pref[:diff_type] || 'inline' @@ -71,10 +81,11 @@ class AttachmentsController < ApplicationController def thumbnail if @attachment.thumbnailable? && tbnail = @attachment.thumbnail(:size => params[:size]) if stale?(:etag => tbnail, :template => false) - send_file tbnail, + send_file( + tbnail, :filename => filename_for_content_disposition(@attachment.filename), - :type => detect_content_type(@attachment), - :disposition => 'inline' + :type => detect_content_type(@attachment, true), + :disposition => 'inline') end else # No thumbnail for the attachment or thumbnail could not be created @@ -156,8 +167,10 @@ class AttachmentsController < ApplicationController # Returns the menu item that should be selected when viewing an attachment def current_menu_item - if @attachment - case @attachment.container + container = @attachment.try(:container) || @container + + if container + case container when WikiPage :wiki when Message @@ -165,7 +178,7 @@ class AttachmentsController < ApplicationController when Project, Version :files else - @attachment.container.class.name.pluralize.downcase.to_sym + container.class.name.pluralize.downcase.to_sym end end end @@ -224,12 +237,20 @@ class AttachmentsController < ApplicationController @attachment.deletable? ? true : deny_access end - def detect_content_type(attachment) + def detect_content_type(attachment, is_thumb = false) content_type = attachment.content_type if content_type.blank? || content_type == "application/octet-stream" - content_type = Redmine::MimeType.of(attachment.filename) + content_type = + Redmine::MimeType.of(attachment.filename).presence || + "application/octet-stream" end - content_type.to_s + + if is_thumb && content_type == "application/pdf" + # PDF previews are stored in PNG format + content_type = "image/png" + end + + content_type end def disposition(attachment) diff --git a/app/controllers/auth_sources_controller.rb b/app/controllers/auth_sources_controller.rb index dcbba36..295dfd1 100644 --- a/app/controllers/auth_sources_controller.rb +++ b/app/controllers/auth_sources_controller.rb @@ -1,5 +1,7 @@ +# frozen_string_literal: true + # Redmine - project management software -# Copyright (C) 2006-2017 Jean-Philippe Lang +# Copyright (C) 2006-2019 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -58,7 +60,7 @@ class AuthSourcesController < ApplicationController begin @auth_source.test_connection flash[:notice] = l(:notice_successful_connection) - rescue Exception => e + rescue => e flash[:error] = l(:error_unable_to_connect, e.message) end redirect_to auth_sources_path @@ -68,22 +70,25 @@ class AuthSourcesController < ApplicationController unless @auth_source.users.exists? @auth_source.destroy flash[:notice] = l(:notice_successful_delete) + else + flash[:error] = l(:error_can_not_delete_auth_source) end redirect_to auth_sources_path end def autocomplete_for_new_user results = AuthSource.search(params[:term]) - - render :json => results.map {|result| { - 'value' => result[:login], - 'label' => "#{result[:login]} (#{result[:firstname]} #{result[:lastname]})", - 'login' => result[:login].to_s, - 'firstname' => result[:firstname].to_s, - 'lastname' => result[:lastname].to_s, - 'mail' => result[:mail].to_s, - 'auth_source_id' => result[:auth_source_id].to_s - }} + render :json => results.map {|result| + { + 'value' => result[:login], + 'label' => "#{result[:login]} (#{result[:firstname]} #{result[:lastname]})", + 'login' => result[:login].to_s, + 'firstname' => result[:firstname].to_s, + 'lastname' => result[:lastname].to_s, + 'mail' => result[:mail].to_s, + 'auth_source_id' => result[:auth_source_id].to_s + } + } end private diff --git a/app/controllers/auto_completes_controller.rb b/app/controllers/auto_completes_controller.rb index 2d707f9..d055476 100644 --- a/app/controllers/auto_completes_controller.rb +++ b/app/controllers/auto_completes_controller.rb @@ -1,5 +1,7 @@ +# frozen_string_literal: true + # Redmine - project management software -# Copyright (C) 2006-2017 Jean-Philippe Lang +# Copyright (C) 2006-2019 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -23,20 +25,18 @@ class AutoCompletesController < ApplicationController q = (params[:q] || params[:term]).to_s.strip status = params[:status].to_s issue_id = params[:issue_id].to_s - if q.present? - scope = Issue.cross_project_scope(@project, params[:scope]).visible - if status.present? - scope = scope.open(status == 'o') - end - if issue_id.present? - scope = scope.where.not(:id => issue_id.to_i) - end - if q.match(/\A#?(\d+)\z/) - issues << scope.find_by_id($1.to_i) - end + scope = Issue.cross_project_scope(@project, params[:scope]).visible + scope = scope.open(status == 'o') if status.present? + scope = scope.where.not(:id => issue_id.to_i) if issue_id.present? + if q.present? + if q =~ /\A#?(\d+)\z/ + issues << scope.find_by(:id => $1.to_i) + end issues += scope.like(q).order(:id => :desc).limit(10).to_a issues.compact! + else + issues += scope.order(:id => :desc).limit(10).to_a end render :json => format_issues_json(issues) @@ -53,10 +53,11 @@ class AutoCompletesController < ApplicationController end def format_issues_json(issues) - issues.map {|issue| { - 'id' => issue.id, - 'label' => "#{issue.tracker} ##{issue.id}: #{issue.subject.to_s.truncate(60)}", - 'value' => issue.id + issues.map {|issue| + { + 'id' => issue.id, + 'label' => "#{issue.tracker} ##{issue.id}: #{issue.subject.to_s.truncate(60)}", + 'value' => issue.id } } end diff --git a/app/controllers/boards_controller.rb b/app/controllers/boards_controller.rb index 03ca50c..bcafada 100644 --- a/app/controllers/boards_controller.rb +++ b/app/controllers/boards_controller.rb @@ -1,5 +1,7 @@ +# frozen_string_literal: true + # Redmine - project management software -# Copyright (C) 2006-2017 Jean-Philippe Lang +# Copyright (C) 2006-2019 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -108,7 +110,8 @@ class BoardsController < ApplicationController redirect_to_settings_in_projects end -private + private + def redirect_to_settings_in_projects redirect_to settings_project_path(@project, :tab => 'boards') end diff --git a/app/controllers/calendars_controller.rb b/app/controllers/calendars_controller.rb index e07ba2e..23e7488 100644 --- a/app/controllers/calendars_controller.rb +++ b/app/controllers/calendars_controller.rb @@ -1,5 +1,7 @@ +# frozen_string_literal: true + # Redmine - project management software -# Copyright (C) 2006-2017 Jean-Philippe Lang +# Copyright (C) 2006-2019 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/app/controllers/comments_controller.rb b/app/controllers/comments_controller.rb index e7974d4..5d7d664 100644 --- a/app/controllers/comments_controller.rb +++ b/app/controllers/comments_controller.rb @@ -1,5 +1,7 @@ +# frozen_string_literal: true + # Redmine - project management software -# Copyright (C) 2006-2017 Jean-Philippe Lang +# Copyright (C) 2006-2019 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/app/controllers/context_menus_controller.rb b/app/controllers/context_menus_controller.rb index bd69775..8622d15 100644 --- a/app/controllers/context_menus_controller.rb +++ b/app/controllers/context_menus_controller.rb @@ -1,5 +1,7 @@ +# frozen_string_literal: true + # Redmine - project management software -# Copyright (C) 2006-2017 Jean-Philippe Lang +# Copyright (C) 2006-2019 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -22,7 +24,7 @@ class ContextMenusController < ApplicationController before_action :find_issues, :only => :issues def issues - if (@issues.size == 1) + if @issues.size == 1 @issue = @issues.first end @issue_ids = @issues.map(&:id).sort @@ -43,6 +45,8 @@ class ContextMenusController < ApplicationController @priorities = IssuePriority.active.reverse @back = back_url + @columns = params[:c] + @options_by_custom_field = {} if @can[:edit] custom_fields = @issues.map(&:editable_custom_fields).reduce(:&).reject(&:multiple?).select {|field| field.format.bulk_edit_supported} @@ -64,7 +68,7 @@ class ContextMenusController < ApplicationController preload(:user).to_a (render_404; return) unless @time_entries.present? - if (@time_entries.size == 1) + if @time_entries.size == 1 @time_entry = @time_entries.first end diff --git a/app/controllers/custom_field_enumerations_controller.rb b/app/controllers/custom_field_enumerations_controller.rb index fc186b9..8c00edc 100644 --- a/app/controllers/custom_field_enumerations_controller.rb +++ b/app/controllers/custom_field_enumerations_controller.rb @@ -1,5 +1,7 @@ +# frozen_string_literal: true + # Redmine - project management software -# Copyright (C) 2006-2017 Jean-Philippe Lang +# Copyright (C) 2006-2019 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/app/controllers/custom_fields_controller.rb b/app/controllers/custom_fields_controller.rb index 41d4a73..f979f8b 100644 --- a/app/controllers/custom_fields_controller.rb +++ b/app/controllers/custom_fields_controller.rb @@ -1,5 +1,7 @@ +# frozen_string_literal: true + # Redmine - project management software -# Copyright (C) 2006-2017 Jean-Philippe Lang +# Copyright (C) 2006-2019 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -46,7 +48,11 @@ class CustomFieldsController < ApplicationController if @custom_field.save flash[:notice] = l(:notice_successful_create) call_hook(:controller_custom_fields_new_after_save, :params => params, :custom_field => @custom_field) - redirect_to edit_custom_field_path(@custom_field) + if params[:continue] + redirect_to new_custom_field_path({:type => @custom_field.type}) + else + redirect_to edit_custom_field_path(@custom_field) + end else render :action => 'new' end diff --git a/app/controllers/documents_controller.rb b/app/controllers/documents_controller.rb index dbf0e88..27cb1b2 100644 --- a/app/controllers/documents_controller.rb +++ b/app/controllers/documents_controller.rb @@ -1,5 +1,7 @@ +# frozen_string_literal: true + # Redmine - project management software -# Copyright (C) 2006-2017 Jean-Philippe Lang +# Copyright (C) 2006-2019 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -31,7 +33,8 @@ class DocumentsController < ApplicationController documents = @project.documents.includes(:attachments, :category).to_a case @sort_by when 'date' - @grouped = documents.group_by {|d| d.updated_on.to_date } + documents.sort!{|a,b| b.updated_on <=> a.updated_on} + @grouped = documents.group_by {|d| d.updated_on.to_date} when 'title' @grouped = documents.group_by {|d| d.title.first.upcase} when 'author' @@ -88,7 +91,7 @@ class DocumentsController < ApplicationController render_attachment_warning_if_needed(@document) if attachments.present? && attachments[:files].present? && Setting.notified_events.include?('document_added') - Mailer.attachments_added(attachments[:files]).deliver + Mailer.deliver_attachments_added(attachments[:files]) end redirect_to document_path(@document) end diff --git a/app/controllers/email_addresses_controller.rb b/app/controllers/email_addresses_controller.rb index 2202625..9af294b 100644 --- a/app/controllers/email_addresses_controller.rb +++ b/app/controllers/email_addresses_controller.rb @@ -1,5 +1,7 @@ +# frozen_string_literal: true + # Redmine - project management software -# Copyright (C) 2006-2017 Jean-Philippe Lang +# Copyright (C) 2006-2019 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/app/controllers/enumerations_controller.rb b/app/controllers/enumerations_controller.rb index a04d7b1..a3d4e47 100644 --- a/app/controllers/enumerations_controller.rb +++ b/app/controllers/enumerations_controller.rb @@ -1,5 +1,7 @@ +# frozen_string_literal: true + # Redmine - project management software -# Copyright (C) 2006-2017 Jean-Philippe Lang +# Copyright (C) 2006-2019 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -57,7 +59,7 @@ class EnumerationsController < ApplicationController end def update - if @enumeration.update_attributes(enumeration_params) + if @enumeration.update(enumeration_params) respond_to do |format| format.html { flash[:notice] = l(:notice_successful_update) @@ -107,7 +109,7 @@ class EnumerationsController < ApplicationController def enumeration_params # can't require enumeration on #new action - cf_ids = @enumeration.available_custom_fields.map{|c| c.id.to_s} + cf_ids = @enumeration.available_custom_fields.map {|c| c.multiple? ? {c.id.to_s => []} : c.id.to_s} params.permit(:enumeration => [:name, :active, :is_default, :position, :custom_field_values => cf_ids])[:enumeration] end end diff --git a/app/controllers/files_controller.rb b/app/controllers/files_controller.rb index 7c59665..00b0dca 100644 --- a/app/controllers/files_controller.rb +++ b/app/controllers/files_controller.rb @@ -1,5 +1,7 @@ +# frozen_string_literal: true + # Redmine - project management software -# Copyright (C) 2006-2017 Jean-Philippe Lang +# Copyright (C) 2006-2019 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -44,7 +46,7 @@ class FilesController < ApplicationController end def new - @versions = @project.versions.sort + @versions = @project.versions.sorted end def create @@ -55,12 +57,13 @@ class FilesController < ApplicationController if attachments[:files].present? if Setting.notified_events.include?('file_added') - Mailer.attachments_added(attachments[:files]).deliver + Mailer.deliver_attachments_added(attachments[:files]) end respond_to do |format| format.html { flash[:notice] = l(:label_file_added) - redirect_to project_files_path(@project) } + redirect_to project_files_path(@project) + } format.api { render_api_ok } end else @@ -68,7 +71,8 @@ class FilesController < ApplicationController format.html { flash.now[:error] = l(:label_attachment) + " " + l('activerecord.errors.messages.invalid') new - render :action => 'new' } + render :action => 'new' + } format.api { render :status => :bad_request } end end diff --git a/app/controllers/gantts_controller.rb b/app/controllers/gantts_controller.rb index 3283282..04a9e35 100644 --- a/app/controllers/gantts_controller.rb +++ b/app/controllers/gantts_controller.rb @@ -1,5 +1,7 @@ +# frozen_string_literal: true + # Redmine - project management software -# Copyright (C) 2006-2017 Jean-Philippe Lang +# Copyright (C) 2006-2019 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index 4379ee3..5cb80e2 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -1,5 +1,7 @@ +# frozen_string_literal: true + # Redmine - project management software -# Copyright (C) 2006-2017 Jean-Philippe Lang +# Copyright (C) 2006-2019 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/app/controllers/imports_controller.rb b/app/controllers/imports_controller.rb index 96589ac..633cc23 100644 --- a/app/controllers/imports_controller.rb +++ b/app/controllers/imports_controller.rb @@ -1,5 +1,7 @@ +# frozen_string_literal: true + # Redmine - project management software -# Copyright (C) 2006-2017 Jean-Philippe Lang +# Copyright (C) 2006-2019 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -18,22 +20,23 @@ require 'csv' class ImportsController < ApplicationController - menu_item :issues - before_action :find_import, :only => [:show, :settings, :mapping, :run] - before_action :authorize_global + before_action :authorize_import + + layout :import_layout helper :issues helper :queries def new + @import = import_type.new end def create - @import = IssueImport.new + @import = import_type.new @import.user = User.current @import.file = params[:file] - @import.set_default_settings + @import.set_default_settings(:project_id => params[:project_id]) if @import.save redirect_to import_settings_path(@import) @@ -50,10 +53,12 @@ class ImportsController < ApplicationController redirect_to import_mapping_path(@import) end - rescue CSV::MalformedCSVError => e - flash.now[:error] = l(:error_invalid_csv_file_or_settings) - rescue ArgumentError, EncodingError => e - flash.now[:error] = l(:error_invalid_file_encoding, :encoding => ERB::Util.h(@import.settings['encoding'])) + rescue CSV::MalformedCSVError, EncodingError => e + if e.is_a?(CSV::MalformedCSVError) && e.message !~ /Invalid byte sequence/ + flash.now[:error] = l(:error_invalid_csv_file_or_settings) + else + flash.now[:error] = l(:error_invalid_file_encoding, :encoding => ERB::Util.h(@import.settings['encoding'])) + end rescue SystemCallError => e flash.now[:error] = l(:error_can_not_read_import_file) end @@ -94,6 +99,14 @@ class ImportsController < ApplicationController end end + def current_menu(project) + if import_layout == 'admin' + nil + else + :application_menu + end + end + private def find_import @@ -109,9 +122,9 @@ class ImportsController < ApplicationController end def update_from_params - if params[:import_settings].is_a?(Hash) + if params[:import_settings].present? @import.settings ||= {} - @import.settings.merge!(params[:import_settings]) + @import.settings.merge!(params[:import_settings].to_unsafe_hash) @import.save! end end @@ -119,4 +132,31 @@ class ImportsController < ApplicationController def max_items_per_request 5 end + + def import_layout + import_type && import_type.layout || 'base' + end + + def menu_items + menu_item = import_type ? import_type.menu_item : nil + + { self.controller_name.to_sym => { :actions => {}, :default => menu_item } } + end + + def authorize_import + return render_404 unless import_type + return render_403 unless import_type.authorized?(User.current) + end + + def import_type + return @import_type if defined? @import_type + + @import_type = + if @import + @import.class + else + type = Object.const_get(params[:type]) rescue nil + type && type < Import ? type : nil + end + end end diff --git a/app/controllers/issue_categories_controller.rb b/app/controllers/issue_categories_controller.rb index d635a28..f5decdd 100644 --- a/app/controllers/issue_categories_controller.rb +++ b/app/controllers/issue_categories_controller.rb @@ -1,5 +1,7 @@ +# frozen_string_literal: true + # Redmine - project management software -# Copyright (C) 2006-2017 Jean-Philippe Lang +# Copyright (C) 2006-2019 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/app/controllers/issue_relations_controller.rb b/app/controllers/issue_relations_controller.rb index 0bcc8c5..ee197d0 100644 --- a/app/controllers/issue_relations_controller.rb +++ b/app/controllers/issue_relations_controller.rb @@ -1,5 +1,7 @@ +# frozen_string_literal: true + # Redmine - project management software -# Copyright (C) 2006-2017 Jean-Philippe Lang +# Copyright (C) 2006-2019 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/app/controllers/issue_statuses_controller.rb b/app/controllers/issue_statuses_controller.rb index 92c0337..f9d051a 100644 --- a/app/controllers/issue_statuses_controller.rb +++ b/app/controllers/issue_statuses_controller.rb @@ -1,5 +1,7 @@ +# frozen_string_literal: true + # Redmine - project management software -# Copyright (C) 2006-2017 Jean-Philippe Lang +# Copyright (C) 2006-2019 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -72,8 +74,8 @@ class IssueStatusesController < ApplicationController def destroy IssueStatus.find(params[:id]).destroy redirect_to issue_statuses_path - rescue - flash[:error] = l(:error_unable_delete_issue_status) + rescue => e + flash[:error] = l(:error_unable_delete_issue_status, ERB::Util.h(e.message)) redirect_to issue_statuses_path end diff --git a/app/controllers/issues_controller.rb b/app/controllers/issues_controller.rb index 69a947b..65caee6 100644 --- a/app/controllers/issues_controller.rb +++ b/app/controllers/issues_controller.rb @@ -1,5 +1,7 @@ +# frozen_string_literal: true + # Redmine - project management software -# Copyright (C) 2006-2017 Jean-Philippe Lang +# Copyright (C) 2006-2019 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -18,7 +20,7 @@ class IssuesController < ApplicationController default_search_scope :issues - before_action :find_issue, :only => [:show, :edit, :update] + before_action :find_issue, :only => [:show, :edit, :update, :issue_tab] before_action :find_issues, :only => [:bulk_edit, :bulk_update, :destroy] before_action :authorize, :except => [:index, :new, :create] before_action :find_optional_project, :only => [:index, :new, :create] @@ -84,13 +86,10 @@ class IssuesController < ApplicationController def show @journals = @issue.visible_journals_with_index - @changesets = @issue.changesets.visible.preload(:repository, :user).to_a + @has_changesets = @issue.changesets.visible.preload(:repository, :user).exists? @relations = @issue.relations.select {|r| r.other_issue(@issue) && r.other_issue(@issue).visible? } - if User.current.wants_comments_in_reverse_order? - @journals.reverse! - @changesets.reverse! - end + @journals.reverse! if User.current.wants_comments_in_reverse_order? if User.current.allowed_to?(:view_time_entries, @project) Issue.load_visible_spent_hours([@issue]) @@ -102,11 +101,15 @@ class IssuesController < ApplicationController @allowed_statuses = @issue.new_statuses_allowed_to(User.current) @priorities = IssuePriority.active @time_entry = TimeEntry.new(:issue => @issue, :project => @issue.project) + @time_entries = @issue.time_entries.visible.preload(:activity, :user) @relation = IssueRelation.new retrieve_previous_and_next_issue_ids render :template => 'issues/show' } - format.api + format.api { + @changesets = @issue.changesets.visible.preload(:repository, :user).to_a + @changesets.reverse! if User.current.wants_comments_in_reverse_order? + } format.atom { render :template => 'journals/index', :layout => false, :content_type => 'application/atom+xml' } format.pdf { send_file_headers! :type => 'application/pdf', :filename => "#{@project.identifier}-#{@issue.id}.pdf" @@ -177,7 +180,7 @@ class IssuesController < ApplicationController if saved render_attachment_warning_if_needed(@issue) - flash[:notice] = l(:notice_successful_update) unless @issue.current_journal.new_record? + flash[:notice] = l(:notice_successful_update) unless @issue.current_journal.new_record? || params[:no_flash] respond_to do |format| format.html { redirect_back_or_default issue_path(@issue, previous_and_next_issue_ids_params) } @@ -191,6 +194,21 @@ class IssuesController < ApplicationController end end + def issue_tab + return render_error :status => 422 unless request.xhr? + tab = params[:name] + + case tab + when 'time_entries' + @time_entries = @issue.time_entries.visible.preload(:activity, :user).to_a + render :partial => 'issues/tabs/time_entries', :locals => {:time_entries => @time_entries} + when 'changesets' + @changesets = @issue.changesets.visible.preload(:repository, :user).to_a + @changesets.reverse! if User.current.wants_comments_in_reverse_order? + render :partial => 'issues/tabs/changesets', :locals => {:changesets => @changesets} + end + end + # Bulk edit/copy a set of issues def bulk_edit @issues.sort! @@ -259,14 +277,16 @@ class IssuesController < ApplicationController end @values_by_custom_field.delete_if {|k,v| v.blank?} - @custom_fields = edited_issues.map{|i|i.editable_custom_fields}.reduce(:&).select {|field| field.format.bulk_edit_supported} + @custom_fields = edited_issues.map{|i| i.editable_custom_fields}.reduce(:&).select {|field| field.format.bulk_edit_supported} @assignables = target_projects.map(&:assignable_users).reduce(:&) @versions = target_projects.map {|p| p.shared_versions.open}.reduce(:&) @categories = target_projects.map {|p| p.issue_categories}.reduce(:&) if @copy @attachments_present = @issues.detect {|i| i.attachments.any?}.present? @subtasks_present = @issues.detect {|i| !i.leaf?}.present? - @watchers_present = User.current.allowed_to?(:add_issue_watchers, @projects) && Watcher.where(:watchable_type => 'Issue', :watchable_id => @issues.map(&:id)).exists? + @watchers_present = User.current.allowed_to?(:add_issue_watchers, @projects) && + Watcher.where(:watchable_type => 'Issue', + :watchable_id => @issues.map(&:id)).exists? end @safe_attributes = edited_issues.map(&:safe_attribute_names).reduce(:&) @@ -316,7 +336,8 @@ class IssuesController < ApplicationController @issues.each do |orig_issue| orig_issue.reload if @copy - issue = orig_issue.copy({}, + issue = orig_issue.copy( + {}, :attachments => copy_attachments, :subtasks => copy_subtasks, :watchers => copy_watchers, @@ -464,6 +485,7 @@ class IssuesController < ApplicationController @issue.init_journal(User.current) issue_attributes = params[:issue] + issue_attributes[:assigned_to_id] = User.current.id if issue_attributes && issue_attributes[:assigned_to_id] == 'me' if issue_attributes && params[:conflict_resolution] case params[:conflict_resolution] when 'overwrite' @@ -520,6 +542,7 @@ class IssuesController < ApplicationController # so we can use the default version for the new project attrs.delete(:fixed_version_id) end + attrs[:assigned_to_id] = User.current.id if attrs[:assigned_to_id] == 'me' @issue.safe_attributes = attrs if @issue.project @@ -554,6 +577,7 @@ class IssuesController < ApplicationController time_entry = @time_entry || TimeEntry.new time_entry.project = @issue.project time_entry.issue = @issue + time_entry.author = User.current time_entry.user = User.current time_entry.spent_on = User.current.today time_entry.safe_attributes = params[:time_entry] diff --git a/app/controllers/journals_controller.rb b/app/controllers/journals_controller.rb index 7f07b38..7a477a0 100644 --- a/app/controllers/journals_controller.rb +++ b/app/controllers/journals_controller.rb @@ -1,5 +1,7 @@ +# frozen_string_literal: true + # Redmine - project management software -# Copyright (C) 2006-2017 Jean-Philippe Lang +# Copyright (C) 2006-2019 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -64,13 +66,14 @@ class JournalsController < ApplicationController if @journal user = @journal.user text = @journal.notes + @content = +"#{ll(Setting.default_language, :text_user_wrote_in, {:value => user, :link => "#note-#{params[:journal_indice]}"})}\n> " else user = @issue.author text = @issue.description + @content = +"#{ll(Setting.default_language, :text_user_wrote, user)}\n> " end # Replaces pre blocks with [...] text = text.to_s.strip.gsub(%r{
(.*?)
}m, '[...]') - @content = "#{ll(Setting.default_language, :text_user_wrote, user)}\n> " @content << text.gsub(/(\r?\n|\r\n?)/, "\n> ") + "\n\n" rescue ActiveRecord::RecordNotFound render_404 diff --git a/app/controllers/mail_handler_controller.rb b/app/controllers/mail_handler_controller.rb index ccb832e..389cd6f 100644 --- a/app/controllers/mail_handler_controller.rb +++ b/app/controllers/mail_handler_controller.rb @@ -1,5 +1,7 @@ +# frozen_string_literal: true + # Redmine - project management software -# Copyright (C) 2006-2017 Jean-Philippe Lang +# Copyright (C) 2006-2019 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -26,7 +28,7 @@ class MailHandlerController < ActionController::Base def index options = params.dup email = options.delete(:email) - if MailHandler.receive(email, options) + if MailHandler.safe_receive(email, options) head :created else head :unprocessable_entity diff --git a/app/controllers/members_controller.rb b/app/controllers/members_controller.rb index 1583e9e..119bf8e 100644 --- a/app/controllers/members_controller.rb +++ b/app/controllers/members_controller.rb @@ -1,5 +1,7 @@ +# frozen_string_literal: true + # Redmine - project management software -# Copyright (C) 2006-2017 Jean-Philippe Lang +# Copyright (C) 2006-2019 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/app/controllers/messages_controller.rb b/app/controllers/messages_controller.rb index 76bc19c..bbc6a35 100644 --- a/app/controllers/messages_controller.rb +++ b/app/controllers/messages_controller.rb @@ -1,5 +1,7 @@ +# frozen_string_literal: true + # Redmine - project management software -# Copyright (C) 2006-2017 Jean-Philippe Lang +# Copyright (C) 2006-2019 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -63,6 +65,7 @@ class MessagesController < ApplicationController if @message.save call_hook(:controller_messages_new_after_save, { :params => params, :message => @message}) render_attachment_warning_if_needed(@message) + flash[:notice] = l(:notice_successful_create) redirect_to board_message_path(@board, @message) end end @@ -80,6 +83,7 @@ class MessagesController < ApplicationController attachments = Attachment.attach_files(@reply, params[:attachments]) render_attachment_warning_if_needed(@reply) end + flash[:notice] = l(:notice_successful_update) redirect_to board_message_path(@board, @topic, :r => @reply) end @@ -101,6 +105,7 @@ class MessagesController < ApplicationController (render_403; return false) unless @message.destroyable_by?(User.current) r = @message.to_param @message.destroy + flash[:notice] = l(:notice_successful_delete) if @message.parent redirect_to board_message_path(@board, @message.parent, :r => r) else @@ -112,18 +117,23 @@ class MessagesController < ApplicationController @subject = @message.subject @subject = "RE: #{@subject}" unless @subject.starts_with?('RE:') - @content = "#{ll(Setting.default_language, :text_user_wrote, @message.author)}\n> " + if @message.root == @message + @content = +"#{ll(Setting.default_language, :text_user_wrote, @message.author)}\n> " + else + @content = +"#{ll(Setting.default_language, :text_user_wrote_in, {:value => @message.author, :link => "message##{@message.id}"})}\n> " + end @content << @message.content.to_s.strip.gsub(%r{
(.*?)
}m, '[...]').gsub(/(\r?\n|\r\n?)/, "\n> ") + "\n\n" end def preview message = @board.messages.find_by_id(params[:id]) - @text = (params[:message] || params[:reply])[:content] + @text = params[:text] ? params[:text] : nil @previewed = message render :partial => 'common/preview' end -private + private + def find_message return unless find_board @message = @board.messages.includes(:parent).find(params[:id]) diff --git a/app/controllers/my_controller.rb b/app/controllers/my_controller.rb index bf04d55..606fd5c 100644 --- a/app/controllers/my_controller.rb +++ b/app/controllers/my_controller.rb @@ -1,5 +1,7 @@ +# frozen_string_literal: true + # Redmine - project management software -# Copyright (C) 2006-2017 Jean-Philippe Lang +# Copyright (C) 2006-2019 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -21,13 +23,17 @@ class MyController < ApplicationController # let user change user's password when user has to skip_before_action :check_password_change, :only => :password - require_sudo_mode :account, only: :post + accept_api_auth :account + + require_sudo_mode :account, only: :put require_sudo_mode :reset_rss_key, :reset_api_key, :show_api_key, :destroy helper :issues helper :users helper :custom_fields helper :queries + helper :activities + helper :calendars def index page @@ -45,15 +51,25 @@ class MyController < ApplicationController def account @user = User.current @pref = @user.pref - if request.post? + if request.put? @user.safe_attributes = params[:user] @user.pref.safe_attributes = params[:pref] if @user.save @user.pref.save set_language_if_valid @user.language - flash[:notice] = l(:notice_account_updated) - redirect_to my_account_path + respond_to do |format| + format.html { + flash[:notice] = l(:notice_account_updated) + redirect_to my_account_path + } + format.api { render_api_ok } + end return + else + respond_to do |format| + format.html { render :action => :account } + format.api { render_validation_errors(@user) } + end end end end @@ -95,7 +111,7 @@ class MyController < ApplicationController if @user.save # The session token was destroyed by the password change, generate a new one session[:tk] = @user.generate_session_token - Mailer.password_updated(@user) + Mailer.deliver_password_updated(@user, User.current) flash[:notice] = l(:notice_account_password_updated) redirect_to my_account_path end @@ -138,7 +154,7 @@ class MyController < ApplicationController block_settings = params[:settings] || {} block_settings.each do |block, settings| - @user.pref.update_block_settings(block, settings) + @user.pref.update_block_settings(block, settings.to_unsafe_hash) end @user.pref.save @updated_blocks = block_settings.keys diff --git a/app/controllers/news_controller.rb b/app/controllers/news_controller.rb index 3df9e5e..5d63f44 100644 --- a/app/controllers/news_controller.rb +++ b/app/controllers/news_controller.rb @@ -1,5 +1,7 @@ +# frozen_string_literal: true + # Redmine - project management software -# Copyright (C) 2006-2017 Jean-Philippe Lang +# Copyright (C) 2006-2019 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -24,7 +26,7 @@ class NewsController < ApplicationController before_action :authorize, :except => [:index] before_action :find_optional_project, :only => :index accept_rss_auth :index - accept_api_auth :index + accept_api_auth :index, :show, :create, :update, :destroy helper :watchers helper :attachments @@ -69,13 +71,21 @@ class NewsController < ApplicationController def create @news = News.new(:project => @project, :author => User.current) @news.safe_attributes = params[:news] - @news.save_attachments(params[:attachments]) + @news.save_attachments(params[:attachments] || (params[:news] && params[:news][:uploads])) if @news.save - render_attachment_warning_if_needed(@news) - flash[:notice] = l(:notice_successful_create) - redirect_to project_news_index_path(@project) + respond_to do |format| + format.html { + render_attachment_warning_if_needed(@news) + flash[:notice] = l(:notice_successful_create) + redirect_to project_news_index_path(@project) + } + format.api { render_api_ok } + end else - render :action => 'new' + respond_to do |format| + format.html { render :action => 'new' } + format.api { render_validation_errors(@news) } + end end end @@ -84,18 +94,29 @@ class NewsController < ApplicationController def update @news.safe_attributes = params[:news] - @news.save_attachments(params[:attachments]) + @news.save_attachments(params[:attachments] || (params[:news] && params[:news][:uploads])) if @news.save - render_attachment_warning_if_needed(@news) - flash[:notice] = l(:notice_successful_update) - redirect_to news_path(@news) + respond_to do |format| + format.html { + render_attachment_warning_if_needed(@news) + flash[:notice] = l(:notice_successful_update) + redirect_to news_path(@news) + } + format.api { render_api_ok } + end else - render :action => 'edit' + respond_to do |format| + format.html { render :action => 'edit' } + format.api { render_validation_errors(@news) } + end end end def destroy @news.destroy - redirect_to project_news_index_path(@project) + respond_to do |format| + format.html { redirect_to project_news_index_path(@project) } + format.api { render_api_ok } + end end end diff --git a/app/controllers/previews_controller.rb b/app/controllers/previews_controller.rb index 37cdc46..f1ef5fa 100644 --- a/app/controllers/previews_controller.rb +++ b/app/controllers/previews_controller.rb @@ -1,5 +1,7 @@ +# frozen_string_literal: true + # Redmine - project management software -# Copyright (C) 2006-2017 Jean-Philippe Lang +# Copyright (C) 2006-2019 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -16,28 +18,28 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. class PreviewsController < ApplicationController - before_action :find_project, :find_attachments + before_action :find_project, :except => :text + before_action :find_attachments def issue - @issue = Issue.visible.find_by_id(params[:id]) unless params[:id].blank? + @issue = Issue.visible.find_by_id(params[:issue_id]) unless params[:issue_id].blank? if @issue - @description = params[:issue] && params[:issue][:description] - if @description && @description.gsub(/(\r?\n|\n\r?)/, "\n") == @issue.description.to_s.gsub(/(\r?\n|\n\r?)/, "\n") - @description = nil - end - @notes = params[:journal] ? params[:journal][:notes] : nil - @notes ||= params[:issue] ? params[:issue][:notes] : nil - else - @description = (params[:issue] ? params[:issue][:description] : nil) + @previewed = @issue end - render :layout => false + @text = params[:text] ? params[:text] : nil + render :partial => 'common/preview' end def news if params[:id].present? && news = News.visible.find_by_id(params[:id]) @previewed = news end - @text = (params[:news] ? params[:news][:description] : nil) + @text = params[:text] ? params[:text] : nil + render :partial => 'common/preview' + end + + def text + @text = params[:text] ? params[:text] : nil render :partial => 'common/preview' end diff --git a/app/controllers/principal_memberships_controller.rb b/app/controllers/principal_memberships_controller.rb index 924ecbd..fdbd8be 100644 --- a/app/controllers/principal_memberships_controller.rb +++ b/app/controllers/principal_memberships_controller.rb @@ -1,5 +1,7 @@ +# frozen_string_literal: true + # Redmine - project management software -# Copyright (C) 2006-2017 Jean-Philippe Lang +# Copyright (C) 2006-2019 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -19,6 +21,8 @@ class PrincipalMembershipsController < ApplicationController layout 'admin' self.main_menu = false + helper :members + before_action :require_admin before_action :find_principal, :only => [:new, :create] before_action :find_membership, :only => [:edit, :update, :destroy] diff --git a/app/controllers/project_enumerations_controller.rb b/app/controllers/project_enumerations_controller.rb index f68d948..8b2768e 100644 --- a/app/controllers/project_enumerations_controller.rb +++ b/app/controllers/project_enumerations_controller.rb @@ -1,5 +1,7 @@ +# frozen_string_literal: true + # Redmine - project management software -# Copyright (C) 2006-2017 Jean-Philippe Lang +# Copyright (C) 2006-2019 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -20,15 +22,8 @@ class ProjectEnumerationsController < ApplicationController before_action :authorize def update - if params[:enumerations] - saved = Project.transaction do - params[:enumerations].each do |id, activity| - @project.update_or_create_time_entry_activity(id, activity) - end - end - if saved - flash[:notice] = l(:notice_successful_update) - end + if @project.update_or_create_time_entry_activities(update_params) + flash[:notice] = l(:notice_successful_update) end redirect_to settings_project_path(@project, :tab => 'activities') @@ -41,4 +36,12 @@ class ProjectEnumerationsController < ApplicationController flash[:notice] = l(:notice_successful_update) redirect_to settings_project_path(@project, :tab => 'activities') end + + private + + def update_params + params. + permit(:enumerations => [:parent_id, :active, {:custom_field_values => {}}]). + require(:enumerations) + end end diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 3e3fc69..c5c98d6 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -1,5 +1,7 @@ +# frozen_string_literal: true + # Redmine - project management software -# Copyright (C) 2006-2017 Jean-Philippe Lang +# Copyright (C) 2006-2019 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -31,8 +33,12 @@ class ProjectsController < ApplicationController helper :custom_fields helper :issues helper :queries + include QueriesHelper + helper :projects_queries + include ProjectsQueriesHelper helper :repositories helper :members + helper :trackers # Lists visible projects def index @@ -41,14 +47,19 @@ class ProjectsController < ApplicationController return end - scope = Project.visible.sorted + retrieve_project_query + scope = project_scope respond_to do |format| format.html { - unless params[:closed] - scope = scope.active + # TODO: see what to do with the board view and pagination + if @query.display_type == 'board' + @entries = scope.to_a + else + @entry_count = scope.count + @entry_pages = Paginator.new @entry_count, per_page_option, params['page'] + @entries = scope.offset(@entry_pages.offset).limit(@entry_pages.per_page).to_a end - @projects = scope.to_a } format.api { @offset, @limit = api_offset_and_limit @@ -59,6 +70,11 @@ class ProjectsController < ApplicationController projects = scope.reorder(:created_on => :desc).limit(Setting.feeds_limit.to_i).to_a render_feed(projects, :title => "#{Setting.app_title}: #{l(:label_project_latest)}") } + format.csv { + # Export all entries + @entries = scope.to_a + send_data(query_to_csv(@entries, @query, params), :type => 'text/csv; header=present', :filename => 'projects.csv') + } end end @@ -158,6 +174,7 @@ class ProjectsController < ApplicationController if User.current.allowed_to_view_all_time_entries?(@project) @total_hours = TimeEntry.visible.where(cond).sum(:hours).to_f + @total_estimated_hours = Issue.visible.where(cond).sum(:estimated_hours).to_f end @key = User.current.rss_key @@ -176,8 +193,7 @@ class ProjectsController < ApplicationController @version_status = params[:version_status] || 'open' @version_name = params[:version_name] - @versions = @project.shared_versions.status(@version_status).like(@version_name) - @wiki ||= @project.wiki || Wiki.new(:project => @project) + @versions = @project.shared_versions.status(@version_status).like(@version_name).sorted end def edit @@ -189,7 +205,7 @@ class ProjectsController < ApplicationController respond_to do |format| format.html { flash[:notice] = l(:notice_successful_update) - redirect_to settings_project_path(@project) + redirect_to settings_project_path(@project, params[:tab]) } format.api { render_api_ok } end @@ -204,12 +220,6 @@ class ProjectsController < ApplicationController end end - def modules - @project.enabled_module_names = params[:enabled_module_names] - flash[:notice] = l(:notice_successful_update) - redirect_to settings_project_path(@project, :tab => 'modules') - end - def archive unless @project.archive flash[:error] = l(:error_can_not_archive_project) @@ -224,6 +234,19 @@ class ProjectsController < ApplicationController redirect_to_referer_or admin_projects_path(:status => params[:status]) end + def bookmark + jump_box = Redmine::ProjectJumpBox.new User.current + if request.delete? + jump_box.delete_project_bookmark @project + elsif request.post? + jump_box.bookmark_project @project + end + respond_to do |format| + format.js + format.html { redirect_to project_path(@project) } + end + end + def close @project.close redirect_to project_path(@project) @@ -247,4 +270,15 @@ class ProjectsController < ApplicationController # hide project in layout @project = nil end + + private + + # Returns the ProjectEntry scope for index + def project_scope(options={}) + @query.results_scope(options) + end + + def retrieve_project_query + retrieve_query(ProjectQuery, false, :defaults => @default_columns_names) + end end diff --git a/app/controllers/queries_controller.rb b/app/controllers/queries_controller.rb index 54f695f..56283dc 100644 --- a/app/controllers/queries_controller.rb +++ b/app/controllers/queries_controller.rb @@ -1,5 +1,7 @@ +# frozen_string_literal: true + # Redmine - project management software -# Copyright (C) 2006-2017 Jean-Philippe Lang +# Copyright (C) 2006-2019 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -104,6 +106,10 @@ class QueriesController < ApplicationController render_404 end + def current_menu_item + @query ? @query.queried_class.to_s.underscore.pluralize.to_sym : nil + end + private def find_query @@ -114,18 +120,11 @@ class QueriesController < ApplicationController render_404 end - def find_optional_project - @project = Project.find(params[:project_id]) if params[:project_id] - render_403 unless User.current.allowed_to?(:save_queries, @project, :global => true) - rescue ActiveRecord::RecordNotFound - render_404 - end - def update_query_from_params @query.project = params[:query_is_for_all] ? nil : @project @query.build_from_params(params) @query.column_names = nil if params[:default_columns] - @query.sort_criteria = params[:query] && params[:query][:sort_criteria] + @query.sort_criteria = (params[:query] && params[:query][:sort_criteria]) || @query.sort_criteria @query.name = params[:query] && params[:query][:name] if User.current.allowed_to?(:manage_public_queries, @query.project) || User.current.admin? @query.visibility = (params[:query] && params[:query][:visibility]) || Query::VISIBILITY_PRIVATE @@ -157,6 +156,10 @@ class QueriesController < ApplicationController redirect_to _time_entries_path(@project, nil, options) end + def redirect_to_project_query(options) + redirect_to projects_path(options) + end + # Returns the Query subclass, IssueQuery by default # for compatibility with previous behaviour def query_class diff --git a/app/controllers/reports_controller.rb b/app/controllers/reports_controller.rb index 08c30ef..6918cb3 100644 --- a/app/controllers/reports_controller.rb +++ b/app/controllers/reports_controller.rb @@ -1,5 +1,7 @@ +# frozen_string_literal: true + # Redmine - project management software -# Copyright (C) 2006-2017 Jean-Philippe Lang +# Copyright (C) 2006-2019 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -21,55 +23,56 @@ class ReportsController < ApplicationController def issue_report @trackers = @project.rolled_up_trackers(false).visible - @versions = @project.shared_versions.sort + @versions = @project.shared_versions.sorted @priorities = IssuePriority.all.reverse @categories = @project.issue_categories - @assignees = (Setting.issue_group_assignment? ? @project.principals : @project.users).sort - @authors = @project.users.sort + @assignees = (Setting.issue_group_assignment? ? @project.principals : @project.users).sorted + @authors = @project.users.sorted @subprojects = @project.descendants.visible - - @issues_by_tracker = Issue.by_tracker(@project) - @issues_by_version = Issue.by_version(@project) - @issues_by_priority = Issue.by_priority(@project) - @issues_by_category = Issue.by_category(@project) - @issues_by_assigned_to = Issue.by_assigned_to(@project) - @issues_by_author = Issue.by_author(@project) + with_subprojects = Setting.display_subprojects_issues? + @issues_by_tracker = Issue.by_tracker(@project, with_subprojects) + @issues_by_version = Issue.by_version(@project, with_subprojects) + @issues_by_priority = Issue.by_priority(@project, with_subprojects) + @issues_by_category = Issue.by_category(@project, with_subprojects) + @issues_by_assigned_to = Issue.by_assigned_to(@project, with_subprojects) + @issues_by_author = Issue.by_author(@project, with_subprojects) @issues_by_subproject = Issue.by_subproject(@project) || [] render :template => "reports/issue_report" end def issue_report_details + with_subprojects = Setting.display_subprojects_issues? case params[:detail] when "tracker" @field = "tracker_id" @rows = @project.rolled_up_trackers(false).visible - @data = Issue.by_tracker(@project) + @data = Issue.by_tracker(@project, with_subprojects) @report_title = l(:field_tracker) when "version" @field = "fixed_version_id" - @rows = @project.shared_versions.sort - @data = Issue.by_version(@project) + @rows = @project.shared_versions.sorted + @data = Issue.by_version(@project, with_subprojects) @report_title = l(:field_version) when "priority" @field = "priority_id" @rows = IssuePriority.all.reverse - @data = Issue.by_priority(@project) + @data = Issue.by_priority(@project, with_subprojects) @report_title = l(:field_priority) when "category" @field = "category_id" @rows = @project.issue_categories - @data = Issue.by_category(@project) + @data = Issue.by_category(@project, with_subprojects) @report_title = l(:field_category) when "assigned_to" @field = "assigned_to_id" - @rows = (Setting.issue_group_assignment? ? @project.principals : @project.users).sort - @data = Issue.by_assigned_to(@project) + @rows = (Setting.issue_group_assignment? ? @project.principals : @project.users).sorted + @data = Issue.by_assigned_to(@project, with_subprojects) @report_title = l(:field_assigned_to) when "author" @field = "author_id" - @rows = @project.users.sort - @data = Issue.by_author(@project) + @rows = @project.users.sorted + @data = Issue.by_author(@project, with_subprojects) @report_title = l(:field_author) when "subproject" @field = "project_id" @@ -84,6 +87,6 @@ class ReportsController < ApplicationController private def find_issue_statuses - @statuses = IssueStatus.sorted.to_a + @statuses = @project.rolled_up_statuses.sorted.to_a end end diff --git a/app/controllers/repositories_controller.rb b/app/controllers/repositories_controller.rb index 29a3b59..ef903f2 100644 --- a/app/controllers/repositories_controller.rb +++ b/app/controllers/repositories_controller.rb @@ -1,5 +1,7 @@ +# frozen_string_literal: true + # Redmine - project management software -# Copyright (C) 2006-2017 Jean-Philippe Lang +# Copyright (C) 2006-2019 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -15,13 +17,11 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -require 'SVG/Graph/Bar' -require 'SVG/Graph/BarHorizontal' require 'digest/sha1' require 'redmine/scm/adapters' -class ChangesetNotFound < Exception; end -class InvalidRevisionParam < Exception; end +class ChangesetNotFound < StandardError; end +class InvalidRevisionParam < StandardError; end class RepositoriesController < ApplicationController menu_item :repository @@ -148,6 +148,13 @@ class RepositoriesController < ApplicationController send_opt[:disposition] = disposition(@path) send_data @repository.cat(@path, @rev), send_opt else + # set up pagination from entry to entry + parent_path = @path.split('/')[0...-1].join('/') + @entries = @repository.entries(parent_path, @rev).reject(&:is_dir?) + if index = @entries.index{|e| e.name == @entry.name} + @paginator = Redmine::Pagination::Paginator.new(@entries.size, 1, index+1) + end + if !@entry.size || @entry.size <= Setting.file_max_size_displayed.to_i.kilobyte content = @repository.cat(@path, @rev) (show_error_not_found; return) unless content @@ -232,7 +239,7 @@ class RepositoriesController < ApplicationController if params[:format] == 'diff' @diff = @repository.diff(@path, @rev, @rev_to) (show_error_not_found; return) unless @diff - filename = "changeset_r#{@rev}" + filename = +"changeset_r#{@rev}" filename << "_r#{@rev_to}" if @rev_to send_data @diff.join, :filename => "#{filename}.diff", :type => 'text/x-patch', @@ -250,18 +257,20 @@ class RepositoriesController < ApplicationController Digest::MD5.hexdigest("#{@path}-#{@rev}-#{@rev_to}-#{@diff_type}-#{current_language}") unless read_fragment(@cache_key) @diff = @repository.diff(@path, @rev, @rev_to) - show_error_not_found unless @diff + (show_error_not_found; return) unless @diff end @changeset = @repository.find_changeset_by_name(@rev) @changeset_to = @rev_to ? @repository.find_changeset_by_name(@rev_to) : nil @diff_format_revisions = @repository.diff_format_revisions(@changeset, @changeset_to) + render :diff, :formats => :html, :layout => 'base.html.erb' end end def stats end + # Returns JSON data for repository graphs def graph data = nil case params[:graph] @@ -271,8 +280,7 @@ class RepositoriesController < ApplicationController data = graph_commits_per_author(@repository) end if data - headers["Content-Type"] = "image/svg+xml" - send_data(data, :type => "image/svg+xml", :disposition => "inline") + render :json => data else render_404 end @@ -313,7 +321,7 @@ class RepositoriesController < ApplicationController @rev = params[:rev].blank? ? @repository.default_branch : params[:rev].to_s.strip @rev_to = params[:rev_to] - unless @rev.to_s.match(REV_PARAM_RE) && @rev_to.to_s.match(REV_PARAM_RE) + unless REV_PARAM_RE.match?(@rev.to_s) && REV_PARAM_RE.match?(@rev_to.to_s) if @repository.branches.blank? raise InvalidRevisionParam end @@ -341,55 +349,37 @@ class RepositoriesController < ApplicationController end def graph_commits_per_month(repository) - @date_to = User.current.today - @date_from = @date_to << 11 - @date_from = Date.civil(@date_from.year, @date_from.month, 1) + date_to = User.current.today + date_from = date_to << 11 + date_from = Date.civil(date_from.year, date_from.month, 1) commits_by_day = Changeset. - where("repository_id = ? AND commit_date BETWEEN ? AND ?", repository.id, @date_from, @date_to). + where("repository_id = ? AND commit_date BETWEEN ? AND ?", repository.id, date_from, date_to). group(:commit_date). count commits_by_month = [0] * 12 - commits_by_day.each {|c| commits_by_month[(@date_to.month - c.first.to_date.month) % 12] += c.last } + commits_by_day.each {|c| commits_by_month[(date_to.month - c.first.to_date.month) % 12] += c.last } changes_by_day = Change. joins(:changeset). - where("#{Changeset.table_name}.repository_id = ? AND #{Changeset.table_name}.commit_date BETWEEN ? AND ?", repository.id, @date_from, @date_to). + where("#{Changeset.table_name}.repository_id = ? AND #{Changeset.table_name}.commit_date BETWEEN ? AND ?", repository.id, date_from, date_to). group(:commit_date). count changes_by_month = [0] * 12 - changes_by_day.each {|c| changes_by_month[(@date_to.month - c.first.to_date.month) % 12] += c.last } + changes_by_day.each {|c| changes_by_month[(date_to.month - c.first.to_date.month) % 12] += c.last } fields = [] today = User.current.today 12.times {|m| fields << month_name(((today.month - 1 - m) % 12) + 1)} - graph = SVG::Graph::Bar.new( - :height => 300, - :width => 800, - :fields => fields.reverse, - :stack => :side, - :scale_integers => true, - :step_x_labels => 2, - :show_data_values => false, - :graph_title => l(:label_commits_per_month), - :show_graph_title => true - ) - - graph.add_data( - :data => commits_by_month[0..11].reverse, - :title => l(:label_revision_plural) - ) - - graph.add_data( - :data => changes_by_month[0..11].reverse, - :title => l(:label_change_plural) - ) - - graph.burn + data = { + :labels => fields.reverse, + :commits => commits_by_month[0..11].reverse, + :changes => changes_by_month[0..11].reverse + } end def graph_commits_per_author(repository) - #data + # data stats = repository.stats_by_author fields, commits_data, changes_data = [], [], [] stats.each do |name, hsh| @@ -398,7 +388,7 @@ class RepositoriesController < ApplicationController changes_data << hsh[:changes_count] end - #expand to 10 values if needed + # expand to 10 values if needed fields = fields + [""]*(10 - fields.length) if fields.length<10 commits_data = commits_data + [0]*(10 - commits_data.length) if commits_data.length<10 changes_data = changes_data + [0]*(10 - changes_data.length) if changes_data.length<10 @@ -406,27 +396,11 @@ class RepositoriesController < ApplicationController # Remove email address in usernames fields = fields.collect {|c| c.gsub(%r{<.+@.+>}, '') } - #prepare graph - graph = SVG::Graph::BarHorizontal.new( - :height => 30 * commits_data.length, - :width => 800, - :fields => fields, - :stack => :side, - :scale_integers => true, - :show_data_values => false, - :rotate_y_labels => false, - :graph_title => l(:label_commits_per_author), - :show_graph_title => true - ) - graph.add_data( - :data => commits_data, - :title => l(:label_revision_plural) - ) - graph.add_data( - :data => changes_data, - :title => l(:label_change_plural) - ) - graph.burn + data = { + :labels => fields.reverse, + :commits => commits_data.reverse, + :changes => changes_data.reverse + } end def disposition(path) diff --git a/app/controllers/roles_controller.rb b/app/controllers/roles_controller.rb index b85c51b..0e29e8c 100644 --- a/app/controllers/roles_controller.rb +++ b/app/controllers/roles_controller.rb @@ -1,5 +1,7 @@ +# frozen_string_literal: true + # Redmine - project management software -# Copyright (C) 2006-2017 Jean-Philippe Lang +# Copyright (C) 2006-2019 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -101,16 +103,22 @@ class RolesController < ApplicationController end def permissions - @roles = Role.sorted.to_a - @permissions = Redmine::AccessControl.permissions.select { |p| !p.public? } - if request.post? - @roles.each do |role| - role.permissions = params[:permissions][role.id.to_s] - role.save - end - flash[:notice] = l(:notice_successful_update) - redirect_to roles_path + scope = Role.sorted + if params[:ids].present? + scope = scope.where(:id => params[:ids]) end + @roles = scope.to_a + @permissions = Redmine::AccessControl.permissions.select { |p| !p.public? } + end + + def update_permissions + @roles = Role.where(:id => params[:permissions].keys) + @roles.each do |role| + role.permissions = params[:permissions][role.id.to_s] + role.save + end + flash[:notice] = l(:notice_successful_update) + redirect_to roles_path end private diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb index ce9e21f..2e81281 100644 --- a/app/controllers/search_controller.rb +++ b/app/controllers/search_controller.rb @@ -1,5 +1,7 @@ +# frozen_string_literal: true + # Redmine - project management software -# Copyright (C) 2006-2017 Jean-Philippe Lang +# Copyright (C) 2006-2019 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -16,12 +18,11 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. class SearchController < ApplicationController - before_action :find_optional_project + before_action :find_optional_project_by_id, :authorize_global accept_api_auth :index def index - @question = params[:q] || "" - @question.strip! + @question = params[:q]&.strip || "" @all_words = params[:all_words] ? params[:all_words].present? : true @titles_only = params[:titles_only] ? params[:titles_only].present? : false @search_attachments = params[:attachments].presence || '0' @@ -68,7 +69,7 @@ class SearchController < ApplicationController fetcher = Redmine::Search::Fetcher.new( @question, User.current, @scope, projects_to_search, :all_words => @all_words, :titles_only => @titles_only, :attachments => @search_attachments, :open_issues => @open_issues, - :cache => params[:page].present?, :params => params + :cache => params[:page].present?, :params => params.to_unsafe_hash ) if fetcher.tokens.present? @@ -87,13 +88,4 @@ class SearchController < ApplicationController format.api { @results ||= []; render :layout => false } end end - -private - def find_optional_project - return true unless params[:id] - @project = Project.find(params[:id]) - check_project_privacy - rescue ActiveRecord::RecordNotFound - render_404 - end end diff --git a/app/controllers/settings_controller.rb b/app/controllers/settings_controller.rb index 7b2dceb..4ff333b 100644 --- a/app/controllers/settings_controller.rb +++ b/app/controllers/settings_controller.rb @@ -1,5 +1,7 @@ +# frozen_string_literal: true + # Redmine - project management software -# Copyright (C) 2006-2017 Jean-Philippe Lang +# Copyright (C) 2006-2019 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -34,7 +36,7 @@ class SettingsController < ApplicationController def edit @notifiables = Redmine::Notifiable.all if request.post? - errors = Setting.set_all_from_params(params[:settings]) + errors = Setting.set_all_from_params(params[:settings].to_unsafe_hash) if errors.blank? flash[:notice] = l(:notice_successful_update) redirect_to settings_path(:tab => params[:tab]) @@ -46,7 +48,7 @@ class SettingsController < ApplicationController end @options = {} - user_format = User::USER_FORMATS.collect{|key, value| [key, value[:setting_order]]}.sort{|a, b| a[1] <=> b[1]} + user_format = User::USER_FORMATS.collect{|key, value| [key, value[:setting_order]]}.sort_by{|f| f[1]} @options[:user_format] = user_format.collect{|f| [User.current.name(f[0]), f[0].to_s]} @deliveries = ActionMailer::Base.perform_deliveries diff --git a/app/controllers/sys_controller.rb b/app/controllers/sys_controller.rb index bd8238a..f217ee2 100644 --- a/app/controllers/sys_controller.rb +++ b/app/controllers/sys_controller.rb @@ -1,5 +1,7 @@ +# frozen_string_literal: true + # Redmine - project management software -# Copyright (C) 2006-2017 Jean-Philippe Lang +# Copyright (C) 2006-2019 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -38,7 +40,7 @@ class SysController < ActionController::Base repository.safe_attributes = params[:repository] repository.project = project if repository.save - render :json => {repository.class.name.underscore.gsub('/', '-') => {:id => repository.id, :url => repository.url}}, :status => 201 + render :json => {repository.class.name.underscore.tr('/', '-') => {:id => repository.id, :url => repository.url}}, :status => 201 else head 422 end @@ -50,7 +52,7 @@ class SysController < ActionController::Base scope = Project.active.has_module(:repository) if params[:id] project = nil - if params[:id].to_s =~ /^\d*$/ + if /^\d*$/.match?(params[:id].to_s) project = scope.find(params[:id]) else project = scope.find_by_identifier(params[:id]) diff --git a/app/controllers/timelog_controller.rb b/app/controllers/timelog_controller.rb index df6b618..1d144b9 100644 --- a/app/controllers/timelog_controller.rb +++ b/app/controllers/timelog_controller.rb @@ -1,5 +1,7 @@ +# frozen_string_literal: true + # Redmine - project management software -# Copyright (C) 2006-2017 Jean-Philippe Lang +# Copyright (C) 2006-2019 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -25,7 +27,6 @@ class TimelogController < ApplicationController before_action :find_optional_issue, :only => [:new, :create] before_action :find_optional_project, :only => [:index, :report] - before_action :authorize_global, :only => [:new, :create, :index, :report] accept_rss_auth :index accept_api_auth :index, :show, :create, :update, :destroy @@ -91,12 +92,12 @@ class TimelogController < ApplicationController end def new - @time_entry ||= TimeEntry.new(:project => @project, :issue => @issue, :user => User.current, :spent_on => User.current.today) + @time_entry ||= TimeEntry.new(:project => @project, :issue => @issue, :author => User.current, :spent_on => User.current.today) @time_entry.safe_attributes = params[:time_entry] end def create - @time_entry ||= TimeEntry.new(:project => @project, :issue => @issue, :user => User.current, :spent_on => User.current.today) + @time_entry ||= TimeEntry.new(:project => @project, :issue => @issue, :author => User.current, :user => User.current, :spent_on => User.current.today) @time_entry.safe_attributes = params[:time_entry] if @time_entry.project && !User.current.allowed_to?(:log_time, @time_entry.project) render_403 @@ -146,7 +147,6 @@ class TimelogController < ApplicationController def update @time_entry.safe_attributes = params[:time_entry] - call_hook(:controller_timelog_edit_before_save, { :params => params, :time_entry => @time_entry }) if @time_entry.save @@ -166,8 +166,18 @@ class TimelogController < ApplicationController end def bulk_edit - @available_activities = @projects.map(&:activities).reduce(:&) + @target_projects = Project.allowed_to(:log_time).to_a @custom_fields = TimeEntry.first.available_custom_fields.select {|field| field.format.bulk_edit_supported} + if params[:time_entry] + @target_project = @target_projects.detect {|p| p.id.to_s == params[:time_entry][:project_id].to_s} + end + if @target_project + @available_activities = @target_project.activities + else + @available_activities = @projects.map(&:activities).reduce(:&) + end + @time_entry_params = params[:time_entry] || {} + @time_entry_params[:custom_field_values] ||= {} end def bulk_update @@ -230,7 +240,8 @@ class TimelogController < ApplicationController end end -private + private + def find_time_entry @time_entry = TimeEntry.find(params[:id]) @project = @time_entry.project @@ -262,25 +273,18 @@ private if params[:issue_id].present? @issue = Issue.find(params[:issue_id]) @project = @issue.project + authorize else find_optional_project end end - def find_optional_project - if params[:project_id].present? - @project = Project.find(params[:project_id]) - end - rescue ActiveRecord::RecordNotFound - render_404 - end - # Returns the TimeEntry scope for index and report actions def time_entry_scope(options={}) @query.results_scope(options) end def retrieve_time_entry_query - retrieve_query(TimeEntryQuery, false) + retrieve_query(TimeEntryQuery, false, :defaults => @default_columns_names) end end diff --git a/app/controllers/trackers_controller.rb b/app/controllers/trackers_controller.rb index caf6b11..e219dea 100644 --- a/app/controllers/trackers_controller.rb +++ b/app/controllers/trackers_controller.rb @@ -1,5 +1,7 @@ +# frozen_string_literal: true + # Redmine - project management software -# Copyright (C) 2006-2017 Jean-Philippe Lang +# Copyright (C) 2006-2019 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -32,7 +34,7 @@ class TrackersController < ApplicationController end def new - @tracker ||= Tracker.new + @tracker ||= Tracker.new(:default_status => IssueStatus.sorted.first) @tracker.safe_attributes = params[:tracker] @trackers = Tracker.sorted.to_a @projects = Project.all diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 0133f97..ec9ecba 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -1,5 +1,7 @@ +# frozen_string_literal: true + # Redmine - project management software -# Copyright (C) 2006-2017 Jean-Philippe Lang +# Copyright (C) 2006-2019 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -28,7 +30,10 @@ class UsersController < ApplicationController include SortHelper helper :custom_fields include CustomFieldsHelper + include UsersHelper helper :principal_memberships + helper :activities + include ActivitiesHelper require_sudo_mode :create, :update, :destroy @@ -59,6 +64,9 @@ class UsersController < ApplicationController @groups = Group.givable.sort render :layout => !request.xhr? } + format.csv { + send_data(users_to_csv(scope.order(sort_clause)), :type => 'text/csv; header=present', :filename => 'users.csv') + } format.api end end @@ -72,6 +80,16 @@ class UsersController < ApplicationController # show projects based on current user visibility @memberships = @user.memberships.preload(:roles, :project).where(Project.visible_condition(User.current)).to_a + @issue_counts = {} + @issue_counts[:assigned] = { + :total => Issue.visible.assigned_to(@user).count, + :open => Issue.visible.open.assigned_to(@user).count + } + @issue_counts[:reported] = { + :total => Issue.visible.where(:author_id => @user.id).count, + :open => Issue.visible.open.where(:author_id => @user.id).count + } + respond_to do |format| format.html { events = Redmine::Activity::Fetcher.new(User.current, :author => @user).events(nil, nil, :limit => 10) @@ -95,13 +113,13 @@ class UsersController < ApplicationController @user.pref.safe_attributes = params[:pref] if @user.save - Mailer.account_information(@user, @user.password).deliver if params[:send_information] + Mailer.deliver_account_information(@user, @user.password) if params[:send_information] respond_to do |format| format.html { flash[:notice] = l(:notice_user_successful_create, :id => view_context.link_to(@user.login, user_path(@user))) if params[:continue] - attrs = params[:user].slice(:generate_password) + attrs = {:generate_password => @user.generate_password } redirect_to new_user_path(:user => attrs) else redirect_to edit_user_path(@user) @@ -140,9 +158,9 @@ class UsersController < ApplicationController @user.pref.save if was_activated - Mailer.account_activated(@user).deliver + Mailer.deliver_account_activated(@user) elsif @user.active? && params[:send_information] && @user != User.current - Mailer.account_information(@user, @user.password).deliver + Mailer.deliver_account_information(@user, @user.password) end respond_to do |format| diff --git a/app/controllers/versions_controller.rb b/app/controllers/versions_controller.rb index a536416..15c24a8 100644 --- a/app/controllers/versions_controller.rb +++ b/app/controllers/versions_controller.rb @@ -1,5 +1,7 @@ +# frozen_string_literal: true + # Redmine - project management software -# Copyright (C) 2006-2017 Jean-Philippe Lang +# Copyright (C) 2006-2019 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -34,7 +36,7 @@ class VersionsController < ApplicationController @trackers = @project.trackers.sorted.to_a retrieve_selected_tracker_ids(@trackers, @trackers.select {|t| t.is_in_roadmap?}) @with_subprojects = params[:with_subprojects].nil? ? Setting.display_subprojects_issues? : (params[:with_subprojects] == '1') - project_ids = @with_subprojects ? @project.self_and_descendants.collect(&:id) : [@project.id] + project_ids = @with_subprojects ? @project.self_and_descendants.pluck(:id) : [@project.id] @versions = @project.shared_versions.preload(:custom_values) @versions += @project.rolled_up_versions.visible.preload(:custom_values) if @with_subprojects diff --git a/app/controllers/watchers_controller.rb b/app/controllers/watchers_controller.rb index 3f080e9..f0692e3 100644 --- a/app/controllers/watchers_controller.rb +++ b/app/controllers/watchers_controller.rb @@ -1,5 +1,7 @@ +# frozen_string_literal: true + # Redmine - project management software -# Copyright (C) 2006-2017 Jean-Philippe Lang +# Copyright (C) 2006-2019 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/app/controllers/welcome_controller.rb b/app/controllers/welcome_controller.rb index 341a64d..571e233 100644 --- a/app/controllers/welcome_controller.rb +++ b/app/controllers/welcome_controller.rb @@ -1,5 +1,7 @@ +# frozen_string_literal: true + # Redmine - project management software -# Copyright (C) 2006-2017 Jean-Philippe Lang +# Copyright (C) 2006-2019 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/app/controllers/wiki_controller.rb b/app/controllers/wiki_controller.rb index 134765e..423f94f 100644 --- a/app/controllers/wiki_controller.rb +++ b/app/controllers/wiki_controller.rb @@ -1,5 +1,7 @@ +# frozen_string_literal: true + # Redmine - project management software -# Copyright (C) 2006-2017 Jean-Philippe Lang +# Copyright (C) 2006-2019 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -42,8 +44,6 @@ class WikiController < ApplicationController helper :watchers include Redmine::Export::PDF - include ActionView::Helpers::SanitizeHelper - # List of pages, sorted alphabetically and by parent (hierarchy) def index load_pages_for_index @@ -72,7 +72,7 @@ class WikiController < ApplicationController @page.title = '' unless editable? @page.validate if @page.errors[:title].blank? - path = project_wiki_page_path(@project, @page.title) + path = project_wiki_page_path(@project, @page.title, :parent => params[:parent]) respond_to do |format| format.html { redirect_to path } format.js { render :js => "window.location = #{path.to_json}" } @@ -109,7 +109,7 @@ class WikiController < ApplicationController send_data(export, :type => 'text/html', :filename => filename_for_content_disposition("#{@page.title}.html")) return elsif params[:format] == 'txt' - send_data(strip_tags(@content.text), :type => 'text/plain', :filename => filename_for_content_disposition("#{@page.title}.txt")) + send_data(@content.text, :type => 'text/plain', :filename => filename_for_content_disposition("#{@page.title}.txt")) return end end @@ -325,7 +325,7 @@ class WikiController < ApplicationController @attachments += page.attachments @previewed = page.content end - @text = params[:content][:text] + @text = params[:content].present? ? params[:content][:text] : params[:text] render :partial => 'common/preview' end @@ -336,7 +336,7 @@ class WikiController < ApplicationController redirect_to :action => 'show', :id => @page.title, :project_id => @project end -private + private def find_wiki @project = Project.find(params[:project_id]) diff --git a/app/controllers/wikis_controller.rb b/app/controllers/wikis_controller.rb index c61c2f9..3c1637e 100644 --- a/app/controllers/wikis_controller.rb +++ b/app/controllers/wikis_controller.rb @@ -1,5 +1,7 @@ +# frozen_string_literal: true + # Redmine - project management software -# Copyright (C) 2006-2017 Jean-Philippe Lang +# Copyright (C) 2006-2019 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -19,13 +21,6 @@ class WikisController < ApplicationController menu_item :settings before_action :find_project, :authorize - # Create or update a project's wiki - def edit - @wiki = @project.wiki || Wiki.new(:project => @project) - @wiki.safe_attributes = params[:wiki] - @wiki.save if request.post? - end - # Delete a project's wiki def destroy if request.post? && params[:confirm] && @project.wiki diff --git a/app/controllers/workflows_controller.rb b/app/controllers/workflows_controller.rb index c831827..0c29b14 100644 --- a/app/controllers/workflows_controller.rb +++ b/app/controllers/workflows_controller.rb @@ -1,5 +1,7 @@ +# frozen_string_literal: true + # Redmine - project management software -# Copyright (C) 2006-2017 Jean-Philippe Lang +# Copyright (C) 2006-2019 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -118,7 +120,7 @@ class WorkflowsController < ApplicationController def find_roles ids = Array.wrap(params[:role_id]) if ids == ['all'] - @roles = Role.sorted.to_a + @roles = Role.sorted.select(&:consider_workflow?) elsif ids.present? @roles = Role.where(:id => ids).to_a end diff --git a/app/helpers/account_helper.rb b/app/helpers/account_helper.rb index e4d8222..1e8de67 100644 --- a/app/helpers/account_helper.rb +++ b/app/helpers/account_helper.rb @@ -1,7 +1,7 @@ -# encoding: utf-8 -# +# frozen_string_literal: true + # Redmine - project management software -# Copyright (C) 2006-2017 Jean-Philippe Lang +# Copyright (C) 2006-2019 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/app/helpers/activities_helper.rb b/app/helpers/activities_helper.rb index 54e5580..573e432 100644 --- a/app/helpers/activities_helper.rb +++ b/app/helpers/activities_helper.rb @@ -1,7 +1,7 @@ -# encoding: utf-8 -# +# frozen_string_literal: true + # Redmine - project management software -# Copyright (C) 2006-2017 Jean-Philippe Lang +# Copyright (C) 2006-2019 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -21,9 +21,9 @@ module ActivitiesHelper def sort_activity_events(events) events_by_group = events.group_by(&:event_group) sorted_events = [] - events.sort {|x, y| y.event_datetime <=> x.event_datetime}.each do |event| + events.sort_by(&:event_datetime).reverse_each do |event| if group_events = events_by_group.delete(event.event_group) - group_events.sort {|x, y| y.event_datetime <=> x.event_datetime}.each_with_index do |e, i| + group_events.sort_by(&:event_datetime).reverse.each_with_index do |e, i| sorted_events << [e, i > 0] end end diff --git a/app/helpers/admin_helper.rb b/app/helpers/admin_helper.rb index e972ef0..a06852b 100644 --- a/app/helpers/admin_helper.rb +++ b/app/helpers/admin_helper.rb @@ -1,7 +1,7 @@ -# encoding: utf-8 -# +# frozen_string_literal: true + # Redmine - project management software -# Copyright (C) 2006-2017 Jean-Philippe Lang +# Copyright (C) 2006-2019 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 792af98..e8284a0 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -1,7 +1,7 @@ -# encoding: utf-8 -# +# frozen_string_literal: true + # Redmine - project management software -# Copyright (C) 2006-2017 Jean-Philippe Lang +# Copyright (C) 2006-2019 Jean-Philippe Lang # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -23,7 +23,6 @@ require 'cgi' module ApplicationHelper include Redmine::WikiFormatting::Macros::Definitions include Redmine::I18n - include GravatarHelper::PublicMethods include Redmine::Pagination::Helper include Redmine::SudoMode::Helper include Redmine::Themes::Helper @@ -53,7 +52,8 @@ module ApplicationHelper if user.is_a?(User) name = h(user.name(options[:format])) if user.active? || (User.current.admin? && user.logged?) - link_to name, user_path(user), :class => user.css_classes + only_path = options[:only_path].nil? ? true : options[:only_path] + link_to name, user_url(user, :only_path => only_path), :class => user.css_classes else name end @@ -62,6 +62,20 @@ module ApplicationHelper end end + # Displays a link to edit group page if current user is admin + # Otherwise display only the group name + def link_to_group(group, options={}) + if group.is_a?(Group) + name = h(group.name) + if User.current.admin? + only_path = options[:only_path].nil? ? true : options[:only_path] + link_to name, edit_group_path(group, :only_path => only_path) + else + name + end + end + end + # Displays a link to +issue+ with its subject. # Examples: # @@ -97,10 +111,17 @@ module ApplicationHelper # * :download - Force download (default: false) def link_to_attachment(attachment, options={}) text = options.delete(:text) || attachment.filename - route_method = options.delete(:download) ? :download_named_attachment_url : :named_attachment_url - html_options = options.slice!(:only_path) + if options.delete(:download) + route_method = :download_named_attachment_url + options[:filename] = attachment.filename + else + route_method = :attachment_url + # make sure we don't have an extraneous :filename in the options + options.delete(:filename) + end + html_options = options.slice!(:only_path, :filename) options[:only_path] = true unless options.key?(:only_path) - url = send(route_method, attachment, attachment.filename, options) + url = send(route_method, attachment, options) link_to text, url, html_options end @@ -146,8 +167,8 @@ module ApplicationHelper h(project.name) else link_to project.name, - project_url(project, {:only_path => true}.merge(options)), - html_options + project_url(project, {:only_path => true}.merge(options)), + html_options end end @@ -169,6 +190,39 @@ module ApplicationHelper link_to_if version.visible?, format_version_name(version), version_path(version), options end + RECORD_LINK = { + 'CustomValue' => -> (custom_value) { link_to_record(custom_value.customized) }, + 'Document' => -> (document) { link_to(document.title, document_path(document)) }, + 'Group' => -> (group) { link_to(group.name, group_path(group)) }, + 'Issue' => -> (issue) { link_to_issue(issue, :subject => false) }, + 'Message' => -> (message) { link_to_message(message) }, + 'News' => -> (news) { link_to(news.title, news_path(news)) }, + 'Project' => -> (project) { link_to_project(project) }, + 'User' => -> (user) { link_to_user(user) }, + 'Version' => -> (version) { link_to_version(version) }, + 'WikiPage' => -> (wiki_page) { link_to(wiki_page.pretty_title, project_wiki_page_path(wiki_page.project, wiki_page.title)) } + } + + def link_to_record(record) + if link = RECORD_LINK[record.class.name] + self.instance_exec(record, &link) + end + end + + ATTACHMENT_CONTAINER_LINK = { + # Custom list, since project/version attachments are listed in the files + # view and not in the project/milestone view + 'Project' => -> (project) { link_to(l(:project_module_files), project_files_path(project)) }, + 'Version' => -> (version) { link_to(l(:project_module_files), project_files_path(version.project)) }, + } + + def link_to_attachment_container(attachment_container) + if link = ATTACHMENT_CONTAINER_LINK[attachment_container.class.name] || + RECORD_LINK[attachment_container.class.name] + self.instance_exec(attachment_container, &link) + end + end + # Helper that formats object for html or text rendering def format_object(object, html=true, &block) if block_given? @@ -228,22 +282,57 @@ module ApplicationHelper :srcset => "#{thumbnail_path(attachment, :size => thumbnail_size * 2)} 2x", :style => "max-width: #{thumbnail_size}px; max-height: #{thumbnail_size}px;" ), - named_attachment_path( - attachment, - attachment.filename + attachment_path( + attachment ), :title => attachment.filename ) end def toggle_link(name, id, options={}) - onclick = "$('##{id}').toggle(); " + onclick = +"$('##{id}').toggle(); " onclick << (options[:focus] ? "$('##{options[:focus]}').focus(); " : "this.blur(); ") onclick << "$(window).scrollTop($('##{options[:focus]}').position().top); " if options[:scroll] onclick << "return false;" link_to(name, "#", :onclick => onclick) end + def link_to_previous_month(year, month, options={}) + target_year, target_month = if month == 1 + [year - 1, 12] + else + [year, month - 1] + end + + name = if target_month == 12 + "#{month_name(target_month)} #{target_year}" + else + month_name(target_month) + end + + link_to_month(("« " + name), target_year, target_month, options) + end + + def link_to_next_month(year, month, options={}) + target_year, target_month = if month == 12 + [year + 1, 1] + else + [year, month + 1] + end + + name = if target_month == 1 + "#{month_name(target_month)} #{target_year}" + else + month_name(target_month) + end + + link_to_month((name + " »"), target_year, target_month, options) + end + + def link_to_month(link_name, year, month, options={}) + link_to(link_name, {:params => request.query_parameters.merge(:year => year, :month => month)}, options) + end + # Used to format item titles on the activity view def format_activity_title(text) text @@ -281,19 +370,19 @@ module ApplicationHelper # The given collection may be a subset of the whole project tree # (eg. some intermediate nodes are private and can not be seen) def render_project_nested_lists(projects, &block) - s = '' + s = +'' if projects.any? ancestors = [] original_project = @project projects.sort_by(&:lft).each do |project| # set the project environment to please macros. @project = project - if (ancestors.empty? || project.is_descendant_of?(ancestors.last)) + if ancestors.empty? || project.is_descendant_of?(ancestors.last) s << "\n" end @@ -311,12 +400,17 @@ module ApplicationHelper end def render_page_hierarchy(pages, node=nil, options={}) - content = '' + content = +'' if pages[node] content << "