Breaking Changes
On this site you'll find all breaking changes introduced with a new Confinity release.
For not breaking changes and diagnostics see Diagnostics
2025.1.9.0
Component Rendering
- Confinity page and view components don't receive a div wrapper anymore. To reenable this feature, set the option
ConfinityPages.EnableLegacyComponentWrappertotrue
2025.1.8.0
Products
The property ProductAttribute.Type is now required and must therefore always be set.
2025.1.7.0 [migration]
Pattern library
ComponentElementdoes not have a propertyItemsanymore. Use the methodItems()instead.
Migrations
- Only relevant when using the products module: This migration is implemented as an extension method, just create a new migration and replace the
Upmethod with the following code (Sql Server & Postgres only):
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.Confinity_2025_1_7_Up(); // set bool arguments accordingly
}
2025.1.6.0
ICspBuilder.GetNoncewas removed useICspBuilder.GetNonceAttribute()instead.ICspBuildermoved from namespaceConfinitytoConfinity.Web
Example update:
// old
@{string nonce = csp.GetNonce(CspDirectives.ScriptSrc);}
<script nonce="{{nonce}}">
// new
@{string nonce = csp.GetNonceAttribute(CspDirectives.ScriptSrc);}
<script {{nonce}}>
2025.1.5.0
IImageAssetEditor was removed.
2025.1.4.0
Excel Export
The methods in Confinity.EntityApp.Exporter.Exporter now always sanitize the values to prevent XSLX-injection with through formulas. If your export should contain formulas as before, use the newly introduced overloads with 'Unsafe' in the name.
DictionaryToXlsxAsync->DictionaryToXlsxUnsafeAsyncToXlsxAsync->ToXlsxUnsafeAsync
2025.1.3.0 [migration]
Migrations
- This migration is implemented as an extension method, just create a new migration and replace the
Upmethod with the following code (Sql Server & Postgres only):
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.Confinity_2025_1_3_Up();
}
This will apply the following migrations:
- A new flag
IsSuperadminwill be added toConfinityRole IsSuperadminwill be set totrueon the existing role with the keyconfinity.admin
2025.1.2.0 [migration]
Products
SelectStringAttributeDefinition got replaced by SelectSingleOptionAttributeDefinition.
The following classes are now internal:
StringListValueIntegerListValueDoubleListValue
When evaluating a AttributeValueModel.Value, expect the following types instead:
ICollection<double>ICollection<int>ICollection<string>
See Products for all possible types.
Migration
- Added index
IX_ConfinityReplicationSchedule_TransactionId.
2025.1.1.0 [migration]
Miscellaneous
- Added a new source analyzer that prevents default values for nullable properties in ConfinityContents.
Migration
- The index
IX_ConfinityAsset_Filename_FolderIdhas been removed.
Forms
- Replaced the property
FormFieldModel.NotSetValuewith the methodFormFieldModel.GetNotSetValue();
2025.1.0.0 [migration]
Miscellaneous
- Confinity is now updated to use .Net 9. The recommended way to update is the .NET Upgrade Assistant.
- The Confinity.Application assembly now references the Confinity.SourceAnalyzers so that this must no longer be included explicitly.
Blog
IQueryOptions, used inIBlogServicemethods, has a new optional parameterIgnoreEntityPermissions.
Pages
IPageModel.ChildPageModelsmoved toIPageService.GetChildPagesPageModelExtensions.PreorderTraversalexpects mandatory parameterIPageService- Implementations of
IDynamicPageResolver<T>should always useIgnoreQueryFilters()to read data. Check Reading ConfinityEntities - LinkModel requires a
Titleto be initialized (this helps the developer to not forget to set theTitle). The old value was an empty string. - ExternalLinkModel requires a
Urlto be initialized (this helps the developer to not forget to set theUrl). The old value was an empty string.
Assets
- The Confinity entity
Assethas a new propertyPublicNamethat should be used outside of entity apps, e.g. in a galleries. - The interface
IAssethas a new methodPublicName()that should be used outside of entity apps, e.g. in a galleries.
Products
- When working with
AttributeValueModel(e.g. by callingGetAttributeById,GetAttributesorGetGroupedAttributes) theValueproperty no longer returns entities or lists of entities butEntityRef<T>orICollection<EntityRef<T>(whereTis the entity).
Entity App
Exporter
- The following methods are now async and thereby got renamed with the Async suffix:
Exporter.DictionaryToXlsxAsync(in addition, the first parameter changed toIEnumerable<IDictionary<string, object?>>)Exporter.ToXlsxAsync
ISelectOptionsProvider
- The return value and parameters of
ISelectOptionsProvider<T>.GetOptionschanged
Task<IReadOnlyCollection<ISelectOption<T>>> GetOptions(GetOptionsParameters getOptionsParameters);
AddSelectSingle/AddSelectMultiple/AddSelectAsset/AddSelectAssets
The entity app form configuration changes slightly. For AddSelectSingle, AddSelectMultiple, AddSelectAsset and AddSelectAssets the generic types must be given explicitly, e.g.
tab.AddSelectSingle<EmployeeViewModel, Employee>(e => e.Picture!, a => a.Title);
tab.AddSelectMultiple<EmployeeViewModel, Employee>(e => e.TeamMembers!, a => a.FirstName + " " + a.LstName);
tab.AddSelectAsset<EmployeeViewModel>(e => e.Picture!);
AddComputed
- When defining an entity form with
AddComputed(...)(the non-generic call), make sure that all used entity properties are either reference by other form elements or callDependentPropertyfor those properties, e.g.
tab.AddComputed(p => a.News.Count(), "News count").DependentProperty(a => a.News);
Confinity Contents
ConfinityContentsgot new restrictions enforced by source analyzers. The provided code fixes and error messages should be enough to migrate. For details see (Confinity Content)[./confinity-content/confinity-content.md].- It is no longer possible to have a reference to a
ConfinityEntityin yourConfinityContent. Instead, theConfinityEntitymust be wrapped in aEntityRef<T>, e.g.
public class EmployeeViewModel : ConfinityContent
{
public string FullName {get; set; } = "";
public EntityRef<Employee>? Boss { get; set; }
public ICollection<EntityRef<Employee>>? TeamMembers { get; set; }
public EntityRef<Asset>? Picture { get; set; }
}
AddWysiwyg
- The property type must be migrated from
stringtoIHtmlContent.- to get a new
IHtmlContentyou can usenew HtmlString(X) HtmlContentUtilprovides utils to create a nullable type (FromNullable), to check if it is empty (IsNullOrWhiteSpace) or to convert tostring. Conversion tostringshould be avoided where possible or to HTML encode a string (GetEncoded).
- to get a new
Migration
- Create a migration with EF.Core and insert the following call in the beginning (either for Postgres or SQL Server)
Postgres
migrationBuilder.Sql(
"""
UPDATE "ConfinityAsset"
SET "Filename" = subquery."WithoutExtension"
|| '_'
|| subquery.row_number - 1
|| subquery."Extension"
FROM (SELECT "Id",
(regexp_matches("ConfinityAsset"."Filename", '(.+?)(\.[^.]*$|$)'))[1] "WithoutExtension",
(regexp_matches("ConfinityAsset"."Filename", '(.+?)(\.[^.]*$|$)'))[2] "Extension",
ROW_NUMBER() OVER (
PARTITION BY "Filename", "FolderId" ORDER BY "Id"
) AS row_number
FROM "ConfinityAsset") AS subquery
WHERE "ConfinityAsset"."Id" = subquery."Id"
AND subquery.row_number > 1;
UPDATE "__ConfinityHistory_ConfinityAsset" history
SET "Entity_Filename" = asset."Filename"
FROM "ConfinityAsset" asset
where history."Entity_Id" = asset."Id"
AND NOT ISFINITE(history."To")
AND history."Entity_Filename" <> asset."Filename";
""");
Sql Server
migrationBuilder.Sql(
"""
;
WITH CTE AS
(SELECT Filename,
REVERSE(SUBSTRING(REVERSE(Filename), CHARINDEX('.', REVERSE(Filename)) + 1,
LEN(Filename))) AS WithoutExtension,
REVERSE(LEFT(reverse(Filename), CHARINDEX('.', REVERSE(Filename)))) AS Extension,
ROW_NUMBER() OVER (
PARTITION BY Filename, Filename ORDER BY Id
) AS row_number
FROM ConfinityAsset)
UPDATE CTE
SET Filename = WithoutExtension + '_' + CONVERT(varchar(10), row_number - 1) + "Extension"
WHERE row_number > 1;
UPDATE history
SET history.Entity_Filename = asset."Filename"
FROM ConfinityAsset asset
INNER JOIN __ConfinityHistory_ConfinityAsset history
ON history.Entity_Id = asset.Id
WHERE history.[To] = '9999-12-31 23:59:59.9999999 +00:00'
AND history.Entity_Filename <> asset.Filename;
""");
- If you are using the
Confinity.Formsmodule, insert the following call in the end (either for Postgres or SQL Server)
Postgres
migrationBuilder.Sql(
"""
INSERT INTO "__ConfinityHistory_ConfinityFormSubmission"
(
"Id",
"Entity_ClientIpAddress",
"Entity_ConfinityFormId",
"Entity_ConfinityMetadata_ModifiedAt",
"Entity_ConfinityMetadata_ModifiedById",
"Entity_FormData",
"Entity_Id",
"Entity_PageUrl",
"From",
"To"
)
SELECT gen_random_uuid(),
"ClientIpAddress",
"ConfinityFormId",
"ConfinityMetadata_ModifiedAt",
"ConfinityMetadata_ModifiedById",
"FormData",
"Id",
"PageUrl",
"ConfinityMetadata_ModifiedAt",
'infinity'
FROM "ConfinityFormSubmission";
""");
Sql Server
migrationBuilder.Sql(
"""
INSERT INTO __ConfinityHistory_ConfinityFormSubmission
(Id,
Entity_ClientIpAddress,
Entity_ConfinityFormId,
Entity_ConfinityMetadata_ModifiedAt,
Entity_ConfinityMetadata_ModifiedById,
Entity_FormData,
Entity_Id,
Entity_PageUrl,
"From",
"To")
SELECT NEWID(),
ClientIpAddress,
ConfinityFormId,
ConfinityMetadata_ModifiedAt,
ConfinityMetadata_ModifiedById,
FormData,
Id,
PageUrl,
ConfinityMetadata_ModifiedAt,
'9999-12-31 23:59:59.9999999 +00:00'
FROM ConfinityFormSubmission
""");
Optional migration
Optionally, you might append the following script to set the new asset file PublicName to the same value as the already existing Name.
migrationBuilder.Sql(
"""
UPDATE "ConfinityAsset" SET "PublicName" = "Name";
UPDATE "__ConfinityHistory_ConfinityAsset" SET "Entity_PublicName" = "Entity_Name";
""");
2024.2.2.0
Entity App
- The following methods are now async and thereby got renamed with the Async suffix:
Exporter.DictionaryToXlsxAsync(in addition, the first parameter changed toIEnumerable<IDictionary<string, object?>>)Exporter.ToXlsxAsync
2024.2.0.0 [migration]
General
- Confinity is replacing the term 'Archive' with 'Rename'. Therefor the following methods got renamed:
- Renamed
ActionEventType.ArchivetoActionEventType.Delete. - Renamed
IReplicationService.ArchiveAsynctoIReplicationService.DeleteAsync. - Renamed
IUserService.ArchiveAsynctoIUserService.DeleteAsync. - Renamed
IUserService.ArchiveRangeAsynctoIUserService.DeleteRangeAsync. - Renamed
IAppBuilder.AddArchiveActiontoIAppBuilder.AddDeleteAction. - Renamed
ITableLayoutBuilder.AddArchiveActiontoITableLayoutBuilder.AddDeleteAction.
- Renamed
- The following entities don't have a history-table anymore (maybe update your db cleanup script?):
- Notification
- DataProtectionKey
- FormSubmission
- UserTaskEntityPayload
- UserTaskLog
- Removed
IDocumentAsset.
Authentication & JWT
UserServiceis no longer public- Removed the following properties from
ConfinityIdentity:Groups,Roles,Email - Removed the following properties from
ConfinityClaims:Username,GivenName,FamilyName,Email,Roles,Groups
AI Search & Chat-Bot
- Removed property
TypefromIContentSearchDocumentResult. UseAttributesinstead. - Removed
ElasticsearchAiServiceKeys - When querying the ElasticSearch-AI index, use
IElasticsearchAiQueryServiceinstead of the keyedISearchQueryService'.
Migration
- This migration is implemented as an extension method, just create a new migration and replace the
Upmethod with the following code (Sql Server & Postgres only):
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.Confinity_2024_2_0_Up();
}
In the Confinity_2024_2_0_Up method, you have to provide a set of Boolean, depending on whether you're using the corresponding module.
WYSIWYG Configuration
- TinyMCE has been upgraded to v6.8.5 and therefore the following configuration properties has been removed from
IWysiwygEditorConfiguration:PasteWordValidElements(replaced withPasteFromWordValidElements)TableResponsiveWidth(replaced withTableSizingMode)
- In
WysiwygEditorToolbarButtonthe following enum members have been renamed:StyleselecttoStylesFormatselecttoBlocks
Pages
HttpContext.ConfinityPage().Resources.AddJavaScriptandHttpContext.ConfinityPage().Resources.AddStylesheetremoved the propertyVersion. A version string is automatically created when the new propertyFilePathis set (preferred). If this is not wanted, add the parameter to your uri. To give attention to this change the propertyFileUriwas renamed toUri.
Examples preferred:
// old
httpContext.ConfinityPage().Resources.AddJavaScript(
new JavaScriptResource(new Uri(url, UriKind.Relative)) { Version = s_version }
);
// new
httpContext.ConfinityPage().Resources.AddJavaScript(
new JavaScriptResource() { FilePath = filePath } // filePath should be the relative path to the file. No call to Url.Content or something is required. The filePath must not start with a "~".
);
// old
string url = urlHelper.Content($"~/assets/bundled/{bundleName}.js");
var fileVersionProvider = httpContext.RequestServices.GetService<IFileVersionProvider>();
var versionUrl = fileVersionProvider?.AddFileVersionToPath(httpContext.Request.PathBase, url);
var js = versionUrl != null ? new JavaScriptResource(new Uri(versionUrl, UriKind.Relative)) { } : new JavaScriptResource(new Uri(url, UriKind.Relative)) { Version = s_version, };
httpContext.ConfinityPage().Resources.AddJavaScript(js);
// new
var js = new JavaScriptResource() { FilePath = $"/assets/bundled/{bundleName}.js" }
httpContext.ConfinityPage().Resources.AddJavaScript(js);
Examples not preferred:
// old
var jsUrl = _staticResourceHelper.CreateUrlForStaticResource(CookieConsentModule.ModuleKey, "/my.js");
return new JavaScriptResource(jsUrl) { Version = "3" };
// new v1
var jsUrl = _staticResourceHelper.CreateUrlForStaticResource(CookieConsentModule.ModuleKey, "/my.js");
return new JavaScriptResource(jsUrl.AddParameter("v","3"));
// new v2
var jsUrl = _staticResourceHelper.CreateUrlForStaticResource(CookieConsentModule.ModuleKey, "/my.js", new QueryString("?v=3"));
return new JavaScriptResource(jsUrl);
2024.1.1.0 Assets Seeding
The folder "Assets" with the id 85E56776-DAEA-461C-A308-CB177E4526F0 is no longer seeded on start up if there are other folders.
2024.1.0.0 [migration]
Web
The content security policy (csp) directive frame-ancestors is now set to 'self' by default.
Custom SSO
IProvideConfinityUserhas been refactored and renamed toIConfinityUserProvider. The class has now two methods:ProvideUsernameAsync()which should return the current username or null, if no user is logged in.- With
UpdateUserAsync()you can update/sync the user stored in confinity.
- Removed
IssuerJsonWebKey,AllowedIssuersandAllowedAudiencesfromAuthJwkoptions, since custom SSO implementation has to be made in host application.
User / Roles / Groups Entities
- Roles are no longer assigned via groups to a user. Instead, roles are now assigned directly to the users.
Entity App
- Implementations of
IActionEventListenershould handle the newActionEventType.Restore, invoked by the new 'Restore' action.
Persistence
- on
IPersistensConfigurationthePropertyBuilderExtensionsmethodHasJsonConversionhas been removed. - Confinity Contents no longer work on Entities not inheriting
ConfinityEntity
Migration
- This migration is implemented as an extension method, just create a new migration and replace the
Upmethod with the following code (Sql Server & Postgres only):
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.Confinity_2024_1_0_Up();
}
Configuration
- Paid editions of Confinity must now be explicitly activated via appsettings or environment variables. Enable paid editions by setting
Confinity__EditiontoEnterpriseorProfessional.
Wizard module
- Added separate handling of relocation results in the view. Reloaction result is now rendered with "a" - Tag instead of "button".
- Styling needs to be created for link results and adjusted for button results separately. Added class="option" to all results for better styling.
- If you have overriden the view, you have to change your view accordingly and you have to with a relocation result.
Feedback module
- New members on
FeedbackPayload : ConfinityContentneed to be implemented:
public abstract class FeedbackPayload : ConfinityContent
{
public abstract IReadOnlyCollection<ExportColumnDefinition> GetExportColumns();
public abstract IReadOnlyCollection<ExportValue> GetExportValues();
}
Unit-Testing
- The base class
WithEfCoreTestBasefor unit testing with aIConfinityDbContextwas removed. UseConfinityTestDatabaseinstead.
References
After installing 2024.1, you must run the scheduled task Confinity.ReferenceChecker.RefreshScheduledTask to repair broken references.
v1.6.X [migration]
Migration
- history of
AssetAttributewas removed.
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.Confinity_1_6_Up();
}
Asset
- History image assets throw an error when they are requested in a specific exact resized dimension. Before this gave a silent wrong result.
- The filename of images for a history version gets ignored. An authenticated user can request a history file with any name.
- Static asset delivery by a different webserver got removed (this feature may come back if the interest is there).
v1.5.X [migration]
Assets module
- Moved
IAssetService.GetContenttoIAssetContentLoader.GetContent. Removed the paramContentKeyfrom and made the method async due to an additional loading which was previously hidden in the stream. There are several other overloads available. - We've changed the image manipulation library. The new one uses less memory, is faster, does correctly handle color profile conversion, makes smaller files (especially for jpg), supports more formats and more.
- If you had issues with color profiles you should delete your asset cache directory.
- If you used the
IImageAssetEditor.Resize(without focal point) orIImageAssetEditor.ResizeByMaxand the image must be cropped, then the new library keeps interesting things centered.
Forms module
IListOfOptionModelsdefines the methodGetOptionsfor the options instead of a readonly property.
Confinity
- Update to .net 8 including all referenced Nuget libraries
ILinkHelper.CreateUrlFromLinkModelwas removed (useCreateUrlFromLinkModelAsyncinstead).IUrlHelper.ConfinityLinkwas removed (useConfinityLinkAsyncinstead).IDatabaseProviderConfiguration.ConfigureModelwas removed (use OnModelCreating).AssetStreamis no longer public.IEntityAppBuilder.Entitywas removed (useAddEntityinstead).IFormContainerBuilder.AddConditionalGroupoverload withlabelproperty was removed.ISettingsStorewas removed (useISettings<TSettingsEntry>instead).HeadViewContext.AddMetaTitlewas removed (useHeadViewContext.SetMetaTitleinstead).HeadViewContext.AddMetaDescriptionwas removed (useHeadViewContext.SetMetaDescriptioninstead).HeadViewContext.AddMetaImagewas removed (useHeadViewContext.SetMetaImageinstead).HeadViewContext.SetMetaImage(string)was removed (useHeadViewContext.SetMetaImage(IImageAsset)instead).ILinkModelUrlResolver.ResolveUrlwas removed (useILinkModelUrlResolver.ResolveUrlAsyncinstead).HtmlHelperExtensions.FromWysiwygwas removed (useHtmlHelperExtensions.FromWysiwyglAsyncinstead).- until this release general
Exceptionwhere thrown. With this release the exceptions got more explicit. - Validation
Confinity.EntityApp.IValidationFailurerequired a fieldErrorCodeandPropertyNameboth were unused / used wrong and were therefore removed.Confinity.Validation.ConfinityValidationResultmoved to an internal namespace. When needed useValidationResult(which is now in the namespace Confinity.Validation)Confinity.Validation.ConfinityValidationFailuremoved to an internal namespace. When needed useValidationFailure(which is now in the namespace Confinity.Validation)
IRelocationStoreusesRelocationDtoinstead ofRelocation.- Changed
IEnumerable<>toICollection<>for the following properties:User.AttributesUserModel.AttributesIUserModel.AttributesUserNewEditViewModel.AttributesPage.DomainsTag.SynonymsBlogArticle.CategoriesBlogArticle.ContentForm.FieldsForm.ConfirmationComponentsForm.ListenerModelsMediaPlayerCategory.RssFeedConfigurationMediaPlayerElement.CategoriesProductAttributes.AttributesProductType.AttributesWizardConfiguration.QuestionsWizardConfiguration.Results
Migration
- This migration is implemented as an extension method, just create a new migration an replace the
Upmethod with the following code (Sql Server & Postgres only):
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.Confinity_1_5_Up();
}
v1.4.X [migration]
Forms module
- The abstract base class
FormItemModelhas been replaced with the class attribute[ConfinityFormItemAttribute]. Existing types should inherit fromViewComponentModelinstead ofFormItemModel.
Confinity
- Instance key has to be unique across all stages.
AddConfinitywithoutWebApplicationBuilderas argument was removed. This method was marked as obsolete since .net 5.- The connection to all instances waits only 10 seconds for a response for an authentication. Before this, it was 110 seconds.
- The replication comes to a halt if any instance goes offline. Before this, this check was only made after startup.
- Renamed
ISelectManyAssetsOnConfinityContentFieldFormElementBuildertoISelectAssetsOnConfinityContentFieldFormElementBuilder.
Blog module
- In
ConfinityBlogOverview/Default.cshtmlrazor view:- Added "most used tags" filter
- In
ConfinityBlogArticle/Default.cshtmlrazor view:- Added "last update" information
- Replaced class
badge-primarywithbg-primaryfor all tags
- Check if both views are still correct in your host application!
- DB-Migration: Added new column
LastUpdatein tableBlogArticle
v1.3.X [migration]
Migration
- This migration is implemented as an extension method, just create a new migration an replace the
Upmethod with the following code (Sql Server & Postgres only):
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.Confinity_1_3_Up();
}
v1.2.1
Core Module
- Moved
ConfinityNoHistoryAttribute&ConfinityAllowInheritanceAttributeto namespaceConfinity.Attributes
v1.2.X [migration]
Migration
- Added new column
PublishedStagein tableConfinityEntityReference
v1.1.x
Core Module
IAssethas a new method calledGetHash(). Custom Implementations of IAsset need to implement this.
v1.0.X [migration]
Razor helper
- Introduced the new
IRazorToStringRenderer(namespaceConfinity.WebUI.Util) that replaces IRazorToStringRenderer (namepsaceConfinity.Mail) andIRazorToStringRenderer(namespaceConfinity.Pages).
Forms Module
- The property
ConfirmationTextwas changed toConfirmationComponents - The property
ConfirmationComponentsis now aIEnumerable<ViewComponentModel> - You can now add
ViewComponentModelwhich will be rendered after the form was submitted successfully which also means the previous Success-Alert after submitting is removed - Models marked with the attribute
ConfinityFormsConfirmationwill be able to used as elements for theConfirmationComponentsproperty - Confirmations are no longer wrapped in this container:
<div data-cfy-form-confirmation>
Migration
- This migration is implemented as an extension method, just create a new migration an replace the
Upmethod with the following code (Sql Server & Postgres only):
//template = JSON string for a confinity content. This will overwrite all existing confirmationtext
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.Confinity_1_0_Up(template);
}
- The variable template must be a json with an array. In the array must be a component model, with the to parameters and
- will be replaced with the text of the previous confirmationtext
Example
"[{\"$type\":\"RocketLaunches.ConfinityPages.PageComponents.TextComponentModel\",\"title\":null,\"lead\":null,\"text\":\"{{text}}\",\"id\":\"{{id}}\"}]"
⚠️ Important: Replace "RocketLaunches.ConfinityPages.PageComponents.TextComponentModel" with an existing ConfinityContent of the Application
v0.68.x [migration]
Replication
- The way how the replication automatically deletes entities on all stages changed. Please check replication for further information.
Entity App
- The property
parentPropertySelectorinIFormContainerBuilder.AddChildList(...)is no longer nullable. - The
IComputedHandlerinIFormContainerBuilder.AddComputed(...)has a second type parameterTProperty. - The property
jsFilenameinIFormContainerBuilder.AddCustom(...)is no longer the path to a file but the filename. IFormContainerBuilder.AddCustom(...)expects one more propertybyte[] jsFileContent- The fields
PublishedAtandPublishedByIdwere removed fromConfinityMetadataand are now on the corresponding History-table- Entities marked with the
ConfinityNoHistoryAttributedo not have those fields anymore.
- Entities marked with the
Frontend Configuration
When the window wasn't scrollable components from modules (like the Forms) which had to scroll, did not scroll to the correct location. The default logic changed so that the first scrollable element (from the target HTMLElement) will scroll. This probably won't break anything, but if there are any issues, you can copy the following javascript to get back to the old behaviour.
window.__confinityScrollTo = (targetElement) => {
// custom scroll which only scroll on the window.
let positionFromTop = element.offsetTop;
const scrollOffset = window.__confinityScrollOffsetTop;
if (scrollOffset && !isNaN(scrollOffset)) {
positionFromTop -= scrollOffset;
}
window.scrollTo({top: positionFromTop, behavior: 'smooth'});
}
OpenTelemetry Update
Due to a dependency update the api to add OpenTelemetry changed:
services.AddOpenTelemetry()
// services.AddOpenTelemetryMetrics(b => b old
.WithMetrics(b => // new
{
b.AddConfinityInstrumentation()
.AddAspNetCoreInstrumentation()
.AddHttpClientInstrumentation();
})
// services.AddOpenTelemetryTracing(b => b old
.WithTracing(b => b // new
.AddConfinityInstrumentation()
.AddHttpClientInstrumentation()
.AddAspNetCoreInstrumentation()
.AddSqlClientInstrumentation()
);
Asset App
- Several permissions are now obsolete or renamed
AssetStreamis obsolete. UseIAssetService.GetContent()instead.
Analytics
Since Google Analytics Universal Tracking is now stop processing data (https://support.google.com/analytics/answer/11583528?sjid=13203110707687459332-EU) we have removed it. Make sure to update your Analytics configuration by configuring Google Analytics 4, Google Tag manager, or Microsoft Clarity (with forward to Google Analytics 4).
Migration
- No more history table for AssetContents
- Cleanup asset permissions
- Sets
Toin History toinfinitywhere date is greater then 9999-01-01 (Postgres only) - Moves
PublishedAtandPublishedByIdfromConfinityMetadatato the corresponding History-table - This migration is implemented as an extension method, just create a new migration an replace the
Upmethod with the following code (Sql Server & Postgres only):
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.Confinity_0_68_Up();
}
v0.67.16
Frontend Configuration (backport)
When the window wasn't scrollable components from modules (like the Forms) which had to scroll, did not scroll to the correct location. The default logic changed so that the first scrollable element (from the target HTMLElement) will scroll. This probably won't break anything, but if there are any issues, you can copy the following javascript to get back to the old behaviour.
window.__confinityScrollTo = (targetElement) => {
// custom scroll which only scroll on the window.
let positionFromTop = element.offsetTop;
const scrollOffset = window.__confinityScrollOffsetTop;
if (scrollOffset && !isNaN(scrollOffset)) {
positionFromTop -= scrollOffset;
}
window.scrollTo({top: positionFromTop, behavior: 'smooth'});
}
v0.67.x [migration] (Confinity MediaPlayer module only)
Confinity Media Player
Database Migration
Warning
This migration will DELETE ALL data from the media player module!
If you have to migrate existing data, create the migration and replace the Up method with the following code. This migration might only work with SQL Server. If you use another DB provider, you have to adapt the migration script accordingly.
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_ConfinityMediaPlayer_ConfinityAsset_PreviewImageId",
table: "ConfinityMediaPlayer");
migrationBuilder.DropForeignKey(
name: "FK_ConfinityMediaPlayer_ConfinityAsset_VideoFileId",
table: "ConfinityMediaPlayer");
migrationBuilder.DropIndex(
name: "IX_ConfinityMediaPlayer_PreviewImageId",
table: "ConfinityMediaPlayer");
migrationBuilder.DropIndex(
name: "IX_ConfinityMediaPlayer_VideoFileId",
table: "ConfinityMediaPlayer");
migrationBuilder.AddColumn<string>(
name: "Content",
table: "ConfinityMediaPlayer",
type: "nvarchar(max)",
nullable: true);
migrationBuilder.AddColumn<Guid>(
name: "AuthorId",
table: "ConfinityMediaPlayer",
type: "uniqueidentifier",
nullable: true);
migrationBuilder.AddColumn<DateTimeOffset>(
name: "Date",
table: "ConfinityMediaPlayer",
type: "datetimeoffset",
nullable: true);
migrationBuilder.AddColumn<string>(
name: "Entity_Content",
table: "__ConfinityHistory_ConfinityMediaPlayer",
type: "nvarchar(max)",
nullable: true);
migrationBuilder.AddColumn<Guid>(
name: "Entity_AuthorId",
table: "__ConfinityHistory_ConfinityMediaPlayer",
type: "uniqueidentifier",
nullable: true);
migrationBuilder.AddColumn<DateTimeOffset>(
name: "Entity_Date",
table: "__ConfinityHistory_ConfinityMediaPlayer",
type: "datetimeoffset",
nullable: true);
// DATA MIGRATION
migrationBuilder.Sql("""
DECLARE @id UNIQUEIDENTIFIER;
DECLARE @videoTemplate NVARCHAR(MAX);
DECLARE @audioTemplate NVARCHAR(MAX);
SET @videoTemplate = '{
"$type": "Confinity.MediaPlayer.MediaElementVideoContent",
"videoAssets": [
{
"$type": "Confinity.MediaPlayer.VideoAsset",
"asset": {
"id": "%s"
},
"resolution": null,
"id": "%s"
}
],
"aspectRatio": "%s",
"previewImage": {
"id": "%s"
},
"chapter": null,
"id": "%s"
}'
SET @audioTemplate = '{
"$type": "Confinity.MediaPlayer.MediaElementAudioContent",
"asset": {
"id": "%s"
},
"previewImage": {
"id": "%s"
},
"chapter": null,
"aspectRatio": "%s",
"id": "%s"
}'
WHILE EXISTS(SELECT NULL
FROM ConfinityMediaPlayer p
INNER JOIN ConfinityAsset a ON a.Id = p.VideoFileId
WHERE p.Content IS NULL AND a.Filename LIKE '%.mp4')
BEGIN
DECLARE @videoAssetId UNIQUEIDENTIFIER;
DECLARE @videoContentId UNIQUEIDENTIFIER;
SET @id = (SELECT TOP 1 p.Id FROM ConfinityMediaPlayer p
INNER JOIN ConfinityAsset a ON a.Id = p.VideoFileId
WHERE p.Content IS NULL AND a.Filename LIKE '%.mp4');
SET @videoAssetId = NEWID();
SET @videoContentId = NEWID();
UPDATE ConfinityMediaPlayer
SET Content = REPLACE(REPLACE(REPLACE(FORMATMESSAGE(@videoTemplate
, LOWER(CONVERT(nvarchar(36), ConfinityMediaPlayer.VideoFileId))
, LOWER(CONVERT(nvarchar(36), @videoAssetId))
, ConfinityMediaPlayer.AspectRatio
, LOWER(CONVERT(nvarchar(36), ConfinityMediaPlayer.PreviewImageId))
, LOWER(CONVERT(nvarchar(36), @videoContentId)))
, CHAR(13), ''), CHAR(10), ''), CHAR(32), ''), --remove line breaks and spaces
Date = ConfinityMetadata_ModifiedAt
WHERE ConfinityMediaPlayer.Id = @id
UPDATE __ConfinityHistory_ConfinityMediaPlayer
SET Entity_Content = REPLACE(REPLACE(REPLACE(FORMATMESSAGE(@videoTemplate
, LOWER(CONVERT(nvarchar(36), __ConfinityHistory_ConfinityMediaPlayer.Entity_VideoFileId))
, LOWER(CONVERT(nvarchar(36), @videoAssetId))
, __ConfinityHistory_ConfinityMediaPlayer.Entity_AspectRatio
, LOWER(CONVERT(nvarchar(36), __ConfinityHistory_ConfinityMediaPlayer.Entity_PreviewImageId))
, LOWER(CONVERT(nvarchar(36), @videoContentId)))
, CHAR(13), ''), CHAR(10), ''), CHAR(32), ''), --remove line breaks and spaces
Entity_Date = Entity_ConfinityMetadata_ModifiedAt
WHERE __ConfinityHistory_ConfinityMediaPlayer.Entity_Id = @id
END
WHILE EXISTS(SELECT NULL
FROM ConfinityMediaPlayer p
INNER JOIN ConfinityAsset a ON a.Id = p.VideoFileId
WHERE p.Content IS NULL AND a.Filename LIKE '%.mp3')
BEGIN
DECLARE @audioContentId UNIQUEIDENTIFIER;
SET @id = (SELECT TOP 1 p.Id FROM ConfinityMediaPlayer p
INNER JOIN ConfinityAsset a ON a.Id = p.VideoFileId
WHERE p.Content IS NULL AND a.Filename LIKE '%.mp3');
SET @audioContentId = NEWID();
UPDATE ConfinityMediaPlayer
SET Content = REPLACE(REPLACE(REPLACE(FORMATMESSAGE(@audioTemplate
, LOWER(CONVERT(nvarchar(36), ConfinityMediaPlayer.VideoFileId))
, LOWER(CONVERT(nvarchar(36), ConfinityMediaPlayer.PreviewImageId))
, ConfinityMediaPlayer.AspectRatio
, LOWER(CONVERT(nvarchar(36), @audioContentId)))
, CHAR(13), ''), CHAR(10), ''), CHAR(32), ''), --remove line breaks and spaces
Date = ConfinityMetadata_ModifiedAt
WHERE ConfinityMediaPlayer.Id = @id
UPDATE __ConfinityHistory_ConfinityMediaPlayer
SET Entity_Content = REPLACE(REPLACE(REPLACE(FORMATMESSAGE(@audioTemplate
, LOWER(CONVERT(nvarchar(36), __ConfinityHistory_ConfinityMediaPlayer.Entity_VideoFileId))
, LOWER(CONVERT(nvarchar(36), __ConfinityHistory_ConfinityMediaPlayer.Entity_PreviewImageId))
, __ConfinityHistory_ConfinityMediaPlayer.Entity_AspectRatio
, LOWER(CONVERT(nvarchar(36), @audioContentId)))
, CHAR(13), ''), CHAR(10), ''), CHAR(32), ''), --remove line breaks and spaces
Entity_Date = Entity_ConfinityMetadata_ModifiedAt
WHERE __ConfinityHistory_ConfinityMediaPlayer.Entity_Id = @id
END
GO
""");
migrationBuilder.Sql("""
UPDATE [ConfinityEntityReference]
SET [DependentEntityName] = 'Confinity.MediaPlayer.MediaPlayerElement'
WHERE [DependentEntityName] = 'Confinity.MediaPlayer.MediaPlayer'
""");
migrationBuilder.DropColumn(
name: "PreviewImageId",
table: "ConfinityMediaPlayer");
migrationBuilder.DropColumn(
name: "VideoFileId",
table: "ConfinityMediaPlayer");
migrationBuilder.DropColumn(
name: "Entity_PreviewImageId",
table: "__ConfinityHistory_ConfinityMediaPlayer");
migrationBuilder.DropColumn(
name: "Entity_VideoFileId",
table: "__ConfinityHistory_ConfinityMediaPlayer");
migrationBuilder.DropColumn(
name: "AspectRatio",
table: "ConfinityMediaPlayer");
migrationBuilder.DropColumn(
name: "Entity_AspectRatio",
table: "__ConfinityHistory_ConfinityMediaPlayer");
migrationBuilder.DropPrimaryKey(
name: "PK_ConfinityMediaPlayer",
table: "ConfinityMediaPlayer");
migrationBuilder.DropPrimaryKey(
name: "PK___ConfinityHistory_ConfinityMediaPlayer",
table: "__ConfinityHistory_ConfinityMediaPlayer");
migrationBuilder.DropIndex(
name: "IX___ConfinityHistory_ConfinityMediaPlayer_Entity_Id",
table: "__ConfinityHistory_ConfinityMediaPlayer");
migrationBuilder.AlterColumn<DateTimeOffset>(
name: "Date",
table: "ConfinityMediaPlayer",
oldType: "datetimeoffset",
type: "datetimeoffset",
oldNullable: true,
nullable: false);
//RENAME TABLE HERE
migrationBuilder.RenameTable(name: "ConfinityMediaPlayer",
newName: "ConfinityMediaPlayerElement");
migrationBuilder.RenameTable(name: "__ConfinityHistory_ConfinityMediaPlayer",
newName: "__ConfinityHistory_ConfinityMediaPlayerElement");
migrationBuilder.AddPrimaryKey(
name: "PK_ConfinityMediaPlayerElement",
table: "ConfinityMediaPlayerElement",
column: "Id");
migrationBuilder.AddPrimaryKey(
name: "PK___ConfinityHistory_ConfinityMediaPlayerElement",
table: "__ConfinityHistory_ConfinityMediaPlayerElement",
column: "Id");
migrationBuilder.CreateTable(
name: "__ConfinityHistory_ConfinityMediaPlayerAuthor",
columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
Entity_ConfinityMetadata_ModifiedAt = table.Column<DateTimeOffset>(type: "datetimeoffset", nullable: false),
Entity_ConfinityMetadata_ModifiedById = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
Entity_ConfinityMetadata_PublishedAt = table.Column<DateTimeOffset>(type: "datetimeoffset", nullable: true),
Entity_ConfinityMetadata_PublishedById = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
Entity_ConfinityMetadata_PublishedStage = table.Column<string>(type: "nvarchar(max)", nullable: true),
Entity_Description = table.Column<string>(type: "nvarchar(max)", nullable: true),
Entity_FirstName = table.Column<string>(type: "nvarchar(max)", nullable: true),
Entity_Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
Entity_ImageId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
Entity_LastName = table.Column<string>(type: "nvarchar(max)", nullable: true),
From = table.Column<DateTimeOffset>(type: "datetimeoffset", nullable: false),
To = table.Column<DateTimeOffset>(type: "datetimeoffset", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK___ConfinityHistory_ConfinityMediaPlayerAuthor", x => x.Id);
});
migrationBuilder.CreateTable(
name: "__ConfinityHistory_ConfinityMediaPlayerCategory",
columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
Entity_ConfinityMetadata_ModifiedAt = table.Column<DateTimeOffset>(type: "datetimeoffset", nullable: false),
Entity_ConfinityMetadata_ModifiedById = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
Entity_ConfinityMetadata_PublishedAt = table.Column<DateTimeOffset>(type: "datetimeoffset", nullable: true),
Entity_ConfinityMetadata_PublishedById = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
Entity_ConfinityMetadata_PublishedStage = table.Column<string>(type: "nvarchar(max)", nullable: true),
Entity_Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
Entity_Name = table.Column<string>(type: "nvarchar(max)", nullable: true),
Entity_RssFeedConfiguration = table.Column<string>(type: "nvarchar(max)", nullable: true),
Entity_Slug = table.Column<string>(type: "nvarchar(max)", nullable: true),
From = table.Column<DateTimeOffset>(type: "datetimeoffset", nullable: false),
To = table.Column<DateTimeOffset>(type: "datetimeoffset", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK___ConfinityHistory_ConfinityMediaPlayerCategory", x => x.Id);
});
migrationBuilder.CreateTable(
name: "__ConfinityHistory_ConfinityMediaPlayerElementCategory",
columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
Entity_ConfinityMetadata_ModifiedAt = table.Column<DateTimeOffset>(type: "datetimeoffset", nullable: false),
Entity_ConfinityMetadata_ModifiedById = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
Entity_ConfinityMetadata_PublishedAt = table.Column<DateTimeOffset>(type: "datetimeoffset", nullable: true),
Entity_ConfinityMetadata_PublishedById = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
Entity_ConfinityMetadata_PublishedStage = table.Column<string>(type: "nvarchar(max)", nullable: true),
Entity_Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
Entity_MediaPlayerCategoryId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
Entity_MediaPlayerElementId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
Entity_SortIndex = table.Column<int>(type: "int", nullable: false),
From = table.Column<DateTimeOffset>(type: "datetimeoffset", nullable: false),
To = table.Column<DateTimeOffset>(type: "datetimeoffset", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK___ConfinityHistory_ConfinityMediaPlayerElementCategory", x => x.Id);
});
migrationBuilder.CreateTable(
name: "ConfinityMediaPlayerAuthor",
columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
FirstName = table.Column<string>(type: "nvarchar(max)", nullable: false),
LastName = table.Column<string>(type: "nvarchar(max)", nullable: false),
ImageId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
Description = table.Column<string>(type: "nvarchar(max)", nullable: true),
ConfinityMetadata_ModifiedById = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
ConfinityMetadata_ModifiedAt = table.Column<DateTimeOffset>(type: "datetimeoffset", nullable: false),
ConfinityMetadata_PublishedStage = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: true),
ConfinityMetadata_PublishedAt = table.Column<DateTimeOffset>(type: "datetimeoffset", nullable: true),
ConfinityMetadata_PublishedById = table.Column<Guid>(type: "uniqueidentifier", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_ConfinityMediaPlayerAuthor", x => x.Id);
table.ForeignKey(
name: "FK_ConfinityMediaPlayerAuthor_ConfinityAsset_ImageId",
column: x => x.ImageId,
principalTable: "ConfinityAsset",
principalColumn: "Id");
});
migrationBuilder.CreateTable(
name: "ConfinityMediaPlayerCategory",
columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
Name = table.Column<string>(type: "nvarchar(max)", nullable: false),
Slug = table.Column<string>(type: "nvarchar(250)", maxLength: 250, nullable: false),
RssFeedConfiguration = table.Column<string>(type: "nvarchar(max)", nullable: true),
ConfinityMetadata_ModifiedById = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
ConfinityMetadata_ModifiedAt = table.Column<DateTimeOffset>(type: "datetimeoffset", nullable: false),
ConfinityMetadata_PublishedStage = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: true),
ConfinityMetadata_PublishedAt = table.Column<DateTimeOffset>(type: "datetimeoffset", nullable: true),
ConfinityMetadata_PublishedById = table.Column<Guid>(type: "uniqueidentifier", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_ConfinityMediaPlayerCategory", x => x.Id);
});
migrationBuilder.CreateTable(
name: "ConfinityMediaPlayerElementCategory",
columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
MediaPlayerElementId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
MediaPlayerCategoryId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
ConfinityMetadata_ModifiedById = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
ConfinityMetadata_ModifiedAt = table.Column<DateTimeOffset>(type: "datetimeoffset", nullable: false),
ConfinityMetadata_PublishedStage = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: true),
ConfinityMetadata_PublishedAt = table.Column<DateTimeOffset>(type: "datetimeoffset", nullable: true),
ConfinityMetadata_PublishedById = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
SortIndex = table.Column<int>(type: "int", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ConfinityMediaPlayerElementCategory", x => x.Id);
table.ForeignKey(
name: "FK_ConfinityMediaPlayerElementCategory_ConfinityMediaPlayerCategory_MediaPlayerCategoryId",
column: x => x.MediaPlayerCategoryId,
principalTable: "ConfinityMediaPlayerCategory",
principalColumn: "Id");
table.ForeignKey(
name: "FK_ConfinityMediaPlayerElementCategory_ConfinityMediaPlayerElement_MediaPlayerElementId",
column: x => x.MediaPlayerElementId,
principalTable: "ConfinityMediaPlayerElement",
principalColumn: "Id");
});
migrationBuilder.CreateIndex(
name: "IX___ConfinityHistory_ConfinityMediaPlayerAuthor_Entity_Id",
table: "__ConfinityHistory_ConfinityMediaPlayerAuthor",
column: "Entity_Id");
migrationBuilder.CreateIndex(
name: "IX___ConfinityHistory_ConfinityMediaPlayerCategory_Entity_Id",
table: "__ConfinityHistory_ConfinityMediaPlayerCategory",
column: "Entity_Id");
migrationBuilder.CreateIndex(
name: "IX___ConfinityHistory_ConfinityMediaPlayerElement_Entity_Id",
table: "__ConfinityHistory_ConfinityMediaPlayerElement",
column: "Entity_Id");
migrationBuilder.CreateIndex(
name: "IX___ConfinityHistory_ConfinityMediaPlayerElementCategory_Entity_Id",
table: "__ConfinityHistory_ConfinityMediaPlayerElementCategory",
column: "Entity_Id");
migrationBuilder.CreateIndex(
name: "IX_ConfinityMediaPlayerAuthor_ImageId",
table: "ConfinityMediaPlayerAuthor",
column: "ImageId");
migrationBuilder.CreateIndex(
name: "IX_ConfinityMediaPlayerCategory_Slug",
table: "ConfinityMediaPlayerCategory",
column: "Slug",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_ConfinityMediaPlayerElement_AuthorId",
table: "ConfinityMediaPlayerElement",
column: "AuthorId");
migrationBuilder.CreateIndex(
name: "IX_ConfinityMediaPlayerElementCategory_MediaPlayerCategoryId",
table: "ConfinityMediaPlayerElementCategory",
column: "MediaPlayerCategoryId");
migrationBuilder.CreateIndex(
name: "IX_ConfinityMediaPlayerElementCategory_MediaPlayerElementId",
table: "ConfinityMediaPlayerElementCategory",
column: "MediaPlayerElementId");
}
Page Component / Frontend
The page component for the media player has been reworked. Please verify if the component still works and check all your CSS & JavaScripts.
v0.66.x only Confinity Forms Module
Csp Headers
AllowPrefetchSource is removed from HttpContext.ConfinityPage().Csp(). This is no longer supported by the browser.
Confinity Forms
A new component for the buttons was created so that they can be overwritten. If you have overwritten the ConfinityForm/Default.cshtml you have to call the corresponding button:
// replace the <button type="submit" with:
@await Component.InvokeAsync(typeof(ConfinityFormButtonViewComponent),
new FormButtonModel { Usage = FormButtonUsage.Submit, Label = Model.SubmitButtonLabel })
If you have overwritten the ConfinityFormWizard/Default.cshtml you have to call the corresponding buttons:
// replace the back button (<button type="button" ...) with:
@await Component.InvokeAsync(typeof(ConfinityFormButtonViewComponent),
new FormButtonModel { Label = "", Usage = FormButtonUsage.WizardBack })
// replace the forward button (<button type="submit" ...) with:
@await Component.InvokeAsync(typeof(ConfinityFormButtonViewComponent),
new FormButtonModel { Label = Model.WizardInfo?.NextButtonLabel ?? "", Usage = FormButtonUsage.WizardForward })
v0.65.x [migration]
Confinity Media Player
Nullability fixed for the MediaPLayer entity.
VideoFileId from Guid? to Guid
PreviewImageId from Guid? to Guid
HasJsonConversion deprecated
PropertyBuilderExtensions.HasJsonConversion is deprecated. ConfinityContents don't need this anyway.
v0.64.x [migration]
ConfinityMetaData
ConfinityMetaData.PublishedStage gets a max. length of 50 to reduce space- and memory consumption. This will effect every ConfinityEntity.
v0.63.x [migration]
Settings can no longer be used with the options pattern!
See Settings for further information.
User Tasks
New input 'planned for' added to UserTask, needs a migration.
v0.62.x
Entity App: No more custom keys for RegisterSettings
The support for named settings was dropped, therefor the Key method on the ISettingsBuilder as well.
WARNING
If you were using the Key method to configure a specific key, your existing Settings will no longer be found!
ISettingsStore deprecated
The ISettingsStore is deprecated, to access your settings, use ISettings<T>. See Settings for further information.
v0.61.x
ConfinitySelectable
Classes inheriting from ConfinitySelectable must have a constructor with the following parameters (type & name). Additional constructor are allowed.
private MySelectable(string id) {}
//or
private MySelectable(string id, string label) {}
//or
private MySelectable(string id, string label, int sortIndex) {}
v0.60.x [mgiration]
Services now uses it's own DbContext. The returned values are therefore no longer tracked. This was an architectural issue and had to be fixed. To help you, the method is renamed and has a different return type.
The migration is for additional navigation properties.
IAssetService
// old
var file = await _assetService.SaveFile(/*...*/);
myEmployee.Image = file; // wrong!
// new
var file = await _assetService.SaveFileAsync(/*...*/);
myEmployee.ImageId = file.Id // correct
For the asset methods, the return type changed from Asset to IAsset. For the folder methods, the return type changed form AssetFolder to IFolder
ITagService
The return / parameter changed to ITag instead of Tag.
OnStartup removed
The base class OnStartup was removed. Use your implementation of IModuleConfiguration.UseModule for startup logic instead.
v0.59.x [migration]
Confinity Pages
PageDomaingot new a new propertyStageKeyto link it to a stage.ConfinityPageOptions.StrictDomainBindingForWebsitesgot removed. Strict domain binding is activated on every public (= non author) instance.
Entity app preview action
- The
IPreviewResolver.ResolveUrlgot a second, optional parameterbaseUrl. This is will be the host (e.g. 'conx.ch') in multi stage environments and should be incorporated in the constructed URL.
v0.58.x
Saferpay Notify API changes
ask Roman
v0.57.x
Saferpay
Implementations of ISaferpayPaymentListener must capture or cancel when the transaction state is authorized.
v0.56.x [migration]
Removal of Confinity.Messages
All NuGet packages named 'Confinity.Messages' were removed.
ConfinityDbContext constructor
The constructor for ConfinityDbContext no longer expects a IMessageBroker<DeltaTopic>.
ISelectOptionsProvider<T> is now async
Implementations of this interface must return a Task:
// from
IList<ISelectOption<T>> GetOptions(string? searchQuery = null, IEnumerable<string>? ids = null);
// to
Task<IList<ISelectOption<T>>> GetOptions(string? searchQuery = null, IEnumerable<string>? ids = null);
v0.55.x [migration]
Update to .Net 7
- All Confinity projects now target .NET 7 and require you to have it installed.
- All Nuget references where updated to versions compatible with .NET 7. Check breaking changes if necessary:
- [.NET 7]https://learn.microsoft.com/en-us/dotnet/core/compatibility/7.0
- [EF7]https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-7.0/breaking-changes
SQL Server connection string
Since the update to EF 7, the SQL Server connection string is supposed to use encryption for the credentials. If you are not using encryption, set Encrypt=false in your connection string.
ConfinityConfiguration
Removed property LogSql. EF.Core always logs to the logger factory defined in DI. Set
{
"Logging": {
"LogLevel": {
"Microsoft.EntityFrameworkCore.Database.Command": "Warning"
}
}
}
in you appsettings to reduce noise from EF.Core logging.
ConfinityOptions
- Renamed
DefaultLanguage->DefaultUiLanguage - Renamed
SupportedLanguages->SupportedUiLanguages
NotEmpty validation
The NotEmpty validation is not accepting default values for value types anymore, e.g. the first value of an enum is not valid.
v0.54.x [migration]
The index on SettingsContainer.Key is now unique.
Warning
Check your settings app and remove the settings entry before the unique key is created.
More specific database configurations causes minimal definition changes.
EfPoly.Random
For random within sql queries you should now use Ef.Functions.Random.
v0.53.x [migration]
The replication now handles longer class names.
DAM / Asset endpoints changed
See Assets for details.
v0.52.x
Search Facets
Search facets (e.g. Type) need to be configured explicitly now. To get the same behaviour as before, use the following snippet:
public void AddModule(IModuleContext module)
{
module.Configure.RegisterSearchFacet(SearchFacetDefault.Type);
module.Configure.RegisterSearchFacet(SearchFacetDefault.DocumentFileExtension);
}
Or specify your own facets configurations:
public void AddModule(IModuleContext module)
{
module.Configure.RegisterSearchFacet("coolness-level", "Coolness level", 5);
}
v0.51.x
Entity App Wysiwyg editor configuration
IWysiwygEditorConfiguration and DefaultWysiwygEditorConfiguration got refactored:
- property names use pascal casing
- properties in
DefaultWysiwygEditorConfigurationare virtual and have setters
v0.50.x [migration]
Mailing
- DB Migration: Removed length limit for
MailingSchedule.Message.
v0.49.x
Assets
The file system cache for image assets changed. It is advised to delete the current cache directory (configured in AssetOptions.CacheDirectory) to save disk space.
v0.48.x
Forms
Changed the property Fields of the interface IFormItemCollectionModel to a method.
v0.47.3 CSP Changes
Analytics
The google tag manager now allows to set the 'strict-dynamic' script-src directive. When this is enabled, all scripts must have a nonce (confinity-nonce).
v0.47.x [migration]
Mailing
Removed length limit for MailingSchedule.OutgoingData & MailingSchedule.IncomingReply.
0.46.x
XCampaign Module refactoring
-IXCampaingService now only has the methods UpdateProfilesAsync & GetProfilesAsync and returns proper response objects. -Renamed Attribute to XCampaignAttribute
0.45.x
ConfinityPictureModel
The default value for the property FocalPoint in the ConfinityPictureModel has been changed to true.
ILinkHelper
CreateUrlFromLinkModelis now obsolete, use the async counterpartCreateUrlFromLinkModelAsyncCreateUrlFromLinkModelAsynchas a new parameterforceAbsolute.
ILinkModelUrlResolver
ResolveUrlis now obsolete, use the async counterpartResolveUrlAsyncResolveUrlAsynchas a new parameterforceAbsolute.
UrlHelperExtensions
ConfinityLinkis now obsolete, use the async counterpartConfinityLinkAsync
0.44.x
TempFile
- The interface
ITempFileControllerrequires a new parameter:[FromForm] string? contextPayload = null - For security reasons, the returned error messages from the JavaScript
TempFileClienthas been reduced to these for errors:- FileCapacityExceeded
- ExtensionNotAllowed
- ContentNotAllowed
- ProcessingError
- The strings for the error messages are now translated on the backend
Forms Module: FileUpload
The view for the ConfinityFormFileUpload view component has been slightly changed.
0.43.x
Forms Module
The interface IFormItemCollectionModel changed slightly:
public interface IFormItemCollectionModel
{
public IEnumerable<ConfinityContent>? Fields { get; }
}
0.42.1
Optional migration forms module
The forms from the form module have now the default spam protection "honeypot". To set this for all forms, use the following sql:
Microsoft SQL Server
migrationBuilder.Sql(@"update ConfinityForm set SpamProtection = 1 where 1=1;");
PostgreSQL
migrationBuilder.Sql(@"update ""ConfinityForm"" set ""SpamProtection"" = 1 where 1=1;");
0.42.x [Migration]
TempFile
- The registration for a TempFileSpace changed, see File Upload / Temp File for an example.
ISpaceDefinitionStoreandSpaceDefinitionwere removed. The configuration of your space is stored as an Named Option in DI. The name is the name of your space.- Instead of configuring your space with code, you can configure it in your appsettings, e.g.
{
"ConfinityTempFile": {
"NAME-OF-YOUR-SPACE": {
"SpaceCapacityInBytes": 209715200,
"SessionCapacityInBytes": 20971520,
"FileCapacityInBytes": 4194304,
"MaxAgeInSeconds": 20,
"PermittedFileSignatures": {
"pdf": [
[
37,
80,
68,
70,
45
]
]
}
}
}
}
Forms Module
If you had configured FileUploadOptions before in your appsettings, you need to transform them according to the example below. Notice that the property name for PermittedFileSignatures changed.
{
"ConfinityTempFile": {
"FileUploadOptions": {
"SpaceCapacityInBytes": 209715200,
"SessionCapacityInBytes": 20971520,
"FileCapacityInBytes": 4194304,
"MaxAgeInSeconds": 20,
"PermittedFileSignatures": {
"pdf": [
[
37,
80,
68,
70,
45
]
]
}
}
}
}
Asset
- History image assets throw an error when they are requested in a specific exact resized dimension. Before this gave a silent wrong result.
- The filename of images for a history version gets ignored. An authenticated user can request a history file with any name.
- Static asset delivery by a different webserver got removed (this feature may come back if the interest is there).
Metadata
- New tables
ConfinityAssignedUserandConfinityEntityNotewill be created.
Configuration
- Paid editions of Confinity must now be explicitly activated via appsettings or environment variables. Enable paid editions by setting
Confinity__EditiontoEnterpriseorProfessional.
Unit-Testing
- The base class
WithEfCoreTestBasefor unit testing with aIConfinityDbContextwas removed. UseConfinityTestDatabaseinstead.
0.41.x
If you reference static files in your module and publish a module separately, you have to use Microsoft.Extensions.FileProviders.Embedded.
For details see [Modules](./modules.md#Add static files)
0.40.x
Addition to 0.36.x:
Use the following migration to ensure there are no null values in the history table of Forms:
migrationBuilder.Sql(@"update ""__ConfinityHistory_ConfinityForm"" set ""Entity_ListenerModels"" = '[]' where ""Entity_ListenerModels"" is null;")
v0.39.x
Entity app
- Most values were removed from enum
InputType. The remaining are all valid and supported. - For configuring the DateOnly-, TimeOnly- and DateAndTime-inputs use the new enum
DateAndTimeType - For hidden fields use the
AddHiddenmethod
v0.38.x [migration]
Scheduled Task Index
Improved index for schedules requires a migration.
MailService throws exceptions
IMailService.SendMail now throws exceptions instead of just logging them.
v0.37.x [migration]
linq2db
Dependency to linq2db was removed. If used in the host application add linq2db there or replace with methods from Microsoft.EfCore.
DataProtection
Confinity stores now comes with a key storage provider that stores data protection keys in the database and ensures they are automatically replicated to all stages.
Notifications
Changed first parameter type from User to Guid for all overloads of SendAsync.
v0.36.x [migration]
From Module IFormSubmitEventListener<EmailFormSubmitEventListenerModel>
- The methode signature of the method
BeforeSubmitAsync(IFormSubmitEventListener) changed.
From:
Task<SubmitListenerResult> BeforeSubmitAsync(TModel model, IFormSubmissionRequest formSubmissionRequest);
To:
Task<SubmitListenerResult> BeforeSubmitAsync(FormSubmit<TConfig> submit);
The submit object holds the earlier properties model and formSubmissionRequest.
- The property
Enumerable<FormSubmitEventListenerModel> ListenerModelsonFormis not longer nullable.
Use the following migration to ensure there are no null values:
migrationBuilder.Sql(@"update ""ConfinityForm"" set ""ListenerModels"" = '[]' where ""ListenerModels"" is null;");
v0.35.x
IHtmlHelper
Replaced ConfinityHead with ConfinityHeadAsync. Call it in your razor view as follows:
@await Html.ConfinityHeadAsync()
v0.34.x
Settings [migration]
Increased length for keys.
v0.33.x
Pages [migration]
- Replaced
MetaNoIndexflag withSearchIndexingselectable on theIDynamicPageinterface - Replaced
MetaNoIndexflag withSearchIndexingselectable on thePageentity. For production databases on Postgres please use the following migration:
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "SearchIndexing",
table: "ConfinityPage",
type: "character varying(50)",
maxLength: 50,
nullable: false,
defaultValue: "index");
migrationBuilder.AddColumn<string>(
name: "Entity_SearchIndexing",
table: "__ConfinityHistory_ConfinityPage",
type: "text",
nullable: true);
migrationBuilder.Sql(@"
UPDATE ""ConfinityPage"" SET ""SearchIndexing"" = 'none'
WHERE ""MetaNoIndex"" = True;
");
migrationBuilder.Sql(@"
UPDATE ""ConfinityPage"" SET ""SearchIndexing"" = 'index'
WHERE ""MetaNoIndex"" = False;
");
migrationBuilder.Sql(@"
UPDATE ""__ConfinityHistory_ConfinityPage"" SET ""Entity_SearchIndexing"" = 'none'
WHERE ""Entity_MetaNoIndex"" = True;
");
migrationBuilder.Sql(@"
UPDATE ""__ConfinityHistory_ConfinityPage"" SET ""Entity_SearchIndexing"" = 'index'
WHERE ""Entity_MetaNoIndex"" = False;
");
migrationBuilder.DropColumn(
name: "MetaNoIndex",
table: "ConfinityPage");
migrationBuilder.DropColumn(
name: "Entity_MetaNoIndex",
table: "__ConfinityHistory_ConfinityPage");
}
v0.32.x
Replication [migration]
Dropped history table on entity ReplicationSchedule.
Entity App [migration]
Added index to column 'Entity_Id' for all history tables.
v0.31.x
Replication [migration]
New field FailureCount on entity ReplicationSchedule.
v0.30.x
UrlHelper
The extension method IUrlHelper.ConfinityAssetImageAsync returns now a Task<Uri?> in compliance with the other helper methods.
v0.29.x
Scheduled tasks
Scheduled task get a cancellation token passed to the Process Method.
public Task Process() -> public Task Process(CancellationToken cancellationToken)
v0.28.x
Replication
The password for the replication moved from Confinity.ReplicationHubSecret to ConfinityReplication.ReplicationHubSecret. The SignalR login for the replications will now use the ReplicationHubSecret as a symmetric key for the token issue / validation.
Error Pages
Confinity did accidentally handle all error pages (status code 399-599) and return the configured _error404 page. Starting with this release Confinity will only handle 404 errors.
v0.27.x
Pages [migration]
The pages app has a new option for case insensitive relocations.
Blog [migration]
The blog has new fields for meta description.
v0.26.x
Confinity.Blog [migration]
BlogArticle.AuthorId is not-nullable anymore. For production databases on Postgres please use the following migration:
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_ConfinityBlogArticle_ConfinityBlogAuthor_AuthorId",
table: "ConfinityBlogArticle");
migrationBuilder.AlterColumn<Guid>(
name: "AuthorId",
table: "ConfinityBlogArticle",
type: "uuid",
nullable: false,
defaultValue: new Guid("00000000-0000-0000-0000-000000000000"),
oldClrType: typeof(Guid),
oldType: "uuid",
oldNullable: true);
migrationBuilder.AlterColumn<Guid>(
name: "Entity_AuthorId",
table: "__ConfinityHistory_ConfinityBlogArticle",
type: "uuid",
nullable: false,
defaultValue: new Guid("00000000-0000-0000-0000-000000000000"),
oldClrType: typeof(Guid),
oldType: "uuid",
oldNullable: true);
migrationBuilder.AddForeignKey(
name: "FK_ConfinityBlogArticle_ConfinityBlogAuthor_AuthorId",
table: "ConfinityBlogArticle",
column: "AuthorId",
principalTable: "ConfinityBlogAuthor",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
// Add this at the end of the migration.
migrationBuilder.Sql(@"
UPDATE ""ConfinityEntityReference"" SET ""IsOptional"" = false
WHERE ""DependentEntityName"" = 'Confinity.Blog.BlogArticle'
AND ""PrincipalId"" IN (SELECT ""Id"" FROM ""ConfinityBlogAuthor"");
");
}
v0.25.x
Confinity.Blog [migration]
The blog name was longer than what the database allowed. The blog authors now have a max length for the name and description.
v0.24.x
Change to async api
The following methods are now asynchronous, therefor their return value changed and the the method name got the suffix Async.
- IAssetUrlHelper.CreateUrlForAssetAsync
- IImageUrlHelper.CreateUrlForAssetImageAsync
- IImageUrlHelper.CreateAbsoluteUrlForAssetImageAsync
- UrlHelperExtension.ConfinityAssetImageAsync
- UrlHelperExtension.ConfinityAssetAsync
v0.23.x
Refactoring of Confinity.Forms [migration]
Warning
There will be data loss when not paying attention to this change! The following instructions only apply for Postgres databases. For any other database, create a migration as usually and accept that the email configuration of forms will be lost.
Before upgrading to this version, you need to make sure there are no hanging migrations. To do so, create a migration and ensure that the Up-method is empty. You can delete the migration afterwards (don't forget to reset the Snapshot). Then follow these steps:
- Upgrade your dependencies and
- Rebuild the solution.
- Create a new migration
- Replace the generated
Up-method with the following method
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "ListenerModels",
table: "ConfinityForm",
type: "TEXT",
nullable: true);
migrationBuilder.AddColumn<string>(
name: "Entity_ListenerModels",
table: "__ConfinityHistory_ConfinityForm",
type: "TEXT",
nullable: true);
migrationBuilder.Sql(@"
UPDATE ""ConfinityForm""
SET ""ListenerModels"" =
format('[{' ||
'""$type"": ""Confinity.Forms.EmailFormSubmitEventListenerModel"",' ||
'""AllowDynamicRecipient"": %s,' ||
'""SendEmailTo"": %s,' ||
'""EmailSubject"": %s,' ||
'""EmailLead"": %s' ||
'}]',
COALESCE(to_json(""AllowDynamicRecipient""), 'null'),
COALESCE(to_json(""SendEmailTo""), 'null'),
COALESCE(to_json(""EmailSubject""), 'null'),
COALESCE(to_json(""EmailLead""), 'null'))
WHERE ""SendEmail"" = true;
");
migrationBuilder.Sql(@"
UPDATE ""__ConfinityHistory_ConfinityForm""
SET ""Entity_ListenerModels"" =
format('[{' ||
'""$type"": ""Confinity.Forms.EmailFormSubmitEventListenerModel"",' ||
'""AllowDynamicRecipient"": %s,' ||
'""SendEmailTo"": %s,' ||
'""EmailSubject"": %s,' ||
'""EmailLead"": %s' ||
'}]',
COALESCE(to_json(""Entity_AllowDynamicRecipient""),'null'),
COALESCE(to_json(""Entity_SendEmailTo""),'null'),
COALESCE(to_json(""Entity_EmailSubject""),'null'),
COALESCE(to_json(""Entity_EmailLead""),'null'))
WHERE ""Entity_SendEmail"" = true;
");
migrationBuilder.DropColumn(
name: "AllowDynamicRecipient",
table: "ConfinityForm");
migrationBuilder.DropColumn(
name: "EmailLead",
table: "ConfinityForm");
migrationBuilder.DropColumn(
name: "EmailSubject",
table: "ConfinityForm");
migrationBuilder.DropColumn(
name: "SendEmail",
table: "ConfinityForm");
migrationBuilder.DropColumn(
name: "SendEmailTo",
table: "ConfinityForm");
migrationBuilder.DropColumn(
name: "Entity_AllowDynamicRecipient",
table: "__ConfinityHistory_ConfinityForm");
migrationBuilder.DropColumn(
name: "Entity_EmailLead",
table: "__ConfinityHistory_ConfinityForm");
migrationBuilder.DropColumn(
name: "Entity_EmailSubject",
table: "__ConfinityHistory_ConfinityForm");
migrationBuilder.DropColumn(
name: "Entity_SendEmail",
table: "__ConfinityHistory_ConfinityForm");
migrationBuilder.DropColumn(
name: "Entity_SendEmailTo",
table: "__ConfinityHistory_ConfinityForm");
}
Moved UrlHelperExtensions to namespace Confinity.Forms
This is only relevant for the extension method ConfinityFormRecipient.
v0.22.x
Moved entity UserTask-entities to Confinity.UserTasks module [migration]
This might result in a migration that removes the tables Confinity_UserTask & Confinity_UserTaskLog if you are not using the Confinity.UserTasks module.
v0.21.x
Added notifications [migration]
For the new notifications feature, the entity Notification was added.
IStaticResourceHelper.CreateUrlForStaticResource signature change
To prevent coding errors the path parameter was changed to a PathString. The path now needs a leading slash and existing query parameters will get escaped and must be passed as a second parameter.
Migration:
// from:
staticResourceHelper.CreateUrlForStaticResource(AnalyticsModule.ModuleKey, "dist/analytics.js?v2");
// to:
staticResourceHelper.CreateUrlForStaticResource(AnalyticsModule.ModuleKey, "/dist/analytics.js", new QueryString("v2"));
v0.20.x
Move Icons to Confinity.Domain assembly
Because the namespace is still the same, this should not affect you.
v0.19.x
Extact TableLayoutBuilder's Add...Action methods to extension methods
AddDefaultAction, AddNewAction etc. are now extension methos for the ITableLayoutBuilder<T> and are available in the Confinity.EntityApp namespace.
Some overloads of the following methods expect a new generic parameter TEntity.
- AddDefaultActions
- AddNewAction
- AddEditAction
- AddArchiveAction
- AddExportAction
- AddPreviewAction
Therefor the invocation changes from
tableLayoutBuilder.AddDefaultActions<PageActionEventListener>(PageFormConfiguration);
to
tableLayoutBuilder.AddDefaultActions<Page, PageActionEventListener>(PageFormConfiguration);
v0.18.x
DateTimeOffset converter for SQLite [migration]
Instead of using the build in DateTimeOffsetToBinaryConverter for storing DateTimeOffset as long in the database, a custom converter is used that stores DateTimeOffset as DateTime. The DateTime is always in UTC.
Warning
There is no migration possible for this change. Your SQLite databases need to be recreated.
Maintenance
There is no maintenance job so far that cleans up the replication tables. You can add the following script to your PostgreSQL migration to clean up records older than a month.
migrationBuilder.Sql(@"
DELETE FROM ""ConfinityReplicationSchedule""
WHERE ""ConfinityMetadata_ModifiedAt"" < CURRENT_DATE - INTERVAL '1 month';
DELETE FROM ""__ConfinityHistory_ConfinityReplicationSchedule""
WHERE ""Entity_ConfinityMetadata_ModifiedAt"" < CURRENT_DATE - INTERVAL '1 month';
");
v0.17.x
TempFile added stream api and unified data structure [migration]
v0.16.x
TempFile add column for size [migration]
The TempFile service is updated to support chunked uploads. The documentation is updated: (https://confinity.conx.ch/latest/guides/fileupload.html)
The TempFile configuration has new, relatively low limits. Please check the documentation.
v0.15.x
Renamed Analytics Helper
@Html.AddAnalyticsScript() has become @Html.ConfinityAnalyticsHead()
v0.14.x
EntityApp: Column with ConfinitySelectable shows label instead of id
Tables and trees with columns that inherit from ConfinitySelectable will show the label instead of the id in your entity app.
decimal precision on history [migration]
History entities with decimal properties now use the same precision and scale as the main table instead of the default value of the database provider.
on-site editing store added [migration]
v0.13.x
AssetPath is no longer a database entity [migration]
v0.12.x
DeleteBehavior for non ConfinityEntities [migration]
Instead of changing the DeleteBehavior for all entities, only the entities inheriting form ConfinityEntity get their DeleteBehavior set to a default. See Cascade Delete for details.
For postgres: Add the following script to your migration (at the end):
migrationBuilder.Sql(@"
CREATE OR REPLACE FUNCTION confinityHistoryUpdate12() RETURNS void AS
$$
DECLARE
rec record;
nbrow bigint;
v_RowCountInt Int;
BEGIN
FOR rec IN
SELECT *
FROM information_schema.tables T1
WHERE table_schema = 'public'
AND table_name NOT LIKE '__ConfinityHistory_%'
AND EXISTS(SELECT NULL
FROM information_schema.tables T2
WHERE T2.""table_name"" = '__ConfinityHistory_' || T1.""table_name"")
AND table_name NOT IN ('ConfinityReplicationSchedule')
ORDER BY table_name
LOOP
execute '
UPDATE ' || quote_ident('__ConfinityHistory_' || rec.table_name) || '
SET ""To"" = ''2021-12-31 23:59:59.999999+00''
WHERE ""To"" > ''2099-01-01 00:00:00''
and ""Entity_Id"" NOT IN (SELECT ""Id"" FROM ' || quote_ident(rec.table_name) || ' )';
GET DIAGNOSTICS v_RowCountInt = ROW_COUNT;
RAISE NOTICE 'Fixed % rows in table %', v_RowCountInt, quote_ident('__ConfinityHistory_' || rec.table_name);
END LOOP;
END
$$ LANGUAGE plpgsql;
select confinityHistoryUpdate12();
DROP FUNCTION confinityHistoryUpdate12;
");
Tips
The migration is resource intensive and can be run separately after the installation.
v0.11.x
User Group [migration]
The UserGroup entity got the new property Key. Please include the following script in your migration.
migrationBuilder.Sql(@"
UPDATE ""ConfinityUserGroup"" SET ""Key"" = ""Name"";
UPDATE ""__ConfinityHistory_ConfinityUserGroup"" SET ""Entity_Key"" = ""Entity_Name"";
");
Dropped support for DeleteBehavior.CascadeDelete [migration]
Confinity does not support DeleteBehavior.CascadeDelete for entities inheriting ConfinityEntitiy. Use DeleteBehavior.ClientCascade instead. This is needed to ensure correct updates of the entity history.
v0.10.x
Confinity Search
Constants moved to a single class.
ConfinitySearchAttributes => ConfinitySearchConstants.AttributesConfinitySearchAttributeTypes => ConfinitySearchConstants.AttributeTypesConfinitySearchFacets => ConfinitySearchConstants.Facets
v0.9.x
Tips
Update to 0.8.x first (with migration) to make the update to .net6 easier.
.Net 6.0 / ef core 6.0 [migration]
- the project was updated to use .net 6.0 see breaking changes
- the project was update to use ef core 6.0 see breaking changes
Warning
Create a migration to verify your schema has not changed. If changes show up, update your model to remove the listed schema changes. The only change from Confinity is a recreation of the foreign key FK_ConfinityTag_ConfinityTag_SynonymForId.
IAsset, IImageAsset, IDocumentAsset changes
- IAsset, IImageAsset, IDocumentAsset properties migrated to methods. Ex. Name => GetName() etc.
- ConfinityPictureViewModel property Model renamed to Picture
v0.8.x
Page [migration]
- removed column length constraint
ConfinityPage.MetaCanonicalLink. - A type migration for
Page.MetaCanonicalLinkandIPageModel.MetaCanonicalLinkfromstring?toLinkModel?is required.
migrationBuilder.Sql(@"
Update ""__ConfinityHistory_ConfinityPage"" set ""Entity_MetaCanonicalLink"" = null;
Update ""ConfinityPage"" set ""MetaCanonicalLink""= null;
");
v0.7.x
Forms [migration]
- Added column
ConfinityForm.AllowDynamicRecipient.
v0.6.x
Entity App: Replace ITableLayoutBuilder SortableBy with Sortable with
- Support for configurable sort fields was removed. If entities should be sortable by authors, they must implement
IConfinitySortable.
Pages [migration]
- Rename
Page.SortPositiontoPage.SortIndex. - Rename
PageModelExtensions.GetSortPositiontoPageModelExtensions.GetSortIndex.
v0.5.11
Core [migration] [admin-configuration]
ScheduledTaskgot split intoScheduledTaskandScheduledTaskExecution. Before this split, every execution of aScheduledTaskwould create a row in the corresponding history table. You can integrate the following script in your migration to tidy up the history table. It will delete all history entries but the newest per `ScheduledTask``.
migrationBuilder.Sql(
@"WITH newest_entry AS
(
SELECT ""Id"" FROM ""__ConfinityHistory_ConfinityScheduledTask"" T1
JOIN (SELECT MAX(""From"") ""MaxFrom"", ""Entity_Id"" from ""__ConfinityHistory_ConfinityScheduledTask"" GROUP BY ""Entity_Id"") T2
ON T1.""From"" = T2.""MaxFrom"" AND T1.""Entity_Id"" = T2.""Entity_Id""
)
DELETE FROM ""__ConfinityHistory_ConfinityScheduledTask""
WHERE ""Id"" NOT IN (SELECT ""Id"" from newest_entry);");
*** Important: All schedules must be reconfigured. Foreach scheduled task select on which instance they should run! ***
v0.5.10
Core [migration]
TempFilechanges
v0.5.7
Core [migration]
- Added
TempFile&TempFileContent
v0.5.x
Removed Component.InvokeConfinityViewComponentAsync(object model)
Component.InvokeConfinityViewComponentAsync(object model) got removed. Use Component.InvokeConfinityViewComponentAsync(ViewComponentModel model)
v0.4.x
Moved DisableSecurityHeaders
Moved DisableSecurityHeaders from ConfinityOptions to new ConfinityHttpHeaderOptions.
v0.3.x
Core [migration]
- Removed
ChangeLogHistory
v0.2.x
Entity App
- Renamed
EntityEventstoIActionEventListener - Renamed
EntityEventContexttoActionEventContext - Renamed
EntityOperationtoActionEventType
v0.1.121
Forms Module
- The base type for form fields has changed from
FormFieldtoFormItem
v0.1.111
Add index to ReplicationSchedule-Table [migration]
v0.1.98
Rendering of meta tags, stylesheets and javascripts
- The html helper
@Html.PageMetaData()is now@Html.ConfinityHead(). - Removed the
Scriptssection and replaced it with the HTML helper@Html.ConfinityJavaScripts() - Removed the
Stylessection and replaced it with the HTML helper@Html.ConfinityStylesheets() - Removed
IStaticResourceManagerand replaced it with an extension for ViewData which can be used like this:- in a view component:
HttpContext.ConfinityPage().Resources.AddJavaScript()(or.AddStyleSheet()) - in a view:
@Context.ConfinityPage().Resources.AddStylesheet()(or.AddJavaScript())
- in a view component:
Changes in IModuleConfigurationBuilder
- New type parameter
TEntityfor methodRegisterPlaceholderPage - Rename
AddMenuEntry->AddMenuGroup
v0.1.95
Harmonization of dynamic links and page links
The Property IDynamicPage.Identifier was replaced by IDynamicPage.Id in order to harmonize the interaction with dynamic pages. This brought the following changes:
- Removed
IPageUrlHelper.CreateUrlForDynamicPage(useIPageUrlHelper.CreateUrlForPageinstead) - Removed
IPageService.GetDynamicPage(useIPageService.GetPageinstead) - Removed
IPageService.CreateUrlForPage<TIdentifier>(useIPageService.CreateUrlForPageinstead)
v0.1.94
Introduce property IsPrimary on PageDomain [migration]
By moving the PageDomain in its on entity app (instead of being a child list on Page) the property SortIndex got removed and a new IsPrimary property was added instead.
IMPORTANT
Before this release, the first (lowest index) PageDomain was considered to be primary. Use the following snippet in the beginning of your generated migration to update the data accordingly.
migrationBuilder.Sql("UPDATE \"ConfinityPageDomain\" SET \"SortIndex\"=0 WHERE \"SortIndex\" <> 1;");
migrationBuilder.Sql("UPDATE \"__ConfinityHistory_ConfinityPageDomain\" SET \"Entity_SortIndex\"=0 WHERE \"Entity_SortIndex\" <> 1;");
v0.1.90
Reading time and authors in blog module [migration]
- Replaced
UserwithBlogAuthorentity - Added reading time for articles
v0.1.88
Rework replication error handling [migration]
- Rename ReplicationSchedule.ErrorMessage -> ReplicationSchedule.Error
- Store Exception.ToString() instead of Exception.Message
- Remove ReplicationSchedule.StackTrace
v0.1.87
Rename EntityReference.IsNullable -> EntityReference.IsOptional [migration]
v0.1.67
Reworked IModuleConfigurationBuilder
Many configuration methods signature was changed. They now return a Builder (e.g. IAppBuilder) instead of void. Thereby the number of parameters and overloads could be reduced by moving optional parameters in those builders.
AddAppreturns aIAppBuilder(was a parameter before)AddEntityAppreturns aIEntityAppBuilder(was a parameter before)AddEntityAppreturns aIEntityAppBuilder(was a parameter before)TryAddScheduledTaskwas renamed toRegisterScheduledTaskand returns a newly introducedIScheduledTaskBuilderRegisterPageLayoutreturns a newly introducedIPageLayoutBuilderRegisterConfinityContentreturns a newly introducedIConfinityContentBuilderRegisterPlaceholderPagereturns a newly introducedIPlaceholderPageBuilderRegisterSettingsreturns a newly introducedISettingsBuilder
v0.1.65
Changed signature for IHistoryDbContext.HistorySet
Removed the second parameter (DbContext historyContext) from IHistoryDbContext.HistorySet.
Replaced ReplicationSchedule.IsDeletion [migration]
ReplicationSchedule.IsDeletion got replaced by ReplicationSchedule.ReplicationType.
IMPORTANT
In order to not corrupt your existing replication schedules a Postgres DB, overwrite the generated migration content with the following code:
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "ReplicationScheduleOptions",
table: "ConfinityReplicationSchedule",
type: "text",
nullable: false,
defaultValue: "");
migrationBuilder.AddColumn<int>(
name: "ReplicationType",
table: "ConfinityReplicationSchedule",
type: "integer",
nullable: false,
defaultValue: 0);
migrationBuilder.AddColumn<string>(
name: "Entity_ReplicationScheduleOptions",
table: "__ConfinityHistory_ConfinityReplicationSchedule",
type: "text",
nullable: true);
migrationBuilder.AddColumn<int>(
name: "Entity_ReplicationType",
table: "__ConfinityHistory_ConfinityReplicationSchedule",
type: "integer",
nullable: false,
defaultValue: 0);
migrationBuilder.Sql("UPDATE \"ConfinityReplicationSchedule\" SET \"ReplicationType\" = 1 WHERE \"IsDeletion\" = TRUE;");
migrationBuilder.Sql("UPDATE \"__ConfinityHistory_ConfinityReplicationSchedule\" SET \"Entity_ReplicationType\" = 1 WHERE \"Entity_IsDeletion\" = TRUE;");
migrationBuilder.DropColumn(
name: "IsDeletion",
table: "ConfinityReplicationSchedule");
migrationBuilder.DropColumn(
name: "Entity_IsDeletion",
table: "__ConfinityHistory_ConfinityReplicationSchedule");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<bool>(
name: "IsDeletion",
table: "ConfinityReplicationSchedule",
type: "boolean",
nullable: false,
defaultValue: false);
migrationBuilder.AddColumn<bool>(
name: "Entity_IsDeletion",
table: "__ConfinityHistory_ConfinityReplicationSchedule",
type: "boolean",
nullable: false,
defaultValue: false);
migrationBuilder.Sql("UPDATE \"ConfinityReplicationSchedule\" SET \"IsDeletion\" = TRUE WHERE \"ReplicationType\" = 1;");
migrationBuilder.Sql("UPDATE \"__ConfinityHistory_ConfinityReplicationSchedule\" SET \"Entity_IsDeletion\" = TRUE WHERE \"Entity_ReplicationType\" = 1;");
migrationBuilder.DropColumn(
name: "ReplicationScheduleOptions",
table: "ConfinityReplicationSchedule");
migrationBuilder.DropColumn(
name: "ReplicationType",
table: "ConfinityReplicationSchedule");
migrationBuilder.DropColumn(
name: "Entity_ReplicationScheduleOptions",
table: "__ConfinityHistory_ConfinityReplicationSchedule");
migrationBuilder.DropColumn(
name: "Entity_ReplicationType",
table: "__ConfinityHistory_ConfinityReplicationSchedule");
}
v0.1.55
Renamed appsettings keys
In order to have appsettings that can be overwritten by environment variables, the following keys got renamed:
- Confinity.Assets -> ConfinityAssets
- Confinity.Pages -> ConfinityPages
- Messaging -> ConfinityMessaging
- Confinity.Elasticsearch -> ConfinityElasticsearch
- Confinity.MediaPlayer -> ConfinityMediaPlayer
- Confinity.Replication -> ConfinityReplication
Changed ConfinityReplication appsettings to dictionary structure
Example:
{
"ConfinityReplication": {
"Stages": {
"previewStage": {
"Key": "previewStage",
"Order": 1,
"Label": "Preview",
"Instances": {
"preview1": {
"Key": "preview1",
"ServerUrl": "http://preview1",
"Secret": "confinityconfinity"
}
}
},
"publicStage": {
"Key": "publicStage",
"Order": 2,
"Label": "Public",
"Instances": {
"public1": {
"Key": "public1",
"ServerUrl": "http://public1",
"Secret": "confinityconfinity"
},
"public2": {
"Key": "public2",
"ServerUrl": "http://public2",
"Secret": "confinityconfinity"
}
}
}
}
}
}
v0.1.53
Moved replication settings into appsettings
The replication configuration (e.g. stages, instances) is no longer a setting but moved to the appsettings. Example:
{
"Confinity.Replication": {
"Stages": [
{
"Key": "previewStage",
"Order": 1,
"Label": "Preview",
"Instances": [
{
"Key": "preview1",
"ServerUrl": "http://preview1",
"Secret": "confinity"
}
]
},
{
"Key": "publicStage",
"Order": 2,
"Label": "Public",
"Instances": [
{
"Key": "public",
"ServerUrl": "http://public1",
"Secret": "confinity"
}
]
}
]
}
}
v0.1.49
Removed IFolderHelper
Removed IFolderHelper and added its methods to IAssetService instead.
v0.1.48
Reworked Unique validator
Calling Unique now has an optional parameter Action<IUniqueValidatorBuilder> with the methods WithSameValue and ErrorMessage and returns a IElementValidatorBuilder.
v0.1.47
Reworked Unique validator
Calling Unique now returns a IUniqueValidatorBuilder with the method WithSameValue. This replaces the overload of Unique with a params of same value selectors.
v0.1.39
User Management [migration]
Added field User Provider to store the system from where the user comes from.
v0.1.37
Moved IAsset
Moved IAsset from namespace Confinity.Interfaces to Confinity.Assets.
Renamed IAssetHelper
Renamed IAssetHelper to IAssetService.
ImageTagHelper and ConfinityPictureComponent requiring IImageAsset
The ImageTagHelper and the ConfinityPictureComponent now require an IImageAsset instead of an IAsset.
You can use IAssetService.GetImageAsset() to get an IImageAsset.
v0.1.29
Renamed ICmsSortable to IConfinitySortable
Renamed the ICmsSortable to IConfinitySortable.
v0.1.28
Renamed AddSelectAsset for multiple assets to AddSelectAssets
Renamed the AddSelectAsset on IFormContainerBuilder to AddSelectAsset for multiple assets.
v0.1.20
Renamed CreateUrlForAssetImage and CreateUrlForAsset
Renamed the IUrlHelper extensions methods CreateUrlForAssetImage and CreateUrlForAsset to ConfinityAssetImage and ConfinityAsset respectively.
v0.1.19
IDynamicPageResolver expects async implementation
Changed signature of GetPagesForPlaceholderPageAsync to
Task<IEnumerable<IDynamicPage>> GetPagesForPlaceholderPageAsync(
TConfiguration configuration, IPageModel placeholderPageModel);
0.1.21
AssetApp
FolderHelper moved to internal package use IFolderHelper
0.1.16
ModuleConfigurationBuilder
Renamed IModuleConfigurationBuilder.RegisterChangeHandler to IModuleConfigurationBuilder.RegisterChangeListener.
IEntityChangeListener: Abstraction for parameter
IEntityChangeListener.Handle parameter changed. IEnumerable<EntityChange> -> IEnumerable<EntityChange>.
Bugfix IEntityChangeListener: Called on all replication instances
Fixed a bug where the implementations of IEntityChangeListener were not invoked on all instances when there where multiple instances configured for the same stage.
0.1.9
Configuration
Email configuration Key changed from Confinity.Mail to ConfinityMail. This way, one can overwrite the variable with environments variables.
0.1.6
IPageService: Simplify methods
All methods in IPageService that return a IPageModel and required a HttpContext now obtain the HttpContext themself and are renamed.
IPageModel? GetPage(HttpContext httpContext)->IPageModel? GetCurrentPage()IPageModel? Get404Page(HttpContext httpContext)->IPageModel? GetCurrent404Page()IPageModel? GetWebsite(HttpContext httpContext)->IPageModel? GetCurrentWebsite()Uri? CreateUrlForPage<TIdentifier>(TIdentifier pageIdentifier, HttpRequest httpRequest, bool forceAbsolute = false)->Uri? CreateUrlForPage<TIdentifier> (TIdentifier pageIdentifier, bool forceAbsolute = false)
0.1.5
Pages: Renamed enum values of PageTyes
Renamed enum PageType.Page to PageType.ContentPage & PageType.DynamicPage to PageType.PlaceholderPage.
Pages: Renamed class DynamicPageConfiguration
Renamed class DynamicPageConfiguration to PlaceholderPageConfiguration.
Removed IPageStore
The IPageStore is now an internal interface. Developers can use IPageService (to retrieve pages) & IPageCacheService to rebuild the page cache.
Introduced IPageModel
IPageService & IPageUrlHelper no longer work with the entity Page but with the abstraction IPageModel.
Reworked IDynamicPageResolver
IDynamicPageResolver has only one method to implement now:
IEnumerable<IDynamicPage> GetPagesForPlaceholderPage(TConfiguration configuration,
IPageModel placeholderPageModel);
0.1.x
Changes of Confinity entities require a data migration.
PostgreSQL
// will be recreated on launch
migrationBuilder.Sql(@"DELETE FROM ""ConfinityRoleRolePermission"";");
migrationBuilder.Sql(@"DELETE FROM ""__ConfinityHistory_ConfinityRoleRolePermission"";");
migrationBuilder.Sql(@"DELETE FROM ""ConfinityRolePermission"";");
migrationBuilder.Sql(@"DELETE FROM ""__ConfinityHistory_ConfinityRolePermission"";");
// default value for new not null
migrationBuilder.Sql(@"UPDATE ""ConfinityUser"" set ""PasswordHash"" = ''::bytea where ""PasswordHash"" is null;");
sqlite
// will be recreated on launch
migrationBuilder.Sql(@"DELETE FROM ConfinityRoleRolePermission;");
migrationBuilder.Sql(@"DELETE FROM __ConfinityHistory_ConfinityRoleRolePermission;");
migrationBuilder.Sql(@"DELETE FROM ConfinityRolePermission;");
migrationBuilder.Sql(@"DELETE FROM __ConfinityHistory_ConfinityRolePermission;");
0.0.99
The method ContextAction has been removed from IEntityAppActionConfigurationBuilder. Every action that has not set an ' ContextAction' is defining a permission.
0.0.90
PageUrlHelper: Moved method CreateUrlForStaticResource
The method CreateUrlForStaticResource has been moved from IPageUrlHelper to IStaticResourceHelper.