Skip to content

Commit 109e1a2

Browse files
committed
Merge remote-tracking branch 'origin/production' into issue-4685
2 parents 22ed268 + 87e7371 commit 109e1a2

File tree

94 files changed

+4641
-3790
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

94 files changed

+4641
-3790
lines changed

CHANGELOG.md

+84
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,90 @@ All notable changes to this project will be documented in this file.
99

1010
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
1111

12+
## [7.9.5](https://github.com/specify/specify7/compare/v7.9.4...v7.9.5) (20 May 2024)
13+
14+
This release has for objective to migrated Workbench to React to align it with the rest of the application to ensure consistency and modernising the codebase.
15+
Workbench functionalities remain unchanged but this release helps improve internal development and maintenance of the workbench going forward.
16+
[Workbench Conversion](https://github.com/specify/specify7/pull/4637)
17+
18+
### Added
19+
20+
- **Live Search feature in Workbench**
21+
22+
## [7.9.4](https://github.com/specify/specify7/compare/v7.9.3.1...v7.9.4) (13 May 2024)
23+
24+
### Added
25+
26+
- **Added a visual editor for some app resources** ([#2796](https://github.com/specify/specify7/pull/2796))
27+
- The initial release supports
28+
- [Form Definitions](https://discourse.specifysoftware.org/t/editing-forms-in-specify-7/1557)
29+
- [Table Formats](https://discourse.specifysoftware.org/t/editing-table-formats-and-aggregations-in-specify-7/1558)
30+
- [Table Aggregation](https://discourse.specifysoftware.org/t/editing-table-formats-and-aggregations-in-specify-7/1558)
31+
- Visual editors for additional app resources will be added in the future.
32+
- Enforce uniqueness requirements for name ([#4164](https://github.com/specify/specify7/pull/4164))
33+
- Added query combo box for Specify Users in Export Feed ([#4345](https://github.com/specify/specify7/pull/4345))
34+
- Added support for [conditional forms](https://discourse.specifysoftware.org/t/editing-forms-in-specify-7/1557#conditional-rendering-22)
35+
- Added a preview for web links, table formats, and table aggregations ([#4343](https://github.com/specify/specify7/pull/4343))
36+
- Added numerous improvements to distinct queries ([#4596](https://github.com/specify/specify7/pull/4596)*Requested by University of Kansas, University of Michigan, Commonwealth Scientific and Industrial Research Organisation*)
37+
- Added links to records in distinct queries where all columns returned are the same
38+
- Introduced the option to view groups of grouped records in distinct query results
39+
- Distinct queries can now be exported to CSV
40+
- Added the ability to display only nodes with associated records in the tree viewer ([#4023](https://github.com/specify/specify7/pull/4023) – Commonwealth Scientific and Industrial Research Organisation)
41+
- Added the ability to set default values for boolean/checkbox fields in the form definition (#4585*Requested by University of Michigan, Commonwealth Scientific and Industrial Research Organisation*
42+
- Added the ability to import and export WorkBench data set upload plans ([#1363](https://github.com/specify/specify7/issues/1363)*Requested by Commonwealth Scientific and Industrial Research Organisation and others*)
43+
- Subforms can now be collapsed ([#3642](https://github.com/specify/specify7/pull/3642)*Requested by Commonwealth Scientific and Industrial Research Organisation and Agriculture and Agri-Food Canada*)
44+
- Added a new form element to show a view button next to a query combo box ([#4199](https://github.com/specify/specify7/pull/4199)*Requested by South African Institute for Aquatic Biodiversity*)
45+
- Fields in the Schema Config can now be sorted by their visibility status ([#3516](https://github.com/specify/specify7/pull/3516))
46+
- Added the ability to switch to 'Basic View' and 'Hide Field Mapper' in embedded query dialogs ([#2863](https://github.com/specify/specify7/pull/2863))
47+
- Added pagination when viewing large lists of resources (record sets, queries, etc.) to improve performance ([#3195](https://github.com/specify/specify7/pull/3195))
48+
- Added a preference to have records in read-only mode by default requiring the user to press an edit button before changes can be made ([#3553](https://github.com/specify/specify7/pull/3553))
49+
- Added the ability to hide the plus button in forms ([#3669](https://github.com/specify/specify7/pull/3669)*Requested by Commonwealth Scientific and Industrial Research Organisation*)
50+
- Added bulk resolve and bulk return when returning loans ([#4224](https://github.com/specify/specify7/pull/4224)*Requested by Commonwealth Scientific and Industrial Research Organisation*)
51+
- Kept the search panel open when modifying XML form ([#4260](https://github.com/specify/specify7/pull/4260))
52+
- A warning is now displayed if there are no available preparations associated with a catalog number when making an interaction ([#4195](https://github.com/specify/specify7/pull/4195))
53+
- Added table icons to the WorkBench data sets dialog based on base table in the upload plan ([#4475](https://github.com/specify/specify7/pull/4475))
54+
- Added frontend business rules for Address `isPrimary` so that it is set by default when adding a new address ([#4443](https://github.com/specify/specify7/pull/4443))
55+
56+
57+
### Changed
58+
- Differentiated list of tables for interactions and data entry ([#4198](https://github.com/specify/specify7/pull/4198))
59+
- Removed table name before app resource titles ([#4132](https://github.com/specify/specify7/pull/4132))
60+
- Numeric inputs no longer change on scroll ([#4249](https://github.com/specify/specify7/pull/4249))
61+
- Disabled gallery icon when there are no attachments on forms ([#4220](https://github.com/specify/specify7/pull/4220))
62+
- Search preview dialogs no longer display subviews ([#4254](https://github.com/specify/specify7/pull/4254))
63+
- Improved logic for selecting main tables fields when creating new records from query combo boxes ([#4293](https://github.com/specify/specify7/pull/4293))
64+
- Correctly detected main table fields in formatters ([#4516](https://github.com/specify/specify7/pull/4516))
65+
- Sidebar color preference has been moved and renamed ([#4355](https://github.com/specify/specify7/pull/4355))
66+
- Displayed disable icon add button instead of link when lowest tree rank ([#4351](https://github.com/specify/specify7/pull/4351))
67+
- "Use Localized Field Labels" checkbox is now a button ([#4344](https://github.com/specify/specify7/pull/4344))
68+
- Renamed record merging policy to `/record/merge` ([#4329](https://github.com/specify/specify7/pull/4329))
69+
- Switched to muted colors for taxon tiles ([#4476](https://github.com/specify/specify7/pull/4476)*Requested by Virginia Institute of Marine Science*)
70+
- Miscellaneous Reports dialog improvements ([#4396](https://github.com/specify/specify7/pull/4396))
71+
- Backend query exports now have user-friendly names ([#3590](https://github.com/specify/specify7/pull/3590))
72+
73+
### Fixed
74+
- Fixed an issue where the WorkBench did not always check for custom uniqueness rules ([#4593](https://github.com/specify/specify7/pull/4593)*Reported by Commonwealth Scientific and Industrial Research Organisation*)
75+
- Fixed bug causing incorrect disambiguation behavior in the WorkBench ([#4777](https://github.com/specify/specify7/pull/4777))
76+
- Made behavior consistent for Reports/Labels across forms and reports dialog, enhanced Report preferences. ([#4299](https://github.com/specify/specify7/pull/4299)*Reported by Florida Fish and Wildlife Research Institute and Commonwealth Scientific and Industrial Research Organisation*)
77+
- Fixed an issue that prevented new reports or labels from being created when there was a formatted table in the query ([#4427](https://github.com/specify/specify7/pull/4427)*Reported by The Hebrew University of Jerusalem, Florida Fish and Wildlife Research Institute, Gothenburg Museum of Natural History, and Oranim College of Education*)
78+
- Make taxonomic rank properly recompute when the parent changes ([#4462](https://github.com/specify/specify7/pull/4462)*Reported by Agriculture and Agri-Food Canada*)
79+
- Added a prompt for a user to define a record set name when creating one ([#4346](https://github.com/specify/specify7/pull/4346))
80+
- Improved home page load performance ([#4240](https://github.com/specify/specify7/pull/4240))
81+
- Makes 'Save' button style consistent in Schema Config and Mapper ([#3527](https://github.com/specify/specify7/pull/3527))
82+
- Make preferences visually read-only for users without proper permissions ([#3551](https://github.com/specify/specify7/pull/3551))
83+
- Fixed an issue preventing preparations from being added on the Loan form when displayed it was displayed in "form view" ([#3659](https://github.com/specify/specify7/pull/3659))
84+
- Fixed an issue where a cell or row could be selected multiple times in the WorkBench ([#3675](https://github.com/specify/specify7/pull/3675))
85+
- Fixed an issue where the WorkBench could not import certain spreadsheets ([#4223](https://github.com/specify/specify7/pull/4223)*Reported by Royal Botanic Garden Edinburgh*)
86+
- Refactored date picker and added tests ([#4276](https://github.com/specify/specify7/pull/4276))
87+
- Fixed spelling mistake in new record set text ([#4317](https://github.com/specify/specify7/pull/4317))
88+
- The cache is cleared when form edits are made, allowing you to see the latest version ([#4290](https://github.com/specify/specify7/pull/4290))
89+
- Fixed issues when parsing remote preferences ([#4251](https://github.com/specify/specify7/pull/4251)*Reported by Museu de Ciències Naturals de Barcelona*)
90+
- Fix inconsistency with hidden tables in schema and new query lists ([#4357](https://github.com/specify/specify7/pull/4357)*Reported by Commonwealth Scientific and Industrial Research Organisation*)
91+
- Made `usertype` get ignored when searching for Common directory app resources to ensure global app resources are applied appropriately ([#4332](https://github.com/specify/specify7/pull/4332))
92+
- Made minor accessibility improvements ([#4449](https://github.com/specify/specify7/pull/4449))
93+
- Scoped all front-end requests where needed to prevent resources from returning records from other collections ([#3304](https://github.com/specify/specify7/pull/3304))
94+
- Fixed WorkBench dialog heading so it displays "Timestamp Uploaded" instead of "Timestamp Modified" ([#4472](https://github.com/specify/specify7/pull/4472))
95+
1296
## [7.9.3.1](https://github.com/specify/specify7/compare/v7.9.3...v7.9.3.1) (29 January 2024)
1397

1498
This release fixes an issue that could cause an error when viewing an Accession or Repository Agreement form with interaction agents present.

specifyweb/frontend/js_src/css/workbench.css

+10
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,16 @@
7777
--accent-color: var(--search-result);
7878
}
7979

80+
/*
81+
* Override default font properties: required for handsontable 10 and above
82+
* See: https://github.com/handsontable/handsontable/pull/8681
83+
*/
84+
.handsontable {
85+
@apply text-inherit;
86+
font-family: inherit;
87+
font-size: inherit;
88+
}
89+
8090
/* Handsontable dark mode */
8191
.handsontable td,
8292
.htContextMenu table tbody tr td {

specifyweb/frontend/js_src/lib/components/Atoms/Form.tsx

+3-1
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ export const Input = {
244244
({ onValueChange, isReadOnly, ...props }) =>
245245
process.env.NODE_ENV === 'development' &&
246246
typeof props.step === 'number' &&
247-
props.step < 0
247+
props.step !== Math.floor(props.step)
248248
? error('If step <1 is needed, use Input.Float instead')
249249
: {
250250
...props,
@@ -267,6 +267,7 @@ export const Input = {
267267
readonly readOnly?: never;
268268
readonly isReadOnly?: boolean;
269269
readonly children?: undefined;
270+
readonly step?: number | 'any';
270271
}
271272
>(
272273
'Input.Float',
@@ -282,6 +283,7 @@ export const Input = {
282283
);
283284
props.onChange?.(event);
284285
},
286+
step: props.step ?? 'any',
285287
...withPreventWheel(props.onWheel),
286288
readOnly: isReadOnly,
287289
})

specifyweb/frontend/js_src/lib/components/DataModel/__tests__/businessRules.test.ts

+92
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { renderHook } from '@testing-library/react';
33
import { overrideAjax } from '../../../tests/ajax';
44
import { mockTime, requireContext } from '../../../tests/helpers';
55
import { overwriteReadOnly } from '../../../utils/types';
6+
import { getPref } from '../../InitialContext/remotePrefs';
67
import type { SerializedResource } from '../helperTypes';
78
import { getResourceApiUrl } from '../resource';
89
import { useSaveBlockers } from '../saveBlockers';
@@ -197,6 +198,18 @@ describe('treeBusinessRules', () => {
197198
_tableName: 'Taxon',
198199
id: 3,
199200
name: 'Acipenser',
201+
isAccepted: true,
202+
rankId: 180,
203+
definition: '/api/specify/taxontreedef/1/',
204+
definitionItem: '/api/specify/taxontreedefitem/9/',
205+
parent: '/api/specify/taxon/2/',
206+
};
207+
208+
const husoResponse: Partial<SerializedResource<Taxon>> = {
209+
_tableName: 'Taxon',
210+
id: 6,
211+
name: 'Huso',
212+
isAccepted: false,
200213
rankId: 180,
201214
definition: '/api/specify/taxontreedef/1/',
202215
definitionItem: '/api/specify/taxontreedefitem/9/',
@@ -207,6 +220,7 @@ describe('treeBusinessRules', () => {
207220
_tableName: 'Taxon',
208221
id: 4,
209222
name: 'oxyrinchus',
223+
isAccepted: true,
210224
rankId: 220,
211225
definition: '/api/specify/taxontreedef/1/',
212226
definitionItem: '/api/specify/taxontreedefitem/2/',
@@ -218,6 +232,7 @@ describe('treeBusinessRules', () => {
218232
id: 5,
219233
rankId: 230,
220234
name: 'oxyrinchus',
235+
isAccepted: true,
221236
definition: '/api/specify/taxontreedef/1/',
222237
definitionItem: '/api/specify/taxontreedefitem/22/',
223238
};
@@ -250,18 +265,47 @@ describe('treeBusinessRules', () => {
250265
resource_uri: '/api/specify/taxontreedefitem/2/',
251266
};
252267

268+
const subSpeciesResponse: Partial<SerializedResource<TaxonTreeDefItem>> = {
269+
id: 22,
270+
fullNameSeparator: ' ',
271+
isEnforced: false,
272+
isInFullName: true,
273+
name: 'Subspecies',
274+
rankId: 230,
275+
title: null,
276+
version: 0,
277+
parent: '/api/specify/taxontreedefitem/2/',
278+
treeDef: '/api/specify/taxontreedef/1/',
279+
resource_uri: '/api/specify/taxontreedefitem/22/',
280+
};
281+
253282
const oxyrinchusFullNameResponse = 'Acipenser oxyrinchus';
283+
const dauricusFullNameResponse = 'Huso dauricus';
254284

255285
overrideAjax('/api/specify/taxon/2/', animaliaResponse);
256286
overrideAjax('/api/specify/taxon/3/', acipenserResponse);
287+
overrideAjax('/api/specify/taxon/6/', husoResponse);
257288
overrideAjax('/api/specify/taxon/4/', oxyrinchusSpeciesResponse);
258289
overrideAjax('/api/specify/taxon/5/', oxyrinchusSubSpeciesResponse);
259290
overrideAjax('/api/specify/taxontreedefitem/9/', genusResponse);
260291
overrideAjax('/api/specify/taxontreedefitem/2/', speciesResponse);
292+
overrideAjax('/api/specify/taxontreedefitem/22/', subSpeciesResponse);
261293
overrideAjax(
262294
'/api/specify_tree/taxon/3/predict_fullname/?name=oxyrinchus&treedefitemid=2',
263295
oxyrinchusFullNameResponse
264296
);
297+
overrideAjax(
298+
'/api/specify_tree/taxon/6/predict_fullname/?name=dauricus&treedefitemid=2',
299+
dauricusFullNameResponse
300+
);
301+
overrideAjax('/api/specify/taxon/?limit=1&parent=4&orderby=rankid', {
302+
objects: [oxyrinchusSubSpeciesResponse],
303+
meta: {
304+
limit: 1,
305+
offset: 0,
306+
total_count: 1,
307+
},
308+
});
265309

266310
test('fullName being set', async () => {
267311
const oxyrinchus = new tables.Taxon.Resource({
@@ -306,4 +350,52 @@ describe('treeBusinessRules', () => {
306350
);
307351
expect(result.current[0]).toStrictEqual(['Bad tree structure.']);
308352
});
353+
test('saveBlocker on synonymized parent', async () => {
354+
const taxon = new tables.Taxon.Resource({
355+
name: 'dauricus',
356+
parent: '/api/specify/taxon/6/',
357+
rankId: 220,
358+
definition: '/api/specify/taxontreedef/1/',
359+
definitionItem: '/api/specify/taxontreedefitem/2/',
360+
});
361+
362+
await taxon.businessRuleManager?.checkField('parent');
363+
364+
const { result } = renderHook(() =>
365+
useSaveBlockers(taxon, tables.Taxon.getField('parent'))
366+
);
367+
expect(result.current[0]).toStrictEqual(['Bad tree structure.']);
368+
369+
await taxon.businessRuleManager?.checkField('integer1');
370+
371+
const { result: fieldChangeResult } = renderHook(() =>
372+
useSaveBlockers(taxon, tables.Taxon.getField('parent'))
373+
);
374+
expect(fieldChangeResult.current[0]).toStrictEqual(['Bad tree structure.']);
375+
});
376+
test('saveBlocker not on synonymized parent w/preference', async () => {
377+
const remotePrefs = await import('../../InitialContext/remotePrefs');
378+
jest
379+
.spyOn(remotePrefs, 'getPref')
380+
.mockImplementation((key) =>
381+
key === 'sp7.allow_adding_child_to_synonymized_parent.Taxon'
382+
? true
383+
: getPref(key)
384+
);
385+
386+
const taxon = new tables.Taxon.Resource({
387+
name: 'dauricus',
388+
parent: '/api/specify/taxon/6/',
389+
rankId: 220,
390+
definition: '/api/specify/taxontreedef/1/',
391+
definitionItem: '/api/specify/taxontreedefitem/2/',
392+
});
393+
394+
await taxon.businessRuleManager?.checkField('parent');
395+
396+
const { result } = renderHook(() =>
397+
useSaveBlockers(taxon, tables.Taxon.getField('parent'))
398+
);
399+
expect(result.current[0]).toStrictEqual([]);
400+
});
309401
});

specifyweb/frontend/js_src/lib/components/DataModel/businessRules.ts

+7-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,12 @@ import { isTreeResource } from '../InitialContext/treeRanks';
88
import type { BusinessRuleDefs } from './businessRuleDefs';
99
import { businessRuleDefs } from './businessRuleDefs';
1010
import { backboneFieldSeparator, djangoLookupSeparator } from './helpers';
11-
import type { AnySchema, AnyTree, CommonFields } from './helperTypes';
11+
import type {
12+
AnySchema,
13+
AnyTree,
14+
CommonFields,
15+
TableFields,
16+
} from './helperTypes';
1217
import type { SpecifyResource } from './legacyTypes';
1318
import {
1419
getFieldBlockerKey,
@@ -77,7 +82,7 @@ export class BusinessRuleManager<SCHEMA extends AnySchema> {
7782
isTreeResource(this.resource as SpecifyResource<AnySchema>)
7883
? treeBusinessRules(
7984
this.resource as SpecifyResource<AnyTree>,
80-
processedFieldName
85+
processedFieldName as TableFields<AnyTree>
8186
)
8287
: Promise.resolve({ isValid: true }),
8388
];

specifyweb/frontend/js_src/lib/components/DataModel/treeBusinessRules.ts

+29-4
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,19 @@
11
import { treeText } from '../../localization/tree';
22
import { ajax } from '../../utils/ajax';
33
import { f } from '../../utils/functools';
4+
import { getPref } from '../InitialContext/remotePrefs';
5+
import { fetchPossibleRanks } from '../PickLists/TreeLevelPickList';
46
import { formatUrl } from '../Router/queryString';
57
import type { BusinessRuleResult } from './businessRules';
6-
import type { AnyTree, FilterTablesByEndsWith } from './helperTypes';
8+
import type {
9+
AnyTree,
10+
FilterTablesByEndsWith,
11+
TableFields,
12+
} from './helperTypes';
713
import type { SpecifyResource } from './legacyTypes';
814

915
// eslint-disable-next-line unicorn/prevent-abbreviations
10-
type TreeDefItem<TREE extends AnyTree> =
16+
export type TreeDefItem<TREE extends AnyTree> =
1117
FilterTablesByEndsWith<`${TREE['tableName']}TreeDefItem`>;
1218

1319
export const initializeTreeRecord = (
@@ -19,15 +25,33 @@ export const initializeTreeRecord = (
1925

2026
export const treeBusinessRules = async (
2127
resource: SpecifyResource<AnyTree>,
22-
fieldName: string
28+
fieldName: TableFields<AnyTree>
2329
): Promise<BusinessRuleResult | undefined> =>
2430
getRelatedTreeTables(resource).then(async ({ parent, definitionItem }) => {
2531
if (parent === undefined) return undefined;
2632

33+
const parentDefItem = ((await parent.rgetPromise('definitionItem')) ??
34+
undefined) as SpecifyResource<TreeDefItem<AnyTree>> | undefined;
35+
36+
const possibleRanks =
37+
parentDefItem === undefined
38+
? undefined
39+
: await fetchPossibleRanks(resource, parentDefItem.get('rankId'));
40+
41+
const doExpandSynonymActionsPref = getPref(
42+
`sp7.allow_adding_child_to_synonymized_parent.${resource.specifyTable.name}`
43+
);
44+
const isParentSynonym = !parent.get('isAccepted');
45+
2746
const hasBadTreeStrcuture =
2847
parent.id === resource.id ||
2948
definitionItem === undefined ||
30-
parent.get('rankId') >= definitionItem.get('rankId');
49+
(isParentSynonym && !doExpandSynonymActionsPref) ||
50+
parent.get('rankId') >= definitionItem.get('rankId') ||
51+
(possibleRanks !== undefined &&
52+
!possibleRanks
53+
.map(({ resource_uri }) => resource_uri)
54+
.includes(definitionItem.get('resource_uri')));
3155

3256
if (!hasBadTreeStrcuture && (resource.get('name').length ?? 0) === 0)
3357
return {
@@ -43,6 +67,7 @@ export const treeBusinessRules = async (
4367
};
4468

4569
if (
70+
hasBadTreeStrcuture ||
4671
(resource.get('name')?.length ?? 0) === 0 ||
4772
definitionItem === undefined
4873
)

0 commit comments

Comments
 (0)