Hi,
I want to ask for advice on how to deal with two problems that I encountered when implementing RichTextEditor in my application.
The expected behavior was that it would be possible to insert .xlsx, .docx and similar files into the content in addition to images.
My problems:
1. When trying to insert a .docx, .xlsx, .pdf file, etc., the insert button in FileManager is unavailable.
2. Thumbnails are not displayed for image files.
My code sample:
FileBrowserController.cs
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
using OrchardCore.Media;
using OrchardCore.Settings;
using Syncfusion.EJ2.FileManager.Base;
using Syncfusion.EJ2.FileManager.PhysicalFileProvider;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net.Http.Headers;
using System.Threading.Tasks;
namespace TheAdmin.Controllers
{
[ApiController]
[IgnoreAntiforgeryToken]
[AllowAnonymous]
[Produces("application/json")]
public partial class FileBrowserController : Controller
{
public PhysicalFileProvider operation;
public string basePath;
public string root = "App_Data\\Sites\\SkDruzec";
private IWebHostEnvironment hostingEnv;
private readonly IMediaFileStore _mediaFileStore;
public FileBrowserController(IWebHostEnvironment hostEnvironment, IMediaFileStore mediaFileStore)
{
this.hostingEnv = hostEnvironment;
this.basePath = hostEnvironment.ContentRootPath;
this.operation = new PhysicalFileProvider();
_mediaFileStore = mediaFileStore;
this.operation.RootFolder(this.basePath + "\\" + this.root);
}
[AllowAnonymous]
[Route("api/FileBrowser/FileOperations"), HttpPost]
public object FileOperations([FromBody] FileManagerDirectoryContent args)
{
var defaultMediaPath = _mediaFileStore.MapPathToPublicUrl(HttpContext.Request.PathBase);
//this.root = defaultMediaPath;
//this.operation.RootFolder(this.basePath + this.root);
//var testTargetPath = args.TargetPath;
if (args.Action == "delete" || args.Action == "rename")
{
if ((args.TargetPath == null) && (args.Path == ""))
{
FileManagerResponse response = new FileManagerResponse();
ErrorDetails er = new ErrorDetails
{
Code = "401",
Message = "Restricted to modify the root folder."
};
response.Error = er;
return this.operation.ToCamelCase(response);
}
}
switch (args.Action)
{
case "read":
return this.operation.ToCamelCase(this.operation.GetFiles(args.Path, args.ShowHiddenItems));
case "delete":
return this.operation.ToCamelCase(this.operation.Delete(args.Path, args.Names));
case "copy":
return this.operation.ToCamelCase(this.operation.Copy(args.Path, args.TargetPath, args.Names, args.RenameFiles, args.TargetData));
case "move":
return this.operation.ToCamelCase(this.operation.Move(args.Path, args.TargetPath, args.Names, args.RenameFiles, args.TargetData));
case "details":
return this.operation.ToCamelCase(this.operation.Details(args.Path, args.Names, args.Data));
case "create":
return this.operation.ToCamelCase(this.operation.Create(args.Path, args.Name));
case "search":
return this.operation.ToCamelCase(this.operation.Search(args.Path, args.SearchString, args.ShowHiddenItems, args.CaseSensitive));
case "rename":
return this.operation.ToCamelCase(this.operation.Rename(args.Path, args.Name, args.NewName));
}
return null;
}
[AllowAnonymous]
[Route("api/FileBrowser/Upload"), HttpPost]
public IActionResult Upload(string path, IList<IFormFile> uploadFiles, string action)
{
FileManagerResponse uploadResponse;
uploadResponse = operation.Upload(path, uploadFiles, action, null);
if (uploadResponse.Error != null)
{
Response.Clear();
Response.ContentType = "application/json; charset=utf-8";
Response.StatusCode = Convert.ToInt32(uploadResponse.Error.Code);
Response.HttpContext.Features.Get<IHttpResponseFeature>().ReasonPhrase = uploadResponse.Error.Message;
}
return Content("");
}
[AllowAnonymous]
[Route("api/FileBrowser/Download")]
public IActionResult Download(string downloadInput)
{
FileManagerDirectoryContent args = JsonConvert.DeserializeObject<FileManagerDirectoryContent>(downloadInput);
return operation.Download(args.Path, args.Names);
}
[AllowAnonymous]
[Route("api/FileBrowser/GetImage")]
public IActionResult GetImage(FileManagerDirectoryContent args)
{
return this.operation.GetImage(args.Path, args.Id, false, null, null);
}
[AllowAnonymous]
[Route("api/FileBrowser/SaveImage")]
[AcceptVerbs("POST")]
public void SaveImage(IList<IFormFile> UploadFiles)
{
try
{
foreach (IFormFile file in UploadFiles)
{
if (UploadFiles != null)
{
string filename = ContentDispositionHeaderValue.Parse(file.ContentDisposition).FileName.Trim('"');
filename = hostingEnv.ContentRootPath + "\\" + this.root + "\\Uploads" + $@"\{filename}";
if (!Directory.Exists(hostingEnv.ContentRootPath + "\\" + this.root + "\\Uploads"))
{
Directory.CreateDirectory(hostingEnv.ContentRootPath + "\\" + this.root + "\\Uploads");
}
if (!System.IO.File.Exists(filename))
{
using (FileStream fs = System.IO.File.Create(filename))
{
file.CopyTo(fs);
fs.Flush();
}
Response.StatusCode = 200;
}
}
}
}
catch (Exception)
{
Response.StatusCode = 204;
}
}
[AllowAnonymous]
[Route("api/FileBrowser/RemoveImage")]
[AcceptVerbs("POST")]
public void RemoveImage(IList<IFormFile> UploadFiles)
{
try
{
foreach (IFormFile file in UploadFiles)
{
if (UploadFiles != null)
{
System.IO.File.Delete(hostingEnv.ContentRootPath + "\\" + this.root + "\\Uploads" + $@"\{file.FileName}");
// Do remove action here}
Response.StatusCode = 200;
}
}
}
catch (Exception)
{
Response.StatusCode = 204;
}
}
//[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
//public IActionResult Error()
//{
// string requestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
// return Ok(requestId);
//}
}
}
HtmlBodyPart-RichTextEditor.Edit.cshtml
@using OrchardCore.Html.ViewModels;
@using Microsoft.AspNetCore.Http.Extensions;
@model HtmlBodyPartViewModel
@{
var hostUrl = string.Format("{0}://{1}", Context.Request.Scheme, Context.Request.Host);
var ajaxSettings = new
{
url = hostUrl + "/api/FileBrowser/FileOperations",
getImageUrl = hostUrl + "/api/FileBrowser/GetImage",
uploadUrl = hostUrl + "/api/FileBrowser/Upload",
downloadUrl = hostUrl + "/api/FileBrowser/Download"
};
var tools = new[]
{
"Bold", "Italic", "Underline", "StrikeThrough",
"FontName", "FontSize", "FontColor", "BackgroundColor",
"LowerCase", "UpperCase","SuperScript", "SubScript", "|",
"Formats", "Alignments", "NumberFormatList", "BulletFormatList",
"Outdent", "Indent", "|",
"CreateTable", "CreateLink", "FileManager", "|", "ClearFormat", "Print",
"SourceCode", "FullScreen", "|", "Undo", "Redo"
};
var table = new[]
{
"TableHeader", "TableRows", "TableColumns", "TableCell", "-", "BackgroundColor", "TableRemove", "TableCellVerticalAlign", "Styles"
};
}
@functions{
}
<fieldset class="mb-3">
<label asp-for="Html">@T["Body"]</label>
<textarea asp-for="Html" hidden></textarea>
<ejs-richtexteditor id='@Html.IdFor(x => x.Html)_editor' showCharCount="true" maxLength="2000" created="created" value="@Model.Html">
<e-richtexteditor-toolbarsettings items="@tools"></e-richtexteditor-toolbarsettings>
<e-richtexteditor-quicktoolbarsettings table="@table"></e-richtexteditor-quicktoolbarsettings>
<e-richtexteditor-filemanagersettings enable="true" path="/media" ajaxSettings="@ajaxSettings"></e-richtexteditor-filemanagersettings>
@*<e-richtexteditor-insertimagesettings saveUrl="/api/FileBrowser/SaveImage" removeUrl="/api/FileBrowser/RemoveImage" path="/Media/"></e-richtexteditor-insertimagesettings>*@
</ejs-richtexteditor>
</fieldset>
<style asp-name="Syncfusion-codemirror"></style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.3.0/codemirror.js" type="text/javascript"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.3.0/mode/css/css.js" type="text/javascript"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.3.0/mode/xml/xml.js" type="text/javascript"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.3.0/mode/htmlmixed/htmlmixed.js" type="text/javascript"></script>
<style>
.e-code-mirror::before {
content: '\e345';
}
.e-html-preview::before {
content: '\e350';
}
.CodeMirror-linenumber,
.CodeMirror-gutters {
display: none;
}
.sb-header {
z-index: 100;
}
.fabric-dark .cm-s-default .cm-tag,
.bootstrap5-dark .cm-s-default .cm-tag,
.material-dark .cm-s-default .cm-tag,
.tailwind-dark .cm-s-default .cm-tag,
.highcontrast .cm-s-default .cm-tag {
color: #00ff00;
}
.fabric-dark .cm-s-default .cm-string,
.bootstrap5-dark .cm-s-default .cm-string,
.material-dark .cm-s-default .cm-string,
.tailwind-dark .cm-s-default .cm-string {
color: blue;
}
.highcontrast .cm-s-default .cm-string {
color: #ffd939;
}
.fabric-dark .cm-s-default .cm-attribute,
.bootstrap5-dark .cm-s-default .cm-attribute,
.material-dark .cm-s-default .cm-attribute,
.tailwind-dark .cm-s-default .cm-attribute,
.highcontrast .cm-s-default .cm-attribute {
color: #f00;
}
.fabric-dark .CodeMirror,
.bootstrap5-dark .CodeMirror,
.material-dark .CodeMirror,
.tailwind-dark .CodeMirror {
background: #303030;
}
.highcontrast .CodeMirror {
background: black;
}
.sb-content.e-view.hide-header {
top: 0 !important;
}
.sb-header.e-view.hide-header {
display: none;
}
.e-richtexteditor .e-rte-content .e-content pre {
padding: 10px;
background: #F4F5F7;
}
.fabric-dark .e-richtexteditor .e-rte-content .e-content pre,
.bootstrap5-dark .e-richtexteditor .e-rte-content .e-content pre,
.material-dark .e-richtexteditor .e-rte-content .e-content pre,
.tailwind-dark .e-richtexteditor .e-rte-content .e-content pre,
.highcontrast .e-richtexteditor .e-rte-content .e-content pre {
padding: 10px;
background: #303030;
}
</style>
<script type="text/javascript">
document.addEventListener("DOMContentLoaded", function() {
var editor = document.getElementById('@Html.IdFor(x => x.Html)_editor');
//editor.value = textArea.value;
window.addEventListener("submit", function() {
var textArea = document.getElementById('@Html.IdFor(x => x.Html)');
textArea.innerHTML = rteObj.value;
console.log("-- editor.value --");
console.log(rteObj.value);
console.log("-- textArea --");
console.log(textArea.value);
console.log(textArea.innerHTML);
alert(textArea.value);
});
});
var rteObj, myCodeMirror, textAreaEditor;
function created() {
rteObj = this;
textAreaEditor = rteObj.contentModule.getEditPanel();
}
</script>
Thanks for any help.
Hi,
I created a test project, download here.
Url for displaying the RichTextEditor interface: ContentItems/TestPage/Edit
Login
User: Admin
Password: Admin8421@
The implementation code is in TheAdmin project
Good day,
I tried the functionality of the example provided and it works on another development PC.
The libraries used can be updated (downloaded) from nuget.org.
Source code here
Hi,
I apologize for the insufficient description of how to view RTE.
The procedure is on the attached video.
We have validated the reported issue with the shared application and able to replicate the issue. We have compared our file service provider with the shared WebAPI file service provider, and the only difference is adding the ApiController attribute in the provider. After removing the ApiController attribute, the sample works fine.
We would like to let you know that inclusion of the ApiController attribute is not mandatory in our file service provider. To achieve your requirement, we suggest you remove the mentioned attribute from your service provider. We have attached the modified sample for your reference.
https://www.syncfusion.com/downloads/support/directtrac/general/ze/SyncfusionRTE830642138
Hi,
thank you, it works almost perfectly.
The only thing missing for perfection is the ability to insert files .xlsx, .docx, .pdf, ...
I noticed that it is not possible to embed images in .webp format either.
Again, thank you very much.
Hi Patrik,
As we mentioned earlier, we have already considered your requirement as a feature and will implement in the certain release based on the customer request count and priority. So, we will intimate you once the road map has been prepared. You can track the status of the feature from the feedback link.
Feedback Link: https://www.syncfusion.com/feedback/28115/provide-an-option-to-browse-the-file-and-insert-a-link-for-the-selected-file-in
Please upvote this feature to make this our priority. We will prioritize the features every release, based on the user demands.
Regards,
Vinitha