📜 ⬆️ ⬇️

CleverStyle CMS module architecture

Recently, there were several articles on CMS written by their developers, so I decided to write.
I already wrote about CleverStyle CMS twice (the last time a year ago), and twice received a huge spectrum of criticism and a large bundle of comments from a different plan - thanks for everything, I spent time taking into account errors and correcting them.

The article is again for developers, in it I will try to convey in simple language the advantages of using this particular engine for developing the site, we will discuss one of the most important and functional components - the module.

A bit of history


When I read that the engine was written by one person for a year in his spare time - there is surprise and respect for the work done. Developing a three-year system with a tail of the year, you understand what a huge layer of work it is, but even more, most likely, ahead. In the majority, I came across the development of sites with unique functionality, and the engine was primarily written as a kernel to help the developer, despite the CMS in the title, which affects almost everything in it.

Module philosophy


A module is such a component that is responsible for rendering the page content or processing requests to the API. The module contains everything in itself and does not distribute its files to the corners of the system (therefore, the paths in the article will always be indicated relative to the root of the module folder), and if you need to put something back - after deletion, the module removes everything in order to leave nothing . This allows you to maintain system performance at the proper level over time.
')
The module can be extremely simple (a folder with a single index.html or index.php file), or as complex as needed: with support for several databases, file storage, dependencies, external API, multilingualism, and lots of various resources. With all this, you use only what you need, and no more than that (minimum template code).

Page hierarchy


There can be three main “parts” of the module (in any combination): administration, API, pages for the end user. All three parts can consist of a single index.php file (or optionally index.html in the case of a page for a user), or a hierarchy of pages, which is described in admin / index.json , api / index.json, and index.json . First and second level pages are supported, the rest are implemented by the developer, if necessary. It looks like this (example from the system module, administration):

{ "components" : [ "modules", "plugins", "blocks", "databases", "storages" ], "general" : [ "site_info", "system", "optimization", "appearance", "languages", "about_server" ], "users" : [ "general", "users", "groups", "permissions", "security", "mail" ] } 

Accordingly, the pages will look like this:


For processing paths, files of the same name are used, and they are executed in the following logical order:


It is convenient to use REST for the API, and in this connection, processing requests to the API is logical and conveniently different:


This way, requests can be sent using different HTTP methods to different files. If there is no suitable file with the method, but there is a file with another method, the logical 405 Method Not Allowed will arrive in response, and in the Allow header there will be a list of available methods.

It is also worth noting that when processing requests, the numerical parts of the path are ignored when parsing the page structure (this is logical, since the numbers are usually the id of some elements or the page number).

For manual manipulations, you can get the page path without the admin or api prefix in such a simple way:

 $Config = \cs\Config::instance(); $route = $Config->route; // ['components', 'modules']   ,  ['posts', 10]    API 

If desired, you can leave only index.php and deal with the route itself.

Resources (scripts, styles, images, fonts, web components)


The system distinguishes three main types of supported resources: scripts, styles, web components. All others are often tied to those mentioned. Located in the appropriate folders:


All files inside folders with the appropriate extension will be automatically picked up by the system in alphabetical order, files can be embedded in subfolders - this is not a problem. If you wish to exclude the processing of a folder by the kernel, it is enough to put in the desired place an empty file with the name ! Include (reads not to connect ).

Everything is simple, and most importantly effective. The fact is that when connecting resources, the kernel is able to resolve dependencies of components, that is, everything necessary for this component will be automatically picked up. Moreover, when setting options in the admin panel, the scripts, styles and web components will be minified, combined (if desired, you can additionally enable the so-called vulcanization for web components), and packed using gzip, while all operations are done atomically, there is each independent set of resources lying in a separate file, and when connecting minified compressed versions, dependency accounting is still preserved, and the files get unique prefixes so that when updating certain resource files, their compressed versions are renamed Enovyvalis and not hung in the browser cache causing problems. It is also worth noting that all relative references to images, fonts, nested style imports are embedded in the resulting css file minimizing the number of HTTP requests (until HTTP2 is ubiquitous or what they decide in the end), this is also true for the styles that are used in web components.

