Welcome to SiteSupra’s Documentation!¶
About SiteSupra:¶
Why Another CMS¶
SiteSupra combines a powerful web framework and an elegant CMS to allow developing and managing websites easily. Developers will enjoy SiteSupra while building simple websites or large, feature-rich web apps. Website editors will love SiteSupra because of advanced CMS features packed into unique elegant and simple interface. While there are many great web frameworks and good CMS products separately there are few products that offer those qualities together. SiteSupra is on the mission to bring the best of both worlds in a single product.
Is SiteSupra the right tool for my website?¶
SiteSupra is based on many years of research and development into web frameworks, CMS usability, and now is able to offer solutions for almost any task:

Corporate and enterprise websites
Build full-scale multilingual, multi-country websites with ability to maintain a single set of features and localized content, control page localization status, setup content editors access rights and approval workflow.

Promo websites
Develop and manage landing pages or one-pagers easily by setting up HTML into a page template and dragging and dropping required page widgets quickly.

Web applications
SiteSupra offers a powerful development framework and an array of internal managers that will allow you developing even very sophisticated web applications easily.
What will be my benefits if I will use SiteSupra?¶
Beautiful CMS
SiteSupra will add a great value to your website by making website editors fall in love with the CMS. Intuitive, elegant, beautifully-designed, it will allow your clients doing sophisticated website maintenance tasks easily thanks to Word-like visual interface that covers not only text editing, but website structure, page widget functionality and back-office applications management.

Industry-standard development approach
There’s no learning curve developing with SiteSupra if you are familiar with Symfony and Doctrine components. SiteSupra goal is to allow you developing advanced application using well-known PHP components instantly.

Reusable drag-and-drop page widgets
Code a page block once and drag-and-drop it anywhere on the site and reuse later in other websites. SiteSupra ships with expanding selection of widgets to allow you creating and managing website handily.

Rich CMS and back-office features
Enjoy advanced CMS features as page versions, scheduled publish and undo history all packed in a beautiful UI accessible without any learning by regular business users. In case of an enterprise environment set up access rights to internal applications or website pages and maintain role-based approval workflow.

Extensive modification capacity
Implement your own logic where needed through custom routes and controllers. SiteSupra has no limitations whether you decide to build a responsive themes or not - it’s only a matter of how you code HTML.

