This commit is contained in:
2021-08-09 14:18:08 +01:00
parent 0d7be69e1e
commit 5e5ef7190b
84 changed files with 2449 additions and 1717 deletions
@@ -159,6 +159,18 @@
<data name="activity.caption.activity" xml:space="preserve">
<value>Activity</value>
</data>
<data name="article.caption.article" xml:space="preserve">
<value>Article</value>
</data>
<data name="article.caption.article.create" xml:space="preserve">
<value>Create Article</value>
</data>
<data name="article.caption.article.update" xml:space="preserve">
<value>Update Article</value>
</data>
<data name="article.caption.articles" xml:space="preserve">
<value>Articles</value>
</data>
<data name="company.name" xml:space="preserve">
<value>Solum Forti</value>
</data>
@@ -198,18 +210,6 @@
<data name="home.organisation.request.para2" xml:space="preserve">
<value>Sign-up now with a few details and we'll let you know when you can login and get going.</value>
</data>
<data name="journalist.caption.article" xml:space="preserve">
<value>Article</value>
</data>
<data name="journalist.caption.article.create" xml:space="preserve">
<value>Create Article</value>
</data>
<data name="journalist.caption.article.update" xml:space="preserve">
<value>Update Article</value>
</data>
<data name="journalist.caption.articles" xml:space="preserve">
<value>Articles</value>
</data>
<data name="label.account" xml:space="preserve">
<value>My Account</value>
</data>
@@ -88,10 +88,9 @@ namespace Saxonite.Net.Core.Web.Online.Controllers
String messageTitle = Localiser["account.caption.profile.contact"];
// Validate session and referrer.
if (!ValidReferer)
return Forbid();
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
if (!ValidReferer) return Forbid();
if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData);
RefreshModel(model);
// Retrieve user details.
SaxUserAccount userAccount = AccountMgr.GetUserAccountById(SessionData.User.UserAccountId);
@@ -140,10 +139,9 @@ namespace Saxonite.Net.Core.Web.Online.Controllers
String messageTitle = Localiser["account.caption.profile.contact"];
// Validate session and referrer.
if (!ValidReferer)
return Forbid();
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
if (!ValidReferer) return Forbid();
if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData);
RefreshModel(model);
// Validate number of images.
if (model.AccountContactImage.IsEmpty())
@@ -229,10 +227,9 @@ namespace Saxonite.Net.Core.Web.Online.Controllers
String messageTitle = Localiser["account.caption.profile.contact"];
// Validate session and referrer.
if (!ValidReferer)
return Forbid();
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
if (!ValidReferer) return Forbid();
if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData);
RefreshModel(model);
// Validate data.
model.ValidateContactDetails(responseData);
@@ -55,6 +55,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers
// Validate session and referrer.
if (!ValidReferer) return Forbid();
if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData);
RefreshModel(model);
// Query organisations that user is a member of.
SaxUserOrganisation.Params prmUserOrganisation = new SaxUserOrganisation.Params();
@@ -75,10 +75,9 @@ namespace Saxonite.Net.Core.Web.Online.Controllers
String messageTitle = Localiser["account.caption.profile.sports"];
// Validate session and referrer.
if (!ValidReferer)
return Forbid();
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
if (!ValidReferer) return Forbid();
if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData);
RefreshModel(model);
// Item filter.
SaxUserSport.Params prmUserSport = new SaxUserSport.Params();
@@ -153,10 +152,9 @@ namespace Saxonite.Net.Core.Web.Online.Controllers
AjaxResponse_ProfileSport responseData = new AjaxResponse_ProfileSport(model);
// Validate session and referrer.
if (!ValidReferer)
return Forbid();
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
if (!ValidReferer) return Forbid();
if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData);
RefreshModel(model);
// Identify item.
SysId userSportId = model.UserSportId.ToSysId();
@@ -197,10 +195,9 @@ namespace Saxonite.Net.Core.Web.Online.Controllers
AjaxResponse_ProfileSport responseData = new AjaxResponse_ProfileSport(model);
// Validate session and referrer.
if (!ValidReferer)
return Forbid();
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
if (!ValidReferer) return Forbid();
if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData);
RefreshModel(model);
// Validate item properties.
model.ValidateProfileSportData(responseData);
@@ -11,6 +11,7 @@ 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;
@@ -49,16 +50,16 @@ namespace Saxonite.Net.Core.Web.Online.Controllers
/// <summary>Article activity info. </summary>
private class ActivityResponseData_Article
{
public ActivityResponseData_Article(SaxController_Online controller, SaxModel_Online model, SaxJournalistArticle article)
public ActivityResponseData_Article(SaxController_Online controller, SaxModel_Online model, SaxArticle article)
{
Author = model.GetUserAccountDisplayName(article.UserAccountIdJournalist);
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);
SaxJournalistArticle.ContentData content = SaxJournalistArticle.ExtractContent(article.BodyData);
foreach (SaxJournalistArticle.ContentData.SegmentData segment in content.Segments)
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; }
@@ -72,7 +73,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers
{
public class SegmentData
{
public SegmentData(SaxController_Online controller, SaxJournalistArticle.ContentData.SegmentData segment)
public SegmentData(SaxController_Online controller, SaxArticle.ContentData.SegmentData segment)
{
if (segment.Body.IsValid())
{
@@ -82,7 +83,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers
else if (segment.Image.IsValid())
{
Image = new ImageData();
Image.Link = JournalistController.GetArticleImageLink(controller, segment.Image.ItemImageId);
Image.Link = ArticleController.GetArticleImageLink(controller, segment.Image.ItemImageId);
}
}
public class BodyData
@@ -140,10 +141,9 @@ namespace Saxonite.Net.Core.Web.Online.Controllers
AjaxResponse_ActivityList responseData = new AjaxResponse_ActivityList();
// Validate session and referrer.
if (!ValidReferer)
return Forbid();
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
if (!ValidReferer) return Forbid();
if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData);
RefreshModel(model);
SaxSessionData_Online sessionData = SessionData_Online;
// User context.
@@ -167,12 +167,14 @@ namespace Saxonite.Net.Core.Web.Online.Controllers
activityItemIds.Add(itemData.ItemId);
ActivityResponseData activityData = new ActivityResponseData();
activityData.ItemType = itemData.ItemId.Type;
if (itemData.ItemId.Type == (int)Sax.DataItemType.JournalistArticle)
if (itemData.ItemId.Type == (int)Sax.DataItemType.Article)
activityData.ItemTypeClass = "Article";
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.ItemIdx = SysId.ToLong(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);
@@ -211,10 +213,9 @@ namespace Saxonite.Net.Core.Web.Online.Controllers
AjaxResponse_ActivityInfo responseData = new AjaxResponse_ActivityInfo();
// Validate session and referrer.
if (!ValidReferer)
return Forbid();
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
if (!ValidReferer) return Forbid();
if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData);
RefreshModel(model);
SaxSessionData_Online sessionData = SessionData_Online;
// Identify activity.
@@ -222,18 +223,18 @@ namespace Saxonite.Net.Core.Web.Online.Controllers
SysId itemId = model.ActivityIdx.ToSysId();
if (itemType <= 0 || !SysId.IsValid(itemId))
{
SaLogger.Warn(fn + " Activity type/Id inncorrectly specified." + getLogSuffix());
SaLogger.Warn(fn + " Activity type/Id inncorrectly specified." + GetLogSuffix());
return Json(responseData);
}
// Retrieve activity details.
if (itemType == Sax.DataItemType.JournalistArticle)
if (itemType == Sax.DataItemType.Article)
{
// Journalist article.
SaxJournalistArticle article = ActivityMgr.GetJournalistArticleById(itemId);
// Article.
SaxArticle article = ActivityMgr.GetArticleById(itemId);
if (article == null)
{
SaLogger.Warn(fn + " Article not found for Activity Id: " + itemId.ToString() + "." + getLogSuffix());
SaLogger.Warn(fn + " Article not found for Activity Id: " + itemId.ToString() + "." + GetLogSuffix());
return Json(responseData);
}
responseData.Article = new ActivityResponseData_Article(this, model, article);
@@ -244,7 +245,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers
SaxOrganisationOpportunity opportunity = ActivityMgr.GetOrganisationOpportunityById(itemId);
if (opportunity == null)
{
SaLogger.Warn(fn + " Opportunity not found for Activity Id: " + itemId.ToString() + "." + getLogSuffix());
SaLogger.Warn(fn + " Opportunity not found for Activity Id: " + itemId.ToString() + "." + GetLogSuffix());
return Json(responseData);
}
responseData.Opportunity = new ActivityResponseData_Opportunity(model, opportunity);
@@ -253,7 +254,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers
// Record activty viewing.
if (!ActivityConsole.RecordUserActivityViewed(SessionData.User.UserAccountId, new ItemId((short)itemType, itemId), DbUpdateInfo))
{
SaLogger.Warn(fn + " Failed to record Activity Viewing: " + ActivityConsole.ErrorStateMessage + "." + getLogSuffix());
SaLogger.Warn(fn + " Failed to record Activity Viewing: " + ActivityConsole.ErrorStateMessage + "." + GetLogSuffix());
return Json(responseData);
}
@@ -280,10 +281,9 @@ namespace Saxonite.Net.Core.Web.Online.Controllers
AjaxResponse_ActivityReaction responseData = new AjaxResponse_ActivityReaction();
// Validate session and referrer.
if (!ValidReferer)
return Forbid();
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
if (!ValidReferer) return Forbid();
if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData);
RefreshModel(model);
SaxSessionData_Online sessionData = SessionData_Online;
// Identify activity.
@@ -291,14 +291,14 @@ namespace Saxonite.Net.Core.Web.Online.Controllers
SysId itemId = model.ActivityIdx.ToSysId();
if (itemType <= 0 || !SysId.IsValid(itemId))
{
SaLogger.Warn(fn + " Activity type/Id inncorrectly specified." + getLogSuffix());
SaLogger.Warn(fn + " Activity type/Id inncorrectly specified." + GetLogSuffix());
return Json(responseData);
}
// Record activity reaction.
if (!ActivityConsole.RecordUserActivityReaction(SessionData.User.UserAccountId, new ItemId((short)itemType, itemId), model.GetActivityReaction(), DbUpdateInfo))
{
SaLogger.Warn(fn + " Failed to record Activity Reaction: " + ActivityConsole.ErrorStateMessage + "." + getLogSuffix());
SaLogger.Warn(fn + " Failed to record Activity Reaction: " + ActivityConsole.ErrorStateMessage + "." + GetLogSuffix());
return Json(responseData);
}
@@ -11,6 +11,7 @@ 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;
@@ -21,12 +22,12 @@ using System.Threading.Tasks;
namespace Saxonite.Net.Core.Web.Online.Controllers
{
//=======================================================================
// Online Journalist Controller - Ajax actions.
// Online Article Controller - Ajax actions.
//=======================================================================
partial class JournalistController
partial class ArticleController
{
//-------------------------------------------------------------------
// Journalist Article Content data.
// Article Content data.
private class ArticleContentData
{
public List<ArticleContentSegmentData> Segments { get; } = new List<ArticleContentSegmentData>();
@@ -48,10 +49,11 @@ namespace Saxonite.Net.Core.Web.Online.Controllers
}
//-------------------------------------------------------------------
// Journalist Article data.
// Article data.
private class ArticleData_List
{
public String Id { get; set; }
public String OrganisationId { get; set; }
public String Headline { get; set; }
public String Summary { get; set; }
public String RegionCaptionPrimary { get; set; }
@@ -70,9 +72,10 @@ namespace Saxonite.Net.Core.Web.Online.Controllers
public String PublishedOn_Calendar { get; set; }
public ArticleContentData Content { get; } = new ArticleContentData();
}
private void PopulateArticleResponseData(Model_Journalist model, SaxJournalistArticle article, ArticleData_List data)
private void PopulateArticleResponseData(Model_Article model, SaxArticle article, ArticleData_List data)
{
data.Id = SysId.ToString(article.Id);
data.OrganisationId = SysId.ToString(article.OrganisationId);
data.Headline = article.Headline;
data.Summary = article.Summary;
data.RegionCaptionPrimary = model.GetRegionCaption(article.RegionIdPrimary);
@@ -82,7 +85,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers
data.StatusCode = article.StatusCode;
data.StatusDesc = article.StatusDesc;
}
private void PopulateArticleResponseData(Model_Journalist model, SaxJournalistArticle article, ArticleData_Full data)
private void PopulateArticleResponseData(Model_Article model, SaxArticle article, ArticleData_Full data)
{
PopulateArticleResponseData(model, article, data as ArticleData_List);
data.RegionIdPrimary = SysId.ToString(article.RegionIdPrimary);
@@ -92,7 +95,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers
data.PublishedOn_Calendar = model.FormatDateForForm(article.PublishedAt);
// Article content.
SaxJournalistArticle.ContentData articleContent = SaxJournalistArticle.ExtractContent(article.BodyData);
SaxArticle.ContentData articleContent = SaxArticle.ExtractContent(article.BodyData);
foreach (var contentSegment in articleContent.Segments)
{
var segmentData = new ArticleContentSegmentData();
@@ -114,48 +117,83 @@ namespace Saxonite.Net.Core.Web.Online.Controllers
//-------------------------------------------------------------------
// 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<ArticleData_List> Articles { get; } = new List<ArticleData_List>();
/// <summary>Organisations that the user has rights to author articles for.</summary>
public List<OrganisationData> Organisations { get; set; }
}
//-------------------------------------------------------------------
[HttpPost]
[Route("Journalist/Ajax/GetArticleList")]
public IActionResult Ajax_GetArticleList(Model_Journalist model)
[Route("Article/Ajax/GetList")]
[Route("Article/Ajax/GetArticles")]
public IActionResult Ajax_GetArticleList(Model_Article model)
{
String fn = GetType().Name + ".Ajax_GetArticleList";
String messageTitle = Localiser["journalist.caption.articles"];
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);
if (!UserIsJournalist) return GetAjaxResult_GoHome(responseData);
SaxSessionData_Online sessionData = SessionData_Online;
SysId userAccountId = SessionData.User.UserAccountId;
RefreshModel(model);
// Organisations that the user has rights to author articles for.
foreach (SaxAccountMgr.OrganisationIdName organisation in AccountMgr.GetOrganisationsForUserRole(userAccountId, Sax.OrganisationRole.ArticleAdmin))
{
if (responseData.Organisations == null) responseData.Organisations = new List<OrganisationData>();
OrganisationData organisationData = new OrganisationData()
{
Id = model.EncryptId(organisation.Id),
Name = organisation.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);
// Item filter.
SaxJournalistArticle.Params prmArticle = new SaxJournalistArticle.Params();
prmArticle.UserAccountIdsJournalistIncluded.Add(sessionData.User.UserAccountId);
SaxArticle.Params prmArticle = new SaxArticle.Params();
prmArticle.UserAccountIdsAuthorIncluded.Add(sessionData.User.UserAccountId);
if (SysId.IsValid(organisationIdFilter))
prmArticle.OrganisationIdsIncluded.Add(organisationIdFilter);
prmArticle.ArticleFilter = model.Filter.Article;
prmArticle.StatusCat = SaxJournalistArticle.StatusCat.NotDeleted;
model.Paging.ItemTotal = (ulong)ActivityMgr.GetJournalistArticleCount(prmArticle);
prmArticle.StatusCat = SaxArticle.StatusCat.NotDeleted;
model.Paging.ItemTotal = (ulong)ActivityMgr.GetArticleCount(prmArticle);
model.PopulatePagingTo(prmArticle.ResultsPaging);
// Item columns.
ColumnDfn colId = ActivityMgr.GetJournalistArticleColumnDfn(SaxJournalistArticle.Column.Id);
ColumnDfn colHeadline = ActivityMgr.GetJournalistArticleColumnDfn(SaxJournalistArticle.Column.Headline);
ColumnDfn colSummary = ActivityMgr.GetJournalistArticleColumnDfn(SaxJournalistArticle.Column.Summary);
ColumnDfn colRegionIdPrimary = ActivityMgr.GetJournalistArticleColumnDfn(SaxJournalistArticle.Column.RegionIdPrimary);
ColumnDfn colSportIdPrimary = ActivityMgr.GetJournalistArticleColumnDfn(SaxJournalistArticle.Column.SportIdPrimary);
ColumnDfn colPublishedOn = ActivityMgr.GetJournalistArticleColumnDfn(SaxJournalistArticle.Column.PublishedAt_Date);
ColumnDfn colStatus = ActivityMgr.GetJournalistArticleColumnDfn(SaxJournalistArticle.Column.Status);
ColumnDfn colCreatedOn = ActivityMgr.GetJournalistArticleColumnDfn(SaxJournalistArticle.Column.CreateOn_Date);
ColumnDfn colId = ActivityMgr.GetArticleColumnDfn(SaxArticle.Column.Id);
ColumnDfn colOrganisationId = ActivityMgr.GetArticleColumnDfn(SaxArticle.Column.OrganisationId);
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);
List<ColumnDfn> columns = new List<ColumnDfn>()
{
colId, colHeadline, colSummary,
colId, colOrganisationId, colHeadline, colSummary,
colRegionIdPrimary, colSportIdPrimary, colPublishedOn,
colStatus, colCreatedOn
};
@@ -165,7 +203,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers
prmArticle.SortColumns.Add(colCreatedOn);
// Query items.
using (DbResultSet resultSet = ActivityMgr.ExecuteJournalistArticleQuery(columns, prmArticle))
using (DbResultSet resultSet = ActivityMgr.ExecuteArticleQuery(columns, prmArticle))
{
while (resultSet != null && resultSet.HasRows && resultSet.NextRecord())
{
@@ -173,6 +211,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers
ArticleData_List articleData = new ArticleData_List()
{
Id = values.GetString(colId.DataName),
OrganisationId = values.GetString(colOrganisationId.DataName),
Headline = values.GetString(colHeadline.DataName),
Summary = values.GetString(colSummary.DataName),
RegionCaptionPrimary = model.GetRegionCaption(values.GetString(colRegionIdPrimary.DataName).ToSysId()),
@@ -206,20 +245,20 @@ namespace Saxonite.Net.Core.Web.Online.Controllers
}
//-------------------------------------------------------------------
[HttpPost]
[Route("Journalist/Ajax/GetArticle")]
public IActionResult Ajax_GetArticle(Model_Journalist model)
[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["journalist.caption.article"];
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);
if (!UserIsJournalist)
return GetAjaxResult_GoHome(responseData);
if (!ValidReferer) return Forbid();
if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData);
SysId userAccountId = SessionData.User.UserAccountId;
RefreshModel(model);
// Identify item.
SysId articleId = model.ArticleId.ToSysId();
@@ -227,16 +266,21 @@ namespace Saxonite.Net.Core.Web.Online.Controllers
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.item.id.not.specified"]));
// Retrieve item.
SaxJournalistArticle article = ActivityMgr.GetJournalistArticleById(articleId);
SaxArticle article = ActivityMgr.GetArticleById(articleId);
if (article == null)
{
SaLogger.Warn(fn + " Failed to retrieve Journalist Article for Id: '" + model.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.UserAccountIdAuthor.Equals(userAccountId))
{
SaLogger.Warn(fn + " Inconsistent Article Author. Expected: " + userAccountId + ". Found: " + article.UserAccountIdAuthor);
return Json(responseData.SetMessage(messageTitle, Localiser["msg.error.system"]));
}
// Delete upload images.
SaxItemImage.Params prmItemImage = new SaxItemImage.Params();
prmItemImage.ItemIdsIncluded.Add(new ItemId((short)Sax.DataItemType.JournalistArticle, articleId));
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))
@@ -254,7 +298,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers
}
//-------------------------------------------------------------------
// Journalist - Upload Article Image.
// Article - Upload Image.
//-------------------------------------------------------------------
private class AjaxResponse_UploadArticleImage : AjaxResponse_Data
{
@@ -266,17 +310,18 @@ namespace Saxonite.Net.Core.Web.Online.Controllers
}
// Ajax request.
[HttpPost]
[Route("Journalist/Ajax/UploadArticleImage")]
public IActionResult UploadArticleImage(Model_Journalist model)
[Route("Article/Ajax/UploadImage")]
public IActionResult UploadArticleImage(Model_Article model)
{
String fn = GetType().Name + ".UploadArticleImage";
AjaxResponse_UploadArticleImage responseData = new AjaxResponse_UploadArticleImage(model);
String messageTitle = Localiser["journalist.caption.article"];
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);
// Validate number of images.
if (model.ArticleImage.IsEmpty())
@@ -307,7 +352,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers
// 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.ArticleId.ToSysId();
ItemId articleItemId = new ItemId((short)Sax.DataItemType.JournalistArticle, 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;
@@ -369,35 +414,59 @@ namespace Saxonite.Net.Core.Web.Online.Controllers
}
public static String GetArticleImageLink(SaxController_Online controller, SysId itemImageId)
{
String imageLink = "Image/Journalist/Article?";
String imageLink = "Image/Article?";
NameValueCollection parameters = new NameValueCollection();
parameters.Add(ImageController.ParamName_ItemImageId, SysId.ToString(itemImageId));
return imageLink + ImageController.ParamName_JournalistArticleImageEncrypted + "=" + SaxController_Online.EncryptOnlineParams(controller, parameters);
return imageLink + ImageController.ParamName_ArticleImageEncrypted + "=" + SaxController_Online.EncryptOnlineParams(controller, parameters);
}
//-------------------------------------------------------------------
// Store Article ajax request.
//-------------------------------------------------------------------
[HttpPost]
[Route("Journalist/Ajax/StoreArticle")]
public IActionResult Ajax_StoreArticle(Model_Journalist model)
[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["journalist.caption.article"];
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);
if (!UserIsJournalist) return GetAjaxResult_GoHome(responseData);
SaxSessionData_Online sessionData = SessionData_Online;
RefreshModel(model);
// Validate item properties.
model.ValidateArticleData(responseData);
if (responseData.HasControlErrors())
return Json(responseData);
// Validate Article against Id against 'Exists' flag.
// Validate User & Organisation.
SysId userAccountId = SessionData.User.UserAccountId;
SysId organisationId = model.ExtractSysId(model.ArticleOrganisationId);
if (SysId.IsValid(organisationId))
{
// Organisation specified - User must have Article Admin role.
if (!AccountMgr.UserHasOrganisationRole(userAccountId, organisationId, 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.ArticleId.ToSysId();
bool articleExists = StringUtil.ToBool(model.ArticleExists);
if (articleExists && !SysId.IsValid(articleId))
@@ -412,16 +481,30 @@ namespace Saxonite.Net.Core.Web.Online.Controllers
}
// Retrieve existing item.
SaxJournalistArticle article = ActivityMgr.GetJournalistArticleById(articleId);
SaxArticle article = ActivityMgr.GetArticleById(articleId);
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"]));
}
}
// Create new item if required.
if (article == null)
article = ActivityMgr.CreateJournalistArticle(sessionData.User.UserAccountId);
article = ActivityMgr.CreateArticle(sessionData.User.UserAccountId, organisationId);
// Populate item properties.
if (model.ArticleRegionId != null)
@@ -451,7 +534,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers
// If Article doesn't yet exist, create it now so that images can be assigned.
if (!SysId.IsValid(article.Id))
{
if (!ActivityMgr.InsertJournalistArticle(article, DbUpdateInfo))
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"]));
@@ -461,7 +544,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers
// Content.
if (model.ArticleContentSegmentBodies != null)
{
SaxJournalistArticle.ContentData articleContent = new SaxJournalistArticle.ContentData();
SaxArticle.ContentData articleContent = new SaxArticle.ContentData();
for (int segmentIdx = 0; segmentIdx < model.ArticleContentSegmentBodies.Count; segmentIdx++)
{
String segmentBody = model.ArticleContentSegmentBodies[segmentIdx];
@@ -505,11 +588,11 @@ namespace Saxonite.Net.Core.Web.Online.Controllers
SaLogger.Warn(fn + " Failed to identify article image. ItemImageId: " + itemImageId.ToInt() + ". SessionIdx: " + sessionData.SessionIdx + ". ImageIdx: " + imageIdx + ".");
}
}
article.BodyData = SaxJournalistArticle.GetContentData(articleContent);
article.BodyData = SaxArticle.GetContentData(articleContent);
}
// Update the Article.
if (!ActivityMgr.UpdateJournalistArticle(article, DbUpdateInfo))
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"]));
@@ -1,7 +1,11 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Sarand.Net.Standard.Base.Bus;
using Saxonite.Net.Core.Web.Base.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.Mgr;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -12,11 +16,11 @@ namespace Saxonite.Net.Core.Web.Online.Controllers
//=======================================================================
// Online Journalist Controller.
//=======================================================================
public partial class JournalistController : SaxController_Online
public partial class ArticleController : SaxController_Online
{
//-------------------------------------------------------------------
// Construction.
public JournalistController(SaxLocaliser localiser, ILogger<HomeController> logger) :
public ArticleController(SaxLocaliser localiser, ILogger<HomeController> logger) :
base(localiser, logger)
{
}
@@ -26,25 +30,24 @@ namespace Saxonite.Net.Core.Web.Online.Controllers
public IActionResult Index()
{
// Validate session.
if (!ValidUserSession || !UserIsJournalist)
if (!ValidUserSession)
return RedirectToAction("", "");
// No 'Index' view as yet - default to 'Articles' view.
//Model_Journalist model = new Model_Journalist(Localiser);
//return View(model);
return RedirectToAction("Articles", "Journalist");
}
//-------------------------------------------------------------------
[HttpGet]
public IActionResult Articles()
{
// Validate session.
if (!ValidUserSession || !UserIsJournalist)
// User must either be a Journalist or have Article Admin rights with at least one organisation.
List<SaxAccountMgr.OrganisationIdName> organisations = AccountMgr.GetOrganisationsForUserRole(SessionData.User.UserAccountId, Sax.OrganisationRole.ArticleAdmin);
if (organisations.Count < 1 && !UserIsJournalist)
return RedirectToAction("", "");
// Paging.
Model_Journalist model = new Model_Journalist(Localiser);
// Model.
Model_Article model = new Model_Article(Localiser);
model.UserCanAuthorOrganisationArticles = organisations.Count > 0;
model.UserCanAuthorJournalistArticles = UserIsJournalist;
if (organisations.Count > 0)
{
// Default filter to first organisation.
model.Filter.OrganisationId = model.EncryptId(organisations[0].Id);
model.Filter.OrganisationCaption = organisations[0].Name;
}
model.Paging.PageNo = 1;
model.Paging.PageLength = 6;
@@ -92,10 +92,8 @@ namespace Saxonite.Net.Core.Web.Online.Controllers
String fn = GetType().Name + ".GetAccountContactImage";
// Validate session and referrer.
if (!ValidReferer)
return Forbid();
if (!ValidUserSession)
return Forbid();
if (!ValidReferer) return Forbid();
if (!ValidUserSession) return Forbid();
ItemId userItemId = new ItemId((short)Sax.DataItemType.UserAccount, userAccountId);
// Retrieve account contact images.
@@ -120,28 +118,29 @@ namespace Saxonite.Net.Core.Web.Online.Controllers
}
//-------------------------------------------------------------------
// Journalist Article image
// Article image
//-------------------------------------------------------------------
public static String ParamName_JournalistArticleImageEncrypted { get; } = "jaie";
public static String ParamName_ArticleImageEncrypted { get; } = "aie";
public static String ParamName_ItemImageId { get; } = "iii";
/// <summary>
/// Get the specified Journalist Article Image.
/// Get the specified Article Image.
/// <para>Method parameter name (aie) must match name of encrypted parameter.</para>
/// <para>Encrypted parameters:</para>
/// <para>- iii: item Image Id.</para>
/// </summary>
[HttpGet]
[Route("Image/Journalist/Article")]
public IActionResult GetJournalistArticleImage(String jaie)
[Route("Image/Article")]
public IActionResult GetArticleImage(String aie)
{
String fn = GetType().Name + ".GetJournalistArticleImage";
String fn = GetType().Name + ".GetArticleImage";
// Validate session and referrer.
if (!ValidReferer) return Forbid();
if (!ValidUserSession) return Forbid();
ItemId userItemId = new ItemId((short)Sax.DataItemType.UserAccount, SessionData.User.UserAccountId);
// EXtract encrypted parameters.
NameValueCollection parameters = ExtractOnlineParams(jaie);
// Extract encrypted parameters.
NameValueCollection parameters = ExtractOnlineParams(aie);
if (parameters.Count<1) return Forbid();
// Item Image.
@@ -102,6 +102,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers
// Validate session and referrer.
if (!ValidReferer) return Forbid();
if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData);
RefreshModel(model);
// Organisation.
SysId organisationId = model.Filter.OrganisationId.ToSysId();
@@ -167,7 +168,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers
NamedValue.Map values = resultSet.GetRecordValues();
MemberData_List memberData = new MemberData_List()
{
Id = values.GetString(colId.DataName),
Id = values.GetLong(colId.DataName).ToString(),
UserAccountId = values.GetString(colUserAccountId.DataName),
EmailAddress = values.GetString(colEmailAddress.DataName),
DisplayName = AccountMgr.GetUserOrganisationDataValue(colDisplayName, values).ToString(),
@@ -222,6 +223,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers
// Validate session and referrer.
if (!ValidReferer) return Forbid();
if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData);
RefreshModel(model);
// Identify item.
SysId memberId = model.MemberId.ToSysId();
@@ -295,6 +297,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers
// Validate session and referrer.
if (!ValidReferer) return Forbid();
if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData);
RefreshModel(model);
// Validate item properties.
model.ValidateMemberData(responseData);
@@ -519,6 +522,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers
// Validate session and referrer.
if (!ValidReferer) return Forbid();
if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData);
RefreshModel(model);
// Record disposable items as they are created so all can be disposed when needed.
using (DisposableCollection<SacsHierarchyItem> disposableItems = new DisposableCollection<SacsHierarchyItem>())
@@ -609,6 +613,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers
// Replace dynamic tag for link back to change password page.
SaEmailConsole.ReplaceDynamicEmailTag(emailData, "[#Link.RequestPassword]", SecurityController.GenerateRequestPasswordLink(this, userAccount.Id, userAccount.EmailAddress, ""));
SaEmailConsole.ReplaceDynamicEmailTag(emailData, "[#Link.SignIn]", GetRedirectLocation(RedirectLocation.WebAppBase) + "Security/SignIn");
SaEmailConsole.ReplaceDynamicEmailTag(emailData, "[#Link.ExpiresMinutes]", SecurityController.GetRequestPasswordEmailLinkExpiresMinutes(this).ToString());
}
@@ -633,6 +638,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers
// Validate session and referrer.
if (!ValidReferer) return Forbid();
if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData);
RefreshModel(model);
// Record disposable items as they are created so all can be disposed when needed.
using (DisposableCollection<SacsHierarchyItem> disposableItems = new DisposableCollection<SacsHierarchyItem>())
@@ -92,10 +92,9 @@ namespace Saxonite.Net.Core.Web.Online.Controllers
AjaxResponse_OpportunityList responseData = new AjaxResponse_OpportunityList(model);
// Validate session and referrer.
if (!ValidReferer)
return Forbid();
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
if (!ValidReferer) return Forbid();
if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData);
RefreshModel(model);
// Organisation.
SysId organisationId = model.Filter.OrganisationId.ToSysId();
@@ -170,7 +169,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers
NamedValue.Map values = resultSet.GetRecordValues();
OpportunityData_List opportunityData = new OpportunityData_List()
{
Id = values.GetString(colId.DataName),
Id = values.GetLong(colId.DataName).ToString(),
TypeCode = values.GetString(colOpportunityTypeCode.DataName),
TypeDesc = Localiser.GetOpportunityTypeCaption((Sax.OpportunityType)OpportunityTypeCodes.GetTypeForCode(values.GetString(colOpportunityTypeCode.DataName))),
Position = values.GetString(colPosition.DataName),
@@ -218,10 +217,9 @@ namespace Saxonite.Net.Core.Web.Online.Controllers
AjaxResponse_Opportunity responseData = new AjaxResponse_Opportunity(model);
// Validate session and referrer.
if (!ValidReferer)
return Forbid();
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
if (!ValidReferer) return Forbid();
if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData);
RefreshModel(model);
// Identify item.
SysId opportunityId = model.OpportunityId.ToSysId();
@@ -271,10 +269,9 @@ namespace Saxonite.Net.Core.Web.Online.Controllers
AjaxResponse_Opportunity responseData = new AjaxResponse_Opportunity(model);
// Validate session and referrer.
if (!ValidReferer)
return Forbid();
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
if (!ValidReferer) return Forbid();
if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData);
RefreshModel(model);
// Validate item properties.
model.ValidateOpportunityData(responseData);
@@ -41,8 +41,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers
AjaxResponse_RequestOrganisation responseData = new AjaxResponse_RequestOrganisation(model);
// Validate session and referrer. User does not need to be logged in.
if (!ValidReferer)
return Forbid();
if (!ValidReferer) return Forbid();
SaxSessionData_Online sessionData = SessionData_Online;
if (!model.ValidateOrganisationRequestData(responseData))
@@ -52,13 +51,13 @@ namespace Saxonite.Net.Core.Web.Online.Controllers
SaxRegion regionCountry = Internals.GetRegion(model.OrganisationRegionIdCountry.ToSysId());
if (SysId.IsValid(model.OrganisationRegionIdCountry.ToSysId()) && regionCountry == null)
{
SaLogger.Warn(fn + " Country region not found. Id: " + model.OrganisationRegionIdCountry + ". " + getLogSuffix());
SaLogger.Warn(fn + " Country region not found. Id: " + model.OrganisationRegionIdCountry + ". " + GetLogSuffix());
return Json(responseData.SetGoHome(messageTitle, Localiser["msg.error.system"]));
}
SaxRegion regionProvince = Internals.GetRegion(model.OrganisationAddressRegionIdProvince.ToSysId());
if (SysId.IsValid(model.OrganisationAddressRegionIdProvince.ToSysId()) && regionProvince == null)
{
SaLogger.Warn(fn + " Province region not found. Id: " + model.OrganisationAddressRegionIdProvince + ". " + getLogSuffix());
SaLogger.Warn(fn + " Province region not found. Id: " + model.OrganisationAddressRegionIdProvince + ". " + GetLogSuffix());
return Json(responseData.SetGoHome(messageTitle, Localiser["msg.error.system"]));
}
@@ -71,7 +70,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers
// hence 'System' error response and nothing more specific.
if (!StringUtil.IsEmpty(model.ContactEmailAddress))
{
SaLogger.Warn(fn + " Contact Email supplied when user logged in. " + getLogSuffix());
SaLogger.Warn(fn + " Contact Email supplied when user logged in. " + GetLogSuffix());
return Json(responseData.SetGoHome(messageTitle, Localiser["msg.error.system"]));
}
// Retrieve user account.
@@ -52,10 +52,9 @@ namespace Saxonite.Net.Core.Web.Online.Controllers
AjaxResponse_OrganisationList responseData = new AjaxResponse_OrganisationList(model);
// Validate session and referrer.
if (!ValidReferer)
return Forbid();
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
if (!ValidReferer) return Forbid();
if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData);
RefreshModel(model);
// Query columns.
ColumnDfn colOrganisationId = AccountMgr.GetOrganisationColumnDfn(SaxOrganisation.Column.Id);
@@ -126,10 +125,9 @@ namespace Saxonite.Net.Core.Web.Online.Controllers
AjaxResponse_MemberOrganisationList responseData = new AjaxResponse_MemberOrganisationList(model);
// Validate session and referrer.
if (!ValidReferer)
return Forbid();
if (!ValidUserSession)
return GetAjaxResult_InvalidSession(responseData);
if (!ValidReferer) return Forbid();
if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData);
RefreshModel(model);
// Query columns.
ColumnDfn colOrganisationId = AccountMgr.GetUserOrganisationColumnDfn(SaxUserOrganisation.Column.OrganisationId);
@@ -43,8 +43,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers
AjaxResponse_SportList responseData = new AjaxResponse_SportList();
// Validate session and referrer.
if (!ValidReferer)
return Forbid();
if (!ValidReferer) return Forbid();
// User doesn't have to be logged in, eg. to register organisation.
//if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData);
@@ -75,9 +74,8 @@ namespace Saxonite.Net.Core.Web.Online.Controllers
AjaxResponse_RegionList responseData = new AjaxResponse_RegionList();
// Validate session and referrer.
if (!ValidReferer)
return Forbid();
// User doesn't have to be logged in, eg. to register organisation.
if (!ValidReferer) return Forbid();
// User doesn't have to be logged in to query regions, eg. for organisation request.
//if (!ValidUserSession) return GetAjaxResult_InvalidSession(responseData);
// Populate regions.
@@ -258,7 +258,13 @@ namespace Saxonite.Net.Core.Web.Online.Controllers
//-------------------------------------------------------------------
// Logging.
//-------------------------------------------------------------------
protected String getLogSuffix()
/// <summary>
/// Log suffix:
/// <para>- User's email address.</para>
/// <para>- Prefixed with space.</para>
/// </summary>
/// <returns></returns>
protected String GetLogSuffix()
{
List<String> logSuffix = new List<string>();
SaxSessionData_Online sessionData = SessionData_Online;
@@ -43,8 +43,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers
String messageTitle = Localiser["signin.caption"];
// Validate referrer.
if (!ValidReferer)
return Forbid();
if (!ValidReferer) return Forbid();
// Validate Sign-in data.
model.ValidateSignInData(responseData);
@@ -81,10 +80,9 @@ namespace Saxonite.Net.Core.Web.Online.Controllers
AjaxResponse_KeepLoggedIn responseData = new AjaxResponse_KeepLoggedIn();
// Validate session and referrer.
if (!ValidReferer)
return Forbid();
if (!ValidUserSession)
return Json(responseData.SetGoHome());
if (!ValidReferer) return Forbid();
if (!ValidUserSession) return Json(responseData.SetGoHome());
RefreshModel(model);
if (!AppConfig.Security.SignIn.KeepLoggedIn.Allowed)
return Json(responseData);
@@ -155,10 +153,9 @@ namespace Saxonite.Net.Core.Web.Online.Controllers
String messageTitle = Localiser["signin.caption.signout"];
// Validate session and referrer.
if (!ValidReferer)
return Forbid();
if (!ValidUserSession)
return Json(responseData.SetSessionExpired(messageTitle, Localiser["security.msg.warn.sessionExpired"]));
if (!ValidReferer) return Forbid();
if (!ValidUserSession) return Json(responseData.SetSessionExpired(messageTitle, Localiser["security.msg.warn.sessionExpired"]));
RefreshModel(model);
// Retrieve user account - in case sign-out needs to be recorded.
SaxUserAccount userAccount = AccountMgr.GetUserAccountById(SessionData_Online.User.UserAccountId);
@@ -194,8 +191,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers
public ActionResult Ajax_ScorePassword(Model_Security model)
{
// Validate referrer.
if (!ValidReferer)
return Forbid();
if (!ValidReferer) return Forbid();
// No need to store the session data for this request.
StoreSessionData = false;
@@ -225,8 +221,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers
String messageTitle = Localiser["security.caption.change.password"];
// Validate referrer.
if (!ValidReferer)
return Forbid();
if (!ValidReferer) return Forbid();
// If 'Request Password' encrypted params (from email link) are present,
// Session is not required and password can be set directly against the user.
@@ -324,8 +319,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers
String messageTitle = Localiser["security.caption.request.password"];
// Validate referrer.
if (!ValidReferer)
return Forbid();
if (!ValidReferer) return Forbid();
// Validate data.
model.ValidateRequestPasswordData(responseData);
@@ -378,11 +372,8 @@ namespace Saxonite.Net.Core.Web.Online.Controllers
private static String ParamName_EmailAddress { get; } = "ema";
private static String ParamName_ExpiresAt { get; } = "exp";
private static String ParamName_Token { get; } = "tok";
// NB IV value is generated from the key and fixed so that it doesn't change when the application restarts.
// IV Must be regenerated if key is changed.
private static String Base64Key_RequestPassword { get; } = SacsEncryptionHelper.EncodePlainKey("{~Saxonite@@Request^Password=}", SacsEncryptionHelper.KeyLength);
private static String Base64IV_RequestPassword_Generated { get; } = SacsEncryptionHelper.InitSymmetricEncryptionIV(Base64Key_RequestPassword);
private static String Base64IV_RequestPassword { get; } = "1VAWYTW6d72n/6HDNhbqow==";
// Request Password encryption parameters (key & IV) are defined in the base Controller
// so that they can be shared with the Adnin application.
internal static String GenerateRequestPasswordLink(SaxController_Online controller, SysId userAccountId, String emailAddress, String requestPasswordToken)
{
NameValueCollection parameters = new NameValueCollection();
@@ -392,7 +383,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers
parameters.Add(ParamName_Token, requestPasswordToken);
String link =
controller.GetRedirectLocation(RedirectLocation.WebAppBase) + "Security/ChangePassword?" +
ParamName_RequestPasswordEncrypted + "=" + HttpUtility.UrlEncode(controller.EncryptParameters(parameters, Base64IV_RequestPassword, Base64Key_RequestPassword));
ParamName_RequestPasswordEncrypted + "=" + HttpUtility.UrlEncode(EncryptParameters(parameters, Base64IV_RequestPassword_Online, Base64Key_RequestPassword_Online));
return link;
}
internal static int GetRequestPasswordEmailLinkExpiresMinutes(SaxController_Online controller)
@@ -445,7 +436,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers
RequestPasswordParams requestPasswordParams = new RequestPasswordParams();
if (!StringUtil.IsEmpty(encryptedParams))
{
NameValueCollection linkParams = DecryptParameters(encryptedParams, Base64IV_RequestPassword, Base64Key_RequestPassword);
NameValueCollection linkParams = DecryptParameters(encryptedParams, Base64IV_RequestPassword_Online, Base64Key_RequestPassword_Online);
requestPasswordParams.UserAccountId = new SysId(StringUtil.ToInt(linkParams.Get(ParamName_UserAccountId)));
requestPasswordParams.EmailAddress = linkParams.Get(ParamName_EmailAddress);
requestPasswordParams.ExpiresAt = DateUtil.ParseXmlDateTime(linkParams.Get(ParamName_ExpiresAt));
@@ -15,29 +15,45 @@ using System.Threading.Tasks;
namespace Saxonite.Net.Core.Web.Online.Models
{
//=======================================================================
public class Model_Journalist : SaxModel_Online
public class Model_Article : SaxModel_Online
{
//-------------------------------------------------------------------
// Construction.
public Model_Journalist(SaxLocaliser localiser) :
public Model_Article(SaxLocaliser localiser) :
base(localiser)
{
AdditionalFiles_CSS.Add("css/sax-jrn-art.css");
AdditionalFiles_JS.Add("js/sax-jrn-art.js");
AdditionalFiles_CSS.Add("css/sax-art.css");
AdditionalFiles_JS.Add("js/sax-art.js");
}
//-------------------------------------------------------------------
// List configuration.
public class FilterData
{
public String OrganisationId { get; set; }
public String OrganisationCaption { get; set; }
public String Article { get; set; }
}
public FilterData Filter { get; } = new FilterData();
/// <summary>
/// User has rights to author articles for at least one organisation.
/// <para>- Output only - not returned with form data.</para>
/// <para>- Only populated for initial page construction.</para>
/// </summary>
public bool UserCanAuthorOrganisationArticles { get; set; }
/// <summary>
/// User has rights to author articles as a journalist.
/// <para>- Output only - not returned with form data.</para>
/// <para>- Only populated for initial page construction.</para>
/// </summary>
public bool UserCanAuthorJournalistArticles { get; set; }
//-------------------------------------------------------------------
// Article properties.
public String ArticleId { get; set; }
public String ArticleExists { get; set; }
// Organisation.
public String ArticleOrganisationId { get; set; }
// Sport.
public String ArticleSportId { get; set; }
public String ArticleSportCaption { get; set; }
@@ -75,8 +91,8 @@ namespace Saxonite.Net.Core.Web.Online.Models
responseData.SetControlValue(nameof(ArticleHeadline), ArticleHeadline);
if (StringUtil.IsEmpty(ArticleHeadline))
responseData.SetControlError(nameof(ArticleHeadline), Localiser["msg.validation.mandatory.value"]);
else if (StringUtil.GetLength(ArticleHeadline) > GetControlMaxLength(SaxDbDfn_Core.ColumnIdx.JournalistArticle_Headline))
responseData.SetControlError(nameof(ArticleHeadline), Localiser["msg.validation.maximum.length", GetControlMaxLength(SaxDbDfn_Core.ColumnIdx.JournalistArticle_Headline)]);
else if (StringUtil.GetLength(ArticleHeadline) > GetControlMaxLength(SaxDbDfn_Core.ColumnIdx.Article_Headline))
responseData.SetControlError(nameof(ArticleHeadline), Localiser["msg.validation.maximum.length", GetControlMaxLength(SaxDbDfn_Core.ColumnIdx.Article_Headline)]);
}
// Article summary.
@@ -85,8 +101,8 @@ namespace Saxonite.Net.Core.Web.Online.Models
responseData.SetControlValue(nameof(ArticleSummary), ArticleSummary);
if (StringUtil.IsEmpty(ArticleSummary))
responseData.SetControlError(nameof(ArticleSummary), Localiser["msg.validation.mandatory.value"]);
else if (StringUtil.GetLength(ArticleSummary) > GetControlMaxLength(SaxDbDfn_Core.ColumnIdx.JournalistArticle_Summary))
responseData.SetControlError(nameof(ArticleSummary), Localiser["msg.validation.maximum.length", GetControlMaxLength(SaxDbDfn_Core.ColumnIdx.JournalistArticle_Summary)]);
else if (StringUtil.GetLength(ArticleSummary) > GetControlMaxLength(SaxDbDfn_Core.ColumnIdx.Article_Summary))
responseData.SetControlError(nameof(ArticleSummary), Localiser["msg.validation.maximum.length", GetControlMaxLength(SaxDbDfn_Core.ColumnIdx.Article_Summary)]);
}
// Article sport.
@@ -123,7 +139,7 @@ namespace Saxonite.Net.Core.Web.Online.Models
{
if (StringUtil.IsEmpty(ArticleStatusCode))
responseData.SetControlError(nameof(ArticleStatusCode), Localiser["msg.validation.mandatory.value"]);
else if (!SaxJournalistArticle.ValidStatuses.Contains(ArticleStatus))
else if (!SaxArticle.ValidStatuses.Contains(ArticleStatus))
responseData.SetControlError(nameof(ArticleStatusCode), Localiser["msg.validation.invalid.value"]);
}
@@ -1,11 +1,16 @@
using Sarand.Net.Standard.Base.Bus;
using Sarand.Net.Standard.Base.App;
using Sarand.Net.Standard.Base.Bus;
using Sarand.Net.Standard.Base.Helpers;
using Sarand.Net.Standard.Base.Utils;
using Saxonite.Net.Core.Web.Base.App;
using Saxonite.Net.Core.Web.Base.Controllers;
using Saxonite.Net.Core.Web.Base.Models;
using Saxonite.Net.Core.Web.Online.App;
using Saxonite.Net.Standard.Base.App;
using Saxonite.Net.Standard.Base.Bus;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Threading.Tasks;
@@ -16,10 +21,9 @@ namespace Saxonite.Net.Core.Web.Online.Models
{
//-------------------------------------------------------------------
// Data members.
private bool userCanManageOrganisationMembers = false;
private bool userCanManageOrganisationMembersChecked = false;
private bool userCanManageOrganisationOpportunities = false;
private bool userCanManageOrganisationOpportunitiesChecked = false;
private bool? userCanManageOrganisationMembers = null;
private bool? userCanManageOrganisationArticles = null;
private bool? userCanManageOrganisationOpportunities = null;
//-------------------------------------------------------------------
// Protected construction.
@@ -30,14 +34,19 @@ namespace Saxonite.Net.Core.Web.Online.Models
//-------------------------------------------------------------------
// Session.
//-------------------------------------------------------------------
public SaxSessionData_Online SessionData_Online { get { return SessionData as SaxSessionData_Online; } }
// Menu options.
public bool MembersAllowed { get { return UserCanManageOrganisationMembers; } }
public bool ArticlesAllowed { get { return UserIsJournalist || UserCanManageOrganisationArticles; } }
public bool OpportunitiesAllowed { get { return UserCanManageOrganisationOpportunities; } }
// Security profile.
public bool UserIsJournalist { get { return SessionData != null && SessionData.User != null && SessionData.User.IsJournalist; } }
public bool UserCanManageOrganisationMembers
{
get
{
if (!userCanManageOrganisationMembersChecked && ValidUserSession)
if (userCanManageOrganisationMembers == null && ValidUserSession)
{
SaxUserOrganisation.Params prmUserOrganisation = new SaxUserOrganisation.Params();
prmUserOrganisation.UserAccountIdsIncluded.Add(SessionData.User.UserAccountId);
@@ -45,16 +54,31 @@ namespace Saxonite.Net.Core.Web.Online.Models
prmUserOrganisation.OrganisationRoles.Add(Sax.OrganisationRole.OpportunityAdmin);
prmUserOrganisation.StatusCat = SaxUserOrganisation.StatusCat.ActiveOnly;
userCanManageOrganisationMembers = (AccountMgr.GetUserOrganisationCount(prmUserOrganisation) > 0);
userCanManageOrganisationMembersChecked = true;
}
return userCanManageOrganisationMembers;
return userCanManageOrganisationMembers == true ? true : false;
}
}
public bool UserCanManageOrganisationArticles
{
get
{
if (userCanManageOrganisationArticles == null && ValidUserSession)
{
SaxUserOrganisation.Params prmUserOrganisation = new SaxUserOrganisation.Params();
prmUserOrganisation.UserAccountIdsIncluded.Add(SessionData.User.UserAccountId);
prmUserOrganisation.OrganisationMembersOnly = true;
prmUserOrganisation.OrganisationRoles.Add(Sax.OrganisationRole.ArticleAdmin);
prmUserOrganisation.StatusCat = SaxUserOrganisation.StatusCat.ActiveOnly;
userCanManageOrganisationArticles = (AccountMgr.GetUserOrganisationCount(prmUserOrganisation) > 0);
}
return userCanManageOrganisationArticles == true ? true : false;
}
}
public bool UserCanManageOrganisationOpportunities
{
get
{
if (!userCanManageOrganisationOpportunitiesChecked && ValidUserSession)
if (userCanManageOrganisationOpportunities == null && ValidUserSession)
{
SaxUserOrganisation.Params prmUserOrganisation = new SaxUserOrganisation.Params();
prmUserOrganisation.UserAccountIdsIncluded.Add(SessionData.User.UserAccountId);
@@ -62,12 +86,57 @@ namespace Saxonite.Net.Core.Web.Online.Models
prmUserOrganisation.OrganisationRoles.Add(Sax.OrganisationRole.OpportunityAdmin);
prmUserOrganisation.StatusCat = SaxUserOrganisation.StatusCat.ActiveOnly;
userCanManageOrganisationOpportunities = (AccountMgr.GetUserOrganisationCount(prmUserOrganisation) > 0);
userCanManageOrganisationOpportunitiesChecked = true;
}
return userCanManageOrganisationOpportunities;
return userCanManageOrganisationOpportunities == true ? true : false;
}
}
//-------------------------------------------------------------------
// Item Id Obfuscation.
//-------------------------------------------------------------------
public SysId ExtractSysId(String idx)
{
String fn = GetType().Name + ".ExtractSysId";
if (StringUtil.IsEmpty(idx)) return null;
// Numeric values are assumed to be unencrypted Ids.
if (StringUtil.IsNumeric(idx))
return idx.ToSysId();
try
{
// Attempt to decrypt.
SysId id = DecryptId(idx);
if (SysId.IsValid(id))
return id;
}
catch (Exception exc)
{
SaLogger.Warn(fn + " " + ExceptionUtil.GetErrorStateMessage(exc));
}
return null;
}
//-------------------------------------------------------------------
// Id encryption.
// To obfuscate Id values that are sent to and from the browser.
// NB IV value can be reset evert time the application restarts.
public static String Base64Key_IdObfuscation = SacsEncryptionHelper.EncodePlainKey("|SaxOnIte.Onl1n3-iD+0bfuscAtion|", SacsEncryptionHelper.KeyLength);
public static String Base64IV_IdObfuscation = SacsEncryptionHelper.InitSymmetricEncryptionIV(Base64Key_IdObfuscation);
public static String ParamName_IdObfuscation_Id { get; } = "iob";
public String EncryptId(SysId id)
{
NameValueCollection idObfuscationParams = new NameValueCollection();
idObfuscationParams.Add(ParamName_IdObfuscation_Id, SysId.ToString(id));
return SaxController_Base.EncryptParameters(idObfuscationParams, Base64IV_IdObfuscation, Base64Key_IdObfuscation);
}
public SysId DecryptId(String encryptedParams)
{
NameValueCollection idObfuscationParams = SaxController_Base.DecryptParameters(encryptedParams, Base64IV_IdObfuscation, Base64Key_IdObfuscation);
return idObfuscationParams.Get(ParamName_IdObfuscation_Id).ToSysId();
}
//-------------------------------------------------------------------
// Reference Data.
//-------------------------------------------------------------------
@@ -1,4 +1,4 @@
@model Model_Journalist
@model Model_Article
@inject SaxLocaliser Localiser
@{
ViewData["Title"] = "Journalist - Articles";
@@ -10,7 +10,7 @@
{
<div class="sax-panel-filter">
<div class="sax-header row">
<div class="col"><div class="sax-caption">@Localiser["journalist.caption.articles"]</div></div>
<div class="col"><div class="sax-caption">@Localiser["article.caption.articles"]</div></div>
<div class="fixed row">
<div>
<partial name="~/Views/Common/ResultsPaging.cshtml" />
@@ -20,6 +20,17 @@
</div>
</div>
<div class="sax-content">
@if (Model.UserCanAuthorOrganisationArticles)
{
<div class="sax-control row OrganisationFilter">
<div class="col-sm-4"><div class="sax-label text-left text-sm-right">@Localiser["label.organisation"]</div></div>
<div class="col-sm-8 row">
@Html.HiddenFor(model => model.Filter.OrganisationId, new { @id = "ifFilterOrganisationId", @class = "FilterOrganisationId" })
<div class="col"><input type="text" id="ifFilterOrganisation" class="FilterOrganisation sax-input" readonly="readonly" value="@(Model.Filter.OrganisationCaption)" /></div>
<div class="fixed"><button class="sax-button" onclick="sax.selectFilterOrganisation()">@Localiser["label.select"]</button></div>
</div>
</div>
}
<div class="row">
<div class="col-sm-4"><div class="sax-label text-left text-sm-right">@Localiser["label.search"]</div></div>
<div class="col-sm-8"><input type="search" name="Filter.Article" id="ifArticleFilter" class="ArticleFilter sax-input" maxlength="64" /></div>
@@ -59,5 +70,6 @@
</div>
<partial name="~/Views/Common/OrganisationSelect.cshtml" />
<partial name="Panel-ArticleDialog.cshtml" />
@@ -1,12 +1,12 @@
@model Model_Journalist
@model Model_Article
@inject SaxLocaliser Localiser
@{
/* Articles - Article dialog panel */
}
<script type="text/javascript">
sax.dlg.title.createArticle = '@Localiser.GetJavascriptText("journalist.caption.article.create")';
sax.dlg.title.updateArticle = '@Localiser.GetJavascriptText("journalist.caption.article.update")';
sax.dlg.title.createArticle = '@Localiser.GetJavascriptText("article.caption.article.create")';
sax.dlg.title.updateArticle = '@Localiser.GetJavascriptText("article.caption.article.update")';
sax.dlg.title.selectRegion = '@Localiser.GetJavascriptText("reference.caption.region.select")';
sax.dlg.title.selectSport = '@Localiser.GetJavascriptText("reference.caption.sport.select")';
</script>
@@ -20,18 +20,35 @@
{
@Html.HiddenFor(model => model.ArticleId, new { @id = "ifArticleId", @class = "ArticleId" })
@Html.HiddenFor(model => model.ArticleExists, new { @id = "ifArticleExists", @class = "ArticleExists" })
@Html.HiddenFor(model => model.ArticleOrganisationId, new { @id = "ifArticleOrganisationId", @class = "ArticleOrganisationId" })
<div class="sax-article ArticleHeader">
<div class="sax-article-panel sax-article-header">
@* Organisation *@
<div class="sax-form-control ArticleOrganisation">
<div class="row">
<div class="col-sm-4">
<div class="sax-label right pt4 pr8">
<label for="ifArticleOrganisation"><span>@Localiser["label.organisation"]</span></label>
</div>
</div>
<div class="col-sm">
<div class="sax-control">
<input type="text" id="ifArticleOrganisation" class="ArticleOrganisation sax-input" readonly="readonly" />
</div>
</div>
</div>
</div>
@* Article headline *@
<div class="sax-form-control ArticleHeadline">
<div class="sax-label">
<label for="ifArticleHeadline"><span><span class="required">*</span> @Localiser["label.headline"]</span></label>
<label for="ifArticleHeadline"><span>@Localiser["label.headline"]</span><span class="required">&nbsp;*</span></label>
</div>
<div class="sax-control">
@Html.TextBoxFor(model => model.ArticleHeadline, new { @id = "ifArticleHeadline", @class = "ArticleHeadline sax-input", @maxlength = @Model.GetControlMaxLength(SaxDbDfn_Core.ColumnIdx.JournalistArticle_Headline) })
@Html.TextBoxFor(model => model.ArticleHeadline, new { @id = "ifArticleHeadline", @class = "ArticleHeadline sax-input", @maxlength = @Model.GetControlMaxLength(SaxDbDfn_Core.ColumnIdx.Article_Headline) })
</div>
<div class="sax-message"></div>
</div>
@@ -39,10 +56,10 @@
@* Article summary *@
<div class="sax-form-control ArticleSummary">
<div class="sax-label">
<label for="ifArticleSummary"><span><span class="required">*</span> @Localiser["label.summary"]</span></label>
<label for="ifArticleSummary"><span>@Localiser["label.summary"]</span><span class="required">&nbsp;*</span></label>
</div>
<div class="sax-control">
@Html.TextAreaFor(model => model.ArticleSummary, new { @id = "ifArticleSummary", @class = "ArticleSummary sax-textarea", @rows = 6, @maxlength = @Model.GetControlMaxLength(SaxDbDfn_Core.ColumnIdx.JournalistArticle_Summary) })
@Html.TextAreaFor(model => model.ArticleSummary, new { @id = "ifArticleSummary", @class = "ArticleSummary sax-textarea", @rows = 6, @maxlength = @Model.GetControlMaxLength(SaxDbDfn_Core.ColumnIdx.Article_Summary) })
</div>
<div class="sax-message"></div>
</div>
@@ -50,7 +67,7 @@
@* Sport *@
<div class="sax-form-control ArticleSportId">
<div class="sax-label">
<label for="ifArticleSportId"><span><span class="required">*</span> @Localiser["label.sport"]</span></label>
<label for="ifArticleSportId"><span>@Localiser["label.sport"]</span><span class="required">&nbsp;*</span></label>
</div>
<div class="sax-control row">
@Html.HiddenFor(model => model.ArticleSportId, new { @id = "ifArticleSportId", @class = "ArticleSportId" })
@@ -63,7 +80,7 @@
@* Region *@
<div class="sax-form-control ArticleRegionId">
<div class="sax-label">
<label for="ifArticleRegionId"><span><span class="required">*</span> @Localiser["label.region"]</span></label>
<label for="ifArticleRegionId"><span>@Localiser["label.region"]</span><span class="required">&nbsp;*</span></label>
</div>
<div class="sax-control row">
@Html.HiddenFor(model => model.ArticleRegionId, new { @id = "ifArticleRegionId", @class = "ArticleRegionId" })
@@ -107,7 +124,7 @@
<div class="sax-panel-file-drop row">
<div class="col">Drop image here</div>
<div class="fixed">
<img id="" class="ContactImageUploadProgress" src="~/img/animated/progress-basketball-32.gif" style="display:none;margin:8px 4px;" />
<img class="ContactImageUploadProgress" src="~/img/animated/progress-basketball-32.gif" style="display:none;margin:8px 4px;" />
</div>
</div>
@* Segment details *@
@@ -138,7 +155,7 @@
<div id="dlgSelectArticleImage" class="sax-dialog">
@using (@Html.BeginForm(FormMethod.Post, new { @id = "frmSelectArticleImage", @class = "sax-form", @onSubmit = "return false;" }))
{
<input type="hidden" id="" class="SegmentIdx" />
<input type="hidden" class="SegmentIdx" />
<div class="spacer4"></div>
<div class="sax-form-control ArticleImage">
<div class="row">
@@ -150,7 +167,7 @@
<div class="spacer2"></div>
<div class="row">
<div class="col"><div class="UploadMessage"></div></div>
<div class="fixed"><img id="" class="ArticleImageUploadProgress" src="~/img/animated/progress-basketball-32.gif" style="display:none;margin:8px 4px;" /></div>
<div class="fixed"><img class="ArticleImageUploadProgress" src="~/img/animated/progress-basketball-32.gif" style="display:none;margin:8px 4px;" /></div>
</div>
<div class="spacer2"></div>
}
@@ -22,7 +22,7 @@
<input type="hidden" class="FilterPageNo" name="DataPageNo" value="0" />
<div class="row">
<div class="col-sm-5 sax-label text-left text-sm-right">@Localiser["label.search"]</div>
<div class="col-sm-7"><input type="search" id="" class="sax-input FilterOrganisation" name="Filter.Organisation" maxlength="32" autocomplete="off" /></div>
<div class="col-sm-7"><input type="search" class="sax-input FilterOrganisation" name="Filter.Organisation" maxlength="32" autocomplete="off" /></div>
</div>
}
@@ -55,7 +55,12 @@ else
<div class="sax-panel-item Activity">
<div class="sax-header row">
<div class="col">
<div class="sax-caption"><div class="sax-action ActivityCaption"></div></div>
<div class="sax-caption">
<div class="row">
<div class="col"><div class="sax-action ActivityCaption"></div></div>
<div class="col right"><div class="sax-action ActivityOrganisation"></div></div>
</div>
</div>
</div>
<div class="fixed sax-actions">
</div>
@@ -21,7 +21,7 @@
<div class="col-sm-4"><div class="sax-label text-left text-sm-right">@Localiser["label.organisation"]</div></div>
<div class="col-sm-8 row">
@Html.HiddenFor(model => model.Filter.OrganisationId, new { @id = "ifFilterOrganisationId", @class = "FilterOrganisationId" })
<div class="col"><input type="text" id="ifFilterOrganisation" class="FilterOrganisation sax-input" value="@(Model.Filter.OrganisationCaption)" /></div>
<div class="col"><input type="text" id="ifFilterOrganisation" class="FilterOrganisation sax-input" readonly="readonly" value="@(Model.Filter.OrganisationCaption)" /></div>
<div class="fixed"><button class="sax-button" onclick="sax.selectFilterOrganisation()">@Localiser["label.select"]</button></div>
</div>
</div>
@@ -64,6 +64,6 @@
</div>
<partial name="Panel-OpportunityDialog.cshtml" />
<partial name="~/Views/Common/OrganisationSelect.cshtml" />
<partial name="Panel-OpportunityDialog.cshtml" />
@@ -129,15 +129,15 @@
</a>
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
<div class="dropdown-item text-dark sax-action" onclick="sax.goToPage('Account/Profile');">@Localiser["account.caption.profile"]</div>
@if (Model.UserIsJournalist)
@if (Model.ArticlesAllowed)
{
<div class="dropdown-item text-dark sax-action" onclick="sax.goToPage('Journalist/Articles');">@Localiser["journalist.caption.articles"]</div>
<div class="dropdown-item text-dark sax-action" onclick="sax.goToPage('Article/Index');">@Localiser["article.caption.articles"]</div>
}
@if (Model.UserCanManageOrganisationMembers)
@if (Model.MembersAllowed)
{
<div class="dropdown-item text-dark sax-action" onclick="sax.goToPage('Organisation/Members');">@Localiser["label.members"]</div>
}
@if (Model.UserCanManageOrganisationOpportunities)
@if (Model.OpportunitiesAllowed)
{
<div class="dropdown-item text-dark sax-action" onclick="sax.goToPage('Organisation/Opportunities');">@Localiser["label.opportunities"]</div>
}
@@ -273,7 +273,7 @@
}
sax.onRefreshProfileOrganisationsInfo = function (response)
{
sax.debug('onRefreshProfileOrganisationsInfo', response);
//sax.debug('onRefreshProfileOrganisationsInfo', response);
if (!response || !response.success) return;
var $section = $('.sax-panel-section.ProfileOrganisations');
$('.sax-message', $section).stop(true, false).fadeOut(200).empty();
@@ -1,5 +1,5 @@
/*
* Journalist - Articles page scripts.
* Article page scripts.
*/
(function (win, doc, undef)
@@ -8,18 +8,18 @@
sax.itemIdPrefix = 'Itm_';
sax.segIdPrefix = 'Seg_';
sax.classFileDrag = 'sax-file-drag';
let $pnl = sax.$pnl, $frm = sax.$frm;
let ac = sax.articleContent = sax.articleContent || {};
//--------------------------------------------------------------
sax.refreshResults = function (delay)
{
var $results = $('.sax-panel-results');
if (!sax.awaitingRefreshResponse)
{
var $items = $('.sax-panel-item-list', $results).fadeOut(200).empty();
var $msgs = $('.sax-panel-messages', $results).hide().empty().append($('<div class="sax-message">' + sax.msg.dataQuerying + '</div>')).fadeIn(200);
var $items = $('.sax-panel-item-list', $pnl.results).fadeOut(200).empty();
var $msgs = $('.sax-panel-messages', $pnl.results).hide().empty().append($('<div class="sax-message">' + sax.msg.dataQuerying + '</div>')).fadeIn(200);
}
sax.ajaxRefreshResults = sax.ajaxRefreshResults || { url: sax.url.base + 'Journalist/Ajax/GetArticleList', formId: 'frmArticleFilter', callbackFn: sax.doRefreshResults };
sax.ajaxRefreshResults = sax.ajaxRefreshResults || { url: sax.url.base + 'Article/Ajax/GetList', formId: 'frmArticleFilter', callbackFn: sax.doRefreshResults };
sax.doAjaxRequest(sax.ajaxRefreshResults, delay || 50);
sax.awaitingRefreshResponse = true;
}
@@ -28,11 +28,11 @@
//sax.debug('doRefreshResults', response);
delete sax.awaitingRefreshResponse;
sax.refreshControlValues(response.controlValues);
var $results = $('.sax-panel-results');
var $msgs = $('.sax-panel-messages', $results).hide().empty();
var $items = $('.sax-panel-item-list', $results).hide().empty();
var $tmpl = $('.sax-panel-item-template .sax-panel-item', $results);
var $msgs = $('.sax-panel-messages', $pnl.results).hide().empty();
var $items = $('.sax-panel-item-list', $pnl.results).hide().empty();
var $tmpl = $('.sax-panel-item-template .sax-panel-item', $pnl.results);
if (!response.success) return;
sax.data.articleOrganisations = response.organisations;
sax.refreshPaging(response);
var items = response.articles;
if (!items.length)
@@ -60,7 +60,26 @@
$('.ArticleStatusDesc', $item).text(item.statusDesc);
$('.sax-caption .sax-action, .sax-actions .sax-action.sax-item-update', $item).off('click').on('click', function () { sax.updateItem(item.id) })
$item.off('dblclick').on('dblclick', function () { sax.updateItem(item.id) })
$('.sax-panel-results .sax-panel-messages').hide().empty();
$('.sax-panel-messages', $pnl.results).hide().empty();
}
//--------------------------------------------------------------
sax.selectFilterOrganisation = function ()
{
if (!sax.data.articleOrganisations || !sax.data.articleOrganisations.length) return;
sax.showSelectOrganisation();
}
sax.showSelectOrganisation = function ()
{
//sax.debug('sax.selectFilterOrganisation', sax.data.articleOrganisations);
sax.selectOrganisation.fn.selectOrganisation({ caption: sax.dlg.title.selectOrganisation, items: sax.data.articleOrganisations, fnCallback: sax.onOrganisationSelected });
}
sax.onOrganisationSelected = function (id, name)
{
//sax.debug('sax.onOrganisationSelected', id, name);
$('#frmArticleFilter #ifFilterOrganisationId, #frmArticle #ifArticleOrganisationId').val(id);
$('#frmArticleFilter #ifFilterOrganisation').val(name);
sax.refreshResults(50);
}
//--------------------------------------------------------------
@@ -245,13 +264,13 @@
if (file.type != 'image/jpg' && file.type != 'image/jpeg' && file.type != 'image/png') return sax.doAlert(sax.dlg.title.imageUpload, sax.msg.uploadFileTypeInvalid.format(sax.getFileType(file)));
$(".UploadMessage", context).text(sax.msg.uploadingFile.format(file.name));
$('img.ArticleImageUploadProgress', context).fadeIn(200);
sax.clearControlError(sax.$pnl.contactImageDrag);
sax.clearControlError();
sax.data.contactImageUploadContext = context;
sax.ajax.uploadArticleImage = sax.ajax.uploadArticleImage || { url: sax.url.base + 'Journalist/Ajax/UploadArticleImage', callbackFn: sax.handleArticleImageUploadResponse, noWaitIcon: true };
sax.ajax.uploadArticleImage = sax.ajax.uploadArticleImage || { url: sax.url.base + 'Article/Ajax/UploadImage', callbackFn: sax.handleArticleImageUploadResponse, noWaitIcon: true };
sax.ajax.uploadArticleImage.formData = new FormData();
sax.ajax.uploadArticleImage.formData.append("__RequestVerificationToken", $('#frmNavigate input[name="__RequestVerificationToken"]').val());
sax.ajax.uploadArticleImage.formData.append("ArticleId", $('#ifArticleId', sax.$frm.article).val());
sax.ajax.uploadArticleImage.formData.append("ArticleId", $('#ifArticleId', $frm.article).val());
sax.ajax.uploadArticleImage.formData.append("ArticleImage", file);
sax.doAjaxRequest(sax.ajax.uploadArticleImage, 50);
}
@@ -275,9 +294,21 @@
sax.createItem = function ()
{
sax.clearItem();
let orgId = $('#ifFilterOrganisationId', $frm.filter).val();
$('#ifArticleOrganisationId', $frm.article).val(orgId);
$('#ifArticleOrganisation', $frm.article).val($('#ifFilterOrganisation', $frm.filter).val());
$('.sax-form-control.ArticleOrganisation', $frm.article).toggle(!!orgId);
sax.populateArticleContent({});
sax.openArticleDialog(sax.dlg.title.createArticle);
}
sax.clearItem = function ()
{
let frm = $frm.article;
$('#ifArticleId,#ifArticleExists,#ifArticleOrganisationId,#ifArticleRegionId,#ifArticleSportId,input[type="text"],textarea,select', frm).val('');
$('#chkArticlePublish', frm).prop("checked", false);
$('.sax-input.sax-calendar.ArticlePublishOn', frm).datepicker('setDate', 'today');
sax.clearControlErrors();
}
sax.openArticleDialog = function (title)
{
sax.clearControlErrors();
@@ -285,24 +316,12 @@
sax.$dlg.article.dialog('option', 'title', title);
sax.openDialog(sax.$dlg.article, 600, 800);
}
sax.clearItem = function ()
{
let $frm = sax.$frm.article;
$('#ifArticleId,#ifArticleExists,#ifArticleRegionId,#ifArticleSportId,input[type="text"],textarea,select', $frm).val('');
$('#chkArticlePublish', $frm).prop("checked", false);
$('.sax-input.sax-calendar.ArticlePublishOn', $frm).datepicker('setDate', 'today');
sax.clearControlErrors();
}
sax.doCreateItem = function ()
{
sax.storeItem();
}
//--------------------------------------------------------------
sax.updateItem = function (id)
{
$('.ArticleId', sax.$frm.articleId).val(id);
sax.ajaxUpdateItem = sax.ajaxUpdateItem || { url: sax.url.base + 'Journalist/Ajax/GetArticle', formId: 'frmArticleId', callbackFn: sax.onUpdateItem };
$('.ArticleId', $frm.articleId).val(id);
sax.ajaxUpdateItem = sax.ajaxUpdateItem || { url: sax.url.base + 'Article/Ajax/GetItem', formId: 'frmArticleId', callbackFn: sax.onUpdateItem };
sax.doAjaxRequest(sax.ajaxUpdateItem, 50);
}
sax.onUpdateItem = function (response)
@@ -310,28 +329,24 @@
//sax.debug('onUpdateItem', response);
sax.clearItem();
if (!response.success) return;
let $frm = sax.$frm.article;
let frm = $frm.article;
let item = response.article;
$('#ifArticleId', $frm).val(item.id);
$('#ifArticleExists', $frm).val(1);
$('#ifArticleHeadline', $frm).val(item.headline);
$('#ifArticleSummary', $frm).val(item.summary);
$('#ifArticleRegionId', $frm).val(item.regionIdPrimary);
$('#ifArticleRegionCaption', $frm).val(item.regionCaptionPrimary);
$('#ifArticleSportId', $frm).val(item.sportIdPrimary);
$('#ifArticleSportCaption', $frm).val(item.sportCaptionPrimary);
$('#ifArticleId', frm).val(item.id);
$('#ifArticleExists', frm).val(1);
$('#ifArticleHeadline', frm).val(item.headline);
$('#ifArticleSummary', frm).val(item.summary);
$('#ifArticleRegionId', frm).val(item.regionIdPrimary);
$('#ifArticleRegionCaption', frm).val(item.regionCaptionPrimary);
$('#ifArticleSportId', frm).val(item.sportIdPrimary);
$('#ifArticleSportCaption', frm).val(item.sportCaptionPrimary);
if (item.published)
$('span.ArticlePublishedOn', $frm).text(item.publishedOnFmt);
$('div.ArticlePublish', $frm).toggle(!item.published);
$('div.ArticlePublished', $frm).toggle(!!item.published);
$('#cbArticleStatusCode', $frm).val(item.statusCode);
$('span.ArticlePublishedOn', frm).text(item.publishedOnFmt);
$('div.ArticlePublish', frm).toggle(!item.published);
$('div.ArticlePublished', frm).toggle(!!item.published);
$('#cbArticleStatusCode', frm).val(item.statusCode);
sax.populateArticleContent(item.content);
sax.openArticleDialog(sax.dlg.title.updateArticle);
}
sax.doUpdateItem = function ()
{
sax.storeItem();
}
//--------------------------------------------------------------
sax.switchArticleHeader = function ()
@@ -368,7 +383,7 @@
//--------------------------------------------------------------
sax.storeItem = function ()
{
sax.ajaxStoreItem = sax.ajaxStoreItem || { url: sax.url.base + 'Journalist/Ajax/StoreArticle', formId: 'frmArticle', callbackFn: sax.onStoreItem };
sax.ajaxStoreItem = sax.ajaxStoreItem || { url: sax.url.base + 'Article/Ajax/StoreItem', formId: 'frmArticle', callbackFn: sax.onStoreItem };
sax.doAjaxRequest(sax.ajaxStoreItem, 50);
}
sax.onStoreItem = function (response)
@@ -382,9 +397,8 @@
var $item = $('#' + sax.itemIdPrefix + item.id);
if (!$item.length)
{
var $results = $('.sax-panel-results');
$item = $('.sax-panel-item-template .sax-panel-item', $results).clone();
$('.sax-panel-item-list', $results).prepend($item).show();
$item = $('.sax-panel-item-template .sax-panel-item', $pnl.results).clone();
$('.sax-panel-item-list', $pnl.results).prepend($item).show();
}
sax.populateItem($item, item)
}
@@ -392,20 +406,23 @@
//--------------------------------------------------------------
$(function ()
{
sax.$frm.filter = $('#frmArticleFilter');
$('.sax-panel-filter .sax-action.sax-item-create').on('click', function () { sax.createItem(); });
$('.sax-panel-filter .sax-action.sax-results-refresh').on('click', function () { sax.refreshResults(); });
$('.ArticleFilter', sax.$frm.filter).on('input', function () { sax.refreshResults(250); }).on('keypress', function (event)
$pnl.filter = $('.sax-panel-filter');
$pnl.results= $('.sax-panel-results');
$frm.filter = $('#frmArticleFilter');
$('.sax-action.sax-item-create', $pnl.filter).on('click', function () { sax.createItem(); });
$('.sax-action.sax-results-refresh', $pnl.filter).on('click', function () { sax.refreshResults(); });
$('.ArticleFilter', $frm.filter).on('input', function () { sax.refreshResults(250); }).on('keypress', function (event)
{
if (event.keyCode == 13) setTimeout(sax.refreshResults, 50);
});
sax.$frm.articleId = $('#frmArticleId');
sax.$frm.article = $('#frmArticle');
$('.sax-input', sax.$frm.article).on('input', function () { sax.clearControlError(this); });
$frm.articleId = $('#frmArticleId');
$frm.article = $('#frmArticle');
$('.sax-input', $frm.article).on('input', function () { sax.clearControlError(this); });
var opt = sax.dlgOpts();
opt.buttons[sax.dlg.btn.cancel] = { text: sax.dlg.btn.cancel, class: "floatr", click: function () { $(this).dialog('close'); } };
opt.buttons[sax.dlg.btn.ok] = { text: sax.dlg.btn.ok, class: "floatr", click: function () { sax.doCreateItem(); } };
opt.buttons[sax.dlg.btn.ok] = { text: sax.dlg.btn.ok, class: "floatr", click: function () { sax.storeItem(); } };
opt.buttons['Header'] = { text: 'Header', class: "floatl", click: function () { sax.switchArticleHeader(); } };
opt.buttons['Content'] = { text: 'Content', class: "floatl", click: function () { sax.switchArticleContent(); } };
sax.$dlg.article = $('#dlgArticle').dialog(opt);
+3 -1
View File
@@ -44,9 +44,11 @@
}
sax.populateItem = function ($item, item)
{
//sax.debug('populateItem', item);
sax.debug('populateItem', item);
if (!item) return;
$item.prop('id', sax.itemIdPrefix + item.itemIdx).addClass(item.itemTypeClass).toggleClass('sax-activity-reaction-' + item.reaction, !!item.reaction);
$('.ActivityOrganisation', $item).text(item.organisation);
$('.ActivityAuthor', $item).text(item.author);
$('.ActivityCaption', $item).text(item.caption);
$('.ActivityBlurb', $item).text(item.blurb);
$('.ActivitySport', $item).text(item.sport);
@@ -35,7 +35,7 @@
}
sax.doRefreshResults = function (response)
{
sax.debug('doRefreshResults', response);
//sax.debug('doRefreshResults', response);
delete sax.awaitingRefreshResponse;
sax.refreshControlValues(response.controlValues);
let $mnu = $('.sax-burger ul.sax-dropdown-menu', sax.$pnl.filter).empty();
@@ -201,7 +201,7 @@
}
sax.updateMemberRoles = function (memberRoleCodes)
{
sax.debug('updateMemberRoles', memberRoleCodes);
//sax.debug('updateMemberRoles', memberRoleCodes);
let $frm = sax.$frm.member;
sax.member.roleCodes = memberRoleCodes;
$('.MemberRoleCodes',$frm).val(memberRoleCodes.join(sax.data.memberRoleCodeDelim));
@@ -230,7 +230,7 @@
}
sax.onStoreItem = function (response)
{
sax.debug('onStoreItem', response);
//sax.debug('onStoreItem', response);
sax.refreshControlValues(response.controlValues);
if (!response.success) return;
let $frm = sax.$frm.member;
@@ -26,7 +26,7 @@
}
or.fn.handleRequestOrganisationResponse = function (response)
{
sax.debug('or.handleRequestOrganisationResponse', response);
//sax.debug('or.handleRequestOrganisationResponse', response);
if (!response) return;
sax.refreshControlValues(response.controlValues, or.$frm);
if (!response.success) return;
@@ -6,7 +6,7 @@
{
"use strict";
let so = sax.selectOrganisation;
so.data = {}; so.fn = {}; so.itemIdPrefix = 'Org_';
so.data = {}; so.fn = {};
//--------------------------------------------------------------
/*
@@ -52,7 +52,8 @@
{
let item = items[itemIdx];
if (!so.fn.matchesFilter(item)) continue;
let $item = $tmpl.clone().prop('id', so.itemIdPrefix + item.id).on('click', function () { so.fn.onClickOrganisation(item); }).on('dblclick', function () { so.fn.onDblClickOrganisation(item); });
let $item = $tmpl.clone();
$item.on('click', function () { so.fn.onClickOrganisation(item, $item); }).on('dblclick', function () { so.fn.onDblClickOrganisation(item, $item); });
$('.Name', $item).text(item.name);
so.$results.append($item);
}
@@ -69,16 +70,16 @@
}
return true;
}
so.fn.onClickOrganisation = function (item)
so.fn.onClickOrganisation = function (item, $item)
{
//sax.debug('onClickOrganisation', item);
//sax.debug('onClickOrganisation', item, $item);
so.data.item = item;
$('.sax-panel-item.Organisation', so.$dlg).removeClass('sel');
$('.sax-panel-item.Organisation#' + so.itemIdPrefix + item.id, so.$dlg).addClass('sel');
$item.addClass('sel');
}
so.fn.onDblClickOrganisation = function (item)
so.fn.onDblClickOrganisation = function (item, $item)
{
//sax.debug('onDblClickCode', item);
//sax.debug('onDblClickCode', item, $item);
so.data.item = item;
so.fn.confirmSelection();
}
@@ -99,7 +100,6 @@
delete so.data.item;
}
//--------------------------------------------------------------
so.fn.init = function ()
{
@@ -33,7 +33,7 @@
{
if (!response || !response.success) return;
sr.regions = response.regions;
sax.debug(sr.cfg);
//sax.debug(sr.cfg);
sr.fn.showSelectRegion();
}
sr.fn.showSelectRegion = function ()