Use your own user type#
Make this package keep your site's users as a content type of your own—a
Person, say—instead of UserProfile.
Once the steps below are done, the package handles your type the way it handles
UserProfile. A first login creates it, api.user.create creates it, the user
catalog files it, and user enumeration, group membership, @group-members, the
catalog rebuild, the export and the consistency check all read it. No
UserProfile is created beside it.
Before you start#
A Plone add-on of your own, with a GenericSetup profile that depends on
pas.plugins.identity:default. See Install the backend.Your content type, as a Dexterity type in that profile. It needs no Python: every marker and field this package reads comes from behaviors it ships.
Steps#
Enable Site user on your type, together with the three behaviors
UserProfilegets its fields from, in your type's FTI:<property name="behaviors" purge="false"> <element value="pas.plugins.identity.principal_user" /> <element value="pas.plugins.identity.profile_details" /> <element value="pas.plugins.identity.email_addresses" /> <element value="pas.plugins.identity.group_membership" /> </property>
pas.plugins.identity.principal_usermakes your type both a user, so the package creates it when somebody adds a user, and a Profile, so the user catalog files it. The other three supply the fields the catalog reads. See Profiles and groups for what each one adds.Bind the Profile workflow to your type, in your profile's
workflows.xml:<object name="portal_workflow" meta_type="Plone Workflow Tool"> <bindings> <type type_id="Person"> <bound-workflow workflow_id="user_profile_workflow" /> </type> </bindings> </object>
A workflow of your own also works, provided the states your users sit in are listed in Enumeration-active states. A user in any other state is not found by user enumeration.
Allow your type in the principals container, in
profiles/default/types/PrincipalsContainer.xml:<object name="PrincipalsContainer" meta_type="Dexterity FTI"> <property name="allowed_content_types" purge="false"> <element value="Person" /> </property> </object>
Name your type in the registry, in
profiles/default/registry.xml:<registry> <record name="pas.plugins.identity.user_content_type"> <value>Person</value> </record> </registry>
Installing
pas.plugins.identityfills this record withUserProfileonly when it is empty, so your profile's value survives a reinstall and a change to the container settings.Optionally, decide where groups go. Leave the group record alone to keep
UserGroup, or change it in the same file:To keep groups
Group record
Also
In Plone's own
source_groups<value></value>Nothing
As a type of your own
Your type's name
Steps 1 to 3 for that type, with
pas.plugins.identity.principal_group,pas.plugins.identity.group_membershipanduser_group_workflowWith the record empty, adding a group creates a
source_groupsgroup, and reads that look for group content look forUserGroupand find none.Install your profile, or apply it to an existing site.
Warning
Mark your type with the shipped behavior rather than with an interface of your
own that extends IUserContent. Registered as a behavior's provides, such an
interface stops Dexterity from answering the defaults of other behaviors'
userid and login fields. See Marking a type of your own.
When only some objects of your type are users#
A type can hold users and non-users both—a Person for each account and a
Person for each team page. Site user does not fit there: it makes
every object of the type a user, and every team page a user who cannot sign in.
Instead, make the type's schema extend IUserContent, and apply IUserProfile
to the objects that are accounts from a subscriber of your own. An object this
package creates for a user always has a login:
from pas.plugins.identity.core.indexers.subscribers import profile_moved
from pas.plugins.identity.core.interfaces import IUserProfile
from zope.interface import alsoProvides
def person_added(person, event):
if IUserProfile.providedBy(person) or not person.login:
return
alsoProvides(person, IUserProfile)
profile_moved(person, event)
Register it for your schema and zope.lifecycleevent.interfaces.IObjectAddedEvent.
Steps 2 to 6 still apply.
Line |
Why |
|---|---|
|
A login keeps the object only if it provides |
|
The user catalog's subscribers are bound to the marker, and the ones for this event were looked up before yours ran. Without the call the object is marked and never filed. |
Verify#
Sign in with a provider as a user who has never signed in before.
Open the principals container. It holds a
Personfor that user, and noUserProfile.Open Site Setup › Users and search for the user. They are listed.
Known quirks#
The Volto add-on registers its profile view for
UserProfileand its group view forUserGroup, by portal type. Your type renders with whatever view your own add-on registers for it.Objects of your type that existed before your profile was applied are not in the user catalog. Apply
pas.plugins.identity:rebuild-catalogonce to file them.
Note
The behaviors, the workflow, the container and the records are exercised
together, for a user type, a user type marked one object at a time and a group
type, by
backend/tests/core/principal_types/test_own_user_type.py, which sets them up in Python. The
XML files above are the GenericSetup spelling of the same configuration and are
not run by the test suite.