Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
61 changes: 51 additions & 10 deletions doajtest/testbook/flagged_journals/flagged_journals.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,23 @@ tests:
steps:
- step: Navigate to /testdrive/flags
- step: Login as an admin
- step: Open Feline Aerodynamics Review application (available on your dashboard)
- step: Click on the Journals icon
- step: Click Edit This Journal on the Feline Aerodynamics Review Journal (second journal in the list)
results:
- Above the notes there is the Add Flag button visible and active
- step: Click Add Flag button
results:
- An empty flag form is displayed
- step: In assigned_to input attempt to add editor's id (you can find it in your testdrive/flags data)
- step: In assigned_to input attempt to add editor's username (you can find it in your testdrive/flags data)
results:
- No matches found is displayed and it is not possible to assign the editor
- step: In assigned_to input add random_user's id (you can find it in your testdrive/flags data)
- step: In assigned_to input add random_user's username (you can find it in your testdrive/flags data)
results:
- Id is found and it can be selected
- step: Select the random_user's id
- Username is found and it can be selected
- step: Select the random_user's username
- step: In deadline input add an improbable date (e.g., 2025-02-31)
results:
- It's not possible to enter an improbable date
- It's not possible to enter a date in the past
- step: In deadline input add a valid date
- step: In text area add flag's note (any text)
- step: Save application
Expand All @@ -33,12 +34,12 @@ tests:
- All fields are editable
- Add Flag button is disabled
- step: Close the application with Unlock & Close
- step: Open The Bermuda Triangle Journal of Lost and Found record (available on your dashboard)
- step: Click Edit This Record for The Bermuda Triangle Journal of Lost and Found record (first journal on the Journals page)
results:
- The record has a flag assigned to another user, with no deadline and with a note.
- All flag fields are editable
- Add Flag button is disabled
- step: Add a valid deadline to a flag (e.g., 2025-04-01)
- step: Add a valid deadline to a flag (e.g., 2026-06-01)
- step: Save the record
results:
- The flag is correctly saved with a new deadline
Expand Down Expand Up @@ -66,7 +67,7 @@ tests:
- The new flag is saved and displayed
- The old flag was converted to a note with text This flag was resolved on <i>date</i> by <your id>; and flag's note
- step: Click Unlock & Close to close the application
- step: Open Journal of Intergalactic Diplomacy record
- step: Open Journal of Intergalactic Diplomacy record (last record on the first page of Journals)
results:
- This record has a flag assigned to you
- In the flag form, in the Assign a User input, there is your id with a red flag icon
Expand Down Expand Up @@ -137,4 +138,44 @@ tests:
- step: Navigate to /editor/your_applications
results:
- There is no information about any flags
- Only Flagged Records and Flagged to you" facets are **not** displayed
- Only Flagged Records and Flagged to you" facets are **not** displayed

- title: Flag Validation
setup:
- Open any journal form without a flag
context:
role: admin
steps:
- step: Click "Add flag"
- step: Enter a note only (leave assignee and deadline empty)
- step: Click "Save"
results:
- An error message is displayed under the assignee input
- An error message is displayed under the deadline input
- step: Enter a deadline
- step: Click "Save"
results:
- The error under the deadline input is cleared
- The error under the assignee input is still displayed
- step: Enter an assignee
- step: Click "Save"
results:
- The flag is saved successfully
- The flag is displayed correctly with note, assignee, and deadline
- step: Edit the flag and remove the deadline value
- step: Click "Resolve flag"
results:
- The flag is marked as resolved
- step: Click "Add flag"
- step: Enter a note only (leave assignee and deadline empty)
- step: Click "Save"
results:
- An error message is displayed under the assignee input of the new flag
- An error message is displayed under the deadline input of the new flag
- No validation errors are shown on the resolved flag fields
- step: Enter a deadline and an assignee for the new flag
- step: Click "Save"
results:
- The resolved flag is converted to a note
- The new flag is saved successfully
- The new flag is displayed with note, assignee, and deadline
63 changes: 53 additions & 10 deletions portality/forms/application_forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
Year,
CurrentISOCurrency,
CurrentISOLanguage,
DateInThePast
DateInThePast, StopValidationOnOtherValue
)
from portality.lib import dates
from portality.lib.formulaic import Formulaic, WTFormsBuilder, FormulaicContext, FormulaicField
Expand Down Expand Up @@ -2026,11 +2026,20 @@ class FieldDefinitions:

