Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
95 commits
Select commit Hold shift + click to select a range
9bb41b8
Create a configuration page
CarolineDenis Aug 14, 2025
d3cf165
Start to adapt backend def for system config
CarolineDenis Aug 15, 2025
2012e4a
Fix new discipline def
CarolineDenis Aug 15, 2025
b0fbf0e
Reload page after addition
CarolineDenis Aug 15, 2025
a502f6b
Remove parent id
CarolineDenis Aug 15, 2025
22ca396
Fetch data system after creation
CarolineDenis Aug 15, 2025
4a6da84
Simplify and comment backend defs
CarolineDenis Aug 15, 2025
4396b60
Handle second call on backedn creation def
CarolineDenis Aug 15, 2025
4bace18
efetch all system data after addition
CarolineDenis Aug 15, 2025
d29b9fa
Simplify handle save
CarolineDenis Aug 15, 2025
6f46015
Remove comment from type
CarolineDenis Aug 15, 2025
66bc315
Lint code with ESLint and Prettier
CarolineDenis Aug 15, 2025
f664812
Merge remote-tracking branch 'origin/issue-2931-1' into issue-6213-1
CarolineDenis Aug 18, 2025
44e2dcd
Merge remote-tracking branch 'origin/issue-2931-1' into issue-6213-1
CarolineDenis Aug 19, 2025
710e529
Add webonly view for discipline and div
CarolineDenis Aug 19, 2025
9d031f6
Merge remote-tracking branch 'origin/issue-2931-1' into issue-6213-1
CarolineDenis Sep 2, 2025
b1d29df
Merge branch 'issue-2931-1' into issue-6213-1
acwhite211 Oct 27, 2025
d5cfc5d
Merge remote-tracking branch 'origin/issue-2931-1' into issue-6213-1
CarolineDenis Dec 9, 2025
7dd465c
Feat: Add data check to define if geo is global & if tree def is present
CarolineDenis Dec 9, 2025
4125f19
Feat: Add geo tree config dialog
CarolineDenis Dec 9, 2025
d7f5fdb
Fix: add handleSave
CarolineDenis Dec 9, 2025
7d4f302
Feat: Add taxon and geo config dialog for disciplines
CarolineDenis Dec 10, 2025
112c14e
Fix: fix typos
CarolineDenis Dec 10, 2025
e617fc2
Feat: add collapsible arrows
CarolineDenis Dec 10, 2025
cd030a1
Feat: Add an edit option for each resource
CarolineDenis Dec 10, 2025
54d6186
Refactor: Break the system config tool in smaller component
CarolineDenis Dec 10, 2025
c955403
Refactor: Exctract resource renderer
CarolineDenis Dec 10, 2025
304e647
type
CarolineDenis Dec 10, 2025
23292c9
Import
CarolineDenis Dec 10, 2025
e95d61a
Refactor: Extract add button
CarolineDenis Dec 10, 2025
7755ec2
Fix type
CarolineDenis Dec 10, 2025
d50ed39
Lint code with ESLint and Prettier
CarolineDenis Dec 10, 2025
f10dcb7
Comment
CarolineDenis Dec 10, 2025
11768e9
Fix: Update all info for hierarchy onDelete and onSave
CarolineDenis Dec 12, 2025
3c69936
Feat: Allow to delete empty resources
CarolineDenis Dec 12, 2025
9aa67fa
Fix: typo in api
CarolineDenis Dec 12, 2025
17d9759
Fix: Typo
CarolineDenis Dec 12, 2025
786a204
Lint code with ESLint and Prettier
CarolineDenis Dec 12, 2025
0e8cccd
Comment: Suggestion for userscopeid crash
CarolineDenis Dec 16, 2025
9061ca6
Merge branch 'issue-2931-1' into issue-6213-1
alesan99 Dec 17, 2025
2113fa7
Lint code with ESLint and Prettier
alesan99 Dec 17, 2025
e11707a
Merge branch 'issue-2931-1' into issue-6213-1
alesan99 Dec 17, 2025
904ce4a
Merge remote-tracking branch 'origin/issue-2931-1' into issue-6213-1
CarolineDenis Dec 23, 2025
2a162ee
Merge branch 'issue-2931-1' into issue-6213-1
alesan99 Dec 30, 2025
fff6d38
Fix: Use new API to avoid UserGroupScopeId issues
alesan99 Dec 30, 2025
2a3e3e5
Fix: tests
alesan99 Dec 30, 2025
8b33e77
Fix: Improve spacing
CarolineDenis Jan 2, 2026
a6e3ad7
Refactor: Remove unnecessary dialog
CarolineDenis Jan 2, 2026
9437765
Feat: New icon for system config menu item
CarolineDenis Jan 2, 2026
4b1b424
Fix: improve collections margins
CarolineDenis Jan 2, 2026
7794c7c
Refactor: Remove imports
CarolineDenis Jan 2, 2026
14324f6
Lint code with ESLint and Prettier
CarolineDenis Jan 2, 2026
d48352f
fix: change chevron direction
grantfitzsimmons Jan 2, 2026
06fd8a9
Lint code with ESLint and Prettier
grantfitzsimmons Jan 2, 2026
4cfc398
[UI]: Add a backgrounf behind the hierarchy
CarolineDenis Jan 6, 2026
271f472
Feat: Add tool list under ea ch resource
CarolineDenis Jan 6, 2026
bed4a8a
Fix: Chnage edit tool to a link
CarolineDenis Jan 6, 2026
3777984
Fix: Add migrations for delete.CASCADE chnages
CarolineDenis Jan 6, 2026
222bb0c
[UI]: Add height to container
CarolineDenis Jan 6, 2026
f94e933
[Fix]: Remove import and unused strings
CarolineDenis Jan 6, 2026
264d76c
Lint code with ESLint and Prettier
CarolineDenis Jan 6, 2026
3e76e8f
fix: add hierarchy
grantfitzsimmons Jan 6, 2026
21024aa
fix: sort out spacing and positioning
grantfitzsimmons Jan 7, 2026
c228fcb
fix(tests): remove unused import
grantfitzsimmons Jan 7, 2026
8c8da8f
fix(localization): remove unused text
grantfitzsimmons Jan 7, 2026
44192be
fix: form when selecting a block
grantfitzsimmons Jan 7, 2026
10c78f1
fix: failing test
grantfitzsimmons Jan 7, 2026
e7d943c
fix(colors): make custom colors important
grantfitzsimmons Jan 7, 2026
36a38cd
Lint code with ESLint and Prettier
grantfitzsimmons Jan 7, 2026
35e2c7f
fix: use correct forms
grantfitzsimmons Jan 7, 2026
7360933
Merge branch 'visual-hierarchy' of https://github.com/specify/specify…
grantfitzsimmons Jan 7, 2026
6ebf432
Lint code with ESLint and Prettier
grantfitzsimmons Jan 7, 2026
33da875
Merge remote-tracking branch 'origin/issue-6213-1' into visual-hierarchy
CarolineDenis Jan 7, 2026
117c57c
Merge pull request #7618 from specify/visual-hierarchy
CarolineDenis Jan 7, 2026
d247094
Lint code with ESLint and Prettier
CarolineDenis Jan 7, 2026
f1dd1d2
[Fix]: Add deletion cascade to preptype and cotype
CarolineDenis Jan 7, 2026
c450d94
[Fix]: Add casdace to COGtyp
CarolineDenis Jan 7, 2026
ee0b25c
[Fix]: Clear cache when deleting resources
CarolineDenis Jan 7, 2026
c0027d8
fix: handle long names for hierarchy nodes
grantfitzsimmons Jan 7, 2026
7550b74
fix: key uniqueness and refresh process
grantfitzsimmons Jan 7, 2026
0bda697
Lint code with ESLint and Prettier
grantfitzsimmons Jan 7, 2026
870c27a
fix: reduce network requests
grantfitzsimmons Jan 7, 2026
1911781
Merge branch 'issue-6213-1' of https://github.com/specify/specify7 in…
grantfitzsimmons Jan 7, 2026
078f42d
Merge branch 'issue-2931-1' into issue-6213-1
alesan99 Jan 7, 2026
80bdb3a
fix: update imports
alesan99 Jan 7, 2026
f5cb5ce
Merge branch 'issue-2931-1' into issue-6213-1
alesan99 Jan 7, 2026
b2a5db6
Lint code with ESLint and Prettier
alesan99 Jan 7, 2026
21a847d
fix(ux): make the institutional hierarchy fit the view
grantfitzsimmons Jan 7, 2026
f9223a6
Lint code with ESLint and Prettier
grantfitzsimmons Jan 7, 2026
004d9ad
fix: change colors...
grantfitzsimmons Jan 7, 2026
73f83a5
Merge branch 'issue-6213-1' of https://github.com/specify/specify7 in…
grantfitzsimmons Jan 7, 2026
238af20
fix: missing colors
grantfitzsimmons Jan 7, 2026
6442863
Lint code with ESLint and Prettier
grantfitzsimmons Jan 7, 2026
a1e073b
Merge branch 'issue-2931-1' into issue-6213-1
alesan99 Jan 7, 2026
171b14e
Lint code with ESLint and Prettier
alesan99 Jan 7, 2026
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Generated by Django 4.2.24 on 2026-01-06 14:32

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
('specify', '0042_alter_deletion_cascade'),
('businessrules', '0008_fix_global_default_rules'),
]

