Merge pull request #23 from oqtane/master

sync
This commit is contained in:
Shaun Walker 2020-05-16 08:54:28 -04:00 committed by GitHub
commit 1b2600c6c4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 287 additions and 234 deletions

View File

@ -1,4 +1,5 @@
@namespace Oqtane.Modules.Admin.Files @namespace Oqtane.Modules.Admin.Files
@using System.IO
@inherits ModuleBase @inherits ModuleBase
@inject NavigationManager NavigationManager @inject NavigationManager NavigationManager
@inject IFileService FileService @inject IFileService FileService
@ -12,7 +13,7 @@
<Label For="upload" HelpText="Upload the file you want">Upload: </Label> <Label For="upload" HelpText="Upload the file you want">Upload: </Label>
</td> </td>
<td> <td>
<FileManager UploadMultiple="true" ShowFiles="false" FolderId="@_folderId.ToString()" /> <FileManager UploadMultiple="true" ShowFiles="false" FolderId="@_folderId" />
</td> </td>
</tr> </tr>
</table> </table>
@ -70,19 +71,33 @@
private async Task Download() private async Task Download()
{ {
try if (url == string.Empty || _folderId == -1)
{ {
if (url != string.Empty && _folderId != -1) AddModuleMessage("You Must Enter A Url And Select A Folder", MessageType.Warning);
return;
}
var filename = url.Substring(url.LastIndexOf("/", StringComparison.Ordinal) + 1);
if (!Constants.UploadableFiles.Split(',')
.Contains(Path.GetExtension(filename).ToLower().Replace(".", "")))
{
AddModuleMessage("File Could Not Be Downloaded From Url Due To Its File Extension", MessageType.Warning);
return ;
}
if (!filename.IsPathOrFileValid())
{
AddModuleMessage("You Must Enter A Url With A Valid File Name", MessageType.Warning);
return;
}
try
{ {
await FileService.UploadFileAsync(url, _folderId); await FileService.UploadFileAsync(url, _folderId);
await logger.LogInformation("File Downloaded Successfully From Url {Url}", url); await logger.LogInformation("File Downloaded Successfully From Url {Url}", url);
AddModuleMessage("File Downloaded Successfully From Url", MessageType.Success); AddModuleMessage("File Downloaded Successfully From Url", MessageType.Success);
} }
else
{
AddModuleMessage("You Must Enter A Url And Select A Folder", MessageType.Warning);
}
}
catch (Exception ex) catch (Exception ex)
{ {
await logger.LogError(ex, "Error Downloading File From Url {Url} {Error}", url, ex.Message); await logger.LogError(ex, "Error Downloading File From Url {Url} {Error}", url, ex.Message);

View File

@ -25,7 +25,7 @@
</tr> </tr>
<tr> <tr>
<td> <td>
<Label for="name" HelpText="Enter the file name">Name: </Label> <Label for="name" HelpText="Enter the folder name">Name: </Label>
</td> </td>
<td> <td>
<input id="name" class="form-control" @bind="@_name" /> <input id="name" class="form-control" @bind="@_name" />
@ -112,9 +112,19 @@
private async Task SaveFolder() private async Task SaveFolder()
{ {
try if (_name == string.Empty || _parentId == -1)
{ {
if (_name != string.Empty && _parentId != -1) AddModuleMessage("Folders Must Have A Parent And A Name", MessageType.Warning);
return;
}
if (!_name.IsPathOrFileValid())
{
AddModuleMessage("Folder Name Not Valid.", MessageType.Warning);
return;
}
try
{ {
Folder folder; Folder folder;
if (_folderId != -1) if (_folderId != -1)
@ -149,6 +159,7 @@
{ {
folder = await FolderService.AddFolderAsync(folder); folder = await FolderService.AddFolderAsync(folder);
} }
if (folder != null) if (folder != null)
{ {
await FolderService.UpdateFolderOrderAsync(folder.SiteId, folder.FolderId, folder.ParentId); await FolderService.UpdateFolderOrderAsync(folder.SiteId, folder.FolderId, folder.ParentId);
@ -160,11 +171,6 @@
AddModuleMessage("An Error Was Encountered Saving The Folder", MessageType.Error); AddModuleMessage("An Error Was Encountered Saving The Folder", MessageType.Error);
} }
} }
else
{
AddModuleMessage("Folders Must Have A Parent And A Name", MessageType.Warning);
}
}
catch (Exception ex) catch (Exception ex)
{ {
await logger.LogError(ex, "Error Saving Folder {FolderId} {Error}", _folderId, ex.Message); await logger.LogError(ex, "Error Saving Folder {FolderId} {Error}", _folderId, ex.Message);

View File

@ -35,7 +35,7 @@
<Label HelpText="Upload one or more module packages. Once they are uploaded click Install to complete the installation.">Module: </Label> <Label HelpText="Upload one or more module packages. Once they are uploaded click Install to complete the installation.">Module: </Label>
</td> </td>
<td> <td>
<FileManager Filter="nupkg" ShowFiles="false" Folder="Modules" UploadMultiple="True" /> <FileManager Filter="nupkg" ShowFiles="false" Folder="Modules" UploadMultiple="true" />
</td> </td>
</tr> </tr>
</table> </table>

View File

@ -39,7 +39,7 @@
<Label For="logo" HelpText="Upload a logo for the site">Logo: </Label> <Label For="logo" HelpText="Upload a logo for the site">Logo: </Label>
</td> </td>
<td> <td>
<FileManager FileId="@_logofileid.ToString()" Filter="@Constants.ImageFiles" @ref="_logofilemanager" /> <FileManager FileId="@_logofileid" Filter="@Constants.ImageFiles" @ref="_logofilemanager" />
</td> </td>
</tr> </tr>
<tr> <tr>
@ -47,7 +47,7 @@
<Label For="favicon" HelpText="Select Your default icon">Favicon: </Label> <Label For="favicon" HelpText="Select Your default icon">Favicon: </Label>
</td> </td>
<td> <td>
<FileManager FileId="@_faviconfileid.ToString()" Filter="ico" @ref="_faviconfilemanager" /> <FileManager FileId="@_faviconfileid" Filter="ico" @ref="_faviconfilemanager" />
</td> </td>
</tr> </tr>
<tr> <tr>
@ -185,7 +185,7 @@
<Label For="appIcon" HelpText="Include an application icon for your PWA. It should be a PNG which is 192 X 192 pixels in dimension.">App Icon: </Label> <Label For="appIcon" HelpText="Include an application icon for your PWA. It should be a PNG which is 192 X 192 pixels in dimension.">App Icon: </Label>
</td> </td>
<td> <td>
<FileManager FileId="@_pwaappiconfileid.ToString()" Filter="png" @ref="_pwaappiconfilemanager" /> <FileManager FileId="@_pwaappiconfileid" Filter="png" @ref="_pwaappiconfilemanager" />
</td> </td>
</tr> </tr>
<tr> <tr>
@ -193,7 +193,7 @@
<Label For="splashIcon" HelpText="Include a splash icon for your PWA. It should be a PNG which is 512 X 512 pixels in dimension.">Splash Icon: </Label> <Label For="splashIcon" HelpText="Include a splash icon for your PWA. It should be a PNG which is 512 X 512 pixels in dimension.">Splash Icon: </Label>
</td> </td>
<td> <td>
<FileManager FileId="@_pwasplashiconfileid.ToString()" Filter="png" @ref="_pwasplashiconfilemanager" /> <FileManager FileId="@_pwasplashiconfileid" Filter="png" @ref="_pwasplashiconfilemanager" />
</td> </td>
</tr> </tr>
</table> </table>

View File

@ -8,7 +8,7 @@
<Label For="version" HelpText="Framework Version">Framework Version: </Label> <Label For="version" HelpText="Framework Version">Framework Version: </Label>
</td> </td>
<td> <td>
<input id="version" class="form-control" @bind="@_version" disabled /> <input id="version" class="form-control" @bind="@_version" readonly />
</td> </td>
</tr> </tr>
<tr> <tr>
@ -16,7 +16,7 @@
<Label For="runtime" HelpText="Blazor Runtime (Server or WebAssembly)">Blazor Runtime: </Label> <Label For="runtime" HelpText="Blazor Runtime (Server or WebAssembly)">Blazor Runtime: </Label>
</td> </td>
<td> <td>
<input id="runtime" class="form-control" @bind="@_runtime" disabled /> <input id="runtime" class="form-control" @bind="@_runtime" readonly />
</td> </td>
</tr> </tr>
<tr> <tr>
@ -24,7 +24,7 @@
<Label For="clrversion" HelpText="Common Language Runtime Version">CLR Version: </Label> <Label For="clrversion" HelpText="Common Language Runtime Version">CLR Version: </Label>
</td> </td>
<td> <td>
<input id="clrversion" class="form-control" @bind="@_clrversion" disabled /> <input id="clrversion" class="form-control" @bind="@_clrversion" readonly />
</td> </td>
</tr> </tr>
<tr> <tr>
@ -32,7 +32,7 @@
<Label For="osversion" HelpText="Operating System Version">OS Version: </Label> <Label For="osversion" HelpText="Operating System Version">OS Version: </Label>
</td> </td>
<td> <td>
<input id="osversion" class="form-control" @bind="@_osversion" disabled /> <input id="osversion" class="form-control" @bind="@_osversion" readonly />
</td> </td>
</tr> </tr>
<tr> <tr>
@ -40,7 +40,7 @@
<Label For="serverpath" HelpText="Server Path">Server Path: </Label> <Label For="serverpath" HelpText="Server Path">Server Path: </Label>
</td> </td>
<td> <td>
<input id="serverpath" class="form-control" @bind="@_serverpath" disabled /> <input id="serverpath" class="form-control" @bind="@_serverpath" readonly />
</td> </td>
</tr> </tr>
<tr> <tr>
@ -48,7 +48,7 @@
<Label For="servertime" HelpText="Server Time">Server Time: </Label> <Label For="servertime" HelpText="Server Time">Server Time: </Label>
</td> </td>
<td> <td>
<input id="servertime" class="form-control" @bind="@_servertime" disabled /> <input id="servertime" class="form-control" @bind="@_servertime" readonly />
</td> </td>
</tr> </tr>
</table> </table>

View File

@ -35,7 +35,7 @@
<Label HelpText="Upload one or more theme packages. Once they are uploaded click Install to complete the installation.">Theme: </Label> <Label HelpText="Upload one or more theme packages. Once they are uploaded click Install to complete the installation.">Theme: </Label>
</td> </td>
<td> <td>
<FileManager Filter="nupkg" ShowFiles="false" Folder="Themes" UploadMultiple="True" /> <FileManager Filter="nupkg" ShowFiles="false" Folder="Themes" UploadMultiple="@true" />
</td> </td>
</tr> </tr>
</table> </table>

View File

@ -64,7 +64,7 @@ else
<label for="Name" class="control-label">Photo: </label> <label for="Name" class="control-label">Photo: </label>
</td> </td>
<td> <td>
<FileManager FileId="@photofileid.ToString()" @ref="filemanager" /> <FileManager FileId="@photofileid" @ref="filemanager" />
</td> </td>
</tr> </tr>
</table> </table>

View File

@ -63,7 +63,7 @@ else
<label class="control-label">Photo: </label> <label class="control-label">Photo: </label>
</td> </td>
<td> <td>
<FileManager FileId="@photofileid.ToString()" @ref="filemanager" /> <FileManager FileId="@photofileid" @ref="filemanager" />
</td> </td>
</tr> </tr>
<tr> <tr>

View File

@ -1,5 +1,6 @@
@namespace Oqtane.Modules.Controls @namespace Oqtane.Modules.Controls
@inherits ModuleBase @inherits ModuleBase
@attribute [OqtaneIgnore] @attribute [OqtaneIgnore]
@inject IFolderService FolderService @inject IFolderService FolderService
@inject IFileService FileService @inject IFileService FileService
@ -10,6 +11,8 @@
<div id="@Id" class="container-fluid px-0"> <div id="@Id" class="container-fluid px-0">
<div class="row"> <div class="row">
<div class="col"> <div class="col">
@if (ShowFolders || FolderId <= 0)
{
<div> <div>
<select class="form-control" @onchange="(e => FolderChanged(e))"> <select class="form-control" @onchange="(e => FolderChanged(e))">
@if (string.IsNullOrEmpty(Folder)) @if (string.IsNullOrEmpty(Folder))
@ -18,7 +21,7 @@
} }
@foreach (Folder folder in _folders) @foreach (Folder folder in _folders)
{ {
if (folder.FolderId == _folderid) if (folder.FolderId == FolderId)
{ {
<option value="@(folder.FolderId)" selected>@(new string('-', folder.Level * 2))@(folder.Name)</option> <option value="@(folder.FolderId)" selected>@(new string('-', folder.Level * 2))@(folder.Name)</option>
} }
@ -29,14 +32,15 @@
} }
</select> </select>
</div> </div>
@if (_showfiles) }
@if (ShowFiles)
{ {
<div> <div>
<select class="form-control" @onchange="(e => FileChanged(e))"> <select class="form-control" @onchange="(e => FileChanged(e))">
<option value="-1">&lt;Select File&gt;</option> <option value="-1">&lt;Select File&gt;</option>
@foreach (File file in _files) @foreach (File file in _files)
{ {
if (file.FileId == _fileid) if (file.FileId == FileId)
{ {
<option value="@(file.FileId)" selected>@(file.Name)</option> <option value="@(file.FileId)" selected>@(file.Name)</option>
} }
@ -48,10 +52,10 @@
</select> </select>
</div> </div>
} }
@if (_haseditpermission) @if (ShowUpload && _haseditpermission)
{ {
<div> <div>
@if (_uploadmultiple) @if (UploadMultiple)
{ {
<input type="file" id="@_fileinputid" name="file" accept="@_filter" multiple/> <input type="file" id="@_fileinputid" name="file" accept="@_filter" multiple/>
} }
@ -68,8 +72,8 @@
} }
</span> </span>
</div> </div>
@((MarkupString)_message)
} }
@((MarkupString) _message)
</div> </div>
@if (_image != string.Empty) @if (_image != string.Empty)
{ {
@ -84,19 +88,19 @@
@code { @code {
private string _id; private string _id;
private List<Folder> _folders; private List<Folder> _folders;
private int _folderid = -1;
private List<File> _files = new List<File>(); private List<File> _files = new List<File>();
private int _fileid = -1;
private bool _showfiles = true; private bool _showfiles = true;
private string _fileinputid = string.Empty; private string _fileinputid = string.Empty;
private string _progressinfoid = string.Empty; private string _progressinfoid = string.Empty;
private string _progressbarid = string.Empty; private string _progressbarid = string.Empty;
private string _filter = "*"; private string _filter = "*";
private bool _uploadmultiple = false;
private bool _haseditpermission = false; private bool _haseditpermission = false;
private string _message = string.Empty; private string _message = string.Empty;
private string _image = string.Empty; private string _image = string.Empty;
private string _guid; private string _guid;
private int _folderId = -1;
private bool _uploadMultiple;
private int _fileId;
[Parameter] [Parameter]
public string Id { get; set; } // optional - for setting the id of the FileManager component for accessibility public string Id { get; set; } // optional - for setting the id of the FileManager component for accessibility
@ -105,19 +109,25 @@
public string Folder { get; set; } // optional - for setting a specific folder by default public string Folder { get; set; } // optional - for setting a specific folder by default
[Parameter] [Parameter]
public string FolderId { get; set; } // optional - for setting a specific folderid by default public int FolderId { get; set; } = -1; // optional - for setting a specific folderid by default
[Parameter] [Parameter]
public string ShowFiles { get; set; } // optional - for indicating whether a list of files should be displayed - default is true public bool ShowFiles { get; set; } = true; // optional - for indicating whether a list of files should be displayed - default is true
[Parameter] [Parameter]
public string FileId { get; set; } // optional - for setting a specific file by default public bool ShowUpload { get; set; } = true; // optional - for indicating whether a Upload controls should be displayed - default is true
[Parameter]
public bool ShowFolders { get; set; } = true; // optional - for indicating whether a list of folders should be displayed - default is true
[Parameter]
public int FileId { get; set; } = -1; // optional - for setting a specific file by default
[Parameter] [Parameter]
public string Filter { get; set; } // optional - comma delimited list of file types that can be selected or uploaded ie. "jpg,gif" public string Filter { get; set; } // optional - comma delimited list of file types that can be selected or uploaded ie. "jpg,gif"
[Parameter] [Parameter]
public string UploadMultiple { get; set; } // optional - enable multiple file uploads - default false public bool UploadMultiple { get; set; } = false; // optional - enable multiple file uploads - default false
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
@ -129,38 +139,26 @@
if (!string.IsNullOrEmpty(Folder)) if (!string.IsNullOrEmpty(Folder))
{ {
_folders = new List<Folder> {new Folder {FolderId = -1, Name = Folder}}; _folders = new List<Folder> {new Folder {FolderId = -1, Name = Folder}};
_folderid = -1; FolderId = -1;
} }
else else
{ {
_folders = await FolderService.GetFoldersAsync(ModuleState.SiteId); _folders = await FolderService.GetFoldersAsync(ModuleState.SiteId);
if (!string.IsNullOrEmpty(FolderId))
{
_folderid = int.Parse(FolderId);
}
} }
if (!string.IsNullOrEmpty(FileId)) if (FileId != -1)
{ {
_fileid = int.Parse(FileId); File file = await FileService.GetFileAsync(FileId);
if (_fileid != -1)
{
File file = await FileService.GetFileAsync(int.Parse(FileId));
if (file != null) if (file != null)
{ {
_folderid = file.FolderId; FolderId = file.FolderId;
} }
else else
{ {
_fileid = -1; // file does not exist FileId = -1; // file does not exist
} }
} }
await SetImage(); await SetImage();
}
if (!string.IsNullOrEmpty(ShowFiles))
{
_showfiles = bool.Parse(ShowFiles);
}
if (!string.IsNullOrEmpty(Filter)) if (!string.IsNullOrEmpty(Filter))
{ {
@ -174,11 +172,6 @@
_fileinputid = _guid + "FileInput"; _fileinputid = _guid + "FileInput";
_progressinfoid = _guid + "ProgressInfo"; _progressinfoid = _guid + "ProgressInfo";
_progressbarid = _guid + "ProgressBar"; _progressbarid = _guid + "ProgressBar";
if (!string.IsNullOrEmpty(UploadMultiple))
{
_uploadmultiple = bool.Parse(UploadMultiple);
}
} }
private async Task GetFiles() private async Task GetFiles()
@ -191,11 +184,11 @@
} }
else else
{ {
Folder folder = _folders.FirstOrDefault(item => item.FolderId == _folderid); Folder folder = _folders.FirstOrDefault(item => item.FolderId == FolderId);
if (folder != null) if (folder != null)
{ {
_haseditpermission = UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, folder.Permissions); _haseditpermission = UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, folder.Permissions);
_files = await FileService.GetFilesAsync(_folderid); _files = await FileService.GetFilesAsync(FolderId);
} }
else else
{ {
@ -222,9 +215,9 @@
_message = string.Empty; _message = string.Empty;
try try
{ {
_folderid = int.Parse((string)e.Value); FolderId = int.Parse((string) e.Value);
await GetFiles(); await GetFiles();
_fileid = -1; FileId = -1;
_image = string.Empty; _image = string.Empty;
StateHasChanged(); StateHasChanged();
} }
@ -238,7 +231,7 @@
private async Task FileChanged(ChangeEventArgs e) private async Task FileChanged(ChangeEventArgs e)
{ {
_message = string.Empty; _message = string.Empty;
_fileid = int.Parse((string)e.Value); FileId = int.Parse((string) e.Value);
await SetImage(); await SetImage();
StateHasChanged(); StateHasChanged();
@ -247,9 +240,9 @@
private async Task SetImage() private async Task SetImage()
{ {
_image = string.Empty; _image = string.Empty;
if (_fileid != -1) if (FileId != -1)
{ {
File file = await FileService.GetFileAsync(_fileid); File file = await FileService.GetFileAsync(FileId);
if (file != null && file.ImageHeight != 0 && file.ImageWidth != 0) if (file != null && file.ImageHeight != 0 && file.ImageWidth != 0)
{ {
var maxwidth = 200; var maxwidth = 200;
@ -259,7 +252,7 @@
var ratioY = (double) maxheight / (double) file.ImageHeight; var ratioY = (double) maxheight / (double) file.ImageHeight;
var ratio = ratioX < ratioY ? ratioX : ratioY; var ratio = ratioX < ratioY ? ratioX : ratioY;
_image = "<img src=\"" + ContentUrl(_fileid) + "\" alt=\"" + file.Name + _image = "<img src=\"" + ContentUrl(FileId) + "\" alt=\"" + file.Name +
"\" width=\"" + Convert.ToInt32(file.ImageWidth * ratio).ToString() + "\" width=\"" + Convert.ToInt32(file.ImageWidth * ratio).ToString() +
"\" height=\"" + Convert.ToInt32(file.ImageHeight * ratio).ToString() + "\" />"; "\" height=\"" + Convert.ToInt32(file.ImageHeight * ratio).ToString() + "\" />";
} }
@ -281,7 +274,7 @@
} }
else else
{ {
result = await FileService.UploadFilesAsync(_folderid, upload, _guid); result = await FileService.UploadFilesAsync(FolderId, upload, _guid);
} }
if (result == string.Empty) if (result == string.Empty)
@ -295,7 +288,7 @@
var file = _files.Where(item => item.Name == upload[0]).FirstOrDefault(); var file = _files.Where(item => item.Name == upload[0]).FirstOrDefault();
if (file != null) if (file != null)
{ {
_fileid = file.FileId; FileId = file.FileId;
await SetImage(); await SetImage();
} }
} }
@ -325,21 +318,21 @@
try try
{ {
await FileService.DeleteFileAsync(_fileid); await FileService.DeleteFileAsync(FileId);
await logger.LogInformation("File Deleted {File}", _fileid); await logger.LogInformation("File Deleted {File}", FileId);
_message = "<br /><div class=\"alert alert-success\" role=\"alert\">File Deleted</div>"; _message = "<br /><div class=\"alert alert-success\" role=\"alert\">File Deleted</div>";
await GetFiles(); await GetFiles();
_fileid = -1; FileId = -1;
await SetImage(); await SetImage();
StateHasChanged(); StateHasChanged();
} }
catch (Exception ex) catch (Exception ex)
{ {
await logger.LogError(ex, "Error Deleting File {File} {Error}", _fileid, ex.Message); await logger.LogError(ex, "Error Deleting File {File} {Error}", FileId, ex.Message);
_message = "<br /><div class=\"alert alert-danger\" role=\"alert\">Error Deleting File</div>"; _message = "<br /><div class=\"alert alert-danger\" role=\"alert\">Error Deleting File</div>";
} }
} }
public int GetFileId() => _fileid; public int GetFileId() => FileId;
} }

View File

@ -1,4 +1,5 @@
using Oqtane.Models; using System;
using Oqtane.Models;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Net.Http; using System.Net.Http;
using System.Linq; using System.Linq;
@ -106,7 +107,7 @@ namespace Oqtane.Services
foreach (KeyValuePair<string, string> kvp in settings) foreach (KeyValuePair<string, string> kvp in settings)
{ {
Setting setting = settingsList.FirstOrDefault(item => item.SettingName == kvp.Key); Setting setting = settingsList.FirstOrDefault(item => item.SettingName.Equals(kvp.Key,StringComparison.OrdinalIgnoreCase));
if (setting == null) if (setting == null)
{ {
setting = new Setting(); setting = new Setting();

View File

@ -32,7 +32,7 @@
} }
else else
{ {
_paneadminborder = ""; _paneadminborder = "container";
_panetitle = ""; _panetitle = "";
} }

View File

@ -189,14 +189,37 @@ namespace Oqtane.Controllers
{ {
Models.File file = null; Models.File file = null;
Folder folder = _folders.GetFolder(int.Parse(folderid)); Folder folder = _folders.GetFolder(int.Parse(folderid));
if (folder != null && _userPermissions.IsAuthorized(User, PermissionNames.Edit, folder.Permissions))
if (folder == null || !_userPermissions.IsAuthorized(User, PermissionNames.Edit, folder.Permissions))
{ {
_logger.Log(LogLevel.Error, this, LogFunction.Create,
"User Not Authorized To Download File {Url} {FolderId}", url, folderid);
HttpContext.Response.StatusCode = 401;
return file;
}
string folderPath = GetFolderPath(folder); string folderPath = GetFolderPath(folder);
CreateDirectory(folderPath); CreateDirectory(folderPath);
string filename = url.Substring(url.LastIndexOf("/", StringComparison.Ordinal) + 1); string filename = url.Substring(url.LastIndexOf("/", StringComparison.Ordinal) + 1);
// check for allowable file extensions // check for allowable file extensions
if (Constants.UploadableFiles.Split(',').Contains(Path.GetExtension(filename).ToLower().Replace(".", ""))) if (!Constants.UploadableFiles.Split(',')
.Contains(Path.GetExtension(filename).ToLower().Replace(".", "")))
{ {
_logger.Log(LogLevel.Error, this, LogFunction.Create,
"File Could Not Be Downloaded From Url Due To Its File Extension {Url}", url);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Conflict;
return file;
}
if (!filename.IsPathOrFileValid())
{
_logger.Log(LogLevel.Error, this, LogFunction.Create,
$"File Could Not Be Downloaded From Url Due To Its File Name Not Allowed {url}");
HttpContext.Response.StatusCode = (int)HttpStatusCode.Conflict;
return file;
}
try try
{ {
var client = new WebClient(); var client = new WebClient();
@ -208,22 +231,12 @@ namespace Oqtane.Controllers
} }
client.DownloadFile(url, targetPath); client.DownloadFile(url, targetPath);
_files.AddFile(CreateFile(filename, folder.FolderId, targetPath)); file = _files.AddFile(CreateFile(filename, folder.FolderId, targetPath));
} }
catch catch
{ {
_logger.Log(LogLevel.Error, this, LogFunction.Create, "File Could Not Be Downloaded From Url {Url}", url); _logger.Log(LogLevel.Error, this, LogFunction.Create,
} "File Could Not Be Downloaded From Url {Url}", url);
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Create, "File Could Not Be Downloaded From Url Due To Its File Extension {Url}", url);
}
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Create, "User Not Authorized To Download File {Url} {FolderId}", url, folderid);
HttpContext.Response.StatusCode = 401;
} }
return file; return file;
@ -233,14 +246,24 @@ namespace Oqtane.Controllers
[HttpPost("upload")] [HttpPost("upload")]
public async Task UploadFile(string folder, IFormFile file) public async Task UploadFile(string folder, IFormFile file)
{ {
if (file.Length > 0) if (file.Length <= 0)
{ {
return;
}
if (!file.FileName.IsPathOrFileValid())
{
HttpContext.Response.StatusCode = (int)HttpStatusCode.Conflict;
return;
}
string folderPath = ""; string folderPath = "";
if (int.TryParse(folder, out int folderId)) if (int.TryParse(folder, out int folderId))
{ {
Folder virtualFolder = _folders.GetFolder(folderId); Folder virtualFolder = _folders.GetFolder(folderId);
if (virtualFolder != null && _userPermissions.IsAuthorized(User, PermissionNames.Edit, virtualFolder.Permissions)) if (virtualFolder != null &&
_userPermissions.IsAuthorized(User, PermissionNames.Edit, virtualFolder.Permissions))
{ {
folderPath = GetFolderPath(virtualFolder); folderPath = GetFolderPath(virtualFolder);
} }
@ -269,11 +292,11 @@ namespace Oqtane.Controllers
} }
else else
{ {
_logger.Log(LogLevel.Error, this, LogFunction.Create, "User Not Authorized To Upload File {Folder} {File}", folder, file); _logger.Log(LogLevel.Error, this, LogFunction.Create,
"User Not Authorized To Upload File {Folder} {File}", folder, file);
HttpContext.Response.StatusCode = 401; HttpContext.Response.StatusCode = 401;
} }
} }
}
private async Task<string> MergeFile(string folder, string filename) private async Task<string> MergeFile(string folder, string filename)
{ {
@ -413,10 +436,13 @@ namespace Oqtane.Controllers
{ {
_logger.Log(LogLevel.Error, this, LogFunction.Read, "File Does Not Exist {FileId} {FilePath}", id, filepath); _logger.Log(LogLevel.Error, this, LogFunction.Read, "File Does Not Exist {FileId} {FilePath}", id, filepath);
HttpContext.Response.StatusCode = 404; HttpContext.Response.StatusCode = 404;
if (System.IO.File.Exists(errorpath))
{
byte[] filebytes = System.IO.File.ReadAllBytes(errorpath); byte[] filebytes = System.IO.File.ReadAllBytes(errorpath);
return File(filebytes, "application/octet-stream", file.Name); return File(filebytes, "application/octet-stream", file.Name);
} }
} }
}
else else
{ {
_logger.Log(LogLevel.Error, this, LogFunction.Read, "User Not Authorized To Access File {FileId}", id); _logger.Log(LogLevel.Error, this, LogFunction.Read, "User Not Authorized To Access File {FileId}", id);
@ -432,6 +458,7 @@ namespace Oqtane.Controllers
byte[] filebytes = System.IO.File.ReadAllBytes(errorpath); byte[] filebytes = System.IO.File.ReadAllBytes(errorpath);
return File(filebytes, "application/octet-stream", "error.png"); return File(filebytes, "application/octet-stream", "error.png");
} }
return null;
} }
private string GetFolderPath(Folder folder) private string GetFolderPath(Folder folder)

View File

@ -105,7 +105,7 @@ namespace Oqtane.Controllers
} }
if (_userPermissions.IsAuthorized(User, PermissionNames.Edit, permissions)) if (_userPermissions.IsAuthorized(User, PermissionNames.Edit, permissions))
{ {
if (FolderPathValid(folder)) if (folder.IsPathValid())
{ {
if (string.IsNullOrEmpty(folder.Path) && folder.ParentId != null) if (string.IsNullOrEmpty(folder.Path) && folder.ParentId != null)
{ {
@ -140,7 +140,7 @@ namespace Oqtane.Controllers
{ {
if (ModelState.IsValid && _userPermissions.IsAuthorized(User, EntityNames.Folder, folder.FolderId, PermissionNames.Edit)) if (ModelState.IsValid && _userPermissions.IsAuthorized(User, EntityNames.Folder, folder.FolderId, PermissionNames.Edit))
{ {
if (FolderPathValid(folder)) if (folder.IsPathValid())
{ {
if (string.IsNullOrEmpty(folder.Path) && folder.ParentId != null) if (string.IsNullOrEmpty(folder.Path) && folder.ParentId != null)
{ {
@ -210,13 +210,5 @@ namespace Oqtane.Controllers
HttpContext.Response.StatusCode = 401; HttpContext.Response.StatusCode = 401;
} }
} }
private bool FolderPathValid(Folder folder)
{
// prevent folder path traversal and reserved devices
return (folder.Name.IndexOfAny(Constants.InvalidFileNameChars) == -1 &&
!Constants.InvalidFileNameEndingChars.Any(x => folder.Name.EndsWith(x)) &&
!Constants.ReservedDevices.Split(',').Contains(folder.Name.ToUpper().Split('.')[0]));
}
} }
} }

View File

@ -2,8 +2,10 @@
using System; using System;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Linq;
using System.Text; using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using File = Oqtane.Models.File;
namespace Oqtane.Shared namespace Oqtane.Shared
{ {
@ -254,5 +256,22 @@ namespace Oqtane.Shared
return Path.Combine(segments).TrimEnd(); return Path.Combine(segments).TrimEnd();
} }
public static bool IsPathValid(this Folder folder)
{
return IsPathOrFileValid(folder.Name);
}
public static bool IsFileValid(this File file)
{
return IsPathOrFileValid(file.Name);
}
public static bool IsPathOrFileValid(this string name)
{
return (name.IndexOfAny(Constants.InvalidFileNameChars) == -1 &&
!Constants.InvalidFileNameEndingChars.Any(name.EndsWith) &&
!Constants.ReservedDevices.Split(',').Contains(name.ToUpper().Split('.')[0]));
}
} }
} }