source: KernelRecordsMVC.Web/Controllers/AdminController.cs@ d89d25b

main
Last change on this file since d89d25b was d89d25b, checked in by mmilevski <markomilevski3@…>, 4 weeks ago

Added few implementaions, minor UI changes.

  • Property mode set to 100644
File size: 37.5 KB
Line 
1using KernelRecordsMVC.Application.ViewModels;
2using KernelRecordsMVC.Domain.Enums;
3using KernelRecordsMVC.Domain.Models;
4using KernelRecordsMVC.Infrastructure.Data;
5using KernelRecordsMVC.Models;
6using Microsoft.AspNetCore.Mvc;
7using Microsoft.EntityFrameworkCore;
8
9namespace KernelRecordsMVC.Web.Controllers;
10
11public class AdminController : Controller
12{
13 private readonly KernelRecordsContext _context;
14
15 public AdminController(KernelRecordsContext context)
16 {
17 _context = context;
18 }
19
20
21 // =========================================================
22 // ADMIN DASHBOARD
23 // =========================================================
24
25 [HttpGet]
26 public IActionResult Index()
27 {
28 if (!IsAdmin())
29 return Forbid();
30
31
32 ViewBag.ProductCount =
33 _context.Products.Count();
34
35 ViewBag.ReleaseCount =
36 _context.Releases.Count();
37
38 ViewBag.OutOfStockCount =
39 _context.Products.Count(x =>
40 x.Stock <= 0);
41
42 ViewBag.LowStockCount =
43 _context.Products.Count(x =>
44 x.Stock > 0 &&
45 x.Stock <= 5);
46
47
48 return View();
49 }
50
51
52 // =========================================================
53 // PRODUCTS
54 // =========================================================
55
56 [HttpGet]
57 public IActionResult Products()
58 {
59 if (!IsProductManager())
60 return Forbid();
61
62
63 var products = _context.Products
64 .Include(p => p.Release)
65 .OrderBy(p => p.Release.Title)
66 .ThenBy(p => p.Format)
67 .ToList();
68
69
70 return View(products);
71 }
72
73
74 // =========================================================
75 // CREATE PRODUCT - GET
76 // =========================================================
77
78 [HttpGet]
79 public IActionResult CreateProduct()
80 {
81 if (!IsProductManager())
82 return Forbid();
83
84
85 LoadReleases();
86
87
88 return View(
89 new CreateProductViewModel());
90 }
91
92
93 // =========================================================
94 // CREATE PRODUCT - POST
95 // =========================================================
96
97 [HttpPost]
98 [ValidateAntiForgeryToken]
99 public IActionResult CreateProduct(
100 CreateProductViewModel model)
101 {
102 if (!IsProductManager())
103 return Forbid();
104
105
106 if (!ModelState.IsValid)
107 {
108 LoadReleases();
109
110 return View(model);
111 }
112
113
114 var release = _context.Releases
115 .FirstOrDefault(x =>
116 x.ReleaseId == model.ReleaseId);
117
118
119 if (release == null)
120 {
121 ModelState.AddModelError(
122 nameof(model.ReleaseId),
123 "The selected release does not exist.");
124
125
126 LoadReleases();
127
128
129 return View(model);
130 }
131
132
133 var alreadyExists =
134 _context.Products.Any(p =>
135 p.ReleaseId == model.ReleaseId &&
136 p.Format == model.Format);
137
138
139 if (alreadyExists)
140 {
141 ModelState.AddModelError(
142 nameof(model.Format),
143 "This release already has a product in this format.");
144
145
146 LoadReleases();
147
148
149 return View(model);
150 }
151
152
153 using var transaction =
154 _context.Database.BeginTransaction();
155
156
157 try
158 {
159 var product = new Product
160 {
161 ProductId =
162 GetNextProductId(),
163
164 ReleaseId =
165 model.ReleaseId,
166
167 Format =
168 model.Format,
169
170 Price =
171 model.Price,
172
173 ProductDescription =
174 model.ProductDescription,
175
176 Stock =
177 model.Stock
178 };
179
180
181 _context.Products.Add(product);
182
183 _context.SaveChanges();
184
185
186 CreateModification(
187 ModificationType.CREATE,
188 product.ProductId);
189
190
191 transaction.Commit();
192
193
194 TempData["Success"] =
195 $"{release.Title} ({product.Format}) was created successfully.";
196
197
198 return RedirectToAction(
199 nameof(Products));
200 }
201 catch
202 {
203 transaction.Rollback();
204
205 throw;
206 }
207 }
208
209
210 // =========================================================
211 // EDIT PRODUCT - GET
212 // =========================================================
213
214 [HttpGet]
215 public IActionResult EditProduct(long id)
216 {
217 if (!IsProductManager())
218 return Forbid();
219
220
221 var product = _context.Products
222 .Include(p => p.Release)
223 .FirstOrDefault(p =>
224 p.ProductId == id);
225
226
227 if (product == null)
228 return NotFound();
229
230
231 var model =
232 new EditProductViewModel
233 {
234 ProductId =
235 product.ProductId,
236
237 ReleaseId =
238 product.ReleaseId,
239
240 ReleaseTitle =
241 product.Release.Title,
242
243 Format =
244 product.Format,
245
246 Price =
247 product.Price,
248
249 ProductDescription =
250 product.ProductDescription,
251
252 Stock =
253 product.Stock,
254
255 ModificationType =
256 ModificationType.UPDATE
257 };
258
259
260 return View(model);
261 }
262
263
264 // =========================================================
265 // EDIT PRODUCT - POST
266 // =========================================================
267
268 [HttpPost]
269 [ValidateAntiForgeryToken]
270 public IActionResult EditProduct(
271 EditProductViewModel model)
272 {
273 if (!IsProductManager())
274 return Forbid();
275
276
277 var product = _context.Products
278 .Include(p => p.Release)
279 .FirstOrDefault(p =>
280 p.ProductId == model.ProductId);
281
282
283 if (product == null)
284 return NotFound();
285
286
287 // These values come from the database.
288 model.ReleaseId =
289 product.ReleaseId;
290
291 model.ReleaseTitle =
292 product.Release.Title;
293
294 model.Format =
295 product.Format;
296
297
298 // Display-only fields should not block
299 // validation of the submitted form.
300 ModelState.Remove(
301 nameof(model.ReleaseId));
302
303 ModelState.Remove(
304 nameof(model.ReleaseTitle));
305
306 ModelState.Remove(
307 nameof(model.Format));
308
309
310 // =====================================================
311 // DISCOUNT VALIDATION
312 // =====================================================
313
314 if (model.ModificationType ==
315 ModificationType.DISCOUNT)
316 {
317 if (!model.Discount.HasValue)
318 {
319 ModelState.AddModelError(
320 nameof(model.Discount),
321 "Please enter a discount percentage.");
322 }
323 else if (
324 model.Discount.Value <= 0 ||
325 model.Discount.Value >= 100)
326 {
327 ModelState.AddModelError(
328 nameof(model.Discount),
329 "Discount must be greater than 0 and less than 100.");
330 }
331 }
332
333
334 if (!ModelState.IsValid)
335 return View(model);
336
337
338 using var transaction =
339 _context.Database.BeginTransaction();
340
341
342 try
343 {
344 // =================================================
345 // APPLY DISCOUNT
346 // =================================================
347
348 if (model.ModificationType ==
349 ModificationType.DISCOUNT)
350 {
351 var discountPercentage =
352 model.Discount!.Value;
353
354
355 var oldPrice =
356 product.Price;
357
358
359 var discountAmount =
360 oldPrice *
361 (discountPercentage / 100m);
362
363
364 var newPrice =
365 oldPrice - discountAmount;
366
367
368 newPrice =
369 Math.Round(
370 newPrice,
371 2,
372 MidpointRounding.AwayFromZero);
373
374
375 // Product.Price becomes the
376 // actual current sale price.
377 product.Price =
378 newPrice;
379
380
381 _context.Products.Update(
382 product);
383
384 _context.SaveChanges();
385
386
387 CreateModification(
388 ModificationType.DISCOUNT,
389 product.ProductId,
390 discountPercentage);
391
392
393 transaction.Commit();
394
395
396 TempData["Success"] =
397 $"{discountPercentage:0.##}% discount applied. " +
398 $"Price changed from {oldPrice:C} to {newPrice:C}.";
399
400
401 return RedirectToAction(
402 nameof(Products));
403 }
404
405
406 // =================================================
407 // NORMAL UPDATE
408 // =================================================
409
410 product.Price =
411 model.Price;
412
413 product.ProductDescription =
414 model.ProductDescription;
415
416 product.Stock =
417 model.Stock;
418
419
420 _context.Products.Update(
421 product);
422
423 _context.SaveChanges();
424
425
426 CreateModification(
427 ModificationType.UPDATE,
428 product.ProductId);
429
430
431 transaction.Commit();
432
433
434 TempData["Success"] =
435 $"{product.Release.Title} was updated successfully.";
436
437
438 return RedirectToAction(
439 nameof(Products));
440 }
441 catch
442 {
443 transaction.Rollback();
444
445 throw;
446 }
447 }
448
449
450 // =========================================================
451 // CREATE RELEASE - GET
452 // =========================================================
453
454 [HttpGet]
455 public IActionResult CreateRelease()
456 {
457 if (!IsAdmin())
458 return Forbid();
459
460
461 LoadArtists();
462
463
464 var model =
465 new CreateReleaseViewModel
466 {
467 ReleaseDate =
468 DateTime.Today,
469
470 ReleaseType =
471 "ALBUM",
472
473 // Start an album with one empty track.
474 Tracks =
475 new List<CreateTrackViewModel>
476 {
477 new CreateTrackViewModel()
478 }
479 };
480
481
482 return View(model);
483 }
484
485
486 // =========================================================
487 // CREATE RELEASE - POST
488 // =========================================================
489
490 [HttpPost]
491 [ValidateAntiForgeryToken]
492 public IActionResult CreateRelease(
493 CreateReleaseViewModel model)
494 {
495 if (!IsAdmin())
496 return Forbid();
497
498
499 // =====================================================
500 // RELEASE TYPE VALIDATION
501 // =====================================================
502
503 if (model.ReleaseType != "ALBUM" &&
504 model.ReleaseType != "SINGLE")
505 {
506 ModelState.AddModelError(
507 nameof(model.ReleaseType),
508 "Please select Album or Single.");
509 }
510
511
512 // =====================================================
513 // MAIN ARTIST VALIDATION
514 // =====================================================
515
516 var mainArtistExists =
517 _context.Artists.Any(a =>
518 a.ArtistId ==
519 model.MainArtistId);
520
521
522 if (!mainArtistExists)
523 {
524 ModelState.AddModelError(
525 nameof(model.MainArtistId),
526 "Please select a valid main artist.");
527 }
528
529
530 // =====================================================
531 // FEATURED ARTISTS
532 // =====================================================
533
534 model.FeaturedArtistIds ??=
535 new List<long>();
536
537
538 model.FeaturedArtistIds =
539 model.FeaturedArtistIds
540 .Distinct()
541 .ToList();
542
543
544 // Do not allow the main artist
545 // to also be a featured artist.
546 model.FeaturedArtistIds.Remove(
547 model.MainArtistId);
548
549
550 // Validate that featured artists exist.
551 if (model.FeaturedArtistIds.Count > 0)
552 {
553 var validFeaturedArtistCount =
554 _context.Artists.Count(a =>
555 model.FeaturedArtistIds
556 .Contains(a.ArtistId));
557
558
559 if (validFeaturedArtistCount !=
560 model.FeaturedArtistIds.Count)
561 {
562 ModelState.AddModelError(
563 nameof(model.FeaturedArtistIds),
564 "One or more featured artists are invalid.");
565 }
566 }
567
568
569 // =====================================================
570 // SINGLE VALIDATION
571 // =====================================================
572
573 if (model.ReleaseType == "SINGLE")
574 {
575 if (string.IsNullOrWhiteSpace(
576 model.SingleDuration))
577 {
578 ModelState.AddModelError(
579 nameof(model.SingleDuration),
580 "Duration is required for a single.");
581 }
582 }
583
584
585 // =====================================================
586 // ALBUM VALIDATION
587 // =====================================================
588
589 if (model.ReleaseType == "ALBUM")
590 {
591 model.Tracks ??=
592 new List<CreateTrackViewModel>();
593
594
595 // Remove completely empty rows.
596 model.Tracks =
597 model.Tracks
598 .Where(t =>
599 !string.IsNullOrWhiteSpace(
600 t.SongName) ||
601 !string.IsNullOrWhiteSpace(
602 t.SongDuration))
603 .ToList();
604
605
606 if (model.Tracks.Count == 0)
607 {
608 ModelState.AddModelError(
609 nameof(model.Tracks),
610 "An album must contain at least one track.");
611 }
612
613
614 for (var i = 0;
615 i < model.Tracks.Count;
616 i++)
617 {
618 var track =
619 model.Tracks[i];
620
621
622 if (string.IsNullOrWhiteSpace(
623 track.SongName))
624 {
625 ModelState.AddModelError(
626 $"Tracks[{i}].SongName",
627 "Track name is required.");
628 }
629
630
631 if (string.IsNullOrWhiteSpace(
632 track.SongDuration))
633 {
634 ModelState.AddModelError(
635 $"Tracks[{i}].SongDuration",
636 "Track duration is required.");
637 }
638
639
640 track.ArtistIds ??=
641 new List<long>();
642
643
644 track.ArtistIds =
645 track.ArtistIds
646 .Distinct()
647 .ToList();
648
649
650 // Validate selected track artists.
651 if (track.ArtistIds.Count > 0)
652 {
653 var validTrackArtistCount =
654 _context.Artists.Count(a =>
655 track.ArtistIds.Contains(
656 a.ArtistId));
657
658
659 if (validTrackArtistCount !=
660 track.ArtistIds.Count)
661 {
662 ModelState.AddModelError(
663 $"Tracks[{i}].ArtistIds",
664 "One or more selected track artists are invalid.");
665 }
666 }
667 }
668 }
669
670
671 if (!ModelState.IsValid)
672 {
673 LoadArtists();
674
675 return View(model);
676 }
677
678
679 using var transaction =
680 _context.Database.BeginTransaction();
681
682
683 try
684 {
685 // =================================================
686 // CREATE RELEASE
687 // =================================================
688
689 var release =
690 new Release
691 {
692 ReleaseId =
693 GetNextReleaseId(),
694
695 Title =
696 model.Title.Trim(),
697
698 RecordLabel =
699 string.IsNullOrWhiteSpace(
700 model.RecordLabel)
701 ? null
702 : model.RecordLabel.Trim(),
703
704 Genre =
705 model.Genre.Trim(),
706
707 ReleaseDate =
708 model.ReleaseDate,
709
710 CoverPhoto =
711 model.CoverPhoto.Trim()
712 };
713
714
715 _context.Releases.Add(
716 release);
717
718 _context.SaveChanges();
719
720
721 // =================================================
722 // MAIN RELEASE ARTIST
723 // =================================================
724
725 var mainReleaseArtist =
726 new ReleaseArtist
727 {
728 ReleaseId =
729 release.ReleaseId,
730
731 ArtistId =
732 model.MainArtistId,
733
734 ReleaseOrdinal = 1,
735
736 Type =
737 ArtistReleaseType.MAIN
738 };
739
740
741 _context.ReleaseArtists.Add(
742 mainReleaseArtist);
743
744
745 // =================================================
746 // FEATURED RELEASE ARTISTS
747 // =================================================
748
749 long releaseOrdinal = 2;
750
751
752 foreach (var artistId
753 in model.FeaturedArtistIds)
754 {
755 var featuredArtist =
756 new ReleaseArtist
757 {
758 ReleaseId =
759 release.ReleaseId,
760
761 ArtistId =
762 artistId,
763
764 ReleaseOrdinal =
765 releaseOrdinal++,
766
767 Type =
768 ArtistReleaseType.FEATURE
769 };
770
771
772 _context.ReleaseArtists.Add(
773 featuredArtist);
774 }
775
776
777 _context.SaveChanges();
778
779
780 // =================================================
781 // ALBUM
782 // =================================================
783
784 if (model.ReleaseType == "ALBUM")
785 {
786 var album =
787 new Album
788 {
789 ReleaseId =
790 release.ReleaseId
791 };
792
793
794 _context.Albums.Add(
795 album);
796
797 _context.SaveChanges();
798
799
800 // =============================================
801 // TRACKS
802 // =============================================
803
804 foreach (var trackModel
805 in model.Tracks)
806 {
807 var song =
808 new Song
809 {
810 SongId =
811 GetNextSongId(),
812
813 SongName =
814 trackModel
815 .SongName
816 .Trim(),
817
818 SongDuration =
819 trackModel
820 .SongDuration
821 .Trim()
822 };
823
824
825 _context.Songs.Add(
826 song);
827
828 _context.SaveChanges();
829
830
831 // =========================================
832 // LINK SONG TO ALBUM
833 // =========================================
834
835 var albumSong =
836 new AlbumSong
837 {
838 AlbumId =
839 release.ReleaseId,
840
841 SongId =
842 song.SongId
843 };
844
845
846 _context.AlbumSongs.Add(
847 albumSong);
848
849
850 // =========================================
851 // SONG ARTISTS
852 // =========================================
853
854 var trackArtistIds =
855 trackModel.ArtistIds
856 .Distinct()
857 .ToList();
858
859
860 // If no track artists were selected,
861 // automatically use the release's
862 // main artist.
863 if (trackArtistIds.Count == 0)
864 {
865 trackArtistIds.Add(
866 model.MainArtistId);
867 }
868
869
870 long songOrdinal = 1;
871
872
873 foreach (var artistId
874 in trackArtistIds)
875 {
876 var songArtist =
877 new SongArtist
878 {
879 SongId =
880 song.SongId,
881
882 ArtistId =
883 artistId,
884
885 SongOrdinal =
886 songOrdinal++
887 };
888
889
890 _context.SongArtists.Add(
891 songArtist);
892 }
893
894
895 _context.SaveChanges();
896 }
897 }
898
899
900 // =================================================
901 // SINGLE
902 // =================================================
903
904 else
905 {
906 var single =
907 new SingleRelease
908 {
909 ReleaseId =
910 release.ReleaseId,
911
912 Duration =
913 model.SingleDuration!
914 .Trim()
915 };
916
917
918 _context.SingleReleases.Add(
919 single);
920
921 _context.SaveChanges();
922 }
923
924
925 transaction.Commit();
926
927
928 TempData["Success"] =
929 $"{release.Title} was created successfully.";
930
931
932 return RedirectToAction(
933 nameof(Index));
934 }
935 catch
936 {
937 transaction.Rollback();
938
939 throw;
940 }
941 }
942
943
944 // =========================================================
945 // AUTHORIZATION HELPERS
946 // =========================================================
947
948 private bool IsAdmin()
949 {
950 return HttpContext.Session
951 .GetString("Role") ==
952 "Admin";
953 }
954
955
956 private bool IsProductManager()
957 {
958 var role =
959 HttpContext.Session
960 .GetString("Role");
961
962 var adminType =
963 HttpContext.Session
964 .GetString("AdminType");
965
966
967 return role == "Admin" &&
968 (
969 adminType == "PRODUCT_MANAGER" ||
970 adminType == "SUPER_ADMIN"
971 );
972 }
973
974
975 // =========================================================
976 // CURRENT ADMIN ID
977 // =========================================================
978
979 private long? GetCurrentUserId()
980 {
981 var userId =
982 HttpContext.Session
983 .GetInt32("UserId");
984
985
986 if (!userId.HasValue)
987 return null;
988
989
990 return userId.Value;
991 }
992
993
994 // =========================================================
995 // LOAD RELEASES
996 // =========================================================
997
998 private void LoadReleases()
999 {
1000 ViewBag.Releases =
1001 _context.Releases
1002 .OrderBy(x =>
1003 x.Title)
1004 .ToList();
1005 }
1006
1007
1008 // =========================================================
1009 // LOAD ARTISTS
1010 // =========================================================
1011
1012 private void LoadArtists()
1013 {
1014 ViewBag.Artists =
1015 _context.Artists
1016 .OrderBy(a =>
1017 a.ArtistName)
1018 .ToList();
1019 }
1020
1021
1022 // =========================================================
1023 // NEXT PRODUCT ID
1024 // =========================================================
1025
1026 private long GetNextProductId()
1027 {
1028 var maxId =
1029 _context.Products
1030 .Select(x =>
1031 (long?)x.ProductId)
1032 .Max();
1033
1034
1035 return (maxId ?? 0) + 1;
1036 }
1037
1038
1039 // =========================================================
1040 // NEXT RELEASE ID
1041 // =========================================================
1042
1043 private long GetNextReleaseId()
1044 {
1045 var maxId =
1046 _context.Releases
1047 .Select(x =>
1048 (long?)x.ReleaseId)
1049 .Max();
1050
1051
1052 return (maxId ?? 0) + 1;
1053 }
1054
1055
1056 // =========================================================
1057 // NEXT SONG ID
1058 // =========================================================
1059
1060 private long GetNextSongId()
1061 {
1062 var maxId =
1063 _context.Songs
1064 .Select(x =>
1065 (long?)x.SongId)
1066 .Max();
1067
1068
1069 return (maxId ?? 0) + 1;
1070 }
1071
1072
1073 // =========================================================
1074 // NEXT MODIFICATION ID
1075 // =========================================================
1076
1077 private long GetNextModificationId()
1078 {
1079 var maxId =
1080 _context.Modifications
1081 .Select(x =>
1082 (long?)x.ModificationId)
1083 .Max();
1084
1085
1086 return (maxId ?? 0) + 1;
1087 }
1088
1089
1090 // =========================================================
1091 // CREATE MODIFICATION RECORD
1092 // =========================================================
1093
1094 private void CreateModification(
1095 ModificationType type,
1096 long productId,
1097 decimal? discount = null)
1098 {
1099 var adminId =
1100 GetCurrentUserId();
1101
1102
1103 if (adminId == null)
1104 {
1105 throw new InvalidOperationException(
1106 "The current admin could not be identified.");
1107 }
1108
1109
1110 var modification =
1111 new Modification
1112 {
1113 ModificationId =
1114 GetNextModificationId(),
1115
1116 AdminId =
1117 adminId.Value,
1118
1119 DateModified =
1120 DateTime.Today,
1121
1122 TypeOfModification =
1123 type,
1124
1125 Discount =
1126 discount
1127 };
1128
1129
1130 _context.Modifications.Add(
1131 modification);
1132
1133 _context.SaveChanges();
1134
1135
1136 var modificationProduct =
1137 new ModificationProduct
1138 {
1139 ModificationId =
1140 modification.ModificationId,
1141
1142 ProductId =
1143 productId
1144 };
1145
1146
1147 _context.ModificationProducts.Add(
1148 modificationProduct);
1149
1150 _context.SaveChanges();
1151 }
1152 // =========================================================
1153// ARTISTS
1154// =========================================================
1155
1156[HttpGet]
1157public IActionResult Artists()
1158{
1159 if (!IsAdmin())
1160 return Forbid();
1161
1162 var artists = _context.Artists
1163 .OrderBy(a => a.ArtistName)
1164 .ToList();
1165
1166 return View(artists);
1167}
1168
1169
1170// =========================================================
1171// CREATE ARTIST - GET
1172// =========================================================
1173
1174[HttpGet]
1175public IActionResult CreateArtist()
1176{
1177 if (!IsAdmin())
1178 return Forbid();
1179
1180 return View(new CreateArtistViewModel());
1181}
1182
1183
1184// =========================================================
1185// CREATE ARTIST - POST
1186// =========================================================
1187
1188[HttpPost]
1189[ValidateAntiForgeryToken]
1190public IActionResult CreateArtist(CreateArtistViewModel model)
1191{
1192 if (!IsAdmin())
1193 return Forbid();
1194
1195 if (!ModelState.IsValid)
1196 return View(model);
1197
1198 var duplicateExists =
1199 _context.Artists.Any(a =>
1200 a.ArtistName.ToLower() ==
1201 model.ArtistName.Trim().ToLower());
1202
1203 if (duplicateExists)
1204 {
1205 ModelState.AddModelError(
1206 nameof(model.ArtistName),
1207 "An artist with this name already exists.");
1208
1209 return View(model);
1210 }
1211
1212 var artist = new Artist
1213 {
1214 ArtistId = GetNextArtistId(),
1215
1216 ArtistName =
1217 model.ArtistName.Trim(),
1218
1219 ArtistDescription =
1220 string.IsNullOrWhiteSpace(model.ArtistDescription)
1221 ? null
1222 : model.ArtistDescription.Trim(),
1223
1224 ArtistPhoto =
1225 string.IsNullOrWhiteSpace(model.ArtistPhoto)
1226 ? null
1227 : model.ArtistPhoto.Trim()
1228 };
1229
1230 _context.Artists.Add(artist);
1231 _context.SaveChanges();
1232
1233 TempData["Success"] =
1234 $"{artist.ArtistName} was created successfully.";
1235
1236 return RedirectToAction(nameof(Artists));
1237}
1238
1239
1240// =========================================================
1241// EDIT ARTIST - GET
1242// =========================================================
1243
1244[HttpGet]
1245public IActionResult EditArtist(long id)
1246{
1247 if (!IsAdmin())
1248 return Forbid();
1249
1250 var artist = _context.Artists
1251 .FirstOrDefault(a =>
1252 a.ArtistId == id);
1253
1254 if (artist == null)
1255 return NotFound();
1256
1257 var model = new EditArtistViewModel
1258 {
1259 ArtistId =
1260 artist.ArtistId,
1261
1262 ArtistName =
1263 artist.ArtistName,
1264
1265 ArtistDescription =
1266 artist.ArtistDescription,
1267
1268 ArtistPhoto =
1269 artist.ArtistPhoto
1270 };
1271
1272 return View(model);
1273}
1274
1275
1276// =========================================================
1277// EDIT ARTIST - POST
1278// =========================================================
1279
1280[HttpPost]
1281[ValidateAntiForgeryToken]
1282public IActionResult EditArtist(EditArtistViewModel model)
1283{
1284 if (!IsAdmin())
1285 return Forbid();
1286
1287 var artist = _context.Artists
1288 .FirstOrDefault(a =>
1289 a.ArtistId == model.ArtistId);
1290
1291 if (artist == null)
1292 return NotFound();
1293
1294 if (!ModelState.IsValid)
1295 return View(model);
1296
1297 var duplicateExists =
1298 _context.Artists.Any(a =>
1299 a.ArtistId != model.ArtistId &&
1300 a.ArtistName.ToLower() ==
1301 model.ArtistName.Trim().ToLower());
1302
1303 if (duplicateExists)
1304 {
1305 ModelState.AddModelError(
1306 nameof(model.ArtistName),
1307 "Another artist already uses this name.");
1308
1309 return View(model);
1310 }
1311
1312 artist.ArtistName =
1313 model.ArtistName.Trim();
1314
1315 artist.ArtistDescription =
1316 string.IsNullOrWhiteSpace(model.ArtistDescription)
1317 ? null
1318 : model.ArtistDescription.Trim();
1319
1320 artist.ArtistPhoto =
1321 string.IsNullOrWhiteSpace(model.ArtistPhoto)
1322 ? null
1323 : model.ArtistPhoto.Trim();
1324
1325 _context.Artists.Update(artist);
1326 _context.SaveChanges();
1327
1328 TempData["Success"] =
1329 $"{artist.ArtistName} was updated successfully.";
1330
1331 return RedirectToAction(nameof(Artists));
1332}
1333private long GetNextArtistId()
1334{
1335 var maxId = _context.Artists
1336 .Select(x => (long?)x.ArtistId)
1337 .Max();
1338
1339 return (maxId ?? 0) + 1;
1340}
1341// =========================================================
1342// RELEASES
1343// =========================================================
1344
1345[HttpGet]
1346public IActionResult Releases()
1347{
1348 if (!IsAdmin())
1349 return Forbid();
1350
1351 var releases = _context.Releases
1352 .Include(r => r.ReleaseArtists)
1353 .ThenInclude(ra => ra.Artist)
1354 .OrderBy(r => r.Title)
1355 .ToList();
1356
1357 return View(releases);
1358}
1359
1360
1361// =========================================================
1362// EDIT RELEASE - GET
1363// =========================================================
1364
1365 [HttpGet]
1366 public IActionResult EditRelease(long id)
1367 {
1368 if (!IsAdmin())
1369 return Forbid();
1370
1371
1372 var release = _context.Releases
1373 .Include(r => r.ReleaseArtists)
1374 .ThenInclude(ra => ra.Artist)
1375 .FirstOrDefault(r =>
1376 r.ReleaseId == id);
1377
1378
1379 if (release == null)
1380 return NotFound();
1381
1382
1383 var mainArtist = release.ReleaseArtists
1384 .FirstOrDefault(ra =>
1385 ra.Type == ArtistReleaseType.MAIN);
1386
1387
1388 var featuredArtists = release.ReleaseArtists
1389 .Where(ra =>
1390 ra.Type == ArtistReleaseType.FEATURE)
1391 .OrderBy(ra => ra.ReleaseOrdinal)
1392 .Select(ra => ra.ArtistId)
1393 .ToList();
1394
1395
1396 var model = new EditReleaseViewModel
1397 {
1398 ReleaseId =
1399 release.ReleaseId,
1400
1401 Title =
1402 release.Title,
1403
1404 RecordLabel =
1405 release.RecordLabel,
1406
1407 Genre =
1408 release.Genre,
1409
1410 ReleaseDate =
1411 release.ReleaseDate,
1412
1413 CoverPhoto =
1414 release.CoverPhoto,
1415
1416 MainArtistId =
1417 mainArtist?.ArtistId ?? 0,
1418
1419 FeaturedArtistIds =
1420 featuredArtists
1421 };
1422
1423
1424 LoadArtists();
1425
1426
1427 return View(model);
1428 }
1429
1430
1431// =========================================================
1432// EDIT RELEASE - POST
1433// =========================================================
1434
1435[HttpPost]
1436[ValidateAntiForgeryToken]
1437public IActionResult EditRelease(
1438 EditReleaseViewModel model)
1439{
1440 if (!IsAdmin())
1441 return Forbid();
1442
1443
1444 var release = _context.Releases
1445 .Include(r => r.ReleaseArtists)
1446 .FirstOrDefault(r =>
1447 r.ReleaseId == model.ReleaseId);
1448
1449
1450 if (release == null)
1451 return NotFound();
1452
1453
1454 // ==========================================
1455 // ARTIST VALIDATION
1456 // ==========================================
1457
1458 var mainArtistExists =
1459 _context.Artists.Any(a =>
1460 a.ArtistId == model.MainArtistId);
1461
1462
1463 if (!mainArtistExists)
1464 {
1465 ModelState.AddModelError(
1466 nameof(model.MainArtistId),
1467 "Please select a valid main artist.");
1468 }
1469
1470
1471 model.FeaturedArtistIds ??=
1472 new List<long>();
1473
1474
1475 model.FeaturedArtistIds =
1476 model.FeaturedArtistIds
1477 .Distinct()
1478 .Where(id =>
1479 id != model.MainArtistId)
1480 .ToList();
1481
1482
1483 if (model.FeaturedArtistIds.Count > 0)
1484 {
1485 var validFeaturedCount =
1486 _context.Artists.Count(a =>
1487 model.FeaturedArtistIds
1488 .Contains(a.ArtistId));
1489
1490
1491 if (validFeaturedCount !=
1492 model.FeaturedArtistIds.Count)
1493 {
1494 ModelState.AddModelError(
1495 nameof(model.FeaturedArtistIds),
1496 "One or more featured artists are invalid.");
1497 }
1498 }
1499
1500
1501 if (!ModelState.IsValid)
1502 {
1503 LoadArtists();
1504
1505 return View(model);
1506 }
1507
1508
1509 using var transaction =
1510 _context.Database.BeginTransaction();
1511
1512
1513 try
1514 {
1515 // ==========================================
1516 // UPDATE RELEASE INFORMATION
1517 // ==========================================
1518
1519 release.Title =
1520 model.Title.Trim();
1521
1522 release.RecordLabel =
1523 string.IsNullOrWhiteSpace(
1524 model.RecordLabel)
1525 ? null
1526 : model.RecordLabel.Trim();
1527
1528 release.Genre =
1529 model.Genre.Trim();
1530
1531 release.ReleaseDate =
1532 model.ReleaseDate;
1533
1534 release.CoverPhoto =
1535 model.CoverPhoto.Trim();
1536
1537
1538 _context.Releases.Update(
1539 release);
1540
1541
1542 // ==========================================
1543 // REMOVE OLD RELEASE ARTIST LINKS
1544 // ==========================================
1545
1546 var existingArtistLinks =
1547 release.ReleaseArtists.ToList();
1548
1549
1550 _context.ReleaseArtists.RemoveRange(
1551 existingArtistLinks);
1552
1553
1554 _context.SaveChanges();
1555
1556
1557 // ==========================================
1558 // ADD MAIN ARTIST
1559 // ==========================================
1560
1561 _context.ReleaseArtists.Add(
1562 new ReleaseArtist
1563 {
1564 ReleaseId =
1565 release.ReleaseId,
1566
1567 ArtistId =
1568 model.MainArtistId,
1569
1570 ReleaseOrdinal =
1571 1,
1572
1573 Type =
1574 ArtistReleaseType.MAIN
1575 });
1576
1577
1578 // ==========================================
1579 // ADD FEATURED ARTISTS
1580 // ==========================================
1581
1582 long ordinal = 2;
1583
1584
1585 foreach (var artistId
1586 in model.FeaturedArtistIds)
1587 {
1588 _context.ReleaseArtists.Add(
1589 new ReleaseArtist
1590 {
1591 ReleaseId =
1592 release.ReleaseId,
1593
1594 ArtistId =
1595 artistId,
1596
1597 ReleaseOrdinal =
1598 ordinal++,
1599
1600 Type =
1601 ArtistReleaseType.FEATURE
1602 });
1603 }
1604
1605
1606 _context.SaveChanges();
1607
1608
1609 transaction.Commit();
1610
1611
1612 TempData["Success"] =
1613 $"{release.Title} was updated successfully.";
1614
1615
1616 return RedirectToAction(
1617 nameof(Releases));
1618 }
1619 catch
1620 {
1621 transaction.Rollback();
1622
1623 throw;
1624 }
1625}
1626}
Note: See TracBrowser for help on using the repository browser.