From df527ef2665442b0cb7a2b618db660d4da116de9 Mon Sep 17 00:00:00 2001 From: Kevin Day Date: Fri, 27 Jan 2023 14:25:05 -0600 Subject: [PATCH] Issue 475: Alternate approach to sorting, using backend. Change sort to use the backend sorting. The Discover View `Unique Identifier Key` is using a weaver field. The `orderBy` filter is not easily added to it (appears to require a weaver change). To avoid that, sorting to the backend is implemented. The sorting is removed from the UI in certain places. This has the benefit of the specially added `all_fields` on appearing at the top again. The internal metadatum is sorted by id. If gloss is intended for the user to use, then should this not be sorted by gloss? It may be that the sorting by field is explicitly desired in this case. The backend is changed to sort by gloss to fix the `Unique Identifier Key` situation and so this would either need to be sorted via the UI or a new endpoint (or parameter) would be needed on the backend. The field mappings are also now sorted by the gloss. Is this desired? It makes little sense to sort the field mappings by the field if this is a list of gloss (doesn't always have field names). In the future we may want a sort by **required** as the alphabetic sorts required fields like `id` to somewhere in the middle. Or we may want to put the important and well known fields at the top. A comparitor is used for the solr core to perform sorting in the backend. The angularjs sorting acts differently than the Java sorting method. Java uses natural sorting. Consider: - Zulu Time - aardvark - A Time of Resolve Java Sort: - aardvark - A Time of Resolve - Zulu Time AngularJS Sort: - A Time of Resolve - aardvark - Zulu Time When java sorting is in place, updates on the menu drop to the bottom. A page refresh triggers proper sorting. Adding both Java sorting and AngularJS sorting handles both cases (but we get stuck with AngularJS' sorting algorithm). --- .../edu/tamu/sage/controller/DiscoveryViewController.java | 2 +- .../tamu/sage/controller/IternalMetadataController.java | 2 +- src/main/java/edu/tamu/sage/controller/JobController.java | 2 +- .../java/edu/tamu/sage/controller/OperatorController.java | 2 +- .../java/edu/tamu/sage/controller/ReaderController.java | 2 +- .../java/edu/tamu/sage/controller/SourceController.java | 6 +++--- .../java/edu/tamu/sage/controller/WriterController.java | 2 +- src/main/java/edu/tamu/sage/job/JobRunner.java | 2 +- .../java/edu/tamu/sage/model/repo/DiscoveryViewRepo.java | 4 +++- .../edu/tamu/sage/model/repo/InternalMetadataRepo.java | 2 +- src/main/java/edu/tamu/sage/model/repo/JobRepo.java | 5 ++++- src/main/java/edu/tamu/sage/model/repo/OperatorRepo.java | 2 ++ src/main/java/edu/tamu/sage/model/repo/ReaderRepo.java | 3 +++ src/main/java/edu/tamu/sage/model/repo/SourceRepo.java | 5 ++++- src/main/java/edu/tamu/sage/model/repo/UserRepo.java | 4 ++-- src/main/java/edu/tamu/sage/model/repo/WriterRepo.java | 2 ++ .../java/edu/tamu/sage/service/SolrSourceService.java | 8 ++++++-- .../webapp/app/views/components/multiSuggestionInput.html | 2 +- src/main/webapp/app/views/discovery.html | 2 +- src/main/webapp/app/views/management/discovery-view.html | 2 +- .../webapp/app/views/management/internal-metadata.html | 2 +- src/main/webapp/app/views/management/job.html | 8 ++++---- src/main/webapp/app/views/management/operator.html | 2 +- src/main/webapp/app/views/management/reader.html | 2 +- src/main/webapp/app/views/management/source.html | 2 +- src/main/webapp/app/views/management/writer.html | 2 +- .../webapp/app/views/modals/cloneDiscoveryViewModal.html | 1 - src/main/webapp/app/views/modals/cloneReaderModal.html | 5 ++--- src/main/webapp/app/views/modals/cloneWriterModal.html | 3 +-- src/main/webapp/app/views/modals/createReaderModal.html | 4 ++-- src/main/webapp/app/views/modals/createWriterModal.html | 2 +- .../webapp/app/views/modals/updateDiscoveryViewModal.html | 6 +++--- src/main/webapp/app/views/modals/updateReaderModal.html | 4 ++-- src/main/webapp/app/views/modals/updateWriterModal.html | 2 +- 34 files changed, 61 insertions(+), 45 deletions(-) diff --git a/src/main/java/edu/tamu/sage/controller/DiscoveryViewController.java b/src/main/java/edu/tamu/sage/controller/DiscoveryViewController.java index 15e97dd3..d8821179 100644 --- a/src/main/java/edu/tamu/sage/controller/DiscoveryViewController.java +++ b/src/main/java/edu/tamu/sage/controller/DiscoveryViewController.java @@ -55,7 +55,7 @@ public class DiscoveryViewController { @RequestMapping(method = RequestMethod.GET) @PreAuthorize("hasRole('ANONYMOUS')") public ApiResponse getAll() { - return new ApiResponse(SUCCESS, discoveryViewRepo.findAll()); + return new ApiResponse(SUCCESS, discoveryViewRepo.findAllByOrderByNameAsc()); } @RequestMapping(value = "/{id}", method = RequestMethod.GET) diff --git a/src/main/java/edu/tamu/sage/controller/IternalMetadataController.java b/src/main/java/edu/tamu/sage/controller/IternalMetadataController.java index 40ab1327..be912d1a 100644 --- a/src/main/java/edu/tamu/sage/controller/IternalMetadataController.java +++ b/src/main/java/edu/tamu/sage/controller/IternalMetadataController.java @@ -33,7 +33,7 @@ public class IternalMetadataController { @GetMapping @PreAuthorize("hasRole('ADMIN')") public ApiResponse getAll() { - return new ApiResponse(SUCCESS, internalMetadataRepo.findAllByOrderByFieldAsc()); + return new ApiResponse(SUCCESS, internalMetadataRepo.findAllByOrderByGlossAsc()); } @PostMapping diff --git a/src/main/java/edu/tamu/sage/controller/JobController.java b/src/main/java/edu/tamu/sage/controller/JobController.java index 76adbd74..5a25b4bb 100644 --- a/src/main/java/edu/tamu/sage/controller/JobController.java +++ b/src/main/java/edu/tamu/sage/controller/JobController.java @@ -41,7 +41,7 @@ public class JobController { @GetMapping @PreAuthorize("hasRole('USER')") public ApiResponse getAll() { - return new ApiResponse(SUCCESS, jobRepo.findAll()); + return new ApiResponse(SUCCESS, jobRepo.findAllByOrderByNameAsc()); } @PostMapping diff --git a/src/main/java/edu/tamu/sage/controller/OperatorController.java b/src/main/java/edu/tamu/sage/controller/OperatorController.java index c55378ad..1b00d0b1 100644 --- a/src/main/java/edu/tamu/sage/controller/OperatorController.java +++ b/src/main/java/edu/tamu/sage/controller/OperatorController.java @@ -42,7 +42,7 @@ public class OperatorController { @GetMapping @PreAuthorize("hasRole('ADMIN')") public ApiResponse getAll() { - return new ApiResponse(SUCCESS, operatorRepo.findAll()); + return new ApiResponse(SUCCESS, operatorRepo.findAllByOrderByNameAsc()); } @GetMapping("/types") diff --git a/src/main/java/edu/tamu/sage/controller/ReaderController.java b/src/main/java/edu/tamu/sage/controller/ReaderController.java index 47701a10..9a98f4e7 100644 --- a/src/main/java/edu/tamu/sage/controller/ReaderController.java +++ b/src/main/java/edu/tamu/sage/controller/ReaderController.java @@ -31,7 +31,7 @@ public class ReaderController { @RequestMapping(method = RequestMethod.GET) @PreAuthorize("hasRole('ANONYMOUS')") public ApiResponse getAll() { - return new ApiResponse(SUCCESS, solrReaderRepo.findAll()); + return new ApiResponse(SUCCESS, solrReaderRepo.findAllByOrderByNameAsc()); } @RequestMapping(method = RequestMethod.POST) diff --git a/src/main/java/edu/tamu/sage/controller/SourceController.java b/src/main/java/edu/tamu/sage/controller/SourceController.java index 9ff7e90f..01bd1eeb 100644 --- a/src/main/java/edu/tamu/sage/controller/SourceController.java +++ b/src/main/java/edu/tamu/sage/controller/SourceController.java @@ -138,19 +138,19 @@ public ApiResponse testSolrCoreAuthorization(@WeaverValidatedModel Source source @RequestMapping(method = RequestMethod.GET) @PreAuthorize("hasRole('ANONYMOUS')") public ApiResponse getAll(@WeaverUser User user) { - return new ApiResponse(SUCCESS, sourceRepo.findAll()); + return new ApiResponse(SUCCESS, sourceRepo.findAllByOrderByNameAsc()); } @RequestMapping("/writeable") @PreAuthorize("hasRole('USER')") public ApiResponse getWriteable(@WeaverUser User user) { - return new ApiResponse(SUCCESS, sourceRepo.findByReadOnly(false)); + return new ApiResponse(SUCCESS, sourceRepo.findByReadOnlyOrderByNameAsc(false)); } @RequestMapping("/readable") @PreAuthorize("hasRole('USER')") public ApiResponse getReadable(@WeaverUser User user) { - return new ApiResponse(SUCCESS, sourceRepo.findByReadOnly(true)); + return new ApiResponse(SUCCESS, sourceRepo.findByReadOnlyOrderByNameAsc(true)); } @RequestMapping(method = RequestMethod.POST) diff --git a/src/main/java/edu/tamu/sage/controller/WriterController.java b/src/main/java/edu/tamu/sage/controller/WriterController.java index 8408a2bb..a71bb045 100644 --- a/src/main/java/edu/tamu/sage/controller/WriterController.java +++ b/src/main/java/edu/tamu/sage/controller/WriterController.java @@ -31,7 +31,7 @@ public class WriterController { @RequestMapping(method = RequestMethod.GET) @PreAuthorize("hasRole('ANONYMOUS')") public ApiResponse getAll() { - return new ApiResponse(SUCCESS, solrWriterRepo.findAll()); + return new ApiResponse(SUCCESS, solrWriterRepo.findAllByOrderByNameAsc()); } @RequestMapping(method = RequestMethod.POST) diff --git a/src/main/java/edu/tamu/sage/job/JobRunner.java b/src/main/java/edu/tamu/sage/job/JobRunner.java index 6c6ddf1b..620bad11 100644 --- a/src/main/java/edu/tamu/sage/job/JobRunner.java +++ b/src/main/java/edu/tamu/sage/job/JobRunner.java @@ -38,7 +38,7 @@ public class JobRunner { @Scheduled(cron = "0 0/30 * * * ?") private void runJobs() { - List activeJobs = jobRepo.findByScheduleActiveTrue(); + List activeJobs = jobRepo.findByScheduleActiveTrueOrderByNameAsc(); logger.debug("Checking " + activeJobs.size() + " Active Jobs"); LocalDateTime schedulerStarted = java.time.LocalDateTime.now(ZoneId.of("UTC")); activeJobs.forEach(j -> { diff --git a/src/main/java/edu/tamu/sage/model/repo/DiscoveryViewRepo.java b/src/main/java/edu/tamu/sage/model/repo/DiscoveryViewRepo.java index 7a576d4a..3a5abcf0 100644 --- a/src/main/java/edu/tamu/sage/model/repo/DiscoveryViewRepo.java +++ b/src/main/java/edu/tamu/sage/model/repo/DiscoveryViewRepo.java @@ -3,9 +3,11 @@ import edu.tamu.sage.model.DiscoveryView; import edu.tamu.sage.model.repo.custom.DiscoveryViewRepoCustom; import edu.tamu.weaver.data.model.repo.WeaverRepo; +import java.util.List; public interface DiscoveryViewRepo extends WeaverRepo, DiscoveryViewRepoCustom { + public List findAllByOrderByNameAsc(); + public DiscoveryView findOneBySlug(String slug); - } diff --git a/src/main/java/edu/tamu/sage/model/repo/InternalMetadataRepo.java b/src/main/java/edu/tamu/sage/model/repo/InternalMetadataRepo.java index f23a1a3d..56c262b7 100644 --- a/src/main/java/edu/tamu/sage/model/repo/InternalMetadataRepo.java +++ b/src/main/java/edu/tamu/sage/model/repo/InternalMetadataRepo.java @@ -7,5 +7,5 @@ public interface InternalMetadataRepo extends WeaverRepo, InternalMetadataRepoCustom { - public List findAllByOrderByFieldAsc(); + public List findAllByOrderByGlossAsc(); } diff --git a/src/main/java/edu/tamu/sage/model/repo/JobRepo.java b/src/main/java/edu/tamu/sage/model/repo/JobRepo.java index 1e794985..063f1ad7 100644 --- a/src/main/java/edu/tamu/sage/model/repo/JobRepo.java +++ b/src/main/java/edu/tamu/sage/model/repo/JobRepo.java @@ -7,5 +7,8 @@ import edu.tamu.weaver.data.model.repo.WeaverRepo; public interface JobRepo extends WeaverRepo, JobRepoCustom { - public List findByScheduleActiveTrue(); + + public List findAllByOrderByNameAsc(); + + public List findByScheduleActiveTrueOrderByNameAsc(); } diff --git a/src/main/java/edu/tamu/sage/model/repo/OperatorRepo.java b/src/main/java/edu/tamu/sage/model/repo/OperatorRepo.java index e2032e55..6016533e 100644 --- a/src/main/java/edu/tamu/sage/model/repo/OperatorRepo.java +++ b/src/main/java/edu/tamu/sage/model/repo/OperatorRepo.java @@ -3,7 +3,9 @@ import edu.tamu.sage.model.BaseOp; import edu.tamu.sage.model.repo.custom.JobRepoCustom; import edu.tamu.weaver.data.model.repo.WeaverRepo; +import java.util.List; public interface OperatorRepo extends WeaverRepo, JobRepoCustom { + public List findAllByOrderByNameAsc(); } diff --git a/src/main/java/edu/tamu/sage/model/repo/ReaderRepo.java b/src/main/java/edu/tamu/sage/model/repo/ReaderRepo.java index e8c0b46d..19eabe33 100644 --- a/src/main/java/edu/tamu/sage/model/repo/ReaderRepo.java +++ b/src/main/java/edu/tamu/sage/model/repo/ReaderRepo.java @@ -4,6 +4,9 @@ import edu.tamu.sage.model.Reader; import edu.tamu.sage.model.repo.custom.ReaderRepoCustom; import edu.tamu.weaver.data.model.repo.WeaverRepo; +import java.util.List; public interface ReaderRepo extends WeaverRepo, ReaderRepoCustom { + + public List findAllByOrderByNameAsc(); } diff --git a/src/main/java/edu/tamu/sage/model/repo/SourceRepo.java b/src/main/java/edu/tamu/sage/model/repo/SourceRepo.java index 644d7fac..79653d18 100644 --- a/src/main/java/edu/tamu/sage/model/repo/SourceRepo.java +++ b/src/main/java/edu/tamu/sage/model/repo/SourceRepo.java @@ -7,5 +7,8 @@ import edu.tamu.weaver.data.model.repo.WeaverRepo; public interface SourceRepo extends WeaverRepo, SourceRepoCustom { - List findByReadOnly(Boolean readOnly); + + public List findAllByOrderByNameAsc(); + + public List findByReadOnlyOrderByNameAsc(Boolean readOnly); } diff --git a/src/main/java/edu/tamu/sage/model/repo/UserRepo.java b/src/main/java/edu/tamu/sage/model/repo/UserRepo.java index decb69dc..e563a1a3 100644 --- a/src/main/java/edu/tamu/sage/model/repo/UserRepo.java +++ b/src/main/java/edu/tamu/sage/model/repo/UserRepo.java @@ -9,11 +9,10 @@ */ package edu.tamu.sage.model.repo; -import org.springframework.stereotype.Repository; - import edu.tamu.sage.model.User; import edu.tamu.sage.model.repo.custom.UserRepoCustom; import edu.tamu.weaver.auth.model.repo.AbstractWeaverUserRepo; +import org.springframework.stereotype.Repository; /** * User repository. @@ -23,5 +22,6 @@ */ @Repository public interface UserRepo extends AbstractWeaverUserRepo, UserRepoCustom { + public User findByEmail(String email); } diff --git a/src/main/java/edu/tamu/sage/model/repo/WriterRepo.java b/src/main/java/edu/tamu/sage/model/repo/WriterRepo.java index f5f7f662..ac7e9728 100644 --- a/src/main/java/edu/tamu/sage/model/repo/WriterRepo.java +++ b/src/main/java/edu/tamu/sage/model/repo/WriterRepo.java @@ -3,7 +3,9 @@ import edu.tamu.sage.model.Writer; import edu.tamu.sage.model.repo.custom.WriterRepoCustom; import edu.tamu.weaver.data.model.repo.WeaverRepo; +import java.util.List; public interface WriterRepo extends WeaverRepo, WriterRepoCustom { + public List findAllByOrderByNameAsc(); } diff --git a/src/main/java/edu/tamu/sage/service/SolrSourceService.java b/src/main/java/edu/tamu/sage/service/SolrSourceService.java index ce5c109c..78c69385 100644 --- a/src/main/java/edu/tamu/sage/service/SolrSourceService.java +++ b/src/main/java/edu/tamu/sage/service/SolrSourceService.java @@ -51,14 +51,18 @@ public List getAvailableFields(String uri, String filter) throws Sour fields.add(defaultField); - getFields(uri, filter).filter(field -> field.isStored()).forEach(field -> fields.add(field)); + getFields(uri, filter).filter(field -> field.isStored()).sorted((left, right) -> { + return left.getName().compareTo(right.getName()); + }).forEach(field -> fields.add(field)); return fields; } @Override public List getIndexedFields(String uri, String filter) throws SourceServiceException { - return getFields(uri, filter).filter(field -> field.isIndexed()).collect(Collectors.toList()); + return getFields(uri, filter).filter(field -> field.isIndexed()).sorted((left, right) -> { + return left.getName().compareTo(right.getName()); + }).collect(Collectors.toList()); } @Override diff --git a/src/main/webapp/app/views/components/multiSuggestionInput.html b/src/main/webapp/app/views/components/multiSuggestionInput.html index eb527a15..c673a86a 100644 --- a/src/main/webapp/app/views/components/multiSuggestionInput.html +++ b/src/main/webapp/app/views/components/multiSuggestionInput.html @@ -12,7 +12,7 @@ />
diff --git a/src/main/webapp/app/views/discovery.html b/src/main/webapp/app/views/discovery.html index 83f0e396..a00387b6 100644 --- a/src/main/webapp/app/views/discovery.html +++ b/src/main/webapp/app/views/discovery.html @@ -7,7 +7,7 @@

Discovery Views

- + diff --git a/src/main/webapp/app/views/management/discovery-view.html b/src/main/webapp/app/views/management/discovery-view.html index 78d9d5f7..5be26738 100644 --- a/src/main/webapp/app/views/management/discovery-view.html +++ b/src/main/webapp/app/views/management/discovery-view.html @@ -9,7 +9,7 @@

Discovery View Management

{{discoveryView.name}}
- + diff --git a/src/main/webapp/app/views/management/internal-metadata.html b/src/main/webapp/app/views/management/internal-metadata.html index 18fb90c7..61b4303b 100644 --- a/src/main/webapp/app/views/management/internal-metadata.html +++ b/src/main/webapp/app/views/management/internal-metadata.html @@ -9,7 +9,7 @@

Internal Metadata Management

{{discoveryView.name}}
- + diff --git a/src/main/webapp/app/views/management/job.html b/src/main/webapp/app/views/management/job.html index b3eead10..eddf2de3 100644 --- a/src/main/webapp/app/views/management/job.html +++ b/src/main/webapp/app/views/management/job.html @@ -13,23 +13,23 @@

Job Management

- +
{{job.name}}
    -
  • {{reader.name}}
  • +
  • {{reader.name}}
    -
  • {{operator.name}}
  • +
  • {{operator.name}}
    -
  • {{writer.name}}
  • +
  • {{writer.name}}
diff --git a/src/main/webapp/app/views/management/operator.html b/src/main/webapp/app/views/management/operator.html index 845d6b3e..2cc594a8 100644 --- a/src/main/webapp/app/views/management/operator.html +++ b/src/main/webapp/app/views/management/operator.html @@ -9,7 +9,7 @@

Operator Management

- + diff --git a/src/main/webapp/app/views/management/reader.html b/src/main/webapp/app/views/management/reader.html index c66a98cc..9a4de7d5 100644 --- a/src/main/webapp/app/views/management/reader.html +++ b/src/main/webapp/app/views/management/reader.html @@ -9,7 +9,7 @@

Reader Management

{{operator.name}}
- + diff --git a/src/main/webapp/app/views/management/source.html b/src/main/webapp/app/views/management/source.html index df2c88be..88891984 100644 --- a/src/main/webapp/app/views/management/source.html +++ b/src/main/webapp/app/views/management/source.html @@ -9,7 +9,7 @@

Core Management

{{reader.name}}
- + diff --git a/src/main/webapp/app/views/management/writer.html b/src/main/webapp/app/views/management/writer.html index add11e0b..4bda92bf 100644 --- a/src/main/webapp/app/views/management/writer.html +++ b/src/main/webapp/app/views/management/writer.html @@ -9,7 +9,7 @@

Writer Management

{{source.name}}
- + diff --git a/src/main/webapp/app/views/modals/cloneDiscoveryViewModal.html b/src/main/webapp/app/views/modals/cloneDiscoveryViewModal.html index e76f9e6f..c4ae10b4 100644 --- a/src/main/webapp/app/views/modals/cloneDiscoveryViewModal.html +++ b/src/main/webapp/app/views/modals/cloneDiscoveryViewModal.html @@ -398,4 +398,3 @@

Result Metadata

- \ No newline at end of file diff --git a/src/main/webapp/app/views/modals/cloneReaderModal.html b/src/main/webapp/app/views/modals/cloneReaderModal.html index 0bb60d90..abbab42d 100644 --- a/src/main/webapp/app/views/modals/cloneReaderModal.html +++ b/src/main/webapp/app/views/modals/cloneReaderModal.html @@ -47,9 +47,9 @@
    -
  • +
  • - + This field is required.
@@ -62,4 +62,3 @@ - \ No newline at end of file diff --git a/src/main/webapp/app/views/modals/cloneWriterModal.html b/src/main/webapp/app/views/modals/cloneWriterModal.html index 74af50af..5ace34f1 100644 --- a/src/main/webapp/app/views/modals/cloneWriterModal.html +++ b/src/main/webapp/app/views/modals/cloneWriterModal.html @@ -36,7 +36,7 @@
    -
  • +
  • This field is required. @@ -51,4 +51,3 @@ - \ No newline at end of file diff --git a/src/main/webapp/app/views/modals/createReaderModal.html b/src/main/webapp/app/views/modals/createReaderModal.html index 8b4f1925..b72eadf9 100644 --- a/src/main/webapp/app/views/modals/createReaderModal.html +++ b/src/main/webapp/app/views/modals/createReaderModal.html @@ -50,9 +50,9 @@
      -
    • +
    • - + This field is required.
    diff --git a/src/main/webapp/app/views/modals/createWriterModal.html b/src/main/webapp/app/views/modals/createWriterModal.html index c8df9dfc..18223f03 100644 --- a/src/main/webapp/app/views/modals/createWriterModal.html +++ b/src/main/webapp/app/views/modals/createWriterModal.html @@ -38,7 +38,7 @@
      -
    • +
    • This field is required. diff --git a/src/main/webapp/app/views/modals/updateDiscoveryViewModal.html b/src/main/webapp/app/views/modals/updateDiscoveryViewModal.html index 88ebf042..198d6d2f 100644 --- a/src/main/webapp/app/views/modals/updateDiscoveryViewModal.html +++ b/src/main/webapp/app/views/modals/updateDiscoveryViewModal.html @@ -169,7 +169,7 @@

      Facet Fields

      - +
      @@ -217,7 +217,7 @@

      Search Fields

      - +
      @@ -362,7 +362,7 @@

      Result Metadata

      - +
      diff --git a/src/main/webapp/app/views/modals/updateReaderModal.html b/src/main/webapp/app/views/modals/updateReaderModal.html index 3e46cbe1..4965092b 100644 --- a/src/main/webapp/app/views/modals/updateReaderModal.html +++ b/src/main/webapp/app/views/modals/updateReaderModal.html @@ -48,9 +48,9 @@
        -
      • +
      • - + This field is required.
      diff --git a/src/main/webapp/app/views/modals/updateWriterModal.html b/src/main/webapp/app/views/modals/updateWriterModal.html index f16852df..9726f32b 100644 --- a/src/main/webapp/app/views/modals/updateWriterModal.html +++ b/src/main/webapp/app/views/modals/updateWriterModal.html @@ -36,7 +36,7 @@
        -
      • +
      • This field is required.
{{writer.name}}