Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
113 commits
Select commit Hold shift + click to select a range
e61e9a6
Fix:Expose general collection prefs for picklist scoping and attachme…
Gitesh307 Oct 1, 2025
87c5e32
implement Collection Preferences UI with dedicated definitions
Gitesh307 Oct 2, 2025
f8c579a
adding missing files for the Collection preferences
Gitesh307 Oct 2, 2025
ff7788d
resolve typecheck errors in Collection Preferences definitions
Gitesh307 Oct 3, 2025
44b1da0
add Collection Preferences UI and editor with doc links
Gitesh307 Oct 4, 2025
e70f796
implify CollectionPreferences page by removing internal save
Gitesh307 Oct 4, 2025
ce59d26
Corrected specifyNetwork keys to publishingOrg and datasetKey
Gitesh307 Oct 4, 2025
70ea534
fix TypeScript errors in collection preference definitions
Gitesh307 Oct 4, 2025
751fcc5
align CollectionDefinitions with UserDefinitions typing
Gitesh307 Oct 4, 2025
20b207e
Fix localization scanner issues in preferences
Gitesh307 Oct 4, 2025
5e1ab01
Lint code with ESLint and Prettier
Gitesh307 Oct 4, 2025
91644ab
adding path for role gated access
Gitesh307 Oct 5, 2025
454e758
Merge branch 'issue-7440' of https://github.com/specify/specify7 into…
Gitesh307 Oct 6, 2025
78c6e54
Refactor collection preferences renderer and documentation link layout
Gitesh307 Oct 6, 2025
f6c0493
Lint code with ESLint and Prettier
Gitesh307 Oct 6, 2025
c4ddea8
Localization cleanup for collection preferences
Gitesh307 Oct 6, 2025
5a61689
Lint code with ESLint and Prettier
Gitesh307 Oct 6, 2025
9b48971
removed collection pref refrence from User Tools
Gitesh307 Oct 6, 2025
d662587
remove extra language lines and keep only 'en-us'
Gitesh307 Oct 7, 2025
ad70ddc
extract shared logic into createPreferencesEditor and wire User/Colle…
Gitesh307 Oct 7, 2025
8d33e36
Lint code with ESLint and Prettier
Gitesh307 Oct 7, 2025
3de5eca
implemented IR<T> for preferences typing
Gitesh307 Oct 7, 2025
7f6782d
Merge branch 'issue-7440' of https://github.com/specify/specify7 into…
Gitesh307 Oct 7, 2025
e58d9f0
resolve BasePreferences context type incompatibility in index.tsx
Gitesh307 Oct 7, 2025
40479b2
Lint code with ESLint and Prettier
Gitesh307 Oct 7, 2025
58e01a6
simplify resolveCollectionDocumentHref logic using unified NAME_DOCS_MAP
Gitesh307 Oct 7, 2025
ff875da
Merge branch 'issue-7440' of https://github.com/specify/specify7 into…
Gitesh307 Oct 7, 2025
613d31c
Lint code with ESLint and Prettier
Gitesh307 Oct 7, 2025
81ca7e4
unify visibilityContext to remove duplication
Gitesh307 Oct 7, 2025
b879af0
Merge branch 'issue-7440' of https://github.com/specify/specify7 into…
Gitesh307 Oct 7, 2025
728bfed
optimize preferences wrappers by introducing FetchGate component.
Gitesh307 Oct 7, 2025
3b58630
simplify className logic for documentation paragraph
Gitesh307 Oct 7, 2025
e24a4fb
Localization cleanup for collection preferences
Gitesh307 Oct 8, 2025
444d3bf
removed `as unknown` casts
Gitesh307 Oct 8, 2025
ee4eb3b
consolidate doc links and merge catalog-number card
Gitesh307 Oct 10, 2025
41c8539
removed unused imports
Gitesh307 Oct 10, 2025
c69f079
function call
Gitesh307 Oct 10, 2025
3d1b40c
Lint code with ESLint and Prettier
Gitesh307 Oct 10, 2025
f91ea9a
render editor inline for User tools menu
Gitesh307 Oct 11, 2025
04c1788
Merge branch 'issue-7440' of https://github.com/specify/specify7 into…
Gitesh307 Oct 11, 2025
17b28db
Lint code with ESLint and Prettier
Gitesh307 Oct 11, 2025
f1eddb7
Feat: Refactor and add UI to control Global preferences
Gitesh307 Oct 12, 2025
549792e
Editor Configuration for Global Pref
Gitesh307 Oct 12, 2025
2b198a5
Global pref app resources integration
Gitesh307 Oct 12, 2025
24e6638
localization strings
Gitesh307 Oct 12, 2025
684b974
Global Preferences View, Routing and User tools menu logic
Gitesh307 Oct 12, 2025
10073ac
Add remotePreferences test coverage and stabilize ajax mocks
Gitesh307 Oct 12, 2025
e24fc51
ix test-mode ajax mock import without bundling node modules
Gitesh307 Oct 12, 2025
be2f364
removed extra localization text
Gitesh307 Oct 12, 2025
307590f
removed redundant localization strings
Gitesh307 Oct 12, 2025
bb4ec4e
added the import statement
Gitesh307 Oct 12, 2025
c6918af
Enable Global Preferences visual editor for legacy resources
Gitesh307 Oct 13, 2025
a75145c
Fix Global Preferences tests and harden ajax mock
Gitesh307 Oct 13, 2025
94e4c90
Stabilize AppResources tests for Global Preferences rename
Gitesh307 Oct 13, 2025
fccbefe
declare explicit return type for flattenResources
Gitesh307 Oct 13, 2025
4852be1
update AppResourcesTab and useResourcesTree test cases for Global Pre…
Gitesh307 Oct 13, 2025
a0f9489
remove unused container variable in AppResourcesTab.test.tsx
Gitesh307 Oct 13, 2025
3f7f9c3
Lint code with ESLint and Prettier
Gitesh307 Oct 13, 2025
f17e059
restrict Global Preferences visual editor to 'Global Prefs' directory…
Gitesh307 Oct 13, 2025
092db6d
Merge branch 'issue-7442-3' of https://github.com/specify/specify7 in…
Gitesh307 Oct 13, 2025
58335eb
Fix Global Preferences date dropdowns in visual editor
Gitesh307 Oct 13, 2025
e223eff
added localized helper to wrap the raw string in the LocalizedString …
Gitesh307 Oct 13, 2025
0f61df1
Lint code with ESLint and Prettier
Gitesh307 Oct 13, 2025
30427ab
Modularize preferences localization into smaller files
Gitesh307 Oct 16, 2025
1c7454a
createDictionary export for modularize prefrences
Gitesh307 Oct 17, 2025
ab64e70
Refactor preference localization dictionaries to expose raw maps
Gitesh307 Oct 17, 2025
c6a0b1d
fix: links to docs
grantfitzsimmons Oct 17, 2025
574acf8
Modularize preferences localization dictionary
Gitesh307 Oct 17, 2025
b1b0339
Merge branch 'issue-7440' of https://github.com/specify/specify7 into…
Gitesh307 Oct 17, 2025
7d78c52
Lint code with ESLint and Prettier
Gitesh307 Oct 17, 2025
9c4bc18
refactor(preferences): improve tree management
grantfitzsimmons Oct 17, 2025
2d5870c
fix: improve public attachment description
grantfitzsimmons Oct 17, 2025
8414c82
Update attachments.ts
grantfitzsimmons Oct 17, 2025
69986f5
feat: add descriptions for specify network fields
grantfitzsimmons Oct 17, 2025
94d499a
feat: improve descriptions for stats fields
grantfitzsimmons Oct 17, 2025
0e9f9c2
feat: improve title for synonym behavior section
grantfitzsimmons Oct 17, 2025
a9133aa
feat: improve cat number inheritance
grantfitzsimmons Oct 17, 2025
2ca038b
feat: improve descriptions for catalog number inheritance
grantfitzsimmons Oct 17, 2025
f2c4c2b
feat: add better icon, distinguish preferences
grantfitzsimmons Oct 17, 2025
c08bc13
fix: reorder trees in order of relevance
grantfitzsimmons Oct 17, 2025
fc6616f
feat(collection preferences): add sidebar
grantfitzsimmons Oct 17, 2025
8f9682f
feat(preferences): match user preferences visual
grantfitzsimmons Oct 17, 2025
9c20d17
Merge branch 'main' into issue-7440
grantfitzsimmons Oct 18, 2025
da3407e
fix: failing test
grantfitzsimmons Oct 18, 2025
84720f7
fix: remove unused localization strings
grantfitzsimmons Oct 18, 2025
a0e5463
refactor: simplify code
grantfitzsimmons Oct 18, 2025
7a18d71
Lint code with ESLint and Prettier
grantfitzsimmons Oct 18, 2025
93e824b
Merge branch 'issue-7440' into issue-7442-3
grantfitzsimmons Oct 18, 2025
d55c3cb
fix: add missing localization strings
grantfitzsimmons Oct 18, 2025
1fac628
feat(preferences): match collection preferences visual
grantfitzsimmons Oct 18, 2025
914d391
fix: add static test globalpreferences
grantfitzsimmons Oct 18, 2025
747f726
Lint code with ESLint and Prettier
grantfitzsimmons Oct 18, 2025
98f7152
feat: organize global settings into categories
grantfitzsimmons Oct 18, 2025
62eaab8
Merge branch 'issue-7442-3' of https://github.com/specify/specify7 in…
grantfitzsimmons Oct 18, 2025
2b35679
fix: failing test
grantfitzsimmons Oct 18, 2025
5cc8078
fixing failing tests
Gitesh307 Oct 20, 2025
8f7c0ae
Migrate screen date format from remote prefs into GlobalPreferences
Gitesh307 Oct 20, 2025
4d49db9
resolve readonly assignment errors in global preferences utils and lo…
Gitesh307 Oct 20, 2025
aa5e251
Lint code with ESLint and Prettier
Gitesh307 Oct 20, 2025
86910b6
ensure Global Preferences read/write the global app resource
Gitesh307 Oct 31, 2025
a70172a
satisfy TS readonly types in global preferences flow
Gitesh307 Oct 31, 2025
d710796
align tests with normalized formats and mock headers
Gitesh307 Oct 31, 2025
fa32925
Lint code with ESLint and Prettier
Gitesh307 Oct 31, 2025
fbf89eb
Merge branch 'issue-7440' into issue-7442-3
Gitesh307 Nov 2, 2025
4598929
Fix remote pref definitions regression
Gitesh307 Nov 2, 2025
21fdc3f
Fix collection pref usage and clean duplicate localization keys
Gitesh307 Nov 2, 2025
1a9946b
resolved merge conflicts
Gitesh307 Nov 3, 2025
03b82ff
Lint code with ESLint and Prettier
Gitesh307 Nov 3, 2025
860f25b
Merge branch 'issue-7440' into issue-7442-3
Gitesh307 Nov 16, 2025
969eceb
Lint code with ESLint and Prettier
Gitesh307 Nov 16, 2025
93290fe
Merge remote-tracking branch 'origin/issue-7440' into issue-7442-3
Gitesh307 Nov 16, 2025
88a5a21
Restrict global pref accessibilty to authorised roles
Gitesh307 Nov 16, 2025
6e0489b
Lint code with ESLint and Prettier
Gitesh307 Nov 16, 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
34 changes: 6 additions & 28 deletions .env
Original file line number Diff line number Diff line change
Expand Up @@ -3,44 +3,22 @@ DATABASE_PORT=3306
MYSQL_ROOT_PASSWORD=password
DATABASE_NAME=specify

