using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; 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.Helpers; using Sarand.Net.Standard.Base.Utils; 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.Collections.Specialized; using System.IO; using System.Linq; using System.Threading.Tasks; namespace Saxonite.Net.Core.Web.Online.Controllers { //======================================================================= // Online Article Controller - Ajax actions. //======================================================================= partial class ArticleController { //------------------------------------------------------------------- // Article Content data. private class ArticleContentData { public List Segments { get; } = new List(); } private class ArticleContentSegmentData { public ArticleContentSegmentBodyData Body { get; set; } public ArticleContentSegmentImageData Image { get; set; } } private class ArticleContentSegmentBodyData { public String Html { get; set; } } private class ArticleContentSegmentImageData { public int ItemImageId { get; set; } public int ImageIdx { get; set; } public String ImageLink { get; set; } } //------------------------------------------------------------------- // Article data. private class ArticleData_List : AjaxResponse_Data.ItemData_Base { public String OrganisationId { get; set; } public String OrganisationName { get; set; } public String Headline { get; set; } public String Summary { get; set; } public class FeedbackData { public String Summary { get; set; } public long? ViewCount { get; set; } = null; public long? FeedbackCount { get; set; } = null; public String FeedbackAverage { get; set; } } public FeedbackData Feedback { get; set; } public String RegionCaptionPrimary { get; set; } public String SportCaptionPrimary { get; set; } public String PublishedOnFmt { get; set; } } private class ArticleData_Full : ArticleData_List { public String RegionIdPrimary { get; set; } public String SportIdPrimary { get; set; } public bool? Published { get; set; } = null; public bool? PublishedInFuture { get; set; } = null; public String PublishedOn_YYYYMMDD { get; set; } public String PublishedOn_Calendar { get; set; } public ArticleContentData Content { get; } = new ArticleContentData(); } private void PopulateArticleResponseListData(Model_Article model, SaxArticle article, SaxOrganisation organisation, SaxActivityFeedback activityFeedback, ArticleData_List data) { data.Id = model.EncryptId(article.Id); data.OrganisationId = SysId.ToString(article.OrganisationId); data.Headline = article.Headline; data.Summary = article.Summary; PopulateFeedbackResponseListData(activityFeedback, data); data.RegionCaptionPrimary = model.GetRegionCaption(article.RegionIdPrimary); data.SportCaptionPrimary = model.GetSportCaption(article.SportIdPrimary); data.CreatedOnFmt = DateUtil.Format(article.CreateStamp.On, "d-MMM-yyyy"); data.PublishedOnFmt = DateUtil.Format(article.PublishedAt, "d-MMM-yyyy"); data.StatusCode = article.StatusCode; data.StatusDesc = article.StatusDesc; } private void PopulateArticleResponseData(Model_Article model, SaxArticle article, SaxOrganisation organisation, SaxActivityFeedback activityFeedback, ArticleData_Full data) { PopulateArticleResponseListData(model, article, organisation, activityFeedback, data as ArticleData_List); data.RegionIdPrimary = model.GetEncryptedRegionId(article.RegionIdPrimary); data.SportIdPrimary = model.GetEncryptedSportId(article.SportIdPrimary); data.Published = DateUtil.IsValid(article.PublishedAt) ? true : (bool?)null; data.PublishedInFuture = (data.Published == true && article.PublishedAt.CompareTo(DateTime.Today) >= 0) ? true : (bool?)null; data.PublishedOn_YYYYMMDD = DateUtil.Format(article.PublishedAt, "yyyyMMdd"); data.PublishedOn_Calendar = model.FormatDateForForm(article.PublishedAt); // Article content. SaxArticle.ContentData articleContent = SaxArticle.ExtractContent(article.BodyData); foreach (var contentSegment in articleContent.Segments) { var segmentData = new ArticleContentSegmentData(); if (contentSegment.Body.IsValid()) { segmentData.Body = new ArticleContentSegmentBodyData(); segmentData.Body.Html = contentSegment.Body.Html; } else if (contentSegment.Image.IsValid()) { segmentData.Image = new ArticleContentSegmentImageData(); segmentData.Image.ItemImageId = SysId.ToInt(contentSegment.Image.ItemImageId); segmentData.Image.ImageLink = GetArticleImageLink(contentSegment.Image.ItemImageId); } data.Content.Segments.Add(segmentData); } } // Feedback. private void PopulateFeedbackResponseData(SaxActivityFeedback activityFeedback, ArticleData_Full data) { PopulateFeedbackResponseListData(activityFeedback, data); } private void PopulateFeedbackResponseListData(SaxActivityFeedback activityFeedback, ArticleData_List data) { if (activityFeedback == null) return; PopulateFeedbackResponseListData(activityFeedback.ViewCount, activityFeedback.FeedbackCount, activityFeedback.FeedbackTotal, activityFeedback.FeedbackAverage, data); } private void PopulateFeedbackResponseListData(long viewCount, long feedbackCount, long feedbackTotal, decimal feedbackAverage, ArticleData_List data) { if (viewCount <= 0) return; data.Feedback = new ArticleData_List.FeedbackData(); data.Feedback.Summary = viewCount.ToString(); if (feedbackCount > 0) data.Feedback.Summary = feedbackAverage.ToString("N1") + " " + feedbackCount.ToString() + "/" + data.Feedback.Summary; data.Feedback.ViewCount = viewCount; data.Feedback.FeedbackCount = feedbackCount; data.Feedback.FeedbackAverage = feedbackAverage.ToString("N1"); } //------------------------------------------------------------------- // Get Article List ajax request. //------------------------------------------------------------------- private class OrganisationData : AjaxResponse_Data.ItemIdName { } private class AjaxResponse_ArticleList : AjaxResponse_Data { // Construction. public AjaxResponse_ArticleList(SaxModel_Online model) : base(model) { } // Article list. public List Articles { get; } = new List(); /// Organisations that the user has rights to author articles for. public List Organisations { get; set; } public ItemIdName defaultSport { get; set; } public ItemIdName defaultRegion { get; set; } } //------------------------------------------------------------------- [HttpPost] [Route("Article/Ajax/GetList")] [Route("Article/Ajax/GetArticles")] public IActionResult Ajax_GetArticleList(Model_Article model) { String fn = GetType().Name + ".Ajax_GetArticleList"; String messageTitle = Localiser["article.caption.articles"]; AjaxResponse_ArticleList responseData = new AjaxResponse_ArticleList(model); // Validate session and referrer. if (!ValidReferer) return Forbid(); if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData); SaxSessionData_Online sessionData = SessionData_Online; SysId userAccountId = UserAccountId; RefreshModel(model); // Record disposable items as they are created so all can be disposed when needed. using (DisposableCollection disposableItems = new DisposableCollection()) { // User account. SaxUserAccount userAccount = AccountMgr.GetUserAccountById(UserAccountId); disposableItems.Add(userAccount); // Organisations that the user has rights to author articles for. foreach (SaxAccountMgr.OrganisationIdName articleOrganisation in AccountMgr.GetOrganisationsForAnyUserRole(userAccountId, new[] { Sax.OrganisationRole.Admin, Sax.OrganisationRole.ArticleAdmin })) { if (responseData.Organisations == null) responseData.Organisations = new List(); OrganisationData organisationData = new OrganisationData() { Id = model.EncryptId(articleOrganisation.Id), Name = articleOrganisation.Name }; responseData.Organisations.Add(organisationData); } // User must either be a journalist or allowed to author organisation articles. if (!UserIsJournalist && responseData.Organisations == null) return GetAjaxResult_GoHome(responseData); if (UserIsJournalist && responseData.Organisations != null) { OrganisationData organisationData = new OrganisationData() { Id = "", Name = "No Organisation - Journalist Articles" }; responseData.Organisations.Insert(0, organisationData); } // Filter organisation. SysId organisationIdFilter = model.ExtractSysId(model.Filter.OrganisationId); SaxOrganisation organisation = AccountMgr.GetOrganisationById(organisationIdFilter); disposableItems.Add(organisation); // Default Sport - defined by organisation if selected. SysId defaultSportId = organisation?.SportIdPrimary ?? null; if (SysId.IsValid(defaultSportId)) { responseData.defaultSport = new AjaxResponse_Data.ItemIdName() { Id = model.GetEncryptedSportId(defaultSportId), Name = model.GetSportCaption(defaultSportId) }; } // Default region - defined by organisation (if selected) otherwise user themselves. SysId defaultRegionId = organisation?.RegionIdPrimary ?? userAccount?.RegionIdCountry ?? null; if (SysId.IsValid(defaultRegionId)) { responseData.defaultRegion = new AjaxResponse_Data.ItemIdName() { Id = model.GetEncryptedRegionId(defaultRegionId), Name = model.GetRegionCaption(defaultRegionId) }; } // Item filter. SaxArticle.Params prmArticle = new SaxArticle.Params(); prmArticle.UserAccountIdsAuthorIncluded.Add(sessionData.User.UserAccountId); if (SysId.IsValid(organisationIdFilter)) prmArticle.OrganisationIdsIncluded.Add(organisationIdFilter); else prmArticle.ExcludeOrganisationArticles = true; prmArticle.ArticleFilter = model.Filter.Article; prmArticle.StatusCat = SaxArticle.StatusCat.NotDeleted; model.Paging.ItemTotal = (ulong)ActivityMgr.GetArticleCount(prmArticle); model.PopulatePagingTo(prmArticle.ResultsPaging); // Item columns. ColumnDfn colId = ActivityMgr.GetArticleColumnDfn(SaxArticle.Column.Id); ColumnDfn colOrganisationId = ActivityMgr.GetArticleColumnDfn(SaxArticle.Column.OrganisationId); ColumnDfn colOrganisationName = ActivityMgr.GetArticleColumnDfn(SaxArticle.Column.Organisation_Name); ColumnDfn colHeadline = ActivityMgr.GetArticleColumnDfn(SaxArticle.Column.Headline); ColumnDfn colSummary = ActivityMgr.GetArticleColumnDfn(SaxArticle.Column.Summary); ColumnDfn colRegionIdPrimary = ActivityMgr.GetArticleColumnDfn(SaxArticle.Column.RegionIdPrimary); ColumnDfn colSportIdPrimary = ActivityMgr.GetArticleColumnDfn(SaxArticle.Column.SportIdPrimary); ColumnDfn colPublishedOn = ActivityMgr.GetArticleColumnDfn(SaxArticle.Column.PublishedAt_Date); ColumnDfn colStatus = ActivityMgr.GetArticleColumnDfn(SaxArticle.Column.Status); ColumnDfn colCreatedOn = ActivityMgr.GetArticleColumnDfn(SaxArticle.Column.CreateOn_Date); // Feedback. ColumnDfn colFeedback_ViewCount = ActivityMgr.GetArticleColumnDfn(SaxArticle.Column.Feedback_ViewCount); ColumnDfn colFeedback_FeedbackCount = ActivityMgr.GetArticleColumnDfn(SaxArticle.Column.Feedback_FeedbackCount); ColumnDfn colFeedback_FeedbackTotal = ActivityMgr.GetArticleColumnDfn(SaxArticle.Column.Feedback_FeedbackTotal); ColumnDfn colFeedback_FeedbackAverage = ActivityMgr.GetArticleColumnDfn(SaxArticle.Column.Feedback_FeedbackAverage); List columns = new List() { colId, colOrganisationId, colOrganisationName, colHeadline, colSummary, colRegionIdPrimary, colSportIdPrimary, colPublishedOn, colStatus, colCreatedOn, colFeedback_ViewCount, colFeedback_FeedbackCount, colFeedback_FeedbackTotal, colFeedback_FeedbackAverage }; // Sort columns. colCreatedOn.SortAscending = false; prmArticle.SortColumns.Add(colCreatedOn); // Query items. using (DbResultSet resultSet = ActivityMgr.ExecuteArticleQuery(columns, prmArticle)) { while (resultSet != null && resultSet.HasRows && resultSet.NextRecord()) { NamedValue.Map values = resultSet.GetRecordValues(); ArticleData_List articleData = new ArticleData_List() { Id = model.EncryptId(new SysId(values.GetLong(colId.DataName))), OrganisationId = model.EncryptId(new SysId(values.GetLong(colOrganisationId.DataName))), OrganisationName = values.GetString(colOrganisationName.DataName), Headline = values.GetString(colHeadline.DataName), Summary = values.GetString(colSummary.DataName), RegionCaptionPrimary = model.GetRegionCaption(values.GetString(colRegionIdPrimary.DataName).ToSysId()), SportCaptionPrimary = model.GetSportCaption(values.GetString(colSportIdPrimary.DataName).ToSysId()), CreatedOnFmt = DateUtil.Format(values.GetDateTime(colCreatedOn.DataName), "d-MMM-yyyy"), PublishedOnFmt = DateUtil.Format(values.GetDateTime(colPublishedOn.DataName), "d-MMM-yyyy"), StatusCode = values.GetString(colStatus.DataName), StatusDesc = SacsSystem.GetStatusDescForCode(values.GetString(colStatus.DataName)) }; // Feedback. long viewCount = values.GetLong(colFeedback_ViewCount.DataName); if (viewCount > 0) PopulateFeedbackResponseListData(viewCount, values.GetLong(colFeedback_FeedbackCount.DataName), values.GetLong(colFeedback_FeedbackTotal.DataName), values.GetDecimal(colFeedback_FeedbackAverage.DataName), articleData); responseData.Articles.Add(articleData); } } if (model.Paging.IsPaged) model.Paging.ItemCount = (ushort)responseData.Articles.Count; } // Complete response. responseData.SetPaging(model); responseData.Success = true; return Json(responseData); } //------------------------------------------------------------------- // Get Article ajax request. //------------------------------------------------------------------- private class AjaxResponse_Article : AjaxResponse_Data { // Construction. public AjaxResponse_Article(SaxModel_Online model) : base(model) { } // Article details. public ArticleData_Full Article { get; } = new ArticleData_Full(); } //------------------------------------------------------------------- [HttpPost] [Route("Article/Ajax/Get")] [Route("Article/Ajax/GetItem")] [Route("Article/Ajax/GetArticle")] public IActionResult Ajax_GetArticle(Model_Article model) { String fn = GetType().Name + ".Ajax_GetArticle"; String messageTitle = Localiser["article.caption.article"]; AjaxResponse_Article responseData = new AjaxResponse_Article(model); // Validate session and referrer. if (!ValidReferer) return Forbid(); if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData); SysId userAccountId = UserAccountId; RefreshModel(model); // Record disposable items as they are created so all can be disposed when needed. using (DisposableCollection disposableItems = new DisposableCollection()) { // Identify item. SysId articleId = model.ExtractSysId(model.ArticleId); if (!SysId.IsValid(articleId)) return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.specified"])); // Retrieve item. SaxArticle article = ActivityMgr.GetArticleById(articleId); disposableItems.Add(article); if (article == null) { SaLogger.Warn(fn + " Failed to retrieve Article for Id: '" + model.ArticleId + "'"); return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.found"])); } if (!article.UserAccountIdAuthor.Equals(userAccountId)) { SaLogger.Warn(fn + " Inconsistent Article Author. Expected: " + userAccountId + ". Found: " + article.UserAccountIdAuthor); return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.system"])); } // Organisation. SaxOrganisation organisation = AccountMgr.GetOrganisationById(article.OrganisationId); disposableItems.Add(organisation); // Delete upload images. SaxItemImage.Params prmItemImage = new SaxItemImage.Params(); prmItemImage.ItemIdsIncluded.Add(new ItemId((short)Sax.DataItemType.Article, articleId)); prmItemImage.ImageTypesIncluded.Add(Sax.ImageType.ArticleUpload); prmItemImage.StatusCat = SaxItemImage.StatusCat.NotDeleted; foreach (SaxItemImage itemImage in ItemDataMgr.GetItemImageList(prmItemImage)) { disposableItems.Add(itemImage); itemImage.SessionUid = 0; itemImage.ImageUid = 0; itemImage.Status = SaStatus.Deleted; ItemDataMgr.UpdateItemImage(itemImage, DbUpdateInfo); } // Article Feedback. SaxActivityFeedback activityFeedback = ActivityMgr.GetActivityFeedbackForItem(article.GetItemId()); disposableItems.Add(activityFeedback); // Construct response. PopulateArticleResponseData(model, article, organisation, activityFeedback, responseData.Article); } responseData.Success = true; return Json(responseData); } //------------------------------------------------------------------- // Article - Upload Image. //------------------------------------------------------------------- private class AjaxResponse_UploadArticleImage : AjaxResponse_Data { // Construction. public AjaxResponse_UploadArticleImage(SaxModel_Online model) : base(model) { } // Article segment details. public ArticleContentSegmentData Segment { get; set; } public String UploadMsg { get; set; } } // Ajax request. [HttpPost] [Route("Article/Ajax/UploadImage")] public IActionResult Ajax_UploadArticleImage(Model_Article model) { String fn = GetType().Name + ".Ajax_UploadArticleImage"; AjaxResponse_UploadArticleImage responseData = new AjaxResponse_UploadArticleImage(model); String messageTitle = Localiser["article.caption.article"]; // Validate session and referrer. if (!ValidReferer) return Forbid(); if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData); SaxSessionData_Online sessionData = SessionData_Online; RefreshModel(model); // Record disposable items as they are created so all can be disposed when needed. using (DisposableCollection disposableItems = new DisposableCollection()) { // Validate number of images. if (model.ArticleImage.IsEmpty()) { SaLogger.Warn(fn + " No image file supplied for upload"); return Json(responseData); } if (model.ArticleImage.Count != 1) { SaLogger.Warn(fn + " Invalid number of image files uploaded: " + model.ArticleImage.Count); return Json(responseData.SetControlError("ArticleImage", Localiser["msg.upload.image.single"])); } IFormFile file = model.ArticleImage[0]; // Image properties. byte[] imageData = null; using (MemoryStream fileStream = new MemoryStream()) { file.CopyTo(fileStream); imageData = fileStream.ToArray(); } // Validate the image. ImageUtils.ImageConfig imageConfig = ImageUtils.VerifyImageData(imageData); if (!imageConfig.ValidImage) return Json(responseData.SetControlError("ArticleImage", Localiser["msg.upload.image.invalid"])); // Image belongs to the article. // If the article doesn't yet exist, it will need to be uploaded to the user first and later transferred. SysId articleId = model.ExtractSysId(model.ArticleId); ItemId articleItemId = new ItemId((short)Sax.DataItemType.Article, articleId); ItemId userItemId = new ItemId((short)Sax.DataItemType.UserAccount, sessionData.User.UserAccountId); ItemId imageItemId = SysId.IsValid(articleId) ? articleItemId : userItemId; // Store uploaded image as 'Upload' - awaiting confirmation that the article details are to be saved. // NB Reuse any existing deleted article upload image to avoid creating new images unless necessary. SaxItemImage.Params prmItemImage = new SaxItemImage.Params(); prmItemImage.ItemIdsIncluded.Add(imageItemId); prmItemImage.ImageTypesIncluded.Add(Sax.ImageType.Article); prmItemImage.ImageTypesIncluded.Add(Sax.ImageType.ArticleUpload); prmItemImage.StatusCat = SaxItemImage.StatusCat.DeletedOnly; prmItemImage.ResultsPaging.PageLength = 1; prmItemImage.ResultsPaging.PageNo = 1; List itemImages = ItemDataMgr.GetItemImageList(prmItemImage); SaxItemImage itemImage = !itemImages.IsEmpty() ? itemImages[0] : null; if (itemImage == null) itemImage = ItemDataMgr.CreateItemImage(imageItemId, Sax.ImageType.ArticleUpload); disposableItems.Add(itemImage); itemImage.ImageType = Sax.ImageType.ArticleUpload; itemImage.Filename = file.FileName; itemImage.Mime = SacsContentTypeHelper.GetMimeTypeFromContentType(file.ContentType).ToString().ToLower(); itemImage.ContentType = file.ContentType; itemImage.Width = imageConfig.Width; itemImage.Height = imageConfig.Height; itemImage.ImageData = imageData; itemImage.SessionUid = sessionData.SessionIdx; itemImage.ImageUid = sessionData.NextItemIdx; itemImage.Status = SaxItemImage.DefaultStatus; if (!SysId.IsValid(itemImage.Id)) { if (!ItemDataMgr.InsertItemImage(itemImage, DbUpdateInfo)) { SaLogger.Warn(fn + " Failed to insert uploaded image - " + ItemDataMgr.ErrorStateMessage); return Json(responseData.SetControlError("ArticleImage", Localiser["msg.error.item.insert.failed"])); } } else { if (!ItemDataMgr.UpdateItemImage(itemImage, DbUpdateInfo)) { SaLogger.Warn(fn + " Failed to update uploaded image - " + ItemDataMgr.ErrorStateMessage); return Json(responseData.SetControlError("ArticleImage", Localiser["msg.error.item.insert.failed"])); } } // Segment image info. responseData.Segment = new ArticleContentSegmentData(); responseData.Segment.Image = new ArticleContentSegmentImageData(); responseData.Segment.Image.ImageLink = GetArticleImageLink(itemImage.Id); responseData.Segment.Image.ImageIdx = itemImage.ImageUid; } // Complete response. //responseData.SetNotification(messageTitle, "Image uploaded."); responseData.Success = true; return Json(responseData); } private String GetArticleImageLink(SysId itemImageId) { return GetArticleImageLink(this, itemImageId); } public static String GetArticleImageLink(SaxController_Online controller, SysId itemImageId) { String imageLink = "Image/Article?"; NameValueCollection parameters = new NameValueCollection(); parameters.Add(ImageController.ParamName_ItemImageId, SysId.ToString(itemImageId)); return imageLink + ImageController.ParamName_ArticleImageEncrypted + "=" + SaxController_Online.EncryptOnlineParams(controller, parameters); } //------------------------------------------------------------------- // Store Article ajax request. //------------------------------------------------------------------- [HttpPost] [Route("Article/Ajax/Store")] [Route("Article/Ajax/StoreItem")] [Route("Article/Ajax/StoreArticle")] public IActionResult Ajax_StoreArticle(Model_Article model) { String fn = GetType().Name + ".Ajax_StoreArticle"; String messageTitle = Localiser["article.caption.article"]; AjaxResponse_Article responseData = new AjaxResponse_Article(model); // Validate session and referrer. if (!ValidReferer) return Forbid(); if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData); SaxSessionData_Online sessionData = SessionData_Online; RefreshModel(model); // Record disposable items as they are created so all can be disposed when needed. using (DisposableCollection disposableItems = new DisposableCollection()) { // Validate item properties. model.ValidateArticleData(responseData); if (responseData.HasControlErrors()) return Json(responseData); // Validate User & Organisation. SysId userAccountId = UserAccountId; SysId organisationId = model.ExtractSysId(model.ArticleOrganisationId); if (SysId.IsValid(organisationId)) { // Organisation specified - User must have Article Admin role. if (!AccountMgr.UserHasAnyOrganisationRole(userAccountId, organisationId, new[] { Sax.OrganisationRole.Admin, Sax.OrganisationRole.ArticleAdmin })) { SaLogger.Warn(fn + " User does not have Article Admin role for organisation." + GetLogSuffix()); return Json(responseData.SetGoHome(messageTitle, Localiser["msg.error.system"])); } } else { // Organisation not specified - User must be a Journalist. if (!UserIsJournalist) { SaLogger.Warn(fn + " User is not a Journalist for non-organisation article." + GetLogSuffix()); return Json(responseData.SetGoHome(messageTitle, Localiser["msg.error.system"])); } } // Validate item Id against 'Exists' flag. SysId articleId = model.ExtractSysId(model.ArticleId); bool articleExists = StringUtil.ToBool(model.ArticleExists); if (articleExists && !SysId.IsValid(articleId)) { SaLogger.Warn(fn + " Id not specified for existing Article."); return Json(responseData.SetGoHome(messageTitle, Localiser["msg.error.system"])); } if (!articleExists && SysId.IsValid(articleId)) { SaLogger.Warn(fn + " Id specified for new Article: '" + model.ArticleId + "'"); return Json(responseData.SetGoHome(messageTitle, Localiser["msg.error.system"])); } // Retrieve existing item. SaxArticle article = ActivityMgr.GetArticleById(articleId); disposableItems.Add(article); if (article == null && SysId.IsValid(articleId)) { SaLogger.Warn(fn + " Failed to retrieve article for Id: '" + model.ArticleId + "'"); return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.found"])); } if (article != null) { // Validate existing article. if (SysId.ToLong(article.UserAccountIdAuthor) != SysId.ToLong(userAccountId)) { SaLogger.Warn(fn + " Inconsistent Article Author." + GetLogSuffix()); return Json(responseData.SetGoHome(messageTitle, Localiser["msg.error.system"])); } if (SysId.ToLong(article.OrganisationId) != SysId.ToLong(organisationId)) { SaLogger.Warn(fn + " Inconsistent Article Organisation." + GetLogSuffix()); return Json(responseData.SetGoHome(messageTitle, Localiser["msg.error.system"])); } } // Organisation. SaxOrganisation organisation = AccountMgr.GetOrganisationById(organisationId); disposableItems.Add(organisation); // Create new item if required. if (article == null) { article = ActivityMgr.CreateArticle(sessionData.User.UserAccountId, organisationId); disposableItems.Add(article); } // Populate item properties. if (model.ArticleRegionId != null) article.RegionIdPrimary = model.ExtractSysId(model.ArticleRegionId); if (model.ArticleSportId != null) article.SportIdPrimary = model.ExtractSysId(model.ArticleSportId); if (model.ArticleHeadline != null) article.Headline = model.ArticleHeadline; if (model.ArticleSummary != null) article.Summary = model.ArticleSummary; if (!model.ArticlePublished.ToBool()) { if (model.ArticlePublish) article.PublishedAt = model.GetArticlePublishOn(); else article.PublishedAt = DateTime.MinValue; } if (model.ArticleStatusCode != null) article.Status = model.ArticleStatus; // Retrieve all article images - before new article is created as uploaded images are assigned to user first. SaxItemImage.Params prmItemImage = new SaxItemImage.Params(); if (SysId.IsValid(article.Id)) prmItemImage.ItemIdsIncluded.Add(article.GetItemId()); else prmItemImage.ItemIdsIncluded.Add(new ItemId((short)Sax.DataItemType.UserAccount, sessionData.User.UserAccountId)); prmItemImage.ImageTypesIncluded.Add(Sax.ImageType.Article); prmItemImage.ImageTypesIncluded.Add(Sax.ImageType.ArticleUpload); prmItemImage.StatusCat = SaxItemImage.StatusCat.NotDeleted; List itemImages = ItemDataMgr.GetItemImageList(prmItemImage); disposableItems.AddRange(itemImages); // If Article doesn't yet exist, create it now so that images can be assigned. bool articleCreated = false; if (!SysId.IsValid(article.Id)) { if (!ActivityMgr.InsertArticle(article, DbUpdateInfo)) { SaLogger.Warn(fn + " Failed to insert article: " + ActivityMgr.ErrorStateMessage); return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.insert.failed"])); } articleCreated = true; } // Content. if (model.ArticleContentSegmentBodies != null) { SaxArticle.ContentData articleContent = new SaxArticle.ContentData(); for (int segmentIdx = 0; segmentIdx < model.ArticleContentSegmentBodies.Count; segmentIdx++) { String segmentBody = model.ArticleContentSegmentBodies[segmentIdx]; String segmentItemImageId = model.ArticleContentSegmentItemImageIds.Count > segmentIdx ? model.ArticleContentSegmentItemImageIds[segmentIdx] : ""; String segmentImageIdx = model.ArticleContentSegmentImageIdxs.Count > segmentIdx ? model.ArticleContentSegmentImageIdxs[segmentIdx] : ""; if (!StringUtil.IsEmpty(segmentBody)) { // HTML Body segment. var segment = articleContent.AddSegment(); segment.Body.Html = segmentBody; } else if (!StringUtil.IsEmpty(segmentItemImageId) || !StringUtil.IsEmpty(segmentImageIdx)) { SysId itemImageId = model.ExtractSysId(segmentItemImageId); int imageIdx = StringUtil.ToInt(segmentImageIdx); // Check for new image first. SaxItemImage itemImage = itemImages.GetItemImageBySessionImage(sessionData.SessionIdx, imageIdx); if (itemImage == null) itemImage = itemImages.GetItemImageById(itemImageId); if (itemImage != null) { // Ensure that the image is correctly identified for the Article. itemImage.ItemId = article.GetItemId(); itemImage.ImageType = Sax.ImageType.Article; itemImage.SessionUid = 0; itemImage.ImageUid = 0; itemImage.Status = SaxItemImage.DefaultStatus; if (ItemDataMgr.UpdateItemImage(itemImage, DbUpdateInfo)) { // Assign the image segment to the article. var segment = articleContent.AddSegment(); segment.Image.ItemImageId = itemImage.Id; // Remove the image from the list as it's now been handled. itemImages.Remove(itemImage); } else SaLogger.Warn(fn + " Failed to update article image: " + ItemDataMgr.ErrorStateMessage); } else SaLogger.Warn(fn + " Failed to identify article image. ItemImageId: " + itemImageId.ToInt() + ". SessionIdx: " + sessionData.SessionIdx + ". ImageIdx: " + imageIdx + "."); } } article.BodyData = SaxArticle.GetContentData(articleContent); } // Update the Article. if (!ActivityMgr.UpdateArticle(article, DbUpdateInfo)) { SaLogger.Warn(fn + " Failed to update article [Id:" + SysId.ToString(article.Id) + "]: " + ActivityMgr.ErrorStateMessage); return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.update.failed"])); } SaLogger.Info(fn + " Article " + (articleCreated ? "created" : "updated") + ". Id: " + article.Id.ToString() + ". User: " + SessionData.User.EmailAddress, true); // Store Article Activity Index data. ActivityConsole.StoreActivityIndex(article, DbUpdateInfo); // Delete remaining images as they are no longer required. foreach (SaxItemImage itemImage in itemImages) { itemImage.SessionUid = 0; itemImage.ImageUid = 0; itemImage.Status = SaStatus.Deleted; ItemDataMgr.UpdateItemImage(itemImage, DbUpdateInfo); } // Article Feedback. SaxActivityFeedback activityFeedback = ActivityMgr.GetActivityFeedbackForItem(article.GetItemId()); disposableItems.Add(activityFeedback); // Construct response. PopulateArticleResponseData(model, article, organisation, activityFeedback, responseData.Article); responseData.Success = true; } return Json(responseData); } //------------------------------------------------------------------- } //======================================================================= }