Skip to content

Commit

Permalink
build(sln): v29.3.0.5 - more admin extensions
Browse files Browse the repository at this point in the history
  • Loading branch information
seangwright committed Aug 7, 2024
1 parent ffc32de commit 877d9ca
Show file tree
Hide file tree
Showing 10 changed files with 474 additions and 7 deletions.
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project>
<!-- See https://aka.ms/dotnet/msbuild/customize for more details on customizing your build -->
<PropertyGroup>
<VersionPrefix>29.3.0.3</VersionPrefix>
<VersionPrefix>29.3.0.5</VersionPrefix>
</PropertyGroup>

<PropertyGroup>
Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ This project is using [Xperience Version v29.3.0](https://docs.kentico.com/chang
<a href="https://raw.githubusercontent.com/Kentico/community-portal/main/images/portal-admin-badge-assignment-management.jpg">
<img src="https://raw.githubusercontent.com/Kentico/community-portal/main/images/portal-admin-badge-assignment-management.jpg" width="400" alt="Portal Admin member badge assignment">
</a>

<a href="https://raw.githubusercontent.com/Kentico/community-portal-internal/main/images/portal-admin-website-page-search.jpg">
<img src="https://raw.githubusercontent.com/Kentico/community-portal-internal/main/images/portal-admin-website-page-search.jpg" width="400" alt="Portal Admin website channel page search">
</a>
</div>

## Site Info
Expand Down
Binary file added images/portal-admin-website-page-search.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
using CMS.Membership;
using CMS.Websites.Internal;
using Kentico.Community.Portal.Admin.Features.QAndA;
using Kentico.Community.Portal.Core;
using Kentico.Community.Portal.Core.Modules;
using Kentico.Xperience.Admin.Base;
using Kentico.Xperience.Admin.Websites.UIPages;

[assembly: UIPage(
parentType: typeof(QAndAApplicationPage),
Expand All @@ -16,8 +18,10 @@

namespace Kentico.Community.Portal.Admin.Features.QAndA;

public class QAndAListingPage : ListingPage
public class QAndAListingPage(IPageUrlGenerator pageUrlGenerator) : ListingPage
{
private readonly IPageUrlGenerator pageUrlGenerator = pageUrlGenerator;

protected override string ObjectType => QAndAAnswerDataInfo.OBJECT_TYPE;

/// <summary>
Expand Down Expand Up @@ -83,7 +87,7 @@ public override async Task ConfigurePage()
_ = PageConfiguration.TableActions.AddDeleteAction(nameof(Delete));
}

private static TableRowLinkProps AnswerLinkModelRetriever(object value, IDataContainer container)
private TableRowLinkProps AnswerLinkModelRetriever(object value, IDataContainer container)
{
int webPageItemID = ValidationHelper.GetInteger(container[nameof(WebPageItemInfo.WebPageItemID)], 0);
int websiteChannelID = ValidationHelper.GetInteger(container[nameof(QAndAAnswerDataInfo.QAndAAnswerDataWebsiteChannelID)], 0);
Expand All @@ -95,10 +99,14 @@ private static TableRowLinkProps AnswerLinkModelRetriever(object value, IDataCon
return new TableRowLinkProps() { Label = label, Path = "" };
}

string pageUrl = pageUrlGenerator.GenerateUrl<ContentTab>($"webpages-{websiteChannelID}", $"{PortalWebSiteChannel.DEFAULT_LANGUAGE}_{webPageItemID}");

return new TableRowLinkProps()
{
Label = label,
Path = $"/admin/webpages-{websiteChannelID}/en-US_{webPageItemID}/content"
Path = pageUrl.StartsWith('/')
? pageUrl[1..]
: pageUrl
};
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,283 @@
using CMS.Base;
using CMS.ContentEngine;
using CMS.ContentEngine.Internal;
using CMS.Core;
using CMS.DataEngine;
using CMS.Membership;
using CMS.Websites;
using CMS.Websites.Internal;
using CSharpFunctionalExtensions;
using Kentico.Community.Portal.Admin.Features.WebsiteChannels;
using Kentico.Xperience.Admin.Base.FormAnnotations;
using Kentico.Xperience.Admin.Base;
using Kentico.Xperience.Admin.Base.Filters;
using Kentico.Xperience.Admin.Base.Forms;
using Kentico.Xperience.Admin.Websites.UIPages;
using Kentico.Community.Portal.Admin.UIPages;

[assembly: UIPage(
parentType: typeof(WebsiteChannelsApplicationPage),
slug: "web-pages",
uiPageType: typeof(WebPageListingPage),
name: "Web Page List",
icon: Icons.Magnifier,
templateName: TemplateNames.LISTING,
order: 0)]

namespace Kentico.Community.Portal.Admin.Features.WebsiteChannels;

public class WebPageListingPage(IPageUrlGenerator pageUrlGenerator, IConversionService conversion) : ListingPage
{
private readonly IPageUrlGenerator pageUrlGenerator = pageUrlGenerator;
private readonly IConversionService conversion = conversion;

protected override string ObjectType => ContentItemInfo.OBJECT_TYPE;

/// <summary>
/// Deletes user specified by the <paramref name="id"/> parameter.
/// </summary>
[PageCommand(Permission = SystemPermissions.DELETE)]
public override Task<ICommandResponse<RowActionResult>> Delete(int id) => base.Delete(id);

public override async Task ConfigurePage()
{
await base.ConfigurePage();

PageConfiguration.FilterFormModel = new WebPageListMultiFilter();

PageConfiguration.QueryModifiers
.AddModifier((query, settings) =>
{
return query
.Source(source => source
.Join<WebPageItemInfo>(nameof(ContentItemInfo.ContentItemID), nameof(WebPageItemInfo.WebPageItemContentItemID))
.Join<ContentItemCommonDataInfo>(nameof(WebPageItemInfo.WebPageItemContentItemID), nameof(ContentItemCommonDataInfo.ContentItemCommonDataContentItemID))
.Join<ContentItemLanguageMetadataInfo>(nameof(ContentItemCommonDataInfo.ContentItemCommonDataContentItemID), nameof(ContentItemLanguageMetadataInfo.ContentItemLanguageMetadataContentItemID),
additionalCondition: new WhereCondition($"CMS_ContentItemCommonData.ContentItemCommonDataContentLanguageID = CMS_ContentItemLanguageMetadata.ContentItemLanguageMetadataContentLanguageID"))
.Join<WebsiteChannelInfo>($"CMS_WebPageItem.{nameof(WebPageItemInfo.WebPageItemWebsiteChannelID)}", nameof(WebsiteChannelInfo.WebsiteChannelID))
.Join<ChannelInfo>(nameof(WebsiteChannelInfo.WebsiteChannelChannelID), nameof(ChannelInfo.ChannelID))
.Join<DataClassInfo>($"CMS_ContentItem.{nameof(ContentItemInfo.ContentItemContentTypeID)}", nameof(DataClassInfo.ClassID))
.Join<ContentLanguageInfo>($"CMS_ContentItemLanguageMetadata.{nameof(ContentItemLanguageMetadataInfo.ContentItemLanguageMetadataContentLanguageID)}", nameof(ContentLanguageInfo.ContentLanguageID)))
.AddColumn(nameof(WebsiteChannelInfo.WebsiteChannelID))
.AddColumn(nameof(ContentLanguageInfo.ContentLanguageName))
.AddColumn(nameof(DataClassInfo.ClassName));
});

PageConfiguration.ColumnConfigurations
.AddColumn(nameof(WebPageItemInfo.WebPageItemID),
"ID",
searchable: true,
maxWidth: 1)
.AddComponentColumn(nameof(ContentItemLanguageMetadataInfo.ContentItemLanguageMetadataDisplayName),
"@kentico-community/portal-web-admin/Link",
modelRetriever: WebPageLinkModelRetriever,
caption: "Name",
searchable: true,
minWidth: 25)
.AddColumn(nameof(DataClassInfo.ClassDisplayName),
"Content Type",
maxWidth: 5)
.AddColumn(nameof(ContentLanguageInfo.ContentLanguageDisplayName),
"Language",
maxWidth: 5)
.AddColumn(nameof(ChannelInfo.ChannelDisplayName),
"Channel",
maxWidth: 10)
.AddColumn(nameof(WebPageItemInfo.WebPageItemTreePath),
"Path",
searchable: true,
minWidth: 50);
}

private TableRowLinkProps WebPageLinkModelRetriever(object value, IDataContainer container)
{
int webPageItemID = conversion.GetInteger(container[nameof(WebPageItemInfo.WebPageItemID)], 0);
int channelID = conversion.GetInteger(container[nameof(WebsiteChannelInfo.WebsiteChannelID)], 0);
string languageName = conversion.GetString(container[nameof(ContentLanguageInfo.ContentLanguageName)], "");
string valueStr = value.ToString() ?? "";

if (webPageItemID == 0 || channelID == 0 || string.IsNullOrWhiteSpace(languageName))
{
return new TableRowLinkProps() { Label = valueStr, Path = "" };
}

string pageUrl = pageUrlGenerator.GenerateUrl<PageBuilderTab>($"webpages-{channelID}", $"{languageName}_{webPageItemID}");

return new TableRowLinkProps()
{
Label = valueStr,
Path = pageUrl.StartsWith('/')
? pageUrl[1..]
: pageUrl
};
}

}

public class TableRowLinkProps
{
public string Path { get; set; } = "";
public string Label { get; set; } = "";
}

public class WebPageListMultiFilter
{
[GeneralSelectorComponent(
dataProviderType: typeof(ContentTypeGeneralSelectorDataProvider),
Label = "Content Types",
Placeholder = "Any",
Order = 0
)]
[FilterCondition(
BuilderType = typeof(GeneralWhereInWhereConditionBuilder),
ColumnName = nameof(DataClassInfo.ClassName)
)]
public IEnumerable<string> ContentTypes { get; set; } = [];

[GeneralSelectorComponent(
dataProviderType: typeof(WebsiteChannelGeneralSelectorDataProvider),
Label = "Channels",
Placeholder = "Any",
Order = 0
)]
[FilterCondition(
BuilderType = typeof(GeneralWhereInWhereConditionBuilder),
ColumnName = nameof(ChannelInfo.ChannelName)
)]
public IEnumerable<string> Channels { get; set; } = [];

[GeneralSelectorComponent(
dataProviderType: typeof(ContentLanguageGeneralSelectorDataProvider),
Label = "Languages",
Placeholder = "Any",
Order = 0
)]
[FilterCondition(
BuilderType = typeof(GeneralWhereInWhereConditionBuilder),
ColumnName = nameof(ContentLanguageInfo.ContentLanguageName)
)]
public IEnumerable<string> Languages { get; set; } = [];
}

public class ContentTypeGeneralSelectorDataProvider
: IGeneralSelectorDataProvider
{
private static ObjectSelectorListItem<string> InvalidItem => new() { IsValid = false, Text = "Inavlid", Value = "" };

public async Task<PagedSelectListItems<string>> GetItemsAsync(string searchTerm, int pageIndex, CancellationToken cancellationToken)
{
var items = await DataClassInfoProvider.GetClasses()
.WhereEquals(nameof(DataClassInfo.ClassContentTypeType), ClassContentTypeType.WEBSITE)
.GetEnumerableTypedResultAsync();

if (!string.IsNullOrEmpty(searchTerm))
{
items = items.Where(i => i.ClassDisplayName.StartsWith(searchTerm, StringComparison.OrdinalIgnoreCase));
}

return new PagedSelectListItems<string>()
{
NextPageAvailable = false,
Items = items.Select(i => new ObjectSelectorListItem<string> { IsValid = true, Text = i.ClassDisplayName, Value = i.ClassName }),
};
}

public async Task<IEnumerable<ObjectSelectorListItem<string>>> GetSelectedItemsAsync(IEnumerable<string> selectedValues, CancellationToken cancellationToken)
{
var items = await DataClassInfoProvider.GetClasses()
.WhereEquals(nameof(DataClassInfo.ClassContentTypeType), ClassContentTypeType.WEBSITE)
.GetEnumerableTypedResultAsync();

return (selectedValues ?? []).Select(GetSelectedItemByValue(items));

}

private Func<string, ObjectSelectorListItem<string>> GetSelectedItemByValue(IEnumerable<DataClassInfo> websiteChannelContentTypes) =>
(string className) => websiteChannelContentTypes
.TryFirst(c => string.Equals(c.ClassName, className))
.Map(c => new ObjectSelectorListItem<string> { IsValid = true, Text = c.ClassDisplayName, Value = c.ClassName })
.GetValueOrDefault(InvalidItem);

}

public class WebsiteChannelGeneralSelectorDataProvider(IInfoProvider<ChannelInfo> channelProvider)
: IGeneralSelectorDataProvider
{
private readonly IInfoProvider<ChannelInfo> channelProvider = channelProvider;

private static ObjectSelectorListItem<string> InvalidItem => new() { IsValid = false, Text = "Inavlid", Value = "" };

public async Task<PagedSelectListItems<string>> GetItemsAsync(string searchTerm, int pageIndex, CancellationToken cancellationToken)
{
var items = await channelProvider.Get()
.WhereEquals(nameof(ChannelInfo.ChannelType), ChannelType.Website.ToString())
.GetEnumerableTypedResultAsync();

if (!string.IsNullOrEmpty(searchTerm))
{
items = items.Where(i => i.ChannelDisplayName.StartsWith(searchTerm, StringComparison.OrdinalIgnoreCase));
}

return new PagedSelectListItems<string>()
{
NextPageAvailable = false,
Items = items.Select(i => new ObjectSelectorListItem<string> { IsValid = true, Text = i.ChannelDisplayName, Value = i.ChannelName }),
};
}

public async Task<IEnumerable<ObjectSelectorListItem<string>>> GetSelectedItemsAsync(IEnumerable<string> selectedValues, CancellationToken cancellationToken)
{
var items = await channelProvider.Get()
.WhereEquals(nameof(ChannelInfo.ChannelType), ChannelType.Website.ToString())
.GetEnumerableTypedResultAsync();

return (selectedValues ?? []).Select(GetSelectedItemByValue(items));

}

private Func<string, ObjectSelectorListItem<string>> GetSelectedItemByValue(IEnumerable<ChannelInfo> websiteChannelContentTypes) =>
(string channelName) => websiteChannelContentTypes
.TryFirst(c => string.Equals(c.ChannelName, channelName))
.Map(c => new ObjectSelectorListItem<string> { IsValid = true, Text = c.ChannelDisplayName, Value = c.ChannelName })
.GetValueOrDefault(InvalidItem);
}

public class ContentLanguageGeneralSelectorDataProvider(IInfoProvider<ContentLanguageInfo> languageProvider)
: IGeneralSelectorDataProvider
{
private readonly IInfoProvider<ContentLanguageInfo> languageProvider = languageProvider;

private static ObjectSelectorListItem<string> InvalidItem => new() { IsValid = false, Text = "Inavlid", Value = "" };

public async Task<PagedSelectListItems<string>> GetItemsAsync(string searchTerm, int pageIndex, CancellationToken cancellationToken)
{
var items = await languageProvider.Get()
.GetEnumerableTypedResultAsync();

if (!string.IsNullOrEmpty(searchTerm))
{
items = items.Where(i => i.ContentLanguageDisplayName.StartsWith(searchTerm, StringComparison.OrdinalIgnoreCase));
}

return new PagedSelectListItems<string>()
{
NextPageAvailable = false,
Items = items.Select(i => new ObjectSelectorListItem<string> { IsValid = true, Text = i.ContentLanguageDisplayName, Value = i.ContentLanguageName }),
};
}

public async Task<IEnumerable<ObjectSelectorListItem<string>>> GetSelectedItemsAsync(IEnumerable<string> selectedValues, CancellationToken cancellationToken)
{
var items = await languageProvider.Get()
.GetEnumerableTypedResultAsync();

return (selectedValues ?? []).Select(GetSelectedItemByValue(items));

}

private Func<string, ObjectSelectorListItem<string>> GetSelectedItemByValue(IEnumerable<ContentLanguageInfo> languages) =>
(string languageName) => languages
.TryFirst(c => string.Equals(c.ContentLanguageName, languageName))
.Map(c => new ObjectSelectorListItem<string> { IsValid = true, Text = c.ContentLanguageDisplayName, Value = c.ContentLanguageName })
.GetValueOrDefault(InvalidItem);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using CMS.Membership;
using Kentico.Community.Portal.Admin;
using Kentico.Community.Portal.Admin.Features.WebsiteChannels;
using Kentico.Xperience.Admin.Base;

[assembly: UIApplication(
identifier: WebsiteChannelsApplicationPage.IDENTIFIER,
type: typeof(WebsiteChannelsApplicationPage),
slug: "website-channels",
name: "Website channels",
category: PortalWebAdminModule.COMMUNITY_CATEGORY,
icon: Icons.Earth,
templateName: TemplateNames.SECTION_LAYOUT)]

namespace Kentico.Community.Portal.Admin.Features.WebsiteChannels;

[UIPermission(SystemPermissions.VIEW)]
[UIPermission(SystemPermissions.CREATE)]
[UIPermission(SystemPermissions.UPDATE)]
[UIPermission(SystemPermissions.DELETE)]
public class WebsiteChannelsApplicationPage : ApplicationPage
{
public const string IDENTIFIER = "website-channels-app";
}
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ public Task<PagedSelectListItems<string>> GetItemsAsync(string searchTerm, int p
}

public Task<IEnumerable<ObjectSelectorListItem<string>>> GetSelectedItemsAsync(IEnumerable<string> selectedValues, CancellationToken cancellationToken) =>
Task.FromResult(selectedValues?.Select(v => GetSelectedItemByValue(v)) ?? []);
Task.FromResult((selectedValues ?? []).Select(GetSelectedItemByValue));

private ObjectSelectorListItem<string> GetSelectedItemByValue(string contentTypeTypeValue) =>
contentTypeTypeValue switch
Expand Down
Loading

0 comments on commit 877d9ca

Please sign in to comment.