feat: gate record creation on isUICreatable only, decoupled from isSystem (#21527)
## Context The generic "create a record" UI affordance previously required `!isSystem`, conflating two distinct concerns: **"hidden from Data Model"** and **"not user-creatable"**. This blocked legitimately creatable system objects (e.g. marketing message lists kept `isSystem: true` only to stay out of the Data Model). This PR makes creatability depend on `isUICreatable` alone, so visibility (`isSystem`) and creatability (`isUICreatable`) become independent. ## Changes - **Front gate** (`canCreateRecordsForObjectMetadataItem.ts`): drop the `!isSystem` clause — creatability is now `isUICreatable && !readOnly`. Updated comment + unit test. - **Command menu** (`standard-command-menu-item.constant.ts`): drop the matching `not objectMetadataItem.isSystem` clause from the `createNewRecord` availability expression so the "Create new X" command mirrors the front gate. Existing workspaces get this via the already-present `SyncCreateRecordCommandAvailabilityExpressionCommand`, which re-syncs from the live definition. - **Standard object audit** (`create-standard-flat-object-metadata.util.ts`): the 15 sync/system-created standard objects that relied on `!isSystem` to stay non-creatable now set `isUICreatable: false` (attachment, blocklist, calendar*/message*/note/task targets, message, messageThread, messageParticipant, timelineActivity, callRecording, workflowAutomatedTrigger, …). `workflowRun`/`workflowVersion`/`workspaceMember` were already `false`. Non-system objects (company, person, note, opportunity, dashboard, task, workflow) are untouched. - **Backfill**: new fast instance command (`2-13-…-1781277480000-backfill-non-ui-creatable-standard-system-objects.ts`) runs `UPDATE core.objectMetadata SET isUICreatable = false` for those standard system objects (symmetric `down`), registered in `instance-commands.constant.ts`. `isSystem` and Data-Model visibility logic are unchanged. ## Verification - Front gate unit test (7 pass), standard-application suite incl. callRecording (10 pass) - `oxlint` clean on all changed files - `twenty-server` and `twenty-front` typecheck green 🤖 https://claude.ai/code/session_01TF4kjD56hHjP31wkHPxPv3 --- _Generated by [Claude Code](https://claude.ai/code/session_01TF4kjD56hHjP31wkHPxPv3)_ <!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/21527?utm_source=github" target="_blank" rel="noopener noreferrer" data-no-image-dialog="true"><picture><source media="(prefers-color-scheme: dark)" srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img alt="Review in cubic" src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a> <!-- End of auto-generated description by cubic. --> --------- Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
+51
@@ -0,0 +1,51 @@
|
||||
import { DataSource, QueryRunner } from 'typeorm';
|
||||
|
||||
import { RegisteredInstanceCommand } from 'src/engine/core-modules/upgrade/decorators/registered-instance-command.decorator';
|
||||
import { SlowInstanceCommand } from 'src/engine/core-modules/upgrade/interfaces/slow-instance-command.interface';
|
||||
|
||||
// The create-record UI affordance is now gated on isUICreatable alone (the
|
||||
// !isSystem clause was dropped). Standard system objects relied on that clause
|
||||
// to stay non-creatable, so their isUICreatable (default true) must be flipped
|
||||
// to false to preserve behavior. Sync/system-created objects only — the
|
||||
// user-creatable system objects (e.g. marketing message lists) keep true.
|
||||
const NON_UI_CREATABLE_STANDARD_SYSTEM_OBJECT_NAMES = [
|
||||
'attachment',
|
||||
'blocklist',
|
||||
'calendarChannelEventAssociation',
|
||||
'calendarEventParticipant',
|
||||
'calendarEvent',
|
||||
'callRecording',
|
||||
'messageChannelMessageAssociation',
|
||||
'messageChannelMessageAssociationMessageFolder',
|
||||
'messageParticipant',
|
||||
'messageThread',
|
||||
'message',
|
||||
'noteTarget',
|
||||
'taskTarget',
|
||||
'timelineActivity',
|
||||
'workflowAutomatedTrigger',
|
||||
];
|
||||
|
||||
@RegisteredInstanceCommand('2.13.0', 1781277480000, { type: 'slow' })
|
||||
export class BackfillNonUiCreatableStandardSystemObjectsSlowInstanceCommand
|
||||
implements SlowInstanceCommand
|
||||
{
|
||||
async runDataMigration(dataSource: DataSource): Promise<void> {
|
||||
await dataSource.query(
|
||||
`UPDATE "core"."objectMetadata"
|
||||
SET "isUICreatable" = false
|
||||
WHERE "isSystem" = true
|
||||
AND "isUICreatable" = true
|
||||
AND "nameSingular" = ANY($1)`,
|
||||
[NON_UI_CREATABLE_STANDARD_SYSTEM_OBJECT_NAMES],
|
||||
);
|
||||
}
|
||||
|
||||
public async up(_queryRunner: QueryRunner): Promise<void> {
|
||||
return;
|
||||
}
|
||||
|
||||
public async down(_queryRunner: QueryRunner): Promise<void> {
|
||||
return;
|
||||
}
|
||||
}
|
||||
+2
@@ -63,6 +63,7 @@ import { DropIsCustomFromObjectAndFieldMetadataFastInstanceCommand } from 'src/d
|
||||
import { DropEmailingDomainDriverColumnFastInstanceCommand } from 'src/database/commands/upgrade-version-command/2-11/2-11-instance-command-fast-1780926908000-drop-emailing-domain-driver-column';
|
||||
import { ViewOverridableEntityFastInstanceCommand } from 'src/database/commands/upgrade-version-command/2-12/2-12-instance-command-fast-1781114009075-view-overridable-entity';
|
||||
import { RenameIsUiReadOnlyToIsUiEditableFastInstanceCommand } from 'src/database/commands/upgrade-version-command/2-13/2-13-instance-command-fast-1781277453604-rename-is-ui-read-only-to-is-ui-editable';
|
||||
import { BackfillNonUiCreatableStandardSystemObjectsSlowInstanceCommand } from 'src/database/commands/upgrade-version-command/2-13/2-13-instance-command-slow-1781277480000-backfill-non-ui-creatable-standard-system-objects';
|
||||
|
||||
export const INSTANCE_COMMANDS = [
|
||||
AddViewFieldGroupIdIndexOnViewFieldFastInstanceCommand,
|
||||
@@ -128,4 +129,5 @@ export const INSTANCE_COMMANDS = [
|
||||
DropEmailingDomainDriverColumnFastInstanceCommand,
|
||||
ViewOverridableEntityFastInstanceCommand,
|
||||
RenameIsUiReadOnlyToIsUiEditableFastInstanceCommand,
|
||||
BackfillNonUiCreatableStandardSystemObjectsSlowInstanceCommand,
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user