Class: User

Inherits:
ApplicationRecord show all
Includes:
CaberSubject, Followable, Follower, Lister, MemoWise, PublicIDable
Defined in:
app/models/user.rb

Constant Summary collapse

DEFAULT_TOUR_STATE =
{"completed" => []}

Constants included from FederailsCommon

FederailsCommon::FEDIVERSE_USERNAMES

Constants inherited from ApplicationRecord

ApplicationRecord::SAFE_NAME_LENGTH

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Followable

#followed_by?, #followers

Methods included from FederailsCommon

#federails_actor, #local?, #remote?

Methods included from Follower

#follow, #following?, #unfollow

Methods included from Lister

#delist, #list, #listed?, #set_list_state

Instance Attribute Details

#skip_invitation=(value) ⇒ Object (writeonly)

Sets the attribute skip_invitation

Parameters:

  • value —

    the value to set the attribute skip_invitation to.



95
96
97
# File 'app/models/user.rb', line 95

def skip_invitation=(value)
  @skip_invitation = value
end

Class Method Details

.find_param(param) ⇒ Object



108
109
110
# File 'app/models/user.rb', line 108

def self.find_param(param)
  find_by!(username: param)
end

.from_omniauth(auth) ⇒ Object



148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
# File 'app/models/user.rb', line 148

def self.from_omniauth(auth)
  # Link existing users by verified email first
  user = find_by(auth_provider: nil, auth_uid: nil, email: auth.info.email)
  if user
    raise OmniAuth::Strategies::OpenIDConnect::CallbackError, error: :email_unverified, reason: "Email not verified" unless auth.info.email_verified
    user.update!(
      auth_provider: auth.provider,
      auth_uid: auth.uid
    )
  else
    # Email isn't present, so let's match by ID
    user = find_or_create_by!(auth_provider: auth.provider, auth_uid: auth.uid) do |user|
      user.email = auth.info.email
      # Find an unused username - get the first of a few options
      user.username = [
        auth.info.preferred_username,
        auth.info.nickname,
        auth.info.email&.split("@")&.[](0),
        # Fallback to any of the above with some random numbers on the end
        (auth.info.preferred_username || auth.info.nickname || auth.info.email&.split("@")&.[](0) || "") + SecureRandom.hex(2)
      ]
        .compact
        .map { |u| u.gsub(/[^[:alnum:].\-_;]/, "-") }
        .find { |u| !User.exists?(username: u) }
    end
  end
  user
end

.invite!(params) ⇒ Object



264
265
266
267
268
# File 'app/models/user.rb', line 264

def self.invite!(params)
  options = params
  options[:username] ||= "invite_#{SecureRandom.hex(8)}"
  super(options)
end

.match!(identifier:, scope: User, invite: false) ⇒ Object



239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
# File 'app/models/user.rb', line 239

def self.match!(identifier:, scope: User, invite: false)
  raise ActiveRecord::RecordNotFound if identifier.blank?
  query = case identifier
  when URI::MailTo::EMAIL_REGEXP
    {email: identifier}
  when /\A(acct:|@)([a-z0-9\-_.]+)(@(.*))?\z/io
    if SiteSettings.federation_enabled?
      actor = Federails::Actor.(identifier) # rubocop:disable Rails/DynamicFindBy
      {id: actor&.entity&.id}
    else
      scope = scope.none
      {}
    end
  else
    {username: identifier}
  end
  scope.find_by! query
rescue ActiveRecord::RecordNotFound
  if identifier =~ URI::MailTo::EMAIL_REGEXP && invite
    scope.find(User.invite!(email: identifier, skip_invitation: true).id)
  else
    raise
  end
end

.send_reset_password_instructions(attributes = {}) ⇒ Object



194
195
196
197
198
199
200
201
202
203
204
# File 'app/models/user.rb', line 194

def self.send_reset_password_instructions(attributes = {})
  recoverable = find_or_initialize_with_errors(reset_password_keys, attributes, :not_found)
  if recoverable.persisted?
    if recoverable.approved?
      recoverable.send_reset_password_instructions
    else
      recoverable.errors.add(:base, :not_approved)
    end
  end
  recoverable
end

.user_count(range) ⇒ Object



177
178
179
180
181
182
183
# File 'app/models/user.rb', line 177

def self.user_count(range)
  return User.count if range.nil? # rubocop:disable Pundit/UsePolicyScope

  # Updated date isn't a great proxy for activity, but it'll do for now
  # We can improve this by using devise trackable to track logins at some point
  User.where(updated_at: range).count # rubocop:disable Pundit/UsePolicyScope