operations = [
migrations.AlterField(
model_name='uniquenessrule',
name='discipline',
field=models.ForeignKey(blank=True, db_column='DisciplineID', null=True, on_delete=django.db.models.deletion.CASCADE, to='specify.discipline'),
),
]
2 changes: 1 addition & 1 deletion specifyweb/backend/businessrules/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ class UniquenessRule(models.Model):
isDatabaseConstraint = models.BooleanField(default=False, db_column='isDatabaseConstraint')
modelName = models.CharField(max_length=256)
discipline = models.ForeignKey(
Discipline, null=True, blank=True, on_delete=models.PROTECT, db_column="DisciplineID")
Discipline, null=True, blank=True, on_delete=models.CASCADE, db_column="DisciplineID")

@property
def fields(self):
Expand Down
1 change: 1 addition & 0 deletions specifyweb/backend/context/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
re_path(r'^user.json$', views.user),
re_path(r'^stats_counts.json$', views.stats_counts),
re_path(r'^system_info.json$', views.system_info),
re_path(r'^all_system_data.json$', views.all_system_data),
re_path(r'^server_time.json$', views.get_server_time),
re_path(r'^domain.json$', views.domain),
re_path(r'^view.json$', views.view),
Expand Down
54 changes: 52 additions & 2 deletions specifyweb/backend/context/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
PermissionTargetAction, \
check_permission_targets, skip_collection_access_check, query_pt, \
CollectionAccessPT
from specifyweb.specify.models import Collection, Collectionobject, Institution, \
from specifyweb.specify.models import Collection, Discipline, Division, Collectionobject, Institution, \
Specifyuser, Spprincipal, Spversion, Collectionobjecttype
from specifyweb.specify.models_utils.schema import base_schema
from specifyweb.specify.models_utils.serialize_datamodel import datamodel_to_json
Expand Down Expand Up @@ -663,10 +663,60 @@ def system_info(request):
collection=collection and collection.collectionname,
collection_guid=collection and collection.guid,
isa_number=collection and collection.isanumber,
discipline_type=discipline and discipline.type
discipline_type=discipline and discipline.type,
geography_is_global=institution.issinglegeographytree
)
return HttpResponse(json.dumps(info), content_type='application/json')

