430 lines
17 KiB
C#
430 lines
17 KiB
C#
using Microsoft.AspNetCore.Mvc;
|
|
using Microsoft.Extensions.Logging;
|
|
using Sarand.Net.Standard.Base.App;
|
|
using Sarand.Net.Standard.Base.Bus;
|
|
using Sarand.Net.Standard.Base.Core;
|
|
using Sarand.Net.Standard.Base.Dbas;
|
|
using Sarand.Net.Standard.Base.Utils;
|
|
using Saxonite.Net.Core.Web.Base.App;
|
|
using Saxonite.Net.Core.Web.Online.App;
|
|
using Saxonite.Net.Core.Web.Online.Models;
|
|
using Saxonite.Net.Standard.Base.App;
|
|
using Saxonite.Net.Standard.Base.Bus;
|
|
using Saxonite.Net.Standard.Base.Consoles;
|
|
using Saxonite.Net.Standard.Base.Mgr;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text.RegularExpressions;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace Saxonite.Net.Core.Web.Online.Controllers
|
|
{
|
|
//=======================================================================
|
|
// Online Activity Controller - Ajax actions only.
|
|
//=======================================================================
|
|
public partial class ActivityController : SaxController_Online
|
|
{
|
|
//-------------------------------------------------------------------
|
|
// Construction.
|
|
public ActivityController(SaxLocaliser localiser, ILogger<HomeController> logger) :
|
|
base(localiser, logger)
|
|
{ }
|
|
|
|
//-------------------------------------------------------------------
|
|
// Data definitions.
|
|
/// <summary>Activity details for home page list.</summary>
|
|
private class ActivityResponseData
|
|
{
|
|
public short ItemType { get; set; }
|
|
public String ItemTypeClass { get; set; }
|
|
public String ItemId { get; set; }
|
|
public String Organisation { get; set; }
|
|
public String Author { get; set; }
|
|
public String Caption { get; set; }
|
|
public String Blurb { get; set; }
|
|
public String Sport { get; set; }
|
|
public String Region { get; set; }
|
|
public int? Reaction { get; set; } = null;
|
|
public String PublishedOnFmt { get; set; }
|
|
}
|
|
/// <summary>Article activity info. </summary>
|
|
private class ActivityResponseData_Article
|
|
{
|
|
public ActivityResponseData_Article(SaxController_Online controller, SaxModel_Online model, SaxArticle article, SaxOrganisation organisation)
|
|
{
|
|
Organisation = organisation?.Name ?? null;
|
|
Author = model.GetUserAccountDisplayName(article.UserAccountIdAuthor);
|
|
Headline = article.Headline;
|
|
Summary = article.Summary;
|
|
Sport = model.GetSportCaption(article.SportIdPrimary);
|
|
Region = model.GetRegionCaption(article.RegionIdPrimary);
|
|
PublishedOnFmt = model.Localiser.FormatDate(article.PublishedAt);
|
|
SaxArticle.ContentData content = SaxArticle.ExtractContent(article.BodyData);
|
|
foreach (SaxArticle.ContentData.SegmentData segment in content.Segments)
|
|
Content.Segments.Add(new ContentData.SegmentData(controller, segment));
|
|
}
|
|
public String Organisation { get; set; }
|
|
public String Author { get; set; }
|
|
public String Headline { get; set; }
|
|
public String Summary { get; set; }
|
|
public String Sport { get; set; }
|
|
public String Region { get; set; }
|
|
public String PublishedOnFmt { get; set; }
|
|
public class ContentData
|
|
{
|
|
public class SegmentData
|
|
{
|
|
public SegmentData(SaxController_Online controller, SaxArticle.ContentData.SegmentData segment)
|
|
{
|
|
if (segment.Body.IsValid())
|
|
{
|
|
Body = new BodyData();
|
|
Body.Html = segment.Body.Html;
|
|
}
|
|
else if (segment.Image.IsValid())
|
|
{
|
|
Image = new ImageData();
|
|
Image.Link = ArticleController.GetArticleImageLink(controller, segment.Image.ItemImageId);
|
|
}
|
|
}
|
|
public class BodyData
|
|
{
|
|
public String Html { get; set; }
|
|
}
|
|
public BodyData Body { get; }
|
|
public class ImageData
|
|
{
|
|
public String Link { get; set; }
|
|
}
|
|
public ImageData Image { get; }
|
|
}
|
|
public List<SegmentData> Segments { get; } = new List<SegmentData>();
|
|
}
|
|
public ContentData Content { get; } = new ContentData();
|
|
}
|
|
/// <summary>Event activity info. </summary>
|
|
private class ActivityResponseData_Event
|
|
{
|
|
public ActivityResponseData_Event(SaxModel_Online model, SaxOrganisationEvent organisationEvent)
|
|
{
|
|
Organisation = model.GetOrganisationName(organisationEvent.OrganisationId);
|
|
EventAt = DateUtil.Format(organisationEvent.EventAt, "d-MMM-yy");
|
|
if (organisationEvent.EventAt.Hour > 0 || organisationEvent.EventAt.Minute > 0)
|
|
EventAt += DateUtil.Format(organisationEvent.EventAt, " HH:ss");
|
|
Summary = organisationEvent.Summary;
|
|
Details = organisationEvent.Details;
|
|
Sport = model.GetSportCaption(organisationEvent.SportIdPrimary);
|
|
Region = model.GetRegionCaption(organisationEvent.RegionIdCountry);
|
|
PublishedOnFmt = model.Localiser.FormatDate(organisationEvent.PublishedAt);
|
|
}
|
|
public String Organisation { get; set; }
|
|
public String EventAt { get; set; }
|
|
public String Summary { get; set; }
|
|
public String Details { get; set; }
|
|
public String Sport { get; set; }
|
|
public String Region { get; set; }
|
|
public String PublishedOnFmt { get; set; }
|
|
}
|
|
/// <summary>Opportunity activity info. </summary>
|
|
private class ActivityResponseData_Opportunity
|
|
{
|
|
public ActivityResponseData_Opportunity(SaxModel_Online model, SaxOrganisationOpportunity opportunity)
|
|
{
|
|
Organisation = model.GetOrganisationName(opportunity.OrganisationId);
|
|
Position = opportunity.Position;
|
|
Outline = opportunity.Outline;
|
|
Sport = model.GetSportCaption(opportunity.SportIdPrimary);
|
|
Region = model.GetRegionCaption(opportunity.RegionIdPrimary);
|
|
PublishedOnFmt = model.Localiser.FormatDate(opportunity.PublishedAt);
|
|
}
|
|
public String Organisation { get; set; }
|
|
public String Position { get; set; }
|
|
public String Outline { get; set; }
|
|
public String Sport { get; set; }
|
|
public String Region { get; set; }
|
|
public String PublishedOnFmt { get; set; }
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// Activities
|
|
//-------------------------------------------------------------------
|
|
private class AjaxResponse_ActivityList : AjaxResponse_Base
|
|
{
|
|
public List<ActivityResponseData> Activities { get; } = new List<ActivityResponseData>();
|
|
}
|
|
//-------------------------------------------------------------------
|
|
[HttpPost]
|
|
[Route("Activity/Ajax/GetList")]
|
|
[Route("Activity/Ajax/GetActivityList")]
|
|
[Route("Activity/Ajax/GetActivities")]
|
|
public IActionResult Ajax_GetActivityList(Model_Activity model)
|
|
{
|
|
String fn = GetType().Name + ".Ajax_GetActivityList";
|
|
String messageTitle = Localiser["activity.caption.activities"];
|
|
AjaxResponse_ActivityList responseData = new AjaxResponse_ActivityList();
|
|
|
|
// Validate session and referrer.
|
|
if (!ValidReferer) return Forbid();
|
|
if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData);
|
|
RefreshModel(model);
|
|
SaxSessionData_Online sessionData = SessionData_Online;
|
|
|
|
// User context.
|
|
SysId userAccountId = UserAccountId;
|
|
SaxActivityConsole.IndexContext indexContext = new SaxActivityConsole.IndexContext(userAccountId);
|
|
if (SysId.IsValid(userAccountId))
|
|
indexContext.UserAccountIdsOwner.Add(userAccountId);
|
|
SysId organisationId = model.DecryptId(model.Filter.OrganisationId);
|
|
if (SysId.IsValid(organisationId))
|
|
indexContext.OrganisationIdsOwner.Add(organisationId);
|
|
|
|
// Period.
|
|
indexContext.EffectiveTo = DateTime.Today;
|
|
|
|
// Search filter is dissected to extract regions and sports.
|
|
String searchFilter = model.Filter.Search.TrimAll();
|
|
List<SysId> sportIds = ExtractSearchFilterSports(ref searchFilter);
|
|
List<SysId> regionIds = ExtractSearchFilterRegions(ref searchFilter);
|
|
|
|
// Sport and region combinations.
|
|
if (sportIds.IsEmpty())
|
|
{
|
|
// No sports extractd from search filter - default to user's sports.
|
|
foreach (SysId sportId in sessionData.Profile.Sports.Select(item => item.SportId))
|
|
{
|
|
if (sportIds.Contains(sportId)) continue;
|
|
sportIds.AddId(sportId);
|
|
// Include child sports.
|
|
sportIds.AddIds(model.GetChildSports_Active(sportId).Select(item => item.Id));
|
|
model.GetSportCaption(sportId);
|
|
}
|
|
}
|
|
if (regionIds.IsEmpty())
|
|
{
|
|
// No regions extracted from search filter - default to user's region(s).
|
|
regionIds.AddId(sessionData.User.RegionIdProvince);
|
|
if (regionIds.IsEmpty())
|
|
regionIds.AddId(sessionData.User.RegionIdCountry);
|
|
}
|
|
foreach (SysId sportId in sportIds)
|
|
{
|
|
foreach (SysId regionId in regionIds)
|
|
indexContext.SportRegionIdsIncluded.Add(new SaxActivityIndex.SportRegionIds(sportId, regionId));
|
|
}
|
|
|
|
// Populate activities.
|
|
List<ItemId> activityItemIds = new List<ItemId>();
|
|
SaxActivityConsole.IndexResults indexResults = ActivityConsole.QueryIndex(indexContext);
|
|
foreach (SaxActivityConsole.IndexResults.ActivityData itemData in indexResults.Items)
|
|
{
|
|
activityItemIds.Add(itemData.ItemId);
|
|
ActivityResponseData activityData = new ActivityResponseData();
|
|
activityData.ItemType = itemData.ItemId.Type;
|
|
if (itemData.ItemId.Type == (int)Sax.DataItemType.Article)
|
|
activityData.ItemTypeClass = "Article";
|
|
else if (itemData.ItemId.Type == (int)Sax.DataItemType.OrganisationEvent)
|
|
activityData.ItemTypeClass = "Event";
|
|
else if (itemData.ItemId.Type == (int)Sax.DataItemType.OrganisationOpportunity)
|
|
activityData.ItemTypeClass = "Opportunity";
|
|
// ToDo Mask activity index Id with cross-reference stored in session data.
|
|
activityData.ItemId = model.EncryptId(itemData.ItemId.Id);
|
|
activityData.Organisation = itemData.Organisation;
|
|
activityData.Author = itemData.Author;
|
|
activityData.Caption = itemData.Caption;
|
|
activityData.Blurb = itemData.Blurb;
|
|
activityData.Sport = Localiser.GetSportName(itemData.SportId, itemData.SportName);
|
|
activityData.Region = Localiser.GetRegionName(itemData.RegionId, itemData.RegionName);
|
|
if ((int)itemData.Reaction > 0)
|
|
activityData.Reaction = (int)itemData.Reaction;
|
|
activityData.PublishedOnFmt = Localiser.FormatDate(itemData.EffectiveOn);
|
|
responseData.Activities.Add(activityData);
|
|
}
|
|
|
|
if (!activityItemIds.IsEmpty())
|
|
ActivityConsole.RecordUserActivitiesListed(userAccountId, activityItemIds, DbUpdateInfo);
|
|
|
|
// Complete response.
|
|
responseData.Success = true;
|
|
return Json(responseData);
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
/// <summary>Extract Sports from activity search filter.</summary>
|
|
private List<SysId> ExtractSearchFilterSports(ref string searchFilter)
|
|
{
|
|
List<SysId> sportIds = new List<SysId>();
|
|
if (StringUtil.IsEmpty(searchFilter)) return sportIds;
|
|
foreach (SaxSport.Data sport in DataCacheConsole.GetActiveSports())
|
|
{
|
|
Regex reSport = new Regex("\\b" + Regex.Escape(sport.Name) + "\\b", RegexOptions.IgnoreCase);
|
|
Match match = reSport.Match(searchFilter);
|
|
if (match.Success)
|
|
{
|
|
sportIds.Add(sport.Id);
|
|
searchFilter = reSport.Replace(searchFilter, " ").TrimAll();
|
|
}
|
|
}
|
|
return sportIds;
|
|
}
|
|
/// <summary>Extract Regions from activity search filter.</summary>
|
|
private List<SysId> ExtractSearchFilterRegions(ref string searchFilter)
|
|
{
|
|
List<SysId> regionIds = new List<SysId>();
|
|
if (StringUtil.IsEmpty(searchFilter)) return regionIds;
|
|
foreach (SaxRegion.Data region in DataCacheConsole.GetActiveRegions())
|
|
{
|
|
Regex reRegion = new Regex("\\b" + Regex.Escape(region.Name) + "\\b", RegexOptions.IgnoreCase);
|
|
Match match = reRegion.Match(searchFilter);
|
|
if (match.Success)
|
|
{
|
|
regionIds.Add(region.Id);
|
|
searchFilter = reRegion.Replace(searchFilter, " ").TrimAll();
|
|
}
|
|
}
|
|
return regionIds;
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// Activity Info
|
|
//-------------------------------------------------------------------
|
|
private class AjaxResponse_ActivityInfo : AjaxResponse_Base
|
|
{
|
|
public ActivityResponseData_Article Article { get; set; }
|
|
public ActivityResponseData_Event Event { get; set; }
|
|
public ActivityResponseData_Opportunity Opportunity { get; set; }
|
|
}
|
|
//-------------------------------------------------------------------
|
|
[HttpPost]
|
|
[Route("Activity/Ajax/GetInfo")]
|
|
[Route("Activity/Ajax/GetItemInfo")]
|
|
[Route("Activity/Ajax/GetActivityInfo")]
|
|
[Route("Activity/Ajax/GetActivity")]
|
|
public IActionResult Ajax_GetActivityInfo(Model_Activity model)
|
|
{
|
|
String fn = GetType().Name + ".Ajax_GetActivityInfo";
|
|
String messageTitle = Localiser["activity.caption.activity"];
|
|
AjaxResponse_ActivityInfo responseData = new AjaxResponse_ActivityInfo();
|
|
|
|
// Validate session and referrer.
|
|
if (!ValidReferer) return Forbid();
|
|
if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData);
|
|
RefreshModel(model);
|
|
SaxSessionData_Online sessionData = SessionData_Online;
|
|
|
|
// Record disposable items as they are created so all can be disposed when needed.
|
|
using (DisposableCollection<SacsHierarchyItem> disposableItems = new DisposableCollection<SacsHierarchyItem>())
|
|
{
|
|
|
|
// Identify activity.
|
|
Sax.DataItemType itemType = (Sax.DataItemType)StringUtil.ToShort(model.ActivityItemType);
|
|
SysId itemId = model.ExtractSysId(model.ActivityItemId);
|
|
if (itemType <= 0 || !SysId.IsValid(itemId))
|
|
{
|
|
SaLogger.Warn(fn + " Activity type/Id inncorrectly specified." + GetLogSuffix());
|
|
return Json(responseData);
|
|
}
|
|
|
|
// Retrieve activity details.
|
|
if (itemType == Sax.DataItemType.Article)
|
|
{
|
|
// Article.
|
|
SaxArticle article = ActivityMgr.GetArticleById(itemId);
|
|
disposableItems.Add(article);
|
|
if (article == null)
|
|
{
|
|
SaLogger.Warn(fn + " Article not found for Activity Id: " + itemId.ToString() + "." + GetLogSuffix());
|
|
return Json(responseData);
|
|
}
|
|
SaxOrganisation organisation = AccountMgr.GetOrganisationById(article.OrganisationId);
|
|
disposableItems.Add(organisation);
|
|
responseData.Article = new ActivityResponseData_Article(this, model, article, organisation);
|
|
}
|
|
else if (itemType == Sax.DataItemType.OrganisationEvent)
|
|
{
|
|
// Organisation Event.
|
|
SaxOrganisationEvent organisationEvent = ActivityMgr.GetOrganisationEventById(itemId);
|
|
disposableItems.Add(organisationEvent);
|
|
if (organisationEvent == null)
|
|
{
|
|
SaLogger.Warn(fn + " Event not found for Activity Id: " + itemId.ToString() + "." + GetLogSuffix());
|
|
return Json(responseData);
|
|
}
|
|
responseData.Event = new ActivityResponseData_Event(model, organisationEvent);
|
|
}
|
|
else if (itemType == Sax.DataItemType.OrganisationOpportunity)
|
|
{
|
|
// Organisation Opportunity.
|
|
SaxOrganisationOpportunity opportunity = ActivityMgr.GetOrganisationOpportunityById(itemId);
|
|
disposableItems.Add(opportunity);
|
|
if (opportunity == null)
|
|
{
|
|
SaLogger.Warn(fn + " Opportunity not found for Activity Id: " + itemId.ToString() + "." + GetLogSuffix());
|
|
return Json(responseData);
|
|
}
|
|
responseData.Opportunity = new ActivityResponseData_Opportunity(model, opportunity);
|
|
}
|
|
|
|
// Record activty viewing.
|
|
if (!ActivityConsole.RecordUserActivityViewed(UserAccountId, new ItemId((short)itemType, itemId), DbUpdateInfo))
|
|
{
|
|
SaLogger.Warn(fn + " Failed to record Activity Viewing: " + ActivityConsole.ErrorStateMessage + "." + GetLogSuffix());
|
|
return Json(responseData);
|
|
}
|
|
}
|
|
|
|
// Complete response.
|
|
responseData.Success = true;
|
|
return Json(responseData);
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// Activity Reaction.
|
|
//-------------------------------------------------------------------
|
|
private class AjaxResponse_ActivityReaction : AjaxResponse_Base
|
|
{
|
|
public int Reaction { get; set; }
|
|
}
|
|
//-------------------------------------------------------------------
|
|
[HttpPost]
|
|
[Route("Activity/Ajax/React")]
|
|
[Route("Activity/Ajax/Reaction")]
|
|
public IActionResult Ajax_SetActivityReaction(Model_Activity model)
|
|
{
|
|
String fn = GetType().Name + ".Ajax_SetActivityReaction";
|
|
String messageTitle = Localiser["activity.caption.activity"];
|
|
AjaxResponse_ActivityReaction responseData = new AjaxResponse_ActivityReaction();
|
|
|
|
// Validate session and referrer.
|
|
if (!ValidReferer) return Forbid();
|
|
if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData);
|
|
RefreshModel(model);
|
|
SaxSessionData_Online sessionData = SessionData_Online;
|
|
|
|
// Identify activity.
|
|
Sax.DataItemType itemType = (Sax.DataItemType)StringUtil.ToShort(model.ActivityItemType);
|
|
SysId itemId = model.ExtractSysId(model.ActivityItemId);
|
|
if (itemType <= 0 || !SysId.IsValid(itemId))
|
|
{
|
|
SaLogger.Warn(fn + " Activity type/Id inncorrectly specified." + GetLogSuffix());
|
|
return Json(responseData);
|
|
}
|
|
|
|
// Record activity reaction.
|
|
if (!ActivityConsole.RecordUserActivityReaction(UserAccountId, new ItemId((short)itemType, itemId), model.GetActivityReaction(), DbUpdateInfo))
|
|
{
|
|
SaLogger.Warn(fn + " Failed to record Activity Reaction: " + ActivityConsole.ErrorStateMessage + "." + GetLogSuffix());
|
|
return Json(responseData);
|
|
}
|
|
|
|
// Complete response.
|
|
responseData.Reaction = (int)model.GetActivityReaction();
|
|
responseData.Success = true;
|
|
return Json(responseData);
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
}
|
|
//=======================================================================
|
|
}
|