diff --git a/invenio_config_tuw/config.py b/invenio_config_tuw/config.py
index 3b6f7ea0191344685ae845b86c3b814e0ef22ce4..6fc45db37e6619f32b789fc43bc858080a09995f 100644
--- a/invenio_config_tuw/config.py
+++ b/invenio_config_tuw/config.py
@@ -15,6 +15,7 @@ from invenio_oauthclient.views.client import auto_redirect_login
from .auth import TUWSSOSettingsHelper
from .forms import tuw_registration_form
from .permissions import TUWRecordPermissionPolicy, TUWRequestsPermissionPolicy
+from .schemas import TUWUserPreferencesSchema, TUWUserSchema
from .utils import check_user_email_for_tuwien, current_user_as_creator
# Invenio-Config-TUW
@@ -251,3 +252,9 @@ RECAPTCHA_PRIVATE_KEY = None
# Preferred URL scheme to use
PREFERRED_URL_SCHEME = "https"
+
+# Extended schema for user preferences
+ACCOUNTS_USER_PREFERENCES_SCHEMA = TUWUserPreferencesSchema
+
+# Extended schema for users in the users service
+USERS_RESOURCES_SERVICE_SCHEMA = TUWUserSchema
diff --git a/invenio_config_tuw/curation.py b/invenio_config_tuw/curation.py
new file mode 100644
index 0000000000000000000000000000000000000000..4d78679edb3d6df029ba2a230c5c21b4a5548fa7
--- /dev/null
+++ b/invenio_config_tuw/curation.py
@@ -0,0 +1,67 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2023 TU Wien.
+#
+# Invenio-Config-TUW is free software; you can redistribute it and/or modify
+# it under the terms of the MIT License; see LICENSE file for more details.
+
+"""Form for curation-related user settings, inspired by the notifications settings."""
+
+from flask_wtf import FlaskForm
+from wtforms import BooleanField
+
+
+class CurationPreferencesProxy:
+ """Proxy class giving direct access to a user's curation preferences."""
+
+ def __init__(self, user):
+ """Constructor."""
+ super().__setattr__("_user", user)
+
+ def __getattr__(self, attr):
+ """Get the attribute's value in the user's curation preferences."""
+ if attr == "_user":
+ return self._user
+
+ return self._user.preferences.get("curation", {}).get(attr, None)
+
+ def __setattr__(self, attr, value):
+ """Set the attribute's value in the user's curation preferences."""
+ self._user.preferences = {
+ **self._user.preferences,
+ "curation": {
+ **self._user.preferences.get("curation", {}),
+ attr: value,
+ },
+ }
+
+ def __hasattr__(self, attr):
+ """Check if the user's curation preferences have the given attribute."""
+ return attr in self._user.preferences.get("curation", {})
+
+
+class CurationForm(FlaskForm):
+ """Form for editing a user's curation preferences."""
+
+ proxy_cls = CurationPreferencesProxy
+
+ consent = BooleanField(
+ "Consent to curation of my records",
+ description=(
+ "Allow the repository team to curate the metadata of my records, by e.g. fixing typos and adding new related works as they are reported." # noqa
+ ),
+ )
+
+ def process(self, formdata=None, obj=None, data=None, extra_filters=None, **kwargs):
+ """Build a proxy around the object."""
+ if obj is not None:
+ obj = self.proxy_cls(obj)
+
+ return super().process(
+ formdata=formdata, obj=obj, data=data, extra_filters=extra_filters, **kwargs
+ )
+
+ def populate_obj(self, user):
+ """Populate the object."""
+ user = self.proxy_cls(user)
+ return super().populate_obj(user)
diff --git a/invenio_config_tuw/forms.py b/invenio_config_tuw/forms.py
index 048bef07b1bd285ddbf9bdea9c5fb3662afb2a3d..fa51cfa9307354dd10cb94061e528a5f11fd5ae0 100644
--- a/invenio_config_tuw/forms.py
+++ b/invenio_config_tuw/forms.py
@@ -32,7 +32,10 @@ def tuw_registration_form(*args, **kwargs):
# `Markup` escapes them
terms_of_use_url = "https://www.tuwien.at/index.php?eID=dms&s=4&path=Directives and Regulations of the Rectorate/Research_Data_Terms_of_Use.pdf" # noqa
message = Markup(
- f"Accept the <a href='{terms_of_use_url}' target='_blank'>Terms and Conditions</a>" # noqa
+ f"Accept the <a href='{terms_of_use_url}' target='_blank'>Terms and Conditions</a> (<strong>required</strong>)" # noqa
+ )
+ curation_consent_message = Markup(
+ "Allow the repository team to curate the metadata of my uploads after publication"
)
class UserProfileForm(Form):
@@ -50,6 +53,8 @@ def tuw_registration_form(*args, **kwargs):
class UserRegistrationForm(_security.confirm_register_form):
"""Form for the basic user information."""
+ # the curation consent is listed here instead of in a sub-form,
+ # because that caused very quirky rendering
email = HiddenField()
username = HiddenField()
user_profile = FormField(UserProfileForm, separator=".")
@@ -58,6 +63,7 @@ def tuw_registration_form(*args, **kwargs):
recaptcha = None
profile = None # disable the default 'profile' form from invenio
submit = None # defined in the template
+ curation_consent = BooleanField(curation_consent_message, default="checked")
terms_of_use = BooleanField(message, [validators.required()])
def to_dict(self):
@@ -73,6 +79,9 @@ def tuw_registration_form(*args, **kwargs):
"preferences": {
"visibility": self.preferences.visibility.data,
"email_visibility": self.preferences.email_visibility.data,
+ "curation": {
+ "consent": self.curation_consent.data,
+ },
},
}
diff --git a/invenio_config_tuw/schemas.py b/invenio_config_tuw/schemas.py
new file mode 100644
index 0000000000000000000000000000000000000000..972b4d6b9aa50c537f1c135da36a431abb3e7166
--- /dev/null
+++ b/invenio_config_tuw/schemas.py
@@ -0,0 +1,39 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2023 TU Wien.
+#
+# Invenio-Config-TUW is free software; you can redistribute it and/or modify
+# it under the terms of the MIT License; see LICENSE file for more details.
+
+"""Various schemas to use in InvenioRDM."""
+
+try:
+ from invenio_app_rdm.users.schemas import NotificationsUserSchema as UserSchema
+ from invenio_app_rdm.users.schemas import (
+ UserPreferencesNotificationsSchema as UserPreferencesSchema,
+ )
+except ImportError:
+ from invenio_users_resources.services.schemas import (
+ UserSchema,
+ UserPreferencesSchema,
+ )
+
+from marshmallow import Schema, fields
+
+
+class UserPreferencesCurationSchema(Schema):
+ """Schema for curation preferences."""
+
+ consent = fields.Boolean(default=False)
+
+
+class TUWUserPreferencesSchema(UserPreferencesSchema):
+ """User preferences schema with TU Wien extensions."""
+
+ curation = fields.Nested(UserPreferencesCurationSchema)
+
+
+class TUWUserSchema(UserSchema):
+ """User schema with TU Wien extensions."""
+
+ preferences = fields.Nested(TUWUserPreferencesSchema)
diff --git a/invenio_config_tuw/startup.py b/invenio_config_tuw/startup.py
index 0f094d6157c5f9b69f87dc5a16c330b12531c72a..41d95b1c3910efbed9ba1a5ed690ebe777389533 100644
--- a/invenio_config_tuw/startup.py
+++ b/invenio_config_tuw/startup.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
#
-# Copyright (C) 2022 TU Wien.
+# Copyright (C) 2022-2023 TU Wien.
#
# Invenio-Config-TUW is free software; you can redistribute it and/or modify
# it under the terms of the MIT License; see LICENSE file for more details.
@@ -16,8 +16,13 @@ initialized, and thus we can rely on them being already available.
from logging import ERROR
from logging.handlers import SMTPHandler
-from flask import Blueprint
+from flask import Blueprint, current_app, flash, render_template, request
+from flask_breadcrumbs import register_breadcrumb
+from flask_login import current_user, login_required
+from flask_menu import register_menu
+from invenio_db import db
+from .curation import CurationForm
from .formatters import CustomFormatter
from .permissions import TUWCommunitiesPermissionPolicy
@@ -100,3 +105,34 @@ def override_communities_permissions(state):
svc.config.permission_policy_cls = TUWCommunitiesPermissionPolicy
svc.files.config.permission_policy_cls = TUWCommunitiesPermissionPolicy
svc.members.config.permission_policy_cls = TUWCommunitiesPermissionPolicy
+
+
+@blueprint.route("/account/settings/curation/", methods=["GET", "POST"])
+@login_required
+@register_menu(
+ blueprint,
+ "settings.curation",
+ '<i class="file icon"></i> Curation',
+ order=1,
+)
+@register_breadcrumb(blueprint, "breadcrumbs.settings.curation", ("Curation"))
+def curation_settings_view():
+ preferences_curation_form = CurationForm(
+ formdata=None, obj=current_user, prefix="preferences-curation"
+ )
+
+ form_name = request.form.get("submit", None)
+ form = preferences_curation_form if form_name else None
+
+ if form:
+ form.process(formdata=request.form)
+ if form.validate_on_submit():
+ form.populate_obj(current_user)
+ db.session.add(current_user)
+ current_app.extensions["security"].datastore.commit()
+ flash(("Curation settings were updated."), category="success")
+
+ return render_template(
+ "invenio_theme_tuw/settings/curation.html",
+ preferences_curation_form=preferences_curation_form,
+ )