private Versions readVersions(VFSLeaf leaf, VFSLeaf fVersions) { if (fVersions == null) { return new NotVersioned(); } try { VFSContainer fVersionContainer = fVersions.getParentContainer(); VersionsFileImpl versions = (VersionsFileImpl) XStreamHelper.readObject(mystream, fVersions); versions.setVersionFile(fVersions); versions.setCurrentVersion((Versionable) leaf); if (versions.getRevisionNr() == null || versions.getRevisionNr().length() == 0) { versions.setRevisionNr(getNextRevisionNr(versions)); } for (VFSRevision revision : versions.getRevisions()) { RevisionFileImpl revisionImpl = (RevisionFileImpl) revision; revisionImpl.setContainer(fVersionContainer); } return versions; } catch (Exception e) { log.warn("This file is not a versions XML file: " + fVersions, e); fVersions.delete(); VersionsFileImpl versions = new VersionsFileImpl(); versions.setCurrentVersion((Versionable) leaf); versions.setVersioned(isVersioned(leaf)); versions.setRevisionNr(getNextRevisionNr(versions)); log.warn("Deleted corrupt version XML file and created new version XML file: " + versions); // the old revisions can not be restored automatically. They are still on disk, you could // recover them // manually. This is not a perfect solution, but at least the user does not get an RS return versions; } }
/** * Clean up all revisions files, xml file * * @param leaf */ private void cleanUp(VFSLeaf leaf) { String relPath = getRelPath(leaf); if (relPath == null) return; // cannot handle File fVersion = new File(getRootVersionsFile(), relPath + ".xml"); File fParentVersion = fVersion.getParentFile(); if (!fParentVersion.exists()) return; // already deleted VFSLeaf versionLeaf = null; if (fVersion.exists()) { LocalFolderImpl localVersionContainer = new LocalFolderImpl(fParentVersion); versionLeaf = (VFSLeaf) localVersionContainer.resolve(fVersion.getName()); } if (versionLeaf == null) return; // already deleted Versions versions = readVersions(leaf, versionLeaf); for (VFSRevision versionToDelete : versions.getRevisions()) { RevisionFileImpl versionImpl = (RevisionFileImpl) versionToDelete; VFSLeaf fileToDelete = versionImpl.getFile(); if (fileToDelete != null) { fileToDelete.delete(); } } versionLeaf.delete(); }
@Override public boolean move(Versionable currentVersion, VFSContainer container) { VFSLeaf currentFile = (VFSLeaf) currentVersion; VFSLeaf fVersions = getCanonicalVersionXmlFile(currentFile, true); Versions versions = readVersions(currentFile, fVersions); VFSContainer versionContainer = getCanonicalVersionFolder(container, true); boolean allOk = VFSConstants.YES.equals(versionContainer.copyFrom(fVersions)); for (VFSRevision revision : versions.getRevisions()) { RevisionFileImpl revisionImpl = (RevisionFileImpl) revision; VFSLeaf revisionFile = revisionImpl.getFile(); if (revisionFile != null) { allOk &= VFSConstants.YES.equals(versionContainer.copyFrom(revisionFile)); } } allOk &= VFSConstants.YES.equals(fVersions.delete()); for (VFSRevision revision : versions.getRevisions()) { VFSLeaf revisionFile = ((RevisionFileImpl) revision).getFile(); if (revisionFile != null) { allOk &= VFSConstants.YES.equals(revisionFile.delete()); } } return allOk; }
private boolean isVersionsXmlFile(VFSLeaf fVersions) { if (fVersions == null || !fVersions.exists()) { return false; } InputStream in = fVersions.getInputStream(); if (in == null) { return false; } Scanner scanner = new Scanner(in); scanner.useDelimiter(TAG_PATTERN); boolean foundVersionsTag = false; while (scanner.hasNext()) { String tag = scanner.next(); if ("versions".equals(tag)) { foundVersionsTag = true; break; } } scanner.close(); IOUtils.closeQuietly(in); return foundVersionsTag; }
private boolean isSameFile(VFSLeaf currentFile, VersionsFileImpl versions) { boolean same = false; if (versions.getRevisions() != null && !versions.getRevisions().isEmpty()) { VFSRevision lastRevision = versions.getRevisions().get(versions.getRevisions().size() - 1); long lastSize = lastRevision.getSize(); long currentSize = currentFile.getSize(); if (currentSize == lastSize && currentSize > 0 && lastRevision instanceof RevisionFileImpl && currentFile instanceof LocalFileImpl) { RevisionFileImpl lastRev = ((RevisionFileImpl) lastRevision); LocalFileImpl current = (LocalFileImpl) currentFile; // can be the same file try { Checksum cm1 = FileUtils.checksum(((LocalFileImpl) lastRev.getFile()).getBasefile(), new Adler32()); Checksum cm2 = FileUtils.checksum(current.getBasefile(), new Adler32()); same = cm1.getValue() == cm2.getValue(); } catch (IOException e) { log.debug("Error calculating the checksum of files"); } } } return same; }
/** * Some temporary/lock files of specific editors need to be force deleted with all versions. Word * can reuse older names. * * @param leaf * @return */ private boolean isTemporaryFile(VFSLeaf leaf) { String name = leaf.getName(); // temporary files if (name.endsWith(".tmp")) { // Word 2010: ~WRD0002.tmp if (name.startsWith("~WRD") || name.startsWith("~WRL")) { return true; } // PowerPoint 2010: ppt5101.tmp if (name.startsWith("ppt")) { return true; } } // lock files of Word 2010, Excel 2010, PowerPoint 2010: if (name.startsWith("~$") && (name.endsWith(".docx") || name.endsWith(".xlsx") || name.endsWith(".pptx"))) { return true; } // OpenOffice locks: .~lock.Versions_21.odt# if (name.startsWith(".~lock.") && (name.endsWith(".odt#") /* Writer */ || name.endsWith(".ods#") /* Calc */ || name.endsWith(".odp#") /* Impress */ || name.endsWith("odf#") /* Math */ || name.endsWith(".odg#") /* Draw */)) { return true; } // OpenOffice database lock if (name.endsWith(".odb.lck")) { return true; } return false; }
@Override public boolean delete(OrphanVersion orphan) { VFSLeaf versionLeaf = orphan.getVersionsLeaf(); if (versionLeaf == null) return true; // already deleted Versions versions = orphan.getVersions(); for (VFSRevision versionToDelete : versions.getRevisions()) { RevisionFileImpl versionImpl = (RevisionFileImpl) versionToDelete; versionImpl.setContainer(orphan.getVersionsLeaf().getParentContainer()); VFSLeaf fileToDelete = versionImpl.getFile(); if (fileToDelete != null) { fileToDelete.delete(); } } versionLeaf.delete(); return true; }
private void pruneVersionHistory( VFSLeaf versionsLeaf, long maxHistoryLength, ProgressDelegate progress) { if (versionsLeaf.getName().endsWith(".xml") && isVersionsXmlFile(versionsLeaf)) { File originalFile = reversedOriginFile(versionsLeaf); if (originalFile.exists()) { VFSLeaf original = new LocalFileImpl(originalFile); if (progress != null) progress.setInfo(original.getName()); Versions versions = readVersions(original, versionsLeaf); List<VFSRevision> revisions = versions.getRevisions(); if (revisions.size() > maxHistoryLength) { List<VFSRevision> revisionsToDelete = revisions.subList(0, revisions.size() - (int) maxHistoryLength); deleteRevisions((Versionable) original, revisionsToDelete); } } } }
@Override public boolean rename(VFSItem item, String newname) { if (item instanceof VFSLeaf) { VFSLeaf currentFile = (VFSLeaf) item; VFSLeaf versionFile = getCanonicalVersionXmlFile(currentFile, true); // infinite loop if rename is own versions file return VFSConstants.YES.equals(versionFile.rename(newname + ".xml")); } else if (item instanceof VFSContainer) { VFSContainer container = (VFSContainer) item; VFSContainer versionContainer = getCanonicalVersionFolder(container, false); if (versionContainer == null) { return true; } return VFSConstants.YES.equals(versionContainer.rename(newname)); } return false; }
@Override public boolean deleteVersions(List<Versions> versions) { for (Versions versionToDelete : versions) { if (versionToDelete instanceof VersionsFileImpl) { VersionsFileImpl versionsImpl = (VersionsFileImpl) versionToDelete; VFSLeaf versionFile = versionsImpl.getVersionFile(); if (versionFile != null) { // robust against manual file system manipulation versionFile.deleteSilently(); } for (VFSRevision revisionToDelete : versionsImpl.getRevisions()) { RevisionFileImpl versionImpl = (RevisionFileImpl) revisionToDelete; VFSLeaf fileToDelete = versionImpl.getFile(); if (fileToDelete != null) { fileToDelete.deleteSilently(); } } } } return true; }
private Versions isOrphan(VFSLeaf potentialOrphan) { try { if (potentialOrphan.exists()) { VersionsFileImpl versions = (VersionsFileImpl) XStreamHelper.readObject(mystream, potentialOrphan); return versions; } return null; } catch (Exception e) { return null; } }
@Override public boolean isThumbnailPossible(VFSLeaf file) { String extension = FileUtils.getFileSuffix(file.getName()); if (StringHelper.containsNonWhitespace(extension)) { for (ThumbnailSPI thumbnailSPI : thumbnailSPIes) { if (thumbnailSPI.getExtensions().contains(extension)) { return true; } } } return false; }
@Override public FinalSize generateThumbnail( VFSLeaf file, VFSLeaf thumbnailFile, int maxWidth, int maxHeight) throws CannotGenerateThumbnailException { String extension = FileUtils.getFileSuffix(file.getName()).toLowerCase(); for (ThumbnailSPI thumbnailSPI : thumbnailSPIes) { if (thumbnailSPI.getExtensions().contains(extension)) { FinalSize finalSize = thumbnailSPI.generateThumbnail(file, thumbnailFile, maxWidth, maxHeight); if (finalSize != null) { return finalSize; } // else, try to find an other SPI which can thumbnailed this file } } return null; }
@Override public boolean deleteRevisions(Versionable currentVersion, List<VFSRevision> versionsToDelete) { VFSLeaf currentFile = (VFSLeaf) currentVersion; Versions versions = readVersions(currentFile, true); List<VFSRevision> allVersions = versions.getRevisions(); Map<String, VFSLeaf> filenamesToDelete = new HashMap<String, VFSLeaf>(allVersions.size()); for (VFSRevision versionToDelete : versionsToDelete) { RevisionFileImpl versionImpl = (RevisionFileImpl) versionToDelete; for (Iterator<VFSRevision> allVersionIt = allVersions.iterator(); allVersionIt.hasNext(); ) { RevisionFileImpl allVersionImpl = (RevisionFileImpl) allVersionIt.next(); if (allVersionImpl.getFilename() != null && allVersionImpl.getFilename().equals(versionImpl.getFilename())) { allVersionIt.remove(); break; } } VFSLeaf fileToDelete = versionImpl.getFile(); if (fileToDelete != null) { filenamesToDelete.put(fileToDelete.getName(), fileToDelete); } } List<RevisionFileImpl> missingFiles = new ArrayList<>(); for (VFSRevision survivingVersion : allVersions) { RevisionFileImpl survivingVersionImpl = (RevisionFileImpl) survivingVersion; VFSLeaf revFile = survivingVersionImpl.getFile(); if (revFile == null) { missingFiles.add(survivingVersionImpl); // file is missing } else if (filenamesToDelete.containsKey(revFile.getName())) { filenamesToDelete.remove(revFile.getName()); } } if (missingFiles.size() > 0) { allVersions.removeAll(missingFiles); } for (VFSLeaf fileToDelete : filenamesToDelete.values()) { fileToDelete.deleteSilently(); } VFSLeaf versionFile = getCanonicalVersionXmlFile(currentFile, true); XStreamHelper.writeObject(mystream, versionFile, versions); if (currentVersion.getVersions() instanceof VersionsFileImpl) { ((VersionsFileImpl) currentVersion.getVersions()).update(versions); } return true; }
private boolean copyRevision( VFSRevision revision, VFSLeaf fNewVersions, VersionsFileImpl targetVersions) { if (!(revision instanceof RevisionFileImpl)) { logWarn("Copy only copy persisted revisions", null); } RevisionFileImpl revisionImpl = (RevisionFileImpl) revision; String revUuid = revisionImpl.getUuid(); for (VFSRevision rev : targetVersions.getRevisions()) { if (rev instanceof RevisionFileImpl) { RevisionFileImpl fRev = (RevisionFileImpl) rev; if (StringHelper.containsNonWhitespace(fRev.getUuid()) && fRev.getUuid().equals(revUuid)) { return true; } } } String uuid = UUID.randomUUID().toString().replace("-", "") + "_" + revision.getName(); RevisionFileImpl newRevision = new RevisionFileImpl(); newRevision.setName(revision.getName()); newRevision.setFilename(uuid); newRevision.setRevisionNr(getNextRevisionNr(targetVersions)); newRevision.setComment(revision.getComment()); newRevision.setAuthor(revision.getAuthor()); newRevision.setLastModified(revision.getLastModified()); newRevision.setUuid(revUuid); // copy -> the files revision InputStream revisionIn = revision.getInputStream(); VFSLeaf target = fNewVersions.getParentContainer().createChildLeaf(uuid); if (VFSManager.copyContent(revisionIn, target)) { targetVersions.setComment(revision.getComment()); targetVersions.getRevisions().add(newRevision); targetVersions.setRevisionNr(getNextRevisionNr(targetVersions)); targetVersions.setAuthor(revision.getAuthor()); return true; } return false; }
/** * @see * org.olat.core.util.vfs.version.VersionsManager#addToRevisions(org.olat.core.util.vfs.version.Versionable, * org.olat.core.id.Identity, java.lang.String) */ @Override public boolean addToRevisions(Versionable currentVersion, Identity identity, String comment) { int maxNumOfVersions = versioningConfigurator.getMaxNumOfVersionsAllowed(); if (maxNumOfVersions == 0) { return true; // deactivated, return all ok } VFSLeaf currentFile = (VFSLeaf) currentVersion; VFSLeaf versionFile = getCanonicalVersionXmlFile(currentFile, true); if (versionFile == null) { return false; // cannot do something with the current file } VFSContainer versionContainer = versionFile.getParentContainer(); String name = currentFile.getName(); // read from the Versions v = readVersions(currentFile, versionFile); if (!(v instanceof VersionsFileImpl)) { log.error("Wrong implementation of Versions: " + v); return false; } VersionsFileImpl versions = (VersionsFileImpl) v; boolean sameFile = isSameFile(currentFile, versions); String uuid = sameFile ? getLastRevisionFilename(versions) : UUID.randomUUID().toString() + "_" + name; String versionNr = getNextRevisionNr(versions); String currentAuthor = versions.getAuthor(); long lastModifiedDate = 0; if (currentFile instanceof MetaTagged) { MetaInfo metaInfo = ((MetaTagged) currentFile).getMetaInfo(); if (metaInfo != null) { metaInfo.clearThumbnails(); if (currentAuthor == null) { currentAuthor = metaInfo.getAuthor(); } lastModifiedDate = metaInfo.getLastModified(); } } if (lastModifiedDate <= 0) { Calendar cal = Calendar.getInstance(); cal.setTime(new Date()); lastModifiedDate = cal.getTimeInMillis(); } RevisionFileImpl newRevision = new RevisionFileImpl(); newRevision.setUuid(UUID.randomUUID().toString()); newRevision.setName(name); newRevision.setFilename(uuid); newRevision.setRevisionNr(versionNr); newRevision.setComment(versions.getComment()); newRevision.setAuthor(currentAuthor); newRevision.setLastModified(lastModifiedDate); if (versions.getRevisions().isEmpty() && currentVersion instanceof MetaTagged) { MetaTagged metaTagged = (MetaTagged) currentVersion; versions.setCreator(metaTagged.getMetaInfo().getAuthor()); } if (sameFile || VFSManager.copyContent(currentFile, versionContainer.createChildLeaf(uuid))) { if (identity != null) { versions.setAuthor(identity.getName()); } if (maxNumOfVersions >= 0 && versions.getRevisions().size() >= maxNumOfVersions) { List<VFSRevision> revisions = versions.getRevisions(); int numOfVersionsToDelete = Math.min(revisions.size(), (revisions.size() - maxNumOfVersions) + 1); if (numOfVersionsToDelete > 0) { List<VFSRevision> versionsToDelete = revisions.subList(0, numOfVersionsToDelete); deleteRevisions(currentVersion, versionsToDelete); versions = (VersionsFileImpl) currentVersion.getVersions(); } } versions.setComment(comment); versions.getRevisions().add(newRevision); versions.setRevisionNr(getNextRevisionNr(versions)); XStreamHelper.writeObject(mystream, versionFile, versions); if (currentVersion.getVersions() instanceof VersionsFileImpl) { ((VersionsFileImpl) currentVersion.getVersions()).update(versions); } return true; } else { log.error("Cannot create a version of this file: " + currentVersion); } return false; }