Cms Component And Plugin Development

1

How to start working with us.

Geolance is a marketplace for remote freelancers who are looking for freelance work from clients around the world.

2

Create an account.

Simply sign up on our website and get started finding the perfect project or posting your own request!

3

Fill in the forms with information about you.

Let us know what type of professional you're looking for, your budget, deadline, and any other requirements you may have!

4

Choose a professional or post your own request.

Browse through our online directory of professionals and find someone who matches your needs perfectly, or post your own request if you don't see anything that fits!

Is there an easy way to develop a CMS plugin? So let's first start with the actual meaning of plugin states: “A plugin is a piece of software that acts as an add-on (i.e. a set of code to achieve some specific component functionality) to a web browser and gives the browser additional functionality.” When a program supports plugins, it allows more features through the user interface, or more content to be displayed. The CMS doesn't come with plugin support by default; however, there is a third-party library that you can use to create your plugin for this platform.

The simplest plugin

We can create is a simple module with its layout and content blocks. We will demonstrate plugin development on our example of the bazaar plugin.

Do you need to create a CMS plugin?

If so, there is an easy way to do it. Let's first start with the actual meaning of plugin states: “A plugin is a piece of software that acts as an add-on (i.e. a set of code to achieve some specific functionality) to a web browser and gives the browser additional functionality.” When a program supports plugins, it allows more features through the user interface, or more content to be displayed. The CMS doesn't come with plugin support by default; however, there is a third-party library that you can use to create your plugin for this platform.

So if you are looking for something simple then Geolance will help you out in creating your own custom CMS Plugin! You won't have any problems with installation because all our products are 100% compatible with WordPress and Joomla! platforms which mean they're very easy to install on these systems without any hassle or errors at all! We offer 24/7 customer support services so feel free to contact us anytime if you have any questions about our products or their usage! Our team will gladly answer them ASAP! And don't forget we provide 30 days money-back guarantee period so buy now and enjoy using our product right away without worrying about anything else! It's time for change - let Geolance help you out today!

Bazaar Plugin

The first step to creating modules for October is getting started and understanding how it works, and that's what we're going to show you now. This tutorial will guide you through creating your first module using XML layouts and views, as well as adding custom fields to your data model so you can save rich information about your modules. The examples in this guide will be applied to my site Kinsta Pizza, so any similarities or differences between their site and my examples are purely coincidental!

Kinsta Pizza

So we have an example website that we're going to turn into a CMS and we want to add a module. The first thing you should do when developing any kind of extension for October is head over to the documentation and look at all the layouts and views, tags, and filters it has to offer. I've divided up the parts of this series so they'll be easier for us to digest, but in reality, these things will be woven together in one large string.

Master Layout

Inside our theme plugin directory structure, there's a file called master-layout.xml which is an XML layout that contains everything around your content blocks, including all navigation elements such as menus, breadcrumbs, and sitemaps: Including the master-layout.xml layout in your plugin module is as easy as creating a file with that name, including all the necessary layout XML markup, and committing it to your plugin directory. In this example, we have a website where there can be multiple types of users who want access to different content on our site. Each of those user groups will have different permissions for viewing certain articles.

This is a plan view of what our CMS will do:

The first group of users has full privileges on every page and article available on our frontend website. They have free reign over what they can read or edit if they so wish. The second group only has limited access- they cannot create new accounts or log in using their profiles, but depending on how important these people are to us (i.e., site admins), we may give them more privileges on the backend administration panel where they can edit existing accounts and content. The third group is a special case- these users have no access to our frontend website, but they are allowed to view all public function posts from the backend admin panel of our CMS.

