fairdm.contrib.contributors.admin#

Admin for contributors, affiliations, claiming and the claiming audit log.

Module Contents#

Classes#

AffiliationForm

Gate writing an Admin or Owner affiliation on manage_organization.

ClaimedStatusFilter

Filter people by claimed or unclaimed status.

AccountEmailInline

Inline for a person’s email addresses.

ContributionInline

Inline for contributions.

ContributorInline

Inline for a contributor’s profile.

AffiliationInline

Inline for a person’s affiliations, gated by AffiliationForm.

MemberInline

Inline for an organisation’s members, gated by AffiliationForm.

IdentifierInline

Inline for a contributor’s identifiers.

SubOrganizationInline

Inline for an organisation’s sub-organisations.

UserAdmin

Admin for people, with import, claim links, merging and duplicate suggestions.

OrganizationActionForm

Add a new-owner selector to the organisation changelist’s action bar.

OrganizationAdmin

Admin for organisations, with ROR sync and ownership transfer actions.

AffiliationAdmin

Administer affiliations directly, gated by manage_organization on each organisation.

ClaimingAuditLogAdmin

Read-only list of claiming audit log entries, which are immutable.

Functions#

bind_affiliation_form_user

Return a subclass of form_class with user bound as its default.

API#

class AffiliationForm(*args, user=None, **kwargs)#

Bases: django.forms.ModelForm

Gate writing an Admin or Owner affiliation on manage_organization.

Setting a type to ADMIN or OWNER, or changing an affiliation that already has one, is a management act, so it needs manage_organization on the organisation. Superusers hold that permission already. The admin and both inlines bind the acting user through bind_affiliation_form_user, so every route that writes a type reaches this rule.

Args: *args: Passed to ModelForm. user: The user submitting the form. **kwargs: Passed to ModelForm.

Initialization

class Meta#
model#

None

fields#

‘all’

clean()#

Refuse setting or changing an Admin or Owner affiliation without manage_organization.

bind_affiliation_form_user(form_class, user)#

Return a subclass of form_class with user bound as its default.

Built per request so the permission check runs against whoever submitted the form.

Args: form_class: The affiliation form class. user: The requesting user.

Returns: The bound form class.

class ClaimedStatusFilter(request, params, model, model_admin)#

Bases: django.contrib.admin.SimpleListFilter

Filter people by claimed or unclaimed status.

Reads is_claimed rather than the email, since an invited person has an email but has not claimed. A deactivated account no longer counts as claimed.

Initialization

title#

‘_(…)’

parameter_name#

‘is_claimed’

lookups(request, model_admin)#

Offer claimed and unclaimed.

queryset(request, queryset)#

Narrow to claimed or unclaimed people.

class AccountEmailInline(parent_model, admin_site)#

Bases: django.contrib.admin.TabularInline

Inline for a person’s email addresses.

Initialization

model#

None

fields#

[‘email’, ‘primary’, ‘verified’]

extra#

0

class ContributionInline(parent_model, admin_site)#

Bases: django.contrib.admin.StackedInline

Inline for contributions.

Initialization

extra#

1

fields#

(‘profile’, ‘roles’)

class ContributorInline(parent_model, admin_site)#

Bases: django.contrib.admin.StackedInline

Inline for a contributor’s profile.

Initialization

model#

None

fields#

[‘profile’]

extra#

0

class AffiliationInline(parent_model, admin_site)#

Bases: django.contrib.admin.StackedInline

Inline for a person’s affiliations, gated by AffiliationForm.

Initialization

model#

None

form#

None

fields#

[(‘organization’, ‘type’, ‘is_primary’)]

extra#

0

get_formset(request, obj=None, **kwargs)#

Bind the requesting user into AffiliationForm.

class MemberInline(parent_model, admin_site)#

Bases: django.contrib.admin.StackedInline

Inline for an organisation’s members, gated by AffiliationForm.

Initialization

model#

None

form#

None

fk_name#

‘organization’

fields#

[(‘person’, ‘type’, ‘is_primary’)]

extra#

0

verbose_name#

‘Member’

verbose_name_plural#

‘Members’

get_formset(request, obj=None, **kwargs)#

Bind the requesting user into AffiliationForm.

class IdentifierInline(parent_model, admin_site)#

Bases: django.contrib.admin.StackedInline

Inline for a contributor’s identifiers.

Initialization

model#

None

fields#

[‘type’, ‘value’]

extra#

0

class SubOrganizationInline(parent_model, admin_site)#

Bases: django.contrib.admin.TabularInline

Inline for an organisation’s sub-organisations.

Initialization

model#

None

fk_name#

‘parent’

fields#

[‘name’]

extra#

0

verbose_name#

‘_(…)’

verbose_name_plural#

‘_(…)’

class UserAdmin(model, admin_site)#

Bases: django.contrib.auth.admin.UserAdmin, hijack.contrib.admin.HijackUserAdminMixin, import_export.admin.ImportExportModelAdmin

Admin for people, with import, claim links, merging and duplicate suggestions.

Initialization

base_model#

None

show_in_index#

True

change_form_template#

‘contributors/admin/change_form.html’

resource_classes#

None

skip_import_confirm#

True

inlines#

None

list_display#

[‘first_name’, ‘last_name’, ‘email’, ‘is_staff’, ‘account_state’]

