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

1""" 

2~~ApplicationForm:Feature~~ 

3""" 

4from copy import deepcopy 

5 

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 

11 

12from flask_babel import lazy_gettext 

13 

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 

53 

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] 

79 

80 

81######################################################## 

82# Define all our individual fields 

83######################################################## 

84 

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 } 

121 

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 } 

154 

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 } 

202 

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 } 

244 

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 } 

271 

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 } 

348 

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 } 

433 

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 } 

464 

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 } 

492 

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~~ 

529 

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~~ 

538 

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 } 

558 

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 } 

590 

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~~ 

616 

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~~ 

622 

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 } 

650 

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 } 

694 

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 } 

741 

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 } 

761 

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 } 

782 

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 } 

802 

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 } 

829 

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 } 

852 

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 } 

885 

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 } 

914 

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 } 

939 

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 } 

959 

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 } 

984 

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 } 

1004 

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 } 

1033 

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 } 

1052 

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 } 

1071 

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 } 

1090 

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 } 

1106 

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 } 

1127 

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 } 

1156 

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 } 

1185 

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 } 

1209 

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 } 

1233 

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 } 

1256 

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 } 

1285 

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 } 

1305 

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 } 

1333 

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 } 

1373 

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 } 

1403 

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 } 

1425 

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 } 

1472 

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 } 

1503 

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 } 

1525 

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 } 

1586 

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 } 

1612 

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 } 

1634 

1635 ####################################### 

1636 ## Editorial fields 

1637 

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 } 

1648 

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 } 

1657 

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 } 

1668 

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 } 

1677 

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 } 

1691 

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 } 

1715 

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 } 

1753 

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 } 

1776 

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 } 

1791 

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 } 

1815 

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 } 

1843 

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 } 

1871 

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 } 

1908 

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 } 

1933 

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 } 

1942 

1943 # ~~->$ NoteAuthor:FormField~~ 

1944 NOTE_AUTHOR = { 

1945 "subfield": True, 

1946 "name": "note_author", 

1947 "group": "notes", 

1948 "input": "text", 

1949 "disabled": True 

1950 } 

1951 

1952 # ~~->$ NoteDate:FormField~~ 

1953 NOTE_DATE = { 

1954 "subfield": True, 

1955 "name": "note_date", 

1956 "group": "notes", 

1957 "input": "text", 

1958 "disabled": True 

1959 } 

1960 

1961 # ~~->$ NoteID:FormField~~ 

1962 NOTE_ID = { 

1963 "subfield": True, 

1964 "name": "note_id", 

1965 "group": "notes", 

1966 "input": "hidden" 

1967 } 

1968 

1969 # ~~->$ NoteAuthorID:FormField~~ 

1970 NOTE_AUTHOR_ID = { 

1971 "subfield": True, 

1972 "name": "note_author_id", 

1973 "group": "notes", 

1974 "input": "hidden" 

1975 } 

1976 

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 } 

2003 

2004 FLAG_RESOLVED = { 

2005 "subfield": True, 

2006 "name": "flag_resolved", 

2007 "group": "flags", 

2008 "input": "hidden", 

2009 } 

2010 

2011 # ~~->$ NoteAuthor:FormField~~ 

2012 FLAG_SETTER = { 

2013 "subfield": True, 

2014 "name": "flag_setter", 

2015 "group": "flags", 

2016 "input": "hidden", 

2017 "disabled": True 

2018 } 

2019 

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 } 

2028 

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 } 

2045 

2046 FLAG_NOTE = { 

2047 "subfield": True, 

2048 "name": "flag_note", 

2049 "group": "flags", 

2050 "input": "textarea", 

2051 } 

2052 

2053 # ~~->$ NoteID:FormField~~ 

2054 FLAG_NOTE_ID = { 

2055 "subfield": True, 

2056 "name": "flag_note_id", 

2057 "group": "flags", 

2058 "input": "hidden" 

2059 } 

2060 

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 } 

2080 

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 } 

