Merge pull request #5309 from sbwalker/dev

improve module export so that content can be saved to a file
This commit is contained in:
Shaun Walker 2025-05-15 08:56:37 -04:00 committed by GitHub
commit f9ca611b8b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 164 additions and 14 deletions

View File

@ -5,24 +5,45 @@
@inject IStringLocalizer<Export> Localizer
@inject IStringLocalizer<SharedResources> SharedLocalizer
<TabStrip>
<TabPanel Name="Text" Heading="Text" ResourceKey="Text">
<div class="container">
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="content" HelpText="The Exported Module Content" ResourceKey="Content">Content: </Label>
<Label Class="col-sm-3" For="content" HelpText="Select the Export option and you will be able to view the module content" ResourceKey="Content">Content: </Label>
<div class="col-sm-9">
<textarea id="content" class="form-control" @bind="@_content" rows="5" readonly></textarea>
</div>
</div>
</div>
<button type="button" class="btn btn-success" @onclick="ExportModule">@Localizer["Export"]</button>
<br />
<button type="button" class="btn btn-success" @onclick="ExportText">@Localizer["Export"]</button>
<NavLink class="btn btn-secondary" href="@PageState.ReturnUrl">@SharedLocalizer["Cancel"]</NavLink>
</TabPanel>
<TabPanel Name="File" Heading="File" ResourceKey="File">
<div class="container">
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="folder" HelpText="Select a folder where you wish to save the exported content" ResourceKey="Folder">Folder: </Label>
<div class="col-sm-9">
<FileManager ShowFiles="false" ShowUpload="false" @ref="_filemanager" />
</div>
</div>
</div>
<br />
<button type="button" class="btn btn-success" @onclick="ExportFile">@Localizer["Export"]</button>
<NavLink class="btn btn-secondary" href="@PageState.ReturnUrl">@SharedLocalizer["Cancel"]</NavLink>
</TabPanel>
</TabStrip>
@code {
private string _content = string.Empty;
private FileManager _filemanager;
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
public override string Title => "Export Content";
private async Task ExportModule()
private async Task ExportText()
{
try
{
@ -35,4 +56,34 @@
AddModuleMessage(Localizer["Error.Module.Export"], MessageType.Error);
}
}
private async Task ExportFile()
{
try
{
var folderid = _filemanager.GetFolderId();
if (folderid != -1)
{
var result = await ModuleService.ExportModuleAsync(ModuleState.ModuleId, PageState.Page.PageId, folderid);
if (result.Success)
{
AddModuleMessage(string.Format(Localizer["Success.Export.File"], result.Message), MessageType.Success);
}
else
{
AddModuleMessage(Localizer["Error.Module.Export"], MessageType.Error);
}
}
else
{
AddModuleMessage(Localizer["Message.Content.Export"], MessageType.Warning);
}
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Exporting Module {ModuleId} {Error}", ModuleState.ModuleId, ex.Message);
AddModuleMessage(Localizer["Error.Module.Export"], MessageType.Error);
}
}
}

View File

@ -121,7 +121,7 @@
<value>Export</value>
</data>
<data name="Content.HelpText" xml:space="preserve">
<value>The Exported Module Content</value>
<value>Select the Export option and you will be able to view the module content</value>
</data>
<data name="Content.Text" xml:space="preserve">
<value>Content: </value>
@ -135,4 +135,22 @@
<data name="Export Content" xml:space="preserve">
<value>Export Content</value>
</data>
<data name="Text.Heading" xml:space="preserve">
<value>Text</value>
</data>
<data name="File.Heading" xml:space="preserve">
<value>File</value>
</data>
<data name="Folder.Text" xml:space="preserve">
<value>Folder:</value>
</data>
<data name="Folder.HelpText" xml:space="preserve">
<value>Select a folder where you wish to save the exported content</value>
</data>
<data name="Message.Content.Export" xml:space="preserve">
<value>Please Select A Folder Before Choosing Export</value>
</data>
<data name="Success.Export.File" xml:space="preserve">
<value>Content Was Successfully Exported To Specified Folder With Filename {0}</value>
</data>
</root>

View File

@ -56,7 +56,17 @@ namespace Oqtane.Services
/// Exports a given module
/// </summary>
/// <param name="moduleId"></param>
/// <returns>module in JSON</returns>
/// <param name="pageId"></param>
/// <returns>module content in JSON format</returns>
Task<string> ExportModuleAsync(int moduleId, int pageId);
/// <summary>
/// Exports a given module
/// </summary>
/// <param name="moduleId"></param>
/// <param name="pageId"></param>
/// <param name="folderId"></param>
/// <returns>success/failure</returns>
Task<Result> ExportModuleAsync(int moduleId, int pageId, int folderId);
}
}

