using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Authorization; using Microsoft.AspNetCore.Components.Forms; using Microsoft.AspNetCore.Identity; using Microsoft.JSInterop; using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.IO; using System.Reflection; using System.Threading.Tasks; using System.Linq; using System.Net; using System.Security.Cryptography; using HyperCube.Models; using Console = HyperCube.Utils.AdvConsole; namespace HyperCube.Pages { public partial class DocEdit : ComponentBase { [Parameter] public int DocID { get; set; } [Inject] public NavigationManager NavigationManager { get; set; } [Inject] public IJSRuntime JsRuntime { get; set; } [Inject] public AuthenticationStateProvider AuthenticationStateProvider { get; set; } [Inject] public UserManager UserManager { get; set; } const string FOLDER_NAME = "articles_storage"; const long MAX_FILE_SIZE = 5120000; //bytes string transactionId; ArticleModel articleClone = new(); ArticleModel article = new(); AccountModel currentAcc = new(); AccountModel initiatorAcc = new(); string status; string header; string storageFolderPath; MemoryStream memoryStream; ModalInfo modalInfo_error { get; set; } ModalInfo modalInfo_transac { get; set; } ModalLoading modalLoading { get; set; } string fullName { get { return FOLDER_NAME + "/" + article.FilenameReal; } } int editsCount; bool IsSubmitDisabled = false; protected override async Task OnInitializedAsync() { currentAcc = await GetCurrentAcc(); string path = AppDomain.CurrentDomain.BaseDirectory+@"wwwroot"; storageFolderPath = (Path.Combine(path, FOLDER_NAME)); Console.WriteLine("docedit OnInitializedAsync storageFolderPath2 " + storageFolderPath); if (DocID > 0) { header = "Проверка материала"; MySQLConnector dbCon = MySQLConnector.Instance(); articleClone = await dbCon.SQLSelectArticle(DocID); article = (ArticleModel)articleClone.Clone(); string initiator = await article.GetInitiatorUUID(ArticleStatus.Saved); initiatorAcc = AccountModel.Find(initiator); status = $"Article ID={DocID} loaded, status: {article.Status}, initiator: {initiatorAcc.Name}"; } else header = "Загрузка материала"; //await InitializeAccount(); //int count = await articleModel.GetEditsCount(); //int countbyid = await articleModel.GetEditsCount(currentAcc.UUID); //header += $", uuid:{currentAcc.UUID}, name: {currentAcc.Name}, edits count:{count}, count by accid: {countbyid}"; } public async Task NewProjectSmopp(long articleId) { WebRequest wrGETURL; wrGETURL = WebRequest.Create("http://dev.prmsys.net/makeproject.php?pt=232&aid="+ articleId); var response = await wrGETURL.GetResponseAsync(); Stream dataStream = response.GetResponseStream(); StreamReader reader = new(dataStream); return reader.ReadToEnd(); } private async Task HandleSubmit() { Console.WriteLine($"HandleSubmit, docID: {DocID}."); modalLoading.Open(); /// form validation List errorFields = ValidateForm(article); if (errorFields.Count > 0) { modalLoading.Close(); string invalid_fields = string.Join(", ", errorFields); modalInfo_error.Open($"Не заполнены поля: {invalid_fields}"); Console.WriteLine($"HandleSubmit. Required fields: '{invalid_fields}' is not filled."); return; } /// all is fine, continue MySQLConnector dbCon = MySQLConnector.Instance(); long id; string stringSQL; if (DocID > 0) { id = DocID; stringSQL = $"UPDATE articles " + $"SET filename='{article.Filename}', article_name='{article.Name}', authors='{article.Authors}', " + $"date_publish='{article.PublishDate:yyyy-MM-dd}', annotation='{article.Annotation}', " + $"keywords='{article.Keywords}', rating={article.Rating}, file_hash='{article.HashSum}' " + $"WHERE id={DocID}"; await dbCon.SQLInsert(stringSQL); } else { stringSQL = $"INSERT INTO articles (filename, article_name, authors, date_publish, annotation, keywords, file_hash) " + $"VALUES ('{article.Filename}', '{article.Name}', '{article.Authors}', '{article.PublishDate:yyyy-MM-dd}'," + $"'{article.Annotation}', '{article.Keywords}', '{article.HashSum}')"; id = await dbCon.SQLInsert(stringSQL); NewProjectSmopp(id); } /// tmp int action_type = DocID > 0 ? 2 : 1; stringSQL = $"INSERT INTO actions_history (article_id, action_type, acc_id) " + $"VALUES ('{id}', '{action_type}', '{currentAcc.UUID}')"; await dbCon.SQLInsert(stringSQL); Dictionary propDict = Compare.SimpleCompare(article, articleClone); foreach (KeyValuePair prop in propDict) { //Console.WriteLine($"property name: {prop.Key}, value: {prop.Value.GetValue(articleModel, null)}"); stringSQL = $"INSERT INTO articles_edit_log (article_id, acc_id, field_name, field_prevvalue, field_newvalue) " + $"VALUES ('{id}', '{currentAcc.UUID}', '{prop.Key}', '{prop.Value.GetValue(articleClone, null)}', '{prop.Value.GetValue(article, null)}')"; await dbCon.SQLInsert(stringSQL); } //dbCon.Close(); if (DocID > 0) { status = propDict.Count > 0 ? "All changes saved, article has veryfied." : "Article verifyed without any changes."; //transactionId = await Verify(); Console.WriteLine("transactionId found " + transactionId); ///tmp editsCount = await article.GetEditsCount(currentAcc.UUID); modalInfo_transac.Open(""); } else { string fullpath = Path.Combine(storageFolderPath, $"{id}_{article.Filename}"); Directory.CreateDirectory(storageFolderPath); FileStream fs = new(fullpath, FileMode.Create, FileAccess.Write); memoryStream.Position = 0; await memoryStream.CopyToAsync(fs); Console.WriteLine($"User has saved new article data, {id}_{article.Filename}, memory size:{memoryStream.Length}b, file size: {fs.Length}b"); memoryStream.Close(); fs.Close(); status = "New article data saved."; bool confirmed = await JsRuntime.InvokeAsync("confirm", "Хотите загрузить еще статью?"); if (confirmed) NavigationManager.NavigateTo("docedit", true); else NavigationManager.NavigateTo(""); } /// reloading articles await AppData.LoadArticles(); } private async Task HandleSelection(InputFileChangeEventArgs e) { modalLoading.Open(); IBrowserFile file = e.File; if (file != null) { Stream stream = file.OpenReadStream(MAX_FILE_SIZE); memoryStream = new(); await stream.CopyToAsync(memoryStream); status = $"Finished loading {memoryStream.Length} bytes from {file.Name}"; Console.WriteLine(status); /// calculating hash string hash = await CalculateHashSum(memoryStream); Console.WriteLine($"Hash: {hash}"); /// checking hash MySQLConnector dbCon = MySQLConnector.Instance(); string stringSQL; stringSQL = $"SELECT COUNT(*) FROM articles WHERE file_hash='{hash}'"; int count = await dbCon.SQLSelectCount(stringSQL); //Console.WriteLine($"File duplicate check, founded files: {count}"); if (count < 1) { //articleClone = DocParse.GetBaseProperties(memoryStream); articleClone = DocParse.GetBaseProperties(""); articleClone.Filename = file.Name; articleClone.HashSum = hash; article = (ArticleModel)articleClone.Clone(); IsSubmitDisabled = false; } else { status = $"File duplicate founded, hash: {hash}."; Console.WriteLine(status); IsSubmitDisabled = true; memoryStream.Close(); stream.Close(); modalInfo_error.Open("Загрузка не удалась, такой документ уже есть в системе."); } } modalLoading.Close(); } private async Task Cancel() { bool confirmed = await JsRuntime.InvokeAsync("confirm", "Вы уверены, что хотите отклонить статью?"); if (confirmed) { ///какая-то логика отмены... NavigationManager.NavigateTo(""); } } //public async Task InitializeAccount() //{ // AccountModel.Current = await GetCurrentAcc(); // Console.WriteLine("InitializeAccount in DocEdit " + AccountModel.Current.Name); //} private async Task GetCurrentAcc() { AccountModel account = new(); var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync(); var user = authState.User; if (user.Identity.IsAuthenticated) { var currentUser = await UserManager.GetUserAsync(user); account.UUID = currentUser.Id; //account.Name = currentUser.UserName; //account.Email = currentUser.Email; var acc = AccountModel.Find(account.UUID); if (acc != null) account = acc; ///tmp //account.AccRole = Role.User; return account; } return null; } private async Task CalculateHashSum(MemoryStream ms) { MD5CryptoServiceProvider md5Provider = new(); ms.Position = 0; byte[] hash = await md5Provider.ComputeHashAsync(ms); return Convert.ToBase64String(hash); } private List ValidateForm(T obj) { var props = typeof(T).GetProperties().Where(pi => Attribute.IsDefined(pi, typeof(RequiredAttribute))); List result = new(); foreach (var prop in props) { var val = prop.GetValue(obj, null); if (val == null || val?.ToString().Length == 0) result.Add(prop.Name); //Console.WriteLine($"Required field '{prop.Name}' is not filled."); } return result; } } }