list_filter#

()

exclude#

(‘username’,)

autocomplete_fields#

[‘groups’]

readonly_fields#

[‘synced_data’, ‘last_synced’, ‘uuid’, ‘added’, ‘modified’]

fieldsets#

((‘Basic info’,), (), (‘Permissions’,))

add_fieldsets#

((None,),)

search_fields#

(‘email’, ‘name’, ‘uuid’)

ordering#

(‘last_name’,)

actions#

[‘generate_claim_link_action’, ‘merge_person_action’]

get_fieldsets(request, obj=None)#

Drop the account-escalation fields for a non-superuser.

groups is dropped too unless the user holds auth.view_group, as a group membership grants a role’s rights.

get_form(request, obj=None, **kwargs)#

Drop password from the built form for a non-superuser.

get_actions(request)#

Drop the merge and claim-link actions for users the views would refuse.

account_state(obj)#

Show the person’s derived account state.

merge_person_action(request, queryset)#

Redirect to the merge page for the one selected person.

Redirect to the claim-link page for the one selected person.

change_view(request, object_id, form_url='', extra_context=None)#

Add possible duplicates, minus those dismissed this session, to the change form.

dismiss_candidate_view(request, pk, candidate_pk)#

Dismiss a duplicate candidate for this session and return to the change page.

Args: request: The current request. pk: The person being edited. candidate_pk: The candidate to dismiss.

Returns: A redirect to the person’s change page.

get_urls()#

Add the claim-link, merge and dismiss-candidate routes.

Render the claim link page for a person.

A claim token is a credential, so the permission check comes first.

Args: request: The current request. pk: The person’s primary key.

Returns: The rendered page.

Raises: PermissionDenied: The user may not manage people.

merge_view(request, pk)#

Render the merge page for a person and merge them into the chosen person on POST.

Merging deletes the discarded person and moves their affiliations, permissions, emails and social account, so the permission check comes first.

Args: request: The current request. pk: The primary key of the person to discard.

Returns: The rendered page, or a redirect to the surviving person after a merge.

Raises: PermissionDenied: The user may not manage people.

class OrganizationActionForm(data=None, files=None, auto_id='id_%s', prefix=None, initial=None, error_class=ErrorList, label_suffix=None, empty_permitted=False, field_order=None, use_required_attribute=None, renderer=None, bound_field_class=None)#

Bases: django.contrib.admin.helpers.ActionForm

Add a new-owner selector to the organisation changelist’s action bar.

The transfer action reads the value from request.POST without validating this form.

Attributes: new_owner: The person to become owner.

Initialization

new_owner#

‘PersonChoiceField(…)’

class OrganizationAdmin(model, admin_site)#

Bases: django.contrib.admin.ModelAdmin

Admin for organisations, with ROR sync and ownership transfer actions.

Initialization

base_model#

None

show_in_index#

True

inlines#

None

action_form#

None

list_display#

[‘name’, ‘city’, ‘country’, ‘lat’, ‘lon’]

list_filter#

[‘country’]

search_fields#

[‘name’]

readonly_fields#

[‘synced_data’, ‘last_synced’, ‘uuid’, ‘added’, ‘modified’]

fieldsets#

((None,), (), ())

actions#

[‘sync_from_ror’, ‘transfer_ownership_action’]

get_readonly_fields(request, obj: fairdm.contrib.contributors.models.Organization | None = None)#

Make the synced fields read-only once the organisation has synced data.

sync_from_ror(request, queryset)#

Queue a ROR sync for each selected organisation that has a ROR identifier.

transfer_ownership_action(request, queryset)#

Transfer the selected organisation to the member chosen in the action bar.

class AffiliationAdmin(model, admin_site)#

Bases: django.contrib.admin.ModelAdmin

Administer affiliations directly, gated by manage_organization on each organisation.

Initialization

form#

None

list_display#

[‘person’, ‘organization’, ‘type’, ‘is_primary’]

list_filter#

[‘type’, ‘is_primary’]

autocomplete_fields#

[‘person’, ‘organization’]

get_form(request, obj=None, **kwargs)#

Bind the requesting user into AffiliationForm.

get_queryset(request)#

Limit a non-superuser to affiliations of organisations they own.

has_change_permission(request, obj=None)#

Refuse changing an affiliation without manage_organization on its organisation.

has_delete_permission(request, obj=None)#

Refuse deleting an affiliation without manage_organization on its organisation.

class ClaimingAuditLogAdmin(model, admin_site)#

Bases: django.contrib.admin.ModelAdmin

Read-only list of claiming audit log entries, which are immutable.

Initialization

list_display#

[‘timestamp’, ‘method’, ‘source_person’, ‘target_person’, ‘initiated_by’, ‘success’]

list_filter#

[‘method’, ‘success’]

search_fields#

[‘source_person__name’, ‘target_person__name’, ‘initiated_by__name’]

date_hierarchy#

‘timestamp’

ordering#

[‘-timestamp’]

has_add_permission(request)#

Never allow adding an entry.

has_view_permission(request, obj=None)#

Allow the list but not an entry’s detail page.

has_change_permission(request, obj=None)#

Never allow changing an entry.

has_delete_permission(request, obj=None)#

Never allow deleting an entry.