using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.StaticFiles;
using Microsoft.Net.Http.Headers;
using Raven.Client.Documents;
using Raven.Client.Documents.Linq;
using System.IO;
namespace zero.Api.Endpoints.Media;
///
/// GET /empty
/// GET /
/// GET /{id}
/// GET /{id}/hierarchy
/// GET /{id}/{size}.tmp
/// POS /
/// PUT /{id}
/// PUT /{id/move/{folderId}
/// DEL /{id}
///
/// GET /folders/empty
/// GET /folders/{id}
/// GET /folders/{id}/children
/// GET /folders/{id}/hierarchy
/// POS /folders/
/// PUT /folders/{id}
/// PUT /folders/{id/move/{folderId}
/// DEL /folders/{id}
///
/// GET /bulk/descendants
/// PUT /bulk/move
/// DEL /bulk/delete
///
public class MediaController : ZeroApiTreeEntityStoreController
{
IMediaManagement Media;
public MediaController(IMediaStore store, IMediaManagement media) : base(store)
{
Media = media;
}
#region bulk operations
[HttpPut("bulk/move")]
[ZeroAuthorize(MediaPermissions.Update)]
public virtual async Task>> BulkMove(MediaBulkMoveOperation operation)
{
List> results = new();
foreach (string id in operation.Ids)
{
Result result = await Store.Move(id, NormalizeParentId(operation.ParentId));
results.Add(result.ConvertTo(id));
}
return results;
}
[HttpDelete("bulk/delete")]
[ZeroAuthorize(MediaPermissions.Update)]
public virtual async Task>> BulkDelete(MediaBulkDeleteOperation operation)
{
List> results = new();
foreach (string id in operation.Ids)
{
Result result = await Store.DeleteWithDescendants(id);
results.Add(result.ConvertTo(id));
}
return results;
}
[HttpGet("bulk/descendants")]
[ZeroAuthorize(MediaPermissions.Update)]
public virtual async Task> GetDescendantStatistics([FromQuery] string[] ids)
{
List> results = new();
Dictionary parents = await Store.Load(ids);
int folderCount = parents.Count(x => x.Value != null && x.Value.IsFolder);
int fileCount = parents.Count(x => x.Value != null && !x.Value.IsFolder);
folderCount += await Store.Session.Query().CountAsync(x => x.Path.ContainsAny(ids) && x.IsFolder);
fileCount += await Store.Session.Query().CountAsync(x => x.Path.ContainsAny(ids) && !x.IsFolder);
return new MediaDescendantStatistics()
{
Files = fileCount,
Folders = folderCount
};
}
#endregion
#region folder operations
[HttpGet("folders/empty")]
[ZeroAuthorize(MediaPermissions.Create)]
public virtual async Task> EmptyFolder(string flavor = null) => await EmptyModel(flavor, x => x.IsFolder = true);
[HttpGet("folders")]
[ZeroAuthorize(MediaPermissions.Read)]
public virtual Task> GetFoldersByQuery([FromQuery] ListQuery query)
{
query.AdditionalQuery = q => q.Where(x => x.IsFolder);
return GetModelsByIndex(query);
}
[HttpGet("folders/{id}")]
[ZeroAuthorize(MediaPermissions.Read)]
public virtual async Task> GetFolder(string id, string changeVector = null) => await GetModel(id, changeVector);
[HttpGet("folders/{id}/children")]
[ZeroAuthorize(MediaPermissions.Read)]
public virtual async Task> GetFolderChildren(string id, [FromQuery] ListQuery query, [FromQuery] bool files = true)
{
id = NormalizeParentId(id);
query.OrderQuery = q => q.OrderByDescending(x => x.IsFolder).ThenByDescending(x => x.CreatedDate);
Paged result = await Store.LoadChildren(id, query.Page, query.PageSize, q => q.WhereIf(x => x.IsFolder, !files).Filter(query));
Paged mappedResult = Mapper.Map(result);
// get children for all folders
string[] folderIds = mappedResult.Items.Where(x => x.IsFolder).Select(x => x.Id).ToArray();
IList children = await Store.Session.Query()
.ProjectInto()
.Where(x => x.Id.In(folderIds))
.ToListAsync();
foreach (MediaBasic item in mappedResult.Items)
{
if (item.IsFolder)
{
zero_Api_Media_ChildCounts.Result childCounts = children.FirstOrDefault(x => x.Id == item.Id);
if (childCounts != null)
{
item.Children = !files ? childCounts.ChildFolderCount : childCounts.ChildCount;
}
}
}
return mappedResult;
}
[HttpGet("folders/{id}/hierarchy")]
[ZeroAuthorize(MediaPermissions.Read)]
public virtual async Task> GetFolderHierarchy(string id)
{
return await Store.GetHierarchy(NormalizeParentId(id));
}
[HttpPost("folders")]
[ZeroAuthorize(MediaPermissions.Create)]
public virtual Task> CreateFolder(zero.Media.Media saveModel)
{
saveModel.IsFolder = true;
return CreateModel(saveModel);
}
[HttpPut("folders/{id}")]
[ZeroAuthorize(MediaPermissions.Update)]
public virtual Task> UpdateFolder(string id, zero.Media.Media updateModel, [FromQuery] string changeToken = null) => UpdateModel(id, updateModel, changeToken);
[HttpPut("folders/{id}/move/{destinationId}")]
[ZeroAuthorize(MediaPermissions.Read)]
public virtual async Task> MoveFolder(string id, string destinationId) => await MoveModel(id, NormalizeParentId(destinationId));
[HttpDelete("folders/{id}")]
[ZeroAuthorize(MediaPermissions.Delete)]
public virtual Task>> DeleteFolder(string id) => DeleteModelWithDescendants(id);
#endregion
#region file operations
[HttpGet("empty")]
[ZeroAuthorize(MediaPermissions.Create)]
public virtual async Task> Empty(string flavor = null) => await EmptyModel(flavor, x => x.IsFolder = false);
[HttpGet("")]
[ZeroAuthorize(MediaPermissions.Read)]
public virtual Task> GetByQuery([FromQuery] ListQuery query)
{
query.AdditionalQuery = q => q.Where(x => !x.IsFolder);
return GetModelsByIndex(query);
}
[HttpGet("{id}")]
[ZeroAuthorize(MediaPermissions.Read)]
public virtual async Task> Get(string id, string changeVector = null) => await GetModel(id, changeVector);
[HttpGet("{id}/hierarchy")]
[ZeroAuthorize(MediaPermissions.Read)]
public virtual async Task> GetHierarchy(string id) => await Store.GetHierarchy(NormalizeParentId(id));
[HttpPost("")]
[ZeroAuthorize(MediaPermissions.Create)]
public virtual async Task> Create([FromForm] IFormFile file, [FromForm] string folderId = null)
{
Result result = await Media.UploadFile(file, folderId);
bool minimalResponse = Hints.ResponsePreference == ApiResponsePreference.Minimal;
if (result.IsSuccess)
{
return Created("/", minimalResponse ? null : result.Model); // TODO correct URL
}
return result.WithoutModel();
}
[HttpPut("{id}")]
[ZeroAuthorize(MediaPermissions.Update)]
public virtual Task> Update(string id, zero.Media.Media updateModel, [FromQuery] string changeToken = null) => UpdateModel(id, updateModel, changeToken);
[HttpPut("{id}/move/{destinationId}")]
[ZeroAuthorize(MediaPermissions.Read)]
public virtual async Task> Move(string id, string destinationId) => await MoveModel(id, NormalizeParentId(destinationId));
[HttpDelete("{id}")]
[ZeroAuthorize(MediaPermissions.Delete)]
public virtual Task> Delete(string id) => DeleteModel(id);
[HttpGet("{id}/{size}.tmp")]
public async Task GetThumbnail(string id, string size)
{
zero.Media.Media media = await Media.GetFile(id);
if (media == null)
{
return NotFound();
}
string path = Media.GetPublicFilePath(media);
if (path == null)
{
return NotFound();
}
if (path.StartsWith("url://"))
{
path = path.Substring(6);
}
FileExtensionContentTypeProvider provider = new();
string contentType;
if (!provider.TryGetContentType(Path.GetFileName(path), out contentType))
{
contentType = "application/octet-stream";
}
try
{
return File(await Media.GetFileStream(media), contentType, DateTimeOffset.Now, EntityTagHeaderValue.Any);
}
catch (FileSystemException)
{
return NotFound();
}
}
#endregion
}