Удаление файлов с возможностью восстановления¶
В системе поддерживается функциональность удаления файла с возможностью его восстановления, если эта опция включена в карточке Настройки сервера. По умолчанию удаление файла в корзину поддерживается только в файловом контроле карточки. Для управления и расширения данной функциональности необходимо написать клиентские и серверные расширения.
Удаление файла¶
Если файл удаляется из файлового контейнера IFileContainer
или IFileUIContainer
, то можно воспользоваться методом расширения RemoveWithNotificationAsync
, который удалит файл с уведомлением его источника. Например, перед сохранением карточки выполните следующий код на клиенте или сервере:
// Получение файлового контейнера из файлового менеджера или контекста
IFileContainer fileContainer = ...
// Поиск необходимого файла в контейнере для удаления
IFile fileToRemove = fileContainer.Files.FirstOrDefault(x => x.Name == "file.txt");
if (fileToRemove is not null)
{
// Удаление файла с возможностью восстановления
bool removed = await fileContainer.Files.RemoveWithNotificationAsync(fileToRemove, withBackup: true, cancellationToken);
}
Код, указанный выше, установит для свойства CardFile.DeletionMode
значение CardFileDeletionMode.Backup
.
Important
Значение CardFileDeletionMode.Backup
учитывается, только если в свойстве CardFile.State
установлено CardFileState.Deleted
. При сохранении карточки с расширениями на сервере для всех удалённых файлов будет выполнена проверка свойств CardFile.State
и CardFile.DeletionMode
. Если файл удаляется с возможностью восстановления, а в системе отключена опция удаления файла с возможностью восстановления (см. Настройки сервера), то для свойства CardFile.DeletionMode
будет установлено значение CardFileDeletionMode.None
. Учитывайте это при написании расширений.
При удалении файла в корзину записи из таблиц Files
и FileVersions
будут перенесены в таблицы DeletedFiles
и DeletedFileVersions
. Также записи из всех секций, которые содержатся в метаданных типа файла, будут перенесены в таблицы с префиксом Deleted
. Например, таковой таблицей является DeletedFileSignatures
.
Important
Если в решении изменялись типовые карточки файлов или были добавлены собственные типы карточек файлов, то для используемых в них секций необходимо добавить соответствующие таблицы с префиксом Deleted
для корректного удаления файла с возможностью восстановления.
Если при удалении файла требуется выполнить бэкап, восстановление или полное удаление связанных данных, то необходимо написать расширение на сохранение карточки с проверкой способа удаления файла. Например:
/// <summary>
/// Расширение для работы со связанными данными при удалении и восстановлении файла.
/// </summary>
public sealed class ManageLinkedDataStoreExtension : CardStoreExtension
{
#region Fields
private readonly ICardFileBackupSettings cardFileBackupSettings;
#endregion
#region Constructors
/// <summary>
/// Создаёт экземпляр класса <see cref="ManageLinkedDataStoreExtension"/>.
/// </summary>
/// <param name="cardFileBackupSettings"><inheritdoc cref="ICardFileBackupSettings" path="/summary"/></param>
public BackupCardFileStoreExtension(ICardFileBackupSettings cardFileBackupSettings) =>
this.cardFileBackupSettings = NotNullOrThrow(cardFileBackupSettings);
#endregion
#region Base Overrides
/// <inheritdoc/>
public override async Task BeforeCommitTransaction(ICardStoreExtensionContext context)
{
if (context.ValidationResult.IsSuccessful()
&& context.Request.Card.TryGetFiles() is { Count: > 0 } files)
{
if (await this.cardFileBackupSettings.CanDeleteWithBackupAsync(context.ValidationResult, context.CancellationToken))
{
// в системе поддерживается удаление файлов в корзину
}
foreach (var file in files)
{
if (file is { State: CardFileState.Deleted, DeletionMode: not CardFileDeletionMode.Backup })
{
// файл удаляется без корзины
}
else if (file is { State: CardFileState.None, DeletionMode: CardFileDeletionMode.Wipe })
{
// файл удаляется из корзины
}
else if (file is { State: CardFileState.Deleted, DeletionMode: CardFileDeletionMode.Backup })
{
// файл удаляется в корзину
}
else if (file is { State: CardFileState.Inserted, DeletionMode: CardFileDeletionMode.Restore })
{
// файл восстанавливается из корзины
}
}
}
}
#endregion
}
Регистрация такого расширения выглядит следующим образом:
[Registrator]
public sealed class Registrator : RegistratorBase
{
public override void RegisterExtensions(IExtensionContainer extensionContainer)
{
extensionContainer
.RegisterExtension<ICardStoreExtension, CardFileBackupStoreExtension>(x => x
.WithOrder(ExtensionStage.AfterPlatform, 1)
.WithSingleton() // если расширение использует зависимости, то необходим использовать Unity
.WhenAnyStoreMethod() // тут может быть фильтрация по способу сохранения карточки
.WhenAnyCardType()); // тут может быть фильтрация по типу карточки
}
}
Пример такого расширения для переноса аннотаций, добавленных к удаляемому файлу можно посмотреть в Tessa.Extensions.Default.Server.PdfAnnotations.PdfAnnotationsStoreExtension
.
Восстановление файла¶
Для восстановления файла необходимо добавить в карточку новый файл, в котором для свойства CardFile.DeletionMode
установлено значение CardFileDeletionMode.Restore
.
Important
Значение CardFileDeletionMode.Restore
учитывается, только если в свойстве CardFile.State
установлено CardFileState.Inserted
.
Добавление нового файла выполняет платформенное расширение RestoreCardFileStoreExtension
. Оно опирается на ключ CardHelper.FilesToRestoreKey
для получения коллекции восстанавливаемых файлов. Чтобы добавить данный ключ при сохранении карточки необходимо написать следующий код:
await using var _ = dbScope.Create();
// получение информации об удалённых файлах
var deletedFiles = await dbScope.Db
.SetCommand(
dbScope.BuilderFactory
.Select().C("f",
nameof(CardFileDeletedInfo.RowID),
nameof(CardFileDeletedInfo.Deleted),
nameof(CardFileDeletedInfo.DeletedByID),
nameof(CardFileDeletedInfo.DeletedByName))
.From("DeletedFiles", "f").NoLock()
.Where().C("f", "ID").Equals().P("CardID")
.Build(),
dbScope.Db.Parameter("CardID", cardID, DataType.Guid))
.LogCommand()
.ExecuteListAsync<CardFileDeletedInfo>(cancellationToken);
// установка ключа в Info сохраняемой карточки
card.Info = [CardHelper.FilesToRestoreKey] = deletedFiles
.Select(f => (object) f.ToSerializedDictionary())
.ToList();
На клиенте можно выполнить запрос на сервер или воспользоваться данными из представления DeletedFiles
(см. пример Tessa.Extensions.Default.Client.Files.RestoreFileControlExtension
).