User

final class User : Model
extension User: Reportable
extension User: ModelAuthenticatable
extension User: ModelSessionAuthenticatable

All accounts are of class User.

The terms “account” and “sub-account” used throughout this documentatiion are all instances of User. The terms “primary account”, “parent account” and “master account” are used interchangeably to refer to any account that is not a sub-account.

A primary account holds the access level, verification token and recovery key, and all sub-accounts (if any) inherit these three credentials.

.id and .parentID are provisioned automatically, by the model protocols and UsersController account creation handlers respectively. .createdAt, .updatedAt and .deletedAt are all maintained automatically by the model protocols and should never be otherwise modified.

Who This User Is

  • The user’s publicly viewable username.

    Declaration

    Swift

    @Field
    var username: String
  • An optional name for display alongside the username. “Display Name (@username)”

    Declaration

    Swift

    @OptionalField
    var displayName: String?
  • An optional real world name for the user.

    Declaration

    Swift

    @OptionalField
    var realName: String?
  • Concatenation of displayName + (@username) + realName, to speed search by name.

    Declaration

    Swift

    @Field
    var userSearch: String

Access

  • The user’s password, encrypted to BCrypt hash value.

    Declaration

    Swift

    @Field
    var password: String
  • The user’s recovery key, encrypted to BCrypt hash value.

    Declaration

    Swift

    @Field
    var recoveryKey: String
  • The registration code (or other identifier) used to activate the user for full read-write access. Prefixed with “*” after being used for password recovery.

    Declaration

    Swift

    @OptionalField
    var verification: String?
  • The user’s UserAccessLevel, set to .unverified at time of creation, or to the parent’s access level if a sub-account.

    Declaration

    Swift

    @Enum
    var accessLevel: UserAccessLevel
  • Only refers to the user’s ability to change their profile. Think of the profile fields as content, and mods can quarantine or lock that content, separate from outright banning a user.

    Declaration

    Swift

    @Enum
    var moderationStatus: ContentModerationStatus
  • Number of successive failed attempts at password recovery.

    Declaration

    Swift

    @Field
    var recoveryAttempts: Int
  • Cumulative number of reports submitted on user’s posts.

    Declaration

    Swift

    @Field
    var reports: Int
  • If non-nil, the account has been handed a time out by a moderator. The account will have an effective access level of ‘Quarantined’ until the given time. Quarantine generally means normal Read access but user canot post or modify any text or image content. The user’s accessLevel field should not be changed when applying a temp quarantine. This way if mods later decide to ban the user, the end of the temp quarantine won’t reset the user’s access level (most likely to .verified).

    Declaration

    Swift

    @Field
    var tempQuarantineUntil: Date?

About This User

  • The filename of the image for the user’s profile picture.

    Declaration

    Swift

    @Field
    var userImage: String?
  • An optional bio or blurb or whatever.

    Declaration

    Swift

    @OptionalField
    var about: String?
  • An optional email address. Social media addresses, URLs, etc. should probably be in .about or maybe .message.

    Declaration

    Swift

    @OptionalField
    var email: String?
  • An optional home city, country, planet…

    Declaration

    Swift

    @OptionalField
    var homeLocation: String?
  • An optional message to anybody viewing the profile. “I like turtles.”

    Declaration

    Swift

    @OptionalField
    var message: String?
  • An optional preferred pronoun or form of address.

    Declaration

    Swift

    @OptionalField
    var preferredPronoun: String?
  • An optional cabin number.

    Declaration

    Swift

    @OptionalField
    var roomNumber: String?
  • An optional dinner team.

    Declaration

    Swift

    @OptionalField
    var dinnerTeam: DinnerTeam?
  • Users that this user has muted. Muting removes twarrts, forums, forumPosts, and LFGs authored by muted users from API results. Here as an array instead of a to-many child relation because the primary operation is to use the list of all muted user IDs as a query filter, and we should never use the inverse relation (“muted by ”) for any purpose.

    Declaration

    Swift

    @Field
    var mutedUserIDs: [UUID]
  • Users that this user has blocked. Blocks act as a bidirectional mute between all accounts held by this user and all accounts held by the target user. However, this only records the blocks requested by this user, and only parent accounts can have this field filled in. Although a block applies to all related accounts of the target, here we only specify the specific account the user requested to block. Although we do need to access the ‘blocked by’ relation here, we build another structure for that, one that tracks all the accounts blocked by incoming or outgoing block requests.

    Declaration

    Swift

    @Field
    var blockedUserIDs: [UUID]