2090 

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 } 

2109 

2110 

2111########################################################## 

2112# Define our fieldsets 

2113########################################################## 

2114 

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 } 

2126 

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 } 

2141 

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 } 

2151 

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 } 

2161 

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 } 

2172 

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 } 

2182 

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 } 

2192 

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 } 

2203 

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 } 

2213 

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 } 

2225 

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 } 

2238 

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 } 

2248 

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 } 

2258 

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 } 

2270 

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 } 

2281 

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 } 

2291 

2292 

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 } 

2302 

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 } 

2313 

2314 # ~~->$ Reassign:FieldSet~~ 

2315 REASSIGN = { 

2316 "name": "reassign", 

2317 "label": lazy_gettext("Re-assign publisher account"), 

2318 "fields": [ 

2319 FieldDefinitions.OWNER["name"] 

2320 ] 

2321 } 

2322 

2323 LAST_FULL_REVIEW = { 

2324 "name": "last_full_review", 

2325 "label": "Last Full Review", 

2326 "fields": [ 

2327 FieldDefinitions.LAST_FULL_REVIEW["name"] 

2328 ] 

2329 } 

2330 

2331 # ~~->$ Status:FieldSet~~ 

2332 STATUS = { 

2333 "name": "status", 

2334 "label": lazy_gettext("Status"), 

2335 "fields": [ 

2336 FieldDefinitions.APPLICATION_STATUS["name"] 

2337 ] 

2338 } 

2339 

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 } 

2349 

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 } 

2361 

2362 # ~~->$ Subject:FieldSet~~ 

2363 SUBJECT = { 

2364 "name": "subject", 

2365 "label": lazy_gettext("Subject classification"), 

2366 "fields": [ 

2367 FieldDefinitions.SUBJECT["name"] 

2368 ] 

2369 } 

2370 

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 } 

2384 

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 } 

2399 

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 } 

2408 

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 } 

2420 

2421 

2422########################################################### 

2423# Define our Contexts 

2424########################################################### 

2425 

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 } 

2461 

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 

2469 

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 

2476 

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 

2489 

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 

2503 

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 

2521 

2522 

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 } 

2558 

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 

2563 

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 

2575 

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 

2586 

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 

2602 

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 } 

2622 

2623 

2624####################################################### 

2625# Gather all of our form information in one place 

2626####################################################### 

2627 

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} 

2640 

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} 

2653 

2654 

2655####################################################### 

2656# Options lists 

2657####################################################### 

2658 

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 

2665 

2666 

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 

2673 

2674 

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 

2686 

2687 

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', [])] 

2692 

2693 

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 ] 

2703 

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 ] 

2718 

2719 _application_status_editor = _application_status_base + [ 

2720 (constants.APPLICATION_STATUS_READY, Messages.FORMS__APPLICATION_STATUS__READY), 

2721 ] 

2722 

2723 formulaic_context_name = None 

2724 if formulaic_context is not None: 

2725 formulaic_context_name = formulaic_context.name 

2726 

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 

2737 

2738 return [{'display': d, 'value': v} for (v, d) in status_list] 

2739 

2740 

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": ""}] 

2750 

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": ""}] 

2764 

2765 

2766####################################################### 

2767## Conditional disableds 

2768####################################################### 

2769 

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] 

2774 

2775 

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 

2780 

2781 :param field: 

2782 :param formulaic_context: 

2783 :return: 

2784 False is editable, True is disabled 

2785 """ 

2786 

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') 

2794 

2795 

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 

2799 

2800 """ 

2801 Only allow the current user to edit this field if current user is an author, assignee or admin 

2802 

2803 :param field: 

2804 :param formulaic_context: 

2805 :return: 

2806 False is editable, True is disabled 