@require_http_methods(["GET"])
@cache_control(max_age=86400, public=True)
@skip_collection_access_check
def all_system_data(request):
"""
Returns all institutions, divisions, disciplines, and collections.
"""
institution = Institution.objects.get()
divisions = list(Division.objects.all())
disciplines = list(Discipline.objects.all())
collections = list(Collection.objects.all())

discipline_map = {}
for discipline in disciplines:
discipline_map[discipline.id] = {
"id": discipline.id,
"name": discipline.name,
"children": [],
"geographytreedef": discipline.geographytreedef_id,
"taxontreedef": discipline.taxontreedef_id
}

for collection in collections:
if collection.discipline_id in discipline_map:
discipline_map[collection.discipline_id]["children"].append({
"id": collection.id,
"name": collection.collectionname
})

division_map = {}
for division in divisions:
division_map[division.id] = {
"id": division.id,
"name": division.name,
"children": []
}

for discipline in disciplines:
if discipline.division_id in division_map:
division_map[discipline.division_id]["children"].append(discipline_map[discipline.id])

institution_data = {
"id": institution.id,
"name": institution.name,
"children": list(division_map.values())
}

return JsonResponse(institution_data, safe=False)

PATH_GROUP_RE = re.compile(r'\(\?P<([^>]+)>[^\)]*\)')
PATH_GROUP_RE_EXTENDED = re.compile(r'<([^:]+):([^>]+)>')

