Skip to content

[MergeDups] Enable word/sense protection bypass #3445

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 36 commits into from
Feb 28, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
167527b
[MergeDups] Enable protection override
imnasnainaec Nov 12, 2024
7185f48
Reset hasProtected when new words loaded
imnasnainaec Nov 13, 2024
e21032c
Add reducer test cases for protect override
imnasnainaec Nov 13, 2024
04400e2
Add project setting for overriding protected words/senses
imnasnainaec Nov 13, 2024
9abdab1
Add pop-up confirmation for overriding protected senses
imnasnainaec Nov 14, 2024
b4d139b
Merge branch 'master' into merge-unprotect
imnasnainaec Nov 14, 2024
cfdf244
Merge branch 'master' into merge-unprotect
imnasnainaec Dec 13, 2024
c5e9cc6
Extract protect-reason text functions
imnasnainaec Dec 13, 2024
72aad50
Unify sense refs and protection override
imnasnainaec Dec 16, 2024
d232d4b
Fix test
imnasnainaec Dec 16, 2024
0d0ebc9
Merge branch 'master' into merge-unprotect
imnasnainaec Jan 8, 2025
4a6445e
Merge branch 'master' into merge-unprotect
imnasnainaec Jan 8, 2025
0cbc9da
Remove redundant consts
imnasnainaec Jan 8, 2025
ef4fafb
Merge branch 'master' into merge-unprotect
imnasnainaec Jan 14, 2025
aa35ddf
Merge branch 'master' into merge-unprotect
imnasnainaec Jan 14, 2025
93118c0
Warn when displacing protected sidebar sense
imnasnainaec Jan 14, 2025
79654b5
Fix non-top sidebar sense bug
imnasnainaec Jan 14, 2025
3f7c4d5
Merge branch 'master' into merge-unprotect
imnasnainaec Jan 14, 2025
a75c25f
Merge branch 'master' into merge-unprotect
imnasnainaec Jan 15, 2025
0c40e4b
Merge branch 'master' into merge-unprotect
imnasnainaec Jan 15, 2025
a065f51
Show protected word warning, even with multiple senses
imnasnainaec Jan 30, 2025
ec426b1
Add toasts to explain illegal drops
imnasnainaec Jan 30, 2025
77aafaa
Merge branch 'master' into merge-unprotect
imnasnainaec Jan 30, 2025
70a80a7
Remove useless ?; Prefer undefined to null
imnasnainaec Jan 30, 2025
0a5f55b
Split off pr #3560
imnasnainaec Jan 31, 2025
e8941de
Merge branch 'master' into merge-unprotect
imnasnainaec Jan 31, 2025
e8a7e33
Merge branch 'master' into merge-unprotect
imnasnainaec Feb 7, 2025
47eb91d
Merge branch 'master' into merge-unprotect
imnasnainaec Feb 12, 2025
1c6be91
Localize bypass checkbox text
imnasnainaec Feb 12, 2025
36c6204
Update User Guide
imnasnainaec Feb 12, 2025
be25106
Merge branch 'master' into merge-unprotect
imnasnainaec Feb 12, 2025
e739975
Fix typo
imnasnainaec Feb 18, 2025
c1e6bf7
Merge branch 'master' into merge-unprotect
imnasnainaec Feb 27, 2025
a46eb43
Add missing toast; Improve comments, tests
imnasnainaec Feb 27, 2025
d979f31
Fix its
imnasnainaec Feb 27, 2025
eb6fd6b
Merge branch 'master' into merge-unprotect
imnasnainaec Feb 27, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Backend.Tests/Models/ProjectTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ public void TestClone()
DefinitionsEnabled = true,
GrammaticalInfoEnabled = true,
AutocompleteSetting = OffOnSetting.On,
ProtectedDataOverrideEnabled = OffOnSetting.Off,
SemDomWritingSystem = new("fr", "Français"),
VernacularWritingSystem = new("en", "English", "Calibri"),
AnalysisWritingSystems = new() { new("es", "Español") },
Expand Down
9 changes: 9 additions & 0 deletions Backend/Models/Project.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ public class Project
[BsonRepresentation(BsonType.String)]
public OffOnSetting AutocompleteSetting { get; set; }

[Required]
[BsonElement("protectedDataOverrideEnabled")]
[BsonRepresentation(BsonType.String)]
public OffOnSetting ProtectedDataOverrideEnabled { get; set; }