end

Instance Method Details

#active? ⇒ Boolean

Returns:

  • (Boolean)


112
113
114
# File 'app/models/user.rb', line 112

def active?
  invitation_token.nil?
end

#active_for_authentication? ⇒ Boolean

Devise approval checks

Returns:

  • (Boolean)


186
187
188
# File 'app/models/user.rb', line 186

def active_for_authentication?
  super && approved?
end

#current_space_used ⇒ Object



227
228
229
# File 'app/models/user.rb', line 227

def current_space_used
  owned_models.sum(&:size_on_disk)
end

#federails_name ⇒ Object



100
101
102
# File 'app/models/user.rb', line 100

def federails_name
  username
end

#first_use? ⇒ Boolean

Returns:

  • (Boolean)


231
232
233
# File 'app/models/user.rb', line 231

def first_use?
  reset_password_token == "first_use"
end

#has_quota? ⇒ Boolean

Returns:

  • (Boolean)


219
220
221
# File 'app/models/user.rb', line 219

def has_quota?
  !(attributes["quota"] == 0) && SiteSettings.enable_user_quota
end

#inactive_message ⇒ Object



190
191
192
# File 'app/models/user.rb', line 190

def inactive_message
  approved? ? super : :not_approved
end

#invited? ⇒ Boolean

Returns:

  • (Boolean)


116
117
118
# File 'app/models/user.rb', line 116

def invited?
  invitation_token.present?
end

#is_administrator? ⇒ Boolean

Returns:

  • (Boolean)


128
129
130
# File 'app/models/user.rb', line 128

def is_administrator?
  has_any_role_of? :administrator
end

#is_contributor? ⇒ Boolean

Returns:

  • (Boolean)


136
137
138
# File 'app/models/user.rb', line 136

def is_contributor?
  has_any_role_of? :administrator, :moderator, :contributor
end

#is_member? ⇒ Boolean

Returns:

  • (Boolean)


140
141
142
# File 'app/models/user.rb', line 140

def is_member?
  has_any_role_of? :administrator, :moderator, :contributor, :member
end

#is_moderator? ⇒ Boolean

Returns:

  • (Boolean)


132
133
134
# File 'app/models/user.rb', line 132

def is_moderator?
  has_any_role_of? :administrator, :moderator
end

#is_printer? ⇒ Boolean

Returns:

  • (Boolean)


124
125
126
# File 'app/models/user.rb', line 124

def is_printer?
  has_any_role_of? :administrator, :printer
end

#liked?(listable) ⇒ Boolean

Returns:

  • (Boolean)


275
276
277
# File 'app/models/user.rb', line 275

def liked?(listable)
  liked_list&.list_items&.where(listable: listable)&.any?
end

#liked_list ⇒ Object



270
271
272
# File 'app/models/user.rb', line 270

def liked_list
  lists.find_by(special: :liked)
end

#owned_models ⇒ Object



223
224
225
# File 'app/models/user.rb', line 223

def owned_models
  permitted_models.with_permission("own")
end

#owns?(thing) ⇒ Boolean

Returns:

  • (Boolean)


235
236
237
# File 'app/models/user.rb', line 235

def owns?(thing)
  has_permission_on?("own", thing)
end

#printed?(file) ⇒ Boolean

Returns:

  • (Boolean)


120
121
122
# File 'app/models/user.rb', line 120

def printed?(file)
  listed?(file, :printed)
end

#problem_severity(category) ⇒ Object



144
145
146
# File 'app/models/user.rb', line 144

def problem_severity(category)
  problem_settings[category.to_s]&.to_sym || Problem::DEFAULT_SEVERITIES[category.to_sym]
end

#public? ⇒ Boolean

Returns:

  • (Boolean)


210
211
212
# File 'app/models/user.rb', line 210

def public?
  true
end

#quota ⇒ Object

Quota is in MB and is referred to in the UI as file storage limits for clarity



215
216
217
# File 'app/models/user.rb', line 215

def quota
  quota_use_site_default ? SiteSettings.default_user_quota : attributes["quota"].to_i * 1.megabyte
end

#to_activitypub_object ⇒ Object



206
207
208
# File 'app/models/user.rb', line 206

def to_activitypub_object
  ActivityPub::UserSerializer.new(self).serialize
end

#to_param ⇒ Object



104
105
106
# File 'app/models/user.rb', line 104

def to_param
  username
end