# The following are database users with specific roles and privileges.
# If the migrator and app user are not defined, the system will use the master user credentials.
# See documenation https://discourse.specifysoftware.org/t/new-blank-database-creation-database-user-levels/3023

# MASTER Database User
# Full database administrator, used for initial setup and migrations requiring elevated privileges.
# When running Specify 7 for the first time or during updates that
# require migrations, ensure that the MASTER_NAME and MASTER_PASSWORD
# are set to the root username and password. This will ensure proper
# execution of Django migrations during the initial setup.
# After launching Specify and verifying the update is complete, you can
# safely replace these credentials with the master SQL user name and password.
MASTER_NAME=root
MASTER_PASSWORD=password

# MIGRATOR Database User
# User with elevated privileges to perform migrations (create/drop/modify tables, etc.), for Django migration steps.
MIGRATOR_NAME=specify_migrator
MIGRATOR_PASSWORD=specify_migrator

# APP Database User
# Normal runtime database user that performs application-level operations.
APP_USER_NAME=specify_user
APP_USER_PASSWORD=specify_user

# Enabling this option allows administrators with access to the
# backend Specify instance to log in as any user for support
# purposes without knowing their password.
# https://discourse.specifysoftware.org/t/allow-support-login-documentation/2838
ALLOW_SUPPORT_LOGIN=false
# The amount of time in seconds each token is valid for
SUPPORT_LOGIN_TTL = 180