Expand Down
1 change: 1 addition & 0 deletions specifyweb/frontend/js_src/lib/components/Atoms/Icons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ export const icons = {
identification: <svg aria-hidden className={iconClassName} fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path clipRule="evenodd" d="M10 2a1 1 0 00-1 1v1a1 1 0 002 0V3a1 1 0 00-1-1zM4 4h3a3 3 0 006 0h3a2 2 0 012 2v9a2 2 0 01-2 2H4a2 2 0 01-2-2V6a2 2 0 012-2zm2.5 7a1.5 1.5 0 100-3 1.5 1.5 0 000 3zm2.45 4a2.5 2.5 0 10-4.9 0h4.9zM12 9a1 1 0 100 2h3a1 1 0 100-2h-3zm-1 4a1 1 0 011-1h2a1 1 0 110 2h-2a1 1 0 01-1-1z" fillRule="evenodd" /></svg>,
informationCircle: <svg aria-hidden className={iconClassName} fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path clipRule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" fillRule="evenodd" /></svg>,
key: <svg aria-hidden className={iconClassName} fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path clipRule="evenodd" d="M18 8a6 6 0 01-7.743 5.743L10 14l-1 1-1 1H6v2H2v-4l4.257-4.257A6 6 0 1118 8zm-6-4a1 1 0 100 2 2 2 0 012 2 1 1 0 102 0 4 4 0 00-4-4z" fillRule="evenodd" /></svg>,
library: <svg aria-hidden className={iconClassName} fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path clipRule="evenodd" d="M10.496 2.132a1 1 0 00-.992 0l-7 4A1 1 0 003 8v7a1 1 0 100 2h14a1 1 0 100-2V8a1 1 0 00.496-1.868l-7-4zM6 9a1 1 0 00-1 1v3a1 1 0 102 0v-3a1 1 0 00-1-1zm3 1a1 1 0 012 0v3a1 1 0 11-2 0v-3zm5-1a1 1 0 00-1 1v3a1 1 0 102 0v-3a1 1 0 00-1-1z" fillRule="evenodd" /></svg>,
locationMarker: <svg aria-hidden className={iconClassName} fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path clipRule="evenodd" d="M5.05 4.05a7 7 0 119.9 9.9L10 18.9l-4.95-4.95a7 7 0 010-9.9zM10 11a2 2 0 100-4 2 2 0 000 4z" fillRule="evenodd" /></svg>,
login: <svg aria-hidden className={iconClassName} fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path clipRule="evenodd" d="M3 3a1 1 0 011 1v12a1 1 0 11-2 0V4a1 1 0 011-1zm7.707 3.293a1 1 0 010 1.414L9.414 9H17a1 1 0 110 2H9.414l1.293 1.293a1 1 0 01-1.414 1.414l-3-3a1 1 0 010-1.414l3-3a1 1 0 011.414 0z" fillRule="evenodd" /></svg>,
logout: <svg aria-hidden className={iconClassName} fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path clipRule="evenodd" d="M3 3a1 1 0 011 1v12a1 1 0 11-2 0V4a1 1 0 011-1zm7.707 3.293a1 1 0 010 1.414L9.414 9H17a1 1 0 110 2H9.414l1.293 1.293a1 1 0 01-1.414 1.414l-3-3a1 1 0 010-1.414l3-3a1 1 0 011.414 0z" fillRule="evenodd" /></svg>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,22 @@ export const webOnlyViews = f.store(() =>
'edit',
['name']
),
[collection]: autoGenerateViewDefinition(
tables.Collection,
'form',
'edit',
['collectionName', 'code', 'catalogNumFormatName']
),
[division]: autoGenerateViewDefinition(tables.Division, 'form', 'edit', [
'name',
'abbrev',
]),
[discipline]: autoGenerateViewDefinition(
tables.Discipline,
'form',
'edit',
['name', 'type']
),
} as const)
);

