Skip to content

C1 CMS 6.0

Compare
Choose a tag to compare
@mawtex mawtex released this 09 Feb 14:57
· 774 commits to master since this release

C1 CMS 6.0 (6.0.6248.33161)

We are super excited to announce this release – delivering major improvements for end-users, website builders and developers - built in search, a new visual components feature, more ways to customize the console and valuable tooling like WAMP, React/Redux and built in DI support.

Also, we are getting back to our open source roots, putting C1 back into the project name. We here at Orckestra are still driving the development and provide services, premium features and commerce integration on top.

Download

Download C1 CMS 6.0

What’s new in C1 CMS 6.0?

With this release end-users can search for content and media files from within the console and there is now a much better experience when inserting functions, thanks to a new feature we call Components.

Website builders can add sophisticated search features to websites, manage canvas styling for WYSIWYG editors depending on what section a user edits and control where Components can be inserted.

Developers can hook in to our Dependency Injection framework, build search providers and leverage the new WebSocket based WAMP router and new client-side framework for building custom UI, based on ReactJS, Redux and Wampy.

There is also support for creating completely custom perspectives, deep linking to items in the console trees and more.

All the featured that have been driving this release has been released as open source, some via packages in our https://github.com/Orckestra/CMS-Packages repo.

IIS8+ / WebSockets required for full console feature support

The system requirements have changed for this release. You now need IIS8 or later and WebSockets must be enabled on your IIS server.

Search

