diff --git a/src/main/java/org/olat/basesecurity/BaseSecurityManager.java b/src/main/java/org/olat/basesecurity/BaseSecurityManager.java index 0aca12db49039a08ea2808687fa96249b44c5672..eb60a5c02ac2a003b5985c3f229d740c2bee7dbe 100644 --- a/src/main/java/org/olat/basesecurity/BaseSecurityManager.java +++ b/src/main/java/org/olat/basesecurity/BaseSecurityManager.java @@ -1063,6 +1063,7 @@ public class BaseSecurityManager implements BaseSecurity, UserDataDeletable { public Identity saveIdentityStatus(Identity identity, Integer status, Identity doer) { IdentityImpl reloadedIdentity = loadForUpdate(identity); if(reloadedIdentity != null) { + Integer previousStatus = reloadedIdentity.getStatus(); reloadedIdentity.setStatus(status); if(status.equals(Identity.STATUS_DELETED)) { if(doer != null && reloadedIdentity.getDeletedBy() == null) { @@ -1071,8 +1072,13 @@ public class BaseSecurityManager implements BaseSecurity, UserDataDeletable { reloadedIdentity.setDeletedDate(new Date()); } else if(status.equals(Identity.STATUS_INACTIVE)) { reloadedIdentity.setInactivationDate(new Date()); - } else if(status.equals(Identity.STATUS_ACTIV) - || status.equals(Identity.STATUS_PERMANENT) + reloadedIdentity.setReactivationDate(null); + } else if(status.equals(Identity.STATUS_ACTIV)) { + reloadedIdentity.setInactivationDate(null); + if(Identity.STATUS_INACTIVE.equals(previousStatus)) { + reloadedIdentity.setReactivationDate(new Date()); + } + } else if(status.equals(Identity.STATUS_PERMANENT) || status.equals(Identity.STATUS_PENDING) || status.equals(Identity.STATUS_LOGIN_DENIED)) { reloadedIdentity.setInactivationDate(null); diff --git a/src/main/java/org/olat/basesecurity/IdentityImpl.hbm.xml b/src/main/java/org/olat/basesecurity/IdentityImpl.hbm.xml index d2fa5c975c6077a7fbd84619b24235b07ed943fd..7d37aad994bb338ae3675b72a66e82fb4a83481f 100644 --- a/src/main/java/org/olat/basesecurity/IdentityImpl.hbm.xml +++ b/src/main/java/org/olat/basesecurity/IdentityImpl.hbm.xml @@ -28,6 +28,7 @@ <property name="inactivationDate" column="inactivationdate" type="timestamp" /> <property name="inactivationEmailDate" column="inactivationemaildate" type="timestamp" /> + <property name="reactivationDate" column="reactivationdate" type="timestamp" /> <one-to-one name="user" property-ref="identity" class="org.olat.user.UserImpl" cascade="persist"/> </class> diff --git a/src/main/java/org/olat/basesecurity/IdentityImpl.java b/src/main/java/org/olat/basesecurity/IdentityImpl.java index 3fa7df457bdf35f2025b3be417f813e8e7ea620e..6b4a09d1412f4d889b5a035f6edee2ee18a9b906 100644 --- a/src/main/java/org/olat/basesecurity/IdentityImpl.java +++ b/src/main/java/org/olat/basesecurity/IdentityImpl.java @@ -58,6 +58,7 @@ public class IdentityImpl implements Identity, IdentityRef, CreateInfo, Persista private Date inactivationDate; private Date inactivationEmailDate; + private Date reactivationDate; private Date deletedDate; private String deletedBy; @@ -219,6 +220,14 @@ public class IdentityImpl implements Identity, IdentityRef, CreateInfo, Persista this.inactivationEmailDate = inactivationEmailDate; } + public Date getReactivationDate() { + return reactivationDate; + } + + public void setReactivationDate(Date reactivationDate) { + this.reactivationDate = reactivationDate; + } + @Override public int hashCode() { int hash = 7; diff --git a/src/main/java/org/olat/core/commons/services/vfs/manager/VFSMetadataDAO.java b/src/main/java/org/olat/core/commons/services/vfs/manager/VFSMetadataDAO.java index 983cde4f85525fcf499920230846680595c7558c..5aa43e9e5b324b703c5acb5b4e7191d7e70a6616 100644 --- a/src/main/java/org/olat/core/commons/services/vfs/manager/VFSMetadataDAO.java +++ b/src/main/java/org/olat/core/commons/services/vfs/manager/VFSMetadataDAO.java @@ -219,6 +219,13 @@ public class VFSMetadataDAO { .getResultList(); } + public List<VFSMetadata> getMetadatasOnly(VFSMetadataRef parentMetadata) { + return dbInstance.getCurrentEntityManager() + .createNamedQuery("metadataOnlyByParent", VFSMetadata.class) + .setParameter("parentKey", parentMetadata.getKey()) + .getResultList(); + } + /** * This is an exact match to find the direct children of a specific * directory. diff --git a/src/main/java/org/olat/core/commons/services/vfs/manager/VFSRepositoryServiceImpl.java b/src/main/java/org/olat/core/commons/services/vfs/manager/VFSRepositoryServiceImpl.java index 7795e2f5ac205928b310a9a1b5d68cd90bd61fb3..2b6ad501264411ffab3e09ca6a9218d1e09f415c 100644 --- a/src/main/java/org/olat/core/commons/services/vfs/manager/VFSRepositoryServiceImpl.java +++ b/src/main/java/org/olat/core/commons/services/vfs/manager/VFSRepositoryServiceImpl.java @@ -468,46 +468,69 @@ public class VFSRepositoryServiceImpl implements VFSRepositoryService, GenericEv public int deleteMetadata(VFSMetadata data) { if(data == null) return 0; // nothing to do - List<VFSThumbnailMetadata> thumbnails = thumbnailDao.loadByMetadata(data); - for(VFSThumbnailMetadata thumbnail:thumbnails) { - VFSItem item = VFSManager.olatRootLeaf("/" + data.getRelativePath(), thumbnail.getFilename()); - if(item != null && item.exists()) { - item.deleteSilently(); - } - thumbnailDao.removeThumbnail(thumbnail); + int deleted = 0; + List<VFSMetadata> children = metadataDao.getMetadatasOnly(data); + for(VFSMetadata child:children) { + deleted += deleteMetadata(child); } - List<VFSRevision> revisions = getRevisions(data); + deleteThumbnailsOfMetadata(data); + deleteRevisionsOfMetadata(data); + + data = dbInstance.getCurrentEntityManager().getReference(VFSMetadataImpl.class, data.getKey()); + metadataDao.removeMetadata(data); + dbInstance.commit(); + + deleted++; + return deleted; + } + + private void deleteRevisionsOfMetadata(VFSMetadata data) { + List<VFSRevision> revisions = revisionDao.getRevisionsOnly(data); for(VFSRevision revision:revisions) { File revFile = getRevisionFile(revision); if(revFile != null && revFile.exists()) { try { Files.delete(revFile.toPath()); } catch (IOException e) { - log.error("Cannot delete thumbnail: {}", revFile, e); + log.error("Cannot delete revision: {}", revFile, e); } } revisionDao.deleteRevision(revision); } - - dbInstance.commit(); + if(!revisions.isEmpty()) { + dbInstance.commit(); + } + } - int count = 0; - int deleted = 0; - List<VFSMetadata> children = getChildren(data); - for(VFSMetadata child:children) { - deleted += deleteMetadata(child); - if(count++ % 10 == 0) { - dbInstance.commitAndCloseSession(); + private void deleteThumbnailsOfMetadata(VFSMetadata data) { + boolean hasThumbnailMetadata = false; + List<VFSThumbnailMetadata> thumbnails = thumbnailDao.loadByMetadata(data); + for(VFSThumbnailMetadata thumbnail:thumbnails) { + VFSItem item = VFSManager.olatRootLeaf("/" + data.getRelativePath(), thumbnail.getFilename()); + if(item != null && item.exists()) { + if(item instanceof LocalFileImpl) { + File thumbnailFile = ((LocalFileImpl)item).getBasefile(); + try { + Files.delete(thumbnailFile.toPath()); + } catch (IOException e) { + log.error("Cannot delete thumbnail: {}", thumbnailFile, e); + } + + VFSMetadata thumbnailMetadata = metadataDao.getMetadata(data.getRelativePath(), thumbnail.getFilename(), false); + if(thumbnailMetadata != null) { + metadataDao.removeMetadata(thumbnailMetadata); + hasThumbnailMetadata = true; + } + } else { + item.deleteSilently(); + } } + thumbnailDao.removeThumbnail(thumbnail); + } + if(!thumbnails.isEmpty() || hasThumbnailMetadata) { + dbInstance.commit(); } - - data = dbInstance.getCurrentEntityManager().getReference(VFSMetadataImpl.class, data.getKey()); - metadataDao.removeMetadata(data); - dbInstance.commit(); - - deleted++; - return deleted; } @Override diff --git a/src/main/java/org/olat/core/commons/services/vfs/manager/VFSRevisionDAO.java b/src/main/java/org/olat/core/commons/services/vfs/manager/VFSRevisionDAO.java index bd5242efc79f8dd3ac081d6b08685ecba8fe8242..26021bc0c3c4871f4cadae10e77d46a10552b6a8 100644 --- a/src/main/java/org/olat/core/commons/services/vfs/manager/VFSRevisionDAO.java +++ b/src/main/java/org/olat/core/commons/services/vfs/manager/VFSRevisionDAO.java @@ -115,6 +115,20 @@ public class VFSRevisionDAO { .setParameter("metadataKey", metadata.getKey()) .getResultList(); } + + /** + * @param metadata The metadata + * @return A list of revisions, without any fetch data, not ordered + */ + public List<VFSRevision> getRevisionsOnly(VFSMetadataRef metadata) { + if(metadata == null) return new ArrayList<>(); + + String sb = "select rev from vfsrevision rev where rev.metadata.key=:metadataKey"; + return dbInstance.getCurrentEntityManager() + .createQuery(sb, VFSRevision.class) + .setParameter("metadataKey", metadata.getKey()) + .getResultList(); + } public List<VFSRevision> getRevisions(List<VFSMetadataRef> metadatas) { if(metadatas == null || metadatas.isEmpty()) return new ArrayList<>(); diff --git a/src/main/java/org/olat/core/commons/services/vfs/model/VFSMetadataImpl.java b/src/main/java/org/olat/core/commons/services/vfs/model/VFSMetadataImpl.java index fda80a17555a73857d01a243cc66b159acb169dd..ada572638bd8bac75fb3ca28a7ab6b8863372a99 100644 --- a/src/main/java/org/olat/core/commons/services/vfs/model/VFSMetadataImpl.java +++ b/src/main/java/org/olat/core/commons/services/vfs/model/VFSMetadataImpl.java @@ -29,6 +29,7 @@ import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; +import javax.persistence.NamedQuery; import javax.persistence.Table; import javax.persistence.Temporal; import javax.persistence.TemporalType; @@ -50,6 +51,7 @@ import org.olat.core.util.StringHelper; */ @Entity(name="filemetadata") @Table(name="o_vfs_metadata") +@NamedQuery(name="metadataOnlyByParent", query="select metadata from filemetadata metadata where metadata.parent.key=:parentKey") public class VFSMetadataImpl implements Persistable, VFSMetadata { private static final long serialVersionUID = 1360000029480576628L; diff --git a/src/main/java/org/olat/core/util/vfs/LocalFileImpl.java b/src/main/java/org/olat/core/util/vfs/LocalFileImpl.java index 865ee312a03fbf6e993acf4afabff926b97072ac..1a475c7087f96d00c4b20a72667c3c1c25dec5b8 100644 --- a/src/main/java/org/olat/core/util/vfs/LocalFileImpl.java +++ b/src/main/java/org/olat/core/util/vfs/LocalFileImpl.java @@ -162,7 +162,7 @@ public class LocalFileImpl extends LocalImpl implements VFSLeaf { public VFSStatus deleteSilently() { if(canMeta() == VFSConstants.YES) { CoreSpringFactory.getImpl(VFSRepositoryService.class).deleteMetadata(getMetaInfo()); - CoreSpringFactory.getImpl(DB.class).commitAndCloseSession(); + CoreSpringFactory.getImpl(DB.class).commit(); } else { // some lock can create a metadata object with canMeta() == NO diff --git a/src/main/java/org/olat/course/CourseFactory.java b/src/main/java/org/olat/course/CourseFactory.java index aa7acbbdc6270a095db1694fe75ccfdafb99adf3..f694a0f87612e1c3b3dd57b30f06c891fa282719 100644 --- a/src/main/java/org/olat/course/CourseFactory.java +++ b/src/main/java/org/olat/course/CourseFactory.java @@ -72,6 +72,7 @@ import org.olat.core.id.context.ContextEntry; import org.olat.core.logging.AssertException; import org.olat.core.logging.OLATRuntimeException; import org.olat.core.logging.Tracing; +import org.olat.core.util.CodeHelper; import org.olat.core.util.ExportUtil; import org.olat.core.util.FileUtils; import org.olat.core.util.Formatter; @@ -439,7 +440,9 @@ public class CourseFactory { // delete course directory VFSContainer fCourseBasePath = getCourseBaseContainer(res.getResourceableId()); + long start4 = System.nanoTime(); VFSStatus status = fCourseBasePath.deleteSilently(); + CodeHelper.printMilliSecondTime(start4, "Delete all files"); boolean deletionSuccessful = (status == VFSConstants.YES || status == VFSConstants.SUCCESS); log.info("deleteCourse: finished deletion. res="+res+", deletion successful: "+deletionSuccessful+", duration: "+(System.currentTimeMillis()-start)+" ms."); } diff --git a/src/main/java/org/olat/course/nodes/gta/ui/AbstractAssignmentEditController.java b/src/main/java/org/olat/course/nodes/gta/ui/AbstractAssignmentEditController.java index 774ff26be25692f8b71d24448a7e3a108ebe445e..a4a5ba69dd0c748a452b5eb8179bdb897f69725e 100644 --- a/src/main/java/org/olat/course/nodes/gta/ui/AbstractAssignmentEditController.java +++ b/src/main/java/org/olat/course/nodes/gta/ui/AbstractAssignmentEditController.java @@ -355,6 +355,7 @@ abstract class AbstractAssignmentEditController extends FormBasicController impl String text = translate("warning.tasks.in.process.delete.text"); confirmDeleteCtrl = activateOkCancelDialog(ureq, title, text, confirmDeleteCtrl); confirmDeleteCtrl.setUserObject(row); + confirmDeleteCtrl.setCssClass("o_warning"); } private void doDelete(UserRequest ureq, TaskDefinition taskDef) { diff --git a/src/main/java/org/olat/course/nodes/gta/ui/DirectoryController.java b/src/main/java/org/olat/course/nodes/gta/ui/DirectoryController.java index 8eb55a18b0a6e34c23397d0145e679aa3a97540f..26ed8046db4d5f8a7505cdcc58a9bf20db965efc 100644 --- a/src/main/java/org/olat/course/nodes/gta/ui/DirectoryController.java +++ b/src/main/java/org/olat/course/nodes/gta/ui/DirectoryController.java @@ -140,6 +140,7 @@ public class DirectoryController extends BasicController implements Activateable linkNames.add(new DocumentInfos(link.getComponentName(), uploadedBy, lastModified)); } mainVC.contextPut("linkNames", linkNames); + bulkReviewLink.setVisible(!linkNames.isEmpty()); putInitialPanel(mainVC); } diff --git a/src/main/java/org/olat/course/nodes/gta/ui/GTAAssignedTaskController.java b/src/main/java/org/olat/course/nodes/gta/ui/GTAAssignedTaskController.java index 0f380edece5fe4f649a8f6ad990988e5d4e82aa1..f54867a3b9569f4c2f4a61dfdee09dd2e82ef366 100644 --- a/src/main/java/org/olat/course/nodes/gta/ui/GTAAssignedTaskController.java +++ b/src/main/java/org/olat/course/nodes/gta/ui/GTAAssignedTaskController.java @@ -111,6 +111,7 @@ public class GTAAssignedTaskController extends BasicController { downloadButton.setTitle(taskInfos); downloadButton.setIconLeftCSS("o_icon o_icon_download"); downloadButton.setTarget("_blank"); + downloadButton.setVisible(taskFile.exists()); downloadLink = LinkFactory.createCustomLink("download.link", "download.link", null, Link.NONTRANSLATED, mainVC, this); if(taskDef != null) { diff --git a/src/main/java/org/olat/course/nodes/gta/ui/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/course/nodes/gta/ui/_i18n/LocalStrings_de.properties index 0fd9a0a92ab5edb6c3bac7c7bf0108a138ef1146..aab3658eaa86506050eef816a6e02976c2517164 100644 --- a/src/main/java/org/olat/course/nodes/gta/ui/_i18n/LocalStrings_de.properties +++ b/src/main/java/org/olat/course/nodes/gta/ui/_i18n/LocalStrings_de.properties @@ -358,7 +358,7 @@ warning.no.task.choosed=Wie es scheint, war es Ihnen aufgrund von \u00C4nderunge warning.no.task.choosed.coach=Wie es scheint, war es der Assignee aufgrund von \u00C4nderungen am Kurselement nicht m\u00F6glich, eine Aufgabe f\u00FCr dieses Aufgabenelement auszuw\u00E4hlen. warning.reopen=Wenn Sie das Abgabedatum verl\u00E4ngern, wird diese Aufgabe neu er\u00F6ffnet. warning.submit.documents.edited=Sie k\u00F6nnen die Aufgabe nicht abgeben, da das Dokument "{1}" noch von "{0}" bearbeitet wird. -warning.tasks.in.process.delete.text=Wollen Sie wirklich dieser Aufgabe l\u00F6schen? Es gibt bereits Benutzer die den Aufgabenprozess gestartet haben. Das kann f\u00FCr diese Benutzer zu Problemen f\u00FChren. +warning.tasks.in.process.delete.text=Wollen Sie wirklich dieser Aufgabe l\u00F6schen? <strong>Es gibt bereits Benutzer die den Aufgabenprozess gestartet haben. Das kann f\u00FCr diese Benutzer zu Problemen f\u00FChren.</strong> warning.tasks.in.process.delete.title=$\:warning.tasks.in.process.title warning.tasks.in.process.text=Es gibt bereits Benutzer die den Aufgabenprozess gestartet haben. \u00C4nderungen an der Workflow-Konfiguration kann f\u00FCr diese Benutzer zu Problemen f\u00FChren. Informationen dazu finden Sie im <a href\="{0}" target\="_blank"><i class\='o_icon o_icon_help'> </i> Handbuch</a>. warning.tasks.in.process.title=Aufgabenprozess bereits gestartet diff --git a/src/main/java/org/olat/course/nodes/gta/ui/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/course/nodes/gta/ui/_i18n/LocalStrings_en.properties index 6c00ad7e2f8fd30fe6b37b5df9b0d601b87033b0..5cda911e2e3783244a6b9fc756a37f79d80a163c 100644 --- a/src/main/java/org/olat/course/nodes/gta/ui/_i18n/LocalStrings_en.properties +++ b/src/main/java/org/olat/course/nodes/gta/ui/_i18n/LocalStrings_en.properties @@ -358,7 +358,7 @@ warning.no.task.choosed=It seems that due to a change in the course element conf warning.no.task.choosed.coach=It seems that due to a change in the course element configuration, the assignee wasn't able to choose a task for this task element. warning.reopen=If you decide to extend the submission deadline, this will automatically reopen the task submission. warning.submit.documents.edited=You cannot submit the task. The document "{1}" is currently being edited by "{0}". -warning.tasks.in.process.delete.text=Do you really wan to delete this task? There are already users who have started the task process. It could result in problems for these users. +warning.tasks.in.process.delete.text=Do you really wan to delete this task? <strong>There are already users who have started the task process. It could result in problems for these users.</strong> warning.tasks.in.process.delete.title=$\:warning.tasks.in.process.title warning.tasks.in.process.text=There are already users who have started the task process. Changing the workflow configuration could result in problems for these users. Please refer to the <a href\="{0}" target\="_blank"><i class\='o_icon o_icon_help'> </i> manual</a> for more information. warning.tasks.in.process.title=Task already started diff --git a/src/main/java/org/olat/course/nodes/gta/ui/_i18n/LocalStrings_fr.properties b/src/main/java/org/olat/course/nodes/gta/ui/_i18n/LocalStrings_fr.properties index 402c47b46f0838139847829744d74d9ec1ca9196..7195050770675d09c95f65b998bfe06cb5744241 100644 --- a/src/main/java/org/olat/course/nodes/gta/ui/_i18n/LocalStrings_fr.properties +++ b/src/main/java/org/olat/course/nodes/gta/ui/_i18n/LocalStrings_fr.properties @@ -358,7 +358,7 @@ warning.no.task.choosed=Il semble qu'il n'a pas \u00E9t\u00E9 possible de vous c warning.no.task.choosed.coach=Il semble qu'en raison d'un changement dans la configuration de l'\u00E9l\u00E9ment de cours, un utilisateur n'a pas pu se voir assigner un devoir. warning.reopen=Si vous d\u00E9cidez de prolonger la date de d\u00E9p\u00F4t, cela rouvrira automatiquement la possibilit\u00E9 de remettre le devoir. warning.submit.documents.edited=Vous ne pouvez pas soumettre le devoir. Le document "{1}" est actuellement \u00E9dit\u00E9 par "{0}". -warning.tasks.in.process.delete.text=Voulez-vous vraiment supprimer ce devoir? Des utilisateurs l'ont d\u00E9j\u00E0 commenc\u00E9. Cela entra\u00EEnera des probl\u00E8mes pour ces utilisateurs. +warning.tasks.in.process.delete.text=Voulez-vous vraiment supprimer ce devoir? <strong>Des utilisateurs l'ont d\u00E9j\u00E0 commenc\u00E9. Cela entra\u00EEnera des probl\u00E8mes pour ces utilisateurs.</strong> warning.tasks.in.process.delete.title=$\:warning.tasks.in.process.title warning.tasks.in.process.text=Il ya d\u00E9j\u00E0 des utilisateurs qui ont commenc\u00E9 les devoirs. Des modifications de la configuration du processus pourra entra\u00EEner des probl\u00E8mes pour ces utilisateurs. Pour plus d'informations, vous pouvez vous r\u00E9f\u00E9rer au <a href\="{0}" target\="_blank"><i class\='o_icon o_icon_help'> </i> manuel d'utilisation</a>. warning.tasks.in.process.title=Devoirs d\u00E9j\u00E0 commenc\u00E9s diff --git a/src/main/java/org/olat/course/nodes/gta/ui/_i18n/LocalStrings_it.properties b/src/main/java/org/olat/course/nodes/gta/ui/_i18n/LocalStrings_it.properties index 3c3762fe9ab9313e8b5c18b0ebfb0b6be21f9c5c..afaa44a3bad219daaf2c5d7fda144a97c09204f6 100644 --- a/src/main/java/org/olat/course/nodes/gta/ui/_i18n/LocalStrings_it.properties +++ b/src/main/java/org/olat/course/nodes/gta/ui/_i18n/LocalStrings_it.properties @@ -358,7 +358,7 @@ warning.no.task.choosed=Sembra che, a causa di un cambiamento nella configurazio warning.no.task.choosed.coach=Sembra che, a causa di un cambiamento nella configurazione, all'assegnatario non sia stato possibile selezionare un compito da svolgere per questo elemento di corso. warning.reopen=Se si decide di prolungare il termine di presentazione, questo riaprir\u00E0 automaticamente la presentazione dell'attivit\u00E0. warning.submit.documents.edited=Non puoi consegnare il compito. Il documento "{1}" \u00E8 attualmente in modifica da parte di "{0}". -warning.tasks.in.process.delete.text=Vuoi veramente eliminare questo compito? Ci sono utenti che hanno gi\u00E0 iniziato a svolgerlo. Potrebbero verificarsi problemi per questi utenti. +warning.tasks.in.process.delete.text=Vuoi veramente eliminare questo compito? <strong>Ci sono utenti che hanno gi\u00E0 iniziato a svolgerlo. Potrebbero verificarsi problemi per questi utenti.</strong> warning.tasks.in.process.delete.title=$\:warning.tasks.in.process.title warning.tasks.in.process.text=Ci sono gi\u00E0 alcuni utenti che hanno iniziato lo svolgimento del compito. Modificare la configurazione del flusso di lavoro potrebbe creare un problema a questi utenti. Prego, consulta il <a href\="{0}" target\="_blank"><i class\='o_icon o_icon_help'> </i> manuale</a> per maggiori informazioni. warning.tasks.in.process.title=Compiti gi\u00E0 iniziati diff --git a/src/main/java/org/olat/course/nodes/gta/ui/_i18n/LocalStrings_pt_BR.properties b/src/main/java/org/olat/course/nodes/gta/ui/_i18n/LocalStrings_pt_BR.properties index 5d92109246ec5a1739d83d4bbc6d45b91d58fe2d..05a1d0c0e9efc507a2c3206583621ddd23e84890 100644 --- a/src/main/java/org/olat/course/nodes/gta/ui/_i18n/LocalStrings_pt_BR.properties +++ b/src/main/java/org/olat/course/nodes/gta/ui/_i18n/LocalStrings_pt_BR.properties @@ -358,7 +358,7 @@ warning.no.task.choosed=Parece que, devido a uma altera\u00E7\u00E3o na configur warning.no.task.choosed.coach=Parece que, devido a uma altera\u00E7\u00E3o na configura\u00E7\u00E3o do elemento de curso, o designado n\u00E3o foi capaz de escolher uma tarefa para este elemento. warning.reopen=Se voc\u00EA decidir estender o prazo de envio, isso reabrir\u00E1 automaticamente o envio da tarefa. warning.submit.documents.edited=Voc\u00EA n\u00E3o pode enviar a tarefa. O documento "{1}" est\u00E1 sendo editado por "{0}". -warning.tasks.in.process.delete.text=Voc\u00EA realmente deseja apagar esta tarefa? J\u00E1 existem usu\u00E1rios que iniciaram o processo da tarefa. Isso pode resultar em problemas para esses usu\u00E1rios. +warning.tasks.in.process.delete.text=Voc\u00EA realmente deseja apagar esta tarefa? <strong>J\u00E1 existem usu\u00E1rios que iniciaram o processo da tarefa. Isso pode resultar em problemas para esses usu\u00E1rios.</strong> warning.tasks.in.process.delete.title=$\:warning.tasks.in.process.title warning.tasks.in.process.text=J\u00E1 existem usu\u00E1rios que iniciaram a tarefa. Alterar a configura\u00E7\u00E3o do Workflow pode resultar em problemas para esses usu\u00E1rios. warning.tasks.in.process.title=Tarefas j\u00E1 iniciadas diff --git a/src/main/java/org/olat/upgrade/_spring/databaseUpgradeContext.xml b/src/main/java/org/olat/upgrade/_spring/databaseUpgradeContext.xml index c2b6b0777b2433ab6006198a014a3c8c64b4b6b8..c73ac324d1b759592c9b66021992c2da731d5681 100644 --- a/src/main/java/org/olat/upgrade/_spring/databaseUpgradeContext.xml +++ b/src/main/java/org/olat/upgrade/_spring/databaseUpgradeContext.xml @@ -288,6 +288,10 @@ <constructor-arg index="0" value="OLAT_15.2.2" /> <property name="alterDbStatements" value="alter_15_2_x_to_15_2_2.sql" /> </bean> + <bean id="database_upgrade_15_2_3" class="org.olat.upgrade.DatabaseUpgrade"> + <constructor-arg index="0" value="OLAT_15.2.3" /> + <property name="alterDbStatements" value="alter_15_2_x_to_15_2_3.sql" /> + </bean> <bean id="database_upgrade_15_3_0" class="org.olat.upgrade.DatabaseUpgrade"> <constructor-arg index="0" value="OLAT_15.3.0" /> <property name="alterDbStatements" value="alter_15_2_x_to_15_3_0.sql" /> diff --git a/src/main/java/org/olat/user/manager/lifecycle/UserLifecycleManagerImpl.java b/src/main/java/org/olat/user/manager/lifecycle/UserLifecycleManagerImpl.java index 5f723d9980bf65475a69bedb18ded9ca5a57d368..a980694995ca9d34c35cf86d43e6187de61ce8b0 100644 --- a/src/main/java/org/olat/user/manager/lifecycle/UserLifecycleManagerImpl.java +++ b/src/main/java/org/olat/user/manager/lifecycle/UserLifecycleManagerImpl.java @@ -96,22 +96,23 @@ public class UserLifecycleManagerImpl implements UserLifecycleManager { private RepositoryDeletionModule repositoryDeletionModule; - public List<Identity> getReadyToInactivateIdentities(Date loginDate) { + public List<Identity> getReadyToInactivateIdentities(Date loginDate, Date reactivationDateLimit) { StringBuilder sb = new StringBuilder(512); sb.append("select ident from ").append(IdentityImpl.class.getName()).append(" as ident") .append(" inner join fetch ident.user as user") .append(" where ident.status in (:statusList) and ((ident.lastLogin = null and ident.creationDate < :lastLogin) or ident.lastLogin < :lastLogin)") - .append(" and ident.inactivationEmailDate is null"); + .append(" and ident.inactivationEmailDate is null and (ident.reactivationDate is null or ident.reactivationDate<:reactivationDateLimit)"); List<Integer> statusList = Arrays.asList(Identity.STATUS_ACTIV, Identity.STATUS_PENDING, Identity.STATUS_LOGIN_DENIED); return dbInstance.getCurrentEntityManager() .createQuery(sb.toString(), Identity.class) .setParameter("statusList", statusList) .setParameter("lastLogin", loginDate, TemporalType.TIMESTAMP) + .setParameter("reactivationDateLimit", reactivationDateLimit) .getResultList(); } - public List<Identity> getIdentitiesToInactivate(Date loginDate, Date emailBeforeDate) { + public List<Identity> getIdentitiesToInactivate(Date loginDate, Date emailBeforeDate, Date reactivationDateLimit) { StringBuilder sb = new StringBuilder(512); sb.append("select ident from ").append(IdentityImpl.class.getName()).append(" as ident") .append(" inner join fetch ident.user as user") @@ -119,12 +120,14 @@ public class UserLifecycleManagerImpl implements UserLifecycleManager { if(emailBeforeDate != null) { sb.append(" and (ident.inactivationEmailDate<:emailDate or ident.lastLogin is null)"); } + sb.append(" and (ident.reactivationDate is null or ident.reactivationDate<:reactivationDateLimit)"); List<Integer> statusList = Arrays.asList(Identity.STATUS_ACTIV, Identity.STATUS_PENDING, Identity.STATUS_LOGIN_DENIED); TypedQuery<Identity> query = dbInstance.getCurrentEntityManager() .createQuery(sb.toString(), Identity.class) .setParameter("statusList", statusList) - .setParameter("lastLogin", loginDate, TemporalType.TIMESTAMP); + .setParameter("lastLogin", loginDate, TemporalType.TIMESTAMP) + .setParameter("reactivationDateLimit", reactivationDateLimit); if(emailBeforeDate != null) { query.setParameter("emailDate", emailBeforeDate); } @@ -182,11 +185,12 @@ public class UserLifecycleManagerImpl implements UserLifecycleManager { public void inactivateIdentities(Set<Identity> vetoed) { int numOfDaysBeforeDeactivation = userModule.getNumberOfInactiveDayBeforeDeactivation(); int numOfDaysBeforeEmail = userModule.getNumberOfDayBeforeDeactivationMail(); + Date reactivationDatebefore = getDate(30); boolean sendMailBeforeDeactivation = userModule.isMailBeforeDeactivation() && numOfDaysBeforeEmail > 0; if(sendMailBeforeDeactivation) { int days = numOfDaysBeforeDeactivation - numOfDaysBeforeEmail; Date lastLoginDate = getDate(days); - List<Identity> identities = getReadyToInactivateIdentities(lastLoginDate); + List<Identity> identities = getReadyToInactivateIdentities(lastLoginDate, reactivationDatebefore); if(!identities.isEmpty()) { for(Identity identity:identities) { if(identity.getLastLogin() != null) { @@ -202,9 +206,9 @@ public class UserLifecycleManagerImpl implements UserLifecycleManager { List<Identity> identities; if(sendMailBeforeDeactivation) { Date emailBeforeDate = getDate(numOfDaysBeforeEmail); - identities = getIdentitiesToInactivate(lastLoginDate, emailBeforeDate); + identities = getIdentitiesToInactivate(lastLoginDate, emailBeforeDate, reactivationDatebefore); } else { - identities = getIdentitiesToInactivate(lastLoginDate, null); + identities = getIdentitiesToInactivate(lastLoginDate, null, reactivationDatebefore); } for(Identity identity:identities) { diff --git a/src/main/resources/database/mysql/alter_15_2_x_to_15_2_3.sql b/src/main/resources/database/mysql/alter_15_2_x_to_15_2_3.sql new file mode 100644 index 0000000000000000000000000000000000000000..97e38dfb99d317d2e872bced2de6941763ab5430 --- /dev/null +++ b/src/main/resources/database/mysql/alter_15_2_x_to_15_2_3.sql @@ -0,0 +1,3 @@ +-- Reactivate user +alter table o_bs_identity add column reactivationdate datetime; + diff --git a/src/main/resources/database/mysql/setupDatabase.sql b/src/main/resources/database/mysql/setupDatabase.sql index 0143e107bad5c63c6922196ac611ff0f958f2e18..cef3c804f3f6bde98e318e1d7b49d2a729a31a0f 100644 --- a/src/main/resources/database/mysql/setupDatabase.sql +++ b/src/main/resources/database/mysql/setupDatabase.sql @@ -177,6 +177,7 @@ create table if not exists o_bs_identity ( deletedby varchar(128), inactivationdate datetime, inactivationemaildate datetime, + reactivationdate datetime, deletionemaildate datetime, primary key (id) ); diff --git a/src/main/resources/database/oracle/alter_15_2_x_to_15_2_3.sql b/src/main/resources/database/oracle/alter_15_2_x_to_15_2_3.sql new file mode 100644 index 0000000000000000000000000000000000000000..b67085a313c59f25f4d891e28d71313af3e90eb2 --- /dev/null +++ b/src/main/resources/database/oracle/alter_15_2_x_to_15_2_3.sql @@ -0,0 +1,2 @@ +-- Reactivate user +alter table o_bs_identity add reactivationdate date; diff --git a/src/main/resources/database/oracle/setupDatabase.sql b/src/main/resources/database/oracle/setupDatabase.sql index 7ddf6dfa9f4640961ffb39aab172df94b6d8830c..22b60948ba1c6281f5bbd27e93958fedf9e69a6c 100644 --- a/src/main/resources/database/oracle/setupDatabase.sql +++ b/src/main/resources/database/oracle/setupDatabase.sql @@ -193,6 +193,7 @@ CREATE TABLE o_bs_identity ( deletedby varchar(128), inactivationdate date, inactivationemaildate date, + reactivationdate date, deletionemaildate date, CONSTRAINT u_o_bs_identity UNIQUE (name), PRIMARY KEY (id) diff --git a/src/main/resources/database/postgresql/alter_15_2_x_to_15_2_3.sql b/src/main/resources/database/postgresql/alter_15_2_x_to_15_2_3.sql new file mode 100644 index 0000000000000000000000000000000000000000..940b638f23114f351402405745ef71b069037dca --- /dev/null +++ b/src/main/resources/database/postgresql/alter_15_2_x_to_15_2_3.sql @@ -0,0 +1,2 @@ +-- Reactivate user +alter table o_bs_identity add column reactivationdate timestamp; diff --git a/src/main/resources/database/postgresql/setupDatabase.sql b/src/main/resources/database/postgresql/setupDatabase.sql index 9d2df9787ff2f995b89e5d8a287b60448f12f175..e694b2fdb783d901ba144da1d38774ea239c632d 100644 --- a/src/main/resources/database/postgresql/setupDatabase.sql +++ b/src/main/resources/database/postgresql/setupDatabase.sql @@ -175,6 +175,7 @@ create table o_bs_identity ( deletedby varchar(128), inactivationdate timestamp, inactivationemaildate timestamp, + reactivationdate timestamp, deletionemaildate timestamp, primary key (id) ); diff --git a/src/test/java/org/olat/core/commons/services/vfs/manager/IMG_1491.jpg b/src/test/java/org/olat/core/commons/services/vfs/manager/IMG_1491.jpg new file mode 100644 index 0000000000000000000000000000000000000000..bb3e25cf30125bac42957fbafcf22cd5a5d986be Binary files /dev/null and b/src/test/java/org/olat/core/commons/services/vfs/manager/IMG_1491.jpg differ diff --git a/src/test/java/org/olat/core/commons/services/vfs/manager/VFSRepositoryServiceTest.java b/src/test/java/org/olat/core/commons/services/vfs/manager/VFSRepositoryServiceTest.java index 19a20486b83e1b06be1fe687d4e71c21a1358849..c7360a387bce81f5bba6d1e5a1a4ad80f23451a8 100644 --- a/src/test/java/org/olat/core/commons/services/vfs/manager/VFSRepositoryServiceTest.java +++ b/src/test/java/org/olat/core/commons/services/vfs/manager/VFSRepositoryServiceTest.java @@ -22,8 +22,10 @@ package org.olat.core.commons.services.vfs.manager; import static org.assertj.core.api.Assertions.assertThat; import java.io.ByteArrayInputStream; +import java.io.File; import java.io.InputStream; import java.io.OutputStream; +import java.util.List; import java.util.UUID; import org.apache.commons.io.IOUtils; @@ -39,6 +41,9 @@ import org.olat.core.commons.services.license.manager.LicenseCleaner; import org.olat.core.commons.services.vfs.VFSMetadata; import org.olat.core.commons.services.vfs.VFSRepositoryService; import org.olat.core.logging.Tracing; +import org.olat.core.util.FileUtils; +import org.olat.core.util.vfs.LocalFileImpl; +import org.olat.core.util.vfs.LocalFolderImpl; import org.olat.core.util.vfs.VFSConstants; import org.olat.core.util.vfs.VFSContainer; import org.olat.core.util.vfs.VFSLeaf; @@ -47,6 +52,7 @@ import org.olat.test.OlatTestCase; import org.springframework.beans.factory.annotation.Autowired; /** + * Versioning is test with @see org.olat.core.commons.services.vfs.manager.VFSVersioningTest * * Initial date: 12 mars 2019<br> * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com @@ -72,7 +78,7 @@ public class VFSRepositoryServiceTest extends OlatTestCase { } @Test - public void getMetadataFor_file() { + public void getMetadataForVFSLeaf() { VFSLeaf leaf = createFile(); // create metadata @@ -87,6 +93,83 @@ public class VFSRepositoryServiceTest extends OlatTestCase { Assert.assertFalse(metadata.isDirectory()); } + @Test + public void getMetadataForFile() { + VFSLeaf leaf = createFile(); + File file = ((LocalFileImpl)leaf).getBasefile(); + + // create metadata + VFSMetadata metadata = vfsRepositoryService.getMetadataFor(file); + dbInstance.commitAndCloseSession(); + Assert.assertNotNull(metadata); + Assert.assertNotNull(metadata.getKey()); + Assert.assertNotNull(metadata.getCreationDate()); + Assert.assertNotNull(metadata.getLastModified()); + Assert.assertNotNull(metadata.getFileLastModified()); + Assert.assertEquals(leaf.getName(), metadata.getFilename()); + Assert.assertFalse(metadata.isDirectory()); + } + + @Test + public void deleteMetadata() { + VFSLeaf leaf = createImage(); + VFSMetadata metadata = vfsRepositoryService.getMetadataFor(leaf); + Assert.assertNotNull(metadata); + + VFSLeaf thumbnail1 = vfsRepositoryService.getThumbnail(leaf, 200, 200, true); + Assert.assertNotNull(thumbnail1); + Assert.assertTrue(thumbnail1.getSize() > 32); + VFSLeaf thumbnail2 = vfsRepositoryService.getThumbnail(leaf, 180, 180, false); + Assert.assertNotNull(thumbnail2); + Assert.assertTrue(thumbnail2.getSize() > 32); + + vfsRepositoryService.deleteMetadata(metadata); + dbInstance.commitAndCloseSession(); + } + + @Test + public void deleteMetadataFolder() { + VFSContainer testContainer = VFSManager.olatRootContainer(VFS_TEST_DIR, null); + VFSContainer container = createContainerRecursive(testContainer, 0, 2, 5, 5); + + VFSMetadata metadata = vfsRepositoryService.getMetadataFor(container); + dbInstance.commitAndCloseSession(); + + String containerPath = metadata.getRelativePath() + "/" + metadata.getFilename(); + List<VFSMetadata> children = vfsRepositoryService.getChildren(containerPath); + Assert.assertNotNull(children); + Assert.assertEquals(10, children.size()); + + container.deleteSilently(); + dbInstance.commitAndCloseSession(); + + List<VFSMetadata> afterChildren = vfsRepositoryService.getChildren(containerPath); + Assert.assertNotNull(afterChildren); + Assert.assertEquals(0, afterChildren.size()); + } + + @Test + public void deleteDirsAndFiles() { + VFSContainer testContainer = VFSManager.olatRootContainer(VFS_TEST_DIR, null); + VFSContainer container = createContainerRecursive(testContainer, 0, 2, 3, 3); + + VFSMetadata metadata = vfsRepositoryService.getMetadataFor(container); + dbInstance.commitAndCloseSession(); + + String containerPath = metadata.getRelativePath() + "/" + metadata.getFilename(); + List<VFSMetadata> children = vfsRepositoryService.getChildren(containerPath); + Assert.assertNotNull(children); + Assert.assertEquals(6, children.size()); + + File dir = ((LocalFolderImpl)container).getBasefile(); + FileUtils.deleteDirsAndFiles(dir, true, true); + dbInstance.commitAndCloseSession(); + + List<VFSMetadata> afterChildren = vfsRepositoryService.getChildren(containerPath); + Assert.assertNotNull(afterChildren); + Assert.assertEquals(0, afterChildren.size()); + } + @Test public void shouldLoadExistingLicenseType() { String typeName = "name"; @@ -140,7 +223,7 @@ public class VFSRepositoryServiceTest extends OlatTestCase { VFSContainer testContainer = VFSManager.olatRootContainer(VFS_TEST_DIR, null); VFSLeaf leaf = testContainer.createChildLeaf(filename); Assert.assertEquals(VFSConstants.YES, leaf.canMeta()); - copyTestTxt(leaf); + copyTestTxt(leaf, "test.txt"); VFSMetadata metaInfo = leaf.getMetaInfo(); metaInfo.setComment("A little comment"); @@ -153,7 +236,7 @@ public class VFSRepositoryServiceTest extends OlatTestCase { String secondFilename = UUID.randomUUID() + ".txt"; VFSLeaf secondLeaf = testContainer.createChildLeaf(secondFilename); - copyTestTxt(secondLeaf); + copyTestTxt(secondLeaf, "test.txt"); VFSMetadata secondMetaInfo = leaf.getMetaInfo(); String comment = null; @@ -170,13 +253,49 @@ public class VFSRepositoryServiceTest extends OlatTestCase { String filename = UUID.randomUUID() + ".txt"; VFSContainer testContainer = VFSManager.olatRootContainer(VFS_TEST_DIR, null); VFSLeaf firstLeaf = testContainer.createChildLeaf(filename); - copyTestTxt(firstLeaf); + copyTestTxt(firstLeaf, "test.txt"); return firstLeaf; } - private int copyTestTxt(VFSLeaf file) { + private VFSLeaf createImage() { + String filename = UUID.randomUUID() + ".jpg"; + VFSContainer testContainer = VFSManager.olatRootContainer(VFS_TEST_DIR, null); + VFSLeaf firstLeaf = testContainer.createChildLeaf(filename); + copyTestTxt(firstLeaf, "IMG_1491.jpg"); + return firstLeaf; + } + + private VFSContainer createContainerRecursive(VFSContainer parent, int depth, int maxDepth, int numOfFiles, int numOfContainers) { + if(depth > maxDepth) { + return null; + } + + String filename = UUID.randomUUID().toString(); + VFSContainer container = parent.createChildContainer(filename); + + for(int i=0; i<numOfFiles; i++) { + String imageName = "IMG_" + depth + "_" + i + ".jpg"; + VFSLeaf image = container.createChildLeaf(imageName); + copyTestTxt(image, "IMG_1491.jpg"); + + for(int j=1; j<5; j++) { + VFSLeaf thumbnail = vfsRepositoryService.getThumbnail(image, j * 20, j * 20, true); + Assert.assertNotNull(thumbnail); + Assert.assertTrue(thumbnail.getSize() > 32); + } + dbInstance.commitAndCloseSession(); + } + + for(int i=0; i<numOfContainers; i++) { + createContainerRecursive(container, depth + 1, maxDepth, numOfFiles, numOfContainers); + } + dbInstance.commitAndCloseSession(); + return container; + } + + private int copyTestTxt(VFSLeaf file, String sourceFilename) { try(OutputStream out = file.getOutputStream(false); - InputStream in = VFSRepositoryServiceTest.class.getResourceAsStream("test.txt")) { + InputStream in = VFSRepositoryServiceTest.class.getResourceAsStream(sourceFilename)) { return IOUtils.copy(in, out); } catch(Exception e) { log.error("", e); diff --git a/src/test/java/org/olat/user/manager/lifecycle/UserLifecycleManagerTest.java b/src/test/java/org/olat/user/manager/lifecycle/UserLifecycleManagerTest.java index a69965e61cc0e5349929702d19a772282f9aaa73..86eb8c95892af0ed69ccf7336302e271dfcfa9eb 100644 --- a/src/test/java/org/olat/user/manager/lifecycle/UserLifecycleManagerTest.java +++ b/src/test/java/org/olat/user/manager/lifecycle/UserLifecycleManagerTest.java @@ -104,7 +104,8 @@ public class UserLifecycleManagerTest extends OlatTestCase { dbInstance.commitAndCloseSession(); Date beforeDate = DateUtils.addDays(new Date(), -900); - List<Identity> identitiesToInactivate = lifecycleManager.getReadyToInactivateIdentities(beforeDate); + Date reactivationDateLimite = DateUtils.addDays(new Date(), -30); + List<Identity> identitiesToInactivate = lifecycleManager.getReadyToInactivateIdentities(beforeDate, reactivationDateLimite); Assert.assertNotNull(identitiesToInactivate); Assert.assertTrue(identitiesToInactivate.contains(id1)); Assert.assertFalse(identitiesToInactivate.contains(id2)); @@ -133,7 +134,8 @@ public class UserLifecycleManagerTest extends OlatTestCase { dbInstance.commitAndCloseSession(); Date beforeDate = DateUtils.addDays(new Date(), -900); - List<Identity> identitiesToInactivate = lifecycleManager.getIdentitiesToInactivate(beforeDate, null); + Date reactivationDatebefore = DateUtils.addDays(new Date(), -30); + List<Identity> identitiesToInactivate = lifecycleManager.getIdentitiesToInactivate(beforeDate, null, reactivationDatebefore); Assert.assertNotNull(identitiesToInactivate); Assert.assertTrue(identitiesToInactivate.contains(id1)); Assert.assertFalse(identitiesToInactivate.contains(id2)); @@ -231,6 +233,30 @@ public class UserLifecycleManagerTest extends OlatTestCase { getSmtpServer().reset(); } + @Test + public void reactivateIdentity() { + Assert.assertTrue(userModule.isUserAutomaticDeactivation()); + userModule.setMailBeforeDeactivation(true); + userModule.setNumberOfInactiveDayBeforeDeactivation(720); + userModule.setNumberOfDayBeforeDeactivationMail(30); + + Identity id1 = JunitTestHelper.createAndPersistIdentityAsRndUser("lifecycle-1"); + identityDao.setIdentityLastLogin(id1, DateUtils.addDays(new Date(), -910)); + id1 = securityManager.saveIdentityStatus(id1, Identity.STATUS_INACTIVE, id1); + id1 = securityManager.saveIdentityStatus(id1, Identity.STATUS_ACTIV, id1); + dbInstance.commitAndCloseSession(); + + Identity reloadedId1 = securityManager.loadIdentityByKey(id1.getKey()); + Assert.assertNotNull(((IdentityImpl)reloadedId1).getReactivationDate()); + + Set<Identity> vetoed = new HashSet<>(); + lifecycleManager.inactivateIdentities(vetoed); + dbInstance.commitAndCloseSession(); + + reloadedId1 = securityManager.loadIdentityByKey(id1.getKey()); + Assert.assertEquals(Identity.STATUS_ACTIV, reloadedId1.getStatus()); + } + @Test public void inactivateBIdentities() { Assert.assertTrue(userModule.isUserAutomaticDeactivation());