2807 """ 

2808 

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 

2816 

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) 

2820 

2821 

2822####################################################### 

2823## Merge disabled 

2824####################################################### 

2825 

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 

2842 

2843 while True: 

2844 try: 

2845 wtf.pop_entry() 

2846 except IndexError: 

2847 break 

2848 

2849 for m in merged: 

2850 wtf.append_entry(m) 

2851 

2852 

2853####################################################### 

2854# Validation features 

2855####################################################### 

2856 

2857class ReservedUsernamesBuilder: 

2858 """ 

2859 ~~->$ ReservedUsernames:FormValidator~~ 

2860 """ 

2861 

2862 @staticmethod 

2863 def render(settings, html_attrs): 

2864 return 

2865 

2866 @staticmethod 

2867 def wtforms(field, settings): 

2868 return ReservedUsernames() 

2869 

2870 

2871class OwnerExistsBuilder: 

2872 """ 

2873 ~~->$ OwnerExists:FormValidator~~ 

2874 """ 

2875 

2876 @staticmethod 

2877 def render(settings, html_attrs): 

2878 return 

2879 

2880 @staticmethod 

2881 def wtforms(field, settings): 

2882 return OwnerExists() 

2883 

2884 

2885class RequiredBuilder: 

2886 """ 

2887 ~~->$ Required:FormValidator~~ 

2888 """ 

2889 

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" 

2898 

2899 @staticmethod 

2900 def wtforms(field, settings): 

2901 return CustomRequired(message=settings.get("message")) 

2902 

2903 

2904class IsURLBuilder: 

2905 # ~~->$ IsURL:FormValidator~~ 

2906 

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>" 

2911 

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() 

2918 

2919 @staticmethod 

2920 def wtforms(field, settings): 

2921 return HTTPURL(message=settings.get('message', IsURLBuilder._get_msg())) 

2922 

2923 

2924class IntRangeBuilder: 

2925 """ 

2926 ~~->$ IntRange:FormValidator~~ 

2927 ~~^-> NumberRange:FormValidator~~ 

2928 """ 

2929 

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>" 

2948 

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) 

2959 

2960 

2961class MaxTagsBuilder: 

2962 """ 

2963 ~~->$ MaxLen:FormValidator~~ 

2964 """ 

2965 

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) 

2972 

2973 

2974class StopWordsBuilder: 

2975 """ 

2976 ~~->$ StopWords:FormValidator~~ 

2977 """ 

2978 

2979 @staticmethod 

2980 def wtforms(field, settings): 

2981 stopwords = settings.get("disallowed", []) 

2982 return StopWords(stopwords) 

2983 

2984 

2985class ISSNInPublicDOAJBuilder: 

2986 """ 

2987 ~~->$ ISSNInPublicDOAJ:FormValidator~~ 

