Coverage for portality / forms / application_forms.py: 81%
681 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-05-05 00:09 +0100
« prev ^ index » next coverage.py v7.13.5, created at 2026-05-05 00:09 +0100
1"""
2~~ApplicationForm:Feature~~
3"""
4from copy import deepcopy
6from wtforms import StringField, TextAreaField, IntegerField, BooleanField, SelectMultipleField, \
7 SelectField, \
8 FormField, FieldList, HiddenField, DateField
9from wtforms import widgets, validators
10from wtforms.widgets.core import html_params, HTMLString
12from flask_babel import lazy_gettext
14from portality import constants
15from portality import regex
16from portality.core import app
17from portality.crosswalks.application_form import ApplicationFormXWalk
18from portality.crosswalks.journal_form import JournalFormXWalk
19from portality.datasets import language_options, country_options, currency_options
20from portality.forms import application_processors
21from portality.forms.fields import TagListField, NestedFormField, UnconstrainedRadioField
22from portality.forms.validate import (
23 HTTPURL,
24 OptionalIf,
25 MaxLen,
26 RegexpOnTagList,
27 StopWords,
28 ISSNInPublicDOAJ,
29 JournalURLInPublicDOAJ,
30 DifferentTo,
31 RequiredIfOtherValue,
32 OnlyIf,
33 OnlyIfExists,
34 NotIf,
35 GroupMember,
36 RequiredValue,
37 BigEndDate,
38 ReservedUsernames,
39 CustomRequired,
40 OwnerExists,
41 NoScriptTag,
42 Year,
43 CurrentISOCurrency,
44 CurrentISOLanguage,
45 DateInThePast
46)
47from portality.lib import dates
48from portality.lib.formulaic import Formulaic, WTFormsBuilder, FormulaicContext, FormulaicField
49from portality.models import EditorGroup
50from portality.regex import ISSN, ISSN_COMPILED
51from portality.ui.messages import Messages
52from portality.ui import templates
54# Stop words used in the keywords field
55STOP_WORDS = [
56 "open access",
57 "high quality",
58 "peer-reviewed",
59 "peer-review",
60 "peer review",
61 "peer reviewed",
62 "quality",
63 "medical journal",
64 "multidisciplinary",
65 "multi-disciplinary",
66 "multi-disciplinary journal",
67 "interdisciplinary",
68 "inter disciplinary",
69 "inter disciplinary research",
70 "international journal",
71 "journal",
72 "scholarly journal",
73 "open science",
74 "impact factor",
75 "scholarly",
76 "research",
77 "research journal"
78]
81########################################################
82# Define all our individual fields
83########################################################
85class FieldDefinitions:
86 # ~~->$ BOAI:FormField~~
87 BOAI = {
88 "name": "boai",
89 "label": lazy_gettext("Does the journal adhere to DOAJ’s definition of open access?"),
90 "input": "radio",
91 "options": [
92 {"display": lazy_gettext("Yes"), "value": "y"},
93 {"display": lazy_gettext("No"), "value": "n"}
94 ],
95 "help": {
96 "long_help": [lazy_gettext("See <a href='https://blog.doaj.org/2020/11/17/"
97 "what-does-doaj-define-as-open-access/' "
98 "target='_blank' rel='noopener'>"
99 "DOAJ’s definition of open access explained "
100 "in full</a>.")],
101 "doaj_criteria": lazy_gettext("You must answer 'Yes'")
102 },
103 "validate": [
104 {"required": {"message": lazy_gettext("You must answer <strong>Yes</strong> to continue")}},
105 {"required_value": {"value": "y"}}
106 ],
107 "contexts": {
108 "admin": {
109 "validate": []
110 },
111 "editor": {
112 "validate": [],
113 "disabled": True
114 },
115 "associate_editor": {
116 "validate": [],
117 "disabled": True
118 }
119 }
120 }
122 # ~~->$ OAStatementURL:FormField~~
123 OA_STATEMENT_URL = {
124 "name": "oa_statement_url",
125 "label": lazy_gettext("The journal website must display its open access statement. Where can we find this information?"),
126 "input": "text",
127 "help": {
128 "long_help": [lazy_gettext("Here is an example of a suitable Open Access "
129 "statement that meets our criteria: <blockquote>This"
130 " is an open access journal, which means that all "
131 "content is freely available without charge to the "
132 "user or his/her institution. Users are allowed to "
133 "read, download, copy, distribute, print, search, or"
134 " link to the full texts of the articles, or use "
135 "them for any other lawful purpose, without asking "
136 "prior permission from the publisher or the author. "
137 "This is in accordance with the BOAI definition of "
138 "open access.</blockquote>")],
139 "short_help": lazy_gettext("Link to the journal’s open access statement"),
140 "placeholder": "https://www.my-journal.com/open-access"
141 },
142 "validate": [
143 {"required": {"message": lazy_gettext("Enter the URL for the journal’s Open Access statement page")}},
144 "is_url" # ~~^->IsURL:FormValidator~~
145 ],
146 "widgets": [
147 "trim_whitespace", # ~~^-> TrimWhitespace:FormWidget~~
148 "clickable_url" # ~~^-> ClickableURL:FormWidget~~
149 ],
150 "attr": {
151 "type": "url"
152 }
153 }
155 # ~~->$ Title:FormField~~
156 TITLE = {
157 "name": "title",
158 "label": lazy_gettext("Journal title"),
159 "input": "text",
160 "help": {
161 "long_help": [lazy_gettext("The journal title must match what is displayed on the website and what is registered at the "
162 "<a href='https://portal.issn.org/' target='_blank' rel='noopener'> ISSN Portal</a>."),
163 lazy_gettext("For translated titles, you may add the "
164 "translation as an alternative title.")],
165 "placeholder": lazy_gettext("Journal title"),
166 "doaj_criteria": lazy_gettext("Title in application form, title at ISSN and website must all match")
167 },
168 "validate": [
169 {"required": {"message": lazy_gettext("Enter the journal’s name")}},
170 "no_script_tag" # ~~^-> NoScriptTag:FormValidator
171 ],
172 "widgets": [
173 "trim_whitespace", # ~~^-> TrimWhitespace:FormWidget~~
174 "full_contents" # ~~^->FullContents:FormWidget~~
175 ],
176 "contexts": {
177 "admin": {
178 "widgets": [
179 "trim_whitespace", # ~~^-> TrimWhitespace:FormWidget~~
180 "click_to_copy", # ~~^-> ClickToCopy:FormWidget~~
181 ]
182 },
183 "editor": {
184 "disabled": True,
185 "widgets": [
186 "trim_whitespace", # ~~^-> TrimWhitespace:FormWidget~~
187 "click_to_copy", # ~~^-> ClickToCopy:FormWidget~~
188 ]
189 },
190 "associate_editor": {
191 "disabled": True,
192 "widgets": [
193 "trim_whitespace", # ~~^-> TrimWhitespace:FormWidget~~
194 "click_to_copy", # ~~^-> ClickToCopy:FormWidget~~
195 ]
196 },
197 "update_request": {
198 "disabled": True
199 }
200 }
201 }
203 # ~~->$ AlternativeTitle:FormField~~
204 ALTERNATIVE_TITLE = {
205 "name": "alternative_title",
206 "label": lazy_gettext("Alternative title (including translation of the title)"),
207 "input": "text",
208 "optional": True,
209 "help": {
210 "placeholder": "Mi revista"
211 },
212 "validate": [
213 "no_script_tag" # ~~^-> NoScriptTag:FormValidator
214 ],
215 "widgets": [
216 "trim_whitespace", # ~~^-> TrimWhitespace:FormWidget~~
217 {"full_contents": {"empty_disabled": "[The journal has no alternative title]"}}
218 # ~~^->FullContents:FormWidget~~
219 ],
220 "contexts": {
221 "update_request": {
222 "disabled": True
223 },
224 "admin": {
225 "widgets": [
226 "trim_whitespace", # ~~^-> TrimWhitespace:FormWidget~~
227 "click_to_copy", # ~~^-> ClickToCopy:FormWidget~~
228 ]
229 },
230 "associate_editor": {
231 "widgets": [
232 "trim_whitespace", # ~~^-> TrimWhitespace:FormWidget~~
233 "click_to_copy", # ~~^-> ClickToCopy:FormWidget~~
234 ]
235 },
236 "editor": {
237 "widgets": [
238 "trim_whitespace", # ~~^-> TrimWhitespace:FormWidget~~
239 "click_to_copy", # ~~^-> ClickToCopy:FormWidget~~
240 ]
241 }
242 }
243 }
245 # ~~->$ JournalURL:FormField~~
246 JOURNAL_URL = {
247 "name": "journal_url",
248 "label": lazy_gettext("Link to the journal’s homepage"),
249 "input": "text",
250 "validate": [
251 "required",
252 "is_url" # ~~^->IsURL:FormValidator~~
253 ],
254 "widgets": [
255 "trim_whitespace", # ~~^-> TrimWhitespace:FormWidget~~
256 "clickable_url" # ~~^-> ClickableURL:FormWidget~~
257 ],
258 "help": {
259 "placeholder": "https://www.my-journal.com"
260 },
261 "contexts": {
262 "public": {
263 "validate": [
264 {"required": {"message": lazy_gettext("Enter the URL for the journal’s <strong>homepage</strong>")}},
265 "is_url", # ~~^->IsURL:FormValidator~~
266 "journal_url_in_public_doaj" # ~~^-> JournalURLInPublicDOAJ:FormValidator~~
267 ],
268 }
269 }
270 }
272 # ~~->$ PISSN:FormField~~
273 PISSN = {
274 "name": "pissn",
275 "label": lazy_gettext("ISSN (print)"),
276 "input": "text",
277 "help": {
278 "long_help": [lazy_gettext("Must be a valid ISSN, fully registered and confirmed at the "
279 "<a href='https://portal.issn.org/' target='_blank' rel='noopener'> ISSN Portal</a>."),
280 lazy_gettext("Use the link under the ISSN you provided to check it."),
281 lazy_gettext("The ISSN must match what is given on the journal website.")],
282 "short_help": lazy_gettext("For example, 2049-3630"),
283 "doaj_criteria": lazy_gettext("ISSN must be provided")
284 },
285 "validate": [
286 {"optional_if": {"field": "eissn", # ~~^-> OptionalIf:FormValidator~~
287 "message": lazy_gettext("You must provide <strong>one or both</strong> an online ISSN or a print ISSN")}},
288 {"is_issn": {"message": lazy_gettext("This is not a valid ISSN")}}, # ~~^-> IsISSN:FormValidator~~
289 {"different_to": {"field": "eissn", "message": lazy_gettext("This field must contain a different value to 'ISSN ("
290 "online)'")}} # ~~^-> DifferetTo:FormValidator~~
291 ],
292 "widgets": [
293 "trim_whitespace", # ~~^-> TrimWhitespace:FormWidget~~
294 "full_contents", # ~~^->FullContents:FormWidget~~
295 "issn_link" # ~~^->IssnLink:FormWidget~~
296 ],
297 "contexts": {
298 "public": {
299 "validate": [
300 {"optional_if": {"field": "eissn", # ~~^-> OptionalIf:FormValidator~~
301 "message": lazy_gettext("You must provide <strong>one or both</strong> an online ISSN or a print ISSN")}},
302 {"is_issn": {"message": lazy_gettext("This is not a valid ISSN")}}, # ~~^-> IsISSN:FormValidator~~
303 {"different_to": {"field": "eissn",
304 "message": lazy_gettext("This field must contain a different value to 'ISSN ("
305 "online)'")}}, # ~~^-> DifferetTo:FormValidator~~
306 "issn_in_public_doaj"
307 ],
308 },
309 "admin": {
310 "help": {
311 "long_help": [lazy_gettext("Must be a valid ISSN, fully registered and confirmed at the "
312 "<a href='https://portal.issn.org/' target='_blank' rel='noopener'> ISSN Portal</a>."),
313 lazy_gettext("The ISSN must match what is given on the journal website.")],
314 "placeholder": "",
315 "doaj_criteria": lazy_gettext("ISSN must be provided")
316 },
317 "widgets": [
318 "trim_whitespace", # ~~^-> TrimWhitespace:FormWidget~~
319 "autocheck", # ~~^-> Autocheck:FormWidget~~
320 "issn_link" # ~~^->IssnLink:FormWidget~~
321 ]
322 },
323 "editor": {
324 "disabled": True,
325 "help": {
326 "long_help": [lazy_gettext("Must be a valid ISSN, fully registered and confirmed at the "
327 "<a href='https://portal.issn.org/' target='_blank' rel='noopener'> ISSN Portal</a>."),
328 lazy_gettext("The ISSN must match what is given on the journal website.")],
329 "placeholder": "",
330 "doaj_criteria": lazy_gettext("ISSN must be provided")
331 },
332 },
333 "associate_editor": {
334 "disabled": True,
335 "help": {
336 "long_help": [lazy_gettext("Must be a valid ISSN, fully registered and confirmed at the "
337 "<a href='https://portal.issn.org/' target='_blank' rel='noopener'> ISSN Portal</a>."),
338 lazy_gettext("The ISSN must match what is given on the journal website.")],
339 "placeholder": "",
340 "doaj_criteria": lazy_gettext("ISSN must be provided")
341 }
342 },
343 "update_request": {
344 "disabled": True
345 }
346 }
347 }
349 # ~~->$ EISSN:FormField~~
350 EISSN = {
351 "name": "eissn",
352 "label": lazy_gettext("ISSN (online)"),
353 "input": "text",
354 "help": {
355 "long_help": [lazy_gettext("Must be a valid ISSN, fully registered and confirmed at the "
356 "<a href='https://portal.issn.org/' target='_blank' rel='noopener'> ISSN Portal</a>."),
357 lazy_gettext("Use the link under the ISSN you provided to check it."),
358 lazy_gettext("The ISSN must match what is given on the journal website.")],
359 "short_help": lazy_gettext("For example, 0378-5955"),
360 "doaj_criteria": lazy_gettext("ISSN must be provided")
361 },
362 "validate": [
363 {"optional_if": {"field": "pissn", # ~~^-> OptionalIf:FormValidator~~
364 "message": lazy_gettext("You must provide <strong>one or both</strong> an online ISSN or a print ISSN")}},
365 {"is_issn": {"message": lazy_gettext("This is not a valid ISSN")}}, # ~~^-> IsISSN:FormValidator~~
366 {"different_to": {"field": "pissn",
367 "message": lazy_gettext("This field must contain a different value to 'ISSN (print)'")}}
368 # ~~^-> DifferetTo:FormValidator~~
369 ],
370 "widgets": [
371 "trim_whitespace", # ~~^-> TrimWhitespace:FormWidget~~
372 "full_contents", # ~~^->FullContents:FormWidget~~
373 "issn_link" # ~~^->IssnLink:FormWidget~~
374 ],
375 "contexts": {
376 "public": {
377 "validate": [
378 {"optional_if": {"field": "pissn", # ~~^-> OptionalIf:FormValidator~~
379 "message": lazy_gettext("You must provide <strong>one or both</strong> an online ISSN or a print ISSN")}},
380 {"is_issn": {"message": lazy_gettext("This is not a valid ISSN")}}, # ~~^-> IsISSN:FormValidator~~
381 {"different_to": {"field": "pissn",
382 "message": lazy_gettext("This field must contain a different value to 'ISSN (print)'")}},
383 # ~~^-> DifferetTo:FormValidator~~
384 "issn_in_public_doaj"
385 ]
386 },
387 "admin": {
388 "help": {
389 "long_help": [lazy_gettext("Must be a valid ISSN, fully registered and confirmed at the "
390 "<a href='https://portal.issn.org/' target='_blank' rel='noopener'> ISSN Portal</a>."),
391 lazy_gettext("The ISSN must match what is given on the journal website.")],
392 "placeholder": "",
393 "doaj_criteria": lazy_gettext("ISSN must be provided")
394 },
395 "widgets": [
396 "trim_whitespace", # ~~^-> TrimWhitespace:FormWidget~~
397 "autocheck", # ~~^-> Autocheck:FormWidget~~
398 "issn_link" # ~~^->IssnLink:FormWidget~~
399 ]
400 },
401 "editor": {
402 "disabled": True,
403 "help": {
404 "long_help": [lazy_gettext("Must be a valid ISSN, fully registered and confirmed at the "
405 "<a href='https://portal.issn.org/' target='_blank' rel='noopener'> ISSN Portal</a>."),
406 lazy_gettext("The ISSN must match what is given on the journal website.")],
407 "placeholder": "",
408 "doaj_criteria": lazy_gettext("ISSN must be provided")
409 },
410 },
411 "associate_editor": {
412 "disabled": True,
413 "help": {
414 "long_help": [lazy_gettext("Must be a valid ISSN, fully registered and confirmed at the "
415 "<a href='https://portal.issn.org/' target='_blank' rel='noopener'> ISSN Portal</a>."),
416 lazy_gettext("The ISSN must match what is given on the journal website.")],
417 "placeholder": "",
418 "doaj_criteria": lazy_gettext("ISSN must be provided")
419 }
420 },
421 "update_request": {
422 "disabled": True,
423 "validate": [
424 {"optional_if": {"field": "pissn", # ~~^-> OptionalIf:FormValidator~~
425 "message": lazy_gettext("You must provide <strong>one or both</strong> an online ISSN or a print ISSN")}},
426 {"is_issn": {"message": lazy_gettext("This is not a valid ISSN")}}, # ~~^-> IsISSN:FormValidator~~
427 {"different_to": {"field": "pissn", # ~~^-> DifferetTo:FormValidator~~
428 "message": lazy_gettext("This field must contain a different value to 'ISSN (print)'")}}
429 ]
430 }
431 }
432 }
434 # ~~->$ Keywords:FormField~~
435 KEYWORDS = {
436 "name": "keywords",
437 "label": lazy_gettext("Up to 6 subject keywords in English"),
438 "input": "taglist",
439 "help": {
440 "long_help": [lazy_gettext("Choose up to 6 keywords that describe the journal's subject matter. "
441 "Keywords must be in English."), lazy_gettext("Use single words or short phrases (2 to 3 words) "
442 "that describe the journal's main topic."),
443 lazy_gettext("Do not add acronyms, abbreviations or descriptive sentences."),
444 lazy_gettext("Note that the keywords may be edited by DOAJ editorial staff.")],
445 },
446 "validate": [
447 {"required": {"message": lazy_gettext("Enter at least <strong>one subject keyword</strong> in English")}},
448 {"stop_words": {"disallowed": STOP_WORDS}}, # ~~^->StopWords:FormValidator~~
449 {"max_tags": {"max": 6}}
450 ],
451 "widgets": [
452 {
453 "taglist": {
454 "maximumSelectionSize": 6,
455 "stopWords": STOP_WORDS,
456 "field": "bibjson.keywords"
457 }
458 }
459 ],
460 "attr": {
461 "class": "input-xlarge"
462 }
463 }
465 # ~~->$ Language:FormField~~
466 LANGUAGE = {
467 "name": "language",
468 "label": lazy_gettext("Languages in which the journal accepts manuscripts"),
469 "input": "select",
470 "default": "",
471 "options_fn": "iso_language_list",
472 "repeatable": {
473 "label": lazy_gettext("Language"),
474 "minimum": 1,
475 "initial": 5
476 },
477 "validate": [
478 {"required": {"message": lazy_gettext("Enter <strong>at least one</strong> language")}},
479 "current_iso_language"
480 ],
481 "widgets": [
482 {"select": {}},
483 "multiple_field"
484 ],
485 "help": {
486 "placeholder": lazy_gettext("Type or select the language")
487 },
488 "attr": {
489 "class": "input-xlarge unstyled-list"
490 }
491 }
493 # ~~->$ PublisherName:FormField~~
494 PUBLISHER_NAME = {
495 "name": "publisher_name",
496 "label": lazy_gettext("Publisher's name"),
497 "input": "text",
498 "validate": [
499 {"required": {"message": lazy_gettext("Enter the name of the journal's publisher")}},
500 ],
501 "widgets": [
502 "trim_whitespace", # ~~^-> TrimWhitespace:FormWidget~~
503 {"autocomplete": {"type": "journal", "field": "bibjson.publisher.name.exact"}},
504 # ~~^-> Autocomplete:FormWidget~~
505 "full_contents" # ~~^->FullContents:FormWidget~~
506 ],
507 "help": {
508 "placeholder": lazy_gettext("Type or select the publisher's name")
509 },
510 "contexts": {
511 "bulk_edit": {
512 "validate": []
513 },
514 "admin": {
515 "widgets": [
516 "trim_whitespace", # ~~^-> TrimWhitespace:FormWidget~~
517 {"autocomplete": {"type": "journal", "field": "bibjson.publisher.name.exact"}},
518 # ~~^-> Autocomplete:FormWidget~~
519 "click_to_copy", # ~~^-> ClickToCopy:FormWidget~~
520 ]
521 },
522 "public": {
523 "validate": [
524 {"required": {"message": lazy_gettext("Enter the name of the journal's publisher")}},
525 {"different_to": {"field": "institution_name",
526 "message": lazy_gettext("The Publisher's name and Other organisation's name cannot be the same.")}}
527 ]
528 # ~~^-> DifferetTo:FormValidator~~
530 },
531 "update_request": {
532 "validate": [
533 {"required": {"message": lazy_gettext("Enter the name of the journal's publisher")}},
534 {"different_to": {"field": "institution_name",
535 "message": lazy_gettext("The Publisher's name and Other organisation's name cannot be the same.")}}
536 ]
537 # ~~^-> DifferetTo:FormValidator~~
539 },
540 "associate_editor": {
541 "widgets": [
542 "trim_whitespace", # ~~^-> TrimWhitespace:FormWidget~~
543 {"autocomplete": {"type": "journal", "field": "bibjson.publisher.name.exact"}},
544 # ~~^-> Autocomplete:FormWidget~~
545 "click_to_copy", # ~~^-> ClickToCopy:FormWidget~~
546 ]
547 },
548 "editor": {
549 "widgets": [
550 "trim_whitespace", # ~~^-> TrimWhitespace:FormWidget~~
551 {"autocomplete": {"type": "journal", "field": "bibjson.publisher.name.exact"}},
552 # ~~^-> Autocomplete:FormWidget~~
553 "click_to_copy", # ~~^-> ClickToCopy:FormWidget~~
554 ]
555 }
556 }
557 }
559 # ~~->$ PublisherCountry:FormField~~
560 PUBLISHER_COUNTRY = {
561 "name": "publisher_country",
562 "label": lazy_gettext("Publisher's country"),
563 "input": "select",
564 "default": "",
565 "options_fn": "iso_country_list",
566 "help": {
567 "long_help": [lazy_gettext("The country where the publisher carries out its business operations and is registered.")],
568 "doaj_criteria": lazy_gettext("You must provide a publisher country"),
569 "placeholder": lazy_gettext("Type or select the country")
570 },
571 "validate": [
572 {"required": {
573 "message": lazy_gettext("Enter the <strong>country</strong> where the publisher carries out its business operations and is registered")}}
574 ],
575 "widgets": [
576 {"select": {}}
577 ],
578 "attr": {
579 "class": "input-xlarge"
580 },
581 "contexts": {
582 "associate_editor": {
583 "disabled": True
584 },
585 "bulk_edit": {
586 "validate": []
587 }
588 }
589 }
591 # ~~->$ InstitutionName:FormField~~
592 INSTITUTION_NAME = {
593 "name": "institution_name",
594 "label": lazy_gettext("Other organisation's name"),
595 "input": "text",
596 "optional": True,
597 "help": {
598 "short_help": lazy_gettext("Any other organisation associated with the journal"),
599 "long_help": [
600 lazy_gettext("The journal may be owned, funded, sponsored, or supported by another organisation that is not "
601 "the publisher. If your journal is linked to "
602 "a second organisation, enter its name here.")],
603 "placeholder": lazy_gettext("Type or select the other organisation's name")
604 },
605 "widgets": [
606 "trim_whitespace", # ~~^-> TrimWhitespace:FormWidget~~
607 {"autocomplete": {"type": "journal", "field": "bibjson.institution.name.exact"}},
608 # ~~^-> Autocomplete:FormWidget~~
609 "full_contents" # ~~^->FullContents:FormWidget~~
610 ],
611 "contexts": {
612 "public": {
613 "validate": [{"different_to": {"field": "publisher_name",
614 "message": lazy_gettext("The Publisher's name and Other organisation's name cannot be the same.")}}]
615 # ~~^-> DifferetTo:FormValidator~~
617 },
618 "update_request": {
619 "validate": [{"different_to": {"field": "publisher_name",
620 "message": lazy_gettext("The Publisher's name and Other organisation's name cannot be the same.")}}]
621 # ~~^-> DifferetTo:FormValidator~~
623 },
624 "admin": {
625 "widgets": [
626 "trim_whitespace", # ~~^-> TrimWhitespace:FormWidget~~
627 {"autocomplete": {"type": "journal", "field": "bibjson.institution.name.exact"}},
628 # ~~^-> Autocomplete:FormWidget~~
629 "click_to_copy", # ~~^-> ClickToCopy:FormWidget~~
630 ]
631 },
632 "associate_editor": {
633 "widgets": [
634 "trim_whitespace", # ~~^-> TrimWhitespace:FormWidget~~
635 {"autocomplete": {"type": "journal", "field": "bibjson.institution.name.exact"}},
636 # ~~^-> Autocomplete:FormWidget~~
637 "click_to_copy", # ~~^-> ClickToCopy:FormWidget~~
638 ]
639 },
640 "editor": {
641 "widgets": [
642 "trim_whitespace", # ~~^-> TrimWhitespace:FormWidget~~
643 {"autocomplete": {"type": "journal", "field": "bibjson.institution.name.exact"}},
644 # ~~^-> Autocomplete:FormWidget~~
645 "click_to_copy", # ~~^-> ClickToCopy:FormWidget~~
646 ]
647 }
648 }
649 }
651 # ~~->$ InstitutionCountry:FormField~~
652 INSTITUTION_COUNTRY = {
653 "name": "institution_country",
654 "label": lazy_gettext("Other organisation's country"),
655 "input": "select",
656 "default": "",
657 "options_fn": "iso_country_list",
658 "optional": True,
659 "help": {
660 "short_help": lazy_gettext("The country in which the other organisation is based"),
661 "placeholder": lazy_gettext("Type or select the country")
662 },
663 "widgets": [
664 {"select": {"allow_clear": True}}
665 ],
666 "contexts": {
667 "public": {
668 "validate": [
669 {
670 "only_if_exists": {
671 "fields":
672 [{"field": "institution_name"}],
673 "message": lazy_gettext("'You must provide the other organization's name. You cannot provide just the country."),
674 }
675 }
676 ]
677 },
678 "update_request": {
679 "validate": [
680 {
681 "only_if_exists": {
682 "fields":
683 [{"field": "institution_name"}],
684 "message": lazy_gettext("'You must provide the other organization's name. You cannot provide just the country."),
685 }
686 }
687 ]
688 },
689 },
690 "attr": {
691 "class": "input-xlarge"
692 }
693 }
695 # ~~->$ License:FormField~~
696 LICENSE = {
697 "name": "license",
698 "label": lazy_gettext("License(s) permitted by the journal"),
699 "input": "checkbox",
700 "multiple": True,
701 "options": [
702 {"display": lazy_gettext("CC BY"), "value": "CC BY"},
703 {"display": lazy_gettext("CC BY-SA"), "value": "CC BY-SA"},
704 {"display": lazy_gettext("CC BY-ND"), "value": "CC BY-ND"},
705 {"display": lazy_gettext("CC BY-NC"), "value": "CC BY-NC"},
706 {"display": lazy_gettext("CC BY-NC-SA"), "value": "CC BY-NC-SA"},
707 {"display": lazy_gettext("CC BY-NC-ND"), "value": "CC BY-NC-ND"},
708 {"display": lazy_gettext("CC0"), "value": "CC0"},
709 {"display": lazy_gettext("Public domain"), "value": "Public domain"},
710 {"display": lazy_gettext("Publisher's own license"), "value": "Publisher's own license",
711 "subfields": ["license_attributes"]},
712 ],
713 "help": {
714 "long_help": [lazy_gettext("The journal must use some form of licensing to be considered for indexing in DOAJ. "),
715 lazy_gettext("If Creative Commons licensing is not used, then select <em>Publisher's own license</em> and enter "
716 "more details below."),
717 lazy_gettext("More information on CC licenses: <br/>"
718 "<a href='https://creativecommons.org/licenses/by/4.0/"
719 "' target='_blank' 'rel='noopener'>CC BY</a> <br/>"
720 "<a href='https://creativecommons.org/licenses/by-sa/4.0/"
721 "' target='_blank' 'rel='noopener'>CC BY-SA</a> <br/>"
722 "<a href='https://creativecommons.org/licenses/by-nd/4.0/"
723 "' target='_blank' 'rel='noopener'>CC BY-ND</a> <br/>"
724 "<a href='https://creativecommons.org/licenses/by-nc/4.0/"
725 "' target='_blank' 'rel='noopener'>CC BY-NC</a> <br/>"
726 "<a href='https://creativecommons.org/licenses/by-nc-sa/4.0/"
727 "' target='_blank' 'rel='noopener'>CC BY-NC-SA</a> <br/>"
728 "<a href='https://creativecommons.org/licenses/by-nc-nd/4.0/"
729 "' target='_blank' 'rel='noopener'>CC BY-NC-ND</a>"),
730 lazy_gettext("<a href='https://wiki.creativecommons.org/wiki/CC0_"
731 "FAQ#What_is_the_difference_between_CC0_and_the_Publ"
732 "ic_Domain_Mark_.28.22PDM.22.29.3F' target='_blank' "
733 "rel='noopener'>What is the difference between CC0 "
734 "and the Public Domain Mark (\"PDM\")?</a>")],
735 "doaj_criteria": lazy_gettext("Content must be licensed")
736 },
737 "validate": [
738 {"required": {"message": lazy_gettext("Select <strong>at least one</strong> type of license")}}
739 ]
740 }
742 # ~~->$ LicenseAttributes:FormField~~
743 LICENSE_ATTRIBUTES = {
744 "name": "license_attributes",
745 "label": lazy_gettext("Select all the attributes that your license has"),
746 "input": "checkbox",
747 "multiple": True,
748 "conditional": [
749 {"field": "license", "value": "Publisher's own license"}
750 ],
751 "options": [
752 {"display": lazy_gettext("Attribution"), "value": "BY"},
753 {"display": lazy_gettext("Share Alike"), "value": "SA"},
754 {"display": lazy_gettext("No Derivatives"), "value": "ND"},
755 {"display": lazy_gettext("No Commercial Usage"), "value": "NC"}
756 ],
757 "help": {
758 "doaj_criteria": lazy_gettext("Content must be licensed")
759 }
760 }
762 # ~~->$ LicenseTermsURL:FormField~~
763 LICENSE_TERMS_URL = {
764 "name": "license_terms_url",
765 "label": lazy_gettext("Where can we find this information?"),
766 "input": "text",
767 "diff_table_context": "License terms",
768 "validate": [
769 {"required": {"message": lazy_gettext("Enter the URL for the journal’s <strong>license terms</strong> page")}},
770 "is_url" # ~~^->IsURL:FormValidator~~
771 ],
772 "help": {
773 "short_help": lazy_gettext("Link to the page where the license terms are stated on your site."),
774 "doaj_criteria": lazy_gettext("You must provide a link to your license terms"),
775 "placeholder": lazy_gettext("https://www.my-journal.com/about#licensing"),
776 },
777 "widgets": [
778 "trim_whitespace", # ~~^-> TrimWhitespace:FormWidget~~
779 "clickable_url" # ~~^-> ClickableURL:FormWidget~~
780 ]
781 }
783 # ~~->$ LicenseDisplay:FormField~~
784 LICENSE_DISPLAY = {
785 "name": "license_display",
786 "label": lazy_gettext("Does the journal embed and/or display licensing information in its articles?"),
787 "input": "radio",
788 "options": [
789 {"display": lazy_gettext("Yes"), "value": "y", "subfields": ["license_display_example_url"]},
790 {"display": lazy_gettext("No"), "value": "n"}
791 ],
792 "help": {
793 "long_help": [lazy_gettext("It is recommended that licensing information is included in full-text articles "
794 "but it is not required for inclusion. "
795 "Answer <strong>Yes</strong> if licensing is displayed or "
796 "embedded in all versions of each article.")]
797 },
798 "validate": [
799 {"required": {"message": lazy_gettext("Select Yes or No")}}
800 ]
801 }
803 # ~~->$ LicenseDisplayExampleUrl:FormField~~
804 LICENSE_DISPLAY_EXAMPLE_URL = {
805 "name": "license_display_example_url",
806 "label": lazy_gettext("Recent article displaying or embedding a license in the full text"),
807 "input": "text",
808 "conditional": [
809 {"field": "license_display", "value": "y"}
810 ],
811 "help": {
812 "short_help": lazy_gettext("Link to an example article"),
813 "placeholder": "https://www.my-journal.com/articles/article-page"
814 },
815 "validate": [
816 {"required_if": {
817 "field": "license_display",
818 "value": "y",
819 "message": lazy_gettext("Enter the URL for any recent article that displays or embeds a license")
820 }
821 },
822 "is_url" # ~~^->IsURL:FormValidator~~
823 ],
824 "widgets": [
825 "trim_whitespace", # ~~^-> TrimWhitespace:FormWidget~~
826 "clickable_url" # ~~^-> ClickableURL:FormWidget~~
827 ]
828 }
830 # ~~->$ CopyrightAuthorRetails:FormField~~
831 COPYRIGHT_AUTHOR_RETAINS = {
832 "name": "copyright_author_retains",
833 "label": lazy_gettext("For all the licenses you have indicated above, do authors retain the copyright "
834 "<b>and</b> full publishing rights without restrictions?"),
835 "input": "radio",
836 "options": [
837 {"display": lazy_gettext("Yes"), "value": "y"},
838 {"display": lazy_gettext("No"), "value": "n"}
839 ],
840 "validate": [
841 {"required": {"message": lazy_gettext("Select Yes or No")}}
842 ],
843 "help": {
844 "long_help": [lazy_gettext("Answer <strong>No</strong> if authors transfer "
845 "copyright or assign exclusive rights to the publisher"
846 " (including commercial rights). <br/><br/> Answer "
847 "<strong>Yes</strong> only if authors publishing "
848 "under any license allowed by the journal "
849 "retain all rights.")]
850 }
851 }
853 # ~~->$ CopyrightURL:FormField~~
854 COPYRIGHT_URL = {
855 "name": "copyright_url",
856 "label": lazy_gettext("Where can we find this information?"),
857 "input": "text",
858 "diff_table_context": lazy_gettext("Copyright terms"),
859 "help": {
860 "short_help": lazy_gettext("Link to the journal’s copyright terms")
861 },
862 "placeholder": "https://www.my-journal.com/about#licensing",
863 "validate": [
864 "is_url" # ~~^->IsURL:FormValidator~~
865 ],
866 "widgets": [
867 "trim_whitespace", # ~~^-> TrimWhitespace:FormWidget~~
868 "clickable_url" # ~~^-> ClickableURL:FormWidget~~
869 ],
870 "contexts": {
871 "public": {
872 "validate": [
873 {"required": {"message": lazy_gettext("Enter the URL for the journal’s <strong>copyright terms</strong> page")}},
874 "is_url" # ~~^->IsURL:FormValidator~~
875 ]
876 },
877 "update_request": {
878 "validate": [
879 "required",
880 "is_url" # ~~^->IsURL:FormValidator~~
881 ]
882 }
883 }
884 }
886 # ~~->$ ReviewProcess:FormField~~
887 REVIEW_PROCESS = {
888 "name": "review_process",
889 "label": lazy_gettext("DOAJ only accepts peer-reviewed journals. "
890 "Which type(s) of peer review does this journal use?"),
891 "input": "checkbox",
892 "multiple": True,
893 "options": [
894 {"display": lazy_gettext("Editorial review"), "value": "Editorial review"},
895 {"display": lazy_gettext("Peer review"), "value": "Peer review"},
896 {"display": lazy_gettext("Anonymous peer review"), "value": "Anonymous peer review"},
897 {"display": lazy_gettext("Double anonymous peer review"), "value": "Double anonymous peer review"},
898 {"display": lazy_gettext("Post-publication peer review"), "value": "Post-publication peer review"},
899 {"display": lazy_gettext("Open peer review"), "value": "Open peer review"},
900 {"display": lazy_gettext("Other"), "value": "other", "subfields": ["review_process_other"]}
901 ],
902 "help": {
903 "long_help": [lazy_gettext("Enter all types of review used by the journal for "
904 "research articles. Note that editorial review is "
905 "only accepted for <a href='https://doaj.org/apply/guide/#arts-and-humanities-journals' target='_blank' rel='nofollow'>arts and humanities journals</a>."
906 "For a detailed description of the peer review types, "
907 "see <a href='https://docs.google.com/document/d/1ADiVPR7tY8a9JKr2VjFEXbNG7FIpz22nOPDDPfRzJxA/edit?tab=t.0' target='_blank' rel='nofollow'>this summary</a>.")],
908 "doaj_criteria": lazy_gettext("Peer review must be carried out")
909 },
910 "validate": [
911 {"required": {"message": lazy_gettext("Select <strong>at least one</strong> type of review process")}}
912 ]
913 }
915 # ~~->$ ReviewProcessOther:FormField~~
916 REVIEW_PROCESS_OTHER = {
917 "name": "review_process_other",
918 "label": lazy_gettext("Other peer review"),
919 "input": "text",
920 "help": {
921 "placeholder": lazy_gettext("Other peer review")
922 },
923 "conditional": [{"field": "review_process", "value": "other"}],
924 "validate": [
925 {"required_if": {
926 "field": "review_process",
927 "value": "other",
928 "message": lazy_gettext("Enter the name of another type of peer review")
929 }
930 }
931 ],
932 "widgets": [
933 "trim_whitespace" # ~~^-> TrimWhitespace:FormWidget~~
934 ],
935 "asynchronous_warning": [
936 {"warn_on_value": {"value": "None"}}
937 ]
938 }
940 # ~~->$ ReviewURL:FormField~~
941 REVIEW_URL = {
942 "name": "review_url",
943 "label": lazy_gettext("Where can we find this information?"),
944 "input": "text",
945 "diff_table_context": lazy_gettext("Peer review policy"),
946 "help": {
947 "doaj_criteria": lazy_gettext("You must provide a URL"),
948 "short_help": lazy_gettext("Link to the journal’s peer review policy")
949 },
950 "validate": [
951 {"required": {"message": lazy_gettext("Enter the URL for the journal’s <strong>peer review policy</strong> page")}},
952 "is_url" # ~~^->IsURL:FormValidator~~
953 ],
954 "widgets": [
955 "trim_whitespace", # ~~^-> TrimWhitespace:FormWidget~~
956 "clickable_url" # ~~^-> ClickableURL:FormWidget~~
957 ]
958 }
960 # ~~->$ OAStart:FormField~~
961 OA_START = {
962 "name": "oa_start",
963 "label": lazy_gettext("When did the journal start to publish all content using an open license?"),
964 "input": "number",
965 "datatype": "integer",
966 "help": {
967 "long_help": [
968 lazy_gettext("Please enter the year that the journal started to publish all content as true open access, according to DOAJ's <a href='https://blog.doaj.org/2020/11/17/what-does-doaj-define-as-open-access/' target='_blank' rel='nofollow'>definition</a>."),
969 lazy_gettext("For journals that have flipped to open access, enter the year that the journal flipped, not the original launch date of the journal."),
970 lazy_gettext("For journals that have made digitised backfiles freely available, enter the year that the journal started publishing as a fully open access title, not the date of the earliest free content.")]
971 },
972 "validate": [
973 {"required": {"message": lazy_gettext("Enter the Year (YYYY).")}},
974 {"int_range": {"gte": app.config.get('MINIMAL_OA_START_DATE', 1900), "lte": dates.now().year}},
975 {"year": {
976 "message": lazy_gettext("OA Start Date must be a year in the 4-digit format (eg. 1987) and must be greater than {}").format(
977 app.config.get('MINIMAL_OA_START_DATE', 1900))}}
978 ],
979 "attr": {
980 "min": app.config.get('MINIMAL_OA_START_DATE', 1900),
981 "max": dates.now().year
982 }
983 }
985 # ~~->$ PlagiarismDetection:FormField~~
986 PLAGIARISM_DETECTION = {
987 "name": "plagiarism_detection",
988 "label": lazy_gettext("Does the journal routinely screen article submissions for plagiarism?"),
989 "input": "radio",
990 "help": {
991 "long_help": [lazy_gettext("Screening for plagiarism is recommended, but is not"
992 " a requirement for inclusion in DOAJ. If the "
993 "journal does screen for plagiarism, state the "
994 "services(s) used on your website.")],
995 },
996 "options": [
997 {"display": lazy_gettext("Yes"), "value": "y", "subfields": ["review_process_other"]},
998 {"display": lazy_gettext("No"), "value": "n"}
999 ],
1000 "validate": [
1001 {"required": {"message": lazy_gettext("Select Yes or No")}}
1002 ]
1003 }
1005 # ~~->$ PlagiarismURL:FormField~~
1006 PLAGIARISM_URL = {
1007 "name": "plagiarism_url",
1008 "label": lazy_gettext("Where can we find this information?"),
1009 "diff_table_context": lazy_gettext("Plagiarism screening"),
1010 "input": "text",
1011 "conditional": [{"field": "plagiarism_detection", "value": "y"}],
1012 "help": {
1013 "doaj_criteria": lazy_gettext("You must provide a URL"),
1014 "placeholder": "https://www.my-journal.com/about#plagiarism",
1015 "short_help": lazy_gettext("Link to the journal’s plagiarism policy"),
1016 "long_help": [lazy_gettext("The page should state that the journal actively checks for plagiarism and explain how this "
1017 "is done (including the name of any software or service used).")]
1018 },
1019 "validate": [
1020 {"required_if": {
1021 "field": "plagiarism_detection",
1022 "value": "y",
1023 "message": lazy_gettext("Enter the URL for the journal’s <strong>plagiarism policy</strong> page")
1024 }
1025 },
1026 "is_url" # ~~^->IsURL:FormValidator~~
1027 ],
1028 "widgets": [
1029 "trim_whitespace", # ~~^-> TrimWhitespace:FormWidget~~
1030 "clickable_url" # ~~^-> ClickableURL:FormWidget~~
1031 ]
1032 }
1034 # ~~->$ AimsScopeURL:FormField~~
1035 AIMS_SCOPE_URL = {
1036 "name": "aims_scope_url",
1037 "label": lazy_gettext("Link to the journal’s <b>Aims & Scope</b>"),
1038 "input": "text",
1039 "help": {
1040 "doaj_criteria": lazy_gettext("You must provide a URL"),
1041 "placeholder": "https://www.my-journal.com/about#aims"
1042 },
1043 "validate": [
1044 {"required": {"message": lazy_gettext("Enter the URL for the journal’s <strong>Aims & Scope</strong> page")}},
1045 "is_url" # ~~^->IsURL:FormValidator~~
1046 ],
1047 "widgets": [
1048 "trim_whitespace", # ~~^-> TrimWhitespace:FormWidget~~
1049 "clickable_url" # ~~^-> ClickableURL:FormWidget~~
1050 ]
1051 }
1053 # ~~->$ EditorialBoardURL:FormField~~
1054 EDITORIAL_BOARD_URL = {
1055 "name": "editorial_board_url",
1056 "label": lazy_gettext("Link to the journal’s <b>Editorial Board</b>"),
1057 "input": "text",
1058 "help": {
1059 "doaj_criteria": lazy_gettext("You must provide a URL"),
1060 "placeholder": "https://www.my-journal.com/about#board"
1061 },
1062 "validate": [
1063 {"required": {"message": lazy_gettext("Enter the URL for the journal’s <strong>Editorial Board</strong> page")}},
1064 "is_url" # ~~^->IsURL:FormValidator~~
1065 ],
1066 "widgets": [
1067 "trim_whitespace", # ~~^-> TrimWhitespace:FormWidget~~
1068 "clickable_url" # ~~^-> ClickableURL:FormWidget~~
1069 ]
1070 }
1072 # ~~->$ AuthorInstructionsURL:FormField~~
1073 AUTHOR_INSTRUCTIONS_URL = {
1074 "name": "author_instructions_url",
1075 "label": lazy_gettext("Link to the journal’s <b>Instructions for Authors</b>"),
1076 "input": "text",
1077 "help": {
1078 "doaj_criteria": lazy_gettext("You must provide a URL"),
1079 "placeholder": "https://www.my-journal.com/for_authors"
1080 },
1081 "validate": [
1082 {"required": {"message": lazy_gettext("Enter the URL for the journal’s <strong>Instructions for Authors</strong> page")}},
1083 "is_url" # ~~^->IsURL:FormValidator~~
1084 ],
1085 "widgets": [
1086 "trim_whitespace", # ~~^-> TrimWhitespace:FormWidget~~
1087 "clickable_url" # ~~^-> ClickableURL:FormWidget~~
1088 ]
1089 }
1091 # ~~->$ PublicationTimeWeeks:FormField~~
1092 PUBLICATION_TIME_WEEKS = {
1093 "name": "publication_time_weeks",
1094 "label": lazy_gettext("Average number of <strong>weeks</strong> between article submission & publication"),
1095 "input": "number",
1096 "datatype": "integer",
1097 "validate": [
1098 {"required": {"message": lazy_gettext("Enter an average number of weeks")}},
1099 {"int_range": {"gte": 1, "lte": 100}}
1100 ],
1101 "attr": {
1102 "min": "1",
1103 "max": "100"
1104 }
1105 }
1107 # ~~->$ APC:FormField~~
1108 APC = {
1109 "name": "apc",
1110 "label": lazy_gettext("Does the journal charge fees for publishing an article (APCs)?"),
1111 "input": "radio",
1112 "options": [
1113 {"display": lazy_gettext("Yes"), "value": "y", "subfields": ["apc_charges"]},
1114 {"display": lazy_gettext("No"), "value": "n"}
1115 ],
1116 "help": {
1117 "long_help": [lazy_gettext("Publication fees are sometimes called "
1118 "article processing charges (APCs). You should answer"
1119 " Yes if any fee is required from the author for "
1120 "publishing their paper.")],
1121 "doaj_criteria": lazy_gettext("You must tell us about any APCs")
1122 },
1123 "validate": [
1124 {"required": {"message": lazy_gettext("Select Yes or No")}}
1125 ]
1126 }
1128 # ~~->$ APCCharges:FormField~~
1129 APC_CHARGES = {
1130 "name": "apc_charges",
1131 "input": "group",
1132 "label": lazy_gettext("Highest fee charged"),
1133 "repeatable": {
1134 "minimum": 1,
1135 "initial": 5
1136 },
1137 "conditional": [
1138 {"field": "apc", "value": "y"}
1139 ],
1140 "help": {
1141 "long_help": [lazy_gettext("If the journal charges a range of fees for "
1142 "the publication of an article, enter the highest fee. "
1143 "If the fee can be paid in more than one currency, "
1144 "you may list them here.")]
1145 },
1146 "subfields": [
1147 "apc_currency",
1148 "apc_max"
1149 ],
1150 "template": templates.AF_LIST,
1151 "entry_template": templates.AF_ENTRY_GROUP_HORIZONTAL,
1152 "widgets": [
1153 "multiple_field"
1154 ]
1155 }
1157 # ~~->$ APCCurrency:FormField~~
1158 APC_CURRENCY = {
1159 "subfield": True,
1160 "group": "apc_charges",
1161 "name": "apc_currency",
1162 "input": "select",
1163 "options_fn": "iso_currency_list",
1164 "default": "",
1165 "help": {
1166 "placeholder": "Currency"
1167 },
1168 "widgets": [
1169 {"select": {}}
1170 ],
1171 "attr": {
1172 "class": "input-xlarge"
1173 },
1174 "validate": [
1175 {
1176 "required_if": {
1177 "field": "apc",
1178 "value": "y",
1179 "message": lazy_gettext("Enter the currency or currencies for the journal’s publishing fees")
1180 }
1181 },
1182 "current_iso_currency"
1183 ]
1184 }
1186 # ~~->$ APCMax:FormField~~
1187 APC_MAX = {
1188 "subfield": True,
1189 "group": "apc_charges",
1190 "name": "apc_max",
1191 "input": "number",
1192 "datatype": "integer",
1193 "help": {
1194 "placeholder": lazy_gettext("Highest fee charged")
1195 },
1196 "validate": [
1197 {
1198 "required_if": {
1199 "field": "apc",
1200 "value": "y",
1201 "message": lazy_gettext("Enter the value of the highest publishing fee the journal has charged")
1202 }
1203 }
1204 ],
1205 "attr": {
1206 "min": "1"
1207 }
1208 }
1210 # ~~->$ APCURL:FormField~~
1211 APC_URL = {
1212 "name": "apc_url",
1213 "label": lazy_gettext("Where can we find this information?"),
1214 "diff_table_context": lazy_gettext("Publication fees"),
1215 "input": "text",
1216 "help": {
1217 "short_help": lazy_gettext("Link to the page where this is stated. The page "
1218 "must declare <b>whether or not</b> there is a fee "
1219 "to publish an article in the journal."),
1220 "doaj_criteria": lazy_gettext("You must provide a URL"),
1221 "placeholder": "https://www.my-journal.com/about#apc"
1222 },
1223 "validate": [
1224 {"required": {
1225 "message": lazy_gettext("Enter the URL for the journal’s <strong>publication fees</strong> information page")}},
1226 "is_url" # ~~^->IsURL:FormValidator~~
1227 ],
1228 "widgets": [
1229 "trim_whitespace", # ~~^-> TrimWhitespace:FormWidget~~
1230 "clickable_url" # ~~^-> ClickableURL:FormWidget~~
1231 ]
1232 }
1234 # ~~->$ HasWaiver:FormField~~
1235 HAS_WAIVER = {
1236 "name": "has_waiver",
1237 "label": lazy_gettext("Does the journal provide a waiver or discount "
1238 "on publication fees for authors?"),
1239 "input": "radio",
1240 "options": [
1241 {"display": lazy_gettext("Yes"), "value": "y", "subfields": ["waiver_url"]},
1242 {"display": lazy_gettext("No"), "value": "n"}
1243 ],
1244 "help": {
1245 "long_help": [lazy_gettext("Answer <strong>Yes</strong> if the journal provides"
1246 " publication fee waivers for authors from "
1247 "low-income economies, discounts for authors from "
1248 "lower middle-income economies, and/or waivers and "
1249 "discounts for other authors with "
1250 "demonstrable needs.")]
1251 },
1252 "validate": [
1253 {"required": {"message": lazy_gettext("Select Yes or No")}}
1254 ]
1255 }
1257 # ~~->$ WaiverURL:FormField~~
1258 WAIVER_URL = {
1259 "name": "waiver_url",
1260 "label": lazy_gettext("Where can we find this information?"),
1261 "input": "text",
1262 "diff_table_context": lazy_gettext("Publication fee waiver"),
1263 "conditional": [
1264 {"field": "has_waiver", "value": "y"}
1265 ],
1266 "help": {
1267 "short_help": lazy_gettext("Link to the journal’s waiver information."),
1268 "doaj_criteria": lazy_gettext("You must provide a URL"),
1269 "placeholder": "https://www.my-journal.com/about#waiver"
1270 },
1271 "validate": [
1272 {"required_if": {
1273 "field": "has_waiver",
1274 "value": "y",
1275 "message": lazy_gettext("Enter the URL for the journal’s <strong>waiver information</strong> page")
1276 }
1277 },
1278 "is_url" # ~~^->IsURL:FormValidator~~
1279 ],
1280 "widgets": [
1281 "trim_whitespace", # ~~^-> TrimWhitespace:FormWidget~~
1282 "clickable_url" # ~~^-> ClickableURL:FormWidget~~
1283 ]
1284 }
1286 # ~~->$ HasOtherCharges:FormField~~
1287 HAS_OTHER_CHARGES = {
1288 "name": "has_other_charges",
1289 "label": lazy_gettext("Does the journal charge any other fees to authors?"),
1290 "input": "radio",
1291 "options": [
1292 {"display": lazy_gettext("Yes"), "value": "y", "subfields": ["other_charges_url"]},
1293 {"display": lazy_gettext("No"), "value": "n"}
1294 ],
1295 "help": {
1296 "long_help": [lazy_gettext("Declare all other charges: editorial processing charges, language editing fees, "
1297 "colour charges, submission fees, page charges, membership fees, print subscription costs, "
1298 "other supplementary charges")],
1299 "doaj_criteria": lazy_gettext("You must declare any other charges if they exist")
1300 },
1301 "validate": [
1302 {"required": {"message": lazy_gettext("Select Yes or No")}}
1303 ]
1304 }
1306 # ~~->$ OtherChargesURL:FormField~~
1307 OTHER_CHARGES_URL = {
1308 "name": "other_charges_url",
1309 "label": lazy_gettext("Where can we find this information?"),
1310 "input": "text",
1311 "diff_table_context": lazy_gettext("Other fees"),
1312 "conditional": [
1313 {"field": "has_other_charges", "value": "y"}
1314 ],
1315 "help": {
1316 "short_help": lazy_gettext("Link to the journal’s fees information"),
1317 "doaj_criteria": lazy_gettext("You must provide a URL")
1318 },
1319 "validate": [
1320 {"required_if": {
1321 "field": "has_other_charges",
1322 "value": "y",
1323 "message": lazy_gettext("Enter the URL for the journal’s <strong>fees<strong> information page")
1324 }
1325 },
1326 "is_url" # ~~^->IsURL:FormValidator~~
1327 ],
1328 "widgets": [
1329 "trim_whitespace", # ~~^-> TrimWhitespace:FormWidget~~
1330 "clickable_url" # ~~^-> ClickableURL:FormWidget~~
1331 ]
1332 }
1334 # ~~->$ PreservationService:FormField~~
1335 PRESERVATION_SERVICE = {
1336 "name": "preservation_service",
1337 "label": lazy_gettext("Long-term preservation service(s) where the journal is currently archived"),
1338 "input": "checkbox",
1339 "multiple": True,
1340 "options": [
1341 {"display": lazy_gettext("CINES"), "value": "CINES", "subfields": ["preservation_service_url"]},
1342 {"display": lazy_gettext("CLOCKSS"), "value": "CLOCKSS", "subfields": ["preservation_service_url"]},
1343 {"display": lazy_gettext("LOCKSS"), "value": "LOCKSS", "subfields": ["preservation_service_url"]},
1344 {"display": lazy_gettext("Internet Archive"), "value": "Internet Archive", "subfields": ["preservation_service_url"]},
1345 {"display": lazy_gettext("PKP PN"), "value": "PKP PN", "subfields": ["preservation_service_url"]},
1346 {"display": lazy_gettext("PubMed Central (PMC)"), "value": "PMC", "subfields": ["preservation_service_url"]},
1347 {"display": lazy_gettext("Portico"), "value": "Portico", "subfields": ["preservation_service_url"]},
1348 {"display": lazy_gettext("A national library"), "value": "national_library",
1349 "subfields": ["preservation_service_library", "preservation_service_url"]},
1350 {"display": lazy_gettext("Other"), "value": "other",
1351 "subfields": ["preservation_service_other", "preservation_service_url"]},
1352 {"display": lazy_gettext(HTMLString("<em>The journal content isn’t archived with a long-term preservation service</em>")),
1353 "value": "none", "exclusive": True}
1354 ],
1355 "help": {
1356 "long_help": [
1357 lazy_gettext("Content must be actively deposited in each of the options you choose. "
1358 "If the journal is registered with a service but archiving is not yet active,"
1359 " choose <em>The journal content isn’t archived with a long-term preservation service</em>."),
1360 lazy_gettext("PubMed Central covers PMC U.S.A. and EuropePMC(Wellcome Trust).")]
1361 },
1362 "validate": [
1363 {"required": {"message": lazy_gettext("Select <strong>at least one</strong> option")}}
1364 ],
1365 "contexts": {
1366 "admin": {
1367 "widgets": [
1368 "autocheck", # ~~^-> Autocheck:FormWidget~~
1369 ]
1370 }
1371 }
1372 }
1374 # ~~->$ PreservationServiceLibrary:FormField~~
1375 PRESERVATION_SERVICE_LIBRARY = {
1376 "name": "preservation_service_library",
1377 "label": lazy_gettext("A national library"),
1378 "input": "text",
1379 "repeatable": {
1380 "minimum": 1,
1381 "initial": 2
1382 },
1383 "help": {
1384 "short_help": lazy_gettext("Name of national library")
1385 },
1386 "conditional": [{"field": "preservation_service", "value": "national_library"}],
1387 "validate": [
1388 {"required_if": {
1389 "field": "preservation_service",
1390 "value": "national_library",
1391 "message": lazy_gettext("Enter the name(s) of the national library or libraries where the journal is archived")
1392 }
1393 }
1394 ],
1395 "widgets": [
1396 "trim_whitespace", # ~~^-> TrimWhitespace:FormWidget~~
1397 "multiple_field"
1398 ],
1399 "attr": {
1400 "class": "input-xlarge unstyled-list"
1401 }
1402 }
1404 # ~~->$ PreservationServiceOther:FormField~~
1405 PRESERVATION_SERVICE_OTHER = {
1406 "name": "preservation_service_other",
1407 "label": lazy_gettext("Other archiving policy:"),
1408 "input": "text",
1409 "conditional": [{"field": "preservation_service", "value": "other"}],
1410 "validate": [
1411 {"required_if": {
1412 "field": "preservation_service",
1413 "value": "other",
1414 "message": lazy_gettext("Enter the name of another archiving policy")
1415 }
1416 }
1417 ],
1418 "asynchronous_warning": [
1419 {"warn_on_value": {"value": "None"}}
1420 ],
1421 "widgets": [
1422 "trim_whitespace" # ~~^-> TrimWhitespace:FormWidget~~
1423 ]
1424 }
1426 # ~~->$ PreservationServiceURL:FormField~~
1427 PRESERVATION_SERVICE_URL = {
1428 "name": "preservation_service_url",
1429 "label": lazy_gettext("Where can we find this information?"),
1430 "input": "text",
1431 "diff_table_context": "Archiving policy",
1432 "help": {
1433 "short_help": lazy_gettext("Link to the preservation and archiving information"),
1434 "doaj_criteria": lazy_gettext("You must provide a URL"),
1435 "placeholder": "https://www.my-journal.com/about#archiving"
1436 },
1437 "conditional": [
1438 {"field": "preservation_service", "value": "CINES"},
1439 {"field": "preservation_service", "value": "CLOCKSS"},
1440 {"field": "preservation_service", "value": "LOCKSS"},
1441 {"field": "preservation_service", "value": "Internet Archive"},
1442 {"field": "preservation_service", "value": "PKP PN"},
1443 {"field": "preservation_service", "value": "PMC"},
1444 {"field": "preservation_service", "value": "Portico"},
1445 {"field": "preservation_service", "value": "national_library"},
1446 {"field": "preservation_service", "value": "other"}
1447 ],
1448 "validate": [
1449 {
1450 "required_if": {
1451 "field": "preservation_service",
1452 "value": [
1453 "CINES",
1454 "CLOCKSS",
1455 "LOCKSS",
1456 "Internet Archive",
1457 "PKP PN",
1458 "PMC",
1459 "Portico",
1460 "national_library",
1461 "other"
1462 ]
1463 }
1464 },
1465 "is_url" # ~~^->IsURL:FormValidator~~
1466 ],
1467 "widgets": [
1468 "trim_whitespace", # ~~^-> TrimWhitespace:FormWidget~~
1469 "clickable_url" # ~~^-> ClickableURL:FormWidget~~
1470 ]
1471 }
1473 # ~~->$ DepositPolicy:FormField~~
1474 DEPOSIT_POLICY = {
1475 "name": "deposit_policy",
1476 "label": lazy_gettext("Does the journal have a policy allowing authors to deposit versions of their work in an "
1477 "institutional or other repository of their choice? Where is this policy recorded?"),
1478 "input": "checkbox",
1479 "multiple": True,
1480 "options": [
1481 {"display": lazy_gettext("Diadorim"), "value": "Diadorim", "subfields": ["deposit_policy_url"]},
1482 {"display": lazy_gettext("Dulcinea"), "value": "Dulcinea", "subfields": ["deposit_policy_url"]},
1483 {"display": lazy_gettext("Mir@bel"), "value": "Mir@bel", "subfields": ["deposit_policy_url"]},
1484 {"display": lazy_gettext("Open Policy Finder"), "value": "Open Policy Finder", "subfields": ["deposit_policy_url"]},
1485 {"display": lazy_gettext("Other (including publisher’s own site)"), "value": "other",
1486 "subfields": ["deposit_policy_other", "deposit_policy_url"]},
1487 {"display": lazy_gettext(HTMLString("<em>The journal has no repository policy</em>")), "value": "none", "exclusive": True}
1488 ],
1489 "help": {
1490 "long_help": [lazy_gettext("Many authors wish to deposit a copy of their paper in an institutional or other repository "
1491 "of their choice. What is the journal’s policy for this?"),
1492 lazy_gettext("""You should state your policy about the different versions of the paper:
1493 <ul style='list-style-type: none;'>
1494 <li>Submitted version</li>
1495 <li>Accepted version (Author Accepted Manuscript)</li>
1496 <li>Published version (Version of Record)</li>
1497 </ul>""")
1498 ]},
1499 "validate": [
1500 {"required": {"message": lazy_gettext("Select <strong>at least one</strong> option")}}
1501 ]
1502 }
1504 # ~~->$ DepositPolicyOther:FormField~~
1505 DEPOSIT_POLICY_OTHER = {
1506 "name": "deposit_policy_other",
1507 "label": lazy_gettext("Name of other website where policy is registered"),
1508 "input": "text",
1509 "conditional": [{"field": "deposit_policy", "value": "other"}],
1510 "validate": [
1511 {"required_if": {
1512 "field": "deposit_policy",
1513 "value": "other",
1514 "message": lazy_gettext("Enter the name of another repository policy")
1515 }
1516 }
1517 ],
1518 "asynchronous_warning": [
1519 {"warn_on_value": {"value": "None"}}
1520 ],
1521 "widgets": [
1522 "trim_whitespace" # ~~^-> TrimWhitespace:FormWidget~~
1523 ]
1524 }
1526 # ~~->$ DepositPolicyURL:FormField~~
1527 DEPOSIT_POLICY_URL = {
1528 "name": "deposit_policy_url",
1529 "label": lazy_gettext("Where can we find this information?"),
1530 "input": "text",
1531 "diff_table_context": "Repository policy",
1532 "conditional": [{"field": "deposit_policy", "value": "Diadorim"},
1533 {"field": "deposit_policy", "value": "Dulcinea"},
1534 {"field": "deposit_policy", "value": "Mir@bel"},
1535 {"field": "deposit_policy", "value": "Open Policy Finder"},
1536 {"field": "deposit_policy", "value": "other"}],
1537 "help": {
1538 "doaj_criteria": lazy_gettext("You must provide a URL"),
1539 "short_help": lazy_gettext("Provide the link to the policy in the selected directory. Or select 'Other' and provide a link to the information on your website."),
1540 "placeholder": "https://www.my-journal.com/about#repository_policy"
1541 },
1542 "validate": [
1543 "is_url" # ~~^->IsURL:FormValidator~~
1544 ],
1545 "widgets": [
1546 "trim_whitespace", # ~~^-> TrimWhitespace:FormWidget~~
1547 "clickable_url", # ~~^-> ClickableURL:FormWidget~~
1548 ],
1549 "contexts": {
1550 "public": {
1551 "validate": [
1552 {
1553 "required_if": {
1554 "field": "deposit_policy",
1555 "value": [
1556 "Diadorim",
1557 "Dulcinea",
1558 "Mir@bel",
1559 "Open Policy Finder",
1560 "other"
1561 ]
1562 }
1563 },
1564 "is_url" # ~~^->IsURL:FormValidator~~
1565 ]
1566 },
1567 "update_request": {
1568 "validate": [
1569 {
1570 "required_if": {
1571 "field": "deposit_policy",
1572 "value": [
1573 "Diadorim",
1574 "Dulcinea",
1575 "Mir@bel",
1576 "Open Policy Finder",
1577 "other"
1578 ]
1579 }
1580 },
1581 "is_url" # ~~^->IsURL:FormValidator~~
1582 ]
1583 }
1584 }
1585 }
1587 # ~~->$ PersistentIdentifiers:FormField~~
1588 PERSISTENT_IDENTIFIERS = {
1589 "name": "persistent_identifiers",
1590 "label": lazy_gettext("Persistent article identifiers used by the journal"),
1591 "input": "checkbox",
1592 "multiple": True,
1593 "hint": lazy_gettext("Select at least one"),
1594 "options": [
1595 {"display": lazy_gettext("DOIs"), "value": "DOI"},
1596 {"display": lazy_gettext("ARKs"), "value": "ARK"},
1597 {"display": lazy_gettext("Handles"), "value": "Handles"},
1598 {"display": lazy_gettext("PURLs"), "value": "PURL"},
1599 {"display": lazy_gettext("Other"), "value": "other", "subfields": ["persistent_identifiers_other"]},
1600 {"display": lazy_gettext(HTMLString("<em>The journal does not use persistent article identifiers</em>")), "value": "none",
1601 "exclusive": True}
1602 ],
1603 "help": {
1604 "long_help": [
1605 lazy_gettext("A persistent article identifier (PID) is used to find the article no matter where it is located. The most common type of PID is the digital object identifier (DOI)."),
1606 lazy_gettext("<a href='https://en.wikipedia.org/wiki/Persistent_identifier' target='_blank' rel='noopener'>Read more about PIDs.</a>")],
1607 },
1608 "validate": [
1609 {"required": {"message": lazy_gettext("Select <strong>at least one</strong> option")}}
1610 ]
1611 }
1613 # ~~->$ PersistentIdentifiersOther:FormField~~
1614 PERSISTENT_IDENTIFIERS_OTHER = {
1615 "name": "persistent_identifiers_other",
1616 "label": lazy_gettext("Other identifier"),
1617 "input": "text",
1618 "conditional": [{"field": "persistent_identifiers", "value": "other"}],
1619 "validate": [
1620 {"required_if": {
1621 "field": "persistent_identifiers",
1622 "value": "other",
1623 "message": lazy_gettext("Enter the name of another type of identifier")
1624 }
1625 }
1626 ],
1627 "asynchronous_warning": [
1628 {"warn_on_value": {"value": "None"}}
1629 ],
1630 "widgets": [
1631 "trim_whitespace" # ~~^-> TrimWhitespace:FormWidget~~
1632 ]
1633 }
1635 #######################################
1636 ## Editorial fields
1638 S2O = {
1639 "name": "s2o",
1640 "label": lazy_gettext("Subscribe to Open"),
1641 "input": "checkbox",
1642 "help": {
1643 "long_help": [
1644 lazy_gettext("Is the journal part of the <a href='https://subscribetoopencommunity.org/' target='_blank' rel='noopener'>"
1645 "Subscribe to Open</a> initiative?")],
1646 }
1647 }
1649 MIRROR = {
1650 "name": "mirror",
1651 "label": "Mirror Journal",
1652 "input": "checkbox",
1653 "help": {
1654 "long_help": ["Is the journal a Mirror Journal?"]
1655 }
1656 }
1658 OJC = {
1659 "name": "ojc",
1660 "label": "Open Journals Collective",
1661 "input": "checkbox",
1662 "help": {
1663 "long_help": [
1664 "Is the journal part of the <a href='https://openjournalscollective.org/' target='_blank' rel='noopener'>"
1665 "Open Journals Collective</a>?"],
1666 }
1667 }
1669 # FIXME: this probably shouldn't be in the admin form fieldsets, rather its own separate form
1670 # ~~->$ QuickReject:FormField~~
1671 QUICK_REJECT = {
1672 "name": "quick_reject",
1673 "label": lazy_gettext("Reason for rejection"),
1674 "input": "select",
1675 "options_fn": "quick_reject"
1676 }
1678 # ~~->$ QuickRejectDetails:FormField~~
1679 QUICK_REJECT_DETAILS = {
1680 "name": "quick_reject_details",
1681 "label": lazy_gettext("Additional info"),
1682 "input": "textarea",
1683 "help": {
1684 "long_help": [lazy_gettext("The selected reason for rejection, and any additional information you include, "
1685 "are sent to the journal contact with the rejection email.")]
1686 },
1687 "validate": [
1688 {"required_if": {"field": "quick_reject", "value": "other"}}
1689 ],
1690 }
1692 # ~~->$ Owner:FormField~~
1693 OWNER = {
1694 "name": "owner",
1695 "label": lazy_gettext("DOAJ Account"),
1696 "input": "text",
1697 "validate": [
1698 "reserved_usernames",
1699 "owner_exists"
1700 ],
1701 "widgets": [
1702 {"autocomplete": {"type": "account", "field": "id", "include": False}}, # ~~^-> Autocomplete:FormWidget~~
1703 "clickable_owner"
1704 ],
1705 "contexts": {
1706 "associate_editor": {
1707 "validate": [
1708 {"required": {"message": lazy_gettext("You must confirm the account id")}},
1709 "reserved_usernames",
1710 "owner_exists"
1711 ]
1712 }
1713 }
1714 }
1716 # ~~->$ ApplicationStatus:FormField~~
1717 APPLICATION_STATUS = {
1718 "name": "application_status",
1719 "label": lazy_gettext("Change status"),
1720 "input": "select",
1721 "options_fn": "application_statuses",
1722 "validate": [
1723 "required"
1724 ],
1725 "help": {
1726 "update_requests_diff": False,
1727 "render_error_box": False
1728 },
1729 "disabled": "application_status_disabled",
1730 "contexts": {
1731 "associate_editor": {
1732 "help": {
1733 "render_error_box": False,
1734 "short_help": lazy_gettext("Set the status to 'In Progress' to signal to the applicant that you have started your review."
1735 "Set the status to 'Completed' to alert the Editor that you have completed your review."),
1736 "update_requests_diff": False
1737 }
1738 },
1739 "editor": {
1740 "help": {
1741 "render_error_box": False,
1742 "short_help": lazy_gettext("Revert the status to 'In Progress' to signal to the Associate Editor that further work is needed."
1743 "Set the status to 'Ready' to alert the Managing Editor that you have completed your review."),
1744 "update_requests_diff": False
1745 }
1746 }
1747 },
1748 "widgets": [
1749 # When Accepted selected display. 'This journal is currently assigned to its applicant account XXXXXX. Is this the correct account for this journal?'
1750 "owner_review"
1751 ]
1752 }
1754 # ~~->$ EditorGroup:FormField~~
1755 EDITOR_GROUP = {
1756 "name": "editor_group",
1757 "label": lazy_gettext("Group"),
1758 "input": "text",
1759 "widgets": [
1760 {"autocomplete": {"type": "editor_group", "field": "name", "include": False}}
1761 # ~~^-> Autocomplete:FormWidget~~
1762 ],
1763 "contexts": {
1764 "editor": {
1765 "disabled": True
1766 },
1767 "admin": {
1768 "widgets": [
1769 {"autocomplete": {"type": "editor_group", "field": "name", "include": False}},
1770 # ~~^-> Autocomplete:FormWidget~~
1771 {"load_editors": {"field": "editor"}}
1772 ]
1773 }
1774 }
1775 }
1777 # ~~->$ Editor:FormField~~
1778 EDITOR = {
1779 "name": "editor",
1780 "label": lazy_gettext("Individual"),
1781 "input": "select",
1782 "options_fn": "editor_choices",
1783 "default": "",
1784 "validate": [
1785 {"group_member": {"group_field": "editor_group"}}
1786 ],
1787 "help": {
1788 "render_error_box": False
1789 }
1790 }
1792 # ~~->$ DiscontinuedDate:FormField~~
1793 DISCONTINUED_DATE = {
1794 "name": "discontinued_date",
1795 "label": lazy_gettext("Discontinued on"),
1796 "input": "text",
1797 "validate": [
1798 {"bigenddate": {"message": lazy_gettext("Date must be a big-end formatted date (e.g. 2020-11-23)")}},
1799 {
1800 "not_if": {
1801 "fields": [
1802 {"field": "continues"},
1803 {"field": "continued_by"}
1804 ],
1805 "message": lazy_gettext("You cannot enter both a discontinued date and continuation information.")
1806 }
1807 }
1808 ],
1809 "help": {
1810 "short_help": lazy_gettext("Please enter the discontinued date in the form YYYY-MM-DD (e.g. 2020-11-23). "
1811 "If the day of the month is not known, please use '01' (e.g. 2020-11-01)"),
1812 "render_error_box": False
1813 }
1814 }
1816 # ~~->$ Continues:FormField~~
1817 CONTINUES = {
1818 "name": "continues",
1819 "label": lazy_gettext("Continues an <strong>older</strong> journal with the ISSN(s)"),
1820 "input": "taglist",
1821 "validate": [
1822 {"is_issn_list": {"message": lazy_gettext("This is not a valid ISSN")}}, # ~~^-> IsISSN:FormValidator~~
1823 {"different_to": {"field": "continued_by",
1824 "message": lazy_gettext("The ISSN provided in both fields must be different. Please make sure to enter the ISSN of an older journal for the first field and the ISSN of a newer journal for the second field. They cannot be the same.")}},
1825 # ~~^-> DifferetTo:FormValidator~~
1826 {
1827 "not_if": {
1828 "fields": [{"field": "discontinued_date"}],
1829 "message": lazy_gettext("You cannot enter both continuation information and a discontinued date")
1830 }
1831 }
1832 ],
1833 "widgets": [
1834 "click_to_copy", # ~~^-> ClickToCopy:FormWidget~~
1835 "full_contents", # ~~^->FullContents:FormWidget~~
1836 "tagentry" # ~~-> TagEntry:FormWidget~~
1837 ],
1838 "help": {
1839 "short_help": lazy_gettext("Enter the ISSN(s) of the previous title(s) of this journal."),
1840 "render_error_box": False
1841 }
1842 }
1844 # ~~->$ ContinuedBy:FormField~~
1845 CONTINUED_BY = {
1846 "name": "continued_by",
1847 "label": lazy_gettext("Continued by a <strong>newer</strong> journal with the ISSN(s)"),
1848 "input": "taglist",
1849 "validate": [
1850 {"is_issn_list": {"message": lazy_gettext("This is not a valid ISSN")}}, # ~~^-> IsISSN:FormValidator~~
1851 {"different_to": {"field": "continues",
1852 "message": lazy_gettext("The ISSN provided in both fields must be different. Please make sure to enter the ISSN of an older journal for the first field and the ISSN of a newer journal for the second field. They cannot be the same.")}},
1853 # ~~^-> DifferetTo:FormValidator~~
1854 {
1855 "not_if": {
1856 "fields": [{"field": "discontinued_date"}],
1857 "message": lazy_gettext("You cannot enter both continuation information and a discontinued date")
1858 }
1859 }
1860 ],
1861 "help": {
1862 "short_help": lazy_gettext("Enter the ISSN(s) of the later title(s) that continue this publication."),
1863 "render_error_box": False
1864 },
1865 "widgets": [
1866 "click_to_copy", # ~~^-> ClickToCopy:FormWidget~~
1867 "full_contents", # ~~^->FullContents:FormWidget~~
1868 "tagentry" # ~~-> TagEntry:FormWidget~~
1869 ]
1870 }
1872 # ~~->$ Subject:FormField~~
1873 SUBJECT = {
1874 "name": "subject",
1875 "label": lazy_gettext("Assign one or a maximum of two subject classifications"),
1876 "input": "taglist",
1877 "help": {
1878 "short_help": lazy_gettext("Selecting a subject will not automatically select its sub-categories"),
1879 "render_error_box": False,
1880 },
1881 "validate": [
1882 {"required_if": {
1883 "field": "application_status",
1884 "value": [
1885 constants.APPLICATION_STATUS_READY,
1886 constants.APPLICATION_STATUS_COMPLETED,
1887 constants.APPLICATION_STATUS_ACCEPTED
1888 ],
1889 "message": lazy_gettext("This field is required when setting the Application Status to %(y)s, %(z)s or %(a)s",
1890 y=constants.APPLICATION_STATUS_READY,
1891 z=constants.APPLICATION_STATUS_COMPLETED,
1892 a=constants.APPLICATION_STATUS_ACCEPTED
1893 )
1894 }
1895 }
1896 ],
1897 "widgets": [
1898 "subject_tree"
1899 ],
1900 "contexts": {
1901 "associate_editor": {
1902 "validate": [
1903 "required"
1904 ]
1905 }
1906 }
1907 }
1909 # ~~->$ Notes:FormField~~
1910 NOTES = {
1911 "name": "notes",
1912 "input": "group",
1913 "label": lazy_gettext("Notes"),
1914 "repeatable": {
1915 "initial": 1,
1916 "add_button_placement": "top"
1917 },
1918 "subfields": [
1919 "note_author",
1920 "note_date",
1921 "note",
1922 "note_id",
1923 "note_author_id",
1924 ],
1925 "template": templates.AF_LIST,
1926 "entry_template": templates.AF_ENTRY_GOUP,
1927 "widgets": [
1928 {"infinite_repeat": {"enable_on_repeat": ["textarea"]}},
1929 "note_modal",
1930 ],
1931 "merge_disabled": "merge_disabled_notes",
1932 }
1934 # ~~->$ Note:FormField~~
1935 NOTE = {
1936 "subfield": True,
1937 "name": "note",
1938 "group": "notes",
1939 "input": "textarea",
1940 "disabled": "disable_edit_note_except_editing_user",
1941 }
1943 # ~~->$ NoteAuthor:FormField~~
1944 NOTE_AUTHOR = {
1945 "subfield": True,
1946 "name": "note_author",
1947 "group": "notes",
1948 "input": "text",
1949 "disabled": True
1950 }
1952 # ~~->$ NoteDate:FormField~~
1953 NOTE_DATE = {
1954 "subfield": True,
1955 "name": "note_date",
1956 "group": "notes",
1957 "input": "text",
1958 "disabled": True
1959 }
1961 # ~~->$ NoteID:FormField~~
1962 NOTE_ID = {
1963 "subfield": True,
1964 "name": "note_id",
1965 "group": "notes",
1966 "input": "hidden"
1967 }
1969 # ~~->$ NoteAuthorID:FormField~~
1970 NOTE_AUTHOR_ID = {
1971 "subfield": True,
1972 "name": "note_author_id",
1973 "group": "notes",
1974 "input": "hidden"
1975 }
1977 FLAGS = {
1978 "name": "flags",
1979 "input": "group",
1980 "label": "Flags",
1981 "repeatable": {
1982 "initial": 2,
1983 "add_button_placement": "top",
1984 "add_field_permission": ["admin"]
1985 },
1986 "subfields": [
1987 "flag_setter",
1988 "flag_created_date",
1989 "flag_assignee",
1990 "flag_deadline",
1991 "flag_note",
1992 "flag_note_id",
1993 "flag_resolved"
1994 ],
1995 "template": templates.FLAGS_LIST,
1996 "entry_template": templates.FLAG_ENTRY_GROUP,
1997 "widgets": [
1998 "multiple_field",
1999 "flag_manager"
2000 ],
2001 "merge_disabled": "merge_disabled_notes"
2002 }
2004 FLAG_RESOLVED = {
2005 "subfield": True,
2006 "name": "flag_resolved",
2007 "group": "flags",
2008 "input": "hidden",
2009 }
2011 # ~~->$ NoteAuthor:FormField~~
2012 FLAG_SETTER = {
2013 "subfield": True,
2014 "name": "flag_setter",
2015 "group": "flags",
2016 "input": "hidden",
2017 "disabled": True
2018 }
2020 # ~~->$ NoteDate:FormField~~
2021 FLAG_CREATED_DATE = {
2022 "subfield": True,
2023 "name": "flag_created_date",
2024 "group": "flags",
2025 "input": "hidden",
2026 "disabled": True
2027 }
2029 FLAG_DEADLINE = {
2030 "subfield": True,
2031 "optional": True,
2032 "label": "Deadline",
2033 "name": "flag_deadline",
2034 "validate": [
2035 {"bigenddate": {"message": "This must be a valid date in the BigEnd format (YYYY-MM-DD)"}}
2036 ],
2037 "help": {
2038 "placeholder": "deadline (YYYY-MM-DD)",
2039 "render_error_box": True,
2040 "warning_message": Messages.FORMS_APPLICATION_FLAG__PAST_DEADLINE_WARNING
2041 },
2042 "group": "flags",
2043 "input": "text",
2044 }
2046 FLAG_NOTE = {
2047 "subfield": True,
2048 "name": "flag_note",
2049 "group": "flags",
2050 "input": "textarea",
2051 }
2053 # ~~->$ NoteID:FormField~~
2054 FLAG_NOTE_ID = {
2055 "subfield": True,
2056 "name": "flag_note_id",
2057 "group": "flags",
2058 "input": "hidden"
2059 }
2061 FLAG_ASSIGNEE = {
2062 "subfield": True,
2063 "name": "flag_assignee",
2064 "label": "Assign a user",
2065 "help": {
2066 "placeholder": "assigned_to",
2067 "short_help": "A Flag must be assigned to a user. The Flag not assigned to a user will be automatically converted to a note",
2068 },
2069 "group": "flags",
2070 "validate": [
2071 "reserved_usernames",
2072 "owner_exists"
2073 ],
2074 "widgets": [
2075 {"autocomplete": {"type": "admin", "include": False, "allow_clear_input": False}},
2076 # ~~^-> Autocomplete:FormWidget~~
2077 ],
2078 "input": "text",
2079 }
2081 # ~~->$ OptionalValidation:FormField~~
2082 OPTIONAL_VALIDATION = {
2083 "name": "make_all_fields_optional",
2084 "label": lazy_gettext("Allow save without validation"),
2085 "input": "checkbox",
2086 "widget": {
2087 "optional_validation"
2088 }
2089 }
2091 LAST_FULL_REVIEW = {
2092 "optional": True,
2093 "label": "Last Full Review Date",
2094 "name": "last_full_review",
2095 "validate": [
2096 {"bigenddate": {"message": "This must be a valid date in the BigEnd format (YYYY-MM-DD)"}},
2097 {"date_in_the_past": {"message": "The date must be in the past"}}
2098 ],
2099 "help": {
2100 "placeholder": "last full review (YYYY-MM-DD)",
2101 "render_error_box": True,
2102 "short_help": "If you have just completed a full review of this Journal, enter the date here."
2103 },
2104 "input": "text", # although this is a date, the text input is the best one to use because the widget will force that type anyway
2105 "widgets": [
2106 {"date_picker": {"earlier_than_now": True}} # ~~^-> DatePicker:FormWidget~~
2107 ]
2108 }
2111##########################################################
2112# Define our fieldsets
2113##########################################################
2115class FieldSetDefinitions:
2116 # ~~->$ BasicCompliance:FieldSet~~
2117 BASIC_COMPLIANCE = {
2118 "name": "basic_compliance",
2119 "label": lazy_gettext("Open access compliance"),
2120 "fields": [
2121 FieldDefinitions.BOAI["name"],
2122 FieldDefinitions.OA_STATEMENT_URL["name"],
2123 FieldDefinitions.OA_START["name"]
2124 ]
2125 }
2127 # ~~->$ AboutJournal:FieldSet~~
2128 ABOUT_THE_JOURNAL = {
2129 "name": "about_the_journal",
2130 "label": lazy_gettext("About the journal"),
2131 "fields": [
2132 FieldDefinitions.TITLE["name"],
2133 FieldDefinitions.ALTERNATIVE_TITLE["name"],
2134 FieldDefinitions.JOURNAL_URL["name"],
2135 FieldDefinitions.PISSN["name"],
2136 FieldDefinitions.EISSN["name"],
2137 FieldDefinitions.KEYWORDS["name"],
2138 FieldDefinitions.LANGUAGE["name"]
2139 ]
2140 }
2142 # ~~->$ Publisher:FieldSet~~
2143 PUBLISHER = {
2144 "name": "publisher",
2145 "label": lazy_gettext("Publisher"),
2146 "fields": [
2147 FieldDefinitions.PUBLISHER_NAME["name"],
2148 FieldDefinitions.PUBLISHER_COUNTRY["name"],
2149 ]
2150 }
2152 # ~~->$ Institution:FieldSet~~
2153 SOCIETY_OR_INSTITUTION = {
2154 "name": "society_or_institution",
2155 "label": lazy_gettext("Other organisation, if applicable"),
2156 "fields": [
2157 FieldDefinitions.INSTITUTION_NAME["name"],
2158 FieldDefinitions.INSTITUTION_COUNTRY["name"]
2159 ]
2160 }
2162 # ~~->$ Licensing:FieldSet~~
2163 LICENSING = {
2164 "name": "licensing",
2165 "label": lazy_gettext("Licensing"),
2166 "fields": [
2167 FieldDefinitions.LICENSE["name"],
2168 FieldDefinitions.LICENSE_ATTRIBUTES["name"],
2169 FieldDefinitions.LICENSE_TERMS_URL["name"]
2170 ]
2171 }
2173 # ~~->$ EmbeddedLicense:FieldSet~~
2174 EMBEDDED_LICENSING = {
2175 "name": "embedded_licensing",
2176 "label": lazy_gettext("Embedded licenses"),
2177 "fields": [
2178 FieldDefinitions.LICENSE_DISPLAY["name"],
2179 FieldDefinitions.LICENSE_DISPLAY_EXAMPLE_URL["name"]
2180 ]
2181 }
2183 # ~~->$ Copyright:FieldSet~~
2184 COPYRIGHT = {
2185 "name": "copyright",
2186 "label": lazy_gettext("Copyright"),
2187 "fields": [
2188 FieldDefinitions.COPYRIGHT_AUTHOR_RETAINS["name"],
2189 FieldDefinitions.COPYRIGHT_URL["name"]
2190 ]
2191 }
2193 # ~~->$ PeerReview:FieldSet~~
2194 PEER_REVIEW = {
2195 "name": "peer_review",
2196 "label": lazy_gettext("Peer review"),
2197 "fields": [
2198 FieldDefinitions.REVIEW_PROCESS["name"],
2199 FieldDefinitions.REVIEW_PROCESS_OTHER["name"],
2200 FieldDefinitions.REVIEW_URL["name"]
2201 ]
2202 }
2204 # ~~->$ Plagiarism:FieldSet~~
2205 PLAGIARISM = {
2206 "name": "plagiarism",
2207 "label": lazy_gettext("Plagiarism"),
2208 "fields": [
2209 FieldDefinitions.PLAGIARISM_DETECTION["name"],
2210 FieldDefinitions.PLAGIARISM_URL["name"]
2211 ]
2212 }
2214 # ~~->$ Editorial:FieldSet~~
2215 EDITORIAL = {
2216 "name": "editorial",
2217 "label": lazy_gettext("Editorial"),
2218 "fields": [
2219 FieldDefinitions.AIMS_SCOPE_URL["name"],
2220 FieldDefinitions.EDITORIAL_BOARD_URL["name"],
2221 FieldDefinitions.AUTHOR_INSTRUCTIONS_URL["name"],
2222 FieldDefinitions.PUBLICATION_TIME_WEEKS["name"]
2223 ]
2224 }
2226 # ~~->$ APC:FieldSet~~
2227 APC = {
2228 "name": "apc",
2229 "label": lazy_gettext("Publication fees"),
2230 "fields": [
2231 FieldDefinitions.APC["name"],
2232 FieldDefinitions.APC_CHARGES["name"],
2233 FieldDefinitions.APC_CURRENCY["name"],
2234 FieldDefinitions.APC_MAX["name"],
2235 FieldDefinitions.APC_URL["name"]
2236 ]
2237 }
2239 # ~~->$ Waivers:FieldSet~~
2240 APC_WAIVERS = {
2241 "name": "apc_waivers",
2242 "label": lazy_gettext("Publication fee waivers"),
2243 "fields": [
2244 FieldDefinitions.HAS_WAIVER["name"],
2245 FieldDefinitions.WAIVER_URL["name"]
2246 ]
2247 }
2249 # ~~->$ OtherFees:FieldSet~~
2250 OTHER_FEES = {
2251 "name": "other_fees",
2252 "label": lazy_gettext("Other fees"),
2253 "fields": [
2254 FieldDefinitions.HAS_OTHER_CHARGES["name"],
2255 FieldDefinitions.OTHER_CHARGES_URL["name"]
2256 ]
2257 }
2259 # ~~->$ ArchivingPolicy:FieldSet~~
2260 ARCHIVING_POLICY = {
2261 "name": "archiving_policy",
2262 "label": lazy_gettext("Archiving policy"),
2263 "fields": [
2264 FieldDefinitions.PRESERVATION_SERVICE["name"],
2265 FieldDefinitions.PRESERVATION_SERVICE_LIBRARY["name"],
2266 FieldDefinitions.PRESERVATION_SERVICE_OTHER["name"],
2267 FieldDefinitions.PRESERVATION_SERVICE_URL["name"]
2268 ]
2269 }
2271 # ~~->$ RepositoryPolicy:FieldSet~~
2272 REPOSITORY_POLICY = {
2273 "name": "deposit_policy",
2274 "label": lazy_gettext("Repository policy"),
2275 "fields": [
2276 FieldDefinitions.DEPOSIT_POLICY["name"],
2277 FieldDefinitions.DEPOSIT_POLICY_OTHER["name"],
2278 FieldDefinitions.DEPOSIT_POLICY_URL["name"]
2279 ]
2280 }
2282 # ~~->$ UniqueIdentifiers:FieldSet~~
2283 UNIQUE_IDENTIFIERS = {
2284 "name": "unique_identifiers",
2285 "label": lazy_gettext("Unique identifiers & structured data"),
2286 "fields": [
2287 FieldDefinitions.PERSISTENT_IDENTIFIERS["name"],
2288 FieldDefinitions.PERSISTENT_IDENTIFIERS_OTHER["name"]
2289 ]
2290 }
2293 LABELS = {
2294 "name": "labels",
2295 "label": lazy_gettext("Specify labels for this journal"),
2296 "fields": [
2297 FieldDefinitions.S2O["name"],
2298 FieldDefinitions.MIRROR["name"],
2299 FieldDefinitions.OJC["name"]
2300 ]
2301 }
2303 # ~~->$ QuickReject:FieldSet~~
2304 # ~~^-> QuickReject:Feature~~
2305 QUICK_REJECT = {
2306 "name": "quick_reject",
2307 "label": lazy_gettext("Quick reject"),
2308 "fields": [
2309 FieldDefinitions.QUICK_REJECT["name"],
2310 FieldDefinitions.QUICK_REJECT_DETAILS["name"]
2311 ]
2312 }
2314 # ~~->$ Reassign:FieldSet~~
2315 REASSIGN = {
2316 "name": "reassign",
2317 "label": lazy_gettext("Re-assign publisher account"),
2318 "fields": [
2319 FieldDefinitions.OWNER["name"]
2320 ]
2321 }
2323 LAST_FULL_REVIEW = {
2324 "name": "last_full_review",
2325 "label": "Last Full Review",
2326 "fields": [
2327 FieldDefinitions.LAST_FULL_REVIEW["name"]
2328 ]
2329 }
2331 # ~~->$ Status:FieldSet~~
2332 STATUS = {
2333 "name": "status",
2334 "label": lazy_gettext("Status"),
2335 "fields": [
2336 FieldDefinitions.APPLICATION_STATUS["name"]
2337 ]
2338 }
2340 # ~~->$ Reviewers:FieldSet~~
2341 REVIEWERS = {
2342 "name": "reviewers",
2343 "label": lazy_gettext("Assign for review"),
2344 "fields": [
2345 FieldDefinitions.EDITOR_GROUP["name"],
2346 FieldDefinitions.EDITOR["name"]
2347 ]
2348 }
2350 # ~~->$ Continuations:FieldSet~~
2351 # ~~^-> Continuations:Feature~~
2352 CONTINUATIONS = {
2353 "name": "continuations",
2354 "label": lazy_gettext("Continuations"),
2355 "fields": [
2356 FieldDefinitions.CONTINUES["name"],
2357 FieldDefinitions.CONTINUED_BY["name"],
2358 FieldDefinitions.DISCONTINUED_DATE["name"]
2359 ]
2360 }
2362 # ~~->$ Subject:FieldSet~~
2363 SUBJECT = {
2364 "name": "subject",
2365 "label": lazy_gettext("Subject classification"),
2366 "fields": [
2367 FieldDefinitions.SUBJECT["name"]
2368 ]
2369 }
2371 # ~~->$ Notes:FieldSet~~
2372 NOTES = {
2373 "name": "notes",
2374 "label": lazy_gettext("Notes"),
2375 "fields": [
2376 FieldDefinitions.NOTES["name"],
2377 FieldDefinitions.NOTE["name"],
2378 FieldDefinitions.NOTE_AUTHOR["name"],
2379 FieldDefinitions.NOTE_DATE["name"],
2380 FieldDefinitions.NOTE_ID["name"],
2381 FieldDefinitions.NOTE_AUTHOR_ID["name"]
2382 ]
2383 }
2385 FLAGS = {
2386 "name": "flags",
2387 "label": "Flag",
2388 "fields": [
2389 FieldDefinitions.FLAGS["name"],
2390 FieldDefinitions.FLAG_SETTER["name"],
2391 FieldDefinitions.FLAG_CREATED_DATE["name"],
2392 FieldDefinitions.FLAG_DEADLINE["name"],
2393 FieldDefinitions.FLAG_NOTE["name"],
2394 FieldDefinitions.FLAG_NOTE_ID["name"],
2395 FieldDefinitions.FLAG_ASSIGNEE["name"],
2396 FieldDefinitions.FLAG_RESOLVED["name"],
2397 ]
2398 }
2400 # ~~->$ OptionalValidation:FieldSet~~
2401 OPTIONAL_VALIDATION = {
2402 "name": "optional_validation",
2403 "label": lazy_gettext("Allow save without validation"),
2404 "fields": [
2405 FieldDefinitions.OPTIONAL_VALIDATION["name"]
2406 ]
2407 }
2409 # ~~->$ BulkEdit:FieldSet~~
2410 # ~~^-> BulkEdit:Feature~~
2411 BULK_EDIT = {
2412 "name": "bulk_edit",
2413 "label": lazy_gettext("Bulk edit"),
2414 "fields": [
2415 FieldDefinitions.PUBLISHER_NAME["name"],
2416 FieldDefinitions.PUBLISHER_COUNTRY["name"],
2417 FieldDefinitions.OWNER["name"]
2418 ]
2419 }
2422###########################################################
2423# Define our Contexts
2424###########################################################
2426class ApplicationContextDefinitions:
2427 # ~~->$ NewApplication:FormContext~~
2428 # ~~^-> ApplicationForm:Crosswalk~~
2429 # ~~^-> NewApplication:FormProcessor~~
2430 PUBLIC = {
2431 "name": "public",
2432 "fieldsets": [
2433 FieldSetDefinitions.BASIC_COMPLIANCE["name"],
2434 FieldSetDefinitions.ABOUT_THE_JOURNAL["name"],
2435 FieldSetDefinitions.PUBLISHER["name"],
2436 FieldSetDefinitions.SOCIETY_OR_INSTITUTION["name"],
2437 FieldSetDefinitions.LICENSING["name"],
2438 FieldSetDefinitions.EMBEDDED_LICENSING["name"],
2439 FieldSetDefinitions.COPYRIGHT["name"],
2440 FieldSetDefinitions.PEER_REVIEW["name"],
2441 FieldSetDefinitions.PLAGIARISM["name"],
2442 FieldSetDefinitions.EDITORIAL["name"],
2443 FieldSetDefinitions.APC["name"],
2444 FieldSetDefinitions.APC_WAIVERS["name"],
2445 FieldSetDefinitions.OTHER_FEES["name"],
2446 FieldSetDefinitions.ARCHIVING_POLICY["name"],
2447 FieldSetDefinitions.REPOSITORY_POLICY["name"],
2448 FieldSetDefinitions.UNIQUE_IDENTIFIERS["name"]
2449 ],
2450 "templates": {
2451 "form": templates.PUBLIC_APPLICATION_FORM,
2452 "default_field": templates.AF_FIELD,
2453 "default_group": templates.AF_GROUP
2454 },
2455 "crosswalks": {
2456 "obj2form": ApplicationFormXWalk.obj2form,
2457 "form2obj": ApplicationFormXWalk.form2obj
2458 },
2459 "processor": application_processors.NewApplication,
2460 }
2462 # ~~->$ UpdateRequest:FormContext~~
2463 # ~~^-> NewApplication:FormContext~~
2464 # ~~^-> UpdateRequest:FormProcessor~~
2465 UPDATE = deepcopy(PUBLIC)
2466 UPDATE["name"] = "update_request"
2467 UPDATE["processor"] = application_processors.PublisherUpdateRequest
2468 UPDATE["templates"]["form"] = templates.PUBLISHER_UPDATE_REQUEST_FORM
2470 # ~~->$ ReadOnlyApplication:FormContext~~
2471 # ~~^-> NewApplication:FormContext~~
2472 READ_ONLY = deepcopy(PUBLIC)
2473 READ_ONLY["name"] = "application_read_only"
2474 READ_ONLY["processor"] = application_processors.NewApplication # FIXME: enter the real processor
2475 READ_ONLY["templates"]["form"] = templates.PUBLISHER_READ_ONLY_APPLICATION
2477 # ~~->$ AssociateEditorApplication:FormContext~~
2478 # ~~^-> NewApplication:FormContext~~
2479 # ~~^-> AssociateEditorApplication:FormProcessor~~
2480 ASSOCIATE = deepcopy(PUBLIC)
2481 ASSOCIATE["name"] = "associate_editor"
2482 ASSOCIATE["fieldsets"] += [
2483 FieldSetDefinitions.STATUS["name"],
2484 FieldSetDefinitions.SUBJECT["name"],
2485 FieldSetDefinitions.NOTES["name"]
2486 ]
2487 ASSOCIATE["processor"] = application_processors.AssociateApplication
2488 ASSOCIATE["templates"]["form"] = templates.ASSED_APPLICATION_FORM
2490 # ~~->$ EditorApplication:FormContext~~
2491 # ~~^-> NewApplication:FormContext~~
2492 # ~~^-> EditorApplication:FormProcessor~~
2493 EDITOR = deepcopy(PUBLIC)
2494 EDITOR["name"] = "editor"
2495 EDITOR["fieldsets"] += [
2496 FieldSetDefinitions.STATUS["name"],
2497 FieldSetDefinitions.REVIEWERS["name"],
2498 FieldSetDefinitions.SUBJECT["name"],
2499 FieldSetDefinitions.NOTES["name"]
2500 ]
2501 EDITOR["processor"] = application_processors.EditorApplication
2502 EDITOR["templates"]["form"] = templates.EDITOR_APPLICATION_FORM
2504 # ~~->$ ManEdApplication:FormContext~~
2505 # ~~^-> NewApplication:FormContext~~
2506 # ~~^-> ManEdApplication:FormProcessor~~
2507 MANED = deepcopy(PUBLIC)
2508 MANED["name"] = "admin"
2509 MANED["fieldsets"] += [
2510 FieldSetDefinitions.LABELS["name"],
2511 FieldSetDefinitions.QUICK_REJECT["name"],
2512 FieldSetDefinitions.REASSIGN["name"],
2513 FieldSetDefinitions.STATUS["name"],
2514 FieldSetDefinitions.REVIEWERS["name"],
2515 FieldSetDefinitions.CONTINUATIONS["name"],
2516 FieldSetDefinitions.SUBJECT["name"],
2517 FieldSetDefinitions.NOTES["name"],
2518 ]
2519 MANED["processor"] = application_processors.AdminApplication
2520 MANED["templates"]["form"] = templates.MANED_APPLICATION_FORM
2523class JournalContextDefinitions:
2524 # ~~->$ ReadOnlyJournal:FormContext~~
2525 # ~~^-> JournalForm:Crosswalk~~
2526 # ~~^-> ReadOnlyJournal:FormProcessor~~
2527 ADMIN_READ_ONLY = {
2528 "name": "admin_readonly",
2529 "fieldsets": [
2530 FieldSetDefinitions.BASIC_COMPLIANCE["name"],
2531 FieldSetDefinitions.ABOUT_THE_JOURNAL["name"],
2532 FieldSetDefinitions.PUBLISHER["name"],
2533 FieldSetDefinitions.SOCIETY_OR_INSTITUTION["name"],
2534 FieldSetDefinitions.LICENSING["name"],
2535 FieldSetDefinitions.EMBEDDED_LICENSING["name"],
2536 FieldSetDefinitions.COPYRIGHT["name"],
2537 FieldSetDefinitions.PEER_REVIEW["name"],
2538 FieldSetDefinitions.PLAGIARISM["name"],
2539 FieldSetDefinitions.EDITORIAL["name"],
2540 FieldSetDefinitions.APC["name"],
2541 FieldSetDefinitions.APC_WAIVERS["name"],
2542 FieldSetDefinitions.OTHER_FEES["name"],
2543 FieldSetDefinitions.ARCHIVING_POLICY["name"],
2544 FieldSetDefinitions.REPOSITORY_POLICY["name"],
2545 FieldSetDefinitions.UNIQUE_IDENTIFIERS["name"]
2546 ],
2547 "templates": {
2548 "form": templates.MANED_READ_ONLY_JOURNAL,
2549 "default_field": templates.AF_FIELD,
2550 "default_group": templates.AF_GROUP
2551 },
2552 "crosswalks": {
2553 "obj2form": JournalFormXWalk.obj2form,
2554 "form2obj": JournalFormXWalk.form2obj
2555 },
2556 "processor": application_processors.ReadOnlyJournal
2557 }
2559 # identical context for editors, mostly to support the different view contexts
2560 EDITOR_READ_ONLY = deepcopy(ADMIN_READ_ONLY)
2561 EDITOR_READ_ONLY["name"] = "editor_readonly"
2562 EDITOR_READ_ONLY["templates"]["form"] = templates.EDITOR_READ_ONLY_JOURNAL
2564 # ~~->$ AssEditorJournal:FormContext~~
2565 # ~~^-> ReadOnlyJournal:FormContext~~
2566 # ~~^-> AssEdJournal:FormProcessor~~
2567 ASSOCIATE = deepcopy(ADMIN_READ_ONLY)
2568 ASSOCIATE["fieldsets"] += [
2569 FieldSetDefinitions.SUBJECT["name"],
2570 FieldSetDefinitions.NOTES["name"]
2571 ]
2572 ASSOCIATE["name"] = "associate_editor"
2573 ASSOCIATE["processor"] = application_processors.AssEdJournalReview
2574 ASSOCIATE["templates"]["form"] = templates.ASSED_JOURNAL_FORM
2576 # ~~->$ EditorJournal:FormContext~~
2577 # ~~^-> AssEdJournal:FormContext~~
2578 # ~~^-> EditorJournal:FormProcessor~~
2579 EDITOR = deepcopy(ASSOCIATE)
2580 EDITOR["name"] = "editor"
2581 EDITOR["fieldsets"] += [
2582 FieldSetDefinitions.REVIEWERS["name"]
2583 ]
2584 EDITOR["processor"] = application_processors.EditorJournalReview
2585 EDITOR["templates"]["form"] = templates.EDITOR_JOURNAL_FORM
2587 # ~~->$ ManEdJournal:FormContext~~
2588 # ~~^-> EditorJournal:FormContext~~
2589 # ~~^-> ManEdJournal:FormProcessor~~
2590 MANED = deepcopy(EDITOR)
2591 MANED["name"] = "admin"
2592 MANED["fieldsets"] += [
2593 FieldSetDefinitions.REASSIGN["name"],
2594 FieldSetDefinitions.OPTIONAL_VALIDATION["name"],
2595 FieldSetDefinitions.LABELS["name"],
2596 FieldSetDefinitions.CONTINUATIONS["name"],
2597 FieldSetDefinitions.FLAGS["name"],
2598 FieldSetDefinitions.LAST_FULL_REVIEW["name"]
2599 ]
2600 MANED["processor"] = application_processors.ManEdJournalReview
2601 MANED["templates"]["form"] = templates.MANED_JOURNAL_FORM
2603 # ~~->$ BulkEditJournal:FormContext~~
2604 # ~~^-> JournalForm:Crosswalk~~
2605 # ~~^-> ManEdJournal:FormProcessor~~
2606 BULK_EDIT = {
2607 "name": "bulk_edit",
2608 "fieldsets": [
2609 FieldSetDefinitions.BULK_EDIT["name"]
2610 ],
2611 "templates": {
2612 "form": templates.MANED_JOURNAL_BULK_EDIT,
2613 "default_field": templates.AF_FIELD,
2614 "default_group": templates.AF_GROUP
2615 },
2616 "crosswalks": {
2617 "obj2form": JournalFormXWalk.obj2form,
2618 "form2obj": JournalFormXWalk.form2obj
2619 },
2620 "processor": application_processors.ManEdBulkEdit
2621 }
2624#######################################################
2625# Gather all of our form information in one place
2626#######################################################
2628APPLICATION_FORMS = {
2629 "contexts": {
2630 ApplicationContextDefinitions.PUBLIC["name"]: ApplicationContextDefinitions.PUBLIC,
2631 ApplicationContextDefinitions.UPDATE["name"]: ApplicationContextDefinitions.UPDATE,
2632 ApplicationContextDefinitions.READ_ONLY["name"]: ApplicationContextDefinitions.READ_ONLY,
2633 ApplicationContextDefinitions.ASSOCIATE["name"]: ApplicationContextDefinitions.ASSOCIATE,
2634 ApplicationContextDefinitions.EDITOR["name"]: ApplicationContextDefinitions.EDITOR,
2635 ApplicationContextDefinitions.MANED["name"]: ApplicationContextDefinitions.MANED
2636 },
2637 "fieldsets": {v['name']: v for k, v in FieldSetDefinitions.__dict__.items() if not k.startswith('_')},
2638 "fields": {v['name']: v for k, v in FieldDefinitions.__dict__.items() if not k.startswith('_')}
2639}
2641JOURNAL_FORMS = {
2642 "contexts": {
2643 JournalContextDefinitions.ADMIN_READ_ONLY["name"]: JournalContextDefinitions.ADMIN_READ_ONLY,
2644 JournalContextDefinitions.EDITOR_READ_ONLY["name"]: JournalContextDefinitions.EDITOR_READ_ONLY,
2645 JournalContextDefinitions.BULK_EDIT["name"]: JournalContextDefinitions.BULK_EDIT,
2646 JournalContextDefinitions.ASSOCIATE["name"]: JournalContextDefinitions.ASSOCIATE,
2647 JournalContextDefinitions.EDITOR["name"]: JournalContextDefinitions.EDITOR,
2648 JournalContextDefinitions.MANED["name"]: JournalContextDefinitions.MANED
2649 },
2650 "fieldsets": APPLICATION_FORMS["fieldsets"],
2651 "fields": APPLICATION_FORMS["fields"]
2652}
2655#######################################################
2656# Options lists
2657#######################################################
2659def iso_country_list(field, formualic_context_name):
2660 # ~~-> Countries:Data~~
2661 cl = [{"display": " ", "value": ""}]
2662 for v, d in country_options:
2663 cl.append({"display": d, "value": v})
2664 return cl
2667def iso_language_list(field, formulaic_context_name):
2668 # ~~-> Languages:Data~~
2669 cl = [{"display": " ", "value": ""}]
2670 for v, d in language_options:
2671 cl.append({"display": d, "value": v})
2672 return cl
2675def iso_currency_list(field, formulaic_context_name):
2676 # ~~-> Currencies:Data~~
2677 cl = [{"display": " ", "value": ""}]
2678 quick_pick = []
2679 for v, d in currency_options:
2680 if v in ["GBP", "USD", "EUR"]:
2681 quick_pick.append({"display": d, "value": v})
2682 cl.append({"display": d, "value": v})
2683 if len(quick_pick) > 0:
2684 cl = quick_pick + cl
2685 return cl
2688def quick_reject(field, formulaic_context_name):
2689 # ~~-> QuickReject:Feature~~
2690 return [{"display": lazy_gettext("Other"), "value": ""}] + [{'display': v, 'value': v} for v in
2691 app.config.get('QUICK_REJECT_REASONS', [])]
2694def application_statuses(field, formulaic_context):
2695 # ~~->$ ApplicationStatus:Workflow~~
2696 # ~~-> ApplicationStatuses:Config~~
2697 _application_status_base = [ # This is all the Associate Editor sees
2698 ('', ' '),
2699 (constants.APPLICATION_STATUS_PENDING, Messages.FORMS__APPLICATION_STATUS__PENDING),
2700 (constants.APPLICATION_STATUS_IN_PROGRESS, Messages.FORMS__APPLICATION_STATUS__IN_PROGRESS),
2701 (constants.APPLICATION_STATUS_COMPLETED, Messages.FORMS__APPLICATION_STATUS__COMPLETED)
2702 ]
2704 # Note that an admin is given the Post Submission Automation status, as technically they
2705 # may edit an item that's in this status, but it is functionally useless to them
2706 # It would be nice to be able to somehow disable it being changed, perhaps we can do that
2707 # via a widget
2708 _application_status_admin = _application_status_base + [
2709 (constants.APPLICATION_STATUS_POST_SUBMISSION_REVIEW,
2710 Messages.FORMS__APPLICATION_STATUS__POST_SUBMISSION_REVIEW),
2711 (constants.APPLICATION_STATUS_UPDATE_REQUEST, Messages.FORMS__APPLICATION_STATUS__UPDATE_REQUEST),
2712 (constants.APPLICATION_STATUS_REVISIONS_REQUIRED, Messages.FORMS__APPLICATION_STATUS__REVISIONS_REQUIRED),
2713 (constants.APPLICATION_STATUS_ON_HOLD, Messages.FORMS__APPLICATION_STATUS__ON_HOLD),
2714 (constants.APPLICATION_STATUS_READY, Messages.FORMS__APPLICATION_STATUS__READY),
2715 (constants.APPLICATION_STATUS_REJECTED, Messages.FORMS__APPLICATION_STATUS__REJECTED),
2716 (constants.APPLICATION_STATUS_ACCEPTED, Messages.FORMS__APPLICATION_STATUS__ACCEPTED)
2717 ]
2719 _application_status_editor = _application_status_base + [
2720 (constants.APPLICATION_STATUS_READY, Messages.FORMS__APPLICATION_STATUS__READY),
2721 ]
2723 formulaic_context_name = None
2724 if formulaic_context is not None:
2725 formulaic_context_name = formulaic_context.name
2727 status_list = []
2728 if formulaic_context_name is None or formulaic_context_name == "admin":
2729 status_list = _application_status_admin
2730 elif formulaic_context_name == "editor":
2731 status_list = _application_status_editor
2732 elif formulaic_context_name == "accepted":
2733 status_list = [(constants.APPLICATION_STATUS_ACCEPTED,
2734 Messages.FORMS__APPLICATION_STATUS__ACCEPTED)] # just the one status - Accepted
2735 else:
2736 status_list = _application_status_base
2738 return [{'display': d, 'value': v} for (v, d) in status_list]
2741def editor_choices(field, formulaic_context):
2742 """
2743 Set the editor field choices from a given editor group name
2744 ~~->EditorGroup:Model~~
2745 """
2746 egf = formulaic_context.get("editor_group")
2747 wtf = egf.wtfield
2748 if wtf is None:
2749 return [{"display": "", "value": ""}]
2751 editor_group_name = wtf.data
2752 if editor_group_name is None:
2753 return [{"display": "", "value": ""}]
2754 else:
2755 eg = EditorGroup.pull_by_key("name", editor_group_name)
2756 if eg is not None:
2757 editors = [eg.editor]
2758 editors += eg.associates
2759 editors = list(set(editors))
2760 return [{"value": "", "display": lazy_gettext("No editor assigned")}] + [{"value": editor, "display": editor} for editor
2761 in editors]
2762 else:
2763 return [{"display": "", "value": ""}]
2766#######################################################
2767## Conditional disableds
2768#######################################################
2770def application_status_disabled(field, formulaic_context):
2771 choices = application_statuses(field, formulaic_context)
2772 field_value = field.wtfield.data
2773 return field_value not in [c.get("value") for c in choices]
2776def disable_edit_note_except_editing_user(field: FormulaicField,
2777 formulaic_context: FormulaicContext):
2778 """
2779 Only allow the current user to edit this field if author is current user
2781 :param field:
2782 :param formulaic_context:
2783 :return:
2784 False is editable, True is disabled
2785 """
2787 # ~~->Notes:Feature~~
2788 editing_user = formulaic_context.extra_param.get('editing_user')
2789 cur_user_id = editing_user and editing_user.id
2790 form_field: FormField = field.find_related_form_field('notes', formulaic_context)
2791 if form_field is None:
2792 return True
2793 return cur_user_id != form_field.data.get('note_author_id')
2796def disable_edit_flag_except_author_admin_assignee(field: FormulaicField,
2797 formulaic_context: FormulaicContext):
2798 # This is currently not used but will be needed again when the flags feature will be made available for non-admin users
2800 """
2801 Only allow the current user to edit this field if current user is an author, assignee or admin
2803 :param field:
2804 :param formulaic_context:
2805 :return:
2806 False is editable, True is disabled
2807 """
2809 # ~~->Notes:Feature~~
2810 editing_user = formulaic_context.extra_param.get('editing_user')
2811 cur_user_id = editing_user and editing_user.id
2812 cur_user_is_admin = editing_user and editing_user.is_super
2813 form_field: FormField = field.find_related_form_field('notes', formulaic_context)
2814 if form_field is None:
2815 return True
2817 return (cur_user_id != form_field.data.get('flag_assignee') and
2818 cur_user_id != form_field.data.get('flag_setter') and
2819 not cur_user_is_admin)
2822#######################################################
2823## Merge disabled
2824#######################################################
2826def merge_disabled_notes(notes_group, original_form):
2827 # ~~->Notes:Feature~~
2828 merged = []
2829 wtf = notes_group.wtfield
2830 for entry in wtf.entries:
2831 if entry.data.get("note") != "" or entry.data.get("note_id") != "":
2832 merged.append(entry.data)
2833 for entry in original_form.notes.entries:
2834 d = entry.data
2835 for m in merged:
2836 if m.get("note_id") != "" and m.get("note_id") == entry.data.get("note_id"):
2837 # keep = False
2838 if m.get("note") == "":
2839 m["note"] = entry.data.get("note")
2840 m["note_date"] = entry.data.get("note_date")
2841 break
2843 while True:
2844 try:
2845 wtf.pop_entry()
2846 except IndexError:
2847 break
2849 for m in merged:
2850 wtf.append_entry(m)
2853#######################################################
2854# Validation features
2855#######################################################
2857class ReservedUsernamesBuilder:
2858 """
2859 ~~->$ ReservedUsernames:FormValidator~~
2860 """
2862 @staticmethod
2863 def render(settings, html_attrs):
2864 return
2866 @staticmethod
2867 def wtforms(field, settings):
2868 return ReservedUsernames()
2871class OwnerExistsBuilder:
2872 """
2873 ~~->$ OwnerExists:FormValidator~~
2874 """
2876 @staticmethod
2877 def render(settings, html_attrs):
2878 return
2880 @staticmethod
2881 def wtforms(field, settings):
2882 return OwnerExists()
2885class RequiredBuilder:
2886 """
2887 ~~->$ Required:FormValidator~~
2888 """
2890 @staticmethod
2891 def render(settings, html_attrs):
2892 html_attrs["required"] = ""
2893 if "message" in settings:
2894 html_attrs["data-parsley-required-message"] = "<p><small>" + settings["message"] + "</small></p>"
2895 else:
2896 html_attrs["data-parsley-required-message"] = "<p><small>" + lazy_gettext("This answer is required") + "</p></small>"
2897 html_attrs["data-parsley-validate-if-empty"] = "true"
2899 @staticmethod
2900 def wtforms(field, settings):
2901 return CustomRequired(message=settings.get("message"))
2904class IsURLBuilder:
2905 # ~~->$ IsURL:FormValidator~~
2907 @staticmethod
2908 def _get_msg():
2909 text = lazy_gettext("Please enter a valid URL. It should start with http or https")
2910 return f"<p><small>{text}</small></p>"
2912 @staticmethod
2913 def render(settings, html_attrs):
2914 html_attrs["type"] = "url"
2915 html_attrs["pattern"] = regex.HTTP_URL
2916 html_attrs["data-parsley-pattern"] = regex.HTTP_URL
2917 html_attrs["data-parsley-pattern-message"] = IsURLBuilder._get_msg()
2919 @staticmethod
2920 def wtforms(field, settings):
2921 return HTTPURL(message=settings.get('message', IsURLBuilder._get_msg()))
2924class IntRangeBuilder:
2925 """
2926 ~~->$ IntRange:FormValidator~~
2927 ~~^-> NumberRange:FormValidator~~
2928 """
2930 @staticmethod
2931 def render(settings, html_attrs):
2932 html_attrs["data-parsley-type"] = "digits"
2933 default_msg = ""
2934 if "gte" in settings and "lte" in settings:
2935 html_attrs["data-parsley-range"] = "[" + str(settings.get("gte")) + ", " + str(settings.get("lte")) + "]"
2936 default_msg = lazy_gettext("This value should be between {min} and {max}").format(
2937 min=str(settings.get("gte")), max=str(settings.get("lte")))
2938 else:
2939 if "gte" in settings:
2940 html_attrs["data-parsley-min"] = settings.get("gte")
2941 default_msg = lazy_gettext("This value should be bigger than {gte}").format(
2942 gte=str(settings.get("gte")))
2943 if "lte" in settings:
2944 html_attrs["data-parsley-max"] = settings.get("lte")
2945 default_msg = lazy_gettext("This value should be smaller than {lte}").format(
2946 lte=str(settings.get("lte")))
2947 html_attrs["data-parsley-range-message"] = "<p><small>" + settings.get("message", default_msg) + "</p></small>"
2949 @staticmethod
2950 def wtforms(field, settings):
2951 min = settings.get("gte")
2952 max = settings.get("lte")
2953 kwargs = {}
2954 if min is not None:
2955 kwargs["min"] = min
2956 if max is not None:
2957 kwargs["max"] = max
2958 return validators.NumberRange(**kwargs)
2961class MaxTagsBuilder:
2962 """
2963 ~~->$ MaxLen:FormValidator~~
2964 """
2966 @staticmethod
2967 def wtforms(field, settings):
2968 max = settings.get("max")
2969 message = settings.get("message") if "message" in settings else 'You can only enter up to {x} keywords.'.format(
2970 x=max)
2971 return MaxLen(max, message=message)
2974class StopWordsBuilder:
2975 """
2976 ~~->$ StopWords:FormValidator~~
2977 """
2979 @staticmethod
2980 def wtforms(field, settings):
2981 stopwords = settings.get("disallowed", [])
2982 return StopWords(stopwords)
2985class ISSNInPublicDOAJBuilder:
2986 """
2987 ~~->$ ISSNInPublicDOAJ:FormValidator~~
2988 """
2990 @staticmethod
2991 def render(settings, html_attrs):
2992 # FIXME: not yet implemented in the front end, so setting here is speculative
2993 html_attrs["data-parsley-issn-in-public-doaj"] = ""
2995 @staticmethod
2996 def wtforms(field, settings):
2997 return ISSNInPublicDOAJ(message=settings.get("message"))
3000class JournalURLInPublicDOAJBuilder:
3001 # ~~->$ JournalURLInPublicDOAJ:FormValidator~~
3002 @staticmethod
3003 def render(settings, html_attrs):
3004 # FIXME: not yet implemented in the front end, so setting here is speculative
3005 html_attrs["data-parsley-journal-url-in-public-doaj"] = ""
3007 @staticmethod
3008 def wtforms(field, settings):
3009 return JournalURLInPublicDOAJ(message=settings.get("message"))
3012class NoScriptTagBuilder:
3013 # ~~->$ NoScriptTag:FormValidator
3014 @staticmethod
3015 def render(settings, html_attrs):
3016 html_attrs["data-parsley-no-script-tag"] = ""
3017 if "message" in settings:
3018 html_attrs["data-parsley-no-script-tag-message"] = "<p><small>" + settings["message"] + "</small></p>"
3019 else:
3020 html_attrs["data-parsley-no-script-tag-message"] = "<p><small>" + lazy_gettext("No script tags allowed") + "</p></small>"
3022 @staticmethod
3023 def wtforms(field, settings):
3024 return NoScriptTag(settings.get("message", lazy_gettext("No script tags allowed")))
3027class OptionalIfBuilder:
3028 # ~~->$ OptionalIf:FormValidator~~
3029 @staticmethod
3030 def render(settings, html_attrs):
3031 html_attrs["data-parsley-validate-if-empty"] = "true"
3032 html_attrs["data-parsley-optional-if"] = settings.get("field")
3033 html_attrs["data-parsley-optional-if-message"] = "<p><small>" + settings.get("message") + "</p></small>"
3035 @staticmethod
3036 def wtforms(field, settings):
3037 return OptionalIf(settings.get("field") or field, settings.get("message"), settings.get("values", []))
3040class IsISSNBuilder:
3041 # ~~->$ IsISSN:FormValidator~~
3042 @staticmethod
3043 def render(settings, html_attrs):
3044 html_attrs["pattern"] = ISSN
3045 html_attrs["data-parsley-pattern"] = ISSN
3046 html_attrs["data-parsley-pattern-message"] = settings.get("message")
3048 @staticmethod
3049 def wtforms(field, settings):
3050 return validators.Regexp(regex=ISSN_COMPILED, message=settings.get("message"))
3053class IsISSNListBuilder:
3054 # ~~->$ IsISSNList:FormValidator~~
3055 @staticmethod
3056 def render(settings, html_attrs):
3057 html_attrs["data-parsley-entry-pattern"] = ISSN
3059 @staticmethod
3060 def wtforms(field, settings):
3061 return RegexpOnTagList(regex=ISSN_COMPILED, message=settings.get("message"))
3064class DifferentToBuilder:
3065 # ~~->$ DifferentTo:FormValidator~~
3066 @staticmethod
3067 def render(settings, html_attrs):
3068 html_attrs["data-parsley-different-to"] = settings.get("field")
3069 html_attrs["data-parsley-different-to-message"] = "<p><small>" + settings.get("message") + "</small></p>"
3071 @staticmethod
3072 def wtforms(field, settings):
3073 return DifferentTo(settings.get("field") or field, settings.get("ignore_empty", True), settings.get("message"))
3076class RequiredIfBuilder:
3077 # ~~->$ RequiredIf:FormValidator~~
3078 @staticmethod
3079 def render(settings, html_attrs):
3080 val = settings.get("value")
3081 if isinstance(val, list):
3082 val = ",".join(val)
3084 html_attrs["data-parsley-validate-if-empty"] = "true"
3085 html_attrs["data-parsley-required-if"] = val
3086 html_attrs["data-parsley-required-if-field"] = settings.get("field")
3087 if "message" in settings:
3088 html_attrs["data-parsley-required-if-message"] = "<p><small>" + settings["message"] + "</small></p>"
3089 else:
3090 html_attrs["data-parsley-required-if-message"] = "<p><small>" + lazy_gettext("This answer is required") + "</small></p>"
3092 @staticmethod
3093 def wtforms(field, settings):
3094 return RequiredIfOtherValue(settings.get("field") or field, settings.get("value"), settings.get("message"))
3097class OnlyIfBuilder:
3098 # ~~->$ OnlyIf:FormValidator~~
3099 @staticmethod
3100 def render(settings, html_attrs):
3101 html_attrs["data-parsley-only-if"] = ",".join([f["field"] for f in settings.get("fields", [])])
3102 for f in settings.get('fields'):
3103 if "value" in f:
3104 html_attrs["data-parsley-only-if-value_" + f["field"]] = f["value"]
3105 if "not" in f:
3106 html_attrs["data-parsley-only-if-not_" + f["field"]] = f["not"]
3107 if "or" in f:
3108 html_attrs["data-parsley-only-if-or_" + f["field"]] = ",".join(f["or"])
3109 html_attrs["data-parsley-only-if-message"] = settings.get("message")
3111 @staticmethod
3112 def wtforms(fields, settings):
3113 return OnlyIf(other_fields=settings.get('fields') or fields, ignore_empty=settings.get('ignore_empty', True),
3114 message=settings.get('message'))
3117class OnlyIfExistsBuilder:
3118 # ~~->$ OnlyIf:FormValidator~~
3119 @staticmethod
3120 def render(settings, html_attrs):
3121 html_attrs["data-parsley-only-if-exists"] = ",".join([f["field"] for f in settings.get("fields", [])])
3122 html_attrs["data-parsley-only-if-exists-message"] = "<p><small>" + settings.get("message") + "</small></p>"
3124 @staticmethod
3125 def wtforms(fields, settings):
3126 return OnlyIfExists(other_fields=settings.get('fields') or fields,
3127 ignore_empty=settings.get('ignore_empty', True), message=settings.get('message'))
3130class NotIfBuildier:
3131 # ~~->$ NotIf:FormValidator~~
3132 @staticmethod
3133 def render(settings, html_attrs):
3134 html_attrs["data-parsley-not-if"] = ",".join([f.get("field") for f in settings.get("fields", [])])
3135 if settings.get("message"):
3136 html_attrs["data-parsley-not-if-message"] = settings.get("message")
3138 @staticmethod
3139 def wtforms(fields, settings):
3140 return NotIf(settings.get('fields') or fields, settings.get('message'))
3143class GroupMemberBuilder:
3144 # ~~->$ GroupMember:FormValidator~~
3145 @staticmethod
3146 def render(settings, html_attrs):
3147 # FIXME: front end validator for this does not yet exist (do we have an existing one from formcontext?)
3148 html_attrs["data-parsley-group-member-field"] = settings.get("group_field")
3150 @staticmethod
3151 def wtforms(field, settings):
3152 return GroupMember(settings.get('group_field') or field)
3155class RequiredValueBuilder:
3156 # ~~->$ RequiredValue:FormValidator~~
3157 @staticmethod
3158 def render(settings, html_attrs):
3159 html_attrs["data-parsley-requiredvalue"] = settings.get("value")
3161 @staticmethod
3162 def wtforms(field, settings):
3163 return RequiredValue(settings.get("value"), settings.get("message"))
3166class BigEndDateBuilder:
3167 # ~~->$ BigEndDate:FormValidator~~
3168 @staticmethod
3169 def render(settings, html_attrs):
3170 html_attrs["data-parsley-validdate"] = ""
3171 html_attrs["data-parsley-pattern-message"] = settings.get("message")
3173 @staticmethod
3174 def wtforms(field, settings):
3175 return BigEndDate(settings.get("message"))
3177class DateInThePastBuilder:
3178 # ~~->$ BigEndDate:FormValidator~~
3179 @staticmethod
3180 def render(settings, html_attrs):
3181 # no client side rendering for this, as it interferes with the datepicker
3182 pass
3184 @staticmethod
3185 def wtforms(field, settings):
3186 return DateInThePast(settings.get("message"))
3188class YearBuilder:
3189 @staticmethod
3190 def render(settings, html_attrs):
3191 html_attrs["data-parsley-year"] = app.config.get('MINIMAL_OA_START_DATE', 1900)
3192 html_attrs["data-parsley-year-message"] = "<p><small>" + settings["message"] + "</small></p>"
3194 @staticmethod
3195 def wtforms(field, settings):
3196 return Year(settings.get("message"))
3199class CurrentISOCurrencyBuilder:
3200 @staticmethod
3201 def render(settings, html_attrs):
3202 pass
3204 @staticmethod
3205 def wtforms(field, settings):
3206 return CurrentISOCurrency(settings.get("message"))
3209class CurrentISOLanguageBuilder:
3210 @staticmethod
3211 def render(settings, html_attrs):
3212 pass
3214 @staticmethod
3215 def wtforms(field, settings):
3216 return CurrentISOLanguage(settings.get("message"))
3219#########################################################
3220# Crosswalks
3221#########################################################
3223PYTHON_FUNCTIONS = {
3224 "options": {
3225 "iso_country_list": iso_country_list,
3226 "iso_language_list": iso_language_list,
3227 "iso_currency_list": iso_currency_list,
3228 "quick_reject": quick_reject,
3229 "application_statuses": application_statuses,
3230 "editor_choices": editor_choices
3231 },
3232 "disabled": {
3233 "application_status_disabled": application_status_disabled,
3234 "disable_edit_note_except_editing_user": disable_edit_note_except_editing_user,
3235 "disable_edit_flag_except_author_admin_assignee": disable_edit_flag_except_author_admin_assignee
3236 },
3237 "merge_disabled": {
3238 "merge_disabled_notes": merge_disabled_notes
3239 },
3240 "validate": {
3241 "render": {
3242 "required": RequiredBuilder.render,
3243 "is_url": IsURLBuilder.render,
3244 "int_range": IntRangeBuilder.render,
3245 "issn_in_public_doaj": ISSNInPublicDOAJBuilder.render,
3246 "journal_url_in_public_doaj": JournalURLInPublicDOAJBuilder.render,
3247 "optional_if": OptionalIfBuilder.render,
3248 "is_issn": IsISSNBuilder.render,
3249 "is_issn_list": IsISSNListBuilder.render,
3250 "different_to": DifferentToBuilder.render,
3251 "required_if": RequiredIfBuilder.render,
3252 "only_if": OnlyIfBuilder.render,
3253 "only_if_exists": OnlyIfExistsBuilder.render,
3254 "group_member": GroupMemberBuilder.render,
3255 "not_if": NotIfBuildier.render,
3256 "required_value": RequiredValueBuilder.render,
3257 "bigenddate": BigEndDateBuilder.render,
3258 "no_script_tag": NoScriptTagBuilder.render,
3259 "year": YearBuilder.render,
3260 "date_in_the_past": DateInThePastBuilder.render
3261 },
3262 "wtforms": {
3263 "required": RequiredBuilder.wtforms,
3264 "is_url": IsURLBuilder.wtforms,
3265 "max_tags": MaxTagsBuilder.wtforms,
3266 "int_range": IntRangeBuilder.wtforms,
3267 "stop_words": StopWordsBuilder.wtforms,
3268 "issn_in_public_doaj": ISSNInPublicDOAJBuilder.wtforms,
3269 "journal_url_in_public_doaj": JournalURLInPublicDOAJBuilder.wtforms,
3270 "optional_if": OptionalIfBuilder.wtforms,
3271 "is_issn": IsISSNBuilder.wtforms,
3272 "is_issn_list": IsISSNListBuilder.wtforms,
3273 "different_to": DifferentToBuilder.wtforms,
3274 "required_if": RequiredIfBuilder.wtforms,
3275 "only_if": OnlyIfBuilder.wtforms,
3276 "only_if_exists": OnlyIfExistsBuilder.wtforms,
3277 "group_member": GroupMemberBuilder.wtforms,
3278 "not_if": NotIfBuildier.wtforms,
3279 "required_value": RequiredValueBuilder.wtforms,
3280 "bigenddate": BigEndDateBuilder.wtforms,
3281 "reserved_usernames": ReservedUsernamesBuilder.wtforms,
3282 "owner_exists": OwnerExistsBuilder.wtforms,
3283 "no_script_tag": NoScriptTagBuilder.wtforms,
3284 "year": YearBuilder.wtforms,
3285 "current_iso_currency": CurrentISOCurrencyBuilder.wtforms,
3286 "current_iso_language": CurrentISOLanguageBuilder.wtforms,
3287 "date_in_the_past": DateInThePastBuilder.wtforms
3288 }
3289 }
3290}
3292JAVASCRIPT_FUNCTIONS = {
3293 "clickable_url": "formulaic.widgets.newClickableUrl", # ~~-> ClickableURL:FormWidget~~
3294 "click_to_copy": "formulaic.widgets.newClickToCopy", # ~~-> ClickToCopy:FormWidget~~
3295 "clickable_owner": "formulaic.widgets.newClickableOwner", # ~~-> ClickableOwner:FormWidget~~
3296 "select": "formulaic.widgets.newSelect", # ~~-> SelectBox:FormWidget~~
3297 "taglist": "formulaic.widgets.newTagList", # ~~-> TagList:FormWidget~~
3298 "tagentry": "formulaic.widgets.newTagEntry", # ~~-> TagEntry:FormWidget~~
3299 "multiple_field": "formulaic.widgets.newMultipleField", # ~~-> MultiField:FormWidget~~
3300 "infinite_repeat": "formulaic.widgets.newInfiniteRepeat", # ~~-> InfiniteRepeat:FormWidget~~
3301 "autocomplete": "formulaic.widgets.newAutocomplete", # ~~-> Autocomplete:FormWidget~~
3302 "subject_tree": "formulaic.widgets.newSubjectTree", # ~~-> SubjectTree:FormWidget~~
3303 "full_contents": "formulaic.widgets.newFullContents", # ~~^->FullContents:FormWidget~~
3304 "load_editors": "formulaic.widgets.newLoadEditors", # ~~-> LoadEditors:FormWidget~~
3305 "trim_whitespace": "formulaic.widgets.newTrimWhitespace", # ~~-> TrimWhitespace:FormWidget~~
3306 "note_modal": "formulaic.widgets.newNoteModal", # ~~-> NoteModal:FormWidget~~,
3307 "autocheck": "formulaic.widgets.newAutocheck", # ~~-> Autocheck:FormWidget~~
3308 "issn_link": "formulaic.widgets.newIssnLink", # ~~-> IssnLink:FormWidget~~,
3309 "article_info": "formulaic.widgets.newArticleInfo", # ~~-> ArticleInfo:FormWidget~~
3310 "flag_manager": "formulaic.widgets.newFlagManager", # ~~-> FlagManager:FormWidget~~
3311 "date_picker": "formulaic.widgets.newDatePicker" # ~~-> DatePicker:FormWidget~~
3313}
3316##############################################################
3317# A couple of convenient widgets for WTForms rendering
3318##############################################################
3320class NumberWidget(widgets.Input):
3321 input_type = 'number'
3323class FieldsetWidget(object):
3324 """
3325 Renders a fieldset with a legend
3326 if legend_display is true the question is displayed
3327 if legend_display is false it is for screen readers only
3328 """
3329 def __init__(self, legend, sr_only_label = True, prefix_label=False):
3330 self.sr_only = sr_only_label
3331 self.prefix_label = prefix_label
3333 self.legend = f'<legend '
3334 if self.sr_only:
3335 self.legend += f'class="sr-only"'
3336 self.legend += f'>{legend}</legend>'
3338 self.html_wrapper = f"<fieldset> {self.legend} [BODY]</fieldset>"
3340 def __call__(self, field, **kwargs):
3341 fields = ''
3342 for subfield in field:
3343 fields += f'{subfield.label}{subfield(**kwargs)}' if self.prefix_label else f'{subfield(**kwargs)}{subfield.label}'
3345 html = self.html_wrapper.replace("[BODY]", fields)
3346 return HTMLString(''.join(html))
3349class ListWidgetWithSubfields(object):
3350 """
3351 Renders a list of fields as a `ul` or `ol` list.
3353 This is used for fields which encapsulate many inner fields as subfields.
3354 The widget will try to iterate the field to get access to the subfields and
3355 call them to render them.
3357 If `prefix_label` is set, the subfield's label is printed before the field,
3358 otherwise afterwards. The latter is useful for iterating radios or
3359 checkboxes.
3360 """
3362 def __init__(self, html_tag='ul', prefix_label=False):
3363 assert html_tag in ('ol', 'ul')
3364 self.html_tag = html_tag
3365 self.prefix_label = prefix_label
3367 def __call__(self, field, **kwargs):
3368 # kwargs.setdefault('id', field.id)
3369 fl = kwargs.pop("formulaic", None)
3370 html = ['<%s %s>' % (self.html_tag, html_params(**kwargs))]
3371 for subfield in field:
3372 if self.prefix_label:
3373 html.append('<li tabindex=0>%s %s' % (subfield.label, subfield(**kwargs)))
3374 else:
3375 label = str(subfield.label)
3376 html.append('<li tabindex=0>%s %s' % (subfield(**kwargs), label))
3378 html.append("</li>")
3380 html.append('</%s>' % self.html_tag)
3381 return HTMLString(''.join(html))
3384##########################################################
3385# Mapping from configurations to WTForms builders
3386##########################################################
3388class RadioBuilder(WTFormsBuilder):
3389 @staticmethod
3390 def match(field):
3391 return field.get("input") == "radio"
3393 @staticmethod
3394 def wtform(formulaic_context, field, wtfargs):
3395 # wtfargs["widget"] = ListWidgetWithSubfields()
3396 wtfargs["widget"] = FieldsetWidget(legend=field.get("label"), sr_only_label=True)
3397 return UnconstrainedRadioField(**wtfargs)
3400class MultiCheckboxBuilder(WTFormsBuilder):
3401 @staticmethod
3402 def match(field):
3403 return field.get("input") == "checkbox" and \
3404 (len(field.get("options", [])) > 0 or field.get("options_fn") is not None)
3406 @staticmethod
3407 def wtform(formulaic_context, field, wtfargs):
3408 wtfargs["option_widget"] = widgets.CheckboxInput()
3409 wtfargs["widget"] = ListWidgetWithSubfields()
3410 return SelectMultipleField(**wtfargs)
3413class SingleCheckboxBuilder(WTFormsBuilder):
3414 @staticmethod
3415 def match(field):
3416 return field.get("input") == "checkbox" and len(field.get("options", [])) == 0 and field.get(
3417 "options_fn") is None
3419 @staticmethod
3420 def wtform(formulaic_context, field, wtfargs):
3421 return BooleanField(**wtfargs)
3424class SelectBuilder(WTFormsBuilder):
3425 @staticmethod
3426 def match(field):
3427 return field.get("input") == "select" and not field.get("multiple", False)
3429 @staticmethod
3430 def wtform(formulaic_context, field, wtfargs):
3431 if field.get("repeatable", {}).get("label", ""):
3432 wtfargs['label'] = field["repeatable"]["label"]
3433 sf = SelectField(**wtfargs)
3434 if "repeatable" in field:
3435 sf = FieldList(sf, min_entries=field.get("repeatable", {}).get("initial", 1))
3437 return sf
3440class MultiSelectBuilder(WTFormsBuilder):
3441 @staticmethod
3442 def match(field):
3443 return field.get("input") == "select" and field.get("multiple", False)
3445 @staticmethod
3446 def wtform(formulaic_context, field, wtfargs):
3447 return SelectMultipleField(**wtfargs)
3450class TextBuilder(WTFormsBuilder):
3451 @staticmethod
3452 def match(field):
3453 return field.get("input") == "text"
3455 @staticmethod
3456 def wtform(formulaic_context, field, wtfargs):
3457 if "filters" not in wtfargs:
3458 wtfargs["filters"] = (lambda x: x.strip() if x is not None else x,)
3459 sf = StringField(**wtfargs)
3460 if "repeatable" in field:
3461 sf = FieldList(sf, min_entries=field.get("repeatable", {}).get("initial", 1))
3462 return sf
3464class DateBuilder(WTFormsBuilder):
3465 @staticmethod
3466 def match(field):
3467 return field.get("input") == "date"
3469 @staticmethod
3470 def wtform(formulaic_context, field, wtfargs):
3471 wtfargs["widget"] = widgets.Input(input_type="date")
3472 sf = DateField(**wtfargs)
3473 if "repeatable" in field:
3474 sf = FieldList(sf, min_entries=field.get("repeatable", {}).get("initial", 1))
3475 return sf
3477class TextAreaBuilder(WTFormsBuilder):
3478 @staticmethod
3479 def match(field):
3480 return field.get("input") == "textarea"
3482 @staticmethod
3483 def wtform(formulaic_context, field, wtfargs):
3484 sf = TextAreaField(**wtfargs)
3485 if "repeatable" in field:
3486 sf = FieldList(sf, min_entries=field.get("repeatable", {}).get("initial", 1))
3487 return sf
3490class TagListBuilder(WTFormsBuilder):
3491 @staticmethod
3492 def match(field):
3493 return field.get("input") == "taglist"
3495 @staticmethod
3496 def wtform(formulaic_context, field, wtfargs):
3497 return TagListField(**wtfargs)
3500class IntegerBuilder(WTFormsBuilder):
3501 @staticmethod
3502 def match(field):
3503 return field.get("input") == "number" and field.get("datatype") == "integer"
3505 @staticmethod
3506 def wtform(formulaic_context, field, wtfargs):
3507 wtfargs["widget"] = NumberWidget()
3508 return IntegerField(**wtfargs)
3511# TODO: multiple group doesn't work
3512class GroupBuilder(WTFormsBuilder):
3513 @staticmethod
3514 def match(field):
3515 return field.get("input") == "group" and field.get("repeatable") is None
3517 @staticmethod
3518 def wtform(formulaic_context, field, wtfargs):
3519 fields = [formulaic_context.get(subfield) for subfield in field.get("subfields", [])]
3520 klazz = formulaic_context.make_wtform_class(fields)
3521 return NestedFormField(klazz)
3524class GroupListBuilder(WTFormsBuilder):
3525 @staticmethod
3526 def match(field):
3527 return field.get("input") == "group" and field.get("repeatable") is not None
3529 @staticmethod
3530 def wtform(formulaic_context, field, wtfargs):
3531 ff = GroupBuilder.wtform(formulaic_context, field, wtfargs)
3532 repeat_cfg = field.get("repeatable", {})
3533 return FieldList(ff, min_entries=repeat_cfg.get("initial", 1))
3536class HiddenFieldBuilder(WTFormsBuilder):
3537 @staticmethod
3538 def match(field):
3539 return field.get("input") == "hidden"
3541 @staticmethod
3542 def wtform(formulaic_context, field, wtfargs):
3543 return HiddenField(**wtfargs)
3546WTFORMS_BUILDERS = [
3547 RadioBuilder,
3548 MultiCheckboxBuilder,
3549 SingleCheckboxBuilder,
3550 SelectBuilder,
3551 MultiSelectBuilder,
3552 TextBuilder,
3553 TextAreaBuilder,
3554 TagListBuilder,
3555 IntegerBuilder,
3556 GroupBuilder,
3557 GroupListBuilder,
3558 HiddenFieldBuilder,
3559 DateBuilder
3560]
3562ApplicationFormFactory = Formulaic(APPLICATION_FORMS, WTFORMS_BUILDERS, function_map=PYTHON_FUNCTIONS,
3563 javascript_functions=JAVASCRIPT_FUNCTIONS)
3564JournalFormFactory = Formulaic(JOURNAL_FORMS, WTFORMS_BUILDERS, function_map=PYTHON_FUNCTIONS,
3565 javascript_functions=JAVASCRIPT_FUNCTIONS)
3567if __name__ == "__main__":
3568 """
3569 Running this file from the command line enables you to output documentation for a given form context.
3571 See `docs/forms.sh` for where this is used
3573 To create the documentation you can call this file with 3 arguments:
3575 -t - the object type to output. Either 'journal' or 'application'
3576 -c - the form context. Will be one of the contexts defined elsewhere in this file, which may be specific to the
3577 object type. For example, 'admin' or 'editor'
3578 -o - the path to the file where to output the result
3580 The output is a CSV which lists the following information:
3582 * Form Position - the position in the form. Felds are listed in order
3583 * Field Name - the form field name
3584 * Label - the form field label
3585 * Input Type - what kind of input (e.g. radio, text)
3586 * Options - the express options allowed, or the name of the function which generates the options
3587 * Disabled? - is the field disabled in this context
3588 * Fieldset ID - the ID (from this file) of the fieldset that this field is part of
3589 """
3590 import argparse
3592 parser = argparse.ArgumentParser()
3593 parser.add_argument("-t", "--type", help="object type to output")
3594 parser.add_argument("-c", "--context", help="context to output")
3595 parser.add_argument("-o", "--out", help="output file path")
3596 args = parser.parse_args()
3598 if not args.out:
3599 print("Please specify an output file path with the -o option")
3600 parser.print_help()
3601 exit()
3603 if not args.context:
3604 print("Please specify a context to output")
3605 parser.print_help()
3606 exit()
3608 if not args.type:
3609 print("Please specify a type to output")
3610 parser.print_help()
3611 exit()
3613 fc = None
3614 if args.type == "journal":
3615 fc = JournalFormFactory.context(args.context)
3616 elif args.type == "application":
3617 fc = ApplicationFormFactory.context(args.context)
3619 if fc is not None:
3620 fc.to_summary_csv(args.out)
3621 else:
3622 print("You did not enter a valid type. Use one of 'journal' or 'application'")