diff --git a/.phpunit.result.cache b/.phpunit.result.cache index aa1a093..b5a34fb 100644 --- a/.phpunit.result.cache +++ b/.phpunit.result.cache @@ -1 +1 @@ -{"version":2,"defects":{"CryptoTest::testEncryptDecryptEmptyString":7,"CryptoTest::testDecryptWithTamperedCiphertextReturnsEmpty":7,"EmailObfuscatorTest::testEmailTextReplacesBareEmail":7,"SystemControllerHelpersTest::testHumanBytesOneMB":7,"SystemControllerHelpersTest::testHumanBytesOneGB":7,"CryptoTest::testEncryptEmptyStringProducesCiphertext":7,"DatabaseExtendedTest::testFindOrCreateAuthorCreatesNew":7,"DatabaseExtendedTest::testFindOrCreateAuthorIdempotent":7,"DatabaseExtendedTest::testFindOrCreateAuthorWithEmail":7,"DatabaseExtendedTest::testFindOrCreateAuthorRejectsCSVArtefacts":7,"DatabaseExtendedTest::testDeduplicateLanguagesMergesCaseInsensitiveDupes":7,"RateLimitExtendedTest::testGetRemainingDecrements":7,"RateLimitExtendedTest::testGetRemainingAtLimit":7,"RateLimitExtendedTest::testGetRemainingReturnsZeroAfterExhaustion":7,"RateLimitExtendedTest::testGetResetTimePositiveAfterHits":7,"ThesisCreateValidationTest::testDuplicateTagsAreDeduplicated":8,"ThesisEditValidationTest::testLoadReturnsDataForKnownId":8},"times":{"CryptoTest::testEncryptDecryptRoundTrip":0.022,"CryptoTest::testEncryptDecryptWithUnicode":0.002,"CryptoTest::testEncryptDecryptMultiline":0.002,"CryptoTest::testDifferentPlaintextsProduceDifferentCiphertexts":0.002,"CryptoTest::testSamePlaintextProducesDifferentCiphertexts":0.002,"CryptoTest::testIsEncryptedRecognizesEncryptedValue":0.002,"CryptoTest::testIsEncryptedRejectsPlaintext":0.002,"CryptoTest::testIsEncryptedReturnsFalseForEmptyString":0.002,"CryptoTest::testIsEncryptedRejectsInvalidBase64":0.002,"CryptoTest::testEncryptDecryptEmptyString":0.008,"CryptoTest::testDecryptEmptyStringReturnsEmpty":0.002,"CryptoTest::testDecryptInvalidBase64ReturnsInputGracefully":0.002,"CryptoTest::testDecryptTooShortBlobReturnsInputGracefully":0.001,"CryptoTest::testDecryptWithTamperedCiphertextReturnsEmpty":0.002,"CryptoTest::testDecryptValidBlobTamperedTagReturnsEmpty":0.002,"EmailObfuscatorTest::testEncodeContainsNoAtSign":0.016,"EmailObfuscatorTest::testEncodeOutputIsNumericEntities":0.002,"EmailObfuscatorTest::testEmailReturnsObfuscatedAddress":0.002,"EmailObfuscatorTest::testMailtoBuildsCorrectHrefStructure":0.002,"EmailObfuscatorTest::testEmailTextReplacesBareEmail":0.002,"EmailObfuscatorTest::testEmailTextReplacesMultipleEmails":0.002,"EmailObfuscatorTest::testMailtoInTextReplacesMailtoLinks":0.002,"EmailObfuscatorTest::testObfuscateHtmlReplacesAnchorTag":0.002,"EmailObfuscatorTest::testObfuscateHtmlKeepsNonMailtoLinksUnchanged":0.002,"EmailObfuscatorTest::testObfuscateHtmlPreservesCustomLinkText":0.002,"EmailObfuscatorTest::testEmptyStringReturnsEmpty":0.001,"EmailObfuscatorTest::testStringWithNoEmailsIsUnchanged":0.002,"EmailObfuscatorTest::testAlreadyObfuscatedContentIsNotDoubleEncoded":0.003,"EmailObfuscatorTest::testMultipleEmailsInOneString":0.003,"EmailObfuscatorTest::testEmailWithPlusSign":0.002,"StudentEmailTest::testBuildHtmlReturnsNonEmptyString":0.027,"StudentEmailTest::testBuildHtmlContainsKeyFields":0.002,"StudentEmailTest::testBuildHtmlEscapesSpecialCharacters":0.003,"StudentEmailTest::testBuildHtmlHandlesMissingOptionalFields":0.002,"StudentEmailTest::testBuildHtmlHandlesNullFieldsGracefully":0.002,"StudentEmailTest::testBuildHtmlHandlesEmptyArray":0.002,"StudentEmailTest::testBuildHtmlContainsLabelFields":0.002,"SystemControllerHelpersTest::testHumanBytesZero":0.122,"SystemControllerHelpersTest::testHumanBytesBelowOneKB":0.002,"SystemControllerHelpersTest::testHumanBytesOneKB":0.002,"SystemControllerHelpersTest::testHumanBytesOneMB":0.002,"SystemControllerHelpersTest::testHumanBytesOneGB":0.002,"SystemControllerHelpersTest::testHumanBytes1523MB":0.003,"SystemControllerHelpersTest::testHumanBytes2500GB":0.002,"SystemControllerHelpersTest::testDiskColorBelowWarning":0.002,"SystemControllerHelpersTest::testDiskColorWarning":0.002,"SystemControllerHelpersTest::testDiskColorCritical":0.002,"SystemControllerHelpersTest::testLogLineClassCrit":0.002,"SystemControllerHelpersTest::testLogLineClassError":0.002,"SystemControllerHelpersTest::testLogLineClassWarn":0.002,"SystemControllerHelpersTest::testLogLineClassNotice":0.003,"SystemControllerHelpersTest::testLogLineClassHttp500":0.003,"SystemControllerHelpersTest::testLogLineClassHttp300":0.002,"SystemControllerHelpersTest::testLogLineClassDefault":0.003,"SystemControllerHelpersTest::testNginxLineClassComment":0.003,"SystemControllerHelpersTest::testNginxLineClassBlock":0.004,"SystemControllerHelpersTest::testNginxLineClassDirective":0.003,"SystemControllerHelpersTest::testStatusLabelActive":0.002,"SystemControllerHelpersTest::testStatusLabelInactive":0.002,"SystemControllerHelpersTest::testStatusLabelFailed":0.002,"SystemControllerHelpersTest::testStatusLabelWarn":0.002,"SystemControllerHelpersTest::testStatusLabelUnknown":0.002,"SystemControllerHelpersTest::testStatusClassOk":0.004,"SystemControllerHelpersTest::testStatusClassWarn":0.002,"SystemControllerHelpersTest::testStatusClassError":0.002,"SystemControllerHelpersTest::testStatusClassUnknown":0.002,"TfeControllerOgTest::testBuildOgTagsReturnsAllRequiredKeys":0.003,"TfeControllerOgTest::testBuildOgTagsTitleIncludesAuthors":0.003,"TfeControllerOgTest::testBuildOgTagsImageEmptyWhenNoFiles":0.002,"TfeControllerOgTest::testBuildOgTagsImageFromCover":0.002,"TfeControllerOgTest::testBuildOgTagsImageFallbackToFirstImage":0.002,"TfeControllerOgTest::testBuildOgTagsUrlIncludesThesisId":0.003,"TfeControllerOgTest::testBuildOgTagsPublishedTimeFormatted":0.002,"TfeControllerOgTest::testBuildOgTagsPublishedTimeEmptyWhenNoYear":0.002,"TfeControllerOgTest::testBuildMetaDescriptionTruncatesLongSynopsis":0.002,"TfeControllerOgTest::testBuildMetaDescriptionKeepsShortSynopsis":0.002,"TfeControllerOgTest::testBuildMetaDescriptionEmptySynopsisReturnsDefault":0.003,"TfeControllerOgTest::testBuildMetaDescriptionStripsHtmlTags":0.003,"CryptoTest::testEncryptEmptyStringProducesCiphertext":0.002,"DatabaseExtendedTest::testEscapeLikeStringViaSearchConditions":0.002,"DatabaseExtendedTest::testBuildSearchConditionsEmptyParams":0.002,"DatabaseExtendedTest::testBuildSearchConditionsWithQuery":0.002,"DatabaseExtendedTest::testBuildSearchConditionsWithYear":0.002,"DatabaseExtendedTest::testBuildSearchConditionsWithAllFilters":0.002,"DatabaseExtendedTest::testFindDuplicateThesisExactMatch":0.002,"DatabaseExtendedTest::testFindDuplicateThesisMissesDifferentTitle":0.003,"DatabaseExtendedTest::testFindDuplicateThesisMissesDifferentYear":0.002,"DatabaseExtendedTest::testFindDuplicateThesisEmptyAuthorNamesReturnsNull":0.002,"DatabaseExtendedTest::testFindDuplicateThesisEmptyTable":0.002,"DatabaseExtendedTest::testFindDuplicateThesisNearDuplicateByLevenshtein":0.002,"DatabaseExtendedTest::testGenerateThesisIdentifierFirstInYear":0.002,"DatabaseExtendedTest::testGenerateThesisIdentifierIncrementsCorrectly":0.002,"DatabaseExtendedTest::testGenerateThesisIdentifierUsesMaxNotCount":0.002,"DatabaseExtendedTest::testGetCoverPathsForThesesReturnsPaths":0.002,"DatabaseExtendedTest::testGetCoverPathsForThesesReturnsEmptyForUnknownIds":0.002,"DatabaseExtendedTest::testGetCoverPathsForThesesEmptyInputReturnsEmpty":0.003,"DatabaseExtendedTest::testGetCoverPathsForThesesMultipleTheses":0.002,"DatabaseExtendedTest::testFindOrCreateAuthorCreatesNew":0.002,"DatabaseExtendedTest::testFindOrCreateAuthorIdempotent":0.002,"DatabaseExtendedTest::testFindOrCreateAuthorWithEmail":0.002,"DatabaseExtendedTest::testFindOrCreateAuthorRejectsCSVArtefacts":0.002,"DatabaseExtendedTest::testDeduplicateLanguagesMergesCaseInsensitiveDupes":0.002,"DatabaseExtendedTest::testRenameLanguageUpdatesName":0.034,"DatabaseExtendedTest::testMergeLanguageReassignsTheses":0.003,"DatabaseExtendedTest::testRenameTagUpdatesName":0.003,"DatabaseExtendedTest::testMergeTagReassignsTheses":0.003,"RateLimitExtendedTest::testCheckKeyCountsPerKey":0.031,"RateLimitExtendedTest::testCheckKeyDoesNotAffectDefaultCheck":0.002,"RateLimitExtendedTest::testGetRemainingDecrements":0,"RateLimitExtendedTest::testGetRemainingAtLimit":0,"RateLimitExtendedTest::testGetRemainingUsesClientIdentifier":0.001,"RateLimitExtendedTest::testCheckUsesConsistentIdentifier":0,"RateLimitExtendedTest::testGetRemainingReturnsZeroAfterExhaustion":0.002,"RateLimitExtendedTest::testGetResetTimeReturnsZeroWhenNoData":0.002,"RateLimitExtendedTest::testGetResetTimePositiveAfterHits":0.002,"RateLimitExtendedTest::testCleanupRemovesOldFiles":0.002,"ShareLinkExtendedTest::testListActiveReturnsOnlyActiveLinks":0.328,"ShareLinkExtendedTest::testListArchivedReturnsOnlyArchivedLinks":0.286,"ShareLinkExtendedTest::testFindBySlugHit":0.292,"ShareLinkExtendedTest::testFindBySlugMiss":0.003,"ShareLinkExtendedTest::testSetPasswordAndDecryptRoundTrip":0.28,"ShareLinkExtendedTest::testGetDecryptedPasswordOnNonexistentId":0.004,"ShareLinkExtendedTest::testUpdateChangesNameAndExpiration":0.279,"ShareLinkExtendedTest::testUpdateOnlyNameLeavesExpirationUnchanged":0.278,"ShareLinkExtendedTest::testUpdateClearsExpiration":0.281,"ShareLinkExtendedTest::testCreateWithLockedYear":0.278,"ShareLinkExtendedTest::testCreateWithInvalidLockedYearRejected":0.278,"ShareLinkExtendedTest::testUpdateLockedYear":0.278,"ShareLinkExtendedTest::testUpdateClearLockedYear":0.278,"ShareLinkExtendedTest::testIncrementUsage":0.285,"ShareLinkExtendedTest::testCreateDefaultsToTfeWhenInvalidObjet":0.294,"ShareLinkExtendedTest::testCreateAcceptsValidObjet":0.282,"RateLimitExtendedTest::testGetRemainingStartsAtMax":0.002,"RateLimitExtendedTest::testCheckDecrementsRemainingForSameIp":0.002,"RateLimitExtendedTest::testCheckAndCheckKeyAreIndependent":0.002,"RateLimitExtendedTest::testMultipleChecksFromSameClient":0.002,"AutofocusFieldForErrorTest::testCreateAutofocusTitle":0.921,"AutofocusFieldForErrorTest::testCreateAutofocusAuthors":0.002,"AutofocusFieldForErrorTest::testCreateAutofocusSynopsis":0.002,"AutofocusFieldForErrorTest::testCreateAutofocusYear":0.003,"AutofocusFieldForErrorTest::testCreateAutofocusOrientation":0.002,"AutofocusFieldForErrorTest::testCreateAutofocusAP":0.002,"AutofocusFieldForErrorTest::testCreateAutofocusFinality":0.002,"AutofocusFieldForErrorTest::testCreateAutofocusLanguages":0.001,"AutofocusFieldForErrorTest::testCreateAutofocusPromoteur":0.002,"AutofocusFieldForErrorTest::testCreateAutofocusLecteurInterne":0.002,"AutofocusFieldForErrorTest::testCreateAutofocusLecteurExterne":0.002,"AutofocusFieldForErrorTest::testCreateAutofocusFormats":0.002,"AutofocusFieldForErrorTest::testCreateAutofocusLicense":0.002,"AutofocusFieldForErrorTest::testCreateAutofocusUrl":0.002,"AutofocusFieldForErrorTest::testCreateAutofocusTags":0.002,"AutofocusFieldForErrorTest::testCreateAutofocusUnknownErrorReturnsNull":0.002,"AutofocusFieldForErrorTest::testEditAutofocusTitle":0.134,"AutofocusFieldForErrorTest::testEditAutofocusYear":0.002,"AutofocusFieldForErrorTest::testEditAutofocusSynopsis":0.001,"AutofocusFieldForErrorTest::testEditAutofocusAuthors":0.001,"AutofocusFieldForErrorTest::testEditAutofocusUnknownErrorReturnsNull":0.002,"AutofocusFieldForErrorTest::testCreateDoesNotLeakEditFieldNames":0.002,"ThesisCreateValidationTest::testValidSubmissionReturnsCleanedData":0.002,"ThesisCreateValidationTest::testMissingTitleThrowsException":0.003,"ThesisCreateValidationTest::testMissingAuthorsThrowsException":0.004,"ThesisCreateValidationTest::testMissingSynopsisThrowsException":0.002,"ThesisCreateValidationTest::testMissingOrientationInNonAdminModeThrowsException":0.002,"ThesisCreateValidationTest::testMissingAPProgramInNonAdminModeThrowsException":0.004,"ThesisCreateValidationTest::testMissingFinalityInNonAdminModeThrowsException":0.004,"ThesisCreateValidationTest::testInvalidYearFormatRejected":0.002,"ThesisCreateValidationTest::testYearZeroRejected":0.003,"ThesisCreateValidationTest::testYearBefore2000Rejected":0.002,"ThesisCreateValidationTest::testFarFutureYearRejected":0.002,"ThesisCreateValidationTest::testCurrentYearAccepted":0.002,"ThesisCreateValidationTest::testMalformedUrlRejected":0.004,"ThesisCreateValidationTest::testValidUrlAccepted":0.002,"ThesisCreateValidationTest::testDuplicateTagsAreDeduplicated":0.002,"ThesisCreateValidationTest::testMaxTenKeywordsEnforced":0.002,"ThesisCreateValidationTest::testXssPayloadStrippedFromTitle":0.003,"ThesisCreateValidationTest::testHtmlInSynopsisStripped":0.002,"ThesisCreateValidationTest::testMultipleAuthorsAreSorted":0.002,"ThesisCreateValidationTest::testMissingPromoteurInNonAdminModeThrowsException":0.002,"ThesisCreateValidationTest::testMissingLecteurInterneInNonAdminModeThrowsException":0.002,"ThesisCreateValidationTest::testMissingLanguagesInNonAdminModeThrowsException":0.003,"ThesisCreateValidationTest::testMissingFormatsInNonAdminModeThrowsException":0.003,"ThesisCreateValidationTest::testMissingLicenseWithLibreAccessThrowsException":0.003,"ThesisEditValidationTest::testLoadReturnsDataForKnownId":0.007,"ThesisEditValidationTest::testLoadThrowsOnUnknownId":0.003,"ThesisEditValidationTest::testLoadThrowsOnInvalidId":0.002,"ThesisEditValidationTest::testLoadThrowsOnNegativeId":0.004,"ThesisEditValidationTest::testCollectJuryMembersEmptyInput":0.002,"ThesisEditValidationTest::testCollectJuryMembersSinglePromoteur":0.002,"ThesisEditValidationTest::testCollectJuryMembersPromoteurUlb":0.002,"ThesisEditValidationTest::testCollectJuryMembersLecteurs":0.002,"ThesisEditValidationTest::testCollectJuryMembersDeduplicatesEmptyStrings":0.002,"ThesisEditValidationTest::testCollectJuryMembersScalarPromoteurAccepted":0.003,"ThesisEditValidationTest::testHandleWebsiteUrlStoresValidUrl":0.003,"ThesisEditValidationTest::testHandleWebsiteUrlSkipsInvalidUrl":0.003,"ThesisEditValidationTest::testHandleWebsiteUrlSkipsEmptyUrl":0.003,"ThesisEditValidationTest::testHandleWebsiteUrlNormalisesHttp":0.003,"ErrorHandlerTest::testFkThesesTableMentionsAllPossibleFields":0.025,"ErrorHandlerTest::testFkApPrograms":0.002,"ErrorHandlerTest::testFkFinalityTypes":0.001,"ErrorHandlerTest::testFkThesisLanguages":0.002,"ErrorHandlerTest::testFkThesisFormats":0.002,"ErrorHandlerTest::testFkThesisTags":0.002,"ErrorHandlerTest::testFkThesisSupervisors":0.002,"ErrorHandlerTest::testFkAccessTypes":0.001,"ErrorHandlerTest::testFkLicenseTypes":0.002,"ErrorHandlerTest::testFkAuthors":0.002,"ErrorHandlerTest::testFkQuotedTableName":0.001,"ErrorHandlerTest::testFkQuotedLanguages":0.001,"ErrorHandlerTest::testFkQuotedFormatTypes":0.002,"ErrorHandlerTest::testFkReferencesTags":0.002,"ErrorHandlerTest::testFkReferencesOrientations":0.002,"ErrorHandlerTest::testFkUnknownTableGenericFallback":0.002,"ErrorHandlerTest::testFkEmptyMessageGenericFallback":0.002,"ErrorHandlerTest::testUniqueConstraint":0.002,"ErrorHandlerTest::testNotNullConstraint":0.002,"ErrorHandlerTest::testGenericPdoError":0.002,"ErrorHandlerTest::testDuplicateThesisExceptionPassesThrough":0.005,"ErrorHandlerTest::testValidationExceptionPassesThrough":0.002,"ErrorHandlerTest::testGenericExceptionPassesThrough":0.002,"ErrorHandlerTest::testTypeErrorReturnsGeneric":0.002,"ErrorHandlerTest::testLogWithContext":0.002,"ErrorHandlerTest::testLogWithNullValues":0.002,"ErrorHandlerTest::testLogWithEmptyExtra":0.002,"ErrorHandlerTest::testFkQuotedColumnNames":0.002,"ErrorHandlerTest::testFkUpdateStatement":0.001,"ErrorHandlerTest::testFkWithReferencesAndInsert":0.002,"PureLogicTest::testSplitJuryByRoleAllRoles":0.042,"PureLogicTest::testSplitJuryByRoleEmptyNameSkipped":0.002,"PureLogicTest::testSplitJuryByRoleEmptyJury":0.002,"PureLogicTest::testCollectCaptionPathsVttByMime":0.002,"PureLogicTest::testCollectCaptionPathsVttByExtension":0.002,"PureLogicTest::testCollectCaptionPathsNoVttReturnsEmpty":0.002,"PureLogicTest::testDetectFileTypeByMime":0.002,"PureLogicTest::testDetectFileTypeByExtensionFallback":0.002,"SearchControllerTest::testHandleSearchReturnsCoverMapKey":0.057,"SearchControllerTest::testCoverMapContainsKnownThesis":0.005}} \ No newline at end of file +{"version":2,"defects":{"CryptoTest::testEncryptDecryptEmptyString":7,"CryptoTest::testDecryptWithTamperedCiphertextReturnsEmpty":7,"EmailObfuscatorTest::testEmailTextReplacesBareEmail":7,"SystemControllerHelpersTest::testHumanBytesOneMB":7,"SystemControllerHelpersTest::testHumanBytesOneGB":7,"CryptoTest::testEncryptEmptyStringProducesCiphertext":7,"DatabaseExtendedTest::testFindOrCreateAuthorCreatesNew":7,"DatabaseExtendedTest::testFindOrCreateAuthorIdempotent":7,"DatabaseExtendedTest::testFindOrCreateAuthorWithEmail":7,"DatabaseExtendedTest::testFindOrCreateAuthorRejectsCSVArtefacts":7,"DatabaseExtendedTest::testDeduplicateLanguagesMergesCaseInsensitiveDupes":7,"RateLimitExtendedTest::testGetRemainingDecrements":7,"RateLimitExtendedTest::testGetRemainingAtLimit":7,"RateLimitExtendedTest::testGetRemainingReturnsZeroAfterExhaustion":7,"RateLimitExtendedTest::testGetResetTimePositiveAfterHits":7,"ThesisCreateValidationTest::testDuplicateTagsAreDeduplicated":8,"ThesisEditValidationTest::testLoadReturnsDataForKnownId":8},"times":{"CryptoTest::testEncryptDecryptRoundTrip":0.002,"CryptoTest::testEncryptDecryptWithUnicode":0,"CryptoTest::testEncryptDecryptMultiline":0,"CryptoTest::testDifferentPlaintextsProduceDifferentCiphertexts":0,"CryptoTest::testSamePlaintextProducesDifferentCiphertexts":0.001,"CryptoTest::testIsEncryptedRecognizesEncryptedValue":0.001,"CryptoTest::testIsEncryptedRejectsPlaintext":0,"CryptoTest::testIsEncryptedReturnsFalseForEmptyString":0,"CryptoTest::testIsEncryptedRejectsInvalidBase64":0,"CryptoTest::testEncryptDecryptEmptyString":0.008,"CryptoTest::testDecryptEmptyStringReturnsEmpty":0,"CryptoTest::testDecryptInvalidBase64ReturnsInputGracefully":0,"CryptoTest::testDecryptTooShortBlobReturnsInputGracefully":0,"CryptoTest::testDecryptWithTamperedCiphertextReturnsEmpty":0,"CryptoTest::testDecryptValidBlobTamperedTagReturnsEmpty":0,"EmailObfuscatorTest::testEncodeContainsNoAtSign":0.001,"EmailObfuscatorTest::testEncodeOutputIsNumericEntities":0.001,"EmailObfuscatorTest::testEmailReturnsObfuscatedAddress":0.001,"EmailObfuscatorTest::testMailtoBuildsCorrectHrefStructure":0.001,"EmailObfuscatorTest::testEmailTextReplacesBareEmail":0.001,"EmailObfuscatorTest::testEmailTextReplacesMultipleEmails":0.001,"EmailObfuscatorTest::testMailtoInTextReplacesMailtoLinks":0.001,"EmailObfuscatorTest::testObfuscateHtmlReplacesAnchorTag":0.002,"EmailObfuscatorTest::testObfuscateHtmlKeepsNonMailtoLinksUnchanged":0.001,"EmailObfuscatorTest::testObfuscateHtmlPreservesCustomLinkText":0.001,"EmailObfuscatorTest::testEmptyStringReturnsEmpty":0,"EmailObfuscatorTest::testStringWithNoEmailsIsUnchanged":0.001,"EmailObfuscatorTest::testAlreadyObfuscatedContentIsNotDoubleEncoded":0.001,"EmailObfuscatorTest::testMultipleEmailsInOneString":0.001,"EmailObfuscatorTest::testEmailWithPlusSign":0.001,"StudentEmailTest::testBuildHtmlReturnsNonEmptyString":0.001,"StudentEmailTest::testBuildHtmlContainsKeyFields":0.001,"StudentEmailTest::testBuildHtmlEscapesSpecialCharacters":0.001,"StudentEmailTest::testBuildHtmlHandlesMissingOptionalFields":0.001,"StudentEmailTest::testBuildHtmlHandlesNullFieldsGracefully":0.001,"StudentEmailTest::testBuildHtmlHandlesEmptyArray":0.001,"StudentEmailTest::testBuildHtmlContainsLabelFields":0.001,"SystemControllerHelpersTest::testHumanBytesZero":0.001,"SystemControllerHelpersTest::testHumanBytesBelowOneKB":0.001,"SystemControllerHelpersTest::testHumanBytesOneKB":0,"SystemControllerHelpersTest::testHumanBytesOneMB":0,"SystemControllerHelpersTest::testHumanBytesOneGB":0,"SystemControllerHelpersTest::testHumanBytes1523MB":0,"SystemControllerHelpersTest::testHumanBytes2500GB":0.001,"SystemControllerHelpersTest::testDiskColorBelowWarning":0.001,"SystemControllerHelpersTest::testDiskColorWarning":0.001,"SystemControllerHelpersTest::testDiskColorCritical":0.001,"SystemControllerHelpersTest::testLogLineClassCrit":0.001,"SystemControllerHelpersTest::testLogLineClassError":0.001,"SystemControllerHelpersTest::testLogLineClassWarn":0,"SystemControllerHelpersTest::testLogLineClassNotice":0.001,"SystemControllerHelpersTest::testLogLineClassHttp500":0.001,"SystemControllerHelpersTest::testLogLineClassHttp300":0.001,"SystemControllerHelpersTest::testLogLineClassDefault":0.001,"SystemControllerHelpersTest::testNginxLineClassComment":0.003,"SystemControllerHelpersTest::testNginxLineClassBlock":0.004,"SystemControllerHelpersTest::testNginxLineClassDirective":0.003,"SystemControllerHelpersTest::testStatusLabelActive":0,"SystemControllerHelpersTest::testStatusLabelInactive":0.001,"SystemControllerHelpersTest::testStatusLabelFailed":0,"SystemControllerHelpersTest::testStatusLabelWarn":0,"SystemControllerHelpersTest::testStatusLabelUnknown":0,"SystemControllerHelpersTest::testStatusClassOk":0,"SystemControllerHelpersTest::testStatusClassWarn":0.001,"SystemControllerHelpersTest::testStatusClassError":0.001,"SystemControllerHelpersTest::testStatusClassUnknown":0.001,"TfeControllerOgTest::testBuildOgTagsReturnsAllRequiredKeys":0.001,"TfeControllerOgTest::testBuildOgTagsTitleIncludesAuthors":0.001,"TfeControllerOgTest::testBuildOgTagsImageEmptyWhenNoFiles":0.001,"TfeControllerOgTest::testBuildOgTagsImageFromCover":0.001,"TfeControllerOgTest::testBuildOgTagsImageFallbackToFirstImage":0.001,"TfeControllerOgTest::testBuildOgTagsUrlIncludesThesisId":0.001,"TfeControllerOgTest::testBuildOgTagsPublishedTimeFormatted":0.001,"TfeControllerOgTest::testBuildOgTagsPublishedTimeEmptyWhenNoYear":0.001,"TfeControllerOgTest::testBuildMetaDescriptionTruncatesLongSynopsis":0.001,"TfeControllerOgTest::testBuildMetaDescriptionKeepsShortSynopsis":0.001,"TfeControllerOgTest::testBuildMetaDescriptionEmptySynopsisReturnsDefault":0.001,"TfeControllerOgTest::testBuildMetaDescriptionStripsHtmlTags":0.001,"CryptoTest::testEncryptEmptyStringProducesCiphertext":0.001,"DatabaseExtendedTest::testEscapeLikeStringViaSearchConditions":0.001,"DatabaseExtendedTest::testBuildSearchConditionsEmptyParams":0.001,"DatabaseExtendedTest::testBuildSearchConditionsWithQuery":0.001,"DatabaseExtendedTest::testBuildSearchConditionsWithYear":0.001,"DatabaseExtendedTest::testBuildSearchConditionsWithAllFilters":0,"DatabaseExtendedTest::testFindDuplicateThesisExactMatch":0.001,"DatabaseExtendedTest::testFindDuplicateThesisMissesDifferentTitle":0.001,"DatabaseExtendedTest::testFindDuplicateThesisMissesDifferentYear":0.002,"DatabaseExtendedTest::testFindDuplicateThesisEmptyAuthorNamesReturnsNull":0.001,"DatabaseExtendedTest::testFindDuplicateThesisEmptyTable":0.001,"DatabaseExtendedTest::testFindDuplicateThesisNearDuplicateByLevenshtein":0.001,"DatabaseExtendedTest::testGenerateThesisIdentifierFirstInYear":0,"DatabaseExtendedTest::testGenerateThesisIdentifierIncrementsCorrectly":0.001,"DatabaseExtendedTest::testGenerateThesisIdentifierUsesMaxNotCount":0.002,"DatabaseExtendedTest::testGetCoverPathsForThesesReturnsPaths":0.001,"DatabaseExtendedTest::testGetCoverPathsForThesesReturnsEmptyForUnknownIds":0.001,"DatabaseExtendedTest::testGetCoverPathsForThesesEmptyInputReturnsEmpty":0.001,"DatabaseExtendedTest::testGetCoverPathsForThesesMultipleTheses":0.001,"DatabaseExtendedTest::testFindOrCreateAuthorCreatesNew":0,"DatabaseExtendedTest::testFindOrCreateAuthorIdempotent":0,"DatabaseExtendedTest::testFindOrCreateAuthorWithEmail":0,"DatabaseExtendedTest::testFindOrCreateAuthorRejectsCSVArtefacts":0.001,"DatabaseExtendedTest::testDeduplicateLanguagesMergesCaseInsensitiveDupes":0.002,"DatabaseExtendedTest::testRenameLanguageUpdatesName":0.008,"DatabaseExtendedTest::testMergeLanguageReassignsTheses":0.001,"DatabaseExtendedTest::testRenameTagUpdatesName":0,"DatabaseExtendedTest::testMergeTagReassignsTheses":0.001,"RateLimitExtendedTest::testCheckKeyCountsPerKey":0.002,"RateLimitExtendedTest::testCheckKeyDoesNotAffectDefaultCheck":0.002,"RateLimitExtendedTest::testGetRemainingDecrements":0,"RateLimitExtendedTest::testGetRemainingAtLimit":0,"RateLimitExtendedTest::testGetRemainingUsesClientIdentifier":0.001,"RateLimitExtendedTest::testCheckUsesConsistentIdentifier":0,"RateLimitExtendedTest::testGetRemainingReturnsZeroAfterExhaustion":0,"RateLimitExtendedTest::testGetResetTimeReturnsZeroWhenNoData":0.001,"RateLimitExtendedTest::testGetResetTimePositiveAfterHits":0.001,"RateLimitExtendedTest::testCleanupRemovesOldFiles":0.001,"ShareLinkExtendedTest::testListActiveReturnsOnlyActiveLinks":0.312,"ShareLinkExtendedTest::testListArchivedReturnsOnlyArchivedLinks":0.313,"ShareLinkExtendedTest::testFindBySlugHit":0.311,"ShareLinkExtendedTest::testFindBySlugMiss":0,"ShareLinkExtendedTest::testSetPasswordAndDecryptRoundTrip":0.325,"ShareLinkExtendedTest::testGetDecryptedPasswordOnNonexistentId":0.001,"ShareLinkExtendedTest::testUpdateChangesNameAndExpiration":0.318,"ShareLinkExtendedTest::testUpdateOnlyNameLeavesExpirationUnchanged":0.319,"ShareLinkExtendedTest::testUpdateClearsExpiration":0.311,"ShareLinkExtendedTest::testCreateWithLockedYear":0.311,"ShareLinkExtendedTest::testCreateWithInvalidLockedYearRejected":0.313,"ShareLinkExtendedTest::testUpdateLockedYear":0.315,"ShareLinkExtendedTest::testUpdateClearLockedYear":0.347,"ShareLinkExtendedTest::testIncrementUsage":0.336,"ShareLinkExtendedTest::testCreateDefaultsToTfeWhenInvalidObjet":0.322,"ShareLinkExtendedTest::testCreateAcceptsValidObjet":0.316,"RateLimitExtendedTest::testGetRemainingStartsAtMax":0.001,"RateLimitExtendedTest::testCheckDecrementsRemainingForSameIp":0.001,"RateLimitExtendedTest::testCheckAndCheckKeyAreIndependent":0.001,"RateLimitExtendedTest::testMultipleChecksFromSameClient":0,"AutofocusFieldForErrorTest::testCreateAutofocusTitle":0.006,"AutofocusFieldForErrorTest::testCreateAutofocusAuthors":0,"AutofocusFieldForErrorTest::testCreateAutofocusSynopsis":0.001,"AutofocusFieldForErrorTest::testCreateAutofocusYear":0,"AutofocusFieldForErrorTest::testCreateAutofocusOrientation":0,"AutofocusFieldForErrorTest::testCreateAutofocusAP":0,"AutofocusFieldForErrorTest::testCreateAutofocusFinality":0,"AutofocusFieldForErrorTest::testCreateAutofocusLanguages":0,"AutofocusFieldForErrorTest::testCreateAutofocusPromoteur":0,"AutofocusFieldForErrorTest::testCreateAutofocusLecteurInterne":0,"AutofocusFieldForErrorTest::testCreateAutofocusLecteurExterne":0,"AutofocusFieldForErrorTest::testCreateAutofocusFormats":0,"AutofocusFieldForErrorTest::testCreateAutofocusLicense":0,"AutofocusFieldForErrorTest::testCreateAutofocusUrl":0,"AutofocusFieldForErrorTest::testCreateAutofocusTags":0,"AutofocusFieldForErrorTest::testCreateAutofocusUnknownErrorReturnsNull":0.001,"AutofocusFieldForErrorTest::testEditAutofocusTitle":0.001,"AutofocusFieldForErrorTest::testEditAutofocusYear":0,"AutofocusFieldForErrorTest::testEditAutofocusSynopsis":0,"AutofocusFieldForErrorTest::testEditAutofocusAuthors":0,"AutofocusFieldForErrorTest::testEditAutofocusUnknownErrorReturnsNull":0,"AutofocusFieldForErrorTest::testCreateDoesNotLeakEditFieldNames":0,"ThesisCreateValidationTest::testValidSubmissionReturnsCleanedData":0.001,"ThesisCreateValidationTest::testMissingTitleThrowsException":0.003,"ThesisCreateValidationTest::testMissingAuthorsThrowsException":0.001,"ThesisCreateValidationTest::testMissingSynopsisThrowsException":0.001,"ThesisCreateValidationTest::testMissingOrientationInNonAdminModeThrowsException":0.001,"ThesisCreateValidationTest::testMissingAPProgramInNonAdminModeThrowsException":0.001,"ThesisCreateValidationTest::testMissingFinalityInNonAdminModeThrowsException":0.001,"ThesisCreateValidationTest::testInvalidYearFormatRejected":0.001,"ThesisCreateValidationTest::testYearZeroRejected":0.001,"ThesisCreateValidationTest::testYearBefore2000Rejected":0.001,"ThesisCreateValidationTest::testFarFutureYearRejected":0.001,"ThesisCreateValidationTest::testCurrentYearAccepted":0.001,"ThesisCreateValidationTest::testMalformedUrlRejected":0.001,"ThesisCreateValidationTest::testValidUrlAccepted":0,"ThesisCreateValidationTest::testDuplicateTagsAreDeduplicated":0,"ThesisCreateValidationTest::testMaxTenKeywordsEnforced":0.001,"ThesisCreateValidationTest::testXssPayloadStrippedFromTitle":0.001,"ThesisCreateValidationTest::testHtmlInSynopsisStripped":0.001,"ThesisCreateValidationTest::testMultipleAuthorsAreSorted":0.001,"ThesisCreateValidationTest::testMissingPromoteurInNonAdminModeThrowsException":0.001,"ThesisCreateValidationTest::testMissingLecteurInterneInNonAdminModeThrowsException":0.001,"ThesisCreateValidationTest::testMissingLanguagesInNonAdminModeThrowsException":0,"ThesisCreateValidationTest::testMissingFormatsInNonAdminModeThrowsException":0.001,"ThesisCreateValidationTest::testMissingLicenseWithLibreAccessThrowsException":0.001,"ThesisEditValidationTest::testLoadReturnsDataForKnownId":0.006,"ThesisEditValidationTest::testLoadThrowsOnUnknownId":0.001,"ThesisEditValidationTest::testLoadThrowsOnInvalidId":0.001,"ThesisEditValidationTest::testLoadThrowsOnNegativeId":0.001,"ThesisEditValidationTest::testCollectJuryMembersEmptyInput":0.001,"ThesisEditValidationTest::testCollectJuryMembersSinglePromoteur":0.001,"ThesisEditValidationTest::testCollectJuryMembersPromoteurUlb":0.001,"ThesisEditValidationTest::testCollectJuryMembersLecteurs":0.003,"ThesisEditValidationTest::testCollectJuryMembersDeduplicatesEmptyStrings":0.001,"ThesisEditValidationTest::testCollectJuryMembersScalarPromoteurAccepted":0.002,"ThesisEditValidationTest::testHandleWebsiteUrlStoresValidUrl":0.001,"ThesisEditValidationTest::testHandleWebsiteUrlSkipsInvalidUrl":0.001,"ThesisEditValidationTest::testHandleWebsiteUrlSkipsEmptyUrl":0.001,"ThesisEditValidationTest::testHandleWebsiteUrlNormalisesHttp":0.001,"ErrorHandlerTest::testFkThesesTableMentionsAllPossibleFields":0.001,"ErrorHandlerTest::testFkApPrograms":0,"ErrorHandlerTest::testFkFinalityTypes":0,"ErrorHandlerTest::testFkThesisLanguages":0.001,"ErrorHandlerTest::testFkThesisFormats":0,"ErrorHandlerTest::testFkThesisTags":0.001,"ErrorHandlerTest::testFkThesisSupervisors":0,"ErrorHandlerTest::testFkAccessTypes":0,"ErrorHandlerTest::testFkLicenseTypes":0,"ErrorHandlerTest::testFkAuthors":0,"ErrorHandlerTest::testFkQuotedTableName":0,"ErrorHandlerTest::testFkQuotedLanguages":0,"ErrorHandlerTest::testFkQuotedFormatTypes":0,"ErrorHandlerTest::testFkReferencesTags":0,"ErrorHandlerTest::testFkReferencesOrientations":0.001,"ErrorHandlerTest::testFkUnknownTableGenericFallback":0.001,"ErrorHandlerTest::testFkEmptyMessageGenericFallback":0.001,"ErrorHandlerTest::testUniqueConstraint":0.001,"ErrorHandlerTest::testNotNullConstraint":0,"ErrorHandlerTest::testGenericPdoError":0.001,"ErrorHandlerTest::testDuplicateThesisExceptionPassesThrough":0.001,"ErrorHandlerTest::testValidationExceptionPassesThrough":0,"ErrorHandlerTest::testGenericExceptionPassesThrough":0,"ErrorHandlerTest::testTypeErrorReturnsGeneric":0,"ErrorHandlerTest::testLogWithContext":0.001,"ErrorHandlerTest::testLogWithNullValues":0,"ErrorHandlerTest::testLogWithEmptyExtra":0,"ErrorHandlerTest::testFkQuotedColumnNames":0.001,"ErrorHandlerTest::testFkUpdateStatement":0.001,"ErrorHandlerTest::testFkWithReferencesAndInsert":0,"PureLogicTest::testSplitJuryByRoleAllRoles":0.001,"PureLogicTest::testSplitJuryByRoleEmptyNameSkipped":0,"PureLogicTest::testSplitJuryByRoleEmptyJury":0,"PureLogicTest::testCollectCaptionPathsVttByMime":0,"PureLogicTest::testCollectCaptionPathsVttByExtension":0,"PureLogicTest::testCollectCaptionPathsNoVttReturnsEmpty":0,"PureLogicTest::testDetectFileTypeByMime":0,"PureLogicTest::testDetectFileTypeByExtensionFallback":0,"SearchControllerTest::testHandleSearchReturnsCoverMapKey":0.006,"SearchControllerTest::testCoverMapContainsKnownThesis":0.003}} \ No newline at end of file diff --git a/README.md b/README.md index df36c35..c732820 100644 --- a/README.md +++ b/README.md @@ -65,17 +65,9 @@ just deploy just deploy-nginx ``` -### Admin users (htpasswd) - -```bash -just manage-admin-users -# Then on server: -ssh xamxam "sudo bash /tmp/manage-admin-users.sh" -``` - ## Security notes -- Admin panel protected by nginx `auth_basic` + PHP session (`AdminAuth`) +- Admin panel protected by PHP session (`AdminAuth`) — password-only, no username - Uploads stored outside webroot, served via controlled `media.php` - Rate limiting on public search (`src/RateLimit.php`) - See `nginx/docs/SECURITY_HEADERS.md` for security headers reference diff --git a/TODO.md b/TODO.md index 5910059..6e5a3af 100644 --- a/TODO.md +++ b/TODO.md @@ -1,15 +1,26 @@ # TODO > Last updated: 2026-06-11 -> Context: Form Accessibility & Resilience improvements for XAMXAM thesis submission platform +> Context: Removed overtype autosave (403 CSRF bug), replaced with explicit Save button above full-page editor ## Pending +- [ ] #apropos-toc-confirm Visually confirm charte + licence TOC layout renders correctly in browser - [ ] #aria-test-manual Test WCAG changes with VoiceOver and NVDA on full add/edit/partage form flows - [ ] #nojs-upload-test Test end-to-end: submit partage form with JS disabled, verify files arrive via `$_FILES` +- [ ] #csp-media-iframe-deploy Deploy nginx config fix to server, test PDF iframe on /tfe?id=221 ## Completed +- [x] #csp-media-iframe-fix Fix CSP `frame-ancestors 'none'` blocking PDF iframes — replaced `try_files` redirect with direct `fastcgi_pass` in `location = /media` so `add_header` CSP override survives internal nginx redirect `(nginx/xamxam.conf)` ✓ + +- [x] #duration-migration Add migration to reintroduce `duration_value` and `duration_unit` columns + update views `(migrations/applied/040_duration_fields.sql)` ✓ +- [x] #duration-database Update `createThesis`, `updateThesis`, `getThesisRawFields` in Database `(Database.php)` ✓ +- [x] #duration-controllers Handle duration in `ThesisCreateController` and `ThesisEditController` `(ThesisCreateController.php, ThesisEditController.php)` ✓ +- [x] #duration-form Add duration fieldset (value + unit dropdown) to form template `(templates/partials/form/form.php)` ✓ +- [x] #duration-display Show duration on public TFE detail page `(templates/public/tfe.php)` ✓ +- [x] #duration-view Include duration in v_theses_full and v_theses_public `(migrations/applied/040_duration_fields.sql, schema.sql, schema.sql.new)` ✓ +- [x] #duration-bootstrapWire Wire duration variables through FormBootstrap adminFormVariables `(FormBootstrap.php)` ✓ - [x] #cleanup-drafts Add periodic cleanup job for orphaned drafts (`Database.php`, `justfile`, `deploy/xamxam-cleanup.cron`, `scripts/cleanup-drafts.php`) ✓ - [x] #form-setup-helper Add `FormBootstrap` helper class to reduce bootstrap duplication across add/edit/partage `(admin/add.php)` `(admin/edit.php)` ✓ - [x] #two-phase-commit Add two-phase commit: INSERT thesis `status='draft'`, COMMIT, move files, UPDATE to `active` `(ThesisCreateController.php)` ✓ @@ -20,6 +31,7 @@ - [x] #aria-errormessage WCAG AA: field-level `aria-errormessage`, `aria-invalid`, `aria-describedby` on all form fields ✓ - [x] #nojs-upload-fix No-JS file uploads: `filepond_mode` default to `0 disabled`, server-side `$_FILES` fallback ✓ - [x] #autosave-partage Autosave text fields on partage form: session draft endpoint (`fragments/draft.php`), HTMX autosave on change/input, page-load hydration, "Brouillon enregistré" indicator, draft cleared on submit ✓ +- [x] #autosave-partage-wire Wire `formExtraAttrs`, `showAutosaveStatus`, draft hydration, `autosave-handler.js`, draft cleanup into partage form (`partage/index.php`, `partage/form-page.php`) ✓ - [x] #mobile-responsive Mobile-responsive form layout: `@media (max-width: 600px)` breakpoint, 44×44px touch targets ✓ - [x] #aria-fieldset-fix Remove invalid `required` attribute from `