using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using Yavsc.Abstract.Workflow; using Yavsc.Server.Helpers; using Yavsc.Models; using Yavsc.Models.Workflow; namespace Yavsc.Controllers { [Authorize] [Produces("application/json")] [Route(Constants.APIPrefix + "/activity")] public class ActivityApiController : Controller { private ApplicationDbContext _context; public ActivityApiController(ApplicationDbContext context) { _context = context; } // GET: api/ActivityApi [HttpGet] public IEnumerable GetActivities() { return _context.Activities.Include(a=>a.Forms).Where( a => !a.Hidden ); } [HttpGet("catalog")] public async Task>> GetCatalog( CancellationToken cancellationToken, [FromQuery] string parentCode = null) { var activities = await _context.Activities .AsNoTracking() .Include(a => a.Forms) .Include(a => a.Children) .ThenInclude(c => c.Forms) .Where(a => !a.Hidden && a.ParentCode == parentCode) .OrderByDescending(a => a.Rate) .ToListAsync(cancellationToken); var codes = activities .Select(a => a.Code) .Concat(activities.SelectMany(a => (a.Children ?? new List()) .Where(c => !c.Hidden) .Select(c => c.Code))) .Where(c => !string.IsNullOrWhiteSpace(c)) .Distinct() .ToArray(); var performerCounts = await ( from ua in _context.UserActivities.AsNoTracking() where !string.IsNullOrWhiteSpace(ua.DoesCode) && codes.Contains(ua.DoesCode) group ua by ua.DoesCode into g select new { Code = g.Key, Count = g.Select(x => x.UserId).Distinct().Count() }) .ToDictionaryAsync(x => x.Code, x => x.Count, cancellationToken); var filteredActivities = activities .Where(a => (performerCounts.TryGetValue(a.Code, out var ownCount) && ownCount > 0) || (a.Children ?? new List()) .Where(c => !c.Hidden) .Any(c => performerCounts.TryGetValue(c.Code, out var childCount) && childCount > 0)) .ToList(); return Ok(filteredActivities.Select(a => ToBrowseItem(a, performerCounts)).ToList()); } [HttpGet("{id}/users")] public async Task>> GetUsers( [FromRoute] string id, CancellationToken cancellationToken) { if (string.IsNullOrWhiteSpace(id)) { return BadRequest("Activity code is required."); } var activity = await _context.Activities .AsNoTracking() .SingleOrDefaultAsync(a => a.Code == id, cancellationToken); if (activity is null) { return NotFound(); } var users = await QueryDeclaredUsersAsync(id, activity.Name, cancellationToken); return Ok(users); } [HttpGet("{id}/performers")] public Task>> GetPerformers( [FromRoute] string id, CancellationToken cancellationToken) { // Backward-compatible alias kept for existing clients. return GetUsers(id, cancellationToken); } private Task> QueryDeclaredUsersAsync( string activityCode, string activityName, CancellationToken cancellationToken) { return ( from ua in _context.UserActivities.AsNoTracking() join u in _context.ApplicationUser.AsNoTracking() on ua.UserId equals u.Id into users from user in users.DefaultIfEmpty() join p in _context.Performers.AsNoTracking() on ua.UserId equals p.PerformerId into performerProfiles from performer in performerProfiles.DefaultIfEmpty() where ua.DoesCode == activityCode orderby user != null ? user.UserName : ua.UserId select new ActivityPerformerDto { PerformerId = ua.UserId, HasPerformerProfile = performer != null, UserName = user != null ? (user.UserName ?? string.Empty) : string.Empty, Active = performer != null && performer.Active, AcceptNotifications = performer != null && performer.AcceptNotifications, AcceptPublicContact = performer != null && performer.AcceptPublicContact, WebSite = performer != null ? (performer.WebSite ?? string.Empty) : string.Empty, ActivityCode = activityCode, ActivityName = activityName, SettingsClassName = _context.Activities .Where(a => a.Code == activityCode) .Select(a => a.SettingsClassName) .FirstOrDefault() ?? string.Empty, ExtraActivityCount = _context.UserActivities .Where(x => x.UserId == ua.UserId && x.DoesCode != activityCode) .Count() }) .Distinct() .ToListAsync(cancellationToken); } // GET: api/ActivityApi/5 [HttpGet("{id}", Name = "GetActivity")] public async Task GetActivity([FromRoute] string id) { if (!ModelState.IsValid) { return BadRequest(ModelState); } Activity activity = await _context.Activities.SingleAsync(m => m.Code == id); if (activity == null) { return NotFound(); } // Also return hidden ones // hidden doesn't mean disabled return Ok(activity); } // PUT: api/ActivityApi/5 [HttpPut("{id}"),Authorize("AdministratorOnly")] public async Task PutActivity([FromRoute] string id, [FromBody] Activity activity) { if (!ModelState.IsValid) { return BadRequest(ModelState); } if (id != activity.Code) { return BadRequest(); } _context.Entry(activity).State = EntityState.Modified; try { await _context.SaveChangesAsync(User.GetUserId()); } catch (DbUpdateConcurrencyException) { if (!ActivityExists(id)) { return NotFound(); } else { throw; } } return new StatusCodeResult(StatusCodes.Status204NoContent); } // POST: api/ActivityApi [HttpPost, Authorize("AdministratorOnly")] public async Task PostActivity([FromBody] Activity activity) { if (!ModelState.IsValid) { return BadRequest(ModelState); } _context.Activities.Add(activity); try { await _context.SaveChangesAsync(User.GetUserId()); } catch (DbUpdateException) { if (ActivityExists(activity.Code)) { return new StatusCodeResult(StatusCodes.Status409Conflict); } else { throw; } } return CreatedAtRoute("GetActivity", new { id = activity.Code }, activity); } // DELETE: api/ActivityApi/5 [HttpDelete("{id}"),Authorize("AdministratorOnly")] public async Task DeleteActivity([FromRoute] string id) { if (!ModelState.IsValid) { return BadRequest(ModelState); } Activity activity = await _context.Activities.SingleAsync(m => m.Code == id); if (activity == null) { return NotFound(); } _context.Activities.Remove(activity); await _context.SaveChangesAsync(User.GetUserId()); return Ok(activity); } protected override void Dispose(bool disposing) { if (disposing) { _context.Dispose(); } base.Dispose(disposing); } private bool ActivityExists(string id) { return _context.Activities.Count(e => e.Code == id) > 0; } private static ActivityBrowseItemDto ToBrowseItem( Activity activity, IReadOnlyDictionary performerCounts) { return new ActivityBrowseItemDto { Code = activity.Code, Name = activity.Name, ParentCode = activity.ParentCode, Description = activity.Description, Photo = activity.Photo, Rate = activity.Rate, PerformerCount = performerCounts.TryGetValue(activity.Code, out var count) ? count : 0, Forms = (activity.Forms ?? Enumerable.Empty()) .Select(f => new CommandFormSummaryDto { Id = f.Id, ActionName = f.ActionName, Title = f.Title, }) .ToList(), Children = (activity.Children ?? Enumerable.Empty()) .Where(c => !c.Hidden) .Where(c => performerCounts.TryGetValue(c.Code, out var childCount) && childCount > 0) .OrderByDescending(c => c.Rate) .Select(c => new ActivityBrowseItemDto { Code = c.Code, Name = c.Name, ParentCode = c.ParentCode, Description = c.Description, Photo = c.Photo, Rate = c.Rate, PerformerCount = performerCounts.TryGetValue(c.Code, out var childCount) ? childCount : 0, Forms = (c.Forms ?? Enumerable.Empty()) .Select(f => new CommandFormSummaryDto { Id = f.Id, ActionName = f.ActionName, Title = f.Title, }) .ToList(), }) .ToList(), }; } } }