# Make sure to set the `SECRET_KEY` to a unique value
SECRET_KEY=change_this_to_some_unique_random_string

ASSET_SERVER_URL=http://host.docker.internal/web_asset_store.xml
# Make sure to set the `ASSET_SERVER_KEY` to a unique value
ASSET_SERVER_KEY=your_asset_server_access_key

REDIS_HOST=redis
REDIS_PORT=6379
REDIS_DB_INDEX=0

REPORT_RUNNER_HOST=report-runner
REPORT_RUNNER_PORT=8080

Expand Down
7 changes: 6 additions & 1 deletion specifyweb/backend/context/remote_prefs.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ def get_remote_prefs() -> str:

def get_global_prefs() -> str:
res = Spappresourcedata.objects.filter(
spappresource__name='GlobalPreferences')
if res.exists():
return '\n'.join(force_str(r.data) for r in res)

legacy_res = Spappresourcedata.objects.filter(
spappresource__name='preferences',
spappresource__spappresourcedir__usertype='Global Prefs')
return '\n'.join(force_str(r.data) for r in res)
return '\n'.join(force_str(r.data) for r in legacy_res)
12 changes: 9 additions & 3 deletions specifyweb/backend/stored_queries/format.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
from sqlalchemy import types

import specifyweb.backend.context.app_resource as app_resource
from specifyweb.backend.context.remote_prefs import get_remote_prefs
from specifyweb.backend.context.remote_prefs import get_global_prefs, get_remote_prefs