What else will I get?¶
Customisation
We will be glad to help you developing custom SiteSupra extensions, blocks and modules to enhance your website and bring it to your requirements in the most effective way.
Integration
Shall you require help developing SiteSupra-based website or web application to your unique requirements we will be happy to assist through all development cycle starting with front-end development to SiteSupra blocks and modules.
Support
We offer help and assistance for your SiteSupra development tasks as well for system installation and maintenance.
SiteSupra at a Glance¶
Installation:¶
Software Requirements¶
Web Server¶
SiteSupra can run under any web server that supports PHP. We prefer Apache or nginx. Although, SiteSupra is not fully tested on Windows platform yet. However, from technical perspective we see no reasons why it may not run well on a WAMP-powered server.
Additional Modules¶
As many other CMS and frameworks SiteSupra rewrites URLs to hide reference to index.php file and make URLs human readable.
To enable URL rewriting Apache requires mod_rewrite
and nginx requires ngx_http_rewrite_module
.
PHP¶
PHP version 5.4 or higher is required.
Database¶
Communications with the database layer is handled by Doctrine ORM. However, only MySQL database server is supported at the moment. Install MySQL version 5.1 or later.
Installing SiteSupra¶
SiteSupra is based on Symfony components and manages dependencies with composer. The installation process is pretty straightforward. However, you may consider checking Software Requirements first.
For first time users we would recommend to start with cloning SiteSupra Demo site. If you would like to contribute feel free to clone SiteSupra core.
Installing and Configuring SiteSupra Demo Site¶
Cloning SiteSupra Demo Site¶
SiteSupra source code is hosted at github. Clone or checkout SiteSupra into your work folder.
$ git clone https://github.com/sitesupra/sitesupra-demo.git sitesupra
Configuring SiteSupra Demo Site¶
To configure SiteSupra on your computer follow the next steps :
- Run
composer update
to update dependencies; - Create an empty database;
- Copy
supra/config.yml.example
tosupra/config.yml
and configure database connection; - Create tables by running
php supra/cli.php doctrine:schema:create
- Setup assets
php supra/cli.php assets:publish
; - Load fixtures
php supra/cli.php sample:fixtures:load storage/fixtures
;
That’s all! Now configure web server of your choice (see chapter Configuring Web Server below) and enjoy SiteSupra.
Installing and Configuring SiteSupra Core¶
If you would like to contribute to SiteSupra project consider to checkout SiteSupra core. Please note SiteSupra core doesn’t contain any web site or blocks. You may need to add and configure them by yourself.
Cloning SiteSupra Core¶
SiteSupra source code is hosted at github. Clone or checkout SiteSupra into your work folder.
$ git clone https://github.com/sitesupra/sitesupra.git sitesupra
Configuring SiteSupra¶
To configure SiteSupra on your computer follow the next steps :
- Run
composer update
to update dependencies; - Set up web server permissions for
storage
folder (you can stick to plain old chmod 777 or use ACL approach as Symfony does); - Create an empty database;
- Copy
supra/config.yml.example
tosupra/config.yml
and provide database credentials; - Create tables by running
php supra/cli.php doctrine:schema:create
; - Load initial fixtures by running
supra/cli.php supra:bootstrap
; - Publish assets with
supra/cli.php assets:publish
.
All done! Now just point your web server of choice to web
directory in SiteSupra project’s root.
Configuring Web Server¶
Apache¶
Point DocumentRoot
to the web
directory in SiteSupra project’s root.
Allow to follow symlinks and configure rewrite rules as listed below.
Options +FollowSymlinks
RewriteEngine On
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME} -f
RewriteRule ^ - [L,NS]
RewriteRule ^.*$ /index.php$0 [L,NS]
Rewrite rules for .htaccess
are provided in .htaccess
file that comes along with SiteSupra source code.
nginx¶
Point root
to the web
directory in SiteSupra project’s root.
Configure rewrite rules as shown below:
location / {
try_files $uri $uri/ /index.php;
}
Quick Start:¶
SiteSupra is open source PHP framework and extremely powerful web site builder bundled with user-friendly CMS.
Developer’s Guide:¶
Standard Packages¶
Core¶
Not much of a package but SiteSupra itself. It contains core classes and definitions of SiteSupra framework.
Cms¶
SiteSupra CMS by itself. This package contains controllers, routing, entities and frontend assets that are used throughout CMS backend.
CmsAuthentication¶
SiteSupra authentication layer is separated in CmsAuthentication bundle. It sets up ``SecurityContext11 and handles backend user authentication. It is not suitable for front-end user authentication.
DebugBar¶
Integrates PHP DebugBar into SiteSupra allowing you to monitor requests, their time lines, SQL queries, events, and much more. This package is active only when SiteSupra runs in debug mode.
Framework¶
This package combines and integrates everything together. It sets up Doctrine, EntityAudit, Twig, and all other components required to run SiteSupra. It also registers most of the commands that you can access from Command Line Interface.
HTTP Kernel and Bootstrap Process¶
Note
SiteSupra does not use HttpKernel.
We had eight versions of SiteSupra before moving to Symfony. Unfortuantely, no all of the Symfony concepts do suit our needs well.
Our implementation is still a bit incomplete, especially RequestStack
and forwarding, so expect refactoring soon.
SiteSupra uses plain HttpFoundation
component (see documentation of HttpFoundation
and HTTP requests in symfony
for more information).
SiteSupra mimics Symfony behaviour as close as possible - a Request
object is created, every controller returns
Response
(read more on controllers here).
Basically, request processing happens in the following order:
Web server hits entry point
webroot/index.php
;SiteSupra builds container,
buildContainer()
is called;SiteSupra boots,
boot()
is called. Two events are fired at that moment -Supra::EVENT_BOOT_START
andSupra::EVENT_BOOT_END
. Methodboot()
is called for every registered package, allowing early initialization;Request handling starts by calling
handleRequest($request)
. This method loadsSupra\Core\Kernel\HttpKernel
and callshandle()
. Request handling by HttpKernel traverses through stages below:KernelEvent::REQUEST
is thrown. Request processing stops when there is at least one event configured for response toRequestResponseEvent
. Request object is returned;- Kernel tries to resolve controller and action by checking
_controller
and_action
parameters of requestAttributeBag
; if found, controller is instantiated, action is called andHttpKernel
expectsController
to return aResponse
. This is used when forwarding requests or instantiating requests without a route; - If controller is not resolved yet kernel loads routing and tries to find current route.
AttributeBag
is overwritten by one created from route configuration and controller is actually executed.KernelEvent::CONTROLLER_START
andKernelEvent::CONTROLLER_END
events are fired, and any listener can override response duringKernelEvent::CONTROLLER_END
event. If any exception (generic orResourceNotFoundException
) is thrown request processing moves to exceptions processing (see stage 5 below); - Kernel fires final
KernelEvent::RESPONSE
event and returns resultingResponse
object. Exception is thrown when the object is not an instance ofResponse
; - If any exception is caught during request processing, then the exception is processed in the following way:
- Kernel fires
KernelEvent::EXCEPTION
event (again, if any listener providesResponse
inside this exception event, then the response is returned); - If exception is instance of
ResourceNotFoundException
, a special event is fired -KernelEvent::ERROR404
, which allows, for example, on-the-fly compilation of assets. Finally, if no response is available after the event is processed, the exception is re-thrown (in debug mode) orexception404Action
of default exception controller (stored in container underexception.controller
key) is called; - If there’s still and exception and no response, an exception is re-thrown (in debug mode) or
exception500Action
of default exception controller (stored in container underexception.controller
key) is called.
Resulting response is sent to the browser (by calling
send()
method)). Any unhandled exception is caught by Debug component;SiteSupra shuts down firing
Supra::EVENT_SHUTDOWN_START
andSupra::EVENT_SHUTDOWN_END
events and callsshutdown()
method of all registered packages, thus allowing some late cleanup.
As you can see, this process is pretty simple and transparent. Last thing to note must be a SupraJsonResponse
class
that is used throughout CMS backend for passing messages, warnings, and errors to frontend in a common way.
See the class source code to learn more on messaging process.
Dependency Injection¶
Core Concepts¶
SiteSupra Dependency Injection layer or DI in short is based on Pimple. If you’re curious about what DI is you can read the following Wiki articles:
We also recommend read Symfony’s documentation about basic DI principles.
SiteSupra main container class is Supra\Core\DependencyInjection\Container
.
It extends Pimple’s Container, implements SiteSupra’s
Supra\Core\DependencyInjection\ContainerInterface
, implements some hard-coded methods (remember, we’re a CMS, and
not a full-stack framework, some items like Doctrine or Cache are always present), and provides parameters
handling.
Note
It’s almost certain that we will drop hard-coded getters later and build container definition on-the-fly in the same way Symfony does.
Everything is simple, right? Last but not least to note would be that any object implementing
Supra\Core\DependencyInjection\Container\ContainerAware
will be provided with Container
on instantiation
(calling setContainer).
Container Building Process¶
Note
The code will be refactored soon.
SiteSupra core class (Supra\Core\Supra
, extending Supra\Core\DependencyInjection\ContainerBuilder
) builds and
returns Container
object during call to buildContainer
. This is done in the following steps:
- Pre-setting some basic variables and objects (like directories, HttpFoundation objects, Command Line Interface, and so on);
- Injecting packages (allowing to expose their basic configuration);
- Building configuration (there the configuration is being validated, default values set, container parameters are substituted, and so on);
- Finishing configuration when packages can override or extend config values of other packages;
- Firing
Supra::EVENT_CONTAINER_BUILD_COMPLETE
event.
Package Integration and Two-pass Container Building¶
First of all, a package needs to be registered. This is done by overriding registerPackages
in SupraApplication
class (located in supra/SupraApplication.php
). This method simply returns array of package instances, like in the example below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | <?php
use Supra\Core\Supra;
class SupraApplication extends Supra
{
protected function registerPackages()
{
return array(
new \Supra\Package\Framework\SupraPackageFramework(),
new \Supra\Package\Cms\SupraPackageCms(),
new \Supra\Package\CmsAuthentication\SupraPackageCmsAuthentication(),
new \Supra\Package\DebugBar\SupraPackageDebugBar(),
new \Sample\SamplePackage()
);
}
}
|
Each package must extend Supra\Core\Package\AbstractSupraPackage
.
You can override any of the following methods to alter SiteSupra behavior:
boot()
method will be called during SiteSupra boot, see HTTP Kernel and Bootstrap Process;inject(ContainerInterface $container)
method will be called during Container building in package injection phase (see above);finish(ContainerInterface $container)
method will be called finishing Container build after the configuration is processed;shutdown()
method will be called during SiteSupra shutdown, see HTTP Kernel and Bootstrap Process.
Package Configuration¶
As mentioned above package configuration may occur in two phases - injection phase and finishing phase. Let’s look at both of them starting from inject()
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <?php
public function inject(ContainerInterface $container)
{
$this->loadConfiguration($container);
$container->getConsole()->add(new DoFooBarCommand());
$container[$this->name.'.some_service_name'] = function (ContainerInterface $container) {
return new SomeService();
};
if ($container->getParameter('debug')) {
//prepare some extended logging, for example
}
}
|
The most important call would be $this->loadConfiguration()
(line 5). This method loads configuration file (by
default it is Resources/config/config.yml
). To load your own configuration pass the file name to the method as a second parameter .
This call parses config file, processes the configuration using package configuration definition (more on that on Symfony configuration component article), and stores the values for further processing.
Later you can access already defined services (see line line 7
, which though is not a very good approach since it
instantiates the service), add your own service definitions (lines 9-11
), and access container parameters (line 13
).
Each package has it’s own configuration definition. Concrete configuration object is created during call to getConfiguration()
method. By default, if there is a package named SupraPackageFooBar
in namespace Com\Package\FooBar
, then the method will search
for configuration definition SupraPackageFooBarConfiguration
in namespace Com\Package\FooBar\Configuration
. Of
course, you can always override you package’s method getConfiguration()
and implement your own logic.
The configuration class should extend Supra\Core\Configuration\AbstractPackageConfiguration
and implement
ConfigurationInterface
. This forces you to implement function getConfigTreeBuilder()
, returning instance of
Symfony\Component\Config\Definition\Builder\TreeBuilder
. If you’re curious about what is a TreeBuilder
and how
exactly the configuration is being defined, please read Defining a Hierarchy of Configuration Values Using the TreeBuilder
on official Symfony documentation web site. Let’s take configuration of SupraPackageFrameworkConfiguration
as an example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | <?php
class SupraPackageFrameworkConfiguration extends AbstractPackageConfiguration implements ConfigurationInterface
{
/**
* Generates the configuration tree builder.
*
* @return \Symfony\Component\Config\Definition\Builder\TreeBuilder The tree builder
*/
public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder();
$treeBuilder->root('framework')
->children()
->append($this->getAuditDefinition())
//some other definitions are skipped for illustrative purposes
->append($this->getServicesDefinition())
->end();
return $treeBuilder;
}
public function getAuditDefinition()
{
$definition = new ArrayNodeDefinition('doctrine_audit');
$definition->children()
->arrayNode('entities')
->prototype('scalar')->end()
->end()
->arrayNode('ignore_columns')
->prototype('scalar')->end()
->end()
->end();
return $definition;
}
}
|
Root node (line 14
) must match your package name. The rest of configuration definition is standard for
Symfony-based applications (lines 24-38
), except for call of ->append($this->getServicesDefinition())
, which is
inherited from AbstractPackageConfiguration
and enables parsing of services
section of your configuration file.
Package configuration files are simple yml files as shown below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | services:
supra.framework.session_storage_native:
class: \Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage
parameters: [[], "@supra.framework.session_handler_doctrine"]
supra.framework.session_handler_doctrine:
class: \Supra\Package\Framework\Session\DoctrineSessionHandler
#some config parts are skipped for illustrative purposes
doctrine:
#some config parts are skipped for illustrative purposes
credentials:
hostname: localhost
username: root
password: ~
charset: utf8
database: supra9
connections:
default:
host: %framework.doctrine.credentials.hostname%
user: %framework.doctrine.credentials.username%
password: %framework.doctrine.credentials.password%
dbname: %framework.doctrine.credentials.database%
charset: %framework.doctrine.credentials.charset%
driver: mysql
event_manager: public
entity_managers:
public:
connection: default
event_manager: public
default_entity_manager: public
default_connection: default
doctrine_audit:
entities: []
ignore_columns:
- created_at
- updated_at
- lock
|
Lines 1-6
define services. Key is service ID, ‘class’ defines class name and ‘parameters’ section enables setter injection
(note that you can inject other services referenced with ‘@’ as shown in line 4
). Setter injection is not yet supported.
First level keys will become container parameters prefixed with package name. In the example above,
container parameters are ‘framework.doctrine’ and ‘framework.doctrine_audit’, and you can call something like
$container->getParameter('framework.doctrine_audit')['entities']
later in your code.
You may also reference any parameter using percent notation (%parameter.name%
). In the example above, line 18
references value from line 11
, possibly overridden by another package or main SiteSupra’s config.yml
.
After calling inject()
method of all packages, container builder merges configuration values (also replacing /
referencing parameters), and starts calling finish()
method of all packages, in load order. You finish()
method
can look like so:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <?php
public function finish(ContainerInterface $container)
{
//extend some other package service
$container->extend('some.other.service', function ($originalService, $container) {
$originalService->callSomeMethod();
return new SomeWrapper($originalService);
};
$doctrineConfig = $container->getParameter('framework.doctrine');
//processed configuration from example above. with merged parameters and optionally overridden by main config.yml
$connectionDetails = $doctrineConfig['connections']['default'];
}
|
So, summing up:
- You define your configuration in
inject()
method; - Container processes your configuration and merges it;
- You retrieve processed values from container in
finish()
method and define your services; - Resulting container is available throughout SiteSupra.
Main SiteSupra Configuration File (config.yml)¶
Default SiteSupra configuration file supra/config.yml.example
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | cms:
active_theme: default
framework:
doctrine:
credentials:
hostname: localhost
username: root
password: ~
charset: utf8
database: supra9
cms_authentication:
users:
shared_connection: null
user_providers:
doctrine:
supra.authentication.user_provider.public:
em: public
entity: CmsAuthentication:User
provider_chain: [ doctrine.entity_managers.public ]
|
Top-level keys correspond to package names, corresponding values are deep-merged with default values resolved in injection phase. Here you can see how default ‘doctrine.configuration’ values are merged with defaults from SupraPackageFramework; any part of configuration can be overridden.
Container Parameter Handling, Parameter Substitution¶
Parameters are SiteSupra-specific extension to Pimple. Basically they represent simple key-value storage (with all
the getters and setters. Refer to Supra\Core\DependencyInjection\Container
for more information. However, some
of the methods are worth to be noted separately:
replaceParameters
searches array of data and replaces all parameters enclosed in percent signs (like %foo.bar%) to their respective values;replaceParametersScalar
replaces all parameters enclosed in percent signs (like %foo.bar%) to their respective values in a scalar variable (string);getParameter
threads dots inside parameter name as internal array keys (thus allowing you to call$container->getParameter('foo.bar.buz.example')
instead of$container->getParameter('foo.bar')['buz']['example']
).
Standard Container Parameters¶
Standard container parameters that can help you in development process are listed below.
Directories¶
There is a number of container parameters reflecting SiteSupra directory structure:
directories.project_root
for project root folder (withcomposer.json
and other core files);directories.supra_root
for directory whereSupra.php
andconfig.yml
reside;directories.storage
for storage folder;directories.cache
for cache folder (inside storage root);directories.web
for webroot (this is where SiteSupra entry point,index.php
, is);directories.public
for asset root,Resources\public
folders of every package are symlinked there.
Environments and Debugging¶
Some parameters are affected by current development settings:
environment
shows current environment - currently on ofcli
,prod
, ordev
;debug
shows current debug state - eithertrue
orfalse
.
Service Definition¶
Adding ->addServiceDefinition()
to package configuration will allow that package to define services.
Service definition has to reside under section services
in configuration file.
A simple service definition contains service id and class name:
1 2 3 | services:
locale.manager:
class: \Supra\Core\Locale\LocaleManager
|
you can provide constructor arguments as an array:
1 2 3 4 | services:
supra.doctrine.event_subscriber.table_name_prefixer:
class: \Supra\Core\Doctrine\Subscriber\TableNamePrefixer
parameters: ['su_', '']
|
or even use container parameters as arguments:
1 2 3 4 | services:
supra.framework.session_storage_native:
class: \Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage
parameters: [[], "@supra.framework.session_handler_doctrine"]
|
Unfortunately, caller injections are not possible with SiteSupra yet. But still you can use common Pimple’s approach
during inject()
or finish()
:
1 2 3 4 5 6 7 8 9 10 11 | <?php
$container['some.service'] = function ($container) use ($dependency1, $dependency2) {
$service = new SomeService($dependency1);
$service->setDependency2($dependency2);
$service->intialize();
return $service;
};
|
Command Line Interface¶
General Concepts¶
SiteSupra uses Symfony console component for console operations. Boris is used for REPL shell.
The main console executable is supra/cli.php
. Running it without parameters (like php supra/cli.php
) will give
you a list of all available commands.
By default CLI loads with cli
environment and debugging enabled.
CLI Events¶
You can use events in your console application; you can use Symfony\Component\Console\Event\ConsoleEvent
or
Supra\Core\Event\ConsoleEvent
(providing methods getData/setData
) for such cases. Currently, there’s only one
CLI-only event in SiteSupra, Supra\Package\Framework\Event\FrameworkConsoleEvent::ASSETS_PUBLISH
, provided by
SupraPackageFramework
, that is fired running supra/cli.php assets:publish
. This event can be used to copy some
non-standard assets to your webroot (to simplify things, you can implement Supra\Core\Event\ConsoleEventListenerInterface
for your listener and use webRoot
and webRootPublic
keys of $assetsPublishEvent->getData()
to determine copy
locations).
Core Commands¶
SiteSupra does not provide any built-in commands itself; on a plain installation you have help
and list
commands,
that are standard for Symfony Console. Only two argumens (--env
, or -e
, for environment, defaulting to cli
,
and --debug
, defaulting to true), are present.
Package Commands¶
SupraPackageCms¶
This package does not provide any CLI commands.
SupraPackageCmsAuthentication¶
This package provides the following commands:
- groups:add
- groups:list
- groups:remove
- groups:update
- users:add
- users:list
- users:remove
- users:update
these are self-explanatory and documented inline (try, for example, supra/cli.php help groups:update
- you’ll like
it!).
SupraPackageDebugBar¶
This package does not provide any CLI commands.
SupraPackageFramework¶
assets:publish¶
Each package can contain assets (in Resources\public
directory). Publishing assets means that this directory
will be symlinked (sorry, no hard copy option yet) into web/public/PACKAGE_NAME
. Supra cleans up package name, so
assets from SupraPackageCms
will we symlinked into web/public/cms
, enabling you to access them from the frontend.
cache:clear¶
Clears SiteSupra cache. Cleans up all cache entries, if no argument is provided, or a particular segment. For more information SiteSupra cache see Cache chapter of this manual.
cache:list¶
Shows info about SiteSupra cache and segments:
+----------------+-------+-------+
| Directory | Size | Files |
+----------------+-------+-------+
| assets_404 | 0.13M | 14 |
| cms_assets | 1.80M | 2 |
| combo | 1.06M | 27 |
| config | 0.04M | 8 |
| doctrine | 0.04M | 5 |
| frontend_cache | 0.02M | 1 |
| twig | 0.02M | 7 |
+----------------+-------+-------+
container:dump¶
Dumps information about container parameters and services:
Container parameters:
+-----------------------------------------+-------------------------------------------------+
| Parameter | Value |
+-----------------------------------------+-------------------------------------------------+
| directories.supra_root | /home/developer/www/composer-test/supra |
| directories.project_root | /home/developer/www/composer-test |
| directories.storage | /home/developer/www/composer-test/storage |
| directories.cache | /home/developer/www/composer-test/storage/cache |
| directories.web | /home/developer/www/composer-test/web |
| directories.public | /home/developer/www/composer-test/web/public |
| environment | cli |
| debug | TRUE |
| cms.media_library_known_file_extensions | array |
| results truncated... | |
+-----------------------------------------+-------------------------------------------------+
Container services:
+----------------------------------------------------------------------+
| ID |
+----------------------------------------------------------------------+
| application |
| config.universal_loader |
| routing.router |
| kernel.kernel |
| exception.controller |
| http.request |
| cache.driver |
| cache.cache |
| results truncated... |
+----------------------------------------------------------------------+
container:packages:list¶
Lists enabled Packages (showing both package name and class).
Doctrine-specific commands¶
The following commands are directly mapped the their Doctrine counterparts:
- doctrine:cache-clear:metadata
- doctrine:cache-clear:query
- doctrine:cache-clear:result
- doctrine:convert-encodings
- doctrine:generate:proxies
- doctrine:schema:create
- doctrine:schema:drop
- doctrine:schema:update
Please refer to Doctrine documentation should you need help on that.
framework:routing:list¶
Displays all registered routes, patterns, resulting controller, and whether the route is exported to fronted:
Defined routes:
+--------------------------------------+-------------------------------------------------------+------------------------------------+----------+
| Name | Pattern | Controller | Frontend |
+--------------------------------------+-------------------------------------------------------+------------------------------------+----------+
| framework_combo | /_framework_internal/combo/{paths} | Framework:Combo:combo | No |
| framework_routes | /_framework_internal/routes | Framework:Routing:export | No |
| cms_dashboard | /backend | Cms:Dashboard:index | Yes |
| cms_dashboard_applications_list | /backend/applications-list | Cms:Dashboard:applicationsList | Yes |
| results truncated... | | | |
+--------------------------------------+-------------------------------------------------------+------------------------------------+----------+
supra:bootstrap¶
Creates default user (username admin, password admin) and loads some initial templates so you can access backend and create new pages.
supra:shell¶
Launches REPL shell, with pre-set $container
and $application
variables. You can play around with some SiteSupra
code without having debug controllers:
[1] supra> $container->getRouter()->generate('cms_dashboard');
// '/backend'
[2] supra>
supra:nested_set:check¶
Warning
Warning! There is a risk of losing your data. Please don’t forget to backup your database prior to running the command.
SiteSupra uses custom NestedSet implementation. It’s quite stable and almost bulletproof, although may need in repair from time to time.
Writing your own Command¶
See Writing Your Own Command for a complete reference.
Database (Doctrine 2) and EntityAudit¶
SiteSupra uses Doctrine ORM for database operations (a nice introduction to Doctrine is available at Symfony docs).
Doctrine Configuration¶
In most cases you do not need to configure anything but set up database credentials in supra/config.yml
. Commonly,
you have to override framework.doctrine
parameter only:
1 2 3 4 5 6 7 8 | framework:
doctrine:
credentials:
hostname: localhost
username: root
password: ~
charset: utf8
database: supra9
|
If you need auditing for your project issues, you will have to add them to framework.doctrine_audit.entities, along with default values, like shown below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | framework:
doctrine_audit:
entities:
Supra\Package\Cms\Entity\Abstraction\Localization
Supra\Package\Cms\Entity\Abstraction\AbstractPage
Supra\Package\Cms\Entity\Page
Supra\Package\Cms\Entity\GroupPage
Supra\Package\Cms\Entity\Template
Supra\Package\Cms\Entity\PageLocalization
Supra\Package\Cms\Entity\PageLocalizationPath
Supra\Package\Cms\Entity\TemplateLocalization
Supra\Package\Cms\Entity\Abstraction\Block
Supra\Package\Cms\Entity\Abstraction\PlaceHolder
Supra\Package\Cms\Entity\PagePlaceHolder
Supra\Package\Cms\Entity\TemplatePlaceHolder
Supra\Package\Cms\Entity\PageBlock
Supra\Package\Cms\Entity\TemplateBlock
Supra\Package\Cms\Entity\BlockProperty
Supra\Package\Cms\Entity\BlockPropertyMetadata
Supra\Package\Cms\Entity\ReferencedElement\LinkReferencedElement
Supra\Package\Cms\Entity\ReferencedElement\ImageReferencedElement
Supra\Package\Cms\Entity\ReferencedElement\ReferencedElementAbstract
Package\MyCustomPackage\Entity\CustomAuditedEntity
|
However, this will override previous values and possibly mess other packages. Thus, a better approach would we adding them during package injection phase:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <?php
public function inject(ContainerInterface $container)
{
$frameworkConfiguration = $container->getApplication()->getConfigurationSection('framework');
//add audited entities
$frameworkConfiguration['doctrine_audit']['entities'] = array_merge(
$frameworkConfiguration['doctrine_audit']['entities'],
array(
'Package\MyCustomPackage\Entity\CustomAuditedEntity',
)
);
$container->getApplication()->setConfigurationSection('framework', $frameworkConfiguration);
}
|
Basically, you can override any package configuration by using getConfigurationSection()
and setConfigurationSection()
.
CLI Commands¶
Please refer to SiteSupra Command Line Interface for more information. All Doctrine commands known by Symfony are available via SiteSupra CLI.
Standard event listeners¶
By default SupraPackageFramework
defines and initializes Doctrine using it’s own config.yml
:
1 2 3 4 5 6 7 8 | doctrine:
event_managers:
public:
subscribers:
- supra.doctrine.event_subscriber.table_name_prefixer
- supra.doctrine.event_subscriber.detached_discriminator_handler
- supra.doctrine.event_subscriber.nested_set_listener
- supra.doctrine.event_subscriber.timestampable
|
subscribers
array references the following classes, also defined in config.yml
, services
section:
1 2 3 4 5 6 7 8 9 10 | services:
supra.doctrine.event_subscriber.table_name_prefixer:
class: \Supra\Core\Doctrine\Subscriber\TableNamePrefixer
parameters: ['su_', '']
supra.doctrine.event_subscriber.detached_discriminator_handler:
class: \Supra\Core\Doctrine\Subscriber\DetachedDiscriminatorHandler
supra.doctrine.event_subscriber.timestampable:
class: \Supra\Package\Framework\Doctrine\Subscriber\TimestampableListener
supra.doctrine.event_subscriber.nested_set_listener:
class: \Supra\Core\NestedSet\Listener\NestedSetListener
|
They serve for the following purposes:
TableNamePrefixer
adds prefixes to SiteSupra database tables (currently not-changeable, defaultsu_
);DetachedDiscriminatorHandler
is internal SiteSupra feature. Quite probably we’ll tune it up and document later;TimestampableListener
listens to changes in entities implementingSupra\Package\Cms\Entity\Abstraction\TimestampableInterface
, callssetCreationTime()
andsetModificationTime
if needed;NestedSetListener
handles changes in SiteSupra’s NestedSet implementation.
If some other package must add other event subscribers, this can be done by overriding SupraPackageFramework
configuration
like it is done in SupraPackageCms
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | <?php
public function inject(ContainerInterface $container)
{
//setting up doctrine
$frameworkConfiguration = $container->getApplication()->getConfigurationSection('framework');
$frameworkConfiguration['doctrine']['event_managers']['public'] = array_merge_recursive(
$frameworkConfiguration['doctrine']['event_managers']['public'],
array(
'subscribers' => array(
'supra.cms.file_storage.event_subscriber.file_path_change_listener',
'supra.cms.pages.event_subscriber.page_path_generator',
'supra.cms.pages.event_subscriber.image_size_creator_listener',
)
)
);
$container->getApplication()->setConfigurationSection('framework', $frameworkConfiguration);
}
|
You can freely alter any configurations during package injection phase (since actual entity managers and subscribers are set up only in finishing phase).
Internal Entities and SupraId¶
Doctrine, by itself, is a very sensitive system. For example, it does not like when you are trying to persist entity that already has id or restore entities with pre-set foreign keys. However, SiteSupra’s versioning, based on EntityAudit, does exactly that! Therefore, we are using:
- A custom type, called
supraId20
(use@Column(type="supraId20")
). That’s currently just a 20 characters length string; - A custom base entity
Supra\Package\Cms\Entity\Abstraction\Entity
, which is a@MappedSuperclass
, and provides base methods likeregenerateId
,__clone
etc.
SiteSupra Id contains twenty symbols and looks like “018dusx9903wosockckg”, where:
- First 9 symbols are reserved for timestamp converted to base36. Tto be honest, we do not use standard unix timestamps. Our base date is 16 Dec 2011, 11:33:05. That’s the day when supraId was introduced;
- Next two symbols are reserved for internal counter of entities persisted in current session;
- Trailing 9 symbols are just a randomly generates suffix.
Note
This is expected to be refactored to @GeneratedValue(strategy=”CUSTOM”) and @CustomIdGenerator(class=”...”) soon
EntityAudit and Versioning¶
SiteSupra’s versioning is almost completely based on EntityAudit library. For more inforamtion refer to respective documentation. We do not override anything there, so this should be enough if you need to implement auditing of your project entities.
Routing¶
Loading Routes¶
SiteSupra routing is heavily based on Symfony’s routing component and uses very similar syntax. However, there are some
minor differences. For example, you have to load all your routing files manually in your package’s inject()
method:
1 2 3 4 5 | <?php
$container->getRouter()->loadConfiguration(
$container->getApplication()->locateConfigFile($this, 'routes.yml')
);
|
Function locateConfigFile
searches routes.yml
in your package’s Resources\config
directory.
Common Example¶
Let’s take a look at some routing definition examples.
The most simple would be SupraPackageFramework
main routing file:
1 2 3 4 5 6 7 8 9 10 11 12 13 | configuration:
prefix: ~
routes:
framework_combo:
pattern: /_framework_internal/combo/{paths}
controller: Framework:Combo:combo
requirements:
paths: .+
defaults:
paths: ~
framework_routes:
pattern: /_framework_internal/routes
controller: Framework:Routing:export
|
configuration
section at line 1
defines global prefix
and defaults
(default parameter values)
keys. prefix
must be explicitly defined even with default ~
value.
routes
section, starting from line 3
, defines actual routes. Each route may contain the following fields:
pattern
defines actual URI that will trigger the route;controller
specifies the controller (in the example above,Framework:Combo:combo
resolves intoSupraPackageFramework
→ComboController
→comboAction
(just like Symfony does!);filters
defines Symfony route filters;requirements
here you can specify per-parameter regex requirements;defaults
provides default parameter values;options
at the moment supportsfrontend
key only.
where only pattern
and controller
are required.
Tip
Due to the fact SiteSupra’s routing is based on Symfony Routing component, everything written in Symfony documentation applies to SiteSupra as well - we did not reinvent the wheel here.
Container Parameters and JavaScript¶
Let’s see a bit more complicated example from SupraPackageCms
:
1 2 3 4 5 6 7 8 | configuration:
prefix: ~
routes:
cms_dashboard:
pattern: %cms.prefix%
controller: Cms:Dashboard:index
options:
frontend: true
|
First of all, you can use container parameters (in %container.parameter.name%
form) in your route pattern
.
Secondly, you can provide frontend: true
option and use in Javascript like this:
1 | Supra.Url.generate('cms_dashboard');
|
Controllers¶
What are Controllers?¶
SiteSupra, following Symfony, is a request-response framework. It boots up in a request context, applies business logic to the data provided, creates a response object, and returns result to the client. In general, requests are mapped through the routing engine to Controllers, which are PHP classes implementing the logic behind SiteSupra.
Each Package has it’s own set of controllers and routing rules. There is no limit on number of controllers and actions your web application may have. You may build and group your logic in the way you like.
Controller, Naming, and Routes¶
Controllers are package - specific classes, extending Supra\Core\Controller\Controller
. They must reside in
Controller
namespace (like Supra\Package\Framework\Controller
). Each controller method should accept Request
and return a Response
(more on these classes in Symfony HttpFoundation documentation).
SiteSupra expect Controller
suffix in each Controller name (like FooBarController
or UserController
) and
Action
suffix in each action method (like deleteAction
or listAction
).
Routes use short syntax (Package:Controller:action
). For example, Framework:Routing:export
resolves
to SupraPackageFramework
(namespace Supra\Package\Framework\Controller
), class RoutingController
, and method
exportAction
(thus, Supra\Package\Framework\Controller\RoutingController::exportAction
).
Each action is expected to return a Symfony\Component\HttpFoundation\Response
object. Returning scalar value or not
returning any value at all (which is equivalent to return NULL
) will cause HttpKernel
to throw an exception.
1 2 3 4 5 6 7 8 | <?php
public function doStuffAction()
{
// doing stuff here
return new Response('<html><head><title>Hello!</title</head><body>Hello!</body></html>');
}
|
Of course, all types of Symfony responses are supported (like JsonResponse
, or RedirectResponse
). For example, a redirect to another URL could be called in the following way:
1 2 3 4 5 6 7 8 | <?php
public function doStuffWithRedirectAction()
{
// doing stuff here
return new RedirectResponse('http://example.com/');
}
|
Base Controller Class¶
SiteSupra provides base class for your controllers, which is Supra\Core\Controller\Controller
. First of all, it is
ContainerAware, so you can always access DI Container via $this->container
(container instance is set by Http kernel when controller is instantiated).
It’s provided with $package
and $application
properties, which are set to current package class name (like
Supra\Package\Framework\SupraPackageFramework
), and frontend application name (like cms_authentication
).
Other handy methods of Controller
class are listed below:
renderResponse
renders twig template and returns aResponse
object;render
renders twig template and returns result as a string;setApplication
overrides current application for ApplicationManager (see SiteSupra Concepts for more details);getUser
returns current user or returns null if there’s no security context, or if the security context does not contain valid token, or if the token does not contain valid user. See Security for more information;getPackage
returns current package name (without namespace prefix, likeFramework
);checkActionPermission
is a security-oriented stub that is not yet ported from legacy SiteSupra code to Symfony’s ACL.
Exceptions¶
Controllers do not provide any custom exception handling. Instead, any exception is caught by Http kernel.
Depending on current debug settings either trace is written or a special controller
is being called (invoking exception500Action
).
A special case is Symfony\Component\Routing\Exception\ResourceNotFoundException
, which is forwarded to exception404Action
of exception controller,
thus allowing you to show pretty 404 page in production mode.
Page routing¶
Cache¶
SiteSupra does not provide any public caching interfaces. However, it has an internal cache system that you can use and benefit on.
Cache Class¶
Cache class (Supra\Core\Cache\Cache
, accessible by cache.cache
service key or $container->getCache()
method) exposes the following methods:
fetch($prefix, $key, $default, $timestamp, $ttl, $respectDebug)
fetches and probably stores value in the cache (if current value was not found). The parameters are explained below:$prefix
and$key
parameters are quite self-explanatory;$default
value can be a scalar or a callable (checked by is_callable), which is being called only on cache miss;$timestamp
is a last modification timestamp allowing you to refresh the cache. It’s very handy for storing and combining assets;$ttl
is a time to live value;$respectDebug
turns cache off development environment when set totrue
. Basically, the cache will still work, but the values in there will overwritten with every page request.
store($prefix, $key, $value, $timestamp, $ttl)
directly proxies data to storage driver. If$value
is callable, it is invoked;delete($prefix, $key)
deletes value from driver;clear($prefix)
deletes all values for particular prefix;
We’ve tried to make the cache as simple as possible, so the common usage pattern with callback looks like the following:
1 2 3 4 5 6 7 8 | <?php
$result = $container->getCache()->fetch('internal', 'value1', function() use ($container) {
//this code will be called only on cache miss
$value = $container['some.service']->doSomeHeavyOperation();
return $value;
}, filemtime('file.txt'), 3600);
|
Doctrine Wrapper¶
Some libraries can use Doctrine Cache as a cache layer.
SiteSupra provides wrapper over it’s native cache, DoctrineCacheWrapper
, which you can use in the following way:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <?php
use Supra\Core\Cache\DoctrineCacheWrapper;
$wrapper = new DoctrineCacheWrapper();
$wrapper->setContainer($container);
$wrapper->setPrefix('supra_native_prefix');
$wrapper->setSuffix('doctrine_cache_suffix');
// now you can use $wrapper anywhere in your code
// where instance of CacheProvider is required.
// Supra will respect ``prefix`` that you have set,
// and Doctrine will use ``suffix``
|
Cache Drivers and Current Implementation¶
SiteSupra has only one cache driver implemented at the moment. The driver called File
stores cached data under storage/cache
folder.
The driver creates separate sub-folders for each prefix
you define.
There are some cache-specific CLI commands available for cache data management.
More cache drivers to come soon. You can always write your own driver just by implementing Supra\Core\Cache\Driver\DriverInterface
interface.
Blocks and Editables¶
Templating¶
SiteSupra uses Twig template engine. Nothing unusual, just few things worth to mention:
- There’s one Twig extension, called
CmsExtension
, that provides functions for preparing CMS JS/CSS assets, and another one, calledPageExtension
, that manages CMS-specific functions and tags (more on that later); - Views reside in package’s
Resource\view
folders, just like in Symfony; - You can reference template inside a package and use shorthand syntax like
{% extends 'SamplePackage:layouts/base.html.twig' %}
.
To reference and render a template you can always access Twig environment by calling $container->getTemplating()
or call renderResponse
from your controller (package defaults to current package here):
1 2 3 4 5 6 | <?php
public function indexAction()
{
return $this->renderResponse('index.html.twig');
}
|
You can register a custom extension during package injection;
1 2 3 4 5 6 | <?php
public function inject(ContainerInterface $container)
{
$container->getTemplating()->addExtension(new PageExtension());
}
|
PageExtension¶
To explain PageExtension
, we need to discuss two things: PageExecutionContext
and BlockExecutionContext
(make
sure you’ve read SiteSupra Concepts first).
Both of these objects are simple container classes for objects defining current block or page being executed. Both of them
contain a Request
object (plain Request
for Blocks and PageRequest
for Pages) and a Controller
object
(BlockController
and PageController
accordingly).
Note
As other SiteSupra internal features, this is likely to change in the future.
This extension defines one filter, called decorate
that works with internal HtmlTag
instances, and a few functions
listed below:
collection()
, andlist()
resolve property to a collection, example would be writing{% for item in collection(property('image', 'image')) %}
;set()
resolves property to a set;property()
fetches single property from a Block or Page;isPropertyEmpty()
checks if property value is empty;placeHolder()
defines a placeholder (see Blocks and Editables and SiteSupra Concepts for more information).
Every function in PageExtension
is based on a custom node_class
.
This facilitates the process of dynamic creation of block properties when template is parsed.
BlockPropertyNodeVisitor
creates block properties on-the-fly.
Development and Production¶
Security¶
SiteSupra does not provide any kind of authentication for user part of CMS; it only provides authentication and user
management layer for CMS part, decoupled in separate SupraPackageCmsAuthentication
(more on standard packages in
corresponding section). So, the documentation below applies only to CMS part, but you can
always add authentication to your website following this cookbook article.
SiteSupra security layer is based on Symfony security component.
Security Concepts and Configuration¶
Security is blindly bound to cms.prefix
container parameter and secures all URLs beginning that.
URL mapping happens in CmsAuthenticationRequestListener
.
When visitor is not authorized yet, then the visitor is being redirected to CMS login page.
Note
We are likely to extend security layer to both backend and frontend - stay tuned!
The second listener, CmsAuthenticationResponseListener
, ensures that current Token
is stored in user session
under the key defined by cms_authentication.session.storage_key
parameter.
SiteSupra dispatches AuthController::TOKEN_CHANGE_EVENT
every time a new token is stored in the session.
Voters and ACL’s are enabled, but not used yet.
Default security configuration is stored in Supra\Package\CmsAuthentication\Resources\config\config.yml
.
Apart from paths and services, it defines a shared user source (explained below), sets up user providers (bound to
CmsAuthentication:User
entity), both combined into provider chain, and sets SupraBlowfishEncoder
as a default
password encoder.
CLI Commands¶
SiteSupra provides some basic user management commands (for adding and removing backend user groups) allowing you to manage users event if the database is empty. refer to Command Line Interface for more details.
User Source and User Provider¶
By default SiteSupra uses Supra\Package\CmsAuthentication\Entity\User
as base user entity and corresponding repository
(which already implements Symfony\Component\Security\Core\User\UserProviderInterface
) as a user source. Again, by
default it is bound to current connection (please refer to Database (Doctrine 2) and EntityAudit if you want to learn more on SiteSupra
database layer).
Cookbook Articles:¶
Creating Custom Block¶
This tutorial will help you to create a simple block with manageable HTML content.
Note
This tutorial assumes that you’ve already read the section about Blocks and Editables and already have sample CMS Package configured.
Block Configuration¶
Create a class, that would represent your block configuration.
It should extend abstract BlockConfig
class:
1 2 3 4 5 6 7 8 9 | <?php
namespace MySamplePackage\Blocks;
use Supra\Package\Cms\Pages\Block\Config\BlockConfig;
class MyTextBlock extends BlockConfig
{
}
|
Override configureAttributes
method to define block title and description.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <?php
namespace MySamplePackage\Blocks;
use Supra\Package\Cms\Pages\Block\Config\BlockConfig;
class MyTextBlock extends BlockConfig
{
public function configureAttributes(AttributeMapper $mapper)
{
$mapper->title('My Text Block')
->description('This block provides you WYSIWYG editor.');
}
}
|
Create Block Template¶
Create Twig file named my_text_block.html.twig
in Resources/view/blocks/
directory with the following code in there:
1 | <div>{{ property('my_content', 'html') }}</div>
|
This will dynamically create block property named 'my_content'
and link CMS WYSIWYG editor to that property.
Note
You may override template name by calling $mapper->template('MyPackage:path/to/file.html.twig')
inside BlockConfig::configureAttributes()
method.
Register Your Block in CMS¶
The last but not least step is register the block with CMS.
If your package extends AbstractSupraCmsPackage
, then just override getBlocks
method:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <?php
namespace MySamplePackage;
use Supra\Package\Cms\AbstractSupraCmsPackage;
class MySamplePackage extends AbstractSupraCmsPackage
{
...
public function getBlocks()
{
return array(new Blocks\MyTextBlock());
}
}
|
Otherwise, this can be done by calling BlockCollection::addConfig()
on package initialization finish.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | <?php
namespace MySamplePackage;
use Supra\Core\Package\AbstractSupraPackage;
class MySamplePackage extends AbstractSupraPackage
{
...
public function finish(ContainerInterface $container)
{
$blockCollection = $container['cms.pages.blocks.collection'];
/* @var $blockCollection \Supra\Package\Cms\Pages\Block\BlockCollection */
$blockCollection->addConfig(new MyTextBlock(), $this);
}
}
|
That’s all. Your block is now registered and should appear in site block list.
Creating a CRUD¶
Creating Custom Controller¶
Writing Your Own Command¶
General Considerations¶
We would recommend to read Symfony’s guide to creating basic command prior to proceed further. SiteSupra mostly follows the same approach with a few minor differences:
- A command must extend
Supra\Core\Console\AbstractCommand
(or implementSupra\Core\DependencyInjection\ContainerAware
), read more about that in Dependency Injection chapter; - There is no limit on where you can store your commands or what namespace are you using. SiteSupra does not autoload commands, so you are free to choose your class structure.
Basic console command can look like shown below:
<?php
namespace Some\Your\Namespace\Command;
use Supra\Core\Console\AbstractCommand;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class FoobarCommand extends AbstractCommand
{
protected function configure()
{
$this->setName('foobar:do')
->setDescription('Does some foobar');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
//foobar is happening here
}
}
Of course, you can use any of the helpers bundled in Symfony Console - tables, dialogs, and so on.
Registering New Command from Your Package¶
Let’s assume that you have a Package created (refer to Creating Sample CMS Package for package creating instructions).
Now you can register a new command instance in package’s inject()
method:
<?php
namespace Vendor\Package;
use Supra\Core\DependencyInjection\ContainerInterface
use Some\Your\Namespace\Command\FoobarCommand;
use Supra\Core\Package\AbstractSupraPackage;
class FoobarPackage extends AbstractSupraPackage
{
public function inject(ContainerInterface $container)
{
//some magic here...
$container->getConsole()->add(new FoobarCommand());
//even more magic here...
}
}
After that, you can run your command with supra/cli.php foobar:do
(shortcuts like supra/cli.php f:d
are working also).
Creating Sample CMS Package¶
CMS:¶
SiteSupra User Manual¶
Welcome to the SiteSupra CMS guide. This guide provides comprehensive information on how to organize your SiteSupra site’s content and manage it easily. Free and open source, SiteSupra offers fast and easy website development and makes website editing and administration stress-free thanks to intuitive visual CMS.
Log in¶
Start by logging into the admin panel of your website. Visit your site’s login page by placing “/backend” after your domain name e.g. http://example.com/backend.
Default login name is admin@sitesupra.org
and password is admin
.
Initial view¶
After you logged in, you are on the site’s main page view. This is the Home page of your website. Here your creativity can break out and let you build as lovely website as possible.
- On the upper menu you can do following:
- Open the Dashboard;
- Open website’s structure or “Sitemap”;
- Open Page for editing

We recommend you to check the demo of Home page and the Blocks to see most of the SiteSupra page and text management features. However, ahead of jumping in and start editing, take a look at the website’s structure by clicking Sitemap icon.
Site Structure Management¶
To delete a page¶
Choose the page you have decided to delete and click the Gears icon. To delete a page click Delete and confirm requested action by clicking Yes button.



To add a new page¶
- Click and hold ” + “ icon and drag & drop it to the required location.

- When you hold and move the page around the Sitemap the drop location will be marked in blue.

- After you drop the page on the desired location a page properties window appears. Specify page Title. You may define Path which is a page name that appears in the page address.
- Click Template to choose a page template you want. You can go through a wide array of templates.
- When you make your choice, click Create button.

- Open created new page and specify required content for page blocks. Then click Publish to the upper right of the window.


NOTE
If by an accident you have dropped your new page in a wrong place, you can easily change page order in the menu. Simply drag & drop new page to the required place in Sitemap view.

Page Settings¶
Page Settings contains the page editing menu in the upper right pane and here you can specify following:
- Page Title - Page Heading
- Page URL - Page address
- SEO - additional parameters for website optimisation to help perform better in search results.
- Schedule publish - you can schedule a page to publish automatically in the future.
- Redirect - you can redirect your page visitors to other pages or external recources.
- More settings - additional page settings:
- Page status management (active or inactive)
- Page appearance in search results (Yes or No)
- Page appearance in Menu
- Page appearance in Sitemap
- Admission to translate the page
After you have made all the necessary changes, click Save to save changes. To publish final version of the page, click Publish. If you wish to continue working with this page some other time, click Close.

Page status¶
While in a Sitemap view, above the page icons or folders can appear inscription Draft, which means that page was changed, but the changes were not published. This inscription appears whenever you save the changes and close the page with the Close button without Publishing.

Main Toolbar options¶
General editing options are displayed at the top of the menu.

- Insert block - to insert new blocks on the page (see full description here [link to blocks])
- Page blocks - displays a list of elements available for editing;
- Page settings - to access and manage page properties
- Page Title
- Page URL
- Template
- SEO
- Schedule publish
- Redirect
- More settings
Blocks¶
Each page consists of several functional widgets, the blocks, which add functions and events to your website. You can edit them by clicking and managing customisations in the block properties which opens on the right. For each block there are different properties, for example, you can add images to the gallery block and set the links for social media follow block.
To add new block¶
- While in a Sitemap view, select the page you want to edit and click Open.

- Click Edit page button to the upper right of the window.

- Click Insert block in the main toolbar, which appears on top of the page. The Insert block panel opens on the left. Then select Features.

- Click the block you need and drag & drop it to where you want it in the page.

- Specify required content for the block properties. Then click Done and Publish the page.

To delete block¶
If you don’t need a certain block it’s easy to remove it from the page:
- While in a Sitemap view, select the page you want to edit and click Open.

- Click Edit page button to the upper right of the window to start page customisation.

- Click the block you want to remove. Block properties panel opens on the right side of window.
- To remove a block click Delete block button.

- Confirm your choice and click Yes.

- To finish work and save changes, click Publish button to the upper right of the window.
To move blocks within a page¶
It’s easy to change location of the block on a page, here’s how:
- While in the Sitemap view, select the page you want to edit and click Open.

- Click Edit page button to the upper right of the window.

- Select the block you want to move by Clicking and holding it with the mouse and move it to the new location. Make sure not to click the block as you will enter in a block editing mode where moving blocks isn’t possible. When you hold and move the block around the page the drop location will be marked in blue.

- To finish and save block repositioning changes within a page, click Publish button on right-top of the window.
IMPORTANT: Blocks are divided into two main categories:
- Global;
- Non-Global.
If a block is Non-Global, it will appear only on pages where it is added manually, but if a block is Global, it will appear on all pages from selected template. These settings you can specify while creating or customising Templates.
Templates¶
Templates control how your website appears. SiteSupra template provides a method of integration between content and blocks in a specific, controlled view. Site is created by first placing one or more blocks on a template and then creating pages based on those templates. While each template can be configured separately, when adding new pages and selecting template, page will consist of template’s specified design, layout and blocks within placeholders, so you can significantly save the time by creating new pages and content.
While in a Sitemap view, you can switch to Templates editing mode:

If you want to create a page with unique set of blocks to implement original page design, you would need to create a separate page template first so that other pages are not affected. Creating page template is easy, you can just duplicate a page template you like.
For example, website consists of three simple pages: Home, Services and Contacts.



Which in templates view will look like this:

Where pages are built on basis of:
- Page Home is created from template Home, where Home page content is inherit Header/Footer and Home template content.
- Page Services is created from template Inner, where Services page content is inherit from Header/Footer and Inner templates.
- Page Contacts is created from template Inner, where Contacts page content is inherit from Header/Footer and Inner - the same as for Services.

While creating pages, you can customise templates separately by adding necessary blocks to the placeholders.
Dashboard¶
You can open the Dashboard by clicking the icon on the upper left of the page.

The admin panel is very simplistic and from here you can manage Back-office Users, Files or return to Site Structure Management.

Back-office Users¶
You can setup a user account for your website contributors and assign certain privileges. These privileges are known as roles. The following roles exist in the SiteSupra:
- Admins: You can do everything including creating new users and assigning them access rights.
- Supervisors: As a supervisor, you can publish (and unpublish) documents on the website, and approve or disapprove Contributor’s requests for publication.
- Contributors: As a contributor, you can create and edit content in the CMS. When your changes are ready to be published on the website, they have to be approved by a Supervisor.
You can add new users by clicking “+” on the left side and drag-and-drop the icon to necessary role.

Then add User Name and E-mail, click Done. New user will receive invitation to become an admin, supervisor or contributor of your site.

Files¶
Program files (Files) app purpose is to gather all uploaded visual information files for further use on the website. This app also allows you to create the directory tree in order to improve your work with files.
General options are displayed at the top of the menu:
- Upload - possibility to upload necessary files
- New Folder - allows to create new directory
- Delete - possibility to delete unnecessary files or directories
Also it is possible to add new files by dragging and dropping them to required folders. To view image details, click on an image icon. From here you can also Download or Replace the file.