FLAG_DEADLINE = {
"subfield": True,
"optional": True,
"label": "Deadline",
"name": "flag_deadline",
"validate": [
{"bigenddate": {"message": "This must be a valid date in the BigEnd format (YYYY-MM-DD)"}}
{"bigenddate": {"message": "This must be a valid date in the BigEnd format (YYYY-MM-DD)", "ignore_empty": True}},
{"stop_validation_on_other_value": {
"field": "flag_resolved",
"value": "true"
}},
{"required_if": {
"field": "flag_note",
"not_empty": True,
"message": "The flag must have a deadline",
"skip_disabled": True
}}
],
"help": {
"placeholder": "deadline (YYYY-MM-DD)",
Expand Down Expand Up @@ -2061,13 +2070,22 @@ class FieldDefinitions:
"name": "flag_assignee",
"label": "Assign a user",
"help": {
"placeholder": "assigned_to",
"short_help": "A Flag must be assigned to a user. The Flag not assigned to a user will be automatically converted to a note",
"placeholder": "assigned_to"
},
"group": "flags",
"validate": [
"reserved_usernames",
"owner_exists"
"owner_exists",
{"stop_validation_on_other_value": {
"field": "flag_resolved",
"value": "true"
}},
{"required_if": {
"field": "flag_note",
"not_empty": True,
"message": "The flag must be assigned to someone",
"skip_disabled": True
}}
],
"widgets": [
{"autocomplete": {"type": "admin", "include": False, "allow_clear_input": False}},
Expand Down Expand Up @@ -3069,12 +3087,20 @@ class RequiredIfBuilder:
# ~~->$ RequiredIf:FormValidator~~
@staticmethod
def render(settings, html_attrs):
val = settings.get("value")
val = settings.get("value", "")
if isinstance(val, list):
val = ",".join(val)

if settings.get("skip_disabled"):
html_attrs["data-parsley-validate-if-disabled"] = "false"

html_attrs["data-parsley-validate-if-empty"] = "true"
html_attrs["data-parsley-required-if"] = val

ne = settings.get("not_empty", False)
if ne:
html_attrs["data-parsley-required-if-not-empty"] = "true"

html_attrs["data-parsley-required-if-field"] = settings.get("field")
if "message" in settings:
html_attrs["data-parsley-required-if-message"] = "<p><small>" + settings["message"] + "</small></p>"
Expand All @@ -3083,8 +3109,21 @@ def render(settings, html_attrs):

@staticmethod
def wtforms(field, settings):
return RequiredIfOtherValue(settings.get("field") or field, settings.get("value"), settings.get("message"))
set_field = settings.get("field", field)
val = settings.get("value")
ne = settings.get("not_empty", False)
return RequiredIfOtherValue(set_field, val, ne, settings.get("message"))

class StopValidationOnOtherValueBuilder:
# ~~->$ StopValidationOnOtherValue:FormValidator~~
@staticmethod
def render(settings, html_attrs):
# no action required here, this is back-end only
return

@staticmethod
def wtforms(field, settings):
return StopValidationOnOtherValue(settings.get("field", field), settings.get("value"))

class OnlyIfBuilder:
# ~~->$ OnlyIf:FormValidator~~
Expand Down Expand Up @@ -3160,6 +3199,8 @@ class BigEndDateBuilder:
@staticmethod
def render(settings, html_attrs):
html_attrs["data-parsley-validdate"] = ""
ignore_empty = settings.get("ignore_empty", False)
html_attrs["data-parsley-validdate-ignore_empty"] = "true" if ignore_empty else "false"
html_attrs["data-parsley-pattern-message"] = settings.get("message")

@staticmethod
Expand Down Expand Up @@ -3249,7 +3290,8 @@ def wtforms(field, settings):
"bigenddate": BigEndDateBuilder.render,
"no_script_tag": NoScriptTagBuilder.render,
"year": YearBuilder.render,
"date_in_the_past": DateInThePastBuilder.render
"date_in_the_past": DateInThePastBuilder.render,
"stop_validation_on_other_value": StopValidationOnOtherValueBuilder.render,
},
"wtforms": {
"required": RequiredBuilder.wtforms,
Expand All @@ -3276,7 +3318,8 @@ def wtforms(field, settings):
"year": YearBuilder.wtforms,
"current_iso_currency": CurrentISOCurrencyBuilder.wtforms,
"current_iso_language": CurrentISOLanguageBuilder.wtforms,
"date_in_the_past": DateInThePastBuilder.wtforms
"date_in_the_past": DateInThePastBuilder.wtforms,
"stop_validation_on_other_value": StopValidationOnOtherValueBuilder.wtforms
}
}
}
Expand Down
53 changes: 52 additions & 1 deletion portality/forms/validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -360,8 +360,9 @@ class RequiredIfOtherValue(MultiFieldValidator):
~~RequiredIfOtherValue:FormValidator~~
"""

def __init__(self, other_field_name, other_value, message=None, *args, **kwargs):
def __init__(self, other_field_name, other_value, not_empty=False, message=None, *args, **kwargs):
self.other_value = other_value
self.not_empty = not_empty
self.message = message if message is not None else "This field is required when {x} is {y}".format(x=other_field_name, y=other_value)
super(RequiredIfOtherValue, self).__init__(other_field_name, *args, **kwargs)

Expand All @@ -372,6 +373,18 @@ def __call__(self, form, field):
except:
return

if self.not_empty:
if isinstance(other_field.data, list):
vals = [v for v in other_field.data if v]
if len(vals) > 0:
dr = validators.DataRequired(self.message)
dr(form, field)
else:
if other_field.data:
dr = validators.DataRequired(self.message)
dr(form, field)
return

if isinstance(self.other_value, list):
self._match_list(form, field, other_field)
else:
Expand Down Expand Up @@ -401,6 +414,44 @@ def _match_list(self, form, field, other_field):
if not field.data or len(field.data) == 0:
raise validators.StopValidation()

class StopValidationOnOtherValue(MultiFieldValidator):
def __init__(self, other_field_name, other_value, *args, **kwargs):
self.other_value = other_value
super(StopValidationOnOtherValue, self).__init__(other_field_name, *args, **kwargs)

def __call__(self, form, field):
# attempt to get the other field - if it doesn't exist, just take this as valid
try:
other_field = self.get_other_field(self.other_field_name, form)
except:
return

if isinstance(self.other_value, list):
self._match_list(form, field, other_field)
else:
self._match_single(form, field, other_field)

def _match_single(self, form, field, other_field):
if isinstance(other_field.data, list):
match = self.other_value in other_field.data
else:
match = other_field.data == self.other_value
if match:
raise validators.StopValidation()
else:
if not field.data or (isinstance(field.data, str) and not field.data.strip()):
raise validators.StopValidation()

def _match_list(self, form, field, other_field):
if isinstance(other_field.data, list):
match = len(list(set(self.other_value) & set(other_field.data))) > 0
else:
match = other_field.data in self.other_value
if match:
raise validators.StopValidation()
else:
if not field.data or len(field.data) == 0:
raise validators.StopValidation()

class OnlyIf(MultiFieldValidator):
"""
Expand Down
Loading
Loading