from specifyweb.specify.utils.agent_types import agent_types
from specifyweb.specify.models import datamodel, Splocalecontainer
Expand Down Expand Up @@ -455,8 +455,14 @@ def _fieldformat(self, table: Table, specify_field: Field,


def get_date_format() -> str:
match = re.search(r'ui\.formatting\.scrdateformat=(.+)', get_remote_prefs())
date_format = match.group(1).strip() if match is not None else 'yyyy-MM-dd'
date_format_text = 'yyyy-MM-dd'
for prefs in (get_global_prefs(), get_remote_prefs()):
match = re.search(r'ui\.formatting\.scrdateformat=(.+)', prefs)
if match is not None:
date_format_text = match.group(1).strip()
break

date_format = date_format_text
mysql_date_format = LDLM_TO_MYSQL.get(date_format, "%Y-%m-%d")
logger.debug("dateformat = %s = %s", date_format, mysql_date_format)
return mysql_date_format
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ export function AppResourceEditor({
});
const isInOverlay = isOverlay(React.useContext(OverlayContext));

const tabs = useEditorTabs(resource);
const tabs = useEditorTabs(resource, directory);
// Return to first tab on resource type change
// eslint-disable-next-line react-hooks/exhaustive-deps
const [tabIndex, setTab] = useLiveState(React.useCallback(() => 0, [tabs]));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,7 @@ import { DataObjectFormatter } from '../Formatters';
import { formattersSpec } from '../Formatters/spec';
import { FormEditor } from '../FormEditor';
import { viewSetsSpec } from '../FormEditor/spec';
import { UserPreferencesEditor } from '../Preferences/Editor';
import { CollectionPreferencesEditor } from '../Preferences/Editor';
import { UserPreferencesEditor, CollectionPreferencesEditor, GlobalPreferencesEditor } from '../Preferences/Editor';
import { useDarkMode } from '../Preferences/Hooks';
import type { BaseSpec } from '../Syncer';
import type { SimpleXmlNode } from '../Syncer/xmlToJson';
Expand Down Expand Up @@ -160,6 +159,10 @@ export const visualAppResourceEditors = f.store<
visual: CollectionPreferencesEditor,
json: AppResourceTextEditor,
},
remotePreferences: {
visual: GlobalPreferencesEditor,
json: AppResourceTextEditor,
},
leafletLayers: undefined,
rssExportFeed: {
visual: RssExportFeedEditor,
Expand All @@ -186,4 +189,4 @@ export const visualAppResourceEditors = f.store<
otherJsonResource: undefined,
otherPropertiesResource: undefined,
otherAppResources: undefined,
}));
}));
22 changes: 20 additions & 2 deletions specifyweb/frontend/js_src/lib/components/AppResources/Tabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,8 @@ export function AppResourcesTab({
type Component = (props: AppResourceTabProps) => JSX.Element;

export function useEditorTabs(
resource: SerializedResource<SpAppResource | SpViewSetObject>
resource: SerializedResource<SpAppResource | SpViewSetObject>,
directory?: SerializedResource<SpAppResourceDir>
): RA<{
readonly label: LocalizedString;
readonly component: (props: AppResourceTabProps) => JSX.Element;
Expand All @@ -103,6 +104,23 @@ export function useEditorTabs(
f.maybe(toResource(resource, 'SpAppResource'), getAppResourceType) ??
'viewSet';
return React.useMemo(() => {
const normalizedUserType =
typeof directory?.userType === 'string'
? directory.userType.toLowerCase()
: undefined;
if (
subType === 'remotePreferences' &&
normalizedUserType !== 'global prefs'
)
return [
{
label: labels.generic,
component(props): JSX.Element {
return <AppResourceTextEditor {...props} />;
},
},
];

const editors =
typeof subType === 'string'
? visualAppResourceEditors()[subType]
Expand Down Expand Up @@ -135,7 +153,7 @@ export function useEditorTabs(
: undefined
)
);
}, [subType]);
}, [directory?.userType, subType]);
}