2988 """ 

2989 

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"] = "" 

2994 

2995 @staticmethod 

2996 def wtforms(field, settings): 

2997 return ISSNInPublicDOAJ(message=settings.get("message")) 

2998 

2999 

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"] = "" 

3006 

3007 @staticmethod 

3008 def wtforms(field, settings): 

3009 return JournalURLInPublicDOAJ(message=settings.get("message")) 

3010 

3011 

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>" 

3021 

3022 @staticmethod 

3023 def wtforms(field, settings): 

3024 return NoScriptTag(settings.get("message", lazy_gettext("No script tags allowed"))) 

3025 

3026 

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>" 

3034 

3035 @staticmethod 

3036 def wtforms(field, settings): 

3037 return OptionalIf(settings.get("field") or field, settings.get("message"), settings.get("values", [])) 

3038 

3039 

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") 

3047 

3048 @staticmethod 

3049 def wtforms(field, settings): 

3050 return validators.Regexp(regex=ISSN_COMPILED, message=settings.get("message")) 

3051 

3052 

3053class IsISSNListBuilder: 

3054 # ~~->$ IsISSNList:FormValidator~~ 

3055 @staticmethod 

3056 def render(settings, html_attrs): 

3057 html_attrs["data-parsley-entry-pattern"] = ISSN 

3058 

3059 @staticmethod 

3060 def wtforms(field, settings): 

3061 return RegexpOnTagList(regex=ISSN_COMPILED, message=settings.get("message")) 

3062 

3063 

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>" 

3070 

3071 @staticmethod 

3072 def wtforms(field, settings): 

3073 return DifferentTo(settings.get("field") or field, settings.get("ignore_empty", True), settings.get("message")) 

3074 

3075 

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) 

3083 

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>" 

3091 

3092 @staticmethod 

3093 def wtforms(field, settings): 

3094 return RequiredIfOtherValue(settings.get("field") or field, settings.get("value"), settings.get("message")) 

3095 

3096 

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") 

3110 

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')) 

3115 

3116 

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>" 

3123 

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')) 

3128 

3129 

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") 

3137 

3138 @staticmethod 

3139 def wtforms(fields, settings): 

3140 return NotIf(settings.get('fields') or fields, settings.get('message')) 

3141 

3142 

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") 

3149 

3150 @staticmethod 

3151 def wtforms(field, settings): 

3152 return GroupMember(settings.get('group_field') or field) 

3153 

3154 

3155class RequiredValueBuilder: 

3156 # ~~->$ RequiredValue:FormValidator~~ 

3157 @staticmethod 

3158 def render(settings, html_attrs): 

3159 html_attrs["data-parsley-requiredvalue"] = settings.get("value") 

3160 

3161 @staticmethod 

3162 def wtforms(field, settings): 

3163 return RequiredValue(settings.get("value"), settings.get("message")) 

3164 

3165 

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") 

3172 

3173 @staticmethod 

3174 def wtforms(field, settings): 

3175 return BigEndDate(settings.get("message")) 

3176 

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 

3183 

3184 @staticmethod 

3185 def wtforms(field, settings): 

3186 return DateInThePast(settings.get("message")) 

3187 

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>" 

3193 

3194 @staticmethod 

3195 def wtforms(field, settings): 

3196 return Year(settings.get("message")) 

3197 

3198 

3199class CurrentISOCurrencyBuilder: 

3200 @staticmethod 

3201 def render(settings, html_attrs): 

3202 pass 

3203 

3204 @staticmethod 

3205 def wtforms(field, settings): 

3206 return CurrentISOCurrency(settings.get("message")) 

3207 

3208 

3209class CurrentISOLanguageBuilder: 

3210 @staticmethod 

3211 def render(settings, html_attrs): 

3212 pass 

3213 

3214 @staticmethod 

3215 def wtforms(field, settings): 

3216 return CurrentISOLanguage(settings.get("message")) 

3217 

3218 

3219######################################################### 

3220# Crosswalks 

3221######################################################### 

3222 

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} 

3291 

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~~ 

3312 

3313} 

3314 

3315 

3316############################################################## 

3317# A couple of convenient widgets for WTForms rendering 

3318############################################################## 

3319 

3320class NumberWidget(widgets.Input): 

3321 input_type = 'number' 

3322 

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 

3332 

3333 self.legend = f'<legend ' 

3334 if self.sr_only: 

3335 self.legend += f'class="sr-only"' 

3336 self.legend += f'>{legend}</legend>' 

3337 

3338 self.html_wrapper = f"<fieldset> {self.legend} [BODY]</fieldset>" 

3339 

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}' 

3344 

3345 html = self.html_wrapper.replace("[BODY]", fields) 

3346 return HTMLString(''.join(html)) 

3347 

3348 

3349class ListWidgetWithSubfields(object): 

3350 """ 

3351 Renders a list of fields as a `ul` or `ol` list. 

3352 

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. 