Moderator Only

  • If the user is a Moderator and is handling user reports, this will be set to the actionGroup of the reports. In this case, all the reports in the group are reporting on the same piece of content, and all have the same actionGroup. Any moderator actions the mod takes while handling reports get set tot his UUID. When the reports are closed, this gets set to nil.

    Declaration

    Swift

    @Field
    var actionGroup: UUID?
  • Timestamp of the model’s creation, set automatically.

    Declaration

    Swift

    @Timestamp
    var createdAt: Date?
  • Timestamp of the model’s last update, set automatically.

    Declaration

    Swift

    @Timestamp
    var updatedAt: Date?
  • Timestamp of the model’s soft-deletion, set automatically.

    Declaration

    Swift

    @Timestamp
    var deletedAt: Date?
  • Timestamp of the UserProfile’s last update.

    Declaration

    Swift

    @Field
    var profileUpdatedAt: Date

Relations

Initialization

  • Declaration

    Swift

    init()
  • Initializes a new User.

    Declaration

    Swift

    init(
    	username: String,
    	password: String,
    	recoveryKey: String,
    	verification: String? = nil,
    	parent: User? = nil,
    	accessLevel: UserAccessLevel,
    	recoveryAttempts: Int = 0,
    	reports: Int = 0,
    	profileUpdatedAt: Date = Date(timeIntervalSince1970: 0)
    )

    Parameters

    username

    The user’s username, unadorned (e.g. “grundoon”, not “@grundoon”).

    password

    A BCrypt hash of the user’s password. Please never store actual passwords.

    recoveryKey

    A BCrypt hash of the user’s recovery key. Please never store the actual key.

    verification

    A token of known identity, such as a provided code or a verified email address. nil if not yet verified.

    parent

    If a sub-account, the id of the master acount, otherwise nil.

    accessLevel

    The user’s access level (see UserAccessLevel).

    recoveryAttempts

    The number of successive failed attempts at password recovery, initially 0.

    reports

    The total number of reports made on the user’s posts, initially 0.

    profileUpdatedAt

    The timestamp of the associated profile’s last update, initially epoch.

  • Declaration

    Swift

    func makeUserHeader() throws -> UserHeader

Functions

  • allAccountIDs(on:) Asynchronous

    Returns a list of IDs of all accounts associated with the User. If user is a primary account (has no .parentID) it returns itself plus any sub-accounts. If user is a sub-account, it determines its parent, then returns the parent and all sub-accounts.

    Declaration

    Swift

    func allAccountIDs(on db: Database) async throws -> [UUID]

    Parameters

    req

    The incoming request Container, which provides the EventLoop on which the query must be run.

    Return Value

    [UUID] containing all the user’s associated IDs.

  • allAccounts(on:) Asynchronous

    Returns an array of User whose first element is the primary account and the remaining elements are sub-accounts of the primary account.

    Declaration

    Swift

    func allAccounts(on db: Database) async throws -> [User]

    Parameters

    db

    The incoming request Container, which provides the EventLoop on which the query must be run.

    Return Value

    [User]

  • parentAccount(on:) Asynchronous

    Returns the parent User of the user sending the request. If the requesting user has no parent, the user itself is returned.

    Declaration

    Swift

    func parentAccount(on req: Request) async throws -> User

    Parameters

    req

    The incoming request Container, which provides reference to the sending user.

  • Returns the ID of the parent account of the receiver. If the receiver has no parent, the receiver’s ID is returned.

    Declaration

    Swift

    func parentAccountID() throws -> UUID
  • Declaration

    Swift

    func buildUserSearchString()
  • Ensures that either the receiver can edit/delete other users’ content (that is, they’re a moderator), or that they authored the content they’re trying to modify/delete themselves, and still have rights to edit their own content (that is, they aren’t banned/quarantined).

    Declaration

    Swift

    func guardCanCreateContent(customErrorString: String = "user cannot modify this content") throws
  • Ensures that either the receiver can edit/delete other users’ content (that is, they’re a moderator), or that they authored the content they’re trying to modify/delete themselves, and still have rights to edit their own content (that is, they aren’t banned/quarantined).

    Declaration

    Swift

    func guardCanModifyContent<T: Reportable>(
    	_ content: T,
    	customErrorString: String = "user cannot modify this content"
    ) throws
  • The receiver is the user performing the edit. ofUser may be nil if the reciever is editing their own profile. If it’s a moderator editing another user’s profile, remember that the receiver is the moderator, and the user whose profile is being edited is the ofUser parameter.

    This fn is very similar to guardCanModifyContent(); it’s a separate function because profile editing is likely to have different access requirements. In particular we’re likely to let unverified users edit their profile.

    Declaration

    Swift

    func guardCanEditProfile(ofUser profileOwner: User? = nil, customErrorString: String = "User cannot edit profile")
    	throws
  • The report type for User reports.

    Declaration

    Swift

    var reportType: ReportType { get }
  • Declaration

    Swift

    var authorUUID: UUID { get }
  • Declaration

    Swift

    var autoQuarantineThreshold: Int { get }

ModelAuthenticatable Conformance