const labels: RR<AppResourceEditorType, LocalizedString> = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ describe('AppResourcesAside (simple no conformation case)', () => {
const onOpen = jest.fn();
const setConformations = jest.fn();

const { asFragment, unmount } = mount(
const { container, unmount } = mount(
<AppResourcesAside
conformations={[[], setConformations]}
filters={undefined}
Expand All @@ -35,7 +35,9 @@ describe('AppResourcesAside (simple no conformation case)', () => {
/>
);

expect(asFragment()).toMatchSnapshot();
const text = container.textContent ?? '';
expect(text).toContain('Global Resources (2)');
expect(text).toContain('Discipline Resources (4)');
unmount();
});
});
Expand Down Expand Up @@ -80,6 +82,7 @@ describe('AppResourcesAside (expanded case)', () => {
asFragment,
unmount: unmountSecond,
getAllByRole: getIntermediate,
container: intermediateContainer,
} = mount(
<AppResourcesAside
conformations={[_conformations, setConformations]}
Expand All @@ -90,7 +93,9 @@ describe('AppResourcesAside (expanded case)', () => {
/>
);

expect(asFragment()).toMatchSnapshot();
expect(intermediateContainer.textContent ?? '').toContain(
'Global Resources (2)'
);

const intermediateFragment = asFragment().textContent;

Expand Down Expand Up @@ -130,25 +135,28 @@ describe('AppResourcesAside (expanded case)', () => {

unmountThird();

const { asFragment: asFragmentAllExpanded, unmount: unmountExpandedll } =
mount(
<Router.MemoryRouter initialEntries={['/specify/resources/']}>
<AppResourcesAside
conformations={[_conformations, setConformations]}
filters={undefined}
isEmbedded
resources={testAppResources}
onOpen={onOpen}
/>
</Router.MemoryRouter>
);
const {
asFragment: asFragmentAllExpanded,
unmount: unmountExpandedll,
container: expandedContainer,
} = mount(
<Router.MemoryRouter initialEntries={['/specify/resources/']}>
<AppResourcesAside
conformations={[_conformations, setConformations]}
filters={undefined}
isEmbedded
resources={testAppResources}
onOpen={onOpen}
/>
</Router.MemoryRouter>
);

const expandedAllFragment = asFragmentAllExpanded().textContent;

expect(expandedAllFragment).toBe(
'Global Resources (2)Global PreferencesRemote PreferencesAdd ResourceDiscipline Resources (4)Botany (4)Add Resourcec (4)Collection PreferencesAdd ResourceUser Accounts (3)testiiif (3)User PreferencesQueryExtraListQueryFreqListAdd ResourceUser Types (0)FullAccess (0)Guest (0)LimitedAccess (0)Manager (0)Expand AllCollapse All'
);
expect(asFragmentAllExpanded()).toMatchSnapshot();
expect(expandedContainer.querySelectorAll('svg').length).toBeGreaterThan(0);
unmountExpandedll();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ describe('AppResourcesFilters', () => {
'otherJsonResource',
'otherPropertiesResource',
'otherXmlResource',
'remotePreferences',
'report',
'rssExportFeed',
'typeSearches',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { within } from '@testing-library/react';
import React from 'react';

import { clearIdStore } from '../../../hooks/useId';
Expand All @@ -24,7 +25,7 @@ function Component(props: AppResourceTabProps) {

describe('AppResourcesTab', () => {
test('simple render', () => {
const { asFragment } = mount(
const { getByRole } = mount(
<AppResourcesTab
appResource={deserializeResource(testAppResources.appResources[0])}
data="TestData"
Expand All @@ -40,7 +41,9 @@ describe('AppResourcesTab', () => {
/>
);

expect(asFragment()).toMatchSnapshot();
expect(
getByRole('heading', { level: 1, name: /data:\s*testdata/i })
).toBeInTheDocument();
});

test('dialog render', () => {
Expand All @@ -63,6 +66,11 @@ describe('AppResourcesTab', () => {
);

const dialog = getByRole('dialog');
expect(dialog).toMatchSnapshot();
expect(
within(dialog).getByRole('heading', {
level: 1,
name: /data:\s*testdata/i,
})
).toBeInTheDocument();
});
});
Loading