diff --git a/requirements/base.in b/requirements/base.in index 9566533..7b3853c 100644 --- a/requirements/base.in +++ b/requirements/base.in @@ -10,5 +10,5 @@ django-treebeard # django-extra-fields django-tinymce geopy - +django-open-forms-client django-localflavor diff --git a/requirements/base.txt b/requirements/base.txt index 916f250..dd24bff 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -82,6 +82,7 @@ django==4.2.13 # django-localflavor # django-log-outgoing-requests # django-markup + # django-open-forms-client # django-otp # django-phonenumber-field # django-privates @@ -130,6 +131,8 @@ django-log-outgoing-requests==0.6.1 # via open-api-framework django-markup==1.8.1 # via open-api-framework +django-open-forms-client==0.4.0 + # via -r requirements/base.in django-ordered-model==3.7.4 # via django-admin-index django-otp==1.5.0 @@ -154,6 +157,7 @@ django-solo==2.3.0 # via # commonground-api-common # django-log-outgoing-requests + # django-open-forms-client # mozilla-django-oidc-db # notifications-api-common # zgw-consumers @@ -306,6 +310,7 @@ requests==2.32.3 # commonground-api-common # coreapi # django-log-outgoing-requests + # django-open-forms-client # gemma-zds-client # mozilla-django-oidc # open-api-framework diff --git a/requirements/ci.txt b/requirements/ci.txt index 13cf7d2..cc344b1 100644 --- a/requirements/ci.txt +++ b/requirements/ci.txt @@ -153,6 +153,7 @@ django==4.2.13 # django-localflavor # django-log-outgoing-requests # django-markup + # django-open-forms-client # django-otp # django-phonenumber-field # django-privates @@ -228,6 +229,10 @@ django-markup==1.8.1 # -c requirements/base.txt # -r requirements/base.txt # open-api-framework +django-open-forms-client==0.4.0 + # via + # -c requirements/base.txt + # -r requirements/base.txt django-ordered-model==3.7.4 # via # -c requirements/base.txt @@ -284,6 +289,7 @@ django-solo==2.3.0 # -r requirements/base.txt # commonground-api-common # django-log-outgoing-requests + # django-open-forms-client # mozilla-django-oidc-db # notifications-api-common # zgw-consumers @@ -633,6 +639,7 @@ requests==2.32.3 # commonground-api-common # coreapi # django-log-outgoing-requests + # django-open-forms-client # gemma-zds-client # mozilla-django-oidc # open-api-framework diff --git a/requirements/dev.txt b/requirements/dev.txt index ce0d69a..0de8757 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -183,6 +183,7 @@ django==4.2.13 # django-localflavor # django-log-outgoing-requests # django-markup + # django-open-forms-client # django-otp # django-phonenumber-field # django-privates @@ -262,6 +263,10 @@ django-markup==1.8.1 # -c requirements/ci.txt # -r requirements/ci.txt # open-api-framework +django-open-forms-client==0.4.0 + # via + # -c requirements/ci.txt + # -r requirements/ci.txt django-ordered-model==3.7.4 # via # -c requirements/ci.txt @@ -318,6 +323,7 @@ django-solo==2.3.0 # -r requirements/ci.txt # commonground-api-common # django-log-outgoing-requests + # django-open-forms-client # mozilla-django-oidc-db # notifications-api-common # zgw-consumers @@ -724,6 +730,7 @@ requests==2.32.3 # commonground-api-common # coreapi # django-log-outgoing-requests + # django-open-forms-client # gemma-zds-client # mozilla-django-oidc # open-api-framework diff --git a/src/open_producten/conf/base.py b/src/open_producten/conf/base.py index 2f77e03..d018748 100644 --- a/src/open_producten/conf/base.py +++ b/src/open_producten/conf/base.py @@ -17,6 +17,7 @@ "localflavor", "treebeard", "tinymce", + "openformsclient", "django.contrib.gis", "open_producten.accounts", "open_producten.utils", @@ -114,6 +115,8 @@ "bold italic backcolor | alignleft aligncenter " "alignright alignjustify | bullist numlist outdent indent | " "removeformat", + "skin": "oxide-dark", + "content_css": "dark", } TINYMCE_EXTRA_MEDIA = {"js": ["initTinymce.js"]} diff --git a/src/open_producten/conf/locale/nl/LC_MESSAGES/django.po b/src/open_producten/conf/locale/nl/LC_MESSAGES/django.po index 17b796b..1150292 100644 --- a/src/open_producten/conf/locale/nl/LC_MESSAGES/django.po +++ b/src/open_producten/conf/locale/nl/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2022-02-13 20:35+0100\n" +"POT-Creation-Date: 2024-11-06 15:53+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -18,45 +18,45 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: foobar/accounts/models.py:18 +#: open_producten/accounts/models.py:18 msgid "username" msgstr "gebruikersnaam" -#: foobar/accounts/models.py:22 +#: open_producten/accounts/models.py:22 msgid "Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only." msgstr "" "Vereist. 150 tekens of minder. Alleen letters, cijfers en @/,/+/-/_ tekens " "zijn toegestaan." -#: foobar/accounts/models.py:26 +#: open_producten/accounts/models.py:26 msgid "A user with that username already exists." msgstr "Een gebruiker met deze gebruikersnaam bestaat al." -#: foobar/accounts/models.py:29 +#: open_producten/accounts/models.py:29 msgid "first name" msgstr "voornaam" -#: foobar/accounts/models.py:30 +#: open_producten/accounts/models.py:30 msgid "last name" msgstr "achternaam" -#: foobar/accounts/models.py:31 +#: open_producten/accounts/models.py:31 msgid "email address" msgstr "e-mailadres" -#: foobar/accounts/models.py:33 +#: open_producten/accounts/models.py:33 msgid "staff status" msgstr "stafstatus" -#: foobar/accounts/models.py:35 +#: open_producten/accounts/models.py:35 msgid "Designates whether the user can log into this admin site." msgstr "Bepaalt of de gebruiker zich op deze beheerwebsite kan aanmelden." -#: foobar/accounts/models.py:38 +#: open_producten/accounts/models.py:38 msgid "active" msgstr "actief" -#: foobar/accounts/models.py:41 +#: open_producten/accounts/models.py:41 msgid "" "Designates whether this user should be treated as active. Unselect this " "instead of deleting accounts." @@ -64,74 +64,770 @@ msgstr "" "Bepaalt of deze gebruiker als actief dient te worden behandeld. U kunt dit " "uitvinken in plaats van een gebruiker te verwijderen." -#: foobar/accounts/models.py:45 +#: open_producten/accounts/models.py:45 msgid "date joined" msgstr "datum toegetreden" -#: foobar/accounts/models.py:53 +#: open_producten/accounts/models.py:53 msgid "user" msgstr "gebruiker" -#: foobar/accounts/models.py:54 +#: open_producten/accounts/models.py:54 msgid "users" msgstr "gebruikers" -#: foobar/accounts/utils.py:18 +#: open_producten/accounts/utils.py:18 msgid "You need to be superuser to create other superusers." msgstr "" "U dient een supergebruiker te zijn om andere supergebruikers aan te maken." -#: foobar/accounts/utils.py:44 +#: open_producten/accounts/utils.py:44 msgid "You cannot create or update a user with more permissions than yourself." msgstr "U kunt een gebruiker niet meer rechten geven dan u zelf heeft." -#: foobar/templates/admin/base_site.html:5 -#: foobar/templates/admin/base_site.html:24 +#: open_producten/locations/admin/organisation.py:21 +#: open_producten/locations/models/contact.py:50 +msgid "Contact" +msgstr "" + +#: open_producten/locations/admin/organisation.py:23 +#, fuzzy +#| msgid "email address" +msgid "Address" +msgstr "e-mailadres" + +#: open_producten/locations/models/contact.py:13 +#: open_producten/locations/models/organisation.py:67 +#, fuzzy +#| msgid "administration" +msgid "Organisation" +msgstr "beheer" + +#: open_producten/locations/models/contact.py:18 +msgid "The organisation of the contact" +msgstr "" + +#: open_producten/locations/models/contact.py:21 +#, fuzzy +#| msgid "first name" +msgid "First name" +msgstr "voornaam" + +#: open_producten/locations/models/contact.py:23 +msgid "First name of the contact" +msgstr "" + +#: open_producten/locations/models/contact.py:26 +#, fuzzy +#| msgid "last name" +msgid "Last name" +msgstr "achternaam" + +#: open_producten/locations/models/contact.py:28 +msgid "Last name of the contact" +msgstr "" + +#: open_producten/locations/models/contact.py:31 +#: open_producten/locations/models/location.py:20 +#, fuzzy +#| msgid "email address" +msgid "Email address" +msgstr "e-mailadres" + +#: open_producten/locations/models/contact.py:33 +msgid "The email address of the contact" +msgstr "" + +#: open_producten/locations/models/contact.py:36 +#: open_producten/locations/models/location.py:24 +msgid "Phone number" +msgstr "" + +#: open_producten/locations/models/contact.py:40 +msgid "The phone number of the contact" +msgstr "" + +#: open_producten/locations/models/contact.py:43 +msgid "Rol" +msgstr "" + +#: open_producten/locations/models/contact.py:46 +msgid "The role/function of the contact" +msgstr "" + +#: open_producten/locations/models/contact.py:51 +#: open_producten/producttypes/models/producttype.py:127 +msgid "Contacts" +msgstr "" + +#: open_producten/locations/models/location.py:15 +#: open_producten/locations/models/organisation.py:10 +#: open_producten/locations/models/organisation.py:26 +#: open_producten/producttypes/models/category.py:23 +#: open_producten/producttypes/models/condition.py:11 +#: open_producten/producttypes/models/field.py:40 +#: open_producten/producttypes/models/link.py:18 +#: open_producten/producttypes/models/producttype.py:31 +#: open_producten/producttypes/models/tag.py:9 +#: open_producten/producttypes/models/tag.py:25 +#: open_producten/producttypes/models/upn.py:9 +msgid "Name" +msgstr "" + +#: open_producten/locations/models/location.py:32 +msgid "street" +msgstr "" + +#: open_producten/locations/models/location.py:32 +msgid "Address street" +msgstr "" + +#: open_producten/locations/models/location.py:35 +msgid "house number" +msgstr "" + +#: open_producten/locations/models/location.py:41 +msgid "postcode" +msgstr "" + +#: open_producten/locations/models/location.py:43 +msgid "Address postcode" +msgstr "" + +#: open_producten/locations/models/location.py:46 +msgid "city" +msgstr "" + +#: open_producten/locations/models/location.py:46 +msgid "Address city" +msgstr "" + +#: open_producten/locations/models/location.py:49 +msgid "coordinates" +msgstr "" + +#: open_producten/locations/models/location.py:50 +msgid "Geo coordinates of the location" +msgstr "" + +#: open_producten/locations/models/location.py:69 +#, python-format +msgid "Locating geo coordinates has failed: %(exc)s" +msgstr "" + +#: open_producten/locations/models/location.py:72 +msgid "No location data was provided" +msgstr "" + +#: open_producten/locations/models/location.py:77 +msgid "" +"Geo coordinates of the address can't be found. Make sure that the address " +"data are correct" +msgstr "" + +#: open_producten/locations/models/location.py:86 +msgid "Location" +msgstr "" + +#: open_producten/locations/models/location.py:87 +#: open_producten/producttypes/models/producttype.py:135 +msgid "Locations" +msgstr "" + +#: open_producten/locations/models/organisation.py:13 +#: open_producten/locations/models/organisation.py:17 +#: open_producten/locations/models/organisation.py:53 +msgid "Organisation type" +msgstr "" + +#: open_producten/locations/models/organisation.py:18 +msgid "Organisation types" +msgstr "" + +#: open_producten/locations/models/organisation.py:29 +msgid "Neighbourhood name" +msgstr "" + +#: open_producten/locations/models/organisation.py:33 +#: open_producten/locations/models/organisation.py:58 +msgid "Neighbourhood" +msgstr "" + +#: open_producten/locations/models/organisation.py:34 +msgid "Neighbourhoods" +msgstr "" + +#: open_producten/locations/models/organisation.py:42 +msgid "Logo" +msgstr "" + +#: open_producten/locations/models/organisation.py:45 +msgid "Logo of the organisation" +msgstr "" + +#: open_producten/locations/models/organisation.py:50 +#: open_producten/producttypes/models/field.py:52 +#: open_producten/producttypes/models/tag.py:35 +msgid "Type" +msgstr "" + +#: open_producten/locations/models/organisation.py:63 +msgid "The neighbourhood of the organisation" +msgstr "" + +#: open_producten/locations/models/organisation.py:68 +#: open_producten/producttypes/models/producttype.py:119 +#, fuzzy +#| msgid "administration" +msgid "Organisations" +msgstr "beheer" + +#: open_producten/products/models/data.py:28 +#: open_producten/producttypes/models/field.py:75 +msgid "Field" +msgstr "" + +#: open_producten/products/models/data.py:30 +msgid "The field that this data belongs to" +msgstr "" + +#: open_producten/products/models/data.py:33 +msgid "Value" +msgstr "" + +#: open_producten/products/models/data.py:33 +msgid "The value of the field" +msgstr "" + +#: open_producten/products/models/data.py:37 +#: open_producten/products/models/product.py:47 +msgid "Product" +msgstr "" + +#: open_producten/products/models/data.py:39 +msgid "The product that this data belongs to" +msgstr "" + +#: open_producten/products/models/data.py:44 +#: open_producten/products/models/data.py:45 +msgid "Data" +msgstr "" + +#: open_producten/products/models/product.py:14 +#: open_producten/producttypes/models/file.py:12 +#: open_producten/producttypes/models/link.py:12 +#: open_producten/producttypes/models/price.py:16 +#: open_producten/producttypes/models/producttype.py:142 +#: open_producten/producttypes/models/question.py:24 +msgid "Product type" +msgstr "" + +#: open_producten/products/models/product.py:16 +msgid "The type of this product" +msgstr "" + +#: open_producten/products/models/product.py:21 +#: open_producten/producttypes/models/price.py:22 +msgid "Start date" +msgstr "" + +#: open_producten/products/models/product.py:21 +msgid "The start date for this product" +msgstr "" + +#: open_producten/products/models/product.py:24 +msgid "End date" +msgstr "" + +#: open_producten/products/models/product.py:24 +msgid "The end date for this product" +msgstr "" + +#: open_producten/products/models/product.py:28 +msgid "BSN" +msgstr "" + +#: open_producten/products/models/product.py:30 +msgid "The BSN of the product owner. BSN of 8 characters needs a leading 0." +msgstr "" + +#: open_producten/products/models/product.py:38 +msgid "KVK number" +msgstr "" + +#: open_producten/products/models/product.py:39 +msgid "The KVK number of the product owner" +msgstr "" + +#: open_producten/products/models/product.py:48 +msgid "Products" +msgstr "" + +#: open_producten/producttypes/admin/category.py:38 +msgid "Parent nodes cannot be unpublished with published children." +msgstr "" + +#: open_producten/producttypes/admin/category.py:71 +msgid "Category permissions" +msgstr "" + +#: open_producten/producttypes/admin/producttype.py:19 +msgid "Allowed admin categories" +msgstr "" + +#: open_producten/producttypes/admin/producttype.py:22 +#: open_producten/producttypes/models/category.py:46 +#: open_producten/producttypes/models/question.py:16 +msgid "Category" +msgstr "" + +#: open_producten/producttypes/admin/producttype.py:28 +msgid "At least one category is required" +msgstr "" + +#: open_producten/producttypes/models/category.py:16 +msgid "Published nodes cannot be nested under unpublished ones." +msgstr "" + +#: open_producten/producttypes/models/category.py:23 +msgid "Name of the category" +msgstr "" + +#: open_producten/producttypes/models/category.py:27 +#: open_producten/producttypes/models/field.py:43 +#: open_producten/producttypes/models/price.py:53 +msgid "Description" +msgstr "" + +#: open_producten/producttypes/models/category.py:30 +msgid "Description of the category" +msgstr "" + +#: open_producten/producttypes/models/category.py:33 +#: open_producten/producttypes/models/producttype.py:44 +#: open_producten/producttypes/models/tag.py:28 +msgid "Icon" +msgstr "" + +#: open_producten/producttypes/models/category.py:36 +msgid "Icon of the category" +msgstr "" + +#: open_producten/producttypes/models/category.py:39 +#: open_producten/producttypes/models/producttype.py:51 +msgid "Image" +msgstr "" + +#: open_producten/producttypes/models/category.py:42 +msgid "Image of the category" +msgstr "" + +#: open_producten/producttypes/models/category.py:47 +#: open_producten/producttypes/models/producttype.py:110 +msgid "Categories" +msgstr "" + +#: open_producten/producttypes/models/category.py:64 +msgid "Parent nodes have to be published in order to publish a child." +msgstr "" + +#: open_producten/producttypes/models/category.py:69 +msgid "Parent nodes cannot be unpublished if they have published children." +msgstr "" + +#: open_producten/producttypes/models/condition.py:13 +msgid "Short name of the condition" +msgstr "" + +#: open_producten/producttypes/models/condition.py:16 +#: open_producten/producttypes/models/question.py:30 +#: open_producten/producttypes/models/question.py:34 +msgid "Question" +msgstr "" + +#: open_producten/producttypes/models/condition.py:17 +msgid "Question used in the question-answer game" +msgstr "" + +#: open_producten/producttypes/models/condition.py:20 +msgid "Positive text" +msgstr "" + +#: open_producten/producttypes/models/condition.py:21 +msgid "Description how to meet the condition" +msgstr "" + +#: open_producten/producttypes/models/condition.py:24 +msgid "Negative text" +msgstr "" + +#: open_producten/producttypes/models/condition.py:25 +msgid "Description how not to meet the condition" +msgstr "" + +#: open_producten/producttypes/models/condition.py:29 +msgid "Condition" +msgstr "" + +#: open_producten/producttypes/models/condition.py:30 +#: open_producten/producttypes/models/producttype.py:95 +msgid "Conditions" +msgstr "" + +#: open_producten/producttypes/models/field.py:40 +msgid "The name of the field" +msgstr "" + +#: open_producten/producttypes/models/field.py:44 +msgid "Short description of the field" +msgstr "" + +#: open_producten/producttypes/models/field.py:53 +msgid "The formio type of the field" +msgstr "" + +#: open_producten/producttypes/models/field.py:58 +msgid "Product Type" +msgstr "" + +#: open_producten/producttypes/models/field.py:59 +msgid "The product type that this field is part of" +msgstr "" + +#: open_producten/producttypes/models/field.py:65 +msgid "Choices" +msgstr "" + +#: open_producten/producttypes/models/field.py:69 +msgid "The Choices that can be selected in the form" +msgstr "" + +#: open_producten/producttypes/models/field.py:76 +msgid "Fields" +msgstr "" + +#: open_producten/producttypes/models/file.py:15 +#: open_producten/producttypes/models/link.py:15 +msgid "Related product type" +msgstr "" + +#: open_producten/producttypes/models/file.py:17 +msgid "File" +msgstr "" + +#: open_producten/producttypes/models/file.py:20 +msgid "Product type file" +msgstr "" + +#: open_producten/producttypes/models/file.py:21 +msgid "Product type files" +msgstr "" + +#: open_producten/producttypes/models/link.py:18 +msgid "Name for the link" +msgstr "" + +#: open_producten/producttypes/models/link.py:20 +msgid "Url" +msgstr "" + +#: open_producten/producttypes/models/link.py:20 +msgid "Url of the link" +msgstr "" + +#: open_producten/producttypes/models/link.py:23 +msgid "Product type link" +msgstr "" + +#: open_producten/producttypes/models/link.py:24 +msgid "Product type links" +msgstr "" + +#: open_producten/producttypes/models/price.py:19 +msgid "The product type that this price belongs to" +msgstr "" + +#: open_producten/producttypes/models/price.py:25 +msgid "The date at which this price is valid" +msgstr "" + +#: open_producten/producttypes/models/price.py:29 +#: open_producten/producttypes/models/price.py:40 +#: open_producten/producttypes/models/price.py:46 +msgid "Price" +msgstr "" + +#: open_producten/producttypes/models/price.py:30 +msgid "Prices" +msgstr "" + +#: open_producten/producttypes/models/price.py:43 +msgid "The price this option belongs to" +msgstr "" + +#: open_producten/producttypes/models/price.py:50 +msgid "The amount of the price option" +msgstr "" + +#: open_producten/producttypes/models/price.py:55 +msgid "Short description of the option" +msgstr "" + +#: open_producten/producttypes/models/price.py:59 +msgid "Price option" +msgstr "" + +#: open_producten/producttypes/models/price.py:60 +msgid "Price options" +msgstr "" + +#: open_producten/producttypes/models/producttype.py:31 +msgid "Name of the product type" +msgstr "" + +#: open_producten/producttypes/models/producttype.py:35 +msgid "Summary" +msgstr "" + +#: open_producten/producttypes/models/producttype.py:39 +msgid "Short description of the product type, limited to 300 characters." +msgstr "" + +#: open_producten/producttypes/models/producttype.py:47 +msgid "Icon of the product type" +msgstr "" + +#: open_producten/producttypes/models/producttype.py:54 +msgid "Main image of the product type" +msgstr "" + +#: open_producten/producttypes/models/producttype.py:58 +msgid "Form link" +msgstr "" + +#: open_producten/producttypes/models/producttype.py:61 +msgid "Action link to request the product type." +msgstr "" + +#: open_producten/producttypes/models/producttype.py:65 +msgid "Content" +msgstr "" + +#: open_producten/producttypes/models/producttype.py:66 +msgid "Product type content with build-in WYSIWYG editor." +msgstr "" + +#: open_producten/producttypes/models/producttype.py:71 +msgid "Related product types" +msgstr "" + +#: open_producten/producttypes/models/producttype.py:73 +msgid "Related product types to this product type" +msgstr "" + +#: open_producten/producttypes/models/producttype.py:78 +msgid "Keywords" +msgstr "" + +#: open_producten/producttypes/models/producttype.py:81 +msgid "List of keywords for search" +msgstr "" + +#: open_producten/producttypes/models/producttype.py:86 +msgid "Uniform Product name" +msgstr "" + +#: open_producten/producttypes/models/producttype.py:88 +msgid "Uniform product name defined by Dutch gov" +msgstr "" + +#: open_producten/producttypes/models/producttype.py:97 +msgid "Conditions applicable for the product type" +msgstr "" + +#: open_producten/producttypes/models/producttype.py:102 +#: open_producten/producttypes/models/tag.py:43 +msgid "Tags" +msgstr "" + +#: open_producten/producttypes/models/producttype.py:105 +msgid "Tags which the product type is linked to" +msgstr "" + +#: open_producten/producttypes/models/producttype.py:113 +msgid "Categories which the product type is linked to" +msgstr "" + +#: open_producten/producttypes/models/producttype.py:122 +msgid "Organisations which provides this product" +msgstr "" + +#: open_producten/producttypes/models/producttype.py:130 +msgid "The contacts responsible for the product" +msgstr "" + +#: open_producten/producttypes/models/producttype.py:138 +msgid "Locations where the product is available at." +msgstr "" + +#: open_producten/producttypes/models/producttype.py:143 +msgid "Product types" +msgstr "" + +#: open_producten/producttypes/models/question.py:31 +msgid "Answer" +msgstr "" + +#: open_producten/producttypes/models/question.py:35 +msgid "Questions" +msgstr "" + +#: open_producten/producttypes/models/tag.py:11 +msgid "Name of the tag type" +msgstr "" + +#: open_producten/producttypes/models/tag.py:16 +msgid "Tag type" +msgstr "" + +#: open_producten/producttypes/models/tag.py:17 +msgid "Tag types" +msgstr "" + +#: open_producten/producttypes/models/tag.py:25 +msgid "Name of the tag" +msgstr "" + +#: open_producten/producttypes/models/tag.py:31 +msgid "Icon of the tag" +msgstr "" + +#: open_producten/producttypes/models/tag.py:38 +msgid "The related tag type" +msgstr "" + +#: open_producten/producttypes/models/tag.py:42 +msgid "Tag" +msgstr "" + +#: open_producten/producttypes/models/upn.py:11 +#: open_producten/producttypes/models/upn.py:27 +msgid "Uniform product name" +msgstr "" + +#: open_producten/producttypes/models/upn.py:16 +msgid "Uri" +msgstr "" + +#: open_producten/producttypes/models/upn.py:17 +msgid "Uri to the upn definition." +msgstr "" + +#: open_producten/producttypes/models/upn.py:21 +msgid "Is deleted" +msgstr "" + +#: open_producten/producttypes/models/upn.py:22 +msgid "defines if the upn definition is deleted." +msgstr "" + +#: open_producten/producttypes/models/upn.py:28 +msgid "Uniform product names" +msgstr "" + +#: open_producten/templates/admin/base_site.html:5 +#: open_producten/templates/admin/base_site.html:21 msgid "Administration" msgstr "Beheer" -#: foobar/templates/admin/base_site.html:41 -msgid "View 2fa profile" -msgstr "Tweestapsauthenticatie beheren" +#: open_producten/templates/admin/base_site.html:32 +msgid "View site" +msgstr "" -#: foobar/templates/admin/base_site.html:44 +#: open_producten/templates/admin/base_site.html:38 +msgid "Account security" +msgstr "" + +#: open_producten/templates/admin/base_site.html:41 msgid "Change password" msgstr "Wachtwoord wijzigen" -#: foobar/templates/admin/base_site.html:46 +#: open_producten/templates/admin/base_site.html:43 msgid "Log out" msgstr "Afmelden" -#: foobar/templates/admin/base_site__admintools.html:4 -msgid "admin" -msgstr "beheer" - -#: foobar/templates/admin/base_site__admintools.html:17 -msgid "administration" -msgstr "beheer" +#: open_producten/templates/maykin_2fa/login.html:18 +msgid "Contact support" +msgstr "" -#: foobar/templates/samples/pager.html:8 foobar/templates/samples/pager.html:10 +#: open_producten/templates/samples/pager.html:8 +#: open_producten/templates/samples/pager.html:10 msgid "Previous" msgstr "Vorige" -#: foobar/templates/samples/pager.html:16 -#: foobar/templates/samples/pager.html:18 +#: open_producten/templates/samples/pager.html:16 +#: open_producten/templates/samples/pager.html:18 msgid "Next" msgstr "Volgende" -#: foobar/utils/management/commands/clear_cache.py:10 +#: open_producten/utils/management/commands/clear_cache.py:10 msgid "Clear given cache only" msgstr "Leeg alleen de opgegeven cache" -#: foobar/utils/validators.py:22 +#: open_producten/utils/models.py:16 +msgid "Published" +msgstr "" + +#: open_producten/utils/models.py:18 +msgid "Whether the object is accessible through the API." +msgstr "" + +#: open_producten/utils/models.py:21 +msgid "Created on" +msgstr "" + +#: open_producten/utils/models.py:23 +msgid "The datetime at which the object was created." +msgstr "" + +#: open_producten/utils/models.py:26 +#, fuzzy +#| msgid "date joined" +msgid "Updated on" +msgstr "datum toegetreden" + +#: open_producten/utils/models.py:28 +msgid "The datetime at which the object was last changed." +msgstr "" + +#: open_producten/utils/templates/utils/includes/version_info.html:4 +#, python-format +msgid "version %(RELEASE)s" +msgstr "" + +#: open_producten/utils/templates/utils/includes/version_info.html:8 +#, python-format +msgid "GIT SHA: %(GIT_SHA)s" +msgstr "" + +#: open_producten/utils/tests/test_validators.py:24 +#: open_producten/utils/validators.py:23 #, python-format msgid "The provided value contains an invalid character: %s" msgstr "Uw invoer bevat een ongeldig teken: %s" -#: foobar/utils/validators.py:30 +#: open_producten/utils/tests/test_validators.py:61 +#: open_producten/utils/validators.py:52 +msgid "Invalid postal code." +msgstr "Ongeldige postcode." + +#: open_producten/utils/tests/test_validators.py:85 +#: open_producten/utils/validators.py:32 msgid "Invalid mobile phonenumber." msgstr "Ongeldig mobiel telefoonnumer." -#: foobar/utils/validators.py:50 -msgid "Invalid postal code." -msgstr "Ongeldige postcode." +#~ msgid "View 2fa profile" +#~ msgstr "Tweestapsauthenticatie beheren" + +#~ msgid "admin" +#~ msgstr "beheer" diff --git a/src/open_producten/fixtures/default_admin_index.json b/src/open_producten/fixtures/default_admin_index.json index ec18745..f554b26 100644 --- a/src/open_producten/fixtures/default_admin_index.json +++ b/src/open_producten/fixtures/default_admin_index.json @@ -147,6 +147,10 @@ [ "admin_index", "contenttypeproxy" + ], + [ + "openformsclient", + "configuration" ] ] } diff --git a/src/open_producten/locations/admin/location.py b/src/open_producten/locations/admin/location.py index c5d7f8b..9a3a620 100644 --- a/src/open_producten/locations/admin/location.py +++ b/src/open_producten/locations/admin/location.py @@ -1,11 +1,12 @@ from django.contrib.gis import admin +from open_producten.utils.geocode import GeoAdminMixin + from ..models import Location @admin.register(Location) -class LocationAdmin(admin.GISModelAdmin): +class LocationAdmin(GeoAdminMixin, admin.GISModelAdmin): list_display = ("name", "city", "postcode", "street", "house_number") list_filter = ("city",) search_fields = ("name", "city") - readonly_fields = ["coordinates"] diff --git a/src/open_producten/locations/admin/organisation.py b/src/open_producten/locations/admin/organisation.py index d2b5c32..c2bdd2c 100644 --- a/src/open_producten/locations/admin/organisation.py +++ b/src/open_producten/locations/admin/organisation.py @@ -1,17 +1,17 @@ from django.contrib.gis import admin from django.utils.translation import gettext_lazy as _ +from open_producten.utils.geocode import GeoAdminMixin + from ..models import Neighbourhood, Organisation, OrganisationType @admin.register(Organisation) -class OrganisationAdmin(admin.GISModelAdmin): +class OrganisationAdmin(GeoAdminMixin, admin.GISModelAdmin): list_display = ("name", "type") list_filter = ("type__name", "city") search_fields = ("name",) ordering = ("name",) - readonly_fields = ["coordinates"] - modifiable = False fieldsets = ( ( diff --git a/src/open_producten/locations/serializers/location.py b/src/open_producten/locations/serializers/location.py index 79cc721..a2f4586 100644 --- a/src/open_producten/locations/serializers/location.py +++ b/src/open_producten/locations/serializers/location.py @@ -75,9 +75,8 @@ def update(self, instance, validated_data: dict) -> Location: class ContactSerializer(serializers.ModelSerializer): - organisation = OrganisationSerializer(read_only=True) organisation_id = serializers.PrimaryKeyRelatedField( - write_only=True, queryset=Organisation.objects.all(), source="organisation" + queryset=Organisation.objects.all(), source="organisation" ) class Meta: diff --git a/src/open_producten/locations/tests/test_geoadmin.py b/src/open_producten/locations/tests/test_geoadmin.py new file mode 100644 index 0000000..186e814 --- /dev/null +++ b/src/open_producten/locations/tests/test_geoadmin.py @@ -0,0 +1,35 @@ +from django.contrib.admin import AdminSite +from django.http import HttpRequest +from django.test import TestCase + +from open_producten.locations.admin import LocationAdmin +from open_producten.locations.models import Location +from open_producten.locations.tests.factories import LocationFactory + + +class TestGeoAdminMixin(TestCase): + + def setUp(self): + site = AdminSite() + self.admin = LocationAdmin(Location, site) + + def test_get_read_only_fields_when_creating_object(self): + read_only_fields = self.admin.get_readonly_fields(HttpRequest()) + self.assertIn("coordinates", read_only_fields) + + def test_get_read_only_fields_when_updating_object(self): + read_only_fields = self.admin.get_readonly_fields( + HttpRequest(), LocationFactory() + ) + self.assertNotIn("coordinates", read_only_fields) + + def test_form_when_creating_object(self): + form_class = self.admin.get_form(HttpRequest()) + form = form_class() + self.assertNotIn("coordinates", form.fields) + + def test_form_when_updating_object(self): + form_class = self.admin.get_form(HttpRequest(), LocationFactory()) + form = form_class() + self.assertIn("coordinates", form.fields) + self.assertTrue(form.fields["coordinates"].disabled) diff --git a/src/open_producten/producttypes/migrations/0008_producttype_open_forms_slug_and_more.py b/src/open_producten/producttypes/migrations/0008_producttype_open_forms_slug_and_more.py new file mode 100644 index 0000000..4988e09 --- /dev/null +++ b/src/open_producten/producttypes/migrations/0008_producttype_open_forms_slug_and_more.py @@ -0,0 +1,60 @@ +# Generated by Django 4.2.13 on 2024-10-28 15:38 + +from django.db import migrations, models +import openformsclient.models + + +class Migration(migrations.Migration): + + dependencies = [ + ("producttypes", "0007_alter_category_description_and_more"), + ] + + operations = [ + migrations.AddField( + model_name="producttype", + name="open_forms_slug", + field=openformsclient.models.OpenFormsSlugField( + blank=True, + db_index=False, + help_text="Open forms slug of the product type form.", + max_length=100, + null=True, + verbose_name="Open forms slug", + ), + ), + migrations.AlterField( + model_name="condition", + name="negative_text", + field=models.TextField( + help_text="Description how not to meet the condition", + verbose_name="Negative text", + ), + ), + migrations.AlterField( + model_name="condition", + name="positive_text", + field=models.TextField( + help_text="Description how to meet the condition", + verbose_name="Positive text", + ), + ), + migrations.AlterField( + model_name="condition", + name="question", + field=models.TextField( + help_text="Question used in the question-answer game", + verbose_name="Question", + ), + ), + migrations.AlterField( + model_name="producttype", + name="summary", + field=models.TextField( + default="", + help_text="Short description of the product type, limited to 300 characters.", + max_length=300, + verbose_name="Summary", + ), + ), + ] diff --git a/src/open_producten/producttypes/migrations/0009_alter_producttype_open_forms_slug.py b/src/open_producten/producttypes/migrations/0009_alter_producttype_open_forms_slug.py new file mode 100644 index 0000000..57edf5a --- /dev/null +++ b/src/open_producten/producttypes/migrations/0009_alter_producttype_open_forms_slug.py @@ -0,0 +1,25 @@ +# Generated by Django 4.2.13 on 2024-10-31 13:30 + +from django.db import migrations +import openformsclient.models + + +class Migration(migrations.Migration): + + dependencies = [ + ("producttypes", "0008_producttype_open_forms_slug_and_more"), + ] + + operations = [ + migrations.AlterField( + model_name="producttype", + name="open_forms_slug", + field=openformsclient.models.OpenFormsSlugField( + blank=True, + db_index=False, + help_text="Open forms slug of the product type form.", + max_length=100, + verbose_name="Open forms slug", + ), + ), + ] diff --git a/src/open_producten/producttypes/models/condition.py b/src/open_producten/producttypes/models/condition.py index a3e100c..5961c95 100644 --- a/src/open_producten/producttypes/models/condition.py +++ b/src/open_producten/producttypes/models/condition.py @@ -1,8 +1,6 @@ from django.db import models from django.utils.translation import gettext_lazy as _ -from tinymce import models as tinymce_models - from open_producten.utils.models import BaseModel @@ -12,15 +10,15 @@ class Condition(BaseModel): max_length=100, help_text=_("Short name of the condition"), ) - question = tinymce_models.HTMLField( + question = models.TextField( verbose_name=_("Question"), help_text=_("Question used in the question-answer game"), ) - positive_text = tinymce_models.HTMLField( + positive_text = models.TextField( verbose_name=_("Positive text"), help_text=_("Description how to meet the condition"), ) - negative_text = tinymce_models.HTMLField( + negative_text = models.TextField( verbose_name=_("Negative text"), help_text=_("Description how not to meet the condition"), ) diff --git a/src/open_producten/producttypes/models/producttype.py b/src/open_producten/producttypes/models/producttype.py index 2ba3a24..5a2c68c 100644 --- a/src/open_producten/producttypes/models/producttype.py +++ b/src/open_producten/producttypes/models/producttype.py @@ -4,6 +4,7 @@ from django.db import models from django.utils.translation import gettext_lazy as _ +from openformsclient.models import OpenFormsSlugField from tinymce import models as tinymce_models from open_producten.locations.models import Contact, Location, Organisation @@ -14,8 +15,6 @@ from .tag import Tag from .upn import UniformProductName -# from ...utils.models import HTMLField - class CategoryProductType(models.Model): """ @@ -31,7 +30,7 @@ class ProductType(BasePublishableModel): verbose_name=_("Name"), max_length=100, help_text=_("Name of the product type") ) - summary = tinymce_models.HTMLField( + summary = models.TextField( verbose_name=_("Summary"), default="", max_length=300, @@ -61,6 +60,12 @@ class ProductType(BasePublishableModel): help_text=_("Action link to request the product type."), ) + open_forms_slug = OpenFormsSlugField( + verbose_name=_("Open forms slug"), + blank=True, + help_text=_("Open forms slug of the product type form."), + ) + content = tinymce_models.HTMLField( verbose_name=_("Content"), help_text=_("Product type content with build-in WYSIWYG editor."), @@ -145,6 +150,14 @@ class Meta: def __str__(self): return self.name + def clean(self): + for contact in self.contacts.all(): + if ( + contact.organisation_id is not None + and not self.organisations.filter(id=contact.organisation_id).exists() + ): + self.organisations.add(contact.organisation) + @property def current_price(self): now = date.today() diff --git a/src/open_producten/producttypes/serializers/producttype.py b/src/open_producten/producttypes/serializers/producttype.py index 1480689..99affde 100644 --- a/src/open_producten/producttypes/serializers/producttype.py +++ b/src/open_producten/producttypes/serializers/producttype.py @@ -169,6 +169,7 @@ def create(self, validated_data): organisations=organisations, contacts=contacts, ) + product_type.clean() product_type.save() return product_type @@ -194,7 +195,7 @@ def update(self, instance, validated_data): organisations=organisations, contacts=contacts, ) - + instance.clean() instance.save() return instance diff --git a/src/open_producten/producttypes/tests/api/test_producttype.py b/src/open_producten/producttypes/tests/api/test_producttype.py index 9a698a6..3966fc2 100644 --- a/src/open_producten/producttypes/tests/api/test_producttype.py +++ b/src/open_producten/producttypes/tests/api/test_producttype.py @@ -215,6 +215,9 @@ def test_create_product_type_with_contact(self): [contact.first_name], ) + # contact org is added in ProductType clean + self.assertEqual(ProductType.objects.first().organisations.count(), 1) + def test_create_product_type_with_duplicate_ids_returns_error(self): tag = TagFactory.create() condition = ConditionFactory.create() @@ -379,6 +382,9 @@ def test_update_product_type_with_contact(self): [contact.first_name], ) + # contact org is added in ProductType clean + self.assertEqual(ProductType.objects.first().organisations.count(), 1) + def test_update_product_type_with_duplicate_ids_returns_error(self): product_type = ProductTypeFactory.create() tag = TagFactory.create() diff --git a/src/open_producten/producttypes/tests/test_producttype.py b/src/open_producten/producttypes/tests/test_producttype.py index e1bb23e..b7390ce 100644 --- a/src/open_producten/producttypes/tests/test_producttype.py +++ b/src/open_producten/producttypes/tests/test_producttype.py @@ -4,6 +4,7 @@ from freezegun import freeze_time +from ...locations.tests.factories import ContactFactory from .factories import PriceFactory, ProductTypeFactory @@ -28,3 +29,18 @@ def test_current_price_when_set(self): def test_current_price_without_prices(self): self.product_type = ProductTypeFactory.create() self.assertIsNone(self.product_type.current_price) + + def test_clean_with_contact_that_has_no_org(self): + contact = ContactFactory(organisation_id=None) + product_type = ProductTypeFactory.create() + product_type.contacts.add(contact) + product_type.clean() + + def test_clean_with_contact_that_has_org(self): + contact = ContactFactory() + product_type = ProductTypeFactory.create() + product_type.contacts.add(contact) + product_type.clean() + + self.assertEqual(product_type.organisations.count(), 1) + self.assertEqual(product_type.organisations.first().id, contact.organisation.id) diff --git a/src/open_producten/utils/geocode.py b/src/open_producten/utils/geocode.py index 8c911d2..3b40e56 100644 --- a/src/open_producten/utils/geocode.py +++ b/src/open_producten/utils/geocode.py @@ -1,5 +1,6 @@ import logging +from django import forms from django.conf import settings from django.contrib.gis.geos import GEOSGeometry, Point from django.utils.module_loading import import_string @@ -91,3 +92,23 @@ def geocode_address(address: str) -> Point | None: coordinates = (location.longitude, location.latitude) return Point(coordinates) + + +class GeoAdminForm(forms.ModelForm): + class Meta: + fields = "__all__" + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + if "coordinates" in self.fields: + self.fields["coordinates"].disabled = True + + +class GeoAdminMixin: + form = GeoAdminForm + + def get_readonly_fields(self, request, obj=None): + readonly_fields = super().get_readonly_fields(request, obj) + if not obj: + return readonly_fields + ("coordinates",) + return readonly_fields