Merge pull request #1116 from ONLYOFFICE/bugfix/room-logo

Bugfix/room logo
This commit is contained in:
Alexey Safronov 2022-12-12 18:50:54 +03:00 committed by GitHub
commit 4629d949e2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 461 additions and 76 deletions

View File

@ -27,6 +27,7 @@ const CreateRoomEvent = ({
withPaging,
addFile,
setCreateRoomDialogVisible,
getRoomLogo,
}) => {
const { t } = useTranslation(["CreateEditRoomDialog", "Common", "Files"]);
const [fetchedTags, setFetchedTags] = useState([]);
@ -59,6 +60,8 @@ const CreateRoomEvent = ({
? await createRoomInThirdpary(storageFolderId, createRoomData)
: await createRoom(createRoomData);
room.isLogoLoading = true;
// delete thirdparty account if not needed
if (!isThirdparty && storageFolderId)
await deleteThirdParty(thirdpartyAccount.providerId);
@ -71,7 +74,7 @@ const CreateRoomEvent = ({
room = await addTagsToRoom(room.id, addTagsData);
// calculate and upload logo to room
if (roomParams.icon.uploadedFile)
if (roomParams.icon.uploadedFile) {
await uploadRoomLogo(uploadLogoData).then((response) => {
const url = URL.createObjectURL(roomParams.icon.uploadedFile);
const img = new Image();
@ -82,13 +85,31 @@ const CreateRoomEvent = ({
...calculateRoomLogoParams(img, x, y, zoom),
});
!withPaging && addFile(room, true);
if (!withPaging) {
const newLogo = await getRoomLogo(room.logo);
room.logoHandlers = room.logo;
room.logo = newLogo;
room.isLogoLoading = false;
addFile(room, true);
}
URL.revokeObjectURL(img.src);
};
img.src = url;
});
else !withPaging && addFile(room, true);
} else {
if (!withPaging) {
const newLogo = await getRoomLogo(room.logo);
room.logoHandlers = room.logo;
room.logo = newLogo;
room.isLogoLoading = false;
addFile(room, true);
}
}
} catch (err) {
toastr.error(err);
console.log(err);
@ -146,6 +167,7 @@ export default inject(
uploadRoomLogo,
addLogoToRoom,
addFile,
getRoomLogo,
} = filesStore;
const { createTag, fetchTags } = tagsStore;
@ -171,6 +193,7 @@ export default inject(
calculateRoomLogoParams,
uploadRoomLogo,
addLogoToRoom,
getRoomLogo,
connectDialogVisible,
currrentFolderId,

View File

@ -29,6 +29,7 @@ const EditRoomEvent = ({
setCreateRoomDialogVisible,
withPaging,
getRoomLogo,
}) => {
const { t } = useTranslation(["CreateEditRoomDialog", "Common", "Files"]);
@ -77,6 +78,8 @@ const EditRoomEvent = ({
let room = await editRoom(item.id, editRoomParams);
room.isLogoLoading = true;
for (let i = 0; i < newTags.length; i++) await createTag(newTags[i]);
room = await addTagsToRoom(room.id, tags);
room = await removeTagsFromRoom(room.id, removedTags);
@ -89,6 +92,7 @@ const EditRoomEvent = ({
...room,
logo: { big: item.logo.small },
});
await uploadRoomLogo(uploadLogoData).then((response) => {
const url = URL.createObjectURL(roomParams.icon.uploadedFile);
const img = new Image();
@ -99,13 +103,31 @@ const EditRoomEvent = ({
...calculateRoomLogoParams(img, x, y, zoom),
});
!withPaging && setFolder(room);
if (!withPaging) {
const newLogo = await getRoomLogo(room.logo);
room.logoHandlers = room.logo;
room.logo = newLogo;
room.isLogoLoading = false;
setFolder(room);
}
URL.revokeObjectURL(img.src);
};
img.src = url;
});
} else !withPaging && setFolder(room);
} else {
if (!withPaging) {
const newLogo = await getRoomLogo(room.logo);
room.logoHandlers = room.logo;
room.logo = newLogo;
room.isLogoLoading = false;
setFolder(room);
}
}
} catch (err) {
console.log(err);
} finally {
@ -113,6 +135,7 @@ const EditRoomEvent = ({
await updateCurrentFolder(null, currentFolderId);
}
setIsLoading(false);
onClose();
}
};
@ -176,6 +199,7 @@ export default inject(
setFolder,
addLogoToRoom,
removeLogoFromRoom,
getRoomLogo,
} = filesStore;
const { createTag, fetchTags } = tagsStore;
@ -189,6 +213,7 @@ export default inject(
editRoom,
addTagsToRoom,
removeTagsFromRoom,
getRoomLogo,
createTag,
fetchTags,

View File

@ -1013,6 +1013,8 @@ class FilesStore {
this.setCreatedItem(null);
}
this.updateRoomLoadingLogo();
return Promise.resolve(selectedFolder);
})
.catch((err) => {
@ -1982,6 +1984,41 @@ class FilesStore {
return this.filter.search;
}
getRoomLogo = async (logoHandlers) => {
const newLogos = {};
let disableFetch = false;
for (let key in logoHandlers) {
const icon = disableFetch
? ""
: await api.rooms.getLogoIcon(logoHandlers[key]);
if (!icon) disableFetch = true;
newLogos[key] = icon ? icon : "";
}
return newLogos;
};
updateRoomLoadingLogo = async () => {
const newRooms = await Promise.all(
this.folders.map(async (f) => {
const newRoom = JSON.parse(JSON.stringify(f));
if (!newRoom.isLogoLoading) return newRoom;
newRoom.isLogoLoading = false;
newRoom.logo = await this.getRoomLogo(newRoom.logoHandlers);
return newRoom;
})
);
this.setFolders(newRooms);
};
get filesList() {
const { getIcon } = this.filesSettingsStore;
//return [...this.folders, ...this.files];
@ -2012,6 +2049,8 @@ class FilesStore {
foldersCount,
id,
logo,
logoHandlers,
isLogoLoading,
locked,
parentId,
pureContentLength,
@ -2125,7 +2164,9 @@ class FilesStore {
icon,
id,
isFolder,
isLogoLoading,
logo,
logoHandlers,
locked,
new: item.new,
parentId,

View File

@ -5,7 +5,7 @@ import { FolderType, RoomSearchArea } from "../../constants";
import find from "lodash/find";
import { getFolderOptions, decodeDisplayName } from "../../utils";
import { Encoder } from "../../utils/encoder";
import { getRooms } from "../rooms";
import { getLogoIcon, getRooms } from "../rooms";
import RoomsFilter from "../rooms/filter";
export function openEdit(fileId, version, doc, view) {
@ -39,7 +39,45 @@ export function getFolderInfo(folderId) {
url: `/files/folder/${folderId}`,
};
return request(options);
return request(options).then((res) => {
if (res.roomType) {
return new Promise((resolve, reject) => {
if (res.rootFolderType === FolderType.Archive) {
res.isLogoLoading = false;
for (let key in room.logo) {
room.logo[key] = "";
}
return resolve(res);
}
res.isLogoLoading = false;
res.logoHandlers = res.logo;
const newLogos = {};
const actions = [];
const getLogo = async (key) => {
const logo = await getLogoIcon(res.logo[key]);
newLogos[key] = logo;
};
for (let key in res.logo) {
actions.push(getLogo(key));
}
return Promise.all(actions).then(() => {
res.logo = newLogos;
resolve(res);
});
});
}
return res;
});
}
export function getFolderPath(folderId) {
@ -57,6 +95,41 @@ export function getFolder(folderId, filter) {
res.files = decodeDisplayName(res.files);
res.folders = decodeDisplayName(res.folders);
const { current } = res;
if (current.roomType) {
res.current.isLogoLoading = false;
res.current.logoHandlers = current.logo;
if (current.rootFolderType === FolderType.Rooms) {
return new Promise((resolve, reject) => {
const actions = [];
const newLogos = {};
const getLogo = async (key) => {
const logo = await getLogoIcon(current.logo[key]);
newLogos[key] = logo;
};
for (let key in current.logo) {
actions.push(getLogo(key));
}
return Promise.all(actions).then(() => {
res.current.logo = newLogos;
resolve(res);
});
});
} else {
for (let key in res.current.logo) {
res.current.logo[key] = "";
}
}
}
return res;
});
}

View File

@ -13,21 +13,84 @@ export function getRooms(filter) {
res.folders = decodeDisplayName(res.folders);
if (res.current.rootFolderType === FolderType.Archive) {
res.folders.forEach((room) => (room.isArchive = true));
res.folders.forEach((room) => {
room.isArchive = true;
room.isLogoLoading = false;
for (let key in room.logo) {
room.logo[key] = "";
}
});
} else {
res.folders.forEach((f, index) => {
res.folders[index].isLogoLoading = true;
res.folders[index].logoHandlers = f.logo;
const newLogos = {};
for (let key in f.logo) {
newLogos[key] = "";
}
res.folders[index].logo = newLogos;
});
}
return res;
});
}
export function getLogoIcon(url) {
const options = {
// baseURL: combineUrl(AppServerConfig.proxyURL, config.homepage),
method: "get",
url: `/products/files/httphandlers${url}`,
responseType: "text",
};
return request(options);
}
export function getRoomInfo(id) {
const options = {
method: "get",
url: `/files/rooms/${id}`,
};
return request(options).then((res) => {
return res;
return request(options).then(async (res) => {
return new Promise((resolve, reject) => {
if (res.rootFolderType === FolderType.Archive) {
res.isLogoLoading = false;
res.isArchive = true;
for (let key in res.logo) {
res.logo[key] = "";
}
return resolve(res);
}
res.isLogoLoading = false;
res.logoHandlers = res.logo;
const newLogos = {};
const actions = [];
const getLogo = async (key) => {
const logo = await getLogoIcon(res.logo[key]);
newLogos[key] = logo;
};
for (let key in res.logo) {
actions.push(getLogo(key));
}
return Promise.all(actions).then(() => {
res.logo = newLogos;
resolve(res);
});
});
});
}

View File

@ -3,7 +3,7 @@ import { makeAutoObservable } from "mobx";
import { getUserRole } from "@docspace/client/src/helpers/people-helpers";
import { getUserById } from "@docspace/common/api/people";
import { combineUrl } from "@docspace/common/utils";
import { AppServerConfig } from "@docspace/common/constants";
import { AppServerConfig, FolderType } from "@docspace/common/constants";
import config from "PACKAGE_FILE";
import Filter from "../api/people/filter";
import { getRoomInfo } from "../api/rooms";
@ -144,6 +144,7 @@ class InfoPanelStore {
if (!currentFolderRoomId || currentFolderRoomId === prevRoomId) return;
const newSelectionParentRoom = await getRoomInfo(currentFolderRoomId);
if (prevRoomId === newSelectionParentRoom.id) return;
this.setSelectionParentRoom(
@ -163,7 +164,16 @@ class InfoPanelStore {
getInfoPanelItemIcon = (item, size) => {
return item.isRoom || !!item.roomType
? item.logo && item.logo.medium
? item.rootFolderType === FolderType.Archive
? this.settingsStore.getIcon(
size,
null,
null,
null,
item.roomType,
true
)
: item.logo && item.logo.medium
? item.logo.medium
: item.icon
? item.icon

View File

@ -110,7 +110,7 @@ public class FolderDtoHelper : FileEntryDtoHelper
result.Tags = folder.Tags.Select(t => t.Name);
}
result.Logo = await _roomLogoManager.GetLogo(folder);
result.Logo = _roomLogoManager.GetLogo(folder);
result.RoomType = folder.FolderType switch
{
FolderType.FillingFormsRoom => RoomType.FillingFormsRoom,

View File

@ -34,10 +34,14 @@ public class RoomLogoManager
private const string LogosPath = "{0}_size_{1}-{2}.{3}";
private const string ModuleName = "room_logos";
private const string TempDomainPath = "logos_temp";
private const string ActionName = "logo";
private const string Default = "default";
private static Size _originalLogoSize = new Size(1280, 1280);
private static Size _largeLogoSize = new Size(96, 96);
private static Size _mediumLogoSize = new Size(32, 32);
private static Size _smallLogoSize = new Size(16, 16);
private readonly IDaoFactory _daoFactory;
private readonly FileSecurity _fileSecurity;
private readonly ILogger<RoomLogoManager> _logger;
@ -50,8 +54,18 @@ public class RoomLogoManager
private static readonly Regex _cachePattern = new Regex(@"\d+\/\S+\/\d+\/\d+", RegexOptions.Compiled);
private static readonly TimeSpan _cacheLifeTime = TimeSpan.FromMinutes(30);
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly FilesLinkUtility _filesLinkUtility;
public RoomLogoManager(StorageFactory storageFactory, TenantManager tenantManager, IDaoFactory daoFactory, FileSecurity fileSecurity, ILogger<RoomLogoManager> logger, AscCache cache, FilesMessageService filesMessageService, IHttpContextAccessor httpContextAccessor)
public RoomLogoManager(
StorageFactory storageFactory,
TenantManager tenantManager,
IDaoFactory daoFactory,
FileSecurity fileSecurity,
ILogger<RoomLogoManager> logger,
AscCache cache,
FilesMessageService filesMessageService,
IHttpContextAccessor httpContextAccessor,
FilesLinkUtility filesLinkUtility)
{
_storageFactory = storageFactory;
_tenantManager = tenantManager;
@ -61,6 +75,7 @@ public class RoomLogoManager
_cache = cache;
_filesMessageService = filesMessageService;
_httpContextAccessor = httpContextAccessor;
_filesLinkUtility = filesLinkUtility;
}
public bool EnableAudit { get; set; } = true;
@ -133,39 +148,19 @@ public class RoomLogoManager
return room;
}
public async Task<Logo> GetLogo<T>(Folder<T> room)
public Logo GetLogo<T>(Folder<T> room)
{
var id = GetId(room);
var id = room.Id;
return new Logo
{
Original = await GetOriginalLogoPath(id),
Large = await GetLargeLogoPath(id),
Medium = await GetMediumLogoPath(id),
Small = await GetSmallLogoPath(id),
Original = GetOriginalLogoUrl(id),
Large = GetLargeLogoUrl(id),
Medium = GetMediumLogoUrl(id),
Small = GetSmallLogoUrl(id)
};
}
public async Task<byte[]> GetTempAsync(string fileName)
{
using var stream = await DataStore.GetReadStreamAsync(TempDomainPath, fileName);
var data = new MemoryStream();
var buffer = new byte[1024 * 10];
while (true)
{
var count = await stream.ReadAsync(buffer, 0, buffer.Length);
if (count == 0)
{
break;
}
data.Write(buffer, 0, count);
}
return data.ToArray();
}
public async Task<string> SaveTempAsync(byte[] data, long maxFileSize)
{
data = UserPhotoThumbnailManager.TryParseImage(data, maxFileSize, _originalLogoSize, out var imgFormat, out _, out _);
@ -178,7 +173,78 @@ public class RoomLogoManager
return path.ToString();
}
public async Task<string> SaveWithProcessAsync<T>(T id, byte[] imageData, long maxFileSize, Point position, Size cropSize)
public string GetOriginalLogoUrl<T>(T id)
{
return GetLogoUrl(id, RoomLogoSize.Original);
}
public string GetLargeLogoUrl<T>(T id)
{
return GetLogoUrl(id, RoomLogoSize.Large);
}
public string GetMediumLogoUrl<T>(T id)
{
return GetLogoUrl(id, RoomLogoSize.Medium);
}
public string GetSmallLogoUrl<T>(T id)
{
return GetLogoUrl(id, RoomLogoSize.Small);
}
public async ValueTask<string> GetMediumLogoPathAsync<T>(T id)
{
return await GetLogoPathAsync(id, _mediumLogoSize);
}
public async ValueTask<string> GetSmallLogoPathAsync<T>(T id)
{
return await GetLogoPathAsync(id, _smallLogoSize);
}
public async ValueTask<string> GetLargeLogoPathAsync<T>(T id)
{
return await GetLogoPathAsync(id, _largeLogoSize);
}
public async ValueTask<string> GetOriginalLogoPathAsync<T>(T id)
{
return await GetLogoPathAsync(id, null, true);
}
public async Task<string> GetLogoPathAsync<T>(T id, string size)
{
var room = await _daoFactory.GetFolderDao<T>().GetFolderAsync(id);
if (room == null)
{
throw new ItemNotFoundException("Room not found");
}
if (!await _fileSecurity.CanReadAsync(room))
{
throw new SecurityException("You don't have permission to read the room");
}
if (!RoomLogoSizeExtensions.TryParse(size, true, out var result))
{
throw new ArgumentException("Size not valid", nameof(size));
}
id = GetId(room);
return result switch
{
RoomLogoSize.Original => await GetOriginalLogoPathAsync(id),
RoomLogoSize.Large => await GetLargeLogoPathAsync(id),
RoomLogoSize.Medium => await GetMediumLogoPathAsync(id),
RoomLogoSize.Small => await GetSmallLogoPathAsync(id),
_ => throw new NotImplementedException()
};
}
private async Task<string> SaveWithProcessAsync<T>(T id, byte[] imageData, long maxFileSize, Point position, Size cropSize)
{
imageData = UserPhotoThumbnailManager.TryParseImage(imageData, maxFileSize, _originalLogoSize, out var imageFormat, out var width, out var height);
@ -240,60 +306,66 @@ public class RoomLogoManager
}
}
public async ValueTask<string> GetOriginalLogoPath<T>(T id)
private string GetLogoUrl<T>(T id, RoomLogoSize size)
{
var path = _cache.Get<string>(GetKey(id));
if (!string.IsNullOrEmpty(path))
{
return await ValueTask.FromResult(path);
}
await LoadPathToCache(id);
path = _cache.Get<string>(GetKey(id));
return path ?? string.Empty;
return $"{_filesLinkUtility.FileHandlerPath}?{FilesLinkUtility.Action}={ActionName}" +
$"&{FilesLinkUtility.FolderId}={Uri.EscapeDataString(id.ToString())}" +
$"&{FilesLinkUtility.Size}={size.ToStringLowerFast()}";
}
public async ValueTask<string> GetMediumLogoPath<T>(T id)
private async ValueTask<string> GetLogoPathAsync<T>(T id, Size? size, bool original = false)
{
return await GetLogoPath(id, _mediumLogoSize);
}
public async ValueTask<string> GetSmallLogoPath<T>(T id)
{
return await GetLogoPath(id, _smallLogoSize);
}
public async ValueTask<string> GetLargeLogoPath<T>(T id)
{
return await GetLogoPath(id, _largeLogoSize);
}
public async ValueTask<string> GetLogoPath<T>(T id, Size size)
{
var key = GetKey(id, size);
var key = original ? GetKey(id) : GetKey(id, size.Value);
var path = _cache.Get<string>(key);
if (path == Default)
{
return string.Empty;
}
if (!string.IsNullOrEmpty(path))
{
return await ValueTask.FromResult(path);
}
await LoadPathToCache(id);
await LoadPathToCacheAsync(id);
path = _cache.Get<string>(key);
return path ?? string.Empty;
return path == null || path == Default ? string.Empty : path;
}
public async Task LoadPathToCache<T>(T id)
private async Task<byte[]> GetTempAsync(string fileName)
{
using var stream = await DataStore.GetReadStreamAsync(TempDomainPath, fileName);
var data = new MemoryStream();
var buffer = new byte[1024 * 10];
while (true)
{
var count = await stream.ReadAsync(buffer, 0, buffer.Length);
if (count == 0)
{
break;
}
data.Write(buffer, 0, count);
}
return data.ToArray();
}
private async Task LoadPathToCacheAsync<T>(T id)
{
var logoPath = await DataStore.ListFilesAsync(string.Empty, $"{ProcessFolderId(id)}*", false)
.Select(u => u.ToString()).ToListAsync();
if (logoPath.Count == 0)
{
SetDefaultCache(id);
return;
}
var original = logoPath.Where(u => u.Contains("orig")).FirstOrDefault();
_cache.Insert(GetKey(id), original, _cacheLifeTime);
@ -333,9 +405,26 @@ public class RoomLogoManager
return $"{TenantId}/{id}/orig";
}
private void SetDefaultCache<T>(T id)
{
_cache.Insert(GetKey(id), Default, _cacheLifeTime);
_cache.Insert(GetKey(id, _largeLogoSize), Default, _cacheLifeTime);
_cache.Insert(GetKey(id, _mediumLogoSize), Default, _cacheLifeTime);
_cache.Insert(GetKey(id, _smallLogoSize), Default, _cacheLifeTime);
}
private T GetId<T>(Folder<T> room)
{
return room.ProviderEntry && (room.RootId.ToString().Contains("sbox")
|| room.RootId.ToString().Contains("spoint")) ? room.RootId : room.Id;
}
}
[EnumExtensions]
public enum RoomLogoSize
{
Original = 0,
Large = 1,
Medium = 2,
Small = 3,
}

View File

@ -74,6 +74,7 @@ public class FilesLinkUtility
public const string OutType = "outputtype";
public const string AuthKey = "stream_auth";
public const string Anchor = "anchor";
public const string Size = "size";
public string FileHandlerPath
{

View File

@ -79,6 +79,7 @@ public class FileHandlerService
private readonly SocketManager _socketManager;
private readonly ILogger<FileHandlerService> _logger;
private readonly IHttpClientFactory _clientFactory;
private readonly RoomLogoManager _roomLogoManager;
public FileHandlerService(
FilesLinkUtility filesLinkUtility,
@ -109,7 +110,8 @@ public class FileHandlerService
CompressToArchive compressToArchive,
InstanceCrypto instanceCrypto,
IHttpClientFactory clientFactory,
ThumbnailSettings thumbnailSettings)
ThumbnailSettings thumbnailSettings,
RoomLogoManager roomLogoManager)
{
_filesLinkUtility = filesLinkUtility;
_tenantExtra = tenantExtra;
@ -139,7 +141,8 @@ public class FileHandlerService
_userManager = userManager;
_logger = logger;
_clientFactory = clientFactory;
this._thumbnailSettings = thumbnailSettings;
_thumbnailSettings = thumbnailSettings;
_roomLogoManager = roomLogoManager;
}
public Task Invoke(HttpContext context)
@ -193,6 +196,9 @@ public class FileHandlerService
case "track":
await TrackFile(context);
break;
case "logo":
await GetRoomLogoPathAsync(context);
break;
default:
throw new HttpException((int)HttpStatusCode.BadRequest, FilesCommonResource.ErrorMassage_BadRequest);
}
@ -1563,6 +1569,60 @@ public class FileHandlerService
await context.Response.WriteAsync(TrackResponse.Serialize(result));
}
private async Task GetRoomLogoPathAsync(HttpContext context)
{
if (!_securityContext.IsAuthenticated)
{
context.Response.StatusCode = (int)HttpStatusCode.Forbidden;
return;
}
var folderId = context.Request.Query[FilesLinkUtility.FolderId].FirstOrDefault();
var size = context.Request.Query[FilesLinkUtility.Size].FirstOrDefault();
string result;
try
{
if (int.TryParse(folderId, out var id))
{
result = await _roomLogoManager.GetLogoPathAsync(id, size);
}
else
{
result = await _roomLogoManager.GetLogoPathAsync(folderId, size);
}
}
catch(ItemNotFoundException)
{
context.Response.StatusCode = (int)HttpStatusCode.NotFound;
return;
}
catch (SecurityException)
{
context.Response.StatusCode = (int)HttpStatusCode.Forbidden;
return;
}
catch
{
context.Response.StatusCode = (int)HttpStatusCode.BadRequest;
return;
}
context.Response.Clear();
await context.Response.WriteAsync(result);
try
{
await context.Response.Body.FlushAsync();
await context.Response.CompleteAsync();
}
catch (HttpException ex)
{
_logger.Error(ex.Message);
}
}
}
public static class FileHandlerExtensions