diff --git a/server/AyaNova/Controllers/LogoController.cs b/server/AyaNova/Controllers/LogoController.cs
new file mode 100644
index 00000000..3200ff85
--- /dev/null
+++ b/server/AyaNova/Controllers/LogoController.cs
@@ -0,0 +1,228 @@
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Routing;
+using Microsoft.AspNetCore.Authorization;
+
+using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.Logging;
+
+using AyaNova.Models;
+using AyaNova.Api.ControllerHelpers;
+using AyaNova.Biz;
+using System;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Serialization;
+using Newtonsoft.Json.Linq;
+using System.Linq;
+using AyaNova.Util;
+
+
+
+namespace AyaNova.Api.Controllers
+{
+
+ ///
+ /// Logo controller
+ ///
+ [ApiController]
+ [ApiVersion("8.0")]
+ [Route("api/v{version:apiVersion}/Logo")]
+ [Produces("application/json")]
+ [Authorize]
+ public class LogoController : ControllerBase
+ {
+ private readonly AyContext ct;
+ private readonly ILogger log;
+ private readonly ApiServerState serverState;
+
+
+ ///
+ /// ctor
+ ///
+ ///
+ ///
+ ///
+ public LogoController(AyContext dbcontext, ILogger logger, ApiServerState apiServerState)
+ {
+ ct = dbcontext;
+ log = logger;
+ serverState = apiServerState;
+ }
+
+
+
+
+
+ ///
+ /// Get Logo
+ ///
+ /// One of "small", "medium", "large"
+ /// A single Logo and it's values
+ [AllowAnonymous]
+ [HttpGet("{size}")]
+ public async Task DownloadLogo([FromRoute] string size)
+ {
+ if (serverState.IsClosed)
+ return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
+
+
+ if (!ModelState.IsValid)
+ {
+ return BadRequest(new ApiErrorResponse(ModelState));
+ }
+
+
+
+
+ var o = await ct.Logo.Include(z => z.LogoItems).SingleOrDefaultAsync(z => z.Id == id);
+
+ //turn into correct format and then send as file
+ if (o == null)
+ {
+ return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND));
+ }
+ var asText = Newtonsoft.Json.JsonConvert.SerializeObject(
+ o,
+ Newtonsoft.Json.Formatting.None,
+ new JsonSerializerSettings { ContractResolver = new ShouldSerializeContractResolver(new string[] { "Concurrency", "Id", "LogoId" }) });
+ var bytes = System.Text.Encoding.UTF8.GetBytes(asText);
+ var file = new FileContentResult(bytes, "application/octet-stream");
+ file.FileDownloadName = Util.FileUtil.StringToSafeFileName(o.Name) + ".json";
+ return file;
+ }
+
+
+ public class ShouldSerializeContractResolver : DefaultContractResolver
+ {
+ private readonly IEnumerable _excludePropertyNames;
+
+ public ShouldSerializeContractResolver(IEnumerable excludePropertyNames)
+ {
+ _excludePropertyNames = excludePropertyNames;
+ }
+
+ protected override IList CreateProperties(Type type, MemberSerialization memberSerialization)
+ {
+ IList properties = base.CreateProperties(type, memberSerialization);
+
+ // only serializer properties that start with the specified character
+ properties =
+ properties.Where(p => !_excludePropertyNames.Any(p2 => p2 == p.PropertyName)).ToList();
+
+ return properties;
+ }
+ }
+
+
+
+ ///
+ /// Upload Logo
+ /// Max 15mb total
+ ///
+ /// One of "small", "medium", "large"
+ /// Accepted
+ [Authorize]
+ [HttpPost("{size}")]
+ [DisableFormValueModelBinding]
+ [RequestSizeLimit(15000000)]//currently export file is 200kb * 50 maximum at a time = 15mb https://github.com/aspnet/Announcements/issues/267
+ public async Task UploadAsync([FromRoute] string size)
+ {
+
+ //https://docs.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads?view=aspnetcore-3.1#upload-small-files-with-buffered-model-binding-to-a-database
+
+ if (!serverState.IsOpen)
+ return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
+
+
+ // AyaTypeId attachToObject = null;
+ ApiUploadProcessor.ApiUploadedFilesResult uploadFormData = null;
+ try
+ {
+ if (!MultipartRequestHelper.IsMultipartContentType(Request.ContentType))
+ return BadRequest(new ApiErrorResponse(ApiErrorCode.INVALID_OPERATION, null, $"Expected a multipart request, but got {Request.ContentType}"));
+
+ //Save uploads to disk under temporary file names until we decide how to handle them
+ uploadFormData = await ApiUploadProcessor.ProcessUploadAsync(HttpContext);
+
+ bool badRequest = false;
+ string UploadObjectType = string.Empty;
+ string UploadObjectId = string.Empty;
+ string errorMessage = string.Empty;
+ string Notes = string.Empty;
+ List FileData = new List();
+
+ if (
+ !uploadFormData.FormFieldData.ContainsKey("FileData"))//only filedata is required
+ {
+ badRequest = true;
+ errorMessage = "Missing required FormFieldData value: FileData";
+ }
+ if (!badRequest)
+ {
+ if (uploadFormData.FormFieldData.ContainsKey("ObjectType"))
+ UploadObjectType = uploadFormData.FormFieldData["ObjectType"].ToString();
+ if (uploadFormData.FormFieldData.ContainsKey("ObjectId"))
+ UploadObjectId = uploadFormData.FormFieldData["ObjectId"].ToString();
+ if (uploadFormData.FormFieldData.ContainsKey("Notes"))
+ Notes = uploadFormData.FormFieldData["Notes"].ToString();
+ //fileData in JSON stringify format which contains the actual last modified dates etc
+ //"[{\"name\":\"Client.csv\",\"lastModified\":1582822079618},{\"name\":\"wmi4fu06nrs41.jpg\",\"lastModified\":1586900220990}]"
+ FileData = Newtonsoft.Json.JsonConvert.DeserializeObject>(uploadFormData.FormFieldData["FileData"].ToString());
+
+ }
+
+
+ // long UserId = UserIdFromContext.Id(HttpContext.Items);
+ //Instantiate the business object handler
+ LogoBiz biz = LogoBiz.GetBiz(ct, HttpContext);
+
+
+ //We have our files now can parse and insert into db
+ if (uploadFormData.UploadedFiles.Count > 0)
+ {
+ //deserialize each file and import
+ foreach (UploadedFileInfo a in uploadFormData.UploadedFiles)
+ {
+ JObject o = JObject.Parse(System.IO.File.ReadAllText(a.InitialUploadedPathName));
+ if (!await biz.ImportAsync(o))
+ {
+ //delete all the files temporarily uploaded and return bad request
+ DeleteTempUploadFile(uploadFormData);
+ return BadRequest(new ApiErrorResponse(biz.Errors));
+ }
+ }
+ }
+ }
+ catch (System.IO.InvalidDataException ex)
+ {
+ return BadRequest(new ApiErrorResponse(ApiErrorCode.INVALID_OPERATION, null, ex.Message));
+ }
+ finally
+ {
+ //delete all the files temporarily uploaded and return bad request
+
+ DeleteTempUploadFile(uploadFormData);
+ }
+
+ //Return the list of attachment ids and filenames
+ return Accepted();
+ }
+
+
+ private static void DeleteTempUploadFile(ApiUploadProcessor.ApiUploadedFilesResult uploadFormData)
+ {
+ if (uploadFormData.UploadedFiles.Count > 0)
+ {
+ foreach (UploadedFileInfo a in uploadFormData.UploadedFiles)
+ {
+ System.IO.File.Delete(a.InitialUploadedPathName);
+ }
+ }
+ }
+
+
+
+ }
+}
\ No newline at end of file