EpiServer JohannesRevisionNotes
EpiServer JohannesRevisionNotes
Nuget:
Install-Package <name> -ProjectName <project>
Update-Package <name> -ProjectName <project> -ToHighestMinor
Update-Package <name> -ProjectName <project> -Vesion <version>
Uninstall-Package <name> -ProjectName <project>
Database Updates
Update-EPiDatabase
Customize TinyMCE
Dynamically change the behaviour - e.g. based on user roles:
settings
.AddSetting("table_toolbar", "tabledelete | tableinsertrowbefore")
.AddSetting("table_appearance_options", false);
}
}
or
defaultSettings
.AddPlugin("table")
.AppendToolbar("table")
defaultSettings
.AppendToolbar("styleselect")
.StyleFormats(
new { title = "Red text", inline = "span", styles = new { color = "#ff0000"
} },
new { title = "Awesome numbering", selector = "ol", classes = "awesome-
numbering" },
new { title = "Roman numbering", selector = "ol", classes = "roman-
numbering" }
);
[assembly: OwinStartup(typeof(AlloyTemplates.Startup))]
namespace AlloyTemplates
{
public class Startup
{
<episerver.framework>
<appData basePath="App_Data" />
<scanAssembly forceBinFolderScan="true" />
<virtualRoles addClaims="true">
<providers>
<add name="Administrators" type="EPiServer.Security.WindowsAdministratorsRole, EP
iServer.Framework" />
<add name="Everyone" type="EPiServer.Security.EveryoneRole, EPiServer.Framework"
/>
<add name="Authenticated" type="EPiServer.Security.AuthenticatedRole, EPiServer.F
ramework" />
<add name="Anonymous" type="EPiServer.Security.AnonymousRole, EPiServer.Framework
" />
<add name="CmsAdmins" type="EPiServer.Security.MappedRole, EPiServer.Framework" r
oles="WebAdmins, Administrators" mode="Any" />
<add name="CmsEditors" type="EPiServer.Security.MappedRole, EPiServer.Framework"
roles="WebEditors" mode="Any" />
<add name="Creator" type="EPiServer.Security.CreatorRole, EPiServer" />
</providers>
</virtualRoles>
Notes:
Permissions
Read
Create
Change
Delete
Publish
Administer
<location path="Views/Plugins">
...
</location>
APIs
UIRoleProvider
CreateRole(string role)
AddUserToRoles(string user, string[] roles)
UIUserProvider
PrincipalInfo
EPiServer.Security.PrincipalInfo.Current
Name: string
RoleList: string[]
(static) HasAdminAccess: bool
(static) HasEditAccess: bool
(static) HasPathAccess(string path): bool
(static) CurrentPrincipal: IPrincipal
(static) Current: PrincipalInfo
(static) CreatePrincipal(string username): IPrincipal
Personalization
Manual:
Automatic:
Notes:
Visitor Groups
Can be used in content areas and rich text areas
Can also personalize pages and media by removing the everyone access rights and adding a
visitor group with read access
Built-in Criteria
Number of visits
User Profile
Visited Category
Visited Page
Geographic Coordinate
Geographic Location
Time of day
Landing URL
Referrer
Search keyword
Visitor Group Membership
Note: Must setup a geolocation provider to have access to Geographic Coordinate and
Geographic Location criteria
<episerver.framework>
<geolocation defaultProvider="maxmind2">
<providers>
<add name="maxmind2"
type="EPiServer.Personalization.MaxMindGeolocationProvider,
EPiServer.Personalization.MaxMindGeolocation"
databaseFileName="App_Data\GeoLite2\GeoLite2-City.mmdb"
locationsFileName="App_Data\GeoLite2\GeoLite2-City-Locations-en.csv" />
</providers>
</geolocation>
Note: EPiServer.VisitorGroupsCriteriaPack adds additional visitor groups which can be used (e.g
IP address / OS / role etc)
User Profile
Episerver CMS includes a user profile feature that can be implemented by using the
EPiServerProfile class and
ASP.NET Profile configuration.
Projects
A project lets you manage the publishing process for multiple related content items.
Project feature is enabled for the entire site by default and appears as a dark gray bar at
the bottom of Edit view. Projects are activated by selecting their name from the popup list
of projects.
When a project is active, new or edited content is automatically added to the project.
When a project is not active, editing content shows its published version, not the draft in
the project. But a user can use the versions gadget to see the draft in the project and
switch to it.
The list of projects is visible to all users. Although a project cannot have access rights, all
the content items in a project could have access rights to prevent unauthorized users form
working with them.
You can publish items that are set to Ready to publish and leave items that are not ready
for a later time. You can continue working with a project even after some or all items are
published.
If a content item has been edited and published outside a project, then when the draft in
the project is published it becomes the published version. There is no merging.
Uploaded media that is associated with a project is not published until it is manually
published or published via scheduling, even if the automatic publish for media assets
setting is turned on.
Content Approval
When an editor has finished working on a content item, the item can only be set to Ready
for Review
At least one reviewer in each step must approve the content item before it is assigned a
status of Ready to Publish. Reviewers must have Read and at least one other access right to
approve or decline. Check boxes are available for comments and four-eyes enforcement
(e.g. cannot approve your own changes).
An approval sequence can be set up with any number of approval steps and any number of
reviewers in each step. The sequence is set up by an administrator, who also defined, for
each step individually, who can approve or decline a content item.
Assets, such as blocks and media (also forms and catalogues), cannot have individual
approval sequences. Instead, the content approval sequencne is set on each assets folder,
and all assets in a folder have the same approval sequence. The block and media folders in
the assets pane are actually the same folders in the software and share the same content
approval sequences.
It is recommended to have at least two reviewers per language and an administrator can
always approve and publish a page.
If content is in more than one language each language must have at least one reviewer.
Avoid large groups of reviewers as this can cause performance issues / spam / people to
assume someone else will deal with it.
Localization
To enable a language: CMS | Admin | Config | Manange Website Languages set language to
enabled and save.
Can specify which languages are available to edit a page in through the tools drop down
on a content item (tools -> language settings). Can still edit a previously published page
even if the language is no longer available.
CMS | Admin | Config | System Settings to enable "Detect language via browser's language
preference"
Episerver langauges addon can auto translate through Azure Translator (optional addon)
Thread.CurrentThread.CurrentCulture : must be a specific sub-culture as it affects date
formats and sort order (e.g. fr-CA)
Thread.CurrentThread.CurrentUICulture : can be netraul because it only affects loading of
localized strings (e.g. fr)
Can set the culture automatically in ASP.NET by setting culture="auto" uiCulture="auto"
on <globalization> in <system.web>
Strict language routing: urls (other than the root) must contain the language of the content
accessed.
Order of preference
System ( CurrentCulture ) and UI language ( CurrentUICulture ):
Content Language
Fallback Language
if no content published for the specific language then this is used (can set up to two per
language)
Also used for nav - e.g. if fallback language content is available then items will be visible
(otherwise they won't show)
Trying to access a page by changing the language in url when the content doesn't exist in
the language or a fallback -> 404 (when using strict language routing)
If a content area does not have [CultureSpecific] then all blocks are shared between
languages; but only the blocks with a match published language (or a fallback) are shown.
Replacement Language
Outright replaces the languages; takes precedence over fallback.
Config
~/Resources/Languagefiles
<productpage>
<name>Produkt</name>
<description>Används för att presentera en specifik produkt</description>
</productpage>
PropertyNames.xml (fields)
GroupNames.xml (tabs)
Usage
@Html.Translate("/productpage/changed")
LocalizationService:
GetString(string)
GetString(string, FallbackBehaviours)
TryGetString(string, out string)
Fallback Behavior
Echo
The key is returned unmodified if no match is found and the key is not a complex key
(starts with a '# or '/'). For complex key an empty value is returned when no match is found.
FallbackCulture
The resource is returned using the fallback culture if no match is found.
MissingMessage
A message saying that no resource was found is returned if no match is found.
Null
Returns null instead of an empty string when no match is found.
Content
Content Type: Defines a set of properties; can be associated with multiple content templates.
Content Item: Instance of a content type.
Content Flow:
Create -> Save -> Check-In (Ready to Publish) -> Publish -> Move to Trash -> Delete
|----------------------------------------^
Modified: Only updated when the editor selects the "update modified date" checkbox.
Saved: last published date
ID : int
WorkID : int (aka version ID)
ProviderName : string ( null if default, or custom provider name)
Pages
[CultureSpecific]
[Display(Name = "Main body",
Description = "Desc",
GroupName = SystemTabNames.Content, Order = 20)]
public virtual XhtmlString MainBody { get; set; }
Blocks
Note: Can also create controller-less blocks by adding a MyBlock.cshtml file to ~/Views/Shared
which is supposedly more efficient...
Media
Inheritance:
Notes:
Types are set at creation; creating a type later does not convert existing instances (and no
tool exists for media; only for pages).
ImageData does not support SVG out of the box; can create custom (e.g. inherit and set
MediaDescriptor(ExtensionString = svg)
Folder structure is shared between blocks and media.
Can create a classname.cshtml to customize the default rendering (e.g. add a preview of an
image / pdf or similar)
Tabs
[GroupDefinitions]
public static class SiteTabNames
{
[Display(Order = 10)] // to sort horizontal tabs
[RequiredAccess(AccessLevel.Edit)]
public const string SEO = "SEO";
[Display(Order = 20)]
[RequiredAccess(AccessLevel.Administer)]
public const string SiteSettings = "Site Settings";
}
Notes:
Do not use PropertyFor for properties on "protected" tabs as apparently this bypasses
security...
The magic string "EPiServerCMS_SettingsPanel" will cause the property to go onto the top
panel rather than within the tabbed interface.
Class must be static
Customization
Property Types:
ContentReference: Link to any IContent (e.g. block; media or page). Can limit input through
UIHints.
PageReference: Link to a page
ContentArea: Collections of IContent
It's possible to have a property of any block type; which will allow inline editing of a block
as part of the parent IContent item (but only a single instance).
Url: either an internal (page or media) or external link (url or email)
LinkItemCollection: multiple links
IList<ContentReference> : A list of references to content items.
Class Attributes:
AccessAttribute:
Users / Roles / VisitorGroups: specify which should have none; if none specified then
nobody will have access.
Access: content types only support Create.
Example: [Access(Users = "anfi", Roles="Site_Editors")]
AvailableContentTypesAttribute:
Availability: Defines whether all or no content types are available. If you set none,
other settings on the attribute is ignored. (None | All | Selected - optional)
Include: Array of types; specifies which content types can be created underneath this
type.
Exclude: Array of types; specifies which content types cannot be created underneath
this type.
IncludeOn: Array of types; specifies underneath which types this content type can be
created.
ExcludeOn: Array of types; specifies underneath which types this content type cannot
be created.
Types can be a (base) class or interface.
Example: [AvailableContentTypes(Exclude = new Type[] { typeof(X) })]
ImageUrl:
Path: the path to the icon to display.
TemplateDescriptor:
Controls which template to use render a block and / or page. Allowing a single content
item to have multiple ways of rendering.
Inherited: When set to true, model types that inherit from the ModelType get the
template as a supported template.
Default: Defines the template as the default template for the model type.
Description: Description of the template
ModelType: The model for the content type. This can be set for untyped templates
which derives from EPiServer.TemplatePage to set the model type, which is the type of
the CurrentPage property.
Path: The path to the rendering template
AvailableWithoutTag: Template will be available when no tags are set.
Tags: Template will be used when any of these tags are set (e.g. @Html.PropertyFor(x
=> x, new { Tag = "x" }))
Example: [TemplateDescriptor(Inherited = true, Tags = new[] { SiteTags.Full },
AvailableWithoutTag = true
Notes:
Changes made in the CMS to these properties will take priority over those set by code; can
reset the CMS changes through the "Revert to default" button on the type
RenderingTags.Preview and RenderingTags.Edit can be used together with a
TemplateDescriptor to create a custom preview controller to render previews for multiple
DisplayOptions
[TemplateDescriptor(Inherited = true,
TemplateTypeCategory = TemplateTypeCategories.MvcController,
Tags = new[] { RenderingTags.Preview, RenderingTags.Edit },
AvailableWithoutTag =false)]
public class PreviewController : ActionControllerBase, IRenderTemplate<BlockData>
{
public ActionResult Index(IContent currentContent)
{
// Start page is used in the below as the default Alloy templates require a C
urrentPage.
var contentLoader = ServiceLocator.Current.GetInstance<IContentLoader>();
var startPage = contentLoader.Get<PageData>(ContentReference.StartPage);
var model = new BlockEditPageViewModel(startPage, currentContent);
return View(model);
}
}
Notes:
Property Attributes:
UIHint
Controls the UI to render for the property within Episerver.
UIHint.Block | UIHint.BlockFolder | UIHint.Image | UIHint.Legacy |
UIHint.LongString | UIHint.MediaFile | UIHint.MediaFolder |
UIHint.PreviewableText | UIHint.TextArea | UIHint.Video
Example: [UIHint(UIHint.Image)]
StringLength
Example: [StringLength(60, MinimumLength = 5)]
SelectOne / SelectMany
Allows the selection of values through either a dropdown or checkboxes. The factory type
must implement ISelectionFactory
Example: [SelectOne(SelectionFactoryType=typeof(LanguageSelectionFactory))]
Required
AllowedTypes
Specifies which content types are allowed within a ContentArea
Example: [AllowTypes(typeof(BlockA), typeof(BlockB))]
Restricted types are passed through as a second array of types:
[AllowedTypes(typeof(BlockData), new[] { typeof(JumbotronBlock) })]
ScaffoldColumn
Hides the property from the content editor
Searchable
Includes the property in the search index
Editable
Whether the property is editable through the UI
ListItems
Specifies the minimum and / or maximum allowed items within a list
Example: [ListItem(1, 2, ErrorMessage("Specify between 1 and 2 items"))]
Ignore
Stops the value from being stored in the database
Various other MVC validation attributes Range , RegularExpression , Compare etc...
APIs
FilterAccess:
FilterPublished:
FilterForVisitor:
IContentLoader:
Selection Factory
Initialization Modules
[InitializableModule]
[ModuleDependency(typeof(EPiServer.Web.InitializationModule))]
public class RegisterAdminInitializationModule : IInitializableModule
{
public void Initialize(InitializationEngine context)
{
// context.Locate.Advanced.GetInstance<TType>();
}
}
}
Dependency Injection
[InitializableModule]
[ModuleDependency(typeof(EPiServer.Web.InitializationModule))]
public class RegisterDependencyResolverInitializationModule : IConfigurableModule
{
public void ConfigureContainer(ServiceConfigurationContext context)
{
DependencyResolver.SetResolver(new StructureMapDependencyResolver(context.Structu
reMap()));
//Implementations for custom interfaces can be registered here.
context.ConfigurationComplete += (o, e) =>
{
context.Services.AddTransient<IService, Service>();
//Register custom implementations that should be used in favour of the default
implementations
};
}
public void Initialize(InitializationEngine context) { }
public void Uninitialize(InitializationEngine context) { }
}
Display Options
Customize the list of options shown when using the urger menu on an item in a content area ->
display as.
Customize the rendering through the EditorDescriptor and the associated tags below.
[InitializableModule]
[ModuleDependency(typeof(EPiServer.Web.InitializationModule))]
public class DisplayOptionsInitializationModule : IInitializableModule
{
public void Initialize(InitializationEngine context)
{
var options = context.Locate.Advanced.GetInstance<DisplayOptions>();
Events
[InitializableModule]
[ModuleDependency(typeof(EPiServer.Web.InitializationModule))]
public class PreventMoreThanMaxChildrenInitializationModule : IInitializableModule
{
private bool initialized = false;
private IContentEvents events;
private IContentLoader loader;
private const int maxChildren = 8;
events.CreatingContent += Events_CreatingContent;
initialized = true;
}
}
if (sitepage != null)
{
var children = loader.GetChildren<IContent>(sitepage.ParentLink);
if (children.Count() >= maxChildren)
{
e.CancelAction = true;
e.CancelReason = $"Cannot create a new page if the parent has {maxChildren} o
r more children.";
}
}
}
Scheduled Jobs
public TestScheduledJob()
{
IsStoppable = true;
}
// Do stuff.
//For long running jobs periodically check if stop is signaled and if so stop exe
cution
if (_stopSignaled)
{
return "Stop of job was called";
}
return "done";
}
}
Caching
Output Caching
Response.Cache.SetCacheability(System.Web.HttpCacheability.Public);
Response.Cache.SetExpires(System.DateTime.Now.AddHours(1));
Response.Cache.SetSlidingExpiration(true);
Alternatively:
[EPiServer.Web.Mvc.ContentOutputCache]
public ViewResult Index(SitePageData currentPage)
Notes:
<system.webServer>
<staticContent>
<clientCache cacheControlMode="UseMaxAge" cacheControlMaxAge="1.00:00:00" />
</staticContent>
<system.webServer>
Object Caching
TODO
Logging
Levels:
Trace
Debug
Information (Because why would you want to type just Info; I think Episerver wants to sell
keyboards or something)
Warning (Warn would have clearly been ambiguous)
Error
Critical (Fuck standards; who has ever heard of Log4Net, NLog and Serilog)
private readonly ILogger logger = LogManager.GetLogger();
logger.Error("msg");
Logger.Error("msg", exception);
logger.Error("{0}, {1}", 1, 2);
logger.Error(item, s => "msg: " + s.Expensive());
logger.Error(item, exception, (s, ex) => "msg:" + s.Expensive() + " ex: " + ex.Messag
e);
Episerver Forms
Form definitions are stored as content in the CMS, in a similar way to blocks, so they can be
treated as content.
Form submissions are stored in Dynamic Data Store by default.
Form elements are included like t Text and Selection. Developers can define their own by
deriving from ElementBlockBase .
Use forms primarily when an editor will do the form management; use a page type for
complex forms requiring custom business logic.
Can use Azure Keyvault to encrypt the form submissions; default is plain text.
Retention policy can differ between in-progress and finalized default 30 days for in-
progress; 90 for finalized. But can specify both days and forever.
Page Shortcuts
No shortcut: Creates a link that displays the content you have created. By selecting this,
you can also reset
the page after using other types of links.
Shortcut to another content item: A visitor who clicks this link will be transferred to the
page or media
asset you have selected and kept within the same navigation menu structure.
Shortcut to page on another website: Creates a link to an external URL. The resource could
be anything.
You must specify the absolute URL address, for example, including "https://"
No shortcut, display text only: Creates a heading with no link in the menu, without
displaying any
information or link to another page.
Fetch content from another content item: Creates a link to the original page but any
empty property values
on that page will be fetched from the referenced page or media asset. Useful when re-
using content on the
website because you only need to maintain it in one place.
DXC
Addons not supported on DXC:
Search
CMO
Mail
Relate
Reporting
1. Not Published Pages
2. Published Pages
3. Changed Pages
4. Expired Pages
5. Simple Addresses
6. Link Status (requires the Link Validation scheduled job to be executed.)
Change Log
The following events are registered within the Change Log:
Check In
Publish
Delete
Save
Move
Create
Delete Language
Delete Children
Deleted Items
Rejected
Delayed Publish
Request Approval
Misc Helpers
@Html.DisplayFor(x => x)
@Html.PropertyFor(x => x)
@Html.Propertyfor(x => x, additionalViewData: new { CssClass = "row", Color = "pink"
})
(retrieve from ViewContext.ParentActionViewContext.ViewData)
@Html.EditAttributes(x => x)
@Url.Content("~/Static/css/file.css")
@Url.ContentUrl(ContentLink)
@Html.ContentLink(ContentLink)
@Html.CanonicalLink()
@Html.AlternateLinks()
@Html.RenderEPiServerQuickNavigator()
@Html.MenuList(ContentLink, @<div>@Html.PageLink(item.Page</div>)
PageEditing.PageIsInEditMode
@RenderBody()
@RenderSection()
CurrentPage.HasTemplate()
// render content item using partial template (e.g. page in block layout)
@Html.RenderContentData(someContent, isContentInContentArea: false)
Notes:
Render tags (used for EditorDescriptor ) can be passed as part of the anonymous class
for PropertyFor and the like.
Episerver Labs
Block Enhancements
Features:
A/B Testing
Notes:
Can set participation percentage; test duration (days); when to start; and whether to
published the winning version automatically.
Conversion Goals:
Landing Page - the chosen page is one that a user must click on in order to count as a
conversion.
Site Stickiness - converts when a user visits the content under test and then visits any other
page within the same browser session.
Time on page: monitors how long a visitor spends on a page and converts after a specified
amount of time.
Self-Optimizing Block
You can add multiple child blocks inside and only a single instance is shown (e.g. chance is
1 / count)
Automatically adjusts over time to show the block that earns the most conversions more
often than the other block variants. For example the best of four blocks might be shown 37
times out of 100.
Can have only a single conversion goal; and only works with blocks.
Asset Pane: Blocks, Media and Forms tabs. Expandable form elements and archived tests.
If you need more complex validation, for example that the value of one property should be
validated
depending on the value of another property, then you can implement
EPiServer.Validation.IValidate<T> (where
T is the type to validate). No registration is needed, the initialization scanning will register all
implementations
automatically.
The validator will be called during Save for each content instance that can be assigned to T.
Note however
that this validation will be done on server side only.
CMS 11 IList<T> aka PropertyList only officially supports simple types like int and DateTime.
It can be used with complex types but this is not officially supported (yet).
SearchHandler = Epi Search
SearchClient = Epi Find
https://world.episerver.com/documentation/developer-guides/content-delivery-api/
https://mmols.io/
getting-started-with-the-episerver-content-delivery-api/
!!!!
GuiPlugIn
Component
BackingType