- | {{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}} | +
{{i18n "user.save_to_change_theme" save_text=(i18n "save") }}
+ {{/if}} {{#if showThemeSetDefault}}+ {{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 @@ + +