Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public async Task<string> ValidateSeName<T>(T entity, string seName, string name
entityUrl.EntityName.Equals(entityName,
StringComparison.OrdinalIgnoreCase));

var reserved2 = seoSettings.ReservedEntityUrlSlugs.Contains(tempSeName, StringComparer.OrdinalIgnoreCase);
var reserved2 = seoSettings.ReservedEntityUrlSlugs?.Contains(tempSeName, StringComparer.OrdinalIgnoreCase) ?? false;
var reserved3 = (await languageService.GetAllLanguages(true)).Any(language =>
language.UniqueSeoCode.Equals(tempSeName, StringComparison.OrdinalIgnoreCase));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,28 @@ public void Setup()
_seNameService = new SeNameService(_mockSlugService.Object, _mockLanguageService.Object, _seoSettings);
}

[Test]
public async Task ValidateSeName_NullReservedSlugs_DoesNotThrow()
{
// Arrange - simulate a MongoDB installation where ReservedEntityUrlSlugs was stored as null
var settingsWithNullSlugs = new SeoSettings {
ReservedEntityUrlSlugs = null,
ConvertNonWesternChars = false,
AllowUnicodeCharsInUrls = false,
AllowSlashChar = false,
SeoCharConversion = null
};
var serviceWithNullSettings = new SeNameService(
_mockSlugService.Object, _mockLanguageService.Object, settingsWithNullSlugs);

var entity = new TestEntity { Id = "123" };
_mockSlugService.Setup(s => s.GetBySlug(It.IsAny<string>())).ReturnsAsync((EntityUrl)null);

// Act & Assert – should not throw ArgumentNullException
var result = await serviceWithNullSettings.ValidateSeName(entity, "my-page", "My Page", false);
ClassicAssert.AreEqual("my-page", result);
}

