From 8e06c427407d51be9ad111c13c55cfc5932f2529 Mon Sep 17 00:00:00 2001 From: pavelbannov Date: Fri, 28 Feb 2020 14:32:11 +0300 Subject: [PATCH] Files: added new interface --- .../Server/Controllers/FilesController.cs | 18 ++ .../Server/Core/Dao/Interfaces/IFileDao.cs | 262 ++++++++++++++++++ .../Server/Core/Dao/TeamlabDao/FileDao.cs | 226 ++++++++++++++- .../ASC.Files/Server/Core/Entries/File.cs | 10 + .../Server/Core/FileStorageService.cs | 24 ++ 5 files changed, 539 insertions(+), 1 deletion(-) diff --git a/products/ASC.Files/Server/Controllers/FilesController.cs b/products/ASC.Files/Server/Controllers/FilesController.cs index ddf13b45ca..7e3926e73c 100644 --- a/products/ASC.Files/Server/Controllers/FilesController.cs +++ b/products/ASC.Files/Server/Controllers/FilesController.cs @@ -279,7 +279,12 @@ namespace ASC.Api.Documents public FolderContentWrapper GetFolder(string folderId, Guid userIdOrGroupId, FilterType filterType) { return ToFolderContentWrapper(folderId, userIdOrGroupId, filterType).NotFoundIfNull(); + } + [Read("{folderId:int}", order: int.MaxValue)] + public FolderContentWrapper GetFolder(int folderId, Guid userIdOrGroupId, FilterType filterType) + { + return ToFolderContentWrapper(folderId, userIdOrGroupId, filterType).NotFoundIfNull(); } /// @@ -822,6 +827,19 @@ namespace ASC.Api.Documents return FileWrapperHelper.Get(file); } + /// + /// Returns a detailed information about the file with the ID specified in the request + /// + /// File information + /// Files + /// File info + [Read("file/{fileId:int}")] + public FileWrapper GetFileInfo(int fileId, int version = -1) + { + var file = FileStorageService.GetFile(fileId, version).NotFoundIfNull("File not found"); + return FileWrapperHelper.Get(file); + } + /// /// Updates the information of the selected file with the parameters specified in the request /// diff --git a/products/ASC.Files/Server/Core/Dao/Interfaces/IFileDao.cs b/products/ASC.Files/Server/Core/Dao/Interfaces/IFileDao.cs index c2f3df23d1..64e4a69f8d 100644 --- a/products/ASC.Files/Server/Core/Dao/Interfaces/IFileDao.cs +++ b/products/ASC.Files/Server/Core/Dao/Interfaces/IFileDao.cs @@ -32,6 +32,268 @@ using ASC.Web.Files.Services.DocumentService; namespace ASC.Files.Core { + public interface IFileDao + { + /// + /// Clear the application cache for the specific file + /// + void InvalidateCache(T fileId); + + /// + /// Receive file + /// + /// file id + /// + File GetFile(T fileId); + + /// + /// Receive file + /// + /// file id + /// file version + /// + File GetFile(T fileId, int fileVersion); + + /// + /// Receive file + /// + /// folder id + /// file name + /// + /// file + /// + File GetFile(T parentId, string title); + + /// + /// Receive last file without forcesave + /// + /// file id + /// + /// + File GetFileStable(T fileId, int fileVersion = -1); + + /// + /// Returns all versions of the file + /// + /// + /// + List> GetFileHistory(T fileId); + + /// + /// Gets the file (s) by ID (s) + /// + /// id file + /// + List> GetFiles(T[] fileIds); + + /// + /// Gets the file (s) by ID (s) for share + /// + /// id file + /// + /// + /// + /// + /// + /// + List> GetFilesForShare(T[] fileIds, FilterType filterType, bool subjectGroup, Guid subjectID, string searchText, bool searchInContent); + + /// + /// + /// + /// + /// + List GetFiles(T parentId); + + /// + /// Get files in folder + /// + /// folder id + /// + /// filterType type + /// + /// + /// + /// + /// + /// list of files + /// + /// Return only the latest versions of files of a folder + /// + List> GetFiles(T parentId, OrderBy orderBy, FilterType filterType, bool subjectGroup, Guid subjectID, string searchText, bool searchInContent, bool withSubfolders = false); + + /// + /// Get stream of file + /// + /// + /// Stream + Stream GetFileStream(File file); + + /// + /// Get stream of file + /// + /// + /// + /// Stream + Stream GetFileStream(File file, long offset); + + /// + /// Get presigned uri + /// + /// + /// + /// Stream uri + Uri GetPreSignedUri(File file, TimeSpan expires); + + /// + /// Check is supported PreSignedUri + /// + /// + /// Stream uri + bool IsSupportedPreSignedUri(File file); + + /// + /// Saves / updates the version of the file + /// and save stream of file + /// + /// + /// + /// + /// + /// Updates the file if: + /// - The file comes with the given id + /// - The file with that name in the folder / container exists + /// + /// Save in all other cases + /// + File SaveFile(File file, Stream fileStream); + + /// + /// + /// + /// + /// + /// + File ReplaceFileVersion(File file, Stream fileStream); + + /// + /// Deletes a file including all previous versions + /// + /// file id + void DeleteFile(T fileId); + + /// + /// Checks whether or not file + /// + /// file name + /// folder id + /// Returns true if the file exists, otherwise false + bool IsExist(string title, T folderId); + + /// + /// Moves a file or set of files in a folder + /// + /// file id + /// The ID of the destination folder + object MoveFile(T fileId, T toFolderId); + + /// + /// Copy the files in a folder + /// + /// file id + /// The ID of the destination folder + File CopyFile(T fileId, T toFolderId); + + /// + /// Rename file + /// + /// + /// new name + object FileRename(File file, string newTitle); + + /// + /// Update comment file + /// + /// file id + /// file version + /// new comment + string UpdateComment(T fileId, int fileVersion, string comment); + + /// + /// Complete file version + /// + /// file id + /// file version + void CompleteVersion(T fileId, int fileVersion); + + /// + /// Continue file version + /// + /// file id + /// file version + void ContinueVersion(T fileId, int fileVersion); + + /// + /// Check the need to use the trash before removing + /// + /// + /// + bool UseTrashForRemove(File file); + + #region chunking + + ChunkedUploadSession CreateUploadSession(File file, long contentLength); + + #endregion + + #region Only in TMFileDao + + /// + /// Set created by + /// + /// + /// + void ReassignFiles(T[] fileIds, Guid newOwnerId); + + /// + /// Search files in SharedWithMe & Projects + /// + /// + /// + /// + /// + /// + /// + /// + List> GetFiles(T[] parentIds, FilterType filterType, bool subjectGroup, Guid subjectID, string searchText, bool searchInContent); + + /// + /// Search the list of files containing text + /// Only in TMFileDao + /// + /// search text + /// + /// list of files + IEnumerable> Search(string text, bool bunch = false); + + /// + /// Checks whether file exists on storage + /// + /// file + /// + bool IsExistOnStorage(File file); + + void SaveEditHistory(File file, string changes, Stream differenceStream); + + List GetEditHistory(DocumentServiceHelper documentServiceHelper, T fileId, int fileVersion = 0); + + Stream GetDifferenceStream(File file); + + bool ContainChanges(T fileId, int fileVersion); + + #endregion + } + /// /// Interface encapsulates access toFolderId files /// diff --git a/products/ASC.Files/Server/Core/Dao/TeamlabDao/FileDao.cs b/products/ASC.Files/Server/Core/Dao/TeamlabDao/FileDao.cs index 4c25de1fe4..b1a70ee87e 100644 --- a/products/ASC.Files/Server/Core/Dao/TeamlabDao/FileDao.cs +++ b/products/ASC.Files/Server/Core/Dao/TeamlabDao/FileDao.cs @@ -51,7 +51,7 @@ using Microsoft.Extensions.DependencyInjection; namespace ASC.Files.Core.Data { - public class FileDao : AbstractDao, IFileDao + public class FileDao : AbstractDao, IFileDao, IFileDao { private static readonly object syncRoot = new object(); public FactoryIndexer FactoryIndexer { get; } @@ -106,12 +106,22 @@ namespace ASC.Files.Core.Data { } + public void InvalidateCache(int fileId) + { + } + public File GetFile(object fileId) { var query = GetFileQuery(r => r.Id.ToString() == fileId.ToString() && r.CurrentVersion); return FromQueryWithShared(query).SingleOrDefault(); } + public File GetFile(int fileId) + { + var query = GetFileQuery(r => r.Id == fileId && r.CurrentVersion); + return FromQueryWithSharedInt(query).SingleOrDefault(); + } + public File GetFile(object fileId, int fileVersion) { var query = GetFileQuery(r => r.Id.ToString() == fileId.ToString() && r.Version == fileVersion); @@ -1200,6 +1210,31 @@ namespace ASC.Files.Core.Data .ToList(); } + protected List> FromQueryWithSharedInt(IQueryable dbFiles) + { + return dbFiles + .Select(r => new DbFileQuery + { + file = r, + root = + FilesDbContext.Folders + .Join(FilesDbContext.Tree, a => a.Id, b => b.ParentId, (folder, tree) => new { folder, tree }) + .Where(x => x.folder.TenantId == r.TenantId) + .Where(x => x.tree.FolderId == r.FolderId) + .OrderByDescending(r => r.tree.Level) + .Select(r => r.folder) + .FirstOrDefault(), + shared = + FilesDbContext.Security + .Where(x => x.EntryType == FileEntryType.File) + .Where(x => x.EntryId == r.Id.ToString()) + .Any() + }) + .ToList() + .Select(ToFileInt) + .ToList(); + } + protected List FromQuery(IQueryable dbFiles) { return dbFiles @@ -1243,6 +1278,193 @@ namespace ASC.Files.Core.Data file.Forcesave = r.file.Forcesave; return file; } + + public File ToFileInt(DbFileQuery r) + { + var file = ServiceProvider.GetService>(); + file.ID = r.file.Id; + file.Title = r.file.Title; + file.FolderID = r.file.FolderId; + file.CreateOn = TenantUtil.DateTimeFromUtc(r.file.CreateOn); + file.CreateBy = r.file.CreateBy; + file.Version = r.file.Version; + file.VersionGroup = r.file.VersionGroup; + file.ContentLength = r.file.ContentLength; + file.ModifiedOn = TenantUtil.DateTimeFromUtc(r.file.ModifiedOn); + file.ModifiedBy = r.file.ModifiedBy; + file.RootFolderType = r.root?.FolderType ?? default; + file.RootFolderCreator = r.root?.CreateBy ?? default; + file.RootFolderId = r.root?.Id ?? default; + file.Shared = r.shared; + file.ConvertedType = r.file.ConvertedType; + file.Comment = r.file.Comment; + file.Encrypted = r.file.Encrypted; + file.Forcesave = r.file.Forcesave; + return file; + } + + + ///test:test + + public File GetFile(int fileId, int fileVersion) + { + throw new NotImplementedException(); + } + + public File GetFile(int parentId, string title) + { + throw new NotImplementedException(); + } + + public File GetFileStable(int fileId, int fileVersion = -1) + { + throw new NotImplementedException(); + } + + public List> GetFileHistory(int fileId) + { + throw new NotImplementedException(); + } + + public List> GetFiles(int[] fileIds) + { + throw new NotImplementedException(); + } + + public List> GetFilesForShare(int[] fileIds, FilterType filterType, bool subjectGroup, Guid subjectID, string searchText, bool searchInContent) + { + throw new NotImplementedException(); + } + + public List GetFiles(int parentId) + { + throw new NotImplementedException(); + } + + public List> GetFiles(int parentId, OrderBy orderBy, FilterType filterType, bool subjectGroup, Guid subjectID, string searchText, bool searchInContent, bool withSubfolders = false) + { + throw new NotImplementedException(); + } + + public Stream GetFileStream(File file) + { + throw new NotImplementedException(); + } + + public Stream GetFileStream(File file, long offset) + { + throw new NotImplementedException(); + } + + public Uri GetPreSignedUri(File file, TimeSpan expires) + { + throw new NotImplementedException(); + } + + public bool IsSupportedPreSignedUri(File file) + { + throw new NotImplementedException(); + } + + public File SaveFile(File file, Stream fileStream) + { + throw new NotImplementedException(); + } + + public File ReplaceFileVersion(File file, Stream fileStream) + { + throw new NotImplementedException(); + } + + public void DeleteFile(int fileId) + { + throw new NotImplementedException(); + } + + public bool IsExist(string title, int folderId) + { + throw new NotImplementedException(); + } + + public object MoveFile(int fileId, int toFolderId) + { + throw new NotImplementedException(); + } + + public File CopyFile(int fileId, int toFolderId) + { + throw new NotImplementedException(); + } + + public object FileRename(File file, string newTitle) + { + throw new NotImplementedException(); + } + + public string UpdateComment(int fileId, int fileVersion, string comment) + { + throw new NotImplementedException(); + } + + public void CompleteVersion(int fileId, int fileVersion) + { + throw new NotImplementedException(); + } + + public void ContinueVersion(int fileId, int fileVersion) + { + throw new NotImplementedException(); + } + + public bool UseTrashForRemove(File file) + { + throw new NotImplementedException(); + } + + public ChunkedUploadSession CreateUploadSession(File file, long contentLength) + { + throw new NotImplementedException(); + } + + public void ReassignFiles(int[] fileIds, Guid newOwnerId) + { + throw new NotImplementedException(); + } + + public List> GetFiles(int[] parentIds, FilterType filterType, bool subjectGroup, Guid subjectID, string searchText, bool searchInContent) + { + throw new NotImplementedException(); + } + + IEnumerable> IFileDao.Search(string text, bool bunch) + { + throw new NotImplementedException(); + } + + public bool IsExistOnStorage(File file) + { + throw new NotImplementedException(); + } + + public void SaveEditHistory(File file, string changes, Stream differenceStream) + { + throw new NotImplementedException(); + } + + public List GetEditHistory(DocumentServiceHelper documentServiceHelper, int fileId, int fileVersion = 0) + { + throw new NotImplementedException(); + } + + public Stream GetDifferenceStream(File file) + { + throw new NotImplementedException(); + } + + public bool ContainChanges(int fileId, int fileVersion) + { + throw new NotImplementedException(); + } } public class DbFileQuery @@ -1257,7 +1479,9 @@ namespace ASC.Files.Core.Data public static DIHelper AddFileDaoService(this DIHelper services) { services.TryAddScoped(); + services.TryAddScoped, FileDao>(); services.TryAddTransient(); + services.TryAddTransient>(); return services .AddFilesDbContextService() diff --git a/products/ASC.Files/Server/Core/Entries/File.cs b/products/ASC.Files/Server/Core/Entries/File.cs index 7d82694d14..6bcc24afd6 100644 --- a/products/ASC.Files/Server/Core/Entries/File.cs +++ b/products/ASC.Files/Server/Core/Entries/File.cs @@ -54,6 +54,16 @@ namespace ASC.Files.Core [EnumMember] IsEditingAlone = 0x10 } + public class File : File + { + [DataMember(Name = "id")] + public new T ID { get; set; } + + public File(Global global, FilesLinkUtility filesLinkUtility, FileUtility fileUtility, FileConverter fileConverter) : base(global, filesLinkUtility, fileUtility, fileConverter) + { + } + } + [Serializable] [DataContract(Name = "file", Namespace = "")] [DebuggerDisplay("{Title} ({ID} v{Version})")] diff --git a/products/ASC.Files/Server/Core/FileStorageService.cs b/products/ASC.Files/Server/Core/FileStorageService.cs index b42f992c1d..7ba1562192 100644 --- a/products/ASC.Files/Server/Core/FileStorageService.cs +++ b/products/ASC.Files/Server/Core/FileStorageService.cs @@ -445,6 +445,30 @@ namespace ASC.Web.Files.Services.WCFService return file; } + public File GetFile(int fileId, int version) + { + var fileDao = GetFileDao() as IFileDao; + fileDao.InvalidateCache(fileId); + + var file = version > 0 + ? fileDao.GetFile(fileId, version) + : fileDao.GetFile(fileId); + ErrorIf(file == null, FilesCommonResource.ErrorMassage_FileNotFound); + ErrorIf(!FileSecurity.CanRead(file), FilesCommonResource.ErrorMassage_SecurityException_ReadFile); + + EntryManager.SetFileStatus(file); + + if (file.RootFolderType == FolderType.USER + && !Equals(file.RootFolderCreator, AuthContext.CurrentAccount.ID)) + { + var folderDao = GetFolderDao(); + if (!FileSecurity.CanRead(folderDao.GetFolder(file.FolderID))) + file.FolderIdDisplay = GlobalFolderHelper.FolderShare; + } + + return file; + } + public ItemList GetSiblingsFile(string fileId, string parentId, FilterType filter, bool subjectGroup, string subjectID, string search, bool searchInContent, bool withSubfolders, OrderBy orderBy) { var subjectId = string.IsNullOrEmpty(subjectID) ? Guid.Empty : new Guid(subjectID);