From d9a02d1336dd86e7fff4f14735fb0f1970555abd Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sat, 23 May 2020 00:56:13 -0400 Subject: [PATCH] Revert "Revert "Merge branch 'master' of https://github.com/discourse/discourse"" This reverts commit 20780a1eeed56b321daf18ee6bbfe681a51d1bf4. * SECURITY: re-adds accidentally reverted commit: 03d26cd6: ensure embed_url contains valid http(s) uri * when the merge commit e62a85cf was reverted, git chose the 2660c2e2 parent to land on instead of the 03d26cd6 parent (which contains security fixes) --- Gemfile | 6 +- Gemfile.lock | 14 +- .../javascripts/admin/models/admin-user.js | 6 - .../admin/routes/admin-web-hooks-show.js | 3 +- app/assets/javascripts/application.js | 2 +- app/assets/javascripts/discourse-loader.js | 1 - .../app/controllers/preferences/interface.js | 30 +-- .../discourse/app/controllers/topic.js | 7 + .../discourse/app/controllers/user.js | 4 +- .../discourse/app/helpers/page-reloader.js | 10 + .../app/initializers/copy-codeblocks.js | 3 +- .../app/initializers/topic-footer-buttons.js | 34 ++- .../discourse/app/lib/theme-selector.js | 26 --- .../discourse/app/models/nav-item.js | 23 +- .../discourse/app/models/topic-details.js | 13 +- .../discourse/app/templates/about.hbs | 88 ++++---- .../app/templates/components/d-navigation.hbs | 3 + .../components/topic-footer-buttons.hbs | 4 +- .../app/templates/preferences/interface.hbs | 4 +- .../discourse/app/templates/topic.hbs | 1 + .../discourse/app/widgets/header.js | 1 + .../javascripts/select-kit/addon/.gitkeep | 0 .../components/admin-group-selector.js | 0 .../components/categories-admin-dropdown.js | 0 .../components/category-chooser.js | 1 + .../components/category-drop.js | 0 .../category-drop/category-drop-header.js | 0 .../category-notifications-button.js | 0 .../{app => addon}/components/category-row.js | 0 .../components/category-selector.js | 0 .../components/color-palettes.js | 0 .../color-palettes/color-palettes-row.js | 0 .../{app => addon}/components/combo-box.js | 0 .../components/combo-box/combo-box-header.js | 0 .../components/composer-actions.js | 0 .../components/create-color-row.js | 0 .../components/dropdown-select-box.js | 0 .../dropdown-select-box-header.js | 0 .../dropdown-select-box-row.js | 0 .../components/future-date-input-selector.js | 0 .../future-date-input-selector-header.js | 0 .../future-date-input-selector-row.js | 0 .../future-date-input-selector/mixin.js | 0 .../components/group-dropdown.js | 0 .../components/group-members-dropdown.js | 0 .../components/group-notifications-button.js | 0 .../{app => addon}/components/icon-picker.js | 0 .../{app => addon}/components/list-setting.js | 0 .../components/mini-tag-chooser.js | 0 .../mini-tag-chooser-header.js | 0 .../mini-tag-chooser/selected-collection.js | 0 .../{app => addon}/components/multi-select.js | 0 .../multi-select/multi-select-filter.js | 0 .../multi-select/multi-select-header.js | 0 .../multi-select/selected-category.js | 0 .../components/multi-select/selected-color.js | 0 .../components/none-category-row.js | 0 .../components/notifications-button.js | 3 +- .../notifications-button-row.js | 0 .../components/notifications-filter.js | 0 .../notifications-filter-header.js | 0 .../components/period-chooser.js | 0 .../period-chooser/period-chooser-header.js | 0 .../period-chooser/period-chooser-row.js | 0 .../components/pinned-button.js | 0 .../components/pinned-options.js | 0 .../search-advanced-category-chooser.js | 0 .../{app => addon}/components/select-kit.js | 6 +- .../select-kit/errors-collection.js | 0 .../components/select-kit/select-kit-body.js | 0 .../select-kit/select-kit-collection.js | 0 .../select-kit/select-kit-create-row.js | 0 .../select-kit/select-kit-filter.js | 0 .../select-kit/select-kit-header.js | 6 +- .../select-kit/select-kit-none-row.js | 0 .../components/select-kit/select-kit-row.js | 0 .../select-kit/single-select-header.js | 0 .../components/selected-color.js | 0 .../components/selected-name.js | 0 .../components/single-select.js | 0 .../components/tag-chooser-row.js | 0 .../{app => addon}/components/tag-chooser.js | 0 .../{app => addon}/components/tag-drop.js | 0 .../components/tag-drop/tag-drop-header.js | 0 .../components/tag-group-chooser.js | 0 .../components/tag-notifications-button.js | 0 .../{app => addon}/components/tag-row.js | 0 .../components/timezone-input.js | 0 .../components/toolbar-popup-menu-options.js | 0 .../toolbar-popup-menu-options-heading.js | 0 .../topic-footer-mobile-dropdown.js | 0 .../components/topic-notifications-button.js | 12 +- .../components/topic-notifications-options.js | 0 .../{app => addon}/components/user-chooser.js | 0 .../components/user-chooser/user-row.js | 0 .../components/user-notifications-dropdown.js | 0 .../{app => addon}/mixins/plugin-api.js | 0 .../select-kit/{app => addon}/mixins/tags.js | 0 .../select-kit/{app => addon}/mixins/utils.js | 0 .../category-drop/category-drop-header.hbs | 0 .../templates/components/category-row.hbs | 0 .../color-palettes/color-palettes-row.hbs | 0 .../components/combo-box/combo-box-header.hbs | 0 .../templates/components/create-color-row.hbs | 0 .../dropdown-select-box-header.hbs | 0 .../dropdown-select-box-row.hbs | 0 .../future-date-input-selector-header.hbs | 0 .../future-date-input-selector-row.hbs | 0 .../mini-tag-chooser-header.hbs | 0 .../mini-tag-chooser/selected-collection.hbs | 0 .../templates/components/multi-select.hbs | 0 .../multi-select/multi-select-header.hbs | 0 .../multi-select/selected-category.hbs | 0 .../notifications-filter-header.hbs | 0 .../period-chooser/period-chooser-header.hbs | 0 .../period-chooser/period-chooser-row.hbs | 0 .../templates/components/pinned-button.hbs | 4 + .../select-kit/errors-collection.hbs | 0 .../components/select-kit/select-kit-body.hbs | 0 .../select-kit/select-kit-collection.hbs | 0 .../select-kit/select-kit-filter.hbs | 0 .../components/select-kit/select-kit-row.hbs | 0 .../select-kit/single-select-header.hbs | 0 .../templates/components/selected-name.hbs | 0 .../templates/components/single-select.hbs | 0 .../templates/components/tag-chooser-row.hbs | 0 .../components/tag-drop/tag-drop-header.hbs | 0 .../templates/components/tag-row.hbs | 0 .../toolbar-popup-menu-options-heading.hbs | 0 .../components/topic-notifications-button.hbs | 30 +++ .../components/user-chooser/user-row.hbs | 0 .../templates/components/pinned-button.hbs | 5 - .../components/topic-notifications-button.hbs | 17 -- app/assets/javascripts/wizard-application.js | 2 +- .../stylesheets/common/base/_topic-list.scss | 1 - app/assets/stylesheets/common/base/alert.scss | 1 + .../common/base/crawler_layout.scss | 30 +++ .../stylesheets/common/base/topic-post.scss | 51 ++++- app/assets/stylesheets/common/base/user.scss | 3 + .../stylesheets/common/components/badges.scss | 1 - .../common/select-kit/category-row.scss | 3 + .../common/select-kit/pinned-button.scss | 21 -- .../common/select-kit/select-kit.scss | 5 + .../topic-notifications-button.scss | 31 +-- app/assets/stylesheets/desktop.scss | 20 +- app/assets/stylesheets/desktop/alert.scss | 3 - .../stylesheets/desktop/topic-post.scss | 17 +- app/assets/stylesheets/mobile.scss | 33 +-- app/assets/stylesheets/mobile/alert.scss | 5 +- app/assets/stylesheets/mobile/banner.scss | 7 +- app/assets/stylesheets/mobile/discourse.scss | 5 - app/assets/stylesheets/mobile/topic-post.scss | 27 +-- app/controllers/about_controller.rb | 2 +- app/controllers/list_controller.rb | 8 +- app/controllers/themes_controller.rb | 31 --- app/helpers/email_helper.rb | 8 +- app/models/application_request.rb | 7 +- app/models/category.rb | 26 +-- app/models/global_setting.rb | 24 ++- app/models/group_archived_message.rb | 2 +- app/models/post.rb | 14 +- app/models/topic.rb | 15 +- app/models/topic_embed.rb | 2 + app/models/upload.rb | 4 - app/models/user.rb | 129 ++++++----- app/models/user_avatar.rb | 12 +- app/models/web_hook_event_type.rb | 2 - app/serializers/about_serializer.rb | 11 +- app/services/badge_granter.rb | 6 +- app/views/email/default_template.html | 2 + app/views/layouts/email_template.html.erb | 5 +- app/views/layouts/publish.html.erb | 7 +- app/views/list/list.erb | 8 +- config/application.rb | 6 +- config/initializers/001-redis.rb | 17 +- config/locales/client.en.yml | 9 +- config/locales/server.en.yml | 1 + config/routes.rb | 4 +- config/site_settings.yml | 11 +- db/fixtures/007_web_hook_event_types.rb | 10 - ...te_bookmarks_from_post_action_bookmarks.rb | 1 + .../20200514075356_remove_flag_web_hooks.rb | 21 ++ ...0514075407_remove_queued_post_web_hooks.rb | 21 ++ ...0200520001619_remove_fks_from_bookmarks.rb | 9 + ...522004855_remove_access_control_post_fk.rb | 7 + lib/backup_restore/backup_file_handler.rb | 7 +- lib/discourse_plugin_registry.rb | 2 + lib/discourse_redis.rb | 6 - lib/file_store/s3_store.rb | 27 ++- lib/freedom_patches/translate_accelerator.rb | 2 +- lib/guardian.rb | 4 + lib/plugin/instance.rb | 10 + lib/plugin_initialization_guard.rb | 53 +++-- lib/post_creator.rb | 4 + lib/post_revisor.rb | 6 +- lib/tasks/bookmarks.rake | 1 + lib/topic_query_params.rb | 2 +- .../db/fixtures/001_discobot.rb | 25 +-- ...avatar_download_attempt_on_user_avatars.rb | 15 ++ plugins/discourse-narrative-bot/plugin.rb | 6 + .../track_selector_spec.rb | 2 +- .../send_advanced_tutorial_message_spec.rb | 2 + .../default_current_user_provider_spec.rb | 2 +- spec/components/discourse_redis_spec.rb | 4 +- spec/components/email/processor_spec.rb | 2 +- spec/components/file_store/s3_store_spec.rb | 17 +- .../components/guardian/user_guardian_spec.rb | 4 +- spec/components/pretty_text_spec.rb | 4 +- spec/components/search_spec.rb | 2 +- spec/fabricators/post_fabricator.rb | 2 +- spec/fabricators/web_hook_fabricator.rb | 16 -- spec/integration/email_style_spec.rb | 203 +++++++++--------- spec/integration/topic_thumbnail_spec.rb | 35 ++- .../bookmark_reminder_notifications_spec.rb | 2 +- ...mark_reminder_notification_handler_spec.rb | 2 +- spec/models/post_spec.rb | 38 ++++ spec/models/topic_embed_spec.rb | 8 + spec/models/topic_spec.rb | 2 +- spec/models/user_spec.rb | 9 +- spec/multisite/s3_store_spec.rb | 43 ++++ spec/rails_helper.rb | 6 + spec/requests/about_controller_spec.rb | 16 ++ .../requests/admin/backups_controller_spec.rb | 2 +- spec/requests/admin/users_controller_spec.rb | 2 +- spec/requests/list_controller_spec.rb | 13 +- spec/requests/posts_controller_spec.rb | 11 + spec/requests/search_controller_spec.rb | 4 +- spec/requests/topics_controller_spec.rb | 2 +- spec/requests/webhooks_controller_spec.rb | 2 +- .../serializers/topic_view_serializer_spec.rb | 2 +- spec/services/word_watcher_spec.rb | 2 +- .../acceptance/category-chooser-test.js | 16 ++ .../acceptance/emoji-picker-test.js | 13 -- .../acceptance/preferences-test.js | 1 - test/javascripts/fixtures/about.js | 1 + test/javascripts/helpers/select-kit-helper.js | 9 + 236 files changed, 1031 insertions(+), 715 deletions(-) create mode 100644 app/assets/javascripts/discourse/app/helpers/page-reloader.js create mode 100644 app/assets/javascripts/select-kit/addon/.gitkeep rename app/assets/javascripts/select-kit/{app => addon}/components/admin-group-selector.js (100%) rename app/assets/javascripts/select-kit/{app => addon}/components/categories-admin-dropdown.js (100%) rename app/assets/javascripts/select-kit/{app => addon}/components/category-chooser.js (99%) rename app/assets/javascripts/select-kit/{app => addon}/components/category-drop.js (100%) rename app/assets/javascripts/select-kit/{app => addon}/components/category-drop/category-drop-header.js (100%) rename app/assets/javascripts/select-kit/{app => addon}/components/category-notifications-button.js (100%) rename app/assets/javascripts/select-kit/{app => addon}/components/category-row.js (100%) rename app/assets/javascripts/select-kit/{app => addon}/components/category-selector.js (100%) rename app/assets/javascripts/select-kit/{app => addon}/components/color-palettes.js (100%) rename app/assets/javascripts/select-kit/{app => addon}/components/color-palettes/color-palettes-row.js (100%) rename app/assets/javascripts/select-kit/{app => addon}/components/combo-box.js (100%) rename app/assets/javascripts/select-kit/{app => addon}/components/combo-box/combo-box-header.js (100%) rename app/assets/javascripts/select-kit/{app => addon}/components/composer-actions.js (100%) rename app/assets/javascripts/select-kit/{app => addon}/components/create-color-row.js (100%) rename app/assets/javascripts/select-kit/{app => addon}/components/dropdown-select-box.js (100%) rename app/assets/javascripts/select-kit/{app => addon}/components/dropdown-select-box/dropdown-select-box-header.js (100%) rename app/assets/javascripts/select-kit/{app => addon}/components/dropdown-select-box/dropdown-select-box-row.js (100%) rename app/assets/javascripts/select-kit/{app => addon}/components/future-date-input-selector.js (100%) rename app/assets/javascripts/select-kit/{app => addon}/components/future-date-input-selector/future-date-input-selector-header.js (100%) rename app/assets/javascripts/select-kit/{app => addon}/components/future-date-input-selector/future-date-input-selector-row.js (100%) rename app/assets/javascripts/select-kit/{app => addon}/components/future-date-input-selector/mixin.js (100%) rename app/assets/javascripts/select-kit/{app => addon}/components/group-dropdown.js (100%) rename app/assets/javascripts/select-kit/{app => addon}/components/group-members-dropdown.js (100%) rename app/assets/javascripts/select-kit/{app => addon}/components/group-notifications-button.js (100%) rename app/assets/javascripts/select-kit/{app => addon}/components/icon-picker.js (100%) rename app/assets/javascripts/select-kit/{app => addon}/components/list-setting.js (100%) rename app/assets/javascripts/select-kit/{app => addon}/components/mini-tag-chooser.js (100%) rename app/assets/javascripts/select-kit/{app => addon}/components/mini-tag-chooser/mini-tag-chooser-header.js (100%) rename app/assets/javascripts/select-kit/{app => addon}/components/mini-tag-chooser/selected-collection.js (100%) rename app/assets/javascripts/select-kit/{app => addon}/components/multi-select.js (100%) rename app/assets/javascripts/select-kit/{app => addon}/components/multi-select/multi-select-filter.js (100%) rename app/assets/javascripts/select-kit/{app => addon}/components/multi-select/multi-select-header.js (100%) rename app/assets/javascripts/select-kit/{app => addon}/components/multi-select/selected-category.js (100%) rename app/assets/javascripts/select-kit/{app => addon}/components/multi-select/selected-color.js (100%) rename app/assets/javascripts/select-kit/{app => addon}/components/none-category-row.js (100%) rename app/assets/javascripts/select-kit/{app => addon}/components/notifications-button.js (96%) rename app/assets/javascripts/select-kit/{app => addon}/components/notifications-button/notifications-button-row.js (100%) rename app/assets/javascripts/select-kit/{app => addon}/components/notifications-filter.js (100%) rename app/assets/javascripts/select-kit/{app => addon}/components/notifications-filter/notifications-filter-header.js (100%) rename app/assets/javascripts/select-kit/{app => addon}/components/period-chooser.js (100%) rename app/assets/javascripts/select-kit/{app => addon}/components/period-chooser/period-chooser-header.js (100%) rename app/assets/javascripts/select-kit/{app => addon}/components/period-chooser/period-chooser-row.js (100%) rename app/assets/javascripts/select-kit/{app => addon}/components/pinned-button.js (100%) rename app/assets/javascripts/select-kit/{app => addon}/components/pinned-options.js (100%) rename app/assets/javascripts/select-kit/{app => addon}/components/search-advanced-category-chooser.js (100%) rename app/assets/javascripts/select-kit/{app => addon}/components/select-kit.js (99%) rename app/assets/javascripts/select-kit/{app => addon}/components/select-kit/errors-collection.js (100%) rename app/assets/javascripts/select-kit/{app => addon}/components/select-kit/select-kit-body.js (100%) rename app/assets/javascripts/select-kit/{app => addon}/components/select-kit/select-kit-collection.js (100%) rename app/assets/javascripts/select-kit/{app => addon}/components/select-kit/select-kit-create-row.js (100%) rename app/assets/javascripts/select-kit/{app => addon}/components/select-kit/select-kit-filter.js (100%) rename app/assets/javascripts/select-kit/{app => addon}/components/select-kit/select-kit-header.js (97%) rename app/assets/javascripts/select-kit/{app => addon}/components/select-kit/select-kit-none-row.js (100%) rename app/assets/javascripts/select-kit/{app => addon}/components/select-kit/select-kit-row.js (100%) rename app/assets/javascripts/select-kit/{app => addon}/components/select-kit/single-select-header.js (100%) rename app/assets/javascripts/select-kit/{app => addon}/components/selected-color.js (100%) rename app/assets/javascripts/select-kit/{app => addon}/components/selected-name.js (100%) rename app/assets/javascripts/select-kit/{app => addon}/components/single-select.js (100%) rename app/assets/javascripts/select-kit/{app => addon}/components/tag-chooser-row.js (100%) rename app/assets/javascripts/select-kit/{app => addon}/components/tag-chooser.js (100%) rename app/assets/javascripts/select-kit/{app => addon}/components/tag-drop.js (100%) rename app/assets/javascripts/select-kit/{app => addon}/components/tag-drop/tag-drop-header.js (100%) rename app/assets/javascripts/select-kit/{app => addon}/components/tag-group-chooser.js (100%) rename app/assets/javascripts/select-kit/{app => addon}/components/tag-notifications-button.js (100%) rename app/assets/javascripts/select-kit/{app => addon}/components/tag-row.js (100%) rename app/assets/javascripts/select-kit/{app => addon}/components/timezone-input.js (100%) rename app/assets/javascripts/select-kit/{app => addon}/components/toolbar-popup-menu-options.js (100%) rename app/assets/javascripts/select-kit/{app => addon}/components/toolbar-popup-menu-options/toolbar-popup-menu-options-heading.js (100%) rename app/assets/javascripts/select-kit/{app => addon}/components/topic-footer-mobile-dropdown.js (100%) rename app/assets/javascripts/select-kit/{app => addon}/components/topic-notifications-button.js (55%) rename app/assets/javascripts/select-kit/{app => addon}/components/topic-notifications-options.js (100%) rename app/assets/javascripts/select-kit/{app => addon}/components/user-chooser.js (100%) rename app/assets/javascripts/select-kit/{app => addon}/components/user-chooser/user-row.js (100%) rename app/assets/javascripts/select-kit/{app => addon}/components/user-notifications-dropdown.js (100%) rename app/assets/javascripts/select-kit/{app => addon}/mixins/plugin-api.js (100%) rename app/assets/javascripts/select-kit/{app => addon}/mixins/tags.js (100%) rename app/assets/javascripts/select-kit/{app => addon}/mixins/utils.js (100%) rename app/assets/javascripts/select-kit/{app => addon}/templates/components/category-drop/category-drop-header.hbs (100%) rename app/assets/javascripts/select-kit/{app => addon}/templates/components/category-row.hbs (100%) rename app/assets/javascripts/select-kit/{app => addon}/templates/components/color-palettes/color-palettes-row.hbs (100%) rename app/assets/javascripts/select-kit/{app => addon}/templates/components/combo-box/combo-box-header.hbs (100%) rename app/assets/javascripts/select-kit/{app => addon}/templates/components/create-color-row.hbs (100%) rename app/assets/javascripts/select-kit/{app => addon}/templates/components/dropdown-select-box/dropdown-select-box-header.hbs (100%) rename app/assets/javascripts/select-kit/{app => addon}/templates/components/dropdown-select-box/dropdown-select-box-row.hbs (100%) rename app/assets/javascripts/select-kit/{app => addon}/templates/components/future-date-input-selector/future-date-input-selector-header.hbs (100%) rename app/assets/javascripts/select-kit/{app => addon}/templates/components/future-date-input-selector/future-date-input-selector-row.hbs (100%) rename app/assets/javascripts/select-kit/{app => addon}/templates/components/mini-tag-chooser/mini-tag-chooser-header.hbs (100%) rename app/assets/javascripts/select-kit/{app => addon}/templates/components/mini-tag-chooser/selected-collection.hbs (100%) rename app/assets/javascripts/select-kit/{app => addon}/templates/components/multi-select.hbs (100%) rename app/assets/javascripts/select-kit/{app => addon}/templates/components/multi-select/multi-select-header.hbs (100%) rename app/assets/javascripts/select-kit/{app => addon}/templates/components/multi-select/selected-category.hbs (100%) rename app/assets/javascripts/select-kit/{app => addon}/templates/components/notifications-filter/notifications-filter-header.hbs (100%) rename app/assets/javascripts/select-kit/{app => addon}/templates/components/period-chooser/period-chooser-header.hbs (100%) rename app/assets/javascripts/select-kit/{app => addon}/templates/components/period-chooser/period-chooser-row.hbs (100%) create mode 100644 app/assets/javascripts/select-kit/addon/templates/components/pinned-button.hbs rename app/assets/javascripts/select-kit/{app => addon}/templates/components/select-kit/errors-collection.hbs (100%) rename app/assets/javascripts/select-kit/{app => addon}/templates/components/select-kit/select-kit-body.hbs (100%) rename app/assets/javascripts/select-kit/{app => addon}/templates/components/select-kit/select-kit-collection.hbs (100%) rename app/assets/javascripts/select-kit/{app => addon}/templates/components/select-kit/select-kit-filter.hbs (100%) rename app/assets/javascripts/select-kit/{app => addon}/templates/components/select-kit/select-kit-row.hbs (100%) rename app/assets/javascripts/select-kit/{app => addon}/templates/components/select-kit/single-select-header.hbs (100%) rename app/assets/javascripts/select-kit/{app => addon}/templates/components/selected-name.hbs (100%) rename app/assets/javascripts/select-kit/{app => addon}/templates/components/single-select.hbs (100%) rename app/assets/javascripts/select-kit/{app => addon}/templates/components/tag-chooser-row.hbs (100%) rename app/assets/javascripts/select-kit/{app => addon}/templates/components/tag-drop/tag-drop-header.hbs (100%) rename app/assets/javascripts/select-kit/{app => addon}/templates/components/tag-row.hbs (100%) rename app/assets/javascripts/select-kit/{app => addon}/templates/components/toolbar-popup-menu-options/toolbar-popup-menu-options-heading.hbs (100%) create mode 100644 app/assets/javascripts/select-kit/addon/templates/components/topic-notifications-button.hbs rename app/assets/javascripts/select-kit/{app => addon}/templates/components/user-chooser/user-row.hbs (100%) delete mode 100644 app/assets/javascripts/select-kit/app/templates/components/pinned-button.hbs delete mode 100644 app/assets/javascripts/select-kit/app/templates/components/topic-notifications-button.hbs delete mode 100644 app/assets/stylesheets/desktop/alert.scss delete mode 100644 app/controllers/themes_controller.rb create mode 100644 db/migrate/20200514075356_remove_flag_web_hooks.rb create mode 100644 db/migrate/20200514075407_remove_queued_post_web_hooks.rb create mode 100644 db/post_migrate/20200520001619_remove_fks_from_bookmarks.rb create mode 100644 db/post_migrate/20200522004855_remove_access_control_post_fk.rb create mode 100644 plugins/discourse-narrative-bot/db/post_migrate/20200520015508_clear_last_gravatar_download_attempt_on_user_avatars.rb diff --git a/Gemfile b/Gemfile index 6c72e48bab4..a80f23dbc0f 100644 --- a/Gemfile +++ b/Gemfile @@ -127,10 +127,6 @@ gem 'mini_racer' # TODO: determine why highline is being held back and upgrade to latest gem 'highline', '~> 1.7.0', require: false -# TODO: Upgrading breaks Sidekiq Web -# This is a bit of a hornets nest cause in an ideal world we much prefer -# if Sidekiq reused session and CSRF mitigation with Discourse on the -# _forum_session cookie instead of a rack.session cookie gem 'rack', '2.2.2' gem 'rack-protection' # security @@ -252,3 +248,5 @@ end gem 'webpush', require: false gem 'colored2', require: false gem 'maxminddb' + +gem 'rails_failover', require: false diff --git a/Gemfile.lock b/Gemfile.lock index 752d48c3c90..db4b88d8631 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -195,7 +195,7 @@ GEM mini_sql (0.2.5) mini_suffix (0.3.0) ffi (~> 1.9) - minitest (5.14.0) + minitest (5.14.1) mocha (1.11.2) mock_redis (0.23.0) msgpack (1.3.3) @@ -262,7 +262,7 @@ GEM pry-rails (0.3.9) pry (>= 0.10.4) public_suffix (4.0.5) - puma (4.3.3) + puma (4.3.5) nio4r (~> 2.0) r2 (0.2.7) rack (2.2.2) @@ -277,6 +277,8 @@ GEM nokogiri (>= 1.6) rails-html-sanitizer (1.3.0) loofah (~> 2.3) + rails_failover (0.2.0) + redis (~> 4) rails_multisite (2.1.2) activerecord (> 5.0, < 7) railties (> 5.0, < 7) @@ -294,7 +296,7 @@ GEM rb-fsevent (0.10.4) rb-inotify (0.10.1) ffi (~> 1.0) - rbtrace (0.4.12) + rbtrace (0.4.13) ffi (>= 1.0.6) msgpack (>= 0.4.3) optimist (>= 3.0.0) @@ -341,13 +343,16 @@ GEM json-schema (~> 2.2) railties (>= 3.1, < 7.0) rtlit (0.0.5) - rubocop (0.83.0) + rubocop (0.84.0) parallel (~> 1.10) parser (>= 2.7.0.1) rainbow (>= 2.2.2, < 4.0) rexml + rubocop-ast (>= 0.0.3) ruby-progressbar (~> 1.7) unicode-display_width (>= 1.4.0, < 2.0) + rubocop-ast (0.0.3) + parser (>= 2.7.0.1) rubocop-discourse (2.1.2) rubocop (>= 0.69.0) rubocop-rspec (>= 1.39.0) @@ -509,6 +514,7 @@ DEPENDENCIES rack (= 2.2.2) rack-mini-profiler rack-protection + rails_failover rails_multisite railties (= 6.0.3) rake diff --git a/app/assets/javascripts/admin/models/admin-user.js b/app/assets/javascripts/admin/models/admin-user.js index 71b8078f6ed..9977fa8dd46 100644 --- a/app/assets/javascripts/admin/models/admin-user.js +++ b/app/assets/javascripts/admin/models/admin-user.js @@ -77,12 +77,6 @@ const AdminUser = User.extend({ }); }, - revokeApiKey() { - return ajax(`/admin/users/${this.id}/revoke_api_key`, { - type: "DELETE" - }).then(() => this.set("api_key", null)); - }, - deleteAllPosts() { let deletedPosts = 0; const user = this; diff --git a/app/assets/javascripts/admin/routes/admin-web-hooks-show.js b/app/assets/javascripts/admin/routes/admin-web-hooks-show.js index 34aed775cc5..f0860e79f71 100644 --- a/app/assets/javascripts/admin/routes/admin-web-hooks-show.js +++ b/app/assets/javascripts/admin/routes/admin-web-hooks-show.js @@ -1,5 +1,4 @@ import { get } from "@ember/object"; -import { isEmpty } from "@ember/utils"; import DiscourseRoute from "discourse/routes/discourse"; export default DiscourseRoute.extend({ @@ -15,7 +14,7 @@ export default DiscourseRoute.extend({ }, setupController(controller, model) { - if (model.get("isNew") || isEmpty(model.get("web_hook_event_types"))) { + if (model.get("isNew")) { model.set("web_hook_event_types", controller.get("defaultEventTypes")); } diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 4445fd79869..40f1d275bb4 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -1,6 +1,6 @@ //= require_tree ./discourse-common/addon //= require ./polyfills -//= require_tree ./select-kit/app +//= require_tree ./select-kit/addon //= require ./discourse/app/app //= require ./app-boot diff --git a/app/assets/javascripts/discourse-loader.js b/app/assets/javascripts/discourse-loader.js index d11217a985a..fc829ae12f0 100644 --- a/app/assets/javascripts/discourse-loader.js +++ b/app/assets/javascripts/discourse-loader.js @@ -36,7 +36,6 @@ var define, requirejs; default: Ember.Object, get: Ember.get, getProperties: Ember.getProperties, - guidFor: Ember.guidFor, set: Ember.set, setProperties: Ember.setProperties, computed: Ember.computed, diff --git a/app/assets/javascripts/discourse/app/controllers/preferences/interface.js b/app/assets/javascripts/discourse/app/controllers/preferences/interface.js index 1ad85b7d7b1..2fcc5781c30 100644 --- a/app/assets/javascripts/discourse/app/controllers/preferences/interface.js +++ b/app/assets/javascripts/discourse/app/controllers/preferences/interface.js @@ -2,13 +2,10 @@ import I18n from "I18n"; import { inject } from "@ember/controller"; import Controller from "@ember/controller"; import { setDefaultHomepage } from "discourse/lib/utilities"; -import discourseComputed, { observes } from "discourse-common/utils/decorators"; -import { - listThemes, - previewTheme, - setLocalTheme -} from "discourse/lib/theme-selector"; +import discourseComputed from "discourse-common/utils/decorators"; +import { listThemes, setLocalTheme } from "discourse/lib/theme-selector"; import { popupAjaxError } from "discourse/lib/ajax-error"; +import pageReloader from "discourse/helpers/page-reloader"; import { safariHacksDisabled, isiPad, @@ -28,6 +25,9 @@ const TEXT_SIZES = ["smaller", "normal", "larger", "largest"]; const TITLE_COUNT_MODES = ["notifications", "contextual"]; export default Controller.extend({ + currentThemeId: -1, + preferencesController: inject("preferences"), + @discourseComputed("makeThemeDefault") saveAttrNames(makeDefault) { let attrs = [ @@ -51,8 +51,6 @@ export default Controller.extend({ return attrs; }, - preferencesController: inject("preferences"), - @discourseComputed() isiPad() { // TODO: remove this preference checkbox when iOS adoption > 90% @@ -105,10 +103,14 @@ export default Controller.extend({ return themes && themes.length > 1; }, - @observes("themeId") - themeIdChanged() { - const id = this.themeId; - previewTheme([id]); + @discourseComputed("themeId") + themeIdChanged(themeId) { + if (this.currentThemeId === -1) { + this.set("currentThemeId", themeId); + return false; + } else { + return this.currentThemeId !== themeId; + } }, @discourseComputed("model.user_option.theme_ids", "themeId") @@ -189,6 +191,10 @@ export default Controller.extend({ this.disableSafariHacks.toString() ); } + + if (this.themeId !== this.currentThemeId) { + pageReloader.reload(); + } }) .catch(popupAjaxError); }, diff --git a/app/assets/javascripts/discourse/app/controllers/topic.js b/app/assets/javascripts/discourse/app/controllers/topic.js index 2c2ebdc7fe6..ba7caf554d1 100644 --- a/app/assets/javascripts/discourse/app/controllers/topic.js +++ b/app/assets/javascripts/discourse/app/controllers/topic.js @@ -185,6 +185,13 @@ export default Controller.extend(bufferedProperty("model"), { ); }, + @discourseComputed("model.category") + minimumRequiredTags(category) { + return category && category.minimum_required_tags > 0 + ? category.minimum_required_tags + : null; + }, + _forceRefreshPostStream() { this.appEvents.trigger("post-stream:refresh", { force: true }); }, diff --git a/app/assets/javascripts/discourse/app/controllers/user.js b/app/assets/javascripts/discourse/app/controllers/user.js index cc2db042fbf..b466f084ed5 100644 --- a/app/assets/javascripts/discourse/app/controllers/user.js +++ b/app/assets/javascripts/discourse/app/controllers/user.js @@ -51,13 +51,15 @@ export default Controller.extend(CanCheckEmails, { hasDeletedPosts: gt("model.number_of_deleted_posts", 0), hasBeenSuspended: gt("model.number_of_suspensions", 0), hasReceivedWarnings: gt("model.warnings_received_count", 0), + hasRejectedPosts: gt("model.number_of_rejected_posts", 0), showStaffCounters: or( "hasGivenFlags", "hasFlaggedPosts", "hasDeletedPosts", "hasBeenSuspended", - "hasReceivedWarnings" + "hasReceivedWarnings", + "hasRejectedPosts" ), showFeaturedTopic: and( diff --git a/app/assets/javascripts/discourse/app/helpers/page-reloader.js b/app/assets/javascripts/discourse/app/helpers/page-reloader.js new file mode 100644 index 00000000000..6429be3a3cc --- /dev/null +++ b/app/assets/javascripts/discourse/app/helpers/page-reloader.js @@ -0,0 +1,10 @@ +import EmberObject from "@ember/object"; +import Ember from "ember"; + +export default EmberObject.create({ + reload: function() { + if (!Ember.testing) { + location.reload(); + } + } +}); diff --git a/app/assets/javascripts/discourse/app/initializers/copy-codeblocks.js b/app/assets/javascripts/discourse/app/initializers/copy-codeblocks.js index a0b0d509621..f6fcef5e17e 100644 --- a/app/assets/javascripts/discourse/app/initializers/copy-codeblocks.js +++ b/app/assets/javascripts/discourse/app/initializers/copy-codeblocks.js @@ -3,6 +3,7 @@ import { cancel, later } from "@ember/runloop"; import { Promise } from "rsvp"; import { iconHTML } from "discourse-common/lib/icon-library"; import I18n from "I18n"; +import { guidFor } from "@ember/object/internals"; // http://github.com/feross/clipboard-copy function clipboardCopy(text) { @@ -90,7 +91,7 @@ export default { const state = button.innerHTML; button.innerHTML = I18n.t("copy_codeblock.copied"); - const commandId = Ember.guidFor(button); + const commandId = guidFor(button); if (_fadeCopyCodeblocksRunners[commandId]) { cancel(_fadeCopyCodeblocksRunners[commandId]); diff --git a/app/assets/javascripts/discourse/app/initializers/topic-footer-buttons.js b/app/assets/javascripts/discourse/app/initializers/topic-footer-buttons.js index a13ccd2219f..a2de76d3e9d 100644 --- a/app/assets/javascripts/discourse/app/initializers/topic-footer-buttons.js +++ b/app/assets/javascripts/discourse/app/initializers/topic-footer-buttons.js @@ -3,6 +3,13 @@ import showModal from "discourse/lib/show-modal"; import { registerTopicFooterButton } from "discourse/lib/register-topic-footer-button"; import { formattedReminderTime } from "discourse/lib/bookmark"; +const SHARE_PRIORITY = 1000; +const BOOKMARK_PRIORITY = 900; +const ARCHIVE_PRIORITY = 800; +const FLAG_PRIORITY = 700; +const EDIT_MESSAGE_PRIORITY = 600; +const DEFER_PRIORITY = 500; + export default { name: "topic-footer-buttons", @@ -11,8 +18,12 @@ export default { registerTopicFooterButton({ id: "share-and-invite", icon: "link", - priority: 999, - label: "topic.share.title", + priority: SHARE_PRIORITY, + label() { + if (!this.get("topic.isPrivateMessage") || this.site.mobileView) { + return "topic.share.title"; + } + }, title: "topic.share.help", action() { const panels = [ @@ -67,7 +78,7 @@ export default { registerTopicFooterButton({ id: "flag", icon: "flag", - priority: 998, + priority: FLAG_PRIORITY, label: "topic.flag_topic.title", title: "topic.flag_topic.help", action: "showFlagTopic", @@ -93,14 +104,16 @@ export default { } return "bookmark"; }, - priority: 1000, + priority: BOOKMARK_PRIORITY, classNames() { const bookmarked = this.get("topic.bookmarked"); return bookmarked ? ["bookmark", "bookmarked"] : ["bookmark"]; }, label() { - const bookmarked = this.get("topic.bookmarked"); - return bookmarked ? "bookmarked.clear_bookmarks" : "bookmarked.title"; + if (!this.get("topic.isPrivateMessage") || this.site.mobileView) { + const bookmarked = this.get("topic.bookmarked"); + return bookmarked ? "bookmarked.clear_bookmarks" : "bookmarked.title"; + } }, translatedTitle() { const bookmarked = this.get("topic.bookmarked"); @@ -126,7 +139,7 @@ export default { registerTopicFooterButton({ id: "archive", - priority: 996, + priority: ARCHIVE_PRIORITY, icon() { return this.archiveIcon; }, @@ -155,13 +168,16 @@ export default { registerTopicFooterButton({ id: "edit-message", - priority: 750, + priority: EDIT_MESSAGE_PRIORITY, icon: "pencil-alt", label: "topic.edit_message.title", title: "topic.edit_message.help", action: "editFirstPost", classNames: ["edit-message"], dependentKeys: ["editFirstPost", "showEditOnFooter"], + dropdown() { + return this.site.mobileView && this.get("topic.isPrivateMessage"); + }, displayed() { return this.showEditOnFooter; } @@ -170,7 +186,7 @@ export default { registerTopicFooterButton({ id: "defer", icon: "circle", - priority: 300, + priority: DEFER_PRIORITY, label: "topic.defer.title", title: "topic.defer.help", action: "deferTopic", diff --git a/app/assets/javascripts/discourse/app/lib/theme-selector.js b/app/assets/javascripts/discourse/app/lib/theme-selector.js index 9d293fe312c..48f63c31fbf 100644 --- a/app/assets/javascripts/discourse/app/lib/theme-selector.js +++ b/app/assets/javascripts/discourse/app/lib/theme-selector.js @@ -1,5 +1,4 @@ import I18n from "I18n"; -import { ajax } from "discourse/lib/ajax"; import deprecated from "discourse-common/lib/deprecated"; const keySelector = "meta[name=discourse_theme_ids]"; @@ -79,31 +78,6 @@ export function refreshCSS(node, hash, newHref) { $orig.data("copy", reloaded); } -export function previewTheme(ids = []) { - ids = ids.reject(id => !id); - if (!ids.includes(currentThemeId())) { - Discourse.set("assetVersion", "forceRefresh"); - - ajax(`/themes/assets/${ids.length > 0 ? ids.join("-") : "default"}`).then( - results => { - const elem = _.first($(keySelector)); - if (elem) { - elem.content = ids.join(","); - } - - results.themes.forEach(theme => { - const node = $( - `link[rel=stylesheet][data-target=${theme.target}]` - )[0]; - if (node) { - refreshCSS(node, null, theme.new_href); - } - }); - } - ); - } -} - export function listThemes(site) { let themes = site.get("user_themes"); diff --git a/app/assets/javascripts/discourse/app/models/nav-item.js b/app/assets/javascripts/discourse/app/models/nav-item.js index 412cd8ec2e4..955e536e1eb 100644 --- a/app/assets/javascripts/discourse/app/models/nav-item.js +++ b/app/assets/javascripts/discourse/app/models/nav-item.js @@ -273,12 +273,17 @@ export function addNavItem(item) { NavItem.extraNavItemDescriptors.push(item); } -Object.defineProperty(Discourse, "NavItem", { - get() { - deprecated("Import the NavItem class instead of using Discourse.NavItem", { - since: "2.4.0", - dropFrom: "2.5.0" - }); - return NavItem; - } -}); +if (typeof Discourse !== "undefined") { + Object.defineProperty(Discourse, "NavItem", { + get() { + deprecated( + "Import the NavItem class instead of using Discourse.NavItem", + { + since: "2.4.0", + dropFrom: "2.5.0" + } + ); + return NavItem; + } + }); +} diff --git a/app/assets/javascripts/discourse/app/models/topic-details.js b/app/assets/javascripts/discourse/app/models/topic-details.js index 50449aa0e39..d8d5ea0036f 100644 --- a/app/assets/javascripts/discourse/app/models/topic-details.js +++ b/app/assets/javascripts/discourse/app/models/topic-details.js @@ -61,12 +61,15 @@ const TopicDetails = RestModel.extend({ } }, - updateNotifications(v) { - this.set("notification_level", v); - this.set("notifications_reason_id", null); - return ajax("/t/" + this.get("topic.id") + "/notifications", { + updateNotifications(level) { + return ajax(`/t/${this.get("topic.id")}/notifications`, { type: "POST", - data: { notification_level: v } + data: { notification_level: level } + }).then(() => { + this.setProperties({ + notification_level: level, + notifications_reason_id: null + }); }); }, diff --git a/app/assets/javascripts/discourse/app/templates/about.hbs b/app/assets/javascripts/discourse/app/templates/about.hbs index 63cae2c2609..2659b0ebac4 100644 --- a/app/assets/javascripts/discourse/app/templates/about.hbs +++ b/app/assets/javascripts/discourse/app/templates/about.hbs @@ -64,50 +64,52 @@ {{/each}} {{/if}} -
-

