Skip to content

Commit

Permalink
Merge branch 'master' into scala-steward-dependencies
Browse files Browse the repository at this point in the history
  • Loading branch information
Tom-Hallett authored Oct 22, 2024
2 parents a6f1635 + 2bdc936 commit afe97b3
Show file tree
Hide file tree
Showing 22 changed files with 416 additions and 220 deletions.
2 changes: 1 addition & 1 deletion .github/mergify.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ queue_rules:
pull_request_rules:
- name: automatic merge for Scala Steward
conditions:
- author=tna-digital-archiving-jenkins
- author=tna-da-bot
- "check-success=test / test"
- "check-success=security/snyk (nationalarchives)"
- or:
Expand Down
2 changes: 2 additions & 0 deletions app/configuration/ApplicationConfig.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ class ApplicationConfig @Inject() (configuration: Configuration) {

val blockMetadataReview: Boolean = configuration.get[Boolean]("featureAccessBlock.blockMetadataReview")

val blockSkipMetadataReview: Boolean = configuration.get[Boolean]("featureAccessBlock.blockSkipMetadataReview")

val metadataValidationBaseUrl: String = configuration.get[String]("metadatavalidation.baseUrl")

val s3Endpoint: String = configuration.get[String]("s3.endpoint")
Expand Down
26 changes: 20 additions & 6 deletions app/controllers/ConfirmTransferController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ package controllers

import auth.TokenSecurity
import com.nimbusds.oauth2.sdk.token.BearerAccessToken
import configuration.{GraphQLConfiguration, KeycloakConfiguration}
import configuration.{ApplicationConfig, GraphQLConfiguration, KeycloakConfiguration}
import controllers.util.RedirectUtils
import org.pac4j.play.scala.SecurityComponents
import play.api.data.Form
import play.api.data.Forms.{boolean, mapping}
import play.api.i18n.{I18nSupport, Lang, Langs}
import play.api.mvc._
import play.api.mvc.Results._
import services.Statuses.{ClientChecksType, CompletedValue, ExportType, FailedValue, InProgressValue, SeriesType, TransferAgreementType, UploadType}
import services.{ConfirmTransferService, ConsignmentExportService, ConsignmentService, ConsignmentStatusService, Statuses}
import services.Statuses._
import services._
import viewsapi.Caching.preventCaching

import java.util.UUID
Expand All @@ -25,6 +25,7 @@ class ConfirmTransferController @Inject() (
val confirmTransferService: ConfirmTransferService,
val consignmentExportService: ConsignmentExportService,
val consignmentStatusService: ConsignmentStatusService,
val applicationConfig: ApplicationConfig,
langs: Langs
)(implicit val ec: ExecutionContext)
extends TokenSecurity
Expand Down Expand Up @@ -70,8 +71,21 @@ class ConfirmTransferController @Inject() (
Ok(views.html.transferAlreadyCompleted(consignmentId, consignmentRef, request.token.name)).uncache()
}
case None =>
getConsignmentSummary(request, consignmentId).map { consignmentSummary =>
httpStatus(views.html.standard.confirmTransfer(consignmentId, consignmentSummary, finalTransferForm, request.token.name)).uncache()
if (applicationConfig.blockSkipMetadataReview) {
val metadataReviewType = consignmentStatuses.find(_.statusType == MetadataReviewType.id)
if (metadataReviewType.isEmpty) {
Future(Redirect(routes.AdditionalMetadataController.start(consignmentId)))
} else {
getConsignmentSummary(request, consignmentId).map { consignmentSummary =>
RedirectUtils.redirectIfReviewNotCompleted(consignmentId, consignmentStatuses)(
httpStatus(views.html.standard.confirmTransfer(consignmentId, consignmentSummary, finalTransferForm, request.token.name)).uncache()
)
}
}
} else {
getConsignmentSummary(request, consignmentId).map { consignmentSummary =>
httpStatus(views.html.standard.confirmTransfer(consignmentId, consignmentSummary, finalTransferForm, request.token.name)).uncache()
}
}
case _ =>
throw new IllegalStateException(s"Unexpected Export status: $exportTransferStatus for consignment $consignmentId")
Expand Down
19 changes: 11 additions & 8 deletions app/controllers/DraftMetadataChecksResultsController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import controllers.util.ExcelUtils
import controllers.util.MetadataProperty.filePath
import graphql.codegen.GetConsignmentStatus.getConsignmentStatus.GetConsignment
import org.pac4j.play.scala.SecurityComponents
import play.api.i18n.{I18nSupport, Messages}
import play.api.i18n.{I18nSupport, Lang, MessagesApi}
import play.api.mvc.{Action, AnyContent, Request}
import services.FileError.SCHEMA_VALIDATION
import services.Statuses._
Expand All @@ -26,7 +26,8 @@ class DraftMetadataChecksResultsController @Inject() (
val consignmentService: ConsignmentService,
val applicationConfig: ApplicationConfig,
val consignmentStatusService: ConsignmentStatusService,
val draftMetadataService: DraftMetadataService
val draftMetadataService: DraftMetadataService,
val messages: MessagesApi
)(implicit val ec: ExecutionContext)
extends TokenSecurity
with I18nSupport {
Expand Down Expand Up @@ -73,18 +74,20 @@ class DraftMetadataChecksResultsController @Inject() (
}
}

private def actionMessage(fileError: FileError.FileError)(implicit messages: Messages): String = {
implicit val defaultLang: Lang = Lang.defaultLang

private def actionMessage(fileError: FileError.FileError): String = {
val key = s"draftMetadata.validation.action.$fileError"
if (Messages.isDefinedAt(key))
Messages(key)
if (messages.isDefinedAt(key))
messages(key)
else
s"Require action message for $key"
}

private def detailsMessage(fileError: FileError.FileError)(implicit messages: Messages): String = {
private def detailsMessage(fileError: FileError.FileError): String = {
val key = s"draftMetadata.validation.details.$fileError"
if (Messages.isDefinedAt(key))
Messages(key)
if (messages.isDefinedAt(key))
messages(key)
else
s"Require details message for $key"
}
Expand Down
8 changes: 7 additions & 1 deletion app/controllers/util/RedirectUtils.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import graphql.codegen.GetConsignmentStatus.getConsignmentStatus.GetConsignment.
import play.api.mvc.Result
import play.api.mvc.Results.Redirect
import services.ConsignmentStatusService
import services.Statuses.{InProgressValue, MetadataReviewType}
import services.Statuses.{CompletedValue, InProgressValue, MetadataReviewType}

import java.util.UUID

Expand All @@ -18,4 +18,10 @@ object RedirectUtils {
Redirect(routes.MetadataReviewStatusController.metadataReviewStatusPage(consignmentId))
} else requestedPage
}

def redirectIfReviewNotCompleted(consignmentId: UUID, consignmentStatuses: Seq[ConsignmentStatuses]): Result => Result = requestedPage => {
if (ConsignmentStatusService.statusValue(MetadataReviewType)(consignmentStatuses) != CompletedValue) {
Redirect(routes.MetadataReviewStatusController.metadataReviewStatusPage(consignmentId))
} else requestedPage
}
}
2 changes: 1 addition & 1 deletion app/views/judgment/judgmentUpload.scala.html
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ <h2 class="govuk-error-summary__title" id="error-summary-title">
<form id="file-upload-form" data-consignment-id="@consignmentId">
<div class="govuk-form-group">
<div class="drag-and-drop">
<div id="success-and-removal-message-container" class="govuk-summary-list govuk-file-upload govuk-visually-hidden">
<div class="success-and-removal-message-container govuk-summary-list govuk-file-upload govuk-visually-hidden">
<div class="govuk-summary-list__row">
<dd class="govuk-summary-list__value drag-and-drop__success"
tabindex="-1" role="alert" aria-describedby="success-message-text">
Expand Down
26 changes: 13 additions & 13 deletions app/views/standard/upload.scala.html
Original file line number Diff line number Diff line change
Expand Up @@ -70,26 +70,26 @@ <h2 class="govuk-error-summary__title" id="error-summary-title">
</label>
</div>
</div>
<div id="success-and-removal-message-container" class="govuk-summary-list govuk-file-upload govuk-visually-hidden">
<div class="js-drag-and-drop-selected drag-and-drop__selected" id="item-selection-success-container">
<p id="success-message-text" aria-live="assertive" aria-atomic="true" class="govuk-!-margin-bottom-3 govuk-!-margin-top-0 drag-and-drop__selected__description">The folder <strong id="files-selected-folder-name" class="folder-name"></strong> (containing <span class="folder-size"></span>) has been selected.</p>
<div class="success-and-removal-message-container govuk-!-margin-bottom-6 govuk-visually-hidden">
<div class="js-drag-and-drop-selected" id="item-selection-success-container">
<p id="success-message-text" aria-live="assertive" aria-atomic="true" class="govuk-body drag-and-drop__selected__description">The folder <strong id="files-selected-folder-name" class="folder-name"></strong> (containing <span class="folder-size"></span>) has been selected.</p>
<a id="remove-file-btn" href="#" aria-describedby="files-selected-folder-name" class="govuk-link govuk-link--no-visited-state govuk-!-font-size-19 govuk-body govuk-!-font-weight-bold">Remove<span class="govuk-visually-hidden">&nbsp; selected files</span></a>
</div>
<div class="js-drag-and-drop-selected drag-and-drop__selected" id="removed-selection-container" hidden="true">
<div class="js-drag-and-drop-selected" id="removed-selection-container" hidden="hidden">
<p id="removed-selection-message-text" class="govuk-error-message">The folder "<span class="folder-name"></span>" (containing <span class="folder-size"></span>) has been removed. Select a folder.</p>
</div>
</div>
<p class="govuk-body">For information on what metadata will be captured during upload, visit the <a href="@routes.FaqController.faq()#metadata-captured" target="_blank" rel="noopener noreferrer" class="govuk-link">FAQ (opens in new tab)</a>.</p>
<div class="govuk-form-group">
<div class="govuk-checkboxes">
<div class="govuk-checkboxes__item">
<input class="govuk-checkboxes__input" id="includeTopLevelFolder" name="includeTopLevelFolder" type="checkbox">
<label class="govuk-label govuk-checkboxes__label" for="includeTopLevelFolder">
Check the box if you want to display the name of your top-level folder in the public catalogue.
</label>
<div class="govuk-form-group govuk-!-margin-bottom-3" id="top-level-folder-checkbox" hidden="hidden">
<div class="govuk-checkboxes govuk-checkboxes--small">
<div class="govuk-checkboxes__item">
<input class="govuk-checkboxes__input" id="includeTopLevelFolder" name="includeTopLevelFolder" type="checkbox">
<label class="govuk-label govuk-checkboxes__label govuk-!-padding-top-0" for="includeTopLevelFolder">
If you want the folder name <strong class="folder-name"></strong> to be displayed on Discovery/the public catalogue, select this checkbox.
</label>
</div>
</div>
</div>
</div>
<p class="govuk-body">For information on what metadata will be captured during upload, visit the <a href="@routes.FaqController.faq()#metadata-captured" target="_blank" rel="noopener noreferrer" class="govuk-link">FAQ (opens in new tab)</a>.</p>
<div class="govuk-button-group">
<button id="start-upload-button" class="govuk-button" type="submit" data-module="govuk-button" role="button">
Start upload
Expand Down
1 change: 1 addition & 0 deletions conf/application.base.conf
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ draft_metadata_s3_bucket_name = ${DRAFT_METADATA_S3_BUCKET_NAME}
featureAccessBlock {
blockDraftMetadataUpload=${BLOCK_DRAFT_METADATA_UPLOAD}
blockMetadataReview=${BLOCK_METADATA_REVIEW}
blockSkipMetadataReview=${BLOCK_SKIP_METADATA_REVIEW}
}

draftMetadata {
Expand Down
1 change: 1 addition & 0 deletions conf/application.local-base.conf
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,5 @@ draft_metadata_s3_bucket_name = "tdr-draft-metadata-intg"
featureAccessBlock {
blockDraftMetadataUpload=false
blockMetadataReview=false
blockSkipMetadataReview=false
}
2 changes: 2 additions & 0 deletions conf/messages
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,5 @@ notification.savedProgress.metadataInfo=Your records and any metadata you added
additionalMetadata.descriptive.sensitive=If the description of a record contains sensitive information, you must enter the full uncensored version on the Descriptive metadata page before entering an alternative description on the Closure metadata page.
draftMetadata.validation.details.SCHEMA_VALIDATION=We found validation errors in the uploaded metadata.
draftMetadata.validation.action.SCHEMA_VALIDATION=Download the report below for details on individual validation errors.
draftMetadata.validation.details.UTF_8=The metadata file you uploaded was not a CSV.
draftMetadata.validation.action.UTF_8=Ensure that you save your Excel file as file type ''CSV UTF-8 (comma separated)''.
7 changes: 5 additions & 2 deletions npm/css-src/sass/_drag-and-drop.scss
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,14 @@
margin-bottom: 0;
}

.drag-and-drop__selected {
align-items: baseline;
.success-and-removal-message-container {
border-bottom: 1px solid #b1b4b6;
}

.js-drag-and-drop-selected {
display: flex;
gap: 15px;
align-items: baseline;
}

.drag-and-drop__selected__description {
Expand Down
16 changes: 14 additions & 2 deletions npm/src/checks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,24 @@ export class Checks {

updateFileCheckProgress: (
isJudgmentUser: boolean,
goToNextPage: (formId: string) => void
goToNextPage: (formId: string) => void,
checksPageRefreshInterval: number
) => void | Error = (
isJudgmentUser: boolean,
goToNextPage: (formId: string) => void
goToNextPage: (formId: string) => void,
checksPageRefreshInterval: number
) => {
if (isJudgmentUser) {
this.checkJudgmentTransferProgress(goToNextPage)
} else {
const refreshPageIntervalId: ReturnType<typeof setInterval> = setInterval(
async () => {
clearInterval(intervalId)
window.location.reload()
},
checksPageRefreshInterval
)

const intervalId: ReturnType<typeof setInterval> = setInterval(
async () => {
const fileChecksProgress: IFileCheckProgress | Error =
Expand All @@ -49,9 +59,11 @@ export class Checks {
const checksCompleted = haveFileChecksCompleted(fileChecksProgress)
if (checksCompleted) {
clearInterval(intervalId)
clearInterval(refreshPageIntervalId)
displayChecksCompletedBanner("file-checks")
}
} else {
clearInterval(refreshPageIntervalId)
return fileChecksProgress
}
},
Expand Down
6 changes: 5 additions & 1 deletion npm/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,13 @@ export const renderModules = async () => {
const nextPageModule = await import(
"./nextpageredirect/next-page-redirect"
)
//interval for page reload set at 90% of token validity period
const checksPageRefreshInterval =
(keycloak.tokenParsed?.exp * 1000 - Date.now()) * 0.9
const resultOrError = new checksModule.Checks().updateFileCheckProgress(
isJudgmentUser,
nextPageModule.goToNextPage
nextPageModule.goToNextPage,
checksPageRefreshInterval
)
if (errorHandlingModule.isError(resultOrError)) {
errorHandlingModule.handleUploadError(resultOrError)
Expand Down
13 changes: 11 additions & 2 deletions npm/src/upload/form/update-and-display-success-message.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,13 @@ export const addFolderSelectionSuccessMessage = (
const selectedContentFragment = document
.createRange()
.createContextualFragment(e.firstElementChild.innerHTML)
selectedContentFragment.querySelector(".folder-name").textContent =
folderName

const folderElements =
document.querySelectorAll<HTMLElement>(".folder-name")

folderElements.forEach((element) => {
element.textContent = folderName
})
selectedContentFragment.querySelector(".folder-size").textContent =
`${folderSize} ${folderSize === 1 ? "file" : "files"}`

Expand All @@ -39,6 +44,9 @@ export const displaySelectionSuccessMessage = (
const successMessageContainer: HTMLElement | null = document.querySelector(
"#item-selection-success-container"
)
const topLevelFolderCheckBox: HTMLElement | null = document.querySelector(
"#top-level-folder-checkbox"
)

selectionArea?.classList.remove("govuk-form-group--error")

Expand All @@ -49,6 +57,7 @@ export const displaySelectionSuccessMessage = (
)

successMessageContainer?.removeAttribute("hidden")
topLevelFolderCheckBox?.removeAttribute("hidden")
successMessage?.classList.remove("govuk-visually-hidden")
successMessage?.focus()
}
6 changes: 5 additions & 1 deletion npm/src/upload/form/upload-form.ts
Original file line number Diff line number Diff line change
Expand Up @@ -188,9 +188,13 @@ export class UploadForm {
const folderSelectionMessage: HTMLElement | null = document.querySelector(
"#item-selection-success-container"
)
const topLevelFolderCheckBox: HTMLElement | null = document.querySelector(
"#top-level-folder-checkbox"
)

this.selectedFiles = []
folderSelectionMessage?.setAttribute("hidden", "true")
topLevelFolderCheckBox?.setAttribute("hidden", "true")
this.warningMessages.removedSelectionMessage?.removeAttribute("hidden")
this.successAndRemovalMessageContainer?.focus()

Expand Down Expand Up @@ -278,7 +282,7 @@ export class UploadForm {
}

readonly successAndRemovalMessageContainer: HTMLElement | null =
document.querySelector("#success-and-removal-message-container")
document.querySelector(".success-and-removal-message-container")

private getParentFolderName(folder: IEntryWithPath[]) {
const firstItem: IEntryWithPath = folder.filter((f) => isFile(f))[0]
Expand Down
Loading

0 comments on commit afe97b3

Please sign in to comment.