Skip to content

Commit ef752a2

Browse files
committed
Merge remote-tracking branch 'origin/issue-6127' into issue-6126
2 parents 45bd49d + 9ad2984 commit ef752a2

Some content is hidden

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

69 files changed

+1635
-543
lines changed

CHANGELOG.md

+29
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,35 @@ All notable changes to this project will be documented in this file.
88

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

11+
## [7.10.2](https://github.com/specify/specify7/compare/v7.10.1...v7.10.2) (23 April 2025)
12+
13+
## Added
14+
15+
* **[Batch Editing](https://discourse.specifysoftware.org/t/batch-edit/2248)** for simple fields ([#5417](https://github.com/specify/specify7/pull/5417)*Requested by many, many members*)
16+
* [**Added a 'Download' button** to record set and query results attachments](https://discourse.specifysoftware.org/t/bulk-download-attachments-from-a-query-result-or-record-set/2456) ([#6052](https://github.com/specify/specify7/pull/6052)*Requested by The University of Michigan, Naturhistorisches Museum Bern, Beaty Biodiversity Museum, Agriculture and Agri-Food Canada, Muséum d'Histoire Naturelle Geneva, and Queensland Herbarium*)
17+
* The [Form Meta menu](https://discourse.specifysoftware.org/t/form-meta-menu/844) is now accessible when using "View in Forms" ([#5416](https://github.com/specify/specify7/pull/5416))
18+
* A default value can now be used for a query combo box ([#6037]([https://github.com/specify/specify7/pull/6037])*Requested by South African Institute for Aquatic Biodiversity and The Ohio State University Mollusk Division*)
19+
* Adds tooltip for required fields in bulk carry forward config ([#6202](https://github.com/specify/specify7/pull/6202))
20+
* Specify will use the default `TypeSearches` when a custom one is not defined for a table ([#6236](https://github.com/specify/specify7/pull/6236))
21+
* Adds a new `uniqueIdentifier` field to Storage ([#6249](https://github.com/specify/specify7/pull/6249))
22+
23+
## Changed
24+
25+
* Non-default Taxon trees can be deleted if they are not used ([#6186](https://github.com/specify/specify7/pull/6186))
26+
* Adds modern attachment placeholder icons for video, audio, text, and other attachment filetypes ([#6119](https://github.com/specify/specify7/pull/6119))
27+
28+
## Fixed
29+
30+
* Fixes an issue that caused some WorkBench data sets to match to the incorrect ranks ([#6322](https://github.com/specify/specify7/pull/6322))
31+
* Fixes an issue where the defaults for insect collections were missing ([#6383](https://github.com/specify/specify7/pull/6383))
32+
* Field formats in the Schema Config utility are selected automatically again ([#6255](https://github.com/specify/specify7/pull/6255))
33+
* Primary Collection Objects (COs) in a consolidated Collection Object Group (COG) can no longer be deleted unless another CO is marked as primary ([#6181](https://github.com/specify/specify7/pull/6181))
34+
* Fixes all cases where table aggregation separators were missing ([#6115](https://github.com/specify/specify7/pull/6115))
35+
* Fixes all cases where taxon tree structure is invalid due to accepted names not being designated as accepted ([#5366](https://github.com/specify/specify7/pull/5366))
36+
* Fixes all cases where coordinate text fields are empty but decimal coordinate fields contain values ([#5368](https://github.com/specify/specify7/pull/5368)*Reported by The University of Michigan, College of Idaho, and several others*)
37+
* Fixes an issue where the Data Entry screen does not appear in the 'Geology' discipline ([#6365](https://github.com/specify/specify7/pull/6365))
38+
* Fixes an issue where unsaved changes would be lost when toggling the "Use Localized Field Labels" setting in the Form Meta menu
39+
1140
## [7.10.1](https://github.com/specify/specify7/compare/v7.10.0...v7.10.1) (10 March 2025)
1241

1342
## Added

Dockerfile

+7-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ RUN apt-get update \
99
libldap-2.4-2 \
1010
libmariadb3 \
1111
rsync \
12+
tzdata \
1213
&& apt-get clean \
1314
&& rm -rf /var/lib/apt/lists/*
1415

@@ -42,6 +43,8 @@ RUN npx webpack --mode production
4243

4344
FROM common AS build-backend
4445

46+
ENV DEBIAN_FRONTEND=noninteractive
47+
4548
RUN apt-get update \
4649
&& apt-get -y install --no-install-recommends \
4750
build-essential \
@@ -58,6 +61,7 @@ RUN apt-get update \
5861
python3.8-distutils \
5962
python3.8-dev \
6063
libmariadbclient-dev \
64+
tzdata \
6165
&& apt-get clean \
6266
&& rm -rf /var/lib/apt/lists/*
6367

@@ -129,13 +133,15 @@ RUN echo \
129133
"\nREPORT_RUNNER_PORT = os.getenv('REPORT_RUNNER_PORT', '')" \
130134
"\nWEB_ATTACHMENT_URL = os.getenv('ASSET_SERVER_URL', None)" \
131135
"\nWEB_ATTACHMENT_KEY = os.getenv('ASSET_SERVER_KEY', None)" \
132-
"\nWEB_ATTACHMENT_COLLECTION = os.getenv('ASSET_SERVER_COLLECTION', None)" \
136+
"\nWEB_ATTACHMENT_COLLECTION = os.getenv('ASSET_SERVER_COLLECTION', DATABASE_NAME) or DATABASE_NAME" \
133137
"\nSEPARATE_WEB_ATTACHMENT_FOLDERS = os.getenv('SEPARATE_WEB_ATTACHMENT_FOLDERS', None)" \
134138
"\nCELERY_BROKER_URL = os.getenv('CELERY_BROKER_URL', None)" \
135139
"\nCELERY_RESULT_BACKEND = os.getenv('CELERY_RESULT_BACKEND', None)" \
136140
"\nCELERY_TASK_DEFAULT_QUEUE = os.getenv('CELERY_TASK_QUEUE', DATABASE_NAME)" \
137141
"\nANONYMOUS_USER = os.getenv('ANONYMOUS_USER', None)" \
138142
"\nSPECIFY_CONFIG_DIR = os.environ.get('SPECIFY_CONFIG_DIR', '/opt/Specify/config')" \
143+
"\nhost = os.getenv('CSRF_TRUSTED_ORIGINS', None)" \
144+
"\nCSRF_TRUSTED_ORIGINS = [origin.strip() for origin in host.split(',')] if host else []" \
139145
> settings/local_specify_settings.py
140146

141147
RUN echo "import os \nDEBUG = os.getenv('SP7_DEBUG', '').lower() == 'true'\n" \

docker-entrypoint.sh

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#!/bin/bash
12
set -e
23
if [ -z "$(ls -A /volumes/static-files/specify-config)" ]; then
34
mkdir -p /volumes/static-files/specify-config/config/

requirements.txt

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
1+
setuptools>=50.0.0
2+
tzdata
3+
wheel
4+
backports.zoneinfo==0.2.1
15
kombu==5.2.4
26
celery[redis]==5.2.7
3-
Django==3.2.15
7+
Django==4.2.18
48
mysqlclient==2.1.1
59
SQLAlchemy==1.2.11
610
requests==2.32.2

specifyweb/accounts/views.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
from django.shortcuts import render
1717
from django.template.response import TemplateResponse
1818
from django.utils import crypto
19-
from django.utils.http import is_safe_url, urlencode
19+
from django.utils.http import url_has_allowed_host_and_scheme, urlencode
2020
from django.views.decorators.cache import never_cache
2121
from typing import Union, Optional, Dict, cast
2222
from typing_extensions import TypedDict
@@ -362,7 +362,7 @@ def choose_collection(request) -> http.HttpResponse:
362362

363363
redirect_to = (request.POST if request.method == "POST" else request.GET).get('next', '')
364364
redirect_resp = http.HttpResponseRedirect(
365-
redirect_to if is_safe_url(url=redirect_to, allowed_hosts=request.get_host())
365+
redirect_to if url_has_allowed_host_and_scheme(url=redirect_to, allowed_hosts=request.get_host())
366366
else settings.LOGIN_REDIRECT_URL
367367
)
368368

specifyweb/attachment_gw/urls.py

+8-9
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
1-
from django.conf.urls import url
1+
from django.urls import path
22

33
from . import views
44

55
urlpatterns = [
6-
url(r'^get_settings/$', views.get_settings),
7-
url(r'^get_upload_params/$', views.get_upload_params),
8-
url(r'^get_token/$', views.get_token),
9-
url(r'^proxy/$', views.proxy),
10-
url(r'^download_all/$', views.download_all),
11-
url(r'^dataset/$', views.datasets),
12-
url(r'^dataset/(?P<ds_id>\d+)/$', views.dataset),
13-
6+
path('get_settings/', views.get_settings),
7+
path('get_upload_params/', views.get_upload_params),
8+
path('get_token/', views.get_token),
9+
path('proxy/', views.proxy),
10+
path('download_all/', views.download_all),
11+
path('dataset/', views.datasets),
12+
path('dataset/<int:ds_id>/', views.dataset),
1413
]

specifyweb/attachment_gw/views.py

+5-2
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,11 @@ def get_collection(request=None):
7272
# do any better than using the first collection
7373
# and hoping that all the assets are in the same
7474
# folder.
75-
from specifyweb.specify.models import Collection
76-
return Collection.objects.all()[0].collectionname
75+
# from specifyweb.specify.models import Collection
76+
# return Collection.objects.all()[0].collectionname
77+
78+
# The default coll parameter to assets is the database name
79+
return settings.DATABASE_NAME
7780

7881
@openapi(schema={
7982
"get": {

specifyweb/barvis/urls.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
from django.conf.urls import url
1+
from django.urls import path
22

33
from . import views
44

55
urlpatterns = [
6-
url(r'^taxon_bar/$', views.taxon_bar),
6+
path('taxon_bar/', views.taxon_bar),
77
]

specifyweb/businessrules/__init__.py

-1
Original file line numberDiff line numberDiff line change
@@ -1 +0,0 @@
1-
default_app_config = 'specifyweb.businessrules.apps.BussinessRuleConfig'

specifyweb/businessrules/urls.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
from django.conf.urls import include, url
1+
from django.urls import path
22

33
from . import views
44

55
urlpatterns = [
6-
url(r'^uniqueness_rules/(?P<discipline_id>\d+)/$', views.uniqueness_rule),
7-
url(r'^uniqueness_rules/validate/$', views.validate_uniqueness),
6+
path('uniqueness_rules/<int:discipline_id>/', views.uniqueness_rule),
7+
path('uniqueness_rules/validate/', views.validate_uniqueness),
88
]

specifyweb/context/remote_prefs.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from django.utils.encoding import force_text
1+
from django.utils.encoding import force_str
22

33
from specifyweb.specify.models import Spappresourcedata
44

@@ -11,10 +11,10 @@ def get_remote_prefs() -> str:
1111
# Spappresource.data is stored in a blob field even though we treat
1212
# it as a TextField. Starting in django 2.2 it doesn't automatically
1313
# get decoded from bytes to str.
14-
return '\n'.join(force_text(r.data) for r in res)
14+
return '\n'.join(force_str(r.data) for r in res)
1515

1616
def get_global_prefs() -> str:
1717
res = Spappresourcedata.objects.filter(
1818
spappresource__name='preferences',
1919
spappresource__spappresourcedir__usertype='Global Prefs')
20-
return '\n'.join(force_text(r.data) for r in res)
20+
return '\n'.join(force_str(r.data) for r in res)

specifyweb/context/testurls.py

+7-7
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@
22
Provides urls to access the mocked views
33
"""
44

5-
from django.conf.urls import url
5+
from django.urls import path, re_path
66

77
from . import testsviews as views
88

99
urlpatterns = [
10-
url(r'^collection/$', views.collection),
11-
url(r'^domain.json$', views.domain),
12-
url(r'^viewsets/(?P<level>\d+).xml$', views.viewsets),
13-
url(r'^schema_localization.json$', views.schema_localization),
14-
url(r'^app.resource$', views.app_resource),
15-
url(r'^available_related_searches.json$', views.available_related_searches),
10+
path('collection/', views.collection),
11+
re_path(r'^domain.json$', views.domain),
12+
re_path(r'^viewsets/(?P<level>\d+).xml$', views.viewsets),
13+
re_path(r'^schema_localization.json$', views.schema_localization),
14+
re_path(r'^app.resource$', views.app_resource),
15+
re_path(r'^available_related_searches.json$', views.available_related_searches),
1616
]

specifyweb/context/urls.py

+23-23
Original file line numberDiff line numberDiff line change
@@ -2,36 +2,36 @@
22
Defines the urls for the app context subsystem
33
"""
44

5-
from django.conf.urls import url
5+
from django.urls import path, re_path
66
from django.urls import path
77

88
from . import views, user_resources, collection_resources
99
from ..attachment_gw.views import get_settings as attachment_settings
1010
from ..report_runner.views import get_status as report_runner_status
1111

1212
urlpatterns = [
13-
url(r'^login/$', views.api_login),
14-
url(r'^collection/$', views.collection),
15-
url(r'^user_collection_access_for_sp6/(?P<userid>\d+)/$', views.user_collection_access_for_sp6),
16-
url(r'^language/$', views.languages),
17-
url(r'^schema/language/$', views.schema_language),
18-
19-
url(r'^api_endpoints.json$', views.api_endpoints),
20-
url(r'^api_endpoints_all.json$', views.api_endpoints_all),
21-
url(r'^user.json$', views.user),
22-
url(r'^system_info.json$', views.system_info),
23-
url(r'^domain.json$', views.domain),
24-
url(r'^view.json$', views.view),
25-
url(r'^views.json$', views.views),
26-
url(r'^viewsets.json$', views.viewsets),
27-
url(r'^datamodel.json$', views.datamodel),
28-
url(r'^schema_localization.json$', views.schema_localization),
29-
url(r'^app.resource$', views.app_resource),
30-
url(r'^available_related_searches.json$', views.available_related_searches),
31-
url(r'^remoteprefs.properties$', views.remote_prefs),
32-
33-
url(r'^attachment_settings.json$', attachment_settings),
34-
url(r'^report_runner_status.json$', report_runner_status),
13+
path('login/', views.api_login),
14+
path('collection/', views.collection),
15+
path('user_collection_access_for_sp6/<int:userid>/', views.user_collection_access_for_sp6),
16+
path('language/', views.languages),
17+
path('schema/language/', views.schema_language),
18+
19+
re_path(r'^api_endpoints.json$', views.api_endpoints),
20+
re_path(r'^api_endpoints_all.json$', views.api_endpoints_all),
21+
re_path(r'^user.json$', views.user),
22+
re_path(r'^system_info.json$', views.system_info),
23+
re_path(r'^domain.json$', views.domain),
24+
re_path(r'^view.json$', views.view),
25+
re_path(r'^views.json$', views.views),
26+
re_path(r'^viewsets.json$', views.viewsets),
27+
re_path(r'^datamodel.json$', views.datamodel),
28+
re_path(r'^schema_localization.json$', views.schema_localization),
29+
re_path(r'^app.resource$', views.app_resource),
30+
re_path(r'^available_related_searches.json$', views.available_related_searches),
31+
re_path(r'^remoteprefs.properties$', views.remote_prefs),
32+
33+
re_path(r'^attachment_settings.json$', attachment_settings),
34+
re_path(r'^report_runner_status.json$', report_runner_status),
3535

3636
path('user_resource/', user_resources.user_resources),
3737
path('user_resource/<int:resourceid>/', user_resources.user_resource),

specifyweb/export/urls.py

+6-6
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
from django.conf.urls import url
1+
from django.urls import path
22

33
from . import views
44

55
urlpatterns = [
6-
url(r'^rss/$', views.rss_feed),
7-
url(r'^extract_eml/(?P<filename>.+)$', views.extract_eml),
8-
url(r'^make_dwca/$', views.export),
9-
url(r'extract_query/(?P<query_id>\d+)/$', views.extract_query),
10-
url(r'force_update/$', views.force_update),
6+
path('rss/', views.rss_feed),
7+
path('extract_eml/<path:filename>', views.extract_eml),
8+
path('make_dwca/', views.export),
9+
path('extract_query/<int:query_id>/', views.extract_query),
10+
path('force_update/', views.force_update),
1111
]

specifyweb/express_search/urls.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
from django.conf.urls import url
1+
from django.urls import path, re_path
22

33
from . import views
44
urlpatterns = [
5-
url(r'^$', views.search),
6-
url(r'^related/$', views.related_search),
7-
url(r'^querycbx/(?P<modelname>\w*)/$', views.querycbx_search),
5+
path('', views.search),
6+
path('related/', views.related_search),
7+
re_path(r'^querycbx/(?P<modelname>\w*)/$', views.querycbx_search),
88
]

specifyweb/frontend/doc_urls.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
from django.conf.urls import url
1+
from django.urls import path
22

33
from . import views
44

55
urlpatterns = [
6-
url(r'^api/tables/$', views.api_tables),
7-
url(r'^api/operations/$', views.api_operations),
8-
url(r'^api/operations/all/$', views.api_operations_all),
6+
path('api/tables/', views.api_tables),
7+
path('api/operations/', views.api_operations),
8+
path('api/operations/all/', views.api_operations_all),
99
]

specifyweb/frontend/js_src/lib/components/Attachments/__tests__/__snapshots__/utils.test.ts.snap

+2
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ exports[`allTablesWithAttachments 1`] = `
3131
"[table TreatmentEvent]",
3232
"[table AbsoluteAge]",
3333
"[table RelativeAge]",
34+
"[table Spdataset]",
3435
]
3536
`;
3637

@@ -66,5 +67,6 @@ exports[`attachmentRelatedTables 1`] = `
6667
"TreatmentEventAttachment",
6768
"AbsoluteAgeAttachment",
6869
"RelativeAgeAttachment",
70+
"SpDataSetAttachment",
6971
]
7072
`;

specifyweb/frontend/js_src/lib/components/DataModel/__tests__/__snapshots__/specifyTable.test.ts.snap

+1
Original file line numberDiff line numberDiff line change
@@ -1527,6 +1527,7 @@ exports[`tableScoping 1`] = `
15271527
"SpAppResourceDir": "collection",
15281528
"SpAuditLog": undefined,
15291529
"SpAuditLogField": undefined,
1530+
"SpDataSetAttachment": "spdataset > collection",
15301531
"SpExportSchema": "discipline",
15311532
"SpExportSchemaItem": "spExportSchema > discipline",
15321533
"SpExportSchemaItemMapping": "exportSchemaItem > spExportSchema > discipline",

0 commit comments

Comments
 (0)