Before we move forward, you must understand some core concepts about how October works: There is a main loop that starts in index.php which calls plugin_preprocess_HOOK() for each hook based on their priority level, beginning with the lowest priority first and going up from there. Each callback returns an array containing instructions for creating context variables, or throwing exceptions if something wrong along the way- this is the core API to creating your plugin. Once all the instructions have been processed, our main loop can finally iterate over those context variables created in that process and run the appropriate theme hook, as requested by a URL or other dynamic variable. October then begins to look for a file named page-HOOK.php, where HOOK is whatever was given as a request parameter- if not given, it's usually page. What happens if there isn't one? Usually, an error will be displayed to the user or a 404 page will be returned, but this can vary depending on your backend settings and configurations. If we're dealing with posts instead of pages, then we favor post-HOOK.php, where HOOK denotes either "post" or "page", depending on what kind of content we're dealing with. If a filename doesn't exist, then the main loop will check to see if there's a file called search.php. You can use this to process custom queries or create dynamic pagination-type behavior in your theme. Keep an eye out for the "Models" section below where I'll explain how you can utilize October's built-in Page and Post data models for methods to get information about your website from their respective objects. Now you should have a basic idea of how things work in general in an October CMS-based module- let's put that knowledge into practice!

How To Build Your Module

Our first order of business is going to be creating our master-layout.xml file in our theme directory, which will let us control the general look-and-feel of our website, including any navigational elements that we'd like to include on every page of our site. We'll start with some HTML5 markup (for SEO purposes) and then move into October's own "super-charged" PHP code to get everything working together properly:

<html class="no-js" lang="en">

Next, we want to get all of the navigation HTML built so that October can hook onto it for use inside its main loop. Navigation is usually an important part of most websites these days, so why not be prepared? Keep in mind that this entire block HTML must be contained within the <body> tags- October does not play nice with others!

<nav id="main-nav" role="navigation">

Lastly, we need to close out our master-layout.xml file so that the main loop knows where it ends:

</html>

Now comes the fun part of building our CMS! We have two core configuration files that every module must have to work properly with October's new event system-- they are listed below. The very first thing you'll want to do is rename your plugin registration class from PluginTutorial to ModuleTutorial, which will tell October that this is a new module instead of just another plugin. You can also copy/paste everything inside your original PHP file over into a new PHP file, which is what you should do for this tutorial.

Once the plugin class has been renamed to ModuleTutorial instead of PluginTutorial, it's time to set up our .inc file so that October knows how to load it within the CMS backend! This can be done by adding your module's name as a sub-string of October's plugin_dir path, minus the filename extension (.txt)

The Future Of Modules In October CMS 3.0+

For those of you who have been using October for a while now, you may be familiar with the fact that modules were deprecated in favor of plugins since October 1.5- so why bring them back? In response to the ever-growing community feedback and code contributions from talented developers all over the world, we've decided to go ahead and add modules back into our development roadmap for version 3.0+. While I can't promise a specific date at this moment, it will be a part of the next major release once October CMS 3.0 gets closer to being finalized- keep an eye on our [website] for more information as it becomes available!

In addition, due to popular demand from the community, we also want to add support for plugin templates into October CMS 3.0+, which will allow you to create a "base" PHP file that other plugins can extend and inherit from to share common code between one another. Furthermore, developers who have been working with WordPress plugin API will feel right at home with this new feature- so be sure to look out for this additional functionality added in a future version of October CMS!

Storing configuration data

In modules has also been a highly-requested feature, which will allow you to keep track of custom site information without having to make additional changes within your config.php file or worry about tooling it up with October's new configuration data system!

This is only the beginning when it comes to what this new version of October CMS is capable of doing- no other PHP CMS on the market today comes even close to offering the same flexibility and modularity that our system provides. For those of you who are interested in diving deeper into what functionality October offers out-of-the-box for developers, be sure to check out some of the great articles written by my colleague Ben over at [his blog], where he continues exploring many more exciting new features and possibilities with October CMS.

Have trouble setting up your first module? Be sure to post on our forums for help, we're happy to walk you through it- or if you'd like to collaborate (or write an article of your own!) send me a PM here on the official October blog!

Managing backend controllers, forms, and lists

When it comes to working with fields & forms, October CMS makes the process of creating backend controllers, using Twig templates for rendering field types, and displaying lists of records easy.

The 1st step in working with form fields is to create a new class that will handle the creation of your specific field type. This controller must extend October's FieldController class- an example file might look like this:

Now that we have our custom field controller set up let's make use of it! Within our template (or our module .inc file), we can declare markup like what you see below within our page loop:

