Invitations

An invitation brings people onto the portal on purpose: staff choose who receives it and the roles it grants, and each recipient is emailed a link of their own. Following it creates their login on the contact that already exists for them — with a password or an external sign-in — or, if they already have one, adds the roles to it. It is how a portal that doesn't let strangers register brings its users on board.

How an invitation travels

An invitation is written as a draft, and sending it changes nothing but its status. The portal's own timed process — running every five minutes in each portal instance — notices it, works out the recipients, and emails each a link with a code of its own. Only the code's hash is stored, so reading the table gives nobody a working link.

  1. Draft — choose who receives it, the roles it grants, its expiry and the email it goes out in.
  2. Ready to send — Send was pressed; the portal picks it up within a few minutes.
  3. Sent — the recipients exist and their links work until it expires (30 days after sending, unless you set a date).
  4. Expired or Canceled — the links stop working.

Who receives it

An invitation goes to a contact, to an account — every active contact it has — or to whoever criteria written with the FetchXML query builder return. Recipients are worked out when the invitation is sent, with the access of the person who sent it, and sending it again later reaches whoever matches by then and hasn't received it, without emailing anybody twice. Contacts without an email address are skipped, and so, unless the invitation says otherwise, are contacts who already have a portal login.

Who receives it is fixed once sent

Once an invitation leaves Draft, who receives it and the roles it grants can't change — the roles are read when somebody accepts, so changing them would change what people already holding a link receive. Its expiry date can still move. Each recipient can be resent a new link or revoked on their own.

The email

Left to itself, the portal sends its own email, from its localization (app.email-templates.invitation). To write your own, create a contact or global email template in Dataverse, put {{invitation-link}} where the link should go, and choose it on the invitation. A template without the placeholder fails the recipient rather than sending an email nobody can act on.

Accepting

The link opens /Account/Invitation. Someone without a login creates one there — the invited contact's own email address, with a password or any configured external sign-in — even when sign-up is turned off. Someone with a login signs in and accepts; someone signed in as another person is asked to sign out. Following a link mailed to the address counts as confirming it, so no confirmation email follows. A code that is unknown, used, revoked, expired or for another portal all say the same thing.

The public address

The timed process works outside any request, so it reads the address the portal is reached at from PowerPortalsPro:PublicUrl — required, for every portal. Its links point there, and it is the address the portal's license is checked for when deciding which invitations are its own. Set it per environment so UAT's links point at UAT; in Azure App Service it is the PowerPortalsPro__PublicUrl application setting, marked as a deployment slot setting. A portal running on localhost sends no invitations unless PowerPortalsPro:Invitations:DevelopmentWebsiteId names the website record to act as.

Wiring it up

InvitationAdmin lists the invitations, with Send and Cancel; InvitationForm edits one, with its recipients beneath. Gate the page on the portal administrator check, like the other admin pages, and give the form a page of its own: criteria are written with the FetchXML query builder, which wants the room. Every change is made as the signed-in member of staff, so Dataverse security applies here as it does in the model-driven app.

React Blazor

InvitationAdmin

React Blazor

InvitationAdmin Class

Parameters

Name
Type
Default
Description
InvitationUrlstring?
Where an invitation is edited, as a URL whose {0} is its id — for example 'InvitationAdmin/Invitation/{0}'. Set it and Create and Open navigate there, with new in place of the id for a new one; left unset, they open a side panel.
Name: InvitationUrl
Type: string?
Description: Where an invitation is edited, as a URL whose {0} is its id — for example 'InvitationAdmin/Invitation/{0}'. Set it and Create and Open navigate there, with new in place of the id for a new one; left unset, they open a side panel.

InvitationForm

React Blazor

InvitationForm Class

Properties

Name
Type
Default
Description
RecordTableRecord?
The invitation being edited.
Name: Record
Type: TableRecord?
Description: The invitation being edited.