View File

@ -50,5 +50,10 @@ namespace Oqtane.Services
{
return await GetStringAsync($"{Apiurl}/export?moduleid={moduleId}&pageid={pageId}");
}
public async Task<Result> ExportModuleAsync(int moduleId, int pageId, int folderId)
{
return await PostJsonAsync<Result>($"{Apiurl}/export?moduleid={moduleId}&pageid={pageId}&folderid={folderId}", null);
}
}
}

View File

@ -9,6 +9,10 @@ using Oqtane.Infrastructure;
using Oqtane.Repository;
using Oqtane.Security;
using System.Net;
using System.IO;
using System;
using static System.Net.WebRequestMethods;
using System.Net.Http;
namespace Oqtane.Controllers
{
@ -20,18 +24,22 @@ namespace Oqtane.Controllers
private readonly IPageRepository _pages;
private readonly IModuleDefinitionRepository _moduleDefinitions;
private readonly ISettingRepository _settings;
private readonly IFolderRepository _folders;
private readonly IFileRepository _files;
private readonly IUserPermissions _userPermissions;
private readonly ISyncManager _syncManager;
private readonly ILogManager _logger;
private readonly Alias _alias;
public ModuleController(IModuleRepository modules, IPageModuleRepository pageModules, IPageRepository pages, IModuleDefinitionRepository moduleDefinitions, ISettingRepository settings, IUserPermissions userPermissions, ITenantManager tenantManager, ISyncManager syncManager, ILogManager logger)
public ModuleController(IModuleRepository modules, IPageModuleRepository pageModules, IPageRepository pages, IModuleDefinitionRepository moduleDefinitions, ISettingRepository settings, IFolderRepository folders, IFileRepository files, IUserPermissions userPermissions, ITenantManager tenantManager, ISyncManager syncManager, ILogManager logger)
{
_modules = modules;
_pageModules = pageModules;
_pages = pages;
_moduleDefinitions = moduleDefinitions;
_settings = settings;
_folders = folders;
_files = files;
_userPermissions = userPermissions;
_syncManager = syncManager;
_logger = logger;
@ -248,6 +256,62 @@ namespace Oqtane.Controllers
return content;
}
// POST api/<controller>/export?moduleid=x&pageid=y&folderid=z
[HttpPost("export")]
[Authorize(Roles = RoleNames.Registered)]
public Result Export(int moduleid, int pageid, int folderid)
{
var result = new Result(false);
var module = _modules.GetModule(moduleid);
if (module != null && module.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, module.SiteId, EntityNames.Page, pageid, PermissionNames.Edit) &&
_userPermissions.IsAuthorized(User, module.SiteId, EntityNames.Folder, folderid, PermissionNames.Edit))
{
// get content
var content = _modules.ExportModule(moduleid);
// get folder
var folder = _folders.GetFolder(folderid, false);
string folderPath = _folders.GetFolderPath(folder);
if (!Directory.Exists(folderPath))
{
Directory.CreateDirectory(folderPath);
}
// create text file
var filename = Utilities.GetTypeNameLastSegment(module.ModuleDefinitionName, 0) + moduleid.ToString() + ".json";
string filepath = Path.Combine(folderPath, filename);
if (System.IO.File.Exists(filepath))
{
System.IO.File.Delete(filepath);
}
System.IO.File.WriteAllText(filepath, content);
// register file
var file = _files.GetFile(folderid, filename);
if (file == null)
{
file = new Models.File { FolderId = folderid, Name = filename, Extension = "txt", Size = (int)new FileInfo(filepath).Length, ImageWidth = 0, ImageHeight = 0 };
_files.AddFile(file);
}
else
{
file.Size = (int)new FileInfo(filepath).Length;
_files.UpdateFile(file);
}
result.Success = true;
result.Message = filename;
_logger.Log(LogLevel.Information, this, LogFunction.Read, "Content Exported For Module {ModuleId} To Folder {FolderId}", moduleid, folderid);
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Export Attempt For Module {Module} To Folder {FolderId}", moduleid, folderid);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
}
return result;
}
// POST api/<controller>/import?moduleid=x&pageid=y
[HttpPost("import")]
[Authorize(Roles = RoleNames.Registered)]

View File

@ -6,6 +6,8 @@ namespace Oqtane.Models
public string Message { get; set; }
public Result() {}
public Result(bool success)
{
Success = success;