Now when this record is viewed by the user in the backend dashboard, they'll be able to click on "Add a new" to add a new record of this type. By default, October automatically calls your controller action to create the form used for entering the data.

In addition to creating fields from within our Twig templates, we can also display lists of records by placing a call to get_records() in our view file. An example of what this might look like is demonstrated below:

There are many different ways that you can structure your controllers, forms, and lists- this just happens to be an example that uses some simple logic to show how flexible October CMS truly is! The possibilities are endless with regards to what you're able to do when working with field types and custom CMS pages- if you're looking for more examples on what is possible, please feel free to reach out on our forums!

Programmatical configuration options

All of October's default field types are also configurable programmatically. For example, let's take a look at how you can configure the SliderField type within your module file. This is certainly one of the more complex field types that exist within October CMS- so this may seem a bit overwhelming at first but I promise you by the time we've finished reading through this article you'll have a strong understanding of all it has to offer you as a developer! Let's take a look:

For those developers who have worked with WordPress before, everything should be pretty straightforward here! The Slider object now uses Bootstrap 4+, which means that many of the classes are no longer prefixed with jquery- but our old descriptors still work well.

Now that we have our hook in place within our module, let's make use of it! Here is an example of how this field might look when used inside a Twig template:

And there you have it! Now any time your administrators use the backend dashboard to add new records to your site they'll be able to upload images, define their title & content, select specific categories, etc. The possibilities are nearly endless when it comes to what can be configured- October CMS makes all of this very easy while also giving developers full control over what gets stored & displayed between field types.

OctoberCMS offers many additional opportunities for extension out-of-the-box beyond both field types and list views. For example, OctoberCMS also provides a very simple way to add your custom buttons & modules to the backend dashboard that can be used by administrators while creating content or managing site settings.

October CMS makes it easy to create plugin hooks for adding functionality to various parts of the system. For example, if you're looking for a way to add additional fields related to a specific module - a developer would simply need to create a new field controller class, place their markup within their template file, and use get_instance() from our FieldPluginManager class. Once the plugin is enabled it will automatically register itself with October's event dispatcher to handle any incoming requests from Twig templates.

Make no mistake, OctoberCMS is not the only CMS system out on the market that offers developer hooks for extending functionality- but it does deserve special attention because of how easy it makes this process for developers who are already working within the Symfony & Twig ecosystem. You don't have to work against an existing theme or template engine if you don't want to - yet still have plenty of opportunities available to you as a developer if you need control over your markup. To learn more about what types of plugin hooks are available, please visit our documentation.

Managing localization

October CMS makes it easy to manage localization for your site using the familiar Symfony translation service. For example, here is an example of how you might use the translation service inside one of your modules:

If you're working within a controller, form class, etc- please feel free to use the get_translator() helper function to retrieve & store translations! It's that easy! Remember - if you're not comfortable with passing around translator objects everywhere then simply create a new subclass that implements October\TranslatableInterface and store translator objects within your objects instead. Once this is done you can access any instance variables directly from Twig templates by iterating over the object & calling translation(). This will allow you to dynamically change any variables from your controller or model directly from Twig!

In addition to working with the Symfony translation service, OctoberCMS also includes a simple way for developers to add their custom translation files. Once these files are in place they can be easily loaded into the translation service by creating a new TranslationLoader class that extends October\Translatable\TranslationLoader and then simply loading it in your module's bootstrap file:

function __construct ( string $locale ): self { parent :: __construct (); if ( null !== $locale ) { include_once ( 'path/to/my/file-en.php' ); } } October will automatically load any language files located inside of this directory & use them when you attempt to translate strings through the helper function i18n().

When it comes to localization, you can choose whatever method makes sense for your development process- but hopefully, this provides an easy way for developers who are already familiar with Symfony to have full control over their translation files.

Managing backend forms

OctoberCMS doesn't force you to work with any specific form framework- but it does include a simple FormHelper class that makes creating forms easier. For example, the FormHelper would automatically display labels & validation errors for fields within your form based on the configuration specified for each field. It will also use your field type information to determine if you need to show radio buttons or select boxes when generating options for dropdown fields.