3356 

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 """ 

3361 

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 

3366 

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)) 

3377 

3378 html.append("</li>") 

3379 

3380 html.append('</%s>' % self.html_tag) 

3381 return HTMLString(''.join(html)) 

3382 

3383 

3384########################################################## 

3385# Mapping from configurations to WTForms builders 

3386########################################################## 

3387 

3388class RadioBuilder(WTFormsBuilder): 

3389 @staticmethod 

3390 def match(field): 

3391 return field.get("input") == "radio" 

3392 

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) 

3398 

3399 

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) 

3405 

3406 @staticmethod 

3407 def wtform(formulaic_context, field, wtfargs): 

3408 wtfargs["option_widget"] = widgets.CheckboxInput() 

3409 wtfargs["widget"] = ListWidgetWithSubfields() 

3410 return SelectMultipleField(**wtfargs) 

3411 

3412 

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 

3418 

3419 @staticmethod 

3420 def wtform(formulaic_context, field, wtfargs): 

3421 return BooleanField(**wtfargs) 

3422 

3423 

3424class SelectBuilder(WTFormsBuilder): 

3425 @staticmethod 

3426 def match(field): 

3427 return field.get("input") == "select" and not field.get("multiple", False) 

3428 

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)) 

3436 

3437 return sf 

3438 

3439 

3440class MultiSelectBuilder(WTFormsBuilder): 

3441 @staticmethod 

3442 def match(field): 

3443 return field.get("input") == "select" and field.get("multiple", False) 

3444 

3445 @staticmethod 

3446 def wtform(formulaic_context, field, wtfargs): 

3447 return SelectMultipleField(**wtfargs) 

3448 

3449 

3450class TextBuilder(WTFormsBuilder): 

3451 @staticmethod 

3452 def match(field): 

3453 return field.get("input") == "text" 

3454 

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 

3463 

3464class DateBuilder(WTFormsBuilder): 

3465 @staticmethod 

3466 def match(field): 

3467 return field.get("input") == "date" 

3468 

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 

3476 

3477class TextAreaBuilder(WTFormsBuilder): 

3478 @staticmethod 

3479 def match(field): 

3480 return field.get("input") == "textarea" 

3481 

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 

3488 

3489 

3490class TagListBuilder(WTFormsBuilder): 

3491 @staticmethod 

3492 def match(field): 

3493 return field.get("input") == "taglist" 

3494 

3495 @staticmethod 

3496 def wtform(formulaic_context, field, wtfargs): 

3497 return TagListField(**wtfargs) 

3498 

3499 

3500class IntegerBuilder(WTFormsBuilder): 

3501 @staticmethod 

3502 def match(field): 

3503 return field.get("input") == "number" and field.get("datatype") == "integer" 

3504 

3505 @staticmethod 

3506 def wtform(formulaic_context, field, wtfargs): 

3507 wtfargs["widget"] = NumberWidget() 

3508 return IntegerField(**wtfargs) 

3509 

3510 

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 

3516 

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) 

3522 

3523 

3524class GroupListBuilder(WTFormsBuilder): 

3525 @staticmethod 

3526 def match(field): 

3527 return field.get("input") == "group" and field.get("repeatable") is not None 

3528 

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)) 

3534 

3535 

3536class HiddenFieldBuilder(WTFormsBuilder): 

3537 @staticmethod 

3538 def match(field): 

3539 return field.get("input") == "hidden" 

3540 

3541 @staticmethod 

3542 def wtform(formulaic_context, field, wtfargs): 

3543 return HiddenField(**wtfargs) 

3544 

3545 

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] 

3561 

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) 

3566 

3567if __name__ == "__main__": 

3568 """ 

3569 Running this file from the command line enables you to output documentation for a given form context. 

3570 

3571 See `docs/forms.sh` for where this is used 

3572 

3573 To create the documentation you can call this file with 3 arguments: 

3574 

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 

3579 

3580 The output is a CSV which lists the following information: 

3581 

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 

3591 

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() 

3597 

3598 if not args.out: 

3599 print("Please specify an output file path with the -o option") 

3600 parser.print_help() 

3601 exit() 

3602 

3603 if not args.context: 

3604 print("Please specify a context to output") 

3605 parser.print_help() 

3606 exit() 

3607 

3608 if not args.type: 

3609 print("Please specify a type to output") 

3610 parser.print_help() 

3611 exit() 

3612 

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) 

3618 

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'")