diff --git a/.babelrc b/.babelrc deleted file mode 100644 index 7605f7bab2f..00000000000 --- a/.babelrc +++ /dev/null @@ -1,5 +0,0 @@ -{ - "presets": [ - ["@babel/env", { "modules": false, "loose": true } ] - ] -} diff --git a/.codeclimate.yml b/.codeclimate.yml deleted file mode 100644 index 2bd21bd046c..00000000000 --- a/.codeclimate.yml +++ /dev/null @@ -1,34 +0,0 @@ ---- - -version: "2" - -checks: - argument-count: - enabled: false - - complex-logic: - enabled: false - - file-lines: - enabled: false - - method-complexity: - enabled: false - - method-count: - enabled: false - - method-lines: - enabled: false - - nested-control-flow: - enabled: false - - return-statements: - enabled: false - - similar-code: - enabled: false - - identical-code: - enabled: false diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index d12c6aa1916..00000000000 --- a/.eslintignore +++ /dev/null @@ -1,6 +0,0 @@ -app/assets/**/*.js -vendor/**/*.js -lib/generators/**/*.js -tmp/**/*.js -spec/**/*.js -src/**/*.js diff --git a/.eslintrc.yml b/.eslintrc.yml deleted file mode 100644 index ad63d102a5e..00000000000 --- a/.eslintrc.yml +++ /dev/null @@ -1,289 +0,0 @@ -env: - browser: true - es6: true -extends: - - 'eslint:recommended' - - 'plugin:import/errors' - - 'plugin:import/warnings' -globals: - $: false - Atomics: readonly - jQuery: false - SharedArrayBuffer: readonly - Turbolinks: false -parserOptions: - ecmaVersion: 2018 - sourceType: module -rules: - accessor-pairs: error - array-bracket-newline: error - array-bracket-spacing: 'off' - array-callback-return: error - array-element-newline: 'off' - arrow-body-style: 'off' - arrow-parens: 'off' - arrow-spacing: - - error - - after: true - before: true - block-scoped-var: error - block-spacing: - - error - - always - brace-style: - - error - - 1tbs - - allowSingleLine: true - callback-return: 'off' - camelcase: 'off' - capitalized-comments: 'off' - class-methods-use-this: 'off' - comma-dangle: error - comma-spacing: - - error - - after: true - before: false - comma-style: - - error - - last - complexity: error - computed-property-spacing: - - error - - never - consistent-return: 'off' - consistent-this: error - curly: error - default-case: error - default-param-last: error - dot-location: 'off' - dot-notation: 'off' - eol-last: error - eqeqeq: 'off' - func-call-spacing: error - func-name-matching: error - func-names: 'off' - func-style: 'off' - function-paren-newline: error - generator-star-spacing: error - global-require: error - grouped-accessor-pairs: error - guard-for-in: 'off' - handle-callback-err: error - id-blacklist: error - id-length: 'off' - id-match: error - implicit-arrow-linebreak: 'off' - indent: 'off' - indent-legacy: 'off' - init-declarations: 'off' - jsx-quotes: error - key-spacing: error - keyword-spacing: - - error - - after: true - before: true - line-comment-position: 'off' - linebreak-style: - - error - - unix - lines-around-comment: error - lines-around-directive: error - lines-between-class-members: - - error - - always - max-classes-per-file: error - max-depth: error - max-len: 'off' - max-lines: error - max-lines-per-function: 'off' - max-nested-callbacks: error - max-params: error - max-statements: 'off' - max-statements-per-line: 'off' - multiline-comment-style: - - error - - separate-lines - multiline-ternary: - - error - - never - new-parens: error - newline-after-var: 'off' - newline-before-return: 'off' - newline-per-chained-call: 'off' - no-alert: error - no-array-constructor: error - no-await-in-loop: error - no-bitwise: error - no-buffer-constructor: error - no-caller: error - no-catch-shadow: error - no-cond-assign: - - error - - except-parens - no-confusing-arrow: error - no-console: 'off' - no-constructor-return: error - no-continue: error - no-div-regex: error - no-dupe-else-if: error - no-duplicate-imports: error - no-else-return: 'off' - no-empty-function: 'off' - no-eq-null: 'off' - no-eval: error - no-extend-native: error - no-extra-bind: error - no-extra-label: error - no-extra-parens: 'off' - no-floating-decimal: error - no-implicit-coercion: error - no-implicit-globals: error - no-implied-eval: error - no-import-assign: error - no-inline-comments: 'off' - no-inner-declarations: - - error - - functions - no-invalid-this: 'off' - no-iterator: error - no-label-var: error - no-labels: error - no-lone-blocks: error - no-lonely-if: error - no-loop-func: 'off' - no-magic-numbers: 'off' - no-mixed-operators: error - no-mixed-requires: error - no-multi-assign: error - no-multi-spaces: 'off' - no-multi-str: error - no-multiple-empty-lines: error - no-native-reassign: error - no-negated-condition: 'off' - no-negated-in-lhs: error - no-nested-ternary: error - no-new: error - no-new-func: error - no-new-object: error - no-new-require: error - no-new-wrappers: error - no-octal-escape: error - no-param-reassign: 'off' - no-path-concat: error - no-plusplus: 'off' - no-process-env: error - no-process-exit: error - no-proto: error - no-restricted-globals: error - no-restricted-imports: error - no-restricted-modules: error - no-restricted-properties: error - no-restricted-syntax: error - no-return-assign: 'off' - no-return-await: error - no-script-url: error - no-self-compare: error - no-sequences: error - no-setter-return: error - no-shadow: error - no-spaced-func: error - no-sync: error - no-tabs: error - no-template-curly-in-string: error - no-ternary: 'off' - no-throw-literal: error - no-trailing-spaces: error - no-undef-init: error - no-undefined: error - no-underscore-dangle: 'off' - no-unmodified-loop-condition: error - no-unneeded-ternary: error - no-unused-expressions: error - no-unused-vars: - - error - - argsIgnorePattern: "^_" - no-use-before-define: 'off' - no-useless-call: error - no-useless-computed-key: error - no-useless-concat: error - no-useless-constructor: error - no-useless-rename: error - no-useless-return: error - no-var: 'off' - no-void: error - no-warning-comments: error - no-whitespace-before-property: error - nonblock-statement-body-position: error - object-curly-newline: error - object-curly-spacing: 'off' - object-shorthand: 'off' - one-var: 'off' - one-var-declaration-per-line: - - error - - initializations - operator-assignment: - - error - - always - operator-linebreak: error - padded-blocks: 'off' - padding-line-between-statements: error - prefer-arrow-callback: 'off' - prefer-const: 'off' - prefer-destructuring: error - prefer-exponentiation-operator: error - prefer-named-capture-group: 'off' - prefer-numeric-literals: error - prefer-object-spread: error - prefer-promise-reject-errors: error - prefer-reflect: 'off' - prefer-regex-literals: error - prefer-rest-params: 'off' - prefer-spread: error - prefer-template: 'off' - quote-props: 'off' - quotes: 'off' - radix: - - error - - always - require-atomic-updates: error - require-await: error - require-jsdoc: 'off' - require-unicode-regexp: 'off' - rest-spread-spacing: - - error - - never - semi: 'off' - semi-spacing: error - semi-style: - - error - - last - sort-keys: 'off' - sort-vars: error - space-before-blocks: 'off' - space-before-function-paren: 'off' - space-in-parens: - - error - - never - space-infix-ops: error - space-unary-ops: error - spaced-comment: - - error - - always - strict: error - switch-colon-spacing: error - symbol-description: error - template-curly-spacing: - - error - - never - template-tag-spacing: error - unicode-bom: - - error - - never - valid-jsdoc: error - vars-on-top: 'off' - wrap-iife: error - wrap-regex: 'off' - yield-star-spacing: error - yoda: - - error - - never diff --git a/.gherkin-lintignore b/.gherkin-lintignore deleted file mode 100644 index 69e2059d1a7..00000000000 --- a/.gherkin-lintignore +++ /dev/null @@ -1 +0,0 @@ -vendor/bundle/**/* diff --git a/.gherkin-lintrc b/.gherkin-lintrc index 9a40fb9d47f..8afd8ef7f5b 100644 --- a/.gherkin-lintrc +++ b/.gherkin-lintrc @@ -1,5 +1,5 @@ { - "no-files-without-scenarios" : "on", + "no-files-without-scenarios" : "off", "no-unnamed-features": "on", "no-unnamed-scenarios": "on", "no-dupe-feature-names": "on", diff --git a/.github/ISSUE_TEMPLATE/bug-report.md b/.github/ISSUE_TEMPLATE/bug-report.md new file mode 100644 index 00000000000..a65fee43345 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug-report.md @@ -0,0 +1,38 @@ +--- +name: Bug report +about: If you've already asked for help with a problem and confirmed something is broken with ActiveAdmin itself, create a bug report. +title: '' +labels: '' +assignees: '' +--- + + + +Describe your issue with a **clear title and description**. Make sure to include +as much relevant information as possible, including a code sample or failing +test that demonstrates the expected behavior, as well as your system +configuration. Your goal should be to make it easy for yourself - and others - +to reproduce the bug and figure out a fix. + +### Expected behavior + +What do you think should happen? + +### Actual behavior + +What actually happens? + +### How to reproduce + +Having a way to reproduce your issue will help people confirm, investigate, +and ultimately fix your issue. You can do this by providing an executable test +case. To make this process easier, please use [our bug report template script]. + +Copy the content of the appropriate template into an `.rb` file and make the +necessary changes to demonstrate the issue. You can execute it by running +`ruby the_file.rb` in your terminal. If all goes well, you should see your test +case failing. + +[our bug report template script]: https://github.com/activeadmin/activeadmin/blob/master/tasks/bug_report_template.rb diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index 6bf232674fe..00000000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,33 +0,0 @@ ---- - -name: Bug report -about: Create a bug report to help us improve ActiveAdmin -title: "" -labels: "" -assignees: "" - ---- - -### Did you find a bug? - -Describe your problem with a **clear title** sumarizing it, and a **clear -description** including as much relevant information as possible about it. - -### Expected behavior - -What do you think should happen? - -### Actual behavior - -What actually happens? - -### How to reproduce - -Your best chance of getting this bug looked at quickly is to provide a **code -sample** or an **executable test case** demonstrating the expected behavior that is not -occurring. Ideally, use [our bug report template] to create the issue. Simply -copy the content of the appropriate template into a .rb file, make the necessary -changes to demonstrate the issue, and **paste the content into the issue -description**. - -[our bug report template]: https://github.com/activeadmin/activeadmin/blob/master/tasks/bug_report_template.rb diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000000..3202933f390 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,14 @@ +blank_issues_enabled: false +contact_links: + - name: Get Help + url: https://github.com/activeadmin/activeadmin/discussions/new?category=help + about: If you can't get something to work the way you expect, open a question in our discussion forums. + - name: Feature Request + url: https://github.com/activeadmin/activeadmin/discussions/new?category=ideas + about: Suggest any ideas you have using our discussion forums. + - name: Documentation Issue + url: https://github.com/activeadmin/activeadmin/pulls + about: For documentation improvements, feel free to create a pull request. + - name: Localization Issue + url: https://github.com/activeadmin/activeadmin/pulls + about: For any localization updates, create a pull request as we rely entirely on the community for these. diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index d40a89ec077..f1294ef22ba 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -2,20 +2,18 @@ Thanks for contributing to ActiveAdmin! -You can find all the instructions for contributing at the [CONTRIBUTING file]. +You can find the instructions in our contributing guide: https://github.com/activeadmin/activeadmin/blob/master/CONTRIBUTING.md Before submitting your PR make sure that: - * You wrote [good commit messages]. * The PR relates to *only* one subject with a clear title and description in grammatically correct, complete sentences. * The PR description [includes keywords to automatically close issues] if it fixes an existing issue. * Your feature branch is up-to-date with `master` (if not - rebase it), and does not include merge commits. -* Related commits are squashed together, and unrelated commits are splitted apart. +* Related commits are squashed together, and unrelated commits are separated. * Your PR includes a regression test if it fixes a bug. -Before expecting a review for your PR make sure that all github checks are passing, but feel free to ask for early feedback if you need guidance. +Before expecting a review for your PR make sure that all CI checks are passing, but feel free to ask for early feedback if you need guidance. -[CONTRIBUTING file]: https://github.com/activeadmin/activeadmin/blob/master/CONTRIBUTING.md [good commit messages]: https://chris.beams.io/posts/git-commit/ [includes keywords to automatically close issues]: https://help.github.com/en/articles/closing-issues-using-keywords diff --git a/.github/dependabot.yml b/.github/dependabot.yml index eab793e8b24..4315a9fb870 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,46 +1,86 @@ ---- - version: 2 - updates: - - package-ecosystem: bundler - directory: / - schedule: - interval: daily - versioning-strategy: lockfile-only - - - package-ecosystem: npm + - package-ecosystem: github-actions directory: / schedule: interval: daily - versioning-strategy: lockfile-only - + groups: + github_actions: + patterns: + - "*" - package-ecosystem: bundler - directory: /docs + directory: / schedule: - interval: daily + interval: monthly versioning-strategy: lockfile-only - - - package-ecosystem: bundler - directory: /gemfiles/rails_60 + groups: + rails_default: + patterns: + - "*" + - package-ecosystem: npm + directory: / schedule: - interval: daily + interval: monthly versioning-strategy: lockfile-only - + groups: + npm: + patterns: + - "*" - package-ecosystem: bundler - directory: /gemfiles/rails_61 + directory: /gemfiles/rails_70 schedule: - interval: daily + interval: monthly versioning-strategy: lockfile-only - + groups: + rails_70: + patterns: + - "*" + ignore: + - dependency-name: rails + versions: ">= 7.1.0" + - dependency-name: rails-i18n + versions: ">= 8.0.0" + - dependency-name: railties + versions: ">= 7.1.0" + - dependency-name: rspec-rails + versions: ">= 8.0.0" + - dependency-name: sqlite3 + versions: ">= 2" - package-ecosystem: bundler - directory: /gemfiles/rails_61_turbolinks + directory: /gemfiles/rails_71 schedule: - interval: daily + interval: monthly versioning-strategy: lockfile-only - + groups: + rails_71: + patterns: + - "*" + ignore: + - dependency-name: erb + versions: ">= 5" + - dependency-name: rails + versions: ">= 7.2.0" + - dependency-name: rails-i18n + versions: ">= 8.0.0" + - dependency-name: railties + versions: ">= 7.2.0" + - dependency-name: rspec-rails + versions: ">= 8.0.0" - package-ecosystem: bundler - directory: /gemfiles/rails_61_webpacker + directory: /gemfiles/rails_72 schedule: - interval: daily + interval: monthly versioning-strategy: lockfile-only + groups: + rails_72: + patterns: + - "*" + ignore: + - dependency-name: erb + versions: ">= 5" + - dependency-name: rails + versions: ">= 8.0.0" + - dependency-name: rails-i18n + versions: ">= 8.0.0" + - dependency-name: railties + versions: ">= 8.0.0" diff --git a/.github/mergify.yml b/.github/mergify.yml deleted file mode 100644 index dd05da031a8..00000000000 --- a/.github/mergify.yml +++ /dev/null @@ -1,11 +0,0 @@ ---- - -pull_request_rules: - - name: Automatic merge for dependabot pull requests - - conditions: - - author~=^dependabot\[bot\]$ - - actions: - merge: - method: merge diff --git a/.github/release.yml b/.github/release.yml new file mode 100644 index 00000000000..fd55d1984b5 --- /dev/null +++ b/.github/release.yml @@ -0,0 +1,23 @@ +changelog: + categories: + - title: Breaking Changes 🚨 + labels: + - type breaking change + - title: Template Updates 📝 + labels: + - type template update + - title: Enhancements ✨ + labels: + - type enhancement + - title: Bug Fixes 🐛 + labels: + - type bug fix + - title: Security Fixes 🔒 + labels: + - type security fix + - title: Other Changes 🛠 + labels: + - "*" + exclude: + authors: + - dependabot diff --git a/.github/workflows/bug-report-template.yml b/.github/workflows/bug-report-template.yml new file mode 100644 index 00000000000..b6df09d4bea --- /dev/null +++ b/.github/workflows/bug-report-template.yml @@ -0,0 +1,42 @@ +name: Bug Reports + +on: + schedule: + # Run every day at noon UTC + - cron: '0 12 * * *' + pull_request: + +concurrency: + group: ${{ github.repository }}-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + +env: + RUBY_VERSION: ruby-3.4 + +jobs: + bug_report_template_test: + name: Run bug report template + # Don't run scheduled workflow on forks + if: ${{ github.event_name == 'pull_request' || github.repository_owner == 'activeadmin' }} + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: tj-actions/changed-files@v46 + id: changed-files + with: + files: | + app/** + config/** + lib/*.rb + lib/active_admin/** + tasks/bug_report_template.rb + Gemfile* + *.gemspec + - uses: ruby/setup-ruby@v1 + if: steps.changed-files.outputs.any_changed == 'true' + with: + ruby-version: ${{ env.RUBY_VERSION }} + bundler-cache: true + - name: Run bug report template + if: steps.changed-files.outputs.any_changed == 'true' + run: ACTIVE_ADMIN_PATH=. ruby tasks/bug_report_template.rb diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 534ef5ffb5f..c3004176fdc 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -1,222 +1,127 @@ ---- - name: ci on: pull_request: - push: branches: - master -jobs: - jruby: - name: Check Gemfile installs fine on JRuby - runs-on: ubuntu-20.04 - - timeout-minutes: 15 - - steps: - - uses: actions/checkout@v2 - - - uses: ruby/setup-ruby@v1 - with: - ruby-version: jruby-9.3.2.0 - - - name: Run bug report template - run: ACTIVE_ADMIN_PATH=. ruby tasks/bug_report_template.rb - - lint: - name: lint (${{ matrix.ruby.name }}) - runs-on: ${{ matrix.os }} - - timeout-minutes: 15 - - strategy: - fail-fast: false - - matrix: - ruby: [{ name: 3.1, value: 3.1.0 }] - os: [ubuntu-20.04] - - steps: - - uses: actions/checkout@v2 - - - name: Configure bundler - run: | - echo "BUNDLE_GEMFILE=Gemfile" >> $GITHUB_ENV - echo "BUNDLE_PATH=$(pwd)/vendor/bundle" >> $GITHUB_ENV - - - name: Enable coverage tracking - run: echo "COVERAGE=true" >> $GITHUB_ENV - - - uses: ruby/setup-ruby@v1 - with: - ruby-version: ${{ matrix.ruby.value }} - rubygems: 3.3.5 - bundler-cache: true - - - name: Generate docs - run: bin/rake docs:build - - - name: Setup git - run: | - git config --global user.email activeadmin@ci.dummy - git config --global user.name ActiveAdmin - - - name: Run lints - run: bin/rake lint - - - name: Run bug report template - run: ACTIVE_ADMIN_PATH=. ruby tasks/bug_report_template.rb - - - name: Format coverage - run: | - bin/prepare_coverage - mv coverage/.resultset.json coverage/raw.lint.json - - - name: Save partial coverage as an artifact - uses: actions/upload-artifact@v2 - with: - name: coverage - path: coverage/raw.lint.json +concurrency: + group: ${{ github.repository }}-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} +jobs: test: - name: test (${{ matrix.ruby.name }}, ${{ matrix.deps }}) + name: test (${{ matrix.ruby }}, ${{ matrix.rails }}) runs-on: ${{ matrix.os }} - timeout-minutes: 15 - strategy: fail-fast: false - matrix: - ruby: [{ name: 2.7, value: 2.7.5 }, { name: 2.6, value: 2.6.9 }] - - os: [ubuntu-20.04] - - deps: [rails_60, rails_61, rails_70] - + ruby: + - "3.4" + - "3.3" + - "3.2" + - "3.1" + os: + - ubuntu-latest + rails: + - rails_80 + - rails_72 + - rails_71 + - rails_70 exclude: - - ruby: { name: '2.6', value: 2.6.9 } - os: ubuntu-20.04 - deps: rails_70 - - include: - - ruby: { name: '3.0', value: 3.0.3 } - os: ubuntu-20.04 - deps: rails_60 - - - ruby: { name: '3.0', value: 3.0.3 } - os: ubuntu-20.04 - deps: rails_61 - - - ruby: { name: '3.0', value: 3.0.3 } - os: ubuntu-20.04 - deps: rails_70 - - - ruby: { name: 3.1, value: 3.1.0 } - os: ubuntu-20.04 - deps: rails_61 - - - ruby: { name: 3.1, value: 3.1.0 } - os: ubuntu-20.04 - deps: rails_70 - - - ruby: { name: 3.1, value: 3.1.0 } - os: ubuntu-20.04 - deps: rails_61_turbolinks - - - ruby: { name: 3.1, value: 3.1.0 } - os: ubuntu-20.04 - deps: rails_61_webpacker - - env: - COVERAGE: true - + - ruby: '3.1' + os: ubuntu-latest + rails: rails_80 + - ruby: '3.4' + os: ubuntu-latest + rails: rails_70 steps: - - uses: actions/checkout@v2 - + - uses: actions/checkout@v4 - name: Configure bundler (default) run: | - echo "BUNDLE_GEMFILE=Gemfile" >> $GITHUB_ENV - echo "BUNDLE_PATH=$(pwd)/vendor/bundle" >> $GITHUB_ENV - if: matrix.deps == 'rails_70' - + echo "BUNDLE_GEMFILE=Gemfile" >> "$GITHUB_ENV" + if: matrix.rails == 'rails_80' - name: Configure bundler (alternative) run: | - echo "BUNDLE_GEMFILE=gemfiles/${{ matrix.deps }}/Gemfile" >> $GITHUB_ENV - echo "BUNDLE_PATH=$(pwd)/gemfiles/${{ matrix.deps }}/vendor/bundle" >> $GITHUB_ENV - if: matrix.deps != 'rails_70' - + echo "BUNDLE_GEMFILE=gemfiles/${{ matrix.rails }}/Gemfile" >> "$GITHUB_ENV" + if: matrix.rails != 'rails_80' - uses: ruby/setup-ruby@v1 with: - ruby-version: ${{ matrix.ruby.value }} - rubygems: 3.3.5 + ruby-version: ${{ matrix.ruby }} bundler-cache: true - - - name: Setup git - run: | - git config --global user.email activeadmin@ci.dummy - git config --global user.name ActiveAdmin - + rubygems: latest - name: Create test app run: bin/rake setup - - name: Restore cached RSpec runtimes - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: tmp/parallel_runtime_rspec.log - key: runtimes-rspec-${{ matrix.ruby.value }}-${{ matrix.deps }}-${{ hashFiles('tmp/parallel_runtime_rspec.log') }} - + key: runtimes-rspec-${{ matrix.ruby }}-${{ matrix.rails }}-${{ hashFiles('tmp/parallel_runtime_rspec.log') }} - name: Run RSpec tests + env: + COVERAGE: true run: | bin/parallel_rspec RSPEC_FILESYSTEM_CHANGES=true bin/rspec - - name: Restore cached cucumber runtimes - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: tmp/parallel_runtime_cucumber.log - key: runtimes-cucumber-${{ matrix.ruby.value }}-${{ matrix.deps }}-${{ hashFiles('tmp/parallel_runtime_cucumber.log') }} - + key: runtimes-cucumber-${{ matrix.ruby }}-${{ matrix.rails }}-${{ hashFiles('tmp/parallel_runtime_cucumber.log') }} - name: Run Cucumber features + env: + COVERAGE: true run: | bin/parallel_cucumber --fail-fast bin/cucumber --profile filesystem-changes bin/cucumber --profile class-reloading - - - name: Format coverage - run: | - bin/prepare_coverage - mv coverage/.resultset.json coverage/raw.${{ matrix.ruby.value }}.${{ matrix.deps }}.json - - - name: Save partial coverage as an artifact - uses: actions/upload-artifact@v2 + - name: Rename coverage file by matrix run + run: mv coverage/coverage.xml coverage/coverage-ruby-${{ matrix.ruby }}-${{ matrix.rails }}.xml + - uses: actions/upload-artifact@v4 with: - name: coverage - path: coverage/raw.${{ matrix.ruby.value }}.${{ matrix.deps }}.json + name: coverage-ruby-${{ matrix.ruby }}-${{ matrix.rails }} + path: coverage + if-no-files-found: error upload_coverage: - runs-on: ubuntu-20.04 - - needs: - - lint - - test - + name: Upload Coverage + runs-on: ubuntu-latest + # Do not run on forks + if: ${{ github.repository_owner == 'activeadmin' }} + needs: [test] steps: - - uses: actions/checkout@v2 - - - name: Download partial coverages - uses: actions/download-artifact@v2 + - uses: actions/checkout@v4 + - uses: actions/download-artifact@v4 with: - name: coverage path: coverage + pattern: coverage-ruby-* + merge-multiple: true + - uses: codecov/codecov-action@v5 + with: + token: ${{ secrets.CODECOV_TOKEN }} + directory: coverage + fail_ci_if_error: true - - name: Format, sum & upload results to Code Climate - uses: paambaati/codeclimate-action@v2.7.4 - env: - CC_TEST_REPORTER_ID: 8d5fcf7abea6d56c625104a9d1a81140a588a7f546f4fa9de9bc6ffc8feaaf70 + test_docs_build: + name: Build docs + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: tj-actions/changed-files@v46 + id: changed-files + with: + files: | + docs/** + package*.json + yarn.lock + - uses: actions/setup-node@v4 + if: steps.changed-files.outputs.any_changed == 'true' with: - debug: true - coverageLocations: coverage/raw.*.json:simplecov + node-version: 22 + cache: yarn + - run: yarn install + if: steps.changed-files.outputs.any_changed == 'true' + - run: yarn docs:build + if: steps.changed-files.outputs.any_changed == 'true' diff --git a/.github/workflows/daily.yaml b/.github/workflows/daily.yaml deleted file mode 100644 index 894c960841c..00000000000 --- a/.github/workflows/daily.yaml +++ /dev/null @@ -1,79 +0,0 @@ ---- - -name: daily - -on: - schedule: - - cron: '1 1 * * *' - -jobs: - test: - name: test (${{ matrix.ruby.name }}, ${{ matrix.deps }}) - runs-on: ${{ matrix.os }} - - timeout-minutes: 15 - - strategy: - fail-fast: false - - matrix: - os: [ubuntu-20.04] - ruby: [{ name: jruby-9.3, value: jruby-9.3.2.0 }] - deps: ["rails_60", "rails_61"] - - env: - JRUBY_OPTS: -J-Xmx1024m --dev - - steps: - - uses: actions/checkout@v2 - - - name: Configure bundler (default) - run: | - echo "BUNDLE_GEMFILE=Gemfile" >> $GITHUB_ENV - echo "BUNDLE_PATH=$(pwd)/vendor/bundle" >> $GITHUB_ENV - if: matrix.deps == 'rails_61' - - - name: Configure bundler (alternative) - run: | - echo "BUNDLE_GEMFILE=gemfiles/${{ matrix.deps }}/Gemfile" >> $GITHUB_ENV - echo "BUNDLE_PATH=$(pwd)/gemfiles/${{ matrix.deps }}/vendor/bundle" >> $GITHUB_ENV - if: matrix.deps != 'rails_61' - - - uses: ruby/setup-ruby@v1 - with: - ruby-version: ${{ matrix.ruby.value }} - bundler-cache: true - - - name: Install a specific rubygems version - run: gem update --system 3.2.17 - - - name: Setup git - run: | - git config --global user.email activeadmin@ci.dummy - git config --global user.name ActiveAdmin - - - name: Create test app - run: bin/rake setup - - - name: Restore cached RSpec runtimes - uses: actions/cache@v2 - with: - path: tmp/parallel_runtime_rspec.log - key: runtimes-rspec-${{ matrix.ruby.value }}-${{ matrix.deps }}-${{ hashFiles('tmp/parallel_runtime_rspec.log') }} - - - name: Run RSpec tests - run: | - bin/parallel_rspec spec/ - RSPEC_FILESYSTEM_CHANGES=true bin/rspec - - - name: Restore cached cucumber runtimes - uses: actions/cache@v2 - with: - path: tmp/parallel_runtime_cucumber.log - key: runtimes-cucumber-${{ matrix.ruby.value }}-${{ matrix.deps }}-${{ hashFiles('tmp/parallel_runtime_cucumber.log') }} - - - name: Run Cucumber features - run: | - bin/parallel_cucumber --fail-fast - bin/cucumber --profile filesystem-changes - bin/cucumber --profile class-reloading diff --git a/.github/workflows/docs-deployment.yml b/.github/workflows/docs-deployment.yml new file mode 100644 index 00000000000..50a4c9ae49b --- /dev/null +++ b/.github/workflows/docs-deployment.yml @@ -0,0 +1,47 @@ +name: Docs Deployment + +on: + release: + types: + - published + workflow_dispatch: + +# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages +permissions: + contents: read + pages: write + id-token: write + +# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. +# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. +concurrency: + group: ${{ github.repository }}-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: false + +jobs: + build_docs: + name: Build docs + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 22 + cache: yarn + - uses: actions/configure-pages@v5 + - run: yarn install + - run: yarn docs:build + - uses: actions/upload-pages-artifact@v3 + with: + path: docs/.vitepress/dist + + deploy_docs: + name: Deploy docs site + runs-on: ubuntu-latest + needs: build_docs + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - uses: actions/deploy-pages@v4 + id: deployment diff --git a/.github/workflows/eslint.yml b/.github/workflows/eslint.yml new file mode 100644 index 00000000000..8d81db9d613 --- /dev/null +++ b/.github/workflows/eslint.yml @@ -0,0 +1,33 @@ +name: ESLint + +on: + pull_request: + +env: + NODE_VERSION: ${{ vars.ESLINT_NODE_VERSION || '22.x' }} + +jobs: + eslint: + name: Run eslint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + cache: yarn + - uses: tj-actions/changed-files@v46 + id: changed-files + with: + files: | + **.js + package*.json + yarn.lock + .github/workflows/eslint.yml + - uses: reviewdog/action-eslint@v1 + if: steps.changed-files.outputs.any_changed == 'true' + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + filter_mode: nofilter # added (default), diff_context, file, nofilter + fail_level: any + reporter: github-pr-check diff --git a/.github/workflows/gherkin-lint.yml b/.github/workflows/gherkin-lint.yml new file mode 100644 index 00000000000..4a9fc1911e6 --- /dev/null +++ b/.github/workflows/gherkin-lint.yml @@ -0,0 +1,32 @@ +name: Gherkin Lint + +on: + pull_request: + +env: + NODE_VERSION: 22.x + +jobs: + gherkin_lint: + name: Run gherkin-lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: tj-actions/changed-files@v46 + id: changed-files + with: + files: | + **.feature + .gherkin-lintrc + package*.json + yarn.lock + .github/workflows/gherkin-lint.yml + - uses: actions/setup-node@v4 + if: steps.changed-files.outputs.any_changed == 'true' + with: + node-version: ${{ env.NODE_VERSION }} + cache: yarn + - run: yarn install --frozen-lockfile --immutable + if: steps.changed-files.outputs.any_changed == 'true' + - run: yarn gherkin-lint + if: steps.changed-files.outputs.any_changed == 'true' diff --git a/.github/workflows/github-actions-lint.yml b/.github/workflows/github-actions-lint.yml new file mode 100644 index 00000000000..44e0a2453a3 --- /dev/null +++ b/.github/workflows/github-actions-lint.yml @@ -0,0 +1,24 @@ +name: GitHub Actions Lint + +on: + pull_request: + +jobs: + github_actions_lint: + name: Run actionlint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: tj-actions/changed-files@v46 + id: changed-files + with: + files: | + .github/workflows/*.yaml + .github/workflows/*.yml + - uses: reviewdog/action-actionlint@v1 + if: steps.changed-files.outputs.any_changed == 'true' + with: + fail_level: any + filter_mode: nofilter # added (default), diff_context, file, nofilter + github_token: ${{ secrets.GITHUB_TOKEN }} + reporter: github-pr-check diff --git a/.github/workflows/markdown-lint.yml b/.github/workflows/markdown-lint.yml new file mode 100644 index 00000000000..012138ea762 --- /dev/null +++ b/.github/workflows/markdown-lint.yml @@ -0,0 +1,29 @@ +name: Markdown Lint + +on: + pull_request: + +env: + MARKDOWNLINT_FLAGS: ${{ vars.REVIEWDOG_MARKDOWNLINT_FLAGS || '--git-recurse .' }} + +jobs: + markdownlint: + name: Run markdownlint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: tj-actions/changed-files@v46 + id: changed-files + with: + files: | + **.md + .markdownlint.yml + .github/workflows/markdown-lint.yml + - uses: reviewdog/action-markdownlint@v0 + if: steps.changed-files.outputs.any_changed == 'true' + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + filter_mode: nofilter # added (default), diff_context, file, nofilter + fail_level: any + markdownlint_flags: ${{ env.MARKDOWNLINT_FLAGS }} + reporter: github-pr-check diff --git a/.github/workflows/rubocop.yml b/.github/workflows/rubocop.yml new file mode 100644 index 00000000000..f40833230c1 --- /dev/null +++ b/.github/workflows/rubocop.yml @@ -0,0 +1,44 @@ +name: Rubocop + +on: + pull_request: + +env: + RUBY_VERSION: ${{ vars.RUBOCOP_RUBY_VERSION || '3.4' }} + +jobs: + rubocop: + name: Run rubocop + runs-on: ubuntu-latest + env: + BUNDLE_ONLY: ${{ vars.RUBOCOP_BUNDLE_ONLY || 'rubocop' }} + steps: + - uses: actions/checkout@v4 + - uses: tj-actions/changed-files@v46 + id: changed-files + with: + files: | + .github/workflows/rubocop.yml + .rubocop.yml + **.rb + **.rake + **.arb + bin/* + gemfiles/**/Gemfile + Gemfile* + Rakefile + *.gemspec + .simplecov + - uses: ruby/setup-ruby@v1 + if: steps.changed-files.outputs.any_changed == 'true' + with: + ruby-version: ${{ env.RUBY_VERSION }} + bundler-cache: true + - uses: reviewdog/action-rubocop@v2 + if: steps.changed-files.outputs.any_changed == 'true' + with: + fail_level: any + filter_mode: nofilter # added (default), diff_context, file, nofilter + github_token: ${{ secrets.GITHUB_TOKEN }} + skip_install: true + use_bundler: true diff --git a/.github/workflows/typos.yml b/.github/workflows/typos.yml new file mode 100644 index 00000000000..953b4099582 --- /dev/null +++ b/.github/workflows/typos.yml @@ -0,0 +1,17 @@ +name: Typos + +on: + pull_request: + +jobs: + typos: + name: Run typos + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: reviewdog/action-typos@v1 + with: + fail_level: any + filter_mode: nofilter # added (default), diff_context, file, nofilter + github_token: ${{ secrets.GITHUB_TOKEN }} + reporter: github-pr-check diff --git a/.github/workflows/yaml-lint.yml b/.github/workflows/yaml-lint.yml new file mode 100644 index 00000000000..be0a0d73d6d --- /dev/null +++ b/.github/workflows/yaml-lint.yml @@ -0,0 +1,24 @@ +name: YAML Lint + +on: + pull_request: + +jobs: + yaml_lint: + name: Run yamllint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: tj-actions/changed-files@v46 + id: changed-files + with: + files: | + **.yaml + **.yml + - uses: reviewdog/action-yamllint@v1 + if: steps.changed-files.outputs.any_changed == 'true' + with: + fail_level: any + filter_mode: nofilter # added (default), diff_context, file, nofilter + github_token: ${{ secrets.GITHUB_TOKEN }} + reporter: github-pr-check diff --git a/.gitignore b/.gitignore index e5c8f1cbe65..e26ea760672 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,8 @@ /.rspec_failures /node_modules /src +/vendor/bundle +/rails_70 +/dist +docs/.vitepress/cache +docs/.vitepress/dist diff --git a/.markdownlint.yml b/.markdownlint.yml new file mode 100644 index 00000000000..9fe39abc0d1 --- /dev/null +++ b/.markdownlint.yml @@ -0,0 +1,5 @@ +default: true +MD002: false +MD013: false +MD024: false +MD041: false diff --git a/.mdlrc b/.mdlrc deleted file mode 100644 index 3c6de4a3a39..00000000000 --- a/.mdlrc +++ /dev/null @@ -1 +0,0 @@ -style "config/mdl_style.rb" diff --git a/.rubocop.yml b/.rubocop.yml index d543dd0b5b3..1c5d1580e51 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -4,23 +4,26 @@ inherit_mode: merge: - Include -require: +plugins: + - rubocop-capybara - rubocop-packaging + - rubocop-performance - rubocop-rails - rubocop-rspec AllCops: DisabledByDefault: true - TargetRubyVersion: 2.6 + TargetRubyVersion: 3.1 + TargetRailsVersion: 7.0 Exclude: - - tmp/development_apps/**/* - - tmp/test_apps/**/* - - gemfiles/rails_60/vendor/bundle/**/* - - gemfiles/rails_61/vendor/bundle/**/* - - gemfiles/rails_61_turbolinks/vendor/bundle/**/* - - gemfiles/rails_61_webpacker/vendor/bundle/**/* - - vendor/bundle/**/* + - .git/**/* + - .github/**/* + - bin/**/* + - gemfiles/**/vendor/**/* + - node_modules/**/* + - tmp/**/* + - vendor/**/* Include: - gemfiles/*/Gemfile @@ -30,13 +33,52 @@ AllCops: StyleGuideCopsOnly: false -Layout/EndAlignment: +Capybara: Enabled: true -Layout/HashAlignment: +Capybara/ClickLinkOrButtonStyle: Enabled: true -Lint/ParenthesesAsGroupedExpression: +Capybara/CurrentPathExpectation: + Enabled: true + +Capybara/FindAllFirst: + Enabled: true + +Capybara/MatchStyle: + Enabled: true + +Capybara/NegationMatcher: + Enabled: true + +Capybara/NegationMatcherAfterVisit: + Enabled: true + +Capybara/RedundantWithinFind: + Enabled: true + +Capybara/RSpec/HaveSelector: + Enabled: true + +Capybara/RSpec/PredicateMatcher: + Enabled: true + +Capybara/SpecificActions: + Enabled: true + +Capybara/SpecificFinders: + Enabled: true + +Capybara/SpecificMatcher: + Enabled: true + +Capybara/VisibilityMatcher: + Enabled: true + +Layout/EndAlignment: + Enabled: true + +Layout/HashAlignment: Enabled: true Layout/AccessModifierIndentation: @@ -91,6 +133,21 @@ Layout/IndentationStyle: Enabled: true EnforcedStyle: spaces +Lint/AmbiguousOperator: + Enabled: true + +Lint/AmbiguousRegexpLiteral: + Enabled: true + +Lint/ParenthesesAsGroupedExpression: + Enabled: true + +Lint/UselessAccessModifier: + Enabled: true + +Lint/UselessAssignment: + Enabled: true + Packaging/BundlerSetupInTests: Enabled: true @@ -103,6 +160,170 @@ Packaging/RequireHardcodingLib: Packaging/RequireRelativeHardcodingLib: Enabled: true +Performance: + Enabled: true + +Performance/AncestorsInclude: + Enabled: false + +Performance/ArraySemiInfiniteRangeSlice: + Enabled: false + +Performance/BigDecimalWithNumericArgument: + Enabled: true + +Performance/BindCall: + Enabled: true + +Performance/BlockGivenWithExplicitBlock: + Enabled: true + +Performance/Caller: + Enabled: true + +Performance/CaseWhenSplat: + Enabled: true + +Performance/Casecmp: + Enabled: false + +Performance/ChainArrayAllocation: + Enabled: false + +Performance/CollectionLiteralInLoop: + Enabled: true + Exclude: + - spec/**/* + +Performance/CompareWithBlock: + Enabled: true + +Performance/ConcurrentMonotonicTime: + Enabled: true + +Performance/ConstantRegexp: + Enabled: true + +Performance/Count: + Enabled: true + +Performance/DeletePrefix: + Enabled: true + +Performance/DeleteSuffix: + Enabled: true + +Performance/Detect: + Enabled: true + +Performance/DoubleStartEndWith: + Enabled: true + IncludeActiveSupportAliases: true + +Performance/EndWith: + Enabled: true + +Performance/FixedSize: + Enabled: true + +Performance/FlatMap: + Enabled: true + EnabledForFlattenWithoutParams: false + +Performance/InefficientHashSearch: + Enabled: true + +Performance/IoReadlines: + Enabled: true + +Performance/MapCompact: + Enabled: false + +Performance/MapMethodChain: + Enabled: false + +Performance/MethodObjectAsBlock: + Enabled: true + +Performance/OpenStruct: + Enabled: true + +Performance/RangeInclude: + Enabled: true + +Performance/RedundantBlockCall: + Enabled: false + +Performance/RedundantEqualityComparisonBlock: + Enabled: false + +Performance/RedundantMatch: + Enabled: true + +Performance/RedundantMerge: + Enabled: true + MaxKeyValuePairs: 2 + +Performance/RedundantSortBlock: + Enabled: true + +Performance/RedundantSplitRegexpArgument: + Enabled: true + +Performance/RedundantStringChars: + Enabled: true + +Performance/RegexpMatch: + Enabled: true + +Performance/ReverseEach: + Enabled: true + +Performance/ReverseFirst: + Enabled: true + +Performance/SelectMap: + Enabled: false + +Performance/Size: + Enabled: true + +Performance/SortReverse: + Enabled: true + +Performance/Squeeze: + Enabled: true + +Performance/StartWith: + Enabled: true + +Performance/StringIdentifierArgument: + Enabled: true + +Performance/StringInclude: + Enabled: true + +Performance/StringReplacement: + Enabled: true + +Performance/StringBytesize: + Enabled: true + +Performance/Sum: + Enabled: false + +Performance/TimesMap: + Enabled: true + +Performance/UnfreezeString: + Enabled: true + +Performance/UriDefaultParser: + Enabled: true + +Performance/ZipWithoutBlock: + Enabled: true + Rails/FilePath: Enabled: true EnforcedStyle: slashes @@ -134,6 +355,7 @@ Style/FrozenStringLiteralComment: Style/HashSyntax: Enabled: true + EnforcedShorthandSyntax: never Style/ParallelAssignment: Enabled: true @@ -144,7 +366,7 @@ Layout/IndentationConsistency: Layout/IndentationWidth: Enabled: true -Naming/PredicateName: +Naming/PredicatePrefix: Enabled: true ForbiddenPrefixes: @@ -156,8 +378,7 @@ Naming/PredicateName: - has_many_actions Style/StringLiterals: - Enabled: true - EnforcedStyle: double_quotes + Enabled: false Style/TrailingCommaInArguments: Enabled: true @@ -194,6 +415,3 @@ Layout/SpaceInsideHashLiteralBraces: Layout/SpaceInsideParens: Enabled: true - -Lint/UselessAccessModifier: - Enabled: true diff --git a/.simplecov b/.simplecov index 893c111e3a8..154ddf432d9 100644 --- a/.simplecov +++ b/.simplecov @@ -1,11 +1,12 @@ # frozen_string_literal: true SimpleCov.start do + add_filter %r{^/spec/} add_filter "tmp/development_apps/" add_filter "tmp/test_apps/" + add_filter "tasks/test_application.rb" end -if ENV["CI"] == "true" - SimpleCov.formatters = [ - SimpleCov::Formatter::HTMLFormatter - ] +if ENV["COVERAGE"] == "true" + require "simplecov-cobertura" + SimpleCov.formatter = SimpleCov::Formatter::CoberturaFormatter end diff --git a/.yamllint.yml b/.yamllint.yml new file mode 100644 index 00000000000..b48d900243b --- /dev/null +++ b/.yamllint.yml @@ -0,0 +1,18 @@ +# https://yamllint.readthedocs.io/en/stable/configuration.html +extends: default +ignore: | + node_modules/ + tmp/ + vendor/ + cucumber.yml +rules: # https://yamllint.readthedocs.io/en/stable/rules.html + comments: + min-spaces-from-content: 1 + document-start: disable + line-length: disable + truthy: + allowed-values: + - "true" + - "false" + - "on" + - "off" diff --git a/.yardopts b/.yardopts deleted file mode 100644 index fe65f43518e..00000000000 --- a/.yardopts +++ /dev/null @@ -1,7 +0,0 @@ -lib/**/*.rb ---protected ---no-private -- -README.md -CHANGELOG.md -docs/**/*.md diff --git a/CHANGELOG.md b/CHANGELOG.md index cfe8e438ff9..dfb583197cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,133 @@ # Changelog -## Unreleased +[Future changelogs have moved to GitHub Releases](https://github.com/activeadmin/activeadmin/releases) + +## 3.2.0 [☰](https://github.com/activeadmin/activeadmin/compare/v3.1.0..v3.2.0) + +### Security Fixes + +* Backport protect against CSV Injection. [#8167] by [@mgrunberg] + +### Enhancements + +* Backport support citext column type in string filter. [#8165] by [@mgrunberg] +* Backport provide detail in DB statement timeout error for filters. [#8163] by [@mgrunberg] + +### Bug Fixes + +* Backport make sure menu creation does not modify menu options. [#8166] by [@mgrunberg] +* Backport ransack error with filters when ActiveStorage is used. [#8164] by [@mgrunberg] + +## 3.1.0 [☰](https://github.com/activeadmin/activeadmin/compare/v3.0.0..v3.1.0) + +### Enhancements + +* Support Rails 7.1. [#8102] by [@mgrunberg] +* Remove deprecated usage of ActiveSupport::Deprecation singleton. [#8106] by [@mgrunberg] +* Replace to_formatted_s with to_s to convert date to string. [#8105] by [@mgrunberg] +* Remove upper bound dependency limits from gemspec. [#8098] by [@javierjulio] + +## 3.0.0 [☰](https://github.com/activeadmin/activeadmin/compare/v2.14.0..v3.0.0) + +### Breaking Changes + +* Remove custom Ransack predicates that were MetaSearch backports. [#8010] by [@javierjulio] +* Require Ransack v4. [#8009] by [@javierjulio] + +### Enhancements + +* Use display name fallback if blank display name result. [#6342] by [@javierjulio] + +### Translation Improvements + +* Improve Swedish translations. [#7993] by [@carlottostromstedt] + +## 2.14.0 [☰](https://github.com/activeadmin/activeadmin/compare/v2.13.1..v2.14.0) + +### Enhancements + +* Add csp_meta_tag to layout. [#7986] by [@javierjulio] +* Update config.register_javascript with options support. [#7002] by [@lanzhiheng] +* Use `csrf_meta_tags` in place of singular version. [#7985] by [@javierjulio] +* Allow different new and edit rules in authorization adapters. [#6535] by [@timwis] + +### Bug Fixes + +* Fix form layout for hints and checkboxes. [#7772] by [@JewelSam] +* Update filters disabled error to include specific action. [#6195] by [@javawizard] +* Fix Comments controller destroy declaration. [#6482] by [@bliof] +* Stop pagination elements from overflowing outside of panel container. [#7599] by [@ray-curran] + +### Translation Improvements + +* Update vi locale with more translations. [#7984] by [@rs-phunt] +* Update zh-CN locale with multiple corrections. [#7944] by [@hfl] +* Fix typo in Vietnamese locale for filter text. [#7920] by [@tvziet] +* Improve French translation. [#7653] by [@cprodhomme] + +### Documentation + +* Add more documentation about PORO decorator requirements. [#7556] by [@sanfrecce-osaka] +* Add Load Paths docs to the active_admin.rb template. [#7541] by [@gabo-cs] + +### Performance + +* Removes docs from exported gem. [#7013] by [@brunoarueira] + +## 2.13.1 [☰](https://github.com/activeadmin/activeadmin/compare/v2.13.0..v2.13.1) + +### Bug Fixes + +* Honor load paths order when loading admin files. [#7488] by [@tf] +* Fix passing expected hash payload argument. [#7487] by [@ispyropoulos] + +## 2.13.0 [☰](https://github.com/activeadmin/activeadmin/compare/v2.12.0..v2.13.0) + +### Documentation + +* Update validation errors documentation to account for deprecated `ActiveModel::Errors#keys`. [#7475] by [@amit] + +### Dependency Changes + +* Drop rails 6.0 support. [#7476] by [@deivid-rodriguez] + +### Performance + +* Fix pundit performance. [#7479] by [@deivid-rodriguez] + +## 2.12.0 [☰](https://github.com/activeadmin/activeadmin/compare/v2.11.2..v2.12.0) + +### Enhancements + +* Add Ransack 3 compatibility. [#7453] by [@tagliala] + +### Bug Fixes + +* Fix pundit namespace detection. [#7144] by [@vlad-psh] + +### Documentation + +* Don't mention webpacker as the default asset generator in Rails. [#7377] by [@jaynetics] + +### Performance + +* Avoid duplicate work when downloading CSV. [#7336] by [@deivid-rodriguez] + +## 2.11.2 [☰](https://github.com/activeadmin/activeadmin/compare/v2.11.1..v2.11.2) + +### Bug Fixes + +* Fix disappearing BOM option for `CSVBuilder`. [#7170] by [@Karoid] + +## 2.11.1 [☰](https://github.com/activeadmin/activeadmin/compare/v2.11.0..v2.11.1) + +### Enhancements + +* Add turbolinks support to has many js. [#7384] by [@amiel] + +### Documentation + +* Remove `insert_tag` from Form-Partial docs. [#7394] by [@TonyArra] ## 2.11.0 [☰](https://github.com/activeadmin/activeadmin/compare/v2.10.1..v2.11.0) @@ -324,7 +451,7 @@ _No changes_. * Fixed `if:` scope option when a lambda is passed. [#5501] by [@deivid-rodriguez] * Comment validation adding redundant errors when resource is missing. [#5517] by [@deivid-rodriguez] * Fixed resource filtering by association when the resource has custom primary key. [#5446] by [@wasifhossain] -* Fixed "create anoter" checkbox styling. [#5324] by [@faucct] +* Fixed "create another" checkbox styling. [#5324] by [@faucct] ## 1.3.1 [☰](https://github.com/activeadmin/activeadmin/compare/v1.3.0..v1.3.1) @@ -484,7 +611,7 @@ _No changes_. * Prevents access to formats that the user not permitted to see. [#4867] by [@Fivell] and [@timoschilling] * Prevents potential DOS attack via Ruby symbols. [#1926] by [@seanlinsley] - * [this isn't an issue for those using Ruby >= 2.2](http://rubykaigi.org/2014/presentation/S-NarihiroNakamura) + * [this isn't an issue for those using Ruby >= 2.2](https://rubykaigi.org/2014/presentation/S-NarihiroNakamura) ### Bug Fixes @@ -688,6 +815,7 @@ Please check [0-6-stable] for previous changes. [#6099]: https://github.com/activeadmin/activeadmin/pull/6099 [#6124]: https://github.com/activeadmin/activeadmin/pull/6124 [#6149]: https://github.com/activeadmin/activeadmin/pull/6149 +[#6195]: https://github.com/activeadmin/activeadmin/pull/6195 [#6198]: https://github.com/activeadmin/activeadmin/pull/6198 [#6210]: https://github.com/activeadmin/activeadmin/pull/6210 [#6221]: https://github.com/activeadmin/activeadmin/pull/6221 @@ -696,14 +824,17 @@ Please check [0-6-stable] for previous changes. [#6315]: https://github.com/activeadmin/activeadmin/pull/6315 [#6318]: https://github.com/activeadmin/activeadmin/pull/6318 [#6341]: https://github.com/activeadmin/activeadmin/pull/6341 +[#6342]: https://github.com/activeadmin/activeadmin/pull/6342 [#6368]: https://github.com/activeadmin/activeadmin/pull/6368 [#6393]: https://github.com/activeadmin/activeadmin/pull/6393 [#6422]: https://github.com/activeadmin/activeadmin/pull/6422 [#6451]: https://github.com/activeadmin/activeadmin/pull/6451 [#6460]: https://github.com/activeadmin/activeadmin/pull/6460 [#6462]: https://github.com/activeadmin/activeadmin/pull/6462 +[#6482]: https://github.com/activeadmin/activeadmin/pull/6482 [#6487]: https://github.com/activeadmin/activeadmin/pull/6487 [#6523]: https://github.com/activeadmin/activeadmin/pull/6523 +[#6535]: https://github.com/activeadmin/activeadmin/pull/6535 [#6536]: https://github.com/activeadmin/activeadmin/pull/6536 [#6548]: https://github.com/activeadmin/activeadmin/pull/6548 [#6583]: https://github.com/activeadmin/activeadmin/pull/6583 @@ -718,8 +849,12 @@ Please check [0-6-stable] for previous changes. [#6936]: https://github.com/activeadmin/activeadmin/pull/6936 [#6954]: https://github.com/activeadmin/activeadmin/pull/6954 [#6959]: https://github.com/activeadmin/activeadmin/pull/6959 +[#7002]: https://github.com/activeadmin/activeadmin/pull/7002 +[#7013]: https://github.com/activeadmin/activeadmin/pull/7013 [#7095]: https://github.com/activeadmin/activeadmin/pull/7095 [#7127]: https://github.com/activeadmin/activeadmin/pull/7127 +[#7144]: https://github.com/activeadmin/activeadmin/pull/7144 +[#7170]: https://github.com/activeadmin/activeadmin/pull/7170 [#7181]: https://github.com/activeadmin/activeadmin/pull/7181 [#7205]: https://github.com/activeadmin/activeadmin/pull/7205 [#7235]: https://github.com/activeadmin/activeadmin/pull/7235 @@ -727,10 +862,38 @@ Please check [0-6-stable] for previous changes. [#7262]: https://github.com/activeadmin/activeadmin/pull/7262 [#7293]: https://github.com/activeadmin/activeadmin/pull/7293 [#7332]: https://github.com/activeadmin/activeadmin/pull/7332 +[#7336]: https://github.com/activeadmin/activeadmin/pull/7336 [#7340]: https://github.com/activeadmin/activeadmin/pull/7340 [#7341]: https://github.com/activeadmin/activeadmin/pull/7341 [#7349]: https://github.com/activeadmin/activeadmin/pull/7349 [#7350]: https://github.com/activeadmin/activeadmin/pull/7350 +[#7377]: https://github.com/activeadmin/activeadmin/pull/7377 +[#7384]: https://github.com/activeadmin/activeadmin/pull/7384 +[#7394]: https://github.com/activeadmin/activeadmin/pull/7394 +[#7453]: https://github.com/activeadmin/activeadmin/pull/7453 +[#7475]: https://github.com/activeadmin/activeadmin/pull/7475 +[#7476]: https://github.com/activeadmin/activeadmin/pull/7476 +[#7479]: https://github.com/activeadmin/activeadmin/pull/7479 +[#7487]: https://github.com/activeadmin/activeadmin/pull/7487 +[#7488]: https://github.com/activeadmin/activeadmin/pull/7488 +[#7541]: https://github.com/activeadmin/activeadmin/pull/7541 +[#7556]: https://github.com/activeadmin/activeadmin/pull/7556 +[#7599]: https://github.com/activeadmin/activeadmin/pull/7599 +[#7653]: https://github.com/activeadmin/activeadmin/pull/7653 +[#7772]: https://github.com/activeadmin/activeadmin/pull/7772 +[#7920]: https://github.com/activeadmin/activeadmin/pull/7920 +[#7944]: https://github.com/activeadmin/activeadmin/pull/7944 +[#7984]: https://github.com/activeadmin/activeadmin/pull/7984 +[#7985]: https://github.com/activeadmin/activeadmin/pull/7985 +[#7986]: https://github.com/activeadmin/activeadmin/pull/7986 +[#7993]: https://github.com/activeadmin/activeadmin/pull/7993 +[#8009]: https://github.com/activeadmin/activeadmin/pull/8009 +[#8010]: https://github.com/activeadmin/activeadmin/pull/8010 +[#8098]: https://github.com/activeadmin/activeadmin/pull/8098 +[#8102]: https://github.com/activeadmin/activeadmin/pull/8102 +[#8105]: https://github.com/activeadmin/activeadmin/pull/8105 +[#8106]: https://github.com/activeadmin/activeadmin/pull/8106 + [@1000ship]: https://github.com/1000ship [@5t111111]: https://github.com/5t111111 @@ -741,13 +904,17 @@ Please check [0-6-stable] for previous changes. [@alejandroperea]: https://github.com/alejandroperea [@alex-bogomolov]: https://github.com/alex-bogomolov [@amiel]: https://github.com/amiel +[@amit]: https://github.com/amit [@amiuhle]: https://github.com/amiuhle [@andreslemik]: https://github.com/andreslemik [@bartoszkopinski]: https://github.com/bartoszkopinski +[@bliof]: https://github.com/bliof [@blocknotes]: https://github.com/blocknotes [@bolshakov]: https://github.com/bolshakov +[@brunoarueira]: https://github.com/brunoarueira [@brunvez]: https://github.com/brunvez [@buren]: https://github.com/buren +[@carlottostromstedt]: https://github.com/carlottostromstedt [@chancancode]: https://github.com/chancancode [@chrp]: https://github.com/chrp [@chumakoff]: https://github.com/chumakoff @@ -767,33 +934,40 @@ Please check [0-6-stable] for previous changes. [@Fivell]: https://github.com/Fivell [@Fs00]: https://github.com/Fs00 [@fuzziness]: https://github.com/fuzziness +[@gabo-cs]: https://github.com/gabo-cs [@giapnhdev]: https://github.com/giapnhdev [@gigorok]: https://github.com/gigorok [@glebtv]: https://github.com/glebtv [@gonzedge]: https://github.com/gonzedge [@guigs]: https://github.com/guigs [@HappyKadaver]: https://github.com/HappyKadaver +[@hfl]: https://github.com/hfl [@imcvampire]: https://github.com/imcvampire [@innparusu95]: https://github.com/innparusu95 [@ionut998]: https://github.com/ionut998 [@irmela]: https://github.com/irmela +[@ispyropoulos]: https://github.com/ispyropoulos [@Ivanov-Anton]: https://github.com/Ivanov-Anton [@jasl]: https://github.com/jasl [@javawizard]: https://github.com/javawizard [@javierjulio]: https://github.com/javierjulio [@jawa]: https://github.com/jawa +[@jaynetics]: https://github.com/jaynetics +[@JewelSam]: https://github.com/JewelSam [@JiiHu]: https://github.com/JiiHu [@jiikko]: https://github.com/jiikko [@johnnyshields]: https://github.com/johnnyshields [@jscheid]: https://github.com/jscheid [@juril33t]: https://github.com/juril33t [@jwesorick]: https://github.com/jwesorick +[@Karoid]: https://github.com/Karoid [@kjeldahl]: https://github.com/kjeldahl [@ko-lem]: https://github.com/ko-lem [@kobeumut]: https://github.com/kobeumut [@Kris-LIBIS]: https://github.com/Kris-LIBIS [@krzcho]: https://github.com/krzcho [@kwent]: https://github.com/kwent +[@lanzhiheng]: https://github.com/lanzhiheng [@leio10]: https://github.com/leio10 [@littleforest]: https://github.com/littleforest [@Looooong]: https://github.com/Looooong @@ -816,11 +990,14 @@ Please check [0-6-stable] for previous changes. [@PChambino]: https://github.com/PChambino [@potatosalad]: https://github.com/potatosalad [@pranas]: https://github.com/pranas +[@ray-curran]: https://github.com/ray-curran [@renotocn]: https://github.com/renotocn [@rn0rno]: https://github.com/rn0rno [@RobinvanderVliet]: https://github.com/RobinvanderVliet [@rogerkk]: https://github.com/rogerkk [@roramirez]: https://github.com/roramirez +[@rs-phunt]: https://github.com/rs-phunt +[@sanfrecce-osaka]: https://github.com/sanfrecce-osaka [@seanlinsley]: https://github.com/seanlinsley [@sergey-alekseev]: https://github.com/sergey-alekseev [@sgara]: https://github.com/sgara @@ -833,11 +1010,15 @@ Please check [0-6-stable] for previous changes. [@stereoscott]: https://github.com/stereoscott [@tagliala]: https://github.com/tagliala [@taralbass]: https://github.com/taralbass +[@tf]: https://github.com/tf [@tiagotex]: https://github.com/tiagotex [@timoschilling]: https://github.com/timoschilling [@TimPetricola]: https://github.com/TimPetricola +[@timwis]: https://github.com/timwis [@tomgilligan]: https://github.com/tomgilligan +[@TonyArra]: https://github.com/TonyArra [@tordans]: https://github.com/tordans +[@tvziet]: https://github.com/tvziet [@utkarsh2102]: https://github.com/utkarsh2102 [@varyonic]: https://github.com/varyonic [@vcsjones]: https://github.com/vcsjones diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index aba557ecab4..fb5d95038d6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,53 +1,34 @@ -## Contributing +# Contributing -First off, thank you for considering contributing to Active Admin. It's people -like you that make Active Admin such a great tool. +Thanks for your interest in contributing to ActiveAdmin! Please take a moment to review this document **before submitting a pull request**. -### Where do I go from here? +## Pull requests -If you've noticed a bug or have a feature request, [make one][new issue]! It's -generally best if you get confirmation of your bug or approval for your feature -request this way before starting to code. +**Please ask first before starting work on any significant new features.** -If you have a general question about activeadmin, you can post it on [Stack -Overflow], the issue tracker is only for bugs and feature requests. +It's never a fun experience to have your pull request declined after investing a lot of time and effort into a new feature. To avoid this from happening, we request that contributors create [a feature request](https://github.com/activeadmin/activeadmin/discussions/new?category=ideas) to first discuss any new ideas. Your ideas and suggestions are welcome! -### Fork & create a branch +Please ensure that the tests are passing when submitting a pull request. If you're adding new features to ActiveAdmin, please include tests. -If this is something you think you can fix, then [fork Active Admin] and create -a branch with a descriptive name. +## Where do I go from here? -A good branch name would be (where issue #325 is the ticket you're working on): +For any questions, support, or ideas, etc. [please create a GitHub discussion](https://github.com/activeadmin/activeadmin/discussions/new). If you've noticed a bug, [please submit an issue][new issue]. -```sh -git checkout -b 325-add-japanese-translations -``` +### Fork and create a branch -### Get the test suite running +If this is something you think you can fix, then [fork Active Admin] and create +a branch with a descriptive name. -Make sure you're using a recent ruby and have the `bundler` gem installed, at -least version `1.14.3`. +### Get the test suite running -You'll also need chrome installed in order to run cucumber scenarios. +Make sure you're using a recent Ruby and Node version. You'll also need Chrome installed in order to run Cucumber scenarios. Now install the development dependencies: ```sh +gem install foreman bundle install -``` - -Then install javascript dependencies with [Yarn] (requires a current version of [Node.js]): - -```sh -bin/yarn install -``` - -JS assets are located in `app/javascript/active_admin`. The config will take care of compiling a complete bundle with [Rollup] using the `build` script and exported to `app/assets/javascripts/active_admin/base.js` ready to be used by Sprockets. - -To update javascript bundle run (add `-w` flag for watch mode): - -```sh -bin/yarn build +yarn install ``` Now you should be able to run the entire suite using: @@ -56,30 +37,23 @@ Now you should be able to run the entire suite using: bin/rake ``` -The test run will generate a sample Rails application in `tmp/test_apps` to run the -tests against. +The task will generate a sample Rails application in `tmp/test_apps` to run the +test suite against. If you want to test against a Rails version different from the latest, make sure you use the correct Gemfile, for example: ```sh -export BUNDLE_GEMFILE=gemfiles/rails_60/Gemfile +export BUNDLE_GEMFILE=gemfiles/rails_61/Gemfile ``` -**Warning** SCSS assets are aimed to be used indifferently with Sprockets **and** webpacker. -As such, make sure not to use any sass-rails directives such as `asset-url` or `image-url`. - ### Implement your fix or feature -At this point, you're ready to make your changes! Feel free to ask for help; -everyone is a beginner at first :smile_cat: +At this point, you're ready to make your changes. Feel free to ask for help. ### View your changes in a Rails application -Active Admin is meant to be used by humans, not cucumbers. So make sure to take -a look at your changes in a browser. - -To boot up a test Rails app: +Make sure to take a look at your changes in a browser. To boot up a test Rails app: ```sh bin/rake local server @@ -88,11 +62,7 @@ bin/rake local server This will automatically create a Rails app if none already exists, and store it in the `tmp/development_apps` folder. -You should now be able to open in your browser. -You can log in using: - -*User*: admin@example.com -*Password*: password +You should now be able to open in your browser and log in using `admin@example.com` and `password`. If you need to perform any other commands on the test application, just pass them to the `local` rake task. For example, to boot the rails console: @@ -101,95 +71,32 @@ them to the `local` rake task. For example, to boot the rails console: bin/rake local console ``` -Or to migrate the database, if you create a new migration or just play around -with the db: +Or to migrate the database for a new migration: ```sh bin/rake local db:migrate ``` -### Get the style right - -Your patch should follow the same conventions & pass the same code quality -checks as the rest of the project. `bin/rake lint` will give you feedback in -this regard. You can check & fix style issues by running each linter -individually. Run `bin/rake -T lint` to see the available linters. - -### Make a Pull Request - -At this point, you should switch back to your master branch and make sure it's -up to date with Active Admin's master branch: - -```sh -git remote add upstream git@github.com:activeadmin/activeadmin.git -git checkout master -git pull upstream master -``` - -Then update your feature branch from your local copy of master, and push it! - -```sh -git checkout 325-add-japanese-translations -git rebase master -git push --set-upstream origin 325-add-japanese-translations -``` - -Finally, go to GitHub and [make a Pull Request][] :D - -Github Actions will run our test suite against all supported Rails versions. We -care about quality, so your PR won't be merged until all tests pass. It's -unlikely, but it's possible that your changes pass tests in one Rails version -but fail in another. In that case, you'll have to setup your development -environment (as explained in step 3) to use the problematic Rails version, and -investigate what's going on! - -### Keeping your Pull Request updated - -If a maintainer asks you to "rebase" your PR, they're saying that a lot of code -has changed, and that you need to update your branch so it's easier to merge. - -To learn more about rebasing in Git, there are a lot of [good][git rebasing] -[resources][interactive rebase] but here's the suggested workflow: - -```sh -git checkout 325-add-japanese-translations -git pull --rebase upstream master -git push --force-with-lease 325-add-japanese-translations -``` +### Create a Pull Request -### Merging a PR (maintainers only) +At this point, if your changes look good and tests are passing, you are ready to create a pull request. -A PR can only be merged into master by a maintainer if: +Github Actions will run our test suite against all supported Rails versions. It's possible that your changes pass tests in one Rails version but fail in another. In that case, you'll have to setup your development +environment with the Gemfile for the problematic Rails version, and investigate what's going on. -* It is passing CI. -* It has been approved by at least two maintainers. If it was a maintainer who - opened the PR, only one extra approval is needed. -* It has no requested changes. -* It is up to date with current master. +## Merging a PR (maintainers only) -Any maintainer is allowed to merge a PR if all of these conditions are -met. +A PR can only be merged into master by a maintainer if: CI is passing, approved by another maintainer and is up to date with the default branch. Any maintainer is allowed to merge a PR if all of these conditions ae met. -### Shipping a release (maintainers only) +## Shipping a release (maintainers only) Maintainers need to do the following to push out a release: -* Switch to the master branch and make sure it's up to date. -* Make sure you have [chandler] properly configured. Chandler is used to - automatically submit github release notes from the changelog right after - pushing the gem to rubygems. -* Run one of `bin/rake release:prepare_{prerelease,prepatch,patch,preminor,minor,premajor,major}`, push the result and create a PR. -* Review and merge the PR. The generated changelog in the PR should include all user visible changes you intend to ship. -* Run `bin/rake release` from the target branch once the PR is merged. +* Create a feature branch from master and make sure it's up to date. +* Run `bin/prep-release [version]` and commit the changes. Use Ruby version format. NPM is handled automatically. +* Optional: To confirm the release contents, run `gem build` (extract contents) and `npm publish --dry-run`. +* Review and merge the PR. +* Run `bin/rake release` from the default branch once the PR is merged. +* [Create a GitHub Release](https://github.com/activeadmin/activeadmin/releases/new) by selecting the tag and generating the release notes. -[chandler]: https://github.com/mattbrictson/chandler#2-configure-credentials -[Stack Overflow]: http://stackoverflow.com/questions/tagged/activeadmin [new issue]: https://github.com/activeadmin/activeadmin/issues/new -[fork Active Admin]: https://help.github.com/articles/fork-a-repo -[make a pull request]: https://help.github.com/articles/creating-a-pull-request -[git rebasing]: http://git-scm.com/book/en/Git-Branching-Rebasing -[interactive rebase]: https://help.github.com/en/github/using-git/about-git-rebase -[shortcut reference links]: https://github.github.com/gfm/#shortcut-reference-link -[Rollup]: https://rollupjs.org/guide/en/#quick-start -[Yarn]: https://yarnpkg.com/en/docs/install -[Node.js]: https://nodejs.org/en/ diff --git a/Gemfile b/Gemfile index ea5aabc19c3..2ee6627530e 100644 --- a/Gemfile +++ b/Gemfile @@ -3,8 +3,6 @@ source "https://rubygems.org" group :development, :test do gem "rake" - gem "pry" # Easily debug from your console with `binding.pry` - gem "pry-byebug", platform: :mri # Step-by-step debugging gem "cancancan" gem "pundit" @@ -12,12 +10,14 @@ group :development, :test do gem "draper" gem "devise" - gem "rails", "~> 7.0.0" - - gem "net-smtp" # The mail gem, depended on transitively, does not specify this dependency + gem "rails", "~> 8.0.0" gem "sprockets-rails" - gem "sassc-rails" + gem "ransack", ">= 4.2.0" + gem "formtastic", ">= 5.0.0" + + gem "cssbundling-rails" + gem "importmap-rails" end group :test do @@ -26,39 +26,28 @@ group :test do gem "webrick" gem "simplecov", require: false # Test coverage generator. Go to /coverage/ after running tests + gem "simplecov-cobertura", require: false gem "cucumber-rails", require: false gem "cucumber" - gem "database_cleaner" - gem "jasmine" - gem "jasmine-core" + gem "database_cleaner-active_record" gem "launchy" gem "parallel_tests" - gem "rails-i18n" # Provides default i18n for many languages gem "rspec-rails" gem "sqlite3", platform: :mri -end -group :release do - gem "chandler" # Github releases from changelog - gem "octokit" + # Translations + gem "i18n-tasks" + gem "i18n-spec" + gem "rails-i18n" # Provides default i18n for many languages end -group :lint do - # Code style +group :rubocop do gem "rubocop" + gem "rubocop-capybara" gem "rubocop-packaging" + gem "rubocop-performance" gem "rubocop-rspec" gem "rubocop-rails" - gem "mdl" - - # Translations - gem "i18n-tasks" - gem "i18n-spec" -end - -group :docs do - gem "yard" # Documentation generator - gem "kramdown" # Markdown implementation (for yard) end gemspec path: "." diff --git a/Gemfile.lock b/Gemfile.lock index df62b405281..13228ef2108 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,250 +1,229 @@ PATH remote: . specs: - activeadmin (2.11.0) - arbre (~> 1.2, >= 1.2.1) - formtastic (>= 3.1, < 5.0) - formtastic_i18n (~> 0.4) - inherited_resources (~> 1.7) - jquery-rails (~> 4.2) - kaminari (~> 1.0, >= 1.2.1) - railties (>= 6.0, < 7.1) - ransack (~> 2.1, >= 2.1.1) + activeadmin (4.0.0.beta15) + arbre (~> 2.0) + csv + formtastic (>= 5.0) + formtastic_i18n (>= 0.7) + inherited_resources (~> 2.0) + kaminari (>= 1.2.1) + railties (>= 7.0) + ransack (>= 4.0) GEM remote: https://rubygems.org/ specs: - actioncable (7.0.2.2) - actionpack (= 7.0.2.2) - activesupport (= 7.0.2.2) + actioncable (8.0.2) + actionpack (= 8.0.2) + activesupport (= 8.0.2) nio4r (~> 2.0) websocket-driver (>= 0.6.1) - actionmailbox (7.0.2.2) - actionpack (= 7.0.2.2) - activejob (= 7.0.2.2) - activerecord (= 7.0.2.2) - activestorage (= 7.0.2.2) - activesupport (= 7.0.2.2) - mail (>= 2.7.1) - net-imap - net-pop - net-smtp - actionmailer (7.0.2.2) - actionpack (= 7.0.2.2) - actionview (= 7.0.2.2) - activejob (= 7.0.2.2) - activesupport (= 7.0.2.2) - mail (~> 2.5, >= 2.5.4) - net-imap - net-pop - net-smtp - rails-dom-testing (~> 2.0) - actionpack (7.0.2.2) - actionview (= 7.0.2.2) - activesupport (= 7.0.2.2) - rack (~> 2.0, >= 2.2.0) + zeitwerk (~> 2.6) + actionmailbox (8.0.2) + actionpack (= 8.0.2) + activejob (= 8.0.2) + activerecord (= 8.0.2) + activestorage (= 8.0.2) + activesupport (= 8.0.2) + mail (>= 2.8.0) + actionmailer (8.0.2) + actionpack (= 8.0.2) + actionview (= 8.0.2) + activejob (= 8.0.2) + activesupport (= 8.0.2) + mail (>= 2.8.0) + rails-dom-testing (~> 2.2) + actionpack (8.0.2) + actionview (= 8.0.2) + activesupport (= 8.0.2) + nokogiri (>= 1.8.5) + rack (>= 2.2.4) + rack-session (>= 1.0.1) rack-test (>= 0.6.3) - rails-dom-testing (~> 2.0) - rails-html-sanitizer (~> 1.0, >= 1.2.0) - actiontext (7.0.2.2) - actionpack (= 7.0.2.2) - activerecord (= 7.0.2.2) - activestorage (= 7.0.2.2) - activesupport (= 7.0.2.2) + rails-dom-testing (~> 2.2) + rails-html-sanitizer (~> 1.6) + useragent (~> 0.16) + actiontext (8.0.2) + actionpack (= 8.0.2) + activerecord (= 8.0.2) + activestorage (= 8.0.2) + activesupport (= 8.0.2) globalid (>= 0.6.0) nokogiri (>= 1.8.5) - actionview (7.0.2.2) - activesupport (= 7.0.2.2) + actionview (8.0.2) + activesupport (= 8.0.2) builder (~> 3.1) - erubi (~> 1.4) - rails-dom-testing (~> 2.0) - rails-html-sanitizer (~> 1.1, >= 1.2.0) - activejob (7.0.2.2) - activesupport (= 7.0.2.2) + erubi (~> 1.11) + rails-dom-testing (~> 2.2) + rails-html-sanitizer (~> 1.6) + activejob (8.0.2) + activesupport (= 8.0.2) globalid (>= 0.3.6) - activemodel (7.0.2.2) - activesupport (= 7.0.2.2) - activemodel-serializers-xml (1.0.2) - activemodel (> 5.x) - activesupport (> 5.x) + activemodel (8.0.2) + activesupport (= 8.0.2) + activemodel-serializers-xml (1.0.3) + activemodel (>= 5.0.0.a) + activesupport (>= 5.0.0.a) builder (~> 3.1) - activerecord (7.0.2.2) - activemodel (= 7.0.2.2) - activesupport (= 7.0.2.2) - activestorage (7.0.2.2) - actionpack (= 7.0.2.2) - activejob (= 7.0.2.2) - activerecord (= 7.0.2.2) - activesupport (= 7.0.2.2) + activerecord (8.0.2) + activemodel (= 8.0.2) + activesupport (= 8.0.2) + timeout (>= 0.4.0) + activestorage (8.0.2) + actionpack (= 8.0.2) + activejob (= 8.0.2) + activerecord (= 8.0.2) + activesupport (= 8.0.2) marcel (~> 1.0) - mini_mime (>= 1.1.0) - activesupport (7.0.2.2) - concurrent-ruby (~> 1.0, >= 1.0.2) + activesupport (8.0.2) + base64 + benchmark (>= 0.3) + bigdecimal + concurrent-ruby (~> 1.0, >= 1.3.1) + connection_pool (>= 2.2.5) + drb i18n (>= 1.6, < 2) + logger (>= 1.4.2) minitest (>= 5.1) - tzinfo (~> 2.0) - addressable (2.8.0) - public_suffix (>= 2.0.2, < 5.0) - arbre (1.5.0) - activesupport (>= 3.0.0, < 7.1) - ruby2_keywords (>= 0.0.2, < 1.0) - ast (2.4.2) - bcrypt (3.1.16) - builder (3.2.4) - byebug (11.1.1) - cancancan (3.3.0) - capybara (3.36.0) + securerandom (>= 0.3) + tzinfo (~> 2.0, >= 2.0.5) + uri (>= 0.13.1) + addressable (2.8.7) + public_suffix (>= 2.0.2, < 7.0) + arbre (2.2.0) + activesupport (>= 7.0) + ast (2.4.3) + base64 (0.3.0) + bcrypt (3.1.20) + benchmark (0.4.1) + bigdecimal (3.2.2) + builder (3.3.0) + cancancan (3.6.1) + capybara (3.40.0) addressable matrix mini_mime (>= 0.1.3) - nokogiri (~> 1.8) + nokogiri (~> 1.11) rack (>= 1.6.0) rack-test (>= 0.6.3) regexp_parser (>= 1.5, < 3.0) xpath (~> 3.2) - chandler (0.9.0) - netrc - octokit (>= 2.2.0) - chef-utils (16.4.41) - cliver (0.3.2) - coderay (1.1.2) - concurrent-ruby (1.1.9) + childprocess (5.1.0) + logger (~> 1.5) + concurrent-ruby (1.3.5) + connection_pool (2.5.3) crass (1.0.6) - cucumber (7.1.0) - builder (~> 3.2, >= 3.2.4) - cucumber-core (~> 10.1, >= 10.1.0) - cucumber-create-meta (~> 6.0, >= 6.0.1) - cucumber-cucumber-expressions (~> 14.0, >= 14.0.0) - cucumber-gherkin (~> 22.0, >= 22.0.0) - cucumber-html-formatter (~> 17.0, >= 17.0.0) - cucumber-messages (~> 17.1, >= 17.1.1) - cucumber-wire (~> 6.2, >= 6.2.0) - diff-lcs (~> 1.4, >= 1.4.4) - mime-types (~> 3.3, >= 3.3.1) - multi_test (~> 0.1, >= 0.1.2) - sys-uname (~> 1.2, >= 1.2.2) - cucumber-core (10.1.0) - cucumber-gherkin (~> 22.0, >= 22.0.0) - cucumber-messages (~> 17.1, >= 17.1.1) - cucumber-tag-expressions (~> 4.0, >= 4.0.2) - cucumber-create-meta (6.0.1) - cucumber-messages (~> 17.0, >= 17.0.1) - sys-uname (~> 1.2, >= 1.2.2) - cucumber-cucumber-expressions (14.0.0) - cucumber-gherkin (22.0.0) - cucumber-messages (~> 17.1, >= 17.1.1) - cucumber-html-formatter (17.0.0) - cucumber-messages (~> 17.1, >= 17.1.0) - cucumber-messages (17.1.1) - cucumber-rails (2.5.0) - capybara (>= 2.18, < 4) - cucumber (>= 3.2, < 8) - mime-types (~> 3.3) - nokogiri (~> 1.10) - railties (>= 5.0, < 8) - rexml (~> 3.0) - webrick (~> 1.7) - cucumber-tag-expressions (4.1.0) - cucumber-wire (6.2.0) - cucumber-core (~> 10.1, >= 10.1.0) - cucumber-cucumber-expressions (~> 14.0, >= 14.0.0) - cucumber-messages (~> 17.1, >= 17.1.1) - cuprite (0.13) - capybara (>= 2.1, < 4) - ferrum (~> 0.11.0) - database_cleaner (2.0.1) - database_cleaner-active_record (~> 2.0.0) - database_cleaner-active_record (2.0.0) + cssbundling-rails (1.4.3) + railties (>= 6.0.0) + csv (3.3.5) + cucumber (9.2.1) + builder (~> 3.2) + cucumber-ci-environment (> 9, < 11) + cucumber-core (> 13, < 14) + cucumber-cucumber-expressions (~> 17.0) + cucumber-gherkin (> 24, < 28) + cucumber-html-formatter (> 20.3, < 22) + cucumber-messages (> 19, < 25) + diff-lcs (~> 1.5) + mini_mime (~> 1.1) + multi_test (~> 1.1) + sys-uname (~> 1.2) + cucumber-ci-environment (10.0.1) + cucumber-core (13.0.3) + cucumber-gherkin (>= 27, < 28) + cucumber-messages (>= 20, < 23) + cucumber-tag-expressions (> 5, < 7) + cucumber-cucumber-expressions (17.1.0) + bigdecimal + cucumber-gherkin (27.0.0) + cucumber-messages (>= 19.1.4, < 23) + cucumber-html-formatter (21.9.0) + cucumber-messages (> 19, < 28) + cucumber-messages (22.0.0) + cucumber-rails (3.1.1) + capybara (>= 3.11, < 4) + cucumber (>= 5, < 10) + railties (>= 5.2, < 9) + cucumber-tag-expressions (6.1.2) + cuprite (0.17) + capybara (~> 3.0) + ferrum (~> 0.17.0) + database_cleaner-active_record (2.2.1) activerecord (>= 5.a) database_cleaner-core (~> 2.0.0) database_cleaner-core (2.0.1) - devise (4.8.1) + date (3.4.1) + devise (4.9.4) bcrypt (~> 3.0) orm_adapter (~> 0.1) railties (>= 4.1.0) responders warden (~> 1.2.3) - diff-lcs (1.5.0) - digest (3.1.0) - docile (1.3.4) - draper (4.0.2) + diff-lcs (1.6.2) + docile (1.4.1) + draper (4.0.4) actionpack (>= 5.0) activemodel (>= 5.0) activemodel-serializers-xml (>= 1.0) activesupport (>= 5.0) request_store (>= 1.0) ruby2_keywords - erubi (1.10.0) - faraday (1.9.3) - faraday-em_http (~> 1.0) - faraday-em_synchrony (~> 1.0) - faraday-excon (~> 1.1) - faraday-httpclient (~> 1.0) - faraday-multipart (~> 1.0) - faraday-net_http (~> 1.0) - faraday-net_http_persistent (~> 1.0) - faraday-patron (~> 1.0) - faraday-rack (~> 1.0) - faraday-retry (~> 1.0) - ruby2_keywords (>= 0.0.4) - faraday-em_http (1.0.0) - faraday-em_synchrony (1.0.0) - faraday-excon (1.1.0) - faraday-httpclient (1.0.1) - faraday-multipart (1.0.3) - multipart-post (>= 1.2, < 3) - faraday-net_http (1.0.1) - faraday-net_http_persistent (1.2.0) - faraday-patron (1.0.0) - faraday-rack (1.0.0) - faraday-retry (1.0.3) - ferrum (0.11) + drb (2.2.3) + erb (5.0.1) + erubi (1.13.1) + ferrum (0.17.1) addressable (~> 2.5) - cliver (~> 0.3) + base64 (~> 0.2) concurrent-ruby (~> 1.1) - websocket-driver (>= 0.6, < 0.8) - ffi (1.15.4) - formtastic (4.0.0) - actionpack (>= 5.2.0) + webrick (~> 1.7) + websocket-driver (~> 0.7) + ffi (1.17.2-aarch64-linux-gnu) + ffi (1.17.2-arm64-darwin) + ffi (1.17.2-x86_64-darwin) + ffi (1.17.2-x86_64-linux-gnu) + formtastic (5.0.0) + actionpack (>= 6.0.0) formtastic_i18n (0.7.0) - globalid (1.0.0) - activesupport (>= 5.0) - has_scope (0.8.0) + globalid (1.2.1) + activesupport (>= 6.1) + has_scope (0.8.2) actionpack (>= 5.2) activesupport (>= 5.2) - highline (2.0.3) - i18n (1.10.0) + highline (3.1.2) + reline + i18n (1.14.7) concurrent-ruby (~> 1.0) i18n-spec (0.6.0) iso - i18n-tasks (0.9.37) + i18n-tasks (1.0.15) activesupport (>= 4.0.2) ast (>= 2.1.0) erubi highline (>= 2.0.0) i18n - parser (>= 2.2.3.0) + parser (>= 3.2.2.1) rails-i18n rainbow (>= 2.2.2, < 4.0) + ruby-progressbar (~> 1.8, >= 1.8.1) terminal-table (>= 1.5.1) - inherited_resources (1.13.1) - actionpack (>= 5.2, < 7.1) - has_scope (~> 0.6) - railties (>= 5.2, < 7.1) - responders (>= 2, < 4) - io-wait (0.2.1) - iso (0.2.2) + importmap-rails (2.1.0) + actionpack (>= 6.0.0) + activesupport (>= 6.0.0) + railties (>= 6.0.0) + inherited_resources (2.1.0) + actionpack (>= 7.0) + has_scope (>= 0.6) + railties (>= 7.0) + responders (>= 2) + io-console (0.8.0) + irb (1.15.2) + pp (>= 0.6.0) + rdoc (>= 4.0.0) + reline (>= 0.4.2) + iso (0.4.0) i18n - jasmine (2.99.0) - jasmine-core (>= 2.99.0, < 3.0.0) - phantomjs - rack (>= 1.2.1) - rake - jasmine-core (2.99.2) - jquery-rails (4.4.0) - rails-dom-testing (>= 1, < 3) - railties (>= 4.2.0) - thor (>= 0.14, < 2.0) + json (2.12.2) kaminari (1.2.2) activesupport (>= 4.1.0) kaminari-actionview (= 1.2.2) @@ -257,256 +236,259 @@ GEM activerecord kaminari-core (= 1.2.2) kaminari-core (1.2.2) - kramdown (2.3.1) - rexml - kramdown-parser-gfm (1.1.0) - kramdown (~> 2.0) - launchy (2.5.0) - addressable (~> 2.7) - loofah (2.14.0) + language_server-protocol (3.17.0.5) + launchy (3.1.1) + addressable (~> 2.8) + childprocess (~> 5.0) + logger (~> 1.6) + lint_roller (1.1.0) + logger (1.7.0) + loofah (2.24.1) crass (~> 1.0.2) - nokogiri (>= 1.5.9) - mail (2.7.1) + nokogiri (>= 1.12.0) + mail (2.8.1) mini_mime (>= 0.1.1) - marcel (1.0.2) + net-imap + net-pop + net-smtp + marcel (1.0.4) matrix (0.4.2) - mdl (0.11.0) - kramdown (~> 2.3) - kramdown-parser-gfm (~> 1.1) - mixlib-cli (~> 2.1, >= 2.1.1) - mixlib-config (>= 2.2.1, < 4) - mixlib-shellout - method_source (1.0.0) - mime-types (3.3.1) - mime-types-data (~> 3.2015) - mime-types-data (3.2021.0901) - mini_mime (1.1.2) - mini_portile2 (2.8.0) - minitest (5.15.0) - mixlib-cli (2.1.8) - mixlib-config (3.0.9) - tomlrb - mixlib-shellout (3.1.4) - chef-utils - multi_test (0.1.2) - multipart-post (2.1.1) - net-imap (0.2.3) - digest + mini_mime (1.1.5) + minitest (5.25.5) + multi_test (1.1.0) + net-imap (0.5.8) + date net-protocol - strscan - net-pop (0.1.1) - digest + net-pop (0.1.2) net-protocol + net-protocol (0.2.2) timeout - net-protocol (0.1.2) - io-wait - timeout - net-smtp (0.3.1) - digest + net-smtp (0.5.1) net-protocol - timeout - netrc (0.11.0) - nio4r (2.5.8) - nokogiri (1.13.3) - mini_portile2 (~> 2.8.0) + nio4r (2.7.4) + nokogiri (1.18.8-aarch64-linux-gnu) + racc (~> 1.4) + nokogiri (1.18.8-arm64-darwin) + racc (~> 1.4) + nokogiri (1.18.8-x86_64-darwin) racc (~> 1.4) - nokogiri (1.13.3-x86_64-linux) + nokogiri (1.18.8-x86_64-linux-gnu) racc (~> 1.4) - octokit (4.22.0) - faraday (>= 0.9) - sawyer (~> 0.8.0, >= 0.5.3) orm_adapter (0.5.0) - parallel (1.21.0) - parallel_tests (3.7.3) + parallel (1.27.0) + parallel_tests (5.3.0) parallel - parser (3.1.0.0) + parser (3.3.8.0) ast (~> 2.4.1) - phantomjs (2.1.1.0) - pry (0.13.1) - coderay (~> 1.1) - method_source (~> 1.0) - pry-byebug (3.9.0) - byebug (~> 11.0) - pry (~> 0.13.0) - public_suffix (4.0.6) - pundit (2.2.0) + racc + pp (0.6.2) + prettyprint + prettyprint (0.2.0) + prism (1.4.0) + psych (5.2.6) + date + stringio + public_suffix (6.0.2) + pundit (2.5.0) activesupport (>= 3.0.0) - racc (1.6.0) - rack (2.2.3) - rack-test (1.1.0) - rack (>= 1.0, < 3) - rails (7.0.2.2) - actioncable (= 7.0.2.2) - actionmailbox (= 7.0.2.2) - actionmailer (= 7.0.2.2) - actionpack (= 7.0.2.2) - actiontext (= 7.0.2.2) - actionview (= 7.0.2.2) - activejob (= 7.0.2.2) - activemodel (= 7.0.2.2) - activerecord (= 7.0.2.2) - activestorage (= 7.0.2.2) - activesupport (= 7.0.2.2) + racc (1.8.1) + rack (3.1.16) + rack-session (2.1.1) + base64 (>= 0.1.0) + rack (>= 3.0.0) + rack-test (2.2.0) + rack (>= 1.3) + rackup (2.2.1) + rack (>= 3) + rails (8.0.2) + actioncable (= 8.0.2) + actionmailbox (= 8.0.2) + actionmailer (= 8.0.2) + actionpack (= 8.0.2) + actiontext (= 8.0.2) + actionview (= 8.0.2) + activejob (= 8.0.2) + activemodel (= 8.0.2) + activerecord (= 8.0.2) + activestorage (= 8.0.2) + activesupport (= 8.0.2) bundler (>= 1.15.0) - railties (= 7.0.2.2) - rails-dom-testing (2.0.3) - activesupport (>= 4.2.0) + railties (= 8.0.2) + rails-dom-testing (2.3.0) + activesupport (>= 5.0.0) + minitest nokogiri (>= 1.6) - rails-html-sanitizer (1.4.2) - loofah (~> 2.3) - rails-i18n (7.0.3) + rails-html-sanitizer (1.6.2) + loofah (~> 2.21) + nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0) + rails-i18n (8.0.1) i18n (>= 0.7, < 2) - railties (>= 6.0.0, < 8) - railties (7.0.2.2) - actionpack (= 7.0.2.2) - activesupport (= 7.0.2.2) - method_source + railties (>= 8.0.0, < 9) + railties (8.0.2) + actionpack (= 8.0.2) + activesupport (= 8.0.2) + irb (~> 1.13) + rackup (>= 1.0.0) rake (>= 12.2) - thor (~> 1.0) - zeitwerk (~> 2.5) + thor (~> 1.0, >= 1.2.2) + zeitwerk (~> 2.6) rainbow (3.1.1) - rake (13.0.6) - ransack (2.5.0) - activerecord (>= 5.2.4) - activesupport (>= 5.2.4) + rake (13.3.0) + ransack (4.3.0) + activerecord (>= 6.1.5) + activesupport (>= 6.1.5) i18n - regexp_parser (2.2.0) - request_store (1.5.0) + rdoc (6.14.0) + erb + psych (>= 4.0.0) + regexp_parser (2.10.0) + reline (0.6.1) + io-console (~> 0.5) + request_store (1.7.0) rack (>= 1.4) - responders (3.0.1) - actionpack (>= 5.0) - railties (>= 5.0) - rexml (3.2.5) - rspec-core (3.11.0) - rspec-support (~> 3.11.0) - rspec-expectations (3.11.0) - diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.11.0) - rspec-mocks (3.11.0) - diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.11.0) - rspec-rails (5.1.1) + responders (3.1.1) actionpack (>= 5.2) - activesupport (>= 5.2) railties (>= 5.2) - rspec-core (~> 3.10) - rspec-expectations (~> 3.10) - rspec-mocks (~> 3.10) - rspec-support (~> 3.10) - rspec-support (3.11.0) - rubocop (0.93.1) + rexml (3.4.1) + rspec-core (3.13.4) + rspec-support (~> 3.13.0) + rspec-expectations (3.13.5) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-mocks (3.13.5) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-rails (8.0.0) + actionpack (>= 7.2) + activesupport (>= 7.2) + railties (>= 7.2) + rspec-core (~> 3.13) + rspec-expectations (~> 3.13) + rspec-mocks (~> 3.13) + rspec-support (~> 3.13) + rspec-support (3.13.4) + rubocop (1.76.0) + json (~> 2.3) + language_server-protocol (~> 3.17.0.2) + lint_roller (~> 1.1.0) parallel (~> 1.10) - parser (>= 2.7.1.5) + parser (>= 3.3.0.2) rainbow (>= 2.2.2, < 4.0) - regexp_parser (>= 1.8) - rexml - rubocop-ast (>= 0.6.0) + regexp_parser (>= 2.9.3, < 3.0) + rubocop-ast (>= 1.45.0, < 2.0) ruby-progressbar (~> 1.7) - unicode-display_width (>= 1.4.0, < 2.0) - rubocop-ast (1.3.0) - parser (>= 2.7.1.5) - rubocop-packaging (0.5.1) - rubocop (>= 0.89, < 2.0) - rubocop-rails (2.9.1) + unicode-display_width (>= 2.4.0, < 4.0) + rubocop-ast (1.45.0) + parser (>= 3.3.7.2) + prism (~> 1.4) + rubocop-capybara (2.22.1) + lint_roller (~> 1.1) + rubocop (~> 1.72, >= 1.72.1) + rubocop-packaging (0.6.0) + lint_roller (~> 1.1.0) + rubocop (>= 1.72.1, < 2.0) + rubocop-performance (1.25.0) + lint_roller (~> 1.1) + rubocop (>= 1.75.0, < 2.0) + rubocop-ast (>= 1.38.0, < 2.0) + rubocop-rails (2.32.0) activesupport (>= 4.2.0) + lint_roller (~> 1.1) rack (>= 1.1) - rubocop (>= 0.90.0, < 2.0) - rubocop-rspec (1.44.1) - rubocop (~> 0.87) - rubocop-ast (>= 0.7.1) - ruby-progressbar (1.10.1) + rubocop (>= 1.75.0, < 2.0) + rubocop-ast (>= 1.44.0, < 2.0) + rubocop-rspec (3.6.0) + lint_roller (~> 1.1) + rubocop (~> 1.72, >= 1.72.1) + ruby-progressbar (1.13.0) ruby2_keywords (0.0.5) - sassc (2.4.0) - ffi (~> 1.9) - sassc-rails (2.1.2) - railties (>= 4.0.0) - sassc (>= 2.0) - sprockets (> 3.0) - sprockets-rails - tilt - sawyer (0.8.2) - addressable (>= 2.3.5) - faraday (> 0.8, < 2.0) - simplecov (0.21.2) + securerandom (0.4.1) + simplecov (0.22.0) docile (~> 1.1) simplecov-html (~> 0.11) simplecov_json_formatter (~> 0.1) - simplecov-html (0.12.3) - simplecov_json_formatter (0.1.2) - sprockets (4.0.2) + simplecov-cobertura (2.1.0) + rexml + simplecov (~> 0.19) + simplecov-html (0.13.1) + simplecov_json_formatter (0.1.4) + sprockets (4.2.2) concurrent-ruby (~> 1.0) - rack (> 1, < 3) - sprockets-rails (3.4.2) - actionpack (>= 5.2) - activesupport (>= 5.2) + logger + rack (>= 2.2.4, < 4) + sprockets-rails (3.5.2) + actionpack (>= 6.1) + activesupport (>= 6.1) sprockets (>= 3.0.0) - sqlite3 (1.4.2) - strscan (3.0.1) - sys-uname (1.2.2) + sqlite3 (2.6.0-aarch64-linux-gnu) + sqlite3 (2.6.0-arm64-darwin) + sqlite3 (2.6.0-x86_64-darwin) + sqlite3 (2.6.0-x86_64-linux-gnu) + stringio (3.1.7) + sys-uname (1.3.1) ffi (~> 1.1) - terminal-table (3.0.2) - unicode-display_width (>= 1.1.1, < 3) - thor (1.2.1) - tilt (2.0.10) - timeout (0.2.0) - tomlrb (1.3.0) - tzinfo (2.0.4) + terminal-table (4.0.0) + unicode-display_width (>= 1.1.1, < 4) + thor (1.3.2) + timeout (0.4.3) + tzinfo (2.0.6) concurrent-ruby (~> 1.0) - unicode-display_width (1.8.0) + unicode-display_width (3.1.4) + unicode-emoji (~> 4.0, >= 4.0.4) + unicode-emoji (4.0.4) + uri (1.0.3) + useragent (0.16.11) warden (1.2.9) rack (>= 2.0.9) - webrick (1.7.0) - websocket-driver (0.7.5) + webrick (1.9.1) + websocket-driver (0.8.0) + base64 websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) xpath (3.2.0) nokogiri (~> 1.8) - yard (0.9.27) - webrick (~> 1.7.0) - zeitwerk (2.5.4) + zeitwerk (2.7.3) PLATFORMS - ruby + aarch64-linux + arm64-darwin + x86_64-darwin x86_64-linux DEPENDENCIES activeadmin! cancancan capybara - chandler + cssbundling-rails cucumber cucumber-rails cuprite - database_cleaner + database_cleaner-active_record devise draper + formtastic (>= 5.0.0) i18n-spec i18n-tasks - jasmine - jasmine-core - kramdown + importmap-rails launchy - mdl - net-smtp - octokit parallel_tests - pry - pry-byebug pundit - rails (~> 7.0.0) + rails (~> 8.0.0) rails-i18n rake + ransack (>= 4.2.0) rspec-rails rubocop + rubocop-capybara rubocop-packaging + rubocop-performance rubocop-rails rubocop-rspec - sassc-rails simplecov + simplecov-cobertura sprockets-rails sqlite3 webrick - yard BUNDLED WITH - 2.3.6 + 2.6.9 diff --git a/README.md b/README.md index 1b1737c73c4..4a77def3afe 100644 --- a/README.md +++ b/README.md @@ -3,11 +3,10 @@ [Active Admin](https://activeadmin.info) is a Ruby on Rails framework for creating elegant backends for website administration. -[![Version ][rubygems_badge]][rubygems] -[![Github Actions ][actions_badge]][actions] -[![Coverage ][coverage_badge]][coverage] -[![Tidelift ][tidelift_badge]][tidelift] -[![Inch CI ][inch_badge]][inch] +[![Version][rubygems_badge]][rubygems] +[![Github Actions][actions_badge]][actions] +[![Coverage][coverage_badge]][coverage] +[![Tidelift][tidelift_badge]][tidelift] ## Goals @@ -17,8 +16,8 @@ creating elegant backends for website administration. ## Getting started -* Check out [the docs][docs]. -* Try the [live demo][demo]. +* Review [the documentation][docs]. +* For help, questions, etc. [discuss ActiveAdmin on GitHub](https://github.com/activeadmin/activeadmin/discussions) or use [StackOverflow][stackoverflow] * The [wiki] includes links to tutorials, articles and sample projects. ## For enterprise @@ -26,15 +25,6 @@ creating elegant backends for website administration. Active Admin for enterprise is available via the Tidelift subscription. [Learn More][tidelift_enterprise]. -## Need help? - -Please use [StackOverflow][stackoverflow] for help requests and how-to questions. - -Please open GitHub issues for bugs and enhancements only, not general help requests. -Please search previous issues (and Google and StackOverflow) before creating a new issue. - -Google Groups, IRC #activeadmin and Gitter are not actively monitored. - ## Want to contribute? If you want to contribute through code or documentation, the [Contributing @@ -58,14 +48,14 @@ sponsor for the project, and also submit expenses to it. We try not to reinvent the wheel, so Active Admin is built with other open source projects: -Tool | Description ---------------------- | ----------- -[Arbre] | Ruby -> HTML, just like that. -[Devise] | Powerful, extensible user authentication -[Formtastic] | A Rails form builder plugin with semantically rich and accessible markup -[Inherited Resources] | Simplifies controllers with pre-built RESTful controller actions -[Kaminari] | Elegant pagination for any sort of collection -[Ransack] | Provides a simple search API to query your data +* [Arbre] +* [Devise] +* [Flowbite](https://flowbite.com) +* [Formtastic] +* [Inherited Resources] +* [Kaminari] +* [Ransack] +* [TailwindCSS](https://tailwindcss.com) ## Security contact information @@ -85,29 +75,26 @@ Thanks to [Tidelift][tidelift] and all our Tidelift subscribers. Thanks to [Open Collective][opencollective contributors] and all our Open Collective contributors. [Arbre]: https://github.com/activeadmin/arbre -[Devise]: https://github.com/plataformatec/devise -[Formtastic]: https://github.com/justinfrench/formtastic +[Devise]: https://github.com/heartcombo/devise +[Formtastic]: https://github.com/formtastic/formtastic [Inherited Resources]: https://github.com/activeadmin/inherited_resources [Kaminari]: https://github.com/kaminari/kaminari [Ransack]: https://github.com/activerecord-hackery/ransack -[rubygems_badge]: http://img.shields.io/gem/v/activeadmin.svg +[rubygems_badge]: https://img.shields.io/gem/v/activeadmin.svg [rubygems]: https://rubygems.org/gems/activeadmin [actions_badge]: https://github.com/activeadmin/activeadmin/workflows/ci/badge.svg [actions]: https://github.com/activeadmin/activeadmin/actions -[coverage_badge]: https://api.codeclimate.com/v1/badges/779e407d22bacff19733/test_coverage -[coverage]: https://codeclimate.com/github/activeadmin/activeadmin/test_coverage -[inch_badge]: http://inch-ci.org/github/activeadmin/activeadmin.svg?branch=master -[inch]: http://inch-ci.org/github/activeadmin/activeadmin +[coverage_badge]: https://codecov.io/gh/activeadmin/activeadmin/branch/master/graph/badge.svg?token=NAjeBdkQXW +[coverage]: https://codecov.io/gh/activeadmin/activeadmin [tidelift_badge]: https://tidelift.com/badges/github/activeadmin/activeadmin [tidelift]: https://tidelift.com/subscription/pkg/rubygems-activeadmin?utm_source=rubygems-activeadmin&utm_medium=readme [tidelift_enterprise]: https://tidelift.com/subscription/pkg/rubygems-activeadmin?utm_source=rubygems-activeadmin&utm_medium=referral&utm_campaign=enterprise [tidelift_support]: https://tidelift.com/subscription/pkg/rubygems-activeadmin?utm_source=rubygems-activeadmin&utm_medium=referral&utm_campaign=github&utm_content=support -[docs]: http://activeadmin.info/0-installation.html -[demo]: http://demo.activeadmin.info/admin +[docs]: https://activeadmin.info/ [wiki]: https://github.com/activeadmin/activeadmin/wiki -[stackoverflow]: http://stackoverflow.com/questions/tagged/activeadmin +[stackoverflow]: https://stackoverflow.com/questions/tagged/activeadmin [contributing]: https://github.com/activeadmin/activeadmin/blob/master/CONTRIBUTING.md [Liberapay]: https://liberapay.com/Active-Admin/donate [Tidelift security contact]: https://tidelift.com/security diff --git a/Rakefile b/Rakefile index 2b87b92920b..e26229820b9 100644 --- a/Rakefile +++ b/Rakefile @@ -1,28 +1,21 @@ # frozen_string_literal: true require "bundler/gem_tasks" -import "tasks/gemfiles.rake" import "tasks/local.rake" import "tasks/test.rake" +import "tasks/dependencies.rake" gemfile = ENV["BUNDLE_GEMFILE"] if gemfile.nil? || File.expand_path(gemfile) == File.expand_path("Gemfile") - import "tasks/changelog.rake" - import "tasks/docs.rake" - import "tasks/lint.rake" import "tasks/release.rake" end task default: :test -require "jasmine" -load "jasmine/tasks/jasmine.rake" - task :console do require "irb" require "irb/completion" - ARGV.clear IRB.start end diff --git a/UPGRADING.md b/UPGRADING.md new file mode 100644 index 00000000000..ca428f87e58 --- /dev/null +++ b/UPGRADING.md @@ -0,0 +1,228 @@ +# Upgrading Guide + +## From v3 to v4 (beta) + +ActiveAdmin v4 uses TailwindCSS. It has **mobile web, dark mode and RTL support** with a default theme that can be customized through partials and CSS. This release assumes `cssbundling-rails` and `importmap-rails` is installed and configured in the host app. Partials can be modified to include a different asset library, e.g. shakapacker. + +**IMPORTANT**: there is **no sortable functionality for has-many forms** in this release so if needed, **do not upgrade**. We are [open to community proposals](https://github.com/activeadmin/activeadmin/discussions/new?category=ideas). The add/remove functionality for has-many forms remains supported. + +These instructions assume the `cssbundling-rails` and `importmap-rails` gems are already installed and you have run their install commands in your app. If you haven't done so, please do before continuing. + +Update your `Gemfile` with `gem "activeadmin", "4.0.0.beta15"` and then run `gem install activeadmin --pre`. + +Now, run `rails generate active_admin:assets` to replace the old assets with the new files. + +Then add the npm package and update the `build:css` script. + +``` +yarn add @activeadmin/activeadmin@4.0.0-beta15 +npm pkg set scripts.build:css="tailwindcss -i ./app/assets/stylesheets/active_admin.css -o ./app/assets/builds/active_admin.css --minify -c tailwind-active_admin.config.js" +``` + +If you are already using Tailwind in your app, then update the `build:css` script to chain the above command to your existing one, e.g. `"tailwindcss ... && tailwindcss ..."`, so both stylesheets are generated. + +Many configs have been removed (meta tags, asset registration, utility nav, etc.) that can now be modified more naturally through partials. + +Open the `config/initializers/active_admin.rb` file and remove these deleted configs. + +``` +site_title_link +site_title_image +logout_link_method +favicon +meta_tags +meta_tags_for_logged_out_pages +register_stylesheet +register_javascript +head +footer +use_webpacker +``` + +Now, run `rails g active_admin:views` which will copy the partials to your app so you can customize if needed. + +Note that the templates can and will change across releases. There are additional partials that can be copied but they are considered private so you do so at your own risk. You will have to keep those up to date per release. + +**IMPORTANT**: if your project has copied any ActiveAdmin, Devise, or Kaminari templates from earlier releases, those templates must be updated from this release to avoid potential errors. Path helpers in Devise templates may require using the `main_app` proxy. The Kaminari templates have moved to `app/views/active_admin/kaminari`. + +With the setup complete, please review the Breaking Changes section and resolve any that may or may not impact your integration. + +### Breaking Changes +- jQuery and jQuery UI have been removed. +- The `columns` component has been removed. Use `div`'s with Tailwind classes for modern, responsive layout. + +
+ Columns Component Migration Alternative + + If you did not specify any parameters for `column` and if all you need is equal width columns, then this single component will restore that functionality for any number of columns. + + ```ruby + # app/admin/components/columns.rb + class Columns < ActiveAdmin::Component + builder_method :columns + + def build(*args) + super + add_class "grid auto-cols-fr grid-flow-col gap-4 mb-4" + end + + def column(*args, &block) + insert_tag Arbre::HTML::Div, *args, &block + end + end + ``` + + Using Tailwind modifiers you can further customize the number of columns for responsive/mobile support. +
+ +- Replace `default_main_content` with `render "show_default"`. + +
+ Show Default Alternative + + If block form `default_main_content do ... end` was used or looking for a partial file + alternative, then replace with existing, public methods. + + ```ruby + attributes_table_for(resource) do + rows *active_admin_config.resource_columns + row :a + row :b + # ... + end + active_admin_comments_for(resource) if active_admin_config.comments? + ``` +
+ +- Replace `as: :datepicker` with Formtastic's `as: :date_picker` for native HTML date input. +- Replace `active_admin_comments` with `active_admin_comments_for(resource)`. +- In a sidebar section, replace `attributes_table` with `attributes_table_for(resource)`. +- The `IndexAsBlog`, `IndexAsBlock` and `IndexAsGrid` components have been removed. Please create your own custom index-as components which remain supported. +- Batch Actions Form DSL has been replaced with Rails partial support so you can supply your own custom form and modal. + +
+ Batch Action Partial Example + + Assuming a Post resource (in the default namespace) with a `mark_published` batch action, we set the partial name and a set of HTML data attributes to trigger a modal using Flowbite which is included by default. + + Note that you can use any modal JS library you want as long as it can be triggered to open using data attributes. Flowbite usage is not a requirement. + + ```ruby + batch_action( + :mark_published, + partial: "mark_published_batch_action", + link_html_options: { + "data-modal-target": "mark-published-modal", + "data-modal-show": "mark-published-modal" + } + ) do |ids, inputs| + # ... + end + ``` + + In the `app/views/admin/posts` directory, create a `_mark_published_batch_action.html.erb` partial file which will be rendered and included automatically in the posts index admin page. + + Now add the modal HTML where the `id` attribute must match the data attributes supplied in the `batch_action` example. The form must have an empty `data-batch-action-form` attribute. + + ``` + + ``` + + The `data-batch-action-form` attribute is a hook for a delegated JS event so when you submit the form, it will post and run your batch action block with the supplied form data, functioning as it did before. +
+ +- Deeply nested submenus has been reverted. Only one level nested menu, e.g. `menu parent: "Administrative"`, is supported. +- Removed `Panel#header_action` method. +- Removed `index_column` method from index table. + +
+ Implementation Example + + You can re-implement this column with the following: + + ```ruby + column "Number", sortable: false do |item| + @collection.offset_value + @collection.index(item) + 1 + end + ``` +
+ +#### Resource named methods + +With the extraction to partials, resource named methods, e.g. `post` or `posts`, used in blocks for `action_item` and `sidebar` will raise an error. You must use the `resource` or `collection` public helper method instead. For example: + +```ruby +action_item :view, if: ->{ post.published? } do link_to(resource) end +sidebar :author, if: ->{ post.published? } +# The above must now change to the following: +action_item :view, if: ->{ resource.published? } do link_to(resource) end +sidebar :author, if: ->{ resource.published? } +``` + +Note that `@post` can also be used here but make sure to call `authorize!` on it if using the authorization feature. The `post` usage would continue to work for `sidebar :name do ... end` content blocks because they can include Arbre but we advise using `resource` or `collection` instead where possible. This may impact other DSL's. + +### Visual Related Changes +- The `sidebar do ... end` contents and the show resource `attributes_table`, are no longer wrapped in a panel so they can be customized. +- Links in custom `action_item`'s have no default styles. Apply your own or use the library's default `action-item-button` class. +- The index table `actions dropdown: true` option will be ignored, reverting to original output. +- An `Arbre::Component` will no longer add a CSS class using the component class name by default. +- Typographic elements (other than links in main content) [are not styled by default](https://tailwindcss.com/docs/preflight). Use the `@tailwindcss/typography` plugin or apply your own CSS alternative. + +### Enhancements +- Dark mode support. +- Mobile web support. For responsive `table_for`'s, wrap them in a div with overflow for horizontal scrolling. +- Customizable admin theme, including main menu and user menu, all through partials. +- RTL support improved. Now using CSS Logical Properties. +- Kaminari templates now consolidated into a single set you can customize. +- Datepicker's now use the native HTML date input. Apply a custom datepicker of your choosing. +- Batch Actions Form DSL has been replaced with partials and form builder for more customization. Please refer to earlier example. +- The `status_tag` component now uses unique labels for `false` and `nil` values. +- Several components: `table_for`, `status_tag`, etc. now use data attributes instead of classes for metadata: status, sort direction, column, etc. +- Arbre builder methods have been reduced to the minimum so you can use elements or DSLs without clashing e.g. `header`, `footer`, `columns`, etc. +- The [app-helpers-not-reloading bug has been fixed](https://github.com/activeadmin/activeadmin/pull/8180) and the engine namespace is now isolated. + +### Localization Updates + +This release includes several locale changes. Please [review the en.yml locale](https://github.com/activeadmin/activeadmin/blob/master/config/locales/en.yml) for the latest translations. + +- The `dashboard_welcome`, `dropdown_actions`, `main_content` and `unsupported_browser` keys have been removed. +- The `active_admin.pagination` keys have been rewritten to be less verbose and include new entries: next and previous. + + ```diff + - one: "Displaying 1 %{model}" + + one: "Showing 1 of 1" + - one_page: "Displaying all %{n} %{model}" + + one_page: "Showing all %{n}" + - multiple: "Displaying %{model} %{from} - %{to} of %{total} in total" + + multiple: "Showing %{from}-%{to} of %{total}" + - multiple_without_total: "Displaying %{model} %{from} - %{to}" + + multiple_without_total: "Showing %{from}-%{to}" + - per_page: "Per page: " + + per_page: "Per page " + + previous: "Previous" + + next: "Next" + ``` + +- The `search_status` key contents has multiple, breaking changes: + + ```diff + - headline: "Search status:" + - current_scope: "Scope:" + - current_filters: "Current filters:" + + title: "Active Search" + + title_with_scope: "Active Search for %{name}" + - no_current_filters: "None" + + no_current_filters: "No filters applied" + ``` + +- The value for the `status_tag.unset` key has changed from "No" to "Unknown". +- The `comments.title_content` text has been updated with an "All " prefix. +- The `comments.delete_confirmation` text has been fixed to use singular form. +- Inconsistent use of login/sign-in related terms so text now uses "Sign in", Sign out", and "Sign up" throughout. +- The `toggle_dark_mode`, `toggle_main_navigation_menu`, `toggle_section`, and `toggle_user_menu` keys have been added. +- The `batch_actions.succesfully_destroyed` key has been renamed to `batch_actions.successfully_destroyed` to fix a typo. diff --git a/_typos.toml b/_typos.toml new file mode 100644 index 00000000000..a89d7205417 --- /dev/null +++ b/_typos.toml @@ -0,0 +1,20 @@ +# https://github.com/crate-ci/typos#false-positives +[default] + +[default.extend-identifiers] + +[default.extend-words] +rememberable = "rememberable" + +[type.md] +extend-ignore-identifiers-re = [ + "succesfully_destroyed" +] + +[files] +extend-exclude = [ + "config/locales/*", + "!config/locales/en*.yml", + "features/step_definitions/batch_action_steps.rb", + "vendor/*" +] diff --git a/activeadmin.gemspec b/activeadmin.gemspec index 391b39031ba..69438afbceb 100644 --- a/activeadmin.gemspec +++ b/activeadmin.gemspec @@ -14,18 +14,29 @@ Gem::Specification.new do |s| "application patterns to make it simple for developers to implement " \ "beautiful and elegant interfaces with very little effort." - s.files = Dir["LICENSE", "{app,config/locales,docs,lib,vendor/assets}/**/{.*,*}"].reject { |f| File.directory?(f) } + s.files = Dir["LICENSE", "plugin.js", 'config/importmap.rb', "{app,config/locales,lib,vendor}/**/{.*,*}"].reject { |f| File.directory?(f) } - s.extra_rdoc_files = %w[CHANGELOG.md CODE_OF_CONDUCT.md CONTRIBUTING.md README.md] + s.extra_rdoc_files = %w[CHANGELOG.md CODE_OF_CONDUCT.md CONTRIBUTING.md README.md UPGRADING.md] - s.required_ruby_version = ">= 2.6" + s.metadata = { + "bug_tracker_uri" => "https://github.com/activeadmin/activeadmin/issues", + "changelog_uri" => "https://github.com/activeadmin/activeadmin/releases", + "documentation_uri" => "https://activeadmin.info", + "homepage_uri" => "https://activeadmin.info", + "mailing_list_uri" => "https://groups.google.com/group/activeadmin", + "rubygems_mfa_required" => "true", + "source_code_uri" => "https://github.com/activeadmin/activeadmin", + "wiki_uri" => "https://github.com/activeadmin/activeadmin/wiki" + } - s.add_dependency "arbre", "~> 1.2", ">= 1.2.1" - s.add_dependency "formtastic", ">= 3.1", "< 5.0" - s.add_dependency "formtastic_i18n", "~> 0.4" - s.add_dependency "inherited_resources", "~> 1.7" - s.add_dependency "jquery-rails", "~> 4.2" - s.add_dependency "kaminari", "~> 1.0", ">= 1.2.1" - s.add_dependency "railties", ">= 6.0", "< 7.1" - s.add_dependency "ransack", "~> 2.1", ">= 2.1.1" + s.required_ruby_version = ">= 3.1" + + s.add_dependency "arbre", "~> 2.0" + s.add_dependency "csv" + s.add_dependency "formtastic", ">= 5.0" + s.add_dependency "formtastic_i18n", ">= 0.7" + s.add_dependency "inherited_resources", "~> 2.0" + s.add_dependency "kaminari", ">= 1.2.1" + s.add_dependency "railties", ">= 7.0" + s.add_dependency "ransack", ">= 4.0" end diff --git a/app/assets/config/active_admin_manifest.js b/app/assets/config/active_admin_manifest.js new file mode 100644 index 00000000000..5d4ceea5b82 --- /dev/null +++ b/app/assets/config/active_admin_manifest.js @@ -0,0 +1,2 @@ +//= link_tree ../../javascript .js +//= link_tree ../../../vendor/javascript .js diff --git a/app/assets/javascripts/active_admin/base.js b/app/assets/javascripts/active_admin/base.js deleted file mode 100644 index b4a60fa6ce3..00000000000 --- a/app/assets/javascripts/active_admin/base.js +++ /dev/null @@ -1,524 +0,0 @@ -/** - * Warning: This file is auto-generated, do not modify. Instead, make your changes in 'app/javascript/active_admin/' and run `yarn build` - */ -//= require jquery3 -//= require jquery-ui/widgets/datepicker -//= require jquery-ui/widgets/dialog -//= require jquery-ui/widgets/sortable -//= require jquery-ui/widgets/tabs -//= require jquery-ui/widget -//= require jquery_ujs -//= require_self - -(function(global, factory) { - typeof exports === "object" && typeof module !== "undefined" ? factory(exports, require("jquery"), require("jquery-ui/ui/widgets/datepicker"), require("jquery-ui/ui/widgets/dialog"), require("jquery-ui/ui/widgets/sortable"), require("jquery-ui/ui/widgets/tabs"), require("jquery-ui/ui/widget"), require("jquery-ujs")) : typeof define === "function" && define.amd ? define([ "exports", "jquery", "jquery-ui/ui/widgets/datepicker", "jquery-ui/ui/widgets/dialog", "jquery-ui/ui/widgets/sortable", "jquery-ui/ui/widgets/tabs", "jquery-ui/ui/widget", "jquery-ujs" ], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, - factory(global.ActiveAdmin = {})); -})(this, function(exports) { - "use strict"; - $.fn.serializeObject = function() { - return this.serializeArray().reduce(function(obj, item) { - obj[item.name] = item.value; - return obj; - }, {}); - }; - $.ui.dialog.prototype._focusTabbable = function() { - this.uiDialog.focus(); - }; - function ModalDialog(message, inputs, callback) { - var html = '
    '; - for (var name in inputs) { - var opts, wrapper; - var type = inputs[name]; - if (/^(datepicker|checkbox|text|number)$/.test(type)) { - wrapper = "input"; - } else if (type === "textarea") { - wrapper = "textarea"; - } else if ($.isArray(type)) { - var _ref = [ "select", type, "" ]; - wrapper = _ref[0]; - opts = _ref[1]; - type = _ref[2]; - } else { - throw new Error("Unsupported input type: {" + name + ": " + type + "}"); - } - var klass = type === "datepicker" ? type : ""; - html += "
  • \n \n <" + wrapper + ' name="' + name + '" class="' + klass + '" type="' + type + '">' + (opts ? function() { - var result = []; - opts.forEach(function(v) { - var $elem = $(""); - if ($.isArray(v)) { - $elem.text(v[0]).val(v[1]); - } else { - $elem.text(v); - } - result.push($elem.wrap("
    ").parent().html()); - }); - return result; - }().join("") : "") + ("") + "
  • "; - var _ref2 = []; - wrapper = _ref2[0]; - opts = _ref2[1]; - type = _ref2[2]; - klass = _ref2[3]; - } - html += "
"; - var form = $(html).appendTo("body"); - $("body").trigger("modal_dialog:before_open", [ form ]); - form.dialog({ - modal: true, - open: function open(_event, _ui) { - $("body").trigger("modal_dialog:after_open", [ form ]); - }, - dialogClass: "active_admin_dialog", - buttons: { - OK: function OK() { - callback($(this).serializeObject()); - $(this).dialog("close"); - }, - Cancel: function Cancel() { - $(this).dialog("close").remove(); - } - } - }); - } - var onDOMReady$2 = function onDOMReady() { - $(".batch_actions_selector li a").off("click confirm:complete"); - $(".batch_actions_selector li a").on("click", function(event) { - var _this = this; - var message; - event.stopPropagation(); - event.preventDefault(); - if (message = $(this).data("confirm")) { - ModalDialog(message, $(this).data("inputs"), function(inputs) { - $(_this).trigger("confirm:complete", inputs); - }); - } else { - $(this).trigger("confirm:complete"); - } - }); - $(".batch_actions_selector li a").on("confirm:complete", function(event, inputs) { - var val; - if (val = JSON.stringify(inputs)) { - $("#batch_action_inputs").removeAttr("disabled").val(val); - } else { - $("#batch_action_inputs").attr("disabled", "disabled"); - } - $("#batch_action").val($(this).data("action")); - $("#collection_selection").submit(); - }); - if ($(".batch_actions_selector").length && $(":checkbox.toggle_all").length) { - if ($(".paginated_collection table.index_table").length) { - $(".paginated_collection table.index_table").tableCheckboxToggler(); - } else { - $(".paginated_collection").checkboxToggler(); - } - $(document).on("change", ".paginated_collection :checkbox", function() { - if ($(".paginated_collection :checkbox:checked").length && $(".dropdown_menu_list").children().length) { - $(".batch_actions_selector").each(function() { - $(this).aaDropdownMenu("enable"); - }); - } else { - $(".batch_actions_selector").each(function() { - $(this).aaDropdownMenu("disable"); - }); - } - }); - } - }; - $(document).ready(onDOMReady$2).on("page:load turbolinks:load", onDOMReady$2); - var CheckboxToggler = function() { - function CheckboxToggler(options, container) { - this.options = options; - this.container = container; - this._init(); - this._bind(); - } - var _proto = CheckboxToggler.prototype; - _proto.option = function option(_key, _value) {}; - _proto._init = function _init() { - if (!this.container) { - throw new Error("Container element not found"); - } else { - this.$container = $(this.container); - } - if (!this.$container.find(".toggle_all").length) { - throw new Error('"toggle all" checkbox not found'); - } else { - this.toggle_all_checkbox = this.$container.find(".toggle_all"); - } - this.checkboxes = this.$container.find(":checkbox").not(this.toggle_all_checkbox); - }; - _proto._bind = function _bind() { - var _this = this; - this.checkboxes.change(function(event) { - return _this._didChangeCheckbox(event.target); - }); - this.toggle_all_checkbox.change(function() { - return _this._didChangeToggleAllCheckbox(); - }); - }; - _proto._didChangeCheckbox = function _didChangeCheckbox(_checkbox) { - var numChecked = this.checkboxes.filter(":checked").length; - var allChecked = numChecked === this.checkboxes.length; - var someChecked = numChecked > 0 && numChecked < this.checkboxes.length; - this.toggle_all_checkbox.prop({ - checked: allChecked, - indeterminate: someChecked - }); - }; - _proto._didChangeToggleAllCheckbox = function _didChangeToggleAllCheckbox() { - var setting = this.toggle_all_checkbox.prop("checked"); - this.checkboxes.prop({ - checked: setting - }); - return setting; - }; - return CheckboxToggler; - }(); - $.widget.bridge("checkboxToggler", CheckboxToggler); - (function($) { - $(document).on("focus", "input.datepicker:not(.hasDatepicker)", function() { - var input = $(this); - if (input[0].type === "date") { - return; - } - var defaults = { - dateFormat: "yy-mm-dd" - }; - var options = input.data("datepicker-options"); - input.datepicker($.extend(defaults, options)); - }); - })(jQuery); - var DropdownMenu = function() { - function DropdownMenu(options, element) { - this.options = options; - this.element = element; - this.$element = $(this.element); - var defaults = { - fadeInDuration: 20, - fadeOutDuration: 100, - onClickActionItemCallback: null - }; - this.options = $.extend(defaults, this.options); - this.isOpen = false; - this.$menuButton = this.$element.find(".dropdown_menu_button"); - this.$menuList = this.$element.find(".dropdown_menu_list_wrapper"); - this._buildMenuList(); - this._bind(); - } - var _proto = DropdownMenu.prototype; - _proto.open = function open() { - this.isOpen = true; - this.$menuList.fadeIn(this.options.fadeInDuration); - this._position(); - return this; - }; - _proto.close = function close() { - this.isOpen = false; - this.$menuList.fadeOut(this.options.fadeOutDuration); - return this; - }; - _proto.destroy = function destroy() { - this.$element = null; - return this; - }; - _proto.isDisabled = function isDisabled() { - return this.$menuButton.hasClass("disabled"); - }; - _proto.disable = function disable() { - this.$menuButton.addClass("disabled"); - }; - _proto.enable = function enable() { - this.$menuButton.removeClass("disabled"); - }; - _proto.option = function option(key, value) { - if ($.isPlainObject(key)) { - return this.options = $.extend(true, this.options, key); - } else if (key != null) { - return this.options[key]; - } else { - return this.options[key] = value; - } - }; - _proto._buildMenuList = function _buildMenuList() { - this.$nipple = $(''); - this.$menuList.prepend(this.$nipple); - this.$menuList.hide(); - }; - _proto._bind = function _bind() { - var _this = this; - $("body").click(function() { - if (_this.isOpen) { - _this.close(); - } - }); - this.$menuButton.click(function() { - if (!_this.isDisabled()) { - if (_this.isOpen) { - _this.close(); - } else { - _this.open(); - } - } - return false; - }); - }; - _proto._position = function _position() { - this.$menuList.css("top", this.$menuButton.position().top + this.$menuButton.outerHeight() + 10); - var button_left = this.$menuButton.position().left; - var button_center = this.$menuButton.outerWidth() / 2; - var button_right = button_left + button_center * 2; - var menu_center = this.$menuList.outerWidth() / 2; - var nipple_center = this.$nipple.outerWidth() / 2; - var window_right = $(window).width(); - var centered_menu_left = button_left + button_center - menu_center; - var centered_menu_right = button_left + button_center + menu_center; - if (centered_menu_left < 0) { - this.$menuList.css("left", button_left); - this.$nipple.css("left", button_center - nipple_center); - } else if (centered_menu_right > window_right) { - this.$menuList.css("right", window_right - button_right); - this.$nipple.css("right", button_center - nipple_center); - } else { - this.$menuList.css("left", centered_menu_left); - this.$nipple.css("left", menu_center - nipple_center); - } - }; - return DropdownMenu; - }(); - $.widget.bridge("aaDropdownMenu", DropdownMenu); - var onDOMReady$1 = function onDOMReady() { - return $(".dropdown_menu").aaDropdownMenu(); - }; - $(document).ready(onDOMReady$1).on("page:load turbolinks:load", onDOMReady$1); - function hasTurbolinks() { - return typeof Turbolinks !== "undefined" && Turbolinks.supported; - } - function turbolinksVisit(params) { - var path = [ window.location.pathname, "?", toQueryString(params) ].join(""); - Turbolinks.visit(path); - } - function queryString() { - return (window.location.search || "").replace(/^\?/, ""); - } - function queryStringToParams() { - var decode = function decode(value) { - return decodeURIComponent((value || "").replace(/\+/g, "%20")); - }; - return queryString().split("&").map(function(pair) { - return pair.split("="); - }).map(function(_ref) { - var key = _ref[0], value = _ref[1]; - return { - name: decode(key), - value: decode(value) - }; - }); - } - function toQueryString(params) { - var encode = function encode(value) { - return encodeURIComponent(value || ""); - }; - return params.map(function(_ref2) { - var name = _ref2.name, value = _ref2.value; - return [ encode(name), encode(value) ]; - }).map(function(pair) { - return pair.join("="); - }).join("&"); - } - var Filters = function() { - function Filters() {} - Filters._clearForm = function _clearForm(event) { - var regex = /^(q\[|q%5B|q%5b|page|utf8|commit)/; - var params = queryStringToParams().filter(function(_ref) { - var name = _ref.name; - return !name.match(regex); - }); - event.preventDefault(); - if (hasTurbolinks()) { - turbolinksVisit(params); - } else { - window.location.search = toQueryString(params); - } - }; - Filters._disableEmptyInputFields = function _disableEmptyInputFields(event) { - var params = $(this).find(":input").filter(function(i, input) { - return input.value === ""; - }).prop({ - disabled: true - }).end().serializeArray(); - if (hasTurbolinks()) { - event.preventDefault(); - turbolinksVisit(params); - } - }; - Filters._setSearchType = function _setSearchType() { - $(this).siblings("input").prop({ - name: "q[" + this.value + "]" - }); - }; - return Filters; - }(); - (function($) { - $(document).on("click", ".clear_filters_btn", Filters._clearForm).on("submit", ".filter_form", Filters._disableEmptyInputFields).on("change", ".filter_form_field.select_and_search select", Filters._setSearchType); - })(jQuery); - $(function() { - $(document).on("click", "a.button.has_many_remove", function(event) { - event.preventDefault(); - var parent = $(this).closest(".has_many_container"); - var to_remove = $(this).closest("fieldset"); - recompute_positions(parent); - parent.trigger("has_many_remove:before", [ to_remove, parent ]); - to_remove.remove(); - return parent.trigger("has_many_remove:after", [ to_remove, parent ]); - }); - $(document).on("click", "a.button.has_many_add", function(event) { - var before_add; - event.preventDefault(); - var parent = $(this).closest(".has_many_container"); - parent.trigger(before_add = $.Event("has_many_add:before"), [ parent ]); - if (!before_add.isDefaultPrevented()) { - var index = parent.data("has_many_index") || parent.children("fieldset").length - 1; - parent.data({ - has_many_index: ++index - }); - var regex = new RegExp($(this).data("placeholder"), "g"); - var html = $(this).data("html").replace(regex, index); - var fieldset = $(html).insertBefore(this); - recompute_positions(parent); - return parent.trigger("has_many_add:after", [ fieldset, parent ]); - } - }); - $(document).on("change", '.has_many_container[data-sortable] :input[name$="[_destroy]"]', function() { - recompute_positions($(this).closest(".has_many")); - }); - init_sortable(); - $(document).on("has_many_add:after", ".has_many_container", init_sortable); - }); - var init_sortable = function init_sortable() { - var elems = $(".has_many_container[data-sortable]:not(.ui-sortable)"); - elems.sortable({ - items: "> fieldset", - handle: "> ol > .handle", - start: function start(ev, ui) { - ui.item.css({ - opacity: .3 - }); - }, - stop: function stop(ev, ui) { - ui.item.css({ - opacity: 1 - }); - recompute_positions($(this)); - } - }); - elems.each(recompute_positions); - }; - var recompute_positions = function recompute_positions(parent) { - parent = parent instanceof jQuery ? parent : $(this); - var input_name = parent.data("sortable"); - var position = parseInt(parent.data("sortable-start") || 0, 10); - parent.children("fieldset").each(function() { - var destroy_input = $(this).find("> ol > .input > :input[name$='[_destroy]']"); - var sortable_input = $(this).find("> ol > .input > :input[name$='[" + input_name + "]']"); - if (sortable_input.length) { - sortable_input.val(destroy_input.is(":checked") ? "" : position++); - } - }); - }; - var PerPage = function() { - function PerPage(element) { - this.element = element; - } - var _proto = PerPage.prototype; - _proto.update = function update() { - var params = queryStringToParams().filter(function(_ref) { - var name = _ref.name; - return name != "per_page" || name != "page"; - }); - params.push({ - name: "per_page", - value: this.element.value - }); - if (hasTurbolinks()) { - turbolinksVisit(params); - } else { - window.location.search = toQueryString(params); - } - }; - PerPage._jQueryInterface = function _jQueryInterface(config) { - return this.each(function() { - var $this = $(this); - var data = $this.data("perPage"); - if (!data) { - data = new PerPage(this); - $this.data("perPage", data); - } - if (config === "update") { - data[config](); - } - }); - }; - return PerPage; - }(); - (function($) { - $(document).on("change", ".pagination_per_page > select", function(_event) { - PerPage._jQueryInterface.call($(this), "update"); - }); - $.fn["perPage"] = PerPage._jQueryInterface; - $.fn["perPage"].Constructor = PerPage; - })(jQuery); - function _inheritsLoose(subClass, superClass) { - subClass.prototype = Object.create(superClass.prototype); - subClass.prototype.constructor = subClass; - _setPrototypeOf(subClass, superClass); - } - function _setPrototypeOf(o, p) { - _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { - o.__proto__ = p; - return o; - }; - return _setPrototypeOf(o, p); - } - var TableCheckboxToggler = function(_CheckboxToggler) { - _inheritsLoose(TableCheckboxToggler, _CheckboxToggler); - function TableCheckboxToggler() { - return _CheckboxToggler.apply(this, arguments) || this; - } - var _proto = TableCheckboxToggler.prototype; - _proto._bind = function _bind() { - var _this = this; - _CheckboxToggler.prototype._bind.apply(this, arguments); - this.$container.find("tbody td").click(function(event) { - if (event.target.type !== "checkbox") { - _this._didClickCell(event.target); - } - }); - }; - _proto._didChangeCheckbox = function _didChangeCheckbox(checkbox) { - _CheckboxToggler.prototype._didChangeCheckbox.apply(this, arguments); - $(checkbox).parents("tr").toggleClass("selected", checkbox.checked); - }; - _proto._didChangeToggleAllCheckbox = function _didChangeToggleAllCheckbox() { - this.$container.find("tbody tr").toggleClass("selected", _CheckboxToggler.prototype._didChangeToggleAllCheckbox.apply(this, arguments)); - }; - _proto._didClickCell = function _didClickCell(cell) { - $(cell).parent("tr").find(":checkbox").click(); - }; - return TableCheckboxToggler; - }(CheckboxToggler); - $.widget.bridge("tableCheckboxToggler", TableCheckboxToggler); - var onDOMReady = function onDOMReady() { - return $("#active_admin_content .tabs").tabs(); - }; - $(document).ready(onDOMReady).on("page:load turbolinks:load", onDOMReady); - function modal_dialog(message, inputs, callback) { - console.warn("ActiveAdmin.modal_dialog is deprecated in favor of ActiveAdmin.ModalDialog, please update usage."); - return ModalDialog(message, inputs, callback); - } - exports.ModalDialog = ModalDialog; - exports.modal_dialog = modal_dialog; - Object.defineProperty(exports, "__esModule", { - value: true - }); -}); diff --git a/app/assets/stylesheets/active_admin/_base.scss b/app/assets/stylesheets/active_admin/_base.scss deleted file mode 100644 index 3e214cc0180..00000000000 --- a/app/assets/stylesheets/active_admin/_base.scss +++ /dev/null @@ -1,57 +0,0 @@ -/* Active Admin CSS */ -@media screen { - // Normalize - @import "./normalize"; - - // Partials - @import "./typography"; - @import "./header"; - @import "./forms"; - @import "./components/comments"; - @import "./components/flash_messages"; - @import "./components/date_picker"; - @import "./components/tables"; - @import "./components/batch_actions"; - @import "./components/modal_dialog"; - @import "./components/blank_slates"; - @import "./components/breadcrumbs"; - @import "./components/dropdown_menu"; - @import "./components/buttons"; - @import "./components/grid"; - @import "./components/links"; - @import "./components/pagination"; - @import "./components/panels"; - @import "./components/columns"; - @import "./components/scopes"; - @import "./components/status_tags"; - @import "./components/table_tools"; - @import "./components/index_list"; - @import "./components/unsupported_browser"; - @import "./components/tabs"; - @import "./pages/logged_out"; - @import "./structure/footer"; - @import "./structure/main_structure"; - @import "./structure/title_bar"; - - html { - box-sizing: border-box; - } - - *, - *:before, - *:after { - box-sizing: inherit; - } - - body { - @include sans-family; - line-height: 1.5; - font-size: 72%; - background: $body-background-color; - color: $text-color; - } -} - -@media print { - @import "./print"; -} diff --git a/app/assets/stylesheets/active_admin/_forms.scss b/app/assets/stylesheets/active_admin/_forms.scss deleted file mode 100644 index 008e0e55b6f..00000000000 --- a/app/assets/stylesheets/active_admin/_forms.scss +++ /dev/null @@ -1,316 +0,0 @@ -// -------------------------------------- Active Admin Forms -form { - /* Reset margins & Padding */ - ul, ol, li, fieldset, legend, input, textarea, select, p { margin:0; padding:0; } - ol, ul { list-style: none } - - fieldset { - border: 0; - padding: 10px 0; - margin-bottom: 20px; - - &.inputs { @include section-background; } - - legend { - width: 100%; - span { display: block; @include section-header; } - } - - ol > li { - padding: 10px; - label { - display: block; - width: 20%; - float: left; - font-size: 1.0em; - font-weight: bold; - color: $form-label-color; - abbr { - border: none; - color: $required-field-marker-color; - } - } - } - - ol > li.has_many_container { - padding: 20px 10px; - h3 { - font-size: 12px; - font-weight: bold; - } - .has_many_fields { margin: 10px 0 } - } - - ol > li > li label { - line-height:100%; - padding-top:0; - input { - line-height:100%; - vertical-align:middle; - margin-top:-0.1em; - } - } - } - - // relative so the absolutely-positioned handle will fit - .has_many_fields { position: relative } - - .has_many_container { - .handle { - position: absolute; - top: calc(50% - 3em / 2); - right: 2px; - padding: 0; - cursor: move; - } - - // If a sortable is nested in a sortable, give the parent handle space! - &.ui-sortable .has_many_container { - margin-right: 2em; - } - } - - .ui-sortable { - // give the handle space! - input[type=text], input[type=password], input[type=email], input[type=number], input[type=url], input[type=tel], textarea { - width: calc(80% - #{$text-input-total-padding} - 2em - 1px); - } - } - - /* Nested Fieldsets and Legends */ - - fieldset > ol > li { - fieldset { - position:relative; - padding: 0; - margin-bottom: 0; - - &:not(.inputs) ol { - float: left; - width: 74%; - margin: 0; - padding: 0 0 0 20%; - - li { - padding: 0; - border: 0; - } - } - - &.inputs ol { - float: left; - width: 100%; - margin: 0; - } - } - } - - /* Text Fields */ - input[type=text], - input[type=password], - input[type=email], - input[type=number], - input[type=url], - input[type=tel], - input[type=date], - input[type=time], - textarea { - width: calc(80% - #{$text-input-total-padding}); - border: $border-width solid #c9d0d6; - @include rounded; - font-size: 0.95em; - @include sans-family; - outline: none; - padding: 8px $text-input-horizontal-padding 7px; - - &:focus { - border: $border-width solid #99a2aa; - @include shadow(0,0,4px,#99a2aa); - } - } - - input[type=date] { - width: calc(100% - #{$text-input-total-padding}); - } - - fieldset > ol > li { - - /* Hints */ - p.inline-hints { - font-size: 0.95em; - font-style: italic; - color:#666; - margin: 0.5em 0 0 20%; - } - - /* Date and Time Fields */ - &.date_select, &.time_select, &.datetime_select { - fieldset ol li { - float:left; width:auto; margin:0 0.5em 0 0; - label { display: none; } - input { display:inline; margin:0; padding:0; } - } - } - - /* Check Boxes or Radio fields */ - &.check_boxes, &.radio { - fieldset ol { - margin-bottom:-0.6em; - li { - margin:0.1em 0 0.5em 0; - label { - float:none; - width:100%; - input { margin-right:0.2em; } - } - } - } - } - - /* Boolean Field */ - &.boolean { - min-height: 1.1em; - label { - width: 80%; - padding-left:20%; - padding-right: 10px; - text-transform: none !important; - font-weight: normal; - input { margin:0 0.5em 0 0.2em; } - } - } - - /* Hidden fields */ - &.hidden { - padding: 0; - } - - /* Errors */ - p.inline-errors { - color: $error-color; - font-weight: bold; - margin:0.3em 0 0 20%; - } - ul.errors { - color: $error-color; - margin:0.5em 0 0 20%; - list-style:square; - li { padding:0; border:none; display:list-item; } - } - - &.error { - input[type=text], input[type=password], input[type=email], input[type=number], input[type=url], input[type=tel], textarea { - border: $border-width solid $error-color; - } - } - } - - /* semantic_errors */ - ul.errors { - background: lighten($error-color, 60%); - @include rounded(4px); - color: $error-color; - font-weight: bold; - margin-bottom: 10px; - padding: 10px; - list-style:square; - li { margin-left:15px; padding:0; border:none; display:list-item; } - } - - /* Buttons */ - - input[type=submit], input[type=button], button { - @include dark-button; - cursor: pointer; - - } - - .buttons, .actions { - margin-top: 15px; - input[type=submit], input[type=button], button { margin-right: 10px; } - } - - .actions .create_another { - float: none; - margin-bottom: 10px; - - label { - float: none; - display: inline; - } - } - - fieldset.buttons li, fieldset.actions li { - float:left; - padding: 0; - - &.cancel a { @include light-button; } - } -} - -// -------------------------------------- Sidebar Forms - -.sidebar_section { - - label { - display: block; - text-transform: uppercase; - color: $form-label-color; - font-size: 0.9em; - font-weight: bold; - } - - select { - width: $sidebar-inner-content-width; - } - - input[type=text], input[type=password], input[type=email], input[type=url], input[type=tel], textarea { - width: $sidebar-inner-content-width - ($text-input-horizontal-padding * 2); - } - -} - -// -------------------------------------- Filter Forms - -form.filter_form { - .filter_form_field { - margin-bottom: 10px; - clear: both; - - &.select_and_search { - input[type=text] { - margin-left: $filter-field-seperator-width + 4; - width: $side-by-side-filter-input-width; - } - select { - width: $side-by-side-filter-select-width; - } - } - - &.filter_check_boxes { - label { margin-bottom: 3px; } - fieldset { - margin-bottom: 0px; - padding-bottom: 0px; - } - .check_boxes_wrapper label { - font-weight: normal; - margin-bottom: 3px; - text-transform: none; - font-size: 1.0em; - input { vertical-align: baseline; } - } - } - - &.filter_date_range { - input[type=text] { - width: $date-range-filter-input-width; - - + input { - margin-left: 6px; - } - } - } - } - a.clear_filters_btn { @include light-button; } -} diff --git a/app/assets/stylesheets/active_admin/_header.scss b/app/assets/stylesheets/active_admin/_header.scss deleted file mode 100644 index 466cec5449e..00000000000 --- a/app/assets/stylesheets/active_admin/_header.scss +++ /dev/null @@ -1,188 +0,0 @@ -// ----------------------------------- Header -#header { - @include primary-gradient; - @include shadow; - @include text-shadow(#000); - display: table; - height: 20px; - width: 100%; - overflow: visible; - position: inherit; - padding: 5px 0; - z-index: 900; - - h1 { - display: table-cell; - vertical-align: middle; - white-space: nowrap; - color: $page-header-text-color; - margin-right: 20px; - margin-bottom: 0px; - padding: 3px $horizontal-page-margin 0 $horizontal-page-margin; - font-size: 1.3em; - font-weight: normal; - line-height: 1.2; - - a { - text-decoration: none; - - &:hover { - color: #fff; - } - } - - img { - position: relative; - top: -2px; - } - } - - a, a:link { color: $page-header-text-color; } - - .header-item { - top: 2px; - position: relative; - height: 20px - } - - ul.tabs { - display: table-cell; - vertical-align: middle; - height: 100%; - margin: 0; - padding: 0; - - li { - /* Hover on li, display the ul */ - &:hover > ul { display: block;} - } - - & > li { - display: inline-block; - margin-right: 4px; - margin-top: 5px; - margin-bottom: 5px; - font-size: 1.0em; - position: relative; - - a { - text-decoration: none; - padding: 6px 10px 4px 10px; - position: relative; - @include rounded(10px); - } - - &.current > a { - background: $current-menu-item-background; - color: #fff; - } - - &.has_nested > a { - background: url($menu-arrow-light-icon-url) no-repeat calc(100% - 7px) 50%; - padding-right: 20px; - } - - &.has_nested.current > a { - background: $current-menu-item-background url($menu-arrow-dark-icon-url) no-repeat calc(100% - 7px) 50%; - padding-right: 20px; - } - - &:hover > a { - background: $hover-menu-item-background; - color: #fff; - } - - &.has_nested:hover > a { - @include rounded-top(10px); - border-bottom: 5px solid $hover-menu-item-background; - background: $hover-menu-item-background url($menu-arrow-dark-icon-url) no-repeat calc(100% - 7px) 50%; - z-index: 1020; - } - - - /* Drop down menus */ - ul { - background: $hover-menu-item-background; - @include rounded-all(0,10px,10px,10px); - @include shadow(0, 1px, 3px, #444); - position: absolute; - width: 120%; - min-width: 175px; - max-width: calc(100% + 20px); - margin-top: 5px; - float: left; - display: none; - padding: 3px 0px 5px 0; - list-style: none; - z-index: 1010; - - li { - position: relative; - margin: 0px; - a { - background: none; - display: block; - &:hover { color: #fff; background: none; } - } - - &.current { - a { @include rounded(0) } - } - - &.has_nested > a { - background: url($menu-arrow-right-light-icon-url) no-repeat calc(100% - 7px) 55%; - padding-right: 20px; - } - - &.has_nested:hover > a { - background: url($menu-arrow-right-dark-icon-url) no-repeat calc(100% - 7px) 55%; - color: #fff; - } - - ul { - @include rounded-all(10px,10px,10px,10px); - margin-top: 0; - top: -3px; - left: 100%; - - /* Create an invisible backdrop that adds 8px margin around the dropdown menu or submenu - that maintains the hover. This makes it much easier to navigate to submenus in - particular without losing hover accientally, especially near rounded corners. */ - &:after { - content: ""; - display: block; - position: absolute; - top: -8px; - left: -8px; - height: calc(100% + 16px); - width: calc(100% + 16px); - z-index: -2; - } - } - } - } - } - } - - #tabs { - width: 100%; - } - - #utility_nav { - color: #aaa; - display: table-cell; - white-space: nowrap; - margin: 0; - padding: 0; - padding-right: 26px; - text-align: right; - - a { text-decoration: none; } - a:hover { color: #fff; } - - li { - display:inline; - } - } - -} diff --git a/app/assets/stylesheets/active_admin/_mixins.scss b/app/assets/stylesheets/active_admin/_mixins.scss deleted file mode 100644 index e57fce41f2d..00000000000 --- a/app/assets/stylesheets/active_admin/_mixins.scss +++ /dev/null @@ -1 +0,0 @@ -@import "./mixins/all"; diff --git a/app/assets/stylesheets/active_admin/_normalize.scss b/app/assets/stylesheets/active_admin/_normalize.scss deleted file mode 100644 index 192eb9ce433..00000000000 --- a/app/assets/stylesheets/active_admin/_normalize.scss +++ /dev/null @@ -1,349 +0,0 @@ -/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */ - -/* Document - ========================================================================== */ - -/** - * 1. Correct the line height in all browsers. - * 2. Prevent adjustments of font size after orientation changes in iOS. - */ - -html { - line-height: 1.15; /* 1 */ - -webkit-text-size-adjust: 100%; /* 2 */ -} - -/* Sections - ========================================================================== */ - -/** - * Remove the margin in all browsers. - */ - -body { - margin: 0; -} - -/** - * Render the `main` element consistently in IE. - */ - -main { - display: block; -} - -/** - * Correct the font size and margin on `h1` elements within `section` and - * `article` contexts in Chrome, Firefox, and Safari. - */ - -h1 { - font-size: 2em; - margin: 0.67em 0; -} - -/* Grouping content - ========================================================================== */ - -/** - * 1. Add the correct box sizing in Firefox. - * 2. Show the overflow in Edge and IE. - */ - -hr { - box-sizing: content-box; /* 1 */ - height: 0; /* 1 */ - overflow: visible; /* 2 */ -} - -/** - * 1. Correct the inheritance and scaling of font size in all browsers. - * 2. Correct the odd `em` font sizing in all browsers. - */ - -pre { - font-family: monospace, monospace; /* 1 */ - font-size: 1em; /* 2 */ -} - -/* Text-level semantics - ========================================================================== */ - -/** - * Remove the gray background on active links in IE 10. - */ - -a { - background-color: transparent; -} - -/** - * 1. Remove the bottom border in Chrome 57- - * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. - */ - -abbr[title] { - border-bottom: none; /* 1 */ - text-decoration: underline; /* 2 */ - text-decoration: underline dotted; /* 2 */ -} - -/** - * Add the correct font weight in Chrome, Edge, and Safari. - */ - -b, -strong { - font-weight: bolder; -} - -/** - * 1. Correct the inheritance and scaling of font size in all browsers. - * 2. Correct the odd `em` font sizing in all browsers. - */ - -code, -kbd, -samp { - font-family: monospace, monospace; /* 1 */ - font-size: 1em; /* 2 */ -} - -/** - * Add the correct font size in all browsers. - */ - -small { - font-size: 80%; -} - -/** - * Prevent `sub` and `sup` elements from affecting the line height in - * all browsers. - */ - -sub, -sup { - font-size: 75%; - line-height: 0; - position: relative; - vertical-align: baseline; -} - -sub { - bottom: -0.25em; -} - -sup { - top: -0.5em; -} - -/* Embedded content - ========================================================================== */ - -/** - * Remove the border on images inside links in IE 10. - */ - -img { - border-style: none; -} - -/* Forms - ========================================================================== */ - -/** - * 1. Change the font styles in all browsers. - * 2. Remove the margin in Firefox and Safari. - */ - -button, -input, -optgroup, -select, -textarea { - font-family: inherit; /* 1 */ - font-size: 100%; /* 1 */ - line-height: 1.15; /* 1 */ - margin: 0; /* 2 */ -} - -/** - * Show the overflow in IE. - * 1. Show the overflow in Edge. - */ - -button, -input { /* 1 */ - overflow: visible; -} - -/** - * Remove the inheritance of text transform in Edge, Firefox, and IE. - * 1. Remove the inheritance of text transform in Firefox. - */ - -button, -select { /* 1 */ - text-transform: none; -} - -/** - * Correct the inability to style clickable types in iOS and Safari. - */ - -button, -[type="button"], -[type="reset"], -[type="submit"] { - -webkit-appearance: button; -} - -/** - * Remove the inner border and padding in Firefox. - */ - -button::-moz-focus-inner, -[type="button"]::-moz-focus-inner, -[type="reset"]::-moz-focus-inner, -[type="submit"]::-moz-focus-inner { - border-style: none; - padding: 0; -} - -/** - * Restore the focus styles unset by the previous rule. - */ - -button:-moz-focusring, -[type="button"]:-moz-focusring, -[type="reset"]:-moz-focusring, -[type="submit"]:-moz-focusring { - outline: 1px dotted ButtonText; -} - -/** - * Correct the padding in Firefox. - */ - -fieldset { - padding: 0.35em 0.75em 0.625em; -} - -/** - * 1. Correct the text wrapping in Edge and IE. - * 2. Correct the color inheritance from `fieldset` elements in IE. - * 3. Remove the padding so developers are not caught out when they zero out - * `fieldset` elements in all browsers. - */ - -legend { - box-sizing: border-box; /* 1 */ - color: inherit; /* 2 */ - display: table; /* 1 */ - max-width: 100%; /* 1 */ - padding: 0; /* 3 */ - white-space: normal; /* 1 */ -} - -/** - * Add the correct vertical alignment in Chrome, Firefox, and Opera. - */ - -progress { - vertical-align: baseline; -} - -/** - * Remove the default vertical scrollbar in IE 10+. - */ - -textarea { - overflow: auto; -} - -/** - * 1. Add the correct box sizing in IE 10. - * 2. Remove the padding in IE 10. - */ - -[type="checkbox"], -[type="radio"] { - box-sizing: border-box; /* 1 */ - padding: 0; /* 2 */ -} - -/** - * Correct the cursor style of increment and decrement buttons in Chrome. - */ - -[type="number"]::-webkit-inner-spin-button, -[type="number"]::-webkit-outer-spin-button { - height: auto; -} - -/** - * 1. Correct the odd appearance in Chrome and Safari. - * 2. Correct the outline style in Safari. - */ - -[type="search"] { - -webkit-appearance: textfield; /* 1 */ - outline-offset: -2px; /* 2 */ -} - -/** - * Remove the inner padding in Chrome and Safari on macOS. - */ - -[type="search"]::-webkit-search-decoration { - -webkit-appearance: none; -} - -/** - * 1. Correct the inability to style clickable types in iOS and Safari. - * 2. Change font properties to `inherit` in Safari. - */ - -::-webkit-file-upload-button { - -webkit-appearance: button; /* 1 */ - font: inherit; /* 2 */ -} - -/* Interactive - ========================================================================== */ - -/* - * Add the correct display in Edge, IE 10+, and Firefox. - */ - -details { - display: block; -} - -/* - * Add the correct display in all browsers. - */ - -summary { - display: list-item; -} - -/* Misc - ========================================================================== */ - -/** - * Add the correct display in IE 10+. - */ - -template { - display: none; -} - -/** - * Add the correct display in IE 10. - */ - -[hidden] { - display: none; -} diff --git a/app/assets/stylesheets/active_admin/_print.scss b/app/assets/stylesheets/active_admin/_print.scss deleted file mode 100644 index 96fdd501228..00000000000 --- a/app/assets/stylesheets/active_admin/_print.scss +++ /dev/null @@ -1,287 +0,0 @@ -/* Active Admin Print Stylesheet */ - -// Set colors used elsewhere -$primary-color: black; -$text-color: black; - -// Normalize -@import "./normalize"; - -// Partials -@import "./typography"; - -body { - font-family: Helvetica, Arial, sans-serif; - line-height: 150%; - font-size: 72%; - background: #fff; - width: 99%; - margin: 0; - padding: .5%; - color: $text-color; -} - -a { - color: $text-color; - text-decoration: none; -} - -h3 { - font-weight: bold; - margin-bottom: .5em; -} - -// Header -#header { - float: left; - - #tabs, .tabs, #utility_nav { - display: none; - } - - h1{ - font-weight: bold; - } -} - -.flashes { - display: none; -} - -#title_bar { - float: right; - - h2 { - line-height: 2em; - margin: 0; - } - - .breadcrumb, #titlebar_right { - display: none; - } -} - -// Content -#active_admin_content { - border-top: thick solid black; - clear: both; - margin-top: 2em; - padding-top: 3em; -} - -// Footer -#footer { - display: none; -} - -// Tables -.table_tools { - ul { - padding: 0; - margin: 0; - list-style-type: none; - - li { - display: none; - padding: 0; - margin-bottom: 1em; - - &.scope.selected, &.index.selected { - display: block; - - &:before { - content: "Showing "; - } - - a { - font-weight: bold; - } - - span { - display: inline-block; - font-weight: normal; - font-size: .9em; - } - } - } - - } -} - -table { - margin-bottom: 1.5em; - text-align: left; - width: 100%; - - thead { - display: table-header-group; - - th { - background: none; - border-bottom: medium solid black; - font-weight: bold; - - a{ - text-decoration: none; - } - } - } - - th, td { - padding: .5em 1em; - - .member_link { - display: none; - } - } - - td { - border-bottom: thin solid black; - } - - tr{ - page-break-inside: avoid; - } -} - -// Index -#index_footer, .pagination_information { - display: none; -} - -.index_grid { - td { - border: none; - text-align: center; - vertical-align: middle; - - img { - max-width: 1in; - } - } -} - -// Show -.panel { - border-bottom: thick solid #ccc; - margin-bottom: 3em; - padding-bottom: 2em; - page-break-inside: avoid; - - &:last-child { - border-bottom: none; - } -} - -.comments { - form { - display: none; - } - - .active_admin_comment { - border-top: thin solid black; - padding-top: 1em; - - .active_admin_comment_meta { - h4 { - font-size: 1em; - font-weight: bold; - float: left; - margin-right: .5em; - margin-bottom: 0; - } - - span { - font-size: .9em; - font-style: italic; - vertical-align: top; - } - } - - .active_admin_comment_body { - clear: both; - margin-bottom: 1em; - } - } -} - - -// Attribute Tables -.attributes_table { - border-top: medium solid black; - - th { - border-bottom: thin solid black; - vertical-align: top; - - &:after { - content: ':'; - } - } - - td { - img { - max-height: 4in; - max-width: 6in; - } - } -} - -// Sidebars -#filters_sidebar_section { - display: none; -} - -// Forms -form { - fieldset { - border-top: thick solid #ccc; - padding-top: 2em; - margin-bottom: 2em; - - &:last-child { - border-bottom: none; - } - } - - .buttons, abbr { - display: none; - } - ol { - list-style-type: none; - padding: 0; - margin: 0; - - li{ - border-top: thin solid black; - margin: 0; - padding: 1em 0; - overflow: hidden; - - &.password, &.hidden { - display: none; - } - - label { - font-weight: bold; - float: left; - width: 20%; - } - - input, textarea, select { - background: none; - border: 0; - font: Arial, Helvetica, sans-serif; - } - - input[type=file] { - display: none; - } - - } - } -} - -.unsupported_browser { - display: none; -} diff --git a/app/assets/stylesheets/active_admin/_typography.scss b/app/assets/stylesheets/active_admin/_typography.scss deleted file mode 100644 index ddb369f37fb..00000000000 --- a/app/assets/stylesheets/active_admin/_typography.scss +++ /dev/null @@ -1,100 +0,0 @@ -// Adapted from Blueprint CSS Framework -// -// Copyright (c) 2007 - 2010 blueprintcss.org -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the "Software"), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. - -// Default font settings. The font-size percentage is of 16px. (0.75 * 16px = 12px) */ -html { font-size:100.01%; } -body { font-size: 75%; font-family: "Helvetica Neue", Arial, Helvetica, sans-serif; } - -// Headings -h1,h2,h3,h4,h5,h6 { - font-weight: normal; - color: $primary-color; - img { margin: 0; } -} - -h1 { font-size: 3em; line-height: 1; margin-bottom: 0.5em; } -h2 { font-size: 2em; margin-bottom: 0.75em; } -h3 { font-size: 1.5em; line-height: 1; margin-bottom: 1em; } -h4 { font-size: 1.2em; line-height: 1.25; margin-bottom: 1.25em; } -h5 { font-size: 1em; font-weight: bold; margin-bottom: 1.5em; } -h6 { font-size: 1em; font-weight: bold; } - - -p { - margin: 0 0 1.5em; - - .left { margin: 1.5em 1.5em 1.5em 0; padding: 0; } - .right { margin: 1.5em 0 1.5em 1.5em; padding: 0; } -} - -.left { float: left !important; } -.right { float: right !important; } - -blockquote { margin: 1.5em; color: #666; font-style: italic; } -strong,dfn { font-weight: bold; } -em,dfn { font-style: italic; } -sup, sub { line-height: 0; } - -abbr, -acronym { border-bottom: 1px dotted #666; } -address { margin: 0 0 1.5em; font-style: italic; } -del { color:#666; } - -pre { margin: 1.5em 0; white-space: pre; } -pre,code,tt { font: 1em 'andale mono', 'lucida console', monospace; line-height: 1.5; } - -// Lists -li ul, -li ol { margin: 0; } -ul, ol { margin: 0 1.5em 1.5em 0; padding-left: 1.5em; } - -ul { list-style-type: disc; } -ol { list-style-type: decimal; } - -dl { margin: 0 0 1.5em 0; } -dl dt { font-weight: bold; } -dd { margin-left: 1.5em;} - -// Tables -table { margin-bottom: 1.4em; width:100%; } -th { font-weight: bold; } -thead th { background: #c3d9ff; } -th,td,caption { padding: 4px 10px 4px 5px; } - -// Helper Classes -.small { font-size: .8em; margin-bottom: 1.875em; line-height: 1.875em; } -.large { font-size: 1.2em; line-height: 2.5em; margin-bottom: 1.25em; } -.hide { display: none; } - -.quiet { color: #666; } -.loud { color: #000; } -.highlight { background:#ff0; } -.added { background:#060; color: #fff; } -.removed { background:#900; color: #fff; } - -.first { margin-left:0; padding-left:0; } -.last { margin-right:0; padding-right:0; } -.top { margin-top:0; padding-top:0; } -.bottom { margin-bottom:0; padding-bottom:0; } diff --git a/app/assets/stylesheets/active_admin/components/_batch_actions.scss b/app/assets/stylesheets/active_admin/components/_batch_actions.scss deleted file mode 100644 index 9a054f6122d..00000000000 --- a/app/assets/stylesheets/active_admin/components/_batch_actions.scss +++ /dev/null @@ -1,6 +0,0 @@ -#collection_selection_toggle_panel { - @include clearfix; - >.resource_selection_toggle_cell { - float:left; - } -} diff --git a/app/assets/stylesheets/active_admin/components/_blank_slates.scss b/app/assets/stylesheets/active_admin/components/_blank_slates.scss deleted file mode 100644 index 7cd17638a28..00000000000 --- a/app/assets/stylesheets/active_admin/components/_blank_slates.scss +++ /dev/null @@ -1,30 +0,0 @@ -.blank_slate_container { - clear: both; - text-align: center; - - .blank_slate { - @include rounded; - border: $blank-slate-border; - color: $blank-slate-primary-color; - display: inline-block; - font-size: 1.2em; - font-weight: bold; - padding: 14px 25px; - text-align: center; - - small { - display: block; - font-size: 0.9em; - font-weight: normal; - } - } -} - -.admin_dashboard .blank_slate_container .blank_slate { - margin-top: 40px; - margin-bottom: 40px; -} - -.with_sidebar .blank_slate_container .blank_slate { - margin-top: 80px; -} diff --git a/app/assets/stylesheets/active_admin/components/_breadcrumbs.scss b/app/assets/stylesheets/active_admin/components/_breadcrumbs.scss deleted file mode 100644 index b339907ff8e..00000000000 --- a/app/assets/stylesheets/active_admin/components/_breadcrumbs.scss +++ /dev/null @@ -1,20 +0,0 @@ -.breadcrumb { - display: block; - font-size: 0.9em; - font-weight: normal; - line-height: 1.0em; - margin-bottom: 12px; - text-transform: uppercase; - - a, a:link, a:visited, a:active { - color: $breadcrumbs-color; - text-decoration: none; - } - - a:hover { text-decoration: underline; } - - .breadcrumb_sep { - margin: 0 2px; - color: $breadcrumbs-separator-color; - } -} diff --git a/app/assets/stylesheets/active_admin/components/_buttons.scss b/app/assets/stylesheets/active_admin/components/_buttons.scss deleted file mode 100644 index 94c1ee18750..00000000000 --- a/app/assets/stylesheets/active_admin/components/_buttons.scss +++ /dev/null @@ -1,6 +0,0 @@ -a.member_link { - margin-right: 7px; - white-space: nowrap; -} - -a.button, a:link.button, a:visited.button, input[type=submit], input[type=button], button { @include dark-button; } diff --git a/app/assets/stylesheets/active_admin/components/_columns.scss b/app/assets/stylesheets/active_admin/components/_columns.scss deleted file mode 100644 index 3587763ddf0..00000000000 --- a/app/assets/stylesheets/active_admin/components/_columns.scss +++ /dev/null @@ -1,3 +0,0 @@ -.columns { - margin-bottom: 10px; -} diff --git a/app/assets/stylesheets/active_admin/components/_comments.scss b/app/assets/stylesheets/active_admin/components/_comments.scss deleted file mode 100644 index c1c83aeb0de..00000000000 --- a/app/assets/stylesheets/active_admin/components/_comments.scss +++ /dev/null @@ -1,41 +0,0 @@ -// -------------------------------------- Admin Notes -.comments { - - .active_admin_comment { - @include clearfix; - margin-top: 10px; - margin-bottom: 20px; - max-width: 700px; - - .active_admin_comment_meta { - width: 130px; - float: left; - overflow: hidden; - font-size: 0.9em; - color: lighten($primary-color, 10%); - .active_admin_comment_author { - font-size: 1.2em; - font-weight: bold; - margin: 0; - color: $primary-color; - } - } - .active_admin_comment_body { - margin-left: 150px; - } - } - form.active_admin_comment { - margin: 0; - padding: 0; - margin-left: 150px; - - fieldset.inputs { - margin: 0; - padding: 0; - background: none; - @include no-shadow; - } - li { padding: 0; } - fieldset.buttons { padding: 0; margin-top: 5px;} - } -} diff --git a/app/assets/stylesheets/active_admin/components/_date_picker.scss b/app/assets/stylesheets/active_admin/components/_date_picker.scss deleted file mode 100644 index 4136e782523..00000000000 --- a/app/assets/stylesheets/active_admin/components/_date_picker.scss +++ /dev/null @@ -1,148 +0,0 @@ -// -------------------------------------- Date Picker -.ui-datepicker { - background: #fff; - background-clip: padding-box; - color: #fff; - display: none; - margin-top: 2px; - padding: 0; - text-align: center; - width: 160px; - - a { - text-decoration: none; - &:hover { - cursor: pointer; - } - } - - .ui-datepicker-header { - @include primary-gradient; - padding: 12px 5px 7px 4px; - margin: 0px 0px 2px 2px; - width: 156px; - border-top-left-radius: 7px; - border-top-right-radius: 7px; - position: relative; - z-index: 2000; - - &:before { - content: ""; - position: absolute; - right: 45%; - top: -6px; - width: 0px; - height: 0px; - border-left: 8.5px solid rgba(0, 0, 0, 0); - border-right: 8.5px solid rgba(0, 0, 0, 0); - border-bottom: 10px solid #676e73; - } - - .ui-datepicker-title { - @include text-shadow(#000); - color: #fff; - display: block; - font-size: 1.1em; - font-weight: bold; - line-height: 0.8em; - text-align: center; - - .ui-datepicker-month { - margin: -4px 0 0 0; - } - .ui-datepicker-year { - margin: -4px 0 0 0; - } - } - - - a { - color: #fff; - display: block; - height: 19px; - margin-top: -4px; - width: 10px; - - &.ui-datepicker-prev { - float: left; - width: 0; - height: 0; - margin: 0px 0px 0px 4px; - border-top: 5px solid transparent; - border-right: 5px solid white; - border-bottom: 5px solid transparent; - } - &.ui-datepicker-next { - float: right; - width: 0; - height: 0; - margin: 0px 4px 0px 0px; - border-top: 5px solid transparent; - border-left: 5px solid white; - border-bottom: 5px solid transparent; - } - - span { - display: none; - } - } - } - - table.ui-datepicker-calendar { - @include rounded-bottom; - @include shadow(0,1px,6px,rgba(0,0,0,0.26)); - background-color: #f4f4f4; - border: solid 1px #63686e; - left: 2px; - margin-bottom: 0px; - position: relative; - top: -2px; - width: 156px; - - td, th { - padding: 0px; - text-align: center; - } - - thead th { - background-color: #dbdddf; - color: #333333; - font-weight: normal; - font-size: 0.8em; - padding-top: 1px; - } - - tbody { - color: #666666; - - td { - border: none; - height: 24px; - width: 22px; - - a { - @include rounded; - color: #666666; - font-weight: bold; - font-size: 0.85em; - padding: 4px; - - &.ui-state-active { - background-color: #5a5f64; - color: #fff; - &.ui-state-hover { - background-color: #5a5f64; - color: #fff; - } - } - &.ui-state-hover { - background-color: #eceef0; - } - &.ui-state-highlight { - background-color: #dbdddf; - } - } - } - } - } -} diff --git a/app/assets/stylesheets/active_admin/components/_dropdown_menu.scss b/app/assets/stylesheets/active_admin/components/_dropdown_menu.scss deleted file mode 100644 index b02f2046981..00000000000 --- a/app/assets/stylesheets/active_admin/components/_dropdown_menu.scss +++ /dev/null @@ -1,151 +0,0 @@ -.dropdown_menu { - display: inline; - - .dropdown_menu_button { - @include light-button; - position: relative; - padding-right: 22px !important; - cursor: pointer; - - &:before { - content: ' '; - position: absolute; - width: 0; - height: 0; - border-width: 3px 3px 0; - border-style: solid; - border-color: #FFF transparent; - right: 12px; - top: 45%; - } - - &:after { - content: ' '; - position: absolute; - width: 0; - height: 0; - border-width: 3px 3px 0; - border-style: solid; - border-color: #777 transparent; - right: 12px; - top: 45%; - } - } - - .dropdown_menu_nipple { - - // The nipple's border - content: ""; - position: absolute; - top: -6px; - display: block; - width: 0; - height: 0; - border-width: 0 6px 6px; - border-style: solid; - border-color: darken($primary-color, 4%) transparent; - z-index: 100; - - // The nipple's inner shadow - - &:before { - content: ' '; - position: absolute; - width: 0; - height: 0; - border-width: 0 5px 5px; - border-style: solid; - border-color: lighten($primary-color, 15%) transparent; - left: -5px; - top: 1px; - } - - // The nipple's background color - - &:after { - content: ' '; - position: absolute; - width: 0; - height: 0; - border-width: 0 5px 5px; - border-style: solid; - border-color: lighten($primary-color, 4%) transparent; - left: -5px; - top: 2px; - } - } - - .dropdown_menu_list_wrapper { - display: inline-block; - position: absolute; - background-color: white; - padding: 2px; - box-shadow: rgba(0,0,0,0.4) 0 1px 3px, lighten($primary-color, 15%) 0px 1px 0px 0px inset; - background-color: $primary-color; - @include gradient(lighten($primary-color, 4%), darken($primary-color, 5%)); - border: solid 1px darken($primary-color, 10%); - border-top-color: darken($primary-color, 4%); - border-bottom-color: darken($primary-color, 17%); - border-radius: 4px; - z-index: 2000; - display: none; - - .dropdown_menu_list { - display: block; - background-color: #FFF; - border: solid 1px darken($primary-color, 10%); - box-shadow: lighten($primary-color, 5%) 0px 1px 0px 0px; - border-radius: 3px; - margin: 0; - overflow: hidden; - padding: 8px; - - list-style-type: none; - padding: 0; - - li { - display: block; - border-bottom: solid 1px #ebebeb; - - a { - display: block; - box-sizing: padding-box; - font-size: 0.95em; - font-weight: bold; - padding: 7px 16px 5px; - text-decoration: none; - text-align: center; - white-space: nowrap; - - &:hover { - @include highlight-gradient; - @include text-shadow(#5a83aa); - color: #FFF; - } - - &:active { - @include reverse-highlight-gradient; - color: #FFF; - } - - } - - &:first-child { - a { - border-top-left-radius: 2px; - border-top-right-radius: 2px; - } - - } - - &:last-child { - a { - border-bottom-left-radius: 2px; - border-bottom-right-radius: 2px; - } - border: none; - } - } - } - } -} diff --git a/app/assets/stylesheets/active_admin/components/_flash_messages.scss b/app/assets/stylesheets/active_admin/components/_flash_messages.scss deleted file mode 100644 index 53e73f57d32..00000000000 --- a/app/assets/stylesheets/active_admin/components/_flash_messages.scss +++ /dev/null @@ -1,37 +0,0 @@ -body.logged_in { - .flash { - @include gradient(#f7f1d3, #f5edc5); - @include text-shadow(#fafafa); - border-bottom: 1px solid #eee098; - color: #cb9810; - font-weight: bold; - font-size: 1.1em; - line-height: 1.0em; - padding: 13px 30px 11px; - position: relative; - - &.flash_notice { - @include gradient(#dce9dd, #ccdfcd); - border-bottom: 1px solid #adcbaf; - color: #416347; - } - &.flash_error { - @include gradient(#f5e4e4, #f1dcdc); - border-bottom: 1px solid #e0c2c0; - color: #b33c33; - } - } -} - -body.logged_out { - .flash { - @include no-shadow; - @include text-shadow(#fff); - background: none; - color: #666; - font-weight: bold; - line-height: 1.0em; - padding: 0; - margin-bottom: 8px; - } -} diff --git a/app/assets/stylesheets/active_admin/components/_grid.scss b/app/assets/stylesheets/active_admin/components/_grid.scss deleted file mode 100644 index 7a8efbc551b..00000000000 --- a/app/assets/stylesheets/active_admin/components/_grid.scss +++ /dev/null @@ -1,9 +0,0 @@ -// -------------------------------------- Index as Grid -table.index_grid td { border: none; background: none; padding: 0 20px 20px 0; margin: 0;} - -// -------------------------------------- Columns -.columns { - clear: both; - padding: 0; - .column { float: left; } -} diff --git a/app/assets/stylesheets/active_admin/components/_index_list.scss b/app/assets/stylesheets/active_admin/components/_index_list.scss deleted file mode 100644 index c81cc6998a5..00000000000 --- a/app/assets/stylesheets/active_admin/components/_index_list.scss +++ /dev/null @@ -1,12 +0,0 @@ -.indexes { - float: right; - - li { - .count { - color: #8e979e; - font-weight: normal; - font-size: 0.9em; - line-height: 10px; - } - } -} diff --git a/app/assets/stylesheets/active_admin/components/_links.scss b/app/assets/stylesheets/active_admin/components/_links.scss deleted file mode 100644 index 0e6a62c2d24..00000000000 --- a/app/assets/stylesheets/active_admin/components/_links.scss +++ /dev/null @@ -1,5 +0,0 @@ -a, a:link, a:visited { - color: $link-color; - text-decoration: underline; -} -a:hover { text-decoration: none; } diff --git a/app/assets/stylesheets/active_admin/components/_modal_dialog.scss b/app/assets/stylesheets/active_admin/components/_modal_dialog.scss deleted file mode 100644 index f3bd4971f24..00000000000 --- a/app/assets/stylesheets/active_admin/components/_modal_dialog.scss +++ /dev/null @@ -1,34 +0,0 @@ -.ui-widget-overlay { - position: fixed; - background: rgba(0,0,0,.2); - top: 0; left: 0; right: 0; bottom: 0; - z-index: 1001; -} - -.ui-dialog { - position: fixed; - z-index: 1002; - @include section-background; - box-shadow: rgba(0,0,0,0.5) 0 0 10px; - - .ui-dialog-titlebar { - @include section-header; - span { font-size: 1.1em } - } - - ul { list-style-type: none } - li { margin: 10px 0 } - label { margin-right: 10px } - - .ui-dialog-buttonpane, form { - padding: 7px 15px 13px - } - .ui-dialog-buttonpane button { - & { @include dark-button } // OK - &:last-child { @include light-button } // Cancel - } -} - -.active_admin_dialog.ui-dialog { - .ui-dialog-titlebar-close { display: none } -} diff --git a/app/assets/stylesheets/active_admin/components/_pagination.scss b/app/assets/stylesheets/active_admin/components/_pagination.scss deleted file mode 100644 index bbed72488c5..00000000000 --- a/app/assets/stylesheets/active_admin/components/_pagination.scss +++ /dev/null @@ -1,55 +0,0 @@ -.paginated_collection_contents { - clear: both; -} - -.pagination { - float: right; - font-size: 0.9em; - margin-left: 10px; - - a { - @include light-button; - } - - span.page.current { - @include default-button; - } - - a, span.page.current { - @include rounded(0px); - margin-right: 4px; - padding: 2px 5px; - } -} - -.pagination_information { - float: right; - margin-bottom: 5px; - color: #b3bcc1; - b { color: #5c6469; } -} - -.download_links { - float: left; -} - -.pagination_per_page { - float: right; - margin-left: 4px; - select { - @include light-button; - @include rounded(0px); - padding: 1px 5px; - } -} - -.comments { - .pagination { - float: left; - margin-bottom: 30px; - } - .pagination_information { - float: left; - color: #000; - } -} diff --git a/app/assets/stylesheets/active_admin/components/_panels.scss b/app/assets/stylesheets/active_admin/components/_panels.scss deleted file mode 100644 index dc28c04a829..00000000000 --- a/app/assets/stylesheets/active_admin/components/_panels.scss +++ /dev/null @@ -1,6 +0,0 @@ -// ----------------------------------- Helper class to apply to elements to make them sections -.section, .panel{ @include section; } - -// ----------------------------------- Sidebar Sections - -.sidebar_section { @include section; } diff --git a/app/assets/stylesheets/active_admin/components/_scopes.scss b/app/assets/stylesheets/active_admin/components/_scopes.scss deleted file mode 100644 index c6fbddd0f90..00000000000 --- a/app/assets/stylesheets/active_admin/components/_scopes.scss +++ /dev/null @@ -1,13 +0,0 @@ -.scopes { - li { - .count { - color: #8e979e; - font-weight: normal; - font-size: 0.9em; - line-height: 10px; - } - &:first-child a { - margin-left: 10px; - } - } -} diff --git a/app/assets/stylesheets/active_admin/components/_status_tags.scss b/app/assets/stylesheets/active_admin/components/_status_tags.scss deleted file mode 100644 index 42cd1682f0b..00000000000 --- a/app/assets/stylesheets/active_admin/components/_status_tags.scss +++ /dev/null @@ -1,12 +0,0 @@ -.status_tag { - background: darken($secondary-color, 15%); - color: #fff; - text-transform: uppercase; - letter-spacing: 0.15em; - padding: 3px 5px 2px 5px; - font-size: 0.8em; - - &.yes { background: #6090DB } - &.no { background: grey } - -} diff --git a/app/assets/stylesheets/active_admin/components/_table_tools.scss b/app/assets/stylesheets/active_admin/components/_table_tools.scss deleted file mode 100644 index 1b5e430c9a4..00000000000 --- a/app/assets/stylesheets/active_admin/components/_table_tools.scss +++ /dev/null @@ -1,67 +0,0 @@ -.table_tools { - @include clearfix; - margin-bottom: 16px; -} - -.table_tools .dropdown_menu { - float: left; -} - -a.table_tools_button, .table_tools .dropdown_menu_button { - @include light-button; - @include gradient(#FFFFFF, #F0F0F0); - @include border-colors(#d9d9d9, #d0d0d0, #c5c5c5); - font-size: 0.9em; - padding: 4px 14px 4px; - margin: 0; - - &:not(.disabled) { - &:hover { - @include gradient(#FFFFFF, #F6F6F6); - } - - &:active { - @include border-colors(#d7d7d7, #c8c8c8, #c3c3c3); - box-shadow: 0 1px 1px 0 rgba(0,0,0,0.17) inset; - @include gradient(#FFFFFF, #E8E8E8); - } - } -} - -.table_tools_segmented_control { - list-style-type: none; - padding: 0; - margin: 0; - - li { - float: left; - - a { - border-width: 1px .5px 1px .5px; - border-radius: 0; - } - - &:first-child a { - border-left-width: 1px; - border-top-left-radius: 12px; - border-bottom-left-radius: 12px; - } - - &:last-child a { - border-right-width: 1px; - border-top-right-radius: 12px; - border-bottom-right-radius: 12px; - } - - &.selected a { - @include gradient(#F0F0F0, #FDFDFD); - box-shadow: 0 1px 1px 0 rgba(0,0,0,0.1) inset; - cursor: default; - - &:hover { - @include gradient(#F0F0F0, #FDFDFD); - } - } - - } -} diff --git a/app/assets/stylesheets/active_admin/components/_tables.scss b/app/assets/stylesheets/active_admin/components/_tables.scss deleted file mode 100644 index 257793d9e64..00000000000 --- a/app/assets/stylesheets/active_admin/components/_tables.scss +++ /dev/null @@ -1,112 +0,0 @@ -// ----------------------------------- Tables - -table tr { - td { - vertical-align: top; - } - - th { - text-align: left; - } -} - -// --------- Index Tables - -table.index_table { - width: 100%; - margin-bottom: 10px; - border: 0; - border-spacing: 0; - - th { - @include section-header; - border-right: none; - padding-left: $cell-horizontal-padding; - padding-right: $cell-horizontal-padding; - - a, a:link, a:visited { - color: $section-header-text-color; - text-decoration: none; - display: block; - white-space: nowrap; - } - - &.sortable a { - background: url($orderable-icon-url) no-repeat 0 4px; padding-left: 13px; - } - - &.sorted-asc a { background-position: 0 -27px; } - &.sorted-desc a { background-position: 0 -56px;} - - &.sorted-asc, &.sorted-desc { - @include gradient(darken($secondary-gradient-start, 5%), darken($secondary-gradient-stop, 5%)); - } - - &:last-child { - border-right: solid 1px #d4d4d4; - } - - } - - tr.even td { background: $table-stripe-color; } - - tr.selected td { - background: $table-selected-color; - } - - td { - padding: 10px $cell-horizontal-padding 8px $cell-horizontal-padding; - border-bottom: 1px solid #e8e8e8; - vertical-align: top; - } -} - -// --------- Tables inside Panels - -.panel_contents table { - margin-top: 5px; - th { - padding-top: 10px; - background: none; - color: $primary-color; - @include no-shadow; - @include text-shadow; - text-transform: uppercase; - border-bottom: 1px solid #ccc; - } - tr.odd td { background: darken($table-stripe-color, 3%); } - tr.even td { background: $table-stripe-color; } -} - -// -------------------------------------- Resource Attributes Table -.attributes_table { overflow: hidden; } - -.attributes_table table { - col.even { background: $table-stripe-color; } - col.odd { background: darken($table-stripe-color, 3%); } - th, td { - padding: 8px $cell-horizontal-padding 6px $cell-horizontal-padding; - vertical-align: top; - border-bottom: 1px solid #e8e8e8; - } - th { - @include no-shadow; - @include no-gradient; - width: 150px; - font-size: 0.9em; - padding-left: 0; - text-transform: uppercase; - color: $primary-color; - @include text-shadow; - } - td { - .empty { - color: #bbb; - font-size: 0.8em; - text-transform: uppercase; - letter-spacing: 0.2em; - } - } -} - -.sidebar_section .attributes_table th { width: 50px; } diff --git a/app/assets/stylesheets/active_admin/components/_tabs.scss b/app/assets/stylesheets/active_admin/components/_tabs.scss deleted file mode 100644 index e212ece7184..00000000000 --- a/app/assets/stylesheets/active_admin/components/_tabs.scss +++ /dev/null @@ -1,65 +0,0 @@ -.ui-tabs-nav { - list-style: none; - display: block; - width: auto; - margin-bottom: -12px; - padding-left: 0; - overflow: auto; - margin-left: 15px; - - li { - display: block; - position: relative; - margin: 0; - padding: 0; - float: left; - - &:first-child a { - border-left-width: 1px; - border-top-left-radius: 12px; - border-bottom-left-radius: 12px; - } - - &:last-child a { - border-right-width: 1px; - border-top-right-radius: 12px; - border-bottom-right-radius: 12px; - } - - a { - @include light-button; - @include gradient(#FFFFFF, #F0F0F0); - @include border-colors(#d9d9d9, #d0d0d0, #c5c5c5); - text-decoration: none; - border-radius: 0; - border-width: 1px .5px 1px .5px; - margin-right: 0; - padding: 4px 14px 4px; - - &:not(.disabled) { - &:hover { - @include gradient(#FFFFFF, #F6F6F6); - } - } - } - - &.ui-tabs-active { - a { - cursor: default; - @include gradient(#F0F0F0, #FDFDFD); - box-shadow: 0 1px 1px 0 rgba(0,0,0,0.1) inset; - - a:hover { - @include gradient(#F0F0F0, #FDFDFD); - } - } - } - } -} - -.tab-content { - border: 1px solid #D3D3D3; - padding: 15px; - padding-top: 30px; - text-align: left; -} diff --git a/app/assets/stylesheets/active_admin/components/_unsupported_browser.scss b/app/assets/stylesheets/active_admin/components/_unsupported_browser.scss deleted file mode 100644 index 7f6f10731c1..00000000000 --- a/app/assets/stylesheets/active_admin/components/_unsupported_browser.scss +++ /dev/null @@ -1,16 +0,0 @@ -.unsupported_browser { - padding: 10px 30px; - color: #211e14; - background-color: #fae692; - @include gradient(#feefae, #fae692); - border-bottom: 1px solid #b3a569; - - h1 { - font-size: 13px; - font-weight: bold; - } - - p { - margin-bottom: 0.5em; - } -} diff --git a/app/assets/stylesheets/active_admin/mixins/_all.scss b/app/assets/stylesheets/active_admin/mixins/_all.scss deleted file mode 100644 index f6f0829e25a..00000000000 --- a/app/assets/stylesheets/active_admin/mixins/_all.scss +++ /dev/null @@ -1,8 +0,0 @@ -@import "./variables"; -@import "./gradients"; -@import "./shadows"; -@import "./rounded"; -@import "./buttons"; -@import "./sections"; -@import "./utilities"; -@import "./typography"; diff --git a/app/assets/stylesheets/active_admin/mixins/_buttons.scss b/app/assets/stylesheets/active_admin/mixins/_buttons.scss deleted file mode 100644 index efb9462d66d..00000000000 --- a/app/assets/stylesheets/active_admin/mixins/_buttons.scss +++ /dev/null @@ -1,65 +0,0 @@ -@mixin basic-button { - @include rounded(200px); - display: inline-block; - font-weight: bold; - font-size: 1.0em; - @include sans-family; - line-height: 12px; - margin-right: 3px; - padding: 7px 16px 6px; - text-decoration: none; - - &.disabled { - opacity: 0.5; - cursor: default; - } - -} - -@mixin default-button { - @include basic-button; - @include gradient(lighten($primary-color, 15%), darken($primary-color, 12%)); - @include text-shadow(#000); - box-shadow: 0 1px 1px rgba(0,0,0,0.10), 0 1px 0 0px rgba(255,255,255, 0.2) inset; - border: solid 1px #484e53; - @include border-colors(#616a71, #484e53, #363b3f); - color: #efefef; - - &:not(.disabled) { - &:hover{ - @include gradient(lighten($primary-color, 18%), darken($primary-color, 9%)); - } - - &:active { - box-shadow: 0 1px 3px rgba(0,0,0,0.40) inset, 0 1px 0 0px #FFF; - @include gradient(lighten($primary-color, 8%), darken($primary-color, 17%)); - } - } -} - -@mixin light-button { - @include basic-button; - @include gradient(#FFFFFF, #E7E7E7); - box-shadow: 0 1px 1px rgba(0,0,0,0.10), 0 1px 0 0 rgba(255,255,255, 0.8) inset; - border: solid 1px #c7c7c7; - @include border-colors(#d3d3d3, #c7c7c7, #c2c2c2); - @include text-shadow; - color: $primary-color; - - &:not(.disabled) { - &:hover { - @include gradient(#FFFFFF, #F1F1F1); - } - - &:active { - box-shadow: 0 1px 2px rgba(0,0,0,0.22) inset, 0 1px 0 0px #EEE; - @include border-colors(#c2c2c2, #b9b9b9, #b7b7b7); - @include gradient(#F3F3F3, #D8D8D8); - } - } - -} - -@mixin dark-button { - @include default-button; -} diff --git a/app/assets/stylesheets/active_admin/mixins/_gradients.scss b/app/assets/stylesheets/active_admin/mixins/_gradients.scss deleted file mode 100644 index ff25348885d..00000000000 --- a/app/assets/stylesheets/active_admin/mixins/_gradients.scss +++ /dev/null @@ -1,28 +0,0 @@ -$secondary-gradient-start: #efefef !default; -$secondary-gradient-stop: #dfe1e2 !default; - -@mixin gradient($start, $end){ - background-color: $start; - background-image: linear-gradient(180deg, $start, $end); -} - -@mixin primary-gradient { - @include gradient(lighten($primary-color, 5%), darken($primary-color, 7%)); - border-bottom: 1px solid darken($primary-color, 11%); -} - -@mixin secondary-gradient { - @include gradient($secondary-gradient-start, $secondary-gradient-stop); -} - -@mixin highlight-gradient { - @include gradient(#75a1c2, #608cb4); -} - -@mixin reverse-highlight-gradient { - @include gradient(#608cb4, #75a1c2); -} - -@mixin no-gradient { - background: none; -} diff --git a/app/assets/stylesheets/active_admin/mixins/_rounded.scss b/app/assets/stylesheets/active_admin/mixins/_rounded.scss deleted file mode 100644 index 4e8201d4f62..00000000000 --- a/app/assets/stylesheets/active_admin/mixins/_rounded.scss +++ /dev/null @@ -1,22 +0,0 @@ -@mixin rounded($radius: 3px) { - border-radius: $radius; -} - -@mixin rounded-all($top-left:3px, $top-right:3px, $bottom-right:3px, $bottom-left:3px) { - border-top-right-radius: $top-right; - border-top-left-radius: $top-left; - border-bottom-right-radius: $bottom-right; - border-bottom-left-radius: $bottom-left; -} - -@mixin rounded-top($radius: 3px) { - @include rounded(0); - border-top-right-radius: $radius; - border-top-left-radius: $radius; -} - -@mixin rounded-bottom($radius: 3px) { - @include rounded(0); - border-bottom-right-radius: $radius; - border-bottom-left-radius: $radius; -} diff --git a/app/assets/stylesheets/active_admin/mixins/_sections.scss b/app/assets/stylesheets/active_admin/mixins/_sections.scss deleted file mode 100644 index f34d78b0639..00000000000 --- a/app/assets/stylesheets/active_admin/mixins/_sections.scss +++ /dev/null @@ -1,41 +0,0 @@ -@mixin section-header { - @include secondary-gradient; - @include text-shadow; - border: solid 1px #cdcdcd; - @include border-colors(#e6e6e6, #d4d4d4, #cdcdcd); - box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 0 1px #FFF inset; - - font-size: 1em; - font-weight: bold; - line-height: 18px; - margin-bottom: 0.5em; - color: $section-header-text-color; - - padding: 5px 10px 3px 10px; -} - -@mixin section-background { - background: #f4f4f4; - @include rounded(4px); - @include inset-shadow(0,1px,4px, #ddd); -} - -@mixin section { - @include section-background; - margin-bottom: 20px; - - > h3 { - @include section-header; - - .header_action { - float: right; - } - } - - > div { padding: 3px $section-padding $section-padding $section-padding; } - - hr { - border: none; - border-bottom: 1px solid #E8E8E8; - } -} diff --git a/app/assets/stylesheets/active_admin/mixins/_shadows.scss b/app/assets/stylesheets/active_admin/mixins/_shadows.scss deleted file mode 100644 index e366bf0ad46..00000000000 --- a/app/assets/stylesheets/active_admin/mixins/_shadows.scss +++ /dev/null @@ -1,15 +0,0 @@ -@mixin shadow($x: 0, $y: 1px, $blur: 2px, $color: rgba(0,0,0,0.37)) { - box-shadow: $x $y $blur $color; -} - -@mixin no-shadow { - box-shadow: none; -} - -@mixin inset-shadow($x: 0, $y: 1px, $blur: 2px, $color: #aaa) { - box-shadow: inset $x $y $blur $color; -} - -@mixin text-shadow($color: #fff, $x: 0, $y: 1px, $blur: 0) { - text-shadow: $color $x $y $blur; -} diff --git a/app/assets/stylesheets/active_admin/mixins/_typography.scss b/app/assets/stylesheets/active_admin/mixins/_typography.scss deleted file mode 100644 index 8ec668216ff..00000000000 --- a/app/assets/stylesheets/active_admin/mixins/_typography.scss +++ /dev/null @@ -1,3 +0,0 @@ -@mixin sans-family { - font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; -} diff --git a/app/assets/stylesheets/active_admin/mixins/_utilities.scss b/app/assets/stylesheets/active_admin/mixins/_utilities.scss deleted file mode 100644 index bf42a8d2d08..00000000000 --- a/app/assets/stylesheets/active_admin/mixins/_utilities.scss +++ /dev/null @@ -1,17 +0,0 @@ -@mixin clearfix { - &:after { - visibility: hidden; - display: block; - content: ""; - clear: both; - height: 0; - } -} - -@mixin border-colors($top, $sides, $bottom) { - border-color: $sides; - border-top-color: $top; - border-right-color: $sides; - border-bottom-color: $bottom; - border-left-color: $sides; -} diff --git a/app/assets/stylesheets/active_admin/mixins/_variables.scss b/app/assets/stylesheets/active_admin/mixins/_variables.scss deleted file mode 100644 index 8d0832a29bc..00000000000 --- a/app/assets/stylesheets/active_admin/mixins/_variables.scss +++ /dev/null @@ -1,51 +0,0 @@ -// Variables used throughout Active Admin. -// They can be overridden by prepending your own -// to 'app/assets/stylesheets/active_admin.scss'. - -// Images -$menu-arrow-light-icon-url: 'data:image/png;base64,R0lGODlhBwAEAKIAAL6+vry8vIiIiJWVlf///3t7ewAAAAAAACH5BAEAAAUALAAAAAAHAAQAAAMLWLol80MoF5mQKgEAOw==' !default; -$menu-arrow-dark-icon-url: 'data:image/png;base64,R0lGODlhBwAEAKIAAG1tbWxsbElJSVBQUP///0JCQgAAAAAAACH5BAEAAAUALAAAAAAHAAQAAAMLWLol80MoF5mQKgEAOw==' !default; -$menu-arrow-right-light-icon-url: 'data:image/gif;base64,R0lGODlhBAAHAKECAKqqqszMzPkVFfkVFSH+EUNyZWF0ZWQgd2l0aCBHSU1QACH5BAEKAAIALAAAAAAEAAcAAAIJlA0XKbH9nmAFADs=' !default; -$menu-arrow-right-dark-icon-url: 'data:image/gif;base64,R0lGODlhBAAHAMIEAG1tbWxsbElJSVBQUPkVFfkVFfkVFfkVFSH+EUNyZWF0ZWQgd2l0aCBHSU1QACH5BAEKAAEALAAAAAAEAAcAAAMKGKqy02G8OGeACQA7' !default; - -$orderable-icon-url: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAABGCAYAAAAAVo4aAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAH5JREFUeNpi3LhlOwMU1AExGxDXwARYoHQLEFdD2cxAXAliMKFJgEAFEHfBJEHGMKLhMpgkTsAEdch/NNwCk2xCdiEQtML4LEgCf6EubUX3Cgh0oNvJ+P//f7wOGpUclRwYSZb41CyidNbB8giNM+9oXhmVHHm5bJjUSAABBgDKKiwMMUxPwgAAAABJRU5ErkJggg==' !default; - -// Colors -$body-background-color: #FFF !default; -$primary-color: #5E6469 !default; -$secondary-color: #f0f0f0 !default; -$text-color: #323537 !default; -$link-color: #38678b !default; -$section-header-text-color: $primary-color !default; -$current-menu-item-background: lighten($primary-color, 12%) !default; -$hover-menu-item-background: lighten($primary-color, 12%) !default; -$table-stripe-color: lighten($primary-color, 57%) !default; -$table-selected-color: #d9e4ec !default; -$error-color: #932419 !default; -$blank-slate-primary-color: #AAA !default; -$breadcrumbs-color: #8a949e !default; -$breadcrumbs-separator-color: #aab2ba !default; -$required-field-marker-color: #aaa !default; -$form-label-color: $section-header-text-color !default; -$page-header-text-color: #cdcdcd !default; - -// Sizes -$border-width: 1px !default; -$horizontal-page-margin: 30px !default; -$sidebar-width: 270px !default; -$cell-padding: 5px 10px 3px 10px !default; -$cell-horizontal-padding: 12px !default; -$section-padding: 15px !default; -$text-input-horizontal-padding: 10px !default; -$text-input-total-padding: $text-input-horizontal-padding * 2 + $border-width * 2; - -$blank-slate-border: 1px dashed #DADADA !default; - -// Sidebar Forms -$sidebar-inner-content-width: $sidebar-width - ($section-padding * 2); - -// Filter Forms -$filter-field-seperator-width: 12px !default; -$side-by-side-filter-input-width: ($sidebar-inner-content-width * 0.5) - ($text-input-horizontal-padding * 2) - $filter-field-seperator-width; -$side-by-side-filter-select-width: ($sidebar-inner-content-width * 0.5) - $filter-field-seperator-width; -$date-range-filter-input-width: ($sidebar-inner-content-width * 0.5) - ($filter-field-seperator-width * 0.5); diff --git a/app/assets/stylesheets/active_admin/pages/_logged_out.scss b/app/assets/stylesheets/active_admin/pages/_logged_out.scss deleted file mode 100644 index cb8c3b58673..00000000000 --- a/app/assets/stylesheets/active_admin/pages/_logged_out.scss +++ /dev/null @@ -1,44 +0,0 @@ -body.logged_out { - background: #e8e9ea; - - #content_wrapper{ - width: 500px; - margin: 70px auto; - #active_admin_content { - @include shadow; - background: #fff; - padding: 13px 30px; - } - } - - h2 { - @include section-header; - @include primary-gradient; - @include text-shadow(#000); - box-shadow: 0 1px 3px rgba(0,0,0,0.3); - border: none; - color: #fff; - margin: -13px -30px 20px -30px; - } - - #login { - /* Login Form */ - form { - fieldset { - @include no-shadow; - background: none; - padding: 0; - li { padding: 10px 0; } - - input[type=text], input[type=email], input[type=password] { - width: 70%; - } - &.buttons { margin-left: 20%; } - margin-bottom: 0; - } - } - - a { float: right; margin-top: -32px; } - } - -} diff --git a/app/assets/stylesheets/active_admin/structure/_footer.scss b/app/assets/stylesheets/active_admin/structure/_footer.scss deleted file mode 100644 index f387d207e1d..00000000000 --- a/app/assets/stylesheets/active_admin/structure/_footer.scss +++ /dev/null @@ -1,14 +0,0 @@ -#footer { - padding: 30px 30px; - font-size: 0.8em; - clear: both; - - p { - padding-top: 10px - } -} - -// -------------------------------------- Index Footer (Under Table) -#index_footer { padding-top: 5px; text-align: right; font-size: 0.85em; } - -.index_content { clear: both; } diff --git a/app/assets/stylesheets/active_admin/structure/_main_structure.scss b/app/assets/stylesheets/active_admin/structure/_main_structure.scss deleted file mode 100644 index 006bdf0b6c4..00000000000 --- a/app/assets/stylesheets/active_admin/structure/_main_structure.scss +++ /dev/null @@ -1,29 +0,0 @@ -#wrapper { - width: 100%; -} - -.index #wrapper { - display: table; -} - -#active_admin_content { - margin: 0; - padding: $horizontal-page-margin; - - #main_content_wrapper { - float: left; - width: 100%; - - #main_content{ - margin-right: $sidebar-width + ($section-padding * 2); - } - } - - &.without_sidebar #main_content_wrapper #main_content{ margin-right: 0; } - - #sidebar { - float: left; - width: $sidebar-width; - margin-left: -$sidebar-width; - } -} diff --git a/app/assets/stylesheets/active_admin/structure/_title_bar.scss b/app/assets/stylesheets/active_admin/structure/_title_bar.scss deleted file mode 100644 index f44d77d7ad4..00000000000 --- a/app/assets/stylesheets/active_admin/structure/_title_bar.scss +++ /dev/null @@ -1,40 +0,0 @@ -#title_bar { - @include section-header; - @include clearfix; - box-shadow: 0 1px 2px rgba(0, 0, 0, 0.37); - display: table; - border-bottom-color: #EEE; - width: 100%; - position: relative; - margin: 0; - padding: 10px $horizontal-page-margin; - z-index: 800; - - #titlebar_left, #titlebar_right { - height: 50px; - vertical-align: middle; - display: table-cell; - } - - #titlebar_right { - text-align: right; - } - - h2 { - margin: 0; - padding: 0; - font-size: 2.6em; - line-height: 100%; - font-weight: bold; - } - - .action_items { - span.action_item { - & > a, & > .dropdown_menu > a { - @include light-button; - padding: 12px 17px 10px; - margin: 0px; - } - } - } -} diff --git a/lib/active_admin/base_controller.rb b/app/controllers/active_admin/base_controller.rb similarity index 62% rename from lib/active_admin/base_controller.rb rename to app/controllers/active_admin/base_controller.rb index fe8c66d4d4f..c2dd73ffaa0 100644 --- a/lib/active_admin/base_controller.rb +++ b/app/controllers/active_admin/base_controller.rb @@ -1,14 +1,17 @@ # frozen_string_literal: true -require "active_admin/base_controller/authorization" -require "active_admin/base_controller/menu" - module ActiveAdmin # BaseController for ActiveAdmin. # It implements ActiveAdmin controllers core features. class BaseController < ::InheritedResources::Base - helper ::ActiveAdmin::ViewHelpers + helper MethodOrProcHelper + helper LayoutHelper + helper FormHelper + helper BreadcrumbHelper + helper AutoLinkHelper + helper DisplayHelper + helper IndexHelper - layout :determine_active_admin_layout + layout "active_admin" before_action :only_render_implemented_actions before_action :authenticate_active_admin_user @@ -22,8 +25,8 @@ class << self attr_accessor :active_admin_config end - include Authorization - include Menu + include BaseController::Authorization + include BaseController::Menu private @@ -61,22 +64,47 @@ def active_admin_namespace ACTIVE_ADMIN_ACTIONS = [:index, :show, :new, :create, :edit, :update, :destroy] - # Determine which layout to use. - # - # 1. If we're rendering a standard Active Admin action, we want layout(false) - # because these actions are subclasses of the Base page (which implements - # all the required layout code) - # 2. If we're rendering a custom action, we'll use the active_admin layout so - # that users can render any template inside Active Admin. - def determine_active_admin_layout - ACTIVE_ADMIN_ACTIONS.include?(params[:action].to_sym) ? false : "active_admin" - end - def active_admin_root controller, action = active_admin_namespace.root_to.split "#" { controller: controller, action: action } end + def page_presenter + active_admin_config.get_page_presenter(params[:action].to_sym) || default_page_presenter + end + helper_method :page_presenter + + def default_page_presenter + PagePresenter.new + end + + def page_title + if page_presenter[:title] + helpers.render_or_call_method_or_proc_on(self, page_presenter[:title]) + else + default_page_title + end + end + helper_method :page_title + + def default_page_title + active_admin_config.name + end + + DEFAULT_DOWNLOAD_FORMATS = [:csv, :xml, :json] + + def build_download_formats(download_links) + download_links = instance_exec(&download_links) if download_links.is_a?(Proc) + if download_links.is_a?(Array) && !download_links.empty? + download_links + elsif download_links == false + [] + else + DEFAULT_DOWNLOAD_FORMATS + end + end + helper_method :build_download_formats + ActiveSupport.run_load_hooks(:active_admin_controller, self) end end diff --git a/lib/active_admin/base_controller/authorization.rb b/app/controllers/active_admin/base_controller/authorization.rb similarity index 96% rename from lib/active_admin/base_controller/authorization.rb rename to app/controllers/active_admin/base_controller/authorization.rb index 55b7d66de86..f4c036f8f39 100644 --- a/lib/active_admin/base_controller/authorization.rb +++ b/app/controllers/active_admin/base_controller/authorization.rb @@ -7,9 +7,9 @@ module Authorization ACTIONS_DICTIONARY = { index: ActiveAdmin::Authorization::READ, show: ActiveAdmin::Authorization::READ, - new: ActiveAdmin::Authorization::CREATE, + new: ActiveAdmin::Authorization::NEW, create: ActiveAdmin::Authorization::CREATE, - edit: ActiveAdmin::Authorization::UPDATE, + edit: ActiveAdmin::Authorization::EDIT, update: ActiveAdmin::Authorization::UPDATE, destroy: ActiveAdmin::Authorization::DESTROY } @@ -40,7 +40,7 @@ def authorized?(action, subject = nil) end # Authorize the action and subject. Available in the controller - # as well as all the views. If the action is not allowd, it raises + # as well as all the views. If the action is not allowed, it raises # an ActiveAdmin::AccessDenied exception. # # @param [Symbol] action The action to check if the user has permission diff --git a/app/controllers/active_admin/base_controller/menu.rb b/app/controllers/active_admin/base_controller/menu.rb new file mode 100644 index 00000000000..2cc3180fa69 --- /dev/null +++ b/app/controllers/active_admin/base_controller/menu.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true +module ActiveAdmin + class BaseController < ::InheritedResources::Base + module Menu + extend ActiveSupport::Concern + + included do + before_action :set_current_menu_item + + helper_method :current_menu + helper_method :current_menu_item? + end + + protected + + def current_menu + active_admin_config.navigation_menu + end + + def current_menu_item?(item) + item.current?(@current_menu_item) + end + + def set_current_menu_item + @current_menu_item = if current_menu && active_admin_config.belongs_to? && parent? + parent_item = active_admin_config.belongs_to_config.target.menu_item + if current_menu.include? parent_item + parent_item + else + active_admin_config.menu_item + end + else + active_admin_config.menu_item + end + end + + end + end +end diff --git a/lib/active_admin/page_controller.rb b/app/controllers/active_admin/page_controller.rb similarity index 87% rename from lib/active_admin/page_controller.rb rename to app/controllers/active_admin/page_controller.rb index 2159b684ee4..e9979e53b43 100644 --- a/lib/active_admin/page_controller.rb +++ b/app/controllers/active_admin/page_controller.rb @@ -15,10 +15,6 @@ def index(options = {}, &block) render "active_admin/page/index" end - def clear_page_actions! - active_admin_config.clear_page_actions! - end - private def authorize_access! diff --git a/app/controllers/active_admin/resource_controller.rb b/app/controllers/active_admin/resource_controller.rb new file mode 100644 index 00000000000..bedd3483f1c --- /dev/null +++ b/app/controllers/active_admin/resource_controller.rb @@ -0,0 +1,102 @@ +# frozen_string_literal: true +require "active_admin/collection_decorator" + +module ActiveAdmin + # All Resources Controller inherits from this controller. + # It implements actions and helpers for resources. + class ResourceController < BaseController + respond_to :html, :xml, :json + respond_to :csv, only: :index + + before_action :restrict_download_format_access!, only: [:index, :show] + + include ResourceController::ActionBuilder + include ResourceController::Decorators + include ResourceController::DataAccess + include ResourceController::PolymorphicRoutes + include ResourceController::Scoping + include ResourceController::Streaming + extend ResourceClassMethods + + def self.active_admin_config=(config) + if @active_admin_config = config + defaults resource_class: config.resource_class, + route_prefix: config.route_prefix, + instance_name: config.resource_name.singular + end + end + + # Inherited Resources uses the `self.inherited(base)` hook to add + # in `self.resource_class`. To override it, we need to install + # our resource_class method each time we're inherited from. + def self.inherited(base) + super(base) + base.override_resource_class_methods! + end + + private + + def page_presenter + case params[:action].to_sym + when :index + active_admin_config.get_page_presenter(params[:action], params[:as]) + when :new, :edit, :create, :update + active_admin_config.get_page_presenter(:form) + end || super + end + + def default_page_presenter + case params[:action].to_sym + when :index + PagePresenter.new(as: :table) + when :new, :edit + PagePresenter.new + end || super + end + + def page_title + if page_presenter[:title] + case params[:action].to_sym + when :index + case page_presenter[:title] + when Symbol, Proc + instance_exec(&page_presenter[:title]) + else + page_presenter[:title] + end + else + helpers.render_or_call_method_or_proc_on(resource, page_presenter[:title]) + end + else + default_page_title + end + end + + def default_page_title + case params[:action].to_sym + when :index + active_admin_config.plural_resource_label + when :show + helpers.display_name(resource) + when :new, :edit, :create, :update + normalized_action = params[:action] + normalized_action = 'new' if normalized_action == 'create' + normalized_action = 'edit' if normalized_action == 'update' + + ActiveAdmin::Localizers.resource(active_admin_config).t("#{normalized_action}_model") + else + I18n.t("active_admin.#{params[:action]}", default: params[:action].to_s.titleize) + end + end + + def restrict_download_format_access! + unless request.format.html? + presenter = active_admin_config.get_page_presenter(:index) + download_formats = (presenter || {}).fetch(:download_links, active_admin_config.namespace.download_links) + unless build_download_formats(download_formats).include?(request.format.symbol) + raise ActiveAdmin::AccessDenied.new(current_active_admin_user, :index) + end + end + end + end +end diff --git a/lib/active_admin/resource_controller/action_builder.rb b/app/controllers/active_admin/resource_controller/action_builder.rb similarity index 87% rename from lib/active_admin/resource_controller/action_builder.rb rename to app/controllers/active_admin/resource_controller/action_builder.rb index cd3c768bcd1..bc85be26849 100644 --- a/lib/active_admin/resource_controller/action_builder.rb +++ b/app/controllers/active_admin/resource_controller/action_builder.rb @@ -20,7 +20,7 @@ def clear_collection_actions! private def remove_action_methods(actions_type) - active_admin_config.public_send("#{actions_type}_actions").each do |action| + active_admin_config.public_send(:"#{actions_type}_actions").each do |action| remove_method action.name end end diff --git a/lib/active_admin/resource_controller/data_access.rb b/app/controllers/active_admin/resource_controller/data_access.rb similarity index 89% rename from lib/active_admin/resource_controller/data_access.rb rename to app/controllers/active_admin/resource_controller/data_access.rb index f075eedc287..18f08b53cff 100644 --- a/lib/active_admin/resource_controller/data_access.rb +++ b/app/controllers/active_admin/resource_controller/data_access.rb @@ -57,7 +57,7 @@ def collection def find_collection(options = {}) collection = scoped_collection collection_applies(options).each do |applyer| - collection = send("apply_#{applyer}", collection) + collection = send(:"apply_#{applyer}", collection) end collection end @@ -168,11 +168,16 @@ def save_resource(object) # # @return [void] def update_resource(object, attributes) - object = assign_attributes(object, attributes) - - run_update_callbacks object do - save_resource(object) + status = nil + ActiveRecord::Base.transaction do + object = assign_attributes(object, attributes) + + run_update_callbacks object do + status = save_resource(object) + raise ActiveRecord::Rollback unless status + end end + status end # Destroys an object from the database and calls appropriate callbacks. @@ -215,7 +220,7 @@ def apply_sorting(chain) # Applies any Ransack search methods to the currently scoped collection. # Both `search` and `ransack` are provided, but we use `ransack` to prevent conflicts. def apply_filtering(chain) - @search = chain.ransack(params[:q] || {}) + @search = chain.ransack(params[:q] || {}, auth_object: active_admin_authorization) @search.result end @@ -231,7 +236,7 @@ def apply_scoping(chain) def apply_includes(chain) if active_admin_config.includes.any? - chain.includes *active_admin_config.includes + chain.includes(*active_admin_config.includes) else chain end @@ -250,12 +255,13 @@ def current_scope end def apply_pagination(chain) + # skip pagination if CSV format was requested + return chain if params["format"] == "csv" # skip pagination if already was paginated by scope return chain if chain.respond_to?(:total_pages) - page_method_name = Kaminari.config.page_method_name page = params[Kaminari.config.param_name] - chain.public_send(page_method_name, page).per(per_page) + paginate(chain, page, per_page) end def collection_applies(options = {}) @@ -265,6 +271,16 @@ def collection_applies(options = {}) COLLECTION_APPLIES & only - except end + def in_paginated_batches(&block) + ActiveRecord::Base.uncached do + (1..paginated_collection.total_pages).each do |page| + paginated_collection(page).each do |resource| + yield apply_decorator(resource) + end + end + end + end + def per_page if active_admin_config.paginate dynamic_per_page || configured_per_page @@ -318,6 +334,20 @@ def smart_resource_url def create_another? params[:create_another].present? end + + def paginated_collection(page_no = 1) + paginate(collection, page_no, batch_size) + end + + def paginate(chain, page, per_page) + page_method_name = Kaminari.config.page_method_name + + chain.public_send(page_method_name, page).per(per_page) + end + + def batch_size + 1000 + end end end end diff --git a/lib/active_admin/resource_controller/decorators.rb b/app/controllers/active_admin/resource_controller/decorators.rb similarity index 96% rename from lib/active_admin/resource_controller/decorators.rb rename to app/controllers/active_admin/resource_controller/decorators.rb index 59243364d66..a916cb24b12 100644 --- a/lib/active_admin/resource_controller/decorators.rb +++ b/app/controllers/active_admin/resource_controller/decorators.rb @@ -61,7 +61,7 @@ def self.wrap(decorator) def self.wrap!(parent, name) ::Class.new parent do delegate :reorder, :page, :current_page, :total_pages, :limit_value, - :total_count, :total_pages, :offset, :to_key, :group_values, + :total_count, :offset, :to_key, :group_values, :except, :find_each, :ransack, to: :object define_singleton_method(:name) { name } diff --git a/lib/active_admin/resource_controller/polymorphic_routes.rb b/app/controllers/active_admin/resource_controller/polymorphic_routes.rb similarity index 100% rename from lib/active_admin/resource_controller/polymorphic_routes.rb rename to app/controllers/active_admin/resource_controller/polymorphic_routes.rb diff --git a/lib/active_admin/resource_controller/resource_class_methods.rb b/app/controllers/active_admin/resource_controller/resource_class_methods.rb similarity index 100% rename from lib/active_admin/resource_controller/resource_class_methods.rb rename to app/controllers/active_admin/resource_controller/resource_class_methods.rb diff --git a/lib/active_admin/resource_controller/scoping.rb b/app/controllers/active_admin/resource_controller/scoping.rb similarity index 91% rename from lib/active_admin/resource_controller/scoping.rb rename to app/controllers/active_admin/resource_controller/scoping.rb index a4446e1955c..19e97206567 100644 --- a/lib/active_admin/resource_controller/scoping.rb +++ b/app/controllers/active_admin/resource_controller/scoping.rb @@ -16,7 +16,7 @@ module Scoping # Collection can be scoped conditionally with an :if or :unless proc. def begin_of_association_chain return nil unless active_admin_config.scope_to?(self) - MethodOrProcHelper.render_in_context(self, active_admin_config.scope_to_method) + helpers.render_in_context(self, active_admin_config.scope_to_method) end # Overriding from InheritedResources::BaseHelpers diff --git a/lib/active_admin/resource_controller/streaming.rb b/app/controllers/active_admin/resource_controller/streaming.rb similarity index 73% rename from lib/active_admin/resource_controller/streaming.rb rename to app/controllers/active_admin/resource_controller/streaming.rb index 144f5e5a99c..efaf58c0812 100644 --- a/lib/active_admin/resource_controller/streaming.rb +++ b/app/controllers/active_admin/resource_controller/streaming.rb @@ -24,20 +24,20 @@ def stream_resource(&block) headers["Last-Modified"] = Time.current.httpdate if ActiveAdmin.application.disable_streaming_in.include? Rails.env - self.response_body = block[String.new] + self.response_body = block[String.new] # rubocop:disable Performance/UnfreezeString to preserve encoding else - self.response_body = Enumerator.new &block + self.response_body = Enumerator.new(&block) end end def csv_filename - "#{resource_collection_name.to_s.gsub('_', '-')}-#{Time.zone.now.to_date.to_formatted_s(:default)}.csv" + "#{resource_collection_name.to_s.tr('_', '-')}-#{Time.zone.now.to_date.to_s}.csv" end def stream_csv headers["Content-Type"] = "text/csv; charset=utf-8" # In Rails 5 it's set to HTML?? headers["Content-Disposition"] = %{attachment; filename="#{csv_filename}"} - stream_resource &active_admin_config.csv_builder.method(:build).to_proc.curry[self] + stream_resource(&active_admin_config.csv_builder.method(:build).to_proc.curry[self]) end end diff --git a/app/helpers/active_admin/auto_link_helper.rb b/app/helpers/active_admin/auto_link_helper.rb new file mode 100644 index 00000000000..ed3e7b823cb --- /dev/null +++ b/app/helpers/active_admin/auto_link_helper.rb @@ -0,0 +1,66 @@ +# frozen_string_literal: true +module ActiveAdmin + module AutoLinkHelper + # Automatically links objects to their resource controllers. If + # the resource has not been registered, a string representation of + # the object is returned. + # + # The default content in the link is returned from ActiveAdmin::DisplayHelper#display_name + # + # You can pass in the content to display + # eg: auto_link(@post, "My Link") + # + def auto_link(resource, content = display_name(resource), **html_options) + if url = auto_url_for(resource) + link_to content, url, html_options + else + content + end + end + + # Like `auto_link`, except that it only returns a URL for the resource + def auto_url_for(resource) + config = active_admin_resource_for(resource.class) + return unless config + + if config.controller.action_methods.include?("show") && + authorized?(ActiveAdmin::Auth::READ, resource) + url_for config.route_instance_path resource, url_options + elsif config.controller.action_methods.include?("edit") && + authorized?(ActiveAdmin::Auth::EDIT, resource) + url_for config.route_edit_instance_path resource, url_options + end + end + + def new_action_authorized?(resource_or_class) + controller.action_methods.include?("new") && authorized?(ActiveAdmin::Auth::NEW, resource_or_class) + end + + def show_action_authorized?(resource_or_class) + controller.action_methods.include?("show") && authorized?(ActiveAdmin::Auth::READ, resource_or_class) + end + + def edit_action_authorized?(resource_or_class) + controller.action_methods.include?("edit") && authorized?(ActiveAdmin::Auth::EDIT, resource_or_class) + end + + def destroy_action_authorized?(resource_or_class) + controller.action_methods.include?("destroy") && authorized?(ActiveAdmin::Auth::DESTROY, resource_or_class) + end + + def auto_logout_link_path + render_or_call_method_or_proc_on(self, active_admin_namespace.logout_link_path) + end + + private + + # Returns the ActiveAdmin::Resource instance for a class + # While `active_admin_namespace` is a helper method, this method seems + # to exist to otherwise resolve failed component specs using mock_action_view. + def active_admin_resource_for(klass) + if respond_to? :active_admin_namespace + active_admin_namespace.resource_for klass + end + end + end +end diff --git a/app/helpers/active_admin/breadcrumb_helper.rb b/app/helpers/active_admin/breadcrumb_helper.rb new file mode 100644 index 00000000000..c7fbd209b96 --- /dev/null +++ b/app/helpers/active_admin/breadcrumb_helper.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true +module ActiveAdmin + module BreadcrumbHelper + ID_FORMAT_REGEXP = /\A(\d+|[a-f0-9]{24}|(?:[a-f0-9]{8}-(?:[a-f0-9]{4}-){3}[a-f0-9]{12}))\z/.freeze + + # Returns an array of links to use in a breadcrumb + def build_breadcrumb_links(path = request.path, html_options = {}) + config = active_admin_config.breadcrumb + if config.is_a?(Proc) + instance_exec(controller, &config) + elsif config.present? + default_breadcrumb_links(path, html_options) + end + end + + def default_breadcrumb_links(path, html_options = {}) + # remove leading "/" and split up the URL + # and remove last since it's used as the page title + parts = path.split("/").select(&:present?)[0..-2] + + parts.each_with_index.map do |part, index| + # 1. try using `display_name` if we can locate a DB object + # 2. try using the model name translation + # 3. default to calling `titlecase` on the URL fragment + if ID_FORMAT_REGEXP.match?(part) && parts[index - 1] + parent = active_admin_config.belongs_to_config.try :target + config = parent && parent.resource_name.route_key == parts[index - 1] ? parent : active_admin_config + name = display_name config.find_resource part + end + name ||= I18n.t "activerecord.models.#{part.singularize}", count: 2.1, default: part.titlecase + + # Don't create a link if the resource's show action is disabled + if !config || config.defined_actions.include?(:show) + link_to name, "/" + parts[0..index].join("/"), html_options + else + name + end + end + end + end +end diff --git a/app/helpers/active_admin/display_helper.rb b/app/helpers/active_admin/display_helper.rb new file mode 100644 index 00000000000..2e2b87575f5 --- /dev/null +++ b/app/helpers/active_admin/display_helper.rb @@ -0,0 +1,117 @@ +# frozen_string_literal: true +module ActiveAdmin + module DisplayHelper + DISPLAY_NAME_FALLBACK = -> { + klass = self.class + name = if klass.respond_to?(:model_name) + if klass.respond_to?(:primary_key) + "#{klass.model_name.human} ##{send(klass.primary_key)}" + else + klass.model_name.human + end + elsif klass.respond_to?(:primary_key) + " ##{send(klass.primary_key)}" + end + name.present? ? name : to_s + } + + def DISPLAY_NAME_FALLBACK.inspect + "DISPLAY_NAME_FALLBACK" + end + + # Attempts to call any known display name methods on the resource. + # See the setting in `application.rb` for the list of methods and their priority. + def display_name(resource) + unless resource.nil? + result = render_in_context(resource, display_name_method_for(resource)) + if result.to_s.strip.present? + ERB::Util.html_escape(result) + else + ERB::Util.html_escape(render_in_context(resource, DISPLAY_NAME_FALLBACK)) + end + end + end + + def format_attribute(resource, attr) + value = find_value resource, attr + + if value.is_a?(Arbre::Element) + value + elsif boolean_attr?(resource, attr, value) + Arbre::Context.new { status_tag value } + else + pretty_format value + end + end + + # Attempts to create a human-readable string for any object + def pretty_format(object) + case object + when String, Numeric, Symbol, Arbre::Element + object.to_s + when Date, Time + I18n.localize object, format: active_admin_application.localize_format + when Array + format_collection(object) + else + if defined?(::ActiveRecord) && object.is_a?(ActiveRecord::Base) || + defined?(::Mongoid) && object.class.include?(Mongoid::Document) + auto_link object + elsif defined?(::ActiveRecord) && object.is_a?(ActiveRecord::Relation) + format_collection(object) + else + display_name object + end + end + end + + private + + # Looks up and caches the first available display name method. + # To prevent conflicts, we exclude any methods that happen to be associations. + # If no methods are available and we're about to use the Kernel's `to_s`, provide our own. + def display_name_method_for(resource) + @@display_name_methods_cache ||= {} + @@display_name_methods_cache[resource.class] ||= begin + methods = active_admin_application.display_name_methods - association_methods_for(resource) + method = methods.detect { |method| resource.respond_to? method } + + if method != :to_s || resource.method(method).source_location + method + else + DISPLAY_NAME_FALLBACK + end + end + end + + def association_methods_for(resource) + return [] unless resource.class.respond_to? :reflect_on_all_associations + resource.class.reflect_on_all_associations.map(&:name) + end + + def find_value(resource, attr) + if attr.is_a? Proc + attr.call resource + elsif resource.respond_to? attr + resource.public_send attr + elsif resource.respond_to? :[] + resource[attr] + end + end + + def format_collection(collection) + safe_join(collection.map { |item| pretty_format(item) }, ", ") + end + + def boolean_attr?(resource, attr, value) + case value + when TrueClass, FalseClass + true + else + if resource.class.respond_to? :attribute_types + resource.class.attribute_types[attr.to_s].is_a?(ActiveModel::Type::Boolean) + end + end + end + end +end diff --git a/app/helpers/active_admin/form_helper.rb b/app/helpers/active_admin/form_helper.rb new file mode 100644 index 00000000000..e9a0b391cc0 --- /dev/null +++ b/app/helpers/active_admin/form_helper.rb @@ -0,0 +1,95 @@ +# frozen_string_literal: true +module ActiveAdmin + module FormHelper + RESERVED_PARAMS = %w(controller action commit utf8).freeze + + def active_admin_form_for(resource, options = {}, &block) + Arbre::Context.new({}, self) do + active_admin_form_for resource, options, &block + end.content + end + + def hidden_field_tags_for(params, options = {}) + fields_for_params(params.to_unsafe_hash, options).map do |kv| + k, v = kv.first + hidden_field_tag k, v, id: sanitize_to_id("hidden_active_admin_#{k}") + end.join("\n").html_safe + end + + # Flatten a params Hash to an array of fields. + # + # @param params [Hash] + # @param options [Hash] :namespace and :except + # + # @return [Array] of [Hash] with one element. + # + # @example + # fields_for_params(scope: "all", users: ["greg"]) + # => [ {"scope" => "all"} , {"users[]" => "greg"} ] + # + def fields_for_params(params, options = {}) + namespace = options[:namespace] + except = Array.wrap(options[:except]).map(&:to_s) + + params.flat_map do |k, v| + next if namespace.nil? && RESERVED_PARAMS.include?(k.to_s) + next if except.include?(k.to_s) + + if namespace + k = "#{namespace}[#{k}]" + end + + case v + when String, TrueClass, FalseClass + { k => v } + when Symbol + { k => v.to_s } + when Hash + fields_for_params(v, namespace: k) + when Array + v.map do |v| + { "#{k}[]" => v } + end + when nil + { k => "" } + else + raise TypeError, "Cannot convert #{v.class} value: #{v.inspect}" + end + end.compact + end + + # Helper method to render a filter form + def active_admin_filters_form_for(search, filters, options = {}) + defaults = { builder: ActiveAdmin::Filters::FormBuilder, url: collection_path, html: { class: "filters-form" } } + required = { html: { method: :get }, as: :q } + options = defaults.deep_merge(options).deep_merge(required) + + form_for search, options do |f| + f.template.concat hidden_field_tags_for(params, except: except_hidden_fields) + + filters.each do |attribute, opts| + next if opts.key?(:if) && !call_method_or_proc_on(self, opts[:if]) + next if opts.key?(:unless) && call_method_or_proc_on(self, opts[:unless]) + + filter_opts = opts.except(:if, :unless) + filter_opts[:input_html] = instance_exec(&filter_opts[:input_html]) if filter_opts[:input_html].is_a?(Proc) + + f.filter attribute, filter_opts + end + + buttons = content_tag :div, class: "filters-form-buttons" do + f.submit(I18n.t("active_admin.filters.buttons.filter"), class: "filters-form-submit") + + link_to(I18n.t("active_admin.filters.buttons.clear"), collection_path, class: "filters-form-clear") + end + + f.template.concat buttons + end + end + + private + + def except_hidden_fields + [:q, :page] + end + end +end diff --git a/app/helpers/active_admin/index_helper.rb b/app/helpers/active_admin/index_helper.rb new file mode 100644 index 00000000000..013201d37a0 --- /dev/null +++ b/app/helpers/active_admin/index_helper.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true +module ActiveAdmin + module IndexHelper + def scope_name(scope) + case scope.name + when Proc then + self.instance_exec(&scope.name).to_s + else + scope.name.to_s + end + end + + def batch_actions_to_display + @batch_actions_to_display ||= begin + if active_admin_config && active_admin_config.batch_actions.any? + active_admin_config.batch_actions.select do |batch_action| + call_method_or_proc_on(self, batch_action.display_if_block) + end + else + [] + end + end + end + + # 1. removes `select` and `order` to prevent invalid SQL + # 2. correctly handles the Hash returned when `group by` is used + def collection_size(c = collection) + return c.count if c.is_a?(Array) + return c.length if c.limit_value + + c = c.except :select, :order + + c.group_values.present? ? c.count.count : c.count + end + + def collection_empty?(c = collection) + collection_size(c) == 0 + end + end +end diff --git a/app/helpers/active_admin/layout_helper.rb b/app/helpers/active_admin/layout_helper.rb new file mode 100644 index 00000000000..cb50ac5fb1a --- /dev/null +++ b/app/helpers/active_admin/layout_helper.rb @@ -0,0 +1,63 @@ +# frozen_string_literal: true +module ActiveAdmin + module LayoutHelper + # Returns the current Active Admin application instance + def active_admin_application + ActiveAdmin.application + end + + def set_page_title(title) + @page_title = title + end + + def site_title + # Prioritize namespace and account for Devise views where namespace is not available + namespace = active_admin_namespace if respond_to?(:active_admin_namespace) + (namespace || active_admin_application).site_title(self) + end + + def html_head_site_title(separator: "-") + "#{@page_title || page_title} #{separator} #{site_title}" + end + + def action_items_for_action + @action_items_for_action ||= begin + if active_admin_config&.action_items? + active_admin_config.action_items_for(params[:action], self) + else + [] + end + end + end + + def sidebar_sections_for_action + @sidebar_sections_for_action ||= begin + if active_admin_config&.sidebar_sections? + active_admin_config.sidebar_sections_for(params[:action], self) + else + [] + end + end + end + + def skip_sidebar! + @skip_sidebar = true + end + + def skip_sidebar? + @skip_sidebar == true + end + + def flash_messages + @flash_messages ||= flash.to_hash.except(*active_admin_application.flash_keys_to_except) + end + + def url_for_comments(*args) + parts = [] + parts << active_admin_namespace.name unless active_admin_namespace.root? + parts << active_admin_namespace.comments_registration_name.underscore + parts << "path" + send parts.join("_"), *args + end + end +end diff --git a/app/javascript/active_admin.js b/app/javascript/active_admin.js new file mode 100644 index 00000000000..fcaaf86ec2b --- /dev/null +++ b/app/javascript/active_admin.js @@ -0,0 +1,10 @@ +import "flowbite" +import Rails from "@rails/ujs" +import "active_admin/features/batch_actions" +import "active_admin/features/dark_mode_toggle" +import "active_admin/features/has_many" +import "active_admin/features/filters" +import "active_admin/features/main_menu" +import "active_admin/features/per_page" + +Rails.start() diff --git a/app/javascript/active_admin/base.js b/app/javascript/active_admin/base.js deleted file mode 100644 index 75912834815..00000000000 --- a/app/javascript/active_admin/base.js +++ /dev/null @@ -1,28 +0,0 @@ -import "jquery" -import "jquery-ui/ui/widgets/datepicker" -import "jquery-ui/ui/widgets/dialog" -import "jquery-ui/ui/widgets/sortable" -import "jquery-ui/ui/widgets/tabs" -import "jquery-ui/ui/widget" -import "jquery-ujs" - -import "./ext/jquery" -import "./ext/jquery-ui" -import "./initializers/batch-actions" -import "./initializers/checkbox-toggler" -import "./initializers/datepicker" -import "./initializers/dropdown-menu" -import "./initializers/filters" -import "./initializers/has-many" -import "./initializers/per-page" -import "./initializers/table-checkbox-toggler" -import "./initializers/tabs" - -import ModalDialog from "./lib/modal-dialog"; - -function modal_dialog(message, inputs, callback) { - console.warn("ActiveAdmin.modal_dialog is deprecated in favor of ActiveAdmin.ModalDialog, please update usage."); - return ModalDialog(message, inputs, callback); -} - -export { ModalDialog, modal_dialog }; diff --git a/app/javascript/active_admin/ext/jquery-ui.js b/app/javascript/active_admin/ext/jquery-ui.js deleted file mode 100644 index 24194ff0884..00000000000 --- a/app/javascript/active_admin/ext/jquery-ui.js +++ /dev/null @@ -1,7 +0,0 @@ -// Short-circuits `_focusTabbable` to focus on the modal itself instead of -// elements inside the modal. Without this, if a datepicker is the first input, -// it'll immediately pop up when the modal opens. -// See this ticket for more info: http://bugs.jqueryui.com/ticket/4731 -$.ui.dialog.prototype._focusTabbable = function() { - this.uiDialog.focus(); -}; diff --git a/app/javascript/active_admin/ext/jquery.js b/app/javascript/active_admin/ext/jquery.js deleted file mode 100644 index e08596cd6a7..00000000000 --- a/app/javascript/active_admin/ext/jquery.js +++ /dev/null @@ -1,9 +0,0 @@ -// `serializeArray` generates => [{ name: 'foo', value: 'bar' }] -// This function remaps it to => { foo: 'bar' } -$.fn.serializeObject = function() { - return this.serializeArray() - .reduce((obj, item) => { - obj[item.name] = item.value - return obj - }, {}); -}; diff --git a/app/javascript/active_admin/features/batch_actions.js b/app/javascript/active_admin/features/batch_actions.js new file mode 100644 index 00000000000..a9bf3ecd408 --- /dev/null +++ b/app/javascript/active_admin/features/batch_actions.js @@ -0,0 +1,95 @@ +import Rails from '@rails/ujs'; + +const submitForm = function() { + let form = document.getElementById("collection_selection") + if (form) { + form.submit() + } +} + +const batchActionClick = function(event) { + event.preventDefault() + let batchAction = document.getElementById("batch_action") + if (batchAction) { + batchAction.value = this.dataset.action + } + + if (!event.target.dataset.confirm && !event.target.dataset.modalTarget) { submitForm() } +} + +const batchActionConfirmComplete = function(event) { + event.preventDefault() + if (event.detail[0] === true) { + let batchAction = document.getElementById("batch_action") + if (batchAction) { + batchAction.value = this.dataset.action + } + submitForm() + } +} + +const batchActionFormSubmit = function(event) { + event.preventDefault(); + let json = JSON.stringify(Object.fromEntries(new FormData(this).entries())); + let inputsField = document.getElementById('batch_action_inputs') + let form = document.getElementById("collection_selection") + if (json && inputsField && form) { + inputsField.value = json + form.submit() + } +} + +Rails.delegate(document, "[data-batch-action-item]", "confirm:complete", batchActionConfirmComplete) +Rails.delegate(document, "[data-batch-action-item]", "click", batchActionClick) +Rails.delegate(document, "form[data-batch-action-form]", "submit", batchActionFormSubmit) + +const disableDropdown = function(condition) { + const button = document.querySelector(".batch-actions-dropdown-toggle") + if (button) { + button.disabled = condition + } +} + +const toggleAllChange = function(event) { + const checkboxes = document.querySelectorAll(".batch-actions-resource-selection") + for (const checkbox of checkboxes) { + checkbox.checked = this.checked + } + + const rows = document.querySelectorAll(".paginated-collection tbody tr") + for (const row of rows) { + row.classList.toggle("selected", this.checked); + } + + disableDropdown(!this.checked) +} + +Rails.delegate(document, ".batch-actions-toggle-all", "change", toggleAllChange) + +const toggleCheckboxChange = function(event) { + const numChecked = document.querySelectorAll(".batch-actions-resource-selection:checked").length; + const allChecked = numChecked === document.querySelectorAll(".batch-actions-resource-selection").length; + const someChecked = (numChecked > 0) && (numChecked < document.querySelectorAll(".batch-actions-resource-selection").length); + + const toggleAll = document.querySelector(".batch-actions-toggle-all") + if (toggleAll) { + toggleAll.checked = allChecked + toggleAll.indeterminate = someChecked + } + + disableDropdown(numChecked === 0) +} + +Rails.delegate(document, ".batch-actions-resource-selection", "change", toggleCheckboxChange) + +const tableRowClick = function(event) { + const type = event.target.type; + if (typeof type === "undefined" || (type !== "checkbox" && type !== "button" && type !== "")) { + const checkbox = event.target.closest("tr").querySelector("input[type=checkbox]") + if (checkbox) { + checkbox.click() + } + } +} + +Rails.delegate(document, ".paginated-collection tbody td", "click", tableRowClick) diff --git a/app/javascript/active_admin/features/dark_mode_toggle.js b/app/javascript/active_admin/features/dark_mode_toggle.js new file mode 100644 index 00000000000..417f36177b3 --- /dev/null +++ b/app/javascript/active_admin/features/dark_mode_toggle.js @@ -0,0 +1,37 @@ +import Rails from '@rails/ujs'; + +const THEME_KEY = "theme"; +const darkModeMedia = window.matchMedia('(prefers-color-scheme: dark)'); + +const setTheme = () => { + // On page load or when changing themes, best to add inline in `head` to avoid FOUC + if (localStorage.getItem(THEME_KEY) === 'dark' || (!(THEME_KEY in localStorage) && darkModeMedia.matches)) { + document.documentElement.classList.add('dark'); + } else { + document.documentElement.classList.remove('dark'); + } +} + +// Detect when user changes their system level preference to set theme. +darkModeMedia.addEventListener("change", setTheme); + +// When the page loads, set theme. By default, uses the system preference. +document.addEventListener("DOMContentLoaded", setTheme); + +// If user deletes the Local Storage key, then re-apply theme. +window.addEventListener("storage", (event) => { + if (event.key === THEME_KEY) { + setTheme() + } +}); + +const toggleTheme = () => { + if (localStorage.getItem(THEME_KEY) === 'dark' || (!(THEME_KEY in localStorage) && darkModeMedia.matches)) { + localStorage.setItem(THEME_KEY, 'light'); + } else { + localStorage.setItem(THEME_KEY, 'dark'); + } + setTheme(); +}; + +Rails.delegate(document, ".dark-mode-toggle", "click", toggleTheme); diff --git a/app/javascript/active_admin/features/filters.js b/app/javascript/active_admin/features/filters.js new file mode 100644 index 00000000000..6e8c5e25263 --- /dev/null +++ b/app/javascript/active_admin/features/filters.js @@ -0,0 +1,34 @@ +import Rails from '@rails/ujs'; +import { nextSibling } from 'active_admin/utils/dom' + +const disableEmptyFields = function(event) { + Array.from(this.querySelectorAll("input, select, textarea")) + .filter((el) => el.value === "") + .forEach((el) => el.disabled = true) +}; + +Rails.delegate(document, ".filters-form", "submit", disableEmptyFields) + +const setSearchType = function(event) { + const input = nextSibling(this, "input") + if (input) { + input.name = `q[${this.value}]` + } +}; + +Rails.delegate(document, ".filters-form-field [data-search-methods]", "change", setSearchType) + +const clearFiltersForm = function(event) { + event.preventDefault() + + const regex = /^(q\[|page|utf8|commit)/ + const params = new URLSearchParams(window.location.search) + + Array.from(params.keys()) + .filter(k => k.match(regex)) + .forEach(k => params.delete(k)) + + window.location.search = params.toString() +} + +Rails.delegate(document, ".filters-form-clear", "click", clearFiltersForm) diff --git a/app/javascript/active_admin/features/has_many.js b/app/javascript/active_admin/features/has_many.js new file mode 100644 index 00000000000..524e093c18d --- /dev/null +++ b/app/javascript/active_admin/features/has_many.js @@ -0,0 +1,28 @@ +import Rails from '@rails/ujs'; + +const hasManyRemoveClick = function(event) { + event.preventDefault() + const oldGroup = this.closest("fieldset") + if (oldGroup) { + oldGroup.remove() + } +} + +Rails.delegate(document, "form .has-many-remove", "click", hasManyRemoveClick) + +const hasManyAddClick = function(event) { + event.preventDefault() + const parent = this.closest(".has-many-container") + + let index = parseInt(parent.dataset.has_many_index || (parent.querySelectorAll('fieldset').length - 1), 10) + parent.dataset.has_many_index = ++index + + const regex = new RegExp(this.dataset.placeholder, 'g') + const html = this.dataset.html.replace(regex, index) + + const tempEl = document.createElement("div"); + tempEl.innerHTML = html + this.before(tempEl.firstChild) +} + +Rails.delegate(document, "form .has-many-add", "click", hasManyAddClick) diff --git a/app/javascript/active_admin/features/main_menu.js b/app/javascript/active_admin/features/main_menu.js new file mode 100644 index 00000000000..46bf78d59b7 --- /dev/null +++ b/app/javascript/active_admin/features/main_menu.js @@ -0,0 +1,12 @@ +import Rails from '@rails/ujs'; + +const toggleMenu = function(event) { + const parent = this.parentNode + if (!("open" in parent.dataset)) { + parent.dataset.open = "" + } else { + delete parent.dataset.open + } +} + +Rails.delegate(document, "#main-menu [data-menu-button]", "click", toggleMenu) diff --git a/app/javascript/active_admin/features/per_page.js b/app/javascript/active_admin/features/per_page.js new file mode 100644 index 00000000000..499c6b45fd1 --- /dev/null +++ b/app/javascript/active_admin/features/per_page.js @@ -0,0 +1,9 @@ +import Rails from '@rails/ujs'; + +const setPerPage = function(event) { + const params = new URLSearchParams(window.location.search) + params.set("per_page", this.value) + window.location.search = params +} + +Rails.delegate(document, ".pagination-per-page", "change", setPerPage) diff --git a/app/javascript/active_admin/initializers/batch-actions.js b/app/javascript/active_admin/initializers/batch-actions.js deleted file mode 100644 index fc9a95cce2c..00000000000 --- a/app/javascript/active_admin/initializers/batch-actions.js +++ /dev/null @@ -1,61 +0,0 @@ -import ModalDialog from "../lib/modal-dialog"; - -const onDOMReady = function() { - // Detach any previously attached handlers before re-attaching them. - // This avoids double-registered handlers when Turbolinks is enabled - $('.batch_actions_selector li a').off('click confirm:complete'); - - // - // Use ModalDialog to prompt user if - // confirmation is required for current Batch Action - // - $('.batch_actions_selector li a').on('click', function(event){ - let message; - event.stopPropagation(); // prevent Rails UJS click event - event.preventDefault(); - if ((message = $(this).data('confirm'))) { - ModalDialog(message, $(this).data('inputs'), inputs => { - $(this).trigger('confirm:complete', inputs); - }); - } else { - $(this).trigger('confirm:complete'); - } - }); - - $('.batch_actions_selector li a').on('confirm:complete', function(event, inputs){ - let val; - if ((val = JSON.stringify(inputs))) { - $('#batch_action_inputs').removeAttr('disabled').val(val); - } else { - $('#batch_action_inputs').attr('disabled', 'disabled'); - } - - $('#batch_action').val($(this).data('action')); - $('#collection_selection').submit(); - }); - - // - // Add checkbox selection to resource tables and lists if batch actions are enabled - // - - if ($(".batch_actions_selector").length && $(":checkbox.toggle_all").length) { - - if ($(".paginated_collection table.index_table").length) { - $(".paginated_collection table.index_table").tableCheckboxToggler(); - } else { - $(".paginated_collection").checkboxToggler(); - } - - $(document).on('change', '.paginated_collection :checkbox', function() { - if ($(".paginated_collection :checkbox:checked").length && $(".dropdown_menu_list").children().length) { - $(".batch_actions_selector").each(function() { $(this).aaDropdownMenu("enable"); }); - } else { - $(".batch_actions_selector").each(function() { $(this).aaDropdownMenu("disable"); }); - } - }); - } -}; - -$(document). - ready(onDOMReady). - on('page:load turbolinks:load', onDOMReady); diff --git a/app/javascript/active_admin/initializers/checkbox-toggler.js b/app/javascript/active_admin/initializers/checkbox-toggler.js deleted file mode 100644 index d1880ee55e7..00000000000 --- a/app/javascript/active_admin/initializers/checkbox-toggler.js +++ /dev/null @@ -1,3 +0,0 @@ -import CheckboxToggler from "../lib/checkbox-toggler"; - -$.widget.bridge('checkboxToggler', CheckboxToggler); diff --git a/app/javascript/active_admin/initializers/datepicker.js b/app/javascript/active_admin/initializers/datepicker.js deleted file mode 100644 index 2c8aabd6166..00000000000 --- a/app/javascript/active_admin/initializers/datepicker.js +++ /dev/null @@ -1,16 +0,0 @@ -(($) => { - - $(document) - .on('focus', 'input.datepicker:not(.hasDatepicker)', function() { - const input = $(this); - - // Only create datepickers in compatible browsers - if (input[0].type === 'date') { return; } - - const defaults = { dateFormat: 'yy-mm-dd' }; - const options = input.data('datepicker-options'); - - input.datepicker($.extend(defaults, options)); - }); - -})(jQuery); diff --git a/app/javascript/active_admin/initializers/dropdown-menu.js b/app/javascript/active_admin/initializers/dropdown-menu.js deleted file mode 100644 index 9f6b3bba635..00000000000 --- a/app/javascript/active_admin/initializers/dropdown-menu.js +++ /dev/null @@ -1,9 +0,0 @@ -import DropdownMenu from "../lib/dropdown-menu"; - -$.widget.bridge('aaDropdownMenu', DropdownMenu); - -const onDOMReady = () => $('.dropdown_menu').aaDropdownMenu(); - -$(document). - ready(onDOMReady). - on('page:load turbolinks:load', onDOMReady); diff --git a/app/javascript/active_admin/initializers/filters.js b/app/javascript/active_admin/initializers/filters.js deleted file mode 100644 index a99fb761fa6..00000000000 --- a/app/javascript/active_admin/initializers/filters.js +++ /dev/null @@ -1,10 +0,0 @@ -import Filters from '../lib/filters'; - -(($) => { - - $(document). - on('click', '.clear_filters_btn', Filters._clearForm). - on('submit', '.filter_form', Filters._disableEmptyInputFields). - on('change', '.filter_form_field.select_and_search select', Filters._setSearchType); - -})(jQuery); diff --git a/app/javascript/active_admin/initializers/has-many.js b/app/javascript/active_admin/initializers/has-many.js deleted file mode 100644 index 1e5dbd1f969..00000000000 --- a/app/javascript/active_admin/initializers/has-many.js +++ /dev/null @@ -1,95 +0,0 @@ -$(function() { - // Provides a before-removal hook: - // $ -> - // # This is a good place to tear down JS plugins to prevent memory leaks. - // $(document).on 'has_many_remove:before', '.has_many_container', (e, fieldset, container)-> - // fieldset.find('.select2').select2 'destroy' - // - // # If you need to do anything after removing the items you can use the - // has_many_remove:after hook - // $(document).on 'has_many_remove:after', '.has_many_container', (e, fieldset, container)-> - // list_item_count = container.find('.has_many_fields').length - // alert("There are now #{list_item_count} items in the list") - // - $(document).on('click', 'a.button.has_many_remove', function(event){ - event.preventDefault(); - const parent = $(this).closest('.has_many_container'); - const to_remove = $(this).closest('fieldset'); - recompute_positions(parent); - - parent.trigger('has_many_remove:before', [to_remove, parent]); - to_remove.remove(); - return parent.trigger('has_many_remove:after', [to_remove, parent]); - }); - - // Provides before and after creation hooks: - // $ -> - // # The before hook allows you to prevent the creation of new records. - // $(document).on 'has_many_add:before', '.has_many_container', (e, container)-> - // if $(@).children('fieldset').length >= 3 - // alert "you've reached the maximum number of items" - // e.preventDefault() - // - // # The after hook is a good place to initialize JS plugins and the like. - // $(document).on 'has_many_add:after', '.has_many_container', (e, fieldset, container)-> - // fieldset.find('select').chosen() - // - $(document).on('click', 'a.button.has_many_add', function(event){ - let before_add; - event.preventDefault(); - const parent = $(this).closest('.has_many_container'); - parent.trigger((before_add = $.Event('has_many_add:before')), [parent]); - - if (!before_add.isDefaultPrevented()) { - let index = parent.data('has_many_index') || (parent.children('fieldset').length - 1); - parent.data({has_many_index: ++index}); - - const regex = new RegExp($(this).data('placeholder'), 'g'); - const html = $(this).data('html').replace(regex, index); - - const fieldset = $(html).insertBefore(this); - recompute_positions(parent); - return parent.trigger('has_many_add:after', [fieldset, parent]); - } - }); - - $(document).on('change', '.has_many_container[data-sortable] :input[name$="[_destroy]"]', function() { - recompute_positions($(this).closest('.has_many')); - }); - - init_sortable(); - $(document).on('has_many_add:after', '.has_many_container', init_sortable); -}); - -var init_sortable = function() { - const elems = $('.has_many_container[data-sortable]:not(.ui-sortable)'); - elems.sortable({ - items: '> fieldset', - handle: '> ol > .handle', - start: (ev, ui) => { - ui.item.css({opacity: 0.3}); - }, - stop: function (ev, ui) { - ui.item.css({opacity: 1.0}); - recompute_positions($(this)); - } - }); - elems.each(recompute_positions); -}; - -var recompute_positions = function(parent){ - parent = parent instanceof jQuery ? parent : $(this); - const input_name = parent.data('sortable'); - let position = parseInt(parent.data('sortable-start') || 0, 10); - - parent.children('fieldset').each(function() { - // We ignore nested inputs, so when defining your has_many, be sure to keep - // your sortable input at the root of the has_many block. - const destroy_input = $(this).find("> ol > .input > :input[name$='[_destroy]']"); - const sortable_input = $(this).find(`> ol > .input > :input[name$='[${input_name}]']`); - - if (sortable_input.length) { - sortable_input.val(destroy_input.is(':checked') ? '' : position++); - } - }); -}; diff --git a/app/javascript/active_admin/initializers/per-page.js b/app/javascript/active_admin/initializers/per-page.js deleted file mode 100644 index 6f218fe71e1..00000000000 --- a/app/javascript/active_admin/initializers/per-page.js +++ /dev/null @@ -1,13 +0,0 @@ -import PerPage from "../lib/per-page"; - -(($) => { - - $(document). - on('change', '.pagination_per_page > select', function(_event) { - PerPage._jQueryInterface.call($(this), 'update') - }); - - $.fn['perPage'] = PerPage._jQueryInterface - $.fn['perPage'].Constructor = PerPage - -})(jQuery); diff --git a/app/javascript/active_admin/initializers/table-checkbox-toggler.js b/app/javascript/active_admin/initializers/table-checkbox-toggler.js deleted file mode 100644 index 48c683cacee..00000000000 --- a/app/javascript/active_admin/initializers/table-checkbox-toggler.js +++ /dev/null @@ -1,3 +0,0 @@ -import TableCheckboxToggler from "../lib/table-checkbox-toggler"; - -$.widget.bridge('tableCheckboxToggler', TableCheckboxToggler); diff --git a/app/javascript/active_admin/initializers/tabs.js b/app/javascript/active_admin/initializers/tabs.js deleted file mode 100644 index fc8ae92c57a..00000000000 --- a/app/javascript/active_admin/initializers/tabs.js +++ /dev/null @@ -1,6 +0,0 @@ -let onDOMReady = () => - $('#active_admin_content .tabs').tabs() - -$(document). - ready(onDOMReady). - on('page:load turbolinks:load', onDOMReady) diff --git a/app/javascript/active_admin/lib/checkbox-toggler.js b/app/javascript/active_admin/lib/checkbox-toggler.js deleted file mode 100644 index 03484179f46..00000000000 --- a/app/javascript/active_admin/lib/checkbox-toggler.js +++ /dev/null @@ -1,49 +0,0 @@ -class CheckboxToggler { - constructor(options, container){ - this.options = options; - this.container = container; - this._init(); - this._bind(); - } - - option(_key, _value) { - } - - _init() { - if (!this.container) { - throw new Error('Container element not found'); - } else { - this.$container = $(this.container); - } - - if (!this.$container.find('.toggle_all').length) { - throw new Error('"toggle all" checkbox not found'); - } else { - this.toggle_all_checkbox = this.$container.find('.toggle_all'); - } - - this.checkboxes = this.$container.find(':checkbox').not(this.toggle_all_checkbox); - } - - _bind() { - this.checkboxes.change(event => this._didChangeCheckbox(event.target)); - this.toggle_all_checkbox.change(() => this._didChangeToggleAllCheckbox()); - } - - _didChangeCheckbox(_checkbox){ - const numChecked = this.checkboxes.filter(':checked').length; - - const allChecked = numChecked === this.checkboxes.length; - const someChecked = (numChecked > 0) && (numChecked < this.checkboxes.length); - - this.toggle_all_checkbox.prop({ checked: allChecked, indeterminate: someChecked }); - } - - _didChangeToggleAllCheckbox() { - const setting = this.toggle_all_checkbox.prop('checked'); - this.checkboxes.prop({ checked: setting }); - return setting; - } -} - -export default CheckboxToggler; diff --git a/app/javascript/active_admin/lib/dropdown-menu.js b/app/javascript/active_admin/lib/dropdown-menu.js deleted file mode 100644 index 7ce32696694..00000000000 --- a/app/javascript/active_admin/lib/dropdown-menu.js +++ /dev/null @@ -1,116 +0,0 @@ -class DropdownMenu { - constructor(options, element) { - this.options = options; - this.element = element; - this.$element = $(this.element); - - const defaults = { - fadeInDuration: 20, - fadeOutDuration: 100, - onClickActionItemCallback: null - }; - - this.options = $.extend(defaults, this.options); - this.isOpen = false; - - this.$menuButton = this.$element.find('.dropdown_menu_button'); - this.$menuList = this.$element.find('.dropdown_menu_list_wrapper'); - - this._buildMenuList(); - this._bind(); - } - - open() { - this.isOpen = true; - this.$menuList.fadeIn(this.options.fadeInDuration); - - this._position(); - return this; - } - - close() { - this.isOpen = false; - this.$menuList.fadeOut(this.options.fadeOutDuration); - return this; - } - - destroy() { - this.$element = null; - return this; - } - - isDisabled() { - return this.$menuButton.hasClass('disabled'); - } - - disable() { - this.$menuButton.addClass('disabled'); - } - - enable() { - this.$menuButton.removeClass('disabled'); - } - - option(key, value) { - if ($.isPlainObject(key)) { - return this.options = $.extend(true, this.options, key); - } else if (key != null) { - return this.options[key]; - } else { - return this.options[key] = value; - } - } - - _buildMenuList() { - this.$nipple = $(''); - this.$menuList.prepend(this.$nipple); - this.$menuList.hide(); - } - - _bind() { - $('body').click(() => { - if (this.isOpen) { this.close(); } - }); - - this.$menuButton.click(() => { - if (!this.isDisabled()) { - if (this.isOpen) { - this.close(); - } else { - this.open(); - } - } - return false; - }); - } - - _position() { - this.$menuList.css('top', this.$menuButton.position().top + this.$menuButton.outerHeight() + 10); - - const button_left = this.$menuButton.position().left; - const button_center = this.$menuButton.outerWidth() / 2; - const button_right = button_left + (button_center * 2); - const menu_center = this.$menuList.outerWidth() / 2; - const nipple_center = this.$nipple.outerWidth() / 2; - const window_right = $(window).width(); - - const centered_menu_left = (button_left + button_center) - menu_center; - const centered_menu_right = button_left + button_center + menu_center; - - if (centered_menu_left < 0) { - // Left align with button - this.$menuList.css('left', button_left); - this.$nipple.css('left', button_center - nipple_center); - } else if (centered_menu_right > window_right) { - // Right align with button - this.$menuList.css('right', window_right - button_right); - this.$nipple.css('right', button_center - nipple_center); - } else { - // Center align under button - this.$menuList.css('left', centered_menu_left); - this.$nipple.css('left', menu_center - nipple_center); - } - } -} - -export default DropdownMenu; diff --git a/app/javascript/active_admin/lib/filters.js b/app/javascript/active_admin/lib/filters.js deleted file mode 100644 index ec034f940f8..00000000000 --- a/app/javascript/active_admin/lib/filters.js +++ /dev/null @@ -1,39 +0,0 @@ -import { queryStringToParams, hasTurbolinks, turbolinksVisit, toQueryString } from '../lib/utils'; - -class Filters { - - static _clearForm(event) { - const regex = /^(q\[|q%5B|q%5b|page|utf8|commit)/; - const params = queryStringToParams() - .filter(({name}) => !name.match(regex)); - - event.preventDefault(); - - if (hasTurbolinks()) { - turbolinksVisit(params); - } else { - window.location.search = toQueryString(params); - } - } - - static _disableEmptyInputFields(event) { - const params = $(this) - .find(':input') - .filter((i, input) => input.value === '') - .prop({ disabled: true }) - .end() - .serializeArray(); - - if (hasTurbolinks()) { - event.preventDefault(); - turbolinksVisit(params); - } - } - - static _setSearchType() { - $(this).siblings('input').prop({name: `q[${this.value}]`}); - } - -} - -export default Filters; diff --git a/app/javascript/active_admin/lib/modal-dialog.js b/app/javascript/active_admin/lib/modal-dialog.js deleted file mode 100644 index b42dfb7d4a8..00000000000 --- a/app/javascript/active_admin/lib/modal-dialog.js +++ /dev/null @@ -1,63 +0,0 @@ -function ModalDialog(message, inputs, callback){ - let html = `
    `; - for (let name in inputs) { - var opts, wrapper; - let type = inputs[name]; - if (/^(datepicker|checkbox|text|number)$/.test(type)) { - wrapper = 'input'; - } else if (type === 'textarea') { - wrapper = 'textarea'; - } else if ($.isArray(type)) { - [wrapper, opts, type] = ['select', type, '']; - } else { - throw new Error(`Unsupported input type: {${name}: ${type}}`); - } - - let klass = type === 'datepicker' ? type : ''; - html += `
  • - - <${wrapper} name="${name}" class="${klass}" type="${type}">` + - (opts ? ((() => { - const result = []; - - opts.forEach(v => { - const $elem = $(''); - if ($.isArray(v)) { - $elem.text(v[0]).val(v[1]); - } else { - $elem.text(v); - } - result.push($elem.wrap('
    ').parent().html()); - }); - - return result; - })()).join('') : '') + - `` + - "
  • "; - [wrapper, opts, type, klass] = []; // unset any temporary variables - } - - html += "
"; - - const form = $(html).appendTo('body'); - $('body').trigger('modal_dialog:before_open', [form]); - - form.dialog({ - modal: true, - open(_event, _ui) { - $('body').trigger('modal_dialog:after_open', [form]); - }, - dialogClass: 'active_admin_dialog', - buttons: { - OK() { - callback($(this).serializeObject()); - $(this).dialog('close'); - }, - Cancel() { - $(this).dialog('close').remove(); - } - } - }); -} - -export default ModalDialog; diff --git a/app/javascript/active_admin/lib/per-page.js b/app/javascript/active_admin/lib/per-page.js deleted file mode 100644 index 7ffb107a75c..00000000000 --- a/app/javascript/active_admin/lib/per-page.js +++ /dev/null @@ -1,38 +0,0 @@ -import { queryStringToParams, hasTurbolinks, turbolinksVisit, toQueryString } from './utils'; - -class PerPage { - constructor(element) { - this.element = element; - } - - update() { - const params = queryStringToParams() - .filter(({name}) => name != 'per_page' || name != 'page') - - params.push({ name: 'per_page', value: this.element.value }); - - if (hasTurbolinks()) { - turbolinksVisit(params); - } else { - window.location.search = toQueryString(params); - } - } - - static _jQueryInterface(config) { - return this.each(function () { - const $this = $(this) - let data = $this.data('perPage') - - if (!data) { - data = new PerPage(this) - $this.data('perPage', data) - } - - if (config === 'update') { - data[config]() - } - }) - } -} - -export default PerPage; diff --git a/app/javascript/active_admin/lib/table-checkbox-toggler.js b/app/javascript/active_admin/lib/table-checkbox-toggler.js deleted file mode 100644 index abc3ef0ddf0..00000000000 --- a/app/javascript/active_admin/lib/table-checkbox-toggler.js +++ /dev/null @@ -1,38 +0,0 @@ -import CheckboxToggler from "./checkbox-toggler"; - -class TableCheckboxToggler extends CheckboxToggler { - _bind() { - super._bind(...arguments); - - this.$container - .find('tbody td') - .click(event => { - if (event.target.type !== 'checkbox') { - this._didClickCell(event.target); - } - }); - } - - _didChangeCheckbox(checkbox) { - super._didChangeCheckbox(...arguments); - - $(checkbox) - .parents('tr') - .toggleClass('selected', checkbox.checked); - } - - _didChangeToggleAllCheckbox() { - this.$container - .find('tbody tr') - .toggleClass('selected', super._didChangeToggleAllCheckbox(...arguments)); - } - - _didClickCell(cell) { - $(cell) - .parent('tr') - .find(':checkbox') - .click(); - } -} - -export default TableCheckboxToggler; diff --git a/app/javascript/active_admin/lib/utils.js b/app/javascript/active_admin/lib/utils.js deleted file mode 100644 index ff2200f514f..00000000000 --- a/app/javascript/active_admin/lib/utils.js +++ /dev/null @@ -1,40 +0,0 @@ -function hasTurbolinks() { - return (typeof Turbolinks !== 'undefined' && Turbolinks.supported); -} - -function turbolinksVisit(params) { - const path = [window.location.pathname, '?', toQueryString(params)].join('') - Turbolinks.visit(path); -} - -function queryString() { - return (window.location.search || '').replace(/^\?/, ''); -} - -function queryStringToParams() { - const decode = (value) => decodeURIComponent((value || '').replace(/\+/g, '%20')); - - return queryString() - .split("&") - .map(pair => pair.split("=")) - .map(([key, value]) => { - return { name: decode(key), value: decode(value) } - }); -} - -function toQueryString(params) { - const encode = (value) => encodeURIComponent(value || ''); - - return params - .map(({name, value}) => [ encode(name), encode(value) ]) - .map(pair => pair.join('=')) - .join('&') -} - -export { - hasTurbolinks, - turbolinksVisit, - queryString, - queryStringToParams, - toQueryString -}; diff --git a/app/javascript/active_admin/utils/dom.js b/app/javascript/active_admin/utils/dom.js new file mode 100644 index 00000000000..20f2d051edb --- /dev/null +++ b/app/javascript/active_admin/utils/dom.js @@ -0,0 +1,17 @@ +const nextSibling = function next(element, selector) { + let sibling = element.nextElementSibling; + + if (!selector) { + return sibling; + } + + while (sibling) { + if (sibling && sibling.matches(selector)) { + return sibling; + } + + sibling = sibling.nextElementSibling; + } +} + +export { nextSibling } diff --git a/app/views/active_admin/_flash_messages.html.erb b/app/views/active_admin/_flash_messages.html.erb new file mode 100644 index 00000000000..d291236f45e --- /dev/null +++ b/app/views/active_admin/_flash_messages.html.erb @@ -0,0 +1,22 @@ +<% if flash_messages.present? %> +
+ <% flash_messages.each do |type, message| %> + <% if type == "error" %> +
+ + <%= message %> +
+ <% elsif type == "alert" %> +
+ + <%= message %> +
+ <% elsif type == "notice" %> +
+ + <%= message %> +
+ <% end %> + <% end %> +
+<% end %> diff --git a/app/views/active_admin/_html_head.html.erb b/app/views/active_admin/_html_head.html.erb new file mode 100644 index 00000000000..afbfd32ae28 --- /dev/null +++ b/app/views/active_admin/_html_head.html.erb @@ -0,0 +1,13 @@ +<%= stylesheet_link_tag "active_admin" %> + +<%= csrf_meta_tags %> +<%= csp_meta_tag %> +<% # On page load or when changing themes, best to add inline in `head` to avoid FOUC %> +<%= javascript_tag nonce: true do %> + if (localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) { + document.documentElement.classList.add('dark') + } else { + document.documentElement.classList.remove('dark') + } +<% end %> +<%= javascript_importmap_tags "active_admin", importmap: ActiveAdmin.importmap %> diff --git a/app/views/active_admin/_main_navigation.html.erb b/app/views/active_admin/_main_navigation.html.erb new file mode 100644 index 00000000000..a2cbd27f94c --- /dev/null +++ b/app/views/active_admin/_main_navigation.html.erb @@ -0,0 +1,28 @@ + diff --git a/app/views/active_admin/_page_header.html.erb b/app/views/active_admin/_page_header.html.erb new file mode 100644 index 00000000000..2b8ababe310 --- /dev/null +++ b/app/views/active_admin/_page_header.html.erb @@ -0,0 +1,27 @@ +
+
+ <% breadcrumb_links = build_breadcrumb_links(request.path, class: "text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200 no-underline") %> + <% if breadcrumb_links.present? %> + + <% end %> +

<%= title %>

+
+ <% if action_items_for_action.present? %> +
+ <%= render "active_admin/shared/action_items" %> +
+ <% end %> +
diff --git a/app/views/active_admin/_sidebar.html.erb b/app/views/active_admin/_sidebar.html.erb new file mode 100644 index 00000000000..eef67b78c45 --- /dev/null +++ b/app/views/active_admin/_sidebar.html.erb @@ -0,0 +1,5 @@ +<% unless skip_sidebar? || sidebar_sections_for_action.blank? %> +
+ <%= render "active_admin/shared/sidebar_sections" %> +
+<% end %> diff --git a/app/views/active_admin/_site_footer.html.erb b/app/views/active_admin/_site_footer.html.erb new file mode 100644 index 00000000000..39b8b77c560 --- /dev/null +++ b/app/views/active_admin/_site_footer.html.erb @@ -0,0 +1,7 @@ +
+ <%= I18n.t( + "active_admin.powered_by", + active_admin: link_to("Active Admin", "https://activeadmin.info", class: "text-gray-500 dark:text-gray-500 hover:text-gray-900 dark:hover:text-gray-400 no-underline"), + version: ActiveAdmin::VERSION + ).html_safe %> +
diff --git a/app/views/active_admin/_site_header.html.erb b/app/views/active_admin/_site_header.html.erb new file mode 100644 index 00000000000..df1b0aa12fb --- /dev/null +++ b/app/views/active_admin/_site_header.html.erb @@ -0,0 +1,30 @@ +
+ + +
+

+ <%= title %> +

+
+ + + + + + + +
diff --git a/app/views/active_admin/devise/confirmations/new.html.erb b/app/views/active_admin/devise/confirmations/new.html.erb index 0b7f25ce6b8..7dfdf58a1e1 100644 --- a/app/views/active_admin/devise/confirmations/new.html.erb +++ b/app/views/active_admin/devise/confirmations/new.html.erb @@ -1,5 +1,7 @@ -
-

<%= active_admin_application.site_title(self) %> <%= title t('active_admin.devise.resend_confirmation_instructions.title') %>

+
+

+ <%= active_admin_application.site_title(self) %> <%= set_page_title t('active_admin.devise.resend_confirmation_instructions.title') %> +

<%= render partial: "active_admin/devise/shared/error_messages", resource: resource %> <%= active_admin_form_for(resource, as: resource_name, url: confirmation_path(resource_name), html: { method: :post }) do |f| @@ -7,7 +9,7 @@ f.input :email end f.actions do - f.action :submit, label: t('active_admin.devise.resend_confirmation_instructions.submit'), button_html: { value: t('active_admin.devise.resend_confirmation_instructions.submit') } + f.action :submit, label: t('active_admin.devise.resend_confirmation_instructions.submit'), button_html: { class: "w-full", value: t('active_admin.devise.resend_confirmation_instructions.submit') } end end %> diff --git a/app/views/active_admin/devise/passwords/edit.html.erb b/app/views/active_admin/devise/passwords/edit.html.erb index 2f35f190fcc..f8c97088d0f 100644 --- a/app/views/active_admin/devise/passwords/edit.html.erb +++ b/app/views/active_admin/devise/passwords/edit.html.erb @@ -1,5 +1,7 @@ -
-

<%= active_admin_application.site_title(self) %> <%= title t('active_admin.devise.change_password.title') %>

+
+

+ <%= active_admin_application.site_title(self) %> <%= set_page_title t('active_admin.devise.change_password.title') %> +

<%= render partial: "active_admin/devise/shared/error_messages", resource: resource %> <%= active_admin_form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :put }) do |f| @@ -9,7 +11,7 @@ f.input :reset_password_token, as: :hidden, input_html: { value: resource.reset_password_token } end f.actions do - f.action :submit, label: t('active_admin.devise.change_password.submit'), button_html: { value: t('active_admin.devise.change_password.submit') } + f.action :submit, label: t('active_admin.devise.change_password.submit'), button_html: { class: "w-full", value: t('active_admin.devise.change_password.submit') } end end %> diff --git a/app/views/active_admin/devise/passwords/new.html.erb b/app/views/active_admin/devise/passwords/new.html.erb index adc00e0d6cd..73bdb9d41d3 100644 --- a/app/views/active_admin/devise/passwords/new.html.erb +++ b/app/views/active_admin/devise/passwords/new.html.erb @@ -1,13 +1,14 @@ -
-

<%= active_admin_application.site_title(self) %> <%= title t('active_admin.devise.reset_password.title') %>

+
+

+ <%= active_admin_application.site_title(self) %> <%= set_page_title t('active_admin.devise.reset_password.title') %> +

- <%= render partial: "active_admin/devise/shared/error_messages", resource: resource %> <%= active_admin_form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :post }) do |f| f.inputs do f.input :email end f.actions do - f.action :submit, label: t('active_admin.devise.reset_password.submit'), button_html: { value: t('active_admin.devise.reset_password.submit') } + f.action :submit, label: t('active_admin.devise.reset_password.submit'), button_html: { class: "w-full", value: t('active_admin.devise.reset_password.submit') } end end %> diff --git a/app/views/active_admin/devise/registrations/new.html.erb b/app/views/active_admin/devise/registrations/new.html.erb index 36c9ccb1f98..189e97acf66 100644 --- a/app/views/active_admin/devise/registrations/new.html.erb +++ b/app/views/active_admin/devise/registrations/new.html.erb @@ -1,9 +1,11 @@ -
-

<%= active_admin_application.site_title(self) %> <%= title t('active_admin.devise.sign_up.title') %>

+
+

+ <%= active_admin_application.site_title(self) %> <%= set_page_title t('active_admin.devise.sign_up.title') %> +

<% scope = Devise::Mapping.find_scope!(resource_name) %> <%= render partial: "active_admin/devise/shared/error_messages", resource: resource %> - <%= active_admin_form_for(resource, as: resource_name, url: send(:"#{scope}_registration_path"), html: { id: "registration_new" }) do |f| + <%= active_admin_form_for(resource, as: resource_name, url: main_app.send(:"#{scope}_registration_path"), html: { id: "registration_new" }) do |f| f.inputs do resource.class.authentication_keys.each_with_index { |key, index| f.input key, label: t('active_admin.devise.'+key.to_s+'.title'), input_html: { autofocus: index.zero? } @@ -12,7 +14,7 @@ f.input :password_confirmation, label: t('active_admin.devise.password_confirmation.title') end f.actions do - f.action :submit, label: t('active_admin.devise.login.submit'), button_html: { value: t('active_admin.devise.sign_up.submit') } + f.action :submit, label: t('active_admin.devise.login.submit'), button_html: { class: "w-full", value: t('active_admin.devise.sign_up.submit') } end end %> diff --git a/app/views/active_admin/devise/sessions/new.html.erb b/app/views/active_admin/devise/sessions/new.html.erb index 8eb54ecf946..ba7edf5a69e 100644 --- a/app/views/active_admin/devise/sessions/new.html.erb +++ b/app/views/active_admin/devise/sessions/new.html.erb @@ -1,8 +1,10 @@ -
-

<%= active_admin_application.site_title(self) %> <%= title t('active_admin.devise.login.title') %>

+
+

+ <%= site_title %> <%= set_page_title t('active_admin.devise.login.title') %> +

<% scope = Devise::Mapping.find_scope!(resource_name) %> - <%= active_admin_form_for(resource, as: resource_name, url: send(:"#{scope}_session_path"), html: { id: "session_new" }) do |f| + <%= active_admin_form_for(resource, as: resource_name, url: main_app.send(:"#{scope}_session_path")) do |f| f.inputs do resource.class.authentication_keys.each_with_index { |key, index| f.input key, label: t("active_admin.devise.#{key}.title"), input_html: { autofocus: index.zero? } @@ -11,7 +13,7 @@ f.input :remember_me, label: t('active_admin.devise.login.remember_me'), as: :boolean if devise_mapping.rememberable? end f.actions do - f.action :submit, label: t('active_admin.devise.login.submit'), button_html: { value: t('active_admin.devise.login.submit') } + f.action :submit, label: t('active_admin.devise.login.submit'), wrapper_html: { class: "grow" }, button_html: { class: "w-full", value: t('active_admin.devise.login.submit') } end end %> diff --git a/app/views/active_admin/devise/shared/_links.erb b/app/views/active_admin/devise/shared/_links.erb index 81baa0f6b1a..e820abe4389 100644 --- a/app/views/active_admin/devise/shared/_links.erb +++ b/app/views/active_admin/devise/shared/_links.erb @@ -1,6 +1,7 @@ +
<%- if controller_name != 'sessions' %> <% scope = Devise::Mapping.find_scope!(resource_name) %> - <%= link_to t('active_admin.devise.links.sign_in'), send(:"new_#{scope}_session_path") %> + <%= link_to t('active_admin.devise.links.sign_in'), main_app.send(:"new_#{scope}_session_path") %>
<% end -%> @@ -31,3 +32,4 @@
<% end -%> <% end -%> +
diff --git a/app/views/active_admin/devise/unlocks/new.html.erb b/app/views/active_admin/devise/unlocks/new.html.erb index d77755545aa..6bd56a1d8af 100644 --- a/app/views/active_admin/devise/unlocks/new.html.erb +++ b/app/views/active_admin/devise/unlocks/new.html.erb @@ -1,5 +1,7 @@ -
-

<%= active_admin_application.site_title(self) %> <%= title t('active_admin.devise.unlock.title') %>

+
+

+ <%= site_title %> <%= set_page_title t('active_admin.devise.unlock.title') %> +

<%= render partial: "active_admin/devise/shared/error_messages", resource: resource %> <%= active_admin_form_for(resource, as: resource_name, url: unlock_path(resource_name), html: { method: :post }) do |f| @@ -7,9 +9,9 @@ f.input :email end f.actions do - f.action :submit, label: t('active_admin.devise.unlock.submit'), button_html: { value: t('active_admin.devise.unlock.submit') } + f.action :submit, label: t('active_admin.devise.unlock.submit'), button_html: { class: "w-full", value: t('active_admin.devise.unlock.submit') } end end %> -<%= render partial: "active_admin/devise/shared/links" %> + <%= render partial: "active_admin/devise/shared/links" %>
diff --git a/app/views/kaminari/active_admin_countless/_gap.html.erb b/app/views/active_admin/kaminari/_gap.html.erb similarity index 60% rename from app/views/kaminari/active_admin_countless/_gap.html.erb rename to app/views/active_admin/kaminari/_gap.html.erb index bbb0f9839c1..a310332dc25 100644 --- a/app/views/kaminari/active_admin_countless/_gap.html.erb +++ b/app/views/active_admin/kaminari/_gap.html.erb @@ -5,4 +5,6 @@ per_page: number of items to fetch per page remote: data-remote -%> -<%= t('views.pagination.truncate').html_safe %> + + <%= t('active_admin.pagination.truncate').html_safe %> + diff --git a/app/views/active_admin/kaminari/_next_page.html.erb b/app/views/active_admin/kaminari/_next_page.html.erb new file mode 100644 index 00000000000..97566c1890d --- /dev/null +++ b/app/views/active_admin/kaminari/_next_page.html.erb @@ -0,0 +1,16 @@ +<%# Link to the "Next" page + - available local variables + url: url to the next page + current_page: a page object for the currently displayed page + total_pages: total number of pages + per_page: number of items to fetch per page + remote: data-remote +-%> +<% unless current_page.last? %> + <%= link_to url, rel: 'next', remote: remote, class: "flex items-center justify-center px-2.5 py-3 h-8 leading-tight text-gray-500 dark:text-gray-400 hover:bg-gray-100 hover:text-gray-700 dark:hover:bg-gray-800 dark:hover:text-white rounded no-underline" do %> + <%= t('active_admin.pagination.next') %> + + <% end %> +<% end %> diff --git a/app/views/active_admin/kaminari/_page.html.erb b/app/views/active_admin/kaminari/_page.html.erb new file mode 100644 index 00000000000..3b48ec63cab --- /dev/null +++ b/app/views/active_admin/kaminari/_page.html.erb @@ -0,0 +1,14 @@ +<%# Link showing page number + - available local variables + page: a page object for "this" page + url: url to this page + current_page: a page object for the currently displayed page + total_pages: total number of pages + per_page: number of items to fetch per page + remote: data-remote +-%> +<% if page.current? %> + <%= link_to page, url, { remote: remote, rel: page.rel, class: "flex items-center justify-center px-2.5 py-3 h-8 leading-tight text-white bg-blue-500 dark:text-white dark:bg-blue-600 hover:bg-blue-500 hover:text-white dark:hover:bg-blue-600 dark:hover:text-white rounded no-underline" } %> +<% else %> + <%= link_to page, url, { remote: remote, rel: page.rel, class: "flex items-center justify-center px-2.5 py-3 h-8 leading-tight text-gray-500 dark:text-gray-400 hover:bg-gray-100 hover:text-gray-700 dark:hover:bg-gray-800 dark:hover:text-white rounded no-underline" } %> +<% end %> diff --git a/app/views/kaminari/active_admin_countless/_paginator.html.erb b/app/views/active_admin/kaminari/_paginator.html.erb similarity index 89% rename from app/views/kaminari/active_admin_countless/_paginator.html.erb rename to app/views/active_admin/kaminari/_paginator.html.erb index a1a145245fe..ade78b2f619 100644 --- a/app/views/kaminari/active_admin_countless/_paginator.html.erb +++ b/app/views/active_admin/kaminari/_paginator.html.erb @@ -7,8 +7,7 @@ paginator: the paginator that renders the pagination tags inside -%> <%= paginator.render do -%> -