diff --git a/.browserslistrc b/.browserslistrc deleted file mode 100644 index e94f8140c..000000000 --- a/.browserslistrc +++ /dev/null @@ -1 +0,0 @@ -defaults diff --git a/.coffeelint.json b/.coffeelint.json deleted file mode 100644 index 6bfd6487f..000000000 --- a/.coffeelint.json +++ /dev/null @@ -1,135 +0,0 @@ -{ - "arrow_spacing": { - "level": "warn" - }, - "braces_spacing": { - "level": "warn", - "spaces": 1, - "empty_object_spaces": 0 - }, - "camel_case_classes": { - "level": "error" - }, - "coffeescript_error": { - "level": "error" - }, - "colon_assignment_spacing": { - "level": "warn", - "spacing": { - "left": 0, - "right": 1 - } - }, - "cyclomatic_complexity": { - "level": "warn", - "value": 10 - }, - "duplicate_key": { - "level": "error" - }, - "empty_constructor_needs_parens": { - "level": "warn" - }, - "ensure_comprehensions": { - "level": "warn" - }, - "eol_last": { - "level": "warn" - }, - "indentation": { - "value": 2, - "level": "error" - }, - "line_endings": { - "level": "warn", - "value": "unix" - }, - "max_line_length": { - "value": 80, - "level": "ignore", - "limitComments": true - }, - "missing_fat_arrows": { - "level": "ignore" - }, - "newlines_after_classes": { - "value": 3, - "level": "warn" - }, - "no_backticks": { - "level": "error" - }, - "no_debugger": { - "level": "warn", - "console": false - }, - "no_empty_functions": { - "level": "warn" - }, - "no_empty_param_list": { - "level": "warn" - }, - "no_implicit_braces": { - "level": "ignore", - "strict": true - }, - "no_implicit_parens": { - "level": "ignore", - "strict": true - }, - "no_interpolation_in_single_quotes": { - "level": "warn" - }, - "no_nested_string_interpolation": { - "level": "warn" - }, - "no_plusplus": { - "level": "warn" - }, - "no_private_function_fat_arrows": { - "level": "warn" - }, - "no_stand_alone_at": { - "level": "warn" - }, - "no_tabs": { - "level": "error" - }, - "no_this": { - "level": "warn" - }, - "no_throwing_strings": { - "level": "error" - }, - "no_trailing_semicolons": { - "level": "error" - }, - "no_trailing_whitespace": { - "level": "error", - "allowed_in_comments": false, - "allowed_in_empty_lines": true - }, - "no_unnecessary_double_quotes": { - "level": "warn" - }, - "no_unnecessary_fat_arrows": { - "level": "warn" - }, - "non_empty_constructor_needs_parens": { - "level": "warn" - }, - "prefer_english_operator": { - "level": "ignore", - "doubleNotLevel": "warn" - }, - "space_operators": { - "level": "warn" - }, - "spacing_after_comma": { - "level": "warn" - }, - "transform_messes_up_line_numbers": { - "level": "warn" - } - } - \ No newline at end of file diff --git a/.erb-lint.yml b/.config/.erb-lint.yml similarity index 90% rename from .erb-lint.yml rename to .config/.erb-lint.yml index 48d5f74d1..5b8aba745 100644 --- a/.erb-lint.yml +++ b/.config/.erb-lint.yml @@ -1,3 +1,4 @@ +# Not used right now --- EnableDefaultLinters: true linters: diff --git a/.rubocop.yml b/.config/.rubocop.yml similarity index 99% rename from .rubocop.yml rename to .config/.rubocop.yml index c625d63ec..cc781ea1b 100644 --- a/.rubocop.yml +++ b/.config/.rubocop.yml @@ -105,7 +105,7 @@ Style/MethodCallWithArgsParentheses: AllowedPatterns: [^redirect_] # Don't enforce in migrations, as we have methods like `add_column`, # `change_column` etc. and parentheses would be very annoying there. - Exclude: ["db/**/*"] + Exclude: ["../db/**/*"] Style/RedundantReturn: AllowMultipleReturnValues: true diff --git a/.config/README.md b/.config/README.md new file mode 100644 index 000000000..f71a48028 --- /dev/null +++ b/.config/README.md @@ -0,0 +1,6 @@ +# Config settings + +This directory contains configuration files for the project according to the [`.config/` directory proposal](https://github.com/pi0/config-dir). + +Note that we currently don't use these files (but might in the near future): +- `.erb-lint.myl` diff --git a/eslint.config.mjs b/.config/eslint.mjs similarity index 99% rename from eslint.config.mjs rename to .config/eslint.mjs index 0d2ada414..c7a82fe39 100644 --- a/eslint.config.mjs +++ b/.config/eslint.mjs @@ -96,7 +96,7 @@ export default [ js.configs.recommended, // Allow linting of ERB files, see https://github.com/Splines/eslint-plugin-erb erb.configs.recommended, - // Globally ignore the following files + // Globally ignore the following paths { ignores: [ "node_modules/", diff --git a/.dockerignore b/.dockerignore index 097e4c485..844014fb0 100644 --- a/.dockerignore +++ b/.dockerignore @@ -3,7 +3,6 @@ docker/developement/Dockerfile docker/run_tests/docker-compose.yml docker/run_tests/Dockerfile **/.git -.travis* LICENSE README.md mampf-gui-transparent.png diff --git a/.github/PULL_REQUEST_TEMPLATE/feature.md b/.github/PULL_REQUEST_TEMPLATE/feature.md deleted file mode 100644 index ac58040fa..000000000 --- a/.github/PULL_REQUEST_TEMPLATE/feature.md +++ /dev/null @@ -1,29 +0,0 @@ -This pull request questionaire is for features and bugs - -* **Please check if the PR fulfills these requirements** - - [ ] E2E Tests for the changes have been added via Cypress - - [ ] Meaningful rspec tests have been added - - [ ] Docs have been added / updated - - Linter - - [ ] `rubocop` reports equal or less errors and warnings **in total** - - [ ] `yarn lint` reports equal or less errors and warnings **in total** - - [ ] `coffeelint .` reports equal or less errors and warnings **in total** - - [ ] `erblint .` reports equal or less errors and warnings **in total** - -* **What kind of change does this PR introduce?** (Bug fix, feature, docs update, ...) - - - -* **What is the current behavior?** (You can also link to an open issue here) - - - -* **What is the new behavior (if this is a feature change)?** - - - -* **Does this PR introduce a breaking change?** (What changes might users need to make in their application due to this PR?) - - - -* **Other information**: diff --git a/.github/PULL_REQUEST_TEMPLATE/localizing.md b/.github/PULL_REQUEST_TEMPLATE/localizing.md deleted file mode 100644 index 99faa3b04..000000000 --- a/.github/PULL_REQUEST_TEMPLATE/localizing.md +++ /dev/null @@ -1 +0,0 @@ -This pull request only changes wording and/or adds localizing information. \ No newline at end of file diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index e40132f75..230aafd40 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -72,4 +72,4 @@ jobs: if: ${{ steps.js-changed.outputs.changed-files != ''}} run: | echo "🚨 Running ESLint version: $(yarn run --silent eslint --version)" - yarn run eslint --max-warnings 0 --no-warn-ignored ${{ steps.js-changed.outputs.changed-files }} + yarn run eslint --config ./.config/eslint.mjs --max-warnings 0 --no-warn-ignored ${{ steps.js-changed.outputs.changed-files }} diff --git a/.gitignore b/.gitignore index a3800f0df..fa78b9564 100644 --- a/.gitignore +++ b/.gitignore @@ -43,7 +43,6 @@ coverage /public/uploads # Ignore environment variables -/config/app_environment_variables.rb completed_initial_run /public/uploads.zip /public/pdfcomprezzor/pdfcomprezzor.wasm diff --git a/.rspec b/.rspec deleted file mode 100644 index 83c8466f5..000000000 --- a/.rspec +++ /dev/null @@ -1,3 +0,0 @@ ---color ---require rails_helper ---format documentation diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index a1fdba20e..000000000 --- a/.travis.yml +++ /dev/null @@ -1,15 +0,0 @@ -language: ruby -service: - - docker - - -before_install: - - cd docker/run_tests/ - -install: - - docker-compose build - -before_script: - -script: - - docker-compose up --abort-on-container-exit diff --git a/.vscode/settings.json b/.vscode/settings.json index 0932c405a..473af2bd1 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -16,6 +16,9 @@ "eslint.experimental.useFlatConfig": true, // this disables VSCode built-in formatter (instead we want to use ESLint) "javascript.validate.enable": false, + "eslint.options": { + "overrideConfigFile": ".config/eslint.mjs" + }, ////////////////////////////////////// // HTML ////////////////////////////////////// @@ -30,7 +33,9 @@ "editor.formatOnSave": true }, "rubyLsp.formatter": "rubocop", - "rubyLsp.rubyVersionManager": "rbenv", + "rubyLsp.rubyVersionManager": { + "identifier": "rbenv" + }, "rubyLsp.enabledFeatures": { "codeActions": true, "diagnostics": true, @@ -89,6 +94,8 @@ ////////////////////////////////////// "cSpell.words": [ "commontator", + "helpdesk", "turbolinks" - ] + ], + "rubyLsp.customRubyCommand": "set -o allexport && . ./docker-dummy.env && set +o allexport" } \ No newline at end of file diff --git a/Gemfile b/Gemfile index aa44445ae..da68ea8e6 100644 --- a/Gemfile +++ b/Gemfile @@ -4,7 +4,7 @@ git_source(:github) { |repo| "https://github.com/#{repo}.git" } ruby "3.1.4" # Bundle edge Rails instead: gem 'rails', github: 'rails/rails' -gem "rails", "~> 7.0.4.3" +gem "rails", "~> 7.1.3" # Use dalli for caching to memcached in production gem "dalli", ">= 2.7" # Ruby wrapper for UglifyJS JavaScript compressor @@ -70,14 +70,14 @@ gem "erubis" gem "exception_handler", "~> 0.8.0.0" gem "faraday", "~> 1.8" gem "fuzzy-string-match" -gem "globalize" -gem "globalize-accessors" +gem "html-pipeline", "~> 2.14" gem "jquery-rails" gem "jquery-ui-rails" gem "js-routes", "1.4.9" gem "kaminari" gem "kaminari-i18n" gem "kramdown-parser-gfm" +gem "mobility" gem "net-smtp" gem "pg" gem "premailer-rails" @@ -104,16 +104,16 @@ gem "trix-rails", require: "trix" gem "webpacker", "~> 5.x" group :development, :docker_development do - # Access an interactive console on exception pages or by calling 'console' anywhere in the code. - gem "listen", ">= 3.0.5", "< 3.2" + gem "listen", "~> 3.9" gem "rails-erd" + # Access an interactive console on exception pages or by calling 'console' anywhere in the code. gem "web-console", ">= 3.3.0" # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring gem "marcel" gem "pgreset" - gem "rubocop", "~> 1.57", require: false - gem "rubocop-performance", "~> 1.16", require: false - gem "rubocop-rails", "~> 2.22", ">= 2.22.1", require: false + gem "rubocop", "~> 1.63", require: false + gem "rubocop-performance", "~> 1.21", require: false + gem "rubocop-rails", "~> 2.24", require: false gem "spring" gem "spring-watcher-listen", "~> 2.0.0" # gem 'bullet' diff --git a/Gemfile.lock b/Gemfile.lock index 946c11b31..8330339dc 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -10,11 +10,11 @@ GIT GIT remote: https://github.com/sunspot/sunspot.git - revision: 414a59413cb7333ba4b2cc7bc23a625c7a965e03 + revision: 41a311e9eff34df5ae7c51905574677dd474e91e glob: sunspot_rails/*.gemspec specs: sunspot_rails (2.6.0) - rails (>= 3) + rails (>= 5) sunspot (= 2.6.0) GIT @@ -28,125 +28,136 @@ GIT GIT remote: https://github.com/zdennis/activerecord-import.git - revision: 748309d1ae03fc1fe56d58e54c634a003408c706 + revision: f4d42e1090dca17e2eaad9f4df0ece8a8b7fe1a2 branch: master specs: - activerecord-import (1.4.1) + activerecord-import (1.6.0) activerecord (>= 4.2) GEM remote: https://rubygems.org/ specs: Ascii85 (1.1.0) - RubyInline (3.13.0) + RubyInline (3.14.0) ZenTest (~> 4.3) ZenTest (4.12.1) - actioncable (7.0.4.3) - actionpack (= 7.0.4.3) - activesupport (= 7.0.4.3) + actioncable (7.1.3.2) + actionpack (= 7.1.3.2) + activesupport (= 7.1.3.2) nio4r (~> 2.0) websocket-driver (>= 0.6.1) - actionmailbox (7.0.4.3) - actionpack (= 7.0.4.3) - activejob (= 7.0.4.3) - activerecord (= 7.0.4.3) - activestorage (= 7.0.4.3) - activesupport (= 7.0.4.3) + zeitwerk (~> 2.6) + actionmailbox (7.1.3.2) + actionpack (= 7.1.3.2) + activejob (= 7.1.3.2) + activerecord (= 7.1.3.2) + activestorage (= 7.1.3.2) + activesupport (= 7.1.3.2) mail (>= 2.7.1) net-imap net-pop net-smtp - actionmailer (7.0.4.3) - actionpack (= 7.0.4.3) - actionview (= 7.0.4.3) - activejob (= 7.0.4.3) - activesupport (= 7.0.4.3) + actionmailer (7.1.3.2) + actionpack (= 7.1.3.2) + actionview (= 7.1.3.2) + activejob (= 7.1.3.2) + activesupport (= 7.1.3.2) mail (~> 2.5, >= 2.5.4) net-imap net-pop net-smtp - rails-dom-testing (~> 2.0) - actionpack (7.0.4.3) - actionview (= 7.0.4.3) - activesupport (= 7.0.4.3) - rack (~> 2.0, >= 2.2.0) + rails-dom-testing (~> 2.2) + actionpack (7.1.3.2) + actionview (= 7.1.3.2) + activesupport (= 7.1.3.2) + nokogiri (>= 1.8.5) + racc + 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.4.3) - actionpack (= 7.0.4.3) - activerecord (= 7.0.4.3) - activestorage (= 7.0.4.3) - activesupport (= 7.0.4.3) + rails-dom-testing (~> 2.2) + rails-html-sanitizer (~> 1.6) + actiontext (7.1.3.2) + actionpack (= 7.1.3.2) + activerecord (= 7.1.3.2) + activestorage (= 7.1.3.2) + activesupport (= 7.1.3.2) globalid (>= 0.6.0) nokogiri (>= 1.8.5) - actionview (7.0.4.3) - activesupport (= 7.0.4.3) + actionview (7.1.3.2) + activesupport (= 7.1.3.2) builder (~> 3.1) - erubi (~> 1.4) - rails-dom-testing (~> 2.0) - rails-html-sanitizer (~> 1.1, >= 1.2.0) - active_model_serializers (0.10.13) - actionpack (>= 4.1, < 7.1) - activemodel (>= 4.1, < 7.1) + erubi (~> 1.11) + rails-dom-testing (~> 2.2) + rails-html-sanitizer (~> 1.6) + active_model_serializers (0.10.14) + actionpack (>= 4.1) + activemodel (>= 4.1) case_transform (>= 0.2) jsonapi-renderer (>= 0.1.1.beta1, < 0.3) active_record_union (1.3.0) activerecord (>= 4.0) - activejob (7.0.4.3) - activesupport (= 7.0.4.3) + activejob (7.1.3.2) + activesupport (= 7.1.3.2) globalid (>= 0.3.6) - activemodel (7.0.4.3) - activesupport (= 7.0.4.3) - activerecord (7.0.4.3) - activemodel (= 7.0.4.3) - activesupport (= 7.0.4.3) - activerecord-nulldb-adapter (0.9.0) - activerecord (>= 5.2.0, < 7.1) - activestorage (7.0.4.3) - actionpack (= 7.0.4.3) - activejob (= 7.0.4.3) - activerecord (= 7.0.4.3) - activesupport (= 7.0.4.3) + activemodel (7.1.3.2) + activesupport (= 7.1.3.2) + activerecord (7.1.3.2) + activemodel (= 7.1.3.2) + activesupport (= 7.1.3.2) + timeout (>= 0.4.0) + activerecord-nulldb-adapter (1.0.1) + activerecord (>= 5.2.0, < 7.2) + activestorage (7.1.3.2) + actionpack (= 7.1.3.2) + activejob (= 7.1.3.2) + activerecord (= 7.1.3.2) + activesupport (= 7.1.3.2) marcel (~> 1.0) - mini_mime (>= 1.1.0) - activesupport (7.0.4.3) + activesupport (7.1.3.2) + base64 + bigdecimal concurrent-ruby (~> 1.0, >= 1.0.2) + connection_pool (>= 2.2.5) + drb i18n (>= 1.6, < 2) minitest (>= 5.1) + mutex_m tzinfo (~> 2.0) acts_as_list (1.1.0) activerecord (>= 4.2) acts_as_tree (2.9.1) activerecord (>= 3.0.0) acts_as_votable (0.14.0) - addressable (2.8.2) + addressable (2.8.6) public_suffix (>= 2.0.2, < 6.0) afm (0.2.2) ast (2.4.2) - autoprefixer-rails (10.4.13.0) + autoprefixer-rails (10.4.16.0) execjs (~> 2) babel-source (5.8.35) babel-transpiler (0.7.0) babel-source (>= 4.0, < 6) execjs (~> 2.0) - barby (0.6.8) - bcrypt (3.1.18) + barby (0.6.9) + base64 (0.2.0) + bcrypt (3.1.20) + bigdecimal (3.1.7) bindex (0.8.1) - bootsnap (1.16.0) + bootsnap (1.18.3) msgpack (~> 1.2) - bootstrap (5.3.1) + bootstrap (5.3.2) autoprefixer-rails (>= 9.1.0) popper_js (>= 2.11.8, < 3) - sassc-rails (>= 2.0.0) - bootstrap_form (5.1.0) - actionpack (>= 5.2) - activemodel (>= 5.2) + bootstrap_form (5.4.0) + actionpack (>= 6.1) + activemodel (>= 6.1) builder (3.2.4) byebug (11.1.3) cancancan (3.5.0) case_transform (0.2) activesupport + childprocess (5.0.0) choice (0.2.0) chunky_png (1.4.0) clipboard-rails (1.7.1) @@ -157,12 +168,12 @@ GEM coffee-script-source execjs coffee-script-source (1.12.2) - commontator (7.0.0) + commontator (7.0.1) rails (>= 6.0) sprockets-rails will_paginate - concurrent-ruby (1.2.2) - connection_pool (2.4.0) + concurrent-ruby (1.2.3) + connection_pool (2.4.1) content_disposition (1.0.0) coveralls (0.7.1) multi_json (~> 1.3) @@ -171,48 +182,48 @@ GEM term-ansicolor thor crass (1.0.6) - css_parser (1.14.0) + css_parser (1.17.1) addressable - cypress-on-rails (1.13.1) + cypress-on-rails (1.17.0) rack - dalli (3.2.4) + dalli (3.2.8) database_cleaner (2.0.2) database_cleaner-active_record (>= 2, < 3) database_cleaner-active_record (2.1.0) activerecord (>= 5.a) database_cleaner-core (~> 2.0.0) database_cleaner-core (2.0.1) - date (3.3.3) + date (3.3.4) db_text_search (1.0.0) activerecord (>= 4.1.15) - devise (4.9.1) + devise (4.9.4) bcrypt (~> 3.0) orm_adapter (~> 0.1) railties (>= 4.1.0) responders warden (~> 1.2.3) devise-bootstrap-views (1.1.0) - diff-lcs (1.5.0) + diff-lcs (1.5.1) docile (1.4.0) - domain_name (0.5.20190701) - unf (>= 0.0.5, < 1.0.0) - down (5.4.0) + domain_name (0.6.20240107) + down (5.4.1) addressable (~> 2.8) + drb (2.2.1) erubi (1.12.0) erubis (2.7.0) - et-orbi (1.2.7) + et-orbi (1.2.11) tzinfo exception_handler (0.8.0.2) bundler rails (>= 4.2.0) responders - execjs (2.8.1) - factory_bot (6.2.1) + execjs (2.9.1) + factory_bot (6.4.6) activesupport (>= 5.0.0) - factory_bot_rails (6.2.0) - factory_bot (~> 6.2.0) + factory_bot_rails (6.4.3) + factory_bot (~> 6.4) railties (>= 5.0.0) - faker (3.1.1) + faker (3.3.1) i18n (>= 1.8.11, < 2) faraday (1.10.3) faraday-em_http (~> 1.0) @@ -237,24 +248,18 @@ GEM faraday-patron (1.0.0) faraday-rack (1.0.0) faraday-retry (1.0.3) - fastimage (2.2.6) - ffi (1.15.5) + fastimage (2.3.1) + ffi (1.16.3) filesize (0.2.0) - friendly_id (5.5.0) + friendly_id (5.5.1) activerecord (>= 4.0.0) - fugit (1.8.1) + fugit (1.10.1) et-orbi (~> 1, >= 1.2.7) raabro (~> 1.4) fuzzy-string-match (1.0.1) RubyInline (>= 3.8.6) - globalid (1.1.0) - activesupport (>= 5.0) - globalize (6.2.1) - activemodel (>= 4.2, < 7.1) - activerecord (>= 4.2, < 7.1) - request_store (~> 1.0) - globalize-accessors (0.3.0) - globalize (>= 5.0.0) + globalid (1.2.1) + activesupport (>= 6.1) hashery (2.1.2) highline (2.1.0) html-pipeline (2.14.3) @@ -264,7 +269,7 @@ GEM http-accept (1.7.0) http-cookie (1.0.5) domain_name (~> 0.5) - i18n (1.14.1) + i18n (1.14.4) concurrent-ruby (~> 1.0) image_processing (1.12.2) mini_magick (>= 4.9.5, < 5) @@ -272,19 +277,23 @@ GEM inline_svg (1.9.0) activesupport (>= 3.0) nokogiri (>= 1.6) + io-console (0.7.2) + irb (1.12.0) + rdoc + reline (>= 0.4.2) jbuilder (2.11.5) actionview (>= 5.0.0) activesupport (>= 5.0.0) - jquery-rails (4.5.1) + jquery-rails (4.6.0) rails-dom-testing (>= 1, < 3) railties (>= 4.2.0) thor (>= 0.14, < 2.0) - jquery-ui-rails (6.0.1) + jquery-ui-rails (7.0.0) railties (>= 3.2.16) js-routes (1.4.9) railties (>= 4) sprockets-rails - json (2.6.3) + json (2.7.2) jsonapi-renderer (0.2.2) kaminari (1.2.2) activesupport (>= 4.1.0) @@ -301,8 +310,8 @@ GEM kaminari-i18n (0.5.0) kaminari rails - katex (0.9.0) - execjs (~> 2.7) + katex (0.10.0) + execjs (~> 2.8) kramdown (2.4.0) rexml kramdown-math-katex (1.0.1) @@ -311,12 +320,13 @@ GEM kramdown-parser-gfm (1.1.0) kramdown (~> 2.0) language_server-protocol (3.17.0.3) - launchy (2.5.2) + launchy (3.0.0) addressable (~> 2.8) - listen (3.0.8) - rb-fsevent (~> 0.9, >= 0.9.4) - rb-inotify (~> 0.9, >= 0.9.7) - loofah (2.21.3) + childprocess (~> 5.0) + listen (3.9.0) + rb-fsevent (~> 0.10, >= 0.10.3) + rb-inotify (~> 0.9, >= 0.9.10) + loofah (2.22.0) crass (~> 1.0.2) nokogiri (>= 1.12.0) mail (2.8.1) @@ -324,30 +334,33 @@ GEM net-imap net-pop net-smtp - marcel (1.0.2) - method_source (1.0.0) - mime-types (3.4.1) + marcel (1.0.4) + mime-types (3.5.2) mime-types-data (~> 3.2015) - mime-types-data (3.2023.0218.1) + mime-types-data (3.2024.0305) mini_magick (4.12.0) - mini_mime (1.1.2) - minitest (5.19.0) - msgpack (1.7.0) + mini_mime (1.1.5) + minitest (5.22.3) + mobility (1.2.9) + i18n (>= 0.6.10, < 2) + request_store (~> 1.0) + msgpack (1.7.2) multi_json (1.15.0) - multipart-post (2.3.0) + multipart-post (2.4.0) mustache (1.1.1) - net-imap (0.3.4) + mutex_m (0.2.0) + net-imap (0.4.10) date net-protocol net-pop (0.1.2) net-protocol - net-protocol (0.2.1) + net-protocol (0.2.2) timeout - net-smtp (0.3.3) + net-smtp (0.5.0) net-protocol netrc (0.11.0) - nio4r (2.5.8) - nokogiri (1.15.4-x86_64-linux) + nio4r (2.7.1) + nokogiri (1.16.4-x86_64-linux) racc (~> 1.4) onebox (2.2.19) addressable (~> 2.8.0) @@ -358,22 +371,22 @@ GEM sanitize options (2.3.2) orm_adapter (0.5.0) - pairing_heap (3.0.0) - parallel (1.23.0) - parser (3.2.2.4) + pairing_heap (3.1.0) + parallel (1.24.0) + parser (3.3.0.5) ast (~> 2.4.1) racc - pdf-reader (2.11.0) + pdf-reader (2.12.0) Ascii85 (~> 1.0) afm (~> 0.2.1) hashery (~> 2.0) ruby-rc4 ttfunk - pg (1.4.6) - pgreset (0.3) + pg (1.5.6) + pgreset (0.4) popper_js (2.11.8) pr_geohash (1.0.0) - premailer (1.21.0) + premailer (1.23.0) addressable css_parser (>= 1.12.0) htmlentities (>= 4.0.0) @@ -384,34 +397,41 @@ GEM progress_bar (1.3.3) highline (>= 1.6, < 3) options (~> 2.3.0) - prometheus_exporter (2.0.8) + prometheus_exporter (2.1.0) webrick - public_suffix (5.0.1) - puma (6.3.1) + psych (5.1.2) + stringio + public_suffix (5.0.5) + puma (6.4.2) nio4r (~> 2.0) - pundit (2.3.0) + pundit (2.3.1) activesupport (>= 3.0.0) raabro (1.4.0) - racc (1.7.1) - rack (2.2.8) - rack-proxy (0.7.6) + racc (1.7.3) + rack (3.0.10) + rack-proxy (0.7.7) rack + rack-session (2.0.0) + rack (>= 3.0.0) rack-test (2.1.0) rack (>= 1.3) - rails (7.0.4.3) - actioncable (= 7.0.4.3) - actionmailbox (= 7.0.4.3) - actionmailer (= 7.0.4.3) - actionpack (= 7.0.4.3) - actiontext (= 7.0.4.3) - actionview (= 7.0.4.3) - activejob (= 7.0.4.3) - activemodel (= 7.0.4.3) - activerecord (= 7.0.4.3) - activestorage (= 7.0.4.3) - activesupport (= 7.0.4.3) + rackup (2.1.0) + rack (>= 3) + webrick (~> 1.8) + rails (7.1.3.2) + actioncable (= 7.1.3.2) + actionmailbox (= 7.1.3.2) + actionmailer (= 7.1.3.2) + actionpack (= 7.1.3.2) + actiontext (= 7.1.3.2) + actionview (= 7.1.3.2) + activejob (= 7.1.3.2) + activemodel (= 7.1.3.2) + activerecord (= 7.1.3.2) + activestorage (= 7.1.3.2) + activesupport (= 7.1.3.2) bundler (>= 1.15.0) - railties (= 7.0.4.3) + railties (= 7.1.3.2) rails-dom-testing (2.2.0) activesupport (>= 5.0.0) minitest @@ -424,29 +444,34 @@ GEM rails-html-sanitizer (1.6.0) loofah (~> 2.21) nokogiri (~> 1.14) - rails-i18n (7.0.6) + rails-i18n (7.0.9) i18n (>= 0.7, < 2) railties (>= 6.0.0, < 8) rails_gravatar (1.0.4) actionview - railties (7.0.4.3) - actionpack (= 7.0.4.3) - activesupport (= 7.0.4.3) - method_source + railties (7.1.3.2) + actionpack (= 7.1.3.2) + activesupport (= 7.1.3.2) + irb + 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) + rake (13.2.1) rb-fsevent (0.11.2) rb-inotify (0.10.1) ffi (~> 1.0) - redis-client (0.14.1) + rdoc (6.6.3.1) + psych (>= 4.0.0) + redis-client (0.22.1) connection_pool - regexp_parser (2.8.2) - request_store (1.5.1) + regexp_parser (2.9.0) + reline (0.5.2) + io-console (~> 0.5) + request_store (1.6.0) rack (>= 1.4) - responders (3.1.0) + responders (3.1.1) actionpack (>= 5.2) railties (>= 5.2) rest-client (2.1.0) @@ -454,65 +479,66 @@ GEM http-cookie (>= 1.0.2, < 2.0) mime-types (>= 1.16, < 4.0) netrc (~> 0.8) - rexml (3.2.5) - rgl (0.6.2) - pairing_heap (>= 0.3.0) + rexml (3.2.6) + rgl (0.6.6) + pairing_heap (>= 0.3, < 4.0) rexml (~> 3.2, >= 3.2.4) stream (~> 0.5.3) rinku (2.0.6) - rqrcode (2.1.2) + rqrcode (2.2.0) chunky_png (~> 1.0) rqrcode_core (~> 1.0) rqrcode_core (1.2.0) - rsolr (2.5.0) + rsolr (2.6.0) builder (>= 2.1.2) faraday (>= 0.9, < 3, != 2.0.0) - rspec-core (3.12.1) - rspec-support (~> 3.12.0) - rspec-expectations (3.12.2) + rspec-core (3.13.0) + rspec-support (~> 3.13.0) + rspec-expectations (3.13.0) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.12.0) - rspec-mocks (3.12.5) + rspec-support (~> 3.13.0) + rspec-mocks (3.13.0) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.12.0) - rspec-rails (6.0.1) + rspec-support (~> 3.13.0) + rspec-rails (6.1.2) actionpack (>= 6.1) activesupport (>= 6.1) railties (>= 6.1) - rspec-core (~> 3.11) - rspec-expectations (~> 3.11) - rspec-mocks (~> 3.11) - rspec-support (~> 3.11) - rspec-support (3.12.0) - rubocop (1.57.2) + rspec-core (~> 3.13) + rspec-expectations (~> 3.13) + rspec-mocks (~> 3.13) + rspec-support (~> 3.13) + rspec-support (3.13.1) + rubocop (1.63.2) json (~> 2.3) language_server-protocol (>= 3.17.0) parallel (~> 1.10) - parser (>= 3.2.2.4) + parser (>= 3.3.0.2) rainbow (>= 2.2.2, < 4.0) regexp_parser (>= 1.8, < 3.0) rexml (>= 3.2.5, < 4.0) - rubocop-ast (>= 1.28.1, < 2.0) + rubocop-ast (>= 1.31.1, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 2.4.0, < 3.0) - rubocop-ast (1.30.0) - parser (>= 3.2.1.0) - rubocop-performance (1.19.1) - rubocop (>= 1.7.0, < 2.0) - rubocop-ast (>= 0.4.0) - rubocop-rails (2.22.1) + rubocop-ast (1.31.2) + parser (>= 3.3.0.4) + rubocop-performance (1.21.0) + rubocop (>= 1.48.1, < 2.0) + rubocop-ast (>= 1.31.1, < 2.0) + rubocop-rails (2.24.1) activesupport (>= 4.2.0) rack (>= 1.1) rubocop (>= 1.33.0, < 2.0) + rubocop-ast (>= 1.31.1, < 2.0) ruby-graphviz (1.2.5) rexml ruby-progressbar (1.13.0) ruby-rc4 (0.1.5) - ruby-vips (2.1.4) + ruby-vips (2.2.1) ffi (~> 1.12) ruby2_keywords (0.0.5) rubyzip (2.3.2) - sanitize (6.0.1) + sanitize (6.1.0) crass (~> 1.0.2) nokogiri (>= 1.12.0) sass-rails (6.0.0) @@ -525,20 +551,20 @@ GEM sprockets (> 3.0) sprockets-rails tilt - selenium-webdriver (4.8.6) + selenium-webdriver (4.10.0) rexml (~> 3.2, >= 3.2.5) rubyzip (>= 1.2.2, < 3.0) websocket (~> 1.0) semantic_range (3.0.0) - shrine (3.4.0) + shrine (3.5.0) content_disposition (~> 1.0) down (~> 5.1) - sidekiq (7.0.9) + sidekiq (7.2.2) concurrent-ruby (< 2) connection_pool (>= 2.3.0) rack (>= 2.2.4) - redis-client (>= 0.11.0) - sidekiq-cron (1.10.0) + redis-client (>= 0.19.0) + sidekiq-cron (1.12.0) fugit (~> 1.8) globalid (>= 1.0.1) sidekiq (>= 6) @@ -555,27 +581,28 @@ GEM spring-watcher-listen (2.0.1) listen (>= 2.7, < 4.0) spring (>= 1.2, < 3.0) - sprockets (4.2.0) + sprockets (4.2.1) concurrent-ruby (~> 1.0) rack (>= 2.2.4, < 4) sprockets-es6 (0.9.2) babel-source (>= 5.8.11) babel-transpiler sprockets (>= 3.0.0) - sqlite3 (1.6.2-x86_64-linux) + sqlite3 (1.7.3-x86_64-linux) stream (0.5.5) streamio-ffmpeg (3.0.2) multi_json (~> 1.8) + stringio (3.1.0) sunspot (2.6.0) pr_geohash (~> 1.0) rsolr (>= 1.1.1, < 3) sunspot_solr (2.6.0) sync (0.5.0) - term-ansicolor (1.7.1) + term-ansicolor (1.8.0) tins (~> 1.0) - terser (1.1.14) + terser (1.2.2) execjs (>= 0.3.0, < 3) - thor (1.2.2) + thor (1.3.1) thredded (1.1.0) active_record_union (>= 1.3.0) autoprefixer-rails @@ -597,46 +624,44 @@ GEM sassc-rails (>= 2.0.0) sprockets-es6 timeago_js (>= 3.0.2.2) - tilt (2.2.0) + tilt (2.3.0) timeago_js (3.0.2.2) - timeout (0.3.2) + timeout (0.4.1) tins (1.32.1) sync trix-rails (2.4.0) rails (> 4.1) - ttfunk (1.7.0) + ttfunk (1.8.0) + bigdecimal (~> 3.1) turbolinks (5.2.1) turbolinks-source (~> 5.2) turbolinks-source (5.2.0) tzinfo (2.0.6) concurrent-ruby (~> 1.0) - unf (0.1.4) - unf_ext - unf_ext (0.0.8.2) unicode-display_width (2.5.0) warden (1.2.9) rack (>= 2.0.9) - web-console (4.2.0) + web-console (4.2.1) actionview (>= 6.0.0) activemodel (>= 6.0.0) bindex (>= 0.4.0) railties (>= 6.0.0) - webdrivers (5.2.0) + webdrivers (5.3.1) nokogiri (~> 1.6) rubyzip (>= 1.3.0) - selenium-webdriver (~> 4.0) + selenium-webdriver (~> 4.0, < 4.11) webpacker (5.4.4) activesupport (>= 5.2) rack-proxy (>= 0.6.1) railties (>= 5.2) semantic_range (>= 2.3.0) webrick (1.8.1) - websocket (1.2.9) - websocket-driver (0.7.5) + websocket (1.2.10) + websocket-driver (0.7.6) websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) - will_paginate (3.3.1) - zeitwerk (2.6.11) + will_paginate (4.0.0) + zeitwerk (2.6.13) PLATFORMS x86_64-linux @@ -671,8 +696,7 @@ DEPENDENCIES fastimage filesize fuzzy-string-match - globalize - globalize-accessors + html-pipeline (~> 2.14) image_processing jbuilder jquery-rails @@ -682,9 +706,10 @@ DEPENDENCIES kaminari-i18n kramdown-parser-gfm launchy - listen (>= 3.0.5, < 3.2) + listen (~> 3.9) marcel mini_magick + mobility net-smtp pdf-reader pg @@ -694,16 +719,16 @@ DEPENDENCIES prometheus_exporter puma (< 7) rack - rails (~> 7.0.4.3) + rails (~> 7.1.3) rails-erd rails-i18n responders rgl rqrcode rspec-rails - rubocop (~> 1.57) - rubocop-performance (~> 1.16) - rubocop-rails (~> 2.22, >= 2.22.1) + rubocop (~> 1.63) + rubocop-performance (~> 1.21) + rubocop-rails (~> 2.24) rubyzip (~> 2.3.0) sass-rails (>= 6) selenium-webdriver @@ -732,4 +757,4 @@ RUBY VERSION ruby 3.1.4p223 BUNDLED WITH - 2.3.13 + 2.5.9 diff --git a/README.md b/README.md index 3cb0c5187..8406cea21 100644 --- a/README.md +++ b/README.md @@ -1,69 +1,121 @@ -