Expand All @@ -92,3 +108,6 @@ export const attachmentView = 'ObjectAttachment';
export const spAppResourceView = '_SpAppResourceView_name';
export const spViewSetNameView = '_SpViewSetObj_name';
export const recordSetView = '_RecordSet_name';
export const collection = '_Collection_setup';
export const division = '_Division_setup';
export const discipline = '_Discipline_setup';
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,11 @@ const rawUserTools = ensure<IR<IR<Omit<MenuItem, 'name'>>>>()({
url: '/specify/security/',
icon: icons.fingerPrint,
},
systemConfigurationTool: {
title: userText.systemConfig(),
url: '/specify/system-configuration/',
icon: icons.library,
},
repairTree: {
title: headerText.repairTree(),
url: '/specify/overlay/tree-repair/',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ type SystemInfo = {
readonly stats_url: string | null;
readonly stats_2_url: string | null;
readonly discipline_type: string;
readonly geography_is_global: string;
};

type StatsCounts = {
Expand Down Expand Up @@ -80,6 +81,7 @@ export const fetchContext = load<SystemInfo>(
collectionObjectCount: counts?.Collectionobject ?? 0,
collectionCount: counts?.Collection ?? 0,
userCount: counts?.Specifyuser ?? 0,
geographyIsGlobal: systemInfo.geography_is_global,
};

await ping(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ const altKeyName =
/**
* Have to be careful as preferences may be used before schema is loaded
*/
const tableLabel = (tableName: keyof Tables): LocalizedString =>
export const tableLabel = (tableName: keyof Tables): LocalizedString =>
genericTables[tableName]?.label ?? camelToHuman(tableName);

export const userPreferenceDefinitions = {
Expand Down
8 changes: 8 additions & 0 deletions specifyweb/frontend/js_src/lib/components/Router/Routes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,14 @@ export const routes: RA<EnhancedRoute> = [
},
],
},
{
path: 'system-configuration',
title: userText.securityPanel(),
element: () =>
import('../SystemConfigurationTool/SystemConfigTool').then(
({ SystemConfigurationTool }) => SystemConfigurationTool
),
},
{
path: 'attachments',
children: [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,13 @@ export const resources: RA<ResourceConfig> = [
type: 'object',
// TODO: Rank fields should be generated from a .json file.
fields: [
{ name: '0', label: 'Site', type: 'boolean', default: true, required: true },
{
name: '0',
label: 'Site',
type: 'boolean',
default: true,
required: true,
},
{ name: '100', label: 'Building', type: 'boolean' },
{ name: '150', label: 'Collection', type: 'boolean' },
{ name: '200', label: 'Room', type: 'boolean' },
Expand Down Expand Up @@ -235,7 +241,13 @@ export const resources: RA<ResourceConfig> = [
required: false,
type: 'object',
fields: [
{ name: '0', label: 'Earth', type: 'boolean', default: true, required: true },
{
name: '0',
label: 'Earth',
type: 'boolean',
default: true,
required: true,
},
{ name: '100', label: 'Continent', type: 'boolean', default: true },
{ name: '200', label: 'Country', type: 'boolean', default: true },
{ name: '300', label: 'State', type: 'boolean', default: true },
Expand Down Expand Up @@ -270,7 +282,13 @@ export const resources: RA<ResourceConfig> = [
required: false,
type: 'object',
fields: [
{ name: '0', label: 'Life', type: 'boolean', default: true, required: true },
{
name: '0',
label: 'Life',
type: 'boolean',
default: true,
required: true,
},
{ name: '10', label: 'Kingdom', type: 'boolean', default: true },
{ name: '30', label: 'Phylum', type: 'boolean', default: true },
{ name: '40', label: 'Subphylum', type: 'boolean', default: true },
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import React from 'react';

import { useBooleanState } from '../../hooks/useBooleanState';
import { Button } from '../Atoms/Button';

export function CollapsibleSection({
title,
children,
defaultOpen = true,
hasChildren,
}: {
readonly title: React.ReactNode;
readonly children: React.ReactNode;
readonly defaultOpen?: boolean;
readonly hasChildren: boolean;
}) {
const [isOpen, _, __, handleOpen] = useBooleanState(defaultOpen);

return (
<div className="mb-2">
<div className="flex items-start">
<Button.Icon
className={`ml-2 ${hasChildren ? '' : 'invisible'}`}
icon={isOpen ? 'chevronDown' : 'chevronRight'}
title="collapse"
onClick={handleOpen}
/>
{title}
</div>

{isOpen && <div className="ml-10">{children}</div>}
</div>
);
}
Loading