The FormHelper also provides an easy way to dynamically create pages that can be used by administrators while managing content & settings within October - all without writing any PHP! This strategy can be helpful if your site includes lots of different kinds of content types and many hierarchical relationships between them because it means fewer Drupal-like menus to deal with. Just remember that your module must contain the appropriate fields/relationships to make this work- but fortunately, OctoberCMS will automatically create & delete all of these for you during installation or upgrade so there's no need to worry about missing anything!

Versioning. Plugin history is stored in a custom table.

October CMS automatically uses Git to store all changes for your site so that you can roll back to previous versions as needed (or even restore from other sites running the same version of OctoberCMS if you like). Because of this, we must provide an easy way for administrators to review the history of updates made to their modules and plugins- and October does this using Drupal's issue queue system which is enabled by default (though de-activated unless it detects certain contributed modules such as issues or views installed). For example:

On administration pages within October, each plugin & module displays its specific changelog information. This information includes both the description & type of update along with a list of any user-visible changes to your site. In addition, you can click the link for any specific version to see a complete list of all updates made in that specific release along with links to view & revert any previous versions if necessary.

The October User Guide goes into more detail on how this works and provides a lot of good tips for managing sites over time- so be sure to check it out!

File system metadata is stored in a custom table. Content-type names are also stored in a custom table. This means no Field API or Entity Metadata API support out of the box, but plugins may provide their APIs instead.

Initializing a new plugin

OctoberCMS uses a plugin system that resembles the Plugin API in Drupal 7. Each plugin resides within its subdirectory under your module's directory (e.g. /modules/mymodule/plugins/) and overrides several different hooks to initialize, alter, or remove database items as necessary (e.g. node types, taxonomies, fields).

This approach allows you to use any of the strategies already available for managing code dependencies between plugins & your module- but your new plugin must make sure it performs all required tasks before allowing other modules to access its functionality!

When it comes to creating plugins for OctoberCMS- there are two primary APIs that should be used whenever possible: $this->getModule() should always be used when accessing anything within your module's directory. This will ensure that your plugin can't accidentally act on the wrong site if two different modules define the same hook! $this->getPlugin() should be used whenever possible. It provides a standardized interface for creating plugins so that they will behave in the same way no matter which framework or CMS is being used.

Navigation menus

OctoberCMS allows you to create new menu items using one of two methods: either via a small configuration form within the main module administration page (or by clicking on the "Add Section" link) or programmatically with a separate API. The former method is easy for site administrators- but requires that you add your new menu items manually and then remember to update it whenever you release any changes to your plugin!

The second approach is more rigorous- but will provide many more benefits such as Plugins can be enabled & disabled without touching any menus. Menus can contain links to specific versions of plugins which will automatically change when they are updated. Site administrators won't be able to accidentally delete pages that your module needs if your pages exist in a separate menu.

These approaches may seem like they are in conflict with each other- but this is actually not the case. Whenever you create links using Drupal's menus functions (either via hook_menu() or theme() functions) you can pass in an additional parameter that defines the weight of the page. For example, if your module creates content types than might be displayed on several different pages- you could add code similar to this to your form/API for creating new content:

$page['admin'] = array( '#type' => 'link', '#title' => $this->t('Admin'), '#href' => $this->moduleBaseUrl . '/plugins/example/pages/'. $entity->id(), '#weight' => 1, );

This will create a link to the Example Plugin's pages/node/{node_id} page- but also specify it as having a weight of 1 so that Drupal will place this menu item in the navigation after all other items with lower weights. You can do this for any links you want to be placed at the bottom of any specific menu hierarchy when using hook_menu()!

A few caveats are required here- due mostly to how field storage works within Drupal 7: Content type machine names must match their display name for them to be displayed on your new page. This means that if your content type's machine name is "example_type" then its display name needs to be "Example Type" to be displayed on this page. The weight of the page created by your plugin is always overridden for specific form element IDs! This means that if you want to place a link upper or lower in any hierarchy- you need to pass its ID via the '#weight' property instead of simply using 1 as above.