You can now find pages, data and media files in the console by searching. The feature is installable as a package (available in preview) and currently we have an Lucene.NET based search provider ready and available as open source (see https://github.com/Orckestra/CMS-Packages/tree/search/Orckestra.Search.LuceneNET)

To get search on this release, install the package Orckestra.Search.LuceneNET.

Under the hood are new APIs that enable you to execute searches against content and create your own search provider, should you need it.

The search is integrated with the data type system in C1, meaning that the search will include core and custom C1 data, provided you declare you want this, either through UI or code.

For UI based data types, you will find new options when you edit your data type, enabling you to have a data type made searchable and giving you control over how fields can be searched.

copenhagen search in console

For code based data types, you can attach the attributes [SearchableTypeAttribute()] to types and [SearchableFieldAttribute()] to fields.

You can read more about search and extensibility in this preview documentation.

Components

Components are a new way to insert functions – it is a lot more intuitive to use and it can do a lot more tricks, like insert HTML blocks and generate markup at insert time.

components dialog

To create a component, simply add a xml file to ~/App_Data/Components with the desired markup. You can augment the file with attributes and customize title, group, images and more.

<html pal:title="YouTube Video"
        pal:description="Insert responsive YouTube video."
        pal:tags="media"
        pal:icon="camera-video"
        xmlns="http://www.w3.org/1999/xhtml"
        xmlns:pal="http://www.composite.net/ns/components/1.0">
    <head>
    </head>
    <body>
        <f:function name="Composite.Media.YouTube" xmlns:f="http://www.composite.net/ns/function/1.0">
        </f:function>
    </body>
</html>

The above component will be presented to the user using the values specified with the pal:* attributes. The content of the body is what will be inserted. In the above case this will simply add the function Composite.Media.YouTube like you can do via the existing "Insert Function" feature, but with components the user experience is a lot more easy and intuitive and you can mix in static html and more than one function in the inside the body and this use this feature to do a lot more.

Beside from html documents – which will be copy to content as is - you can make function documents, which will prompt the user to fill in function parameters, and then insert the result of the function execution.

<f:function name="Orckestra.Web.Html.Grid.Columns" 
        pal:title="Social columns..."
        pal:description="Create a set of columns, teasing your social networks"
        pal:tags="social"
        pal:icon="page"
        xmlns:f="http://www.composite.net/ns/function/1.0" xmlns="http://www.w3.org/1999/xhtml" xmlns:pal="http://www.composite.net/ns/components/1.0">
    <f:param name="Column1">        
        <html>
            <head></head>
            <body>
                <f:function name="Orckestra.Web.Html.TeaserCards.IconTeaser">
                    <f:param name="Icon" value="facebook-square" />
                    <f:param name="IconColor" value="primary" />
                    <f:param name="Title" value="Facebook" />
                    <f:param name="Description" value="Find us on Facebook - contact us or like our page to keep up to date with news." />
                    <f:param name="ReadMoreUrl" value="http://www.facebook.com/OrckestraCMS" />
                </f:function>
            </body>
        </html>
    </f:param>
    <f:param name="Column2">
        <html>
            <head></head>
            <body>
                <f:function name="Orckestra.Web.Html.TeaserCards.IconTeaser">
                    <f:param name="Icon" value="twitter-square" />
                    <f:param name="IconColor" value="primary" />
                    <f:param name="Title" value="Twitter" />
                    <f:param name="Description" value="Find us on Twitter - engage with us or follow us to get our short news tweets." />
                    <f:param name="ReadMoreUrl" value="http://www.twitter.com/OrckestraCMS" />
                </f:function>
            </body>
        </html>
    </f:param>
</f:function>

You can augment components with container class names (see below) and there by limit where a component can be inserted. For instance, you can limit a component to only be available in the WYSIWYG editor when users are editing a specific placeholder or the content for a given page type.

Container classes

You can attach class names to placeholders in the CMS, both at template level and per page type.
Doing so will have two effects: the class names will be attached to the body of the WYSIWYG editor’s canvas, enabling you to have custom styling for individual placeholders. Also, the components that are available for user selection will be filtered according to the active container classes and filter rules defined on the component (if any).

    [Placeholder(Id = "content", ContainerClasses="primary,wide", Title = "Content", IsDefault = true)]
    public XhtmlDocument Content { get; set; }

For page types, if you add a Placeholder Content element to it, you will find a new field “Container classes” on the Settings tab, where you can add one or more container classes.

page type container classes

If classes are specified at both template and page type level, the combination of both sets is used. If you want one class to rule out another (for instance, ensure "wide" and "narrow" cannot coexist) you can define this in ~/App_Data/Composite/Configuration/AntonymClassDefinitions.xml

If you are using the Visual Editor on for function parameters or data fields, you can configure what container classes are sent to the editor via a new parameter ContainerClasses on the Composite.Widgets.XhtmlDocument.VisualXhtmlEditor widget.

Dependency Injection

We have used the DI framework from ASP.NET Core – described in depth here https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection

Getting a service

To request a service, use Composite.Core.ServiceLocator – for services registered with the locator, you add the required services to your class constructor, as described in the ASP.NET Core documentation.

var indexUpdater = ServiceLocator.GetService<ISearchIndexUpdater>(); 

Registering a Service

You can register services during the startup of C1 CMS – to do so, create a class marked with the ApplicationStartup attribute and implement the ConfigureServices method as shown below.

The IServiceCollection that is passed to you, let you resister your service as singleton, transient or scoped (shared per request).

[ApplicationStartup]
internal class WampRouterResolverRegistry
{
    public void ConfigureServices(IServiceCollection serviceCollection)
    {
        serviceCollection.Add(ServiceDescriptor.Singleton(new WampRouter()));
    }
}

Function Parameter construction via Dependency Injection

When editing/executing Functions (Razor Functions, MVC Functions, C# Functions etc.) that have one or more parameters of a type that the DI Service Locator can provide, we will hide those parameters from the user UI and dynamically populate the parameter by getting instances from ServiceLocator.

This enable you to extend DI use to Functions in C1.

NuGet Feed

We have a new NuGet feed for our core assemblies and dependencies - this package ensure that your references to C1 CMS assemblies are marked as "do not copy local".

See https://www.nuget.org/packages/C1CMS.Assemblies/

WAMP

While looking around for a more up-to-date technology for client/server communication we came across WAMP - Web Application Messaging Protocol – and we are quite happy to have this included in C1.

WAMP provides Unified Application Routing in an open WebSocket protocol. Routing of both events (for PubSub) and routing of calls (for RPC) between applications components in one protocol. By using WAMP we can build distributed systems out of application components which are loosely coupled and communicate in (soft) real-time.

Our implementation of Wamp make use of WampSharp open source project to build a singular router which would accept different roles to be registered and routed.

Concepts:

  • Realm: A Realm is a routing namespace and an administrative domain for WAMP. For example, a single WAMP router can manage multiple Realms, and those realms are completely separate. We have used a single default realm for core operations however our interfaces exposed realms if you need to separate your namespace.
  • Caller: A Caller issues calls to remote procedures by providing the procedure URI and any arguments for the call.
  • Callee: The Callee executes the procedure using the supplied arguments to the call and return the result of the call to the Caller.
  • Publisher: A Publishers publishes events to topics by providing the topic URI and any payload for the event.
  • Subscriber: Subscribers of the topic will receive the event together with the event payload.

C1 now supports and exposes interfaces to easily register Callees and Publishers in backend.

How to:

For a Callee you should first define it like this:

public class ASampleRpcService : IRpcService
{
    [WampProcedure("uri.sample.your")]
    public IEnumerable<IPage> GetComponents(string title = null)
    {
        return DataFacade.GetData<IPage>().Where(f => f.Title == title);
    }
}

Then you should utilize WampRouterFacade to register it in the router:

WampRouterFacade.RegisterCallee(new ASampleRpcService());

Or

WampRouterFacade.RegisterCallee("myRealm",new ASampleRpcService());

For a publisher definition is like this:

public class ASamplePublisher : IWampEventHandler<long, int>
{
    public IObservable<long> Event => Observable.Timer(TimeSpan.FromMilliseconds(0),
                                                TimeSpan.FromMilliseconds(1000));

    public string Topic => "topic.chosen.your";

    private int counter = 0;

    public int GetNewData()
    {
        return counter++;
    }
}

And just like callee you should utilize WampRouterFacade to register it in the router:

WampRouterFacade.RegisterPublisher(new ASamplePublisher());

Or

WampRouterFacade.RegisterPublisher("myRealm", new ASamplePublisher());

A new way of building a console

The C1 console has started seeing the first elements of our upcoming UI overhaul. To most users, this will be invisible, but for developers, the underlying mechanisms step into the modern world of JavaScript-based front end systems.

The UI for the Components and Search features are based on this new technologies.

Server access with RPCs and PubSub

Web application UIs usually access the server in one of two ways: Listening for messages, and fetching or putting data. The former is useful when you need to know when to update the view, and the latter is good for pushing changes to the server or fetching information from it. To that end, we have picked WAMP - Web Application Messaging Protocol - which provides both these functions. We use a lightweight implementation called Wampy on the client side, which means we have easy access to fluid updates and quick server calls.

A pony trick you can try out is to make updates to one of the component files in ~/App_Data/Components and see changes update instantly in the client Component view when you save your file. This is an example of our use of PubSub.

Client state and a single source of truth

Keeping a client state under control is often difficult, so we chose Redux to help us do it. This Facebook-sponsored library is becoming an industry standard with its sharply delineated update steps and clear activity tracking, as well as its high level of developer support and tooling. Redux works by dispatching actions to perform state updates. Whenever some aspect of the application changes, Redux has an action for that, and the effects can be tracked through it. This allows us to even ‘time travel’ by stepping back through the actions that were dispatched, showing us the state as it was before.

Markup, style and rendering

React, another industry-standard library, was our choice for rendering content. React provides the ability to build your user interface from components, each containing the code and markup they need to show a part of the UI. Styled-components adds to this the ability to enclose style information organically with these components, meaning whenever we render a component in a new place, it comes complete with all its needed pieces, improving reusability. React also interfaces well with Redux, mentioned above, making for clear, fast and easy state handling, re-rendering and updates of the browser view.

Modules and the management thereof

In development, we have taken up using the ECMAScript 2015 standard, allowing the use of promises, template strings, and above all modules. To this end, we’re employing JSPM, System.JS and Babel. System.JS is a module loader system based on the WHATWG System standard, and JSPM serves as a package manager - in the vein of npm or bower - that integrates with it. System.JS also provides our production build system, providing a single, lean bundle that loads quickly. We use Babel to transpile the codebase into something runnable by older browsers, which allows us to remain compatible with IE10 and 11.

Where we’re going

The first few pieces of the new console UI are meant to battle test our concepts, to see what might need adjustment and what works well. New UIs added to the console will be based on our tools and experiences from this, and meanwhile we will be working to build a full version of the new UI to replace the old one.

Placeholder Perspectives

Perspectives – like Content, Media, Data – have been opened up, so developers can now own the entire canvas, breaking out of the tree/command buttons/browser view. We’ve used this to provide a custom search perspective.

To create a new perspective with a custom path to manage the entire canvas, insert the following in ~/App_Data/Composite/Composite.config inside the element:

 <add name="SearchPerspective" tag="Search" label="Search" closeFolderIconName="Composite.Icons.generic-search" type="Composite.Plugins.Elements.ElementProviders.VirtualElementProvider.PlaceholderVirtualElement, Composite" path="${root}/console/index.html?pageId=search" IsTool="true">
  <Elements />
 </add>

The search feature is using this feature to create a custom canvas for search.

Focusing tree elements in the console

The console will locate, select and focus tree elements for URL anchors formatted like this:

~/Composte/top.aspx#FocusElement;{Serialized Entity Token for Element}

Here is a C# example on how to construct such a URL for a data element:

IData data = (the data you with to focus on);
var entityToken = data.GetDataEntityToken();
var serializedEntityToken = EntityTokenSerializer.Serialize(entityToken, true);
return UrlUtils.AdminRootPath + "/top.aspx#FocusElement;" + serializedEntityToken;

On the client such a link should be exposed like

Breaking changes

IApplicationStartupHandler interface changed in v6 – see 8bdd981#diff-6dcc0e5ce729322577094e941a56e44b for change. If you have implemented this interface, add an empty method ConfigureServices(IServiceCollection serviceCollection) to your implementation.

inspectocat-01

Issues fixed since last release

The following issues that were reported on previous releases has been fixed in 6.0:

  • #294: Not possible to install data for a static data type data with DataPackageFragmentInstaller if static data type is installed as well
  • #295: "Open Cph Master Pages" shows a compilation error on the home page – not the core I guess
  • #301: Can't overwrite some entities via package installer
  • #341: UrlActionToken is not handling when the provided url already has a querystring
  • #385: SEO Assistant: "In URL" check does not examine whole URL