[Required]
[BsonElement("semDomWritingSystem")]
public WritingSystem SemDomWritingSystem { get; set; }
Expand Down Expand Up @@ -93,6 +98,7 @@ public Project()
DefinitionsEnabled = false;
GrammaticalInfoEnabled = false;
AutocompleteSetting = OffOnSetting.On;
ProtectedDataOverrideEnabled = OffOnSetting.Off;
SemDomWritingSystem = new();
VernacularWritingSystem = new();
AnalysisWritingSystems = new();
Expand All @@ -117,6 +123,7 @@ public Project Clone()
DefinitionsEnabled = DefinitionsEnabled,
GrammaticalInfoEnabled = GrammaticalInfoEnabled,
AutocompleteSetting = AutocompleteSetting,
ProtectedDataOverrideEnabled = ProtectedDataOverrideEnabled,
SemDomWritingSystem = SemDomWritingSystem.Clone(),
VernacularWritingSystem = VernacularWritingSystem.Clone(),
AnalysisWritingSystems = AnalysisWritingSystems.Select(ws => ws.Clone()).ToList(),
Expand All @@ -140,6 +147,7 @@ public bool ContentEquals(Project other)
other.DefinitionsEnabled == DefinitionsEnabled &&
other.GrammaticalInfoEnabled == GrammaticalInfoEnabled &&
other.AutocompleteSetting.Equals(AutocompleteSetting) &&
other.ProtectedDataOverrideEnabled.Equals(ProtectedDataOverrideEnabled) &&
other.SemDomWritingSystem.Equals(SemDomWritingSystem) &&
other.VernacularWritingSystem.Equals(VernacularWritingSystem) &&

Expand Down Expand Up @@ -191,6 +199,7 @@ public override int GetHashCode()
hash.Add(DefinitionsEnabled);
hash.Add(GrammaticalInfoEnabled);
hash.Add(AutocompleteSetting);
hash.Add(ProtectedDataOverrideEnabled);
hash.Add(SemDomWritingSystem);
hash.Add(VernacularWritingSystem);
hash.Add(AnalysisWritingSystems);
Expand Down
1 change: 1 addition & 0 deletions Backend/Repositories/ProjectRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ public async Task<ResultOfUpdate> Update(string projectId, Project project)
.Set(x => x.DefinitionsEnabled, project.DefinitionsEnabled)
.Set(x => x.GrammaticalInfoEnabled, project.GrammaticalInfoEnabled)
.Set(x => x.AutocompleteSetting, project.AutocompleteSetting)
.Set(x => x.ProtectedDataOverrideEnabled, project.ProtectedDataOverrideEnabled)
.Set(x => x.SemDomWritingSystem, project.SemDomWritingSystem)
.Set(x => x.VernacularWritingSystem, project.VernacularWritingSystem)
.Set(x => x.AnalysisWritingSystems, project.AnalysisWritingSystems)
Expand Down
2 changes: 1 addition & 1 deletion docs/user_guide/docs/goals.es.md
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ la siguiente manera:

A sense can only have one part of speech. If two senses are merged that have different parts of speech in the same general category, the parts of speech will be combined, separated by a semicolon (;). However, if they have different general categories, only the first one is preserved.

#### Entradas y acepciones protegidas
#### Entradas y acepciones protegidas {#protected-entries-and-senses}

Si una entrada o acepción importada contiene datos no admitidos en The Combine (por ejemplo, etimologías o inversiones
de sentido), se protege para evitar su eliminación. Si una acepción está protegida, su tarjeta tendrá un fondo
Expand Down
2 changes: 1 addition & 1 deletion docs/user_guide/docs/goals.md
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ information will appear in the Merge Duplicate sense cards as follows:

A sense can only have one part of speech. If two senses are merged that have different parts of speech in the same general category, the parts of speech will be combined, separated by a semicolon (;). However, if they have different general categories, only the first one is preserved.

#### Protected Entries and Senses
#### Protected Entries and Senses {#protected-entries-and-senses}

If an imported entry or sense contains data not supported in The Combine (e.g., etymologies or sense reversals), it is
protected to prevent its deletion. If a sense is protected, its card will have a yellow background—it cannot be deleted
Expand Down
2 changes: 1 addition & 1 deletion docs/user_guide/docs/goals.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ You can delete an entire entry by clicking the

A sense can only have one part of speech. If two senses are merged that have different parts of speech in the same general category, the parts of speech will be combined, separated by a semicolon (;). However, if they have different general categories, only the first one is preserved.

#### 受保护的词条与词义
#### 受保护的词条与词义 {#protected-entries-and-senses}

如果导入的词条或词义项包含 The Combine 不支持的资料(例如词源或词义反转),它将被保护以防止删除。 受保护的词义卡会有一个黄
色背景。它无法被删除,也无法(被合并)放入另一张词义卡。 如果整个词条受保护,其列将具有黄色标题(位于土语和旗标位置)。 当受
Expand Down
6 changes: 6 additions & 0 deletions docs/user_guide/docs/project.es.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,12 @@ más detalles.
(Esto no afecta a las sugerencias ortográficas para la glosa, ya que dichas sugerencias se basan en un diccionario
independiente de los datos existentes del proyecto)

#### Protected Data Override

