This article covers how to implement custom blocks for Piranha. If you want to read more about the standard blocks included, please refer to Blocks.
Blocks are the content pieces that the Administrators can use when designing the main content body. They main difference between Fields
and Blocks
is that blocks are intended to be more visual in their presentation than fields to give editors a clear understanding of the content they are creating.
When naming your components, whether it's fields, blocks or block groups you should stay clear of the names already used by the internal components of the Piranha manager.
block-group-horizontal
block-group-vertical
block-group
folder-item
generic-block
page-item
pagecopy-item
post-archive
region
sitemap-item
After all of the block fields have been initialized additional initialization can be performed on the block itself for loading or preparing data based on the values of the fields.
// Synchronous Init method
void Init(...);
// Asyncronous Init method
Task Init(...);
Initializes the block for client use. This is called when the block is loaded and is used for loading additional resources.
The method supports Dependency Injection which means that any service registered in the DI Container can be added as an parameter. This is useful when loading custom data from your application.
// Synchronous Init method
void InitManager(...);
// Asyncronous Init method
Task InitManager(...);
Initializes the block when it's loaded from within the manager. Just like the regular Init
method it supports Dependency Injection so that you can use any service registered in the DI Container.
A block is in essence a class that inherits from the abstract base class Piranha.Extend.Block
. A block can contain one or several fields, it can actually contain data that is not a field as well, but only properties that derive from Piranha.Extend.IField
will be saved in the database. All blocks need to be marked with the BlockType
attribute.
As an example, let's take a look at how the Quote block is implemented.
using Piranha.Extend;
using Piranha.Extend.Fields;
[BlockType(Name = "Quote", Category = "Content",
Icon = "fas fa-quote-right", Component = "quote-block")]
public class QuoteBlock : Block
{
/// <summary>
/// Gets/sets the text body.
/// </summary>
public TextField Body { get; set; }
}
All of the standard blocks included in Piranha has their own Vue
component rendering them in the manager, but sometime you might just want to add a block that is not that visual and just gives the editor a form with the available fields in it.
By omitting the Component
property of the BlockTypeAttribute
Piranha will automatically handle the rendering of the block the same way it would with a region. Let's for example say we wanted an Author block that we could add at the bottom of our Posts.
using Piranha.Extend;
using Piranha.Extend.Fields;
[BlockType(Name = "Author", Category = "Content", Icon = "fas fa-use")]
public class AuthorBlock : Block
{
public StringField Name { get; set; }
public ImageField Image { get; set; }
public HtmlField Description { get; set; }
}
The edit model for the block used for the manager interface is different in the way that instead of sending the actual AuthorBlock
as the model it sends an array of the fields together with meta-data for rendering. Please note that it's completely possible to use this edit model for your custom block components as well, by setting the IsGeneric
property together with a Component
.
[BlockType(Name = "Author", Category = "Content", Icon = "fas fa-use",
Component = "author-block", IsGeneric = true)]
In order to register your block you need to mark it with the BlockType
attribute like in the above example. This has the following properties.
public string Name { get; set; }
This is the name that is shown in the manager interface when editing.
public string Category { get; set; }
The name of category the Block will be grouped under in the manager interface.
public string Icon { get; set; }
Css class for rendering the Block icon in the manager interface. The manager interface uses the free icon package from font awesome.
public bool IsUnlisted { get; set; }
If you're developing a block that is supposed to be used within a Block Group you should set this to true
. This will prevent it from being shown elsewhere when adding Blocks.
public string Component { get; set; }
The name of the global Vue
component that should be used when rendering the block in the manager.
All available blocks has to be registered in the singleton Piranha.App
after the app has been initialized.
Piranha.App.Init(api);
// Register custom blocks
Piranha.App.Blocks.Register<MyCustomBlock>();
The manager interface is based on Vue.js
and assumes that all blocks are registered as global components. As these components are written in Javascript
you need to register your custom resources in the manager. For more information about this, please refer to Resources in the section Manager Extensions.
A note on block templates
The root element of the block template needs to have the class
block-body
for the manager interface to be able to collapse it in the edit view.
Let's again take a look at how the component for the Quote block is implemented:
/*global
piranha
*/
Vue.component("quote-block", {
props: ["uid", "toolbar", "model"],
methods: {
onBlur: function (e) {
this.model.body.value = e.target.innerText;
}
},
computed: {
isEmpty: function () {
return piranha.utils.isEmptyText(this.model.body.value);
}
},
template:
"<div class='block-body' :class='{ empty: isEmpty }'>" +
" <i class='fas fa-quote-right quote'></i>" +
" <p class='lead' contenteditable='true' spellcheck='false' " +
" v-html='model.body.value' v-on:blur='onBlur'></p>" +
"</div>"
});
All field components get three properties from the main edit views in the manager interface.
A globally unique id that can be used in the component when rendering more complex views. This field is generated at runtime and will be different each time for every field.
The id of the container that toolbars should be positioned inside, for example when using the Tiny MCE
editor.
The model value from the block instance.