To include resources on certain pages, the includes / map.json file is used, which indicates on which pages which resources are needed. If the resource is not specified there, it will be connected on all pages of the site (just like that, not only in the current module). Here is one example:

 { "admin/Blogs" : [ "admin.css" ], "Blogs" : [ "general.css", "general.js" ] } 

includes / css and similar prefixes do not need to be specified, it is clear from the file extension where it lies.

Web components

In fact, they are worth mentioning a little separately. The system kernel comes bundled with the Polymer Platform (polyfill web components) and Polymer itself. Making them work with jQuery and some other libraries was not trivial at all, but the developers promptly accepted patches with fixes, so the Polymer and jQuery that are included are the git versions after the patch with the fix, since there was no release yet. Also, the engine of the jQuery.ready () patch engine to execute it after initializing all the web components.

To be brief, I don’t know at the moment where there is such a combination of patches that allows you to use web components in production right now (sounds a bit loud, but if you tried to use web components in real projects with jQuery, you understand what I’m talking about). ).

If you are not going to use CleverStyle CMS

Although the code is rather monolithic, it’s quite easy to take certain functionality and use it independently in your project (the MIT license allows it), as an example, combining and minifying styles and web components (with support for vulcanization).

Dependencies


Modules can depend on each other, and on plug-ins (another type of components, does not have its own page for display).

Each module can have not only a name, but also provide a specific functionality, and dependencies often point to the functionality, which allows you to select one of several modules that provide the desired functionality (and the engine will not allow you to install a second module that provides the same functionality). Also in the case of a specific module (for example, System, which is associated with the engine core), you can limit the desired version.

In conclusion, you can specify optional dependencies (which extend the functionality, but are not mandatory), as well as starting with which version of the module it can be upgraded to the current one (sometimes there are compatibility issues, and the update needs to be done gradually, or just update from outdated versions terminated to get rid of obsolete update scripts).

An example meta-file meta.json describing the details of the module and its dependencies:

 { "package" : "Blogs", "category" : "modules", "version" : "0.097.0+build-107", "description" : "Adds blogging functionality. Comments module is required for comments functionality, Plupload or similar module is required for files uploading functionality.", "author" : "Nazar Mokrynskyi", "website" : "cleverstyle.org/cms", "license" : "MIT License", "db_support" : [ "MySQLi" ], "provide" : "blogs", "require" : "System=>0.574", "optional" : [ "Comments", "Plupload", "TinyMCE", "file_upload", "editor", "simple_editor", "inline_editor" ], "multilingual" : [ "interface", "content" ], "languages" : [ "English", "", "Ń—" ] } 

What about the code?


All classes, traits, and similar things are in the cs or nested namespace.

The autoloader of classes is designed so that the class cs \ modules \ Blog \ Post will be searched in the Post.php file of the Blog module - simply and logically.

In order to subscribe to events in the system (for example, clearing the data of the module when it is deleted), triggers are used, and their announcement most often lies in the trigger.php file, which is called on each page regardless of the displayed module, and is used specifically for these purposes .

I do not write code examples, it will take a lot of space, and the article is more about the architecture and operation principle than about specific examples.

Summary


It is briefly about what the module is, how it is arranged, and why it is so. I hope it is clear from what was written that the system does a lot of obvious and useful things itself, but at the same time it allows the developer to change the behavior (as examples, the ability to manually handle page addresses and connection of resources was mentioned). This is not exhaustive information, some things are missed for simple, as they cover narrower usage scenarios, but if there is interest, I will continue to describe more specific details.

Plans


Recently, the engine has been significantly refactored, and the first release in three years, version 1.0, is near.
In this regard, I would like to get more feedback.
At the moment, I am most worried about the lack of tests (several existing ones are not considered). In recent changes, steps have been taken towards improving code testability, but something still doesn’t add up with tests, I will be very happy to receive help in this area (I don’t have to offer DI).

That's all, I hope I managed to arouse interest to look at all this live. The code is covered with phpDoc comments almost everywhere, which is very well taken up by the IDE, as well as an example you can watch the finished modules in the engine repository.
The project page on GitHub , there is also a wiki with documentation (mainly on Backend).

Demo


In the comments they asked for a demo, here it is (Nginx + HHVM):
http://demo.cscms.org
admin: 1111

Source: https://habr.com/ru/post/240789/


All Articles