From bc96eb864bb6803f167f91e04f513e0cebd4714d Mon Sep 17 00:00:00 2001 From: evazion Date: Tue, 16 Nov 2021 17:09:06 -0600 Subject: [PATCH] votes: make private favorites and upvotes a Gold-only option. Make private favorites and upvotes a Gold-only account option. Existing Members with private favorites enabled are allowed to keep it enabled, as long as they don't disable it. If they disable it, then they can't re-enable it again without upgrading to Gold first. This is a Gold-only option to prevent uploaders from creating multiple accounts to upvote their own posts. If private upvotes were allowed for Members, then it would be too easy to use fake accounts and private upvotes to upvote your own posts. --- app/logical/concerns/has_bit_flags.rb | 9 ++++++++ app/models/user.rb | 9 ++++++++ app/policies/user_policy.rb | 4 ++++ app/views/users/edit.html.erb | 8 ++++++- test/functional/users_controller_test.rb | 28 ++++++++++++++++++++++++ 5 files changed, 57 insertions(+), 1 deletion(-) diff --git a/app/logical/concerns/has_bit_flags.rb b/app/logical/concerns/has_bit_flags.rb index c05b6d54a..9364c042f 100644 --- a/app/logical/concerns/has_bit_flags.rb +++ b/app/logical/concerns/has_bit_flags.rb @@ -6,6 +6,7 @@ module HasBitFlags def has_bit_flags(attributes, field: :bit_flags) attributes.each.with_index do |attribute, i| bit_flag = 1 << i + field_was = "#{field}_was" define_method(attribute) do send(field) & bit_flag > 0 @@ -15,6 +16,14 @@ module HasBitFlags send(field) & bit_flag > 0 end + define_method("#{attribute}_was") do + send(field_was) & bit_flag > 0 + end + + define_method("#{attribute}_was?") do + send(field_was) & bit_flag > 0 + end + define_method("#{attribute}=") do |val| if val.to_s =~ /[t1y]/ send("#{field}=", send(field) | bit_flag) diff --git a/app/models/user.rb b/app/models/user.rb index 4ff18780c..74b21d966 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -103,6 +103,7 @@ class User < ApplicationRecord validates :per_page, inclusion: { in: (1..PostSets::Post::MAX_PER_PAGE) } validates :password, confirmation: true validates :comment_threshold, inclusion: { in: (-100..5) } + validate :validate_enable_private_favorites, on: :update before_validation :normalize_blacklisted_tags before_create :promote_to_owner_if_first_user has_many :artist_versions, foreign_key: :updater_id @@ -192,6 +193,14 @@ class User < ApplicationRecord end end + concerning :ValidationMethods do + def validate_enable_private_favorites + if enable_private_favorites_was == false && enable_private_favorites == true && !Pundit.policy!(self, self).can_enable_private_favorites? + errors.add(:base, "Can't enable privacy mode without a Gold account") + end + end + end + concerning :AuthenticationMethods do def password=(new_password) @password = new_password diff --git a/app/policies/user_policy.rb b/app/policies/user_policy.rb index ebf97d350..75ae4deb5 100644 --- a/app/policies/user_policy.rb +++ b/app/policies/user_policy.rb @@ -31,6 +31,10 @@ class UserPolicy < ApplicationPolicy user.is_admin? || record.id == user.id || !record.enable_private_favorites? end + def can_enable_private_favorites? + user.is_gold? + end + def permitted_attributes_for_create [:name, :password, :password_confirmation, { email_address_attributes: [:address] }] end diff --git a/app/views/users/edit.html.erb b/app/views/users/edit.html.erb index fe556b763..0d7487658 100644 --- a/app/views/users/edit.html.erb +++ b/app/views/users/edit.html.erb @@ -63,7 +63,13 @@ <%= f.input :hide_deleted_posts, :as => :select, :label => "Deleted post filter", :hint => "Remove deleted posts from search results", :include_blank => false, :collection => [["Yes", "true"], ["No", "false"]] %> <%= f.input :show_deleted_children, :as => :select, :label => "Show deleted children", :hint => "Show thumbnail borders on parent posts even if the children are deleted", :include_blank => false, :collection => [["Yes", "true"], ["No", "false"]] %> <%= f.input :disable_categorized_saved_searches, :hint => "Don't show dialog box when creating a new saved search", :as => :select, :collection => [["No", "false"], ["Yes", "true"]], :include_blank => false %> - <%= f.input :enable_private_favorites, :label => "Private favorites and votes", :as => :select, :hint => "Make your favorites and upvotes private", :collection => [["No", "false"], ["Yes", "true"]], :include_blank => false %> + <% if policy(@user).can_enable_private_favorites? %> + <%= f.input :enable_private_favorites, label: "Private favorites and votes", as: :select, hint: "Make your favorites and upvotes private", collection: { "No" => false, "Yes" => true }, include_blank: false %> + <% elsif @user.enable_private_favorites? %> + <%= f.input :enable_private_favorites, label: "Private favorites and votes", as: :select, hint: "Make your favorites and upvotes private. (Warning: if you disable this, you can't re-enable it without ".html_safe + link_to("upgrading to a Gold account", new_user_upgrade_path) + " first. (".html_safe + link_to_wiki("learn more", "help:privacy_mode") + ")".html_safe, collection: { "No" => false, "Yes" => true }, include_blank: false %> + <% else %> + <%= f.input :enable_private_favorites, label: "Private favorites and votes", as: :select, hint: link_to("Upgrade to Danbooru Gold to enable private favorites and upvotes", new_user_upgrade_path), collection: { "No" => false, "Yes" => true }, include_blank: false %> + <% end %> <%= f.input :disable_tagged_filenames, :as => :select, :hint => "Don't include tags in image filenames", :collection => [["No", "false"], ["Yes", "true"]], :include_blank => false %> <%= f.input :disable_mobile_gestures, :as => :select, :hint => "Disable swipe left / swipe right gestures on mobile", :collection => [["No", "false"], ["Yes", "true"]], :include_blank => false %> <%= f.input :disable_post_tooltips, :as => :select, :hint => "Disable advanced tooltips when hovering over thumbnails", :collection => [["No", "false"], ["Yes", "true"]], :include_blank => false %> diff --git a/test/functional/users_controller_test.rb b/test/functional/users_controller_test.rb index e71d27c12..8053f0ddb 100644 --- a/test/functional/users_controller_test.rb +++ b/test/functional/users_controller_test.rb @@ -420,6 +420,34 @@ class UsersControllerTest < ActionDispatch::IntegrationTest assert_equal("xyz", @user.favorite_tags) end + context "for a Member-level user" do + should "allow disabling the private favorites option" do + @user = create(:user, enable_private_favorites: true) + put_auth user_path(@user), @user, params: { user: { enable_private_favorites: false }} + + assert_redirected_to edit_user_path(@user) + assert_equal(false, @user.reload.enable_private_favorites) + end + + should "not allow enabling the private favorites option" do + @user = create(:user, enable_private_favorites: false) + put_auth user_path(@user), @user, params: { user: { enable_private_favorites: true }} + + assert_redirected_to edit_user_path(@user) + assert_equal(false, @user.reload.enable_private_favorites) + end + end + + context "for a Gold-level user" do + should "allow enabling the private favorites option" do + @user = create(:gold_user, enable_private_favorites: false) + put_auth user_path(@user), @user, params: { user: { enable_private_favorites: true }} + + assert_redirected_to edit_user_path(@user) + assert_equal(true, @user.reload.enable_private_favorites) + end + end + context "changing the level" do should "not work" do @owner = create(:owner_user)