Theming Menus

OctoberCMS allows site administrators to them different menus on their site- either via the main module administration screen or programmatically with a separate API. Although this method does provide many benefits (like making it easy for users without access to edit code to rearrange menu items)- some caveats must be considered when creating, updating & deleting menu items programmatically versus through the user interface.

The menus API will let you create, update and delete menu items as usual- but there are a few very important caveats to keep in mind: Menu placement is configured as an array of weights (similar to how Drupal 7 calculates page weights) which must be placed in the "weight_prc" field of any menu item before it will appear within another menu! The weight calculation order can seem counterintuitive because lower numbers always go first. This means that if you want something like your plugin's pages to appear below all other regular pages (such as blog posts) then you need to place its weight behind those pages by giving it a negative default value such as -1000. Unfortunately, this means that for your module's pages to appear after all other pages on the site- their weight must be set to 999. If you are using the Module Administration page to create your menu items- make sure that they are initially placed at a weight of 0 so that Drupal will calculate their actual weights during hook_menu() processing time!

Dependency definitions

OctoberCMS allows you to specify dependencies for your plugins- however, these do not behave in the same way as Drupal 7. Instead of specifying what modules should be made active before your plugin is loaded (which does work), October uses a system of "soft" dependencies. This means that while it will load all required modules before loading any other plugins, there are no guarantees that other plugins dependent on other modules won't try to use them until they are ready! For this reason- if possible, it is best practice to move functions that need certain modules/plugin capabilities into their separate classes so that they can be initialized whenever the necessary dependencies have been loaded by the site administrator.

This issue comes down to how module developers think about their code and is one of the disadvantages of not using a "version" API since Drupal 7. If you think about your module from a technical point of view, rather than from an end user's perspective- it may make more sense to add all of your API hooks where they are needed instead of on hook_init() or other similar hooks. In some cases, the API will still require developer intervention when deciding when to initialize plugin functions while others can be resolved by creating new API methods that behave in a more object-oriented fashion. This is something that developers should keep in mind when writing new modules for Drupal 8 as well!

Writing Controllers/Routing

OctoberCMS controller classes look almost identical to standard Drupal 7 ones- but their implementation is a little different. In Drupal 7, you need to use the drupal_container() helper function to make any page callable from your controllers- but in OctoberCMS, this approach doesn't work unless you are using standard functionality provided by the CMS itself.

In OctoberCMS, as with other Symfony-based platforms- all routes live within the application's routing configuration file instead of being defined within each controller class itself. Unlike Drupal 7 however- this means that if you want to create a new route then you must either delete or comment out any default routes that exist before that point! Since most applications will have a small subset of routes compared to others- it may be beneficial for developers to just copy an existing module's routing component class file defines and modifies it instead of writing one from scratch.

As with standard Drupal 7 modules, OctoberCMS controller classes should be placed in the app/code/local folder within your optional component partials directory structure (wherever that happens to be).

Best Practices For Module Developers

Over the last few years, I've seen several different developers working on similar projects- but each time they created a new plugin type for their users; things tended to become messy. This is because the more code you write- the more places you can create bugs or introduce security vulnerabilities! There are plenty of examples out there of how simply checking data at multiple locations within your codebase can multiply these issues exponentially. To help avoid this problem, developers should try and avoid writing new plugin types unless necessary. The typical use case for creating a new plugin type is if you want to provide different behavior for a different group of users.

When it comes to managing data, Drupal 8's configuration system provides several options; but the single most important thing that module developers can do is try and avoid storing any stateful information within the database- especially not in critical sections like URLs! This means that even though all OctoberCMS modules extend from ContainerBuilder (which allows them to define their services) they should also contain an Init() method that accepts a service container as one of its parameters- just like with Drupal 7! Doing so will result in cleaner dependency injection which makes debugging easier since there are no awkward inline service calls within controllers and routes.

The most important thing that developers can do is to use Composer! If you're not using it already then you should check out the documentation on how to get started. The more people that start using it, the more we will see Drupal 8 modules solving issues in a standard way; which will hopefully result in less "Drupal-specific" code moving forward and easier interoperability with other parts of the community!

