DocEdit.razor.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. using Microsoft.AspNetCore.Components;
  2. using Microsoft.AspNetCore.Components.Authorization;
  3. using Microsoft.AspNetCore.Components.Forms;
  4. using Microsoft.AspNetCore.Identity;
  5. using Microsoft.JSInterop;
  6. using System;
  7. using System.Collections.Generic;
  8. using System.ComponentModel.DataAnnotations;
  9. using System.IO;
  10. using System.Reflection;
  11. using System.Threading.Tasks;
  12. using System.Linq;
  13. using System.Net;
  14. using System.Security.Cryptography;
  15. using HyperCube.Models;
  16. using Console = HyperCube.Utils.AdvConsole;
  17. namespace HyperCube.Pages
  18. {
  19. public partial class DocEdit : ComponentBase
  20. {
  21. [Parameter]
  22. public int DocID { get; set; }
  23. [Inject]
  24. public NavigationManager NavigationManager { get; set; }
  25. [Inject]
  26. public IJSRuntime JsRuntime { get; set; }
  27. [Inject]
  28. public AuthenticationStateProvider AuthenticationStateProvider { get; set; }
  29. [Inject]
  30. public UserManager<IdentityUser> UserManager { get; set; }
  31. const string FOLDER_NAME = "articles_storage";
  32. const long MAX_FILE_SIZE = 5120000; //bytes
  33. string transactionId;
  34. ArticleModel articleClone = new();
  35. ArticleModel article = new();
  36. AccountModel currentAcc = new();
  37. AccountModel initiatorAcc = new();
  38. string status;
  39. string header;
  40. string storageFolderPath;
  41. MemoryStream memoryStream;
  42. ModalInfo modalInfo_error { get; set; }
  43. ModalInfo modalInfo_transac { get; set; }
  44. ModalLoading modalLoading { get; set; }
  45. string fullName { get { return FOLDER_NAME + "/" + article.FilenameReal; } }
  46. int editsCount;
  47. bool IsSubmitDisabled = false;
  48. protected override async Task OnInitializedAsync()
  49. {
  50. currentAcc = await GetCurrentAcc();
  51. string path = AppDomain.CurrentDomain.BaseDirectory+@"wwwroot";
  52. storageFolderPath = (Path.Combine(path, FOLDER_NAME));
  53. Console.WriteLine("docedit OnInitializedAsync storageFolderPath2 " + storageFolderPath);
  54. if (DocID > 0)
  55. {
  56. header = "Проверка материала";
  57. MySQLConnector dbCon = MySQLConnector.Instance();
  58. string stringSQL = $"SELECT articles.id, filename, article_name, authors, date_publish, annotation, keywords, action_type, rating " +
  59. $"FROM articles " +
  60. $"JOIN actions_history ON actions_history.article_id = articles.id " +
  61. $"WHERE articles.id={DocID} " +
  62. $"ORDER BY actions_history.id DESC LiMIT 1";
  63. articleClone = await dbCon.SQLSelectArticle(stringSQL);
  64. article = (ArticleModel)articleClone.Clone();
  65. string initiator = await article.GetInitiatorUUID(ArticleStatus.Saved);
  66. initiatorAcc = AccountModel.Find(initiator);
  67. status = $"Article ID={DocID} loaded, status: {article.Status}, initiator: {initiatorAcc.Name}";
  68. }
  69. else
  70. header = "Загрузка материала";
  71. //await InitializeAccount();
  72. //int count = await articleModel.GetEditsCount();
  73. //int countbyid = await articleModel.GetEditsCount(currentAcc.UUID);
  74. //header += $", uuid:{currentAcc.UUID}, name: {currentAcc.Name}, edits count:{count}, count by accid: {countbyid}";
  75. }
  76. public async Task<string> NewProjectSmopp(long articleId)
  77. {
  78. WebRequest wrGETURL;
  79. wrGETURL = WebRequest.Create("http://dev.prmsys.net/makeproject.php?pt=232&aid="+ articleId);
  80. var response = await wrGETURL.GetResponseAsync();
  81. Stream dataStream = response.GetResponseStream();
  82. StreamReader reader = new(dataStream);
  83. return reader.ReadToEnd();
  84. }
  85. private async Task HandleSubmit()
  86. {
  87. Console.WriteLine($"HandleSubmit, docID: {DocID}.");
  88. modalLoading.Open();
  89. /// form validation
  90. List<string> errorFields = ValidateForm<ArticleModel>(article);
  91. if (errorFields.Count > 0)
  92. {
  93. modalLoading.Close();
  94. string invalid_fields = string.Join(", ", errorFields);
  95. modalInfo_error.Open($"Не заполнены поля: {invalid_fields}");
  96. Console.WriteLine($"HandleSubmit. Required fields: '{invalid_fields}' is not filled.");
  97. return;
  98. }
  99. /// all is fine, continue
  100. MySQLConnector dbCon = MySQLConnector.Instance();
  101. long id;
  102. string stringSQL;
  103. if (DocID > 0)
  104. {
  105. id = DocID;
  106. stringSQL = $"UPDATE articles " +
  107. $"SET filename='{article.Filename}', article_name='{article.Name}', authors='{article.Authors}', " +
  108. $"date_publish='{article.PublishDate:yyyy-MM-dd}', annotation='{article.Annotation}', " +
  109. $"keywords='{article.Keywords}', rating={article.Rating}, file_hash='{article.HashSum}' " +
  110. $"WHERE id={DocID}";
  111. await dbCon.SQLInsert(stringSQL);
  112. }
  113. else
  114. {
  115. stringSQL = $"INSERT INTO articles (filename, article_name, authors, date_publish, annotation, keywords, file_hash) " +
  116. $"VALUES ('{article.Filename}', '{article.Name}', '{article.Authors}', '{article.PublishDate:yyyy-MM-dd}'," +
  117. $"'{article.Annotation}', '{article.Keywords}', '{article.HashSum}')";
  118. id = await dbCon.SQLInsert(stringSQL);
  119. NewProjectSmopp(id);
  120. }
  121. /// tmp
  122. int action_type = DocID > 0 ? 2 : 1;
  123. stringSQL = $"INSERT INTO actions_history (article_id, action_type, acc_id) " +
  124. $"VALUES ('{id}', '{action_type}', '{currentAcc.UUID}')";
  125. await dbCon.SQLInsert(stringSQL);
  126. Dictionary<string, PropertyInfo> propDict = Compare.SimpleCompare<ArticleModel>(article, articleClone);
  127. foreach (KeyValuePair<string, PropertyInfo> prop in propDict)
  128. {
  129. //Console.WriteLine($"property name: {prop.Key}, value: {prop.Value.GetValue(articleModel, null)}");
  130. stringSQL = $"INSERT INTO articles_edit_log (article_id, acc_id, field_name, field_prevvalue, field_newvalue) " +
  131. $"VALUES ('{id}', '{currentAcc.UUID}', '{prop.Key}', '{prop.Value.GetValue(articleClone, null)}', '{prop.Value.GetValue(article, null)}')";
  132. await dbCon.SQLInsert(stringSQL);
  133. }
  134. //dbCon.Close();
  135. if (DocID > 0)
  136. {
  137. status = propDict.Count > 0 ? "All changes saved, article has veryfied." : "Article verifyed without any changes.";
  138. //transactionId = await Verify();
  139. Console.WriteLine("transactionId found " + transactionId);
  140. ///tmp
  141. editsCount = await article.GetEditsCount(currentAcc.UUID);
  142. modalInfo_transac.Open("");
  143. }
  144. else
  145. {
  146. string fullpath = Path.Combine(storageFolderPath, $"{id}_{article.Filename}");
  147. Directory.CreateDirectory(storageFolderPath);
  148. FileStream fs = new(fullpath, FileMode.Create, FileAccess.Write);
  149. memoryStream.Position = 0;
  150. await memoryStream.CopyToAsync(fs);
  151. Console.WriteLine($"User has saved new article data, {id}_{article.Filename}, memory size:{memoryStream.Length}b, file size: {fs.Length}b");
  152. memoryStream.Close();
  153. fs.Close();
  154. status = "New article data saved.";
  155. bool confirmed = await JsRuntime.InvokeAsync<bool>("confirm", "Хотите загрузить еще статью?");
  156. if (confirmed)
  157. NavigationManager.NavigateTo("docedit", true);
  158. else
  159. NavigationManager.NavigateTo("");
  160. }
  161. /// reloading articles
  162. await AppData.LoadArticles();
  163. }
  164. private async Task HandleSelection(InputFileChangeEventArgs e)
  165. {
  166. modalLoading.Open();
  167. IBrowserFile file = e.File;
  168. if (file != null)
  169. {
  170. Stream stream = file.OpenReadStream(MAX_FILE_SIZE);
  171. memoryStream = new();
  172. await stream.CopyToAsync(memoryStream);
  173. status = $"Finished loading {memoryStream.Length} bytes from {file.Name}";
  174. Console.WriteLine(status);
  175. /// calculating hash
  176. string hash = await CalculateHashSum(memoryStream);
  177. Console.WriteLine($"Hash: {hash}");
  178. /// checking hash
  179. MySQLConnector dbCon = MySQLConnector.Instance();
  180. string stringSQL;
  181. stringSQL = $"SELECT COUNT(*) FROM articles WHERE file_hash='{hash}'";
  182. int count = await dbCon.SQLSelectCount(stringSQL);
  183. //Console.WriteLine($"File duplicate check, founded files: {count}");
  184. if (count < 1)
  185. {
  186. //articleClone = DocParse.GetBaseProperties(memoryStream);
  187. articleClone = DocParse.GetBaseProperties("");
  188. articleClone.Filename = file.Name;
  189. articleClone.HashSum = hash;
  190. article = (ArticleModel)articleClone.Clone();
  191. IsSubmitDisabled = false;
  192. }
  193. else
  194. {
  195. status = $"File duplicate founded, hash: {hash}.";
  196. Console.WriteLine(status);
  197. IsSubmitDisabled = true;
  198. memoryStream.Close();
  199. stream.Close();
  200. modalInfo_error.Open("Загрузка не удалась, такой документ уже есть в системе.");
  201. }
  202. }
  203. modalLoading.Close();
  204. }
  205. private async Task Cancel()
  206. {
  207. bool confirmed = await JsRuntime.InvokeAsync<bool>("confirm", "Вы уверены, что хотите отклонить статью?");
  208. if (confirmed)
  209. {
  210. ///какая-то логика отмены...
  211. NavigationManager.NavigateTo("");
  212. }
  213. }
  214. //public async Task InitializeAccount()
  215. //{
  216. // AccountModel.Current = await GetCurrentAcc();
  217. // Console.WriteLine("InitializeAccount in DocEdit " + AccountModel.Current.Name);
  218. //}
  219. private async Task<AccountModel> GetCurrentAcc()
  220. {
  221. AccountModel account = new();
  222. var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
  223. var user = authState.User;
  224. if (user.Identity.IsAuthenticated)
  225. {
  226. var currentUser = await UserManager.GetUserAsync(user);
  227. account.UUID = currentUser.Id;
  228. //account.Name = currentUser.UserName;
  229. //account.Email = currentUser.Email;
  230. var acc = AccountModel.Find(account.UUID);
  231. if (acc != null)
  232. account = acc;
  233. ///tmp
  234. //account.AccRole = Role.User;
  235. return account;
  236. }
  237. return null;
  238. }
  239. private async Task<string> CalculateHashSum(MemoryStream ms)
  240. {
  241. MD5CryptoServiceProvider md5Provider = new();
  242. ms.Position = 0;
  243. byte[] hash = await md5Provider.ComputeHashAsync(ms);
  244. return Convert.ToBase64String(hash);
  245. }
  246. private List<string> ValidateForm<T>(T obj)
  247. {
  248. var props = typeof(T).GetProperties().Where(pi => Attribute.IsDefined(pi, typeof(RequiredAttribute)));
  249. List<string> result = new();
  250. foreach (var prop in props)
  251. {
  252. var val = prop.GetValue(obj, null);
  253. if (val == null || val?.ToString().Length == 0)
  254. result.Add(prop.Name);
  255. //Console.WriteLine($"Required field '{prop.Name}' is not filled.");
  256. }
  257. return result;
  258. }
  259. }
  260. }