DocEdit.razor.cs 13 KB

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