Plugin namespaces should be used whenever possible (the PSR-4 standard).

Developers should always implement plugin types as interfaces instead of classes. If you're providing the same functionality to multiple groups of users then you can always use traits or extend multiple interfaces; but if it's only for one group of users then by definition, it isn't a "plugin type" because plugins are meant to solve different problems! This is also where I feel that using Composer will help since someone who writes an OctoberCMS module may not know about Drupal 8 core development at all but they do know how to write Symfony so by implementing your interface as a PSR-4 autoloadable class, they will instantly understand how to resolve your plugin type to use it.

Use the annotation-based plugin discovery provided by ContainerBuilder instead of writing your custom code! If you take a look at Drupal 8's core modules then you will notice that they all use annotations for wiring up their plugins. There is no reason why OctoberCMS should be any different, especially since this allows us to avoid using any custom code within our modules which would result in more overhead and less reliability when upgrading or maintaining them later on! To make things even easier, we can write generic helper classes (or Symfony bundles) which extract these annotations and convert them into service definitions; allowing developers to annotate their controller methods with @Route(), @Controller(), and other annotations without needing to know about how to configure ContainerBuilder!

Developers should write their modules instead of modifying core or contrib modules. This is because it will result in better code reuse and easier interoperability with other parts of the community- so no more "Drupal specific" classes which don't work elsewhere! Also, by writing your module rather than modifying an existing one then you can release them on Packagist (so that other people can use them) if they are generic enough to be useful outside of OctoberCMS. If you're interested in learning how to write your own Drupal 8 module then I would recommend checking out this article.

Convention over configuration helps keep things simple but there are always exceptions... The problem is that when creating a plugin type, it is convention over configuration but trying to mirror the exact API for a Drupal 7 contrib module will result in a lot of unnecessary duplication since this API was designed before Symfony's standardized service container API! I feel that instead of using one annotation per method then we should use a single annotation that can accept an array of tags and resolve them into multiple annotations where necessary. This would simplify things within our modules and solve most edge cases without resorting to writing custom code!

The above-mentioned helper classes/Symfony bundles could also define their annotation(s) if they wanted to provide even more flexibility. For example, if you look at how OctoberCMS bootstrap their application by php file defining services within bootstrap/system.services.yml then you will notice that we can use any Symfony service within our modules, and it all results in autoloading and resolving the right object at runtime! This means that we could define an annotation that looks for a "service" tag and other annotations which look for their specific tags (i.e. "controller", "router", etc).

We can configure ContainerBuilder to resolve automatic dependencies within classes so long as they are tagged as such... For example, if your controller required a logger because it needs to write debug messages before passing control back to OctoberCMS then you would simply add a "@Logger()" annotation above the class definition/methods which need access to this dependency:

The above code will result in ContainerBuilder automatically resolving an instance of Monolog\Logger as a constructor argument when instantiating the object you're trying to resolve. This works because Monolog\Logger implements Psr\Container\ContainerInterface which means that it is visible from within the container- allowing ContainerBuilder to resolve it from this interface. Because we have tagged our class as being a "controller", and all of its methods (which require logging) with "@Logger()" - then ContainerBuilder will know about these dependencies and make them available for use!

No more cluttering up the global namespace... Go ahead and look at any Drupal 7 contrib module, and you will notice that there are many classes/methods which have the same component class file name! For example, try looking at how the Comment module defines its "comment" entity type... This results in a lot of classes/methods which all have the same name- most likely you will want to use one or two of these within your modules but are forced into using them all! When using Symfony components then it is recommended that they be given namespaces that coincide with the specific component. Go ahead and look at OctoberCMS' source code for their HtmlEditor component in an external parameter editor where you'll notice that any class definitions will prefix each class with Oc\HtmlEditor\.


Geolance is an on-demand staffing platform

We're a new kind of staffing platform that simplifies the process for professionals to find work. No more tedious job boards, we've done all the hard work for you.


Geolance is a search engine that combines the power of machine learning with human input to make finding information easier.

© Copyright 2022 Geolance. All rights reserved.