diff --git a/SaxoniteCoreUnitTests/SaxoniteCoreUnitTests.csproj b/SaxoniteCoreUnitTests/SaxoniteCoreUnitTests.csproj index 30c0f57..5c7f172 100644 --- a/SaxoniteCoreUnitTests/SaxoniteCoreUnitTests.csproj +++ b/SaxoniteCoreUnitTests/SaxoniteCoreUnitTests.csproj @@ -10,7 +10,7 @@ - + diff --git a/SaxoniteCoreUnitTestsOnline/SaxoniteCoreUnitTestsOnline.csproj b/SaxoniteCoreUnitTestsOnline/SaxoniteCoreUnitTestsOnline.csproj index c0d1a44..51af624 100644 --- a/SaxoniteCoreUnitTestsOnline/SaxoniteCoreUnitTestsOnline.csproj +++ b/SaxoniteCoreUnitTestsOnline/SaxoniteCoreUnitTestsOnline.csproj @@ -10,7 +10,7 @@ - + diff --git a/SaxoniteCoreWebAdmin/Views/Security/ChangePassword.cshtml b/SaxoniteCoreWebAdmin/Views/Security/ChangePassword.cshtml index be0cb60..2ec9c83 100644 --- a/SaxoniteCoreWebAdmin/Views/Security/ChangePassword.cshtml +++ b/SaxoniteCoreWebAdmin/Views/Security/ChangePassword.cshtml @@ -12,7 +12,7 @@
- @using (@Html.BeginForm(FormMethod.Post, new { @id = "frmChangePassword", @class = "sax-form" })) + @using (@Html.BeginForm(FormMethod.Post, new { @id = "frmChangePassword", @class = "sax-form", @onsubmit = "return false;" })) { @if (!Model.RequestPasswordMode) diff --git a/SaxoniteCoreWebAdmin/Views/Security/RequestPassword.cshtml b/SaxoniteCoreWebAdmin/Views/Security/RequestPassword.cshtml index 44ca7d9..7a617fa 100644 --- a/SaxoniteCoreWebAdmin/Views/Security/RequestPassword.cshtml +++ b/SaxoniteCoreWebAdmin/Views/Security/RequestPassword.cshtml @@ -12,7 +12,7 @@
- @using (@Html.BeginForm(FormMethod.Post, new { @id = "frmRequestPassword", @class = "sax-form" })) + @using (@Html.BeginForm(FormMethod.Post, new { @id = "frmRequestPassword", @class = "sax-form", @onsubmit = "return false;" })) {
diff --git a/SaxoniteCoreWebAdmin/Views/Security/Signin.cshtml b/SaxoniteCoreWebAdmin/Views/Security/Signin.cshtml index 2b7fa1a..fd02f99 100644 --- a/SaxoniteCoreWebAdmin/Views/Security/Signin.cshtml +++ b/SaxoniteCoreWebAdmin/Views/Security/Signin.cshtml @@ -13,7 +13,7 @@
- @using (@Html.BeginForm(FormMethod.Post, new { @id = "frmSignIn", @class = "sax-form" })) + @using (@Html.BeginForm(FormMethod.Post, new { @id = "frmSignIn", @class = "sax-form", @onsubmit = "return false;" })) {
@@ -25,33 +25,32 @@
+
-
-
-
- -
-
-
- -
+
+
@Html.PasswordFor(model => model.Password, new { @id = "ifPassword", @class = "sax-input", @maxlength = @Model.PasswordMaxLength })
- -
+
-
- @Html.CheckBoxFor(model => model.KeepMeSignedIn, new { @id = "chkKeepMeSignedIn" }) -   - @Localiser["label.keep.signed.in"] +
+
+ @Html.CheckBoxFor(model => model.KeepMeSignedIn, new { @id = "chkKeepMeSignedIn" }) +   + @Localiser["label.keep.signed.in"] +
+
+ +
+
@Localiser["security.caption.signin"]
diff --git a/SaxoniteCoreWebAdmin/wwwroot/js/sax-security.js b/SaxoniteCoreWebAdmin/wwwroot/js/sax-security.js index 8de2041..34b708e 100644 --- a/SaxoniteCoreWebAdmin/wwwroot/js/sax-security.js +++ b/SaxoniteCoreWebAdmin/wwwroot/js/sax-security.js @@ -30,7 +30,7 @@ sax.onScorePassword = function (response) { var score = (response || {}).score || 0; - sax.$prgPasswordStrength.progressbar("option", "value", score).removeClass('amber green').addClass(score > 3 ? 'green' : score > 2 ? 'amber' : ''); + sax.$prgPwordStrength.progressbar("option", "value", score).removeClass('amber green').addClass(score > 3 ? 'green' : score > 2 ? 'amber' : ''); } //-------------------------------------------------------------- @@ -74,16 +74,20 @@ { $(':input:enabled:visible:first').focus(); $('#ifEmailAddress,#ifPasswordCurrent,#ifPassword,#ifPasswordConfirm').on('input', function () { sax.clearControlError(this); }); - $('#frmChangePassword #ifPassword').on('keyup', sax.doScorePassword); - sax.$prgPasswordStrength = $('#frmChangePassword #prgPasswordStrength').progressbar({ 'max': 5 }); - $('#ifEmailAddress,#ifPassword', $('#frmSignIn')).on('keypress', function (e) { if (e.which == 13) sax.doSignin(); }); - $('#frmSignIn #pbSignIn').on('click', function () { sax.doSignin(); }); - $('#ifPasswordCurrent,#ifPassword,#ifPasswordConfirm', $('#frmChangePassword')).on('keypress', function (e) { if (e.which == 13) sax.doChangePassword(); }); - $('#frmChangePassword #pbChangePassword').on('click', function () { sax.doChangePassword(); }); - $('#ifEmailAddress', $('#frmRequestPassword')).on('keypress', function (e) { if (e.which == 13) sax.doRequestPassword(); }); - $('#frmRequestPassword #pbRequestPassword').on('click', function () { sax.doRequestPassword(); }); + sax.$frm.signIn = $('#frmSignIn'); + sax.$frm.signIn.on('keypress', '#ifEmailAddress,#ifPassword,#chkKeepMeSignedIn', function (e) { if (e.which == 13 && $('#ifEmailAddress', sax.$frm.signIn).val() && $('#ifPassword', sax.$frm.signIn).val()) sax.doSignin(); }); + sax.$frm.signIn.on('click', '#pbSignIn', function () { sax.doSignin(); }).on('keypress', '#pbSignIn', function (e) { if (e.which == 13) sax.doSignin(); });; + sax.$frm.chgPword = $('#frmChangePassword'); + sax.$frm.chgPword.on('keypress', '#ifPasswordCurrent,#ifPassword,#ifPasswordConfirm', function (e) { if (e.which == 13) sax.doChangePassword(); }); + sax.$frm.chgPword.on('keyup', '#ifPassword', sax.doScorePassword); + sax.$prgPwordStrength = $('#prgPasswordStrength', sax.$frm.chgPword).progressbar({ 'max': 5 }); + $('#pbChangePassword', sax.$frm.chgPword).on('click', function () { sax.doChangePassword(); }).on('keypress', function (e) { if (e.which == 13) sax.doChangePassword(); }); + sax.$frm.reqPword = $('#frmRequestPassword'); + sax.$frm.reqPword.on('keypress', '#ifEmailAddress', function (e) { if (e.which == 13) sax.doRequestPassword(); }); + $('#pbRequestPassword', sax.$frm.reqPword).on('click', function () { sax.doRequestPassword(); }).on('keypress', function (e) { if (e.which == 13) sax.doRequestPassword(); }); }); + //-------------------------------------------------------------- }(window, window.document)); /* The End */ \ No newline at end of file diff --git a/SaxoniteCoreWebOnline/Localisation/SaxOnlineProperties.resx b/SaxoniteCoreWebOnline/Localisation/SaxOnlineProperties.resx index 71c7a8c..0c026a1 100644 --- a/SaxoniteCoreWebOnline/Localisation/SaxOnlineProperties.resx +++ b/SaxoniteCoreWebOnline/Localisation/SaxOnlineProperties.resx @@ -635,6 +635,15 @@ You will then be able to publicise your sporting activities. Invalid image file uploaded + + Invalid image content type. + + + Invalid image mime type. + + + Image resize failed. + Only one image can be uploaded. diff --git a/SaxoniteCoreWebOnline/Online/Controllers/AccountController-Ajax-Contact.cs b/SaxoniteCoreWebOnline/Online/Controllers/AccountController-Ajax-Contact.cs index 653c2ad..3ebbb8c 100644 --- a/SaxoniteCoreWebOnline/Online/Controllers/AccountController-Ajax-Contact.cs +++ b/SaxoniteCoreWebOnline/Online/Controllers/AccountController-Ajax-Contact.cs @@ -89,6 +89,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers // Reference data. public List Genders { get; } = new List(); public List Months { get; } = new List(); + public List ImageContentTypes { get; set; } } // Ajax request. [HttpPost] @@ -113,6 +114,9 @@ namespace Saxonite.Net.Core.Web.Online.Controllers for (int month = 1; month <= 12; month++) responseData.Months.Add(month.ToString(), DateUtil.Format(new DateTime(DateTime.Today.Year, month, 1), "MMMM", SessionCulture)); + // Profile image content types. + responseData.ImageContentTypes = SaxItemImage.GetValidContentTypes(Sax.DataItemType.UserAccount); + // Retrieve user details. SaxUserAccount userAccount = AccountMgr.GetUserAccountById(UserAccountId); if (userAccount == null) @@ -190,6 +194,18 @@ namespace Saxonite.Net.Core.Web.Online.Controllers if (!imageConfig.ValidImage) return Json(responseData.SetControlError("AccountContactImage", Localiser["msg.upload.image.invalid"])); + // Validate mime type. + if (!SaxItemImage.GetValidMimeTypes(Sax.DataItemType.UserAccount).Contains(imageConfig.MimeType)) + return Json(responseData.SetControlError("AccountContactImage", Localiser["msg.upload.image.invalid.mime"])); + + // Validate content type. + if (StringUtil.IsEmpty(file.ContentType) || !SaxItemImage.GetValidContentTypes(Sax.DataItemType.UserAccount).ContainsCI(file.ContentType)) + return Json(responseData.SetControlError("AccountContactImage", Localiser["msg.upload.image.invalid.content.type"])); + + // Resize the image if required. + if (!ImageUtils.ConstrainImage(imageConfig, Sax.Config.Online.Profile.User.Image.WidthMax, Sax.Config.Online.Profile.User.Image.HeightMax)) + return Json(responseData.SetControlError("AccountContactImage", Localiser["msg.upload.image.resize.failed"])); + // Image belongs to the user's account. ItemId userItemId = GetUserItemId(UserAccountId); @@ -200,9 +216,9 @@ namespace Saxonite.Net.Core.Web.Online.Controllers itemImage.Filename = file.FileName; itemImage.Mime = SacsContentTypeHelper.GetMimeTypeFromContentType(file.ContentType).ToString().ToLower(); itemImage.ContentType = file.ContentType; + itemImage.ImageData = imageConfig.ImageData; itemImage.Width = imageConfig.Width; itemImage.Height = imageConfig.Height; - itemImage.ImageData = imageData; itemImage.Status = SaxItemImage.DefaultStatus; if (!SysId.IsValid(itemImage.Id)) diff --git a/SaxoniteCoreWebOnline/Online/Controllers/ArticleController-Ajax.cs b/SaxoniteCoreWebOnline/Online/Controllers/ArticleController-Ajax.cs index 7ebdcd3..84b1e57 100644 --- a/SaxoniteCoreWebOnline/Online/Controllers/ArticleController-Ajax.cs +++ b/SaxoniteCoreWebOnline/Online/Controllers/ArticleController-Ajax.cs @@ -155,8 +155,9 @@ namespace Saxonite.Net.Core.Web.Online.Controllers 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; } + public ItemIdName DefaultSport { get; set; } + public ItemIdName DefaultRegion { get; set; } + public List ImageContentTypes { get; set; } } //------------------------------------------------------------------- [HttpPost] @@ -215,7 +216,7 @@ namespace Saxonite.Net.Core.Web.Online.Controllers SysId defaultSportId = organisation?.SportIdPrimary ?? null; if (SysId.IsValid(defaultSportId)) { - responseData.defaultSport = new AjaxResponse_Data.ItemIdName() + responseData.DefaultSport = new AjaxResponse_Data.ItemIdName() { Id = model.GetEncryptedSportId(defaultSportId), Name = model.GetSportCaption(defaultSportId) @@ -226,13 +227,16 @@ namespace Saxonite.Net.Core.Web.Online.Controllers SysId defaultRegionId = organisation?.RegionIdPrimary ?? userAccount?.RegionIdCountry ?? null; if (SysId.IsValid(defaultRegionId)) { - responseData.defaultRegion = new AjaxResponse_Data.ItemIdName() + responseData.DefaultRegion = new AjaxResponse_Data.ItemIdName() { Id = model.GetEncryptedRegionId(defaultRegionId), Name = model.GetRegionCaption(defaultRegionId) }; } + // Article image content types. + responseData.ImageContentTypes = SaxItemImage.GetValidContentTypes(Sax.DataItemType.Article); + // Item filter. SaxArticle.Params prmArticle = new SaxArticle.Params(); prmArticle.UserAccountIdsAuthorIncluded.Add(sessionData.User.UserAccountId); @@ -445,6 +449,18 @@ namespace Saxonite.Net.Core.Web.Online.Controllers if (!imageConfig.ValidImage) return Json(responseData.SetControlError("ArticleImage", Localiser["msg.upload.image.invalid"])); + // Validate mime type. + if (!SaxItemImage.GetValidMimeTypes(Sax.DataItemType.Article).Contains(imageConfig.MimeType)) + return Json(responseData.SetControlError("ArticleImage", Localiser["msg.upload.image.invalid.mime"])); + + // Validate content type. + if (StringUtil.IsEmpty(file.ContentType) || !SaxItemImage.GetValidContentTypes(Sax.DataItemType.Article).ContainsCI(file.ContentType)) + return Json(responseData.SetControlError("ArticleImage", Localiser["msg.upload.image.invalid.content.type"])); + + // Resize the image if required. + if (!ImageUtils.ConstrainImage(imageConfig, Sax.Config.Online.Articles.Images.WidthMax, Sax.Config.Online.Articles.Images.HeightMax)) + return Json(responseData.SetControlError("ArticleImage", Localiser["msg.upload.image.resize.failed"])); + // 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); @@ -470,9 +486,9 @@ namespace Saxonite.Net.Core.Web.Online.Controllers itemImage.Filename = file.FileName; itemImage.Mime = SacsContentTypeHelper.GetMimeTypeFromContentType(file.ContentType).ToString().ToLower(); itemImage.ContentType = file.ContentType; + itemImage.ImageData = imageConfig.ImageData; itemImage.Width = imageConfig.Width; itemImage.Height = imageConfig.Height; - itemImage.ImageData = imageData; itemImage.SessionUid = sessionData.SessionIdx; itemImage.ImageUid = sessionData.NextItemIdx; itemImage.Status = SaxItemImage.DefaultStatus; diff --git a/SaxoniteCoreWebOnline/Views/Article/Panel-ArticleDialog.cshtml b/SaxoniteCoreWebOnline/Views/Article/Panel-ArticleDialog.cshtml index 159770a..88eba0a 100644 --- a/SaxoniteCoreWebOnline/Views/Article/Panel-ArticleDialog.cshtml +++ b/SaxoniteCoreWebOnline/Views/Article/Panel-ArticleDialog.cshtml @@ -20,170 +20,169 @@ @* Article dialog and form *@
-
@using (@Html.BeginForm(FormMethod.Post, new { @id = "frmArticle", @class = "sax-form", @onSubmit = "return false;" })) { @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" }) -
+
-
+
+
- @* Organisation *@ -
-
-
-
- -
+ @* Organisation *@ +
+
+
+
+
-
-
- -
+
+
+
+
- - @* Article headline *@ -
-
-
-
-
-
-
-
- @Html.TextBoxFor(model => model.ArticleHeadline, new { @id = "ifArticleHeadline", @class = "ArticleHeadline sax-input", @maxlength = @Model.GetControlMaxLength(SaxDbDfn_Core.ColumnIdx.Article_Headline) }) -
-
-
- - @* Article summary *@ -
-
-
-
-
-
-
-
- @Html.TextAreaFor(model => model.ArticleSummary, new { @id = "ifArticleSummary", @class = "ArticleSummary sax-textarea", @rows = 6, @maxlength = @Model.GetControlMaxLength(SaxDbDfn_Core.ColumnIdx.Article_Summary) }) -
-
-
- - @* Sport *@ -
-
-
-
- -
-
-
-
- @Html.HiddenFor(model => model.ArticleSportId, new { @id = "ifArticleSportId", @class = "ArticleSportId" }) -
@Html.TextBoxFor(model => model.ArticleSportCaption, new { @id = "ifArticleSportCaption", @class = "ArticleSportCaption sax-input", @readonly = "readonly", @onDblClick = "sax.selectSport()" })
-
-
-
-
-
-
- - @* Region *@ -
-
-
-
- -
-
-
-
- @Html.HiddenFor(model => model.ArticleRegionId, new { @id = "ifArticleRegionId", @class = "ArticleRegionId" }) -
@Html.TextBoxFor(model => model.ArticleRegionCaption, new { @id = "ifArticleRegionCaption", @class = "ArticleRegionCaption sax-input", @readonly = "readonly", @onDblClick = "sax.selectRegion()" })
-
-
-
-
-
-
- - @* Publication *@ -
-
-
-
- -
-
-
-
-
- @Html.HiddenFor(model => model.ArticlePublished, new { @id = "ifArticlePublished" }) -
@Html.CheckBoxFor(model => model.ArticlePublish, new { @id = "chkArticlePublish", @class = "ArticlePublish" }) @Localiser["label.publish"]
-
@Html.TextBoxFor(model => model.ArticlePublishOn, new { @id = "ifArticlePublishOn", @class = "sax-input sax-calendar ArticlePublishOn", @readonly = "readonly" })
-
- -
-
-
-
-
-
-
- -
+ @* Article headline *@ +
+
+
+
+
+
+
+ @Html.TextBoxFor(model => model.ArticleHeadline, new { @id = "ifArticleHeadline", @class = "ArticleHeadline sax-input", @maxlength = @Model.GetControlMaxLength(SaxDbDfn_Core.ColumnIdx.Article_Headline) }) +
+
+
-
-
- @* Inputs with same name for every segment to ensure they are uploaded as a list. *@ - - - - @* Image drop panel *@ -
-
Drop image here
-
- + @* Article summary *@ +
+
+
+
+
+
+
+
+ @Html.TextAreaFor(model => model.ArticleSummary, new { @id = "ifArticleSummary", @class = "ArticleSummary sax-textarea", @rows = 6, @maxlength = @Model.GetControlMaxLength(SaxDbDfn_Core.ColumnIdx.Article_Summary) }) +
+
+
+ + @* Sport *@ +
+
+
+
+ +
+
+
+
+ @Html.HiddenFor(model => model.ArticleSportId, new { @id = "ifArticleSportId", @class = "ArticleSportId" }) +
@Html.TextBoxFor(model => model.ArticleSportCaption, new { @id = "ifArticleSportCaption", @class = "ArticleSportCaption sax-input", @readonly = "readonly", @onDblClick = "sax.selectSport()" })
+
+
+
+
+
+
+ + @* Region *@ +
+
+
+
+ +
+
+
+
+ @Html.HiddenFor(model => model.ArticleRegionId, new { @id = "ifArticleRegionId", @class = "ArticleRegionId" }) +
@Html.TextBoxFor(model => model.ArticleRegionCaption, new { @id = "ifArticleRegionCaption", @class = "ArticleRegionCaption sax-input", @readonly = "readonly", @onDblClick = "sax.selectRegion()" })
+
+
+
+
+
+
+ + @* Publication *@ +
+
+
+
+ +
+
+
+
+
+ @Html.HiddenFor(model => model.ArticlePublished, new { @id = "ifArticlePublished" }) +
@Html.CheckBoxFor(model => model.ArticlePublish, new { @id = "chkArticlePublish", @class = "ArticlePublish" }) @Localiser["label.publish"]
+
@Html.TextBoxFor(model => model.ArticlePublishOn, new { @id = "ifArticlePublishOn", @class = "sax-input sax-calendar ArticlePublishOn", @readonly = "readonly" })
+
+
- @* Segment details *@ -
- @* Segment body *@ -
-
- -
-
- @* Segment actions *@ -
-
-
-
-
-
+
+
+
+
+ +
+
+ +
+ +
+
+ +
+
+ @* Inputs with same name for every segment to ensure they are uploaded as a list. *@ + + + + @* Image drop panel *@ +
+
Drop image here
+
+ +
+
+ @* Segment details *@ +
+ @* Segment body *@ +
+
+ +
+
+ @* Segment actions *@ +
+
+
+
+
-
- } -
+
+ }
@* Article Image upload dialog and form *@ diff --git a/SaxoniteCoreWebOnline/Views/Security/Signin.cshtml b/SaxoniteCoreWebOnline/Views/Security/Signin.cshtml index 1e88e8e..ab6661e 100644 --- a/SaxoniteCoreWebOnline/Views/Security/Signin.cshtml +++ b/SaxoniteCoreWebOnline/Views/Security/Signin.cshtml @@ -11,11 +11,11 @@
@Localiser["security.caption.signin"]
+
@using (@Html.BeginForm(FormMethod.Post, new { @id = "frmSignIn", @class = "sax-form", @onsubmit = "return false;" })) { -
@@ -25,23 +25,19 @@
-
+
-
-
-
- -
-
+
+
@Html.PasswordFor(model => model.Password, new { @id = "ifPassword", @class = "sax-input", @maxlength = @Model.PasswordMaxLength })
-
+
@@ -53,16 +49,13 @@
-
+
@Localiser["security.caption.signin"]
} -
-
-
diff --git a/SaxoniteCoreWebOnline/wwwroot/js/sax-art.js b/SaxoniteCoreWebOnline/wwwroot/js/sax-art.js index ef78808..8f3e926 100644 --- a/SaxoniteCoreWebOnline/wwwroot/js/sax-art.js +++ b/SaxoniteCoreWebOnline/wwwroot/js/sax-art.js @@ -35,6 +35,7 @@ sax.data.articleOrganisations = response.organisations; sax.data.defaultSport = response.defaultSport; sax.data.defaultRegion = response.defaultRegion; + sax.data.imageContentTypes = response.imageContentTypes; sax.refreshPaging(response, '#frmArticleFilter', am.refreshResults); // Menu. let $mnu = $('.sax-burger ul.sax-dropdown-menu', $pnl.filter).empty(); @@ -165,9 +166,8 @@ sax.clearControlErrors(); am.switchArticleHeader(); $dlg.article.dialog('option', 'title', title); - sax.openDialog($dlg.article, 600, 800); - $('.ArticleHeadline.sax-input', $frm.article).trigger("input"); - $('.ArticleSummary.sax-textarea', $frm.article).trigger("input"); + sax.openDialog($dlg.article, 800, 1200); + $('.ArticleHeadline.sax-input,.ArticleSummary.sax-textarea', $frm.article).trigger("input"); } //-------------------------------------------------------------- @@ -304,6 +304,20 @@ { ed.notificationManager.close(); }); + /* + ed.on('paste', function (e) + { + sax.debug('segment.paste'); + let paste = (event.clipboardData || window.clipboardData).getData('text'); + paste = paste.toUpperCase(); + sax.debug(paste); + const selection = window.getSelection(); + if (!selection.rangeCount) return false; + selection.deleteFromDocument(); + selection.getRangeAt(0).insertNode(document.createTextNode(paste)); + event.preventDefault(); + }); + */ ed.on('blur', function (e) { let ed = tinymce.get(e.target.id); @@ -315,6 +329,7 @@ }); }, plugins: [ + 'paste' //'paste image' //'link', //'lists', @@ -330,7 +345,7 @@ */ toolbar: [ 'undo redo | bold italic underline | fontselect fontsizeselect', - 'forecolor backcolor | alignleft aligncenter alignright alignfull | numlist bullist outdent indent' + 'forecolor backcolor | alignleft aligncenter alignright alignfull | numlist bullist outdent indent | pastetext' ], valid_elements: 'p[style],strong,em,span[style],a[href],ul,ol,li', valid_styles: { '*': 'font-size,font-family,color,background-color,text-decoration,text-align' }, @@ -417,7 +432,7 @@ //sax.debug('onDropArticleImageFile', file, context); ac.segIdxAddImage = idx; if (file.size > sax.maxProfileImageSize) return sax.doAlert(sax.dlg.title.imageUpload, sax.msg.uploadFileSizeMax.format(sax.formatFileSize(sax.maxProfileImageSize))); - 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))); + if (sax.data.imageContentTypes.indexOf(file.type) < 0) 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(); diff --git a/SaxoniteCoreWebOnline/wwwroot/js/sax-security.js b/SaxoniteCoreWebOnline/wwwroot/js/sax-security.js index d8389ec..05e9ced 100644 --- a/SaxoniteCoreWebOnline/wwwroot/js/sax-security.js +++ b/SaxoniteCoreWebOnline/wwwroot/js/sax-security.js @@ -29,7 +29,7 @@ sax.onScorePassword = function (response) { var score = (response || {}).score || 0; - sax.$prgPasswordStrength.progressbar("option", "value", score).removeClass('amber green').addClass(score > 3 ? 'green' : score > 2 ? 'amber' : ''); + sax.$prgPwordStrength.progressbar("option", "value", score).removeClass('amber green').addClass(score > 3 ? 'green' : score > 2 ? 'amber' : ''); } //-------------------------------------------------------------- @@ -76,16 +76,17 @@ $('#ifEmailAddress,#ifPasswordCurrent,#ifPassword,#ifPasswordConfirm').on('input', function () { sax.clearControlError(this); }); sax.$frm.signIn.on('keypress', '#ifEmailAddress,#ifPassword,#chkKeepMeSignedIn', function (e) { if (e.which == 13 && $('#ifEmailAddress', sax.$frm.signIn).val() && $('#ifPassword', sax.$frm.signIn).val()) sax.doSignin(); }); sax.$frm.signIn.on('click', '#pbSignIn', function () { sax.doSignin(); }).on('keypress', '#pbSignIn', function (e) { if (e.which == 13) sax.doSignin(); }); - sax.$frm.changePassword = $('#frmChangePassword'); - sax.$frm.changePassword.on('keypress', '#ifPasswordCurrent,#ifPassword,#ifPasswordConfirm', function (e) { if (e.which == 13) sax.doChangePassword(); }); - sax.$frm.changePassword.on('keyup', '#ifPassword', sax.doScorePassword); - sax.$prgPasswordStrength = $('#prgPasswordStrength', sax.$frm.changePassword).progressbar({ 'max': 5 }); - sax.$frm.changePassword.on('click', '#pbChangePassword', function () { sax.doChangePassword(); }).on('keypress', '#pbChangePassword', function (e) { if (e.which == 13) sax.doChangePassword(); }); + sax.$frm.chgPword = $('#frmChangePassword'); + sax.$frm.chgPword.on('keypress', '#ifPasswordCurrent,#ifPassword,#ifPasswordConfirm', function (e) { if (e.which == 13) sax.doChangePassword(); }); + sax.$frm.chgPword.on('keyup', '#ifPassword', sax.doScorePassword); + sax.$prgPwordStrength = $('#prgPasswordStrength', sax.$frm.chgPword).progressbar({ 'max': 5 }); + $('#pbChangePassword', sax.$frm.chgPword).on('click', function () { sax.doChangePassword(); }).on('keypress', function (e) { if (e.which == 13) sax.doChangePassword(); }); sax.$frm.reqPword = $('#frmRequestPassword'); sax.$frm.reqPword.on('keypress', '#ifEmailAddress', function (e) { if (e.which == 13) sax.doRequestPassword(); }); - sax.$frm.reqPword.on('click', '#pbRequestPassword', function () { sax.doRequestPassword(); }); + $('#pbRequestPassword', sax.$frm.reqPword).on('click', function () { sax.doRequestPassword(); }).on('keypress', function (e) { if (e.which == 13) sax.doRequestPassword(); }); }); + //-------------------------------------------------------------- }(window, window.document)); /* The End */ \ No newline at end of file diff --git a/SaxoniteCoreWebOnline/wwwroot/js/sax-usr-pro.js b/SaxoniteCoreWebOnline/wwwroot/js/sax-usr-pro.js index f0a9c08..3a6a379 100644 --- a/SaxoniteCoreWebOnline/wwwroot/js/sax-usr-pro.js +++ b/SaxoniteCoreWebOnline/wwwroot/js/sax-usr-pro.js @@ -48,6 +48,7 @@ if (!response || !response.success) return; sax.data.genders = response.genders; sax.data.months = response.months; + sax.data.imageContentTypes = response.imageContentTypes; $('.sax-message', $pnl.proCon).stop(true, false).fadeOut(200).empty(); sax.refreshInput($pnl.proConFrm, 'AccountEmailAddress', response.contact.emailAddress); sax.refreshSelect($pnl.proConFrm, 'AccountTitle', response.contact.title); @@ -148,7 +149,7 @@ { //sax.debug('onDropContactImageFile', file, context); if (file.size > sax.maxProfileImageSize) return sax.doAlert(sax.dlg.title.imageUpload, sax.msg.uploadFileSizeMax.format(sax.formatFileSize(sax.maxProfileImageSize))); - 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))); + if ((sax.data.imageContentTypes||[]).indexOf(file.type) < 0) return sax.doAlert(sax.dlg.title.imageUpload, sax.msg.uploadFileTypeInvalid.format(sax.getFileType(file))); $(".UploadMessage", context).text(sax.msg.uploadingFile.format(file.name)); $('img.ContactImageUploadProgress', context).fadeIn(200); sax.clearControlError($pnl.contactImageDrag); diff --git a/SaxoniteStandardBase/Base/App/Sax-Config.cs b/SaxoniteStandardBase/Base/App/Sax-Config.cs index 73d913f..d2ab573 100644 --- a/SaxoniteStandardBase/Base/App/Sax-Config.cs +++ b/SaxoniteStandardBase/Base/App/Sax-Config.cs @@ -73,8 +73,29 @@ namespace Saxonite.Net.Standard.Base.App public static int Periodic { get; } = SacsSystem.DebugMode ? 120 : 1800; } } + public static class Profile + { + public static class User + { + public static class Image + { + public static ushort WidthMax { get; } = 320; + public static ushort HeightMax { get; } = 400; + } + } + } public static class Articles { + public static class Segments + { + public static ushort Max { get; } = 12; + public static ushort CharsMax { get; } = 8000; + } + public static class Images + { + public static ushort WidthMax { get; } = 800; + public static ushort HeightMax { get; } = 640; + } public static ushort PublishMaxDaysBefore { get; } = 7; public static ushort PublishMaxDaysAhead { get; } = 10; } diff --git a/SaxoniteStandardBase/Base/App/Sax-Version.cs b/SaxoniteStandardBase/Base/App/Sax-Version.cs index 3ef71bd..8d0f1e3 100644 --- a/SaxoniteStandardBase/Base/App/Sax-Version.cs +++ b/SaxoniteStandardBase/Base/App/Sax-Version.cs @@ -18,7 +18,7 @@ namespace Saxonite.Net.Standard.Base.App //------------------------------------------------------------------- public static class Version { - private static String version { get; } = "1.2.5.1"; + private static String version { get; } = "1.2.6.0"; // Retrieve version. public static String GetFullVersion () { return version; } public static String GetAppVersion () @@ -40,6 +40,8 @@ namespace Saxonite.Net.Standard.Base.App //------------------------------------------------------------------- // Version history. //------------------------------------------------------------------- + // 1.2.6 ??-May-2022 Image resizing + // - Profile and article images resized to maximum width/height. // 1.2.5 20-Apr-2022 Article feedback // - Online Activity/Article feedback 5 stars, summary on Article list/popup. // - Online Activity filter. diff --git a/SaxoniteStandardBase/Base/Bus/SaxItemImage.cs b/SaxoniteStandardBase/Base/Bus/SaxItemImage.cs index 2e487e5..d830636 100644 --- a/SaxoniteStandardBase/Base/Bus/SaxItemImage.cs +++ b/SaxoniteStandardBase/Base/Bus/SaxItemImage.cs @@ -211,6 +211,37 @@ namespace Saxonite.Net.Standard.Base.Bus return new ItemId((short)Sax.DataItemType.ItemImage, Id); } + // Validation. + public static List ValidItemTypes + { + get + { + return new List { Sax.DataItemType.UserAccount, Sax.DataItemType.Organisation, Sax.DataItemType.Article }; + } + } + public static List GetValidMimeTypes(Sax.DataItemType itemType) + { + switch (itemType) + { + case Sax.DataItemType.UserAccount: + case Sax.DataItemType.Organisation: + case Sax.DataItemType.Article: + return new List { Sacs.MimeType.Jpeg, Sacs.MimeType.Webp, Sacs.MimeType.Png }; + } + return new List(); + } + public static List GetValidContentTypes(Sax.DataItemType itemType) + { + switch (itemType) + { + case Sax.DataItemType.UserAccount: + case Sax.DataItemType.Organisation: + case Sax.DataItemType.Article: + return new List { "image/jpg", "image/jpeg", "image/webp", "image/png" }; + } + return new List(); + } + //------------------------------------------------------------------- // Properties. public Data GetData() { return data.Clone(); } diff --git a/SaxoniteStandardBase/Base/Mgr/SaxItemDataMgr-ItemImage.cs b/SaxoniteStandardBase/Base/Mgr/SaxItemDataMgr-ItemImage.cs index 74820ae..05a81a2 100644 --- a/SaxoniteStandardBase/Base/Mgr/SaxItemDataMgr-ItemImage.cs +++ b/SaxoniteStandardBase/Base/Mgr/SaxItemDataMgr-ItemImage.cs @@ -72,10 +72,18 @@ namespace Saxonite.Net.Standard.Base.Mgr //--------------------------------------------------------------- // Item Id. - if (errField.ItemId != SacsHierarchyItem.ValidValue.Ignore && !ItemId.IsValid(data.ItemId)) + if (errField.ItemId != SacsHierarchyItem.ValidValue.Ignore) { - errField.ItemId = SacsHierarchyItem.ValidValue.Invalid; - return SetErrorState("Item Id must be specified"); + if (!ItemId.IsValid(data.ItemId)) + { + errField.ItemId = SacsHierarchyItem.ValidValue.Invalid; + return SetErrorState("Item Id must be specified"); + } + if (!SaxItemImage.ValidItemTypes.Contains((Sax.DataItemType)data.ItemId.Type)) + { + errField.ItemId = SacsHierarchyItem.ValidValue.Invalid; + return SetErrorState("Invalid item type specified"); + } } // Image type. if (errField.ImageType != SacsHierarchyItem.ValidValue.Ignore && (int)data.ImageType < 1)