{{d-icon "far-chart-bar"}} {{i18n "about.stats"}}

+ {{#if model.can_see_about_stats}} +
+

{{d-icon "far-chart-bar"}} {{i18n "about.stats"}}

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 {{i18n "about.stat.last_7_days"}}{{i18n "about.stat.last_30_days"}}{{i18n "about.stat.all_time"}}
{{i18n "about.topic_count"}}{{number model.stats.topics_7_days}}{{number model.stats.topics_30_days}}{{number model.stats.topic_count}}
{{i18n "about.post_count"}}{{number model.stats.posts_7_days}}{{number model.stats.posts_30_days}}{{number model.stats.post_count}}
{{i18n "about.user_count"}}{{number model.stats.users_7_days}}{{number model.stats.users_30_days}}{{number model.stats.user_count}}
{{i18n "about.active_user_count"}}{{number model.stats.active_users_7_days}}{{number model.stats.active_users_30_days}}
{{i18n "about.like_count"}}{{number model.stats.likes_7_days}}{{number model.stats.likes_30_days}}{{number model.stats.like_count}}
-
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 {{i18n "about.stat.last_7_days"}}{{i18n "about.stat.last_30_days"}}{{i18n "about.stat.all_time"}}
{{i18n "about.topic_count"}}{{number model.stats.topics_7_days}}{{number model.stats.topics_30_days}}{{number model.stats.topic_count}}
{{i18n "about.post_count"}}{{number model.stats.posts_7_days}}{{number model.stats.posts_30_days}}{{number model.stats.post_count}}
{{i18n "about.user_count"}}{{number model.stats.users_7_days}}{{number model.stats.users_30_days}}{{number model.stats.user_count}}
{{i18n "about.active_user_count"}}{{number model.stats.active_users_7_days}}{{number model.stats.active_users_30_days}}
{{i18n "about.like_count"}}{{number model.stats.likes_7_days}}{{number model.stats.likes_30_days}}{{number model.stats.like_count}}
+
+ {{/if}} {{#if contactInfo}}
diff --git a/app/assets/javascripts/discourse/app/templates/components/d-navigation.hbs b/app/assets/javascripts/discourse/app/templates/components/d-navigation.hbs index e9e0b9a34f1..2ac52d5f71b 100644 --- a/app/assets/javascripts/discourse/app/templates/components/d-navigation.hbs +++ b/app/assets/javascripts/discourse/app/templates/components/d-navigation.hbs @@ -3,6 +3,9 @@ {{#if showCategoryAdmin}} {{categories-admin-dropdown onChange=(action "selectCategoryAdminDropdownAction") + options=(hash + triggerOnChangeOnTab=false + ) }} {{/if}} diff --git a/app/assets/javascripts/discourse/app/templates/components/topic-footer-buttons.hbs b/app/assets/javascripts/discourse/app/templates/components/topic-footer-buttons.hbs index 007ec9c19d2..aa5b44a7163 100644 --- a/app/assets/javascripts/discourse/app/templates/components/topic-footer-buttons.hbs +++ b/app/assets/javascripts/discourse/app/templates/components/topic-footer-buttons.hbs @@ -50,10 +50,10 @@ args=(hash topic=topic) tagName="" connectorTagName="span"}} - - {{pinned-button pinned=topic.pinned topic=topic}} +{{pinned-button pinned=topic.pinned topic=topic}} + {{#if showNotificationsButton}} {{topic-notifications-button notificationLevel=topic.details.notification_level diff --git a/app/assets/javascripts/discourse/app/templates/preferences/interface.hbs b/app/assets/javascripts/discourse/app/templates/preferences/interface.hbs index 45607c8cb75..4f1f13c6147 100644 --- a/app/assets/javascripts/discourse/app/templates/preferences/interface.hbs +++ b/app/assets/javascripts/discourse/app/templates/preferences/interface.hbs @@ -5,9 +5,11 @@ {{combo-box content=userSelectableThemes value=themeId - onChange=(action (mut themeId)) }} + {{#if themeIdChanged}} +

{{i18n "user.save_to_change_theme" save_text=(i18n "save") }}

+ {{/if}} {{#if showThemeSetDefault}}
{{preference-checkbox labelKey="user.theme_default_on_all_devices" checked=makeThemeDefault}} diff --git a/app/assets/javascripts/discourse/app/templates/topic.hbs b/app/assets/javascripts/discourse/app/templates/topic.hbs index 488970c10e8..d5e22f54379 100644 --- a/app/assets/javascripts/discourse/app/templates/topic.hbs +++ b/app/assets/javascripts/discourse/app/templates/topic.hbs @@ -36,6 +36,7 @@ options=(hash filterable=true categoryId=buffered.category_id + minimum=minimumRequiredTags ) }} {{/if}} diff --git a/app/assets/javascripts/discourse/app/widgets/header.js b/app/assets/javascripts/discourse/app/widgets/header.js index ca4d73c9256..8e27a15a036 100644 --- a/app/assets/javascripts/discourse/app/widgets/header.js +++ b/app/assets/javascripts/discourse/app/widgets/header.js @@ -125,6 +125,7 @@ createWidget( { attributes: { href: attrs.user.get("path"), + title: attrs.user.get("name"), "data-auto-route": true } }, diff --git a/app/assets/javascripts/select-kit/addon/.gitkeep b/app/assets/javascripts/select-kit/addon/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/app/assets/javascripts/select-kit/app/components/admin-group-selector.js b/app/assets/javascripts/select-kit/addon/components/admin-group-selector.js similarity index 100% rename from app/assets/javascripts/select-kit/app/components/admin-group-selector.js rename to app/assets/javascripts/select-kit/addon/components/admin-group-selector.js diff --git a/app/assets/javascripts/select-kit/app/components/categories-admin-dropdown.js b/app/assets/javascripts/select-kit/addon/components/categories-admin-dropdown.js similarity index 100% rename from app/assets/javascripts/select-kit/app/components/categories-admin-dropdown.js rename to app/assets/javascripts/select-kit/addon/components/categories-admin-dropdown.js diff --git a/app/assets/javascripts/select-kit/app/components/category-chooser.js b/app/assets/javascripts/select-kit/addon/components/category-chooser.js similarity index 99% rename from app/assets/javascripts/select-kit/app/components/category-chooser.js rename to app/assets/javascripts/select-kit/addon/components/category-chooser.js index f36e8be2d32..72d0167ac09 100644 --- a/app/assets/javascripts/select-kit/app/components/category-chooser.js +++ b/app/assets/javascripts/select-kit/addon/components/category-chooser.js @@ -67,6 +67,7 @@ export default ComboBoxComponent.extend({ search(filter) { if (filter) { + filter = filter.toLowerCase(); return this.content.filter(item => { const category = Category.findById(this.getValue(item)); const categoryName = this.getName(item); diff --git a/app/assets/javascripts/select-kit/app/components/category-drop.js b/app/assets/javascripts/select-kit/addon/components/category-drop.js similarity index 100% rename from app/assets/javascripts/select-kit/app/components/category-drop.js rename to app/assets/javascripts/select-kit/addon/components/category-drop.js diff --git a/app/assets/javascripts/select-kit/app/components/category-drop/category-drop-header.js b/app/assets/javascripts/select-kit/addon/components/category-drop/category-drop-header.js similarity index 100% rename from app/assets/javascripts/select-kit/app/components/category-drop/category-drop-header.js rename to app/assets/javascripts/select-kit/addon/components/category-drop/category-drop-header.js diff --git a/app/assets/javascripts/select-kit/app/components/category-notifications-button.js b/app/assets/javascripts/select-kit/addon/components/category-notifications-button.js similarity index 100% rename from app/assets/javascripts/select-kit/app/components/category-notifications-button.js rename to app/assets/javascripts/select-kit/addon/components/category-notifications-button.js diff --git a/app/assets/javascripts/select-kit/app/components/category-row.js b/app/assets/javascripts/select-kit/addon/components/category-row.js similarity index 100% rename from app/assets/javascripts/select-kit/app/components/category-row.js rename to app/assets/javascripts/select-kit/addon/components/category-row.js diff --git a/app/assets/javascripts/select-kit/app/components/category-selector.js b/app/assets/javascripts/select-kit/addon/components/category-selector.js similarity index 100% rename from app/assets/javascripts/select-kit/app/components/category-selector.js rename to app/assets/javascripts/select-kit/addon/components/category-selector.js diff --git a/app/assets/javascripts/select-kit/app/components/color-palettes.js b/app/assets/javascripts/select-kit/addon/components/color-palettes.js similarity index 100% rename from app/assets/javascripts/select-kit/app/components/color-palettes.js rename to app/assets/javascripts/select-kit/addon/components/color-palettes.js diff --git a/app/assets/javascripts/select-kit/app/components/color-palettes/color-palettes-row.js b/app/assets/javascripts/select-kit/addon/components/color-palettes/color-palettes-row.js similarity index 100% rename from app/assets/javascripts/select-kit/app/components/color-palettes/color-palettes-row.js rename to app/assets/javascripts/select-kit/addon/components/color-palettes/color-palettes-row.js diff --git a/app/assets/javascripts/select-kit/app/components/combo-box.js b/app/assets/javascripts/select-kit/addon/components/combo-box.js similarity index 100% rename from app/assets/javascripts/select-kit/app/components/combo-box.js rename to app/assets/javascripts/select-kit/addon/components/combo-box.js diff --git a/app/assets/javascripts/select-kit/app/components/combo-box/combo-box-header.js b/app/assets/javascripts/select-kit/addon/components/combo-box/combo-box-header.js similarity index 100% rename from app/assets/javascripts/select-kit/app/components/combo-box/combo-box-header.js rename to app/assets/javascripts/select-kit/addon/components/combo-box/combo-box-header.js diff --git a/app/assets/javascripts/select-kit/app/components/composer-actions.js b/app/assets/javascripts/select-kit/addon/components/composer-actions.js similarity index 100% rename from app/assets/javascripts/select-kit/app/components/composer-actions.js rename to app/assets/javascripts/select-kit/addon/components/composer-actions.js diff --git a/app/assets/javascripts/select-kit/app/components/create-color-row.js b/app/assets/javascripts/select-kit/addon/components/create-color-row.js similarity index 100% rename from app/assets/javascripts/select-kit/app/components/create-color-row.js rename to app/assets/javascripts/select-kit/addon/components/create-color-row.js diff --git a/app/assets/javascripts/select-kit/app/components/dropdown-select-box.js b/app/assets/javascripts/select-kit/addon/components/dropdown-select-box.js similarity index 100% rename from app/assets/javascripts/select-kit/app/components/dropdown-select-box.js rename to app/assets/javascripts/select-kit/addon/components/dropdown-select-box.js diff --git a/app/assets/javascripts/select-kit/app/components/dropdown-select-box/dropdown-select-box-header.js b/app/assets/javascripts/select-kit/addon/components/dropdown-select-box/dropdown-select-box-header.js similarity index 100% rename from app/assets/javascripts/select-kit/app/components/dropdown-select-box/dropdown-select-box-header.js rename to app/assets/javascripts/select-kit/addon/components/dropdown-select-box/dropdown-select-box-header.js diff --git a/app/assets/javascripts/select-kit/app/components/dropdown-select-box/dropdown-select-box-row.js b/app/assets/javascripts/select-kit/addon/components/dropdown-select-box/dropdown-select-box-row.js similarity index 100% rename from app/assets/javascripts/select-kit/app/components/dropdown-select-box/dropdown-select-box-row.js rename to app/assets/javascripts/select-kit/addon/components/dropdown-select-box/dropdown-select-box-row.js diff --git a/app/assets/javascripts/select-kit/app/components/future-date-input-selector.js b/app/assets/javascripts/select-kit/addon/components/future-date-input-selector.js similarity index 100% rename from app/assets/javascripts/select-kit/app/components/future-date-input-selector.js rename to app/assets/javascripts/select-kit/addon/components/future-date-input-selector.js diff --git a/app/assets/javascripts/select-kit/app/components/future-date-input-selector/future-date-input-selector-header.js b/app/assets/javascripts/select-kit/addon/components/future-date-input-selector/future-date-input-selector-header.js similarity index 100% rename from app/assets/javascripts/select-kit/app/components/future-date-input-selector/future-date-input-selector-header.js rename to app/assets/javascripts/select-kit/addon/components/future-date-input-selector/future-date-input-selector-header.js diff --git a/app/assets/javascripts/select-kit/app/components/future-date-input-selector/future-date-input-selector-row.js b/app/assets/javascripts/select-kit/addon/components/future-date-input-selector/future-date-input-selector-row.js similarity index 100% rename from app/assets/javascripts/select-kit/app/components/future-date-input-selector/future-date-input-selector-row.js rename to app/assets/javascripts/select-kit/addon/components/future-date-input-selector/future-date-input-selector-row.js diff --git a/app/assets/javascripts/select-kit/app/components/future-date-input-selector/mixin.js b/app/assets/javascripts/select-kit/addon/components/future-date-input-selector/mixin.js similarity index 100% rename from app/assets/javascripts/select-kit/app/components/future-date-input-selector/mixin.js rename to app/assets/javascripts/select-kit/addon/components/future-date-input-selector/mixin.js diff --git a/app/assets/javascripts/select-kit/app/components/group-dropdown.js b/app/assets/javascripts/select-kit/addon/components/group-dropdown.js similarity index 100% rename from app/assets/javascripts/select-kit/app/components/group-dropdown.js rename to app/assets/javascripts/select-kit/addon/components/group-dropdown.js diff --git a/app/assets/javascripts/select-kit/app/components/group-members-dropdown.js b/app/assets/javascripts/select-kit/addon/components/group-members-dropdown.js similarity index 100% rename from app/assets/javascripts/select-kit/app/components/group-members-dropdown.js rename to app/assets/javascripts/select-kit/addon/components/group-members-dropdown.js diff --git a/app/assets/javascripts/select-kit/app/components/group-notifications-button.js b/app/assets/javascripts/select-kit/addon/components/group-notifications-button.js similarity index 100% rename from app/assets/javascripts/select-kit/app/components/group-notifications-button.js rename to app/assets/javascripts/select-kit/addon/components/group-notifications-button.js diff --git a/app/assets/javascripts/select-kit/app/components/icon-picker.js b/app/assets/javascripts/select-kit/addon/components/icon-picker.js similarity index 100% rename from app/assets/javascripts/select-kit/app/components/icon-picker.js rename to app/assets/javascripts/select-kit/addon/components/icon-picker.js diff --git a/app/assets/javascripts/select-kit/app/components/list-setting.js b/app/assets/javascripts/select-kit/addon/components/list-setting.js similarity index 100% rename from app/assets/javascripts/select-kit/app/components/list-setting.js rename to app/assets/javascripts/select-kit/addon/components/list-setting.js diff --git a/app/assets/javascripts/select-kit/app/components/mini-tag-chooser.js b/app/assets/javascripts/select-kit/addon/components/mini-tag-chooser.js similarity index 100% rename from app/assets/javascripts/select-kit/app/components/mini-tag-chooser.js rename to app/assets/javascripts/select-kit/addon/components/mini-tag-chooser.js diff --git a/app/assets/javascripts/select-kit/app/components/mini-tag-chooser/mini-tag-chooser-header.js b/app/assets/javascripts/select-kit/addon/components/mini-tag-chooser/mini-tag-chooser-header.js similarity index 100% rename from app/assets/javascripts/select-kit/app/components/mini-tag-chooser/mini-tag-chooser-header.js rename to app/assets/javascripts/select-kit/addon/components/mini-tag-chooser/mini-tag-chooser-header.js diff --git a/app/assets/javascripts/select-kit/app/components/mini-tag-chooser/selected-collection.js b/app/assets/javascripts/select-kit/addon/components/mini-tag-chooser/selected-collection.js similarity index 100% rename from app/assets/javascripts/select-kit/app/components/mini-tag-chooser/selected-collection.js rename to app/assets/javascripts/select-kit/addon/components/mini-tag-chooser/selected-collection.js diff --git a/app/assets/javascripts/select-kit/app/components/multi-select.js b/app/assets/javascripts/select-kit/addon/components/multi-select.js similarity index 100% rename from app/assets/javascripts/select-kit/app/components/multi-select.js rename to app/assets/javascripts/select-kit/addon/components/multi-select.js diff --git a/app/assets/javascripts/select-kit/app/components/multi-select/multi-select-filter.js b/app/assets/javascripts/select-kit/addon/components/multi-select/multi-select-filter.js similarity index 100% rename from app/assets/javascripts/select-kit/app/components/multi-select/multi-select-filter.js rename to app/assets/javascripts/select-kit/addon/components/multi-select/multi-select-filter.js diff --git a/app/assets/javascripts/select-kit/app/components/multi-select/multi-select-header.js b/app/assets/javascripts/select-kit/addon/components/multi-select/multi-select-header.js similarity index 100% rename from app/assets/javascripts/select-kit/app/components/multi-select/multi-select-header.js rename to app/assets/javascripts/select-kit/addon/components/multi-select/multi-select-header.js diff --git a/app/assets/javascripts/select-kit/app/components/multi-select/selected-category.js b/app/assets/javascripts/select-kit/addon/components/multi-select/selected-category.js similarity index 100% rename from app/assets/javascripts/select-kit/app/components/multi-select/selected-category.js rename to app/assets/javascripts/select-kit/addon/components/multi-select/selected-category.js diff --git a/app/assets/javascripts/select-kit/app/components/multi-select/selected-color.js b/app/assets/javascripts/select-kit/addon/components/multi-select/selected-color.js similarity index 100% rename from app/assets/javascripts/select-kit/app/components/multi-select/selected-color.js rename to app/assets/javascripts/select-kit/addon/components/multi-select/selected-color.js diff --git a/app/assets/javascripts/select-kit/app/components/none-category-row.js b/app/assets/javascripts/select-kit/addon/components/none-category-row.js similarity index 100% rename from app/assets/javascripts/select-kit/app/components/none-category-row.js rename to app/assets/javascripts/select-kit/addon/components/none-category-row.js diff --git a/app/assets/javascripts/select-kit/app/components/notifications-button.js b/app/assets/javascripts/select-kit/addon/components/notifications-button.js similarity index 96% rename from app/assets/javascripts/select-kit/app/components/notifications-button.js rename to app/assets/javascripts/select-kit/addon/components/notifications-button.js index f5423b9e64a..32dc2ad7ba1 100644 --- a/app/assets/javascripts/select-kit/app/components/notifications-button.js +++ b/app/assets/javascripts/select-kit/addon/components/notifications-button.js @@ -13,8 +13,7 @@ export default DropdownSelectBoxComponent.extend({ autoFilterable: false, filterable: false, i18nPrefix: "", - i18nPostfix: "", - showCaret: true + i18nPostfix: "" }, modifyComponentForRow() { diff --git a/app/assets/javascripts/select-kit/app/components/notifications-button/notifications-button-row.js b/app/assets/javascripts/select-kit/addon/components/notifications-button/notifications-button-row.js similarity index 100% rename from app/assets/javascripts/select-kit/app/components/notifications-button/notifications-button-row.js rename to app/assets/javascripts/select-kit/addon/components/notifications-button/notifications-button-row.js diff --git a/app/assets/javascripts/select-kit/app/components/notifications-filter.js b/app/assets/javascripts/select-kit/addon/components/notifications-filter.js similarity index 100% rename from app/assets/javascripts/select-kit/app/components/notifications-filter.js rename to app/assets/javascripts/select-kit/addon/components/notifications-filter.js diff --git a/app/assets/javascripts/select-kit/app/components/notifications-filter/notifications-filter-header.js b/app/assets/javascripts/select-kit/addon/components/notifications-filter/notifications-filter-header.js similarity index 100% rename from app/assets/javascripts/select-kit/app/components/notifications-filter/notifications-filter-header.js rename to app/assets/javascripts/select-kit/addon/components/notifications-filter/notifications-filter-header.js diff --git a/app/assets/javascripts/select-kit/app/components/period-chooser.js b/app/assets/javascripts/select-kit/addon/components/period-chooser.js similarity index 100% rename from app/assets/javascripts/select-kit/app/components/period-chooser.js rename to app/assets/javascripts/select-kit/addon/components/period-chooser.js diff --git a/app/assets/javascripts/select-kit/app/components/period-chooser/period-chooser-header.js b/app/assets/javascripts/select-kit/addon/components/period-chooser/period-chooser-header.js similarity index 100% rename from app/assets/javascripts/select-kit/app/components/period-chooser/period-chooser-header.js rename to app/assets/javascripts/select-kit/addon/components/period-chooser/period-chooser-header.js diff --git a/app/assets/javascripts/select-kit/app/components/period-chooser/period-chooser-row.js b/app/assets/javascripts/select-kit/addon/components/period-chooser/period-chooser-row.js similarity index 100% rename from app/assets/javascripts/select-kit/app/components/period-chooser/period-chooser-row.js rename to app/assets/javascripts/select-kit/addon/components/period-chooser/period-chooser-row.js diff --git a/app/assets/javascripts/select-kit/app/components/pinned-button.js b/app/assets/javascripts/select-kit/addon/components/pinned-button.js similarity index 100% rename from app/assets/javascripts/select-kit/app/components/pinned-button.js rename to app/assets/javascripts/select-kit/addon/components/pinned-button.js diff --git a/app/assets/javascripts/select-kit/app/components/pinned-options.js b/app/assets/javascripts/select-kit/addon/components/pinned-options.js similarity index 100% rename from app/assets/javascripts/select-kit/app/components/pinned-options.js rename to app/assets/javascripts/select-kit/addon/components/pinned-options.js diff --git a/app/assets/javascripts/select-kit/app/components/search-advanced-category-chooser.js b/app/assets/javascripts/select-kit/addon/components/search-advanced-category-chooser.js similarity index 100% rename from app/assets/javascripts/select-kit/app/components/search-advanced-category-chooser.js rename to app/assets/javascripts/select-kit/addon/components/search-advanced-category-chooser.js diff --git a/app/assets/javascripts/select-kit/app/components/select-kit.js b/app/assets/javascripts/select-kit/addon/components/select-kit.js similarity index 99% rename from app/assets/javascripts/select-kit/app/components/select-kit.js rename to app/assets/javascripts/select-kit/addon/components/select-kit.js index f2fbbbf1c2b..02acfb5a2e1 100644 --- a/app/assets/javascripts/select-kit/app/components/select-kit.js +++ b/app/assets/javascripts/select-kit/addon/components/select-kit.js @@ -1,5 +1,6 @@ import I18n from "I18n"; -import EmberObject, { computed, get, guidFor } from "@ember/object"; +import EmberObject, { computed, get } from "@ember/object"; +import { guidFor } from "@ember/object/internals"; import Component from "@ember/component"; import deprecated from "discourse-common/lib/deprecated"; import { makeArray } from "discourse-common/lib/helpers"; @@ -271,7 +272,8 @@ export default Component.extend( selectedNameComponent: "selected-name", castInteger: false, preventsClickPropagation: false, - focusAfterOnChange: true + focusAfterOnChange: true, + triggerOnChangeOnTab: true }, autoFilterable: computed("content.[]", "selectKit.filter", function() { diff --git a/app/assets/javascripts/select-kit/app/components/select-kit/errors-collection.js b/app/assets/javascripts/select-kit/addon/components/select-kit/errors-collection.js similarity index 100% rename from app/assets/javascripts/select-kit/app/components/select-kit/errors-collection.js rename to app/assets/javascripts/select-kit/addon/components/select-kit/errors-collection.js diff --git a/app/assets/javascripts/select-kit/app/components/select-kit/select-kit-body.js b/app/assets/javascripts/select-kit/addon/components/select-kit/select-kit-body.js similarity index 100% rename from app/assets/javascripts/select-kit/app/components/select-kit/select-kit-body.js rename to app/assets/javascripts/select-kit/addon/components/select-kit/select-kit-body.js diff --git a/app/assets/javascripts/select-kit/app/components/select-kit/select-kit-collection.js b/app/assets/javascripts/select-kit/addon/components/select-kit/select-kit-collection.js similarity index 100% rename from app/assets/javascripts/select-kit/app/components/select-kit/select-kit-collection.js rename to app/assets/javascripts/select-kit/addon/components/select-kit/select-kit-collection.js diff --git a/app/assets/javascripts/select-kit/app/components/select-kit/select-kit-create-row.js b/app/assets/javascripts/select-kit/addon/components/select-kit/select-kit-create-row.js similarity index 100% rename from app/assets/javascripts/select-kit/app/components/select-kit/select-kit-create-row.js rename to app/assets/javascripts/select-kit/addon/components/select-kit/select-kit-create-row.js diff --git a/app/assets/javascripts/select-kit/app/components/select-kit/select-kit-filter.js b/app/assets/javascripts/select-kit/addon/components/select-kit/select-kit-filter.js similarity index 100% rename from app/assets/javascripts/select-kit/app/components/select-kit/select-kit-filter.js rename to app/assets/javascripts/select-kit/addon/components/select-kit/select-kit-filter.js diff --git a/app/assets/javascripts/select-kit/app/components/select-kit/select-kit-header.js b/app/assets/javascripts/select-kit/addon/components/select-kit/select-kit-header.js similarity index 97% rename from app/assets/javascripts/select-kit/app/components/select-kit/select-kit-header.js rename to app/assets/javascripts/select-kit/addon/components/select-kit/select-kit-header.js index 785b2a47022..5697826f59f 100644 --- a/app/assets/javascripts/select-kit/app/components/select-kit/select-kit-header.js +++ b/app/assets/javascripts/select-kit/addon/components/select-kit/select-kit-header.js @@ -140,7 +140,11 @@ export default Component.extend(UtilsMixin, { this._focusFilterInput(); } else if (event.keyCode === 9) { // Tab - if (this.selectKit.highlighted && this.selectKit.isExpanded) { + if ( + this.selectKit.highlighted && + this.selectKit.isExpanded && + this.selectKit.options.triggerOnChangeOnTab + ) { this.selectKit.select( this.getValue(this.selectKit.highlighted), this.selectKit.highlighted diff --git a/app/assets/javascripts/select-kit/app/components/select-kit/select-kit-none-row.js b/app/assets/javascripts/select-kit/addon/components/select-kit/select-kit-none-row.js similarity index 100% rename from app/assets/javascripts/select-kit/app/components/select-kit/select-kit-none-row.js rename to app/assets/javascripts/select-kit/addon/components/select-kit/select-kit-none-row.js diff --git a/app/assets/javascripts/select-kit/app/components/select-kit/select-kit-row.js b/app/assets/javascripts/select-kit/addon/components/select-kit/select-kit-row.js similarity index 100% rename from app/assets/javascripts/select-kit/app/components/select-kit/select-kit-row.js rename to app/assets/javascripts/select-kit/addon/components/select-kit/select-kit-row.js diff --git a/app/assets/javascripts/select-kit/app/components/select-kit/single-select-header.js b/app/assets/javascripts/select-kit/addon/components/select-kit/single-select-header.js similarity index 100% rename from app/assets/javascripts/select-kit/app/components/select-kit/single-select-header.js rename to app/assets/javascripts/select-kit/addon/components/select-kit/single-select-header.js diff --git a/app/assets/javascripts/select-kit/app/components/selected-color.js b/app/assets/javascripts/select-kit/addon/components/selected-color.js similarity index 100% rename from app/assets/javascripts/select-kit/app/components/selected-color.js rename to app/assets/javascripts/select-kit/addon/components/selected-color.js diff --git a/app/assets/javascripts/select-kit/app/components/selected-name.js b/app/assets/javascripts/select-kit/addon/components/selected-name.js similarity index 100% rename from app/assets/javascripts/select-kit/app/components/selected-name.js rename to app/assets/javascripts/select-kit/addon/components/selected-name.js diff --git a/app/assets/javascripts/select-kit/app/components/single-select.js b/app/assets/javascripts/select-kit/addon/components/single-select.js similarity index 100% rename from app/assets/javascripts/select-kit/app/components/single-select.js rename to app/assets/javascripts/select-kit/addon/components/single-select.js diff --git a/app/assets/javascripts/select-kit/app/components/tag-chooser-row.js b/app/assets/javascripts/select-kit/addon/components/tag-chooser-row.js similarity index 100% rename from app/assets/javascripts/select-kit/app/components/tag-chooser-row.js rename to app/assets/javascripts/select-kit/addon/components/tag-chooser-row.js diff --git a/app/assets/javascripts/select-kit/app/components/tag-chooser.js b/app/assets/javascripts/select-kit/addon/components/tag-chooser.js similarity index 100% rename from app/assets/javascripts/select-kit/app/components/tag-chooser.js rename to app/assets/javascripts/select-kit/addon/components/tag-chooser.js diff --git a/app/assets/javascripts/select-kit/app/components/tag-drop.js b/app/assets/javascripts/select-kit/addon/components/tag-drop.js similarity index 100% rename from app/assets/javascripts/select-kit/app/components/tag-drop.js rename to app/assets/javascripts/select-kit/addon/components/tag-drop.js diff --git a/app/assets/javascripts/select-kit/app/components/tag-drop/tag-drop-header.js b/app/assets/javascripts/select-kit/addon/components/tag-drop/tag-drop-header.js similarity index 100% rename from app/assets/javascripts/select-kit/app/components/tag-drop/tag-drop-header.js rename to app/assets/javascripts/select-kit/addon/components/tag-drop/tag-drop-header.js diff --git a/app/assets/javascripts/select-kit/app/components/tag-group-chooser.js b/app/assets/javascripts/select-kit/addon/components/tag-group-chooser.js similarity index 100% rename from app/assets/javascripts/select-kit/app/components/tag-group-chooser.js rename to app/assets/javascripts/select-kit/addon/components/tag-group-chooser.js diff --git a/app/assets/javascripts/select-kit/app/components/tag-notifications-button.js b/app/assets/javascripts/select-kit/addon/components/tag-notifications-button.js similarity index 100% rename from app/assets/javascripts/select-kit/app/components/tag-notifications-button.js rename to app/assets/javascripts/select-kit/addon/components/tag-notifications-button.js diff --git a/app/assets/javascripts/select-kit/app/components/tag-row.js b/app/assets/javascripts/select-kit/addon/components/tag-row.js similarity index 100% rename from app/assets/javascripts/select-kit/app/components/tag-row.js rename to app/assets/javascripts/select-kit/addon/components/tag-row.js diff --git a/app/assets/javascripts/select-kit/app/components/timezone-input.js b/app/assets/javascripts/select-kit/addon/components/timezone-input.js similarity index 100% rename from app/assets/javascripts/select-kit/app/components/timezone-input.js rename to app/assets/javascripts/select-kit/addon/components/timezone-input.js diff --git a/app/assets/javascripts/select-kit/app/components/toolbar-popup-menu-options.js b/app/assets/javascripts/select-kit/addon/components/toolbar-popup-menu-options.js similarity index 100% rename from app/assets/javascripts/select-kit/app/components/toolbar-popup-menu-options.js rename to app/assets/javascripts/select-kit/addon/components/toolbar-popup-menu-options.js diff --git a/app/assets/javascripts/select-kit/app/components/toolbar-popup-menu-options/toolbar-popup-menu-options-heading.js b/app/assets/javascripts/select-kit/addon/components/toolbar-popup-menu-options/toolbar-popup-menu-options-heading.js similarity index 100% rename from app/assets/javascripts/select-kit/app/components/toolbar-popup-menu-options/toolbar-popup-menu-options-heading.js rename to app/assets/javascripts/select-kit/addon/components/toolbar-popup-menu-options/toolbar-popup-menu-options-heading.js diff --git a/app/assets/javascripts/select-kit/app/components/topic-footer-mobile-dropdown.js b/app/assets/javascripts/select-kit/addon/components/topic-footer-mobile-dropdown.js similarity index 100% rename from app/assets/javascripts/select-kit/app/components/topic-footer-mobile-dropdown.js rename to app/assets/javascripts/select-kit/addon/components/topic-footer-mobile-dropdown.js diff --git a/app/assets/javascripts/select-kit/app/components/topic-notifications-button.js b/app/assets/javascripts/select-kit/addon/components/topic-notifications-button.js similarity index 55% rename from app/assets/javascripts/select-kit/app/components/topic-notifications-button.js rename to app/assets/javascripts/select-kit/addon/components/topic-notifications-button.js index 58329393e7c..d9c09001823 100644 --- a/app/assets/javascripts/select-kit/app/components/topic-notifications-button.js +++ b/app/assets/javascripts/select-kit/addon/components/topic-notifications-button.js @@ -1,20 +1,28 @@ import Component from "@ember/component"; -import { action } from "@ember/object"; +import { action, computed } from "@ember/object"; export default Component.extend({ layoutName: "select-kit/templates/components/topic-notifications-button", classNames: ["topic-notifications-button"], + classNameBindings: ["isLoading"], appendReason: true, showFullTitle: true, placement: "bottom-start", notificationLevel: null, topic: null, showCaret: true, + isLoading: false, + icon: computed("isLoading", function() { + return this.isLoading ? "spinner" : null; + }), @action changeTopicNotificationLevel(levelId) { if (levelId !== this.notificationLevel) { - this.topic.details.updateNotifications(levelId); + this.set("isLoading", true); + this.topic.details + .updateNotifications(levelId) + .finally(() => this.set("isLoading", false)); } } }); diff --git a/app/assets/javascripts/select-kit/app/components/topic-notifications-options.js b/app/assets/javascripts/select-kit/addon/components/topic-notifications-options.js similarity index 100% rename from app/assets/javascripts/select-kit/app/components/topic-notifications-options.js rename to app/assets/javascripts/select-kit/addon/components/topic-notifications-options.js diff --git a/app/assets/javascripts/select-kit/app/components/user-chooser.js b/app/assets/javascripts/select-kit/addon/components/user-chooser.js similarity index 100% rename from app/assets/javascripts/select-kit/app/components/user-chooser.js rename to app/assets/javascripts/select-kit/addon/components/user-chooser.js diff --git a/app/assets/javascripts/select-kit/app/components/user-chooser/user-row.js b/app/assets/javascripts/select-kit/addon/components/user-chooser/user-row.js similarity index 100% rename from app/assets/javascripts/select-kit/app/components/user-chooser/user-row.js rename to app/assets/javascripts/select-kit/addon/components/user-chooser/user-row.js diff --git a/app/assets/javascripts/select-kit/app/components/user-notifications-dropdown.js b/app/assets/javascripts/select-kit/addon/components/user-notifications-dropdown.js similarity index 100% rename from app/assets/javascripts/select-kit/app/components/user-notifications-dropdown.js rename to app/assets/javascripts/select-kit/addon/components/user-notifications-dropdown.js diff --git a/app/assets/javascripts/select-kit/app/mixins/plugin-api.js b/app/assets/javascripts/select-kit/addon/mixins/plugin-api.js similarity index 100% rename from app/assets/javascripts/select-kit/app/mixins/plugin-api.js rename to app/assets/javascripts/select-kit/addon/mixins/plugin-api.js diff --git a/app/assets/javascripts/select-kit/app/mixins/tags.js b/app/assets/javascripts/select-kit/addon/mixins/tags.js similarity index 100% rename from app/assets/javascripts/select-kit/app/mixins/tags.js rename to app/assets/javascripts/select-kit/addon/mixins/tags.js diff --git a/app/assets/javascripts/select-kit/app/mixins/utils.js b/app/assets/javascripts/select-kit/addon/mixins/utils.js similarity index 100% rename from app/assets/javascripts/select-kit/app/mixins/utils.js rename to app/assets/javascripts/select-kit/addon/mixins/utils.js diff --git a/app/assets/javascripts/select-kit/app/templates/components/category-drop/category-drop-header.hbs b/app/assets/javascripts/select-kit/addon/templates/components/category-drop/category-drop-header.hbs similarity index 100% rename from app/assets/javascripts/select-kit/app/templates/components/category-drop/category-drop-header.hbs rename to app/assets/javascripts/select-kit/addon/templates/components/category-drop/category-drop-header.hbs diff --git a/app/assets/javascripts/select-kit/app/templates/components/category-row.hbs b/app/assets/javascripts/select-kit/addon/templates/components/category-row.hbs similarity index 100% rename from app/assets/javascripts/select-kit/app/templates/components/category-row.hbs rename to app/assets/javascripts/select-kit/addon/templates/components/category-row.hbs diff --git a/app/assets/javascripts/select-kit/app/templates/components/color-palettes/color-palettes-row.hbs b/app/assets/javascripts/select-kit/addon/templates/components/color-palettes/color-palettes-row.hbs similarity index 100% rename from app/assets/javascripts/select-kit/app/templates/components/color-palettes/color-palettes-row.hbs rename to app/assets/javascripts/select-kit/addon/templates/components/color-palettes/color-palettes-row.hbs diff --git a/app/assets/javascripts/select-kit/app/templates/components/combo-box/combo-box-header.hbs b/app/assets/javascripts/select-kit/addon/templates/components/combo-box/combo-box-header.hbs similarity index 100% rename from app/assets/javascripts/select-kit/app/templates/components/combo-box/combo-box-header.hbs rename to app/assets/javascripts/select-kit/addon/templates/components/combo-box/combo-box-header.hbs diff --git a/app/assets/javascripts/select-kit/app/templates/components/create-color-row.hbs b/app/assets/javascripts/select-kit/addon/templates/components/create-color-row.hbs similarity index 100% rename from app/assets/javascripts/select-kit/app/templates/components/create-color-row.hbs rename to app/assets/javascripts/select-kit/addon/templates/components/create-color-row.hbs diff --git a/app/assets/javascripts/select-kit/app/templates/components/dropdown-select-box/dropdown-select-box-header.hbs b/app/assets/javascripts/select-kit/addon/templates/components/dropdown-select-box/dropdown-select-box-header.hbs similarity index 100% rename from app/assets/javascripts/select-kit/app/templates/components/dropdown-select-box/dropdown-select-box-header.hbs rename to app/assets/javascripts/select-kit/addon/templates/components/dropdown-select-box/dropdown-select-box-header.hbs diff --git a/app/assets/javascripts/select-kit/app/templates/components/dropdown-select-box/dropdown-select-box-row.hbs b/app/assets/javascripts/select-kit/addon/templates/components/dropdown-select-box/dropdown-select-box-row.hbs similarity index 100% rename from app/assets/javascripts/select-kit/app/templates/components/dropdown-select-box/dropdown-select-box-row.hbs rename to app/assets/javascripts/select-kit/addon/templates/components/dropdown-select-box/dropdown-select-box-row.hbs diff --git a/app/assets/javascripts/select-kit/app/templates/components/future-date-input-selector/future-date-input-selector-header.hbs b/app/assets/javascripts/select-kit/addon/templates/components/future-date-input-selector/future-date-input-selector-header.hbs similarity index 100% rename from app/assets/javascripts/select-kit/app/templates/components/future-date-input-selector/future-date-input-selector-header.hbs rename to app/assets/javascripts/select-kit/addon/templates/components/future-date-input-selector/future-date-input-selector-header.hbs diff --git a/app/assets/javascripts/select-kit/app/templates/components/future-date-input-selector/future-date-input-selector-row.hbs b/app/assets/javascripts/select-kit/addon/templates/components/future-date-input-selector/future-date-input-selector-row.hbs similarity index 100% rename from app/assets/javascripts/select-kit/app/templates/components/future-date-input-selector/future-date-input-selector-row.hbs rename to app/assets/javascripts/select-kit/addon/templates/components/future-date-input-selector/future-date-input-selector-row.hbs diff --git a/app/assets/javascripts/select-kit/app/templates/components/mini-tag-chooser/mini-tag-chooser-header.hbs b/app/assets/javascripts/select-kit/addon/templates/components/mini-tag-chooser/mini-tag-chooser-header.hbs similarity index 100% rename from app/assets/javascripts/select-kit/app/templates/components/mini-tag-chooser/mini-tag-chooser-header.hbs rename to app/assets/javascripts/select-kit/addon/templates/components/mini-tag-chooser/mini-tag-chooser-header.hbs diff --git a/app/assets/javascripts/select-kit/app/templates/components/mini-tag-chooser/selected-collection.hbs b/app/assets/javascripts/select-kit/addon/templates/components/mini-tag-chooser/selected-collection.hbs similarity index 100% rename from app/assets/javascripts/select-kit/app/templates/components/mini-tag-chooser/selected-collection.hbs rename to app/assets/javascripts/select-kit/addon/templates/components/mini-tag-chooser/selected-collection.hbs diff --git a/app/assets/javascripts/select-kit/app/templates/components/multi-select.hbs b/app/assets/javascripts/select-kit/addon/templates/components/multi-select.hbs similarity index 100% rename from app/assets/javascripts/select-kit/app/templates/components/multi-select.hbs rename to app/assets/javascripts/select-kit/addon/templates/components/multi-select.hbs diff --git a/app/assets/javascripts/select-kit/app/templates/components/multi-select/multi-select-header.hbs b/app/assets/javascripts/select-kit/addon/templates/components/multi-select/multi-select-header.hbs similarity index 100% rename from app/assets/javascripts/select-kit/app/templates/components/multi-select/multi-select-header.hbs rename to app/assets/javascripts/select-kit/addon/templates/components/multi-select/multi-select-header.hbs diff --git a/app/assets/javascripts/select-kit/app/templates/components/multi-select/selected-category.hbs b/app/assets/javascripts/select-kit/addon/templates/components/multi-select/selected-category.hbs similarity index 100% rename from app/assets/javascripts/select-kit/app/templates/components/multi-select/selected-category.hbs rename to app/assets/javascripts/select-kit/addon/templates/components/multi-select/selected-category.hbs diff --git a/app/assets/javascripts/select-kit/app/templates/components/notifications-filter/notifications-filter-header.hbs b/app/assets/javascripts/select-kit/addon/templates/components/notifications-filter/notifications-filter-header.hbs similarity index 100% rename from app/assets/javascripts/select-kit/app/templates/components/notifications-filter/notifications-filter-header.hbs rename to app/assets/javascripts/select-kit/addon/templates/components/notifications-filter/notifications-filter-header.hbs diff --git a/app/assets/javascripts/select-kit/app/templates/components/period-chooser/period-chooser-header.hbs b/app/assets/javascripts/select-kit/addon/templates/components/period-chooser/period-chooser-header.hbs similarity index 100% rename from app/assets/javascripts/select-kit/app/templates/components/period-chooser/period-chooser-header.hbs rename to app/assets/javascripts/select-kit/addon/templates/components/period-chooser/period-chooser-header.hbs diff --git a/app/assets/javascripts/select-kit/app/templates/components/period-chooser/period-chooser-row.hbs b/app/assets/javascripts/select-kit/addon/templates/components/period-chooser/period-chooser-row.hbs similarity index 100% rename from app/assets/javascripts/select-kit/app/templates/components/period-chooser/period-chooser-row.hbs rename to app/assets/javascripts/select-kit/addon/templates/components/period-chooser/period-chooser-row.hbs diff --git a/app/assets/javascripts/select-kit/addon/templates/components/pinned-button.hbs b/app/assets/javascripts/select-kit/addon/templates/components/pinned-button.hbs new file mode 100644 index 00000000000..91bfe147ff6 --- /dev/null +++ b/app/assets/javascripts/select-kit/addon/templates/components/pinned-button.hbs @@ -0,0 +1,4 @@ +

+ {{pinned-options value=pinned topic=topic}} + {{html-safe reasonText}} +

diff --git a/app/assets/javascripts/select-kit/app/templates/components/select-kit/errors-collection.hbs b/app/assets/javascripts/select-kit/addon/templates/components/select-kit/errors-collection.hbs similarity index 100% rename from app/assets/javascripts/select-kit/app/templates/components/select-kit/errors-collection.hbs rename to app/assets/javascripts/select-kit/addon/templates/components/select-kit/errors-collection.hbs diff --git a/app/assets/javascripts/select-kit/app/templates/components/select-kit/select-kit-body.hbs b/app/assets/javascripts/select-kit/addon/templates/components/select-kit/select-kit-body.hbs similarity index 100% rename from app/assets/javascripts/select-kit/app/templates/components/select-kit/select-kit-body.hbs rename to app/assets/javascripts/select-kit/addon/templates/components/select-kit/select-kit-body.hbs diff --git a/app/assets/javascripts/select-kit/app/templates/components/select-kit/select-kit-collection.hbs b/app/assets/javascripts/select-kit/addon/templates/components/select-kit/select-kit-collection.hbs similarity index 100% rename from app/assets/javascripts/select-kit/app/templates/components/select-kit/select-kit-collection.hbs rename to app/assets/javascripts/select-kit/addon/templates/components/select-kit/select-kit-collection.hbs diff --git a/app/assets/javascripts/select-kit/app/templates/components/select-kit/select-kit-filter.hbs b/app/assets/javascripts/select-kit/addon/templates/components/select-kit/select-kit-filter.hbs similarity index 100% rename from app/assets/javascripts/select-kit/app/templates/components/select-kit/select-kit-filter.hbs rename to app/assets/javascripts/select-kit/addon/templates/components/select-kit/select-kit-filter.hbs diff --git a/app/assets/javascripts/select-kit/app/templates/components/select-kit/select-kit-row.hbs b/app/assets/javascripts/select-kit/addon/templates/components/select-kit/select-kit-row.hbs similarity index 100% rename from app/assets/javascripts/select-kit/app/templates/components/select-kit/select-kit-row.hbs rename to app/assets/javascripts/select-kit/addon/templates/components/select-kit/select-kit-row.hbs diff --git a/app/assets/javascripts/select-kit/app/templates/components/select-kit/single-select-header.hbs b/app/assets/javascripts/select-kit/addon/templates/components/select-kit/single-select-header.hbs similarity index 100% rename from app/assets/javascripts/select-kit/app/templates/components/select-kit/single-select-header.hbs rename to app/assets/javascripts/select-kit/addon/templates/components/select-kit/single-select-header.hbs diff --git a/app/assets/javascripts/select-kit/app/templates/components/selected-name.hbs b/app/assets/javascripts/select-kit/addon/templates/components/selected-name.hbs similarity index 100% rename from app/assets/javascripts/select-kit/app/templates/components/selected-name.hbs rename to app/assets/javascripts/select-kit/addon/templates/components/selected-name.hbs diff --git a/app/assets/javascripts/select-kit/app/templates/components/single-select.hbs b/app/assets/javascripts/select-kit/addon/templates/components/single-select.hbs similarity index 100% rename from app/assets/javascripts/select-kit/app/templates/components/single-select.hbs rename to app/assets/javascripts/select-kit/addon/templates/components/single-select.hbs diff --git a/app/assets/javascripts/select-kit/app/templates/components/tag-chooser-row.hbs b/app/assets/javascripts/select-kit/addon/templates/components/tag-chooser-row.hbs similarity index 100% rename from app/assets/javascripts/select-kit/app/templates/components/tag-chooser-row.hbs rename to app/assets/javascripts/select-kit/addon/templates/components/tag-chooser-row.hbs diff --git a/app/assets/javascripts/select-kit/app/templates/components/tag-drop/tag-drop-header.hbs b/app/assets/javascripts/select-kit/addon/templates/components/tag-drop/tag-drop-header.hbs similarity index 100% rename from app/assets/javascripts/select-kit/app/templates/components/tag-drop/tag-drop-header.hbs rename to app/assets/javascripts/select-kit/addon/templates/components/tag-drop/tag-drop-header.hbs diff --git a/app/assets/javascripts/select-kit/app/templates/components/tag-row.hbs b/app/assets/javascripts/select-kit/addon/templates/components/tag-row.hbs similarity index 100% rename from app/assets/javascripts/select-kit/app/templates/components/tag-row.hbs rename to app/assets/javascripts/select-kit/addon/templates/components/tag-row.hbs diff --git a/app/assets/javascripts/select-kit/app/templates/components/toolbar-popup-menu-options/toolbar-popup-menu-options-heading.hbs b/app/assets/javascripts/select-kit/addon/templates/components/toolbar-popup-menu-options/toolbar-popup-menu-options-heading.hbs similarity index 100% rename from app/assets/javascripts/select-kit/app/templates/components/toolbar-popup-menu-options/toolbar-popup-menu-options-heading.hbs rename to app/assets/javascripts/select-kit/addon/templates/components/toolbar-popup-menu-options/toolbar-popup-menu-options-heading.hbs diff --git a/app/assets/javascripts/select-kit/addon/templates/components/topic-notifications-button.hbs b/app/assets/javascripts/select-kit/addon/templates/components/topic-notifications-button.hbs new file mode 100644 index 00000000000..f21111ebbd5 --- /dev/null +++ b/app/assets/javascripts/select-kit/addon/templates/components/topic-notifications-button.hbs @@ -0,0 +1,30 @@ +{{#if appendReason}} +

+ {{topic-notifications-options + value=notificationLevel + topic=topic + onChange=(action "changeTopicNotificationLevel") + options=(hash + icon=icon + showFullTitle=showFullTitle + placement=placement + preventsClickPropagation=true + showCaret=showCaret + ) + }} + {{html-safe topic.details.notificationReasonText}} +

+{{else}} + {{topic-notifications-options + value=notificationLevel + topic=topic + onChange=(action "changeTopicNotificationLevel") + options=(hash + icon=icon + showFullTitle=showFullTitle + placement=placement + preventsClickPropagation=true + showCaret=showCaret + ) + }} +{{/if}} diff --git a/app/assets/javascripts/select-kit/app/templates/components/user-chooser/user-row.hbs b/app/assets/javascripts/select-kit/addon/templates/components/user-chooser/user-row.hbs similarity index 100% rename from app/assets/javascripts/select-kit/app/templates/components/user-chooser/user-row.hbs rename to app/assets/javascripts/select-kit/addon/templates/components/user-chooser/user-row.hbs diff --git a/app/assets/javascripts/select-kit/app/templates/components/pinned-button.hbs b/app/assets/javascripts/select-kit/app/templates/components/pinned-button.hbs deleted file mode 100644 index f54bdb6b5fe..00000000000 --- a/app/assets/javascripts/select-kit/app/templates/components/pinned-button.hbs +++ /dev/null @@ -1,5 +0,0 @@ -{{pinned-options value=pinned topic=topic}} - -

- {{html-safe reasonText}} -

diff --git a/app/assets/javascripts/select-kit/app/templates/components/topic-notifications-button.hbs b/app/assets/javascripts/select-kit/app/templates/components/topic-notifications-button.hbs deleted file mode 100644 index 50dd20e56eb..00000000000 --- a/app/assets/javascripts/select-kit/app/templates/components/topic-notifications-button.hbs +++ /dev/null @@ -1,17 +0,0 @@ -{{topic-notifications-options - value=notificationLevel - topic=topic - onChange=(action "changeTopicNotificationLevel") - options=(hash - showFullTitle=showFullTitle - placement=placement - preventsClickPropagation=true - showCaret=showCaret - ) -}} - -{{#if appendReason}} -

- {{html-safe topic.details.notificationReasonText}} -

-{{/if}} diff --git a/app/assets/javascripts/wizard-application.js b/app/assets/javascripts/wizard-application.js index b8e8797f0c6..4e2a7adc072 100644 --- a/app/assets/javascripts/wizard-application.js +++ b/app/assets/javascripts/wizard-application.js @@ -1,6 +1,6 @@ //= require_tree ./discourse-common/addon //= require i18n-patches -//= require_tree ./select-kit/app +//= require_tree ./select-kit/addon //= require wizard/router //= require wizard/wizard //= require_tree ./wizard/templates diff --git a/app/assets/stylesheets/common/base/_topic-list.scss b/app/assets/stylesheets/common/base/_topic-list.scss index aa51fccce15..a80e54c4fda 100644 --- a/app/assets/stylesheets/common/base/_topic-list.scss +++ b/app/assets/stylesheets/common/base/_topic-list.scss @@ -53,7 +53,6 @@ color: $primary-medium; } span.badge-category { - font-weight: normal; color: $primary-medium; } a.discourse-tag { diff --git a/app/assets/stylesheets/common/base/alert.scss b/app/assets/stylesheets/common/base/alert.scss index d729cdc7423..05a29029059 100644 --- a/app/assets/stylesheets/common/base/alert.scss +++ b/app/assets/stylesheets/common/base/alert.scss @@ -3,6 +3,7 @@ background-color: $danger-low; color: $primary; position: relative; + margin-bottom: 1em; .close { font-size: $font-up-3; diff --git a/app/assets/stylesheets/common/base/crawler_layout.scss b/app/assets/stylesheets/common/base/crawler_layout.scss index c279aceff4c..bff0addac1a 100644 --- a/app/assets/stylesheets/common/base/crawler_layout.scss +++ b/app/assets/stylesheets/common/base/crawler_layout.scss @@ -27,6 +27,36 @@ body.crawler { .topic-list { margin-bottom: 1em; + @media (max-width: 850px) { + td { + word-break: break-all; + &.posters { + a:not(:last-of-type) { + display: none; + } + a:last-of-type { + display: block; + } + } + } + + td, + th { + &.views { + display: none; + } + } + + .link-top-line { + a.title { + padding: 0; + } + } + + .link-bottom-line { + margin-top: 0.25em; + } + } } footer { diff --git a/app/assets/stylesheets/common/base/topic-post.scss b/app/assets/stylesheets/common/base/topic-post.scss index 6e465bbc6b6..48c361c1647 100644 --- a/app/assets/stylesheets/common/base/topic-post.scss +++ b/app/assets/stylesheets/common/base/topic-post.scss @@ -1005,10 +1005,51 @@ a.mention-group { } #topic-footer-buttons { - .reason { - color: $primary-high; - display: inline; - margin: 0 0 0 8px; - line-height: $line-height-medium; + padding: 0.5em 0; + + .topic-footer-main-buttons { + margin: 0 0 -0.5em 0; + display: flex; + flex-wrap: wrap; + + > .btn { + margin: 0 0.5em 0.5em 0; + display: inline-flex; + align-items: center; + + .d-button-label { + display: flex; + flex: 1 0 auto; + align-items: center; + } + } + + .topic-admin-menu-button-container { + display: inline-flex; + } + + .topic-admin-menu-button-container > span:not(:empty) { + margin: 0 0.5em 0.5em 0; + } + } + + .pinned-button:not(.is-hidden) + .topic-notifications-button { + margin-top: 0; + } + + .pinned-button, + .topic-notifications-button { + margin: 1em 0; + + .reason { + color: $primary-high; + display: inline-flex; + margin: 0; + align-items: center; + + .text { + margin-left: 0.5em; + } + } } } diff --git a/app/assets/stylesheets/common/base/user.scss b/app/assets/stylesheets/common/base/user.scss index 587af3c3539..4d5e9f42886 100644 --- a/app/assets/stylesheets/common/base/user.scss +++ b/app/assets/stylesheets/common/base/user.scss @@ -653,6 +653,9 @@ padding: 5px 0; font-weight: bold; } + .save-theme-alert { + font-size: $font-down-1; + } } .paginated-topics-list { diff --git a/app/assets/stylesheets/common/components/badges.scss b/app/assets/stylesheets/common/components/badges.scss index 3881ee7b70f..20ed535931d 100644 --- a/app/assets/stylesheets/common/components/badges.scss +++ b/app/assets/stylesheets/common/components/badges.scss @@ -17,7 +17,6 @@ .badge-wrapper { font-size: $font-down-1; - font-weight: bold; white-space: nowrap; position: relative; display: inline-flex; diff --git a/app/assets/stylesheets/common/select-kit/category-row.scss b/app/assets/stylesheets/common/select-kit/category-row.scss index 2bbe33c96ea..df43c956035 100644 --- a/app/assets/stylesheets/common/select-kit/category-row.scss +++ b/app/assets/stylesheets/common/select-kit/category-row.scss @@ -9,6 +9,9 @@ -ms-flex: 1 1 auto; flex: 1 1 auto; } + .category-desc p { + margin: 0; + } .category-status { .badge-wrapper.box { margin-bottom: 1px; diff --git a/app/assets/stylesheets/common/select-kit/pinned-button.scss b/app/assets/stylesheets/common/select-kit/pinned-button.scss index f080b8a016c..3e51092ae39 100644 --- a/app/assets/stylesheets/common/select-kit/pinned-button.scss +++ b/app/assets/stylesheets/common/select-kit/pinned-button.scss @@ -1,28 +1,7 @@ #topic-footer-buttons { .pinned-button { - min-width: auto; - margin: 1em 0; - &.is-hidden { display: none; } - - .btn { - margin: 0; - } - - .reason { - display: inline; - line-height: $line-height-medium; - } - } -} - -.pinned-button { - margin: 0; - min-width: auto; - - .pinned-options { - display: inline; } } diff --git a/app/assets/stylesheets/common/select-kit/select-kit.scss b/app/assets/stylesheets/common/select-kit/select-kit.scss index 130bad52f80..a9769b59710 100644 --- a/app/assets/stylesheets/common/select-kit/select-kit.scss +++ b/app/assets/stylesheets/common/select-kit/select-kit.scss @@ -63,6 +63,11 @@ flex-direction: row; min-height: 30px; + .d-icon-spinner { + -webkit-animation: rotate-forever 1s infinite linear; + animation: rotate-forever 1s infinite linear; + } + .selected-name { text-align: left; flex: 0 1 auto; diff --git a/app/assets/stylesheets/common/select-kit/topic-notifications-button.scss b/app/assets/stylesheets/common/select-kit/topic-notifications-button.scss index 1e7c7972054..b3808031a1d 100644 --- a/app/assets/stylesheets/common/select-kit/topic-notifications-button.scss +++ b/app/assets/stylesheets/common/select-kit/topic-notifications-button.scss @@ -1,27 +1,18 @@ -#topic-footer-buttons { - .topic-notifications-button { - min-width: auto; - margin: 1em 0; +.topic-notifications-button { + &.is-loading { + pointer-events: none; + user-select: none; - .btn { + .d-icon-spinner { margin: 0; } - .reason { - display: inline; - line-height: $line-height-medium; + .selected-name .d-icon { + display: none; + } + + .topic-notifications-options { + opacity: 0.5; } } } - -.topic-notifications-button .topic-notifications-options { - min-width: auto; -} - -.topic-notifications-button { - margin: 0; - - .topic-notifications-options { - display: inline-flex; - } -} diff --git a/app/assets/stylesheets/desktop.scss b/app/assets/stylesheets/desktop.scss index 70229afbbc1..e9213e897b3 100644 --- a/app/assets/stylesheets/desktop.scss +++ b/app/assets/stylesheets/desktop.scss @@ -1,24 +1,6 @@ @import "common"; -/* @import "desktop/*"; TODO: get this working again */ - -@import "desktop/alert"; -@import "desktop/banner"; -@import "desktop/compose"; -@import "desktop/discourse"; -@import "desktop/header"; -@import "desktop/login"; -@import "desktop/modal"; -@import "desktop/category-list"; -@import "desktop/latest-topic-list"; -@import "desktop/topic-list"; -@import "desktop/topic-post"; -@import "desktop/topic"; -@import "desktop/upload"; -@import "desktop/user"; -@import "desktop/history"; -@import "desktop/group"; -@import "desktop/admin_customize"; +@import "desktop/*"; // Import all component-specific files @import "desktop/components/*"; diff --git a/app/assets/stylesheets/desktop/alert.scss b/app/assets/stylesheets/desktop/alert.scss deleted file mode 100644 index f3f57eac668..00000000000 --- a/app/assets/stylesheets/desktop/alert.scss +++ /dev/null @@ -1,3 +0,0 @@ -.alert { - margin-bottom: 1em; -} diff --git a/app/assets/stylesheets/desktop/topic-post.scss b/app/assets/stylesheets/desktop/topic-post.scss index ca304b8e951..5adc17d3eb1 100644 --- a/app/assets/stylesheets/desktop/topic-post.scss +++ b/app/assets/stylesheets/desktop/topic-post.scss @@ -451,20 +451,15 @@ pre.copy-codeblocks:hover .copy-cmd { } } -@mixin topic-footer-button { - margin-bottom: 5px; - margin-right: 10px; -} - #topic-footer-buttons { - padding: 10px 10px 0 0; - float: left; - .btn { - @include topic-footer-button; + max-width: calc(690px + (11px * 2) + 45px); + + .bookmark { .d-icon-bookmark.bookmarked { color: $tertiary; } } + .bookmark.bookmarked .d-icon-bookmark, .bookmark.bookmarked .d-icon-discourse-bookmark-clock { color: $tertiary; @@ -474,10 +469,6 @@ pre.copy-codeblocks:hover .copy-cmd { } } -#topic-footer-button { - width: 757px; -} - .suggested-topics { clear: left; padding: 20px 0 15px 0; diff --git a/app/assets/stylesheets/mobile.scss b/app/assets/stylesheets/mobile.scss index ee9c3ed92df..a3202ca43c1 100644 --- a/app/assets/stylesheets/mobile.scss +++ b/app/assets/stylesheets/mobile.scss @@ -1,37 +1,6 @@ @import "common"; -/* @import "mobile/*"; TODO: get this working again */ - -@import "mobile/buttons"; -@import "mobile/alert"; -@import "mobile/banner"; -@import "mobile/compose"; -@import "mobile/discourse"; -@import "mobile/header"; -@import "mobile/login"; -@import "mobile/modal"; -@import "mobile/topic-list"; -@import "mobile/topic-post"; -@import "mobile/topic"; -@import "mobile/upload"; -@import "mobile/user"; -@import "mobile/history"; -@import "mobile/lightbox"; -@import "mobile/directory"; -@import "mobile/search"; -@import "mobile/emoji"; -@import "mobile/ring"; -@import "mobile/group"; -@import "mobile/dashboard"; -@import "mobile/admin_badges"; -@import "mobile/admin_customize"; -@import "mobile/admin_reports"; -@import "mobile/admin_report"; -@import "mobile/admin_report_table"; -@import "mobile/admin_report_counters"; -@import "mobile/admin_emojis"; -@import "mobile/menu-panel"; -@import "mobile/reviewables"; +@import "mobile/*"; // Import all component-specific files @import "mobile/components/*"; diff --git a/app/assets/stylesheets/mobile/alert.scss b/app/assets/stylesheets/mobile/alert.scss index 0ee9b44ee0e..12e60245e57 100644 --- a/app/assets/stylesheets/mobile/alert.scss +++ b/app/assets/stylesheets/mobile/alert.scss @@ -1,7 +1,8 @@ -// there are (n) new or updated topics, click to show .alert.alert-info { - margin: 0; + margin-bottom: 0.5em; &.clickable { + // there are (n) new or updated topics, click to show + margin-bottom: 0; padding: 1em; } } diff --git a/app/assets/stylesheets/mobile/banner.scss b/app/assets/stylesheets/mobile/banner.scss index 852b2847364..fc308f05880 100644 --- a/app/assets/stylesheets/mobile/banner.scss +++ b/app/assets/stylesheets/mobile/banner.scss @@ -3,11 +3,6 @@ // -------------------------------------------------- #banner { - // go full width on mobile, by extending into the 10px wrap - // borders on left and right - margin: 0 -10px; max-height: 180px; - @media all and (max-height: 499px) { - max-height: 100px; - } + height: 20vh; } diff --git a/app/assets/stylesheets/mobile/discourse.scss b/app/assets/stylesheets/mobile/discourse.scss index 260f8d4b13f..c9406abca6b 100644 --- a/app/assets/stylesheets/mobile/discourse.scss +++ b/app/assets/stylesheets/mobile/discourse.scss @@ -63,11 +63,6 @@ blockquote { margin-bottom: 9px; } -// categories should not be bold on mobile; they fight with the topic title too much -.badge-wrapper { - font-weight: normal; -} - .mobile-nav { margin: 0; padding: 0; diff --git a/app/assets/stylesheets/mobile/topic-post.scss b/app/assets/stylesheets/mobile/topic-post.scss index 9cd4931ce83..d84b4a727cf 100644 --- a/app/assets/stylesheets/mobile/topic-post.scss +++ b/app/assets/stylesheets/mobile/topic-post.scss @@ -36,6 +36,7 @@ span.badge-posts { .double-button { display: flex; flex: 0 1 auto; + align-items: center; button { &.like, &.read-indicator, @@ -235,20 +236,16 @@ a.reply-to-tab { } #topic-footer-buttons { - @include clearfix; - padding: 20px 0 0 0; .d-icon-bookmark.bookmarked, .d-icon-discourse-bookmark-clock.bookmarked { color: $tertiary; } - .combobox { - margin-right: 0.5em; - width: 160px; - margin-bottom: 0.5em; - } -} -#topic-footer-buttons { + .topic-footer-mobile-dropdown { + margin: 0 0.5em 0.5em 0; + width: 160px; + } + .topic-notifications-button, .pinned-button { display: flex; @@ -263,11 +260,6 @@ a.reply-to-tab { } } -#topic-footer-button { - width: 100px; - margin: 0 auto; -} - .suggested-topics { clear: left; padding: 20px 0 15px 0; @@ -292,13 +284,6 @@ span.post-count { opacity: 0.8; } -#topic-footer-buttons { - .btn { - margin-bottom: 0.5em; - margin-right: 0.5em; - } -} - #topic-title { z-index: z("base") + 1; margin: 0 0 0 0 !important; diff --git a/app/controllers/about_controller.rb b/app/controllers/about_controller.rb index fe2d3a2327d..3f3f9e15a36 100644 --- a/app/controllers/about_controller.rb +++ b/app/controllers/about_controller.rb @@ -16,7 +16,7 @@ class AboutController < ApplicationController render :index end format.json do - render_serialized(@about, AboutSerializer) + render_json_dump(AboutSerializer.new(@about, scope: guardian)) end end end diff --git a/app/controllers/list_controller.rb b/app/controllers/list_controller.rb index 81511387f73..5eadbbcf6d7 100644 --- a/app/controllers/list_controller.rb +++ b/app/controllers/list_controller.rb @@ -331,7 +331,13 @@ class ListController < ApplicationController params[:category] = @category.id.to_s - @description_meta = @category.description_text + @description_meta = if @category.uncategorized? + I18n.t('category.uncategorized_description', locale: SiteSetting.default_locale) + else + @category.description_text + end + @description_meta = SiteSetting.site_description if @description_meta.blank? + if !guardian.can_see?(@category) if SiteSetting.detailed_404 raise Discourse::InvalidAccess diff --git a/app/controllers/themes_controller.rb b/app/controllers/themes_controller.rb deleted file mode 100644 index 6520ec70975..00000000000 --- a/app/controllers/themes_controller.rb +++ /dev/null @@ -1,31 +0,0 @@ -# frozen_string_literal: true - -class ThemesController < ::ApplicationController - def assets - theme_ids = params[:ids].to_s.split("-").map(&:to_i) - - if params[:ids] == "default" - theme_ids = nil - else - raise Discourse::NotFound unless guardian.allow_themes?(theme_ids) - end - - targets = view_context.mobile_view? ? [:mobile, :mobile_theme] : [:desktop, :desktop_theme] - targets << :admin if guardian.is_staff? - targets.append(*Discourse.find_plugin_css_assets(mobile_view: true, desktop_view: true)) - - object = targets.map do |target| - Stylesheet::Manager.stylesheet_data(target, theme_ids).map do |hash| - next hash unless Rails.env.development? - - dup_hash = hash.dup - dup_hash[:new_href] = dup_hash[:new_href].dup - dup_hash[:new_href] << (dup_hash[:new_href].include?("?") ? "&" : "?") - dup_hash[:new_href] << SecureRandom.hex - dup_hash - end - end.flatten - - render json: object.as_json - end -end diff --git a/app/helpers/email_helper.rb b/app/helpers/email_helper.rb index c579473a4d2..14364d45c32 100644 --- a/app/helpers/email_helper.rb +++ b/app/helpers/email_helper.rb @@ -25,12 +25,8 @@ module EmailHelper raw "#{title}" end - def email_html_template(binding_arg) - template = EmailStyle.new.html.sub( - '%{email_content}', - '<%= yield %><% if defined?(html_body) %><%= html_body %><% end %>' - ) - ERB.new(template).result(binding_arg) + def email_html_template + EmailStyle.new.html.sub('%{email_content}', yield).html_safe end protected diff --git a/app/models/application_request.rb b/app/models/application_request.rb index 52a6a8a87e0..3d0d8ae905f 100644 --- a/app/models/application_request.rb +++ b/app/models/application_request.rb @@ -16,15 +16,16 @@ class ApplicationRequest < ActiveRecord::Base include CachedCounting def self.disable - @enabled = false + @disabled = true end def self.enable - @enabled = true + @disabled = false end def self.increment!(type, opts = nil) - perform_increment!(redis_key(type), opts) if @enabled + return if @disabled + perform_increment!(redis_key(type), opts) end def self.write_cache!(date = nil) diff --git a/app/models/category.rb b/app/models/category.rb index 63db814752e..6ecfe9d3afe 100644 --- a/app/models/category.rb +++ b/app/models/category.rb @@ -709,37 +709,29 @@ class Category < ActiveRecord::Base ].include? id end - @@url_cache = DistributedCache.new('category_url') + def full_slug(separator = "-") + start_idx = "#{Discourse.base_uri}/c/".size + url[start_idx..-1].gsub("/", separator) + end + + @@url_cache = DistributedCache.new("category_url") def clear_url_cache @@url_cache.clear end - def full_slug(separator = "-") - start_idx = "#{Discourse.base_uri}/c/".length - url[start_idx..-1].gsub("/", separator) - end - def url - url = @@url_cache[self.id] - unless url - url = "#{Discourse.base_uri}/c/#{slug_path.join('/')}" - - @@url_cache[self.id] = url - end - - url + @@url_cache[self.id] ||= "#{Discourse.base_uri}/c/#{slug_path.join('/')}" end def url_with_id self.parent_category ? "#{url}/#{self.id}" : "#{Discourse.base_uri}/c/#{self.slug}/#{self.id}" end - # If the name changes, try and update the category definition topic too if it's - # an exact match + # If the name changes, try and update the category definition topic too if it's an exact match def rename_category_definition - old_name = saved_changes.transform_values(&:first)["name"] return unless topic.present? + old_name = saved_changes.transform_values(&:first)["name"] if topic.title == I18n.t("category.topic_prefix", category: old_name) topic.update_attribute(:title, I18n.t("category.topic_prefix", category: name)) end diff --git a/app/models/global_setting.rb b/app/models/global_setting.rb index f4425919ed3..21e5a137dcf 100644 --- a/app/models/global_setting.rb +++ b/app/models/global_setting.rb @@ -164,9 +164,15 @@ class GlobalSetting c[:port] = redis_port if redis_port if redis_slave_host && redis_slave_port - c[:slave_host] = redis_slave_host - c[:slave_port] = redis_slave_port - c[:connector] = DiscourseRedis::Connector + if ENV["RAILS_FAILOVER"] + c[:replica_host] = redis_slave_host + c[:replica_port] = redis_slave_port + c[:connector] = RailsFailover::Redis::Connector + else + c[:slave_host] = redis_slave_host + c[:slave_port] = redis_slave_port + c[:connector] = DiscourseRedis::Connector + end end c[:password] = redis_password if redis_password.present? @@ -188,9 +194,15 @@ class GlobalSetting c[:port] = message_bus_redis_port if message_bus_redis_port if message_bus_redis_slave_host && message_bus_redis_slave_port - c[:slave_host] = message_bus_redis_slave_host - c[:slave_port] = message_bus_redis_slave_port - c[:connector] = DiscourseRedis::Connector + if ENV["RAILS_FAILOVER"] + c[:replica_host] = message_bus_redis_slave_host + c[:replica_port] = message_bus_redis_slave_port + c[:connector] = RailsFailover::Redis::Connector + else + c[:slave_host] = message_bus_redis_slave_host + c[:slave_port] = message_bus_redis_slave_port + c[:connector] = DiscourseRedis::Connector + end end c[:password] = message_bus_redis_password if message_bus_redis_password.present? diff --git a/app/models/group_archived_message.rb b/app/models/group_archived_message.rb index 1715a57585f..8199a53cf62 100644 --- a/app/models/group_archived_message.rb +++ b/app/models/group_archived_message.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class GroupArchivedMessage < ActiveRecord::Base - belongs_to :user + belongs_to :group belongs_to :topic def self.move_to_inbox!(group_id, topic) diff --git a/app/models/post.rb b/app/models/post.rb index 89dbbcfebba..e1a3a1f391e 100644 --- a/app/models/post.rb +++ b/app/models/post.rb @@ -159,14 +159,6 @@ class Post < ActiveRecord::Base includes(:post_details).find_by(post_details: { key: key, value: value }) end - def self.excerpt_size=(sz) - @excerpt_size = sz - end - - def self.excerpt_size - @excerpt_size || 220 - end - def whisper? post_type == Post.types[:whisper] end @@ -482,7 +474,7 @@ class Post < ActiveRecord::Base end def excerpt_for_topic - Post.excerpt(cooked, Post.excerpt_size, strip_links: true, strip_images: true, post: self) + Post.excerpt(cooked, SiteSetting.topic_excerpt_maxlength, strip_links: true, strip_images: true, post: self) end def is_first_post? @@ -658,6 +650,10 @@ class Post < ActiveRecord::Base baked_version: BAKED_VERSION ) + if is_first_post? + topic.update_excerpt(excerpt_for_topic) + end + if invalidate_broken_images custom_fields.delete(BROKEN_IMAGES) save_custom_fields diff --git a/app/models/topic.rb b/app/models/topic.rb index d331a308060..38affdb327b 100644 --- a/app/models/topic.rb +++ b/app/models/topic.rb @@ -33,11 +33,11 @@ class Topic < ActiveRecord::Base end def self.thumbnail_sizes - [ self.share_thumbnail_size ] + [ self.share_thumbnail_size ] + DiscoursePluginRegistry.topic_thumbnail_sizes end - def thumbnail_job_redis_key(extra_sizes) - "generate_topic_thumbnail_enqueue_#{id}_#{extra_sizes.inspect}" + def thumbnail_job_redis_key(sizes) + "generate_topic_thumbnail_enqueue_#{id}_#{sizes.inspect}" end def filtered_topic_thumbnails(extra_sizes: []) @@ -79,7 +79,7 @@ class Topic < ActiveRecord::Base if SiteSetting.create_thumbnails && enqueue_if_missing && records.length < thumbnail_sizes.length && - Discourse.redis.set(thumbnail_job_redis_key(extra_sizes), 1, nx: true, ex: 1.minute) + Discourse.redis.set(thumbnail_job_redis_key(thumbnail_sizes), 1, nx: true, ex: 1.minute) Jobs.enqueue(:generate_topic_thumbnails, { topic_id: id, extra_sizes: extra_sizes }) end @@ -1476,6 +1476,13 @@ class Topic < ActiveRecord::Base private_topic end + def update_excerpt(excerpt) + update_column(:excerpt, excerpt) + if archetype == "banner" + ApplicationController.banner_json_cache.clear + end + end + def pm_with_non_human_user? sql = <<~SQL SELECT 1 FROM topics diff --git a/app/models/topic_embed.rb b/app/models/topic_embed.rb index a18e5b85dea..362ee79f4ac 100644 --- a/app/models/topic_embed.rb +++ b/app/models/topic_embed.rb @@ -109,6 +109,8 @@ class TopicEmbed < ActiveRecord::Base url = UrlHelper.escape_uri(url) original_uri = URI.parse(url) + raise URI::InvalidURIError unless original_uri.is_a?(URI::HTTP) + opts = { tags: %w[div p code pre h1 h2 h3 b em i strong a img ul li ol blockquote], attributes: %w[href src class], diff --git a/app/models/upload.rb b/app/models/upload.rb index bb4e7976f5e..31c931c172b 100644 --- a/app/models/upload.rb +++ b/app/models/upload.rb @@ -448,7 +448,3 @@ end # index_uploads_on_url (url) # index_uploads_on_user_id (user_id) # -# Foreign Keys -# -# fk_rails_... (access_control_post_id => posts.id) -# diff --git a/app/models/user.rb b/app/models/user.rb index 9d682a62bd8..d9678d1a1c7 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -7,102 +7,95 @@ class User < ActiveRecord::Base include SecondFactorManager include HasDestroyedWebHook + DEFAULT_FEATURED_BADGE_COUNT = 3 + + # not deleted on user delete has_many :posts - has_many :notifications, dependent: :delete_all - has_many :topic_users, dependent: :delete_all + has_many :topics + has_many :uploads + has_many :category_users, dependent: :destroy has_many :tag_users, dependent: :destroy has_many :user_api_keys, dependent: :destroy - has_many :topics - has_many :bookmarks + has_many :topic_allowed_users, dependent: :destroy + has_many :user_archived_messages, dependent: :destroy + has_many :email_change_requests, dependent: :destroy + has_many :email_tokens, dependent: :destroy + has_many :invites, dependent: :destroy + has_many :topic_links, dependent: :destroy + has_many :user_uploads, dependent: :destroy + has_many :user_emails, dependent: :destroy + has_many :user_associated_accounts, dependent: :destroy + has_many :oauth2_user_infos, dependent: :destroy + has_many :user_second_factors, dependent: :destroy + has_many :user_badges, -> { for_enabled_badges }, dependent: :destroy + has_many :user_auth_tokens, dependent: :destroy + has_many :group_users, dependent: :destroy + has_many :user_warnings, dependent: :destroy + has_many :api_keys, dependent: :destroy + has_many :push_subscriptions, dependent: :destroy + has_many :acting_group_histories, dependent: :destroy, foreign_key: :acting_user_id, class_name: 'GroupHistory' + has_many :targeted_group_histories, dependent: :destroy, foreign_key: :target_user_id, class_name: 'GroupHistory' + has_many :reviewable_scores, dependent: :destroy - # dependent deleting handled via before_destroy + has_one :user_option, dependent: :destroy + has_one :user_avatar, dependent: :destroy + has_one :github_user_info, dependent: :destroy + has_one :primary_email, -> { where(primary: true) }, class_name: 'UserEmail', dependent: :destroy + has_one :user_stat, dependent: :destroy + has_one :user_profile, dependent: :destroy, inverse_of: :user + has_one :single_sign_on_record, dependent: :destroy + has_one :anonymous_user_master, class_name: 'AnonymousUser', dependent: :destroy + has_one :anonymous_user_shadow, ->(record) { where(active: true) }, foreign_key: :master_user_id, class_name: 'AnonymousUser', dependent: :destroy + + # delete all is faster but bypasses callbacks + has_many :bookmarks, dependent: :delete_all + has_many :notifications, dependent: :delete_all + has_many :topic_users, dependent: :delete_all + has_many :email_logs, dependent: :delete_all + has_many :incoming_emails, dependent: :delete_all + has_many :user_visits, dependent: :delete_all + has_many :user_auth_token_logs, dependent: :delete_all + has_many :group_requests, dependent: :delete_all + has_many :muted_user_records, class_name: 'MutedUser', dependent: :delete_all + has_many :ignored_user_records, class_name: 'IgnoredUser', dependent: :delete_all + + # dependent deleting handled via before_destroy (special cases) has_many :user_actions has_many :post_actions + has_many :post_timings + has_many :directory_items + has_many :security_keys, -> { + where(enabled: true) + }, class_name: "UserSecurityKey" - DEFAULT_FEATURED_BADGE_COUNT = 3 - - has_many :user_badges, -> { for_enabled_badges }, dependent: :destroy has_many :badges, through: :user_badges has_many :default_featured_user_badges, -> { for_enabled_badges.grouped_with_count.where("featured_rank <= ?", DEFAULT_FEATURED_BADGE_COUNT) }, class_name: "UserBadge" - has_many :email_logs, dependent: :delete_all - has_many :incoming_emails, dependent: :delete_all - has_many :post_timings - has_many :topic_allowed_users, dependent: :destroy has_many :topics_allowed, through: :topic_allowed_users, source: :topic - has_many :email_tokens, dependent: :destroy - has_many :user_visits, dependent: :destroy - has_many :invites, dependent: :destroy - has_many :topic_links, dependent: :destroy - has_many :uploads - has_many :user_warnings - has_many :user_archived_messages, dependent: :destroy - has_many :email_change_requests, dependent: :destroy - - # see before_destroy - has_many :directory_items - has_many :user_auth_tokens, dependent: :destroy - has_many :user_auth_token_logs, dependent: :destroy - - has_many :group_users, dependent: :destroy has_many :groups, through: :group_users - has_many :group_requests, dependent: :destroy has_many :secure_categories, through: :groups, source: :categories - has_many :user_uploads, dependent: :destroy - has_many :user_emails, dependent: :destroy - - has_one :primary_email, -> { where(primary: true) }, class_name: 'UserEmail', dependent: :destroy - - has_one :user_option, dependent: :destroy - has_one :user_avatar, dependent: :destroy - has_many :user_associated_accounts, dependent: :destroy - has_one :github_user_info, dependent: :destroy - has_many :oauth2_user_infos, dependent: :destroy - has_many :user_second_factors, dependent: :destroy - + # deleted in user_second_factors relationship has_many :totps, -> { where(method: UserSecondFactor.methods[:totp], enabled: true) }, class_name: "UserSecondFactor" - has_many :security_keys, -> { - where(enabled: true) - }, class_name: "UserSecurityKey" - - has_one :anonymous_user_master, class_name: 'AnonymousUser' - has_one :anonymous_user_shadow, ->(record) { where(active: true) }, foreign_key: :master_user_id, class_name: 'AnonymousUser' - has_one :master_user, through: :anonymous_user_master has_one :shadow_user, through: :anonymous_user_shadow, source: :user - has_one :user_stat, dependent: :destroy - has_one :user_profile, dependent: :destroy, inverse_of: :user has_one :profile_background_upload, through: :user_profile has_one :card_background_upload, through: :user_profile - has_one :single_sign_on_record, dependent: :destroy belongs_to :approved_by, class_name: 'User' belongs_to :primary_group, class_name: 'Group' - has_many :muted_user_records, class_name: 'MutedUser' has_many :muted_users, through: :muted_user_records - - has_many :ignored_user_records, class_name: 'IgnoredUser' has_many :ignored_users, through: :ignored_user_records - has_many :api_keys, dependent: :destroy - - has_many :push_subscriptions, dependent: :destroy - belongs_to :uploaded_avatar, class_name: 'Upload' - has_many :acting_group_histories, dependent: :destroy, foreign_key: :acting_user_id, class_name: 'GroupHistory' - has_many :targeted_group_histories, dependent: :destroy, foreign_key: :target_user_id, class_name: 'GroupHistory' - - has_many :reviewable_scores, dependent: :destroy - delegate :last_sent_email_address, to: :email_logs validates_presence_of :username @@ -164,6 +157,9 @@ class User < ActiveRecord::Base DirectoryItem.where(user_id: self.id) .where('period_type in (?)', DirectoryItem.period_types.values) .delete_all + + # our relationship filters on enabled, this makes sure everything is deleted + UserSecurityKey.where(user_id: self.id).delete_all end # Skip validating email, for example from a particular auth provider plugin @@ -933,7 +929,7 @@ class User < ActiveRecord::Base def activate if email_token = self.email_tokens.active.where(email: self.email).first - user = EmailToken.confirm(email_token.token, skip_reviewable: true) + EmailToken.confirm(email_token.token, skip_reviewable: true) end self.update!(active: true) create_reviewable @@ -1119,10 +1115,9 @@ class User < ActiveRecord::Base end def number_of_rejected_posts - Post.with_deleted - .where(user_id: self.id) - .joins('INNER JOIN reviewables r ON posts.id = r.target_id') - .where(r: { status: Reviewable.statuses[:rejected], type: ReviewableQueuedPost.name }) + ReviewableQueuedPost + .where(status: Reviewable.statuses[:rejected]) + .where(created_by_id: self.id) .count end diff --git a/app/models/user_avatar.rb b/app/models/user_avatar.rb index f6dbf133126..bd2751d6b45 100644 --- a/app/models/user_avatar.rb +++ b/app/models/user_avatar.rb @@ -5,6 +5,14 @@ class UserAvatar < ActiveRecord::Base belongs_to :gravatar_upload, class_name: 'Upload' belongs_to :custom_upload, class_name: 'Upload' + @@custom_user_gravatar_email_hash = { + Discourse::SYSTEM_USER_ID => User.email_hash("info@discourse.org") + } + + def self.register_custom_user_gravatar_email_hash(user_id, email) + @@custom_user_gravatar_email_hash[user_id] = User.email_hash(email) + end + def contains_upload?(id) gravatar_upload_id == id || custom_upload_id == id end @@ -12,14 +20,14 @@ class UserAvatar < ActiveRecord::Base def update_gravatar! DistributedMutex.synchronize("update_gravatar_#{user_id}") do begin - self.update!(last_gravatar_download_attempt: Time.now) + self.update!(last_gravatar_download_attempt: Time.zone.now) max = Discourse.avatar_sizes.max # The user could be deleted before this executes return if user.blank? || user.primary_email.blank? - email_hash = user_id == Discourse::SYSTEM_USER_ID ? User.email_hash("info@discourse.org") : user.email_hash + email_hash = @@custom_user_gravatar_email_hash[user_id] || user.email_hash gravatar_url = "https://#{SiteSetting.gravatar_base_url}/avatar/#{email_hash}.png?s=#{max}&d=404&reset_cache=#{SecureRandom.urlsafe_base64(5)}" # follow redirects in case gravatar change rules on us diff --git a/app/models/web_hook_event_type.rb b/app/models/web_hook_event_type.rb index 952c1c2062c..442f14cb8ce 100644 --- a/app/models/web_hook_event_type.rb +++ b/app/models/web_hook_event_type.rb @@ -7,8 +7,6 @@ class WebHookEventType < ActiveRecord::Base GROUP = 4 CATEGORY = 5 TAG = 6 - FLAG = 7 - QUEUED_POST = 8 REVIEWABLE = 9 NOTIFICATION = 10 SOLVED = 11 diff --git a/app/serializers/about_serializer.rb b/app/serializers/about_serializer.rb index 308f990fd6c..8230c590a4c 100644 --- a/app/serializers/about_serializer.rb +++ b/app/serializers/about_serializer.rb @@ -21,7 +21,16 @@ class AboutSerializer < ApplicationSerializer :title, :locale, :version, - :https + :https, + :can_see_about_stats + + def can_see_about_stats + scope.can_see_about_stats? + end + + def include_stats? + can_see_about_stats + end def stats object.class.fetch_cached_stats || Jobs::AboutStats.new.execute({}) diff --git a/app/services/badge_granter.rb b/app/services/badge_granter.rb index e402fb880cc..a5027c53030 100644 --- a/app/services/badge_granter.rb +++ b/app/services/badge_granter.rb @@ -3,11 +3,11 @@ class BadgeGranter def self.disable_queue - @queue_enabled = false + @queue_disabled = true end def self.enable_queue - @queue_enabled = true + @queue_disabled = false end def initialize(badge, user, opts = {}) @@ -124,7 +124,7 @@ class BadgeGranter end def self.queue_badge_grant(type, opt) - return if !SiteSetting.enable_badges || !@queue_enabled + return if !SiteSetting.enable_badges || @queue_disabled payload = nil case type diff --git a/app/views/email/default_template.html b/app/views/email/default_template.html index 382f939273b..99b2e94995c 100644 --- a/app/views/email/default_template.html +++ b/app/views/email/default_template.html @@ -3,6 +3,8 @@ + + diff --git a/app/views/layouts/email_template.html.erb b/app/views/layouts/email_template.html.erb index 80da92efc20..611eba0df49 100644 --- a/app/views/layouts/email_template.html.erb +++ b/app/views/layouts/email_template.html.erb @@ -2,5 +2,8 @@ <%= yield %> <% if defined?(html_body) %><%= html_body %><% end %> <% else %> - <%= email_html_template(binding).html_safe %> + <%= email_html_template do %> + <%= yield %> + <% if defined?(html_body) %><%= html_body %><% end %> + <% end %> <% end %> diff --git a/app/views/layouts/publish.html.erb b/app/views/layouts/publish.html.erb index 5bb0f957c58..4a249e8f32d 100644 --- a/app/views/layouts/publish.html.erb +++ b/app/views/layouts/publish.html.erb @@ -2,13 +2,8 @@ - - + <%= render partial: "layouts/head" %> <%= render partial: "common/discourse_publish_stylesheet" %> - - <%- if @canonical_url -%> - - <%- end -%> <%= yield %> diff --git a/app/views/list/list.erb b/app/views/list/list.erb index 2d32efb77bf..2338d39c6a5 100644 --- a/app/views/list/list.erb +++ b/app/views/list/list.erb @@ -47,8 +47,8 @@ <%= t 'js.topic.title' %> - <%= t 'js.replies' %> - <%= t 'js.views' %> + <%= t 'js.replies' %> + <%= t 'js.views' %> <%= t 'js.activity' %> @@ -101,10 +101,10 @@ <% end %> <% end %> - + '><%= t.posts_count %> - + '><%= t.views %> diff --git a/config/application.rb b/config/application.rb index 43a8bf3091d..0157dc82895 100644 --- a/config/application.rb +++ b/config/application.rb @@ -27,6 +27,10 @@ require_relative '../lib/discourse_plugin_registry' require_relative '../lib/plugin_gem' +if ENV['RAILS_FAILOVER'] + require 'rails_failover' +end + # Global config require_relative '../app/models/global_setting' GlobalSetting.configure! @@ -250,7 +254,7 @@ module Discourse # Our templates shouldn't start with 'discourse/app/templates' config.handlebars.templates_root = { 'discourse/app/templates' => '', - 'select-kit/app/templates' => 'select-kit/templates/' + 'select-kit/addon/templates' => 'select-kit/templates/' } config.handlebars.raw_template_namespace = "__DISCOURSE_RAW_TEMPLATES" diff --git a/config/initializers/001-redis.rb b/config/initializers/001-redis.rb index e48441594e9..f28c59a3b1d 100644 --- a/config/initializers/001-redis.rb +++ b/config/initializers/001-redis.rb @@ -2,5 +2,20 @@ if Rails.env.development? && ENV['DISCOURSE_FLUSH_REDIS'] puts "Flushing redis (development mode)" - Discourse.redis.flushall + Discourse.redis.flushdb +end + +if ENV['RAILS_FAILOVER'] + message_bus_keepalive_interval = MessageBus.keepalive_interval + + RailsFailover::Redis.register_master_up_callback do + MessageBus.keepalive_interval = message_bus_keepalive_interval + Discourse.clear_readonly! + Discourse.request_refresh! + end + + RailsFailover::Redis.register_master_down_callback do + # Disables MessageBus keepalive when Redis is in readonly mode + MessageBus.keepalive_interval = 0 + end end diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 9746f37f376..3c3618ef96e 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -963,6 +963,7 @@ en: api_approved: "Approved:" api_last_used_at: "Last used at:" theme: "Theme" + save_to_change_theme: 'Theme will be updated after you click "%{save_text}"' home: "Default Home Page" staged: "Staged" @@ -2100,7 +2101,7 @@ en: help: "Move message back to Inbox" edit_message: help: "Edit first post of the message" - title: "Edit Message" + title: "Edit" defer: help: "Mark as unread" title: "Defer" @@ -3598,12 +3599,6 @@ en: tag_event: name: "Tag Event" details: "When a tag is created, updated or destroyed." - flag_event: - name: "Flag Event" - details: "When a flag is created, agreed, disagreed or ignored." - queued_post_event: - name: "Post Approval Event" - details: "When a new queued post is created, approved or rejected." reviewable_event: name: "Reviewable Event" details: "When a new item is ready for review and when its status is updated." diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index 06f02313eb9..2341f05c729 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -1467,6 +1467,7 @@ en: exclude_rel_nofollow_domains: "A list of domains where nofollow should not be added to links. example.com will automatically allow sub.example.com as well. As a minimum, you should add the domain of this site to help web crawlers find all content. If other parts of your website are at other domains, add those too." post_excerpt_maxlength: "Maximum length of a post excerpt / summary." + topic_excerpt_maxlength: "Maximum length of a topic excerpt / summary, generated from the first post in a topic." show_pinned_excerpt_mobile: "Show excerpt on pinned topics in mobile view." show_pinned_excerpt_desktop: "Show excerpt on pinned topics in desktop view." post_onebox_maxlength: "Maximum length of a oneboxed Discourse post in characters." diff --git a/config/routes.rb b/config/routes.rb index d3a1082d435..69ade71938a 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -23,6 +23,7 @@ Discourse::Application.routes.draw do post "webhooks/sparkpost" => "webhooks#sparkpost" scope path: nil, constraints: { format: /.*/ } do + Sidekiq::Web.set :sessions, Rails.application.config.session_options if Rails.env.development? mount Sidekiq::Web => "/sidekiq" mount Logster::Web => "/logs" @@ -120,7 +121,6 @@ Discourse::Application.routes.draw do put "unsuspend" put "revoke_admin", constraints: AdminConstraint.new put "grant_admin", constraints: AdminConstraint.new - post "generate_api_key", constraints: AdminConstraint.new put "revoke_moderation", constraints: AdminConstraint.new put "grant_moderation", constraints: AdminConstraint.new put "approve" @@ -950,8 +950,6 @@ Discourse::Application.routes.draw do get "/safe-mode" => "safe_mode#index" post "/safe-mode" => "safe_mode#enter", as: "safe_mode_enter" - get "/themes/assets/:ids" => "themes#assets" - unless Rails.env.production? get "/qunit" => "qunit#index" get "/wizard/qunit" => "wizard#qunit" diff --git a/config/site_settings.yml b/config/site_settings.yml index d49bb0e4cfa..6209cc621aa 100644 --- a/config/site_settings.yml +++ b/config/site_settings.yml @@ -394,7 +394,7 @@ login: default: false github_client_id: default: "" - regex: "^[a-f0-9]+$" + regex: "^[a-zA-Z0-9\\.]+$" github_client_secret: default: "" regex: "^[a-f0-9]+$" @@ -819,6 +819,12 @@ posting: ja: 120 zh_CN: 120 zh_TW: 120 + topic_excerpt_maxlength: + default: 220 + locale_default: + ja: 120 + zh_CN: 120 + zh_TW: 120 show_pinned_excerpt_mobile: client: true default: true @@ -1382,7 +1388,8 @@ security: - Lax - Strict - Disabled - regex: "^(Lax|Strict|Disabled)$" + - None + regex: "^(Lax|Strict|Disabled|None)$" enable_escaped_fragments: true allow_index_in_robots_txt: true moderators_create_categories: false diff --git a/db/fixtures/007_web_hook_event_types.rb b/db/fixtures/007_web_hook_event_types.rb index 8bf8e938055..ec666ec5ad2 100644 --- a/db/fixtures/007_web_hook_event_types.rb +++ b/db/fixtures/007_web_hook_event_types.rb @@ -30,16 +30,6 @@ WebHookEventType.seed do |b| b.name = "tag" end -WebHookEventType.seed do |b| - b.id = WebHookEventType::FLAG - b.name = "flag" -end - -WebHookEventType.seed do |b| - b.id = WebHookEventType::QUEUED_POST - b.name = "queued_post" -end - WebHookEventType.seed do |b| b.id = WebHookEventType::REVIEWABLE b.name = "reviewable" diff --git a/db/migrate/20200409033412_create_bookmarks_from_post_action_bookmarks.rb b/db/migrate/20200409033412_create_bookmarks_from_post_action_bookmarks.rb index 092a0e7b992..57b1885e350 100644 --- a/db/migrate/20200409033412_create_bookmarks_from_post_action_bookmarks.rb +++ b/db/migrate/20200409033412_create_bookmarks_from_post_action_bookmarks.rb @@ -16,6 +16,7 @@ class CreateBookmarksFromPostActionBookmarks < ActiveRecord::Migration[6.0] INNER JOIN posts ON posts.id = post_actions.post_id LEFT JOIN bookmarks ON bookmarks.post_id = post_actions.post_id AND bookmarks.user_id = post_actions.user_id INNER JOIN topics ON topics.id = posts.topic_id + INNER JOIN users ON users.id = post_actions.user_id WHERE bookmarks.id IS NULL AND post_action_type_id = :type_id AND post_actions.deleted_at IS NULL AND posts.deleted_at IS NULL LIMIT 2000 SQL diff --git a/db/migrate/20200514075356_remove_flag_web_hooks.rb b/db/migrate/20200514075356_remove_flag_web_hooks.rb new file mode 100644 index 00000000000..448b7fb771b --- /dev/null +++ b/db/migrate/20200514075356_remove_flag_web_hooks.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +class RemoveFlagWebHooks < ActiveRecord::Migration[6.0] + def up + flag_event_type_id = 7 + + DB.exec <<~SQL + DELETE FROM web_hook_event_types_hooks + WHERE web_hook_event_type_id = #{flag_event_type_id} + SQL + + DB.exec <<~SQL + DELETE FROM web_hook_event_types + WHERE id = #{flag_event_type_id} + SQL + end + + def down + raise ActiveRecord::IrreversibleMigration + end +end diff --git a/db/migrate/20200514075407_remove_queued_post_web_hooks.rb b/db/migrate/20200514075407_remove_queued_post_web_hooks.rb new file mode 100644 index 00000000000..632e5013dc9 --- /dev/null +++ b/db/migrate/20200514075407_remove_queued_post_web_hooks.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +class RemoveQueuedPostWebHooks < ActiveRecord::Migration[6.0] + def up + queued_post_event_type_id = 8 + + DB.exec <<~SQL + DELETE FROM web_hook_event_types_hooks + WHERE web_hook_event_type_id = #{queued_post_event_type_id} + SQL + + DB.exec <<~SQL + DELETE FROM web_hook_event_types + WHERE id = #{queued_post_event_type_id} + SQL + end + + def down + raise ActiveRecord::IrreversibleMigration + end +end diff --git a/db/post_migrate/20200520001619_remove_fks_from_bookmarks.rb b/db/post_migrate/20200520001619_remove_fks_from_bookmarks.rb new file mode 100644 index 00000000000..80fee6dab4f --- /dev/null +++ b/db/post_migrate/20200520001619_remove_fks_from_bookmarks.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +class RemoveFksFromBookmarks < ActiveRecord::Migration[6.0] + def change + remove_foreign_key :bookmarks, :topics + remove_foreign_key :bookmarks, :posts + remove_foreign_key :bookmarks, :users + end +end diff --git a/db/post_migrate/20200522004855_remove_access_control_post_fk.rb b/db/post_migrate/20200522004855_remove_access_control_post_fk.rb new file mode 100644 index 00000000000..36c8d0fbb72 --- /dev/null +++ b/db/post_migrate/20200522004855_remove_access_control_post_fk.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class RemoveAccessControlPostFk < ActiveRecord::Migration[6.0] + def change + remove_foreign_key :uploads, column: :access_control_post_id + end +end diff --git a/lib/backup_restore/backup_file_handler.rb b/lib/backup_restore/backup_file_handler.rb index c5e37024880..7b26356989e 100644 --- a/lib/backup_restore/backup_file_handler.rb +++ b/lib/backup_restore/backup_file_handler.rb @@ -65,9 +65,10 @@ module BackupRestore return if !@is_archive log "Unzipping archive, this may take a while..." - pipeline = Compression::Pipeline.new([Compression::Tar.new, Compression::Gzip.new]) - unzipped_path = pipeline.decompress(@tmp_directory, @archive_path, available_size) - pipeline.strip_directory(unzipped_path, @tmp_directory) + Discourse::Utils.execute_command( + 'tar', '--extract', '--gzip', '--file', @archive_path, '--directory', @tmp_directory, + failure_message: "Failed to decompress archive." + ) end def extract_db_dump diff --git a/lib/discourse_plugin_registry.rb b/lib/discourse_plugin_registry.rb index ae260e2ce1a..ab382a4d15c 100644 --- a/lib/discourse_plugin_registry.rb +++ b/lib/discourse_plugin_registry.rb @@ -75,6 +75,8 @@ class DiscoursePluginRegistry define_filtered_register :editable_group_custom_fields + define_filtered_register :topic_thumbnail_sizes + def self.register_auth_provider(auth_provider) self.auth_providers << auth_provider end diff --git a/lib/discourse_redis.rb b/lib/discourse_redis.rb index ff946cacfb5..6b630b7b34d 100644 --- a/lib/discourse_redis.rb +++ b/lib/discourse_redis.rb @@ -266,12 +266,6 @@ class DiscourseRedis end end - def flushdb - DiscourseRedis.ignore_readonly do - keys.each { |k| del(k) } - end - end - def reconnect @redis._client.reconnect end diff --git a/lib/file_store/s3_store.rb b/lib/file_store/s3_store.rb index 6a26ebd7ee6..f42fa73057b 100644 --- a/lib/file_store/s3_store.rb +++ b/lib/file_store/s3_store.rb @@ -79,12 +79,35 @@ module FileStore def has_been_uploaded?(url) return false if url.blank? + begin + parsed_url = URI.parse(URI.encode(url)) + rescue URI::InvalidURIError + return false + end + base_hostname = URI.parse(absolute_base_url).hostname - return true if url[base_hostname] + if url[base_hostname] + # if the hostnames match it means the upload is in the same + # bucket on s3. however, the bucket folder path may differ in + # some cases, and we do not want to assume the url is uploaded + # here. e.g. the path of the current site could be /prod and the + # other site could be /staging + if s3_bucket_folder_path.present? + return parsed_url.path.starts_with?("/#{s3_bucket_folder_path}") + else + return true + end + return false + end return false if SiteSetting.Upload.s3_cdn_url.blank? cdn_hostname = URI.parse(SiteSetting.Upload.s3_cdn_url || "").hostname - cdn_hostname.presence && url[cdn_hostname] + return true if cdn_hostname.presence && url[cdn_hostname] + false + end + + def s3_bucket_folder_path + @s3_helper.s3_bucket_folder_path end def s3_bucket_name diff --git a/lib/freedom_patches/translate_accelerator.rb b/lib/freedom_patches/translate_accelerator.rb index ee724d56d12..5a82623f8f7 100644 --- a/lib/freedom_patches/translate_accelerator.rb +++ b/lib/freedom_patches/translate_accelerator.rb @@ -164,7 +164,7 @@ module I18n by_site[locale].with_indifferent_access rescue ActiveRecord::StatementInvalid => e - if PG::UndefinedTable === e.cause + if PG::UndefinedTable === e.cause || PG::UndefinedColumn === e.cause {} else raise diff --git a/lib/guardian.rb b/lib/guardian.rb index 00d496e46eb..456461fead6 100644 --- a/lib/guardian.rb +++ b/lib/guardian.rb @@ -493,6 +493,10 @@ class Guardian is_staff? end + def can_see_about_stats? + true + end + def auth_token if cookie = request&.cookies[Auth::DefaultCurrentUserProvider::TOKEN_COOKIE] UserAuthToken.hash_token(cookie) diff --git a/lib/plugin/instance.rb b/lib/plugin/instance.rb index a993ce52f0b..ab48f7cbead 100644 --- a/lib/plugin/instance.rb +++ b/lib/plugin/instance.rb @@ -165,6 +165,16 @@ class Plugin::Instance DiscoursePluginRegistry.register_editable_group_custom_field(field, self) end + # Request a new size for topic thumbnails + # Will respect plugin enabled setting is enabled + # Size should be an array with two elements [max_width, max_height] + def register_topic_thumbnail_size(size) + if !(size.kind_of?(Array) && size.length == 2) + raise ArgumentError.new("Topic thumbnail dimension is not valid") + end + DiscoursePluginRegistry.register_topic_thumbnail_size(size, self) + end + def custom_avatar_column(column) reloadable_patch do |plugin| AvatarLookup.lookup_columns << column diff --git a/lib/plugin_initialization_guard.rb b/lib/plugin_initialization_guard.rb index a5a164e9e2e..42c1bda864f 100644 --- a/lib/plugin_initialization_guard.rb +++ b/lib/plugin_initialization_guard.rb @@ -6,33 +6,44 @@ def plugin_initialization_guard(&block) rescue => error plugins_directory = Rails.root + 'plugins' - plugin_path = error.backtrace_locations.lazy.map do |location| - Pathname.new(location.absolute_path) - .ascend - .lazy - .find { |path| path.parent == plugins_directory } - end.next + if error.backtrace && error.backtrace_locations + plugin_path = error.backtrace_locations.lazy.map do |location| + Pathname.new(location.absolute_path) + .ascend + .lazy + .find { |path| path.parent == plugins_directory } + end.next - raise unless plugin_path + raise unless plugin_path - stack_trace = error.backtrace.each_with_index.inject([]) do |messages, (line, index)| - if index == 0 - messages << "#{line}: #{error} (#{error.class})" - else - messages << "\t#{index}: from #{line}" - end - end.reverse.join("\n") + stack_trace = error.backtrace.each_with_index.inject([]) do |messages, (line, index)| + if index == 0 + messages << "#{line}: #{error} (#{error.class})" + else + messages << "\t#{index}: from #{line}" + end + end.reverse.join("\n") - STDERR.puts <<~MESSAGE - #{stack_trace} + STDERR.puts <<~MESSAGE + #{stack_trace} - ** INCOMPATIBLE PLUGIN ** + ** INCOMPATIBLE PLUGIN ** - You are unable to build Discourse due to errors in the plugin at - #{plugin_path} + You are unable to build Discourse due to errors in the plugin at + #{plugin_path} - Please try removing this plugin and rebuilding again! - MESSAGE + Please try removing this plugin and rebuilding again! + MESSAGE + else + STDERR.puts <<~MESSAGE + ** PLUGIN FAILURE ** + + You are unable to build Discourse due to this error during plugin + initialization: + + #{error} + MESSAGE + end exit 1 end end diff --git a/lib/post_creator.rb b/lib/post_creator.rb index 8a37e4c9c64..c3b9556df21 100644 --- a/lib/post_creator.rb +++ b/lib/post_creator.rb @@ -374,6 +374,10 @@ class PostCreator # discourse post. def create_embedded_topic return unless @opts[:embed_url].present? + + original_uri = URI.parse(@opts[:embed_url]) + raise Discourse::InvalidParameters.new(:embed_url) unless original_uri.is_a?(URI::HTTP) + embed = TopicEmbed.new(topic_id: @post.topic_id, post_id: @post.id, embed_url: @opts[:embed_url]) rollback_from_errors!(embed) unless embed.save end diff --git a/lib/post_revisor.rb b/lib/post_revisor.rb index 3c9ec277cdb..d610fccf375 100644 --- a/lib/post_revisor.rb +++ b/lib/post_revisor.rb @@ -567,11 +567,7 @@ class PostRevisor end def update_topic_excerpt - excerpt = @post.excerpt_for_topic - @topic.update_column(:excerpt, excerpt) - if @topic.archetype == "banner" - ApplicationController.banner_json_cache.clear - end + @topic.update_excerpt(@post.excerpt_for_topic) end def update_category_description diff --git a/lib/tasks/bookmarks.rake b/lib/tasks/bookmarks.rake index d3cf545d2d0..87fe005b075 100644 --- a/lib/tasks/bookmarks.rake +++ b/lib/tasks/bookmarks.rake @@ -22,6 +22,7 @@ task "bookmarks:sync_to_table" => :environment do |_t, args| INNER JOIN posts ON posts.id = post_actions.post_id LEFT JOIN bookmarks ON bookmarks.post_id = post_actions.post_id AND bookmarks.user_id = post_actions.user_id INNER JOIN topics ON topics.id = posts.topic_id + INNER JOIN users ON users.id = post_actions.user_id WHERE bookmarks.id IS NULL AND post_action_type_id = :type_id AND post_actions.deleted_at IS NULL AND posts.deleted_at IS NULL LIMIT 2000 SQL diff --git a/lib/topic_query_params.rb b/lib/topic_query_params.rb index e1bb23cb9f7..bab30135a17 100644 --- a/lib/topic_query_params.rb +++ b/lib/topic_query_params.rb @@ -3,7 +3,7 @@ module TopicQueryParams def build_topic_list_options options = {} - params[:tags] = [params[:tag_id].parameterize] if params[:tag_id].present? && guardian.can_tag_pms? + params[:tags] = [params[:tag_id]] if params[:tag_id].present? && guardian.can_tag_pms? TopicQuery.public_valid_options.each do |key| if params.key?(key) diff --git a/plugins/discourse-narrative-bot/db/fixtures/001_discobot.rb b/plugins/discourse-narrative-bot/db/fixtures/001_discobot.rb index 3972a350fbf..fda50b48299 100644 --- a/plugins/discourse-narrative-bot/db/fixtures/001_discobot.rb +++ b/plugins/discourse-narrative-bot/db/fixtures/001_discobot.rb @@ -4,20 +4,20 @@ discobot_username = 'discobot' def seed_primary_email UserEmail.seed do |ue| - ue.id = -2 + ue.id = DiscourseNarrativeBot::BOT_USER_ID ue.email = "discobot_email" ue.primary = true - ue.user_id = -2 + ue.user_id = DiscourseNarrativeBot::BOT_USER_ID end end -unless user = User.find_by(id: -2) +unless user = User.find_by(id: DiscourseNarrativeBot::BOT_USER_ID) suggested_username = UserNameSuggester.suggest(discobot_username) seed_primary_email User.seed do |u| - u.id = -2 + u.id = DiscourseNarrativeBot::BOT_USER_ID u.name = discobot_username u.username = suggested_username u.username_lower = suggested_username.downcase @@ -26,22 +26,9 @@ unless user = User.find_by(id: -2) u.approved = true u.trust_level = TrustLevel[4] end - - # TODO Pull the user avatar from that thread for now. In the future, pull it from a local file or from some central discobot repo. - if !Rails.env.test? - begin - UserAvatar.import_url_for_user( - "https://cdn.discourse.org/dev/uploads/default/original/2X/e/edb63d57a720838a7ce6a68f02ba4618787f2299.png", - User.find(-2), - override_gravatar: true - ) - rescue - # In case the avatar can't be downloaded, don't fail seed - end - end end -bot = User.find(-2) +bot = User.find(DiscourseNarrativeBot::BOT_USER_ID) # ensure discobot has a primary email unless bot.primary_email @@ -62,4 +49,4 @@ if !bot.user_profile.bio_raw ) end -Group.user_trust_level_change!(-2, TrustLevel[4]) +Group.user_trust_level_change!(DiscourseNarrativeBot::BOT_USER_ID, TrustLevel[4]) diff --git a/plugins/discourse-narrative-bot/db/post_migrate/20200520015508_clear_last_gravatar_download_attempt_on_user_avatars.rb b/plugins/discourse-narrative-bot/db/post_migrate/20200520015508_clear_last_gravatar_download_attempt_on_user_avatars.rb new file mode 100644 index 00000000000..c9029730814 --- /dev/null +++ b/plugins/discourse-narrative-bot/db/post_migrate/20200520015508_clear_last_gravatar_download_attempt_on_user_avatars.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +class ClearLastGravatarDownloadAttemptOnUserAvatars < ActiveRecord::Migration[6.0] + def up + execute <<~SQL + UPDATE user_avatars + SET last_gravatar_download_attempt = null + WHERE user_id = -2 AND custom_upload_id IS NULL AND gravatar_upload_id IS NULL + SQL + end + + def down + raise ActiveRecord::IrreversibleMigration + end +end diff --git a/plugins/discourse-narrative-bot/plugin.rb b/plugins/discourse-narrative-bot/plugin.rb index a6be5dd056f..61047c0e8b2 100644 --- a/plugins/discourse-narrative-bot/plugin.rb +++ b/plugins/discourse-narrative-bot/plugin.rb @@ -55,6 +55,7 @@ after_initialize do module ::DiscourseNarrativeBot PLUGIN_NAME = "discourse-narrative-bot".freeze + BOT_USER_ID = -2 class Engine < ::Rails::Engine engine_name PLUGIN_NAME @@ -271,4 +272,9 @@ after_initialize do end end end + + UserAvatar.register_custom_user_gravatar_email_hash( + DiscourseNarrativeBot::BOT_USER_ID, + "discobot@discourse.org" + ) end diff --git a/plugins/discourse-narrative-bot/spec/discourse_narrative_bot/track_selector_spec.rb b/plugins/discourse-narrative-bot/spec/discourse_narrative_bot/track_selector_spec.rb index 4e24b9feaef..6914dff1d2f 100644 --- a/plugins/discourse-narrative-bot/spec/discourse_narrative_bot/track_selector_spec.rb +++ b/plugins/discourse-narrative-bot/spec/discourse_narrative_bot/track_selector_spec.rb @@ -472,7 +472,7 @@ describe DiscourseNarrativeBot::TrackSelector do let(:post) { Fabricate(:post, topic: topic) } after do - Discourse.redis.flushall + Discourse.redis.flushdb end describe 'when random reply massage has been displayed in the last 6 hours' do diff --git a/plugins/discourse-narrative-bot/spec/jobs/send_advanced_tutorial_message_spec.rb b/plugins/discourse-narrative-bot/spec/jobs/send_advanced_tutorial_message_spec.rb index 5d3674cae08..874e40cfe81 100644 --- a/plugins/discourse-narrative-bot/spec/jobs/send_advanced_tutorial_message_spec.rb +++ b/plugins/discourse-narrative-bot/spec/jobs/send_advanced_tutorial_message_spec.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +require 'rails_helper' + RSpec.describe Jobs::SendAdvancedTutorialMessage do before do Jobs.run_immediately! diff --git a/spec/components/auth/default_current_user_provider_spec.rb b/spec/components/auth/default_current_user_provider_spec.rb index 1b698cbf368..8c14233a5ba 100644 --- a/spec/components/auth/default_current_user_provider_spec.rb +++ b/spec/components/auth/default_current_user_provider_spec.rb @@ -237,7 +237,7 @@ describe Auth::DefaultCurrentUserProvider do end after do - Discourse.redis.flushall + Discourse.redis.flushdb end it "should not update last seen for suspended users" do diff --git a/spec/components/discourse_redis_spec.rb b/spec/components/discourse_redis_spec.rb index 6d4af1d8174..abc9a5917a8 100644 --- a/spec/components/discourse_redis_spec.rb +++ b/spec/components/discourse_redis_spec.rb @@ -21,11 +21,11 @@ describe DiscourseRedis do let(:raw_redis) { Redis.new(DiscourseRedis.config) } before do - raw_redis.flushall + raw_redis.flushdb end after do - raw_redis.flushall + raw_redis.flushdb end describe 'when namespace is enabled' do diff --git a/spec/components/email/processor_spec.rb b/spec/components/email/processor_spec.rb index 3fe4ab41b7a..606955551a1 100644 --- a/spec/components/email/processor_spec.rb +++ b/spec/components/email/processor_spec.rb @@ -5,7 +5,7 @@ require "email/processor" describe Email::Processor do after do - Discourse.redis.flushall + Discourse.redis.flushdb end let(:from) { "foo@bar.com" } diff --git a/spec/components/file_store/s3_store_spec.rb b/spec/components/file_store/s3_store_spec.rb index 99f57c71047..15c36b05be6 100644 --- a/spec/components/file_store/s3_store_spec.rb +++ b/spec/components/file_store/s3_store_spec.rb @@ -251,7 +251,7 @@ describe FileStore::S3Store do before do optimized_image.update!( - url: "//s3-upload-bucket.s3.dualstack.us-west-1.amazonaws.com#{image_path}" + url: "//s3-upload-bucket.s3.dualstack.us-west-1.amazonaws.com/#{image_path}" ) end @@ -272,6 +272,12 @@ describe FileStore::S3Store do SiteSetting.s3_upload_bucket = "s3-upload-bucket/discourse-uploads" end + before do + optimized_image.update!( + url: "//s3-upload-bucket.s3.dualstack.us-west-1.amazonaws.com/discourse-uploads/#{image_path}" + ) + end + it "removes the file from s3 with the right paths" do s3_helper.expects(:s3_bucket).returns(s3_bucket).at_least_once s3_object = stub @@ -298,6 +304,15 @@ describe FileStore::S3Store do describe ".has_been_uploaded?" do + it "doesn't crash for invalid URLs" do + expect(store.has_been_uploaded?("https://site.discourse.com/#bad#6")).to eq(false) + end + + it "doesn't crash if URL contains non-ascii characters" do + expect(store.has_been_uploaded?("//s3-upload-bucket.s3.dualstack.us-east-1.amazonaws.com/漢1337.png")).to eq(true) + expect(store.has_been_uploaded?("//s3-upload-bucket.s3.amazonaws.com/漢1337.png")).to eq(false) + end + it "identifies S3 uploads" do expect(store.has_been_uploaded?("//s3-upload-bucket.s3.dualstack.us-east-1.amazonaws.com/1337.png")).to eq(true) end diff --git a/spec/components/guardian/user_guardian_spec.rb b/spec/components/guardian/user_guardian_spec.rb index b812fa9526d..10b6afc9413 100644 --- a/spec/components/guardian/user_guardian_spec.rb +++ b/spec/components/guardian/user_guardian_spec.rb @@ -25,13 +25,13 @@ describe UserGuardian do end let :already_uploaded do - u = Upload.new(user_id: 999, id: 2) + u = Upload.new(user_id: 9999, id: 2) user_avatar.custom_upload_id = u.id u end let :not_my_upload do - Upload.new(user_id: 999, id: 3) + Upload.new(user_id: 9999, id: 3) end let(:moderator_upload) do diff --git a/spec/components/pretty_text_spec.rb b/spec/components/pretty_text_spec.rb index 8969a526bd5..c5ef73c4d23 100644 --- a/spec/components/pretty_text_spec.rb +++ b/spec/components/pretty_text_spec.rb @@ -508,7 +508,7 @@ describe PrettyText do ['apple', 'banana'].each { |w| Fabricate(:watched_word, word: w, action: WatchedWord.actions[:censor]) } expect(PrettyText.cook("# banana")).not_to include('banana') ensure - Discourse.redis.flushall + Discourse.redis.flushdb end end end @@ -1166,7 +1166,7 @@ HTML end describe "censoring" do - after(:all) { Discourse.redis.flushall } + after(:all) { Discourse.redis.flushdb } def expect_cooked_match(raw, expected_cooked) expect(PrettyText.cook(raw)).to eq(expected_cooked) diff --git a/spec/components/search_spec.rb b/spec/components/search_spec.rb index a455d81ec13..599ea3c5aa5 100644 --- a/spec/components/search_spec.rb +++ b/spec/components/search_spec.rb @@ -444,7 +444,7 @@ describe Search do end let(:expected_blurb) do - "...to satisfy any test conditions that require content longer than the typical test post raw content. elephant" + "...quire content longer than the typical test post raw content. It really is some long content, folks. elephant" end it 'returns the post' do diff --git a/spec/fabricators/post_fabricator.rb b/spec/fabricators/post_fabricator.rb index 42235cb94fc..77a234d9814 100644 --- a/spec/fabricators/post_fabricator.rb +++ b/spec/fabricators/post_fabricator.rb @@ -10,7 +10,7 @@ end Fabricator(:post_with_long_raw_content, from: :post) do raw 'This is a sample post with semi-long raw content. The raw content is also more than two hundred characters to satisfy any test conditions that require content longer - than the typical test post raw content.' + than the typical test post raw content. It really is some long content, folks.' end Fabricator(:post_with_youtube, from: :post) do diff --git a/spec/fabricators/web_hook_fabricator.rb b/spec/fabricators/web_hook_fabricator.rb index 46472510808..9edc591791f 100644 --- a/spec/fabricators/web_hook_fabricator.rb +++ b/spec/fabricators/web_hook_fabricator.rb @@ -71,22 +71,6 @@ Fabricator(:tag_web_hook, from: :web_hook) do end end -Fabricator(:flag_web_hook, from: :web_hook) do - transient flag_hook: WebHookEventType.find_by(name: 'flag') - - after_build do |web_hook, transients| - web_hook.web_hook_event_types = [transients[:flag_hook]] - end -end - -Fabricator(:queued_post_web_hook, from: :web_hook) do - transient queued_post_hook: WebHookEventType.find_by(name: 'queued_post') - - after_build do |web_hook, transients| - web_hook.web_hook_event_types = [transients[:queued_post_hook]] - end -end - Fabricator(:reviewable_web_hook, from: :web_hook) do transient reviewable_hook: WebHookEventType.find_by(name: 'reviewable') diff --git a/spec/integration/email_style_spec.rb b/spec/integration/email_style_spec.rb index ef69850891d..395dd19df15 100644 --- a/spec/integration/email_style_spec.rb +++ b/spec/integration/email_style_spec.rb @@ -3,128 +3,139 @@ require "rails_helper" describe EmailStyle do - before do - SiteSetting.email_custom_template = "

FOR YOU

%{email_content}
" - SiteSetting.email_custom_css = 'h1 { color: red; } div.body { color: #FAB; }' - SiteSetting.email_custom_css_compiled = SiteSetting.email_custom_css - end - after do - SiteSetting.remove_override!(:email_custom_template) - SiteSetting.remove_override!(:email_custom_css) - end - - context 'invite' do - fab!(:invite) { Fabricate(:invite) } - let(:invite_mail) { InviteMailer.send_invite(invite) } - - subject(:mail_html) { Email::Renderer.new(invite_mail).html } - - it 'applies customizations' do - expect(mail_html.scan('

FOR YOU

').count).to eq(1) - expect(mail_html).to match("#{Discourse.base_url}/invites/#{invite.invite_key}") - end - - it 'applies customizations if compiled is missing' do - SiteSetting.remove_override!(:email_custom_css_compiled) - expect(mail_html.scan('

FOR YOU

').count).to eq(1) - expect(mail_html).to match("#{Discourse.base_url}/invites/#{invite.invite_key}") - end - - it 'can apply RTL attrs' do - SiteSetting.default_locale = 'he' - body_attrs = mail_html.match(/])+/) - expect(body_attrs[0]&.downcase).to match(/text-align:\s*right/) - expect(body_attrs[0]&.downcase).to include('dir="rtl"') + context "ERB evaluation" do + it "does not evaluate ERB outside of the email itself" do + SiteSetting.email_custom_template = "
%{email_content}
<%= (111 * 333) %>" + html = Email::Renderer.new(UserNotifications.signup(Fabricate(:user))).html + expect(html).not_to match("36963") end end - context 'user_replied' do - let(:response_by_user) { Fabricate(:user, name: "John Doe") } - let(:category) { Fabricate(:category, name: 'India') } - let(:topic) { Fabricate(:topic, category: category, title: "Super cool topic") } - let(:post) { Fabricate(:post, topic: topic, raw: 'This is My super duper cool topic') } - let(:response) { Fabricate(:basic_reply, topic: post.topic, user: response_by_user) } - let(:user) { Fabricate(:user) } - let(:notification) { Fabricate(:replied_notification, user: user, post: response) } - - let(:mail) do - UserNotifications.user_replied( - user, - post: response, - notification_type: notification.notification_type, - notification_data_hash: notification.data_hash - ) + context "with a custom template" do + before do + SiteSetting.email_custom_template = "

FOR YOU

%{email_content}
" + SiteSetting.email_custom_css = 'h1 { color: red; } div.body { color: #FAB; }' + SiteSetting.email_custom_css_compiled = SiteSetting.email_custom_css end - subject(:mail_html) { Email::Renderer.new(mail).html } - - it "customizations are applied to html part of emails" do - expect(mail_html.scan('

FOR YOU

').count).to eq(1) - matches = mail_html.match(/
#{post.raw}/) - expect(matches[1]).to include('color: #FAB;') # custom - expect(matches[1]).to include('padding-top:5px;') # div.body + after do + SiteSetting.remove_override!(:email_custom_template) + SiteSetting.remove_override!(:email_custom_css) end - # TODO: translation override - end + context 'invite' do + fab!(:invite) { Fabricate(:invite) } + let(:invite_mail) { InviteMailer.send_invite(invite) } - context 'signup' do - let(:signup_mail) { UserNotifications.signup(Fabricate(:user)) } - subject(:mail_html) { Email::Renderer.new(signup_mail).html } + subject(:mail_html) { Email::Renderer.new(invite_mail).html } - it "customizations are applied to html part of emails" do - expect(mail_html.scan('

FOR YOU

').count).to eq(1) - expect(mail_html).to include('activate-account') + it 'applies customizations' do + expect(mail_html.scan('

FOR YOU

').count).to eq(1) + expect(mail_html).to match("#{Discourse.base_url}/invites/#{invite.invite_key}") + end + + it 'applies customizations if compiled is missing' do + SiteSetting.remove_override!(:email_custom_css_compiled) + expect(mail_html.scan('

FOR YOU

').count).to eq(1) + expect(mail_html).to match("#{Discourse.base_url}/invites/#{invite.invite_key}") + end + + it 'can apply RTL attrs' do + SiteSetting.default_locale = 'he' + body_attrs = mail_html.match(/])+/) + expect(body_attrs[0]&.downcase).to match(/text-align:\s*right/) + expect(body_attrs[0]&.downcase).to include('dir="rtl"') + end end - context 'translation override' do - before do - TranslationOverride.upsert!( - 'en', - 'user_notifications.signup.text_body_template', - "CLICK THAT LINK: %{base_url}/u/activate-account/%{email_token}" + context 'user_replied' do + let(:response_by_user) { Fabricate(:user, name: "John Doe") } + let(:category) { Fabricate(:category, name: 'India') } + let(:topic) { Fabricate(:topic, category: category, title: "Super cool topic") } + let(:post) { Fabricate(:post, topic: topic, raw: 'This is My super duper cool topic') } + let(:response) { Fabricate(:basic_reply, topic: post.topic, user: response_by_user) } + let(:user) { Fabricate(:user) } + let(:notification) { Fabricate(:replied_notification, user: user, post: response) } + + let(:mail) do + UserNotifications.user_replied( + user, + post: response, + notification_type: notification.notification_type, + notification_data_hash: notification.data_hash ) end - after do - TranslationOverride.revert!('en', ['user_notifications.signup.text_body_template']) + subject(:mail_html) { Email::Renderer.new(mail).html } + + it "customizations are applied to html part of emails" do + expect(mail_html.scan('

FOR YOU

').count).to eq(1) + matches = mail_html.match(/
#{post.raw}/) + expect(matches[1]).to include('color: #FAB;') # custom + expect(matches[1]).to include('padding-top:5px;') # div.body end - it "applies customizations when translation override exists" do - expect(mail_html.scan('

FOR YOU

').count).to eq(1) - expect(mail_html.scan('CLICK THAT LINK').count).to eq(1) - end + # TODO: translation override end - context 'with some bad css' do - before do - SiteSetting.email_custom_css = '@import "nope.css"; h1 {{{ size: really big; ' - SiteSetting.email_custom_css_compiled = SiteSetting.email_custom_css - end + context 'signup' do + let(:signup_mail) { UserNotifications.signup(Fabricate(:user)) } + subject(:mail_html) { Email::Renderer.new(signup_mail).html } - it "can render the html" do - expect(mail_html.scan(/FOR YOU<\/h1>/).count).to eq(1) + it "customizations are applied to html part of emails" do + expect(mail_html.scan('

FOR YOU

').count).to eq(1) expect(mail_html).to include('activate-account') end - end - end - context 'digest' do - fab!(:popular_topic) { Fabricate(:topic, user: Fabricate(:coding_horror), created_at: 1.hour.ago) } - let(:summary_email) { UserNotifications.digest(Fabricate(:user)) } - subject(:mail_html) { Email::Renderer.new(summary_email).html } + context 'translation override' do + before do + TranslationOverride.upsert!( + 'en', + 'user_notifications.signup.text_body_template', + "CLICK THAT LINK: %{base_url}/u/activate-account/%{email_token}" + ) + end - it "customizations are applied to html part of emails" do - expect(mail_html.scan('

FOR YOU

').count).to eq(1) - expect(mail_html).to include(popular_topic.title) + after do + TranslationOverride.revert!('en', ['user_notifications.signup.text_body_template']) + end + + it "applies customizations when translation override exists" do + expect(mail_html.scan('

FOR YOU

').count).to eq(1) + expect(mail_html.scan('CLICK THAT LINK').count).to eq(1) + end + end + + context 'with some bad css' do + before do + SiteSetting.email_custom_css = '@import "nope.css"; h1 {{{ size: really big; ' + SiteSetting.email_custom_css_compiled = SiteSetting.email_custom_css + end + + it "can render the html" do + expect(mail_html.scan(/FOR YOU<\/h1>/).count).to eq(1) + expect(mail_html).to include('activate-account') + end + end end - it "doesn't apply customizations if apply_custom_styles_to_digest is disabled" do - SiteSetting.apply_custom_styles_to_digest = false - expect(mail_html).to_not include('

FOR YOU

') - expect(mail_html).to_not include('FOR YOU') - expect(mail_html).to include(popular_topic.title) + context 'digest' do + fab!(:popular_topic) { Fabricate(:topic, user: Fabricate(:coding_horror), created_at: 1.hour.ago) } + let(:summary_email) { UserNotifications.digest(Fabricate(:user)) } + subject(:mail_html) { Email::Renderer.new(summary_email).html } + + it "customizations are applied to html part of emails" do + expect(mail_html.scan('

FOR YOU

').count).to eq(1) + expect(mail_html).to include(popular_topic.title) + end + + it "doesn't apply customizations if apply_custom_styles_to_digest is disabled" do + SiteSetting.apply_custom_styles_to_digest = false + expect(mail_html).to_not include('

FOR YOU

') + expect(mail_html).to_not include('FOR YOU') + expect(mail_html).to include(popular_topic.title) + end end end end diff --git a/spec/integration/topic_thumbnail_spec.rb b/spec/integration/topic_thumbnail_spec.rb index 9ba35cbe970..dcdbb084d5a 100644 --- a/spec/integration/topic_thumbnail_spec.rb +++ b/spec/integration/topic_thumbnail_spec.rb @@ -10,7 +10,7 @@ describe "Topic Thumbnails" do context 'latest' do def get_topic - Discourse.redis.del(topic.thumbnail_job_redis_key([])) + Discourse.redis.del(topic.thumbnail_job_redis_key(Topic.thumbnail_sizes)) get '/latest.json' response.parsed_body["topic_list"]["topics"][0] end @@ -84,5 +84,38 @@ describe "Topic Thumbnails" do expect(thumbnails.length).to eq(5) end end + + context "with a plugin" do + before do + plugin = Plugin::Instance.new + plugin.register_topic_thumbnail_size [512, 512] + end + + after do + DiscoursePluginRegistry.reset! + end + + it "includes the theme specified resolutions" do + topic_json = nil + + expect do + topic_json = get_topic + end.to change { Jobs::GenerateTopicThumbnails.jobs.size }.by(1) + + # Run the job + args = Jobs::GenerateTopicThumbnails.jobs.last["args"].first + Jobs::GenerateTopicThumbnails.new.execute(args.with_indifferent_access) + + # Request again + expect do + topic_json = get_topic + end.to change { Jobs::GenerateTopicThumbnails.jobs.size }.by(0) + + thumbnails = topic_json["thumbnails"] + + # Original + Optimized + 1 plugin request + expect(thumbnails.length).to eq(3) + end + end end end diff --git a/spec/jobs/bookmark_reminder_notifications_spec.rb b/spec/jobs/bookmark_reminder_notifications_spec.rb index 0bcfa0b3d21..25379d0ba39 100644 --- a/spec/jobs/bookmark_reminder_notifications_spec.rb +++ b/spec/jobs/bookmark_reminder_notifications_spec.rb @@ -22,7 +22,7 @@ RSpec.describe Jobs::BookmarkReminderNotifications do bookmark1.update_column(:reminder_at, five_minutes_ago - 10.minutes) bookmark2.update_column(:reminder_at, five_minutes_ago - 5.minutes) bookmark3.update_column(:reminder_at, five_minutes_ago) - Discourse.redis.flushall + Discourse.redis.flushdb end it "sends every reminder and marks the reminder_at to nil for all bookmarks, as well as last sent date" do diff --git a/spec/lib/bookmark_reminder_notification_handler_spec.rb b/spec/lib/bookmark_reminder_notification_handler_spec.rb index 6af860f2f04..3d9a6d77f64 100644 --- a/spec/lib/bookmark_reminder_notification_handler_spec.rb +++ b/spec/lib/bookmark_reminder_notification_handler_spec.rb @@ -8,7 +8,7 @@ RSpec.describe BookmarkReminderNotificationHandler do fab!(:user) { Fabricate(:user) } before do - Discourse.redis.flushall + Discourse.redis.flushdb end describe "#send_notification" do diff --git a/spec/models/post_spec.rb b/spec/models/post_spec.rb index 4cc716d6916..5bc5206bced 100644 --- a/spec/models/post_spec.rb +++ b/spec/models/post_spec.rb @@ -1158,6 +1158,25 @@ describe Post do expect(post.custom_fields).to eq("Tommy" => "Hanks", "Vincent" => "Vega") end + describe "#excerpt_for_topic" do + it "returns a topic excerpt, defaulting to 220 chars" do + expected_excerpt = "This is a sample post with semi-long raw content. The raw content is also more than \ntwo hundred characters to satisfy any test conditions that require content longer \nthan the typical test post raw content. It really is…" + post = Fabricate(:post_with_long_raw_content) + post.rebake! + excerpt = post.excerpt_for_topic + expect(excerpt).to eq(expected_excerpt) + end + + it "respects the site setting for topic excerpt" do + SiteSetting.topic_excerpt_maxlength = 10 + expected_excerpt = "This is a …" + post = Fabricate(:post_with_long_raw_content) + post.rebake! + excerpt = post.excerpt_for_topic + expect(excerpt).to eq(expected_excerpt) + end + end + describe "#rebake!" do it "will rebake a post correctly" do post = create_post @@ -1176,6 +1195,25 @@ describe Post do expect(post.cooked).to eq(first_cooked) expect(result).to eq(true) end + + it "updates the topic excerpt at the same time if it is the OP" do + post = create_post + post.topic.update(excerpt: "test") + DB.exec("UPDATE posts SET cooked = 'frogs' WHERE id = ?", [ post.id ]) + post.reload + result = post.rebake! + post.topic.reload + expect(post.topic.excerpt).not_to eq("test") + end + + it "does not update the topic excerpt if the post is not the OP" do + post = create_post + post2 = create_post + post.topic.update(excerpt: "test") + result = post2.rebake! + post.topic.reload + expect(post.topic.excerpt).to eq("test") + end end describe "#set_owner" do diff --git a/spec/models/topic_embed_spec.rb b/spec/models/topic_embed_spec.rb index ddfbe115ecf..c91781e6dd7 100644 --- a/spec/models/topic_embed_spec.rb +++ b/spec/models/topic_embed_spec.rb @@ -308,6 +308,14 @@ describe TopicEmbed do end end + context "non-http URL" do + let(:url) { '/test.txt' } + + it "throws an error" do + expect { TopicEmbed.find_remote(url) }.to raise_error(URI::InvalidURIError) + end + end + context "emails" do let(:url) { 'http://example.com/foo' } let(:contents) { '

URL encoded @ symbol

normal mailto link

' } diff --git a/spec/models/topic_spec.rb b/spec/models/topic_spec.rb index d3815793009..d1d5d1cc911 100644 --- a/spec/models/topic_spec.rb +++ b/spec/models/topic_spec.rb @@ -33,7 +33,7 @@ describe Topic do describe 'censored words' do after do - Discourse.redis.flushall + Discourse.redis.flushdb end describe 'when title contains censored words' do diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 89d5f627041..4d2592c788f 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -1578,20 +1578,15 @@ describe User do describe '#number_of_rejected_posts' do it 'counts rejected posts' do - post = Fabricate(:post, user: user) - - Fabricate(:reviewable_queued_post, target: post, status: Reviewable.statuses[:rejected]) + Fabricate(:reviewable_queued_post, created_by: user, status: Reviewable.statuses[:rejected]) expect(user.number_of_rejected_posts).to eq(1) end it 'ignore non-rejected posts' do - post = Fabricate(:post, user: user) - - Fabricate(:reviewable_queued_post, target: post, status: Reviewable.statuses[:approved]) + Fabricate(:reviewable_queued_post, created_by: user, status: Reviewable.statuses[:approved]) expect(user.number_of_rejected_posts).to eq(0) - end end end diff --git a/spec/multisite/s3_store_spec.rb b/spec/multisite/s3_store_spec.rb index 922c5f433ba..064c699a12b 100644 --- a/spec/multisite/s3_store_spec.rb +++ b/spec/multisite/s3_store_spec.rb @@ -217,4 +217,47 @@ RSpec.describe 'Multisite s3 uploads', type: :multisite do end end end + + describe "#has_been_uploaded?" do + before do + SiteSetting.s3_region = 'us-west-1' + SiteSetting.s3_upload_bucket = "s3-upload-bucket/test" + SiteSetting.s3_access_key_id = "s3-access-key-id" + SiteSetting.s3_secret_access_key = "s3-secret-access-key" + SiteSetting.enable_s3_uploads = true + end + + let(:store) { FileStore::S3Store.new } + let(:client) { Aws::S3::Client.new(stub_responses: true) } + let(:resource) { Aws::S3::Resource.new(client: client) } + let(:s3_bucket) { resource.bucket(SiteSetting.s3_upload_bucket) } + let(:s3_helper) { store.s3_helper } + + it "returns false for blank urls" do + url = "" + expect(store.has_been_uploaded?(url)).to eq(false) + end + + it "returns true if the base hostname is the same for both urls" do + url = "https://s3-upload-bucket.s3.dualstack.us-west-1.amazonaws.com/test/original/2X/d/dd7964f5fd13e1103c5244ca30abe1936c0a4b88.png" + expect(store.has_been_uploaded?(url)).to eq(true) + end + + it "returns false if the base hostname is the same for both urls BUT the bucket name is different in the path" do + bucket = "someotherbucket" + url = "https://s3-upload-bucket.s3.dualstack.us-west-1.amazonaws.com/#{bucket}/original/2X/d/dd7964f5fd13e1103c5244ca30abe1936c0a4b88.png" + expect(store.has_been_uploaded?(url)).to eq(false) + end + + it "returns false if the hostnames do not match and the s3_cdn_url is blank" do + url = "https://www.someotherhostname.com/test/original/2X/d/dd7964f5fd13e1103c5244ca30abe1936c0a4b88.png" + expect(store.has_been_uploaded?(url)).to eq(false) + end + + it "returns true if the s3_cdn_url is present and matches the url hostname" do + SiteSetting.s3_cdn_url = "https://www.someotherhostname.com" + url = "https://www.someotherhostname.com/test/original/2X/d/dd7964f5fd13e1103c5244ca30abe1936c0a4b88.png" + expect(store.has_been_uploaded?(url)).to eq(true) + end + end end diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index a0ce4b71b7d..256de296583 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -128,6 +128,12 @@ module TestSetup # code that runs inside jobs. run_later! means they are put on the redis # queue and never processed. Jobs.run_later! + + # Don't track ApplicationRequests in test mode unless opted in + ApplicationRequest.disable + + # Don't queue badge grant in test mode + BadgeGranter.disable_queue end end diff --git a/spec/requests/about_controller_spec.rb b/spec/requests/about_controller_spec.rb index 6c58284979f..a10b7663043 100644 --- a/spec/requests/about_controller_spec.rb +++ b/spec/requests/about_controller_spec.rb @@ -36,5 +36,21 @@ describe AboutController do expect(response.body).to include("About - Discourse") end end + + it "serializes stats when 'Guardian#can_see_about_stats?' is true" do + Guardian.any_instance.stubs(:can_see_about_stats?).returns(true) + get "/about.json" + + expect(response.status).to eq(200) + expect(response.parsed_body["about"].keys).to include("stats") + end + + it "does not serialize stats when 'Guardian#can_see_about_stats?' is false" do + Guardian.any_instance.stubs(:can_see_about_stats?).returns(false) + get "/about.json" + + expect(response.status).to eq(200) + expect(response.parsed_body["about"].keys).not_to include("stats") + end end end diff --git a/spec/requests/admin/backups_controller_spec.rb b/spec/requests/admin/backups_controller_spec.rb index 466d4a938be..03b9245744d 100644 --- a/spec/requests/admin/backups_controller_spec.rb +++ b/spec/requests/admin/backups_controller_spec.rb @@ -35,7 +35,7 @@ RSpec.describe Admin::BackupsController do end after do - Discourse.redis.flushall + Discourse.redis.flushdb @paths&.each { |path| File.delete(path) if File.exists?(path) } @paths = nil diff --git a/spec/requests/admin/users_controller_spec.rb b/spec/requests/admin/users_controller_spec.rb index f62407d9569..099372cad00 100644 --- a/spec/requests/admin/users_controller_spec.rb +++ b/spec/requests/admin/users_controller_spec.rb @@ -293,7 +293,7 @@ RSpec.describe Admin::UsersController do fab!(:another_user) { Fabricate(:coding_horror) } after do - Discourse.redis.flushall + Discourse.redis.flushdb end it "raises an error when the user doesn't have permission" do diff --git a/spec/requests/list_controller_spec.rb b/spec/requests/list_controller_spec.rb index d1238c1f00f..bc913c58a04 100644 --- a/spec/requests/list_controller_spec.rb +++ b/spec/requests/list_controller_spec.rb @@ -128,7 +128,7 @@ RSpec.describe ListController do let(:moderator) { Fabricate(:moderator) } let(:admin) { Fabricate(:admin) } let(:tag) { Fabricate(:tag) } - let(:private_message) { Fabricate(:private_message_topic) } + let(:private_message) { Fabricate(:private_message_topic, user: admin) } before do SiteSetting.tagging_enabled = true @@ -149,6 +149,17 @@ RSpec.describe ListController do expect(response.status).to eq(200) end end + + it 'should work for tag with unicode name' do + unicode_tag = Fabricate(:tag, name: 'hello-🇺🇸') + Fabricate(:topic_tag, tag: unicode_tag, topic: private_message) + + sign_in(admin) + get "/topics/private-messages-tags/#{admin.username}/#{UrlHelper.encode_component(unicode_tag.name)}.json" + expect(response.status).to eq(200) + expect(response.parsed_body["topic_list"]["topics"].first["id"]) + .to eq(private_message.id) + end end describe '#private_messages_group' do diff --git a/spec/requests/posts_controller_spec.rb b/spec/requests/posts_controller_spec.rb index 909db8c509b..ff355ada967 100644 --- a/spec/requests/posts_controller_spec.rb +++ b/spec/requests/posts_controller_spec.rb @@ -675,6 +675,17 @@ describe PostsController do I18n.t("invalid_params", message: "category") ) end + + it 'will raise an error if specified embed_url is invalid' do + user = Fabricate(:admin) + master_key = Fabricate(:api_key).key + + post "/posts.json", + params: { title: 'this is a test title', raw: 'this is test body', embed_url: '/test.txt' }, + headers: { HTTP_API_USERNAME: user.username, HTTP_API_KEY: master_key } + + expect(response.status).to eq(422) + end end describe "when logged in" do diff --git a/spec/requests/search_controller_spec.rb b/spec/requests/search_controller_spec.rb index 947bd8754a9..dac11690d3e 100644 --- a/spec/requests/search_controller_spec.rb +++ b/spec/requests/search_controller_spec.rb @@ -26,11 +26,11 @@ describe SearchController do before do # TODO be a bit more strategic here instead of junking # all of redis - Discourse.redis.flushall + Discourse.redis.flushdb end after do - Discourse.redis.flushall + Discourse.redis.flushdb end context "when overloaded" do diff --git a/spec/requests/topics_controller_spec.rb b/spec/requests/topics_controller_spec.rb index 726b92228f2..9649f57f5ba 100644 --- a/spec/requests/topics_controller_spec.rb +++ b/spec/requests/topics_controller_spec.rb @@ -2127,7 +2127,7 @@ RSpec.describe TopicsController do let(:topic) { post.topic } after do - Discourse.redis.flushall + Discourse.redis.flushdb end it 'returns first post of the topic' do diff --git a/spec/requests/webhooks_controller_spec.rb b/spec/requests/webhooks_controller_spec.rb index 7772e0eb186..580c0c72f05 100644 --- a/spec/requests/webhooks_controller_spec.rb +++ b/spec/requests/webhooks_controller_spec.rb @@ -3,7 +3,7 @@ require "rails_helper" describe WebhooksController do - before { Discourse.redis.flushall } + before { Discourse.redis.flushdb } let(:email) { "em@il.com" } let(:message_id) { "12345@il.com" } diff --git a/spec/serializers/topic_view_serializer_spec.rb b/spec/serializers/topic_view_serializer_spec.rb index 5da26a674ec..f6249b275bb 100644 --- a/spec/serializers/topic_view_serializer_spec.rb +++ b/spec/serializers/topic_view_serializer_spec.rb @@ -60,7 +60,7 @@ describe TopicViewSerializer do it 'should have thumbnails' do SiteSetting.create_thumbnails = true - Discourse.redis.del(topic.thumbnail_job_redis_key([])) + Discourse.redis.del(topic.thumbnail_job_redis_key(Topic.thumbnail_sizes)) json = nil expect do diff --git a/spec/services/word_watcher_spec.rb b/spec/services/word_watcher_spec.rb index 0f6a2b7bb39..5cf70b65839 100644 --- a/spec/services/word_watcher_spec.rb +++ b/spec/services/word_watcher_spec.rb @@ -7,7 +7,7 @@ describe WordWatcher do let(:raw) { "Do you like liquorice?\n\nI really like them. One could even say that I am *addicted* to liquorice. Anf if\nyou can mix it up with some anise, then I'm in heaven ;)" } after do - Discourse.redis.flushall + Discourse.redis.flushdb end describe '.word_matcher_regexp' do diff --git a/test/javascripts/acceptance/category-chooser-test.js b/test/javascripts/acceptance/category-chooser-test.js index b6772ab7710..2247c8cbee7 100644 --- a/test/javascripts/acceptance/category-chooser-test.js +++ b/test/javascripts/acceptance/category-chooser-test.js @@ -29,3 +29,19 @@ QUnit.test("prefill category when category_id is set", async assert => { 1 ); }); + +QUnit.test("filter is case insensitive", async assert => { + const categoryChooser = selectKit(".category-chooser"); + + await visit("/"); + await click("#create-topic"); + await categoryChooser.expand(); + await categoryChooser.fillInFilter("bug"); + + assert.ok(categoryChooser.rows().length, 1); + + await categoryChooser.emptyFilter(); + await categoryChooser.fillInFilter("Bug"); + + assert.ok(categoryChooser.rows().length, 1); +}); diff --git a/test/javascripts/acceptance/emoji-picker-test.js b/test/javascripts/acceptance/emoji-picker-test.js index 6a4922ed5c0..a3d8472b535 100644 --- a/test/javascripts/acceptance/emoji-picker-test.js +++ b/test/javascripts/acceptance/emoji-picker-test.js @@ -168,19 +168,6 @@ QUnit.test( } ); -QUnit.test("emoji picker lazy loads emojis", async assert => { - await visit("/t/internationalization-localization/280"); - await click("#topic-footer-buttons .btn.create"); - - await click("button.emoji.btn"); - - assert.equal( - find('.emoji-picker button[title="massage_woman"]').css("background-image"), - "none", - "it doesn't load invisible emojis" - ); -}); - QUnit.test("emoji picker persists state", async assert => { await visit("/t/internationalization-localization/280"); await click("#topic-footer-buttons .btn.create"); diff --git a/test/javascripts/acceptance/preferences-test.js b/test/javascripts/acceptance/preferences-test.js index a9db1f08e50..20afdcd1677 100644 --- a/test/javascripts/acceptance/preferences-test.js +++ b/test/javascripts/acceptance/preferences-test.js @@ -1,7 +1,6 @@ import I18n from "I18n"; import { acceptance, updateCurrentUser } from "helpers/qunit-helpers"; import selectKit from "helpers/select-kit-helper"; - import User from "discourse/models/user"; acceptance("User Preferences", { diff --git a/test/javascripts/fixtures/about.js b/test/javascripts/fixtures/about.js index 7f9f17cbba5..4c7de6a87a3 100644 --- a/test/javascripts/fixtures/about.js +++ b/test/javascripts/fixtures/about.js @@ -1,6 +1,7 @@ export default { "about.json": { about: { + can_see_about_stats: true, stats: { topic_count: 27480, post_count: 490358, diff --git a/test/javascripts/helpers/select-kit-helper.js b/test/javascripts/helpers/select-kit-helper.js index 5d80b8f0b37..f7959913263 100644 --- a/test/javascripts/helpers/select-kit-helper.js +++ b/test/javascripts/helpers/select-kit-helper.js @@ -32,6 +32,11 @@ async function selectKitFillInFilter(filter, selector) { ); } +async function selectKitEmptyFilter(selector) { + checkSelectKitIsNotCollapsed(selector); + await fillIn(`${selector} .filter-input`, ""); +} + async function selectKitSelectRowByValue(value, selector) { checkSelectKitIsNotCollapsed(selector); await click(`${selector} .select-kit-row[data-value='${value}']`); @@ -180,6 +185,10 @@ export default function selectKit(selector) { await selectKitFillInFilter(filter, selector); }, + async emptyFilter() { + await selectKitEmptyFilter(selector); + }, + async keyboard(value, target) { await keyboardHelper(value, target, selector); },