[Test]
public async Task ValidateSeName_ShouldReturnName_WhenSeNameIsEmpty()
{
Expand Down
8 changes: 4 additions & 4 deletions src/Web/Grand.Web.Admin/Areas/Admin/Views/Page/List.cshtml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
@model PageListModel
@inject AdminAreaSettings adminAreaSettings
@model PageListModel
@{
//page title
ViewBag.Title = Loc["Admin.Content.Pages"];
Expand Down Expand Up @@ -77,15 +78,14 @@
// Cancel the changes
this.cancelChanges();
},
pageSize: @(adminAreaSettings.DefaultGridPageSize),
serverPaging: true,
serverFiltering: true,
serverSorting: true
},
pageable: {
refresh: true,
numeric: false,
previousNext: false,
info: false
pageSizes: [@(adminAreaSettings.GridPageSizes)]
},
editable: {
confirmation: false,
Expand Down
6 changes: 4 additions & 2 deletions src/Web/Grand.Web.Admin/Controllers/PageController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,11 @@ public async Task<IActionResult> List(DataSourceRequest command, PageListModel m
(x.Title != null && x.Title.ToLowerInvariant().Contains(model.Name.ToLowerInvariant()))).ToList();
//"Error during serialization or deserialization using the JSON JavaScriptSerializer. The length of the string exceeds the value set on the maxJsonLength property. "
foreach (var page in pageModels) page.Body = "";
var total = pageModels.Count;
var pagedData = pageModels.Skip((command.Page - 1) * command.PageSize).Take(command.PageSize).ToList();
var gridModel = new DataSourceResult {
Data = pageModels,
Total = pageModels.Count
Data = pagedData,
Total = total
};

return Json(gridModel);
Expand Down
37 changes: 37 additions & 0 deletions src/Web/Grand.Web.Store/Areas/Store/Views/Page/Create.cshtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
@model PageModel
@{
//page title
ViewBag.Title = Loc["Admin.Content.Pages.AddNew"];
Layout = Constants.LayoutStore;
}
<form asp-area="@Constants.AreaStore" asp-controller="Page" asp-action="Create" method="post">

<div class="row">
<div class="col-md-12">
<div class="x_panel light form-fit">
<div class="x_title">
<div class="caption">
<i class="fa fa-fw fa-file-o"></i>
@Loc["Admin.Content.Pages.AddNew"]
<small>
<i class="fa fa-arrow-circle-left"></i>@Html.ActionLink(Loc["Admin.Content.Pages.BackToList"], "List")
</small>
</div>
<div class="actions">
<div class="btn-group btn-group-devided">
<button class="btn btn-success" type="submit" name="save">
<i class="fa fa-check"></i> @Loc["Admin.Common.Save"]
</button>
<button class="btn btn-success" type="submit" name="save-continue">
<i class="fa fa-check-circle"></i> @Loc["Admin.Common.SaveContinue"]
</button>
</div>
</div>
</div>
<div class="x_content form">
<partial name="Partials/CreateOrUpdate" model="Model"/>
</div>
</div>
</div>
</div>
</form>
60 changes: 60 additions & 0 deletions src/Web/Grand.Web.Store/Areas/Store/Views/Page/Edit.cshtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
@model PageModel
@{
//page title
ViewBag.Title = Loc["Admin.Content.Pages.EditPageDetails"];
Layout = Constants.LayoutStore;
}
<form asp-area="@Constants.AreaStore" asp-controller="Page" asp-action="Edit" method="post">

<div class="row">
<div class="col-md-12">
<div class="x_panel light form-fit">
<div class="x_title">
<div class="caption">
<i class="fa fa-fw fa-file-o"></i>
@Loc["Admin.Content.Pages.EditPageDetails"] - @Model.SystemName
<small>
<i class="fa fa-arrow-circle-left"></i>@Html.ActionLink(Loc["Admin.Content.Pages.BackToList"], "List")
</small>
</div>
<div class="actions">
<div class="btn-group btn-group-devided">
@if (!string.IsNullOrEmpty(Model.SeName))
{
<button type="button" onclick="window.open('@Url.RouteUrl("Page", new { Model.SeName })','_blank');" class="btn purple">
Comment thread
KrzysztofPajak marked this conversation as resolved.
<i class="fa fa-eye"></i>
@Loc["Admin.Common.Preview"]
</button>
}
<button class="btn btn-success" type="submit" name="save">
<i class="fa fa-check"></i> @Loc["Admin.Common.Save"]
</button>
<button class="btn btn-success" type="submit" name="save-continue">
<i class="fa fa-check-circle"></i> @Loc["Admin.Common.SaveContinue"]
</button>
@if (ViewBag.ShowCopyButton == true)
{
<button type="submit" form="page-copy-form" class="btn blue">
<i class="fa fa-copy"></i> @Loc["Admin.Common.Copy"]
</button>
}
<span id="page-delete" class="btn red">
<i class="fa fa-trash-o"></i> @Loc["Admin.Common.Delete"]
</span>
</div>
</div>
</div>
<div class="x_content form">
<partial name="Partials/CreateOrUpdate" model="Model"/>
</div>
</div>
</div>
</div>
</form>
<admin-delete-confirmation button-id="page-delete"/>
@if (ViewBag.ShowCopyButton == true)
{
<form id="page-copy-form" asp-area="@Constants.AreaStore" asp-controller="Page" asp-action="Copy" method="post">
<input type="hidden" name="id" value="@Model.Id"/>
</form>
}
167 changes: 167 additions & 0 deletions src/Web/Grand.Web.Store/Areas/Store/Views/Page/List.cshtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
@model PageListModel
@inject AdminAreaSettings adminAreaSettings
@{
//page title
ViewBag.Title = Loc["Admin.Content.Pages"];
Layout = Constants.LayoutStore;
}

<div class="row">
<div class="col-md-12">
<div class="x_panel light form-fit">
<div class="x_title">
<div class="caption">
<i class="fa fa-fw fa-file-o"></i>
@Loc["Admin.Content.Pages"]
</div>
<div class="actions btn-group btn-group-devided">
<a href="@Url.Action("Create", new { area = Constants.AreaStore })" class="btn green">
<i class="fa fa-plus"></i><span class="d-none d-sm-inline"> @Loc["Admin.Common.AddNew"] </span>
</a>
</div>
</div>
<div class="x_content">
<div class="form-horizontal">
<div class="form-body">
<div class="row align-items-end mb-1">
<div class="col-md-9 col-sm-12 col-12">
<div class="form-group mb-0">
<admin-label asp-for="Name" class="d-flex flex-column align-items-start mw-100 px-0 text-left"/>
<admin-input asp-for="Name"/>
</div>
</div>
<div class="col-md-3 col-sm-12 col-12 mt-md-0 mt-3">
<div class="form-actions">
<button class="btn btn-success filter-submit" id="search-pages">
<i class="fa fa-search"></i> @Loc["Admin.Common.Search"]
</button>
</div>
</div>
</div>
</div>
<div class="x_content">
<admin-tabstrip name="pages-list-tabs" BindGrid="true">
<items>
<tabstrip-item text="@Loc["Admin.Content.Pages.List.StorePages"]" tab-index="0">
<content>
<div id="store-pages-grid"></div>
</content>
</tabstrip-item>
<tabstrip-item text="@Loc["Admin.Content.Pages.List.GlobalPages"]" tab-index="1">
<content>
<div id="global-pages-grid"></div>
</content>
</tabstrip-item>
</items>
</admin-tabstrip>
</div>
</div>
</div>
</div>
</div>
</div>

<script>
$(document).ready(function () {
var gridColumns = [{
field: "SystemName",
title: "@Loc["Admin.Content.Pages.Fields.SystemName"]",
template: '<a class="k-link" href="Edit/#=Id#">#=SystemName#</a>',
}, {
field: "Title",
title: "@Loc["Admin.Content.Pages.Fields.Title"]",
width: 300,
template: '<a class="k-link" href="Edit/#=Id#">#=kendo.htmlEncode(Title == null ? "" : Title)#</a>',
}, {
field: "IncludeInMenu",
title: "@Loc["Admin.Content.Pages.Fields.IncludeInMenu"]",
width: 100,
headerAttributes: { style: "text-align:center" },
attributes: { style: "text-align:center" },
template: '# if(IncludeInMenu) {# <i class="fa fa-check" aria-hidden="true" style="color:green"></i> #} else {# <i class="fa fa-times" aria-hidden="true" style="color:red"></i> #} #'
}, {
field: "Published",
title: "@Loc["Admin.Content.Pages.Fields.Published"]",
width: 100,
headerAttributes: { style: "text-align:center" },
attributes: { style: "text-align:center" },
template: '# if(Published) {# <i class="fa fa-check" aria-hidden="true" style="color:green"></i> #} else {# <i class="fa fa-times" aria-hidden="true" style="color:red"></i> #} #'
}, {
field: "DisplayOrder",
title: "@Loc["Admin.Content.Pages.Fields.DisplayOrder"]",
width: 100
}];

function makeDataSource(url) {
return {
transport: {
read: {
url: url,
type: "POST",
dataType: "json",
data: additionalData
}
},
schema: {
data: "Data",
total: "Total",
errors: "Errors"
},
error: function(e) {
display_kendoui_grid_error(e);
this.cancelChanges();
},
pageSize: @(adminAreaSettings.DefaultGridPageSize),
serverPaging: true,
serverFiltering: true,
serverSorting: true
};
}

function makeGrid(selector, url) {
$(selector).kendoGrid({
dataSource: makeDataSource(url),
pageable: {
refresh: true,
pageSizes: [@(adminAreaSettings.GridPageSizes)]
},
editable: {
confirmation: false,
mode: "inline"
},
scrollable: false,
columns: gridColumns
});
}

makeGrid("#store-pages-grid", "@Html.Raw(Url.Action("StorePagesList", "Page", new { area = Constants.AreaStore }))");
makeGrid("#global-pages-grid", "@Html.Raw(Url.Action("GlobalPagesList", "Page", new { area = Constants.AreaStore }))");
});
</script>

<script>
$(document).ready(function () {
$('#search-pages').click(function () {
var sg = $('#store-pages-grid').data('kendoGrid');
if (sg) sg.dataSource.read();
var gg = $('#global-pages-grid').data('kendoGrid');
if (gg) gg.dataSource.read();
return false;
});

$("#@Html.IdFor(model => model.Name)").keydown(function (event) {
if (event.keyCode == 13) {
$("#search-pages").click();
return false;
}
});
});

function additionalData() {
var data = {
Name: $('#@Html.IdFor(model => model.Name)').val()
};
addAntiForgeryToken(data);
return data;
}
</script>
Loading
Loading