From c95b90bd98edea269537257d1047307a57b2be59 Mon Sep 17 00:00:00 2001 From: Jay Pfaffman Date: Wed, 11 Oct 2017 15:53:12 -0700 Subject: [PATCH 001/174] move mailgun to top--elastic email seems to confuse people --- docs/INSTALL-email.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/INSTALL-email.md b/docs/INSTALL-email.md index e1c534897e..d62a669d8c 100644 --- a/docs/INSTALL-email.md +++ b/docs/INSTALL-email.md @@ -7,7 +7,13 @@ The following are template configurations for email service providers who offer **Please note that in any email provider, you _must_ verify and use the subdomain, e.g. `discourse.example.com`. If you verify the domain only, e.g. `example.com`, mail will not be configured correctly.** Enter these values when prompted by `./discourse-setup` per the [install guide](https://github.com/discourse/discourse/blob/master/docs/INSTALL-cloud.md#edit-discourse-configuration): - + +#### [Mailgun][gun] — 10k emails/month (with credit card) + + SMTP server address? smtp.mailgun.org + SMTP user name? [SMTP credentials for your domain under domains tab] + SMTP password? [SMTP credentials for your domain under domains tab] + #### [Elastic Email][ee] — 150k emails/month SMTP server address? smtp.elasticemail.com @@ -23,12 +29,6 @@ Enter these values when prompted by `./discourse-setup` per the [install guide]( We recommend creating an [API Key][sg2] instead of using your SendGrid username and password. -#### [Mailgun][gun] — 10k emails/month (with credit card) - - SMTP server address? smtp.mailgun.org - SMTP user name? [SMTP credentials for your domain under domains tab] - SMTP password? [SMTP credentials for your domain under domains tab] - #### [Mailjet][jet] — 6k emails/month Go to [My Account page](https://www.mailjet.com/account) and click on the ["SMTP and SEND API Settings"](https://www.mailjet.com/account/setup) link. From b124e5f19f553911e349ac9824cb201942dc0736 Mon Sep 17 00:00:00 2001 From: Neil Lalonde Date: Fri, 13 Oct 2017 11:55:49 -0400 Subject: [PATCH 002/174] FIX: TL0 users' messages to moderators were not being posted when flagging private messages --- lib/guardian.rb | 6 +- spec/components/guardian_spec.rb | 23 +++++- spec/components/topic_creator_spec.rb | 3 +- spec/models/post_action_spec.rb | 114 ++++++++++++++++---------- 4 files changed, 96 insertions(+), 50 deletions(-) diff --git a/lib/guardian.rb b/lib/guardian.rb index 773dfcb0da..2bc13f9e68 100644 --- a/lib/guardian.rb +++ b/lib/guardian.rb @@ -289,8 +289,10 @@ class Guardian (target.is_a?(Group) || target.is_a?(User)) && # User is authenticated authenticated? && - # Have to be a basic level at least - @user.has_trust_level?(SiteSetting.min_trust_to_send_messages) && + # Have to be a basic level at least, or are contacting moderators + (@user.has_trust_level?(SiteSetting.min_trust_to_send_messages) || + (target.is_a?(User) && target.moderator?) || + (target.name == Group[:moderators].name)) && # PMs are enabled (is_staff? || SiteSetting.enable_private_messages) && # Can't send PMs to suspended users diff --git a/spec/components/guardian_spec.rb b/spec/components/guardian_spec.rb index 82ee29609a..c2674e38ea 100644 --- a/spec/components/guardian_spec.rb +++ b/spec/components/guardian_spec.rb @@ -154,9 +154,22 @@ describe Guardian do expect(Guardian.new(user).can_send_private_message?(user)).to be_truthy end - it "returns false when you are untrusted" do - user.trust_level = TrustLevel[0] - expect(Guardian.new(user).can_send_private_message?(another_user)).to be_falsey + context "when user is untrusted " do + before do + user.trust_level = TrustLevel[0] + end + + it "returns false to another user" do + expect(Guardian.new(user).can_send_private_message?(another_user)).to be_falsey + end + + it "returns true to moderator user" do + expect(Guardian.new(user).can_send_private_message?(moderator)).to be_truthy + end + + it "returns true to moderator group" do + expect(Guardian.new(user).can_send_private_message?(Group[:moderators])).to be_truthy + end end it "returns true to another user" do @@ -180,6 +193,10 @@ describe Guardian do expect(Guardian.new(moderator).can_send_private_message?(another_user)).to be_truthy expect(Guardian.new(admin).can_send_private_message?(another_user)).to be_truthy end + + it "returns false even to a moderator" do + expect(Guardian.new(trust_level_4).can_send_private_message?(moderator)).to be_falsey + end end context "target user is suspended" do diff --git a/spec/components/topic_creator_spec.rb b/spec/components/topic_creator_spec.rb index 0ed814cae1..7aa8330c87 100644 --- a/spec/components/topic_creator_spec.rb +++ b/spec/components/topic_creator_spec.rb @@ -3,11 +3,12 @@ require 'rails_helper' describe TopicCreator do let(:user) { Fabricate(:user, trust_level: TrustLevel[2]) } + let(:user2) { Fabricate(:user, trust_level: TrustLevel[2]) } let(:moderator) { Fabricate(:moderator) } let(:admin) { Fabricate(:admin) } let(:valid_attrs) { Fabricate.attributes_for(:topic) } - let(:pm_valid_attrs) { { raw: 'this is a new post', title: 'this is a new title', archetype: Archetype.private_message, target_usernames: moderator.username } } + let(:pm_valid_attrs) { { raw: 'this is a new post', title: 'this is a new title', archetype: Archetype.private_message, target_usernames: user2.username } } let(:pm_to_email_valid_attrs) do { diff --git a/spec/models/post_action_spec.rb b/spec/models/post_action_spec.rb index 34514489b3..d1463b22c3 100644 --- a/spec/models/post_action_spec.rb +++ b/spec/models/post_action_spec.rb @@ -42,62 +42,88 @@ describe PostAction do expect { PostAction.act(admin, post, PostActionType.types[:notify_user], message: "WAT") }.not_to raise_error end - it "notify moderators integration test" do - post = create_post - mod = moderator - Group.refresh_automatic_groups! + context "notify moderators integration test" do + let!(:mod) { moderator } - action = PostAction.act(codinghorror, post, PostActionType.types[:notify_moderators], message: "this is my special long message") + before { Group.refresh_automatic_groups! } - posts = Post.joins(:topic) - .select('posts.id, topics.subtype, posts.topic_id') - .where('topics.archetype' => Archetype.private_message) - .to_a + it "flag from TL1 user" do + post = create_post - expect(posts.count).to eq(1) - expect(action.related_post_id).to eq(posts[0].id.to_i) - expect(posts[0].subtype).to eq(TopicSubtype.notify_moderators) + action = PostAction.act(codinghorror, post, PostActionType.types[:notify_moderators], message: "this is my special long message") - topic = posts[0].topic + posts = Post.joins(:topic) + .select('posts.id, topics.subtype, posts.topic_id') + .where('topics.archetype' => Archetype.private_message) + .to_a - # Moderators should be invited to the private topic, otherwise they're not permitted to see it - topic_user_ids = topic.reload.topic_users.map { |x| x.user_id } - expect(topic_user_ids).to include(codinghorror.id) - expect(topic_user_ids).to include(mod.id) + expect(posts.count).to eq(1) + expect(action.related_post_id).to eq(posts[0].id.to_i) + expect(posts[0].subtype).to eq(TopicSubtype.notify_moderators) - expect(topic.topic_users.where(user_id: mod.id) - .pluck(:notification_level).first).to eq(TopicUser.notification_levels[:tracking]) + topic = posts[0].topic - expect(topic.topic_users.where(user_id: codinghorror.id) - .pluck(:notification_level).first).to eq(TopicUser.notification_levels[:watching]) + # Moderators should be invited to the private topic, otherwise they're not permitted to see it + topic_user_ids = topic.reload.topic_users.map { |x| x.user_id } + expect(topic_user_ids).to include(codinghorror.id) + expect(topic_user_ids).to include(mod.id) - # reply to PM should not clear flag - PostCreator.new(mod, topic_id: posts[0].topic_id, raw: "This is my test reply to the user, it should clear flags").create - action.reload - expect(action.deleted_at).to eq(nil) + expect(topic.topic_users.where(user_id: mod.id) + .pluck(:notification_level).first).to eq(TopicUser.notification_levels[:tracking]) - # Acting on the flag should not post an automated status message (since a moderator already replied) - expect(topic.posts.count).to eq(2) - PostAction.agree_flags!(post, admin) - topic.reload - expect(topic.posts.count).to eq(2) + expect(topic.topic_users.where(user_id: codinghorror.id) + .pluck(:notification_level).first).to eq(TopicUser.notification_levels[:watching]) - # Clearing the flags should not post an automated status message - PostAction.act(mod, post, PostActionType.types[:notify_moderators], message: "another special message") - PostAction.clear_flags!(post, admin) - topic.reload - expect(topic.posts.count).to eq(2) + # reply to PM should not clear flag + PostCreator.new(mod, topic_id: posts[0].topic_id, raw: "This is my test reply to the user, it should clear flags").create + action.reload + expect(action.deleted_at).to eq(nil) - # Acting on the flag should post an automated status message - another_post = create_post - action = PostAction.act(codinghorror, another_post, PostActionType.types[:notify_moderators], message: "foobar") - topic = action.related_post.topic + # Acting on the flag should not post an automated status message (since a moderator already replied) + expect(topic.posts.count).to eq(2) + PostAction.agree_flags!(post, admin) + topic.reload + expect(topic.posts.count).to eq(2) - expect(topic.posts.count).to eq(1) - PostAction.agree_flags!(another_post, admin) - topic.reload - expect(topic.posts.count).to eq(2) - expect(topic.posts.last.post_type).to eq(Post.types[:moderator_action]) + # Clearing the flags should not post an automated status message + PostAction.act(mod, post, PostActionType.types[:notify_moderators], message: "another special message") + PostAction.clear_flags!(post, admin) + topic.reload + expect(topic.posts.count).to eq(2) + + # Acting on the flag should post an automated status message + another_post = create_post + action = PostAction.act(codinghorror, another_post, PostActionType.types[:notify_moderators], message: "foobar") + topic = action.related_post.topic + + expect(topic.posts.count).to eq(1) + PostAction.agree_flags!(another_post, admin) + topic.reload + expect(topic.posts.count).to eq(2) + expect(topic.posts.last.post_type).to eq(Post.types[:moderator_action]) + end + + it "flag from TL0 user" do + tl0_user = Fabricate(:user, trust_level: 0) + first_post = Fabricate(:post, user: Fabricate(:user, trust_level: 1)) + + pm_topic = Fabricate(:private_message_topic, + first_post: first_post, + topic_allowed_users: [ + Fabricate.build(:topic_allowed_user, user: first_post.user), + Fabricate.build(:topic_allowed_user, user: tl0_user), + ] + ) + + action = PostAction.act(tl0_user, first_post, PostActionType.types[:notify_moderators], message: 'my special message') + + expect(action.related_post_id).to be_present + + notify_moderators_post = action.related_post + + expect(notify_moderators_post.topic&.subtype).to eq(TopicSubtype.notify_moderators) + expect(notify_moderators_post.raw).to include('my special message') + end end describe 'notify_moderators' do From c2a8b95e8337a9402558f63b46f0ee2e8f2b491a Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Fri, 13 Oct 2017 11:56:24 -0400 Subject: [PATCH 003/174] FIX: Don't show the up arrow to jump to posts in different topics --- app/assets/javascripts/discourse/widgets/post-cooked.js.es6 | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/discourse/widgets/post-cooked.js.es6 b/app/assets/javascripts/discourse/widgets/post-cooked.js.es6 index b9ffee9585..6e8f82c167 100644 --- a/app/assets/javascripts/discourse/widgets/post-cooked.js.es6 +++ b/app/assets/javascripts/discourse/widgets/post-cooked.js.es6 @@ -159,10 +159,11 @@ export default class PostCooked { _updateQuoteElements($aside, desc) { let navLink = ""; const quoteTitle = I18n.t("post.follow_quote"); - const postNumber = $aside.data('post'); + let postNumber = $aside.data('post'); + let topicNumber = $aside.data('topic'); // If we have a post reference - if (postNumber) { + if (topicNumber && topicNumber === this.attrs.topicId && postNumber) { let icon = iconHTML('arrow-up'); navLink = `${icon}`; } From dc2d9f05dce0ea19c4f26c9094e7a19b10230092 Mon Sep 17 00:00:00 2001 From: Kris Date: Tue, 10 Oct 2017 16:07:46 -0400 Subject: [PATCH 004/174] removing scale-color, first pass --- .../stylesheets/common/admin/admin_base.scss | 24 ++++----- .../stylesheets/common/admin/customize.scss | 4 +- .../stylesheets/common/admin/flagging.scss | 4 +- .../stylesheets/common/base/_topic-list.scss | 22 ++++---- app/assets/stylesheets/common/base/alert.scss | 4 +- .../common/base/category-list.scss | 2 +- .../stylesheets/common/base/combobox.scss | 2 +- .../stylesheets/common/base/compose.scss | 8 +-- .../stylesheets/common/base/directory.scss | 4 +- .../stylesheets/common/base/discourse.scss | 10 ++-- app/assets/stylesheets/common/base/emoji.scss | 6 +-- app/assets/stylesheets/common/base/group.scss | 4 +- .../stylesheets/common/base/groups.scss | 8 +-- .../stylesheets/common/base/header.scss | 2 +- .../stylesheets/common/base/history.scss | 8 +-- app/assets/stylesheets/common/base/login.scss | 10 ++-- .../stylesheets/common/base/menu-panel.scss | 16 +++--- app/assets/stylesheets/common/base/modal.scss | 8 +-- .../common/base/notifications-button.scss | 2 +- .../stylesheets/common/base/onebox.scss | 8 +-- .../stylesheets/common/base/search.scss | 12 ++--- .../stylesheets/common/base/share_link.scss | 4 +- .../stylesheets/common/base/tagging.scss | 16 +++--- .../stylesheets/common/base/topic-post.scss | 28 +++++----- app/assets/stylesheets/common/base/topic.scss | 4 +- .../stylesheets/common/base/user-badges.scss | 4 +- app/assets/stylesheets/common/base/user.scss | 14 ++--- .../stylesheets/common/components/badges.scss | 8 +-- .../stylesheets/common/components/banner.scss | 6 +-- .../common/components/buttons.scss | 8 +-- .../components/dropdown-select-box.scss | 4 +- .../common/components/hashtag.scss | 4 +- .../common/components/select-box.scss | 2 +- .../common/components/user-info.scss | 8 +-- .../common/components/user-stream-item.scss | 8 +-- .../common/foundation/helpers.scss | 2 +- .../common/foundation/variables.scss | 18 +++++-- .../stylesheets/common/topic-timeline.scss | 14 ++--- .../stylesheets/desktop/category-list.scss | 6 +-- app/assets/stylesheets/desktop/compose.scss | 14 ++--- app/assets/stylesheets/desktop/discourse.scss | 10 ++-- app/assets/stylesheets/desktop/group.scss | 2 +- app/assets/stylesheets/desktop/header.scss | 4 +- .../desktop/latest-topic-list.scss | 6 +-- app/assets/stylesheets/desktop/login.scss | 4 +- app/assets/stylesheets/desktop/modal.scss | 8 +-- .../stylesheets/desktop/queued-posts.scss | 2 +- .../stylesheets/desktop/topic-list.scss | 20 +++---- .../stylesheets/desktop/topic-post.scss | 54 +++++++++---------- app/assets/stylesheets/desktop/topic.scss | 4 +- app/assets/stylesheets/desktop/upload.scss | 2 +- app/assets/stylesheets/desktop/user.scss | 12 ++--- app/assets/stylesheets/embed.scss | 2 +- .../mobile/components/user-stream-item.scss | 2 +- app/assets/stylesheets/mobile/compose.scss | 14 ++--- app/assets/stylesheets/mobile/directory.scss | 2 +- app/assets/stylesheets/mobile/discourse.scss | 4 +- app/assets/stylesheets/mobile/login.scss | 4 +- app/assets/stylesheets/mobile/modal.scss | 4 +- app/assets/stylesheets/mobile/topic-list.scss | 18 +++---- app/assets/stylesheets/mobile/topic-post.scss | 28 +++++----- app/assets/stylesheets/mobile/topic.scss | 8 +-- app/assets/stylesheets/mobile/upload.scss | 2 +- 63 files changed, 285 insertions(+), 271 deletions(-) diff --git a/app/assets/stylesheets/common/admin/admin_base.scss b/app/assets/stylesheets/common/admin/admin_base.scss index 63f4f97fbc..f9e4f5bce9 100644 --- a/app/assets/stylesheets/common/admin/admin_base.scss +++ b/app/assets/stylesheets/common/admin/admin_base.scss @@ -512,7 +512,7 @@ $mobile-breakpoint: 700px; } .desc { - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); + color: dark-light-choose($primary-medium, $secondary-medium); } h3 { @@ -545,7 +545,7 @@ section.details { } #selected-controls { - background-color: scale-color($tertiary, $lightness: 75%); + background-color: $tertiary-low; padding: 8px; min-height: 27px; position: fixed; @@ -581,7 +581,7 @@ section.details { border-top: 0; } &.highlight-danger { - background-color: scale-color($danger, $lightness: 50%); + background-color: $danger-low; } border-top: 1px solid $primary-low; &:before, &:after { @@ -651,7 +651,7 @@ section.details { font-weight: normal; padding: 0 6px; color: $secondary; - background-color: scale-color($tertiary, $lightness: 50%); + background-color: $tertiary-medium; border-radius: 3px; } } @@ -661,7 +661,7 @@ section.details { p.help { margin: 0; - color: dark-light-choose(scale-color($primary, $lightness: 40%), scale-color($secondary, $lightness: 60%)); + color: dark-light-choose($primary-medium, $secondary-medium); font-size: 0.9em; } } @@ -679,7 +679,7 @@ section.details { .current-badge-actions { margin: 10px; padding: 10px; - border-top: 1px solid dark-light-choose(scale-color($primary, $lightness: 80%), scale-color($secondary, $lightness: 20%)); + border-top: 1px solid dark-light-choose($primary-low, $secondary-high); } .buttons { @@ -732,7 +732,7 @@ section.details { .groups { .ac-wrap { width: 100% !important; - border-color: dark-light-choose(scale-color($primary, $lightness: 75%), scale-color($secondary, $lightness: 25%)); + border-color: dark-light-choose($primary-low-mid, $secondary-high); .item { margin-right: 10px; } @@ -754,7 +754,7 @@ section.details { } .select2-choices { width: 100%; - border-color: dark-light-choose(scale-color($primary, $lightness: 75%), scale-color($secondary, $lightness: 25%)); + border-color: dark-light-choose($primary-low-mid, $secondary-high); } .content-list { @@ -1250,7 +1250,7 @@ table.api-keys { margin: 0 0 20px 6px; a.filter { display: inline-block; - background-color: dark-light-choose(scale-color($primary, $lightness: 75%), scale-color($secondary, $lightness: 25%)); + background-color: dark-light-choose($primary-low-mid, $secondary-high); padding: 3px 10px; border-radius: 3px; @@ -1283,7 +1283,7 @@ table.api-keys { .staff-actions, .screened-emails, .screened-urls, .screened-ip-addresses, .permalinks, .web-hook-events { - border-bottom: dotted 1px dark-light-choose(scale-color($primary, $lightness: 75%), scale-color($secondary, $lightness: 25%)); + border-bottom: dotted 1px dark-light-choose($primary-low-mid, $secondary); .heading-container { width: 100%; @@ -1607,7 +1607,7 @@ table#user-badges { } p.description { - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); + color: dark-light-choose($primary-medium, $secondary-medium); margin-bottom: 1em; max-width: 700px; } @@ -1629,7 +1629,7 @@ table#user-badges { .reply-key { display: block; font-size: 12px; - color: dark-light-choose(scale-color($primary, $lightness: 40%), scale-color($secondary, $lightness: 40%)); + color: dark-light-choose($primary-medium, $secondary-high); } .username div { max-width: 180px; diff --git a/app/assets/stylesheets/common/admin/customize.scss b/app/assets/stylesheets/common/admin/customize.scss index 1ba3bbc507..bbcb624aa2 100644 --- a/app/assets/stylesheets/common/admin/customize.scss +++ b/app/assets/stylesheets/common/admin/customize.scss @@ -35,7 +35,7 @@ .select2-chosen, .color-schemes li { .fa { margin-right: 6px; - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); + color: dark-light-choose($primary-medium, $secondary-medium); } } .show-current-style { @@ -173,7 +173,7 @@ td.actions { width: 200px; } .hex-input { width: 80px; margin-bottom: 0; } .hex { text-align: center; } - .description { color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); } + .description { color: dark-light-choose($primary-medium, $secondary-medium); } .invalid .hex input { background-color: white; diff --git a/app/assets/stylesheets/common/admin/flagging.scss b/app/assets/stylesheets/common/admin/flagging.scss index b1cce63b51..166a092620 100644 --- a/app/assets/stylesheets/common/admin/flagging.scss +++ b/app/assets/stylesheets/common/admin/flagging.scss @@ -6,7 +6,7 @@ .flagged-post.deleted { .flagged-post-excerpt, .flagged-post-avatar { - background-color: scale-color($danger, $lightness: 70%); + background-color: $danger-low; } } @@ -122,7 +122,7 @@ } &:hover { - background-color: $highlight-low; + background-color: dark-light-choose($highlight-low, $highlight-medium); } } diff --git a/app/assets/stylesheets/common/base/_topic-list.scss b/app/assets/stylesheets/common/base/_topic-list.scss index 3a015867a6..b381960d5e 100644 --- a/app/assets/stylesheets/common/base/_topic-list.scss +++ b/app/assets/stylesheets/common/base/_topic-list.scss @@ -74,7 +74,7 @@ border: none; td { - border-bottom: 1px solid scale-color($danger, $lightness: 60%); + border-bottom: 1px solid $danger-low; line-height: 0.1em; padding: 0px; text-align: center; @@ -82,7 +82,7 @@ td span { background-color: $secondary; - color: scale-color($danger, $lightness: 60%); + color: $danger-low; padding: 0px 8px; font-size: 0.929em; } @@ -100,14 +100,14 @@ } th { - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); + color: dark-light-choose($primary-medium, $secondary-medium); font-weight: normal; font-size: 1em; - button .d-icon {color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%));} + button .d-icon {color: dark-light-choose($primary-medium, $secondary-medium);} } td { - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); + color: dark-light-choose($primary-medium, $secondary-medium); font-size: 1em; } @@ -122,7 +122,7 @@ .topic-excerpt { font-size: 0.929em; margin-top: 8px; - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); + color: dark-light-choose($primary-medium, $secondary-medium); word-wrap: break-word; line-height: 1.4; padding-right: 20px; @@ -167,7 +167,7 @@ .category .badge-notification { background-color:transparent; - color: scale-color($primary, $lightness: 50%); + color: $primary-medium; } .subcategories .badge { @@ -224,7 +224,7 @@ background-color: transparent; padding: 0; border: 0; - color: scale-color($danger, $lightness: 20%); + color: $danger-medium; font-size: 0.929em; cursor: default; } @@ -309,7 +309,7 @@ ol.category-breadcrumb { } .top-date-string { - color: dark-light-choose(scale-color($primary, $lightness: 60%), scale-color($secondary, $lightness: 40%)); + color: dark-light-choose($primary-medium, $secondary-high); font-weight: normal; text-transform: uppercase; } @@ -369,12 +369,12 @@ ol.category-breadcrumb { } div.education { - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); + color: dark-light-choose($primary-medium, $secondary-medium); } .list-cell { padding: 12px 5px; - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 40%)); + color: dark-light-choose($primary-medium, $secondary-high); } .table-heading { diff --git a/app/assets/stylesheets/common/base/alert.scss b/app/assets/stylesheets/common/base/alert.scss index 892151a3b2..397c7c9da8 100644 --- a/app/assets/stylesheets/common/base/alert.scss +++ b/app/assets/stylesheets/common/base/alert.scss @@ -1,6 +1,6 @@ .alert { padding: 8px 35px 8px 14px; - background-color: scale-color($danger, $lightness: 75%); + background-color: $danger-low; color: #c09853; .close { @@ -30,7 +30,7 @@ -webkit-appearance: none; } &.alert-success { - background-color: $success-low; + background-color: $success-medium; color: $primary; } &.alert-error { diff --git a/app/assets/stylesheets/common/base/category-list.scss b/app/assets/stylesheets/common/base/category-list.scss index 0626f26704..c38bfe61c7 100644 --- a/app/assets/stylesheets/common/base/category-list.scss +++ b/app/assets/stylesheets/common/base/category-list.scss @@ -76,7 +76,7 @@ padding: 0 1em 1em 1em; text-align: center; font-size: 1.05em; - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 40%)); + color: dark-light-choose($primary-medium, $secondary-high); .overflow { max-height: 6em; overflow: hidden; diff --git a/app/assets/stylesheets/common/base/combobox.scss b/app/assets/stylesheets/common/base/combobox.scss index 4c80abc5c6..08f1f155fd 100644 --- a/app/assets/stylesheets/common/base/combobox.scss +++ b/app/assets/stylesheets/common/base/combobox.scss @@ -26,7 +26,7 @@ .select2-drop { background: $secondary; .d-icon { - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); + color: dark-light-choose($primary-medium, $secondary-medium); } } diff --git a/app/assets/stylesheets/common/base/compose.scss b/app/assets/stylesheets/common/base/compose.scss index 10b5c88311..35bac3fdca 100644 --- a/app/assets/stylesheets/common/base/compose.scss +++ b/app/assets/stylesheets/common/base/compose.scss @@ -35,7 +35,7 @@ background-color: $tertiary-low; } @include hover { - background-color: $highlight-low; + background-color: dark-light-choose($highlight-low, $highlight-medium); text-decoration: none; } } @@ -92,8 +92,8 @@ div.ac-wrap div.item a.remove, .remove-link { display: inline-block; border: 1px solid $primary-low; &:hover { - background-color: scale-color($danger, $lightness: 75%); - border: 1px solid scale-color($danger, $lightness: 30%); + background-color: $danger-low; + border: 1px solid $danger-medium; text-decoration: none; color: $danger; } @@ -162,7 +162,7 @@ div.ac-wrap { } #draft-status, #file-uploading { - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); + color: dark-light-choose($primary-medium, $secondary-medium); } .composer-bottom-right { diff --git a/app/assets/stylesheets/common/base/directory.scss b/app/assets/stylesheets/common/base/directory.scss index 8a31885b8c..7b5f7bdc6c 100644 --- a/app/assets/stylesheets/common/base/directory.scss +++ b/app/assets/stylesheets/common/base/directory.scss @@ -36,10 +36,10 @@ tr.me { td { - background-color: $highlight-low; + background-color: dark-light-choose($highlight-low, $highlight-medium); .username a, .name, .title, .number, .time-read { - color: dark-light-choose(scale-color($highlight, $lightness: -50%), scale-color($highlight, $lightness: 50%)); + color: dark-light-choose(scale-color($highlight, $lightness: -50%), $highlight-medium); } } } diff --git a/app/assets/stylesheets/common/base/discourse.scss b/app/assets/stylesheets/common/base/discourse.scss index 2cca6788a5..077acd0c68 100644 --- a/app/assets/stylesheets/common/base/discourse.scss +++ b/app/assets/stylesheets/common/base/discourse.scss @@ -132,7 +132,7 @@ input { } &.invalid { - background-color: dark-light-choose(scale-color($danger, $lightness: 80%), scale-color($danger, $lightness: -60%)); + background-color: dark-light-choose($danger-low, scale-color($danger, $lightness: -60%)); } .radio &[type="radio"], @@ -191,19 +191,19 @@ input { } // the default for table cells in topic list -// is scale-color($primary, $lightness: 50%) +// is $primary-medium // numbers get dimmer as they get colder .coldmap { &-high { - color: dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)) !important; + color: dark-light-choose($primary-low-mid, $secondary-high) !important; } &-med { - color: dark-light-choose(scale-color($primary, $lightness: 60%), scale-color($secondary, $lightness: 40%)) !important; + color: dark-light-choose($primary-medium, $secondary-high) !important; } &-low { - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)) !important; + color: dark-light-choose($primary-medium, $secondary-medium) !important; } } diff --git a/app/assets/stylesheets/common/base/emoji.scss b/app/assets/stylesheets/common/base/emoji.scss index f20fd3dae2..1e10fd422e 100644 --- a/app/assets/stylesheets/common/base/emoji.scss +++ b/app/assets/stylesheets/common/base/emoji.scss @@ -76,7 +76,7 @@ img.emoji { .emoji-picker .section-header .clear-recent .fa{ margin: 0; padding: 0; - color: dark-light-choose(scale-color($header_primary, $lightness: 50%), $header_primary); + color: dark-light-choose($header_primary-medium, $header_primary); &:hover { color: $primary; @@ -197,7 +197,7 @@ img.emoji { } .emoji-picker .filter .d-icon-search { - color: dark-light-choose(scale-color($header_primary, $lightness: 50%), $header_primary); + color: dark-light-choose($header_primary-medium, $header_primary); font-size: 16px; margin-left: 5px; margin-right: 5px; @@ -239,7 +239,7 @@ img.emoji { top: 12px; border: 0; background: none; - color: dark-light-choose(scale-color($header_primary, $lightness: 50%), $header_primary); + color: dark-light-choose($header_primary-medium, $header_primary); outline: none; display: none; diff --git a/app/assets/stylesheets/common/base/group.scss b/app/assets/stylesheets/common/base/group.scss index c08de36782..c476e892da 100644 --- a/app/assets/stylesheets/common/base/group.scss +++ b/app/assets/stylesheets/common/base/group.scss @@ -15,7 +15,7 @@ .group-info-full-name { font-size: 1.2em; - color: dark-light-choose(scale-color($primary, $lightness: 30%), scale-color($secondary, $lightness: 70%)); + color: dark-light-choose($primary-high, $secondary-low); } span { @@ -93,7 +93,7 @@ table.group-members { border-bottom: 3px solid $primary-low; text-align: center; padding: 5px 0px 5px 5px; - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); + color: dark-light-choose($primary-medium, $secondary-medium); font-weight: normal; } diff --git a/app/assets/stylesheets/common/base/groups.scss b/app/assets/stylesheets/common/base/groups.scss index 6dae404db0..c6e7791ab8 100644 --- a/app/assets/stylesheets/common/base/groups.scss +++ b/app/assets/stylesheets/common/base/groups.scss @@ -8,7 +8,7 @@ width: 100%; th { - border-bottom: 1px solid $primary-low; + border-bottom: 1px solid $primary-low; padding: 5px 0px; text-align: left; } @@ -30,16 +30,16 @@ .groups-info-name { font-weight: bold; color: $primary; - color: dark-light-choose(scale-color($primary, $lightness: 30%), scale-color($secondary, $lightness: 70%)); + color: dark-light-choose($primary-high, $secondary-low); } .groups-info-full-name { - color: dark-light-choose(scale-color($primary, $lightness: 30%), scale-color($secondary, $lightness: 70%)); + color: dark-light-choose($primary-high, $secondary-low); } .groups-info-title { font-size: 0.9em; - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); + color: dark-light-choose($primary-medium, $secondary-medium); } span { diff --git a/app/assets/stylesheets/common/base/header.scss b/app/assets/stylesheets/common/base/header.scss index 57c776bf7a..ac9feaa0a8 100644 --- a/app/assets/stylesheets/common/base/header.scss +++ b/app/assets/stylesheets/common/base/header.scss @@ -151,7 +151,7 @@ .unread-notifications { left: auto; right: 0; - background-color: scale-color($tertiary, $lightness: 50%); + background-color: dark-light-choose($tertiary-medium, $tertiary); } .unread-private-messages, .ring { left: auto; diff --git a/app/assets/stylesheets/common/base/history.scss b/app/assets/stylesheets/common/base/history.scss index 0de3abad17..138784a737 100644 --- a/app/assets/stylesheets/common/base/history.scss +++ b/app/assets/stylesheets/common/base/history.scss @@ -40,11 +40,11 @@ } .diff-ins { color: dark-light-choose($primary, $secondary); - background: scale-color($success, $lightness: 90%); + background: $success-low; } ins { color: $success; - background: scale-color($success, $lightness: 90%); + background: $success-low; } del, .diff-del { code, img { @@ -67,11 +67,11 @@ filter: alpha(opacity=50); } .diff-del { - background: scale-color($danger, $lightness: 60%); + background: $danger-low; } del { color: $danger; - background: scale-color($danger, $lightness: 60%); + background: $danger-low; } span.date { font-weight: bold; diff --git a/app/assets/stylesheets/common/base/login.scss b/app/assets/stylesheets/common/base/login.scss index 01b2f82510..c45ce027bf 100644 --- a/app/assets/stylesheets/common/base/login.scss +++ b/app/assets/stylesheets/common/base/login.scss @@ -29,7 +29,7 @@ $input-width: 220px; .disclaimer { font-size: 0.9em; - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); + color: dark-light-choose($primary-medium, $secondary-medium); clear: both; } @@ -56,7 +56,7 @@ $input-width: 220px; float: auto; } .instructions { - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); + color: dark-light-choose($primary-medium, $secondary-medium); margin: 0; font-size: 0.929em; font-weight: normal; @@ -70,7 +70,7 @@ $input-width: 220px; .password-reset { .instructions { label { - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); + color: dark-light-choose($primary-medium, $secondary-medium); } } } @@ -98,7 +98,7 @@ $input-width: 220px; margin-bottom: 10px; } .instructions { - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); + color: dark-light-choose($primary-medium, $secondary-medium); margin: 0; font-size: 0.929em; font-weight: normal; @@ -113,5 +113,5 @@ $input-width: 220px; button#login-link, button#new-account-link { background: transparent; - color: dark-light-choose(scale-color($primary, $lightness: 35%), scale-color($secondary, $lightness: 65%)); + color: dark-light-choose($primary-high, $secondary-low); } diff --git a/app/assets/stylesheets/common/base/menu-panel.scss b/app/assets/stylesheets/common/base/menu-panel.scss index c70c7eb12a..c3c9c8ceb0 100644 --- a/app/assets/stylesheets/common/base/menu-panel.scss +++ b/app/assets/stylesheets/common/base/menu-panel.scss @@ -67,7 +67,7 @@ .new { font-size: 0.8em; margin-left: 0.5em; - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); + color: dark-light-choose($primary-medium, $secondary-medium); } } @@ -79,7 +79,7 @@ margin: 5px 5px 0 8px; .box {margin-top: 0;} .badge-notification { - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); + color: dark-light-choose($primary-medium, $secondary-medium); background-color: transparent; display: inline; padding: 0; @@ -88,7 +88,7 @@ // note these topic counts only appear for anons in the category hamburger drop down b.topics-count { - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); + color: dark-light-choose($primary-medium, $secondary-medium); font-weight: normal; font-size: 11px; } @@ -161,7 +161,7 @@ .topic-statuses { float: none; display: inline-block; - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); + color: dark-light-choose($primary-medium, $secondary-medium); margin: 0; .fa { margin: 0; @@ -183,7 +183,7 @@ transition: all linear .15s; .user-results { - color: dark-light-choose(scale-color($primary, $lightness: 30%), scale-color($secondary, $lightness: 70%)); + color: dark-light-choose($primary-high, $secondary-low); } } @@ -204,8 +204,8 @@ margin: 0.5em 0; } - .fa { color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); } - .icon { color: dark-light-choose(scale-color($primary, $lightness: 30%), scale-color($secondary, $lightness: 70%)); } + .fa { color: dark-light-choose($primary-medium, $secondary-medium); } + .icon { color: dark-light-choose($primary-high, $secondary-low); } li { background-color: $tertiary-low; padding: 0.25em 0.5em; @@ -299,7 +299,7 @@ div.menu-links-header { text-align: right; } .fa, a { - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); + color: dark-light-choose($primary-medium, $secondary-medium); } a { diff --git a/app/assets/stylesheets/common/base/modal.scss b/app/assets/stylesheets/common/base/modal.scss index 67be235c9b..d782185c68 100644 --- a/app/assets/stylesheets/common/base/modal.scss +++ b/app/assets/stylesheets/common/base/modal.scss @@ -13,7 +13,7 @@ .input-hint-text { margin-left: 0.5em; - color: $secondary-medium; + color: $secondary-high; } .modal-header { @@ -327,8 +327,8 @@ .cannot_delete_reason { position: absolute; - background: dark-light-choose(scale-color($primary, $lightness: 10%), scale-color($secondary, $lightness: 10%)); - color: dark-light-choose(scale-color($primary, $lightness: 100%), scale-color($secondary, $lightness: 0%)); + background: dark-light-choose($primary, $secondary); + color: dark-light-choose($secondary, $secondary); text-align: center; border-radius: 2px; padding: 12px 8px; @@ -339,7 +339,7 @@ border: solid transparent; content: " "; position: absolute; - border-top-color: dark-light-choose(scale-color($primary, $lightness: 10%), scale-color($secondary, $lightness: 10%)); + border-top-color: dark-light-choose($primary, $secondary); border-width: 8px; } } diff --git a/app/assets/stylesheets/common/base/notifications-button.scss b/app/assets/stylesheets/common/base/notifications-button.scss index 63d3c2cf13..7aab2c4444 100644 --- a/app/assets/stylesheets/common/base/notifications-button.scss +++ b/app/assets/stylesheets/common/base/notifications-button.scss @@ -1,6 +1,6 @@ .notifications-button.notifications-button.notifications-button { .d-icon.regular, .d-icon.muted, .d-icon.watching-first-post { - color: dark-light-choose(scale-color($primary, $lightness: 40%), scale-color($secondary, $lightness: 60%)); + color: dark-light-choose($primary-medium, $secondary-medium); } .d-icon.tracking, .d-icon.watching { color: $tertiary; diff --git a/app/assets/stylesheets/common/base/onebox.scss b/app/assets/stylesheets/common/base/onebox.scss index 86e505e622..b89a3efc78 100644 --- a/app/assets/stylesheets/common/base/onebox.scss +++ b/app/assets/stylesheets/common/base/onebox.scss @@ -97,7 +97,7 @@ aside.onebox { header { margin-bottom: 8px; a[href] { - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); + color: dark-light-choose($primary-medium, $secondary-medium); text-decoration: none; } } @@ -123,12 +123,12 @@ aside.onebox { } a[href] { - color: dark-light-choose(scale-color($tertiary, $lightness: -20%), $tertiary); + color: dark-light-choose($tertiary, $tertiary); text-decoration: none; } a[href]:visited { - color: dark-light-choose(scale-color($tertiary, $lightness: -20%), $tertiary); + color: dark-light-choose($tertiary, $tertiary); } img { @@ -364,7 +364,7 @@ aside.onebox.stackexchange .onebox-body { } .onebox-metadata { - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); + color: dark-light-choose($primary-medium, $secondary-medium); } .onebox.xkcd .onebox-body { diff --git a/app/assets/stylesheets/common/base/search.scss b/app/assets/stylesheets/common/base/search.scss index a8a81759f4..cd3e92688f 100644 --- a/app/assets/stylesheets/common/base/search.scss +++ b/app/assets/stylesheets/common/base/search.scss @@ -10,7 +10,7 @@ } .like-count { - color: dark-light-choose(scale-color($primary, $lightness: 40%), scale-color($secondary, $lightness: 60%)); + color: dark-light-choose($primary-medium, $secondary-medium); .fa { color: $love; font-size: 12px; } } @@ -32,7 +32,7 @@ margin-right: 14px; } a.search-link:visited .topic-title { - color: scale-color($tertiary, $lightness: 15%); + color: $tertiary-high; } .search-link { .topic-statuses, .topic-title { @@ -43,7 +43,7 @@ .topic-statuses { float: none; display: inline-block; - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); + color: dark-light-choose($primary-medium, $secondary-medium); font-size: 1.0em; } } @@ -52,13 +52,13 @@ line-height: 20px; word-wrap: break-word; max-width: 640px; - color: dark-light-choose(scale-color($primary, $lightness: 40%), scale-color($secondary, $lightness: 60%)); + color: dark-light-choose($primary-medium, $secondary-medium); .date { - color: dark-light-choose(scale-color($primary, $lightness: 60%), scale-color($secondary, $lightness: 40%)); + color: dark-light-choose($primary-medium, $secondary-high); } .search-highlight { - color: dark-light-choose(scale-color($primary, $lightness: 10%), scale-color($secondary, $lightness: 90%)); + color: dark-light-choose($primary, $secondary-low); } } diff --git a/app/assets/stylesheets/common/base/share_link.scss b/app/assets/stylesheets/common/base/share_link.scss index 0f74cb0e76..10ba75d110 100644 --- a/app/assets/stylesheets/common/base/share_link.scss +++ b/app/assets/stylesheets/common/base/share_link.scss @@ -55,7 +55,7 @@ float: right; font-size: 1.429em; a { - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); + color: dark-light-choose($primary-medium, $secondary-medium); } } @@ -67,7 +67,7 @@ .date { float: right; margin: 5px; - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); + color: dark-light-choose($primary-medium, $secondary-medium); } input[type=text] { diff --git a/app/assets/stylesheets/common/base/tagging.scss b/app/assets/stylesheets/common/base/tagging.scss index 65c06c6f8e..623b4369bc 100644 --- a/app/assets/stylesheets/common/base/tagging.scss +++ b/app/assets/stylesheets/common/base/tagging.scss @@ -83,7 +83,7 @@ margin: 0; } -$tag-color: scale-color($primary, $lightness: 40%); +$tag-color: $primary-medium; .discourse-tag-count { font-size: 0.8em; @@ -109,18 +109,18 @@ $tag-color: scale-color($primary, $lightness: 40%); } &.box { - background-color: scale-color($primary, $lightness: 90%); - color: scale-color($primary, $lightness: 30%); + background-color: $primary-low; + color: $primary-high; padding: 2px 8px; .extra-info-wrapper & { - background-color: scale-color($header-primary, $lightness: 90%); - color: scale-color($header-primary, $lightness: 30%); + background-color: $header_primary-low; + color: $header_primary-medium; } } &.simple, &.simple:visited, &.simple:hover { margin-right: 0px; - color: scale-color($primary, $lightness: 30%); + color: $primary-high; } } @@ -183,7 +183,7 @@ $tag-color: scale-color($primary, $lightness: 40%); .discourse-tag.bullet:before { content: "\f04d"; font-family: FontAwesome; - color: scale-color($primary, $lightness: 70%); + color: $primary-low-mid; margin-right: 5px; font-size: 0.7em; position:relative; @@ -226,7 +226,7 @@ header .discourse-tag {color: $tag-color } .autocomplete { .d-icon-tag { - color: dark-light-choose($primary, scale-color($primary, $lightness: 70%)); + color: dark-light-choose($primary, $primary-low-mid); padding-right: 5px; } diff --git a/app/assets/stylesheets/common/base/topic-post.scss b/app/assets/stylesheets/common/base/topic-post.scss index 4f8dd185db..84b7aeac1c 100644 --- a/app/assets/stylesheets/common/base/topic-post.scss +++ b/app/assets/stylesheets/common/base/topic-post.scss @@ -30,16 +30,16 @@ overflow: hidden; text-overflow: ellipsis; a { - color: dark-light-choose(scale-color($primary, $lightness: 30%), scale-color($secondary, $lightness: 70%)); + color: dark-light-choose($primary-high, $secondary-low); } } .fa { font-size: 11px; margin-left: 3px; - color: dark-light-choose(scale-color($primary, $lightness: 40%), scale-color($secondary, $lightness: 60%)); + color: dark-light-choose($primary-medium, $secondary-medium); } .new_user a, .user-title, .user-title a { - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); + color: dark-light-choose($primary-medium, $secondary-medium); } } @@ -49,8 +49,8 @@ h1, h2, h3, h4, h5, h6 { margin: 30px 0 10px; } h1 { line-height: 1em; } /* normalize.css sets h1 font size but not line height */ a { word-wrap: break-word; } - ins { background-color: dark-light-choose(scale-color($success, $lightness: 90%), scale-color($success, $lightness: -60%)); } - del { background-color: dark-light-choose(scale-color($danger, $lightness: 90%), scale-color($danger, $lightness: -60%)); } + ins { background-color: dark-light-choose($success-low, scale-color($success, $lightness: -60%)); } + del { background-color: dark-light-choose($danger-low, scale-color($danger, $lightness: -60%)); } } @@ -72,7 +72,7 @@ aside.quote { .title { @include post-aside; - color: dark-light-choose(scale-color($primary, $lightness: 30%), scale-color($secondary, $lightness: 70%)); + color: dark-light-choose($primary-high, $secondary-low); // IE will screw up the blockquote underneath if bottom padding is 0px padding: 12px 12px 1px 12px; // blockquote is underneath this and has top margin @@ -88,7 +88,7 @@ aside.quote { } .quote-controls, .quote-controls .d-icon { - color: dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)); + color: dark-light-choose($primary-low-mid, $secondary-high); } .cooked .highlight { @@ -238,7 +238,7 @@ aside.quote { } &.via-email { - color: dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)); + color: dark-light-choose($primary-low-mid, $secondary-high); } &.raw-email { cursor: pointer; @@ -287,7 +287,7 @@ blockquote > *:last-child { .gap { padding: 0.25em 0 0.5em 4.3em; - color: dark-light-choose(scale-color($primary, $lightness: 60%), scale-color($secondary, $lightness: 40%)); + color: dark-light-choose($primary-medium, $secondary-high); cursor: pointer; text-transform: uppercase; font-weight: bold; @@ -329,12 +329,12 @@ blockquote > *:last-child { font-size: 35px; width: 45px; text-align: center; - color: dark-light-choose(scale-color($primary, $lightness: 75%), scale-color($secondary, $lightness: 25%)); + color: dark-light-choose($primary-low-mid, $secondary-high); } } .small-action-desc.timegap { - color: dark-light-choose(scale-color($primary, $lightness: 60%), scale-color($secondary, $lightness: 40%)); + color: dark-light-choose($primary-medium, $secondary-high); } .small-action-desc { padding: 0.25em 0 0.5em 4.3em; @@ -342,7 +342,7 @@ blockquote > *:last-child { text-transform: uppercase; font-weight: bold; font-size: 0.9em; - color: dark-light-choose(scale-color($primary, $lightness: 75%), scale-color($secondary, $lightness: 25%)); + color: dark-light-choose($primary-low-mid, $secondary-high); .custom-message { text-transform: none; @@ -388,7 +388,7 @@ blockquote > *:last-child { a.mention, a.mention-group { padding: 2px 4px; - color: dark-light-choose(scale-color($primary, $lightness: 30%), scale-color($secondary, $lightness: 70%)); + color: dark-light-choose($primary-high, $secondary-low); background: $primary-low; border-radius: 8px; font-weight: bold; @@ -408,7 +408,7 @@ a.mention, a.mention-group { } .broken-image, .large-image { - color: dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)); + color: dark-light-choose($primary-low-mid, $secondary-high); border: 1px solid $primary-low; font-size: 32px; padding: 16px; diff --git a/app/assets/stylesheets/common/base/topic.scss b/app/assets/stylesheets/common/base/topic.scss index ed3784cf54..57c1cce9d2 100644 --- a/app/assets/stylesheets/common/base/topic.scss +++ b/app/assets/stylesheets/common/base/topic.scss @@ -149,7 +149,7 @@ } .expand-links { - color: dark-light-choose(scale-color($primary, $lightness: 40%), scale-color($secondary, $lightness: 60%)); + color: dark-light-choose($primary-medium, $secondary-medium); } .track-link { @@ -165,7 +165,7 @@ li { margin-bottom: 0.5em; a[href] { - color: dark-light-choose(scale-color($primary, $lightness: 40%), scale-color($secondary, $lightness: 60%)); + color: dark-light-choose($primary-medium, $secondary-medium); } .clicks { margin-left: 0.5em; diff --git a/app/assets/stylesheets/common/base/user-badges.scss b/app/assets/stylesheets/common/base/user-badges.scss index cdcfc202ad..4c275a0598 100644 --- a/app/assets/stylesheets/common/base/user-badges.scss +++ b/app/assets/stylesheets/common/base/user-badges.scss @@ -196,7 +196,7 @@ .badge-groups { margin: 20px 0; - color: dark-light-choose(scale-color($primary, $lightness: 40%), scale-color($secondary, $lightness: 60%)); + color: dark-light-choose($primary-medium, $secondary-medium); h3 { margin-bottom: 1.0em; } @@ -222,7 +222,7 @@ } .grant-info-item { margin-bottom: 1em; - color: dark-light-choose(scale-color($primary, $lightness: 40%), scale-color($secondary, $lightness: 60%)); + color: dark-light-choose($primary-medium, $secondary-medium); } .badge-title .form-horizontal .controls { diff --git a/app/assets/stylesheets/common/base/user.scss b/app/assets/stylesheets/common/base/user.scss index bf2a27c58f..f397d38508 100644 --- a/app/assets/stylesheets/common/base/user.scss +++ b/app/assets/stylesheets/common/base/user.scss @@ -198,7 +198,7 @@ } a { - color: $secondary; + color: dark-light-choose($primary-medium, $secondary-high); } .active { @@ -253,7 +253,7 @@ } .instructions { - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); + color: dark-light-choose($primary-medium, $secondary-medium); margin-top: 5px; margin-bottom: 10px; font-size: 80%; @@ -343,7 +343,7 @@ padding-top: 10px; li a { - color: dark-light-choose(scale-color($primary, $lightness: 40%), scale-color($secondary, $lightness: 40%)); + color: dark-light-choose($primary-medium, $secondary-medium); } } @@ -415,7 +415,7 @@ } .topic-info { - color: dark-light-choose(scale-color($primary, $lightness: 40%), scale-color($secondary, $lightness: 40%)); + color: dark-light-choose($primary-medium, $secondary-high); } @media all and (max-width : 600px) { @@ -435,7 +435,7 @@ .links-section { .domain { font-size: 0.714em; - color: dark-light-choose(scale-color($primary, $lightness: 40%), scale-color($secondary, $lightness: 40%)); + color: dark-light-choose($primary-medium, $secondary-high); } } @@ -462,7 +462,7 @@ } .instructions { - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); + color: dark-light-choose($primary-medium, $secondary-medium); margin-bottom: 10px; font-size: 80%; line-height: 1.4em; @@ -477,7 +477,7 @@ } .warning { - background-color: scale-color($danger, $lightness: 30%); + background-color: $danger-medium; padding: 5px 8px; color: $secondary; width: 520px; diff --git a/app/assets/stylesheets/common/components/badges.scss b/app/assets/stylesheets/common/components/badges.scss index 6c05df9ebd..c9ccb9fecf 100644 --- a/app/assets/stylesheets/common/components/badges.scss +++ b/app/assets/stylesheets/common/components/badges.scss @@ -254,7 +254,7 @@ font-size: 11px; line-height: 1; text-align: center; - background-color: dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 70%)); + background-color: dark-light-choose($primary-low-mid, $secondary-low); &[href] { color: $secondary; } @@ -266,14 +266,14 @@ // New posts &.new-posts, &.unread-posts { - background-color: dark-light-choose(scale-color($tertiary, $lightness: 50%), $tertiary); + background-color: dark-light-choose($tertiary-medium, $tertiary); color: dark-light-choose($secondary, $secondary); font-weight: dark-light-choose(normal, bold); } &.new-topic { background-color: transparent; - color: scale-color($tertiary, $lightness: 20%); + color: $tertiary-high; font-weight: normal; font-size: 0.929em; } @@ -304,7 +304,7 @@ font-size: 1em; line-height: 1; &[href] { - color: dark-light-choose(scale-color($primary, $lightness: 40%), scale-color($secondary, $lightness: 60%)); + color: dark-light-choose($primary-medium, $secondary-medium); } } diff --git a/app/assets/stylesheets/common/components/banner.scss b/app/assets/stylesheets/common/components/banner.scss index 5fe427df04..ada639bc08 100644 --- a/app/assets/stylesheets/common/components/banner.scss +++ b/app/assets/stylesheets/common/components/banner.scss @@ -5,8 +5,8 @@ #banner { padding: 10px; border-radius: 5px; - background: scale-color($tertiary, $lightness: 90%); - box-shadow: 0 1px 2px scale-color($tertiary, $lightness: 70%); + background: $tertiary-low; + box-shadow: 0 1px 2px $tertiary-medium; color: darken($tertiary, 45%); z-index: 1001; overflow: auto; @@ -18,7 +18,7 @@ .close { font-size: 1.786em; margin-top: -5px; - color: scale-color($tertiary, $lightness: 70%); + color: $tertiary-medium; padding-left: 5px; float: right; } diff --git a/app/assets/stylesheets/common/components/buttons.scss b/app/assets/stylesheets/common/components/buttons.scss index d15030ce26..e14f7bda0f 100644 --- a/app/assets/stylesheets/common/components/buttons.scss +++ b/app/assets/stylesheets/common/components/buttons.scss @@ -56,7 +56,7 @@ } &[disabled], &.disabled { background: $primary-low; - &:hover { color: dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)); } + &:hover { color: dark-light-choose($primary-low-mid, $secondary-high); } cursor: not-allowed; } @@ -74,7 +74,7 @@ .btn-primary { border: none; font-weight: normal; - color: dark-light-choose(#fff, scale-color($primary, $lightness: 60%)); + color: dark-light-choose(#fff, $primary-medium); background: $tertiary; &[href] { @@ -82,10 +82,10 @@ } &:hover, &.btn-hover { color: #fff; - background: dark-light-choose(scale-color($tertiary, $lightness: -20%), scale-color($tertiary, $lightness: -20%)); + background: dark-light-choose($tertiary, $tertiary); } &:active, &.btn-active { - @include linear-gradient(scale-color($tertiary, $lightness: -20%), scale-color($tertiary, $lightness: -10%)); + @include linear-gradient($tertiary, $tertiary); color: $secondary; } &[disabled], &.disabled { diff --git a/app/assets/stylesheets/common/components/dropdown-select-box.scss b/app/assets/stylesheets/common/components/dropdown-select-box.scss index 8691886546..2172969ff9 100644 --- a/app/assets/stylesheets/common/components/dropdown-select-box.scss +++ b/app/assets/stylesheets/common/components/dropdown-select-box.scss @@ -37,7 +37,7 @@ align-self: center; margin-right: 0; opacity: 1; - color: dark-light-choose(scale-color($primary, $lightness: 40%), scale-color($secondary, $lightness: 60%)); + color: dark-light-choose($primary-high, $secondary-medium); } } @@ -62,7 +62,7 @@ flex: 1; font-size: 0.857em; font-weight: normal; - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 40%));; + color: dark-light-choose($primary-medium, $secondary-medium);; white-space: normal; } } diff --git a/app/assets/stylesheets/common/components/hashtag.scss b/app/assets/stylesheets/common/components/hashtag.scss index f3ae1052a5..4cf0903d44 100644 --- a/app/assets/stylesheets/common/components/hashtag.scss +++ b/app/assets/stylesheets/common/components/hashtag.scss @@ -1,9 +1,9 @@ a.hashtag { - color: dark-light-choose($primary, scale-color($primary, $lightness: 70%)); + color: dark-light-choose($primary, $primary-low-mid); font-weight: bold; &:visited, &:hover { - color: dark-light-choose($primary, scale-color($primary, $lightness: 70%)); + color: dark-light-choose($primary, $primary-low-mid); } &:hover { diff --git a/app/assets/stylesheets/common/components/select-box.scss b/app/assets/stylesheets/common/components/select-box.scss index 026dd0ed97..6cbf831bad 100644 --- a/app/assets/stylesheets/common/components/select-box.scss +++ b/app/assets/stylesheets/common/components/select-box.scss @@ -231,7 +231,7 @@ &::-webkit-scrollbar-thumb { cursor: pointer; border-radius: 5px; - background: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); + background: dark-light-choose($primary-medium, $secondary-medium); } &::-webkit-scrollbar-track { diff --git a/app/assets/stylesheets/common/components/user-info.scss b/app/assets/stylesheets/common/components/user-info.scss index 8c274f7b5f..63bdc88280 100644 --- a/app/assets/stylesheets/common/components/user-info.scss +++ b/app/assets/stylesheets/common/components/user-info.scss @@ -23,17 +23,17 @@ .username a { font-weight: bold; - color: dark-light-choose(scale-color($primary, $lightness: 30%), scale-color($secondary, $lightness: 70%)); + color: dark-light-choose($primary-high, $secondary-low); } .name { margin-left: 5px; - color: dark-light-choose(scale-color($primary, $lightness: 30%), scale-color($secondary, $lightness: 70%)); + color: dark-light-choose($primary-high, $secondary-low); } .title { margin-top: 3px; - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); + color: dark-light-choose($primary-medium, $secondary-medium); } } @@ -56,7 +56,7 @@ min-height: 80px; .granted-on { - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); + color: dark-light-choose($primary-medium, $secondary-medium); } .post-link { diff --git a/app/assets/stylesheets/common/components/user-stream-item.scss b/app/assets/stylesheets/common/components/user-stream-item.scss index 42c6e1ca38..fbdcc97e66 100644 --- a/app/assets/stylesheets/common/components/user-stream-item.scss +++ b/app/assets/stylesheets/common/components/user-stream-item.scss @@ -70,7 +70,7 @@ } .edit-reason { - background-color: dark-light-choose(scale-color($highlight, $lightness: 25%), scale-color($highlight, $lightness: -50%)); + background-color: dark-light-choose($highlight-medium, scale-color($highlight, $lightness: -50%)); padding: 3px 5px 5px 5px; } @@ -100,7 +100,7 @@ // common/base/header.scss .fa, .icon { - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); + color: dark-light-choose($primary-medium, $secondary-medium); font-size: 1.714em; } } @@ -116,13 +116,13 @@ .name { display: inline-block; margin-top: 5px; - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); + color: dark-light-choose($primary-medium,$secondary-medium); } .title { display: inline-block; margin-top: 5px; - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); + color: dark-light-choose($primary-medium, $secondary-medium); } } } diff --git a/app/assets/stylesheets/common/foundation/helpers.scss b/app/assets/stylesheets/common/foundation/helpers.scss index 2b540dca04..883c8926cf 100644 --- a/app/assets/stylesheets/common/foundation/helpers.scss +++ b/app/assets/stylesheets/common/foundation/helpers.scss @@ -81,6 +81,6 @@ // Buttons // --------------------------------------------------- .disable-no-hover:hover { - background: dark-light-choose(scale-color($primary, $lightness: 90%), scale-color($secondary, $lightness: 60%));; + background: dark-light-choose($primary-low, $secondary-medium);; color: $primary; } diff --git a/app/assets/stylesheets/common/foundation/variables.scss b/app/assets/stylesheets/common/foundation/variables.scss index 08988f1a71..118725aab6 100644 --- a/app/assets/stylesheets/common/foundation/variables.scss +++ b/app/assets/stylesheets/common/foundation/variables.scss @@ -83,14 +83,25 @@ $base-font-family: Helvetica, Arial, sans-serif !default; //primary $primary-low: dark-light-diff($primary, $secondary, 90%, -65%); +$primary-low-mid: dark-light-diff($primary, $secondary, 70%, -45%); $primary-medium: dark-light-diff($primary, $secondary, 50%, -20%); +$primary-high: dark-light-diff($primary, $secondary, 30%, -10%); + +//header_primary +$header_primary-low: dark-light-diff($header_primary, $secondary, 90%, -65%); +$header_primary-medium: dark-light-diff($header_primary, $secondary, 50%, -20%); +$header_primary-high: dark-light-diff($header_primary, $secondary, 20%, 20%); + //secondary -$secondary-low: dark-light-diff($secondary, $primary, 50%, -50%); -$secondary-medium: dark-light-diff($secondary, $primary, 30%, -35%); +$secondary-low: dark-light-diff($secondary, $primary, 70%, -70%); +$secondary-medium: dark-light-diff($secondary, $primary, 50%, -50%); +$secondary-high: dark-light-diff($secondary, $primary, 30%, -35%); //tertiary $tertiary-low: dark-light-diff($tertiary, $secondary, 85%, -65%); +$tertiary-medium: dark-light-diff($tertiary, $secondary, 50%, -45%); +$tertiary-high: dark-light-diff($tertiary, $secondary, 20%, -25%); //quaternary $quaternary-low: dark-light-diff($quaternary, $secondary, 70%, -70%); @@ -104,7 +115,8 @@ $danger-low: dark-light-diff($danger, $secondary, 50%, -40%); $danger-medium: dark-light-diff($danger, $secondary, 30%, -60%); //success -$success-low: dark-light-diff($success, $secondary, 50%, -60%); +$success-low: dark-light-diff($success, $secondary, 80%, -60%); +$success-medium: dark-light-diff($success, $secondary, 50%, -40%); //love $love-low: dark-light-diff($love, $secondary, 85%, -60%); diff --git a/app/assets/stylesheets/common/topic-timeline.scss b/app/assets/stylesheets/common/topic-timeline.scss index 0aed64cad0..3f5deb9df9 100644 --- a/app/assets/stylesheets/common/topic-timeline.scss +++ b/app/assets/stylesheets/common/topic-timeline.scss @@ -48,7 +48,7 @@ bottom: 0; left: 0; right: 0; - border-top: 1px solid dark-light-choose(scale-color($primary, $lightness: 90%), scale-color($secondary, $lightness: 90%)); + border-top: 1px solid dark-light-choose($primary-low, $secondary-low); box-shadow: 0px -2px 4px -1px rgba(0,0,0,.25); padding-top: 20px; z-index: 100000; @@ -81,7 +81,7 @@ -webkit-box-orient: vertical; } .username { - color: dark-light-choose(scale-color($primary, $lightness: 30%), scale-color($secondary, $lightness: 70%)); + color: dark-light-choose($primary-high, $secondary-low); word-wrap: break-word; font-weight: bold; } @@ -201,14 +201,14 @@ .start-date { @include unselectable; - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); + color: dark-light-choose($primary-medium, $secondary-medium); } .timeline-scrollarea { margin-top: 0.5em; margin-left: 0.5em; border-left: 1px solid; - border-color: dark-light-choose(scale-color($tertiary, $lightness: 80%), scale-color($tertiary, $lightness: 20%)); + border-color: dark-light-choose($tertiary-low, $tertiary-high); position: relative; -webkit-transform: translate3d(0,0,0); } @@ -224,7 +224,7 @@ .timeline-handle { @include border-radius-all(0.8em); width: 0.35em; - background-color: dark-light-choose(scale-color($tertiary, $lightness: 80%), scale-color($tertiary, $lightness: 20%)); + background-color: dark-light-choose($tertiary-low, $tertiary-high); height: 100%; float: left; z-index: 501; @@ -235,7 +235,7 @@ } .timeline-ago { - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); + color: dark-light-choose($primary-medium, $secondary-medium); } .timeline-scroller { @@ -273,7 +273,7 @@ .now-date { @include unselectable; display: inline-block; - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); + color: dark-light-choose($primary-medium, $secondary-medium); margin-top: 0.5em; i { margin-left: 0.15em; diff --git a/app/assets/stylesheets/desktop/category-list.scss b/app/assets/stylesheets/desktop/category-list.scss index fb2de65344..b5d0a93ab0 100644 --- a/app/assets/stylesheets/desktop/category-list.scss +++ b/app/assets/stylesheets/desktop/category-list.scss @@ -27,7 +27,7 @@ .topics .badge-notification, .category .badge-notification { background-color: transparent; - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 40%)); + color: dark-light-choose($primary-medium, $secondary-high); } .topics { @@ -70,11 +70,11 @@ a.last-posted-at, a.last-posted-at:visited { font-size: 0.86em; - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 40%)); + color: dark-light-choose($primary-medium, $secondary-high); } .topic-statuses .fa { - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 40%)); + color: dark-light-choose($primary-medium, $secondary-high); } .topic-post-badges { diff --git a/app/assets/stylesheets/desktop/compose.scss b/app/assets/stylesheets/desktop/compose.scss index 88044ba265..c673cb913c 100644 --- a/app/assets/stylesheets/desktop/compose.scss +++ b/app/assets/stylesheets/desktop/compose.scss @@ -100,7 +100,7 @@ } .posts-count { - background-color: dark-light-choose(scale-color($tertiary, $lightness: -40%), scale-color($tertiary, $lightness: 40%)); + background-color: dark-light-choose($tertiary, $tertiary-medium); } ul { @@ -110,7 +110,7 @@ } .search-link { .fa, .blurb { - color: dark-light-choose(scale-color($primary, $lightness: 45%), scale-color($secondary, $lightness: 55%)); + color: dark-light-choose($primary-high, $secondary-medium); } } .badge-wrapper { @@ -155,7 +155,7 @@ right: 1px; position: absolute; i { font-size: 1.1em; } - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); + color: dark-light-choose($primary-medium, $secondary-medium); padding: 0 10px 5px 10px; } a.cancel { @@ -191,7 +191,7 @@ display: block; i { - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); + color: dark-light-choose($primary-medium, $secondary-medium); } } } @@ -353,7 +353,7 @@ } i { - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); + color: dark-light-choose($primary-medium, $secondary-medium); } } @@ -437,7 +437,7 @@ .d-editor-button-bar { top: 0; position: absolute; - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); + color: dark-light-choose($primary-medium, $secondary-medium); background-color: $secondary; z-index: 100; overflow: hidden; @@ -447,7 +447,7 @@ box-sizing: border-box; button { - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); + color: dark-light-choose($primary-high, $secondary-high); } } } diff --git a/app/assets/stylesheets/desktop/discourse.scss b/app/assets/stylesheets/desktop/discourse.scss index 268ee3beae..986ae3e4a5 100644 --- a/app/assets/stylesheets/desktop/discourse.scss +++ b/app/assets/stylesheets/desktop/discourse.scss @@ -26,7 +26,7 @@ header { display: block; width: 27px; margin: auto; - border-top: 3px double dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 50%)); + border-top: 3px double dark-light-choose($primary-low-mid, $secondary-medium); } } @@ -293,7 +293,7 @@ input { } .active { - background-color: scale-color($danger, $lightness: 30%); + background-color: $danger-medium; border-color: $danger; } } @@ -328,6 +328,8 @@ input { input, select { border-radius: 0; + background-color: $primary-low; + border-color: $primary-low; } .add-on, @@ -392,7 +394,7 @@ input { .input-append { .add-on { color: $danger; - background-color: scale-color($danger, $lightness: 30%); + background-color: $danger-medium; border-color: scale-color($danger, $lightness: -20%); } } @@ -421,7 +423,7 @@ input { .input-append { .add-on { color: $success; - background-color: scale-color($success, $lightness: 90%); + background-color: $success-low; border-color: $success; } } diff --git a/app/assets/stylesheets/desktop/group.scss b/app/assets/stylesheets/desktop/group.scss index 5bd0b3b9e6..e287be2cb0 100644 --- a/app/assets/stylesheets/desktop/group.scss +++ b/app/assets/stylesheets/desktop/group.scss @@ -3,7 +3,7 @@ float: left; a, i { - color: dark-light-choose(scale-color($primary, $lightness: 40%), scale-color($secondary, $lightness: 40%)); + color: dark-light-choose($primary-medium, $secondary-high); } .active { diff --git a/app/assets/stylesheets/desktop/header.scss b/app/assets/stylesheets/desktop/header.scss index c401a76594..9417b84809 100644 --- a/app/assets/stylesheets/desktop/header.scss +++ b/app/assets/stylesheets/desktop/header.scss @@ -31,13 +31,13 @@ and (max-width : 570px) { } .search-link .blurb { - color: dark-light-choose(scale-color($primary, $lightness: 45%), scale-color($secondary, $lightness: 55%)); + color: dark-light-choose($primary-high, $secondary-medium); display: block; word-wrap: break-word; font-size: 11px; line-height: 1.3em; .search-highlight { - color: dark-light-choose(scale-color($primary, $lightness: 25%), scale-color($secondary, $lightness: 75%)); + color: dark-light-choose($primary-high, $secondary-low); } } diff --git a/app/assets/stylesheets/desktop/latest-topic-list.scss b/app/assets/stylesheets/desktop/latest-topic-list.scss index db703a77b3..6a4337309d 100644 --- a/app/assets/stylesheets/desktop/latest-topic-list.scss +++ b/app/assets/stylesheets/desktop/latest-topic-list.scss @@ -3,7 +3,7 @@ .table-heading { padding: 12px 5px; - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 40%)); + color: dark-light-choose($primary-medium, $secondary-high); } .no-topics, .more-topics { @@ -32,10 +32,10 @@ .topic-stats { flex: 1; text-align: right; - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 40%)); + color: dark-light-choose($primary-medium, $secondary-high); } .topic-last-activity a { - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 40%)); + color: dark-light-choose($primary-medium, $secondary-high); } } diff --git a/app/assets/stylesheets/desktop/login.scss b/app/assets/stylesheets/desktop/login.scss index 800a954322..23c933180f 100644 --- a/app/assets/stylesheets/desktop/login.scss +++ b/app/assets/stylesheets/desktop/login.scss @@ -14,7 +14,7 @@ #login-form { a { - color: dark-light-choose(scale-color($primary, $lightness: 35%), scale-color($secondary, $lightness: 65%)); + color: dark-light-choose($primary-high, $secondary-low); } } @@ -53,7 +53,7 @@ .instructions { label { - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); + color: dark-light-choose($primary-medium, $secondary-medium); } } diff --git a/app/assets/stylesheets/desktop/modal.scss b/app/assets/stylesheets/desktop/modal.scss index 000e2d168b..373921ddab 100644 --- a/app/assets/stylesheets/desktop/modal.scss +++ b/app/assets/stylesheets/desktop/modal.scss @@ -62,7 +62,7 @@ .close { font-size: 1.429em; text-decoration: none; - color: dark-light-choose(scale-color($primary, $lightness: 35%), scale-color($secondary, $lightness: 65%)); + color: dark-light-choose($primary-high, $secondary-low); cursor: pointer; &:hover { color: $primary; @@ -93,7 +93,7 @@ .custom-message-length { margin: -10px 0 10px 20px; - color: dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)); + color: dark-light-choose($primary-low-mid, $secondary-high); font-size: 85%; } @@ -116,11 +116,11 @@ li { margin: 0 4px 8px 0; a { - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); + color: dark-light-choose($primary-medium, $secondary-medium); cursor: pointer; } a:hover { - color: dark-light-choose(scale-color($primary, $lightness: 40%), scale-color($secondary, $lightness: 60%)); + color: dark-light-choose($primary-medium, $secondary-medium); } } } diff --git a/app/assets/stylesheets/desktop/queued-posts.scss b/app/assets/stylesheets/desktop/queued-posts.scss index 363eca2dc8..38f48442b7 100644 --- a/app/assets/stylesheets/desktop/queued-posts.scss +++ b/app/assets/stylesheets/desktop/queued-posts.scss @@ -11,7 +11,7 @@ float: right; font-size: 0.929em; margin-top: 1px; - span {color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); } + span {color: dark-light-choose($primary-medium, $secondary-medium); } } .cooked { diff --git a/app/assets/stylesheets/desktop/topic-list.scss b/app/assets/stylesheets/desktop/topic-list.scss index 6d7094999e..b7ebb95653 100644 --- a/app/assets/stylesheets/desktop/topic-list.scss +++ b/app/assets/stylesheets/desktop/topic-list.scss @@ -34,10 +34,10 @@ // -------------------------------------------------- .topic-list-icons { - .d-icon-thumb-tack { color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); } - .d-icon-thumb-tack.unpinned { color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); } + .d-icon-thumb-tack { color: dark-light-choose($primary-medium, $secondary-medium); } + .d-icon-thumb-tack.unpinned { color: dark-light-choose($primary-medium, $secondary-medium); } a.title {color: $primary;} - .d-icon-bookmark { color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); } + .d-icon-bookmark { color: dark-light-choose($primary-medium, $secondary-medium); } } .topic-list { @@ -55,7 +55,7 @@ } } th { - button .d-icon {color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); } + button .d-icon {color: dark-light-choose($primary-medium, $secondary-medium); } } > tbody > tr { @@ -106,8 +106,8 @@ } .posters a:first-child .avatar.latest:not(.single) { - box-shadow: 0 0 3px 1px desaturate(scale-color($tertiary, $lightness: 65%), 35%); - border: 2px solid desaturate(scale-color($tertiary, $lightness: 50%), 40%); + box-shadow: 0 0 3px 1px desaturate($tertiary-medium, 35%); + border: 2px solid desaturate($tertiary-medium, 40%); position: relative; top: -2px; left: -2px; @@ -134,7 +134,7 @@ .post-actions { clear: both; width: auto; - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); + color: dark-light-choose($primary-medium, $secondary-medium); text-align: left; font-size: 12px; margin-top: 5px; @@ -143,7 +143,7 @@ } a { font-size: 11px; - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); + color: dark-light-choose($primary-medium, $secondary-medium); margin-right: 3px; line-height: 20px; } @@ -220,11 +220,11 @@ margin: 10px 0 0; /* topic status glyphs */ i { - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)) !important; + color: dark-light-choose($primary-medium, $secondary-medium) !important; font-size: 0.929em; } a.last-posted-at, a.last-posted-at:visited { - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); + color: dark-light-choose($primary-medium, $secondary-medium); font-size: 0.88em; } .badge { diff --git a/app/assets/stylesheets/desktop/topic-post.scss b/app/assets/stylesheets/desktop/topic-post.scss index cc7bc92f3b..a85d624486 100644 --- a/app/assets/stylesheets/desktop/topic-post.scss +++ b/app/assets/stylesheets/desktop/topic-post.scss @@ -31,7 +31,7 @@ h1 .topic-statuses .topic-status i { font-size: 0.929em; float: right; margin: 1px 25px 0 0; - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); + color: dark-light-choose($primary-medium, $secondary-medium); } .actions .fade-out { @@ -61,11 +61,11 @@ nav.post-controls { } .highlight-action { - color: dark-light-choose(scale-color($primary, $lightness: 60%), scale-color($secondary, $lightness: 40%)); + color: dark-light-choose($primary-medium, $secondary-high); } a, button { - color: dark-light-choose(scale-color($primary, $lightness: 75%), scale-color($secondary, $lightness: 25%)); + color: dark-light-choose($primary-low-mid, $secondary-high); .d-icon { opacity: 1.0; @@ -98,7 +98,7 @@ nav.post-controls { .show-replies { margin-left: -10px; font-size: inherit; - span.badge-posts {color: dark-light-choose(scale-color($primary, $lightness: 60%), scale-color($secondary, $lightness: 40%)); } + span.badge-posts {color: dark-light-choose($primary-medium, $secondary-high); } &:hover { background: $primary-low; span.badge-posts {color: $primary;} @@ -111,7 +111,7 @@ nav.post-controls { button.create { margin-right: 0; - color: dark-light-choose(scale-color($primary, $lightness: 20%), scale-color($secondary, $lightness: 80%)); + color: dark-light-choose($primary-high, $secondary-low); margin-left: 10px; } @@ -248,7 +248,7 @@ nav.post-controls { padding: 0; } - .post-date { color: dark-light-choose(scale-color($primary, $lightness: 60%), scale-color($secondary, $lightness: 40%)); } + .post-date { color: dark-light-choose($primary-medium, $secondary-high); } .d-icon-arrow-up, .d-icon-arrow-down { margin-left: 5px; } .reply:first-of-type .row { border-top: none; } @@ -261,10 +261,10 @@ nav.post-controls { font-size: 0.929em; a { font-weight: bold; - color: dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)); + color: dark-light-choose($primary-low-mid, $secondary-high); } } - .arrow {color: dark-light-choose(scale-color($primary, $lightness: 60%), scale-color($secondary, $lightness: 40%)); } + .arrow {color: dark-light-choose($primary-medium, $secondary-high); } } .post-action { @@ -291,7 +291,7 @@ a.star { h3 { margin-bottom: 4px; - color: dark-light-choose(scale-color($primary, $lightness: 20%), scale-color($secondary, $lightness: 80%)); + color: dark-light-choose($primary-high, $secondary-low); line-height: 23px; font-weight: normal; font-size: 1em; @@ -299,7 +299,7 @@ a.star { h4 { margin: 1px 0 2px 0; - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); + color: dark-light-choose($primary-medium, $secondary-medium); font-weight: normal; font-size: 0.857em; line-height: 15px; @@ -312,7 +312,7 @@ a.star { span.domain { font-size: 0.714em; - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); + color: dark-light-choose($primary-medium, $secondary-medium); } .avatars { @@ -352,7 +352,7 @@ a.star { line-height: 20px; } .number, i { - color: dark-light-choose(scale-color($primary, $lightness: 20%), scale-color($secondary, $lightness: 80%)); + color: dark-light-choose($primary-high, $secondary-low); font-size: 130%; } .avatar a { @@ -369,12 +369,12 @@ a.star { .participants { // PMs // .user { float: left; margin: 7px 20px 7px 0; } .user a { - color: dark-light-choose(scale-color($primary, $lightness: 30%), scale-color($secondary, $lightness: 70%)); + color: dark-light-choose($primary-high, $secondary-low); font-weight: bold; font-size: 0.929em; } .d-icon-times { - color: dark-light-choose(scale-color($primary, $lightness: 60%), scale-color($secondary, $lightness: 40%)); + color: dark-light-choose($primary-medium, $secondary-high); } } @@ -394,7 +394,7 @@ a.star { .btn { border: 0; padding: 0 23px; - color: dark-light-choose(scale-color($primary, $lightness: 60%), scale-color($secondary, $lightness: 40%)); + color: dark-light-choose($primary-medium, $secondary-high); background: blend-primary-secondary(5%); border-left: 1px solid $primary-low; border-top: 1px solid $primary-low; @@ -415,7 +415,7 @@ a.star { } .link-summary .btn { - color: dark-light-choose(scale-color($primary, $lightness: 60%), scale-color($secondary, $lightness: 40%)); + color: dark-light-choose($primary-medium, $secondary-high); background: blend-primary-secondary(5%); width: 100%; &:hover { @@ -427,7 +427,7 @@ a.star { @mixin topic-footer-buttons-text { line-height: 32px; - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); + color: dark-light-choose($primary-medium, $secondary-medium); } @mixin topic-footer-button { @@ -574,7 +574,7 @@ video { .moderator { .topic-body { - background-color: $highlight-low; + background-color: dark-light-choose($highlight-low, $highlight-medium); } } @@ -710,7 +710,7 @@ $topic-avatar-width: 45px; background-clip: padding-box; span { font-size: 0.857em; - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); + color: dark-light-choose($primary-medium, $secondary-medium); } span.title { font-weight: bold; @@ -809,7 +809,7 @@ $topic-avatar-width: 45px; color: $secondary; font-weight: normal; margin-bottom: 10px; - background: scale-color($tertiary, $lightness: 50%); + background: $tertiary-medium; &[href] { color: $secondary; @@ -817,7 +817,7 @@ $topic-avatar-width: 45px; &:hover { color: $secondary; - background: scale-color($tertiary, $lightness: 20%); + background: $tertiary-high; } &:active { @include linear-gradient(darken($tertiary, 18%), darken($tertiary, 12%)); @@ -837,7 +837,7 @@ $topic-avatar-width: 45px; article.boxed { .select-posts { button.select-post { - background-color: scale-color($tertiary, $lightness: 50%); + background-color: $tertiary-medium; color: $secondary; } } @@ -860,8 +860,8 @@ $topic-avatar-width: 45px; button { margin-left: 8px; - background-color: dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)); - border: 1px solid dark-light-choose(scale-color($primary, $lightness: 60%), scale-color($secondary, $lightness: 40%)); + background-color: dark-light-choose($primary-low-mid, $secondary-high); + border: 1px solid dark-light-choose($primary-medium, $secondary-high); color: $primary; } } @@ -905,7 +905,7 @@ a.attachment:before { float: right; font-size: 0.929em; margin-top: 1px; - a {color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); } + a {color: dark-light-choose($primary-medium, $secondary-medium); } } } @@ -922,11 +922,11 @@ span.highlighted { } .username.new-user a { - color: dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)); + color: dark-light-choose($primary-low-mid, $secondary-high); } .read-state { - color: scale-color($tertiary, $lightness: 50%); + color: $tertiary-medium; position: absolute; right: 0px; top: 13px; diff --git a/app/assets/stylesheets/desktop/topic.scss b/app/assets/stylesheets/desktop/topic.scss index 98f7ec65fd..a55c836347 100644 --- a/app/assets/stylesheets/desktop/topic.scss +++ b/app/assets/stylesheets/desktop/topic.scss @@ -45,7 +45,7 @@ } .private-message-glyph { - color: dark-light-choose(scale-color($primary, $lightness: 75%), scale-color($secondary, $lightness: 25%)); + color: dark-light-choose($primary-low-mid, $secondary-high); float: left; margin: 0 5px 0 0; } @@ -188,7 +188,7 @@ #topic-filter { - background-color: scale-color($highlight, $lightness: 25%); + background-color: $highlight-medium; padding: 8px; bottom: 0; position: fixed; diff --git a/app/assets/stylesheets/desktop/upload.scss b/app/assets/stylesheets/desktop/upload.scss index 557bdbdbdd..07f68b1598 100644 --- a/app/assets/stylesheets/desktop/upload.scss +++ b/app/assets/stylesheets/desktop/upload.scss @@ -17,7 +17,7 @@ line-height: 18px; } .description, .hint { - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); + color: dark-light-choose($primary-medium, $secondary-medium); display: block; } .hint { diff --git a/app/assets/stylesheets/desktop/user.scss b/app/assets/stylesheets/desktop/user.scss index 32124e6ca3..2e07197d0c 100644 --- a/app/assets/stylesheets/desktop/user.scss +++ b/app/assets/stylesheets/desktop/user.scss @@ -54,7 +54,7 @@ } a { - color: dark-light-choose(scale-color($primary, $lightness: 40%), scale-color($secondary, $lightness: 40%)); + color: dark-light-choose($primary-medium, $secondary-high); &.active { color: $primary; @@ -99,7 +99,7 @@ text-align: left; border-bottom: 3px solid $primary-low; padding: 0 0 10px 0; - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); + color: dark-light-choose($primary-medium, $secondary-medium); font-weight: normal; } @@ -140,10 +140,10 @@ } } - .secondary { - background: scale-color($secondary, $lightness: -5%); - border-top: 1px solid $primary-low; - border-bottom: 1px solid $primary-low; + .secondary { + background: $secondary; + border-top: 1px solid $primary-low; + border-bottom: 1px solid $primary-low; dl { padding: 8px 10px; diff --git a/app/assets/stylesheets/embed.scss b/app/assets/stylesheets/embed.scss index 65a25aa8f1..0f48544c04 100644 --- a/app/assets/stylesheets/embed.scss +++ b/app/assets/stylesheets/embed.scss @@ -87,7 +87,7 @@ article.post { } a.new-user { - color: dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)); + color: dark-light-choose($primary-low-mid, $secondary-high); } span.title { diff --git a/app/assets/stylesheets/mobile/components/user-stream-item.scss b/app/assets/stylesheets/mobile/components/user-stream-item.scss index 40e43cf7a7..3727bbd76b 100644 --- a/app/assets/stylesheets/mobile/components/user-stream-item.scss +++ b/app/assets/stylesheets/mobile/components/user-stream-item.scss @@ -17,7 +17,7 @@ .notification { &.unread { - background-color: dark-light-choose(scale-color($tertiary, $lightness: 85%), srgb-scale($tertiary, $secondary, 15%)); + background-color: dark-light-choose($tertiary-low, srgb-scale($tertiary, $secondary, 15%)); } } diff --git a/app/assets/stylesheets/mobile/compose.scss b/app/assets/stylesheets/mobile/compose.scss index 48844826c5..45664c752b 100644 --- a/app/assets/stylesheets/mobile/compose.scss +++ b/app/assets/stylesheets/mobile/compose.scss @@ -66,7 +66,7 @@ input[type=radio], input[type=checkbox] { right: 1px; position: absolute; font-size: 1.071em; - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); + color: dark-light-choose($primary-medium, $secondary-medium); } .toggle-toolbar { @@ -84,7 +84,7 @@ input[type=radio], input[type=checkbox] { max-width: 80%; white-space: nowrap; i { - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); + color: dark-light-choose($primary-medium, $secondary-medium); } } } @@ -119,7 +119,7 @@ input[type=radio], input[type=checkbox] { text-overflow: ellipsis; i { - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); + color: dark-light-choose($primary-medium, $secondary-medium); } } } @@ -176,11 +176,11 @@ input[type=radio], input[type=checkbox] { #reply-title { margin-right: 10px; &:disabled { - background-color: dark-light-choose(scale-color($primary, $lightness: 75%), scale-color($secondary, $lightness: 25%)); + background-color: dark-light-choose($primary-low-mid, $secondary-high); } } .d-editor-input:disabled { - background-color: dark-light-choose(scale-color($primary, $lightness: 75%), scale-color($secondary, $lightness: 25%)); + background-color: dark-light-choose($primary-low-mid, $secondary-high); } .d-editor-input { color: dark-light-choose(darken($primary, 40%), blend-primary-secondary(90%)); @@ -317,7 +317,7 @@ input[type=radio], input[type=checkbox] { display: block; margin: 1px 4px; position: absolute; - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); + color: dark-light-choose($primary-medium, $secondary-medium); background-color: $secondary; z-index: 100; overflow: hidden; @@ -328,7 +328,7 @@ input[type=radio], input[type=checkbox] { box-sizing: border-box; button { - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); + color: dark-light-choose($primary-medium, $secondary-medium); } button.btn.no-text { margin: 0 2px; diff --git a/app/assets/stylesheets/mobile/directory.scss b/app/assets/stylesheets/mobile/directory.scss index 4cd15c8900..4636e391eb 100644 --- a/app/assets/stylesheets/mobile/directory.scss +++ b/app/assets/stylesheets/mobile/directory.scss @@ -23,7 +23,7 @@ &.me { - background-color: $highlight-low; + background-color: dark-light-choose($highlight-low, $highlight-medium); .username a, .name, .title, .number, .time-read, .user-stat .label { color: scale-color($highlight, $lightness: -50%); diff --git a/app/assets/stylesheets/mobile/discourse.scss b/app/assets/stylesheets/mobile/discourse.scss index fce24121a2..2acd1d8412 100644 --- a/app/assets/stylesheets/mobile/discourse.scss +++ b/app/assets/stylesheets/mobile/discourse.scss @@ -63,7 +63,7 @@ h2 { .topic-status { i { - color: $secondary-medium; + color: $secondary-high; } } @@ -100,7 +100,7 @@ h2 { .fa { margin-right: 8px; - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); + color: dark-light-choose($primary-medium, $secondary-medium); } } diff --git a/app/assets/stylesheets/mobile/login.scss b/app/assets/stylesheets/mobile/login.scss index c5d7bd61ac..e642c7bcdb 100644 --- a/app/assets/stylesheets/mobile/login.scss +++ b/app/assets/stylesheets/mobile/login.scss @@ -16,7 +16,7 @@ #login-form { a { - color: dark-light-choose(scale-color($primary, $lightness: 35%), scale-color($secondary, $lightness: 65%)); + color: dark-light-choose($primary-high, $secondary-low); } label { float: left; display: block; } textarea, input, select { @@ -59,7 +59,7 @@ $input-width: 184px; tr.instructions { label { - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); + color: dark-light-choose($primary-medium, $secondary-medium); } } diff --git a/app/assets/stylesheets/mobile/modal.scss b/app/assets/stylesheets/mobile/modal.scss index 5bceb7f11d..2adf1c67cc 100644 --- a/app/assets/stylesheets/mobile/modal.scss +++ b/app/assets/stylesheets/mobile/modal.scss @@ -105,7 +105,7 @@ .custom-message-length { margin: -4px 0 10px 20px; - color: dark-light-choose(scale-color($primary, $lightness: 30%), scale-color($secondary, $lightness: 70%)); + color: dark-light-choose($primary-high, $secondary-low); font-size: 85%; } @@ -164,7 +164,7 @@ .alert { padding: 5px; &.alert-success { - background-color: scale-color($success, $lightness: 90%); + background-color: $success-low; color: $success; } } diff --git a/app/assets/stylesheets/mobile/topic-list.scss b/app/assets/stylesheets/mobile/topic-list.scss index cfe5b09e04..8b00bd6cd2 100644 --- a/app/assets/stylesheets/mobile/topic-list.scss +++ b/app/assets/stylesheets/mobile/topic-list.scss @@ -26,7 +26,7 @@ position: relative; } .nav-pills .drop { - border: 1px solid $primary-low; + border: 1px solid $primary-low; position: absolute; z-index: 1000; background-color: $secondary; @@ -82,7 +82,7 @@ th, td { padding: 7px 0; - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); + color: dark-light-choose($primary-medium, $secondary-medium); max-width: 300px; } @@ -119,7 +119,7 @@ max-width: 160px; } .num .fa, a, a:visited { - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); + color: dark-light-choose($primary-medium, $secondary-medium); } } @@ -129,7 +129,7 @@ a { // let's make all ages dim on mobile so we're not // overwhelming people with info about each topic - color: dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)) !important; + color: dark-light-choose($primary-low-mid, $secondary-high) !important; } } } @@ -142,7 +142,7 @@ td { padding: 12px 5px; - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 40%)); + color: dark-light-choose($primary-medium, $secondary-high); vertical-align: top; } @@ -212,7 +212,7 @@ tr.category-topic-link { .featured-topic { margin: 8px 0; a.last-posted-at, a.last-posted-at:visited { - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); + color: dark-light-choose($primary-medium, $secondary-medium); } } } @@ -291,7 +291,7 @@ tr.category-topic-link { figure { float: left; margin: 3px 7px 0 0; - color: dark-light-choose(scale-color($primary, $lightness: 10%), scale-color($secondary, $lightness: 90%)); + color: dark-light-choose($primary, $secondary-low); font-weight: bold; font-size: 0.857em; } @@ -359,7 +359,7 @@ tr.category-topic-link { padding: 4px 0; list-style: none; background-color: $secondary; - border: 1px solid dark-light-choose(rgba(0, 0, 0, 0.2), scale-color($primary, $lightness: -60%)); + border: 1px solid dark-light-choose(rgba(0, 0, 0, 0.2), $primary); border-radius: 5px; box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); background-clip: padding-box; @@ -379,7 +379,7 @@ tr.category-topic-link { .dropdown-menu .active > a:hover { color: $tertiary; text-decoration: none; - background-color: scale-color($tertiary, $lightness: 75%); + background-color: $tertiary-low; } .open > .dropdown-menu { display: block; diff --git a/app/assets/stylesheets/mobile/topic-post.scss b/app/assets/stylesheets/mobile/topic-post.scss index 4e35da9a5c..57d2302749 100644 --- a/app/assets/stylesheets/mobile/topic-post.scss +++ b/app/assets/stylesheets/mobile/topic-post.scss @@ -63,7 +63,7 @@ nav.post-controls { padding: 8px 10px; vertical-align: top; background: transparent; - color: dark-light-choose(scale-color($primary, $lightness: 75%), scale-color($secondary, $lightness: 25%)); + color: dark-light-choose($primary-low-mid, $secondary-high); float: left; &.hidden { display: none; @@ -91,7 +91,7 @@ nav.post-controls { /* shift post reply button to the right and make it black */ .post-controls button.create { float: right; - color: dark-light-choose(scale-color($primary, $lightness: 20%), scale-color($secondary, $lightness: 80%)); + color: dark-light-choose($primary-high, $secondary-low); } @@ -153,7 +153,7 @@ a.reply-to-tab { position: absolute; z-index: 400; right: 80px; - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); + color: dark-light-choose($primary-medium, $secondary-medium); span { display: none; } } @@ -181,7 +181,7 @@ a.star { h3 { margin-bottom: 4px; margin-top: 0; - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); + color: dark-light-choose($primary-medium, $secondary-medium); line-height: 23px; font-weight: normal; font-size: 1em; @@ -189,7 +189,7 @@ a.star { h4 { margin: 0 0 3px 0; - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); + color: dark-light-choose($primary-medium, $secondary-medium); font-weight: normal; font-size: 0.857em; line-height: 15px; @@ -246,7 +246,7 @@ a.star { line-height: 20px; } .number, i { - color: dark-light-choose(scale-color($primary, $lightness: 20%), scale-color($secondary, $lightness: 80%)); + color: dark-light-choose($primary-high, $secondary-low); font-size: 110%; } @@ -269,7 +269,7 @@ a.star { } .domain { - color: dark-light-choose(scale-color($primary, $lightness: 75%), scale-color($secondary, $lightness: 25%)); + color: dark-light-choose($primary-low-mid, $secondary-high); } .topic-links { @@ -288,7 +288,7 @@ a.star { .btn { border: 0; padding: 0 15px; - color: dark-light-choose(scale-color($primary, $lightness: 60%), scale-color($secondary, $lightness: 40%)); + color: dark-light-choose($primary-medium, $secondary-high); background: blend-primary-secondary(5%); border-left: 1px solid $primary-low; border-top: 1px solid $primary-low; @@ -301,7 +301,7 @@ a.star { } .link-summary .btn { - color: dark-light-choose(scale-color($primary, $lightness: 60%), scale-color($secondary, $lightness: 40%)); + color: dark-light-choose($primary-medium, $secondary-high); background: blend-primary-secondary(5%); width: 100%; } @@ -324,7 +324,7 @@ a.star { #topic-footer-buttons p { clear: both; /* this is to force the drop-down notification state description para below the button */ margin: 0; - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); + color: dark-light-choose($primary-medium, $secondary-medium); } #topic-footer-button { @@ -382,7 +382,7 @@ span.post-count { } .moderator .topic-body { - background-color: $highlight-low; + background-color: dark-light-choose($highlight-low, $highlight-medium); } .quote-button.visible { @@ -410,7 +410,7 @@ iframe { padding-top: 1px; } span { - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); + color: dark-light-choose($primary-medium, $secondary-medium); } span.title { color: $primary; @@ -495,7 +495,7 @@ blockquote { .posts-wrapper { position: relative; } span.highlighted { - background-color: dark-light-choose(scale-color($highlight, $lightness: 70%), $highlight); + background-color: dark-light-choose($highlight-low, $highlight); } .topic-avatar { @@ -526,7 +526,7 @@ span.highlighted { } .username.new-user a { - color: dark-light-choose(scale-color($primary, $lightness: 70%), scale-color($secondary, $lightness: 30%)); + color: dark-light-choose($primary-low-mid, $secondary-high); } .user-title { diff --git a/app/assets/stylesheets/mobile/topic.scss b/app/assets/stylesheets/mobile/topic.scss index 3cd5f0ca3a..6b292ff8fa 100644 --- a/app/assets/stylesheets/mobile/topic.scss +++ b/app/assets/stylesheets/mobile/topic.scss @@ -31,7 +31,7 @@ .private-message-glyph { display: none; } } -.private-message-glyph { color: dark-light-choose(scale-color($primary, $lightness: 75%), scale-color($secondary, $lightness: 25%)); } +.private-message-glyph { color: dark-light-choose($primary-low-mid, $secondary-high); } .private_message #topic-title .private-message-glyph { display: inline; } @@ -172,9 +172,9 @@ .topic-post:last-of-type {padding-bottom: 40px;} -.heatmap-high {color: scale-color($danger, $lightness: -25%) !important;} -.heatmap-med {color: $danger !important;} -.heatmap-low {color: scale-color($danger, $lightness: 25%) !important;} +.heatmap-high {color: $danger !important;} +.heatmap-med {color: $danger-medium !important;} +.heatmap-low {color: $danger-low !important;} sup sup, sub sup, sup sub, sub sub { top: 0; } diff --git a/app/assets/stylesheets/mobile/upload.scss b/app/assets/stylesheets/mobile/upload.scss index 00797993b0..b9fb058267 100644 --- a/app/assets/stylesheets/mobile/upload.scss +++ b/app/assets/stylesheets/mobile/upload.scss @@ -7,7 +7,7 @@ line-height: 18px; } .description { - color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); + color: dark-light-choose($primary-medium, $secondary-medium); } } From f73a3cc0d4b9d7e8c1fbf2a4309397074ce4ec02 Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Fri, 13 Oct 2017 12:17:26 -0400 Subject: [PATCH 005/174] Don't include suspended_at or suspended_till unless suspended --- app/models/user.rb | 2 +- app/serializers/admin_user_list_serializer.rb | 8 ++++++++ spec/controllers/admin/users_controller_spec.rb | 2 ++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/app/models/user.rb b/app/models/user.rb index 2429418648..c70890af63 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -655,7 +655,7 @@ class User < ActiveRecord::Base end def suspended? - suspended_till && suspended_till > DateTime.now + !!(suspended_till && suspended_till > DateTime.now) end def suspend_record diff --git a/app/serializers/admin_user_list_serializer.rb b/app/serializers/admin_user_list_serializer.rb index ba3953e5bb..b4f4d60037 100644 --- a/app/serializers/admin_user_list_serializer.rb +++ b/app/serializers/admin_user_list_serializer.rb @@ -44,6 +44,14 @@ class AdminUserListSerializer < BasicUserSerializer object.suspended? end + def include_suspended_at? + object.suspended? + end + + def include_suspended_till? + object.suspended? + end + def can_impersonate scope.can_impersonate?(object) end diff --git a/spec/controllers/admin/users_controller_spec.rb b/spec/controllers/admin/users_controller_spec.rb index f1ac0aa408..64dbb1060b 100644 --- a/spec/controllers/admin/users_controller_spec.rb +++ b/spec/controllers/admin/users_controller_spec.rb @@ -125,6 +125,7 @@ describe Admin::UsersController do it "works properly" do Fabricate(:api_key, user: user) + expect(user).not_to be_suspended put( :suspend, params: { @@ -137,6 +138,7 @@ describe Admin::UsersController do expect(response).to be_success user.reload + expect(user).to be_suspended expect(user.suspended_at).to be_present expect(user.suspended_till).to be_present expect(ApiKey.where(user_id: user.id).count).to eq(0) From 5572d1d5f73ab7fa87a0b4ee419d504a49aa33f5 Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Fri, 13 Oct 2017 15:20:42 -0400 Subject: [PATCH 006/174] Refactor user summary page to use more components --- .../components/user-summary-section.js.es6 | 3 + .../components/user-summary-topic.js.es6 | 3 + .../user-summary-topics-list.js.es6 | 13 ++ .../components/user-summary-user.js.es6 | 3 + .../discourse/controllers/user-summary.js.es6 | 8 - .../javascripts/discourse/models/user.js.es6 | 54 +++---- .../templates/components/user-stat.hbs | 6 +- .../components/user-summary-section.hbs | 2 + .../components/user-summary-topic.hbs | 9 ++ .../components/user-summary-topics-list.hbs | 16 ++ .../components/user-summary-user.hbs | 4 + .../components/user-summary-users-list.hbs | 9 ++ .../discourse/templates/user/summary.hbs | 150 +++++------------- app/assets/stylesheets/common/base/user.scss | 8 +- config/locales/client.en.yml | 8 +- test/javascripts/acceptance/user-test.js.es6 | 15 +- .../helpers/create-pretender.js.es6 | 18 ++- 17 files changed, 172 insertions(+), 157 deletions(-) create mode 100644 app/assets/javascripts/discourse/components/user-summary-section.js.es6 create mode 100644 app/assets/javascripts/discourse/components/user-summary-topic.js.es6 create mode 100644 app/assets/javascripts/discourse/components/user-summary-topics-list.js.es6 create mode 100644 app/assets/javascripts/discourse/components/user-summary-user.js.es6 create mode 100644 app/assets/javascripts/discourse/templates/components/user-summary-section.hbs create mode 100644 app/assets/javascripts/discourse/templates/components/user-summary-topic.hbs create mode 100644 app/assets/javascripts/discourse/templates/components/user-summary-topics-list.hbs create mode 100644 app/assets/javascripts/discourse/templates/components/user-summary-user.hbs create mode 100644 app/assets/javascripts/discourse/templates/components/user-summary-users-list.hbs diff --git a/app/assets/javascripts/discourse/components/user-summary-section.js.es6 b/app/assets/javascripts/discourse/components/user-summary-section.js.es6 new file mode 100644 index 0000000000..bc13ab3644 --- /dev/null +++ b/app/assets/javascripts/discourse/components/user-summary-section.js.es6 @@ -0,0 +1,3 @@ +export default Ember.Component.extend({ + classNames: ['top-sub-section'] +}); diff --git a/app/assets/javascripts/discourse/components/user-summary-topic.js.es6 b/app/assets/javascripts/discourse/components/user-summary-topic.js.es6 new file mode 100644 index 0000000000..d100e27e25 --- /dev/null +++ b/app/assets/javascripts/discourse/components/user-summary-topic.js.es6 @@ -0,0 +1,3 @@ +export default Ember.Component.extend({ + tagName: 'li' +}); diff --git a/app/assets/javascripts/discourse/components/user-summary-topics-list.js.es6 b/app/assets/javascripts/discourse/components/user-summary-topics-list.js.es6 new file mode 100644 index 0000000000..ccdf6ef0f9 --- /dev/null +++ b/app/assets/javascripts/discourse/components/user-summary-topics-list.js.es6 @@ -0,0 +1,13 @@ +import computed from 'ember-addons/ember-computed-decorators'; + +// should be kept in sync with 'UserSummary::MAX_SUMMARY_RESULTS' +const MAX_SUMMARY_RESULTS = 6; + +export default Ember.Component.extend({ + tagName: '', + + @computed('items.length') + hasMore(length) { + return length >= MAX_SUMMARY_RESULTS; + } +}); diff --git a/app/assets/javascripts/discourse/components/user-summary-user.js.es6 b/app/assets/javascripts/discourse/components/user-summary-user.js.es6 new file mode 100644 index 0000000000..d100e27e25 --- /dev/null +++ b/app/assets/javascripts/discourse/components/user-summary-user.js.es6 @@ -0,0 +1,3 @@ +export default Ember.Component.extend({ + tagName: 'li' +}); diff --git a/app/assets/javascripts/discourse/controllers/user-summary.js.es6 b/app/assets/javascripts/discourse/controllers/user-summary.js.es6 index c40014854c..bb4bec2077 100644 --- a/app/assets/javascripts/discourse/controllers/user-summary.js.es6 +++ b/app/assets/javascripts/discourse/controllers/user-summary.js.es6 @@ -1,7 +1,5 @@ import computed from 'ember-addons/ember-computed-decorators'; -// should be kept in sync with 'UserSummary::MAX_SUMMARY_RESULTS' -const MAX_SUMMARY_RESULTS = 6; // should be kept in sync with 'UserSummary::MAX_BADGES' const MAX_BADGES = 6; @@ -9,12 +7,6 @@ export default Ember.Controller.extend({ userController: Ember.inject.controller('user'), user: Ember.computed.alias('userController.model'), - @computed("model.topics.length") - moreTopics(topicsLength) { return topicsLength >= MAX_SUMMARY_RESULTS; }, - - @computed("model.replies.length") - moreReplies(repliesLength) { return repliesLength >= MAX_SUMMARY_RESULTS; }, - @computed("model.badges.length") moreBadges(badgesLength) { return badgesLength >= MAX_BADGES; }, }); diff --git a/app/assets/javascripts/discourse/models/user.js.es6 b/app/assets/javascripts/discourse/models/user.js.es6 index 10a822a4e9..b8adf073d8 100644 --- a/app/assets/javascripts/discourse/models/user.js.es6 +++ b/app/assets/javascripts/discourse/models/user.js.es6 @@ -11,7 +11,6 @@ import UserBadge from 'discourse/models/user-badge'; import UserActionStat from 'discourse/models/user-action-stat'; import UserAction from 'discourse/models/user-action'; import Group from 'discourse/models/group'; -import Topic from 'discourse/models/topic'; import { emojiUnescape } from 'discourse/lib/text'; import PreloadStore from 'preload-store'; import { defaultHomepage } from 'discourse/lib/utilities'; @@ -492,38 +491,39 @@ const User = RestModel.extend({ }, summary() { - return ajax(userPath(`${this.get("username_lower")}/summary.json`)) - .then(json => { - const summary = json["user_summary"]; - const topicMap = {}; - const badgeMap = {}; + let { store } = this; - json.topics.forEach(t => topicMap[t.id] = Topic.create(t)); - Badge.createFromJson(json).forEach(b => badgeMap[b.id] = b ); + return ajax(userPath(`${this.get("username_lower")}/summary.json`)).then(json => { + const summary = json.user_summary; + const topicMap = {}; + const badgeMap = {}; - summary.topics = summary.topic_ids.map(id => topicMap[id]); + json.topics.forEach(t => topicMap[t.id] = store.createRecord('topic', t)); + Badge.createFromJson(json).forEach(b => badgeMap[b.id] = b ); - summary.replies.forEach(r => { - r.topic = topicMap[r.topic_id]; - r.url = r.topic.urlForPostNumber(r.post_number); - r.createdAt = new Date(r.created_at); - }); + summary.topics = summary.topic_ids.map(id => topicMap[id]); - summary.links.forEach(l => { - l.topic = topicMap[l.topic_id]; - l.post_url = l.topic.urlForPostNumber(l.post_number); - }); + summary.replies.forEach(r => { + r.topic = topicMap[r.topic_id]; + r.url = r.topic.urlForPostNumber(r.post_number); + r.createdAt = new Date(r.created_at); + }); - if (summary.badges) { - summary.badges = summary.badges.map(ub => { - const badge = badgeMap[ub.badge_id]; - badge.count = ub.count; - return badge; - }); - } + summary.links.forEach(l => { + l.topic = topicMap[l.topic_id]; + l.post_url = l.topic.urlForPostNumber(l.post_number); + }); - return summary; - }); + if (summary.badges) { + summary.badges = summary.badges.map(ub => { + const badge = badgeMap[ub.badge_id]; + badge.count = ub.count; + return badge; + }); + } + + return summary; + }); }, canManageGroup(group) { diff --git a/app/assets/javascripts/discourse/templates/components/user-stat.hbs b/app/assets/javascripts/discourse/templates/components/user-stat.hbs index 72bfaedac6..d4ec14677c 100644 --- a/app/assets/javascripts/discourse/templates/components/user-stat.hbs +++ b/app/assets/javascripts/discourse/templates/components/user-stat.hbs @@ -1,5 +1,7 @@ - {{#if icon}}{{d-icon icon}}{{/if}} {{number value}} -{{{i18n label count=value}}} + + {{#if icon}}{{d-icon icon}}{{/if}} + {{{i18n label count=value}}} + diff --git a/app/assets/javascripts/discourse/templates/components/user-summary-section.hbs b/app/assets/javascripts/discourse/templates/components/user-summary-section.hbs new file mode 100644 index 0000000000..28bdf9d7b8 --- /dev/null +++ b/app/assets/javascripts/discourse/templates/components/user-summary-section.hbs @@ -0,0 +1,2 @@ +

{{i18n (concat "user.summary." title)}}

+{{yield}} diff --git a/app/assets/javascripts/discourse/templates/components/user-summary-topic.hbs b/app/assets/javascripts/discourse/templates/components/user-summary-topic.hbs new file mode 100644 index 0000000000..056b9cf088 --- /dev/null +++ b/app/assets/javascripts/discourse/templates/components/user-summary-topic.hbs @@ -0,0 +1,9 @@ + + {{format-date createdAt format="tiny" noTitle="true"}} + {{#if likes}} + · + {{d-icon 'heart'}}  + {{/if}} + +
+{{{topic.fancyTitle}}} diff --git a/app/assets/javascripts/discourse/templates/components/user-summary-topics-list.hbs b/app/assets/javascripts/discourse/templates/components/user-summary-topics-list.hbs new file mode 100644 index 0000000000..a74e5d6a23 --- /dev/null +++ b/app/assets/javascripts/discourse/templates/components/user-summary-topics-list.hbs @@ -0,0 +1,16 @@ +{{#if items}} +
    + {{#each items as |item|}} + {{yield item}} + {{/each}} +
+ {{#if hasMore}} +

+ {{#link-to (concat "userActivity." type) user class="more"}} + {{i18n (concat "user.summary.more_" type)}} + {{/link-to}} +

+ {{/if}} +{{else}} +

{{i18n (concat "user.summary.no_" type)}}

+{{/if}} diff --git a/app/assets/javascripts/discourse/templates/components/user-summary-user.hbs b/app/assets/javascripts/discourse/templates/components/user-summary-user.hbs new file mode 100644 index 0000000000..876702f195 --- /dev/null +++ b/app/assets/javascripts/discourse/templates/components/user-summary-user.hbs @@ -0,0 +1,4 @@ +{{#user-info user=user}} + {{d-icon icon}} + {{number user.count}} +{{/user-info}} diff --git a/app/assets/javascripts/discourse/templates/components/user-summary-users-list.hbs b/app/assets/javascripts/discourse/templates/components/user-summary-users-list.hbs new file mode 100644 index 0000000000..1b291b638a --- /dev/null +++ b/app/assets/javascripts/discourse/templates/components/user-summary-users-list.hbs @@ -0,0 +1,9 @@ +{{#if users}} +
    + {{#each users as |user|}} + {{yield user}} + {{/each}} +
+{{else}} +

{{i18n (concat "user.summary." none)}}

+{{/if}} diff --git a/app/assets/javascripts/discourse/templates/user/summary.hbs b/app/assets/javascripts/discourse/templates/user/summary.hbs index d28eef4a30..5ee7df597f 100644 --- a/app/assets/javascripts/discourse/templates/user/summary.hbs +++ b/app/assets/javascripts/discourse/templates/user/summary.hbs @@ -14,7 +14,7 @@
  • {{#link-to 'userActivity.likesGiven'}} - {{user-stat value=model.likes_given label="user.summary.likes_given"}} + {{user-stat value=model.likes_given icon="heart" label="user.summary.likes_given"}} {{/link-to}}
  • {{#if model.bookmark_count}} @@ -35,70 +35,36 @@ {{/link-to}}
  • - {{user-stat value=model.likes_received label="user.summary.likes_received"}} + {{user-stat value=model.likes_received icon="heart" label="user.summary.likes_received"}}
  • - {{plugin-outlet name="user-summary-stat" - connectorTagName="li" - args=(hash model=model)}} + {{plugin-outlet name="user-summary-stat" connectorTagName="li" args=(hash model=model)}}
    -
    -

    {{i18n "user.summary.top_replies"}}

    - {{#if model.replies.length}} -
      - {{#each model.replies as |reply|}} -
    • - - {{format-date reply.createdAt format="tiny" noTitle="true"}} - {{#if reply.like_count}} - · - {{d-icon 'heart'}}  - {{/if}} - -
      - {{{reply.topic.fancyTitle}}} -
    • - {{/each}} -
    - {{#if moreReplies}} -

    {{#link-to "userActivity.replies" user class="more"}}{{i18n "user.summary.more_replies"}}{{/link-to}}

    - {{/if}} - {{else}} -

    {{i18n "user.summary.no_replies"}}

    - {{/if}} -
    -
    -

    {{i18n "user.summary.top_topics"}}

    - {{#if model.topics.length}} -
      - {{#each model.topics as |topic|}} -
    • - - {{format-date topic.createdAt format="tiny" noTitle="true"}} - {{#if topic.like_count}} - · - {{d-icon 'heart'}}  - {{/if}} - -
      - {{{topic.fancyTitle}}} -
    • - {{/each}} -
    - {{#if moreTopics}} -

    {{#link-to "userActivity.topics" user class="more"}}{{i18n "user.summary.more_topics"}}{{/link-to}}

    - {{/if}} - {{else}} -

    {{i18n "user.summary.no_topics"}}

    - {{/if}} -
    + {{#user-summary-section title="top_replies" class="replies-section pull-left"}} + {{#user-summary-topics-list type="replies" items=model.replies user=user as |reply|}} + {{user-summary-topic + createdAt=reply.createdAt + topic=reply.topic + likes=reply.like_count + url=reply.url}} + {{/user-summary-topics-list}} + {{/user-summary-section}} + + {{#user-summary-section title="top_topics" class="topics-section pull-right"}} + {{#user-summary-topics-list type="topics" items=model.topics user=user as |topic|}} + {{user-summary-topic + createdAt=topic.created_at + topic=topic + likes=topic.like_count + url=topic.url}} + {{/user-summary-topics-list}} + {{/user-summary-section}}
    - - + {{/user-summary-section}} + + {{#user-summary-section title="most_replied_to_users" class="summary-user-list replied-section pull-right"}} + {{#user-summary-users-list none="no_replies" users=model.most_replied_to_users as |user|}} + {{user-summary-user user=user icon="reply" countClass="replies"}} + {{/user-summary-users-list}} + {{/user-summary-section}}
    - - + {{#user-summary-section title="most_liked_by" class="summary-user-list liked-by-section pull-left"}} + {{#user-summary-users-list none="no_likes" users=model.most_liked_by_users as |user|}} + {{user-summary-user user=user icon="heart" countClass="likes"}} + {{/user-summary-users-list}} + {{/user-summary-section}} + + {{#user-summary-section title="most_liked_users" class="summary-user-list liked-section pull-right"}} + {{#user-summary-users-list none="no_likes" users=model.most_liked_users as |user|}} + {{user-summary-user user=user icon="heart" countClass="likes"}} + {{/user-summary-users-list}} + {{/user-summary-section}}
    {{#if siteSettings.enable_badges}} diff --git a/app/assets/stylesheets/common/base/user.scss b/app/assets/stylesheets/common/base/user.scss index f397d38508..4719c6c446 100644 --- a/app/assets/stylesheets/common/base/user.scss +++ b/app/assets/stylesheets/common/base/user.scss @@ -397,6 +397,12 @@ } .label { + // TODO: Remove once all languages have been translated to remove icons from + // their user-stat labels + .fa:nth-of-type(2) { + display: none; + } + color: blend-primary-secondary(50%); } } @@ -439,7 +445,7 @@ } } -.likes-section { +.summary-user-list { li { height: 40px; } diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index eb1fadc993..548dd60280 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -895,11 +895,11 @@ en: one: "post created" other: "posts created" likes_given: - one: " given" - other: " given" + one: "given" + other: "given" likes_received: - one: " received" - other: " received" + one: "received" + other: "received" days_visited: one: "day visited" other: "days visited" diff --git a/test/javascripts/acceptance/user-test.js.es6 b/test/javascripts/acceptance/user-test.js.es6 index 22759a7ee7..261a9b6632 100644 --- a/test/javascripts/acceptance/user-test.js.es6 +++ b/test/javascripts/acceptance/user-test.js.es6 @@ -30,4 +30,17 @@ QUnit.test("Root URL - Viewing Self", assert => { assert.equal(currentPath(), 'user.userActivity.index', "it defaults to activity"); assert.ok(exists('.container.viewing-self'), "has the viewing-self class"); }); -}); \ No newline at end of file +}); + +QUnit.test("Viewing Summary", assert => { + visit("/u/eviltrout/summary"); + andThen(() => { + assert.ok(exists('.replies-section li a'), 'replies'); + assert.ok(exists('.topics-section li a'), 'topics'); + assert.ok(exists('.links-section li a'), 'links'); + assert.ok(exists('.replied-section .user-info'), 'liked by'); + assert.ok(exists('.liked-by-section .user-info'), 'liked by'); + assert.ok(exists('.liked-section .user-info'), 'liked'); + assert.ok(exists('.badges-section .badge-card'), 'badges'); + }); +}); diff --git a/test/javascripts/helpers/create-pretender.js.es6 b/test/javascripts/helpers/create-pretender.js.es6 index a466f632cc..60c5762905 100644 --- a/test/javascripts/helpers/create-pretender.js.es6 +++ b/test/javascripts/helpers/create-pretender.js.es6 @@ -81,12 +81,20 @@ export default function() { this.get('/u/eviltrout/summary.json', () => { return response({ user_summary: { - topics: [], - topic_ids: [], - replies: [], - links: [] + topic_ids: [1234], + replies: [{ topic_id: 1234 }], + links: [{ topic_id: 1234, url: 'https://eviltrout.com' }], + most_replied_to_users: [ { id: 333 } ], + most_liked_by_users: [ { id: 333 } ], + most_liked_users: [ { id: 333 } ], + badges: [ { badge_id: 444 } ] }, - topics: [], + badges: [ + { id: 444, count: 1 } + ], + topics: [ + { id: 1234, title: 'cool title', url: '/t/1234/cool-title' } + ], }); }); From 30bb3b4a86451b73d1abf86b127dbc08c33b35c9 Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Fri, 13 Oct 2017 16:29:56 -0400 Subject: [PATCH 007/174] Include category for summary topics --- app/serializers/user_summary_serializer.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/serializers/user_summary_serializer.rb b/app/serializers/user_summary_serializer.rb index dca6dc544d..76662e1ea6 100644 --- a/app/serializers/user_summary_serializer.rb +++ b/app/serializers/user_summary_serializer.rb @@ -1,7 +1,7 @@ class UserSummarySerializer < ApplicationSerializer - class TopicSerializer < ApplicationSerializer - attributes :id, :created_at, :fancy_title, :slug, :like_count + class TopicSerializer < ListableTopicSerializer + attributes :category_id end class ReplySerializer < ApplicationSerializer From 512a7239369fdeb08c439ef5504eb0d62b0eaf40 Mon Sep 17 00:00:00 2001 From: Joshua Rosenfeld Date: Fri, 13 Oct 2017 21:47:04 -0400 Subject: [PATCH 008/174] Update username rule description --- config/locales/server.en.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index faf758f5c9..c92a06e8ec 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -1749,7 +1749,7 @@ en: username: short: "must be at least %{min} characters" long: "must be no more than %{max} characters" - characters: "must only include numbers, letters and underscores" + characters: "must only include numbers, letters, hyphens, and underscores" unique: "must be unique" blank: "must be present" must_begin_with_alphanumeric_or_underscore: "must begin with a letter, a number or an underscore" From 52b33b448dad0f291aa3cf134e4cca67b2ab0071 Mon Sep 17 00:00:00 2001 From: Joshua Rosenfeld Date: Sun, 15 Oct 2017 14:36:50 -0400 Subject: [PATCH 009/174] Use simpler language --- config/locales/server.en.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index c92a06e8ec..726820abbe 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -1749,7 +1749,7 @@ en: username: short: "must be at least %{min} characters" long: "must be no more than %{max} characters" - characters: "must only include numbers, letters, hyphens, and underscores" + characters: "must only include numbers, letters, dashes, and underscores" unique: "must be unique" blank: "must be present" must_begin_with_alphanumeric_or_underscore: "must begin with a letter, a number or an underscore" From 64e5532b90fe9fc594f6d9bf424d18e0af46e01e Mon Sep 17 00:00:00 2001 From: Joshua Rosenfeld Date: Sun, 15 Oct 2017 14:45:24 -0400 Subject: [PATCH 010/174] Add div to login-required text --- app/views/static/login.html.erb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/views/static/login.html.erb b/app/views/static/login.html.erb index ed8badab6f..c08a7661b1 100644 --- a/app/views/static/login.html.erb +++ b/app/views/static/login.html.erb @@ -1,3 +1,5 @@ <% if SiteSetting.login_required %> - <%= PrettyText.cook(I18n.t('login_required.welcome_message', title: SiteSetting.title)).html_safe %> + <% end %> From 9cb088e3f6762df3369e647b1de122edd536fa30 Mon Sep 17 00:00:00 2001 From: Sam Date: Mon, 16 Oct 2017 09:34:30 +1100 Subject: [PATCH 011/174] FIX: restrict classes allowed for img tag in Markdown --- .../pretty-text/engines/discourse-markdown/emoji.js.es6 | 2 ++ .../pretty-text/engines/discourse-markdown/quotes.js.es6 | 2 ++ app/assets/javascripts/pretty-text/white-lister.js.es6 | 1 - 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/pretty-text/engines/discourse-markdown/emoji.js.es6 b/app/assets/javascripts/pretty-text/engines/discourse-markdown/emoji.js.es6 index 0472216a65..1f0acfc32c 100644 --- a/app/assets/javascripts/pretty-text/engines/discourse-markdown/emoji.js.es6 +++ b/app/assets/javascripts/pretty-text/engines/discourse-markdown/emoji.js.es6 @@ -240,4 +240,6 @@ export function setup(helper) { state, (c,s)=>applyEmoji(c,s,md.options.discourse.emojiUnicodeReplacer)) ); }); + + helper.whiteList(['img[class=emoji]']); } diff --git a/app/assets/javascripts/pretty-text/engines/discourse-markdown/quotes.js.es6 b/app/assets/javascripts/pretty-text/engines/discourse-markdown/quotes.js.es6 index 06de98068f..70ce99c6fc 100644 --- a/app/assets/javascripts/pretty-text/engines/discourse-markdown/quotes.js.es6 +++ b/app/assets/javascripts/pretty-text/engines/discourse-markdown/quotes.js.es6 @@ -130,4 +130,6 @@ export function setup(helper) { helper.registerPlugin(md=>{ md.block.bbcode.ruler.push('quotes', rule); }); + + helper.whiteList(['img[class=avatar]']); } diff --git a/app/assets/javascripts/pretty-text/white-lister.js.es6 b/app/assets/javascripts/pretty-text/white-lister.js.es6 index cc022a827f..c6e9f40f5f 100644 --- a/app/assets/javascripts/pretty-text/white-lister.js.es6 +++ b/app/assets/javascripts/pretty-text/white-lister.js.es6 @@ -156,7 +156,6 @@ const DEFAULT_LIST = [ 'iframe[marginwidth]', 'iframe[width]', 'img[alt]', - 'img[class]', 'img[height]', 'img[title]', 'img[width]', From 7af1bf32d510ef5af7345c53f03ced304e65472d Mon Sep 17 00:00:00 2001 From: Sam Date: Mon, 16 Oct 2017 10:01:17 +1100 Subject: [PATCH 012/174] UX: switch global search trigger to ctrl+alt+s Otherwise it conflicts with firefox --- app/assets/javascripts/discourse/components/d-editor.js.es6 | 2 +- .../javascripts/discourse/lib/keyboard-shortcuts.js.es6 | 4 ++-- config/locales/client.en.yml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/assets/javascripts/discourse/components/d-editor.js.es6 b/app/assets/javascripts/discourse/components/d-editor.js.es6 index d661b26a21..441bfe4e87 100644 --- a/app/assets/javascripts/discourse/components/d-editor.js.es6 +++ b/app/assets/javascripts/discourse/components/d-editor.js.es6 @@ -233,7 +233,7 @@ export default Ember.Component.extend({ const shortcuts = this.get('toolbar.shortcuts'); // for some reason I am having trouble bubbling this so hack it in - mouseTrap.bind(['ctrl+shift+s','command+shift+s'], (event) =>{ + mouseTrap.bind(['ctrl+alt+s','command+alt+s'], (event) =>{ this.appEvents.trigger('header:keyboard-trigger', {type: 'search', event}); return true; }); diff --git a/app/assets/javascripts/discourse/lib/keyboard-shortcuts.js.es6 b/app/assets/javascripts/discourse/lib/keyboard-shortcuts.js.es6 index 6ae50d0475..bf36a4a50c 100644 --- a/app/assets/javascripts/discourse/lib/keyboard-shortcuts.js.es6 +++ b/app/assets/javascripts/discourse/lib/keyboard-shortcuts.js.es6 @@ -6,8 +6,8 @@ const bindings = { '!': {postAction: 'showFlags'}, '#': {handler: 'goToPost', anonymous: true}, '/': {handler: 'toggleSearch', anonymous: true}, - 'ctrl+shift+s': {handler: 'toggleSearch', anonymous: true}, - 'command+shift+s': {handler: 'toggleSearch', anonymous: true}, + 'ctrl+alt+s': {handler: 'toggleSearch', anonymous: true}, + 'command+alt+s': {handler: 'toggleSearch', anonymous: true}, '=': {handler: 'toggleHamburgerMenu', anonymous: true}, '?': {handler: 'showHelpModal', anonymous: true}, '.': {click: '.alert.alert-info.clickable', anonymous: true}, // show incoming/updated topics diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 548dd60280..8284adbcd5 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -2383,7 +2383,7 @@ en: hamburger_menu: '= Open hamburger menu' user_profile_menu: 'p Open user menu' show_incoming_updated_topics: '. Show updated topics' - search: '/ or ctrl+shift+s Search' + search: '/ or ctrl+alt+s Search' help: '? Open keyboard help' dismiss_new_posts: 'x, r Dismiss New/Posts' dismiss_topics: 'x, t Dismiss Topics' From 229a10e142abd9df20373d3f47bce37675b3d86e Mon Sep 17 00:00:00 2001 From: Sam Date: Mon, 16 Oct 2017 10:46:01 +1100 Subject: [PATCH 013/174] Missed a whitelist, compensate for strict classes --- .../pretty-text/engines/discourse-markdown/emoji.js.es6 | 2 +- spec/models/post_analyzer_spec.rb | 4 ++++ spec/models/post_spec.rb | 4 ++++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/pretty-text/engines/discourse-markdown/emoji.js.es6 b/app/assets/javascripts/pretty-text/engines/discourse-markdown/emoji.js.es6 index 1f0acfc32c..522edbe766 100644 --- a/app/assets/javascripts/pretty-text/engines/discourse-markdown/emoji.js.es6 +++ b/app/assets/javascripts/pretty-text/engines/discourse-markdown/emoji.js.es6 @@ -241,5 +241,5 @@ export function setup(helper) { ); }); - helper.whiteList(['img[class=emoji]']); + helper.whiteList(['img[class=emoji]','img[class=emoji emoji-custom]']); } diff --git a/spec/models/post_analyzer_spec.rb b/spec/models/post_analyzer_spec.rb index fda9180071..c82341e881 100644 --- a/spec/models/post_analyzer_spec.rb +++ b/spec/models/post_analyzer_spec.rb @@ -113,21 +113,25 @@ describe PostAnalyzer do it "doesn't count avatars as images" do post_analyzer = PostAnalyzer.new(raw_post_with_avatars, default_topic_id) + PrettyText.stubs(:cook).returns(raw_post_with_avatars) expect(post_analyzer.image_count).to eq(0) end it "doesn't count favicons as images" do post_analyzer = PostAnalyzer.new(raw_post_with_favicon, default_topic_id) + PrettyText.stubs(:cook).returns(raw_post_with_favicon) expect(post_analyzer.image_count).to eq(0) end it "doesn't count thumbnails as images" do post_analyzer = PostAnalyzer.new(raw_post_with_thumbnail, default_topic_id) + PrettyText.stubs(:cook).returns(raw_post_with_thumbnail) expect(post_analyzer.image_count).to eq(0) end it "doesn't count whitelisted images" do Post.stubs(:white_listed_image_classes).returns(["classy"]) + PrettyText.stubs(:cook).returns(raw_post_with_two_classy_images) post_analyzer = PostAnalyzer.new(raw_post_with_two_classy_images, default_topic_id) expect(post_analyzer.image_count).to eq(0) end diff --git a/spec/models/post_spec.rb b/spec/models/post_spec.rb index b387c7e179..99b5e898bc 100644 --- a/spec/models/post_spec.rb +++ b/spec/models/post_spec.rb @@ -189,15 +189,19 @@ describe Post do end it "doesn't count favicons as images" do + PrettyText.stubs(:cook).returns(post_with_favicon.raw) expect(post_with_favicon.image_count).to eq(0) end it "doesn't count thumbnails as images" do + PrettyText.stubs(:cook).returns(post_with_thumbnail.raw) expect(post_with_thumbnail.image_count).to eq(0) end it "doesn't count whitelisted images" do Post.stubs(:white_listed_image_classes).returns(["classy"]) + # I dislike this, but passing in a custom whitelist is hard + PrettyText.stubs(:cook).returns(post_with_two_classy_images.raw) expect(post_with_two_classy_images.image_count).to eq(0) end From 5f76e5062dd9bb04534c7464fed29d629bbc692c Mon Sep 17 00:00:00 2001 From: Guo Xiang Tan Date: Mon, 16 Oct 2017 19:48:31 +0800 Subject: [PATCH 014/174] Pause Sidekiq when postgres failovers. --- .../connection_adapters/postgresql_fallback_adapter.rb | 6 +++++- .../connection_adapters/postgresql_fallback_adapter_spec.rb | 2 ++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/postgresql_fallback_adapter.rb b/lib/active_record/connection_adapters/postgresql_fallback_adapter.rb index 1e2e84f8c6..f151830c5f 100644 --- a/lib/active_record/connection_adapters/postgresql_fallback_adapter.rb +++ b/lib/active_record/connection_adapters/postgresql_fallback_adapter.rb @@ -32,7 +32,10 @@ class PostgreSQLFallbackHandler end def master_down=(args) - synchronize { @masters_down[namespace] = args } + synchronize do + @masters_down[namespace] = args + Sidekiq.pause! if args + end end def master_up(namespace) @@ -53,6 +56,7 @@ class PostgreSQLFallbackHandler self.master_up(key) Discourse.disable_readonly_mode(Discourse::PG_READONLY_MODE_KEY) + Sidekiq.unpause! end rescue => e logger.warn "#{log_prefix}: Connection to master PostgreSQL server failed with '#{e.message}'" diff --git a/spec/components/active_record/connection_adapters/postgresql_fallback_adapter_spec.rb b/spec/components/active_record/connection_adapters/postgresql_fallback_adapter_spec.rb index 924e44390e..0013cf2be5 100644 --- a/spec/components/active_record/connection_adapters/postgresql_fallback_adapter_spec.rb +++ b/spec/components/active_record/connection_adapters/postgresql_fallback_adapter_spec.rb @@ -71,6 +71,7 @@ describe ActiveRecord::ConnectionHandling do .to change { Discourse.readonly_mode? }.from(false).to(true) expect(postgresql_fallback_handler.master_down?).to eq(true) + expect(Sidekiq.paused?).to eq(true) with_multisite_db(multisite_db) do expect(postgresql_fallback_handler.master_down?).to eq(nil) @@ -92,6 +93,7 @@ describe ActiveRecord::ConnectionHandling do expect(Discourse.readonly_mode?).to eq(false) expect(postgresql_fallback_handler.master_down?).to eq(nil) + expect(Sidekiq.paused?).to eq(false) expect(ActiveRecord::Base.connection_pool.connections.count).to eq(0) skip("Figuring out why the following keeps failing to obtain a connection on Travis") From fb2e581b2681041264837edff6058c155eaf2b85 Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Mon, 16 Oct 2017 11:53:47 -0400 Subject: [PATCH 015/174] FIX: Headings must begin with `heading--` to avoid some griefing --- .../javascripts/pretty-text/sanitizer.js.es6 | 8 +++++++ .../pretty-text/white-lister.js.es6 | 12 +++++----- test/javascripts/lib/sanitizer-test.js.es6 | 22 ++++++++++++++----- 3 files changed, 30 insertions(+), 12 deletions(-) diff --git a/app/assets/javascripts/pretty-text/sanitizer.js.es6 b/app/assets/javascripts/pretty-text/sanitizer.js.es6 index f62df9d4bb..512e332846 100644 --- a/app/assets/javascripts/pretty-text/sanitizer.js.es6 +++ b/app/assets/javascripts/pretty-text/sanitizer.js.es6 @@ -98,6 +98,14 @@ export function sanitize(text, whiteLister) { return "-STRIP-"; } + // Heading ids must begin with `heading--` + if ( + ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'].indexOf(tag) !== -1 && + value.match(/^heading\-\-[a-zA-Z0-9\-\_]+$/) + ) { + return attr(name, value); + } + const custom = whiteLister.getCustom(); for (let i=0; i { QUnit.test("ids on headings", assert => { const pt = new PrettyText(buildOptions({ siteSettings: {} })); assert.equal(pt.sanitize("

    Test Heading

    "), "

    Test Heading

    "); - assert.equal(pt.sanitize(`

    Test Heading

    `), `

    Test Heading

    `); - assert.equal(pt.sanitize(`

    Test Heading

    `), `

    Test Heading

    `); - assert.equal(pt.sanitize(`

    Test Heading

    `), `

    Test Heading

    `); - assert.equal(pt.sanitize(`

    Test Heading

    `), `

    Test Heading

    `); - assert.equal(pt.sanitize(`
    Test Heading
    `), `
    Test Heading
    `); - assert.equal(pt.sanitize(`
    Test Heading
    `), `
    Test Heading
    `); + assert.equal(pt.sanitize(`

    Test Heading

    `), `

    Test Heading

    `); + assert.equal(pt.sanitize(`

    Test Heading

    `), `

    Test Heading

    `); + assert.equal(pt.sanitize(`

    Test Heading

    `), `

    Test Heading

    `); + assert.equal(pt.sanitize(`

    Test Heading

    `), `

    Test Heading

    `); + assert.equal(pt.sanitize(`
    Test Heading
    `), `
    Test Heading
    `); + assert.equal(pt.sanitize(`
    Test Heading
    `), `
    Test Heading
    `); +}); + +QUnit.test("poorly formed ids on headings", assert => { + let pt = new PrettyText(buildOptions({ siteSettings: {} })); + assert.equal(pt.sanitize(`

    Test Heading

    `), `

    Test Heading

    `); + assert.equal(pt.sanitize(`

    Test Heading

    `), `

    Test Heading

    `); + assert.equal(pt.sanitize(`

    Test Heading

    `), `

    Test Heading

    `); + assert.equal(pt.sanitize(`

    Test Heading

    `), `

    Test Heading

    `); + assert.equal(pt.sanitize(`

    Test Heading

    `), `

    Test Heading

    `); + assert.equal(pt.sanitize(`

    Test Heading

    `), `

    Test Heading

    `); }); QUnit.test("urlAllowed", assert => { From 80d0c6df7c2cf04548a491b54f607dd228801da0 Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Mon, 16 Oct 2017 13:18:52 -0400 Subject: [PATCH 016/174] Use a `user-stat` for the read time too --- app/assets/javascripts/discourse/templates/user/summary.hbs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/assets/javascripts/discourse/templates/user/summary.hbs b/app/assets/javascripts/discourse/templates/user/summary.hbs index 5ee7df597f..d55c4476d2 100644 --- a/app/assets/javascripts/discourse/templates/user/summary.hbs +++ b/app/assets/javascripts/discourse/templates/user/summary.hbs @@ -6,8 +6,7 @@ {{user-stat value=model.days_visited label="user.summary.days_visited"}}
  • - {{model.time_read}} - {{{i18n "user.summary.time_read"}}} + {{user-stat value=model.time_read label="user.summary.time_read"}}
  • {{user-stat value=model.posts_read_count label="user.summary.posts_read"}} From 2db66072d7f7faef8f75078e2b84ad2635855636 Mon Sep 17 00:00:00 2001 From: Neil Lalonde Date: Mon, 16 Oct 2017 13:51:35 -0400 Subject: [PATCH 017/174] SECURITY: signup without verified email using Google auth --- app/controllers/users_controller.rb | 5 +++++ app/services/user_authenticator.rb | 6 +++++- lib/auth/google_oauth2_authenticator.rb | 2 +- .../auth/google_oauth2_authenticator_spec.rb | 19 +++++++++++++++++-- spec/controllers/users_controller_spec.rb | 18 ++++++++++++++++++ 5 files changed, 46 insertions(+), 4 deletions(-) diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index ca3dba2426..346cf4d8bf 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -351,6 +351,11 @@ class UsersController < ApplicationController authentication.start + if authentication.email_valid? && !authentication.authenticated? + # posted email is different that the already validated one? + return fail_with('login.incorrect_username_email_or_password') + end + activation = UserActivator.new(user, request, session, cookies) activation.start diff --git a/app/services/user_authenticator.rb b/app/services/user_authenticator.rb index 4019fefead..cd75e93122 100644 --- a/app/services/user_authenticator.rb +++ b/app/services/user_authenticator.rb @@ -25,12 +25,16 @@ class UserAuthenticator @session = nil end - private + def email_valid? + @session && @session[:email_valid] + end def authenticated? @session && @session[:email] == @user.email && @session[:email_valid] end + private + def authenticator if authenticator_name @authenticator ||= @authenticator_finder.find_authenticator(authenticator_name) diff --git a/lib/auth/google_oauth2_authenticator.rb b/lib/auth/google_oauth2_authenticator.rb index 385aa92148..310f2ab9e9 100644 --- a/lib/auth/google_oauth2_authenticator.rb +++ b/lib/auth/google_oauth2_authenticator.rb @@ -31,7 +31,7 @@ class Auth::GoogleOAuth2Authenticator < Auth::Authenticator def after_create_account(user, auth) data = auth[:extra_data] GoogleUserInfo.create({ user_id: user.id }.merge(data)) - if auth[:email_valid].to_s == 'true' + if auth[:email_valid].to_s == 'true' && data[:email]&.downcase == user.email EmailToken.confirm(user.email_tokens.first.token) user.set_automatic_groups end diff --git a/spec/components/auth/google_oauth2_authenticator_spec.rb b/spec/components/auth/google_oauth2_authenticator_spec.rb index dc0a6d29b2..e3af7278ba 100644 --- a/spec/components/auth/google_oauth2_authenticator_spec.rb +++ b/spec/components/auth/google_oauth2_authenticator_spec.rb @@ -85,16 +85,31 @@ describe Auth::GoogleOAuth2Authenticator do context 'after_create_account' do it 'confirms email' do authenticator = Auth::GoogleOAuth2Authenticator.new - user = Fabricate(:user) + user = Fabricate(:user, email: 'realgoogleuser@gmail.com') session = { email_valid: "true", extra_data: { - google_user_id: 1 + google_user_id: 1, + email: 'realgoogleuser@gmail.com' } } authenticator.after_create_account(user, session) expect(user.email_confirmed?).to eq(true) end + + it "doesn't confirm email if it was changed" do + authenticator = Auth::GoogleOAuth2Authenticator.new + user = Fabricate(:user, email: 'changed@gmail.com') + session = { + email_valid: "true", + extra_data: { + google_user_id: 1, + email: 'realgoogleuser@gmail.com' + } + } + authenticator.after_create_account(user, session) + expect(user.email_confirmed?).to eq(false) + end end end diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb index 258d788f9b..03490e1b08 100644 --- a/spec/controllers/users_controller_spec.rb +++ b/spec/controllers/users_controller_spec.rb @@ -809,6 +809,24 @@ describe UsersController do expect(TwitterUserInfo.count).to eq(1) end end + + it "returns an error when email has been changed from the validated email address" do + auth = session[:authentication] = {} + auth[:email_valid] = 'true' + auth[:email] = 'therealone@gmail.com' + post_user + json = JSON.parse(response.body) + expect(json['success']).to eq(false) + expect(json['message']).to be_present + end + + it "will create the user successfully if email validation is required" do + auth = session[:authentication] = {} + auth[:email] = post_user_params[:email] + post_user + json = JSON.parse(response.body) + expect(json['success']).to eq(true) + end end context 'after success' do From b54eb8f53c3814e1a81f10ede10d00b2a33dd5a8 Mon Sep 17 00:00:00 2001 From: Guo Xiang Tan Date: Tue, 17 Oct 2017 12:32:41 +0800 Subject: [PATCH 018/174] FIX: Set PG `connect_timeout` to 5 seconds. * 30 seconds is alittle too long. --- app/models/global_setting.rb | 2 +- config/discourse_defaults.conf | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/app/models/global_setting.rb b/app/models/global_setting.rb index 48d6071803..33535335d7 100644 --- a/app/models/global_setting.rb +++ b/app/models/global_setting.rb @@ -92,7 +92,7 @@ class GlobalSetting def self.database_config hash = { "adapter" => "postgresql" } - %w{pool timeout socket host port username password replica_host replica_port}.each do |s| + %w{pool connect_timeout timeout socket host port username password replica_host replica_port}.each do |s| if val = self.send("db_#{s}") hash[s] = val end diff --git a/config/discourse_defaults.conf b/config/discourse_defaults.conf index 2f47f7ea21..cede9cbeae 100644 --- a/config/discourse_defaults.conf +++ b/config/discourse_defaults.conf @@ -17,9 +17,12 @@ # connection pool size, sidekiq is set to 5, allowing an extra 3 for bg threads db_pool = 8 -# database timeout in milliseconds +# ActiveRecord connection pool timeout in milliseconds db_timeout = 5000 +# Database connection timoue in seconds +db_connect_timeout = 5 + # socket file used to access db db_socket = From 6c04eb911defd5c059110f7438c3db1202442e2d Mon Sep 17 00:00:00 2001 From: Guo Xiang Tan Date: Tue, 17 Oct 2017 12:34:49 +0800 Subject: [PATCH 019/174] Fix typo. --- config/discourse_defaults.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/discourse_defaults.conf b/config/discourse_defaults.conf index cede9cbeae..2f1ba286d4 100644 --- a/config/discourse_defaults.conf +++ b/config/discourse_defaults.conf @@ -20,7 +20,7 @@ db_pool = 8 # ActiveRecord connection pool timeout in milliseconds db_timeout = 5000 -# Database connection timoue in seconds +# Database connection timeout in seconds db_connect_timeout = 5 # socket file used to access db From 646c6eb7cd781b47663d104de4f37cdd99b465bf Mon Sep 17 00:00:00 2001 From: Sam Saffron Date: Tue, 17 Oct 2017 14:17:00 +1100 Subject: [PATCH 020/174] FEATURE: add :before_post_process_cooked hook Also reduce amount of image downloading --- lib/cooked_post_processor.rb | 12 ++++++++++-- spec/components/cooked_post_processor_spec.rb | 6 +++--- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/lib/cooked_post_processor.rb b/lib/cooked_post_processor.rb index c1c51717b6..9af9220dad 100644 --- a/lib/cooked_post_processor.rb +++ b/lib/cooked_post_processor.rb @@ -29,6 +29,7 @@ class CookedPostProcessor def post_process(bypass_bump = false) DistributedMutex.synchronize("post_process_#{@post.id}") do + DiscourseEvent.trigger(:before_post_process_cooked, @doc, @post) keep_reverse_index_up_to_date post_process_images post_process_oneboxes @@ -193,7 +194,14 @@ class CookedPostProcessor return unless src.present? width, height = img["width"].to_i, img["height"].to_i - original_width, original_height = get_size(src) + upload = Upload.get_from_url(src) + + original_width, original_height = + if upload + [upload.width, upload.height] + else + get_size(src) + end # can't reach the image... if original_width.nil? || @@ -217,7 +225,7 @@ class CookedPostProcessor img["height"] = height end - if upload = Upload.get_from_url(src) + if upload upload.create_thumbnail!(width, height, crop) end diff --git a/spec/components/cooked_post_processor_spec.rb b/spec/components/cooked_post_processor_spec.rb index e56f364cc6..009a25615c 100644 --- a/spec/components/cooked_post_processor_spec.rb +++ b/spec/components/cooked_post_processor_spec.rb @@ -150,7 +150,7 @@ describe CookedPostProcessor do context "with large images" do - let(:upload) { Fabricate(:upload) } + let(:upload) { Fabricate(:upload, width: 1750, height: 2000) } let(:post) { Fabricate(:post_with_large_image) } let(:cpp) { CookedPostProcessor.new(post) } @@ -179,7 +179,7 @@ describe CookedPostProcessor do context "with large images when using subfolders" do - let(:upload) { Fabricate(:upload) } + let(:upload) { Fabricate(:upload, width: 1750, height: 2000) } let(:post) { Fabricate(:post_with_large_image_on_subfolder) } let(:cpp) { CookedPostProcessor.new(post) } let(:base_url) { "http://test.localhost/subfolder" } @@ -220,7 +220,7 @@ describe CookedPostProcessor do context "with title" do - let(:upload) { Fabricate(:upload) } + let(:upload) { Fabricate(:upload, width: 1750, height: 2000) } let(:post) { Fabricate(:post_with_large_image_and_title) } let(:cpp) { CookedPostProcessor.new(post) } From 8185b8cb06b1bec438c0cdf98dd495b87e0d3fcf Mon Sep 17 00:00:00 2001 From: Sam Saffron Date: Tue, 17 Oct 2017 16:22:38 +1100 Subject: [PATCH 021/174] FEATURE: cache https redirects per hostname If a hostname does an https redirect we cache that so next lookup does not incur it. Also, only rate limit per ip once per final destination Raise final destination protection to 1000 ip lookups an hour --- lib/final_destination.rb | 42 ++++++++++++++++++++--- spec/components/final_destination_spec.rb | 22 ++++++++++++ 2 files changed, 60 insertions(+), 4 deletions(-) diff --git a/lib/final_destination.rb b/lib/final_destination.rb index a8175135d3..dae0cea24b 100644 --- a/lib/final_destination.rb +++ b/lib/final_destination.rb @@ -1,11 +1,30 @@ -require "socket" -require "ipaddr" +require 'socket' +require 'ipaddr' require 'excon' require 'rate_limiter' # Determine the final endpoint for a Web URI, following redirects class FinalDestination + def self.clear_https_cache!(domain) + key = redis_https_key(domain) + $redis.without_namespace.del(key) + end + + def self.cache_https_domain(domain) + key = redis_https_key(domain) + $redis.without_namespace.setex(key, "1", 1.day.to_i).present? + end + + def self.is_https_domain?(domain) + key = redis_https_key(domain) + $redis.without_namespace.get(key).present? + end + + def self.redis_https_key(domain) + "HTTPS_DOMAIN_#{domain}" + end + attr_reader :status, :cookie, :status_code def initialize(url, opts = nil) @@ -31,6 +50,7 @@ class FinalDestination @status = :ready @http_verb = @force_get_hosts.any? { |host| hostname_matches?(host) } ? :get : :head @cookie = nil + @limited_ips = [] end def self.connection_timeout @@ -66,6 +86,11 @@ class FinalDestination end def resolve + if @uri && @uri.port == 80 && FinalDestination.is_https_domain?(@uri.hostname) + @uri.scheme = "https" + @uri = URI(@uri.to_s) + end + if @limit < 0 @status = :too_many_redirects return nil @@ -132,9 +157,17 @@ class FinalDestination end if location + old_port = @uri.port + location = "#{@uri.scheme}://#{@uri.host}#{location}" if location[0] == "/" @uri = URI(location) rescue nil @limit -= 1 + + # https redirect, so just cache that whole new domain is https + if old_port == 80 && @uri.port == 443 && (URI::HTTPS === @uri) + FinalDestination.cache_https_domain(@uri.hostname) + end + return resolve end @@ -191,8 +224,9 @@ class FinalDestination end # Rate limit how often this IP can be crawled - unless @opts[:skip_rate_limit] - RateLimiter.new(nil, "crawl-destination-ip:#{address_s}", 100, 1.hour).performed! + if !@opts[:skip_rate_limit] && !@limited_ips.include?(address) + @limited_ips << address + RateLimiter.new(nil, "crawl-destination-ip:#{address_s}", 1000, 1.hour).performed! end true diff --git a/spec/components/final_destination_spec.rb b/spec/components/final_destination_spec.rb index 5248299e72..6599f38d0d 100644 --- a/spec/components/final_destination_spec.rb +++ b/spec/components/final_destination_spec.rb @@ -20,6 +20,7 @@ describe FinalDestination do when 'internal-ipv6.com' then '2001:abc:de:01:3:3d0:6a65:c2bf' when 'ignore-me.com' then '53.84.143.152' when 'force.get.com' then '22.102.29.40' + when 'wikipedia.com' then '1.2.3.4' else as_ip = IPAddr.new(host) rescue nil raise "couldn't lookup #{host}" if as_ip.nil? @@ -308,6 +309,27 @@ describe FinalDestination do end end + describe "https cache" do + it 'will cache https lookups' do + + FinalDestination.clear_https_cache!("wikipedia.com") + + stub_request(:head, "http://wikipedia.com/image.png") + .to_return(status: 302, body: "", headers: { location: 'https://wikipedia.com/image.png' }) + stub_request(:head, "https://wikipedia.com/image.png") + .to_return(status: 200, body: "", headers: []) + stub_request(:get, "https://wikipedia.com/image.png").to_return(status: 200, body: "", headers: {}) + + fd('http://wikipedia.com/image.png').resolve + + stub_request(:head, "https://wikipedia.com/image2.png") + .to_return(status: 200, body: "", headers: []) + stub_request(:get, "https://wikipedia.com/image2.png").to_return(status: 200, body: "", headers: {}) + + fd('http://wikipedia.com/image2.png').resolve + end + end + describe "#escape_url" do it "correctly escapes url" do fragment_url = "https://eviltrout.com/2016/02/25/fixing-android-performance.html#discourse-comments" From 1b5ee0ae7246d59195114aa49f536a8c0f9c9bc0 Mon Sep 17 00:00:00 2001 From: Guo Xiang Tan Date: Tue, 17 Oct 2017 13:38:46 +0800 Subject: [PATCH 022/174] FIX: Couldn't boot Discourse app with a readonly postgres. --- .../postgresql_fallback_adapter.rb | 34 +++++++++--- .../postgresql_fallback_adapter_spec.rb | 52 ++++++++++++------- 2 files changed, 59 insertions(+), 27 deletions(-) diff --git a/lib/active_record/connection_adapters/postgresql_fallback_adapter.rb b/lib/active_record/connection_adapters/postgresql_fallback_adapter.rb index f151830c5f..5dc5f2278e 100644 --- a/lib/active_record/connection_adapters/postgresql_fallback_adapter.rb +++ b/lib/active_record/connection_adapters/postgresql_fallback_adapter.rb @@ -1,6 +1,7 @@ require 'active_record/connection_adapters/abstract_adapter' require 'active_record/connection_adapters/postgresql_adapter' require 'discourse' +require 'sidekiq/pausable' class PostgreSQLFallbackHandler include Singleton @@ -48,14 +49,15 @@ class PostgreSQLFallbackHandler begin logger.warn "#{log_prefix}: Checking master server..." connection = ActiveRecord::Base.postgresql_connection(config) + is_connection_active = connection.active? + connection.disconnect! - if connection.active? - connection.disconnect! - ActiveRecord::Base.clear_all_connections! + if is_connection_active logger.warn "#{log_prefix}: Master server is active. Reconnecting..." - + ActiveRecord::Base.clear_active_connections! + ActiveRecord::Base.clear_all_connections! self.master_up(key) - Discourse.disable_readonly_mode(Discourse::PG_READONLY_MODE_KEY) + disable_readonly_mode Sidekiq.unpause! end rescue => e @@ -68,10 +70,15 @@ class PostgreSQLFallbackHandler # Use for testing def setup! @masters_down = {} + disable_readonly_mode end private + def disable_readonly_mode + Discourse.disable_readonly_mode(Discourse::PG_READONLY_MODE_KEY) + end + def config ActiveRecord::Base.connection_config end @@ -100,19 +107,30 @@ module ActiveRecord config = config.symbolize_keys if fallback_handler.master_down? + Discourse.enable_readonly_mode(Discourse::PG_READONLY_MODE_KEY) fallback_handler.verify_master - connection = postgresql_connection(config.dup.merge(host: config[:replica_host], port: config[:replica_port])) + connection = postgresql_connection(config.dup.merge( + host: config[:replica_host], + port: config[:replica_port] + )) verify_replica(connection) - Discourse.enable_readonly_mode(Discourse::PG_READONLY_MODE_KEY) else begin + now = Time.zone.now connection = postgresql_connection(config) + fallback_handler.master_down = false rescue PG::ConnectionBad => e + on_boot = fallback_handler.master_down?.nil? fallback_handler.master_down = true fallback_handler.verify_master - raise e + + if on_boot + return postgresql_fallback_connection(config) + else + raise e + end end end diff --git a/spec/components/active_record/connection_adapters/postgresql_fallback_adapter_spec.rb b/spec/components/active_record/connection_adapters/postgresql_fallback_adapter_spec.rb index 0013cf2be5..c0170df7db 100644 --- a/spec/components/active_record/connection_adapters/postgresql_fallback_adapter_spec.rb +++ b/spec/components/active_record/connection_adapters/postgresql_fallback_adapter_spec.rb @@ -6,13 +6,34 @@ describe ActiveRecord::ConnectionHandling do let(:replica_port) { 6432 } let(:config) do - ActiveRecord::Base.configurations[Rails.env].merge("adapter" => "postgresql_fallback", - "replica_host" => replica_host, - "replica_port" => replica_port).symbolize_keys! + ActiveRecord::Base.configurations[Rails.env].merge( + "adapter" => "postgresql_fallback", + "replica_host" => replica_host, + "replica_port" => replica_port + ).symbolize_keys! + end + + let(:multisite_db) { "database_2" } + + let(:multisite_config) do + { + host: 'localhost1', + port: 5432, + replica_host: replica_host, + replica_port: replica_port + } end let(:postgresql_fallback_handler) { PostgreSQLFallbackHandler.instance } + before do + ['default', multisite_db].each do |db| + with_multisite_db(db) do + postgresql_fallback_handler.master_down = false + end + end + end + after do postgresql_fallback_handler.setup! end @@ -24,17 +45,6 @@ describe ActiveRecord::ConnectionHandling do end context 'when master server is down' do - let(:multisite_db) { "database_2" } - - let(:multisite_config) do - { - host: 'localhost1', - port: 5432, - replica_host: replica_host, - replica_port: replica_port - } - end - before do @replica_connection = mock('replica_connection') end @@ -59,10 +69,12 @@ describe ActiveRecord::ConnectionHandling do ActiveRecord::Base.expects(:postgresql_connection).with(configuration).raises(PG::ConnectionBad) ActiveRecord::Base.expects(:verify_replica).with(@replica_connection) - ActiveRecord::Base.expects(:postgresql_connection).with(configuration.merge(host: replica_host, port: replica_port)).returns(@replica_connection) + ActiveRecord::Base.expects(:postgresql_connection).with(configuration.merge( + host: replica_host, port: replica_port) + ).returns(@replica_connection) end - expect(postgresql_fallback_handler.master_down?).to eq(nil) + expect(postgresql_fallback_handler.master_down?).to eq(false) expect { ActiveRecord::Base.postgresql_fallback_connection(config) } .to raise_error(PG::ConnectionBad) @@ -74,7 +86,7 @@ describe ActiveRecord::ConnectionHandling do expect(Sidekiq.paused?).to eq(true) with_multisite_db(multisite_db) do - expect(postgresql_fallback_handler.master_down?).to eq(nil) + expect(postgresql_fallback_handler.master_down?).to eq(false) expect { ActiveRecord::Base.postgresql_fallback_connection(multisite_config) } .to raise_error(PG::ConnectionBad) @@ -96,7 +108,6 @@ describe ActiveRecord::ConnectionHandling do expect(Sidekiq.paused?).to eq(false) expect(ActiveRecord::Base.connection_pool.connections.count).to eq(0) - skip("Figuring out why the following keeps failing to obtain a connection on Travis") expect(ActiveRecord::Base.connection) .to be_an_instance_of(ActiveRecord::ConnectionAdapters::PostgreSQLAdapter) end @@ -106,7 +117,10 @@ describe ActiveRecord::ConnectionHandling do it 'should raise the right error' do ActiveRecord::Base.expects(:postgresql_connection).with(config).raises(PG::ConnectionBad).once - ActiveRecord::Base.expects(:postgresql_connection).with(config.dup.merge(host: replica_host, port: replica_port)).raises(PG::ConnectionBad).once + ActiveRecord::Base.expects(:postgresql_connection).with(config.dup.merge( + host: replica_host, + port: replica_port + )).raises(PG::ConnectionBad).once 2.times do expect { ActiveRecord::Base.postgresql_fallback_connection(config) } From bdd3713ca4b2ac887090f3cf5d546a623817ddcb Mon Sep 17 00:00:00 2001 From: Guo Xiang Tan Date: Tue, 17 Oct 2017 16:20:24 +0800 Subject: [PATCH 023/174] Allow smoke tests that writes data to be skipped. --- spec/phantom_js/smoke_test.js | 186 +++++++++++++++++----------------- 1 file changed, 94 insertions(+), 92 deletions(-) diff --git a/spec/phantom_js/smoke_test.js b/spec/phantom_js/smoke_test.js index 04d0984399..bf6eb4afa5 100644 --- a/spec/phantom_js/smoke_test.js +++ b/spec/phantom_js/smoke_test.js @@ -178,121 +178,123 @@ var runTests = function() { return $("#user-card .names").length; }); - exec("open login modal", function() { - $(".login-button").click(); - }); + if (!system.env["SKIP_WRITE_TESTS"]) { + exec("open login modal", function() { + $(".login-button").click(); + }); - test("login modal is open", function() { - return $(".login-modal").length; - }); + test("login modal is open", function() { + return $(".login-modal").length; + }); - exec("type in credentials & log in", function(system) { - $("#login-account-name").val(system.env['DISCOURSE_USERNAME'] || 'smoke_user').trigger("change"); - $("#login-account-password").val(system.env["DISCOURSE_PASSWORD"] || 'P4ssw0rd').trigger("change"); - $(".login-modal .btn-primary").click(); - }); + exec("type in credentials & log in", function(system) { + $("#login-account-name").val(system.env['DISCOURSE_USERNAME'] || 'smoke_user').trigger("change"); + $("#login-account-password").val(system.env["DISCOURSE_PASSWORD"] || 'P4ssw0rd').trigger("change"); + $(".login-modal .btn-primary").click(); + }); - test("is logged in", function() { - return $(".current-user").length; - }); + test("is logged in", function() { + return $(".current-user").length; + }); - exec("go home", function() { - if ($('#site-logo').length) $('#site-logo').click(); - if ($('#site-text-logo').length) $('#site-text-logo').click(); - }); + exec("go home", function() { + if ($('#site-logo').length) $('#site-logo').click(); + if ($('#site-text-logo').length) $('#site-text-logo').click(); + }); - test("it shows a topic list", function() { - return $(".topic-list").length; - }); + test("it shows a topic list", function() { + return $(".topic-list").length; + }); - test('we have a create topic button', function() { - return $("#create-topic").length; - }); + test('we have a create topic button', function() { + return $("#create-topic").length; + }); - exec("open composer", function() { - $("#create-topic").click(); - }); + exec("open composer", function() { + $("#create-topic").click(); + }); - test('the editor is visible', function() { - return $(".d-editor").length; - }); + test('the editor is visible', function() { + return $(".d-editor").length; + }); - exec("compose new topic", function() { - var date = " (" + (+new Date()) + ")", - title = "This is a new topic" + date, - post = "I can write a new topic inside the smoke test!" + date + "\n\n"; + exec("compose new topic", function() { + var date = " (" + (+new Date()) + ")", + title = "This is a new topic" + date, + post = "I can write a new topic inside the smoke test!" + date + "\n\n"; - $("#reply-title").val(title).trigger("change"); - $("#reply-control .d-editor-input").val(post).trigger("change"); - $("#reply-control .d-editor-input").focus()[0].setSelectionRange(post.length, post.length); - }); + $("#reply-title").val(title).trigger("change"); + $("#reply-control .d-editor-input").val(post).trigger("change"); + $("#reply-control .d-editor-input").focus()[0].setSelectionRange(post.length, post.length); + }); - test("updates preview", function() { - return $(".d-editor-preview p").length; - }); + test("updates preview", function() { + return $(".d-editor-preview p").length; + }); - exec("open upload modal", function() { - $(".d-editor-button-bar .upload").click(); - }); + exec("open upload modal", function() { + $(".d-editor-button-bar .upload").click(); + }); - test("upload modal is open", function() { - return $("#filename-input").length; - }); + test("upload modal is open", function() { + return $("#filename-input").length; + }); - // TODO: Looks like PhantomJS 2.0.0 has a bug with `uploadFile` - // which breaks this code. + // TODO: Looks like PhantomJS 2.0.0 has a bug with `uploadFile` + // which breaks this code. - // upload("#filename-input", "spec/fixtures/images/large & unoptimized.png"); - // test("the file is inserted into the input", function() { - // return document.getElementById('filename-input').files.length - // }); - // screenshot('/tmp/upload-modal.png'); - // - // test("upload modal is open", function() { - // return document.querySelector("#filename-input"); - // }); - // - // exec("click upload button", function() { - // $(".modal .btn-primary").click(); - // }); - // - // test("image is uploaded", function() { - // return document.querySelector(".cooked img"); - // }); + // upload("#filename-input", "spec/fixtures/images/large & unoptimized.png"); + // test("the file is inserted into the input", function() { + // return document.getElementById('filename-input').files.length + // }); + // screenshot('/tmp/upload-modal.png'); + // + // test("upload modal is open", function() { + // return document.querySelector("#filename-input"); + // }); + // + // exec("click upload button", function() { + // $(".modal .btn-primary").click(); + // }); + // + // test("image is uploaded", function() { + // return document.querySelector(".cooked img"); + // }); - exec("submit the topic", function() { - $("#reply-control .create").click(); - }); + exec("submit the topic", function() { + $("#reply-control .create").click(); + }); - test("topic is created", function() { - return $(".fancy-title").length; - }); + test("topic is created", function() { + return $(".fancy-title").length; + }); - exec("click reply button", function() { - $(".post-controls:first .create").click(); - }); + exec("click reply button", function() { + $(".post-controls:first .create").click(); + }); - test("composer is open", function() { - return $("#reply-control .d-editor-input").length; - }); + test("composer is open", function() { + return $("#reply-control .d-editor-input").length; + }); - exec("compose reply", function() { - var post = "I can even write a reply inside the smoke test ;) (" + (+new Date()) + ")"; - $("#reply-control .d-editor-input").val(post).trigger("change"); - }); + exec("compose reply", function() { + var post = "I can even write a reply inside the smoke test ;) (" + (+new Date()) + ")"; + $("#reply-control .d-editor-input").val(post).trigger("change"); + }); - test("waiting for the preview", function() { - return $(".d-editor-preview").text().trim().indexOf("I can even write") === 0; - }); + test("waiting for the preview", function() { + return $(".d-editor-preview").text().trim().indexOf("I can even write") === 0; + }); - execAsync("submit the reply", 6000, function() { - $("#reply-control .create").click(); - }); + execAsync("submit the reply", 6000, function() { + $("#reply-control .create").click(); + }); - test("reply is created", function() { - return !document.querySelector(".saving-text") - && $(".topic-post").length === 2; - }); + test("reply is created", function() { + return !document.querySelector(".saving-text") + && $(".topic-post").length === 2; + }); + } run(); }; From 62b260b3f9705e4e699887cda7b02d42e3772dca Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Tue, 17 Oct 2017 10:44:52 -0400 Subject: [PATCH 024/174] UX: Improve markup for flag modal --- .../components/flag-action-type.js.es6 | 1 + .../templates/components/flag-action-type.hbs | 27 ++++++++++++------- app/assets/stylesheets/desktop/modal.scss | 3 +-- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/app/assets/javascripts/discourse/components/flag-action-type.js.es6 b/app/assets/javascripts/discourse/components/flag-action-type.js.es6 index 0598ffa8e8..7f0ffb70ed 100644 --- a/app/assets/javascripts/discourse/components/flag-action-type.js.es6 +++ b/app/assets/javascripts/discourse/components/flag-action-type.js.es6 @@ -2,6 +2,7 @@ import { MAX_MESSAGE_LENGTH } from 'discourse/models/post-action-type'; import computed from 'ember-addons/ember-computed-decorators'; export default Ember.Component.extend({ + classNames: ['flag-action-type'], @computed('flag.name_key') customPlaceholder(nameKey) { diff --git a/app/assets/javascripts/discourse/templates/components/flag-action-type.hbs b/app/assets/javascripts/discourse/templates/components/flag-action-type.hbs index 8138a72954..c1ee046348 100644 --- a/app/assets/javascripts/discourse/templates/components/flag-action-type.hbs +++ b/app/assets/javascripts/discourse/templates/components/flag-action-type.hbs @@ -1,11 +1,17 @@ {{#if isNotifyUser}}

    {{formattedName}}

    - - {{#if showMessageInput}} - {{textarea name="message" class="flag-message" placeholder=customPlaceholder value=message}} -
    {{customMessageLength}}
    - {{/if}} +
    {{#if staffFlagsAvailable}}
    @@ -14,10 +20,13 @@ {{else}}
    {{#if showMessageInput}} {{textarea name="message" class="flag-message" placeholder=customPlaceholder value=message}} diff --git a/app/assets/stylesheets/desktop/modal.scss b/app/assets/stylesheets/desktop/modal.scss index 373921ddab..41c8edf3e4 100644 --- a/app/assets/stylesheets/desktop/modal.scss +++ b/app/assets/stylesheets/desktop/modal.scss @@ -92,14 +92,13 @@ } .custom-message-length { - margin: -10px 0 10px 20px; color: dark-light-choose($primary-low-mid, $secondary-high); font-size: 85%; } .flag-message { - margin-left: 20px; width: 95% !important; + margin: 0; } .edit-category-modal { From 77652f43877ced461775515ae96c0952f9da24e6 Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Tue, 17 Oct 2017 11:23:44 -0400 Subject: [PATCH 025/174] FIX: Use computed properties instead of manual HTML for buttons This way they receive the proper classes --- .../discourse/controllers/flag.js.es6 | 18 +++++++++++------- .../discourse/templates/modal/flag.hbs | 3 ++- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/app/assets/javascripts/discourse/controllers/flag.js.es6 b/app/assets/javascripts/discourse/controllers/flag.js.es6 index b5f823500a..947c8fe153 100644 --- a/app/assets/javascripts/discourse/controllers/flag.js.es6 +++ b/app/assets/javascripts/discourse/controllers/flag.js.es6 @@ -1,4 +1,3 @@ -import { iconHTML } from 'discourse-common/lib/icon-library'; import ModalFunctionality from 'discourse/mixins/modal-functionality'; import ActionSummary from 'discourse/models/action-summary'; import { MAX_MESSAGE_LENGTH } from 'discourse/models/post-action-type'; @@ -97,13 +96,18 @@ export default Ember.Controller.extend(ModalFunctionality, { return !flagTopic && !isCustomFlag && this.currentUser.get('staff'); }, - submitText: function(){ - if (this.get('selected.is_custom_flag')) { - return iconHTML('envelope') + (I18n.t(this.get('flagTopic') ? "flagging_topic.notify_action" : "flagging.notify_action")); - } else { - return iconHTML('flag') + (I18n.t(this.get('flagTopic') ? "flagging_topic.action" : "flagging.action")); + @computed('selected.is_custom_flag') + submitIcon(isCustomFlag) { + return isCustomFlag ? "envelope" : "flag"; + }, + + @computed('selected.is_custom_flag', 'flagTopic') + submitLabel(isCustomFlag, flagTopic) { + if (isCustomFlag) { + return flagTopic ? "flagging_topic.notify_action" : "flagging.notify_action"; } - }.property('selected.is_custom_flag'), + return flagTopic ? "flagging_topic.action" : "flagging.action"; + }, actions: { deleteSpammer() { diff --git a/app/assets/javascripts/discourse/templates/modal/flag.hbs b/app/assets/javascripts/discourse/templates/modal/flag.hbs index da2f218cbd..d24d2e0e46 100644 --- a/app/assets/javascripts/discourse/templates/modal/flag.hbs +++ b/app/assets/javascripts/discourse/templates/modal/flag.hbs @@ -18,7 +18,8 @@ action=(action "createFlag") disabled=submitDisabled title="flagging.submit_tooltip" - translatedLabel=submitText}} + icon=submitIcon + label=submitLabel}} {{#if canSendWarning}} {{d-button From 69920b7e60bee72ec0ef2c986ffa1543e12c0e82 Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Tue, 17 Oct 2017 11:37:30 -0400 Subject: [PATCH 026/174] UX: More flag modal improvements. Remove `!important` class --- .../discourse/templates/components/flag-action-type.hbs | 8 ++++---- app/assets/stylesheets/desktop/modal.scss | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/assets/javascripts/discourse/templates/components/flag-action-type.hbs b/app/assets/javascripts/discourse/templates/components/flag-action-type.hbs index c1ee046348..8802d1c9b2 100644 --- a/app/assets/javascripts/discourse/templates/components/flag-action-type.hbs +++ b/app/assets/javascripts/discourse/templates/components/flag-action-type.hbs @@ -26,11 +26,11 @@ {{#if showDescription}}
    {{{description}}}
    {{/if}} + {{#if showMessageInput}} + {{textarea name="message" class="flag-message" placeholder=customPlaceholder value=message}} +
    {{customMessageLength}}
    + {{/if}}
    - {{#if showMessageInput}} - {{textarea name="message" class="flag-message" placeholder=customPlaceholder value=message}} -
    {{customMessageLength}}
    - {{/if}} {{/if}} diff --git a/app/assets/stylesheets/desktop/modal.scss b/app/assets/stylesheets/desktop/modal.scss index 41c8edf3e4..ddede9fca8 100644 --- a/app/assets/stylesheets/desktop/modal.scss +++ b/app/assets/stylesheets/desktop/modal.scss @@ -97,7 +97,7 @@ } .flag-message { - width: 95% !important; + width: 95%; margin: 0; } From 518e101ad6a61146ed2b19c88da9c0c21472bd79 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 17 Oct 2017 13:41:52 -0400 Subject: [PATCH 027/174] single_sign_on: encode the payload with strict_encode64 which doesn't add extraneous newlines --- lib/single_sign_on.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/single_sign_on.rb b/lib/single_sign_on.rb index 7578a8aabe..3d31722293 100644 --- a/lib/single_sign_on.rb +++ b/lib/single_sign_on.rb @@ -78,7 +78,7 @@ class SingleSignOn end def payload - payload = Base64.encode64(unsigned_payload) + payload = Base64.strict_encode64(unsigned_payload) "sso=#{CGI::escape(payload)}&sig=#{sign(payload)}" end From 65dc47adb40d840ff9908e1c17c8c301c3c18883 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Hanol?= Date: Wed, 18 Oct 2017 01:50:23 +0200 Subject: [PATCH 028/174] FIX: prevent infinite 'pull_hotlinked_images' jobs when a oneboxed image has a different scheme --- lib/cooked_post_processor.rb | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/lib/cooked_post_processor.rb b/lib/cooked_post_processor.rb index 9af9220dad..5e005549ec 100644 --- a/lib/cooked_post_processor.rb +++ b/lib/cooked_post_processor.rb @@ -110,8 +110,16 @@ class CookedPostProcessor end def oneboxed_image_uploads - urls = oneboxed_images.map { |img| img["src"] } - Upload.where(origin: urls) + urls = Set.new + + oneboxed_images.each do |img| + url = img["src"].sub(/^https?:/i, "") + urls << url + urls << "http:#{url}" + urls << "https:#{url}" + end + + Upload.where(origin: urls.to_a) end def limit_size!(img) @@ -323,7 +331,8 @@ class CookedPostProcessor uploads = oneboxed_image_uploads.select(:url, :origin) oneboxed_images.each do |img| - upload = uploads.detect { |u| u.origin == img["src"] } + url = img["src"].sub(/^https?:/i, "") + upload = uploads.find { |u| u.origin.sub(/^https?:/i, "") == url } next unless upload.present? img["src"] = upload.url # make sure we grab dimensions for oneboxed images From 1dd2b510591f62e2bde599cfe4ccc49fdcf96aff Mon Sep 17 00:00:00 2001 From: Sam Date: Wed, 18 Oct 2017 12:06:47 +1100 Subject: [PATCH 029/174] remove redundent stubs --- spec/components/final_destination_spec.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/spec/components/final_destination_spec.rb b/spec/components/final_destination_spec.rb index 6599f38d0d..93b6f061d2 100644 --- a/spec/components/final_destination_spec.rb +++ b/spec/components/final_destination_spec.rb @@ -318,13 +318,11 @@ describe FinalDestination do .to_return(status: 302, body: "", headers: { location: 'https://wikipedia.com/image.png' }) stub_request(:head, "https://wikipedia.com/image.png") .to_return(status: 200, body: "", headers: []) - stub_request(:get, "https://wikipedia.com/image.png").to_return(status: 200, body: "", headers: {}) fd('http://wikipedia.com/image.png').resolve stub_request(:head, "https://wikipedia.com/image2.png") .to_return(status: 200, body: "", headers: []) - stub_request(:get, "https://wikipedia.com/image2.png").to_return(status: 200, body: "", headers: {}) fd('http://wikipedia.com/image2.png').resolve end From a4c539bade2c52782d3802ead965a06fa0b95da3 Mon Sep 17 00:00:00 2001 From: Sam Date: Wed, 18 Oct 2017 12:10:12 +1100 Subject: [PATCH 030/174] FEATURE: Allow registration of detailed request logger Detailed request loggers can be used to gather rich timing info from all requests (which in turn can be forwarded to monitoring solution) Middleware::RequestTracker.detailed_request_logger(->|env, data| do # do stuff with env and data end --- lib/method_profiler.rb | 43 +++++++++++++ lib/middleware/request_tracker.rb | 60 ++++++++++++++++--- .../middleware/request_tracker_spec.rb | 52 ++++++++++++++-- 3 files changed, 143 insertions(+), 12 deletions(-) create mode 100644 lib/method_profiler.rb diff --git a/lib/method_profiler.rb b/lib/method_profiler.rb new file mode 100644 index 0000000000..6d99b9f88e --- /dev/null +++ b/lib/method_profiler.rb @@ -0,0 +1,43 @@ +# see https://samsaffron.com/archive/2017/10/18/fastest-way-to-profile-a-method-in-ruby +class MethodProfiler + def self.patch(klass, methods, name) + patches = methods.map do |method_name| + <<~RUBY + unless defined?(#{method_name}__mp_unpatched) + alias_method :#{method_name}__mp_unpatched, :#{method_name} + def #{method_name}(*args, &blk) + unless prof = Thread.current[:_method_profiler] + return #{method_name}__mp_unpatched(*args, &blk) + end + begin + start = Process.clock_gettime(Process::CLOCK_MONOTONIC) + #{method_name}__mp_unpatched(*args, &blk) + ensure + data = (prof[:#{name}] ||= {duration: 0.0, calls: 0}) + data[:duration] += Process.clock_gettime(Process::CLOCK_MONOTONIC) - start + data[:calls] += 1 + end + end + end + RUBY + end.join("\n") + + klass.class_eval patches + end + + def self.start + Thread.current[:_method_profiler] = { + __start: Process.clock_gettime(Process::CLOCK_MONOTONIC) + } + end + + def self.stop + finish = Process.clock_gettime(Process::CLOCK_MONOTONIC) + if data = Thread.current[:_method_profiler] + Thread.current[:_method_profiler] = nil + start = data.delete(:__start) + data[:total_duration] = finish - start + end + data + end +end diff --git a/lib/middleware/request_tracker.rb b/lib/middleware/request_tracker.rb index a2dae84e97..7a57ba94de 100644 --- a/lib/middleware/request_tracker.rb +++ b/lib/middleware/request_tracker.rb @@ -1,7 +1,43 @@ +# frozen_string_literal: true + require_dependency 'middleware/anonymous_cache' class Middleware::RequestTracker + @@detailed_request_loggers = nil + + # register callbacks for detailed request loggers called on every request + # example: + # + # Middleware::RequestTracker.detailed_request_logger(->|env, data| do + # # do stuff with env and data + # end + def self.register_detailed_request_logger(callback) + + unless @patched_instrumentation + require_dependency "method_profiler" + MethodProfiler.patch(PG::Connection, [ + :exec, :async_exec, :exec_prepared, :send_query_prepared, :query + ], :sql) + + MethodProfiler.patch(Redis::Client, [ + :call, :call_pipeline + ], :redis) + @patched_instrumentation = true + end + + (@@detailed_request_loggers ||= []) << callback + end + + def self.unregister_detailed_request_logger(callback) + @@detailed_request_loggers.delete callback + + if @@detailed_request_loggers.length == 0 + @detailed_request_loggers = nil + end + + end + def initialize(app, settings = {}) @app = app end @@ -44,19 +80,17 @@ class Middleware::RequestTracker end - TRACK_VIEW = "HTTP_DISCOURSE_TRACK_VIEW".freeze - CONTENT_TYPE = "Content-Type".freeze - def self.get_data(env, result) + def self.get_data(env, result, timing) status, headers = result status = status.to_i helper = Middleware::AnonymousCache::Helper.new(env) request = Rack::Request.new(env) - env_track_view = env[TRACK_VIEW] + env_track_view = env["HTTP_DISCOURSE_TRACK_VIEW"] track_view = status == 200 - track_view &&= env_track_view != "0".freeze && env_track_view != "false".freeze - track_view &&= env_track_view || (request.get? && !request.xhr? && headers[CONTENT_TYPE] =~ /text\/html/) + track_view &&= env_track_view != "0" && env_track_view != "false" + track_view &&= env_track_view || (request.get? && !request.xhr? && headers["Content-Type"] =~ /text\/html/) track_view = !!track_view { @@ -65,22 +99,32 @@ class Middleware::RequestTracker has_auth_cookie: helper.has_auth_cookie?, is_background: request.path =~ /^\/message-bus\// || request.path == /\/topics\/timings/, is_mobile: helper.is_mobile?, - track_view: track_view + track_view: track_view, + timing: timing } + end def call(env) + MethodProfiler.start if @@detailed_request_loggers result = @app.call(env) + info = MethodProfiler.stop if @@detailed_request_loggers + result ensure # we got to skip this on error ... its just logging - data = self.class.get_data(env, result) rescue nil + data = self.class.get_data(env, result, info) rescue nil host = RailsMultisite::ConnectionManagement.host(env) if data if result && (headers = result[1]) headers["X-Discourse-TrackView"] = "1" if data[:track_view] end + + if @@detailed_request_loggers + @@detailed_request_loggers.each { |logger| logger.call(env, data) } + end + log_later(data, host) end diff --git a/spec/components/middleware/request_tracker_spec.rb b/spec/components/middleware/request_tracker_spec.rb index c8c217eb44..b61a995d22 100644 --- a/spec/components/middleware/request_tracker_spec.rb +++ b/spec/components/middleware/request_tracker_spec.rb @@ -21,7 +21,7 @@ describe Middleware::RequestTracker do def log_tracked_view(val) data = Middleware::RequestTracker.get_data(env( "HTTP_DISCOURSE_TRACK_VIEW" => val - ), ["200", { "Content-Type" => 'text/html' }]) + ), ["200", { "Content-Type" => 'text/html' }], 0.2) Middleware::RequestTracker.log_request(data) end @@ -40,19 +40,19 @@ describe Middleware::RequestTracker do data = Middleware::RequestTracker.get_data(env( "HTTP_USER_AGENT" => "AdsBot-Google (+http://www.google.com/adsbot.html)" - ), ["200", { "Content-Type" => 'text/html' }]) + ), ["200", { "Content-Type" => 'text/html' }], 0.1) Middleware::RequestTracker.log_request(data) data = Middleware::RequestTracker.get_data(env( "HTTP_DISCOURSE_TRACK_VIEW" => "1" - ), ["200", {}]) + ), ["200", {}], 0.1) Middleware::RequestTracker.log_request(data) data = Middleware::RequestTracker.get_data(env( "HTTP_USER_AGENT" => "Mozilla/5.0 (iPhone; CPU iPhone OS 8_1 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Version/8.0 Mobile/12B410 Safari/600.1.4" - ), ["200", { "Content-Type" => 'text/html' }]) + ), ["200", { "Content-Type" => 'text/html' }], 0.1) Middleware::RequestTracker.log_request(data) @@ -65,5 +65,49 @@ describe Middleware::RequestTracker do expect(ApplicationRequest.page_view_crawler.first.count).to eq(1) expect(ApplicationRequest.page_view_anon_mobile.first.count).to eq(1) end + + end + + context "callbacks" do + def app(result, sql_calls: 0, redis_calls: 0) + lambda do |env| + sql_calls.times do + User.where(id: -100).first + end + redis_calls.times do + $redis.get("x") + end + result + end + end + + let :logger do + ->(env, data) do + @env = env + @data = data + end + end + + before do + Middleware::RequestTracker.register_detailed_request_logger(logger) + end + + after do + Middleware::RequestTracker.register_detailed_request_logger(logger) + end + + it "can correctly log detailed data" do + tracker = Middleware::RequestTracker.new(app([200, {}, []], sql_calls: 2, redis_calls: 2)) + tracker.call(env) + + timing = @data[:timing] + expect(timing[:total_duration]).to be > 0 + + expect(timing[:sql][:duration]).to be > 0 + expect(timing[:sql][:calls]).to eq 2 + + expect(timing[:redis][:duration]).to be > 0 + expect(timing[:redis][:calls]).to eq 2 + end end end From 7f9b0f5e6099e0b1a3d424b8797fb8ee2cd909e7 Mon Sep 17 00:00:00 2001 From: Sam Date: Wed, 18 Oct 2017 12:20:45 +1100 Subject: [PATCH 031/174] try moving shortcut to ctrl+alt+f --- app/assets/javascripts/discourse/components/d-editor.js.es6 | 2 +- app/assets/javascripts/discourse/lib/keyboard-shortcuts.js.es6 | 3 +-- config/locales/client.en.yml | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/app/assets/javascripts/discourse/components/d-editor.js.es6 b/app/assets/javascripts/discourse/components/d-editor.js.es6 index 441bfe4e87..07cb6872a7 100644 --- a/app/assets/javascripts/discourse/components/d-editor.js.es6 +++ b/app/assets/javascripts/discourse/components/d-editor.js.es6 @@ -233,7 +233,7 @@ export default Ember.Component.extend({ const shortcuts = this.get('toolbar.shortcuts'); // for some reason I am having trouble bubbling this so hack it in - mouseTrap.bind(['ctrl+alt+s','command+alt+s'], (event) =>{ + mouseTrap.bind(['ctrl+alt+f'], (event) =>{ this.appEvents.trigger('header:keyboard-trigger', {type: 'search', event}); return true; }); diff --git a/app/assets/javascripts/discourse/lib/keyboard-shortcuts.js.es6 b/app/assets/javascripts/discourse/lib/keyboard-shortcuts.js.es6 index bf36a4a50c..b011f8bab0 100644 --- a/app/assets/javascripts/discourse/lib/keyboard-shortcuts.js.es6 +++ b/app/assets/javascripts/discourse/lib/keyboard-shortcuts.js.es6 @@ -6,8 +6,7 @@ const bindings = { '!': {postAction: 'showFlags'}, '#': {handler: 'goToPost', anonymous: true}, '/': {handler: 'toggleSearch', anonymous: true}, - 'ctrl+alt+s': {handler: 'toggleSearch', anonymous: true}, - 'command+alt+s': {handler: 'toggleSearch', anonymous: true}, + 'ctrl+alt+f': {handler: 'toggleSearch', anonymous: true}, '=': {handler: 'toggleHamburgerMenu', anonymous: true}, '?': {handler: 'showHelpModal', anonymous: true}, '.': {click: '.alert.alert-info.clickable', anonymous: true}, // show incoming/updated topics diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 8284adbcd5..db8bf8bd1b 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -2383,7 +2383,7 @@ en: hamburger_menu: '= Open hamburger menu' user_profile_menu: 'p Open user menu' show_incoming_updated_topics: '. Show updated topics' - search: '/ or ctrl+alt+s Search' + search: '/ or ctrl+alt+f Search' help: '? Open keyboard help' dismiss_new_posts: 'x, r Dismiss New/Posts' dismiss_topics: 'x, t Dismiss Topics' From de82bc5734c6e381c0e02ec45de8954707eddadd Mon Sep 17 00:00:00 2001 From: Panayotis Matsinopoulos Date: Wed, 18 Oct 2017 09:29:21 +0100 Subject: [PATCH 032/174] Correct folder for pgsql socket The phrase that asks user to create `/var/pgsql` should refer to `/var/pgsql_socket` instead. --- docs/DEVELOPMENT-OSX-NATIVE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/DEVELOPMENT-OSX-NATIVE.md b/docs/DEVELOPMENT-OSX-NATIVE.md index abb88cb2aa..a7c90019f7 100644 --- a/docs/DEVELOPMENT-OSX-NATIVE.md +++ b/docs/DEVELOPMENT-OSX-NATIVE.md @@ -114,7 +114,7 @@ unix_socket_directories = '/var/pgsql_socket' # comma-separated list of direct #and unix_socket_permissions = 0777 # begin with 0 to use octal notation ``` -Then create the '/var/pgsql/' folder and set up the appropriate permission in your bash (this requires admin access) +Then create the '/var/pgsql_socket/' folder and set up the appropriate permission in your bash (this requires admin access) ``` sudo mkdir /var/pgsql_socket sudo chmod 770 /var/pgsql_socket From f50d4478810a0d5616ba06448dcae51aaeccb192 Mon Sep 17 00:00:00 2001 From: Arpit Jalan Date: Wed, 18 Oct 2017 14:13:47 +0530 Subject: [PATCH 033/174] FIX: render secure category topics in RSS if the user can view the topics --- app/controllers/list_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/list_controller.rb b/app/controllers/list_controller.rb index 02b3cdd3e5..dd41d020e5 100644 --- a/app/controllers/list_controller.rb +++ b/app/controllers/list_controller.rb @@ -187,7 +187,7 @@ class ListController < ApplicationController @link = "#{Discourse.base_url}#{@category.url}" @atom_link = "#{Discourse.base_url}#{@category.url}.rss" @description = "#{I18n.t('topics_in_category', category: @category.name)} #{@category.description}" - @topic_list = TopicQuery.new.list_new_in_category(@category) + @topic_list = TopicQuery.new(current_user).list_new_in_category(@category) render 'list', formats: [:rss] end From c838f43a75b4b1b7f13a378722cf60f97bbd7557 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Hanol?= Date: Wed, 18 Oct 2017 23:14:13 +0200 Subject: [PATCH 034/174] let's not generate an error when logging errors... --- app/jobs/regular/pull_hotlinked_images.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/jobs/regular/pull_hotlinked_images.rb b/app/jobs/regular/pull_hotlinked_images.rb index 64455c859f..39a630635f 100644 --- a/app/jobs/regular/pull_hotlinked_images.rb +++ b/app/jobs/regular/pull_hotlinked_images.rb @@ -66,7 +66,7 @@ module Jobs if upload.persisted? downloaded_urls[src] = upload.url else - log(:info, "Failed to pull hotlinked image for post: #{post_id}: #{src} - #{upload.errors.join("\n")}") + log(:info, "Failed to pull hotlinked image for post: #{post_id}: #{src} - #{upload.errors.full_messages.join("\n")}") end else large_images << original_src From cbdfc85466e049c53c01a759f5824d6431187a50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Hanol?= Date: Wed, 18 Oct 2017 23:54:36 +0200 Subject: [PATCH 035/174] FIX: images aren't lightboxed anymore (partially reverts 646c6eb7cd781b47663d104de4f37cdd99b465bf) --- lib/cooked_post_processor.rb | 27 +++++++------------ spec/components/cooked_post_processor_spec.rb | 24 +++++------------ 2 files changed, 16 insertions(+), 35 deletions(-) diff --git a/lib/cooked_post_processor.rb b/lib/cooked_post_processor.rb index 5e005549ec..5a961eefb8 100644 --- a/lib/cooked_post_processor.rb +++ b/lib/cooked_post_processor.rb @@ -183,7 +183,7 @@ class CookedPostProcessor # we can *always* crawl our own images return unless SiteSetting.crawl_images? || Discourse.store.has_been_uploaded?(url) - @size_cache[url] ||= FastImage.size(absolute_url) + @size_cache[url] = FastImage.size(absolute_url) rescue Zlib::BufError # FastImage.size raises BufError for some gifs end @@ -199,30 +199,21 @@ class CookedPostProcessor def convert_to_link!(img) src = img["src"] - return unless src.present? + return if src.blank? width, height = img["width"].to_i, img["height"].to_i - upload = Upload.get_from_url(src) - - original_width, original_height = - if upload - [upload.width, upload.height] - else - get_size(src) - end + # TODO: even though get_size is cached, a better solution is to store + # both original and "cropped" dimensions on the uploads table + original_width, original_height = (get_size(src) || [0, 0]).map(&:to_i) # can't reach the image... - if original_width.nil? || - original_height.nil? || - original_width == 0 || - original_height == 0 + if original_width == 0 || original_height == 0 Rails.logger.info "Can't reach '#{src}' to get its dimension." return end - return if original_width.to_i <= width && original_height.to_i <= height - return if original_width.to_i <= SiteSetting.max_image_width && original_height.to_i <= SiteSetting.max_image_height - + return if original_width <= width && original_height <= height + return if original_width <= SiteSetting.max_image_width && original_height <= SiteSetting.max_image_height return if is_a_hyperlink?(img) crop = false @@ -233,7 +224,7 @@ class CookedPostProcessor img["height"] = height end - if upload + if upload = Upload.get_from_url(src) upload.create_thumbnail!(width, height, crop) end diff --git a/spec/components/cooked_post_processor_spec.rb b/spec/components/cooked_post_processor_spec.rb index 009a25615c..7247649417 100644 --- a/spec/components/cooked_post_processor_spec.rb +++ b/spec/components/cooked_post_processor_spec.rb @@ -90,9 +90,7 @@ describe CookedPostProcessor do shared_examples "leave dimensions alone" do it "doesn't use them" do - # adds the width from the image sizes provided when no dimension is provided expect(cpp.html).to match(/src="http:\/\/foo.bar\/image.png" width="" height=""/) - # adds the width from the image sizes provided expect(cpp.html).to match(/src="http:\/\/domain.com\/picture.jpg" width="50" height="42"/) expect(cpp).to be_dirty end @@ -108,10 +106,7 @@ describe CookedPostProcessor do let(:image_sizes) { { "http://foo.bar/image.png" => { "width" => 111, "height" => 222 } } } it "uses them" do - - # adds the width from the image sizes provided when no dimension is provided expect(cpp.html).to match(/src="http:\/\/foo.bar\/image.png" width="111" height="222"/) - # adds the width from the image sizes provided expect(cpp.html).to match(/src="http:\/\/domain.com\/picture.jpg" width="50" height="42"/) expect(cpp).to be_dirty end @@ -150,7 +145,7 @@ describe CookedPostProcessor do context "with large images" do - let(:upload) { Fabricate(:upload, width: 1750, height: 2000) } + let(:upload) { Fabricate(:upload) } let(:post) { Fabricate(:post_with_large_image) } let(:cpp) { CookedPostProcessor.new(post) } @@ -159,9 +154,7 @@ describe CookedPostProcessor do SiteSetting.create_thumbnails = true Upload.expects(:get_from_url).returns(upload) - FastImage.stubs(:size).returns([1750, 2000]) - - # hmmm this should be done in a cleaner way + FastImage.expects(:size).returns([1750, 2000]) OptimizedImage.expects(:resize).returns(true) FileStore::BaseStore.any_instance.expects(:get_depth_for).returns(0) @@ -179,7 +172,7 @@ describe CookedPostProcessor do context "with large images when using subfolders" do - let(:upload) { Fabricate(:upload, width: 1750, height: 2000) } + let(:upload) { Fabricate(:upload) } let(:post) { Fabricate(:post_with_large_image_on_subfolder) } let(:cpp) { CookedPostProcessor.new(post) } let(:base_url) { "http://test.localhost/subfolder" } @@ -192,9 +185,7 @@ describe CookedPostProcessor do Discourse.stubs(:base_uri).returns(base_uri) Upload.expects(:get_from_url).returns(upload) - FastImage.stubs(:size).returns([1750, 2000]) - - # hmmm this should be done in a cleaner way + FastImage.expects(:size).returns([1750, 2000]) OptimizedImage.expects(:resize).returns(true) FileStore::BaseStore.any_instance.expects(:get_depth_for).returns(0) @@ -220,7 +211,7 @@ describe CookedPostProcessor do context "with title" do - let(:upload) { Fabricate(:upload, width: 1750, height: 2000) } + let(:upload) { Fabricate(:upload) } let(:post) { Fabricate(:post_with_large_image_and_title) } let(:cpp) { CookedPostProcessor.new(post) } @@ -229,10 +220,9 @@ describe CookedPostProcessor do SiteSetting.create_thumbnails = true Upload.expects(:get_from_url).returns(upload) - FastImage.stubs(:size).returns([1750, 2000]) - - # hmmm this should be done in a cleaner way + FastImage.expects(:size).returns([1750, 2000]) OptimizedImage.expects(:resize).returns(true) + FileStore::BaseStore.any_instance.expects(:get_depth_for).returns(0) end From 814c7ab50378109626726a80689c5bf1c6d5ca7a Mon Sep 17 00:00:00 2001 From: Guo Xiang Tan Date: Thu, 19 Oct 2017 12:25:50 +0800 Subject: [PATCH 036/174] Skip randomly failing tests first. --- .../connection_adapters/postgresql_fallback_adapter_spec.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/spec/components/active_record/connection_adapters/postgresql_fallback_adapter_spec.rb b/spec/components/active_record/connection_adapters/postgresql_fallback_adapter_spec.rb index c0170df7db..54a51d40f2 100644 --- a/spec/components/active_record/connection_adapters/postgresql_fallback_adapter_spec.rb +++ b/spec/components/active_record/connection_adapters/postgresql_fallback_adapter_spec.rb @@ -108,6 +108,7 @@ describe ActiveRecord::ConnectionHandling do expect(Sidekiq.paused?).to eq(false) expect(ActiveRecord::Base.connection_pool.connections.count).to eq(0) + skip("Need to figure out why we keep running out of connections") expect(ActiveRecord::Base.connection) .to be_an_instance_of(ActiveRecord::ConnectionAdapters::PostgreSQLAdapter) end From 79de10b212f1f3d63d39d52a2f330eb957e3d943 Mon Sep 17 00:00:00 2001 From: Guo Xiang Tan Date: Fri, 6 Oct 2017 15:56:58 +0800 Subject: [PATCH 037/174] FEATURE: Allow users to disable new PMs. https://meta.discourse.org/t/is-it-possible-to-disable-private-messaging-for-a-specific-user/46391 --- .../controllers/preferences/interface.js.es6 | 3 ++- .../javascripts/discourse/models/user.js.es6 | 3 ++- .../templates/preferences/interface.hbs | 10 ++++++++ app/models/user_option.rb | 13 ++++++---- app/serializers/user_option_serializer.rb | 3 ++- app/services/user_updater.rb | 3 ++- config/locales/client.en.yml | 1 + ..._allow_private_messages_to_user_options.rb | 5 ++++ lib/guardian.rb | 9 +++++-- lib/post_creator.rb | 14 +++++++++-- lib/topic_creator.rb | 2 +- spec/components/guardian_spec.rb | 21 ++++++++++++++++ spec/components/post_creator_spec.rb | 24 +++++++++++++++++++ spec/services/user_updater_spec.rb | 6 +++-- 14 files changed, 101 insertions(+), 16 deletions(-) create mode 100644 db/migrate/20171006030028_add_allow_private_messages_to_user_options.rb diff --git a/app/assets/javascripts/discourse/controllers/preferences/interface.js.es6 b/app/assets/javascripts/discourse/controllers/preferences/interface.js.es6 index d49a6ffd6c..6a7094886b 100644 --- a/app/assets/javascripts/discourse/controllers/preferences/interface.js.es6 +++ b/app/assets/javascripts/discourse/controllers/preferences/interface.js.es6 @@ -13,7 +13,8 @@ export default Ember.Controller.extend(PreferencesTabController, { 'dynamic_favicon', 'enable_quoting', 'disable_jump_reply', - 'automatically_unpin_topics' + 'automatically_unpin_topics', + 'allow_private_messages', ]; if (makeDefault) { diff --git a/app/assets/javascripts/discourse/models/user.js.es6 b/app/assets/javascripts/discourse/models/user.js.es6 index b8adf073d8..3296c5fbe7 100644 --- a/app/assets/javascripts/discourse/models/user.js.es6 +++ b/app/assets/javascripts/discourse/models/user.js.es6 @@ -247,7 +247,8 @@ const User = RestModel.extend({ 'notification_level_when_replying', 'like_notification_frequency', 'include_tl0_in_digests', - 'theme_key' + 'theme_key', + 'allow_private_messages', ]; if (fields) { diff --git a/app/assets/javascripts/discourse/templates/preferences/interface.hbs b/app/assets/javascripts/discourse/templates/preferences/interface.hbs index 99ad7891fe..4096ef0ad6 100644 --- a/app/assets/javascripts/discourse/templates/preferences/interface.hbs +++ b/app/assets/javascripts/discourse/templates/preferences/interface.hbs @@ -10,6 +10,16 @@ {{/if}} +
    + + +
    + {{preference-checkbox + labelKey="user.allow_private_messages" + checked=model.user_option.allow_private_messages}} +
    +
    + {{#if siteSettings.allow_user_locale}}
    diff --git a/app/models/user_option.rb b/app/models/user_option.rb index 6ce2f12ef5..17eed6baa7 100644 --- a/app/models/user_option.rb +++ b/app/models/user_option.rb @@ -59,11 +59,6 @@ class UserOption < ActiveRecord::Base super end - def update_tracked_topics - return unless saved_change_to_auto_track_topics_after_msecs? - TrackedTopicsUpdater.new(id, auto_track_topics_after_msecs).call - end - def redirected_to_top_yet? last_redirected_to_top_at.present? end @@ -133,6 +128,13 @@ class UserOption < ActiveRecord::Base times.max end + private + + def update_tracked_topics + return unless saved_change_to_auto_track_topics_after_msecs? + TrackedTopicsUpdater.new(id, auto_track_topics_after_msecs).call + end + end # == Schema Information @@ -162,6 +164,7 @@ end # notification_level_when_replying :integer # theme_key :string # theme_key_seq :integer default(0), not null +# allow_private_messages :boolean default(TRUE), not null # # Indexes # diff --git a/app/serializers/user_option_serializer.rb b/app/serializers/user_option_serializer.rb index b8253b40d7..844f6c06b5 100644 --- a/app/serializers/user_option_serializer.rb +++ b/app/serializers/user_option_serializer.rb @@ -20,7 +20,8 @@ class UserOptionSerializer < ApplicationSerializer :like_notification_frequency, :include_tl0_in_digests, :theme_key, - :theme_key_seq + :theme_key_seq, + :allow_private_messages, def auto_track_topics_after_msecs object.auto_track_topics_after_msecs || SiteSetting.default_other_auto_track_topics_after_msecs diff --git a/app/services/user_updater.rb b/app/services/user_updater.rb index 8b36ce9425..d7f764e7b0 100644 --- a/app/services/user_updater.rb +++ b/app/services/user_updater.rb @@ -34,7 +34,8 @@ class UserUpdater :email_in_reply_to, :like_notification_frequency, :include_tl0_in_digests, - :theme_key + :theme_key, + :allow_private_messages, ] def initialize(actor, user) diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index db8bf8bd1b..5c78efd4f0 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -595,6 +595,7 @@ en: disable_jump_reply: "Don't jump to my post after I reply" dynamic_favicon: "Show new / updated topic count on browser icon" theme_default_on_all_devices: "Make this my default theme on all my devices" + allow_private_messages: "Allow other users to send me private messages" external_links_in_new_tab: "Open all external links in a new tab" enable_quoting: "Enable quote reply for highlighted text" change: "change" diff --git a/db/migrate/20171006030028_add_allow_private_messages_to_user_options.rb b/db/migrate/20171006030028_add_allow_private_messages_to_user_options.rb new file mode 100644 index 0000000000..c74f68fff8 --- /dev/null +++ b/db/migrate/20171006030028_add_allow_private_messages_to_user_options.rb @@ -0,0 +1,5 @@ +class AddAllowPrivateMessagesToUserOptions < ActiveRecord::Migration[5.1] + def change + add_column :user_options, :allow_private_messages, :boolean, default: true, null: false + end +end diff --git a/lib/guardian.rb b/lib/guardian.rb index 2bc13f9e68..00bad7075c 100644 --- a/lib/guardian.rb +++ b/lib/guardian.rb @@ -286,17 +286,22 @@ class Guardian end def can_send_private_message?(target) - (target.is_a?(Group) || target.is_a?(User)) && + is_user = target.is_a?(User) + is_group = target.is_a?(Group) + + (is_group || is_user) && # User is authenticated authenticated? && # Have to be a basic level at least, or are contacting moderators (@user.has_trust_level?(SiteSetting.min_trust_to_send_messages) || (target.is_a?(User) && target.moderator?) || (target.name == Group[:moderators].name)) && + # User disabled private message + (is_staff? || is_group || target.user_option.allow_private_messages) && # PMs are enabled (is_staff? || SiteSetting.enable_private_messages) && # Can't send PMs to suspended users - (is_staff? || target.is_a?(Group) || !target.suspended?) && + (is_staff? || is_group || !target.suspended?) && # Blocked users can only send PM to staff (!is_blocked? || target.staff?) end diff --git a/lib/post_creator.rb b/lib/post_creator.rb index dc28b66bce..be0e6a8026 100644 --- a/lib/post_creator.rb +++ b/lib/post_creator.rb @@ -109,10 +109,20 @@ class PostCreator # Make sure none of the users have muted the creator users = User.where(username: names).pluck(:id, :username).to_h - MutedUser.where(user_id: users.keys, muted_user_id: @user.id).pluck(:user_id).each do |m| + User + .joins("LEFT JOIN user_options ON user_options.user_id = users.id") + .joins("LEFT JOIN muted_users ON muted_users.muted_user_id = #{@user.id.to_i}") + .where("user_options.user_id IS NOT NULL") + .where(" + (user_options.user_id IN (:user_ids) AND NOT user_options.allow_private_messages) OR + muted_users.user_id IN (:user_ids) + ", user_ids: users.keys) + .pluck(:id).each do |m| + errors[:base] << I18n.t(:not_accepting_pms, username: users[m]) - return false end + + return false if errors[:base].present? end if new_topic? diff --git a/lib/topic_creator.rb b/lib/topic_creator.rb index 36c20247c0..69ca76d73c 100644 --- a/lib/topic_creator.rb +++ b/lib/topic_creator.rb @@ -190,7 +190,7 @@ class TopicCreator names = usernames.split(',').flatten len = 0 - User.where(username: names).each do |user| + User.includes(:user_option).where(username: names).find_each do |user| check_can_send_permission!(topic, user) @added_users << user topic.topic_allowed_users.build(user_id: user.id) diff --git a/spec/components/guardian_spec.rb b/spec/components/guardian_spec.rb index c2674e38ea..0449684131 100644 --- a/spec/components/guardian_spec.rb +++ b/spec/components/guardian_spec.rb @@ -233,6 +233,27 @@ describe Guardian do end end end + + context 'target user has private message disabled' do + before do + another_user.user_option.update!(allow_private_messages: false) + end + + context 'for a normal user' do + it 'should return false' do + expect(Guardian.new(user).can_send_private_message?(another_user)).to eq(false) + end + end + + context 'for a staff user' do + it 'should return true' do + [admin, moderator].each do |staff_user| + expect(Guardian.new(staff_user).can_send_private_message?(another_user)) + .to eq(true) + end + end + end + end end describe 'can_reply_as_new_topic' do diff --git a/spec/components/post_creator_spec.rb b/spec/components/post_creator_spec.rb index a2e4908dbd..f3d2e8350a 100644 --- a/spec/components/post_creator_spec.rb +++ b/spec/components/post_creator_spec.rb @@ -954,6 +954,30 @@ describe PostCreator do end end + context 'private message to a user that has disabled private messages' do + let(:another_user) { Fabricate(:user) } + + before do + another_user.user_option.update!(allow_private_messages: false) + end + + it 'should not be valid' do + post_creator = PostCreator.new( + user, + title: 'this message is to someone who muted me!', + raw: "you will have to see this even if you muted me!", + archetype: Archetype.private_message, + target_usernames: "#{another_user.username}" + ) + + expect(post_creator).to_not be_valid + + expect(post_creator.errors.full_messages).to include(I18n.t( + "not_accepting_pms", username: another_user.username + )) + end + end + context "private message to a muted user" do let(:muted_me) { Fabricate(:evil_trout) } diff --git a/spec/services/user_updater_spec.rb b/spec/services/user_updater_spec.rb index 91fde1605a..b7c595c7be 100644 --- a/spec/services/user_updater_spec.rb +++ b/spec/services/user_updater_spec.rb @@ -76,8 +76,9 @@ describe UserUpdater do notification_level_when_replying: 3, email_in_reply_to: false, date_of_birth: date_of_birth, - theme_key: theme.key - ) + theme_key: theme.key, + allow_private_messages: false) + expect(val).to be_truthy user.reload @@ -92,6 +93,7 @@ describe UserUpdater do expect(user.user_option.email_in_reply_to).to eq false expect(user.user_option.theme_key).to eq theme.key expect(user.user_option.theme_key_seq).to eq(seq + 1) + expect(user.user_option.allow_private_messages).to eq(false) expect(user.date_of_birth).to eq(date_of_birth.to_date) end From 25c25ae423859ae5a1abea3d2230677d058caa0d Mon Sep 17 00:00:00 2001 From: Guo Xiang Tan Date: Tue, 10 Oct 2017 16:26:56 +0800 Subject: [PATCH 038/174] FEATURE: Allow user to leave a PM. --- .../discourse/controllers/topic.js.es6 | 6 +- .../discourse/lib/transform-post.js.es6 | 1 + .../widgets/post-small-action.js.es6 | 1 + .../widgets/private-message-map.js.es6 | 32 ++++++++--- app/controllers/topics_controller.rb | 5 +- app/models/topic.rb | 17 ++++-- app/serializers/topic_view_serializer.rb | 1 + config/locales/client.en.yml | 2 + lib/guardian/topic_guardian.rb | 9 ++- spec/components/guardian_spec.rb | 56 +++++++++++++++++++ spec/models/topic_spec.rb | 20 +++++++ 11 files changed, 132 insertions(+), 18 deletions(-) diff --git a/app/assets/javascripts/discourse/controllers/topic.js.es6 b/app/assets/javascripts/discourse/controllers/topic.js.es6 index bb8500f08d..3966e69905 100644 --- a/app/assets/javascripts/discourse/controllers/topic.js.es6 +++ b/app/assets/javascripts/discourse/controllers/topic.js.es6 @@ -231,7 +231,11 @@ export default Ember.Controller.extend(SelectedPostsCount, BufferedContent, { }, removeAllowedUser(user) { - return this.get('model.details').removeAllowedUser(user); + return this.get('model.details').removeAllowedUser(user).then(() => { + if (this.currentUser.id === user.id) { + this.transitionToRoute("userPrivateMessages", user); + } + }); }, removeAllowedGroup(group) { diff --git a/app/assets/javascripts/discourse/lib/transform-post.js.es6 b/app/assets/javascripts/discourse/lib/transform-post.js.es6 index 5faac346ec..ac81e64e68 100644 --- a/app/assets/javascripts/discourse/lib/transform-post.js.es6 +++ b/app/assets/javascripts/discourse/lib/transform-post.js.es6 @@ -127,6 +127,7 @@ export default function transformPost(currentUser, site, post, prevPost, nextPos postAtts.allowedGroups = details.allowed_groups; postAtts.allowedUsers = details.allowed_users; postAtts.canRemoveAllowedUsers = details.can_remove_allowed_users; + postAtts.canRemoveSelfId = details.can_remove_self_id; postAtts.canInvite = details.can_invite_to; } diff --git a/app/assets/javascripts/discourse/widgets/post-small-action.js.es6 b/app/assets/javascripts/discourse/widgets/post-small-action.js.es6 index 2f26fac098..8f7113727d 100644 --- a/app/assets/javascripts/discourse/widgets/post-small-action.js.es6 +++ b/app/assets/javascripts/discourse/widgets/post-small-action.js.es6 @@ -48,6 +48,7 @@ const icons = { 'split_topic': 'sign-out', 'invited_user': 'plus-circle', 'invited_group': 'plus-circle', + 'user_left': 'minus-circle', 'removed_user': 'minus-circle', 'removed_group': 'minus-circle', 'public_topic': 'comment', diff --git a/app/assets/javascripts/discourse/widgets/private-message-map.js.es6 b/app/assets/javascripts/discourse/widgets/private-message-map.js.es6 index fc8b505c28..3e3eba852b 100644 --- a/app/assets/javascripts/discourse/widgets/private-message-map.js.es6 +++ b/app/assets/javascripts/discourse/widgets/private-message-map.js.es6 @@ -36,8 +36,12 @@ createWidget('pm-remove-link', { template: hbs`{{d-icon "times"}}`, click() { - bootbox.confirm(I18n.t("private_message_info.remove_allowed_user", {name: this.attrs.username}), (confirmed) => { - if (confirmed) { this.sendWidgetAction('removeAllowedUser', this.attrs); } + const messageKey = this.attrs.isCurrentUser ? 'leave_message' : 'remove_allowed_user'; + + bootbox.confirm(I18n.t(`private_message_info.${messageKey}`, { name: this.attrs.user.username }), confirmed => { + if (confirmed) { + this.sendWidgetAction('removeAllowedUser', this.attrs.user); + } }); } }); @@ -49,11 +53,12 @@ createWidget('pm-map-user', { const user = attrs.user; const avatar = avatarFor('small', { template: user.avatar_template, username: user.username }); const link = h('a', { attributes: { href: user.get('path') } }, [ avatar, ' ', user.username ]); - const result = [link]; - if (attrs.canRemoveAllowedUsers) { + const isCurrentUser = attrs.canRemoveSelfId === user.get('id'); + + if (attrs.canRemoveAllowedUsers || isCurrentUser) { result.push(' '); - result.push(this.attach('pm-remove-link', user)); + result.push(this.attach('pm-remove-link', { user, isCurrentUser } )); } return result; @@ -67,12 +72,23 @@ export default createWidget('private-message-map', { const participants = []; if (attrs.allowedGroups.length) { - participants.push(attrs.allowedGroups.map(ag => this.attach('pm-map-user-group', {group: ag, canRemoveAllowedUsers: attrs.canRemoveAllowedUsers}))); + participants.push(attrs.allowedGroups.map(ag => { + this.attach('pm-map-user-group', { + group: ag, + canRemoveAllowedUsers: attrs.canRemoveAllowedUsers + }); + })); } - if (attrs.allowedUsers.length) { + const allowedUsersLength = attrs.allowedUsers.length; + + if (allowedUsersLength) { participants.push(attrs.allowedUsers.map(au => { - return this.attach('pm-map-user', { user: au, canRemoveAllowedUsers: attrs.canRemoveAllowedUsers }); + return this.attach('pm-map-user', { + user: au, + canRemoveAllowedUsers: attrs.canRemoveAllowedUsers, + canRemoveSelfId: attrs.canRemoveSelfId + }); })); } diff --git a/app/controllers/topics_controller.rb b/app/controllers/topics_controller.rb index 631a615824..69717e3587 100644 --- a/app/controllers/topics_controller.rb +++ b/app/controllers/topics_controller.rb @@ -439,9 +439,10 @@ class TopicsController < ApplicationController def remove_allowed_user params.require(:username) topic = Topic.find_by(id: params[:topic_id]) - guardian.ensure_can_remove_allowed_users!(topic) + user = User.find_by(username: params[:username]) + guardian.ensure_can_remove_allowed_users!(topic, user) - if topic.remove_allowed_user(current_user, params[:username]) + if topic.remove_allowed_user(current_user, user) render json: success_json else render json: failed_json, status: 422 diff --git a/app/models/topic.rb b/app/models/topic.rb index f4093cb8f5..9cbaed9ee7 100644 --- a/app/models/topic.rb +++ b/app/models/topic.rb @@ -709,14 +709,21 @@ SQL end def remove_allowed_user(removed_by, username) - if user = User.find_by(username: username) + user = username.is_a?(User) ? username : User.find_by(username: username) + + if user topic_user = topic_allowed_users.find_by(user_id: user.id) + if topic_user topic_user.destroy - # we can not remove ourselves cause then we will end up adding - # ourselves in add_small_action - removed_by = Discourse.system_user if user.id == removed_by&.id - add_small_action(removed_by, "removed_user", user.username) + + if user.id == removed_by&.id + removed_by = Discourse.system_user + add_small_action(removed_by, "user_left", user.username) + else + add_small_action(removed_by, "removed_user", user.username) + end + return true end end diff --git a/app/serializers/topic_view_serializer.rb b/app/serializers/topic_view_serializer.rb index df70f3592b..552aeeb25f 100644 --- a/app/serializers/topic_view_serializer.rb +++ b/app/serializers/topic_view_serializer.rb @@ -114,6 +114,7 @@ class TopicViewSerializer < ApplicationSerializer result[:can_delete] = true if scope.can_delete?(object.topic) result[:can_recover] = true if scope.can_recover_topic?(object.topic) result[:can_remove_allowed_users] = true if scope.can_remove_allowed_users?(object.topic) + result[:can_remove_self_id] = scope.user.id if scope.can_remove_allowed_users?(object.topic, scope.user) result[:can_invite_to] = true if scope.can_invite_to?(object.topic) result[:can_invite_via_email] = true if scope.can_invite_via_email?(object.topic) result[:can_create_post] = true if scope.can_create?(Post, object.topic) diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 5c78efd4f0..d5e17d752d 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -143,6 +143,7 @@ en: split_topic: "split this topic %{when}" invited_user: "invited %{who} %{when}" invited_group: "invited %{who} %{when}" + user_left: "%{who} left this message %{when}" removed_user: "removed %{who} %{when}" removed_group: "removed %{who} %{when}" autoclosed: @@ -1030,6 +1031,7 @@ en: private_message_info: title: "Message" invite: "Invite Others..." + leave_message: "Do you really want to leave this message?" remove_allowed_user: "Do you really want to remove {{name}} from this message?" remove_allowed_group: "Do you really want to remove {{name}} from this message?" diff --git a/lib/guardian/topic_guardian.rb b/lib/guardian/topic_guardian.rb index b8db72d315..1bf07ef82c 100644 --- a/lib/guardian/topic_guardian.rb +++ b/lib/guardian/topic_guardian.rb @@ -1,8 +1,13 @@ #mixin for all guardian methods dealing with topic permisions module TopicGuardian - def can_remove_allowed_users?(topic) - is_staff? + def can_remove_allowed_users?(topic, target_user = nil) + is_staff? || + ( + topic.allowed_users.count > 1 && + topic.user != target_user && + !!(target_user && user == target_user) + ) end # Creating Methods diff --git a/spec/components/guardian_spec.rb b/spec/components/guardian_spec.rb index 0449684131..d0af49efb9 100644 --- a/spec/components/guardian_spec.rb +++ b/spec/components/guardian_spec.rb @@ -2613,4 +2613,60 @@ describe Guardian do end end end + + describe '#can_remove_allowed_users?' do + context 'staff users' do + it 'should be true' do + expect(Guardian.new(moderator).can_remove_allowed_users?(topic)) + .to eq(true) + end + end + + context 'normal user' do + let(:topic) { Fabricate(:topic, user: Fabricate(:user)) } + let(:another_user) { Fabricate(:user) } + + before do + topic.allowed_users << user + topic.allowed_users << another_user + end + + it 'should be false' do + expect(Guardian.new(user).can_remove_allowed_users?(topic)) + .to eq(false) + end + + describe 'target_user is the user' do + describe 'when user is in a pm with another user' do + it 'should return true' do + expect(Guardian.new(user).can_remove_allowed_users?(topic, user)) + .to eq(true) + end + end + + describe 'when user is the creator of the topic' do + it 'should return false' do + expect(Guardian.new(topic.user).can_remove_allowed_users?(topic, topic.user)) + .to eq(false) + end + end + + describe 'when user is the only user in the topic' do + it 'should return false' do + topic.remove_allowed_user(Discourse.system_user, another_user.username) + + expect(Guardian.new(user).can_remove_allowed_users?(topic, user)) + .to eq(false) + end + end + end + + describe 'target_user is not the user' do + it 'should return false' do + expect(Guardian.new(user).can_remove_allowed_users?(topic, moderator)) + .to eq(false) + end + end + end + end end diff --git a/spec/models/topic_spec.rb b/spec/models/topic_spec.rb index 706b2ee497..6bb619fbce 100644 --- a/spec/models/topic_spec.rb +++ b/spec/models/topic_spec.rb @@ -6,6 +6,7 @@ require_dependency 'post_destroyer' describe Topic do let(:now) { Time.zone.local(2013, 11, 20, 8, 0) } let(:user) { Fabricate(:user) } + let(:topic) { Fabricate(:topic) } context 'validations' do let(:topic) { Fabricate.build(:topic) } @@ -2040,4 +2041,23 @@ describe Topic do end end end + + describe '#remove_allowed_user' do + let(:another_user) { Fabricate(:user) } + + describe 'removing oneself' do + it 'should remove onself' do + topic.allowed_users << another_user + + expect(topic.remove_allowed_user(another_user, another_user)).to eq(true) + expect(topic.allowed_users.include?(another_user)).to eq(false) + + post = Post.last + + expect(post.user).to eq(Discourse.system_user) + expect(post.post_type).to eq(Post.types[:small_action]) + expect(post.action_code).to eq('user_left') + end + end + end end From c2a5e603c22ed104cfe48f1d93338fea98f8fd23 Mon Sep 17 00:00:00 2001 From: Guo Xiang Tan Date: Thu, 19 Oct 2017 14:47:29 +0800 Subject: [PATCH 039/174] Guard `ActionRecord::Base.exec_sql` against a readonly PostgreSQL cluster. --- lib/freedom_patches/active_record_base.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/freedom_patches/active_record_base.rb b/lib/freedom_patches/active_record_base.rb index bd4eb2c1e8..18746093f9 100644 --- a/lib/freedom_patches/active_record_base.rb +++ b/lib/freedom_patches/active_record_base.rb @@ -5,6 +5,8 @@ class ActiveRecord::Base conn = ActiveRecord::Base.connection sql = ActiveRecord::Base.send(:sanitize_sql_array, args) conn.raw_connection.exec(sql) + rescue PG::ReadOnlySqlTransaction + Rails.logger.warn("WARN: PostgreSQL is in a readonly state. Performed a noop") end def self.exec_sql_row_count(*args) From 9d6449ae92cd2dc0dd442023e4429540c9c9beee Mon Sep 17 00:00:00 2001 From: Guo Xiang Tan Date: Thu, 19 Oct 2017 15:39:03 +0800 Subject: [PATCH 040/174] Revert "Guard `ActionRecord::Base.exec_sql` against a readonly PostgreSQL cluster." This reverts commit c2a5e603c22ed104cfe48f1d93338fea98f8fd23. --- lib/freedom_patches/active_record_base.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/freedom_patches/active_record_base.rb b/lib/freedom_patches/active_record_base.rb index 18746093f9..bd4eb2c1e8 100644 --- a/lib/freedom_patches/active_record_base.rb +++ b/lib/freedom_patches/active_record_base.rb @@ -5,8 +5,6 @@ class ActiveRecord::Base conn = ActiveRecord::Base.connection sql = ActiveRecord::Base.send(:sanitize_sql_array, args) conn.raw_connection.exec(sql) - rescue PG::ReadOnlySqlTransaction - Rails.logger.warn("WARN: PostgreSQL is in a readonly state. Performed a noop") end def self.exec_sql_row_count(*args) From 5b9ddaf972254e5f0c1a92aa676e74fbf3a0a6c2 Mon Sep 17 00:00:00 2001 From: Guo Xiang Tan Date: Thu, 19 Oct 2017 15:41:03 +0800 Subject: [PATCH 041/174] FIX: `Topic#fancy_title` should not write in readonly mode. --- app/models/topic.rb | 3 +-- spec/models/topic_spec.rb | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/app/models/topic.rb b/app/models/topic.rb index f4093cb8f5..89a2c79dd6 100644 --- a/app/models/topic.rb +++ b/app/models/topic.rb @@ -311,11 +311,10 @@ class Topic < ActiveRecord::Base return ERB::Util.html_escape(title) unless SiteSetting.title_fancy_entities? unless fancy_title = read_attribute(:fancy_title) - fancy_title = Topic.fancy_title(title) write_attribute(:fancy_title, fancy_title) - unless new_record? + if !new_record? && !Discourse.readonly_mode? # make sure data is set in table, this also allows us to change algorithm # by simply nulling this column exec_sql("UPDATE topics SET fancy_title = :fancy_title where id = :id", id: self.id, fancy_title: fancy_title) diff --git a/spec/models/topic_spec.rb b/spec/models/topic_spec.rb index 706b2ee497..748c9aa0b6 100644 --- a/spec/models/topic_spec.rb +++ b/spec/models/topic_spec.rb @@ -327,6 +327,27 @@ describe Topic do topic.title = "this is another edge case" expect(topic.fancy_title).to eq("this is another edge case") end + + context 'readonly mode' do + before do + Discourse.enable_readonly_mode + end + + after do + Discourse.disable_readonly_mode + end + + it 'should not attempt to update `fancy_title`' do + topic.save! + expect(topic.fancy_title).to eq('“this topic” – has “fancy stuff”') + + topic.title = "This is a test testing testing" + expect(topic.fancy_title).to eq("This is a test testing testing") + + expect(topic.reload.read_attribute(:fancy_title)) + .to eq('“this topic” – has “fancy stuff”') + end + end end end From 38123a4246934665f14c602c3bd20c9ac4dcc114 Mon Sep 17 00:00:00 2001 From: Guo Xiang Tan Date: Thu, 19 Oct 2017 16:34:54 +0800 Subject: [PATCH 042/174] Add readonly test to smoke tests. --- spec/phantom_js/smoke_test.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/spec/phantom_js/smoke_test.js b/spec/phantom_js/smoke_test.js index bf6eb4afa5..50926f3338 100644 --- a/spec/phantom_js/smoke_test.js +++ b/spec/phantom_js/smoke_test.js @@ -178,7 +178,11 @@ var runTests = function() { return $("#user-card .names").length; }); - if (!system.env["SKIP_WRITE_TESTS"]) { + if (system.env["READONLY_TESTS"]) { + test("readonly alert is present", function() { + return $(".alert-read-only").length; + }); + } else { exec("open login modal", function() { $(".login-button").click(); }); From c41880ab193e85c727e19a23de05f21b4c8ebdd3 Mon Sep 17 00:00:00 2001 From: Gerhard Schlager Date: Thu, 19 Oct 2017 14:27:40 +0200 Subject: [PATCH 043/174] Improvements to the experimental mbox importer * Disable journaling to improve performance in Docker * Use the email cooking method * Store IncomingEmail in order find related posts by Message-ID * Escape HTML in imported messages --- script/import_scripts/mbox/importer.rb | 21 ++++++++++++++++--- .../import_scripts/mbox/support/database.rb | 2 +- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/script/import_scripts/mbox/importer.rb b/script/import_scripts/mbox/importer.rb index 762c162e82..71328df3a7 100644 --- a/script/import_scripts/mbox/importer.rb +++ b/script/import_scripts/mbox/importer.rb @@ -97,7 +97,7 @@ module ImportScripts::Mbox def map_post(row) user_id = user_id_from_imported_user_id(row['from_email']) || Discourse::SYSTEM_USER_ID - body = row['body'] || '' + body = CGI.escapeHTML(row['body'] || '') body << map_attachments(row['raw_message'], user_id) if row['attachment_count'].positive? body << Email::Receiver.elided_html(row['elided']) if row['elided'].present? @@ -108,14 +108,17 @@ module ImportScripts::Mbox raw: body, raw_email: row['raw_message'], via_email: true, - # cook_method: Post.cook_methods[:email] # this is slowing down the import by factor 4 + cook_method: Post.cook_methods[:email], + post_create_action: proc do |post| + create_incoming_email(post, row) + end } end def map_first_post(row) mapped = map_post(row) mapped[:category] = category_id_from_imported_category_id(row['category']) - mapped[:title] = row['subject'].strip[0...255] + mapped[:title] = CGI.escapeHTML(row['subject'].strip)[0...255] mapped end @@ -154,6 +157,18 @@ module ImportScripts::Mbox attachment_markdown end + def create_incoming_email(post, row) + IncomingEmail.create( + message_id: row['msg_id'], + raw: row['raw_message'], + subject: row['subject'], + from_address: row['from_email'], + user_id: post.user_id, + topic_id: post.topic_id, + post_id: post.id + ) + end + def to_time(datetime) Time.zone.at(DateTime.iso8601(datetime)) if datetime end diff --git a/script/import_scripts/mbox/support/database.rb b/script/import_scripts/mbox/support/database.rb index 396d23e5b2..a3cb723046 100644 --- a/script/import_scripts/mbox/support/database.rb +++ b/script/import_scripts/mbox/support/database.rb @@ -163,7 +163,7 @@ module ImportScripts::Mbox private def configure_database - @db.execute 'PRAGMA journal_mode = TRUNCATE' + @db.execute 'PRAGMA journal_mode = OFF' end def upgrade_schema_version From 3cd73cdf184a9660070551a65dd7bfe75f1b110c Mon Sep 17 00:00:00 2001 From: Gerhard Schlager Date: Tue, 17 Oct 2017 22:37:13 +0200 Subject: [PATCH 044/174] FIX: fancy topic title must fit into column --- app/models/topic.rb | 7 ++++++- spec/models/topic_spec.rb | 8 ++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/app/models/topic.rb b/app/models/topic.rb index 86b882203e..fe15c98f00 100644 --- a/app/models/topic.rb +++ b/app/models/topic.rb @@ -37,6 +37,10 @@ class Topic < ActiveRecord::Base @max_sort_order ||= (2**31) - 1 end + def self.max_fancy_title_length + 400 + end + def featured_users @featured_users ||= TopicFeaturedUsers.new(self) end @@ -304,7 +308,8 @@ class Topic < ActiveRecord::Base def self.fancy_title(title) escaped = ERB::Util.html_escape(title) return unless escaped - Emoji.unicode_unescape(HtmlPrettify.render(escaped)) + fancy_title = Emoji.unicode_unescape(HtmlPrettify.render(escaped)) + fancy_title.length > Topic.max_fancy_title_length ? title : fancy_title end def fancy_title diff --git a/spec/models/topic_spec.rb b/spec/models/topic_spec.rb index 10560a25de..1cea61ad4b 100644 --- a/spec/models/topic_spec.rb +++ b/spec/models/topic_spec.rb @@ -329,6 +329,14 @@ describe Topic do expect(topic.fancy_title).to eq("this is another edge case") end + it "works with long title that results in lots of entities" do + long_title = "NEW STOCK PICK: PRCT - LAST PICK UP 233%, NNCO.................................................................................................................................................................. ofoum" + topic.title = long_title + + expect { topic.save! }.to_not raise_error + expect(topic.fancy_title).to eq(long_title) + end + context 'readonly mode' do before do Discourse.enable_readonly_mode From 6c829c24d77715bc2f20080665280f42bc868f28 Mon Sep 17 00:00:00 2001 From: Gerhard Schlager Date: Thu, 19 Oct 2017 15:25:20 +0200 Subject: [PATCH 045/174] escaping the subject isn't needed in the mbox importer --- script/import_scripts/mbox/importer.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/import_scripts/mbox/importer.rb b/script/import_scripts/mbox/importer.rb index 71328df3a7..1c55804a98 100644 --- a/script/import_scripts/mbox/importer.rb +++ b/script/import_scripts/mbox/importer.rb @@ -118,7 +118,7 @@ module ImportScripts::Mbox def map_first_post(row) mapped = map_post(row) mapped[:category] = category_id_from_imported_category_id(row['category']) - mapped[:title] = CGI.escapeHTML(row['subject'].strip)[0...255] + mapped[:title] = row['subject'].strip[0...255] mapped end From 1cae875146109b40a56d7ae5d4132276089c607d Mon Sep 17 00:00:00 2001 From: Gerhard Schlager Date: Thu, 19 Oct 2017 15:26:37 +0200 Subject: [PATCH 046/174] FIX: topic link extraction shouldn't fail when the parsed URL has no path --- app/models/topic_link.rb | 2 +- spec/models/topic_link_spec.rb | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/app/models/topic_link.rb b/app/models/topic_link.rb index d66e1616f5..da635aeb99 100644 --- a/app/models/topic_link.rb +++ b/app/models/topic_link.rb @@ -161,7 +161,7 @@ SQL added_urls << url unless TopicLink.exists?(topic_id: post.topic_id, post_id: post.id, url: url) - file_extension = File.extname(parsed.path)[1..10].downcase unless File.extname(parsed.path).empty? + file_extension = File.extname(parsed.path)[1..10].downcase unless parsed.path.nil? || File.extname(parsed.path).empty? begin TopicLink.create!(post_id: post.id, user_id: post.user_id, diff --git a/spec/models/topic_link_spec.rb b/spec/models/topic_link_spec.rb index 40fafa5a03..bb9408f808 100644 --- a/spec/models/topic_link_spec.rb +++ b/spec/models/topic_link_spec.rb @@ -230,7 +230,6 @@ http://b.com/#{'a' * 500} end end - end describe 'internal link from pm' do @@ -382,6 +381,11 @@ http://b.com/#{'a' * 500} expect(result).to eq({}) end end + + it "works with invalid link target" do + post = Fabricate(:post, raw: 'http:geturl', user: user, topic: topic, cook_method: Post.cook_methods[:raw_html]) + expect { TopicLink.extract_from(post) }.to_not raise_error + end end end From 838568cbc3706b9ceb0833cf5ce5b61dd685f697 Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Tue, 17 Oct 2017 13:31:45 -0400 Subject: [PATCH 047/174] Refactor flag types for more customization --- .../admin/components/flag-counts.js.es6 | 10 --- .../admin/helpers/post-action-title.js.es6 | 12 +++ .../templates/components/flag-counts.hbs | 2 - .../templates/components/flagged-post.hbs | 2 +- .../admin/templates/flags-topics-index.hbs | 5 +- .../discourse/models/post-action-type.js.es6 | 8 +- app/models/post.rb | 4 +- app/models/post_action.rb | 25 ++++-- app/models/post_action_type.rb | 86 ++++++++++++++++--- app/models/report.rb | 2 +- app/models/user.rb | 4 +- .../flagged_topic_summary_serializer.rb | 2 +- .../post_action_type_serializer.rb | 24 +++++- app/serializers/post_serializer.rb | 2 +- app/serializers/site_serializer.rb | 5 +- app/services/spam_rule/auto_block.rb | 2 +- config/locales/client.en.yml | 22 ++--- lib/badge_queries.rb | 2 +- lib/flag_settings.rb | 42 +++++++++ lib/guardian/post_guardian.rb | 10 +-- lib/plugin/instance.rb | 9 ++ lib/post_revisor.rb | 2 +- plugins/discourse-narrative-bot/plugin.rb | 2 +- spec/components/flag_settings_spec.rb | 46 ++++++++++ spec/models/post_action_spec.rb | 2 +- spec/serializers/post_serializer_spec.rb | 3 +- 26 files changed, 253 insertions(+), 82 deletions(-) delete mode 100644 app/assets/javascripts/admin/components/flag-counts.js.es6 create mode 100644 app/assets/javascripts/admin/helpers/post-action-title.js.es6 create mode 100644 lib/flag_settings.rb create mode 100644 spec/components/flag_settings_spec.rb diff --git a/app/assets/javascripts/admin/components/flag-counts.js.es6 b/app/assets/javascripts/admin/components/flag-counts.js.es6 deleted file mode 100644 index 040207e695..0000000000 --- a/app/assets/javascripts/admin/components/flag-counts.js.es6 +++ /dev/null @@ -1,10 +0,0 @@ -import computed from 'ember-addons/ember-computed-decorators'; - -export default Ember.Component.extend({ - classNames: ['flag-counts'], - - @computed('details.flag_type_id') - title(id) { - return I18n.t(`admin.flags.summary.action_type_${id}`, { count: 1 }); - } -}); diff --git a/app/assets/javascripts/admin/helpers/post-action-title.js.es6 b/app/assets/javascripts/admin/helpers/post-action-title.js.es6 new file mode 100644 index 0000000000..ced180d8bb --- /dev/null +++ b/app/assets/javascripts/admin/helpers/post-action-title.js.es6 @@ -0,0 +1,12 @@ +function postActionTitle([id, nameKey]) { + let title = I18n.t(`admin.flags.short_names.${nameKey}`, { defaultValue: null }); + + // TODO: We can remove this once other translations have been updated + if (!title) { + return I18n.t(`admin.flags.summary.action_type_${id}`, { count: 1 }); + } + + return title; +} + +export default Ember.Helper.helper(postActionTitle); diff --git a/app/assets/javascripts/admin/templates/components/flag-counts.hbs b/app/assets/javascripts/admin/templates/components/flag-counts.hbs index ff1f011825..e69de29bb2 100644 --- a/app/assets/javascripts/admin/templates/components/flag-counts.hbs +++ b/app/assets/javascripts/admin/templates/components/flag-counts.hbs @@ -1,2 +0,0 @@ -{{title}} -x{{details.count}} diff --git a/app/assets/javascripts/admin/templates/components/flagged-post.hbs b/app/assets/javascripts/admin/templates/components/flagged-post.hbs index 98ac4bf5e0..4a832771a8 100644 --- a/app/assets/javascripts/admin/templates/components/flagged-post.hbs +++ b/app/assets/javascripts/admin/templates/components/flagged-post.hbs @@ -73,7 +73,7 @@ {{#each flaggedPost.post_actions as |postAction|}} {{#flag-user user=postAction.user date=postAction.created_at}}
    - {{i18n (concat "admin.flags.summary.action_type_" postAction.post_action_type_id) count=1}} + {{post-action-title postAction.post_action_type_id postAction.name_key}}
    {{/flag-user}} {{/each}} diff --git a/app/assets/javascripts/admin/templates/flags-topics-index.hbs b/app/assets/javascripts/admin/templates/flags-topics-index.hbs index 61ef340129..d9fdf87066 100644 --- a/app/assets/javascripts/admin/templates/flags-topics-index.hbs +++ b/app/assets/javascripts/admin/templates/flags-topics-index.hbs @@ -19,7 +19,10 @@ {{#each ft.flag_counts as |fc|}} - {{flag-counts details=fc}} +
    + {{post-action-title fc.post_action_type_id fc.name_key}} + x{{fc.count}} +
    {{/each}} diff --git a/app/assets/javascripts/discourse/models/post-action-type.js.es6 b/app/assets/javascripts/discourse/models/post-action-type.js.es6 index 94a82ff5cb..fb3125f0fa 100644 --- a/app/assets/javascripts/discourse/models/post-action-type.js.es6 +++ b/app/assets/javascripts/discourse/models/post-action-type.js.es6 @@ -1,9 +1,7 @@ import RestModel from 'discourse/models/rest'; -const PostActionType = RestModel.extend({ - notCustomFlag: Em.computed.not('is_custom_flag') -}); - export const MAX_MESSAGE_LENGTH = 500; -export default PostActionType; +export default RestModel.extend({ + notCustomFlag: Em.computed.not('is_custom_flag') +}); diff --git a/app/models/post.rb b/app/models/post.rb index a70c69a915..4a0f7df391 100644 --- a/app/models/post.rb +++ b/app/models/post.rb @@ -411,11 +411,11 @@ class Post < ActiveRecord::Base end def is_flagged? - post_actions.where(post_action_type_id: PostActionType.flag_types.values, deleted_at: nil).count != 0 + post_actions.where(post_action_type_id: PostActionType.flag_types_without_custom.values, deleted_at: nil).count != 0 end def has_active_flag? - post_actions.active.where(post_action_type_id: PostActionType.flag_types.values).count != 0 + post_actions.active.where(post_action_type_id: PostActionType.flag_types_without_custom.values).count != 0 end def unhide! diff --git a/app/models/post_action.rb b/app/models/post_action.rb index 2ee784f7af..c5306c3949 100644 --- a/app/models/post_action.rb +++ b/app/models/post_action.rb @@ -44,7 +44,7 @@ class PostAction < ActiveRecord::Base def self.flag_count_by_date(start_date, end_date, category_id = nil) result = where('post_actions.created_at >= ? AND post_actions.created_at <= ?', start_date, end_date) - result = result.where(post_action_type_id: PostActionType.flag_types.values) + result = result.where(post_action_type_id: PostActionType.flag_types_without_custom.values) result = result.joins(post: :topic).where("topics.category_id = ?", category_id) if category_id result.group('date(post_actions.created_at)') .order('date(post_actions.created_at)') @@ -164,7 +164,7 @@ SQL if moderator.id == Discourse::SYSTEM_USER_ID PostActionType.auto_action_flag_types.values else - PostActionType.flag_types.values + PostActionType.flag_types_without_custom.values end actions = PostAction.where(post_id: post.id) @@ -179,8 +179,13 @@ SQL end # reset all cached counters - f = action_type_ids.map { |t| ["#{PostActionType.types[t]}_count", 0] } - Post.with_deleted.where(id: post.id).update_all(Hash[*f.flatten]) + cached = {} + action_type_ids.each do |atid| + column = "#{PostActionType.types[atid]}_count" + cached[column] = 0 if ActiveRecord::Base.connection.column_exists?(:posts, column) + end + + Post.with_deleted.where(id: post.id).update_all(cached) update_flagged_posts_count end @@ -188,7 +193,7 @@ SQL def self.defer_flags!(post, moderator, delete_post = false) actions = PostAction.active .where(post_id: post.id) - .where(post_action_type_id: PostActionType.flag_types.values) + .where(post_action_type_id: PostActionType.flag_types_without_custom.values) actions.each do |action| action.deferred_at = Time.zone.now @@ -355,7 +360,7 @@ SQL end def is_flag? - PostActionType.flag_types.values.include?(post_action_type_id) + !!PostActionType.flag_types[post_action_type_id] end def is_private_message? @@ -387,7 +392,7 @@ SQL end before_create do - post_action_type_ids = is_flag? ? PostActionType.flag_types.values : post_action_type_id + post_action_type_ids = is_flag? ? PostActionType.flag_types_without_custom.values : post_action_type_id raise AlreadyActed if PostAction.where(user_id: user_id) .where(post_id: post_id) .where(post_action_type_id: post_action_type_ids) @@ -445,7 +450,9 @@ SQL .sum("CASE WHEN users.moderator OR users.admin THEN #{SiteSetting.staff_like_weight} ELSE 1 END") Post.where(id: post_id).update_all ["like_count = :count, like_score = :score", count: count, score: score] else - Post.where(id: post_id).update_all ["#{column} = ?", count] + if ActiveRecord::Base.connection.column_exists?(:posts, column) + Post.where(id: post_id).update_all ["#{column} = ?", count] + end end topic_id = Post.with_deleted.where(id: post_id).pluck(:topic_id).first @@ -583,7 +590,7 @@ SQL end def self.post_action_type_for_post(post_id) - post_action = PostAction.find_by(deferred_at: nil, post_id: post_id, post_action_type_id: PostActionType.flag_types.values, deleted_at: nil) + post_action = PostAction.find_by(deferred_at: nil, post_id: post_id, post_action_type_id: PostActionType.flag_types_without_custom.values, deleted_at: nil) PostActionType.types[post_action.post_action_type_id] end diff --git a/app/models/post_action_type.rb b/app/models/post_action_type.rb index 6af07c53af..9bb75363b7 100644 --- a/app/models/post_action_type.rb +++ b/app/models/post_action_type.rb @@ -1,5 +1,6 @@ require_dependency 'enum' require_dependency 'distributed_cache' +require_dependency 'flag_settings' class PostActionType < ActiveRecord::Base after_save :expire_cache @@ -14,23 +15,72 @@ class PostActionType < ActiveRecord::Base class << self + def flag_settings + unless @flag_settings + @flag_settings = FlagSettings.new + @flag_settings.add( + 3, + :off_topic, + notify_type: true, + auto_action_type: true + ) + @flag_settings.add( + 4, + :inappropriate, + topic_type: true, + notify_type: true, + auto_action_type: true + ) + @flag_settings.add( + 8, + :spam, + topic_type: true, + notify_type: true, + auto_action_type: true + ) + @flag_settings.add( + 6, + :notify_user, + topic_type: true, + notify_type: true, + custom_type: true + ) + @flag_settings.add( + 7, + :notify_moderators, + topic_type: true, + notify_type: true, + custom_type: true + ) + end + + @flag_settings + end + + def replace_flag_settings(settings) + @flag_settings = settings + @types = nil + end + def ordered order('position asc') end def types - @types ||= Enum.new(bookmark: 1, - like: 2, - off_topic: 3, - inappropriate: 4, - vote: 5, - notify_user: 6, - notify_moderators: 7, - spam: 8) + unless @types + @types = Enum.new( + bookmark: 1, + like: 2, + vote: 5 + ) + @types.merge!(flag_settings.flag_types) + end + + @types end def auto_action_flag_types - @auto_action_flag_types ||= flag_types.except(:notify_user, :notify_moderators) + flag_settings.auto_action_types end def public_types @@ -41,17 +91,29 @@ class PostActionType < ActiveRecord::Base @public_type_ids ||= public_types.values end + def flag_types_without_custom + flag_settings.without_custom_types + end + def flag_types - @flag_types ||= types.only(:off_topic, :spam, :inappropriate, :notify_moderators) + flag_settings.flag_types end # flags resulting in mod notifications def notify_flag_type_ids - @notify_flag_type_ids ||= types.only(:off_topic, :spam, :inappropriate, :notify_moderators).values + notify_flag_types.values + end + + def notify_flag_types + flag_settings.notify_types end def topic_flag_types - @topic_flag_types ||= types.only(:spam, :inappropriate, :notify_moderators) + flag_settings.topic_flag_types + end + + def custom_types + flag_settings.custom_types end def is_flag?(sym) diff --git a/app/models/report.rb b/app/models/report.rb index 8144df648b..83f046cc36 100644 --- a/app/models/report.rb +++ b/app/models/report.rb @@ -175,7 +175,7 @@ class Report # Post action counts: def self.report_flags(report) basic_report_about report, PostAction, :flag_count_by_date, report.start_date, report.end_date, report.category_id - countable = PostAction.where(post_action_type_id: PostActionType.flag_types.values) + countable = PostAction.where(post_action_type_id: PostActionType.flag_types_without_custom.values) countable = countable.joins(post: :topic).where("topics.category_id = ?", report.category_id) if report.category_id add_counts report, countable, 'post_actions.created_at' end diff --git a/app/models/user.rb b/app/models/user.rb index c70890af63..c52b456ce4 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -615,7 +615,7 @@ class User < ActiveRecord::Base end def flags_given_count - PostAction.where(user_id: id, post_action_type_id: PostActionType.flag_types.values).count + PostAction.where(user_id: id, post_action_type_id: PostActionType.flag_types_without_custom.values).count end def warnings_received_count @@ -623,7 +623,7 @@ class User < ActiveRecord::Base end def flags_received_count - posts.includes(:post_actions).where('post_actions.post_action_type_id' => PostActionType.flag_types.values).count + posts.includes(:post_actions).where('post_actions.post_action_type_id' => PostActionType.flag_types_without_custom.values).count end def private_topics_count diff --git a/app/serializers/flagged_topic_summary_serializer.rb b/app/serializers/flagged_topic_summary_serializer.rb index de770fef25..f29c365a46 100644 --- a/app/serializers/flagged_topic_summary_serializer.rb +++ b/app/serializers/flagged_topic_summary_serializer.rb @@ -15,7 +15,7 @@ class FlaggedTopicSummarySerializer < ActiveModel::Serializer def flag_counts object.flag_counts.map do |k, v| - { flag_type_id: k, count: v } + { post_action_type_id: k, count: v, name_key: PostActionType.types[k] } end end diff --git a/app/serializers/post_action_type_serializer.rb b/app/serializers/post_action_type_serializer.rb index e0bf6f7c97..718c84a5b7 100644 --- a/app/serializers/post_action_type_serializer.rb +++ b/app/serializers/post_action_type_serializer.rb @@ -2,13 +2,25 @@ require_dependency 'configurable_urls' class PostActionTypeSerializer < ApplicationSerializer - attributes :name_key, :name, :description, :short_description, :long_form, :is_flag, :icon, :id, :is_custom_flag + attributes( + :id, + :name_key, + :name, + :description, + :short_description, + :long_form, + :is_flag, + :is_custom_flag + ) include ConfigurableUrls def is_custom_flag - object.id == PostActionType.types[:notify_user] || - object.id == PostActionType.types[:notify_moderators] + !!PostActionType.custom_types[object.id] + end + + def is_flag + !!PostActionType.flag_types[object.id] end def name @@ -27,10 +39,14 @@ class PostActionTypeSerializer < ApplicationSerializer i18n('short_description', tos_url: tos_path) end + def name_key + PostActionType.types[object.id] + end + protected def i18n(field, vars = nil) - key = "post_action_types.#{object.name_key}.#{field}" + key = "post_action_types.#{name_key}.#{field}" vars ? I18n.t(key, vars) : I18n.t(key) end diff --git a/app/serializers/post_serializer.rb b/app/serializers/post_serializer.rb index e06617b0f1..d756ee1dd3 100644 --- a/app/serializers/post_serializer.rb +++ b/app/serializers/post_serializer.rb @@ -246,7 +246,7 @@ class PostSerializer < BasicPostSerializer # The following only applies if you're logged in if summary[:can_act] && scope.current_user.present? summary[:can_defer_flags] = true if scope.is_staff? && - PostActionType.flag_types.values.include?(id) && + PostActionType.flag_types_without_custom.values.include?(id) && active_flags.present? && active_flags.has_key?(id) && active_flags[id].count > 0 end diff --git a/app/serializers/site_serializer.rb b/app/serializers/site_serializer.rb index ecd93e02ae..6216711d52 100644 --- a/app/serializers/site_serializer.rb +++ b/app/serializers/site_serializer.rb @@ -52,13 +52,14 @@ class SiteSerializer < ApplicationSerializer def post_action_types cache_fragment("post_action_types_#{I18n.locale}") do - ActiveModel::ArraySerializer.new(PostActionType.ordered).as_json + types = PostActionType.types.values.map { |id| PostActionType.new(id: id) } + ActiveModel::ArraySerializer.new(types).as_json end end def topic_flag_types cache_fragment("post_action_flag_types_#{I18n.locale}") do - flags = PostActionType.ordered.where(name_key: ['inappropriate', 'spam', 'notify_moderators']) + flags = PostActionType.ordered.where(id: PostActionType.topic_flag_types.values) ActiveModel::ArraySerializer.new(flags, each_serializer: TopicFlagTypeSerializer).as_json end diff --git a/app/services/spam_rule/auto_block.rb b/app/services/spam_rule/auto_block.rb index bf855bea87..fd2cb3357f 100644 --- a/app/services/spam_rule/auto_block.rb +++ b/app/services/spam_rule/auto_block.rb @@ -66,7 +66,7 @@ class SpamRule::AutoBlock def flagged_post_ids Post.where(user_id: @user.id) - .where('spam_count > ? OR off_topic_count > ? OR inappropriate_count > ?', 0, 0, 0) + .where('spam_count > 0 OR off_topic_count > 0 OR inappropriate_count > 0') .pluck(:id) end diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index d5e17d752d..422283e400 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -2666,22 +2666,12 @@ en: users: "Users" last_flagged: "Last Flagged" - summary: - action_type_3: - one: "off-topic" - other: "off-topic x{{count}}" - action_type_4: - one: "inappropriate" - other: "inappropriate x{{count}}" - action_type_6: - one: "custom" - other: "custom x{{count}}" - action_type_7: - one: "custom" - other: "custom x{{count}}" - action_type_8: - one: "spam" - other: "spam x{{count}}" + short_names: + off_topic: "off-topic" + inappropriate: "inappropriate" + spam: "spam" + notify_user: "custom" + notify_moderators: "custom" groups: primary: "Primary Group" diff --git a/lib/badge_queries.rb b/lib/badge_queries.rb index dd70192c08..9bba4dca8c 100644 --- a/lib/badge_queries.rb +++ b/lib/badge_queries.rb @@ -70,7 +70,7 @@ SQL SELECT pa.user_id, min(pa.id) id FROM post_actions pa JOIN badge_posts p on p.id = pa.post_id - WHERE post_action_type_id IN (#{PostActionType.flag_types.values.join(",")}) AND + WHERE post_action_type_id IN (#{PostActionType.flag_types_without_custom.values.join(",")}) AND (:backfill OR pa.post_id IN (:post_ids) ) GROUP BY pa.user_id ) x diff --git a/lib/flag_settings.rb b/lib/flag_settings.rb new file mode 100644 index 0000000000..3fb700d7cc --- /dev/null +++ b/lib/flag_settings.rb @@ -0,0 +1,42 @@ +class FlagSettings + + attr_reader( + :without_custom_types, + :notify_types, + :topic_flag_types, + :auto_action_types, + :custom_types + ) + + def initialize + @all_flag_types = Enum.new + @topic_flag_types = Enum.new + @notify_types = Enum.new + @auto_action_types = Enum.new + @custom_types = Enum.new + @without_custom_types = Enum.new + end + + def add(id, name, details = nil) + details ||= {} + + @all_flag_types[name] = id + @topic_flag_types[name] = id if !!details[:topic_type] + @notify_types[name] = id if !!details[:notify_type] + @auto_action_types[name] = id if !!details[:auto_action_type] + if !!details[:custom_type] + @custom_types[name] = id + else + @without_custom_types[name] = id + end + end + + def is_flag?(key) + @all_flag_types.valid?(key) + end + + def flag_types + @all_flag_types + end + +end diff --git a/lib/guardian/post_guardian.rb b/lib/guardian/post_guardian.rb index cd274e0d05..83139fc851 100644 --- a/lib/guardian/post_guardian.rb +++ b/lib/guardian/post_guardian.rb @@ -10,13 +10,14 @@ module PostGuardian return false if (action_key == :notify_user && !is_staff? && opts[:is_warning].present? && opts[:is_warning] == 'true') taken = opts[:taken_actions].try(:keys).to_a - is_flag = PostActionType.is_flag?(action_key) + is_flag = PostActionType.flag_types_without_custom[action_key] already_taken_this_action = taken.any? && taken.include?(PostActionType.types[action_key]) - already_did_flagging = taken.any? && (taken & PostActionType.flag_types.values).any? + already_did_flagging = taken.any? && (taken & PostActionType.flag_types_without_custom.values).any? result = if authenticated? && post && !@user.anonymous? - return false if action_key == :notify_moderators && !SiteSetting.enable_private_messages + return false if [:notify_user, :notify_moderators].include?(action_key) && + !SiteSetting.enable_private_messages? # we allow flagging for trust level 1 and higher # always allowed for private messages @@ -37,9 +38,6 @@ module PostGuardian # new users can't notify_user because they are not allowed to send private messages not(action_key == :notify_user && !@user.has_trust_level?(SiteSetting.min_trust_to_send_messages)) && - # can't send private messages if they're disabled globally - not(action_key == :notify_user && !SiteSetting.enable_private_messages) && - # no voting more than once on single vote topics not(action_key == :vote && opts[:voted_in_topic] && post.topic.has_meta_data_boolean?(:single_vote)) end diff --git a/lib/plugin/instance.rb b/lib/plugin/instance.rb index db1c866baf..721441229f 100644 --- a/lib/plugin/instance.rb +++ b/lib/plugin/instance.rb @@ -90,6 +90,15 @@ class Plugin::Instance end end + def replace_flags + settings = ::FlagSettings.new + yield settings + + reloadable_patch do |plugin| + ::PostActionType.replace_flag_settings(settings) if plugin.enabled? + end + end + def whitelist_staff_user_custom_field(field) reloadable_patch do |plugin| ::User.register_plugin_staff_custom_field(field, plugin) if plugin.enabled? diff --git a/lib/post_revisor.rb b/lib/post_revisor.rb index f443a64d93..01f14a1f80 100644 --- a/lib/post_revisor.rb +++ b/lib/post_revisor.rb @@ -316,7 +316,7 @@ class PostRevisor def remove_flags_and_unhide_post return unless editing_a_flagged_and_hidden_post? - @post.post_actions.where(post_action_type_id: PostActionType.flag_types.values).each do |action| + @post.post_actions.where(post_action_type_id: PostActionType.flag_types_without_custom.values).each do |action| action.remove_act!(Discourse.system_user) end @post.unhide! diff --git a/plugins/discourse-narrative-bot/plugin.rb b/plugins/discourse-narrative-bot/plugin.rb index efca56e7e8..1f2d04984d 100644 --- a/plugins/discourse-narrative-bot/plugin.rb +++ b/plugins/discourse-narrative-bot/plugin.rb @@ -184,7 +184,7 @@ after_initialize do if self.user.enqueue_narrative_bot_job? input = case self.post_action_type_id - when *PostActionType.flag_types.values + when *PostActionType.flag_types_without_custom.values :flag when PostActionType.types[:like] :like diff --git a/spec/components/flag_settings_spec.rb b/spec/components/flag_settings_spec.rb new file mode 100644 index 0000000000..e82d572814 --- /dev/null +++ b/spec/components/flag_settings_spec.rb @@ -0,0 +1,46 @@ +require 'rails_helper' +require 'flag_settings' + +RSpec.describe FlagSettings do + + let(:settings) { FlagSettings.new } + + describe 'add' do + it 'will add a type' do + settings.add(3, :off_topic) + expect(settings.flag_types).to include(:off_topic) + expect(settings.is_flag?(:off_topic)).to eq(true) + expect(settings.is_flag?(:vote)).to eq(false) + + expect(settings.topic_flag_types).to be_empty + expect(settings.notify_types).to be_empty + expect(settings.auto_action_types).to be_empty + end + + it 'will add a topic type' do + settings.add(4, :inappropriate, topic_type: true) + expect(settings.flag_types).to include(:inappropriate) + expect(settings.topic_flag_types).to include(:inappropriate) + expect(settings.without_custom_types).to include(:inappropriate) + end + + it 'will add a notify type' do + settings.add(3, :off_topic, notify_type: true) + expect(settings.flag_types).to include(:off_topic) + expect(settings.notify_types).to include(:off_topic) + end + + it 'will add an auto action type' do + settings.add(7, :notify_moderators, auto_action_type: true) + expect(settings.flag_types).to include(:notify_moderators) + expect(settings.auto_action_types).to include(:notify_moderators) + end + + it 'will add a custom type' do + settings.add(7, :notify_user, custom_type: true) + expect(settings.flag_types).to include(:notify_user) + expect(settings.custom_types).to include(:notify_user) + expect(settings.without_custom_types).to be_empty + end + end +end diff --git a/spec/models/post_action_spec.rb b/spec/models/post_action_spec.rb index d1463b22c3..d54df92913 100644 --- a/spec/models/post_action_spec.rb +++ b/spec/models/post_action_spec.rb @@ -532,7 +532,7 @@ describe PostAction do it "prevents user to act twice at the same time" do # flags are already being tested - all_types_except_flags = PostActionType.types.except(PostActionType.flag_types) + all_types_except_flags = PostActionType.types.except(PostActionType.flag_types_without_custom) all_types_except_flags.values.each do |action| expect do PostAction.act(eviltrout, post, action) diff --git a/spec/serializers/post_serializer_spec.rb b/spec/serializers/post_serializer_spec.rb index 50f9f08c65..5e75727cfc 100644 --- a/spec/serializers/post_serializer_spec.rb +++ b/spec/serializers/post_serializer_spec.rb @@ -9,8 +9,7 @@ describe PostSerializer do let(:admin) { Fabricate(:admin) } let(:acted_ids) { PostActionType.public_types.values - .concat([:notify_user, :spam] - .map { |k| PostActionType.types[k] }) + .concat([:notify_user, :spam].map { |k| PostActionType.types[k] }) } def visible_actions_for(user) From 7cfb8ed5c16c0b7bb92ff19bd8ccd7e67b977512 Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Thu, 19 Oct 2017 14:11:31 -0400 Subject: [PATCH 048/174] FIX: You can't notify a user on a flag topic --- app/models/post_action_type.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/post_action_type.rb b/app/models/post_action_type.rb index 9bb75363b7..4d8e228214 100644 --- a/app/models/post_action_type.rb +++ b/app/models/post_action_type.rb @@ -41,7 +41,7 @@ class PostActionType < ActiveRecord::Base @flag_settings.add( 6, :notify_user, - topic_type: true, + topic_type: false, notify_type: true, custom_type: true ) From a9f718fe57ce29bb4865ed25593b4a1edf45a555 Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Thu, 19 Oct 2017 14:27:38 -0400 Subject: [PATCH 049/174] FIX: Custom flags didn't work when flagging a topic --- .../discourse/controllers/flag.js.es6 | 18 ++++++++---------- app/serializers/site_serializer.rb | 4 ++-- app/serializers/topic_flag_type_serializer.rb | 2 +- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/app/assets/javascripts/discourse/controllers/flag.js.es6 b/app/assets/javascripts/discourse/controllers/flag.js.es6 index 947c8fe153..00627a2ef8 100644 --- a/app/assets/javascripts/discourse/controllers/flag.js.es6 +++ b/app/assets/javascripts/discourse/controllers/flag.js.es6 @@ -53,19 +53,17 @@ export default Ember.Controller.extend(ModalFunctionality, { return flagsAvailable; } else { // flagging topic - const self = this, - lookup = Em.Object.create(); - - _.each(this.get("model.actions_summary"),function(a) { - a.flagTopic = self.get('model'); - a.actionType = self.site.topicFlagTypeById(a.id); - const actionSummary = ActionSummary.create(a); - lookup.set(a.actionType.get('name_key'), actionSummary); + let lookup = Em.Object.create(); + let model = this.get('model'); + model.get('actions_summary').forEach(a => { + a.flagTopic = model; + a.actionType = this.site.topicFlagTypeById(a.id); + lookup.set(a.actionType.get('name_key'), ActionSummary.create(a)); }); this.set('topicActionByName', lookup); - return this.site.get('topic_flag_types').filter(function(item) { - return _.any(self.get("model.actions_summary"), function(a) { + return this.site.get('topic_flag_types').filter(item => { + return _.any(this.get("model.actions_summary"), a => { return (a.id === item.get('id') && a.can_act); }); }); diff --git a/app/serializers/site_serializer.rb b/app/serializers/site_serializer.rb index 6216711d52..850a8dd8dd 100644 --- a/app/serializers/site_serializer.rb +++ b/app/serializers/site_serializer.rb @@ -59,8 +59,8 @@ class SiteSerializer < ApplicationSerializer def topic_flag_types cache_fragment("post_action_flag_types_#{I18n.locale}") do - flags = PostActionType.ordered.where(id: PostActionType.topic_flag_types.values) - ActiveModel::ArraySerializer.new(flags, each_serializer: TopicFlagTypeSerializer).as_json + types = PostActionType.topic_flag_types.values.map { |id| PostActionType.new(id: id) } + ActiveModel::ArraySerializer.new(types, each_serializer: TopicFlagTypeSerializer).as_json end end diff --git a/app/serializers/topic_flag_type_serializer.rb b/app/serializers/topic_flag_type_serializer.rb index ee81618448..c0ac6951bd 100644 --- a/app/serializers/topic_flag_type_serializer.rb +++ b/app/serializers/topic_flag_type_serializer.rb @@ -3,7 +3,7 @@ class TopicFlagTypeSerializer < PostActionTypeSerializer protected def i18n(field, vars = nil) - key = "topic_flag_types.#{object.name_key}.#{field}" + key = "topic_flag_types.#{name_key}.#{field}" vars ? I18n.t(key, vars) : I18n.t(key) end From ae1743c61f3006b72d80dcb10be7d6862ce36b79 Mon Sep 17 00:00:00 2001 From: Joffrey JAFFEUX Date: Thu, 19 Oct 2017 12:51:08 -0700 Subject: [PATCH 050/174] [WIP] select-box-kit refactoring --- .eslintrc | 1 + .../admin/components/list-setting.js.es6 | 2 - .../admin/templates/badges-show.hbs | 7 +- .../components/admin-user-field-item.hbs | 2 +- .../templates/components/embeddable-host.hbs | 2 +- .../admin/templates/customize-themes-show.hbs | 10 +- .../admin/templates/groups-bulk.hbs | 2 +- .../templates/logs/staff-action-logs.hbs | 2 +- .../modal/admin-color-scheme-select-base.hbs | 1 - .../javascripts/admin/templates/reports.hbs | 4 +- .../admin/templates/user-badges.hbs | 2 +- .../admin/templates/user-index.hbs | 3 +- .../admin/templates/web-hooks-show.hbs | 2 - app/assets/javascripts/application.js | 1 + .../components/combo-box.js.es6 | 146 ------ .../categories-admin-dropdown.js.es6 | 49 -- .../components/category-chooser.js.es6 | 88 ---- .../category-notifications-button.js.es6 | 28 - .../components/category-select-box.js.es6 | 162 ------ .../category-select-box-row.js.es6 | 13 - .../components/dropdown-select-box.js.es6 | 33 -- .../dropdown-header.js.es6 | 7 - .../future-date-input-selector.js.es6 | 207 -------- .../components/future-date-input.js.es6 | 2 +- .../group-notifications-button.js.es6 | 2 +- .../components/notifications-button.js.es6 | 68 --- .../components/pinned-options.js.es6 | 73 --- .../discourse/components/select-box.js.es6 | 485 ------------------ .../select-box/select-box-collection.js.es6 | 9 - .../select-box/select-box-filter.js.es6 | 5 - .../select-box/select-box-header.js.es6 | 24 - .../select-box/select-box-row.js.es6 | 47 -- .../tag-notifications-button.js.es6 | 24 - .../topic-footer-mobile-dropdown.js.es6 | 78 --- .../topic-notifications-button.js.es6 | 9 - .../topic-notifications-options.js.es6 | 52 -- .../discourse/lib/safari-hacks.js.es6 | 2 +- .../templates/components/badge-title.hbs | 2 +- .../dropdown-select-box/dropdown-header.hbs | 12 - .../components/edit-category-general.hbs | 4 +- .../components/edit-category-settings.hbs | 10 +- .../components/edit-topic-timer-form.hbs | 2 +- .../components/future-date-input.hbs | 3 - .../templates/components/queued-post.hbs | 2 +- .../templates/components/select-box.hbs | 55 -- .../select-box/select-box-collection.hbs | 28 - .../select-box/select-box-header.hbs | 9 - .../components/select-box/select-box-row.hbs | 1 - .../discourse/templates/composer.hbs | 2 +- .../discourse/templates/full-page-search.hbs | 2 +- .../templates/modal/bulk-change-category.hbs | 2 +- .../discourse/templates/modal/split-topic.hbs | 2 +- .../templates/preferences/card-badge.hbs | 2 +- .../templates/preferences/emails.hbs | 4 +- .../templates/preferences/interface.hbs | 4 +- .../javascripts/discourse/templates/topic.hbs | 2 +- .../categories-admin-dropdown.js.es6 | 50 ++ .../components/category-chooser.js.es6 | 131 +++++ .../category-notifications-button.js.es6 | 17 + ...ategory-notifications-button-header.js.es6 | 13 + .../components/combo-box.js.es6 | 21 + .../combo-box/combo-box-header.js.es6 | 39 ++ .../components/dropdown-select-box.js.es6 | 24 + .../dropdown-select-box-header.js.es6 | 6 + .../dropdown-select-box-row.js.es6 | 9 + .../future-date-input-selector.js.es6 | 118 +++++ .../future-date-input-selector-header.js.es6 | 14 + .../future-date-input-selector-row.js.es6 | 14 + .../future-date-input-selector/mixin.js.es6 | 101 ++++ .../components/multi-combo-box.js.es6 | 99 ++++ .../multi-combo-box-header.js.es6 | 40 ++ .../components/notifications-button.js.es6 | 40 ++ .../notifications-button-header.js.es6 | 24 + .../notifications-button-row.js.es6 | 37 ++ .../components/pinned-button.js.es6 | 9 +- .../components/pinned-options.js.es6 | 64 +++ .../pinned-options-header.js.es6 | 30 ++ .../components/select-box-kit.js.es6 | 440 ++++++++++++++++ .../select-box-kit-collection.js.es6 | 5 + .../select-box-kit-create-row.js.es6 | 10 + .../select-box-kit-filter.js.es6 | 6 + .../select-box-kit-header.js.es6 | 28 + .../select-box-kit-none-row.js.es6 | 10 + .../select-box-kit/select-box-kit-row.js.es6 | 59 +++ .../tag-notifications-button.js.es6 | 15 + .../tag-notifications-button-header.js.es6 | 13 + .../topic-footer-mobile-dropdown.js.es6 | 75 +++ .../topic-notifications-button.js.es6 | 6 + .../topic-notifications-options.js.es6 | 40 ++ .../select-box-kit/mixins/dom-helpers.js.es6 | 53 ++ .../select-box-kit/mixins/keyboard.js.es6 | 196 +++++++ .../select-box-kit/mixins/utils.js.es6 | 9 + .../components/combo-box/combo-box-header.hbs | 15 + .../dropdown-select-box-header.hbs | 17 + .../dropdown-select-box-row.hbs | 15 + .../future-date-input-selector-header.hbs | 23 + .../future-date-input-selector-row.hbs | 13 + .../multi-combo-box-header.hbs | 30 ++ .../templates/components/pinned-button.hbs | 0 .../templates/components/select-box-kit.hbs | 65 +++ .../select-box-kit-collection.hbs | 49 ++ .../select-box-kit/select-box-kit-filter.hbs} | 5 +- .../select-box-kit/select-box-kit-header.hbs | 7 + .../select-box-kit/select-box-kit-row.hbs | 9 + .../components/topic-notifications-button.hbs | 0 app/assets/javascripts/wizard-application.js | 1 + .../templates/components/invite-list.hbs | 2 +- .../components/wizard-field-dropdown.hbs | 1 - app/assets/stylesheets/common.scss | 1 + .../stylesheets/common/admin/admin_base.scss | 10 +- .../stylesheets/common/base/_topic-list.scss | 2 +- .../stylesheets/common/base/combobox.scss | 88 ---- .../base/edit-topic-status-update-modal.scss | 4 + app/assets/stylesheets/common/base/modal.scss | 14 +- .../components/categories-admin-dropdown.scss | 6 - .../components/category-select-box.scss | 41 -- .../components/dropdown-select-box.scss | 103 ---- .../future-date-input-selector.scss | 9 - .../components/notifications-button.scss | 12 - .../categories-admin-dropdown.scss | 12 + .../select-box-kit/category-chooser.scss | 46 ++ .../common/select-box-kit/combo-box.scss | 85 +++ .../select-box-kit/dropdown-select-box.scss | 153 ++++++ .../future-date-input-selector.scss | 22 + .../common/select-box-kit/multi-combobox.scss | 131 +++++ .../select-box-kit/notifications-button.scss | 17 + .../pinned-button.scss | 16 +- .../select-box-kit.scss} | 170 +++--- .../topic-notifications-button.scss | 12 +- app/assets/stylesheets/desktop/compose.scss | 6 +- app/assets/stylesheets/desktop/modal.scss | 6 +- app/assets/stylesheets/desktop/topic.scss | 2 +- app/assets/stylesheets/mobile/compose.scss | 2 +- app/assets/stylesheets/mobile/topic-list.scss | 6 +- app/assets/stylesheets/mobile/topic.scss | 2 +- app/assets/stylesheets/wizard.scss | 9 +- config/locales/client.en.yml | 2 +- plugins/discourse-narrative-bot/public/public | 1 + .../acceptance/admin-suspend-user-test.js.es6 | 6 +- ...st.js.es6 => category-chooser-test.js.es6} | 9 +- .../acceptance/category-edit-test.js.es6 | 4 +- .../acceptance/category-hashtag-test.js.es6 | 2 +- .../acceptance/queued-posts-test.js.es6 | 4 +- .../acceptance/search-full-test.js.es6 | 22 +- .../javascripts/acceptance/search-test.js.es6 | 48 +- .../topic-notifications-button-test.js.es6 | 11 +- test/javascripts/acceptance/topic-test.js.es6 | 6 +- .../categories-admin-dropdown-test.js.es6 | 19 + .../components/category-chooser-test.js.es6 | 139 +++++ .../components/combo-box-test.js.es6 | 153 +++++- .../dropdown-select-box-test.js.es6 | 23 - .../components/multi-combo-box-test.js.es6 | 17 + .../components/pinned-button-test.js.es6 | 4 +- .../components/select-box-test.js.es6 | 189 +++---- .../topic-footer-mobile-dropdown-test.js.es6 | 28 + .../topic-notifications-button-test.js.es6 | 4 +- test/javascripts/helpers/select-box-helper.js | 69 ++- 157 files changed, 3262 insertions(+), 2404 deletions(-) delete mode 100644 app/assets/javascripts/discourse-common/components/combo-box.js.es6 delete mode 100644 app/assets/javascripts/discourse/components/categories-admin-dropdown.js.es6 delete mode 100644 app/assets/javascripts/discourse/components/category-chooser.js.es6 delete mode 100644 app/assets/javascripts/discourse/components/category-notifications-button.js.es6 delete mode 100644 app/assets/javascripts/discourse/components/category-select-box.js.es6 delete mode 100644 app/assets/javascripts/discourse/components/category-select-box/category-select-box-row.js.es6 delete mode 100644 app/assets/javascripts/discourse/components/dropdown-select-box.js.es6 delete mode 100644 app/assets/javascripts/discourse/components/dropdown-select-box/dropdown-header.js.es6 delete mode 100644 app/assets/javascripts/discourse/components/future-date-input-selector.js.es6 delete mode 100644 app/assets/javascripts/discourse/components/notifications-button.js.es6 delete mode 100644 app/assets/javascripts/discourse/components/pinned-options.js.es6 delete mode 100644 app/assets/javascripts/discourse/components/select-box.js.es6 delete mode 100644 app/assets/javascripts/discourse/components/select-box/select-box-collection.js.es6 delete mode 100644 app/assets/javascripts/discourse/components/select-box/select-box-filter.js.es6 delete mode 100644 app/assets/javascripts/discourse/components/select-box/select-box-header.js.es6 delete mode 100644 app/assets/javascripts/discourse/components/select-box/select-box-row.js.es6 delete mode 100644 app/assets/javascripts/discourse/components/tag-notifications-button.js.es6 delete mode 100644 app/assets/javascripts/discourse/components/topic-footer-mobile-dropdown.js.es6 delete mode 100644 app/assets/javascripts/discourse/components/topic-notifications-button.js.es6 delete mode 100644 app/assets/javascripts/discourse/components/topic-notifications-options.js.es6 delete mode 100644 app/assets/javascripts/discourse/templates/components/dropdown-select-box/dropdown-header.hbs delete mode 100644 app/assets/javascripts/discourse/templates/components/select-box.hbs delete mode 100644 app/assets/javascripts/discourse/templates/components/select-box/select-box-collection.hbs delete mode 100644 app/assets/javascripts/discourse/templates/components/select-box/select-box-header.hbs delete mode 100644 app/assets/javascripts/discourse/templates/components/select-box/select-box-row.hbs create mode 100644 app/assets/javascripts/select-box-kit/components/categories-admin-dropdown.js.es6 create mode 100644 app/assets/javascripts/select-box-kit/components/category-chooser.js.es6 create mode 100644 app/assets/javascripts/select-box-kit/components/category-notifications-button.js.es6 create mode 100644 app/assets/javascripts/select-box-kit/components/category-notifications-button/category-notifications-button-header.js.es6 create mode 100644 app/assets/javascripts/select-box-kit/components/combo-box.js.es6 create mode 100644 app/assets/javascripts/select-box-kit/components/combo-box/combo-box-header.js.es6 create mode 100644 app/assets/javascripts/select-box-kit/components/dropdown-select-box.js.es6 create mode 100644 app/assets/javascripts/select-box-kit/components/dropdown-select-box/dropdown-select-box-header.js.es6 create mode 100644 app/assets/javascripts/select-box-kit/components/dropdown-select-box/dropdown-select-box-row.js.es6 create mode 100644 app/assets/javascripts/select-box-kit/components/future-date-input-selector.js.es6 create mode 100644 app/assets/javascripts/select-box-kit/components/future-date-input-selector/future-date-input-selector-header.js.es6 create mode 100644 app/assets/javascripts/select-box-kit/components/future-date-input-selector/future-date-input-selector-row.js.es6 create mode 100644 app/assets/javascripts/select-box-kit/components/future-date-input-selector/mixin.js.es6 create mode 100644 app/assets/javascripts/select-box-kit/components/multi-combo-box.js.es6 create mode 100644 app/assets/javascripts/select-box-kit/components/multi-combo-box/multi-combo-box-header.js.es6 create mode 100644 app/assets/javascripts/select-box-kit/components/notifications-button.js.es6 create mode 100644 app/assets/javascripts/select-box-kit/components/notifications-button/notifications-button-header.js.es6 create mode 100644 app/assets/javascripts/select-box-kit/components/notifications-button/notifications-button-row.js.es6 rename app/assets/javascripts/{discourse => select-box-kit}/components/pinned-button.js.es6 (75%) create mode 100644 app/assets/javascripts/select-box-kit/components/pinned-options.js.es6 create mode 100644 app/assets/javascripts/select-box-kit/components/pinned-options/pinned-options-header.js.es6 create mode 100644 app/assets/javascripts/select-box-kit/components/select-box-kit.js.es6 create mode 100644 app/assets/javascripts/select-box-kit/components/select-box-kit/select-box-kit-collection.js.es6 create mode 100644 app/assets/javascripts/select-box-kit/components/select-box-kit/select-box-kit-create-row.js.es6 create mode 100644 app/assets/javascripts/select-box-kit/components/select-box-kit/select-box-kit-filter.js.es6 create mode 100644 app/assets/javascripts/select-box-kit/components/select-box-kit/select-box-kit-header.js.es6 create mode 100644 app/assets/javascripts/select-box-kit/components/select-box-kit/select-box-kit-none-row.js.es6 create mode 100644 app/assets/javascripts/select-box-kit/components/select-box-kit/select-box-kit-row.js.es6 create mode 100644 app/assets/javascripts/select-box-kit/components/tag-notifications-button.js.es6 create mode 100644 app/assets/javascripts/select-box-kit/components/tag-notifications-button/tag-notifications-button-header.js.es6 create mode 100644 app/assets/javascripts/select-box-kit/components/topic-footer-mobile-dropdown.js.es6 create mode 100644 app/assets/javascripts/select-box-kit/components/topic-notifications-button.js.es6 create mode 100644 app/assets/javascripts/select-box-kit/components/topic-notifications-options.js.es6 create mode 100644 app/assets/javascripts/select-box-kit/mixins/dom-helpers.js.es6 create mode 100644 app/assets/javascripts/select-box-kit/mixins/keyboard.js.es6 create mode 100644 app/assets/javascripts/select-box-kit/mixins/utils.js.es6 create mode 100644 app/assets/javascripts/select-box-kit/templates/components/combo-box/combo-box-header.hbs create mode 100644 app/assets/javascripts/select-box-kit/templates/components/dropdown-select-box/dropdown-select-box-header.hbs create mode 100644 app/assets/javascripts/select-box-kit/templates/components/dropdown-select-box/dropdown-select-box-row.hbs create mode 100644 app/assets/javascripts/select-box-kit/templates/components/future-date-input-selector/future-date-input-selector-header.hbs create mode 100644 app/assets/javascripts/select-box-kit/templates/components/future-date-input-selector/future-date-input-selector-row.hbs create mode 100644 app/assets/javascripts/select-box-kit/templates/components/multi-combo-box/multi-combo-box-header.hbs rename app/assets/javascripts/{discourse => select-box-kit}/templates/components/pinned-button.hbs (100%) create mode 100644 app/assets/javascripts/select-box-kit/templates/components/select-box-kit.hbs create mode 100644 app/assets/javascripts/select-box-kit/templates/components/select-box-kit/select-box-kit-collection.hbs rename app/assets/javascripts/{discourse/templates/components/select-box/select-box-filter.hbs => select-box-kit/templates/components/select-box-kit/select-box-kit-filter.hbs} (71%) create mode 100644 app/assets/javascripts/select-box-kit/templates/components/select-box-kit/select-box-kit-header.hbs create mode 100644 app/assets/javascripts/select-box-kit/templates/components/select-box-kit/select-box-kit-row.hbs rename app/assets/javascripts/{discourse => select-box-kit}/templates/components/topic-notifications-button.hbs (100%) delete mode 100644 app/assets/stylesheets/common/base/combobox.scss delete mode 100644 app/assets/stylesheets/common/components/categories-admin-dropdown.scss delete mode 100644 app/assets/stylesheets/common/components/category-select-box.scss delete mode 100644 app/assets/stylesheets/common/components/dropdown-select-box.scss delete mode 100644 app/assets/stylesheets/common/components/future-date-input-selector.scss delete mode 100644 app/assets/stylesheets/common/components/notifications-button.scss create mode 100644 app/assets/stylesheets/common/select-box-kit/categories-admin-dropdown.scss create mode 100644 app/assets/stylesheets/common/select-box-kit/category-chooser.scss create mode 100644 app/assets/stylesheets/common/select-box-kit/combo-box.scss create mode 100644 app/assets/stylesheets/common/select-box-kit/dropdown-select-box.scss create mode 100644 app/assets/stylesheets/common/select-box-kit/future-date-input-selector.scss create mode 100644 app/assets/stylesheets/common/select-box-kit/multi-combobox.scss create mode 100644 app/assets/stylesheets/common/select-box-kit/notifications-button.scss rename app/assets/stylesheets/common/{components => select-box-kit}/pinned-button.scss (56%) rename app/assets/stylesheets/common/{components/select-box.scss => select-box-kit/select-box-kit.scss} (62%) rename app/assets/stylesheets/common/{components => select-box-kit}/topic-notifications-button.scss (58%) create mode 120000 plugins/discourse-narrative-bot/public/public rename test/javascripts/acceptance/{category-select-box-test.js.es6 => category-chooser-test.js.es6} (60%) create mode 100644 test/javascripts/components/categories-admin-dropdown-test.js.es6 create mode 100644 test/javascripts/components/category-chooser-test.js.es6 delete mode 100644 test/javascripts/components/dropdown-select-box-test.js.es6 create mode 100644 test/javascripts/components/multi-combo-box-test.js.es6 create mode 100644 test/javascripts/components/topic-footer-mobile-dropdown-test.js.es6 diff --git a/.eslintrc b/.eslintrc index 19d38e35c7..71e21b4381 100644 --- a/.eslintrc +++ b/.eslintrc @@ -46,6 +46,7 @@ "expandSelectBox":true, "collapseSelectBox":true, "selectBoxSelectRow":true, + "selectBoxSelectNoneRow":true, "selectBoxFillInFilter":true, "asyncTestDiscourse":true, "fixture":true, diff --git a/app/assets/javascripts/admin/components/list-setting.js.es6 b/app/assets/javascripts/admin/components/list-setting.js.es6 index da6c5173d6..9a1d865133 100644 --- a/app/assets/javascripts/admin/components/list-setting.js.es6 +++ b/app/assets/javascripts/admin/components/list-setting.js.es6 @@ -50,5 +50,3 @@ export default Ember.Component.extend({ }); } }); - - diff --git a/app/assets/javascripts/admin/templates/badges-show.hbs b/app/assets/javascripts/admin/templates/badges-show.hbs index e40e6bfec0..c9fe0496c1 100644 --- a/app/assets/javascripts/admin/templates/badges-show.hbs +++ b/app/assets/javascripts/admin/templates/badges-show.hbs @@ -26,9 +26,7 @@ {{combo-box name="badge_type_id" value=buffered.badge_type_id content=badgeTypes - optionValuePath="content.id" - optionLabelPath="content.name" - disabled=readOnly}} + isDisabled=readOnly}}
    @@ -36,8 +34,7 @@ {{combo-box name="badge_grouping_id" value=buffered.badge_grouping_id content=badgeGroupings - optionValuePath="content.id" - optionLabelPath="content.displayName"}} + nameProperty="name"}}  
    diff --git a/app/assets/javascripts/admin/templates/components/admin-user-field-item.hbs b/app/assets/javascripts/admin/templates/components/admin-user-field-item.hbs index c5620d84ed..419ef0b69c 100644 --- a/app/assets/javascripts/admin/templates/components/admin-user-field-item.hbs +++ b/app/assets/javascripts/admin/templates/components/admin-user-field-item.hbs @@ -1,6 +1,6 @@ {{#if editing}} {{#admin-form-row label="admin.user_fields.type"}} - {{combo-box content=fieldTypes valueAttribute="id" value=buffered.field_type}} + {{combo-box content=fieldTypes value=buffered.field_type}} {{/admin-form-row}} {{#admin-form-row label="admin.user_fields.name"}} diff --git a/app/assets/javascripts/admin/templates/components/embeddable-host.hbs b/app/assets/javascripts/admin/templates/components/embeddable-host.hbs index 88317ecd18..e21685831b 100644 --- a/app/assets/javascripts/admin/templates/components/embeddable-host.hbs +++ b/app/assets/javascripts/admin/templates/components/embeddable-host.hbs @@ -9,7 +9,7 @@ {{input value=buffered.path_whitelist placeholder="/blog/.*" enter="save" class="path-whitelist"}} - {{category-select-box value=categoryId class="small"}} + {{category-chooser value=categoryId class="small"}} {{d-button icon="check" action="save" class="btn-primary" disabled=cantSave}} diff --git a/app/assets/javascripts/admin/templates/customize-themes-show.hbs b/app/assets/javascripts/admin/templates/customize-themes-show.hbs index 6b02429d77..6f8ac614aa 100644 --- a/app/assets/javascripts/admin/templates/customize-themes-show.hbs +++ b/app/assets/javascripts/admin/templates/customize-themes-show.hbs @@ -36,8 +36,7 @@

    {{i18n "admin.customize.theme.color_scheme"}}

    {{i18n "admin.customize.theme.color_scheme_select"}}

    -

    {{select-box content=colorSchemes - textKey="name" +

    {{combo-box content=colorSchemes filterable=true value=colorSchemeId icon="paint-brush"}} @@ -123,11 +122,8 @@ {{/unless}} {{#if selectableChildThemes}} -

    {{combo-box content=selectableChildThemes - nameProperty="name" - value=selectedChildThemeId - valueAttribute="id"}} - +

    + {{combo-box content=selectableChildThemes value=selectedChildThemeId}} {{#d-button action="addChildTheme" icon="plus"}}{{i18n "admin.customize.theme.add"}}{{/d-button}}

    {{/if}} diff --git a/app/assets/javascripts/admin/templates/groups-bulk.hbs b/app/assets/javascripts/admin/templates/groups-bulk.hbs index baf3a63cda..337ab37cb3 100644 --- a/app/assets/javascripts/admin/templates/groups-bulk.hbs +++ b/app/assets/javascripts/admin/templates/groups-bulk.hbs @@ -6,7 +6,7 @@
    - {{combo-box content=groups valueAttribute="id" value=groupId none="admin.groups.bulk_select"}} + {{combo-box filterable=true content=groups value=groupId none="admin.groups.bulk_select"}}
    diff --git a/app/assets/javascripts/admin/templates/logs/staff-action-logs.hbs b/app/assets/javascripts/admin/templates/logs/staff-action-logs.hbs index 50f02f9f5b..c44edc1a3e 100644 --- a/app/assets/javascripts/admin/templates/logs/staff-action-logs.hbs +++ b/app/assets/javascripts/admin/templates/logs/staff-action-logs.hbs @@ -30,7 +30,7 @@ {{/if}}
    {{else}} - {{i18n "admin.logs.staff_actions.filter"}} {{combo-box content=userHistoryActions nameProperty="name" value=filterActionId none="admin.logs.staff_actions.all"}} + {{i18n "admin.logs.staff_actions.filter"}} {{combo-box content=userHistoryActions value=filterActionId none="admin.logs.staff_actions.all"}} {{/if}}
    diff --git a/app/assets/javascripts/admin/templates/modal/admin-color-scheme-select-base.hbs b/app/assets/javascripts/admin/templates/modal/admin-color-scheme-select-base.hbs index d58a63c134..1316942634 100644 --- a/app/assets/javascripts/admin/templates/modal/admin-color-scheme-select-base.hbs +++ b/app/assets/javascripts/admin/templates/modal/admin-color-scheme-select-base.hbs @@ -2,7 +2,6 @@ {{#d-modal-body title="admin.customize.colors.select_base.title"}} {{i18n "admin.customize.colors.select_base.description"}} {{combo-box content=model - nameProperty="name" value=selectedBaseThemeId valueAttribute="base_scheme_id"}} {{/d-modal-body}} diff --git a/app/assets/javascripts/admin/templates/reports.hbs b/app/assets/javascripts/admin/templates/reports.hbs index 57acba8f50..3927d1e384 100644 --- a/app/assets/javascripts/admin/templates/reports.hbs +++ b/app/assets/javascripts/admin/templates/reports.hbs @@ -4,10 +4,10 @@ {{i18n 'admin.dashboard.reports.start_date'}} {{date-picker-past value=startDate defaultDate=startDate}} {{i18n 'admin.dashboard.reports.end_date'}} {{date-picker-past value=endDate defaultDate=endDate}} {{#if showCategoryOptions}} - {{combo-box valueAttribute="value" content=categoryOptions value=categoryId}} + {{combo-box filterable=true valueAttribute="value" content=categoryOptions value=categoryId}} {{/if}} {{#if showGroupOptions}} - {{combo-box valueAttribute="value" content=groupOptions value=groupId}} + {{combo-box filterable=true valueAttribute="value" content=groupOptions value=groupId}} {{/if}} {{d-button action="refreshReport" class="btn-primary" label="admin.dashboard.reports.refresh_report" icon="refresh"}} {{d-button action="exportCsv" label="admin.export_csv.button_text" icon="download"}} diff --git a/app/assets/javascripts/admin/templates/user-badges.hbs b/app/assets/javascripts/admin/templates/user-badges.hbs index 9977aca39d..1f6bae617c 100644 --- a/app/assets/javascripts/admin/templates/user-badges.hbs +++ b/app/assets/javascripts/admin/templates/user-badges.hbs @@ -16,7 +16,7 @@
    - {{combo-box valueAttribute="id" value=selectedBadgeId content=grantableBadges nameProperty="name"}} + {{combo-box filterable=true value=selectedBadgeId content=grantableBadges}}