github kobotoolbox/kpi 2.026.37

14 hours ago

What's changed

Features (45)
  • AudioCell: add view details and open action buttons for audio cells (#7490)

    Audio responses in the submissions table now have two separate action
    buttons — "View details" and "Open" — and the inline audio player's
    timer now shows hours for all recordings.

    The single "Open" button on audio response cells has been split into two
    icon buttons: a new "View details" icon (placeholder for now) and an
    "Open" icon that keeps launching the transcript/translation editor as
    before. The inline mini audio player's time display is now always
    visible — showing 00:00:00 while the duration isn't known yet, and
    --:--:-- if playback fails — and switches to an HH:MM:SS format for
    consistency with the newest designs.

  • AudioCell: add details dialog for audio responses (#7492)

    Clicking "View details" on an audio response now opens a dialog with an
    audio player and a "Translate & analyze" action, instead of doing
    nothing.

    Adds a reusable ProcessingPromptModal dialog and wires it up to the
    "View details" button on the audio submission data cell. The dialog
    shows the question label, an inline audio player for the response, and a
    "Translate & analyze" button that takes the user into the existing
    Processing view for that submission/question.

  • SSO: hide password/SSO disable for managed SSO users (#7489)

    Hide password update and disable SSO UI for users with a managed SSO
    social account.

  • SSO: confirm modal for managed SSO signup (#7498)

    Adds a confirmation modal for connecting to a managed SSO provider.

  • TextCell: add NLP action buttons for text questions (#7494)

    Text question answers in the submissions table now get the same "View
    details" preview and NLP actions that audio responses already have.

  • accounts: require re-authentication before email change (#7525)

    Changing the account email now requires the user to have authenticated
    recently with their password, and also with 2FA when it is enabled.

    POST /me/emails/ already bundled the whole change into one call: add
    the new address, promote it on confirmation, drop the old one, and send
    a confirmation link to the new address.

    This PR adds the missing piece from the Authentication Redesign spec:
    re-authentication before a sensitive account change.

    Browser sessions:

    Browser sessions re-authenticate through allauth, which records each
    step in the session. allauth's own did_recently_authenticate() was not
    sufficient because it inspects only the timestamp of the most recent
    authentication record, whichever method produced it. As a result, a
    password-only step would satisfy the check even for an account with 2FA
    enabled.

    kobo/apps/accounts/reauthentication.py therefore requires every
    authentication method available to the account to be fresh within
    ACCOUNT_REAUTHENTICATION_TIMEOUT (5 minutes,
    environment-configurable).

    MFA is resolved through KPI's own MfaAdapter, so the MFA_ENABLED
    constance flag and active MfaMethodsWrapper rows are honoured.

    Stateless credentials:

    Stateless credentials such as token, Basic, and OAuth2 authentication
    have no session for allauth to record a re-authentication in, so there
    is nothing they can do to satisfy the session check.

    Instead, they prove their identity through the request body:

    {
     "email": "new@example.com",
     "current_password": "…",
     "mfa_code": "123456"
    }
    

    current_password is required whenever the account has a usable
    password. mfa_code is additionally required when MFA is enabled and
    accepts either a TOTP code or a recovery code.

  • accounts: add endpoint for requesting another confirmation email (#7532)

    Adds POST /api/v2/email-confirmations/, an unauthenticated endpoint
    that resends a confirmation link to a registered, unverified email
    address without revealing whether the address is registered.

    Confirmation links expire after a day. Currently, a replacement is sent
    as a side effect of an unverified user trying to log in; that will be
    removed, so users need an explicit way to request a new one. The caller
    has just followed a dead link and has no session, so the endpoint is
    anonymous. allauth has no equivalent: its resend endpoints require
    either authentication or verification-by-code, which we don't use.

    • Enumeration: The same 200 and body are returned whether the address is
      unverified, already verified, or unknown. Mail is sent only in the first
      case. Delivery failures are logged rather than raised, since a 5xx would
      itself confirm
      that the address is registered.

    • Which email: An account with nothing verified yet is being activated
      and gets the activation email; one that already has a verified address
      is mid-email-change and gets the address verification email.

    • Throttling: ACCOUNT_RATE_LIMITS = False disables allauth's own
      cooldown, so without a limit this is an open mail relay. Throttling is
      per requested address, not per caller, so rotating source addresses
      can't flood one inbox. The limit is the
      EMAIL_CONFIRMATION_REQUESTS_PER_HOUR Constance option (default 5; 0
      disables).

  • auth: add auth redesign feature flag (#7446)

  • auth: add server configuration props to /environment (#7472)

    The settings that control how the sign-in and account creation pages
    look are now published through the /environment API, ahead of those
    pages being rebuilt in the main application interface. Nothing changes
    for users yet.

    The sign-in and account creation pages are being rebuilt as part of the
    main application interface rather than as separate server-rendered
    pages. Those pages currently read their appearance settings, the logo,
    the background image, the text shown beside the account creation form,
    directly from the server while the page is being assembled. The rebuilt
    pages are assembled in the browser instead, so that route is no longer
    available to them.
    This publishes the same values through the /environment API, which the
    application already reads when it starts, so the new pages can pick them
    up. The existing pages are untouched and keep working exactly as they do
    today.
    Two values are added that the new designs call for and the server did
    not previously hold: an image displayed beside the account creation
    form, and a setting controlling whether the KoboToolbox logo appears.

  • auth: main skeleton (#7501)

    Groundwork for the redesigned sign-in and account creation screens.
    Hidden behind a feature flag.

  • auth: intermediate confirmation screen for managed SSO domains modifications (#7522)

    This PR introduces an intermediate confirmation page in
    SocialAppCustomDataAdmin when toggling managed SSO or modifying
    domains.

    This includes the following changes:

    • Admin confirmation flow (kobo/apps/accounts/admin.py):
      SocialAppCustomDataAdmin.changeform_view intercepts saves when managed
      is toggled or domains are modified under a managed app. The confirmation
      screen displays status changes (Enabling/Disabling Managed SSO),
      added/removed domain lists, and plain account counts.
    • Reusable Queryset Logic (kobo/apps/accounts/utils.py):
      get_managed_sso_track_1_queryset returns linked accounts, skipping
      sso_exempt=True and anonymous users.
      get_managed_sso_track_2_queryset returns unlinked accounts with
      matching email domains. These exclude users with existing InAppMessage
      records matching message_type=MessageType.MANAGED_SSO_REMINDER for
      idempotence.
  • auth: read-only REGISTRATION_SSO_ALLOWED_EMAIL_DOMAINS aggregate in Constance (#7470)

    Added a display-only constance setting
    REGISTRATION_SSO_MANAGED_EMAIL_DOMAINS that displays a list of all
    email domains configured across managed SocialApps. It is located under
    General Options next to the other REGISTRATION_* settings in the
    Constance Config screen

    Registered REGISTRATION_SSO_MANAGED_EMAIL_DOMAINS setting in
    CONSTANCE_CONFIG with field type disabled_textarea and a note
    explaining per-app management in Admin > SocialApp. The
    disabled_textarea field type was added to
    CONSTANCE_ADDITIONAL_FIELDS. The signal update_managed_sso_email_domains() helper function aggregates email
    domains from managed SocialApp objects (social_app__managed=True) when
    they are modified and stores them in the new read only setting in
    constance.
    I've also created a data migration to initialize
    REGISTRATION_SSO_MANAGED_EMAIL_DOMAINS for existing database
    deployments.

    👀 Preview Steps

    1. ℹ️ Log in with a superuser / admin account.
    2. Navigate to Django Admin > Constance > Config
    3. 🟢 Confirm REGISTRATION_SSO_MANAGED_EMAIL_DOMAINS is placed directly
      after REGISTRATION_BLACKLIST_ERROR_MESSAGE
    4. Navigate to Account Extras > Social app custom datas
    5. Toggle Managed for a SocialApp and add an email domain
    6. Return to Django Admin > Constance > Config
    7. 🟢 Confirm REGISTRATION_SSO_MANAGED_EMAIL_DOMAINS lists the new
      domain in read-only format
  • auth: custom mutator for allauth endpoints (#7549)

    Adds a custom mutator for allauth endpoints to handle expected,
    non-server-error 4xx responses.

  • bulkProcessing: improve language blocking (#7412)

    Fixed bulk translation blocking languages it shouldn't: when a project
    has transcripts in more than one language for the same question, all of
    those languages became unavailable as translation targets.

    If some submissions were transcribed in English and others in Spanish
    for the same question, both English and Spanish disappeared from the
    language list when starting a bulk translation, leaving no way to
    translate one into the other. Now only the transcript you're actually
    translating from counts.

  • designSystem: move dynamic data attachments modal to mantine (#7410)

    Part of ongoing design system changes to move old components to new
    themed components. The dynamic data attachment modal has been updated to
    use mantine

  • designSystem: update bulk edit submissions form modal to mantine (#7455)

    Update the bulk edit submissions form modal to mantine

  • frontend: override Switch styles for Formbuilder (#7426)

  • frontend: standalone error pages app (#7438)

    New upcoming 404 and 500 error pages. Not visible to users until
    switches the error handlers over.

  • frontend: standalone UI language selector (#7449)

    A new component for selectin interface language.

  • frontend: style textarea to match other inputs (#7285)

    Kobo text inputs now look and behave consistently across TextInput,
    Textarea, NumberInput, and PasswordInput.

  • map: display points across antimeridian (#7475)

    Project → Data → Map now keep submissions collected on both sides of the
    180th meridian together, and keep showing them while you pan around the
    world.

    A project with points on both sides of the 180th meridian used to open
    zoomed out to the whole world, with the data split between the far left
    and far right edges of the map. The map now reads those coordinates the
    short way round and opens on the area the data actually covers.

    Panning sideways no longer leaves the data behind either: markers,
    clusters and heat map are drawn in every copy of the world on screen.
    Auto-fitting also leaves a small margin now, so points on the outer edge
    of a project's area aren't cut in half by the edge of the map.

  • organizations: enable sorting for organizations members API (#7329)

    Adds an ordering parameter to the organization members API

    The combined list of members and invitees is sorted in
    OrganizationMemberViewSet.get_queryset. The new parameter is also
    documented and the schema docs were updated. Since the get_queryset
    method converts the data to lists, the sorting is not done with the ORM
    but with a call to the sorted funcion.

  • organizations: add sorting to Members Table (#7424)

    The Members table columns can now be sorted by name, status, date added,
    and role.

  • organizations: display SSO column in Members Table (#7411)

    The Members table now has an SSO column showing which members sign in
    through single sign-on.

  • organizations: add search parameter to org members API (#7404)

    Adding the q parameter query parsing solution to the organizations
    members API so that frontend can have search filters for the members.

    Inside OrganizationMemberViewSet.get_queryset(), when the q parameter is
    present, we now run parse() twice:

    • For existing members: Parsed q against OrganizationUser fields (user__username__icontains, user__email__icontains, user__first_name__icontains, user__last_name__icontains)
    • For pending invites: Parsed q against OrganizationInvitation fields (invitee__username__icontains, invitee__email__icontains, invitee__first_name__icontains, invitee__last_name__icontains, invitee_identifier__icontains)
      We explicitly bypassed the global field denylist for this view by
      specifying custom allowed_lookup_fields (allowing kobo_auth.user fields
      like first_name and last_name and others mentioned in the ticket). I
      also include the parameter definition @extend_schema_view to get the
      correct documentation for the endpoint auto generated.

    Since the members API uses two different models (user for active
    members and invitee for pending invites), the lookups work like so:

    • To search ONLY active members: ?q=user__email:luis@example.com
    • To search ONLY pending invites: ?q=invitee__email:luis@example.com
    • To search across both groups simultaneously use the generic query
      (e.g., q=luis@example.com which will match any of the allowed fields
      using icontains) without specifying prefixes.
  • organizations: support q search on organization asset usage (#7515)

    📣 Summary

    Projects in the organization usage table can now be searched by name.

    📖 Description

    The usage page lists every project in the organization. This adds name
    search to the data behind that table, so the search box coming in
    can narrow the list instead of always showing everything.

    💭 Notes

    • Reuses the existing SearchFilter, chained before
      AssetOrganizationUsageFilter in OrganizationViewSet.asset_usage.
      Bare q terms search name__icontains.
    • Fielded queries still go through the query parser's allowlist, and the
      queryset was already scoped to the org owner, so nothing new is exposed.
    • Schema and orval client regenerated.

    👀 Preview steps

    1. Log in as an owner or admin of an org with a few projects.
    2. Open /api/v2/organizations/<org_id>/asset_usage/?q=<part of a project name>.

    🔴 On main: q is ignored and every project comes back.
    🟢 On this PR: only projects whose name contains the term (case
    insensitive). A q under 3 characters returns a 400.

  • organizations: members table search (#7523)

    The members list of a team or organization can now be searched.

    A search field sits next to the "Members" title. Typing at least three
    characters filters the list; the search covers name, username and email.
    When nothing matches, the table says so instead of showing an empty
    header.

  • queryParser: filter assets by multiple tags (#7388)

    Filtering projects by several tags now returns only the projects that
    have all of them.

    Combining tags in a search (e.g. "foo" and "bar") used to return
    nothing. It now returns the projects tagged with every tag asked for.
    Single-tag and "any of these tags" searches are unchanged.

  • sidebar: add text submission component and update display settings for question types (ac8dfa7)

  • sso: add managed sso properties (#7434)

    Add necessary fields for managed SSO work to Django admin.

    Allows admins to toggle the managed property of Social App Custom Data
    and add/remove associated email domains, as well as mark users as SSO exempt. These properties are not currently functional but are added
    here for ease of development.

  • sso: do not allow blocked domains in managed sso (#7444)

    Do not allow users to create managed SSO domains using domains that are
    on the blocklist or, if one is present, not in the allowlist.

  • sso: restrict how users with managed sso domains can register (#7447)

    Ensure users with an SSO-managed email cannot register using a username
    and password but can still register with SSO even if registration is
    otherwise closed.

  • sso: expose managed sso fields in /environment endpoint (#7463)

    Include information about managed SSO domains in the /environment
    endpoint.

  • sso: restrict account management of sso-managed accounts (#7483)

    Restrict account management for users with SSO-managed accounts.

    Users with SSO-managed accounts are forbidden from setting or changing a
    password or unlinking their account. An admin cannot add another social
    account or set a password either, but they may unlink.

  • sso: add functional index for email domains (#7508)

    Adds a new index to lookup email domains used by the enforce task and
    the registration checks

    When searching for users with specific domains, this index built using
    the expression split_part(lower(email), '@', 2) will help reduce the
    query execution time by around 21x compared to a sequential scan. This
    is important for registration checks and also for a enforce background
    task that will be triggered when an organization adds a managed domain.
    This task will query auth_user to find all existing user accounts with
    that domain and enforce SSO restriction rules on them.

    👷 Description for instance maintainers

    Note that the index creation is going to block writes to the auth_user
    table, so you will have to disable heavy migrations manually if this is
    a concern for your instance.

    IMPORTANT NOTE: This index won't be supported by SQLite, only by
    Postgres.

  • sso: update InAppMessage for managed sso (#7516)

  • sso: actions on adding managed domain (#7517)

    Update users who have managed SSO accounts to limit their login
    capabilities to that SSO, and notify users with the corresponding domain
    who do not yet have SSO set up that they need to do so.

  • sso: optional and customizable in-app message on confirmation page (#7531)

    When enabling managed SSO, admins can now choose whether to send the
    in-app "connect your SSO account" message, and edit its text before
    confirming.

    When a superuser enables managed SSO or adds a managed domain, the
    confirmation screen now has a "Send in-app message" checkbox and an
    editable message prefilled with the default text. Unchecking the box
    skips the message. When it is checked, the message cannot be empty.

  • sso: run update task on save managed SSO custom data (#7528)

    Update/notify affected users when SSOs are set to be managed or new
    domains are added.

  • sso: enforce SSO restrictions on connecting a managed account (#7542)

    🗒️ Checklist

    1. run linter locally
    2. update developer docs (API, README, inline, etc.), if any
    3. for user-facing doc changes create a Zulip thread at #Support Docs Updates, if any
    4. draft PR with a title <type>(<scope>)<!>: <title>
    5. assign yourself, tag PR: at least Front end and/or Back end
      or workflow
    6. fill in the template below and delete template comments
    7. review thyself: read the diff and repro the preview as written
    8. open PR & confirm that CI passes & request reviewers, if needed
    9. act on any greptile review below a 5/5 score or leave comment
      explaining why you won't
    10. delete this checklist section from the final squash commit
      before merging

    When a user of a managed domain connects their SSO account, disable
    other login methods and remove any in-app notifications.

  • sso: add provider detail endpoint (#7496)

    This PR adds a public API endpoint that resolves an SSO provider's URL
    identifier into its display name, so the upcoming React login screens
    can render the "Log in with …" page that Django templates render today.

    This PR adds GET /api/v2/social-apps/<provider_id>/, which returns the
    provider_id and name for a configured Social Application.

    Some organizations sign in through an SSO provider that is deliberately
    not shown on the public login page. Its Social Application has
    is_public unchecked, and they distribute a direct link to their own
    staff, for example: https://<kobo-server>/accounts/oidc/nca/login/.
    Only the nca part varies. Today, that URL renders a shared Django
    template titled "Log in with {{ SocialApp.name }}". The same template is
    used for every provider on every server.

    The Authentication Redesign deletes that template and rebuilds the
    screen in the SPA. To render it at a route like
    #/login/oidc/<provider_id>, the frontend needs to turn provider_id
    into a display name and distinguish a real provider from a typo so it
    can show the appropriate 404. The frontend cannot read the database
    directly, so it needs this endpoint.

    Nothing changes for users yet. The existing login pages are untouched.

  • sso: withdraw reminders when managed SSO is turned off (#7545)

    Turning managed SSO off, removing a managed email domain, or deleting
    the SSO provider now withdraws the in-app reminders it sent.

    When an organization's SSO provider stops being managed, or one of its
    email domains is removed, users on those domains no longer see the
    "Update your account" reminder asking them to link their SSO account.
    They can set a password and link other social accounts again. Deleting
    the provider itself has the same effect. Reminders left behind by
    earlier changes are cleared by the nightly job.

  • sso: make the signup-closed message configurable (#7544)

    Admins can now customize the message shown on the sign-up page when
    registration is closed.

    When account registration is turned off, anyone opening the sign-up page
    sees a fixed "Sign Up Closed" notice. Admins can now replace that text
    with their own, for example to point people to their organization's SSO
    login. If nothing is configured, the default notice stays as it was.

  • subsequences: run automatic qualitative analysis on text questions (#7458)

    Automatic qualitative analysis and translation now run on text
    questions, without needing a transcript first.

    Both features used to read their input from a transcript, so they only
    worked on audio and video questions. A text question has nothing to
    transcribe, so they were unavailable there.

    They now read the answer straight from the submission when the question
    is a text question. Audio and video keep working as before, through the
    transcript. Enabling an action on a question type that doesn't support
    it, like transcription on a text question, is now refused up front
    instead of failing later.

    Nobody declares what language a typed answer is in, so translation uses
    the form's own language when it knows it, and otherwise lets the
    translation service detect it. Very long answers are the exception: they
    go through a batch API that cannot detect a language, so those ask for
    the form language to be set.

  • usage: add support link (#7459)

    Adds a link to support.

Bug Fixes (23)
  • account: after updating password leave form (#7478)

    After successfully updating account password, we now redirect back to
    Security route.

  • accounts: restrict users to one linked SSO account (#7224)

    You can now only connect one single-sign-on (SSO) provider to your
    account at a time; attempting to link a second while one is already
    connected is blocked with a clear error.

    Previously the "connect SSO" action was only hidden in the UI once an
    account was linked, but the underlying link was still reachable, so a
    user could connect a second SSO provider by navigating to it directly.
    This change enforces the one-SSO-per-account rule on the server, so the
    restriction holds regardless of how the request is made.

    👷 Description for instance maintainers

    Adds a SocialAccountAdapter (kobo.apps.accounts.adapter) overriding
    pre_social_login, registered via the new SOCIALACCOUNT_ADAPTER
    setting. On a connect flow, if the authenticated user already has a
    SocialAccount for a different provider/uid, the link is rejected and
    a dedicated error page is rendered via allauth's own
    render_authentication_error() helper (the same helper allauth uses for
    its other SSO errors) explaining that only one SSO account can be linked
    at a time. Reconnecting the same account, normal SSO login, and signup
    are unaffected. The guard sits in allauth's complete_login before the
    account is created, so it covers both the classic redirect flow and the
    headless API (/api/v2/allauth/…).

  • api: fix schema for Imports Create to support XLS/base64 library upload payload (#7150)

    Fixed the ImportCreateRequestSerializer schema definition to include the
    missing fields base64Encoded, library, desired_type, totalFiles.
    The OpenAPI and Orval output files were updated to reflect this change.

  • attachments: match media filenames across unicode normalization forms (#7467)

    Media files with accented characters in their name now upload and play
    back instead of vanishing from the data table.

    A recording called something like Guérisseur.ogg would upload without
    any error, then land in the data table as plain text with no way to play
    or download it. Same thing on edits, and re-uploading never helped.
    Those files stay attached to their question now.

  • attachments: resolve question xpath on the name the client sent (#7609)

    Two files attached to the same submission are no longer mixed up when
    their names differ only by a character the storage layer rewrites.

    A submission answering two media questions could show one of its files
    under the wrong question, if the two file names differed only by a
    character the server rewrites when storing them, a space against an
    underscore for instance. Both names ended up recorded the same way, so
    only one of the two questions could be told apart, and the other file
    followed it. The file the client sent is now matched on the name they
    sent it under, so each one goes back to its own question.

  • auth: remove unnecessary managed sso domains sync in migration (#7591)

    Remove the call to sync_managed_sso_email_domains withing the
    migration 0010_socialappcustomdata_managed_socialappmanageddomain.py
    because it's unnecessary. It runs as a signal when social app custom
    data changes.

  • bulkProcessing: use rootUuid in bulk processing code (#7420)

    Fixes bulk transcription, translation and approval failing with an
    "Unknown submission UUIDs" error when the selection included submissions
    that had been edited.

  • bulkProcessing: review button showing up wrongly (#7425)

    The "Review" button for an automatic transcription now appears only in
    the column of the language it was transcribed into, instead of in every
    transcript column of that row.

  • celery: schedule trash bin and project ownership dispatchers on kpi_queue (#7513)

    Moves four scheduled orchestration tasks from kpi_low_priority_queue
    to kpi_queue:

    • trash_bin.task_restarter
    • trash_bin.garbage_collector
    • project_ownership.task_restarter
    • project_ownership.garbage_collector

    These tasks only enqueue work and finish in milliseconds, but they were
    scheduled onto the same queue as the long-running deletions and
    transfers they dispatch. When that queue backs up, the recovery path is
    stuck behind the backlog it exists to clear.

    The deletion and transfer tasks themselves are unchanged and stay on
    kpi_low_priority_queue.

  • designSystem: change project top tabs to old component (#7416)

    Brings back the old project top tabs compoenent

  • exports: include submissions in XLS (legacy) exports of transferred projects (#7623)

    XLS (legacy) exports are no longer empty for projects that were
    transferred to another owner.

    Once a project had been transferred to another account, its XLS (legacy)
    export came out with the column headers and no submissions. The table
    view and the CSV (legacy) export still showed everything. Projects that
    moved to an organization when their owner joined it were affected too,
    since that is also a transfer. The export now contains the submissions
    again.

  • formbuilder: duplicate imported question name (#7428)

    Fixed a bug where adding the same question or block from the Library
    multiple times gave every copy the same data column name in Formbuilder.

  • massemails: fix edge cases for one-off mass email campaigns (#7555)

    Fixes edge cases where one-off mass email campaigns (frequency = -1)
    could be accidentally resent if remaining enqueued records transitioned
    to STALE or if the Celery worker process terminated before the campaign
    was finalized

    • get_users_for_config() now excludes users who already have a
      terminal status (SENT, FAILED, STALE) under that one-off config
    • Any one-off campaign that has already run and has 0 enqueued records
      is disabled immediately, even if all remaining records went STALE.
      send_emails() now filters on jobs__isnull=False instead of
      pk__in=sender.config_ids.
    • If an unclosed one-off config has records and no eligible recipients
      remaining, generate_mass_email_user_lists() marks live = False
      directly without creating an empty MassEmailJob re-enqueueing records
    • Included new unit tests to validate that the edge cases are handled
      correctly
  • organizations: sort role by level of privilege instead of alphabetically (#7430)

    Organizations members are now sorted by level of privilege in the
    members API when using ordering=role (previously they were sorted
    alphabetically)

  • qrcode: drop qrcode.react in favour of react-qr-code (#7499)

    Internal frontend updates

  • sso: allow users to unlink unamanged accounts (#7539)

    Fixes a bug that was preventing users from unlinking unmanaged SSO
    accounts

  • sso: fix label in constance (#7558)

    Corrects a label in Constance, pointing users to the correct place to
    edit managed domains.

  • storybook: broken test (#7469)

  • tos: hide fields for MMO users (#7491)

    Members of a multi-member organization can now accept the Terms of
    Service — the form no longer asks them for organization details they
    aren't allowed to change.

  • trashBin: give each trash type its own restart budget (#7514)

    Splits MAX_RESTARTED_TASKS into three per-type settings for trash bin
    deletions, and stops task_restarter from swallowing its own soft time
    limit.

    Implements the findings from #7422 and supersedes it.

    The investigation showed that limiting how many deletions
    task_restarter enqueues does not limit DB pressure: concurrency is set
    by the size of the worker pool, not by how many messages are published.
    Two things from #7422 are worth keeping on their own merits, and this PR
    is just those two.

    1. Per-type restart budgets. _restart_stuck_tasks() sliced all three
      trash types with settings.MAX_RESTARTED_TASKS, which
      project_ownership/tasks.py also uses with a different "batch per run"
      meaning - one shared name, two unrelated behaviours. Each trash type now
      has its own setting:
    • MAX_RESTARTED_ACCOUNT_DELETIONS
    • MAX_RESTARTED_PROJECT_DELETIONS
    • MAX_RESTARTED_ATTACHMENT_DELETIONS

    All default to 100, so behaviour is unchanged. They are separate because
    the three deletions do not cost the same.

    1. Do not swallow SoftTimeLimitExceeded. In the enqueue block it was
      caught by the generic except Exception and logged as Could not restart
      #, which is wrong on both counts: nothing is wrong with the
      object, and the restarter had already claimed it, so it sat untouched
      until the next stuck threshold ~76 minutes later. It now restores
      date_modified and re-raises.
  • trashBin: suspend owner submissions during project deletion (#7553)

    Permanently deleting a project or an account no longer fails when that
    account keeps receiving submissions while the deletion runs.

    While a project is being permanently deleted, incoming submissions for
    all projects of that project's owner are paused. Data collection apps
    get the usual "temporarily unavailable" answer and retry on their own,
    so no data is lost. The pause is lifted as soon as the deletion ends,
    whether it succeeded or not. Until now a submission arriving in the
    middle of a deletion could make the deletion fail and get rescheduled.

  • xlsImport: match project languages to XLSForm languages (#7417)

    Re-uploading an XLSForm now sets the project languages to exactly the
    languages of the uploaded file, instead of keeping languages that were
    removed from it.

    Uploading a form with three languages and re-uploading it with two used
    to leave the third language defined with empty strings, and removing all
    languages created an unnamed language. Project languages now always
    match the file: a language stays only if at least one survey or
    choices column still uses it, and a file with plain label columns
    brings the project back to "no languages defined".

  • xlsImport: block translated XLSForm missing language-specific columns (#7473)

    Uploading an XLSForm that has translations but leaves some columns
    untranslated (like a plain hint next to label::English (en)) is now
    blocked with a clear error instead of creating a broken project.

    Until now, importing such a form quietly added an "Unnamed language" to
    the project, which could break the form builder, or silently dropped the
    missing translations. The import now fails right away with a message
    naming the column, e.g. "The hint column is not translated". Forms
    with no translations at all import as before.

Continous Integration (1)
  • du: remove du as a pipeline trigger INFRA-598 (#7484)
Build & Dependencies (5)
  • deps: bump the actions-deps group across 1 directory with 3 updates (#7471)

  • deps: bump the actions-deps group across 1 directory with 2 updates (#7547)

  • deps-dev: bump fast-uri from 3.1.5 to 3.1.7 (#7530)

  • frontend: bump insecure js-yaml versions (#7427)

    Updated the js-yaml dependency to patch a set of denial-of-service
    advisories.

  • frontend: bump mobx-react (#7480)

Testing (2)
  • storybook: improve map stories (#7503)
  • storybook: even sturdier fix for flaky map test (#7518)
Refactor (9)
  • assets: drop deprecated calls to ReactDOM.findDOMnode (#7495)

    Internal improvements for frontend

  • auth: move sessionStore logout handling to RQ (#7550)

    Removes logout handling from sessionStore and moves it to react query
    hooks called by components.

  • frontend: SSOT for columns order (#7389)

    Columns are now listed in the same order in every place that shows them:
    Data Table, "hide fields" menu, Single Submission modal, and the field
    picker in Downloads.

  • frontend: replace KoboSelect with Mantine Select (#7408)

    Migrated multiple different selects from deprecated KoboSelect to
    Mantine's Select.

  • frontend: replace react-select with Mantine Select (#7462)

    A few dropdowns are now unified to look like all the other dropdowns.

  • frontend: remove TextBox use Mantine (#7487)

    Text fields across the app now share one consistent look and size, and
    password fields — including your API key — are revealed with a toggle
    inside the field itself.

  • library: typescriptize and de-mixin LibraryNewItemForm (#7497)

    Update icons in Library "NEW" modal.

  • library: move template and collection creation modals to mantine (#7506)

    Part of ongoing process to modernize old frontend components. Updates
    the modals for library creation of collections and templates.

  • project: typescriptize formSummary (#7507)

Styling (2)
Chores (6)
  • CODEOWNERS: add James K to replace Kalvis, remove Jackie, reduce John (#7505)

  • bulkProcessing: remove feature flag (#7439)

  • constants: refactor loose interval constants into one place (#7465)

    Internal refactor

  • frontend: drop use-immer (#7479)

  • frontend: drop unused dependencies (#7482)

  • mfa: remove the django-trench backend dependency (#7396)

    Internal cleanup — removed an old, unused authentication library. No
    change to how two-step verification (MFA) works.

    Two-step verification already runs on our current authentication stack;
    an older library it used to depend on was left behind with no live data
    still using it. This removes that old library and its leftover database
    tables. Nothing changes for you — enabling, using, and disabling
    two-step verification all behave exactly as before.

Revert (3)
  • revert "remove bulk processing feature flags" (c5a03a6)
  • revert "drop unused dependencies" (465a1e5)
  • revert "feat(sidebar): add text submission component and update display settings for question types" (e05ec0f)
Other (2)
  • remove bulk processing feature flags (3432526)
  • drop unused dependencies (3c85be5)

Full Changelog: https://github.com/kobotoolbox/kpi/compare/2.026.33b..2.026.37

Don't miss a new kpi release

NewReleases is sending notifications on new releases.