Add per-user maximum number of collections (#38769)
This commit is contained in:
parent
bc09d3c5f2
commit
1cae543e8f
@ -53,6 +53,7 @@ class Collection < ApplicationRecord
|
||||
validates :language, language: { if: :local?, allow_nil: true }
|
||||
validate :tag_is_usable
|
||||
validate :items_do_not_exceed_limit
|
||||
validate :user_does_not_exceed_limit, on: :create
|
||||
|
||||
scope :with_items, -> { includes(:collection_items).merge(CollectionItem.with_accounts) }
|
||||
scope :with_tag, -> { includes(:tag) }
|
||||
@ -105,4 +106,11 @@ class Collection < ApplicationRecord
|
||||
def items_do_not_exceed_limit
|
||||
errors.add(:collection_items, :too_many, count: MAX_ITEMS) if pending_or_accepted_items.size > MAX_ITEMS
|
||||
end
|
||||
|
||||
def user_does_not_exceed_limit
|
||||
return unless local?
|
||||
|
||||
limit = account.user.role.collection_limit
|
||||
errors.add(:base, :too_many, count: limit) if account.collections.count >= limit
|
||||
end
|
||||
end
|
||||
|
||||
@ -4,15 +4,16 @@
|
||||
#
|
||||
# Table name: user_roles
|
||||
#
|
||||
# id :bigint(8) not null, primary key
|
||||
# color :string default(""), not null
|
||||
# highlighted :boolean default(FALSE), not null
|
||||
# name :string default(""), not null
|
||||
# permissions :bigint(8) default(0), not null
|
||||
# position :integer default(0), not null
|
||||
# require_2fa :boolean default(FALSE), not null
|
||||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
# id :bigint(8) not null, primary key
|
||||
# collection_limit :integer default(10), not null
|
||||
# color :string default(""), not null
|
||||
# highlighted :boolean default(FALSE), not null
|
||||
# name :string default(""), not null
|
||||
# permissions :bigint(8) default(0), not null
|
||||
# position :integer default(0), not null
|
||||
# require_2fa :boolean default(FALSE), not null
|
||||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
#
|
||||
|
||||
class UserRole < ApplicationRecord
|
||||
@ -104,6 +105,7 @@ class UserRole < ApplicationRecord
|
||||
validates :name, presence: true, unless: :everyone?
|
||||
validates :color, format: { with: CSS_COLORS }, if: :color?
|
||||
validates :position, numericality: { in: (-POSITION_LIMIT..POSITION_LIMIT) }
|
||||
validates :collection_limit, numericality: { only_integer: true, greater_than_or_equal_to: 0 }
|
||||
|
||||
validate :validate_permissions_elevation
|
||||
validate :validate_position_elevation
|
||||
|
||||
@ -3,6 +3,8 @@
|
||||
class REST::RoleSerializer < ActiveModel::Serializer
|
||||
attributes :id, :name, :permissions, :color, :highlighted
|
||||
|
||||
attribute :collection_limit, if: -> { Mastodon::Feature.collections_enabled? }
|
||||
|
||||
def id
|
||||
object.id.to_s
|
||||
end
|
||||
|
||||
@ -32,6 +32,14 @@
|
||||
|
||||
%hr.spacer/
|
||||
|
||||
- if Mastodon::Feature.collections_enabled?
|
||||
|
||||
.fields-group
|
||||
= form.input :collection_limit,
|
||||
wrapper: :with_label
|
||||
|
||||
%hr.spacer/
|
||||
|
||||
- unless current_user.role == form.object
|
||||
|
||||
.field-group
|
||||
|
||||
@ -160,6 +160,7 @@ en:
|
||||
other: We have to make sure you're at least %{count} to use %{domain}. We won't store this.
|
||||
role: The role controls which permissions the user has.
|
||||
user_role:
|
||||
collection_limit: Limits the number of Collections that a single user with this role can create. Please note that when you decrease this number, users who are already at this limit will not lose any Collections. But they will not be able to create additional ones.
|
||||
color: Color to be used for the role throughout the UI, as RGB in hex format
|
||||
highlighted: This makes the role publicly visible
|
||||
name: Public name of the role, if role is set to be displayed as a badge
|
||||
@ -386,6 +387,7 @@ en:
|
||||
role: Role
|
||||
time_zone: Time zone
|
||||
user_role:
|
||||
collection_limit: Maximum number of Collections per user
|
||||
color: Badge color
|
||||
highlighted: Display role as badge on user profiles
|
||||
name: Name
|
||||
|
||||
@ -0,0 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class AddCollectionLimitToUserRoles < ActiveRecord::Migration[8.1]
|
||||
def change
|
||||
add_column :user_roles, :collection_limit, :integer, null: false, default: 10
|
||||
end
|
||||
end
|
||||
@ -10,7 +10,7 @@
|
||||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema[8.1].define(version: 2026_04_15_133505) do
|
||||
ActiveRecord::Schema[8.1].define(version: 2026_04_20_124030) do
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "pg_catalog.plpgsql"
|
||||
|
||||
@ -1337,6 +1337,7 @@ ActiveRecord::Schema[8.1].define(version: 2026_04_15_133505) do
|
||||
end
|
||||
|
||||
create_table "user_roles", force: :cascade do |t|
|
||||
t.integer "collection_limit", default: 10, null: false
|
||||
t.string "color", default: "", null: false
|
||||
t.datetime "created_at", null: false
|
||||
t.boolean "highlighted", default: false, null: false
|
||||
|
||||
@ -71,6 +71,20 @@ RSpec.describe Collection do
|
||||
it { is_expected.to be_valid }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the user is already at the per-user limit of collections' do
|
||||
subject { Fabricate.build(:collection, account:) }
|
||||
|
||||
let(:role) { Fabricate(:user_role, collection_limit: 2) }
|
||||
let(:user) { Fabricate(:user, role:) }
|
||||
let(:account) { user.account }
|
||||
|
||||
before do
|
||||
Fabricate.times(2, :collection, account:)
|
||||
end
|
||||
|
||||
it { is_expected.to_not be_valid }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#item_for' do
|
||||
|
||||
@ -31,6 +31,12 @@ RSpec.describe UserRole do
|
||||
it { is_expected.to_not allow_values('x', '112233445566', '#xxyyzz').for(:color) }
|
||||
end
|
||||
|
||||
describe 'collection_limit' do
|
||||
subject { Fabricate.build :user_role }
|
||||
|
||||
it { is_expected.to validate_numericality_of(:collection_limit).only_integer.is_greater_than_or_equal_to(0) }
|
||||
end
|
||||
|
||||
context 'when current_account is set' do
|
||||
subject { Fabricate :user_role }
|
||||
|
||||
|
||||
43
spec/serializers/rest/role_serializer_spec.rb
Normal file
43
spec/serializers/rest/role_serializer_spec.rb
Normal file
@ -0,0 +1,43 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe REST::RoleSerializer do
|
||||
subject { serialized_record_json(role, described_class) }
|
||||
|
||||
let(:everyone) do
|
||||
Fabricate.build(:user_role, permissions: 0)
|
||||
end
|
||||
let(:role) do
|
||||
Fabricate.build(:user_role, id: 2342, name: 'test role', color: '#ABC', highlighted: true, permissions: 2300, collection_limit: 11)
|
||||
end
|
||||
|
||||
before do
|
||||
allow(UserRole).to receive(:everyone).and_return(everyone)
|
||||
end
|
||||
|
||||
it 'includes the relevant attributes' do
|
||||
expect(subject)
|
||||
.to include({
|
||||
'id' => '2342',
|
||||
'name' => 'test role',
|
||||
'color' => '#ABC',
|
||||
'highlighted' => true,
|
||||
'permissions' => '2300',
|
||||
})
|
||||
end
|
||||
|
||||
context 'when collections are enabled', feature: :collections do
|
||||
it 'includes the relevant attributes' do
|
||||
expect(subject)
|
||||
.to include({
|
||||
'id' => '2342',
|
||||
'name' => 'test role',
|
||||
'color' => '#ABC',
|
||||
'highlighted' => true,
|
||||
'permissions' => '2300',
|
||||
'collection_limit' => 11,
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
Loading…
x
Reference in New Issue
Block a user