- +

+ + MaMpf Logo + +
+

MaMpf

+

+ Mathematical Media Platform for universities +

+
+
+ Website (🇩🇪/🇺🇸) + | Blog (🇩🇪) + | User docs (🇩🇪) + | Dev wiki (🇺🇸) +
Create your own free account here. + Note the user docs are outdated with respect to the screenshots showcased over there. +
+
-

MaMpf

-

Mathematische Medienplattform

-

+## 💡 About / Motivation -## 💡 About +MaMpf is an innovative open source e-learning platform for the mathematical sciences developed at the [Institute for Mathematics at Heidelberg University](https://www.math.uni-heidelberg.de/en). It's actively used in teaching and learning; you can [register for free here](https://mampf.mathi.uni-heidelberg.de/) (no student email required). Our platform is fully available in English & German. -**MaMpf (*Mathematische Medienplattform*)** is an innovative open source E-Learning platform for the mathematical sciences. -Central point is the interconnection between different content in the sense -of a hypermedia system. + + MaMpf landing page used to log in + -MaMpf uses the contextual classification of a course as visual leitmotiv, -instead of organizational aspects. +MaMpf aims to be a hypermedia system for mathematical content. Like _moodle_, it provides a platform for lecturers to upload & organize their teaching material including videos and scripts. But MaMpf goes beyond that and eases learning through interconnected contents: +- 🎞 **Lecture videos** can be enriched with a navigation that allows students to jump to specific parts of the video, e.g. mathematical definitions, theorems, examples etc. References to other media are also possible, e.g. to different lecture videos / quizzes / worked examples etc. +- 🏷 Any media can be **tagged** with keywords. This allows students to easily find content related to a specific topic and discover how items are connected in a **graph view**. +- 🕹 Interactive **quizzes** allow students to test their understanding of the material. The system can automatically evaluate the answers and provide direct feedback, e.g. explain why an answer is wrong or provide a link to the relevant part of the video or an additional "worked example" video. +- 👩‍🏫 Students can sign up for tutorials and form teams themselves. Tutors are then able to manage the groups and upload corrected homework assignments for their students. +- 🗨 A **comment system** allows students to ask questions about the material in the context of the specific video/script or in a general forum. Lecturers will get a notification when a new comment is posted (of course adjustable). Students may choose their own alias name when posting comments in order to stay anonymous. -![mampf-gui](public/mampf-gui-transparent.png) +This is just a brief overview of the feature set. You may think of MaMpf as a mix of _Moodle_, _Khan Academy_ and _YouTube_. But it's more than that as features are tailored to the needs of the mathematical sciences and a university context. Start exploring MaMpf [here](https://mampf.mathi.uni-heidelberg.de/). -MaMpf comes with its own hypermedia player and editor THymE -(*The hypermedia Experience*). ThymeE uses the internal structure of -mathematical content (consisting of theorems, remarks, definitions etc.) and allows -exact navigation between content that is related, but temporally apart. -References can be created not only to content within the same video, but within -the whole MaMpf database. -![thyme](public/thyme.png) +## 📷 Screenshots -ThymE is lean and makes use of WebVTT and HTML5 video capabilites -of modern browsers. A sample hypervideo can be found -[here](https://mampf.mathi.uni-heidelberg.de/media/384/play). +To give you a closer look, here are some **screenshots** taken from our live system: -MaMpf is equipped with a tagging system and rich visualisations for content relations, -making use of [cytoscape.js](http://js.cytoscape.org/). +
+ Video player + + Try out the video player [here](https://mampf.mathi.uni-heidelberg.de/media/384/play) (even without any account). Press `i` to open the outline on the right. It can hold references to other parts of the video or other items in the whole MaMpf database. The player makes use of WebVTT and HTML5 video capabilities of modern browsers. -![tags](public/tag_visualisation.png) + + MaMpf video player + +
-MaMpf has a quiz system that allows you to create complex quizzes quite easily. +
+ Courses overview + + Here, users can select courses from the current semester or from previous ones. -![quizzes](public/quizzes.png) + ![User courses view](https://github.com/MaMpf-HD/mampf/assets/37160523/a1e386ad-7642-49f2-aecf-f2f0722cc3c1) +
-MaMpf makes use of the JS based symbolic math expression evaluator -[nerdamer](https://github.com/jiggzson/nerdamer) to parse student's input in quizzes. +
+ Lectures overview + + In the lectures view, users can click on a lecture to see the video. + ![User lectures view](https://github.com/MaMpf-HD/mampf/assets/37160523/a3936d73-dc45-489d-85f8-68326f61654a) +
-For more information see this [blog](https://mampfdev.wordpress.com). -There you can also find a [screenshot gallery](https://mampfdev.wordpress.com/gallery/). -## System background +
+ Graph tag search + + MaMpf is equipped with a tagging system and rich visualizations for content relations, making use of [cytoscape.js](http://js.cytoscape.org/). -[![MaMpf](https://img.shields.io/endpoint?url=https://dashboard.cypress.io/badge/simple/v45wg9/main&style=flat&logo=cypress)](https://dashboard.cypress.io/projects/v45wg9/runs) -[![codecov](https://codecov.io/gh/MaMpf-HD/mampf/branch/main/graph/badge.svg?token=x7Zq3m5lVH)](https://codecov.io/gh/MaMpf-HD/mampf) + ![Search graph](https://github.com/MaMpf-HD/mampf/assets/37160523/cd54b651-70c0-439d-a8dd-01de95995cb5) +
-MaMpf is implemented in Ruby on Rails. +
+ Quizzes -* Ruby version: 3.1.4 -* Rails Version: 7.0.4.3 -* Test suite: rspec, cypress -* support for I18n + Users can play quizzes in MaMpf and get immediate feedback. In order to parse student's input in quizzes (e.g. when they enter a symbolic expression), MaMpf makes use of the JS based symbolic math expression evaluator [nerdamer](https://github.com/jiggzson/nerdamer). -## 💻 Installation (with docker compose) + ![playing a quiz](https://github.com/MaMpf-HD/mampf/assets/37160523/baa3ae6d-e7bf-4ecc-9db0-22cab367d4ee) -To easily try out MaMpf you can use `docker compose`. Clone the MaMpf repository and run `docker compose`: + Lecturers can create quizzes and edit them in a graph: -``` + ![admin view for a quiz](https://github.com/MaMpf-HD/mampf/assets/37160523/855089b4-9358-4ff5-a9b0-d1aa89962c20) +
+ +
+ Comments + + Users can post comments directly on videos. LaTeX is supported and rendered via [KaTeX](https://katex.org/). + + ![posting a comment](https://github.com/MaMpf-HD/mampf/assets/37160523/5ee4b51c-5ea5-4cf5-bf25-a0048434cb1f) +
+ + + + + +## 💻 Installation + +MaMpf is a **Ruby on Rails** application with a **PostgreSQL** database. For our frontend styling, we rely on **Bootstrap**. Our [website](https://mampf.mathi.uni-heidelberg.de/) is hosted on a server at Heidelberg University. We use docker (compose) for development and deployment. + +MaMpf is actively developed & maintained. If you are interested in using MaMpf at your university, please get [in touch](mailto:mampf@mathi.uni-heidelberg.de). But please note that we're a very small team and can't provide support for setting up your own instance of MaMpf at the moment. Our [installation guide](./INSTALL.md) should be a good starting point. We have to admit, though, that getting your own instance up and running might involve quite some effort including setting up a mail server, the database, SSL certificates, an nginx web server / proxy, deploying the Ruby on Rails application, and more. + +To clone the source code and build MaMpf locally with `docker compose`, run these commands: + +```bash git clone -b main --recursive https://github.com/MaMpf-HD/mampf.git cd mampf/docker/development/ -docker compose up +docker compose up -d ``` -See the full installation guide [here](./INSTALL.md). +See the full installation guide [here](./INSTALL.md). There you will also find out how to init your local database with some sample data. + + + MaMpf footer + diff --git a/app/assets/javascripts/lectures.coffee b/app/assets/javascripts/lectures.coffee index e8a0f5587..6e8d60759 100644 --- a/app/assets/javascripts/lectures.coffee +++ b/app/assets/javascripts/lectures.coffee @@ -136,7 +136,6 @@ $(document).on 'turbolinks:load', -> tags = $(this).data('tags') for t in tags $('.lecture-tag[data-id="'+t+'"]').removeClass('bg-warning') - .addClass('bg-light') return # mouseenter over lesson -> colorize tags @@ -203,30 +202,6 @@ $(document).on 'turbolinks:load', -> userModalContent.dataset.filled = 'true' return - # Dynamically render content for entering emergency links. - updateEmergencyLink = (value) -> - if value == "no_link" - $('#direct-link-field').hide() - $('#lecture-link-field').hide() - if value == "lecture_link" - $('#direct-link-field').hide() - $('#lecture-link-field').show() - if value == "direct_link" - $('#lecture-link-field').hide() - $('#direct-link-field').show() - return - - emergencyLinkRadios = document.getElementById('emergency-link-status-radios') - - if (emergencyLinkRadios != null) - $('#emergency-link-status-radios input:radio:checked').each -> - updateEmergencyLink(this.value) - return - emergencyLinkRadios.addEventListener 'click', (evt) -> - if evt.target && event.target.matches("input[type='radio']") - updateEmergencyLink(evt.target.value) - return - # on small mobile display, use shortened tag badges and # shortened course titles mobileDisplay = -> diff --git a/app/assets/javascripts/thyme/thyme_editor.js b/app/assets/javascripts/thyme/thyme_editor.js index a86f1c371..2d3346d0e 100644 --- a/app/assets/javascripts/thyme/thyme_editor.js +++ b/app/assets/javascripts/thyme/thyme_editor.js @@ -12,6 +12,16 @@ $(document).on("turbolinks:load", function () { thymeAttributes.video = video; thymeAttributes.mediumId = thymeEdit.dataset.medium; + const canvasId = "snapshot"; + + // Adjust the width of the canvas according to the video + // such that screenshot generation is performed with the same ratio. + video.addEventListener("loadedmetadata", () => { + this.canvas = document.getElementById(canvasId); + this.canvas.width = Math.floor($(video).width()); + this.canvas.height = Math.floor($(video).height()); + }); + /* COMPONENTS */ @@ -30,7 +40,7 @@ $(document).on("turbolinks:load", function () { (new AddItemButton("add-item")).add(); (new AddReferenceButton("add-reference")).add(); - (new AddScreenshotButton("add-screenshot", "snapshot")).add(); + (new AddScreenshotButton("add-screenshot", canvasId)).add(); thymeUtility.setUpMaxTime("max-time"); }); diff --git a/app/assets/stylesheets/annotations.scss b/app/assets/stylesheets/annotations.scss index 02b3fc257..946dd05aa 100644 --- a/app/assets/stylesheets/annotations.scss +++ b/app/assets/stylesheets/annotations.scss @@ -104,10 +104,6 @@ } } -#emergency-link { - text-align: center; -} - .annotation-marker { position: relative; top: -12px; diff --git a/app/controllers/commontator/comments_controller.rb b/app/controllers/commontator/comments_controller.rb index 331919345..43d668ecb 100644 --- a/app/controllers/commontator/comments_controller.rb +++ b/app/controllers/commontator/comments_controller.rb @@ -156,7 +156,7 @@ def upvote # PUT /comments/1/downvote def downvote - security_transgression_unless(@comment.can_be_voted_on_by?(@commontator_user) && \ + security_transgression_unless(@comment.can_be_voted_on_by?(@commontator_user) && @comment.thread.config.comment_voting.to_sym == :ld) @comment.downvote_from(@commontator_user) diff --git a/app/controllers/courses_controller.rb b/app/controllers/courses_controller.rb index bfe9704a9..40b51e207 100644 --- a/app/controllers/courses_controller.rb +++ b/app/controllers/courses_controller.rb @@ -94,13 +94,12 @@ def set_course_admin end def course_params - params.require(:course).permit(:title, :short_title, :organizational, - :organizational_concept, :locale, - :term_independent, :image, - tag_ids: [], - preceding_course_ids: [], - editor_ids: [], - division_ids: []) + allowed_params = [:title, :short_title, :organizational, + :organizational_concept, :locale, + :term_independent, :image, + { tag_ids: [], preceding_course_ids: [], division_ids: [] }] + allowed_params.push(editor_ids: []) if current_user.admin? + params.require(:course).permit(allowed_params) end def tag_params diff --git a/app/controllers/divisions_controller.rb b/app/controllers/divisions_controller.rb index dc2195d75..03de7eb0c 100644 --- a/app/controllers/divisions_controller.rb +++ b/app/controllers/divisions_controller.rb @@ -43,6 +43,6 @@ def set_division end def division_params - params.require(:division).permit(*Division.globalize_attribute_names) + params.require(:division).permit(*Division.locale_accessor_names) end end diff --git a/app/controllers/lectures_controller.rb b/app/controllers/lectures_controller.rb index ff1e44734..10682539f 100644 --- a/app/controllers/lectures_controller.rb +++ b/app/controllers/lectures_controller.rb @@ -62,15 +62,6 @@ def edit Time.zone.parse(ENV.fetch("RAILS_CACHE_ID", nil))].max) eager_load_stuff end - - # emergency link -> prefill form - status = @lecture.emergency_link_status_for_database - link = @lecture.emergency_link - if status == Lecture.emergency_link_statuses[:lecture_link] - @linked_lecture = Lecture.find_by(id: link.tr("^[0-9]", "")) - elsif status == Lecture.emergency_link_statuses[:direct_link] - @direct_link = link - end end def create @@ -121,19 +112,6 @@ def update end end - # emergency link update - status = params[:lecture][:emergency_link_status] # string - status = Lecture.emergency_link_statuses[status] - if status == Lecture.emergency_link_statuses[:lecture_link] - params[:lecture][:emergency_link] = params[:lecture][:lecture_link] - elsif status == Lecture.emergency_link_statuses[:direct_link] - link = params[:lecture][:direct_link] - # Prepend "https://" to link if not present to make it an absolute URL - # instead of a relative one. E.g. "example.com" -> "https://example.com". - link = "https://#{link}" unless link.start_with?("http") - params[:lecture][:emergency_link] = link - end - @lecture.update(lecture_params) if structure_params.present? structure_ids = structure_params.select { |_k, v| v.to_i == 1 }.keys @@ -345,8 +323,7 @@ def lecture_params :organizational_on_top, :disable_teacher_display, :content_mode, :passphrase, :sort, :comments_disabled, :submission_max_team_size, :submission_grace_period, - :annotations_status, :emergency_link, - :emergency_link_status] + :annotations_status] if action_name == "update" && current_user.can_update_personell?(@lecture) allowed_params.push(:teacher_id, { editor_ids: [] }) end diff --git a/app/controllers/programs_controller.rb b/app/controllers/programs_controller.rb index 28e9aab55..836423078 100644 --- a/app/controllers/programs_controller.rb +++ b/app/controllers/programs_controller.rb @@ -43,6 +43,6 @@ def set_program end def program_params - params.require(:program).permit(*Program.globalize_attribute_names) + params.require(:program).permit(*Program.locale_accessor_names) end end diff --git a/app/controllers/readers_controller.rb b/app/controllers/readers_controller.rb index 931b93a45..ed0a41de6 100644 --- a/app/controllers/readers_controller.rb +++ b/app/controllers/readers_controller.rb @@ -21,9 +21,8 @@ def update_all .map(&:commontator_thread) existing_readers = Reader.where(user: current_user, thread: threads) missing_thread_ids = threads.map(&:id) - existing_readers.pluck(:thread_id) - new_readers = [] - missing_thread_ids.each do |t| - new_readers << Reader.new(thread_id: t, user: current_user) + new_readers = missing_thread_ids.map do |t| + Reader.new(thread_id: t, user: current_user) end Reader.import new_readers Reader.where(user: current_user, thread: threads).touch_all diff --git a/app/controllers/subjects_controller.rb b/app/controllers/subjects_controller.rb index 29fa26cf4..2e9d0c439 100644 --- a/app/controllers/subjects_controller.rb +++ b/app/controllers/subjects_controller.rb @@ -42,6 +42,6 @@ def set_subject end def subject_params - params.require(:subject).permit(*Subject.globalize_attribute_names) + params.require(:subject).permit(*Subject.locale_accessor_names) end end diff --git a/app/helpers/media_helper.rb b/app/helpers/media_helper.rb index 6cc9c5140..fe432a51f 100644 --- a/app/helpers/media_helper.rb +++ b/app/helpers/media_helper.rb @@ -147,6 +147,6 @@ def edit_or_show_medium_path(medium) def external_link_description_not_empty(medium) # Uses link display name if not empty, otherwise falls back to the # link url itself. - (medium.external_link_description.presence || medium.external_reference_link) + medium.external_link_description.presence || medium.external_reference_link end end diff --git a/app/models/annotation.rb b/app/models/annotation.rb index b15b36052..6ee3ec804 100644 --- a/app/models/annotation.rb +++ b/app/models/annotation.rb @@ -7,7 +7,7 @@ class Annotation < ApplicationRecord scope :commented, -> { where.not(public_comment_id: nil) } # the timestamp for the annotation position is serialized as text in the db - serialize :timestamp, TimeStamp + serialize :timestamp, coder: TimeStamp enum category: { note: 0, content: 1, mistake: 2, presentation: 3 } enum subcategory: { definition: 0, argument: 1, strategy: 2 } diff --git a/app/models/division.rb b/app/models/division.rb index bab3b37ce..995a9af0d 100644 --- a/app/models/division.rb +++ b/app/models/division.rb @@ -2,12 +2,10 @@ class Division < ApplicationRecord belongs_to :program has_many :division_course_joins has_many :courses, through: :division_course_joins - + extend Mobility + extend I18nLocaleAccessors translates :name - globalize_accessors locales: I18n.available_locales, - attributes: translated_attribute_names - def name_with_program "#{program.subject.name}:#{program.name}:#{name}" end diff --git a/app/models/item.rb b/app/models/item.rb index c4778ca63..17f2b3cde 100644 --- a/app/models/item.rb +++ b/app/models/item.rb @@ -25,7 +25,7 @@ class Item < ApplicationRecord # an item that corresponds to a toc entry of a video has a start time # start_time is a TimeStamp object (which is serialized for the db) - serialize :start_time, TimeStamp + serialize :start_time, coder: TimeStamp # sort should be one of the following: # remark, ... , corollary - correspond to to toc entries of videos diff --git a/app/models/lecture.rb b/app/models/lecture.rb index 097bb4b52..4ad4a39a5 100644 --- a/app/models/lecture.rb +++ b/app/models/lecture.rb @@ -65,11 +65,7 @@ class Lecture < ApplicationRecord # a lecture has many structure_ids, referring to the ids of structures # in the erdbeere database - serialize :structure_ids, Array - - # if the annotation button is enabled, one can add different types of links - # that e.g. bring students to the helpdesk - enum emergency_link_status: { no_link: 0, lecture_link: 1, direct_link: 2 } + serialize :structure_ids, type: Array, coder: YAML # we do not allow that a teacher gives a certain lecture in a given term # of the same sort twice diff --git a/app/models/manuscript.rb b/app/models/manuscript.rb index 3316d5fb7..13317cb1d 100644 --- a/app/models/manuscript.rb +++ b/app/models/manuscript.rb @@ -148,19 +148,16 @@ def create_or_update_chapter_items! attrs = [:medium_id, :pdf_destination, :section_id, :sort, :page, :description, :ref_number, :position, :quarantine] item_details = items.pluck(*attrs).map { |i| attrs.zip(i).to_h } - contents = [] - @chapters.each do |c| - contents.push( - { medium_id: @medium.id, - pdf_destination: c["destination"], - section_id: nil, - sort: "chapter", - page: c["page"].to_i, - description: c["description"], - ref_number: c["label"], - position: nil, - quarantine: nil } - ) + contents = @chapters.map do |c| + { medium_id: @medium.id, + pdf_destination: c["destination"], + section_id: nil, + sort: "chapter", + page: c["page"].to_i, + description: c["description"], + ref_number: c["label"], + position: nil, + quarantine: nil } end create_or_update_items!(contents, item_details, item_destinations, item_id_map) @@ -176,21 +173,18 @@ def create_or_update_section_items! attrs = [:medium_id, :pdf_destination, :section_id, :sort, :page, :description, :ref_number, :position, :quarantine] item_details = items.pluck(*attrs).map { |i| attrs.zip(i).to_h } - contents = [] # NOTE: that sections get a position -1 in order to place them ahead # of all content items within themseleves in #script_items_by_position - @sections.each do |s| - contents.push( - { medium_id: @medium.id, - pdf_destination: s["destination"], - section_id: s["mampf_section"].id, - sort: "section", - page: s["page"].to_i, - description: s["description"], - ref_number: s["label"], - position: -1, - quarantine: nil } - ) + contents = @sections.map do |s| + { medium_id: @medium.id, + pdf_destination: s["destination"], + section_id: s["mampf_section"].id, + sort: "section", + page: s["page"].to_i, + description: s["description"], + ref_number: s["label"], + position: -1, + quarantine: nil } end create_or_update_items!(contents, item_details, item_destinations, item_id_map) @@ -208,22 +202,19 @@ def create_or_update_content_items!(filter_boxes) attrs = [:medium_id, :pdf_destination, :section_id, :sort, :page, :description, :ref_number, :position, :hidden, :quarantine] item_details = items.pluck(*attrs).map { |i| attrs.zip(i).to_h } - contents = [] - @content.each do |c| - contents.push( - { medium_id: @medium.id, - pdf_destination: c["destination"], - section_id: @sections.find do |s| - c["section"] == s["section"] - end ["mampf_section"]&.id, - sort: Item.internal_sort(c["sort"]), - page: c["page"].to_i, - description: c["description"], - ref_number: c["label"], - position: c["counter"], - hidden: filter_boxes[c["counter"]].third == false, - quarantine: nil } - ) + contents = @content.map do |c| + { medium_id: @medium.id, + pdf_destination: c["destination"], + section_id: @sections.find do |s| + c["section"] == s["section"] + end ["mampf_section"]&.id, + sort: Item.internal_sort(c["sort"]), + page: c["page"].to_i, + description: c["description"], + ref_number: c["label"], + position: c["counter"], + hidden: filter_boxes[c["counter"]].third == false, + quarantine: nil } end create_or_update_items!(contents, item_details, item_destinations, item_id_map) diff --git a/app/models/medium.rb b/app/models/medium.rb index f0f079536..fe7b8e191 100644 --- a/app/models/medium.rb +++ b/app/models/medium.rb @@ -56,11 +56,11 @@ class Medium < ApplicationRecord has_many :assignments - serialize :quiz_graph, QuizGraph + serialize :quiz_graph, coder: QuizGraph - serialize :solution, Solution + serialize :solution, coder: Solution - serialize :publisher, MediumPublisher + serialize :publisher, coder: MediumPublisher # include uploaders to realize video/manuscript/screenshot upload # this makes use of the shrine gem diff --git a/app/models/medium_publisher.rb b/app/models/medium_publisher.rb index 3607359e3..ea13cb70f 100644 --- a/app/models/medium_publisher.rb +++ b/app/models/medium_publisher.rb @@ -128,13 +128,12 @@ def realize_optional_stuff! # to the medium's teachable's media_scope def create_notifications! @medium.teachable&.media_scope&.touch - notifications = [] @medium.teachable.media_scope.users.touch_all - @medium.teachable.media_scope.users.each do |u| - notifications << Notification.new(recipient: u, - notifiable_id: @medium.id, - notifiable_type: "Medium", - action: "create") + notifications = @medium.teachable.media_scope.users.map do |u| + Notification.new(recipient: u, + notifiable_id: @medium.id, + notifiable_type: "Medium", + action: "create") end Notification.import notifications end diff --git a/app/models/notion.rb b/app/models/notion.rb index 15d339ef9..fbc1cd4e7 100644 --- a/app/models/notion.rb +++ b/app/models/notion.rb @@ -2,7 +2,7 @@ class Notion < ApplicationRecord belongs_to :tag, optional: true, touch: true belongs_to :aliased_tag, class_name: "Tag", optional: true, touch: true - validates :title, uniqueness: { scope: :locale } # rubocop:todo Rails/UniqueValidationWithoutIndex + validates :title, uniqueness: { scope: :locale } validates :title, presence: true validate :presence_of_tag, if: :persisted? diff --git a/app/models/program.rb b/app/models/program.rb index 1815a27a6..2502b5650 100644 --- a/app/models/program.rb +++ b/app/models/program.rb @@ -1,10 +1,9 @@ class Program < ApplicationRecord belongs_to :subject has_many :divisions, dependent: :destroy - + extend Mobility + extend I18nLocaleAccessors translates :name - globalize_accessors locales: I18n.available_locales, - attributes: translated_attribute_names def name_with_subject "#{subject.name}: #{name}" diff --git a/app/models/quiz.rb b/app/models/quiz.rb index 4d03afe8c..f4935b447 100644 --- a/app/models/quiz.rb +++ b/app/models/quiz.rb @@ -115,7 +115,7 @@ def preselected_hide_solution(vertex_id, crosses) def questions ids = quiz_graph&.vertices&.values&.select { |v| v[:type] == "Question" } - &.map { |v| v[:id] } + &.pluck(:id) Question.where(id: ids) end diff --git a/app/models/referral.rb b/app/models/referral.rb index 769d0a116..da95119cf 100644 --- a/app/models/referral.rb +++ b/app/models/referral.rb @@ -7,8 +7,8 @@ class Referral < ApplicationRecord belongs_to :medium # start_time and end_time are serialized columns - serialize :start_time, TimeStamp - serialize :end_time, TimeStamp + serialize :start_time, coder: TimeStamp + serialize :end_time, coder: TimeStamp # validations for start time and end time validate :valid_start_time @@ -33,7 +33,7 @@ def vtt_time_span # provide metadata for vtt file def vtt_properties - link = (item.link.presence || item.medium_link) + link = item.link.presence || item.medium_link # at the moment, relations between items can be only of the form # script <-> video, which means that between them there will be at most # one script, one manuscript and one video diff --git a/app/models/section.rb b/app/models/section.rb index 9c54309b4..11c651041 100644 --- a/app/models/section.rb +++ b/app/models/section.rb @@ -10,7 +10,7 @@ class Section < ApplicationRecord has_many :section_tag_joins, dependent: :destroy has_many :tags, through: :section_tag_joins # the tags have an ordering (an array with their ids) - serialize :tags_order, Array + serialize :tags_order, type: Array, coder: YAML # a section has many lessons has_many :lesson_section_joins, dependent: :destroy diff --git a/app/models/subject.rb b/app/models/subject.rb index 110504013..98f432f18 100644 --- a/app/models/subject.rb +++ b/app/models/subject.rb @@ -1,9 +1,8 @@ class Subject < ApplicationRecord has_many :programs - + extend Mobility + extend I18nLocaleAccessors translates :name - globalize_accessors locales: I18n.available_locales, - attributes: translated_attribute_names def deletable? programs.none? diff --git a/app/models/submission.rb b/app/models/submission.rb index 61c640286..ecdfa44b5 100644 --- a/app/models/submission.rb +++ b/app/models/submission.rb @@ -63,7 +63,7 @@ def correction_size end def preceding_tutorial(user) - assignment.previous&.map { |a| a.tutorial(user) }&.compact&.first + assignment.previous&.filter_map { |a| a.tutorial(user) }&.first end def invited_users diff --git a/app/models/tag.rb b/app/models/tag.rb index c92002ec0..dacbe1e91 100644 --- a/app/models/tag.rb +++ b/app/models/tag.rb @@ -39,7 +39,7 @@ class Tag < ApplicationRecord class_name: "Notion", inverse_of: :aliased_tag - serialize :realizations, Array + serialize :realizations, type: Array, coder: YAML accepts_nested_attributes_for :notions, reject_if: lambda { |attributes| @@ -184,13 +184,11 @@ def self.select_by_title_except(excluded_tags) # converts the subgraph of all tags of distance <= 2 to the given marked tag # into a cytoscape array representing this subgraph def self.to_cytoscape(tags, marked_tag, highlight_related_tags: true) - result = [] # add vertices - tags.each do |t| - result.push(data: t.cytoscape_vertex(marked_tag, - highlight_related_tags: - highlight_related_tags)) + result = tags.map do |t| + { data: t.cytoscape_vertex(marked_tag, highlight_related_tags: highlight_related_tags) } end + # add edges edges = [] tags.each do |t| diff --git a/app/models/term.rb b/app/models/term.rb index 28999bcea..1fe8d72bb 100644 --- a/app/models/term.rb +++ b/app/models/term.rb @@ -4,7 +4,7 @@ class Term < ApplicationRecord has_many :lectures # season can only be SS/WS, and there can be only one of this type each year - validates :season, presence: true, # rubocop:todo Rails/UniqueValidationWithoutIndex + validates :season, presence: true, inclusion: { in: ["SS", "WS"] }, uniqueness: { scope: :year } # a year >=2000 needs to be present diff --git a/app/views/annotations/_form_content_further_help.html.erb b/app/views/annotations/_form_content_further_help.html.erb deleted file mode 100644 index d2600963d..000000000 --- a/app/views/annotations/_form_content_further_help.html.erb +++ /dev/null @@ -1,19 +0,0 @@ - diff --git a/app/views/annotations/edit.js.erb b/app/views/annotations/edit.js.erb index 5c8db9ef2..c7b199108 100644 --- a/app/views/annotations/edit.js.erb +++ b/app/views/annotations/edit.js.erb @@ -116,27 +116,8 @@ function content() { contentCategoryRadios.addEventListener("click", function (evt) { if (evt.target && event.target.matches("input[type='radio']")) { submitButton.disabled = false; - - // Show further help - // (right now, the same help is display for all the different categories) - switch (evt.target.value) { - case Subcategory.DEFINITION.name: - showFurtherHelp(); - break; - case Subcategory.ARGUMENT.name: - showFurtherHelp(); - break; - case Subcategory.STRATEGY.name: - showFurtherHelp(); - break; - } } }); - - function showFurtherHelp() { - $("#content-specific").empty() - .append("<%= j render partial: "annotations/form_content_further_help"%>"); - } } function mistake() { diff --git a/app/views/courses/_basics.html.erb b/app/views/courses/_basics.html.erb index f2bbc8bbc..298e7c283 100644 --- a/app/views/courses/_basics.html.erb +++ b/app/views/courses/_basics.html.erb @@ -70,6 +70,9 @@ <% else %> <%= t('basics.editors') %> + <%= helpdesk(t('admin.course.info.no_right_to_change_editors', + project_email: mail_to(DefaultSetting::PROJECT_EMAIL)), + true) %>