The default setting is Off. Change this to On to allow project users in Merge Duplicates to override the
[protection](goals.md#protected-entries-and-senses) of words and senses that were imported with data not handled by The
Combine.

#### Archivar el proyecto

Sólo está disponible para el Propietario del proyecto. Archivar un proyecto lo hace inaccesible para todos los usuarios.
Expand Down
6 changes: 6 additions & 0 deletions docs/user_guide/docs/project.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,12 @@ entry, rather than creating a (mostly) duplicate to something previously entered
(This does not affect spelling suggestions for the gloss, since those suggestions are based on a dictionary independent
of existing project data.)

#### Protected Data Override

The default setting is Off. Change this to On to allow project users in Merge Duplicates to override the
[protection](goals.md#protected-entries-and-senses) of words and senses that were imported with data not handled by The
Combine.

#### Archive Project

This is only available to the project Owner. Archiving a project makes it inaccessible to all users. This can only be
Expand Down
6 changes: 6 additions & 0 deletions docs/user_guide/docs/project.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@ Combine、[WeSay](https://software.sil.org/wesay)、[FLEx](https://software.sil.

(这不影响对注释的拼写建议,因为这些建议是基于独立于现有项目数据的字典的)。

#### Protected Data Override

The default setting is Off. Change this to On to allow project users in Merge Duplicates to override the
[protection](goals.md#protected-entries-and-senses) of words and senses that were imported with data not handled by The
Combine.

#### 存档项目

这只有项目所有者才能获取。 将项目存档后,所有用户都无法访问该项目。 只有网站管理员才能取消这一操作。 如果用户希望从服务
Expand Down
13 changes: 10 additions & 3 deletions public/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,10 @@
"on": "On",
"hint": "In Data Entry, suggest existing Vernaculars similar to the Vernacular being typed."
},
"protectedDataOverride": {
"hint": "In Merge Duplicates, allow overriding protection of protected words and senses.",
"label": "Protected Data Override"
},
"invite": {
"inviteByEmailLabel": "Invite by Email",
"userExists": "This user is already registered.",
Expand Down Expand Up @@ -417,13 +421,16 @@
"delete": "Delete sense",
"deleteDialog": "Delete this sense?",
"protectedSense": "This sense was imported with data that The Combine doesn't handle.",
"protectedSenseInfo": "This sense cannot be deleted or dropped into another sense. You may still move it to another word or drop other senses into this one to merge them.",
"protectedSenseInfo": "This sense cannot be safely deleted or dropped into another sense. You may still move it to another word or drop other senses into this one to merge them.",
"protectedWord": "This word was imported with data that The Combine doesn't handle.",
"protectedWordInfo": "To prevent deletion, the final sense of this word cannot be removed.",
"protectedWordInfo": "To prevent deletion, the final sense of this word cannot be safely removed.",
"protectedData": "Protected data: {{ val }}",
"protectedOverride": "Allow deletion of protected words or senses?",
"protectedOverrideWarning": "The following data will be lost: {{ val }}",
"deleteProtectedSenseWarning": "You cannot delete a protected sense.",
"dropIntoSidebarWarning": "You cannot drop senses into the sidebar.",
"dropProtectedSenseWarning": "You cannot merge a protected sense into another sense."
"dropProtectedSenseWarning": "You cannot merge a protected sense into another sense.",
"orderProtectedSidebarWarning": "A protected sense in the sidebar cannot be moved out of top position."
},
"protectReason": {
"annotations": "annotations",
Expand Down
6 changes: 6 additions & 0 deletions src/api/models/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,12 @@ export interface Project {
* @memberof Project
*/
autocompleteSetting: OffOnSetting;
/**
*
* @type {OffOnSetting}
* @memberof Project
*/
protectedDataOverrideEnabled: OffOnSetting;
/**
*
* @type {WritingSystem}
Expand Down
48 changes: 48 additions & 0 deletions src/components/ProjectSettings/ProjectProtectedOverride.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { HelpOutline } from "@mui/icons-material";
import { Grid, MenuItem, Select, Tooltip } from "@mui/material";
import { type ReactElement } from "react";
import { useTranslation } from "react-i18next";

import { OffOnSetting } from "api/models";
import { type ProjectSettingProps } from "components/ProjectSettings/ProjectSettingsTypes";

export default function ProjectProtectedOverride(
props: ProjectSettingProps
): ReactElement {
const { t } = useTranslation();

const updateProtectOverrideSetting = async (
protectedDataOverrideEnabled: OffOnSetting
): Promise<void> => {
await props.setProject({ ...props.project, protectedDataOverrideEnabled });
};

return (
<Grid container>
<Grid>
<Select
variant="standard"
value={props.project.protectedDataOverrideEnabled}
onChange={(e) =>
updateProtectOverrideSetting(e.target.value as OffOnSetting)
}
>
<MenuItem value={OffOnSetting.Off}>
{t("projectSettings.autocomplete.off")}
</MenuItem>
<MenuItem value={OffOnSetting.On}>
{t("projectSettings.autocomplete.on")}
</MenuItem>
</Select>
</Grid>
<Grid>
<Tooltip
title={t("projectSettings.protectedDataOverride.hint")}
placement={document.body.dir === "rtl" ? "left" : "right"}
>
<HelpOutline fontSize="small" />
</Tooltip>
</Grid>
</Grid>
);
}
17 changes: 17 additions & 0 deletions src/components/ProjectSettings/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
People,
PersonAdd,
RecordVoiceOver,
RemoveModerator,
Settings,
Sms,
} from "@mui/icons-material";
Expand Down Expand Up @@ -53,6 +54,7 @@ import ProjectLanguages, {
SemanticDomainLanguage,
} from "components/ProjectSettings/ProjectLanguages";
import ProjectName from "components/ProjectSettings/ProjectName";
import ProjectProtectedOverride from "components/ProjectSettings/ProjectProtectedOverride";
import ProjectSchedule from "components/ProjectSettings/ProjectSchedule";
import ProjectSelect from "components/ProjectSettings/ProjectSelect";
import ActiveProjectUsers from "components/ProjectUsers/ActiveProjectUsers";
Expand Down Expand Up @@ -80,6 +82,7 @@ export enum Setting {
Import = "SettingImport",
Languages = "SettingLanguages",
Name = "SettingName",
ProtectOverride = "SettingProtectOverride",
Schedule = "SettingSchedule",
Speakers = "SettingSpeakers",
UserAdd = "SettingUserAdd",
Expand Down Expand Up @@ -176,6 +179,20 @@ export default function ProjectSettingsComponent(): ReactElement {
/>
)}

{/* Protected data override toggle */}
{permissions.includes(Permission.DeleteEditSettingsAndUsers) && (
<BaseSettings
icon={<RemoveModerator data-testid={Setting.ProtectOverride} />}
title={t("projectSettings.protectedDataOverride.label")}
body={
<ProjectProtectedOverride
project={project}
setProject={updateProject}
/>
}
/>
)}

{/* Archive project */}
{permissions.includes(Permission.Archive) && (
<BaseSettings
Expand Down
2 changes: 2 additions & 0 deletions src/components/ProjectSettings/tests/SettingsTabTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const settingsByTab: Record<ProjectSettingsTab, Setting[]> = {
Setting.Archive,
Setting.Autocomplete,
Setting.Name,
Setting.ProtectOverride,
],
[ProjectSettingsTab.ImportExport]: [Setting.Export, Setting.Import],
[ProjectSettingsTab.Languages]: [Setting.Languages],
Expand All @@ -34,6 +35,7 @@ const settingsByPermission: Record<Permission, Setting[]> = {
Setting.DomainsCustom,
Setting.Languages,
Setting.Name,
Setting.ProtectOverride,
Setting.Speakers,
Setting.UserAdd,
Setting.Users,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ export default function DragSense(props: DragSenseProps): ReactElement {
arraysEqual<string>
);
const dispatch = useAppDispatch();
const overrideProtection = useAppSelector(
(state: StoreState) => state.mergeDuplicateGoal.overrideProtection
);
const sidebar = useAppSelector(
(state: StoreState) => state.mergeDuplicateGoal.tree.sidebar
);
Expand Down Expand Up @@ -88,7 +91,7 @@ export default function DragSense(props: DragSenseProps): ReactElement {
key={props.senseRef.mergeSenseId}
draggableId={JSON.stringify(props.senseRef)}
index={props.index}
isDragDisabled={props.isOnlySenseInProtectedWord}
isDragDisabled={props.isOnlySenseInProtectedWord && !overrideProtection}
>
{(provided, snapshot): ReactElement => (
<Card
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ export default function DropWord(props: DropWordProps): ReactElement {
senseRef={{
isSenseProtected: senses[0].protected,
mergeSenseId: id,
protectReasons: senses[0].protectReasons,
wordId: props.wordId,
}}
isOnlySenseInProtectedWord={protectedWithOneChild}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,16 @@ export default function SidebarDragSense(
const ref = state.mergeDuplicateGoal.tree.sidebar.senseRef;
return JSON.stringify({ ...ref, order: props.index });
});
const overrideProtection = useAppSelector(
(state: StoreState) => state.mergeDuplicateGoal.overrideProtection
);

return (
<Draggable
key={props.mergeSense.sense.guid}
draggableId={draggableId}
index={props.index}
isDragDisabled={props.mergeSense.protected}
isDragDisabled={props.mergeSense.protected && !overrideProtection}
>
{(provided, snapshot): ReactElement => (
<div
Expand Down
Loading
Loading